AD单通道&AD多通道

AD单通道&AD多通道

ADC

  • 什么是ADC?
    ①ADC(Analog-Digital Converter)模拟-数字转换器,ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁.
    12位逐次逼近型ADC,1us转换时间
    输入电压范围:0-3.3v,转换结果范围:0~4095
    ④18个输入通道,可测量16个外部和2个内部信号源
    ⑤规则组和注入组两个转换单元
    STM32F103C8T6 的ADC资源:ADC1、ADC2,10个外部输入通道
  • ADC的基本结构如图:
    在这里插入图片描述
  • ADC通道与引脚对应关系如图:

值得注意的是:STM32只有10个外部输入通道,即PA0到PB1

  • ADC规则组的转换模式有四种:
    ①单次转换,非扫描:
    在这里插入图片描述
    ②连续转换,非扫描:
    在这里插入图片描述
    ③单次转换,扫描:
    在这里插入图片描述
    ④连续转换,扫描:
    在这里插入图片描述
    ⑤各个转换模式的EOC变化如图:
    在这里插入图片描述

AD单通道

  • 接线图如下:
    电位器相当于滑动变阻器,往右拧阻值减小,往左拧阻值增大.
    在这里插入图片描述

  • 复制OLED显示屏的项目并新增在Hardware文件夹下新增AD.c和AD.h文件
    AD.c代码:

#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
	//ADC都是总线APB2上的设备
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//ADCCLK配置,ADC最大时钟频率是14MHZ,RCC_PCLK2_Div6表示6分频,使用的内部时钟是72MHZ,6分频后是12MHZ
	//配置ADCCLK是为了驱动ADC内部有规律的逐次比较
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	//使用参数GPIO_Mode_AIN是因为模数转换需要未经任何“加工”的原始模拟电压
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	//GPIO_Speed(速度)参数在配置为模拟输入模式时是​​不起作用的,但是程序上仍需要填写一个合法值
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//配置ADC规则组通道模式.ADC_Channel_0即通道0(因为使用PA0输入信号,根据引脚定义表对应为ADC_Channel_0)
	//1即序列1,这两个参数表明将ADC通道0放在序列1上进行采样操作
	//ADC_SampleTime_55Cycles5表示采样时间为55.5个ADCCLK周期
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	
	//结构体初始化ADC
	ADC_InitTypeDef ADC_InitStructure;
	//转换模式,连续转换或者单次转换,DISABLE是单次转换
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	//ADC是12bit的,而数据寄存器是16bit的,ADC_DataAlign指定ADC数据在数据寄存器是左对齐还是右对齐
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	//外部触发转换选择,指定外部触发器的触发源,这里使用内部软件触发,所有填参数ADC_ExternalTrigConv_None
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	//选择ADC是独立转换还是双ADC模式,ADC_Mode_Independent是独立转换模式
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	//指定ADC在扫描模式下一次扫描几个通道,仅在扫描模式下生效
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	//扫描模式,连续扫描模式或者单次扫描模式,DISABLE表示单次扫描模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_Init(ADC1,&ADC_InitStructure);
	
	//ADC上电
	ADC_Cmd(ADC1,ENABLE);
	
	//上电后校准,大幅减小因 ADC 内部电容器组变化造成的精度误差,提高转换准确性
	//复位校准
	ADC_ResetCalibration(ADC1);
	//获取复位校准状态,由ADC_ResetCalibration(ADC1);设置为校准状态为1,硬件校准完毕后将校准状态置0
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
	//开始校准
	ADC_StartCalibration(ADC1);
	//等待校准完成
	while(ADC_GetCalibrationStatus(ADC1) == SET);

}

uint16_t AD_GetValue(void)
{
	//软件触发转换
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	//查看规则组是否转换完成ADC_FLAG_EOC = RESET表示未转换完成,EOC为规则组或注入组转换完成标志位,时间为采样时间+转换时间
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
	//将数据寄存器的值返回,该函数读取完寄存器的值会自动清除标志位
	return ADC_GetConversionValue(ADC1);

}

