07:STM32----ADC模数转化器

2023-11-06

目录

1:简历

2:逐次逼近型ADC

3:ADC基本结构

 4:输入通道

5:规则组的4种转换模式 

1:单次转化,非扫描模式

2:连续转化,非扫描模式

3:单次转化,扫描模式

4:单次转化,扫描模式

6:触发控制

7:数据对齐

 8:转化时间

9:校准

10:ADC的硬件电路

A: AD单通道

1:连接图 

2:函数介绍

3:步骤

4:代码

 B:AD多通道

 1:连接图  

2:代码


1:简历

        ADC(Analog-Digital Converter)模拟-数字转换器

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

        12位逐次逼近型ADC,1us转换时间

        输入电压范围:0~3.3V,转换结果范围:0~4095

        18个输入通道,可测量16个外部(GPIO)和2个内部信号源(内部温度传感器和内部参考电压)

        规则组和注入组两个转换单元

        模拟看门狗自动监测

        输入电压范围 STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道

        12位逐次逼近型ADC,1us转换时间 : 

        分辨率 : 一般用多少位来表示,12位AD值,它的表示范围就是0~2^12-1,量化结果的范围是0~4095 , 位数越高,量化结果就越精细,对应分辨率就越高

        转换时间: 就是转化频率AD转换是需要花一小段时间的,这里1us就表示从AD转换开始到产生结果,需要花1us的时间。对应AD转换的频率就是1MHZ ,  这个就是STM32 ADC的最快转换频率

 规则组和注入组两个转换单元:

        普通的AD转换流程是,启动一次转换、读一次值 , 然后再启动、再读值,这样的流程 ; 

STM32的ADC就比较高级: 可以列一个组,一次性启动一个组,连续转换多个值 , 并且有两个组,一个是用于常规使用的规则组 ,一个是用于突发事件的注入组

2:逐次逼近型ADC

         ADC0809 : 独立的8位逐次逼近型ADC芯片
        EOC是End of Convert : 转换结束信号

        START : 是开始转换,给一个输入脉冲,开始转换

        CLOCK : 是ADC时钟,因为ADC内部是一步一步进行判断的
        REF+和VREF- : DAC的参考电压

3:ADC基本结构

 4:输入通道

5:规则组的4种转换模式 

在初始化ADC时配置的参数

单次转化 : ADC 执行一次转换,然后,ADC 停止

连续转化: 与单次转换不同的是,它在一次转换结束后不会停止 ,  而是立刻开始下一轮的转换,然后一直持续下去  ,  这样就只需要最开始触发一次,之后就可以一直转换了

扫描模式 : 在组中填几个通道几个通道就有效 , 填入多个的时候应避免覆盖的问题,  使用使用DMA

非扫描模式 : 这个组就只有第一个序列1的位置有效 , 这时选中一组的方式就退化为简单地选中一个的方式了

 X.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单X.ADC_ScanConvMode=DISABLE;//可以选择是扫描模式还是非扫描模式---非扫描模式

1:单次转化,非扫描模式

 

        在非扫描的模式下,这个组就只有第一个序列1的位置有效 , 这时选中一组的方式就退化为简单地选中一个的方式了

        我们可以在序列1的位置指定我们想转换的通道 , 比如通道2 , 然后,我们就可以触发转换,ADC就会对这个通道2进行模数转换 , 过一段时间转化完成  , 转换结果放在数据寄存器里,同时给EOC标志位置1----转换过程就结束了 .   我们判断这个EOC标志位,如果转换完了 ,  可以在数据寄存器里读取结果了 ,  如果我们想再启动一次转换,那就需要再触发一次 ,  转换结束,置EOC标志位,读结果

2:连续转化,非扫描模式

 

        首先,它还是非扫描模式,所以组列表就只用第一个  ,  与单次转换不同的是,它在一次转换结束后不会停止 ,  而是立刻开始下一轮的转换,然后一直持续下去  ,  这样就只需要最开始触发一次,之后就可以一直转换了

        优点  :  开始转换之后不需要等待一段时间的 ,  它一直都在转换,所以你就不需要手动开始转换了 , 也不用判断是否结束的 ,  想要读AD值的时候,直接从数据寄存器取就是了

