一.ADC概念
STM32 系列微控制器的 ADC(模拟 - 数字转换器)是其重要的外设之一,用于将外部或内部的模拟信号转换为数字信号,广泛应用于传感器数据采集、电压监测、信号处理等场景。以下是 STM32 ADC 的核心特性和关键信息:
1. 基本特性
- 分辨率 :常见为 12 位(部分型号支持 10/8/6 位可调,以牺牲精度换取速度),即转换结果范围为 0~4095(对应模拟输入电压范围)。
- 转换速率:最高可达数兆采样率(如 STM32H7 系列可达 36MSPS,不同系列性能差异较大,如 F1 系列最高约 1MHz,F4 系列约 2.4MHz)。
- 通道数量 :最多支持 16 个外部模拟通道(对应 GPIO 引脚)+ 多个内部通道(如温度传感器、内部参考电压 VREFINT、VBAT 电池电压等)。
- 供电与参考电压:通常以 VDD 为供电,模拟参考电压可使用内部 VREF+(默认与 VDD 相连)或外部独立参考电压(部分型号支持)。
2. 工作模式
- 单次转换模式:触发一次转换后,只转换一个通道或一组通道,完成后停止。
- 连续转换模式:触发后持续转换指定通道,直到手动停止。
- 扫描模式:自动按顺序转换多个通道(需配置通道序列),适用于多通道采集。
- 间断模式:将通道序列分成多组,每组转换完成后暂停,等待下一次触发,适合分批采集。
3. 转换触发方式
- 软件触发:通过程序写入寄存器主动触发转换。
- 硬件触发:由定时器(TIM1/TIM2 等)、外部中断引脚(EXTI)等外设触发,适合周期性或事件驱动的采集(如定时采样传感器)。
4. 数据对齐方式
- 转换结果可配置为左对齐 或右对齐(默认右对齐,便于直接处理 0~4095 的数值)。
5. 校准功能
- 支持内部校准(包括单一端校准和差分校准,部分型号),可减少偏移误差和增益误差,提高转换精度,建议初始化时执行校准。
6. 常见应用步骤
- 引脚配置:将 ADC 通道对应的 GPIO 配置为模拟输入模式(GPIO_Mode_AN)。
- ADC 时钟配置:ADC 时钟来自 APB2 总线(部分系列如 H7 有独立时钟),需确保时钟频率在手册规定范围内(如 F1 系列 ADC 时钟不超过 14MHz)。
- 初始化配置:设置分辨率、转换模式(单次 / 连续)、扫描模式、触发源、数据对齐等。
- 通道配置:指定转换通道及顺序(如规则通道组或注入通道组,注入通道可中断优先级更高的转换)。
- 校准 :调用校准函数(如
ADC_Calibration_Volts()
)。 - 启动转换:通过软件或硬件触发,等待转换完成(可查询标志位或使用中断 / DMA)。
- 读取数据:从 ADC 数据寄存器(ADC_DR)读取转换结果。
7. 提高精度的技巧
- 确保参考电压稳定(必要时外接高精度基准源)。
- 对模拟输入信号进行滤波(如 RC 滤波),减少噪声。
- 避免 ADC 通道与高频数字信号引脚相邻,减少干扰。
- 多次采样后取平均值,降低随机误差。
二.原理图

