STM32学习笔记(7_2)- ADC模数转换器代码

无人问津也好,技不如人也罢,都应静下心来,去做该做的事。

最近在学STM32,所以也开贴记录一下主要内容,省的过目即忘。视频教程为江科大(改名江协科技),网站jiangxiekeji.com

本期开始介绍STM32的ADC——模数转换器,对于GPIO来说,它只能读取引脚的高低电平,要么是高电平,要么是低电平,只有两个值。而使用ADC,我们就可以对这个高电平和低电平之间的任意电压进行量化,最终用一个变量来表示,读取这个变量,就可以知道引脚的具体电压到底是多少了。

所以ADC其实就是一个电压表,把引脚的电压值测出来,放在一个变量里。这就是ADC的作用。

CTRL+ALT+空格或CTRL+空格:提示代码,代码自动补全

ADC常用函数

RCC_ADCCLKConfig:这个函数是用来配置ADCCLK分频器的,'它可以对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK。这个函数在RCC库函数里。

void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);

下面的函数在ADC.h里

ADC_DeInit恢复缺省配置、ADC_Init初始化、ADC_StructInit结构体初始化

void ADC_DeInit(ADC_TypeDef* ADCx);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);

 ADC_Cmd:这个是用于给ADC上电的,就是ADC基本结构图里的开关控制。

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

DMA_Cmd:这个是用于开启DMA输出信号的,如果使用DMA转运数据,那就得调用这个函数。

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

ADC_ITConfig:中断输出控制,用于控制某个中断,能不能通往NVIC。

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

接下来四个函数分别是复位校准、获取复位校准状态、开始校准、获取开始校准状态,这是用于控制校准的函数。我们在ADC初始化完成之后,依次调用就行了。

void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);

ADC软件开始转换控制,这个就是用于软件触发的函数了。调用一下就能进行软件触发控制,我们这个代码目前使用软件触发。

void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

ADC获取软件开始转换状态,这个函数的返回值跟转换是否结束,毫无关系。这个函数其实没啥用,我们一般不用,不要被他误导了。

FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);

ADC_GetFlagStatus:这个函数才是判断是否转换结束的。获取标志位状态,然后参数给EOC的标志位,判断EOC标志位是不是置1了。如果转换结束,EOC标志位置1,然后调用这个函数,判断标志位。这样才是正确的判断转换是否结束的方法。

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

然后下面这两个函数,是用来配置间断模式的。

ADC_DiscModeChannelCountConfig:每隔几个通道间断一次。

ADC_DiscModeCmd:是不是启用间断模式。

void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

ADC_RegularChannelConfig(ADCx,你想指定的通道,序列几的位置,指定通道的采样时间):ADC规则组通道配置,这个函数比较重要,它的作用就是给序列的每个位置填写指定的通道,就是填写点菜菜单的过程。

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

ADC外部触发转换控制,就是是否允许外部触发转换。

void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

ADC获取转换值,这个函数也比较重要。获取AD转换的数据寄存器,读取转换结果就要使用这个函数。

uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

 ADC获取双模式转换值,这个是双ADC模式读取转换结果的函数,暂时不用。

uint32_t ADC_GetDualModeConversionValue(void);

到这里,以上这些函数是对ADC的一些基本功能和规则组的配置。

接下来这些函数里面都带了一个Injected,就是注入组的意思,注入组本期也不用。

void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv);
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx);
void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length);
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset);
uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);

这三个函数就是对模拟看门狗进行配置的,第一个是是否启动模拟看门狗,第二个是配置高低阈值,第三个是配置看门的通道。

void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);

ADC温度传感器、内部参考电压控制,这个是用来开启内部的两个通道的,如果你要用这两个通道,那得调用这个函数,开启一下。要不然是读不到正确的结果的,这个注意一下。

void ADC_TempSensorVrefintCmd(FunctionalState NewState);

那最后四个,获取标志位状态、清除标志位、获取中断状态、清除中断挂起位,这些函数也是常用函数了。

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);

       

 ADC基本结构

左边是输入通道,16个GPIO口,外加两个内部的通道。然后进入AD转换器,AD转换器里有两个组,一个是规则组,一个是注入组,规则组最多可以选中16个通道,注入组最大可以选择4个通道。然后转换的结果可以存放在AD数据寄存器里,其中规则组只有1个数据寄存器,注入组有4个。然后下面这里有触发控制, 提供了开始转换这个START信号,触发控制可以选择软件触发和硬件触发。硬件触发主要是来自于定时器,当然也可以选择外部中断的引脚。右边这里是来自于RCC的ADC时钟CLOCK,ADC逐次比较的过程就是由这个时钟推动的。然后上面,可以布置一个模拟看门狗用于监测转换结果的范围,如果超出设定的阈值,就通过中断输出控制,向NVIC申请中断。另外,规则组和注入组转换完成后会有个EOC信号,它会置一个标志位,当然也可以通向NVIC。最后右下角这里还有个开关控制,在库函数中,就是ADC_Cmd函数,用于给ADC上电的。

 程序现象

