历史上的今天
返回首页

历史上的今天

今天是:2024年08月28日(星期三)

正在发生

2019年08月28日 | STM32 Uart 接收不定长数据

2019-08-28 来源:eefocus

      前面讲了Uart三种不同的方式接收数据,请参照《STM32 Uart及其配置》《STM32 Uart中断接收》《STM32 Uart DMA方式接收数据》,但是,它们都需要指定数据的长度,但实际应用中,会出现不定长度的数据,比如,某些模块的@命令,那么,如何接收不定长度的数据呢?今天,我们就来扒一扒STM32 Uart 变长数据的接收。


      问题来了,变长数据包,我们如何确定数据包的长度?


      带着问题思考,我们可以得出以下几种思路:


      1. 制定严格的通信协议,带有一串特殊字符的数据包头、长度、校验、包尾等,比如,一开始收到:0xAA-0xBB-0xCC-0xDD-0xEE,表示数据包头,接下来两个字节是数据包长度,接下来是数据包内容,接下来是校验值,接下来是结尾。


      2. 使用定时器,判定在指定时间内没有收到数据,就算一个数据包结束。可以在收到一个数据后,开启定时器,定时器的超时时间设定为1~2个数据之间的时间,若在此期间有数据,则判定为数据包未结束,重载定时器,若此期间无数据,则判定为数据包结束,关闭定时器。


      这篇章里不讨论1和2,因为STM32有更方便的处理方式,STM32能够检测空闲。


      看RM0033,空闲,可以理解为,下一个起始位之前的全部1,也就是Rx线上高电平。


      会不会和数据0xFF冲突呢?不会,因为数据0xFF有起始位,空闲没有。


      看下来这个手画的图,中间那一段高电平,就是空闲。


      

      当空闲被检测到时,如果IDLEIE位设置,就会产生一个IDLE中断。


      我们捕获这个中断,并处理它就行了。

      在空闲中断产生之前,我们收到的数据,怎么处理呢?先收着,存进一个Buffer里面!


      如前面讲的《STM32 Uart中断接收》《STM32 Uart DMA方式接收数据》,接收数据,也可以用中断和DMA两种不同的方式。我们把两种方式都写进代码里面,用两个不同的宏定义区分开来。


      我们就写一个,收到数据,往串口发送出收到的数据长度,以及完整的数据吧。


      理论讲完,就开始实践吧!


      参照《STM32 Uart DMA方式接收数据》,建立工程,生成代码。


   在 usart.c 里面写这样一段代码,#define RCV_RXNEIDLE_PROCESS 表示用RX中断方式接收数据,#define RCV_DMAIDLE_PROCESS 表示用DMA方式接收数据。这段代码主要用于选择接收数据的方式。同一时间只能使用一种方式,#error这里做了个限制,不允许同时打开这两个宏定义,否则会编译出错。


// select oneof two method

//#define RCV_RXNEIDLE_PROCESS

#define RCV_DMAIDLE_PROCESS

 

#if defined(RCV_DMAIDLE_PROCESS)&&defined(RCV_RXNEIDLE_PROCESS)

    #error "Don't Allow #define Two Micro at the same time, Check RCV_RXNEIDLE_PROCESS && RCV_DMAIDLE_PROCESS"

#endif

      接收数据,就定义一个数组来存放数据,就申请1024个字节的数组吧;


      数据要变长,就定义一个变量来表示接收到的数据长度;


      最后,再定义一个指针,用来指向存放数据的数组。


      在 usart.c 里面,这一断代码定义了所需要的数据。


uint8_t uart4Rx[UART4_BUF_MAX];      // 存放接收到的数据

uint8_t *pUart4Rx;    // 指向存认数据的数组

uint16_t uart4RxLength;     // 接收到数据的长度

      整个事件的流程就是,接收一段数据后,产生IDLE中断,IDLE中断服务例程里处理数据。


      那我们就看一下stm32f2xx_it.c里面UART4_IRQHandler()中断服务例程里面,对Idle中断是怎么处理的?


      看一下 HAL_UART_IRQHandler() 这个函数,它对Idle中断没有处理!那怎么办呢?自己写一段咯。


      在HAL_UART_IRQHandler() 函数加上这一段。


