Showing posts with label ADC. Show all posts
Showing posts with label ADC. Show all posts

Friday, August 5, 2022

How to create periodic timer event and use iADC for EFR32 series 2 to do battery voltage monitor in GSDK 4.x and EmberZnet 7.x

How to create periodic timer event and use iADC for EFR32 series 2 to do battery voltage monitor in GSDK 4.x and EmberZnet 7.x

1. Add the following code in app.c

#define _IADC_

#ifdef _IADC_
#include "em_device.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_iadc.h"
#include "em_gpio.h"
#endif //#ifdef _IADC_

#ifdef _IADC_
#define CLK_SRC_ADC_FREQ          20000000 // CLK_SRC_ADC
#define CLK_ADC_FREQ              10000000 // CLK_ADC - 10MHz max in normal mode
/*
 * Specify the IADC input using the IADC_PosInput_t typedef.  This
 * must be paired with a corresponding macro definition that allocates
 * the corresponding ABUS to the IADC.  These are...
 *
 * GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0
 * GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AODD0_ADC0
 * GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BEVEN0_ADC0
 * GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BODD0_ADC0
 * GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0
 * GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDODD0_ADC0
 *
 * ...for port A, port B, and port C/D pins, even and odd, respectively.
 */
#define IADC_INPUT_0_PORT_PIN     iadcPosInputAvdd;
#define IADC_INPUT_1_PORT_PIN     iadcNegInputGnd;

#define IADC_INPUT_0_BUS          BBUSALLOC
#define IADC_INPUT_0_BUSALLOC     GPIO_BBUSALLOC_BEVEN0_ADC0
#define IADC_INPUT_1_BUS          BBUSALLOC
#define IADC_INPUT_1_BUSALLOC     GPIO_BBUSALLOC_BODD0_ADC0

/*******************************************************************************
 ***************************   GLOBAL VARIABLES   *******************************
 ******************************************************************************/

static volatile int32_t sample;
static volatile double singleResult; // Volts

static sl_sleeptimer_timer_handle_t bat_volt_read_periodic_timer;
static void bat_volt_read_periodic_timer_callback(sl_sleeptimer_timer_handle_t *handle, void *data)
{
  (void)handle;
  (void)data;
  // Start IADC conversion
  IADC_command(IADC0, iadcCmdStartSingle);

  // Wait for conversion to be complete
  while((IADC0->STATUS & (_IADC_STATUS_CONVERTING_MASK
              | _IADC_STATUS_SINGLEFIFODV_MASK)) != IADC_STATUS_SINGLEFIFODV); //while combined status bits 8 & 6 don't equal 1 and 0 respectively

  // Get ADC result
  sample = IADC_pullSingleFifoResult(IADC0).data;

  // Calculate input voltage:
  // For differential inputs, the resultant range is from -Vref to +Vref, i.e.,
  // for Vref = VBGR = 2.42V, and with analog gain = 0.5, 12 bits represents
  // 4.84V full scale IADC range.
  singleResult = (sample * 4.84) / 0xFFF;

}
/**************************************************************************//**
 * @brief  Initialize IADC function
 *****************************************************************************/
