在调用相关库函数对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开发非常好的一个选择。
原创作品