349 lines
10 KiB
C
349 lines
10 KiB
C
#include <math.h>
|
|
#include "lsm6dsv.h"
|
|
#include "driver/i2c.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_system.h"
|
|
#include "esp_log.h"
|
|
#include "sdkconfig.h"
|
|
|
|
static const char *TAG = "LSM6DSV";
|
|
static const int I2C_PORT = 0;
|
|
|
|
// I2C helper functions
|
|
static esp_err_t i2c_write_reg(uint8_t reg_addr, uint8_t data) {
|
|
uint8_t write_buf[2] = {reg_addr, data};
|
|
return i2c_master_write_to_device(I2C_PORT, LSM6DSV_ADDR, write_buf, sizeof(write_buf), pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
static esp_err_t i2c_read_reg(uint8_t reg_addr, uint8_t *data) {
|
|
return i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®_addr, 1, data, 1, pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
/**
|
|
* @brief Converts uint16_t half-precision number into a uint32_t single-precision number.
|
|
* @param h half-precision number
|
|
* @param f pointer where the result of the conversion is written
|
|
* @retval 0
|
|
*/
|
|
esp_err_t npy_halfbits_to_floatbits(uint16_t h, uint32_t *f)
|
|
{
|
|
uint16_t h_exp, h_sig;
|
|
uint32_t f_sgn, f_exp, f_sig;
|
|
|
|
h_exp = (h & 0x7c00u);
|
|
f_sgn = ((uint32_t)h & 0x8000u) << 16;
|
|
switch (h_exp) {
|
|
case 0x0000u: /* 0 or subnormal */
|
|
h_sig = (h & 0x03ffu);
|
|
/* Signed zero */
|
|
if (h_sig == 0) {
|
|
*f = f_sgn;
|
|
return ESP_OK;
|
|
}
|
|
/* Subnormal */
|
|
h_sig <<= 1;
|
|
while ((h_sig & 0x0400u) == 0) {
|
|
h_sig <<= 1;
|
|
h_exp++;
|
|
}
|
|
f_exp = ((uint32_t)(127 - 15 - h_exp)) << 23;
|
|
f_sig = ((uint32_t)(h_sig & 0x03ffu)) << 13;
|
|
*f = f_sgn + f_exp + f_sig;
|
|
return ESP_OK;
|
|
case 0x7c00u: /* inf or NaN */
|
|
/* All-ones exponent and a copy of the significand */
|
|
*f = f_sgn + 0x7f800000u + (((uint32_t)(h & 0x03ffu)) << 13);
|
|
return ESP_OK;
|
|
default: /* normalized */
|
|
/* Just need to adjust the exponent and shift */
|
|
*f = f_sgn + (((uint32_t)(h & 0x7fffu) + 0x1c000u) << 13);
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
|
|
esp_err_t npy_half_to_float(uint16_t h, float *f)
|
|
{
|
|
union {
|
|
float ret;
|
|
uint32_t retbits;
|
|
} conv;
|
|
npy_halfbits_to_floatbits(h, &conv.retbits);
|
|
*f = conv.ret;
|
|
return ESP_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Compute quaternions.
|
|
* @param quat results of the computation
|
|
* @param sflp raw value of the quaternions
|
|
* @retval 0
|
|
*/
|
|
esp_err_t sflp2q(float quat[4], uint16_t sflp[3])
|
|
{
|
|
float sumsq = 0;
|
|
|
|
npy_half_to_float(sflp[0], &quat[0]);
|
|
npy_half_to_float(sflp[1], &quat[1]);
|
|
npy_half_to_float(sflp[2], &quat[2]);
|
|
|
|
for (uint8_t i = 0; i < 3; i++) {
|
|
sumsq += quat[i] * quat[i];
|
|
}
|
|
|
|
if (sumsq > 1.0f) {
|
|
float n = sqrtf(sumsq);
|
|
quat[0] /= n;
|
|
quat[1] /= n;
|
|
quat[2] /= n;
|
|
sumsq = 1.0f;
|
|
}
|
|
|
|
quat[3] = sqrtf(1.0f - sumsq);
|
|
return ESP_OK;
|
|
}
|
|
|
|
void quaternion_to_euler(float qx, float qy, float qz, float qw, float *roll, float *pitch, float *yaw) {
|
|
// Roll (X-axis rotation)
|
|
*roll = atan2f(2.0f * (qw * qx + qy * qz), 1.0f - 2.0f * (qx * qx + qy * qy));
|
|
|
|
// Pitch (Y-axis rotation)
|
|
*pitch = asinf(2.0f * (qw * qy - qz * qx));
|
|
|
|
// Yaw (Z-axis rotation)
|
|
*yaw = atan2f(2.0f * (qw * qz + qx * qy), 1.0f - 2.0f * (qy * qy + qz * qz));
|
|
}
|
|
|
|
esp_err_t lsm6dsv_init(int scl_pin, int sda_pin) {
|
|
// Configure I2C
|
|
i2c_config_t conf = {
|
|
.mode = I2C_MODE_MASTER,
|
|
.sda_io_num = sda_pin,
|
|
.scl_io_num = scl_pin,
|
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
|
.master.clk_speed = 400000 // 400 KHz
|
|
};
|
|
|
|
esp_err_t ret = i2c_param_config(I2C_PORT, &conf);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "I2C parameter configuration failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, 0, 0, 0);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "I2C driver installation failed");
|
|
return ret;
|
|
}
|
|
|
|
// Verify device ID
|
|
uint8_t who_am_i;
|
|
ret = i2c_read_reg(LSM6DSV_WHO_AM_I, &who_am_i);
|
|
if (ret != ESP_OK || who_am_i != 0x70) { // 0x70 is the expected WHO_AM_I value
|
|
ESP_LOGE(TAG, "Failed to verify device ID or incorrect device (expected: 0x70, got: 0x%02x)", who_am_i);
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
// Configure accelerometer (±2g, 104 Hz)
|
|
ret = i2c_write_reg(LSM6DSV_CTRL1, 0x44); // ODR = 104 Hz (0x04), ±2g (0x40)
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to configure accelerometer");
|
|
return ret;
|
|
}
|
|
|
|
// Verify accelerometer configuration
|
|
uint8_t ctrl1_val;
|
|
ret = i2c_read_reg(LSM6DSV_CTRL1, &ctrl1_val);
|
|
if (ret == ESP_OK) {
|
|
ESP_LOGI(TAG, "CTRL1 value: 0x%02x", ctrl1_val);
|
|
}
|
|
|
|
// Configure gyroscope (±250 dps, 104 Hz)
|
|
ret = i2c_write_reg(LSM6DSV_CTRL2, 0x04); // ODR = 104 Hz (0x04), ±250 dps (0x40)
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to configure gyroscope");
|
|
return ret;
|
|
}
|
|
|
|
// Verify gyroscope configuration
|
|
uint8_t regval, tmp1;
|
|
ret = i2c_read_reg(LSM6DSV_CTRL2, ®val);
|
|
if (ret == ESP_OK) {
|
|
ESP_LOGI(TAG, "CTRL2 value: 0x%02x", regval);
|
|
}
|
|
|
|
|
|
// ret = i2c_read_reg(LSM6DSV_SFLP_ODR, ®val);
|
|
// regval |= SFLP_ODR_15HZ;
|
|
// ret = i2c_write_reg(LSM6DSV_SFLP_ODR, regval);
|
|
|
|
// // get current value of FUNC_CFG_ACCESS
|
|
// ret = i2c_read_reg(LSM6DSV_FUNC_CFG_ACCESS, ®val);
|
|
|
|
// // enable embedded function access
|
|
// regval |= LSM6DSV_EMB_FUNC_REG_ACCESS;
|
|
// ret = i2c_write_reg(LSM6DSV_FUNC_CFG_ACCESS, regval);
|
|
|
|
|
|
// // enable game vector FIDO
|
|
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_FIFO_EN_A, SFLP_GAME_FIFO_EN);
|
|
|
|
// // enable sensor fusion algorithm
|
|
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_EN_A, SFLP_GAME_EN);
|
|
// ret = i2c_write_reg(LSM6DSV_EMB_FUNC_INIT_A, SFLP_GAME_INIT);
|
|
|
|
|
|
// // restore FUNC_CFG_ACCESS
|
|
// regval &= ~LSM6DSV_EMB_FUNC_REG_ACCESS;
|
|
// ret = i2c_write_reg(LSM6DSV_FUNC_CFG_ACCESS, regval);
|
|
|
|
|
|
// ret = i2c_write_reg(LSM6DSV_FIFO_CTRL4, 0x06);
|
|
|
|
// ret = i2c_read_reg(LSM6DSV_FIFO_CTRL4, ®val);
|
|
// if (ret == ESP_OK) {
|
|
// ESP_LOGI(TAG, "FIFO_CTRL4 value: 0x%02x", regval);
|
|
// }
|
|
|
|
// Add a small delay after configuration
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
|
|
ESP_LOGI(TAG, "LSM6DSV initialized successfully");
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t lsm6dsv_read_data(lsm6dsv_data_t *data) {
|
|
if (data == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
esp_err_t ret;
|
|
uint8_t buffer[12];
|
|
uint8_t reg = LSM6DSV_OUTX_L_A;
|
|
|
|
// Check status register
|
|
uint8_t status;
|
|
ret = i2c_read_reg(LSM6DSV_STATUS, &status);
|
|
if (ret == ESP_OK) {
|
|
ESP_LOGD(TAG, "Status register: 0x%02x", status);
|
|
if (!(status & 0x01)) { // Check if accelerometer data is ready
|
|
ESP_LOGW(TAG, "Accelerometer data not ready");
|
|
return ESP_ERR_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
// Read accelerometer data (6 bytes)
|
|
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, buffer, 6, pdMS_TO_TICKS(100));
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to read accelerometer data");
|
|
return ret;
|
|
}
|
|
|
|
ESP_LOGD(TAG, "ACC raw: x=0x%02x%02x y=0x%02x%02x z=0x%02x%02x",
|
|
buffer[1], buffer[0], buffer[3], buffer[2], buffer[5], buffer[4]);
|
|
|
|
// Check if gyroscope data is ready
|
|
ret = i2c_read_reg(LSM6DSV_STATUS, &status);
|
|
if (ret == ESP_OK) {
|
|
if (!(status & 0x02)) { // Check if gyroscope data is ready
|
|
ESP_LOGW(TAG, "Gyroscope data not ready");
|
|
return ESP_ERR_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
// Read gyroscope data (6 bytes)
|
|
reg = LSM6DSV_OUTX_L_G;
|
|
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, buffer + 6, 6, pdMS_TO_TICKS(100));
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to read gyroscope data");
|
|
return ret;
|
|
}
|
|
|
|
ESP_LOGD(TAG, "GYRO raw: x=0x%02x%02x y=0x%02x%02x z=0x%02x%02x",
|
|
buffer[7], buffer[6], buffer[9], buffer[8], buffer[11], buffer[10]);
|
|
|
|
// Convert accelerometer data (±2g scale)
|
|
const float acc_scale = 2.0f / 32768.0f; // ±2g range
|
|
data->acc_x = -(int16_t)(buffer[0] | (buffer[1] << 8)) * acc_scale;
|
|
data->acc_y = -(int16_t)(buffer[2] | (buffer[3] << 8)) * acc_scale;
|
|
data->acc_z = -(int16_t)(buffer[4] | (buffer[5] << 8)) * acc_scale;
|
|
|
|
// Convert gyroscope data (±250 dps scale)
|
|
const float gyro_scale = 250.0f / 32768.0f; // ±250 dps range
|
|
data->gyro_x = (int16_t)(buffer[6] | (buffer[7] << 8)) * gyro_scale;
|
|
data->gyro_y = (int16_t)(buffer[8] | (buffer[9] << 8)) * gyro_scale;
|
|
data->gyro_z = (int16_t)(buffer[10] | (buffer[11] << 8)) * gyro_scale;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
uint8_t lsm6dsv_fifo_ready(void)
|
|
{
|
|
esp_err_t ret;
|
|
uint8_t status;
|
|
|
|
// Check if fifo data is ready
|
|
ret = i2c_read_reg(LSM6DSV_FIFO_STATUS2, &status);
|
|
if (ret == ESP_OK) {
|
|
//ESP_LOGI(TAG, "FIFO_STATUS2: %d", status);
|
|
}
|
|
|
|
ret = i2c_read_reg(LSM6DSV_FIFO_STATUS1, &status);
|
|
if (ret == ESP_OK) {
|
|
return status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
esp_err_t lsm6dsv_fifo_read(lsm6dsv_fifo_t *fifo)
|
|
{
|
|
esp_err_t ret;
|
|
uint8_t data;
|
|
uint8_t reg;
|
|
ret = i2c_read_reg(LSM6DSV_FIFO_DATA_OUT_TAG, &data);
|
|
|
|
reg = LSM6DSV_FIFO_DATA_OUT_X_L;
|
|
ret = i2c_master_write_read_device(I2C_PORT, LSM6DSV_ADDR, ®, 1, fifo->data, 6, pdMS_TO_TICKS(100));
|
|
|
|
if (ret == ESP_OK)
|
|
{
|
|
fifo->count = (data & 0x06) >> 1;
|
|
fifo->tag = (data & 0xf8) >> 3;
|
|
|
|
fifo->v[0] = (uint16_t)(fifo->data[0] | (fifo->data[1] << 8));
|
|
fifo->v[1] = (uint16_t)(fifo->data[2] | (fifo->data[3] << 8));
|
|
fifo->v[2] = (uint16_t)(fifo->data[4] | (fifo->data[5] << 8));
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
esp_err_t lsm6dsv_getAttitude(lsm6dsv_fifo_t *fifo, float *roll, float *pitch, float *yaw)
|
|
{
|
|
float q[4];
|
|
sflp2q(q, fifo->v);
|
|
|
|
float w = q[3];
|
|
float x = q[0];
|
|
float y = q[1];
|
|
float z = q[2];
|
|
|
|
// Yaw (z-axis rotation)
|
|
*yaw = atan2(2.0f * (x * y + w * z), w * w + x * x - y * y - z * z);
|
|
|
|
// Pitch (y-axis rotation)
|
|
*pitch = -asin(2.0f * (x * z - w * y));
|
|
|
|
// Roll (x-axis rotation)
|
|
*roll = atan2(2.0f * (w * x + y * z), w * w - x * x - y * y + z * z);
|
|
|
|
return ESP_OK;
|
|
|
|
} |