0

How can I reuse this library into TWI library in nrF52?

ndarkness gravatar image

asked 2017-03-20 21:33:24 +0100

updated 2017-03-22 11:03:37 +0100

Hello all,

I am trying to make use of some Adafruit libraries for some sensors in my nrF52pca10040, the libraries are these twoAdafruit_BME280.cpp, Adafruit_BME280.h and Adafruit_TSL2591.h,Adafruit_TSL2591.cpp

They both make use of either SPI or I2C interfaces, I am interested on I2C, so I was trying to port both to the nrF52, I have started with the BME280 one. Using SEGGER I am compiling them but I am getting an error when reaching the method Wire.begin(); on Adafruit_BME280::begin(uint8_t a), which is expected since this function is from Arduino.

I have looked into the example twi_sensor and I see that there the twi_init is configured as

const nrf_drv_twi_config_t twi_lm75b_config = {
   .scl                = ARDUINO_SCL_PIN,
   .sda                = ARDUINO_SDA_PIN,
   .frequency          = NRF_TWI_FREQ_100K,
   .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
   .clear_bus_init     = false
};

Can I use the same configuration for my case? What should I change to be able to make use of that library?

On the other hand, those libs work a lot with the definitions HIGH,LOW,INPUT or OUTPUT, where can I find those definitons for the nrf52 board? As well as functions as configuring the PIN(pinmode(pin,OUPUT)) and writing onto it (digitalWrite(pin,HIGH)) ?

Thank you very much in advance,

Kind regards

Edit: following the advice of Jorgen, I have created my own version of the begin, read and write, however, even though there is no compilation errors, debuging the lib it gets stack when reaching the condition if (read8(BME280_REGISTER_CHIPID) != 0x60) in the begin() method.

bool Adafruit_BME280::begin(uint8_t a) {
  _i2caddr = a;

  if (_cs == -1) {
    // i2c
    //Wire.begin();

    ret_code_t err_code;

    const nrf_drv_twi_config_t twi_BME280_config = {
       .scl                = ARDUINO_SCL_PIN,
       .sda                = ARDUINO_SDA_PIN,
       .frequency          = NRF_TWI_FREQ_100K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init     = false
    };

    err_code = nrf_drv_twi_init(&m_twi, &twi_BME280_config, twi_handler, NULL);
    APP_ERROR_CHECK(err_code);

    nrf_drv_twi_enable(&m_twi);

  } else {
//    digitalWrite(_cs, HIGH);
//    pinMode(_cs, OUTPUT);
//
//    if (_sck == -1) {
//      // hardware SPI
//      SPI.begin();
//    } else {
//      // software SPI
//      pinMode(_sck, OUTPUT);
//      pinMode(_mosi, OUTPUT);
//      pinMode(_miso, INPUT);
//    }
  }

  if (read8(BME280_REGISTER_CHIPID) != 0x60)
    return false;

  readCoefficients();

  //Set before CONTROL_meas (DS 5.4.3)
  write8(BME280_REGISTER_CONTROLHUMID, 0x05); //16x oversampling 

  write8(BME280_REGISTER_CONTROL, 0xB7); // 16x ovesampling, normal mode
  return true;
}

void Adafruit_BME280::write8(byte reg, byte value)
{
  if (_cs == -1) {

    ret_code_t err_code;
    err_code = nrf_drv_twi_tx(&m_twi, _i2caddr, (const byte*)reg, sizeof(reg), false);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_twi_tx(&m_twi, _i2caddr, (byte*)value, sizeof(value), false);
    APP_ERROR_CHECK(err_code);


//    Wire.beginTransmission((uint8_t)_i2caddr);
//    Wire.write((uint8_t)reg);
//    Wire.write((uint8_t)value);
//    Wire.endTransmission();
}}
uint8_t Adafruit_BME280::read8(byte reg)
{
  uint8_t value = 0;
  uint8_t valueAux = 0;

  if (_cs == -1) {

    ret_code_t err_code = nrf_drv_twi_rx(&m_twi, _i2caddr, &valueAux, sizeof(byte));
    APP_ERROR_CHECK(err_code);
    if (m_rxfer_done) {
      value = valueAux;//getting the value of the pointer
      m_rxfer_done = false;
    }


//    Wire.beginTransmission((uint8_t)_i2caddr);
//    Wire.write((uint8_t)reg);
//    Wire.endTransmission();
//    Wire.requestFrom((uint8_t)_i2caddr, (byte)1);
//    value = Wire.read();

  } 
  return value;
}

