« 上一篇下一篇 »

STM32——库函数分析:USART相关

在调用相关库函数对USART模块进行配置前,我们需要先设定好相应的参数。首先,定义一个结构体USART_InitStructure,其类型为USART_InitTypeDef,它的成员即为配置USART需要的相关参数。第二就是开启USART时钟,其挂载于高速APB线(APB2)上。第三是进行参数设定,这里设定波特率为115200,字长为8位,1个停止位,无奇偶校验,无硬件流控制,接收与发送均开启模式。限于文章篇幅,所以此处没有提到USART相关IO口的配置,在编码时请注意添加。参数设定代码如下:

USART_InitTypeDef USART_InitStructure;
        /* config USART1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* USART1 mode config */
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(USART1, &USART_InitStructure);
/* 使能串口1接收中断*/
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);

设置好了参数,就需要调用库函数来完成对USART的配置,上面有3个库函数,我们按代码顺序来。

1、USART_Init()

函数功能:初始化USART模块,完成对它的配置。

/**
  * @brief  Initializes the USARTx peripheral according to the specified
  *         parameters in the USART_InitStruct .
  * @param  USARTx: Select the USART or the UART peripheral. 
  *   This parameter can be one of the following values:
  *   USART1, USART2, USART3, UART4 or UART5.
  * @param  USART_InitStruct: pointer to a USART_InitTypeDef structure
  *         that contains the configuration information for the specified USART 
  *         peripheral.
  * @retval None
  */
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
{
  uint32_t tmpreg = 0x00, apbclock = 0x00;
  uint32_t integerdivider = 0x00;
  uint32_t fractionaldivider = 0x00;
  uint32_t usartxbase = 0;
  RCC_ClocksTypeDef RCC_ClocksStatus;
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_BAUDRATE(USART_InitStruct->USART_BaudRate));  
  assert_param(IS_USART_WORD_LENGTH(USART_InitStruct->USART_WordLength));
  assert_param(IS_USART_STOPBITS(USART_InitStruct->USART_StopBits));
  assert_param(IS_USART_PARITY(USART_InitStruct->USART_Parity));
  assert_param(IS_USART_MODE(USART_InitStruct->USART_Mode));
  assert_param(IS_USART_HARDWARE_FLOW_CONTROL(USART_InitStruct->USART_HardwareFlowControl));
  /* The hardware flow control is available only for USART1, USART2 and USART3 */
  if (USART_InitStruct->USART_HardwareFlowControl != USART_HardwareFlowControl_None)
  {
    assert_param(IS_USART_123_PERIPH(USARTx));
  }
  usartxbase = (uint32_t)USARTx;
/*---------------------------- USART CR2 Configuration -----------------------*/
  tmpreg = USARTx->CR2;
  /* Clear STOP[13:12] bits */
  tmpreg &= CR2_STOP_CLEAR_Mask;
  /* Configure the USART Stop Bits, Clock, CPOL, CPHA and LastBit ------------*/
  /* Set STOP[13:12] bits according to USART_StopBits value */
  tmpreg |= (uint32_t)USART_InitStruct->USART_StopBits;
  
  /* Write to USART CR2 */
  USARTx->CR2 = (uint16_t)tmpreg;
/*---------------------------- USART CR1 Configuration -----------------------*/
  tmpreg = USARTx->CR1;
  /* Clear M, PCE, PS, TE and RE bits */
  tmpreg &= CR1_CLEAR_Mask;
  /* Configure the USART Word Length, Parity and mode ----------------------- */
  /* Set the M bits according to USART_WordLength value */
  /* Set PCE and PS bits according to USART_Parity value */
  /* Set TE and RE bits according to USART_Mode value */
  tmpreg |= (uint32_t)USART_InitStruct->USART_WordLength | USART_InitStruct->USART_Parity |
            USART_InitStruct->USART_Mode;
  /* Write to USART CR1 */
  USARTx->CR1 = (uint16_t)tmpreg;
