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;		//模式,选择独立模式,即单独使用ADC1ADC_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的数,在非扫描模式下,只能是1ADC_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.6usreturn 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_Hvoid 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;		//模式,选择独立模式,即单独使用ADC1ADC_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的数,在非扫描模式下,只能是1ADC_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);	//在每次转换前,根据函数形参灵活更改规则组的通道1ADC_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,转换通道0AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1AD2 = AD_GetValue(ADC_Channel_2);		//单次启动ADC,转换通道2AD3 = AD_GetValue(ADC_Channel_3);		//单次启动ADC,转换通道3OLED_ShowNum(1, 5, AD0, 4);				//显示通道0的转换结果AD0OLED_ShowNum(2, 5, AD1, 4);				//显示通道1的转换结果AD1OLED_ShowNum(3, 5, AD2, 4);				//显示通道2的转换结果AD2OLED_ShowNum(4, 5, AD3, 4);				//显示通道3的转换结果AD3Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间}
}

AD.h文件

#ifndef __AD_H
#define __AD_Hvoid 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;		//模式,选择独立模式,即单独使用ADC1ADC_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的数,在非扫描模式下,只能是1ADC_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);	//在每次转换前,根据函数形参灵活更改规则组的通道1ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/288153.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

稀碎从零算法笔记Day28-LeetCode:零钱兑换

前言:鸽了好多天了哈哈哈,虽然C站没更但是LC还是坚持刷的,任重道远啊!(可恶的寝室熄灯) 题型:动态规划 链接:322. 零钱兑换 - 力扣(LeetCode) 来源:LeetCode 题目描述…

五五复制模式:电商营销新策略,轻松打造百万用户平台

你是否曾感受到平台发展的瓶颈,渴望找到一种有效的方式,快速吸引用户,推动平台裂变,进而实现百万用户的规模?今天,我要向你介绍一种创新的商业模式——五五复制模式,它或许能为你带来全新的启示…

Chrome 插件各模块之间的消息传递

