单片机(MCU)如何才能不死机之串口Overrun

简介: 单片机(MCU)如何才能不死机之串口Overrun

闲言少叙,先上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 Configuration
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1);       // Tx PA9
  GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_1);      // Rx PA10
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;      // USART1_TX | USART1_RX
  GPIO_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 Configuration
void 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 Configuration
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  // USART1 interrupt Config
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
// USART1 Interrupt Handler
void USART1_IRQHandler (void)
{
  static uint8_t i = 0;
  if(USART_GetITStatus(USART1,USART_IT_RXNE)!= RESET)
  {// Clear Receive Data Register Not Empty Flag
   USART_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 clock
  RCC_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 Handler
void USART1_IRQHandler (void)
{
  static uint8_t i = 0;
  if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET) 
  {// Clear Overrun Error Flag
    USART_ClearFlag(USART1, USART_FLAG_ORE);
  }
  else if(USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET)
  {// Clear Noise Error Flag
    USART_ClearFlag(USART1, USART_FLAG_NE);
  }
  else if(USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET)
  {// Clear Framing Error Flag
    USART_ClearFlag(USART1, USART_FLAG_FE);
  }
  else if(USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET)
  {// Clear Parity Error Flag
    USART_ClearFlag(USART1, USART_FLAG_PE);
  }
  else if(USART_GetITStatus(USART1,USART_IT_RXNE)!= RESET)
  {// Clear Receive Data Register Not Empty Flag
    USART_ClearITPendingBit(USART1,USART_IT_RXNE);
    uart_buffer[i++] = USART_ReceiveData(USART1);
    if(i == 100)
      i = 0;
  }
}


相关文章
|
4月前
|
芯片
STC15F100E单片机模拟串口
STC15F100E单片机模拟串口
STC15F100E单片机模拟串口
|
编译器 芯片
单片机(MCU)如何才能不死机之对齐访问(Aligned Access)
单片机(MCU)如何才能不死机之对齐访问(Aligned Access)
|
3月前
单片机IO口模拟串口实现原理
单片机IO口模拟串口实现原理
45 5
|
4月前
|
存储 小程序 中间件
单片机中MCU跑RTOS相比裸机的优势
单片机中MCU跑RTOS相比裸机的优势
59 1
单片机 (MCU) 如何才能不死机之 IIC 操作
单片机 (MCU) 如何才能不死机之 IIC 操作
|
4月前
|
网络协议 Linux
嵌入式单片机开源的串口示波器实现方法
嵌入式单片机开源的串口示波器实现方法
50 0
|
存储 传感器 程序员
STM32F0单片机快速入门三 MCU启动过程
STM32F0单片机快速入门三 MCU启动过程
STM32F0单片机快速入门六 用库操作串口(UART)原来如此简单
STM32F0单片机快速入门六 用库操作串口(UART)原来如此简单
STM32F0单片机快速入门七 串口(UART)操作从轮询到中断
STM32F0单片机快速入门七 串口(UART)操作从轮询到中断
STM32F0单片机快速入门七 串口(UART)操作从轮询到中断
|
监控 芯片
单片机如何才能不死机之内外部时钟
单片机如何才能不死机之内外部时钟

热门文章

最新文章