在單片機中,UART是常用的通信方式。最近在研究Bootloader需要設置UART的波特率自適應,通過查閱資料參考了網友的方法,故借此分享我的方法。
一般的,串口自適應波特率有以下2種方法。
一是通過具有獨立的同步字符。使用串行通訊前,要先進行同步操作,即接收端通過對比接收到的字符與同步字符的差異調整波特率,或者通過定時器測量同步字符的位寬計算波特率。即將串口接收IO電平狀態進行定時器計時,計算出最小脈寬時長,即一幀時長,就可算出其波特率值,這種方式需要特定的同步字符(如:0x55、0xAA等)或者增加檢測的周期。
二是不需要獨立的同步字符。即不管波特率如何,可以直接通訊。通訊命令前導字符固定,比如一些短消息模塊使用AT指令集,每條指令都是以AT開始的,這樣雖然是命令但是有同步的作用,不過要求必須在真實命令到來前調整波特率,這種方式需要的時間周期長,因為單片機需要不斷調整波特率去接收判斷傳輸的字符直到通訊成功,且判斷必須快速准確。
由於方便需要,以下實驗采用的是方法一,使用單片機SC95F8617作為串口實驗對象。
實驗條件:內部定時器一個。
實驗方法:通過定時器,連續檢測UART輸入引腳RXD上的電平變化,得到RXD上最小的脈寬時長計算出波特率,以達到波特率自適應。
參考代碼如下,最后返回的就是波特率數值,其中發送的同步字符為0x55:
1 #define SystemClock 32000000 //HRC=32MHz 2 #define UART_TX P21 //TX口 3 #define UART_RX P20 //RX口 4 #define UART_TX_INIT {P2CON &= ~(1<<1);P2PH |= (1<<1);} //TX設為輸入上拉模式 5 #define UART_RX_INIT {P2CON &= ~(1<<0);P2PH |= (1<<0);} //RX設為輸入上拉模式 6 7 //波特率自適應 8 //需要用到讀IO電平 9 //通過檢測到最小的脈寬時間 10 unsigned long UART_Baud_Adapt(void) 11 { 12 unsigned long baudrate =0; 13 unsigned long t1=0,t2,t=0; 14 unsigned long oldLevel,newLevel; 15 unsigned char i=0; 16 UART_RX_INIT; //RX設為輸入模式 17 18 oldLevel = UART_RX; //讀取一次RX的電平 19 while(UART_RX == 1); //等待RX被拉低 20 Timer0_Open(); 21 Timer0_Enable(); 22 for(i=0;i<8;) //連續檢測8次電平變化,檢測1個字節 23 { 24 newLevel = UART_RX; // 讀RX的電平 25 if(newLevel != oldLevel) // 如果有電平變化 26 { 27 t2 = (unsigned long)(TH0<<8)+TL0; // 讀定時器中的值 28 oldLevel = newLevel; // 更新為新的引腳值 29 if((t1 == 0)&&(t == 0)) // 第一個電平變化 30 { 31 t1 = t2; // 記錄第一個時刻點 32 } 33 else // 不是第一個電平變化 34 { 35 if(t == 0) // 第一段電平 36 { 37 t = t2-t1; // 記錄第一段電平所用時間 38 } 39 else // 不是第一段電平 40 { 41 if((t2-t1)< t) 42 { 43 t = t2-t1; // 保留電平脈寬的最小值 44 } 45 } 46 t1 = t2; // 更新為新的時刻點 47 } 48 i++; // 電平變化數+1 49 } 50 } 51 Timer0_Close(); 52 baudrate = (unsigned long)(t*403/400); //計算時間 53 baudrate = SystemClock/baudrate; //計算波特率 54 return baudrate; 55 } 56 //Timer0設置 57 void Timer0_Open(void) 58 { 59 TMCON = 0X07; //不分頻 60 TMOD |= 0x01; //0000 0001;Timer0設置工作方式1 61 TH0=TL0=0; 62 } 63 //使能Timer0 64 void Timer0_Enable(void) 65 { 66 ET0 = 1; //定時器0允許 67 TR0 = 1; //打開定時器0 68 } 69 //關閉Timer0 70 void Timer0_Close(void) 71 { 72 ET0 = 0; //定時器0關閉 73 TR0 = 0; //關閉定時器0 74 }
通過實際測試多個波特率,結果如下:
| 發送同步字符波特率(bps) | 自適應方法計算得到的波特率(bps) |
| 4800 | 4764 |
| 9600 | 9589 |
| 19200 | 19417 |
| 38400 | 38787 |
| 115200 | 122605 |
| 256000 | 240601 |
從上面測試數據可以看到這種方法計算出的低速波特率還是比較接近的,系統時鍾越快,理論上計算出來的數據越接近。
至此,串口波特率自適應基本實現。
