esp32_shock/components/lis3dsh/shake_detect.c

434 lines
15 KiB
C
Raw Normal View History

2024-04-27 09:15:55 +08:00
// lis3dsh数据手册https://www.findic.com/doc/browser/8LWZ55XQZ?doc_id=48813340#locale=zh-CN
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
2024-04-27 09:42:47 +08:00
#include "esp_dsp.h"
2024-04-27 09:15:55 +08:00
#include "esp_err.h"
#include "esp_log.h"
#include "shake_detect.h"
#include "lis3dsh_lib/lis3dsh_reg.h"
static const char *TAG = "shake_detect";
static QueueHandle_t int1_evt_queue = NULL;
// 0--SPI, 1--I2C
static void lis3dsh_cs_io_init(int mode)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << LIS3DSH_CS_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
gpio_set_level(LIS3DSH_CS_IO, mode);
}
static void lis3dsh_i2c_sel_io_init(int addr01)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << LIS3DSH_I2C_SEL_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
gpio_set_level(LIS3DSH_I2C_SEL_IO, addr01);
}
static esp_err_t lis3dsh_i2c_master_init(void)
{
int i2c_master_port = LIS3DSH_I2C_MASTER_NUM;
lis3dsh_cs_io_init(1);
lis3dsh_i2c_sel_io_init(0);
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = LIS3DSH_I2C_MASTER_SDA_IO,
.scl_io_num = LIS3DSH_I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = LIS3DSH_I2C_MASTER_FREQ_HZ,
};
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
}
static void lis3dsh_led_init(void)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << LIS3DSH_LED_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
}
static void lis3dsh_led_toggle(void)
{
static int state = 0;
gpio_set_level(LIS3DSH_LED_IO, state);
state = !state;
}
static void IRAM_ATTR int1_isr_handler(void* arg)
{
uint32_t gpio_num = (uint32_t) arg;
if (gpio_num == LIS3DSH_INT1_IO)
{
xQueueSendFromISR(int1_evt_queue, &gpio_num, NULL);
}
}
static void lis3dsh_int1_int_init(void)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL << LIS3DSH_INT1_IO);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
int1_evt_queue = xQueueCreate(1, sizeof(uint32_t));
gpio_install_isr_service(0);
gpio_isr_handler_add(LIS3DSH_INT1_IO, int1_isr_handler, (void*) LIS3DSH_INT1_IO);
}
static int platform_init(void)
{
ESP_ERROR_CHECK(lis3dsh_i2c_master_init());
lis3dsh_int1_int_init();
lis3dsh_led_init();
return LIS3DSH_I2C_MASTER_NUM;
}
static int32_t platform_write(void *handle, uint8_t reg, uint8_t *bufp, uint16_t len)
{
uint8_t write_buf[128];
if (len > 127)
return -1;
write_buf[0] = reg;
memcpy(&write_buf[1], bufp, len);
return i2c_master_write_to_device(LIS3DSH_I2C_MASTER_NUM, LIS3DSH_I2C_ADD_L >> 1, write_buf, len + 1, LIS3DSH_I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}
static int32_t platform_read(void *handle, uint8_t reg, uint8_t *bufp, uint16_t len)
{
return i2c_master_write_read_device(LIS3DSH_I2C_MASTER_NUM, LIS3DSH_I2C_ADD_L >> 1, &reg, 1, bufp, len, LIS3DSH_I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}
static void platform_delay(uint32_t ms)
{
vTaskDelay(pdMS_TO_TICKS(ms));
}
stmdev_ctx_t dev_ctx;
void lis3dsh_init(void)
{
int ret = 0;
lis3dsh_pin_int1_route_t int1_route;
lis3dsh_all_sources_t all_sources;
lis3dsh_bus_mode_t bus_mode;
lis3dsh_status_var_t status;
lis3dsh_ctrl_reg3_t ctrl_reg3;
lis3dsh_id_t id = {0};
lis3dsh_md_t md;
lis3dsh_reg_t reg;
/* Initialize mems driver interface */
dev_ctx.handle = (void *)platform_init();
dev_ctx.write_reg = platform_write;
dev_ctx.read_reg = platform_read;
/* Wait sensor boot time */
platform_delay(BOOT_TIME);
/* Check device ID */
ret = lis3dsh_id_get(&dev_ctx, &id);
ESP_LOGI(TAG, "ret=%d, lis3dsh_id=%02x%02x%02x", ret, id.whoami, id.info1, id.info2);
while (id.whoami != LIS3DSH_ID)
{
platform_delay(100);
memset(&id, 0, sizeof(id));
ret = lis3dsh_id_get(&dev_ctx, &id);
ESP_LOGI(TAG, "ret=%d, lis3dsh_id=%02x%02x%02x", ret, id.whoami, id.info1, id.info2);
}
// 有时候系统启动会卡在这个循环里
// /* Restore default configuration */
// lis3dsh_init_set(&dev_ctx, LIS3DSH_RESET);
// do
// {
// lis3dsh_status_get(&dev_ctx, &status);
// } while (status.sw_reset);
/* Set bdu and if_inc recommended for driver usage */
lis3dsh_init_set(&dev_ctx, LIS3DSH_DRV_RDY);
/* Set Output Data Rate */
// ctrl_reg4, ctrl_reg5
lis3dsh_mode_get(&dev_ctx, &md);
md.fs = LIS3DSH_2g;
md.odr = LIS3DSH_1kHz6;
lis3dsh_mode_set(&dev_ctx, &md);
/* FIFO configuration */
// fifo_ctrl
lis3dsh_read_reg(&dev_ctx, LIS3DSH_FIFO_CTRL, &reg.byte, 1);
reg.fifo_ctrl.fmode = 2; /* FIFO stream mode */
reg.fifo_ctrl.wtmp = 16;
lis3dsh_write_reg(&dev_ctx, LIS3DSH_FIFO_CTRL, &reg.byte, 1);
// ctrl_reg6:fifo使能地址自增使能fifo水位中断int1使能
lis3dsh_read_reg(&dev_ctx, LIS3DSH_CTRL_REG6, &reg.byte, 1);
memset(&reg.byte, 0, sizeof(reg.byte));
ESP_LOGI(TAG, "LIS3DSH_CTRL_REG6 old=%02x", reg.byte);
reg.ctrl_reg6.fifo_en = PROPERTY_ENABLE;
reg.ctrl_reg6.wtm_en = PROPERTY_ENABLE;
reg.ctrl_reg6.add_inc = PROPERTY_ENABLE;
reg.ctrl_reg6.p1_wtm = PROPERTY_ENABLE;
lis3dsh_write_reg(&dev_ctx, LIS3DSH_CTRL_REG6, &reg.byte, 1);
lis3dsh_read_reg(&dev_ctx, LIS3DSH_CTRL_REG6, &reg.byte, 1);
ESP_LOGI(TAG, "LIS3DSH_CTRL_REG6 new=%02x", reg.byte);
// ctrl_reg3:中断配置int1使能DRDY失能int2失能低电平有效锁存
lis3dsh_read_reg(&dev_ctx, LIS3DSH_CTRL_REG3, (uint8_t *)&ctrl_reg3, 1);
memset(&ctrl_reg3, 0, sizeof(ctrl_reg3));
ctrl_reg3.int1_en = PROPERTY_ENABLE;
lis3dsh_write_reg(&dev_ctx, LIS3DSH_CTRL_REG3, (uint8_t *)&ctrl_reg3, 1);
lis3dsh_read_reg(&dev_ctx, LIS3DSH_CTRL_REG4, &reg.byte, 1);
ESP_LOGI(TAG, "LIS3DSH_CTRL_REG4 old=%02x", reg.byte);
}
// 乒乓缓冲区定义fft一次需要取N_SAMPLES个数据运算定义缓冲区大小为其2倍在写另一边的时候fft可以对这边数据进行运算
__attribute__((aligned(16)))
lis3dsh_a_data_t lis3dsh_data[N_SAMPLES * 2];
int buf_r = 0;
int fft_h = 0;
int angle_h = 0;
static QueueHandle_t fft_queue = NULL;
static QueueHandle_t angle_queue = NULL;
void lis3dsh_read_task(void)
{
uint32_t io_num;
lis3dsh_reg_t reg;
uint8_t rx_buffer[128];
while (1)
{
if(xQueueReceive(int1_evt_queue, &io_num, pdMS_TO_TICKS(1000)))
{
lis3dsh_read_reg(&dev_ctx, LIS3DSH_FIFO_SRC, &reg.byte, 1);
// ESP_LOGI(TAG, "fss:%d", reg.fifo_src.fss);
for (int i = 0; i < reg.fifo_src.fss; i++)
{
lis3dsh_read_reg(&dev_ctx, LIS3DSH_OUT_X_L, rx_buffer, 6);
// ESP_LOGI(TAG, "read %02d:%10f, %10f, %10f", i, (int16_t)(rx_buffer[0] | (rx_buffer[1] << 8)) * 2.0 / 0x7fff, (int16_t)(rx_buffer[2] | (rx_buffer[3] << 8)) * 2.0 / 0x7fff, (int16_t)(rx_buffer[4] | (rx_buffer[5] << 8)) * 2.0 / 0x7fff);
lis3dsh_data[buf_r].a[0] = (int16_t)(rx_buffer[0] | (rx_buffer[1] << 8)) * 2.0 / 0x7fff;
lis3dsh_data[buf_r].a[1] = (int16_t)(rx_buffer[2] | (rx_buffer[3] << 8)) * 2.0 / 0x7fff; // y 有重力加速度分量计算fft的时候需要去掉
lis3dsh_data[buf_r].a[2] = (int16_t)(rx_buffer[4] | (rx_buffer[5] << 8)) * 2.0 / 0x7fff;
buf_r++;
if (buf_r == N_SAMPLES || buf_r == N_SAMPLES * 2)
{
// ESP_LOGI(TAG, "send fft queue");
fft_h = buf_r - N_SAMPLES;
xQueueSend(fft_queue, &fft_h, portMAX_DELAY);
}
if (buf_r != 0 && buf_r % ANGLE_N_SAMPLES == 0)
{
// ESP_LOGI(TAG, "send angle queue");
angle_h = buf_r - ANGLE_N_SAMPLES;
xQueueSend(angle_queue, &angle_h, portMAX_DELAY);
}
if (buf_r == N_SAMPLES * 2)
{
buf_r = 0;
}
}
}
else
{
ESP_LOGI(TAG, "wait int1..."); // 很奇怪,第一次中断进不去,只有读一次才能进
lis3dsh_read_reg(&dev_ctx, LIS3DSH_OUT_X_L, rx_buffer, 6);
ESP_LOGI(TAG, "read :%f, %f, %f", (int16_t)(rx_buffer[0] | (rx_buffer[1] << 8)) * 2.0 / 0x7fff, (int16_t)(rx_buffer[2] | (rx_buffer[3] << 8)) * 2.0 / 0x7fff, (int16_t)(rx_buffer[4] | (rx_buffer[5] << 8)) * 2.0 / 0x7fff);
}
}
}
shake_detect_cfg_t shake_cfg = {10, 25, 0.06, 0.03, 2, 3, 3};
__attribute__((aligned(16)))
fft_data_t fft_data;
bool shake_detect_fun(float *feq_data)
{
if (feq_data == NULL) return false;
float a_max = 0;
float a_th = fft_data.is_shake ? shake_cfg.exit_amplitude_th : shake_cfg.enter_amplitude_th;
for (int i = shake_cfg.feq_min_id; i <= shake_cfg.feq_max_id; i++)
{
a_max = a_max >= feq_data[i] ? a_max : feq_data[i];
}
if (a_max >= a_th)
return true;
return false;
}
void fft_calculate_task(void)
{
2024-04-27 09:42:47 +08:00
esp_err_t ret;
2024-04-27 09:15:55 +08:00
fft_data.y1_cf = &fft_data.y_cf[0];
fft_data.y2_cf = &fft_data.y_cf[N_SAMPLES];
// fft 初始化
ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE);
if (ret != ESP_OK) {
ESP_LOGI(TAG, "Not possible to initialize FFT. Error = %i", ret);
return;
}
// Generate hann window
dsps_wind_hann_f32(fft_data.wind, N_SAMPLES);
while (1)
{
// ESP_LOGI(TAG, "fft wait...");
if(xQueueReceive(fft_queue, &fft_h, portMAX_DELAY))
{
// Convert two input vectors to one complex vector
uint8_t ch1 = shake_cfg.channel1;
uint8_t ch2 = shake_cfg.channel2;
if (ch1 <= 0 || ch1 > 3 || ch2 <= 0 || ch2 > 3)
{
ESP_LOGI(TAG, "CH select err[1-3]: ch1=%d, ch2=%d", ch1, ch2);
continue;
}
for (int i = 0 ; i < N_SAMPLES ; i++) {
fft_data.y_cf[i * 2 + 0] = lis3dsh_data[fft_h + i].a[ch1 - 1] * fft_data.wind[i];
fft_data.y_cf[i * 2 + 1] = lis3dsh_data[fft_h + i].a[ch2 - 1] * fft_data.wind[i];
}
// FFT
2024-04-27 09:42:47 +08:00
unsigned int start_b = dsp_get_cpu_cycle_count();
2024-04-27 09:15:55 +08:00
dsps_fft2r_fc32(fft_data.y_cf, N_SAMPLES);
2024-04-27 09:42:47 +08:00
unsigned int end_b = dsp_get_cpu_cycle_count();
2024-04-27 09:15:55 +08:00
// Bit reverse
dsps_bit_rev_fc32(fft_data.y_cf, N_SAMPLES);
// Convert one complex vector to two complex vectors
dsps_cplx2reC_fc32(fft_data.y_cf, N_SAMPLES);
// 归一化处理参考https://blog.csdn.net/weixin_39591031/article/details/110392352
for (int i = 0 ; i < N_SAMPLES / 2 ; i++) {
int normal_div = N_SAMPLES / 2.0;
if (i == 0) normal_div = N_SAMPLES;
fft_data.y1_cf[i] = (sqrt(fft_data.y1_cf[i * 2 + 0] * fft_data.y1_cf[i * 2 + 0] + fft_data.y1_cf[i * 2 + 1] * fft_data.y1_cf[i * 2 + 1]) / normal_div);
fft_data.y2_cf[i] = (sqrt(fft_data.y2_cf[i * 2 + 0] * fft_data.y2_cf[i * 2 + 0] + fft_data.y2_cf[i * 2 + 1] * fft_data.y2_cf[i * 2 + 1]) / normal_div);
// Simple way to show two power spectrums as one plot
fft_data.sum_y[i] = fmax(fft_data.y1_cf[i], fft_data.y2_cf[i]);
float freq = i * 1.0 / N_SAMPLES * 1600; // 单位HZ
// ESP_LOGI(TAG, "fft %d: f=%f, y=%f, z=%f sum=%f", i, freq, fft_data.y1_cf[i], fft_data.y2_cf[i], fft_data.sum_y[i]);
}
2024-04-27 09:42:47 +08:00
ESP_LOGW(TAG, "Signal ch1");
dsps_view(fft_data.y1_cf, N_SAMPLES / 2, 64, 10, -2, 2, '|');
ESP_LOGI(TAG, "FFT for %i complex points take %i cycles", N_SAMPLES, end_b - start_b);
2024-04-27 09:15:55 +08:00
// 震动检测
float *feq_data = NULL;
switch (shake_cfg.select)
{
case 1:
feq_data = fft_data.y1_cf;
break;
case 2:
feq_data = fft_data.y2_cf;
break;
case 3:
feq_data = fft_data.sum_y;
break;
default:
ESP_LOGI(TAG, "select err[1-3]: %d", shake_cfg.select);
continue;
break;
}
fft_data.is_shake = shake_detect_fun(feq_data);
// ESP_LOGI(TAG, "is_shake = %d", fft_data.is_shake);
lis3dsh_led_toggle();
}
}
}
#define PI (3.1415926)
void lis3dsh_calculating_angle(float ax, float ay, float az, float *anglex, float *angley, float *anglez)
{
float axy = sqrt(ax * ax + ay * ay);
*anglez = (atan2(axy, az) * 180 / PI);
float axz = sqrt(ax * ax + az * az);
*angley = (atan2(ay, axz) * 180 / PI);
float ayz = sqrt(ay * ay + az * az);
*anglex = (atan2(ax, ayz) * 180 / PI);
}
angle_data_t angle_data;
void angle_calculate_task(void)
{
float ax = 0, ay = 0, az = 0;
while (1)
{
// ESP_LOGI(TAG, "angle wait...");
if(xQueueReceive(angle_queue, &angle_h, portMAX_DELAY))
{
for (int i = 0; i < ANGLE_N_SAMPLES; i++)
{
ax += lis3dsh_data[angle_h + i].a[0];
ay += lis3dsh_data[angle_h + i].a[1];
az += lis3dsh_data[angle_h + i].a[2];
}
ax /= ANGLE_N_SAMPLES;
ay /= ANGLE_N_SAMPLES;
az /= ANGLE_N_SAMPLES;
lis3dsh_calculating_angle(ax, ay, az, &angle_data.x, &angle_data.y, &angle_data.z);
2024-04-27 09:42:47 +08:00
// ESP_LOGI(TAG, "angle: x=%5.5f\ty=%5.5f\tz=%5.5f", angle_data.x, angle_data.y, angle_data.z);
2024-04-27 09:15:55 +08:00
}
}
}
void shake_detect_init(void)
{
lis3dsh_init();
fft_queue = xQueueCreate(1, sizeof(uint32_t));
angle_queue = xQueueCreate(1, sizeof(uint32_t));
// 震动检测配置参数加载
xTaskCreatePinnedToCore(lis3dsh_read_task, "lis3dsh_read_task", 4096, NULL, 9, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(fft_calculate_task, "fft_calculate_task", 4096, NULL, 9, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(angle_calculate_task, "angle_calculate_task", 4096, NULL, 9, NULL, tskNO_AFFINITY);
}