STM32F429+FreeRTOS队列 串口DMA双缓存数据接收

2023-05-16

前言

最近做项目需要用到数据大量数据处理的应用场景,本来想使用串口空闲中断+DMA传输+FreeRTOS队列的方式接收数据,然而实际使用中发现效果不理想,经常丢包。最后查明原因为在空闲中断频繁关闭/开启DMA接收会影响数据接收效率,所以在查找了相关资料之后,决定使用DMA双缓存的方式接收数据,实际测试结果表明,这种方式大大增加了数据接收效率,现在把这个知识分享给大家。

一、开发环境

芯片:STM32F429

开发软件:keilv5、STM32CubeMX

os:FreeRTOS

二、原理

通过查看官方的《STM32F4xx中文参考手册》,我们得到下面资料:

使用DMA双缓存模式有以下几个特点:

  • 使能双缓存模式会自动使能循环模式。
  • 双缓存模式只支持外设到存储器或者存储器到外设方向,其他方向不允许使用。
  • 双缓存模式适合接收定长数据,如果是不定长数据,建议使用空闲帧处理。 

三、使用DMA双缓存

1、使用STM32CubeMX新建工程,配置串口和DMA。

2、 定义变量

#define UART_BUFF_SIZE  25

#pragma pack(4)
typedef struct
{
    uint16_t len;
    uint8_t data[UART_BUFF_SIZE];
}s_usart_data;
#pragma pack()


QueueHandle_t        queue_mes;  
s_usart_data uart_buf[2];

 3、定义DMA回调函数

