STM32 如何使用DMA和获取ADC
目录
背景
摇杆的原理
程序
端口配置
ADC 配置
DMA配置
背景
DMA是一种计算机技术,允许某些硬件子系统直接访问系统内存,而不需要中央处理器(CPU)的介入,从而减轻CPU的负担。我们可以通过DMA来从外设(ADC、UART等)读取数据之后,搬运到指定的内存。
ADC是根据用户动作或者环境变化会造成传感器等设备的电压值发生变化,再通过STM32的ADC块实现采样、保持、量化、编码将模拟量转换成数据量。
本篇文章会介绍目前无人机常用的摇杆操作(左右、上下、按下等),变为电压值变化,以及STM32 如何实现DMA定期读取遥感ADC值。
摇杆的原理


JS_X:作为X轴方向的模拟信号输入口
JS_Y:作为Y轴方向的模拟信号输入口
JS_D:挥动开关的状态的检测端口
通过原理图可以通过X轴方向滑动可以改变接触点在电阻的位置,从而影响读取到的电压值。Y轴方向亦是同理。因此只需将JS_X和JS_Y的端口设置位模拟量输入端,然后由STM32单片机的ADC块处理。
而按下的操作显然是普通的Port Key, 因此只用将JS_D设置位上拉输入,然后通过读取该端口的状态,来判断是否摇杆被按下。
STM32的DMA通道:STM32系列最多有12个独立可配置的通道,包括DMA1(7个通道)和DMA2(5个通道)。每个通道可以分别设置源地址与目的地址,实现独立工作
程序
端口配置
#define ADC1_DR_Address ((uint32_t)0x4001244C) //ADC1这个外设的地址(查参考手册得出)#define ADCPORT GPIOA //定义ADC接口
#define ADC_CH4 GPIO_Pin_4 //定义ADC接口 电压电位器
#define ADC_CH5 GPIO_Pin_5 //定义ADC接口 光敏电阻
#define ADC_CH6 GPIO_Pin_6 //定义ADC接口 摇杆X轴
#define ADC_CH7 GPIO_Pin_7 //定义ADC接口 摇杆Y轴#define JoyStickPORT GPIOB //定义IO接口组
#define JoyStick_KEY GPIO_Pin_2 //定义IO接口void ADC_GPIO_Init(void){ //GPIO初始化设置GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟(用于ADC的数据传送)RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//使能ADC1时钟GPIO_InitStructure.GPIO_Pin = ADC_CH6 | ADC_CH7; //!!!选择端口 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //选择IO接口工作方式 GPIO_Init(ADCPORT, &GPIO_InitStructure);
}void JoyStick_Init(void){ //摇杆的挥动开关的接口初始化GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO的初始化枚举结构 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); GPIO_InitStructure.GPIO_Pin = JoyStick_KEY; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻 GPIO_Init(JoyStickPORT,&GPIO_InitStructure);
}
1)GPIOA组、GPIOB组、DMA1的外设时钟使能起来(GPIOC组功能用于其他,我们可以忽略它)
2) X轴和Y轴的输入端要设置为模拟量输入端口
3)Port Key 要设置位上拉电阻输入口,因为在没有被按下时,要能读入高电平!
3)初始化端口
ADC 配置
void ADC_Configuration(void){ //初始化设置ADC_InitTypeDef ADC_InitStructure;//定义ADC初始化结构体变量ADC_GPIO_Init();//GPIO初始化设置ADC_DMA_Init();//DMA初始化设置ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在独立模式ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//有软件控制转换ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换数据右对齐ADC_InitStructure.ADC_NbrOfChannel = 2;//!!!顺序进行规则转换的ADC输入口的数目(根据ADC采集通道数量修改)ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间//ADC1,ADC通道x,规则采样顺序值为y,采样时间为28周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 2, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期ADC_DMACmd(ADC1, ENABLE);// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)ADC_Cmd(ADC1, ENABLE);//使能ADC1ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校准重置完成ADC_StartCalibration(ADC1);//开始ADC1校准while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校准完成ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件开始转换
}
常见的ADC数据对齐方式
右对齐(默认情况):这是大多数STM32 ADC寄存器的默认行为。例如,如果你使用12位分辨率的ADC,那么只有最低的12位将被用来存储转换结果,而最高的4位(在16位寄存器中)将会是0。
左对齐:在某些情况下,你可能需要将数据左对齐,这意味着将最高有效位(MSB)放在寄存器的最高位。这通常通过特定的硬件配置或软件操作来实现,比如在某些STM32系列中,可以通过配置ADC寄存器来实现数据的左对齐。
![]()
输入通道数配置,X轴通道和Y轴通道总共两个。
![]()
注册规则组