共有两个程序

第一个程序是AD单通道

在面包板上接一个电位器,用这个电位器产生一个0-3.3V连续变化的模拟电压信号,接到STM32的PA0,之后用STM32内部的ADC读取电压数据,显示在屏幕上。这里屏幕第一行显示的是AD转换后的原始数据,第二行是经过处理后实际的电压值。往左拧,AD值减小,电压值也减,;AD值最小是0,对应的电压就是0V;往右拧,AD值变大,对应电压值也变大。STM32的ADC是12位的,所以AD结果最大值是4095,也就是2^12-1,对应电压是3.3V。

那这里AD值的未尾会有些抖动,这是正常的波动。如果你想对这个值进行判断,再执行一些操作,比如光线的AD值小于某一阈值,就开灯;大于某一阈值,就关灯。可能会存在这种情况,比如光线逐渐变暗,AD值逐渐变小,但是由于波动,AD值会在判断阈值附近来回跳变。这会导致输出产生抖动,来回开灯关灯开灯关灯,是吧。

那如何避免?这个可以使用迟滞比较的方法来完成,设置两个阈值,低于下阈值时,开灯;高于上阈值时,才关灯,这就可以避免输出抖动的问题了。还可以采取滤波的方法,让AD值平滑一些,比如均值滤波,就是读10个或20个值,取平均值,作为滤波的AD值。或者还可以裁剪分辨率,把数据的尾数去掉,也能减少波动。

接线图

根据引脚定义表,PA0到PB1这10个脚是ADC的10个通道,可以10个任意选一个接。

初始化步骤

根据ADC基本结构图来一步步配置即可。

第一步,开启RCC时钟,包括ADC和GPIO的时钟。另外这里ADCCLK的分频器,也需要配置一下。

	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz

第二步,配置GPIO,把需要用的GPIO配置成模拟输入的模式。

	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0引脚初始化为模拟输入

第三步,配置这里的多路开关,把左边的通道接入到右边的规则组列表里。这个过程就是我们之前说的点菜,把各个通道的菜,列在菜单里。使用单次转换、非扫描模式。

	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0

第四步,就是配置ADC转换器了。在库函数里,是用结构体来配置的,可以配置AD转换器和AD数据寄存器电路的参数。包括ADC是单次转换还是连续转换、扫描还是非扫描、有几个通道。触发源是什么,数据对齐是左对齐还是右对齐。这一大批参数,用一个结构体配置就可以了。

	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	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,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1

如果你需要模拟看门狗,那会有几个函数用来配置阈值和监测通道的。

如果你想开启中断,那就在中断输出控制里用ITConfig函数开启对应的中断输出,然后再在NVIC里,配置一下优先级,这样就能触发中断了。不过这一块,模拟看门狗和中断,我们本期暂时不用。

第五步,就是开关控制,调用一下ADC_Cmd函数,开启ADC。这样ADC就酒配置完成了,就能正常工作了。当然,在开启ADC之后,根据手册里的建议。我们还可以对ADC开启校准,减小误差。

	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);

在ADC工作时,如果想要软件触发转换,那会有函数可以触发。如果想读取转换结果,那也会有函数可以读取结果。

