Align mode-select and status bar with 0902/0819 design.

Fill three mode rows to the screen bottom without a nav bar, and fix the status bar to one volume meter, centered Bluetooth, and battery percentage.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-02 16:23:18 +08:00
parent e62c2270af
commit 41052d334d
9 changed files with 104 additions and 83 deletions

View File

@ -24,12 +24,12 @@ static uint8_t s_mode_select_focus = 0;
extern bool InModeFlag;
/* 0902 设计稿:整行位图(背景+图标+文字+箭头)存外部 Flash222x84 */
/* 0902/0819三行模式撑满状态栏下方至底边无底栏222x88 @ y=43/135/225 */
#define UI0902_MODE_ROW_X 9
#define UI0902_MODE_ROW_W 222
#define UI0902_MODE_ROW_H 84
#define UI0902_MODE_ROW_Y0 30
#define UI0902_MODE_ROW_DY 86
#define UI0902_MODE_ROW_H 88
#define UI0902_MODE_ROW_Y0 43
#define UI0902_MODE_ROW_DY 92
static const uint32_t s_mode_row_addr[3][2] = {
{ UI0902_ROW_UNIVERSAL_NOT_ADDR, UI0902_ROW_UNIVERSAL_SEL_ADDR }, /* 0 万能 */
@ -89,17 +89,14 @@ void UI_DrawModeSelectScreen(void)
LCD_FillByColor(0, 0, 240, 320, UI0902_BG_COLOR);
label_top();
UI_ModeSelect_DrawButtons();
Draw_Bottom_Bar(UI0902_NAV_MODE, 0);
/* 设计稿 0902-01模式选择页无底部三栏导航仅三行模式按钮 */
}
const Touch_AreaTypeDef Touch_ModeSelect_Areas[] = {
/* Flag=按钮序号(0万能/1普通/2专业)ID=进入 TAB */
{ 9, 231, 30, 114, 0, 0, 0, NULL },
{ 9, 231, 116, 200, 1, 0, 2, NULL },
{ 9, 231, 202, 286, 2, 0, 1, NULL },
/* 底栏导航:设置 / 调音台(模式选择即本页) */
{ 0, 79, 287, 319, 0, UI0902_NAV_SETTING, GUI_NAV_BAR, NULL },
{ 80, 159, 287, 319, 0, UI0902_NAV_MIXER, GUI_NAV_BAR, NULL },
/* Flag=按钮序号(0万能/1普通/2专业)ID=进入 TAB。三行撑满无底栏 */
{ 9, 231, 43, 130, 0, 0, 0, NULL },
{ 9, 231, 135, 222, 1, 0, 2, NULL },
{ 9, 231, 225, 312, 2, 0, 1, NULL },
};
#define TOUCH_AREA_MODE_SELECT_NUM (sizeof(Touch_ModeSelect_Areas)/sizeof(Touch_AreaTypeDef))
@ -229,13 +226,6 @@ void IdleProcess(DisplayTaskMessage_Type msg)
msg.LoByte >= Touch_ModeSelect_Areas[k].y_min && msg.LoByte <= Touch_ModeSelect_Areas[k].y_max )
{
hit = 1U;
if (Touch_ModeSelect_Areas[k].ID == GUI_NAV_BAR) {
if (Touch_ModeSelect_Areas[k].Value == UI0902_NAV_SETTING)
MenuPage_Setting_Proc();
else if (Touch_ModeSelect_Areas[k].Value == UI0902_NAV_MIXER)
MenuPage_Mixer_Proc();
break;
}
uint8_t row = (uint8_t)Touch_ModeSelect_Areas[k].Flag;
LOG_I("UI", "mode_select hit area=%u row=%u focus=%u x=%u y=%u",
(unsigned)k, (unsigned)row, (unsigned)s_mode_select_focus,
@ -301,7 +291,7 @@ void IdleProcess(DisplayTaskMessage_Type msg)
else if (vol <= 95) level = 3;
else if (vol <= 115) level = 4;
else level = 5;
Draw_Volume_Bar(55, 3, level, 1, WHITE, GRAY);
Draw_Volume_Bar(UI0902_VOL_X, UI0902_VOL_Y, level, 1, WHITE, GRAY);
uint8_t mMasterSysExVolume[12] = { 0xF0, 0x41, 0x00, 0x42, 0x12, 0x40, 0x00, 0x04, 0x64, 0x00, 0x00,0xF7};
memset(mMasterSysExVolume+8,msg.ID,1);
SendMidiDataToDreamDSP(mMasterSysExVolume,12);

View File

@ -418,7 +418,7 @@ void UI_SongMode_Process(DisplayTaskMessage_Type msg)
case MSG_ID_EC_VOL:
Send_volume(msg.ID);
Draw_Volume_Bar(55, 3, msg.HiByte, 1, WHITE, GRAY);
Draw_Volume_Bar(UI0902_VOL_X, UI0902_VOL_Y, msg.HiByte, 1, WHITE, GRAY);
break;
case MSG_ID_TAP_SPEED:

View File

@ -175,11 +175,10 @@ uint8_t UI_SetFocus(uint8_t widget_id)
void label_top()
{
const uint16_t btn_color = /*DARKLGRAY*/0x2988; // 深灰<E6B7B1>?????
//LCD_FillByColor(0,0,240,25,BLACK);
const uint16_t btn_color = /*DARKLGRAY*/0x2988;
LCD_FillByColor(0, 0, 240, UI_STATUS_BAR_H, UI0902_BG_COLOR);
LCD_WR_PIC_Trans(8, 6, 20, 15, gImage_UI0902_Volume_20x15, BLACK);
Draw_Volume_Bar(55, 3, level, 1, WHITE, GRAY);
/* 仅一组动态音量条(音量图标素材本身也是竖条,不再叠绘以免双图标) */
Draw_Volume_Bar(UI0902_VOL_X, UI0902_VOL_Y, level, 1, WHITE, GRAY);
LCD_DrawLine(0, 26, 240, 26, btn_color);
Show_BL_Icon(mGuiData[GUI_BL_SW].Current);
Show_Battery_Icon(usb_charging_state);
@ -245,31 +244,49 @@ void UI_GotoCurrentModePage(void)
}
}
/* 蓝牙图标显示(占位) */
/* 蓝牙图标:设计稿居中 */
void Show_BL_Icon(uint8_t value)
{
if(value)
LCD_FillByColor(UI0902_BT_X, UI0902_BT_Y,
UI0902_BT_X + UI0902_BT_W - 1,
UI0902_BT_Y + UI0902_BT_H - 1, UI0902_BG_COLOR);
if (value)
{
LCD_WR_PIC_Trans(36, 5, 11, 17, gImage_UI0902_Bluetooth_11x17, BLACK);
LCD_WR_PIC_Trans(UI0902_BT_X, UI0902_BT_Y, UI0902_BT_W, UI0902_BT_H,
gImage_UI0902_Bluetooth_11x17, BLACK);
}
else
{
LCD_FillByColor(36, 5, 47, 22, UI0902_BG_COLOR);
}
BSP_BlueToothPowerEnable (value);
BSP_BlueToothPowerEnable(value);
}
void Draw_Status_Battery_Icon(uint8_t battery_percent)
{
uint8_t Info[8];
uint16_t fill_w;
if (battery_percent > 100) battery_percent = 100;
LCD_FillByColor(UI0902_BATTERY_X, UI0902_BATTERY_Y,
UI0902_BATTERY_X + 29, UI0902_BATTERY_Y + 12, UI0902_BG_COLOR);
uint16_t text_w;
uint16_t bat_x;
uint16_t text_x;
uint16_t text_y;
if (battery_percent > 100)
battery_percent = 100;
sprintf((char *)Info, "%d%%", battery_percent);
text_w = (uint16_t)(strlen((char *)Info) * (UI0902_BATTERY_TEXT_SIZE / 2));
bat_x = (uint16_t)(240 - UI0902_BATTERY_RIGHT_MARGIN - text_w
- UI0902_BATTERY_TEXT_GAP - UI0902_BATTERY_W);
text_x = (uint16_t)(bat_x + UI0902_BATTERY_W + UI0902_BATTERY_TEXT_GAP);
text_y = (uint16_t)((UI_STATUS_BAR_H - UI0902_BATTERY_TEXT_SIZE) / 2);
LCD_FillByColor(bat_x > 2 ? (uint16_t)(bat_x - 2) : 0, 0, 240, UI_STATUS_BAR_H,
UI0902_BG_COLOR);
fill_w = (uint16_t)((22UL * battery_percent) / 100);
LCD_WR_PIC_ClipX(UI0902_BATTERY_X + 3, UI0902_BATTERY_Y + 2, 22, 9, fill_w,
LCD_WR_PIC_ClipX(bat_x + 3, UI0902_BATTERY_Y + 2, 22, 9, fill_w,
gImage_UI0902_BatteryFill_22x9);
LCD_WR_PIC_Trans(UI0902_BATTERY_X, UI0902_BATTERY_Y, 29, 12,
LCD_WR_PIC_Trans(bat_x, UI0902_BATTERY_Y, UI0902_BATTERY_W, UI0902_BATTERY_H,
gImage_UI0902_BatteryOutline_29x12, BLACK);
LCD_ShowString(text_x, text_y, Info, WHITE, UI0902_BG_COLOR,
UI0902_BATTERY_TEXT_SIZE, 0);
}
void Show_Battery_Icon(uint8_t flag)

View File

@ -31,13 +31,24 @@ void CallUI_RestoreSelect(void);
/* 0902 设计稿配色与底栏导航 */
#define UI0902_BG_COLOR 0x0042 /* #000813 页面背景 */
#define UI0902_ROW_BG 0x2147 /* #242B3A 列表行背景 */
#define UI0902_BATTERY_X 203
/* 0902 状态栏:左音量条 / 中蓝牙 / 右电池+百分比(对齐屏幕参考 0902-01 */
#define UI0902_VOL_X 8
#define UI0902_VOL_Y 3
#define UI0902_BT_W 11
#define UI0902_BT_H 17
#define UI0902_BT_X ((240 - UI0902_BT_W) / 2) /* 居中 */
#define UI0902_BT_Y 5
#define UI0902_BATTERY_W 29
#define UI0902_BATTERY_H 12
#define UI0902_BATTERY_Y 7
#define UI0902_BATTERY_RIGHT_MARGIN 8
#define UI0902_BATTERY_TEXT_GAP 4
#define UI0902_BATTERY_TEXT_SIZE 16
#define UI0902_NAV_SETTING 0 /* 底栏位0设置设置页内为返回 */
#define UI0902_NAV_MIXER 1 /* 底栏位1调音台 */
#define UI0902_NAV_MODE 2 /* 底栏位2模式选择 */
#define UI0902_NAV_NONE 0xFF
/* 电量组在 Draw_Battery_Icon 内按右对齐重算;此值为允许的最左起点 */
/* 旧几何电池Draw_Battery_Icon最左起点0902 图电池用 Draw_Status_Battery_Icon */
#define UI_STATUS_BATTERY_X 150
#define UI_STATUS_BATTERY_H 11
#define UI_STATUS_BATTERY_Y ((UI_STATUS_BAR_H - UI_STATUS_BATTERY_H) / 2)

View File

@ -1780,7 +1780,7 @@ void Draw_Volume_Bar(uint16_t x, uint16_t y, uint8_t volume_level,uint8_t scale,
}
clear_w = (uint16_t)(w + gap) * bar_count - gap;
LCD_FillByColor(x, y, x + clear_w, y + max_h * scale, BLACK);
LCD_FillByColor(x, y, x + clear_w, y + max_h * scale, UI0902_BG_COLOR);
if(volume_level == 0)
{

View File

@ -4,29 +4,29 @@
/* Auto-generated by tools/gen_ui0902_assets.py. W25Q128 resource map. */
#define UI0902_RES_BASE 0x00100000UL
#define UI0902_ROW_UNIVERSAL_NOT_ADDR (UI0902_RES_BASE + 0x000000UL) /* 37296 bytes */
#define UI0902_ROW_UNIVERSAL_NOT_ADDR (UI0902_RES_BASE + 0x000000UL) /* 39072 bytes */
#define UI0902_ROW_UNIVERSAL_NOT_W 222
#define UI0902_ROW_UNIVERSAL_NOT_H 84
#define UI0902_ROW_UNIVERSAL_NOT_H 88
#define UI0902_ROW_UNIVERSAL_SEL_ADDR (UI0902_RES_BASE + 0x00A000UL) /* 37296 bytes */
#define UI0902_ROW_UNIVERSAL_SEL_ADDR (UI0902_RES_BASE + 0x00A000UL) /* 39072 bytes */
#define UI0902_ROW_UNIVERSAL_SEL_W 222
#define UI0902_ROW_UNIVERSAL_SEL_H 84
#define UI0902_ROW_UNIVERSAL_SEL_H 88
#define UI0902_ROW_NORMAL_NOT_ADDR (UI0902_RES_BASE + 0x014000UL) /* 37296 bytes */
#define UI0902_ROW_NORMAL_NOT_ADDR (UI0902_RES_BASE + 0x014000UL) /* 39072 bytes */
#define UI0902_ROW_NORMAL_NOT_W 222
#define UI0902_ROW_NORMAL_NOT_H 84
#define UI0902_ROW_NORMAL_NOT_H 88
#define UI0902_ROW_NORMAL_SEL_ADDR (UI0902_RES_BASE + 0x01E000UL) /* 37296 bytes */
#define UI0902_ROW_NORMAL_SEL_ADDR (UI0902_RES_BASE + 0x01E000UL) /* 39072 bytes */
#define UI0902_ROW_NORMAL_SEL_W 222
#define UI0902_ROW_NORMAL_SEL_H 84
#define UI0902_ROW_NORMAL_SEL_H 88
#define UI0902_ROW_EXPERT_NOT_ADDR (UI0902_RES_BASE + 0x028000UL) /* 37296 bytes */
#define UI0902_ROW_EXPERT_NOT_ADDR (UI0902_RES_BASE + 0x028000UL) /* 39072 bytes */
#define UI0902_ROW_EXPERT_NOT_W 222
#define UI0902_ROW_EXPERT_NOT_H 84
#define UI0902_ROW_EXPERT_NOT_H 88
#define UI0902_ROW_EXPERT_SEL_ADDR (UI0902_RES_BASE + 0x032000UL) /* 37296 bytes */
#define UI0902_ROW_EXPERT_SEL_ADDR (UI0902_RES_BASE + 0x032000UL) /* 39072 bytes */
#define UI0902_ROW_EXPERT_SEL_W 222
#define UI0902_ROW_EXPERT_SEL_H 84
#define UI0902_ROW_EXPERT_SEL_H 88
#define UI0902_ICON_UNIVERSAL_SEL_ADDR (UI0902_RES_BASE + 0x03C000UL) /* 2730 bytes */
#define UI0902_ICON_UNIVERSAL_SEL_W 39

View File

@ -102,6 +102,8 @@ def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--bin", default=DEFAULT_BIN)
parser.add_argument("--device", default="Cortex-M4")
parser.add_argument("--no-reset", action="store_true",
help="Skip hardware reset (device must already be running)")
args = parser.parse_args()
if not os.path.isfile(args.bin):
@ -121,45 +123,47 @@ def main() -> None:
time.sleep(0.3)
_ = rtt_read_text(jlink, 0.3)
# Hardware reset clears any stuck flash_mode from a previous attempt
print("hardware reset...")
jlink.reset(halt=False)
time.sleep(3.5)
# RTT CB may move after reboot — re-find
try:
jlink.rtt_stop()
except Exception:
pass
cb = find_rtt_control_block(jlink)
if cb is None:
raise SystemExit("RTT CB missing after reset")
print(f"RTT CB @ 0x{cb:08X}")
jlink.rtt_start(cb)
time.sleep(0.5)
_ = rtt_read_text(jlink, 0.5)
if not args.no_reset:
# Hardware reset clears any stuck flash_mode from a previous attempt
print("hardware reset...")
jlink.reset(halt=False)
time.sleep(3.5)
# RTT CB may move after reboot — re-find
try:
jlink.rtt_stop()
except Exception:
pass
cb = find_rtt_control_block(jlink)
if cb is None:
raise SystemExit("RTT CB missing after reset")
print(f"RTT CB @ 0x{cb:08X}")
jlink.rtt_start(cb)
time.sleep(0.5)
_ = rtt_read_text(jlink, 0.5)
cmd = f"flash ui0902 {size}\n".encode("ascii")
send_bytes(jlink, cmd)
print("sent flash command, waiting GO...")
log = wait_for(jlink, "FLASH_UI0902_GO", 15.0)
log = wait_for(jlink, "FLASH_UI0902_GO", 20.0)
print(log.strip().splitlines()[-1])
time.sleep(0.2)
# wait for first byte confirmation after starting transfer
chunk = 256
# Smaller chunks + brief pause so erase/write can keep up with RTT down-buffer
chunk = 128
sent = 0
t0 = time.time()
# prime first page then expect FLASH_RX
send_bytes(jlink, data[0:256])
sent = 256
wait_for(jlink, "FLASH_RX", 15.0)
wait_for(jlink, "FLASH_RX", 20.0)
print("device receiving...")
while sent < size:
end = min(sent + chunk, size)
send_bytes(jlink, data[sent:end])
sent = end
if sent % 256 == 0:
time.sleep(0.002)
if sent % 4096 == 0 or sent == size:
log = wait_for(jlink, "FLASH_ACK", 30.0)
log = wait_for(jlink, "FLASH_ACK", 60.0)
if "FLASH_UI0902_OK" in log:
print(log.strip().splitlines()[-1])
print("Done. Mode-select should redraw with new assets.")
@ -168,7 +172,7 @@ def main() -> None:
elapsed = time.time() - t0
print(f" {sent}/{size} ({100.0 * sent / size:.1f}%) {elapsed:.1f}s")
log = wait_for(jlink, "FLASH_UI0902_OK", 30.0)
log = wait_for(jlink, "FLASH_UI0902_OK", 60.0)
print(log.strip().splitlines()[-1])
print("Done. Mode-select should redraw with new assets.")
finally:

View File

@ -23,8 +23,8 @@ OUT_BIN = OUT / "ui0902_res.bin"
RES_BASE = 0x00100000 # W25Q128 offset; avoids logo(0x0)/charge(0xCB70)/preset(<~0xA8B60) regions
ALIGN = 0x1000
# 222x89 rows do not fit 3x between status bar (26) and bottom bar (285); squash to 84px
ROW_RESIZE = (222, 84)
# 模式选择页无底栏:三行按设计稿 0902-01 撑满(约 y=43/135/225高 88
ROW_RESIZE = (222, 88)
MISANS = r"c:\Users\qjyu\Documents\SoundWalker\一诺国际吉他\Doc\UI\MiSans\MiSans 字体\MiSans-Medium.ttf"
PAGE_BG = (0, 8, 19) # #000813

View File

@ -64,12 +64,11 @@ def text_center(d, cx, y, s, size, color=(255, 255, 255)):
# ---------- 1. mode select ----------
p, d = new_page()
draw_status_bar(p, d)
rows = [("04_模式图标/完整版/模式界面栏背景-17.png", 30),
("04_模式图标/完整版/模式界面栏背景-18.png", 116),
("04_模式图标/完整版/模式界面栏背景-20.png", 202)]
rows = [("04_模式图标/完整版/模式界面栏背景-19.png", 43),
("04_模式图标/完整版/模式界面栏背景-17.png", 135),
("04_模式图标/完整版/模式界面栏背景-18.png", 225)]
for rel, y in rows:
paste_trans(p, load(rel).resize((222, 84), Image.LANCZOS), 9, y)
draw_bottom_bar(p, d, sel=2)
paste_trans(p, load(rel).resize((222, 88), Image.LANCZOS), 9, y)
p.save(OUT / "preview_01_mode_select.png")
# ---------- 2. mixer ----------