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

How to use lis2dh12 with latest SDK 15.3 (SPI)

Hi I would like to use sdk15.3 with the \nRF5_SDK_15.3.0_59ac345\components\drivers_ext\lis2dh12 but I need to have it with SPI. Is this possible?

Parents
  • Take one of the 15.3.0 SPI examples and use the code I show here for initialisation, which works with the LIS2DH12. Since you already have i2c functional, that should get you most of the way there. SPI is much cleaner to use than i2c.

    // Description
    // ===========
    // The LIS2DH12 is an ultra-low-power highperformance three-axis linear accelerometer
    // belonging to the femto family with digital I2C/SPI serial interface standard output.
    // The LIS2DH12 has user-selectable full scales of +-2g/4g/8g/16g and is capable of measuring
    // accelerations with output data rates from 1 Hz to 5.3 kHz.
    // The self-test capability allows the user to check the functionality of the sensor in the final
    // application.
    // The device may be configured to generate interrupt signals by detecting two independent
    // inertial wake-up/free-fall events as well as by the position of the device itself.
    //
    // SPI Interface
    // =============
    // bit 0: RW bit. When 0, the data DI(7:0) is written into the device. When 1, the data DO(7:0)
    //                from the device is read. In the latter case, the chip will drive SDO at the start of bit 8.
    // bit 1: MS bit. When 0, the address will remain unchanged in multiple read/write commands.
    //                When 1, the address is auto incremented in multiple read/write commands.
    // bit 2-7:  address AD(5:0). This is the address field of the indexed register.
    // bit 8-15: data DI(7:0) (write mode). This is the data that is written into the device (MSb first).
    // bit 8-15: data DO(7:0) (read mode). This is the data that is read from the device (MSb first).
    //                In multiple read/write commands further blocks of 8 clock periods will be added. When the
    //                MS bit is 0, the address used to read/write data remains the same for every block. When
    //                the MS bit is 1, the address used to read/write data is increased at every block.
    
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
        ret_code_t err_code;
    
        spi_config.ss_pin    = LIS2DH12_CS_PIN;
        spi_config.miso_pin  = LIS2DH12_MISO_PIN;
        spi_config.mosi_pin  = LIS2DH12_MOSI_PIN;
        spi_config.sck_pin   = LIS2DH12_SCK_PIN;
        spi_config.frequency = NRF_DRV_SPI_FREQ_8M;  // LIS2DH12 works up to 10MHz on SPI
        spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
        spi_config.mode      = NRF_DRV_SPI_MODE_0;
        APP_ERROR_CHECK(nrf_drv_spi_init(&mLis2dh12SpiInstance, &spi_config, Lis2dh12_spi_event_handler, NULL));

    The event handler should be kept ultra-simple:

    // Interrupt-driven events related to ADC sampling
    static volatile bool mLisPacketTransferComplete = false;
    
    /**
     * @brief SPI event handler indicating SPI transfer has completed
     * @param event
     */
    static void lis2dh12_spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
    {
        mLisPacketTransferComplete = true;
    }

    Choose an SPI not used elsewhere:

    #define SPI_INSTANCE  2   // SPI instance index
    static const nrf_drv_spi_t mLisSpiInstance = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);

    Transmit and wait for a result (simplistic example):

     // Send a command
     APP_ERROR_CHECK(nrf_drv_spi_transfer(&mLisSpiInstance, tx_buf, length, rx_buf, length));
     // Check for successful transfer
     while (!mLisPacketTransferComplete) ;
     // Completed ok

    If you have issues with nrfx instead of nrf, this might be helpful

    #if defined(NRFX_SPI2_ENABLED)
    #warning NRFX_SPI2_ENABLED
    #endif
    #if defined(NRFX_SPIM2_ENABLED)
    #warning NRFX_SPIM2_ENABLED
    #endif
    #if defined(NRF_SPI2_ENABLED)
    #warning NRF_SPI2_ENABLED
    #endif
    #if defined(NRF_SPIM2_ENABLED)
    #warning NRF_SPIM2_ENABLED
    #endif

    Good luck, it works well. If you are going off-board via leads longer than an inch/25mm or so then boost the drive level on CLK.

  • Hi hmolesworth,

    Can you please show a clearer example that I can use for this application? I am not sure which example I can apply so that this is working fine.

    Thanks and have a nice day!

  • The code I posted above is all the code required to get it working. The difficult part is navigating sd_config.h. I have the following enabled (all others set to '0')::

    #define NRFX_SPI2_ENABLED 1
    #define NRFX_SPIM2_ENABLED 1
    #define SPI2_ENABLED 1
    #define SPI2_USE_EASY_DMA 1
    

    The various macros mess with these settings, so I don't actually know if they are correct, but it seems to work!

    Start by reading the Id code:

    // Device identification register - LIS3DH has same code as LIS2DH12
    #define WHO_AM_I        0x0F
    #define I_AM_LIS3DH     0x33
    #define I_AM_LIS2DH     0x33
    
    /**
     * @brief SPI event handler indicating SPI transfer has completed
     * @param event
     */
    static void acc_spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
    {
        mAccPacketTransferComplete = true;
    }
    
    /**
     * @brief Function Send a command to read Id
     * This function sends a one-byte command to ACC via SPI
     * @param[in] none
     */
    static uint8_t AccWhoAmI(void)
    {
        uint8_t tx_buf[] = {0x80|LIS2DH12_WHO_AM_I, 0xFF};
        uint8_t rx_buf[sizeof(tx_buf)];
        uint16_t length = sizeof(tx_buf);
    
        memset(rx_buf, '?', length);
        mAccPacketTransferComplete = false;
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&mAccSpiInstance, tx_buf, length, rx_buf, length));
        // Check for successful transfer
        while (!mAccPacketTransferComplete) ;
        // Confirm we have a connection, should be I_AM_LIS2DH
        return rx_buf[1];
    }
    

