本人采用的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]); } /*串口外设初始化*/ /* 被调用流程:
用户串口初始化函数->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;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(););
所有串口均可直接复制串口一中的代码直接使用(只需要修改数据存储部分和标志位设置或是信号量发送部分),那为什么代码完全相同还要分开实现呢,因为每个串口接入的设备不一样,通讯协议也不一定一样,这样可以分开实现,灵活性更强。
代码实现已处理发现的所有边界条件,可直接用于项目中,未经允许,请勿转载。
热门工具 换一换