Bluetooth Low Energy (LE) and Drivers#

Texas Instruments

2 - 3 hours read

Introduction#

This module covers the basics of how to build a project that involves using TI developed driver functionalities on top of the basic_ble project. We will discuss the recommended approach on how to add these drivers in the SDK to create your own custom application.

The lab activities will walk you through how to use drivers like GPIO, ADC, LGPTimer, Systerm Timer, and Real Time Clock (RTC) while also enabling Bluetooth Low Energy to transmit data from a peripheral device configured as a server. First we will learn how to set the drivers to work correctly and then we will learn how to use them with Bluetooth LE functionalities.

It is recommended to have the TI Drivers API Reference section of the TI BLE5-Stack User’s Guide alongside this lab for details and further information.

Prerequisites#

Hardware#

For this lab, you need one Bluetooth-enabled development board. Supported devices are:

Software and Tools for Development#

Why is it important to execute from BLE task context?#

Processes that run on interrupt routines or RTOS tasks will be able to invoke specific functions and access variables from their own context (stack for instance). Therefore, if we want to do Bluetooth LE operations we first need to change the context to the correct one.

BLEAppUtil_invokeFunctionNoData() is used for this purpose, particularly when dealing with callbacks or handlers that do not require any data to be passed to them, BLEAppUtil_invokeFunction() is used otherwise. This utility function is typically part of a BLE software stack or library, and it simplifies the invocation of specific functions within the BLE application framework.

Please see below for each driver how this function is being used.

Note

If BLEstack functions are called from a different context then the application might go into ICall_abort().

GPIO#

General Purpose Input/Output (GPIO) is a set of pins in the MCU, used to interact with peripherals on a bidirectional way depending on their configuration, either as an input or output pin. The GPIO driver module allows you to manage General Purpose I/O pins via a series of APIs. GPIO pin behavior is usually configured statically, but can also be configured or reconfigured at runtime. We will approach this in next steps. For further information please refer to the GPIO API detailed description located inside the TI Drivers API Reference Section of TI BLE5-Stack User’s Guide.

First, lets make sure we have configured our GPIO as required. The following example demonstrates how to configure a GPIO pin to generate an interrupt and how to toggle a LED on and off within the registered interrupt callback function. Please consider the following:

  1. Pin configuration is handled within SysConfig for this example as shown below. Go to SysConfig → TI DRIVERS → GPIO → Add a new GPIO and configure as following. In this example we are only configuring the GPIO pin (DIO01 in this example) and its defined referenced name, the rest (Mode, Pull, Interrupt, etc) will be configured inside the code for explanatory purposes but can be configured using SysConfig as well.

    ../../_images/GPIO_sysconfig.png

    GPIO Configuration in SysConfig#

Note

In this example we are selecting GPIO pins that are not the Buttons. This is intentional to showcase that any GPIO pin can be configured as explained and can be used for any other applications.

  1. Inside the appMain(void) function in the app_main.c file, include the following lines to configure the GPIO pin you have setup in SysConfig. Here we are configuring the CONFIG_GPIO_0 pin as Input with Pull Down mode (GPIO_CFG_IN_PD), and enabling an interrupt (GPIO_CFG_INT_ENABLE) to be triggered at rising edge (GPIO_CFG_IN_INT_RISING). Lastly, we configure the interrupt and its callback function (gpioButton0Fxn).

    GPIO configuration#
       // Driver header file
       #include <ti/drivers/GPIO.h>
       // TI Drivers Configuration
       #include "ti_drivers_config.h"
    
       void gpioButton0Fxn(uint_least8_t index)
       {
          // Toggle the LED
          GPIO_toggle(CONFIG_GPIO_LED_RED);
       }
    
       void appMain(void)
       {
          // One-time init of GPIO driver
          GPIO_init();
          // Configure a button pin as input and configure its interrupt
          // Passing INT_ENABLE means you do not need to also call GPIO_enableInt()
          GPIO_setConfig(CONFIG_GPIO_0, GPIO_CFG_IN_PD | GPIO_CFG_IN_INT_RISING | GPIO_CFG_INT_ENABLE);
          GPIO_setCallback(CONFIG_GPIO_0, gpioButton0Fxn);
    
          // Call the BLEAppUtil module init function
          BLEAppUtil_init(&criticalErrorHandler, &App_StackInitDoneHandler, &appMainParams, &appMainPeriCentParams);
       }
    
  2. Build and flash the code. Test your solution by inputting a high voltage (3.3v) into the configured GPIO pin. The Red LED should toggle.

