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

twi_sensor - override register value with TX and read it with RX - nrf52840 dk

Hello,

i have an issue with i2c on my nrf52840 dk. I attached the distance sensor vl53l1x to pin 27 (SCL) and 26 (SDA) and wanted to communicate via i2c with it. To test the connection, I planned to write to a register on the vl53l1x and read the value afterwards. Problem, no matter what I write via TX, I can't read the correct written value from the sensor register. It always reads a zero.

#include <stdio.h>
#include <string.h>
#include "boards.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_drv_twi.h"
#include "nrf_delay.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

/* TWI instance ID. */
#define TWI_INSTANCE_ID     0

#define VL53L1X_ADDR        0x29U //from scan

/* TWI instance. */
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

uint8_t buffer = 8;


__STATIC_INLINE void data_handler(uint8_t newValue)
{
    NRF_LOG_INFO("New value: %d ", newValue);
}


void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
{
    NRF_LOG_INFO("handler!");
    switch (p_event->type)
    {
        case NRF_DRV_TWI_EVT_DONE:
            if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
            {
                data_handler(buffer);
            }
            break;
        default:
            break;
    }
}


void twi_init (void)
{
    ret_code_t err_code;

    const nrf_drv_twi_config_t twi_vl53l1x_config = {
       .scl                = ARDUINO_SCL_PIN,
       .sda                = ARDUINO_SDA_PIN,
       .frequency          = NRF_DRV_TWI_FREQ_100K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init     = true
    };

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

    nrf_drv_twi_enable(&m_twi);
}


void sensorInit(){

    uint8_t Addr = 0x30;

	uint8_t reg[2] = {Addr, 0x10};
	ret_code_t err_code;
	
	err_code = nrf_drv_twi_tx(&m_twi, VL53L1X_ADDR, &reg, sizeof(reg), false);
    APP_ERROR_CHECK(err_code);
}

void sensorRead(){

    uint8_t regAddr = 0x30;
	ret_code_t err_code;

    err_code = nrf_drv_twi_tx(&m_twi, VL53L1X_ADDR, regAddr, 1, false);
	
	err_code = nrf_drv_twi_rx(&m_twi, VL53L1X_ADDR, &buffer, sizeof(buffer));
	APP_ERROR_CHECK(err_code);
}


int main(void)
{
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("\r\nTWI sensor example started.");
    NRF_LOG_FLUSH();
    twi_init();  
    sensorInit();

    nrf_delay_ms(500);   

    while (true)
    {
        nrf_delay_ms(500);

        sensorRead();
     
        NRF_LOG_FLUSH();
        
    }
}

/** @} */

How I understand the i2c drv:

  1. I can send via tx an array like reg[2] = {Addr, value} with a new value to the sensor register. 
  2. If I want to read a register I need to point to it, by sending just the address via tx. Then read the sensor via rx and write the read value to a buffer.

Is this correct?

So it overrides the initialized value (8) of the buffer, therefore i think it is reading something but not the correct (written) value. I must have made a mistake somewhere?!

Ps: I use Linux, SDK17, nrf52840 DK, VL53L1X

Parents
  • Hi,

    I suggest that you take a look at an earlier answer of mine in a another thread regarding how the I2C/TWI protocol works. The answer contains a link to a good read that is helpful to understand the protocol. 

    Regarding your issue that you only read a 0:

    The first step is to actually check what is sent by the slave, and verify if this is the same as you receive. Use a Oscilloscope or a logic analyzer, and check if the clock on the SCL is correct and if correct data is sent and received on the SDA line. Is the Slave sending the data that you expected it to send? 

    The next step is to verify if you're writing and configuring the device correctly. I couldn't find information regarding the register layout in the datasheet of the slave, it seems that you have to register to download the driver on their website. But, I find it hard to believe that you're initializing and reading the correct register. Because you write and read to the same register during the initialization and during the read transaction. I would expect you to write to a configuration register during the initialization and then read from a data register during the read transaction.

    You can start with the first step of checking if any data is sent by the slave and if it's correct by probing SDA Slight smile

    regards

    Jared 

Reply
  • Hi,

    I suggest that you take a look at an earlier answer of mine in a another thread regarding how the I2C/TWI protocol works. The answer contains a link to a good read that is helpful to understand the protocol. 

    Regarding your issue that you only read a 0:

    The first step is to actually check what is sent by the slave, and verify if this is the same as you receive. Use a Oscilloscope or a logic analyzer, and check if the clock on the SCL is correct and if correct data is sent and received on the SDA line. Is the Slave sending the data that you expected it to send? 

    The next step is to verify if you're writing and configuring the device correctly. I couldn't find information regarding the register layout in the datasheet of the slave, it seems that you have to register to download the driver on their website. But, I find it hard to believe that you're initializing and reading the correct register. Because you write and read to the same register during the initialization and during the read transaction. I would expect you to write to a configuration register during the initialization and then read from a data register during the read transaction.

    You can start with the first step of checking if any data is sent by the slave and if it's correct by probing SDA Slight smile

    regards

    Jared 

