Add initial board, HAL, and executor modules

This commit is contained in:
wang committed 2026-05-07 17:24:10 +08:00
1 parent 3b9e129c88
commit 0b2eebe76d
82 files changed
+20611

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
#ifndef BOARD_SELECTION_H
#define BOARD_SELECTION_H
#include "sdkconfig.h"
// 根据menuconfig的配置选择不同的board版本
#ifdef CONFIG_BOARD_VERSION_V1_1_11
#include "02_physical/boards/board_v1_1_11/board_sweekar.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_12)
#include "02_physical/boards/board_v1_1_12/board_sweekar.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_13)
#include "02_physical/boards/board_v1_1_13/board_sweekar.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_14)
#include "02_physical/boards/board_v1_1_14/board_sweekar.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_16)
#include "02_physical/boards/board_v1_1_16/board_sweekar.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_17)
#include "02_physical/boards/board_v1_1_17/board_sweekar.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_18)
#include "02_physical/boards/board_v1_1_18/board_sweekar.h"
#else
#include "02_physical/boards/board_v1_1_11/board_sweekar.h"
#endif
#endif // BOARD_SELECTION_H
@@ -0,0 +1,261 @@
/**
* @file board.cc
* @brief 板级外设初始化类,包含步进电机、毫米波雷达、I2C总线等外设的初始化函数。
* @author zouyuhua
* @date 2025年8月7日
*
*/
extern "C" {
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/uart_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2c_bus.h"
}
#include "board_sweekar.h"
#include "config.h"
#include "drv8428.h"
#include "aw8646.h"
#define TAG "board_sweekar"
BoardSweekar::BoardSweekar()
{
#ifdef CONFIG_STEPPER_DRIVER_AW8646
stepper_driver_ = new Aw8646Driver(io_expansion_);
#else
stepper_driver_ = new Drv8428Driver(io_expansion_);
#endif
}
BoardSweekar::~BoardSweekar()
{
if (stepper_driver_) {
delete stepper_driver_;
stepper_driver_ = nullptr;
}
}
/**
* @file board.h
* @brief 板级外设初始化类
*/
/**
* @brief 步进电机初始化函数
*
* 配置LEDC定时器和通道用于PWM输出,设置步进电机的工作模式、使能状态、细分模式和方向,
* 并初始化PWM占空比为50%。初始化过程中若出现错误,会通过ESP_LOGE输出错误信息及错误码。
*/
void BoardSweekar::StepperMotorInit(void)
{
esp_err_t err = ESP_OK;
// 1) STEP 引脚改为普通 GPIO
gpio_config_t io_cfg;
io_cfg.pin_bit_mask = 1ULL << STEPPER_STEP_IO;
io_cfg.mode = GPIO_MODE_OUTPUT;
io_cfg.pull_up_en = GPIO_PULLUP_DISABLE;
io_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_cfg.intr_type = GPIO_INTR_DISABLE;
err = gpio_config(&io_cfg);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机 STEP GPIO 配置失败, 错误码: 0x%x", err);
}
// 初始电平拉低
gpio_set_level(STEPPER_STEP_IO, 0);
// 2) 检查 I2C 总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 3) 初始化驱动对象并初始化
if (stepper_driver_) {
err = stepper_driver_->Init();
if (err != ESP_OK) {
ESP_LOGE(TAG_BOARD, "步进电机驱动初始化失败, 错误码: 0x%x", err);
}
} else {
ESP_LOGE(TAG_BOARD, "步进电机驱动创建失败");
}
}
/**
* @brief 毫米波雷达串口初始化函数
*
* 配置毫米波雷达所使用的串口参数(波特率、数据位、校验位、停止位等),设置串口引脚,
* 并安装串口驱动。采用较大的RX/TX FIFO深度以处理突发数据,禁用驱动自带事件队列以避免
* 与自定义中断冲突。
*/
void BoardSweekar::MillimeterWaveRadarUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = 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,
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(RADAR_UART_NUM, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(RADAR_UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动(禁用驱动自带事件队列,避免与自定义中断冲突)
// 增加 RX/TX FIFO 深度以处理突发数据
ESP_ERROR_CHECK(uart_driver_install(RADAR_UART_NUM, RADAR_BUF_SIZE * 4, RADAR_BUF_SIZE * 4,
0, NULL, 0));
}
/**
* @brief 毫米波雷达中断引脚初始化函数
*
* 配置毫米波雷达中断引脚为输入模式,禁用上拉电阻,使能下拉电阻,初始禁用中断功能
* (如需使用中断需单独进行配置)。
*/
void BoardSweekar::MillimeterWaveRadarGpioInit(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << MILLIMETER_WAVE_RADAR_PIN), // 选择GPIO引脚
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE, // 使能下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断(如需中断需单独配置)
};
// 配置GPIO
gpio_config(&io_conf);
}
/**
* @brief I2C总线及从机设备初始化函数
*
* 首先配置并创建I2C主机总线,然后依次添加IO扩展板和线性马达作为I2C从机设备到总线上。
* 若总线创建或设备添加失败,会输出错误信息并进行相应的资源清理(删除已创建的总线)。
* 初始化成功后,会通过ESP_LOGI输出IO扩展板和线性马达的从机地址信息。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示相应的失败原因
*/
esp_err_t BoardSweekar::I2CMasterInit(void)
{
static const char *I2C_INIT = "I2C_INIT";
// 初始化IO扩展板复位引脚
gpio_config_t reset_io_conf = {
.pin_bit_mask = (1ULL << IO_EXPAND_RSTN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&reset_io_conf);
// 执行复位时序:先拉低100ms,再拉高并等待100ms
gpio_set_level(IO_EXPAND_RSTN, 0); // 拉低复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms
gpio_set_level(IO_EXPAND_RSTN, 1); // 释放复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
ESP_LOGI(I2C_INIT, "IO扩展板复位完成");
// 创建统一的I2C总线(使用ESP-ADF的i2c_bus)
i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C0_MASTER_SDA_IO,
.scl_io_num = I2C0_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = 100000,
},
};
// 创建I2C总线(ESP-ADF的audio_board_init会复用这个总线)
bus_handle_ = i2c_bus_create(I2C0_MASTER_PORT, &i2c_cfg);
if (bus_handle_ == NULL) {
ESP_LOGE(TAG, "Failed to create I2C bus");
return ESP_FAIL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
// ESP-ADF的i2c_bus不需要单独添加设备,直接通过总线句柄和设备地址进行通信
// 验证线性马达设备是否存在
esp_err_t err = i2c_bus_probe_addr(bus_handle_, LINEAR_MOTOR_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "线性马达设备未找到(地址:0x%02X),错误码: 0x%x", LINEAR_MOTOR_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "线性马达设备检测成功(地址:0x%02X)", LINEAR_MOTOR_I2C_SLAVE_ADDR);
}
// 验证IO扩展板设备是否存在
err = i2c_bus_probe_addr(bus_handle_, I2C_IO_EXPAND_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "IO扩展板设备未找到(地址:0x%02X),错误码: 0x%x", I2C_IO_EXPAND_SLAVE_ADDR, err);
// 不返回错误,继续初始化其他设备
} else {
ESP_LOGI(I2C_INIT, "IO扩展板设备检测成功(地址:0x%02X)", I2C_IO_EXPAND_SLAVE_ADDR);
}
// 验证陀螺仪设备是否存在
err = i2c_bus_probe_addr(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "陀螺仪设备未找到(地址:0x%02X),错误码: 0x%x", GYROSCOPE_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "陀螺仪设备检测成功(地址:0x%02X)", GYROSCOPE_I2C_SLAVE_ADDR);
}
// 为IO扩展板设置总线句柄和设备地址(使用7位地址)
io_expansion_.bus_handle_ = bus_handle_;
io_expansion_.device_addr_ = I2C_IO_EXPAND_SLAVE_ADDR << 1;
// 为线性马达设置总线句柄和设备地址(使用7位地址)
linear_motor_.bus_handle_ = bus_handle_;
linear_motor_.device_addr_ = LINEAR_MOTOR_I2C_SLAVE_ADDR << 1;
ESP_LOGI(I2C_INIT, "I2C总线初始化完成");
return ESP_OK;
}
/**
* @brief 开启VBAT to VBAT_5V
*
* 创建开启升压的电源管理单元
*
*/
void BoardSweekar::VbatToVbat5vInit(void)
{
// 配置VBAT_5V引脚为输出模式
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << VBAT_5V_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 使能VBAT_5V
gpio_set_level(VBAT_5V_PIN, 1);
}
@@ -0,0 +1,46 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include "config.h"
#include "io_expansion_board.h"
#include "stepper_motor.h"
#include "stepper_driver.h"
#include "i2c_bus.h"
#include "gyroscope.h"
#include "touch_sensor.h"
class BoardSweekar
{
private:
i2c_bus_handle_t bus_handle_ = nullptr; // 使用ESP-ADF的I2C总线句柄
public:
BoardSweekar();
~BoardSweekar();
IOExpansionBoard io_expansion_; // IO扩展板对象
LinearMotor linear_motor_; // 线性马达对象
Gyroscope gyroscope_; // 陀螺仪对象
TouchSensor touch_sensor_; // 触摸传感器对象
StepperDriver* stepper_driver_ = nullptr; // 步进电机驱动对象
StepperDriver* GetStepperDriver() const { return stepper_driver_; }
// 初始化步进电机
void StepperMotorInit(void);
// 初始化串口
void MillimeterWaveRadarUartInit(void);
//初始化毫米波雷达的中断引脚
void MillimeterWaveRadarGpioInit(void);
// 初始化I2C总线
esp_err_t I2CMasterInit(void);
// 初始化VBAT toVBAT_5V引脚
void VbatToVbat5vInit(void);
// 获取I2C总线句柄
i2c_bus_handle_t GetBusHandle() const { return bus_handle_; }
};
#endif
@@ -0,0 +1,138 @@
/* 定义引脚分配 */
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
/**
* @file config.h
* @brief 定义了引脚的分配。
* @author zouyuhua
* @date 2025年8月7日
*
*/
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_rom_sys.h" // 用于 esp_rom_delay_us
#include "esp_timer.h"
#include "esp_mac.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/timer.h"
#include "esp_intr_alloc.h"
#include "soc/timer_group_struct.h" // 包含TIMERG0、TIMERG1的结构体定义
#include "soc/timer_group_reg.h" // 包含定时器相关寄存器宏定义
#include "driver/uart.h"
#include "esp_err.h"
#include "freertos/queue.h"
#include "soc/uart_reg.h"
#include "linear_motor.h"
#include "gyroscope.h"
#define TAG_BOARD "SWEEKAR_ESP32"
/*I2C总线引脚定义*/
#define I2C0_MASTER_PORT I2C_NUM_0
#define I2C0_MASTER_SDA_IO GPIO_NUM_2
#define I2C0_MASTER_SCL_IO GPIO_NUM_3
/* 步进电机引脚设置 */
#define LEDC_OUTPUT_IO GPIO_NUM_42 // 步进电机LEDC引脚输入
/* 定义屏幕驱动控制引脚 */
#define ST7789_PIN_CLK GPIO_NUM_48 // SPI时钟引脚
#define ST7789_PIN_DC GPIO_NUM_38 // 数据/命令选择引脚RSEL
#define ST7789_PIN_MOSI GPIO_NUM_47 // SPI数据输出引脚
#define ST7789_PIN_CS GPIO_NUM_40 // SPI片选引脚
#define ST7789_PIN_BL GPIO_NUM_NC // 背光引脚
#define ST7789_PIN_RST GPIO_NUM_NC // 复位引脚
#define LCD_BL_ACTIVE_LEVEL 0 // 背光有效电平:1=高电平有效(NMOS),0=低电平有效(PMOS)
#define LCD_WIDTH 172 // 屏幕宽度(水平方向像素数)
#define LCD_HEIGHT 320 // 屏幕高度(垂直方向像素数)
/* 毫米波雷达配置参数 */
/* 定义毫米波雷达的中断引脚 */
#define MILLIMETER_WAVE_RADAR_PIN GPIO_NUM_35
// UART 配置参数
#define RADAR_UART_NUM UART_NUM_1 // 使用 UART0
#define UART_TX_PIN GPIO_NUM_NC // GPIO43 作为 TX(根据实际硬件连接调整)
#define UART_RX_PIN GPIO_NUM_39 // GPIO44 作为 RX
#define BAUD_RATE 9600 // 波特率
#define RADAR_BUF_SIZE 100 // 雷达接收缓冲区大小
/* IO扩展硬件配置常量 */
#define I2C_MASTER_FREQ_HZ 100000 // I2C通信频率(100kHz)(可更改至400000)
#define I2C_IO_EXPAND_SLAVE_ADDR 0x5B // AW9523BTQR器件地址
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // 中断检测引脚
#define ESP_INTR_FLAG_DEFAULT 0 // 中断标志默认值
#define IO_EXPAND_RSTN GPIO_NUM_45 // IO扩展板复位引脚
/* 定义线性马达的I2C从机配置常量 */
#define LINEAR_MOTOR_I2C_SLAVE_ADDR 0x5A // 线性马达的I2C从机地址
/* 定义陀螺仪的I2C从机配置常量 */
#define GYROSCOPE_I2C_SLAVE_ADDR 0x6A // 陀螺仪的I2C从机地址
#define GYROSCOPE_INPUT_PIN_SEL GPIO_NUM_21 // 陀螺仪中断引脚
/* 原生触摸引脚配置 */
// 触摸按键数量配置
// #define TOUCH_BUTTON_NUM 4
#define TOUCH_BUTTON_NUM 3
// 触摸通道定义
#define TOUCH_PAD_CHANNEL_1 TOUCH_PAD_NUM11 // GPIO11
#define TOUCH_PAD_CHANNEL_2 TOUCH_PAD_NUM12 // GPIO12
#define TOUCH_PAD_CHANNEL_3 TOUCH_PAD_NUM13 // GPIO13
// #define TOUCH_PAD_CHANNEL_4 TOUCH_PAD_NUM14 // GPIO14
/* ES8311音频编解码器配置 (ESP-ADF兼容) */
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
/* I2S GPIO定义 */
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_15
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
/* PA引脚定义 */
#define AUDIO_CODEC_PA_PIN GPIO_NUM_8
/* I2C配置 */
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_2
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_3
#define AUDIO_CODEC_ES8311_ADDR 0x18
#define AUDIO_CODEC_ES7210_ADDR 0x40
/* WS2812 RGB LED配置 */
#define WS2812_PIN GPIO_NUM_41 // WS2812数据引脚
#define WS2812_NUM_LEDS 2 // WS2812 LED数量
/* 创建VBAT to VBAT_5V */
#define VBAT_5V_PIN GPIO_NUM_8 // VBAT_5V引脚
/* 定义按键的中断引脚 */
#define BUTTON_PIN GPIO_NUM_0 // 按键中断引脚
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,732 @@
#include "io_expansion_board.h"
#include "millimeter_wave_radar.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/idf_additions.h"
#include "freertos/portable.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_rom_sys.h"
/**
* @file io_expansion_board.cc
* @brief IO扩展板功能实现类,包含I2C通信、GPIO中断配置、外设控制等功能
* @details 实现了IO扩展板的初始化(中断引脚、复位引脚)、寄存器读写、步进电机控制(细分模式、使能、方向、睡眠状态)、
* LCD相关引脚控制(背光、复位、片选)、毫米波雷达中断引脚配置等功能,并提供I2C数据读取任务的创建与管理
* @author zouyuhua
* @date 2025年8月7日
*
*/
/**
* @brief 构造函数
*/
IOExpansionBoard::IOExpansionBoard()
: bus_handle_(nullptr),
device_addr_(0),
i2c_task_event_group_(nullptr),
xI2CReadTaskHandle_(nullptr)
{
}
/**
* @brief 析构函数
*/
IOExpansionBoard::~IOExpansionBoard()
{
// 删除任务
if (xI2CReadTaskHandle_) {
vTaskDelete(xI2CReadTaskHandle_);
}
// if(pI2CReadTaskStackBuffer) {
// heap_caps_free(pI2CReadTaskStackBuffer);
// pI2CReadTaskStackBuffer = nullptr;
// }
// if(pxI2CReadTaskBuffer) {
// vPortFree(pxI2CReadTaskBuffer);
// pxI2CReadTaskBuffer = nullptr;
// }
// 删除事件组
if (i2c_task_event_group_) {
vEventGroupDelete(i2c_task_event_group_);
}
// 移除中断处理
if (interrupt_gpio_ != GPIO_NUM_NC) {
gpio_isr_handler_remove(interrupt_gpio_);
}
// 删除IO扩展板设备
// 使用i2c_bus API时不需要手动删除设备句柄
// bus_handle_由board_sweekar统一管理
}
/**
* @brief 静态中断处理函数
*/
void IRAM_ATTR IOExpansionBoard::GpioIsrHandler(void* arg)
{
IOExpansionBoard* instance = static_cast<IOExpansionBoard*>(arg);
if (!instance) return;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// 设置事件标志位
if (instance->i2c_task_event_group_) {
xEventGroupSetBitsFromISR(
instance->i2c_task_event_group_,
BIT0,
&xHigherPriorityTaskWoken
);
}
// // 唤醒挂起的I2C任务
// if (instance->xI2CReadTaskHandle_) {
// if (xTaskResumeFromISR(instance->xI2CReadTaskHandle_) == pdTRUE) {
// xHigherPriorityTaskWoken = pdTRUE;
// }
// }
// 触发上下文切换
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
/**
* @brief 初始化GPIO中断检测
*/
esp_err_t IOExpansionBoard::InterruptGpioInit(gpio_num_t gpio_num)
{
// 配置IO扩展板中断引脚
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.pin_bit_mask = 1ULL << gpio_num;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
esp_err_t err = gpio_config(&io_conf);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO配置失败,错误码: 0x%x", err);
return err;
}
// 配置中断服务
err = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO中断服务安装失败,错误码: 0x%x", err);
return err;
}
// 注册中断处理函数
err = gpio_isr_handler_add(gpio_num, GpioIsrHandler, this);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO中断处理函数添加失败,错误码: 0x%x", err);
return err;
}
// 创建事件组
i2c_task_event_group_ = xEventGroupCreate();
if (!i2c_task_event_group_) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "事件组创建失败");
return ESP_FAIL;
}
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "GPIO%d中断初始化完成", gpio_num);
return ESP_OK;
}
/**
* @brief 初始化硬件复位引脚
* 设置为上拉,需要复位,需要输出低电平为20us的脉冲信号
*/
esp_err_t IOExpansionBoard::ResetGpioInit(gpio_num_t gpio_num)
{
reset_gpio_ = gpio_num;
// 配置复位引脚
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = 1ULL << gpio_num;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
esp_err_t err = gpio_config(&io_conf);
gpio_set_level(gpio_num, 1); // 复位引脚拉高
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO配置失败,错误码: 0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "GPIO%d复位引脚初始化完成", gpio_num);
return err;
}
}
/**
* @brief 复位IO扩展板
*/
esp_err_t IOExpansionBoard::ResetIoExpand()
{
if (reset_gpio_ == GPIO_NUM_NC) {
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "复位引脚未初始化");
return ESP_ERR_INVALID_STATE;
}
// 复位IO扩展板
gpio_set_level(reset_gpio_, 0);
esp_rom_delay_us(30);
gpio_set_level(reset_gpio_, 1);
uint8_t ctl_val;
//配置P0_0-P0_7的端口为推挽输出模式
esp_err_t err = IoexpandReadReg(CTL, &ctl_val);
ctl_val |= (1 << 4); // 设置D[4]位为1(GPOMD=1),切换P0为推挽模式
err = IoexpandWriteReg(CTL, ctl_val);
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "复位失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "复位成功");
}
return ESP_OK;
}
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IOExpansionBoard::IoexpandWriteReg(uint8_t reg_addr, uint8_t data)
{
return i2c_bus_write_bytes(bus_handle_, device_addr_, &reg_addr, 1, &data, 1);
}
/**
* @brief 从寄存器读取数据
* @param reg_addr 寄存器地址(如INPUT_PORT0、ID等)
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果(ESP_OK为成功)
*/
esp_err_t IOExpansionBoard::IoexpandReadReg(uint8_t reg_addr, uint8_t* data)
{
if (data == nullptr){ // 检查输入参数有效性
return ESP_ERR_INVALID_ARG;
}
// 使用i2c_bus_read_bytes进行寄存器读取
esp_err_t err = i2c_bus_read_bytes(bus_handle_, device_addr_, &reg_addr, 1, data, 1);
if (err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "读取寄存器失败,错误码:0x%x", err);
return err;
}
return ESP_OK;
}
//设置IO扩展板为输入模式或是输出模式
/*
* 这里需要根据实际情况设置IO扩展板的模式,比如输入模式还是输出模式,以及各个引脚的配置。
* P0_0 对应NFC_RST引脚,需要设置为输出模式,低电平有效。(待测)
* P0_1 对应USB_VBUS引脚,需要设置为输出模式,低电平有效。(待测)
* P0_2 对应BTN_RIGHT_EAR引脚,需要设置为输出模式,低电平有效。(待测)
* P0_3 暂时未使用。
* P0_4 对应步进电机方向引脚,需要设置为输出模式,根据需求设置。(待测)
* P0_5 对应CHRG引脚(待测)
* P0_6 LCD_DC引脚,需要设置为输出模式,根据需求设置电平。(待测)
* P0_7 对应步进电机的使能引脚,需要设置为输出模式,根据需求设置。(待测)
*
*
* P1_0 对应EG800K_PWR。(待测)
* P1_1 对应LCD_RST,需要设置为输出模式,低电平有效。(待测)
* P1_2 对应LCD_CS,需要设置为输出模式,低电平有效。(待测)
* P1_3 暂时未使用。
* P1_4 对应步进电机的细分模式M1引脚。(待测)
* P1_5 对应步进电机的细分模式M0引脚。(待测)
* P1_6 对应步进电机的睡眠引脚。(待测)
* P1_7 对应RADAR_O雷达的输出引脚。(待测)
* */
esp_err_t IOExpansionBoard::IoexpandSetMode(void)
{
uint8_t ctl_val;
//配置P0_0-P0_7的端口为推挽输出模式
esp_err_t err = IoexpandReadReg(CTL, &ctl_val);
ctl_val |= (1 << 4); // 设置D[4]位为1(GPOMD=1),切换P0为推挽模式
err = IoexpandWriteReg(CTL, ctl_val);
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P0端口失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P0端口成功");
}
// 配置P0_0-P0_7的端口为输入还是输出:0-输出;1-输入
// err = IoexpandWriteReg(CONFIG_PORT0, 0x04); // 0000 0100
err = IoexpandWriteReg(CONFIG_PORT0, 0x24); // 0010 0100
// err = IoexpandWriteReg(CONFIG_PORT0, 0x2C); // 0010 1100 0-2,0-3,0-5都是输入
// D 1101
// 9 1001
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P0端口失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P0端口成功");
}
// 配置P1_0-P1_7的端口输出:0-输出;1-输入
// err = IoexpandWriteReg(CONFIG_PORT1, 0x84);
err = IoexpandWriteReg(CONFIG_PORT1, 0x80); // 1000 0000
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P1端口失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P1端口成功");
}
// 配置P0_0-P0_7的初始为高电平
// 配置P1_0-P1_7的初始为高电平
err = IoexpandWriteReg(OUTPUT_PORT0, 0xFD); // 1111 1101
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P0端口初始值失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P0端口初始值成功");
}
err = IoexpandWriteReg(OUTPUT_PORT1, 0x7E); // 0111 1110
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P1端口初始值失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P1端口初始值成功");
}
err = IoexpandWriteReg(INT_PORT1, 0x7F); // 0111 1111
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P1端口中断使能失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P1端口中断使能成功");
}
return ESP_OK;
}
/**
* @brief 设置步进电机的细分模式
*/
void IOExpansionBoard::SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT, uint8_t m0_pin, uint8_t m1_pin)
{
uint8_t current_val;
switch (mode) {
case 1: //全步输出模式 低低电平
/* code */
printf("设置步进电机为全步模式\n");
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
case 2: //非圆 1/2 高低电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
case 3: //1/4 步 低高电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
case 4: //1/8 步 高高电平
/* code */
printf("设置步进电机为1/8步模式\n");
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
default: //全步输入模式 低低电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
}
}
/**
* @brief 设置步进电机的使能
*/
esp_err_t IOExpansionBoard::SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin)
{
uint8_t current_val;
if(enable){
// 进入工作状态mode
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << enable_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入睡眠状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << enable_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief 设置步进电机运行方向
*/
esp_err_t IOExpansionBoard::StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin)
{
uint8_t current_val;
if(dir){
// 方向为正转
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief 设置步进电机是否进入睡眠或者工作状态(高电平进入工作状态,低电平进入睡眠状态)
*/
esp_err_t IOExpansionBoard::SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin)
{
uint8_t current_val;
if(mode){
// 进入工作状态mode
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << nsleep_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入睡眠状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << nsleep_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/*********************************************IO扩展板LCD引脚配置参数***********************************************/
/**
* @brief 设置 LCD_BL引脚电平(高电平为命令模式,低电平为数据模式)
*/
esp_err_t IOExpansionBoard::SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin)
{
uint8_t current_val;
if(mode){
// 进入命令模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << bl_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入数据模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << bl_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief 设置 LCD_RST引脚电平(高电平为复位模式,低电平为工作模式)
*/
esp_err_t IOExpansionBoard::SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t reset_pin)
{
uint8_t current_val;
if(mode){
// 进入复位模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << reset_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入工作模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << reset_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/*********************************************IO扩展板的输入引脚配置以及中断引脚定义***********************************************/
/**
* @brief 设置 毫米波雷达的中断引脚
*/
esp_err_t IOExpansionBoard::SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin)
{
uint8_t current_val;
if(mode){
// 进入输入模式
IoexpandReadReg(INPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << interrupt_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(INPUT_PORT, current_val);
}
else{
// 进入输出模式
IoexpandReadReg(INPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << interrupt_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(INPUT_PORT, current_val);
}
}
/**
* @brief 设置 LCD_CS引脚电平(低电平为选中状态,高电平为不选中状态)
*/
esp_err_t IOExpansionBoard::SetLcdCsMode(bool mode, uint8_t OUTPUT_PORT, uint8_t cs_pin)
{
uint8_t current_val;
if(mode){
// 进入选中状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << cs_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入不选中状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << cs_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief I2C数据读取任务主体
*/
void IOExpansionBoard::I2cReadTask(void* pvParameters)
{
IOExpansionBoard* instance = static_cast<IOExpansionBoard*>(pvParameters);
if (!instance) {
vTaskDelete(nullptr);
return;
}
// 初始化复位引脚
esp_err_t err = instance->ResetGpioInit(IO_EXPAND_RSTN);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO复位初始化失败");
}
// 初始化IO扩展板子的中断引脚
err = instance->InterruptGpioInit(GPIO_INPUT_PIN_SEL);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO中断初始化失败");
}
// 设置IO扩展板模式
instance->IoexpandSetMode();
// 设置LCD屏幕背光引脚(初始为关闭)
err = instance->SetLcdBlMode(LCD_BL_HIGH, LCD_BL_PORT, LCD_BL_PIN);
if(err != ESP_OK){
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "设置LCD屏幕背光引脚失败");
}
else{
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "设置LCD屏幕背光引脚成功");
}
// 设置LCD屏幕复位引脚
err = instance->SetLcdResetMode(LCD_RST_HIGH, LCD_RST_PORT, LCD_RST_PIN);
if(err != ESP_OK){
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "设置LCD屏幕复位引脚失败");
}
else{
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "设置LCD屏幕复位引脚成功");
}
uint8_t current_val;
for (;;) {
// 等待中断事件触发
if (instance->i2c_task_event_group_) {
xEventGroupWaitBits(instance->i2c_task_event_group_, BIT0, pdTRUE, pdFALSE, portMAX_DELAY);
}
// 读取I2C数据
instance->IoexpandReadReg(INPUT_PORT1,&current_val);
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "读取数据: 0x%0X", current_val);
if(current_val & (1 << 7)){
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "检测到中断来自雷达引脚(P1_7)\n");
// 调用毫米波雷达回调函数
if (instance->radar_) {
instance->radar_->TriggerRadarDetectCallback();
}
}
// vTaskSuspend(nullptr); // 挂起任务等待下次唤醒
}
}
// bool IOExpansionBoard::AllocateStaticTaskMemory()
// {
// printf("IO拓展芯片创建任务前:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
// printf("IO拓展芯片创建任务前 Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
// // 分配任务栈(PSRAM)
// const size_t TASK_STACK_SIZE = 4096;
// pI2CReadTaskStackBuffer = (StackType_t*)heap_caps_malloc(
// TASK_STACK_SIZE * sizeof(StackType_t),
// MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT
// );
// if (!pI2CReadTaskStackBuffer) {
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "Failed to allocate task stack from PSRAM");
// return false;
// }
// // 分配任务控制块(内部RAM)
// pxI2CReadTaskBuffer = (StaticTask_t*)pvPortMalloc(sizeof(StaticTask_t));
// if (!pxI2CReadTaskBuffer) {
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "Failed to allocate task TCB from internal RAM");
// heap_caps_free(pI2CReadTaskStackBuffer);
// pI2CReadTaskStackBuffer = nullptr;
// return false;
// }
// ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "Static task memory allocated successfully");
// printf("IO拓展芯片创建任务后:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
// printf("IO拓展芯片创建任务后 Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
// return true;
// }
// void IOExpansionBoard::FreeStaticTaskMemory()
// {
// if (pI2CReadTaskStackBuffer != nullptr) {
// heap_caps_free(pI2CReadTaskStackBuffer);
// pI2CReadTaskStackBuffer = nullptr;
// }
// if (pxI2CReadTaskBuffer != nullptr) {
// vPortFree(pxI2CReadTaskBuffer);
// pxI2CReadTaskBuffer = nullptr;
// }
// ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "Static task memory freed");
// }
/**
* @brief 创建I2C数据读取任务
*/
void IOExpansionBoard::CreateI2cReadTask()
{
printf("创建I2C读取任务之前:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
printf("创建I2C读取任务之前:PSRAM Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
if (xI2CReadTaskHandle_) {
return; // 任务已创建
}
xTaskCreateWithCaps(
I2cReadTask,
"i2c_read_task",
I2C_TASK_STACK,
this,
I2C_TASK_PRIORITY,
&xI2CReadTaskHandle_,
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT
);
if (xI2CReadTaskHandle_ == NULL) {
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "Failed to create static I2C read task");
} else {
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "Static I2C read task created successfully");
}
printf("创建I2C读取任务之后:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
printf("创建I2C读取任务之后:PSRAM Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
/**
* @brief 启动I2C任务 (整合初始化和任务创建)
*/
esp_err_t IOExpansionBoard::StartTask()
{
// 再创建任务
if (!xI2CReadTaskHandle_) {
CreateI2cReadTask();
}
return ESP_OK;
}
@@ -0,0 +1,284 @@
#ifndef IO_EXPANSION_BOARD_H
#define IO_EXPANSION_BOARD_H
#include <cstdint>
#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 "portmacro.h"
// 前向声明,避免循环引用
class MillimeterWaveRadar;
/* 寄存器地址 */
#define INPUT_PORT0 0x00 // 输入端口0寄存器
#define INPUT_PORT1 0x01 // 输入端口1寄存器 0为低电平(仅支持读取)
#define OUTPUT_PORT0 0x02 // 输出端口0寄存器
#define OUTPUT_PORT1 0x03 // 输出端口1寄存器 0为低电平
#define CONFIG_PORT0 0x04 // 配置端口0寄存器
#define CONFIG_PORT1 0x05 // 配置端口1寄存器 0为输出模式
#define INT_PORT0 0x06 // 中断端口0寄存器
#define INT_PORT1 0x07 // 中断端口1寄存器 0为使能中断(根据手册默认是使能的)
#define CTL 0x11 // 全局控制寄存器
#define ID 0x23 // 芯片ID寄存器(0x23)
/***********************************IO扩展板电机引脚配置参数*************************************/
/* 设置步进电机方向 */
#define STEPPER_MOTOR_LEFT false //步进电机的方向为左转
#define STEPPER_MOTOR_RIGHT true //步进电机的方向为右转
#define STEPPER_MOTOR_DIR_PORT OUTPUT_PORT0 //步进电机方向引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_DIR_PIN 4 //步进电机方向引脚对应的IO扩展板引脚P0_4
/* 设置步进电机细分模式 */
#define STEPPER_MOTOR_M0_PIN 5 //步进电机细分模式选择引脚对应IO扩展板引脚P1_5
#define STEPPER_MOTOR_M1_PIN 4 //步进电机细分模式选择引脚对应IO扩展板引脚P1_4
#define STEPPER_MOTOR_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置步进电机睡眠引脚 */
#define STEPPER_MOTOR_SLEEP false //步进电机进入睡眠模式
#define STEPPER_MOTOR_WORK true //步进电机进入工作模式
#define STEPPER_MOTOR_SLEEP_PIN 6 //步进电机睡眠引脚对应IO扩展板引脚P1_6
#define STEPPER_MOTOR_SLEEP_PORT OUTPUT_PORT1 //步进电机睡眠引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置电机的使能引脚 */
#define STEPPER_MOTOR_ENABLE_PIN 7 //电机使能引脚对应IO扩展板引脚P0_7
#define STEPPER_MOTOR_ENABLE_PORT OUTPUT_PORT0 //电机使能引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_ENABLE_ENABLE true //电机使能引脚状态为高电平
#define STEPPER_MOTOR_ENABLE_DISABLE false //电机使能引脚状态为低电平
/* 设置毫米波雷达对应IO扩展板的中断引脚 */
#define RADAR_INTERRUPT_PIN 7 //毫米波雷达中断引脚对应IO扩展板引脚P1_7
#define RADAR_INTERRUPT_PORT CONFIG_PORT1 //毫米波雷达中断引脚挂载在IO扩展板的输入端口INPUT_PORT1
#define RADAR_INTERRUPT_ENABLE true //毫米波雷达中断引脚状态为输入状态
#define RADAR_INTERRUPT_DISABLE false //毫米波雷达中断引脚状态为输出状态
/* 任务配置常量 */
#define WRITE_BUF_SIZE 2 // I2C单次写入字节数
#define READ_BUF_SIZE 2 // I2C单次读取字节数
#define I2C_TASK_PRIORITY 2 // 任务优先级(中等)
#define I2C_TASK_STACK 4096 // 任务栈大小(4KB)
/***********************************IO扩展板LCD引脚配置参数*************************************/
/* 设置LCD的BL引脚 */
#define LCD_BL_PIN 6 //LCD背光引脚
#define LCD_BL_PORT OUTPUT_PORT0 //LCD背光引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define LCD_BL_HIGH true //LCD背光引脚状态为高电平
#define LCD_BL_LOW false //LCD背光引脚状态为低电平
/* 设置LCD的RST引脚 */
#define LCD_RST_PIN 1 //LCD复位引脚
#define LCD_RST_PORT OUTPUT_PORT1 //LCD复位引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_RST_HIGH true //LCD复位引脚状态为高电平
#define LCD_RST_LOW false //LCD复位引脚状态为低电平
/* 设置LCD的CS引脚 */
#define LCD_CS_PIN 2 //LCD片选引脚
#define LCD_CS_PORT OUTPUT_PORT1 //LCD片选引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_CS_HIGH true //LCD片选引脚状态为高电平
#define LCD_CS_LOW false //LCD片选引脚状态为低电平
#define TAG_GPIO_IO_EXPAND_DETECT "GPIO_IO_EXPAND_DETECT" // GPIO中断检测日志标签
#define TAG_I2C_IO_EXPAND_TASK "I2C_IO_EXPAND_TASK" // I2CIO扩展板任务日志标签
// GPIO引脚定义
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // IO扩展板中断检测引脚
#define IO_EXPAND_RSTN GPIO_NUM_45 // IO扩展板复位引脚
/**
* @class IOExpansionBoard
* @brief I/O扩展板驱动类,封装AW9523BTQR芯片操作
*/
class IOExpansionBoard
{
public:
/**
* @brief 构造函数
*/
IOExpansionBoard();
/**
* @brief 设置毫米波雷达指针
* @param radar 毫米波雷达对象指针
*/
void SetRadar(MillimeterWaveRadar* radar) { radar_ = radar; };
/**
* @brief 析构函数
*/
~IOExpansionBoard();
/**
* @brief 初始化GPIO中断检测
* @param gpio_num 中断引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetIoExpand();
/**
* @brief 初始化I2C主机通信
* @return esp_err_t 初始化结果
*/
// esp_err_t IoExpansionI2cInit();
/**
* @brief 创建I2C数据读取任务
*/
void CreateI2cReadTask();
/**
* @brief 启动I2C任务(整合初始化和任务创建)
* @return esp_err_t 操作结果
*/
esp_err_t StartTask();
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandWriteReg(uint8_t reg_addr, uint8_t data);
/**
* @brief 读寄存器数据
* @param reg_addr 寄存器地址
* @param data 要读出的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandReadReg(uint8_t reg_addr, uint8_t *data);
// /**
// * @brief 设置IO扩展板GPIO中断状态 通过读取Input_Port0 和 Input_Port1 寄存器可以清除中断
// * @param reg_addr 寄存器地址
// * @return esp_err_t 操作结果
// */
// esp_err_t SetInterruptStatus(bool status);
/**
* @brief 配置IO扩展版上的IO口为输出还是输入,并且设置初始为高电平
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandSetMode();
/**
* @brief 设置步进电机的细分模式
* @param mode 模式:1/2/4/8/16/32/64
* @return esp_err_t 操作结果
*/
void SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT, uint8_t m0_pin, uint8_t m1_pin);
/**
* @brief 设置步进电机的方向
* @param dir 方向
* @param OUTPUT_PORT 输出端口
* @param pin 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin);
/**
* @brief 设置步进电机的睡眠模式
* @param 是否进入睡眠模式
* @param 输出端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin);
/**
* @brief 设置步进电机的使能引脚状态
* @param 使能状态
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin);
/**
* @brief 设置LCD的BL引脚状态
* @param BL状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin);
/**
* @brief 设置LCD的RST引脚状态
* @param RST状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t rst_pin);
/**
* @brief 设置LCD的CS引脚状态
* @param CS状态(低电平为选中状态,高电平为不选中状态)
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdCsMode(bool mode, uint8_t OUTPUT_PORT, uint8_t cs_pin);
/**
* @brief 设置雷达中断引脚
* @param 毫米波雷达的中断引脚模式
* @param 输入端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin);
// 公共成员变量
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
uint8_t device_addr_; // I2C设备地址
private:
// 静态成员函数
MillimeterWaveRadar* radar_ = nullptr; // 毫米波雷达指针,用于触发回调
static void IRAM_ATTR GpioIsrHandler(void* arg); // 中断处理函数
static void I2cReadTask(void* pvParameters); // I2C数据读取任务函数
// /**
// * @brief 分配静态任务所需的内存
// * @return bool 分配是否成功
// */
// bool AllocateStaticTaskMemory();
// /**
// * @brief 释放静态任务分配的内存
// */
// void FreeStaticTaskMemory();
// 实例成员变量
EventGroupHandle_t i2c_task_event_group_ = nullptr; // 事件组
TaskHandle_t xI2CReadTaskHandle_ = nullptr; // 任务句柄
bool is_initialized_ = false; // 初始化状态标记(新增)
gpio_num_t interrupt_gpio_ = GPIO_NUM_NC; // 中断引脚
gpio_num_t reset_gpio_ = GPIO_NUM_NC; // 复位引脚
// StackType_t* pI2CReadTaskStackBuffer = nullptr; // 任务栈指针(PSRAM)
// StaticTask_t* pxI2CReadTaskBuffer = nullptr; // 任务控制块指针(内部RAM)
};
#endif
@@ -0,0 +1,257 @@
/**
* @file board.cc
* @brief 板级外设初始化类,包含步进电机、毫米波雷达、I2C总线等外设的初始化函数。
* @author zouyuhua
* @date 2025年8月7日
*
*/
extern "C" {
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/uart_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2c_bus.h"
}
#include "board_sweekar.h"
#include "config.h"
#define TAG "board_sweekar"
/**
* @file board.h
* @brief 板级外设初始化类
*/
/**
* @brief 步进电机初始化函数
*
* 配置LEDC定时器和通道用于PWM输出,设置步进电机的工作模式、使能状态、细分模式和方向,
* 并初始化PWM占空比为50%。初始化过程中若出现错误,会通过ESP_LOGE输出错误信息及错误码。
*/
void BoardSweekar::StepperMotorInit(void)
{
// 配置LEDC用于PWM输出
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_MODE,
.duty_resolution = LEDC_DUTY_RES,
.timer_num = LEDC_TIMER,
.freq_hz = LEDC_FREQ_HZ,
.clk_cfg = LEDC_USE_APB_CLK,
};
esp_err_t err = ledc_timer_config(&ledc_timer);
if (err != ESP_OK){
ESP_LOGE(TAG_BOARD, "步进电机LEDC定时器配置失败,错误码: 0x%x", err);
}
// 配置LEDC通道
ledc_channel_config_t ledc_channel = {
.gpio_num = LEDC_OUTPUT_IO,
.speed_mode = LEDC_MODE,
.channel = LEDC_CHANNEL,
.timer_sel = LEDC_TIMER,
.duty = 0, // 初始占空比0
.hpoint = 0
};
err = ledc_channel_config(&ledc_channel);
if (err != ESP_OK){
ESP_LOGE(TAG_BOARD, "步进电机LEDC通道配置失败,错误码: 0x%x", err);
}
// 检查I2C总线是否已初始化
if (bus_handle_ == nullptr) {
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 设置步进电机工作模式,使能电机,设置步进电机细分模式,设置步进电机方向
err = io_expansion_.SetMotorSleepMode(STEPPER_MOTOR_WORK, STEPPER_MOTOR_SLEEP_PORT, STEPPER_MOTOR_WORK);
if (err != ESP_OK){
ESP_LOGE(TAG_BOARD, "步进电机初始化失败,错误码: 0x%x", err);
}
err = io_expansion_.SetMotorEnableMode(STEPPER_MOTOR_ENABLE_DISABLE, STEPPER_MOTOR_ENABLE_PORT, STEPPER_MOTOR_ENABLE_PIN);
if (err != ESP_OK){
ESP_LOGE(TAG_BOARD, "步进电机初始化失败,错误码: 0x%x", err);
}
io_expansion_.SetStepperMicrostepMode(4, STEPPER_MOTOR_MADE_PORT, STEPPER_MOTOR_M0_PIN, STEPPER_MOTOR_M1_PIN);
err = io_expansion_.StepperMotorSetDir(STEPPER_MOTOR_LEFT, STEPPER_MOTOR_DIR_PORT, STEPPER_MOTOR_DIR_PIN);
if (err != ESP_OK){
ESP_LOGE(TAG_BOARD, "步进电机初始化失败,错误码: 0x%x", err);
}
// 初始PWM占空比设为0%
ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0);
ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);
}
/**
* @brief 毫米波雷达串口初始化函数
*
* 配置毫米波雷达所使用的串口参数(波特率、数据位、校验位、停止位等),设置串口引脚,
* 并安装串口驱动。采用较大的RX/TX FIFO深度以处理突发数据,禁用驱动自带事件队列以避免
* 与自定义中断冲突。
*/
void BoardSweekar::MillimeterWaveRadarUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = RADAR_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,
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(RADAR_UART_NUM, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(RADAR_UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动(禁用驱动自带事件队列,避免与自定义中断冲突)
// 增加 RX/TX FIFO 深度以处理突发数据
ESP_ERROR_CHECK(uart_driver_install(RADAR_UART_NUM, RADAR_BUF_SIZE * 4, RADAR_BUF_SIZE * 4,
0, NULL, 0));
}
/**
* @brief 毫米波雷达中断引脚初始化函数
*
* 配置毫米波雷达中断引脚为输入模式,禁用上拉电阻,使能下拉电阻,初始禁用中断功能
* (如需使用中断需单独进行配置)。
*/
void BoardSweekar::MillimeterWaveRadarGpioInit(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << MILLIMETER_WAVE_RADAR_PIN), // 选择GPIO引脚
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE, // 使能下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断(如需中断需单独配置)
};
// 配置GPIO
gpio_config(&io_conf);
}
/**
* @brief I2C总线及从机设备初始化函数
*
* 首先配置并创建I2C主机总线,然后依次添加IO扩展板和线性马达作为I2C从机设备到总线上。
* 若总线创建或设备添加失败,会输出错误信息并进行相应的资源清理(删除已创建的总线)。
* 初始化成功后,会通过ESP_LOGI输出IO扩展板和线性马达的从机地址信息。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示相应的失败原因
*/
esp_err_t BoardSweekar::I2CMasterInit(void)
{
static const char *I2C_INIT = "I2C_INIT";
// 初始化IO扩展板复位引脚
gpio_config_t reset_io_conf = {
.pin_bit_mask = (1ULL << IO_EXPAND_RSTN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&reset_io_conf);
// 执行复位时序:先拉低100ms,再拉高并等待100ms
gpio_set_level(IO_EXPAND_RSTN, 0); // 拉低复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms
gpio_set_level(IO_EXPAND_RSTN, 1); // 释放复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
ESP_LOGI(I2C_INIT, "IO扩展板复位完成");
// 创建统一的I2C总线(使用ESP-ADF的i2c_bus)
i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C0_MASTER_SDA_IO,
.scl_io_num = I2C0_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = 100000,
},
};
// 创建I2C总线(ESP-ADF的audio_board_init会复用这个总线)
bus_handle_ = i2c_bus_create(I2C0_MASTER_PORT, &i2c_cfg);
if (bus_handle_ == NULL) {
ESP_LOGE(TAG, "Failed to create I2C bus");
return ESP_FAIL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
// ESP-ADF的i2c_bus不需要单独添加设备,直接通过总线句柄和设备地址进行通信
// 验证线性马达设备是否存在
esp_err_t err = i2c_bus_probe_addr(bus_handle_, LINEAR_MOTOR_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "线性马达设备未找到(地址:0x%02X),错误码: 0x%x", LINEAR_MOTOR_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "线性马达设备检测成功(地址:0x%02X)", LINEAR_MOTOR_I2C_SLAVE_ADDR);
}
// 验证IO扩展板设备是否存在
err = i2c_bus_probe_addr(bus_handle_, I2C_IO_EXPAND_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "IO扩展板设备未找到(地址:0x%02X),错误码: 0x%x", I2C_IO_EXPAND_SLAVE_ADDR, err);
// 不返回错误,继续初始化其他设备
} else {
ESP_LOGI(I2C_INIT, "IO扩展板设备检测成功(地址:0x%02X)", I2C_IO_EXPAND_SLAVE_ADDR);
}
// 验证陀螺仪设备是否存在
err = i2c_bus_probe_addr(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "陀螺仪设备未找到(地址:0x%02X),错误码: 0x%x", GYROSCOPE_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "陀螺仪设备检测成功(地址:0x%02X)", GYROSCOPE_I2C_SLAVE_ADDR);
}
// 为IO扩展板设置总线句柄和设备地址(使用7位地址)
io_expansion_.bus_handle_ = bus_handle_;
io_expansion_.device_addr_ = I2C_IO_EXPAND_SLAVE_ADDR << 1;
// 为线性马达设置总线句柄和设备地址(使用7位地址)
linear_motor_.bus_handle_ = bus_handle_;
linear_motor_.device_addr_ = LINEAR_MOTOR_I2C_SLAVE_ADDR << 1;
ESP_LOGI(I2C_INIT, "I2C总线初始化完成");
return ESP_OK;
}
/**
* @brief 开启VBAT to VBAT_5V
*
* 创建开启升压的电源管理单元
*
*/
void BoardSweekar::VbatToVbat5vInit(void)
{
// 配置VBAT_5V引脚为输出模式
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << VBAT_5V_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 使能VBAT_5V
gpio_set_level(VBAT_5V_PIN, 1);
}
@@ -0,0 +1,38 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include "config.h"
#include "io_expansion_board.h"
#include "stepper_motor.h"
#include "i2c_bus.h"
#include "gyroscope.h"
#include "touch_sensor.h"
class BoardSweekar
{
private:
i2c_bus_handle_t bus_handle_ = nullptr; // 使用ESP-ADF的I2C总线句柄
public:
IOExpansionBoard io_expansion_; // IO扩展板对象
LinearMotor linear_motor_; // 线性马达对象
Gyroscope gyroscope_; // 陀螺仪对象
TouchSensor touch_sensor_; // 触摸传感器对象
// 初始化步进电机
void StepperMotorInit(void);
// 初始化串口
void MillimeterWaveRadarUartInit(void);
//初始化毫米波雷达的中断引脚
void MillimeterWaveRadarGpioInit(void);
// 初始化I2C总线
esp_err_t I2CMasterInit(void);
// 初始化VBAT toVBAT_5V引脚
void VbatToVbat5vInit(void);
// 获取I2C总线句柄
i2c_bus_handle_t GetBusHandle() const { return bus_handle_; }
};
#endif
@@ -0,0 +1,139 @@
/* 定义引脚分配 */
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
/**
* @file config.h
* @brief 定义了引脚的分配。
* @author zouyuhua
* @date 2025年8月7日
*
*/
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_rom_sys.h" // 用于 esp_rom_delay_us
#include "esp_timer.h"
#include "esp_mac.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/timer.h"
#include "esp_intr_alloc.h"
#include "soc/timer_group_struct.h" // 包含TIMERG0、TIMERG1的结构体定义
#include "soc/timer_group_reg.h" // 包含定时器相关寄存器宏定义
#include "driver/uart.h"
#include "esp_err.h"
#include "freertos/queue.h"
#include "soc/uart_reg.h"
#include "linear_motor.h"
#include "gyroscope.h"
#define TAG_BOARD "SWEEKAR_ESP32"
/*I2C总线引脚定义*/
#define I2C0_MASTER_PORT I2C_NUM_0
#define I2C0_MASTER_SDA_IO GPIO_NUM_2
#define I2C0_MASTER_SCL_IO GPIO_NUM_3
/* 步进电机引脚设置 */
#define LEDC_OUTPUT_IO GPIO_NUM_42 // 步进电机LEDC引脚输入
/* 定义屏幕驱动控制引脚 */
#define ST7789_PIN_CLK GPIO_NUM_48 // SPI时钟引脚
#define ST7789_PIN_DC GPIO_NUM_38 // 数据/命令选择引脚RSEL
#define ST7789_PIN_MOSI GPIO_NUM_47 // SPI数据输出引脚
#define ST7789_PIN_CS GPIO_NUM_46 // SPI片选引脚
#define ST7789_PIN_BL GPIO_NUM_NC // 背光引脚
#define ST7789_PIN_RST GPIO_NUM_NC // 复位引脚
#define LCD_BL_ACTIVE_LEVEL 0 // 背光有效电平:1=高电平有效(NMOS),0=低电平有效(PMOS)
#define LCD_WIDTH 172 // 屏幕宽度(水平方向像素数)
#define LCD_HEIGHT 320 // 屏幕高度(垂直方向像素数)
/* 毫米波雷达配置参数 */
/* 定义毫米波雷达的中断引脚 */
#define MILLIMETER_WAVE_RADAR_PIN GPIO_NUM_35
// UART 配置参数
#define RADAR_UART_NUM UART_NUM_1 // 使用 UART1
#define UART_TX_PIN GPIO_NUM_40 // GPIO43 作为 TX(根据实际硬件连接调整)
#define UART_RX_PIN GPIO_NUM_39 // GPIO44 作为 RX
#define RADAR_BAUD_RATE 9600 // 雷达波特率
#define RADAR_BUF_SIZE 100 // 雷达接收缓冲区大小
/* IO扩展硬件配置常量 */
#define I2C_MASTER_FREQ_HZ 100000 // I2C通信频率(100kHz)(可更改至400000)
#define I2C_IO_EXPAND_SLAVE_ADDR 0x5B // AW9523BTQR器件地址
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // 中断检测引脚
#define ESP_INTR_FLAG_DEFAULT 0 // 中断标志默认值
#define IO_EXPAND_RSTN GPIO_NUM_45 // IO扩展板复位引脚
/* 定义线性马达的I2C从机配置常量 */
#define LINEAR_MOTOR_I2C_SLAVE_ADDR 0x5A // 线性马达的I2C从机地址
/* 定义陀螺仪的I2C从机配置常量 */
#define GYROSCOPE_I2C_SLAVE_ADDR 0x6A // 陀螺仪的I2C从机地址
#define GYROSCOPE_INPUT_PIN_SEL GPIO_NUM_21 // 陀螺仪中断引脚
/* 原生触摸引脚配置 */
// 触摸按键数量配置
// #define TOUCH_BUTTON_NUM 4
#define TOUCH_BUTTON_NUM 3
// 触摸通道定义
// #define TOUCH_PAD_CHANNEL_1 TOUCH_PAD_NUM11 // GPIO11
#define TOUCH_PAD_CHANNEL_2 TOUCH_PAD_NUM12 // GPIO12
#define TOUCH_PAD_CHANNEL_3 TOUCH_PAD_NUM13 // GPIO13
#define TOUCH_PAD_CHANNEL_4 TOUCH_PAD_NUM14 // GPIO14
/* ES8311音频编解码器配置 (ESP-ADF兼容) */
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
/* I2S GPIO定义 */
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_15
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
/* PA引脚定义 */
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
/* I2C配置 */
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_2
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_3
#define AUDIO_CODEC_ES8311_ADDR 0x18
#define AUDIO_CODEC_ES7210_ADDR 0x40
/* WS2812 RGB LED配置 */
#define WS2812_PIN GPIO_NUM_41 // WS2812数据引脚
#define WS2812_NUM_LEDS 2 // WS2812 LED数量
/* 创建VBAT to VBAT_5V */
#define VBAT_5V_PIN GPIO_NUM_NC // VBAT_5V引脚
/* 定义按键的中断引脚 */
#define BUTTON_PIN GPIO_NUM_0 // 按键中断引脚
#define RIGHT_BUTTON_PIN GPIO_NUM_8 // 按键中断引脚
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,724 @@
#include "io_expansion_board.h"
#include "millimeter_wave_radar.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/idf_additions.h"
#include "freertos/portable.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_rom_sys.h"
/**
* @file io_expansion_board.cc
* @brief IO扩展板功能实现类,包含I2C通信、GPIO中断配置、外设控制等功能
* @details 实现了IO扩展板的初始化(中断引脚、复位引脚)、寄存器读写、步进电机控制(细分模式、使能、方向、睡眠状态)、
* LCD相关引脚控制(背光、复位、片选)、毫米波雷达中断引脚配置等功能,并提供I2C数据读取任务的创建与管理
* @author zouyuhua
* @date 2025年8月7日
*
*/
/**
* @brief 构造函数
*/
IOExpansionBoard::IOExpansionBoard()
: bus_handle_(nullptr),
device_addr_(0),
i2c_task_event_group_(nullptr),
xI2CReadTaskHandle_(nullptr)
{
}
/**
* @brief 析构函数
*/
IOExpansionBoard::~IOExpansionBoard()
{
// 删除任务
if (xI2CReadTaskHandle_) {
vTaskDelete(xI2CReadTaskHandle_);
}
// if(pI2CReadTaskStackBuffer) {
// heap_caps_free(pI2CReadTaskStackBuffer);
// pI2CReadTaskStackBuffer = nullptr;
// }
// if(pxI2CReadTaskBuffer) {
// vPortFree(pxI2CReadTaskBuffer);
// pxI2CReadTaskBuffer = nullptr;
// }
// 删除事件组
if (i2c_task_event_group_) {
vEventGroupDelete(i2c_task_event_group_);
}
// 移除中断处理
if (interrupt_gpio_ != GPIO_NUM_NC) {
gpio_isr_handler_remove(interrupt_gpio_);
}
// 删除IO扩展板设备
// 使用i2c_bus API时不需要手动删除设备句柄
// bus_handle_由board_sweekar统一管理
}
/**
* @brief 静态中断处理函数
*/
void IRAM_ATTR IOExpansionBoard::GpioIsrHandler(void* arg)
{
IOExpansionBoard* instance = static_cast<IOExpansionBoard*>(arg);
if (!instance) return;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// 设置事件标志位
if (instance->i2c_task_event_group_) {
xEventGroupSetBitsFromISR(
instance->i2c_task_event_group_,
BIT0,
&xHigherPriorityTaskWoken
);
}
// // 唤醒挂起的I2C任务
// if (instance->xI2CReadTaskHandle_) {
// if (xTaskResumeFromISR(instance->xI2CReadTaskHandle_) == pdTRUE) {
// xHigherPriorityTaskWoken = pdTRUE;
// }
// }
// 触发上下文切换
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
/**
* @brief 初始化GPIO中断检测
*/
esp_err_t IOExpansionBoard::InterruptGpioInit(gpio_num_t gpio_num)
{
// 配置IO扩展板中断引脚
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.pin_bit_mask = 1ULL << gpio_num;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
esp_err_t err = gpio_config(&io_conf);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO配置失败,错误码: 0x%x", err);
return err;
}
// 配置中断服务
err = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO中断服务安装失败,错误码: 0x%x", err);
return err;
}
// 注册中断处理函数
err = gpio_isr_handler_add(gpio_num, GpioIsrHandler, this);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO中断处理函数添加失败,错误码: 0x%x", err);
return err;
}
// 创建事件组
i2c_task_event_group_ = xEventGroupCreate();
if (!i2c_task_event_group_) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "事件组创建失败");
return ESP_FAIL;
}
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "GPIO%d中断初始化完成", gpio_num);
return ESP_OK;
}
/**
* @brief 初始化硬件复位引脚
* 设置为上拉,需要复位,需要输出低电平为20us的脉冲信号
*/
esp_err_t IOExpansionBoard::ResetGpioInit(gpio_num_t gpio_num)
{
reset_gpio_ = gpio_num;
// 配置复位引脚
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = 1ULL << gpio_num;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
esp_err_t err = gpio_config(&io_conf);
gpio_set_level(gpio_num, 1); // 复位引脚拉高
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO配置失败,错误码: 0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "GPIO%d复位引脚初始化完成", gpio_num);
return err;
}
}
/**
* @brief 复位IO扩展板
*/
esp_err_t IOExpansionBoard::ResetIoExpand()
{
if (reset_gpio_ == GPIO_NUM_NC) {
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "复位引脚未初始化");
return ESP_ERR_INVALID_STATE;
}
// 复位IO扩展板
gpio_set_level(reset_gpio_, 0);
esp_rom_delay_us(30);
gpio_set_level(reset_gpio_, 1);
uint8_t ctl_val;
//配置P0_0-P0_7的端口为推挽输出模式
esp_err_t err = IoexpandReadReg(CTL, &ctl_val);
ctl_val |= (1 << 4); // 设置D[4]位为1(GPOMD=1),切换P0为推挽模式
err = IoexpandWriteReg(CTL, ctl_val);
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "复位失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "复位成功");
}
return ESP_OK;
}
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IOExpansionBoard::IoexpandWriteReg(uint8_t reg_addr, uint8_t data)
{
return i2c_bus_write_bytes(bus_handle_, device_addr_, &reg_addr, 1, &data, 1);
}
/**
* @brief 从寄存器读取数据
* @param reg_addr 寄存器地址(如INPUT_PORT0、ID等)
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果(ESP_OK为成功)
*/
esp_err_t IOExpansionBoard::IoexpandReadReg(uint8_t reg_addr, uint8_t* data)
{
if (data == nullptr){ // 检查输入参数有效性
return ESP_ERR_INVALID_ARG;
}
// 使用i2c_bus_read_bytes进行寄存器读取
esp_err_t err = i2c_bus_read_bytes(bus_handle_, device_addr_, &reg_addr, 1, data, 1);
if (err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "读取寄存器失败,错误码:0x%x", err);
return err;
}
return ESP_OK;
}
//设置IO扩展板为输入模式或是输出模式
/*
* 这里需要根据实际情况设置IO扩展板的模式,比如输入模式还是输出模式,以及各个引脚的配置。
* P0_0 对应NFC_RST引脚,需要设置为输出模式,低电平有效。(待测)
* P0_1 对应USB_VBUS引脚,需要设置为输出模式,低电平有效。(待测)
* P0_2 对应BTN_RIGHT_EAR引脚,需要设置为输出模式,低电平有效。(待测)
* P0_3 暂时未使用。
* P0_4 对应步进电机方向引脚,需要设置为输出模式,根据需求设置。(待测)
* P0_5 对应CHRG引脚(待测)
* P0_6 LCD_DC引脚,需要设置为输出模式,根据需求设置电平。(待测)
* P0_7 对应步进电机的使能引脚,需要设置为输出模式,根据需求设置。(待测)
*
*
* P1_0 对应EG800K_PWR。(待测)
* P1_1 对应LCD_RST,需要设置为输出模式,低电平有效。(待测)
* P1_2 对应LCD_CS,需要设置为输出模式,低电平有效。(待测)
* P1_3 暂时未使用。
* P1_4 对应步进电机的细分模式M1引脚。(待测)
* P1_5 对应步进电机的细分模式M0引脚。(待测)
* P1_6 对应步进电机的睡眠引脚。(待测)
* P1_7 对应RADAR_O雷达的输出引脚。(待测)
* */
esp_err_t IOExpansionBoard::IoexpandSetMode(void)
{
uint8_t ctl_val;
//配置P0_0-P0_7的端口为推挽输出模式
esp_err_t err = IoexpandReadReg(CTL, &ctl_val);
ctl_val |= (1 << 4); // 设置D[4]位为1(GPOMD=1),切换P0为推挽模式
err = IoexpandWriteReg(CTL, ctl_val);
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P0端口失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P0端口成功");
}
// 配置P0_0-P0_7的端口为输入还是输出:0-输出;1-输入
// err = IoexpandWriteReg(CONFIG_PORT0, 0x04); // 0000 0100
err = IoexpandWriteReg(CONFIG_PORT0, 0x24); // 0010 0100
// err = IoexpandWriteReg(CONFIG_PORT0, 0x2C); // 0010 1100 0-2,0-3,0-5都是输入
// D 1101
// 9 1001
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P0端口失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P0端口成功");
}
// 配置P1_0-P1_7的端口输出:0-输出;1-输入
// err = IoexpandWriteReg(CONFIG_PORT1, 0x84);
err = IoexpandWriteReg(CONFIG_PORT1, 0x80); // 1000 0000
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P1端口失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P1端口成功");
}
// 配置P0_0-P0_7的初始为高电平
// 配置P1_0-P1_7的初始为高电平
err = IoexpandWriteReg(OUTPUT_PORT0, 0xFD); // 1111 1101
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P0端口初始值失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P0端口初始值成功");
}
err = IoexpandWriteReg(OUTPUT_PORT1, 0x7E); // 0111 1110
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P1端口初始值失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P1端口初始值成功");
}
err = IoexpandWriteReg(INT_PORT1, 0x7F); // 0111 1111
if(err != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "配置P1端口中断使能失败,错误码:0x%x", err);
return err;
}
else{
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "配置P1端口中断使能成功");
}
return ESP_OK;
}
/**
* @brief 设置步进电机的细分模式
*/
void IOExpansionBoard::SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT, uint8_t m0_pin, uint8_t m1_pin)
{
uint8_t current_val;
switch (mode) {
case 1: //全步输出模式 低低电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
case 2: //非圆 1/2 高低电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
case 3: //1/4 步 低高电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
case 4: //1/8 步 高高电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
default: //全步输入模式 低低电平
/* code */
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m0_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << m1_pin);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
if(IoexpandWriteReg(OUTPUT_PORT, current_val) != ESP_OK){
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "设置步进电机细分模式失败");
}
break;
}
}
/**
* @brief 设置步进电机的使能
*/
esp_err_t IOExpansionBoard::SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin)
{
uint8_t current_val;
if(enable){
// 进入工作状态mode
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << enable_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入睡眠状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << enable_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief 设置步进电机运行方向
*/
esp_err_t IOExpansionBoard::StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin)
{
uint8_t current_val;
if(dir){
// 方向为正转
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief 设置步进电机是否进入睡眠或者工作状态(高电平进入工作状态,低电平进入睡眠状态)
*/
esp_err_t IOExpansionBoard::SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin)
{
uint8_t current_val;
if(mode){
// 进入工作状态mode
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << nsleep_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入睡眠状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << nsleep_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/*********************************************IO扩展板LCD引脚配置参数***********************************************/
/**
* @brief 设置 LCD_BL引脚电平(高电平为命令模式,低电平为数据模式)
*/
esp_err_t IOExpansionBoard::SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin)
{
uint8_t current_val;
if(mode){
// 进入命令模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << bl_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入数据模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << bl_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief 设置 LCD_RST引脚电平(高电平为复位模式,低电平为工作模式)
*/
esp_err_t IOExpansionBoard::SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t reset_pin)
{
uint8_t current_val;
if(mode){
// 进入复位模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << reset_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入工作模式
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << reset_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/*********************************************IO扩展板的输入引脚配置以及中断引脚定义***********************************************/
/**
* @brief 设置 毫米波雷达的中断引脚
*/
esp_err_t IOExpansionBoard::SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin)
{
uint8_t current_val;
if(mode){
// 进入输入模式
IoexpandReadReg(INPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << interrupt_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(INPUT_PORT, current_val);
}
else{
// 进入输出模式
IoexpandReadReg(INPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << interrupt_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(INPUT_PORT, current_val);
}
}
/**
* @brief 设置 LCD_CS引脚电平(低电平为选中状态,高电平为不选中状态)
*/
esp_err_t IOExpansionBoard::SetLcdCsMode(bool mode, uint8_t OUTPUT_PORT, uint8_t cs_pin)
{
uint8_t current_val;
if(mode){
// 进入选中状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val &= ~(1 << cs_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
else{
// 进入不选中状态
IoexpandReadReg(OUTPUT_PORT, &current_val);
ESP_LOGW(TAG_I2C_IO_EXPAND_TASK, "0x%x", current_val);
current_val |= (1 << cs_pin);
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "!!!!!!!!!0x%x", current_val);
return IoexpandWriteReg(OUTPUT_PORT, current_val);
}
}
/**
* @brief I2C数据读取任务主体
*/
void IOExpansionBoard::I2cReadTask(void* pvParameters)
{
IOExpansionBoard* instance = static_cast<IOExpansionBoard*>(pvParameters);
if (!instance) {
vTaskDelete(nullptr);
return;
}
// 初始化复位引脚
esp_err_t err = instance->ResetGpioInit(IO_EXPAND_RSTN);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO复位初始化失败");
}
// 初始化IO扩展板子的中断引脚
err = instance->InterruptGpioInit(GPIO_INPUT_PIN_SEL);
if (err != ESP_OK) {
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "GPIO中断初始化失败");
}
// 设置IO扩展板模式
instance->IoexpandSetMode();
// 设置LCD屏幕复位引脚
err = instance->SetLcdResetMode(LCD_RST_HIGH, LCD_RST_PORT, LCD_RST_PIN);
if(err != ESP_OK){
ESP_LOGE(TAG_GPIO_IO_EXPAND_DETECT, "设置LCD屏幕复位引脚失败");
}
else{
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "设置LCD屏幕复位引脚成功");
}
instance->IoexpandWriteReg(OUTPUT_PORT0, 0xff);
uint8_t current_val;
for (;;) {
// 等待中断事件触发
if (instance->i2c_task_event_group_) {
xEventGroupWaitBits(instance->i2c_task_event_group_, BIT0, pdTRUE, pdFALSE, portMAX_DELAY);
}
// 读取I2C数据
instance->IoexpandReadReg(INPUT_PORT1,&current_val);
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "读取数据: 0x%0X", current_val);
if(current_val & (1 << 7)){
ESP_LOGI(TAG_GPIO_IO_EXPAND_DETECT, "检测到中断来自雷达引脚(P1_7)\n");
// 调用毫米波雷达回调函数
if (instance->radar_) {
instance->radar_->TriggerRadarDetectCallback();
}
}
// vTaskSuspend(nullptr); // 挂起任务等待下次唤醒
}
}
// bool IOExpansionBoard::AllocateStaticTaskMemory()
// {
// printf("IO拓展芯片创建任务前:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
// printf("IO拓展芯片创建任务前 Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
// // 分配任务栈(PSRAM)
// const size_t TASK_STACK_SIZE = 4096;
// pI2CReadTaskStackBuffer = (StackType_t*)heap_caps_malloc(
// TASK_STACK_SIZE * sizeof(StackType_t),
// MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT
// );
// if (!pI2CReadTaskStackBuffer) {
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "Failed to allocate task stack from PSRAM");
// return false;
// }
// // 分配任务控制块(内部RAM)
// pxI2CReadTaskBuffer = (StaticTask_t*)pvPortMalloc(sizeof(StaticTask_t));
// if (!pxI2CReadTaskBuffer) {
// ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "Failed to allocate task TCB from internal RAM");
// heap_caps_free(pI2CReadTaskStackBuffer);
// pI2CReadTaskStackBuffer = nullptr;
// return false;
// }
// ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "Static task memory allocated successfully");
// printf("IO拓展芯片创建任务后:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
// printf("IO拓展芯片创建任务后 Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
// return true;
// }
// void IOExpansionBoard::FreeStaticTaskMemory()
// {
// if (pI2CReadTaskStackBuffer != nullptr) {
// heap_caps_free(pI2CReadTaskStackBuffer);
// pI2CReadTaskStackBuffer = nullptr;
// }
// if (pxI2CReadTaskBuffer != nullptr) {
// vPortFree(pxI2CReadTaskBuffer);
// pxI2CReadTaskBuffer = nullptr;
// }
// ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "Static task memory freed");
// }
/**
* @brief 创建I2C数据读取任务
*/
void IOExpansionBoard::CreateI2cReadTask()
{
printf("创建I2C读取任务之前:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
printf("创建I2C读取任务之前:PSRAM Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
if (xI2CReadTaskHandle_) {
return; // 任务已创建
}
xTaskCreateWithCaps(
I2cReadTask,
"i2c_read_task",
I2C_TASK_STACK,
this,
I2C_TASK_PRIORITY,
&xI2CReadTaskHandle_,
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT
);
if (xI2CReadTaskHandle_ == NULL) {
ESP_LOGE(TAG_I2C_IO_EXPAND_TASK, "Failed to create static I2C read task");
} else {
ESP_LOGI(TAG_I2C_IO_EXPAND_TASK, "Static I2C read task created successfully");
}
printf("创建I2C读取任务之后:Internal Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
printf("创建I2C读取任务之后:PSRAM Free: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
/**
* @brief 启动I2C任务 (整合初始化和任务创建)
*/
esp_err_t IOExpansionBoard::StartTask()
{
// 再创建任务
if (!xI2CReadTaskHandle_) {
CreateI2cReadTask();
}
return ESP_OK;
}
@@ -0,0 +1,284 @@
#ifndef IO_EXPANSION_BOARD_H
#define IO_EXPANSION_BOARD_H
#include <cstdint>
#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 "portmacro.h"
// 前向声明,避免循环引用
class MillimeterWaveRadar;
/* 寄存器地址 */
#define INPUT_PORT0 0x00 // 输入端口0寄存器
#define INPUT_PORT1 0x01 // 输入端口1寄存器 0为低电平(仅支持读取)
#define OUTPUT_PORT0 0x02 // 输出端口0寄存器
#define OUTPUT_PORT1 0x03 // 输出端口1寄存器 0为低电平
#define CONFIG_PORT0 0x04 // 配置端口0寄存器
#define CONFIG_PORT1 0x05 // 配置端口1寄存器 0为输出模式
#define INT_PORT0 0x06 // 中断端口0寄存器
#define INT_PORT1 0x07 // 中断端口1寄存器 0为使能中断(根据手册默认是使能的)
#define CTL 0x11 // 全局控制寄存器
#define ID 0x23 // 芯片ID寄存器(0x23)
/***********************************IO扩展板电机引脚配置参数*************************************/
/* 设置步进电机方向 */
#define STEPPER_MOTOR_LEFT false //步进电机的方向为左转
#define STEPPER_MOTOR_RIGHT true //步进电机的方向为右转
#define STEPPER_MOTOR_DIR_PORT OUTPUT_PORT0 //步进电机方向引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_DIR_PIN 4 //步进电机方向引脚对应的IO扩展板引脚P0_4
/* 设置步进电机细分模式 */
#define STEPPER_MOTOR_M0_PIN 5 //步进电机细分模式选择引脚对应IO扩展板引脚P1_5
#define STEPPER_MOTOR_M1_PIN 4 //步进电机细分模式选择引脚对应IO扩展板引脚P1_4
#define STEPPER_MOTOR_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置步进电机睡眠引脚 */
#define STEPPER_MOTOR_SLEEP false //步进电机进入睡眠模式
#define STEPPER_MOTOR_WORK true //步进电机进入工作模式
#define STEPPER_MOTOR_SLEEP_PIN 6 //步进电机睡眠引脚对应IO扩展板引脚P1_6
#define STEPPER_MOTOR_SLEEP_PORT OUTPUT_PORT1 //步进电机睡眠引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置电机的使能引脚 */
#define STEPPER_MOTOR_ENABLE_PIN 7 //电机使能引脚对应IO扩展板引脚P0_7
#define STEPPER_MOTOR_ENABLE_PORT OUTPUT_PORT0 //电机使能引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_ENABLE_ENABLE true //电机使能引脚状态为高电平
#define STEPPER_MOTOR_ENABLE_DISABLE false //电机使能引脚状态为低电平
/* 设置毫米波雷达对应IO扩展板的中断引脚 */
#define RADAR_INTERRUPT_PIN 7 //毫米波雷达中断引脚对应IO扩展板引脚P1_7
#define RADAR_INTERRUPT_PORT CONFIG_PORT1 //毫米波雷达中断引脚挂载在IO扩展板的输入端口INPUT_PORT1
#define RADAR_INTERRUPT_ENABLE true //毫米波雷达中断引脚状态为输入状态
#define RADAR_INTERRUPT_DISABLE false //毫米波雷达中断引脚状态为输出状态
/* 任务配置常量 */
#define WRITE_BUF_SIZE 2 // I2C单次写入字节数
#define READ_BUF_SIZE 2 // I2C单次读取字节数
#define I2C_TASK_PRIORITY 2 // 任务优先级(中等)
#define I2C_TASK_STACK 4096 // 任务栈大小(4KB)
/***********************************IO扩展板LCD引脚配置参数*************************************/
/* 设置LCD的BL引脚 */
#define LCD_BL_PIN 6 //LCD背光引脚
#define LCD_BL_PORT OUTPUT_PORT0 //LCD背光引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define LCD_BL_HIGH true //LCD背光引脚状态为高电平
#define LCD_BL_LOW false //LCD背光引脚状态为低电平
/* 设置LCD的RST引脚 */
#define LCD_RST_PIN 1 //LCD复位引脚
#define LCD_RST_PORT OUTPUT_PORT1 //LCD复位引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_RST_HIGH true //LCD复位引脚状态为高电平
#define LCD_RST_LOW false //LCD复位引脚状态为低电平
/* 设置LCD的CS引脚 */
#define LCD_CS_PIN 2 //LCD片选引脚
#define LCD_CS_PORT OUTPUT_PORT1 //LCD片选引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_CS_HIGH true //LCD片选引脚状态为高电平
#define LCD_CS_LOW false //LCD片选引脚状态为低电平
#define TAG_GPIO_IO_EXPAND_DETECT "GPIO_IO_EXPAND_DETECT" // GPIO中断检测日志标签
#define TAG_I2C_IO_EXPAND_TASK "I2C_IO_EXPAND_TASK" // I2CIO扩展板任务日志标签
// GPIO引脚定义
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // IO扩展板中断检测引脚
#define IO_EXPAND_RSTN GPIO_NUM_45 // IO扩展板复位引脚
/**
* @class IOExpansionBoard
* @brief I/O扩展板驱动类,封装AW9523BTQR芯片操作
*/
class IOExpansionBoard
{
public:
/**
* @brief 构造函数
*/
IOExpansionBoard();
/**
* @brief 设置毫米波雷达指针
* @param radar 毫米波雷达对象指针
*/
void setRadar(MillimeterWaveRadar* radar) { radar_ = radar; };
/**
* @brief 析构函数
*/
~IOExpansionBoard();
/**
* @brief 初始化GPIO中断检测
* @param gpio_num 中断引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetIoExpand();
/**
* @brief 初始化I2C主机通信
* @return esp_err_t 初始化结果
*/
// esp_err_t IoExpansionI2cInit();
/**
* @brief 创建I2C数据读取任务
*/
void CreateI2cReadTask();
/**
* @brief 启动I2C任务(整合初始化和任务创建)
* @return esp_err_t 操作结果
*/
esp_err_t StartTask();
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandWriteReg(uint8_t reg_addr, uint8_t data);
/**
* @brief 读寄存器数据
* @param reg_addr 寄存器地址
* @param data 要读出的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandReadReg(uint8_t reg_addr, uint8_t *data);
// /**
// * @brief 设置IO扩展板GPIO中断状态 通过读取Input_Port0 和 Input_Port1 寄存器可以清除中断
// * @param reg_addr 寄存器地址
// * @return esp_err_t 操作结果
// */
// esp_err_t SetInterruptStatus(bool status);
/**
* @brief 配置IO扩展版上的IO口为输出还是输入,并且设置初始为高电平
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandSetMode();
/**
* @brief 设置步进电机的细分模式
* @param mode 模式:1/2/4/8/16/32/64
* @return esp_err_t 操作结果
*/
void SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT, uint8_t m0_pin, uint8_t m1_pin);
/**
* @brief 设置步进电机的方向
* @param dir 方向
* @param OUTPUT_PORT 输出端口
* @param pin 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin);
/**
* @brief 设置步进电机的睡眠模式
* @param 是否进入睡眠模式
* @param 输出端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin);
/**
* @brief 设置步进电机的使能引脚状态
* @param 使能状态
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin);
/**
* @brief 设置LCD的BL引脚状态
* @param BL状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin);
/**
* @brief 设置LCD的RST引脚状态
* @param RST状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t rst_pin);
/**
* @brief 设置LCD的CS引脚状态
* @param CS状态(低电平为选中状态,高电平为不选中状态)
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdCsMode(bool mode, uint8_t OUTPUT_PORT, uint8_t cs_pin);
/**
* @brief 设置雷达中断引脚
* @param 毫米波雷达的中断引脚模式
* @param 输入端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin);
// 公共成员变量
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
uint8_t device_addr_; // I2C设备地址
private:
// 静态成员函数
MillimeterWaveRadar* radar_ = nullptr; // 毫米波雷达指针,用于触发回调
static void IRAM_ATTR GpioIsrHandler(void* arg); // 中断处理函数
static void I2cReadTask(void* pvParameters); // I2C数据读取任务函数
// /**
// * @brief 分配静态任务所需的内存
// * @return bool 分配是否成功
// */
// bool AllocateStaticTaskMemory();
// /**
// * @brief 释放静态任务分配的内存
// */
// void FreeStaticTaskMemory();
// 实例成员变量
EventGroupHandle_t i2c_task_event_group_ = nullptr; // 事件组
TaskHandle_t xI2CReadTaskHandle_ = nullptr; // 任务句柄
bool is_initialized_ = false; // 初始化状态标记(新增)
gpio_num_t interrupt_gpio_ = GPIO_NUM_NC; // 中断引脚
gpio_num_t reset_gpio_ = GPIO_NUM_NC; // 复位引脚
// StackType_t* pI2CReadTaskStackBuffer = nullptr; // 任务栈指针(PSRAM)
// StaticTask_t* pxI2CReadTaskBuffer = nullptr; // 任务控制块指针(内部RAM)
};
#endif
@@ -0,0 +1,311 @@
/**
* @file board.cc
* @brief 板级外设初始化类,包含步进电机、毫米波雷达、I2C总线等外设的初始化函数。
* @author zouyuhua
* @date 2025年8月7日
*
*/
extern "C" {
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "driver/gptimer.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/uart_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2c_bus.h"
}
#include "board_sweekar.h"
#include "config.h"
#define TAG "board_sweekar"
/*========================== 步进电机相关驱动 ==========================*/
/**
* @brief 步进电机初始化函数
* 配置步进电机所需的GPIO引脚模式,通过IO扩展板设置步进驱动芯片的工作模式
* 在 stepper_motor.cc 中完成 GPTimer + GPIO 翻转 STEP 脉冲的初始化
*/
void BoardSweekar::StepperMotorInit(void)
{
esp_err_t err = ESP_OK;
// 1) STEP 引脚改为普通 GPIO(不再用 LEDC)
gpio_config_t io_cfg;
io_cfg.pin_bit_mask = 1ULL << STEPPER_STEP_IO;
io_cfg.mode = GPIO_MODE_OUTPUT;
io_cfg.pull_up_en = GPIO_PULLUP_DISABLE;
io_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_cfg.intr_type = GPIO_INTR_DISABLE;
err = gpio_config(&io_cfg);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机 STEP GPIO 配置失败, 错误码: 0x%x", err);
}
// 初始电平拉低,保证上电不乱跳
gpio_set_level(STEPPER_STEP_IO, 0);
// 2) 检查 I2C 总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 3) 通过 IO 扩展板配置步进驱动芯片:睡眠/使能/细分/方向
err = io_expansion_.SetMotorSleepMode(
STEPPER_MOTOR_WORK,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_WORK
);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机睡眠模式配置失败, 错误码: 0x%x", err);
}
err = io_expansion_.SetMotorEnableMode(
STEPPER_MOTOR_ENABLE_DISABLE,
STEPPER_MOTOR_ENABLE_PORT,
STEPPER_MOTOR_ENABLE_PIN
);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机使能配置失败, 错误码: 0x%x", err);
}
// 细分设置:这里仍然用原来的 1/8 细分
io_expansion_.SetStepperMicrostepMode(
4,
STEPPER_MOTOR_M0_MADE_PORT,
STEPPER_MOTOR_M0_PIN,
STEPPER_MOTOR_M1_MADE_PORT,
STEPPER_MOTOR_M1_PIN
);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机细分模式配置失败, 错误码: 0x%x", err);
}
// 默认方向先设一个(比如“上升”方向)
err = io_expansion_.StepperMotorSetDir(
STEPPER_MOTOR_LEFT,
STEPPER_MOTOR_DIR_PORT,
STEPPER_MOTOR_DIR_PIN
);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机方向配置失败, 错误码: 0x%x", err);
}
}
/*========================== 毫米波雷达相关驱动 ==========================*/
/**
* @brief 毫米波雷达串口初始化函数
*
* 配置毫米波雷达所使用的串口参数(波特率、数据位、校验位、停止位等),设置串口引脚,
* 并安装串口驱动。采用较大的RX/TX FIFO深度以处理突发数据,禁用驱动自带事件队列以避免
* 与自定义中断冲突。
*/
void BoardSweekar::MillimeterWaveRadarUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = RADAR_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,
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(RADAR_UART_NUM, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(RADAR_UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动(禁用驱动自带事件队列,避免与自定义中断冲突)
// 增加 RX/TX FIFO 深度以处理突发数据
ESP_ERROR_CHECK(uart_driver_install(RADAR_UART_NUM, RADAR_BUF_SIZE * 4, RADAR_BUF_SIZE * 4,
0, NULL, 0));
}
/**
* @brief 毫米波雷达中断引脚初始化函数
*
* 配置毫米波雷达中断引脚为输入模式,禁用上拉电阻,使能下拉电阻,初始禁用中断功能
* (如需使用中断需单独进行配置)。
*/
void BoardSweekar::MillimeterWaveRadarGpioInit(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << MILLIMETER_WAVE_RADAR_PIN), // 选择GPIO引脚
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE, // 使能下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断(如需中断需单独配置)
};
// 配置GPIO
gpio_config(&io_conf);
}
/*========================== I2C总线及挂载的从设备相关驱动 ==========================*/
/**
* @brief I2C总线及从机设备初始化函数
*
* 首先配置并创建I2C主机总线,然后依次添加IO扩展板和线性马达作为I2C从机设备到总线上。
* 若总线创建或设备添加失败,会输出错误信息并进行相应的资源清理(删除已创建的总线)。
* 初始化成功后,会通过ESP_LOGI输出IO扩展板和线性马达的从机地址信息。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示相应的失败原因
*/
esp_err_t BoardSweekar::I2CMasterInit(void)
{
static const char *I2C_INIT = "I2C_INIT";
// 初始化IO扩展板复位引脚
gpio_config_t reset_io_conf = {
.pin_bit_mask = (1ULL << IO_EXPAND_RSTN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&reset_io_conf);
// 执行复位时序:先拉低100ms,再拉高并等待100ms
gpio_set_level(IO_EXPAND_RSTN, 0); // 拉低复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms
gpio_set_level(IO_EXPAND_RSTN, 1); // 释放复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
ESP_LOGI(I2C_INIT, "IO扩展板复位完成");
// 创建统一的I2C总线(使用ESP-ADF的i2c_bus)
i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C0_MASTER_SDA_IO,
.scl_io_num = I2C0_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = 100000,
},
};
// 创建I2C总线(ESP-ADF的audio_board_init会复用这个总线)
bus_handle_ = i2c_bus_create(I2C0_MASTER_PORT, &i2c_cfg);
if (bus_handle_ == NULL) {
ESP_LOGE(TAG, "Failed to create I2C bus");
return ESP_FAIL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
// ESP-ADF的i2c_bus不需要单独添加设备,直接通过总线句柄和设备地址进行通信
// 验证线性马达设备是否存在
esp_err_t err = i2c_bus_probe_addr(bus_handle_, LINEAR_MOTOR_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "线性马达设备未找到(地址:0x%02X),错误码: 0x%x", LINEAR_MOTOR_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "线性马达设备检测成功(地址:0x%02X)", LINEAR_MOTOR_I2C_SLAVE_ADDR);
}
// 验证IO扩展板设备是否存在
err = i2c_bus_probe_addr(bus_handle_, I2C_IO_EXPAND_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "IO扩展板设备未找到(地址:0x%02X),错误码: 0x%x", I2C_IO_EXPAND_SLAVE_ADDR, err);
// 不返回错误,继续初始化其他设备
} else {
ESP_LOGI(I2C_INIT, "IO扩展板设备检测成功(地址:0x%02X)", I2C_IO_EXPAND_SLAVE_ADDR);
}
// 验证陀螺仪设备是否存在
err = i2c_bus_probe_addr(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "陀螺仪设备未找到(地址:0x%02X),错误码: 0x%x", GYROSCOPE_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "陀螺仪设备检测成功(地址:0x%02X)", GYROSCOPE_I2C_SLAVE_ADDR);
}
// 为IO扩展板设置总线句柄和设备地址(使用7位地址)
io_expansion_.bus_handle_ = bus_handle_;
io_expansion_.device_addr_ = I2C_IO_EXPAND_SLAVE_ADDR << 1;
// 为线性马达设置总线句柄和设备地址(使用7位地址)
linear_motor_.bus_handle_ = bus_handle_;
linear_motor_.device_addr_ = LINEAR_MOTOR_I2C_SLAVE_ADDR << 1;
ESP_LOGI(I2C_INIT, "I2C总线初始化完成");
return ESP_OK;
}
/*========================== VBAT to VBAT_5V相关驱动 ==========================*/
/**
* @brief 开启VBAT to VBAT_5V
*
* 创建开启升压的电源管理单元
*
*/
void BoardSweekar::VbatToVbat5vInit(void)
{
// 配置VBAT_5V引脚为输出模式
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << VBAT_5V_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 使能VBAT_5V
gpio_set_level(VBAT_5V_PIN, 1);
}
/*========================== 底座控制串口相关驱动 ==========================*/
/**
* @brief 底座控制命令串口初始化函数
*/
void BoardSweekar::BaseBoardControlUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = BASE_BOARD_CTL_UART_BAUDRATE,
.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,
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(BASE_BOARD_CTL_UART_PORT, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(BASE_BOARD_CTL_UART_PORT, BASE_BOARD_CTL_UART_TX_PIN, BASE_BOARD_CTL_UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(BASE_BOARD_CTL_UART_PORT,
BASE_BOARD_CTL_UART_BUF_SIZE,
BASE_BOARD_CTL_UART_BUF_SIZE,
0, NULL, 0));
base_board_control_.SetUartPort(BASE_BOARD_CTL_UART_PORT);
}
@@ -0,0 +1,45 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include "config.h"
#include "io_expansion_board.h"
#include "stepper_motor.h"
#include "i2c_bus.h"
#include "gyroscope.h"
#include "touch_sensor.h"
#include "base_board_control.h"
class BoardSweekar
{
private:
i2c_bus_handle_t bus_handle_ = nullptr; // 使用ESP-ADF的I2C总线句柄
public:
IOExpansionBoard io_expansion_; // IO扩展板对象
LinearMotor linear_motor_; // 线性马达对象
Gyroscope gyroscope_; // 陀螺仪对象
TouchSensor touch_sensor_; // 触摸传感器对象
BaseBoardControl base_board_control_; // 底座控制对象
// 初始化步进电机
void StepperMotorInit(void);
// 初始化串口
void MillimeterWaveRadarUartInit(void);
//初始化毫米波雷达的中断引脚
void MillimeterWaveRadarGpioInit(void);
// 初始化I2C总线
esp_err_t I2CMasterInit(void);
// 初始化VBAT toVBAT_5V引脚
void VbatToVbat5vInit(void);
// 初始化底座控制串口
void BaseBoardControlUartInit(void);
// 获取I2C总线句柄
i2c_bus_handle_t GetBusHandle() const { return bus_handle_; }
};
#endif
@@ -0,0 +1,145 @@
/* 定义引脚分配 */
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
/**
* @file config.h
* @brief 定义了引脚的分配。
* @author zouyuhua
* @date 2025年8月7日
*
*/
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_rom_sys.h" // 用于 esp_rom_delay_us
#include "esp_timer.h"
#include "esp_mac.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/timer.h"
#include "esp_intr_alloc.h"
#include "soc/timer_group_struct.h" // 包含TIMERG0、TIMERG1的结构体定义
#include "soc/timer_group_reg.h" // 包含定时器相关寄存器宏定义
#include "driver/uart.h"
#include "esp_err.h"
#include "freertos/queue.h"
#include "soc/uart_reg.h"
#include "linear_motor.h"
#include "gyroscope.h"
#define TAG_BOARD "SWEEKAR_ESP32"
/*I2C总线引脚定义*/
#define I2C0_MASTER_PORT I2C_NUM_0
#define I2C0_MASTER_SDA_IO GPIO_NUM_2
#define I2C0_MASTER_SCL_IO GPIO_NUM_3
/* 步进电机配置 */
#define STEPPER_STEP_IO GPIO_NUM_42 // 步进电机脉冲输入引脚
#define STEPPER_TIMER_RES_HZ 1000000 // 步进电机定时器分辨率 1 MHz(1 us 步进)
/* 定义屏幕驱动控制引脚 */
#define ST7789_PIN_CLK GPIO_NUM_48 // SPI时钟引脚
#define ST7789_PIN_DC GPIO_NUM_38 // 数据/命令选择引脚RSEL
#define ST7789_PIN_MOSI GPIO_NUM_47 // SPI数据输出引脚
#define ST7789_PIN_CS GPIO_NUM_46 // SPI片选引脚
#define ST7789_PIN_BL GPIO_NUM_NC // 背光引脚
#define ST7789_PIN_RST GPIO_NUM_NC // 复位引脚
#define LCD_BL_ACTIVE_LEVEL 0 // 背光有效电平:1=高电平有效(NMOS),0=低电平有效(PMOS)
#define LCD_WIDTH 172 // 屏幕宽度(水平方向像素数)
#define LCD_HEIGHT 320 // 屏幕高度(垂直方向像素数)
/* 毫米波雷达配置参数 */
/* 定义毫米波雷达的中断引脚 */
#define MILLIMETER_WAVE_RADAR_PIN GPIO_NUM_35
// UART 配置参数
#define RADAR_UART_NUM UART_NUM_1 // 使用 UART1
#define UART_TX_PIN GPIO_NUM_40 // GPIO40 作为 TX(根据实际硬件连接调整)
#define UART_RX_PIN GPIO_NUM_39 // GPIO39 作为 RX
#define RADAR_BAUD_RATE 9600 // 雷达波特率
#define RADAR_BUF_SIZE 100 // 雷达接收缓冲区大小
/* IO扩展硬件配置常量 */
#define I2C_MASTER_FREQ_HZ 100000 // I2C通信频率(100kHz)(可更改至400000)
#define I2C_IO_EXPAND_SLAVE_ADDR 0x58 // AW9523BTQR器件地址,v1_1_13的addr1和addr0是0
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // 中断检测引脚
#define ESP_INTR_FLAG_DEFAULT 0 // 中断标志默认值
#define IO_EXPAND_RSTN GPIO_NUM_45 // IO扩展板复位引脚
/* 定义线性马达的I2C从机配置常量 */
#define LINEAR_MOTOR_I2C_SLAVE_ADDR 0x5A // 线性马达的I2C从机地址
/* 定义陀螺仪的I2C从机配置常量 */
#define GYROSCOPE_I2C_SLAVE_ADDR 0x6A // 陀螺仪的I2C从机地址
#define GYROSCOPE_INPUT_PIN_SEL GPIO_NUM_21 // 陀螺仪中断引脚
/* 原生触摸引脚配置 */
// 触摸按键数量配置
#define TOUCH_BUTTON_NUM 3
// 触摸通道定义
#define TOUCH_PAD_CHANNEL_1 TOUCH_PAD_NUM11 // GPIO11
#define TOUCH_PAD_CHANNEL_2 TOUCH_PAD_NUM12 // GPIO12
#define TOUCH_PAD_CHANNEL_3 TOUCH_PAD_NUM13 // GPIO13
/* ES8311音频编解码器配置 (ESP-ADF兼容) */
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
/* I2S GPIO定义 */
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_15
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
/* PA引脚定义 */
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
/* I2C配置 */
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_2
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_3
#define AUDIO_CODEC_ES8311_ADDR 0x18
#define AUDIO_CODEC_ES7210_ADDR 0x40
/* WS2812 RGB LED配置 */
#define WS2812_PIN GPIO_NUM_41 // WS2812数据引脚
#define WS2812_NUM_LEDS 2 // WS2812 LED数量
/* 创建VBAT to VBAT_5V */
#define VBAT_5V_PIN GPIO_NUM_NC // VBAT_5V引脚
/* 定义按键的中断引脚 */
#define BUTTON_PIN GPIO_NUM_0 // 按键中断引脚
#define RIGHT_BUTTON_PIN GPIO_NUM_8 // 按键中断引脚
/* 底座控制命令串口配置 */
#define BASE_BOARD_CTL_UART_PORT UART_NUM_0
#define BASE_BOARD_CTL_UART_TX_PIN GPIO_NUM_43
#define BASE_BOARD_CTL_UART_RX_PIN GPIO_NUM_44
#define BASE_BOARD_CTL_UART_BAUDRATE 115200
#define BASE_BOARD_CTL_UART_BUF_SIZE 256
#endif // _BOARD_CONFIG_H_
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,419 @@
#ifndef IO_EXPANSION_BOARD_H
#define IO_EXPANSION_BOARD_H
#include <cstdint>
#include <esp_log.h>
#include <esp_check.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include <functional>
#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 "portmacro.h"
// 前向声明,避免循环引用
class MillimeterWaveRadar;
class PhotoInterrupter;
class AdcBatteryMonitor;
// 回调函数类型定义
typedef std::function<void()> RadarCallback;
typedef std::function<void()> PhotoInterrupterStartCallback;
typedef std::function<void()> PhotoInterrupterEndCallback;
typedef std::function<void()> BatteryChargingCallback;
typedef std::function<void()> BatteryFullCallback;
typedef std::function<void()> BatteryNotChargingCallback;
/* 寄存器地址 */
#define INPUT_PORT0 0x00 // 输入端口0寄存器
#define INPUT_PORT1 0x01 // 输入端口1寄存器 0为低电平(仅支持读取)
#define OUTPUT_PORT0 0x02 // 输出端口0寄存器
#define OUTPUT_PORT1 0x03 // 输出端口1寄存器 0为低电平
#define CONFIG_PORT0 0x04 // 配置端口0寄存器
#define CONFIG_PORT1 0x05 // 配置端口1寄存器 0为输出模式
#define INT_PORT0 0x06 // 中断端口0寄存器
#define INT_PORT1 0x07 // 中断端口1寄存器 0为使能中断(根据手册默认是使能的)
#define CTL 0x11 // 全局控制寄存器
#define ID 0x23 // 芯片ID寄存器(0x23)
/***********************************IO扩展板电机引脚配置参数*************************************/
/* 设置步进电机方向 */
#define STEPPER_MOTOR_LEFT false //步进电机的方向为左转
#define STEPPER_MOTOR_RIGHT true //步进电机的方向为右转
#define STEPPER_MOTOR_DIR_PORT OUTPUT_PORT0 //步进电机方向引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_DIR_PIN 4 //步进电机方向引脚对应的IO扩展板引脚P0_4
#define STEPPER_MOTOR_DIR_CONFIG CONFIG_PORT0 //步进电机方向引脚配置在P0_4
/* 设置步进电机细分模式 */
#define STEPPER_MOTOR_M0_PIN 5 //步进电机细分模式选择引脚对应IO扩展板引脚P1_5
#define STEPPER_MOTOR_M0_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M0_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_5
#define STEPPER_MOTOR_M1_PIN 4 //步进电机细分模式选择引脚对应IO扩展板引脚P1_4
#define STEPPER_MOTOR_M1_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M1_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_4
/* 设置步进电机睡眠引脚 */
#define STEPPER_MOTOR_SLEEP false //步进电机进入睡眠模式
#define STEPPER_MOTOR_WORK true //步进电机进入工作模式
#define STEPPER_MOTOR_SLEEP_PIN 6 //步进电机睡眠引脚对应IO扩展板引脚P1_6
#define STEPPER_MOTOR_SLEEP_CONFIG CONFIG_PORT1 //步进电机睡眠引脚配置在P1_6
#define STEPPER_MOTOR_SLEEP_PORT OUTPUT_PORT1 //步进电机睡眠引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置电机的使能引脚 */
#define STEPPER_MOTOR_ENABLE_PIN 7 //电机使能引脚对应IO扩展板引脚P0_7
#define STEPPER_MOTOR_ENABLE_PORT OUTPUT_PORT0 //电机使能引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_ENABLE_CONFIG CONFIG_PORT0 //电机使能引脚配置在P0_7
#define STEPPER_MOTOR_ENABLE_ENABLE true //电机使能引脚状态为高电平
#define STEPPER_MOTOR_ENABLE_DISABLE false //电机使能引脚状态为低电平
/* 设置毫米波雷达对应IO扩展板的中断引脚 */
#define RADAR_INTERRUPT_PIN 7 //毫米波雷达中断引脚对应IO扩展板引脚P1_7
#define RADAR_INTERRUPT_CONFIG CONFIG_PORT1 //毫米波雷达中断引脚挂载在IO扩展板的输入端口
#define RADAR_INTERRUPT_INPUT INPUT_PORT1 //毫米波雷达中断引脚输入状态检测在INPUT_PORT1
#define RADAR_INTERRUPT_ENABLE true //毫米波雷达中断引脚状态为输入状态
#define RADAR_INTERRUPT_DISABLE false //毫米波雷达中断引脚状态为输出状态
/* 任务配置常量 */
#define WRITE_BUF_SIZE 2 // I2C单次写入字节数
#define READ_BUF_SIZE 2 // I2C单次读取字节数
#define I2C_TASK_PRIORITY 2 // 任务优先级(中等)
#define I2C_TASK_STACK 4096 // 任务栈大小(4KB)
// 光学传感器引脚定义
#define START_PHOTO_INTERRUPTER_PIN 2 // 光学传感器引脚(P0_2)
#define START_PHOTO_INTERRUPTER_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define START_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define START_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define START_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
#define END_PHOTO_INTERRUPTER_PIN 3 // 光学传感器引脚(P0_3)
#define END_PHOTO_INTERRUPTER_PORT_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define END_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define END_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define END_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
/***********************************IO扩展板LCD引脚配置参数*************************************/
/* 设置LCD的BL引脚 */
#define LCD_BL_PIN 6 //LCD背光引脚对应IO扩展板引脚P0_6
#define LCD_BL_PORT OUTPUT_PORT0 //LCD背光引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define LCD_BL_CONFIG CONFIG_PORT0 //LCD背光引脚配置在P0_6
#define LCD_BL_HIGH true //LCD背光引脚状态为高电平
#define LCD_BL_LOW false //LCD背光引脚状态为低电平
/* 设置LCD的RST引脚 */
#define LCD_RST_PIN 1 //LCD复位引脚对应IO扩展板引脚P1_1
#define LCD_RST_CONFIG CONFIG_PORT1 //LCD复位引脚配置在P1_1
#define LCD_RST_PORT OUTPUT_PORT1 //LCD复位引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_RST_HIGH true //LCD复位引脚状态为高电平
#define LCD_RST_LOW false //LCD复位引脚状态为低电平
/* 设置PA控制引脚 */
#define PA_CTRL_PIN 2 //PA控制引脚对应IO扩展板引脚P1_2
#define PA_CTRL_CONFIG CONFIG_PORT1 //PA控制引脚配置在P1_2
#define PA_CTRL_PORT OUTPUT_PORT1 //PA控制引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define PA_CTRL_HIGH true //PA控制引脚状态为高电平
#define PA_CTRL_LOW false //PA控制引脚状态为低电平
// 电池充电检测引脚定义
#define BATTERY_FULL_INPUT INPUT_PORT1 // 电池充满检测引脚挂载在IO扩展板的输入端口
#define BATTERY_FULL_PIN 3 // 电池充满检测引脚对应IO扩展板引脚P1_3
#define BATTERY_FULL_CONFIG CONFIG_PORT1 // 电池充满检测引脚配置在P1_3
#define BATTERY_CHRG_INPUT INPUT_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define BATTERY_CHRG_PIN 5 // 充电检测引脚(P0_5)
#define BATTERY_CHRG_CONFIG CONFIG_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define TAG_GPIO_IO_EXPAND_DETECT "GPIO_IO_EXPAND_DETECT" // GPIO中断检测日志标签
#define TAG_I2C_IO_EXPAND_TASK "I2C_IO_EXPAND_TASK" // I2CIO扩展板任务日志标签
// GPIO引脚定义
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // IO扩展板中断检测引脚
#define IO_EXPAND_RSTN GPIO_NUM_45 // IO扩展板复位引脚
/**
* @class IOExpansionBoard
* @brief I/O扩展板驱动类,封装AW9523BTQR芯片操作
*/
class IOExpansionBoard
{
public:
/**
* @brief 构造函数
*/
IOExpansionBoard();
/**
* @brief 设置毫米波雷达指针
* @param radar 毫米波雷达对象指针
*/
void SetRadar(MillimeterWaveRadar* radar) { radar_ = radar; };
/**
* @brief 设置限位开关指针
* @param photo_interrupter 限位开关对象指针
*/
void setPhotoInterrupter(PhotoInterrupter* photo_interrupter) { photo_interrupter_ = photo_interrupter; };
/**
* @brief 设置电池监控器指针
* @param battery_monitor 电池监控器对象指针
*/
void setBatteryMonitor(AdcBatteryMonitor* battery_monitor) { battery_monitor_ = battery_monitor; };
/**
* @brief 析构函数
*/
~IOExpansionBoard();
/**
* @brief 初始化GPIO中断检测
* @param gpio_num 中断引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetIoExpand();
/**
* @brief 初始化I2C主机通信
* @return esp_err_t 初始化结果
*/
// esp_err_t IoExpansionI2cInit();
/**
* @brief 创建I2C数据读取任务
*/
void CreateI2cReadTask();
/**
* @brief 启动I2C任务(整合初始化和任务创建)
* @return esp_err_t 操作结果
*/
esp_err_t StartTask();
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandWriteReg(uint8_t reg_addr, uint8_t data);
/**
* @brief 读寄存器数据
* @param reg_addr 寄存器地址
* @param data 要读出的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandReadReg(uint8_t reg_addr, uint8_t *data);
/**
* @brief 读取输入端口0寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort0(uint8_t *data);
/**
* @brief 读取输入端口1寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort1(uint8_t *data);
// /**
// * @brief 设置IO扩展板GPIO中断状态 通过读取Input_Port0 和 Input_Port1 寄存器可以清除中断
// * @param reg_addr 寄存器地址
// * @return esp_err_t 操作结果
// */
// esp_err_t SetInterruptStatus(bool status);
/**
* @brief 配置IO扩展版上的IO口为输出还是输入,并且设置初始为高电平
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandSetMode();
/**
* @brief 设置步进电机的细分模式
* @param mode 模式:1/2/4/8/16/32/64
* @return esp_err_t 操作结果
*/
void SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT_m0, uint8_t m0_pin, uint8_t OUTPUT_PORT_m1, uint8_t m1_pin);
/**
* @brief 设置步进电机的方向
* @param dir 方向
* @param OUTPUT_PORT 输出端口
* @param pin 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin);
/**
* @brief 设置步进电机的睡眠模式
* @param 是否进入睡眠模式
* @param 输出端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin);
/**
* @brief 设置步进电机的使能引脚状态
* @param 使能状态
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin);
/**
* @brief 设置LCD的BL引脚状态
* @param BL状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin);
/**
* @brief 设置LCD的RST引脚状态
* @param RST状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t rst_pin);
/**
* @brief 设置PA控制引脚状态
* @param PA_CTRL状态(低电平为选中状态,高电平为不选中状态)
* @return esp_err_t 操作结果
*/
esp_err_t SetPaCtrlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t pa_ctrl_pin);
/**
* @brief 设置雷达中断引脚
* @param 毫米波雷达的中断引脚模式
* @param 输入端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin);
/**
* @brief 计算Port0的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort0();
/**
* @brief 计算Port1的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort1();
/**
* @brief 设置雷达中断回调函数
* @param callback 雷达中断回调函数
*/
void SetRadarCallback(RadarCallback callback) { radar_callback_ = callback; }
/**
* @brief 设置光学传感器开始中断回调函数
* @param callback 光学传感器开始中断回调函数
*/
void SetPhotoInterrupterStartCallback(PhotoInterrupterStartCallback callback) { photo_start_callback_ = callback; }
/**
* @brief 设置光学传感器结束中断回调函数
* @param callback 光学传感器结束中断回调函数
*/
void SetPhotoInterrupterEndCallback(PhotoInterrupterEndCallback callback) { photo_end_callback_ = callback; }
/**
* @brief 设置电池充电中断回调函数
* @param callback 电池充电中断回调函数
*/
void SetBatteryChargingCallback(BatteryChargingCallback callback) { battery_charging_callback_ = callback; }
/**
* @brief 设置电池充满中断回调函数
* @param callback 电池充满中断回调函数
*/
void SetBatteryFullCallback(BatteryFullCallback callback) { battery_full_callback_ = callback; }
/**
* @brief 设置电池非充电中断回调函数
* @param callback 电池非充电中断回调函数
*/
void SetBatteryNotChargingCallback(BatteryNotChargingCallback callback) { battery_not_charging_callback_ = callback; }
// 公共成员变量
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
uint8_t device_addr_; // I2C设备地址
private:
// 静态成员函数
MillimeterWaveRadar* radar_ = nullptr; // 毫米波雷达指针,用于触发回调
PhotoInterrupter* photo_interrupter_ = nullptr; // 限位开关指针,用于触发回调
AdcBatteryMonitor* battery_monitor_ = nullptr; // 电池监控器指针,用于触发回调
static void IRAM_ATTR GpioIsrHandler(void* arg); // 中断处理函数
static void I2cReadTask(void* pvParameters); // I2C数据读取任务函数
// 成员变量用于状态跟踪
uint8_t last_port0_state_ = 0; // 上次检测到的Port0状态
uint8_t last_port1_state_ = 0; // 上次检测到的Port1状态
bool radar_last_state_ = false; // 雷达引脚上一次的状态
bool battery_charging_last_state_ = false; // 电池充电状态上一次的状态
bool battery_full_last_state_ = false; // 电池充满状态上一次的状态
// 回调函数成员变量
RadarCallback radar_callback_ = nullptr;
PhotoInterrupterStartCallback photo_start_callback_ = nullptr;
PhotoInterrupterEndCallback photo_end_callback_ = nullptr;
BatteryChargingCallback battery_charging_callback_ = nullptr;
BatteryFullCallback battery_full_callback_ = nullptr;
BatteryNotChargingCallback battery_not_charging_callback_ = nullptr;
// PhotoInterrupter* photo_interrupter_ = nullptr; // 光学传感器指针,用于触发回调
// /**
// * @brief 分配静态任务所需的内存
// * @return bool 分配是否成功
// */
// bool AllocateStaticTaskMemory();
// /**
// * @brief 释放静态任务分配的内存
// */
// void FreeStaticTaskMemory();
// 实例成员变量
EventGroupHandle_t i2c_task_event_group_ = nullptr; // 事件组
TaskHandle_t xI2CReadTaskHandle_ = nullptr; // 任务句柄
bool is_initialized_ = false; // 初始化状态标记(新增)
gpio_num_t interrupt_gpio_ = GPIO_NUM_NC; // 中断引脚
gpio_num_t reset_gpio_ = GPIO_NUM_NC; // 复位引脚
// StackType_t* pI2CReadTaskStackBuffer = nullptr; // 任务栈指针(PSRAM)
// StaticTask_t* pxI2CReadTaskBuffer = nullptr; // 任务控制块指针(内部RAM)
};
#endif
@@ -0,0 +1,702 @@
/**
* @file board.cc
* @brief 板级外设初始化类,包含步进电机、毫米波雷达、I2C总线等外设的初始化函数。
* @author zouyuhua
* @date 2025年8月7日
*
*/
extern "C" {
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "driver/gptimer.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/uart_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2c_bus.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
}
#include "board_sweekar.h"
#include "config.h"
#include "board.h" // 添加board.h以获取audio_board_sdcard_init函数声明
#include "drv8428.h"
#include "aw8646.h"
#define TAG "board_sweekar"
BoardSweekar::BoardSweekar()
: base_board_hal_uart_(BASE_BOARD_CTL_UART_PORT)
{
#ifdef CONFIG_STEPPER_DRIVER_AW8646
stepper_driver_ = new Aw8646Driver(io_expansion_);
#else
stepper_driver_ = new Drv8428Driver(io_expansion_);
#endif
}
BoardSweekar::~BoardSweekar()
{
if (stepper_driver_) {
delete stepper_driver_;
stepper_driver_ = nullptr;
}
}
/*========================== 步进电机相关驱动 ==========================*/
/**
* @brief 步进电机初始化函数
* 配置步进电机所需的GPIO引脚模式,通过IO扩展板设置步进驱动芯片的工作模式
* 在 stepper_motor.cc 中完成 GPTimer + GPIO 翻转 STEP 脉冲的初始化
*/
void BoardSweekar::StepperMotorInit(void)
{
esp_err_t err = ESP_OK;
// 1) STEP 引脚改为普通 GPIO(不再用 LEDC)
gpio_config_t io_cfg;
io_cfg.pin_bit_mask = 1ULL << STEPPER_STEP_IO;
io_cfg.mode = GPIO_MODE_OUTPUT;
io_cfg.pull_up_en = GPIO_PULLUP_DISABLE;
io_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_cfg.intr_type = GPIO_INTR_DISABLE;
err = gpio_config(&io_cfg);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机 STEP GPIO 配置失败, 错误码: 0x%x", err);
}
// 初始电平拉低,保证上电不乱跳
gpio_set_level(STEPPER_STEP_IO, 0);
// 2) 检查 I2C 总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 3) 初始化驱动对象并初始化
if (stepper_driver_) {
err = stepper_driver_->Init();
if (err != ESP_OK) {
ESP_LOGE(TAG_BOARD, "步进电机驱动初始化失败, 错误码: 0x%x", err);
}
} else {
ESP_LOGE(TAG_BOARD, "步进电机驱动创建失败");
}
}
/**
* @brief LCD IO引脚初始化函数(消除与步进电机的耦合)
*
* 配置LCD所需的IO扩展板寄存器,确保LCD复位引脚正常工作
* 此函数独立于步进电机初始化,专门处理LCD相关的IO配置
*/
void BoardSweekar::LcdIoInit(void)
{
esp_err_t err = ESP_OK;
// 1) 检查I2C总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 2) 执行硬件复位,确保IO扩展板处于已知状态
err = io_expansion_.ResetIoExpand();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板硬件复位失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板硬件复位成功");
}
// 3) 配置IO扩展板模式(端口方向等)
err = io_expansion_.IoexpandSetMode();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板模式配置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板模式配置成功");
}
// 4) 初始化LCD复位引脚:设置为高电平
err = io_expansion_.SetLcdResetMode(LCD_RST_HIGH, LCD_RST_PORT, LCD_RST_PIN);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "LCD复位引脚高电平设置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "LCD复位引脚高电平设置成功");
}
io_expansion_.SetLedEnMode(true);
}
/*========================== 毫米波雷达相关驱动 ==========================*/
/**
* @brief 毫米波雷达串口初始化函数
*
* 配置毫米波雷达所使用的串口参数(波特率、数据位、校验位、停止位等),设置串口引脚,
* 并安装串口驱动。采用较大的RX/TX FIFO深度以处理突发数据,禁用驱动自带事件队列以避免
* 与自定义中断冲突。
*/
void BoardSweekar::MillimeterWaveRadarUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = RADAR_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 122,
.source_clk = UART_SCLK_DEFAULT,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(RADAR_UART_NUM, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(RADAR_UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动(禁用驱动自带事件队列,避免与自定义中断冲突)
// 增加 RX/TX FIFO 深度以处理突发数据
ESP_ERROR_CHECK(uart_driver_install(RADAR_UART_NUM, RADAR_BUF_SIZE * 4, RADAR_BUF_SIZE * 4,
0, NULL, 0));
}
/**
* @brief 毫米波雷达中断引脚初始化函数
*
* 配置毫米波雷达中断引脚为输入模式,禁用上拉电阻,使能下拉电阻,初始禁用中断功能
* (如需使用中断需单独进行配置)。
*/
void BoardSweekar::MillimeterWaveRadarGpioInit(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << MILLIMETER_WAVE_RADAR_PIN), // 选择GPIO引脚
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE, // 使能下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断(如需中断需单独配置)
};
// 配置GPIO
gpio_config(&io_conf);
}
/*========================== I2C总线及挂载的从设备相关驱动 ==========================*/
/**
* @brief I2C总线及从机设备初始化函数
*
* 首先配置并创建I2C主机总线,然后依次添加IO扩展板和线性马达作为I2C从机设备到总线上。
* 若总线创建或设备添加失败,会输出错误信息并进行相应的资源清理(删除已创建的总线)。
* 初始化成功后,会通过ESP_LOGI输出IO扩展板和线性马达的从机地址信息。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示相应的失败原因
*/
esp_err_t BoardSweekar::I2CMasterInit(void)
{
static const char *I2C_INIT = "I2C_INIT";
// 初始化IO扩展板复位引脚
gpio_config_t reset_io_conf = {
.pin_bit_mask = (1ULL << IO_EXPAND_RSTN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&reset_io_conf);
// 执行复位时序:先拉低100ms,再拉高并等待100ms
gpio_set_level(IO_EXPAND_RSTN, 0); // 拉低复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms
gpio_set_level(IO_EXPAND_RSTN, 1); // 释放复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
ESP_LOGI(I2C_INIT, "IO扩展板复位完成");
// 创建统一的I2C总线(使用ESP-ADF的i2c_bus)
i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C0_MASTER_SDA_IO,
.scl_io_num = I2C0_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = 100000,
},
.clk_flags = 0,
};
// 创建I2C总线(ESP-ADF的audio_board_init会复用这个总线)
bus_handle_ = i2c_bus_create(I2C0_MASTER_PORT, &i2c_cfg);
if (bus_handle_ == NULL) {
ESP_LOGE(TAG, "Failed to create I2C bus");
return ESP_FAIL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
// ESP-ADF的i2c_bus不需要单独添加设备,直接通过总线句柄和设备地址进行通信
// 验证线性马达设备是否存在
esp_err_t err = i2c_bus_probe_addr(bus_handle_, LINEAR_MOTOR_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "线性马达设备未找到(地址:0x%02X),错误码: 0x%x", LINEAR_MOTOR_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "线性马达设备检测成功(地址:0x%02X)", LINEAR_MOTOR_I2C_SLAVE_ADDR);
}
// 验证IO扩展板设备是否存在
err = i2c_bus_probe_addr(bus_handle_, I2C_IO_EXPAND_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "IO扩展板设备未找到(地址:0x%02X),错误码: 0x%x", I2C_IO_EXPAND_SLAVE_ADDR, err);
// 不返回错误,继续初始化其他设备
} else {
ESP_LOGI(I2C_INIT, "IO扩展板设备检测成功(地址:0x%02X)", I2C_IO_EXPAND_SLAVE_ADDR);
}
// 验证陀螺仪设备是否存在
err = i2c_bus_probe_addr(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "陀螺仪设备未找到(地址:0x%02X),错误码: 0x%x", GYROSCOPE_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "陀螺仪设备检测成功(地址:0x%02X)", GYROSCOPE_I2C_SLAVE_ADDR);
}
// 为IO扩展板设置总线句柄和设备地址(使用7位地址)
io_expansion_.bus_handle_ = bus_handle_;
io_expansion_.device_addr_ = I2C_IO_EXPAND_SLAVE_ADDR << 1;
// 线性马达的初始化已移至 InitializeAllHardware 中统一调用 Init 方法
// linear_motor_ 的参数传递将在那里完成,不再此处直接赋值,提高内聚性
ESP_LOGI(I2C_INIT, "I2C总线初始化完成");
return ESP_OK;
}
/*========================== VBAT to VBAT_5V相关驱动 ==========================*/
/**
* @brief 开启VBAT to VBAT_5V
*
* 创建开启升压的电源管理单元
*
*/
void BoardSweekar::VbatToVbat5vInit(void)
{
if (VBAT_5V_PIN == GPIO_NUM_NC) {
return;
}
// 配置VBAT_5V引脚为输出模式
gpio_config_t io_conf = {
.pin_bit_mask = (VBAT_5V_PIN != GPIO_NUM_NC) ? (1ULL << VBAT_5V_PIN) : 0,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 使能VBAT_5V
gpio_set_level(VBAT_5V_PIN, 1);
}
/*========================== 底座控制串口相关驱动 ==========================*/
/**
* @brief 底座控制命令串口初始化函数
*/
void BoardSweekar::BaseBoardControlUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = BASE_BOARD_CTL_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 122,
.source_clk = UART_SCLK_DEFAULT,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(BASE_BOARD_CTL_UART_PORT, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(BASE_BOARD_CTL_UART_PORT, BASE_BOARD_CTL_UART_TX_PIN, BASE_BOARD_CTL_UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(BASE_BOARD_CTL_UART_PORT,
BASE_BOARD_CTL_UART_BUF_SIZE,
BASE_BOARD_CTL_UART_BUF_SIZE,
0, NULL, 0));
base_board_control_.Init(&base_board_hal_uart_);
}
/*========================== SD卡相关驱动 ==========================*/
/**
* @brief SD卡初始化函数
*
* 初始化SD卡外设,使用1线模式进行通信。该函数负责初始化外设管理器
* 并配置SD卡,为后续的文件系统操作做准备。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示失败原因
*/
esp_err_t BoardSweekar::InitializeSdcard(void)
{
static const char *SDCARD_TAG = "SDCARD_INIT";
esp_err_t ret;
ESP_LOGI(SDCARD_TAG, "Initializing SD card");
if (is_sdcard_initialized_) {
ESP_LOGW(SDCARD_TAG, "SD card already initialized");
return ESP_OK;
}
// 初始化外设管理
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
periph_cfg.extern_stack = true; // 将任务栈分配到 PSRAM 以节省 Internal SRAM
periph_set_ = esp_periph_set_init(&periph_cfg);
if (!periph_set_) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize peripheral set");
return ESP_FAIL;
}
// 初始化SD卡外设
ret = audio_board_sdcard_init(periph_set_, SD_MODE_1_LINE);
if (ret != ESP_OK) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
return ret;
}
is_sdcard_initialized_ = true;
ESP_LOGI(SDCARD_TAG, "SD card initialized successfully");
return ESP_OK;
}
/**
* @brief 统一的硬件初始化接口实现
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备可能依赖)
* 2. IO扩展板(可能影响其他设备)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. 其他外设
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardware(void)
{
static const char *HW_INIT_TAG = "HW_INIT";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting unified hardware initialization...");
// 1. 初始化I2C总线(最基础)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "SD card initialized");
}
// 4. 初始化LCD IO引脚
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "LCD IO pins initialized");
// 5. 初始化触摸传感器
touch_sensor_.SetI2cHandle(bus_handle_);
touch_sensor_.Begin();
ESP_LOGI(HW_INIT_TAG, "Touch sensor initialized");
// 6. 初始化其他必要外设
// BaseBoardControlUartInit(); // 底座控制串口(demo5需要)
// ESP_LOGI(HW_INIT_TAG, "Base board control UART initialized");
// 初始化底座控制
// base_board_control_.Init();
// ESP_LOGI(HW_INIT_TAG, "Base board control initialized");
// 可选外设(根据具体应用需求)
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
StepperMotorInit();
ESP_LOGI(HW_INIT_TAG, "Unified hardware initialization completed successfully");
return ESP_OK;
}
/**
* @brief 进入低功耗模式
*
* 根据指定的功耗模式,执行相应的功耗优化策略:
* - IDLE模式:关闭非必要外设,保持快速响应
* - LIGHT_SLEEP模式:进入轻度睡眠,CPU暂停,外设断电
* - DEEP_SLEEP模式:深度睡眠,仅RTC运行,功耗最低
*/
esp_err_t BoardSweekar::EnterLowPowerMode(power_mode_t mode) {
static const char *POWER_TAG = "POWER_MGMT";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "Entering power mode: %d", mode);
if (mode == current_power_mode_) {
ESP_LOGW(POWER_TAG, "Already in power mode %d", mode);
return ESP_OK;
}
// 保存当前模式,以便恢复
power_mode_t previous_mode = current_power_mode_;
switch (mode) {
case POWER_MODE_NORMAL:
// 正常模式,不需要特殊处理
break;
case POWER_MODE_IDLE:
// 空闲模式:关闭非必要外设
ESP_LOGI(POWER_TAG, "Idle mode: disabling non-essential peripherals");
// 关闭SD卡(如果正在使用,需要先卸载)
if (is_sdcard_initialized_) {
// 注意:这里只是示例,实际应用中需要确保没有文件操作
ESP_LOGW(POWER_TAG, "SD card still active in idle mode");
}
// 关闭显示背光
// 关闭非必要GPIO的上拉/下拉
// 降低I2C时钟频率
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠
ESP_LOGI(POWER_TAG, "Light sleep mode: preparing for sleep");
// 设置唤醒源(需要根据具体需求配置)
// esp_sleep_enable_timer_wakeup(1000000); // 1秒后唤醒
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_XX, 0); // 外部中断唤醒
// 进入轻度睡眠
// ret = esp_light_sleep_start();
if (ret != ESP_OK) {
ESP_LOGE(POWER_TAG, "Failed to enter light sleep: %s", esp_err_to_name(ret));
current_power_mode_ = previous_mode;
return ret;
}
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠
ESP_LOGI(POWER_TAG, "Deep sleep mode: system will restart after wake-up");
// 保存必要的RTC数据
// rtc_gpio_isolate(GPIO_NUM_XX); // 隔离GPIO
// 设置深度睡眠唤醒源
// esp_sleep_enable_timer_wakeup(60000000); // 60秒后唤醒
// 进入深度睡眠
// esp_deep_sleep_start();
// 注意:深度睡眠后系统会重启,不会返回
break;
default:
ESP_LOGE(POWER_TAG, "Invalid power mode: %d", mode);
return ESP_ERR_INVALID_ARG;
}
current_power_mode_ = mode;
ESP_LOGI(POWER_TAG, "Successfully entered power mode: %d", mode);
return ret;
}
/**
* @brief 退出低功耗模式
*
* 从低功耗模式恢复到正常运行模式,重新初始化必要的硬件资源。
*/
esp_err_t BoardSweekar::ExitLowPowerMode(void) {
static const char *POWER_TAG = "POWER_MGMT";
ESP_LOGI(POWER_TAG, "Exiting low power mode");
if (current_power_mode_ == POWER_MODE_NORMAL) {
ESP_LOGW(POWER_TAG, "Already in normal mode");
return ESP_OK;
}
// 根据之前的功耗模式执行相应的恢复操作
switch (current_power_mode_) {
case POWER_MODE_IDLE:
// 恢复空闲模式
ESP_LOGI(POWER_TAG, "Restoring from idle mode");
// 重新初始化必要的外设
// 恢复I2C时钟频率
// 重新开启显示背光
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠唤醒后会自动继续执行
ESP_LOGI(POWER_TAG, "Woken up from light sleep");
// 重新配置时钟(如果需要)
// 重新初始化外设
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠后系统会重启,不会执行到这里
ESP_LOGW(POWER_TAG, "Deep sleep recovery should not reach here");
break;
default:
break;
}
// 恢复到正常模式
current_power_mode_ = POWER_MODE_NORMAL;
ESP_LOGI(POWER_TAG, "Successfully exited low power mode");
return ESP_OK;
}
/**
* @brief 设置硬件模块电源状态
*
* 提供精细化的电源管理,可以单独控制各个硬件模块的电源状态。
*/
esp_err_t BoardSweekar::SetModulePower(hardware_module_t module, bool enable) {
static const char *POWER_TAG = "POWER_MODULE";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "%s module %d", enable ? "Enabling" : "Disabling", module);
// 更新电源状态位图
if (enable) {
power_state_flags_ |= (1 << module);
} else {
power_state_flags_ &= ~(1 << module);
}
switch (module) {
case HW_MODULE_I2C:
if (!enable && bus_handle_) {
// 注意:I2C总线通常不建议完全关闭,可能影响多个设备
ESP_LOGW(POWER_TAG, "I2C bus power control not recommended");
}
break;
case HW_MODULE_SDCARD:
if (enable && !is_sdcard_initialized_) {
ret = InitializeSdcard();
} else if (!enable && is_sdcard_initialized_) {
// 卸载SD卡文件系统
// 注意:需要确保没有打开的文件
ESP_LOGW(POWER_TAG, "SD card power down requires file system cleanup");
}
break;
case HW_MODULE_LCD:
// LCD背光控制
// LCD电源引脚控制(如果有)
break;
case HW_MODULE_AUDIO:
// 音频编解码器电源控制
// 需要与AudioPlayer协调
break;
case HW_MODULE_MOTOR:
// 步进电机驱动器电源控制
// 电机睡眠模式
break;
case HW_MODULE_GYROSCOPE:
// 陀螺仪电源控制
// 设置陀螺仪为低功耗模式
break;
case HW_MODULE_TOUCH:
// 触摸传感器电源控制
// 设置触摸传感器为睡眠模式
break;
case HW_MODULE_RADAR:
// 毫米波雷达电源控制
// 设置雷达为低功耗模式
break;
case HW_MODULE_IO_EXPAND:
// IO扩展板电源控制
// 设置扩展板为低功耗模式
break;
default:
ESP_LOGE(POWER_TAG, "Unknown hardware module: %d", module);
return ESP_ERR_INVALID_ARG;
}
if (ret == ESP_OK) {
ESP_LOGI(POWER_TAG, "Module %d power %s successfully", module, enable ? "enabled" : "disabled");
} else {
ESP_LOGE(POWER_TAG, "Failed to %s module %d: %s", enable ? "enable" : "disable", module, esp_err_to_name(ret));
}
return ret;
}
@@ -0,0 +1,171 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include "config.h"
#include "io_expansion_board.h"
#include "stepper_motor.h"
#include "stepper_driver.h"
#include "i2c_bus.h"
#include "gyroscope.h"
#include "touch_sensor.h"
#include "base_board_control.h"
#include "base_board_hal_uart.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
/**
* @brief 功耗模式枚举
*/
typedef enum {
POWER_MODE_NORMAL, // 正常运行模式,所有硬件可用
POWER_MODE_IDLE, // 空闲模式,关闭非必要外设
POWER_MODE_LIGHT_SLEEP, // 轻度睡眠,快速唤醒
POWER_MODE_DEEP_SLEEP, // 深度睡眠,仅RTC运行
} power_mode_t;
/**
* @brief 硬件模块枚举,用于精细化电源管理
*/
typedef enum {
HW_MODULE_I2C, // I2C总线
HW_MODULE_SDCARD, // SD卡
HW_MODULE_LCD, // LCD显示
HW_MODULE_AUDIO, // 音频编解码器
HW_MODULE_MOTOR, // 步进电机
HW_MODULE_GYROSCOPE, // 陀螺仪
HW_MODULE_TOUCH, // 触摸传感器
HW_MODULE_RADAR, // 毫米波雷达
HW_MODULE_IO_EXPAND, // IO扩展板
} hardware_module_t;
/**
* @brief 硬件板级管理器
*
* 采用高内聚低耦合设计原则,统一管理所有硬件资源。
* 提供统一的硬件初始化接口和资源访问,避免各模块重复初始化。
*/
class BoardSweekar
{
private:
i2c_bus_handle_t bus_handle_ = nullptr; // 使用ESP-ADF的I2C总线句柄
esp_periph_set_handle_t periph_set_ = nullptr; // 外设管理句柄
bool is_sdcard_initialized_ = false; // SD卡初始化状态
power_mode_t current_power_mode_ = POWER_MODE_NORMAL; // 当前功耗模式
uint32_t power_state_flags_ = 0xFFFFFFFF; // 各模块电源状态位图(1为开启)
public:
BoardSweekar();
~BoardSweekar();
IOExpansionBoard io_expansion_; // IO扩展板对象
Gyroscope gyroscope_; // 陀螺仪对象
TouchSensor touch_sensor_; // 触摸传感器对象
BaseBoardHalUart base_board_hal_uart_; // 底座控制HAL对象
BaseBoardControl base_board_control_; // 底座控制对象
StepperDriver* stepper_driver_ = nullptr; // 步进电机驱动对象
StepperDriver* GetStepperDriver() const { return stepper_driver_; }
// 初始化步进电机
void StepperMotorInit(void);
// 初始化LCD IO引脚(消除与步进电机的耦合)
void LcdIoInit(void);
// 初始化串口
void MillimeterWaveRadarUartInit(void);
//初始化毫米波雷达的中断引脚
void MillimeterWaveRadarGpioInit(void);
// 初始化I2C总线
esp_err_t I2CMasterInit(void);
// 初始化VBAT toVBAT_5V引脚
void VbatToVbat5vInit(void);
// 初始化底座控制串口
void BaseBoardControlUartInit(void);
// 初始化SD卡(推荐使用统一的硬件初始化)
esp_err_t InitializeSdcard(void);
// 获取SD卡初始化状态
bool IsSdcardInitialized() const { return is_sdcard_initialized_; }
// 获取外设管理句柄(供需要访问SD卡的模块使用)
esp_periph_set_handle_t GetPeriphSet() const { return periph_set_; }
// 获取I2C总线句柄
i2c_bus_handle_t GetBusHandle() const { return bus_handle_; }
/**
* @brief 统一的硬件初始化接口
*
* 按照依赖关系顺序初始化所有硬件资源,确保各模块可以安全使用。
* 推荐在主程序启动时调用此函数,而不是分别调用各个初始化函数。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardware(void);
/**
* @brief 完整的统一硬件初始化接口(专为复杂应用优化)
*
* 包含所有必要外设的完整初始化,适用于demo5等复杂应用场景。
* 这个函数会初始化:I2C总线、IO扩展板、SD卡、LCD IO、底座控制等。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardwareComplete(void);
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置,优化启动流程。
*
* @param app_type 应用类型(demo5、音频播放器、显示应用等)
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeHardwareForApplication(int app_type);
/**
* @brief 功耗管理接口 - 进入低功耗模式
*
* 根据指定的功耗模式,关闭非必要硬件、设置GPIO为低功耗状态、
* 调整时钟频率等。支持多种功耗模式以适应不同应用场景。
*
* @param mode 功耗模式
* @return esp_err_t 操作结果
*/
esp_err_t EnterLowPowerMode(power_mode_t mode);
/**
* @brief 功耗管理接口 - 退出低功耗模式
*
* 从低功耗模式恢复,重新初始化必要的硬件资源。
*
* @return esp_err_t 操作结果
*/
esp_err_t ExitLowPowerMode(void);
/**
* @brief 获取当前功耗模式
*
* @return power_mode_t 当前功耗模式
*/
power_mode_t GetPowerMode(void) const { return current_power_mode_; }
/**
* @brief 设置硬件模块电源状态
*
* 单独控制某个硬件模块的电源状态,用于精细化的功耗管理。
*
* @param module 硬件模块
* @param enable true为开启,false为关闭
* @return esp_err_t 操作结果
*/
esp_err_t SetModulePower(hardware_module_t module, bool enable);
};
#endif
@@ -0,0 +1,186 @@
/**
* @file board_sweekar_complete_init.cc
* @brief 完整的统一硬件初始化实现
*
* 这个文件提供了完整的统一硬件初始化函数,包含所有必要的硬件初始化步骤。
* 专为demo5等复杂应用场景设计。
*/
/**
* @brief 完整的统一硬件初始化接口(专为demo5优化)
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备依赖)
* 2. IO扩展板(控制其他外设)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. LCD IO引脚(显示相关)
* 5. 底座控制串口(通信)
* 6. 底座控制模块(业务逻辑)
* 7. 其他外设(根据应用需求)
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardwareComplete(void)
{
static const char *HW_INIT_TAG = "HW_INIT_COMPLETE";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting complete hardware initialization...");
// 1. 初始化I2C总线(最基础,其他设备依赖)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "✓ I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "✓ I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "⚠ SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "✓ SD card initialized");
}
// 4. 初始化LCD IO引脚(显示相关)
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "✓ LCD IO pins initialized");
// 5. 初始化底座控制串口(demo5需要)
BaseBoardControlUartInit();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control UART initialized");
// 6. 初始化底座控制模块
base_board_control_.Init();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control initialized");
// 7. 设置IO扩展板的PA控制模式(demo5需要)
io_expansion_.SetPaCtrlMode(true);
ESP_LOGI(HW_INIT_TAG, "✓ IO expansion PA control mode set");
// 8. 可选外设(根据具体应用需求启用)
// ESP_LOGI(HW_INIT_TAG, "Initializing optional peripherals...");
//
// // 毫米波雷达(如需要)
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Millimeter wave radar initialized");
//
// // VBAT升压(如需要)
// VbatToVbat5vInit();
// ESP_LOGI(HW_INIT_TAG, "✓ VBAT to 5V booster initialized");
//
// // 步进电机(如需要)
// StepperMotorInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Stepper motor initialized");
ESP_LOGI(HW_INIT_TAG, "=== Complete hardware initialization finished successfully ===");
return ESP_OK;
}
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置
*/
esp_err_t BoardSweekar::InitializeHardwareForApplication(application_type_t app_type) {
static const char *APP_INIT_TAG = "APP_HW_INIT";
ESP_LOGI(APP_INIT_TAG, "Initializing hardware for application type: %d", app_type);
// 基础硬件初始化(所有应用都需要)
esp_err_t ret = InitializeAllHardwareComplete();
if (ret != ESP_OK) {
ESP_LOGE(APP_INIT_TAG, "Basic hardware initialization failed");
return ret;
}
switch (app_type) {
case APP_TYPE_DEMO5: // 宠物游戏demo
ESP_LOGI(APP_INIT_TAG, "Configuring for Demo5 (Pet Game)...");
// Demo5需要的额外初始化
// 触摸传感器、陀螺仪、RGB LED等会在应用层初始化
ESP_LOGI(APP_INIT_TAG, "✓ Demo5 hardware configuration complete");
break;
case APP_TYPE_AUDIO_PLAYER: // 音频播放器
ESP_LOGI(APP_INIT_TAG, "Configuring for Audio Player...");
// 音频应用需要确保音频编解码器正确初始化
// 这部分通常在AudioPlayer类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Audio player hardware configuration complete");
break;
case APP_TYPE_DISPLAY_ONLY: // 纯显示应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Display Only...");
// 显示应用需要确保LCD相关硬件正确配置
// 这部分通常在Display类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Display-only hardware configuration complete");
break;
case APP_TYPE_MINIMAL: // 最小化应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Minimal Application...");
// 最小化配置,只保留最基本的功能
// 关闭非必要外设以节省功耗
ESP_LOGI(APP_INIT_TAG, "✓ Minimal hardware configuration complete");
break;
default:
ESP_LOGW(APP_INIT_TAG, "Unknown application type, using default configuration");
break;
}
return ESP_OK;
}
/**
* @brief 硬件初始化状态报告
*
* 提供详细的硬件初始化状态报告,便于调试和问题排查
*/
void BoardSweekar::ReportHardwareStatus(void) {
static const char *STATUS_TAG = "HW_STATUS";
ESP_LOGI(STATUS_TAG, "=== Hardware Initialization Status Report ===");
// I2C总线状态
ESP_LOGI(STATUS_TAG, "I2C Bus: %s", (bus_handle_ != nullptr) ? "✓ Initialized" : "✗ Not initialized");
// SD卡状态
ESP_LOGI(STATUS_TAG, "SD Card: %s", (is_sdcard_initialized_) ? "✓ Initialized" : "✗ Not initialized");
if (is_sdcard_initialized_) {
ESP_LOGI(STATUS_TAG, " Peripheral Set: %s", (periph_set_ != nullptr) ? "✓ Available" : "✗ Not available");
}
// IO扩展板状态
ESP_LOGI(STATUS_TAG, "IO Expansion Board: %s", (io_expansion_.bus_handle_ != nullptr) ? "✓ Configured" : "✗ Not configured");
// 底座控制状态
ESP_LOGI(STATUS_TAG, "Base Board Control: %s", (base_board_control_.GetUartPort() != 0) ? "✓ Initialized" : "✗ Not initialized");
// 功耗管理状态
ESP_LOGI(STATUS_TAG, "Power Management: %s", (current_power_mode_ == POWER_MODE_NORMAL) ? "✓ Normal mode" : "⚠ Low power mode");
ESP_LOGI(STATUS_TAG, " Current Mode: %d", current_power_mode_);
ESP_LOGI(STATUS_TAG, " Module Power States: 0x%08X", power_state_flags_);
// 内存状态
ESP_LOGI(STATUS_TAG, "Memory Status:");
ESP_LOGI(STATUS_TAG, " Internal RAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
ESP_LOGI(STATUS_TAG, " PSRAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
ESP_LOGI(STATUS_TAG, "=== End of Hardware Status Report ===");
}
@@ -0,0 +1,132 @@
/* 定义引脚分配 */
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
/**
* @file config.h
* @brief 定义了引脚的分配。
* @author zouyuhua
* @date 2025年8月7日
*
*/
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_rom_sys.h" // 用于 esp_rom_delay_us
#include "esp_timer.h"
#include "esp_mac.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/timer.h"
#include "esp_intr_alloc.h"
#include "soc/timer_group_struct.h" // 包含TIMERG0、TIMERG1的结构体定义
#include "soc/timer_group_reg.h" // 包含定时器相关寄存器宏定义
#include "driver/uart.h"
#include "esp_err.h"
#include "freertos/queue.h"
#include "soc/uart_reg.h"
#include "linear_motor.h"
#include "gyroscope.h"
#define TAG_BOARD "SWEEKAR_ESP32"
/*I2C总线引脚定义*/
#define I2C0_MASTER_PORT I2C_NUM_0
#define I2C0_MASTER_SDA_IO GPIO_NUM_2
#define I2C0_MASTER_SCL_IO GPIO_NUM_3
/* 步进电机配置 */
#define STEPPER_STEP_IO GPIO_NUM_42 // 步进电机脉冲输入引脚
#define STEPPER_TIMER_RES_HZ 1000000 // 步进电机定时器分辨率 1 MHz(1 us 步进)
/* 定义屏幕驱动控制引脚 */
#define ST7789_PIN_CLK GPIO_NUM_48 // SPI时钟引脚
#define ST7789_PIN_DC GPIO_NUM_38 // 数据/命令选择引脚RSEL
#define ST7789_PIN_MOSI GPIO_NUM_47 // SPI数据输出引脚
#define ST7789_PIN_CS GPIO_NUM_46 // SPI片选引脚
#define ST7789_PIN_BL GPIO_NUM_45 // 背光引脚
#define ST7789_PIN_RST GPIO_NUM_NC // 复位引脚
#define LCD_BL_ACTIVE_LEVEL 0 // 背光有效电平:1=高电平有效(NMOS),0=低电平有效(PMOS)
#define LCD_WIDTH 172 // 屏幕宽度(水平方向像素数)
#define LCD_HEIGHT 320 // 屏幕高度(垂直方向像素数)
/* 毫米波雷达配置参数 */
/* 定义毫米波雷达的中断引脚 */
#define MILLIMETER_WAVE_RADAR_PIN GPIO_NUM_35
// UART 配置参数
#define RADAR_UART_NUM UART_NUM_1 // 使用 UART1
#define UART_TX_PIN GPIO_NUM_40 // GPIO40 作为 TX(根据实际硬件连接调整)
#define UART_RX_PIN GPIO_NUM_39 // GPIO39 作为 RX
#define RADAR_BAUD_RATE 9600 // 雷达波特率
#define RADAR_BUF_SIZE 100 // 雷达接收缓冲区大小
/* IO扩展硬件配置常量 */
#define I2C_MASTER_FREQ_HZ 100000 // I2C通信频率(100kHz)(可更改至400000)
#define I2C_IO_EXPAND_SLAVE_ADDR 0x58 // AW9523BTQR器件地址,v1_1_13的addr1和addr0是0
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // 中断检测引脚
#define ESP_INTR_FLAG_DEFAULT 0 // 中断标志默认值
#define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/* 定义线性马达的I2C从机配置常量 */
#define LINEAR_MOTOR_I2C_SLAVE_ADDR 0x5A // 线性马达的I2C从机地址
/* 定义陀螺仪的I2C从机配置常量 */
#define GYROSCOPE_I2C_SLAVE_ADDR 0x6A // 陀螺仪的I2C从机地址
#define GYROSCOPE_INPUT_PIN_SEL GPIO_NUM_21 // 陀螺仪中断引脚
/* 原生触摸引脚配置 */
// 触摸按键数量配置
#define TOUCH_BUTTON_NUM 3
// 触摸通道定义
#define TOUCH_PAD_CHANNEL_1 TOUCH_PAD_NUM11 // GPIO11
#define TOUCH_PAD_CHANNEL_2 TOUCH_PAD_NUM12 // GPIO12
#define TOUCH_PAD_CHANNEL_3 TOUCH_PAD_NUM13 // GPIO13
/* ES8311音频编解码器配置 (ESP-ADF兼容) */
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
/* I2S GPIO定义 */
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_15
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
/* PA引脚定义 */
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
/* I2C配置 */
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_2
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_3
#define AUDIO_CODEC_ES8311_ADDR 0x18
#define AUDIO_CODEC_ES7210_ADDR 0x40
/* WS2812 RGB LED配置 */
#define WS2812_PIN GPIO_NUM_41 // WS2812数据引脚
#define WS2812_NUM_LEDS 2 // WS2812 LED数量
/* 创建VBAT to VBAT_5V */
#define VBAT_5V_PIN GPIO_NUM_NC // VBAT_5V引脚
/* 定义按键的中断引脚 */
#define BUTTON_PIN GPIO_NUM_0 // 按键中断引脚
#define RIGHT_BUTTON_PIN GPIO_NUM_8 // 按键中断引脚
/* 底座控制命令串口配置 */
#define BASE_BOARD_CTL_UART_PORT UART_NUM_0
#define BASE_BOARD_CTL_UART_TX_PIN GPIO_NUM_43
#define BASE_BOARD_CTL_UART_RX_PIN GPIO_NUM_44
#define BASE_BOARD_CTL_UART_BAUDRATE 115200
#define BASE_BOARD_CTL_UART_BUF_SIZE 256
/* 背光控制引脚配置 */
#define BL_PIN GPIO_NUM_45
#endif // _BOARD_CONFIG_H_
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,421 @@
#ifndef IO_EXPANSION_BOARD_H
#define IO_EXPANSION_BOARD_H
#include <cstdint>
#include <esp_log.h>
#include <esp_check.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include <functional>
#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 "portmacro.h"
// 前向声明,避免循环引用
class MillimeterWaveRadar;
class PhotoInterrupter;
class AdcBatteryMonitor;
// 回调函数类型定义
typedef std::function<void()> RadarCallback;
typedef std::function<void()> PhotoInterrupterStartCallback;
typedef std::function<void()> PhotoInterrupterEndCallback;
typedef std::function<void()> BatteryChargingCallback;
typedef std::function<void()> BatteryFullCallback;
typedef std::function<void()> BatteryNotChargingCallback;
/* 寄存器地址 */
#define INPUT_PORT0 0x00 // 输入端口0寄存器
#define INPUT_PORT1 0x01 // 输入端口1寄存器 0为低电平(仅支持读取)
#define OUTPUT_PORT0 0x02 // 输出端口0寄存器
#define OUTPUT_PORT1 0x03 // 输出端口1寄存器 0为低电平
#define CONFIG_PORT0 0x04 // 配置端口0寄存器
#define CONFIG_PORT1 0x05 // 配置端口1寄存器 0为输出模式
#define INT_PORT0 0x06 // 中断端口0寄存器
#define INT_PORT1 0x07 // 中断端口1寄存器 0为使能中断(根据手册默认是使能的)
#define CTL 0x11 // 全局控制寄存器
#define ID 0x23 // 芯片ID寄存器(0x23)
/***********************************IO扩展板电机引脚配置参数*************************************/
/* 设置步进电机方向 */
#define STEPPER_MOTOR_LEFT false //步进电机的方向为左转
#define STEPPER_MOTOR_RIGHT true //步进电机的方向为右转
#define STEPPER_MOTOR_DIR_PORT OUTPUT_PORT0 //步进电机方向引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_DIR_PIN 4 //步进电机方向引脚对应的IO扩展板引脚P0_4
#define STEPPER_MOTOR_DIR_CONFIG CONFIG_PORT0 //步进电机方向引脚配置在P0_4
/* 设置步进电机细分模式 */
#define STEPPER_MOTOR_M0_PIN 5 //步进电机细分模式选择引脚对应IO扩展板引脚P1_5
#define STEPPER_MOTOR_M0_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M0_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_5
#define STEPPER_MOTOR_M1_PIN 4 //步进电机细分模式选择引脚对应IO扩展板引脚P1_4
#define STEPPER_MOTOR_M1_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M1_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_4
/* 设置步进电机睡眠引脚 */
#define STEPPER_MOTOR_SLEEP false //步进电机进入睡眠模式
#define STEPPER_MOTOR_WORK true //步进电机进入工作模式
#define STEPPER_MOTOR_SLEEP_PIN 6 //步进电机睡眠引脚对应IO扩展板引脚P1_6
#define STEPPER_MOTOR_SLEEP_CONFIG CONFIG_PORT1 //步进电机睡眠引脚配置在P1_6
#define STEPPER_MOTOR_SLEEP_PORT OUTPUT_PORT1 //步进电机睡眠引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置电机的使能引脚 */
#define STEPPER_MOTOR_ENABLE_PIN 7 //电机使能引脚对应IO扩展板引脚P0_7
#define STEPPER_MOTOR_ENABLE_PORT OUTPUT_PORT0 //电机使能引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_ENABLE_CONFIG CONFIG_PORT0 //电机使能引脚配置在P0_7
#define STEPPER_MOTOR_ENABLE_ENABLE true //电机使能引脚状态为高电平
#define STEPPER_MOTOR_ENABLE_DISABLE false //电机使能引脚状态为低电平
/* 设置毫米波雷达对应IO扩展板的中断引脚 */
#define RADAR_INTERRUPT_PIN 7 //毫米波雷达中断引脚对应IO扩展板引脚P1_7
#define RADAR_INTERRUPT_CONFIG CONFIG_PORT1 //毫米波雷达中断引脚挂载在IO扩展板的输入端口
#define RADAR_INTERRUPT_INPUT INPUT_PORT1 //毫米波雷达中断引脚输入状态检测在INPUT_PORT1
#define RADAR_INTERRUPT_ENABLE true //毫米波雷达中断引脚状态为输入状态
#define RADAR_INTERRUPT_DISABLE false //毫米波雷达中断引脚状态为输出状态
/* 任务配置常量 */
#define WRITE_BUF_SIZE 2 // I2C单次写入字节数
#define READ_BUF_SIZE 2 // I2C单次读取字节数
#define I2C_TASK_PRIORITY 2 // 任务优先级(中等)
#define I2C_TASK_STACK 4096 // 任务栈大小(4KB)
// 光学传感器引脚定义
#define START_PHOTO_INTERRUPTER_PIN 2 // 光学传感器引脚(P0_2)
#define START_PHOTO_INTERRUPTER_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define START_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define START_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define START_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
#define END_PHOTO_INTERRUPTER_PIN 3 // 光学传感器引脚(P0_3)
#define END_PHOTO_INTERRUPTER_PORT_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define END_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define END_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define END_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
/***********************************IO扩展板LCD引脚配置参数*************************************/
// /* 设置LCD的BL引脚 */
// #define LCD_BL_PIN 6 //LCD背光引脚对应IO扩展板引脚P0_6
// #define LCD_BL_PORT OUTPUT_PORT0 //LCD背光引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
// #define LCD_BL_CONFIG CONFIG_PORT0 //LCD背光引脚配置在P0_6
// #define LCD_BL_HIGH true //LCD背光引脚状态为高电平
// #define LCD_BL_LOW false //LCD背光引脚状态为低电平
/* 设置LCD的RST引脚 */
#define LCD_RST_PIN 1 //LCD复位引脚对应IO扩展板引脚P1_1
#define LCD_RST_CONFIG CONFIG_PORT1 //LCD复位引脚配置在P1_1
#define LCD_RST_PORT OUTPUT_PORT1 //LCD复位引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_RST_HIGH true //LCD复位引脚状态为高电平
#define LCD_RST_LOW false //LCD复位引脚状态为低电平
/* 设置PA控制引脚 */
#define PA_CTRL_PIN 2 //PA控制引脚对应IO扩展板引脚P1_2
#define PA_CTRL_CONFIG CONFIG_PORT1 //PA控制引脚配置在P1_2
#define PA_CTRL_PORT OUTPUT_PORT1 //PA控制引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define PA_CTRL_HIGH true //PA控制引脚状态为高电平
#define PA_CTRL_LOW false //PA控制引脚状态为低电平
// 电池充电检测引脚定义
#define BATTERY_FULL_INPUT INPUT_PORT1 // 电池充满检测引脚挂载在IO扩展板的输入端口
#define BATTERY_FULL_PIN 3 // 电池充满检测引脚对应IO扩展板引脚P1_3
#define BATTERY_FULL_CONFIG CONFIG_PORT1 // 电池充满检测引脚配置在P1_3
#define BATTERY_CHRG_INPUT INPUT_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define BATTERY_CHRG_PIN 5 // 充电检测引脚(P0_5)
#define BATTERY_CHRG_CONFIG CONFIG_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define TAG_GPIO_IO_EXPAND_DETECT "GPIO_IO_EXPAND_DETECT" // GPIO中断检测日志标签
#define TAG_I2C_IO_EXPAND_TASK "I2C_IO_EXPAND_TASK" // I2CIO扩展板任务日志标签
// GPIO引脚定义
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // IO扩展板中断检测引脚
// #define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/**
* @class IOExpansionBoard
* @brief I/O扩展板驱动类,封装AW9523BTQR芯片操作
*/
class IOExpansionBoard
{
public:
/**
* @brief 构造函数
*/
IOExpansionBoard();
/**
* @brief 设置毫米波雷达指针
* @param radar 毫米波雷达对象指针
*/
void SetRadar(MillimeterWaveRadar* radar) { radar_ = radar; };
/**
* @brief 设置限位开关指针
* @param photo_interrupter 限位开关对象指针
*/
void setPhotoInterrupter(PhotoInterrupter* photo_interrupter) { photo_interrupter_ = photo_interrupter; };
/**
* @brief 设置电池监控器指针
* @param battery_monitor 电池监控器对象指针
*/
void setBatteryMonitor(AdcBatteryMonitor* battery_monitor) { battery_monitor_ = battery_monitor; };
/**
* @brief 析构函数
*/
~IOExpansionBoard();
/**
* @brief 初始化GPIO中断检测
* @param gpio_num 中断引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetIoExpand();
/**
* @brief 初始化I2C主机通信
* @return esp_err_t 初始化结果
*/
// esp_err_t IoExpansionI2cInit();
/**
* @brief 创建I2C数据读取任务
*/
void CreateI2cReadTask();
/**
* @brief 启动I2C任务(整合初始化和任务创建)
* @return esp_err_t 操作结果
*/
esp_err_t StartTask();
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandWriteReg(uint8_t reg_addr, uint8_t data);
/**
* @brief 读寄存器数据
* @param reg_addr 寄存器地址
* @param data 要读出的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandReadReg(uint8_t reg_addr, uint8_t *data);
/**
* @brief 读取输入端口0寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort0(uint8_t *data);
/**
* @brief 读取输入端口1寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort1(uint8_t *data);
// /**
// * @brief 设置IO扩展板GPIO中断状态 通过读取Input_Port0 和 Input_Port1 寄存器可以清除中断
// * @param reg_addr 寄存器地址
// * @return esp_err_t 操作结果
// */
// esp_err_t SetInterruptStatus(bool status);
/**
* @brief 配置IO扩展版上的IO口为输出还是输入,并且设置初始为高电平
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandSetMode();
/**
* @brief 设置步进电机的细分模式
* @param mode 模式:1/2/4/8/16/32/64
* @return esp_err_t 操作结果
*/
void SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT_m0, uint8_t m0_pin, uint8_t OUTPUT_PORT_m1, uint8_t m1_pin);
/**
* @brief 设置步进电机的方向
* @param dir 方向
* @param OUTPUT_PORT 输出端口
* @param pin 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin);
/**
* @brief 设置步进电机的睡眠模式
* @param 是否进入睡眠模式
* @param 输出端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin);
/**
* @brief 设置步进电机的使能引脚状态
* @param 使能状态
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin);
/**
* @brief 设置LCD的BL引脚状态
* @param BL状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin);
esp_err_t SetLedEnMode(bool mode);
/**
* @brief 设置LCD的RST引脚状态
* @param RST状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t rst_pin);
/**
* @brief 设置PA控制引脚状态
* @param mode PA_CTRL状态(true为高电平,false为低电平)
* @return esp_err_t 操作结果
*/
esp_err_t SetPaCtrlMode(bool mode);
/**
* @brief 设置雷达中断引脚
* @param 毫米波雷达的中断引脚模式
* @param 输入端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin);
/**
* @brief 计算Port0的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort0();
/**
* @brief 计算Port1的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort1();
/**
* @brief 设置雷达中断回调函数
* @param callback 雷达中断回调函数
*/
void SetRadarCallback(RadarCallback callback) { radar_callback_ = callback; }
/**
* @brief 设置光学传感器开始中断回调函数
* @param callback 光学传感器开始中断回调函数
*/
void SetPhotoInterrupterStartCallback(PhotoInterrupterStartCallback callback) { photo_start_callback_ = callback; }
/**
* @brief 设置光学传感器结束中断回调函数
* @param callback 光学传感器结束中断回调函数
*/
void SetPhotoInterrupterEndCallback(PhotoInterrupterEndCallback callback) { photo_end_callback_ = callback; }
/**
* @brief 设置电池充电中断回调函数
* @param callback 电池充电中断回调函数
*/
void SetBatteryChargingCallback(BatteryChargingCallback callback) { battery_charging_callback_ = callback; }
/**
* @brief 设置电池充满中断回调函数
* @param callback 电池充满中断回调函数
*/
void SetBatteryFullCallback(BatteryFullCallback callback) { battery_full_callback_ = callback; }
/**
* @brief 设置电池非充电中断回调函数
* @param callback 电池非充电中断回调函数
*/
void SetBatteryNotChargingCallback(BatteryNotChargingCallback callback) { battery_not_charging_callback_ = callback; }
// 公共成员变量
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
uint8_t device_addr_; // I2C设备地址
private:
// 静态成员函数
MillimeterWaveRadar* radar_ = nullptr; // 毫米波雷达指针,用于触发回调
PhotoInterrupter* photo_interrupter_ = nullptr; // 限位开关指针,用于触发回调
AdcBatteryMonitor* battery_monitor_ = nullptr; // 电池监控器指针,用于触发回调
static void IRAM_ATTR GpioIsrHandler(void* arg); // 中断处理函数
static void I2cReadTask(void* pvParameters); // I2C数据读取任务函数
// 成员变量用于状态跟踪
uint8_t last_port0_state_ = 0; // 上次检测到的Port0状态
uint8_t last_port1_state_ = 0; // 上次检测到的Port1状态
bool radar_last_state_ = false; // 雷达引脚上一次的状态
bool battery_charging_last_state_ = false; // 电池充电状态上一次的状态
bool battery_full_last_state_ = false; // 电池充满状态上一次的状态
// 回调函数成员变量
RadarCallback radar_callback_ = nullptr;
PhotoInterrupterStartCallback photo_start_callback_ = nullptr;
PhotoInterrupterEndCallback photo_end_callback_ = nullptr;
BatteryChargingCallback battery_charging_callback_ = nullptr;
BatteryFullCallback battery_full_callback_ = nullptr;
BatteryNotChargingCallback battery_not_charging_callback_ = nullptr;
// PhotoInterrupter* photo_interrupter_ = nullptr; // 光学传感器指针,用于触发回调
// /**
// * @brief 分配静态任务所需的内存
// * @return bool 分配是否成功
// */
// bool AllocateStaticTaskMemory();
// /**
// * @brief 释放静态任务分配的内存
// */
// void FreeStaticTaskMemory();
// 实例成员变量
EventGroupHandle_t i2c_task_event_group_ = nullptr; // 事件组
TaskHandle_t xI2CReadTaskHandle_ = nullptr; // 任务句柄
bool is_initialized_ = false; // 初始化状态标记(新增)
gpio_num_t interrupt_gpio_ = GPIO_NUM_NC; // 中断引脚
gpio_num_t reset_gpio_ = GPIO_NUM_NC; // 复位引脚
// StackType_t* pI2CReadTaskStackBuffer = nullptr; // 任务栈指针(PSRAM)
// StaticTask_t* pxI2CReadTaskBuffer = nullptr; // 任务控制块指针(内部RAM)
};
#endif
@@ -0,0 +1,178 @@
/**
* @file power_management_config.h
* @brief 功耗管理配置和策略定义
* @author Embedded Software Expert
* @date 2025年1月
*
* 这个文件定义了功耗管理的配置参数、策略和优化建议,
* 为系统提供灵活的功耗管理方案。
*/
#ifndef POWER_MANAGEMENT_CONFIG_H
#define POWER_MANAGEMENT_CONFIG_H
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "esp32/rom/rtc.h"
/**
* @brief 功耗管理配置结构体
*/
typedef struct {
// 睡眠模式配置
uint64_t light_sleep_duration_us; // 轻度睡眠持续时间(微秒)
uint64_t deep_sleep_duration_us; // 深度睡眠持续时间(微秒)
// 唤醒源配置
bool enable_timer_wakeup; // 启用定时器唤醒
bool enable_gpio_wakeup; // 启用GPIO唤醒
bool enable_touch_wakeup; // 启用触摸唤醒
gpio_num_t wakeup_gpio_num; // 唤醒GPIO编号
int wakeup_level; // 唤醒电平(0或1)
// 功耗优化配置
bool disable_bt_in_sleep; // 睡眠时关闭蓝牙
bool disable_wifi_in_sleep; // 睡眠时关闭WiFi
bool reduce_cpu_frequency; // 降低CPU频率
uint32_t cpu_frequency_hz; // CPU频率(Hz)
// 外设功耗配置
bool power_down_sdcard; // 睡眠时关闭SD卡电源
bool power_down_display; // 睡眠时关闭显示电源
bool power_down_audio; // 睡眠时关闭音频电源
bool power_down_motor; // 睡眠时关闭电机电源
// RTC内存配置
bool preserve_rtc_memory; // 保留RTC内存
size_t rtc_memory_size; // RTC内存大小
} power_management_config_t;
/**
* @brief 默认功耗管理配置
*/
#define DEFAULT_POWER_CONFIG { \
.light_sleep_duration_us = 1000000, /* 1秒 */ \
.deep_sleep_duration_us = 30000000, /* 30秒 */ \
.enable_timer_wakeup = true, /* 启用定时器唤醒 */ \
.enable_gpio_wakeup = false, /* 不启用GPIO唤醒 */ \
.enable_touch_wakeup = false, /* 不启用触摸唤醒 */ \
.wakeup_gpio_num = GPIO_NUM_NC, /* 无GPIO唤醒 */ \
.wakeup_level = 0, /* 低电平唤醒 */ \
.disable_bt_in_sleep = true, /* 睡眠时关闭蓝牙 */ \
.disable_wifi_in_sleep = true, /* 睡眠时关闭WiFi */ \
.reduce_cpu_frequency = true, /* 降低CPU频率 */ \
.cpu_frequency_hz = 80000000, /* 80MHz */ \
.power_down_sdcard = true, /* 睡眠时关闭SD卡 */ \
.power_down_display = true, /* 睡眠时关闭显示 */ \
.power_down_audio = true, /* 睡眠时关闭音频 */ \
.power_down_motor = true, /* 睡眠时关闭电机 */ \
.preserve_rtc_memory = true, /* 保留RTC内存 */ \
.rtc_memory_size = 4096, /* 4KB RTC内存 */ \
}
/**
* @brief 各功耗模式的详细配置
*/
static const power_management_config_t idle_mode_config = {
.light_sleep_duration_us = 500000, // 500ms轻度睡眠
.deep_sleep_duration_us = 0, // 不使用深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0, // 按键唤醒
.wakeup_level = 0,
.disable_bt_in_sleep = false, // 保持蓝牙连接
.disable_wifi_in_sleep = false, // 保持WiFi连接
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000, // 40MHz
.power_down_sdcard = false, // 保持SD卡(可能有后台任务)
.power_down_display = true, // 关闭显示背光
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 2048,
};
static const power_management_config_t light_sleep_config = {
.light_sleep_duration_us = 0, // 直接进入轻度睡眠
.deep_sleep_duration_us = 0,
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000,
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 4096,
};
static const power_management_config_t deep_sleep_config = {
.light_sleep_duration_us = 0,
.deep_sleep_duration_us = 60000000, // 60秒深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 10000000, // 10MHz
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 8192,
};
/**
* @brief 功耗统计和监控结构体
*/
typedef struct {
uint64_t total_sleep_time_us; // 总睡眠时间
uint64_t total_wake_time_us; // 总唤醒时间
uint32_t sleep_count; // 睡眠次数
uint32_t wake_count; // 唤醒次数
uint32_t average_sleep_duration_us; // 平均睡眠时长
uint32_t average_wake_duration_us; // 平均唤醒时长
float estimated_power_saving_percent; // 估计的功耗节省百分比
} power_statistics_t;
/**
* @brief 功耗优化建议
*
* 基于硬件特性和使用模式,提供功耗优化建议
*/
typedef enum {
OPTIMIZATION_NONE = 0,
OPTIMIZATION_REDUCE_CPU_FREQ, // 降低CPU频率
OPTIMIZATION_DISABLE_UNUSED_PERIPHERALS, // 关闭未使用的外设
OPTIMIZATION_USE_LIGHT_SLEEP, // 使用轻度睡眠
OPTIMIZATION_USE_DEEP_SLEEP, // 使用深度睡眠
OPTIMIZATION_OPTIMIZE_GPIO_STATES, // 优化GPIO状态
OPTIMIZATION_USE_RTC_MEMORY, // 使用RTC内存
OPTIMIZATION_BATCH_OPERATIONS, // 批量操作
} power_optimization_t;
/**
* @brief 功耗管理事件回调
*/
typedef void (*power_event_callback_t)(power_mode_t old_mode, power_mode_t new_mode, void* user_data);
/**
* @brief 功耗管理事件
*/
typedef enum {
POWER_EVENT_MODE_CHANGE, // 功耗模式改变
POWER_EVENT_SLEEP_START, // 睡眠开始
POWER_EVENT_SLEEP_END, // 睡眠结束
POWER_EVENT_WAKEUP, // 系统唤醒
POWER_EVENT_OPTIMIZATION_TRIGGER, // 功耗优化触发
} power_event_t;
#endif // POWER_MANAGEMENT_CONFIG_H
@@ -0,0 +1,738 @@
/**
* @file board.cc
* @brief 板级外设初始化类,包含步进电机、毫米波雷达、I2C总线等外设的初始化函数。
* @author zouyuhua
* @date 2025年8月7日
*
*/
extern "C" {
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "driver/gptimer.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/uart_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2c_bus.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
}
#include "board_sweekar.h"
#include "config.h"
#include "board.h" // 添加board.h以获取audio_board_sdcard_init函数声明
#include "drv8428.h"
#include "aw8646.h"
#define TAG "board_sweekar"
BoardSweekar::BoardSweekar()
{
#ifdef CONFIG_STEPPER_DRIVER_AW8646
stepper_driver_ = new Aw8646Driver(io_expansion_);
#else
stepper_driver_ = new Drv8428Driver(io_expansion_);
#endif
// Initialize Shutdown Control Pin (GPIO_NUM_13) and pull it HIGH
// This is required to keep the main power on.
#ifdef SHUTDOWN_CTRL_PIN
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << SHUTDOWN_CTRL_PIN);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
gpio_set_level(SHUTDOWN_CTRL_PIN, 1);
ESP_LOGI(TAG, "Initialized SHUTDOWN_CTRL_PIN (%d) to HIGH", SHUTDOWN_CTRL_PIN);
#endif
}
BoardSweekar::~BoardSweekar()
{
if (stepper_driver_) {
delete stepper_driver_;
stepper_driver_ = nullptr;
}
}
/*========================== 步进电机相关驱动 ==========================*/
/**
* @brief 步进电机初始化函数
* 配置步进电机所需的GPIO引脚模式,通过IO扩展板设置步进驱动芯片的工作模式
* 在 stepper_motor.cc 中完成 GPTimer + GPIO 翻转 STEP 脉冲的初始化
*/
void BoardSweekar::StepperMotorInit(void)
{
esp_err_t err = ESP_OK;
// 1) STEP 引脚改为普通 GPIO(不再用 LEDC)
gpio_config_t io_cfg;
io_cfg.pin_bit_mask = 1ULL << STEPPER_STEP_IO;
io_cfg.mode = GPIO_MODE_OUTPUT;
io_cfg.pull_up_en = GPIO_PULLUP_DISABLE;
io_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_cfg.intr_type = GPIO_INTR_DISABLE;
err = gpio_config(&io_cfg);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机 STEP GPIO 配置失败, 错误码: 0x%x", err);
}
// 初始电平拉低,保证上电不乱跳
gpio_set_level(STEPPER_STEP_IO, 0);
// 2) 检查 I2C 总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 3) 初始化驱动对象并初始化
if (stepper_driver_) {
err = stepper_driver_->Init();
if (err != ESP_OK) {
ESP_LOGE(TAG_BOARD, "步进电机驱动初始化失败, 错误码: 0x%x", err);
}
} else {
ESP_LOGE(TAG_BOARD, "步进电机驱动创建失败");
}
}
/**
* @brief LCD IO引脚初始化函数(消除与步进电机的耦合)
*
* 配置LCD所需的IO扩展板寄存器,确保LCD复位引脚正常工作
* 此函数独立于步进电机初始化,专门处理LCD相关的IO配置
*/
void BoardSweekar::LcdIoInit(void)
{
esp_err_t err = ESP_OK;
// 1) 检查I2C总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 2) 执行硬件复位,确保IO扩展板处于已知状态
err = io_expansion_.ResetIoExpand();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板硬件复位失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板硬件复位成功");
}
// 3) 配置IO扩展板模式(端口方向等)
err = io_expansion_.IoexpandSetMode();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板模式配置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板模式配置成功");
}
// 4) 初始化LCD复位引脚:设置为高电平
err = io_expansion_.SetLcdResetMode(LCD_RST_HIGH, LCD_RST_PORT, LCD_RST_PIN);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "LCD复位引脚高电平设置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "LCD复位引脚高电平设置成功");
}
}
/*========================== 毫米波雷达相关驱动 ==========================*/
/**
* @brief 毫米波雷达串口初始化函数
*
* 配置毫米波雷达所使用的串口参数(波特率、数据位、校验位、停止位等),设置串口引脚,
* 并安装串口驱动。采用较大的RX/TX FIFO深度以处理突发数据,禁用驱动自带事件队列以避免
* 与自定义中断冲突。
*/
void BoardSweekar::MillimeterWaveRadarUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = RADAR_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,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(RADAR_UART_NUM, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(RADAR_UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动(禁用驱动自带事件队列,避免与自定义中断冲突)
// 增加 RX/TX FIFO 深度以处理突发数据
ESP_ERROR_CHECK(uart_driver_install(RADAR_UART_NUM, RADAR_BUF_SIZE * 4, RADAR_BUF_SIZE * 4,
0, NULL, 0));
}
/**
* @brief 执行系统关机(拉高 SHUTDOWN_CTRL_PIN 切断电源)
*/
void BoardSweekar::SystemShutdown(void)
{
#ifdef SHUTDOWN_CTRL_PIN
ESP_LOGI(TAG, "Executing System Shutdown... Pulling SHUTDOWN_CTRL_PIN (%d) HIGH", SHUTDOWN_CTRL_PIN);
gpio_set_level(SHUTDOWN_CTRL_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(1000));
#else
ESP_LOGW(TAG, "SHUTDOWN_CTRL_PIN is not defined. Cannot execute hardware shutdown.");
#endif
}
/**
* @brief 毫米波雷达中断引脚初始化函数
*
* 配置毫米波雷达中断引脚为输入模式,禁用上拉电阻,使能下拉电阻,初始禁用中断功能
* (如需使用中断需单独进行配置)。
*/
void BoardSweekar::MillimeterWaveRadarGpioInit(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << MILLIMETER_WAVE_RADAR_PIN), // 选择GPIO引脚
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE, // 使能下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断(如需中断需单独配置)
};
// 配置GPIO
gpio_config(&io_conf);
}
/*========================== I2C总线及挂载的从设备相关驱动 ==========================*/
/**
* @brief I2C总线及从机设备初始化函数
*
* 首先配置并创建I2C主机总线,然后依次添加IO扩展板和线性马达作为I2C从机设备到总线上。
* 若总线创建或设备添加失败,会输出错误信息并进行相应的资源清理(删除已创建的总线)。
* 初始化成功后,会通过ESP_LOGI输出IO扩展板和线性马达的从机地址信息。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示相应的失败原因
*/
esp_err_t BoardSweekar::I2CMasterInit(void)
{
static const char *I2C_INIT = "I2C_INIT";
// 初始化IO扩展板复位引脚
gpio_config_t reset_io_conf = {
.pin_bit_mask = (1ULL << IO_EXPAND_RSTN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&reset_io_conf);
// 执行复位时序:先拉低100ms,再拉高并等待100ms
gpio_set_level(IO_EXPAND_RSTN, 0); // 拉低复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms
gpio_set_level(IO_EXPAND_RSTN, 1); // 释放复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
ESP_LOGI(I2C_INIT, "IO扩展板复位完成");
// 创建统一的I2C总线(使用ESP-ADF的i2c_bus)
i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C0_MASTER_SDA_IO,
.scl_io_num = I2C0_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = 100000,
},
};
// 创建I2C总线(ESP-ADF的audio_board_init会复用这个总线)
bus_handle_ = i2c_bus_create(I2C0_MASTER_PORT, &i2c_cfg);
if (bus_handle_ == NULL) {
ESP_LOGE(TAG, "Failed to create I2C bus");
return ESP_FAIL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
// ESP-ADF的i2c_bus不需要单独添加设备,直接通过总线句柄和设备地址进行通信
// 验证线性马达设备是否存在
esp_err_t err = i2c_bus_probe_addr(bus_handle_, LINEAR_MOTOR_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "线性马达设备未找到(地址:0x%02X),错误码: 0x%x", LINEAR_MOTOR_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "线性马达设备检测成功(地址:0x%02X)", LINEAR_MOTOR_I2C_SLAVE_ADDR);
}
// 验证IO扩展板设备是否存在
err = i2c_bus_probe_addr(bus_handle_, I2C_IO_EXPAND_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "IO扩展板设备未找到(地址:0x%02X),错误码: 0x%x", I2C_IO_EXPAND_SLAVE_ADDR, err);
// 不返回错误,继续初始化其他设备
} else {
ESP_LOGI(I2C_INIT, "IO扩展板设备检测成功(地址:0x%02X)", I2C_IO_EXPAND_SLAVE_ADDR);
}
// 验证陀螺仪设备是否存在
err = i2c_bus_probe_addr(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "陀螺仪设备未找到(地址:0x%02X),错误码: 0x%x", GYROSCOPE_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "陀螺仪设备检测成功(地址:0x%02X)", GYROSCOPE_I2C_SLAVE_ADDR);
}
// 为IO扩展板设置总线句柄和设备地址(使用7位地址)
io_expansion_.bus_handle_ = bus_handle_;
io_expansion_.device_addr_ = I2C_IO_EXPAND_SLAVE_ADDR << 1;
// 线性马达的初始化已移至 InitializeAllHardware 中统一调用 Init 方法
// linear_motor_ 的参数传递将在那里完成,不再此处直接赋值,提高内聚性
ESP_LOGI(I2C_INIT, "I2C总线初始化完成");
return ESP_OK;
}
/*========================== VBAT to VBAT_5V相关驱动 ==========================*/
/**
* @brief 开启VBAT to VBAT_5V
*
* 创建开启升压的电源管理单元
*
*/
void BoardSweekar::VbatToVbat5vInit(void)
{
// 配置VBAT_5V引脚为输出模式
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << VBAT_5V_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 使能VBAT_5V
gpio_set_level(VBAT_5V_PIN, 1);
}
/*========================== 底座控制串口相关驱动 ==========================*/
/**
* @brief 底座控制命令串口初始化函数
*/
// 根据配置选择通信方式的HAL头文件
#ifdef CONFIG_BASE_BOARD_COMM_I2C
#include "base_board_hal_i2c.h"
#else
#include "base_board_hal_uart.h"
#endif
void BoardSweekar::BaseBoardControlInit(void)
{
#ifdef CONFIG_BASE_BOARD_COMM_I2C
// I2C Mode
if (bus_handle_ == nullptr) {
ESP_LOGE(TAG, "I2C bus not initialized before BaseBoardControlInit");
return;
}
// 创建 I2C HAL 实例
BaseBoardHalI2c *hal = new BaseBoardHalI2c(bus_handle_);
base_board_control_.Init(hal);
ESP_LOGI(TAG, "BaseBoardControl I2C initialized");
#else
// UART Mode
uart_config_t uart_config = {
.baud_rate = BASE_BOARD_CTL_UART_BAUDRATE,
.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,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(BASE_BOARD_CTL_UART_PORT, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(BASE_BOARD_CTL_UART_PORT, BASE_BOARD_CTL_UART_TX_PIN, BASE_BOARD_CTL_UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(BASE_BOARD_CTL_UART_PORT,
BASE_BOARD_CTL_UART_BUF_SIZE,
BASE_BOARD_CTL_UART_BUF_SIZE,
0, NULL, 0));
BaseBoardHalUart *hal = new BaseBoardHalUart(BASE_BOARD_CTL_UART_PORT);
base_board_control_.Init(hal);
ESP_LOGI(TAG, "BaseBoardControl UART initialized");
#endif
}
/*========================== SD卡相关驱动 ==========================*/
/**
* @brief SD卡初始化函数
*
* 初始化SD卡外设,使用1线模式进行通信。该函数负责初始化外设管理器
* 并配置SD卡,为后续的文件系统操作做准备。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示失败原因
*/
esp_err_t BoardSweekar::InitializeSdcard(void)
{
static const char *SDCARD_TAG = "SDCARD_INIT";
esp_err_t ret;
ESP_LOGI(SDCARD_TAG, "Initializing SD card");
if (is_sdcard_initialized_) {
ESP_LOGW(SDCARD_TAG, "SD card already initialized");
return ESP_OK;
}
// 初始化外设管理
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
periph_cfg.extern_stack = true; // 将任务栈分配到 PSRAM 以节省 Internal SRAM
periph_set_ = esp_periph_set_init(&periph_cfg);
if (!periph_set_) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize peripheral set");
return ESP_FAIL;
}
// 初始化SD卡外设
ret = audio_board_sdcard_init(periph_set_, SD_MODE_1_LINE);
if (ret != ESP_OK) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
return ret;
}
is_sdcard_initialized_ = true;
ESP_LOGI(SDCARD_TAG, "SD card initialized successfully");
return ESP_OK;
}
/**
* @brief 统一的硬件初始化接口实现
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备可能依赖)
* 2. IO扩展板(可能影响其他设备)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. 其他外设
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardware(void)
{
static const char *HW_INIT_TAG = "HW_INIT";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting unified hardware initialization...");
// 1. 初始化I2C总线(最基础)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "SD card initialized");
}
// 4. 初始化LCD IO引脚
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "LCD IO pins initialized");
// 5. 初始化其他必要外设
// BaseBoardControlUartInit(); // 底座控制串口(demo5需要)
// ESP_LOGI(HW_INIT_TAG, "Base board control UART initialized");
// 初始化底座控制
// base_board_control_.Init();
// ESP_LOGI(HW_INIT_TAG, "Base board control initialized");
// 可选外设(根据具体应用需求)
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
StepperMotorInit();
ESP_LOGI(HW_INIT_TAG, "Unified hardware initialization completed successfully");
return ESP_OK;
}
/**
* @brief 进入低功耗模式
*
* 根据指定的功耗模式,执行相应的功耗优化策略:
* - IDLE模式:关闭非必要外设,保持快速响应
* - LIGHT_SLEEP模式:进入轻度睡眠,CPU暂停,外设断电
* - DEEP_SLEEP模式:深度睡眠,仅RTC运行,功耗最低
*/
esp_err_t BoardSweekar::EnterLowPowerMode(power_mode_t mode) {
static const char *POWER_TAG = "POWER_MGMT";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "Entering power mode: %d", mode);
if (mode == current_power_mode_) {
ESP_LOGW(POWER_TAG, "Already in power mode %d", mode);
return ESP_OK;
}
// 保存当前模式,以便恢复
power_mode_t previous_mode = current_power_mode_;
switch (mode) {
case POWER_MODE_NORMAL:
// 正常模式,不需要特殊处理
break;
case POWER_MODE_IDLE:
// 空闲模式:关闭非必要外设
ESP_LOGI(POWER_TAG, "Idle mode: disabling non-essential peripherals");
// 关闭SD卡(如果正在使用,需要先卸载)
if (is_sdcard_initialized_) {
// 注意:这里只是示例,实际应用中需要确保没有文件操作
ESP_LOGW(POWER_TAG, "SD card still active in idle mode");
}
// 关闭显示背光
// 关闭非必要GPIO的上拉/下拉
// 降低I2C时钟频率
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠
ESP_LOGI(POWER_TAG, "Light sleep mode: preparing for sleep");
// 设置唤醒源(需要根据具体需求配置)
// esp_sleep_enable_timer_wakeup(1000000); // 1秒后唤醒
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_XX, 0); // 外部中断唤醒
// 进入轻度睡眠
// ret = esp_light_sleep_start();
if (ret != ESP_OK) {
ESP_LOGE(POWER_TAG, "Failed to enter light sleep: %s", esp_err_to_name(ret));
current_power_mode_ = previous_mode;
return ret;
}
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠
ESP_LOGI(POWER_TAG, "Deep sleep mode: system will restart after wake-up");
// 保存必要的RTC数据
// rtc_gpio_isolate(GPIO_NUM_XX); // 隔离GPIO
// 设置深度睡眠唤醒源
// esp_sleep_enable_timer_wakeup(60000000); // 60秒后唤醒
// 进入深度睡眠
// esp_deep_sleep_start();
// 注意:深度睡眠后系统会重启,不会返回
break;
default:
ESP_LOGE(POWER_TAG, "Invalid power mode: %d", mode);
return ESP_ERR_INVALID_ARG;
}
current_power_mode_ = mode;
ESP_LOGI(POWER_TAG, "Successfully entered power mode: %d", mode);
return ret;
}
/**
* @brief 退出低功耗模式
*
* 从低功耗模式恢复到正常运行模式,重新初始化必要的硬件资源。
*/
esp_err_t BoardSweekar::ExitLowPowerMode(void) {
static const char *POWER_TAG = "POWER_MGMT";
ESP_LOGI(POWER_TAG, "Exiting low power mode");
if (current_power_mode_ == POWER_MODE_NORMAL) {
ESP_LOGW(POWER_TAG, "Already in normal mode");
return ESP_OK;
}
// 根据之前的功耗模式执行相应的恢复操作
switch (current_power_mode_) {
case POWER_MODE_IDLE:
// 恢复空闲模式
ESP_LOGI(POWER_TAG, "Restoring from idle mode");
// 重新初始化必要的外设
// 恢复I2C时钟频率
// 重新开启显示背光
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠唤醒后会自动继续执行
ESP_LOGI(POWER_TAG, "Woken up from light sleep");
// 重新配置时钟(如果需要)
// 重新初始化外设
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠后系统会重启,不会执行到这里
ESP_LOGW(POWER_TAG, "Deep sleep recovery should not reach here");
break;
default:
break;
}
// 恢复到正常模式
current_power_mode_ = POWER_MODE_NORMAL;
ESP_LOGI(POWER_TAG, "Successfully exited low power mode");
return ESP_OK;
}
/**
* @brief 设置硬件模块电源状态
*
* 提供精细化的电源管理,可以单独控制各个硬件模块的电源状态。
*/
esp_err_t BoardSweekar::SetModulePower(hardware_module_t module, bool enable) {
static const char *POWER_TAG = "POWER_MODULE";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "%s module %d", enable ? "Enabling" : "Disabling", module);
// 更新电源状态位图
if (enable) {
power_state_flags_ |= (1 << module);
} else {
power_state_flags_ &= ~(1 << module);
}
switch (module) {
case HW_MODULE_I2C:
if (!enable && bus_handle_) {
// 注意:I2C总线通常不建议完全关闭,可能影响多个设备
ESP_LOGW(POWER_TAG, "I2C bus power control not recommended");
}
break;
case HW_MODULE_SDCARD:
if (enable && !is_sdcard_initialized_) {
ret = InitializeSdcard();
} else if (!enable && is_sdcard_initialized_) {
// 卸载SD卡文件系统
// 注意:需要确保没有打开的文件
ESP_LOGW(POWER_TAG, "SD card power down requires file system cleanup");
}
break;
case HW_MODULE_LCD:
// LCD背光控制
// LCD电源引脚控制(如果有)
break;
case HW_MODULE_AUDIO:
// 音频编解码器电源控制
// 需要与AudioPlayer协调
break;
case HW_MODULE_MOTOR:
// 步进电机驱动器电源控制
// 电机睡眠模式
break;
case HW_MODULE_GYROSCOPE:
// 陀螺仪电源控制
// 设置陀螺仪为低功耗模式
break;
case HW_MODULE_TOUCH:
// 触摸传感器电源控制
// 设置触摸传感器为睡眠模式
break;
case HW_MODULE_RADAR:
// 毫米波雷达电源控制
// 设置雷达为低功耗模式
break;
case HW_MODULE_IO_EXPAND:
// IO扩展板电源控制
// 设置扩展板为低功耗模式
break;
default:
ESP_LOGE(POWER_TAG, "Unknown hardware module: %d", module);
return ESP_ERR_INVALID_ARG;
}
if (ret == ESP_OK) {
ESP_LOGI(POWER_TAG, "Module %d power %s successfully", module, enable ? "enabled" : "disabled");
} else {
ESP_LOGE(POWER_TAG, "Failed to %s module %d: %s", enable ? "enable" : "disable", module, esp_err_to_name(ret));
}
return ret;
}
@@ -0,0 +1,174 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include "config.h"
#include "io_expansion_board.h"
// #include "stepper_motor.h" // !
#include "stepper_driver.h"
#include "i2c_bus.h"
#include "gyroscope.h"
// #include "touch_sensor.h" // !
#include "base_board_control.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
/**
* @brief 功耗模式枚举
*/
typedef enum {
POWER_MODE_NORMAL, // 正常运行模式,所有硬件可用
POWER_MODE_IDLE, // 空闲模式,关闭非必要外设
POWER_MODE_LIGHT_SLEEP, // 轻度睡眠,快速唤醒
POWER_MODE_DEEP_SLEEP, // 深度睡眠,仅RTC运行
} power_mode_t;
/**
* @brief 硬件模块枚举,用于精细化电源管理
*/
typedef enum {
HW_MODULE_I2C, // I2C总线
HW_MODULE_SDCARD, // SD卡
HW_MODULE_LCD, // LCD显示
HW_MODULE_AUDIO, // 音频编解码器
HW_MODULE_MOTOR, // 步进电机
HW_MODULE_GYROSCOPE, // 陀螺仪
HW_MODULE_TOUCH, // 触摸传感器
HW_MODULE_RADAR, // 毫米波雷达
HW_MODULE_IO_EXPAND, // IO扩展板
} hardware_module_t;
/**
* @brief 硬件板级管理器
*
* 采用高内聚低耦合设计原则,统一管理所有硬件资源。
* 提供统一的硬件初始化接口和资源访问,避免各模块重复初始化。
*/
class BoardSweekar
{
private:
i2c_bus_handle_t bus_handle_ = nullptr; // 使用ESP-ADF的I2C总线句柄
esp_periph_set_handle_t periph_set_ = nullptr; // 外设管理句柄
bool is_sdcard_initialized_ = false; // SD卡初始化状态
power_mode_t current_power_mode_ = POWER_MODE_NORMAL; // 当前功耗模式
uint32_t power_state_flags_ = 0xFFFFFFFF; // 各模块电源状态位图(1为开启)
public:
BoardSweekar();
~BoardSweekar();
IOExpansionBoard io_expansion_; // IO扩展板对象
Gyroscope gyroscope_; // 陀螺仪对象
// TouchSensor touch_sensor_; // 触摸传感器对象 //!
BaseBoardControl base_board_control_; // 底座控制对象
StepperDriver* stepper_driver_ = nullptr; // 步进电机驱动对象
StepperDriver* GetStepperDriver() const { return stepper_driver_; }
// 初始化步进电机
void StepperMotorInit(void);
// 初始化LCD IO引脚(消除与步进电机的耦合)
void LcdIoInit(void);
// 初始化串口
void MillimeterWaveRadarUartInit(void);
//初始化毫米波雷达的中断引脚
void MillimeterWaveRadarGpioInit(void);
// 初始化I2C总线
esp_err_t I2CMasterInit(void);
// 初始化VBAT toVBAT_5V引脚
void VbatToVbat5vInit(void);
// 初始化底座控制串口
void BaseBoardControlInit(void);
// 初始化SD卡(推荐使用统一的硬件初始化)
esp_err_t InitializeSdcard(void);
// 获取SD卡初始化状态
bool IsSdcardInitialized() const { return is_sdcard_initialized_; }
// 获取外设管理句柄(供需要访问SD卡的模块使用)
esp_periph_set_handle_t GetPeriphSet() const { return periph_set_; }
// 获取I2C总线句柄
i2c_bus_handle_t GetBusHandle() const { return bus_handle_; }
/**
* @brief 统一的硬件初始化接口
*
* 按照依赖关系顺序初始化所有硬件资源,确保各模块可以安全使用。
* 推荐在主程序启动时调用此函数,而不是分别调用各个初始化函数。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardware(void);
/**
* @brief 完整的统一硬件初始化接口(专为复杂应用优化)
*
* 包含所有必要外设的完整初始化,适用于demo5等复杂应用场景。
* 这个函数会初始化:I2C总线、IO扩展板、SD卡、LCD IO、底座控制等。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardwareComplete(void);
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置,优化启动流程。
*
* @param app_type 应用类型(demo5、音频播放器、显示应用等)
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeHardwareForApplication(int app_type);
/**
* @brief 功耗管理接口 - 进入低功耗模式
*
* 根据指定的功耗模式,关闭非必要硬件、设置GPIO为低功耗状态、
* 调整时钟频率等。支持多种功耗模式以适应不同应用场景。
*
* @param mode 功耗模式
* @return esp_err_t 操作结果
*/
esp_err_t EnterLowPowerMode(power_mode_t mode);
/**
* @brief 功耗管理接口 - 退出低功耗模式
*
* 从低功耗模式恢复,重新初始化必要的硬件资源。
*
* @return esp_err_t 操作结果
*/
esp_err_t ExitLowPowerMode(void);
/**
* @brief 获取当前功耗模式
*
* @return power_mode_t 当前功耗模式
*/
power_mode_t GetPowerMode(void) const { return current_power_mode_; }
/**
* @brief 设置硬件模块电源状态
*
* 单独控制某个硬件模块的电源状态,用于精细化的功耗管理。
*
* @param module 硬件模块
* @param enable true为开启,false为关闭
* @return esp_err_t 操作结果
*/
esp_err_t SetModulePower(hardware_module_t module, bool enable);
/**
* @brief 执行系统关机(拉高 SHUTDOWN_CTRL_PIN)
*/
void SystemShutdown(void);
};
#endif
@@ -0,0 +1,186 @@
/**
* @file board_sweekar_complete_init.cc
* @brief 完整的统一硬件初始化实现
*
* 这个文件提供了完整的统一硬件初始化函数,包含所有必要的硬件初始化步骤。
* 专为demo5等复杂应用场景设计。
*/
/**
* @brief 完整的统一硬件初始化接口(专为demo5优化)
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备依赖)
* 2. IO扩展板(控制其他外设)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. LCD IO引脚(显示相关)
* 5. 底座控制串口(通信)
* 6. 底座控制模块(业务逻辑)
* 7. 其他外设(根据应用需求)
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardwareComplete(void)
{
static const char *HW_INIT_TAG = "HW_INIT_COMPLETE";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting complete hardware initialization...");
// 1. 初始化I2C总线(最基础,其他设备依赖)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "✓ I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "✓ I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "⚠ SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "✓ SD card initialized");
}
// 4. 初始化LCD IO引脚(显示相关)
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "✓ LCD IO pins initialized");
// 5. 初始化底座控制串口(demo5需要)
BaseBoardControlUartInit();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control UART initialized");
// 6. 初始化底座控制模块
base_board_control_.Init();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control initialized");
// 7. 设置IO扩展板的PA控制模式(demo5需要)
io_expansion_.SetPaCtrlMode(true);
ESP_LOGI(HW_INIT_TAG, "✓ IO expansion PA control mode set");
// 8. 可选外设(根据具体应用需求启用)
// ESP_LOGI(HW_INIT_TAG, "Initializing optional peripherals...");
//
// // 毫米波雷达(如需要)
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Millimeter wave radar initialized");
//
// // VBAT升压(如需要)
// VbatToVbat5vInit();
// ESP_LOGI(HW_INIT_TAG, "✓ VBAT to 5V booster initialized");
//
// // 步进电机(如需要)
// StepperMotorInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Stepper motor initialized");
ESP_LOGI(HW_INIT_TAG, "=== Complete hardware initialization finished successfully ===");
return ESP_OK;
}
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置
*/
esp_err_t BoardSweekar::InitializeHardwareForApplication(application_type_t app_type) {
static const char *APP_INIT_TAG = "APP_HW_INIT";
ESP_LOGI(APP_INIT_TAG, "Initializing hardware for application type: %d", app_type);
// 基础硬件初始化(所有应用都需要)
esp_err_t ret = InitializeAllHardwareComplete();
if (ret != ESP_OK) {
ESP_LOGE(APP_INIT_TAG, "Basic hardware initialization failed");
return ret;
}
switch (app_type) {
case APP_TYPE_DEMO5: // 宠物游戏demo
ESP_LOGI(APP_INIT_TAG, "Configuring for Demo5 (Pet Game)...");
// Demo5需要的额外初始化
// 触摸传感器、陀螺仪、RGB LED等会在应用层初始化
ESP_LOGI(APP_INIT_TAG, "✓ Demo5 hardware configuration complete");
break;
case APP_TYPE_AUDIO_PLAYER: // 音频播放器
ESP_LOGI(APP_INIT_TAG, "Configuring for Audio Player...");
// 音频应用需要确保音频编解码器正确初始化
// 这部分通常在AudioPlayer类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Audio player hardware configuration complete");
break;
case APP_TYPE_DISPLAY_ONLY: // 纯显示应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Display Only...");
// 显示应用需要确保LCD相关硬件正确配置
// 这部分通常在Display类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Display-only hardware configuration complete");
break;
case APP_TYPE_MINIMAL: // 最小化应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Minimal Application...");
// 最小化配置,只保留最基本的功能
// 关闭非必要外设以节省功耗
ESP_LOGI(APP_INIT_TAG, "✓ Minimal hardware configuration complete");
break;
default:
ESP_LOGW(APP_INIT_TAG, "Unknown application type, using default configuration");
break;
}
return ESP_OK;
}
/**
* @brief 硬件初始化状态报告
*
* 提供详细的硬件初始化状态报告,便于调试和问题排查
*/
void BoardSweekar::ReportHardwareStatus(void) {
static const char *STATUS_TAG = "HW_STATUS";
ESP_LOGI(STATUS_TAG, "=== Hardware Initialization Status Report ===");
// I2C总线状态
ESP_LOGI(STATUS_TAG, "I2C Bus: %s", (bus_handle_ != nullptr) ? "✓ Initialized" : "✗ Not initialized");
// SD卡状态
ESP_LOGI(STATUS_TAG, "SD Card: %s", (is_sdcard_initialized_) ? "✓ Initialized" : "✗ Not initialized");
if (is_sdcard_initialized_) {
ESP_LOGI(STATUS_TAG, " Peripheral Set: %s", (periph_set_ != nullptr) ? "✓ Available" : "✗ Not available");
}
// IO扩展板状态
ESP_LOGI(STATUS_TAG, "IO Expansion Board: %s", (io_expansion_.bus_handle_ != nullptr) ? "✓ Configured" : "✗ Not configured");
// 底座控制状态
ESP_LOGI(STATUS_TAG, "Base Board Control: %s", (base_board_control_.GetUartPort() != 0) ? "✓ Initialized" : "✗ Not initialized");
// 功耗管理状态
ESP_LOGI(STATUS_TAG, "Power Management: %s", (current_power_mode_ == POWER_MODE_NORMAL) ? "✓ Normal mode" : "⚠ Low power mode");
ESP_LOGI(STATUS_TAG, " Current Mode: %d", current_power_mode_);
ESP_LOGI(STATUS_TAG, " Module Power States: 0x%08X", power_state_flags_);
// 内存状态
ESP_LOGI(STATUS_TAG, "Memory Status:");
ESP_LOGI(STATUS_TAG, " Internal RAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
ESP_LOGI(STATUS_TAG, " PSRAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
ESP_LOGI(STATUS_TAG, "=== End of Hardware Status Report ===");
}
@@ -0,0 +1,138 @@
/* 定义引脚分配 */
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
/**
* @file config.h
* @brief 定义了引脚的分配。
* @author zouyuhua
* @date 2025年8月7日
*
*/
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_rom_sys.h" // 用于 esp_rom_delay_us
#include "esp_timer.h"
#include "esp_mac.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/timer.h"
#include "esp_intr_alloc.h"
#include "soc/timer_group_struct.h" // 包含TIMERG0、TIMERG1的结构体定义
#include "soc/timer_group_reg.h" // 包含定时器相关寄存器宏定义
#include "driver/uart.h"
#include "esp_err.h"
#include "freertos/queue.h"
#include "soc/uart_reg.h"
// #include "linear_motor.h"
#include "gyroscope.h"
#define TAG_BOARD "SWEEKAR_ESP32"
/*I2C总线引脚定义*/
#define I2C0_MASTER_PORT I2C_NUM_0
#define I2C0_MASTER_SDA_IO GPIO_NUM_2
#define I2C0_MASTER_SCL_IO GPIO_NUM_3
/* 步进电机配置 */
#define STEPPER_STEP_IO GPIO_NUM_42 // 步进电机脉冲输入引脚
#define STEPPER_TIMER_RES_HZ 1000000 // 步进电机定时器分辨率 1 MHz(1 us 步进)
/* 定义屏幕驱动控制引脚 */
#define ST7789_PIN_CLK GPIO_NUM_48 // SPI时钟引脚
#define ST7789_PIN_DC GPIO_NUM_38 // 数据/命令选择引脚RSEL
#define ST7789_PIN_MOSI GPIO_NUM_47 // SPI数据输出引脚
#define ST7789_PIN_CS GPIO_NUM_46 // SPI片选引脚
#define ST7789_PIN_BL GPIO_NUM_45 // 背光引脚
#define ST7789_PIN_RST GPIO_NUM_NC // 复位引脚
#define LCD_BL_ACTIVE_LEVEL 1 // 背光有效电平:1=高电平有效(NMOS),0=低电平有效(PMOS)
#define LCD_WIDTH 172 // 屏幕宽度(水平方向像素数)
#define LCD_HEIGHT 320 // 屏幕高度(垂直方向像素数)
/* 毫米波雷达配置参数 */
/* 定义毫米波雷达的中断引脚 */
#define MILLIMETER_WAVE_RADAR_PIN GPIO_NUM_35
// UART 配置参数
#define RADAR_UART_NUM UART_NUM_1 // 使用 UART1
#define UART_TX_PIN GPIO_NUM_40 // GPIO40 作为 TX(根据实际硬件连接调整)
#define UART_RX_PIN GPIO_NUM_39 // GPIO39 作为 RX
#define RADAR_BAUD_RATE 9600 // 雷达波特率
#define RADAR_BUF_SIZE 100 // 雷达接收缓冲区大小
/* IO扩展硬件配置常量 */
#define I2C_MASTER_FREQ_HZ 100000 // I2C通信频率(100kHz)(可更改至400000)
#define I2C_IO_EXPAND_SLAVE_ADDR 0x58 // AW9523BTQR器件地址,v1_1_13的addr1和addr0是0
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // 中断检测引脚
#define ESP_INTR_FLAG_DEFAULT 0 // 中断标志默认值
#define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/* 定义线性马达的I2C从机配置常量 */
#define LINEAR_MOTOR_I2C_SLAVE_ADDR 0x5A // 线性马达的I2C从机地址
/* 定义陀螺仪的I2C从机配置常量 */
#define GYROSCOPE_I2C_SLAVE_ADDR 0x6A // 陀螺仪的I2C从机地址
#define GYROSCOPE_INPUT_PIN_SEL GPIO_NUM_21 // 陀螺仪中断引脚
/* AW触摸芯片配置 */
#define AW_TOUCH_IRQ_PIN GPIO_NUM_11 // AW触摸芯片中断引脚
#define NFC_RST_PIN GPIO_NUM_12 // NFC复位引脚
#define SHUTDOWN_CTRL_PIN GPIO_NUM_13 // 关机控制引脚
/* 原生触摸引脚配置 (v1.1.16已改为上述功能,保留定义需谨慎) */
// 触摸按键数量配置
#define TOUCH_BUTTON_NUM 3
// 触摸通道定义 - 注意:v1.1.16如果不使用ESP32原生触摸,这些定义可能不再适用
// 但为了兼容编译,可以保留或注释
#define TOUCH_PAD_CHANNEL_1 TOUCH_PAD_NUM11 // GPIO11 (Now AW_IRQ)
#define TOUCH_PAD_CHANNEL_2 TOUCH_PAD_NUM12 // GPIO12 (Now NFC_RST)
#define TOUCH_PAD_CHANNEL_3 TOUCH_PAD_NUM13 // GPIO13 (Now SHUTDOWN)
/* ES8311音频编解码器配置 (ESP-ADF兼容) */
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
/* I2S GPIO定义 */
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_15
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
/* PA引脚定义 */
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
/* I2C配置 */
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_2
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_3
#define AUDIO_CODEC_ES8311_ADDR 0x18
#define AUDIO_CODEC_ES7210_ADDR 0x40
/* WS2812 RGB LED配置 */
#define WS2812_PIN GPIO_NUM_41 // WS2812数据引脚
#define WS2812_NUM_LEDS 2 // WS2812 LED数量
/* 创建VBAT to VBAT_5V */
#define VBAT_5V_PIN GPIO_NUM_NC // VBAT_5V引脚
/* 定义按键的中断引脚 */
#define BUTTON_PIN GPIO_NUM_0 // 按键中断引脚
#define RIGHT_BUTTON_PIN GPIO_NUM_8 // 按键中断引脚
/* 底座控制命令串口配置 */
#define BASE_BOARD_CTL_UART_PORT UART_NUM_0
#define BASE_BOARD_CTL_UART_TX_PIN GPIO_NUM_43
#define BASE_BOARD_CTL_UART_RX_PIN GPIO_NUM_44
#define BASE_BOARD_CTL_UART_BAUDRATE 115200
#define BASE_BOARD_CTL_UART_BUF_SIZE 256
/* 背光控制引脚配置 */
#define BL_PIN GPIO_NUM_45
#endif // _BOARD_CONFIG_H_
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,432 @@
#ifndef IO_EXPANSION_BOARD_H
#define IO_EXPANSION_BOARD_H
#include <cstdint>
#include <esp_log.h>
#include <esp_check.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include <functional>
#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 "portmacro.h"
// 前向声明,避免循环引用
class MillimeterWaveRadar;
class PhotoInterrupter;
class AdcBatteryMonitor;
// 回调函数类型定义
typedef std::function<void()> RadarCallback;
typedef std::function<void()> PhotoInterrupterStartCallback;
typedef std::function<void()> PhotoInterrupterEndCallback;
typedef std::function<void()> BatteryChargingCallback;
typedef std::function<void()> BatteryFullCallback;
typedef std::function<void()> BatteryNotChargingCallback;
/* 寄存器地址 */
#define INPUT_PORT0 0x00 // 输入端口0寄存器
#define INPUT_PORT1 0x01 // 输入端口1寄存器 0为低电平(仅支持读取)
#define OUTPUT_PORT0 0x02 // 输出端口0寄存器
#define OUTPUT_PORT1 0x03 // 输出端口1寄存器 0为低电平
#define CONFIG_PORT0 0x04 // 配置端口0寄存器
#define CONFIG_PORT1 0x05 // 配置端口1寄存器 0为输出模式
#define INT_PORT0 0x06 // 中断端口0寄存器
#define INT_PORT1 0x07 // 中断端口1寄存器 0为使能中断(根据手册默认是使能的)
#define CTL 0x11 // 全局控制寄存器
#define ID 0x23 // 芯片ID寄存器(0x23)
/***********************************IO扩展板电机引脚配置参数*************************************/
/* 设置步进电机方向 */
#define STEPPER_MOTOR_LEFT false //步进电机的方向为左转
#define STEPPER_MOTOR_RIGHT true //步进电机的方向为右转
#define STEPPER_MOTOR_DIR_PORT OUTPUT_PORT0 //步进电机方向引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_DIR_PIN 4 //步进电机方向引脚对应的IO扩展板引脚P0_4
#define STEPPER_MOTOR_DIR_CONFIG CONFIG_PORT0 //步进电机方向引脚配置在P0_4
/* 设置步进电机细分模式 */
#define STEPPER_MOTOR_M0_PIN 5 //步进电机细分模式选择引脚对应IO扩展板引脚P1_5
#define STEPPER_MOTOR_M0_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M0_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_5
#define STEPPER_MOTOR_M1_PIN 4 //步进电机细分模式选择引脚对应IO扩展板引脚P1_4
#define STEPPER_MOTOR_M1_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M1_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_4
/* 设置步进电机睡眠引脚 */
#define STEPPER_MOTOR_SLEEP false //步进电机进入睡眠模式
#define STEPPER_MOTOR_WORK true //步进电机进入工作模式
#define STEPPER_MOTOR_SLEEP_PIN 6 //步进电机睡眠引脚对应IO扩展板引脚P1_6
#define STEPPER_MOTOR_SLEEP_CONFIG CONFIG_PORT1 //步进电机睡眠引脚配置在P1_6
#define STEPPER_MOTOR_SLEEP_PORT OUTPUT_PORT1 //步进电机睡眠引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置电机的使能引脚 */
#define STEPPER_MOTOR_ENABLE_PIN 7 //电机使能引脚对应IO扩展板引脚P0_7
#define STEPPER_MOTOR_ENABLE_PORT OUTPUT_PORT0 //电机使能引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_ENABLE_CONFIG CONFIG_PORT0 //电机使能引脚配置在P0_7
#define STEPPER_MOTOR_ENABLE_ENABLE true //电机使能引脚状态为高电平
#define STEPPER_MOTOR_ENABLE_DISABLE false //电机使能引脚状态为低电平
/* 设置毫米波雷达对应IO扩展板的中断引脚 */
#define RADAR_INTERRUPT_PIN 7 //毫米波雷达中断引脚对应IO扩展板引脚P1_7
#define RADAR_INTERRUPT_CONFIG CONFIG_PORT1 //毫米波雷达中断引脚挂载在IO扩展板的输入端口
#define RADAR_INTERRUPT_INPUT INPUT_PORT1 //毫米波雷达中断引脚输入状态检测在INPUT_PORT1
#define RADAR_INTERRUPT_ENABLE true //毫米波雷达中断引脚状态为输入状态
#define RADAR_INTERRUPT_DISABLE false //毫米波雷达中断引脚状态为输出状态
/* 任务配置常量 */
#define WRITE_BUF_SIZE 2 // I2C单次写入字节数
#define READ_BUF_SIZE 2 // I2C单次读取字节数
#define I2C_TASK_PRIORITY 2 // 任务优先级(中等)
#define I2C_TASK_STACK 4096 // 任务栈大小(4KB)
// 光学传感器引脚定义
#define START_PHOTO_INTERRUPTER_PIN 2 // 光学传感器引脚(P0_2)
#define START_PHOTO_INTERRUPTER_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define START_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define START_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define START_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
#define END_PHOTO_INTERRUPTER_PIN 3 // 光学传感器引脚(P0_3)
#define END_PHOTO_INTERRUPTER_PORT_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define END_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define END_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define END_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
/***********************************LED_EN 电源控制(WS2812/背光等)*************************************/
// v1.1.16: LED_EN 高电平有效,低电平无效;端口为 IO 扩展 P0_1
#define LED_EN_PIN 1
#define LED_EN_PORT OUTPUT_PORT0
#define LED_EN_CONFIG CONFIG_PORT0
/***********************************IO扩展板LCD引脚配置参数*************************************/
// /* 设置LCD的BL引脚 */
// #define LCD_BL_PIN 6 //LCD背光引脚对应IO扩展板引脚P0_6
// #define LCD_BL_PORT OUTPUT_PORT0 //LCD背光引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
// #define LCD_BL_CONFIG CONFIG_PORT0 //LCD背光引脚配置在P0_6
// #define LCD_BL_HIGH true //LCD背光引脚状态为高电平
// #define LCD_BL_LOW false //LCD背光引脚状态为低电平
/* 设置LCD的RST引脚 */
#define LCD_RST_PIN 1 //LCD复位引脚对应IO扩展板引脚P1_1
#define LCD_RST_CONFIG CONFIG_PORT1 //LCD复位引脚配置在P1_1
#define LCD_RST_PORT OUTPUT_PORT1 //LCD复位引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_RST_HIGH true //LCD复位引脚状态为高电平
#define LCD_RST_LOW false //LCD复位引脚状态为低电平
/* 设置PA控制引脚 */
#define PA_CTRL_PIN 2 //PA控制引脚对应IO扩展板引脚P1_2
#define PA_CTRL_CONFIG CONFIG_PORT1 //PA控制引脚配置在P1_2
#define PA_CTRL_PORT OUTPUT_PORT1 //PA控制引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define PA_CTRL_HIGH true //PA控制引脚状态为高电平
#define PA_CTRL_LOW false //PA控制引脚状态为低电平
// 电池充电检测引脚定义
#define BATTERY_FULL_INPUT INPUT_PORT1 // 电池充满检测引脚挂载在IO扩展板的输入端口
#define BATTERY_FULL_PIN 3 // 电池充满检测引脚对应IO扩展板引脚P1_3
#define BATTERY_FULL_CONFIG CONFIG_PORT1 // 电池充满检测引脚配置在P1_3
#define BATTERY_CHRG_INPUT INPUT_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define BATTERY_CHRG_PIN 5 // 充电检测引脚(P0_5)
#define BATTERY_CHRG_CONFIG CONFIG_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define TAG_GPIO_IO_EXPAND_DETECT "GPIO_IO_EXPAND_DETECT" // GPIO中断检测日志标签
#define TAG_I2C_IO_EXPAND_TASK "I2C_IO_EXPAND_TASK" // I2CIO扩展板任务日志标签
// GPIO引脚定义
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // IO扩展板中断检测引脚
// #define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/**
* @class IOExpansionBoard
* @brief I/O扩展板驱动类,封装AW9523BTQR芯片操作
*/
class IOExpansionBoard
{
public:
/**
* @brief 构造函数
*/
IOExpansionBoard();
/**
* @brief 设置毫米波雷达指针
* @param radar 毫米波雷达对象指针
*/
void SetRadar(MillimeterWaveRadar* radar) { radar_ = radar; };
/**
* @brief 设置限位开关指针
* @param photo_interrupter 限位开关对象指针
*/
void setPhotoInterrupter(PhotoInterrupter* photo_interrupter) { photo_interrupter_ = photo_interrupter; };
/**
* @brief 设置电池监控器指针
* @param battery_monitor 电池监控器对象指针
*/
void setBatteryMonitor(AdcBatteryMonitor* battery_monitor) { battery_monitor_ = battery_monitor; };
/**
* @brief 析构函数
*/
~IOExpansionBoard();
/**
* @brief 初始化GPIO中断检测
* @param gpio_num 中断引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetIoExpand();
/**
* @brief 初始化I2C主机通信
* @return esp_err_t 初始化结果
*/
// esp_err_t IoExpansionI2cInit();
/**
* @brief 创建I2C数据读取任务
*/
void CreateI2cReadTask();
/**
* @brief 启动I2C任务(整合初始化和任务创建)
* @return esp_err_t 操作结果
*/
esp_err_t StartTask();
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandWriteReg(uint8_t reg_addr, uint8_t data);
/**
* @brief 读寄存器数据
* @param reg_addr 寄存器地址
* @param data 要读出的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandReadReg(uint8_t reg_addr, uint8_t *data);
/**
* @brief 读取输入端口0寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort0(uint8_t *data);
/**
* @brief 读取输入端口1寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort1(uint8_t *data);
// /**
// * @brief 设置IO扩展板GPIO中断状态 通过读取Input_Port0 和 Input_Port1 寄存器可以清除中断
// * @param reg_addr 寄存器地址
// * @return esp_err_t 操作结果
// */
// esp_err_t SetInterruptStatus(bool status);
/**
* @brief 配置IO扩展版上的IO口为输出还是输入,并且设置初始为高电平
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandSetMode();
/**
* @brief 设置步进电机的细分模式
* @param mode 模式:1/2/4/8/16/32/64
* @return esp_err_t 操作结果
*/
void SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT_m0, uint8_t m0_pin, uint8_t OUTPUT_PORT_m1, uint8_t m1_pin);
/**
* @brief 设置步进电机的方向
* @param dir 方向
* @param OUTPUT_PORT 输出端口
* @param pin 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin);
/**
* @brief 设置步进电机的睡眠模式
* @param 是否进入睡眠模式
* @param 输出端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin);
/**
* @brief 设置步进电机的使能引脚状态
* @param 使能状态
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin);
/**
* @brief 设置LCD的BL引脚状态
* @param BL状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin);
/**
* @brief 设置LCD的RST引脚状态
* @param RST状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t rst_pin);
/**
* @brief 设置 LED_EN 引脚状态(v1.1.16:高电平有效,低电平无效)
* @param enable true: 开启(置高),false: 关闭(置低)
* @return esp_err_t 操作结果
*/
esp_err_t SetLedEnMode(bool enable);
/**
* @brief 设置PA控制引脚状态
* @param mode PA_CTRL状态(true为高电平,false为低电平)
* @return esp_err_t 操作结果
*/
esp_err_t SetPaCtrlMode(bool mode);
/**
* @brief 设置雷达中断引脚
* @param 毫米波雷达的中断引脚模式
* @param 输入端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin);
/**
* @brief 计算Port0的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort0();
/**
* @brief 计算Port1的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort1();
/**
* @brief 设置雷达中断回调函数
* @param callback 雷达中断回调函数
*/
void SetRadarCallback(RadarCallback callback) { radar_callback_ = callback; }
/**
* @brief 设置光学传感器开始中断回调函数
* @param callback 光学传感器开始中断回调函数
*/
void SetPhotoInterrupterStartCallback(PhotoInterrupterStartCallback callback) { photo_start_callback_ = callback; }
/**
* @brief 设置光学传感器结束中断回调函数
* @param callback 光学传感器结束中断回调函数
*/
void SetPhotoInterrupterEndCallback(PhotoInterrupterEndCallback callback) { photo_end_callback_ = callback; }
/**
* @brief 设置电池充电中断回调函数
* @param callback 电池充电中断回调函数
*/
void SetBatteryChargingCallback(BatteryChargingCallback callback) { battery_charging_callback_ = callback; }
/**
* @brief 设置电池充满中断回调函数
* @param callback 电池充满中断回调函数
*/
void SetBatteryFullCallback(BatteryFullCallback callback) { battery_full_callback_ = callback; }
/**
* @brief 设置电池非充电中断回调函数
* @param callback 电池非充电中断回调函数
*/
void SetBatteryNotChargingCallback(BatteryNotChargingCallback callback) { battery_not_charging_callback_ = callback; }
// 公共成员变量
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
uint8_t device_addr_; // I2C设备地址
private:
// 静态成员函数
MillimeterWaveRadar* radar_ = nullptr; // 毫米波雷达指针,用于触发回调
PhotoInterrupter* photo_interrupter_ = nullptr; // 限位开关指针,用于触发回调
AdcBatteryMonitor* battery_monitor_ = nullptr; // 电池监控器指针,用于触发回调
static void IRAM_ATTR GpioIsrHandler(void* arg); // 中断处理函数
static void I2cReadTask(void* pvParameters); // I2C数据读取任务函数
// 成员变量用于状态跟踪
uint8_t last_port0_state_ = 0; // 上次检测到的Port0状态
uint8_t last_port1_state_ = 0; // 上次检测到的Port1状态
bool radar_last_state_ = false; // 雷达引脚上一次的状态
bool battery_charging_last_state_ = false; // 电池充电状态上一次的状态
bool battery_full_last_state_ = false; // 电池充满状态上一次的状态
// 回调函数成员变量
RadarCallback radar_callback_ = nullptr;
PhotoInterrupterStartCallback photo_start_callback_ = nullptr;
PhotoInterrupterEndCallback photo_end_callback_ = nullptr;
BatteryChargingCallback battery_charging_callback_ = nullptr;
BatteryFullCallback battery_full_callback_ = nullptr;
BatteryNotChargingCallback battery_not_charging_callback_ = nullptr;
// PhotoInterrupter* photo_interrupter_ = nullptr; // 光学传感器指针,用于触发回调
// /**
// * @brief 分配静态任务所需的内存
// * @return bool 分配是否成功
// */
// bool AllocateStaticTaskMemory();
// /**
// * @brief 释放静态任务分配的内存
// */
// void FreeStaticTaskMemory();
// 实例成员变量
EventGroupHandle_t i2c_task_event_group_ = nullptr; // 事件组
TaskHandle_t xI2CReadTaskHandle_ = nullptr; // 任务句柄
bool is_initialized_ = false; // 初始化状态标记(新增)
gpio_num_t interrupt_gpio_ = GPIO_NUM_NC; // 中断引脚
gpio_num_t reset_gpio_ = GPIO_NUM_NC; // 复位引脚
// StackType_t* pI2CReadTaskStackBuffer = nullptr; // 任务栈指针(PSRAM)
// StaticTask_t* pxI2CReadTaskBuffer = nullptr; // 任务控制块指针(内部RAM)
};
#endif
@@ -0,0 +1,178 @@
/**
* @file power_management_config.h
* @brief 功耗管理配置和策略定义
* @author Embedded Software Expert
* @date 2025年1月
*
* 这个文件定义了功耗管理的配置参数、策略和优化建议,
* 为系统提供灵活的功耗管理方案。
*/
#ifndef POWER_MANAGEMENT_CONFIG_H
#define POWER_MANAGEMENT_CONFIG_H
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "esp32/rom/rtc.h"
/**
* @brief 功耗管理配置结构体
*/
typedef struct {
// 睡眠模式配置
uint64_t light_sleep_duration_us; // 轻度睡眠持续时间(微秒)
uint64_t deep_sleep_duration_us; // 深度睡眠持续时间(微秒)
// 唤醒源配置
bool enable_timer_wakeup; // 启用定时器唤醒
bool enable_gpio_wakeup; // 启用GPIO唤醒
bool enable_touch_wakeup; // 启用触摸唤醒
gpio_num_t wakeup_gpio_num; // 唤醒GPIO编号
int wakeup_level; // 唤醒电平(0或1)
// 功耗优化配置
bool disable_bt_in_sleep; // 睡眠时关闭蓝牙
bool disable_wifi_in_sleep; // 睡眠时关闭WiFi
bool reduce_cpu_frequency; // 降低CPU频率
uint32_t cpu_frequency_hz; // CPU频率(Hz)
// 外设功耗配置
bool power_down_sdcard; // 睡眠时关闭SD卡电源
bool power_down_display; // 睡眠时关闭显示电源
bool power_down_audio; // 睡眠时关闭音频电源
bool power_down_motor; // 睡眠时关闭电机电源
// RTC内存配置
bool preserve_rtc_memory; // 保留RTC内存
size_t rtc_memory_size; // RTC内存大小
} power_management_config_t;
/**
* @brief 默认功耗管理配置
*/
#define DEFAULT_POWER_CONFIG { \
.light_sleep_duration_us = 1000000, /* 1秒 */ \
.deep_sleep_duration_us = 30000000, /* 30秒 */ \
.enable_timer_wakeup = true, /* 启用定时器唤醒 */ \
.enable_gpio_wakeup = false, /* 不启用GPIO唤醒 */ \
.enable_touch_wakeup = false, /* 不启用触摸唤醒 */ \
.wakeup_gpio_num = GPIO_NUM_NC, /* 无GPIO唤醒 */ \
.wakeup_level = 0, /* 低电平唤醒 */ \
.disable_bt_in_sleep = true, /* 睡眠时关闭蓝牙 */ \
.disable_wifi_in_sleep = true, /* 睡眠时关闭WiFi */ \
.reduce_cpu_frequency = true, /* 降低CPU频率 */ \
.cpu_frequency_hz = 80000000, /* 80MHz */ \
.power_down_sdcard = true, /* 睡眠时关闭SD卡 */ \
.power_down_display = true, /* 睡眠时关闭显示 */ \
.power_down_audio = true, /* 睡眠时关闭音频 */ \
.power_down_motor = true, /* 睡眠时关闭电机 */ \
.preserve_rtc_memory = true, /* 保留RTC内存 */ \
.rtc_memory_size = 4096, /* 4KB RTC内存 */ \
}
/**
* @brief 各功耗模式的详细配置
*/
static const power_management_config_t idle_mode_config = {
.light_sleep_duration_us = 500000, // 500ms轻度睡眠
.deep_sleep_duration_us = 0, // 不使用深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0, // 按键唤醒
.wakeup_level = 0,
.disable_bt_in_sleep = false, // 保持蓝牙连接
.disable_wifi_in_sleep = false, // 保持WiFi连接
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000, // 40MHz
.power_down_sdcard = false, // 保持SD卡(可能有后台任务)
.power_down_display = true, // 关闭显示背光
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 2048,
};
static const power_management_config_t light_sleep_config = {
.light_sleep_duration_us = 0, // 直接进入轻度睡眠
.deep_sleep_duration_us = 0,
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000,
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 4096,
};
static const power_management_config_t deep_sleep_config = {
.light_sleep_duration_us = 0,
.deep_sleep_duration_us = 60000000, // 60秒深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 10000000, // 10MHz
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 8192,
};
/**
* @brief 功耗统计和监控结构体
*/
typedef struct {
uint64_t total_sleep_time_us; // 总睡眠时间
uint64_t total_wake_time_us; // 总唤醒时间
uint32_t sleep_count; // 睡眠次数
uint32_t wake_count; // 唤醒次数
uint32_t average_sleep_duration_us; // 平均睡眠时长
uint32_t average_wake_duration_us; // 平均唤醒时长
float estimated_power_saving_percent; // 估计的功耗节省百分比
} power_statistics_t;
/**
* @brief 功耗优化建议
*
* 基于硬件特性和使用模式,提供功耗优化建议
*/
typedef enum {
OPTIMIZATION_NONE = 0,
OPTIMIZATION_REDUCE_CPU_FREQ, // 降低CPU频率
OPTIMIZATION_DISABLE_UNUSED_PERIPHERALS, // 关闭未使用的外设
OPTIMIZATION_USE_LIGHT_SLEEP, // 使用轻度睡眠
OPTIMIZATION_USE_DEEP_SLEEP, // 使用深度睡眠
OPTIMIZATION_OPTIMIZE_GPIO_STATES, // 优化GPIO状态
OPTIMIZATION_USE_RTC_MEMORY, // 使用RTC内存
OPTIMIZATION_BATCH_OPERATIONS, // 批量操作
} power_optimization_t;
/**
* @brief 功耗管理事件回调
*/
typedef void (*power_event_callback_t)(power_mode_t old_mode, power_mode_t new_mode, void* user_data);
/**
* @brief 功耗管理事件
*/
typedef enum {
POWER_EVENT_MODE_CHANGE, // 功耗模式改变
POWER_EVENT_SLEEP_START, // 睡眠开始
POWER_EVENT_SLEEP_END, // 睡眠结束
POWER_EVENT_WAKEUP, // 系统唤醒
POWER_EVENT_OPTIMIZATION_TRIGGER, // 功耗优化触发
} power_event_t;
#endif // POWER_MANAGEMENT_CONFIG_H
@@ -0,0 +1,780 @@
/**
* @file board.cc
* @brief 板级外设初始化类,包含步进电机、毫米波雷达、I2C总线等外设的初始化函数。
* @author zouyuhua
* @date 2025年8月7日
*
*/
extern "C" {
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "driver/gptimer.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/uart_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2c_bus.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
}
#include "board_sweekar.h"
#include "config.h"
#include "board.h" // 添加board.h以获取audio_board_sdcard_init函数声明
#include "drv8428.h"
#include "aw8646.h"
#include "esp_vfs_fat.h"
#include "audio_mem.h"
#include "esp_timer.h"
#include "base_board_hal_i2c.h"
#define TAG "board_sweekar"
static BoardSweekar* s_board_instance = nullptr;
BoardSweekar* BoardSweekar::GetInstance()
{
if (s_board_instance == nullptr) {
static BoardSweekar global_board;
s_board_instance = &global_board;
}
return s_board_instance;
}
BoardSweekar::BoardSweekar()
{
#ifdef CONFIG_STEPPER_DRIVER_AW8646
stepper_driver_ = new Aw8646Driver(io_expansion_);
#else
stepper_driver_ = new Drv8428Driver(io_expansion_);
#endif
// Initialize Shutdown Control Pin (GPIO_NUM_13) and pull it HIGH
// This is required to keep the main power on.
#ifdef SHUTDOWN_CTRL_PIN
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << SHUTDOWN_CTRL_PIN);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
// 拉低开机
gpio_set_level(SHUTDOWN_CTRL_PIN, 0);
ESP_LOGI(TAG, "Initialized SHUTDOWN_CTRL_PIN (%d) to LOW (Power ON)", SHUTDOWN_CTRL_PIN);
#endif
}
BoardSweekar::~BoardSweekar()
{
if (stepper_driver_) {
delete stepper_driver_;
stepper_driver_ = nullptr;
}
}
void BoardSweekar::SetAudioAmplifier(bool enable) {
// 隐藏具体的硬件实现细节(比如当前版本是通过 IO 扩展板控制的)
io_expansion_.SetPaCtrlMode(enable);
ESP_LOGI(TAG, "Audio Amplifier %s", enable ? "ENABLED" : "DISABLED");
}
/*========================== 步进电机相关驱动 ==========================*/
/**
* @brief 步进电机初始化函数
* 配置步进电机所需的GPIO引脚模式,通过IO扩展板设置步进驱动芯片的工作模式
* 在 stepper_motor.cc 中完成 GPTimer + GPIO 翻转 STEP 脉冲的初始化
*/
void BoardSweekar::StepperMotorInit(void)
{
esp_err_t err = ESP_OK;
// 1) STEP 引脚改为普通 GPIO(不再用 LEDC)
gpio_config_t io_cfg;
io_cfg.pin_bit_mask = 1ULL << STEPPER_STEP_IO;
io_cfg.mode = GPIO_MODE_OUTPUT;
io_cfg.pull_up_en = GPIO_PULLUP_DISABLE;
io_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_cfg.intr_type = GPIO_INTR_DISABLE;
err = gpio_config(&io_cfg);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机 STEP GPIO 配置失败, 错误码: 0x%x", err);
}
// 初始电平拉低,保证上电不乱跳
gpio_set_level(STEPPER_STEP_IO, 0);
// 2) 检查 I2C 总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 3) 初始化驱动对象并初始化
if (stepper_driver_) {
err = stepper_driver_->Init();
if (err != ESP_OK) {
ESP_LOGE(TAG_BOARD, "步进电机驱动初始化失败, 错误码: 0x%x", err);
}
} else {
ESP_LOGE(TAG_BOARD, "步进电机驱动创建失败");
}
}
/**
* @brief LCD IO引脚初始化函数(消除与步进电机的耦合)
*
* 配置LCD所需的IO扩展板寄存器,确保LCD复位引脚正常工作
* 此函数独立于步进电机初始化,专门处理LCD相关的IO配置
*/
void BoardSweekar::LcdIoInit(void)
{
esp_err_t err = ESP_OK;
// 1) 检查I2C总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 2) 执行硬件复位,确保IO扩展板处于已知状态
err = io_expansion_.ResetIoExpand();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板硬件复位失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板硬件复位成功");
}
// 3) 配置IO扩展板模式(端口方向等)
err = io_expansion_.IoexpandSetMode();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板模式配置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板模式配置成功");
}
// 4) 初始化LCD复位引脚:设置为高电平
err = io_expansion_.SetLcdResetMode(LCD_RST_HIGH, LCD_RST_PORT, LCD_RST_PIN);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "LCD复位引脚高电平设置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "LCD复位引脚高电平设置成功");
}
// 5) 初始化LED_EN电源为关闭状态 (PMOS高电平关)
// 只有在需要亮灯时,由ActuatorActor主动开启,避免平静状态下常亮
err = io_expansion_.SetLedEnMode(false);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "初始化LED_EN电源(关闭)失败, 错误码: 0x%x", err);
}
else
{
ESP_LOGI(TAG_BOARD, "初始化LED_EN电源(关闭)成功");
}
}
/**
* @brief 执行系统关机
* 内部拉高 SHUTDOWN_CTRL_PIN 引脚切断电源
*/
void BoardSweekar::SystemShutdown(void)
{
#ifdef SHUTDOWN_CTRL_PIN
ESP_LOGI(TAG, "Executing System Shutdown... Pulling SHUTDOWN_CTRL_PIN (%d) HIGH", SHUTDOWN_CTRL_PIN);
// 用户指定:拉高可以完成关机动作
gpio_set_level(SHUTDOWN_CTRL_PIN, 1);
// 给一点时间让电源完全断开
vTaskDelay(pdMS_TO_TICKS(1000));
#else
ESP_LOGW(TAG, "SHUTDOWN_CTRL_PIN is not defined. Cannot execute hardware shutdown.");
#endif
}
/*========================== 毫米波雷达相关驱动 ==========================*/
/**
* @brief 毫米波雷达串口初始化函数
*
* 配置毫米波雷达所使用的串口参数(波特率、数据位、校验位、停止位等),设置串口引脚,
* 并安装串口驱动。采用较大的RX/TX FIFO深度以处理突发数据,禁用驱动自带事件队列以避免
* 与自定义中断冲突。
*/
void BoardSweekar::MillimeterWaveRadarUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = RADAR_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,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(RADAR_UART_NUM, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(RADAR_UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动(禁用驱动自带事件队列,避免与自定义中断冲突)
// 增加 RX/TX FIFO 深度以处理突发数据
ESP_ERROR_CHECK(uart_driver_install(RADAR_UART_NUM, RADAR_BUF_SIZE * 4, RADAR_BUF_SIZE * 4,
0, NULL, 0));
}
/**
* @brief 毫米波雷达中断引脚初始化函数
*
* 配置毫米波雷达中断引脚为输入模式,禁用上拉电阻,使能下拉电阻,初始禁用中断功能
* (如需使用中断需单独进行配置)。
*/
void BoardSweekar::MillimeterWaveRadarGpioInit(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << MILLIMETER_WAVE_RADAR_PIN), // 选择GPIO引脚
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE, // 使能下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断(如需中断需单独配置)
};
// 配置GPIO
gpio_config(&io_conf);
}
/*========================== I2C总线及挂载的从设备相关驱动 ==========================*/
/**
* @brief I2C总线及从机设备初始化函数
*
* 首先配置并创建I2C主机总线,然后依次添加IO扩展板和线性马达作为I2C从机设备到总线上。
* 若总线创建或设备添加失败,会输出错误信息并进行相应的资源清理(删除已创建的总线)。
* 初始化成功后,会通过ESP_LOGI输出IO扩展板和线性马达的从机地址信息。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示相应的失败原因
*/
esp_err_t BoardSweekar::I2CMasterInit(void)
{
static const char *I2C_INIT = "I2C_INIT";
// 初始化IO扩展板复位引脚
gpio_config_t reset_io_conf = {
.pin_bit_mask = (1ULL << IO_EXPAND_RSTN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&reset_io_conf);
// 执行复位时序:先拉低100ms,再拉高并等待100ms
gpio_set_level(IO_EXPAND_RSTN, 0); // 拉低复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms
gpio_set_level(IO_EXPAND_RSTN, 1); // 释放复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
ESP_LOGI(I2C_INIT, "IO扩展板复位完成");
// 创建统一的I2C总线(使用ESP-ADF的i2c_bus)
i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C0_MASTER_SDA_IO,
.scl_io_num = I2C0_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = 100000,
},
};
// 创建I2C总线(ESP-ADF的audio_board_init会复用这个总线)
bus_handle_ = i2c_bus_create(I2C0_MASTER_PORT, &i2c_cfg);
if (bus_handle_ == NULL) {
ESP_LOGE(TAG, "Failed to create I2C bus");
return ESP_FAIL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
// ESP-ADF的i2c_bus不需要单独添加设备,直接通过总线句柄和设备地址进行通信
// 验证线性马达设备是否存在
esp_err_t err = i2c_bus_probe_addr(bus_handle_, LINEAR_MOTOR_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "线性马达设备未找到(地址:0x%02X),错误码: 0x%x", LINEAR_MOTOR_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "线性马达设备检测成功(地址:0x%02X)", LINEAR_MOTOR_I2C_SLAVE_ADDR);
}
// 验证IO扩展板设备是否存在
err = i2c_bus_probe_addr(bus_handle_, I2C_IO_EXPAND_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "IO扩展板设备未找到(地址:0x%02X),错误码: 0x%x", I2C_IO_EXPAND_SLAVE_ADDR, err);
// 不返回错误,继续初始化其他设备
} else {
ESP_LOGI(I2C_INIT, "IO扩展板设备检测成功(地址:0x%02X)", I2C_IO_EXPAND_SLAVE_ADDR);
}
// 验证陀螺仪设备是否存在
err = i2c_bus_probe_addr(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "陀螺仪设备未找到(地址:0x%02X),错误码: 0x%x", GYROSCOPE_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "陀螺仪设备检测成功(地址:0x%02X)", GYROSCOPE_I2C_SLAVE_ADDR);
}
// 为IO扩展板设置总线句柄和设备地址(使用7位地址)
io_expansion_.bus_handle_ = bus_handle_;
io_expansion_.device_addr_ = I2C_IO_EXPAND_SLAVE_ADDR << 1;
// 线性马达的初始化已移至 InitializeAllHardware 中统一调用 Init 方法
// linear_motor_ 的参数传递将在那里完成,不再此处直接赋值,提高内聚性
ESP_LOGI(I2C_INIT, "I2C总线初始化完成");
return ESP_OK;
}
/*========================== VBAT to VBAT_5V相关驱动 ==========================*/
/**
* @brief 开启VBAT to VBAT_5V
*
* 创建开启升压的电源管理单元
*
*/
void BoardSweekar::VbatToVbat5vInit(void)
{
// 配置VBAT_5V引脚为输出模式
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << VBAT_5V_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 使能VBAT_5V
gpio_set_level(VBAT_5V_PIN, 1);
}
/*========================== 底座控制串口相关驱动 ==========================*/
/**
* @brief 底座控制命令串口初始化函数
*/
// 根据配置选择通信方式的HAL头文件
#ifdef CONFIG_BASE_BOARD_COMM_I2C
#include "base_board_hal_i2c.h"
#else
#include "base_board_hal_uart.h"
#endif
void BoardSweekar::BaseBoardControlInit(void)
{
#ifdef CONFIG_BASE_BOARD_COMM_I2C
// I2C Mode
if (bus_handle_ == nullptr) {
ESP_LOGE(TAG, "I2C bus not initialized before BaseBoardControlInit");
return;
}
// 创建 I2C HAL 实例
BaseBoardHalI2c *hal = new BaseBoardHalI2c(bus_handle_);
base_board_control_.Init(hal);
ESP_LOGI(TAG, "BaseBoardControl I2C initialized");
#else
// UART Mode
uart_config_t uart_config = {
.baud_rate = BASE_BOARD_CTL_UART_BAUDRATE,
.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,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(BASE_BOARD_CTL_UART_PORT, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(BASE_BOARD_CTL_UART_PORT, BASE_BOARD_CTL_UART_TX_PIN, BASE_BOARD_CTL_UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(BASE_BOARD_CTL_UART_PORT,
BASE_BOARD_CTL_UART_BUF_SIZE,
BASE_BOARD_CTL_UART_BUF_SIZE,
0, NULL, 0));
BaseBoardHalUart *hal = new BaseBoardHalUart(BASE_BOARD_CTL_UART_PORT);
base_board_control_.Init(hal);
ESP_LOGI(TAG, "BaseBoardControl UART initialized");
#endif
}
/*========================== SD卡相关驱动 ==========================*/
/**
* @brief SD卡初始化函数
*
* 初始化SD卡外设,使用1线模式进行通信。该函数负责初始化外设管理器
* 并配置SD卡,为后续的文件系统操作做准备。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示失败原因
*/
esp_err_t BoardSweekar::InitializeSdcard(void)
{
static const char *SDCARD_TAG = "SDCARD_INIT";
esp_err_t ret;
ESP_LOGI(SDCARD_TAG, "Initializing SD card");
if (is_sdcard_initialized_) {
ESP_LOGW(SDCARD_TAG, "SD card already initialized");
return ESP_OK;
}
// 初始化外设管理
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
periph_cfg.extern_stack = true; // 将任务栈分配到 PSRAM 以节省 Internal SRAM
periph_set_ = esp_periph_set_init(&periph_cfg);
if (!periph_set_) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize peripheral set");
return ESP_FAIL;
}
// 初始化SD卡外设
ret = audio_board_sdcard_init(periph_set_, SD_MODE_1_LINE);
if (ret != ESP_OK) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
return ret;
}
is_sdcard_initialized_ = true;
ESP_LOGI(SDCARD_TAG, "SD card initialized successfully");
return ESP_OK;
}
/**
* @brief 统一的硬件初始化接口实现
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备可能依赖)
* 2. IO扩展板(可能影响其他设备)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. 其他外设
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardware(void)
{
static const char *HW_INIT_TAG = "HW_INIT";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting unified hardware initialization...");
// 1. 初始化I2C总线(最基础)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "SD card initialized");
}
// 4. 初始化LCD IO引脚
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "LCD IO pins initialized");
// 5. 初始化底座控制
if (bus_handle_ != nullptr) {
BaseBoardHalI2c *hal_i2c = new BaseBoardHalI2c(bus_handle_);
base_board_control_.Init(hal_i2c);
ESP_LOGI(HW_INIT_TAG, "Base board control initialized with I2C handle");
} else {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize base board control: I2C bus is null");
}
// 6. 可选外设(根据具体应用需求)
// BaseBoardControlUartInit(); // 底座控制串口(demo5需要)
// ESP_LOGI(HW_INIT_TAG, "Base board control UART initialized");
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
StepperMotorInit();
ESP_LOGI(HW_INIT_TAG, "Unified hardware initialization completed successfully");
return ESP_OK;
}
/**
* @brief 进入低功耗模式
*
* 根据指定的功耗模式,执行相应的功耗优化策略:
* - IDLE模式:关闭非必要外设,保持快速响应
* - LIGHT_SLEEP模式:进入轻度睡眠,CPU暂停,外设断电
* - DEEP_SLEEP模式:深度睡眠,仅RTC运行,功耗最低
*/
esp_err_t BoardSweekar::EnterLowPowerMode(power_mode_t mode) {
static const char *POWER_TAG = "POWER_MGMT";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "Entering power mode: %d", mode);
if (mode == current_power_mode_) {
ESP_LOGW(POWER_TAG, "Already in power mode %d", mode);
return ESP_OK;
}
// 保存当前模式,以便恢复
power_mode_t previous_mode = current_power_mode_;
switch (mode) {
case POWER_MODE_NORMAL:
// 正常模式,不需要特殊处理
break;
case POWER_MODE_IDLE:
// 空闲模式:关闭非必要外设
ESP_LOGI(POWER_TAG, "Idle mode: disabling non-essential peripherals");
// 关闭SD卡(如果正在使用,需要先卸载)
if (is_sdcard_initialized_) {
// 注意:这里只是示例,实际应用中需要确保没有文件操作
ESP_LOGW(POWER_TAG, "SD card still active in idle mode");
}
// 关闭显示背光
// 关闭非必要GPIO的上拉/下拉
// 降低I2C时钟频率
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠
ESP_LOGI(POWER_TAG, "Light sleep mode: preparing for sleep");
// 设置唤醒源(需要根据具体需求配置)
// esp_sleep_enable_timer_wakeup(1000000); // 1秒后唤醒
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_XX, 0); // 外部中断唤醒
// 进入轻度睡眠
// ret = esp_light_sleep_start();
if (ret != ESP_OK) {
ESP_LOGE(POWER_TAG, "Failed to enter light sleep: %s", esp_err_to_name(ret));
current_power_mode_ = previous_mode;
return ret;
}
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠
ESP_LOGI(POWER_TAG, "Deep sleep mode: system will restart after wake-up");
// 保存必要的RTC数据
// rtc_gpio_isolate(GPIO_NUM_XX); // 隔离GPIO
// 设置深度睡眠唤醒源
// esp_sleep_enable_timer_wakeup(60000000); // 60秒后唤醒
// 进入深度睡眠
// esp_deep_sleep_start();
// 注意:深度睡眠后系统会重启,不会返回
break;
default:
ESP_LOGE(POWER_TAG, "Invalid power mode: %d", mode);
return ESP_ERR_INVALID_ARG;
}
current_power_mode_ = mode;
ESP_LOGI(POWER_TAG, "Successfully entered power mode: %d", mode);
return ret;
}
/**
* @brief 退出低功耗模式
*
* 从低功耗模式恢复到正常运行模式,重新初始化必要的硬件资源。
*/
esp_err_t BoardSweekar::ExitLowPowerMode(void) {
static const char *POWER_TAG = "POWER_MGMT";
ESP_LOGI(POWER_TAG, "Exiting low power mode");
if (current_power_mode_ == POWER_MODE_NORMAL) {
ESP_LOGW(POWER_TAG, "Already in normal mode");
return ESP_OK;
}
// 根据之前的功耗模式执行相应的恢复操作
switch (current_power_mode_) {
case POWER_MODE_IDLE:
// 恢复空闲模式
ESP_LOGI(POWER_TAG, "Restoring from idle mode");
// 重新初始化必要的外设
// 恢复I2C时钟频率
// 重新开启显示背光
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠唤醒后会自动继续执行
ESP_LOGI(POWER_TAG, "Woken up from light sleep");
// 重新配置时钟(如果需要)
// 重新初始化外设
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠后系统会重启,不会执行到这里
ESP_LOGW(POWER_TAG, "Deep sleep recovery should not reach here");
break;
default:
break;
}
// 恢复到正常模式
current_power_mode_ = POWER_MODE_NORMAL;
ESP_LOGI(POWER_TAG, "Successfully exited low power mode");
return ESP_OK;
}
/**
* @brief 设置硬件模块电源状态
*
* 提供精细化的电源管理,可以单独控制各个硬件模块的电源状态。
*/
esp_err_t BoardSweekar::SetModulePower(hardware_module_t module, bool enable) {
static const char *POWER_TAG = "POWER_MODULE";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "%s module %d", enable ? "Enabling" : "Disabling", module);
// 更新电源状态位图
if (enable) {
power_state_flags_ |= (1 << module);
} else {
power_state_flags_ &= ~(1 << module);
}
switch (module) {
case HW_MODULE_I2C:
if (!enable && bus_handle_) {
// 注意:I2C总线通常不建议完全关闭,可能影响多个设备
ESP_LOGW(POWER_TAG, "I2C bus power control not recommended");
}
break;
case HW_MODULE_SDCARD:
if (enable && !is_sdcard_initialized_) {
ret = InitializeSdcard();
} else if (!enable && is_sdcard_initialized_) {
// 卸载SD卡文件系统
// 注意:需要确保没有打开的文件
ESP_LOGW(POWER_TAG, "SD card power down requires file system cleanup");
}
break;
case HW_MODULE_LCD:
// LCD背光控制
// LCD电源引脚控制(如果有)
break;
case HW_MODULE_AUDIO:
// 音频编解码器电源控制
// 需要与AudioPlayer协调
break;
case HW_MODULE_MOTOR:
// 步进电机驱动器电源控制
// 电机睡眠模式
break;
case HW_MODULE_GYROSCOPE:
// 陀螺仪电源控制
// 设置陀螺仪为低功耗模式
break;
case HW_MODULE_TOUCH:
// 触摸传感器电源控制
// 设置触摸传感器为睡眠模式
break;
case HW_MODULE_RADAR:
// 毫米波雷达电源控制
// 设置雷达为低功耗模式
break;
case HW_MODULE_IO_EXPAND:
// IO扩展板电源控制
// 设置扩展板为低功耗模式
break;
default:
ESP_LOGE(POWER_TAG, "Unknown hardware module: %d", module);
return ESP_ERR_INVALID_ARG;
}
if (ret == ESP_OK) {
ESP_LOGI(POWER_TAG, "Module %d power %s successfully", module, enable ? "enabled" : "disabled");
} else {
ESP_LOGE(POWER_TAG, "Failed to %s module %d: %s", enable ? "enable" : "disable", module, esp_err_to_name(ret));
}
return ret;
}
@@ -0,0 +1,181 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include "config.h"
#include "io_expansion_board.h"
#include "stepper_motor.h"
#include "stepper_driver.h"
#include "i2c_bus.h"
#include "gyroscope.h"
#include "touch_sensor.h"
#include "base_board_control.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
/**
* @brief 功耗模式枚举
*/
typedef enum {
POWER_MODE_NORMAL, // 正常运行模式,所有硬件可用
POWER_MODE_IDLE, // 空闲模式,关闭非必要外设
POWER_MODE_LIGHT_SLEEP, // 轻度睡眠,快速唤醒
POWER_MODE_DEEP_SLEEP, // 深度睡眠,仅RTC运行
} power_mode_t;
/**
* @brief 硬件模块枚举,用于精细化电源管理
*/
typedef enum {
HW_MODULE_I2C, // I2C总线
HW_MODULE_SDCARD, // SD卡
HW_MODULE_LCD, // LCD显示
HW_MODULE_AUDIO, // 音频编解码器
HW_MODULE_MOTOR, // 步进电机
HW_MODULE_GYROSCOPE, // 陀螺仪
HW_MODULE_TOUCH, // 触摸传感器
HW_MODULE_RADAR, // 毫米波雷达
HW_MODULE_IO_EXPAND, // IO扩展板
} hardware_module_t;
/**
* @brief 硬件板级管理器
*
* 采用高内聚低耦合设计原则,统一管理所有硬件资源。
* 提供统一的硬件初始化接口和资源访问,避免各模块重复初始化。
*/
class BoardSweekar
{
private:
i2c_bus_handle_t bus_handle_ = nullptr; // 使用ESP-ADF的I2C总线句柄
esp_periph_set_handle_t periph_set_ = nullptr; // 外设管理句柄
bool is_sdcard_initialized_ = false; // SD卡初始化状态
power_mode_t current_power_mode_ = POWER_MODE_NORMAL; // 当前功耗模式
uint32_t power_state_flags_ = 0xFFFFFFFF; // 各模块电源状态位图(1为开启)
public:
BoardSweekar();
~BoardSweekar();
static BoardSweekar* GetInstance();
// --- 语义化的高层 API:音频与功放控制 ---
// 开启或关闭外放功放 (PA),供上层 AudioActor/AudioPlayer 调用,屏蔽底层引脚实现
void SetAudioAmplifier(bool enable);
IOExpansionBoard io_expansion_; // IO扩展板对象
Gyroscope gyroscope_; // 陀螺仪对象
TouchSensor touch_sensor_; // 触摸传感器对象
BaseBoardControl base_board_control_; // 底座控制对象
StepperDriver* stepper_driver_ = nullptr; // 步进电机驱动对象
StepperDriver* GetStepperDriver() const { return stepper_driver_; }
// 初始化步进电机
void StepperMotorInit(void);
// 初始化LCD IO引脚(消除与步进电机的耦合)
void LcdIoInit(void);
// 初始化串口
void MillimeterWaveRadarUartInit(void);
//初始化毫米波雷达的中断引脚
void MillimeterWaveRadarGpioInit(void);
/**
* @brief 执行系统关机
* 内部拉高/拉低 SHUTDOWN_CTRL_PIN 引脚切断电源
*/
void SystemShutdown(void);
// 初始化I2C总线
esp_err_t I2CMasterInit(void);
// 初始化VBAT toVBAT_5V引脚
void VbatToVbat5vInit(void);
// 初始化底座控制串口
void BaseBoardControlInit(void);
// 初始化SD卡(推荐使用统一的硬件初始化)
esp_err_t InitializeSdcard(void);
// 获取SD卡初始化状态
bool IsSdcardInitialized() const { return is_sdcard_initialized_; }
// 获取外设管理句柄(供需要访问SD卡的模块使用)
esp_periph_set_handle_t GetPeriphSet() const { return periph_set_; }
// 获取I2C总线句柄
i2c_bus_handle_t GetBusHandle() const { return bus_handle_; }
/**
* @brief 统一的硬件初始化接口
*
* 按照依赖关系顺序初始化所有硬件资源,确保各模块可以安全使用。
* 推荐在主程序启动时调用此函数,而不是分别调用各个初始化函数。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardware(void);
/**
* @brief 完整的统一硬件初始化接口(专为复杂应用优化)
*
* 包含所有必要外设的完整初始化,适用于demo5等复杂应用场景。
* 这个函数会初始化:I2C总线、IO扩展板、SD卡、LCD IO、底座控制等。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardwareComplete(void);
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置,优化启动流程。
*
* @param app_type 应用类型(demo5、音频播放器、显示应用等)
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeHardwareForApplication(int app_type);
/**
* @brief 功耗管理接口 - 进入低功耗模式
*
* 根据指定的功耗模式,关闭非必要硬件、设置GPIO为低功耗状态、
* 调整时钟频率等。支持多种功耗模式以适应不同应用场景。
*
* @param mode 功耗模式
* @return esp_err_t 操作结果
*/
esp_err_t EnterLowPowerMode(power_mode_t mode);
/**
* @brief 功耗管理接口 - 退出低功耗模式
*
* 从低功耗模式恢复,重新初始化必要的硬件资源。
*
* @return esp_err_t 操作结果
*/
esp_err_t ExitLowPowerMode(void);
/**
* @brief 获取当前功耗模式
*
* @return power_mode_t 当前功耗模式
*/
power_mode_t GetPowerMode(void) const { return current_power_mode_; }
/**
* @brief 设置硬件模块电源状态
*
* 单独控制某个硬件模块的电源状态,用于精细化的功耗管理。
*
* @param module 硬件模块
* @param enable true为开启,false为关闭
* @return esp_err_t 操作结果
*/
esp_err_t SetModulePower(hardware_module_t module, bool enable);
};
#endif
@@ -0,0 +1,186 @@
/**
* @file board_sweekar_complete_init.cc
* @brief 完整的统一硬件初始化实现
*
* 这个文件提供了完整的统一硬件初始化函数,包含所有必要的硬件初始化步骤。
* 专为demo5等复杂应用场景设计。
*/
/**
* @brief 完整的统一硬件初始化接口(专为demo5优化)
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备依赖)
* 2. IO扩展板(控制其他外设)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. LCD IO引脚(显示相关)
* 5. 底座控制串口(通信)
* 6. 底座控制模块(业务逻辑)
* 7. 其他外设(根据应用需求)
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardwareComplete(void)
{
static const char *HW_INIT_TAG = "HW_INIT_COMPLETE";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting complete hardware initialization...");
// 1. 初始化I2C总线(最基础,其他设备依赖)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "✓ I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "✓ I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "⚠ SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "✓ SD card initialized");
}
// 4. 初始化LCD IO引脚(显示相关)
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "✓ LCD IO pins initialized");
// 5. 初始化底座控制串口(demo5需要)
BaseBoardControlUartInit();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control UART initialized");
// 6. 初始化底座控制模块
base_board_control_.Init();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control initialized");
// 7. 设置IO扩展板的PA控制模式(demo5需要)
io_expansion_.SetPaCtrlMode(true);
ESP_LOGI(HW_INIT_TAG, "✓ IO expansion PA control mode set");
// 8. 可选外设(根据具体应用需求启用)
// ESP_LOGI(HW_INIT_TAG, "Initializing optional peripherals...");
//
// // 毫米波雷达(如需要)
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Millimeter wave radar initialized");
//
// // VBAT升压(如需要)
// VbatToVbat5vInit();
// ESP_LOGI(HW_INIT_TAG, "✓ VBAT to 5V booster initialized");
//
// // 步进电机(如需要)
// StepperMotorInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Stepper motor initialized");
ESP_LOGI(HW_INIT_TAG, "=== Complete hardware initialization finished successfully ===");
return ESP_OK;
}
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置
*/
esp_err_t BoardSweekar::InitializeHardwareForApplication(application_type_t app_type) {
static const char *APP_INIT_TAG = "APP_HW_INIT";
ESP_LOGI(APP_INIT_TAG, "Initializing hardware for application type: %d", app_type);
// 基础硬件初始化(所有应用都需要)
esp_err_t ret = InitializeAllHardwareComplete();
if (ret != ESP_OK) {
ESP_LOGE(APP_INIT_TAG, "Basic hardware initialization failed");
return ret;
}
switch (app_type) {
case APP_TYPE_DEMO5: // 宠物游戏demo
ESP_LOGI(APP_INIT_TAG, "Configuring for Demo5 (Pet Game)...");
// Demo5需要的额外初始化
// 触摸传感器、陀螺仪、RGB LED等会在应用层初始化
ESP_LOGI(APP_INIT_TAG, "✓ Demo5 hardware configuration complete");
break;
case APP_TYPE_AUDIO_PLAYER: // 音频播放器
ESP_LOGI(APP_INIT_TAG, "Configuring for Audio Player...");
// 音频应用需要确保音频编解码器正确初始化
// 这部分通常在AudioPlayer类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Audio player hardware configuration complete");
break;
case APP_TYPE_DISPLAY_ONLY: // 纯显示应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Display Only...");
// 显示应用需要确保LCD相关硬件正确配置
// 这部分通常在Display类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Display-only hardware configuration complete");
break;
case APP_TYPE_MINIMAL: // 最小化应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Minimal Application...");
// 最小化配置,只保留最基本的功能
// 关闭非必要外设以节省功耗
ESP_LOGI(APP_INIT_TAG, "✓ Minimal hardware configuration complete");
break;
default:
ESP_LOGW(APP_INIT_TAG, "Unknown application type, using default configuration");
break;
}
return ESP_OK;
}
/**
* @brief 硬件初始化状态报告
*
* 提供详细的硬件初始化状态报告,便于调试和问题排查
*/
void BoardSweekar::ReportHardwareStatus(void) {
static const char *STATUS_TAG = "HW_STATUS";
ESP_LOGI(STATUS_TAG, "=== Hardware Initialization Status Report ===");
// I2C总线状态
ESP_LOGI(STATUS_TAG, "I2C Bus: %s", (bus_handle_ != nullptr) ? "✓ Initialized" : "✗ Not initialized");
// SD卡状态
ESP_LOGI(STATUS_TAG, "SD Card: %s", (is_sdcard_initialized_) ? "✓ Initialized" : "✗ Not initialized");
if (is_sdcard_initialized_) {
ESP_LOGI(STATUS_TAG, " Peripheral Set: %s", (periph_set_ != nullptr) ? "✓ Available" : "✗ Not available");
}
// IO扩展板状态
ESP_LOGI(STATUS_TAG, "IO Expansion Board: %s", (io_expansion_.bus_handle_ != nullptr) ? "✓ Configured" : "✗ Not configured");
// 底座控制状态
ESP_LOGI(STATUS_TAG, "Base Board Control: %s", (base_board_control_.GetUartPort() != 0) ? "✓ Initialized" : "✗ Not initialized");
// 功耗管理状态
ESP_LOGI(STATUS_TAG, "Power Management: %s", (current_power_mode_ == POWER_MODE_NORMAL) ? "✓ Normal mode" : "⚠ Low power mode");
ESP_LOGI(STATUS_TAG, " Current Mode: %d", current_power_mode_);
ESP_LOGI(STATUS_TAG, " Module Power States: 0x%08X", power_state_flags_);
// 内存状态
ESP_LOGI(STATUS_TAG, "Memory Status:");
ESP_LOGI(STATUS_TAG, " Internal RAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
ESP_LOGI(STATUS_TAG, " PSRAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
ESP_LOGI(STATUS_TAG, "=== End of Hardware Status Report ===");
}
@@ -0,0 +1,136 @@
/* 定义引脚分配 */
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
/**
* @file config.h
* @brief 定义了引脚的分配。
* @author zouyuhua
* @date 2025年8月7日
*
*/
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_rom_sys.h" // 用于 esp_rom_delay_us
#include "esp_timer.h"
#include "esp_mac.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gptimer.h"
#include "esp_intr_alloc.h"
#include "driver/uart.h"
#include "esp_err.h"
#include "freertos/queue.h"
#include "soc/uart_reg.h"
#include "linear_motor.h"
#include "gyroscope.h"
#define TAG_BOARD "SWEEKAR_ESP32"
/*I2C总线引脚定义*/
#define I2C0_MASTER_PORT I2C_NUM_0
#define I2C0_MASTER_SDA_IO GPIO_NUM_2
#define I2C0_MASTER_SCL_IO GPIO_NUM_3
/* 步进电机配置 */
#define STEPPER_STEP_IO GPIO_NUM_42 // 步进电机脉冲输入引脚
#define STEPPER_TIMER_RES_HZ 1000000 // 步进电机定时器分辨率 1 MHz(1 us 步进)
/* 定义屏幕驱动控制引脚 */
#define ST7789_PIN_CLK GPIO_NUM_48 // SPI时钟引脚
#define ST7789_PIN_DC GPIO_NUM_38 // 数据/命令选择引脚RSEL
#define ST7789_PIN_MOSI GPIO_NUM_47 // SPI数据输出引脚
#define ST7789_PIN_CS GPIO_NUM_46 // SPI片选引脚
#define ST7789_PIN_BL GPIO_NUM_45 // 背光引脚
#define ST7789_PIN_RST GPIO_NUM_NC // 复位引脚
#define LCD_BL_ACTIVE_LEVEL 0 // 背光有效电平:1=高电平有效(NMOS),0=低电平有效(PMOS)
#define LCD_WIDTH 172 // 屏幕宽度(水平方向像素数)
#define LCD_HEIGHT 320 // 屏幕高度(垂直方向像素数)
/* 毫米波雷达配置参数 */
/* 定义毫米波雷达的中断引脚 */
#define MILLIMETER_WAVE_RADAR_PIN GPIO_NUM_35
// UART 配置参数
#define RADAR_UART_NUM UART_NUM_1 // 使用 UART1
#define UART_TX_PIN GPIO_NUM_40 // GPIO40 作为 TX(根据实际硬件连接调整)
#define UART_RX_PIN GPIO_NUM_39 // GPIO39 作为 RX
#define RADAR_BAUD_RATE 9600 // 雷达波特率
#define RADAR_BUF_SIZE 100 // 雷达接收缓冲区大小
/* IO扩展硬件配置常量 */
#define I2C_MASTER_FREQ_HZ 100000 // I2C通信频率(100kHz)(可更改至400000)
#define I2C_IO_EXPAND_SLAVE_ADDR 0x58 // AW9523BTQR器件地址,v1_1_13的addr1和addr0是0
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // 中断检测引脚
#define ESP_INTR_FLAG_DEFAULT 0 // 中断标志默认值
#define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/* 定义线性马达的I2C从机配置常量 */
#define LINEAR_MOTOR_I2C_SLAVE_ADDR 0x5A // 线性马达的I2C从机地址
/* 定义陀螺仪的I2C从机配置常量 */
#define GYROSCOPE_I2C_SLAVE_ADDR 0x6A // 陀螺仪的I2C从机地址
#define GYROSCOPE_INPUT_PIN_SEL GPIO_NUM_21 // 陀螺仪中断引脚
/* AW触摸芯片配置 */
#define AW_TOUCH_IRQ_PIN GPIO_NUM_11 // AW触摸芯片中断引脚
#define NFC_RST_PIN GPIO_NUM_12 // NFC复位引脚
#define SHUTDOWN_CTRL_PIN GPIO_NUM_13 // 关机控制引脚
/* 原生触摸引脚配置 (v1.1.17已改为上述功能,保留定义需谨慎) */
// 触摸按键数量配置
#define TOUCH_BUTTON_NUM 3
// 触摸通道定义 - 注意:v1.1.17如果不使用ESP32原生触摸,这些定义可能不再适用
// 但为了兼容编译,可以保留或注释
#define TOUCH_PAD_CHANNEL_1 TOUCH_PAD_NUM11 // GPIO11 (Now AW_IRQ)
#define TOUCH_PAD_CHANNEL_2 TOUCH_PAD_NUM12 // GPIO12 (Now NFC_RST)
#define TOUCH_PAD_CHANNEL_3 TOUCH_PAD_NUM13 // GPIO13 (Now SHUTDOWN)
/* ES8311音频编解码器配置 (ESP-ADF兼容) */
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
/* I2S GPIO定义 */
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_15
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
/* PA引脚定义 */
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
/* I2C配置 */
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_2
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_3
#define AUDIO_CODEC_ES8311_ADDR 0x18
#define AUDIO_CODEC_ES7210_ADDR 0x40
/* WS2812 RGB LED配置 */
#define WS2812_PIN GPIO_NUM_41 // WS2812数据引脚
#define WS2812_NUM_LEDS 2 // WS2812 LED数量
/* 创建VBAT to VBAT_5V */
#define VBAT_5V_PIN GPIO_NUM_NC // VBAT_5V引脚
/* 定义按键的中断引脚 */
#define BUTTON_PIN GPIO_NUM_0 // 按键中断引脚
#define RIGHT_BUTTON_PIN GPIO_NUM_8 // 按键中断引脚
/* 底座控制命令串口配置 */
#define BASE_BOARD_CTL_UART_PORT UART_NUM_0
#define BASE_BOARD_CTL_UART_TX_PIN GPIO_NUM_43
#define BASE_BOARD_CTL_UART_RX_PIN GPIO_NUM_44
#define BASE_BOARD_CTL_UART_BAUDRATE 115200
#define BASE_BOARD_CTL_UART_BUF_SIZE 256
/* 背光控制引脚配置 */
#define BL_PIN GPIO_NUM_45
#endif // _BOARD_CONFIG_H_
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,432 @@
#ifndef IO_EXPANSION_BOARD_H
#define IO_EXPANSION_BOARD_H
#include <cstdint>
#include <esp_log.h>
#include <esp_check.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include <functional>
#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 "portmacro.h"
// 前向声明,避免循环引用
class MillimeterWaveRadar;
class PhotoInterrupter;
class AdcBatteryMonitor;
// 回调函数类型定义
typedef std::function<void()> RadarCallback;
typedef std::function<void()> PhotoInterrupterStartCallback;
typedef std::function<void()> PhotoInterrupterEndCallback;
typedef std::function<void()> BatteryChargingCallback;
typedef std::function<void()> BatteryFullCallback;
typedef std::function<void()> BatteryNotChargingCallback;
/* 寄存器地址 */
#define INPUT_PORT0 0x00 // 输入端口0寄存器
#define INPUT_PORT1 0x01 // 输入端口1寄存器 0为低电平(仅支持读取)
#define OUTPUT_PORT0 0x02 // 输出端口0寄存器
#define OUTPUT_PORT1 0x03 // 输出端口1寄存器 0为低电平
#define CONFIG_PORT0 0x04 // 配置端口0寄存器
#define CONFIG_PORT1 0x05 // 配置端口1寄存器 0为输出模式
#define INT_PORT0 0x06 // 中断端口0寄存器
#define INT_PORT1 0x07 // 中断端口1寄存器 0为使能中断(根据手册默认是使能的)
#define CTL 0x11 // 全局控制寄存器
#define ID 0x23 // 芯片ID寄存器(0x23)
/***********************************IO扩展板电机引脚配置参数*************************************/
/* 设置步进电机方向 */
#define STEPPER_MOTOR_LEFT false //步进电机的方向为左转
#define STEPPER_MOTOR_RIGHT true //步进电机的方向为右转
#define STEPPER_MOTOR_DIR_PORT OUTPUT_PORT0 //步进电机方向引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_DIR_PIN 4 //步进电机方向引脚对应的IO扩展板引脚P0_4
#define STEPPER_MOTOR_DIR_CONFIG CONFIG_PORT0 //步进电机方向引脚配置在P0_4
/* 设置步进电机细分模式 */
#define STEPPER_MOTOR_M0_PIN 5 //步进电机细分模式选择引脚对应IO扩展板引脚P1_5
#define STEPPER_MOTOR_M0_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M0_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_5
#define STEPPER_MOTOR_M1_PIN 4 //步进电机细分模式选择引脚对应IO扩展板引脚P1_4
#define STEPPER_MOTOR_M1_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M1_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_4
/* 设置步进电机睡眠引脚 */
#define STEPPER_MOTOR_SLEEP false //步进电机进入睡眠模式
#define STEPPER_MOTOR_WORK true //步进电机进入工作模式
#define STEPPER_MOTOR_SLEEP_PIN 6 //步进电机睡眠引脚对应IO扩展板引脚P1_6
#define STEPPER_MOTOR_SLEEP_CONFIG CONFIG_PORT1 //步进电机睡眠引脚配置在P1_6
#define STEPPER_MOTOR_SLEEP_PORT OUTPUT_PORT1 //步进电机睡眠引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置电机的使能引脚 */
#define STEPPER_MOTOR_ENABLE_PIN 7 //电机使能引脚对应IO扩展板引脚P0_7
#define STEPPER_MOTOR_ENABLE_PORT OUTPUT_PORT0 //电机使能引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_ENABLE_CONFIG CONFIG_PORT0 //电机使能引脚配置在P0_7
#define STEPPER_MOTOR_ENABLE_ENABLE true //电机使能引脚状态为高电平
#define STEPPER_MOTOR_ENABLE_DISABLE false //电机使能引脚状态为低电平
/* 设置毫米波雷达对应IO扩展板的中断引脚 */
#define RADAR_INTERRUPT_PIN 7 //毫米波雷达中断引脚对应IO扩展板引脚P1_7
#define RADAR_INTERRUPT_CONFIG CONFIG_PORT1 //毫米波雷达中断引脚挂载在IO扩展板的输入端口
#define RADAR_INTERRUPT_INPUT INPUT_PORT1 //毫米波雷达中断引脚输入状态检测在INPUT_PORT1
#define RADAR_INTERRUPT_ENABLE true //毫米波雷达中断引脚状态为输入状态
#define RADAR_INTERRUPT_DISABLE false //毫米波雷达中断引脚状态为输出状态
/* 任务配置常量 */
#define WRITE_BUF_SIZE 2 // I2C单次写入字节数
#define READ_BUF_SIZE 2 // I2C单次读取字节数
#define I2C_TASK_PRIORITY 2 // 任务优先级(中等)
#define I2C_TASK_STACK 4096 // 任务栈大小(4KB)
// 光学传感器引脚定义
#define START_PHOTO_INTERRUPTER_PIN 2 // 光学传感器引脚(P0_2)
#define START_PHOTO_INTERRUPTER_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define START_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define START_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define START_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
#define END_PHOTO_INTERRUPTER_PIN 3 // 光学传感器引脚(P0_3)
#define END_PHOTO_INTERRUPTER_PORT_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define END_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define END_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define END_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
/***********************************IO扩展板LCD引脚配置参数*************************************/
// /* 设置LCD的BL引脚 */
// #define LCD_BL_PIN 6 //LCD背光引脚对应IO扩展板引脚P0_6
// #define LCD_BL_PORT OUTPUT_PORT0 //LCD背光引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
// #define LCD_BL_CONFIG CONFIG_PORT0 //LCD背光引脚配置在P0_6
// #define LCD_BL_HIGH true //LCD背光引脚状态为高电平
// #define LCD_BL_LOW false //LCD背光引脚状态为低电平
/* 设置LED_EN (LCD背光/LED控制)引脚, PMOS高侧驱动 */
#define LED_EN_PIN 1 //LED_EN对应IO扩展板引脚P0_1
#define LED_EN_PORT OUTPUT_PORT0 //挂载在输出端口OUTPUT_PORT0
#define LED_EN_CONFIG CONFIG_PORT0 //配置在P0_1
#define LED_EN_OFF true //高电平关(PMOS)
#define LED_EN_ON false //低电平开(PMOS)
/* 设置LCD的RST引脚 */
#define LCD_RST_PIN 1 //LCD复位引脚对应IO扩展板引脚P1_1
#define LCD_RST_CONFIG CONFIG_PORT1 //LCD复位引脚配置在P1_1
#define LCD_RST_PORT OUTPUT_PORT1 //LCD复位引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_RST_HIGH true //LCD复位引脚状态为高电平
#define LCD_RST_LOW false //LCD复位引脚状态为低电平
/* 设置PA控制引脚 */
#define PA_CTRL_PIN 2 //PA控制引脚对应IO扩展板引脚P1_2
#define PA_CTRL_CONFIG CONFIG_PORT1 //PA控制引脚配置在P1_2
#define PA_CTRL_PORT OUTPUT_PORT1 //PA控制引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define PA_CTRL_HIGH true //PA控制引脚状态为高电平
#define PA_CTRL_LOW false //PA控制引脚状态为低电平
// 电池充电检测引脚定义
#define BATTERY_FULL_INPUT INPUT_PORT1 // 电池充满检测引脚挂载在IO扩展板的输入端口
#define BATTERY_FULL_PIN 3 // 电池充满检测引脚对应IO扩展板引脚P1_3
#define BATTERY_FULL_CONFIG CONFIG_PORT1 // 电池充满检测引脚配置在P1_3
#define BATTERY_CHRG_INPUT INPUT_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define BATTERY_CHRG_PIN 5 // 充电检测引脚(P0_5)
#define BATTERY_CHRG_CONFIG CONFIG_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define TAG_GPIO_IO_EXPAND_DETECT "GPIO_IO_EXPAND_DETECT" // GPIO中断检测日志标签
#define TAG_I2C_IO_EXPAND_TASK "I2C_IO_EXPAND_TASK" // I2CIO扩展板任务日志标签
// GPIO引脚定义
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // IO扩展板中断检测引脚
// #define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/**
* @class IOExpansionBoard
* @brief I/O扩展板驱动类,封装AW9523BTQR芯片操作
*/
class IOExpansionBoard
{
public:
/**
* @brief 构造函数
*/
IOExpansionBoard();
/**
* @brief 设置毫米波雷达指针
* @param radar 毫米波雷达对象指针
*/
void SetRadar(MillimeterWaveRadar* radar) { radar_ = radar; };
/**
* @brief 设置限位开关指针
* @param photo_interrupter 限位开关对象指针
*/
void setPhotoInterrupter(PhotoInterrupter* photo_interrupter) { photo_interrupter_ = photo_interrupter; };
/**
* @brief 设置电池监控器指针
* @param battery_monitor 电池监控器对象指针
*/
void setBatteryMonitor(AdcBatteryMonitor* battery_monitor) { battery_monitor_ = battery_monitor; };
/**
* @brief 析构函数
*/
~IOExpansionBoard();
/**
* @brief 初始化GPIO中断检测
* @param gpio_num 中断引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetIoExpand();
/**
* @brief 初始化I2C主机通信
* @return esp_err_t 初始化结果
*/
// esp_err_t IoExpansionI2cInit();
/**
* @brief 创建I2C数据读取任务
*/
void CreateI2cReadTask();
/**
* @brief 启动I2C任务(整合初始化和任务创建)
* @return esp_err_t 操作结果
*/
esp_err_t StartTask();
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandWriteReg(uint8_t reg_addr, uint8_t data);
/**
* @brief 读寄存器数据
* @param reg_addr 寄存器地址
* @param data 要读出的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandReadReg(uint8_t reg_addr, uint8_t *data);
/**
* @brief 读取输入端口0寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort0(uint8_t *data);
/**
* @brief 读取输入端口1寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort1(uint8_t *data);
// /**
// * @brief 设置IO扩展板GPIO中断状态 通过读取Input_Port0 和 Input_Port1 寄存器可以清除中断
// * @param reg_addr 寄存器地址
// * @return esp_err_t 操作结果
// */
// esp_err_t SetInterruptStatus(bool status);
/**
* @brief 配置IO扩展版上的IO口为输出还是输入,并且设置初始为高电平
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandSetMode();
/**
* @brief 设置步进电机的细分模式
* @param mode 模式:1/2/4/8/16/32/64
* @return esp_err_t 操作结果
*/
void SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT_m0, uint8_t m0_pin, uint8_t OUTPUT_PORT_m1, uint8_t m1_pin);
/**
* @brief 设置步进电机的方向
* @param dir 方向
* @param OUTPUT_PORT 输出端口
* @param pin 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin);
/**
* @brief 设置步进电机的睡眠模式
* @param 是否进入睡眠模式
* @param 输出端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin);
/**
* @brief 设置步进电机的使能引脚状态
* @param 使能状态
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin);
/**
* @brief 设置LCD的BL引脚状态
* @param BL状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin);
/**
* @brief 设置 LED_EN 引脚电平(用于控制LCD背光/LED灯电源)
* @param mode true:开启(低电平), false:关闭(高电平)
*/
esp_err_t SetLedEnMode(bool mode);
/**
* @brief 设置LCD的RST引脚状态
* @param RST状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t rst_pin);
/**
* @brief 设置PA控制引脚状态
* @param mode PA_CTRL状态(true为高电平,false为低电平)
* @return esp_err_t 操作结果
*/
esp_err_t SetPaCtrlMode(bool mode);
/**
* @brief 设置雷达中断引脚
* @param 毫米波雷达的中断引脚模式
* @param 输入端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin);
/**
* @brief 计算Port0的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort0();
/**
* @brief 计算Port1的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort1();
/**
* @brief 设置雷达中断回调函数
* @param callback 雷达中断回调函数
*/
void SetRadarCallback(RadarCallback callback) { radar_callback_ = callback; }
/**
* @brief 设置光学传感器开始中断回调函数
* @param callback 光学传感器开始中断回调函数
*/
void SetPhotoInterrupterStartCallback(PhotoInterrupterStartCallback callback) { photo_start_callback_ = callback; }
/**
* @brief 设置光学传感器结束中断回调函数
* @param callback 光学传感器结束中断回调函数
*/
void SetPhotoInterrupterEndCallback(PhotoInterrupterEndCallback callback) { photo_end_callback_ = callback; }
/**
* @brief 设置电池充电中断回调函数
* @param callback 电池充电中断回调函数
*/
void SetBatteryChargingCallback(BatteryChargingCallback callback) { battery_charging_callback_ = callback; }
/**
* @brief 设置电池充满中断回调函数
* @param callback 电池充满中断回调函数
*/
void SetBatteryFullCallback(BatteryFullCallback callback) { battery_full_callback_ = callback; }
/**
* @brief 设置电池非充电中断回调函数
* @param callback 电池非充电中断回调函数
*/
void SetBatteryNotChargingCallback(BatteryNotChargingCallback callback) { battery_not_charging_callback_ = callback; }
// 公共成员变量
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
uint8_t device_addr_; // I2C设备地址
private:
// 静态成员函数
MillimeterWaveRadar* radar_ = nullptr; // 毫米波雷达指针,用于触发回调
PhotoInterrupter* photo_interrupter_ = nullptr; // 限位开关指针,用于触发回调
AdcBatteryMonitor* battery_monitor_ = nullptr; // 电池监控器指针,用于触发回调
static void IRAM_ATTR GpioIsrHandler(void* arg); // 中断处理函数
static void I2cReadTask(void* pvParameters); // I2C数据读取任务函数
// 成员变量用于状态跟踪
uint8_t last_port0_state_ = 0; // 上次检测到的Port0状态
uint8_t last_port1_state_ = 0; // 上次检测到的Port1状态
bool radar_last_state_ = false; // 雷达引脚上一次的状态
bool battery_charging_last_state_ = false; // 电池充电状态上一次的状态
bool battery_full_last_state_ = false; // 电池充满状态上一次的状态
// 回调函数成员变量
RadarCallback radar_callback_ = nullptr;
PhotoInterrupterStartCallback photo_start_callback_ = nullptr;
PhotoInterrupterEndCallback photo_end_callback_ = nullptr;
BatteryChargingCallback battery_charging_callback_ = nullptr;
BatteryFullCallback battery_full_callback_ = nullptr;
BatteryNotChargingCallback battery_not_charging_callback_ = nullptr;
// PhotoInterrupter* photo_interrupter_ = nullptr; // 光学传感器指针,用于触发回调
// /**
// * @brief 分配静态任务所需的内存
// * @return bool 分配是否成功
// */
// bool AllocateStaticTaskMemory();
// /**
// * @brief 释放静态任务分配的内存
// */
// void FreeStaticTaskMemory();
// 实例成员变量
EventGroupHandle_t i2c_task_event_group_ = nullptr; // 事件组
TaskHandle_t xI2CReadTaskHandle_ = nullptr; // 任务句柄
bool is_initialized_ = false; // 初始化状态标记(新增)
gpio_num_t interrupt_gpio_ = GPIO_NUM_NC; // 中断引脚
gpio_num_t reset_gpio_ = GPIO_NUM_NC; // 复位引脚
// StackType_t* pI2CReadTaskStackBuffer = nullptr; // 任务栈指针(PSRAM)
// StaticTask_t* pxI2CReadTaskBuffer = nullptr; // 任务控制块指针(内部RAM)
};
#endif
@@ -0,0 +1,178 @@
/**
* @file power_management_config.h
* @brief 功耗管理配置和策略定义
* @author Embedded Software Expert
* @date 2025年1月
*
* 这个文件定义了功耗管理的配置参数、策略和优化建议,
* 为系统提供灵活的功耗管理方案。
*/
#ifndef POWER_MANAGEMENT_CONFIG_H
#define POWER_MANAGEMENT_CONFIG_H
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "esp32/rom/rtc.h"
/**
* @brief 功耗管理配置结构体
*/
typedef struct {
// 睡眠模式配置
uint64_t light_sleep_duration_us; // 轻度睡眠持续时间(微秒)
uint64_t deep_sleep_duration_us; // 深度睡眠持续时间(微秒)
// 唤醒源配置
bool enable_timer_wakeup; // 启用定时器唤醒
bool enable_gpio_wakeup; // 启用GPIO唤醒
bool enable_touch_wakeup; // 启用触摸唤醒
gpio_num_t wakeup_gpio_num; // 唤醒GPIO编号
int wakeup_level; // 唤醒电平(0或1)
// 功耗优化配置
bool disable_bt_in_sleep; // 睡眠时关闭蓝牙
bool disable_wifi_in_sleep; // 睡眠时关闭WiFi
bool reduce_cpu_frequency; // 降低CPU频率
uint32_t cpu_frequency_hz; // CPU频率(Hz)
// 外设功耗配置
bool power_down_sdcard; // 睡眠时关闭SD卡电源
bool power_down_display; // 睡眠时关闭显示电源
bool power_down_audio; // 睡眠时关闭音频电源
bool power_down_motor; // 睡眠时关闭电机电源
// RTC内存配置
bool preserve_rtc_memory; // 保留RTC内存
size_t rtc_memory_size; // RTC内存大小
} power_management_config_t;
/**
* @brief 默认功耗管理配置
*/
#define DEFAULT_POWER_CONFIG { \
.light_sleep_duration_us = 1000000, /* 1秒 */ \
.deep_sleep_duration_us = 30000000, /* 30秒 */ \
.enable_timer_wakeup = true, /* 启用定时器唤醒 */ \
.enable_gpio_wakeup = false, /* 不启用GPIO唤醒 */ \
.enable_touch_wakeup = false, /* 不启用触摸唤醒 */ \
.wakeup_gpio_num = GPIO_NUM_NC, /* 无GPIO唤醒 */ \
.wakeup_level = 0, /* 低电平唤醒 */ \
.disable_bt_in_sleep = true, /* 睡眠时关闭蓝牙 */ \
.disable_wifi_in_sleep = true, /* 睡眠时关闭WiFi */ \
.reduce_cpu_frequency = true, /* 降低CPU频率 */ \
.cpu_frequency_hz = 80000000, /* 80MHz */ \
.power_down_sdcard = true, /* 睡眠时关闭SD卡 */ \
.power_down_display = true, /* 睡眠时关闭显示 */ \
.power_down_audio = true, /* 睡眠时关闭音频 */ \
.power_down_motor = true, /* 睡眠时关闭电机 */ \
.preserve_rtc_memory = true, /* 保留RTC内存 */ \
.rtc_memory_size = 4096, /* 4KB RTC内存 */ \
}
/**
* @brief 各功耗模式的详细配置
*/
static const power_management_config_t idle_mode_config = {
.light_sleep_duration_us = 500000, // 500ms轻度睡眠
.deep_sleep_duration_us = 0, // 不使用深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0, // 按键唤醒
.wakeup_level = 0,
.disable_bt_in_sleep = false, // 保持蓝牙连接
.disable_wifi_in_sleep = false, // 保持WiFi连接
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000, // 40MHz
.power_down_sdcard = false, // 保持SD卡(可能有后台任务)
.power_down_display = true, // 关闭显示背光
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 2048,
};
static const power_management_config_t light_sleep_config = {
.light_sleep_duration_us = 0, // 直接进入轻度睡眠
.deep_sleep_duration_us = 0,
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000,
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 4096,
};
static const power_management_config_t deep_sleep_config = {
.light_sleep_duration_us = 0,
.deep_sleep_duration_us = 60000000, // 60秒深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 10000000, // 10MHz
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 8192,
};
/**
* @brief 功耗统计和监控结构体
*/
typedef struct {
uint64_t total_sleep_time_us; // 总睡眠时间
uint64_t total_wake_time_us; // 总唤醒时间
uint32_t sleep_count; // 睡眠次数
uint32_t wake_count; // 唤醒次数
uint32_t average_sleep_duration_us; // 平均睡眠时长
uint32_t average_wake_duration_us; // 平均唤醒时长
float estimated_power_saving_percent; // 估计的功耗节省百分比
} power_statistics_t;
/**
* @brief 功耗优化建议
*
* 基于硬件特性和使用模式,提供功耗优化建议
*/
typedef enum {
OPTIMIZATION_NONE = 0,
OPTIMIZATION_REDUCE_CPU_FREQ, // 降低CPU频率
OPTIMIZATION_DISABLE_UNUSED_PERIPHERALS, // 关闭未使用的外设
OPTIMIZATION_USE_LIGHT_SLEEP, // 使用轻度睡眠
OPTIMIZATION_USE_DEEP_SLEEP, // 使用深度睡眠
OPTIMIZATION_OPTIMIZE_GPIO_STATES, // 优化GPIO状态
OPTIMIZATION_USE_RTC_MEMORY, // 使用RTC内存
OPTIMIZATION_BATCH_OPERATIONS, // 批量操作
} power_optimization_t;
/**
* @brief 功耗管理事件回调
*/
typedef void (*power_event_callback_t)(power_mode_t old_mode, power_mode_t new_mode, void* user_data);
/**
* @brief 功耗管理事件
*/
typedef enum {
POWER_EVENT_MODE_CHANGE, // 功耗模式改变
POWER_EVENT_SLEEP_START, // 睡眠开始
POWER_EVENT_SLEEP_END, // 睡眠结束
POWER_EVENT_WAKEUP, // 系统唤醒
POWER_EVENT_OPTIMIZATION_TRIGGER, // 功耗优化触发
} power_event_t;
#endif // POWER_MANAGEMENT_CONFIG_H
@@ -0,0 +1,778 @@
/**
* @file board.cc
* @brief 板级外设初始化类,包含步进电机、毫米波雷达、I2C总线等外设的初始化函数。
* @author zouyuhua
* @date 2025年8月7日
*
*/
extern "C" {
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "driver/gptimer.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/uart_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2c_bus.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
}
#include "board_sweekar.h"
#include "config.h"
#include "board.h" // 添加board.h以获取audio_board_sdcard_init函数声明
#include "drv8428.h"
#include "aw8646.h"
#include "esp_vfs_fat.h"
#include "audio_mem.h"
#include "esp_timer.h"
#include "base_board_hal_i2c.h"
#define TAG "board_sweekar"
static BoardSweekar* s_board_instance = nullptr;
BoardSweekar* BoardSweekar::GetInstance()
{
if (s_board_instance == nullptr) {
static BoardSweekar global_board;
s_board_instance = &global_board;
}
return s_board_instance;
}
BoardSweekar::BoardSweekar()
{
#ifdef CONFIG_STEPPER_DRIVER_AW8646
stepper_driver_ = new Aw8646Driver(io_expansion_);
#else
stepper_driver_ = new Drv8428Driver(io_expansion_);
#endif
}
BoardSweekar::~BoardSweekar()
{
if (stepper_driver_) {
delete stepper_driver_;
stepper_driver_ = nullptr;
}
}
void BoardSweekar::SetAudioAmplifier(bool enable) {
// AUD_CTRL 是 codec 侧控制脚,不参与 PA 开关;PA 仍由 IO 扩展板控制。
io_expansion_.SetPaCtrlMode(enable);
ESP_LOGI(TAG, "Audio Amplifier (via IO Expansion) %s", enable ? "ENABLED" : "DISABLED");
}
/*========================== 步进电机相关驱动 ==========================*/
/**
* @brief 步进电机初始化函数
* 配置步进电机所需的GPIO引脚模式,通过IO扩展板设置步进驱动芯片的工作模式
* 在 stepper_motor.cc 中完成 GPTimer + GPIO 翻转 STEP 脉冲的初始化
*/
void BoardSweekar::StepperMotorInit(void)
{
esp_err_t err = ESP_OK;
// 1) STEP 引脚改为普通 GPIO(不再用 LEDC)
gpio_config_t io_cfg;
io_cfg.pin_bit_mask = 1ULL << STEPPER_STEP_IO;
io_cfg.mode = GPIO_MODE_OUTPUT;
io_cfg.pull_up_en = GPIO_PULLUP_DISABLE;
io_cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_cfg.intr_type = GPIO_INTR_DISABLE;
err = gpio_config(&io_cfg);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "步进电机 STEP GPIO 配置失败, 错误码: 0x%x", err);
}
// 初始电平拉低,保证上电不乱跳
gpio_set_level(STEPPER_STEP_IO, 0);
// 2) 检查 I2C 总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 3) 初始化驱动对象并初始化
if (stepper_driver_) {
err = stepper_driver_->Init();
if (err != ESP_OK) {
ESP_LOGE(TAG_BOARD, "步进电机驱动初始化失败, 错误码: 0x%x", err);
}
} else {
ESP_LOGE(TAG_BOARD, "步进电机驱动创建失败");
}
}
/**
* @brief LCD IO引脚初始化函数(消除与步进电机的耦合)
*
* 配置LCD所需的IO扩展板寄存器,确保LCD复位引脚正常工作
* 此函数独立于步进电机初始化,专门处理LCD相关的IO配置
*/
void BoardSweekar::LcdIoInit(void)
{
esp_err_t err = ESP_OK;
// 1) 检查I2C总线是否已初始化
if (bus_handle_ == nullptr)
{
ESP_LOGE(TAG_BOARD, "I2C总线未初始化,请先调用I2CMasterInit()");
return;
}
// 2) 执行硬件复位,确保IO扩展板处于已知状态
err = io_expansion_.ResetIoExpand();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板硬件复位失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板硬件复位成功");
}
// 3) 配置IO扩展板模式(端口方向等)
err = io_expansion_.IoexpandSetMode();
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "IO扩展板模式配置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "IO扩展板模式配置成功");
}
// 4) 初始化LCD复位引脚:设置为高电平
err = io_expansion_.SetLcdResetMode(LCD_RST_HIGH, LCD_RST_PORT, LCD_RST_PIN);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "LCD复位引脚高电平设置失败, 错误码: 0x%x", err);
return;
}
else
{
ESP_LOGI(TAG_BOARD, "LCD复位引脚高电平设置成功");
}
// 5) 初始化LED_EN电源为关闭状态 (PMOS高电平关)
// 只有在需要亮灯时,由ActuatorActor主动开启,避免平静状态下常亮
err = io_expansion_.SetLedEnMode(false);
if (err != ESP_OK)
{
ESP_LOGE(TAG_BOARD, "初始化LED_EN电源(关闭)失败, 错误码: 0x%x", err);
}
else
{
ESP_LOGI(TAG_BOARD, "初始化LED_EN电源(关闭)成功");
}
}
/**
* @brief 执行系统关机
* 内部拉高 SHUTDOWN_CTRL_PIN 引脚切断电源
*/
void BoardSweekar::SystemShutdown(void)
{
#ifdef SHUTDOWN_CTRL_PIN
ESP_LOGI(TAG, "Executing System Shutdown... Pulling SHUTDOWN_CTRL_PIN (%d) HIGH", SHUTDOWN_CTRL_PIN);
// 用户指定:拉高可以完成关机动作
gpio_set_level(SHUTDOWN_CTRL_PIN, 1);
// 给一点时间让电源完全断开
vTaskDelay(pdMS_TO_TICKS(1000));
#else
ESP_LOGW(TAG, "SHUTDOWN_CTRL_PIN is not defined. Cannot execute hardware shutdown.");
#endif
}
/*========================== 毫米波雷达相关驱动 ==========================*/
/**
* @brief 毫米波雷达串口初始化函数
*
* 配置毫米波雷达所使用的串口参数(波特率、数据位、校验位、停止位等),设置串口引脚,
* 并安装串口驱动。采用较大的RX/TX FIFO深度以处理突发数据,禁用驱动自带事件队列以避免
* 与自定义中断冲突。
*/
void BoardSweekar::MillimeterWaveRadarUartInit(void)
{
uart_config_t uart_config = {
.baud_rate = RADAR_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,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(RADAR_UART_NUM, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(RADAR_UART_NUM, UART_TX_PIN, UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动(禁用驱动自带事件队列,避免与自定义中断冲突)
// 增加 RX/TX FIFO 深度以处理突发数据
ESP_ERROR_CHECK(uart_driver_install(RADAR_UART_NUM, RADAR_BUF_SIZE * 4, RADAR_BUF_SIZE * 4,
0, NULL, 0));
}
/**
* @brief 毫米波雷达中断引脚初始化函数
*
* 配置毫米波雷达中断引脚为输入模式,禁用上拉电阻,使能下拉电阻,初始禁用中断功能
* (如需使用中断需单独进行配置)。
*/
void BoardSweekar::MillimeterWaveRadarGpioInit(void)
{
// GPIO配置结构体
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << MILLIMETER_WAVE_RADAR_PIN), // 选择GPIO引脚
.mode = GPIO_MODE_INPUT, // 设置为输入模式
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE, // 使能下拉
.intr_type = GPIO_INTR_DISABLE // 禁用中断(如需中断需单独配置)
};
// 配置GPIO
gpio_config(&io_conf);
}
/*========================== I2C总线及挂载的从设备相关驱动 ==========================*/
/**
* @brief I2C总线及从机设备初始化函数
*
* 首先配置并创建I2C主机总线,然后依次添加IO扩展板和线性马达作为I2C从机设备到总线上。
* 若总线创建或设备添加失败,会输出错误信息并进行相应的资源清理(删除已创建的总线)。
* 初始化成功后,会通过ESP_LOGI输出IO扩展板和线性马达的从机地址信息。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示相应的失败原因
*/
esp_err_t BoardSweekar::I2CMasterInit(void)
{
static const char *I2C_INIT = "I2C_INIT";
// 初始化IO扩展板复位引脚
gpio_config_t reset_io_conf = {
.pin_bit_mask = (1ULL << IO_EXPAND_RSTN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // 使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&reset_io_conf);
// 执行复位时序:先拉低100ms,再拉高并等待100ms
gpio_set_level(IO_EXPAND_RSTN, 0); // 拉低复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms
gpio_set_level(IO_EXPAND_RSTN, 1); // 释放复位
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
ESP_LOGI(I2C_INIT, "IO扩展板复位完成");
// 创建统一的I2C总线(使用ESP-ADF的i2c_bus)
i2c_config_t i2c_cfg = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C0_MASTER_SDA_IO,
.scl_io_num = I2C0_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = 100000,
},
};
// 创建I2C总线(ESP-ADF的audio_board_init会复用这个总线)
bus_handle_ = i2c_bus_create(I2C0_MASTER_PORT, &i2c_cfg);
if (bus_handle_ == NULL) {
ESP_LOGE(TAG, "Failed to create I2C bus");
return ESP_FAIL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // 等待设备启动
// ESP-ADF的i2c_bus不需要单独添加设备,直接通过总线句柄和设备地址进行通信
// 验证线性马达设备是否存在
esp_err_t err = i2c_bus_probe_addr(bus_handle_, LINEAR_MOTOR_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "线性马达设备未找到(地址:0x%02X),错误码: 0x%x", LINEAR_MOTOR_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "线性马达设备检测成功(地址:0x%02X)", LINEAR_MOTOR_I2C_SLAVE_ADDR);
}
// 验证IO扩展板设备是否存在
err = i2c_bus_probe_addr(bus_handle_, I2C_IO_EXPAND_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "IO扩展板设备未找到(地址:0x%02X),错误码: 0x%x", I2C_IO_EXPAND_SLAVE_ADDR, err);
// 不返回错误,继续初始化其他设备
} else {
ESP_LOGI(I2C_INIT, "IO扩展板设备检测成功(地址:0x%02X)", I2C_IO_EXPAND_SLAVE_ADDR);
}
// 验证陀螺仪设备是否存在
err = i2c_bus_probe_addr(bus_handle_, GYROSCOPE_I2C_SLAVE_ADDR << 1);
if (err != ESP_OK) {
ESP_LOGE(I2C_INIT, "陀螺仪设备未找到(地址:0x%02X),错误码: 0x%x", GYROSCOPE_I2C_SLAVE_ADDR, err);
// 不返回错误,继续初始化
} else {
ESP_LOGI(I2C_INIT, "陀螺仪设备检测成功(地址:0x%02X)", GYROSCOPE_I2C_SLAVE_ADDR);
}
// 为IO扩展板设置总线句柄和设备地址(使用7位地址)
io_expansion_.bus_handle_ = bus_handle_;
io_expansion_.device_addr_ = I2C_IO_EXPAND_SLAVE_ADDR << 1;
// 线性马达的初始化已移至 InitializeAllHardware 中统一调用 Init 方法
// linear_motor_ 的参数传递将在那里完成,不再此处直接赋值,提高内聚性
ESP_LOGI(I2C_INIT, "I2C总线初始化完成");
return ESP_OK;
}
/*========================== VBAT to VBAT_5V相关驱动 ==========================*/
/**
* @brief 开启VBAT to VBAT_5V
*
* 创建开启升压的电源管理单元
*
*/
void BoardSweekar::VbatToVbat5vInit(void)
{
// 配置VBAT_5V引脚为输出模式
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << VBAT_5V_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 使能VBAT_5V
gpio_set_level(VBAT_5V_PIN, 1);
}
/*========================== 底座控制串口相关驱动 ==========================*/
/**
* @brief 底座控制命令串口初始化函数
*/
// 根据配置选择通信方式的HAL头文件
#ifdef CONFIG_BASE_BOARD_COMM_I2C
#include "base_board_hal_i2c.h"
#else
#include "base_board_hal_uart.h"
#endif
void BoardSweekar::BaseBoardControlInit(void)
{
#ifdef CONFIG_BASE_BOARD_COMM_I2C
// I2C Mode
if (bus_handle_ == nullptr) {
ESP_LOGE(TAG, "I2C bus not initialized before BaseBoardControlInit");
return;
}
// 创建 I2C HAL 实例
BaseBoardHalI2c *hal = new BaseBoardHalI2c(bus_handle_);
base_board_control_.Init(hal);
ESP_LOGI(TAG, "BaseBoardControl I2C initialized");
#else
// UART Mode
uart_config_t uart_config = {
.baud_rate = BASE_BOARD_CTL_UART_BAUDRATE,
.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,
.flags = {
.allow_pd = 0,
.backup_before_sleep = 0,
},
};
// 1. 设置串口参数
ESP_ERROR_CHECK(uart_param_config(BASE_BOARD_CTL_UART_PORT, &uart_config));
// 2. 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(BASE_BOARD_CTL_UART_PORT, BASE_BOARD_CTL_UART_TX_PIN, BASE_BOARD_CTL_UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 3. 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(BASE_BOARD_CTL_UART_PORT,
BASE_BOARD_CTL_UART_BUF_SIZE,
BASE_BOARD_CTL_UART_BUF_SIZE,
0, NULL, 0));
BaseBoardHalUart *hal = new BaseBoardHalUart(BASE_BOARD_CTL_UART_PORT);
base_board_control_.Init(hal);
ESP_LOGI(TAG, "BaseBoardControl UART initialized");
#endif
}
/*========================== SD卡相关驱动 ==========================*/
/**
* @brief SD卡初始化函数
*
* 初始化SD卡外设,使用1线模式进行通信。该函数负责初始化外设管理器
* 并配置SD卡,为后续的文件系统操作做准备。
*
* @return esp_err_t 初始化结果,ESP_OK表示成功,其他错误码表示失败原因
*/
esp_err_t BoardSweekar::InitializeSdcard(void)
{
static const char *SDCARD_TAG = "SDCARD_INIT";
esp_err_t ret;
ESP_LOGI(SDCARD_TAG, "Initializing SD card");
if (is_sdcard_initialized_) {
ESP_LOGW(SDCARD_TAG, "SD card already initialized");
return ESP_OK;
}
// 初始化外设管理
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
periph_cfg.extern_stack = true; // 将任务栈分配到 PSRAM 以节省 Internal SRAM
periph_set_ = esp_periph_set_init(&periph_cfg);
if (!periph_set_) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize peripheral set");
return ESP_FAIL;
}
// 初始化SD卡外设
ret = audio_board_sdcard_init(periph_set_, SD_MODE_1_LINE);
if (ret != ESP_OK) {
ESP_LOGE(SDCARD_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
return ret;
}
is_sdcard_initialized_ = true;
ESP_LOGI(SDCARD_TAG, "SD card initialized successfully");
return ESP_OK;
}
/**
* @brief 统一的硬件初始化接口实现
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备可能依赖)
* 2. IO扩展板(可能影响其他设备)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. 其他外设
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardware(void)
{
static const char *HW_INIT_TAG = "HW_INIT";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting unified hardware initialization...");
// 1. 初始化I2C总线(最基础)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "SD card initialized");
}
// 4. 初始化LCD IO引脚
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "LCD IO pins initialized");
// 5. 初始化底座控制
if (bus_handle_ != nullptr) {
BaseBoardHalI2c *hal_i2c = new BaseBoardHalI2c(bus_handle_);
base_board_control_.Init(hal_i2c);
ESP_LOGI(HW_INIT_TAG, "Base board control initialized with I2C handle");
} else {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize base board control: I2C bus is null");
}
// 6. AUD_CTRL 为 codec 控制脚,板级初始化后保持拉高,不参与 PA 开关。
#ifdef AUD_CTRL_PIN
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << AUD_CTRL_PIN);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
gpio_set_level(AUD_CTRL_PIN, 1);
ESP_LOGI(HW_INIT_TAG, "AUD_CTRL pin initialized to HIGH");
#endif
// 7. 可选外设(根据具体应用需求)
// BaseBoardControlUartInit(); // 底座控制串口(demo5需要)
// ESP_LOGI(HW_INIT_TAG, "Base board control UART initialized");
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
StepperMotorInit();
ESP_LOGI(HW_INIT_TAG, "Unified hardware initialization completed successfully");
return ESP_OK;
}
/**
* @brief 进入低功耗模式
*
* 根据指定的功耗模式,执行相应的功耗优化策略:
* - IDLE模式:关闭非必要外设,保持快速响应
* - LIGHT_SLEEP模式:进入轻度睡眠,CPU暂停,外设断电
* - DEEP_SLEEP模式:深度睡眠,仅RTC运行,功耗最低
*/
esp_err_t BoardSweekar::EnterLowPowerMode(power_mode_t mode) {
static const char *POWER_TAG = "POWER_MGMT";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "Entering power mode: %d", mode);
if (mode == current_power_mode_) {
ESP_LOGW(POWER_TAG, "Already in power mode %d", mode);
return ESP_OK;
}
// 保存当前模式,以便恢复
power_mode_t previous_mode = current_power_mode_;
switch (mode) {
case POWER_MODE_NORMAL:
// 正常模式,不需要特殊处理
break;
case POWER_MODE_IDLE:
// 空闲模式:关闭非必要外设
ESP_LOGI(POWER_TAG, "Idle mode: disabling non-essential peripherals");
// 关闭SD卡(如果正在使用,需要先卸载)
if (is_sdcard_initialized_) {
// 注意:这里只是示例,实际应用中需要确保没有文件操作
ESP_LOGW(POWER_TAG, "SD card still active in idle mode");
}
// 关闭显示背光
// 关闭非必要GPIO的上拉/下拉
// 降低I2C时钟频率
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠
ESP_LOGI(POWER_TAG, "Light sleep mode: preparing for sleep");
// 设置唤醒源(需要根据具体需求配置)
// esp_sleep_enable_timer_wakeup(1000000); // 1秒后唤醒
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_XX, 0); // 外部中断唤醒
// 进入轻度睡眠
// ret = esp_light_sleep_start();
if (ret != ESP_OK) {
ESP_LOGE(POWER_TAG, "Failed to enter light sleep: %s", esp_err_to_name(ret));
current_power_mode_ = previous_mode;
return ret;
}
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠
ESP_LOGI(POWER_TAG, "Deep sleep mode: system will restart after wake-up");
// 保存必要的RTC数据
// rtc_gpio_isolate(GPIO_NUM_XX); // 隔离GPIO
// 设置深度睡眠唤醒源
// esp_sleep_enable_timer_wakeup(60000000); // 60秒后唤醒
// 进入深度睡眠
// esp_deep_sleep_start();
// 注意:深度睡眠后系统会重启,不会返回
break;
default:
ESP_LOGE(POWER_TAG, "Invalid power mode: %d", mode);
return ESP_ERR_INVALID_ARG;
}
current_power_mode_ = mode;
ESP_LOGI(POWER_TAG, "Successfully entered power mode: %d", mode);
return ret;
}
/**
* @brief 退出低功耗模式
*
* 从低功耗模式恢复到正常运行模式,重新初始化必要的硬件资源。
*/
esp_err_t BoardSweekar::ExitLowPowerMode(void) {
static const char *POWER_TAG = "POWER_MGMT";
ESP_LOGI(POWER_TAG, "Exiting low power mode");
if (current_power_mode_ == POWER_MODE_NORMAL) {
ESP_LOGW(POWER_TAG, "Already in normal mode");
return ESP_OK;
}
// 根据之前的功耗模式执行相应的恢复操作
switch (current_power_mode_) {
case POWER_MODE_IDLE:
// 恢复空闲模式
ESP_LOGI(POWER_TAG, "Restoring from idle mode");
// 重新初始化必要的外设
// 恢复I2C时钟频率
// 重新开启显示背光
break;
case POWER_MODE_LIGHT_SLEEP:
// 轻度睡眠唤醒后会自动继续执行
ESP_LOGI(POWER_TAG, "Woken up from light sleep");
// 重新配置时钟(如果需要)
// 重新初始化外设
break;
case POWER_MODE_DEEP_SLEEP:
// 深度睡眠后系统会重启,不会执行到这里
ESP_LOGW(POWER_TAG, "Deep sleep recovery should not reach here");
break;
default:
break;
}
// 恢复到正常模式
current_power_mode_ = POWER_MODE_NORMAL;
ESP_LOGI(POWER_TAG, "Successfully exited low power mode");
return ESP_OK;
}
/**
* @brief 设置硬件模块电源状态
*
* 提供精细化的电源管理,可以单独控制各个硬件模块的电源状态。
*/
esp_err_t BoardSweekar::SetModulePower(hardware_module_t module, bool enable) {
static const char *POWER_TAG = "POWER_MODULE";
esp_err_t ret = ESP_OK;
ESP_LOGI(POWER_TAG, "%s module %d", enable ? "Enabling" : "Disabling", module);
// 更新电源状态位图
if (enable) {
power_state_flags_ |= (1 << module);
} else {
power_state_flags_ &= ~(1 << module);
}
switch (module) {
case HW_MODULE_I2C:
if (!enable && bus_handle_) {
// 注意:I2C总线通常不建议完全关闭,可能影响多个设备
ESP_LOGW(POWER_TAG, "I2C bus power control not recommended");
}
break;
case HW_MODULE_SDCARD:
if (enable && !is_sdcard_initialized_) {
ret = InitializeSdcard();
} else if (!enable && is_sdcard_initialized_) {
// 卸载SD卡文件系统
// 注意:需要确保没有打开的文件
ESP_LOGW(POWER_TAG, "SD card power down requires file system cleanup");
}
break;
case HW_MODULE_LCD:
// LCD背光控制
// LCD电源引脚控制(如果有)
break;
case HW_MODULE_AUDIO:
// 音频编解码器电源控制
// 需要与AudioPlayer协调
break;
case HW_MODULE_MOTOR:
// 步进电机驱动器电源控制
// 电机睡眠模式
break;
case HW_MODULE_GYROSCOPE:
// 陀螺仪电源控制
// 设置陀螺仪为低功耗模式
break;
case HW_MODULE_TOUCH:
// 触摸传感器电源控制
// 设置触摸传感器为睡眠模式
break;
case HW_MODULE_RADAR:
// 毫米波雷达电源控制
// 设置雷达为低功耗模式
break;
case HW_MODULE_IO_EXPAND:
// IO扩展板电源控制
// 设置扩展板为低功耗模式
break;
default:
ESP_LOGE(POWER_TAG, "Unknown hardware module: %d", module);
return ESP_ERR_INVALID_ARG;
}
if (ret == ESP_OK) {
ESP_LOGI(POWER_TAG, "Module %d power %s successfully", module, enable ? "enabled" : "disabled");
} else {
ESP_LOGE(POWER_TAG, "Failed to %s module %d: %s", enable ? "enable" : "disable", module, esp_err_to_name(ret));
}
return ret;
}
@@ -0,0 +1,181 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include "config.h"
#include "io_expansion_board.h"
#include "stepper_motor.h"
#include "stepper_driver.h"
#include "i2c_bus.h"
#include "gyroscope.h"
#include "touch_sensor.h"
#include "base_board_control.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
/**
* @brief 功耗模式枚举
*/
typedef enum {
POWER_MODE_NORMAL, // 正常运行模式,所有硬件可用
POWER_MODE_IDLE, // 空闲模式,关闭非必要外设
POWER_MODE_LIGHT_SLEEP, // 轻度睡眠,快速唤醒
POWER_MODE_DEEP_SLEEP, // 深度睡眠,仅RTC运行
} power_mode_t;
/**
* @brief 硬件模块枚举,用于精细化电源管理
*/
typedef enum {
HW_MODULE_I2C, // I2C总线
HW_MODULE_SDCARD, // SD卡
HW_MODULE_LCD, // LCD显示
HW_MODULE_AUDIO, // 音频编解码器
HW_MODULE_MOTOR, // 步进电机
HW_MODULE_GYROSCOPE, // 陀螺仪
HW_MODULE_TOUCH, // 触摸传感器
HW_MODULE_RADAR, // 毫米波雷达
HW_MODULE_IO_EXPAND, // IO扩展板
} hardware_module_t;
/**
* @brief 硬件板级管理器
*
* 采用高内聚低耦合设计原则,统一管理所有硬件资源。
* 提供统一的硬件初始化接口和资源访问,避免各模块重复初始化。
*/
class BoardSweekar
{
private:
i2c_bus_handle_t bus_handle_ = nullptr; // 使用ESP-ADF的I2C总线句柄
esp_periph_set_handle_t periph_set_ = nullptr; // 外设管理句柄
bool is_sdcard_initialized_ = false; // SD卡初始化状态
power_mode_t current_power_mode_ = POWER_MODE_NORMAL; // 当前功耗模式
uint32_t power_state_flags_ = 0xFFFFFFFF; // 各模块电源状态位图(1为开启)
public:
BoardSweekar();
~BoardSweekar();
static BoardSweekar* GetInstance();
// --- 语义化的高层 API:音频与功放控制 ---
// 开启或关闭外放功放 (PA),供上层 AudioActor/AudioPlayer 调用,屏蔽底层引脚实现
void SetAudioAmplifier(bool enable);
IOExpansionBoard io_expansion_; // IO扩展板对象
Gyroscope gyroscope_; // 陀螺仪对象
TouchSensor touch_sensor_; // 触摸传感器对象
BaseBoardControl base_board_control_; // 底座控制对象
StepperDriver* stepper_driver_ = nullptr; // 步进电机驱动对象
StepperDriver* GetStepperDriver() const { return stepper_driver_; }
// 初始化步进电机
void StepperMotorInit(void);
// 初始化LCD IO引脚(消除与步进电机的耦合)
void LcdIoInit(void);
// 初始化串口
void MillimeterWaveRadarUartInit(void);
//初始化毫米波雷达的中断引脚
void MillimeterWaveRadarGpioInit(void);
/**
* @brief 执行系统关机
* 内部拉高/拉低 SHUTDOWN_CTRL_PIN 引脚切断电源
*/
void SystemShutdown(void);
// 初始化I2C总线
esp_err_t I2CMasterInit(void);
// 初始化VBAT toVBAT_5V引脚
void VbatToVbat5vInit(void);
// 初始化底座控制串口
void BaseBoardControlInit(void);
// 初始化SD卡(推荐使用统一的硬件初始化)
esp_err_t InitializeSdcard(void);
// 获取SD卡初始化状态
bool IsSdcardInitialized() const { return is_sdcard_initialized_; }
// 获取外设管理句柄(供需要访问SD卡的模块使用)
esp_periph_set_handle_t GetPeriphSet() const { return periph_set_; }
// 获取I2C总线句柄
i2c_bus_handle_t GetBusHandle() const { return bus_handle_; }
/**
* @brief 统一的硬件初始化接口
*
* 按照依赖关系顺序初始化所有硬件资源,确保各模块可以安全使用。
* 推荐在主程序启动时调用此函数,而不是分别调用各个初始化函数。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardware(void);
/**
* @brief 完整的统一硬件初始化接口(专为复杂应用优化)
*
* 包含所有必要外设的完整初始化,适用于demo5等复杂应用场景。
* 这个函数会初始化:I2C总线、IO扩展板、SD卡、LCD IO、底座控制等。
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeAllHardwareComplete(void);
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置,优化启动流程。
*
* @param app_type 应用类型(demo5、音频播放器、显示应用等)
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t InitializeHardwareForApplication(int app_type);
/**
* @brief 功耗管理接口 - 进入低功耗模式
*
* 根据指定的功耗模式,关闭非必要硬件、设置GPIO为低功耗状态、
* 调整时钟频率等。支持多种功耗模式以适应不同应用场景。
*
* @param mode 功耗模式
* @return esp_err_t 操作结果
*/
esp_err_t EnterLowPowerMode(power_mode_t mode);
/**
* @brief 功耗管理接口 - 退出低功耗模式
*
* 从低功耗模式恢复,重新初始化必要的硬件资源。
*
* @return esp_err_t 操作结果
*/
esp_err_t ExitLowPowerMode(void);
/**
* @brief 获取当前功耗模式
*
* @return power_mode_t 当前功耗模式
*/
power_mode_t GetPowerMode(void) const { return current_power_mode_; }
/**
* @brief 设置硬件模块电源状态
*
* 单独控制某个硬件模块的电源状态,用于精细化的功耗管理。
*
* @param module 硬件模块
* @param enable true为开启,false为关闭
* @return esp_err_t 操作结果
*/
esp_err_t SetModulePower(hardware_module_t module, bool enable);
};
#endif
@@ -0,0 +1,200 @@
/**
* @file board_sweekar_complete_init.cc
* @brief 完整的统一硬件初始化实现
*
* 这个文件提供了完整的统一硬件初始化函数,包含所有必要的硬件初始化步骤。
* 专为demo5等复杂应用场景设计。
*/
/**
* @brief 完整的统一硬件初始化接口(专为demo5优化)
*
* 按照依赖关系顺序初始化所有硬件资源:
* 1. I2C总线(最基础,其他设备依赖)
* 2. IO扩展板(控制其他外设)
* 3. SD卡(文件系统,供显示和音频使用)
* 4. LCD IO引脚(显示相关)
* 5. 底座控制串口(通信)
* 6. 底座控制模块(业务逻辑)
* 7. 其他外设(根据应用需求)
*
* @return esp_err_t 初始化结果,ESP_OK表示全部成功
*/
esp_err_t BoardSweekar::InitializeAllHardwareComplete(void)
{
static const char *HW_INIT_TAG = "HW_INIT_COMPLETE";
esp_err_t ret;
ESP_LOGI(HW_INIT_TAG, "Starting complete hardware initialization...");
// 1. 初始化I2C总线(最基础,其他设备依赖)
ret = I2CMasterInit();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize I2C master: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(HW_INIT_TAG, "✓ I2C master initialized");
// 2. 创建I2C读任务(IO扩展板需要)
io_expansion_.CreateI2cReadTask();
ESP_LOGI(HW_INIT_TAG, "✓ I2C read task created");
// 3. 初始化SD卡(文件系统,供显示和音频使用)
ret = InitializeSdcard();
if (ret != ESP_OK) {
ESP_LOGE(HW_INIT_TAG, "Failed to initialize SD card: %s", esp_err_to_name(ret));
// SD卡失败不是致命错误,继续初始化其他硬件
ESP_LOGW(HW_INIT_TAG, "⚠ SD card initialization failed, but continuing with other hardware");
} else {
ESP_LOGI(HW_INIT_TAG, "✓ SD card initialized");
}
// 4. 初始化LCD IO引脚(显示相关)
LcdIoInit();
ESP_LOGI(HW_INIT_TAG, "✓ LCD IO pins initialized");
// 5. 初始化底座控制串口(demo5需要)
BaseBoardControlUartInit();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control UART initialized");
// 6. 初始化底座控制模块
base_board_control_.Init();
ESP_LOGI(HW_INIT_TAG, "✓ Base board control initialized");
// 7. 设置IO扩展板的PA控制模式(demo5需要)
io_expansion_.SetPaCtrlMode(true);
ESP_LOGI(HW_INIT_TAG, "✓ IO expansion PA control mode set");
// 8. 初始化 AUD_CTRL 引脚(如果定义了的话)
#ifdef AUD_CTRL_PIN
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << AUD_CTRL_PIN);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
// 拉高开机
gpio_set_level(AUD_CTRL_PIN, 1);
ESP_LOGI(HW_INIT_TAG, "✓ AUD_CTRL pin initialized to HIGH");
#endif
// 9. 可选外设(根据具体应用需求启用)
// ESP_LOGI(HW_INIT_TAG, "Initializing optional peripherals...");
//
// // 毫米波雷达(如需要)
// MillimeterWaveRadarUartInit();
// MillimeterWaveRadarGpioInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Millimeter wave radar initialized");
//
// // VBAT升压(如需要)
// VbatToVbat5vInit();
// ESP_LOGI(HW_INIT_TAG, "✓ VBAT to 5V booster initialized");
//
// // 步进电机(如需要)
// StepperMotorInit();
// ESP_LOGI(HW_INIT_TAG, "✓ Stepper motor initialized");
ESP_LOGI(HW_INIT_TAG, "=== Complete hardware initialization finished successfully ===");
return ESP_OK;
}
/**
* @brief 针对特定应用的硬件初始化配置
*
* 为不同类型的应用提供专门的初始化配置
*/
esp_err_t BoardSweekar::InitializeHardwareForApplication(application_type_t app_type) {
static const char *APP_INIT_TAG = "APP_HW_INIT";
ESP_LOGI(APP_INIT_TAG, "Initializing hardware for application type: %d", app_type);
// 基础硬件初始化(所有应用都需要)
esp_err_t ret = InitializeAllHardwareComplete();
if (ret != ESP_OK) {
ESP_LOGE(APP_INIT_TAG, "Basic hardware initialization failed");
return ret;
}
switch (app_type) {
case APP_TYPE_DEMO5: // 宠物游戏demo
ESP_LOGI(APP_INIT_TAG, "Configuring for Demo5 (Pet Game)...");
// Demo5需要的额外初始化
// 触摸传感器、陀螺仪、RGB LED等会在应用层初始化
ESP_LOGI(APP_INIT_TAG, "✓ Demo5 hardware configuration complete");
break;
case APP_TYPE_AUDIO_PLAYER: // 音频播放器
ESP_LOGI(APP_INIT_TAG, "Configuring for Audio Player...");
// 音频应用需要确保音频编解码器正确初始化
// 这部分通常在AudioPlayer类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Audio player hardware configuration complete");
break;
case APP_TYPE_DISPLAY_ONLY: // 纯显示应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Display Only...");
// 显示应用需要确保LCD相关硬件正确配置
// 这部分通常在Display类中处理
ESP_LOGI(APP_INIT_TAG, "✓ Display-only hardware configuration complete");
break;
case APP_TYPE_MINIMAL: // 最小化应用
ESP_LOGI(APP_INIT_TAG, "Configuring for Minimal Application...");
// 最小化配置,只保留最基本的功能
// 关闭非必要外设以节省功耗
ESP_LOGI(APP_INIT_TAG, "✓ Minimal hardware configuration complete");
break;
default:
ESP_LOGW(APP_INIT_TAG, "Unknown application type, using default configuration");
break;
}
return ESP_OK;
}
/**
* @brief 硬件初始化状态报告
*
* 提供详细的硬件初始化状态报告,便于调试和问题排查
*/
void BoardSweekar::ReportHardwareStatus(void) {
static const char *STATUS_TAG = "HW_STATUS";
ESP_LOGI(STATUS_TAG, "=== Hardware Initialization Status Report ===");
// I2C总线状态
ESP_LOGI(STATUS_TAG, "I2C Bus: %s", (bus_handle_ != nullptr) ? "✓ Initialized" : "✗ Not initialized");
// SD卡状态
ESP_LOGI(STATUS_TAG, "SD Card: %s", (is_sdcard_initialized_) ? "✓ Initialized" : "✗ Not initialized");
if (is_sdcard_initialized_) {
ESP_LOGI(STATUS_TAG, " Peripheral Set: %s", (periph_set_ != nullptr) ? "✓ Available" : "✗ Not available");
}
// IO扩展板状态
ESP_LOGI(STATUS_TAG, "IO Expansion Board: %s", (io_expansion_.bus_handle_ != nullptr) ? "✓ Configured" : "✗ Not configured");
// 底座控制状态
ESP_LOGI(STATUS_TAG, "Base Board Control: %s", (base_board_control_.GetUartPort() != 0) ? "✓ Initialized" : "✗ Not initialized");
// 功耗管理状态
ESP_LOGI(STATUS_TAG, "Power Management: %s", (current_power_mode_ == POWER_MODE_NORMAL) ? "✓ Normal mode" : "⚠ Low power mode");
ESP_LOGI(STATUS_TAG, " Current Mode: %d", current_power_mode_);
ESP_LOGI(STATUS_TAG, " Module Power States: 0x%08X", power_state_flags_);
// 内存状态
ESP_LOGI(STATUS_TAG, "Memory Status:");
ESP_LOGI(STATUS_TAG, " Internal RAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
ESP_LOGI(STATUS_TAG, " PSRAM Free: %d bytes", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
ESP_LOGI(STATUS_TAG, "=== End of Hardware Status Report ===");
}
@@ -0,0 +1,136 @@
/* 定义引脚分配 */
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
/**
* @file config.h
* @brief 定义了引脚的分配。
* @author zouyuhua
* @date 2025年8月7日
*
*/
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_rom_sys.h" // 用于 esp_rom_delay_us
#include "esp_timer.h"
#include "esp_mac.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gptimer.h"
#include "esp_intr_alloc.h"
#include "driver/uart.h"
#include "esp_err.h"
#include "freertos/queue.h"
#include "soc/uart_reg.h"
#include "linear_motor.h"
#include "gyroscope.h"
#define TAG_BOARD "SWEEKAR_ESP32"
/*I2C总线引脚定义*/
#define I2C0_MASTER_PORT I2C_NUM_0
#define I2C0_MASTER_SDA_IO GPIO_NUM_2
#define I2C0_MASTER_SCL_IO GPIO_NUM_3
/* 步进电机配置 */
#define STEPPER_STEP_IO GPIO_NUM_42 // 步进电机脉冲输入引脚
#define STEPPER_TIMER_RES_HZ 1000000 // 步进电机定时器分辨率 1 MHz(1 us 步进)
/* 定义屏幕驱动控制引脚 */
#define ST7789_PIN_CLK GPIO_NUM_48 // SPI时钟引脚
#define ST7789_PIN_DC GPIO_NUM_38 // 数据/命令选择引脚RSEL
#define ST7789_PIN_MOSI GPIO_NUM_47 // SPI数据输出引脚
#define ST7789_PIN_CS GPIO_NUM_46 // SPI片选引脚
#define ST7789_PIN_BL GPIO_NUM_45 // 背光引脚
#define ST7789_PIN_RST GPIO_NUM_NC // 复位引脚
#define LCD_BL_ACTIVE_LEVEL 0 // 背光有效电平:1=高电平有效(NMOS),0=低电平有效(PMOS)
#define LCD_WIDTH 172 // 屏幕宽度(水平方向像素数)
#define LCD_HEIGHT 320 // 屏幕高度(垂直方向像素数)
/* 毫米波雷达配置参数 */
/* 定义毫米波雷达的中断引脚 */
#define MILLIMETER_WAVE_RADAR_PIN GPIO_NUM_35
// UART 配置参数
#define RADAR_UART_NUM UART_NUM_1 // 使用 UART1
#define UART_TX_PIN GPIO_NUM_40 // GPIO40 作为 TX(根据实际硬件连接调整)
#define UART_RX_PIN GPIO_NUM_39 // GPIO39 作为 RX
#define RADAR_BAUD_RATE 9600 // 雷达波特率
#define RADAR_BUF_SIZE 100 // 雷达接收缓冲区大小
/* IO扩展硬件配置常量 */
#define I2C_MASTER_FREQ_HZ 100000 // I2C通信频率(100kHz)(可更改至400000)
#define I2C_IO_EXPAND_SLAVE_ADDR 0x58 // AW9523BTQR器件地址,v1_1_13的addr1和addr0是0
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // 中断检测引脚
#define ESP_INTR_FLAG_DEFAULT 0 // 中断标志默认值
#define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/* 定义线性马达的I2C从机配置常量 */
#define LINEAR_MOTOR_I2C_SLAVE_ADDR 0x5A // 线性马达的I2C从机地址
/* 定义陀螺仪的I2C从机配置常量 */
#define GYROSCOPE_I2C_SLAVE_ADDR 0x6A // 陀螺仪的I2C从机地址
#define GYROSCOPE_INPUT_PIN_SEL GPIO_NUM_21 // 陀螺仪中断引脚
/* AW触摸芯片配置 */
#define AW_TOUCH_IRQ_PIN GPIO_NUM_11 // AW触摸芯片中断引脚
#define NFC_RST_PIN GPIO_NUM_12 // NFC复位引脚
#define AUD_CTRL_PIN GPIO_NUM_13 // AUD_CTRL引脚
/* 原生触摸引脚配置 (v1.1.18已改为上述功能,保留定义需谨慎) */
// 触摸按键数量配置
#define TOUCH_BUTTON_NUM 3
// 触摸通道定义 - 注意:v1.1.18如果不使用ESP32原生触摸,这些定义可能不再适用
// 但为了兼容编译,可以保留或注释
#define TOUCH_PAD_CHANNEL_1 TOUCH_PAD_NUM11 // GPIO11 (Now AW_IRQ)
#define TOUCH_PAD_CHANNEL_2 TOUCH_PAD_NUM12 // GPIO12 (Now NFC_RST)
#define TOUCH_PAD_CHANNEL_3 TOUCH_PAD_NUM13 // GPIO13 (Now AUD_CTRL)
/* ES8311音频编解码器配置 (ESP-ADF兼容) */
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
/* I2S GPIO定义 */
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_15
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
/* PA引脚定义 */
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
/* I2C配置 */
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_2
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_3
#define AUDIO_CODEC_ES8311_ADDR 0x18
#define AUDIO_CODEC_ES7210_ADDR 0x40
/* WS2812 RGB LED配置 */
#define WS2812_PIN GPIO_NUM_41 // WS2812数据引脚
#define WS2812_NUM_LEDS 2 // WS2812 LED数量
/* 创建VBAT to VBAT_5V */
#define VBAT_5V_PIN GPIO_NUM_NC // VBAT_5V引脚
/* 定义按键的中断引脚 */
#define BUTTON_PIN GPIO_NUM_0 // 按键中断引脚
#define RIGHT_BUTTON_PIN GPIO_NUM_8 // 按键中断引脚
/* 底座控制命令串口配置 */
#define BASE_BOARD_CTL_UART_PORT UART_NUM_0
#define BASE_BOARD_CTL_UART_TX_PIN GPIO_NUM_43
#define BASE_BOARD_CTL_UART_RX_PIN GPIO_NUM_44
#define BASE_BOARD_CTL_UART_BAUDRATE 115200
#define BASE_BOARD_CTL_UART_BUF_SIZE 256
/* 背光控制引脚配置 */
#define BL_PIN GPIO_NUM_45
#endif // _BOARD_CONFIG_H_
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,432 @@
#ifndef IO_EXPANSION_BOARD_H
#define IO_EXPANSION_BOARD_H
#include <cstdint>
#include <esp_log.h>
#include <esp_check.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include <functional>
#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 "portmacro.h"
// 前向声明,避免循环引用
class MillimeterWaveRadar;
class PhotoInterrupter;
class AdcBatteryMonitor;
// 回调函数类型定义
typedef std::function<void()> RadarCallback;
typedef std::function<void()> PhotoInterrupterStartCallback;
typedef std::function<void()> PhotoInterrupterEndCallback;
typedef std::function<void()> BatteryChargingCallback;
typedef std::function<void()> BatteryFullCallback;
typedef std::function<void()> BatteryNotChargingCallback;
/* 寄存器地址 */
#define INPUT_PORT0 0x00 // 输入端口0寄存器
#define INPUT_PORT1 0x01 // 输入端口1寄存器 0为低电平(仅支持读取)
#define OUTPUT_PORT0 0x02 // 输出端口0寄存器
#define OUTPUT_PORT1 0x03 // 输出端口1寄存器 0为低电平
#define CONFIG_PORT0 0x04 // 配置端口0寄存器
#define CONFIG_PORT1 0x05 // 配置端口1寄存器 0为输出模式
#define INT_PORT0 0x06 // 中断端口0寄存器
#define INT_PORT1 0x07 // 中断端口1寄存器 0为使能中断(根据手册默认是使能的)
#define CTL 0x11 // 全局控制寄存器
#define ID 0x23 // 芯片ID寄存器(0x23)
/***********************************IO扩展板电机引脚配置参数*************************************/
/* 设置步进电机方向 */
#define STEPPER_MOTOR_LEFT false //步进电机的方向为左转
#define STEPPER_MOTOR_RIGHT true //步进电机的方向为右转
#define STEPPER_MOTOR_DIR_PORT OUTPUT_PORT0 //步进电机方向引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_DIR_PIN 4 //步进电机方向引脚对应的IO扩展板引脚P0_4
#define STEPPER_MOTOR_DIR_CONFIG CONFIG_PORT0 //步进电机方向引脚配置在P0_4
/* 设置步进电机细分模式 */
#define STEPPER_MOTOR_M0_PIN 5 //步进电机细分模式选择引脚对应IO扩展板引脚P1_5
#define STEPPER_MOTOR_M0_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M0_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_5
#define STEPPER_MOTOR_M1_PIN 4 //步进电机细分模式选择引脚对应IO扩展板引脚P1_4
#define STEPPER_MOTOR_M1_MADE_PORT OUTPUT_PORT1 //步进电机模式选择引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define STEPPER_MOTOR_M1_CONFIG CONFIG_PORT1 //步进电机细分模式选择引脚配置在P1_4
/* 设置步进电机睡眠引脚 */
#define STEPPER_MOTOR_SLEEP false //步进电机进入睡眠模式
#define STEPPER_MOTOR_WORK true //步进电机进入工作模式
#define STEPPER_MOTOR_SLEEP_PIN 6 //步进电机睡眠引脚对应IO扩展板引脚P1_6
#define STEPPER_MOTOR_SLEEP_CONFIG CONFIG_PORT1 //步进电机睡眠引脚配置在P1_6
#define STEPPER_MOTOR_SLEEP_PORT OUTPUT_PORT1 //步进电机睡眠引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
/* 设置电机的使能引脚 */
#define STEPPER_MOTOR_ENABLE_PIN 7 //电机使能引脚对应IO扩展板引脚P0_7
#define STEPPER_MOTOR_ENABLE_PORT OUTPUT_PORT0 //电机使能引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
#define STEPPER_MOTOR_ENABLE_CONFIG CONFIG_PORT0 //电机使能引脚配置在P0_7
#define STEPPER_MOTOR_ENABLE_ENABLE true //电机使能引脚状态为高电平
#define STEPPER_MOTOR_ENABLE_DISABLE false //电机使能引脚状态为低电平
/* 设置毫米波雷达对应IO扩展板的中断引脚 */
#define RADAR_INTERRUPT_PIN 7 //毫米波雷达中断引脚对应IO扩展板引脚P1_7
#define RADAR_INTERRUPT_CONFIG CONFIG_PORT1 //毫米波雷达中断引脚挂载在IO扩展板的输入端口
#define RADAR_INTERRUPT_INPUT INPUT_PORT1 //毫米波雷达中断引脚输入状态检测在INPUT_PORT1
#define RADAR_INTERRUPT_ENABLE true //毫米波雷达中断引脚状态为输入状态
#define RADAR_INTERRUPT_DISABLE false //毫米波雷达中断引脚状态为输出状态
/* 任务配置常量 */
#define WRITE_BUF_SIZE 2 // I2C单次写入字节数
#define READ_BUF_SIZE 2 // I2C单次读取字节数
#define I2C_TASK_PRIORITY 2 // 任务优先级(中等)
#define I2C_TASK_STACK 4096 // 任务栈大小(4KB)
// 光学传感器引脚定义
#define START_PHOTO_INTERRUPTER_PIN 2 // 光学传感器引脚(P0_2)
#define START_PHOTO_INTERRUPTER_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define START_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define START_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define START_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
#define END_PHOTO_INTERRUPTER_PIN 3 // 光学传感器引脚(P0_3)
#define END_PHOTO_INTERRUPTER_PORT_CONFIG CONFIG_PORT0 // 光学传感器引脚挂载在IO扩展板的输入端口
#define END_PHOTO_INTERRUPTER_INPUT INPUT_PORT0 // 光学传感器引脚输入状态检测在INPUT_PORT0
#define END_PHOTO_INTERRUPTER_HIGH true // 光学传感器引脚状态为高电平
#define END_PHOTO_INTERRUPTER_LOW false // 光学传感器引脚状态为低电平
/***********************************IO扩展板LCD引脚配置参数*************************************/
// /* 设置LCD的BL引脚 */
// #define LCD_BL_PIN 6 //LCD背光引脚对应IO扩展板引脚P0_6
// #define LCD_BL_PORT OUTPUT_PORT0 //LCD背光引脚挂载在IO扩展板的输出端口OUTPUT_PORT0
// #define LCD_BL_CONFIG CONFIG_PORT0 //LCD背光引脚配置在P0_6
// #define LCD_BL_HIGH true //LCD背光引脚状态为高电平
// #define LCD_BL_LOW false //LCD背光引脚状态为低电平
/* 设置LED_EN (LCD背光/LED控制)引脚, PMOS高侧驱动 */
#define LED_EN_PIN 1 //LED_EN对应IO扩展板引脚P0_1
#define LED_EN_PORT OUTPUT_PORT0 //挂载在输出端口OUTPUT_PORT0
#define LED_EN_CONFIG CONFIG_PORT0 //配置在P0_1
#define LED_EN_OFF true //高电平关(PMOS)
#define LED_EN_ON false //低电平开(PMOS)
/* 设置LCD的RST引脚 */
#define LCD_RST_PIN 1 //LCD复位引脚对应IO扩展板引脚P1_1
#define LCD_RST_CONFIG CONFIG_PORT1 //LCD复位引脚配置在P1_1
#define LCD_RST_PORT OUTPUT_PORT1 //LCD复位引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define LCD_RST_HIGH true //LCD复位引脚状态为高电平
#define LCD_RST_LOW false //LCD复位引脚状态为低电平
/* 设置PA控制引脚 */
#define PA_CTRL_PIN 2 //PA控制引脚对应IO扩展板引脚P1_2
#define PA_CTRL_CONFIG CONFIG_PORT1 //PA控制引脚配置在P1_2
#define PA_CTRL_PORT OUTPUT_PORT1 //PA控制引脚挂载在IO扩展板的输出端口OUTPUT_PORT1
#define PA_CTRL_HIGH true //PA控制引脚状态为高电平
#define PA_CTRL_LOW false //PA控制引脚状态为低电平
// 电池充电检测引脚定义
#define BATTERY_FULL_INPUT INPUT_PORT1 // 电池充满检测引脚挂载在IO扩展板的输入端口
#define BATTERY_FULL_PIN 3 // 电池充满检测引脚对应IO扩展板引脚P1_3
#define BATTERY_FULL_CONFIG CONFIG_PORT1 // 电池充满检测引脚配置在P1_3
#define BATTERY_CHRG_INPUT INPUT_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define BATTERY_CHRG_PIN 5 // 充电检测引脚(P0_5)
#define BATTERY_CHRG_CONFIG CONFIG_PORT0 // 充电检测引脚挂载在IO扩展板的输入端口
#define TAG_GPIO_IO_EXPAND_DETECT "GPIO_IO_EXPAND_DETECT" // GPIO中断检测日志标签
#define TAG_I2C_IO_EXPAND_TASK "I2C_IO_EXPAND_TASK" // I2CIO扩展板任务日志标签
// GPIO引脚定义
#define GPIO_INPUT_PIN_SEL GPIO_NUM_10 // IO扩展板中断检测引脚
// #define IO_EXPAND_RSTN GPIO_NUM_14 // IO扩展板复位引脚
/**
* @class IOExpansionBoard
* @brief I/O扩展板驱动类,封装AW9523BTQR芯片操作
*/
class IOExpansionBoard
{
public:
/**
* @brief 构造函数
*/
IOExpansionBoard();
/**
* @brief 设置毫米波雷达指针
* @param radar 毫米波雷达对象指针
*/
void SetRadar(MillimeterWaveRadar* radar) { radar_ = radar; };
/**
* @brief 设置限位开关指针
* @param photo_interrupter 限位开关对象指针
*/
void setPhotoInterrupter(PhotoInterrupter* photo_interrupter) { photo_interrupter_ = photo_interrupter; };
/**
* @brief 设置电池监控器指针
* @param battery_monitor 电池监控器对象指针
*/
void setBatteryMonitor(AdcBatteryMonitor* battery_monitor) { battery_monitor_ = battery_monitor; };
/**
* @brief 析构函数
*/
~IOExpansionBoard();
/**
* @brief 初始化GPIO中断检测
* @param gpio_num 中断引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t InterruptGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetGpioInit(gpio_num_t gpio_num);
/**
* @brief 初始化IO扩展板复位引脚
* @param gpio_num 复位引脚编号
* @return esp_err_t 初始化结果
*/
esp_err_t ResetIoExpand();
/**
* @brief 初始化I2C主机通信
* @return esp_err_t 初始化结果
*/
// esp_err_t IoExpansionI2cInit();
/**
* @brief 创建I2C数据读取任务
*/
void CreateI2cReadTask();
/**
* @brief 启动I2C任务(整合初始化和任务创建)
* @return esp_err_t 操作结果
*/
esp_err_t StartTask();
/**
* @brief 写寄存器数据
* @param reg_addr 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandWriteReg(uint8_t reg_addr, uint8_t data);
/**
* @brief 读寄存器数据
* @param reg_addr 寄存器地址
* @param data 要读出的数据
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandReadReg(uint8_t reg_addr, uint8_t *data);
/**
* @brief 读取输入端口0寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort0(uint8_t *data);
/**
* @brief 读取输入端口1寄存器数据
* @param data 输出参数,用于存储读取到的数据
* @return esp_err_t 操作结果
*/
esp_err_t ReadInputPort1(uint8_t *data);
// /**
// * @brief 设置IO扩展板GPIO中断状态 通过读取Input_Port0 和 Input_Port1 寄存器可以清除中断
// * @param reg_addr 寄存器地址
// * @return esp_err_t 操作结果
// */
// esp_err_t SetInterruptStatus(bool status);
/**
* @brief 配置IO扩展版上的IO口为输出还是输入,并且设置初始为高电平
* @return esp_err_t 操作结果
*/
esp_err_t IoexpandSetMode();
/**
* @brief 设置步进电机的细分模式
* @param mode 模式:1/2/4/8/16/32/64
* @return esp_err_t 操作结果
*/
void SetStepperMicrostepMode(uint8_t mode, uint8_t OUTPUT_PORT_m0, uint8_t m0_pin, uint8_t OUTPUT_PORT_m1, uint8_t m1_pin);
/**
* @brief 设置步进电机的方向
* @param dir 方向
* @param OUTPUT_PORT 输出端口
* @param pin 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t StepperMotorSetDir(bool dir, uint8_t OUTPUT_PORT, uint8_t pin);
/**
* @brief 设置步进电机的睡眠模式
* @param 是否进入睡眠模式
* @param 输出端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorSleepMode(bool mode, uint8_t OUTPUT_PORT, uint8_t nsleep_pin);
/**
* @brief 设置步进电机的使能引脚状态
* @param 使能状态
* @return esp_err_t 操作结果
*/
esp_err_t SetMotorEnableMode(bool enable, uint8_t OUTPUT_PORT, uint8_t enable_pin);
/**
* @brief 设置LCD的BL引脚状态
* @param BL状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdBlMode(bool mode, uint8_t OUTPUT_PORT, uint8_t bl_pin);
/**
* @brief 设置 LED_EN 引脚电平(用于控制LCD背光/LED灯电源)
* @param mode true:开启(低电平), false:关闭(高电平)
*/
esp_err_t SetLedEnMode(bool mode);
/**
* @brief 设置LCD的RST引脚状态
* @param RST状态
* @return esp_err_t 操作结果
*/
esp_err_t SetLcdResetMode(bool mode, uint8_t OUTPUT_PORT, uint8_t rst_pin);
/**
* @brief 设置PA控制引脚状态
* @param mode PA_CTRL状态(true为高电平,false为低电平)
* @return esp_err_t 操作结果
*/
esp_err_t SetPaCtrlMode(bool mode);
/**
* @brief 设置雷达中断引脚
* @param 毫米波雷达的中断引脚模式
* @param 输入端口
* @param 引脚号
* @return esp_err_t 操作结果
*/
esp_err_t SetRadarInterruptPin(bool mode, uint8_t INPUT_PORT, uint8_t interrupt_pin);
/**
* @brief 计算Port0的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort0();
/**
* @brief 计算Port1的字节值
* @return uint8_t 操作结果
*/
uint8_t ByteCalculatePort1();
/**
* @brief 设置雷达中断回调函数
* @param callback 雷达中断回调函数
*/
void SetRadarCallback(RadarCallback callback) { radar_callback_ = callback; }
/**
* @brief 设置光学传感器开始中断回调函数
* @param callback 光学传感器开始中断回调函数
*/
void SetPhotoInterrupterStartCallback(PhotoInterrupterStartCallback callback) { photo_start_callback_ = callback; }
/**
* @brief 设置光学传感器结束中断回调函数
* @param callback 光学传感器结束中断回调函数
*/
void SetPhotoInterrupterEndCallback(PhotoInterrupterEndCallback callback) { photo_end_callback_ = callback; }
/**
* @brief 设置电池充电中断回调函数
* @param callback 电池充电中断回调函数
*/
void SetBatteryChargingCallback(BatteryChargingCallback callback) { battery_charging_callback_ = callback; }
/**
* @brief 设置电池充满中断回调函数
* @param callback 电池充满中断回调函数
*/
void SetBatteryFullCallback(BatteryFullCallback callback) { battery_full_callback_ = callback; }
/**
* @brief 设置电池非充电中断回调函数
* @param callback 电池非充电中断回调函数
*/
void SetBatteryNotChargingCallback(BatteryNotChargingCallback callback) { battery_not_charging_callback_ = callback; }
// 公共成员变量
i2c_bus_handle_t bus_handle_ = nullptr; // I2C总线句柄
uint8_t device_addr_; // I2C设备地址
private:
// 静态成员函数
MillimeterWaveRadar* radar_ = nullptr; // 毫米波雷达指针,用于触发回调
PhotoInterrupter* photo_interrupter_ = nullptr; // 限位开关指针,用于触发回调
AdcBatteryMonitor* battery_monitor_ = nullptr; // 电池监控器指针,用于触发回调
static void IRAM_ATTR GpioIsrHandler(void* arg); // 中断处理函数
static void I2cReadTask(void* pvParameters); // I2C数据读取任务函数
// 成员变量用于状态跟踪
uint8_t last_port0_state_ = 0; // 上次检测到的Port0状态
uint8_t last_port1_state_ = 0; // 上次检测到的Port1状态
bool radar_last_state_ = false; // 雷达引脚上一次的状态
bool battery_charging_last_state_ = false; // 电池充电状态上一次的状态
bool battery_full_last_state_ = false; // 电池充满状态上一次的状态
// 回调函数成员变量
RadarCallback radar_callback_ = nullptr;
PhotoInterrupterStartCallback photo_start_callback_ = nullptr;
PhotoInterrupterEndCallback photo_end_callback_ = nullptr;
BatteryChargingCallback battery_charging_callback_ = nullptr;
BatteryFullCallback battery_full_callback_ = nullptr;
BatteryNotChargingCallback battery_not_charging_callback_ = nullptr;
// PhotoInterrupter* photo_interrupter_ = nullptr; // 光学传感器指针,用于触发回调
// /**
// * @brief 分配静态任务所需的内存
// * @return bool 分配是否成功
// */
// bool AllocateStaticTaskMemory();
// /**
// * @brief 释放静态任务分配的内存
// */
// void FreeStaticTaskMemory();
// 实例成员变量
EventGroupHandle_t i2c_task_event_group_ = nullptr; // 事件组
TaskHandle_t xI2CReadTaskHandle_ = nullptr; // 任务句柄
bool is_initialized_ = false; // 初始化状态标记(新增)
gpio_num_t interrupt_gpio_ = GPIO_NUM_NC; // 中断引脚
gpio_num_t reset_gpio_ = GPIO_NUM_NC; // 复位引脚
// StackType_t* pI2CReadTaskStackBuffer = nullptr; // 任务栈指针(PSRAM)
// StaticTask_t* pxI2CReadTaskBuffer = nullptr; // 任务控制块指针(内部RAM)
};
#endif
@@ -0,0 +1,178 @@
/**
* @file power_management_config.h
* @brief 功耗管理配置和策略定义
* @author Embedded Software Expert
* @date 2025年1月
*
* 这个文件定义了功耗管理的配置参数、策略和优化建议,
* 为系统提供灵活的功耗管理方案。
*/
#ifndef POWER_MANAGEMENT_CONFIG_H
#define POWER_MANAGEMENT_CONFIG_H
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "esp32/rom/rtc.h"
/**
* @brief 功耗管理配置结构体
*/
typedef struct {
// 睡眠模式配置
uint64_t light_sleep_duration_us; // 轻度睡眠持续时间(微秒)
uint64_t deep_sleep_duration_us; // 深度睡眠持续时间(微秒)
// 唤醒源配置
bool enable_timer_wakeup; // 启用定时器唤醒
bool enable_gpio_wakeup; // 启用GPIO唤醒
bool enable_touch_wakeup; // 启用触摸唤醒
gpio_num_t wakeup_gpio_num; // 唤醒GPIO编号
int wakeup_level; // 唤醒电平(0或1)
// 功耗优化配置
bool disable_bt_in_sleep; // 睡眠时关闭蓝牙
bool disable_wifi_in_sleep; // 睡眠时关闭WiFi
bool reduce_cpu_frequency; // 降低CPU频率
uint32_t cpu_frequency_hz; // CPU频率(Hz)
// 外设功耗配置
bool power_down_sdcard; // 睡眠时关闭SD卡电源
bool power_down_display; // 睡眠时关闭显示电源
bool power_down_audio; // 睡眠时关闭音频电源
bool power_down_motor; // 睡眠时关闭电机电源
// RTC内存配置
bool preserve_rtc_memory; // 保留RTC内存
size_t rtc_memory_size; // RTC内存大小
} power_management_config_t;
/**
* @brief 默认功耗管理配置
*/
#define DEFAULT_POWER_CONFIG { \
.light_sleep_duration_us = 1000000, /* 1秒 */ \
.deep_sleep_duration_us = 30000000, /* 30秒 */ \
.enable_timer_wakeup = true, /* 启用定时器唤醒 */ \
.enable_gpio_wakeup = false, /* 不启用GPIO唤醒 */ \
.enable_touch_wakeup = false, /* 不启用触摸唤醒 */ \
.wakeup_gpio_num = GPIO_NUM_NC, /* 无GPIO唤醒 */ \
.wakeup_level = 0, /* 低电平唤醒 */ \
.disable_bt_in_sleep = true, /* 睡眠时关闭蓝牙 */ \
.disable_wifi_in_sleep = true, /* 睡眠时关闭WiFi */ \
.reduce_cpu_frequency = true, /* 降低CPU频率 */ \
.cpu_frequency_hz = 80000000, /* 80MHz */ \
.power_down_sdcard = true, /* 睡眠时关闭SD卡 */ \
.power_down_display = true, /* 睡眠时关闭显示 */ \
.power_down_audio = true, /* 睡眠时关闭音频 */ \
.power_down_motor = true, /* 睡眠时关闭电机 */ \
.preserve_rtc_memory = true, /* 保留RTC内存 */ \
.rtc_memory_size = 4096, /* 4KB RTC内存 */ \
}
/**
* @brief 各功耗模式的详细配置
*/
static const power_management_config_t idle_mode_config = {
.light_sleep_duration_us = 500000, // 500ms轻度睡眠
.deep_sleep_duration_us = 0, // 不使用深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0, // 按键唤醒
.wakeup_level = 0,
.disable_bt_in_sleep = false, // 保持蓝牙连接
.disable_wifi_in_sleep = false, // 保持WiFi连接
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000, // 40MHz
.power_down_sdcard = false, // 保持SD卡(可能有后台任务)
.power_down_display = true, // 关闭显示背光
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 2048,
};
static const power_management_config_t light_sleep_config = {
.light_sleep_duration_us = 0, // 直接进入轻度睡眠
.deep_sleep_duration_us = 0,
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 40000000,
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 4096,
};
static const power_management_config_t deep_sleep_config = {
.light_sleep_duration_us = 0,
.deep_sleep_duration_us = 60000000, // 60秒深度睡眠
.enable_timer_wakeup = true,
.enable_gpio_wakeup = true,
.wakeup_gpio_num = GPIO_NUM_0,
.wakeup_level = 0,
.disable_bt_in_sleep = true, // 关闭蓝牙
.disable_wifi_in_sleep = true, // 关闭WiFi
.reduce_cpu_frequency = true,
.cpu_frequency_hz = 10000000, // 10MHz
.power_down_sdcard = true, // 关闭SD卡
.power_down_display = true, // 关闭显示
.power_down_audio = true, // 关闭音频
.power_down_motor = true, // 关闭电机
.preserve_rtc_memory = true,
.rtc_memory_size = 8192,
};
/**
* @brief 功耗统计和监控结构体
*/
typedef struct {
uint64_t total_sleep_time_us; // 总睡眠时间
uint64_t total_wake_time_us; // 总唤醒时间
uint32_t sleep_count; // 睡眠次数
uint32_t wake_count; // 唤醒次数
uint32_t average_sleep_duration_us; // 平均睡眠时长
uint32_t average_wake_duration_us; // 平均唤醒时长
float estimated_power_saving_percent; // 估计的功耗节省百分比
} power_statistics_t;
/**
* @brief 功耗优化建议
*
* 基于硬件特性和使用模式,提供功耗优化建议
*/
typedef enum {
OPTIMIZATION_NONE = 0,
OPTIMIZATION_REDUCE_CPU_FREQ, // 降低CPU频率
OPTIMIZATION_DISABLE_UNUSED_PERIPHERALS, // 关闭未使用的外设
OPTIMIZATION_USE_LIGHT_SLEEP, // 使用轻度睡眠
OPTIMIZATION_USE_DEEP_SLEEP, // 使用深度睡眠
OPTIMIZATION_OPTIMIZE_GPIO_STATES, // 优化GPIO状态
OPTIMIZATION_USE_RTC_MEMORY, // 使用RTC内存
OPTIMIZATION_BATCH_OPERATIONS, // 批量操作
} power_optimization_t;
/**
* @brief 功耗管理事件回调
*/
typedef void (*power_event_callback_t)(power_mode_t old_mode, power_mode_t new_mode, void* user_data);
/**
* @brief 功耗管理事件
*/
typedef enum {
POWER_EVENT_MODE_CHANGE, // 功耗模式改变
POWER_EVENT_SLEEP_START, // 睡眠开始
POWER_EVENT_SLEEP_END, // 睡眠结束
POWER_EVENT_WAKEUP, // 系统唤醒
POWER_EVENT_OPTIMIZATION_TRIGGER, // 功耗优化触发
} power_event_t;
#endif // POWER_MANAGEMENT_CONFIG_H
+26
View File
@@ -0,0 +1,26 @@
#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H
#include "sdkconfig.h"
// 根据menuconfig的配置选择不同版本的config.h文件
#ifdef CONFIG_BOARD_VERSION_V1_1_11
#include "boards/board_v1_1_11/config.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_12)
#include "boards/board_v1_1_12/config.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_13)
#include "boards/board_v1_1_13/config.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_14)
#include "boards/board_v1_1_14/config.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_16)
#include "boards/board_v1_1_16/config.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_17)
#include "boards/board_v1_1_17/config.h"
#elif defined(CONFIG_BOARD_VERSION_V1_1_18)
#include "boards/board_v1_1_18/config.h"
#else
// 默认使用v1.1.11版本,避免编译错误
#include "boards/board_v1_1_13/config.h"
#endif
#endif // BOARD_CONFIG_H
+35
View File
@@ -0,0 +1,35 @@
# 根据 menuconfig 中选择的板卡配置,自动匹配 boards/board_v* 目录
# 这样新增板卡时,只要:
# 1. 增加对应的 Kconfig 项,例如 CONFIG_BOARD_VERSION_V1_1_18
# 2. 新增目录 02_physical/boards/board_v1_1_18/
# 就不需要再修改 main/CMakeLists.txt
set(BOARD_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}")
set(MAIN_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../..")
set(SELECTED_BOARD_DIR "")
file(GLOB BOARD_CANDIDATE_DIRS RELATIVE "${BOARD_ROOT_DIR}" "${BOARD_ROOT_DIR}/board_v*")
foreach(board_dir ${BOARD_CANDIDATE_DIRS})
if(IS_DIRECTORY "${BOARD_ROOT_DIR}/${board_dir}")
string(REGEX REPLACE "^board_" "" board_suffix "${board_dir}")
string(TOUPPER "${board_suffix}" board_suffix_upper)
set(board_config_var "CONFIG_BOARD_VERSION_${board_suffix_upper}")
if(DEFINED ${board_config_var} AND ${board_config_var})
set(SELECTED_BOARD_DIR "02_physical/boards/${board_dir}")
break()
endif()
endif()
endforeach()
if(NOT SELECTED_BOARD_DIR)
message(FATAL_ERROR "Please select a board version in menuconfig")
endif()
target_sources(${COMPONENT_LIB} PRIVATE "${SELECTED_BOARD_DIR}/board_sweekar.cc")
target_sources(${COMPONENT_LIB} PRIVATE "${SELECTED_BOARD_DIR}/io_expansion_board.cc")
target_include_directories(${COMPONENT_LIB} PRIVATE "${SELECTED_BOARD_DIR}")
# 添加到 main 目录,使类似 "boards/config.h" 的包含路径保持兼容
target_include_directories(${COMPONENT_LIB} PRIVATE "${MAIN_ROOT_DIR}")
+247
View File
@@ -0,0 +1,247 @@
#include "audio_codec.h"
#include "esp_log.h"
#include "es7210.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
static const char *TAG = "AudioCodec";
// 静态成员初始化
AudioCodec* AudioCodec::instance_ = nullptr;
SemaphoreHandle_t AudioCodec::mutex_ = nullptr;
AudioCodec::AudioCodec()
: board_handle_(nullptr),
is_initialized_(false),
current_mode_(AUDIO_CODEC_MODE_DECODE),
current_volume_(100)
{
}
AudioCodec::~AudioCodec() {
Deinitialize();
}
AudioCodec* AudioCodec::GetInstance() {
// 双重检查锁定模式确保线程安全
if (instance_ == nullptr) {
if (mutex_ == nullptr) {
mutex_ = xSemaphoreCreateMutex();
}
if (xSemaphoreTake(mutex_, portMAX_DELAY) == pdTRUE) {
if (instance_ == nullptr) {
instance_ = new AudioCodec();
}
xSemaphoreGive(mutex_);
}
}
return instance_;
}
esp_err_t AudioCodec::Initialize() {
if (is_initialized_) {
ESP_LOGW(TAG, "AudioCodec already initialized");
return ESP_OK;
}
ESP_LOGI(TAG, "Initializing AudioCodec");
esp_err_t ret = InitializeBoard();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize audio board: %s", esp_err_to_name(ret));
return ret;
}
// 先设置初始化标志,然后启动编解码器
is_initialized_ = true;
// 默认启用双向模式,同时支持播放和录音
ret = StartCodec(AUDIO_CODEC_MODE_BOTH);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start codec in both mode: %s", esp_err_to_name(ret));
is_initialized_ = false; // 如果启动失败,重置标志
return ret;
}
ESP_LOGI(TAG, "AudioCodec initialized successfully with both encode and decode enabled");
return ESP_OK;
}
esp_err_t AudioCodec::InitializeBoard() {
// 初始化音频板
board_handle_ = audio_board_init();
if (board_handle_ == nullptr) {
ESP_LOGE(TAG, "Failed to initialize audio board");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Audio board initialized successfully");
return ESP_OK;
}
esp_err_t AudioCodec::StartCodec(audio_codec_mode_t mode) {
// 检查音频板是否已初始化,而不是检查整体初始化状态
if (board_handle_ == nullptr) {
ESP_LOGE(TAG, "Audio board not initialized");
return ESP_ERR_INVALID_STATE;
}
esp_err_t ret = ESP_OK;
switch (mode) {
case AUDIO_CODEC_MODE_DECODE:
ESP_LOGI(TAG, "Starting codec in decode mode");
ret = audio_hal_ctrl_codec(board_handle_->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);
break;
case AUDIO_CODEC_MODE_ENCODE:
ESP_LOGI(TAG, "Starting codec in encode mode");
ret = audio_hal_ctrl_codec(board_handle_->audio_hal, AUDIO_HAL_CODEC_MODE_ENCODE, AUDIO_HAL_CTRL_START);
if (ret == ESP_OK) {
// 设置ADC音量
ret = audio_hal_set_volume(board_handle_->adc_hal, 100);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "Failed to set ADC volume: %s", esp_err_to_name(ret));
}
// 设置ES7210麦克风增益
ret = es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3), GAIN_37_5DB);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "Failed to set ES7210 gain: %s", esp_err_to_name(ret));
} else {
ESP_LOGI(TAG, "ES7210 gain set to 37.5dB");
}
}
break;
case AUDIO_CODEC_MODE_BOTH:
ESP_LOGI(TAG, "Starting codec in both modes");
ret = audio_hal_ctrl_codec(board_handle_->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START);
if (ret == ESP_OK) {
// 同时设置播放和录音参数
audio_hal_set_volume(board_handle_->adc_hal, 100);
es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3), GAIN_37_5DB);
}
break;
default:
ESP_LOGE(TAG, "Invalid codec mode: %d", mode);
return ESP_ERR_INVALID_ARG;
}
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start codec: %s", esp_err_to_name(ret));
return ret;
}
current_mode_ = mode;
// 设置初始音量(仅在播放模式下)
if (mode == AUDIO_CODEC_MODE_DECODE || mode == AUDIO_CODEC_MODE_BOTH) {
SetVolume(current_volume_);
}
ESP_LOGI(TAG, "Codec started successfully in mode %d", mode);
return ESP_OK;
}
esp_err_t AudioCodec::StopCodec() {
if (!is_initialized_) {
ESP_LOGW(TAG, "AudioCodec not initialized");
return ESP_OK;
}
ESP_LOGI(TAG, "Stopping codec");
// 修复:直接使用audio_hal_codec_mode_t类型
esp_err_t ret = audio_hal_ctrl_codec(board_handle_->audio_hal, (audio_hal_codec_mode_t)current_mode_, AUDIO_HAL_CTRL_STOP);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "Failed to stop codec: %s", esp_err_to_name(ret));
}
return ret;
}
esp_err_t AudioCodec::SetVolume(uint8_t volume) {
if (!is_initialized_) {
ESP_LOGE(TAG, "AudioCodec not initialized");
return ESP_ERR_INVALID_STATE;
}
if (volume > 100) {
ESP_LOGW(TAG, "Volume %d exceeds maximum, setting to 100", volume);
volume = 100;
}
// 转换为HAL音量范围 (0-63)
int hal_volume = (volume * 63) / 100;
esp_err_t ret = audio_hal_set_volume(board_handle_->audio_hal, hal_volume);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set volume: %s", esp_err_to_name(ret));
return ret;
}
current_volume_ = volume;
ESP_LOGI(TAG, "Volume set to %d%% (HAL volume: %d)", volume, hal_volume);
return ESP_OK;
}
esp_err_t AudioCodec::GetVolume(uint8_t* volume) {
if (!volume) {
return ESP_ERR_INVALID_ARG;
}
*volume = current_volume_;
return ESP_OK;
}
esp_err_t AudioCodec::SetAdcVolume(uint8_t volume) {
if (!is_initialized_) {
ESP_LOGE(TAG, "AudioCodec not initialized");
return ESP_ERR_INVALID_STATE;
}
if (volume > 100) {
volume = 100;
}
esp_err_t ret = audio_hal_set_volume(board_handle_->adc_hal, volume);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set ADC volume: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(TAG, "ADC volume set to %d", volume);
return ESP_OK;
}
audio_board_handle_t AudioCodec::GetBoardHandle() {
return board_handle_;
}
bool AudioCodec::IsInitialized() {
return is_initialized_;
}
esp_err_t AudioCodec::Deinitialize() {
if (!is_initialized_) {
return ESP_OK;
}
ESP_LOGI(TAG, "Deinitializing AudioCodec");
StopCodec();
Cleanup();
is_initialized_ = false;
ESP_LOGI(TAG, "AudioCodec deinitialized");
return ESP_OK;
}
void AudioCodec::Cleanup() {
if (board_handle_) {
audio_board_deinit(board_handle_);
board_handle_ = nullptr;
}
}
+74
View File
@@ -0,0 +1,74 @@
#ifndef AUDIO_CODEC_H
#define AUDIO_CODEC_H
#include "esp_err.h"
#include "audio_hal.h"
#include "board.h"
#include "esp_peripherals.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// 使用audio_hal中已定义的模式类型,避免重复定义
typedef audio_hal_codec_mode_t audio_codec_mode_t;
// 为了代码可读性,定义别名
#define AUDIO_CODEC_MODE_DECODE AUDIO_HAL_CODEC_MODE_DECODE
#define AUDIO_CODEC_MODE_ENCODE AUDIO_HAL_CODEC_MODE_ENCODE
#define AUDIO_CODEC_MODE_BOTH AUDIO_HAL_CODEC_MODE_BOTH
class AudioCodec {
public:
// 获取单例实例
static AudioCodec* GetInstance();
// 初始化音频编解码器(默认启用双向模式)
esp_err_t Initialize();
// 音量控制
esp_err_t SetVolume(uint8_t volume);
esp_err_t GetVolume(uint8_t* volume);
// ADC音量控制(录音)
esp_err_t SetAdcVolume(uint8_t volume);
// 获取音频板句柄
audio_board_handle_t GetBoardHandle();
// 检查是否已初始化
bool IsInitialized();
// 反初始化
esp_err_t Deinitialize();
private:
AudioCodec(); // 私有构造函数
~AudioCodec();
// 禁止拷贝构造和赋值
AudioCodec(const AudioCodec&) = delete;
AudioCodec& operator=(const AudioCodec&) = delete;
static AudioCodec* instance_;
static SemaphoreHandle_t mutex_;
audio_board_handle_t board_handle_;
bool is_initialized_;
audio_codec_mode_t current_mode_;
uint8_t current_volume_;
esp_err_t InitializeBoard();
void Cleanup();
esp_err_t StartCodec(audio_codec_mode_t mode);
esp_err_t StopCodec();
};
#ifdef __cplusplus
}
#endif
#endif // AUDIO_CODEC_H
+16
View File
@@ -0,0 +1,16 @@
# main/04_hal/audio_codec/module.cmake
# 判断 menuconfig 中的开关
if(NOT CONFIG_ENABLE_HAL_AUDIO_CODEC AND NOT EARLY_EXPANSION)
message(STATUS " [Skip] audio_codec module disabled by menuconfig.")
return()
endif()
list(APPEND MODULE_SRCS
"04_hal/audio_codec/audio_codec.cc"
)
list(APPEND MODULE_INCLUDE_DIRS
"04_hal/audio_codec"
)
@@ -0,0 +1,41 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include "esp_err.h"
/**
* @brief Base Board Hardware Abstraction Layer
* Abstracts the communication transport (UART vs I2C) and sensor reading.
*/
class BaseBoardHal
{
public:
virtual ~BaseBoardHal() = default;
/**
* @brief Initialize the hardware interface
*/
virtual esp_err_t Init() = 0;
/**
* @brief Send raw data to the base board
* @param data Pointer to data buffer
* @param len Length of data
*/
virtual esp_err_t Send(const uint8_t *data, size_t len) = 0;
/**
* @brief Read raw data from the base board
* @param data Pointer to data buffer
* @param len Length of data to read
*/
virtual esp_err_t Read(uint8_t *data, size_t len) = 0;
/**
* @brief Get the current angle from Hall sensor (or via query)
* @param[out] angle Output angle in degrees (0.0 - 360.0)
* @return ESP_OK on success
*/
virtual esp_err_t GetAngle(float *angle) = 0;
};
@@ -0,0 +1,65 @@
#pragma once
#include "base_board_hal.h"
#include "i2c_bus.h"
#include "esp_log.h"
class BaseBoardHalI2c : public BaseBoardHal
{
public:
static constexpr uint8_t kBaseSlaveAddr = 0x22;
// TMAG5273 default address often 0x35 or defined by config
static constexpr uint8_t kHallSensorAddr = 0x35;
BaseBoardHalI2c(i2c_bus_handle_t bus_handle)
: bus_handle_(bus_handle) {}
virtual ~BaseBoardHalI2c()
{
// i2c_bus handles device management internally if used via i2c_bus_create
// But here we just use the bus handle directly.
}
virtual esp_err_t Init() override
{
if (!bus_handle_) return ESP_ERR_INVALID_STATE;
// Check if device is alive
esp_err_t err = i2c_bus_probe_addr(bus_handle_, kBaseSlaveAddr << 1);
if (err != ESP_OK) {
ESP_LOGW("BaseBoardHalI2c", "Base board not found at 0x%02X", kBaseSlaveAddr);
// Don't fail init, maybe it's sleeping
} else {
ESP_LOGI("BaseBoardHalI2c", "Base board found at 0x%02X", kBaseSlaveAddr);
}
return ESP_OK;
}
virtual esp_err_t Send(const uint8_t *data, size_t len) override
{
if (!bus_handle_) return ESP_ERR_INVALID_STATE;
// i2c_bus_write_bytes expects: bus, addr, reg_addr, reg_len, data, datalen
// The data pointer in i2c_bus_write_bytes is (uint8_t *), not (const uint8_t *)
// This is a common issue in legacy C APIs. We have to cast away constness.
return i2c_bus_write_bytes(bus_handle_, kBaseSlaveAddr << 1, NULL, 0, (uint8_t*)data, len);
}
virtual esp_err_t Read(uint8_t *data, size_t len) override
{
if (!bus_handle_) return ESP_ERR_INVALID_STATE;
return i2c_bus_read_bytes(bus_handle_, kBaseSlaveAddr << 1, NULL, 0, data, len);
}
virtual esp_err_t GetAngle(float *angle) override
{
// TODO: Implement TMAG5273 read logic using i2c_bus_read_bytes
return ESP_ERR_NOT_SUPPORTED;
}
private:
i2c_bus_handle_t bus_handle_;
};
@@ -0,0 +1,45 @@
#pragma once
#include "base_board_hal.h"
#include "driver/uart.h"
class BaseBoardHalUart : public BaseBoardHal
{
public:
BaseBoardHalUart(uart_port_t port) : port_(port) {}
virtual ~BaseBoardHalUart() = default;
virtual esp_err_t Init() override
{
// Assuming UART is already initialized by board.cc or similar
return ESP_OK;
}
virtual esp_err_t Send(const uint8_t *data, size_t len) override
{
if (port_ >= UART_NUM_MAX) {
return ESP_ERR_INVALID_STATE;
}
int written = uart_write_bytes(port_, (const char *)data, len);
return (written == (int)len) ? ESP_OK : ESP_FAIL;
}
virtual esp_err_t Read(uint8_t *data, size_t len) override
{
if (port_ >= UART_NUM_MAX) {
return ESP_ERR_INVALID_STATE;
}
int read = uart_read_bytes(port_, data, len, pdMS_TO_TICKS(100));
return (read == (int)len) ? ESP_OK : ESP_FAIL;
}
virtual esp_err_t GetAngle(float *angle) override
{
// TODO: Implement angle reading via UART (query command or report parsing)
// For now, return not supported or cached value if we had one
return ESP_ERR_NOT_SUPPORTED;
}
private:
uart_port_t port_;
};
+13
View File
@@ -0,0 +1,13 @@
# main/04_hal/base_board_hal/module.cmake
# 注意:此文件夹可能没有 cpp 源文件,仅有头文件。如果有源文件,请追加到 MODULE_SRCS。
# 判断 menuconfig 中的开关
if(NOT CONFIG_ENABLE_HAL_BASE_BOARD_HAL AND NOT EARLY_EXPANSION)
message(STATUS " [Skip] base_board_hal module disabled by menuconfig.")
return()
endif()
list(APPEND MODULE_INCLUDE_DIRS
"04_hal/base_board_hal"
)
+213
View File
@@ -0,0 +1,213 @@
#include "aw8646.h"
#include "config.h"
#include "esp_log.h"
#include "io_expansion_board.h"
// Define AW8646 specific pins for Board v1.1.16
// P0_0 -> DRV_T0
// P0_6 -> DRV_T1
// Only if NFC_RST_PIN is defined (implying it's moved to GPIO, freeing up P0_0)
#ifdef NFC_RST_PIN
#define AW8646_USE_CURRENT_CONTROL
#define AW8646_T0_PIN 0
#define AW8646_T0_PORT OUTPUT_PORT0
#define AW8646_T1_PIN 6
#define AW8646_T1_PORT OUTPUT_PORT0
#endif
Aw8646Driver::Aw8646Driver(IOExpansionBoard &ioExp) : io_expansion_(ioExp)
{
}
esp_err_t Aw8646Driver::Init()
{
esp_err_t err = ESP_OK;
// 1. 默认处于休眠和禁用状态,防止发热!
err = io_expansion_.SetMotorSleepMode(
STEPPER_MOTOR_SLEEP,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set sleep mode: 0x%x", err);
return err;
}
// 2. Disable Enable Pin (High = Disable for AW8646)
err = io_expansion_.SetMotorEnableMode(
true, // High -> Disable
STEPPER_MOTOR_ENABLE_PORT,
STEPPER_MOTOR_ENABLE_PIN
);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set enable mode: 0x%x", err);
return err;
}
#ifdef AW8646_USE_CURRENT_CONTROL
// 3. Initialize T0 and T1 pins for Current Control
// Default to Low (0, 0) for now.
uint8_t output0_val = 0;
err = io_expansion_.IoexpandReadReg(OUTPUT_PORT0, &output0_val);
if (err == ESP_OK) {
output0_val &= ~(1 << AW8646_T0_PIN); // Set T0 Low
output0_val &= ~(1 << AW8646_T1_PIN); // Set T1 Low
err = io_expansion_.IoexpandWriteReg(OUTPUT_PORT0, output0_val);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set T0/T1: 0x%x", err);
}
} else {
ESP_LOGE(TAG, "Failed to read port 0: 0x%x", err);
}
#endif
// 4. Set Microsteps (Default to 1/8 step)
err = SetMicrosteps(8);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set microsteps: 0x%x", err);
return err;
}
// 5. Set Default Direction
err = SetDirection(true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set direction: 0x%x", err);
return err;
}
ESP_LOGI(TAG, "AW8646 Driver Initialized (EN Logic Inverted, T0/T1 Configured)");
return ESP_OK;
}
esp_err_t Aw8646Driver::Enable(bool enable)
{
esp_err_t err = ESP_OK;
if (enable)
{
ESP_LOGD(TAG, "Enabling AW8646 (sleep + enable=Low)");
// Ensure Sleep is High (Work)
err = io_expansion_.SetMotorSleepMode(
STEPPER_MOTOR_WORK,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
if (err != ESP_OK) return err;
// Enable Pin: Low to Enable
err = io_expansion_.SetMotorEnableMode(
false, // Low -> Enable
STEPPER_MOTOR_ENABLE_PORT,
STEPPER_MOTOR_ENABLE_PIN
);
}
else
{
ESP_LOGD(TAG, "Disabling AW8646 (disable=High + sleep)");
// Enable Pin: High to Disable
err = io_expansion_.SetMotorEnableMode(
true, // High -> Disable
STEPPER_MOTOR_ENABLE_PORT,
STEPPER_MOTOR_ENABLE_PIN
);
if (err != ESP_OK) return err;
// Sleep Pin: Low to Sleep
err = io_expansion_.SetMotorSleepMode(
STEPPER_MOTOR_SLEEP,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
}
return err;
}
esp_err_t Aw8646Driver::SetDirection(bool dir)
{
uint8_t mode = dir ? STEPPER_MOTOR_RIGHT : STEPPER_MOTOR_LEFT;
return io_expansion_.StepperMotorSetDir(
mode,
STEPPER_MOTOR_DIR_PORT,
STEPPER_MOTOR_DIR_PIN
);
}
esp_err_t Aw8646Driver::Sleep(bool sleep)
{
uint8_t mode = sleep ? STEPPER_MOTOR_SLEEP : STEPPER_MOTOR_WORK;
return io_expansion_.SetMotorSleepMode(
mode,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
}
esp_err_t Aw8646Driver::SetTriLevelPin(uint8_t port_reg, uint8_t config_reg, uint8_t pin, int state)
{
uint8_t val;
esp_err_t err = ESP_OK;
if (state == 2) { // Hi-Z -> Input
// Set as Input
err = io_expansion_.IoexpandReadReg(config_reg, &val);
if (err != ESP_OK) return err;
val |= (1 << pin); // Set Input
err = io_expansion_.IoexpandWriteReg(config_reg, val);
} else { // 0 or 1 -> Output
// Set as Output
err = io_expansion_.IoexpandReadReg(config_reg, &val);
if (err != ESP_OK) return err;
val &= ~(1 << pin); // Set Output
err = io_expansion_.IoexpandWriteReg(config_reg, val);
if (err != ESP_OK) return err;
// Set Level
err = io_expansion_.IoexpandReadReg(port_reg, &val);
if (err != ESP_OK) return err;
if (state == 1) val |= (1 << pin);
else val &= ~(1 << pin);
err = io_expansion_.IoexpandWriteReg(port_reg, val);
}
return err;
}
esp_err_t Aw8646Driver::SetMicrosteps(int microsteps)
{
// AW8646 Microstep Table (Rising Edge Only)
// 1 (Full): MODE1=0, MODE0=0
// 2 (1/2): MODE1=0, MODE0=Z (Hi-Z)
// 4 (1/4): MODE1=0, MODE0=1
// 8 (1/8): MODE1=Z, MODE0=0
// 16 (1/16): MODE1=Z, MODE0=1
// 32 (1/32): MODE1=1, MODE0=Z
int m1 = 0;
int m0 = 0;
switch(microsteps) {
case 1: m1 = 0; m0 = 0; break;
case 2: m1 = 0; m0 = 2; break; // Z
case 4: m1 = 0; m0 = 1; break;
case 8: m1 = 2; m0 = 0; break; // Z
case 16: m1 = 2; m0 = 1; break; // Z
case 32: m1 = 1; m0 = 2; break; // Z
default:
ESP_LOGW(TAG, "Unsupported microsteps: %d, defaulting to 1/8", microsteps);
m1 = 2; m0 = 0;
break;
}
ESP_LOGI(TAG, "Setting Microsteps to 1/%d (M1=%d, M0=%d)", microsteps, m1, m0);
esp_err_t err;
err = SetTriLevelPin(STEPPER_MOTOR_M1_MADE_PORT, STEPPER_MOTOR_M1_CONFIG, STEPPER_MOTOR_M1_PIN, m1);
if (err != ESP_OK) return err;
err = SetTriLevelPin(STEPPER_MOTOR_M0_MADE_PORT, STEPPER_MOTOR_M0_CONFIG, STEPPER_MOTOR_M0_PIN, m0);
return err;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef AW8646_DRIVER_H
#define AW8646_DRIVER_H
#include "stepper_driver.h"
#include "io_expansion_board.h"
#include "esp_log.h"
class Aw8646Driver : public StepperDriver
{
public:
explicit Aw8646Driver(IOExpansionBoard &ioExp);
virtual ~Aw8646Driver() = default;
esp_err_t Init() override;
esp_err_t Enable(bool enable) override;
esp_err_t SetDirection(bool dir) override;
esp_err_t Sleep(bool sleep) override;
esp_err_t SetMicrosteps(int microsteps) override;
private:
esp_err_t SetTriLevelPin(uint8_t port_reg, uint8_t config_reg, uint8_t pin, int state);
IOExpansionBoard &io_expansion_;
const char* TAG = "Aw8646Driver";
};
#endif // AW8646_DRIVER_H
Binary file not shown.
+147
View File
@@ -0,0 +1,147 @@
#include "drv8428.h"
#include "config.h"
Drv8428Driver::Drv8428Driver(IOExpansionBoard &ioExp) : io_expansion_(ioExp)
{
}
esp_err_t Drv8428Driver::Init()
{
esp_err_t err = ESP_OK;
// 1. Set Sleep Mode to WORK (wake up initially to configure?)
// Actually BoardSweekar::StepperMotorInit sets it to WORK then ENABLE_DISABLE.
// Let's follow that.
err = io_expansion_.SetMotorSleepMode(
STEPPER_MOTOR_WORK,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set sleep mode: 0x%x", err);
return err;
}
// 2. Disable Enable Pin
err = io_expansion_.SetMotorEnableMode(
STEPPER_MOTOR_ENABLE_DISABLE,
STEPPER_MOTOR_ENABLE_PORT,
STEPPER_MOTOR_ENABLE_PIN
);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set enable mode: 0x%x", err);
return err;
}
// 3. Set Microsteps (Defaulting to 1/8 as per original code which passed 4? need to verify)
// BoardSweekar passed 4.
// "io_expansion_.SetStepperMicrostepMode(4, ...)"
// Let's assume 4 maps to 1/8 or whatever.
err = SetMicrosteps(4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set microsteps: 0x%x", err);
return err;
}
// 4. Set Default Direction
err = SetDirection(true); // Default to "UP" or whatever true means
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set direction: 0x%x", err);
return err;
}
ESP_LOGI(TAG, "DRV8428 Driver Initialized");
return ESP_OK;
}
esp_err_t Drv8428Driver::Enable(bool enable)
{
esp_err_t err = ESP_OK;
if (enable)
{
ESP_LOGD(TAG, "Enabling stepper motor (sleep + enable)");
// 1. Set sleep mode to WORK
err = io_expansion_.SetMotorSleepMode(
STEPPER_MOTOR_WORK,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
if (err != ESP_OK) return err;
// 2. Set enable mode to ENABLE
err = io_expansion_.SetMotorEnableMode(
STEPPER_MOTOR_ENABLE_ENABLE,
STEPPER_MOTOR_ENABLE_PORT,
STEPPER_MOTOR_ENABLE_PIN
);
}
else
{
ESP_LOGD(TAG, "Disabling stepper motor (disable + sleep)");
// 1. Disable enable pin
err = io_expansion_.SetMotorEnableMode(
STEPPER_MOTOR_ENABLE_DISABLE,
STEPPER_MOTOR_ENABLE_PORT,
STEPPER_MOTOR_ENABLE_PIN
);
if (err != ESP_OK) return err;
// 2. Set sleep mode to SLEEP
err = io_expansion_.SetMotorSleepMode(
STEPPER_MOTOR_SLEEP,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
}
return err;
}
esp_err_t Drv8428Driver::SetDirection(bool dir)
{
// dir: true=UP, false=DOWN (based on original StepperMotor::SetDir logic)
// Original: dir ? STEPPER_MOTOR_RIGHT : STEPPER_MOTOR_LEFT
// Wait, StepperMotor::SetDir used STEPPER_MOTOR_RIGHT for true.
// STEPPER_MOTOR_RIGHT is a macro, likely 1 or 0.
uint8_t mode = dir ? STEPPER_MOTOR_RIGHT : STEPPER_MOTOR_LEFT;
return io_expansion_.StepperMotorSetDir(
mode,
STEPPER_MOTOR_DIR_PORT,
STEPPER_MOTOR_DIR_PIN
);
}
esp_err_t Drv8428Driver::Sleep(bool sleep)
{
// This is explicitly setting sleep mode, might be redundant with Enable(false)
// but useful if we want to sleep without disabling or vice versa if supported.
// For DRV8428, Enable(false) sets Sleep to SLEEP.
uint8_t mode = sleep ? STEPPER_MOTOR_SLEEP : STEPPER_MOTOR_WORK;
return io_expansion_.SetMotorSleepMode(
mode,
STEPPER_MOTOR_SLEEP_PORT,
STEPPER_MOTOR_SLEEP_PIN
);
}
esp_err_t Drv8428Driver::SetMicrosteps(int microsteps)
{
// Original code used hardcoded ports for microsteps.
// io_expansion_.SetStepperMicrostepMode(4, STEPPER_MOTOR_M0_MADE_PORT, STEPPER_MOTOR_M0_PIN,
// STEPPER_MOTOR_M1_MADE_PORT, STEPPER_MOTOR_M1_PIN);
// We can just pass the value 'microsteps' if it corresponds to the index expected by SetStepperMicrostepMode.
// Or we might need a mapping.
// Assuming 'microsteps' passed here is the raw value/index for now.
io_expansion_.SetStepperMicrostepMode(
(uint8_t)microsteps,
STEPPER_MOTOR_M0_MADE_PORT, STEPPER_MOTOR_M0_PIN,
STEPPER_MOTOR_M1_MADE_PORT, STEPPER_MOTOR_M1_PIN
);
return ESP_OK;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef DRV8428_DRIVER_H
#define DRV8428_DRIVER_H
#include "stepper_driver.h"
#include "io_expansion_board.h"
#include "esp_log.h"
class Drv8428Driver : public StepperDriver
{
public:
explicit Drv8428Driver(IOExpansionBoard &ioExp);
virtual ~Drv8428Driver() = default;
esp_err_t Init() override;
esp_err_t Enable(bool enable) override;
esp_err_t SetDirection(bool dir) override;
esp_err_t Sleep(bool sleep) override;
esp_err_t SetMicrosteps(int microsteps) override;
private:
IOExpansionBoard &io_expansion_;
const char* TAG = "Drv8428Driver";
};
#endif // DRV8428_DRIVER_H
+17
View File
@@ -0,0 +1,17 @@
# main/04_hal/stepper_driver/module.cmake
# 判断 menuconfig 中的开关
if(NOT CONFIG_ENABLE_HAL_STEPPER_DRIVER AND NOT EARLY_EXPANSION)
message(STATUS " [Skip] stepper_driver module disabled by menuconfig.")
return()
endif()
list(APPEND MODULE_SRCS
"04_hal/stepper_driver/drv8428.cc"
"04_hal/stepper_driver/aw8646.cc"
)
list(APPEND MODULE_INCLUDE_DIRS
"04_hal/stepper_driver"
)
@@ -0,0 +1,54 @@
#ifndef STEPPER_DRIVER_H
#define STEPPER_DRIVER_H
#include <cstdint>
#include "esp_err.h"
/**
* @brief Abstract base class for stepper motor driver chips.
*
* This interface abstracts the low-level control of the stepper motor driver,
* such as enabling/disabling, setting direction, sleep mode, and microstepping configuration.
* It does NOT handle step generation (that is handled by the StepperMotor class using GPTimer).
*/
class StepperDriver
{
public:
virtual ~StepperDriver() = default;
/**
* @brief Initialize the driver hardware (GPIOs, IO Expander, etc.)
* @return ESP_OK on success
*/
virtual esp_err_t Init() = 0;
/**
* @brief Enable or disable the motor driver output.
* @param enable true to enable, false to disable
* @return ESP_OK on success
*/
virtual esp_err_t Enable(bool enable) = 0;
/**
* @brief Set the motor direction.
* @param dir true for one direction (e.g. UP/CW), false for the other (e.g. DOWN/CCW)
* @return ESP_OK on success
*/
virtual esp_err_t SetDirection(bool dir) = 0;
/**
* @brief Set the motor driver to sleep mode to save power.
* @param sleep true to sleep, false to wake up
* @return ESP_OK on success
*/
virtual esp_err_t Sleep(bool sleep) = 0;
/**
* @brief Set microstepping mode.
* @param microsteps Microstepping value (e.g., 1, 2, 4, 8, 16, 32, etc.)
* @return ESP_OK on success
*/
virtual esp_err_t SetMicrosteps(int microsteps) = 0;
};
#endif // STEPPER_DRIVER_H
@@ -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"
)
+625
View File
@@ -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, &reg, 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, &reg, 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, &reg, 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检查一次,更快的响应
}
}
+214
View File
@@ -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
+16
View File
@@ -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
+150
View File
@@ -0,0 +1,150 @@
# 获取当前选择的 demo 配置
if(CONFIG_SWEEKAR_DEMO_1)
set(ACTIVE_DEMO "sweekar_demo1")
elseif(CONFIG_SWEEKAR_DEMO_2)
set(ACTIVE_DEMO "sweekar_demo2")
elseif(CONFIG_SWEEKAR_DEMO_3)
set(ACTIVE_DEMO "sweekar_demo3")
elseif(CONFIG_SWEEKAR_DEMO_4)
set(ACTIVE_DEMO "sweekar_demo4")
elseif(CONFIG_SWEEKAR_DEMO_5)
set(ACTIVE_DEMO "sweekar_demo5")
elseif(CONFIG_SWEEKAR_DEMO_6)
set(ACTIVE_DEMO "sweekar_demo6")
elseif(CONFIG_SWEEKAR_DEMO_7)
set(ACTIVE_DEMO "sweekar_demo7")
endif()
# 初始化全局变量,用于收集各模块的信息
set(MODULE_SRCS)
set(MODULE_INCLUDE_DIRS)
set(MODULE_REQUIRES)
set(MODULE_PRIV_REQUIRES)
set(MODULE_EMBED_TXTFILES)
if(NOT DEFINED CONFIG_SWEEKAR_DEMO_SELECTION)
set(EARLY_EXPANSION TRUE)
message(STATUS "Early expansion pass: loading all module requirements")
else()
set(EARLY_EXPANSION FALSE)
endif()
# ===================================================================
# 执行 Kconfig 自动组装脚本 (在配置阶段自动生成 Kconfig.modules)
# ===================================================================
execute_process(
COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/generate_kconfig_modules.py
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
RESULT_VARIABLE generate_kconfig_result
)
if(NOT generate_kconfig_result EQUAL 0)
message(FATAL_ERROR "Failed to generate Kconfig.modules")
endif()
# ==================== 模块注册区 ====================
# 定义需要扫描的架构层级目录
set(SUBSYSTEM_DIRS
"04_hal"
"05_executors"
"05_service"
"06_behaviors"
"tests"
)
# 扫描并加载所有启用的子模块(依赖各个模块中的 module.cmake)
foreach(dir ${SUBSYSTEM_DIRS})
file(GLOB MODULE_FILES "${CMAKE_CURRENT_LIST_DIR}/${dir}/*/module.cmake")
foreach(mod_file ${MODULE_FILES})
include(${mod_file})
endforeach()
endforeach()
# sweekar_demos 模块 (单独加载)
# include(${CMAKE_CURRENT_LIST_DIR}/sweekar_demos/module.cmake)
# ===================================================================
if(MODULE_REQUIRES)
list(REMOVE_DUPLICATES MODULE_REQUIRES)
endif()
if(MODULE_PRIV_REQUIRES)
list(REMOVE_DUPLICATES MODULE_PRIV_REQUIRES)
endif()
message(STATUS "==== DEBUG ====")
message(STATUS "MODULE_REQUIRES: ${MODULE_REQUIRES}")
message(STATUS "MODULE_SRCS: ${MODULE_SRCS}")
message(STATUS "===============")
idf_component_register(
SRCS
"main.cc"
# "tests/test.cc"
# "tests/korvo_port/korvo_port_runner.cc"
# "tests/korvo_port/ai_agent.c"
# "tests/korvo_port/audio_proc.c"
# "tests/korvo_port/rtc_proc.c"
# "../managed_components/espressif__zlib/zlib/contrib/minizip/unzip.c"
# "../managed_components/espressif__zlib/zlib/contrib/minizip/ioapi.c"
${MODULE_SRCS}
INCLUDE_DIRS
"."
# "../managed_components/espressif__zlib/zlib/contrib/minizip"
# "05_executors/touch_sensor"
# "05_executors/aw_touch_sensor"
${MODULE_INCLUDE_DIRS}
REQUIRES
# lvgl
# esp_timer
# driver
# esp_driver_i2c
# esp_driver_i2s
# esp_event
# nvs_flash
# spi_flash
# esp_driver_gpio
# spiffs
# vfs
# esp_http_server
# log
# fatfs
# bt
# mbedtls
# audio_pipeline
# audio_hal
# audio_board
# esp_peripherals
# audio_stream
# audio_sal
# esp-adf-libs
# espressif__zlib
${MODULE_REQUIRES}
PRIV_REQUIRES
# esp-qcloud
${MODULE_PRIV_REQUIRES}
EMBED_TXTFILES
${MODULE_EMBED_TXTFILES}
)
# 添加 C++ 标准库支持
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 自动创建SPIFFS分区镜像
# 这会在构建时自动生成audio.bin并在烧录时一并烧录
# spiffs_create_partition_image(audio ../spiffs FLASH_IN_PROJECT)
# 添加编译选项来忽略字符串常量转换警告
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-write-strings)
target_compile_options(${COMPONENT_LIB} PRIVATE -DUSE_FILE32API)
set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h")
# 根据 boards 目录内部模块自动匹配当前选择的板卡
include(${CMAKE_CURRENT_LIST_DIR}/02_physical/boards/module.cmake)
if (CONFIG_VOLC_RTC_MODE)
set(COMPONENT_SRCS ${COMPONENT_SRCS} RtcBotUtils.c)
endif()
target_compile_definitions(${COMPONENT_LIB} PRIVATE IOAPI_NO_64)
+52
View File
@@ -0,0 +1,52 @@
menu "Sweekar Subsystems Configuration"
menu "04 Hal Modules"
config ENABLE_HAL_AUDIO_CODEC
bool "Enable Audio Codec"
default y
help
Enable the audio_codec module in 04_hal.
config ENABLE_HAL_BASE_BOARD_HAL
bool "Enable Base Board Hal"
default y
help
Enable the base_board_hal module in 04_hal.
config ENABLE_HAL_STEPPER_DRIVER
bool "Enable Stepper Driver"
default y
help
Enable the stepper_driver module in 04_hal.
endmenu
menu "05 Executors Modules"
config ENABLE_EXEC_ADC_BATTERY_MONITOR
bool "Enable Adc Battery Monitor"
default y
help
Enable the adc_battery_monitor module in 05_executors.
config ENABLE_EXEC_BASE_BOARD_CONTROL
bool "Enable Base Board Control"
default y
help
Enable the base_board_control module in 05_executors.
config ENABLE_EXEC_GYROSCOPE
bool "Enable Gyroscope"
default y
help
Enable the gyroscope module in 05_executors.
config ENABLE_EXEC_MILLIMETER_WAVE_RADAR
bool "Enable Millimeter Wave Radar"
default y
help
Enable the millimeter_wave_radar module in 05_executors.
config ENABLE_EXEC_PHOTO_INTERRUPTER
bool "Enable Photo Interrupter"
default y
help
Enable the photo_interrupter module in 05_executors.
endmenu
menu "tests Modules"
endmenu
endmenu
+544
View File
@@ -0,0 +1,544 @@
menu "Sweekar Application Configuration"
# ==============================================================
# 模块化配置自动扫描引入点
# 这里会自动加载 main/Kconfig.modules 文件
# ==============================================================
rsource "Kconfig.modules"
choice BOARD_VERSION_SELECTION
prompt "Select Board Version"
default BOARD_VERSION_V1_1_11
help
Select the hardware board version to use.
config BOARD_VERSION_V1_1_11
bool "Board v1.1.11"
help
Use board version v1.1.11
config BOARD_VERSION_V1_1_12
bool "Board v1.1.12"
help
Use board version v1.1.12
config BOARD_VERSION_V1_1_13
bool "Board v1.1.13"
help
Use board version v1.1.13
config BOARD_VERSION_V1_1_14
bool "Board v1.1.14"
help
Use board version v1.1.14
config BOARD_VERSION_V1_1_16
bool "Board v1.1.16"
help
Use board version v1.1.16
config BOARD_VERSION_V1_1_17
bool "Board v1.1.17"
help
Use board version v1.1.17
config BOARD_VERSION_V1_1_18
bool "Board v1.1.18"
help
Use board version v1.1.18
endchoice
choice STEPPER_DRIVER_IMPL
prompt "Stepper Driver Implementation"
default STEPPER_DRIVER_DRV8428
help
Select the stepper motor driver chip.
config STEPPER_DRIVER_DRV8428
bool "TI DRV8428"
help
Use TI DRV8428 stepper motor driver.
config STEPPER_DRIVER_AW8646
bool "Awinic AW8646"
help
Use Awinic AW8646 stepper motor driver.
endchoice
choice LINEAR_MOTOR_TYPE
prompt "Linear Motor Type"
default LINEAR_MOTOR_LD0832
help
Select the linear motor type.
config LINEAR_MOTOR_LD0832
bool "LD0832 (1.8V, 235Hz, 21 ohm)"
help
Use LD0832 linear motor.
config LINEAR_MOTOR_LD0625
bool "LD0625 (0.7V, 260Hz, 8 ohm)"
help
Use LD0625 linear motor.
endchoice
choice AUDIO_DECODER_SUPPORT
prompt "Audio Decoder"
default AUDIO_SUPPORT_OPUS_DECODER
config AUDIO_SUPPORT_G711A_DECODER
bool "AUDIUO-G711-DECODER"
config AUDIO_SUPPORT_OPUS_DECODER
bool "AUDIO-OPUS-DECODER"
config AUDIO_SUPPORT_AAC_DECODER
bool "AUDIO-AAC-DECODER"
endchoice
choice AUDIO_CONVERSATION_MODE_SUPPORT
prompt "Conversion Mode"
default CONTINUOUS_CONVERSATION_MODE
config LANGUAGE_WAKEUP_MODE
select USE_WAKENET
select SR_WN_WN9_HILEXIN
bool "LANGUAGE-WAKEUP-MODE"
config CONTINUOUS_CONVERSATION_MODE
bool "CONTINUOUS-CONVERSATION-MODE"
endchoice
choice RTC_MODE_CHOICE
prompt "rtc mode choice"
default COZE_REQUEST
help
Select rtc request mode
config COZE_REQUEST
bool "enable coze request"
config RTC_TOKEN_REQUEST
bool "enable rtc token request"
endchoice
choice RTC_BACKEND_PROVIDER
prompt "RTC backend provider"
default RTC_BACKEND_VOLC
help
Select the RTC SDK backend used by the AI RTC service.
config RTC_BACKEND_VOLC
bool "VolcEngine RTC (current default)"
config RTC_BACKEND_AGORA
bool "Agora RTC (migration target)"
endchoice
menu "Agora RTC Configuration"
depends on RTC_BACKEND_AGORA
config AGORA_SDK_ENABLED
bool "Agora SDK integrated"
default n
help
Enable this option after adding Agora RTC SDK headers/libs.
config AGORA_APP_ID
string "Agora App ID"
default ""
config AGORA_TOKEN
string "Agora RTC Token"
default ""
config AGORA_CHANNEL
string "Agora Channel Name"
default "default"
config AGORA_UID
int "Agora Local UID"
default 0
endmenu
choice TOUCH_SENSOR_IMPL
prompt "Touch Sensor Implementation"
default TOUCH_SENSOR_IMPL_ESP
config TOUCH_SENSOR_IMPL_ESP
bool "ESP32 Touch Pad"
help
Use ESP32 internal touch sensor (via touch_pad).
config TOUCH_SENSOR_IMPL_AW
bool "Awinic Touch Sensor"
help
Use Awinic AW933xx touch sensor.
endchoice
choice BASE_BOARD_COMM_INTERFACE
prompt "Base Board Communication Interface"
default BASE_BOARD_COMM_UART
help
Select the communication interface for the base board.
config BASE_BOARD_COMM_UART
bool "UART (Legacy)"
help
Use UART for base board communication.
config BASE_BOARD_COMM_I2C
bool "I2C (v1.1.16+)"
help
Use I2C for base board communication.
endchoice
config COZE_URL
string "COZE-URL"
default "http://115.190.92.216:8899/startvoicechat"
depends on COZE_REQUEST
config COZE_AUTHORIZATION
string "COZE-AUTHORIZATION"
default "af78e3067582ac8c04ba20174410bd1"
depends on COZE_REQUEST
config COZE_BOTID
string "COZE-BOTID"
default "ep-20241104163951-sdmjc"
depends on COZE_REQUEST
config RTC_TOKEN
depends on RTC_TOKEN_REQUEST
config APPID
string "appid"
depends on RTC_TOKEN_REQUEST
config ROOMID
string "roomid"
depends on RTC_TOKEN_REQUEST
config UID
string "userid"
depends on RTC_TOKEN_REQUEST
config TOKEN
string "token"
depends on RTC_TOKEN_REQUEST
config ENABLE_RECORDER_DEBUG
bool "enable recorder debug save to sdcard"
default "n"
help
Save the data processed by AEC to the SD card. The saved file name is "rec.pcm", and by default, 30 seconds' worth of data will be saved.
config ENABLE_OTA_UPDATE
bool "Enable OTA Update"
default y
help
Enable or disable OTA
choice OTA_UPDATE_STRATEGY
prompt "OTA Update Strategy"
default OTA_MODE_NOTIFY
depends on ENABLE_OTA_UPDATE
help
Select the OTA update strategy
config OTA_MODE_AUTO
bool "Auto Update (Immediate)"
help
Automatically download and install updates immediately
config OTA_MODE_NOTIFY
bool "Notify Update (Wait for Confirmation)"
help
Wait for user confirmation before starting the OTA update process.
endchoice
endmenu
menu "QCloud COS Download Configuration"
config QCLOUD_COS_QUEUE_SIZE
int "COS download queue size"
default 20
range 1 100
help
Number of pending COS download messages that can be buffered.
config QCLOUD_COS_WORKER_COUNT
int "COS worker thread count"
default 1
range 1 4
help
Number of worker threads for COS download processing.
config QCLOUD_COS_WORKER_STACK_SIZE
int "COS worker stack size (bytes)"
default 16384
range 4096 65536
help
Stack size in bytes for each COS worker thread.
config QCLOUD_COS_HTTP_BUFFER_SIZE
int "COS HTTP download buffer size (bytes)"
default 32768
range 4096 131072
help
Buffer size used by HTTP read and esp_http_client internal buffer.
config QCLOUD_COS_ZIP_UNZIP_BUFFER_SIZE
int "COS ZIP unzip read buffer size (bytes)"
default 32768
range 4096 131072
help
Buffer size used when reading compressed data from ZIP entries.
config QCLOUD_COS_SD_WRITE_BUFFER_SIZE
int "COS SD write buffer size (bytes)"
default 32768
range 4096 131072
help
stdio write buffer size for HTTP download and ZIP extraction output.
endmenu
menu "WiFi Configuration"
config ENABLE_HARDCODED_WIFI
bool "Enable Hardcoded WiFi Credentials"
default n
help
If enabled, the device will first attempt to connect to the specified WiFi network.
If disabled or connection fails, it will fall back to SmartConfig/QCloud provisioning.
config HARDCODED_WIFI_SSID
string "WiFi SSID"
depends on ENABLE_HARDCODED_WIFI
default ""
help
The SSID of the WiFi network to connect to.
Leave empty to fallback to SmartConfig/QCloud provisioning.
config HARDCODED_WIFI_PASSWORD
string "WiFi Password"
depends on ENABLE_HARDCODED_WIFI
default ""
help
The password of the WiFi network to connect to.
endmenu
# Sweekar Demo Selection
menu "Sweekar Demo Selection"
choice SWEEKAR_DEMO_SELECTION
prompt "Select Active Demo"
default SWEEKAR_DEMO_3
help
Select which demo to build and run. Only one demo can be active at a time.
config SWEEKAR_DEMO_2
bool "Demo 2"
config SWEEKAR_DEMO_3
bool "Demo 3 - Basic Pet Game"
config SWEEKAR_DEMO_5
bool "Demo 5"
config SWEEKAR_DEMO_6
bool "Demo 6 - Layered Architecture"
config SWEEKAR_DEMO_7
bool "Demo 7 - Microkernel Architecture"
endchoice
config ACTIVE_DEMO_NAME
string
default "sweekar_demo2" if SWEEKAR_DEMO_2
default "sweekar_demo3" if SWEEKAR_DEMO_3
default "sweekar_demo5" if SWEEKAR_DEMO_5
default "sweekar_demo6" if SWEEKAR_DEMO_6
default "sweekar_demo7" if SWEEKAR_DEMO_7
config SWEEKAR_DEMO6_HATCHING5_TVC
bool "Use TVC version for Hatching 5"
depends on SWEEKAR_DEMO_6
default n
help
If enabled, plays EGG_EMORAISING_HATCHING5_TVC instead of EGG_EMORAISING_HATCHING5_NORMAL during the final hatching stage.
endmenu
menu "ESP QCloud Example Configuration"
menu "Light development board selection "
choice LIGHTBULB_DEMO_DRIVER_SELECT
bool "Demo select"
default LIGHTBULB_DEMO_DRIVER_SELECT_WS2812 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32H2
default LIGHTBULB_DEMO_DRIVER_SELECT_PWM
help
Select the driver type you want to demonstrate
config LIGHTBULB_DEMO_DRIVER_SELECT_SM2135E
bool "Demo SM2135E"
config LIGHTBULB_DEMO_DRIVER_SELECT_WS2812
bool "Demo WS2812"
config LIGHTBULB_DEMO_DRIVER_SELECT_PWM
bool "Demo PWM"
endchoice
config WS2812_LED_GPIO
int "WS2812 LED GPIO"
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32H2
default 18 if IDF_TARGET_ESP32S2
default 48 if IDF_TARGET_ESP32S3
default 4 if IDF_TARGET_ESP32C2
default 18
depends on LIGHTBULB_DEMO_DRIVER_SELECT_WS2812
help
Set the WS2812 RGB LED GPIO.
config WS2812_LED_NUM
int "WS2812 LED NUM"
default 1
depends on LIGHTBULB_DEMO_DRIVER_SELECT_WS2812
help
Set the WS2812 LED NUM.
config PWM_FREQ_HZ
int "PWM Frequency (hz)"
default 4000
range 2000 5000
depends on LIGHTBULB_DEMO_DRIVER_SELECT_PWM
help
Set the PWM Frequency (hz).
config PWM_RED_GPIO
int "PWM Red GPIO"
default 10 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S2
default 15 if IDF_TARGET_ESP32S3
default 0 if IDF_TARGET_ESP32C2
default 23 if IDF_TARGET_ESP32H2
default 25
depends on LIGHTBULB_DEMO_DRIVER_SELECT_PWM
help
Set the PWM Red LED GPIO.
config PWM_GREEN_GPIO
int "PWM Green GPIO"
default 6 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S2
default 16 if IDF_TARGET_ESP32S3
default 1 if IDF_TARGET_ESP32C2
default 24 if IDF_TARGET_ESP32H2
default 26
depends on LIGHTBULB_DEMO_DRIVER_SELECT_PWM
help
Set the PWM Green LED GPIO.
config PWM_BLUE_GPIO
int "PWM Blue GPIO"
default 7 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S2
default 17 if IDF_TARGET_ESP32S3
default 8 if IDF_TARGET_ESP32C2
default 25 if IDF_TARGET_ESP32H2
default 27
depends on LIGHTBULB_DEMO_DRIVER_SELECT_PWM
help
Set the PWM Blue LED GPIO.
config SM2135E_IIC_CLK_GPIO
int "SM2135E IIC CLK GPIO"
default 4
depends on LIGHTBULB_DEMO_DRIVER_SELECT_SM2135E
help
Set the SM2135E CLK GPIO.
config SM2135E_IIC_SDA_GPIO
int "SM2135E IIC SDA GPIO"
default 5
depends on LIGHTBULB_DEMO_DRIVER_SELECT_SM2135E
help
Set the SM2135E SDA GPIO.
endmenu
menu "Light provisioning network selection"
config LIGHT_PROVISIONING_SMARTCONFIG
bool "Enable SmartConfig's smart network configuration method"
default n
help
Enable SmartConfig's smart network configuration method
config LIGHT_PROVISIONING_SOFTAPCONFIG
bool "Enable SoftAP's network configuration method"
default y
help
"Enable SoftAP's network configuration method"
config LIGHT_PROVISIONING_BLECONFIG
depends on QCLOUD_ENABLE_BLECONFIG
bool "Enable BLE's network configuration method"
default y
help
"Enable BLE's network configuration method"
config LIGHT_PROVISIONING_SOFTAPCONFIG_SSID
depends on LIGHT_PROVISIONING_SOFTAPCONFIG
string "SoftAP ssid"
default "tcloud_XXX"
help
SSID (network name) for the phone to connect to.
config LIGHT_PROVISIONING_BLECONFIG_NAME
depends on LIGHT_PROVISIONING_BLECONFIG
string "BLE name"
default "Qcloud"
help
BLE for the phone to connect to.
endmenu
config LIGHT_REBOOT_UNBROKEN_COUNT_RESET
int "More than this number of continuous uninterrupted restarts triggers a reset of the device"
range 3 15
default 5
help
Devices without buttons trigger a device reset through continuous restart.
config LIGHT_DEBUG
bool "The device will be in debug mode"
default n
help
Debug mode device performance is only used during development.
choice QCLOUD_WIFI_CONFIG_METHOD
prompt "WiFi Provisioning Method"
default QCLOUD_WIFI_SMARTCONFIG
help
Select how the device should connect to WiFi.
config QCLOUD_WIFI_PROVISIONING
bool "WeChat Mini-Program (connectWiFi)"
config QCLOUD_WIFI_SMARTCONFIG
bool "ESP-Touch / SmartConfig (connectWiFi_smartconfig)"
endchoice
endmenu
+124
View File
@@ -0,0 +1,124 @@
# Sweekar-ESP32 模块化开发与构建系统指南
为了支持多人无冲突协作,并保持系统代码的高内聚、低耦合,Sweekar-ESP32 的 CMake 构建系统采用 **“透明化脚手架 (Transparent Scaffolding) + Kconfig 自动组装”** 架构。
在这套架构下,开发者在添加新的底层硬件驱动、业务服务或测试代码时,**完全不需要修改任何顶层文件(包括 `main/CMakeLists.txt` 和 `main/Kconfig.projbuild`)**,构建系统会自动帮你生成必要的模板文件,而后续的修改则完全交由你掌控,没有任何“黑魔法”。
以下是添加新模块的标准流程和机制说明。
---
## 1. 确定模块归属的目录分类
系统顶层会自动扫描以下目录下的子文件夹:
- `04_hal/` : 硬件抽象层代码(如 Codec 驱动、马达驱动等)
- `05_executors/` : 执行器组件(如 AudioPlayer, Display, 传感器读取等)
- `05_service/` : 后台服务组件(如 AI RTC、QCloud、WiFi 连接等)
- `06_behaviors/` : 行为逻辑组件
- `tests/` : 各种测试模块(如麦克风测试、扬声器测试等)
- `sweekar_demos/` : 各种组装好的 Demo 入口
*假设我们要添加一个专门测试麦克风的模块,名为 `test_mic`,它显然应该归属于 `tests/` 目录。*
## 2. 新建模块与文件 (自动化部分)
在对应的分类下新建文件夹,并将你的源码和头文件放进去:
```bash
mkdir -p main/tests/test_mic
touch main/tests/test_mic/test_mic.cc
touch main/tests/test_mic/test_mic.h
```
**【核心自动化机制】**
此时,你**不需要**手动去写 CMake 文件。你只需要在终端执行一次:
```bash
idf.py reconfigure
```
(或者 `idf.py build` 如果这是你第一次编译该工程)。
构建系统中的 Python 脚本 (`generate_kconfig_modules.py`) 会在配置阶段自动扫描目录。当它发现 `tests/test_mic` 目录下有源码但**没有** `module.cmake` 时,它会**自动为你生成一个 `module.cmake` 脚手架文件**。
自动生成的文件内容类似于:
```cmake
# main/tests/test_mic/module.cmake
# 自动生成的模块脚手架,可直接手工修改。
# 如果后续添加了新文件,可以手工在此处添加,或者删除本文件让脚本重新生成。
if(NOT CONFIG_ENABLE_TEST_TEST_MIC AND NOT EARLY_EXPANSION)
message(STATUS " [Skip] test_mic module disabled by menuconfig.")
return()
endif()
list(APPEND MODULE_SRCS
"tests/test_mic/test_mic.cc"
)
list(APPEND MODULE_INCLUDE_DIRS
"tests/test_mic"
)
# 需要公开依赖时,在这里手工补充。例如:
# list(APPEND MODULE_REQUIRES
# esp_timer
# driver
# )
```
## 3. 后续维护与修改 (手工部分)
**“透明化”原则**:一旦 `module.cmake` 被生成,Python 脚本就**再也不会覆盖或修改它**。它现在完全属于你。
### 3.1 当你新增/删除源文件时
你有两种选择:
1. **手动修改(推荐)**:直接打开 `module.cmake`,在 `list(APPEND MODULE_SRCS ...)` 中手动添加或删除文件路径。所见即所得,非常直观。
2. **重新生成(偷懒大法)**:如果你一口气加了十几个文件,不想手写路径,你可以**直接删除**这个 `module.cmake` 文件,然后运行 `idf.py reconfigure`,脚本会重新帮你扫描并生成一份包含所有最新文件的脚手架。
### 3.2 当你需要依赖其他库时
直接在 `module.cmake` 底部,取消注释并填入你需要的 ESP-IDF 组件名(如 `esp_adc`, `audio_pipeline` 等):
```cmake
list(APPEND MODULE_REQUIRES
audio_pipeline
driver
)
```
## 4. 编写模块内部的 `Kconfig` (可选)
系统会自动为你的模块生成一个默认的配置开关(例如 `CONFIG_ENABLE_TEST_TEST_MIC`)。
- `tests/` 目录下的默认开关为关闭(`default n`)。
- 其他目录默认开启(`default y`)。
如果你需要为你的模块定义更复杂的配置选项(比如定义默认开启、依赖其他模块、或者包含子菜单),你可以在模块目录(`main/tests/test_mic/`)下建立一个名为 `Kconfig` 的文件:
```kconfig
# main/tests/test_mic/Kconfig
config ENABLE_TEST_MIC
bool "Enable Microphone Test Module"
default n
help
Enable this to compile the microphone testing routines.
```
> 系统如果扫描到模块自带 `Kconfig`,就会使用你写的,而不再自动生成默认开关。
## 5. 编译与配置
1. 运行 `idf.py menuconfig`。
2. 导航到 `Sweekar Subsystems Configuration` -> 对应的目录(如 `Tests Modules`)。
3. 找到你刚刚添加的 `Enable test_mic`(或你自己定义的开关名字),按空格键勾选 `[*]`。
4. 保存退出后,运行 `idf.py build` 即可!
---
## ⚠️ 常见问题与注意事项
### Q: 为什么我新建了一个文件夹或者删除了 `module.cmake`,运行 `idf.py build` 没有反应?
这是 CMake 的缓存机制导致的。如果你只是增删了目录或文件,但没有修改任何现有的 `CMakeLists.txt` 内容,CMake 会跳过配置阶段直接去编译,导致 Python 扫描脚本没有运行。
**解决办法**:遇到这种情况,请明确告诉 IDF 重新配置:
```bash
idf.py reconfigure
```
然后再运行 `idf.py build`。
### Q: 为什么要加 `NOT EARLY_EXPANSION`?
ESP-IDF 的构建系统分为两步:
1. **依赖解析阶段 (Early Expansion)**:此时 CMake 快速扫描所有组件收集 `REQUIRES`,但此时 `menuconfig` 的配置尚未加载,所有的 `CONFIG_xxx` 都是空值!
2. **实际构建阶段 (Build Pass)**:此时配置加载完毕,开始真实编译。
如果在 `module.cmake` 顶部仅仅写 `if(NOT CONFIG_ENABLE_XXX) return()`,会导致在依赖解析阶段该模块就被错误地跳过了,从而丢失其依赖声明,引发“找不到头文件”的编译灾难。
加入 `AND NOT EARLY_EXPANSION` 后,能保证所有启用的组件依赖都被正确收集到构建树中,实现完美解耦。
+247
View File
@@ -0,0 +1,247 @@
import os
import re
# The directories to scan for Kconfig files
SUBSYSTEM_DIRS = ["04_hal", "05_executors", "05_service", "06_behaviors", "tests"]
TEMPLATE_SCAN_DIRS = SUBSYSTEM_DIRS + ["sweekar_demos"]
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
OUTPUT_FILE = os.path.join(BASE_DIR, "Kconfig.modules")
SOURCE_EXTENSIONS = (".c", ".cc", ".cpp")
HEADER_EXTENSIONS = (".h", ".hpp")
SKIP_DIR_NAMES = {"build", "__pycache__"}
def iter_child_dirs(parent_dir):
if not os.path.exists(parent_dir):
return []
return sorted(
[
name
for name in os.listdir(parent_dir)
if os.path.isdir(os.path.join(parent_dir, name)) and not name.startswith(".")
]
)
def compute_default_config_name(subsystem_dir, module_name):
if subsystem_dir == "tests":
prefix = "TEST"
else:
prefix = subsystem_dir.split("_")[1].upper()
if prefix == "EXECUTORS":
prefix = "EXEC"
elif prefix == "BEHAVIORS":
prefix = "BEHAV"
elif prefix == "SERVICE":
prefix = "SVC"
return f"CONFIG_ENABLE_{prefix}_{module_name.upper()}"
def contains_module_files(module_dir):
for _, _, files in os.walk(module_dir):
for filename in files:
if filename.endswith(SOURCE_EXTENSIONS) or filename.endswith(HEADER_EXTENSIONS):
return True
return False
def collect_existing_module_cmake_files():
module_files = []
for root, _, files in os.walk(BASE_DIR):
if "module.cmake" in files:
module_files.append(os.path.join(root, "module.cmake"))
return sorted(module_files)
def collect_module_files(module_dir):
srcs = []
include_dirs = {"."}
for root, dirs, files in os.walk(module_dir):
dirs[:] = [
d for d in dirs
if d not in SKIP_DIR_NAMES and not d.startswith(".")
]
rel_root = os.path.relpath(root, module_dir).replace(os.sep, "/")
rel_root = "." if rel_root == "." else rel_root
has_header = False
for filename in files:
if filename.endswith(SOURCE_EXTENSIONS):
rel_path = os.path.normpath(os.path.join(rel_root, filename)).replace(os.sep, "/")
if rel_path.startswith("./"):
rel_path = rel_path[2:]
srcs.append(rel_path)
elif filename.endswith(HEADER_EXTENSIONS):
has_header = True
if has_header:
include_dirs.add(rel_root)
srcs = sorted(set(srcs))
include_dirs = sorted(set(include_dirs))
return srcs, include_dirs
def is_already_aggregated(rel_module_dir, existing_module_files):
rel_module_prefix = f'"{rel_module_dir}/'
for module_file in existing_module_files:
try:
with open(module_file, "r", encoding="utf-8") as f:
content = f.read()
except OSError:
continue
if rel_module_prefix in content:
return True
return False
def render_module_template(subsystem_dir, module_name, module_dir):
srcs, include_dirs = collect_module_files(module_dir)
rel_module_dir = os.path.relpath(module_dir, BASE_DIR).replace(os.sep, "/")
lines = [
f"# main/{subsystem_dir}/{module_name}/module.cmake",
"# 自动生成的模块脚手架,可直接手工修改。",
"# 如果后续添加了新文件,可以手工在此处添加,或者删除本文件让脚本重新生成。",
"",
]
if subsystem_dir != "sweekar_demos":
config_name = compute_default_config_name(subsystem_dir, module_name)
lines.extend(
[
f"if(NOT {config_name} AND NOT EARLY_EXPANSION)",
f' message(STATUS " [Skip] {module_name} module disabled by menuconfig.")',
" return()",
"endif()",
"",
]
)
lines.append("list(APPEND MODULE_SRCS")
for src in srcs:
lines.append(f' "{rel_module_dir}/{src}"')
lines.append(")")
lines.append("")
lines.append("list(APPEND MODULE_INCLUDE_DIRS")
for inc in include_dirs:
if inc == ".":
lines.append(f' "{rel_module_dir}"')
else:
lines.append(f' "{rel_module_dir}/{inc}"')
lines.append(")")
lines.append("")
lines.extend(
[
"# 需要公开依赖时,在这里手工补充。例如:",
"# list(APPEND MODULE_REQUIRES",
"# esp_timer",
"# driver",
"# )",
"",
"# 需要私有依赖时,在这里手工补充。例如:",
"# list(APPEND MODULE_PRIV_REQUIRES",
"# nvs_flash",
"# )",
"",
]
)
return "\n".join(lines)
def ensure_module_templates():
created_files = []
existing_module_files = collect_existing_module_cmake_files()
for subsystem_dir in TEMPLATE_SCAN_DIRS:
dir_path = os.path.join(BASE_DIR, subsystem_dir)
if not os.path.exists(dir_path):
continue
for module_name in iter_child_dirs(dir_path):
if subsystem_dir == "sweekar_demos" and not module_name.startswith("sweekar_demo"):
continue
module_dir = os.path.join(dir_path, module_name)
module_cmake = os.path.join(module_dir, "module.cmake")
if os.path.exists(module_cmake):
continue
if not contains_module_files(module_dir):
continue
rel_module_dir = os.path.relpath(module_dir, BASE_DIR).replace(os.sep, "/")
if subsystem_dir != "sweekar_demos" and is_already_aggregated(rel_module_dir, existing_module_files):
continue
template_content = render_module_template(subsystem_dir, module_name, module_dir)
with open(module_cmake, "w", encoding="utf-8") as f:
f.write(template_content)
created_files.append(module_cmake)
return created_files
def generate_kconfig_modules():
created_templates = ensure_module_templates()
kconfig_content = 'menu "Sweekar Subsystems Configuration"\n\n'
for d in SUBSYSTEM_DIRS:
dir_path = os.path.join(BASE_DIR, d)
if not os.path.exists(dir_path):
continue
# Create a pretty menu name from the directory name
# e.g., "05_executors" -> "05 Executors Modules"
parts = d.split('_')
menu_name = parts[0] + " " + " ".join([p.capitalize() for p in parts[1:]]) + " Modules"
kconfig_content += f' menu "{menu_name}"\n'
# Sort subdirectories to ensure consistent ordering
subdirs = iter_child_dirs(dir_path)
for module_name in subdirs:
module_dir = os.path.join(dir_path, module_name)
kconfig_file = os.path.join(module_dir, "Kconfig")
if os.path.exists(kconfig_file):
# If the module has its own Kconfig, just source it
# Make sure the source path is relative to the project root or uses proper env vars
# Since Kconfig.projbuild is processed relative to project root usually
rel_path = os.path.join("main", d, module_name, "Kconfig")
kconfig_content += f' source "{rel_path}"\n'
else:
# If no Kconfig exists, generate a default toggle for it
config_name = compute_default_config_name(d, module_name)
pretty_name = " ".join([word.capitalize() for word in module_name.split('_')])
# Default to 'n' for tests, 'y' for everything else
default_val = 'n' if d == 'tests' else 'y'
kconfig_content += f''' config {config_name.replace("CONFIG_", "")}
bool "Enable {pretty_name}"
default {default_val}
help
Enable the {module_name} module in {d}.
'''
kconfig_content += ' endmenu\n\n'
kconfig_content += 'endmenu\n'
with open(OUTPUT_FILE, 'w') as f:
f.write(kconfig_content)
print(f"Generated {OUTPUT_FILE} successfully.")
if created_templates:
print("Generated module.cmake templates:")
for path in created_templates:
print(f" - {path}")
if __name__ == "__main__":
generate_kconfig_modules()
+19
View File
@@ -0,0 +1,19 @@
## IDF Component Manager Manifest File
dependencies:
## Required IDF version
idf:
version: '>=5.0.0'
# lvgl/lvgl: ^8.*
# 添加button组件依赖
# espressif/button: ~4.1.3
# 78/esp-wifi-connect: ~2.4.3
espressif/adc_battery_estimation: ^0.2.0
# lightbulb_driver: ^0.0.9
# espressif/touch_slider_sensor: ^0.2.0
# espressif/touch_button_sensor: ^0.2.0
+5
View File
@@ -0,0 +1,5 @@
extern "C" void app_main() {
}