3. STM32CubeMX配置(上):时钟树配置、ADC多通道与DMA循环模式
好,咱们正式开始动手配置。这一节我会带着你一步步在CubeMX里把时钟树、ADC多通道和DMA循环模式搭起来。说实话,很多初学者一上来就急着写代码,结果硬件配置没搞对,后面调试到怀疑人生。我当年也吃过这个亏,所以现在养成了一个习惯——先花半小时把CubeMX配清楚,后面能省下半天时间。
3.1 时钟树配置——ADC与PWM的“心脏”
时钟树是整个系统的脉搏。ADC需要时钟来驱动采样,PWM需要时钟来生成精确的波形。如果时钟配乱了,两个外设都会出问题。
我个人习惯先把时钟树理清楚,再配其他外设。咱们以STM32F103为例,目标是把系统时钟跑到72MHz,同时给ADC一个合适的时钟分频。
核心原则:ADC时钟不能超过14MHz(STM32F1系列),否则采样精度会下降。PWM的定时器时钟则希望尽可能高,以获得更高的分辨率。
具体步骤是这样的:
- 选择时钟源:在CubeMX的Clock Configuration页面,选择HSE(外部高速晶振)作为时钟源。一般开发板用的是8MHz晶振。
- 配置PLL:HSE 8MHz → PLL倍频9倍 → 得到72MHz系统时钟。这个公式很简单:8MHz × 9 = 72MHz。
- APB2分频器:ADC挂在APB2总线上。APB2默认不分频,所以APB2时钟也是72MHz。但ADC需要≤14MHz,所以必须给ADC单独分频。
- ADC预分频器:在ADC配置里,把预分频器设为6分频或8分频。72MHz ÷ 6 = 12MHz,刚好满足要求。
小技巧:我建议把ADC时钟设在12MHz左右。8MHz也行,但采样率会低一些。14MHz是上限,别卡着上限跑,留点余量。
为什么要这么配?你想想看,PWM的定时器(比如TIM2)挂在APB1上,APB1最大36MHz。如果APB1不分频,定时器时钟就是36MHz,对于一般的PWM频率(比如10kHz~100kHz)完全够用。而ADC需要低时钟,所以必须分频。这两个需求其实不冲突,只要在时钟树上分好路就行。
我曾经遇到过一个项目,ADC采样值总是跳变,查了半天发现是ADC时钟配到了18MHz,超了规格。把分频系数改大后,数据立马稳定了。嗯,这种坑踩过一次就记住了。
3.2 ADC1多通道配置——IN0到IN3的“四路采集”
时钟配好了,接下来配置ADC本身。咱们要用ADC1的四个通道:IN0、IN1、IN2、IN3。这四个通道对应PA0、PA1、PA2、PA3引脚。
在CubeMX的Pinout & Configuration页面,找到ADC1,按以下步骤操作:
- Mode选择:设置为Independent mode(独立模式)。
- 通道使能:在Channel列表里,依次勾选IN0、IN1、IN2、IN3。每个通道可以单独设置采样时间。
- 采样时间:我一般设成55.5个周期(对应ADC时钟12MHz时,约4.6μs)。如果信号变化慢,可以设更长;如果信号快,可以缩短到13.5个周期。
- 扫描模式:使能Scan Conversion Mode(扫描模式)。这样ADC会自动轮询所有使能的通道。
- 连续转换:使能Continuous Conversion Mode(连续转换模式)。配合DMA,可以实现后台不间断采集。
注意:扫描模式和连续模式必须同时使能,才能实现多通道的自动循环采集。如果只开扫描不开连续,ADC只扫一轮就停了。
这里有个细节——通道顺序。CubeMX默认按IN0→IN1→IN2→IN3的顺序扫描。如果你想调整顺序,可以在Rank设置里手动拖拽。我个人习惯保持默认,除非有特殊的相位要求。
采样时间的选择其实是个权衡。采样时间越长,采样电容充电越充分,精度越高,但采样率会降低。对于一般的传感器信号(比如电位器、光敏电阻),55.5个周期绰绰有余。如果是高速信号,可以降到13.5个周期,但要注意精度可能会掉1~2个LSB。
3.3 DMA循环模式配置——解放CPU的“搬运工”
ADC配好了,但数据怎么取出来?如果每次都用CPU去读,那CPU就啥也别干了。这时候DMA就该上场了。
DMA(直接存储器访问)可以在不占用CPU的情况下,把ADC转换结果自动搬运到内存里。咱们要配置的是DMA的循环模式,这样DMA会不停地搬运,形成一个环形缓冲区。
配置步骤如下:
- 添加DMA请求:在ADC1的DMA Settings选项卡里,点击Add,选择ADC1作为DMA请求源。
- DMA通道选择:STM32F103的ADC1通常使用DMA1的通道1。CubeMX会自动分配,不用手动改。
- 数据传输方向:Peripheral To Memory(外设到内存)。ADC是源,内存是目的地。
- 数据宽度:外设和内存都设成Half Word(半字,16位)。因为ADC的转换结果是12位,用16位存刚好。
- 模式:选择Circular(循环模式)。这是关键!不选循环的话,DMA搬完一轮就停了。
- 增量模式:内存地址使能Increment(增量模式)。这样每次搬运后,内存地址自动加2(因为半字是2字节),数据会依次存到数组的不同位置。
核心配置总结:
| 参数 | 设置值 | 说明 |
|---|---|---|
| DMA模式 | Circular | 循环搬运,永不停止 |
| 数据宽度 | Half Word | 匹配ADC的12位输出 |
| 内存增量 | Enabled | 地址自动递增,形成数组 |
| 优先级 | Medium | 够用就行,不用太高 |
你可能会问:DMA缓冲区设多大?这取决于你的应用。如果四个通道各采一次,那就是4个半字,缓冲区至少4个元素。但为了稳定,我一般设成16或32个元素,这样即使CPU偶尔没来得及处理,数据也不会被覆盖。
我曾经在做一个电机电流采集项目时,DMA缓冲区只设了4个元素,结果CPU处理速度跟不上,数据被覆盖了,波形看起来像鬼画符。后来改成32个元素,问题就解决了。说白了,缓冲区大小就是给CPU留的“缓冲时间”。
3.4 整体配置逻辑图
下面这张图把整个配置流程串起来了。你可以看到时钟从HSE出发,一路分给ADC,一路分给定时器;ADC采集的数据通过DMA自动搬到内存;CPU只需要定期从内存里读数据就行。
从图上可以看得很清楚:时钟从HSE出发,经过PLL倍频到72MHz,然后分两路——一路到APB2给ADC,一路到APB1给定时器。ADC再经过6分频降到12MHz,然后通过DMA把数据搬到内存。整个过程CPU只需要在初始化时参与,后面就只管从内存里读数据了。
个人经验:配置完成后,记得在CubeMX里点一下“Generate Code”生成代码。然后打开生成的main.c,检查一下HAL_ADC_Start_DMA()函数是否被调用。如果没调用,手动加上。我遇到过CubeMX偶尔漏掉这个调用的情况。