| nRF5 SDK v12.0.0 |

Choose documentation: | nRF5 SDK | S130 SoftDevice API | S132 SoftDevice API | S212 SoftDevice API | S332 SoftDevice API

All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
TWI master

The TWI driver includes two layers: the hardware access layer (HAL) and the driver layer (DRV).

The hardware access layer provides basic APIs for accessing the TWI registers. See the API documentation for the TWI HAL for details.

The driver layer provides APIs on a higher level than the HAL. See the API documentation for the TWIS driver for details.

Key features include:

- Repeated start
- No multi-master
- Only 7-bit addressing
- Supports clock stretching
- Includes blocking and non-blocking mode
- EasyDMA and legacy support (TWIM and TWI)
- TXRX and TXTX transfers (non-blocking mode only)
- Multi-instance
- Supports triggering transfer from PPI (TWIM only)
- Supports post-incrementation of buffer addresses and repeated transfers (TWIM only)

# Initialization

Before using the instance of the driver, it must be initialized with the nrf_drv_twi_init function. If no event handler is provided, the instance is configured to use blocking mode. After initialization, the instance must be enabled with nrf_drv_twi_enable.

The instance can be disabled using nrf_drv_twi_disable and uninitialized using nrf_drv_twi_uninit.

# Basic usage

The TWI master driver supports two modes of operation:

- In blocking mode, the functions nrf_drv_twi_rx and nrf_drv_twi_tx return if the requested transfer is complete, or if an error was reported by the peripheral. In blocking mode, the driver does not use the interrupt; there is no context switch in the function.
- In non-blocking mode, the functions nrf_drv_twi_tx, nrf_drv_twi_rx, and nrf_drv_twi_xfer return with NRF_SUCCESS immediately after the transfer is set up or with NRF_ERROR_BUSY if the driver is busy (because a previous transfer is not completed). The event handler is called from the TWI interrupt context when the transfer is complete (if enabled) or if an error occurs.

The following code example for basic usage in blocking mode shows a simple write sequence:

uint32_t err_code;
uint8_t tx_data[] = {'a', 'b', 'c', 'd', 'e'};
const nrf_drv_twi_t twi = NRF_DRV_TWI_INSTANCE(0);
err_code = nrf_drv_twi_init(&twi, NULL, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&twi);
err_code = nrf_drv_twi_tx(&twi, SLAVE_ADDRESS, tx_data, sizeof(tx_data), false);
APP_ERROR_CHECK(err_code);

# Advanced usage

In non-blocking mode, you can use the nrf_drv_twi_xfer function that supports complex transfers. As parameters, nrf_drv_twi_xfer takes the driver instance, a pointer to a transfer descriptor (nrf_drv_twi_xfer_desc_t), and flags that enable more features.

Note that TWI supports only the NRF_DRV_TWI_FLAG_TX_NO_STOP flag. All other flags require TWIM.

nrf_drv_twi_xfer_desc_t::type specifies the type of the transfer. The following transfer types are supported:

Transfer type | Description
--- | ---
NRF_DRV_TWI_XFER_TX | TX transfer. By default, this transfer ends with a stop condition. Specify NRF_DRV_TWI_FLAG_TX_NO_STOP to suppress the stop condition. Use the macro NRF_DRV_TWI_XFER_DESC_TX to create a transfer descriptor with this type.
NRF_DRV_TWI_XFER_RX | RX transfer with a stop condition at the end of the transfer. Use the macro NRF_DRV_TWI_XFER_DESC_RX to create a transfer descriptor with this type.
NRF_DRV_TWI_XFER_TXRX | TX transfer followed by an RX transfer with a repeated start condition. The transfer ends with a stop condition. Note that there is no interrupt after the TX transfer is completed (TWIM only). Use the macro NRF_DRV_TWI_XFER_DESC_TXRX to create a transfer descriptor with this type.
NRF_DRV_TWI_XFER_TXTX | TX transfer followed by a TX transfer with a repeated start condition. By default, the transfer ends with a stop condition. Specify NRF_DRV_TWI_FLAG_TX_NO_STOP to suppress the stop condition. Use the macro NRF_DRV_TWI_XFER_DESC_TXTX to create a transfer descriptor with this type.

The following sections show complex scenarios that use different transfer types. These scenarios are not supported with TWI, but require TWIM.

## Starting a transfer from PPI

You can use nrf_drv_twi_xfer to set up a transfer and start it synchronously using PPI. To do so, specify the NRF_DRV_TWI_FLAG_HOLD_XFER flag and use nrf_drv_twi_start_task_get to get the address of the task that starts the transfer.