void initIADC (void)
{
  // Declare init structs
  IADC_Init_t init = IADC_INIT_DEFAULT;
  IADC_AllConfigs_t initAllConfigs = IADC_ALLCONFIGS_DEFAULT;
  IADC_InitSingle_t initSingle = IADC_INITSINGLE_DEFAULT;
  IADC_SingleInput_t initSingleInput = IADC_SINGLEINPUT_DEFAULT;

  // Enable IADC0 and GPIO clock branches

  /* Note: For EFR32xG21 radio devices, library function calls to
   * CMU_ClockEnable() have no effect as oscillators are automatically turned
   * on/off based on demand from the peripherals; CMU_ClockEnable() is a dummy
   * function for EFR32xG21 for library consistency/compatibility.
   */
  CMU_ClockEnable(cmuClock_IADC0, true);
  CMU_ClockEnable(cmuClock_GPIO, true);

  // Reset IADC to reset configuration in case it has been modified by
  // other code
  IADC_reset(IADC0);

  // Select clock for IADC
  CMU_ClockSelectSet(cmuClock_IADCCLK, cmuSelect_FSRCO);  // FSRCO - 20MHz

  // Modify init structs and initialize
  init.warmup = iadcWarmupKeepWarm;

  // Set the HFSCLK prescale value here
  init.srcClkPrescale = IADC_calcSrcClkPrescale(IADC0, CLK_SRC_ADC_FREQ, 0);

  // Configuration 0 is used by both scan and single conversions by default
  // Use internal bandgap (supply voltage in mV) as reference
  initAllConfigs.configs[0].reference = iadcCfgReferenceInt1V2;
  initAllConfigs.configs[0].vRef = 1210;
  initAllConfigs.configs[0].analogGain = iadcCfgAnalogGain1x;

  // Divides CLK_SRC_ADC to set the CLK_ADC frequency
  initAllConfigs.configs[0].adcClkPrescale = IADC_calcAdcClkPrescale(IADC0,
                                             CLK_ADC_FREQ,
                                             0,
                                             iadcCfgModeNormal,
                                             init.srcClkPrescale);

  // Assign pins to positive and negative inputs in differential mode
  initSingleInput.posInput   = IADC_INPUT_0_PORT_PIN;
  initSingleInput.negInput   = IADC_INPUT_1_PORT_PIN;

  // Initialize the IADC
  IADC_init(IADC0, &init, &initAllConfigs);

  // Initialize the Single conversion inputs
  IADC_initSingle(IADC0, &initSingle, &initSingleInput);

  // Allocate the analog bus for ADC0 inputs
  GPIO->IADC_INPUT_0_BUS |= IADC_INPUT_0_BUSALLOC;
  GPIO->IADC_INPUT_1_BUS |= IADC_INPUT_1_BUSALLOC;
}
#endif//#ifdef _IADC_

2. Add the following codes in emberAfMainInitCallback

#ifdef _IADC_
  initIADC();
  sl_sleeptimer_start_periodic_timer(&bat_volt_read_periodic_timer, 1000, bat_volt_read_periodic_timer_callback, NULL, 0, 0);
#endif //#ifdef _IADC_

3. Battery voltage input to AVDD would be read to singleResult in bat_volt_read_periodic_timer_callback every second.

Tuesday, December 25, 2018

How to do ADC reading on Silicon Labs EFR32

The following steps show you how to do ADC reading on Silicon Labs EFR32. In this example, I will use BRD4151A radio board with mainboard BRD4001A to do ADC reading from PC9 pin.

1.Use Simplicity Studio to new a Z3Light example and open brd4151a_efr32mg1p232f256gm48.heconf to adjust POS_BX to use PC9 pin.




2. Add the following codes in Z3LightSoc_callbacks.c so we can press PB0/PB1 to do ADC reading from PC9 pin. Here we set ADC reference to internal 2.5V and 12-bit resolution so the max measurement value would be 2.5V which max reading from ADC is 4095.

#include
#include "em_device.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_adc.h"

#define adcFreq   16000000

volatile uint32_t sample;
volatile uint32_t millivolts;
 

...

/**************************************************************************//**
 * @brief  Initialize ADC function
 *****************************************************************************/
void initADC (void)
{
  // Enable ADC0 clock
  CMU_ClockEnable(cmuClock_ADC0, true);

  // Declare init structs
  ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
  ADC_InitSingle_TypeDef initSingle = ADC_INITSINGLE_DEFAULT;

  // Modify init structs and initialize
  init.prescale = ADC_PrescaleCalc(adcFreq, 0); // Init to max ADC clock for Series 1

  initSingle.diff       = false;        // single ended
  initSingle.reference  = adcRef2V5;    // internal 2.5V reference
  initSingle.resolution = adcRes12Bit;  // 12-bit resolution
  initSingle.acqTime    = adcAcqTime4;  // set acquisition time to meet minimum requirement

  // Select ADC input.
  initSingle.posSel = adcPosSelAPORT2XCH9;

  ADC_Init(ADC0, &init);
  ADC_InitSingle(ADC0, &initSingle);
}

void emberAfHalButtonIsrCallback(uint8_t button, uint8_t state)
{
  initADC();

  // Start ADC conversion
  ADC_Start(ADC0, adcStartSingle);

  // Wait for conversion to be complete
  while(!(ADC0->STATUS & _ADC_STATUS_SINGLEDV_MASK));

  // Get ADC result
  sample = ADC_DataSingleGet(ADC0);

  // Calculate input voltage in mV
  millivolts = (sample * 2500) / 4096;
}




