细说MCU用DMA控制ADC采样和串口传送的实现方法

news2024/9/21 0:34:02

目录

一、建立工程

1.相同的配置

2.配置ADC

3.配置DMA

二、代码修改

1.定义存储ADC采样结果的数组

2.启动ADC与定时器

3.编写主程序代码

4.重定义回调函数

5.查看结果

三、修改DMA模式

1. 修改DMA模式为Circular

2.查看结果


        采用DMA(Direct Memory Access,直接存储器访问)控制器实现A/D采样。采用这种方式时,一旦配置好ADC参数及所使用的DMA通道,DMA控制器就会自动将A/D转换结果送至指定的存储器空间中(数组)。在使用A/D转换数据时,只需要在主程序中读取相应的数组变量就可以了,无需再调用HAL_ADC_GetValue()等函数来获取A/D转换结果。

        采用DMA的方式可以不占用CPU的资源,直接由DMA控制器来实现外设(或存储器与存储器之间的数据交互。所以,这种方式在实际中是比较实用的,并且可以极大地提高CPU的工作效率。

一、建立工程

       本文项目以来的硬件工程及配置参考本文作者的下述文章,工程配置基本一致。本文只描述不一样的地方。细说MCU用定时器控制ADC采样频率的实现方法并通过Simulink查看串口输出波形-CSDN博客  https://wenchm.blog.csdn.net/article/details/140523545icon-default.png?t=N7T8https://wenchm.blog.csdn.net/article/details/140523545

1.相同的配置

  • 配置串口;
  • 配置TIM3,TIM4;
  • 选择时钟源和Debug模式,配置系统时钟和ADC时钟;
  • 配置GPIO,LED;

2.配置ADC

        在硬件配置界面中打开Analog→ADC1,在其模式Mode区中,通道1(IN1)选择IN1 Single-ended;在下面的配置(Configuration)区中,需要对几个参数进行调整:

        首先,在ADC设置(ADC_Settings)参数栏,依然可以不对ADC的时钟进行分频,还将预分频参数(Clock Prescaler)选择为Asynchronous clock mode divided by 1。本例中用定时器实现对采样频率的控制。

        随后,依然将ADC设置(ADC_Settings)参数栏中连续转换模式(Continuous Conversion Mode)设置为Disabled,由于要用DMA,所以需要使能DMA连续请求(DAM Continuous Requests)参数。

3.配置DMA

        打开DMA设置(DMA Settings)选项卡,先添加一个ADC1的DMA请求。DMA有多个可选通道,这里随便选择一个即可(共有两个DMA,每个都有8个通道)。此外,优先级有四级,从低(Low)到很高(Very High),可以先保持默认值Low。

        DMA请求设置(DMA Request Settings)栏,可以设置DMA的模式;模式有两种:常规(Normal)和循环(Circular)。如果是Normal模式,仅会执行一次DMA,若要继续执行,则要重新启动。在Circular模式下,可以连续执行DMA。此例中,先将DMA模式设置为Normal。此外,在增量地址(Increment Address)中,勾选上存储器(Memory),这样就可以将数据顺次存储到一个数组中。因为A/D的转换结果需要一个16位的数,所以将数据宽度(Data Width)设置为半字(Half Word),一个字为32位。

        ADC1的DMA请求设置完毕后,设置DMA连续请求(DAM Continuous Requests)参数为Enabled。

        在ADC规则转换模式(ADC_Regular_ConversionMode)栏,还是将外部触发转换源(External Trigger Conversion Source)选择为Timer 3 Trigger Out event。在ADC规则转换模式参数栏中,将Rank下的采样时间选择为2.5个周期。

        由于是使用DMA来实现将A/D采样结果传递到存储器(数组)的,所以无需配置ADC的中断。不过,因为配置了ADC的DMA功能,所以会用到DMA的中断。由于上面配置的是DMA1的通道1,所以会自动开启DMA1的通道1中断。打开ADC配置界面中的NVIC设置(NVIC Settings),可以看到DMA1 channel 1 global interrupt已经自动被使能了,并且不能取消。另外一个ADC1的中断(ADC1 and AD2 global interrupt)由于用不到,所以无需开启。

二、代码修改

1.定义存储ADC采样结果的数组

        首先定义存储ADC采样结果的数组,本例中用数组变量ADC1ConvertedData。将存储ADC采样结果的数组定义为全局变量,同时定义一个后面会用到的变量ADCDMAFlag,将它们一并放到主程序中的注释对中:

/* USER CODE BEGIN PV */
uint16_t ADC1ConvertedData[ADC_CONVERTED_DATA_BUFFER_SIZE] = {0};
uint8_t ADCDMAFlag = 0;
/* USER CODE END PV */

        其中,数组长度ADC_CONVERTED_DATA_BUFFER_SIZE可以定义到main.h中:

/* USER CODE BEGIN Private defines */
#define ADC_CONVERTED_DATA_BUFFER_SIZE (uint16_t) 60
/* USER CODE END Private defines */

2.启动ADC与定时器

        本例中无需开启ADC1的中断。不过,要在主函数的初始化代码中调用ADC校验函数HAL_ADCEx_Calibration_Start,启动DMA方式的ADC转换(通过调用HAL_ADC_Start_DMA函数),并开启TIM3(通过调用HAL_TIM_Base_Start)。将上述三个函数的调用放到while(1)之前、MX_ADC1_Init()之后的注释对中:

 /* USER CODE BEGIN 2 */
  HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);
  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1ConvertedData,ADC_CONVERTED_DATA_BUFFER_SIZE);
  HAL_TIM_Base_Start(&htim3);
  HAL_TIM_Base_Start_IT(&htim4);
  HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
 /* USER CODE END 2 */

        ADC的采样是由TIM3控制的,采样值存入存储器(数组)的过程是通过DMA完成的,即ADC采样值在DMA控制器的控制下直接传送到数组ADC1ConvertedData中。虽然没有开启ADC1的中断,但在DMA完成设定长度的ADC采样数据传递后,也会调用一次回调函数HAL_ADC_ConvCpltCallback()。这里所谓的“设定长度”,就是函数HAL_ADC_Start_DMA()中的第三个参数。该参数在前面的代码中被设定为60。

        TIM4用来产生信号源。

