纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。 Co-authored-by: Cursor <cursoragent@cursor.com>
989 lines
32 KiB
C
989 lines
32 KiB
C
#include <stdio.h>
|
||
#include <inttypes.h>
|
||
#include "../config.h"
|
||
|
||
#if !EXAMPLE_USE_TOUCH
|
||
#error "SC7U22 requires EXAMPLE_USE_TOUCH (IMU shares the touch I2C bus)."
|
||
#endif
|
||
|
||
#include "driver/gpio.h"
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "esp_log.h"
|
||
#include <math.h>
|
||
#include "esp_timer.h"
|
||
#include "../ui/generated/gui_guider.h" // 包含GUI引导器头文件
|
||
#include "lvgl.h" // 包含LVGL头文件
|
||
#include "LVGL/lvgl_init.h"
|
||
#include "ui_cmd.h"
|
||
#include "main.h"
|
||
|
||
|
||
static bool event_cooldown_active = false; // 是否处于冷却屏蔽中
|
||
static int64_t cooldown_end_time = 0; // 冷却结束时间戳
|
||
static const char *TAG_IMU = "SCU722";
|
||
i2c_master_dev_handle_t SC7U22_handle;
|
||
/** 与触摸共用的 I2C 总线句柄(SC7U22_init 写入,供扫描/查询) */
|
||
i2c_master_bus_handle_t s_i2c_bus;
|
||
sensor_data_t data_buffer[DATA_BUFFER_SIZE];
|
||
SC7U22_DataType sensor_data = {0};
|
||
static int buffer_index = 0;
|
||
static int buffer_count = 0;
|
||
|
||
struct acc_offset offset_data = {0};
|
||
noise_datatype noise_data = {0};
|
||
|
||
float ax=0,ay=0,az=0,gx=0,gy=0,gz=0;
|
||
//float acc_pre_x=0,acc_pre_y=0,acc_pre_z=0;
|
||
|
||
float acc_sum = 0;
|
||
float acc_sign_sum = 0;
|
||
|
||
float acc_hp = 0;
|
||
uint32_t bum_energy = 0;
|
||
float iir_state = 0;
|
||
|
||
uint16_t start_stop_cnt = 0;
|
||
uint16_t stop_cnt = 0;
|
||
|
||
|
||
uint8_t sharp_turn_flag = 0,pre_st_flag = 0;
|
||
bool sharpturn_ret_L = false,sharpturn_ret_R = false;
|
||
|
||
|
||
uint8_t rapid_acc_flag = 0,pre_ra_flag = 0,rapid_acc_ret = 0;
|
||
uint8_t rapid_dec_flag = 0,pre_rd_flag = 0,rapid_dec_ret = 0;
|
||
bool brake_fir_flag = false, rapid_fir_flag = false;
|
||
|
||
const char* MYTAG = "SC7U22";
|
||
|
||
extern int lottie_id;
|
||
extern int current_page_idx;
|
||
extern lv_ui guider_ui; // 外部声明GUI引导器
|
||
|
||
/*
|
||
* @brief This function converts lsb to meter per second squared for 16 bit accelerometer at
|
||
* range 2G, 4G, 8G or 16G.
|
||
*/
|
||
float lsb_to_mps2(int16_t val, float g_range, uint8_t bit_width)
|
||
{
|
||
double power = 2;
|
||
|
||
float half_scale = (float)((pow((double)power, (double)bit_width) / 2.0f));
|
||
|
||
return (GRAVITY_EARTH * val * g_range) / half_scale;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* @brief This function converts lsb to degree per second for 16 bit gyro at
|
||
* range 125, 250, 500, 1000 or 2000dps.
|
||
*/
|
||
float lsb_to_dps(int16_t val, float dps, uint8_t bit_width)
|
||
{
|
||
double power = 2;
|
||
|
||
float half_scale = (float)((pow((double)power, (double)bit_width) / 2.0f));
|
||
|
||
return (dps / (half_scale)) * (val);
|
||
}
|
||
|
||
void i2c_scan_devices(void)
|
||
{
|
||
if (s_i2c_bus == NULL) {
|
||
ESP_LOGW(MYTAG, "i2c_scan: bus not ready (call SC7U22_init first)");
|
||
return;
|
||
}
|
||
|
||
uint8_t address;
|
||
esp_err_t ret;
|
||
bool found = false;
|
||
|
||
ESP_LOGI(MYTAG, "Scanning I2C devices on shared bus...");
|
||
|
||
for (address = 1; address < 127; address++) {
|
||
ret = i2c_master_probe(s_i2c_bus, (uint16_t)address, 100);
|
||
|
||
if (ret == ESP_OK) {
|
||
ESP_LOGI(MYTAG, "Device found at address: 0x%02X", address);
|
||
found = true;
|
||
}
|
||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||
}
|
||
|
||
if (!found) {
|
||
ESP_LOGI(MYTAG, "No I2C devices found");
|
||
}
|
||
}
|
||
|
||
void SC7U22_init(i2c_master_bus_handle_t i2c_bus)
|
||
{
|
||
|
||
if (i2c_bus == NULL) {
|
||
ESP_LOGE(MYTAG, "SC7U22_init: I2C bus handle is NULL");
|
||
return;
|
||
}
|
||
|
||
s_i2c_bus = i2c_bus;
|
||
ESP_LOGI(MYTAG, "SC7U22 on shared I2C bus: %p", (void *)s_i2c_bus);
|
||
|
||
i2c_device_config_t dev_cfg = {
|
||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||
.device_address = SC7U22_I2C_ADDR,
|
||
.scl_speed_hz = I2C_MASTER_FREQ_HZ,
|
||
};
|
||
|
||
esp_err_t ret = i2c_master_bus_add_device(s_i2c_bus, &dev_cfg, &SC7U22_handle);
|
||
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE(MYTAG, "添加设备失败,错误码: %s", esp_err_to_name(ret));
|
||
return;
|
||
}
|
||
|
||
SC7U22_Sensor_enable();
|
||
|
||
ESP_LOGI(MYTAG, "SC7U22 初始化完成(硬件就绪,校准将在后台进行)");
|
||
}
|
||
esp_err_t SC7U22_Reg_W(uint8_t reg_addr, uint8_t data)
|
||
{
|
||
uint8_t write_buf[2] = {reg_addr, data};
|
||
return i2c_master_transmit(SC7U22_handle, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
|
||
}
|
||
|
||
esp_err_t SC7U22_Reg_R(uint8_t reg_addr, uint8_t *data)
|
||
{
|
||
return i2c_master_transmit_receive(SC7U22_handle, ®_addr, 1, data, 1, 1000 / portTICK_PERIOD_MS);
|
||
}
|
||
|
||
void SC7U22_Sensor_enable(void)
|
||
{
|
||
SC7U22_Reg_W(0x7F, 0x00); // Bank / page(如需要)
|
||
vTaskDelay(pdMS_TO_TICKS(2));
|
||
SC7U22_Reg_W(0x7D, 0x0E); // PWR_CTRL enable acc + gyro
|
||
vTaskDelay(pdMS_TO_TICKS(5));
|
||
|
||
// 加速度
|
||
SC7U22_Reg_W(0x40, 0xA8); // ACC_CONF(用缺省)
|
||
SC7U22_Reg_W(0x41, 0x02); // ±4g
|
||
|
||
// 陀螺仪(关键)
|
||
SC7U22_Reg_W(0x42, 0xA9); // GYR_CONF(缺省、安全)
|
||
SC7U22_Reg_W(0x43, 0x00); // ±2000 dps
|
||
|
||
}
|
||
|
||
|
||
void SC7U22_DaMYTAGet(SC7U22_DataType* SC7U22_DataStucture)
|
||
{
|
||
esp_err_t ret;
|
||
uint8_t raw_acc[6]; // 用于存放 AX_L, AX_H, AY_L, AY_H, AZ_L, AZ_H
|
||
uint8_t raw_gyr[6]; // 用于存放 GX_L, GX_H, GY_L, GY_H, GZ_L, GZ_H
|
||
|
||
// ================= 加速度计读取 (一次读6字节) =================
|
||
// 假设 XOUT_L 是起始地址。如果你的宏定义 XOUT_H 比 XOUT_L 小,请改用 XOUT_H
|
||
uint8_t acc_reg_addr = SC7U22_ACCEL_XOUT_H;
|
||
|
||
ret = i2c_master_transmit_receive(SC7U22_handle, &acc_reg_addr, 1, raw_acc, 6, 1000 / portTICK_PERIOD_MS);
|
||
|
||
if (ret == ESP_OK) {
|
||
// 拼接数据。注意:通常 I2C 传感器先发低位(L),后发高位(H)。
|
||
// 具体的 buffer 顺序取决于寄存器地址顺序。
|
||
// 假设地址顺序是: X_L(0), X_H(1), Y_L(2), Y_H(3), Z_L(4), Z_H(5)
|
||
SC7U22_DataStucture->Ax = (int16_t)((raw_acc[0] << 8) | raw_acc[1]);
|
||
SC7U22_DataStucture->Ay = (int16_t)((raw_acc[2] << 8) | raw_acc[3]);
|
||
SC7U22_DataStucture->Az = (int16_t)((raw_acc[4] << 8) | raw_acc[5]);
|
||
|
||
} else {
|
||
ESP_LOGE(MYTAG, "Accel read failed: %s", esp_err_to_name(ret));
|
||
}
|
||
|
||
// ================= 陀螺仪读取 (一次读6字节) =================
|
||
// 假设 XOUT_L 是起始地址
|
||
uint8_t gyr_reg_addr = SC7U22_GYRO_XOUT_H;
|
||
//暂时注释掉
|
||
ret = i2c_master_transmit_receive(SC7U22_handle, &gyr_reg_addr, 1, raw_gyr, 6, 1000 / portTICK_PERIOD_MS);
|
||
|
||
if (ret == ESP_OK) {
|
||
SC7U22_DataStucture->Gx = (int16_t)((raw_gyr[0] << 8) | raw_gyr[1]);
|
||
SC7U22_DataStucture->Gy = (int16_t)((raw_gyr[2] << 8) | raw_gyr[3]);
|
||
SC7U22_DataStucture->Gz = (int16_t)((raw_gyr[4] << 8) | raw_gyr[5]);
|
||
} else {
|
||
ESP_LOGE(MYTAG, "Gyro read failed");
|
||
}
|
||
}
|
||
|
||
void baseline_remove(void)
|
||
{
|
||
// 加速度计去偏
|
||
ax = lsb_to_mps2(sensor_data.Ax, (float)4, 16) - offset_data.acc_x_offset;
|
||
ay = lsb_to_mps2(sensor_data.Ay, (float)4, 16) - offset_data.acc_y_offset;
|
||
az = lsb_to_mps2(sensor_data.Az, (float)4, 16) - offset_data.acc_z_offset;
|
||
|
||
// 陀螺仪去偏 (新增)
|
||
gx = lsb_to_dps(sensor_data.Gx, (float)2000, 16) - offset_data.gyr_x_offset;
|
||
gy = lsb_to_dps(sensor_data.Gy, (float)2000, 16) - offset_data.gyr_y_offset;
|
||
gz = lsb_to_dps(sensor_data.Gz, (float)2000, 16) - offset_data.gyr_z_offset;
|
||
}
|
||
|
||
void noise_threshold_get(void)
|
||
{
|
||
uint8_t ax_cnt = 0,ay_cnt = 0,az_cnt = 0,gx_cnt = 0,gy_cnt = 0,gz_cnt = 0;
|
||
SC7U22_DaMYTAGet(&sensor_data);
|
||
baseline_remove();
|
||
for(int i = 0;i<50;i++)
|
||
{
|
||
if(ax!=0) {noise_data.accx_noise+=fabs(ax); ax_cnt++;}
|
||
if(ay!=0) {noise_data.accy_noise+=fabs(ay); ay_cnt++;}
|
||
if(az!=0) {noise_data.accz_noise+=fabs(az); az_cnt++;}
|
||
if(gx!=0) {noise_data.gyrx_noise+=fabs(gx); gx_cnt++;}
|
||
if(gy!=0) {noise_data.gyry_noise+=fabs(gy); gy_cnt++;}
|
||
if(gz!=0) {noise_data.gyrz_noise+=fabs(gz); gz_cnt++;}
|
||
vTaskDelay(pdMS_TO_TICKS(5));
|
||
}
|
||
noise_data.accx_noise/=(float)ax_cnt;
|
||
noise_data.accy_noise/=(float)ay_cnt;
|
||
noise_data.accz_noise/=(float)az_cnt;
|
||
noise_data.gyrx_noise/=(float)gx_cnt;
|
||
noise_data.gyry_noise/=(float)gy_cnt;
|
||
noise_data.gyrz_noise/=(float)gz_cnt;
|
||
}
|
||
|
||
void data_deal_with_noise(void)
|
||
{
|
||
if(fabs(ax)<fabs(noise_data.accx_noise)*1.5f+0.05) ax = 0;
|
||
if(fabs(ay)<fabs(noise_data.accy_noise)*1.5f+0.05) ay = 0;
|
||
if(fabs(az)<fabs(noise_data.accz_noise)*1.5f+0.05) az = 0;
|
||
if(fabs(gx)<fabs(noise_data.gyrx_noise)*1.5f+1) gx = 0;
|
||
if(fabs(gy)<fabs(noise_data.gyry_noise)*1.5f+1) gy = 0;
|
||
if(fabs(gz)<fabs(noise_data.gyrz_noise)*1.5f+1) gz = 0;
|
||
}
|
||
|
||
void mySC7_getdata(void)
|
||
{
|
||
|
||
SC7U22_DaMYTAGet(&sensor_data);
|
||
baseline_remove(); //去零偏处理
|
||
data_deal_with_noise();//去噪声处理
|
||
|
||
|
||
data_buffer[buffer_index].accel_x = ax;
|
||
data_buffer[buffer_index].accel_y = ay;
|
||
data_buffer[buffer_index].accel_z = az;
|
||
data_buffer[buffer_index].timestamp = esp_timer_get_time();
|
||
|
||
buffer_index = (buffer_index + 1) % DATA_BUFFER_SIZE;
|
||
if (buffer_count < DATA_BUFFER_SIZE) {
|
||
buffer_count++;
|
||
}
|
||
}
|
||
|
||
void accel_Getoffset(void)
|
||
{
|
||
float ax_sum=0, ay_sum=0, az_sum=0;
|
||
float gx_sum=0, gy_sum=0, gz_sum=0; // 新增
|
||
|
||
ESP_LOGI(MYTAG, "正在校准传感器,请保持静止...");
|
||
|
||
for(int i = 0; i < 100; i++) // 采样100次
|
||
{
|
||
SC7U22_DaMYTAGet(&sensor_data);
|
||
|
||
// 累加加速度
|
||
ax_sum += lsb_to_mps2(sensor_data.Ax, (float)4, 16);
|
||
ay_sum += lsb_to_mps2(sensor_data.Ay, (float)4, 16);
|
||
az_sum += lsb_to_mps2(sensor_data.Az, (float)4, 16);
|
||
|
||
// 累加陀螺仪 (注意:这里使用 2000dps 量程)
|
||
gx_sum += lsb_to_dps(sensor_data.Gx, (float)2000, 16);
|
||
gy_sum += lsb_to_dps(sensor_data.Gy, (float)2000, 16);
|
||
gz_sum += lsb_to_dps(sensor_data.Gz, (float)2000, 16);
|
||
|
||
vTaskDelay(pdMS_TO_TICKS(10));
|
||
}
|
||
|
||
// 计算平均值
|
||
offset_data.acc_x_offset = ax_sum / 100.0f;
|
||
offset_data.acc_y_offset = ay_sum / 100.0f;
|
||
offset_data.acc_z_offset = (az_sum / 100.0f) - GRAVITY_EARTH; // Z轴要扣除重力
|
||
|
||
// 计算陀螺仪偏移 (理论上静止时全是0,所以读数即为偏移)
|
||
offset_data.gyr_x_offset = gx_sum / 100.0f;
|
||
offset_data.gyr_y_offset = gy_sum / 100.0f;
|
||
offset_data.gyr_z_offset = gz_sum / 100.0f;
|
||
|
||
int gz_milli = (int)(offset_data.gyr_z_offset * 1000.0f);
|
||
ESP_LOGI(MYTAG, "校准完成! Gz Offset: %d.%03d", gz_milli / 1000, abs(gz_milli % 1000));
|
||
}
|
||
void bum_energy_get(void)
|
||
{
|
||
acc_sum += fabs(az);
|
||
acc_sign_sum += az;
|
||
iir_state = ALPHA * iir_state + (1 - ALPHA) * acc_sum;
|
||
acc_hp = acc_sum -iir_state; //高通滤波
|
||
bum_energy += (uint32_t)(acc_hp*acc_hp*1000); //处理颠簸能量
|
||
}
|
||
|
||
bool is_bump(void)
|
||
{
|
||
uint32_t temp = bum_energy;
|
||
static bool wasbump = false;
|
||
float variance = 0;
|
||
bum_energy = 0;
|
||
acc_sum = 0;
|
||
acc_sign_sum /= DATA_BUFFER_SIZE;
|
||
for(int i = 0;i<DATA_BUFFER_SIZE;i++)
|
||
{
|
||
variance += fabs(data_buffer[i].accel_z - acc_sign_sum); //平均绝对值误差
|
||
}
|
||
acc_sign_sum = 0;
|
||
bool isbump = (temp>=BUM_THRESHOLD&&variance>=400);
|
||
if(isbump&&!wasbump)
|
||
{
|
||
wasbump = isbump;
|
||
return true;
|
||
}
|
||
wasbump = isbump;
|
||
return false;
|
||
}
|
||
|
||
void sharp_turn_detect(void)
|
||
{
|
||
if(fabs(gz) > turn_gyro_threshold && fabs(ay) > turn_accel_threshold)
|
||
{
|
||
sharp_turn_flag++;
|
||
}
|
||
else
|
||
{
|
||
sharp_turn_flag = 0;
|
||
}
|
||
|
||
if(sharp_turn_flag == 0 && pre_st_flag >= 5) // 连续触发次数稍微减少一点,灵敏些
|
||
{
|
||
pre_st_flag = 0;
|
||
// 左转通常 gyrz > 0 (逆时针),右转 gyrz < 0 (顺时针)
|
||
// 具体正负取决于芯片安装面,你测试时如果不准,互换一下即可
|
||
if(gz > 0) sharpturn_ret_L = true;
|
||
else if(gz < 0) sharpturn_ret_R = true;
|
||
}
|
||
pre_st_flag = sharp_turn_flag;
|
||
}
|
||
|
||
driving_event_t is_sharp_turn(void)
|
||
{
|
||
if(sharpturn_ret_L)
|
||
{
|
||
sharpturn_ret_L = 0;
|
||
return EVENT_SHARP_LTURN;
|
||
}
|
||
else if(sharpturn_ret_R)
|
||
{
|
||
sharpturn_ret_R = 0;
|
||
return EVENT_SHARP_RTURN;
|
||
}
|
||
return EVENT_NONE;
|
||
}
|
||
|
||
static float calculate_acceleration_magnitude(void)
|
||
{
|
||
return sqrtf(ax * ax + ay * ay + az * az);
|
||
}
|
||
|
||
void start_stop_state(void)
|
||
{
|
||
static uint8_t st_cnt = 0;
|
||
static uint8_t sp_cnt = 0;
|
||
static bool stop_flag = false;
|
||
bool is_stop = calculate_acceleration_magnitude()<= is_stop_magnitude;
|
||
if(is_stop&&!stop_flag) sp_cnt++;
|
||
if(sp_cnt>=3)
|
||
{
|
||
stop_flag = true;
|
||
sp_cnt = 0;
|
||
}
|
||
if(is_stop&&stop_flag)
|
||
{
|
||
stop_cnt++;
|
||
}
|
||
|
||
if(stop_flag&&!is_stop)
|
||
{
|
||
st_cnt++;
|
||
}
|
||
else{
|
||
st_cnt = 0;
|
||
}
|
||
if(st_cnt>=3)
|
||
{
|
||
start_stop_cnt++;
|
||
st_cnt = 0;
|
||
stop_flag = false;
|
||
}
|
||
}
|
||
|
||
// 判断急停急加速
|
||
void detect_driving_events(void)
|
||
{
|
||
//获取数据
|
||
mySC7_getdata();
|
||
//颠簸能量累加
|
||
bum_energy_get();
|
||
//急转弯检测
|
||
sharp_turn_detect();
|
||
//急加速检测 急刹车检测
|
||
|
||
if (buffer_count < 2) return ;
|
||
|
||
int current_idx = (buffer_index - 1 + DATA_BUFFER_SIZE) % DATA_BUFFER_SIZE;
|
||
int prev_idx = (buffer_index - 2 + DATA_BUFFER_SIZE) % DATA_BUFFER_SIZE;
|
||
|
||
// 计算时间差(秒)
|
||
float time_diff = (data_buffer[current_idx].timestamp -
|
||
data_buffer[prev_idx].timestamp) / 1000000.0f;
|
||
|
||
if (time_diff <= 0) return ;
|
||
|
||
|
||
float accel_diff_x = data_buffer[current_idx].accel_x - data_buffer[prev_idx].accel_x;
|
||
float jerk_x = accel_diff_x / time_diff; // 急动度
|
||
|
||
// 急刹车/减速检测
|
||
// 逻辑:当前有一个很大的负向加速度(刹车),且变化率很大
|
||
// 注意:如果是“前倾 accx 变大”,说明 +X 是车头向下或向前。
|
||
// 假设 +X 是向前加速,那么 -X 就是刹车。
|
||
if ((jerk_x < -rapid_accel_threshold/0.02) && (data_buffer[current_idx].accel_x < -rapid_accel_threshold))
|
||
rapid_dec_flag = 1;
|
||
|
||
if ((data_buffer[current_idx].accel_x < -rapid_accel_threshold) && rapid_dec_flag)
|
||
rapid_dec_flag++;
|
||
else
|
||
rapid_dec_flag = 0;
|
||
|
||
if (rapid_dec_flag == 0 && pre_rd_flag >= 4) rapid_dec_ret = 1; // 阈值改小一点容易触发
|
||
pre_rd_flag = rapid_dec_flag;
|
||
|
||
// 急加速检测
|
||
// 逻辑:当前有一个很大的正向加速度
|
||
if ((jerk_x > rapid_accel_threshold/0.02) && (data_buffer[current_idx].accel_x > rapid_accel_threshold))
|
||
rapid_acc_flag = 1;
|
||
|
||
if ((data_buffer[current_idx].accel_x > rapid_accel_threshold) && rapid_acc_flag)
|
||
rapid_acc_flag++;
|
||
else
|
||
rapid_acc_flag = 0;
|
||
|
||
if (rapid_acc_flag == 0 && pre_ra_flag >= 4) rapid_acc_ret = 1;
|
||
pre_ra_flag = rapid_acc_flag;
|
||
|
||
}
|
||
driving_event_t is_rapid_acc_or_dec(void)
|
||
{
|
||
if(rapid_acc_ret)
|
||
{
|
||
rapid_acc_ret = 0;
|
||
return EVENT_SUDDEN_ACCEL;
|
||
}
|
||
if(rapid_dec_ret)
|
||
{
|
||
rapid_dec_ret = 0;
|
||
return EVENT_SUDDEN_BRAKE;
|
||
}
|
||
return EVENT_NONE;
|
||
}
|
||
|
||
bool is_jam(void)
|
||
{
|
||
|
||
float stop_proportion = (float)stop_cnt/600.0;
|
||
uint16_t temp_cnt = start_stop_cnt;
|
||
stop_cnt = 0;
|
||
start_stop_cnt = 0;
|
||
if((stop_proportion>=stop_proportion_threshold&&temp_cnt>=start_stop_threshold)||stop_proportion>=0.8f)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
// 在IMU任务函数外定义(全局区),避免栈溢出,同时供回调函数访问
|
||
// 驾驶事件冷却检查:冷却中返回true,允许触发返回false
|
||
bool is_event_in_cooldown(void)
|
||
{
|
||
if (!event_cooldown_active) {
|
||
return false;
|
||
}
|
||
|
||
// 当前时间 < 结束时间 = 还在冷却
|
||
if (esp_timer_get_time() < cooldown_end_time) {
|
||
return true;
|
||
} else {
|
||
// 冷却结束,复位
|
||
event_cooldown_active = false;
|
||
ESP_LOGI(MYTAG, "冷却时间已结束,可以再次触发驾驶事件");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 触发一次事件后,启动50秒冷却
|
||
void start_event_cooldown(void)
|
||
{
|
||
event_cooldown_active = true;
|
||
cooldown_end_time = esp_timer_get_time() + (EVENT_COOLDOWN_MS * 1000); // 转微秒
|
||
ESP_LOGI(MYTAG, "已启动冷却,%d 毫秒内不再触发", EVENT_COOLDOWN_MS);
|
||
}
|
||
|
||
/** 诊断:因 g_ble_play_sticker_active 抑制幸运表情时打印(节流,避免刷屏) */
|
||
#define BLE_GUARD_LOG_THROTTLE_MS 3000
|
||
static uint32_t s_ble_guard_last_log_tick;
|
||
static void ble_guard_log_lucky_suppressed(const char *reason)
|
||
{
|
||
uint32_t t = lv_tick_get();
|
||
if (s_ble_guard_last_log_tick != 0 && (t - s_ble_guard_last_log_tick) < BLE_GUARD_LOG_THROTTLE_MS)
|
||
return;
|
||
s_ble_guard_last_log_tick = t;
|
||
ESP_LOGW("BLE_STICKER_GUARD",
|
||
"Lucky suppressed: %s (g_ble_play_sticker_active still true)",
|
||
reason);
|
||
}
|
||
|
||
void imu_task(void *param)
|
||
{
|
||
ESP_LOGI(MYTAG, "Starting IMU task");
|
||
driving_event_t drive_even = EVENT_NONE;
|
||
uint16_t i = 0;
|
||
|
||
/* 保持原校准逻辑不变,只调整执行时机:由主初始化路径改为后台任务执行。 */
|
||
uint32_t calib_start_ms = (uint32_t)(esp_timer_get_time() / 1000ULL);
|
||
ESP_LOGI(MYTAG, "IMU 后台校准开始");
|
||
vTaskDelay(pdMS_TO_TICKS(20));
|
||
accel_Getoffset();
|
||
noise_threshold_get();
|
||
uint32_t calib_elapsed_ms = (uint32_t)(esp_timer_get_time() / 1000ULL) - calib_start_ms;
|
||
ESP_LOGI(MYTAG, "IMU 后台校准完成,耗时: %" PRIu32 " ms", calib_elapsed_ms);
|
||
|
||
while(1)
|
||
{
|
||
// ========== 原有逻辑保持不变 ==========
|
||
mySC7_getdata(); // 永远读取传感器数据
|
||
ESP_LOGW(MYTAG,"正在读取IMU数据...");
|
||
if (current_page_idx == 0)
|
||
{
|
||
detect_driving_events();
|
||
drive_even = is_sharp_turn();
|
||
if(drive_even == EVENT_SHARP_LTURN) //急转弯判断 结束一个急转弯后判定
|
||
{
|
||
if (is_event_in_cooldown()) continue; // 冷却中直接跳过
|
||
if (g_ble_play_sticker_active)
|
||
{
|
||
ble_guard_log_lucky_suppressed("left sharp turn");
|
||
continue; /* BLE 正在播表情时不插播 IMU 幸运 */
|
||
}
|
||
start_event_cooldown(); // 启动50秒冷却
|
||
|
||
|
||
/*左急转弯反馈入口*/
|
||
lottie_id = 29;
|
||
ESP_LOGE(MYTAG,"左急转!");
|
||
/*左急转弯反馈结束*/
|
||
}else if(drive_even == EVENT_SHARP_RTURN)
|
||
{
|
||
|
||
if (is_event_in_cooldown()) continue; // 冷却中直接跳过
|
||
if (g_ble_play_sticker_active)
|
||
{
|
||
ble_guard_log_lucky_suppressed("right sharp turn");
|
||
continue;
|
||
}
|
||
start_event_cooldown(); // 启动50秒冷却
|
||
|
||
|
||
/*右急转弯反馈入口*/
|
||
lottie_id = 29;
|
||
ESP_LOGE(MYTAG,"右急转!");
|
||
/*右急转弯反馈入口*/
|
||
}
|
||
|
||
drive_even = is_rapid_acc_or_dec();
|
||
if(drive_even==EVENT_SUDDEN_ACCEL)
|
||
{
|
||
if (is_event_in_cooldown()) continue; // 冷却中直接跳过
|
||
if (g_ble_play_sticker_active)
|
||
{
|
||
ble_guard_log_lucky_suppressed("sudden accel");
|
||
continue;
|
||
}
|
||
start_event_cooldown(); // 启动50秒冷却
|
||
|
||
|
||
/*急加速反馈入口*/
|
||
lottie_id = 29;
|
||
ESP_LOGE(MYTAG,"急加速!");
|
||
/*急加速反馈结束*/
|
||
}
|
||
else if(drive_even==EVENT_SUDDEN_BRAKE)
|
||
{
|
||
if (is_event_in_cooldown()) continue; // 冷却中直接跳过
|
||
if (g_ble_play_sticker_active)
|
||
{
|
||
ble_guard_log_lucky_suppressed("sudden brake");
|
||
continue;
|
||
}
|
||
start_event_cooldown(); // 启动50秒冷却
|
||
|
||
|
||
/*急刹车反馈入口*/
|
||
lottie_id = 29;
|
||
ESP_LOGE(MYTAG,"急刹车!");
|
||
/*急刹车反馈结束*/
|
||
}
|
||
}
|
||
|
||
|
||
// ========== 4. 通用逻辑(堵车/颠簸检测) ==========
|
||
vTaskDelay(pdMS_TO_TICKS(20));
|
||
i++;
|
||
|
||
if(i%25==0 && current_page_idx == 0)
|
||
{
|
||
//堵车数据检测
|
||
start_stop_state();
|
||
// printf("accx:%4.2f, accy:%4.2f, accz:%4.2f, gyrx:%4.2f, gyry:%4.2f, gyrz:%4.2f\n",
|
||
// ax,ay,az,gx,gy,gz);
|
||
}
|
||
|
||
if(i%200==0 && current_page_idx == 0) //2秒累加检测 反馈程序入口
|
||
{
|
||
if(is_bump()) //判断路面颠簸
|
||
{
|
||
ESP_LOGE(MYTAG,"路面颠簸!");
|
||
}
|
||
}
|
||
|
||
if(i>=15000 && current_page_idx == 0) //5分钟判定一次堵车
|
||
{
|
||
if(is_jam())
|
||
{
|
||
ESP_LOGE(MYTAG,"堵车!");
|
||
}
|
||
i = 0;
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(30));
|
||
}
|
||
}
|
||
|
||
|
||
i2c_master_bus_handle_t get_i2c_master_bus_handle(void)
|
||
{
|
||
return s_i2c_bus;
|
||
}
|
||
i2c_master_bus_handle_t touch_bus_handle_BMI;
|
||
i2c_master_bus_handle_t touch_bus_handle_NST112;
|
||
i2c_master_bus_handle_t touch_bus_handle_MS523;
|
||
i2c_master_bus_handle_t n_i2c_bus;
|
||
//i2c_master_bus_config_t i2c_bus_config = {0};
|
||
esp_err_t BMI_init(void)
|
||
{
|
||
ESP_LOGI(TAG_IMU, "开始初始化 SC7U22...");
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
|
||
#if !EXAMPLE_USE_TOUCH
|
||
#error "BMI_init requires EXAMPLE_USE_TOUCH (SC7U22 shares touch I2C bus)."
|
||
#endif
|
||
|
||
//extern i2c_master_bus_handle_t touch_bus_handle; I2C主总线
|
||
i2c_master_bus_config_t i2c_bus_config = {0};
|
||
i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
||
i2c_bus_config.i2c_port = 0;
|
||
i2c_bus_config.scl_io_num = TEST_BOARD_I2C_SCL_PIN;
|
||
i2c_bus_config.sda_io_num = TEST_BOARD_I2C_SDA_PIN;
|
||
i2c_bus_config.glitch_ignore_cnt = 7;
|
||
i2c_bus_config.flags.enable_internal_pullup = true;
|
||
//i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_config, &touch_bus_handle_BMI));
|
||
s_i2c_bus = touch_bus_handle_BMI;
|
||
i2c_scan_devices();
|
||
SC7U22_init(touch_bus_handle_BMI);
|
||
bmi_task_stack = heap_caps_malloc(BMI_TASK_STACK_SIZE, MALLOC_CAP_SPIRAM);
|
||
if (bmi_task_stack == NULL)
|
||
{
|
||
ESP_LOGE("LVGL", "Failed to allocate BMI task stack");
|
||
return ESP_FAIL;
|
||
}
|
||
|
||
TaskHandle_t task_handle = xTaskCreateStaticPinnedToCore(imu_task, "BMI", BMI_TASK_STACK_SIZE, NULL, BMI_TASK_PRIORITY, bmi_task_stack, &bmi_task_tcb, 0);
|
||
if (task_handle == NULL)
|
||
{
|
||
ESP_LOGE("LVGL", "Failed to create BMI task");
|
||
heap_caps_free(bmi_task_stack);
|
||
bmi_task_stack = NULL;
|
||
return ESP_FAIL;
|
||
}
|
||
ESP_LOGI("LVGL", "BMI task created successfully");
|
||
return ESP_OK;
|
||
}
|
||
|
||
i2c_master_dev_handle_t NST112_handle;
|
||
uint8_t temp_data[2] ={0};
|
||
uint8_t config_data[2] = {0};
|
||
uint8_t H_tmp_data[2] = {0};
|
||
uint8_t L_tmp_data[2] = {0};
|
||
esp_err_t NST112_Reg_W(uint8_t reg_addr, uint8_t data)
|
||
{
|
||
uint8_t write_buf[] = {reg_addr, data};
|
||
return i2c_master_transmit(NST112_handle, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
|
||
}
|
||
|
||
esp_err_t NST112_Reg_R(uint8_t reg_addr)
|
||
{
|
||
esp_err_t ret = i2c_master_transmit_receive(NST112_handle,®_addr,1,temp_data,2,1000 / portTICK_PERIOD_MS);
|
||
if(ret != ESP_OK){
|
||
ESP_LOGE("NST112", "读取 NST112 寄存器失败,错误码: %s", esp_err_to_name(ret));
|
||
return ret;
|
||
}
|
||
else{
|
||
return ESP_OK;
|
||
}
|
||
}
|
||
|
||
extern StaticTask_t nst112_task_tcb;
|
||
extern StackType_t *nst112_task_stack;
|
||
void nst112_task(void *param)
|
||
{
|
||
ESP_LOGI("NST112", "Starting NST112 task");
|
||
uint8_t read_addr = 0x00;
|
||
float temp_c = 0;
|
||
while(1)
|
||
{
|
||
i2c_master_transmit_receive(NST112_handle, &read_addr, 1, temp_data, 2, 1000 / portTICK_PERIOD_MS);
|
||
int16_t raw = (int16_t)((temp_data[0] << 8) | temp_data[1]);
|
||
temp_c = raw / 256.0f;
|
||
ESP_LOGI("NST112", "TEMP: %02X %02X", temp_data[0], temp_data[1]);
|
||
ESP_LOGI("NST112", "TEMP (C): %f", temp_c);
|
||
if(temp_c > 70){
|
||
ESP_LOGI("NST112", "TEMP: WARNING");
|
||
}
|
||
if(temp_c < (-20)){
|
||
ESP_LOGI("NST112", "TEMP: WARNING");
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||
}
|
||
}
|
||
|
||
esp_err_t NST112_init(void)
|
||
{
|
||
|
||
//extern i2c_master_bus_handle_t touch_bus_handle_touch;
|
||
|
||
i2c_master_bus_config_t i2c_bus_config = {0};
|
||
i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
||
i2c_bus_config.i2c_port = 1;
|
||
i2c_bus_config.scl_io_num = NST112_I2C_SCL_PIN;
|
||
i2c_bus_config.sda_io_num = NST112_I2C_SDA_PIN;
|
||
i2c_bus_config.glitch_ignore_cnt = 7;
|
||
i2c_bus_config.flags.enable_internal_pullup = true;
|
||
//i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_config, &touch_bus_handle_NST112)); //此处单传感器调试用,正式使用时改为传入共享总线句柄
|
||
|
||
n_i2c_bus = touch_bus_handle_NST112;//touch_bus_handle_touch
|
||
s_i2c_bus = touch_bus_handle_NST112;
|
||
i2c_scan_devices();
|
||
ESP_LOGI("NST112", "NST112 on shared I2C bus: %p", (void *)n_i2c_bus);
|
||
|
||
i2c_device_config_t dev_cfg = {
|
||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||
.device_address = NST112_I2C_ADDR,
|
||
.scl_speed_hz = I2C_MASTER_FREQ_HZ,
|
||
};
|
||
esp_err_t ret = i2c_master_bus_add_device(n_i2c_bus, &dev_cfg, &NST112_handle);
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE("NST112", "添加 NST112 设备失败");
|
||
return ret;
|
||
}
|
||
uint8_t write_buf[] = {0x01,0x60,0xA0}; // 0x01 寄存器地址,0x60 和 0xA0 是要写入的数据
|
||
uint8_t high_temp_config_reg[] = {0x03,0x05,0x50};
|
||
uint8_t low_temp_config_reg[] = {0x02,0x00,0xA0};
|
||
uint8_t read_config_addr = 0x01;
|
||
uint8_t read_H_TMP_addr = 0x02;
|
||
uint8_t read_L_TMP_addr = 0x03;
|
||
i2c_master_transmit(NST112_handle, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
|
||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||
ret = i2c_master_transmit_receive(NST112_handle,&read_config_addr,1 ,config_data,2,pdMS_TO_TICKS(100));
|
||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||
ret = i2c_master_transmit_receive(NST112_handle,&read_L_TMP_addr,1 ,H_tmp_data,2,pdMS_TO_TICKS(100));
|
||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||
ret = i2c_master_transmit_receive(NST112_handle,&read_H_TMP_addr,1 ,L_tmp_data,2,pdMS_TO_TICKS(100));
|
||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||
ESP_LOGI("NST112", "TEMP: %02X %02X L:%02X %02X H:%02X %02X ", config_data[0], config_data[1],L_tmp_data[0],L_tmp_data[1],H_tmp_data[0],L_tmp_data[1]);
|
||
|
||
//i2c_master_transmit(NST112_handle, high_temp_config_reg, sizeof(high_temp_config_reg), 1000 / portTICK_PERIOD_MS);
|
||
//i2c_master_transmit(NST112_handle, low_temp_config_reg, sizeof(low_temp_config_reg), 1000 / portTICK_PERIOD_MS);
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
uint8_t read_addr = 0x00;
|
||
//i2c_master_transmit_receive(NST112_handle, &read_addr, 1, temp_data, 2, 1000 / portTICK_PERIOD_MS);
|
||
nst112_task_stack = heap_caps_malloc(NST112_TASK_STACK_SIZE, MALLOC_CAP_SPIRAM);
|
||
if (nst112_task_stack == NULL)
|
||
{
|
||
ESP_LOGE("LVGL", "Failed to allocate NST112 task stack");
|
||
return ESP_FAIL;
|
||
}
|
||
xTaskCreateStaticPinnedToCore(nst112_task, "NST112", NST112_TASK_STACK_SIZE, NULL, NST112_TASK_PRIORITY, nst112_task_stack, &nst112_task_tcb,0);
|
||
return ESP_OK;
|
||
}
|
||
|
||
#define MS523_CTRL_GPIO GPIO_NUM_48
|
||
i2c_master_dev_handle_t MS523_handle;
|
||
extern StaticTask_t MS523_task_tcb;
|
||
extern StackType_t *MS523_task_stack;
|
||
esp_err_t ms523_write_byte(uint8_t reg_addr, uint8_t data) {
|
||
uint8_t write_buf[2] = {reg_addr, data};
|
||
esp_err_t ret = i2c_master_transmit(MS523_handle, write_buf, sizeof(write_buf), pdMS_TO_TICKS(100));
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE("MS523", "Write reg 0x%02X failed: %s", reg_addr, esp_err_to_name(ret));
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
esp_err_t ms523_read_byte(uint8_t reg_addr, uint8_t *data) {
|
||
esp_err_t ret = i2c_master_transmit_receive(MS523_handle, ®_addr, 1, data, 1, pdMS_TO_TICKS(100));
|
||
if (ret == ESP_OK) {
|
||
ESP_LOGI("MS523", "Read reg 0x%02X = 0x%02X", reg_addr, *data);
|
||
} else {
|
||
ESP_LOGE("MS523", "Read reg 0x%02X failed: %s", reg_addr, esp_err_to_name(ret));
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
|
||
|
||
|
||
#define GPIO_OUTPUT_PIN GPIO_NUM_48 // 以GPIO2为例
|
||
void MS523_ctrl_init(void){
|
||
|
||
gpio_config_t io_conf = {
|
||
.pin_bit_mask = (1ULL << GPIO_OUTPUT_PIN), // 选择要配置的引脚[reference:5]
|
||
.mode = GPIO_MODE_OUTPUT, // 设置为输出模式[reference:6]
|
||
.pull_up_en = GPIO_PULLUP_DISABLE, // 禁用内部上拉电阻[reference:7]
|
||
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用内部下拉电阻[reference:8]
|
||
.intr_type = GPIO_INTR_DISABLE // 禁用中断[reference:9]
|
||
};
|
||
|
||
gpio_config(&io_conf); // 应用配置[reference:10]
|
||
|
||
|
||
// 2. 设置GPIO输出高电平
|
||
gpio_set_level(GPIO_OUTPUT_PIN, 1);
|
||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||
ESP_LOGI("MS523", "当前输出高电平");
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
void ms523_detect_card(void) {
|
||
// 1-4:发送寻卡指令
|
||
ms523_write_byte(0x0A, 0x80);
|
||
ms523_write_byte(0x09, 0x52);
|
||
ms523_write_byte(0x0D, 0x87);
|
||
ms523_write_byte(0x01, 0x0C);
|
||
|
||
// 5:轮询等待 RxIRq(超时 50ms)
|
||
uint8_t irq = 0;
|
||
int timeout_ms = 50;
|
||
TickType_t start = xTaskGetTickCount();
|
||
while ((xTaskGetTickCount() - start) < pdMS_TO_TICKS(timeout_ms)) {
|
||
ms523_read_byte(0x04, &irq);
|
||
if (irq & 0x20) {
|
||
ESP_LOGI("MS523", "✅ Card detected!");
|
||
return; // 检测到卡,退出函数
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(1));
|
||
}
|
||
|
||
// 检查碰撞
|
||
uint8_t err;
|
||
ms523_read_byte(0x06, &err);
|
||
if (err & 0x08) {
|
||
ESP_LOGI("MS523", "✅ Card detected (Collision)!");
|
||
return;
|
||
}
|
||
|
||
// 无卡
|
||
ESP_LOGW("MS523", "No card detected.");
|
||
}
|
||
|
||
void MS523_task(void *param){
|
||
while (1)
|
||
{
|
||
ms523_detect_card();
|
||
ESP_LOGW("MS523", "运行中");
|
||
vTaskDelay(500);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
esp_err_t MS523_init(void)
|
||
{
|
||
MS523_ctrl_init();
|
||
ESP_LOGI(TAG_IMU, "开始初始化 MS523");
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
|
||
#if !EXAMPLE_USE_TOUCH
|
||
#error "BMI_init requires EXAMPLE_USE_TOUCH (SC7U22 shares touch I2C bus)."
|
||
#endif
|
||
|
||
//extern i2c_master_bus_handle_t touch_bus_handle; I2C主总线
|
||
i2c_master_bus_config_t i2c_bus_config = {0};
|
||
i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
||
i2c_bus_config.i2c_port = 0;
|
||
i2c_bus_config.scl_io_num = TEST_BOARD_I2C_SCL_PIN;
|
||
i2c_bus_config.sda_io_num = TEST_BOARD_I2C_SDA_PIN;
|
||
i2c_bus_config.glitch_ignore_cnt = 7;
|
||
i2c_bus_config.flags.enable_internal_pullup = true;
|
||
//i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
|
||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_config, &touch_bus_handle_MS523));
|
||
s_i2c_bus = touch_bus_handle_MS523;
|
||
//i2c_scan_devices();
|
||
|
||
i2c_device_config_t dev_cfg = {
|
||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||
.device_address = MS523_I2C_ADDR,
|
||
.scl_speed_hz = I2C_MASTER_FREQ_HZ,
|
||
};
|
||
esp_err_t ret = i2c_master_bus_add_device(s_i2c_bus, &dev_cfg, &MS523_handle);
|
||
uint8_t reg_addr = 0x37;
|
||
uint8_t version = 0;
|
||
uint8_t data = 0;
|
||
ms523_read_byte(reg_addr,&version);
|
||
ms523_write_byte(0x14, 0x83); // 开启 TX1/TX2 天线驱动
|
||
|
||
ms523_write_byte(0x12, 0x00); // 发送速率 106k
|
||
ms523_write_byte(0x13, 0x08); // 接收速率 106k
|
||
ms523_write_byte(0x26, 0x48); // 接收增益 48dB
|
||
if (ret == ESP_OK) {
|
||
ESP_LOGI("MS523", "NFC version:0x%02X", version);
|
||
} else {
|
||
ESP_LOGE("MS523", "Read version failed: %s", esp_err_to_name(ret));
|
||
}
|
||
|
||
|
||
MS523_task_stack = heap_caps_malloc(MS523_TASK_STACK_SIZE, MALLOC_CAP_SPIRAM);
|
||
if (MS523_task_stack == NULL)
|
||
{
|
||
ESP_LOGE("LVGL", "Failed to allocate NST112 task stack");
|
||
return ESP_FAIL;
|
||
}
|
||
|
||
|
||
//ms523_write_byte(0x0A, 0x80);
|
||
//ms523_read_byte(0x0A,&data);
|
||
//ms523_write_byte(0x09, 0x52);
|
||
//ms523_read_byte(0x09,&data);
|
||
//ms523_write_byte(0x0D, 0x87);
|
||
//ms523_read_byte(0x0D,&data);
|
||
//ms523_write_byte(0x01, 0x0C);
|
||
//ms523_read_byte(0x01,&data);
|
||
xTaskCreateStaticPinnedToCore(MS523_task, "MS523", MS523_TASK_STACK_SIZE, NULL, MS523_TASK_PRIORITY, MS523_task_stack, &MS523_task_tcb,1);
|
||
return ESP_OK;
|
||
} |