基于HAL库STM32串口驱动不定长数据接收_hal库串口接收不定长数据,这些年我所经历的所有面试

191 阅读11分钟

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 6
    
typedef 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;
    else
        return 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;//空
    else
        return 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 返回1
    memcpy(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 返回1
    
    len_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);
    


![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/30c24eb54ad24436838b892e1b667a36~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771252033&x-signature=srhuEYnGIhzWk8PI14c9qivH4eo%3D)
![img](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d73475f8961b41f88720127463b330ee~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771252033&x-signature=2MZpwZrGcxX11MI1dbcu7iiHmwE%3D)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://gitee.com/vip204888)**