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

Usage of nrf_twi_mngr in a real life driver (how can this SDK be so awkward and badly written)

Hi,

We have two sensors, both on the same I2C bus, and we use them with softdevice.

First i must say that the SDK is really poorly written, time wasting and awkward to use, any simple task takes hours of guessing, digging and google searching, the sdk_config.h is dreadful and can probably get the award of the worst configuration system ever made (and there is a lot of poorly written vendor SDK). The SDK is also badly organized, with bits and pieces of libraries in various folders and subfolder, and examples missing for many of these libraries.

On top of that Segger Embedded Studio probably wins the palm of the worst editor ever, with such basic function missing as code folding (quite mandatory when the SDK makes you write files with an average of 1K lines and callback and nonsense all over the place), worst includes management ever made and the list goes on and on.

That being said, back to the problem, it seems impossible to use nrf_twi_mngr for such a basic task as writing an I2C driver, not only it is very akward to use, with a lot of functions buffer inside buffers, macros and specific storage class, but also it seems that basic and mandatory actions such as reading a register and using the output value to write the next register is close to impossible. Writing an I2C driver normally takes 1 hour or less in any ecosystem, here with the NRF SDK after 8 hours of digging and frustration the driver is still not working, how is that possible?

here is what i need to do in any normal driver

void lis3_setFullScaleRange(scale_type_t range) {

  uint8_t data = 0;

  data = readRegister(LIS3DHTR_REG_ACCEL_CTRL_REG4);

  data &= ~LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_MASK;
  data |= range;

  writeRegister(LIS3DHTR_REG_ACCEL_CTRL_REG4, data);
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);

  switch (range) {
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_16G:
    accRange = 1280;
    break;
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_8G:
    accRange = 3968;
    break;
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_4G:
    accRange = 7282;
    break;
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_2G:
    accRange = 16000;
    break;
  default:
    break;
  }
}

void lis3_setOutputDataRate(odr_type_t odr) {

  uint8_t data = 0;

  data = readRegister(LIS3DHTR_REG_ACCEL_CTRL_REG1);

  data &= ~LIS3DHTR_REG_ACCEL_CTRL_REG1_AODR_MASK;
  data |= odr;

  writeRegister(LIS3DHTR_REG_ACCEL_CTRL_REG1, data);
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);
}

void LIS3DHTR_getAcceleration(float *x, float *y, float *z) {

  uint8_t xAccelLo, xAccelHi, yAccelLo, yAccelHi, zAccelLo, zAccelHi;

  xAccelLo = readRegister(LIS3DHTR_REG_ACCEL_OUT_X_L);
  xAccelHi = readRegister(LIS3DHTR_REG_ACCEL_OUT_X_H);
  *x = (float)((int16_t)((xAccelHi << 8) | xAccelLo)) / accRange;

  yAccelLo = readRegister(LIS3DHTR_REG_ACCEL_OUT_Y_L);
  yAccelHi = readRegister(LIS3DHTR_REG_ACCEL_OUT_Y_H);
  *y = (float)((int16_t)((yAccelHi << 8) | yAccelLo)) / accRange;

  zAccelLo = readRegister(LIS3DHTR_REG_ACCEL_OUT_Z_L);
  zAccelHi = readRegister(LIS3DHTR_REG_ACCEL_OUT_Z_H);
  *z = (float)((int16_t)((zAccelHi << 8)) | zAccelLo) / accRange;
}

void lis3_init(void) {

  uint8_t config5 = LIS3DHTR_REG_TEMP_ADC_PD_ENABLED |
                    LIS3DHTR_REG_TEMP_TEMP_EN_DISABLED;

  writeRegister(LIS3DHTR_REG_TEMP_CFG, config5);
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);

  uint8_t config1 = LIS3DHTR_REG_ACCEL_CTRL_REG1_LPEN_NORMAL | // Normal Mode
                    LIS3DHTR_REG_ACCEL_CTRL_REG1_AZEN_ENABLE | // Acceleration Z-Axis Enabled
                    LIS3DHTR_REG_ACCEL_CTRL_REG1_AYEN_ENABLE | // Acceleration Y-Axis Enabled
                    LIS3DHTR_REG_ACCEL_CTRL_REG1_AXEN_ENABLE;

  writeRegister(LIS3DHTR_REG_ACCEL_CTRL_REG1, config1);

  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);

  uint8_t config4 = LIS3DHTR_REG_ACCEL_CTRL_REG4_BDU_NOTUPDATED | // Continuous Update
                    LIS3DHTR_REG_ACCEL_CTRL_REG4_BLE_LSB |        // Data LSB @ lower address
                    LIS3DHTR_REG_ACCEL_CTRL_REG4_HS_DISABLE |     // High Resolution Disable
                    LIS3DHTR_REG_ACCEL_CTRL_REG4_ST_NORMAL |      // Normal Mode
                    LIS3DHTR_REG_ACCEL_CTRL_REG4_SIM_4WIRE;       // 4-Wire Interface

  writeRegister(LIS3DHTR_REG_ACCEL_CTRL_REG4, config4);

  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);

  lis3_setFullScaleRange(LIS3DHTR_RANGE_16G);
  lis3_setOutputDataRate(LIS3DHTR_DATARATE_400HZ);
}

