一种串口完整帧数据接收的实现方式

本人采用的STM32HAL库,部分函数为库函数提供,其中硬件初始化反初始化函数部分需要自己实现,这里不给出实现代码,数据帧接收实现方式基本通用于所有串口通信,以下是实现的代码。

附:如果你想使用字符串形式的协议,可参考我微信公众号中的这篇文章(https://mp.weixin.qq.com/s/qP4wxFnwK5aUR1YgX4F9bA),协议解析更加简单更加严密,协议格式是"AT+CMD=XXXX\r\n"。

所使用的协议格式:AA 55/56/57... length data sum(帧头:AA 55/56/57...;length:剩余数据长度;data:有效数据区;sum:校验和不包含帧头),可识别多种帧头数据,只需添加第二个帧头即可;

#ifndef	__UART_H
#define __UART_H

#include "stm32f1xx_hal.h"

#include "./UART/uart1.h"
#include "./UART/uart2.h"
#include "./UART/uart3.h"
#include "./UART/uart4.h"
#include "./UART/uart5.h"

/*帧头长度*/
#define FRAME_HEADER_LENGTH     2U
/*帧尾长度(即校验和)*/
#define FRAME_TAIL_LENGTH       1U

/*帧头相同字节(第一字节)*/
#define FRAME_HEAD_SAME_AA      0xAA
/*帧头区别字节(第二字节)*/
#define FRAME_HEAD_DIFF_55      0x55
#define FRAME_HEAD_DIFF_56      0x56
#define FRAME_HEAD_DIFF_57      0x57

/*接收缓冲区长度*/
#define RX_BUF_1_LENGTH         50U

/*接收协议公共变量*/
typedef struct{
  volatile uint8_t step;           /*switch 语句跳转条件*/
  volatile uint8_t tmpCnt;         /*用于计数的临时变量*/
  volatile uint8_t aRxBufIndex;    /*接收数据缓冲区索引*/
  uint8_t aRxBuf_1[RX_BUF_1_LENGTH];
}protocolComType_t;

/*串口接收协议结构体*/
typedef struct{
  protocolComType_t  uart1Ptc;
  protocolComType_t  uart2Ptc;
  protocolComType_t  uart3Ptc;
  protocolComType_t  uart4Ptc;
  protocolComType_t  uart5Ptc;
}uartPtcType_t;

extern uartPtcType_t uartPtc;/*声明接收结构体*/

/*校验和计算*/
uint8_t CheckSumCal(uint8_t *pData,uint32_t num);

#endif/*__UART_H*/
#include "./UART/uart.h"

/*是否使用定时器监测串口,0U不使用,1U使用*/
#define IF_USE_TIM_MONITOR    0U

uartPtcType_t uartPtc;/*定义一个接收结构体*/

/*串口1接收完成回调函数*/
static void HAL_UART1_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data);
/*串口2接收完成回调函数*/
static void HAL_UART2_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data);
/*串口3接收完成回调函数*/
static void HAL_UART3_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data);
/*串口4接收完成回调函数*/
static void HAL_UART4_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data);
/*串口5接收完成回调函数*/
static void HAL_UART5_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data);

/*校验和计算*/
uint8_t CheckSumCal(uint8_t *pData,uint32_t num)
{
    if(pData == NULL){ return 0x00; }
    if(num == 0){ return 0x00; }
	
    /*将校验和字节置为0x00*/
    pData[num - 1] = 0x00;
    /*除去帧头和校验位本身的校验和*/
    for(uint32_t i = 0;i<(num - FRAME_HEADER_LENGTH - FRAME_TAIL_LENGTH);i++)
    {
        /*仅保留低位*/
        pData[num - 1] += (0xff & (pData[i + FRAME_HEADER_LENGTH]));
    }
    return (pData[num - 1]);
}