/**
  * 函    数:获取AD转换的值
  * 参    数:无
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束,此程序这里大概会等待5.6us
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

此程序的转换时间:72MHz/6 × (55.5+12.5)= 5.6us

代码展示

在Hardware文件夹下新建AD_Init.h、.c文件,把ADC驱动函数封装起来。

main函数

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

uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	AD_Init();				//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "Voltage:0.00V");
	
	while (1)
	{
		ADValue = AD_GetValue();					//获取AD转换的值
		Voltage = (float)ADValue / 4095 * 3.3;		//将AD值线性变换到0~3.3的范围,表示电压
		
		OLED_ShowNum(1, 9, ADValue, 4);				//显示AD值
		OLED_ShowNum(2, 9, Voltage, 1);				//显示电压值的整数部分
		OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);	//显示电压值的小数部分
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

AD.h文件

#ifndef __AD_H
#define __AD_H

void AD_Init(void);
uint16_t AD_GetValue(void);

#endif

AD.c文件

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0引脚初始化为模拟输入
	
	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	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,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:无
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

第二个是AD多通道

在第一个实验的基础上接多三个模块,把它们的AO(模拟电压输出端),分别接在了A1、A2、A3脚,加上刚才的电位器,总共4个输出通道。然后测出来的4个AD数据分别显示在屏幕上。

第一个电位器,看第一行的AD0。往右拧增大,和第一个程序一样;光敏电阻,看第二行的AD1,遮挡一下光敏电阻,光线减小,AD值增大;热敏电阻,看第三行的AD2,用手热一下这个热敏电阻,温度升高,AD值减小;最后是反射式红外传感器,手靠近,有反光,AD值减小。

接线图

同样,这些GPIO口也是可以在PA0~PB1之间任意选择的。这里选择前四个GPIO口。

初始化步骤

如果想要用扫描模式实现多通道,最好要配合DMA来实现。

这里使用单次转换、非扫描模式,只需要在每次触发转换之前,手动更改一下列表第一个位置的通道就行了。比如第一次转换,先写入通道0,之后触发、等待、读值;第二次转换,再把通道0改成通道1,之后触发、等待、读值;第三次转换,再先改成通道2,等等等等。

在AD单通道的代码上稍加修改即可,把规则组通道配置代码移动到

	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0

 以下函数里,并添加多一个参数uint8_t  ADC_Channel

/**
  * 函    数:获取AD转换的值
  * 参    数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);	//在每次转换前,根据函数形参灵活更改规则组的通道1
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

代码展示

现在就是依次启动4次转换,并且在转换之前,指定了转换的通道。每次转换完成之后,把结果分别存在4个数据里,最后显示一下。这就是使用单次转换非扫描的模式,实现AD多通道的方法。

main函数

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

uint16_t AD0, AD1, AD2, AD3;	//定义AD值变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	AD_Init();					//AD初始化
	
	/*显示静态字符串*/
	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);		//单次启动ADC,转换通道0
		AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1
		AD2 = AD_GetValue(ADC_Channel_2);		//单次启动ADC,转换通道2
		AD3 = AD_GetValue(ADC_Channel_3);		//单次启动ADC,转换通道3
		
		OLED_ShowNum(1, 5, AD0, 4);				//显示通道0的转换结果AD0
		OLED_ShowNum(2, 5, AD1, 4);				//显示通道1的转换结果AD1
		OLED_ShowNum(3, 5, AD2, 4);				//显示通道2的转换结果AD2
		OLED_ShowNum(4, 5, AD3, 4);				//显示通道3的转换结果AD3
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

AD.h文件

#ifndef __AD_H
#define __AD_H

void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);

#endif

AD.c文件

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	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);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
	
	/*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*/
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	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,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);	//在每次转换前,根据函数形参灵活更改规则组的通道1
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: en.stm32cubemx-win_v6-3-0_v6.3.0.zip是一个软件包,主要用于STM32微控制器的开发。该软件包包含了一个名为STM32CubeMX的集成开发环境,可以帮助开发人员快速生成基于STM32芯片的项目代码。此外,该软件包还包含了许多现成的设备驱动程序、库以及示例代码,大大简化了STM32开发和调试的流程。最新的版本为6.3.0,新增了许多新的功能和设备支持,可以更好地满足不同项目的需求。总之,en.stm32cubemx-win_v6-3-0_v6.3.0.zip是一款非常实用的软件包,值得STM32开发人员下载和使用。 ### 回答2: en.stm32cubemx-win_v6-3-0_v6.3.0.zip是一个STM32CubeMX软件的安装文件。STM32CubeMX是STMicroelectronics公司开发的一个集成式软件工具,用于在编写STM32微控制器的应用程序时生成初始化代码和配置文件。该软件可以使嵌入式开发人员节省大量时间,减少错误,并提高生产效率。 安装这个软件可以使嵌入式开发人员更快速地初始化STM32系列微控制器,减少测试,编程和生产时间。该软件提供可视化的图形用户界面和许多与STM32芯片系列相关联的配置选项,使得使用STM32系列芯片开发嵌入式系统变得更加便捷和高效。 总之,en.stm32cubemx-win_v6-3-0_v6.3.0.zip是一个非常有用的工具,适用于嵌入式开发人员,可以优化他们的工作流程,提高编程效率。 ### 回答3: en.stm32cubemx-win_v6-3-0_v6.3.0.zip是一个软件包,它包含了ST公司的STM32CubeMX软件的最新版本6.3.0。STM32CubeMX是一款用于STM32单片机代码生成工具,它可以帮助开发人员快速地创建基于STM32系列芯片的应用程序。 这个软件包适用于Windows操作系统。通过下载并安装这个软件包,开发人员可以轻松地创建STM32应用程序的开发环境,并使用STM32CubeMX的图形界面来进行代码的生成和配置。此外,这个软件包还包含了一些STM32CubeMX支持的设备的驱动程序和固件库。 使用STM32CubeMX能够大大降低STM32单片机应用程序的开发难度和开发成本,使得开发人员可以更加专注于应用程序的业务逻辑上,从而提高开发效率和软件的质量。所以,如果你需要开发基于STM32系列芯片的应用程序,en.stm32cubemx-win_v6-3-0_v6.3.0.zip是一个非常有用的工具包。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值