这张图展示了 STM32 系列单片机中 ADC(模拟 - 数字转换器)的内部结构与信号流程,可从以下几个部分来解释:
输入部分
- GPIO :有 16 个通用输入 / 输出引脚,可作为 ADC 的模拟输入通道,用于采集外部传感器(如温度传感器、电压传感器等)的模拟信号。
- 温度:芯片内部的温度传感器,其输出的模拟信号可被 ADC 采集,用于监测芯片温度。
- VREFINT:内部参考电压,为 ADC 转换提供稳定的参考电平,保证 ADC 转换的精度。
ADC 转换器部分
ADC 转换器分为规则组和注入组:
- 规则组:最多可配置 16 个通道,用于常规的 ADC 转换任务,按预先设置的顺序进行转换,转换结果存储在 1 个规则组结果寄存器中。
- 注入组:最多可配置 4 个通道,具有更高的优先级,可在规则组转换过程中插入转换,转换结果存储在 4 个注入组结果寄存器中,适用于需要紧急处理的模拟信号采集。
控制与时钟部分
- 触发控制(START):用于启动 ADC 转换,可以是软件触发(通过程序指令启动),也可以是硬件触发(如定时器触发、外部引脚触发等)。
- RCC(CLOCK):复位与时钟控制模块,为 ADC 提供工作时钟,时钟的配置会影响 ADC 转换的速度和精度。
数据存储与中断部分
- AD 数据寄存器 :
- 规则组结果寄存器:存储规则组通道转换后的数字结果。
- 注入组结果寄存器:存储注入组通道转换后的数字结果。
- 模拟看门狗:可设置上限和下限阈值,当 ADC 转换结果超出阈值范围时,会触发中断或标志位,用于对模拟信号进行监控和异常检测。
- 中断输出控制与 NVIC:当 ADC 转换完成(EOC,End Of Conversion)或模拟看门狗触发等事件发生时,会产生中断请求,通过中断输出控制模块传递给嵌套向量中断控制器(NVIC),进而执行相应的中断服务程序。
开关控制
用于控制 ADC 模块的使能与关闭,以实现功耗管理等功能。
三.输入通道对应图

四.转换模式
1. 单次转换模式(Single - conversion mode)
- 工作原理:当触发 ADC 转换后,ADC 仅执行一次转换操作,完成对指定通道的模拟信号到数字信号的转换,转换结束后,ADC 进入等待状态,不会自动进行下一次转换。触发方式可以是软件触发,也可以是硬件触发(如定时器触发、外部中断触发等) 。
- 代码示例(基于 HAL 库):
cpp
// 配置ADC为单次转换模式
ADC_HandleTypeDef hadc;
hadc.Instance = ADC1;
hadc.Init.ContinuousConvMode = DISABLE;
HAL_ADC_Init(&hadc);
// 触发转换
HAL_ADC_Start(&hadc);
while(HAL_ADC_PollForConversion(&hadc, 100)!= HAL_OK);
uint16_t adc_value = HAL_ADC_GetValue(&hadc);
- 适用场景:适用于对采样频率要求不高,只需要在特定时刻获取一次模拟信号数值的场景,例如按键按下后采集一次传感器数据,或者在系统初始化时对某个模拟量进行一次检测 。
2. 连续转换模式(Continuous - conversion mode)
- 工作原理:一旦触发 ADC 转换,ADC 会持续不断地对指定通道进行转换,转换完一个数据后立即开始下一次转换,无需再次触发。只有当手动停止 ADC 或者发生复位等操作时,转换才会停止。
- 代码示例(基于 HAL 库):
cpp
// 配置ADC为连续转换模式
ADC_HandleTypeDef hadc;
hadc.Instance = ADC1;
hadc.Init.ContinuousConvMode = ENABLE;
HAL_ADC_Init(&hadc);
// 启动连续转换
HAL_ADC_Start(&hadc);
while(1) {
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc), HAL_ADC_STATE_REG_EOC)) {
uint16_t adc_value = HAL_ADC_GetValue(&hadc);
}
}
- 适用场景:常用于需要实时监测模拟信号变化的场景,如实时监测环境温度、压力等变化缓慢的物理量,或者音频信号采集等对采样频率要求较高的场景 。
3. 扫描模式(Scan mode)
- 工作原理:当启用扫描模式时,ADC 会按照预先配置的通道序列,依次对多个通道进行转换。在转换过程中,可以选择是否开启连续转换模式。如果开启连续转换,ADC 会循环扫描所有配置的通道;如果是单次扫描,则只扫描一次通道序列。
- 代码示例(基于 HAL 库):
cpp
// 配置ADC为扫描模式并选择多个通道
ADC_HandleTypeDef hadc;
hadc.Instance = ADC1;
hadc.Init.ScanConvMode = ENABLE;
HAL_ADC_Init(&hadc);
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_2;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
// 启动转换
HAL_ADC_Start(&hadc);
while(HAL_ADC_PollForConversion(&hadc, 100)!= HAL_OK);
uint16_t adc_value_0 = HAL_ADC_GetValue(&hadc);
// 读取下一个通道的值需要重新触发转换(非连续模式下)或等待下次扫描(连续模式下)
- 适用场景:适用于需要同时采集多个模拟信号的场景,如在工业控制系统中,同时监测多个传感器(温度、压力、液位等)的数据,或者在多通道音频采集系统中 。
4. 间断模式(Discontinuous conversion mode)
- 工作原理:间断模式是对扫描模式的一种补充。可以将通道序列分成若干组,每次触发只转换一组通道,转换完一组后停止,等待下一次触发再转换下一组。这样可以在不需要连续扫描所有通道的情况下,灵活地对部分通道进行采样。
- 代码示例(基于 HAL 库):
cpp
// 配置ADC为间断模式
ADC_HandleTypeDef hadc;
hadc.Instance = ADC1;
hadc.Init.DiscontinuousConvMode = ENABLE;
hadc.Init.NbrOfDiscConversion = 2; // 分2组
HAL_ADC_Init(&hadc);
// 配置通道组
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_2;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
// 触发转换
HAL_ADC_Start(&hadc);
while(HAL_ADC_PollForConversion(&hadc, 100)!= HAL_OK);
uint16_t adc_value_0 = HAL_ADC_GetValue(&hadc);
// 触发下一组转换
HAL_ADC_Start(&hadc);
while(HAL_ADC_PollForConversion(&hadc, 100)!= HAL_OK);
uint16_t adc_value_1 = HAL_ADC_GetValue(&hadc);
- 适用场景:适用于对不同通道有不同采样频率要求的场景,或者在资源有限的情况下,不需要频繁扫描所有通道,而是按需分组扫描 。
五.触发控制
1.触发规则图

