• [BLE]CC2640之ADC功能实现和供电电压的採集


    一、开篇

            Write programs that do one thing and do it well ~~~~~

            发现非常多人关于使用CC2640/CC2650的过程中比較难以应对的问题就是实现ADC。为了方便大家,所以有了本篇博客,都是一些自己的理解。不正确的地方请大家指正。

            TI的这款新品上市不久,还有须要须要更新的地方,尤其是以往以其文档多为优势。而现在到了CC26xx这却差点儿没什么可參考的文档了,大家就等等吧。肯定会越来越完好的。

            本篇主要介绍怎样使用TI官方给的driverlib实现ADC的使用,SCS也能控制ADC的实现,可是这个过于复杂。生产的代码也比較难以理解,所以还是使用driverlib吧。


    三、试验平台

    Software Version:BLE_STACK_CC26XX_2.1.0

    Hardware Version:CC2640/CC2650 

    IDE:IAR 7.40

    四、基础知识

           1) ADC模数转换器,顾名思义也就是输入模拟量输出数字量。我们感知世界上的不论什么事物都是感知的模拟量,可是相应于CPU而言仅仅能识别非0即1的数字量,所以产生了ADC,ADC的实现过程中基本的两个过程就是採用保持和量化(感觉在讲废话)。在外设接口中ADC属于较难得部分了,关于ADC的一些基本概念还是比較多的,当中INL和DNL须要特别注意一下(Ps:我旁边坐了一位专门研究ADC的beauty,是她讲的。平时实验室关于ADC不懂得都找她解决)。由于本篇博文主要是解说关于CC26xx的ADC实现的,它的ADC是内部集成的12位ADC,採样速率高达200Ks/s,所以本篇博文不再具体介绍在ADC芯片选型方面要考虑的參数了,可是本篇涉及的參数肯定是选型时须要考虑的。


            2)以下结合CC26xx的Datasheet介绍一些关于ADC的基本參数,下图是ADC在整个IC内部的位置。由下图可知CC26xx的ADC在RF core内部。由M0核控制,在实现ADC时能够使用官方的driverlib也能够使用官方特有的SCS平台去控制实现ADC的基本功能。CC26xx的Sensor controller的功能还是相当强大的,可惜我还不会使用。在低功耗中使用UART就是靠的它用流控控制的


            3)例如以下图中所看到的。因为INL和DNL(详细含义去百度吧)以及offset的原因,在採集数据时就会存在一个偏差。这个是不可避免的,这个採集数据的偏差还是自行软件处理吧,内部ADC的功耗还是比較低的。



    五、怎样在project中实现ADC

            1、首先你要知道有一个driverlib库的存在。这个里面TI官方给出了使用时调用的API,此处就不在赘述了。路径太长不方便写。

            2、然后CC26xx芯片不是全部的IO口都能够作为的ADC接口使用的。官方在TRM中也给出了介绍。例如以下图。


            3、include库文件

            在simpleBLEPeripheral.c文件里加入例如以下头文件。

    #include <driverlib/aux_adc.h>
    #include <driverlib/aux_wuc.h>
            4、配置ADC(最关键的一步)
            代码实现部分,放在simpleBLEPeripheral.c就可以。ADC不须要像使用UART那样再去配置IO口映射。

    //*****************************************************************************
    //! rief Selects internal or external input for the ADC
    //! Note that calling this function also selects the same input for AUX_COMPB.
    //! param input
    //!     Internal/external input selection:
    //!     - 
    ef ADC_COMPB_IN_VDD1P2V
    //!     - 
    ef ADC_COMPB_IN_VSSA
    //!     - 
    ef ADC_COMPB_IN_VDDA3P3V
    //!     - 
    ef ADC_COMPB_IN_AUXIO7 //DIO9
    //!     - 
    ef ADC_COMPB_IN_AUXIO6 //DIO8
    //!     - 
    ef ADC_COMPB_IN_AUXIO5 //DIO7
    //!     - 
    ef ADC_COMPB_IN_AUXIO4 //DIO6
    //!     - 
    ef ADC_COMPB_IN_AUXIO3 //DIO5
    //!     - 
    ef ADC_COMPB_IN_AUXIO2
    //!     - 
    ef ADC_COMPB_IN_AUXIO1
    //!     - 
    ef ADC_COMPB_IN_AUXIO0
    //*****************************************************************************
    uint32_t AdcOneShotRead(uint8_t auxIo)
    {
        uint32_t turnedOnClocks = 0;
        //////////// Config clock/////////////////////
        // Only turn on clocks that are not already enabled. Not thread-safe, obviously.
        turnedOnClocks |= AUXWUCClockStatus(AUX_WUC_ADC_CLOCK) ?

    0 : AUX_WUC_ADC_CLOCK; turnedOnClocks |= AUXWUCClockStatus(AUX_WUC_ADI_CLOCK) ? 0 : AUX_WUC_ADI_CLOCK; turnedOnClocks |= AUXWUCClockStatus(AUX_WUC_SOC_CLOCK) ? 0 : AUX_WUC_SOC_CLOCK; // Enable clocks and wait for ready AUXWUCClockEnable(turnedOnClocks); while(AUX_WUC_CLOCK_OFF == AUXWUCClockStatus(turnedOnClocks)); /////// Seclect auxIO ///////////// AUXADCSelectInput(auxIo); ////////// Enable /////////// AUXADCEnableSync(AUXADC_REF_FIXED, AUXADC_SAMPLE_TIME_2P7_US, AUXADC_TRIGGER_MANUAL); delay(10); //Scaling disable AUXADCDisableInputScaling(); AUXADCGenManualTrigger(); // Trigger sample uint32_t adcValue = AUXADCReadFifo(); AUXADCDisable();//Power_Saving return adcValue; }

            在上述实现代码中所涉及的一些问题,讲述一下自己的理解,假设不过为了实现ADC功能那么以下能够不用看了,以下讲的比較细了。

            1)首先是ADC使能函数

            跟踪查看函数原型例如以下。

    //*****************************************************************************
    // Enables the ADC for synchronous operation
    //*****************************************************************************
    void AUXADCEnableSync(uint32_t refSource, uint32_t sampleTime, uint32_t trigger)
    {
        // Enable the ADC reference, with the following options:
        // - SRC: Set when using relative reference
        // - REF_ON_IDLE: Set when using fixed reference and sample time < 21.3 us
        uint8_t adcref0 = refSource | ADI_4_AUX_ADCREF0_EN_M;
        if (!refSource && (sampleTime < AUXADC_SAMPLE_TIME_21P3_US)) {
            adcref0 |= ADI_4_AUX_ADCREF0_REF_ON_IDLE_M;
        }
        ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADCREF0, adcref0);
    
        // Enable the ADC clock
        HWREG(AUX_WUC_BASE + AUX_WUC_O_ADCCLKCTL) = AUX_WUC_ADCCLKCTL_REQ_M;
        while (!(HWREG(AUX_WUC_BASE + AUX_WUC_O_ADCCLKCTL) & AUX_WUC_ADCCLKCTL_ACK_M));
    
        // Enable the ADC data interface
        if (trigger == AUXADC_TRIGGER_MANUAL) {
            // Manual trigger: No need to configure event routing from GPT
            HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_NO_EVENT0 | AUX_ANAIF_ADCCTL_CMD_EN;
        } else {
            // GPT trigger: Configure event routing via MCU_EV to the AUX domain
            HWREG(EVENT_BASE + EVENT_O_AUXSEL0) = trigger;
            HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_MCU_EV | AUX_ANAIF_ADCCTL_CMD_EN;
        }
        // Release reset and enable the ADC
        ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M |
                                                     (sampleTime << ADI_4_AUX_ADC0_SMPL_CYCLE_EXP_S));
    }
            用法:AUXADCEnableSync(AUXADC_REF_FIXED, AUXADC_SAMPLE_TIME_2P7_US, AUXADC_TRIGGER_MANUAL)。比較重要的就是第一和第二个參数。

    当中第一个參数AUXADC_REF_FIXED是4.3V,关于这个4.3的由来,TI一直没有给相关的介绍。我认为应该是由电源端引入然后经过内部的boost电路升压到4.3V作为这个參考电压的。当中还有几个能够作为參考电压的,我没有试过。仅仅是用了AUXADC_REF_FIXED这一个,不敢妄下结论,大家能够去实測一下。第二个參数就是所谓的採样时间,其倒数就是採样频率,由于这套代码配置是使用的同步採样(Sync)。所以採样频率的配置就不能像用单独的用timer去尾随实现非同步採样(Async)产生的採样频率多了。仅仅有官方给的几个能够用,感觉这个影响不大。接近即可了,例如以下。

    *****************************************************************************
    // Defines for ADC sampling type for synchronous operation.
    *****************************************************************************
    #define AUXADC_SAMPLE_TIME_2P7_US           3
    #define AUXADC_SAMPLE_TIME_5P3_US           4
    #define AUXADC_SAMPLE_TIME_10P6_US          5
    #define AUXADC_SAMPLE_TIME_21P3_US          6
    #define AUXADC_SAMPLE_TIME_42P6_US          7
    #define AUXADC_SAMPLE_TIME_85P3_US          8
    #define AUXADC_SAMPLE_TIME_170_US           9
    #define AUXADC_SAMPLE_TIME_341_US           10
    #define AUXADC_SAMPLE_TIME_682_US           11
    #define AUXADC_SAMPLE_TIME_1P37_MS          12
    #define AUXADC_SAMPLE_TIME_2P73_MS          13
    #define AUXADC_SAMPLE_TIME_5P46_MS          14
    #define AUXADC_SAMPLE_TIME_10P9_MS          15
            假设使用使用非同步採样(Async)的话。CC26xx就不能进入standby状态了,官方解释例如以下图(应该没有理解错吧)。

            2)Scaling disable

            作用就是缩小ADC的IO口的採样电压的范围。disable以后最大输入电压1.49就达到满量程了(实測),所以这样能够提升单位电压内的分辨率(1.49/4096)。


            函数原型:

    //*****************************************************************************
    // Disables scaling of the ADC input
    //*****************************************************************************
    // Register: ADI_4_AUX_O_ADC1
    // Field:     [0] SCALE_DIS
    // Disable capacitive input voltage scaling. Should only be 1 for test
    // purposes.
    // 0: ADC input is scaled from 0-4.3V to 0-1.4V internally
    // 1: ADC input is not scaled. Do not exceed 1.4V on input
    #define ADI_4_AUX_ADC1_SCALE_DIS                                    0x00000001
    #define ADI_4_AUX_ADC1_SCALE_DIS_BITN                                        0
    #define ADI_4_AUX_ADC1_SCALE_DIS_M                                  0x00000001
    #define ADI_4_AUX_ADC1_SCALE_DIS_S                                           0  */
    //*****************************************************************************
    void
    AUXADCDisableInputScaling(void)
    {
        ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC1, ADI_4_AUX_ADC1_SCALE_DIS_M);
    }
    六、关于精度和測试结果
            TI的 R&D给出了一份他们在实验的測试结果,大家能够依照这个作为比較,实測和这份数据差点儿相同。


    七、附加题(你肯定懂得它的含义)
            什么?不知道“附加题”的意义何在?

    那你肯定没经历过高考的洗礼~~~~

            原本想再单独写一篇博文介绍关于供电电压採集的。可是感觉比較简单就和ADC放在一起讲吧,它俩比較接近。首先,非常多project师在做类似于手环、心率计、HCG等等时可能都须要使用到芯片的ADC功能。又想实时的获取供电电压。可是内部ADC的数量又是有限的。怎样实现多路使用ADC呢,大家首先想到的应该就是切换使用内部ADC,可是在一路正在使用的时候直接切换掉是不是对正在ADC採集的数据导致错误呢。或者说这个切换的时间怎样把握呢。所以CC26xx有了这个Battery Monitor的功能。让project师測量供电电压时不再占用外部adc_io接口。

    关于IO口重映射是怎样实现的,我也不了解(who knows? tell me.),反正认为比較牛掰,用起来很顺手,尤其是在layout布局布线的时候。个人臆測可能是使用电子开关之类的方法切换的吧,电子开关的功耗也很小,搞硬件的就是牛掰。

    为什么这些高科技都是国外的。那么有哪些是“黑科技”掌握在国人手里呢?例如以下就是。国人之骄傲~~~


           好吧,废话不多说。

    关于供电电压的測试问题,这个能够不使用ADC測试了,CC26xx内部有专门測试芯片供电电压的(还有測试芯片温度的,不再赘述測试温度)。


            1、代码实现

            在simpleBLEPeripheral.c文件里加入例如以下头文件。


    #include <driverlib/aon_batmon.h>
            在须要的地方使用例如以下代码获取当前的电池电压。

     //BAT Monitor
    AONBatMonEnable();
    // <int.frac> format size <3.8> in units of volt
    //返回值32位中[10:8]代表INT 。

    [7:0]代表FRAC ,对于小数部分,一个单位代表0.00390625v,小数部分的分辨率仅仅有50mV(TYP) batval = AONBatMonBatteryVoltageGet();

            AONBatMonBatteryVoltageGet()的函数原型是:

    __STATIC_INLINE uint32_t
    AONBatMonBatteryVoltageGet(void)
    {
        uint32_t ui32CurrentBattery;
        ui32CurrentBattery = HWREG(AON_BATMON_BASE + AON_BATMON_O_BAT);
        // Return the current battery voltage measurement.
        return (ui32CurrentBattery >> AON_BATMON_BAT_FRAC_S);
    }

            返回值是依据不同的位代表芯片供电电压的整数部分和小数部分的,具体介绍例如以下。

    //*****************************************************************************
    // Register: AON_BATMON_O_BAT
    //*****************************************************************************
    // Field:  [10:8] INT
    // Integer part:
    // 0x0: 0V + fractional part
    // ...
    // 0x3: 3V + fractional part
    // 0x4: 4V + fractional part
    #define AON_BATMON_BAT_INT_M                                        0x00000700
    #define AON_BATMON_BAT_INT_S                                                 8
    // Field:   [7:0] FRAC
    // Fractional part, standard binary fractional encoding.
    // 0x00: .0V
    // ...
    // 0x20: 1/8 = .125V
    // 0x40: 1/4 = .25V
    // 0x80: 1/2 = .5V
    // ...
    // 0xA0: 1/2 + 1/8 = .625V
    // ...
    // 0xFF: Max
    #define AON_BATMON_BAT_FRAC_M                                       0x000000FF
    #define AON_BATMON_BAT_FRAC_S                                                0
            測试结果:仅仅測试了0.1V的电压变化值。能够精确获取到。

    八、结论

            实在不知道写什么了,还是打个广告吧。

            学挖掘机技术哪家强?

  • 相关阅读:
    数据仓库-数据清洗
    数据仓库-维度模型(模型类型、建模过程)
    数据仓库-维度
    I am going to India on a business trip
    Android O编译前修改文件和目录权限
    apktool介绍
    Android中国官网资源网站
    android7.0后对于detected problems with app native libraries提示框显示
    android7.0后对于file://的限制
    android7.0对于SharedPreferences设置模式的限制
  • 原文地址:https://www.cnblogs.com/yfceshi/p/7199126.html
Copyright © 2020-2023  润新知