3:单次转化,扫描模式

 

        这个模式也是单次转换,所以每触发一次 , 转换结束后,就会停下来  ,  下次转换就得再触发能开始

        扫描模式 : 会用到这个组了 , 在序列中填入通道 , 这里每个位置是通道几可以任意指定,并且也是可以重复的 ,  初始化结构体里还会有个参数,就是通道数目 (x.ADC_NbrOfChannel=)  比如这里指定通道数目为7,那它就只看前7个位置,那么x.ADC_NbrOfChannel=7, 它就会只对前7个AD通道进行转化,   转换结果都放在数据寄存器里  ,   这里为了防止数据被覆盖,就需要用DMA及时将数据挪走  ,  那7个通道转换完成之后,产生EOC信号(EOC置1),转换结束 ,  然后再触发下一次,就又开始新一轮的转换

使用DMA---避免通道数据覆盖

        因为这里只有一个规则组的数据寄存器 , 如果使用了 扫描模式在一个组中开启了7个通道,  只会有最后一个通道被保留下来,  前面的6个通道会被覆盖掉. 最后只会得到一个通道.

        使用这里使用MDA在下一个通道来之前,  把现在的数据放到了MDA中, 避免出现通道的覆盖问题

4:单次转化,扫描模式

         次转换完成后,立刻开始下一次的转换 , 也开启组

6:触发控制

配置ADC时使用的参数-----X.ADC_ExternalTrigConv

7:数据对齐

ADC初始化中的配置---X.ADC_DataAlign

 

        我们这个ADC是12位的,它的转换结果就是一个12位的数据 ,  但是这个数据寄存器是16位的,所以就存在一个数据对齐的问题

右对齐 : 就是12位的数据向右靠 , 就是12位的数据向有靠 , 高位多出来的几位就补0 ,一般

使用右对齐,  这样读取这个16位寄存器,直接就是转换结果

左对齐 : 是12位的数据向左靠 ,  低位多出来的几位补0 ,  得到的数据会比实际的大 ,  数据左对齐实际上就是把数据左移了4次  ,数据左移一次,就等效于把这个数据乘2 , 直接读取的话相当于把数据扩大了16倍 . 

 8:转化时间

 

 

9:校准

        ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差

        建议在每次上电后执行一次校准

        启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期

10:ADC的硬件电路

 

A: AD单通道

1:连接图

 

2:函数介绍

在stm32f10x rcc.h文件中-----配置ADCCLK

void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)

RCC_ADCCLKConfig :  APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号 ; ADCCLK最大是14MHz , 所以这里只能是6分频或者是8分频

        6分频:72Mhz/6=12Mhz,             8分频:72Mhz/8=9Mhz

在stm32f10x adc.h文件中----选择组----我们选择规则组的输入通道

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

 ADC_RegularChannelConfig : 参数2--选择通道 , 参数3--选择序列范围1~16       参数3: 指定通道的采样时间

在stm32f10x adc.h文件中----初始化ADC

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

ADC_InitTypeDef ADC_initstruct;


    ADC_initstruct.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单次
    ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
    ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
    ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
    ADC_initstruct.ADC_NbrOfChannel=1;  //通道数目--指定在扫描模式下,总共会用到几个通道
    ADC_initstruct.ADC_ScanConvMode=DISABLE;//可以选择是扫描模式还是非扫描模式---非扫描模式
    ADC_Init(ADC1,&ADC_initstruct);

在stm32f10x adc.h文件中----开启ADC

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState)

在stm32f10x adc.h文件中----校准ADC

第一步,调用第一个函数,复位校准

第二步,调用第二个函数,等待复位校准完成

第三步,调用第三个函数,开始校准

第四步,调用第四个函数,等待校准完成