2.软件触发
- 原理 :通过在程序中执行特定的代码指令,向 ADC 的相关寄存器写入启动转换的命令,从而触发 ADC 开始转换。在 HAL 库中,使用HAL_ADC_Start()或HAL_ADC_Start_IT() 函数(后者用于开启中断方式的转换)来实现软件触发;在标准库中,一般是通过操作 ADC 的控制寄存器(如
ADC_CR2
寄存器的SWSTART
位)来触发转换 。 - 示例代码(HAL 库):
cpp
ADC_HandleTypeDef hadc;
// 初始化ADC相关配置...
// 软件触发ADC转换
HAL_ADC_Start(&hadc);
while(HAL_ADC_PollForConversion(&hadc, 100)!= HAL_OK); // 等待转换完成,超时时间100ms
uint16_t adc_value = HAL_ADC_GetValue(&hadc); // 获取转换结果
- 特点与应用场景 :
- 灵活性高:可以在程序的任意位置,根据逻辑需要随时触发 ADC 转换,例如在检测到某个特定条件满足时(如按键按下),才触发 ADC 对传感器数据进行采集。
- 存在软件延迟:由于触发信号的产生依赖于 CPU 执行代码,而 CPU 可能正忙于处理其他任务,导致触发时刻存在不确定性,因此不适用于对转换时间精度要求极高的场景。它比较适合对采样实时性要求不高、按需采样的应用,如一些简单的工业控制参数监测、用户手动触发的数据采集等。
3.硬件触发
- 原理 :利用 STM32 内部的其他外设(如定时器、外部中断线等)产生的信号来触发 ADC 转换。当指定的硬件外设产生特定事件(如定时器更新事件、外部引脚的上升沿或下降沿)时,会自动向 ADC 发送触发信号,启动转换。在 HAL 库中,需要先配置相关硬件外设(如定时器),然后通过 ADC 的初始化结构体
ADC_HandleTypeDef
中的ExternalTrigConv
成员来指定触发源 。 - 示例代码(以定时器触发为例,HAL 库):
cpp
ADC_HandleTypeDef hadc;
TIM_HandleTypeDef htim;
// ADC初始化配置
hadc.Instance = ADC1;
hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1; // 选择TIM1的CC1事件作为触发源
HAL_ADC_Init(&hadc);
// 定时器初始化配置
htim.Instance = TIM1;
htim.Init.Prescaler = 71; // 预分频器,72MHz / (71 + 1) = 1MHz
htim.Init.Period = 999; // 定时周期,1ms
HAL_TIM_Base_Init(&htim);
// 使能定时器更新事件触发
__HAL_TIM_ENABLE_IT(&htim, TIM_IT_UPDATE);
HAL_TIM_Base_Start(&htim);
// 启动ADC
HAL_ADC_Start(&hadc);
- 特点与应用场景 :
- 高精度定时触发:硬件触发信号由专门的硬件外设产生,不受 CPU 任务调度的影响,触发时间的精度主要取决于硬件时钟的精度,因此可以实现非常精确的定时采样。例如,在音频信号采集应用中,需要按照固定的采样频率对音频进行采样,就可以使用定时器触发 ADC 来实现精确的周期性转换。
- 多外设联动:能够方便地实现多个外设之间的协同工作,构建复杂的系统功能。比如,在工业自动化控制系统中,通过定时器定时触发 ADC 采集传感器数据,同时配合 DMA(直接内存访问)将采集到的数据快速传输到内存中,整个过程无需 CPU 过多干预,大大提高了系统的运行效率。
六.校准
1.校准原因
- 制造工艺差异:在 ADC 芯片制造过程中,由于工艺的局限性,不同芯片的模拟电路参数(如放大器增益、比较器阈值等)会存在一定的偏差,这些偏差会导致转换结果与实际值之间存在误差。
- 环境因素影响:工作环境中的温度、电源电压波动等因素,也会对 ADC 的性能产生影响,使得转换精度下降。通过校准,可以在一定程度上补偿这些因素带来的误差,使 ADC 的转换结果更加接近真实值。
2.校准类型
- 零点校准:主要用于修正 ADC 在输入模拟信号为零(或接近零)时的偏差,确保转换结果在零点附近的准确性。
- 满量程校准:用于调整 ADC 在输入满量程模拟信号时的转换精度,使转换结果能够准确反映满量程的实际值。
3.校准步骤
- 使能 ADC 并等待稳定:在进行校准前,需要先使能 ADC,让其进入工作状态,并等待一段时间,确保 ADC 内部电路达到稳定状态。
- 复位校准寄存器:通过操作 ADC 的相关寄存器,复位校准电路,清除之前可能存在的校准数据。
- 等待复位校准完成:复位校准寄存器后,需要等待复位操作完成,这可以通过查询相关的状态标志位来判断。
- 启动校准:复位校准完成后,启动 ADC 的校准过程,ADC 内部会自动进行一系列的校准操作,例如对内部参考电压、放大器增益等参数进行调整。
- 等待校准完成:在校准过程中,持续查询校准状态标志位,当标志位表明校准完成后,才可以进行正常的 ADC 转换操作。
七.电位器测电压adc单通道
1.接线图

