添加 jiuchuan-s3 开发板支持 (#775)

* 添加 jiuchuang-s3 开发板支持

* 增加编译指南

* 优开发板支持包文件目录,更新README.md

删除了多余板载文件
[* ]README.md -> 更新了编译指南和烧录指南

* 修改板级支持包名

* 使用乐鑫提供的电源监测

* 修复部分代码格式问题

* 解决合并冲突

* 解决部分合并内容

* 完善合并内容

* 修复电量映射表错误

---------

Co-authored-by: unknown <jake12355>
This commit is contained in:
Hangon66
2025-06-29 05:41:42 +08:00
committed by GitHub
parent f1277934d1
commit f2f54ba1fd
12 changed files with 1123 additions and 1 deletions

View 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. 烧录完成前请勿松开电源键。

View 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_

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "jiuchuan-s3",
"sdkconfig_append": []
}
]
}

View 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;
}

View 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

View 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_;

View 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);

View 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_;
};

View 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;
}
};