ADC+DWA

发布时间:2026/11/6 3:24:07

ADC+DWA 1.5V2.GND3. PA04.PD11使用DWA之前先看STM32的参考手册-DMA映像147防止冲突你的现状完全够用互不干扰STM32F103 系列有两个 DMA 控制器资源其实是挺丰富的DMA1有 7 个通道。DMA2有 5 个通道。总计你有 12 条“高速公路”可以用。你的项目目前只用了 2 条光敏 (ADC3)占用了DMA2的通道5。MQ2 (ADC1)将占用DMA1的通道1。结论你手里还有 10 条空闲通道哪怕你再加几个传感器比如再加个 ADC2或者加几个串口 USART通道都是绰绰有余的。什么时候会觉得“通道不够”虽然总共有 12 个通道但在实际工程中确实会出现“抢车道”的情况。主要有两个原因1. “硬性绑定”导致的局部拥堵这是最常见的问题。STM32 的外设和 DMA 通道是固定死的不能随便换。例子假设你想用 3 个 ADC 同时采集ADC1, ADC2, ADC3。ADC1只能走DMA1 Channel 1。ADC2只能走DMA1 Channel 1注意它和 ADC1 抢同一个通道。ADC3只能走DMA2 Channel 5。冲突点如果你同时开启 ADC1 和 ADC2 的 DMA它们就会在DMA1 Channel 1撞车。这时候你就得二选一或者放弃 DMA 改用中断。2. 高级外设占用了“黄金通道”有些通道是“兵家必争之地”。SPI / I2S如果你要用高速 SPI 传输屏幕数据或者用 I2S 播放音乐它们通常会占用 DMA1 或 DMA2 的特定通道。定时器 (TIM)如果你要做复杂的电机控制PWM 输入捕获定时器也会占用 DMA 通道。如果你的项目既要搞音频I2S又要搞多路 ADC还要搞串口通信那 12 个通道可能很快就分完了。如果通道真的不够了怎么办假如你是一个超级复杂的项目12 个通道全被占满了还想加功能有以下 3 种解决方案按推荐程度排序方案 A使用“轮询”或“中断”代替 DMA最常用不是所有数据都需要 DMA 搬运。低速数据比如温度传感器DS18B20、按键扫描速度很慢直接用 CPU 读取轮询或者用中断读取即可没必要浪费 DMA 通道。中速数据比如普通的串口打印调试信息用TXE发送寄存器空中断来发虽然比 DMA 稍微占一点 CPU但完全来得及。方案 B分时复用Time Division Multiplexing如果两个设备不会同时工作可以共用一个通道。例子你有一个 ADC 用来测电池电压几分钟测一次另一个 ADC 用来测电机电流一直在测。做法平时把 DMA 配置给电机电流用。当需要测电池时暂停电机 DMA把 DMA 重新配置给电池 ADC测完再切回去。这需要写代码动态切换 DMA 的配置。方案 C软件模拟 DMA下策如果硬件 DMA 真的没了你可以写一个定时器中断比如 1ms 进一次在中断里手动把数据从外设寄存器读到内存数组里。这其实就是用 CPU 模拟了 DMA 的功能虽然效率低一点但能解决问题。然后此处使用ADC 的作用就是把连续的模拟电压量化成一个离散的数字配置ADC的流程1. 开 ADC 时钟2. 配置引脚为模拟输入3. 设置 ADC 分频复制一份主频降速到合适的频率再送给这个机器4. 设置采样时间采集一次花的时间5. 设置转换通道和顺序通道 选哪个引脚顺序 先采谁后采谁6. 设置工作模式7. 上电 ADC 并校准每次上电后校准一次测出 ADC 内部的固定零点偏差并补偿之后就可以正常采样了。校准解决的是天生不准不是随机抖动。8. 启动转换并读取结果第一步光敏电阻是什么光敏电阻就是一个会随光照变化的电阻。光照强 → 电阻变小光照弱 → 电阻变大它本身不会说话不会告诉你现在是多亮。第二步怎么让单片机看到它光敏电阻单独接在单片机上单片机是读不出来的。所以我们要把它和一个固定电阻串在一起接在 3.3V 和 GND 之间中间那个点接到单片机的 PA5 引脚。这样光照变化时PA5 上的电压就会跟着变化。第三步单片机怎么读这个电压单片机有一个东西叫ADC它的作用就是把电压变成一个数字。你的单片机 ADC 是 12 位的它会把 0~3.3V 的电压分成 4096 个等级0V → 数字 03.3V → 数字 4095中间电压 → 中间的数字所以 ADC 读出来的就是一个0 到 4095 之间的整数。第四步这个数字代表什么这个数字代表的是PA5 引脚上的电压高低不是光照强度本身。但因为你用了光敏电阻电压会随光照变化所以这个数字间接反映了光照强弱。你的电路接法是光越亮ADC 数字越小光越暗ADC 数字越大。第五步怎么变成百分比百分比的意思就是当前亮度占最大亮度的多少。但是问题来了——你不知道最亮是多少也不知道最暗是多少。所以你要先测用手完全遮住光敏电阻看 ADC 读出来是多少比如 3800。这就是最暗。用手电筒照光敏电阻看 ADC 读出来是多少比如 200。这就是最亮。有了这两个数就可以算了ADC 200 → 100%最亮ADC 3800 → 0%最暗ADC 2000 → 中间某个百分比公式就是用最暗的值减去当前值再除以最暗减最亮的范围乘以 100就得到百分比。总结一句话光敏电阻随光照变阻值 → 变成电压 → ADC 变成 0~4095 的数字 → 你先测出最亮和最暗时各是多少 → 用公式算出 0~100% → 串口打印出来。只要你能测出遮光和光照下的两个 ADC 值就能映射不好意思2026年11月4号的时候发现光敏的电压不够现在是调整过的3.3v通过网盘分享的文件ADC链接: https://pan.baidu.com/s/1TVpcMFcf8o6lLUOWRFFCAg?pwd8888 提取码: 8888--来自百度网盘超级会员v5的分享其中发送数字转字符串的逻辑如下/** 串口发送数字先转换再发送逻辑清晰版 ** 转换原理* 1. 整数在内存里是二进制没有5、2、8这样的字符* 必须用除法算出每一位。** 2. 每次用 val % 10 取出最右边一位个位* 再用 0 余数 把数字 0~9 变成字符 0~9。* 例如数字 8 → 0 8 → 字符 8ASCII 码 56** 3. 因为每次都取最右边的个位所以先取到的是低位* 存进 buf 的顺序是反的。* 例如 val528* 第1轮528 % 10 8 → buf[0]8* 第2轮 52 % 10 2 → buf[1]2* 第3轮 5 % 10 5 → buf[2]5* 此时 buf [8][2][5]是反的。** 4. 翻转 buf首尾交换把反序变成正序。* [8][2][5] → 交换 buf[0] 和 buf[2] → [5][2][8]** 5. 正序发送从头到尾逐个发出去串口收到的就是 528。** 示例val 528* 阶段1 转换后buf [8][2][5] i 3* 阶段2 翻转后buf [5][2][8]* 阶段3 发送 依次发出 5 2 8 → 串口显示 528*/然后是DMA的配置和讲解开时钟 → 复位通道 → 配规则 → 使能DMA → 使能外设DMA请求 → 启动外设开启 DMA 时钟DMA 也是一个外设使用前必须先开时钟。DMA1 属于 AHB 总线所以调用RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE)使能时钟。不开时钟的话后面所有 DMA 寄存器都写不进去。复位 DMA 通道调用DMA_DeInit(DMAy_Channelx)把该通道的寄存器恢复到默认状态。这样即使之前有人用过这个通道也不会留下错误配置。设置外设基地址告诉 DMA数据从哪里来或者数据送到哪里去。如果是从外设读取数据就写外设的数据寄存器地址例如(uint32_t)USART1-DR或(uint32_t)ADC1-DR。如果是向串口发送数据源地址是内存外设地址仍然是USART1-DR因为 DMA 每次都要把数据写进同一个寄存器。设置内存基地址告诉 DMA数据要搬到内存里的哪个数组。例如你定义了uint8_t rx_buf[100];就写(uint32_t)rx_buf。DMA 每收到一个数据就会依次放进rx_buf[0]、rx_buf[1]、rx_buf[2]……所以内存基地址就是缓冲区的起始地址。设置传输方向传输方向决定数据是从外设到内存还是从内存到外设。DMA_DIR_PeripheralSRC外设是数据源表示从外设搬到内存常用于接收。DMA_DIR_PeripheralDST外设是目的地表示从内存搬到外设常用于发送。设置缓冲区大小告诉 DMA 一共要搬运多少个数据。例如数组有 100 个元素就写100。单位由后面的数据宽度决定如果数据宽度是字节就是 100 个字节如果是半字就是 100 个半字。设置外设地址是否递增这个要看外设地址是不是同一个寄存器。像USART1-DR、ADC1-DR都是固定地址每次都要访问同一个寄存器所以选择DMA_PeripheralInc_Disable表示外设地址不递增。如果外设是一连串寄存器才需要开启递增。大多数情况下外设地址都不递增。设置内存地址是否递增如果数据要依次放进数组的每一个元素就选择DMA_MemoryInc_Enable。例如接收数据时第一个数据放buf[0]第二个放buf[1]第三个放buf[2]所以内存地址要递增。如果始终只操作同一个内存地址就选择DMA_MemoryInc_Disable。设置外设数据宽度告诉 DMA 每次从外设搬运多少数据。Byte每次搬 1 个字节常用于 USART。HalfWord每次搬 2 个字节常用于 ADC。Word每次搬 4 个字节常用于 32 位数据。外设数据宽度要和外设寄存器宽度匹配否则数据会错位。设置内存数据宽度内存数据宽度通常和外设数据宽度保持一致。如果外设每次给 1 个字节内存也按 1 个字节存放如果外设每次给 2 个字节内存也按 2 个字节存放。两边宽度不一致时数据排列会出错。设置工作模式DMA_Mode_Normal正常模式。搬运完指定数量的数据后DMA 自动停止。DMA_Mode_Circular循环模式。搬运完成后自动回到起始位置继续搬运适合连续采集数据例如 ADC 连续采样。设置通道优先级当多个 DMA 通道同时请求时优先级高的先服务。可选 Low、Medium、High、VeryHigh。一般普通外设用 Medium 或 High 即可。设置内存到内存模式如果不使用内存到内存拷贝就选择DMA_M2M_Disable。如果开启DMA_M2M_EnableDMA 可以不依赖外设直接把一块内存的数据拷贝到另一块内存类似于memcpy但由硬件完成。初始化 DMA 通道调用DMA_Init(DMAy_Channelx, DMA_InitStructure)把前面设置的所有参数写入 DMA 通道寄存器。使能 DMA 通道调用DMA_Cmd(DMAy_Channelx, ENABLE)DMA 通道正式开始工作。只初始化不使能DMA 不会搬运数据。使能外设的 DMA 请求仅使能 DMA 通道还不够还必须让外设主动发出 DMA 请求。例如串口发送需要调用USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE)ADC 需要调用ADC_DMACmd(ADC1, ENABLE)。如果不开启外设的 DMA 请求DMA 通道虽然已经使能但不会开始传输。通过网盘分享的文件ADC DMA链接: https://pan.baidu.com/s/1d0BlhWj-LRbAZQ6BX-FIcg?pwd8888 提取码: 8888--来自百度网盘超级会员v5的分享嵌入式外设开发实战流程粗看手册先抓框架看芯片参考手册里这个模块是干什么的。重点看时钟、通道/引脚、寄存器、工作模式、触发条件。不要一开始死磕每个位先知道“有哪些寄存器、哪些参数要配”。用 AI 或例程梳理流程让 AI 给出配置步骤比如 DMA 的开时钟复位通道配源地址、目标地址、方向、长度、递增、模式使能 DMA使能外设 DMA 请求启动外设这一步不是照抄而是先建立正确骨架。对照手册逐项核对每个参数都要回到手册确认通道是否对应正确地址是不是外设数据寄存器方向是外设到内存还是内存到外设数据宽度是否匹配是否需要循环模式是否需要中断这一步最容易发现错误比如 DMA 方向配反、通道选错、没开外设 DMA 请求。写最小可运行代码先只实现核心功能不要一次加太多东西。比如 DMA ADC 先只验证数组能被自动填充remain会变化数据会循环刷新。烧录验证看现象用串口、LED、示波器、调试器观察。如果现象不对不要乱改先判断是哪一步没生效没开时钟 → 模块不工作通道没使能 → 不搬运外设没开 DMA 请求 → 不触发方向/地址错 → 数据不对。根据手册微调发现异常后再回到手册查具体位域。比如 DMA 模式、优先级、中断标志、传输完成判断等。封装成稳定函数验证通过后把初始化、启动、状态查询封装成函数。后面换外设时只要改源地址、方向、通道即可。套到 DMA 上就是看手册 → 列步骤 → 写初始化 → 开时钟 → 配通道 → 使能 DMA → 使能外设 DMA 请求 → 启动外设 → 串口/调试验证 → 对照手册修正。你总结的“看手册 → AI 给流程 → 按手册改”已经抓住了重点真正拉开差距的地方就在第三步每个配置项都要回到手册确认而不是只凭感觉改
延伸阅读