//DMA 缓存0 传输结束回调函数
void DMA_M0_RC_Callback(DMA_HandleTypeDef *hdma)
{
    BaseType_t xHigherPriorityTaskWoken; 

    uart_buf[0].len = hdma->Instance->NDTR;
    xQueueSendFromISR(queue_mes,&uart_buf[0],&xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

//DMA 缓存1 传输结束回调函数
void DMA_M1_RC_Callback(DMA_HandleTypeDef *hdma)
{
    BaseType_t xHigherPriorityTaskWoken; 

    uart_buf[1].len = hdma->Instance->NDTR;
    xQueueSendFromISR(queue_mes,&uart_buf[1],&xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

//DMA 传输错误回调函数
void DMA_Error_Callback(DMA_HandleTypeDef *hdma)
{
    //里面做一些异常处理
}

4、使能DMA传输

//使能DMA
void Enable_Uart(void)
{
		uint32_t u32wk0;
	
	  SET_BIT(huart1.Instance->CR3,USART_CR3_DMAR);
    HAL_DMAEx_MultiBufferStart_IT(&hdma_usart1_rx,
                                  (uint32_t)(&huart1.Instance->DR),
                                  (uint32_t)&uart_buf[0].data[0],
                                  (uint32_t)&uart_buf[1].data[0],
                                   UART_BUFF_SIZE);
		
    //这里是解决DMA在启动时,如果接收到大量数据会出现死机的问题
    u32wk0 = huart1.Instance->SR;  
    u32wk0 = huart1.Instance->DR;
	  UNUSED(u32wk0);
}

5、注册回调函数

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_PIN_10;
    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_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA2_Stream2;
    hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
	hdma_usart1_rx.XferCpltCallback = DMA_M0_RC_Callback;
    hdma_usart1_rx.XferM1CpltCallback = DMA_M1_RC_Callback;
    hdma_usart1_rx.XferErrorCallback = DMA_Error_Callback;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

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

  /* USER CODE END USART1_MspInit 1 */
  }
}

6、编写接收代码任务,数据处理可以根据自己需要添加,这里把收到的数据再传输出去。

void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
	BaseType_t ret = pdFALSE;
  /* Infinite loop */
  while(1)
  {
    ret = xQueueReceive(queue_mes,&queue_data,portMAX_DELAY);
	if(ret == pdTRUE)
	{
		HAL_UART_Transmit(&huart1,queue_data.data,queue_data.len,100);
	}
  }
  /* USER CODE END StartDefaultTask */
}

7、主函数创建队列和使能DMA传输

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  queue_mes = xQueueCreate(10,sizeof(s_usart_data));
  Enable_Uart();             
  /* USER CODE END 2 */

  /* Call init function for freertos objects (in freertos.c) */
  MX_FREERTOS_Init();
  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、测试

使用串口工具按1ms的频率发送25个字节数据,测试结果如下:

从测试结果来看,没有发生丢包现象,非常稳定,符合预期目标。

后记

使用DMA双缓存+FreeRTOS队列的方式接收定长数据,适合要求实时性非常高的应用场景,DMA双缓存机制保证数据不会丢包,而且采用队列的方式传输,更加高效和安全,防止出现资源冲突的问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F429+FreeRTOS队列 串口DMA双缓存数据接收 的相关文章

随机推荐

  • Spring Boot Kafka概览、配置及优雅地实现发布订阅

    本文属于原创 xff0c 转载注明出处 xff0c 欢迎关注微信小程序小白AI博客 微信公众号小白AI或者网站 https xiaobaiai net 文章目录 1 前言2 Spring Kafka功能概览2 1 自动创建主题2 2 发送消
  • An Ota Package Tool

    文章目录 OtaPackageToolInstallationBinary InstallationInstalling Tool from Source UsagePreparationExamplesFull UpdatesIncrem
  • [开源]OTA打包工具

    文章目录 OTA打包工具 96 ota packer 96 安装二进制安装源码编译安装 使用准备示例全量包增量包生成关于OTA包版本之间文件变更类型说明 96 ota packer 96 使用条件License OTA打包工具 ota pa
  • [golang]包管理

    文章目录 1 GOPATH vs Go Modules2 Go Modules Go Module Proxy 和 goproxy cn3 Go Modules 相关知识3 1 语义化版本控制规范3 2 go mod3 3 go sum3
  • 【名名的Bazel笔记】自定义规则实现将多个静态库合并为一个动态库或静态库

    文章目录 1 前言2 自定义规则实现2 1 规则功能2 2 实现规则的理论基础2 3 规则代码实现 3 总结4 参考资料 1 前言 为了实现如标题所述的将多个静态库合并为一个动态库 xff0c 内置的 Bazel 规则是没有这个功能的 xf
  • 【名名的Bazel笔记】自定义工具链实现交叉编译

    文章目录 1 前言2 Non Platform 方式3 Platform 方式3 1 平台3 1 1 概述3 1 2 定义约束和平台3 1 3 通用的约束和平台3 1 4 指定平台构建 3 2 工具链3 3 Platform 43 Tool
  • PX4/Pixhawk---uORB深入理解和应用

    The Instructions of uORB PX4 Pixhawk 软件体系结构 uORB 主题发布 主题订阅 1 简介 1 1 PX4 Pixhawk的软件体系结构 PX4 Pixhawk的软件体系结构主要被分为四个层次 xff0c
  • join函数

    Python中我们经常会用到join函数 join函数的基本格式是 xff1a span class token string 39 39 span span class token punctuation span join span c
  • Glance详解

    Glance简介 Glance是OpenStack平台中负责镜像服务的组件 xff0c 其功能包括系统镜像的查找 注册和获取等 简单来说glance的功能就是用户可以通过其提供的REST API查询和获取镜像元数据 xff0c 通过Glan
  • 深入理解k8s中的service概念

    文章目录 service的概念kube proxy的作用kube proxy的三种模式Userspace Proxy ModeIptables Proxy ModeIPVS proxy mode service的概念 在k8s集群中 xff
  • Java_Save could not be completed. Try File> Save As. if the problem persists.

    所以最好不要用 开头的符号作为变量名 xff0c 变量名中含有一些奇怪的字符也会产生编码问题
  • Cinder详解

    文章目录 理解cindercinder架构cinder apicinder volumecinder schedulervolume providercinder DB cinder设计思想 理解cinder 操作系统得到存储空间一般有两种
  • shell:重启&&关机

    文章目录 shutdownhaltpoweroffrebootinitsync shutdown 关机重启命令 shutdown h 10十分钟后关机shutdown h 0马上关机shutdown h now马上关机shutdown c取
  • 基于docker的Jenkins-Gitlab-Ansible实现自动化部署

    环境准备 安装docker xff0c 略 拉取Jenkins Gitlab镜像 docker pull jenkins docker pull gitlab ce 部署 Jenkins 生成Jenkins span class token
  • SSH远程登录出现的常见问题与解决方法

    运维工程师经常会使用ssh远程登录主机 ssh的使用并不复杂 xff0c 但是也有可能会遇到各种各样的错误 xff0c 我在本篇博文中总结了一些常见的ssh报错信息与解决方法 Connection refused 可能原因 xff1a 网络
  • 从两个角度理解Kubernetes基本概念

    想要理解Kubernetes集群首先要思考两个问题 xff1a 它是由什么组成的 它是怎样工作的 而想要搞清楚这两个问题我们可以在两个不同的层面寻找答案 从物理层面看 从直观的层面来看 xff0c Kubernetes将多个物理机或虚拟机汇
  • 基于CentOS 7.6搭建Kubernetes 1.17.3集群demo

    本demo仅涉及基本的Kubernetes功能实践与介绍 xff0c 无高可用配置 部署版本为当前时间阿里开源镜像站提供的最新版本1 17 3 文章目录 部署环境安装准备域名解析关闭SELinux和防火墙配置yum源时间同步禁用swap加载
  • 为Kubernetes部署dashboard组件

    dashboard是Kubernetes社区中一个很受欢迎的开源项目 xff0c 它可以为使用者提供一个可视化web界面来进行Kubernetes的管理和使用 环境信息 组件版本Kubernetesv1 17 3dashboardv2 0
  • linux UIO驱动实践

    linux UIO驱动实践 环境搭建platform 设备驱动UIO驱动 环境搭建 Ubuntu20地址 虚拟机安装与配置见博客开头 xff1a 驱动虚拟环境搭建记录 一直以为用镜像直接安装的Ubuntu没有内核源码 xff0c 不能用来编
  • STM32F429+FreeRTOS队列 串口DMA双缓存数据接收

    前言 最近做项目需要用到数据大量数据处理的应用场景 xff0c 本来想使用串口空闲中断 43 DMA传输 43 FreeRTOS队列的方式接收数据 xff0c 然而实际使用中发现效果不理想 xff0c 经常丢包 最后查明原因为在空闲中断频繁