Children
  • Hi,

    thanks for your reply

    The first step is to actually check what is sent by the slave, and verify if this is the same as you receive. Use a Oscilloscope or a logic analyzer, and check if the clock on the SCL is correct and if correct data is sent and received on the SDA line. Is the Slave sending the data that you expected it to send?

    Good idea, I will do that.

    The next step is to verify if you're writing and configuring the device correctly. I couldn't find information regarding the register layout in the datasheet of the slave, it seems that you have to register to download the driver on their website. But, I find it hard to believe that you're initializing and reading the correct register. Because you write and read to the same register during the initialization and during the read transaction. I would expect you to write to a configuration register during the initialization and then read from a data register during the read transaction.

    I downloaded the driver earlier and I am working with it right now. What i found out was, during the initialization the init_method writes a config array (filled with config data) to register from 0x00 to 0x87. I guess that is what you mean with writing to a configuration register? So in other words, if I don't initialize the sensor, I can't read from data register properly?

    And another question. In your recommended i2c-tutorial (a very good tutorial btw) is written, the device address, 7 bits long, needs to have a 8 bit. And that bit has to be 0 or 1 in order to write or read to/from the device register. Do I have to consider this or does the i2c driver do it for me?

    regards

    Maboo

  • Hi,

    The driver will set the R/W flag based on if you're doing a write or a read.  The driver tx and rx function takes in a uint8_t address as a parameter. So it will effectively be 8 bits.

    regards

    Jared

  • Hi,

    I used a logic analyzer and found the problem:

    #include <stdio.h>
    #include <string.h>
    #include "boards.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "nrf_drv_twi.h"
    #include "nrf_delay.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    /* TWI instance ID. */
    #define TWI_INSTANCE_ID     0
    
    #define VL53L1X_ADDR        0x29U //from scan
    
    /* TWI instance. */
    static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);
    
    uint8_t buffer = 0x08;
    
    __STATIC_INLINE void data_handler(uint8_t newValue)
    {
        NRF_LOG_INFO("New value: %d ", newValue);
    }
    
    
    void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
    {
        NRF_LOG_INFO("handler!");
        switch (p_event->type)
        {
            case NRF_DRV_TWI_EVT_DONE:
                if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
                {
                    data_handler(buffer);
                }
                break;
            default:
                break;
        }
    }
    
    
    void twi_init (void)
    {
        ret_code_t err_code;
    
        const nrf_drv_twi_config_t twi_vl53l1x_config = {
           .scl                = ARDUINO_SCL_PIN,
           .sda                = ARDUINO_SDA_PIN,
           .frequency          = NRF_DRV_TWI_FREQ_400K,
           .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
           .clear_bus_init     = false
        };
    
        err_code = nrf_drv_twi_init(&m_twi, &twi_vl53l1x_config, twi_handler, NULL);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_twi_enable(&m_twi);
    }
    
    
    void sensorInit(){
    
        const uint8_t regAddr[2] = {0x00, 0xE5};
    	ret_code_t err_code;
    
        err_code = nrf_drv_twi_tx(&m_twi, VL53L1X_ADDR, regAddr, sizeof(regAddr), false);
        APP_ERROR_CHECK(err_code);
    }
    
    void sensorRead(){
    
        
        ret_code_t err_code;
    
    	err_code = nrf_drv_twi_rx(&m_twi, VL53L1X_ADDR, &buffer, sizeof(buffer));
        APP_ERROR_CHECK(err_code);
    }
    
    
    int main(void)
    {
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO("\r\nTWI sensor example started.");
        NRF_LOG_FLUSH();
        twi_init();  
    
        nrf_delay_ms(500); 
    
        sensorInit();    
    
        while (true)
        {
            nrf_delay_ms(500);
    
            sensorRead();
    
            NRF_LOG_INFO("Buffer %d ", buffer);
            NRF_LOG_FLUSH();        
        }
    }

    Thats my code to read from "VL53L1_FIRMWARE__SYSTEM_STATUS 0x00E5" register. It sends back the status, whether the sensor has booted or not. If it has, you receive a 3. 

    It took me a while, but I guess now I understand how to properly read and write via i2c (hopefully Slight smile).

    I have one last question. Is it possible to continuously read from a register? Because in my case it reads once a 3 and then I receive just zeros.

    regards 

    Maboo

  • Hi, 

    It depends on the slave. Most devices allow you to do additional reads without having to re-specify the register again. I would expect this sensor to also act this way based on the little information that is shared in the datasheet. Have you verified with the logical analyzer that you actually are getting zeros? Could you try decreasing the frequency? 

    regards

    Jared

  • Hi Jared,

    now its working. Unfortunately I can't tell were the problem was. But the logic analyzer shows that actually no zeros are being sent. So, I am happy now Slight smile Thanks a lot for your help.

    (I'am going to publish the code as soon as the sensor is completely working)

    Regards

    Maboo

Related