ADC
概念
众所周知,GPIO只能读入高电平或者低电平,那如果现有一个模拟量,该如何读取呢,比如电压的范围是0~3.3v,如何获取电压的值。就需要ADC(Analog-Digital Converter)了。ADC可以将引脚上连续变化的模拟电压转变为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。
stm32f103自带的是12位逐次逼近型ADC,1us转换时间,这样最多可以输出0-4095的数字量。
框图
蓝色框,表示ADC有18个数据来源通道:ADCx_IN0~15、和内部的温度传感器、Verfint。
绿色框,表示注入组最多可以同时转化4个(所谓同时,其实是依次)。规则组最多可以同时转化16个数据。
橙色框:需要注意的是,注入通道数据寄存器有4个,转化好的数据一人一个;而规则通道数据寄存器只有一个,16个转好的数据共用一个。
蓝色箭头:ADC转化的触发源可以如图选择:外部中断触发、TIM…触发、内部软件触发。
ADC转化模式和扫描模式
**单次转化 非扫描模式:**只转化一次,就停止转化;不管配置几个数据源,只转化第一个,发出EOC信号。
**单次转化 扫描模式:**只转化一次,就停止转化;配置多个数据源,会依次转化完,然后发出EOC信号。
**连续转化 非扫描模式:**本轮转化结束后自动开始下一轮;不管配置几个数据源,只转化第一个,发出EOC信号。
**连续转化 扫描模式:**本轮转化结束后自动开始下一轮:配置多个数据源,会依次转化完,然后发出EOC信号。
单通道模式
流程:
- 初始化GPIO 模拟输入模式
- 设置ADC时钟
RCC_ADCCLKConfig()
- 设置ADC
ADC_Init()
- 设置规则组
ADC_RegularChannelConfig
- 使能ADC
- 校准(4个函数)
- 触发,开始转换
代码
void AD_Init()
{// PA0 引脚初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;GPIO_Init(GPIOA, &GPIO_InitStruct);//ADC时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADC内部时钟,PCLK2意为APB2总线时钟// 设置ADC ADC初始化ADC_DeInit(ADC1); //复位ADC1 ADC_InitTypeDef ADC_InitStruct;ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC独立模式ADC_InitStruct.ADC_ScanConvMode =DISABLE;//非扫描(因为只有一个通道,不需要扫描)ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//连续转化ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//右对齐数据ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发 NONEADC_InitStruct.ADC_NbrOfChannel = 1; //要配置几个通道,这里是一个ADC_Init(ADC1, &ADC_InitStruct);// 配置规则组 ADC1 通道0的优先级是1,采样时间1.5 如果是多个通道,就多次配置该函数ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_1Cycles5);ADC_Cmd(ADC1, ENABLE);// ADC校准ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1));// ADC软件触发转换ADC_SoftwareStartConvCmd(ADC1, ENABLE);}//获取ADC转化后的原始数值
uint16_t AD_GetValue()
{// 获取ADC的EOC标志位,转换完成置SETwhile(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));// 获取值,读ADC的DR寄存区,读完后,会自动降EOC置RESETreturn ADC_GetConversionValue(ADC1);
}
注:RCC时钟不仅要开启GPIO和ADC的,还要配置ADC内部的时钟,这个时钟来自于APB2总线时钟。
多通道模式(手动,非DMA)
如上就可以被称为多通道,四个模拟量接入PA0-PA3,
这是我们设置扫描模式,使用ADC1CH0-CH3
这里的四个通道有数据后,都往DR寄存器中写数据,如果不适用DMA转存,就会被覆盖。当四个通道的数据都搬运完后,才会发出一个EOC信号。
因为本节不涉及DMA,因此推荐另一种手动方法,使用非连续非扫描模式。该模式下,依次转化一个通道的数据,然后停止。
在该模式下,每次我们读取值之前,都设置将要读取的通道设置,然后触发,等待EOC,读值。与单通道模式相似。
//轮流多次配置规则组已达到多通道效果
uint16_t AD_GetValue_CHS(uint8_t ADC_Channel)
{// 配置规则组 ADC1 通道0的优先级是1,采样时间1.5 如果是多个通道,就多次配置该函数ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_1Cycles5);// ADC软件触发转换ADC_SoftwareStartConvCmd(ADC1, ENABLE);// 获取ADC的EOC标志位,转换完成置SETwhile(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));// 获取值,读ADC的DR寄存区,读完后,会自动降EOC置RESETreturn ADC_GetConversionValue(ADC1);
}
根据形参选择通道