Here is what i had to write with nrf_twi_mngr, after a lot of stuggle, and it still doesnt work

//lis3dhtr.c

static uint8_t NRF_TWI_MNGR_BUFFER_LOC_IND conf5[] = {LIS3DHTR_REG_TEMP_ADC_PD_ENABLED | LIS3DHTR_REG_TEMP_TEMP_EN_DISABLED};

static uint8_t NRF_TWI_MNGR_BUFFER_LOC_IND conf1[] = {LIS3DHTR_REG_ACCEL_CTRL_REG1_LPEN_NORMAL | // Normal Mode
                                                      LIS3DHTR_REG_ACCEL_CTRL_REG1_AZEN_ENABLE | // Acceleration Z-Axis Enabled
                                                      LIS3DHTR_REG_ACCEL_CTRL_REG1_AYEN_ENABLE | // Acceleration Y-Axis Enabled
                                                      LIS3DHTR_REG_ACCEL_CTRL_REG1_AXEN_ENABLE}; // Acceleration X-Axis Enabled

static uint8_t NRF_TWI_MNGR_BUFFER_LOC_IND conf4[] = {LIS3DHTR_REG_ACCEL_CTRL_REG4_BDU_NOTUPDATED | // Continuous Update
                                                      LIS3DHTR_REG_ACCEL_CTRL_REG4_BLE_LSB |        // Data LSB @ lower address
                                                      LIS3DHTR_REG_ACCEL_CTRL_REG4_HS_DISABLE |     // High Resolution Disable
                                                      LIS3DHTR_REG_ACCEL_CTRL_REG4_ST_NORMAL |      // Normal Mode
                                                      LIS3DHTR_REG_ACCEL_CTRL_REG4_SIM_4WIRE};      // 4-Wire Interface

nrf_twi_mngr_transfer_t const lis3_conf5[2] = {LIS3_WRITE_REG(LIS3DHTR_REG_TEMP_CFG, conf5, sizeof(conf5))};
nrf_twi_mngr_transfer_t const lis3_conf1[2] = {LIS3_WRITE_REG(LIS3DHTR_REG_ACCEL_CTRL_REG1, conf1, sizeof(conf1))};
nrf_twi_mngr_transfer_t const lis3_conf4[2] = {LIS3_WRITE_REG(LIS3DHTR_REG_ACCEL_CTRL_REG4, conf4, sizeof(conf4))};

//in lis3dhtr.h
#define LIS3_WRITE_REG(p_reg, p_buffer, byte_cnt)                                     \
  NRF_TWI_MNGR_WRITE(LIS3DHTR_DEFAULT_ADDRESS, p_reg, 1, 0 /*NRF_TWI_MNGR_NO_STOP*/), \
      NRF_TWI_MNGR_WRITE(LIS3DHTR_DEFAULT_ADDRESS, p_buffer, byte_cnt, 0)

#define LIS3_READ_REG(p_regs, p_buffer, byte_cnt)                                      \
  NRF_TWI_MNGR_WRITE(LIS3DHTR_DEFAULT_ADDRESS, p_regs, 1, 0 /*NRF_TWI_MNGR_NO_STOP*/), \
      NRF_TWI_MNGR_READ(LIS3DHTR_DEFAULT_ADDRESS, p_buffer, byte_cnt, 0)

extern nrf_twi_mngr_transfer_t const lis3_conf5[2];
extern nrf_twi_mngr_transfer_t const lis3_conf1[2];
extern nrf_twi_mngr_transfer_t const lis3_conf4[2];

//in main.c

int16_t accRange;
scale_type_t range;
odr_type_t odr;

