單片機:STC89C52
開發板:普中科技HC6800-ES V2.0
串口調試助手:sscom32
燒錄軟件:STC 第一次進行串口通信的時候燒錄的程序參照的是買板子時送的資料的例程,雖然最後助手界面顯示PC機接收到單片機傳來的信號,但是PC機向單片機發送了好幾次數據纔出現了一次單片機向PC機的通信反饋。
今天我又重新上機調試,找出了導致通信失敗的原因,那就是定時器的溢出誤差所導致的波特率誤差使得通信失敗。
首先,在沒有把程序燒錄進板子前先不要打開串口調試助手sscom32,這樣做調試助手並不能識別出串口號。不過如果打開了也沒關係,只要在把程序燒錄進板子的操作前把調試助手軟件先關閉即可(助手檢測到串口後便會佔用串口,導致無法燒錄)。生成hex文件(由下方的第一段源碼生成)並寫入板子就可以打開串口助手了(下圖是還沒有燒錄就先打開調試助手所顯示的界面)
如果設置的波特率爲9600bps,那麼理想的通信速度也是9600bps,但是由於開發板用的是12MHZ的晶振,而並非是11.0592MHZ的晶振,因此計算定時器1的初值時算出的不是一個整數,這樣每次累計下來就會導致實際的T1溢出率並非是理想的溢出率,實際上的波特率就不是9600bps,經計算,真實波特率和理想波特率的誤差是8.51%(這個誤差就是通信失敗的原因) (源程序如下:)
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
uchar code table[]="I get ";
uchar a,flag,i;
void Init()
{
TMOD=0x20;
TH1=0xfd;TL0=0xfd; //9600bps對應的定時器初值
SCON=0x50;
EA=1; //總中斷打開
ES=1; //串口中斷打開
TR1=1; //開啓定時器1
}
void T_R_Serve() interrupt 4 //串口的中斷服務程序
{
RI=0; //軟件清零串口接收標誌位
a=SBUF; //將PC機傳入單片機的數據讀出
flag=1; //進入中斷則產生標誌
}
void main()
{
Init();
while(1)
{
if(flag=1) //等待PC機發送完數據
{
ES=0; //關閉串口中斷,防止進入死循環(不關閉的話單片機每發送完一個數據都要進入中斷)
for(i=0;i<6;i++) //依次向上位機傳送"I get "這6個字符
{
SBUF=table[i];
while(!TI); //每一次都要等待這個字符傳送完畢才能開始傳送下一個字符,沒傳送完就一直停留在這裏
TI=0;
}
SBUF=a; //把剛纔從上位機讀取到的數據再作爲發送數據傳送回去
while(!TI);
TI=0;
flag=0; //標誌清零,以便可以多次接收上位機傳來的數據
ES=1; //重新開啓串口中斷
}
}
}
接下來用這段源代碼所生成的hex文件燒錄進板子,並打開調試助手,設置其中的波特率爲9600bps,在字符串輸入框中輸入一個字符(例如b),然後點擊發送,就可以看到產生了錯誤:
發送了10次,每一次發送上位機上接收到的都是亂碼,說明通信失敗了。
原因在於:調試助手軟件選擇的通信波特率爲9600bps,而實際上單片機的實際波特率並不是9600bps,而是有着8.51%的誤差,這樣波特率不匹配,就無法成功地通信。
接着可以多調試一些別的波特率,或者改變SMOD的狀態來減小誤差(串口方式1的波特率=((2^SMOD)/32)×(T1的溢出率) ),下面我再編程用4800bps的波特率和SMOD=1(只需要修改定時器1的初值和在初始化中添加指令PCON=0x80即可),誤差降低爲0.16%,試試看能否成功通信:
看到上位機發送了5次數據,每一次都能得到單片機的反饋,說明通信已經成功了。
再調試一次300bps,SMOD=0的情況,這種情況下波特率誤差幾乎爲0,可以忽略不計。
效果如下:
從界面顯示看沒有任何不同,但實際上300bps的情況下每次由上位機發送數據到單片機傳回數據所消耗的時間都比4800bps要長很多。
如果你的板子出現了通信失敗的現象時,可以考慮一下是否是由於晶振頻率不同導致的定時器的溢出誤差所引發的。