p.s. Here, we set initSingle.posSel = adcPosSelAPORT2XCH9 since we set POS_BX to use PC9 pin in HW configurator. You can refer to Table 6.9. ADC0 Bus and Pin Mapping in efr32mg1 datasheet.


3. Build and download the code into BRD4151+ BRD4001A.

4. According to Figure 2.1. BRD4151A Radio Board Connector Pin Mapping, PC9 would be mapped to pin 10 on BRD4001A EXP header and we can apply different voltage between 0V-2.5V to test the ADC reading.



Thursday, February 9, 2017

How to add a COAP resource to read ADC input from DIO23 in Contiki cc26xx-web-demo on CC2650 LaunchPad.

To add a COAP resource to read ADC input from DIO23 in Contiki cc26xx-web-demo on CC2650 LaunchPad, you have to add the following code with "+" into your cc26xx-web-demo.c, cc26xx-web-demo.h, coap-server.c, and res-sensors.c.

1. /examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.c
@@ -55,9 +55,15 @@
 #include
 #include
 #include
+
+#include "ti-lib.h"
+#include "driverlib/aux_adc.h"
+#include "driverlib/aux_wuc.h"
+

 /*---------------------------------------------------------------------------*/
 PROCESS_NAME(cetic_6lbr_client_process);
 PROCESS(cc26xx_web_demo_process, "CC26XX Web Demo");
+PROCESS(adc_process, "ADC process");
 /*---------------------------------------------------------------------------*/
 /*
  * Update sensor readings in a staggered fashion every SENSOR_READING_PERIOD
@@ -84,6 +90,9 @@ static struct uip_icmp6_echo_reply_notification echo_reply_notification;
 static struct etimer echo_request_timer;
 int def_rt_rssi = 0;
 #endif
+
+uint16_t singleSample;
+

 /*---------------------------------------------------------------------------*/
 process_event_t cc26xx_web_demo_publish_event;
 process_event_t cc26xx_web_demo_config_loaded_event;
@@ -110,6 +119,9 @@ DEMO_SENSOR(batmon_temp, CC26XX_WEB_DEMO_SENSOR_BATMON_TEMP,
 DEMO_SENSOR(batmon_volt, CC26XX_WEB_DEMO_SENSOR_BATMON_VOLT,
             "Battery Volt", "battery-volt", "batmon_volt",
             CC26XX_WEB_DEMO_UNIT_VOLT);
+DEMO_SENSOR(adc_dio23, CC26XX_WEB_DEMO_SENSOR_ADC_DIO23,
+            "ADC DIO23", "adc-dio23", "adc_dio23",
+            CC26XX_WEB_DEMO_UNIT_VOLT);

 /* Sensortag sensors */
 #if BOARD_SENSORTAG
@@ -464,6 +476,14 @@ get_batmon_reading(void *data)
     }
   }

+  if(adc_dio23_reading.publish) {
+    if(1) {
+      buf = adc_dio23_reading.converted;
+      memset(buf, 0, CC26XX_WEB_DEMO_CONVERTED_LEN);
+      snprintf(buf, CC26XX_WEB_DEMO_CONVERTED_LEN, "%d", singleSample);
+    }
+  }
+

   ctimer_set(&batmon_timer, next, get_batmon_reading, NULL);
 }
 /*---------------------------------------------------------------------------*/
@@ -825,6 +845,7 @@ init_sensors(void)

   list_add(sensor_list, &batmon_temp_reading);
   list_add(sensor_list, &batmon_volt_reading);
+  list_add(sensor_list, &adc_dio23_reading);
   SENSORS_ACTIVATE(batmon_sensor);

 #if BOARD_SENSORTAG
@@ -864,6 +885,7 @@ PROCESS_THREAD(cc26xx_web_demo_process, ev, data)

   /* Start all other (enabled) processes first */
   process_start(&httpd_simple_process, NULL);
+  process_start(&adc_process, NULL);
 #if CC26XX_WEB_DEMO_COAP_SERVER
   process_start(&coap_server_process, NULL);
 #endif
@@ -966,6 +988,56 @@ PROCESS_THREAD(cc26xx_web_demo_process, ev, data)

   PROCESS_END();
 }
