STM32 uart dma接收,环形buff,带缓冲

串口接收看一个就够了!

实现效果,连着发3条数据,但主函数处理一命令需要5秒钟,或者处理不过来,就需要缓冲下来,然后再处理,效果如下图:

看log看的出来第一条消息123立马执行了,执行需要5秒,第二条abcde则是在5秒后执行,第三条12345在10秒后执行,这些数据被缓冲起来了,并没有被覆盖,跟预期一样。

主函数代码如下图:

原理:测试使用串口3,使用dma接收,启用环形模式,开启串口空闲中断。

环形dma接收的效果是只要有数据就会被接收在缓冲区里面,缓冲区满了就从头开始覆盖。

串口空闲中断,在一包数据后会产生一个串口空闲中断。

环形dma接收+串口空闲中断 = 完美

需要注意的是,环形dma接收在缓冲区满的时候,最后一包数据会被分成两半,一半在后面一半在前面(举例:buff大小为10,发了3包数据1234、abcd、ABCD,那么buff里面就是CD34abcdAB,ABCD这包数据被分成两半了),在串口数据处理中,一定是希望数据连在一起好操作,所以要解决这个问题,第一种方法就是令搞一个新的buff,每一次把数据拷贝进去,但是这也太捞了,而且要缓冲数据就要开好几个buff管理也复杂。第二种方法就是把最后一包数据拷贝在buff后面,初始化缓冲区的时候初始化15个字节的buff,把前面的10个用来充当环形buff,交给dma管理,后面的5个字节手动管理,把最后一包数据后半部分给手动拷贝回来,这样数据就连续了(举例:buff大小为10+5,发了3包数据1234、abcd、ABCD,那么buff里面就是CD34abcdAB*****,*代表空,ABCD这包数据依然分成两半了,但手动拷贝到后面buff里面的数据就是CD34abcdABCD***),然后只需要记录每包数据的数据头指针地址与数据长度可以了。

那么怎么管理这个数据头指针地址与数据长度,当然就是队列。怎样更简单,就是freertos,rtos里面有一个消息队列,完美。

初始化一个任务,一个队列的代码如下(STM32CUBEMX 配置的,自己只是加了一些):

这里又来了一遍osMessageQueueNew,是因为配置的时候默认一个宽度是2个字节16个总共32个字节,自己用的是一个结构体,当时不知道结构体要多大,这里就再来一遍初始化,这样之后在UART_STRUCT添加内容也不用打开CUBEMX改这个队列每个数据的大小。


  /* Create the queue(s) */
  /* creation of uartQueue */
  uartQueueHandle = osMessageQueueNew (16, sizeof(uint16_t), &uartQueue_attributes);

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  // 16*2/8=4,实际缓冲只有4个
  uartQueueHandle = osMessageQueueNew (16, sizeof(UART_STRUCT), &uartQueue_attributes);
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of uartTask */
  uartTaskHandle = osThreadNew(UartTask, NULL, &uartTask_attributes);


/* USER CODE END Header_UartTask */
void UartTask(void *argument)
{
  /* USER CODE BEGIN UartTask */
  UART_STRUCT msgRecv;
  /* Infinite loop */
  for(;;)
  {
	vTaskDelay(5000);//这里阻塞,模拟任务执行时间很久很久
    if(xQueueReceive(uartQueueHandle, &msgRecv, portMAX_DELAY)== pdTRUE)
	{
		DBG("USART3 rx len = %d HEX:", msgRecv.length);
		DBG_HEX(msgRecv.pBuf, msgRecv.length);
	}
  }
  /* USER CODE END UartTask */
}

stm32l1xx_it.c的代码:

这里自己加入了USART3_DMAHandler,为了处理空闲中断。

切记usart3 tx dma配置成单次 usart3 rx dma配置成环形,默认是单次,需要自己改rx为环形,否则buff满了就停掉了,就会出现刚开始几次可以,后面几次数据不可以了。 

/**
  * @brief This function handles USART3 global interrupt.
  */
void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */
  extern void USART3_DMAHandler(void);
  USART3_DMAHandler();
  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */

  /* USER CODE END USART3_IRQn 1 */
}

usart.c代码:

都是自动生成的没改什么,生成后的初始化代码 加入开启空闲中断,加入启用dma接收就好了。

