使用HAL库配置单通道ADC_1的步骤详解

文章目录

  • 一、ADC概念
  • 1.ADC特点—以STM32F4x系列为例
  • 2.ADC引脚介绍—以STM32F4x系列为例
  • 3.ADC框图简介—以STM32F4x系列为例
  • 4.ADC参数的简介
  • 5.采样时间和采样率的说明
  • 二、ADC工作模式
  • 1.轮询模式
  • 2.中断模式
  • 3.DMA模式
  • 三、ADC单通道采集-stm32Fx系列为例
  • 1.轮询模式下单通道采集
  • 2.轮询模式-单通道下采集代码示例
  • 单通道-单次采集模式
  • 单通道-连续采集模式
  • 3.中断模式-单通道下采集代码示例
  • 单通道-单次采集模式
  • 单通道-连续采集模式
  • 4.DMA模式-单通道下采集代码示例
  • 单通道-单次采集模式
  • 单通道-连续采集模式

  • 一、ADC概念

    1.ADC特点—以STM32F4x系列为例

    💦在使用ADC外设前,先查看下芯片手册,看下ADC的特点。

    2.ADC引脚介绍—以STM32F4x系列为例

    💦ADC的使用,一定是有参考电压的,在使用时要注意,芯片手册上关于ADC参考电压的范围。
    通常正参考电压VREF+连接到VCC,负参考电压VREF-连接到GND

    3.ADC框图简介—以STM32F4x系列为例

    💦通过框图可以了解ADC的工作流程。

    4.ADC参数的简介

      下面是ADC比较常用的参数说明:

    💦(1)测量范围:对于 ADC 来说测量范围就好比尺子的量程,需要采集的信号范围,不能超过 ADC 的测量范围(比如,STM32系列的 ADC 正常就不能超过3.3V)。
    💦(2)分辨率:假如 ADC 的测量范围为 0-3.3V,分辨率设置为12位,分辨率就是最 > 💦低有效位(LSB)的对应输入电压值,分辨率 =3300/4095 ≈ 0.806mV。12位的2进制数最大可以表示4096,ADC采集就是将模拟信号量化为数字信号的过程,3300/4096就是将3.3V分成4096个份,每份电压为0.000805V。所以,分辨率越高,采集到的信号越精确,因此分辨率是衡量 ADC 的一个重要指标。
    💦(3)采样时间:当 ADC 在某时刻采集外部电压信号的时候,此时外部的信号应该保持不变,但实际上外部的信号是不停变化的。所以在 ADC 内部有一个保持电路,保持某一时刻的外部信号,这样 ADC 就可以稳定采集了,保持这个信号的时间就是采样时间。
    💦(4)采样率:也就是在一秒的时间内采集多少次。很明显,采样率越高越好,当采样率不够的时候可能会丢失部分信息,所以 ADC 采样率是衡量 ADC 性能的另一个重要指标。

    5.采样时间和采样率的说明

    💦ADC采样两点间隔的时间一定要大于ADC的采样时间

    💦举例说明:
    STM32F103一般将时钟配置主频为72M、APB2为72M。ADC挂在APB2时钟总线上,且ADC的时钟不能超过14M。所以一般将ADC的分频设置为6,ADC的时钟主频就为72/6=12MHz。那么一个周期就是:1/12MHz=0.0833us。

    💦ADC转换时间 = 采样时间 + 12.5个周期

    💦示例:
    💦当ADC时钟主频为14MHz并且采样时间为1.5个周期时:采样时间 = 1.5 + 12.5 = 14个周期;一个周期的时间为1/14MHz,一共14个周期,1 / 14MHz * 14个周期 = 1us,
    那么此时ADC的采样频率就是1/1us=1000KHz=1MHz,这也是理论上ADC的最大采样频率。

    STM32F1系列的时钟主频一般设置为了12M,采样时间的设置所对应采样频率如下图所示:

    💦从上图可以知道ADC的最小采样时间,当ADC时钟主频为12M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1.17us。
    💦而STM32F031系列的ADC时钟主频为14MHz,采样时间的设置所对应采样频率如下图所示:

    💦当ADC时钟主频为14M并且采样时间为1.5个周期时,ADC采样两点的时间必须大于1us。

    💦举例说明:
    💦确定采样率
      (1)如果我们的输入信号是 20KHz (周期为 50us),若要将它恢复出来,一个周期最少采样20个点,此时采样率要达到400KHz(两个点的时间间隔为2.5us),所以ADC的采样率必须在400KHz 以上。为了达到最好的精度,我们选取ADC时钟为12MHz,即6分频。在12MHz 以及保证采样率的情况下,采样时间越长其,准确性就越好。
      (2)可以计算 2.5us = (12.5 + 采样时间)/ 12MHz ,可以求得采样时间为17.5;所以采样时间的选择必须小于等于17.5个周期,才能保证采样率在400KHz 以上。所以我们可以选择1.5、7.5、13.5,为获得更高的精准度,我们可以选择13.5个周期。
    切记采样点数必须达到要求。

    二、ADC工作模式

    💦1. 轮询模式
    💦2. 中断模式
    💦3. DMA模式

    1.轮询模式

    HAL库函数如下:

    HAL_ADC_Start(ADC_HandleTypeDef* hadc);//轮询模式,需放在循环中不断开启
    HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
    HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout); //等待转换结束,只适用于轮询,注意配置转换时间
    
    

    2.中断模式

    HAL库函数如下:

    
    HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);//中断模式
    HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
    HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc);//中断
    

    3.DMA模式

    HAL库函数如下:

    HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);//DMA模式
    HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc); 
    HAL_ADC_GetValue(ADC_HandleTypeDef* hadc); //读取ADC的值
    HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);//结束后回调
    HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc);//转换过程中回调
    
    

    三、ADC单通道采集-stm32Fx系列为例

    1.轮询模式下单通道采集

    STM32CubeMX配置

    💦ADC模式选择
    通常选择独立模式,在此模式下,ADC相互独立工作。在ADC_CDR寄存器中配置ADC的模式,详情如下。

    💦ADC分频
    用于设置ADC的工作频率。注意ADC的工作频率不能太高,根据实际情况进行选择。

    💦ADC分辨率

    💦ADC对齐方式
    一般选择右对齐方式
    💦ADC的扫描模式
    扫描模式用于多通道转换,单通道的时候不适用。

    💦ADC的连续转换模式
    在连续转换模式下,ADC 结束一个转换后立即启动一个新的转换。
       在CUBE中选中ENABLE就是连续模式,DISABLE就是单次模式。开启连续模式后,ADC的转换不由其他控制。例如将ADC设置为了定时器的TGRO触发采样,如果开启连续模式,ADC将忽略定时器的触发采样。(连续转换模式开启后其实就是满频率的采样)。
    💦ADC的间接转换模式(不连续模式)
    通俗来讲_间断模式: 可以将多个通道进行分组采集,例如你开启了CH0~3这4个通道,假如你设置了间断次数为4,就相当于将4个通道分成了4组,每组1个通道,那么要想采集完这4个通道就需要手动触发4次ADC采集;如果设置了间断次数为2,那么采集完4个通道就需要手动触发2次ADC采集

    💦ADC的DMA
    用于开启DMA模式

    2.轮询模式-单通道下采集代码示例

    单通道-单次采集模式

    //获得ADC值
    //ch: 通道值 0~16,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
    //返回值:转换结果
    uint16_t Get_Adc(uint32_t ch)   
    {
        ADC_ChannelConfTypeDef ADC1_ChanConf;    
        ADC1_ChanConf.Channel=ch;                                   //通道
        ADC1_ChanConf.Rank=1;                                       //第1个序列,序列1
        ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_480CYCLES;        //采样时间
        ADC1_ChanConf.Offset=0;                 
        HAL_ADC_ConfigChannel(&hadc1,&ADC1_ChanConf);        //通道配置
        HAL_ADC_Start(&hadc1);                               //开启ADC	
        HAL_ADC_PollForConversion(&hadc1,10);                //轮询转换 
    	return (uint16_t)HAL_ADC_GetValue(&hadc1);	        //返回最近一次ADC1规则组的转换结果
    }
    //获取指定通道的转换值,取times次,然后平均 
    //times:获取次数
    //返回值:通道ch的times次转换结果平均值
    uint16_t Get_Adc_Average(uint32_t ch,uint8_t times)
    {
    	uint32_t temp_val=0;
    	uint8_t t;
    	for(t=0;t<times;t++)
    	{
    		temp_val+=Get_Adc(ch);
    		HAL_Delay(5);
    	}
    	return temp_val/times;
    }
    
    int main(void)
    {
      uint16_t adc_temp=0;
        float voltage_temp=0;
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_ADC1_Init();
      MX_USART1_UART_Init();
      while (1)
      {    
          adc_temp=Get_Adc_Average(ADC_CHANNEL_0,20);
          voltage_temp=(float)adc_temp*(3.3/4096);
          printf("V:%f\n",voltage_temp);
      }
    }
    

    输入正弦信号,采集结果如下:

    轮询模式,要在循环连续采集

    单通道-连续采集模式

    暂未实现,原因未知

    3.中断模式-单通道下采集代码示例

    单通道-单次采集模式

    产生中断后回进入公共的中断服务函数,完成中断处理,中断服务函数最终会调用ADC转换完成回调函数,所以采集完成后在回调函数中处理。

    在回调函数中,设置一个状态量,在主函数中对此状态量进行查询,当变量为1时,在进行数据的获取和处理。

    uint8_t adc_state=0;
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
    {
        if(hadc==&hadc1)
        {
         adc_state=1;     
        }
    }
    

    下面代码是单次采集模式,转换一次就结束转换,所以要在循环中,开启下一次转换。
    所以要在循环中,开启下一次转换。

    int main(void)
    {
        uint16_t adc_temp=0,adc_cnt=0,temp=0;;
        float voltage_temp=0;
        uint32_t temp_val=0;
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_ADC1_Init();
      MX_USART1_UART_Init();
       HAL_ADC_Start_IT(&hadc1);
      while (1)
      { 
          extern uint8_t adc_state;
          if(adc_state==1)
          {
             adc_state=0;
              temp=HAL_ADC_GetValue(&hadc1);         
              temp_val+=temp;
              HAL_Delay(1);          
              adc_cnt++; 
              HAL_ADC_Start_IT(&hadc1); //开启下一次转换
                if(adc_cnt>=30)
              {  
                  adc_temp=(uint16_t)(temp_val/adc_cnt);
                  adc_cnt=0;
                  temp_val=0;
                  voltage_temp=(float)adc_temp*(3.3/4096);
                  printf("V:%f\n",voltage_temp);
              }                          
          }            
      }
    }
    

    输入正弦信号,采集结果如下:

    单通道-连续采集模式

    将连续转换模式使能,即可开启连续采集模式。

    与单次采集的区别是,开始转换函数HAL_ADC_Start_IT(&hadc1),在主函数初始化时,调用一次即可,开启连续转换。

    int main(void)
    {
        uint16_t adc_temp=0,adc_cnt=0,temp=0;;
        float voltage_temp=0;
        uint32_t temp_val=0;
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_ADC1_Init();
      MX_USART1_UART_Init();
       HAL_ADC_Start_IT(&hadc1);
      while (1)
      { 
          extern uint8_t adc_state;
          if(adc_state==1)
          {
             adc_state=0;
              temp=HAL_ADC_GetValue(&hadc1);         
              temp_val+=temp;
              HAL_Delay(1);          
              adc_cnt++; 
             // HAL_ADC_Start_IT(&hadc1); //开启下一次转换
                if(adc_cnt>=30)
              {  
                  adc_temp=(uint16_t)(temp_val/adc_cnt);
                  adc_cnt=0;
                  temp_val=0;
                  voltage_temp=(float)adc_temp*(3.3/4096);
                  printf("V:%f\n",voltage_temp);
              }                          
          }            
      }
    }
    

    4.DMA模式-单通道下采集代码示例

    单通道-单次采集模式

    产生中断后回进入公共的中断服务函数,完成中断处理,中断服务函数最终会调用ADC转换完成回调函数,所以采集完成后在回调函数中处理。
    在回调函数中,获取数据,可以在回调函数里将数据进行赋值,处理数据可以在主函数中处理,也可以在回调中处理。

       uint16_t adc_dma_temp=0;
        uint8_t adc_dma_cnt=0;
        uint32_t adc_dma_sum=0;
       float voltage_temp=0;
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
    {
        if(hadc==&hadc1)
        {       
              adc_dma_sum+=adc_dma_temp;
              HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_dma_temp, 1);//获取数据后,开启下次转换
              adc_dma_cnt++;
              if(adc_dma_cnt>=20)
              {
                  adc_dma_temp=(uint16_t)(adc_dma_sum/adc_dma_cnt);
                  voltage_temp=(float)adc_dma_temp*(3.3/4096);
                  printf("V:%f\n",voltage_temp);
                  adc_dma_cnt=0;
                  adc_dma_sum=0;             
               }    
        }
    }
    
    int main(void)
    {
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_DMA_Init();
      MX_ADC1_Init();
      MX_USART1_UART_Init();
      /* USER CODE BEGIN 2 */
      extern uint16_t adc_dma_temp;
      HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_dma_temp, 1);//开启一次,让其进入中断
    
      while (1)
      {
          HAL_Delay(5);
      }
    
    }
    

    单通道-连续采集模式

    暂未成功

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用HAL库配置单通道ADC_1的步骤详解

    发表回复