目录
一、ADC数模转换介绍
1.1、逐次逼近型ADC结构
1.2、ADC框图
1.3、ADC基本配置结构
二、相关函数介绍
2.1、ADC时钟分频器时钟函数
编辑
2.2、ADC函数
2.2.1、ADC_DeInit()
2.2.2、ADC_Init
2.2.3、ADC_StructInit()
2.2.4、ADC_Cmd()
2.2.5、ADC_DMACmd()
2.2.6、ADC_ITConfig()
2.2.7、结尾部分
2.2.7.1、ADC_GetFlagStatus()
2.2.8、校准函数
2.2.8.1、ADC_ResetCalibration()
2.2.8.2、ADC_GetResetCalibrationStatus()
2.2.8.3、ADC_StartCalibration()
2.2.8.4、ADC_GetCalibrationStatus()
2.2.9、触发控制函数
2.2.9.1、ADC_SoftwareStartConvCmd()
2.2.9.2、ADC_GetSoftwareStartConvStatus()
2.2.10、ADC_规则组通道配置函数
2.2.10.1、ADC_RegularChannelConfig()
2.2.11、ADC_获取转换值
2.2.11.1、ADC_GetConversionValue()
2.2.11.2、ADC_GetDualModeConversionValue()
2.3、看门狗函数
2.3.1、ADC_AnalogWatchdogCmd()
2.3.2、ADC_AnalogWatchdogThresholdsConfig()
2.3.3、ADC_AnalogWatchdogSingleChannelConfig()
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。
STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道。STM32F103C8T6 使用的是12位逐次逼近型ADC。
上图中电压大小比较框图中,输入电压的待测电压与DAC的输出电压进行比较,当待测电压比DAC输出电压大时,调大DAC输出电压,并再次进行判断,当待测电压比DAC输出电压小时,调小DAC输出电压。直到ADC输出电压与外部通道的输入电压近视相等。这样DAC的编码数据就是外部输入电压的编码数据。该电压调节过程即为逐次逼近过程,一般采取二分法。
对于上图中ADCCLK重度额来自ADC预分频器,其来源是RCC时钟,见下图
由于ADC最大分频为14MHz,所以ADC预分频智能选取6、8。
//定义ADC时钟分频器。这个钟是APB2时钟(PCLK2),即下图部分
RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
//将ADCx外设寄存器初始化为其默认重置值
ADC_DeInit(ADC_TypeDef* ADCx);
//根据指定的参数初始化ADCx外设
ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)
//用默认值填充每个ADC_InitStruct成员。
ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
//启用或禁用指定的ADC外设
ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState)
/./启用或禁用指定的ADC DMA请求,使用DMA转运数据,需要用到此函数
ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState)
//启用或禁用指定的ADC中断。
ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState)
//检查是否设置了指定的ADC标志。(获取标志位状态,通过判断标志位转态从而判断转换是否结束)
ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)
参数2:可以是下图所对应的值
AWD、模拟看门狗标志位;EOC:规则组转换完成标志位;JOEC:注入组转换完成标志位,JSTRE:注入组开始转换标志位;STRT:规则组开始转换标志位。
//清除ADCx的挂起标志。(清除标志位)
ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)
//获取中断状态
ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT)
//清除中断挂起位置
ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT)
//重置所选ADC校准寄存器。(即复位校准)
ADC_ResetCalibration(ADC_TypeDef* ADCx)
//获取所选ADC复位校准寄存器状态。(获取复位校准状态)
ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx)
//启动选定的ADC校准过程。(开始校准)
ADC_StartCalibration(ADC_TypeDef* ADCx)
//获取所选ADC校准状态。(获取开始校准状态)
ADC_GetCalibrationStatus(ADC_TypeDef* ADCx)
//启用或禁用所选ADC软件启动转换。(软件触发)
ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
//获取所选ADC软件启动转换状态。(注:不可调用该函数用于判断转换是否结束。因为该函数是给SWSTART位置1,以开始转换,器返回值为SWSTART的状态,所以一般不会使用到该函数)。若需要获取转换结束标志,可以使用(2.2.7.1)的函数
ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx)
//为所选ADC配置其对应的常规通道,即为下图的每个序列填写指定通道。
ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
//返回常规通道的最后一次ADCx转换结果数据。即获取AD转换的数据寄存器,读取转换结果。
ADC_GetConversionValue(ADC_TypeDef* ADCx)
//在双模式下返回最后的ADC1和ADC2转换结果数据。
ADC_GetDualModeConversionValue(void)
//在单个/所有常规或注入通道上启用或禁用模拟看门狗(是否启动看门狗)
ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog)
//设置模拟看门狗的高低阈值。
ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold)
//配置模拟看门狗保护单通道,即配置看门的通道
ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel)
配置内容参考下图内容
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟,选择GPIOkou
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK=72MHz/6=12MHz 6分频
第一步首先就是要开启时钟,ADC和GPIO都是挂载在APB2总线上。
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//选择模拟输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
注意输入模式选择的是模拟输入,这是ADC特有的输入模式,避免外界的干扰。
规则组使用的函数参见(2.2.10)。
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
对于参数2是选择通道。通道可以是以下参数。(本例中使用的是通道0)
参数3属选择规则组序列器里面的次序。参数选择在0~16(对应着2.2.10.1的图所示的序列)
参数4是采样时间。需要快的转换选择小参数,需要稳定的转换选择大参数。
若想在其他徐序列写入其他通道可以通过修改参数2和参数3实现。采样时间可以不同
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,2,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,3,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,4,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,5,ADC_SampleTime_55Cycles5);
需要用到ADC_Init()函数。
该函数的结构体包含参数有:工作模式、扫描模式(连续扫描or菲连续扫描)、连续转换模式(单词转换or连续转换)、外部触发选择、数据对齐、通道数目(扫描模式下扫描通道的数量)。
其中外部触发对应的是(1.2图中硬件配置部分)
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//选择工作模式,独立or双ADC模式
ADC_InitStructure.ADC_ScanConvMode=DISABLE; //扫描模式,
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None; //外部触发转换选择
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐
ADC_InitStructure.ADC_NbrOfChannel= 1; //通道数目,在扫描模式下用到几个通道
ADC_Init(ADC1,&ADC_InitStructure);
ADC_Cmd(ADC1,ENABLE);//开启ADC
ADC_ResetCalibration(ADC1);//1、复位校准
while(ADC_GetResetCalibrationStatus(ADC1)==SET);//2、等待复位校准完成
ADC_StartCalibration(ADC1);//3、开始校准
while(ADC_GetCalibrationStatus(ADC1)==SET);//等待校准完成
3.5.1、ADC_GetResetCalibrationStatus()
由其函数定义可知该函数用于检查ADC(模数转换器)外设的控制寄存器2(CR2)中的RSTCAL位的状态。返回值是 ADC复位校准寄存器的新状态(SET或reset)。
其用法是软件将CR2的位3置1,硬件开始复位校准,复位校准完成后,位3有硬件清零。
为了实现复位等待完成,需要配合 while()函数,若未校准完成,即位3未被清零,则会一直在while()中循环。
3.5.2、ADC_GetCalibrationStatus()
等待校准完成函数与复位校准函数用法类似。
流程参照下图
首先软件触发,然后等待转换完成,即等待EOC标志位置1,最后读取ADC数据寄存器。
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//触发转换
while(ADC_GetFlagStatus(ADC1,ENABLE)==RESET);//注入组/规则组完成都会置1,RESET为未完成
return ADC_GetConversionValue(ADC1);//获取ADC转换的结果
}
uint16_t ADValue;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"ADValue:");
while(1)
{
ADValue=AD_GetValue();
OLED_ShowNum(1,10,ADValue,5);
}
}
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟,选择GPIOkou
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK=72MHz/6=12MHz 6分频
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//选择模拟输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitTypeDef GPIO_LED;
GPIO_LED.GPIO_Mode = GPIO_Mode_IPU;
GPIO_LED.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_LED.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOA, &GPIO_LED);
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//选择工作模式,独立or双ADC模式
ADC_InitStructure.ADC_ScanConvMode=DISABLE; //扫描模式,
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None; //外部触发转换选择
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐
ADC_InitStructure.ADC_NbrOfChannel= 1; //通道数目,在扫描模式下用到几个通道
ADC_Init(ADC1,&ADC_InitStructure);
ADC_Cmd(ADC1,ENABLE);//开启ADC
ADC_ResetCalibration(ADC1);//1、复位校准
while(ADC_GetResetCalibrationStatus(ADC1)==SET);//2、等待复位校准完成
ADC_StartCalibration(ADC1);//3、开始校准
while(ADC_GetCalibrationStatus(ADC1)==SET);//等待校准完成
}
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//触发转换
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);//注入组/规则组完成都会置1,RESET为未完成
return ADC_GetConversionValue(ADC1);//获取ADC转换的结果
}
首先,转换模式更改为 ENABLE 以此选择连续转换模式
其次,触发转换函数挪到ADC_Init()结尾。因为连续转换非扫描模式仅需要一次触发,内部ADC会一次接着一次的连续不断的队指定通道进行连续转换,转换结果会存放在数据寄存器。因此判断标志位也不需要了。
以下便是简化版的连续非扫描模式的ADC工作模式。
void ADC_Init()
{
ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//连续转换模式
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//触发转换
}
uint16_t AD_GetValue(void)
{
return ADC_GetConversionValue(ADC1);//获取ADC转换的结果
}
目前是基于前文所使用的单次转换&非扫描模式来实现多通道,(一般情况是使用DMA来实现多通道)。
工作原理:在每次触发转换前,手动更改列表第一个位置通道即可。
5.1、代码实现
基于前文(3.8、AD.c 文件完整代码)进行修改
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);//注入组/规则组完成都会置1,RESET为未完成
return ADC_GetConversionValue(ADC1);//获取ADC转换的结果
}
其中ADC_RegularChannelConfig()由ADC_Init()放到AD_GetValue()当中,是因为我们使用的是单次转换&非扫描模式来实现多通道,每一次转换结束后需要重新选择通道以实现多通道的功能。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO口也需要修改成所需要的GPIO口
最后就是在oled屏上读取数据
uint16_t ADValue;
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);
}
}