The following example shows how to configure the driver to setup a TX transfer without starting it:

nrf_drv_twi_xfer_desc_t xfer = NRF_DRV_TWI_XFER_DESC_TX(addr, p_data, length);
ret_code_t ret = nrf_drv_twi_xfer(&twi, &xfer, NRF_DRV_TWI_FLAG_HOLD_XFER);
// TWIM is now configured and ready to be started.
if (ret == NRF_SUCCESS)
{
uint32_t start_tsk_addr = nrf_drv_twi_start_task_get(&twi, xfer.type);
// Set up PPI to trigger the transfer.
}

## Repeated transfers

You can configure the TWI driver to perform a certain amount of PPI-triggered transfers. To do so, specify the NRF_DRV_TWI_FLAG_REPEATED_XFER flag. The driver will not put the instance into busy state in this case, because in this mode, you control when the sequence of transfers is complete. Use the STOPPED event to count the number of completed transfers. To get the address of the STOPPED event, use nrf_drv_twi_stopped_event_get.

Specify the NRF_DRV_TWI_FLAG_TX_POSTINC and NRF_DRV_TWI_FLAG_RX_POSTINC flags to enable post-incrementation of buffer addresses.

Additionally, you can specify the NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER flag to disable calling the user event handler after each transfer completion. If you specify this flag, transfer completion interrupts are disabled if EasyDMA and TWIM is used, and the user event handler will be called only if an ERROR interrupt is triggered.

The following example shows how to set up a sequence of NRF_DRV_TWI_XFER_TXRX transfers that will be triggered using PPI (for example, using RTC or TIMER compare events). A single transfer consists of a transmission of 1 byte and a reception of 3 bytes. The TX data is repeated for all transfers, but the RX data from all transfers is collected in the buffer. Therefore, NRF_DRV_TWI_FLAG_RX_POSTINC is set so that the buffer address for the received data is incremented. The end of sequence is controlled externally, and the processing of data is postponed until the end of the sequence (the user event handler is disabled).

nrf_drv_twi_xfer_desc_t xfer = NRF_DRV_TWI_XFER_DESC_TXRX(addr, p_tx_data, 1, p_rx_data, 3);
uint32_t flags = NRF_DRV_TWI_FLAG_HOLD_XFER |
NRF_DRV_TWI_FLAG_RX_POSTINC |
NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER |
NRF_DRV_TWI_FLAG_REPEATED_XFER;
ret_code_t ret = nrf_drv_twi_xfer(&twi, &xfer, flags);
// TWIM is now configured and ready to be started.
if (ret == NRF_SUCCESS)
{
uint32_t start_tsk_addr = nrf_drv_twi_start_task_get(&twi, xfer.type);
// Set up PPI to trigger the transfer.
uint32_t stopped_evt_addr = nrf_drv_twi_stopped_event_get(&twi);
// Set up PPI to count the number of transfers.
}

In this example, there is no interrupt from TWI unless an error condition is detected.

# Events

When a transfer is complete, which means that the requested amount of data was transferred or an error condition was detected, the driver generates an event (unless this was suppressed with the NRF_DRV_TWI_FLAG_NO_XFER_EVT_HANDLER flag). This event contains the transfer type and the transfer descriptor. If an error occurred, the length field is set to the amount of bytes that were transferred before the error occurred.

It is possible to start another transfer from the event handler context.

The functions nrf_drv_twi_rx, nrf_drv_twi_tx, and nrf_drv_twi_xfer internally disable all TWI/TWIM interrupts when setting up transfers, and then re-enable them before leaving the function. There is no context blocking in the interrupt handler. Therefore, it is assumed that the driver API will not be called from a context with higher priority than the instance interrupt, because this might cause driver failure.

The following code shows an event handler that uses TX RX with a repeated start:

void twi_event_handler(nrf_drv_twi_evt_t * p_event, void * p_context)
{
if ((p_event->type == NRF_DRV_TWI_EVT_DONE) &&
(p_event->xfer_desc.type == NRF_DRV_TWI_XFER_TXRX))
{ xfer_completed = true; }
}

The following code shows the transfer starting:

nrf_drv_twi_xfer_desc_t xfer = NRF_DRV_TWI_XFER_DESC_TXRX(addr, p_tx_buffer, tx_length, p_rx_buffer, rx_length);
ret_code_t ret = nrf_drv_twi_xfer(&twi, &xfer, 0);
if (ret == NRF_SUCCESS)
{
while(xfer_completed == false){}
}