This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Using Zephyr and Bluetooth (and other peripherals)

I have an application that is running on using nRF52840 and FreeRTOS.   I am considering transitioning to nRF5340.  For now, it appears that this also means transitioning from FreeRTOS to Zephyr.

I'm attempting to evaluate the nRF5340 using the nRF5340-PDK (version 0.8.0).   Since I'm having some issues getting some of the sample applications to run on the nRF5340-PDK, I'm attempting to port (at least a portion of) my application to Zephyr using the nRF52840 board.   Using the documentation and some of the zephyr/samples/bluetooth examples, I have some basic functionality working, but have a number of questions:

  1. It appears that there are two options for using Bluetooth with Zephyr: either using the native BLE implementation or a SoftDevice (or is this just when using nRF5340?).  It appears that the default is to use the Zephyr native BLE implementation (which looks to be version 5.0).  My nRF52840 application uses S140 (which I believe is version 5.1).  If this is true, what are the tradeoffs, and how do you select one or the other (Zephyr vs. S140)? 
  2. In my nRF52840 application, I "extend" the data length (setting the MTU to 251 bytes), and prefer the 2MBPS Phy.  It is unclear to me from the documentation how to accomplish this when using Zephyr and nRF Connect SDK (I see some defines, but am unclear where they are applied).   Is there a sample program that uses these features?
  3. My application also uses I2C and SPI peripherals using nrfx drivers.   Zephyr has native drivers for both, but it appears that nrfx drivers can also be used within Zephyr.   Is there a comparison available somewhere that shows the pros/cons of each?    I also use the TWI/SPI transaction manager libraries (from nRF SDK) in my application.   Are these available in Zephyr using nRF Connect SDK?
  4. When using peripherals with nRF Connect SDK, is there a chart (or some other documentation) that explains which CONFIG_ options are needed with each driver?
  5. I expected some of the nRF Connect sample programs to work on either nRF52840 or nRF5340 boards (of course using the appropriate builds).   I got the peripheral_hr Bluetooth example to work on the nRF52840 board, but when configured for the nRF5340-PDK, it fails at line 455 of init.c (when I click Debug->Go).   Could this be because I have a very early version (0.8.0) of the PDK?   I'm using version 2.4.0  of Zephyr and version 1.4.0 of nRF Connect SDK.

Thanks!

Parents
  • Hi Kenneth,

    Thanks for the links.   Even though I had spent a lot of time reading, I missed some of these documents (perhaps due to looking in the Zephyr links instead of the Nordic/Zephyr links).

    I am still a bit confused on using Notifications when using BLE with Zephyr.    Under nRF5 SDK, a callback (BLE_GATTS_EVT_HVN_TX_COMPLETE) indicates how many notifications have been sent (by traversing the pointer properly (p_ble_evt->evt.gatts_evt.params.hvn_tx_complete.count)) during a connection interval. This allows one to monitor how many TX buffers are being used/available.  (My application under nRF5 FreeRTOS was overrunning the buffers...)

    Under Zephyr, I see that a callback can be requested when sending a notification (bt_gatt_notify_cb).    The callback indicates that the notification has been sent, but I don't see a way to get a count of how many have been sent.    Can I assume that this callback will occur for each notification (and that if multiple notifications are sent in a single connection interval, multiple callbacks will occur)?

    Thanks again!

Reply
  • Hi Kenneth,

    Thanks for the links.   Even though I had spent a lot of time reading, I missed some of these documents (perhaps due to looking in the Zephyr links instead of the Nordic/Zephyr links).

    I am still a bit confused on using Notifications when using BLE with Zephyr.    Under nRF5 SDK, a callback (BLE_GATTS_EVT_HVN_TX_COMPLETE) indicates how many notifications have been sent (by traversing the pointer properly (p_ble_evt->evt.gatts_evt.params.hvn_tx_complete.count)) during a connection interval. This allows one to monitor how many TX buffers are being used/available.  (My application under nRF5 FreeRTOS was overrunning the buffers...)

    Under Zephyr, I see that a callback can be requested when sending a notification (bt_gatt_notify_cb).    The callback indicates that the notification has been sent, but I don't see a way to get a count of how many have been sent.    Can I assume that this callback will occur for each notification (and that if multiple notifications are sent in a single connection interval, multiple callbacks will occur)?

    Thanks again!

