欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-26 08:25:53
...

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

所使用的协议格式: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]);
}

/*串口外设初始化*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
  /*用于检测到串口发生 ORE 错误时,清除此标志位,以使串口恢复工作*/
  #if (IF_USE_TIM_MONITOR > 0U)
  TIM_InitCfg();
  #endif
  
  if((huart->Instance) == USART1)
  {
    HAL_UART1_Init(huart);
  }
  else if((huart->Instance) == USART2)
  {
    HAL_UART2_Init(huart);
  }
  else if((huart->Instance) == USART3)
  {
    HAL_UART3_Init(huart);
  }
  else if((huart->Instance) == UART4)
  {
    HAL_UART4_Init(huart);
  }
  else if((huart->Instance) == UART5)
  {
    HAL_UART5_Init(huart);
  }
}

/*串口外设反初始化*/
void HAL_UART_MspDeInit(UART_HandleTypeDef	*huart)
{
  if((huart->Instance) == USART1)
  {
    HAL_UART1_DeInit();
  }
  else if((huart->Instance) == USART2)
  {
    HAL_UART2_DeInit();
  }
  else if((huart->Instance) == USART3)
  {
    HAL_UART3_DeInit();
  }
  else if((huart->Instance) == UART4)
  {
    HAL_UART4_DeInit();
  }
  else if((huart->Instance) == UART5)
  {
    HAL_UART5_DeInit();
  }
}

/*发送完成回调函数*/
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;i<pUartHandle->aRxBufIndex + 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(););

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

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

相关标签: 串口 STM32