#include "gyroscope.h" #include "freertos/idf_additions.h" Gyroscope::Gyroscope() { bus_handle_ = nullptr; on_shake_callback_ = nullptr; // 初始化回调函数 on_pickup_callback_ = nullptr; // 初始化拿起回调函数 on_putdown_callback_ = nullptr; // 初始化放置回调函数 on_tilt_callback_ = nullptr; // 初始化倾斜回调函数 timer_handle_ = nullptr; // 初始化定时器句柄 pGyroscopeTaskStackBuffer_ = nullptr; pxGyroscopeTaskBuffer_ = nullptr; // 初始化滤波相关变量 last_filtered_x_ = 0; last_filtered_y_ = 0; last_filtered_z_ = 0; is_first_sample_ = true; } Gyroscope::~Gyroscope() { // 删除定时器 DeleteTimer(); if (pGyroscopeTaskStackBuffer_) { free(pGyroscopeTaskStackBuffer_); pGyroscopeTaskStackBuffer_ = nullptr; } if (pxGyroscopeTaskBuffer_) { vPortFree(pxGyroscopeTaskBuffer_); pxGyroscopeTaskBuffer_ = nullptr; } } /** * @brief 创建500ms周期定时器 * @return esp_err_t 创建结果 */ esp_err_t Gyroscope::CreateTimer() { if (timer_handle_ != nullptr) { ESP_LOGW(TAG_GYROSCOPE, "定时器已存在"); return ESP_OK; } // 配置定时器参数 esp_timer_create_args_t timer_args = {}; timer_args.callback = &TimerCallback; // 静态回调函数 timer_args.arg = this; // 传入实例指针 timer_args.name = "gyro_timer"; // 定时器名称 timer_args.dispatch_method = ESP_TIMER_TASK; // 使用任务调度 timer_args.skip_unhandled_events = false; // 不跳过未处理事件 // 创建定时器 esp_err_t err = esp_timer_create(&timer_args, &timer_handle_); if (err != ESP_OK) { ESP_LOGE(TAG_GYROSCOPE, "创建定时器失败: 0x%x", err); return err; } // 启动定时器,500ms周期重复 err = esp_timer_start_periodic(timer_handle_, 50000); // 50000微秒 = 50ms 20hz if (err != ESP_OK) { ESP_LOGE(TAG_GYROSCOPE, "启动定时器失败: 0x%x", err); esp_timer_delete(timer_handle_); timer_handle_ = nullptr; return err; } ESP_LOGI(TAG_GYROSCOPE, "50ms周期定时器创建并启动成功"); return ESP_OK; } /** * @brief 停止并删除定时器 */ void Gyroscope::DeleteTimer() { if (timer_handle_ == nullptr) { return; } // 停止定时器 esp_err_t err = esp_timer_stop(timer_handle_); if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { ESP_LOGE(TAG_GYROSCOPE, "停止定时器失败: 0x%x", err); } // 删除定时器 err = esp_timer_delete(timer_handle_); if (err != ESP_OK) { ESP_LOGE(TAG_GYROSCOPE, "删除定时器失败: 0x%x", err); } timer_handle_ = nullptr; ESP_LOGI(TAG_GYROSCOPE, "定时器已删除"); } /** * @brief 静态定时器回调函数 * @param arg 传入的Gyroscope实例指针 */ void Gyroscope::TimerCallback(void* arg) { Gyroscope* gyroscope = static_cast(arg); if (gyroscope) { gyroscope->TimerInterruptHandler(); } } /** * @brief 定时器中断处理函数 * 每50ms调用一次,可以在这里执行周期性任务 */ void Gyroscope::TimerInterruptHandler() { // 在这里添加定时器中断处理逻辑 // 例如:读取传感器数据、检测动作、发送状态等 AccelerationData accel_data_ = {0}; esp_err_t err = GetAcceleration(&accel_data_); if (err == ESP_OK) { // 根据实际安装方式重新定义姿态判断 // 平放状态:Y轴接近±1g(因为Y轴垂直于桌面),X和Z轴接近0 bool is_flat = (fabs(accel_data_.y) > 0.85 && fabs(accel_data_.y) < 1.15) && (fabs(accel_data_.x) < 0.15 && fabs(accel_data_.z) < 0.15); // 手持状态:Y轴接近0,X和Z轴中至少有一个有明显值 bool is_held = (fabs(accel_data_.y) < 0.7) && (fabs(accel_data_.x) > 0.15 || fabs(accel_data_.z) > 0.15); // 计算合成加速度(总加速度大小) float total_accel = sqrt(accel_data_.x * accel_data_.x + accel_data_.y * accel_data_.y + accel_data_.z * accel_data_.z); // 计算与上次合成加速度的差值(加速度变化量) float total_accel_diff = fabs(total_accel - total_accel_last_); // 计算Y轴加速度变化 float y_diff = accel_data_.y - accel_data_y_last_; // 检测是否处于平放稳定状态 if(is_flat) { stable_count_++; if(stable_count_ >= 8) { // 连续8次检测到平放状态(400ms),减少误判 stable_count_ = 8; // 防止溢出 if(!is_stable_) { ESP_LOGI(TAG_GYROSCOPE, "定时器: 检测到稳定平放状态 (x:%.2f, y:%.2f, z:%.2f, total:%.2f)", accel_data_.x, accel_data_.y, accel_data_.z, total_accel); is_stable_ = true; is_lifted_ = false; // 重置拿起状态 putdown_count_ = 0; // 重置放置计数器 lift_count_ = 0; // 重置拿起计数器 // 调用放置回调 if (on_putdown_callback_) { on_putdown_callback_(); } } } } else { stable_count_ = 0; // 不在范围内则重置稳定计数器 // 如果之前是稳定状态,检测是否有拿起动作 if(is_stable_) { // 检测拿起动作的多种特征: // 1. Y轴从接近1g变为接近0g(位置变化) // 2. 合成加速度有显著变化(表示有加速度) bool y_change_indicates_lift = fabs(y_diff) > 0.25; bool total_accel_change_indicates_lift = total_accel_diff > 0.3; // 检测加速度突变 if(y_change_indicates_lift || total_accel_change_indicates_lift) { lift_count_++; if(lift_count_ >= 1) { // 降低拿起检测阈值,提高灵敏度 ESP_LOGI(TAG_GYROSCOPE, "定时器: 检测到拿起动作 (x:%.2f, y:%.2f, z:%.2f, total:%.2f, y_diff:%.2f, accel_diff:%.2f)", accel_data_.x, accel_data_.y, accel_data_.z, total_accel, y_diff, total_accel_diff); is_stable_ = false; // 重置状态 is_lifted_ = true; // 设置拿起状态 lift_count_ = 0; stable_count_ = 0; // 调用拿起回调 if (on_pickup_callback_) { on_pickup_callback_(); } } } else { lift_count_ = 0; // 变化不符合预期,重置拿起计数器 } } // 如果之前是被拿起状态,检测是否有放置动作 else if(is_lifted_) { // 检测从手持到平放的变化 - Y轴从接近0g变为接近1g bool y_change_indicates_putdown = (y_diff > 0.25 && y_diff < 0.80); if(y_change_indicates_putdown) { putdown_count_++; if(putdown_count_ >= 3) { // 连续3次检测到缓慢变化 ESP_LOGI(TAG_GYROSCOPE, "定时器: 检测到缓慢放置动作 (x:%.2f, y:%.2f, z:%.2f, total:%.2f, y_diff:%.2f)", accel_data_.x, accel_data_.y, accel_data_.z, total_accel, y_diff); is_lifted_ = false; // 重置拿起状态 putdown_count_ = 0; // 调用放置回调 if (on_putdown_callback_) { on_putdown_callback_(); } } } else { putdown_count_ = 0; // 变化不符合预期,重置放置计数器 } } // 检测倾斜动作 - 仅当手持时才触发倾斜 if (is_held && (fabs(accel_data_.x) > 0.3f || fabs(accel_data_.z) > 0.3f)) { // 检测到倾斜,调用倾斜回调 if (on_tilt_callback_) { on_tilt_callback_(); } } } // 保存当前值作为下一次的上一次值 accel_data_y_last_ = accel_data_.y; total_accel_last_ = total_accel; } } /** * @brief 注册摇晃检测回调函数 * * @param callback 当检测到摇晃时调用的回调函数 */ void Gyroscope::OnShake(std::function callback) { on_shake_callback_ = callback; } /** * @brief 注册拿起检测回调函数 * * @param callback 当检测到设备被拿起时调用的回调函数 */ void Gyroscope::OnPickUp(std::function callback) { on_pickup_callback_ = callback; } /** * @brief 注册放置检测回调函数 * * @param callback 当检测到设备被放置时调用的回调函数 */ void Gyroscope::OnPutDown(std::function callback) { on_putdown_callback_ = callback; } /** * @brief 注册倾斜检测回调函数 * * @param callback 当检测到设备倾斜时调用的回调函数 */ void Gyroscope::OnTilt(std::function callback) { on_tilt_callback_ = callback; } /** * @brief 静态中断处理函数 * * @param arg 传入的Gyroscope实例指针 */ void IRAM_ATTR Gyroscope::GpioIsrHandler(void* arg) { Gyroscope* gyroscope = static_cast(arg); if (gyroscope) { gyroscope->interrupt_flag_ = true; } } /** * @brief 向陀螺仪寄存器写入数据 * * @param reg 寄存器地址 * @param data 要写入的数据 * @return esp_err_t ESP_OK表示成功 */ esp_err_t Gyroscope::WriteRegister(uint8_t reg, uint8_t data) { if (bus_handle_ == nullptr) { ESP_LOGE(TAG_GYROSCOPE, "I2C总线未初始化"); return ESP_FAIL; } return i2c_bus_write_bytes(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1, ®, 1, &data, 1); } /** * @brief 从陀螺仪寄存器读取数据 * * @param reg 寄存器地址 * @param data 读取到的数据指针 * @return esp_err_t ESP_OK表示成功 */ esp_err_t Gyroscope::ReadRegister(uint8_t reg, uint8_t* data) { if (bus_handle_ == nullptr || data == nullptr) { ESP_LOGE(TAG_GYROSCOPE, "参数错误或I2C总线未初始化"); return ESP_FAIL; } return i2c_bus_read_bytes(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1, ®, 1, data, 1); } /** * @brief 批量读取陀螺仪寄存器数据 * * @param reg 起始寄存器地址 * @param data 读取到的数据指针 * @param length 要读取的字节数 * @return esp_err_t ESP_OK表示成功 */ esp_err_t Gyroscope::ReadRegisters(uint8_t reg, uint8_t* data, size_t length) { if (bus_handle_ == nullptr || data == nullptr) { ESP_LOGE(TAG_GYROSCOPE, "参数错误或I2C总线未初始化"); return ESP_FAIL; } return i2c_bus_read_bytes(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1, ®, 1, data, length); } /** * @brief 初始化陀螺仪中断GPIO * * @param gpio_num 要初始化的GPIO引脚号 * @return esp_err_t ESP_OK表示成功 */ esp_err_t Gyroscope::InterruptGpioInit(gpio_num_t gpio_num) { gpio_config_t io_conf = {}; io_conf.intr_type = GPIO_INTR_POSEDGE; // 上升沿触发 io_conf.pin_bit_mask = (1ULL<x = raw_x * ACCEL_SENSITIVITY_2G / 1000.0f; data->y = raw_y * ACCEL_SENSITIVITY_2G / 1000.0f; data->z = raw_z * ACCEL_SENSITIVITY_2G / 1000.0f; return ESP_OK; } /** * @brief 陀螺仪读取任务 * Y轴立起 Z轴躺下 * 该任务会周期性读取陀螺仪数据并处理中断 */ void IRAM_ATTR Gyroscope::GyroReadTask(void* pvParameters) { Gyroscope* gyroscope = static_cast(pvParameters); uint32_t interrupt_count = 0; while (1) { if (gyroscope->interrupt_flag_) { interrupt_count++; gyroscope->ClearInterruptFlags(); gyroscope->interrupt_flag_ = false; // 调用用户注册的回调函数 if (gyroscope->on_shake_callback_) { gyroscope->on_shake_callback_(); } ESP_LOGI(TAG_GYROSCOPE, "累计中断次数: %lu", interrupt_count); if(interrupt_count >= 2){ interrupt_count = 0; ESP_LOGI(TAG_GYROSCOPE, "检测到摇晃>>>>>>>>>>"); } } vTaskDelay(pdMS_TO_TICKS(50)); // 50ms检查一次,更快的响应 } }