STM32串口驱动不定长数据接收带环形缓冲区

  • 最新框架代码
  • 使用方法
  • 源码
    • 串口接口文件
    • 环形缓冲区接口文件
  • 移植图示

使用涉及4个文件, UART_Port.c UART_Port.h CircularQueue.h CircularQueue.c

最新框架代码

2022年10月25日更新:https://github.com/aron566/Serial_Port.git

使用方法

1、在配置串口中使能DMA传输 ,全局中断
2、在串口初始化完成后调用Uart_Port_Init();
3、在串口中断服务函数中调用USER_UART_IRQHandler(UART_HandleTypeDef *huart);
4、获取数据接口,请查看CircularQueue.h提供的缓冲区提取数据接口
5、注意事项:malloc无法申请内存的需调整堆栈大小,或者调整缓冲区大小

源码

串口接口文件

PS:默认使用串口1作为调试回环打印接口
需调整宏定义DEBUG_UART USE_LOOPBACK

/**  FILE: UART_Port.c**  Created on: 2020/2/22**      Author: aron66**  DESCRIPTION:--*/
#ifdef __cplusplus //use C compiler
extern "C" {#endif
#include "UART_Port.h"/*外部接口*/
#define USE_LOOPBACK    1
#define DEBUG_UART      &huart1
/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart5;
extern UART_HandleTypeDef huart6;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern DMA_HandleTypeDef hdma_usart2_rx;
extern DMA_HandleTypeDef hdma_usart2_tx;
extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_usart3_tx;
extern DMA_HandleTypeDef hdma_usart4_rx;
extern DMA_HandleTypeDef hdma_usart4_tx;
extern DMA_HandleTypeDef hdma_usart5_rx;
extern DMA_HandleTypeDef hdma_usart5_tx;
extern DMA_HandleTypeDef hdma_usart6_rx;
extern DMA_HandleTypeDef hdma_usart6_tx;static uint8_t get_uart_index(USART_TypeDef *Instance);
static Uart_Dev_info_t *Create_Uart_Dev(Uart_num_t uart_num ,UART_HandleTypeDef *huart ,DMA_HandleTypeDef *hdma_rx ,uint16_t rx_temp_size ,uint32_t rxsize ,int work_mode ,osSemaphoreId *pRX_Sem);/*预定义串口设备信息*/
Uart_Dev_info_t *Uart_pDevice[UART_MAX_NUM+1];
/********************************************************************* @brief   初始化串口设备信息* @author  aron66* @version v1.0* @date    2020/3/15*******************************************************************/
void Uart_Port_Init(void)
{Uart_pDevice[UART_NUM_1] = Create_Uart_Dev(UART_NUM_1 ,&huart1 ,&hdma_usart1_rx ,128 ,128 ,0 ,NULL);Uart_pDevice[UART_NUM_2] = Create_Uart_Dev(UART_NUM_2 ,&huart2 ,&hdma_usart2_rx ,128 ,128 ,0 ,NULL);}/********************************************************************* @brief   建立串口设备,为其建立双缓冲区-->使能串口空闲中断* @param   串口号 串口设备指针 dma操作地址 ,临时缓冲大小 接收队列大小 工作模式 二值信号量(适用于带FreeRTOS操作系统的工程)* @author  aron66* @version v1.0* @date    2020/3/15*******************************************************************/
static Uart_Dev_info_t *Create_Uart_Dev(Uart_num_t uart_num ,UART_HandleTypeDef *huart ,DMA_HandleTypeDef *hdma_rx ,uint16_t rx_temp_size ,uint32_t rxsize ,int work_mode ,osSemaphoreId *pRX_Sem)
{Uart_Dev_info_t *pUart_Dev = (Uart_Dev_info_t *)malloc(sizeof(Uart_Dev_info_t));pUart_Dev->phuart = huart;pUart_Dev->phdma_rx = hdma_rx;pUart_Dev->cb = cb_create(rxsize);pUart_Dev->MAX_RX_Temp = rx_temp_size;pUart_Dev->RX_Buff_Temp = (uint8_t *)malloc(sizeof(uint8_t)*rx_temp_size);if(NULL == pUart_Dev->RX_Buff_Temp){return NULL;}pUart_Dev->Is_Half_Duplex = work_mode;pUart_Dev->pRX_Sem = pRX_Sem;//打开空闲中断__HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);//使能DMA接收HAL_UART_Receive_DMA(huart, pUart_Dev->RX_Buff_Temp, pUart_Dev->MAX_RX_Temp);return pUart_Dev;
}/************************************************************* @brief   Rx Transfer IRQ* @param   huart UART handle.* @return  None* @author  aron66* @date    2020/3/15* @version v1.0* @note    @@***********************************************************/
void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{uint8_t index = get_uart_index(huart->Instance);if(index != 0){if((__HAL_UART_GET_FLAG(Uart_pDevice[index]->phuart ,UART_FLAG_IDLE) != RESET)){/*首先停止DMA传输,1.防止后面又有数据接收到,产生干扰,因为此时的数据还未处理。2.DMA需要重新配置。*/HAL_UART_DMAStop(Uart_pDevice[index]->phuart);/*清楚空闲中断标志,否则会一直不断进入中断*/__HAL_UART_CLEAR_IDLEFLAG(Uart_pDevice[index]->phuart);/*计算本次接收数据长度*/uint32_t data_length  = Uart_pDevice[index]->MAX_RX_Temp - __HAL_DMA_GET_COUNTER(Uart_pDevice[index]->phdma_rx);/*将数据记录至环形区*/CQ_putData(Uart_pDevice[index]->cb ,Uart_pDevice[index]->RX_Buff_Temp ,(uint32_t)data_length);
#if USE_LOOPBACK    HAL_UART_Transmit(DEBUG_UART, (uint8_t *)Uart_pDevice[index]->RX_Buff_Temp,(uint16_t)data_length,0xFFFF);
#endif/*清空临时缓冲区*/memset(Uart_pDevice[index]->RX_Buff_Temp ,0 ,data_length);data_length = 0;/*打开空闲中断*/__HAL_UART_ENABLE_IT(Uart_pDevice[index]->phuart ,UART_IT_IDLE);/*重启开始DMA传输*/HAL_UART_Receive_DMA(Uart_pDevice[index]->phuart ,Uart_pDevice[index]->RX_Buff_Temp, Uart_pDevice[index]->MAX_RX_Temp);}}
}   /*获得当前串口信息索引*/
static uint8_t get_uart_index(USART_TypeDef *Instance)
{uint8_t index = 0;for(;index < UART_MAX_NUM+1;index++){if(Uart_pDevice[index]->phuart->Instance == Instance){return index;}  }return 0;
}#if (USE_NEW_REDIRECT == 0)
#include "stdio.h"
/************************************************** 函数功能: 重定向c库函数printf到HAL_UART_Transmit* 输入参数: 无* 返 回 值: 无* 说    明:无*/
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(DEBUG_UART, (uint8_t *)&ch, 1, 10);//原来使用阻塞式传输return ch;
}
/*** 函数功能: 重定向c库函数getchar,scanf* 输入参数: 无* 返 回 值: 无* 说    明:无*/
int fgetc(FILE * f)
{uint8_t ch = 0;while(HAL_UART_Receive(DEBUG_UART,&ch, 1, 0xffff)!=HAL_OK);return ch;
}#else
/*新式重定向*/
#include "stdio.h"
int __io_putchar(int ch)
{HAL_UART_Transmit(DEBUG_UART ,()uint8_t)&ch ,1 ,0xFFFF);return ch;
}
int __write(int file, char *ptr, int len)
{int DataIdx;for(DataIdx = 0; DataIdx < len; DataIdx++){__io_putchar(*ptr++);}return len;
}
#endif#ifdef __cplusplus //end extern c
}
#endif

