單片機(jī)模擬串口發(fā)送和波特率問(wèn)題
方法1:使用能夠支持多串口通信的單片機(jī),不過(guò)通過(guò)更換其他單片機(jī)來(lái)代替8051系列單片機(jī),這樣就會(huì)直接導(dǎo)致成本的增加,優(yōu)點(diǎn)就是編程簡(jiǎn)單,而且通信穩(wěn)定可靠。
方法2:在IO資源比較充足的情況下,可以通過(guò)IO來(lái)模擬串口的通信,雖然這樣會(huì)增加編程的難度,模擬串口的波特率會(huì)比真正的串口通信低一個(gè)層次,但是唯一優(yōu)點(diǎn)就是成本上得到控制,而且通過(guò)不同的IO組合可以實(shí)現(xiàn)更加之多的模擬串口,在實(shí)際應(yīng)用中往往會(huì)采用模擬串口的方法來(lái)實(shí)現(xiàn)多串口通信。
普遍使用串口通信的數(shù)據(jù)流都是1位起始位、8位數(shù)據(jù)位、1位停止位的格式的,如表1。
表1
要注意的是,起始位作為識(shí)別是否有數(shù)據(jù)到來(lái),停止位標(biāo)志數(shù)據(jù)已經(jīng)發(fā)送完畢。起始位固定值為0,停止位固定值為1,那么為什么起始位要是0,停止位要是1呢?這個(gè)很好理解,假設(shè)停止位固定值為1,為了更加易識(shí)別數(shù)據(jù)的到來(lái),電平的跳變最為簡(jiǎn)單也最容易識(shí)別,那么當(dāng)有數(shù)據(jù)來(lái)的時(shí)候,只要在規(guī)定的時(shí)間內(nèi)檢測(cè)到發(fā)送過(guò)來(lái)的第一位的電平是否0值,就可以確定是否有數(shù)據(jù)到來(lái);另外停止位為1的作用就是當(dāng)沒(méi)有收發(fā)數(shù)據(jù)之后引腳置為高電平起到抗干擾的作用。
在平時(shí)使用紅外無(wú)線(xiàn)收發(fā)數(shù)據(jù)時(shí),一般都采用模擬串口來(lái)實(shí)現(xiàn)的,但是有個(gè)問(wèn)題要注意,波特率越高,傳輸距離越近;波特率越低,傳輸距離越遠(yuǎn)。對(duì)于這些通過(guò)模擬串口進(jìn)行數(shù)據(jù)傳輸,波特率適宜為1200b/s來(lái)進(jìn)行數(shù)據(jù)傳輸。
例子:在使用單片機(jī)的串口接收數(shù)據(jù)實(shí)驗(yàn)當(dāng)中,使用串口調(diào)試助手發(fā)送16字節(jié)數(shù)據(jù),單片機(jī)采用模擬串口的方法將接收到的數(shù)據(jù)返發(fā)到PC機(jī)。
模擬串口實(shí)驗(yàn)代碼:
代碼分析
在模擬串口實(shí)驗(yàn)代碼中,宏的使用占用了相當(dāng)?shù)囊徊糠帧?br />#define RXD P3_0 //宏定義:接收數(shù)據(jù)的引腳
#define TXD P3_1 //宏定義:發(fā)送數(shù)據(jù)的引腳
#define TIMER_ENABLE() {TL0=TH0;TR0=1;fTimeouts=0;}//使能T/C
#define TIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C
#define TIMER_WAIT() {while(!fTimeouts);fTimeouts=0;}//等待T/C超時(shí)
模擬串口接收引腳為P3.0,發(fā)送引腳為P3.1。為了達(dá)到精確的定時(shí),減少模擬串口時(shí)收發(fā)數(shù)據(jù)的累積誤差,有必要通過(guò)對(duì)T/C進(jìn)行頻繁的使能和禁止等操作。例如宏TIMER_ENABLE為使能T/C,宏TIMER_DISABLE禁止T/C,宏TIMER_WAIT等待T/C超時(shí)。
模擬串口的工作波特率為9600b/s,在串口收發(fā)的數(shù)據(jù)流當(dāng)中,每一位的時(shí)間為1/9600≈104us,
若單片機(jī)工作在12MHz頻率下,使用T/C0工作在方式2,那么為了達(dá)到104us的定時(shí)時(shí)間,TH0、TL0的初值為256-104=152,在實(shí)際的模擬串口中,往往出現(xiàn)收發(fā)數(shù)據(jù)不正確的現(xiàn)象。原因就在于TH0、TL0的初值,或許很多人會(huì)疑惑,按道理來(lái)說(shuō),計(jì)算T/C0的初值是沒(méi)有錯(cuò)的。對(duì),是沒(méi)有錯(cuò),但是在SendByte和Recv的函數(shù)當(dāng)中,執(zhí)行每一行代碼都要消耗一定的時(shí)間,這就是所謂的“累積誤差”導(dǎo)致收發(fā)數(shù)據(jù)出現(xiàn)問(wèn)題,因此我們必須通過(guò)實(shí)際測(cè)試得到TH0、TL0的初值,最佳值256-99=157。那么在T/C初始化TimerInit函數(shù)中,TH0、TL0的初值不能夠按照常規(guī)來(lái)計(jì)算得到,實(shí)際初值在正常初值附近,可以通過(guò)實(shí)際測(cè)試得到。
模擬串口主要復(fù)雜在模擬串口發(fā)送與接收,具體實(shí)現(xiàn)函數(shù)在SendByte和RecvByte函數(shù),這兩個(gè)函數(shù)必須要遵循“1位起始位、8位數(shù)據(jù)位、1位停止位”的數(shù)據(jù)流。
SendByte函數(shù)用于模擬串口發(fā)送數(shù)據(jù),以起始位“0”作為移位傳輸?shù)钠鹗紭?biāo)志,然后將要發(fā)送的自己從低字節(jié)到高字節(jié)移位傳輸,最后以停止位“1”作為移位傳輸?shù)慕Y(jié)束標(biāo)志。
RecvByte函數(shù)用于模擬串口接收數(shù)據(jù),一旦檢測(cè)到起始位“0”,就立刻將接收到的每一位移位存儲(chǔ),最后以判斷停止位“1”結(jié)束當(dāng)前數(shù)據(jù)的接收。
main函數(shù)完成T/C的初始化,在while(1)死循環(huán)以檢測(cè)起始位“0”為目的,當(dāng)接收到的數(shù)據(jù)達(dá)到宏RECEIVE_MAX_BYTES的個(gè)數(shù)時(shí),將接收到的數(shù)據(jù)返發(fā)到外設(shè)。
方法2:在IO資源比較充足的情況下,可以通過(guò)IO來(lái)模擬串口的通信,雖然這樣會(huì)增加編程的難度,模擬串口的波特率會(huì)比真正的串口通信低一個(gè)層次,但是唯一優(yōu)點(diǎn)就是成本上得到控制,而且通過(guò)不同的IO組合可以實(shí)現(xiàn)更加之多的模擬串口,在實(shí)際應(yīng)用中往往會(huì)采用模擬串口的方法來(lái)實(shí)現(xiàn)多串口通信。
普遍使用串口通信的數(shù)據(jù)流都是1位起始位、8位數(shù)據(jù)位、1位停止位的格式的,如表1。
表1
起始位 | 8位數(shù)據(jù)位 | 停止位 | |||||||
0 | Bit0 | Bit1 | Bit2 | Bit3 | Bit4 | Bit5 | Bit6 | Bit7 | 1 |
要注意的是,起始位作為識(shí)別是否有數(shù)據(jù)到來(lái),停止位標(biāo)志數(shù)據(jù)已經(jīng)發(fā)送完畢。起始位固定值為0,停止位固定值為1,那么為什么起始位要是0,停止位要是1呢?這個(gè)很好理解,假設(shè)停止位固定值為1,為了更加易識(shí)別數(shù)據(jù)的到來(lái),電平的跳變最為簡(jiǎn)單也最容易識(shí)別,那么當(dāng)有數(shù)據(jù)來(lái)的時(shí)候,只要在規(guī)定的時(shí)間內(nèi)檢測(cè)到發(fā)送過(guò)來(lái)的第一位的電平是否0值,就可以確定是否有數(shù)據(jù)到來(lái);另外停止位為1的作用就是當(dāng)沒(méi)有收發(fā)數(shù)據(jù)之后引腳置為高電平起到抗干擾的作用。
在平時(shí)使用紅外無(wú)線(xiàn)收發(fā)數(shù)據(jù)時(shí),一般都采用模擬串口來(lái)實(shí)現(xiàn)的,但是有個(gè)問(wèn)題要注意,波特率越高,傳輸距離越近;波特率越低,傳輸距離越遠(yuǎn)。對(duì)于這些通過(guò)模擬串口進(jìn)行數(shù)據(jù)傳輸,波特率適宜為1200b/s來(lái)進(jìn)行數(shù)據(jù)傳輸。
模擬串口實(shí)驗(yàn)代碼:
1#include"stc.h"
2
3 #defineRXD P3_0//宏定義:接收數(shù)據(jù)的引腳
4 #defineTXD P3_1//宏定義:發(fā)送數(shù)據(jù)的引腳
5 #defineRECEIVE_MAX_BYTES 16//宏定義:最大接收字節(jié)數(shù)
6
7 #defineTIMER_ENABLE() {TL0=TH0;TR0=1;fTimeouts=0;}//使能T/C
8 #defineTIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C
9 #defineTIMER_WAIT() {while(!fTimeouts);fTimeouts=0;}//等待T/C超時(shí)
10
11
12unsignedcharfTimeouts=0;//T/C超時(shí)溢出標(biāo)志位
13 unsignedcharRecvBuf[16];//接收數(shù)據(jù)緩沖區(qū)
14 unsignedcharRecvCount=0;//接收數(shù)據(jù)計(jì)數(shù)器
15
16
17
23 voidSendByte(unsignedcharb)
24{
25unsignedchari=8;
26
27TXD=0;
28
29TIMER_ENABLE();
30TIMER_WAIT();
31
32
33while(i--)
34{
35if(b&1)TXD=1;
36elseTXD=0;
37
38TIMER_WAIT();
39
40b>>=1;
41
42}
43
44
45TXD=1;
46
47TIMER_WAIT();
48TIMER_DISABLE();
49}
50
56unsignedcharRecvByte(void)
57{
58unsignedchari;
59unsignedcharb=0;
60
61TIMER_ENABLE();
62TIMER_WAIT();
63
64for(i=0;i<8;i++)
65{
66if(RXD)b|=(1<67
68TIMER_WAIT();
69}
70
71TIMER_WAIT();//等待結(jié)束位
72 TIMER_DISABLE();
73
74returnb;
75
76}
77
83 voidPrintfStr(char*pstr)
84{
85while(pstr&&*pstr)
86{
87SendByte(*pstr++);
88}
89}
90
96 voidTimerInit(void)
97{
98TMOD=0x02;
99TR0=0;
100TF0=0;
101TH0=(256-99);
102TL0=TH0;
103ET0=1;
104EA=1;
105}
106
112unsignedcharStartBitCome(void)
113{
114return(RXD==0);
115}
116
122 voidmain(void)
123{
124unsignedchari;
125
126TimerInit();
127
128PrintfStr("Hello 8051rn");
129
130while(1)
131{
132if(StartBitCome())
133{
134RecvBuf[RecvCount++]=RecvByte();
135
136if(RecvCount>=RECEIVE_MAX_BYTES)
137{
138RecvCount=0;
139
140for(i=0;i 141{
142SendByte(RecvBuf[i]);
143}
144}
145}
146
147}
148}
149
155 voidTimer0IRQ(void) interrupt1using0
156{
157fTimeouts=1;
158}
159
2
3
4
5
6
7
8
9
10
11
12unsignedcharfTimeouts=0;//T/C超時(shí)溢出標(biāo)志位
13
14
15
16
17
23
24{
25unsignedchari=8;
26
27TXD=0;
28
29TIMER_ENABLE();
30TIMER_WAIT();
31
32
33while(i--)
34{
35if(b&1)TXD=1;
36elseTXD=0;
37
38TIMER_WAIT();
39
40b>>=1;
41
42}
43
44
45TXD=1;
46
47TIMER_WAIT();
48TIMER_DISABLE();
49}
50
56unsignedcharRecvByte(void)
57{
58unsignedchari;
59unsignedcharb=0;
60
61TIMER_ENABLE();
62TIMER_WAIT();
63
64for(i=0;i<8;i++)
65{
66if(RXD)b|=(1<67
68TIMER_WAIT();
69}
70
71TIMER_WAIT();//等待結(jié)束位
72
73
74returnb;
75
76}
77
83
84{
85while(pstr&&*pstr)
86{
87SendByte(*pstr++);
88}
89}
90
96
97{
98TMOD=0x02;
99TR0=0;
100TF0=0;
101TH0=(256-99);
102TL0=TH0;
103ET0=1;
104EA=1;
105}
106
112unsignedcharStartBitCome(void)
113{
114return(RXD==0);
115}
116
122
123{
124unsignedchari;
125
126TimerInit();
127
128PrintfStr("Hello 8051rn");
129
130while(1)
131{
132if(StartBitCome())
133{
134RecvBuf[RecvCount++]=RecvByte();
135
136if(RecvCount>=RECEIVE_MAX_BYTES)
137{
138RecvCount=0;
139
140for(i=0;i
142SendByte(RecvBuf[i]);
143}
144}
145}
146
147}
148}
149
155
156{
157fTimeouts=1;
158}
159
在模擬串口實(shí)驗(yàn)代碼中,宏的使用占用了相當(dāng)?shù)囊徊糠帧?br />#define RXD P3_0
#define TXD P3_1
#define TIMER_ENABLE()
#define TIMER_DISABLE() {TR0=0;fTimeouts=0;}//禁止T/C
#define TIMER_WAIT()
模擬串口接收引腳為P3.0,發(fā)送引腳為P3.1。為了達(dá)到精確的定時(shí),減少模擬串口時(shí)收發(fā)數(shù)據(jù)的累積誤差,有必要通過(guò)對(duì)T/C進(jìn)行頻繁的使能和禁止等操作。例如宏TIMER_ENABLE為使能T/C,宏TIMER_DISABLE禁止T/C,宏TIMER_WAIT等待T/C超時(shí)。
模擬串口的工作波特率為9600b/s,在串口收發(fā)的數(shù)據(jù)流當(dāng)中,每一位的時(shí)間為1/9600≈104us,
若單片機(jī)工作在12MHz頻率下,使用T/C0工作在方式2,那么為了達(dá)到104us的定時(shí)時(shí)間,TH0、TL0的初值為256-104=152,在實(shí)際的模擬串口中,往往出現(xiàn)收發(fā)數(shù)據(jù)不正確的現(xiàn)象。原因就在于TH0、TL0的初值,或許很多人會(huì)疑惑,按道理來(lái)說(shuō),計(jì)算T/C0的初值是沒(méi)有錯(cuò)的。對(duì),是沒(méi)有錯(cuò),但是在SendByte和Recv的函數(shù)當(dāng)中,執(zhí)行每一行代碼都要消耗一定的時(shí)間,這就是所謂的“累積誤差”導(dǎo)致收發(fā)數(shù)據(jù)出現(xiàn)問(wèn)題,因此我們必須通過(guò)實(shí)際測(cè)試得到TH0、TL0的初值,最佳值256-99=157。那么在T/C初始化TimerInit函數(shù)中,TH0、TL0的初值不能夠按照常規(guī)來(lái)計(jì)算得到,實(shí)際初值在正常初值附近,可以通過(guò)實(shí)際測(cè)試得到。
模擬串口主要復(fù)雜在模擬串口發(fā)送與接收,具體實(shí)現(xiàn)函數(shù)在SendByte和RecvByte函數(shù),這兩個(gè)函數(shù)必須要遵循“1位起始位、8位數(shù)據(jù)位、1位停止位”的數(shù)據(jù)流。
SendByte函數(shù)用于模擬串口發(fā)送數(shù)據(jù),以起始位“0”作為移位傳輸?shù)钠鹗紭?biāo)志,然后將要發(fā)送的自己從低字節(jié)到高字節(jié)移位傳輸,最后以停止位“1”作為移位傳輸?shù)慕Y(jié)束標(biāo)志。
RecvByte函數(shù)用于模擬串口接收數(shù)據(jù),一旦檢測(cè)到起始位“0”,就立刻將接收到的每一位移位存儲(chǔ),最后以判斷停止位“1”結(jié)束當(dāng)前數(shù)據(jù)的接收。
main函數(shù)完成T/C的初始化,在while(1)死循環(huán)以檢測(cè)起始位“0”為目的,當(dāng)接收到的數(shù)據(jù)達(dá)到宏RECEIVE_MAX_BYTES的個(gè)數(shù)時(shí),將接收到的數(shù)據(jù)返發(fā)到外設(shè)。
波特率的研究
8051系列單片機(jī)外接能夠被除盡的晶振即12MHz、24MHz、48MHz這些晶振時(shí),波特率的精確性就得不到保證。
波特率 (11.0592MHz) | 初值 | 波特率 (12MHz) | 初值 | ||
TH1、TL1 (SMOD=0) | TH1、TL1 (SMOD=1) | TH1、TL1 (SMOD=0) | TH1、TL1 (SMOD=1) | ||
1200 | 0xE7 | 0xD0 | 1200 | 0xE5 | 0xCB |
2400 | 0xF3 | 0xE7 | 2400 | 0xF2 | 0xE5 |
4800 | 0xF9 | 0xF3 | 4800 | 0xF9 | 0xF2 |
9600 | 0xFC | 0xF9 | 9600 | 0xFC | 0xF9 |
14400 | 0xFD | 0xFB | 14400 | 0xFD | 0xFB |
19200 | 0xFE | 0xFC | 19200 | 0xFE | 0xFC |
波特率 (11.0592MHz) | 初值 | 波特率 (12MHz) | 初值 | ||
RCAL2H | RCAL2L | RCAL2H | RCAL2L | ||
1200 | 0xFE | 0xE0 | 1200 | 0xFE | 0xC8 |
2400 | 0xFF | 0x70 | 2400 | 0xFF | 0x64 |
4800 | 0xFF | 0xD8 | 4800 | 0xFF | 0xB2 |
9600 | 0xFF | 0xDC | 9600 | 0xFF | 0xD9 |
14400 | 0xFF | 0xE8 | 14400 | 0xFF | 0xE6 |
19200 | 0xFF | 0xEE | 19200 | 0xFF | 0xED |
如果大家想通過(guò)設(shè)置不同的晶振獲取更加多的波特率的值,可以下載以下工具進(jìn)行計(jì)算:
軟件下載地址:http://files.cnblogs.com/wenziqi/單片機(jī)多功能助手.rar
評(píng)論