STM32——ADC、多通道转换(十三)

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);
	}	
}
相关推荐
日更嵌入式的打工仔2 小时前
嵌入式MPU、MCU与SoC的本质区别
单片机·嵌入式硬件
__万波__2 小时前
STM32L475看门狗
stm32·单片机·嵌入式硬件
wanglong37133 小时前
51单片机STC8G1K08输出PWM
单片机·嵌入式硬件·51单片机
传感器与混合集成电路13 小时前
210℃与175℃高温存储器选型研究:LHM256MB与LDMF4GA-H架构与可靠性对比(上)
嵌入式硬件·能源
时光找茬14 小时前
【瑞萨AI挑战赛-FPB-RA6E2】+ 从零开始:FPB-RA6E2 开箱测评与 e2 studio 环境配置
c++·单片机·边缘计算
17(无规则自律)14 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考
@good_good_study15 小时前
FreeRTOS内存管理
单片机
Hello_Embed16 小时前
libmodbus 移植 STM32(基础篇)
笔记·stm32·单片机·学习·modbus
qq_3975623116 小时前
QT工程 , 生成别的电脑运行的exe程序
嵌入式硬件·qt