久久ER99热精品一区二区-久久精品99国产精品日本-久久精品免费一区二区三区-久久综合九色综合欧美狠狠

新聞中心

EEPW首頁 > 嵌入式系統 > 設計應用 > 單片機模擬串口發送和波特率問題

單片機模擬串口發送和波特率問題

作者: 時間:2016-11-27 來源:網絡 收藏
傳統的8051系列單片機一般都配備一個串口,而STC 89C52RC增強型單片機也不例外,只有一個串口可供使用,這樣就出問題了,假如當前單片機系統要求二個串口或多個串口進行同時通信,8051系列單片機只有一個串口可供通信就顯得十分尷尬,但是在實際的應用中,有兩種方法可以選擇。
方法1:使用能夠支持多串口通信的單片機,不過通過更換其他單片機來代替8051系列單片機,這樣就會直接導致成本的增加,優點就是編程簡單,而且通信穩定可靠。
方法2:在IO資源比較充足的情況下,可以通過IO來模擬串口的通信,雖然這樣會增加編程的難度,模擬串口的波特率會比真正的串口通信低一個層次,但是唯一優點就是成本上得到控制,而且通過不同的IO組合可以實現更加之多的模擬串口,在實際應用中往往會采用模擬串口的方法來實現多串口通信。
普遍使用串口通信的數據流都是1位起始位、8位數據位、1位停止位的格式的,如表1。
表1
起始位8位數據位停止位
0Bit0Bit1Bit2Bit3Bit4Bit5Bit6Bit71

要注意的是,起始位作為識別是否有數據到來,停止位標志數據已經發送完畢。起始位固定值為0,停止位固定值為1,那么為什么起始位要是0,停止位要是1呢?這個很好理解,假設停止位固定值為1,為了更加易識別數據的到來,電平的跳變最為簡單也最容易識別,那么當有數據來的時候,只要在規定的時間內檢測到發送過來的第一位的電平是否0值,就可以確定是否有數據到來;另外停止位為1的作用就是當沒有收發數據之后引腳置為高電平起到抗干擾的作用。
在平時使用紅外無線收發數據時,一般都采用模擬串口來實現的,但是有個問題要注意,波特率越高,傳輸距離越近;波特率越低,傳輸距離越遠。對于這些通過模擬串口進行數據傳輸,波特率適宜為1200b/s來進行數據傳輸。
例子:在使用單片機的串口接收數據實驗當中,使用串口調試助手發送16字節數據,單片機采用模擬串口的方法將接收到的數據返發到PC機。
模擬串口實驗代碼:



1#include"stc.h"
2
3#defineRXD P3_0//宏定義:接收數據的引腳
4#defineTXD P3_1//宏定義:發送數據的引腳
5#defineRECEIVE_MAX_BYTES 16//宏定義:最大接收字節數
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超時
10
11
12unsignedcharfTimeouts=0;//T/C超時溢出標志位
13unsignedcharRecvBuf[16];//接收數據緩沖區
14unsignedcharRecvCount=0;//接收數據計數器
15
16
17
23voidSendByte(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();//等待結束位
72TIMER_DISABLE();
73
74returnb;
75
76}
77
83voidPrintfStr(char*pstr)
84{
85while(pstr&&*pstr)
86{
87SendByte(*pstr++);
88}
89}
90
96voidTimerInit(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
122voidmain(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;i141{
142SendByte(RecvBuf[i]);
143}
144}
145}
146
147}
148}
149
155voidTimer0IRQ(void) interrupt1using0
156{
157fTimeouts=1;
158}
159

代碼分析
在模擬串口實驗代碼中,宏的使用占用了相當的一部分。
#define RXD P3_0//宏定義:接收數據的引腳
#define TXD P3_1//宏定義:發送數據的引腳
#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超時