1 : void ADC_ResetCalibration(ADC_TypeDef* ADCx);
2 : FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
3 : void ADC_StartCalibration(ADC_TypeDef* ADCx);
4 : FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);

ADC_ResetCalibration : 复位校准

ADC_GetResetCalibrationStatus : 等待复位校准完成

ADC_StartCalibration : 开始校准

ADC_GetCalibrationStatus : 获取校准状态

在stm32f10x adc.h文件中----启动转换,获取结果

void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx)

ADC_SoftwareStartConvCmd : 软件触发转换

ADC_GetFlagStatus : 获取标志位状态的函数

ADC_GetConversionValue : ADC 获取转换值

3:步骤

1:RCC开启时钟-----ADC和GPIO的时钟

2: 配置ADCCLK----RCC_ADCCLKConfig

3: 配置GPIO-----GPIO_Init

4:选择组----我们选择规则组的输入通道--------ADC_RegularChannelConfig

5: 初始化ADC-----ADC_Init

6:开启ADC----ADC_Cmd

7:校准ADC:

A: 复位校准-----ADC_ResetCalibration

B:等待复位校准完成----ADC_GetResetCalibrationStatus

C: 开始校准-----ADC_StartCalibration

D:获取校准状态-----ADC_GetCalibrationStatus 

4:代码

        使用的是单次转化,非扫描模式

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

#include "AD.h"
void AD_init(void){
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//配置ADCCLK
	//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;  //模拟输入,可以理解为ADC的专属模式
	GPIO_initstruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_initstruct);
	
	//选择AD转化器----我们选择规则组的输入通道
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);  
	/*ADC_Channel_0--通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	
	//初始化ADC
	ADC_InitTypeDef ADC_initstruct;
	ADC_initstruct.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单次
	ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
	ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
	ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
	ADC_initstruct.ADC_NbrOfChannel=1;  //通道数目--指定在扫描模式下,总共会用到几个通道
	ADC_initstruct.ADC_ScanConvMode=DISABLE;//可以选择是扫描模式还是非扫描模式---非扫描模式
	ADC_Init(ADC1,&ADC_initstruct);

	//开启ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//校准ADC
	
	//复位校准
	ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
	//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
	while (ADC_GetResetCalibrationStatus(ADC1)==SET);   //SET=1
	/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
	在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
	你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
	*/
	//开始校准
	ADC_StartCalibration(ADC1);
	//获取校准状态
	while(ADC_GetCalibrationStatus(ADC1)==SET);

}

uint16_t ad_getvalue(){
	//启动转换,获取结果
	
	//软件触发转换
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	//获取标志位状态的函数
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
	/*
	EOC是规则组或注入组完成时都会置1 , 0(RESET):转换未完成:
	1(SET):转换完成
	*/
	//ADC 获取转换值
	return ADC_GetConversionValue(ADC1);
	/*ADC_GetConversionValue---那这里,因为读取DR寄存器会自动清除EOC标志位
	所以这之后我们就不需要再手动清除标志位了
	
	*/
	

}



uint16_t advalue;
float volatge;
int main(void)
{
	OLED_Init();
	AD_init();
	
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "volatge:0.00V");
	
	
	while (1)
	{
		advalue=ad_getvalue();
		volatge=(float)advalue /4095 *3.3;
		OLED_ShowNum(1, 9, advalue, 4);
		OLED_ShowNum(2, 9, volatge, 1);
//浮点数不能取余
		OLED_ShowNum(2, 11, (uint16_t)(volatge * 100) % 100, 2);
		Delay_ms(100);
//OLED_ShowNum函数是写整数的,所以使用这中方法表示小数

	}
}