UART_Port.h文件

/**  FILE: UART_Port.h**  Created on: 2020/2/22**      Author: aron566**  DESCRIPTION:--*/
#ifndef UART_PORT_H
#define UART_PORT_H
#ifdef __cplusplus //use C compiler
extern "C" {#endif
/*库接口*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/*外部接口*/
#include "stm32f1xx_hal.h"
#include "usart.h"
#include "cmsis_os.h"
/*内部接口*/
#include "CircularQueue.h"#define UART_MAX_NUM    6typedef enum
{UART_NUM_0 = 0,UART_NUM_1,UART_NUM_2,UART_NUM_3,UART_NUM_4,UART_NUM_5,UART_NUM_6,
}Uart_num_t;typedef struct
{UART_HandleTypeDef *phuart;      //uart端口DMA_HandleTypeDef  *phdma_rx;CQ_handleTypeDef *cb;           //环形队列uint8_t *RX_Buff_Temp;          //接收缓冲uint16_t MAX_RX_Temp;           //最大接收数量int Is_Half_Duplex;             //半双工模式osSemaphoreId *pRX_Sem;         //接收二值信号量,如果没有使用FreeRTOS则屏蔽即可
}Uart_Dev_info_t;void Uart_Port_Init(void);
void USER_UART_IRQHandler(UART_HandleTypeDef *huart);
#ifdef __cplusplus //end extern c
}
#endif
#endif

环形缓冲区接口文件

CircularQueue.c文件

