一文了解串口打印
闲言少叙,先上Code,大家看一下下面这段代码有没有问题?
// Note: USART demo code runs on STM32F030#include "main.h"static __IO uint32_t TimingDelay;RCC_ClocksTypeDef RCC_Clocks;uint8_t uart_buffer[100];// GPIO Configurationvoid GPIO_Configuration(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1); // Tx PA9GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_1); // Rx PA10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // USART1_TX | USART1_RXGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_1;GPIO_Init(GPIOA, &GPIO_InitStructure);}// USART Configurationvoid USART_Configuration(void){USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;//USART_InitStructure.USART_BaudRate = 9600;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_Tx | USART_Mode_Rx;USART_Init(USART1,&USART_InitStructure);USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);USART_Cmd(USART1,ENABLE);}// Interrupt Configurationvoid NVIC_Configuration(void){NVIC_InitTypeDef NVIC_InitStructure;// USART1 interrupt ConfigNVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);}// USART1 Interrupt Handlervoid USART1_IRQHandler (void){static uint8_t i = 0;if(USART_GetITStatus(USART1,USART_IT_RXNE)!= RESET){// Clear Receive Data Register Not Empty FlagUSART_ClearITPendingBit(USART1,USART_IT_RXNE);uart_buffer[i++] = USART_ReceiveData(USART1);if(i == 100)i = 0;}}int main(void){static uint8_t ch;// Init a 1ms timer interrupt, for Delay function implementation.RCC_GetClocksFreq(&RCC_Clocks);SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);// Enable USART1 and GPIOA clockRCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE);GPIO_Configuration();USART_Configuration();NVIC_Configuration();ch = 'A';while(1){Delay(50);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_SendData(USART1, ch);ch++;}}/*** @brief Inserts a delay time.* @param nTime: specifies the delay time length, in 1 ms.* @retval None*/void Delay(__IO uint32_t nTime){TimingDelay = nTime;while(TimingDelay != 0);}/*** @brief Decrements the TimingDelay variable.* @param None* @retval None*/void TimingDelay_Decrement(void){if(TimingDelay != 0x00){TimingDelay--;}}
它是可以在 STM32F030 上调试通过的串口收发测试程序,发送采用延时循环,接收采用中断,接收到的数据存入缓冲区。
有很多比较认真的实战派的同学估计会下载到板子上跑一跑,它确实能跑通,看起来也没什么问题。很多教程甚至官方的代码都是类似的处理方法。
但这确实有点儿像陷马坑,看似一马平川,跑着跑着突然连马带人 kucha 一声掉坑里了。这还真不是开玩笑,某知名楼宇自控公司的产品就在安装到客户现场后,经常莫名奇妙的死机。查来查去,查去查来,才发现问题。可是解决起来不容易啊,一个一个的去拆开,更新代码,想想都。。。
所以同学们不要轻视任何一段代码啊!
这段代码的问题是,如果接收数据之间间隔时间较长,可以正常收数据。但是如果对方发送数据非常快,或者偶尔在自己还没从串口接收寄存器取走数据的时候突然又来了数据,会导致 Overrun 标志位的置位。这个标志位一置,串口基本上就罢工了。所以,在程序中一定要有对异常情况的处理。甚至觉得不会发生的异常也不要置之不理。(想一想为什么要填充Flash的空白区域?在正常情况下代码永远不会跑到空白区域是吧。)
对串口异常的处理可以参考下面中断处理函数代码。当然也可以在主程序中定时处理,以便在中断失效的情况下还能恢复。
// USART1 Interrupt Handlervoid USART1_IRQHandler (void){static uint8_t i = 0;if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET){// Clear Overrun Error FlagUSART_ClearFlag(USART1, USART_FLAG_ORE);}else if(USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET){// Clear Noise Error FlagUSART_ClearFlag(USART1, USART_FLAG_NE);}else if(USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET){// Clear Framing Error FlagUSART_ClearFlag(USART1, USART_FLAG_FE);}else if(USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET){// Clear Parity Error FlagUSART_ClearFlag(USART1, USART_FLAG_PE);}else if(USART_GetITStatus(USART1,USART_IT_RXNE)!= RESET){// Clear Receive Data Register Not Empty FlagUSART_ClearITPendingBit(USART1,USART_IT_RXNE);uart_buffer[i++] = USART_ReceiveData(USART1);if(i == 100)i = 0;}}
赞 (0)
