#include "includes.h" uint8_t CurrentMode = 0; /* 样机「开机彩灯」:侧灯全亮色序 蓝→青→红→绿(末态与模式切换同色绿) */ void UI_BootLedShow(void) { static const LedColor_TypeDef seq[] = { LED_COLOR_B, LED_COLOR_CYA, LED_COLOR_R, LED_COLOR_G, }; static const uint16_t hold_ms[] = { 400, 300, 400, 0 }; uint8_t i; for (i = 0; i < (uint8_t)(sizeof(seq) / sizeof(seq[0])); i++) { app_tm1629_all_on(seq[i]); if (hold_ms[i] != 0U) rt_thread_mdelay(hold_ms[i]); } } #define UI_MODE_SCREEN_WIDTH 240 #define UI_MODE_SCREEN_HEIGHT 320 extern bool InModeFlag; /* 模式选择三行与演奏页移调卡不再共用 Y:演奏页按 0923-04 上移后仍保持本页 48/140/232 */ #define UI0902_MODE_ROW_X 9 #define UI0902_MODE_ROW_W 222 #define UI0902_MODE_ROW_H 88 #define UI0902_MODE_ROW_Y0 48 #define UI0902_MODE_ROW_DY 92 /* 48 / 140 / 232 */ /* 模式页不显示焦点:三行均用未选中资源;单击直接进入 */ static const uint32_t s_mode_row_addr[3] = { UI0902_ROW_UNIVERSAL_NOT_ADDR, /* 0 万能 */ UI0902_ROW_NORMAL_NOT_ADDR, /* 1 普通 */ UI0902_ROW_EXPERT_NOT_ADDR, /* 2 专业 */ }; static void UI_ModeSelect_DrawButtons(void) { uint8_t i; for (i = 0; i < 3; i++) { uint16_t y = (uint16_t)(UI0902_MODE_ROW_Y0 + i * UI0902_MODE_ROW_DY); LCD_WR_PIC_FROM_FLASH(UI0902_MODE_ROW_X, y, UI0902_MODE_ROW_W, UI0902_MODE_ROW_H, s_mode_row_addr[i]); } } void UI_ReturnToModeSelect(void) { InModeFlag = false; Stop_AutoBand(); /* 长按回模式选择时,清掉万能 LOOP_LIGHT 残留红灯 */ app_tm1629_all_on(LED_COLOR_G); CallUI_Idle(); UI_DrawModeSelectScreen(); } void UI_DrawModeSelectScreen(void) { InModeFlag = false; /* 模式选择页:未进入演奏模式,禁止伴奏起奏 */ UI_ClearFocus(); pCurrentTouch_Area = Touch_ModeSelect_Areas; LCD_FillByColor(0, 0, 240, 320, UI0902_BG_COLOR); label_top(); UI_ModeSelect_DrawButtons(); /* 设计稿 0902-01:模式选择页无底部三栏导航,仅三行模式按钮 */ } const Touch_AreaTypeDef Touch_ModeSelect_Areas[] = { /* 状态栏蓝牙:点击开关(须在模式行之前,避免被误判) */ { UI0902_BT_HIT_X0, UI0902_BT_HIT_X1, UI0902_BT_HIT_Y0, UI0902_BT_HIT_Y1, 0, 0, GUI_BL_SW, NULL }, /* Flag=按钮序号(0万能/1普通/2专业);ID=进入 TAB。全宽便于命中;Y 与 MODE_ROW_* 一致 */ { 0, 239, 48, 135, 0, 0, 0, NULL }, { 0, 239, 140, 227, 1, 0, 2, NULL }, { 0, 239, 232, 319, 2, 0, 1, NULL }, }; #define TOUCH_AREA_MODE_SELECT_NUM (sizeof(Touch_ModeSelect_Areas)/sizeof(Touch_AreaTypeDef)) /* Timbre_index ���� midi_tables.h ������ */ extern uint8_t channel; extern uint8_t level; extern bool powon; extern bool InModeFlag; extern int16_t MIC_VOL_MAP[11]; extern int8_t delta; extern STRING_MIDI USE_MIDI; void LoadConfig(void) { uint8_t ch; /* 双音色库:主弦走 Bank1,先选 bank 再 PC */ SendProgramWithBankMidiEvent(0, MIDI_BANK_STRING, Timbre_index[mGuiData[GUI_TIMBRE_SELECT].Current]); /* 伴奏通道预置 Bank0,避免上电后首个 PC 前误用残留 bank */ for (ch = 1; ch <= 15; ch++) SendBankChangeMidiEvent(ch, MIDI_BANK_ACCOMP); uint8_t val_127 = (faders[0].val * 127) / 10; SendVolumeChangeMidiEvent(1,val_127); SendVolumeChangeMidiEvent(2,val_127); SendVolumeChangeMidiEvent(3,val_127); SendVolumeChangeMidiEvent(4,val_127); SendVolumeChangeMidiEvent(5,val_127); SendVolumeChangeMidiEvent(6,val_127); SendVolumeChangeMidiEvent(7,val_127); SendVolumeChangeMidiEvent(8,val_127); SendVolumeChangeMidiEvent(9,val_127); SendVolumeChangeMidiEvent(10,val_127); SendVolumeChangeMidiEvent(11,val_127); SendVolumeChangeMidiEvent(12,val_127); SendVolumeChangeMidiEvent(13,val_127); SendVolumeChangeMidiEvent(14,val_127); SendVolumeChangeMidiEvent(15,val_127); SendVolumeChangeMidiEvent(0,(faders[1].val * 127) / 10); Dream_ApplyEditorParams(); AW816_Set_Volume(MIC_VOL_MAP[faders[2].val]); AW816_Set_Reverb(faders[3].val * 10); } void Touch_Mode_Select_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex) { (void)areaIndex; (void)FLAG; if (ID == GUI_BL_SW) { UI_StatusBar_ToggleBluetooth(); return; } /* 音源板初始化要到秒级。没就绪就进模式,LoadConfig/拨片会打出不对的声音 */ if (!Audio_IsReady()) { LOG_I("UI", "mode enter deferred: audio not ready"); return; } StartTask(); /* ID 为 TAB 索引:0=万能(Song) 1=专业(Expert) 2=普通(Free) */ mGuiData[GUI_TAB_INDEX].Current = ID; InModeFlag = true; /* 进模式:默认当前调一级,避免软关机残留把位影响拨片/前奏 */ StartFlag = 0; Accomp_ApplyDefaultIChord(); /* 进入子页后抑制残留按下,否则易误触底栏 GUI_TAB_INDEX(id=5)→设置 */ app_touch_suppress(500); /* DSP 已就绪:再下发音色 + 调音台音量(与关机前 NVM/faders 一致) */ LoadConfig(); switch (ID) { case 0: /* 万能 → 3.bin / 本地→2.bin */ MenuPage_SongMode_Proc(); /* Init 设 TAB 并 Reload */ break; case 1: /* 专业 → 1.bin / 本地→2.bin */ MenuPage_ExpertMode_Proc(); break; case 2: /* 普通 → 1.bin */ MenuPage_FreeMode_Proc(); break; default: break; } } extern uint8_t bat_init_done; /* 关机充电页已绘制标志:POWER_ON 需清除,以便再次软关机充电时重绘 */ static bool s_charge_ui_drawn = false; static uint8_t s_boot_led_done = 0; /* Logo 保持到彩灯结束再进模式选择。电压未知或仍低于 6.8V 时留在 Logo,不点灯。 */ static void UI_BeginPerformancePower(void) { if (!powon || !app_adc_charge_ui_allowed() || s_boot_led_done) { if (powon && app_adc_charge_ui_allowed()) System_RequestAudioBringUp(); return; } s_boot_led_done = 1; UI_BootLedShow(); System_RequestAudioBringUp(); UI_DrawModeSelectScreen(); StartTouchTask(); Draw_Volume_Bar(UI0902_VOL_X, UI0902_VOL_Y, level, 1, WHITE, GRAY); } void IdleProcess(DisplayTaskMessage_Type msg) { switch (msg.MessageType) { case MSG_ID_POWER_ON : /* 充电软关机开机:滞后的拔电消息可能已切主电/关显示,强制恢复 */ BSP_MainPowerEnable(1); LCD_WR_REG(0x29); /* Display ON */ s_charge_ui_drawn = false; LCD_BLK_Set(); s_boot_led_done = 0; /* 先留 Logo。彩灯(蓝→青→红→绿,约 1.1s)播完才进模式选择 */ LCD_WR_PIC_FROM_FLASH(0, 0, UI0902_BOOT_LOGO_W, UI0902_BOOT_LOGO_H, UI0902_BOOT_LOGO_ADDR); /* 扫描先跑,才能采到包电压。电压已高于 6.8V 时这里就播彩灯并切模式页 */ StartScanTask(); UI_BeginPerformancePower(); break; case MSG_ID_HEAVY_BOOT: UI_BeginPerformancePower(); break; case MSG_ID_POWER_OFF: /* 无 Type-C:整机关机熄屏;有 Type-C 时随后 POWOFF_CHARG 会再亮充电图 */ LCD_BLK_Clr(); LCD_WR_REG(0x28); /* Display OFF */ LCD_FillByColor(0, 0, 240, 320, BLACK); break; case MSG_ID_CHARG: Show_Battery_Icon(msg.ID); break; case MSG_ID_TOUCH: { if (!powon) break; /* 软关机已停触摸;兜底勿进模式选择 */ static uint32_t s_last_enter_ms; uint32_t now = rt_tick_get() * 1000U / RT_TICK_PER_SECOND; uint8_t hit = 0U; for(uint8_t k=0; k= Touch_ModeSelect_Areas[k].x_min && msg.HiByte <= Touch_ModeSelect_Areas[k].x_max && msg.LoByte >= Touch_ModeSelect_Areas[k].y_min && msg.LoByte <= Touch_ModeSelect_Areas[k].y_max ) { hit = 1U; LOG_I("UI", "mode_select hit area=%u row=%u x=%u y=%u", (unsigned)k, (unsigned)Touch_ModeSelect_Areas[k].Flag, (unsigned)msg.HiByte, (unsigned)msg.LoByte); /* 蓝牙开关不受进模式防抖限制 */ if (Touch_ModeSelect_Areas[k].ID == GUI_BL_SW) { Touch_Mode_Select_Action(Touch_ModeSelect_Areas[k].Flag, Touch_ModeSelect_Areas[k].ID, k); break; } /* 单击直接进入对应模式主页 */ if (now - s_last_enter_ms < 300U) break; s_last_enter_ms = now; Touch_Mode_Select_Action(Touch_ModeSelect_Areas[k].Flag, Touch_ModeSelect_Areas[k].ID, k); break; } } if (!hit) { LOG_I("UI", "mode_select miss x=%u y=%u", (unsigned)msg.HiByte, (unsigned)msg.LoByte); } } break; case MSG_ID_TOUCH_LONG_REPEAT: /* 模式页暂无长按动作 */ break; case MSG_ID_BATTERY_VALUE: if (!s_boot_led_done) break; Draw_Status_Battery_Icon(msg.ID); break; case MSG_ID_POWOFF_CHARG: { /* 已开机:忽略滞后的关机充电消息,勿覆盖模式选择页或切断主电 */ if (powon) break; if(!s_charge_ui_drawn) { uint8_t peek[4]; BSP_MainPowerEnable(1); LCD_WR_REG(0x29); /* Display on */ /* 先铺设计底色,避免外置 Flash 未烧充电图时整屏 0xFF 灰底 */ LCD_FillByColor(0, 0, 240, 320, UI0902_BG_COLOR); W25Q128_Read(peek, UI0902_CHARGE_SCREEN_ADDR, 4); if (peek[0] != 0xFF || peek[1] != 0xFF) { /* 0904 全屏充电画面(UI0902 @ CHARGE_SCREEN);% 由固件叠画 */ LCD_WR_PIC_FROM_FLASH(0, 0, UI0902_CHARGE_SCREEN_W, UI0902_CHARGE_SCREEN_H, UI0902_CHARGE_SCREEN_ADDR); } else { /* 资源未烧录时的兜底:圆环闪电(需稍后 flash ui0902) */ Draw_Charging_Icon(120, 150, 2, 0x3D7F); LOG_W("UI", "charge flash empty @0x%08lX — flash ui0902", (unsigned long)UI0902_CHARGE_SCREEN_ADDR); } s_charge_ui_drawn = true; Power_LogRails("charge-ui"); } if(usb_charging_state) { /* MiSans Semibold 位图:数字与 % 同高对齐,对齐 0904 设计 */ char digits[4]; uint8_t n, i; uint16_t total_w = g_ChargePctPercent.w; uint16_t x, y = 266; uint8_t pct = msg.ID; if (pct > 100) pct = 100; n = (uint8_t)sprintf(digits, "%u", (unsigned)pct); for (i = 0; i < n; i++) total_w = (uint16_t)(total_w + g_ChargePctDigits[digits[i] - '0'].w); LCD_FillByColor(40, 262, 200, 290, UI0902_BG_COLOR); x = (uint16_t)((240 - total_w) / 2); for (i = 0; i < n; i++) { const ChargePctGlyph *g = &g_ChargePctDigits[digits[i] - '0']; LCD_WR_PIC_Trans(x, y, g->w, g->h, g->data, BLACK); x = (uint16_t)(x + g->w); } LCD_WR_PIC_Trans(x, y, g_ChargePctPercent.w, g_ChargePctPercent.h, g_ChargePctPercent.data, BLACK); /* 插入后先显示充电画面。高于 6.8V 一直亮;否则 10s 后关屏,充电继续 */ if (app_adc_charge_ui_allowed()) Power_ChargeBacklightSteady(); else Power_ChargeBacklightLowHold(); } else { /* 关机充电中拔掉:熄屏,允许下次再进充电页 */ Power_ChargeBacklightSessionReset(); LCD_BLK_Clr(); LCD_FillByColor(0, 0, 240, 320, BLACK); BSP_MainPowerEnable(0); s_charge_ui_drawn = false; Power_LogRails("charge-unplug"); } } break; case MSG_ID_EC_VOL: { /* 主音量已在 MainTask 下发;Logo 期间不画音量条 */ level = msg.HiByte; if (!s_boot_led_done) break; Draw_Volume_Bar(UI0902_VOL_X, UI0902_VOL_Y, level, 1, WHITE, GRAY); ResetAutoPowerCount(); } break; } }