处理的都在.c文件最下面空闲中断里面。交给dma管理的256个字节,后面多开64个字节是为了处理最后一包数据,这样一包数据不得大于64,可以缓冲4包,初始化队列那里的大小就是根据或者算出来的。

/* 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 */

#include "cmsis_os.h"
#include <stdio.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

// 定义接收buff的大长度
#define UART_DMA_RX3_BUFFSIZE (256)
// 长度+64是为了处理环形buf的尾数据被分割?,这里要保证一帧数据小于64
static uint8_t UART_DMA_RX3_Buffer[UART_DMA_RX3_BUFFSIZE + 64];

/* USER CODE END 0 */

UART_HandleTypeDef huart3;
DMA_HandleTypeDef hdma_usart3_rx;
DMA_HandleTypeDef hdma_usart3_tx;

/* 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 = 115200;
  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 */
	__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);

	if (HAL_UART_Receive_DMA(&huart3, (uint8_t *)UART_DMA_RX3_Buffer, UART_DMA_RX3_BUFFSIZE) != HAL_OK)
	{
		Error_Handler();
	}
  /* USER CODE END USART3_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  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_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* USART3 DMA Init */
    /* USART3_RX Init */
    hdma_usart3_rx.Instance = DMA1_Channel3;
    hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart3_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart3_rx);

    /* USART3_TX Init */
    hdma_usart3_tx.Instance = DMA1_Channel2;
    hdma_usart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart3_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart3_tx.Init.Mode = DMA_NORMAL;
    hdma_usart3_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart3_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart3_tx);

    /* USART3 interrupt Init */
    HAL_NVIC_SetPriority(USART3_IRQn, 5, 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==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 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);
    HAL_DMA_DeInit(uartHandle->hdmatx);

    /* 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)
{
#if SEGGER_RTT
    SEGGER_RTT_PutChar(0, ch);
#else
    while ((USART3->SR & 0X40) == 0);
    USART3->DR = (uint8_t)ch;
#endif
    return ch;
}

// 处理环形buf的尾数据
static void uart_cpy(uint8_t *buf, uint32_t read_oft, uint32_t write_oft, uint32_t buf_size)
{
	if (read_oft == write_oft)
		return;
	if (read_oft < write_oft)
		return;
	// 数据被分成两半了,buf前面也有,这里把buff前面的数据拷贝到buf尾,提前已经预留了空间
	memcpy(buf + buf_size, buf, write_oft);
}

void USART3_DMAHandler(void)
{
	static uint32_t lastUartCnt = 0;
	uint32_t uartCnt = 0;
	UART_STRUCT msgSend;

	if (RESET != __HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE))
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart3);
		uartCnt = UART_DMA_RX3_BUFFSIZE - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
		uart_cpy(UART_DMA_RX3_Buffer, lastUartCnt, uartCnt, UART_DMA_RX3_BUFFSIZE);
		msgSend.pBuf = UART_DMA_RX3_Buffer + lastUartCnt;																// 找到当前一帧数据的头的地址
		msgSend.length = uartCnt > lastUartCnt ? uartCnt - lastUartCnt : UART_DMA_RX3_BUFFSIZE + uartCnt - lastUartCnt; // 计算当前一帧数据的长度
		lastUartCnt = uartCnt;

		// 发消息给串口数据处理任务处理数据
		{
			BaseType_t xHigherPriorityTaskWoken = pdTRUE;
			extern osMessageQueueId_t uartQueueHandle;
			xQueueSendFromISR(uartQueueHandle, &msgSend, &xHigherPriorityTaskWoken);
		}
	}
}

/* USER CODE END 1 */

usart.h代码:

只是加入了一个结构体

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.h
  * @brief   This file contains all the function prototypes for
  *          the usart.c file
  ******************************************************************************
  * @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 */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

typedef struct {
    uint8_t *pBuf;		// 数据起始地址
    uint32_t length;	// 数据长度
} UART_STRUCT;

/* USER CODE END Includes */

extern UART_HandleTypeDef huart3;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_USART3_UART_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __USART_H__ */

最后还有一个自己用的debug.h

#ifndef __DEBUG_H
#define __DEBUG_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include "main.h"

#define DEBUG 1
#define SEGGER_RTT 0//要添加jlink的rtt 使用jlink才可以