Reply
  • The code I posted above is all the code required to get it working. The difficult part is navigating sd_config.h. I have the following enabled (all others set to '0')::

    #define NRFX_SPI2_ENABLED 1
    #define NRFX_SPIM2_ENABLED 1
    #define SPI2_ENABLED 1
    #define SPI2_USE_EASY_DMA 1
    

    The various macros mess with these settings, so I don't actually know if they are correct, but it seems to work!

    Start by reading the Id code:

    // Device identification register - LIS3DH has same code as LIS2DH12
    #define WHO_AM_I        0x0F
    #define I_AM_LIS3DH     0x33
    #define I_AM_LIS2DH     0x33
    
    /**
     * @brief SPI event handler indicating SPI transfer has completed
     * @param event
     */
    static void acc_spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
    {
        mAccPacketTransferComplete = true;
    }
    
    /**
     * @brief Function Send a command to read Id
     * This function sends a one-byte command to ACC via SPI
     * @param[in] none
     */
    static uint8_t AccWhoAmI(void)
    {
        uint8_t tx_buf[] = {0x80|LIS2DH12_WHO_AM_I, 0xFF};
        uint8_t rx_buf[sizeof(tx_buf)];
        uint16_t length = sizeof(tx_buf);
    
        memset(rx_buf, '?', length);
        mAccPacketTransferComplete = false;
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&mAccSpiInstance, tx_buf, length, rx_buf, length));
        // Check for successful transfer
        while (!mAccPacketTransferComplete) ;
        // Confirm we have a connection, should be I_AM_LIS2DH
        return rx_buf[1];
    }
    