3.编写主程序代码

        如果要通过串口送出采样值数据,可以在本次DMA传送完毕后进行。如果DMA还在更新时就进行串口数据发送,可能会出现数据不连续的情况。所以,可以在回调函数HAL_ADC_ConvCpltCallback()中将一个标志变量置位(可使用前面定义的变量ADCDMAFlag),置位就表示DMA传送完毕。然后,在while(1)循环中,以此标志位为条件,实现一段完整的采样值数据发送。串口数据发送,可以通过在主程序中调用串口发送函数来实现。

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  if(ADCDMAFlag == 1)
	  {
		  ADCDMAFlag = 0;
		  HAL_ADC_Stop_DMA(&hadc1);
		  HAL_UART_Transmit(&huart2,(uint8_t*)&ADC1ConvertedData,ADC_CONVERTED_DATA_BUFFER_SIZE*2,0xFFFF);
		  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1ConvertedData,ADC_CONVERTED_DATA_BUFFER_SIZE);
		  HAL_Delay(1000);
	  }
  }
  /* USER CODE END 3 */

        上面这段代码中有两个是控制DMA的函数,有一个是串口发送数据的函数。第一个函数是让DMA停止工作,暂停数据搬运,然后用函数HAL_UART_Transmit发送A/D采样数据。注意,在HAL_UART_Transmit的参数中,设置发送数据的长度为ADC采样数据的2倍,这是因为串口每次只能发送1个字节的数据,而一个A/D采样值会占用2个字节。数据发送完毕后,再重新启动ADC的DMA传输。

4.重定义回调函数

        此外,在main.c中重新定义回调函数HAL_ADC_ConvCpltCallback():

/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
	ADCDMAFlag =1;
}

//信号源
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
/* USER CODE END 4 */

5.查看结果

        施加信号源(可以是PA5)到ADC输入端PA0上,打开串口接收的Simulink模型,即可看到通过串口送来的信号波形。

三、修改DMA模式

