cancel
Showing results for 
Search instead for 
Did you mean: 

LSM303 Magnetometer Freezes while the Accelerometer Works - Guidance Wanted

CTong.4
Associate

Dear all,

A newbie here. Any help is much appreciated.

I appreciate this is an STM forum and while I am using ESP-IDF to interface with STM's lsm303, I believe the problem is caused by my incorrect configuration of the STM lsm303 sensor. Hence any guidance is much appreciated.

I am using an LSM303 sensor, connected via I2C to an ESP32 and programmed via ESP-IDF to retrieve the magnetometer and accelerometer data. I was previously using an Ardunio and the the Pololu LSM303 library and everything worked with the same wiring.

However, when using bare i2c in ESP-IDF, it seems like I have messed something up as the magnetometer data freezes unless I power cycle the LSM303 (rebooting the ESP32 doesn't seem to change the readings, while unplugging the VCC of the lsm303 and plugging it back in again results in new readings).

Here is the code, which reads the data at 50 Hz via I2C. I have checked various sources including the stack exchange electrical engineering forum and others but I am worried I missed something that may actually be quite obvious.

I have followed someone's advice to set "ODR[0:3]" to 220hz, which I believe I did as confirmed by a i2cdump (I assume it is chip 0x1E register 0x00) has hex 0x70, which is equivalent to 0b01110000.

Here is the i2c dump for reference:


_legacyfs_online_stmicro_images_0693W00000bi9tQQAQ.png 

Just for reference, here is the Code related to the i2c implementation, using ESP-IDF

#include "lsm303v2_clement.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
 
#define LSM303_ADDR_ACCEL 0x19 // Accelerometer I2C address //0x19 seems to work 
#define LSM303_ADDR_MAG   0x1E // Magnetometer I2C address
 
// Register addresses
#define LSM303_CTRL_REG1_A 0x20
#define LSM303_CTRL_REG4_A 0x23
#define LSM303_OUT_X_L_A   0x28
#define LSM303_CTRL_REG1_M 0x20
#define LSM303_CTRL_REG2_M 0x21
#define LSM303_CTRL_REG3_M 0x22
#define LSM303_CTRL_REG4_M 0x23
#define LSM303_OUT_X_H_M   0x03
 
//self added test
#define LSM303_OUT_X_L_M   0x04
 
//self added for setting to cont mode
#define LSM303_SR_REG_M 0x09
#define LSM303_MR_REG_M 0x02
 
// Circular buffer settings
#define BUFFER_SIZE 100 // 50 Hz * 2 seconds = 100 samples
#define BUFFER_MASK (BUFFER_SIZE - 1)
 
static lsm303_data_t buffer[BUFFER_SIZE];
static volatile uint32_t buffer_head = 0;
 
//static esp_err_t i2c_init(void);
//static esp_err_t lsm303_read_bytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len);
 
 
 
//new code
static esp_err_t i2c_init(void)
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = GPIO_NUM_21,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = GPIO_NUM_22,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 400000
    };
 
    i2c_param_config(I2C_NUM_0, &conf);
    return i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
}
 
static esp_err_t lsm303_read_bytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len)
{
    if (len == 0) {
        return ESP_OK;
    }
 
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg, true);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, true);
 
    if (len > 1) {
        i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK);
    }
 
    i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
    i2c_master_stop(cmd);
 
    esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(10)); //was 1000ms
    i2c_cmd_link_delete(cmd);
 
    return ret;
}
 
static esp_err_t lsm303_write_bytes(uint8_t addr, const uint8_t *data, uint8_t len)
{
    if (len == 0) {
        return ESP_OK;
    }
 
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write(cmd, data, len, true);
    i2c_master_stop(cmd);
 
    esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(10));
    i2c_cmd_link_delete(cmd);
 
    return ret;
}
//end of new code
 
