Add initial board, HAL, and executor modules
This commit is contained in:
1 parent
3b9e129c88
commit
0b2eebe76d
82 files changed
+20611
No files matched your search
@@ -0,0 +1,167 @@
|
||||
#include "adc_battery_monitor.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "AdcBatteryMonitor" ///< 日志标签,用于ESP-IDF日志系统
|
||||
|
||||
AdcBatteryMonitor::AdcBatteryMonitor(BoardSweekar& board, adc_unit_t adc_unit, adc_channel_t adc_channel, float upper_resistor, float lower_resistor)
|
||||
: board_(board) {
|
||||
|
||||
// Initialize ADC battery estimation
|
||||
adc_battery_estimation_t adc_cfg = {
|
||||
.internal = {
|
||||
.adc_unit = adc_unit,
|
||||
.adc_bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
.adc_atten = ADC_ATTEN_DB_12,
|
||||
},
|
||||
.adc_channel = adc_channel,
|
||||
.upper_resistor = upper_resistor,
|
||||
.lower_resistor = lower_resistor
|
||||
};
|
||||
adc_cfg.charging_detect_cb = [](void *user_data) -> bool {
|
||||
AdcBatteryMonitor *self = (AdcBatteryMonitor *)user_data;
|
||||
return self->IsCharging();
|
||||
};
|
||||
adc_cfg.charging_detect_user_data = this;
|
||||
adc_battery_estimation_handle_ = adc_battery_estimation_create(&adc_cfg);
|
||||
|
||||
// Initialize timer
|
||||
esp_timer_create_args_t timer_cfg = {
|
||||
.callback = [](void *arg) {
|
||||
AdcBatteryMonitor *self = (AdcBatteryMonitor *)arg;
|
||||
self->CheckBatteryStatus();
|
||||
},
|
||||
.arg = this,
|
||||
.name = "adc_battery_monitor",
|
||||
.skip_unhandled_events = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&timer_cfg, &timer_handle_));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(timer_handle_, 1000000));
|
||||
}
|
||||
|
||||
// 注册ADC电池回调函数
|
||||
void AdcBatteryMonitor::AdcBatteryMonitor_Init(void) {
|
||||
ESP_LOGI(TAG, "注册ADC电池回调函数");
|
||||
// 设置默认的电池状态回调函数
|
||||
OnNotCharging([]() {
|
||||
ESP_LOGI(TAG, "电池未充电状态回调 - 默认处理");
|
||||
});
|
||||
|
||||
OnFull([]() {
|
||||
ESP_LOGI(TAG, "电池充满电状态回调 - 默认处理");
|
||||
});
|
||||
|
||||
OnCharging([]() {
|
||||
ESP_LOGI(TAG, "电池充电中状态回调 - 默认处理");
|
||||
});
|
||||
}
|
||||
|
||||
AdcBatteryMonitor::~AdcBatteryMonitor() {
|
||||
if (adc_battery_estimation_handle_) {
|
||||
ESP_ERROR_CHECK(adc_battery_estimation_destroy(adc_battery_estimation_handle_));
|
||||
}
|
||||
|
||||
if (timer_handle_) {
|
||||
esp_timer_stop(timer_handle_);
|
||||
esp_timer_delete(timer_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool AdcBatteryMonitor::IsCharging() {
|
||||
// 通过IO扩展板读取充电检测引脚状态
|
||||
// BATTERY_CHRG_PIN连接到P0_5,对应INPUT_PORT0
|
||||
uint8_t port0_data = 0;
|
||||
esp_err_t err = board_.io_expansion_.ReadInputPort0(&port0_data);
|
||||
|
||||
// 如果读取失败,返回false
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查BATTERY_CHRG_PIN位状态
|
||||
// 当引脚为低电平时表示正在充电
|
||||
return (port0_data & (1 << BATTERY_CHRG_PIN)) == 0;
|
||||
}
|
||||
|
||||
bool AdcBatteryMonitor::IsDischarging() {
|
||||
return !IsCharging();
|
||||
}
|
||||
|
||||
uint8_t AdcBatteryMonitor::GetBatteryLevel() {
|
||||
// 如果句柄无效,返回默认值
|
||||
if (adc_battery_estimation_handle_ == nullptr) {
|
||||
printf("adc_battery_estimation_handle_ 为空!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
float capacity = 0;
|
||||
esp_err_t err = adc_battery_estimation_get_capacity(adc_battery_estimation_handle_, &capacity);
|
||||
if (err != ESP_OK) {
|
||||
printf("返回值出错!\n");
|
||||
return 0; // 出错时返回默认值
|
||||
}
|
||||
return (uint8_t)capacity;
|
||||
}
|
||||
|
||||
void AdcBatteryMonitor::OnChargingStatusChanged(std::function<void(bool)> callback) {
|
||||
on_charging_status_changed_ = callback;
|
||||
}
|
||||
|
||||
void AdcBatteryMonitor::CheckBatteryStatus() {
|
||||
bool new_charging_status = IsCharging();
|
||||
if (new_charging_status != is_charging_) {
|
||||
is_charging_ = new_charging_status;
|
||||
if (on_charging_status_changed_) {
|
||||
on_charging_status_changed_(is_charging_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 三个电池状态回调函数的实现
|
||||
void AdcBatteryMonitor::TriggerNotChrgCallback() {
|
||||
if (on_not_charging_callback_) {
|
||||
on_not_charging_callback_();
|
||||
}
|
||||
}
|
||||
|
||||
void AdcBatteryMonitor::TriggerFullCallback() {
|
||||
if (on_full_callback_) {
|
||||
on_full_callback_();
|
||||
}
|
||||
}
|
||||
|
||||
void AdcBatteryMonitor::TriggerChrgCallback() {
|
||||
if (on_charging_callback_) {
|
||||
on_charging_callback_();
|
||||
}
|
||||
}
|
||||
|
||||
// 注册电池状态回调函数的实现
|
||||
void AdcBatteryMonitor::OnNotCharging(std::function<void()> callback) {
|
||||
on_not_charging_callback_ = callback;
|
||||
}
|
||||
|
||||
void AdcBatteryMonitor::OnFull(std::function<void()> callback) {
|
||||
on_full_callback_ = callback;
|
||||
}
|
||||
|
||||
void AdcBatteryMonitor::OnCharging(std::function<void()> callback) {
|
||||
on_charging_callback_ = callback;
|
||||
}
|
||||
|
||||
//CHRG引脚充电前为高电平 充电中为低电平 充满电为高电平
|
||||
//FULL未充满为高电平 充满电为低电平
|
||||
bool AdcBatteryMonitor::IsFull() {
|
||||
// 通过IO扩展板读取电池充满检测引脚状态
|
||||
// BATTERY_FULL_PIN连接到P1_3,对应INPUT_PORT1
|
||||
uint8_t port1_data = 0;
|
||||
esp_err_t err = board_.io_expansion_.ReadInputPort1(&port1_data);
|
||||
|
||||
// 如果读取失败,返回false
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查BATTERY_FULL_PIN位状态
|
||||
// 当引脚为低电平时表示电池已充满
|
||||
return (port1_data & (1 << BATTERY_FULL_PIN)) == 0;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef ADC_BATTERY_MONITOR_H
|
||||
#define ADC_BATTERY_MONITOR_H
|
||||
|
||||
#include <functional>
|
||||
#include <driver/gpio.h>
|
||||
#include <adc_battery_estimation.h>
|
||||
#include <esp_timer.h>
|
||||
#include "02_physical/boards/board_sweekar.h"
|
||||
|
||||
class AdcBatteryMonitor {
|
||||
public:
|
||||
AdcBatteryMonitor(BoardSweekar& board, adc_unit_t adc_unit, adc_channel_t adc_channel, float upper_resistor, float lower_resistor);
|
||||
~AdcBatteryMonitor();
|
||||
|
||||
bool IsCharging();
|
||||
bool IsDischarging();
|
||||
bool IsFull(); // 电池充满检测函数
|
||||
uint8_t GetBatteryLevel();
|
||||
|
||||
// 注册电池状态回调函数
|
||||
void AdcBatteryMonitor_Init(void);
|
||||
// 三个电池状态回调函数
|
||||
void TriggerNotChrgCallback(); // 触发未充电回调
|
||||
void TriggerFullCallback(); // 触发充满电回调
|
||||
void TriggerChrgCallback(); // 触发充电中回调
|
||||
|
||||
// 注册电池状态回调函数
|
||||
void OnNotCharging(std::function<void()> callback); // 注册未充电回调
|
||||
void OnFull(std::function<void()> callback); // 注册充满电回调
|
||||
void OnCharging(std::function<void()> callback); // 注册充电中回调
|
||||
|
||||
void OnChargingStatusChanged(std::function<void(bool)> callback);
|
||||
|
||||
private:
|
||||
BoardSweekar& board_; // 添加对BoardSweekar的引用
|
||||
adc_battery_estimation_handle_t adc_battery_estimation_handle_ = nullptr;
|
||||
esp_timer_handle_t timer_handle_ = nullptr;
|
||||
bool is_charging_ = false;
|
||||
std::function<void(bool)> on_charging_status_changed_;
|
||||
std::function<void()> on_not_charging_callback_; // 未充电回调函数
|
||||
std::function<void()> on_full_callback_; // 充满电回调函数
|
||||
std::function<void()> on_charging_callback_; // 充电中回调函数
|
||||
|
||||
void CheckBatteryStatus();
|
||||
};
|
||||
|
||||
#endif // ADC_BATTERY_MONITOR_H
|
||||
@@ -0,0 +1,16 @@
|
||||
# main/05_executors/adc_battery_monitor/module.cmake
|
||||
|
||||
|
||||
# 判断 menuconfig 中的开关
|
||||
if(NOT CONFIG_ENABLE_EXEC_ADC_BATTERY_MONITOR AND NOT EARLY_EXPANSION)
|
||||
message(STATUS " [Skip] adc_battery_monitor module disabled by menuconfig.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(APPEND MODULE_SRCS
|
||||
"05_executors/adc_battery_monitor/adc_battery_monitor.cc"
|
||||
)
|
||||
|
||||
list(APPEND MODULE_INCLUDE_DIRS
|
||||
"05_executors/adc_battery_monitor"
|
||||
)
|
||||
@@ -0,0 +1,257 @@
|
||||
#include "base_board_control.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "BaseBoardCtrl";
|
||||
|
||||
esp_err_t BaseBoardControl::Init(BaseBoardHal *hal)
|
||||
{
|
||||
if (hal == nullptr)
|
||||
{
|
||||
ESP_LOGE(TAG, "Init: hal is null");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
hal_ = hal;
|
||||
return hal_->Init();
|
||||
}
|
||||
|
||||
esp_err_t BaseBoardControl::GetAngle(float *angle)
|
||||
{
|
||||
if (hal_ == nullptr)
|
||||
{
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
return hal_->GetAngle(angle);
|
||||
}
|
||||
|
||||
esp_err_t BaseBoardControl::SendSetAngleAbs(uint16_t angle_deg,
|
||||
uint8_t speed_percent,
|
||||
uint8_t flags)
|
||||
{
|
||||
if (hal_ == nullptr)
|
||||
{
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// 角度限制在 0~359 之间
|
||||
if (angle_deg > 359)
|
||||
{
|
||||
angle_deg = static_cast<uint16_t>(angle_deg % 360);
|
||||
}
|
||||
|
||||
// 速度百分比限制在 0~100
|
||||
if (speed_percent > 100)
|
||||
{
|
||||
speed_percent = 100;
|
||||
}
|
||||
|
||||
uint8_t payload[4];
|
||||
|
||||
payload[0] = static_cast<uint8_t>(angle_deg & 0xFF); // LSB
|
||||
payload[1] = static_cast<uint8_t>((angle_deg >> 8) & 0xFF); // MSB
|
||||
payload[2] = speed_percent;
|
||||
payload[3] = flags; // 预留/扩展位
|
||||
|
||||
return SendFrame(0x01, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
esp_err_t BaseBoardControl::SendSetAngleRel(int16_t delta_deg,
|
||||
uint8_t speed_percent,
|
||||
uint8_t flags)
|
||||
{
|
||||
if (hal_ == nullptr)
|
||||
{
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (speed_percent > 100)
|
||||
{
|
||||
speed_percent = 100;
|
||||
}
|
||||
|
||||
uint8_t payload[4];
|
||||
|
||||
// int16_t 直接按补码低字节在前的顺序发
|
||||
uint16_t delta_u = static_cast<uint16_t>(delta_deg);
|
||||
|
||||
payload[0] = static_cast<uint8_t>(delta_u & 0xFF); // LSB
|
||||
payload[1] = static_cast<uint8_t>((delta_u >> 8) & 0xFF); // MSB
|
||||
payload[2] = speed_percent;
|
||||
payload[3] = flags; // 预留/扩展位
|
||||
|
||||
return SendFrame(0x02, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
esp_err_t BaseBoardControl::SendSetMotorMove(MotorMode mode, uint8_t speed_percent, uint16_t duration_ms)
|
||||
{
|
||||
if (hal_ == nullptr)
|
||||
{
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (speed_percent > 100) {
|
||||
speed_percent = 100;
|
||||
}
|
||||
|
||||
uint8_t payload[4];
|
||||
|
||||
payload[0] = mode;
|
||||
payload[1] = speed_percent;
|
||||
payload[2] = static_cast<uint8_t>(duration_ms & 0xFF);
|
||||
payload[3] = static_cast<uint8_t>((duration_ms >> 8) & 0xFF);
|
||||
|
||||
return SendFrame(0x03, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
esp_err_t BaseBoardControl::SendSetLedRgb(uint8_t mode,
|
||||
uint8_t target_mask,
|
||||
uint8_t r,
|
||||
uint8_t g,
|
||||
uint8_t b,
|
||||
uint8_t speed)
|
||||
{
|
||||
if (hal_ == nullptr)
|
||||
{
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (speed > 100)
|
||||
{
|
||||
speed = 100;
|
||||
}
|
||||
|
||||
uint8_t payload[7];
|
||||
|
||||
payload[0] = mode; // 灯效模式
|
||||
payload[1] = target_mask; // 位掩码,指示哪些 LED 生效
|
||||
payload[2] = r;
|
||||
payload[3] = g;
|
||||
payload[4] = b;
|
||||
payload[5] = speed; // 呼吸/渐变速度
|
||||
payload[6] = 0; // Reserved,写死为 0
|
||||
|
||||
return SendFrame(0x10, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
esp_err_t BaseBoardControl::SendFrame(uint8_t cmd,
|
||||
const uint8_t *payload,
|
||||
uint8_t payload_len)
|
||||
{
|
||||
if (hal_ == nullptr)
|
||||
{
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// LEN = CMD(1) + payload_len
|
||||
uint8_t len = static_cast<uint8_t>(1u + payload_len);
|
||||
|
||||
// 总长度 = HEAD1 + HEAD2 + LEN + (CMD + payload_len) + CRC = 3 + len + 1
|
||||
size_t frame_len = static_cast<size_t>(3u + len + 1u);
|
||||
if (frame_len > kMaxFrameLen)
|
||||
{
|
||||
ESP_LOGE(TAG, "SendFrame: frame too long, len=%u", static_cast<unsigned>(frame_len));
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
uint8_t frame[kMaxFrameLen];
|
||||
|
||||
// 帧头
|
||||
frame[0] = 0xAA;
|
||||
frame[1] = 0x55;
|
||||
|
||||
// LEN 字段:从 CMD 开始计数
|
||||
frame[2] = len;
|
||||
|
||||
// CMD
|
||||
frame[3] = cmd;
|
||||
|
||||
// payload
|
||||
for (uint8_t i = 0; i < payload_len; ++i)
|
||||
{
|
||||
frame[4u + i] = payload[i];
|
||||
}
|
||||
|
||||
uint8_t crc = CRC8(&frame[2], len + 1); // 从 LEN 开始算起
|
||||
|
||||
// CRC 放在数据之后
|
||||
frame[3u + len] = crc;
|
||||
|
||||
const int kMaxRetries = 5;
|
||||
const int kRetryDelayMs = 50;
|
||||
|
||||
for (int i = 0; i < kMaxRetries; i++)
|
||||
{
|
||||
esp_err_t err = hal_->Send(frame, frame_len);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
// 等待从机处理并准备好 ACK
|
||||
// 增加延时以确保从机有足够时间写入 I2C 缓冲
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
|
||||
uint8_t ack_buf[kAckFrameLen] = {0};
|
||||
err = hal_->Read(ack_buf, kAckFrameLen);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
// 校验 ACK
|
||||
if (ack_buf[0] == 0xAA && ack_buf[1] == 0x55 &&
|
||||
ack_buf[2] == 0x01 && ack_buf[3] == 0xFF)
|
||||
{
|
||||
uint8_t crc = CRC8(&ack_buf[2], 2);
|
||||
if (crc == ack_buf[4])
|
||||
{
|
||||
return ESP_OK; // 握手成功
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "ACK CRC Fail: Recv=0x%02X Calc=0x%02X", ack_buf[4], crc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "Invalid ACK: %02X %02X %02X %02X",
|
||||
ack_buf[0], ack_buf[1], ack_buf[2], ack_buf[3]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "Read ACK Failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "Send Failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
// 重试前等待
|
||||
vTaskDelay(pdMS_TO_TICKS(kRetryDelayMs));
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "SendFrame Failed after %d retries", kMaxRetries);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t BaseBoardControl::CRC8(const uint8_t *data, size_t len)
|
||||
{
|
||||
uint8_t crc = 0x00;
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
crc ^= data[i];
|
||||
|
||||
for (int b = 0; b < 8; ++b)
|
||||
{
|
||||
if (crc & 0x80)
|
||||
{
|
||||
crc = static_cast<uint8_t>((crc << 1) ^ 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
crc = static_cast<uint8_t>(crc << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// base_board_control.h
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "esp_err.h"
|
||||
#include "base_board_hal.h"
|
||||
|
||||
class BaseBoardControl
|
||||
{
|
||||
public:
|
||||
/** 帧最大长度,按你的协议冗余留一点空间 */
|
||||
static constexpr size_t kMaxFrameLen = 16;
|
||||
/** ACK 帧长度: HEAD(2) + LEN(1) + CMD(1) + CRC(1) = 5 */
|
||||
static constexpr size_t kAckFrameLen = 5;
|
||||
|
||||
BaseBoardControl() = default;
|
||||
~BaseBoardControl() = default;
|
||||
|
||||
esp_err_t Init(BaseBoardHal *hal);
|
||||
|
||||
/** 发送绝对角度命令 CMD=0x01 */
|
||||
esp_err_t SendSetAngleAbs(uint16_t angle_deg,
|
||||
uint8_t speed_percent,
|
||||
uint8_t flags = 0);
|
||||
|
||||
/** 发送相对角度命令 CMD=0x02 */
|
||||
esp_err_t SendSetAngleRel(int16_t delta_deg,
|
||||
uint8_t speed_percent,
|
||||
uint8_t flags = 0);
|
||||
|
||||
/** 电机工作模式枚举 */
|
||||
enum MotorMode
|
||||
{
|
||||
MotorOff = 0,
|
||||
MotorForward = 1,
|
||||
MotorBackward = 2
|
||||
};
|
||||
|
||||
/** 发送电机开环控制命令 CMD=0x03 */
|
||||
esp_err_t SendSetMotorMove(MotorMode mode, uint8_t speed_percent, uint16_t duration_ms);
|
||||
|
||||
/** 发送 RGB 命令 CMD=0x10 */
|
||||
esp_err_t SendSetLedRgb(uint8_t mode,
|
||||
uint8_t target_mask,
|
||||
uint8_t r,
|
||||
uint8_t g,
|
||||
uint8_t b,
|
||||
uint8_t speed);
|
||||
|
||||
/** 获取当前角度 */
|
||||
esp_err_t GetAngle(float *angle);
|
||||
|
||||
private:
|
||||
BaseBoardHal *hal_ = nullptr;
|
||||
|
||||
/** 通用打包 + 发送接口:传 CMD 和 payload,内部加头、LEN、CRC */
|
||||
esp_err_t SendFrame(uint8_t cmd,
|
||||
const uint8_t *payload,
|
||||
uint8_t payload_len);
|
||||
|
||||
/** CRC8 工具函数 */
|
||||
static uint8_t CRC8(const uint8_t *data, size_t len);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
# main/05_executors/base_board_control/module.cmake
|
||||
|
||||
|
||||
# 判断 menuconfig 中的开关
|
||||
if(NOT CONFIG_ENABLE_EXEC_BASE_BOARD_CONTROL AND NOT EARLY_EXPANSION)
|
||||
message(STATUS " [Skip] base_board_control module disabled by menuconfig.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(APPEND MODULE_SRCS
|
||||
"05_executors/base_board_control/base_board_control.cc"
|
||||
)
|
||||
|
||||
list(APPEND MODULE_INCLUDE_DIRS
|
||||
"05_executors/base_board_control"
|
||||
)
|
||||
@@ -0,0 +1,625 @@
|
||||
#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<Gyroscope*>(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<void()> callback)
|
||||
{
|
||||
on_shake_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册拿起检测回调函数
|
||||
*
|
||||
* @param callback 当检测到设备被拿起时调用的回调函数
|
||||
*/
|
||||
void Gyroscope::OnPickUp(std::function<void()> callback)
|
||||
{
|
||||
on_pickup_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册放置检测回调函数
|
||||
*
|
||||
* @param callback 当检测到设备被放置时调用的回调函数
|
||||
*/
|
||||
void Gyroscope::OnPutDown(std::function<void()> callback)
|
||||
{
|
||||
on_putdown_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册倾斜检测回调函数
|
||||
*
|
||||
* @param callback 当检测到设备倾斜时调用的回调函数
|
||||
*/
|
||||
void Gyroscope::OnTilt(std::function<void()> callback)
|
||||
{
|
||||
on_tilt_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 静态中断处理函数
|
||||
*
|
||||
* @param arg 传入的Gyroscope实例指针
|
||||
*/
|
||||
void IRAM_ATTR Gyroscope::GpioIsrHandler(void* arg)
|
||||
{
|
||||
Gyroscope* gyroscope = static_cast<Gyroscope*>(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<<gpio_num);
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // 禁用上拉,因为INT1已经是推挽输出
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; // 启用下拉
|
||||
|
||||
esp_err_t err = gpio_config(&io_conf);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "GPIO配置失败,错误码: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// 配置中断服务
|
||||
err = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3 | ESP_INTR_FLAG_IRAM);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "GPIO中断服务安装失败,错误码: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// 注册中断处理函数
|
||||
err = gpio_isr_handler_add(gpio_num, GpioIsrHandler, this);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "GPIO中断处理函数添加失败,错误码: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// 清除任何待处理的中断
|
||||
gpio_intr_disable(gpio_num);
|
||||
gpio_intr_enable(gpio_num);
|
||||
|
||||
ESP_LOGI(TAG_GYROSCOPE, "GPIO%d中断初始化完成", gpio_num);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除中断标志
|
||||
*
|
||||
* 该函数会读取唤醒源寄存器和功能源寄存器来清除中断标志
|
||||
*
|
||||
* @return esp_err_t ESP_OK表示成功
|
||||
*/
|
||||
esp_err_t Gyroscope::ClearInterruptFlags()
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t wake_up_src = 0;
|
||||
uint8_t func_src1 = 0;
|
||||
// 读取唤醒源寄存器和功能源寄存器来清除中断
|
||||
err = ReadRegister(WAKE_UP_SRC, &wake_up_src);
|
||||
if (err != ESP_OK) return err;
|
||||
err = ReadRegister(FUNC_SRC1, &func_src1);
|
||||
if (err != ESP_OK) return err;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化陀螺仪
|
||||
*
|
||||
* @param bus_handle I2C总线句柄
|
||||
* @return esp_err_t ESP_OK表示成功
|
||||
*/
|
||||
esp_err_t Gyroscope::Init(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
bus_handle_ = bus_handle;
|
||||
if (bus_handle_ == nullptr) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "I2C总线句柄为空");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG_GYROSCOPE, "开始初始化陀螺仪...");
|
||||
|
||||
// 1. 验证设备连接和通信
|
||||
uint8_t who_am_i = 0;
|
||||
esp_err_t err = ReadRegister(0x0F, &who_am_i); // WHO_AM_I寄存器
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "无法读取陀螺仪ID: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (who_am_i != 0x6A) { // LSM6DS3TR-C的WHO_AM_I值
|
||||
ESP_LOGE(TAG_GYROSCOPE, "设备ID不匹配,期望0x6A,实际0x%02X", who_am_i);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG_GYROSCOPE, "陀螺仪设备ID验证成功: 0x%02X", who_am_i);
|
||||
|
||||
// 2. 初始化中断引脚
|
||||
err = InterruptGpioInit(GYROSCOPE_INPUT_PIN_SEL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "中断引脚初始化失败: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG_GYROSCOPE, "开始配置唤醒中断...");
|
||||
|
||||
// 1. 软件复位陀螺仪
|
||||
err = WriteRegister(CTRL3_C, 0x01); // SW_RESET
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // 等待复位完成
|
||||
|
||||
// 3. 配置加速度计: 104Hz ODR, ±2g满量程
|
||||
err = WriteRegister(CTRL1_XL, 0x50); // 0b01010000 - 104Hz ODR, ±2g
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 4. 配置陀螺仪: 104Hz ODR, ±125dps满量程
|
||||
err = WriteRegister(CTRL2_G, 0x50); // 0b01010000
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 5. 配置控制寄存器3: 高电平有效,推挽输出
|
||||
err = WriteRegister(CTRL3_C, 0x44); // 0b01000100
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 6. 禁用陀螺仪低功耗模式
|
||||
err = WriteRegister(CTRL7_G, 0x00); // 禁用低功耗模式
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 7. 配置唤醒功能: 启用X轴的唤醒检测
|
||||
err = WriteRegister(TAP_CFG, 0x81); // 0b10000000 - 仅启用高通滤波器
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 8. 配置唤醒阈值: 非常灵敏的设置
|
||||
err = WriteRegister(WAKE_UP_THS, 0x3F); // 31.25mg - 最灵敏的阈值
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 9. 配置唤醒持续时间: 6个ODR周期
|
||||
err = WriteRegister(WAKE_UP_DUR, 0x06);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 10. 配置中断1路由: 将唤醒中断路由到INT1
|
||||
err = WriteRegister(MD1_CFG, 0x20); // 0b00100000 - 仅唤醒中断到INT1
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 11. 配置中断2路由: 禁用所有中断
|
||||
err = WriteRegister(MD2_CFG, 0x00);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 12. 配置INT1_CTRL: 启用唤醒中断到INT1
|
||||
err = WriteRegister(INT1_CTRL, 0x20); // 0b00100000
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 13. 启用嵌入式功能
|
||||
err = WriteRegister(CTRL10_C, 0x3C); // 0b00111000
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// 14. 等待配置生效
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
CreateTimer();
|
||||
|
||||
ESP_LOGI(TAG_GYROSCOPE, "陀螺仪唤醒中断配置完成");
|
||||
|
||||
ESP_LOGI(TAG_GYROSCOPE, "陀螺仪初始化完成");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建陀螺仪读取任务
|
||||
*
|
||||
* 该任务会周期性读取陀螺仪数据并处理中断
|
||||
*/
|
||||
void Gyroscope::CreateGyroReadTask()
|
||||
{
|
||||
if (bus_handle_ == nullptr) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "I2C总线未初始化,无法启动陀螺仪任务");
|
||||
return;
|
||||
}
|
||||
|
||||
xTaskCreateWithCaps(
|
||||
GyroReadTask,
|
||||
"gyro_read_task",
|
||||
4096,
|
||||
this,
|
||||
5, // Reduced priority to allow other tasks to run
|
||||
&xGyroscopeTaskHandle_,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT
|
||||
);
|
||||
|
||||
if (xGyroscopeTaskHandle_ == NULL) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "Failed to create static gyro read task");
|
||||
} else {
|
||||
ESP_LOGI(TAG_GYROSCOPE, "Static gyro read task created successfully");
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t Gyroscope::ReadRawAcceleration(int16_t* x, int16_t* y, int16_t* z) {
|
||||
// 1. 参数合法性检查
|
||||
if (x == nullptr || y == nullptr || z == nullptr) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "参数为空指针");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// 2. 读取原始加速度数据(6字节:X_L/X_H/Y_L/Y_H/Z_L/Z_H)
|
||||
uint8_t accel_data_[6] = {0};
|
||||
esp_err_t err = ReadRegisters(ACCEL_X_L, accel_data_, 6);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "读取加速度寄存器失败: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// 3. 组合高低字节为int16_t(二进制补码,适配传感器输出)
|
||||
int16_t current_raw_x = (int16_t)((accel_data_[1] << 8) | accel_data_[0]);
|
||||
int16_t current_raw_y = (int16_t)((accel_data_[3] << 8) | accel_data_[2]);
|
||||
int16_t current_raw_z = (int16_t)((accel_data_[5] << 8) | accel_data_[4]);
|
||||
|
||||
// 4. 一阶α滤波(整数版,无浮点运算,避免精度丢失)
|
||||
int16_t filtered_x, filtered_y, filtered_z;
|
||||
if (is_first_sample_) {
|
||||
// 首次采样:直接初始化滤波值为原始值
|
||||
filtered_x = current_raw_x;
|
||||
filtered_y = current_raw_y;
|
||||
filtered_z = current_raw_z;
|
||||
is_first_sample_ = false; // 标记首次采样完成
|
||||
} else {
|
||||
// 核心滤波公式(整数版):α*原始值 + (1-α)*上一次滤波值
|
||||
// WEIGHTFACTOR=0.9; (1-WEIGHTFACTOR)=0.1
|
||||
const int32_t weight_scaled = (int32_t)(WEIGHTFACTOR * 10); // 9
|
||||
const int32_t inv_weight_scaled = 10 - weight_scaled; // 1
|
||||
|
||||
filtered_x = (weight_scaled * (int32_t)current_raw_x +
|
||||
inv_weight_scaled * last_filtered_x_) / 10;
|
||||
filtered_y = (weight_scaled * (int32_t)current_raw_y +
|
||||
inv_weight_scaled * last_filtered_y_) / 10;
|
||||
filtered_z = (weight_scaled * (int32_t)current_raw_z +
|
||||
inv_weight_scaled * last_filtered_z_) / 10;
|
||||
}
|
||||
|
||||
// 5. 转换为int16_t输出(filtered_x/y/z是int32_t,范围仍在int16_t内)
|
||||
*x = (int16_t)filtered_x;
|
||||
*y = (int16_t)filtered_y;
|
||||
*z = (int16_t)filtered_z;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Gyroscope::GetAcceleration(AccelerationData* data) {
|
||||
if (data == nullptr) {
|
||||
ESP_LOGE(TAG_GYROSCOPE, "数据指针为空");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int16_t raw_x, raw_y, raw_z;
|
||||
esp_err_t err = ReadRawAcceleration(&raw_x, &raw_y, &raw_z);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// 转换公式:加速度(g) = 原始值 × 灵敏度(mg/LSB) × 10^-3
|
||||
data->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<Gyroscope*>(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检查一次,更快的响应
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
#ifndef GYROSCOPE_H
|
||||
#define GYROSCOPE_H
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_check.h>
|
||||
#include <esp_timer.h>
|
||||
#include <driver/gpio.h>
|
||||
#include "i2c_bus.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include "esp_err.h"
|
||||
#include "config.h"
|
||||
#include "io_expansion_board.h"
|
||||
#include <functional>
|
||||
#include "esp_timer.h"
|
||||
|
||||
// 加速度数据结构体
|
||||
typedef struct {
|
||||
float x; // X轴加速度 (g)
|
||||
float y; // Y轴加速度 (g)
|
||||
float z; // Z轴加速度 (g)
|
||||
} AccelerationData;
|
||||
|
||||
// 一阶滤波系数
|
||||
#define WEIGHTFACTOR 0.93f
|
||||
|
||||
/* 陀螺仪寄存器定义 */
|
||||
#define CTRL1_XL 0x10 // 加速度计控制寄存器1
|
||||
#define CTRL2_G 0x11 // 陀螺仪控制寄存器2
|
||||
#define CTRL3_C 0x12 // 控制寄存器3
|
||||
#define CTRL4_C 0x13 // 控制寄存器4
|
||||
#define CTRL5_C 0x14 // 控制寄存器5
|
||||
#define CTRL6_C 0x15 // 控制寄存器6
|
||||
#define CTRL7_G 0x16 // 陀螺仪控制寄存器7
|
||||
#define CTRL8_XL 0x17 // 加速度计控制寄存器8
|
||||
#define CTRL9_XL 0x18 // 加速度计控制寄存器9
|
||||
#define CTRL10_C 0x19 // 控制寄存器10
|
||||
#define INT1_CTRL 0x0D // 中断1控制寄存器
|
||||
#define WAKE_UP_DUR 0x5B // 唤醒持续时间寄存器
|
||||
#define WAKE_UP_THS 0x5C // 唤醒阈值寄存器
|
||||
#define TAP_CFG 0x58 // 敲击配置寄存器
|
||||
#define TAP_THS_6D 0x59 // 6D方向阈值寄存器
|
||||
#define INT_DUR2 0x5A // 中断持续时间寄存器
|
||||
#define MD1_CFG 0x5E // 中断1配置寄存器
|
||||
#define MD2_CFG 0x5F // 中断2配置寄存器
|
||||
#define FUNC_SRC1 0x53 // 功能源寄存器1
|
||||
#define WAKE_UP_SRC 0x1B // 唤醒源寄存器
|
||||
|
||||
#define ACCEL_X_L 0x28 // OUTX_L_XL
|
||||
#define ACCEL_X_H 0x29 // OUTX_H_XL
|
||||
#define ACCEL_Y_L 0x2A // OUTY_L_XL
|
||||
#define ACCEL_Y_H 0x2B // OUTY_H_XL
|
||||
#define ACCEL_Z_L 0x2C // OUTZ_L_XL
|
||||
#define ACCEL_Z_H 0x2D // OUTZ_H_XL
|
||||
|
||||
#define ACCEL_SENSITIVITY_2G 0.061f // 2g 灵敏度 (g/LSB)
|
||||
|
||||
#define I2C_MASTER_TIMEOUT_MS 1000 // I2C主设备超时时间(毫秒)
|
||||
|
||||
/**
|
||||
* @class Gyroscope
|
||||
* @brief 陀螺仪驱动类,封装芯片操作
|
||||
*/
|
||||
#define TAG_GYROSCOPE "Gyroscope"
|
||||
class Gyroscope
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Gyroscope();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Gyroscope();
|
||||
|
||||
/**
|
||||
* @brief 初始化中断引脚
|
||||
* @param gpio_num 中断引脚编号
|
||||
* @return esp_err_t 初始化结果
|
||||
*/
|
||||
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
|
||||
|
||||
/**
|
||||
* @brief 创建陀螺仪读取任务
|
||||
*/
|
||||
void CreateGyroReadTask();
|
||||
|
||||
/**
|
||||
* @brief 初始化陀螺仪
|
||||
* @return esp_err_t 初始化结果
|
||||
*/
|
||||
esp_err_t Init(i2c_bus_handle_t bus_handle);
|
||||
|
||||
/**
|
||||
* @brief 清除中断标志
|
||||
* @return esp_err_t 清除结果
|
||||
*/
|
||||
esp_err_t ClearInterruptFlags();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取当前加速度数据
|
||||
* @param data 指向AccelerationData结构体的指针,用于存储读取到的加速度数据
|
||||
* @return esp_err_t 获取结果
|
||||
*/
|
||||
esp_err_t GetAcceleration(AccelerationData* data);
|
||||
|
||||
/**
|
||||
* @brief 创建500ms周期定时器
|
||||
* @return esp_err_t 创建结果
|
||||
*/
|
||||
esp_err_t CreateTimer();
|
||||
|
||||
/**
|
||||
* @brief 停止并删除定时器
|
||||
*/
|
||||
void DeleteTimer();
|
||||
|
||||
/**
|
||||
* @brief 注册摇晃检测回调函数
|
||||
* @param callback 当检测到摇晃时调用的回调函数
|
||||
*/
|
||||
void OnShake(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* @brief 注册拿起检测回调函数
|
||||
* @param callback 当检测到设备被拿起时调用的回调函数
|
||||
*/
|
||||
void OnPickUp(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* @brief 注册放置检测回调函数
|
||||
* @param callback 当检测到设备被放置时调用的回调函数
|
||||
*/
|
||||
void OnPutDown(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* @brief 注册倾斜检测回调函数
|
||||
* @param callback 当检测到设备倾斜时调用的回调函数
|
||||
*/
|
||||
void OnTilt(std::function<void()> callback);
|
||||
|
||||
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
|
||||
volatile uint8_t interrupt_count_ = 0; // 中断计数
|
||||
volatile bool interrupt_flag_ = false; // 中断标志位)(判断是否需要处理)
|
||||
|
||||
private:
|
||||
static void GpioIsrHandler(void* arg); // 中断处理函数
|
||||
static void GyroReadTask(void* pvParameters);
|
||||
static void TimerCallback(void* arg); // 定时器回调函数
|
||||
|
||||
void TimerInterruptHandler(); // 定时器中断处理函数
|
||||
|
||||
esp_err_t WriteRegister(uint8_t reg, uint8_t data);
|
||||
esp_err_t ReadRegister(uint8_t reg, uint8_t* data);
|
||||
esp_err_t ReadRegisters(uint8_t reg, uint8_t* data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief 读取原始加速度数据
|
||||
* @param x 指向X轴加速度存储的指针
|
||||
* @param y 指向Y轴加速度存储的指针
|
||||
* @param z 指向Z轴加速度存储的指针
|
||||
* @return esp_err_t 读取结果
|
||||
*/
|
||||
esp_err_t ReadRawAcceleration(int16_t* x, int16_t* y, int16_t* z);
|
||||
|
||||
std::function<void()> on_shake_callback_; // 摇晃回调函数
|
||||
std::function<void()> on_pickup_callback_; // 拿起回调函数
|
||||
std::function<void()> on_putdown_callback_; // 放置回调函数
|
||||
std::function<void()> on_tilt_callback_; // 倾斜回调函数
|
||||
|
||||
esp_timer_handle_t timer_handle_ = nullptr; // 定时器句柄
|
||||
|
||||
/**
|
||||
* @brief 分配静态任务所需的内存
|
||||
* @return bool 分配是否成功
|
||||
*/
|
||||
bool AllocateStaticTaskMemory();
|
||||
|
||||
/**
|
||||
* @brief 释放静态任务分配的内存
|
||||
*/
|
||||
void FreeStaticTaskMemory();
|
||||
|
||||
TaskHandle_t xGyroscopeTaskHandle_ = nullptr;
|
||||
StackType_t* pGyroscopeTaskStackBuffer_ = nullptr; // 任务栈指针(PSRAM)
|
||||
StaticTask_t* pxGyroscopeTaskBuffer_ = nullptr; // 任务控制块指针(内部RAM)
|
||||
|
||||
// 滤波相关成员变量
|
||||
int16_t last_filtered_x_ = 0; // 上一次滤波后的X轴值
|
||||
int16_t last_filtered_y_ = 0; // 上一次滤波后的Y轴值
|
||||
int16_t last_filtered_z_ = 0; // 上一次滤波后的Z轴值
|
||||
bool is_first_sample_ = true; // 是否为第一次采样标志
|
||||
|
||||
// 状态检测相关成员变量
|
||||
float accel_data_y_last_ = 0.0f; // 保存上一次Y轴数据
|
||||
float total_accel_last_ = 1.0f; // 保存上一次合成加速度值,初始值为1g(平放时的典型值)
|
||||
uint8_t stable_count_ = 0; // 稳定状态计数器
|
||||
uint8_t lift_count_ = 0; // 拿起动作计数器
|
||||
uint8_t putdown_count_ = 0; // 放置动作计数器
|
||||
bool is_stable_ = false; // 是否处于稳定平放状态
|
||||
bool is_lifted_ = false; // 是否处于被拿起状态
|
||||
};
|
||||
|
||||
#endif // GYROSCOPE_H
|
||||
@@ -0,0 +1,16 @@
|
||||
# main/05_executors/gyroscope/module.cmake
|
||||
|
||||
|
||||
# 判断 menuconfig 中的开关
|
||||
if(NOT CONFIG_ENABLE_EXEC_GYROSCOPE AND NOT EARLY_EXPANSION)
|
||||
message(STATUS " [Skip] gyroscope module disabled by menuconfig.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(APPEND MODULE_SRCS
|
||||
"05_executors/gyroscope/gyroscope.cc"
|
||||
)
|
||||
|
||||
list(APPEND MODULE_INCLUDE_DIRS
|
||||
"05_executors/gyroscope"
|
||||
)
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "millimeter_wave_radar.h"
|
||||
#include <cstring>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "MillimeterWaveRadar";
|
||||
|
||||
bool MillimeterWaveRadar::Init() {
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = MillimeterWaveRadar::BAUD_RATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
|
||||
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, TX_PIN, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, 256, 0, 0, NULL, 0));
|
||||
|
||||
// 清空缓冲区
|
||||
uart_flush(UART_NUM);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t MillimeterWaveRadar::CalculateChecksum(uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4) {
|
||||
return d1 ^ d2 ^ d3 ^ d4;
|
||||
}
|
||||
|
||||
// 通过查看最高位是0还是1来判断是否是写命令。但是查询感应模式时最高位不是1(5A 11 00 00 00 11 FE),输入的是0x11,所以仅对此条不适用。
|
||||
bool MillimeterWaveRadar::IsWriteCommand(uint8_t cmd) {
|
||||
if (cmd == 0x11) {
|
||||
return false;
|
||||
}
|
||||
return (cmd & 0x80) == 0x00; // 写命令高位为0
|
||||
}
|
||||
|
||||
bool MillimeterWaveRadar::SendFrame(const uint8_t *frame, size_t len) {
|
||||
int written = uart_write_bytes(UART_NUM, frame, len);
|
||||
uart_wait_tx_done(UART_NUM, pdMS_TO_TICKS(10));
|
||||
return written == (int)len;
|
||||
}
|
||||
|
||||
MillimeterWaveRadar::Result MillimeterWaveRadar::ReceiveFrame(uint8_t *buffer, size_t maxSize, size_t &receivedLen) {
|
||||
TickType_t start = xTaskGetTickCount();
|
||||
size_t total = 0;
|
||||
while ((xTaskGetTickCount() - start) < pdMS_TO_TICKS(TIMEOUT_MS)) {
|
||||
size_t available;
|
||||
uart_get_buffered_data_len(UART_NUM, &available);
|
||||
if (available >= 7) {
|
||||
int read = uart_read_bytes(UART_NUM, buffer, 7, pdMS_TO_TICKS(10));
|
||||
if (read == 7) {
|
||||
receivedLen = 7;
|
||||
// 校验帧头尾
|
||||
if (buffer[0] != 0x5A || buffer[6] != 0xFE) {
|
||||
ESP_LOGW(TAG, "Invalid frame header/tail");
|
||||
return Result::CHECKSUM_ERROR;
|
||||
}
|
||||
// 校验 checksum
|
||||
uint8_t expected = CalculateChecksum(buffer[1], buffer[2], buffer[3], buffer[4]);
|
||||
if (expected != buffer[5]) {
|
||||
ESP_LOGW(TAG, "Checksum mismatch");
|
||||
return Result::CHECKSUM_ERROR;
|
||||
}
|
||||
return Result::OK;
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
return Result::TIMEOUT;
|
||||
}
|
||||
|
||||
MillimeterWaveRadar::Result MillimeterWaveRadar::SetParameter(uint8_t paramId, uint32_t value) {
|
||||
if (!IsWriteCommand(paramId)) {
|
||||
return Result::INVALID_PARAM;
|
||||
}
|
||||
|
||||
uint8_t frame[7];
|
||||
frame[0] = 0x5A;
|
||||
frame[1] = paramId;
|
||||
|
||||
switch (paramId) {
|
||||
case SET_RADAR_DISTANCE_THRESHOLD:
|
||||
if (value < 100 || value > 65000) return Result::INVALID_PARAM;
|
||||
frame[2] = 0x00;
|
||||
frame[3] = (value >> 8) & 0xFF;
|
||||
frame[4] = value & 0xFF;
|
||||
ESP_LOGI(TAG, "Setting distance threshold to %d", static_cast<int>(value));
|
||||
break;
|
||||
case SET_RADAR_DELAY_TIME:
|
||||
if (value > 0xFFFFFF) return Result::INVALID_PARAM;
|
||||
frame[2] = (value >> 16) & 0xFF;
|
||||
frame[3] = (value >> 8) & 0xFF;
|
||||
frame[4] = value & 0xFF;
|
||||
ESP_LOGI(TAG, "Setting delay time to %d", static_cast<int>(value));
|
||||
break;
|
||||
case SET_RADAR_LOCKOUT_TIME:
|
||||
if (value > 0xFFFFFF) return Result::INVALID_PARAM;
|
||||
frame[2] = (value >> 16) & 0xFF;
|
||||
frame[3] = (value >> 8) & 0xFF;
|
||||
frame[4] = value & 0xFF;
|
||||
ESP_LOGI(TAG, "Setting lockout time to %d", static_cast<int>(value));
|
||||
break;
|
||||
case SET_RADAR_LIGHT_SENSOR_ENABLE:
|
||||
case SET_RADAR_OUTPUT_LEVEL:
|
||||
case SET_RADAR_POWER_MODE:
|
||||
case SET_RADAR_TRIGGER_MODE:
|
||||
case SET_RADAR_TX_POWER_LEVEL:
|
||||
case SET_RADAR_PWM_ENABLE:
|
||||
case SET_RADAR_PULSE_WIDTH:
|
||||
case SET_RADAR_SENSING_MODE:
|
||||
case SET_RADAR_SENSING_RESULT:
|
||||
if (value > 0xFF) return Result::INVALID_PARAM;
|
||||
frame[2] = 0x00;
|
||||
frame[3] = 0x00;
|
||||
frame[4] = value & 0xFF;
|
||||
break;
|
||||
case SET_RADAR_LIGHT_THRESHOLD:
|
||||
if (value > 0xFF) return Result::INVALID_PARAM;
|
||||
frame[2] = 0x00;
|
||||
frame[3] = 0x00;
|
||||
frame[4] = value & 0xFF;
|
||||
break;
|
||||
case SET_RADAR_PWM_DUTY:
|
||||
if (value > 3500) return Result::INVALID_PARAM;
|
||||
frame[2] = 0x00;
|
||||
frame[3] = (value >> 8) & 0xFF;
|
||||
frame[4] = value & 0xFF;
|
||||
break;
|
||||
default:
|
||||
return Result::INVALID_PARAM;
|
||||
}
|
||||
|
||||
frame[5] = CalculateChecksum(frame[1], frame[2], frame[3], frame[4]);
|
||||
frame[6] = 0xFE;
|
||||
|
||||
if (!SendFrame(frame, 7)) {
|
||||
return Result::UART_ERROR;
|
||||
}
|
||||
|
||||
uint8_t reply[7];
|
||||
size_t len;
|
||||
Result res = ReceiveFrame(reply, sizeof(reply), len);
|
||||
ESP_LOGW(TAG, "Received raw reply frame: %02X %02X %02X %02X %02X %02X %02X",
|
||||
reply[0], reply[1], reply[2], reply[3], reply[4], reply[5], reply[6]);
|
||||
if (res != Result::OK) return res;
|
||||
|
||||
// 发送保存指令
|
||||
uint8_t saveFrame[7] = {0x5A, 0x20, 0x00, 0x00, 0x01, 0x21, 0xFE};
|
||||
SendFrame(saveFrame, 7);
|
||||
// 等待保存回复(协议注释1:约100ms)
|
||||
vTaskDelay(pdMS_TO_TICKS(110)); // 稍微多等一点
|
||||
uart_flush(UART_NUM); // 清除可能的残留
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
void MillimeterWaveRadar::OnRadarDetect(RadarDetectCallback callback) {
|
||||
ESP_LOGI(TAG, "Registering radar detection callback");
|
||||
radar_detect_callback_ = callback;
|
||||
}
|
||||
|
||||
void MillimeterWaveRadar::TriggerRadarDetectCallback() {
|
||||
ESP_LOGI(TAG, "Triggering radar detection callback");
|
||||
if (radar_detect_callback_) {
|
||||
radar_detect_callback_();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
MillimeterWaveRadar::Result MillimeterWaveRadar::QueryParameter(uint8_t paramId, uint32_t &outputValue) {
|
||||
if (IsWriteCommand(paramId)) {
|
||||
ESP_LOGE(TAG, "Attempted to query with a write command ID: 0x%02X", paramId);
|
||||
return Result::INVALID_PARAM;
|
||||
}
|
||||
|
||||
uint8_t frame[7] = {0x5A, paramId, 0x00, 0x00, 0x00, paramId, 0xFE};
|
||||
ESP_LOGI(TAG, "Sending query frame for param 0x%02X: %02X %02X %02X %02X %02X %02X %02X",
|
||||
paramId, frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
|
||||
|
||||
if (!SendFrame(frame, 7)) {
|
||||
ESP_LOGE(TAG, "UART send failed");
|
||||
return Result::UART_ERROR;
|
||||
}
|
||||
|
||||
uint8_t reply[7];
|
||||
size_t len;
|
||||
Result res = ReceiveFrame(reply, sizeof(reply), len);
|
||||
if (res != Result::OK) {
|
||||
ESP_LOGW(TAG, "Failed to receive reply for param 0x%02X, error code: %d", paramId, static_cast<int>(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
// 打印原始回复帧(新增功能)
|
||||
ESP_LOGW(TAG, "Received raw reply frame: %02X %02X %02X %02X %02X %02X %02X",
|
||||
reply[0], reply[1], reply[2], reply[3], reply[4], reply[5], reply[6]);
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <driver/uart.h>
|
||||
#include <functional>
|
||||
|
||||
// 参数名称宏定义(对应协议中的命令)
|
||||
#define SET_RADAR_DISTANCE_THRESHOLD 0x01 // 设置感应距离门限值。Data3、Data4 为感应距离门限值,取值范围为 100 ~ 65000【值越大,感应距离越近】
|
||||
#define SET_RADAR_DELAY_TIME 0x02 // 设置输出延时时间。取值范围为 0X00~ 0XFFFFFF
|
||||
#define SET_RADAR_LIGHT_SENSOR_ENABLE 0x03 // 开关光感。Data4 为 0x01 时开启光感,为 0x00 时关闭光感。
|
||||
#define SET_RADAR_LOCKOUT_TIME 0x04 // 设置封锁时间。取值范围为 0X00~ 0XFFFFFF
|
||||
#define SET_RADAR_OUTPUT_LEVEL 0x05 // 设置有效电平。Data4 为 0x01 时高电平有效,为 0x00 时低电平有效
|
||||
#define SET_RADAR_POWER_MODE 0x06 // 设置功耗模式。Data4 为 0x00 时超低功耗模式(50/60uA),为 0x01 时常规功耗模式(13mA 档位)
|
||||
#define SET_RADAR_SENSING_RESULT 0x07 // 设置触发状态。Data4 为 0x00 时触发状态为未触发感应,为 0x01 时触发状态为已触发感应。
|
||||
#define SET_RADAR_TRIGGER_MODE 0x0A // 设置触发模式。Data4 为 0x00 时触发模式为连续触发,为 0x01 时触发模式为单次触发。
|
||||
#define SET_RADAR_TX_POWER_LEVEL 0x0B // 设置发射功率档位。Data4 取值范围为 0~ 7,总共 8 档发射功率可调,值越大,功率越小,=7 时最小,相当于关闭发射功率。发射功率档位值越小,实际发射功率越大,距离越远,抗干扰性越好。
|
||||
#define SET_RADAR_LIGHT_THRESHOLD 0x0C // 设置光感门限值。取值范围为 0X00~ 0XFFFFFF。
|
||||
#define SET_RADAR_PWM_ENABLE 0x0D // 开关 PWM 功能
|
||||
#define SET_RADAR_PWM_DUTY 0x0E // 设置 PWM 占空比
|
||||
#define SET_RADAR_PULSE_WIDTH 0x0F // 设置供电脉冲宽度
|
||||
#define SET_RADAR_SENSING_MODE 0x10 // 选择感应模式。Data4 为 0x00 时为移动感应模式,为 0x01 时为手扫感应模式。
|
||||
|
||||
// 参数名称宏定义(对应协议中的命令)
|
||||
#define QUERY_RADAR_DISTANCE_THRESHOLD 0x81 // 查询感应距离门限值
|
||||
#define QUERY_RADAR_DELAY_TIME 0x82 // 查询输出延时时间
|
||||
#define QUERY_RADAR_LIGHT_SENSOR_ENABLE 0x83 // 查询开关光感
|
||||
#define QUERY_RADAR_LOCKOUT_TIME 0x84 // 查询封锁时间
|
||||
#define QUERY_RADAR_OUTPUT_LEVEL 0x85 // 查询有效电平
|
||||
#define QUERY_RADAR_POWER_MODE 0x86 // 查询功耗模式
|
||||
#define QUERY_RADAR_SENSING_RESULT 0x87 // 查询触发状态
|
||||
#define QUERY_RADAR_TRIGGER_MODE 0x8A // 查询触发模式
|
||||
#define QUERY_RADAR_TX_POWER_LEVEL 0x8B // 查询发射功率档位
|
||||
#define QUERY_RADAR_LIGHT_THRESHOLD 0x8C // 查询光感门限值
|
||||
#define QUERY_RADAR_PWM_ENABLE 0x8D // 查询开关 PWM 功能
|
||||
#define QUERY_RADAR_PWM_DUTY 0x8E // 查询 PWM 占空比
|
||||
#define QUERY_RADAR_PULSE_WIDTH 0x8F // 查询供电脉冲宽度
|
||||
#define QUERY_RADAR_SENSING_MODE 0x11 // 查询感应模式
|
||||
|
||||
class MillimeterWaveRadar {
|
||||
public:
|
||||
// 雷达检测回调函数类型定义
|
||||
typedef std::function<void(void)> RadarDetectCallback;
|
||||
enum class Result {
|
||||
OK,
|
||||
TIMEOUT,
|
||||
CHECKSUM_ERROR,
|
||||
INVALID_PARAM,
|
||||
UART_ERROR
|
||||
};
|
||||
|
||||
// 初始化 UART(使用 UART1, TX=GPIO39, RX=GPIO40)
|
||||
bool Init();
|
||||
|
||||
// 设置参数(自动保存)
|
||||
Result SetParameter(uint8_t paramId, uint32_t value);
|
||||
|
||||
// 查询参数(返回值通过 outputValue 返回)
|
||||
Result QueryParameter(uint8_t paramId, uint32_t &outputValue);
|
||||
|
||||
Result QueryRawResponse(uint8_t paramId);
|
||||
|
||||
// 注册雷达检测回调函数
|
||||
void OnRadarDetect(RadarDetectCallback callback);
|
||||
|
||||
// 触发雷达检测回调函数(供IO扩展板调用)
|
||||
void TriggerRadarDetectCallback();
|
||||
|
||||
private:
|
||||
static constexpr uart_port_t UART_NUM = UART_NUM_1;
|
||||
static constexpr int TX_PIN = 39;
|
||||
static constexpr int RX_PIN = 40;
|
||||
static constexpr uint32_t BAUD_RATE = 9600;
|
||||
static constexpr uint32_t TIMEOUT_MS = 100;
|
||||
|
||||
// 雷达检测回调函数
|
||||
RadarDetectCallback radar_detect_callback_ = nullptr;
|
||||
|
||||
bool SendFrame(const uint8_t *frame, size_t len);
|
||||
Result ReceiveFrame(uint8_t *buffer, size_t maxSize, size_t &receivedLen);
|
||||
uint8_t CalculateChecksum(uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4);
|
||||
bool IsWriteCommand(uint8_t cmd);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
# main/05_executors/millimeter_wave_radar/module.cmake
|
||||
|
||||
|
||||
# 判断 menuconfig 中的开关
|
||||
if(NOT CONFIG_ENABLE_EXEC_MILLIMETER_WAVE_RADAR AND NOT EARLY_EXPANSION)
|
||||
message(STATUS " [Skip] millimeter_wave_radar module disabled by menuconfig.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(APPEND MODULE_SRCS
|
||||
"05_executors/millimeter_wave_radar/millimeter_wave_radar.cc"
|
||||
)
|
||||
|
||||
list(APPEND MODULE_INCLUDE_DIRS
|
||||
"05_executors/millimeter_wave_radar"
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
# main/05_executors/photo_interrupter/module.cmake
|
||||
|
||||
|
||||
# 判断 menuconfig 中的开关
|
||||
if(NOT CONFIG_ENABLE_EXEC_PHOTO_INTERRUPTER AND NOT EARLY_EXPANSION)
|
||||
message(STATUS " [Skip] photo_interrupter module disabled by menuconfig.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(APPEND MODULE_SRCS
|
||||
"05_executors/photo_interrupter/photo_interrupter.cc"
|
||||
)
|
||||
|
||||
list(APPEND MODULE_INCLUDE_DIRS
|
||||
"05_executors/photo_interrupter"
|
||||
)
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* @file photo_interrupter.cc
|
||||
* @brief 光电限位开关类实现文件
|
||||
*
|
||||
* 该类实现了光电限位开关的完整功能,包括初始化、中断处理、
|
||||
* 回调函数管理等。支持起始位置和结束位置两个限位开关的检测。
|
||||
*/
|
||||
|
||||
#include "photo_interrupter.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "PhotoInterrupter" ///< 日志标签,用于ESP-IDF日志系统
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*
|
||||
* 初始化PhotoInterrupter对象,设置默认状态
|
||||
* 目前构造函数为空,所有初始化操作在Init()方法中完成
|
||||
*/
|
||||
PhotoInterrupter::PhotoInterrupter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 清理资源,释放分配的内存
|
||||
* 目前析构函数为空,没有需要特殊清理的资源
|
||||
*/
|
||||
PhotoInterrupter::~PhotoInterrupter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化限位开关
|
||||
*
|
||||
* 执行限位开关的初始化操作,包括:
|
||||
* 1. 输出初始化日志
|
||||
* 2. 设置默认的起始限位开关回调函数
|
||||
* 3. 设置默认的结束限位开关回调函数
|
||||
*
|
||||
* 默认回调函数仅输出触发日志,用户可以通过OnStartTrigger()和OnEndTrigger()
|
||||
* 方法覆盖这些默认回调函数
|
||||
*/
|
||||
void PhotoInterrupter::Init()
|
||||
{
|
||||
ESP_LOGI(TAG, "限位开关初始化");
|
||||
|
||||
// 设置默认回调函数 - 起始限位开关触发时输出日志
|
||||
OnStartTrigger([]() {
|
||||
ESP_LOGI(TAG, "起始限位开关触发 - 默认回调");
|
||||
});
|
||||
|
||||
// 设置默认回调函数 - 结束限位开关触发时输出日志
|
||||
OnEndTrigger([]() {
|
||||
ESP_LOGI(TAG, "结束限位开关触发 - 默认回调");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理限位开关中断
|
||||
*
|
||||
* 当限位开关被触发时调用的中断处理函数
|
||||
* 目前仅输出中断触发日志,可以根据需要扩展更多处理逻辑
|
||||
*/
|
||||
void PhotoInterrupter::Interrupt()
|
||||
{
|
||||
ESP_LOGI(TAG, "限位开关中断触发");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置起始限位开关触发回调函数
|
||||
* @param callback 回调函数对象,当起始限位开关被触发时执行
|
||||
*
|
||||
* 允许用户自定义起始限位开关被触发时的处理逻辑。
|
||||
* 该函数会保存传入的回调函数,并输出注册日志。
|
||||
*/
|
||||
void PhotoInterrupter::OnStartTrigger(std::function<void()> callback)
|
||||
{
|
||||
on_start_trigger_ = callback;
|
||||
ESP_LOGI(TAG, "注册起始限位开关回调函数");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置结束限位开关触发回调函数
|
||||
* @param callback 回调函数对象,当结束限位开关被触发时执行
|
||||
*
|
||||
* 允许用户自定义结束限位开关被触发时的处理逻辑。
|
||||
* 该函数会保存传入的回调函数,并输出注册日志。
|
||||
*/
|
||||
void PhotoInterrupter::OnEndTrigger(std::function<void()> callback)
|
||||
{
|
||||
on_end_trigger_ = callback;
|
||||
ESP_LOGI(TAG, "注册结束限位开关回调函数");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 触发起始限位开关回调
|
||||
*
|
||||
* 当检测到起始限位开关被触发时,执行注册的回调函数。
|
||||
* 如果回调函数已设置(不为nullptr),则执行回调并输出触发日志。
|
||||
*
|
||||
* 该函数通常由IO扩展板在检测到起始限位开关中断时调用。
|
||||
*/
|
||||
void PhotoInterrupter::TriggerStartCallback()
|
||||
{
|
||||
if (on_start_trigger_) {
|
||||
ESP_LOGI(TAG, "触发起始限位开关回调");
|
||||
on_start_trigger_();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 触发结束限位开关回调
|
||||
*
|
||||
* 当检测到结束限位开关被触发时,执行注册的回调函数。
|
||||
* 如果回调函数已设置(不为nullptr),则执行回调并输出触发日志。
|
||||
*
|
||||
* 该函数通常由IO扩展板在检测到结束限位开关中断时调用。
|
||||
*/
|
||||
void PhotoInterrupter::TriggerEndCallback()
|
||||
{
|
||||
if (on_end_trigger_) {
|
||||
ESP_LOGI(TAG, "触发结束限位开关回调");
|
||||
on_end_trigger_();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file photo_interrupter.h
|
||||
* @brief 光电限位开关类头文件
|
||||
*
|
||||
* 该类用于处理光电限位开关的触发事件,支持起始位置和结束位置的检测。
|
||||
* 通过回调函数机制,当限位开关被触发时执行相应的处理逻辑。
|
||||
*/
|
||||
|
||||
#ifndef PHOTO_INTERRUPTER_H
|
||||
#define PHOTO_INTERRUPTER_H
|
||||
|
||||
#include "io_expansion_board.h"
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* @class PhotoInterrupter
|
||||
* @brief 光电限位开关类
|
||||
*
|
||||
* 提供限位开关的初始化、中断处理和回调函数管理功能。
|
||||
* 支持起始位置和结束位置两个限位开关的检测。
|
||||
*/
|
||||
class PhotoInterrupter {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*
|
||||
* 初始化PhotoInterrupter对象,设置默认状态
|
||||
*/
|
||||
PhotoInterrupter();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 清理资源,释放分配的内存
|
||||
*/
|
||||
~PhotoInterrupter();
|
||||
|
||||
/**
|
||||
* @brief 初始化限位开关
|
||||
*
|
||||
* 执行限位开关的初始化操作,包括设置默认回调函数
|
||||
*/
|
||||
void Init();
|
||||
|
||||
/**
|
||||
* @brief 处理限位开关中断
|
||||
*
|
||||
* 当限位开关被触发时调用的中断处理函数
|
||||
*/
|
||||
void Interrupt();
|
||||
|
||||
/**
|
||||
* @brief 设置起始限位开关触发回调函数
|
||||
* @param callback 回调函数对象,当起始限位开关被触发时执行
|
||||
*
|
||||
* 允许用户自定义起始限位开关被触发时的处理逻辑
|
||||
*/
|
||||
void OnStartTrigger(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* @brief 设置结束限位开关触发回调函数
|
||||
* @param callback 回调函数对象,当结束限位开关被触发时执行
|
||||
*
|
||||
* 允许用户自定义结束限位开关被触发时的处理逻辑
|
||||
*/
|
||||
void OnEndTrigger(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* @brief 触发起始限位开关回调
|
||||
*
|
||||
* 当检测到起始限位开关被触发时,执行注册的回调函数
|
||||
*/
|
||||
void TriggerStartCallback();
|
||||
|
||||
/**
|
||||
* @brief 触发结束限位开关回调
|
||||
*
|
||||
* 当检测到结束限位开关被触发时,执行注册的回调函数
|
||||
*/
|
||||
void TriggerEndCallback();
|
||||
|
||||
private:
|
||||
std::function<void()> on_start_trigger_; ///< 起始限位开关回调函数
|
||||
std::function<void()> on_end_trigger_; ///< 结束限位开关回调函数
|
||||
};
|
||||
|
||||
#endif // PHOTO_INTERRUPTER_H
|
||||
Reference in new issue
Block a user