STM32HAL库 环形队列实现串口接收中断

        在我们需要使用串口接收中断去解析大量指令或数据时,为了避免直接在串口中断函数中进行解析,可以使用环形队列作为缓冲区。

一、初始化结构体

        将环形缓冲区的head(队列头)和tail(队列尾)都初始化为0,表示缓冲区是空的,头和尾指向同一个位置。

typedef struct {
    uint8_t buffer[128];   
    uint16_t head;                    
    uint16_t tail;                   
} CircularBuffer;

CircularBuffer rxBuffer;

void CircularBuffer_Init(CircularBuffer* cb) {
    cb->head = 0;
    cb->tail = 0;
}

二、写数据到队列

  • 计算出下一个head位置 next = (cb->head + 1) % RX_BUFFER_SIZE,通过模运算确保head位置在缓冲区大小范围内。
  • 如果 next != cb->tail,意味着缓冲区未满,可以写入数据。
  • 将数据写入当前位置 cb->buffer[cb->head],然后更新headnext
  • 如果缓冲区已满(即next == cb->tail),则返回0,表示写入失败。
  • 否则返回1,表示写入成功。
int CircularBuffer_Write(CircularBuffer* cb, uint8_t data) {
    uint16_t next = (cb->head + 1) % RX_BUFFER_SIZE;
    if (next != cb->tail) {  
        cb->buffer[cb->head] = data;
        cb->head = next;
        return 1;  
    }
    return 0;  
}

 三、从队列读取数据

  • 如果headtail不相等,表示缓冲区中有数据可以读取。
  • 将数据从cb->buffer[cb->tail]中读取出来,并将其存储到data指向的内存中。
  • 更新tail为下一个位置 (cb->tail + 1) % RX_BUFFER_SIZE,确保尾指针循环回到缓冲区的起始位置。
  • 如果缓冲区为空(head == tail),则返回0,表示读取失败。
  • 否则返回1,表示读取成功。
int CircularBuffer_Read(CircularBuffer* cb, uint8_t* data) {
    if (cb->head != cb->tail) {  
        *data = cb->buffer[cb->tail];
        cb->tail = (cb->tail + 1) % RX_BUFFER_SIZE;
        return 1;  
    }
    return 0;  
}

四、获取缓冲区长度

        获取缓冲区中可用的数据字节数(即从tailhead之间的距离)。        

uint16_t CircularBuffer_Available(CircularBuffer* cb) {
    return (cb->head - cb->tail + RX_BUFFER_SIZE) % RX_BUFFER_SIZE;
}

代码示例 
 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.c
  * @brief   This file provides code for the configuration
  *          of the USART instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */
#define RX_BUFFER_SIZE 128 
uint8_t receivedByte;
CircularBuffer rxBuffer;
/* USER CODE END 0 */

UART_HandleTypeDef huart1;
UART_HandleTypeDef huart3;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 9600;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */
	HAL_UART_Receive_IT(&huart1, &receivedByte, 1);
  /* USER CODE END USART1_Init 2 */

}
/* USART3 init function */

void MX_USART3_UART_Init(void)
{

  /* USER CODE BEGIN USART3_Init 0 */

  /* USER CODE END USART3_Init 0 */

  /* USER CODE BEGIN USART3_Init 1 */

  /* USER CODE END USART3_Init 1 */
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 9600;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART3_Init 2 */

  /* USER CODE END USART3_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
  else if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspInit 0 */

  /* USER CODE END USART3_MspInit 0 */
    /* USART3 clock enable */
    __HAL_RCC_USART3_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**USART3 GPIO Configuration
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* USART3 interrupt Init */
    HAL_NVIC_SetPriority(USART3_IRQn, 4, 0);
    HAL_NVIC_EnableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspInit 1 */

  /* USER CODE END USART3_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
  else if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspDeInit 0 */

  /* USER CODE END USART3_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART3_CLK_DISABLE();

    /**USART3 GPIO Configuration
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);

    /* USART3 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspDeInit 1 */

  /* USER CODE END USART3_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */



int fputc(int ch, FILE *f)//printf
{
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1,0xffff);  //发送一个字节的数据到指定的串口
	return (ch);
}
 
