472 lines
18 KiB
C++
472 lines
18 KiB
C++
#include "wifi_board.h"
|
||
#include "audio_codec.h"
|
||
#include "codecs/es8311_audio_codec.h"
|
||
#include "codecs/box_audio_codec.h"
|
||
#include "codecs/no_audio_codec.h"
|
||
#include "display/lcd_display.h"
|
||
#include "application.h"
|
||
#include "button.h"
|
||
#include "config.h"
|
||
#include "led/single_led.h"
|
||
|
||
#include "i2c_device.h"
|
||
#include "esp_video.h"
|
||
#include <wifi_station.h>
|
||
#include <esp_log.h>
|
||
#include <driver/i2c_master.h>
|
||
#include <freertos/FreeRTOS.h>
|
||
#include <freertos/task.h>
|
||
#include <freertos/timers.h>
|
||
#include <esp_lcd_panel_io.h>
|
||
#include <esp_lcd_panel_vendor.h>
|
||
#include <esp_lcd_panel_ops.h>
|
||
#include "power_save_timer.h"
|
||
#include "power_manager.h"
|
||
#include <driver/rtc_io.h>
|
||
#include <esp_sleep.h>
|
||
#include "esp_io_expander_tca95xx_16bit.h"
|
||
#include "assets/lang_config.h"
|
||
#include <driver/spi_common.h>
|
||
|
||
#define TAG "atk_dnesp32s3_box3"
|
||
|
||
LV_FONT_DECLARE(font_puhui_20_4);
|
||
LV_FONT_DECLARE(font_awesome_20_4);
|
||
|
||
class atk_dnesp32s3_box3 : public WifiBoard {
|
||
private:
|
||
i2c_master_bus_handle_t i2c_bus_;
|
||
LcdDisplay* display_;
|
||
EspVideo* camera_;
|
||
static atk_dnesp32s3_box3* instance_;
|
||
esp_io_expander_handle_t io_exp_handle;
|
||
button_handle_t btns;
|
||
button_driver_t* btn_driver_ = nullptr;
|
||
PowerSaveTimer* power_save_timer_;
|
||
PowerManager* power_manager_;
|
||
PowerSupply power_status_;
|
||
esp_timer_handle_t wake_timer_handle_;
|
||
int ticks_ = 0;
|
||
const int kChgCtrlInterval = 5;
|
||
|
||
void InitializeBoardPowerManager() {
|
||
instance_ = this;
|
||
|
||
if (IoExpanderGetLevel(XIO_BAT_CHRG) == 0) {
|
||
power_status_ = kDeviceTypecSupply;
|
||
} else {
|
||
power_status_ = kDeviceBatterySupply;
|
||
}
|
||
|
||
esp_timer_create_args_t wake_display_timer_args = {
|
||
.callback = [](void *arg) {
|
||
atk_dnesp32s3_box3* self = static_cast<atk_dnesp32s3_box3*>(arg);
|
||
|
||
self->ticks_ ++;
|
||
if (self->ticks_ % self->kChgCtrlInterval == 0) {
|
||
if (self->IoExpanderGetLevel(XIO_BAT_CHRG) == 0) {
|
||
self->power_status_ = kDeviceTypecSupply;
|
||
} else {
|
||
self->power_status_ = kDeviceBatterySupply;
|
||
}
|
||
|
||
/* 低于某个电量,会自动关机 */
|
||
if (self->power_manager_->low_voltage_ < 2630 && self->power_status_ == kDeviceBatterySupply) {
|
||
esp_timer_stop(self->power_manager_->timer_handle_);
|
||
|
||
esp_io_expander_set_dir(self->io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_OUTPUT);
|
||
esp_io_expander_set_level(self->io_exp_handle, XIO_BAT_CHRG_EN, 0);
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
|
||
esp_io_expander_set_dir(self->io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_INPUT);
|
||
esp_io_expander_set_level(self->io_exp_handle, XIO_BAT_CHRG_EN, 0);
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
}
|
||
}
|
||
},
|
||
.arg = this,
|
||
.dispatch_method = ESP_TIMER_TASK,
|
||
.name = "wake_update_timer",
|
||
.skip_unhandled_events = true,
|
||
};
|
||
ESP_ERROR_CHECK(esp_timer_create(&wake_display_timer_args, &wake_timer_handle_));
|
||
ESP_ERROR_CHECK(esp_timer_start_periodic(wake_timer_handle_, 100000));
|
||
}
|
||
|
||
void InitializePowerManager() {
|
||
power_manager_ = new PowerManager(io_exp_handle);
|
||
power_manager_->OnChargingStatusChanged([this](bool is_charging) {
|
||
if (is_charging) {
|
||
power_save_timer_->SetEnabled(false);
|
||
} else {
|
||
power_save_timer_->SetEnabled(true);
|
||
}
|
||
});
|
||
}
|
||
|
||
void InitializePowerSaveTimer() {
|
||
power_save_timer_ = new PowerSaveTimer(-1, -1, -1);
|
||
power_save_timer_->OnEnterSleepMode([this]() {
|
||
GetDisplay()->SetPowerSaveMode(true);
|
||
GetBacklight()->SetBrightness(1);
|
||
});
|
||
power_save_timer_->OnExitSleepMode([this]() {
|
||
GetDisplay()->SetPowerSaveMode(false);
|
||
GetBacklight()->RestoreBrightness();
|
||
});
|
||
power_save_timer_->OnShutdownRequest([this]() {
|
||
if (power_status_ == kDeviceBatterySupply) {
|
||
GetBacklight()->SetBrightness(0);
|
||
esp_timer_stop(power_manager_ ->timer_handle_);
|
||
esp_io_expander_set_dir(io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_OUTPUT);
|
||
esp_io_expander_set_level(io_exp_handle, XIO_BAT_CHRG_EN, 0);
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
esp_io_expander_set_level(io_exp_handle, XIO_VDD_3V3_EN, 0);
|
||
}
|
||
});
|
||
|
||
power_save_timer_->SetEnabled(true);
|
||
}
|
||
|
||
void audio_volume_change(bool direction) {
|
||
auto codec = GetAudioCodec();
|
||
auto volume = codec->output_volume();
|
||
|
||
if (direction) {
|
||
volume += 10;
|
||
if (volume > 100) {
|
||
volume = 100;
|
||
}
|
||
codec->SetOutputVolume(volume);
|
||
} else {
|
||
volume -= 10;
|
||
if (volume < 0) {
|
||
volume = 0;
|
||
}
|
||
codec->SetOutputVolume(volume);
|
||
}
|
||
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
|
||
}
|
||
|
||
void audio_volume_minimum(){
|
||
GetAudioCodec()->SetOutputVolume(0);
|
||
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
|
||
}
|
||
|
||
void audio_volume_maxmum(){
|
||
GetAudioCodec()->SetOutputVolume(100);
|
||
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
|
||
}
|
||
|
||
esp_err_t IoExpanderSetLevel(uint16_t pin_mask, uint8_t level) {
|
||
return esp_io_expander_set_level(io_exp_handle, pin_mask, level);
|
||
}
|
||
|
||
uint8_t IoExpanderGetLevel(uint16_t pin_mask) {
|
||
uint32_t pin_val = 0;
|
||
esp_io_expander_get_level(io_exp_handle, DRV_IO_EXP_INPUT_MASK, &pin_val);
|
||
pin_mask &= DRV_IO_EXP_INPUT_MASK;
|
||
return (uint8_t)((pin_val & pin_mask) ? 1 : 0);
|
||
}
|
||
|
||
void InitializeIoExpander() {
|
||
esp_err_t ret = ESP_OK;
|
||
esp_io_expander_new_i2c_tca95xx_16bit(i2c_bus_, AW9523B_ADDR, &io_exp_handle);
|
||
|
||
// ret |= esp_io_expander_set_pullupdown(io_exp_handle, DRV_IO_EXP_INPUT_MASK, IO_EXPANDER_PULL_NONE);
|
||
|
||
ret |= esp_io_expander_set_dir(io_exp_handle, DRV_IO_EXP_OUTPUT_MASK, IO_EXPANDER_OUTPUT);
|
||
ret |= esp_io_expander_set_dir(io_exp_handle, DRV_IO_EXP_INPUT_MASK, IO_EXPANDER_INPUT);
|
||
|
||
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VDD_2V8_EN, 1); /* 0308 */
|
||
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VDD_3V3_EN, 1); /* 电源 */
|
||
ret |= esp_io_expander_set_level(io_exp_handle, XIO_ESP_ADC_SEL, 1); /* ADC */
|
||
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VDDA_3V3_EN, 1);/* 音频电源 */
|
||
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VBAT_EN, 1); /* 音频 */
|
||
ret |= esp_io_expander_set_level(io_exp_handle, XIO_PA_CTRL, 1); /* 音频功放 */
|
||
ret |= esp_io_expander_set_level(io_exp_handle, XIO_LCD_BL, 0); /* LCD背光 */
|
||
|
||
assert(ret == ESP_OK);
|
||
}
|
||
|
||
void InitializeI2c() {
|
||
// Initialize I2C peripheral
|
||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||
.i2c_port = (i2c_port_t)I2C_NUM_0,
|
||
.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, &i2c_bus_));
|
||
}
|
||
|
||
// Initialize spi peripheral
|
||
void InitializeSpi() {
|
||
spi_bus_config_t buscfg = {};
|
||
buscfg.mosi_io_num = LCD_MOSI_PIN;
|
||
buscfg.miso_io_num = LCD_MISO_PIN;
|
||
buscfg.sclk_io_num = LCD_SCLK_PIN;
|
||
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(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||
}
|
||
|
||
void InitializeSt7789Display() {
|
||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||
esp_lcd_panel_handle_t panel = nullptr;
|
||
ESP_LOGD(TAG, "Install panel IO");
|
||
// 液晶屏控制IO初始化
|
||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||
io_config.cs_gpio_num = LCD_CS_PIN;
|
||
io_config.dc_gpio_num = LCD_DC_PIN;
|
||
io_config.spi_mode = 0;
|
||
io_config.pclk_hz = 60 * 1000 * 1000;
|
||
io_config.trans_queue_depth = 7;
|
||
io_config.lcd_cmd_bits = 8;
|
||
io_config.lcd_param_bits = 8;
|
||
esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io);
|
||
|
||
// 初始化液晶屏驱动芯片ST7789
|
||
ESP_LOGD(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_ELEMENT_ORDER_RGB;
|
||
panel_config.bits_per_pixel = 16;
|
||
panel_config.data_endian = LCD_RGB_DATA_ENDIAN_BIG,
|
||
esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel);
|
||
esp_lcd_panel_reset(panel);
|
||
|
||
esp_lcd_panel_init(panel);
|
||
esp_lcd_panel_invert_color(panel, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||
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);
|
||
}
|
||
|
||
void InitializeButtons() {
|
||
instance_ = this;
|
||
|
||
button_config_t bo_btn_cfg = {
|
||
.long_press_time = 800,
|
||
.short_press_time = 500
|
||
};
|
||
|
||
button_config_t k1_btn_cfg = {
|
||
.long_press_time = 800,
|
||
.short_press_time = 500
|
||
};
|
||
|
||
button_config_t k2_btn_cfg = {
|
||
.long_press_time = 800,
|
||
.short_press_time = 500
|
||
};
|
||
|
||
button_driver_t* xio_k1_btn_driver_ = nullptr;
|
||
button_driver_t* xio_k2_btn_driver_ = nullptr;
|
||
|
||
button_handle_t bo_btn_handle = NULL;
|
||
button_handle_t k1_btn_handle = NULL;
|
||
button_handle_t k2_btn_handle = NULL;
|
||
|
||
xio_k1_btn_driver_ = (button_driver_t*)calloc(1, sizeof(button_driver_t));
|
||
xio_k1_btn_driver_->enable_power_save = false;
|
||
xio_k1_btn_driver_->get_key_level = [](button_driver_t *button_driver) -> uint8_t {
|
||
return !instance_->IoExpanderGetLevel(XIO_KEY_K1);
|
||
};
|
||
ESP_ERROR_CHECK(iot_button_create(&k1_btn_cfg, xio_k1_btn_driver_, &k1_btn_handle));
|
||
|
||
xio_k2_btn_driver_ = (button_driver_t*)calloc(1, sizeof(button_driver_t));
|
||
xio_k2_btn_driver_->enable_power_save = false;
|
||
xio_k2_btn_driver_->get_key_level = [](button_driver_t *button_driver) -> uint8_t {
|
||
return instance_->IoExpanderGetLevel(XIO_KEY_K2);
|
||
};
|
||
ESP_ERROR_CHECK(iot_button_create(&k2_btn_cfg, xio_k2_btn_driver_, &k2_btn_handle));
|
||
|
||
button_gpio_config_t bo_cfg = {
|
||
.gpio_num = BOOT_BUTTON_GPIO,
|
||
.active_level = BUTTON_INACTIVE,
|
||
.enable_power_save = false,
|
||
.disable_pull = false
|
||
};
|
||
ESP_ERROR_CHECK(iot_button_new_gpio_device(&bo_btn_cfg, &bo_cfg, &bo_btn_handle));
|
||
|
||
iot_button_register_cb(k1_btn_handle, BUTTON_PRESS_DOWN, nullptr, [](void* button_handle, void* usr_data) {
|
||
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
|
||
self->power_save_timer_->WakeUp();
|
||
self->audio_volume_change(false);
|
||
}, this);
|
||
|
||
iot_button_register_cb(k1_btn_handle, BUTTON_LONG_PRESS_START, nullptr, [](void* button_handle, void* usr_data) {
|
||
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
|
||
self->power_save_timer_->WakeUp();
|
||
self->audio_volume_minimum();
|
||
}, this);
|
||
|
||
iot_button_register_cb(k2_btn_handle, BUTTON_PRESS_DOWN, nullptr, [](void* button_handle, void* usr_data) {
|
||
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
|
||
self->power_save_timer_->WakeUp();
|
||
auto& app = Application::GetInstance();
|
||
app.ToggleChatState();
|
||
}, this);
|
||
|
||
iot_button_register_cb(k2_btn_handle, BUTTON_LONG_PRESS_START, nullptr, [](void* button_handle, void* usr_data) {
|
||
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
|
||
|
||
auto& app = Application::GetInstance();
|
||
if (app.GetDeviceState() == kDeviceStateStarting) {
|
||
self->EnterWifiConfigMode();
|
||
return;
|
||
}
|
||
|
||
if (self->power_status_ == kDeviceBatterySupply) {
|
||
auto backlight = Board::GetInstance().GetBacklight();
|
||
backlight->SetBrightness(0);
|
||
esp_timer_stop(self->power_manager_->timer_handle_);
|
||
esp_io_expander_set_dir(self->io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_OUTPUT);
|
||
esp_io_expander_set_level(self->io_exp_handle, XIO_BAT_CHRG_EN, 0);
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
esp_io_expander_set_level(self->io_exp_handle, XIO_VDD_3V3_EN, 0);
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
}
|
||
}, this);
|
||
|
||
iot_button_register_cb(bo_btn_handle, BUTTON_PRESS_DOWN, nullptr, [](void* button_handle, void* usr_data) {
|
||
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
|
||
self->power_save_timer_->WakeUp();
|
||
self->audio_volume_change(true);
|
||
}, this);
|
||
|
||
iot_button_register_cb(bo_btn_handle, BUTTON_LONG_PRESS_START, nullptr, [](void* button_handle, void* usr_data) {
|
||
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
|
||
self->power_save_timer_->WakeUp();
|
||
self->audio_volume_maxmum();
|
||
}, this);
|
||
}
|
||
|
||
/* 初始化摄像头:GC0308; */
|
||
/* 根据正点原子官方示例参数 */
|
||
void InitializeCamera() {
|
||
esp_io_expander_set_level(io_exp_handle, XIO_TP_CAM_RESET, 0); /* 确保复位 */
|
||
vTaskDelay(pdMS_TO_TICKS(50)); /* 延长复位保持时间 */
|
||
esp_io_expander_set_level(io_exp_handle, XIO_TP_CAM_RESET, 1); /* 释放复位 */
|
||
vTaskDelay(pdMS_TO_TICKS(50)); /* 延长 50ms */
|
||
|
||
/* DVP pin configuration */
|
||
static esp_cam_ctlr_dvp_pin_config_t dvp_pin_config = {
|
||
.data_width = CAM_CTLR_DATA_WIDTH_8,
|
||
.data_io = {
|
||
[0] = CAM_PIN_D0,
|
||
[1] = CAM_PIN_D1,
|
||
[2] = CAM_PIN_D2,
|
||
[3] = CAM_PIN_D3,
|
||
[4] = CAM_PIN_D4,
|
||
[5] = CAM_PIN_D5,
|
||
[6] = CAM_PIN_D6,
|
||
[7] = CAM_PIN_D7,
|
||
},
|
||
.vsync_io = CAM_PIN_VSYNC,
|
||
.de_io = CAM_PIN_LREF,
|
||
.pclk_io = CAM_PIN_PCLK,
|
||
.xclk_io = CAM_PIN_XCLK,
|
||
};
|
||
|
||
/* 复用 I2C 总线 */
|
||
esp_video_init_sccb_config_t sccb_config = {
|
||
.init_sccb = false, /* 不初始化新的 SCCB,使用现有的 I2C 总线 */
|
||
.i2c_handle = i2c_bus_, /* 使用现有的 I2C 总线句柄 */
|
||
.freq = 100000, /* SCCB 通信频率,通常为 100kHz */
|
||
};
|
||
|
||
esp_video_init_dvp_config_t dvp_config = {
|
||
.sccb_config = sccb_config,
|
||
.reset_pin = CAM_PIN_RESET,
|
||
.pwdn_pin = CAM_PIN_PWDN,
|
||
.dvp_pin = dvp_pin_config,
|
||
.xclk_freq = 24000000,
|
||
};
|
||
|
||
esp_video_init_config_t video_config = {
|
||
.dvp = &dvp_config,
|
||
};
|
||
|
||
camera_ = new EspVideo(video_config);
|
||
}
|
||
|
||
public:
|
||
atk_dnesp32s3_box3(){
|
||
InitializeI2c();
|
||
InitializeSpi();
|
||
InitializeIoExpander();
|
||
InitializePowerSaveTimer();
|
||
//InitializePowerManager();
|
||
InitializeSt7789Display();
|
||
InitializeButtons();
|
||
//GetBacklight()->RestoreBrightness();
|
||
//InitializeBoardPowerManager();
|
||
InitializeCamera();
|
||
}
|
||
|
||
virtual AudioCodec* GetAudioCodec() override {
|
||
static BoxAudioCodec audio_codec(
|
||
i2c_bus_,
|
||
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,
|
||
AUDIO_CODEC_ES7210_ADDR,
|
||
AUDIO_INPUT_REFERENCE);
|
||
return &audio_codec;
|
||
}
|
||
|
||
virtual Display* GetDisplay() override {
|
||
return display_;
|
||
}
|
||
|
||
// virtual Backlight* GetBacklight() override {
|
||
// static PwmBacklight backlight(GPIO_NUM_0, 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 SetPowerSaveLevel(PowerSaveLevel level) override {
|
||
if (level != PowerSaveLevel::LOW_POWER) {
|
||
power_save_timer_->WakeUp();
|
||
}
|
||
WifiBoard::SetPowerSaveLevel(level);
|
||
}
|
||
|
||
virtual Camera* GetCamera() override {
|
||
return camera_;
|
||
}
|
||
};
|
||
|
||
DECLARE_BOARD(atk_dnesp32s3_box3);
|
||
|
||
// 定义静态成员变量
|
||
atk_dnesp32s3_box3* atk_dnesp32s3_box3::instance_ = nullptr;
|