在单片机中,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 |
从上面测试数据可以看到这种方法计算出的低速波特率还是比较接近的,系统时钟越快,理论上计算出来的数据越接近。
至此,串口波特率自适应基本实现。