This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2530: How to detect when a 3v battery has dropped to <2.5v

Part Number: CC2530
Other Parts Discussed in Thread: CC2533

Hello,

How can I use the CC2530 to detect when a 3v battery has dropped to <2.5v with the indication of a LED? What would the wiring diagram look like and how can I program it starting from scratch?

  • You can use ADC in CC2530 to read battery voltage. You can refer to for ADC reading and you need to use voltage divider circuit to divide battery voltage to less than 1.15V before connecting it to ADC input pin on CC2530.

  • How much current needs to go into the ADC input in order to get a reading? I will use this information to determine the resistors I need for the voltage divider.
  • ADC input doesn't need input current. You just need to connect the voltage after voltage divider to ADC input for ADC reading.
  • Do you mind providing an example sketch code that I could test on my CC2530 board? I would really appreciate it.
  • I meant that if you could provide the entire project including all build files, workspaces, c files (start to finish, including init) , etc? Can you provide the entire project in which I can just download and test on my CC2530?

  • No, I can't provide my project to you and my project only works on my own HW.
  • According to chapter 13 of the CC2530 user guide, I wanted to know which c file I can set BATTMON_PD = 0 in SampleLight?
  • You can use it in any C file.
  • I placed the code in zcl_samplelight.c, but I get an error saying "this declaration has no storage class or type specifier". Why's this so and how can I fix this? According to chapter 13, BATTMON_PD should've been made.
  • If you read CC253x user guide, it says "The battery monitor (in the CC2533 only) enables simple voltage monitoring in the devices that do not
    include an ADC." Do you use CC2533?
  • I am purchasing one soon. So that's why I am looking into this.
  • Do you intend to use CC2533 for Zigbee?
  • Flash size of CC2533 is not enough for Zigbee application.
  • Do you know how to rework the code from the site: https://e2e.ti.com/support/wireless_connectivity/zigbee_6lowpan_802-15-4_mac/f/158/t/68930 to do battery monitoring instead of temperature monitoring?

    8015.7853.CC2530_peripherals.zip

  • I had tested that code but it doesn't work on my CC2530DK.
  • So I've got the code from the site sunmaysky.blogspot.tw/.../cc2530-adc-howto-in-ti-z-stack.html inside of hal_adc.c in the HalAdcRead function. Below is the file of hal_adc.c. There's no warning or errors. So I wanted to know if I placed the code in the correct function and if this is the correct code to use to light an LED once the voltage is below 2.5v?

    P.S. Also below is a picture of my wiring. Is it correct?

    /**************************************************************************************************
     *                                           INCLUDES
     **************************************************************************************************/
    
    #include  "hal_adc.h"
    #include  "hal_defs.h"
    #include  "hal_mcu.h"
    #include  "hal_types.h"
    
    /**************************************************************************************************
     *                                            CONSTANTS
     **************************************************************************************************/
    #define HAL_ADC_EOC         0x80    /* End of Conversion bit */
    #define HAL_ADC_START       0x40    /* Starts Conversion */
    
    #define HAL_ADC_STSEL_EXT   0x00    /* External Trigger */
    #define HAL_ADC_STSEL_FULL  0x10    /* Full Speed, No Trigger */
    #define HAL_ADC_STSEL_T1C0  0x20    /* Timer1, Channel 0 Compare Event Trigger */
    #define HAL_ADC_STSEL_ST    0x30    /* ADCCON1.ST =1 Trigger */
    
    #define HAL_ADC_RAND_NORM   0x00    /* Normal Operation */
    #define HAL_ADC_RAND_LFSR   0x04    /* Clock LFSR */
    #define HAL_ADC_RAND_SEED   0x08    /* Seed Modulator */
    #define HAL_ADC_RAND_STOP   0x0c    /* Stop Random Generator */
    #define HAL_ADC_RAND_BITS   0x0c    /* Bits [3:2] */
    
    #define HAL_ADC_DEC_064     0x00    /* Decimate by 64 : 8-bit resolution */
    #define HAL_ADC_DEC_128     0x10    /* Decimate by 128 : 10-bit resolution */
    #define HAL_ADC_DEC_256     0x20    /* Decimate by 256 : 12-bit resolution */
    #define HAL_ADC_DEC_512     0x30    /* Decimate by 512 : 14-bit resolution */
    #define HAL_ADC_DEC_BITS    0x30    /* Bits [5:4] */
    
    #define HAL_ADC_STSEL       HAL_ADC_STSEL_ST
    #define HAL_ADC_RAND_GEN    HAL_ADC_RAND_STOP
    #define HAL_ADC_REF_VOLT    HAL_ADC_REF_AVDD
    #define HAL_ADC_DEC_RATE    HAL_ADC_DEC_064
    #define HAL_ADC_SCHN        HAL_ADC_CHN_VDD3
    #define HAL_ADC_ECHN        HAL_ADC_CHN_GND
    
    /* ------------------------------------------------------------------------------------------------
     *                                       Local Variables
     * ------------------------------------------------------------------------------------------------
     */
    
    #if (HAL_ADC == TRUE)
    static uint8 adcRef;
    #endif
    
    /**************************************************************************************************
     * @fn      HalAdcInit
     *
     * @brief   Initialize ADC Service
     *
     * @param   None
     *
     * @return  None
     **************************************************************************************************/
    void HalAdcInit (void)
    {
    #if (HAL_ADC == TRUE)
      adcRef = HAL_ADC_REF_VOLT;
    #endif
    }
    
    /**************************************************************************************************
     * @fn      HalAdcRead
     *
     * @brief   Read the ADC based on given channel and resolution
     *
     * @param   channel - channel where ADC will be read
     * @param   resolution - the resolution of the value
     *
     * @return  16 bit value of the ADC in offset binary format.
     *
     *          Note that the ADC is "bipolar", which means the GND (0V) level is mid-scale.
     *          Note2: This function assumes that ADCCON3 contains the voltage reference.
     **************************************************************************************************/
    #include "onboard.h"
    uint16 HalAdcRead (uint8 channel, uint8 resolution)
    {   
      int16  reading = 0;  
      
    
    #if (HAL_ADC == TRUE)
      uint8   i, resbits;
      uint8  adcChannel = 1;
    
      /*
       * If Analog input channel is AIN0..AIN7, make sure corresponing P0 I/O pin is enabled.  The code
       * does NOT disable the pin at the end of this function.  I think it is better to leave the pin
       * enabled because the results will be more accurate.  Because of the inherent capacitance on the
       * pin, it takes time for the voltage on the pin to charge up to its steady-state level.  If
       * HalAdcRead() has to turn on the pin for every conversion, the results may show a lower voltage
       * than actuality because the pin did not have time to fully charge.
       */
      if (channel < 8)
      {
        for (i=0; i < channel; i++)
        {
          adcChannel <<= 1;
        }
      }
      
      uint16 adc_ain2=0; 
      HalAdcSetReference(HAL_ADC_REF_125V);
      adc_ain2=HalAdcRead(HAL_ADC_CHN_AIN2,HAL_ADC_RESOLUTION_10);
      
      uint16 holder = (1.15*adc_ain2)/511; //3v in with voltage divider
      
      if (holder < 1.01){ //2.8v in with voltage divider
        P1_5 = 1;
        for(int i=0; i<50; i++){
          Onboard_wait(10000);
        } 
        P1_5 = 0;
        for(int i=0; i<1000; i++){
          Onboard_wait(10000);
        }
      }
        
      
    
      /* Enable channel */
      ADCCFG |= adcChannel;
    
      /* Convert resolution to decimation rate */
      switch (resolution)
      {
        case HAL_ADC_RESOLUTION_8:
          resbits = HAL_ADC_DEC_064;
          break;
        case HAL_ADC_RESOLUTION_10:
          resbits = HAL_ADC_DEC_128;
          break;
        case HAL_ADC_RESOLUTION_12:
          resbits = HAL_ADC_DEC_256;
          break;
        case HAL_ADC_RESOLUTION_14:
        default:
          resbits = HAL_ADC_DEC_512;
          break;
      }
    
      /* writing to this register starts the extra conversion */
      ADCCON3 = channel | resbits | adcRef;
    
      /* Wait for the conversion to be done */
      while (!(ADCCON1 & HAL_ADC_EOC));
    
      /* Disable channel after done conversion */
      ADCCFG &= (adcChannel ^ 0xFF);
    
      /* Read the result */
      reading = (int16) (ADCL);
      reading |= (int16) (ADCH << 8);
    
      /* Treat small negative as 0 */
      if (reading < 0)
        reading = 0;
    
      switch (resolution)
      {
        case HAL_ADC_RESOLUTION_8:
          reading >>= 8;
          break;
        case HAL_ADC_RESOLUTION_10:
          reading >>= 6;
          break;
        case HAL_ADC_RESOLUTION_12:
          reading >>= 4;
          break;
        case HAL_ADC_RESOLUTION_14:
        default:
          reading >>= 2;
        break;
      }
    #else
      // unused arguments
      (void) channel;
      (void) resolution;
    #endif
    
      return ((uint16)reading);
    }
    
    /**************************************************************************************************
     * @fn      HalAdcSetReference
     *
     * @brief   Sets the reference voltage for the ADC and initializes the service
     *
     * @param   reference - the reference voltage to be used by the ADC
     *
     * @return  none
     *
     **************************************************************************************************/
    void HalAdcSetReference ( uint8 reference )
    {
    #if (HAL_ADC == TRUE)
      adcRef = reference;
    #endif
    }
    
    /*********************************************************************
     * @fn      HalAdcCheckVdd
     *
     * @brief   Check for minimum Vdd specified.
     *
     * @param   vdd - The board-specific Vdd reading to check for.
     *
     * @return  TRUE if the Vdd measured is greater than the 'vdd' minimum parameter;
     *          FALSE if not.
     *
     *********************************************************************/
    bool HalAdcCheckVdd(uint8 vdd)
    {
      ADCCON3 = 0x0F;
      while (!(ADCCON1 & 0x80));
      return (ADCH > vdd);
    }
    
    /**************************************************************************************************
    **************************************************************************************************/
    

  • The schematic looks OK but you should not put ADC sample code in hal_adc.c. You should create a periodic event in your zcl_SampleLight.c and put ADC code in periodic event to read battery voltage.
  • I will type in the periodic function later, but for now, I moved the code into the event loop:

    #include <hal_adc.h>

    uint16 zclSampleLight_event_loop( uint8 task_id, uint16 events )

    {

     //////////////////////////////////////////////////////////

     uint16 adc_ain2=0;

     HalAdcSetReference(HAL_ADC_REF_125V);

     adc_ain2=HalAdcRead(HAL_ADC_CHN_AIN2,HAL_ADC_RESOLUTION_10);

     uint16 holder = (1.15*adc_ain2)/511;

     if (holder < 1.01){

       P1_5 = 1;

     }

    Right now the code says the LED should light up once the voltage drops below 1.01 volts, but when it does, the LED doesn't light up. How can I fix this?

  • If you change battery voltage, do you see different adc_ain2 reading in your code?
  • "do you see different adc_ain2 reading in your code"

    How do I see if the adc_ain2 is reading? Do I look at the debugger?
    Also if I change the battery voltage, the LED still doesn't light up.
  • You can set a breakpoint after ADC reading and run debug to check it.
  • I can only set a breakpoint on the read line. So below is all I got.

  • I changed the code to the below and the LED still doesn't light up when I give it an input of less than 1.15v. I have changed the resistors on the voltage divider multiple times for testing, but it's no good. What do you suggest?

    uint16 adc_ain2=0;
    HalAdcSetReference(HAL_ADC_REF_125V);
    adc_ain2=HalAdcRead(HAL_ADC_CHN_AIN2,HAL_ADC_RESOLUTION_10);

    uint16 holder = (1.15*adc_ain2)/511;

    if(adc_ain2 != 511){
    P1_5 = 1;
    }
    else if(holder == 511){
    P1_5 = 0;
    }
  • What do you mean no good? For example, if you change voltage divider to output 0.8V to ADC input pin, what reading do you see on adc_ain2?
  • You told me to set a breakpoint and that's what I did. I can't see any value. Is there a value I should be seeing on the debugger regarding adc_ain2? Where can I see this value?

  • You can add it to watch window to check the value.
  • When I enter the variable in the watch 1 view, I get the below message. Why can't this variable be found? (The KeyPressCnt variable above can be found)

  • Try to claim adc_ain2 as global variable and debug again.
  • It works now. However the variable always stays at 0 every time it hits the breakpoint. Why doesn't the variable change?

  • Try to use multimeter to check the voltage on P0.2 first.
  • I used a 3v battery with a voltage divider. For 1.08v, R1=100k ohm and R2=56k ohm. For 0.93v, R1=100k ohm and R2=45k ohm. I measured with a multimeter and I did get those voltages. The LED is supposed to light up when the voltage is below 1v, but it doesn't. What do you suggest?
  • Where do you call HalAdcSetReference?
  • In the event loop.
  • If you use a while loop to do ADC reading in zcl_samplelight_init, can you see ADC reading changes?
  • I've been testing the code from the site: e2e.ti.com/.../130649. It appears to be working, but my interpretations of the value g could be wrong. I got a value of 29 (2.9v) for g and that's the voltage I get off my multimeter. Does the code form the site work for ADC battery monitoring?

  • Yes, that code works too. But I see no reason why that code works but it didn't work by using HalAdcRead API.
  • How can I import the time library into IAR so that the CC2530 will check the battery every 24 hrs?
  • You can use a periodic event to do battery reading every 24 hours.
  • How can I do that? Is their a function I can already use?
  • Thanks. Which of the following lines do I change to set the time?

    osal_start_timerEx( zclSampleLight_TaskID, PERIODIC_EVT, 10);
    osal_start_timerEx( zclSampleLight_TaskID, PERIODIC_EVT, 5000 );

    And what value do I change it to be every 24 hrs?
  • You should change the second line parameter 5000 which means 5000 ms. You should change it to 86400000 which means 24 hours.
  • I've been testing the periodic event code more thoroughly and I have found it isn't working the way it is supposed to. The CC2530 checks the battery every second or so. I used an LED to verify this. If the LED keeps on turning on and off instantaneously, that means the periodic event is not working right. Why isn't it waiting for every 10 seconds to check the battery (10 sec for testing)?

    #include <hal_adc.h>
    
    #define KEYHOLD_EVT  0x0100    //define event for key hold
    int KeyPressCnt=0; 
    
    uint16 adc_ain2=0; 
    
    #define HAL_ADC_REF_125V    0x00    /* Internal 1.25V Reference */
    #define HAL_ADC_DEC_064     0x00    /* Decimate by 64 : 8-bit resolution */
    #define HAL_ADC_DEC_128     0x10    /* Decimate by 128 : 10-bit resolution */
    #define HAL_ADC_DEC_512     0x30    /* Decimate by 512 : 14-bit resolution */
    #define HAL_ADC_CHN_VDD3    0x0f    /* Input channel: VDD/3 */
    #define HAL_ADC_CHN_TEMP    0x0e    /* Temperature sensor */
    uint16 value_bat;
    uint16 g;
    char k;
    int i=0;
    
    #define PERIODIC_EVT 0x0001
    
    uint16 zclSampleLight_event_loop( uint8 task_id, uint16 events )
    {
      //////////////////////////////////////////////////////////  
      osal_start_timerEx( zclSampleLight_TaskID, PERIODIC_EVT, 10 );
      if( events & PERIODIC_EVT )
      {
        //Do things that need periodic processing here!
        /* Clear ADC interrupt flag */
        ADCIF = 0;
        ADCCON3 = (HAL_ADC_REF_125V| HAL_ADC_DEC_128 | HAL_ADC_CHN_VDD3);
        /* Wait for the conversion to finish */
        while ( !ADCIF ); 
        /* Get the result */
        value_bat = ADCL;
        value_bat |= ((uint16) ADCH) << 8;
        /*
        * value now contains measurement of Vdd/3
        * 0 indicates 0V and 32767 indicates 1.25V
        * voltage = (value*3*1.25)/32767 volts
        * we will multiply by this by 10 to allow units of 0.1 volts
        */
        value_bat = value_bat >> 6; // divide first by 2^6
        value_bat = (uint16)(value_bat * 37.5);
        value_bat = value_bat >> 9; // ...and later by 2^9...to prevent overflow during multiplication
        g=value_bat;
        
        P0SEL &= ~BV(4);
        P0DIR |= BV(4);
        P0_4=0; //Set P1.5 to low
        
        //////////////////////////////IF the following code keeps on turning the LED on and off every second, that means 
        //////////////////////////////the periodic event isn't working correctly
        
        if(i==0){           //if comes here, LED will turn on
          P0_4=1;
          i=1;
        }
        else if(i==1){      //if comes here, LED will turn off
          P0_4=0;
          i=0;
        }
        
        /*if(g<=25){
          k='b';
          P0_4=1;    
        }
        else if(g>25){
          k='g';
          P0_4=0;    
        }*/
    
        osal_start_timerEx( zclSampleLight_TaskID, PERIODIC_EVT, 100000 ); //supposed to be every 10 seconds
        return (events ^ PERIODIC_EVT);
      }

  • Where do you call osal_start_timerEx to start event first time?
  • I put it in the code below inside of the event loop.

    uint16 zclSampleLight_event_loop( uint8 task_id, uint16 events )
    {
    //////////////////////////////////////////////////////////
    osal_start_timerEx( zclSampleLight_TaskID, PERIODIC_EVT, 10 );
  • You should not put it in event loop. You can put it in zcl_SampleLight_init.
  • I did that, but now the CC2530 never checks the battery. Why is that?
  • If you set a breakpoint in periodic event, does it hit?