Setting Up TWI on nRF52xx
Image for post

Prerequisites

This is tutorial is not intended to be a guide for learning C language or about the Nordic SDK platform. It’s primary target is to provide developers a concise guide about integrating peripheral modules and features into active applications.

If you are a beginner, I would recommend you look into an nRF52 Project Setup guide like this one.

https://medium.com/vicara-hardware-university/nrf52-project-setup-with-segger-embedded-system-f64958052a2d

Another easy way to get started with coding, without bothering with all basic stuff like files and driver inclusion, check out this Code Generation Tool

nrf52 Code Generator: https://vicara.co/nrf52-code-generator

TWI

Two Wire Interface, as the name suggests uses two wires, the clock and data, to enable communication between several devices. This protocol allows communication of a single master device with multiple slave devices. Differentiation is done either by address of the slave device, or using a GPIO pin to enable only a single device at a time.

Devices which use TWI or I2C include sensor peripheral devices like IMU, Temperature Sensors and Multi-Channel Motor Control Devices.

Image for post

Implementing TWI in nRF52832

In the following section, I will provide a guide as it has been tested in the nRF52832 Dev Kit. However, the same structure will remain common across all nRF52 devices.

Including correct Headers

Image for post


Include the TWI Driver files. This file should be titled, nrf_drv_twi.c It is at SDK/integration/nrfx/legacy folder.

Update sdk_config.h File

  • TWI_ENABLED has to be set to set to HIGH.

Image for post

In main.c File

Header File

#include "nrf_drv_twi.h"

Define TWI Instance


/* TWI instance ID. */
#define TWI_INSTANCE_ID 0

/* TWI instance. */
staticconstnrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

/* Indicates if operation on TWI has ended. */
staticvolatilebool m_xfer_done = false;

TWI Callback Function


voidtwi_handler (nrf_drv_twi_evt_tconst *p_event, void *p_context)
{
switch (p_event->type)
   {
case NRF_DRV_TWI_EVT_DONE:
if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
       {
       }

       m_xfer_done = true;
break;
default:
break;
   }
}

Note: One callback function is needed per instance

Initialize TWI


voidtwi_init(void)
{
ret_code_t err_code;

constnrf_drv_twi_config_t twi_config = {
       .scl = 15,
       .sda = 14,
       .frequency = NRF_TWI_FREQ_100K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init = false,
       .hold_bus_uninit = false};

   err_code = nrf_drv_twi_init(&m_twi, &twi_config, twi_handler, NULL);
   APP_ERROR_CHECK(err_code);

   nrf_drv_twi_enable(&m_twi);
}

Note: The above block of code configures and enables one instance of TWI. If another instance is needed, a new variable of type nrf_drv_twi_config_tneeds to be defined again. nrf_drv_twi_init and nrf_drv_twi_enable need to be called again. Interrupt priority options are 2, 3, 6, 7.

Add Init Function to Main Function

twi_init(); // After default definition, before Advertise Start

API for Using TWI

/*
* dev_addr -> Device address
* reg_adddr -> Register address
* data -> Buffer which holds data to be written
* length -> Length of data to be written
*/
int8_ttwi_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
uint32_t err_code;

uint8_t buffer[255] = {0};
   buffer[0] = reg_addr;
   memcpy(&buffer[1], data, length);
   m_xfer_done = false;
   err_code = nrf_drv_twi_tx(&m_twi, dev_addr, buffer,
sizeof(length), false);
   APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);

return err_code;
}

/*
* dev_addr -> Device address
* reg_adddr -> Register address
* data -> Buffer where data read from TWI will be stored
* length -> Length of data to be read
*/
int8_ttwi_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t length)
{
uint32_t err_code;
   m_xfer_done = false;
   err_code = nrf_drv_twi_tx(&m_twi, dev_addr, &reg_addr,
sizeof(reg_addr), true);
   APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);

   m_xfer_done = false;
   err_code = nrf_drv_twi_rx(&m_twi, dev_addr, data, length);
   APP_ERROR_CHECK(err_code);
while (m_xfer_done == false);

return err_code;
}

Conclusion

With the above steps anyone can easily get started with incorporating TWI.

NOTE

There is another easier method to initialize and auto-generate code for nRF52. This tool, will handle all library additions and code generations for a variety of peripherals like SPI, I2C, UART etc.

Link: https://vicara.co/nrf52-code-generator