This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Most Significative Bit in TWIM RX transfer is always 1

While trying to communicate through I2C/TWI with a MCP39F521 using a NRF52 DK (NRF52832), the first bit of the received data is always 1. The NRF52 DK is the TWI Master and the MCP39F521 is the TWI slave.

According to the MCP39F521 datasheet, the first byte in a response is the ACK/NAK, which is 0x06 on an ACK and 0x15 on a NAK, but I am receiving 0x86, no matter the command I sent to the MCP39F521. However, the remaining of the transfer is received correctly and corresponds to what is expected for the sent command. A command, on this context, is a set of bytes sent in a given order and with a given value to the MCP39F521.

Moreover, the last byte of a command is the checksum of the sent data. This checksum is sent either from MCP39F521 to NRF52DK on an RX command and vice-versa on a TX command. According to the datasheet, the MCP39F521 only processes commands once the command is fully sent (STOP bit sent by the master) and only if the checksum is correct. From this, I conclude that the commands are being correctly sent to the TWI bus by the NRF52 DK and correctly received by the MCP39F521.

I do not have a scope or a logical analyzer, but debugging also shows the correct data being sent by the nrfx_twim_xfer.

On section 4.3, page 16 of the MCP39F521 datasheet, it is indicated that the MCP39F521 performs clock stretching and "In the case of the MCP39F521, after a frame is received, the device will hold the clock low until the frame has been processed. The maximum clock stretching duration is less than 10 milliseconds"

Tested with an Arduino and its Wire library, the first byte received is 0x06, and this problem does not occur.

From the previous, and to the best of my understanding, I believe that the NRF52 DK is sampling the SDA line too soon after a response from the MCP39F521 and this timing issue results on the SDA line not yet been driven low by theMCP39F521. Can someone confirm if my conclusion makes sense?

Also, I also believe to be experiencing the Errata 149 -  TWIM: First clock pulse after clock stretching may be too long or too short.

Comments or suggestions on how to tackle this issue are very much appreciated.

I am using the SDK v16.0.0, and the nrfx_twim drivers. I have already tried using the nrf_drv_twi and nrfx_twi, both blocking and non-blocking and the problem persists.

The TWIM config:

/**
 * @brief TWIM initialization.
 */
void twim_init (void)
{
    ret_code_t err_code;

    const nrfx_twim_config_t twim_config = {
       .scl                 = ARDUINO_SCL_PIN,
       .sda                 = ARDUINO_SDA_PIN,
       .frequency           = NRF_TWIM_FREQ_100K,
       .interrupt_priority  = NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY,
       .hold_bus_uninit     = NRFX_TWIM_DEFAULT_CONFIG_HOLD_BUS_UNINIT
    };

    err_code = nrfx_twim_init(&m_twi_master, &twim_config, twim_handler, NULL);
    APP_ERROR_CHECK(err_code);

    nrfx_twim_enable(&m_twi_master);
}

The twim handler:

/**
 * @brief TWI events handler.
 */
void twim_handler(nrfx_twim_evt_t const * p_event, void * p_context)
{
  switch (p_event->type)
  {
      case NRFX_TWIM_EVT_DONE:
      test = 1;
          if (p_event->xfer_desc.type == NRFX_TWIM_XFER_TX) // TX Done
          {
            m_xfer_done = true;
          }
          if (p_event->xfer_desc.type == NRFX_TWIM_XFER_RX) // RX Done
          {
            m_xfer_done = true;
          }
          break;
      default:
          break;
  }
}

And the instruction to read a command frame. rx_data is passed by argument and resides in .data (global variable defined in main).

ret_code_t mcp39f521_reg_read(const uint16_t reg_address, const uint8_t n_bytes, uint8_t * rx_data)
{
  if (n_bytes > CMD_FRAME_MAX_REG_READ_BYTES)
  {
    return NRF_ERROR_INVALID_PARAM;
  }

  m_xfer_done = false;
  ret_code_t err_code;

  // partially Init Array (Checksum field not initialized)
  uint8_t cmd_frame[REG_READ_CMD_N_BYTES] = {MCP39F521_HEADER_BYTE, \
                                             REG_READ_CMD_N_BYTES, \
                                             MCP39F521_SET_ADR_PTR_INSTR, \
                                             (reg_address & LEFTMOST_BYTE_BIT_MASK) >> BYTE_SHIFT, \
                                             reg_address & RIGHTMOST_BYTE_BIT_MASK, \
                                             MCP39F521_REG_READ_INSTR, \
                                             n_bytes, \
                                            };

  uint8_t bytes = n_bytes + MCP39F521_RESPONSE_BASE_N_BYTES;
  cmd_frame[REG_READ_CMD_N_BYTES - 1] = __compute_byte_addition_checksum(cmd_frame);      // Compute Command Checksum

  nrfx_twim_xfer_desc_t tx_xfer = NRFX_TWIM_XFER_DESC_TX(MCP39F521_BASE_ADDRESS, cmd_frame, sizeof(cmd_frame));
  nrfx_twim_xfer_desc_t rx_xfer = NRFX_TWIM_XFER_DESC_RX(MCP39F521_BASE_ADDRESS, rx_data, bytes);

  err_code = nrfx_twim_xfer(g_mcp39f521_twim_instance, &tx_xfer, 0  );
  if (err_code != NRF_SUCCESS) {
    NRF_LOG_DEBUG("TX Error Code: %d", err_code);
  }

  while(m_xfer_done == false){
    __WFE();
  };

  m_xfer_done = false;
  err_code = nrfx_twim_xfer(g_mcp39f521_twim_instance, &rx_xfer, 0 );
  if (err_code != NRF_SUCCESS) {
    NRF_LOG_DEBUG("RX Error Code: %d", err_code);
  }
 
  while(m_xfer_done == false){
    __WFE();
  };
  
  return err_code;
}

Thank you in advance!

Related