void lsm303_init()
{
    i2c_init();
 
    // Set up the accelerometer
    uint8_t accel_config[2] = {LSM303_CTRL_REG1_A, 0x47}; // 50 Hz, normal mode, all axes enabled
    lsm303_write_bytes(LSM303_ADDR_ACCEL, accel_config, 2);
 
    vTaskDelay(pdMS_TO_TICKS(100)); //self added for debugging
    // Set up the magnetometer
    //uint8_t mag_config[5] = {LSM303_CTRL_REG1_M, 0x14, 0x00, 0x00, 0x00}; // 50 Hz, low power mode, continuous conversion
    uint8_t mag_config[5] = {LSM303_CTRL_REG1_M, 0x1C, 0x00, 0x00, 0x00}; // 50 Hz, high power mode, continuous conversion
    lsm303_write_bytes(LSM303_ADDR_MAG, mag_config, 5);
    
 
    //doesn't seem to fix the problem either
    //lsm303_write_bytes(LSM303_ADDR_MAG, LSM303_MR_REG_M, 0x00);
    uint8_t data[2] = {LSM303_MR_REG_M, 0x00};
    esp_err_t retfix2 = lsm303_write_bytes(LSM303_ADDR_MAG, data , 1);
    if (retfix2 != ESP_OK) {
        // Handle error
        ESP_LOGE("lsm303_init - fix2", "Fix2 failed, error: %d", retfix2);
    } else {
        ESP_LOGI("lsm303_init - fix2", "Fix2 Success");
    }
 
    //lets try the ODR[0:3] to magnetometer 220hz fix suggested by the stack forum
    //uint8_t ODRdata = 0x06; // Set ODR to 220Hz
    uint8_t dataODR[2] = {0x00, 0x70}; //data was 0x06, now changed to 0x70
    esp_err_t retODR = lsm303_write_bytes(LSM303_ADDR_MAG, dataODR, 2);
    if (retODR != ESP_OK) {
        // Handle error
        ESP_LOGE("lsm303_init - ODR", "Set ODR to 220hz failed, error: %d", retODR);
    } else {
        ESP_LOGI("lsm303_init - ODR", "Success");
    }
 
    /*uint8_t data = 0x94; // bits 7-6 set to 10
    esp_err_t ret = i2c_master_write_byte_data(I2C_MASTER_NUM, LSM303_MAG_ADDR, LSM303_MAG_CTRL_REG5, data);
    if (ret != ESP_OK) {
        printf("Failed to set LSM303 magnetometer data rate: %d\n", ret);
    }*/
}
 
void sensor_read_task(void *arg)
{
    TickType_t xLastWakeTime = xTaskGetTickCount();
 
    while (1)
    {
        lsm303_data_t data;
 
        // Read magnetometer data //note: originally it was accel data first but I decided to swap it around to see if it fixes the problem 
        uint8_t mag_data[6];
        lsm303_read_bytes(LSM303_ADDR_MAG, LSM303_OUT_X_H_M, mag_data, 6); //was X_H_M
        data.mx = (int16_t)(mag_data[0] << 8 | mag_data[1]);
        data.my = (int16_t)(mag_data[2] << 8 | mag_data[3]);
        data.mz = (int16_t)(mag_data[4] << 8 | mag_data[5]);
        /*lsm303_read_bytes(LSM303_ADDR_MAG, LSM303_OUT_X_L_M, mag_data, 6); //was X_H_M
        data.mx = (int16_t)(mag_data[1] << 8 | mag_data[0]);
        data.my = (int16_t)(mag_data[3] << 8 | mag_data[2]);
        data.mz = (int16_t)(mag_data[5] << 8 | mag_data[4]);*/
 
        vTaskDelay(pdMS_TO_TICKS(10));
 
        // Read accelerometer data
        uint8_t accel_data[6];
        lsm303_read_bytes(LSM303_ADDR_ACCEL, LSM303_OUT_X_L_A | 0x80, accel_data, 6);
        data.ax = (int16_t)(accel_data[1] << 8 | accel_data[0]);
        data.ay = (int16_t)(accel_data[3] << 8 | accel_data[2]);
        data.az = (int16_t)(accel_data[5] << 8 | accel_data[4]);
 
        
        
 
        // Save the data to the circular buffer
        buffer[buffer_head & BUFFER_MASK] = data;
        buffer_head++;
        ESP_LOGI("lsm303v2 - sensor_read_task", "accel data %s mag data %s", accel_data, mag_data);
        ESP_LOGI("lsm303v2 - sensor_read_task", "Accel Data %hd, %hd, %hd Mag data %hd, %hd, %hd", data.ax, data.ay, data.az, data.mx, data.my, data.mz);
        // Sleep for 20 ms (50 Hz)
        vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(20));
 
        //for testing purposes, reducing to 7Hz
        //vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(143));
    }
}

1 ACCEPTED SOLUTION

Accepted Solutions
CTong.4
Associate

Solved:

Needed to change: Chip: 0x1E Address: 0x02 Data: 0x0

I ended up going through the data sheet and checking the i2c dump 1 by 1 and found out!

The reason for this is in P.38 of the data sheet: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf 

MR_REG_M(02h) needs to be set to 0,0 to be in continuous conversion mode (the i2cdump revealed it was 0x03, which is 0b11.... in binary, representing sleep mode), but we want 0b00.

Thanks for all the help.

View solution in original post

1 REPLY 1
CTong.4
Associate

Solved:

Needed to change: Chip: 0x1E Address: 0x02 Data: 0x0

I ended up going through the data sheet and checking the i2c dump 1 by 1 and found out!

The reason for this is in P.38 of the data sheet: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf 

MR_REG_M(02h) needs to be set to 0,0 to be in continuous conversion mode (the i2cdump revealed it was 0x03, which is 0b11.... in binary, representing sleep mode), but we want 0b00.

Thanks for all the help.