STM32标准库-ADC数模转换器
文章目录
- 一、ADC
- 1.1简介
- 1. 2逐次逼近型ADC
- 1.3ADC框图
- 1.4ADC基本结构
- 1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)
- 1.4.2 信号 “调度站”:多路开关
- 1.4.3 信号 “加工厂”:ADC 转换器(规则组 + 注入组)
- 1.4.4信号 “暂存柜”:AD 数据寄存器
- 1.4.5 信号 “监督员”:模拟看门狗
- 1.4.6信号 “报警器”:中断输出控制 + NVIC
- 1.4.7 系统 “动力源”:触发控制 + RCC
- 1.4.8系统 “总开关”:开关控制
- 1.5输入通道
- 1.6转换模式
- 1.7触发控制
- 1.8数据对齐
- 1.9转换时间
- 1.10校准
- 1.11硬件电路
- 二、AD单通道
- 2.1接线图
- 2.2代码
- 2.3ADC 相关 API 函数
- 1. 时钟配置函数
- 2. GPIO 配置函数
- 3. ADC 配置函数
- 4. ADC 控制函数
- 5. 状态和数据读取函数
- 2.4工作现象
- 三、AD多通道
- 3.1接线图
- 3.2代码
- 3.3AD单通道未提及的关键 API
- 1. ADC 通道配置函数
- 2. 采样时间说明
- 3. ADC 状态标志位
- 3.4工作现象
- 1. **光敏传感器(AO 接 ADC 通道,如 PA0)**
- 2. **声音传感器(AO 接 ADC 通道,如 PA1)**
- 3. **红外避障传感器(AO 接 ADC 通道,如 PA2)**
- 4. **电位器(中间引脚接 ADC 通道,如 PA3)**
一、ADC
1.1简介
1. 2逐次逼近型ADC
1.3ADC框图
1.4ADC基本结构
以下用更通俗的 “信号 journey(旅程)” 逻辑,拆解各模块咋连接、咋配合干活:
1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)
- GPIO(16 路):像 16 个 “小天线”,能接外部五花八门的模拟信号(比如传感器电压、旋钮调节的电平 ),是最常用的信号入口。
- 温度传感器:专门测温度,把温度变化转成电压信号,相当于 “温度→电信号” 翻译器。
- V_REFINT:内部精准参考电压,像 “标准尺”,给 ADC 转换当基准(比如转换时拿信号和它比,确定数字值 )。
这些信号要先 “排队上车”,统一交给 多路开关(下面讲它的作用 )。
1.4.2 信号 “调度站”:多路开关
- 作用:当 “交通调度员”,决定让哪路信号进 ADC 转换器。
- 逻辑:比如同时有 GPIO、温度传感器的信号,它选 1 路(规则组模式)或几路(注入组模式 ),送往后边的 ADC 转换器。
1.4.3 信号 “加工厂”:ADC 转换器(规则组 + 注入组)
- 规则组(最多 16 路):常规 “生产线”,按预设顺序、节奏转换信号(比如循环采集 16 路 GPIO ),适合普通、批量的转换需求。
- 注入组(最多 4 路):“加急生产线”,优先级更高!遇到紧急信号(比如关键传感器超阈值 ),能插队先转换,灵活处理特殊需求。
不管规则组还是注入组,干的事一样:把模拟信号(连续电压 )切成数字信号(0 和 1 组成的编码 )。
1.4.4信号 “暂存柜”:AD 数据寄存器
- 转换完的数字信号,得找地方 “临时存一下”:
- 规则组结果:存在 “规则结果 ×1” 里(1 组数据对应 1 个寄存器 )。
- 注入组结果:存在 “注入结果 ×4” 里(最多存 4 组,方便快速连续处理 )。
- 就像工厂生产完零件,先放仓库,等 CPU 来 “取货” 处理。
1.4.5 信号 “监督员”:模拟看门狗
- 作用:当 “质量检测员”,盯着转换后的数字信号,看是否超出你设定的范围(比如温度不能太高 / 太低 )。
- 触发:一旦超范围,立马发信号(相当于 “报警” ),告诉系统 “这信号有问题!”
1.4.6信号 “报警器”:中断输出控制 + NVIC
- 中断输出控制:收到模拟看门狗的 “报警” 后,整理信号,决定咋通知系统。
- NVIC(嵌套向量中断控制器):系统的 “总调度台”,收到中断信号后,暂停当前任务,优先处理 “信号异常” 事件(比如触发紧急程序、记录日志 )。
1.4.7 系统 “动力源”:触发控制 + RCC
- RCC(时钟控制器):给 ADC 整个系统提供 “心跳”(时钟信号 ),所有模块得跟着时钟节奏干活,否则会乱套。
- 触发控制:决定啥时候启动 ADC 转换,像 “开关”:可以软件手动触发(程序里写代码启动 ),也能硬件触发(比如定时器定时启动 ),发的信号叫 “START”。
1.4.8系统 “总开关”:开关控制
- 简单粗暴:控制 ADC 模块整体 “开 / 关”。不用 ADC 时,关掉省点电;要用时,打开干活。
1.5输入通道
1.6转换模式
1.7触发控制
1.8数据对齐
1.9转换时间
1.10校准
1.11硬件电路
二、AD单通道
2.1接线图
2.2代码
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"uint16_t ADValue; //定义AD值变量
float Voltage; //定义电压变量int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化AD_Init(); //AD初始化/*显示静态字符串*/OLED_ShowString(1, 1, "ADValue:");OLED_ShowString(2, 1, "Voltage:0.00V");while (1){ADValue = AD_GetValue(); //获取AD转换的值Voltage = (float)ADValue / 4095 * 3.3; //将AD值线性变换到0~3.3的范围,表示电压OLED_ShowNum(1, 9, ADValue, 4); //显示AD值OLED_ShowNum(2, 9, Voltage, 1); //显示电压值的整数部分OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2); //显示电压值的小数部分Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间}
}
AD.c
#include "stm32f10x.h" // Device header/*** 函 数:AD初始化* 参 数:无* 返 回 值:无*/
void AD_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*设置ADC时钟*/RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为模拟输入/*规则组通道配置*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0/*ADC初始化*/ADC_InitTypeDef ADC_InitStructure; //定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续转换,失能,每转换一次规则组序列后停止ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式,失能,只转换规则组的序列1这一个位置ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1/*ADC使能*/ADC_Cmd(ADC1, ENABLE); //使能ADC1,ADC开始运行/*ADC校准*/ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);
}/*** 函 数:获取AD转换的值* 参 数:无* 返 回 值:AD转换的值,范围:0~4095*/
uint16_t AD_GetValue(void)
{ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果
}
2.3ADC 相关 API 函数
1. 时钟配置函数
c
运行
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
-
功能:启用或禁用 APB2 总线上的外设时钟(如 ADC1、GPIOA)。
-
参数 :
RCC_APB2Periph
:外设选择(如RCC_APB2Periph_ADC1
、RCC_APB2Periph_GPIOA
)。NewState
:ENABLE
或DISABLE
。
c
运行
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
-
功能:配置 ADC 时钟分频(ADC 时钟必须 ≤ 14MHz)。
-
参数 :
RCC_PCLK2
:分频选项(如RCC_PCLK2_Div6
表示 72MHz/6=12MHz)。
2. GPIO 配置函数
c
运行
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
-
功能:初始化 GPIO 引脚。
-
参数 :
GPIOx
:GPIO 端口(如GPIOA
)。GPIO_InitStruct
:GPIO 配置结构体(模式、引脚、速度)。
3. ADC 配置函数
c
运行
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
-
功能:配置规则组通道的转换顺序和采样时间。
-
参数 :
ADCx
:ADC 外设(如ADC1
)。ADC_Channel
:通道号(如ADC_Channel_0
对应 PA0)。Rank
:序列位置(1~16)。ADC_SampleTime
:采样周期(如ADC_SampleTime_55Cycles5
)。
c
运行
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
-
功能:初始化 ADC 参数。
-
参数 :
ADCx
:ADC 外设。ADC_InitStruct
:ADC 配置结构体(模式、对齐方式、触发方式等)。
4. ADC 控制函数
c
运行
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
-
功能:启用或禁用 ADC。
-
参数 :
ADCx
:ADC 外设。NewState
:ENABLE
或DISABLE
。
c
运行
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
-
功能:软件触发 ADC 转换。
-
参数 :
ADCx
:ADC 外设。NewState
:ENABLE
触发一次转换。
c
运行
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
- 功能:ADC 校准流程(复位校准、获取复位状态、开始校准、获取校准状态)。
5. 状态和数据读取函数
c
运行
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
-
功能:检查 ADC 标志位状态(如转换完成标志
ADC_FLAG_EOC
)。 -
参数 :
ADCx
:ADC 外设。ADC_FLAG
:标志位类型。
c
运行
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
-
功能:获取 ADC 转换结果(0~4095,12 位分辨率)。
-
参数 :
ADCx
:ADC 外设。
AD.h
#ifndef __AD_H
#define __AD_Hvoid AD_Init(void);
uint16_t AD_GetValue(void);#endif
2.4工作现象
- 初始化阶段
- 调用
AD_Init()
后,ADC1 时钟开启,GPIOA 引脚配置为模拟输入模式,规则组通道 0(PA0)完成配置,ADC 进入就绪状态。 - 校准过程中,
ADC_ResetCalibration
和ADC_StartCalibration
会触发内部校准电路工作,校准完成后标志位自动清除。
- 调用
- 转换阶段
- 调用
AD_GetValue()
时,软件触发转换(ADC_SoftwareStartConvCmd
),ADC_FLAG_EOC
标志位在转换完成后置 1,返回值在 0~4095 范围内(对应输入电压 0~3.3V)。 - 若输入电压稳定(如接固定电阻分压),多次读取的 ADC 值波动较小(通常≤±3LSB)。
- 调用
- 数据对应关系
三、AD多通道
3.1接线图
3.2代码
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"uint16_t AD0, AD1, AD2, AD3; //定义AD值变量int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化AD_Init(); //AD初始化/*显示静态字符串*/OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:");while (1){AD0 = AD_GetValue(ADC_Channel_0); //单次启动ADC,转换通道0AD1 = AD_GetValue(ADC_Channel_1); //单次启动ADC,转换通道1AD2 = AD_GetValue(ADC_Channel_2); //单次启动ADC,转换通道2AD3 = AD_GetValue(ADC_Channel_3); //单次启动ADC,转换通道3OLED_ShowNum(1, 5, AD0, 4); //显示通道0的转换结果AD0OLED_ShowNum(2, 5, AD1, 4); //显示通道1的转换结果AD1OLED_ShowNum(3, 5, AD2, 4); //显示通道2的转换结果AD2OLED_ShowNum(4, 5, AD3, 4); //显示通道3的转换结果AD3Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间}
}
AD.c
#include "stm32f10x.h" // Device header/*** 函 数:AD初始化* 参 数:无* 返 回 值:无*/
void AD_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*设置ADC时钟*/RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0、PA1、PA2和PA3引脚初始化为模拟输入/*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*//*ADC初始化*/ADC_InitTypeDef ADC_InitStructure; //定义结构体变量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续转换,失能,每转换一次规则组序列后停止ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式,失能,只转换规则组的序列1这一个位置ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1/*ADC使能*/ADC_Cmd(ADC1, ENABLE); //使能ADC1,ADC开始运行/*ADC校准*/ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);
}/*** 函 数:获取AD转换的值* 参 数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3* 返 回 值:AD转换的值,范围:0~4095*/
uint16_t AD_GetValue(uint8_t ADC_Channel)
{ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); //在每次转换前,根据函数形参灵活更改规则组的通道1ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果
}
AD.h
#ifndef __AD_H
#define __AD_Hvoid AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);#endif
3.3AD单通道未提及的关键 API
除了之前提到的 ADC API,这段代码还涉及以下未详细说明的函数:
1. ADC 通道配置函数
c
运行
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
-
功能:配置规则组序列中的通道及其采样时间。
-
参数 :
ADC_Channel
:通道号(如ADC_Channel_0
~ADC_Channel_17
)。Rank
:序列位置(1~16,单通道模式下固定为 1)。ADC_SampleTime
:采样周期(如ADC_SampleTime_55Cycles5
)。
2. 采样时间说明
ADC 采样时间影响转换精度和速度,可选值包括:
c
运行
ADC_SampleTime_1Cycles5 // 1.5个ADC时钟周期(最快)
ADC_SampleTime_7Cycles5 // 7.5个周期
ADC_SampleTime_13Cycles5 // 13.5个周期
ADC_SampleTime_28Cycles5 // 28.5个周期
ADC_SampleTime_41Cycles5 // 41.5个周期
ADC_SampleTime_55Cycles5 // 55.5个周期
ADC_SampleTime_71Cycles5 // 71.5个周期
ADC_SampleTime_239Cycles5 // 239.5个周期(最慢,抗干扰最强)
3. ADC 状态标志位
c
运行
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
-
常用标志位 :
ADC_FLAG_EOC
:规则组转换完成标志(代码中使用)。ADC_FLAG_AWD
:模拟看门狗触发标志(需额外配置)。ADC_FLAG_OVR
:数据溢出标志(连续转换模式下可能出现)。
3.4工作现象
-
初始化后:ADC1 和 GPIOA 时钟开启,PA0~PA3 配置为模拟输入模式,ADC 处于就绪状态但未开始转换(需调用
AD_GetValue()
触发)。 -
动态通道选择:每次调用
AD_GetValue(channel)
时,会临时配置规则组序列 1 为指定通道(如ADC_Channel_2
对应 PA2),然后触发单次转换。 -
转换结果
:返回值范围为 0~4095,对应输入电压 0~3.3V(假设 VREF 为 3.3V)。例如:
- 输入 1.65V → 返回值约为 2048(1.65V/3.3V × 4095 ≈ 2048)。
- 输入悬空(无信号)→ 返回随机值(受噪声影响)。
1. 光敏传感器(AO 接 ADC 通道,如 PA0)
-
现象 :
- 光照越强(如手电筒照射),ADC 值 越大(对应电压越高,假设传感器输出与光照正相关);
- 光照越弱(手遮挡),ADC 值 越小,OLED / 串口数据同步变化。
-
原理:光敏电阻阻值随光照变化,转换为电压信号被 ADC 采集。
2. 声音传感器(AO 接 ADC 通道,如 PA1)
-
现象 :
- 安静时,ADC 值 稳定在低范围(背景噪声小);
- 拍手 / 说话时,ADC 值 瞬间跳升(声音越强,跳升幅度越大),数据波动明显。
-
原理:咪头采集声波,转换为电压波动,ADC 捕捉瞬时变化。
3. 红外避障传感器(AO 接 ADC 通道,如 PA2)
-
现象 :
- 无遮挡时,ADC 值 接近最大值(红外反射强,输出电压高);
- 手靠近遮挡时,ADC 值 骤降(反射弱,电压低),响应迅速。
-
原理:红外发射 + 接收,距离 / 遮挡影响反射强度,转换为电压变化。
4. 电位器(中间引脚接 ADC 通道,如 PA3)
-
现象 :
- 顺时针旋转:ADC 值 从 0→4095 线性递增(或递减,取决于接线方向);
- 逆时针旋转:ADC 值 反向递减 / 递增,变化平滑无跳变。
-
原理:电位器分压,输出电压与旋转角度线性相关。