一、ADC基础概念
ADC是一种将连续的模拟信号(如电压)转换为离散的数字信号(一系列二进制数)的器件。STM32内部集成了高精度、高速的ADC模块。
二、核心参数
1.分辨率: 表示ADC能够分辨的最小模拟量变化,用输出数字量的位数表示。
STM32 ADC通常为12位 。这意味着它可以将参考电压(如3.3V)划分为
2
12
2^{12}
212=4096个等级。
数字输出值范围:0 ~ 4095。
2.参考电压(Vref+和Vref-):
这是ADC测量的基准。通常Vref- 连接到GND(0V),Vref+ 连接到VDDA(模拟电源,通常与VDD同为3.3V)。
测量电压 = (ADC转换值 / 4095) * (Vref+)
例如,转换值为2048,Vref+=3.3V,则测量电压 = (2048 / 4095) * 3.3V ≈ 1.65V。
3.采样时间:
ADC内部的采样保持电容需要时间充电到输入信号的电压。这个时间就是采样时间。
对于高阻抗的信号源,需要设置更长的采样时间以确保准确采样。STM32允许你为每个通道独立配置采样时间。
4.转换时间:
总转换时间 = 采样时间 + 12.5个ADC时钟周期(12位分辨率下的固定转换时间)。
转换时间决定了ADC的最高采样速率。例如,ADC时钟为14MHz,采样时间为1.5周期,则总转换时间 = 1.5 + 12.5 = 14周期。采样率 = 14MHz / 14周期 = 1MHz(即1Msps)。
三、STM32 ADC的工作模式
STM32的ADC功能非常灵活,其模式是理解和使用它的关键。
1. 单次转换模式(Single Conversion)
工作流程: 启动一次转换 → 转换一个通道 → 转换完成 → 停止,等待下一次启动。
适用场景: 低速、非连续的采样,如偶尔读取一下电位器的电压。
2. 连续转换模式(Continuous Conversion)
工作流程: 启动后,ADC会不停地自动进行转换。转换完一个通道后立即开始下一次转换。
适用场景: 需要持续获取某个通道的数据,但通常还是会配合定时器触发来达到固定采样率。
3. 扫描模式(Scan Mode)
工作流程: 在多通道配置下使能此模式。ADC会按照通道序列(如CH0, CH1, CH2, CH3)自动依次转换每一个通道。
必须与DMA配合: 因为ADC数据寄存器(DR)只有一个,当多个通道快速连续转换时,CPU来不及读取,必须用DMA将每个通道的转换结果自动搬运到不同的内存数组中。
适用场景:多通道数据采集(如同时采集温度、光照、电压等多个传感器信号)。
4. 定时器触发转换(Injected / Regular Group)
这是实现精确固定采样率的核心!
Regular Group(规则通道组): 主转换组,可以包含最多16个通道,我们最常用的就是规则通道组。
Injected Group(注入通道组): 可以理解为“插队”通道组,最多4个通道。当触发信号来临时,它会中断规则组的转换,优先执行注入组的转换,完成后再回到规则组。
注:ADC通道号(如IN0, IN1, IN2…)与MCU的特定物理引脚是一一对应的关系。 这个映射关系是由芯片设计决定的,无法更改。使用STM32CubeMX时,在Pinout view中,点击芯片模型上的任何一个引脚,会弹出该引脚的所有复用功能。
触发源: ADC转换可以由定时器(TIMx的TRGO信号)来触发,而不是由软件触发。
应用: 设置一个定时器每1ms产生一个触发信号(TRGO)。ADC收到这个触发信号后,开始一次转换(单次模式)或一轮扫描转换(扫描模式)。这样就实现了硬件级别的、绝对精确的1kHz采样率 ,不受软件延迟影响。
5. 看门狗模式(Analog Watchdog)
功能: 设置一个电压范围(高阈值和低阈值)。当ADC转换的结果超出这个范围时,会产生一个中断。
应用: 用于监控关键参数,如电池电压。当电压过低时,自动触发中断报警,无需CPU一直去读取判断。
四、STM32CubeMX ADC配置
1.新建工程
1.双击打开桌面下载好的STM32CubeMX,点击File–>New Project,或直接点击ACCEE TO MCU SELECTOR