+
+PROCESS_THREAD(adc_process, ev, data)
+{
+  PROCESS_BEGIN();
+  static struct etimer et_adc;
+  while(1)
+  {
+         etimer_set(&et_adc, CLOCK_SECOND*5);
+         PROCESS_WAIT_EVENT();
+         if(etimer_expired(&et_adc)) {
+               //intialisation of ADC
+               ti_lib_aon_wuc_aux_wakeup_event(AONWUC_AUX_WAKEUP);
+               while(!(ti_lib_aon_wuc_power_status_get() & AONWUC_AUX_POWER_ON))
+               { }
+
+               // Enable clock for ADC digital and analog interface (not currently enabled in driver)
+               // Enable clocks
+               ti_lib_aux_wuc_clock_enable(AUX_WUC_ADI_CLOCK | AUX_WUC_ANAIF_CLOCK | AUX_WUC_SMPH_CLOCK);
+               while(ti_lib_aux_wuc_clock_status(AUX_WUC_ADI_CLOCK | AUX_WUC_ANAIF_CLOCK | AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY)
+               { }
+               //printf("clock selected\r\n");
+
+               // Connect AUX IO7 (DIO23, but also DP2 on XDS110) as analog input.
+               AUXADCSelectInput(ADC_COMPB_IN_AUXIO7);
+               //printf("input selected\r\n");
+
+               // Set up ADC range
+               // AUXADC_REF_FIXED = nominally 4.3 V
+               AUXADCEnableSync(AUXADC_REF_FIXED,  AUXADC_SAMPLE_TIME_2P7_US, AUXADC_TRIGGER_MANUAL);
+               //printf("init adc --- OK\r\n");
+
+               //Trigger ADC converting
+               AUXADCGenManualTrigger();
+               //printf("trigger --- OK\r\n");
+
+               //reading adc value
+               singleSample = AUXADCReadFifo();
+
+               printf("%d mv on ADC\r\n",singleSample);
+
+               //shut the adc down
+               AUXADCDisable();
+               //printf("disable --- OK\r\n");
+               get_batmon_reading(NULL);
+
+               etimer_reset(&et_adc);
+               }
+  }
+  PROCESS_END();
+}

 

2./examples/cc26xx/cc26xx-web-demo/cc26xx-web-demo.h
@@ -146,6 +146,7 @@
 #define CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_X    12
 #define CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Y    13
 #define CC26XX_WEB_DEMO_SENSOR_MPU_GYRO_Z    14
+#define CC26XX_WEB_DEMO_SENSOR_ADC_DIO23     15
 /*---------------------------------------------------------------------------*/
 extern process_event_t cc26xx_web_demo_publish_event;
 extern process_event_t cc26xx_web_demo_config_loaded_event;

3. /examples/cc26xx/cc26xx-web-demo/coap-server.c
@@ -50,6 +50,7 @@ extern resource_t res_leds;

 extern resource_t res_batmon_temp;
 extern resource_t res_batmon_volt;
+extern resource_t res_adc_dio23;

 extern resource_t res_device_sw;
 extern resource_t res_device_hw;
@@ -133,6 +134,7 @@ PROCESS_THREAD(coap_server_process, ev, data)

   rest_activate_resource(&res_batmon_temp, "sen/batmon/temp");
   rest_activate_resource(&res_batmon_volt, "sen/batmon/voltage");
+  rest_activate_resource(&res_adc_dio23, "adc/dio23");

   rest_activate_resource(&res_device_hw, "dev/mdl/hw");
   rest_activate_resource(&res_device_sw, "dev/mdl/sw");

4. examples/cc26xx/cc26xx-web-demo/resources/res-sensors.c
@@ -111,12 +111,24 @@ res_get_handler_batmon_volt(void *request, void *response, uint8_t *buffer,
                       buffer, preferred_size, offset);
 }
 /*---------------------------------------------------------------------------*/