Second, now that we know that our GPIO interrupt is set correctly, lets do some Bluetooth LE stuff! In this example, we will trigger a notification from a specific characteristic of our basic_ble project. Notifications are important mechanisms for fast and asynchronous transmission of data from the server-side to the central. For the implementation, please consider the following:

  1. Inside the Profiles folder, open the app_simple_gatt.c file and add a new function that will send a notification. Due to the fact that we are using the GATT table defined in the basic_ble project, we will use the Characteristic 4 (handle number 46 - HANDLE_CHAR_4) that is configured to send notifications.

       extern uint8 value_gpio_level;
       #define HANDLE_CHAR_4 46
    
       void NotifyTrigger() {
    
          uint8_t status;
          attHandleValueNoti_t noti;
          uint8_t len = 1;
          noti.pValue = GATT_bm_alloc(0, ATT_HANDLE_VALUE_NOTI, len, NULL);
          if (noti.pValue != NULL)
          {
             noti.handle = HANDLE_CHAR_4;
             noti.len = len;
             memcpy(noti.pValue, &value_gpio_level, len);
    
             // Send notification
             status = GATT_Notification(0, &noti, FALSE);
             if (status != SUCCESS)
             {
                GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);
             }
          }
          else
          {
             status = bleMemAllocError;
          }
       }
    
  2. Inside the callback function (gpioButton0Fxn) app_main.c, we will invoke the new function we have created but within the BLEstack context. The function BLEAppUtil_invokeFunctionNoData() is especially important to achieve this. To make it more interesting, we will transmit a dynamic value value_gpio_level. This value will be updated by reading the left button (CONFIG_GPIO_BUTTON_0_INPUT) GPIO digital level. This button is already configured in SysConfig by default in the basic_ble project, therefore there is no need to declare something else.

       uint8 value_gpio_level = 0;
    
       void gpioButton0Fxn(uint_least8_t index)
       {
          // Toggle the LED
          GPIO_toggle(CONFIG_GPIO_LED_RED);
          value_gpio_level = GPIO_read(CONFIG_GPIO_BUTTON_0_INPUT);
          BLEAppUtil_invokeFunctionNoData(NotifyTrigger);
       }
    
  3. Build the project and flash the device.

  4. To verify your implementation you will need a central device where you can see the notification being received. We suggest using the SimpleLink™ Connect Mobile Application. If you are new to the mobile application, please visit our lab on TI SimpleLink™ Connect App.

  5. Establish a connection between the device and the mobile application and go inside TI Simple Peripheral Service. Search for Characteristic 4 and click on Enable (this option will enable the central to receive notifications). If you generate an interrupt (by inputting a high voltage (3.3v) into the configured GPIO pin) you should see a 01 (logic level HIGH) value displayed. This makes sense considering the button is configured with Pull-Up mode. On the contrary, if you press the left button, you should see a 00 (logic level HIGH) value displayed.

Congrats! You have been able to send information through Bluetooth LE triggered using a GPIO interrupt.

TIMERS#

LGPTIMER#

The General Purpose Timer (LGPT) LGPT is often used for high accuracy measurement, timer comparison, DMA triggering, PWM, etc. The LGPTimer driver allows for asynchronous callbacks after a certain amount of time has elapsed. The LGPT peripherals support different timer counter widths depending on the peripheral instance number (LGPT0, LGPT1, LGPT2, LGPT3). The timer counter clock is sourced from the internal prescaler stage which has the high frequency clock (CLKSVT) as input and is only available in Active or Idle mode. The prescaler can be configured to divide the input system clock, effectively extending the maximal time interval for the timer counter while reducing the timer resolution. For further information please refer to the LGPTimerLPF3 API detailed description located inside the TI Drivers API Reference Section of TI BLE5-Stack User’s Guide.

