Skip to content

Commit

Permalink
NRF52: fix i2c byte read/write implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
maciejbocianski committed Oct 23, 2019
1 parent 7d5f6cf commit 627e6de
Showing 1 changed file with 110 additions and 55 deletions.
165 changes: 110 additions & 55 deletions targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/i2c_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,106 @@ int i2c_start(i2c_t *obj)
return 0;
}

static uint8_t twi_byte_write(NRF_TWI_Type *twi, uint8_t data)
{
uint32_t t0;
nrf_twi_event_clear(twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(twi, NRF_TWI_EVENT_BB);
(void)nrf_twi_errorsrc_get_and_clear(twi);

nrf_twi_txd_set(twi, data);

uint32_t start_us = tick2us * lp_ticker_read();
uint8_t ret = 2;

do {
if (nrf_twi_event_check(twi, NRF_TWI_EVENT_TXDSENT)) {
nrf_twi_event_clear(twi, NRF_TWI_EVENT_TXDSENT);
ret = 1; // ACK received
break;
}
if (nrf_twi_event_check(twi, NRF_TWI_EVENT_ERROR)) {
nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);
ret = 0; // some error occurred
break;
}
} while (((tick2us * (uint32_t)lp_ticker_read()) - start_us) < MAXIMUM_TIMEOUT_US);

return ret; // timeout;
}

static int twi_start_write(NRF_TWI_Type *twi, int address)
{
nrf_twi_event_clear(twi, NRF_TWI_EVENT_STOPPED);
nrf_twi_event_clear(twi, NRF_TWI_EVENT_TXDSENT);
nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);
nrf_twi_event_clear(twi, NRF_TWI_EVENT_BB);
(void)nrf_twi_errorsrc_get_and_clear(twi);

nrf_twi_shorts_set(twi, 0);

nrf_twi_address_set(twi, address >> 1);
nrf_twi_task_trigger(twi, NRF_TWI_TASK_RESUME);
nrf_twi_task_trigger(twi, NRF_TWI_TASK_STARTTX);

/* Setup stop watch for timeout. */
uint32_t start_us = tick2us * lp_ticker_read();
uint32_t now_us = start_us;

/* Block until timeout or an address error has been detected. */
while (((now_us - start_us) < 150) &&
!(nrf_twi_event_check(twi, NRF_TWI_EVENT_TXDSENT))) {
now_us = tick2us * lp_ticker_read();
}

/* Check error register and update return value if an address NACK was detected. */
uint32_t error = nrf_twi_errorsrc_get_and_clear(twi);

if (error & NRF_TWI_ERROR_ADDRESS_NACK) {
return 0; // set NACK
} else if (now_us - start_us >= DEFAULT_TIMEOUT_US) {
return 2; // set timeout
}

return 1; // ACK
}

static int twi_start_read(NRF_TWI_Type *twi, int address)
{
nrf_twi_event_clear(twi, NRF_TWI_EVENT_STOPPED);
nrf_twi_event_clear(twi, NRF_TWI_EVENT_RXDREADY);
nrf_twi_event_clear(twi, NRF_TWI_EVENT_ERROR);
(void)nrf_twi_errorsrc_get_and_clear(twi);

nrf_twi_shorts_set(twi, NRF_TWI_SHORT_BB_SUSPEND_MASK);

nrf_twi_address_set(twi, address >> 1);
nrf_twi_task_trigger(twi, NRF_TWI_TASK_RESUME);
nrf_twi_task_trigger(twi, NRF_TWI_TASK_STARTRX);

/* Setup stop watch for timeout. */
uint32_t start_us = tick2us * lp_ticker_read();
uint32_t now_us = start_us;

/* Block until timeout or an address error has been detected. */
while (((now_us - start_us) < 150) &&
!(nrf_twi_event_check(twi, NRF_TWI_EVENT_TXDSENT))) {
now_us = tick2us * lp_ticker_read();
}

/* Check error register and update return value if an address NACK was detected. */
uint32_t error = nrf_twi_errorsrc_get_and_clear(twi);

if (error & NRF_TWI_ERROR_ADDRESS_NACK) {
return 0; // set NACK
} else if (now_us - start_us >= DEFAULT_TIMEOUT_US) {
return 2; // set timeout
}

return 1; // ACK
}