/*串口外设初始化*/
/*
被调用流程:
用户串口初始化函数->HAL_UART_Init();->HAL_UART_MspInit();->根据不同串口调用不同 Msp 初始化函数
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
  /*用于检测到串口发生 ORE 错误时,清除此标志位,以使串口恢复工作*/
  #if (IF_USE_TIM_MONITOR > 0U)
  TIM_InitCfg();
  #endif
  
  if((huart->Instance) == USART1)
  {
    HAL_UART1_MspInit(huart);
  }
  else if((huart->Instance) == USART2)
  {
    HAL_UART2_MspInit(huart);
  }
  else if((huart->Instance) == USART3)
  {
    HAL_UART3_MspInit(huart);
  }
  else if((huart->Instance) == UART4)
  {
    HAL_UART4_MspInit(huart);
  }
  else if((huart->Instance) == UART5)
  {
    HAL_UART5_MspInit(huart);
  }
}

/*串口外设反初始化*/
void HAL_UART_MspDeInit(UART_HandleTypeDef	*huart)
{
  if((huart->Instance) == USART1)
  {
    HAL_UART1_MspDeInit();
  }
  else if((huart->Instance) == USART2)
  {
    HAL_UART2_MspDeInit();
  }
  else if((huart->Instance) == USART3)
  {
    HAL_UART3_MspDeInit();
  }
  else if((huart->Instance) == UART4)
  {
    HAL_UART4_MspDeInit();
  }
  else if((huart->Instance) == UART5)
  {
    HAL_UART5_MspDeInit();
  }
}

/*发送完成回调函数*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if((huart->Instance) == USART1)
  {
  }
  else if((huart->Instance) == USART2)
  {
  }
  else if((huart->Instance) == USART3)
  {
  }
  else if((huart->Instance) == UART4)
  {
  }
  else if((huart->Instance) == UART5)
  {
  }
}
/*接收完成回调函数*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if((huart->Instance) == USART1)
    {
        HAL_UART1_RxCpltCallback(&uartPtc.uart1Ptc,uart1SingleByteRecBuf);
    }
    else if((huart->Instance) == USART2)
    {
        HAL_UART2_RxCpltCallback(&uartPtc.uart2Ptc,uart2SingleByteRecBuf);
    }
    else if((huart->Instance) == USART3)
    {
        HAL_UART3_RxCpltCallback(&uartPtc.uart3Ptc,uart3SingleByteRecBuf);
    }
    else if((huart->Instance) == UART4)
    {
        HAL_UART4_RxCpltCallback(&uartPtc.uart4Ptc,uart4SingleByteRecBuf);
    }
    else if((huart->Instance) == UART5)
    {
        HAL_UART5_RxCpltCallback(&uartPtc.uart5Ptc,uart5SingleByteRecBuf);
    }
}

/*初始化结构体变量*/
static void InitPtcStruct(protocolComType_t *pUartHandle)
{
  pUartHandle->step         = 0;
  pUartHandle->tmpCnt       = 0;
  pUartHandle->aRxBufIndex  = 0;
}