注册包含通道信息、采样顺序、采样时间。ADC通道通过查阅端口定义可知。
STM32F103C8T6



C8:48pin 64K Flash 属于中容量 SRAM 20K.
查看引脚定义可以知道PA6的ADC通道是6,PA7的通道是7.

使能ADC1的DMA请求
![]()
使能ADC1
![]()
ADC1自校准

软件触发开始ADC1转换
![]()
DMA配置
vu16 ADC_DMA_IN[2]; //ADC数值存放的变量void ADC_DMA_Init(void){ //DMA初始化设置DMA_InitTypeDef DMA_InitStructure;//定义DMA初始化结构体DMA_DeInit(DMA1_Channel1);//复位DMA通道1DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_AddressDMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址DMA_InitStructure.DMA_BufferSize = 2;//!!!定义DMA缓冲区大小(根据ADC采集通道数量修改)DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//当前外设寄存器地址不变DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//!!! 当前存储器地址:Disable不变,Enable递增(用于多通道采集)DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定义外设数据宽度16位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定义存储器数据宽度16位DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位环形缓冲模式DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道优先级高DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存储器到存储器传输DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}


由于是从ADC1外设(ADC1的数据寄存器DR)到内存,因此方向配置如下
![]()
DMA设置为循环模式,可以连续多次的从ADC1外设搬运数据到指定内存。
![]()
因为ADC1外设(DR)是16位(ADC分辨率是12位),所以
![]()
DMA1的每个channel用于设置数据源外设和目的地内存地址。因为我们的外设是ADC1。所以我们选择Channel1.
![]()


ADC1的外设的地址



DMA优先级设置为HIGH


![]()
优先级相同情况下,由内部硬件优先级决定!

