我們可以不使用單片機本身帶有的串口,而自己用程序去模擬一個串口並達到和本身的串口具有同樣的功能,
首先,我們需要用到CH340串口模塊,大家可以上某寶自行購買。
正面:
反面:
然后我們需要了解一下這串口模塊上的引腳:
5V :與VCC短路為5V TLL輸出(電源和信號輸出都是5V)
VCC:可以與3.3V和5V用跳帽連接
3.3V:與VCC短路為3.3V TLL輸出(電源和信號輸出都是3.3V)
TXD:發送數據端口(與單片機上的接收引腳用杜邦線連接)
RXD:接收數據端口(與單片機上的發送引腳用杜邦線連接)
GND:地線
因為我是有另外一條串口提供了51單片機的電源,所以就沒連接5V和VCC,只與單片機連發送、接收和地端口。
現在,讓我們一起了解一下串口通信協議是怎樣的
我們可以將該協議分成三部分
一、 起始信號
二、 數據位
三、 結束信號
首先,串口主要有發送和接受兩個主要功能,
發送
比如說我們需要發送一個0X48的十六進制數,它的二進制為 01001011
則過程為
一、起始信號
默認電平為高(1),先將發送端的引腳電平拉低(0)持續104us,來作為一個起始信號
二、八個數據位
數據的發送,是從最低位開始到最高位一位一位發送的,每一個數據位都需要持續104us(串口助手上選擇了9600波特率,所以延遲時間 104us = 1s / 9600,如果波特率為其他,需要自行換算)
三、結束信號
需要將發送端的引腳電平拉高(1)持續104us以上。
接收
其實接收也很好理解的,怎樣發送就怎樣接收,只是看接收的技巧而已
比如說剛剛發送的0X48從主機發送到了單片機上,我們需要接收
則過程為
一、起始信號
我們需要讀取到它的起始信號,就是當它把你的接收端引腳持續拉低104us之后,就可以當作是開始接收了。
二、八個數據位
因為是八個數據位了,所以所接收的數據也會按照發送一樣一個一個發送,我們只要去讀取這時候的引腳電平就好。
三、結束信號
最后也是一樣,再需要確認一次結束信號,讀取電平是否為高電平,如果全部確認成功后,就可以認為是接收一個有效的數據了。
下面為個人圖解
理論已經說完,接下來就是怎樣用代碼實現了
那我們一步一步開始吧
第一、創建工程
第二、創建延時函數
代碼:
1 void Delay_us(int x) //微秒 2 { 3 while( x-- != 0 ) 4 { 5 _nop_(); 6 _nop_(); 7 } 8 } 9 void Delay_ms(int x) //毫秒 10 { 11 int i,j; 12 while( x-- !=0 ) 13 { 14 for(j=0;j<10;j++) 15 for(i=0;i<85;i++); 16 } 17 }
這里的Delay.c里面有兩個延時函數,一個為微秒級,一個為毫秒級。
我們還需要再創建一個Delay.h頭文件來向外聲明這兩個函數。
頭文件的話,我們還需要把它的路徑添加到工程里,步驟如圖
我不把這兩個函數寫進main.c的原因是如果以后寫的代碼過長了,全部都堆在主函數里的話不方便管理,而這樣分開的話可以降低他們的耦合關系,下面也同理了。
第三、串口函數
這里的函數創建和上面的步驟是一樣的了
發送函數:
代碼:
void vFn_Uart_Send(unsigned char uContent) { unsigned int i; unsigned char uSendContent =0xff; //定義一個發送數據的變量 uSendContent = uContent; //將要發送的值賦給該變量 //起始信號 P05 = 0; //P05作為發送端的引腳 Delay_us(61); //延遲104us //數據發送 for(i=0;i<8;i++) { P05 = uSendContent & 0x01; //將所要發送的八位數據的最低位和0x01進行於運算,若最低位為1,相於后的結果為1,則P05電平為高,否則為0 uSendContent = uSendContent >> 1; //將第二位數據位移到最低位,重復進行七次移動 Delay_us(61); } //結束信號 P05 = 1; //將該引腳電平拉高 Delay_us(61); //延時104us }
這里的104us延遲函數,我已經用示波器測試過時間的了,是可以直接使用的。
因為燒寫時候不同的頻率也會造成延時函數改變,所以大家有條件的話,也可以自己具體去測量一下的
接收函數:
代碼:
void vFn_Uart_Receive() { unsigned int i; unsigned char uContent = 0x00; //定義一個用於接收數據變量 //起始信號 if(P21 == 0) //判斷是否接收到起始信號 { Delay_us(29); //延時52微秒 if(P21 == 0) //再判斷一次接收端電平是否被拉低 { //數據接收 for(i=0;i<8;i++) { uContent = uContent >> 1; //將值右移一位 Delay_us(61); //延時104微秒 if(P21 == 1) //判斷是否為高電平 { uContent |= 0x80; //若為1則與0x80進行或運算(加上0x80也是一樣的道理) } } } Delay_us(61); //延時104微秒 //結束信號 if(P21 == 1) //判斷電平是否被拉高 { vFn_Uart_Send(uContent); //將uContent的值用發送函數發送出來 Delay_ms(1000); } } }
這里講解一下接收函數,我們知道起始信號拉低后,有104us的延時,延時完后就是八個數據位了,如果我們每一次都等完104us再去讀取引腳的電平的話可能會不太穩定,所以我們可以在每個電平中間去讀取,這個時候是最穩定的。下面為圖解
第四、主函數編寫
因為都把函數放到了其他文件中,這樣主函數會看的比較簡潔,我們也直接調用相關的函數就行了,主函數使用也沒有太多的要求。
這里的話是需要每寫完一個函數就去測試的,不過我是已經是全部測試通過的了,然后大家就還是寫完一個測試一個,因為一次性寫完所有函數然后馬上通過的機率不大,還是要修改很多東西
然后我用的是下載軟件是這個,燒寫的時候我們需要將IRC的頻率改成12MHz進行燒寫
串口助手的話,我用的是SSCOM,另外因為我這個單片機的主芯片是IAP15W4K48S4,里面的GPIO的波特率也設置為了9600,所以我們也需要將其設置為9600的波特率才能看到數據,不然就是一堆亂碼了,再然后就是點擊HEX顯示和HEX發送了。
我們來看是已經發送出來了。
再就是測試接收了
這里是把接收直接放到while(1)里面跑,大家也可以使用定時器的,用中斷去跑的話就更好了,這里只是一個小小的示范。
發送了0x52,也接收了0x52,成功了。
這樣一來,我們也就完成模擬串口的收發了。
最后再教一下如何用模擬串口發送文字吧,非常簡單的一個小函數,
代碼
void vFn_Send_Word(unsigned char *s) { while(*s) { vFn_Uart_Send(*s++); } }
然后再在主函數調用就可以實現了。
我的關於51單片機模擬串口到這就結束了,謝謝大家。