/*---------------------------- USART CR3 Configuration -----------------------*/  
  tmpreg = USARTx->CR3;
  /* Clear CTSE and RTSE bits */
  tmpreg &= CR3_CLEAR_Mask;
  /* Configure the USART HFC -------------------------------------------------*/
  /* Set CTSE and RTSE bits according to USART_HardwareFlowControl value */
  tmpreg |= USART_InitStruct->USART_HardwareFlowControl;
  /* Write to USART CR3 */
  USARTx->CR3 = (uint16_t)tmpreg;
/*---------------------------- USART BRR Configuration -----------------------*/
  /* Configure the USART Baud Rate -------------------------------------------*/
  RCC_GetClocksFreq(&RCC_ClocksStatus);
  if (usartxbase == USART1_BASE)
  {
    apbclock = RCC_ClocksStatus.PCLK2_Frequency;
  }
  else
  {
    apbclock = RCC_ClocksStatus.PCLK1_Frequency;
  }
  
  /* Determine the integer part */
  if ((USARTx->CR1 & CR1_OVER8_Set) != 0)
  {
    /* Integer part computing in case Oversampling mode is 8 Samples */
    integerdivider = ((25 * apbclock) / (2 * (USART_InitStruct->USART_BaudRate)));    
  }
  else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 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 & CR1_OVER8_Set) != 0)
  {
    tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t)0x07);
  }
  else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */
  {
    tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);
  }
  
  /* Write to USART BRR */
  USARTx->BRR = (uint16_t)tmpreg;
}

上述代码即为USART_Init()的具体实现,现在我们以前面设定的参数为例,来分析这个库函数。

 if (USART_InitStruct->USART_HardwareFlowControl != USART_HardwareFlowControl_None)

括号内条件不满足,跳过相应代码。

  usartxbase = (uint32_t)USARTx;

根据USART1相关宏定义

#define USART1              ((USART_TypeDef *) USART1_BASE)
#define USART1_BASE           (APB2PERIPH_BASE + 0x3800)
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */

可以得到 usartxbase = 0x4001 3800;此即为USART1存储器映像首地址。

  tmpreg = USARTx->CR2;

因为此时USART1->CR2暂未赋值,所以tmpreg = 0;

  tmpreg &= CR2_STOP_CLEAR_Mask;

CR2_STOP_CLEAR_Mask定义如下

#define CR2_STOP_CLEAR_Mask       ((uint16_t)0xCFFF)  /*!< USART CR2 STOP Bits Mask */

因为tmpreg = 0,所以相与后temreg还是0。

  tmpreg |= (uint32_t)USART_InitStruct->USART_StopBits;

因为USART_StopBits_1定义如下

#define USART_StopBits_1                     ((uint16_t)0x0000)

所以相或后temreg仍然为0。

  USARTx->CR2 = (uint16_t)tmpreg;

USART1->CR2 = 0,由CR2寄存器描述

可知,USART被配置为1个停止位。

  tmpreg = USARTx->CR1;

同理,此时tmpreg = 0;

  tmpreg &= CR1_CLEAR_Mask;

CR1_CLEAR_Mask定义如下

#define CR1_CLEAR_Mask            ((uint16_t)0xE9F3)  /*!< USART CR1 Mask */

然而不管它定义如何,此时tmpreg总是为0。

  tmpreg |= (uint32_t)USART_InitStruct->USART_WordLength | USART_InitStruct->USART_Parity |
            USART_InitStruct->USART_Mode;

先找到3个参数的定义

#define USART_WordLength_8b                  ((uint16_t)0x0000)
#define USART_Parity_No                      ((uint16_t)0x0000)
#define USART_Mode_Rx                        ((uint16_t)0x0004)
#define USART_Mode_Tx                        ((uint16_t)0x0008)

所以tmpreg |= 0x0000 | 0x0000 | 0x0004 | 0x0008 = 0x000C;

  USARTx->CR1 = (uint16_t)tmpreg;

USART1->CR1 = 0x000C,由CR1寄存器描述

可知,USART1被配置为8位数据字长、校验控制被禁止、发送与接收均使能。

  tmpreg = USARTx->CR3;