2.在左边搜索栏里输入使用的芯片型号,右边选中并开始创建

2.设置RCC时钟源
设置高/低速时钟源都由外部晶振产生。

3.配置ADC
1.根据硬件电路选择对应的ADC通道,例如,硬件电路板上连接的是PA4作为ADC引脚,那么在Pinout view中,点击芯片模型上的PA4引脚,在弹出该引脚的复用功能中选择ADC_IN4。

2.设置ADC参数

备注:
1. ADC_Settings
- Clock Prescaler (时钟预分频器): 用于分频输入到ADC的时钟(ADCCLK)。ADCCLK来源于APB2时钟(PCLK2)。必须确保ADCCLK不超过数据手册中规定的最大允许速度(例如,对于STM32F1系列,通常为14MHz;对于F4系列,通常为36MHz)。时钟越快,转换速度越快,但精度可能会受一定影响。通常选择分频后使得ADCCLK略低于最大允许值。
- Resolution (分辨率): 决定ADC转换结果的位数,即精度。位数越高,精度越高(例如12位对应4096个量化级别),但单次转换时间也稍长。绝大多数应用选择12位。
- Data Alignment (数据对齐): 转换后的12位数据在16位结果寄存器(如ADCx->DR)中的对齐方式。Right Alignment (右对齐): 数据从bit0开始存放,高4位为0。这是最常用的模式,直接读取寄存器值即可得到0-4095的结果。Right Alignment (右对齐): 数据从bit0开始存放,高4位为0。这是最常用的模式,直接读取寄存器值即可得到0-4095的结果。
- Scan Conversion Mode (扫描转换模式): 用于是否自动转换一组规则通道(在Number Of Conversion中设置的序列)。Disabled: 仅转换序列中的第一个通道。Enabled: 按配置好的序列(Rank1, Rank2, …)依次转换所有通道。通常在多通道采集时需要启用此模式。
- Continuous Conversion Mode (连续转换模式): 决定完成一次转换(或一次扫描转换)后,是否自动立即开始新一轮的转换。Disabled: 需要外部触发(如软件触发、定时器触发)或每次手动启动一次转换。Enabled: ADC会永不停止地连续进行转换。在需要实时监控信号时非常有用。
- Discontinuous Conversion Mode (间断模式): 一种特殊的转换模式,允许一次触发只转换序列中的n个通道(n由Number Of Discontinuous Conversions设定)。这是一个高级功能,通常用于节省功耗或复杂触发场景,初学者可以保持Disabled。
- DMA Continuous Requests (DMA连续请求): 在与DMA配合使用时,控制DMA请求的模式。Disabled: 每完成一次转换(或一次扫描转换)产生一次DMA请求。Enabled: 在DMA传输完成后,自动发起新的DMA请求以开始下一次转换。在需要DMA搬运大量连续数据(如音频采样)时,必须启用此项。
- End Of Conversion Selection (转换结束选择): 选择何时产生EOC(转换结束)标志。仅在某些系列(如STM32L4)中存在。EOC flag at the end of single channel conversion: 每个通道转换完成后都产生EOC。EOC flag at the end of all conversions: 整个序列(所有Rank)转换完成后才产生一个EOC。在扫描模式下,如果希望每转换一个通道就中断一次,选前者;如果希望转换完所有通道再中断一次,选后者。
2. ADC_Regular_ConversionMode (规则通道转换模式)
- Number Of Conversions (转换数量): 设置规则通道转换序列的长度,即你要按顺序转换多少个通道。
- External Trigger Conversion Source(外部触发转换源): 这是指由什么硬件事件来发出“开始转换”这个命令。软件触发 (Software Trigger): 由CPU通过代码(如调用HAL_ADC_Start()或设置某个寄存器位)来启动一次ADC转换。定时器触发 (Timer Trigger): 由微控制器内部的某个定时器/计数器自动产生触发信号。这是最常用、最重要的硬件触发方式。外部引脚触发 (EXTI Line Trigger): 由芯片的某个外部GPIO引脚上的电平变化来触发。
- External Trigger Conversion Edge(外部触发转换边沿): 指上述的触发信号在哪种电平时刻变化(边沿)下才被认为是有效的。禁止边沿检测 (None / Disabled): 不使用边沿检测,通常用于软件触发。上升沿触发 (Rising Edge): 当触发信号从低电平(0)跳变到高电平(1)的瞬间,启动一次ADC转换。绝大多数定时器触发和需要高电平有效的场景。下降沿触发 (Falling Edge): 需要低电平有效的信号触发时。当触发信号从高电平(1)跳变到低电平(0)的瞬间 ,启动一次ADC转换。
- 对于每个Rank (采样顺序):Channel (通道): 选择此顺序上要转换的ADC通道(如Channel 0, Channel 1对应GPIO引脚PA0, PA1)。Sampling Time (采样时间): 这是极其重要的参数。指ADC内部电容对输入电压进行采样的时间。时间越长,采样越充分,精度越高,尤其是对于高阻抗源。
3. ADC_Injected_ConversionMode (注入通道转换模式):
Number Of Conversions (转换数量): 定义注入序列的长度。即一次注入触发信号到来时,ADC会自动按顺序转换的通道数量。
4. WatchDog (看门狗):
Enable Analog WatchDog Mode(使能模拟看门狗模式): 是否使能模拟看门狗中断。ADC内置的模拟看门狗,可以监控转换结果。如果结果低于下限或高于上限,会产生中断。可用于检测信号是否超出预期范围,无需CPU一直参与判断。
4.关于ADC开不开中断问题

