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

发布者:红尘清梦最新更新时间:2018-12-10 来源: eefocus关键字: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;

 

 

  }


关键字:STM32  串口  空闲中断  不定长数据 引用地址:STM32的串口空闲中断接收不定长数据

上一篇:STM32F103RCT6+串口DMA方式接收定长数据
下一篇:STM32 定时器输出比较模式和PWM输出模式的区别

推荐阅读最新更新时间:2024-03-16 16:19

基于STM32两轮自平衡小车
一、硬件介绍 主控芯片用的是100脚的STM32F103VET6,陀螺仪用的是MPU6050,电机驱动用的是TB6612,蓝牙是汇承的HC05邮票孔封装的,WIFI用的是济南有人科技的USR-WIFI232-S,小车底盘用的是平衡小车之家的某一款带编码器的(不是我买的,同学的),电池用的是一节7.2的镍镉电池,液晶用的是中景园电子1.3寸IIC接口的OLED,开关用的是三脚纽子开关,电池接口用的是T插,电阻电容这些用的基本上是0603封装,编码器5V降压用的是ASM1117-5.0,3.3V降压用的是SP6203,拨码开关用的是4P贴片式2.54mm角距的,按键是两脚贴片,microusb接口用的是5针 7.2四脚插板牛角
[单片机]
基于<font color='red'>STM32</font>两轮自平衡小车
STM32 通用定时器的几种配置方式
1、普通定时使用 #include stm32f10x.h #include time.h static Time_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0000); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = TIMx_IRQn; NVIC_InitStructure.NVIC_IRQCha
[单片机]
STM32使用STM32CubeMX配置烧录后无法识别设备的解决方法
近来闲下来了,想用下STM32CubeMX,咱也赶赶潮流,摸索了一下,点了个灯,完美。再把灯灭了吧,但发现找不到SW设备。这是怎么回事?我重新设置了Keil,不行;重新用STM32CubeMX生成,也不行。难道我的板就这样废了吗?我在网上寻找答案,最后锁定应该是SW端口没设置好,导致无法烧录。经过一番搜寻和探索,最终得到了解决方法。 造成SW失效的原因是我在配置STM32CubeMX时没有配置SW,如图所示,找到SYS选项里的Debug,选择你的下载器。 那么在无法下载程序的情况下我们又该如何下载呢?我的方法是按住复位按钮,点Keil的下载。当然有的人是没引出复位按钮的,那么也可以通过设置boot来操作,具体
[单片机]
<font color='red'>STM32</font>使用STM32CubeMX配置烧录后无法识别设备的解决方法
STM32-Keil软件仿真和硬件仿真/在线仿真
软件仿真和硬件仿真什么区别?软件仿真就是没有硬件参与的仿真,完全是模拟实现的。硬件仿真是将程序下载到控制芯片的FLASH或RAM中,直接在硬件上实现仿真。【有什么问题欢迎联系讨论,一起解决问题】 仿真这种东西,因为涉及到信任问题,用的好觉得好用,用不好可能会徒增麻烦——“还不如直接在硬件上调试靠谱”。但是总体上,仿真还是比较有用的,比如在排查软件问题(寄存器配置等)的时候,使用软件仿真是非常靠谱的。而如果涉及到硬件的问题(比如你的板子代码需要读取外部信号,或者输出信号等),可能需要用到硬件仿真,或者说在线仿真。关于仿真,网上的资料说的还是挺全的,这里只做总结。 一、软件仿真 1.1 仿真配置 首先确定仿真的硬件环境。点击魔
[单片机]
STM32-Keil软件仿真和硬件仿真/在线仿真
基于STM32的家庭服务机器人系统工作原理及设计
为解决当前大部分家庭服务机器人不具备行走功能或只具有简单的避障能力等问题,本文设计一种基于STM32的家庭服务机器人系统。整个系统由轮式机器人、XBee协调器、RFID智慧地板和上位机组成。轮式机器人由主板、传感器模块、射频模块、舵机模块、电源模块和机器人金属主体组成,传感器模块包括电子罗盘、红外传感器和RFID读卡器。主板以ARMCortex-M3内核微处理器STM32F103VCT6为核心进行开发,采集传感器模块数据信息,实现机器人与XBee协调器之间的通信连接。该系统在模拟智能家居的环境下通过自主决策稳定和高效地完成设定任务,能够满足家庭服务机器人的应用要求。 随着人工智能和传感器技术的发展,机器人已从工厂的结构化环境进入人
[单片机]
基于<font color='red'>STM32</font>的家庭服务机器人系统工作原理及设计
基于STM32的平衡小车设计过程
一、简介 接触STM32开发一段时间了,想用STM32做一个有意思的项目,经历了无数的调参调参再调参,终于让它站稳了,接一下就一步步的跟大家介绍一下,项目的整体实现过程— 二、项目介绍 STM32平衡小车是一种基于STM32芯片的智能小车,它可以通过自动控制来保持平衡,使其可以在不同的地形上稳定行驶。其使用范围非常广泛。需要用到一些基本的硬件组件,例如电机、轮子、陀螺仪、加速度计、电池等。通过设计的电路板进行连接,组成一个完整的系统。 三、硬件设计 根据上述需求,我进行了电路图设计 四、软件设计 4.1电机驱动编写 4.1.1电机引脚说明 编码电机 引脚说明: M1电机电源线(12V) GND编码器地线 C
[单片机]
基于<font color='red'>STM32</font>的平衡小车设计过程
关于STM32的USB问题
1、stm32f10xusb无法识别 问:我现在用的片子是103t8和开发板的是103vct6的,没有8M的晶振所以我用了6M的但是程序里面我已经改了,12倍频,usb时钟1.5分频我没动,还是开发板的那部分,1.5k的上拉直连3.3,我把开发板控制usben的管教断开直接接了3.3v上拉也可以识别usb我做的板子也改了1。5的上拉接3.3v还是不能识别,两个程序的差别只有外部晶振倍频系数不同求解 答:解决这个办法1、你先用8M晶振试试程序,如果还是不行,就说明当前硬件有问题2、时钟配置的时候,分频晶振、倍频,然后获得系统时钟,建议你检查一下这段代码,确定系统时钟是72M的。
[单片机]
stm32串口发送数据,丢失字节问题分析
STM32 串口 发送 必须 先检测 状态,否则 第一个 字节 无法 发出,发送完毕,必须检测发送状态是否完成,否则,发送不成功, 使用stm32f10x调试串口通讯时,发现一个出错的现象,硬件复位重启之后,发送测试数据0x01 0x02 0x03 0x04..接收端收到的数据为:0x02 0x03 0x04,第一个数据丢失。换成发送别的数值的数据,如0x06 0x0ff,则接收到0x0ff,0x06丢失。错误依旧。 故障排除过程: 1、刚开始怀疑是接收端的错误,我是使用电脑串口,运行串口辅助调试工具接收,换成其他软件后,发现故障依旧,而且电脑软件一直是开启状态,不像和电脑软件有关。 2、使用单步调试,单步运行各个发送指令,都正常
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved