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

@@ -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