+static void
+res_get_handler_adc_dio23(void *request, void *response, uint8_t *buffer,
+                            uint16_t preferred_size, int32_t *offset)
+{
+  res_get_handler_all(CC26XX_WEB_DEMO_SENSOR_ADC_DIO23, request, response,
+                      buffer, preferred_size, offset);
+}
+/*---------------------------------------------------------------------------*/

 RESOURCE(res_batmon_temp, "title=\"Battery Temp\";rt=\"C\"",
          res_get_handler_batmon_temp, NULL, NULL, NULL);
 /*---------------------------------------------------------------------------*/
 RESOURCE(res_batmon_volt, "title=\"Battery Voltage\";rt=\"mV\"",
          res_get_handler_batmon_volt, NULL, NULL, NULL);
 /*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+RESOURCE(res_adc_dio23, "title=\"ADC DIO23\";rt=\"mV\"",
+         res_get_handler_adc_dio23, NULL, NULL, NULL);
+/*---------------------------------------------------------------------------*/

 #if BOARD_SENSORTAG
 /*---------------------------------------------------------------------------*/
 /* MPU resources and handler: Accelerometer and Gyro */

Build cc26xx-web-demo for CC2650 LaunchPad by "make TARGET=srf06-cc26xx BOARD=launchpad/cc2650 cc26xx-web-demo.bin" and download cc26xx-web-demo.bin to your CC2650 LaunchPad. Let it join 6lbr and you should see resource adc/dio23 on COAP interface. You can press Get button to get the ADC reading from DIO23. Try to connect different voltage (0-3.6V) to test.


Wednesday, April 6, 2016

Contiki CC2650 ADC demo using DIO23 as ADC input

The following code shows how to use CC2650 DIO23 as ADC input to read analog signal in Contiki OS.

1. Replace the following code to hello-world.c

#include "contiki.h"
#include "ti-lib.h"
#include "driverlib/aux_adc.h"
#include "driverlib/aux_wuc.h"

#include /* For printf() */

#define CLOCK_SECOND 128

char buf[32];
static struct etimer et;

/*---------------------------------------------------------------------------*/
PROCESS(hello_world_process, "Hello world process");
AUTOSTART_PROCESSES(&hello_world_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(hello_world_process, ev, data)
{
  PROCESS_BEGIN();
    unsigned short i;
    uint16_t singleSample;

    etimer_set(&et, CLOCK_SECOND);
    printf("Contiki CC26xx ADC demo using DIO23 as ADC input\r\n");
    while(1)
    {
        if(ev == PROCESS_EVENT_TIMER && etimer_expired(&et)) {
        //intialisation of ADC
        ti_lib_aon_wuc_aux_wakeup_event(AONWUC_AUX_WAKEUP);
        while(!(ti_lib_aon_wuc_power_status_get() & AONWUC_AUX_POWER_ON))
        { }

        // Enable clock for ADC digital and analog interface (not currently enabled in driver)
        // Enable clocks
        ti_lib_aux_wuc_clock_enable(AUX_WUC_ADI_CLOCK | AUX_WUC_ANAIF_CLOCK | AUX_WUC_SMPH_CLOCK);
        while(ti_lib_aux_wuc_clock_status(AUX_WUC_ADI_CLOCK | AUX_WUC_ANAIF_CLOCK | AUX_WUC_SMPH_CLOCK) != AUX_WUC_CLOCK_READY)
        { }

        printf("clock selected\r\n");
       
        // Connect AUX IO7 (DIO23, but also DP2 on XDS110) as analog input.
        AUXADCSelectInput(ADC_COMPB_IN_AUXIO7); 
        printf("input selected\r\n");
       
        // Set up ADC range
        // AUXADC_REF_FIXED = nominally 4.3 V
        AUXADCEnableSync(AUXADC_REF_FIXED,  AUXADC_SAMPLE_TIME_2P7_US, AUXADC_TRIGGER_MANUAL);
        printf("init adc --- OK\r\n");

        //Trigger ADC converting
        AUXADCGenManualTrigger();
        printf("trigger --- OK\r\n");
       
        //reading adc value
        singleSample = AUXADCReadFifo();

        printf("%d mv on ADC\r\n",singleSample);
       
        //shut the adc down
        AUXADCDisable();
        printf("disable --- OK\r\n");

        }
        etimer_set(&et, CLOCK_SECOND);
        PROCESS_YIELD();
    }

  PROCESS_END();
}


2. Build by "make TARGET=srf06-cc26xx BOARD=srf06/cc26xx hello-world.bin" and download hello-world.bin using Flash Programmer 2 to CC2650 LaunchPad.

3. You can see ADC reading output on UART.