[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口映射。
//***************************************************************************** //! \brief 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: //! - \ref ADC_COMPB_IN_VDD1P2V //! - \ref ADC_COMPB_IN_VSSA //! - \ref ADC_COMPB_IN_VDDA3P3V //! - \ref ADC_COMPB_IN_AUXIO7 //DIO9 //! - \ref ADC_COMPB_IN_AUXIO6 //DIO8 //! - \ref ADC_COMPB_IN_AUXIO5 //DIO7 //! - \ref ADC_COMPB_IN_AUXIO4 //DIO6 //! - \ref ADC_COMPB_IN_AUXIO3 //DIO5 //! - \ref ADC_COMPB_IN_AUXIO2 //! - \ref ADC_COMPB_IN_AUXIO1 //! - \ref 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 。AONBatMonBatteryVoltageGet()的函数原型是:[7:0]代表FRAC ,对于小数部分,一个单位代表0.00390625v,小数部分的分辨率仅仅有50mV(TYP) batval = 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的电压变化值。能够精确获取到。
八、结论
实在不知道写什么了,还是打个广告吧。
学挖掘机技术哪家强?