/* UART in mode Idle -------------------------------------------------*/

if(((isrflags & USART_SR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))

{

    HAL_UART_IdleCpltCallback(huart);      

    return;

}  

      这个回调函数HAL_UART_IdleCpltCallback(),仿照着在stm32f2xx_hal_uart.c里面加一个回调函数。


      后面,我们在 usart.c 里面重写它。


/**

  * @brief  Idle callbacks.

  * @param  huart pointer to a UART_HandleTypeDef structure that contains

  *                the configuration information for the specified UART module.

  * @retval None

  */

__weak void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)

{

  /* Prevent unused argument(s) compilation warning */

  UNUSED(huart);

  /* NOTE: This function Should not be modified, when the callback is needed,

           the HAL_UART_TxCpltCallback could be implemented in the user file

   */

}

 


      整个HAL_UART_IRQHandler()函数,是这样的。


void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)

{

   uint32_t isrflags   = READ_REG(huart->Instance->SR);

   uint32_t cr1its     = READ_REG(huart->Instance->CR1);

   uint32_t cr3its     = READ_REG(huart->Instance->CR3);

   uint32_t errorflags = 0x00U;

   uint32_t dmarequest = 0x00U;

 

  /* If no error occurs */

  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));

  if(errorflags == RESET)

  {

    /* UART in mode Receiver -------------------------------------------------*/

    if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))

    {

      UART_Receive_IT(huart);

      return;

    }

  }  

 

  /* If some errors occur */

  if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))

  {

    /* UART parity error interrupt occurred ----------------------------------*/

    if(((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))

    {

      huart->ErrorCode |= HAL_UART_ERROR_PE;

    }

    

    /* UART noise error interrupt occurred -----------------------------------*/

    if(((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))

    {

      huart->ErrorCode |= HAL_UART_ERROR_NE;

    }

    

    /* UART frame error interrupt occurred -----------------------------------*/

    if(((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))

    {

      huart->ErrorCode |= HAL_UART_ERROR_FE;

    }

    

    /* UART Over-Run interrupt occurred --------------------------------------*/

    if(((isrflags & USART_SR_ORE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))

    {

      huart->ErrorCode |= HAL_UART_ERROR_ORE;

    }

 

    /* Call UART Error Call back function if need be --------------------------*/    

    if(huart->ErrorCode != HAL_UART_ERROR_NONE)

    {

      /* UART in mode Receiver -----------------------------------------------*/

      if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))

      {

        UART_Receive_IT(huart);

      }

 

      /* If Overrun error occurs, or if any error occurs in DMA mode reception,

         consider error as blocking */

      dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);

      if(((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)

      {

        /* Blocking error : transfer is aborted

           Set the UART state ready to be able to start again the process,

           Disable Rx Interrupts, and disable Rx DMA request, if ongoing */

        UART_EndRxTransfer(huart);

        

        /* Disable the UART DMA Rx request if enabled */

        if(HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))

        {

          CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

          

          /* Abort the UART DMA Rx channel */

          if(huart->hdmarx != NULL)

          {

            /* Set the UART DMA Abort callback :

               will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */

            huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;

            if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)

            {

              /* Call Directly XferAbortCallback function in case of error */

              huart->hdmarx->XferAbortCallback(huart->hdmarx);

            }

          }

          else

          {

            /* Call user error callback */

            HAL_UART_ErrorCallback(huart);

          }

        }

        else

        {

          /* Call user error callback */

          HAL_UART_ErrorCallback(huart);

        }

      }

      else

      {

        /* Non Blocking error : transfer could go on.

           Error is notified to user through user error callback */

        HAL_UART_ErrorCallback(huart);

        huart->ErrorCode = HAL_UART_ERROR_NONE;

      }

    }

    return;

  } /* End if some error occurs */

 

// 这里,就是对Idle中断的处理啊啊啊啊啊!!!

     /* UART in mode Idle -------------------------------------------------*/

    if(((isrflags & USART_SR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))

    {

        HAL_UART_IdleCpltCallback(huart);      

      return;

    }  

 

  /* UART in mode Transmitter ------------------------------------------------*/

  if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))

  {

    UART_Transmit_IT(huart);

    return;

  }

  

  /* UART in mode Transmitter end --------------------------------------------*/

  if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))

