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

Custom NRF52832 + LIS2DH12 accelerometer but always get '0'

I got a nrf52832 custom board to communicate with LIS2DH12 accelerometer by I2C(TWI).

I edited part of the code from "twi_sensor" project in NRF5_SDK version 17.0.

I tried to read the "x_out_low " byte from LIS2DH12, but always get zero.

Here's my schematic diagram:

Here's my code:

/**
 * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
 * @defgroup tw_sensor_example main.c
 * @{
 * @ingroup nrf_twi_example
 * @brief TWI Sensor Example main file.
 *
 * This file contains the source code for a sample application using TWI.
 *
 */

#include "app_error.h"
#include "app_util_platform.h"
#include "boards.h"
#include "nrf_delay.h"
#include "nrf_drv_twi.h"
#include <stdio.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 ACC_ADDR (0x33U >> 1)
#define X_OUT_L 0x28
#define SCL_PIN 5
#define SDA_PIN 2

/* Indicates if operation on TWI has ended. */
static volatile bool m_xfer_done = false;

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

/* Buffer for samples read from temperature sensor. */
static uint8_t m_sample;

/**
 * @brief Function for setting active mode on MMA7660 accelerometer.
 */
void ACC_set_mode(void) {
    ret_code_t err_code;

    // set data rate to 100 Hz
    uint8_t CTRL_REG1[] = {0x20, 0x5F};
    // set full-scale to +/- 2g
    uint8_t CTRL_REG4[] = {0x23, 0x80};
    // set FIFO to stream mode
    uint8_t FIFO_CTRL[] = {0x2E, 0x80};

    err_code = nrf_drv_twi_tx(&m_twi, ACC_ADDR, CTRL_REG1, sizeof(CTRL_REG1), false);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);

    m_xfer_done = false;
    err_code = nrf_drv_twi_tx(&m_twi, ACC_ADDR, CTRL_REG4, sizeof(CTRL_REG4), false);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);

    m_xfer_done = false;
    err_code = nrf_drv_twi_tx(&m_twi, ACC_ADDR, FIFO_CTRL, sizeof(FIFO_CTRL), false);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);
}

/**
 * @brief Function for handling data from temperature sensor.
 *
 * @param[in] temp          Temperature in Celsius degrees read from sensor.
 */
__STATIC_INLINE void data_handler(uint8_t temp) {
    printf("data: %d\n", temp);
}

/**
 * @brief TWI events handler.
 */
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;
    }
}

/**
 * @brief UART initialization.
 */
void twi_init(void) {
    ret_code_t err_code;

    const nrf_drv_twi_config_t twi_acc_config = {
        .scl = SCL_PIN,
        .sda = SDA_PIN,
        .frequency = NRF_DRV_TWI_FREQ_100K,
        .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
        .clear_bus_init = false};

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

    nrf_drv_twi_enable(&m_twi);
}

/**
 * @brief Function for reading data from temperature sensor.
 */
static void read_sensor_data() {
    printf("READING......\n");
    uint8_t addr8 = X_OUT_L;
    m_xfer_done = false;
    ret_code_t err_code = nrf_drv_twi_tx(&m_twi, ACC_ADDR, &addr8, 1, true);
    APP_ERROR_CHECK(err_code);
    while (m_xfer_done == false);

    err_code = nrf_drv_twi_rx(&m_twi, ACC_ADDR, &m_sample, sizeof(m_sample));
    APP_ERROR_CHECK(err_code);
    if(m_sample != 0){
        nrf_gpio_pin_toggle(LED_1);
        printf("read: %d\n", m_sample);
    }
}

/**
 * @brief Function for main application entry.
 */
int main(void) {
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    twi_init();

    nrf_gpio_cfg_input(3, NRF_GPIO_PIN_PULLUP);
    nrf_gpio_cfg_output(4);
    nrf_gpio_pin_write(4, 1);
    nrf_gpio_cfg_output(8);
    nrf_gpio_pin_write(8, 1);
    nrf_gpio_cfg_output(LED_1);
    ACC_set_mode();

    while (true) {
        nrf_delay_ms(500);

        do {
            __WFE();
        } while (m_xfer_done == false);
        
        // to confirm everything is still working
        nrf_gpio_pin_toggle(LED_1);
        read_sensor_data();
    }
}

/** @} */

Feel free to tell me if you need more information.

Any recommendation will be highly appreciated. Thank you very much!!