/*串口1接收完成回调函数*/
static void HAL_UART1_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data)
{
    switch(pUartHandle->step)
    {
        case 0:
            if(data == FRAME_HEAD_SAME_AA)/*帧头正确*/
            {
                pUartHandle->step++;/*跳转下一步骤*/
                pUartHandle->aRxBuf_1[pUartHandle->aRxBufIndex++] = data;
            }
        break;
        case 1:
            if((data == FRAME_HEAD_DIFF_55) || (data == FRAME_HEAD_DIFF_56) || (data == FRAME_HEAD_DIFF_57))/*帧头正确*/
            {
                pUartHandle->step++;/*跳转下一步骤*/
                pUartHandle->aRxBuf_1[pUartHandle->aRxBufIndex++] = data;
            }
            else if(data == FRAME_HEAD_SAME_AA)
                pUartHandle->step = 1;/*第一帧头重复,回到第二帧头判断处,AA AA 情况*/
            else
                InitPtcStruct(pUartHandle);/*初始化结构体值,准备下一次接收*/
        break;
        case 2:
            if(data == FRAME_HEAD_SAME_AA)
            {
                pUartHandle->step = 1;/*第一帧头重复,回到第二帧头判断处,AA 55 AA 55的情况*/
                pUartHandle->aRxBufIndex = 1;/*更新第二帧头*/
            }
            else
            {
                pUartHandle->tmpCnt = data;/*临时计数值*/
                pUartHandle->step++;/*跳转下一步骤*/
                pUartHandle->aRxBuf_1[pUartHandle->aRxBufIndex++] = data;/*压入缓冲区*/
                if(((RX_BUF_1_LENGTH - pUartHandle->aRxBufIndex) < data) || (data == 0))
                {/*缓冲区溢出或数据长度为 0*/
                    InitPtcStruct(pUartHandle);/*初始化结构体值,准备下一次接收*/
                }
            }
        break;
        case 3:
            if(--pUartHandle->tmpCnt)
            {/*接收数据到缓冲区*/
                pUartHandle->aRxBuf_1[pUartHandle->aRxBufIndex++] = data;
                if(pUartHandle->aRxBufIndex >= RX_BUF_1_LENGTH)
                {/*长度被意外修改,导致缓冲区溢出*/
                    InitPtcStruct(pUartHandle);/*初始化结构体值,准备下一次接收*/
                }
            }
            else
            {
                /*检查校验和并写入缓冲区*/
                if(CheckSumCal(pUartHandle->aRxBuf_1,pUartHandle->aRxBufIndex + 1) == data)
                {
//                PRINTF("uart1\n");
//                for(uint32_t i = 0;iaRxBufIndex + 1;i++)
//                PRINTF("%02x\t",pUartHandle->aRxBuf_1[i]);PRINTF("\n");
            
                /*这里可结合上篇文章将数据存入环形缓冲区(也可不存直接处理这里接收的数据,不过数据量太大时可能会丢帧),并且设置标志位或是发送信号量给任务以处理接收到的数据*/
                }
					
                InitPtcStruct(pUartHandle);/*初始化结构体值,准备下一次接收*/
            }
        break;
        
        default:
            InitPtcStruct(pUartHandle);/*初始化结构体值,准备下一次接收*/
        break;
    }
		
    #if UART1_USE_DMA
    /*DMA 接收开启循环模式不需要再次触发*/
    #else
    HAL_UART_Receive_IT(&huart1,&uart1SingleByteRecBuf,1);/*再次开启中断,触发下一次接收*/
    #endif
}

/*串口2接收完成回调函数*/
static void HAL_UART2_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data)
{
  (void)pUartHandle;
  (void)data;

  #if UART2_USE_DMA
  /*DMA 接收开启循环模式不需要再次触发*/
  #else
  HAL_UART_Receive_IT(&huart2,&uart2SingleByteRecBuf,1);/*再次开启中断,触发下一次接收*/
  #endif
}

/*串口3接收完成回调函数*/
static void HAL_UART3_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data)
{
  (void)pUartHandle;
  (void)data;
  
  #if UART3_USE_DMA
  /*DMA 接收开启循环模式不需要再次触发*/
  #else
  HAL_UART_Receive_IT(&huart3,&uart3SingleByteRecBuf,1);/*再次开启中断,触发下一次接收*/
  #endif
}

/*串口4接收完成回调函数*/
static void HAL_UART4_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data)
{
  (void)pUartHandle;
  (void)data;
  
  #if UART4_USE_DMA
  /*DMA 接收开启循环模式不需要再次触发*/
  #else
  HAL_UART_Receive_IT(&huart4,&uart4SingleByteRecBuf,1);/*再次开启中断,触发下一次接收*/
  #endif
}

/*串口5接收完成回调函数*/
static void HAL_UART5_RxCpltCallback(protocolComType_t *pUartHandle,uint8_t data)
{
  (void)pUartHandle;
  (void)data;
  
  HAL_UART_Receive_IT(&huart5,&uart5SingleByteRecBuf,1);/*再次开启中断,触发下一次接收*/
}

uart(x)SingleByteRecBuf:(x)为数字1-5,此变量定义在串口初始化文件中,这里没有给出,在串口初始化完成后需要调用一次开启接收中断函数(即HAL_UART_Receive_IT(););

所有串口均可直接复制串口一中的代码直接使用(只需要修改数据存储部分和标志位设置或是信号量发送部分),那为什么代码完全相同还要分开实现呢,因为每个串口接入的设备不一样,通讯协议也不一定一样,这样可以分开实现,灵活性更强。

代码实现已处理发现的所有边界条件,可直接用于项目中,未经允许,请勿转载。

你可能感兴趣的:(单片机)