资讯详情

ADC+DWA

📅 2026/11/6 3:24:39 | 华诺云谱 👁 阅读
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 给流程 → 按手册改”已经抓住了重点真正拉开差距的地方就在第三步每个配置项都要回到手册确认而不是只凭感觉改
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