更多相关文章

2026/11/6 3:19:07

iloader iOS 侧载教程:免命令行,3 步在 iPhone 装好 SideStore

iloader iOS 侧载教程:免命令行,3 步在 iPhone 装好 SideStore 【免费下载链接】iloader User friendly sideloader 项目地址: https://gitcode.com/GitHub_Trending/iloa/iloader 免费 Apple ID 的证书配额用满、配对文件不知道丢进哪个目录、手…

2026/11/6 4:34:17

单链表实现与核心操作详解

1. 单链表基础概念解析单链表作为数据结构中最基础也最重要的线性结构之一,是每个程序员必须掌握的硬核技能。记得我初学链表时,曾被指针的指针绕得头晕目眩,直到亲手实现了一遍所有操作才真正理解其精髓。本文将用8000字的篇幅,带…

2026/11/6 4:34:17

测试工程师如何识别和预防需求缺陷

1. 需求战场的隐形地雷:测试工程师的排雷手册在软件开发的战场上,需求缺陷就像埋藏在前进道路上的地雷,随时可能引爆项目危机。作为测试工程师,我们不仅是最后的防线,更应该成为冲锋在前的排雷兵。根据行业数据&#x…

2026/11/6 4:34:17

云原生时代运维转型:从手动配置到DevBox自动化

