某日,一工程师咨询在使用STM32F407 MCU开发产品时用到UART5和USART6做串行异步通信,将二者波特率配置为1200bps时,发现UART5正常,而USART6工作不正常。
咋听起来的确有点奇怪。怀疑其相关配置有问题,查看代码并无异常,而且当波特率调高时,二者都表现正常。这基本断定代码配置没有逻辑或流程上的错误。
结合技术手册来看,UART5与USART6的差别主要体现在挂在不同的外设总线上,UART5挂在APB1上,USART6挂在APB2总线上。对于32f407而言,APB1最高时钟42M, APB2最高时钟可达84M。
经进一步了解,客户系统的APB1总线时钟工作在42M,APB2总线时钟工作在84M。客户工程师使用ST官方参考固件库进行开发。如果利用库来开发的话,对于UART波特率的设定,只需要在相应变量位置填入你期望的波特率数值即可,至于具体的寄存器配置是通过库函数调用实现的。用固件库操作的话,工程师往往并没怎么在意这个实现过程。
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USARTx, &USART_InitStructure);
这里有必要看看关于STM32F407 UART的波特率的设置及实现。UART的波特率具体是 通过USART_BRR寄存器来配置实现的,具体是用USARTDIV来设置。
关于波特率、外设时钟、USART_BRR的配置参数有如下关系式:
上图算式中OVER8取0或1,8 x( 2-OVER8) 分别代表过采样的外设时钟个数16或8。Fck表示UART所对应的外设时钟【APB1或APB2】,USARTDIV就是对应 某波特率和外设时钟及过采样情况下要填入USART_BRR寄存器中的数字。
结合上面的计算,这个USARTDIV很可能就不是整数,可能是带小数的数字,这正好满足USART_BRR内部寄存器内部的2部分设计,DIV_MANTISSA【11:0】和DIV_Fraction【3:0】。DIV_MANTISSA放USARTDIV的整数部分,DIV_Fraction放USARTDIV的小数部分。当然,放入DIV_Fraction的数字是将小数部分折算成多少个采样时钟个数后来存放的。具体细节请查看stm32f4系列参考手册相关部分,这里不细化了。
细心的人可能会发现,因为USART_BRR寄存器的整数部分DIV_MANTISSA【11:0】是12位,加上小数部分,USARTDIV最大只能为4096。显然,根据上面算式不难理解在外设频率一定、采样频率一定的情况下,那个波特率的设计值是有一定范围的,并不能随意地天马行空。不然,你所需要的USARTDIV根本没法在USART_BRR寄存器中实现。
讲到这里,大家或许明白了为什么会出现文章开头提出的疑问。我们可以核实下,此时UART5的APB1总线时钟42M,假如过采样为16、波特率为1200的话,可以算得此时的USARTDIV 为2187.5;而UART6此时的APB2总线时钟为84M,同样假如过采样为16,波特率1200的话,不难算得此时的USARTDIV为 4375,显然4375远超出USART_BRR寄存器中USARTDIV所能实现的数据范围了。如果通过调用库函数做黑匣子式地修改USART_BRR的话,就会导致实际配置的波特率跟期望值大相径庭而不不自知。
既然知道原因就好办了,可结合产品应用需求具体调整来解决该问题,比方调整UART口或者调整APB2的工作时钟等。