static void lis3_ODR_cb(ret_code_t result, void *p_user_data) {

  if (result != NRF_SUCCESS) {
    NRF_LOG_INFO("lis3_ODR_cb - error: %d", (int)result);
    return;
  }
  m_buffer[0] &= ~LIS3DHTR_REG_ACCEL_CTRL_REG1_AODR_MASK;
  m_buffer[0] |= odr;
  const nrf_twi_mngr_transfer_t const lis3_odr_d[1] = {LIS3_WRITE_REG(LIS3DHTR_REG_ACCEL_CTRL_REG1, m_buffer[0], 1)};
  APP_ERROR_CHECK(nrf_twi_mngr_perform(&m_nrf_twi_mngr, NULL, lis3_odr_d, 1, NULL));
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);
}

void lis3_ODR(const odr_type_t odr_) {

  odr = odr_;
  static nrf_twi_mngr_transfer_t const transfers[] = {LIS3_READ_REG(LIS3DHTR_REG_ACCEL_CTRL_REG1, m_buffer, 1)};
  static nrf_twi_mngr_transaction_t NRF_TWI_MNGR_BUFFER_LOC_IND transaction = {
      .callback = lis3_ODR_cb,
      .p_user_data = NULL,
      .p_transfers = transfers,
      .number_of_transfers = sizeof(transfers) / sizeof(transfers[0])};
  APP_ERROR_CHECK(nrf_twi_mngr_schedule(&m_nrf_twi_mngr, &transaction));
}

static void lis3_scale_cb(ret_code_t result, void *p_user_data) {

  if (result != NRF_SUCCESS) {
    NRF_LOG_INFO("lis3_scale_cb - error: %d", (int)result);
    return;
  }
  m_buffer[0] &= ~LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_MASK;
  m_buffer[0] |= range;
  const nrf_twi_mngr_transfer_t const lis3_scale_d[1] = {LIS3_WRITE_REG(LIS3DHTR_REG_ACCEL_CTRL_REG4, m_buffer[0], 1)};
  APP_ERROR_CHECK(nrf_twi_mngr_perform(&m_nrf_twi_mngr, NULL, lis3_scale_d, 1, NULL));
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);
  switch (range) {
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_16G:
    accRange = 1280;
    break;
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_8G:
    accRange = 3968;
    break;
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_4G:
    accRange = 7282;
    break;
  case LIS3DHTR_REG_ACCEL_CTRL_REG4_FS_2G:
    accRange = 16000;
    break;
  default:
    break;
  }
}

void lis3_scale(scale_type_t range_) {

  range = range_;
  static nrf_twi_mngr_transfer_t const transfers[] = {LIS3_READ_REG(LIS3DHTR_REG_ACCEL_CTRL_REG4, m_buffer, 1)};
  static nrf_twi_mngr_transaction_t NRF_TWI_MNGR_BUFFER_LOC_IND transaction = {
      .callback = lis3_scale_cb,
      .p_user_data = NULL,
      .p_transfers = transfers,
      .number_of_transfers = sizeof(transfers) / sizeof(transfers[0])};
  APP_ERROR_CHECK(nrf_twi_mngr_schedule(&m_nrf_twi_mngr, &transaction));
}

static void lis3_data_cb(ret_code_t result, void *p_user_data) {

  float x, y, z;
  if (result != NRF_SUCCESS) {
    NRF_LOG_INFO("lis3_data_cb - error: %d", (int)result);
    return;
  }
  x = (float)((int16_t)((m_buffer[1] << 8) | m_buffer[0])) / accRange;
  y = (float)((int16_t)((m_buffer[3] << 8) | m_buffer[2])) / accRange;
  z = (float)((int16_t)((m_buffer[5] << 8)) | m_buffer[4]) / accRange;
  //NRF_LOG_INFO( "X " NRF_LOG_FLOAT_MARKER "", NRF_LOG_FLOAT(x));
  NRF_LOG_INFO("X %x", m_buffer[0]);
}

void lis3_get_data() {

  static nrf_twi_mngr_transfer_t const transfers[] = {LIS3_READ_REG(LIS3DHTR_REG_ACCEL_OUT_X_L, m_buffer, 1)};
  static nrf_twi_mngr_transaction_t NRF_TWI_MNGR_BUFFER_LOC_IND transaction = {
      .callback = lis3_data_cb,
      .p_user_data = NULL,
      .p_transfers = transfers,
      .number_of_transfers = sizeof(transfers) / sizeof(transfers[0])};
  APP_ERROR_CHECK(nrf_twi_mngr_schedule(&m_nrf_twi_mngr, &transaction));
}