1. 运维角色的历史演变与技术冲击2000年初期的运维工作场景与今天截然不同。那时的运维工程师需要亲自上架服务器、配置交换机、安装操作系统,工作内容高度依赖物理设备操作。我曾见过一位老运维的笔记本,上面密密麻麻记录着不同机房的设备位置、IP分配和…

2026/11/6 4:34:17

JS金额补零到两位小数:toFixed浮点坑与整数分方案

做前端这些年,被问得最多的一类“小问题”就是金额为什么少了个 0。JS 金额补充到 2 位小数、末尾自动补零、整数后面补上“.00”——单看每一条都简单得不像个问题,真写进项目里,边界情况能把人折腾一下午。前阵子帮朋友看一个记账小工具&am…

2026/11/6 4:34:16

动态规划解决子序列问题:从LIS到最大子序和

1. 动态规划在子序列问题中的应用概述动态规划(Dynamic Programming)是解决子序列问题的利器,尤其擅长处理那些需要全局最优解的序列匹配问题。在算法领域,子序列问题通常分为两大类:连续子序列和非连续子序列。连续子…

2026/11/6 4:29:16

数据库连接操作详解:从内连接到外连接

1. 数据库连接操作的本质理解在数据库查询中,连接(Join)操作就像是在两个数据表之间搭建桥梁。想象你手上有两份Excel表格:一份记录着公司所有员工的基本信息,另一份存储着各部门的绩效数据。当我们需要分析"每个…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/6 1:03:39

MCU晶振电路设计全解析:从负载电容计算到PCB布局调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