推荐阅读

史海拾趣

Helicomm公司的发展小趣事

在电子行业中,Helicomm公司凭借其深厚的技术积累和不断创新的精神,逐渐崭露头角并发展成为业界的佼佼者。以下是关于Helicomm公司发展起来的五个相关故事:

1. ZigBee技术的引领者

自2002年成立以来,Helicomm公司便致力于ZigBee及IEEE 802.15.4标准的研究与产品开发,成为全球ZigBee技术的引领者之一。公司不仅成功完成了IEEE 802.15.4和ZCP的双重资格认证,还推出了多款经过FCC认证的ZigBee模块,广泛应用于市政、安全、监测、物流、定位等领域。Helicomm的ZigBee模块为客户搭建了一条从芯片到应用的快速通道,极大地简化了无线产品的开发流程。(信息来源:百度百科)

2. 技术并购与子公司成立

2005年,Helicomm公司完成了对尉普(WirelessPlug)科技股份有限公司的并购,并成立了赫立讯科技(北京)有限公司,作为其在中国的全资子公司。这一举措不仅增强了Helicomm在全球无线通讯市场的竞争力,还为其在中国市场的拓展奠定了坚实的基础。赫立讯科技继承了尉普在无线传输终端产品方面的技术积累,进一步推动了ZigBee等无线通讯技术在中国的应用与发展。(信息来源:安阳市菲云网络科技有限公司网站)

3. 参与国际标准制定

作为ZigBee联盟的创始会员之一,Helicomm公司积极参与了ZigBee标准的制定与推广工作。通过与国际同行的紧密合作与交流,Helicomm不断提升自身在无线通讯领域的技术实力和影响力。其产品在符合国际标准的同时,也融入了公司的独特创新,满足了不同行业客户的多样化需求。(信息来源:综合多个来源)

4. 工业无线监控系统的创新

近年来,Helicomm公司联合柏元网控、研华科技等合作伙伴,共同推出了针对油田、水利等行业的工业无线监控系统方案。该系统方案充分利用了Helicomm在ZigBee、GSM/GPRS/CDMA等无线通讯技术方面的优势,结合合作伙伴在软件监控、硬件采集控制等方面的专长,为相关行业提供了高效、可靠的无线监控解决方案。这一创新不仅提升了行业的智能化水平,也进一步巩固了Helicomm在无线通讯领域的领先地位。(信息来源:重庆市智畅行物联科技有限公司网站)

5. 国际市场的拓展

Helicomm公司的产品不仅在中国市场取得了显著成绩,还成功打入国际市场,赢得了广泛的认可。公司凭借其先进的技术、稳定的产品性能和优质的本地化服务,在国际市场中建立了一席之地。特别是在水利、交通、环保、气象等领域,Helicomm的无线通讯产品得到了广泛应用,为全球客户提供了可靠的无线通讯解决方案。(信息来源:综合多个来源)

以上五个故事展示了Helicomm公司在电子行业中的发展历程和成就,体现了其在无线通讯领域的深厚技术实力和不断创新的精神。

GTE Microcircuits公司的发展小趣事
复合放大器相比单放大器具有更高的增益稳定性、更低的噪声、更宽的带宽以及更强的驱动能力等优势。这些优势使得复合放大器在需要高性能放大的场合更具竞争力。
富芯森美(FUXINSEMI)公司的发展小趣事

为了进一步提升公司的竞争力和市场地位,富芯森美积极寻求资本市场的支持。通过上市融资等方式,公司获得了充足的资金支持,为技术研发、市场拓展和产能扩张提供了有力保障。同时,资本市场的关注也进一步提升了富芯森美的品牌价值和市场影响力。在资本市场的助力下,富芯森美实现了快速发展,成为了电子行业中一颗璀璨的明星。

Alpha (Taiwan)公司的发展小趣事

面对全球环保意识的日益增强,Alpha (Taiwan)公司积极响应号召,致力于绿色电子产品的研发和生产。公司投入大量资金研发环保材料和技术,成功推出了一系列绿色环保电子产品。这些产品不仅具有优异的性能,而且在使用过程中对环境的影响较小,符合现代社会的环保理念。此外,公司还积极参与环保公益活动,宣传环保知识,推动电子行业的绿色发展。