校准ADC     SET=1   RESET=0

       //复位校准
    ADC_ResetCalibration(ADC1);
   //等待复位校准完成
    while (ADC_GetResetCalibrationStatus(ADC1)==SET);   
    //开始校准
    ADC_StartCalibration(ADC1);
    //获取校准状态
    while(ADC_GetCalibrationStatus(ADC1)==SET);

        复位校准(ADC_ResetCalibration)函数的作用是把CR2_RSTCAL_Set这一位置一 ; 

         等待复位校准完成(while (ADC_GetResetCalibrationStatus(ADC1)==SET) :  获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除在校准寄存器被初始化后该位将被清除,所以该位的用法就是:你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0

        开始校准(ADC_StartCalibration) : 这样子就可以启动校准

        获取校准状态(while(ADC_GetCalibrationStatus(ADC1)==SET);) : 看校准是否完成

启动转换,获取结果    SET=1   RESET=0

        ​​​​​//软件触发转换
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);
    //获取标志位状态的函数
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
     //ADC 获取转换值
    return ADC_GetConversionValue(ADC1);
          软件触发转换(ADC_SoftwareStartConvCmd) : ADC开始转化

        获取标志位状态的函(while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);)

: EOC是规则组或注入组完成时都会置1 , 0(RESET):转换未完成:1(SET):转换完成

        ADC 获取转换值(ADC_GetConversionValue(ADC1);) : ADC_GetConversionValue---那这里,因为读取DR寄存器会自动清除EOC标志位 所以这之后我们就不需要再手动清除标志位了
    

按照上面的流程来执行就ok了 : 首先,软件触发转换 , 然后等待转换完成,也就是等待EOC标志位置1 , 最后,读取ADC数据寄存器

配置GPIO---模式选择模拟输入 

GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;

        在AIN模式下,GPIO口是无效的 ,  断开GPIO”防止你GPIO口的输入输出对我模拟电压造成干扰 ,  所以AIN模式就是ADC的专属模式

引脚分布

       

使用连续转化,非扫描模式

        优点: 不需要不断地触发,也不需要等待转换完成的

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

#include "AD.h"
void AD_init(void){
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//配置ADCCLK
	//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;  //模拟输入,可以理解为ADC的专属模式
	GPIO_initstruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_initstruct);
	
	//选择AD转化器----我们选择规则组的输入通道
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);  
	/*ADC_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	
	//初始化ADC
	ADC_InitTypeDef ADC_initstruct;
	ADC_initstruct.ADC_ContinuousConvMode=ENABLE;//选择是连续转换还是单次转换---单次
	ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
	ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
	ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
	ADC_initstruct.ADC_NbrOfChannel=1;  //通道数目--指定在扫描模式下,总共会用到几个通道
	ADC_initstruct.ADC_ScanConvMode=DISABLE;//可以选择是扫描模式还是非扫描模式---非扫描模式
	ADC_Init(ADC1,&ADC_initstruct);

	//开启ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//校准ADC
	
	//复位校准
	ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
	//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
	while (ADC_GetResetCalibrationStatus(ADC1)==SET);   //SET=1
	/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
	在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
	你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
	*/
	//开始校准
	ADC_StartCalibration(ADC1);
	//获取校准状态
	while(ADC_GetCalibrationStatus(ADC1)==SET);
	
	//软件触发转换
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//连续模式下只需要触发一次

}

uint16_t ad_getvalue(){
	//启动转换,获取结果

	return ADC_GetConversionValue(ADC1);
	/*ADC_GetConversionValue---那这里,因为读取DR寄存器会自动清除EOC标志位
	所以这之后我们就不需要再手动清除标志位了
	*/	

}


uint16_t advalue;
float volatge;
int main(void)
{
	OLED_Init();
	AD_init();
	
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "volatge:0.00V");
	
	
	while (1)
	{
		advalue=ad_getvalue();
		volatge=(float)advalue /4095 *3.3;
		OLED_ShowNum(1, 9, advalue, 4);
		OLED_ShowNum(2, 9, volatge, 1);
		OLED_ShowNum(2, 11, (uint16_t)(volatge * 100) % 100, 2);
		Delay_ms(100);

	}
}

        因为使用了连续模式 , 使用只有需要触发一次即可,  把//软件触发转换 ADC_SoftwareStartConvCmd(ADC1,ENABLE);移到 void AD_init函数下即可

        //获取标志位状态的while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);