值得注意的是:
ADC对模拟电压转换到数字电压是采用逐次逼近法的方式,这就要采用一个时钟频率来控制逐次逼近法的规律性比较,所以要开启ADC的时钟,即RCC_ADCCLKConfig(RCC_PCLK2_Div6);,参数RCC_PCLK2_Div6表示6分频,ADC时钟最大频率是14MHZ,而STM32的内部时钟是72MHZ,所以要对其进行分频操作,ADCCLK支持2 4 6 8 四种分频操作,采用6分频为72MHZ/6=12MHZ正好在14MHZ范围内.
②在初始化GPIO操作时,GPIO的模式GPIO_Mode 应选择为GPIO_Mode_AIN模式,使用参数GPIO_Mode_AIN是因为模数转换需要未经任何“加工”的原始模拟电压,具体原因如下图:
在这里插入图片描述
③配置ADC规则组通道模式函数ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);,参数ADC_Channel_0含义是ADC的通道0,由于使用的是PA0引脚,根据引脚定义图,PA0引脚对应的是ADC_Channel_0.1的含义是序列1,这两个参数表明将ADC通道0放在序列1上进行采样操作.ADC_SampleTime_55Cycles5表示采样时间为55.5个ADCCLK周期
④ADC上电后要进行先复位再校准.为什么要这样?
答:目的是为了确保ADC模块能够从一个已知且稳定的初始状态开始计算校准系数,从而获得最高的转换精度。简单来说,这个固定的顺序 复位校准 -> AD校准的核心原因是:复位校准是准备工作,确保“从零开始”。AD校准是核心计算,负责“计算误差”并“存储修正值”。跳过复位校准直接进行AD校准,就像没有清零的计算器直接做复杂的数学运算,结果很可能是不正确的。
⑤ADC_SoftwareStartConvCmd(ADC1, ENABLE);函数的核心作用是通过软件方式触发指定的ADC(此处为ADC1)开始一次模数转换。该函数设置ADC控制寄存器中的软件启动位(SWSTART),一旦SWSTART位被设置,ADC模块便会立即开始对当前选中的通道进行转换,硬件会在转换开始后立刻自动将SWSTART位清零。因此,ADC_GetSoftwareStartConvStatus函数并不能用来判断转换是否结束,它仅仅反映SWSTART位的瞬时状态.
ADC_Init();是“定规矩”,而 ADC_SoftwareStartConvCmd();才是“下命令开始干活”.
ADC的连续转换是指按序列号大小从小到大扫描待处理的序列,每次处理完毕单个序列号都会有EOC信号产生,扫描模式是指按序列号扫描完毕到最后一组才产生EOC信号.个人感觉扫描模式只是单纯的控制不在每一组数据处理完后就产生EOC的,而是等序列里的所有待处理的数据处理完毕才产生一个EOC,另外说一下EOC,EOC是ADC状态寄存器(如STM32的ADC_SR)中的一个硬件标志位(通常对应寄存器的第1位,即BIT1)。当ADC完成对规则组(Regular Group)中某一个通道的模数转换后,硬件会自动将该位置1(表示转换结束).

  • 所以ADC规则组的初始化流程如图:

main.c代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;
float Voltage;
int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	AD_Init();
	
	OLED_ShowString(1,1,"ADValue:");
	OLED_ShowString(2,1,"Voltage:0.00v");
	while (1)
	{
		ADValue = AD_GetValue();
		//获取电压值,4095是ADC的最大量程
		Voltage = (float)AD_GetValue() / 4095 * 3.3;
		OLED_ShowNum(1,9,ADValue,5);
		OLED_ShowNum(2,9,Voltage,1);
		OLED_ShowNum(2,11,(uint16_t)(Voltage*100)%100,2);
		
		Delay_ms(100);
	}
}

这的注意的是:
①在代码Voltage = (float)AD_GetValue() / 4095 * 3.3;是为了AD_GetValue() / 4095 是在获取比例(ADC的最大量程是4095),然后乘以3.3(电源电压)来计算电压.
①在代码OLED_ShowNum(2,11,(uint16_t)(Voltage100)%100,2);,Voltage100可能是小数,而小数是没有办法取余的.

AD多通道

  • 依旧是采用单次转换,非扫描模式,因为扫描模式还要用DMA,我还没学到,感觉也可以使用多次转换,非扫描模式.
  • 接线图如下:
    在这里插入图片描述
  • 复制AD单通道工程并修改AD.c代码
    AD.c代码:
#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
	//ADC都是总线APB2上的设备
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//ADCCLK配置,ADC最大时钟频率是14MHZ,RCC_PCLK2_Div6表示6分频,使用的内部时钟是72MHZ,6分频后是12MHZ
	//配置ADCCLK是为了驱动ADC内部有规律的逐次比较
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	//使用参数GPIO_Mode_AIN是因为模数转换需要未经任何“加工”的原始模拟电压
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	//GPIO_Speed(速度)参数在配置为模拟输入模式时是​​不起作用的,但是程序上仍需要填写一个合法值
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//配置ADC规则组通道模式.ADC_Channel_0即通道0(因为使用PA0输入信号,根据引脚定义表对应为ADC_Channel_0)
	//1即序列1,这两个参数表明将ADC通道0放在序列1上进行采样操作
	//ADC_SampleTime_55Cycles5表示采样时间为55.5个ADCCLK周期
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	
	//结构体初始化ADC
	ADC_InitTypeDef ADC_InitStructure;
	//转换模式,连续转换或者单次转换,DISABLE是单次转换
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	//ADC是12bit的,而数据寄存器是16bit的,ADC_DataAlign指定ADC数据在数据寄存器是左对齐还是右对齐
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	//外部触发转换选择,指定外部触发器的触发源,这里使用内部软件触发,所有填参数ADC_ExternalTrigConv_None
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	//选择ADC是独立转换还是双ADC模式,ADC_Mode_Independent是独立转换模式
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	//指定ADC在扫描模式下一次扫描几个通道,仅在扫描模式下生效
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	//扫描模式,连续扫描模式或者单次扫描模式,DISABLE表示单次扫描模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_Init(ADC1,&ADC_InitStructure);
	
	//ADC上电
	ADC_Cmd(ADC1,ENABLE);
	
	//上电后校准,大幅减小因 ADC 内部电容器组变化造成的精度误差,提高转换准确性
	//复位校准
	ADC_ResetCalibration(ADC1);
	//获取复位校准状态,由ADC_ResetCalibration(ADC1);设置为校准状态为1,硬件校准完毕后将校准状态置0
	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);
	//查看规则组是否转换完成ADC_FLAG_EOC = RESET表示未转换完成,EOC为规则组或注入组转换完成标志位,时间为采样时间+转换时间
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
	//将数据寄存器的值返回,该函数读取完寄存器的值会自动清除标志位
	return ADC_GetConversionValue(ADC1);

}

