USART波特率计算溢出Bug

硬件平台是STM32F429,运行在 180MHz 主频下,固件库版本V1.2.1。使用 USART1 和 USART2 进行串口输出,但是用示波器一看,波特率明显不对。

我的代码如下:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);

    USART_InitStruct.USART_BaudRate = 9600;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_Mode = USART_Mode_Tx;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(UART_CHN0, &USART_InitStruct);
    USART_Init(UART_CHN1, &USART_InitStruct);

    USART_Cmd(UART_CHN0, ENABLE);
    USART_Cmd(UART_CHN1, ENABLE);


一开始严重怀疑 USART_InitStruct.USART_BaudRate 的设置不应该那么简单,但仔细看了 USART_Init() 函数才发现这个波特率是自动计算出来的,也没有轻易怀疑官方固件库存在问题。网上别人也是这么写的,都没有问题。
于是看了手册。这里说下 STM32 的波特率设置寄存器,其实是一个定点数存储器,如下:


从上图可以看出,该寄存器高 16 位无效,最低 4 位为小数部分,其余部分为整数部分。这样的设计可以使波特率更加精确。
关于波特率的产生,有这么一段话来解释:
“分数波特率的产生:
接收器和发送器(RX和TX)都是设置城USARTDIV整数和小数寄存器中配置的值。
TX/RX波特率=Fck/(16*USARTDIV)
例子:从BRR寄存器的值计算得到USARTDIV
如果DIV_Mantissa=27D,DIV_Fraction=12D(BRR=1BCH),那么
Mantissa(USARTDIV)=27D
Fraciton(USARTDIV)=12/16=0.75D
因此,USARTDIV=27.75D”
(其中,D表示十进制)
按照这个说法,看了一下 USART_Init() 函数计算的 BRR 寄存器值,发现明显的不对,先给出 USART_Init() 函数中 BRR 寄存器计算部分的代码:


  uint32_t tmpreg = 0x00, apbclock = 0x00;
  uint32_t integerdivider = 0x00;
  uint32_t fractionaldivider = 0x00;
/* Configure the USART Baud Rate */
  RCC_GetClocksFreq(&RCC_ClocksStatus);

  if ((USARTx == USART1) || (USARTx == USART6))
  {
    apbclock = RCC_ClocksStatus.PCLK2_Frequency;
  }
  else
  {
    apbclock = RCC_ClocksStatus.PCLK1_Frequency;
  }
  
  /* Determine the integer part */
  if ((USARTx->CR1 & USART_CR1_OVER8) != 0)
  {
    /* Integer part computing in case Oversampling mode is 8 Samples */
    integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate)));
  }
  else /* if ((USARTx->CR1 & USART_CR1_OVER8) == 0) */
  {
    /* Integer part computing in case Oversampling mode is 16 Samples */
    integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate)));
  }
  tmpreg = (integerdivider / 100) << 4;

  /* Determine the fractional part */
  fractionaldivider = integerdivider - (100 * (tmpreg >> 4));

  /* Implement the fractional part in the register */
  if ((USARTx->CR1 & USART_CR1_OVER8) != 0)
  {
    tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07);
  }
  else /* if ((USARTx->CR1 & USART_CR1_OVER8) == 0) */
  {
    tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);
  }
  
  /* Write to USART BRR register */
  USARTx->BRR = (uint16_t)tmpreg;


可以看出,这里用于计算的 Fck 是直接从寄存器中读出来的,读出的值是正确的。接下来计算整数部分,这里就出错了。进一步跟踪“integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate)));”这段代码,看汇编程序,发现当 apbclock 为 180,000,000 的时候计算结果是错的,原因在于 25*180000000=4500000000,已经超过了 2^32=4294967296 所能表示的最大数,因此计算结果溢出。
因此,将计算整数部分的代码修改如下:
  
  /* Determine the integer part */
  if ((USARTx->CR1 & USART_CR1_OVER8) != 0)
  {
    /* Integer part computing in case Oversampling mode is 8 Samples */
//    integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate)));
    integerdivider = ((25 * (apbclock / 2)) / (USART_InitStruct->USART_BaudRate));
  }
  else /* if ((USARTx->CR1 & USART_CR1_OVER8) == 0) */
  {
    /* Integer part computing in case Oversampling mode is 16 Samples */
//    integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate)));
    integerdivider = ((25 * (apbclock / 4)) / (USART_InitStruct->USART_BaudRate));
  }


经测试验证,该计算结果正确,波特率正确,USART 能够正常通信。我在我的博客里再次质问下 ST 吧,这样的边界问题,你们不做测试么?
时间: 2024-08-11 06:37:52

USART波特率计算溢出Bug的相关文章

波特率计算