在初始化完成之后,触发一次就行了 , 内部的ADC就会一次接着一次地、连续不断地 ,  对我们指定的通道0进行转换,转换结果放在数据寄存器里  ,  此时数据寄存器会不断地刷新最新的转换结果 ,使用在uint16_t ad_getvalue() 这里就不需要获取标志位状态的函数了

 B:AD多通道

 1:连接图  

 AO----模拟量的输出引脚

3个模块的AO,分别接在PA1、PA2和PA3口

DO-----数字输出

        AO是指传感器输出的模拟电信号:        可以是电压、电流等连续变化的信号,其数值与光照强度(或其他被测量的参数)相关。AO信号可以通过模数转换器(ADC)转换为数字信号,以供后续处理或控制使用。

        DO是指传感器输出的数字信号:        通常以逻辑电平(例如高电平或低电平)表示,表示光照强度(或其他被测量的参数)是否达到或超过设定的阈值。DO信号可以直接用于触发开关、报警或其他数字控制应用。

通过同时使用AO和DO,光敏传感器可以提供更丰富的信息输出,并满足不同应用场景的需求。

以前我们使用的都是DO,  这里我们使用的为A0

2:代码

使用: 单次非扫描的模式

        多通道  :  首先我们应该想到扫描的模式 , 启动一个组在组中填入我们要使用的通道,  但是由于覆盖的问题,要使用DMA(见下一节) ,所以没有使用扫描模式

        //选择AD转化器----我们选择规则组的输入通道
    ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5); 第二个参数就是我们的通道,  我们把通道作为参数, 放在组中的第一给序列中, 只需要在主函数中不断调用ad_getvalue函数即可

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

#include "AD.h"
void AD_init(void){
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//配置ADCCLK
	//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;  //模拟输入,可以理解为ADC的专属模式
	GPIO_initstruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
	GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_initstruct);
	
	 
	/*ADC_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	
	//初始化ADC
	ADC_InitTypeDef ADC_initstruct;
	ADC_initstruct.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单次
	ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
	ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
	ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
	ADC_initstruct.ADC_NbrOfChannel=1;  //通道数目--指定在扫描模式下,总共会用到几个通道
	ADC_initstruct.ADC_ScanConvMode=DISABLE;//可以选择是扫描模式还是非扫描模式---非扫描模式
	ADC_Init(ADC1,&ADC_initstruct);

	//开启ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//校准ADC
	
	//复位校准
	ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
	//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
	while (ADC_GetResetCalibrationStatus(ADC1)==SET);   //SET=1
	/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
	在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
	你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
	*/
	//开始校准
	ADC_StartCalibration(ADC1);
	//获取校准状态
	while(ADC_GetCalibrationStatus(ADC1)==SET);

}

uint16_t ad_getvalue(uint8_t ADC_Channel){
	//启动转换,获取结果
	
	//选择AD转化器----我们选择规则组的输入通道
	ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5); 
	
	//软件触发转换
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	//获取标志位状态的函数
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
	/*
	EOC是规则组或注入组完成时都会置1 , 0(RESET):转换未完成:
	1(SET):转换完成
	*/
	//ADC 获取转换值
	return ADC_GetConversionValue(ADC1);
	/*ADC_GetConversionValue---那这里,因为读取DR寄存器会自动清除EOC标志位
	所以这之后我们就不需要再手动清除标志位了
	
	*/
	


}



uint16_t AD1,AD2,AD3,AD4;
float volatge;
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)
	{
		
		AD1=ad_getvalue(ADC_Channel_0);
		AD2=ad_getvalue(ADC_Channel_1);
		AD3=ad_getvalue(ADC_Channel_2);
		AD4=ad_getvalue(ADC_Channel_3);

		OLED_ShowNum(1, 5, AD1, 4);
		OLED_ShowNum(2, 5, AD2, 4);
		OLED_ShowNum(3, 5, AD3, 4);
		OLED_ShowNum(4, 5, AD4, 4);
		Delay_ms(100);

	}
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

