GPIO一、简介
• ADC ( Analog-Digital Converter )模拟 - 数字转换器
• ADC 可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
• 12 位逐次逼近型 ADC , 1us 转换时间
• 输入电压范围: 0~3.3V ,转换结果范围: 0~4095
• 18 个输入通道,可测量 16 个外部和 2 个内部信号源
• 规则组和注入组两个转换单元
• 模拟看门狗自动监测输入电压范围
• STM32F103C8T6 ADC 资源: ADC1 、 ADC2 , 10 个外部输入通道
二、ADC结构
基本结构

信号输入层(左侧绿色模块)
这是 ADC 的信号来源,分为三类:
-
GPIO:16 路外部模拟输入引脚,用于采集外部传感器等信号。
-
温度传感器:芯片内部集成的温度检测模块,对应通道 16,用于测量芯片自身温度。
-
VREFINT:内部参考电压源,对应通道 17,为 ADC 提供精准的电压基准,用于校准和提升采样精度。

这些信号先经过一个输入多路选择器(黄色模块),再分配给转换层的不同通道。
核心转换层(橙色模块)
这是 ADC 的核心工作区,分为两个并行的转换通道组:
-
规则组:最多 16 个通道,结果存在 1 个规则组数据寄存器中。
-
注入组:最多 4 个通道,是 "优先级更高" 的中断式通道,可打断规则组的采样,用于处理紧急信号(如过压保护),结果存在 4 个独立的注入组数据寄存器中。
控制与驱动层(下方绿色模块)
-
触发控制 :负责选择软件 / 硬件触发方式,给 AD 转换器发送
START启动信号。 -
RCC :时钟控制模块,为 AD 转换器提供
CLOCK(即 ADCCLK),决定转换速度。
输出与处理层(右侧模块)
-
AD 数据寄存器:存储转换后的数字值,规则组和注入组的结果分开存储,避免数据覆盖。
-
模拟看门狗:用于监控转换结果,当电压超出预设阈值时,会立刻触发中断,常用于过压 / 欠压保护场景。
-
中断输出控制 :将转换完成(
EOC)、模拟看门狗等事件的中断信号,发送给NVIC(嵌套向量中断控制器),实现中断驱动的采样流程。
注
- EOC(End Of Conversion):转换完成标志,单次转换结束后由硬件置位,用于通知 CPU 读取结果或触发中断。
- START:启动转换信号,由触发控制模块发出,启动一次采样流程。
- CLOCK:ADCCLK 时钟,由 RCC 提供,是所有硬件动作的时序基准。
CLOCK

看上面的右下角ADC部分,ADC预分频可以2/4/6/8分频,但是ADCLOCK最大为14MHZ,所以只能6/8分频,建议6分频。
触发控制

左下角的触发模块可选择软件 / 硬件触发方式。
规则组触发
-
触发源选择 (
EXTSEL[2:0]控制位)可以选择来自定时器(TIM1/TIM2/TIM3/TIM4/TIM8)的 TRGO 事件、定时器捕获比较通道(CH1/CH2/CH3),或外部中断线(EXTI_11)作为触发信号。 -
触发使能 (
EXTTRIG控制位)用于使能硬件触发功能;若关闭此位,则默认使用软件触发(通过ADC_SoftwareStartConvCmd启动)。 -
触发重映射 (
ADCx_ETRGREG_REMAP控制位)部分型号可通过此位重新映射触发源,增加配置灵活性。
注入组触发(高优先级采样的启动逻辑)
-
触发源选择 (
JEXTSEL[2:0]控制位)可选触发源包括定时器(TIM1/TIM2/TIM3/TIM4/TIM8)的 TRGO 事件、定时器捕获比较通道(CH4),或外部中断线(EXTI_15)。 -
触发使能 (
JEXTTRIG控制位)用于使能注入组的硬件触发;关闭时,注入组只能通过软件触发启动。 -
触发重映射 (
ADCx_ETRGINJ_REMAP控制位)可重新映射注入组的触发源,适配更复杂的场景。
数据对齐方式
ADC 的12 位转换结果在 16 位数据寄存器中的两种对齐方式,剩余的4位补零。

校准
•ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
•建议在每次上电后执行一次校准
•启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
三、ADC工作方式
转换
AD转换的步骤:采样,保持,量化,编码
STM32 ADC的总转换时间为:TCONV = 采样时间 + 12.5个ADC周期
例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs
采样:用电容将外部信号电平采集起来。
保持:把采样电容上的电压稳定保存下来,为后续的量化、编码提供一个固定不变的电压参考。
量化:与 ADC 的参考电压(Vref+,通常为 3.3V)做比较,把信号数字化。
编码:将量化得到的数字量级,转换成二进制编码(或其他数字编码),存储到 ADC 的数据寄存器中。
扫描模式
有多个通道工作,触发一次就扫描到最后一个通道。

连续转化
转化完成就进入下一个。

四、单转化实现多通道转换
接线图

时钟使能+分频
使能外设时钟:ADC1时钟、GPIOA时钟。
PCLK2 6分频。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
配置GPIO
配置为模拟输入模式,该模式下 GPIO 引脚与数字电路完全隔离,仅作为 ADC 的模拟输入口,速度配置无实际作用,可任意配置。
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AIN;//此时GPIO口只受到ADC的控制
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);
配置ADC
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 独立模式:ADC1单独工作,不与其他ADC同步
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,与非扫描模式匹配 ADC_Init(ADC1,&ADC_InitStructure);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1,&ADC_InitStructure);
校准+使能
使能ADC1 ADC_Cmd(ADC1,ENABLE);
这些校准标志位会自动复位。
ADC_ResetCalibration(ADC1);
// 复位校准:清除旧的校准数据
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
// 等待复位校准完成
ADC_StartCalibration(ADC1);
// 开始ADC自校准:生成新的校准参数
while(ADC_GetCalibrationStatus(ADC1) == SET);
// 等待校准完成
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
触发转换+读取转换值
实现 "一次编写,多通道复用",也是实现 "单次转换模式多通道采集" 的关键。
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
// 动态配置规则组通道:ADC1、指定通道、序列1、采样时间55.5个ADCCLK周期 ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
// 软件触发ADC1开始单次转换
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
// 轮询等待转换完成标志(EOC)置位,未完成则持续等待 while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
// 返回ADC转换结果:读取数据寄存器的值
return ADC_GetConversionValue(ADC1);
调用:
调用函数AD_GetValue(uint8_t ADC_Channel),选择相对应的通道。
uint16_t AD0, AD1, AD2,AD3;
int main(void)
{
OLED_Init();
AD_Init();
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);
AD1 = AD_GetValue(ADC_Channel_1);
AD2 = AD_GetValue(ADC_Channel_2);
AD3 = AD_GetValue(ADC_Channel_3);
OLED_ShowNum(1,5,AD0,4);
OLED_ShowNum(2,5,AD1,4);
OLED_ShowNum(3,5,AD2,4);
OLED_ShowNum(4,5,AD3,4);
Delay_ms(100);
}
}