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.

CC2540 keyfob read analog voltage and use comparator

Other Parts Discussed in Thread: CC2540, CC2533

Hello all,


we are currently working on a project using the CC2540 keyfob (mini dev kit). We would like to read an analog voltage and use a comparator so that only if the voltage has a certain value a signal is sent to the microcontroller and only then a bluetooth signal is transmitted to a smartphone.

We have a few questions regarding this:

Is it possible to use pins P0_6 or P0_7 for reading the analog voltage? Or are these pins used otherwise already?

How can we set these pins as analog input?

I read that the CC2540 has an analog comparator. Is it possible to use it and set a certain voltage as reference voltage so that a bluetooth signal is only sent if the analog voltage has the value 1,2V or greater? How would you program this?

We would be very grateful for your help!

Thanks alot!

  • Hi Johanna,

    Those pins are not connected to anything on the board so feel free to utilize them. Keyfobdemo project (included in the SDK install directory) implements ADC readings which you can use as a template. Take a look at battMeasure function in battservice.c.

    Best Regards

    Joakim

  • In addition to Joakim's advice I would recommend having a look in the CC2540 User Guide: http://www.ti.com/lit/pdf/swru191

    Cheers,
    Fredrik

  • Hi Joakim,

    thanks a lot for your reply!

    The battservice seems to be very similar to what we would like to do. We are still unsure which pin of the CC2540 is used for measuring the voltage of the battery. In the source code we couldn't find the port number initilization which refers to the battery. Would it be possible to show us the expression where the initilization of the "battery" pin is taking place?

    Kind regards

    Miriam & Johanna

  • Hi Miriam and Johanna,

    The battservice measures the VDD voltage (divided by 3) internally on the device and not through an external pin. This is described in the user guide I linked to above.

    Best regards,
    Fredrik

  • Hi Fredrik,

    thank you for your help!

    We now had a closer look at the user guide and the battservice.c and are slightly confused. In the user guide we found the following: "The battery monitor makes it possible to check whether the supply voltage (AVDD5) is above or below a certain programmable level." The way we understand this is that the voltage is measured from AVDD5. Within the source code battservice.c we cannot find this voltage input nor the AVDD5. Could you tell us where in the code this voltage is inserted?

    If we want to measure a voltage from an external pin how would you do this? Would it be possible to change the battservice.c so that we use our voltage from the external pin as input to be measured instead of the battery voltage?

    Kind regards

    Miriam und Johanna

  • Hi Miriam and Johanna,

    The Battery Monitor is a CC2533 specific feature (as stated at the start of the chapter) because this device does not have an ADC.

    The CC2540 uses the ADC to read the supply voltage (also from AVDD5). In battMeasure() the following is done:

    // Configure ADC and perform a read
      HalAdcSetReference( HAL_ADC_REF_125V );
      adc = HalAdcRead( battServiceAdcCh, HAL_ADC_RESOLUTION_10 );

    If you look at the top of battservice.c you will see that battServiceAdcCh is declared as HAL_ADC_CHANNEL_VDD.

    So if you want to measure an external pin instead you need to change the first argument in HalAdcRead(). You can find more details on this by looking into hal_adc.c. I would not change battservice, but rather make some new functions to do the adc reads.

    Reading through (and understanding) the ADC chapter in the User Guide is also helpful.

    Cheers,
    Fredrik

  • Hi Fredrik,

    as we wanted to find out how the adc code behaves we made some changes to the source code and connected a 1.2V voltage across P0.7 and P0.6. Unfortunately we cannot read the voltage level on our smartphone ( before making the changes to the code we were able to read the battery voltage on the smartphone). Below you can see the changes we made to the code. Is there anything obvious which is incorrect?

    in battservice.c:

     * CONSTANTS
     */

    // ADC voltage levels
    //#define BATT_ADC_LEVEL_3V           409
    //#define BATT_ADC_LEVEL_2V           273
    #define VOLT_ADC_LEVEL_12V          490 // maximum voltage 1,2V
    #define VOLT_ADC_LEVEL_1V           409 // minimum voltage 1V

    *LOCAL VARIABLES

    //static uint16 battMinLevel = BATT_ADC_LEVEL_2V; // For VDD/3 measurements
    //static uint16 battMaxLevel = BATT_ADC_LEVEL_3V; // For VDD/3 measurements
    static uint16 voltMinLevel = VOLT_ADC_LEVEL_1V; // MIN For voltage measurements
    static uint16 voltMaxLevel = VOLT_ADC_LEVEL_12V; //MAX For voltage measurements

    // Critical battery level setting
    static uint8 battCriticalLevel;

    // ADC channel to be used for reading
    static uint8 voltServiceAdcCh = HAL_ADC_CHN_A6A7; //  pin 0_7 and pin 0_6 connected to ADC for voltage measurements

     

    //Batt_Setup function

     //battServiceAdcCh = adc_ch;
      voltServiceAdcCh = adc_ch;
      //battMinLevel = minVal;
      //battMaxLevel = maxVal;
     
      voltMinLevel = minVal;
      voltMaxLevel = maxVal;

     

    //BattMeasure function

    // Configure ADC and perform a read
      HalAdcSetReference( HAL_ADC_REF_125V );
      adc = HalAdcRead(voltServiceAdcCh , HAL_ADC_RESOLUTION_10 ); //Channel 6 and 7 is used

      // Call measurement teardown callback
      if (battServiceTeardownCB != NULL)
      {
        battServiceTeardownCB();
      }

      if (adc >= voltMaxLevel)
      {
        percent = 100;
      }
      else if (adc <= voltMinLevel)
      {
        percent = 0;
      }
      /*
      else
      {
        if (battServiceCalcCB != NULL)
        {
          percent = battServiceCalcCB(adc);
        }
        else
        {
          uint16 range =  voltMaxLevel - voltMinLevel + 1;

          // optional if you want to keep it even, otherwise just take floor of divide
          // range += (range & 1);
          range >>= 2; // divide by 4

          percent = (uint8) ((((adc - voltMinLevel) * 25) + (range - 1)) / range);
        }
      }*/

      return percent;

     

     

    in keyfobdemo.c

    //KeyFobApp_Init

      P0DIR = 0x3C; // Port 0 pins P0.0 and P0.1 (buttons) and P0.7 and P.06 as input (to measure voltage),
                    // all others (P0.2-P0.5) as output
      APCFG = 0xC0; //Pin P0.7 and P0.6 as analog input

     

     

     

    A few other questions we had:

    In keyfobdemo.c do we need the piece of code below? As this seems to be using pin7, too, which we are now using for our voltage measurement.

    #if defined ( DC_DC_P0_7 )

      // Enable stack to toggle bypass control on  (DC/DC converter)
      HCI_EXT_MapPmIoPortCmd( HCI_EXT_PM_IO_PORT_P0, HCI_EXT_PM_IO_PORT_PIN7 );

    #endif // defined ( DC_DC_P0_7 )

     

    In battservice.c the function Batt_Setup is defined. Is this function ever used or called? We couldnt find where that would be.

     

    We are very grateful for your help, as we are unfortunately still fairly unexperienced with this CC2540 programming!

    Kind regards,

    Miriam and Johanna

  • I might be an issue that you first try to configure the ADC to have differential input and at the same time configures it to use the internal voltage reference. That will not work. I suggest using single ended input instead.

    Have you tried to debug your code to see what is happening when running the ADC?

  • Hello Fredrik,


    thanks alot! We can now read our analog voltage using the adc and transmitting it via bluetooth to our smartphone.

    The only thing we are struggling with now, is that we would like to get a constant output voltage from the microprocessor. We set one of the pins (P0.6 ) as output and would like to use a timer so that there is only an output every few seconds. Using the debugger this works fine and we can measure the output voltage, but when just using the keyfob with its battery we never get this voltage. Do you know what the problem could be? Is it possible to get a constant output voltage of 3V? This is the code which we adapted so we can use the timer:

    in keyfobdemo.c:

    in function KeyFobApp_ProcessEvent

    if ( events & KFD_BATTERY_CHECK_EVT )
      {
        // Restart timer
        if ( BATTERY_CHECK_PERIOD )
        {
          osal_start_timerEx( keyfobapp_TaskID, KFD_BATTERY_CHECK_EVT, BATTERY_CHECK_PERIOD );
        }

        // perform battery level check
        P0 = 0x43; //Pin P0.6 high only for voltage measurement
        Batt_MeasLevel( );
        P0 = 0x03; //Pin P0.6 low

        return (events ^ KFD_BATTERY_CHECK_EVT);
      }

     

    Thank you for your help!

    Kind regards

    Miriam & Johanna

  • Hi Miriam and Johanna,

    Great that you got the ADC working!

    I am not sure why the code above does not work without the debugger connected. How are you measuring? Do you see the same with and without power saving enabled?

    Also, the proper way to set and clear bit 6 is:

    P0 |= 0x40; (will only affect bit 6 and set it to 1 independent of its current state)

    P0 &= ~0x40; (will only affect bit 6 and clear it to 0 independent of its current state)

    Cheers,
    Fredrik

  • Hi Fredrik,

    we are using a voltmeter for measuring the voltage.

    About the power saving mode: we are not sure if we are using it, as we dont know where in the code we would enable it. Where would you enable the power saving mode? Does the power saving mode have to do with the active mode TX?


    Thank you for your help!

    Kind regards

    Miriam & Johanna

  • We still have the problem using the timer to get a voltage (when just using the battery). When simply setting pin P0.6 as high from the very beginning we get a voltage (also when just using the battery), only when using the timer there is no voltage. Could it be possible that the timer is set incorrectly?

  • Hi,

    Fredrik is technically correct, but as it happens, the GPIO pins are bit-addressable. So you can do P0_6 = 1 and P0_6 = 0.

    Since it works when you set the pin statically, I'll take a stab at guessing that whatever circuit you are powering with this pin doesn't have time to settle. Try to set the pin high, wait some miliseconds (a new event timer) and then do the ADC measurement.

    Best regards,
    Aslak

  • Aslak N. said:
    the GPIO pins are bit-addressable

    I knew that. :-)

  • Hello Aslak,

    we tried to use a new event timer, but it is not working. After setting our pin P0.6 to high we would like to wait for a few seconds until Batt_MeasLevel() is called. Can you tell us whether we implemented this correctly in the code below? How would we need to change it to make it work? We defined KFD_WAIT_LOOP_EVT as 0x0040 and WAIT_LOOP_PERIOD as 10000 .

    if ( events & KFD_BATTERY_CHECK_EVT )

     {

       // Restart timer

       if ( BATTERY_CHECK_PERIOD )

       {

         osal_start_timerEx( keyfobapp_TaskID, KFD_BATTERY_CHECK_EVT, BATTERY_CHECK_PERIOD );

         // perform battery level check

          }

       P0 |= 0x40; //(will only affect bit 6 and set it to 1 independent of its current state)

      /* for(int c= 1; c<=10000; c++) //Wait until pin has reached high

         for(int d=1; d<=10000;d++)

           for(int e=1; e<=1000; e++)

             for(int f=1; f<=1000; f++)

       {}*/

       if( events & KFD_WAIT_LOOP_EVT)

       {

       if(WAIT_LOOP_PERIOD)

       {

         osal_start_timerEx(keyfobapp_TaskID, KFD_WAIT_LOOP_EVT, WAIT_LOOP_PERIOD);

       }

       }

       Batt_MeasLevel( );

       P0 &= ~0x40; //(will only affect bit 6 and clear it to 0 independent of its current state)

       //osal_pwrmgr_device( PWRMGR_BATTERY );//power saving on

       return (events ^ KFD_BATTERY_CHECK_EVT);

     }

    Thank you very much for your help!

    Kind regards

  • Hi,

    I like your multi-level for-loop. However, the perhaps better way to do this would be:

    1. Battery check event happens every some seconds
    2. This event sets P0.6 high
    3. This event starts a timer for a adc event

    if ( events & KFD_BATTERY_CHECK_EVT )
    {
      P0_6 = 1;
      osal_start_timerEx( keyfobapp_TaskID, KFD_BATTERY_CHECK_EVT, BATTERY_CHECK_PERIOD );
      osal_start_timerEx( keyfobapp_TaskID, KFD_ADC_READ_EVT, 10);
      return events ^ KFD_BATTERY_CHECK_EVT;
    }
    
    if ( events & KFD_ADC_READ_EVT)
    {
      Batt_MeasLevel( );
      P0_6 = 0;
      return events & KFD_ADC_READ_EVT;
    }

    You need to handle the two osal events separately - it looks like you checked for one within another.

    BR,
    Aslak

  • Hello Aslak,

    thank you for your help!


    We are using the following code now. It works the first time when the event occurs and we get 3V on pin 6, but every further time (after 20s) where it should set the pin high again, it only shows a very small voltage of about 3mV. Do you have any suggestions or ideas what the problem could be and how to fix it?

    We use  BATTERY_CHECK_PERIOD = 20000ms and WAIT_LOOP_PERIOD = 10000ms

      if ( events & KFD_BATTERY_CHECK_EVT )
    {
      P0_6 = 1;
        if (BATTERY_CHECK_PERIOD)
      {
      osal_start_timerEx( keyfobapp_TaskID, KFD_BATTERY_CHECK_EVT, BATTERY_CHECK_PERIOD );
     
      osal_start_timerEx( keyfobapp_TaskID, KFD_WAIT_LOOP_EVT, WAIT_LOOP_PERIOD);
      }
      return events ^ KFD_BATTERY_CHECK_EVT;
    }
    if ( events & KFD_WAIT_LOOP_EVT)
    {
      Batt_MeasLevel( );
      P0_6 = 0;
      return events & KFD_WAIT_LOOP_EVT;
    }
     

    Kind regards