zoukankan      html  css  js  c++  java
  • ADC模数转换(一)——独立模式单通道电压采集实验

     

    图21-1(截自stm32f103参考手册)

    图21-1为ADC的功能框图,下面将围绕这个框图进行解析。

    电压输入范围

    图21-2(截自stm32f103参考手册)

    ADC一般用于采集小电压,其输入值不能超过 V_{DDA} ,即:V_{REF-} le Vin le V_{REFf+} 。相关的定义见图21-2。一般把 V_{SSA}V_{REF-} 接地,V_{DDA}V_{REF+} 接3V3,那么ADC的输入范围是0~3.3V。

    如果想采集到超过阈值的模拟电压,则可改善电路,使其输入ADC的电压在有效范围内即可。

    输入通道

    电压经过输入通道进入ADC。stm32的ADC共有18个通道,其中16个是外部通道,ADCx_IN0~ADCx_IN15。还有两个内部通道。相关的引脚定义和描述可在开发板的数据手册里找。

    而外部的16个通道又分为规则通道(最多16路)和注入通道(最多4路)。一般情况下我们使用规则通道,而注入通道需要在规则通道转换过程中强行插入的,一旦插入后得先等注入通道转换完成再继续规则通道的转换。类似于中断服务。

    转换顺序

    三个规则序列寄存器ADC_SQR1、ADC_SQR2、ADC_SQR3,分别定义着第13~16、第7~12、第1~6个转换,哪个通道想要哪个转换,即可赋值对应通道给相应的转换位。例如,通道0想要第9个转换,则在SQ9[4:0]写0即可。而具体的转换数目则由ADC_SQR1 的L[3:0]决定,最多16个转换。

    而注入序列寄存器只有ADC_JSQR一个,最多支持4个通道,具体转换数目由JL[1:0]位决定。需要注意的是,如果JL位的值小于4(不含4),则转换顺序刚好相反,即第一次转换的是JSQx[4:0](x=4-JL),而不是JSQ1[4:0]。

    触发源

    除了可以用ADC_CR2寄存器的ADON位控制转换的开始与停止,还可以支持触发转换。包括内部定时器触发和外部IO触发。具体的触发源由ADC_CR2的EXTSEL[2:0]位(规则通道触发源)和JEXTSEL[2:0]位(注入通道触发源)控制。

    转换时间

    ADC的输入时钟ADCCLK由PCLK2分频产生,分频因子由RCC_CFGR的ADCPRE[1:0]配置,可配置2/4/6/8分频,分频后的ADCCLK最大应不超过14MHz。

    在进行输入电压的采样时,可以配置ADC_SMPR1(通道0~9)、ADC_SMPR2(通道10~17)寄存器的SMP[2:0]位,来控制采样周期。每个通道可以配置不同的采样周期,最小周期是1.5个。那么,ADC的转换时间为(参考手册里有公式表述):

    T = 采样时间 + 12.5个周期,其中1周期为1/ADCCLK

    例如,ADCCLK分频后为12MHz(PCLK2为72MHz,6分频),采样时间为1.5个周期,则T=14个周期=1.17us。

    ADC_DR和ADC_JDRx

    ADC_DR和ADC_JDRx分别是规则数据寄存器和注入数据寄存器。ADC_DR只有一个,有32位,低16位在单ADC时使用,高16位在ADC1中双模式下保存ADC2转换的规则数据(双模式就是ADC1和ADC2同时使用)。

    规则通道多达16个,而ADC_DR只有一个,在多通道转换的情况下,就需要将前一个时间点转换的数据快速移出,否则会被下一个时间点转换的数据覆盖。这里用到DMA模式,可将数据传输到内存。如图21-3是ADC_DR的寄存器描述。

    图21-3

    注入通道最多只有4个,刚好ADC_JDRx也有4个,每个通道都有对应的寄存器,不会产生数据覆盖问题。如图21-4为ADC_JDRx的寄存器描述。

    图21-4

    由于ADC精度为12位,所以无论在低16位或高16位,都无法完全放满,这时由ADC_CR2的ALIGN位配置转换结果的左对齐或右对齐。

    中断

    分为三种:规则通道转换结束中断、注入通道转换结束中断和模拟看门狗中断。前两种转换结束中断就是普通的根据中断标志位和使能位判断执行。这里说明下模拟看门狗中断。

    使能模拟看门狗中断后,当被ADC转换的模拟电压值低于低阈值或高于高阈值时,便会产生中断。阈值的高低值由ADC_LTR和ADC_HTR配置。

    DMA请求

    规则和注入通道转换结束后会产生DMA请求,用于将转换好的数据传输到内存。需要注意的是,只有ADC1和ADC3可以产生DMA请求。(有关DMA请求可移步stm32:DMA数据传输

    电压转换

    模拟电压转换后是一个12位的数字值,坦诚地讲,这个值人类是看不懂的,所以需要再次转换成可读性较好的电压值,也就是用万用表量到的电压值。

    一般情况下,ADC的输入电压范围在0~3.3v,所以12位满量程对应的电压值为3.3v,数字值为2^12。我们假设ADC转换后的12位的值为x,其对应的电压值为y,那么,

    2^{12} / 3.3 = x / y ,即 y = (3.3*x) / 2^{12}

    所求得的y值便是我们需要的电压值,也就是用万用表测得的值。

    ADC_InitTypeDef

    至此,ADC的基础部分大概清楚了,可以开始进行程序的说明了。先讲讲ADC_InitTypeDef结构体。

    图21-5

    图21-5的结构体截图自stm32f10x_adc.h文件,下面对其结构体成员逐一分析。

    • ADC_Mode:ADC模式,使用一个ADC为独立模式,使用两个ADC为双模式等等。由ADC_CR1寄存器的DUALMOD[3:0]位配置;
    • ADC_ScanConvMode:配置是否使能扫描。如果是单通道AD转换则DISABLE,如果是多通道AD转换则ENABLE。具体由ADC_CR1寄存器的SCAN位配置;
    • ADC_ContinuousConvMode:配置是否自动连续转换。ENABLE为使能自动连续转换,DISABLE为单次转换(转换一次后需要手动控制才能重新启动转换)。具体由ADC_CR2寄存器的CONT位配置;
    • ADC_ExternalTrigConv:外部触发转换,图21-1列出了很多外部触发条件,可根据项目需求配置触发来源。不过我们一般使用软件触发;
    • ADC_DataAlign:转换结果数据对齐模式,可选ADC_DataAlign_Right和ADC_DataAlign_Left,我们一般选择右对齐模式;
    • ADC_NbrOfChannel:AD转换通道数量,根据项目实际配置即可。

    独立模式单通道采集实验

    这个实验被用来实现电位器(滑动变阻器)电压的采集,通过串口将采集到的电压值打印到串口调试助手。这里使用AD转换完成中断,在中断服务函数中读取数据,不使用DMA传输,在多通道采集时才使用DMA传输。

    ADC的GPIO配置

    使能ADC外设的GPIO时钟,将ADC的引脚配置为模拟输入模式。

    查阅数据手册的引脚定义说明(Table 5. High-density STM32F103xx pin definitions),我们可以选择PC1引脚,该引脚支持ADC1/2/3_IN11,后面的ADC配置程序会用到,如图21-6。关于外设引脚模式的配置可查阅参考手册的8.1.11外设的GPIO配置章节,如图21-7。

    图21-6

    图21-7

    ADC配置

    其实就是配置ADC_InitTypeDef结构体的成员,配置成项目需要的。直接贴出代码,比较直观。

    /**
      * @brief  配置ADC工作模式
      * @param  无
      * @retval 无
      */
    static void ADC_Mode_Config(void)
    {
        ADC_InitTypeDef ADC_InitStructure;	
    
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                     // 独立模式
        ADC_InitStructure.ADC_ScanConvMode = DISABLE;                          // 这是单通道实验,不使用扫描                  
        ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                     // 连续转换
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    // 不使用外部触发,使用软件触发
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                 // 转化结果右对齐
        ADC_InitStructure.ADC_NbrOfChannel = 1;                                // 通道数量,这是单通道,所以为1
        ADC_Init(ADC1, &ADC_InitStructure);
    
        RCC_ADCCLKConfig(RCC_PCLK2_Div8);                                      // 配置ADC时钟为8分频,即9MHz
        // 转换通道、转换顺序、采样时间(这里是55.5个周期)
        ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5); 
        ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);                                // 转换结束中断
    
        ADC_Cmd(ADC1, ENABLE);
    
        ADC_ResetCalibration(ADC1);                                            // 初始化ADC校准寄存器
        while(ADC_GetResetCalibrationStatus(ADC1));                            // 等待校准寄存器初始化完成
        ADC_StartCalibration(ADC1);                                            // ADC开始校准
        while(ADC_GetCalibrationStatus(ADC1));                                 // 等待校准完成
    
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);                                // 软件触发ADC转换
    }
    

    相关的分析都在程序注释里了,很容易读懂。这里只说明一个关键点。ADC_RegularChannelConfig()函数用来配置ADC的转换顺序和采样时间等,关于采样时间的周期选择,周期越长,采样精度越高,反之则反。

    中断配置

    关于中断的配置,在之前的文章也介绍了,可移步阅读。这里直接贴出配置代码。

    /**
      * @brief  ADC中断配置
      * @param  无
      * @retval 无
      */
    static void ADC_NVIC_Config(void)
    {
        NVIC_InitTypeDef NVIC_InitStructure;
    
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    
        NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;            // ADC中断源
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    // 抢占优先级为1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;           // 子优先级为1
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    
        NVIC_Init(&NVIC_InitStructure);
    }
    

    中断服务函数

    中断函数一般定义在stm32f10x_it.c文件里,进入中断服务函数后,在函数内直接读取ADC转换结果,并保存在ADC_ConvertedValue变量里,该变量是在main.c文件里定义的。

    extern __IO uint16_t ADC_ConvertedValue;                         // 该变量在main.c文件定义,所以需添加extern关键字
    
    void ADC_IRQHandler(void)
    {	
        if (ADC_GetITStatus(ADC1, ADC_IT_EOC) == SET) 
        {
    	ADC_ConvertedValue = ADC_GetConversionValue(ADC1);       // 读取ADC的转换值
        }
        ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
    }
    

    ADC_GetConversionValue()库函数直接读取ADC_DR寄存器的值。

    最后在main()函数里执行电压转换的公式即可得出我们需要的电压值了。

    ADC_ConvertedValueLocal = (float)ADC_ConvertedValue / 4096 * 3.3;
    

    那么ADC_ConvertedValueLocal 即为我们需要的值了,可以和万用表测量到的值做对比。

  • 相关阅读:
    2-7.交集选择器
    2-6.并集选择器
    2-5.后代选择器
    python----字符串,反向编码与乱码记录
    python----输出1-100之和的方法
    python----基础之三元运算、文件操作
    python----基础之令人头疼的字符编码
    python----基础之可变、不可变数据类型、collections模块
    python----基础之数据类型(元祖,字典,集合)
    python----基础之变量的创建与id
  • 原文地址:https://www.cnblogs.com/fire909090/p/8882549.html
Copyright © 2011-2022 走看看