First, lets make sure we have configured our LGPTimer as required. The following example demonstrates how to configure a LGPT timer to generate an interrupt approximately every 2.5 seconds and how to toggle a LED on and off within the registered interrupt callback function. Please consider the following:

  1. LGPT configuration is handled within SysConfig for this example as shown below. Go to SysConfig → TI DRIVERS → LGPTimer → Add a new LGPTimer and configure as following. In this example we using the LGPT3 as it allows for the longest bit-counter.

    ../../_images/TIMER_sysconfig.png

    GPIO Configuration in SysConfig#

Note

In this example we are selecting LGPTimer…

  1. Inside the appMain(void) function in the app_main.c file, include the following lines to configure the LGPTimer you have setup in SysConfig. Here we are configuring the CONFIG_LGPTIMER_0 timer with a clock source prescaled by 256 (params.prescalerDiv) and a counter (counterTarget) set to count up to 4800000 before triggering. If we do the math, the Timer will trigger every counterTarget ÷ sourceclock frequency (48 MHz) ÷ prescalerDiv. In addition, we set the software callback routine that will be called upon the timer interrupt.

    GPIO configuration#
    #include <ti/drivers/timer/LGPTimerLPF3.h>
    
    extern void NotifyTrigger();
    
    void timerCallback(LGPTimerLPF3_Handle lgptHandle, LGPTimerLPF3_IntMask interruptMask) {
       // interrupt callback code goes here. Minimize processing in interrupt.
       GPIO_toggle(CONFIG_GPIO_LED_RED);
    }
    
    void appMain(void)
    {
       LGPTimerLPF3_Handle lgptHandle;
       LGPTimerLPF3_Params params;
       uint32_t counterTarget;
       // Initialize parameters and assign callback function to be used
       LGPTimerLPF3_Params_init(&params);
       params.hwiCallbackFxn = timerCallback;
       params.prescalerDiv = 256;
       // Open driver
       lgptHandle = LGPTimerLPF3_open(0, &params);
       // Set counter target
       counterTarget = 4800000 - 1;  // 2560 ms with a system clock of 48 MHz
       LGPTimerLPF3_setInitialCounterTarget(lgptHandle, counterTarget, true);
       // Enable counter target interrupt
       LGPTimerLPF3_enableInterrupt(lgptHandle, LGPTimerLPF3_INT_TGT);
       // Start counter in count-up-periodic mode
       LGPTimerLPF3_start(lgptHandle, LGPTimerLPF3_CTL_MODE_UP_PER);
    
       // Call the BLEAppUtil module init function
       BLEAppUtil_init(&criticalErrorHandler, &App_StackInitDoneHandler,
                      &appMainParams, &appMainPeriCentParams);
    }
    
  2. Build and flash the code. You should be able to see the Red LED toggling every 2.5 seconds approximately.

