STM32入门笔记08_ADC模数转换器案例:单通道ADC应用详解

AD模数转换器

ADC简介

  • ADC(Analog-Digtal Converter) 模拟-数字转换器
  • ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量, 建立模拟电路到数字电路的桥梁
  • 12位逐次逼近型ADC, 1us转换时间
  • 输入电压范围: 0~3.3V, 转换结果范围: 0~4095
  • 18个输入通道, 可测量16个外部和2个内部信号源
  • 规则组和注入组两个转换单元
  • 模拟看门狗自动监测输入电压范围
  • STM32F103C8T6 ADC资源: ADC1、ADC2, 10个外部输入通道
  • 逐次逼近型ADC

  • IN0~IN7是八个输入通道
  • 通过配置ADDA~ADDC可以选择一个通道作为信号输入
  • 通过比较器, DAC逐渐逼近输入信号, DAC的值最终与输入信号十分接近
  • ADC框图

    ADC基本结构

    输入通道

    通道 ADC1 ADC2 ADC3
    通道0 PA0 PA0 PA0
    通道1 PA1 PA1 PA1
    通道2 PA2 PA2 PA2
    通道3 PA3 PA3 PA3
    通道4 PA4 PA4 PF6
    通道5 PA5 PA5 PF7
    通道6 PA6 PA6 PF8
    通道7 PA7 PA7 PF9
    通道8 PB0 PB0 PF10
    通道9 PB1 PB1
    通道10 PC0 PC0 PC0
    通道11 PC1 PC1 PC1
    通道12 PC2 PC2 PC2
    通道13 PC3 PC3 PC3
    通道14 PC4 PC4
    通道15 PC5 PC5
    通道16 温度传感器
    通道17 内部参考电压

    转换模式

  • 单次转换, 非扫描模式
  • 连续转换, 非扫描模式
  • 单次转换, 扫描模式
  • 连续转换, 扫描模式
  • 触发控制

    数据对齐

  • 数据右对齐:
  • 数据左对齐:
  • 转换时间

  • AD转换的步骤: 采样, 保持, 量化, 编码

  • STM32 ADC的总转换时间为:

    ​ Tconv = 采样时间 + 12.5个ADC周期

  • 例如: 当ADCCLK = 14MHz, 采样时间为1.5个ADC周期

    ​ Tconv = 1.5 + 12.5 = 14个ADC周期 = 1us

  • 校准

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

    案例1: ADC单通道

    配置ADC转换

    void AD_Init(void)
    {
    	// RCC使能时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    	RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 12MHz
    	// 配置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_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    	
    	// 配置ADC转换器
    	ADC_InitTypeDef ADC_InitStructure;
    	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换或者连续转换
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据对齐模式
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // ADC模式, 单独还是交叉
    	ADC_InitStructure.ADC_NbrOfChannel = 1;  // 扫描的通道数
    	ADC_InitStructure.ADC_ScanConvMode = DISABLE;  // 扫描模式或者非扫描模式
    	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  // 触发控制
    	ADC_Init(ADC1, &ADC_InitStructure);
    	
    	// 开启ADC功能
    	ADC_Cmd(ADC1, ENABLE);
    	
    	// ADC校准
    	ADC_ResetCalibration(ADC1);
    	while(ADC_GetResetCalibrationStatus(ADC1) == SET);  // 已初始化为零
    	ADC_StartCalibration(ADC1);
    	while (ADC_GetCalibrationStatus(ADC1) == SET); 
    	
    }
    

    RCC_ADCCLKConfig

    /**
      * @brief  Configures the ADC clock (ADCCLK).
      * @param  RCC_PCLK2: defines the ADC clock divider. This clock is derived from 
      *   the APB2 clock (PCLK2).
      *   This parameter can be one of the following values:
      *     @arg RCC_PCLK2_Div2: ADC clock = PCLK2/2
      *     @arg RCC_PCLK2_Div4: ADC clock = PCLK2/4
      *     @arg RCC_PCLK2_Div6: ADC clock = PCLK2/6
      *     @arg RCC_PCLK2_Div8: ADC clock = PCLK2/8
      * @retval None
      */
    void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
    

    ADC_ContinuousConvMode

    *!< Specifies whether the conversion is performed in
                                                   Continuous or Single mode.
                                                   This parameter can be set to ENABLE or DISABLE. */
    

    ADC_DataAlign

    #define ADC_DataAlign_Right                        ((uint32_t)0x00000000)
    #define ADC_DataAlign_Left                         ((uint32_t)0x00000800)
    

    ADC_Mode

    #define ADC_Mode_Independent                       ((uint32_t)0x00000000)
    #define ADC_Mode_RegInjecSimult                    ((uint32_t)0x00010000)
    #define ADC_Mode_RegSimult_AlterTrig               ((uint32_t)0x00020000)
    #define ADC_Mode_InjecSimult_FastInterl            ((uint32_t)0x00030000)
    #define ADC_Mode_InjecSimult_SlowInterl            ((uint32_t)0x00040000)
    #define ADC_Mode_InjecSimult                       ((uint32_t)0x00050000)
    #define ADC_Mode_RegSimult                         ((uint32_t)0x00060000)
    #define ADC_Mode_FastInterl                        ((uint32_t)0x00070000)
    #define ADC_Mode_SlowInterl                        ((uint32_t)0x00080000)
    #define ADC_Mode_AlterTrig                         ((uint32_t)0x00090000)
    

    ADC_NbrOfChannel

     /*!< Specifies the number of ADC channels that will be converted
                                                   using the sequencer for regular channel group.
                                                   This parameter must range from 1 to 16. */
    

    ADC_ScanConvMode

    /*!< Specifies whether the conversion is performed in
                                                   Scan (multichannels) or Single (one channel) mode.
                                                   This parameter can be set to ENABLE or DISABLE */
    

    ADC_ExternalTrigConv

    #define ADC_ExternalTrigConv_T1_CC1                ((uint32_t)0x00000000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T1_CC2                ((uint32_t)0x00020000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T2_CC2                ((uint32_t)0x00060000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T3_TRGO               ((uint32_t)0x00080000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T4_CC4                ((uint32_t)0x000A0000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO    ((uint32_t)0x000C0000) /*!< For ADC1 and ADC2 */
    
    #define ADC_ExternalTrigConv_T1_CC3                ((uint32_t)0x00040000) /*!< For ADC1, ADC2 and ADC3 */
    #define ADC_ExternalTrigConv_None                  ((uint32_t)0x000E0000) /*!< For ADC1, ADC2 and ADC3 */
    
    #define ADC_ExternalTrigConv_T3_CC1                ((uint32_t)0x00000000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T2_CC3                ((uint32_t)0x00020000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T8_CC1                ((uint32_t)0x00060000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T8_TRGO               ((uint32_t)0x00080000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T5_CC1                ((uint32_t)0x000A0000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T5_CC3                ((uint32_t)0x000C0000) /*!< For ADC3 only */
    
    

    整体代码:

    ADC.c
    #include "stm32f10x.h"
    
    /*
    初始化ADC
    */
    void AD_Init(void)
    {
    	// RCC使能时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    	RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 12MHz
    	// 配置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_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    	
    	// 配置ADC转换器
    	ADC_InitTypeDef ADC_InitStructure;
    	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换或者连续转换
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据对齐模式
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // ADC模式, 单独还是交叉
    	ADC_InitStructure.ADC_NbrOfChannel = 1;  // 扫描的通道数
    	ADC_InitStructure.ADC_ScanConvMode = DISABLE;  // 扫描模式或者非扫描模式
    	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  // 触发控制
    	ADC_Init(ADC1, &ADC_InitStructure);
    	
    	// 开启ADC功能
    	ADC_Cmd(ADC1, ENABLE);
    	
    	// ADC校准
    	ADC_ResetCalibration(ADC1);
    	while(ADC_GetResetCalibrationStatus(ADC1) == SET);  // 已初始化为零
    	ADC_StartCalibration(ADC1);
    	while (ADC_GetCalibrationStatus(ADC1) == SET); 
    	
    }
    
    /*
    获取ADC模块转换的值
    */
    uint16_t AD_GetValue(void)
    {
    	ADC_SoftwareStartConvCmd(ADC1, ENABLE);  // 软件触发
    	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);  // 等待转换完毕
    	return ADC_GetConversionValue(ADC1);
    }
    
    main.c
    #include "stm32f10x.h" 
    #include "delay.h"
    #include "OLED.h"
    #include "ADC.h"
    // ADC单通道
    // 2023年3月25日19:12:09
    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);
    	}
    }
    

    案例2: ADC多通道

    整体代码

    ADC.c
    #include "stm32f10x.h"
    
    /*
    初始化ADC
    */
    void AD_Init(void)
    {
    	// RCC使能时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    	RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 12MHz
    	// 配置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_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    	// 配置ADC转换器
    	ADC_InitTypeDef ADC_InitStructure;
    	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换或者连续转换
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据对齐模式
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // ADC模式, 单独还是交叉
    	ADC_InitStructure.ADC_NbrOfChannel = 1;  // 扫描的通道数
    	ADC_InitStructure.ADC_ScanConvMode = DISABLE;  // 扫描模式或者非扫描模式
    	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  // 触发控制
    	ADC_Init(ADC1, &ADC_InitStructure);
    	
    	// 开启ADC功能
    	ADC_Cmd(ADC1, ENABLE);
    	
    	// ADC校准
    	ADC_ResetCalibration(ADC1);
    	while(ADC_GetResetCalibrationStatus(ADC1) == SET);  // 已初始化为零
    	ADC_StartCalibration(ADC1);
    	while (ADC_GetCalibrationStatus(ADC1) == SET); 
    	
    }
    
    /*
    获取ADC模块转换的值
    */
    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);  // 等待转换完毕
    	return ADC_GetConversionValue(ADC1);
    }
    
    main.c
    #include "stm32f10x.h" 
    #include "delay.h"
    #include "OLED.h"
    #include "ADC.h"
    // ADC多通道
    // 2023年3月25日20:42:57
    uint16_t AD0, AD1, AD2, AD3;
    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)
    	{
    		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);
    	}
    }
    

    参考资料

    【STM32入门教程-2023持续更新中】 https://www.bilibili.com/video/BV1th411z7sn/?p=22&share_source=copy_web&vd_source=ee06a25b3dfb2900ab707b01fdff6667

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32入门笔记08_ADC模数转换器案例:单通道ADC应用详解

    发表回复