STM32—ADC数模转换

目录

一、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数模转换介绍

ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。

STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道。STM32F103C8T6 使用的是12位逐次逼近型ADC。

1.1、逐次逼近型ADC结构

 上图中电压大小比较框图中,输入电压的待测电压与DAC的输出电压进行比较,当待测电压比DAC输出电压大时,调大DAC输出电压,并再次进行判断,当待测电压比DAC输出电压小时,调小DAC输出电压。直到ADC输出电压与外部通道的输入电压近视相等。这样DAC的编码数据就是外部输入电压的编码数据。该电压调节过程即为逐次逼近过程,一般采取二分法。

1.2、ADC框图

 对于上图中ADCCLK重度额来自ADC预分频器,其来源是RCC时钟,见下图

 由于ADC最大分频为14MHz,所以ADC预分频智能选取6、8。

1.3、ADC基本配置结构

二、相关函数介绍

2.1、ADC时钟分频器时钟函数

//定义ADC时钟分频器。这个钟是APB2时钟(PCLK2),即下图部分

RCC_ADCCLKConfig(uint32_t RCC_PCLK2)

2.2、ADC函数

2.2.1、ADC_DeInit()

//将ADCx外设寄存器初始化为其默认重置值

ADC_DeInit(ADC_TypeDef* ADCx);

2.2.2、ADC_Init()

//根据指定的参数初始化ADCx外设

 ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)

2.2.3、ADC_StructInit()

//用默认值填充每个ADC_InitStruct成员。

ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);

2.2.4、ADC_Cmd()

//启用或禁用指定的ADC外设

ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState)

 2.2.5、ADC_DMACmd()

/./启用或禁用指定的ADC DMA请求,使用DMA转运数据,需要用到此函数

ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState)

2.2.6、ADC_ITConfig()

//启用或禁用指定的ADC中断。

ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState)

2.2.7、结尾部分

2.2.7.1、ADC_GetFlagStatus()

//检查是否设置了指定的ADC标志。(获取标志位状态,通过判断标志位转态从而判断转换是否结束)

ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)

参数2:可以是下图所对应的值

 AWD、模拟看门狗标志位;EOC:规则组转换完成标志位;JOEC:注入组转换完成标志位,JSTRE:注入组开始转换标志位;STRT:规则组开始转换标志位。

2.2.7.2、ADC_ClearFlag()

//清除ADCx的挂起标志。(清除标志位)

ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)

2.2.7.3、ADC_GetITStatus()

//获取中断状态

ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT)

2.2.7.4、ADC_ClearITPendingBit()

//清除中断挂起位置
ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT)

 2.2.8、校准函数

2.2.8.1、ADC_ResetCalibration()

//重置所选ADC校准寄存器。(即复位校准)

ADC_ResetCalibration(ADC_TypeDef* ADCx)

2.2.8.2、ADC_GetResetCalibrationStatus()

//获取所选ADC复位校准寄存器状态。(获取复位校准状态)

ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx)

2.2.8.3、ADC_StartCalibration()

//启动选定的ADC校准过程。(开始校准)

ADC_StartCalibration(ADC_TypeDef* ADCx)

2.2.8.4、ADC_GetCalibrationStatus()

//获取所选ADC校准状态。(获取开始校准状态)

ADC_GetCalibrationStatus(ADC_TypeDef* ADCx)

2.2.9、触发控制函数

2.2.9.1、ADC_SoftwareStartConvCmd()

//启用或禁用所选ADC软件启动转换。(软件触发)

ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)

2.2.9.2、ADC_GetSoftwareStartConvStatus()

//获取所选ADC软件启动转换状态。(注:不可调用该函数用于判断转换是否结束。因为该函数是给SWSTART位置1,以开始转换,器返回值为SWSTART的状态,所以一般不会使用到该函数)。若需要获取转换结束标志,可以使用(2.2.7.1)的函数

ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx)

2.2.10、ADC_规则组通道配置函数

2.2.10.1、ADC_RegularChannelConfig()

//为所选ADC配置其对应的常规通道,即为下图的每个序列填写指定通道。

ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)

2.2.11、ADC_获取转换值 

2.2.11.1、ADC_GetConversionValue()

//返回常规通道的最后一次ADCx转换结果数据。即获取AD转换的数据寄存器,读取转换结果。

ADC_GetConversionValue(ADC_TypeDef* ADCx)

2.2.11.2、ADC_GetDualModeConversionValue()

//在双模式下返回最后的ADC1和ADC2转换结果数据。

ADC_GetDualModeConversionValue(void)

2.3、看门狗函数

2.3.1、ADC_AnalogWatchdogCmd()

 //在单个/所有常规或注入通道上启用或禁用模拟看门狗(是否启动看门狗)

ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog)

2.3.2、ADC_AnalogWatchdogThresholdsConfig()

//设置模拟看门狗的高低阈值。

ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold)

2.3.3、ADC_AnalogWatchdogSingleChannelConfig()

//配置模拟看门狗保护单通道,即配置看门的通道
ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel)

三、单通道的单次转换&非扫描模式下的AD_Init()函数配置

配置内容参考下图内容

3.1、开启时钟&ADCCLK配置

    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总线上。

3.2、GPIO口配置

    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特有的输入模式,避免外界的干扰。

3.3、规则组内容配置

规则组使用的函数参见(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);

3.4、用结构体初始化ADC

需要用到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

3.5、校准 

    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()

等待校准完成函数与复位校准函数用法类似。

 3.6、开始转换&获取转换结果

流程参照下图

 首先软件触发,然后等待转换完成,即等待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转换的结果
}

 3.7、最终现象代码

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);
	}
}

3.8、AD.c 文件完整代码

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转换的结果
}

 五、AD的多通道使用

目前是基于前文所使用的单次转换&非扫描模式来实现多通道,(一般情况是使用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);
	}
}

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

橘洲青年

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

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

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

打赏作者

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

抵扣说明:

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

余额充值