江科大STM32教程:ADC转换(单通道与多通道)

文章目录

  • ADC(Analog-Digital Converter)模拟-数字转换器
  • DAC的实现原理
  • 逐次逼近的过程
  • 知识点补充:
  • RC振荡器和锁相环(PLL)
  • 晶体振荡器
  • RTC(Real-Time Clock)即实时时钟
  • Reset and clock control (RCC),即复位与时钟控制,主要是通过寄存器配置时钟源。
  • STM32的时钟源
  • ADC预分频器来自于RCC ,2,4分频后分别是32,和18,最大16MHZ 因此只能选择6 和8 12/9
  • 模拟看门狗
  • ADC基本结构
  • 输入通道
  • 规则组4种转换模式
  • 1. 单次转换,非扫描模式
  • 2. 连续转换,非扫描模式
  • 3. 单次转换,扫描模式
  • 4. 连续转换,扫描模式
  • 触发控制(触发源)
  • 1.触发源选择
  • 2.数据对齐————右对齐
  • 3. AD转换的步骤:转换时间的计算
  • 4. 校准:固定的,只需要在初始化完ADC,加几条代码即可
  • 5. 数据波动的处理方案
  • 6. ADC 选择和DMA结合,为什么不可以手动转运
  • 7. 硬件电路
  • ADC+单通道
  • 显示电压值uint16_t ADValue;
  • 连续转换
  • ADC+多通道
  • 多通道 利用单通道非扫描完成
  • AD.c
  • ADC库函数
  • ADC(Analog-Digital Converter)模拟-数字转换器

    1. ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
    2. 12位(分辨率)逐次逼近型ADC,1us转换时间(最大1MHZ)
      输入电压范围:0~3.3V,转换结果范围:0–4095 (位数越高,分辨率越高) 0~3.3V 一一对应
    3. 18个输入通道,可测量16个外部(GPIO)和2个内部信号源(温度传感器,内部参考电压(基准电压,不会受外部电压影响))
    4. 规则组(用于常规事件)和注入组(用于突发事件)两个转换单元(一次启动一个组,)
    5. 模拟看门狗自动监测输入电压范围(判断AD值高于某个阈值或低于。。后,执行操作)
    6. STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道

    DAC的实现原理

    1. DAC:电压输出端:给他一个数据,它就能输出数据对应的电压,DAC内部使用加权电阻网络来实现的转换。(具体看51单片机ADDA)
    2. 外部通道输入一个未知编码的电压,DAC输出的是已知编码的电压,在电压比较器进行比较
    3. 如果DAC输出的电压比较小,就增大DAC数据,反之亦然,直到它和外部输入的基本相同。这样DAC输入的数据即使外部电压的编码数据了。
    4. 电压调节的过程就是通过逐次逼近寄存器SAR完成。

    逐次逼近的过程

    为了尽快找外部未知电压的编码,通常采用二分法进行寻找。
    比如图中8位ADC(0–255),第一次输入255的一半,128看谁大谁小,如果大了就64.。。。。
    如果用二进制来看,128,64,36这些数其实就是位权
    这个判断过程就是从高位到低位依次判断1还是0的过程。1000 0000 =>0100 0000 => 0010 0000
    对于8位,判断8此就可以得出DAC编码,12位就是12次.

    clock:驱动内部逐次比较的时钟

    如,硬件出发,ADC需要过一个固定时间转换一次,如1ms,那么定时器每隔1ms申请一次中断来触发转发转换。
    由于频繁进中断会影响主程序的进行,且中断有优先级,这也会影响ADC的转换,因此必须提供硬件支持,比如TIM1_CH1定时1ms由触发信号,就产生更新事件,连接到TIM3的TRGO口,,因此TIM3的更新事件就可以通过硬件自动触发ADC转换了。

    知识点补充:

    RC振荡器和锁相环(PLL)

  • 单片机内部是不会放晶振的,只有RC振荡器!

  • 在振荡电路中的频率选择部分可以只用电阻R和电容C构成。 这种只用电阻和电容构成的振荡器称为RC振荡器 。

  • 优点:是实现的成本比较低,毕竟就是一个电阻电容。缺点是由于电阻电容的精度问题所以RC振荡器的震荡频率会有误差,同时受到温度、湿度的影响,这个跟元器件的工艺有关。

    晶体振荡器

    优点是相对来说震荡频率一般都比较稳定。缺点的话就是价格要稍微高点了,还有用晶体振荡器一般还需要接两个 15-33pF起振电容。
    一般单片机中很少用RC振荡器,可能在实验室环境会用,而在实际的工程、工业上很少用到,常用的也就是晶体振荡器。因为很多时候单片机需要一个精度的机器周期作定时、通讯等用途,如果震荡频

    RTC(Real-Time Clock)即实时时钟

    目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。任何实时时钟的核心都是晶振,此时晶振的作用是提供基准频率,频率为32.768kHz。相比较晶振只可以产生稳定的频率,实时时钟是以输入频率做基础,再依此作除频、倍频、PLL等等,产生出处理器与主板各部分所需的频率。
    RTC是单片机的实时时钟,它需要时钟源,stm32中32 kHz 低速内部 RC (LSI RC),可选择提供给 RTC 用于停机/待机模式下的自动唤醒。32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK)

    Reset and clock control (RCC),即复位与时钟控制,主要是通过寄存器配置时钟源。

    STM32的时钟源

    有五个时钟源,为HSI、HSE、LSI、LSE、PLL:
    ①HSI是高速内部时钟,RC振荡器,频率为8MHz。
    ②HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
    ③LSI是低速内部时钟,RC振荡器,频率为40kHz。
    ④LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
    ⑤PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

    ADC预分频器来自于RCC ,2,4分频后分别是32,和18,最大16MHZ 因此只能选择6 和8 12/9

    模拟看门狗


    STM32 中有双ADC模式 可以组成同步交叉模式,交叉对一个通道采样。

    ADC基本结构

    输入通道

    规则组4种转换模式

    1. 单次转换,非扫描模式

    1.选择要转换的通道
    2.转换完成,将转换结果放入寄存器,对于EOC置为1

    2. 连续转换,非扫描模式

    一次转换完成后,会接着连续不断的转换。不需要手动开启转换

    3. 单次转换,扫描模式

    完成一次转换,需要再次触发,才能继续转换
    由于是扫描模式,需要用到菜单列表,
    通道数目,如果写7,就依次对前7个通道ADC转换。防止数据被覆盖,需要及时将数据挪走。转换完成,触发EOC

    4. 连续转换,扫描模式

    触发控制(触发源)

    1.触发源选择

    2.数据对齐————右对齐

    间断转换 (了解即可)数据对齐

    3. AD转换的步骤:转换时间的计算

    (采样,保持):在量化编码之前需要设置一个采样开关,先打开开关,收集外部电压,断开采样开关。可以保证比较时电压稳定。
    (量化,编码):ADC逐次比较的过程
    STM32 ADC的总转换时间为:
    TCONV = 采样时间 + 12.5个ADC周期(12位)
    例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
    TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs

    4. 校准:固定的,只需要在初始化完ADC,加几条代码即可

  • ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
  • 建议在每次上电后执行一次校准
  • 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
  • 5. 数据波动的处理方案

    1. 采用阈值,高低阈值,利用和施密特触发器一样的原理,得到平稳的正交波形
    2. 过滤器,取多个AD值的平均值,
    3. 裁剪分辨率

    6. ADC 选择和DMA结合,为什么不可以手动转运

    因为ADC完成转换后没有任何的标志位,不知道某一通道谁转换完了,只有在整个列表都转换完了,返回一个EOC结束标志,才触发中断
    转换一个数据只需要几微秒,因此很难做到数据不会丢失。
    但是可以采用间断模式,每转换一个通道就是暂停,等数据转运走了,再继续转换。

    7. 硬件电路

    ADC+单通道

    void AD_Init(void)
    {
    	//给ADC和GPIO口使能
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
        
    	GPIO_InitTypeDef GPIO_InitStruct;
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; //在AIN模式下GPIO口是无效的,因此是ADC的专属模式
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;	
    	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    	
    	GPIO_Init(GPIOA,&GPIO_InitStruct);
    	
    	//给序列的每个位置填写对应的通道
        ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
        ADC_InitTypeDef ADC_InitStruect;
    	ADC_InitStruect.ADC_ContinuousConvMode =DISABLE;     //单次转换还是连续
    	ADC_InitStruect.ADC_DataAlign = ADC_DataAlign_Right;  //右对齐
    	ADC_InitStruect.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None  ;   //软件触发,不使用外部触发启动ADC转换,触发控制的触发源
    	ADC_InitStruect.ADC_Mode = ADC_Mode_Independent;//独立模式   
    	ADC_InitStruect.ADC_NbrOfChannel = 1;     //通道数目
    	ADC_InitStruect.ADC_ScanConvMode = DISABLE;  //扫描还是非扫描
    	
    	ADC_Init(ADC1,&ADC_InitStruect);
    	ADC_Cmd(ADC1,ENABLE);
    	
    	//对ADC进行校准 复位校准
        ADC_ResetCalibration(ADC1);  //
        while(ADC_GetResetCalibrationStatus(ADC1) == SET);
    	//启动校准
        ADC_StartCalibration(ADC1);
        //获取校准状态 ---是否校准完成
    	while(ADC_GetCalibrationStatus(ADC1) == SET);
    }
    
    uint16_t AD_GetValue(void)//启动转换,获取结果
    {
    	//软件触发
    	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//不断初始化
    	//获取标志位状态  ,在转换后获取             ==0 表示转换未完成
    	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)  == RESET);//滚则组是否转换完成
    	//获取ADC转换值 ,就是读取DR数据寄存器的值
    	return ADC_GetConversionValue(ADC1);  //获取已经完成转换的AD值
    }
      //显示AD值
    uint8_t ADValue;
    int main(void)
    {
    
        OLED_Init();
        AD_Init();
    	
    	OLED_ShowString(1,1,"ADValue:");
    
    	while(1)
    	{
    		ADValue =AD_GetValue();
    		OLED_ShowNum(1,9,ADValue,4);  //显示AD值
    	}
    }
    

    显示电压值uint16_t ADValue;

    float Voltage;
    int main(void)
    {
    
        OLED_Init();
        AD_Init();
    	
    	OLED_ShowString(1,1,"ADValue:");
    
    	while(1)
    	{
    		ADValue =AD_GetValue();
    		Voltage = (float)ADValue / 4095 *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);   //显示电压值,就得到小数位了
    		
    	}
    	
    }
    

    连续转换

    	//软件触发
    	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//每次转换完成,数据会存到数据寄存器中,然后继续执行转换
    uint16_t AD_GetValue(void)  //启动转换,获取结果
    {
    	//获取标志位状态  ,在转换后获取             ==0 表示转换未完成
    	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)  == RESET);//
    	//获取ADC转换值 ,就是读取DR数据寄存器的值
    	return ADC_GetConversionValue(ADC1);  //获取已经完成转换的AD值
    }
    

    ADC+多通道

    多通道 利用单通道非扫描完成

    AD.c

    uint16_t AD_GetValue(uint8_t ADC_Channel)
    {
    	//给序列的每个位置填写对应的通道
       ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
    	//软件触发
    	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
    	//获取标志位状态  ,在转换后获取             ==0 表示转换未完成
    	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)  == RESET);
    	//获取ADC转换值 ,就是读取DR数据寄存器的值
    	return ADC_GetConversionValue(ADC1);  //获取已经完成转换的AD值
    }
    uint16_t AD0;
    uint16_t AD1;
    uint16_t AD2;
    uint16_t AD3;
    
    int main(void)
    {
    
        OLED_Init();
        AD_Init();
    	
    	OLED_ShowString(1,1,"AD0:");
        OLED_ShowString(1,1,"AD1:");
        OLED_ShowString(1,1,"AD2:");
        OLED_ShowString(1,1,"AD3:");
    
    	{
    		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,9,AD0,4);  //显示AD值
        OLED_ShowNum(1,9,AD1,4);  //显示AD值
    		OLED_ShowNum(1,9,AD2,4);  //显示AD值
    		OLED_ShowNum(1,9,AD3,4);  //显示AD值
    
    		
    	}
    	
    }
    

    ADC库函数

    //恢复初始状态
    void ADC_DeInit(ADC_TypeDef* ADCx);
    void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
    //给结构体一个默认值
    void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
    //开关控制就是给ADC上电
    void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
    //用来开启DMA输出信号,如果是用DMA转运数据用
    void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
    //中断输出控制,用来控制某个中断,能不能通往NVIC
    void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
    //复位校准
    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 获取开启转换状态  ---不能判断是否转换结束 返回SWSTART的状态,
    FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);  //一般不用
    
    //配置间断模式
    void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
    //是否启用间断模式
    void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
    
    //规则组 给序列的每个位置填写对应的通道
    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转换值
    uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
    //获取双ADC转换值
    uint32_t ADC_GetDualModeConversionValue(void);
    //下面都是ADC注入组配置
    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);
    //ADC 配置高低阈值   (看门狗)
    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);
    
    //获取EOC标志位是否变为1,是1就是转换结束  --判断转换是否结束
    //获取标志位
    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);
    
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 江科大STM32教程:ADC转换(单通道与多通道)

    发表回复