void Adafruit_BME280::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_RX) {
      m_rxfer_done = true;
    } else if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_TX) {
      m_txfer_done = true;
    }

    break;
  default:
    break;
  }
}

EDIT2image description Debug

edit retag flag offensive close delete report spam

1 answer

Sort by » oldest newest most voted
0
joh2 gravatar image

answered 2017-03-21 12:50:34 +0100

Hi,

You will have to rewrite all the init, write and read functions to use the corresponding TWI driver functions. For instance, in place of Wire.begin(), you should use an init function similar to twi_init() in the twi_sensor example, and use nrf_drv_twi_rx() and nrf_drv_twi_tx() in place of the read and write functions.

For details about configuring and setting GPIOs, you should have a look at the GPIO abstraction documentation. Pins are configured as input or output using nrf_gpio_cfg_input() and nrf_gpio_cfg_output() functions. There are functions available for setting, clearing, toggling and writing to GPIOs.

Best regards,

Jørgen

edit flag offensive delete publish link more

Comments

Thanks Jorgen, I was wondering as well, in order to make the read and write function, can I use the handler function of twi_sensorex_ample to read and write? I get a bit confused by the fact hat in the main of such as example there's an inifite loop for reading, should my function include that so?

ndarkness ( 2017-03-21 13:43:36 +0100 )editconvert to answer

You can use the handler function of the example, but you should modify it to do what you need for your sensor. The TWI sensor example runs in a loop as it reads the sensor once every 500ms (note the delay inside the loop). It is normal to have a loop in the application to put the chip in sleep mode when no processing needs to be done.

Jørgen Holmefjord ( 2017-03-21 13:52:16 +0100 )editconvert to answer

ok, thanks again, but that handler function only works for the twi_init() not for the receiver or transmitter, is there a way to reuse it for those functions? I would be interested on checking the events like NRF_DRV_TWI_XFER_TX or NRF_DRV_TWI_XFER_RX so that I can know that the transfer is done.

ndarkness ( 2017-03-21 14:16:52 +0100 )editconvert to answer

There is no such events using the TWI driver. If you want a higher level abstraction and control of your TWI transactions, you can check out the TWI transaction manager library.

Jørgen Holmefjord ( 2017-03-21 16:04:43 +0100 )editconvert to answer

Jorgen, then the handler that in twi_sensor_example make use of such a value, does it have a different meaning/purpose?

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_RX)
            {
                data_handler(m_sample);
            }
            m_xfer_done = true;
            break;
        default:
            break;
    }
}

I have created the function that you mentioned, but when reading from the sensor I get nothing, actually the check if (read8(BME280_REGISTER_CHIPID) != 0x60) seems to fail... I have updated the main post with my changes

ndarkness ( 2017-03-21 19:25:49 +0100 )editconvert to answer
1

Sorry, I misunderstood you. I thought you wanted separate events for TX and RX. You can check the transfer type of the event, but both TX and RX events are handled in the code you posted above. If the transfer type was TX, m_xfer_done is set, while if the transfer type was RX, the received data is handled using the function data_handler() in addition to m_xfer_done to true.

Have you checked the error codes returned by the functions?

Jørgen Holmefjord ( 2017-03-21 19:35:05 +0100 )editconvert to answer

Thanks Jorgen, then the code above should work to detect either a TX or RX transfers has been carried out ( the handler function in the main post that I have just edited)?

