介绍一下完整接收一帧数据的方法

本文介绍3种使用串口接受一帧完整数据包的方法,串口接收数据是字节接收的,串口每接收1字节数据,产生一个串口中断,我们在中断中将接收到的数据存放到buf中进行保存,但是数据的发送和接收都是按照帧为单位进行传输的,因此我们要在接收数据的同时判断当前接收的数据是否是完整的一帧。
一般串口完整数据帧的定义:帧头(2字节,例如AA、BB) + 数据长度(2字节) + 数据 + CRC16校验(2字节) + 帧尾(2字节)
帧头、帧尾表示一帧数据的开始和结尾,数据长度表示当前数据帧中负载数据大小,CRC16校验用来检查接收到的数据是否正确。
第一种方法(根据帧头、帧尾进行判断):
串口在接收数据时,我门在串口中断函数中对接收到的每一字节数据进行判断,如果检测到帧头数据(例如AA、BB),我们开始将接收到的数据存到buf中,同时记录下该帧数据的数据长度字段,然后一直接收,直到接收到的数据长度与我们记录下的数据长度字段值一致或接收到帧尾数据,到此一帧数据接收完成,将数据扔到消息队列,等待任务处理即可。
假如接收的数据包格式如下 帧头(AA 、BB) + 数据长度 + 数据 + CRC校验 + 帧尾(CC、DD)
void USART1_IRQHandler(void) //串口中断处理函数
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
buf[buf_size++] = USART_ReceiveData(USART1);
if (buf_size >= 2)
{
if (buf[0] == 0xAA && buf[1] == 0xBB) //接收到帧头
{
//接收到帧尾
if (buf[buf_size] == 0xCC && buf[buf_size-1] == 0xDD)
{
//此处为数据包处理逻辑
buf_size = 0;
memset(buf,0,BUF_SIEZ);
}
}
else
{
buf_size = 0;
memset(buf,0,BUF_SIZE);
}
}
if(buf_size >= BUF_SIZE)
{
buf_size = 0;
memset(buf,0,BUF_SIZE);
}
}
}
第二种方法(根据接收到的字符之间的间隔进行判断):
串口数传输都是使用标准波特率,因此串口传输一帧数据时,字符与字符之间的时间间隔是一个固定值,我们可以根据串口的波特率去计算串口每个字符的间隔时间,在数据接收的过程中判断当字符间隔大于3.5个(modbus协议常用),则认为当前数据帧传输完毕,具体方法如下:
我们先设置定时器超时时间为计算出的3.5字符间隔时间,然后在串口中断中每接收到一个字符,就将其保存至buf中,并刷新定时器计数值,如果串口接收到的数据时间间隔大于3.5个字符间隔,定时器就会进入超时中断,我们在定时器中断中判断当前buf中的数据是否完整,如果完整,则扔到消息队列中,等待任务去处理。
//本例中,如果串口字符间隔大于3ms,我们认为一帧数据接收完毕,如果使用的协议是Modbus 协议,则时间间隔应该设置为3.5字符间隔时间。
#define BUF_SIZE 128 // 定义串口接收buf 长度
typedef enum {DISABLE = 0, ENABLE = !DISABLE} ; //定义枚举类型
u16 buf_size = 0;
u8 buf[BUF_SIZE] = {0}; //定义串口接收缓存区
u16 TimerCount = 0;
u8 TimerEnable = ENABLE; //定义定时器计数使能标志位
void USART1_IRQHandler(void) //串口中断处理函数
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断是否接收中断标志位置位
{
buf[buf_size++]= USART_ReceiveData(USART1); //将接收到的数据存入buf
TimerCount=0;
TimerEnable = ENABLE; //置位定时器计数使能标志位
if(buf_size >= BUF_SIZE)
{
buf_size = 0; //接收数据缓冲区溢出,重新开始接收
memset(buf,0,BUF_SIZE);
}
}
}
void TIM1_IRQHandler(void) //定时器中断处理函数 每1ms产生一次中断
{
u8 cnt = 0;
if(TIM_GetITStatus(TIM1 , TIM_IT_Update) != RESET )
{
TIM_ClearITPendingBit(TIM1 , TIM_FLAG_Update); //清除定时器中断标志位
if(TimerEnable == ENABLE)
{
TimerCount++;
if(TimerCount > 3) //大于3ms,则判断为一帧数据接收完成
{
TimerCount = 0;
Timer.Enable = DISABLE;
//此处为数据包处理逻辑
buf_size = 0;
memset(buf,0,BUF_SIZE);
}
}
}
}
第三种方法(使用串口帧空闲中断,推荐使用):
串口IDLE中断,串口接收完完整的一帧数据自身产生的中断,配置使能该中断后,串口会判断总线上一个字节的时间间隔内有没有再次接收到数据,如果没有则当前一帧数据接收完成,产生IDLE中断。
使用方法,原串口配置不变,添加下列语句,开启IDLE中断:
#define BUF_SIZE 128 // 定义串口接收buf 长度
u16 buf_size = 0;
u8 buf[BUF_SIZE] = {0}; //定义串口接收缓存区
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口帧空闲中断
void USART1_IRQHandler(void) //串口中断服务函数
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
buf[buf_size++] = USART_ReceiveData(USART1);
}
if(buf_size >= BUF_SIZE )
{
buf_size = 0; //接收缓冲区溢出,重新开始接收
memset(buf,0,BUF_SIZE);
}
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //当前为接收到一帧完整的数据包
{
USART1->SR; //先读SR
USART1->DR; //再度DR 清除帧空闲中断标志位
//此处为数据包处理逻辑
buf_size = 0;
memset(buf,0,BUF_SIZE);
}

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: HAL库提供了许多关于串口的函数,可以通过串口完成一帧数据接收,具体步骤如下: 1. 定义接收缓冲区和接收计数器。缓冲区的大小至少应该是一帧数据的大小,接收计数器用于统计已经接收了多少字节。 2. 配置串口,设置波特率、奇偶校验等参数。可以使用HAL库提供的函数来完成串口配置。 3. 开启串口中断。在接收数据过程中,串口会不断产生中断,通过中断处理函数来处理接收数据。 4. 编写中断处理函数。在中断处理函数中,首先读取接收寄存器中的数据,然后将数据存储到接收缓冲区中。接着判断是否接收了一帧完整数据,如果数据接收完整,则对数据进行处理,否则继续等待数据。 5. 在主函数中循环检测数据是否接收完成。当接收计数器等于一帧数据的大小时,认为数据接收完整,可以进行下一步操作。 总之,在使用HAL库进行串口接收一帧数据时,需要定义接收缓冲区和接收计数器,配置串口,开启串口中断,编写中断处理函数和在主函数中循环检测数据是否接收完成。 ### 回答2: HAL库是基于STM32标准固件库的一个高级抽象层,提供了丰富的功能接口,方便了开发者针对STM32微控制器开发应用程序。下面我将介绍使用HAL库串口接收一帧数据的步骤。 1. 配置串口:首先我们需要配置串口接口,确定波特率、数据位、停止位等参数。 ``` /* UART7初始化设置 */ huart7.Instance = UART7; huart7.Init.BaudRate = 115200; huart7.Init.WordLength = UART_WORDLENGTH_8B; huart7.Init.StopBits = UART_STOPBITS_1; huart7.Init.Parity = UART_PARITY_NONE; huart7.Init.Mode = UART_MODE_RX; huart7.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart7.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart7) != HAL_OK) { Error_Handler(); } ``` 2. 等待数据:使用`HAL_UART_Receive()`函数等待数据,当有数据到达时会调用中断处理函数。 ``` /* 在主函数中调用 */ HAL_UART_Receive_IT(&huart7, rx_buffer, 10); // 接收10字节 ``` 3. 中断处理函数:数据到达时会调用中断处理函数,在中断处理函数中读取数据。 ``` /* 在stm32f4xx_it.c中实现中断处理函数 */ void UART7_IRQHandler(void) { /* 判断是否是接收中断 */ if(__HAL_UART_GET_FLAG(&huart7, UART_FLAG_RXNE) != RESET) { HAL_UART_IRQHandler(&huart7); } } /* 重新定义HAL库中的回调函数 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* 在此处读取数据 */ } ``` 这样,我们就可以使用HAL库串口接收一帧数据了。需要注意的是,串口使用时需要根据实际情况进行配置,同时在中断处理函数中读取数据时需要特别小心,防止丢失数据。 ### 回答3: HAL库是一种使嵌入式系统的开发更容易的库。HAL库的串口接收函数包括如下几个步骤: 1. 创建串口句柄。首先,需要定义一个USART_HandleTypeDef的结构体,在结构体中设置串口的参数(波特率、数据位数、停止位数等),然后调用HAL_UART_Init函数,创建一个串口句柄。 2. 在串口中断回调函数中获取数据。HAL库的串口接收是通过中断来完成的。每当有数据到达串口寄存器时,就会触发一个中断。HAL库中已经提供了中断回调函数,在其中获取数据。当数据成功接收到后,会调用USARTx_IRQHandler回调函数。 3. 在中断回调函数中读取数据。通过句柄中的Instance属性,能够轻易地找到所对应的串口,并从中读取数据。通常情况下,我们从串口缓冲区中获取数据,并将其存储在一个缓存数组中,直到接受完毕。在获取数据的时候,需要考虑到数据可能会分帧接收,因此需要作出相应的操作,以区分数据帧的开始和结束。 4. 对数据帧进行解析。在帧数据中,需要解析出数据的有效负载,并对其进行相应的处理。处理的方式因应用而异,通常情况下可以通过协议完成这一操作。 5. 结束串口接收。一旦一个数据帧被接收并处理,需要清空缓存区并准备接收下一帧。当所有的数据都被接受完毕后,应该调用HAL_UART_Receive_IT函数重新开启串口接收中断。 需要指出的是,串口接收并不总是一帧一帧地进行,可能会出现部分帧或多帧数据同时到达串口,此时需要在中断回调函数中分析数据完整性,对数据进行处理,确保正在处理的是一完整数据,避免出错。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姚晋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值