forked from xiaozhi/xiaozhi-esp32
添加 jiuchuan-s3 开发板支持 (#775)
* 添加 jiuchuang-s3 开发板支持 * 增加编译指南 * 优开发板支持包文件目录,更新README.md 删除了多余板载文件 [* ]README.md -> 更新了编译指南和烧录指南 * 修改板级支持包名 * 使用乐鑫提供的电源监测 * 修复部分代码格式问题 * 解决合并冲突 * 解决部分合并内容 * 完善合并内容 * 修复电量映射表错误 --------- Co-authored-by: unknown <jake12355>
This commit is contained in:
25
main/boards/jiuchuan-s3/README.md
Normal file
25
main/boards/jiuchuan-s3/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# jiuchuan-xiaozhi-sound
|
||||
九川科技小智AI音箱
|
||||
|
||||
## 🛠️ 编译指南
|
||||
**开发环境**:ESP-IDF v5.4.1
|
||||
|
||||
### 编译步骤:
|
||||
> ⚠️ **提示**:若在编译过程中访问在线库失败,可以尝试切换加速器状态,或修改 [idf_component.yml] 文件,替换为国内镜像源。
|
||||
|
||||
1. 使用 VSCode 打开项目文件夹;
|
||||
2. 清除工程(Clean Project);
|
||||
3. 设置 ESP-IDF 版本为 `v5.4.1`;
|
||||
4. 点击 VSCode 右下角提示,生成 [compile_commands.json] 文件;
|
||||
5. 设置目标设备为 `[esp32s3] -> [JTAG]`;
|
||||
6. 打开 **SDK Configuration Editor**;
|
||||
7. 配置自定义分区表路径为:`partitions/v1/16m.csv`;
|
||||
8. 设置 **Board Type** 为 **九川科技**;
|
||||
9. 保存配置并开始编译。
|
||||
|
||||
## 🔌 烧录步骤
|
||||
1. 使用数据线连接电脑与音箱;
|
||||
2. 关闭设备电源后,长按电源键不松手;
|
||||
3. 在烧录工具中选择对应的串口(COM Port);
|
||||
4. 点击烧录按钮,选择 UART 模式;
|
||||
5. 烧录完成前请勿松开电源键。
|
||||
52
main/boards/jiuchuan-s3/config.h
Normal file
52
main/boards/jiuchuan-s3/config.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
#define AUDIO_INPUT_REFERENCE true
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_38
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_13
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_14
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_11
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_12
|
||||
|
||||
#define AUDIO_CODEC_PA_PIN GPIO_NUM_42
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_1
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_2
|
||||
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_10
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define PWR_BUTTON_GPIO GPIO_NUM_3
|
||||
#define PWR_EN_GPIO GPIO_NUM_5
|
||||
#define PWR_ADC_GPIO GPIO_NUM_4
|
||||
#define PWR_BUTTON_TIME 3000000U
|
||||
|
||||
#define WIFI_BUTTON_GPIO GPIO_NUM_6
|
||||
#define CMD_BUTTON_GPIO GPIO_NUM_7
|
||||
|
||||
|
||||
#define DISPLAY_SPI_SCK_PIN GPIO_NUM_41
|
||||
#define DISPLAY_SPI_MOSI_PIN GPIO_NUM_40
|
||||
#define DISPLAY_DC_PIN GPIO_NUM_39
|
||||
#define DISPLAY_SPI_CS_PIN GPIO_NUM_9
|
||||
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_46
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
9
main/boards/jiuchuan-s3/config.json
Normal file
9
main/boards/jiuchuan-s3/config.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "jiuchuan-s3",
|
||||
"sdkconfig_append": []
|
||||
}
|
||||
]
|
||||
}
|
||||
384
main/boards/jiuchuan-s3/esp_lcd_panel_gc9301.c
Normal file
384
main/boards/jiuchuan-s3/esp_lcd_panel_gc9301.c
Normal file
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#include <string.h>
|
||||
#if CONFIG_LCD_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_commands.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_compiler.h"
|
||||
/* GC9309NA LCD controller driver for ESP-IDF
|
||||
* SPDX-FileCopyrightText: 2024 Your Name
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
|
||||
// GC9309NA Command Set
|
||||
#define GC9309NA_CMD_SLPIN 0x10
|
||||
#define GC9309NA_CMD_SLPOUT 0x11
|
||||
#define GC9309NA_CMD_INVOFF 0x20
|
||||
#define GC9309NA_CMD_INVON 0x21
|
||||
#define GC9309NA_CMD_DISPOFF 0x28
|
||||
#define GC9309NA_CMD_DISPON 0x29
|
||||
#define GC9309NA_CMD_CASET 0x2A
|
||||
#define GC9309NA_CMD_RASET 0x2B
|
||||
#define GC9309NA_CMD_RAMWR 0x2C
|
||||
#define GC9309NA_CMD_MADCTL 0x36
|
||||
#define GC9309NA_CMD_COLMOD 0x3A
|
||||
#define GC9309NA_CMD_TEOFF 0x34
|
||||
#define GC9309NA_CMD_TEON 0x35
|
||||
#define GC9309NA_CMD_WRDISBV 0x51
|
||||
#define GC9309NA_CMD_WRCTRLD 0x53
|
||||
|
||||
// Manufacturer Commands
|
||||
#define GC9309NA_CMD_SETGAMMA1 0xF0
|
||||
#define GC9309NA_CMD_SETGAMMA2 0xF1
|
||||
#define GC9309NA_CMD_PWRCTRL1 0x67
|
||||
#define GC9309NA_CMD_PWRCTRL2 0x68
|
||||
#define GC9309NA_CMD_PWRCTRL3 0x66
|
||||
#define GC9309NA_CMD_PWRCTRL4 0xCA
|
||||
#define GC9309NA_CMD_PWRCTRL5 0xCB
|
||||
#define GC9309NA_CMD_DINVCTRL 0xB5
|
||||
#define GC9309NA_CMD_REG_ENABLE1 0xFE
|
||||
#define GC9309NA_CMD_REG_ENABLE2 0xEF
|
||||
|
||||
// 自检模式颜色定义
|
||||
|
||||
|
||||
static const char *TAG = "lcd_panel.gc9309na";
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_t base;
|
||||
esp_lcd_panel_io_handle_t io;
|
||||
int reset_gpio_num;
|
||||
bool reset_level;
|
||||
int x_gap;
|
||||
int y_gap;
|
||||
uint8_t madctl_val;
|
||||
uint8_t colmod_val;
|
||||
uint16_t te_scanline;
|
||||
uint8_t fb_bits_per_pixel;
|
||||
} gc9309na_panel_t;
|
||||
|
||||
static esp_err_t panel_gc9309na_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_gc9309na_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_gc9309na_init(esp_lcd_panel_t *panel);
|
||||
static esp_err_t panel_gc9309na_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
|
||||
static esp_err_t panel_gc9309na_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||
static esp_err_t panel_gc9309na_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||
static esp_err_t panel_gc9309na_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
|
||||
static esp_err_t panel_gc9309na_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
|
||||
static esp_err_t panel_gc9309na_disp_on_off(esp_lcd_panel_t *panel, bool off);
|
||||
static esp_err_t panel_gc9309na_sleep(esp_lcd_panel_t *panel, bool sleep);
|
||||
|
||||
|
||||
esp_err_t esp_lcd_new_panel_gc9309na(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
gc9309na_panel_t *gc9309 = NULL;
|
||||
|
||||
ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid arg");
|
||||
|
||||
gc9309 = calloc(1, sizeof(gc9309na_panel_t));
|
||||
ESP_GOTO_ON_FALSE(gc9309, ESP_ERR_NO_MEM, err, TAG, "no mem");
|
||||
|
||||
|
||||
// Hardware reset GPIO config
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "GPIO config failed");
|
||||
}
|
||||
|
||||
gc9309->colmod_val = 0x55; // RGB565
|
||||
// Initial register values
|
||||
|
||||
gc9309->fb_bits_per_pixel = 16;
|
||||
gc9309->io = io;
|
||||
gc9309->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||
gc9309->reset_level = panel_dev_config->flags.reset_active_high;
|
||||
gc9309->x_gap = 0;
|
||||
gc9309->y_gap = 0;
|
||||
|
||||
// Function pointers
|
||||
gc9309->base.del = panel_gc9309na_del;
|
||||
gc9309->base.reset = panel_gc9309na_reset;
|
||||
gc9309->base.init = panel_gc9309na_init;
|
||||
gc9309->base.draw_bitmap = panel_gc9309na_draw_bitmap;
|
||||
gc9309->base.invert_color = panel_gc9309na_invert_color;
|
||||
gc9309->base.set_gap = panel_gc9309na_set_gap;
|
||||
gc9309->base.mirror = panel_gc9309na_mirror;
|
||||
gc9309->base.swap_xy = panel_gc9309na_swap_xy;
|
||||
gc9309->base.disp_on_off = panel_gc9309na_disp_on_off;
|
||||
gc9309->base.disp_sleep = panel_gc9309na_sleep;
|
||||
|
||||
*ret_panel = &(gc9309->base);
|
||||
ESP_LOGI(TAG, "New GC9309NA panel @%p", gc9309);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (gc9309) {
|
||||
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||
}
|
||||
free(gc9309);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_del(esp_lcd_panel_t *panel)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
|
||||
if (gc9309->reset_gpio_num >= 0) {
|
||||
gpio_reset_pin(gc9309->reset_gpio_num);
|
||||
}
|
||||
free(gc9309);
|
||||
ESP_LOGI(TAG, "Del GC9309NA panel");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_reset(esp_lcd_panel_t *panel)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
|
||||
if (gc9309->reset_gpio_num >= 0) {
|
||||
// Hardware reset
|
||||
gpio_set_level(gc9309->reset_gpio_num, gc9309->reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
gpio_set_level(gc9309->reset_gpio_num, !gc9309->reset_level);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
} else {
|
||||
// Software reset
|
||||
// uint8_t unlock_cmd[] = {GC9309NA_CMD_REG_ENABLE1, GC9309NA_CMD_REG_ENABLE2};
|
||||
// ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(gc9309->io, 0xFE, unlock_cmd, 2),
|
||||
// TAG, "Unlock failed");
|
||||
// ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(gc9309->io, LCD_CMD_SWRESET, NULL, 0),
|
||||
// TAG, "SW Reset failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
static esp_err_t panel_gc9309na_init(esp_lcd_panel_t *panel)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = gc9309->io;
|
||||
|
||||
// Unlock commands
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xFE, NULL, 0), TAG, "Unlock cmd1 failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xEF, NULL, 0), TAG, "Unlock cmd2 failed");
|
||||
|
||||
// Sleep out command
|
||||
//ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x11, NULL, 0), TAG, "Sleep out failed");
|
||||
//vTaskDelay(pdMS_TO_TICKS(80));
|
||||
|
||||
// Timing control commands
|
||||
//ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xE8, (uint8_t[]){0xA0}, 1), TAG, "Timing control failed");
|
||||
//ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xE8, (uint8_t[]){0xF0}, 1), TAG, "Timing control failed");
|
||||
|
||||
// Display on command
|
||||
//ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x29, NULL, 0), TAG, "Display on failed");
|
||||
// vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x80, (uint8_t[]){0xC0}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x81, (uint8_t[]){0x01}, 1), TAG, "DINV failed");
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x82, (uint8_t[]){0x07}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x83, (uint8_t[]){0x38}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x88, (uint8_t[]){0x64}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x89, (uint8_t[]){0x86}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x8B, (uint8_t[]){0x3C}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x8D, (uint8_t[]){0x51}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x8E, (uint8_t[]){0x70}, 1), TAG, "DINV failed");
|
||||
|
||||
//高低位交换
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xB4, (uint8_t[]){0x80}, 1), TAG, "DINV failed");
|
||||
|
||||
gc9309->colmod_val = 0x05; // RGB565
|
||||
gc9309->madctl_val = 0x48; // BGR顺序,设置bit3=1(即0x08)
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, GC9309NA_CMD_COLMOD, &gc9309->colmod_val, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, GC9309NA_CMD_MADCTL, &gc9309->madctl_val, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0XBF, (uint8_t[]){0X1F}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x7d, (uint8_t[]){0x45,0x06}, 2), TAG, "DINV failed");
|
||||
// Continue from where you left off
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xEE, (uint8_t[]){0x00,0x06}, 2), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0XF4, (uint8_t[]){0x53}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xF6, (uint8_t[]){0x17,0x08}, 2), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x70, (uint8_t[]){0x4F,0x4F}, 2), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x71, (uint8_t[]){0x12,0x20}, 2), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x72, (uint8_t[]){0x12,0x20}, 2), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xB5, (uint8_t[]){0x50}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xBA, (uint8_t[]){0x00}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xEC, (uint8_t[]){0x71}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x7b, (uint8_t[]){0x00,0x0d}, 2), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x7c, (uint8_t[]){0x0d,0x03}, 2), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0XF5, (uint8_t[]){0x02,0x10,0x12}, 3), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xF0, (uint8_t[]){0x0C,0x11,0x0b,0x0a,0x05,0x32,0x44,0x8e,0x9a,0x29,0x2E,0x5f}, 12), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xF1, (uint8_t[]){0x0B,0x11,0x0b,0x07,0x07,0x32,0x45,0xBd,0x8D,0x21,0x28,0xAf}, 12), TAG, "DINV failed");
|
||||
|
||||
// 240x296 resolution settings
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x2a, (uint8_t[]){0x00,0x00,0x00,0xef}, 4), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x2b, (uint8_t[]){0x00,0x00,0x01,0x27}, 4), TAG, "DINV failed");
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x66, (uint8_t[]){0x2C}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x67, (uint8_t[]){0x18}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x68, (uint8_t[]){0x3E}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xCA, (uint8_t[]){0x0E}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xe8, (uint8_t[]){0xf0}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xCB, (uint8_t[]){0x06}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xB6, (uint8_t[]){0x5C,0x40,0x40}, 3), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xCC, (uint8_t[]){0x33}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xCD, (uint8_t[]){0x33}, 1), TAG, "DINV failed");
|
||||
|
||||
// Sleep out command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x11, NULL, 0), TAG, "Sleep out failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(80));
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xe8, (uint8_t[]){0xA0}, 1), TAG, "DINV failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xe8, (uint8_t[]){0xf0}, 1), TAG, "DINV failed");
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xfe, NULL, 0), TAG, "unlock cmd1 failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0xee, NULL, 0), TAG, "unlock cmd2 failed");
|
||||
|
||||
// Display on command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x29, NULL, 0), TAG, "Display on failed");
|
||||
|
||||
// Memory write command
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, 0x2c, NULL, 0), TAG, "Memory write failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t panel_gc9309na_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
|
||||
|
||||
esp_lcd_panel_io_handle_t io = gc9309->io;
|
||||
|
||||
x_start += gc9309->x_gap;
|
||||
x_end += gc9309->x_gap;
|
||||
y_start += gc9309->y_gap;
|
||||
y_end += gc9309->y_gap;
|
||||
|
||||
// define an area of frame memory where MCU can access
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) {
|
||||
(x_start >> 8) & 0xFF,
|
||||
x_start & 0xFF,
|
||||
((x_end - 1) >> 8) & 0xFF,
|
||||
(x_end - 1) & 0xFF,
|
||||
}, 4), TAG, "io tx param failed");
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) {
|
||||
(y_start >> 8) & 0xFF,
|
||||
y_start & 0xFF,
|
||||
((y_end - 1) >> 8) & 0xFF,
|
||||
(y_end - 1) & 0xFF,
|
||||
}, 4), TAG, "io tx param failed");
|
||||
// transfer frame buffer
|
||||
size_t len = (x_end - x_start) * (y_end - y_start) * gc9309->fb_bits_per_pixel / 8;
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len), TAG, "io tx color failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
esp_lcd_panel_io_handle_t io = gc9309->io;
|
||||
int command = 0;
|
||||
if (invert_color_data) {
|
||||
command = LCD_CMD_INVON;
|
||||
} else {
|
||||
command = LCD_CMD_INVOFF;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG,
|
||||
"io tx param failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
// gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
// esp_lcd_panel_io_handle_t io = gc9309->io;
|
||||
// if (mirror_x) {
|
||||
// gc9309->madctl_val |= LCD_CMD_MX_BIT;
|
||||
// } else {
|
||||
// gc9309->madctl_val &= ~LCD_CMD_MX_BIT;
|
||||
// }
|
||||
// if (mirror_y) {
|
||||
// gc9309->madctl_val |= LCD_CMD_MY_BIT;
|
||||
// } else {
|
||||
// gc9309->madctl_val &= ~LCD_CMD_MY_BIT;
|
||||
// }
|
||||
// ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
// gc9309->madctl_val
|
||||
// }, 1), TAG, "io tx param failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
|
||||
{
|
||||
// gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
// esp_lcd_panel_io_handle_t io = gc9309->io;
|
||||
// if (swap_axes) {
|
||||
// gc9309->madctl_val |= LCD_CMD_MV_BIT;
|
||||
// } else {
|
||||
// gc9309->madctl_val &= ~LCD_CMD_MV_BIT;
|
||||
// }
|
||||
// ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
||||
// gc9309->madctl_val
|
||||
// }, 1), TAG, "io tx param failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
gc9309->x_gap = x_gap;
|
||||
gc9309->y_gap = y_gap;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
uint8_t cmd = on_off ? GC9309NA_CMD_DISPON : GC9309NA_CMD_DISPOFF;
|
||||
return esp_lcd_panel_io_tx_param(gc9309->io, cmd, NULL, 0);
|
||||
}
|
||||
|
||||
static esp_err_t panel_gc9309na_sleep(esp_lcd_panel_t *panel, bool sleep)
|
||||
{
|
||||
gc9309na_panel_t *gc9309 = __containerof(panel, gc9309na_panel_t, base);
|
||||
uint8_t cmd = sleep ? GC9309NA_CMD_SLPIN : GC9309NA_CMD_SLPOUT;
|
||||
esp_err_t ret = esp_lcd_panel_io_tx_param(gc9309->io, cmd, NULL, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
return ret;
|
||||
}
|
||||
31
main/boards/jiuchuan-s3/esp_lcd_panel_gc9301.h
Normal file
31
main/boards/jiuchuan-s3/esp_lcd_panel_gc9301.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_panel_dev.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create LCD panel for model ST7789
|
||||
*
|
||||
* @param[in] io LCD panel IO handle
|
||||
* @param[in] panel_dev_config general panel device configuration
|
||||
* @param[out] ret_panel Returned LCD panel handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_NO_MEM if out of memory
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_lcd_new_panel_gc9309na(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
62
main/boards/jiuchuan-s3/gpio_manager.h
Normal file
62
main/boards/jiuchuan-s3/gpio_manager.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <mutex>
|
||||
#include <inttypes.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
class GpioManager {
|
||||
public:
|
||||
enum class GpioMode {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
INPUT_PULLUP,
|
||||
INPUT_PULLDOWN
|
||||
};
|
||||
|
||||
static void SetLevel(gpio_num_t gpio, uint32_t level) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
ESP_ERROR_CHECK(gpio_set_level(gpio, level));
|
||||
ESP_LOGD("GpioManager", "Set GPIO %d level: %d", static_cast<int>(gpio), static_cast<int>(level));
|
||||
}
|
||||
|
||||
static int GetLevel(gpio_num_t gpio) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
int level = gpio_get_level(gpio);
|
||||
ESP_LOGD("GpioManager", "Get GPIO %d level: %d", static_cast<int>(gpio), level);
|
||||
return level;
|
||||
}
|
||||
|
||||
static void Config(gpio_num_t gpio, GpioMode mode) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
gpio_config_t config = {};
|
||||
config.pin_bit_mask = (1ULL << gpio);
|
||||
|
||||
switch(mode) {
|
||||
case GpioMode::INPUT:
|
||||
config.mode = GPIO_MODE_INPUT;
|
||||
config.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
config.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
break;
|
||||
case GpioMode::OUTPUT:
|
||||
config.mode = GPIO_MODE_OUTPUT;
|
||||
break;
|
||||
case GpioMode::INPUT_PULLUP:
|
||||
config.mode = GPIO_MODE_INPUT;
|
||||
config.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
break;
|
||||
case GpioMode::INPUT_PULLDOWN:
|
||||
config.mode = GPIO_MODE_INPUT;
|
||||
config.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(gpio_config(&config));
|
||||
ESP_LOGI("GpioManager", "Configured GPIO %d mode: %d", gpio, static_cast<int>(mode));
|
||||
}
|
||||
|
||||
private:
|
||||
static std::mutex mutex_;
|
||||
};
|
||||
|
||||
std::mutex GpioManager::mutex_;
|
||||
354
main/boards/jiuchuan-s3/jiuchuan_dev_board.cc
Normal file
354
main/boards/jiuchuan-s3/jiuchuan_dev_board.cc
Normal file
@@ -0,0 +1,354 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/es8311_audio_codec.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "i2c_device.h"
|
||||
#include "iot/thing_manager.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <wifi_station.h>
|
||||
#include "led/single_led.h"
|
||||
#include "assets/lang_config.h"
|
||||
#include "esp_lcd_panel_gc9301.h"
|
||||
|
||||
#include "power_save_timer.h"
|
||||
#include "power_manager.h"
|
||||
#include "power_controller.h"
|
||||
#include "gpio_manager.h"
|
||||
#include <driver/rtc_io.h>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
#define TAG "JiuchuanDevBoard"
|
||||
#define __USER_GPIO_PWRDOWN__
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
class JiuchuanDevBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
Button boot_button_;
|
||||
Button pwr_button_;
|
||||
Button wifi_button;
|
||||
Button cmd_button;
|
||||
LcdDisplay* display_;
|
||||
PowerSaveTimer* power_save_timer_;
|
||||
PowerManager* power_manager_;
|
||||
esp_lcd_panel_io_handle_t panel_io = NULL;
|
||||
esp_lcd_panel_handle_t panel = NULL;
|
||||
|
||||
void InitializePowerManager() {
|
||||
power_manager_ = new PowerManager(PWR_ADC_GPIO);
|
||||
power_manager_->OnChargingStatusChanged([this](bool is_charging) {
|
||||
if (is_charging) {
|
||||
power_save_timer_->SetEnabled(false);
|
||||
} else {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void InitializePowerSaveTimer() {
|
||||
#ifndef __USER_GPIO_PWRDOWN__
|
||||
RTC_DATA_ATTR static bool long_press_occurred = false;
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
if (cause == ESP_SLEEP_WAKEUP_EXT0) {
|
||||
ESP_LOGI(TAG, "Wake up by EXT0");
|
||||
const int64_t start = esp_timer_get_time();
|
||||
ESP_LOGI(TAG, "esp_sleep_get_wakeup_cause");
|
||||
while (gpio_get_level(PWR_BUTTON_GPIO) == 0) {
|
||||
if (esp_timer_get_time() - start > 3000000) {
|
||||
long_press_occurred = true;
|
||||
break;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
if (long_press_occurred) {
|
||||
ESP_LOGI(TAG, "Long press wakeup");
|
||||
long_press_occurred = false;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Short press, return to sleep");
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(PWR_BUTTON_GPIO, 0));
|
||||
ESP_ERROR_CHECK(rtc_gpio_pullup_en(PWR_BUTTON_GPIO)); // 内部上拉
|
||||
ESP_ERROR_CHECK(rtc_gpio_pulldown_dis(PWR_BUTTON_GPIO));
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//一分钟进入浅睡眠,5分钟进入深睡眠关机
|
||||
power_save_timer_ = new PowerSaveTimer(-1, (60*10), (60*30));
|
||||
// power_save_timer_ = new PowerSaveTimer(-1, 6, 10);//test
|
||||
power_save_timer_->OnEnterSleepMode([this]() {
|
||||
ESP_LOGI(TAG, "Enabling sleep mode");
|
||||
display_->SetChatMessage("system", "");
|
||||
display_->SetEmotion("sleepy");
|
||||
GetBacklight()->SetBrightness(1);
|
||||
});
|
||||
power_save_timer_->OnExitSleepMode([this]() {
|
||||
display_->SetChatMessage("system", "");
|
||||
display_->SetEmotion("neutral");
|
||||
GetBacklight()->RestoreBrightness();
|
||||
});
|
||||
power_save_timer_->OnShutdownRequest([this]() {
|
||||
ESP_LOGI(TAG, "Shutting down");
|
||||
#ifndef __USER_GPIO_PWRDOWN__
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(PWR_BUTTON_GPIO, 0));
|
||||
ESP_ERROR_CHECK(rtc_gpio_pullup_en(PWR_BUTTON_GPIO)); // 内部上拉
|
||||
ESP_ERROR_CHECK(rtc_gpio_pulldown_dis(PWR_BUTTON_GPIO));
|
||||
|
||||
esp_lcd_panel_disp_on_off(panel, false); //关闭显示
|
||||
esp_deep_sleep_start();
|
||||
#else
|
||||
rtc_gpio_set_level(PWR_EN_GPIO, 0);
|
||||
rtc_gpio_hold_dis(PWR_EN_GPIO);
|
||||
#endif
|
||||
});
|
||||
power_save_timer_->SetEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = (i2c_port_t)1,
|
||||
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
|
||||
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_));
|
||||
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
auto& powerCtrl = PowerController::Instance();
|
||||
powerCtrl.SetState(PowerController::PowerState::ACTIVE); // 确保初始状态为ACTIVE
|
||||
|
||||
// 配置GPIO
|
||||
ESP_LOGI(TAG, "Configuring power button GPIO");
|
||||
GpioManager::Config(GPIO_NUM_3, GpioManager::GpioMode::INPUT_PULLDOWN);
|
||||
GpioManager::Config(PWR_EN_GPIO, GpioManager::GpioMode::OUTPUT);
|
||||
GpioManager::SetLevel(PWR_EN_GPIO, 1); // 确保电源使能
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
ESP_LOGI(TAG, "Boot button clicked");
|
||||
power_save_timer_->WakeUp();
|
||||
});
|
||||
|
||||
// 检查电源按钮初始状态
|
||||
ESP_LOGI(TAG, "Power button initial state: %d", GpioManager::GetLevel(PWR_BUTTON_GPIO));
|
||||
|
||||
// 高电平有效长按关机逻辑
|
||||
pwr_button_.OnLongPress([&powerCtrl]() {
|
||||
ESP_LOGI(TAG, "Power button long press detected (high-active)");
|
||||
|
||||
// 高电平有效防抖确认
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int level = GpioManager::GetLevel(PWR_BUTTON_GPIO);
|
||||
ESP_LOGD(TAG, "Debounce check %d: GPIO%d level=%d", i+1, PWR_BUTTON_GPIO, level);
|
||||
|
||||
if (level == 0) {
|
||||
ESP_LOGW(TAG, "Power button inactive during confirmation - abort shutdown");
|
||||
return;
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
ESP_LOGI(TAG, "Confirmed power button pressed (level=1)");
|
||||
|
||||
ESP_LOGI(TAG, "Confirmed power button pressed - initiating shutdown");
|
||||
powerCtrl.SetState(PowerController::PowerState::SHUTDOWN);
|
||||
|
||||
// 确保状态变更
|
||||
if (powerCtrl.GetState() != PowerController::PowerState::SHUTDOWN) {
|
||||
ESP_LOGE(TAG, "Failed to set shutdown state!");
|
||||
}
|
||||
});
|
||||
|
||||
wifi_button.OnClick([this, &powerCtrl]() {
|
||||
ESP_LOGI(TAG, "Wifi button clicked");
|
||||
power_save_timer_->WakeUp();
|
||||
|
||||
if (powerCtrl.GetState() == PowerController::PowerState::ACTIVE) {
|
||||
ESP_LOGI(TAG, "Resetting WiFi configuration");
|
||||
GpioManager::SetLevel(PWR_EN_GPIO, 1);
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
|
||||
cmd_button.OnClick([this]() {
|
||||
ESP_LOGI(TAG, "Command button clicked");
|
||||
power_save_timer_->WakeUp();
|
||||
Application::GetInstance().ToggleChatState();
|
||||
});
|
||||
|
||||
// 配置电源状态变更回调(优化版)
|
||||
powerCtrl.OnStateChange([this](PowerController::PowerState newState) {
|
||||
switch(newState) {
|
||||
case PowerController::PowerState::SHUTDOWN: {
|
||||
ESP_LOGI(TAG, "Entering shutdown sequence");
|
||||
|
||||
// 统一唤醒触发条件
|
||||
#ifndef __USER_GPIO_PWRDOWN__
|
||||
ESP_LOGD(TAG, "Configuring high-level wakeup on GPIO%d", PWR_BUTTON_GPIO);
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(PWR_BUTTON_GPIO, 1)); // 高电平唤醒
|
||||
#else
|
||||
ESP_LOGD(TAG, "Powering down via GPIO control");
|
||||
GpioManager::SetLevel(PWR_EN_GPIO, 0);
|
||||
#endif
|
||||
|
||||
// 确保所有外设已关闭
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
ESP_LOGI(TAG, "Initiating deep sleep");
|
||||
|
||||
// 最后状态确认(通过单例访问)
|
||||
if (PowerController::Instance().GetState() != PowerController::PowerState::SHUTDOWN) {
|
||||
ESP_LOGE(TAG, "State inconsistency! Forcing shutdown");
|
||||
}
|
||||
esp_deep_sleep_start();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, "State changed to %d", static_cast<int>(newState));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void InitializeGC9301isplay() {
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGI(TAG, "test Install panel IO");
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = DISPLAY_SPI_MOSI_PIN;
|
||||
buscfg.sclk_io_num = DISPLAY_SPI_SCK_PIN;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.quadwp_io_num = GPIO_NUM_NC;
|
||||
buscfg.quadhd_io_num = GPIO_NUM_NC;
|
||||
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
|
||||
// 初始化SPI总线
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = DISPLAY_SPI_CS_PIN;
|
||||
io_config.dc_gpio_num = DISPLAY_DC_PIN;
|
||||
io_config.spi_mode = 3;
|
||||
io_config.pclk_hz = 80 * 1000 * 1000;
|
||||
io_config.trans_queue_depth = 10;
|
||||
io_config.lcd_cmd_bits = 8;
|
||||
io_config.lcd_param_bits = 8;
|
||||
esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io);
|
||||
|
||||
// 初始化液晶屏驱动芯片9309
|
||||
ESP_LOGI(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = GPIO_NUM_NC;
|
||||
panel_config.rgb_ele_order = LCD_RGB_ENDIAN_BGR;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
esp_lcd_new_panel_gc9309na(panel_io, &panel_config, &panel);
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, false);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
#else
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Battery"));
|
||||
}
|
||||
public:
|
||||
JiuchuanDevBoard() :
|
||||
boot_button_(BOOT_BUTTON_GPIO),
|
||||
pwr_button_(PWR_BUTTON_GPIO,true),
|
||||
wifi_button(WIFI_BUTTON_GPIO),
|
||||
cmd_button(CMD_BUTTON_GPIO) {
|
||||
InitializeI2c();
|
||||
InitializePowerManager();
|
||||
InitializePowerSaveTimer();
|
||||
InitializeButtons();
|
||||
InitializeGC9301isplay();
|
||||
InitializeIot();
|
||||
GetBacklight()->RestoreBrightness();
|
||||
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
static SingleLed led(BUILTIN_LED_GPIO);
|
||||
return &led;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
|
||||
static Es8311AudioCodec audio_codec(
|
||||
codec_i2c_bus_,
|
||||
I2C_NUM_0,
|
||||
AUDIO_INPUT_SAMPLE_RATE,
|
||||
AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_MCLK,
|
||||
AUDIO_I2S_GPIO_BCLK,
|
||||
AUDIO_I2S_GPIO_WS,
|
||||
AUDIO_I2S_GPIO_DOUT,
|
||||
AUDIO_I2S_GPIO_DIN,
|
||||
AUDIO_CODEC_PA_PIN,
|
||||
AUDIO_CODEC_ES8311_ADDR);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual Backlight* GetBacklight() override {
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
return &backlight;
|
||||
}
|
||||
|
||||
virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override {
|
||||
static bool last_discharging = false;
|
||||
charging = power_manager_->IsCharging();
|
||||
discharging = power_manager_->IsDischarging();
|
||||
if (discharging != last_discharging) {
|
||||
power_save_timer_->SetEnabled(discharging);
|
||||
last_discharging = discharging;
|
||||
}
|
||||
level = power_manager_->GetBatteryLevel();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void SetPowerSaveMode(bool enabled) override {
|
||||
if (!enabled) {
|
||||
power_save_timer_->WakeUp();
|
||||
}
|
||||
WifiBoard::SetPowerSaveMode(enabled);
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(JiuchuanDevBoard);
|
||||
51
main/boards/jiuchuan-s3/power_controller.h
Normal file
51
main/boards/jiuchuan-s3/power_controller.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
class PowerController {
|
||||
public:
|
||||
enum class PowerState {
|
||||
ACTIVE,
|
||||
LIGHT_SLEEP,
|
||||
DEEP_SLEEP,
|
||||
SHUTDOWN
|
||||
};
|
||||
|
||||
static PowerController& Instance() {
|
||||
static PowerController instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void SetState(PowerState newState) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (currentState_ != newState) {
|
||||
ESP_LOGI("PowerCtrl", "State change: %d -> %d",
|
||||
static_cast<int>(currentState_),
|
||||
static_cast<int>(newState));
|
||||
|
||||
currentState_ = newState;
|
||||
if (stateChangeCallback_) {
|
||||
stateChangeCallback_(newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PowerState GetState() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return currentState_;
|
||||
}
|
||||
|
||||
void OnStateChange(std::function<void(PowerState)> callback) {
|
||||
stateChangeCallback_ = callback;
|
||||
}
|
||||
|
||||
private:
|
||||
PowerController() = default;
|
||||
~PowerController() = default;
|
||||
|
||||
PowerState currentState_ = PowerState::ACTIVE;
|
||||
std::function<void(PowerState)> stateChangeCallback_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
149
main/boards/jiuchuan-s3/power_manager.h
Normal file
149
main/boards/jiuchuan-s3/power_manager.h
Normal file
@@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <esp_timer.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#include "adc_battery_estimation.h"
|
||||
|
||||
#define JIUCHUAN_ADC_UNIT (ADC_UNIT_1)
|
||||
#define JIUCHUAN_ADC_BITWIDTH (ADC_BITWIDTH_12)
|
||||
#define JIUCHUAN_ADC_ATTEN (ADC_ATTEN_DB_12)
|
||||
#define JIUCHUAN_ADC_CHANNEL (ADC_CHANNEL_3)
|
||||
#define JIUCHUAN_RESISTOR_UPPER (200000)
|
||||
#define JIUCHUAN_RESISTOR_LOWER (100000)
|
||||
|
||||
class PowerManager {
|
||||
private:
|
||||
esp_timer_handle_t timer_handle_;
|
||||
std::function<void(bool)> on_charging_status_changed_;
|
||||
std::function<void(bool)> on_low_battery_status_changed_;
|
||||
|
||||
gpio_num_t charging_pin_ = GPIO_NUM_NC;
|
||||
std::vector<uint16_t> adc_values_;
|
||||
uint32_t battery_level_ = 0;
|
||||
bool is_charging_ = false;
|
||||
bool is_low_battery_ = false;
|
||||
int ticks_ = 0;
|
||||
const int kBatteryAdcInterval = 60;
|
||||
const int kBatteryAdcDataCount = 3;
|
||||
const int kLowBatteryLevel = 20;
|
||||
|
||||
adc_battery_estimation_handle_t adc_battery_estimation_handle;
|
||||
|
||||
void CheckBatteryStatus() {
|
||||
// Get charging status
|
||||
bool new_charging_status = gpio_get_level(charging_pin_) == 1;
|
||||
if (new_charging_status != is_charging_) {
|
||||
is_charging_ = new_charging_status;
|
||||
if (on_charging_status_changed_) {
|
||||
on_charging_status_changed_(is_charging_);
|
||||
}
|
||||
ReadBatteryAdcData();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果电池电量数据不足,则读取电池电量数据
|
||||
if (adc_values_.size() < kBatteryAdcDataCount) {
|
||||
ReadBatteryAdcData();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果电池电量数据充足,则每 kBatteryAdcInterval 个 tick 读取一次电池电量数据
|
||||
ticks_++;
|
||||
if (ticks_ % kBatteryAdcInterval == 0) {
|
||||
ReadBatteryAdcData();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadBatteryAdcData() {
|
||||
float battery_capacity_temp = 0;
|
||||
adc_battery_estimation_get_capacity(adc_battery_estimation_handle, &battery_capacity_temp);
|
||||
ESP_LOGI("PowerManager", "Battery level: %.1f%%", battery_capacity_temp);
|
||||
battery_level_ = battery_capacity_temp;
|
||||
}
|
||||
|
||||
public:
|
||||
PowerManager(gpio_num_t pin) : charging_pin_(pin) {
|
||||
// 初始化充电引脚
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << charging_pin_);
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// 创建电池电量检查定时器
|
||||
esp_timer_create_args_t timer_args = {
|
||||
.callback = [](void* arg) {
|
||||
PowerManager* self = static_cast<PowerManager*>(arg);
|
||||
self->CheckBatteryStatus();
|
||||
},
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "battery_check_timer",
|
||||
.skip_unhandled_events = true,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &timer_handle_));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(timer_handle_, 1000000));
|
||||
|
||||
// 初始化 ADC
|
||||
static const battery_point_t battery_ponint_table[]={
|
||||
{ 4.2 , 100},
|
||||
{ 4.06 , 80},
|
||||
{ 3.82 , 60},
|
||||
{ 3.58 , 40},
|
||||
{ 3.34 , 20},
|
||||
{ 3.1 , 0}
|
||||
};
|
||||
|
||||
adc_battery_estimation_t config = {
|
||||
.internal = {
|
||||
.adc_unit = JIUCHUAN_ADC_UNIT,
|
||||
.adc_bitwidth = JIUCHUAN_ADC_BITWIDTH,
|
||||
.adc_atten = JIUCHUAN_ADC_ATTEN,
|
||||
},
|
||||
.adc_channel = JIUCHUAN_ADC_CHANNEL,
|
||||
.upper_resistor = JIUCHUAN_RESISTOR_UPPER,
|
||||
.lower_resistor = JIUCHUAN_RESISTOR_LOWER,
|
||||
.battery_points = battery_ponint_table,
|
||||
.battery_points_count = sizeof(battery_ponint_table) / sizeof(battery_ponint_table[0])
|
||||
};
|
||||
|
||||
adc_battery_estimation_handle = adc_battery_estimation_create(&config);
|
||||
}
|
||||
|
||||
~PowerManager() {
|
||||
if (timer_handle_) {
|
||||
esp_timer_stop(timer_handle_);
|
||||
esp_timer_delete(timer_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCharging() {
|
||||
// 如果电量已经满了,则不再显示充电中
|
||||
if (battery_level_ == 100) {
|
||||
return false;
|
||||
}
|
||||
return is_charging_;
|
||||
}
|
||||
|
||||
bool IsDischarging() {
|
||||
// 没有区分充电和放电,所以直接返回相反状态
|
||||
return !is_charging_;
|
||||
}
|
||||
|
||||
uint8_t GetBatteryLevel() {
|
||||
return battery_level_;
|
||||
}
|
||||
|
||||
void OnLowBatteryStatusChanged(std::function<void(bool)> callback) {
|
||||
on_low_battery_status_changed_ = callback;
|
||||
}
|
||||
|
||||
void OnChargingStatusChanged(std::function<void(bool)> callback) {
|
||||
on_charging_status_changed_ = callback;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user