07:STM32----ADC模数转化器 的相关文章

  • 串口流控(CTS/RTS)使用详解

    1 流控概念 在两个设备正常通信时 由于处理速度不同 就存在这样一个问题 有的快 有的慢 在某些情况下 就可能导致丢失数据的情况 如台式机与单片机之间的通讯 接收端数据缓冲区已满 则此时继续发送来的数据就会丢失 流控制能解决这个问题 当接收
  • 最终启动顺序错误 - STM32L476 的 Eclipse System Workbench 调试

    我正在尝试调试和运行 STM32L476 的简单汇编代码 我已经设置了 Eclipse Oxygen 在 Eclipse 中安装了最新版本的 System Workbench 插件并安装了 ST Link 驱动程序 IDE 成功构建了程序
  • allegro画PCB如何更新元件的PCB封装

    allegro画PCB如何更新元件的PCB封装 一 更新单个器件的PCB封装 首先菜单栏选择Place gt Update Symbols 如下图 注意此案例是更新了C0805封装 中更新封装 就将上图第二个红色方框中的勾选即可 二 更新某
  • 串口通讯第一次发送数据多了一字节

    先初始化IO再初始化串口 导致第一次发送时 多出一个字节数据 优化方案 先初始化串口再初始化IO 即可正常通讯
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • STM32的HAL中实现单按、长按和双按功能

    我正在尝试实现单击 双击和长按功能来执行不同的功能 到目前为止 我已经理解了单击和长按的逻辑 但我不知道如何检测双击 至于代码 我使用计数器实现了单击和长按 但代码仅停留在第一个 if 条件上 bool single press false
  • STM32F207 I2C 测试失败

    我正在使用 STM32F207 微控制器在 STM3220G EVAL 板上学习嵌入式开发 我尝试通过连接同一芯片上的两个 I2C2 和 I2C3 模块并发送 接收字符来测试 I2C 接口 这是我当前编写的代码 使用 mdk arm 5 i
  • 普冉32位单片机 PY32C642,M0+内核,1.7 V ~ 5.5 V宽工作电压

    PY32C642 单片机采用高性能的 32 位 ARM Cortex M0 内核 宽电压工作范围 嵌入 24Kbytes Flash 和 3 Kbytes SRAM 存储器 最高工作频率 24 MHz 包含多种不同封装类型产品 工作温度范围
  • 世微 AP2402降压恒流驱动IC LED刹车灯方案 全亮 半亮 瀑闪

    AP2402 是一款 PWM 工作模式 高效 率 外围简单 内置功率管 适用于 5 100V 输入的高精度降压 LED 恒流驱动芯片 输 出功率可达 15W 电流 1 5A AP2402 可实现三段功能切换 通过 MODE1 2 3 切换三
  • 世微AP9196 DC-DC 升压恒流电源管理芯 电解水驱动电源应用线路

    AP9196 是一系列外围电路简洁的宽调光比升压调光恒流驱动器 适用于 3 40V 输入电压范围的 LED照明领域 AP9196 采用我司专利算法 可以实现高精度的恒流效果 输出电流恒流精度 3 电压工作范围为5 40V 可以轻松满足锂电池
  • 物联网项目分享 Stm32单片机的音乐播放器设计 - 物联网 嵌入式

    文章目录 0 前言 1 简介 2 主要器件 3 实现效果 4 设计原理 5 部分核心代码 6 最后 0 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的毕设题目缺少创新和亮点 往往达不到毕业答辩的要求 这两年不断有学弟学妹告诉
  • 太阳诱电 | 陶瓷电容器的静电容量相关

    陶瓷电容器的静电容量会因温度而变化吗 电容器的静电容量的温度特性是什么 陶瓷电容器的静电容量随周围的温度而变化 静电容量因温度而变化的现象 称为静电容量的温度特性 这是由于陶瓷电容器使用的材料造成的 也是所有陶瓷电容的常有现象 以下是本公司
  • for循环延时时间计算

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 之前做led点亮的实验 好像是被delay函数影响了 因为delay参数设置的不对
  • 嵌入式开发--STM32G4系列片上FLASH的读写

    这个玩意吧 说起来很简单 就是几行代码的事 但楞是折腾了我大半天时间才搞定 原因后面说 先看代码吧 读操作 读操作很简单 以32位方式读取的时候是这样的 data IO uint32 t 0x0800F000 需要注意的是 当以32位方式读
  • 从没有中断引脚并且在测量准备好之前需要一些时间的传感器读取数据的最佳方法

    我正在尝试将压力传感器 MS5803 14BA 与我的板 NUCLEO STM32L073RZ 连接 根据 第 3 页 压力传感器需要几毫秒才能准备好读取测量值 对于我的项目 我对需要大约 10 毫秒来转换原始数据的最高分辨率感兴趣 不幸的
  • STM32 上的位置无关代码 - 指针

    我已成功在 STM32 上构建并运行位置无关的代码 向量表和 GOT 已修补 一切正常 但我对这样的代码有问题 double myAdd double x return x 0 1 double ptrmyAdd double myAdd
  • STM32内部时钟

    我对 STM32F7 设备 意法半导体的 Cortex M7 微控制器 上的时钟系统感到困惑 参考手册没有充分阐明这些时钟之间的差异 SYSCLK HCLK FCLK 参考手册中阅读章节 gt RCC 为 Cortex 系统定时器 SysT
  • PWM DMA 到整个 GPIO

    我有一个 STM32F4 我想对一个已与掩码进行 或 运算的 GPIO 端口进行 PWM 处理 所以 也许我们想要 PWM0b00100010一段时间为 200khz 但随后 10khz 后 我们现在想要 PWM0b00010001 然后
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015
  • 哪些变量类型/大小在 STM32 微控制器上是原子的?

    以下是 STM32 微控制器上的数据类型 http www keil com support man docs armcc armcc chr1359125009502 htm http www keil com support man d