4.1.不开中断(轮询模式):
工作方式:
1)CPU调用 HAL_ADC_Start() 启动转换。
2)CPU调用 HAL_ADC_PollForConversion(),在这个函数里原地死等,不断检查ADC的标志位,直到转换完成。
3)转换完成后,CPU读取结果 HAL_ADC_GetValue()。
示例代码:
// 1. 启动转换
HAL_ADC_Start(&hadc1);
// 2. CPU在这里等待,直到转换完成或超时(阻塞!)
if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) {
// 3. 读取结果
uint16_t adc_value = HAL_ADC_GetValue(&hadc1);
// ... 处理数据
}
应用场景:
1)单次、非频繁的采样: 比如在初始化时读取一次电位器的初始值,或者只在用户按下按钮后才读一次传感器。
2)对CPU效率要求不高的简单应用。
4.2. 开启ADC转换完成中断(中断模式 )
工作方式:
1)CPU调用 HAL_ADC_Start_IT() 启动转换,然后立即返回去执行其他任务。
2)ADC硬件在后台独立进行转换。
3)转换完成后,ADC硬件会自动产生一个中断。
4)CPU暂停当前工作,跳转到ADC的中断服务函数。
5)中断服务函数调用你的回调函数 HAL_ADC_ConvCpltCallback(),你在这里读取和处理数据。
示例代码:
// 1. 启动中断模式的转换
HAL_ADC_Start_IT(&hadc1);
// ... CPU这里可以执行其他代码,无需等待 ...
// 2. 转换完成后的中断回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == hadc1.Instance) {
uint16_t adc_value = HAL_ADC_GetValue(&hadc1);
// ... 处理数据 ...
// 如果你需要单次模式,这里就不需要再启动。
// 如果你需要连续转换,HAL库会自动开始下一次转换。
}
}
应用场景:
1.需要定期采样但采样率不高: 比如每秒采样10次温度。
2.需要非阻塞式操作: CPU需要在ADC转换期间去执行其他重要任务。
3.对事件的响应有一定实时性要求: 当转换完成时,你能在“第一时间”得到通知并处理。
4.3. 开启DMA中断(DMA Mode)
工作方式:
1)CPU调用 HAL_ADC_Start_DMA(),并提供一个内存数组(Buffer)。
2)ADC和DMA配合工作:每完成一次转换,DMA硬件就自动把数据从ADC数据寄存器搬运到你指定的数组中,完全不需要CPU参与。
3)当DMA传输了指定数量的数据后(例如,填满了半个缓冲区或整个缓冲区),DMA才产生一个中断通知CPU。
4)CPU在DMA中断回调函数中处理一整批数据。
代码示例:
#define ADC_BUF_SIZE 100
uint16_t adc_buffer[ADC_BUF_SIZE];
// 1. 启动DMA传输,ADC会自动循环填充adc_buffer
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUF_SIZE);
// ... CPU完全自由,可以去处理其他复杂任务 ...
// 2. DMA传输完成一半或全部时的回调函数(需要用户实现)
// 例如,当DMA传输完成一半时(前50个数据就绪)
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) {
// 处理 adc_buffer[0] 到 adc_buffer[49]
}
// 当DMA传输全部完成时(后50个数据就绪)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
// 处理 adc_buffer[50] 到 adc_buffer[99]
// 注意:此时DMA可能已经重新开始填充前50个数据了(循环模式)
}
应用场景:
1)高速、连续、多通道采样: 这是DMA的主战场。比如音频采集、振动信号分析、电机三相电流同步采样等。
2)需要极高效率: 将CPU从繁琐的数据搬运工作中彻底解放出来,只在需要时处理一批数据,效率最高。
各类场景下的模式推荐:
| 应用场景 | 推荐模式 | 理由 |
|---|---|---|
| 偶尔读一次值(如调参电位器) | 轮询(不开中断) | 简单直接,代码好写。 |
| 低速定期采样(每秒几次~几百次) | ADC中断 | CPU不用傻等,可以干别的,转换完成能及时处理。 |
| 高速连续采样(>1kHz)或多通道扫描 | DMA + DMA中断 | 避免频繁进入ADC中断,批量处理数据,效率之王。 |
| 超高速采样(极限速率) | DMA(不开中断) | 只让DMA安静地搬运数据,CPU通过查询标志位或定时处理数据,消除一切中断开销。 |
| 模拟看门狗报警 | AWD中断 | 必须开中断,才能在电压异常时立即得到通知并采取紧急措施。 |
| 用定时器硬件触发 | 通常配DMA | 硬件触发保证了精确的采样间隔,DMA负责高效搬运数据,是最佳组合。 |
5.时钟配置
1.时钟源设置
默认时钟源是由内部RC振荡器产生,可通过图中按钮进行修改,外部晶振数值取决于实际电路板上的晶振大小.