/***  @file CircularQueue.c**  @date 2020/6/25**  @author aron566**  @copyright None**  @brief None**  @details None**  @version v1.1*/
#ifdef __cplusplus ///<use C compiler
extern "C" {#endif
/** Includes -----------------------------------------------------------------*/
/* Private includes ----------------------------------------------------------*/
#include "CircularQueue.h"
#if USE_LINUX_SYSTEM
#include <sys/types.h>
#endif
/** Private typedef ----------------------------------------------------------*/
/** Private macros -----------------------------------------------------------*/
/*** @name 返回值定义* @{*/
#define TRUE true
#define FALSE false
/** @}*/
/** Private constants --------------------------------------------------------*/
/** Public variables ---------------------------------------------------------*/
/** Private variables --------------------------------------------------------*/
/** Private function prototypes ----------------------------------------------*/
/** Private user code --------------------------------------------------------*//** Private application code -------------------------------------------------*/
/*******************************************************************************
*
*       Static code
*
********************************************************************************
*/
/** Public application code --------------------------------------------------*/
/*******************************************************************************
*
*       Public code
*
********************************************************************************
*//*初始化参数定义:
*CircularQueue作为环形冲区的记录器是个结构体
*memAdd 作为实际数据存储区,
*len 记录实际存储区的最大长度,需为2的整数倍
*/
bool CQ_init(CQ_handleTypeDef *CircularQueue ,uint8_t *memAdd, uint16_t len)
{CircularQueue->size = len;if (!IS_POWER_OF_2(CircularQueue->size))return FALSE;if(memAdd == NULL){return FALSE;}CircularQueue->dataBufer = memAdd;memset(CircularQueue->dataBufer, 0, len);CircularQueue->entrance = CircularQueue->exit = 0;return TRUE;
}/*环形缓冲区判断是否为空:
*CircularQueue作为环形冲区的记录器,是个结构体
*若写入数据与,读取数据长度一致,那么缓冲区为空return 1
*/
bool CQ_isEmpty(CQ_handleTypeDef *CircularQueue)
{if (CircularQueue->entrance == CircularQueue->exit)return TRUE;elsereturn FALSE;
}/*环形缓冲区判断是否为满
*CircularQueue作为环形冲区的记录器,是个结构体
*若 【已】写入数据与,减去 【已】读取数据长度 = 剩余空间 剩余空间==总长度  判断满
*/
bool CQ_isFull(CQ_handleTypeDef *CircularQueue)
{if ((CircularQueue->entrance - CircularQueue->exit) == CircularQueue->size)//MAXSIZE=5,Q.rear=2,Q.front=3?return TRUE;//空elsereturn FALSE;
}/*环形缓冲区获取剩余空间长度:
*CircularQueue作为环形冲区的记录器,是个结构体
*若 【已】写入数据与,减去 【已】读取数据长度 = 剩余空间
*/
uint32_t CQ_getLength(CQ_handleTypeDef*CircularQueue)
{return (CircularQueue->entrance - CircularQueue->exit);
}/*环形缓冲区清空操作:
*CircularQueue作为环形冲区的记录器,是个结构体
* 已读和可读数据长度清零      实际存储区清空
*/
void CQ_emptyData(CQ_handleTypeDef*CircularQueue)
{CircularQueue->entrance = CircularQueue->exit = 0;memset(CircularQueue->dataBufer, 0, CircularQueue->size);
}/*
*环形缓冲区读走数据:
*CircularQueue作为环形冲区的记录器,是个结构体
*targetBuf 为临时数据处理处
*len 为本次数据读取长度
*使用写入长度-读取的长度 == 剩余可读  ,要读   取小值
*/
uint32_t CQ_getData(CQ_handleTypeDef *CircularQueue, uint8_t *targetBuf, uint32_t len)
{uint32_t size = 0;/*此次读取的实际大小,取 缓存事件数据大小 和 目标读取数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->entrance - CircularQueue->exit);// 假设总大小10  写入了5 - 已读4  == 1 未读   要读5个   返回1/*原理雷同存入*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->exit & (CircularQueue->size - 1)));//10 - 0 > 1 返回1memcpy(targetBuf, CircularQueue->dataBufer + (CircularQueue->exit & (CircularQueue->size - 1)), size);//偏移0个 复制一个字节memcpy(targetBuf + size, CircularQueue->dataBufer, len - size);// 存储区偏移0个字节/*利用无符号数据的溢出特性*/CircularQueue->exit += len;//取出数据加 len 记录return len;
}/*环形缓冲区加入新数据:存入数据功能已做修改:每次数据帧开头先存入本帧的数据长度,所以每次先取一个字节得到包长度,再按长度取包
*CircularQueue作为环形冲区的记录器,是个结构体
*sourceBuf 为实际存储区地址
*len 为本次数据存入长度
*使用总长度-已写入+读取完的 == 可用空间大小
*对kfifo->size取模运算可以转化为与运算,如:kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1)
*/
uint32_t CQ_putData(CQ_handleTypeDef *CircularQueue, uint8_t * sourceBuf, uint32_t len)
{uint32_t size = 0;/*此次存入的实际大小,取 剩余空间 和 目标存入数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->size - CircularQueue->entrance + CircularQueue->exit);/*&(size-1)代替取模运算,同上原理,得到此次存入队列入口到末尾的大小*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->entrance & (CircularQueue->size - 1)));memcpy(CircularQueue->dataBufer + (CircularQueue->entrance & (CircularQueue->size - 1)), sourceBuf, size);memcpy(CircularQueue->dataBufer, sourceBuf + size, len - size);//下次需要写入的数据长度/*利用无符号数据的溢出特性*/CircularQueue->entrance += len; //写入数据记录return len;
}/*修改后的-->环形缓冲区加入新数据:存入数据功能已做修改:每次数据帧开头先存入本帧的数据长度,所以每次先取一个字节得到包长度,再按长度取包
*CircularQueue作为环形冲区的记录器
*sourceBuf 为实际存储区地址
*len 为本次数据存入长度
*使用总长度-已写入+读取完的 == 可用空间大小
*对kfifo->size取模运算可以转化为与运算,如:kfifo->in % kfifo->size 可以转化为 kfifo->in & (kfifo->size – 1)
*/
uint32_t DQ_putData(CQ_handleTypeDef *CircularQueue, uint8_t * sourceBuf, uint32_t len)
{uint32_t size = 0;uint32_t lenth = 1;uint32_t pack_len = len;/*此次存入的实际大小,取 剩余空间 和 目标存入数量  两个值小的那个*/len = GET_MIN(len+lenth, CircularQueue->size - CircularQueue->entrance + CircularQueue->exit);//长度上头部加上数据长度记录/*&(size-1)代替取模运算,同上原理,得到此次存入队列入口到末尾的大小*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->entrance & (CircularQueue->size - 1)));memcpy(CircularQueue->dataBufer + (CircularQueue->entrance & (CircularQueue->size - 1)), &pack_len, lenth);memcpy(CircularQueue->dataBufer + (CircularQueue->entrance & (CircularQueue->size - 1))+lenth, sourceBuf, size-lenth);memcpy(CircularQueue->dataBufer, sourceBuf + size - lenth, len - size);/*利用无符号数据的溢出特性*/CircularQueue->entrance += len;return len;
}/*
*修改后的-->环形缓冲区读走数据:DQ会调用CQ取走一字节数据用来判断本次数据包长度
*CircularQueue作为环形冲区的记录器,是个结构体
*targetBuf 为临时数据处理处
*len 为本次数据读取长度
*使用写入长度-读取的长度 == 剩余可读  ,要读   取小值
*/
uint32_t DQ_getData(CQ_handleTypeDef *CircularQueue, uint8_t *targetBuf)
{uint32_t size = 0;uint32_t len = 0;//存储帧头 长度信息uint8_t package_len[1];//获取长度信息CQ_getData(CircularQueue, (uint8_t *)package_len, 1);len = package_len[0];/*此次读取的实际大小,取 缓存事件数据大小 和 目标读取数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->entrance - CircularQueue->exit);/*原理雷同存入*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->exit & (CircularQueue->size - 1)));memcpy(targetBuf, CircularQueue->dataBufer + (CircularQueue->exit & (CircularQueue->size - 1)), size);memcpy(targetBuf + size, CircularQueue->dataBufer, len - size);/*利用无符号数据的溢出特性*/CircularQueue->exit += len;return len;
}/*
*环形缓冲区读走数据:(手动缓冲区长度记录---适用于modbus解析)
*CircularQueue作为环形冲区的记录器,是个结构体
*targetBuf 为临时数据处理处
*len 为本次数据读取长度
*使用写入长度-读取的长度 == 剩余可读  ,要读   取小值
*/
uint32_t CQ_ManualGetData(CQ_handleTypeDef *CircularQueue, uint8_t *targetBuf, uint32_t len)
{uint32_t size = 0;/*此次读取的实际大小,取 缓存事件数据大小 和 目标读取数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->entrance - CircularQueue->exit);/*原理雷同存入*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->exit & (CircularQueue->size - 1)));memcpy(targetBuf, CircularQueue->dataBufer + (CircularQueue->exit & (CircularQueue->size - 1)), size);memcpy(targetBuf + size, CircularQueue->dataBufer, len - size);return len;
}/*** [CQ_ManualGet_Offset_Data 读取指定索引号的数据]* @param CircularQueue [环形缓冲区句柄]* @param index         [索引号]*/
uint8_t CQ_ManualGet_Offset_Data(uint32_t index ,CQ_handleTypeDef *CircularQueue)
{/*计算偏移*/uint32_t read_offset = ((CircularQueue->exit + index) & (CircularQueue->size - 1));/*取出数据*/uint8_t data = *((uint8_t*)CircularQueue->dataBufer + read_offset);return data;
}/*** [CQ_ManualOffsetInc 手动增加已取出长度]* @param CircularQueue [环形缓冲区句柄]* @param len           [偏移长度]*/
void CQ_ManualOffsetInc(CQ_handleTypeDef *CircularQueue, uint32_t len)
{CircularQueue->exit += len ;
}/*** [cb_create 申请并初始化环形缓冲区]* @param  buffsize [申请环形缓冲区大小]* @return          [环形队列管理句柄]*/
CQ_handleTypeDef *cb_create(uint32_t buffsize)
{if (!IS_POWER_OF_2(buffsize))return NULL;CQ_handleTypeDef *cb = (CQ_handleTypeDef *)malloc(sizeof(CQ_handleTypeDef));if(NULL == cb){return NULL;}buffsize = (buffsize <= 2048 ? buffsize : 2048);cb->size = buffsize;cb->exit = 0;cb->entrance = 0;//the buff never release!cb->dataBufer = (uint8_t *)malloc(sizeof(uint8_t)*cb->size);if(NULL == cb->dataBufer){return NULL;}return cb;
}/*** [CQ_16_init 静态初始化16bit环形缓冲区]* @param  CircularQueue [缓冲区指针]* @param  memAdd        [uint16_t 缓冲区地址]* @param  len           [缓冲区长度>1]* @return               [初始化状态]*/
bool CQ_16_init(CQ_handleTypeDef *CircularQueue ,uint16_t *memAdd,uint16_t len)
{CircularQueue->size = len;if (!IS_POWER_OF_2(CircularQueue->size))return FALSE;if(memAdd == NULL){return FALSE;}CircularQueue->data16Bufer = memAdd;memset(CircularQueue->data16Bufer, 0, len*2);CircularQueue->entrance = CircularQueue->exit = 0;return TRUE;
}/*** [cb_16create 动态申请并初始化环形缓冲区]* @param  buffsize [申请环形缓冲区大小]* @return          [环形队列管理句柄]*/
CQ_handleTypeDef *cb_16create(uint32_t buffsize)
{if (!IS_POWER_OF_2(buffsize))return NULL;CQ_handleTypeDef *cb = (CQ_handleTypeDef *)malloc(sizeof(CQ_handleTypeDef));if(NULL == cb){return NULL;}buffsize = (buffsize <= 2048 ? buffsize : 2048);cb->size = buffsize;cb->exit = 0;cb->entrance = 0;//the buff never release!cb->data16Bufer = (uint16_t *)malloc(sizeof(uint16_t)*cb->size);if(NULL == cb->data16Bufer){return NULL;}return cb;
}/*** [CQ_16getData 取出数据]* @param  CircularQueue [环形缓冲区句柄]* @param  targetBuf     [目标地址]* @param  len           [取出长度]* @return               [取出长度]*/
uint32_t CQ_16getData(CQ_handleTypeDef *CircularQueue, uint16_t *targetBuf, uint32_t len)
{uint32_t size = 0;uint32_t len_temp = 0;uint32_t size_temp = 0;/*此次读取的实际大小,取 缓存事件数据大小 和 目标读取数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->entrance - CircularQueue->exit);// 假设总大小10  写入了5 - 已读4  == 1 未读   要读5个   返回1/*原理雷同存入*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->exit & (CircularQueue->size - 1)));//10 - 0 > 1 返回1len_temp = 2*len;size_temp = 2*size;memcpy(targetBuf, CircularQueue->data16Bufer + (CircularQueue->exit & (CircularQueue->size - 1)), size_temp);//偏移0个 复制一个字节memcpy(targetBuf + size, CircularQueue->data16Bufer, len_temp - size_temp);// 存储区偏移0个字节/*利用无符号数据的溢出特性*/CircularQueue->exit += len;//取出数据加 len 记录return len;
}/*** [CQ_16putData 加入数据]* @param  CircularQueue [环形缓冲区句柄]* @param  sourceBuf     [源地址]* @param  len           [长度]* @return               [加入数据长度]*/
uint32_t CQ_16putData(CQ_handleTypeDef *CircularQueue, uint16_t * sourceBuf, uint32_t len)
{uint32_t size = 0;uint32_t len_temp = 0;uint32_t size_temp = 0;/*此次存入的实际大小,取 剩余空间 和 目标存入数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->size - CircularQueue->entrance + CircularQueue->exit);/*&(size-1)代替取模运算,同上原理,得到此次存入队列入口到末尾的大小*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->entrance & (CircularQueue->size - 1)));len_temp = 2*len;size_temp = 2*size;memcpy(CircularQueue->data16Bufer + (CircularQueue->entrance & (CircularQueue->size - 1)), sourceBuf, size_temp);memcpy(CircularQueue->data16Bufer, sourceBuf + size, len_temp - size_temp);/*利用无符号数据的溢出特性*/CircularQueue->entrance += len; //写入数据记录return len;
}/*** [CQ_32_init 静态初始化16bit环形缓冲区]* @param  CircularQueue [缓冲区指针]* @param  memAdd        [uint16_t 缓冲区地址]* @param  len           [缓冲区长度>1]* @return               [初始化状态]*/
bool CQ_32_init(CQ_handleTypeDef *CircularQueue ,uint32_t *memAdd ,uint16_t len)
{CircularQueue->size = len;if (!IS_POWER_OF_2(CircularQueue->size))return FALSE;if(memAdd == NULL){return FALSE;}CircularQueue->data32Bufer = memAdd;memset(CircularQueue->data32Bufer, 0, len*4);CircularQueue->entrance = CircularQueue->exit = 0;return TRUE;
}/*** [cb_32create 动态申请并初始化环形缓冲区]* @param  buffsize [申请环形缓冲区大小]* @return          [环形队列管理句柄]*/
CQ_handleTypeDef *cb_32create(uint32_t buffsize)
{if (!IS_POWER_OF_2(buffsize))return NULL;CQ_handleTypeDef *cb = (CQ_handleTypeDef *)malloc(sizeof(CQ_handleTypeDef));if(NULL == cb){return NULL;}buffsize = (buffsize <= 2048 ? buffsize : 2048);cb->size = buffsize;cb->exit = 0;cb->entrance = 0;//the buff never release!cb->data32Bufer = (uint32_t *)malloc(sizeof(uint32_t)*cb->size);if(NULL == cb->data32Bufer){return NULL;}return cb;
}/*** [CQ_32putData 加入数据]* @param  CircularQueue [环形缓冲区句柄]* @param  sourceBuf     [源地址]* @param  len           [长度]* @return               [加入数据长度]*/
uint32_t CQ_32putData(CQ_handleTypeDef *CircularQueue ,uint32_t * sourceBuf ,uint32_t len)
{uint32_t size = 0;uint32_t len_temp = 0;uint32_t size_temp = 0;/*此次存入的实际大小,取 剩余空间 和 目标存入数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->size - CircularQueue->entrance + CircularQueue->exit);/*&(size-1)代替取模运算,同上原理,得到此次存入队列入口到末尾的大小*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->entrance & (CircularQueue->size - 1)));len_temp = 4*len;size_temp = 4*size;memcpy(CircularQueue->data32Bufer + (CircularQueue->entrance & (CircularQueue->size - 1)), sourceBuf, size_temp);memcpy(CircularQueue->data32Bufer, sourceBuf + size, len_temp - size_temp);//下次需要写入的数据长度/*利用无符号数据的溢出特性*/CircularQueue->entrance += len; //写入数据记录return len;
}/*** [CQ_32getData 取出数据]* @param  CircularQueue [环形缓冲区句柄]* @param  targetBuf     [目标地址]* @param  len           [取出长度]* @return               [取出长度]*/
uint32_t CQ_32getData(CQ_handleTypeDef *CircularQueue ,uint32_t *targetBuf ,uint32_t len)
{uint32_t size = 0;uint32_t len_temp = 0;uint32_t size_temp = 0;/*此次读取的实际大小,取 缓存事件数据大小 和 目标读取数量  两个值小的那个*/len = GET_MIN(len, CircularQueue->entrance - CircularQueue->exit);// 假设总大小10  写入了5 - 已读4  == 1 未读   要读5个   返回1/*原理雷同存入*/size = GET_MIN(len, CircularQueue->size - (CircularQueue->exit & (CircularQueue->size - 1)));//10 - 0 > 1 返回1len_temp = 4*len;size_temp = 4*size;memcpy(targetBuf, CircularQueue->data32Bufer + (CircularQueue->exit & (CircularQueue->size - 1)), size_temp);//偏移0个 复制一个字节memcpy(targetBuf + size, CircularQueue->data32Bufer, len_temp - size_temp);// 存储区偏移0个字节/*利用无符号数据的溢出特性*/CircularQueue->exit += len;//取出数据加 len 记录return len;
}
#ifdef __cplusplus ///<end extern c
}
#endif
/******************************** End of file *********************************/

CircularQueue.h文件

/***  @file CircularQueue.h**  @date 2020/6/25**  @author aron566**  @brief None*  *  @version v1.1*/
#ifndef CIRCULARQUEUE_H_
#define CIRCULARQUEUE_H_
#ifdef __cplusplus ///<use C compiler
extern "C" {#endif
/** Includes -----------------------------------------------------------------*/
#include <stdint.h> /**< need definition of uint8_t */
#include <stddef.h> /**< need definition of NULL    */
#include <stdbool.h>/**< need definition of BOOL    */
#include <stdio.h>  /**< if need printf             */
#include <stdlib.h>
#include <string.h>
/** Private includes ---------------------------------------------------------*/
/** Private defines ----------------------------------------------------------*//** Exported typedefines -----------------------------------------------------*/
/** 数据结构体*/
typedef struct
{uint8_t *dataBufer;        /**< for 8bit buffer*/uint16_t *data16Bufer; /**< for 16bit buffer*/uint32_t *data32Bufer;    /**< for 32bit buffer*/uint32_t size;uint32_t entrance;uint32_t exit;
}CQ_handleTypeDef;
/** Exported constants -------------------------------------------------------*//** Exported macros-----------------------------------------------------------*/
#define GET_MIN(a, b) (((a) < (b)) ? (a) : (b))
#define IS_POWER_OF_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))/** Exported variables -------------------------------------------------------*/
/** Exported functions prototypes --------------------------------------------*//*============================= Common ================================*/
bool CQ_isEmpty(CQ_handleTypeDef *CircularQueue);
bool CQ_isFull(CQ_handleTypeDef *CircularQueue);
void CQ_emptyData(CQ_handleTypeDef*CircularQueue);
uint32_t CQ_getLength(CQ_handleTypeDef *CircularQueue);
/*手动缩减缓冲区长度--用作:错误帧偏移-正确帧读取后剔除*/
void CQ_ManualOffsetInc(CQ_handleTypeDef *CircularQueue, uint32_t len);/*===========================8 Bit Option==============================*/
bool CQ_init(CQ_handleTypeDef *CircularQueue ,uint8_t *memAdd,uint16_t len);
/*分配一个缓冲区并进行初始化--替代--CQ_init*/
CQ_handleTypeDef *cb_create(uint32_t buffsize);uint32_t CQ_getData(CQ_handleTypeDef *CircularQueue, uint8_t *targetBuf, uint32_t len);
uint32_t CQ_putData(CQ_handleTypeDef *CircularQueue, uint8_t *sourceBuf, uint32_t len);/*修改后的加入数据操作--数据长度作为帧头先存入缓冲区*/
uint32_t DQ_putData(CQ_handleTypeDef *CircularQueue, uint8_t *sourceBuf, uint32_t len);
/*修改后的取数据操作--会直接读取帧长度信息,依据当前一包长度加入缓冲区*/
uint32_t DQ_getData(CQ_handleTypeDef *CircularQueue, uint8_t *targetBuf);/*修改后的获取数据操作--数据读取后不会减小缓冲区长度,需手动减小,目的为了分步取出完整数据*/
uint32_t CQ_ManualGetData(CQ_handleTypeDef *CircularQueue, uint8_t *targetBuf, uint32_t len);
/*修改后的获取数据操作--读取指定偏移的数据,不会减小缓冲区长度,目的为了验证数据,判断帧头等*/
uint8_t CQ_ManualGet_Offset_Data(uint32_t index ,CQ_handleTypeDef *CircularQueue);/*===========================16 Bit Option==============================*/
/*16bit环形缓冲区初始化*/
bool CQ_16_init(CQ_handleTypeDef *CircularQueue ,uint16_t *memAdd,uint16_t len);
/*分配一个缓冲区并进行初始化--替代--CQ_16_init*/
CQ_handleTypeDef *cb_16create(uint32_t buffsize);
/*加入16bit类型数据*/
uint32_t CQ_16putData(CQ_handleTypeDef *CircularQueue, uint16_t * sourceBuf, uint32_t len);
/*取出16bit类型数据*/
uint32_t CQ_16getData(CQ_handleTypeDef *CircularQueue, uint16_t *targetBuf, uint32_t len);/*===========================32 Bit Option==============================*/
/*32bit环形缓冲区初始化*/
bool CQ_32_init(CQ_handleTypeDef *CircularQueue ,uint32_t *memAdd,uint16_t len);
/*分配一个缓冲区并进行初始化--替代--CQ_32_init*/
CQ_handleTypeDef *cb_32create(uint32_t buffsize);
/*加入32bit类型数据*/
uint32_t CQ_32putData(CQ_handleTypeDef *CircularQueue, uint32_t * sourceBuf, uint32_t len);
/*取出32bit类型数据*/
uint32_t CQ_32getData(CQ_handleTypeDef *CircularQueue, uint32_t *targetBuf, uint32_t len);#ifdef __cplusplus ///<end extern c
}
#endif
#endif
/******************************** End of file *********************************/

移植图示

1、加入接口代码文件至工程

2、确定自己的平台库文件

3、加入串口中断服务接口处理方法

4、main.c初始化代码加入

5、测试结果

参考博客:https://blog.csdn.net/jiejiemcu/article/details/82743945

基于HAL库STM32串口驱动不定长数据接收相关推荐

  1. 嵌入式Linux 串口编程系列3——通过VTIM、VMIN、select实现串口不定长数据接收功能

    上一篇文章中,我们详细分析了VTIM和VMIN的功能, <嵌入式Linux 串口编程系列2--termios的VMIN和VTIME深入理解> 也明白了这两个参数设计的初衷和使用方法,接下来 ...

  2. RT1052 LPSPI1 dma传输和UART2 DMA传输实现不定长数据接收

    买了一块飞凌的RT1052的开发板,主要是看中了它的核心板价格便宜,准备后面产品使用.使用了一段时间,然后今天准备做个记录,上一下关于LPSPI1和UART2的DMA操作. 由于现在RT1052的网上 ...

  3. STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)

    目录 前言 一.工程配置 二.串口DMA部分代码 1.源文件UART_DMA.c 2.头文件UART_DMA.h 3.stm32f1xx_it.c的修改 4.串口收发DMA测试 三.字符串数字提取代码 ...

  4. STM32从零到一,从标准库移植到HAL库,UART串口1以DMA模式收发不定长数据代码详解+常见问题 一文解析

    前言 本文的参考资料 感谢提供标准库版本的CSDN同学:这两篇文章至少是我看过的最详细的标准库配置DMA版本.而且代码实测稳定能用. STM32 | DMA配置和使用如此简单(超详细)_...| .. ...

  5. STM32软件学习笔记(一)基于HAL库的STM32F429单片机串口打印程序

    |版权声明:本文为博主原创文章,转载请注明出处.https://blog.csdn.net/NeverImagine_/article/details/95517664   目前ST官方有提供两种库文 ...

  6. 【32单片机学习】(6)STM32串口+DMA收发不定长数据

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1.DMA介绍 2.串口接收数据 3.实验现象 1.实验电路图 2.串口收发不定长数据视频演示 3.OLED 显示接收数据 ...

  7. stm32F051 HAL库+DMA+串口空闲中断

    STM32CubeMX 配置实现参考这里. 1. 串口空闲中断 1.1 UART_DMA方式接收数据 STM32串口使用DMA方式接收数据可以减小CPU的开销. 对于接收定长数据,可以将DMA接收缓冲 ...

  8. ZYNQ进阶之路14--PS端uart串口接收不定长数据

    ZYNQ进阶之路14--PS端uart串口接收不定长数据 导语 ZYNQ串口简介 实现步骤 导语 繁忙的博主又来了,本节我们实现一个比较简单的东西:串口.之前的章节中我们也有使用PS端的串口进行收发数 ...

  9. STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片

    STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片 STM32基础工程生成 首先使用STM32CUBEMX生成STM32的基础工程,配置时钟到7 ...

