Compare commits
9 Commits
f1f9520637
...
c1b32c0351
| Author | SHA1 | Date |
|---|---|---|
|
|
c1b32c0351 | |
|
|
bb6d9ae7f9 | |
|
|
99f7ed3e5a | |
|
|
cd21e4e45e | |
|
|
92e5e32de3 | |
|
|
015417dc3d | |
|
|
6f2f7b6b75 | |
|
|
ba110565f1 | |
|
|
ad0d990c55 |
|
|
@ -280,6 +280,12 @@ uint8_t app_log_try_command(const char *cmd)
|
|||
"FLASH_HAITIAN_GO", "FLASH_HAITIAN_OK\n");
|
||||
return 1U;
|
||||
}
|
||||
if (strncmp(cmd, "flash bin3 ", 11) == 0) {
|
||||
uint32_t size = (uint32_t)strtoul(cmd + 11, NULL, 10);
|
||||
app_log_flash_start_at(EXTFLASH_BIN3_UNIVERSAL_ADDR, size,
|
||||
"FLASH_BIN3_GO", "FLASH_BIN3_OK\n");
|
||||
return 1U;
|
||||
}
|
||||
if (strncmp(cmd, "tone status", 11) == 0) {
|
||||
uint8_t hdr[16];
|
||||
uint8_t name[40];
|
||||
|
|
@ -320,6 +326,16 @@ uint8_t app_log_try_command(const char *cmd)
|
|||
(unsigned long)(cur_hdr[12] | (cur_hdr[13] << 8) |
|
||||
(cur_hdr[14] << 16) | (cur_hdr[15] << 24)));
|
||||
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
|
||||
{
|
||||
uint8_t b3[16];
|
||||
W25Q128_Read(b3, EXTFLASH_BIN3_UNIVERSAL_ADDR, 16);
|
||||
snprintf(line, sizeof(line),
|
||||
"TONE @BIN3 magic=%02X%02X%02X%02X cnt=%lu\n",
|
||||
b3[0], b3[1], b3[2], b3[3],
|
||||
(unsigned long)(b3[12] | (b3[13] << 8) |
|
||||
(b3[14] << 16) | (b3[15] << 24)));
|
||||
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
|
||||
}
|
||||
snprintf(line, sizeof(line),
|
||||
"TONE @BIN2 magic=%02X%02X%02X%02X max=%lu cnt=%lu name=%.16s\n",
|
||||
hdr[0], hdr[1], hdr[2], hdr[3],
|
||||
|
|
@ -365,6 +381,20 @@ uint8_t app_log_try_command(const char *cmd)
|
|||
return 1U;
|
||||
}
|
||||
if (strncmp(cmd, "tone pick", 9) == 0) {
|
||||
/* tone pick uni → 万能;默认仍测专业+本地曲目 */
|
||||
if (cmd[9] == ' ' && (cmd[10] == 'u' || cmd[10] == 'U')) {
|
||||
mGuiData[GUI_TAB_INDEX].Current = 0;
|
||||
mGuiData[GUI_AUTOBAND_SW].Current = 0;
|
||||
UI_ApplyToneAddress();
|
||||
AutoBandTop1_Stop();
|
||||
StartFlag = 0;
|
||||
(void)AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current);
|
||||
KEY_ID_1629 = 0;
|
||||
PressFlag = 0;
|
||||
Pick_Handle();
|
||||
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "TONE_PICK_UNI_DONE\n");
|
||||
return 1U;
|
||||
}
|
||||
/* 模拟专业+本地曲目拨片(无和弦板) */
|
||||
mGuiData[GUI_TAB_INDEX].Current = 1;
|
||||
mGuiData[GUI_AUTOBAND_SW].Current = 1;
|
||||
|
|
|
|||
|
|
@ -89,11 +89,11 @@ const Touch_AreaTypeDef Touch_Areas[] =
|
|||
{ 126, 172, UI0902_SECTION_Y, UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1, 0, 2, GUI_PLAY_SECTION, NULL },
|
||||
{ 183, 229, UI0902_SECTION_Y, UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1, 0, 3, GUI_PLAY_SECTION, NULL },
|
||||
|
||||
/* 底栏四键:万能 | 普通 | 专业 | 设置(触区中线对齐 cx 40/100/160/210) */
|
||||
{ 0, 69, 280, 319, 0, UI0902_NAV_UNIVERSAL, GUI_NAV_BAR, NULL },
|
||||
{ 70, 129, 280, 319, 0, UI0902_NAV_NORMAL, GUI_NAV_BAR, NULL },
|
||||
{ 130, 184, 280, 319, 0, UI0902_NAV_EXPERT, GUI_NAV_BAR, NULL },
|
||||
{ 185, 239, 280, 319, 0, UI0902_NAV_SETTING, GUI_NAV_BAR, NULL },
|
||||
/* 底栏四键:万能 | 普通 | 专业 | 设置(60×55,y265–319) */
|
||||
{ UI0902_NAV_X0(0), UI0902_NAV_X1(0), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_UNIVERSAL, GUI_NAV_BAR, NULL },
|
||||
{ UI0902_NAV_X0(1), UI0902_NAV_X1(1), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_NORMAL, GUI_NAV_BAR, NULL },
|
||||
{ UI0902_NAV_X0(2), UI0902_NAV_X1(2), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_EXPERT, GUI_NAV_BAR, NULL },
|
||||
{ UI0902_NAV_X0(3), UI0902_NAV_X1(3), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_SETTING, GUI_NAV_BAR, NULL },
|
||||
};
|
||||
|
||||
/* 推子 0~10 共 11 档;档10 取 AW816 上限附近 */
|
||||
|
|
@ -101,7 +101,7 @@ int16_t MIC_VOL_MAP[11]= {-9000,-7000,-5000,-3000,-1000,1000,3000,5000,7000,9000
|
|||
|
||||
uint8_t TOUCH_AREA_NUM = (sizeof(Touch_Areas)/sizeof(Touch_AreaTypeDef));
|
||||
|
||||
const uint8_t Version[3] = {0,2,6}; /* major.minor.patch — 0.2.6: local song 2.bin from Doc/音色文件/0908/2ss_2.bin */
|
||||
const uint8_t Version[3] = {0,2,7}; /* major.minor.patch — 0.2.7: 1.bin from Doc/音色文件/0909/31cs.bin; fixed BIN3 pack */
|
||||
|
||||
uint8_t Led = 8;
|
||||
bool PressFlag = 0;
|
||||
|
|
@ -783,15 +783,10 @@ void Pick_Handle()
|
|||
AutoBandTop1_SetLoopMode(0,1);
|
||||
break;
|
||||
case 0:
|
||||
if(PressFlag)
|
||||
{
|
||||
AutoBandTop1_SetLoopMode(0,1);
|
||||
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
||||
}else
|
||||
{
|
||||
/* 设置页内沿用万能:与主路径一致,始终 loop */
|
||||
AutoBandTop1_SetPiecePlayMode(1, 0, 480);
|
||||
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
||||
}
|
||||
AutoBandTop1_SetLoopMode(0, 1);
|
||||
StartFlag = 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -842,17 +837,14 @@ void Pick_Handle()
|
|||
}
|
||||
break;
|
||||
case 0:
|
||||
/* 万能:和弦走向用 piece 模式;须 SetLoopMode,否则无循环时常听不到伴奏 */
|
||||
StartFlag = 1;
|
||||
if(PressFlag)
|
||||
{
|
||||
AutoBandTop1_SetPiecePlayMode(1, 0, 480);
|
||||
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
||||
AutoBandTop1_SetLoopMode(0, 1);
|
||||
}else
|
||||
{
|
||||
AutoBandTop1_SetPiecePlayMode(1,0,480);
|
||||
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
||||
}
|
||||
LOG_I("pick", "universal piece name=%s press=%u notes=%u chord=%u",
|
||||
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
|
||||
(unsigned)PressFlag, (unsigned)note_cnt, (unsigned)chord_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,8 +148,12 @@ void App_Auto_Init(void)
|
|||
int ret = AutoBandTop1_LoadPresetItemFromFlash(0);
|
||||
if (ret != 0)
|
||||
{
|
||||
LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X map=%s",
|
||||
ret, (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS));
|
||||
uint8_t hdr[16];
|
||||
W25Q128_Read(hdr, (uint32_t)ADDRESS, 16);
|
||||
LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X map=%s magic=%02X%02X%02X%02X cnt=%lu",
|
||||
ret, (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS),
|
||||
hdr[0], hdr[1], hdr[2], hdr[3],
|
||||
(unsigned long)(hdr[12] | (hdr[13] << 8) | (hdr[14] << 16) | (hdr[15] << 24)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,12 +43,6 @@ static void UI_ModeSelect_DrawButtons(void)
|
|||
LCD_WR_PIC_FROM_FLASH(UI0902_MODE_ROW_X, y,
|
||||
UI0902_MODE_ROW_W, UI0902_MODE_ROW_H,
|
||||
s_mode_row_addr[i]);
|
||||
/* 万能模式素材顶部有残留近白横线(相对行内 y=7/8),盖成行底色 */
|
||||
if (i == 0) {
|
||||
LCD_FillByColor(UI0902_MODE_ROW_X, (uint16_t)(y + 7),
|
||||
(uint16_t)(UI0902_MODE_ROW_X + UI0902_MODE_ROW_W),
|
||||
(uint16_t)(y + 9), UI0902_ROW_BG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ const Touch_AreaTypeDef Touch_SetingSelect_Areas[] = {
|
|||
{ UI0902_HUB_CARD_X, (uint16_t)(UI0902_HUB_CARD_X + UI0902_HUB_CARD_W - 1),
|
||||
UI0902_HUB_CARD_Y1, (uint16_t)(UI0902_HUB_CARD_Y1 + UI0902_HUB_CARD_H - 1),
|
||||
0, 0, GUI_SETING_SELECT3, NULL },
|
||||
/* 底栏四键 */
|
||||
{ 0, 69, 280, 319, 0, UI0902_NAV_UNIVERSAL, GUI_NAV_BAR, NULL },
|
||||
{ 70, 129, 280, 319, 0, UI0902_NAV_NORMAL, GUI_NAV_BAR, NULL },
|
||||
{ 130, 184, 280, 319, 0, UI0902_NAV_EXPERT, GUI_NAV_BAR, NULL },
|
||||
{ 185, 239, 280, 319, 0, UI0902_NAV_SETTING, GUI_NAV_BAR, NULL },
|
||||
/* 底栏四键:与 Touch_Areas 共用 UI0902_NAV_* 几何 */
|
||||
{ UI0902_NAV_X0(0), UI0902_NAV_X1(0), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_UNIVERSAL, GUI_NAV_BAR, NULL },
|
||||
{ UI0902_NAV_X0(1), UI0902_NAV_X1(1), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_NORMAL, GUI_NAV_BAR, NULL },
|
||||
{ UI0902_NAV_X0(2), UI0902_NAV_X1(2), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_EXPERT, GUI_NAV_BAR, NULL },
|
||||
{ UI0902_NAV_X0(3), UI0902_NAV_X1(3), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_SETTING, GUI_NAV_BAR, NULL },
|
||||
};
|
||||
|
||||
#define TOUCH_AREA_SETING_SELECT_NUM (sizeof(Touch_SetingSelect_Areas)/sizeof(Touch_AreaTypeDef))
|
||||
|
|
|
|||
105
UI/UI_global.c
105
UI/UI_global.c
|
|
@ -168,10 +168,23 @@ void UI_ReloadTonePreset(void)
|
|||
if (mGuiData[GUI_TAB_INDEX].Current == 0)
|
||||
{
|
||||
int ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current);
|
||||
if (ret != 0)
|
||||
{
|
||||
uint8_t hdr[16];
|
||||
W25Q128_Read(hdr, (uint32_t)ADDRESS, 16);
|
||||
LOG_E("tone", "load universal idx=%d ret=%d magic=%02X%02X%02X%02X cnt=%lu addr=0x%08X",
|
||||
ParamGuiData[ALL_MODE_PARAM].Current, ret,
|
||||
hdr[0], hdr[1], hdr[2], hdr[3],
|
||||
(unsigned long)(hdr[12] | (hdr[13] << 8) | (hdr[14] << 16) | (hdr[15] << 24)),
|
||||
(unsigned)ADDRESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_I("tone", "load universal idx=%d ret=%d name=%s addr=0x%08X map=BIN3@0x%08X",
|
||||
ParamGuiData[ALL_MODE_PARAM].Current, ret,
|
||||
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
|
||||
(unsigned)ADDRESS, (unsigned)EXTFLASH_BIN3_UNIVERSAL_ADDR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* 普通/专业:本地曲目或节奏类型 */
|
||||
|
|
@ -443,6 +456,58 @@ void Show_Battery_Icon(uint8_t flag)
|
|||
}
|
||||
|
||||
|
||||
/* 中英文混排像素宽(与 LCD_GetMixedStringWidth 规则一致:CJK=sizey,ASCII=UI0902_ASCII_W) */
|
||||
static uint16_t UI_MixedTextWidth(const uint8_t *str, uint8_t sizey)
|
||||
{
|
||||
uint16_t width = 0;
|
||||
uint8_t ascii_w = UI0902_ASCII_W(sizey);
|
||||
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
while (*str != '\0') {
|
||||
if (*str > 0x80) {
|
||||
width = (uint16_t)(width + sizey);
|
||||
str += 2;
|
||||
} else {
|
||||
width = (uint16_t)(width + ascii_w);
|
||||
str += 1;
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
/* 曲目/音色共用:序号与名称间距(与「1.名称」拼接一致,保证两行视觉间距相同) */
|
||||
#define UI0902_INDEX_NAME_GAP 0
|
||||
|
||||
/* 在 [x1,x2] 内整体居中绘制「N.」+ 名称(无渐变,show_Param 用) */
|
||||
static void UI_DrawIndexedName_Mixed(uint16_t x1, uint16_t x2, uint16_t text_y,
|
||||
const char *num, const char *name,
|
||||
uint16_t fc, uint16_t bc, uint8_t sizey)
|
||||
{
|
||||
char line[48] = {0};
|
||||
snprintf(line, sizeof(line), "%s%s", num, name);
|
||||
LCD_ShowMixedString(x1, x2, text_y, (uint8_t *)line, LCD_ALIGN_CENTER,
|
||||
fc, bc, sizey, 1);
|
||||
}
|
||||
|
||||
/* 在 [x1,x2] 内整体居中绘制「N.」+ 中文名(渐变底,音色行用);间隙与曲目行一致 */
|
||||
static void UI_DrawIndexedName_Grad(uint16_t x1, uint16_t x2, uint16_t text_y,
|
||||
const char *num, const uint8_t *name,
|
||||
uint16_t fc, uint16_t c_top, uint16_t c_bot, uint8_t sizey)
|
||||
{
|
||||
uint16_t num_w = (uint16_t)(strlen(num) * UI0902_ASCII_W(sizey));
|
||||
uint16_t name_w = UI_MixedTextWidth(name, sizey);
|
||||
uint16_t total_w = (uint16_t)(num_w + UI0902_INDEX_NAME_GAP + name_w);
|
||||
uint16_t area_w = (x2 > x1) ? (uint16_t)(x2 - x1) : 0;
|
||||
uint16_t start_x = (total_w >= area_w) ? x1 : (uint16_t)(x1 + (area_w - total_w) / 2);
|
||||
uint16_t name_x = (uint16_t)(start_x + num_w + UI0902_INDEX_NAME_GAP);
|
||||
|
||||
LCD_ShowString_AutoAlign_Gradient(start_x, (uint16_t)(start_x + num_w), text_y,
|
||||
(const uint8_t *)num, LCD_ALIGN_LEFT, fc, c_top, c_bot, sizey);
|
||||
LCD_ShowChinese_AutoAlign_Gradient(name_x, x2, text_y, name, LCD_ALIGN_LEFT,
|
||||
fc, c_top, c_bot, sizey);
|
||||
}
|
||||
|
||||
void show_Param(const LEBEL_UI *UI, GUI_SWITCH *Group)
|
||||
{
|
||||
char Textstr_new[40] = {0};
|
||||
|
|
@ -453,6 +518,7 @@ void show_Param(const LEBEL_UI *UI, GUI_SWITCH *Group)
|
|||
const uint16_t text_y = row_y + (row_h - UI->Text_size) / 2 + 1;
|
||||
uint8_t idx = Group->Current + 1;
|
||||
uint16_t c_top, c_bot;
|
||||
uint16_t x1, x2;
|
||||
|
||||
UI_GetFocusGrad(GUI_MODE_PARAM, &c_top, &c_bot);
|
||||
|
||||
|
|
@ -464,18 +530,16 @@ void show_Param(const LEBEL_UI *UI, GUI_SWITCH *Group)
|
|||
c_top, c_bot
|
||||
);
|
||||
|
||||
// 拼接序号 1. 2. 3.
|
||||
/* 序号+名称整体居中(约等于标注「右移7px」的视觉效果) */
|
||||
sprintf(NumBuf, "%d.", idx);
|
||||
uint16_t num_w = (uint16_t)(strlen(NumBuf) * UI0902_ASCII_W(UI->Text_size));
|
||||
if (Group->Id == EXPRESS_MODE_PARAM && Group->Current < LOCAL_SONG_COUNT)
|
||||
strcpy(Textstr_new, LocalSongNameGbk[Group->Current]);
|
||||
else
|
||||
GET_ParamNum_Str(Group->Current, 0, Textstr_new, Group->Id);
|
||||
uint32_t string_start_x = LCD_ShowMixedString(UI->x+10, UI->x + UI->w, text_y, (uint8_t *)Textstr_new , LCD_ALIGN_CENTER, WHITE, BLACK, UI->Text_size, 1);
|
||||
//序号X = 中文起点 - 数字宽度 - 5,间<EFBC8C>?像素
|
||||
uint16_t num_x = string_start_x - num_w;
|
||||
// 绘制左侧数字序号
|
||||
LCD_ShowString(num_x, text_y, (const uint8_t *)NumBuf, UI->Text_fc, UI->Text_bc, UI->Text_size, 1);
|
||||
x1 = (uint16_t)(UI->x + 10);
|
||||
x2 = (uint16_t)(UI->x + UI->w - 10);
|
||||
UI_DrawIndexedName_Mixed(x1, x2, text_y, NumBuf, Textstr_new,
|
||||
WHITE, BLACK, UI->Text_size);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -851,22 +915,17 @@ void label_TimbreSelece_ui(const LEBEL_UI *UI, GUI_SWITCH * Group)
|
|||
c_top, c_bot
|
||||
);
|
||||
sprintf(NumBuf, "%d.", idx);
|
||||
uint16_t num_w = (uint16_t)(strlen(NumBuf) * UI0902_ASCII_W(UI->Text_size));
|
||||
GET_Num_Str(mGuiData[UI->Id].Current, 0, (char*)Textstr, UI->Id);
|
||||
uint32_t chinese_start_x = LCD_ShowChinese_AutoAlign_Gradient(
|
||||
UI->x + num_w,
|
||||
UI->x + UI->w,
|
||||
UI_DrawIndexedName_Grad(
|
||||
(uint16_t)(UI->x + 10),
|
||||
(uint16_t)(UI->x + UI->w - 10),
|
||||
text_y,
|
||||
(const uint8_t*)Textstr,
|
||||
UI->Text_Align,
|
||||
NumBuf,
|
||||
Textstr,
|
||||
UI->Text_fc,
|
||||
c_top, c_bot,
|
||||
UI->Text_size
|
||||
);
|
||||
uint16_t num_x = chinese_start_x - num_w - 5;
|
||||
LCD_ShowString_AutoAlign_Gradient(num_x, num_x + num_w, text_y,
|
||||
(const uint8_t *)NumBuf, LCD_ALIGN_LEFT, UI->Text_fc,
|
||||
c_top, c_bot, UI->Text_size);
|
||||
switch(Group->Id)
|
||||
{
|
||||
case GUI_TIMBRE_SELECT:
|
||||
|
|
@ -988,7 +1047,7 @@ void Draw_Tab_Section_Menu(const LEBEL_UI *UI, TAB_SECTION_INDEX select_tab)
|
|||
const uint8_t pitch = UI0902_SECTION_PITCH;
|
||||
const uint16_t start_x = UI0902_MARGIN_X; /* 0902-04:12/69/126/183 */
|
||||
const uint16_t start_y = UI->y;
|
||||
/* 0819:演奏段落数字与标题同为 13,按钮内垂直居中(视觉重心 +1) */
|
||||
/* 0819:演奏段落数字与标题同为 13,按钮内垂直居中(视觉重心 +1);稿面数字整体右移 2px */
|
||||
const uint16_t text_y = start_y + (tab_h - UI0902_FONT_BODY) / 2 + 1;
|
||||
const char *nums[4] = {"1", "2", "3", "4"};
|
||||
uint8_t i;
|
||||
|
|
@ -999,7 +1058,7 @@ void Draw_Tab_Section_Menu(const LEBEL_UI *UI, TAB_SECTION_INDEX select_tab)
|
|||
uint16_t bg = (select_tab == i) ? COLOR_GRAD_MID : UI0902_ROW_BG;
|
||||
LCD_FillRoundRect(x, start_y, tab_w, tab_h, tab_r, bg);
|
||||
LCD_ShowString_AutoAlign(
|
||||
x, (uint16_t)(x + tab_w - 1), text_y,
|
||||
(uint16_t)(x + 2), (uint16_t)(x + tab_w - 1 + 2), text_y,
|
||||
(const uint8_t*)nums[i],
|
||||
UI->Text_Align,
|
||||
WHITE,
|
||||
|
|
@ -1269,8 +1328,12 @@ void Draw_System_Item_List(const LEBEL_UI *UI, GUI_SWITCH *Group)
|
|||
(uint16_t)(UI->y + (UI->h - UI0902_ICON_SYS_RESTORE_H) / 2),
|
||||
UI0902_ICON_SYS_RESTORE_W, UI0902_ICON_SYS_RESTORE_H,
|
||||
UI0902_ICON_SYS_RESTORE_ADDR);
|
||||
/* 右三角与蓝牙/自动关机同一右缘(val_x2) */
|
||||
UI_DrawTriRight((uint16_t)(val_x2 - UI0902_TRI_R_W), chev_cy);
|
||||
/* 0902-07:右侧为进入下级页的细 chevron(设置图标-03),勿用实心三角 */
|
||||
LCD_WR_PIC_FROM_FLASH_Trans(
|
||||
(uint16_t)(val_x2 - UI0902_ICON_CHEVRON_W),
|
||||
(uint16_t)(UI->y + (UI->h - UI0902_ICON_CHEVRON_H) / 2),
|
||||
UI0902_ICON_CHEVRON_W, UI0902_ICON_CHEVRON_H,
|
||||
UI0902_ICON_CHEVRON_ADDR);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,12 @@ void CallUI_RestoreSelect(void);
|
|||
#define UI0902_NAV_EXPERT 2 /* 底栏:专业模式 */
|
||||
#define UI0902_NAV_SETTING 3 /* 底栏:设置 */
|
||||
#define UI0902_NAV_NONE 0xFF
|
||||
/* 底栏触区:段落钮底(y264)之下四等分全宽,各 60×55 */
|
||||
#define UI0902_NAV_Y0 265
|
||||
#define UI0902_NAV_Y1 319
|
||||
#define UI0902_NAV_W 60
|
||||
#define UI0902_NAV_X0(i) ((uint16_t)((i) * UI0902_NAV_W))
|
||||
#define UI0902_NAV_X1(i) ((uint16_t)((i) * UI0902_NAV_W + UI0902_NAV_W - 1))
|
||||
/* 兼容旧名(已废弃三键:设置|调音台|模式) */
|
||||
#define UI0902_NAV_MIXER UI0902_NAV_NORMAL
|
||||
#define UI0902_NAV_MODE UI0902_NAV_EXPERT
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#define EXTFLASH_CHARGING_H 190
|
||||
|
||||
#define EXTFLASH_BIN1_RHYTHM_ADDR 0x0001B8F0UL
|
||||
#define EXTFLASH_BIN1_RHYTHM_SIZE 530317UL
|
||||
#define EXTFLASH_BIN1_RHYTHM_SIZE 530273UL
|
||||
|
||||
#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x0009D07DUL
|
||||
#define EXTFLASH_BIN2_SONG_HAITIAN_SIZE 41263UL
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
/* Auto-generated by tools/gen_git_user_fw_ver.py — do not edit.
|
||||
* git HEAD (dirty): 57448b3da8a3f912503e9d50313b5ec635888b69
|
||||
* git HEAD (dirty): bb6d9ae7f9987ec6cfb3ab50579951d2b58ed888
|
||||
* branch: develop → develop
|
||||
* 01 0C wire: develop_57448b*
|
||||
* 01 0C wire: develop_bb6d9a*
|
||||
*/
|
||||
#ifndef GIT_USER_FW_VER_H
|
||||
#define GIT_USER_FW_VER_H
|
||||
|
||||
#define GIT_COMMIT_ID_FULL "57448b3da8a3f912503e9d50313b5ec635888b69"
|
||||
#define GIT_COMMIT_ID_FULL "bb6d9ae7f9987ec6cfb3ab50579951d2b58ed888"
|
||||
#define GIT_BRANCH_NAME "develop"
|
||||
#define GIT_COMMIT_SHORT6 "57448b"
|
||||
#define GIT_COMMIT_SHORT6 "bb6d9a"
|
||||
#define GIT_DIRTY (1)
|
||||
#define GIT_BUILD_ID "develop_57448b*"
|
||||
#define GIT_BUILD_ID "develop_bb6d9a*"
|
||||
#define GIT_BUILD_ID_LEN 15u
|
||||
|
||||
#endif /* GIT_USER_FW_VER_H */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Flash Doc/音色文件/0903/3.bin to W25Q128 @ EXTFLASH_BIN3_UNIVERSAL_ADDR via RTT.
|
||||
|
||||
Requires firmware that accepts RTT command: flash bin3 <size>
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import time
|
||||
|
||||
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
REPO = os.path.dirname(os.path.dirname(ROOT))
|
||||
DEFAULT_BIN = os.path.join(REPO, "Doc", "音色文件", "0903", "3.bin")
|
||||
EXTFLASH_BIN3_UNIVERSAL_ADDR = 0x000A71AC
|
||||
DAB_MAGIC = bytes((0xAB, 0x44, 0x41, 0x42))
|
||||
|
||||
|
||||
def connect_jlink(device: str):
|
||||
import pylink
|
||||
|
||||
jlink = pylink.JLink()
|
||||
jlink.open()
|
||||
try:
|
||||
jlink.exec_command("HideDeviceSelection = 1")
|
||||
except Exception:
|
||||
pass
|
||||
jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
|
||||
|
||||
last_error = None
|
||||
for candidate in (device, *DEFAULT_DEVICES):
|
||||
try:
|
||||
try:
|
||||
jlink.exec_command(f"Device = {candidate}")
|
||||
except Exception:
|
||||
pass
|
||||
jlink.connect(candidate)
|
||||
print(f"Connected as {candidate}")
|
||||
return jlink
|
||||
except Exception as exc:
|
||||
last_error = exc
|
||||
jlink.close()
|
||||
raise SystemExit(f"Failed to connect J-Link: {last_error}")
|
||||
|
||||
|
||||
def find_rtt_control_block(jlink, ram_base: int = 0x20000000, ram_size: int = 0x18000):
|
||||
needle = b"SEGGER RTT"
|
||||
chunk = 0x1000
|
||||
was_halted = jlink.halted()
|
||||
if not was_halted:
|
||||
jlink.halt()
|
||||
try:
|
||||
for off in range(0, ram_size, chunk):
|
||||
data = bytes(jlink.memory_read8(ram_base + off, min(chunk, ram_size - off)))
|
||||
idx = data.find(needle)
|
||||
if idx >= 0:
|
||||
return ram_base + off + idx
|
||||
finally:
|
||||
if not was_halted:
|
||||
if hasattr(jlink, "restart"):
|
||||
jlink.restart()
|
||||
else:
|
||||
jlink.go()
|
||||
time.sleep(0.1)
|
||||
return None
|
||||
|
||||
|
||||
def rtt_read_text(jlink, timeout_s: float = 0.2) -> str:
|
||||
deadline = time.time() + timeout_s
|
||||
chunks: list[bytes] = []
|
||||
while time.time() < deadline:
|
||||
try:
|
||||
data = bytes(jlink.rtt_read(0, 512) or [])
|
||||
except Exception:
|
||||
data = b""
|
||||
if data:
|
||||
chunks.append(data)
|
||||
deadline = time.time() + timeout_s
|
||||
else:
|
||||
time.sleep(0.02)
|
||||
return b"".join(chunks).decode("ascii", errors="replace")
|
||||
|
||||
|
||||
def wait_for(jlink, marker: str, timeout_s: float = 30.0) -> str:
|
||||
deadline = time.time() + timeout_s
|
||||
buf = ""
|
||||
while time.time() < deadline:
|
||||
buf += rtt_read_text(jlink, 0.15)
|
||||
if marker in buf:
|
||||
return buf
|
||||
raise SystemExit(f"Timeout waiting for {marker!r}\n--- RTT ---\n{buf[-800:]}")
|
||||
|
||||
|
||||
def send_bytes(jlink, payload: bytes) -> None:
|
||||
off = 0
|
||||
while off < len(payload):
|
||||
try:
|
||||
n = jlink.rtt_write(0, list(payload[off : off + 64]))
|
||||
except Exception:
|
||||
time.sleep(0.05)
|
||||
continue
|
||||
if n is None or n <= 0:
|
||||
time.sleep(0.02)
|
||||
continue
|
||||
off += n
|
||||
|
||||
|
||||
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")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isfile(args.bin):
|
||||
raise SystemExit(f"missing bin: {args.bin}")
|
||||
|
||||
data = open(args.bin, "rb").read()
|
||||
size = len(data)
|
||||
if size < 16 or data[:4] != DAB_MAGIC:
|
||||
raise SystemExit(f"not a DAB bank (magic={data[:4].hex()})")
|
||||
|
||||
print(f"bin={args.bin} size={size} addr=0x{EXTFLASH_BIN3_UNIVERSAL_ADDR:08X}")
|
||||
|
||||
jlink = connect_jlink(args.device)
|
||||
try:
|
||||
cb = find_rtt_control_block(jlink)
|
||||
if cb is None:
|
||||
raise SystemExit("RTT control block not found")
|
||||
print(f"RTT CB @ 0x{cb:08X}")
|
||||
jlink.rtt_start(cb)
|
||||
time.sleep(0.3)
|
||||
_ = rtt_read_text(jlink, 0.3)
|
||||
|
||||
if not args.no_reset:
|
||||
print("hardware reset...")
|
||||
jlink.reset(halt=False)
|
||||
time.sleep(3.5)
|
||||
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 bin3 {size}\n".encode("ascii")
|
||||
send_bytes(jlink, cmd)
|
||||
print("sent flash command, waiting GO...")
|
||||
try:
|
||||
log = wait_for(jlink, "FLASH_BIN3_GO", 20.0)
|
||||
except SystemExit:
|
||||
raise SystemExit(
|
||||
"Firmware did not accept 'flash bin3'. Rebuild App with app_log.c "
|
||||
"support, or temporarily flash via: concatenate 2.bin+3.bin and "
|
||||
"'flash haitian <combined_size>'."
|
||||
)
|
||||
print(log.strip().splitlines()[-1])
|
||||
time.sleep(0.2)
|
||||
|
||||
chunk = 256
|
||||
sent = 0
|
||||
t0 = time.time()
|
||||
while sent < size:
|
||||
end = min(sent + chunk, size)
|
||||
send_bytes(jlink, data[sent:end])
|
||||
sent = end
|
||||
log = wait_for(jlink, "FLASH_ACK", 60.0)
|
||||
if "FLASH_BIN3_OK" in log:
|
||||
print(log.strip().splitlines()[-1])
|
||||
print("Done. Universal bank (3.bin) updated.")
|
||||
return
|
||||
if sent % (4 * 1024) == 0 or sent == size:
|
||||
elapsed = time.time() - t0
|
||||
print(f" {sent}/{size} ({100.0 * sent / size:.1f}%) {elapsed:.1f}s")
|
||||
|
||||
log = wait_for(jlink, "FLASH_BIN3_OK", 60.0)
|
||||
print(log.strip().splitlines()[-1])
|
||||
print("Done. Universal bank (3.bin) updated.")
|
||||
finally:
|
||||
try:
|
||||
jlink.rtt_stop()
|
||||
except Exception:
|
||||
pass
|
||||
jlink.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -2,21 +2,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Pack external W25Q128 tone/logo image for K1 (0903).
|
||||
|
||||
Layout (absolute W25Q128 offsets):
|
||||
Layout (absolute W25Q128 offsets — BIN2/BIN3 are FIXED so FW addresses stay stable):
|
||||
0x00000000 logo.bin (legacy pad; boot UI uses UI0902 full-screen logo)
|
||||
0x0000CB70 Charg.bin (legacy pad; charge UI uses UI0902_CHARGE_SCREEN full-screen)
|
||||
0x0001B8F0 1.bin 普通/专业 31 rhythms
|
||||
0x0009D07D 2.bin 海阔天空
|
||||
0x000A8607 3.bin 万能模式
|
||||
0x0009D07D 2.bin 本地曲目(变长,尾部 0xFF 填到 BIN3;<=41KB)
|
||||
0x000A71AC 3.bin 万能模式(固定;勿随 2.bin 长度漂移)
|
||||
|
||||
Outputs:
|
||||
tools/out/extflash_tone_0903.bin
|
||||
tools/out/extflash_tone_0903.res (same bytes, .res alias)
|
||||
tools/out/extflash_ALL_tone0903_ui0902.res (tone + pad + ui0902, if ui pack present)
|
||||
project/inc/ExtFlash_Tone_Addr.h
|
||||
Doc/音色文件/0903/FLASH_MAP.txt
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
|
@ -25,11 +27,25 @@ ROOT = Path(__file__).resolve().parents[1] # firmware project root
|
|||
REPO = ROOT.parent.parent # 一诺国际吉他
|
||||
OUT_DIR = ROOT / "tools" / "out"
|
||||
OUT_BIN = OUT_DIR / "extflash_tone_0903.bin"
|
||||
OUT_RES = OUT_DIR / "extflash_tone_0903.res"
|
||||
OUT_ALL_RES = OUT_DIR / "extflash_ALL_tone0903_ui0902.res"
|
||||
OUT_HDR = ROOT / "project" / "inc" / "ExtFlash_Tone_Addr.h"
|
||||
OUT_MAP = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
|
||||
UI0902_BIN = OUT_DIR / "ui0902_res.bin"
|
||||
|
||||
TONE_DIR = REPO / "Doc" / "音色文件" / "0903"
|
||||
|
||||
# Fixed map — must match project/inc/ExtFlash_Tone_Addr.h consumed by firmware.
|
||||
OFF_LOGO = 0x00000000
|
||||
OFF_CHARGING = 0x0000CB70
|
||||
OFF_BIN1 = 0x0001B8F0
|
||||
OFF_BIN2 = 0x0009D07D
|
||||
OFF_BIN3 = 0x000A71AC # FIXED: never place 3.bin by concatenating after variable 2.bin
|
||||
UI0902_RES_BASE = 0x00100000
|
||||
DAB_MAGIC = b"\xABDAB"
|
||||
MAX_SONG_BIN_BYTES = 41 * 1024 # 曲目文件(2.bin)硬上限 41KB
|
||||
BIN2_SLOT_BYTES = OFF_BIN3 - OFF_BIN2 # 41263; must keep BIN3 fixed
|
||||
|
||||
|
||||
def find_ziliao() -> Path:
|
||||
for p in REPO.iterdir():
|
||||
|
|
@ -38,29 +54,66 @@ def find_ziliao() -> Path:
|
|||
raise FileNotFoundError("资料/logo.bin + Charg.bin not found under repo")
|
||||
|
||||
|
||||
def dab_ok(blob: bytes, off: int, expect_cnt: int | None = None) -> None:
|
||||
if off + 16 > len(blob):
|
||||
raise SystemExit(f"DAB check @0x{off:X}: past end of pack ({len(blob)})")
|
||||
magic = blob[off : off + 4]
|
||||
cnt = struct.unpack_from("<I", blob, off + 12)[0]
|
||||
if magic != DAB_MAGIC:
|
||||
raise SystemExit(
|
||||
f"DAB check @0x{off:X}: bad magic {magic.hex()} (want {DAB_MAGIC.hex()})"
|
||||
)
|
||||
if expect_cnt is not None and cnt != expect_cnt:
|
||||
raise SystemExit(f"DAB check @0x{off:X}: cnt={cnt} want {expect_cnt}")
|
||||
|
||||
|
||||
def require_region_equals(blob: bytes, off: int, src: bytes, label: str) -> None:
|
||||
end = off + len(src)
|
||||
if end > len(blob):
|
||||
raise SystemExit(f"{label}: pack too short for region @0x{off:X}+{len(src)}")
|
||||
if blob[off:end] != src:
|
||||
raise SystemExit(f"{label}: bytes @0x{off:X} do not match source file")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ziliao = find_ziliao()
|
||||
bin1_path = TONE_DIR / "1.bin"
|
||||
bin2_path = TONE_DIR / "2.bin"
|
||||
bin3_path = TONE_DIR / "3.bin"
|
||||
parts = [
|
||||
("LOGO", ziliao / "logo.bin", 0x00000000, "legacy pad; boot uses UI0902_BOOT_LOGO"),
|
||||
("CHARGING", ziliao / "Charg.bin", 0x0000CB70, "legacy pad; UI uses UI0902_CHARGE_SCREEN"),
|
||||
("BIN1_RHYTHM", TONE_DIR / "1.bin", 0x0001B8F0, "普通/专业 31 条节奏"),
|
||||
("BIN2_SONG_HAITIAN", TONE_DIR / "2.bin", None, "本地曲目 海阔天空"),
|
||||
("BIN3_UNIVERSAL", TONE_DIR / "3.bin", None, "万能模式"),
|
||||
("LOGO", ziliao / "logo.bin", OFF_LOGO, "legacy pad; boot uses UI0902_BOOT_LOGO"),
|
||||
("CHARGING", ziliao / "Charg.bin", OFF_CHARGING, "legacy pad; UI uses UI0902_CHARGE_SCREEN"),
|
||||
("BIN1_RHYTHM", bin1_path, OFF_BIN1, "普通/专业 31 条节奏"),
|
||||
("BIN2_SONG_HAITIAN", bin2_path, OFF_BIN2, "本地曲目"),
|
||||
("BIN3_UNIVERSAL", bin3_path, OFF_BIN3, "万能模式(固定偏移)"),
|
||||
]
|
||||
|
||||
blobs = []
|
||||
bin2_data = bin2_path.read_bytes()
|
||||
if len(bin2_data) > MAX_SONG_BIN_BYTES:
|
||||
raise SystemExit(
|
||||
f"2.bin (曲目) too large: {len(bin2_data)} bytes > {MAX_SONG_BIN_BYTES} (41KB)"
|
||||
)
|
||||
if len(bin2_data) > BIN2_SLOT_BYTES:
|
||||
raise SystemExit(
|
||||
f"2.bin too large for fixed BIN3 slot: {len(bin2_data)} > {BIN2_SLOT_BYTES} "
|
||||
f"(would shift 3.bin past 0x{OFF_BIN3:X})"
|
||||
)
|
||||
|
||||
blobs: list[bytes] = []
|
||||
cursor = 0
|
||||
rows = []
|
||||
rows: list[tuple[str, int, int, str]] = []
|
||||
for name, path, force_off, note in parts:
|
||||
data = path.read_bytes()
|
||||
if force_off is not None:
|
||||
if cursor > force_off:
|
||||
raise SystemExit(f"{name}: cursor 0x{cursor:X} past forced 0x{force_off:X}")
|
||||
raise SystemExit(
|
||||
f"{name}: cursor 0x{cursor:X} past forced 0x{force_off:X} "
|
||||
f"(previous blob too large; enlarge next fixed gap or shrink prior file)"
|
||||
)
|
||||
if cursor < force_off:
|
||||
pad = force_off - cursor
|
||||
blobs.append(b"\xFF" * pad)
|
||||
rows.append(("(pad)", cursor, pad, "gap fill 0xFF"))
|
||||
cursor = force_off
|
||||
rows.append((f"(pad)", force_off - pad, pad, "gap fill 0xFF"))
|
||||
off = cursor
|
||||
blobs.append(data)
|
||||
cursor += len(data)
|
||||
|
|
@ -69,9 +122,25 @@ def main() -> None:
|
|||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
packed = b"".join(blobs)
|
||||
OUT_BIN.write_bytes(packed)
|
||||
OUT_RES.write_bytes(packed) # 总音色 .bin 同步为 .res
|
||||
|
||||
# named lookup
|
||||
by_name = {r[0]: r for r in rows if not r[0].startswith("(")}
|
||||
bin1_data = bin1_path.read_bytes()
|
||||
bin3_data = bin3_path.read_bytes()
|
||||
|
||||
# Hard integrity gates
|
||||
dab_ok(packed, OFF_BIN1, expect_cnt=31)
|
||||
dab_ok(packed, OFF_BIN2, expect_cnt=1)
|
||||
dab_ok(packed, OFF_BIN3, expect_cnt=3)
|
||||
if by_name["BIN1_RHYTHM"][1] != OFF_BIN1:
|
||||
raise SystemExit(f"BIN1 placed @0x{by_name['BIN1_RHYTHM'][1]:X} want 0x{OFF_BIN1:X}")
|
||||
if by_name["BIN2_SONG_HAITIAN"][1] != OFF_BIN2:
|
||||
raise SystemExit(f"BIN2 placed @0x{by_name['BIN2_SONG_HAITIAN'][1]:X} want 0x{OFF_BIN2:X}")
|
||||
if by_name["BIN3_UNIVERSAL"][1] != OFF_BIN3:
|
||||
raise SystemExit(f"BIN3 placed @0x{by_name['BIN3_UNIVERSAL'][1]:X} want 0x{OFF_BIN3:X}")
|
||||
require_region_equals(packed, OFF_BIN1, bin1_data, "BIN1/1.bin")
|
||||
require_region_equals(packed, OFF_BIN2, bin2_data, "BIN2/2.bin")
|
||||
require_region_equals(packed, OFF_BIN3, bin3_data, "BIN3/3.bin")
|
||||
|
||||
hdr = f"""#ifndef __EXTFLASH_TONE_ADDR_H
|
||||
#define __EXTFLASH_TONE_ADDR_H
|
||||
|
|
@ -92,13 +161,13 @@ def main() -> None:
|
|||
#define EXTFLASH_CHARGING_W 160
|
||||
#define EXTFLASH_CHARGING_H 190
|
||||
|
||||
#define EXTFLASH_BIN1_RHYTHM_ADDR 0x{by_name['BIN1_RHYTHM'][1]:08X}UL
|
||||
#define EXTFLASH_BIN1_RHYTHM_ADDR 0x{OFF_BIN1:08X}UL
|
||||
#define EXTFLASH_BIN1_RHYTHM_SIZE {by_name['BIN1_RHYTHM'][2]}UL
|
||||
|
||||
#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x{by_name['BIN2_SONG_HAITIAN'][1]:08X}UL
|
||||
#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x{OFF_BIN2:08X}UL
|
||||
#define EXTFLASH_BIN2_SONG_HAITIAN_SIZE {by_name['BIN2_SONG_HAITIAN'][2]}UL
|
||||
|
||||
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x{by_name['BIN3_UNIVERSAL'][1]:08X}UL
|
||||
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x{OFF_BIN3:08X}UL
|
||||
#define EXTFLASH_BIN3_UNIVERSAL_SIZE {by_name['BIN3_UNIVERSAL'][2]}UL
|
||||
|
||||
/* Convenience aliases used by UI ADDRESS */
|
||||
|
|
@ -121,8 +190,11 @@ def main() -> None:
|
|||
map_lines = [
|
||||
"K1 external Flash map — tone pack 0903",
|
||||
f"Packed file: Code/.../tools/out/{OUT_BIN.name} ({len(packed)} bytes)",
|
||||
f"Also: {OUT_RES.name}; ALL.res when ui0902_res.bin present",
|
||||
f"Ends at 0x{cursor:X}; UI0902_RES_BASE=0x00100000; free gap={0x100000 - cursor} bytes",
|
||||
"",
|
||||
"NOTE: BIN3 is FIXED at 0x000A71AC. 2.bin must be <= 41KB and <= slot 41263 bytes.",
|
||||
"",
|
||||
f"{'Name':<22} {'Offset':>10} {'Size':>10} Note",
|
||||
"-" * 72,
|
||||
]
|
||||
|
|
@ -131,31 +203,52 @@ def main() -> None:
|
|||
map_lines += [
|
||||
"",
|
||||
"Firmware ADDRESS mapping:",
|
||||
" 普通/专业 -> FLASH_ADDR_MODE_NORMAL/EXPERT (1.bin)",
|
||||
" 海阔天空 -> FLASH_ADDR_SONG_HAITIAN (2.bin)",
|
||||
" 万能 -> FLASH_ADDR_MODE_UNIVERSAL (3.bin)",
|
||||
" 普通/专业 -> FLASH_ADDR_MODE_NORMAL/EXPERT (1.bin @ 0x1B8F0)",
|
||||
" 本地曲目 -> FLASH_ADDR_SONG_HAITIAN (2.bin @ 0x9D07D, max 41KB)",
|
||||
" 万能 -> FLASH_ADDR_MODE_UNIVERSAL (3.bin @ 0xA71AC FIXED)",
|
||||
" AutoBand -> FLASH_ADDR_AUTOBAND_LEGACY 0x9EB5F (unchanged; overlaps 2.bin region — do not enable until remapped)",
|
||||
" Boot logo -> UI0902_BOOT_LOGO_ADDR (full-screen); packed logo.bin only pads 0x0..0xCB70",
|
||||
" Charging -> EXTFLASH_CHARGING_ADDR",
|
||||
"",
|
||||
"Add a local song:",
|
||||
" 1. Pack the new preset into 2.bin",
|
||||
" 1. Pack the new preset into 2.bin (must stay <= 41KB / 41263 slot)",
|
||||
" 2. Append a row to local_songs.csv (index,code,name)",
|
||||
" 3. python tools/pack_extflash_tone_0903.py (also regenerates LocalSongNames.h)",
|
||||
" 4. Rebuild firmware and flash MCU + ExtFlash",
|
||||
" 4. Rebuild firmware and flash MCU + ExtFlash ALL.res",
|
||||
]
|
||||
OUT_MAP.write_text("\n".join(map_lines) + "\n", encoding="utf-8", newline="\n")
|
||||
|
||||
print(f"Wrote {OUT_BIN} ({len(packed)} bytes)")
|
||||
print(f"Wrote {OUT_RES} ({len(packed)} bytes)")
|
||||
print(f"Wrote {OUT_HDR}")
|
||||
print(f"Wrote {OUT_MAP}")
|
||||
for name, off, size, note in rows:
|
||||
print(f" 0x{off:08X} {size:8d} {name} {note}")
|
||||
if cursor > 0x00100000:
|
||||
if cursor > UI0902_RES_BASE:
|
||||
raise SystemExit("ERROR: pack overflows into UI0902_RES_BASE")
|
||||
print(f"OK: {0x00100000 - cursor} bytes free before UI0902 @ 0x00100000")
|
||||
print(f"OK: {UI0902_RES_BASE - cursor} bytes free before UI0902 @ 0x{UI0902_RES_BASE:X}")
|
||||
print("OK: BIN1/BIN2/BIN3 present at fixed addresses and match source files")
|
||||
print(f"OK: 2.bin size {len(bin2_data)} <= 41KB ({MAX_SONG_BIN_BYTES})")
|
||||
|
||||
if UI0902_BIN.is_file():
|
||||
ui = UI0902_BIN.read_bytes()
|
||||
all_res = packed + (b"\xFF" * (UI0902_RES_BASE - len(packed))) + ui
|
||||
require_region_equals(all_res, OFF_BIN1, bin1_data, "ALL.res BIN1")
|
||||
require_region_equals(all_res, OFF_BIN2, bin2_data, "ALL.res BIN2")
|
||||
require_region_equals(all_res, OFF_BIN3, bin3_data, "ALL.res BIN3")
|
||||
dab_ok(all_res, OFF_BIN1, 31)
|
||||
dab_ok(all_res, OFF_BIN2, 1)
|
||||
dab_ok(all_res, OFF_BIN3, 3)
|
||||
OUT_ALL_RES.write_bytes(all_res)
|
||||
# Mirror under repo tools/out for publish/consumers
|
||||
repo_out = REPO / "tools" / "out"
|
||||
repo_out.mkdir(parents=True, exist_ok=True)
|
||||
(repo_out / OUT_ALL_RES.name).write_bytes(all_res)
|
||||
(repo_out / OUT_RES.name).write_bytes(packed)
|
||||
print(f"Wrote {OUT_ALL_RES} ({len(all_res)} bytes) — verified 1/2/3.bin")
|
||||
else:
|
||||
print(f"WARN: {UI0902_BIN} missing; skipped ALL.res")
|
||||
|
||||
# Keep LocalSongNames.h in sync with Doc/.../local_songs.csv when packing tones.
|
||||
gen = ROOT / "tools" / "gen_local_song_names.py"
|
||||
r = subprocess.run([sys.executable, str(gen)], check=False)
|
||||
if r.returncode != 0:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ MAP_TXT = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
|
|||
IAP_DIR = REPO / "升级" / "MCU主控升级"
|
||||
OUT_ROOT = REPO / "tools" / "out"
|
||||
|
||||
FW_VER = "0.2.6"
|
||||
FW_VER = "0.2.7"
|
||||
TONE_RES_VER = "0903"
|
||||
UI0902_RES_BASE = 0x00100000
|
||||
BOOT_FLASH_ADDR = 0x08000000
|
||||
|
|
@ -69,6 +69,33 @@ def build_combined_extflash(tone: bytes, ui: bytes) -> bytes:
|
|||
return tone + (b"\xFF" * pad) + ui
|
||||
|
||||
|
||||
def verify_tone_layout(tone: bytes, check_sources: bool = True) -> None:
|
||||
"""Refuse to ship ALL.res if 1/2/3.bin missing, shifted, or content mismatch."""
|
||||
dab = b"\xABDAB"
|
||||
sources = {
|
||||
"BIN1": (0x0001B8F0, 31, REPO / "Doc" / "音色文件" / "0903" / "1.bin"),
|
||||
"BIN2": (0x0009D07D, 1, REPO / "Doc" / "音色文件" / "0903" / "2.bin"),
|
||||
"BIN3": (0x000A71AC, 3, REPO / "Doc" / "音色文件" / "0903" / "3.bin"),
|
||||
}
|
||||
for name, (off, want_cnt, src_path) in sources.items():
|
||||
if off + 16 > len(tone):
|
||||
raise SystemExit(f"verify {name}: tone pack too short for 0x{off:X}")
|
||||
magic = tone[off : off + 4]
|
||||
cnt = int.from_bytes(tone[off + 12 : off + 16], "little")
|
||||
if magic != dab or cnt != want_cnt:
|
||||
raise SystemExit(
|
||||
f"verify {name} @0x{off:X} failed: magic={magic.hex()} cnt={cnt} "
|
||||
f"(want ABDAB/{want_cnt}). Re-run pack_extflash_tone_0903.py."
|
||||
)
|
||||
if check_sources and src_path.is_file():
|
||||
src = src_path.read_bytes()
|
||||
if name == "BIN2" and len(src) > 41 * 1024:
|
||||
raise SystemExit(f"verify BIN2: source 2.bin {len(src)} > 41KB")
|
||||
if tone[off : off + len(src)] != src:
|
||||
raise SystemExit(f"verify {name}: content @0x{off:X} != {src_path}")
|
||||
print("verify tone layout: BIN1/BIN2/BIN3 addresses + content OK")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not BOOT_BIN.is_file():
|
||||
raise SystemExit(f"missing Boot bin: {BOOT_BIN}")
|
||||
|
|
@ -97,8 +124,10 @@ def main() -> None:
|
|||
pkg.mkdir(parents=True)
|
||||
|
||||
tone_data = TONE_BIN.read_bytes()
|
||||
verify_tone_layout(tone_data)
|
||||
ui_data = UI0902_BIN.read_bytes()
|
||||
combined = build_combined_extflash(tone_data, ui_data)
|
||||
verify_tone_layout(combined) # same offsets in ALL.res
|
||||
|
||||
boot_name = f"AT32F403ARCT7_BOOT_v{FW_VER}_{stamp}.bin"
|
||||
mcu_name = f"YNGJ-GT1-M_MCU_v{FW_VER}_{stamp}.bin"
|
||||
|
|
@ -147,7 +176,7 @@ def main() -> None:
|
|||
"",
|
||||
"外部 Flash 分区:",
|
||||
" 0x00000000 音色/充电图 (toneRes)",
|
||||
" logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin(HKTK/2s) + 3.bin",
|
||||
" logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin@0x9D07D + 3.bin@0xA71AC(FIXED)",
|
||||
" 0x00100000 UI0902 图片 (模式选择、开机 Logo、充电、烧录模式、设置/调音台等)",
|
||||
" 0x001D2000 UI0902_FLASH_MODE 烧录模式全屏图(Boot 读取)",
|
||||
"",
|
||||
|
|
|
|||
Loading…
Reference in New Issue