学习一段时间后,写了一段接收代码。
但是在实际运用中发现需要2到3次接收才能更新数据,希望能指点一下,谢谢。
内容如下:
接收一段报文
有报头0x81
数据长度0x01或0x02
指令名0x9?
数据 1~2位
bcc验证 1位
void USART1_IRQHandler(void) //串口中断服务函数
{
static uint8_t RxState = 0;//状态步
static uint8_t count = 0;//接收计数
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
uint8_t RxData = USART_ReceiveData(USART1);
if (RxState == 0)
{
if (RxData == 0x81)
{
RxState = 1; count = 0;
}
else
{
RxState = 0;
}
}
else if (RxState == 1)
{
Serial_Length = RxData; RxState = 2;
}
else if (RxState == 2)
{
Serial_RxPacket[count] = RxData; count++;
if(count == Serial_Length)
{
RxState = 3;
}
}
else if (RxState == 3)
{
Serial_RxPacket[count] = RxData; count++;
if(count > Serial_Length)
{
RxState = 0; Serial_RxFlag = 1;
}
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
中断服务函数有点问题,给你修改了,见下面程序,应该是可以用的,但这种方式不推荐使用,中断服务函数里耽误时间太久,影响整体性能的。
void USART1_IRQHandler(void) //串口中断服务函数
{
static uint8_t RxState = 0;//状态步
static uint8_t count = 0;//接收计数
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除标志位,准备下次接收
uint8_t RxData = USART_ReceiveData(USART1);
if (RxState == 0)
{
if (RxData == 0x81)
{
RxState = 1;
count = 0;
}
else
{
RxState = 0;
}
}
else if (RxState == 1)
{
Serial_Length = RxData;
RxState = 2;
}
else if (RxState == 2)
{
Serial_RxPacket[count] = RxData;
count++;
if(count == Serial_Length+1)//保证数据和命令号接受完成
{
RxState = 3;
}
}
else if (RxState == 3)
{
Serial_RxPacket[count] = RxData; //获得帧尾数据,代表一帧数据接收完成
count++;
RxState = 0;
Serial_RxFlag = 1;
}
}
}
我了解您的问题,关于STM32串口通信中的接收问题,我可以给出以下解决方案:
确认串口通信配置是否正确,包括波特率、数据位、校验位、停止位等;
确认接收代码是否正确,包括接收报头、数据长度、指令名和数据,并进行bcc验证;
考虑使用中断方式进行串口接收,可以提高接收效率并使主程序更加稳定。可以使用CubeMX生成中断服务程序代码,并在主程序中添加中断处理函数,以便及时处理接收到的数据;
在接收代码中增加超时机制,当接收时间超过一定时间仍未接收到完整数据帧时,及时清空接收缓冲区,防止出现粘包或漏包现象;
可以增加一些调试信息,例如打印出接收数据的十六进制表示,便于排查问题。可以使用printf函数或串口发送数据来实现。
针对您的具体问题,我可以给出一份示例代码,如下所示:
#include "stm32f1xx_hal.h"
/* 定义接收缓冲区 */
#define RX_BUF_SIZE 10
uint8_t rx_buf[RX_BUF_SIZE];
/* 定义接收状态和计数器 */
typedef enum {
RX_IDLE = 0,
RX_HEADER,
RX_LENGTH,
RX_CMD,
RX_DATA,
RX_CHECK
} rx_state_t;
rx_state_t rx_state = RX_IDLE;
uint8_t rx_count = 0;
/* 定义超时时间 */
#define RX_TIMEOUT_MS 10
uint32_t rx_timeout = 0;
/* 定义处理数据的函数 */
void process_data(uint8_t cmd, uint8_t* data, uint8_t data_len) {
/* 在这里进行数据处理 */
}
/* 定义串口接收中断处理函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
/* 读取接收到的数据 */
uint8_t rx_data = huart->Instance->DR;
/* 检查当前状态并处理数据 */
switch(rx_state) {
case RX_IDLE:
/* 等待头部 */
if(rx_data == 0x81) {
rx_state = RX_HEADER;
rx_timeout = HAL_GetTick() + RX_TIMEOUT_MS;
}
break;
case RX_HEADER:
/* 读取长度 */
rx_buf[rx_count++] = rx_data;
if(rx_data == 0x01 || rx_data == 0x02) {
rx_state = RX_LENGTH;
}
else {
rx_state = RX_IDLE;
rx_count = 0;
}
rx_timeout = HAL_GetTick() + RX_TIMEOUT_MS;
break;
case RX_LENGTH:
/* 读取指令 */
rx_buf[rx_count++] = rx_data;
if(rx_data == 0x90 || rx_data == 0x91 || rx_data == 0x92) {
rx_state = RX_CMD;
}
else {
rx_state = RX_IDLE;
rx_count = 0;
}
rx_timeout = HAL_GetTick() + RX_TIMEOUT_MS;
break;
case RX_CMD:
/* 读取数据 */
rx_buf[rx_count++] = rx_data;
if(rx_count == rx_buf[1] + 3) {
rx_timeout = 0;
uint8_t bcc_check = 0;
for(int i = 0; i < rx_buf[1] + 2; i++) {
bcc_check ^= rx_buf[i];
}
if(bcc_check == rx_data) {
process_data(rx_buf[2], &rx_buf[3], rx_buf[1]);
}
rx_state = RX_IDLE;
rx_count = 0;
}
break;
default:
rx_state = RX_IDLE;
rx_count = 0;
break;
}
/* 如果超时,则清空接收缓冲区 */
if(rx_timeout != 0 && HAL_GetTick() > rx_timeout) {
rx_state = RX_IDLE;
rx_count = 0;
}
}
/* 在main函数中需要使能串口中断 */
int main(void)
{
/* 代码省略 */
/* 使能串口接收中断 */
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
/* 代码省略 */
}
以上是一份示例代码,仅供参考。具体实现还需要根据实际情况进行调整。希望对您有帮助。
中断服务代码虽然很长,但实际上并没有很消耗时间的操作,应该不是中断处理时间过长导致。