/** Write one byte
*
* @param obj The I2C object
Expand All @@ -359,72 +459,27 @@ int i2c_byte_write(i2c_t *obj, int data)
struct i2c_s *config = obj;
#endif

int instance = config->instance;
NRF_TWI_Type *p_twi = nordic_nrf5_twi_register[config->instance];
int result = 1; // default to ACK

/* Check if this is the first byte to be transferred. If it is, then send start signal and address. */
if (config->state == NORDIC_TWI_STATE_START) {
config->state = NORDIC_TWI_STATE_BUSY;

/* Beginning of new transaction, configure peripheral if necessary. */
config->update = true;
i2c_configure_twi_instance(obj);

/* Set I2C device address. NOTE: due to hardware limitations only 7-bit addresses are supported. */
nrf_twi_address_set(nordic_nrf5_twi_register[instance], data >> 1);

/* If read bit is set, trigger read task otherwise trigger write task. */
if (data & I2C_READ_BIT) {
/* For timing reasons, reading bytes requires shorts to suspend peripheral after each byte. */
nrf_twi_shorts_set(nordic_nrf5_twi_register[instance], NRF_TWI_SHORT_BB_SUSPEND_MASK);
nrf_twi_task_trigger(nordic_nrf5_twi_register[instance], NRF_TWI_TASK_STARTRX);
} else {
/* Reset shorts register. */
nrf_twi_shorts_set(nordic_nrf5_twi_register[instance], 0);
nrf_twi_task_trigger(nordic_nrf5_twi_register[instance], NRF_TWI_TASK_STARTTX);
}

/* Setup stop watch for timeout. */
uint32_t start_us = tick2us * lp_ticker_read();
uint32_t now_us = start_us;

/* Block until timeout or an address error has been detected. */
while (((now_us - start_us) < DEFAULT_TIMEOUT_US) &&
!(nrf_twi_event_check(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_ERROR))) {
now_us = tick2us * lp_ticker_read();
}

/* Check error register and update return value if an address NACK was detected. */
uint32_t error = nrf_twi_errorsrc_get_and_clear(nordic_nrf5_twi_register[instance]);

if (error & NRF_TWI_ERROR_ADDRESS_NACK) {
result = 0; // set NACK
return twi_start_read(p_twi, data);
} else {
/* Normal write. Send next byte after clearing event flag. */
nrf_twi_event_clear(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_TXDSENT);
nrf_twi_txd_set(nordic_nrf5_twi_register[instance], data);

/* Setup stop watch for timeout. */
uint32_t start_us = tick2us * lp_ticker_read();
uint32_t now_us = start_us;

/* Block until timeout or the byte has been sent. */
while (((now_us - start_us) < MAXIMUM_TIMEOUT_US) &&
!(nrf_twi_event_check(nordic_nrf5_twi_register[instance], NRF_TWI_EVENT_TXDSENT))) {
now_us = tick2us * lp_ticker_read();
}

/* Check the error code to see if the byte was acknowledged. */
uint32_t error = nrf_twi_errorsrc_get_and_clear(nordic_nrf5_twi_register[instance]);

if (error & NRF_TWI_ERROR_DATA_NACK) {
result = 0; // set NACK
} else if (now_us - start_us >= MAXIMUM_TIMEOUT_US) {
result = 2; // set timeout
}
return twi_start_write(p_twi, data);
}
} else {
nrf_twi_task_trigger(p_twi, NRF_TWI_TASK_RESUME);
// 0 - TWI signaled error (NAK is the only possibility here)
// 1 - ACK received
// 2 - timeout (clock stretched for too long?)
return twi_byte_write(p_twi, (uint8_t)data);
}

return result;
}

/** Read one byte
Expand Down

0 comments on commit 627e6de

Please sign in to comment.