Second, now that we know that our LGPTimer interrupt is set correctly, lets do some Bluetooth LE stuff! In this example, we will trigger a notification from a specific characteristic of our basic_ble project. Please consider the following:

  1. Inside the Profiles folder, open the app_simple_gatt.c file and add a new function that will send a notification. Due to the fact that we are using the GATT table defined in the basic_ble project, we will use the Characteristic 4 (handle number 46 - HANDLE_CHAR_4) that is configured to send notifications.

       extern uint8 value_gpio_level;
       #define HANDLE_CHAR_4 46
    
       void NotifyTrigger() {
    
          uint8_t status;
          attHandleValueNoti_t noti;
          uint8_t len = 1;
          noti.pValue = GATT_bm_alloc(0, ATT_HANDLE_VALUE_NOTI, len, NULL);
          if (noti.pValue != NULL)
          {
             noti.handle = HANDLE_CHAR_4;
             noti.len = len;
             memcpy(noti.pValue, &value_gpio_level, len);
    
             // Send notification
             status = GATT_Notification(0, &noti, FALSE);
             if (status != SUCCESS)
             {
                GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);
             }
          }
          else
          {
             status = bleMemAllocError;
          }
       }
    
  2. Inside the callback function (timerCallback) app_main.c, we will invoke the new function we have created but within the BLEstack context. The function BLEAppUtil_invokeFunctionNoData() is especially important to achieve this. To make it more interesting, we will make transmit a dynamic value value_gpio_level. This value will be updated by reading the left button (CONFIG_GPIO_BUTTON_0_INPUT) GPIO digital level. This button is already configured in SysConfig by default in the basic_ble project, therefore there is no need to declare something else.

       uint8 value_gpio_level = 0;
    
       void timerCallback(uint_least8_t index)
       {
          // Toggle the LED
          GPIO_toggle(CONFIG_GPIO_LED_RED);
          value_gpio_level = GPIO_read(CONFIG_GPIO_BUTTON_0_INPUT);
          BLEAppUtil_invokeFunctionNoData(NotifyTrigger);
       }
    
  3. Build the project and flash the device.

  4. To verify your implementation you will need a central device where you can see the notification being received. We suggest using the SimpleLink™ Connect Mobile Application. If you are new to the mobile application, please visit our lab on TI SimpleLink™ Connect App.

  5. Establish a connection between the device and the mobile application and go inside TI Simple Peripheral Service. Search for Characteristic 4 and click on Enable (this option will enable the central to receive notifications). You should be able to see the logic level of the button GPIO displayed and updated every 2.5 seconds approximately.

Congrats! You have been able to send information through Bluetooth LE triggered using a LGPTimer interrupt.

System Timer (SYSTIM)#

SYSTIM has 5 channels from which 4 channels are reserved by system software and radio operation. Different to the LGPTimer, the SYSTIM is available in Active mode, Idle mode, and Standby mode and it is synchronized to RTC in hardware.

In general, it is recommended to use ClockP Module if users want to realize the functionality provided by SYSTIM. The ClockP module, uses the available SYSTIM channel with 1 us resolution and is synchronized with the RTC. Among other things such as allowing us to get current timestamp and get CPU clock frequency, ClockP can be used to create a periodic timer to trigger periodic tasks. For further information please refer to the ClockP API detailed description located inside the TI Drivers API Reference Section of TI BLE5-Stack User’s Guide.

Note

The main difference between using ClockP over LGPTimer is that the first one runs on the System Timer which is capable of running in the background, even when the device is in standby while the LGPTimer runs in active mode.

  1. We will modify the LGPTimer example to use the ClockP module instead. Inside the appMain(void) function in the app_main.c file, include the following lines to configure the ClockP module instead of the LGPTimer. Here we are configuring the clockTicks to 5000 ms by setting TIME_UNIT to 5000 and the CLOCK_UNITS_MS to 1000 based on the value configTICK_RATE_HZ defined inside FreeRTOSConfig.h which is also 1000. In addition, we setup the callback routine clockHandler that will be triggered when the number of ticks (set in ClockP_setTimeout()) have reached the same value as clockTicks, and the period to 0 (one-shot timer) as the ClockP_start() function is being called constantly from the callback.

    ClockP configuration#
    #include <ti/drivers/dpl/ClockP.h>
    
    static ClockP_Struct clkStruct;
    ClockP_Handle clkHandle;
    ClockP_Params clockpParams;
    
    #define TIME_UNIT 5000
    #define CLOCK_UNITS_MS 1000
    
    extern void NotifyTrigger();
    uint8 value_gpio_level = 0;
    
    static void clockHandler(void)
    {
       GPIO_toggle(CONFIG_GPIO_LED_RED);
       value_gpio_level = GPIO_read(CONFIG_GPIO_BUTTON_0_INPUT);
       if(!ClockP_isActive(clkHandle))
       {
          int32_t clockTicks = TIME_UNIT * (CLOCK_UNITS_MS);
          ClockP_setTimeout(clkHandle, clockTicks);
          ClockP_start(clkHandle);
       }
       BLEAppUtil_invokeFunctionNoData(NotifyTrigger);
    }
    
    void appMain(void)
    {
       ClockP_Params_init(&clockpParams);
       uint32_t clockTicks = TIME_UNIT * (CLOCK_UNITS_MS);
       clockpParams.period = 0;  // set to use one-shot timer
       clockpParams.startFlag = true;
       clockpParams.arg = (uintptr_t)clockHandler;
       // Initialize clock instance.
       clkHandle = ClockP_construct(&clkStruct, (void *)BLEAppUtil_invokeFunctionNoData, clockTicks, &clockpParams);
       
       // Call the BLEAppUtil module init function
       BLEAppUtil_init(&criticalErrorHandler, &App_StackInitDoneHandler, &appMainParams, &appMainPeriCentParams);
    }
    