当定时器Tl作波特率发生器使用时,通常选用可自动装入初值模式(工作方式2), 在工作方式2中,TLl作为计数用,而自动装入的初值放在THl中,设计数初值为x, 则每过"256一x"个机器周期,定时器T1就会产生一次溢出.为了避免因溢出而引起中断,此时应禁止T1中断. 这时,溢出周期为: 波特率计算,布布扣,bubuko.com

STM32 波特率计算

The baud rate for the receiver and transmitter (Rx and Tx) are both set to the same value as programmed in the Mantissa and Fraction values of USARTDIV. 从上图可以看出,该寄存器高 16 位无效,最低 4 位为小数部分,其余部分为整数部分. 这样的设计可以使波特率更加精确.关于波特率的产生,有这么一段话来解释: 分数波特率的产生:  接收器和发送

STM32下波特率计算详解

波特率的计算 STM32下的波特率和串口外设时钟息息相关,USART 1的时钟来源于APB2,USART 2-5的时钟来源于APB1.在STM32中,有个波特率寄存器USART_BRR,如下: STM32串口波特率通过USART_BRR进行设置,STM32的波特率寄存器支持分数设置,以提高精确度.USART_BRR的前4位用于表示小数,后12位用于表示整数.但是它还不是我们想要设置的波特率,想要设置我们串口的波特率大小还需要进行计算.其实有关波特率的计算是下面这一条表达式: 从上面的表达式,我们

串口之波特率计算

1.1 波特率结构框图 1.2 波特率寄存器示意图 1.3 波特率计算公式示意图 两图看出,串口波特率寄存器是一个32位,只用低16位,低16位又划分,低4位用来装小数,其他用来装整数. 波特率计算公式:Tx/Rx 波特率  = fCK/(8*(2- OVER8 )* USARTDIV) USARTDIV =  fCK/8*(2- OVER8 )/TxRx 波特率 Tx/Rx 波特率已知值    //就是我们我们平常说设置的115200Hz. fCK是已知值     //串口时钟84MHz OV

java 计算溢出

在 java/js 中decimal类型进行计算(累加之类的),计算结果之后还有一切乱七八在的数据,不晓得啥原因.但是解决方案如下: 1. 在java中的解决方案,步骤: a. 把decimal的数据转化成double类型: double xmz = 0D; BigDecimal zxMz = zx.getMz(); xmz= zxMz==null?0D:zxMz.doubleValue(); b. 将转化后的double数据,进行计算,之后通过DecimalFormat格式化下: java.t

IE7下li超出ul的固定宽度后溢出bug

问题描述: ul固定宽度,li浮动超出ul的宽度自动换行,li有左margin,但是靠近ul左边缘的那一列l 的margin设为0,其他浏览器正常,但是在ie7中超出ul宽度后会有一个l溢出并导致出现横向滚动         条,即便ul设置了overflow:hidden,也无用. 解决方法:特殊li的margin-lef不能设置为0,可以设置为1px,即正常.

8051单片机串口波特率计算方式

STC12C5A60S2单片机兼容80C51单片机,其串口波特率可以由定时器产生,也可以由独立波特率发生器产生.其波特率模式可以是固定的,也可以是可变的. 固定波特率:当 模式0的通信速度设置 位UART_M0x6/AUXR.5 = 0时,其波特率 = SYSclk/12 当 模式0的通信速度设置 位UART_M0x6/AUXR.5 = 1时,其波特率 = SYSclk/2 可变波特率:使用串行通信模式2,即可自定义串口通信波特率,其计算公式为:波特率 = 2^SMODE /64×( SYScl

数学计算 的bug:(理论)数学上等价,实际运行未必等价

1. 计算表达式的值(lambda 表达式) fun1 和 fun2 理论上是等价的:同样的输入情形下,两种输出结果不一致. 2. 运行示例 >>> x=1e16 >>> x 1e+16 >>> fun1=lambda x:sqrt(x+1)-sqrt(x) # lambda 表达式定义 >>> fun2= lambda x:1/(sqrt(x+1)+sqrt(x)) >>> fun1(x) 0.0 >>

关于BUG率的计算和它的实际意义的思考

我的微信号是Shalayang,以下是我的二维码名片,欢迎添加. 问题1:bug率有什么作用? my opion:用处有很多,需要具体情况具体分析,不过主要作用一般是来评价工作产品的质量.如果bug率较高,说明系统质量较差,需要大量的返工.项目经理就需要做好缺陷分析(缺陷的类型.分布.严重程度等),找出原因,以便做好下一阶段的缺陷预防工作.除此之外,还可以结合其它方面的信息,判断是否一些工作不充分.譬如,如果缺陷密度过低,有两个原因:可能工作产品质量确实高:也可能评审或测试不充分,更多的缺陷没有