Children
  • Hi hmolesworth,

    thanks, but the lis2dh12 from the drivers_ext are using TWI can we just use it without changing anything? 

    ret_code_t lis2dh12_cfg_commit(lis2dh12_instance_t * p_inst)
    {
        ASSERT(p_inst != NULL);
        ret_code_t err;
        p_inst->ctrl0 &= ~LIS2DH12_CTRL_REG0_VALID_MASK;
        p_inst->ctrl0 |= LIS2DH12_CTRL_REG0_VALID_SET;
    
        uint8_t ctrl_msg[] = {
            LIS2DH12_REG_CTRL_REG0 | LIS2DH12_AUTO_INCR_MASK,
            p_inst->ctrl0,
            p_inst->temp_cfg,
            p_inst->ctrl1,
            p_inst->ctrl2,
            p_inst->ctrl3,
            p_inst->ctrl4,
            p_inst->ctrl5,
            p_inst->ctrl6,
            p_inst->reference
        };
        err = nrf_twi_sensor_write(p_inst->p_sensor_data,
                                   p_inst->sensor_addr,
                                   ctrl_msg,
                                   ARRAY_SIZE(ctrl_msg),
                                   true);
        RETURN_IF_ERR(err);
        uint8_t fifo_msg[] = {
            LIS2DH12_REG_FIFO_CTRL | LIS2DH12_AUTO_INCR_MASK,
            p_inst->fifo_ctrl,
            0,
            p_inst->int1_cfg,
            0,
            p_inst->int1_ths,
            p_inst->int1_dur,
            p_inst->int2_cfg,
            0,
            p_inst->int2_ths,
            p_inst->int2_dur,
            p_inst->click_cfg
        };
        err = nrf_twi_sensor_write(p_inst->p_sensor_data,
                                   p_inst->sensor_addr,
                                   fifo_msg,
                                   ARRAY_SIZE(fifo_msg),
                                   true);
        RETURN_IF_ERR(err);
    
        uint8_t time_msg[] = {
            LIS2DH12_REG_CLICK_THS | LIS2DH12_AUTO_INCR_MASK,
            p_inst->click_ths,
            p_inst->time_lim,
            p_inst->latency,
            p_inst->time_win,
            p_inst->act_ths,
            p_inst->act_dur
        };
        err = nrf_twi_sensor_write(p_inst->p_sensor_data,
                                   p_inst->sensor_addr,
                                   time_msg,
                                   ARRAY_SIZE(time_msg),
                                   true);
        return err;

    I am really confuse that where should I start from what you said.

    // Device identification register - LIS3DH has same code as LIS2DH12
    #define WHO_AM_I        0x0F
    #define I_AM_LIS3DH     0x33
    #define I_AM_LIS2DH     0x33
    
    /**
     * @brief SPI event handler indicating SPI transfer has completed
     * @param event
     */
    static void acc_spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
    {
        mAccPacketTransferComplete = true;
    }
    
    /**
     * @brief Function Send a command to read Id
     * This function sends a one-byte command to ACC via SPI
     * @param[in] none
     */
    static uint8_t AccWhoAmI(void)
    {
        uint8_t tx_buf[] = {0x80|LIS2DH12_WHO_AM_I, 0xFF};
        uint8_t rx_buf[sizeof(tx_buf)];
        uint16_t length = sizeof(tx_buf);
    
        memset(rx_buf, '?', length);
        mAccPacketTransferComplete = false;
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&mAccSpiInstance, tx_buf, length, rx_buf, length));
        // Check for successful transfer
        while (!mAccPacketTransferComplete) ;
        // Confirm we have a connection, should be I_AM_LIS2DH
        return rx_buf[1];
    }

    For instance, there do you mean to put these code? in the main.c ? or the lis2dh12 driver.

    Thank you very much !

    sorry that I am really confuse.

  • Don't use any of the i2c files and settings; SPI is much simpler and I see you don't have i2c anyway ..

  • Hi,

    I believe this is twi mean i2C nrf_twi_sensor_write(p_inst->p_sensor_data,p_inst->sensor_addr,time_msg,ARRAY_SIZE(time_msg),true); can you please let me know how exactly you make this work for spi? where should the place to add your code in. I really need this urgently. 

    Thanks!

  • Ah, so sorry - I am Old School. i2c (I2C) was the original multi-master, multi-slave, 2-wire communication protocol back in the early 80s and was great for its time. TWI is the same thing (pretty much, only a few differences) and I forget that TWI is now often used as the generic term - particularly with the Nordic teams. It's a bit like DFU - what? Oh, you mean OTA. Times change...

    Anyway, to clarify, "get rid of anything with TWI in the name and just use SPI" is what I was suggesting in my earlier posts. All the code you need for SPI I posted earlier, but perhaps start with running a Nordic example as the Nordic engineer suggests. For what you are doing (LIS2DH12 single slave sensor, nRF52832 single master) SPI is much easier than i2c (aka TWI). TWI is inherently slow as it requires the use of open-drain drivers (which allows multiple masters and multiple slaves). SPI instead uses an extra signal to select multiple slaves - Chip Select, usually /CS or NCS. On the nRF52 SPI can run up to 16MHz in single data mode, 4 x faster in quad (QSPI) mode. TWI is slow by comparison, 100kHz or 400kHz typically. The LIS2DH12 runs at 10MHz, 8MHz when used with nRF52832 as 10MHz is not a (supported) option.

    The other thing of beauty with SPI is that you cannot possibly get the pins connected wrong (oh yeah  ..); unlike serial uarts, for example, which use Tx and Rx. Tx? Tx from who? To whom? Rx from whom? To whom? One cpu/PC Tx is another cpu Rx/Tx so maybe swap the wires around and see if it works better. MISO, only an idiot can get that mixed up with MOSI . Many slaves, single master.

Related