|
| 1 | +#include "ml307_board.h" |
| 2 | +#include "audio_codecs/no_audio_codec.h" |
| 3 | +#include "display/lcd_display.h" |
| 4 | +#include "system_reset.h" |
| 5 | +#include "application.h" |
| 6 | +#include "button.h" |
| 7 | +#include "config.h" |
| 8 | +#include "power_save_timer.h" |
| 9 | +#include "iot/thing_manager.h" |
| 10 | +#include "led/single_led.h" |
| 11 | +#include "assets/lang_config.h" |
| 12 | +#include "../xingzhi-cube-1.54tft-wifi/power_manager.h" |
| 13 | + |
| 14 | +#include <esp_log.h> |
| 15 | +#include <esp_lcd_panel_vendor.h> |
| 16 | +#include <wifi_station.h> |
| 17 | + |
| 18 | +#include <driver/rtc_io.h> |
| 19 | +#include <esp_sleep.h> |
| 20 | + |
| 21 | +#include <esp_lcd_nv3023.h> |
| 22 | +#include "settings.h" |
| 23 | + |
| 24 | +#define TAG "XINGZHI_CUBE_0_85TFT_ML307" |
| 25 | + |
| 26 | +LV_FONT_DECLARE(font_puhui_16_4); |
| 27 | +LV_FONT_DECLARE(font_awesome_16_4); |
| 28 | + |
| 29 | + |
| 30 | +static const nv3023_lcd_init_cmd_t lcd_init_cmds[] = { |
| 31 | + {0xff, (uint8_t[]){0xa5}, 1, 0}, |
| 32 | + {0x3E, (uint8_t[]){0x09}, 1, 0}, |
| 33 | + {0x3A, (uint8_t[]){0x65}, 1, 0}, |
| 34 | + {0x82, (uint8_t[]){0x00}, 1, 0}, |
| 35 | + {0x98, (uint8_t[]){0x00}, 1, 0}, |
| 36 | + {0x63, (uint8_t[]){0x0f}, 1, 0}, |
| 37 | + {0x64, (uint8_t[]){0x0f}, 1, 0}, |
| 38 | + {0xB4, (uint8_t[]){0x34}, 1, 0}, |
| 39 | + {0xB5, (uint8_t[]){0x30}, 1, 0}, |
| 40 | + {0x83, (uint8_t[]){0x03}, 1, 0}, |
| 41 | + {0x86, (uint8_t[]){0x04}, 1, 0}, |
| 42 | + {0x87, (uint8_t[]){0x16}, 1, 0}, |
| 43 | + {0x88, (uint8_t[]){0x0A}, 1, 0}, |
| 44 | + {0x89, (uint8_t[]){0x27}, 1, 0}, |
| 45 | + {0x93, (uint8_t[]){0x63}, 1, 0}, |
| 46 | + {0x96, (uint8_t[]){0x81}, 1, 0}, |
| 47 | + {0xC3, (uint8_t[]){0x10}, 1, 0}, |
| 48 | + {0xE6, (uint8_t[]){0x00}, 1, 0}, |
| 49 | + {0x99, (uint8_t[]){0x01}, 1, 0}, |
| 50 | + {0x70, (uint8_t[]){0x09}, 1, 0}, |
| 51 | + {0x71, (uint8_t[]){0x1D}, 1, 0}, |
| 52 | + {0x72, (uint8_t[]){0x14}, 1, 0}, |
| 53 | + {0x73, (uint8_t[]){0x0a}, 1, 0}, |
| 54 | + {0x74, (uint8_t[]){0x11}, 1, 0}, |
| 55 | + {0x75, (uint8_t[]){0x16}, 1, 0}, |
| 56 | + {0x76, (uint8_t[]){0x38}, 1, 0}, |
| 57 | + {0x77, (uint8_t[]){0x0B}, 1, 0}, |
| 58 | + {0x78, (uint8_t[]){0x08}, 1, 0}, |
| 59 | + {0x79, (uint8_t[]){0x3E}, 1, 0}, |
| 60 | + {0x7a, (uint8_t[]){0x07}, 1, 0}, |
| 61 | + {0x7b, (uint8_t[]){0x0D}, 1, 0}, |
| 62 | + {0x7c, (uint8_t[]){0x16}, 1, 0}, |
| 63 | + {0x7d, (uint8_t[]){0x0F}, 1, 0}, |
| 64 | + {0x7e, (uint8_t[]){0x14}, 1, 0}, |
| 65 | + {0x7f, (uint8_t[]){0x05}, 1, 0}, |
| 66 | + {0xa0, (uint8_t[]){0x04}, 1, 0}, |
| 67 | + {0xa1, (uint8_t[]){0x28}, 1, 0}, |
| 68 | + {0xa2, (uint8_t[]){0x0c}, 1, 0}, |
| 69 | + {0xa3, (uint8_t[]){0x11}, 1, 0}, |
| 70 | + {0xa4, (uint8_t[]){0x0b}, 1, 0}, |
| 71 | + {0xa5, (uint8_t[]){0x23}, 1, 0}, |
| 72 | + {0xa6, (uint8_t[]){0x45}, 1, 0}, |
| 73 | + {0xa7, (uint8_t[]){0x07}, 1, 0}, |
| 74 | + {0xa8, (uint8_t[]){0x0a}, 1, 0}, |
| 75 | + {0xa9, (uint8_t[]){0x3b}, 1, 0}, |
| 76 | + {0xaa, (uint8_t[]){0x0d}, 1, 0}, |
| 77 | + {0xab, (uint8_t[]){0x18}, 1, 0}, |
| 78 | + {0xac, (uint8_t[]){0x14}, 1, 0}, |
| 79 | + {0xad, (uint8_t[]){0x0F}, 1, 0}, |
| 80 | + {0xae, (uint8_t[]){0x19}, 1, 0}, |
| 81 | + {0xaf, (uint8_t[]){0x08}, 1, 0}, |
| 82 | + {0xff, (uint8_t[]){0x00}, 1, 0}, |
| 83 | + {0x11, (uint8_t[]){0x00}, 0, 120}, |
| 84 | + {0x29, (uint8_t[]){0x00}, 0, 10} |
| 85 | +}; |
| 86 | + |
| 87 | +class XINGZHI_CUBE_0_85TFT_ML307 : public Ml307Board { |
| 88 | +private: |
| 89 | + Button boot_button_; |
| 90 | + Button volume_up_button_; |
| 91 | + Button volume_down_button_; |
| 92 | + SpiLcdDisplay* display_; |
| 93 | + PowerSaveTimer* power_save_timer_; |
| 94 | + PowerManager* power_manager_; |
| 95 | + esp_lcd_panel_io_handle_t panel_io_ = nullptr; |
| 96 | + esp_lcd_panel_handle_t panel_ = nullptr; |
| 97 | + |
| 98 | + void InitializePowerManager() { |
| 99 | + power_manager_ = new PowerManager(GPIO_NUM_38); |
| 100 | + power_manager_->OnChargingStatusChanged([this](bool is_charging) { |
| 101 | + if (is_charging) { |
| 102 | + power_save_timer_->SetEnabled(false); |
| 103 | + } else { |
| 104 | + power_save_timer_->SetEnabled(true); |
| 105 | + } |
| 106 | + }); |
| 107 | + } |
| 108 | + |
| 109 | + void InitializePowerSaveTimer() { |
| 110 | + rtc_gpio_init(GPIO_NUM_21); |
| 111 | + rtc_gpio_set_direction(GPIO_NUM_21, RTC_GPIO_MODE_OUTPUT_ONLY); |
| 112 | + rtc_gpio_set_level(GPIO_NUM_21, 1); |
| 113 | + |
| 114 | + power_save_timer_ = new PowerSaveTimer(-1, 60, 300); |
| 115 | + power_save_timer_->OnEnterSleepMode([this]() { |
| 116 | + ESP_LOGI(TAG, "Enabling sleep mode"); |
| 117 | + display_->SetChatMessage("system", ""); |
| 118 | + display_->SetEmotion("sleepy"); |
| 119 | + GetBacklight()->SetBrightness(1); |
| 120 | + }); |
| 121 | + power_save_timer_->OnExitSleepMode([this]() { |
| 122 | + display_->SetChatMessage("system", ""); |
| 123 | + display_->SetEmotion("neutral"); |
| 124 | + GetBacklight()->RestoreBrightness(); |
| 125 | + }); |
| 126 | + power_save_timer_->OnShutdownRequest([this]() { |
| 127 | + ESP_LOGI(TAG, "Shutting down"); |
| 128 | + rtc_gpio_set_level(GPIO_NUM_21, 0); |
| 129 | + // 启用保持功能,确保睡眠期间电平不变 |
| 130 | + rtc_gpio_hold_en(GPIO_NUM_21); |
| 131 | + esp_lcd_panel_disp_on_off(panel_, false); //关闭显示 |
| 132 | + esp_deep_sleep_start(); |
| 133 | + }); |
| 134 | + power_save_timer_->SetEnabled(true); |
| 135 | + } |
| 136 | + |
| 137 | + void InitializeSpi() { |
| 138 | + spi_bus_config_t buscfg = {}; |
| 139 | + buscfg.mosi_io_num = DISPLAY_SDA; |
| 140 | + buscfg.miso_io_num = GPIO_NUM_NC; |
| 141 | + buscfg.sclk_io_num = DISPLAY_SCL; |
| 142 | + buscfg.quadwp_io_num = GPIO_NUM_NC; |
| 143 | + buscfg.quadhd_io_num = GPIO_NUM_NC; |
| 144 | + buscfg.max_transfer_sz = DISPLAY_HEIGHT * 80 *sizeof(uint16_t); |
| 145 | + ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO)); |
| 146 | + } |
| 147 | + |
| 148 | + |
| 149 | + void InitializeButtons() { |
| 150 | + boot_button_.OnClick([this]() { |
| 151 | + auto& app = Application::GetInstance(); |
| 152 | + app.ToggleChatState(); |
| 153 | + }); |
| 154 | + } |
| 155 | + |
| 156 | + void InitializeNv3023Display() { |
| 157 | + ESP_LOGD(TAG, "Install panel IO"); |
| 158 | + esp_lcd_panel_io_spi_config_t io_config = NV3023_PANEL_IO_SPI_CONFIG(DISPLAY_CS, DISPLAY_DC, NULL, NULL); |
| 159 | + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI3_HOST, &io_config, &panel_io_)); |
| 160 | + |
| 161 | + ESP_LOGD(TAG, "Install LCD driver"); |
| 162 | + esp_lcd_panel_dev_config_t panel_config = {}; |
| 163 | + nv3023_vendor_config_t vendor_config = { // Uncomment these lines if use custom initialization commands |
| 164 | + .init_cmds = lcd_init_cmds, |
| 165 | + .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(nv3023_lcd_init_cmd_t), |
| 166 | + }; |
| 167 | + panel_config.reset_gpio_num = DISPLAY_RES; |
| 168 | + panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR; |
| 169 | + panel_config.bits_per_pixel = 16; |
| 170 | + panel_config.vendor_config = &vendor_config; |
| 171 | + |
| 172 | + ESP_ERROR_CHECK(esp_lcd_new_panel_nv3023(panel_io_, &panel_config, &panel_)); |
| 173 | + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_)); |
| 174 | + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_)); |
| 175 | + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_, DISPLAY_SWAP_XY)); |
| 176 | + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y)); |
| 177 | + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_, false)); |
| 178 | + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true)); |
| 179 | + |
| 180 | + display_ = new SpiLcdDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, |
| 181 | + DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, |
| 182 | + { |
| 183 | + .text_font = &font_puhui_16_4, |
| 184 | + .icon_font = &font_awesome_16_4, |
| 185 | + .emoji_font = font_emoji_32_init(), |
| 186 | + }); |
| 187 | + } |
| 188 | + |
| 189 | + void InitializeIot() { |
| 190 | + auto& thing_manager = iot::ThingManager::GetInstance(); |
| 191 | + thing_manager.AddThing(iot::CreateThing("Speaker")); |
| 192 | + thing_manager.AddThing(iot::CreateThing("Screen")); |
| 193 | + thing_manager.AddThing(iot::CreateThing("Battery")); |
| 194 | + } |
| 195 | + |
| 196 | + void Initializegpio21_45() { |
| 197 | + rtc_gpio_init(GPIO_NUM_21); |
| 198 | + rtc_gpio_set_direction(GPIO_NUM_21, RTC_GPIO_MODE_OUTPUT_ONLY); |
| 199 | + rtc_gpio_set_level(GPIO_NUM_21, 1); |
| 200 | + |
| 201 | + //gpio_num_t sp_45 = GPIO_NUM_45; |
| 202 | + gpio_config_t io_conf = {}; |
| 203 | + io_conf.intr_type = GPIO_INTR_DISABLE; |
| 204 | + io_conf.mode = GPIO_MODE_OUTPUT; |
| 205 | + io_conf.pin_bit_mask = (1ULL << GPIO_NUM_45); |
| 206 | + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; |
| 207 | + io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; |
| 208 | + gpio_config(&io_conf); |
| 209 | + gpio_set_level(GPIO_NUM_45, 0); |
| 210 | + } |
| 211 | + |
| 212 | +public: |
| 213 | + XINGZHI_CUBE_0_85TFT_ML307(): Ml307Board(ML307_TX_PIN, ML307_RX_PIN, 4096), |
| 214 | + boot_button_(BOOT_BUTTON_GPIO), |
| 215 | + volume_up_button_(VOLUME_UP_BUTTON_GPIO), |
| 216 | + volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) { |
| 217 | + Initializegpio21_45(); // 初始时,拉高21引脚,保证4g模块正常工作 |
| 218 | + InitializePowerManager(); |
| 219 | + InitializePowerSaveTimer(); |
| 220 | + InitializeSpi(); |
| 221 | + InitializeButtons(); |
| 222 | + InitializeNv3023Display(); |
| 223 | + InitializeIot(); |
| 224 | + GetBacklight()->RestoreBrightness(); |
| 225 | + } |
| 226 | + |
| 227 | + virtual AudioCodec* GetAudioCodec() override { |
| 228 | + static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, |
| 229 | + AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN); |
| 230 | + return &audio_codec; |
| 231 | + } |
| 232 | + |
| 233 | + virtual Display* GetDisplay() override { |
| 234 | + return display_; |
| 235 | + } |
| 236 | + |
| 237 | + virtual Backlight* GetBacklight() override { |
| 238 | + static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT); |
| 239 | + return &backlight; |
| 240 | + } |
| 241 | + |
| 242 | + virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override { |
| 243 | + static bool last_discharging = false; |
| 244 | + charging = power_manager_->IsCharging(); |
| 245 | + discharging = power_manager_->IsDischarging(); |
| 246 | + if (discharging != last_discharging) { |
| 247 | + power_save_timer_->SetEnabled(discharging); |
| 248 | + last_discharging = discharging; |
| 249 | + } |
| 250 | + level = power_manager_->GetBatteryLevel(); |
| 251 | + return true; |
| 252 | + } |
| 253 | + |
| 254 | + virtual void SetPowerSaveMode(bool enabled) override { |
| 255 | + if (!enabled) { |
| 256 | + power_save_timer_->WakeUp(); |
| 257 | + } |
| 258 | + Ml307Board::SetPowerSaveMode(enabled); |
| 259 | + } |
| 260 | +}; |
| 261 | + |
| 262 | +DECLARE_BOARD(XINGZHI_CUBE_0_85TFT_ML307); |
0 commit comments