最新文章

  1. 谢文睿:西瓜书 + 南瓜书 吃瓜系列 11. 贝叶斯分类器
  2. Software-Defined Networking之搬砖的故事
  3. 001@多用派发队列,少用同步锁
  4. 什么时候该用MySQL,什么时候该用ES呢?
  5. HDU 6836 Expectation(矩阵生成树 + 期望)
  6. 嵌入式Linux交叉开发环境建立-NFS【ZT】
  7. LeetCode(59):螺旋矩阵 II
  8. 机器学习基础(二十)—— 数学语言与 Python 代码
  9. 李洪强-HEAD 和nil和NULL
  10. 数字电视智能卡的定义
  11. 如何用天气预警API接口进行快速开发
  12. Java面向对象编程练习题(28题集)
  13. 卡刷supersu和magisk实现安卓手机获取root权限
  14. powermockito测试私有方法_03 增强测试: 静态、私有方法处理
  15. 数据化运营17 留存:如何通过数据、社交、内容手段提升用户留存?
  16. 第四周项目3单链表应用(2)
  17. mysql安装报错181012_安装MySQL时提示requires .NET Framework 4. 0错误
  18. LeetCode 96~100
  19. 5. 机器学习基石-Why can Machine Learn?
  20. halcon算子——highpass_image--从图像中提取高频分量((过滤--图像边缘处理))

热门文章

  1. 视频教程-SpringBoot微信公众号开发-微信开发
  2. python 爬虫 爬取快手视频 批量解析 建议收藏
  3. 养成一个做笔记的良好习惯何乐而不为?
  4. 补充一下亚信科技(上海)面经(offer)
  5. Android通过包名启动应用微信qq淘宝
  6. Windows7主题包
  7. PAT 1033 旧键盘打字 (20 分) C++
  8. CRM实施能给客户带来什么好处
  9. SPI FLASH NAND FLASH 和NOR FLASH 的关系
  10. Java使用zip4j进行文件压缩