2.代码
1.AD.h
cpp
#ifndef __AD_H
#define __AD_H
void AD_Init(void);
uint16_t AD_GetValue(void);
#endif
2.AD.c
cpp
#include "stm32f10x.h" // Device header
/**
* 函 数:AD初始化
* 参 数:无
* 返 回 值:无
*/
void AD_Init(void)
{
/*时钟使能:STM32 外设需先开启时钟才能工作。
开启 ADC1 的时钟(ADC1 挂载在 APB2 总线上)。
开启 GPIOA 的时钟(PA0 作为 ADC 输入引脚,需配置其模式)。*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*ADC 时钟配置:ADC 的时钟(ADCCLK)来自 APB2 总线时钟(PCLK2,默认 72MHz)。
STM32F10x 的 ADC 最大时钟为 14MHz,此处 6 分频后得到 12MHz(72/6),符合规格。*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //ADC 通道需连接到 "模拟输入模式" 的 GPIO 引脚(PA0 对应 ADC1 的通道 0)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为模拟输入
/*配置规则通道:ADC 转换通道分为 "规则组"(常规转换)和 "注入组"(中断优先级转换),此处配置规则组:
ADC1:操作的 ADC 外设。
ADC_Channel_0:选择通道 0(对应 PA0)。
1:通道在转换序列中的排名(序列 1,即第一个转换)。
ADC_SampleTime_55Cycles5:采样时间为 55.5 个 ADC 时钟周期
(采样时间越长,抗噪声能力越强,转换速度越慢)*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
/*ADC初始化*/
ADC_InitTypeDef ADC_InitStructure; //定义结构体变量
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1
ADC_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的数,在非扫描模式下,只能是1
ADC_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转换的结果
}
3.main.c
cpp
#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,手动增加一些转换的间隔时间
}
}
3.程序现象

