I have been working on a C++ library for the BME280 sensor to be used within the Nordic SDK and am so far only focused on using TWIM, Within this library I intend to use TWIM in a non-blocking manner by utilizing an event handler. The code I will be including below is functional so the purpose of this post is not so much "how do I implement this" but more "am I implementing this correctly".
I have searched through devzone and found slightly different implementations of using the twi event handler, most of which seem to defeat the purpose by using an empty while loop. I have taken a look at the twi sensor example included with the SDK as well as a number of the external drivers included with the SDK, namely the mcp4725 driver. Here we find examples that repeatedly call a sensor read function in the main loop then wait for the event handler to set a transfer flag. The twi_sensor example seems to only care about the RX event being raised.
// twi_sensor.c int main { while (true) { nrf_delay_ms(500); do { __WFE(); }while (m_xfer_done == false); read_sensor_data(); NRF_LOG_FLUSH(); } }
If we look into mcp4725.c we find an example of an event handler that tracks both XFER_RX and XFER_TX event types but waits for the flags to be set in an empty wait loop on line 34:
static void twi_handler(nrf_drv_twi_evt_t const * 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_TX) { m_xfer_done = true; } if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX) { m_read_done = true; } break; default: break; } } ret_code_t mcp4725_set_voltage(uint16_t val, bool write_eeprom) { /* Shift parameter val to get 2 8-bits values. */ uint8_t reg[3] = {write_eeprom ? MCP4725_EEPROM_ADDRESS : MCP4725_DAC_ADDRESS, (val>>4), (val<<4)}; m_xfer_done = false; ret_code_t err_code = nrf_drv_twi_tx(&m_twi, MCP4725_BASE_ADDRESS, reg, sizeof(reg), false); if (err_code != NRF_SUCCESS) { return err_code; } while (m_xfer_done == false); return NRF_SUCCESS; }
My first question is, is there any benefit to tracking m_xfer_done and m_read_done separately other than to make clear which transfer type finished?
As I currently have my event handler and readRegister functions implemented, I follow the lead of the mcp4725 driver above and keep track of both RX and TX event types. This works but I am wondering if it couldn't be equally effective if consolidated to a generic m_xfer_done variable.
void BME280::twim_handler(const nrfx_twim_evt_t *p_event, void * p_context) { switch (p_event->type) { case NRFX_TWIM_EVT_DONE: if (p_event->xfer_desc.type == NRFX_TWIM_XFER_TX) { BME280::m_tx_done = true; } if (p_event->xfer_desc.type == NRFX_TWIM_XFER_RX) { BME280::m_rx_done = true; } break; case NRFX_TWIM_EVT_ADDRESS_NACK: break; case NRFX_TWIM_EVT_DATA_NACK: break; default: break; } } ret_code_t BME280::readRegister(uint8_t reg_addr, uint8_t *p_reg_data, uint16_t len) { ret_code_t err_code; err_code = nrfx_twim_tx(&_twim, _dev_addr, ®_addr, sizeof(reg_addr), true); if (err_code == NRF_SUCCESS) { do { __WFE(); } while (!BME280::m_tx_done); BME280::m_tx_done = false; } err_code = nrfx_twim_rx(&_twim, _dev_addr, p_reg_data, len); if (err_code == NRF_SUCCESS) { do { __WFE(); } while (!BME280::m_rx_done); BME280::m_rx_done = false; } return NRF_SUCCESS; }
My second question is, would there be any benefit to using nrfx_twim_xfer() with a TXRX transfer description? I assume that would allow me to eliminate one wait loop from my readRegister function and only require handling the appropriate transfer type in the event handler. I am also assuming I would only need a single m_xfer_done flag to wait on.
Lastly, is the do { __WFE(); } while(!BME280::m_tx_done); all that is needed to keep these functions non-blocking? The key part being __WFE().