3. STM32CubeMX配置(上):时钟树配置、ADC多通道与DMA循环模式

好,咱们正式开始动手配置。这一节我会带着你一步步在CubeMX里把时钟树、ADC多通道和DMA循环模式搭起来。说实话,很多初学者一上来就急着写代码,结果硬件配置没搞对,后面调试到怀疑人生。我当年也吃过这个亏,所以现在养成了一个习惯——先花半小时把CubeMX配清楚,后面能省下半天时间。

3.1 时钟树配置——ADC与PWM的“心脏”

时钟树是整个系统的脉搏。ADC需要时钟来驱动采样,PWM需要时钟来生成精确的波形。如果时钟配乱了,两个外设都会出问题。

我个人习惯先把时钟树理清楚,再配其他外设。咱们以STM32F103为例,目标是把系统时钟跑到72MHz,同时给ADC一个合适的时钟分频。

核心原则:ADC时钟不能超过14MHz(STM32F1系列),否则采样精度会下降。PWM的定时器时钟则希望尽可能高,以获得更高的分辨率。

具体步骤是这样的:

  1. 选择时钟源:在CubeMX的Clock Configuration页面,选择HSE(外部高速晶振)作为时钟源。一般开发板用的是8MHz晶振。
  2. 配置PLL:HSE 8MHz → PLL倍频9倍 → 得到72MHz系统时钟。这个公式很简单:8MHz × 9 = 72MHz。
  3. APB2分频器:ADC挂在APB2总线上。APB2默认不分频,所以APB2时钟也是72MHz。但ADC需要≤14MHz,所以必须给ADC单独分频。
  4. 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,按以下步骤操作:

  1. Mode选择:设置为Independent mode(独立模式)。
  2. 通道使能:在Channel列表里,依次勾选IN0、IN1、IN2、IN3。每个通道可以单独设置采样时间。
  3. 采样时间:我一般设成55.5个周期(对应ADC时钟12MHz时,约4.6μs)。如果信号变化慢,可以设更长;如果信号快,可以缩短到13.5个周期。
  4. 扫描模式:使能Scan Conversion Mode(扫描模式)。这样ADC会自动轮询所有使能的通道。
  5. 连续转换:使能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会不停地搬运,形成一个环形缓冲区。

配置步骤如下:

  1. 添加DMA请求:在ADC1的DMA Settings选项卡里,点击Add,选择ADC1作为DMA请求源。
  2. DMA通道选择:STM32F103的ADC1通常使用DMA1的通道1。CubeMX会自动分配,不用手动改。
  3. 数据传输方向:Peripheral To Memory(外设到内存)。ADC是源,内存是目的地。
  4. 数据宽度:外设和内存都设成Half Word(半字,16位)。因为ADC的转换结果是12位,用16位存刚好。
  5. 模式:选择Circular(循环模式)。这是关键!不选循环的话,DMA搬完一轮就停了。
  6. 增量模式:内存地址使能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只需要定期从内存里读数据就行。

ADC多通道DMA采集配置逻辑图 ADC多通道DMA采集配置逻辑图 HSE 8MHz PLL ×9 → 72MHz SYSCLK 72MHz APB2 72MHz APB1 36MHz ADC1 12MHz (÷6) TIM2 (PWM) 36MHz DMA1 循环模式 内存缓冲区 关键点 ADC时钟≤14MHz DMA循环模式 缓冲区≥16元素

从图上可以看得很清楚:时钟从HSE出发,经过PLL倍频到72MHz,然后分两路——一路到APB2给ADC,一路到APB1给定时器。ADC再经过6分频降到12MHz,然后通过DMA把数据搬到内存。整个过程CPU只需要在初始化时参与,后面就只管从内存里读数据了。

个人经验:配置完成后,记得在CubeMX里点一下“Generate Code”生成代码。然后打开生成的main.c,检查一下HAL_ADC_Start_DMA()函数是否被调用。如果没调用,手动加上。我遇到过CubeMX偶尔漏掉这个调用的情况。