模擬串口接收引腳為P3.0,發送引腳為P3.1。為了達到精確的定時,減少模擬串口時收發數據的累積誤差,有必要通過對T/C進行頻繁的使能和禁止等操作。例如宏TIMER_ENABLE為使能T/C,宏TIMER_DISABLE禁止T/C,宏TIMER_WAIT等待T/C超時。
模擬串口的工作波特率為9600b/s,在串口收發的數據流當中,每一位的時間為1/9600≈104us,
若單片機工作在12MHz頻率下,使用T/C0工作在方式2,那么為了達到104us的定時時間,TH0、TL0的初值為256-104=152,在實際的模擬串口中,往往出現收發數據不正確的現象。原因就在于TH0、TL0的初值,或許很多人會疑惑,按道理來說,計算T/C0的初值是沒有錯的。對,是沒有錯,但是在SendByte和Recv的函數當中,執行每一行代碼都要消耗一定的時間,這就是所謂的“累積誤差”導致收發數據出現問題,因此我們必須通過實際測試得到TH0、TL0的初值,最佳值256-99=157。那么在T/C初始化TimerInit函數中,TH0、TL0的初值不能夠按照常規來計算得到,實際初值在正常初值附近,可以通過實際測試得到。
模擬串口主要復雜在模擬串口發送與接收,具體實現函數在SendByte和RecvByte函數,這兩個函數必須要遵循“1位起始位、8位數據位、1位停止位”的數據流。
SendByte函數用于模擬串口發送數據,以起始位“0”作為移位傳輸的起始標志,然后將要發送的自己從低字節到高字節移位傳輸,最后以停止位“1”作為移位傳輸的結束標志。
RecvByte函數用于模擬串口接收數據,一旦檢測到起始位“0”,就立刻將接收到的每一位移位存儲,最后以判斷停止位“1”結束當前數據的接收。
main函數完成T/C的初始化,在while(1)死循環以檢測起始位“0”為目的,當接收到的數據達到宏RECEIVE_MAX_BYTES的個數時,將接收到的數據返發到外設。

波特率的研究


通常情況下,8051系列單片機外接晶振頻率一般是12MHz、24MHz、48MHz如圖7-6-1,為什么會這樣選取呢?從前面的章節已經介紹8051系列單片機的每12個時鐘周期為一個指令周期,當8051系列單片機外接12MHz晶振時,指令周期=12/12MHz=1us;若外接24MHz晶振時,指令周期=12/24MHz=0.5us;若外接48MHz晶振時,指令周期=12/48MHz=0.25us。8051系列單片機外接能夠被除盡的晶振,在使用單片機內部的定時器/計數器資源時作定時器使用時能夠得到精確定時應用;當使用匯編語言編程時,可以清楚知道當前每一行代碼執行的時間。
8051系列單片機外接能夠被除盡的晶振即12MHz、24MHz、48MHz這些晶振時,波特率的精確性就得不到保證。

假若現在單片機外接的晶振為12MHz時,以T/C2作波特率發生器,根據波特率公式:
波特率=Fosc/2x16x(65536-t)
9600=12MHz/2x16x(65536-t)
t=65496.9375
“65496.9375”不是一個整數值,是一個帶有小數點的數值。對于常用的8位、9位、11位一幀的數據接收與傳輸,最大的允許誤差分別是6.25%、5.56%、4.5%。雖然波特率允許誤差,但是這樣通信時便會產生積累誤差,進而影響數據的正確性。唯一的解決辦法就是更改單片機外接的晶振頻率,更改為常用于產生精確波特率的晶振如11.0592MHz、22.1184MHz。
假若現在單片機外接的晶振為11.0592MHz時,以T/C2作波特率發生器,根據波特率公式:
波特率=Fosc/2x16x(65536-t)
9600=11.0592MHz/2x16x(65536-t)
t=65500=0xFFDC

雖然使用11.0592MHz、22.1184MHz的晶振能夠產生精確的波特率,但是用于系統精確的定時服務不是十分的理想。例如單片機外接11.0592MHz晶振時,指令周期=12/11.0592MHz≈1.085us,是一個無限循環的小數。當單片機外接22.1184MHz晶振時,指令周期=12/22.1184MHz≈0.5425us,也是一個無限循環的小數。

串口工作在方式1時分別采用T/C1和T/C2產生常用波特率初值表如下。
波特率
(11.0592MHz)
初值波特率
(12MHz)
初值
TH1、TL1
(SMOD=0)
TH1、TL1
(SMOD=1)
TH1、TL1
(SMOD=0)
TH1、TL1
(SMOD=1)
12000xE70xD012000xE50xCB
24000xF30xE724000xF20xE5
48000xF90xF348000xF90xF2
96000xFC0xF996000xFC0xF9
144000xFD0xFB144000xFD0xFB
192000xFE0xFC192000xFE0xFC


波特率
(11.0592MHz)
初值波特率
(12MHz)
初值
RCAL2HRCAL2LRCAL2HRCAL2L
12000xFE0xE012000xFE0xC8
24000xFF0x7024000xFF0x64
48000xFF0xD848000xFF0xB2
96000xFF0xDC96000xFF0xD9
144000xFF0xE8144000xFF0xE6
192000xFF0xEE192000xFF0xED

如果大家想通過設置不同的晶振獲取更加多的波特率的值,可以下載以下工具進行計算:
軟件下載地址:http://files.cnblogs.com/wenziqi/單片機多功能助手.rar



評論


技術專區

關閉