#if DEBUG

#if SEGGER_RTT
#include "./SEGGER_RTT/SEGGER_RTT.h"
#endif

#if SEGGER_RTT
extern __IO uint32_t uwTick;//stm32 HAL库FREERTOS的tick,用它来加入时间戳
	
	//jlink的SEGGER_RTT_printf不能使用浮点,所以使用浮点用APP_DBG 正常使用DBG
    #define DBG(format,...)  SEGGER_RTT_printf(0, "[%02d:%02d]:"format, (uwTick/1000)/60, (uwTick/1000)%60, ##__VA_ARGS__)
	//使用自带的printf需要自己实现int fputc(int ch, FILE *f)这个函数
    #define APP_DBG(format,...)  printf("[%02d:%02d]:"format, (uwTick/1000)/60, (uwTick/1000)%60, ##__VA_ARGS__)

    #define DBG_R(format,...)  SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_RED"[%02d:%02d]:"format RTT_CTRL_RESET, (uwTick/1000)/60, (uwTick/1000)%60, ##__VA_ARGS__)
    #define DBG_G(format,...)  SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_GREEN"[%02d:%02d]:"format RTT_CTRL_RESET, (uwTick/1000)/60, (uwTick/1000)%60, ##__VA_ARGS__)
    #define DBG_B(format,...)  SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_BLUE"[%02d:%02d]:"format RTT_CTRL_RESET, (uwTick/1000)/60, (uwTick/1000)%60, ##__VA_ARGS__)
#else
    #define DBG(format,...)  printf(format, ##__VA_ARGS__)
    #define APP_DBG(format,...)  printf(format, ##__VA_ARGS__)
#endif
	//打印函数名+行号
    //#define DBG(format,...)  printf("[%s][%05d]:"format, __func__, __LINE__, ##__VA_ARGS__) 
	//打印文件名+打印函数名+行号
    //#define DBG(format,...) printf("["__FILE__"][%s][%05d]: "format, __func__, __LINE__, ##__VA_ARGS__)
	
	//打印16进制
	#define DBG_HEX(buff, len) do{\
								for(int i = 0; i < len; i++)\
								{ printf("%02x ", buff[i]); }\
								printf("\n");\
								}while(0);
#else  
    #define DBG(format,...)  
	#define APP_DBG(format,...)
	#define DBG_HEX(buff, len)
#endif 

#ifdef __cplusplus
}
#endif

//Demo
#if 0 //放uart.c里面了
int fputc(int ch, FILE *f)
{
#if SEGGER_RTT
    SEGGER_RTT_PutChar(0, ch);
#else
    while ((USART1->SR & 0X40) == 0);
    USART1->DR = (uint8_t)ch;
#endif
    return ch;
}
#endif

#endif /* __DEBUG_H */

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于STM32的UART DMA同时接收和发送的示例程序: ```c #include "stm32f10x.h" #include <stdio.h> #include <string.h> #define BUFFER_SIZE 100 uint8_t rx_buffer[BUFFER_SIZE]; uint8_t tx_buffer[BUFFER_SIZE]; void dma_init(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tx_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel5, &DMA_InitStructure); USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE); DMA_Cmd(DMA1_Channel4, ENABLE); DMA_Cmd(DMA1_Channel5, ENABLE); } void uart_init(void) { USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } int main(void) { uart_init(); dma_init(); while (1) { if (USART_GetFlagStatus(USART1, USART_FLAG_TC) == SET) { DMA_Cmd(DMA1_Channel4, DISABLE); memcpy(tx_buffer, rx_buffer, BUFFER_SIZE); DMA_SetCurrDataCounter(DMA1_Channel4, BUFFER_SIZE); DMA_Cmd(DMA1_Channel4, ENABLE); } } } ``` 这个程序实现了UART的循环DMA接收DMA发送功能。在初始化函数`dma_init()`中,配置了DMA通道4和DMA通道5,分别用于UARTDMA发送和DMA接收。在主函数中,通过判断UART的发送完成标志位,来触发DMA传输,并将接收到的数据复制到发送缓冲区中,实现了循环DMA传输的功能。需要注意的是,在实际应用中,可能需要根据具体的需求对DMA传输模式和中断处理方式进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值