Note

Another way of generating a periodic time trigger is to set the clockpParams.period to the value of clockTicks. This way, it will not be necessary to re-start the ClockP counter by calling ClockP_setTimeout and ClockP_start inside the callback every time.

  1. Build the project and flash the device.

  2. To verify your implementation you will need a central device where you can see the notification being received. We suggest using the SimpleLink™ Connect Mobile Application. If you are new to the mobile application, please visit our lab on TI SimpleLink™ Connect App.

  3. Establish a connection between the device and the mobile application and go inside TI Simple Peripheral Service. Search for Characteristic 4 and click on Enable (this option will enable the central to receive notifications). You should be able to see the logic level of the button GPIO displayed and updated every 5 seconds approximately.

Congrats! You have been able to send information through Bluetooth LE triggered using the ClockP module.

Real Time Clock (RTC)#

The RTC is a 67-bit, 2-channel timer running on the Low Frequency Clock. The RTC is active in stanby and active power states. We will learn how to fetch the timestamp by directly reading from the RTC registers. You can find more information about these registers (TIME8U and TIME524M) inside the TI CC2340R5 - Technical Reference Manual in Chapter 12.

  • RTC.TIME8U has a range of approximately 9.5 hours with an LSB representing 8 microseconds.

  • RTC.TIME524M has a range of approximately 71.4 years with an LSB representing 524 milliseconds.

The main reason from reading the RTC registers directly is to use the TIME524M if a longer range of time is needed.

