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

SPI reading multiple register then concatenate

Hi,

I'm using the LSM6DS3 IMU using SPI. I am able to read the WHO_AM_I register and retrieve the correct data with this code based on the spi example from the SDKv14.2.0 :

static uint8_t       m_tx_buf[2];           /**< TX buffer. */
static uint8_t       m_rx_buf[2];           /**< RX buffer */

void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
    spi_xfer_done = true;
    NRF_LOG_HEXDUMP_INFO(m_rx_buf, rx_length);
}


int main(void)
{
    // [...]

    m_tx_buf[0] = (SPI_READ | LSM6DS3_WHO_AM_I );
    m_tx_buf[1] = 0;



    while (1)
    {
        memset(m_rx_buf, 0, rx_length);
        spi_xfer_done = false;

        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 1, m_rx_buf, rx_length));

        while (!spi_xfer_done)
        {
            __WFE();
        }
    }
}

I get the following output from the NRF_LOG_HEXDUMP_INFO : 00 69 has expected from the datasheet.

Now I want to retrieve value from the accelerometer but according to the doc, the result is splitted between two registers ! For example for Z-axis : OUTZ_L_XL (address : 0x2C) for LSbyte OUTZ_H_XL (address : 0x2D) for MSbyte

How do I read the two registers one after the other without risking to loose data ?

Because if I do :

m_tx_buf[0] = (SPI_READ | LSM6DS3_OUTZ_L_XL );
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 1, m_rx_buf, rx_length));
m_tx_buf[0] = (SPI_READ | LSM6DS3_OUTZ_H_XL );
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 1, m_rx_buf, rx_length));

How can I be sure that the LSB and the MSB that I read are supposed to be together and that the value didn't change between the two transfer calls ?

I hope it's not too blurred.

As an example, here is how they do it for Arduino but I can find a way to reproduce this with the SDK.

Thanks for your help

  • According to that datasheet your IMU supports multi-byte read operations (end of section 6.2), which auto-increments the address with every byte clocked out. Make m_rx_buf 3 bytes long instead of 2, and modify the rx_length variable used by the nrf_drv_spi_transfer function to be 3. The last two bytes in the buffer are your LSB and MSB. So a simple blocking method you could rewrite for your purpose would be:

    uint8_t outz_l_xl;
    uint8_t outz_h_xl;
    m_tx_buf[0] = (SPI_READ | LSM6DS3_OUTZ_L_XL );
    rx_length = 3;
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 1, m_rx_buf, rx_length));
    
    while (!spi_xfer_done) {}
    outz_l_xl = m_rx_buf[1];
    outz_h_xl = m_rx_buf[2];
    

    Note however that depending on your specific device this may not be enough (I'm not going through the whole datasheet for you). Some IMUs have an automatic update lock feature so as long as the bus is busy the values won't update. Others do not, in which case they usually have a FIFO you can take advantage of. As long as you don't wait too long to read the values and blow the FIFO's memory limit, each entry in the FIFO is guaranteed to be access safe, and is read using a multi byte access method.

  • Thanks it seems to work well. I will dig deeper but the first results are encouraging. Thank you very much

  • What if I want to give a value to the particular register. for an e.g

    m_tx_buf[0] = (SPI_READ | LSM6DS3_OUTZ_L_XL );

    m_tx_buf[1] = 0x01; //this could be any values then how it will retrieve

Related