1. 修改DMA模式为Circular

        上面例子中,主程序每间隔1000 ms发送一组数据;每次发送前要关闭DMA,发送后再重启。这种方式送来的两组数据其实并非连续的数据。那么,如何让串口实时向外连续发送A/D采样的数据呢?

        在前面配置ADC1的DMA、设置ADC1的DMA请求的模式时,选择的是Normal。如果选择Circular,DMA就会持续传送ADC采样数据到数组中,不过会循环覆盖;如果能够在下次DMA数据传递完成前将数据发送出去,就不会有影响。假如还是设置ADC采集缓冲区长度为60,则DMA一次会传送60个采样值数据;因为采样频率为1 kHz,所以完成这些数据的采样需要60 ms的时间。加上DMA的处理时间,DMA完成这些数据的传递至少需要60 ms。这60个ADC采样值,占120个字节。串口发送1个字节的数,至少要发送10个二进制位(8个数据位、1个停止位和1个起始位),所以发送120个字节的数据,对应的二进制位数为1200,而设置的串口波特率为115200 bit/s发送1200位需要的时间为(1200/115200)s,约为10.4 ms。这个时间小于DMA搬运一次数据所需的60 ms。所以完全可以实现通过串口的数据实时发送。

        修改while(1)循环中的代码如下:

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  //DMA标准模式
	  /*if(ADCDMAFlag == 1)
	  {
		  ADCDMAFlag = 0;
		  HAL_ADC_Stop_DMA(&hadc1);
		  HAL_UART_Transmit(&huart2,(uint8_t*)&ADC1ConvertedData,ADC_CONVERTED_DATA_BUFFER_SIZE*2,0xFFFF);
		  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1ConvertedData,ADC_CONVERTED_DATA_BUFFER_SIZE);
		  HAL_Delay(1000);
	  } */
	  //DMA循环模式
	  if(ADCDMAFlag ==1)
	  {
		  ADCDMAFlag =0;
		  HAL_UART_Transmit(&huart2,(uint8_t *)&ADC1ConvertedData,ADC_CONVERTED_DATA_BUFFER_SIZE*2,0xFFFF);
	  }
  }
  /* USER CODE END 3 */

2.查看结果

 

         DMA模式修改为循环后,串口的工作效率更高,不丢数据,达到了实时传递数据的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1936974.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python爬虫(1) --基础知识

爬虫 爬虫是什么? spider 是一种模仿浏览器上网过程的一种程序,可以获取一些网页的数据 基础知识 URL 统一资源定位符 uniform resource locator http: 超文本传输协议 HyperText Transfer Protocol 默认端口 80 https: 安全的超文本传输协议 security…

docker 部署wechatbot-webhook 并获取接口实现微信群图片自动保存到chevereto图库等

功能如图: docker部署 version: "3" services:excalidraw:image: dannicool/docker-wechatbot-webhook:latestcontainer_name: wechatbot-webhookdeploy:resources:limits:cpus: 0.15memory: 500Mreservations:cpus: 0.05memory: 80Mrestart: alwayspor…

数据结构_Map和Set

目录 一、搜索模型 二、Map 2.1 Map.Entry 2.2 Map 方法 2.3 Map 注意事项 三、Set 3.1 Set 方法 3.2 Set 注意事项 四、哈希表 4.1 哈希表 4.2 冲突 4.3 哈希函数设计 4.4 闭散列 4.5 开散列/哈希桶 总结 【搜索树】 二叉搜索树又称二叉排序树,它或…

P4-AI产品经理-九五小庞

从0开始做AI产品的完整工作方法 项目启动 项目实施 样本测试模型推荐引擎 构建DMP(数据管理平台) 项目上线

Intel和AMD用户再等等!微软确认Win11 24H2年底前登陆

微软近日确认,Windows 11 24H2版本将于2024年底前正式登陆使用英特尔和AMD处理器的PC。 根据微软介绍,Windows 11 24H2将作为传统功能更新,将在今年晚些时候提供给所有设备。 此前,微软已向搭载骁龙X Plus和X Elite系列处理器的Co…

2024年计算机软考中级【硬件工程师】面试题目汇总(附答案)

硬件工程师面试题汇总分析 1、解释一下同步电路和异步电路 解题思路 同步电路和异步电路是指同步时序电路和异步时序电路。由于存储电路中触发器的动作特点不同,因此可以把时序电路分为同步时序电路和异步时序电路两种。同步时序电路所有的触发器状态的变化都是在同…

pytorch学习(七)torchvision.datasets的使用

网络上已经有公开的数据集,并且这些数据集被整合到了torchvision.datasets中,使用自带的函数可以直接下载。 1.数据集 具体有哪些数据可直接用torchvision.datasets加载呢?可以查看这个网址: datasets官网:Datasets…

Windows11+VS2019+Trimesh2 2.16 安装配置记录