We will also use this opportunity to show how to transmit data through Bluetooth LE advertisement report, and therefore without the need to establish a connection.

  1. Go to app_peripheral.c and add a new event called BLEAPPUTIL_ADV_END to the peripheralAdvHandler. This will allow us to receive an event from the BLEstack everytime an advertisement is finished.

    Adding new event to peripheralAdvHandler#
    BLEAppUtil_EventHandler_t peripheralAdvHandler =
    {
       .handlerType    = BLEAPPUTIL_GAP_ADV_TYPE,
       .pEventHandler  = Peripheral_AdvEventHandler,
       .eventMask      = BLEAPPUTIL_ADV_START_AFTER_ENABLE |
                         BLEAPPUTIL_ADV_END_AFTER_DISABLE |
                         BLEAPPUTIL_ADV_END
    };
    
  2. Still in app_peripheral.c, collect the time when the first advertisement is first enabled by using the BLEAPPUTIL_ADV_START_AFTER_ENABLE case event inside the Peripheral_AdvEventHandler() function.

    Fetch RTC Timestamp after Advertisement is first enabled#
       case BLEAPPUTIL_ADV_START_AFTER_ENABLE:
       {
             // Read the TIME8U register.
             RTC_cnt_8U = HWREG(RTC_BASE + RTC_O_TIME8U) & 0xFFFFFFFF;
             // Read the TIME524M register.
             RTC_cnt_524M = HWREG(RTC_BASE + RTC_O_TIME524M) & 0xFFFFFFFF;
             // Both registers (524M and 8U) are latched from the same RTC register, but this happen between 1-2 clock cycles apart.
             // The two registers have some shared bits, and the loop assumes that if the shared bits are equal,
             // then the values read from 524M and 8U are synchronized (values latched at same clock cycle).
             while ((RTC_cnt_524M & 0xFFFF) != (RTC_cnt_8U >> 16) & 0xFFFF)
             {
                RTC_cnt_8U = HWREG(RTC_BASE + RTC_O_TIME8U);
                RTC_cnt_524M = HWREG(RTC_BASE + RTC_O_TIME524M);
             }
             // Calculate the RTC time in mili seconds.
             RTCTime_msec = (RTC_cnt_524M * 524) + (RTC_cnt_8U & 0xFFFF)*8/1000;
             timestamp_when_start_adv = (uint32_t)RTCTime_msec/1000;
          break;
       }
    
  3. In addition, add the new case scenario inside the Peripheral_AdvEventHandler() function to change the advertisement report data after every SKIP_ADV advertisements (BLEAPPUTIL_ADV_END). Therefore the time in between transmitting a timestamp will be calculated as advertisement_interval (ms) x SKIP_ADV seconds. In addition, we fetch the RTC ticks timestamp (timestamp_when_adv) and calculate the overall timestamp (rtc_overall) which is: timestamp_when_start_adv - timestamp_when_end_adv.

    Fetch RTC Timestamp after every Advertisement#
          case BLEAPPUTIL_ADV_END:
          {
                update_rtc++;
    
                   if(update_rtc > SKIP_ADV){
    
                      // This function will automatically disable advertising for all advertising handles
                      // that use this buffer and allows to update the contents of a data buffer that is 
                      // currently being used by multiple advertising sets.
                      GapAdv_prepareLoadByBuffer(advData1, FALSE);
    
                      RTC_cnt_8U = HWREG(RTC_BASE + RTC_O_TIME8U) & 0xFFFFFFFF;
                      RTC_cnt_524M = HWREG(RTC_BASE + RTC_O_TIME524M) & 0xFFFFFFFF;
                      while ((RTC_cnt_524M & 0xFFFF) != (RTC_cnt_8U >> 16) & 0xFFFF)
                      {
                            RTC_cnt_8U = HWREG(RTC_BASE + RTC_O_TIME8U);
                            RTC_cnt_524M = HWREG(RTC_BASE + RTC_O_TIME524M);
                      }
                      RTCTime_msec = (RTC_cnt_524M * 524) + (RTC_cnt_8U & 0xFFFF)*8/1000;
                      timestamp_when_end_adv = (uint32_t)RTCTime_msec/1000;
    
                      rtc_overall =  (timestamp_when_start_adv - timestamp_when_end_adv);
                      // Each of the advertisement report fields is 1-byte long, therefore
                      // we need to segment the data accordingly.
                      advData1[11] = (rtc_overall & 0xFF000000)>>24;
                      advData1[12] = ((rtc_overall & 0x00FF0000)>>16);
                      advData1[13] = ((rtc_overall & 0x0000FF00)>>8);
                      advData1[14] = ((rtc_overall & 0x000000FF));
    
                      // The function will automatically re-enable advertising for all handles that use this buffer.
                      // Consider the size of the advertisement report (which is 15 in this example).
                      GapAdv_loadByBuffer(15, advData1);
                      update_rtc = 0;
                   }
                break;
          }
    

Note

Please take into account that the advertisement report structure may be different depending on your configuration. You can take a look at the advData1 structure inside SysConfig → Show Generated Files (< > symbol on upper right corner) → ti_ble_config.c and search for advData1[]. The field we can use to add this information (timestamp in our example) is the Additional Data section. Make sure to create an advertisement structure that already accounts for the amount of data you will input inside the Additional Data as you will need to modify the already existing memory addresses. Please take a look at the BLE Scanning and Advertising lab for further reference on this.

  1. Make sure to declare the required libraries, variables and definitions.

    RTC configuration#
             #include <ti/drivers/dpl/ClockP.h>
             #include <ti/drivers/GPIO.h>
             uint32_t timestamp_when_adv = 0;
             extern uint32_t timestamp_when_connect;
             uint32_t rtc_overall = 0;
             char update_rtc = 0;
             uint8 RTC_master;
             #define SKIP_ADV 10
    
             // RTC Lower Time Slice
             #define RTC_O_TIME8U           0x00000018U // TIME8U register address.
             // RTC Upper Time Slice
             #define RTC_O_TIME524M         0x0000001CU // TIME524M register address.
             uint64_t RTC_cnt_524M;
             uint64_t RTC_cnt_8U;
             uint64_t RTCTime_msec = 0;
    
  2. Build the project and flash the device.

  3. To verify your implementation you will need a central device where you can receive and parse the advertisement report. We suggest visiting our lab on BLE Scanning and Advertising.

  4. Establish a connection between the device and the mobile application and go inside TI Simple Peripheral Service. Search for Characteristic 4 and click on Enable (this option will enable the central to receive notifications). You should be able to see the logic level of the button GPIO displayed and updated every 5 seconds approximately.

