| 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 slave
This information applies to the nRF52 Series only.

The two-wire interface slave with EasyDMA (TWIS) driver includes two layers: the hardware access layer (HAL) and the driver layer (DRV).

The hardware access layer provides basic APIs for accessing the registers of the TWI. See the API documentation for the TWIS driver 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:

- Simple events communication:
  - Data request
  - Transfer pending
  - Transfer finished
  - Error
- Two 7-bit addresses for listening
- Supports clock stretching

# Driver configuration

The following configuration options are available in `sdk_config.h:`

- TWIS_ASSUME_INIT_AFTER_RESET_ONLY: Set this option for better performance if you activate TWI only once.
- TWIS_NO_SYNC_MODE: Set this option to always use Asynchronous mode.

# Modes of operation

The TWIS driver supports two different modes: synchronous mode and asynchronous mode.

## Synchronous mode

In synchronous mode, the driver works completely without interrupts. To configure the driver to use synchronous mode, pass a NULL pointer as the pointer to the event handler during driver initialization.

In synchronous mode, events are pooled every time a state function is called. Pooling and state machine processing can be entered only once at the same time. Therefore, if a function is called from the main thread and, while it is processed, an interrupt occurs that tries to process a TWIS event again, the checking function in the interrupt is skipped.

The TWIS driver is a slave driver. Therefore, the reasons for configuring this driver to use synchronous mode are different than for a master driver. For a slave driver, it does not make sense to support sending data from the main thread and from interrupts at the same time. State testing functions like nrf_drv_twis_is_busy are not guaranteed to process the current state in the interrupts if the same functions are processed in the main thread. Interrupts should not wait for a state change. Otherwise, they might be locked.

The following example code shows how to use synchronous mode:

nrf_drv_twis_t instance = NRF_DRV_TWIS_INSTANCE(0);
int main(void)
{
// Initialization
bool waitingRx = false;
nrf_drv_twis_init(&instance, NULL, NULL);
nrf_drv_twis_enable(&instance);
// Main loop
while(1)
{
// It is important to process it first
if(waitingRx && !nrf_drv_twis_is_pending_rx(&instance))
{
waitingRx = false;
// Check of errors
uint32_t err = nrf_drv_twis_error_get(&instance);
if(err)
{
// ... process errors ....
}
else
{
// ... process received data ...
}
}
if(nrf_drv_twis_is_waiting_tx_buff(&instance))
{
// Prepare TX buffer (response for READ command)
nrf_drv_twis_tx_prepare(&instance, txbuffer, sizeof(txbuffer));
}
if(nrf_drv_twis_is_waiting_rx_buff(&instance))
{
// Prepare RX buffer (response for WRITE command)
nrf_drv_twis_rx_prepare(&instance, rxbuffer, sizeof(rxbuffer));
waitingRx = true;
}
// ... The rest of the main lop tasks ...
}
}

## Asynchronous mode

In asynchronous mode, all events are processed in internal interrupt service runtime. When anything requires processing, the event handler is called. See nrf_drv_twis_evt_type_t for a list of defined events.

The event handler is always called in the following pattern: REQ -> DONE or ERROR. This means that if we get a TWIS_EVT_READ_REQ message, the answer will always be TWIS_EVT_READ_DONE or TWIS_EVT_READ_ERROR. The answer will be sent even if there is no STOP condition on the bus, but reading finishes for example by a repeated START condition.

There are different ways to prepare buffers for receiving or sending data:

- Prepare the buffer when required inside the event handler.
- Prepare the buffer outside of the event handler by using a flag set in the event handler or by checking if the buffer is required by nrf_drv_twis_is_waiting_rx_buff or nrf_drv_twis_is_waiting_tx_buff.
- Prepare the buffer before it is requested. If the buffer is not required, the nrf_drv_twis_evt_t::buf_req field is cleared, and the buffer can be set for the next transaction in that event.

The following pseudocode shows how to implement an event handler:

void twis_event_handler(nrf_drv_twis_evt_t const * const p_event)
{
switch(p_event->type)
{
case TWIS_EVT_READ_REQ:
if(p_event->data.buf_req)
{
// Function size_t get_buffer_to_send(char const * * const rptr)
// needs to be implemented
const char * rptr;
size_t size = get_buffer_to_send(&rptr);
nrf_drv_twis_tx_prepare(&m_slave_inst, rptr, size);
}
break;
case TWIS_EVT_READ_DONE:
break;
case TWIS_EVT_WRITE_REQ:
if(p_event->data.buf_req)
{
// Function size_t get_buffer_to_write(char * * const wptr)
// needs to be implemented
char *wptr;
size_t size = get_buffer_to_write(&wptr);
nrf_drv_twis_rx_prepare(&m_slave_inst, wptr, size);
}
break;
case TWIS_EVT_WRITE_DONE:
// Function void mark_buffer_ready(size_t amount)
// needs to be implemented
mark_buffer_ready(p_event->data.rx_amount);
break;
default:
break;
}
}