Windows11VS2019Trimesh2 2.16 编译配置记录 trimesh2下载在VS进行配置trimesh2 vs2019编译 若无特别说明均在Release x64平台下配置测试 trimesh2下载 TriMesh2官网 的自述文件里说其新版本不能直接使用MSVC编译,但在Github找到一个大佬的仓库,该大佬直…

多口适配器,给您的生活增添便利

随着科技的快速发展,我们的生活已离不开各种各样的电子设备,智能手机、平板电脑、智能手表、无线耳机……它们共同构建了我们丰富多彩的数字生活。然而,面对众多设备的充电需求,传统的单一充电口已难以满足现代人的使用习惯。在这…

安卓系统签名的制作与使用(SignApk.jar)踩坑记录

看到这里的你应该能区分apk签名跟系统签名吧,如果无法区分的请看下面链接 android 应用的证书签名跟系统签名 看过上面的文章应该知道系统签名需要的文件清单大概有哪些 前两个是编译安卓系统时在build目录下,详细目录为 /build/target/product/security 每组签名用途不同&am…

IP转接服务的重要性及其应用

在现今互联网高度发达的时代,IP转接服务的重要性日益凸显。对于家庭和企业而言,它不仅是连接互联网的桥梁,更是确保网络稳定、高效运行的关键。本文将深入探讨IP转接服务的核心意义及其在互联网世界中的应用。 IP转接服务,简而言之…

大语言模型-文本向量模型评估基准 MTEB

MTEB(Massive Text Embedding Benchmark) 涵盖112种语言的58个数据集,包含如下8种任务。 1、双语文本挖掘(Bitext Mining) 任务目标: 在双语语料库中识别语义等价的句子对。 任务描述: 输入…

Spring Cloud LoadBalanced

负载均衡(Load Balance,简称 LB) 是⾼并发, ⾼可⽤系统必不可少的关键组件. 当服务流量增⼤时, 通常会采⽤增加机器的⽅式进⾏扩容, 负载均衡就是⽤来在多个机器或者其他资源中, 按照⼀定的规则合理分配负载. 负载均衡的⼀些实现 就像是eureka中对请求进行轮询的…

【工具使用】EMACS的verilog_mode脚本

#工作记录# 俗话说不会玩连连看的工程师不是一个好的SoC工程师。 在做集成工作的时候,集成连线估计是一件比较繁琐且容易出错的事情,连线类型定义出错、位宽问题、连线众多等等问题,此时使用由Veripool带来的verilog_mode简直是令人神清气爽…

Linux云计算 |【第一阶段】ENGINEER-DAY2

主要内容: 磁盘空间管理fdisk、parted工具、开机自动挂载、文件系统、交换空间 KVM虚拟化 实操前骤: 1)添加一块硬盘(磁盘),需要关机才能进行操作,点击左下角【添加硬件】 2)选择2…

Qt:26.Qt项目:贪吃蛇游戏

一、项目功能演示: 开始界面可以点击进入游戏。 点击进入游戏之后,切换到选项界面,该界面可以选择游戏难度,回退,以及查询最近一次游戏得分。 游戏具体界面如下。贴图啥的可以自己换,本人审美不咋行&#x…

SPA Fiori开发实战课程(一)

前言 由于工作需要,对Fiori的开发有了一些具体实践,所以做一些记录和总结。 准备工作 1. 对前端有一定的了解,熟悉Node.js,Vue等前端服务器和基础框架。 2. 后端使用ABAP系统。 3. 使用Visual Studio Code进行开发。 工程搭建 打…

TIMO后台管理系统:基于SpringBoot2.0的模块化快速开发平台

摘要 随着软件技术的快速发展,后台管理系统的构建成为企业级应用的重要组成部分。本文旨在介绍TIMO后台管理系统的设计与实现,该系统基于Spring Boot 2.0、Spring Data JPA、Thymeleaf和Shiro等主流技术构建,采用模块化设计以简化开发和维护…

第2章 矩阵

A 乘以此列向量,1的位置依次往下,所以A的列向量全为0 B C、D 取BE 要统一

设计模式——模版方法和策略模式

前言 作为一名资深CV工程师,学会为自己减少工作量乃重中之重。但只是一味地CV,只会因为劣质代码而让自己的工作量加倍,为了将来不被繁重的维护工作而打扰自己的休息日,为了更好的节能,学习设计模式,刻不容缓…