历史上的今天
今天是: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))
史海拾趣
|
摘要:介绍如何用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 ...… 查看全部问答> |
|
小弟刚刚开始学DSP,在CCS4.2环境连接仿真器和开发板时,每次一点击Launch TI Debgger的时候就会出现这个问题,之前都是好的,不知道这个是怎么回事啊?希望各位前辈不吝赐教!… 查看全部问答> |
|
最近我开始学习FPGA,但看了一些教程后,自己很困惑,好像没有在教程中看见不同的芯片之间有什么不同,我以前一直都是学习单片机的,习惯上了处理器外设的这种配置,我知道FPGA的一切都需要自己设计,但不同的FPGA之间都有那些区别啊?我就知道他们 ...… 查看全部问答> |
|
本人是大二学生,想学习做一个控制电路。。。要用到AD转换 pwm输出等。。 想问问如何入门学习 现在已经学完微机原理 数电 电路 要看什么书,用什么开发板,要不要仿真器? 希望各位多多指教… 查看全部问答> |