Congrats! You have been able to send RTC timestamp information through Bluetooth LE advertisement packets. Remember that a similar approach can be used to transmit data coming from other sources.

Note

As you have seen, we have transmitted data as part of the advertisement report, therefore without the need of establishing a connection. In the same way, you can transmit data using the other drivers we have seen so far by using this method. However, as you can expect, there are limitations and considerations to take into account, some of them are: limited payload (max 33 bytes of advertisement report data and 252 with Advertisement Extension) and no data encryption (only supported through connection followed by pairing/bonding).

ADC#

The Analog to Digital Conversion (ADC) driver supports sampling and converting raw values into microvolts. The next example will use the LGPTimer configured previously in order to trigger an ADC conversion. However, you can use the ClockP as well.

We will replace a simple GPIO read with a value coming from the ADC! For further information please refer to the ADC API detailed description located inside the TI Drivers API Reference Section of TI BLE5-Stack User’s Guide.

  1. ADC configuration is handled within SysConfig for this example as shown below. Go to SysConfig → TI DRIVERS → ADC → Add a new ADC instance and configure as following.

    ../../_images/ADC_sysconfig.png

    ADC Configuration in SysConfig#

  2. Inside the appMain(void) function in the app_main.c file, add the following lines to configure the ADC you have setup in SysConfig and modify the timer callback to read the ADC value. You would have to change the variable value_gpio_level type to uint16_t as this is required by the ADC if configured for 12-bits sample.

    ADC configuration#
    #include <ti/drivers/timer/LGPTimerLPF3.h>
    #include <ti/drivers/ADC.h>
    ADC_Params ADCparams;
    ADC_Handle adcHandle;
    extern uint16_t value_gpio_level;
    
    void timerCallback(LGPTimerLPF3_Handle lgptHandle, LGPTimerLPF3_IntMask interruptMask) {
       // interrupt callback code goes here. Minimize processing in interrupt.
       GPIO_toggle(CONFIG_GPIO_LED_RED);
       ADC_convert(adcHandle, &value_gpio_level);
       BLEAppUtil_invokeFunctionNoData(NotifyTrigger);
    }
    
    void appMain(void)
    {
       //...Timer Config...//
    
       // One-time init of ADC driver
       ADC_init();
       // initialize optional ADC parameters
    
       ADC_Params_init(&ADCparams);
       ADCparams.isProtected = true;
       // Open ADC channels for usage
       adcHandle = ADC_open(CONFIG_ADC_0, &ADCparams);
    
       // Call the BLEAppUtil module init function
       BLEAppUtil_init(&criticalErrorHandler, &App_StackInitDoneHandler,
                      &appMainParams, &appMainPeriCentParams);
    }
    
  3. Build the project and flash the device.

  4. To verify your implementation you will need a central device where you can see the notification being received. We suggest using the SimpleLink™ Connect Mobile Application. If you are new to the mobile application, please visit our lab on TI SimpleLink™ Connect App.

  5. Establish a connection between the device and the mobile application and go inside TI Simple Peripheral Service. Search for Characteristic 4 and click on Enable (this option will enable the central to receive notifications). You should be able to see the ADC value displayed. If the application is configured to display in decimal format, you should be able to see 0 when ADC pin (DIO07) connected to GND and 255 when connected to 3.3 volts.

References#

TI BLE5-Stack User’s Guide

TI CC2340R5 - Technical Reference Manual

Bluetooth Core Specification Version 5.3

Bluetooth LE 5 Fundamentals

Bluetooth LE - Security Fundamentals

BLE Scanning and Advertising

TI SimpleLink™ Connect App