Children
  • Hi Kenneth,

    Is there an example that shows how to configure using an nrfxlib peripheral driver (for example TWIM) with Zephyr?  (I tried adding the line: CONFIG_I2C_NRFX=1 to the prj.conf file.)

    Thanks!

  • OK, thanks.    I looked at the example you linked to.

    Is the DeviceTree overlay file required, or can the nrfxlib calls defining peripheral instances, etc. be used directly?

    Also, any comment on the other question from the earlier entry (how to determine the number of free BLE notify buffers available)?

    Update:

    After a lot of reading and trial/error, I was able to get the nrfx TWIM driver interface to work using C calls to initialize and initiate transfers, but only in blocking mode.  

    From the example you linked to in DevZone, I found that prj.conf should have:

    # I2C
    #CONFIG_I2C=y
    #CONFIG_I2C_NRFX=y
    #CONFIG_I2C_1=y
    CONFIG_NRFX_TWIM1=y

    but after some trial/error found that only the last statement is really needed, so the first 3 CONFIG_ statements are commented out.

    Here is the code segment I used to declare and initialize the interface:

    void
    i2cHandler(nrfx_twim_evt_t const *evtPtr, void *contextPtr) {
      printk("TWIM Event\n");
    }
    
      // I2C instance
    nrfx_twim_t i2c1= NRFX_TWIM_INSTANCE(1);
    
    void main(void) {
      int err;
    
      // Initialize interfaces
    
      // I2C configure
      nrfx_twim_config_t i2cConfig = {
        .scl =                 I2C_SCL_PIN,
        .sda =                 I2C_SDA_PIN,
        .frequency =           NRF_TWIM_FREQ_400K,
        .interrupt_priority =  NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY,
        .hold_bus_uninit =     false
      };
      err= nrfx_twim_init(&i2c1,
                          &i2cConfig,
                          (nrfx_twim_evt_handler_t)i2cHandler,
                          //NULL,
                          NULL);
      if(NRFX_SUCCESS != err) {
        printk("I2C driver init failed: %d\n", err);
      }
      nrfx_twim_enable(&i2c1);

    where I2C_SCL_PIN is defined as (NRF_GPIO_PIN_MAP(0,27)), etc. 

    And here is a snip of code where I write to an I2C peripheral to initialize it.

     #define LM75A_ADDR       0x48
      #define LM75A_REG_CONF   0x01
      #define LM75A_REG_TEMP   0x00
      uint8_t initWrite[] = { LM75A_REG_CONF, 0x00 };
      uint8_t readWrite[] = { LM75A_REG_TEMP };
      uint8_t readBuf[2];
      nrfx_twim_xfer_desc_t lm75aInit = {
        .type             = NRFX_TWIM_XFER_TX,
        .address          = LM75A_ADDR,
        .primary_length   = sizeof(initWrite),
        .secondary_length = 0,
        .p_primary_buf    = initWrite,
        .p_secondary_buf  = NULL
      };
      nrfx_twim_xfer_desc_t lm75aRead = {
        .type             = NRFX_TWIM_XFER_TXRX,
        .address          = LM75A_ADDR,
        .primary_length   = sizeof(readWrite),
        .secondary_length = 2,
        .p_primary_buf    = readWrite,
        .p_secondary_buf  = readBuf
      };
    #if 1
      err= nrfx_twim_xfer(i2cPtr,&lm75aInit,0);
      if(NRFX_SUCCESS != err) {
        printk("TWIM error during init!\n");
      }
    #endif

    This only works when the first "NULL" is uncommented, and the i2cHandler statement is commented out in the "init" statement (making TWIM run in blocking mode).  So the interface works in blocking mode.

    If I run with the "init" as shown above (i2cHandler declared and the "NULL" commented out, so it should run in non-blocking mode), a run-time error is reported:

    [00:00:00.345,550] ·[1;31m<err> os: >>> ZEPHYR FATAL ERROR 1: Unhandled interrupt on CPU 0·[0m
    [00:00:00.345,581] ·[1;31m<err> os: Current thread: 0x200024a0 (BT ECC)·[0m
    [00:00:00.760,314] ·[1;31m<err> fatal_error: Resetting system·[0m
    *** Booting Zephyr OS build v2.4.0-ncs2 ***
    [00:00:00.336,914] ·[0m<inf> sdc_hci_driver: SoftDevice Controller build revision:
    cf 5c 0f 11 88 9c d7 02 15 27 c7 c3 ca 60 19 85 |.\...... .'...`..
    b7 c4 50 e3 |..P. ·[0m
    [00:00:00.340,545] ·[0m<inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)·[0m
    [00:00:00.340,545] ·[0m<inf> bt_hci_core: HW Variant: nRF52x (0x0002)·[0m
    [00:00:00.340,545] ·[0m<inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 207.3932 Build 3617359889·[0m
    [00:00:00.342,071] ·[0m<inf> bt_hci_core: Identity: d4:7d:d9:fe:63:8d (random)·[0m
    [00:00:00.342,102] ·[0m<inf> bt_hci_core: HCI: version 5.2 (0x0b) revision 0x1123, manufacturer 0x0059·[0m
    [00:00:00.342,102] ·[0m<inf> bt_hci_core: LMP: version 5.2 (0x0b) subver 0x1123·[0m

    and the board (nRF52840-DK) reboots (and everything repeats as the error occurs again).

    It looks like Zephyr is receiving an interrupt (presumably the TWIM completion) that it is not ready for.   I suspect that I haven't properly initialized something, but am unsure what...    I suspect that there has to be some linkage between the HW interrupt and the TWIM driver.

    I was expecting "i2cHandler" to be called when the I2C xfr completed.

    Can you give me some pointers?

    Thanks...

  • OK, thanks.    I looked at the example you linked to.

    Is the DeviceTree overlay file required, or can the nrfxlib calls defining peripheral instances, etc. be used directly?

    Also, any comment on the other question from the earlier entry (how to determine the number of free BLE notify buffers available)?

    Update:

    After a lot of reading and trial/error, I was able to get the nrfx TWIM driver interface to work using C calls to initialize and initiate transfers, but only in blocking mode.  

    From the example you linked to in DevZone, I found that prj.conf should have:

    # I2C
    #CONFIG_I2C=y
    #CONFIG_I2C_NRFX=y
    #CONFIG_I2C_1=y
    CONFIG_NRFX_TWIM1=y

    but after some trial/error found that only the last statement is really needed, so the first 3 CONFIG_ statements are commented out.

    Here is the code segment I used to declare and initialize the interface:

    void
    i2cHandler(nrfx_twim_evt_t const *evtPtr, void *contextPtr) {
      printk("TWIM Event\n");
    }
    
      // I2C instance
    nrfx_twim_t i2c1= NRFX_TWIM_INSTANCE(1);
    
    void main(void) {
      int err;
    
      // Initialize interfaces
    
      // I2C configure
      nrfx_twim_config_t i2cConfig = {
        .scl =                 I2C_SCL_PIN,
        .sda =                 I2C_SDA_PIN,
        .frequency =           NRF_TWIM_FREQ_400K,
        .interrupt_priority =  NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY,
        .hold_bus_uninit =     false
      };
      err= nrfx_twim_init(&i2c1,
                          &i2cConfig,
                          (nrfx_twim_evt_handler_t)i2cHandler,
                          //NULL,
                          NULL);
      if(NRFX_SUCCESS != err) {
        printk("I2C driver init failed: %d\n", err);
      }
      nrfx_twim_enable(&i2c1);

    where I2C_SCL_PIN is defined as (NRF_GPIO_PIN_MAP(0,27)), etc. 

    And here is a snip of code where I write to an I2C peripheral to initialize it.

      #define LM75A_ADDR       0x48
      #define LM75A_REG_CONF   0x01
      #define LM75A_REG_TEMP   0x00
      uint8_t initWrite[] = { LM75A_REG_CONF, 0x00 };
      uint8_t readWrite[] = { LM75A_REG_TEMP };
      uint8_t readBuf[2];
      nrfx_twim_xfer_desc_t lm75aInit = {
        .type             = NRFX_TWIM_XFER_TX,
        .address          = LM75A_ADDR,
        .primary_length   = sizeof(initWrite),
        .secondary_length = 0,
        .p_primary_buf    = initWrite,
        .p_secondary_buf  = NULL
      };
      nrfx_twim_xfer_desc_t lm75aRead = {
        .type             = NRFX_TWIM_XFER_TXRX,
        .address          = LM75A_ADDR,
        .primary_length   = sizeof(readWrite),
        .secondary_length = 2,
        .p_primary_buf    = readWrite,
        .p_secondary_buf  = readBuf
      };
    #if 1
      err= nrfx_twim_xfer(i2cPtr,&lm75aInit,0);
      if(NRFX_SUCCESS != err) {
        printk("TWIM error during init!\n");
      }
    #endif


    This only works when the first "NULL" is uncommented, and the i2cHandler statement is commented out in the "init" statement (making TWIM run in blocking mode).  So the interface works in blocking mode.

    But if I run with the "init" as shown above (i2cHandler declared and the "NULL" commented out, so it should run in non-blocking mode), a run-time error is reported:

    [00:00:00.345,550] <err> os: >>> ZEPHYR FATAL ERROR 1: Unhandled interrupt on CPU 0
    [00:00:00.345,581] <err> os: Current thread: 0x200024a0 (BT ECC)
    [00:00:00.760,314] <err> fatal_error: Resetting system
    *** Booting Zephyr OS build v2.4.0-ncs2  ***
    [00:00:00.336,914] <inf> sdc_hci_driver: SoftDevice Controller build revision: 
    cf 5c 0f 11 88 9c d7 02  15 27 c7 c3 ca 60 19 85 |.\...... .'...`..
    b7 c4 50 e3                                      |..P.             
    [00:00:00.340,545] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
    [00:00:00.340,545] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
    [00:00:00.340,545] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 207.3932 Build 3617359889
    [00:00:00.342,071] <inf> bt_hci_core: Identity: d4:7d:d9:fe:63:8d (random)
    [00:00:00.342,102] <inf> bt_hci_core: HCI: version 5.2 (0x0b) revision 0x1123, manufacturer 0x0059
    [00:00:00.342,102] <inf> bt_hci_core: LMP: version 5.2 (0x0b) subver 0x1123

    and the board (nRF52840-DK) reboots (and everything repeats as the error occurs again).

    It looks like Zephyr is receiving an interrupt (presumably the TWIM completion) that it is not ready for.   I suspect that I haven't properly initialized something, but am unsure what...    I suspect that there has to be some linkage between the HW interrupt and the TWIM driver.

    I was expecting "i2cHandler" to be called when the I2C xfr completed.

    Can you give me some pointers?

    Thanks...

  • Hi,

    I made an example with spi some time ago:
    https://devzone.nordicsemi.com/f/nordic-q-a/66488/using-spi-with-ppi-in-zephyr/272139#272139

    I am thinking you may be missing the IRQ_CONNECT() if you want to use nrfx directly.

    Kenneth

Related