void lis3_init() {

  APP_ERROR_CHECK(nrf_twi_mngr_perform(&m_nrf_twi_mngr, NULL, lis3_conf5, 1, NULL));
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);
  APP_ERROR_CHECK(nrf_twi_mngr_perform(&m_nrf_twi_mngr, NULL, lis3_conf1, 1, NULL));
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);
  APP_ERROR_CHECK(nrf_twi_mngr_perform(&m_nrf_twi_mngr, NULL, lis3_conf4, 1, NULL));
  nrf_delay_ms(LIS3DHTR_CONVERSIONDELAY);
  lis3_scale(LIS3DHTR_RANGE_2G);
  lis3_ODR(LIS3DHTR_DATARATE_400HZ);
}

As soon as i call nrf_twi_mngr_perform in a callback i immediately get

<error>
app : ERROR 4 [NRF_ERROR_NO_MEM] at / Volumes / HD1 / NRF52 / nRF5SDK160098a08e2 / examples / peripheral / twi_master_using_nrf_twi_mngr / main.c : 426 PC at : 0x0000A301 <
error > app : End of error report



MAX_PENDING_TRANSACTIONS has been increased to 30, which doesnt help at all. I am now starting to think it is not possible to schedule a register
write from the register read callback, and then how to write a driver?

Parents
  • First purchase a nRF9160-feather then visit https://github.com/circuitdojo/nrf9160-feather-examples-and-drivers

    He shows how to create a I²C driver and use it with the SDK..

    I can relate to your frustration, things that take 10 minutes with CubeIDE take a day or more. I'm finding if you learn how to drive the Zephyr RTOS then the Kconfig kernel compiler system, add to that west, python, TCL, cmake, ninja, and 'other' build tools life gets better... or Secure/NON Secure, Bootloader or Flash, command line Linux, Mac, Windows, or GUI. 

    I can't tell 'which' SDK your talking about but using nRF Connect Desktop to install the nRF Connect SDK 's look at D:\ncs\v1.4.99-dev1\zephyr\drivers\sensor\lis2dh for examples, Zephyr is providing the drivers

Reply
  • First purchase a nRF9160-feather then visit https://github.com/circuitdojo/nrf9160-feather-examples-and-drivers

    He shows how to create a I²C driver and use it with the SDK..

    I can relate to your frustration, things that take 10 minutes with CubeIDE take a day or more. I'm finding if you learn how to drive the Zephyr RTOS then the Kconfig kernel compiler system, add to that west, python, TCL, cmake, ninja, and 'other' build tools life gets better... or Secure/NON Secure, Bootloader or Flash, command line Linux, Mac, Windows, or GUI. 

    I can't tell 'which' SDK your talking about but using nRF Connect Desktop to install the nRF Connect SDK 's look at D:\ncs\v1.4.99-dev1\zephyr\drivers\sensor\lis2dh for examples, Zephyr is providing the drivers

Children
  •  Not sure if you are replying to the correct question or what?, we use NRF52832 on custom hardware and there is no point purchasing nor using nRF9160.

    I'm finding if you learn how to drive the Zephyr RTOS

    Mmmh, again, not sure what it has to do with the topic, i used various RTOS since a decade, thanks, and i certainly dont have any plan to use Zephyr OS

    In the current case i am revising a firmware that was developped 2 years ago, due to recent hardware and functions changes, the architecture is already defined, written, changing it is not an option. There is no RTOS, it uses the scheduler and the softdevice, which was not present previously, has now been added. I already spent (way too much) time refractoring everything to work with the new target and functions, except the I2C which is the current matter.

    SDK rev is 16, because rev 17 removed the "serial" driver, which my colleague used to implement the at-commands handler in the previous firwmare version, i initally moved to SDK 17 but the refractoring of the at-handler without serial library was so shitty that i had to go back to SDK 16 and reuse the previous implementation (to avoid loosing 2 more days on such a ridiculous task).

    The github project you link has totally nothing to do with the question, it is a different target, it is not an IDE project, and it is based on zephyr os which we dont and wont use; so 100% off-topic.

  • I'm so sorry to have offended you, I didn't see an SDK number and thought you were talking about nrF Connect SDK since the nRF52832 is part of that SDK.

    Please forgive me for taking the time to try to find a solution to your issues since obviously I don't know the difference between the many SDK's Nordic has. Hopefully your entire world won't end by me not understanding which SDK you were ranting about.

    Have a nice day.

Related