ndarkness ( 2017-03-21 19:39:48 +0100 )editconvert to answer

There were no error codes, as far as I remember, before executing the call to nrf_drv_twi_tx the return value of err_code was something like 0x040300 or so ( I don't have the board with me now). But just after the execution I could see in the debugger that it was 0x00000

ndarkness ( 2017-03-21 19:47:12 +0100 )editconvert to answer
1

You should send both register address and value in the same TX transfer (in your write8() function), something like the code shown below, else the driver will end the transmission and the sensor will most likely return to an idle state.

void Adafruit_BME280::write8(byte reg, byte value)
{
  if (_cs == -1) {
    byte xfer[2] = {reg, value};
    ret_code_t err_code;
    err_code = nrf_drv_twi_tx(&m_twi, _i2caddr, (const byte*)xfer, sizeof(xfer), false);
    APP_ERROR_CHECK(err_code);
  }
}

If you need to do two separate transfers, you need to make sure that the first transfer is finished before starting the second transfer, using the flags set in the handler.

Jørgen Holmefjord ( 2017-03-21 20:07:26 +0100 )editconvert to answer

I will try it tomorrow morning and come back with the outcome, thanks again!

ndarkness ( 2017-03-21 20:28:02 +0100 )editconvert to answer

Jorgen, is this also needed for the receiver part? I mean sending register address and value together.

ndarkness ( 2017-03-21 20:36:03 +0100 )editconvert to answer

From the Adafruit driver you posted, it looks like the read8 function first write the register, sends a stop flag and then request and reads 1 byte from the device. Note that the way devices use the TWI protocol varies from device to device. You need to look in the datasheet of the device to know exactly how to read and write to the device.

Jørgen Holmefjord ( 2017-03-22 09:47:41 +0100 )editconvert to answer

Hi Jorgen, thanks for replying,I see now that there is a write indeed I will incorporate the same into my code

ndarkness ( 2017-03-22 10:44:23 +0100 )editconvert to answer

Hi Jorgen that was ineeded something that was happening , now I read as BME280_REGISTER_CHIPID 0x60 but the assignation between my return value (aux) and the read one(valueAux) seems to fail not sure why ( I have upadted a pic of the debugger in such as case), notice that value before the return has only debugging purpose to check the value itself.

uint8_t Adafruit_BME280::read8(byte reg)
{
  uint8_t value = 0;
  uint8_t valueAux = 0;
  ret_code_t err_code;

  if (_cs == -1) {

    byte xfer[1] = {reg};
    err_code = nrf_drv_twi_tx(&m_twi, _i2caddr, (const byte*)xfer, sizeof(xfer), false);
    APP_ERROR_CHECK(err_code);
    while (m_txfer_done == false);

    err_code = nrf_drv_twi_rx(&m_twi, _i2caddr, &valueAux, sizeof(byte));
    APP_ERROR_CHECK(err_code);
    while (m_rxfer_done == false);
    if (m_rxfer_done) {
      value = valueAux;
      m_rxfer_done = false;
    }
  }
  value;
  return value;
}
ndarkness ( 2017-03-22 11:02:28 +0100 )editconvert to answer

What is the purpose of the 3rd last line of your above code? You are also waiting for m_txfer_done, while in the if-check you have m_rxfer_done.

Jørgen Holmefjord ( 2017-03-22 11:11:17 +0100 )editconvert to answer

sorry, you're right I had changed that in the code but not in the post, now it goes through the condition if (read8(BME280_REGISTER_CHIPID) != 0x60) but it stalls in the read16 function, I need to figure out how to translate these lines

//    Wire.requestFrom((uint8_t)_i2caddr, (byte)2);
//    value = (Wire.read() << 8) | Wire.read();

for reading 16bits, so far I have done this but still not working as expected

err_code = nrf_drv_twi_rx(&m_twi, _i2caddr, &valueAux, 2*sizeof(byte));//2*sizeof(byte) = 2bytes = 16bits
APP_ERROR_CHECK(err_code);
while (m_rxfer_done == false);
if (m_rxfer_done) {
  value = valueAux << 8;
  value = value | valueAux;
  m_rxfer_done = false;
}
ndarkness ( 2017-03-22 12:30:21 +0100 )editconvert to answer

You will need a transmitt here as well. I would suggest something like this (not tested):

uint8_t Adafruit_BME280::read16(byte reg)
{
  uint16_t value = 0;
  uint8_t valueAux[2];
  ret_code_t err_code;

  if (_cs == -1) {

    err_code = nrf_drv_twi_tx(&m_twi, _i2caddr, (const byte*)reg, sizeof(reg), false);
    APP_ERROR_CHECK(err_code);
    while (m_txfer_done == false);

    err_code = nrf_drv_twi_rx(&m_twi, _i2caddr, valueAux, 2*sizeof(byte));
    APP_ERROR_CHECK(err_code);
    while (m_rxfer_done == false);
    if (m_rxfer_done) {
      value = valueAux[0] << 8;
      value = value | valueAux[1];
      m_rxfer_done = false;
    }
  }
  return value;
}

You don't really need the if (m_rxfer_done) part though, as the above while loop will ensure the RX operation in finished.

Jørgen Holmefjord ( 2017-03-22 12:51:02 +0100 )editconvert to answer

Thanks Jorgen, I had the transmit as well but I didn't show before. Thanks for the tip I will incorporate as well, could you tell me why is better using an array than two variables, performance? In case of reading 24 bits, I can reuse the above function like this

uint32_t Adafruit_BME280::read24(byte reg)
{
  uint32_t value = 0;
  uint8_t valueAux[3] ;
  ret_code_t err_code;

  if (_cs == -1) {

    byte xfer[1] = {reg};
    err_code = nrf_drv_twi_tx(&m_twi, _i2caddr, (const byte*)xfer, sizeof(xfer), false);
    APP_ERROR_CHECK(err_code);
    while (m_txfer_done == false);

    err_code = nrf_drv_twi_rx(&m_twi, _i2caddr, valueAux, 3*sizeof(byte));//2*sizeof(byte) = 2bytes = 16bits
    APP_ERROR_CHECK(err_code);
    while (m_txfer_done == false);
      value = valueAux[0] << 8;
      value = valueAux[1] << 8;
      value = value | valueAux[0] | valueAux[1];
      m_rxfer_done = false;

//    Wire.beginTransmission((uint8_t)_i2caddr);
//    Wire.write((uint8_t)reg);
//    Wire.endTransmission();
//    Wire.requestFrom((uint8_t)_i2caddr, (byte)3);
//    
//    value = Wire.read();
//    value <<= 8;
//    value |= Wire.read();
//    value <<= 8;
//    value |= Wire.read();
      return value;
    }
ndarkness ( 2017-03-22 14:19:38 +0100 )editconvert to answer
1

The TWI driver will read one byte at a time and place it into the buffer. If you do not provide an array to the buffer parameter, the driver will try to access an index outside of the provided buffer on the second read. If you want to use single variable you need to call the rx function two times with a size of 1 byte to receive 16 bits.

Jørgen Holmefjord ( 2017-03-22 14:24:12 +0100 )editconvert to answer
1

Should work, but you need to use:

value = valueAux[0] << 16;
value = valueAux[1] << 8;
value = value | valueAux[2];

Otherwise, value will be overwritten multiple times.

Jørgen Holmefjord ( 2017-03-22 15:52:31 +0100 )editconvert to answer

I understand Jorgen, thanks again! I will try it

ndarkness ( 2017-03-22 16:07:26 +0100 )editconvert to answer

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer. Do not ask a new question or reply to an answer here.

[hide preview]

User menu

    or sign up

Recent questions

Question Tools

1 follower

Stats

Asked: 2017-03-20 21:33:24 +0100

Seen: 86 times

Last updated: 2 days ago