int fgetc(FILE * f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1,&ch, 1, 0xffff);
  return ch;

}

//初始化环形队列
void CircularBuffer_Init(CircularBuffer* cb) {
    cb->head = 0;
    cb->tail = 0;
}
//将数据添加到环形缓冲区
int CircularBuffer_Write(CircularBuffer* cb, uint8_t data) {
    uint16_t next = (cb->head + 1) % RX_BUFFER_SIZE;
    if (next != cb->tail) {  
        cb->buffer[cb->head] = data;
        cb->head = next;
        return 1;  
    }
    return 0;  
}

//从环形缓冲区读取数据
int CircularBuffer_Read(CircularBuffer* cb, uint8_t* data) {
    if (cb->head != cb->tail) {  
        *data = cb->buffer[cb->tail];
        cb->tail = (cb->tail + 1) % RX_BUFFER_SIZE;
        return 1;  
    }
    return 0;  
}

//获取环形缓冲区中的字节
uint16_t CircularBuffer_Available(CircularBuffer* cb) {
    return (cb->head - cb->tail + RX_BUFFER_SIZE) % RX_BUFFER_SIZE;
}


// USART1 中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        CircularBuffer_Write(&rxBuffer, receivedByte);
        HAL_UART_Receive_IT(&huart1, (uint8_t*) &receivedByte, 1);
    }
}













/* USER CODE END 1 */

在使用 HAL 进行串口接收时,可以使用 HAL_UART_Receive_IT() 函数来启动中断接收接收到的数据会存储在串口的硬件缓冲区中,我们可以在 HAL_UART_RxCpltCallback() 回调函数中实现对数据的处理。 为了实现接收多字节数据,我们可以使用一个环形缓冲区来存储接收到的数据。具体实现步骤如下: 1. 定义环形缓冲区结构体 ```c typedef struct { uint8_t* buffer; // 缓冲区指针 uint32_t size; // 缓冲区大小 uint32_t head; // 缓冲区头指针 uint32_t tail; // 缓冲区尾指针 } RingBuffer; ``` 2. 初始化环形缓冲区 ```c void RingBuffer_Init(RingBuffer* buffer, uint8_t* data, uint32_t size) { buffer->buffer = data; buffer->size = size; buffer->head = 0; buffer->tail = 0; } ``` 3. 缓冲区写入数据 ```c void RingBuffer_Write(RingBuffer* buffer, uint8_t* data, uint32_t size) { uint32_t i; for (i = 0; i < size; i++) { uint32_t next = (buffer->head + 1) % buffer->size; if (next != buffer->tail) { buffer->buffer[buffer->head] = data[i]; buffer->head = next; } else { // 缓冲区已满 break; } } } ``` 4. 缓冲区读取数据 ```c uint32_t RingBuffer_Read(RingBuffer* buffer, uint8_t* data, uint32_t size) { uint32_t i; for (i = 0; i < size; i++) { if (buffer->tail != buffer->head) { data[i] = buffer->buffer[buffer->tail]; buffer->tail = (buffer->tail + 1) % buffer->size; } else { // 缓冲区已空 break; } } return i; } ``` 5. 在 HAL_UART_RxCpltCallback() 回调函数中实现数据的存储 ```c void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart) { static uint8_t data[64]; static uint32_t count = 0; uint32_t size = 0; if (huart->Instance == USARTx) { // 读取接收到的数据 data[count++] = huart->Instance->DR & 0xFF; if (count >= sizeof(data)) { // 缓冲区已满,存储数据并清空计数器 RingBuffer_Write(&rx_buffer, data, count); count = 0; } // 继续启动中断接收 HAL_UART_Receive_IT(huart, &data[count], 1); } } ``` 6. 在主程序中读取缓冲区中的数据 ```c uint8_t data[64]; uint32_t size = RingBuffer_Read(&rx_buffer, data, sizeof(data)); if (size > 0) { // 处理接收到的数据 } ``` 通过以上步骤,我们就可以实现STM32F4 中使用 HAL 进行串口接收,并使用环形缓冲区来存储多字节数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值