STM32串口通信问题

学习一段时间后,写了一段接收代码。
但是在实际运用中发现需要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;

        } 
    } 
}


  • 以下回答由chatgpt基于相关博客总结生成:

    我了解您的问题,关于STM32串口通信中的接收问题,我可以给出以下解决方案:

    1. 确认串口通信配置是否正确,包括波特率、数据位、校验位、停止位等;

    2. 确认接收代码是否正确,包括接收报头、数据长度、指令名和数据,并进行bcc验证;

    3. 考虑使用中断方式进行串口接收,可以提高接收效率并使主程序更加稳定。可以使用CubeMX生成中断服务程序代码,并在主程序中添加中断处理函数,以便及时处理接收到的数据;

    4. 在接收代码中增加超时机制,当接收时间超过一定时间仍未接收到完整数据帧时,及时清空接收缓冲区,防止出现粘包或漏包现象;

    5. 可以增加一些调试信息,例如打印出接收数据的十六进制表示,便于排查问题。可以使用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);
      /* 代码省略 */
    }
    

    以上是一份示例代码,仅供参考。具体实现还需要根据实际情况进行调整。希望对您有帮助。

中断服务代码虽然很长,但实际上并没有很消耗时间的操作,应该不是中断处理时间过长导致。