左转减小,右转增大
八.adc多通道
1.接线图

2.代码
1.AD.h
cpp
#ifndef __AD_H
#define __AD_H
void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);
#endif
2.AD.c
cpp
#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; //模式,选择独立模式,即单独使用ADC1
ADC_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的数,在非扫描模式下,只能是1
ADC_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); //在每次转换前,根据函数形参灵活更改规则组的通道1
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束
return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果
}
3.main.c
cpp
#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,转换通道0
AD1 = AD_GetValue(ADC_Channel_1); //单次启动ADC,转换通道1
AD2 = AD_GetValue(ADC_Channel_2); //单次启动ADC,转换通道2
AD3 = AD_GetValue(ADC_Channel_3); //单次启动ADC,转换通道3
OLED_ShowNum(1, 5, AD0, 4); //显示通道0的转换结果AD0
OLED_ShowNum(2, 5, AD1, 4); //显示通道1的转换结果AD1
OLED_ShowNum(3, 5, AD2, 4); //显示通道2的转换结果AD2
OLED_ShowNum(4, 5, AD3, 4); //显示通道3的转换结果AD3
Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间
}
}
3.程序现象

可以看到,对射红外传感器,温度传感器,激光传感器,变化时,adc转换的数值也发生了变化
九.总结adc转换步骤
-
时钟配置
- 开启 ADC 外设时钟(如 ADC1)和对应 GPIO 时钟(如 GPIOA)。
- 配置 ADC 时钟(ADCCLK),确保不超过芯片最大限制(F10x 系列≤14MHz)。
-
GPIO 配置
- 将 ADC 通道对应的 GPIO 引脚设置为 "模拟输入模式"(
GPIO_Mode_AIN
),禁用上拉 / 下拉。
- 将 ADC 通道对应的 GPIO 引脚设置为 "模拟输入模式"(
-
通道配置
- 配置规则组通道(或注入组),指定通道编号、转换序列排名和采样时间。
-
ADC 核心参数初始化
- 设置工作模式(独立 / 同步)、数据对齐方式(左 / 右)、触发方式(软件 / 硬件)、转换模式(单次 / 连续)、扫描模式(使能 / 禁用)等。
-
使能 ADC 并校准
- 使能 ADC 外设,执行复位校准和启动校准流程,确保转换精度。
-
触发转换并读取数据
- 软件触发(或等待硬件触发)ADC 转换。
- 等待转换完成标志(EOC)。
- 读取数据寄存器的值,得到数字量(0~4095,12 位分辨率)。
-
(可选)计算实际物理量
- 根据参考电压(如 3.3V),通过公式
物理量 = (数字值 / 4095) × 满量程值
计算实际值(如电压、温度等)。
- 根据参考电压(如 3.3V),通过公式