DURATOOL公司的发展小趣事

DURATOOL公司自成立以来,一直致力于电子工具的技术创新。在20世纪90年代,公司研发出了一款具有革命性的数字万用表,该产品在精确度、稳定性和操作便捷性方面均大幅超越当时的市场主流产品。这一创新不仅为DURATOOL公司赢得了大量忠实用户,也为其在电子测量工具市场树立了技术领先的形象。随后,DURATOOL公司不断加大研发投入,推出了一系列具有竞争力的新产品,逐步巩固了其在电子工具行业的领先地位。

CONEC公司的发展小趣事

1978年,CONEC在利普施塔特创立,标志着公司电子业务的起点。创立初期,公司可能面临着资金、技术和市场等多方面的挑战。然而,凭借着对电子行业的深刻理解和坚定的创业决心,CONEC逐渐在市场中站稳脚跟。1980年,DIN EN 60603系列产品的成功上线生产,为公司赢得了初步的声誉和市场认可,为后续的发展奠定了坚实基础。

问答坊 | AI 解惑

用PowerPC860实现FPGA配置

摘要:介绍如何用PowerPC860(MPC860)进行FPGA(Xilinx的Virtex-II系列)的配置;给出进行FPGA配置所需的详细时序图和原理图。本配置基本原理对其它FPGA的配置也适用。 关键词:PowerPC860 FPGA Xilinx 1 概述 MPC860是基于PowerPC结构的通信控 ...…

查看全部问答>

关于USB MassStorage Client Function的几个问题?

在WinCE上实现优盘的功能已经实现了,现在我有几个小问题想请教一下? 1、usbmsfn.dll是MassStorage Client Function对应的驱动文件,这个文件的源代码在哪里,如何编译? 我通过查阅以前的帖子,发现可以通过sysgen_capture usbmsfn 得到对应的s ...…

查看全部问答>

关于内部数据寄存器的问题

在keilC51里,用了可以位寻址的存储器,可是工程芯片设成89C51时编译可以通过,就是不能实现功能(在电脑上通过串口仿真)。只有把芯片改成89C52才可以实现。 在平凡单片机书上也写了用到内部数据存储器时工程里需要把芯片设成52系列的。 不知道 ...…

查看全部问答>

为什么取不到串口输入缓冲区的值?

用MSCOMM32.OCX,串口初始化和输出都正常,但不能取输入缓冲区的值,为什么? 步骤如下 1)用SUDT SerialNull 软件虚拟了两个串口com15,com16 2)程序对com15,com16初始化,com15用做模拟接收,com16用做模拟发送 Ole_MSC.object.CommPort=15  &nb ...…

查看全部问答>

蓝牙控制器价格?

做一个蓝牙常用控制器价格如何?…

查看全部问答>

CCS4.2中debug的问题

小弟刚刚开始学DSP,在CCS4.2环境连接仿真器和开发板时,每次一点击Launch TI Debgger的时候就会出现这个问题,之前都是好的,不知道这个是怎么回事啊?希望各位前辈不吝赐教!…

查看全部问答>

急!!求高手解答

想问下inverse z transform变换 分母如果没有常数能变换么…

查看全部问答>

不同的FPGA芯片之间有那些差别啊?

最近我开始学习FPGA,但看了一些教程后,自己很困惑,好像没有在教程中看见不同的芯片之间有什么不同,我以前一直都是学习单片机的,习惯上了处理器外设的这种配置,我知道FPGA的一切都需要自己设计,但不同的FPGA之间都有那些区别啊?我就知道他们 ...…

查看全部问答>

新手怎样入门msp430

本人是大二学生,想学习做一个控制电路。。。要用到AD转换 pwm输出等。。 想问问如何入门学习 现在已经学完微机原理 数电 电路 要看什么书,用什么开发板,要不要仿真器? 希望各位多多指教…

查看全部问答>

NRF24L01无线模块

这个模块可以用来无线传输视频数据(摄像头即时传输)吗?…

查看全部问答>