相较于ADC单通道工程,代码变化如下:
①外接设备增多,修改GPIO引脚参数为GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
②在函数uint16_t AD_GetValue(uint8_t ADC_Channel);增加代码ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);**这个函数是STM32库中用来配置ADC规则组转换通道的核心函数,它告诉ADC哪个引脚需要转换、转换的顺序以及采样时间。**第二个参数ADC_Channel是通过外部传参来保证测量各个引脚的.

main.c代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	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);
	}
}

效果图
<think>好的,我现在需要回答用户关于ADC通道原理及应用的问题。首先,我应该先回顾用户提供的引用资料,确保回答符合要求。引用[2]提到ADC是将模拟信号转换为数字信号的过程,包括采样、保持、量化和编码四个步骤。而引用[1]则涉及多通道ADC采集和DMA读取的实验,说明实际应用中可能需要同时处理多个通道的信号。 接下来,我需要整理ADC通道的基本原理。每个ADC通道对应一个输入引脚,可以独立采集模拟信号。多通道ADC通过复用同一转换器,分时处理不同通道,这样可以节省硬件资源。然后,应用方面,多通道ADC常用于需要同时监测多个传感器的场景,比如温度、压力等工业控制系统,或者医疗设备中的多参数监测。 同时,用户的问题中还提到DMA读取,这在多通道采集时非常重要,因为DMA可以减少CPU的负担,提高效率。需要解释DMA的作用,即直接内存访问,使得数据转换后自动传输到内存,不需要CPU干预。 此外,还需要考虑ADC的分辨率、采样率、通道间串扰等参数对性能的影响。例如,分辨率决定了转换精度,采样率影响信号还原的准确性。在多通道情况下,切换通道时可能会有延迟,需要合理设计采样顺序和时间。 可能用户还会想知道如何选择ADC通道的数量,或者如何在实际项目中配置多通道ADC。这部分可以结合引用中的编程实战,简要说明配置步骤,比如初始化ADC、设置DMA、配置扫描模式等。 最后,生成相关问题时要覆盖原理、配置、优化和应用场景,确保问题能够帮助用户进一步深入学习。例如,多通道ADC如何避免通道干扰,或者DMA在其中的具体配置方法等。 检查是否所有数学公式都正确使用$...$,比如采样定理中的Nyquist频率,可能需要写成$f_s \geq 2f_{max}$。独立公式如转换公式需要用$$...$$,但用户问题中没有涉及复杂公式,所以可能不需要。但如果有的话,比如量化公式,需要正确应用格式。 总结回答结构:先介绍ADC的基本原理,再详细说明通道的概念,多通道的工作方式,接着是应用场景,最后提到DMA的作用。确保引用标记正确,比如在采样保持阶段引用[2],在多通道实验部分引用[1]。</think>### ADC通道原理及应用 #### 1. ADC通道基本原理 ADC(数模转换器)的核心功能是将连续模拟信号转换为离散数字信号,其过程包含四个步骤:**采样**、**保持**、**量化**和**编码**[^2]。 - **采样**:以固定时间间隔捕获模拟信号的瞬时值,需满足$f_s \geq 2f_{max}$(奈奎斯特定理)以避免混叠。 - **保持**:在转换期间维持采样值稳定。 - **量化**:将连续幅值映射为有限离散电平。 - **编码**:将量化值转为二进制数字输出。 **ADC通道**指独立的模拟信号输入路径,每个通道对应一个物理引脚。多通道ADC通过内部复用器分时切换不同通道,共享同一转换器资源。 #### 2. 多通道ADC的工作模式 - **扫描模式**:依次自动切换多个通道进行转换。 - **间断模式**:按需启动部分通道转换。 - **DMA传输**:转换完成后通过DMA直接将数据存入内存,减少CPU负载[^1]。例如,在STM32中,多通道ADC+DMA可实现高效实时采集。 #### 3. 应用场景 1. **工业控制**:同时监测温度、压力、流量等多个传感器信号。 2. **医疗设备**:心电图(ECG)、血氧等多参数同步采集。 3. **通信系统**:多路信号调制解调。 4. **电池管理系统**:多节电池电压监测。 #### 4. 关键参数与优化 - **分辨率**:如12位ADC对应$2^{12}=4096$个量化电平。 - **采样率**:影响信号还原精度,需根据信号带宽选择。 - **通道间串扰**:通过硬件布局优化和软件滤波降低干扰。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

必胜的思想钢印

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值