随机推荐

  • 基于Vue实现的用户可注册登录的Todo-List清单

    用到技术 Vue 脚手架 路由 element UI 文件目录 src components Enter vue MyBanner vue MyFooter vue MyHeader vue MyItem vue MyList vue pa
  • 定义一个表示学生信息的类Student...

    定义一个表示学生信息的类Student 要求如下 1 类Student的成员变量 nO 表示学号 name表示姓名 sex表示性别 age表示年龄 java 表示Java课程成绩 2 类Student带参数的构造方法 在构造方法中通过参数完
  • 笔记1 - Web API 的概念和http协议

    接口定义 接口是个比较泛义上的概念 主要表示系统对外交互的部分 比如电源插座是电器和电能之间的接口 图形界面是应用软件和用户的接口 医院挂号大厅是医生和病人之间的接口 webAPI 我们要学习的接口概念缩小到web系统提供的对外消息交互接口
  • C# 如何在一个项目中引用另外一个项目

    C 如何在一个项目中引用另外一个项目 直接使用 using namespace 会发现引用不了 需要先添加引用 勾上需要引用的项目 我们再用 using namespace 就会发现可以了
  • html制作自动切换的广告页,js实现每个IP每天只弹出一次广告页,js实现html自动弹出对话框...

    js实现每个IP每天只弹出一次广告页 定义Cookie function setCookie name value expire window document cookie name escape value expire null ex
  • Excalidraw 简介及 Docker Compose 部署指南

    家人们好 我们在工作生活中经常需要画些图 我们往期了已经出过draw io私有化部署的文章了 今天我要向大家介绍一款名为 Excalidraw 的绘图工具 这款工具了我个人非常喜欢使用 是因为它可以修改成类似于手写体的字体 并且可以直接绘画
  • 深度学习&图像处理(深度学习的医学图像分割3.1)

    基于深度学习的医学图像分割模型研究 曹祺炜 1 基于改进的3D FCN CRF以及MS CapsNetGAN实现脑肿瘤图像分割 图像语义分割 简单而言就是给定一张图片 对图片上的每一个像素点分类 不同颜色代表不同类别 图像分割的主要步骤 图
  • mysql安装配置——超详细图文教程

    相关资源包进群自取 953882093 1 首先单击MySQL5 5 21的安装文件 出现该数据库的安装向导界面 单击 next 继续安装 如图所示 2 在打开的窗口中 选择接受安装协议 单击 next 继续安装 如图所示 3 在出现选择安
  • C# 提取 PDF 文档中的文本

    安装 IText7 首先在 Nuget 包管理器中 安装 itext7 和 itext7 font asian 如果不安装 itext7 font asian PDF 文件中有非Unicode编码的字符 将会抛出运行时异常 iText IO
  • ImportError: /home/yukang/anaconda3/envs/fsgan/lib/python3.9/site-packages/torch/lib/libtorch_cuda_c

    ImportError home yukang anaconda3 envs fsgan lib python3 9 site packages torch lib libtorch cuda cpp so undefined symbol
  • 解决Anaconda安装后出现Conda environment has not been activated.问题

    问题描述 Win10安装完成Anaconda3 2021 11 Windows x86 64 exe 且手动配置好环境变量后 出现conda未激活现象 cmd控制台输入 C Users Queena gt python Python 3 9
  • Qt使用QGraphicsView实现滑动窗体效果

    源码已上传至CSDN http download csdn net source 2808505 QGraphicsView用来显示一个滚动视图区的QGraphicsScene内容 QGraphicsScene提供了QGraphicsIte
  • 利用公交刷卡数据分析北京职住关系和通勤出行

    01 文章信息 利用公交刷卡数据分析北京职住关系和通勤出行 本篇文章于2021年10月15日发表于 地理学报 02 摘要 基于位置服务 Location Based Service LBS 技术为研究城市系统的时空动态规律提供 了新的视角
  • c++中的常用知识点总结

    命名空间 使用命名空间之后 调用代码时可以省去也可以不省去相关的前缀 include
  • CSS语法与CSS选择器

    目录 CSS 语法 实例 例子解释 CSS 选择器 CSS 元素选择器 实例 CSS id 选择器 实例 CSS 类选择器 实例 实例 实例 CSS 通用选择器 实例 CSS 分组选择器 实例 所有简单的 CSS 选择器 延伸阅读 CSS
  • 宝塔后渗透-添加用户_反弹shell

    更新时间 2022年11月21日 1 背景介绍 对于想拿到bt后台来说 非常的艰难 无非是通过bypass之后提权 直接拿到服务器的root权限 然后再去宝塔后台 当然 还有一种运气十分爆棚的方法 发现了bt的账密信息 可以直接登陆了bt后
  • 电磁兼容RE典型整改案例分析

    1 可视对讲门铃EMI解决方案 客户介绍 珠海某电子科技有限公司是一家专门从事智能家居和楼宇对讲生产 研发 销售为一体的科技型企业 经客户转介绍认识 因其有一个新研发的高端出口可视对讲系统在深圳宝安某检测公司进行空间 RE 辐射测试EN55
  • LVGL8学习之Flex布局2

    这一篇来学习Flex布局的把项目按行包裹 且让他们周围的控件平均 Arrange items in rows with wrap and even spacing 还是通过codeblock来模拟代码的运行 代码如下 void lv fle
  • vue高级特性

    Vue是一款流行的JavaScript框架 它可以帮助我们构建高效 可维护的Web应用程序 本篇文章中 我将给大家分享三个Vue的高级技术 并且详细地讲解它们的实现原理 动态组件 动态组件是Vue中非常有用的一项功能 它允许我们在不同的组件
  • 07:STM32----ADC模数转化器

    目录 1 简历 2 逐次逼近型ADC 3 ADC基本结构 4 输入通道 5 规则组的4种转换模式 1 单次转化 非扫描模式 2 连续转化 非扫描模式 3 单次转化 扫描模式 4 单次转化 扫描模式 6 触发控制 7 数据对齐 8 转化时间