Parents
  • As you know, "If the SA0 pad is connected to the voltage supply, LSb is ‘1’ (address 0011001b), else if the SA0 pad is connected to ground, the LSb value is ‘0’ (address 0011000b)."

    This line doesn't do what is intended:

    #define ACC_ADDR (0x33U >> 1)

    That '1' LS bit has just been discarded; this is often confusing with i2c, is the 7-bit address left- or right-shifted or not and the answer depends on the library. Check the function to see whether it actually expects an un-shifted address.

    I would try this:

    #define ACC_ADDR (0x33U << 1)

    The red LED works fine off a CR2032, but to get enough voltage for blue and green LEDs when the battery is low try adding another i/o pin and driving a capacitor with a diode clamp to provide voltage boost; the nRF52832 4-output PWM circuit is ideal for this - red/green/blue/boost, a common application made easy.

Reply
  • As you know, "If the SA0 pad is connected to the voltage supply, LSb is ‘1’ (address 0011001b), else if the SA0 pad is connected to ground, the LSb value is ‘0’ (address 0011000b)."

    This line doesn't do what is intended:

    #define ACC_ADDR (0x33U >> 1)

    That '1' LS bit has just been discarded; this is often confusing with i2c, is the 7-bit address left- or right-shifted or not and the answer depends on the library. Check the function to see whether it actually expects an un-shifted address.

    I would try this:

    #define ACC_ADDR (0x33U << 1)

    The red LED works fine off a CR2032, but to get enough voltage for blue and green LEDs when the battery is low try adding another i/o pin and driving a capacitor with a diode clamp to provide voltage boost; the nRF52832 4-output PWM circuit is ideal for this - red/green/blue/boost, a common application made easy.

Children
  • Hi hmolesworth, thanks for your fast response!

    I left shifted the slave address, with debug mode in Segger Embedded Studio, but I got stuck in the while loop.(line 93)

        err_code = nrf_drv_twi_tx(&m_twi, ACC_ADDR, CTRL_REG1, sizeof(CTRL_REG1), false);
        APP_ERROR_CHECK(err_code);
        while (m_xfer_done == false); // <== stuck here

    It seems that "m_xfer_done" was always false which means twi_handler() never returned to NRF_DRV_TWI_EVT_DONE state.

    I read the official documentation and it said a 7-bit address is needed.

    I thought the slave address(0x33U >> 1) is correct, because the LED_1 still blinks.

    regards

  • The driver will take care of the R/W bit. You need to decide whether to use address 0b11000 or 0b11001. 

    By diving into the driver you'll eventually end up in:

    __STATIC_INLINE void nrf_twim_address_set(NRF_TWIM_Type * p_reg,
                                              uint8_t address)
    {
        p_reg->ADDRESS = address;
    }

    Where 'address' has not been modified in any way from what you pass to nrf_drv_twi_tx(). The  ADDRESS register description declares that the ADDRESS register takes a 7-bit address without any bit-shifting. 

    The R/W bit is added to the address depending on whether the TASKS_STARTRX or TASKS_STARTTX task was triggered. 


    For the future I suggest you add test points to all serial I/O lines, as a digital scope will give you a lot more information than a peripheral driver who's waiting for a reply from a slave who's not listening. 

  • Thanks for your reply, haakonsh!

    Maybe the bit shift is kind of confusing. What I'm doing is to discard the LSB of the 8-bit address and make it into a 7-bit address. The LSB in 0x33 is a R/W bit.

    0x33(0b 0011 0011)  right shift  ==> 0x19(0b 0001 1001) 

    The i2c slave address table in datasheet, 

    Documentation:  https://www.mouser.tw/datasheet/2/389/dm00091513-1797743.pdf

    I read the input like this, and I know SA0 is connected to high, so the slave address will be 0x19(first 7 bit).

        nrf_gpio_cfg_input(3, NRF_GPIO_PIN_PULLUP);
        int pin_status = nrf_gpio_pin_read(3);
        printf("%d: %d\n", 3, pin_status);

    I guess my situation is more likely to be a hardware problem. I run the same code on NRF52DK + ADXL345, with only register and slave address change, it works fine. I can read non-zero value from ADXL345.

    I pulled p0.04 high to enable I2C, and also pulled p0.08 high for power supply.  Am I doing something wrong?

    Thank you, regards

  •  You need to scope the I/O lines and the accelerometer's power supply. 

  • Looks like  you do have the correct device address, I thought the TWI driver bit-shifted but I see now that it does not.

    The 10uF capacitor is very large for a standard nRF52 port pin to drive; I would suggest reducing to 1uF or less but in any case set P0.08 to S0H1 drive. P0.08 must be driven high before P0.04 is driven high, as otherwise P0.04 provides phantom power via internal LIS2DH12 schottky clamp diode before real power is applied and that will cause the LIS2DH12 to behave badly. Allow a delay of (say) 30mSecs after raising P0.08 before setting P0.04 as an active high output to allow the huge 10uF capacitor to charge. This might correct the issue.

Related