同理,tmpreg = 0;

  tmpreg &= CR3_CLEAR_Mask;

CR3_CLEAR_Mask定义为

#define CR3_CLEAR_Mask            ((uint16_t)0xFCFF)  /*!< USART CR3 Mask */

相与后temreg = 0;

  tmpreg |= USART_InitStruct->USART_HardwareFlowControl;
#define USART_HardwareFlowControl_None       ((uint16_t)0x0000)

所以tmpreg = 0;

  USARTx->CR3 = (uint16_t)tmpreg;

USART1->CR3 = 0,由CR3寄存器描述

可知,USART1被配置为禁止CTS硬件流控制、禁止RTS硬件流控制,即无硬件流控制。

  RCC_GetClocksFreq(&RCC_ClocksStatus);

这是个相关时钟配置的函数,通过Debug我们可以看到各个时钟的配置情况

PCLK2为0x044A A200,转换为十进制即为72 000 000,即PCLK2 = 72MHz。

  if (usartxbase == USART1_BASE)
  {
    apbclock = RCC_ClocksStatus.PCLK2_Frequency;
  }

满足判断条件,所以apbclock = 72 000 000;

这里说明一下:参考手册上面规定了只有USART1才能使用PCLK2时钟(最高72MHz),其他USART模块用PCLK1时钟(最高36MHz)

  if ((USARTx->CR1 & CR1_OVER8_Set) != 0)

CR1 & CR1_OVER8_Set定义如下

#define CR1_OVER8_Set             ((u16)0x8000)  /* USART OVER8 mode Enable Mask */

而USART1->CR1 = 0x000C,两者相与结果为0,不满足条件。

  else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */
  {
    /* Integer part computing in case Oversampling mode is 16 Samples */
    integerdivider = ((25 * apbclock) / (4 * (USART_InitStruct->USART_BaudRate)));    
  }

integerdivider = ( (25 * 72 000 000) / (4 * 115200) ) = 3906.25,舍去小数部分,即为3906。

  tmpreg = (integerdivider / 100) << 4;

tmpreg = ( 3906 / 100 ) << 4 = 39 << 4 = 0x27 << 4 = 0x270 = 624;

  fractionaldivider = integerdivider - (100 * (tmpreg >> 4));

fractionaldivider = 3906 - ( 100 * (0x270 >> 4) ) = 3906 - ( 100 * 0x27 ) = 3906 - 100 * 39 = 3906 - 3900 = 6;

  if ((USARTx->CR1 & CR1_OVER8_Set) != 0)

前面已经判断过了,不满足条件,跳过语句。

  else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */
  {
    tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t)0x0F);
  }

tmpreg |= ( ( (6 * 16) + 50 ) / 100 ) & 0x0F = tmpreg | ( ( 96 + 50 ) / 100 ) & 0x0F = tmpreg | (146 / 100) & 0x0F = tmpreg | 1 & 0x0F = tmpreg | 0x01 = 0x270 | 0x01 = 0x271;

  USARTx->BRR = (uint16_t)tmpreg;

USART1->BRR = 0x271,由BRR寄存器描述

Mantissa:基数

Fraction:分数

所以基数部分为0x27 = 39,分数部分为0x01 = 1,根据参考手册描述

Mantissa(USARTDIV) = 39d

Fraction(USARTDIV) = 1 / 16 = 0.0625d

所以USARTDIV = 39.0625d。

再由波特率计算表

可知,USART1波特率被配置为115.2Kbps,即波特率 = 115.2 * 1000 = 115200。

至此,对USART1的配置总结为:

USART1波特率 = 115200、8位数据字长、校验控制被禁止、发送与接收均使能、1个停止位、无硬件流控制。

再回过头来看一下之前的配置代码。

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;

对比上面的分析,就能知道库函数根据我们的需要帮我们准确的完成了对USART1的配置,是的,它干的就是这么件事,而我们只需要利用库函数,就能够直观且轻而易举的完成对串口模块的配置,库的存在,能够节约开发人员许多的时间,而且在很大程度上降低了开发的难度,的确是进行STM32开发非常好的一个选择。


原创作品