由于不是内存搬运到内存,故DMA_M2M要设置为Disable
![]()
相关文章:
STM32 如何使用DMA和获取ADC
目录 背景 摇杆的原理 程序 端口配置 ADC 配置 DMA配置 背景 DMA是一种计算机技术,允许某些硬件子系统直接访问系统内存,而不需要中央处理器(CPU)的介入,从而减轻CPU的负担。我们可以通过DMA来从外设…...
细胞计数专题 | LUNA-FX7™新自动对焦算法提高极低细胞浓度下的细胞计数准确性
现代细胞计数仪采用自动化方法,在特定浓度范围内进行细胞计数。其上限受限于在高浓度条件下准确区分细胞边界的能力,而相机视野等因素则决定了下限。在图像中仅包含少量可识别细胞或特征的情况下,自动对焦可能会失效,从而影响细胞…...
DeepSeek教unity------MessagePack-01
中文:GitCode - 全球开发者的开源社区,开源代码托管平台 MessagePack是C# 的极速 MessagePack 序列化器。它比 MsgPack-Cli 快 10 倍,并且性能超过其他 C# 序列化器。MessagePack for C# 还内置支持 LZ4 压缩——一种极其快速的压缩算法。性能在诸如游戏…...
vite+vue3开发uni-app时低版本浏览器不支持es6语法的问题排坑笔记
重要提示:请首先完整阅读完文章内容后再操作,以免不必要的时间浪费!切记!!!在使用vitevue3开发uni-app项目时,存在低版本浏览器不兼容es6语法的问题,如“?.” “??” 等。为了方便…...
WPF-数据转换器
一、单值转换器 1.不传参数 转换器 当Value值大于100时返回红色 public class DataConverter : IValueConverter{/// <summary>/// 表示从源到目标数据转换/// </summary>/// <param name"value">数据源的值</param>/// <param name&q…...
蓝桥杯备考:贪心算法之纪念品分组
P1094 [NOIP 2007 普及组] 纪念品分组 - 洛谷 这道题我们的贪心策略就是每次找出最大的和最小的,如果他们加起来不超过我们给的值,就分成一组,如果超过了,就把大的单独成一组,小的待定 #include <iostream> #i…...
Win11配置wsl、ubuntu、docker
系统要求 安装WSL。 开通虚拟化: 准备工作 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestartdism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestartwsl --set-default-versi…...
以mysql驱动为案例,从源码角度深入分析Java的SPI机制
本文将以mysql驱动为案例,深入跟踪源码分析Java的SPI(Service Provider Interface)机制。 环境 java 8,mysql8.0,mysql-connector-java 8.0.20 代码 public class MysqlConnectorTest {public static void main(St…...
市盈率(P/E Ratio):理解股票价格与盈利的关系(中英双语)
市盈率(P/E Ratio):理解股票价格与盈利的关系 今天在阅读《漫步华尔街》(原书第13版)的过程中,看到了“股票价格是每股盈利的 6 倍”的类似表述,于是产生了本文。 在投资股票时,投资…...
尚硅谷爬虫note008
一、handler处理器 定制更高级的请求头 # _*_ coding : utf-8 _*_ # Time : 2025/2/17 08:55 # Author : 20250206-里奥 # File : demo01_urllib_handler处理器的基本使用 # Project : PythonPro17-21# 导入 import urllib.request from cgitb import handler# 需求ÿ…...
AWS上基于高德地图API验证Amazon Redshift里国内地址数据正确性的设计方案
该方案通过无服务架构实现高可扩展性,结合分页查询和批量更新确保高效处理海量数据,同时通过密钥托管和错误重试机制保障安全性及可靠性。 一、技术栈 组件技术选型说明计算层AWS Lambda无服务器执行,适合事件驱动、按需处理,成…...
matlab汽车动力学半车垂向振动模型
1、内容简介 matlab141-半车垂向振动模型 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...
【新品解读】AI 应用场景全覆盖!解码超高端 VU+ FPGA 开发平台 AXVU13F
「AXVU13F」Virtex UltraScale XCVU13P Jetson Orin NX 继发布 AMD Virtex UltraScale FPGA PCIE3.0 开发平台 AXVU13P 后,ALINX 进一步研究尖端应用市场,面向 AI 场景进行优化设计,推出 AXVU13F。 AXVU13F 和 AXVU13P 采用相同的 AMD Vir…...
智能硬件定位技术发展趋势
在科技飞速进步的当下,智能硬件定位技术作为众多领域的关键支撑,正沿着多元且极具创新性的路径蓬勃发展,持续重塑我们的生活与工作方式。 一、精度提升的极致追求 当前,智能硬件定位精度虽已满足诸多日常应用,但未来…...
【Elasticsearch】`nested`和`flattened`字段在索引时有显著的区别
有同学问,nested查询效率不高为啥不直接扁平化查询呢?就跟之前的普通结构查询一样,这就有些想当然了,因为扁平化的结构在存储时,其实跟我们想的不一样,接下来给出扁平化在索引时的存储结构(尤其是当嵌套对象…...
【Linux探索学习】第二十七弹——信号(上):Linux 信号基础详解
Linux学习笔记: https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言: 前面我们已经将进程通信部分讲完了,现在我们来讲一个进程部分也非常重要的知识点——信号,信号也是进程间通信的一…...
redis解决高并发看门狗策略
当一个业务执行时间超过自己设定的锁释放时间,那么会导致有其他线程进入,从而抢到同一个票,所有需要使用看门狗策略,其实就是开一个守护线程,让守护线程去监控key,如果到时间了还未结束,就会将这个key重新s…...
Ollama+DeepSeek+Open-WebUi
环境准备 Docker Ollama Open-WebUi Ollama 下载地址:Ollama docker安装ollama docker run -d \ -v /data/ollama/data:/root/.ollama \ -p 11434:11434 \ --name ollama ollama/ollama 下载模型 Ollama模型仓库 # 示例:安装deepseek-r1:7b doc…...
MySQL-事务隔离级别
事务有四大特性(ACID):原子性,一致性,隔离性和持久性。隔离性一般在事务并发的时候需要保证事务的隔离性,事务并发会出现很多问题,包括脏写,脏读,不可重复读,…...
对于简单的HTML、CSS、JavaScript前端,我们可以通过几种方式连接后端
1. 使用Fetch API发送HTTP请求(最简单的方式): //home.html // 示例:提交表单数据到后端 const submitForm async (formData) > {try {const response await fetch(http://your-backend-url/api/submit, {method: POST,head…...
从入门到精通:Postman 实用指南
Postman 是一款超棒的 API 开发工具,能用来测试、调试和管理 API,大大提升开发效率。下面就给大家详细讲讲它的安装、使用方法,再分享些实用技巧。 一、安装 Postman 你能在 Postman 官网(https://www.postman.com )下…...
error: conflicting types for ‘SSL_SESSION_get_master_key’
$ make make all-am make[1]: Entering directory ‘/home/linuxuser/tor’ CC src/lib/tls/libtor_tls_a-tortls_openssl.o In file included from src/lib/tls/tortls_openssl.c:61: ./src/lib/tls/tortls_internal.h:55:8: error: conflicting types for ‘SSL_SESSION_get_…...
sql sqlserver的特殊函数COALESCE和PIVOT的用法分析
一、COALESCE是一个返回参数中第一个非NULL值的函数, 列如:COALESCE(a,b,c,d,e);可以按照顺序取abcde,中的第一个非空数据,abcde可以是表达式 用case when 加ISNULL也可以实现,但是写法复杂了…...
智能猫眼实现流程图
物理端开发流程图 客户端端开发流程图 用户功能开发流程图 管理员开发流程图...
c/c++蓝桥杯经典编程题100道(19)汉诺塔问题
汉诺塔问题 ->返回c/c蓝桥杯经典编程题100道-目录 目录 汉诺塔问题 一、题型解释 二、例题问题描述 三、C语言实现 解法1:递归法(难度★) 解法2:迭代法(难度★★★) 四、C实现 解法1࿱…...
蓝桥杯单片机大模板(西风)
#include <REGX52.H> #include "Key.h" #include "Seg.h" //变量声明区 unsigned char Key_Val,Key_Down,Key_Old;//按键扫描专用变量 unsigned char Key_Slow_Down;//按键减速专用变量 10ms unsigned int Seg_Slow_Down;//按键扫描专用变量 500ms …...
【Java基础】静态多态和动态多态
多态(Polymorphism) 多态是面向对象编程(OOP)中的核心概念之一,它指的是 同一接口,多个实现方式。在 Java 中,多态主要有 两种形式: 静态多态(Static Polymorphism&…...
Android 10.0 移除wifi功能及相关菜单
介绍 客户的机器没有wifi功能,所以需要删除wifi相关的菜单,主要有设置-网络和互联网-WLAN,长按桌面设置弹出的WALN快捷方式,长按桌面-微件-设置-WLAN。 修改 Android10 上直接将config_show_wifi_settings改为false,这样wifi菜单的入口就隐…...
【LeetCode Hot100 子串】和为 k 的子数组、滑动窗口最大值、最小覆盖子串
子串 1. 和为 k 的子数组题目描述解题思路主要思路步骤 时间复杂度与空间复杂度代码实现 2. 滑动窗口最大值题目描述解题思路双端队列的原理:优化步骤: Java实现 3. 最小覆盖子串题目描述解题思路滑动窗口的基本思路:具体步骤:算法…...
【kafka系列】Kafka如何实现高吞吐量?
目录 1. 生产者端优化 核心机制: 关键参数: 2. Broker端优化 核心机制: 关键源码逻辑: 3. 消费者端优化 核心机制: 关键参数: 全链路优化流程 吞吐量瓶颈与调优 总结 Kafka的高吞吐能力源于其生…...


