STM32的串口空闲中断接收不定长数据

最近想尝试STM32 通过DMA收发数据,网上找了不少参考文章,最后参考https://blog.csdn.net/youmeichifan/article/details/51750435?utm_source=dlogxgwz2 博文中的方法。

按照此文的方法实现了串口的收发,但是实际使用中发现:

接收空闲中断的产生是在数据接收停止一个字节时产生的,但是有时由于上位机编写问题或硬件问题(本人用到的USB转串口的硬件有问题)上位机发送数据不连续,中间有时间间隔大于一个字节,从而造成无法完整接收数据。通过对空闲中断接收数据方法的分析,重新修改代码,实现规定数据格式的不定长数据的接收。


主要实现方法:
1、定义通讯协议:
 第一个字节为起始符,我用的是0x7B
 第二个字节为数据长度(包含起始符)
2、在接收空闲中断的产生时判断是否接收到合法数据(起始符),判断数据接收长度
3、增加了超时处理

以下是代码:

定义串口结构体:

#define RECEIVELEN 1024
#define USART_DMA_SENDING 1//发送未完成
#define USART_DMA_SENDOVER 0//发送完成


typedef struct
{
 uint8_t receive_flag:1;//空闲接收标记
 uint8_t dmaSend_flag:1;//发送完成标记
 uint16_t rx_len;//接收长度
 uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA接收缓存
 uint16_t timeOutCount;//超时计数
 uint8_t timeOutState;//超时状态 1:允许超时计数 2:超时
}USART_REEIVETYPE;

变量申明:

USART_RECEIVETYPE UsartType1;

串口收发用到的函数:

//DMA发送函数
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
{
 while(UsartType1.dmaSend_flag == USART_DMA_SENDING);
 UsartType1.dmaSend_flag = USART_DMA_SENDING;
 HAL_UART_Transmit_DMA(&huart1, pdata, Length);
}


//DMA发送完成中断回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  __HAL_DMA_DISABLE(huart->hdmatx);
 if(huart->Instance==USART1)
  UsartType1.dmaSend_flag = USART_DMA_SENDOVER;
 if(huart->Instance==USART2)
  UsartType2.dmaSend_flag = USART_DMA_SENDOVER;
}

//串口接收空闲中断
void Usart1Receive_IDLE(UART_HandleTypeDef *huart)
{
 uint32_t temp;
 uint8_t *p;
 uint16_t size;
 if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
 {
  __HAL_UART_CLEAR_IDLEFLAG(&huart1);
  HAL_UART_DMAStop(&huart1);
  temp = huart1.hdmarx->Instance->CNDTR;
   UsartType1.rx_len +=  RECEIVELEN - temp; //计算数据长度,RECEIVELEN - temp为本次长度
  //判断是否为数据开始,判断起始位
  if(UsartType1.usartDMA_rxBuf[0]==0x7b)
  {
   //判断接收数据长度是否符合
   if(UsartType1.rx_len>2)//防止上位机在发了起始位后就有空闲中断产生
   {
    if(UsartType1.usartDMA_rxBuf[1]<=UsartType1.rx_len)//数据接收完整,buff[0]:数据头,buff[1]数据长度(包含头)
    {
     //接收标志位=1,
     UsartType1.receive_flag=1;
     //下次接收缓存指针从头开始,
     p=UsartType1.usartDMA_rxBuf;
     //接收缓存大小=RECEIVELEN,
     size=RECEIVELEN;
     //禁止定时器开始对timeOutCount减计数
     UsartType1.timeOutState=0;
    }
    else
    {
    //下次接收缓存指针为UsartType1.usartDMA_rxBuf+已经接收到的长度,
     p=UsartType1.usartDMA_rxBuf+UsartType1.rx_len;
    //大小为RECEIVELEN-已经接收到的长度,
     size=RECEIVELEN-UsartType1.rx_len;
    //给timeout填值
     UsartType1.timeOutCount=1000;
     UsartType1.timeOutState=1;//允许定时器开始对timeOutCount减计数
    }
   }
   else
   {
    //下次接收缓存指针为UsartType1.usartDMA_rxBuf+已经接收到的长度,
    p=UsartType1.usartDMA_rxBuf+UsartType1.rx_len;
    //大小为RECEIVELEN-已经接收到的长度,
    size=RECEIVELEN-UsartType1.rx_len;
    UsartType1.timeOutCount=1000;
    UsartType1.timeOutState=1;//允许定时器开始对timeOutCount减计数
   }
  }
  else
  {
   UsartType1.rx_len=0;//Reset UsartType1
   p=UsartType1.usartDMA_rxBuf;
   size=RECEIVELEN;
   //禁止定时器开始对timeOutCount减计数
   UsartType1.timeOutState=0;
  }
  HAL_UART_Receive_DMA(&huart1,p,size);//设置DMA接收缓存和大小,为下次接收做准备
  
 }
}

 

超时计数:
我这里用的是SYSTick的中断,每1ms产生一次中断。

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
 if(UsartType1.timeOutCount!=0&UsartType1.timeOutState==1)//USART1超时计数
 {
  UsartType1.timeOutCount--;
  //判断是否发生超时
  if(UsartType1.timeOutCount==0)
  {
   UsartType1.timeOutState=2;
   UsartType1.rx_len=0;
   //超时发生后,重新设置DMA缓存
   HAL_UART_DMAStop(&huart1);
   HAL_UART_Receive_DMA(&huart1,UsartType1.usartDMA_rxBuf,RECEIVELEN);
  }
 }
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  HAL_SYSTICK_IRQHandler();
  /* USER CODE BEGIN SysTick_IRQn 1 */


  /* USER CODE END SysTick_IRQn 1 */
}

 

主程序初始化时,打开串口DMA接收
 

 /* USER CODE BEGIN 2 */
 HAL_UART_Receive_DMA(&huart1, UsartType1.usartDMA_rxBuf, RECEIVELEN);
 __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);


  /* USER CODE END 2 */

while(1)中处理数据:

  //正常接收到数据
  if(UsartType1.receive_flag)
  {
   UsartType1.receive_flag=0;//清零标记
   Usart1SendData_DMA(UsartType1.usartDMA_rxBuf,UsartType1.rx_len);//串口打印收到的数据。
   UsartType1.rx_len=0;
  }
  //此处为超时处理
  if(UsartType1.timeOutState==2)
  {
   
   UsartType1.timeOutState=0;


  }


 

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值