Chrome 插件各模块之间的消息传递 一、消息传递 1. 消息传递分类 Chrome 插件的 Action、Background 和 content_script 三个模块之间的信息传输插件和插件之间的信息传输网页向插件进行信息传输与原生应用进行消息传递 2. 消息传递 API runtime API runtime.sendMessage(…

Swift知识点(二)

6. 闭包表达式与闭包 闭包表达式(Closure Expression) 闭包表达式是一种在简短行内就能写完闭包的语法 也就是,闭包表达式,只是一种简洁、快速实现闭包的语法 Swift 的闭包表达式拥有简洁的风格,鼓励在常见场景中实现…

由浅到深认识Java语言(11):封装

该文章Github地址:https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.c…

MySQL中的日历/时间/时间戳

一,日历 MySQL 使用通常所说的 proleptic 阳历。 每个将日历由朱利安改为阳历的国家在改变日历期间都不得不删除至少10天。 为了了解其运作,让我们看看1582年10月,这是由朱利安日历转换为阳历的第一次: 周一 周二 周三 周四 周五 周六…

单臂路由和三层交换机

目录 一.单臂路由 1.单臂路由的工作原理 2.单臂路由的配置 2.1画出拓扑图 2.2配置PC 2.3配置交换机 2.4配置路由器 2.5测试 二.三层交换机 1.三层交换机的概述 2.三层交换机的配置 2.1画出拓扑图 2.2配置PC 2.3配置二层交换机 2.4配置三层交换机 2.5测试 3.拓展 三.总结 一.…

git提交和回退

目录 一. git 提交二. git commit 后准备回退,尚未 git push三. git add 添加多余文件 撤销操作四. 更改 Git commit 的默认编辑器五. 撤销某个commit的变更六. 回退到之前的commit状态总结: 一. git 提交 git pull # 更新代码 git status # 查看代码状…

ITES | 深圳工业展正运动重磅产品即将亮相

■展会名称: 第二十五届深圳国际工业制造技术及设备展览会(以下简称“深圳工业展”) ■展会日期 2024年3月28日-31日 ■展馆地点 中国深圳国际会展中心(宝安) ■展位号 9号馆F04 2024年深圳工业展(ITES)将于3月28日至31日在深圳宝安国…

springboot多模块

一、demo 1、创建父项目 首先使用 Spring Initializr 来快速创建好一个Maven工程。然后删除无关的文件,只需保留pom.xml 文件。 (1)new Project -> spring initializr快速构建SpringBoot,artifactId为springbootmodules&…

SpringBoot+ElasticSearch实现文档内容抽取、高亮分词、全文检索

需求 产品希望我们这边能够实现用户上传PDF、WORD、TXT之内得文本内容,然后用户可以根据附件名称或文件内容模糊查询文件信息,并可以在线查看文件内容。 一、环境 项目开发环境: 后台管理系统springbootmybatis_plusmysqles 搜索引擎&#…

PTA L2-037 包装机

一种自动包装机的结构如图 1 所示。首先机器中有 N 条轨道,放置了一些物品。轨道下面有一个筐。当某条轨道的按钮被按下时,活塞向左推动,将轨道尽头的一件物品推落筐中。当 0 号按钮被按下时,机械手将抓取筐顶部的一件物品&#x…

JVM第八讲:GC - Java 垃圾回收基础知识

GC - Java 垃圾回收基础知识 本文是JVM第八讲, Java 垃圾回收基础知识。垃圾收集主要是针对堆和方法区进行;程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失&#xff0…

最“原始”的收音机长啥样?

同学们大家好,今天我们继续学习杨欣的《电子设计从零开始》,这本书从基本原理出发,知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例,可以说是全面系统地介绍了电子设计所需的知识…

hadoop基本概念

一、概念 Hadoop 是一个开源的分布式计算和存储框架。 Hadoop 使用 Java 开发,所以可以在多种不同硬件平台的计算机上部署和使用。其核心部件包括分布式文件系统 (Hadoop DFS,HDFS) 和 MapReduce。 二、HDFS 命名节点 (NameNode) 命名节点 (NameNod…

k8s入门到实战(十四)—— Helm详细介绍及使用

Helm 使用 Helm 是一个 k8s 应用的包管理工具,类似于 Ubuntu 的 APT 和 CentOS 中的 YUM。 Helm 使用 chart 来封装 k8s 应用的 yaml 文件,我们只需要设置自己的参数,就可以实现自动化的快速部署应用。 Helm 通过打包的方式,支…

Nacos 配置管理-应用于分布式系统

** Nacos 配置管理-应用于分布式系统 ** 目录: 一、Nacos 配置管理-应用于分布式系统-微服务创建 1.1 发布配置 ( nacos-1.1.3 ) 1.2 打开 idea 创建一个父 Maven 工程 nacos_config 工程,和两个子模块(service1, service2 )…

MySQL进阶——锁

锁 概述 全局锁 表级锁 行级锁 概述 同Java中的锁。目的是为了保证数据一致性、完整性,提高并发安全、控制访问顺序。 分类 在MySQL中,根据锁的粒度分,分为以下3种: 全局锁:锁定数据库种的所有表 表级锁&#…

React中 类组件 与 函数组件 的区别

类组件 与 函数组件 的区别 1. 类组件2. 函数组件HookuseStateuseEffectuseCallbackuseMemouseContextuseRef 3. 函数组件与类组件的区别3.1 表面差异3.2 最大不同原因 1. 类组件 在React中,类组件就是基于ES6语法,通过继承 React.component 得到的组件…

后端常问面经之Java集合

HashMap底层原理 HashMap的数据结构: 底层使用hash表数据结构,即数组和链表或红黑树 当我们往HashMap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标 存储时,如果出现hash值相同的key,此…