提示:
- 这里用到的芯片的最大时钟频率是100MHz,有的芯片最大只有72MHz,实际最大频率可通过查看芯片数据手册确定。
2.时钟频率设置

①PLLM—PLL输入时钟分频系数,根据自己需要的系统时钟频率来进行修改
②PLLN—主PLL倍频系数(自动计算)
③PLLP—主PLL分频系数(自动计算)
④SYSCLK—系统时钟
⑤HCLK—AHB总线时钟,由系统时钟SYSCLK 分频得到,一般不分频,等于系统时钟
⑥APB1/APB2 Prescaler—APB1/APB2总线的预分频系数,可根据需要修改
6.工程文件设置
1.工程设置
注:工程路径中不能有中文,否则会输出错误。
库文件要提前下载好,具体方法在 “【STM32CubeMX学习教程】——1.软件安装” 这一篇文章中有提到。

2.代码生成设置

3.点击右上角按钮生成代码,之后会出现下面的窗口,再点击打开工程即可在用keil查看工程代码。

7.编译成功,0错误0警告

五、HAL库中常用的ADC相关代码
1.ADC1初始化函数
void MX_ADC1_Init(void);
2.启动ADC转换
HAL_ADC_Start(&hadc1); //如果是软件触发,调用此函数即开始转换。
3.中断函数
HAL_ADC_Start_IT(&hadc1); //启动ADC转换,并使能转换完成中断。
4.回调函数
//STM32CubeMX工程源代码是__weak void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc),
//这是一个弱定义的回调函数。当用到中断处理任务时,需要我们重新实现这个函数,在里面处理ADC数据。
// 2. 在main.c或其他文件中实现回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == hadc1.Instance) { // 判断是哪个ADC
uint16_t adc_value = HAL_ADC_GetValue(&hadc1);
// ... 处理 adc_value (例如,更新全局变量)
}
}
5.ADC+DMA传输函数
#define ADC_BUFFER_SIZE 100
uint16_t adc_buffer[ADC_BUFFER_SIZE];
// 1. 启动DMA传输(在main的初始化中)
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE);
以上就是本章的全部内容,如果对你有帮助,欢迎点赞支持,谢谢!
5192

被折叠的 条评论
为什么被折叠?



