Release 0.2.3: fix local song HKTK load/play and keep tone map logs.

Use 41KB preset buffer with compressed 2.bin, expert/normal StartWithNote path, Flash Map address logs, and ship flash-mode/hub assets plus flash helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-07 21:48:54 +08:00
parent c4852aca5d
commit 4544f8706a
17 changed files with 543 additions and 40 deletions

View File

@ -14,13 +14,15 @@ static char s_debug_level = 'D';
static char s_cmd_buf[48]; static char s_cmd_buf[48];
static uint8_t s_cmd_len; static uint8_t s_cmd_len;
/* RTT 二进制烧录:将 ui0902_res.bin 写到 W25Q128 @ UI0902_RES_BASE */ /* RTT 二进制烧录:写到 W25Q128ui0902 或本地曲目) */
static uint8_t s_flash_mode; static uint8_t s_flash_mode;
static uint32_t s_flash_base;
static uint32_t s_flash_addr; static uint32_t s_flash_addr;
static uint32_t s_flash_remain; static uint32_t s_flash_remain;
static uint32_t s_flash_last_sector; static uint32_t s_flash_last_sector;
static uint16_t s_flash_page_len; static uint16_t s_flash_page_len;
static uint8_t s_flash_page[256]; static uint8_t s_flash_page[256];
static char s_flash_ok_tag[24];
uint8_t app_log_flash_busy(void) uint8_t app_log_flash_busy(void)
{ {
@ -61,7 +63,7 @@ static void app_log_flash_feed(const uint8_t *data, unsigned len)
unsigned i; unsigned i;
for (i = 0; i < len && s_flash_remain > 0U; i++) { for (i = 0; i < len && s_flash_remain > 0U; i++) {
if (s_flash_page_len == 0U && s_flash_addr == UI0902_RES_BASE && i == 0U) { if (s_flash_page_len == 0U && s_flash_addr == s_flash_base && i == 0U) {
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_RX\n"); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_RX\n");
} }
s_flash_page[s_flash_page_len++] = data[i]; s_flash_page[s_flash_page_len++] = data[i];
@ -73,34 +75,42 @@ static void app_log_flash_feed(const uint8_t *data, unsigned len)
if (s_flash_remain == 0U && s_flash_mode) { if (s_flash_remain == 0U && s_flash_mode) {
s_flash_mode = 0U; s_flash_mode = 0U;
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_UI0902_OK\n"); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, s_flash_ok_tag);
UI_DrawModeSelectScreen(); UI_DrawModeSelectScreen();
} }
} }
static void app_log_flash_start(uint32_t size) static void app_log_flash_start_at(uint32_t base, uint32_t size, const char *go_tag, const char *ok_tag)
{ {
char msg[64]; char msg[80];
if (size == 0U || size > (2U * 1024U * 1024U)) { if (size == 0U || size > (2U * 1024U * 1024U)) {
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_UI0902_ERR size\n"); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERR size\n");
return; return;
} }
s_flash_mode = 1U; s_flash_mode = 1U;
s_flash_addr = UI0902_RES_BASE; s_flash_base = base;
s_flash_addr = base;
s_flash_remain = size; s_flash_remain = size;
s_flash_last_sector = 0xFFFFFFFFUL; s_flash_last_sector = 0xFFFFFFFFUL;
s_flash_page_len = 0U; s_flash_page_len = 0U;
s_cmd_len = 0U; s_cmd_len = 0U;
s_cmd_buf[0] = '\0'; s_cmd_buf[0] = '\0';
strncpy(s_flash_ok_tag, ok_tag, sizeof(s_flash_ok_tag) - 1U);
s_flash_ok_tag[sizeof(s_flash_ok_tag) - 1U] = '\0';
snprintf(msg, sizeof(msg), snprintf(msg, sizeof(msg),
"FLASH_UI0902_GO base=0x%08lX size=%lu\n", "%s base=0x%08lX size=%lu\n",
(unsigned long)UI0902_RES_BASE, (unsigned long)size); go_tag, (unsigned long)base, (unsigned long)size);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, msg); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, msg);
} }
static void app_log_flash_start(uint32_t size)
{
app_log_flash_start_at(UI0902_RES_BASE, size, "FLASH_UI0902_GO", "FLASH_UI0902_OK\n");
}
static void app_log_slot_store(const char *line) static void app_log_slot_store(const char *line)
{ {
uint16_t idx = s_head; uint16_t idx = s_head;
@ -264,6 +274,106 @@ uint8_t app_log_try_command(const char *cmd)
app_log_flash_start(size); app_log_flash_start(size);
return 1U; return 1U;
} }
if (strncmp(cmd, "flash haitian ", 14) == 0) {
uint32_t size = (uint32_t)strtoul(cmd + 14, NULL, 10);
app_log_flash_start_at(FLASH_ADDR_SONG_HAITIAN, size,
"FLASH_HAITIAN_GO", "FLASH_HAITIAN_OK\n");
return 1U;
}
if (strncmp(cmd, "tone status", 11) == 0) {
uint8_t hdr[16];
uint8_t name[40];
uint8_t cur_hdr[16];
char *pname;
char line[192];
const char *map;
W25Q128_Read(hdr, FLASH_ADDR_SONG_HAITIAN, 16);
W25Q128_Read(name, FLASH_ADDR_SONG_HAITIAN + 1280 + 4, 32);
name[32] = 0;
W25Q128_Read(cur_hdr, (uint32_t)ADDRESS, 16);
pname = AutoBandTop1_GetPresetName();
if (ADDRESS == EXTFLASH_BIN1_RHYTHM_ADDR)
map = "BIN1_RHYTHM";
else if (ADDRESS == EXTFLASH_BIN2_SONG_HAITIAN_ADDR)
map = "BIN2_SONG_HAITIAN";
else if (ADDRESS == EXTFLASH_BIN3_UNIVERSAL_ADDR)
map = "BIN3_UNIVERSAL";
else
map = "UNKNOWN";
snprintf(line, sizeof(line),
"TONE tab=%u sw=%u addr=0x%08lX map=%s songidx=%u rhyt=%u\n",
(unsigned)mGuiData[GUI_TAB_INDEX].Current,
(unsigned)mGuiData[GUI_AUTOBAND_SW].Current,
(unsigned long)ADDRESS, map,
(unsigned)ParamGuiData[EXPRESS_MODE_PARAM].Current,
(unsigned)ParamGuiData[SONG_MODE_PARAM].Current);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
snprintf(line, sizeof(line),
"TONE map BIN1=0x%08lX BIN2=0x%08lX BIN3=0x%08lX\n",
(unsigned long)EXTFLASH_BIN1_RHYTHM_ADDR,
(unsigned long)EXTFLASH_BIN2_SONG_HAITIAN_ADDR,
(unsigned long)EXTFLASH_BIN3_UNIVERSAL_ADDR);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
snprintf(line, sizeof(line),
"TONE @ADDRESS magic=%02X%02X%02X%02X cnt=%lu\n",
cur_hdr[0], cur_hdr[1], cur_hdr[2], cur_hdr[3],
(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);
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],
(unsigned long)(hdr[8] | (hdr[9] << 8) | (hdr[10] << 16) | (hdr[11] << 24)),
(unsigned long)(hdr[12] | (hdr[13] << 8) | (hdr[14] << 16) | (hdr[15] << 24)),
(char *)name);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
snprintf(line, sizeof(line),
"TONE loaded name=%s count=%d\n",
pname ? pname : "(null)",
AutoBandTop1_GetPresetItemCount());
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
return 1U;
}
if (strncmp(cmd, "tone reload", 11) == 0) {
char line[96];
UI_ReloadTonePreset();
snprintf(line, sizeof(line),
"TONE_RELOAD addr=0x%08lX name=%s\n",
(unsigned long)ADDRESS,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "(null)");
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
return 1U;
}
if (strncmp(cmd, "tone local", 10) == 0) {
char line[128];
int ret;
mGuiData[GUI_TAB_INDEX].Current = 1; /* 专业 */
mGuiData[GUI_AUTOBAND_SW].Current = 1; /* 本地曲目 */
ParamGuiData[EXPRESS_MODE_PARAM].Current = 0;
UI_ApplyToneAddress();
AutoBandTop1_Stop();
StartFlag = 0;
ret = AutoBandTop1_LoadPresetItemFromFlash(0);
snprintf(line, sizeof(line),
"TONE_LOCAL ret=%d addr=0x%08lX map=BIN2@0x%08lX name=%s count=%d %s\n",
ret, (unsigned long)ADDRESS,
(unsigned long)EXTFLASH_BIN2_SONG_HAITIAN_ADDR,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "(null)",
AutoBandTop1_GetPresetItemCount(),
(ADDRESS == EXTFLASH_BIN2_SONG_HAITIAN_ADDR) ? "OK" : "MISMATCH");
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
return 1U;
}
if (strncmp(cmd, "tone pick", 9) == 0) {
/* 模拟专业+本地曲目拨片(无和弦板) */
mGuiData[GUI_TAB_INDEX].Current = 1;
mGuiData[GUI_AUTOBAND_SW].Current = 1;
KEY_ID_1629 = 0; /* 走默认和弦兜底 */
StartFlag = 0;
Pick_Handle();
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "TONE_PICK_DONE\n");
return 1U;
}
if (strncmp(cmd, "flash erase chip", 16) == 0) { if (strncmp(cmd, "flash erase chip", 16) == 0) {
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERASE_BEGIN\n"); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERASE_BEGIN\n");
LOG_I("FLASH", "W25Q128 chip erase start"); LOG_I("FLASH", "W25Q128 chip erase start");

View File

@ -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)); uint8_t TOUCH_AREA_NUM = (sizeof(Touch_Areas)/sizeof(Touch_AreaTypeDef));
const uint8_t Version[3] = {0,2,2}; /* major.minor.patch — 0.2.2: BT persist, settings icon focus, auto-off UI */ const uint8_t Version[3] = {0,2,3}; /* major.minor.patch — 0.2.3: local song HKTK load/play + tone addr logs */
uint8_t Led = 8; uint8_t Led = 8;
bool PressFlag = 0; bool PressFlag = 0;
@ -775,21 +775,48 @@ void Pick_Handle()
break; break;
case 2: case 2:
if(StartFlag == 0) //当播放了之后,拨片不再起作用 /* 普通:节奏/本地曲目均用 StartWithNote切库后允许再次拨片起奏 */
if (StartFlag == 0 || mGuiData[GUI_AUTOBAND_SW].Current)
{ {
StartFlag = 1; StartFlag = 1;
AutoBandTop1_Stop();
AutoBandTop1_SetLoopMode(0,1); AutoBandTop1_SetLoopMode(0,1);
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
/* 起调必须用 0x3C+移调;勿用和弦音区 MIDI如 48否则本地曲目起奏异常 */
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current; midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi); AutoBandTop1_StartWithNote(midi);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt); AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
if (mGuiData[GUI_AUTOBAND_SW].Current)
LOG_I("pick", "local/normal root=%u name=%s", midi,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?");
} }
break; break;
case 1: case 1:
StartFlag = 1; StartFlag = 1;
AutoBandTop1_SetPiecePlayMode(1,0,1920); if (mGuiData[GUI_AUTOBAND_SW].Current)
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt); {
AutoBandTop1_SetLoopMode(0,1); /* 本地曲目与普通模式同一套起奏0x3C 根音 + 和弦音) */
AutoBandTop1_Stop();
AutoBandTop1_SetLoopMode(0,1);
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
LOG_I("pick", "local/expert root=%u name=%s sw=%u tab=%u",
midi,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)mGuiData[GUI_AUTOBAND_SW].Current,
(unsigned)mGuiData[GUI_TAB_INDEX].Current);
}
else
{
AutoBandTop1_SetPiecePlayMode(1,0,1920);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0,1);
LOG_I("pick", "expert/rhythm piece name=%s",
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?");
}
break; break;
case 0: case 0:
StartFlag = 1; StartFlag = 1;

View File

@ -13,7 +13,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "includes.h" #include "includes.h"
MidiFifoSeq_t m_MidiSendFifo; MidiFifoSeq_t m_MidiSendFifo;
#define PRESET_BUFFER_SIZE (1024*40) #define PRESET_BUFFER_SIZE (1024*41) /* 2s.res compressed HKTK preset 39760 bytes */
static int m_presetCount = 0; static int m_presetCount = 0;
static uint8_t pStoreBuffer[PRESET_BUFFER_SIZE]; static uint8_t pStoreBuffer[PRESET_BUFFER_SIZE];
//static uint8_t* pStoreBuffer; //static uint8_t* pStoreBuffer;
@ -76,9 +76,60 @@ static void Func_CallBack_PitchWheel(int channel,int cc,int value)
mymidififo_InQueue(&m_MidiSendFifo,&midimsg); mymidififo_InQueue(&m_MidiSendFifo,&midimsg);
} }
/* Flash Map 0903对照 ExtFlash_Tone_Addr.h / Doc/.../FLASH_MAP.txt */
static const char *ToneFlashMapBank(uint32_t abs)
{
if (abs >= EXTFLASH_BIN1_RHYTHM_ADDR &&
abs < EXTFLASH_BIN1_RHYTHM_ADDR + EXTFLASH_BIN1_RHYTHM_SIZE)
return "BIN1_RHYTHM";
if (abs >= EXTFLASH_BIN2_SONG_HAITIAN_ADDR &&
abs < EXTFLASH_BIN2_SONG_HAITIAN_ADDR + EXTFLASH_BIN2_SONG_HAITIAN_SIZE)
return "BIN2_SONG_HAITIAN";
if (abs >= EXTFLASH_BIN3_UNIVERSAL_ADDR &&
abs < EXTFLASH_BIN3_UNIVERSAL_ADDR + EXTFLASH_BIN3_UNIVERSAL_SIZE)
return "BIN3_UNIVERSAL";
if (abs >= FLASH_ADDR_AUTOBAND_LEGACY &&
abs < FLASH_ADDR_AUTOBAND_LEGACY + 0x10000UL)
return "LEGACY_AUTOBAND";
return "OUT_OF_MAP";
}
static uint32_t s_flash_rd_last_base = 0xFFFFFFFFu;
static uint16_t s_flash_rd_log_cnt = 0;
static int Func_CallBack_ReadFlash(int address,int length,uint8_t* pOutput) static int Func_CallBack_ReadFlash(int address,int length,uint8_t* pOutput)
{ {
W25Q128_Read(pOutput, (uint32_t)ADDRESS+address, length); uint32_t base = (uint32_t)ADDRESS;
uint32_t abs = base + (uint32_t)address;
const char *bank;
if (base != s_flash_rd_last_base) {
s_flash_rd_last_base = base;
s_flash_rd_log_cnt = 0;
LOG_I("FLASH_RD", "bank base=0x%08X map=%s (BIN1=0x%08X BIN2=0x%08X BIN3=0x%08X)",
(unsigned)base,
ToneFlashMapBank(base),
(unsigned)EXTFLASH_BIN1_RHYTHM_ADDR,
(unsigned)EXTFLASH_BIN2_SONG_HAITIAN_ADDR,
(unsigned)EXTFLASH_BIN3_UNIVERSAL_ADDR);
}
bank = ToneFlashMapBank(abs);
/* 加载阶段打印前若干次 + 每次 offset==0避免播放期刷屏 */
if (s_flash_rd_log_cnt < 16u || address == 0) {
LOG_I("FLASH_RD", "abs=0x%08X off=%d len=%d map=%s",
(unsigned)abs, address, length, bank);
if (s_flash_rd_log_cnt < 0xFFFFu)
s_flash_rd_log_cnt++;
if (s_flash_rd_log_cnt == 16u)
LOG_I("FLASH_RD", "further reads omitted (same base)");
} else if (bank[0] == 'O') {
/* 读到 Map 外:始终告警 */
LOG_W("FLASH_RD", "OUT_OF_MAP abs=0x%08X off=%d len=%d base=0x%08X",
(unsigned)abs, address, length, (unsigned)base);
}
W25Q128_Read(pOutput, abs, length);
return 0; return 0;
} }
// //
@ -97,11 +148,14 @@ void App_Auto_Init(void)
int ret = AutoBandTop1_LoadPresetItemFromFlash(0); int ret = AutoBandTop1_LoadPresetItemFromFlash(0);
if (ret != 0) if (ret != 0)
{ {
LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X", LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X map=%s",
ret, (unsigned)ADDRESS); ret, (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS));
} }
else else
{ {
LOG_I("AUTO", "LoadPreset ok name=%s addr=0x%08X map=%s",
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS));
AutoBandTop1_ChangeBPM(mGuiData[1].Current); AutoBandTop1_ChangeBPM(mGuiData[1].Current);
} }
} }

View File

@ -178,12 +178,21 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
show_Param(&UI_Label[GUI_MODE_PARAM], pCurData); show_Param(&UI_Label[GUI_MODE_PARAM], pCurData);
UI_ApplyToneAddress(); UI_ApplyToneAddress();
int ret = 0;
if (tab == 0) if (tab == 0)
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current); ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current);
else if (mGuiData[GUI_AUTOBAND_SW].Current) else if (mGuiData[GUI_AUTOBAND_SW].Current)
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[EXPRESS_MODE_PARAM].Current); ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[EXPRESS_MODE_PARAM].Current);
else else
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[SONG_MODE_PARAM].Current); ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[SONG_MODE_PARAM].Current);
if (ret != 0)
LOG_E("tone", "load preset failed tab=%d sw=%d ret=%d addr=0x%08X",
tab, mGuiData[GUI_AUTOBAND_SW].Current, ret, (unsigned)ADDRESS);
else
LOG_I("tone", "load preset ok tab=%d sw=%d name=%s addr=0x%08X",
tab, mGuiData[GUI_AUTOBAND_SW].Current,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)ADDRESS);
if (tab == 0) if (tab == 0)
nvm->param.Param3 = ParamGuiData[ALL_MODE_PARAM].Current; nvm->param.Param3 = ParamGuiData[ALL_MODE_PARAM].Current;
@ -216,7 +225,11 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
/* 万能无节奏/本地 TAB忽略该触控 */ /* 万能无节奏/本地 TAB忽略该触控 */
if (mGuiData[GUI_TAB_INDEX].Current == 0) if (mGuiData[GUI_TAB_INDEX].Current == 0)
return; return;
if (mGuiData[ID].Current == value)
return;
mGuiData[ID].Current = value; mGuiData[ID].Current = value;
StartFlag = 0;
AutoBandTop1_Stop();
UI_ReloadTonePreset(); UI_ReloadTonePreset();
if (!UI_SetFocus(GUI_MODE_PARAM)) if (!UI_SetFocus(GUI_MODE_PARAM))
label_ModeSelece_ui(&UI_Label[GUI_MODE_PARAM],&mGuiData[GUI_MODE_PARAM]); label_ModeSelece_ui(&UI_Label[GUI_MODE_PARAM],&mGuiData[GUI_MODE_PARAM]);

View File

@ -112,36 +112,66 @@ void CallUI_RestoreSelect(void) {
/* 音师万能→3.bin普通/专业节奏→1.bin、本地曲目→2.bin */ /* 音师万能→3.bin普通/专业节奏→1.bin、本地曲目→2.bin */
void UI_ApplyToneAddress(void) void UI_ApplyToneAddress(void)
{ {
uint32_t expect = 0;
const char *map_name = "?";
switch (mGuiData[GUI_TAB_INDEX].Current) switch (mGuiData[GUI_TAB_INDEX].Current)
{ {
case 0: /* 万能:和弦走向库 */ case 0: /* 万能:和弦走向库 */
ADDRESS = FLASH_ADDR_MODE_UNIVERSAL; ADDRESS = FLASH_ADDR_MODE_UNIVERSAL;
return; expect = EXTFLASH_BIN3_UNIVERSAL_ADDR;
map_name = "BIN3_UNIVERSAL";
break;
case 1: /* 专业 */ case 1: /* 专业 */
if (mGuiData[GUI_AUTOBAND_SW].Current) if (mGuiData[GUI_AUTOBAND_SW].Current) {
ADDRESS = FLASH_ADDR_SONG_HAITIAN; ADDRESS = FLASH_ADDR_SONG_HAITIAN;
else expect = EXTFLASH_BIN2_SONG_HAITIAN_ADDR;
map_name = "BIN2_SONG_HAITIAN";
} else {
ADDRESS = FLASH_ADDR_MODE_EXPERT; ADDRESS = FLASH_ADDR_MODE_EXPERT;
return; expect = EXTFLASH_BIN1_RHYTHM_ADDR;
map_name = "BIN1_RHYTHM";
}
break;
case 2: /* 普通 */ case 2: /* 普通 */
if (mGuiData[GUI_AUTOBAND_SW].Current) if (mGuiData[GUI_AUTOBAND_SW].Current) {
ADDRESS = FLASH_ADDR_SONG_HAITIAN; ADDRESS = FLASH_ADDR_SONG_HAITIAN;
else expect = EXTFLASH_BIN2_SONG_HAITIAN_ADDR;
map_name = "BIN2_SONG_HAITIAN";
} else {
ADDRESS = FLASH_ADDR_MODE_NORMAL; ADDRESS = FLASH_ADDR_MODE_NORMAL;
return; expect = EXTFLASH_BIN1_RHYTHM_ADDR;
map_name = "BIN1_RHYTHM";
}
break;
default: default:
ADDRESS = FLASH_ADDR_MODE_UNIVERSAL; ADDRESS = FLASH_ADDR_MODE_UNIVERSAL;
return; expect = EXTFLASH_BIN3_UNIVERSAL_ADDR;
map_name = "BIN3_UNIVERSAL";
break;
} }
LOG_I("tone", "ADDRESS=0x%08X map=%s expect=0x%08X tab=%u sw=%u %s",
(unsigned)ADDRESS, map_name, (unsigned)expect,
(unsigned)mGuiData[GUI_TAB_INDEX].Current,
(unsigned)mGuiData[GUI_AUTOBAND_SW].Current,
(ADDRESS == expect) ? "OK" : "MISMATCH");
} }
void UI_ReloadTonePreset(void) void UI_ReloadTonePreset(void)
{ {
UI_ApplyToneAddress(); UI_ApplyToneAddress();
/* 换库前先停,避免播放中改 relocate 指针 */
AutoBandTop1_Stop();
StartFlag = 0;
/* 万能:和弦走向 */ /* 万能:和弦走向 */
if (mGuiData[GUI_TAB_INDEX].Current == 0) if (mGuiData[GUI_TAB_INDEX].Current == 0)
{ {
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current); int ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current);
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; return;
} }
/* 普通/专业:本地曲目或节奏类型 */ /* 普通/专业:本地曲目或节奏类型 */
@ -151,7 +181,18 @@ void UI_ReloadTonePreset(void)
local->Max = (LOCAL_SONG_COUNT > 0) ? (uint16_t)(LOCAL_SONG_COUNT - 1) : 0; local->Max = (LOCAL_SONG_COUNT > 0) ? (uint16_t)(LOCAL_SONG_COUNT - 1) : 0;
if (local->Current > local->Max) if (local->Current > local->Max)
local->Current = local->Max; local->Current = local->Max;
AutoBandTop1_LoadPresetItemFromFlash(local->Current); int ret = AutoBandTop1_LoadPresetItemFromFlash(local->Current);
if (ret != 0)
LOG_E("tone", "load local song idx=%d failed ret=%d addr=0x%08X map=BIN2@0x%08X",
local->Current, ret, (unsigned)ADDRESS,
(unsigned)EXTFLASH_BIN2_SONG_HAITIAN_ADDR);
else
LOG_I("tone", "local song idx=%d name=%s addr=0x%08X map=BIN2@0x%08X %s",
local->Current,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)ADDRESS,
(unsigned)EXTFLASH_BIN2_SONG_HAITIAN_ADDR,
(ADDRESS == EXTFLASH_BIN2_SONG_HAITIAN_ADDR) ? "OK" : "MISMATCH");
{ {
int bin_n = AutoBandTop1_GetPresetItemCount(); int bin_n = AutoBandTop1_GetPresetItemCount();
if (bin_n != LOCAL_SONG_COUNT) if (bin_n != LOCAL_SONG_COUNT)
@ -159,7 +200,16 @@ void UI_ReloadTonePreset(void)
} }
return; return;
} }
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[SONG_MODE_PARAM].Current); {
int ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[SONG_MODE_PARAM].Current);
LOG_I("tone", "load rhythm idx=%d ret=%d name=%s addr=0x%08X map=BIN1@0x%08X %s",
ParamGuiData[SONG_MODE_PARAM].Current, ret,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)ADDRESS,
(unsigned)EXTFLASH_BIN1_RHYTHM_ADDR,
(ADDRESS == EXTFLASH_BIN1_RHYTHM_ADDR) ? "OK" : "MISMATCH");
(void)ret;
}
} }
static uint8_t s_ui_focus_id = UI_FOCUS_NONE; static uint8_t s_ui_focus_id = UI_FOCUS_NONE;

View File

@ -21,9 +21,9 @@
#define EXTFLASH_BIN1_RHYTHM_SIZE 530317UL #define EXTFLASH_BIN1_RHYTHM_SIZE 530317UL
#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x0009D07DUL #define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x0009D07DUL
#define EXTFLASH_BIN2_SONG_HAITIAN_SIZE 46474UL #define EXTFLASH_BIN2_SONG_HAITIAN_SIZE 41040UL
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x000A8607UL #define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x000A70CDUL
#define EXTFLASH_BIN3_UNIVERSAL_SIZE 31854UL #define EXTFLASH_BIN3_UNIVERSAL_SIZE 31854UL
/* Convenience aliases used by UI ADDRESS */ /* Convenience aliases used by UI ADDRESS */
@ -35,8 +35,8 @@
/* Legacy AutoBand bank — not repacked in 0903; left unchanged in firmware */ /* Legacy AutoBand bank — not repacked in 0903; left unchanged in firmware */
#define FLASH_ADDR_AUTOBAND_LEGACY 0x0009EB5FUL #define FLASH_ADDR_AUTOBAND_LEGACY 0x0009EB5FUL
#define EXTFLASH_TONE_PACK_END 0x000B0275UL #define EXTFLASH_TONE_PACK_END 0x000AED3BUL
#define EXTFLASH_TONE_PACK_SIZE 721525UL #define EXTFLASH_TONE_PACK_SIZE 716091UL
#define UI0902_RES_BASE_SAFE_GAP (0x00100000UL - EXTFLASH_TONE_PACK_END) #define UI0902_RES_BASE_SAFE_GAP (0x00100000UL - EXTFLASH_TONE_PACK_END)
#endif #endif

View File

@ -17,7 +17,7 @@
* Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP * Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP
* the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes * the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes
*/ */
#define RT_HEAP_SIZE (20*1024) #define RT_HEAP_SIZE (19*1024) /* 20KB-1KB to fit 41KB preset buffer in 96KB RAM */
static rt_uint8_t rt_heap[RT_HEAP_SIZE]; static rt_uint8_t rt_heap[RT_HEAP_SIZE];
RT_WEAK void *rt_heap_begin_get(void) RT_WEAK void *rt_heap_begin_get(void)

View File

@ -0,0 +1,60 @@
import subprocess, shutil, os, time
base = os.path.join(r"C:\Users\qjyu\Documents\SoundWalker", "一诺国际吉他",
r"Code\YNGJ-GT1-M - AT32F403ARCT7")
stage = r"C:\Temp\k1flash"
os.makedirs(stage, exist_ok=True)
out_src = os.path.join(base, r"project\IAR_V7.4\YNGJ-GT1-M\Exe\YNGJ-GT1-M.out")
out_dst = os.path.join(stage, "YNGJ-GT1-M.out")
with open(out_src, "rb") as a, open(out_dst, "wb") as b:
shutil.copyfileobj(a, b)
print("copied", os.path.getsize(out_dst), flush=True)
gen_src = os.path.join(base, r"project\IAR_V7.4\settings\YNGJ-GT1-M.YNGJ-GT1-M.general.xcl")
drv_src = os.path.join(base, r"project\IAR_V7.4\settings\YNGJ-GT1-M.YNGJ-GT1-M.driver.xcl")
lines = open(gen_src, encoding="utf-8", errors="replace").read().splitlines()
new = []
for ln in lines:
new.append(f'"{out_dst}" ' if (".out" in ln and "YNGJ" in ln) else ln)
open(os.path.join(stage, "general.xcl"), "w", encoding="utf-8", newline="\n").write("\n".join(new) + "\n")
shutil.copy2(drv_src, os.path.join(stage, "driver.xcl"))
for n in ("cspybat.exe", "CSpyBat.exe", "JLink.exe", "IarIdePm.exe",
"JLinkGUIServer.exe", "JLinkRTTClient.exe", "JFlash.exe"):
subprocess.run(["taskkill", "/F", "/IM", n], capture_output=True)
time.sleep(1.5)
# Force Artery AT32F403ACx (RCT7) — avoid Cortex-M4 / wrong-chip popup
drv_path = os.path.join(stage, "driver.xcl")
drv_lines = open(drv_path, encoding="utf-8", errors="replace").read().splitlines()
forced = []
for ln in drv_lines:
if ln.strip().startswith("--jlink_device"):
continue
forced.append(ln)
forced.append('--jlink_device=AT32F403AC')
open(drv_path, "w", encoding="utf-8", newline="\n").write("\n".join(forced) + "\n")
print("driver device forced: AT32F403AC", flush=True)
cspy = r"C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.3\common\bin\cspybat.exe"
cmd = [cspy, "-f", os.path.join(stage, "general.xcl"),
f"--debug_file={out_dst}", "--download_only", "--backend",
"-f", drv_path]
logp = os.path.join(stage, "cspy.log")
print("cspybat start (AT32F403AC / FlashAT32F403ARCT7)", flush=True)
with open(logp, "w", encoding="utf-8", errors="replace") as log:
r = subprocess.run(cmd, stdout=log, stderr=subprocess.STDOUT, timeout=180)
print("cspy exit", r.returncode, flush=True)
print(open(logp, encoding="utf-8", errors="replace").read()[-2000:])
if r.returncode != 0:
raise SystemExit(r.returncode)
jlink = r"C:\Program Files\SEGGER\JLink_V818\JLink.exe"
reset = os.path.join(base, r"tools\reset_run.jlink")
print("reset AT32F403AC", flush=True)
subprocess.run([jlink, "-Device", "AT32F403AC", "-If", "SWD",
"-Speed", "4000", "-AutoConnect", "1",
"-CommandFile", reset], capture_output=True, timeout=30)
time.sleep(3)
print("Done", flush=True)

View File

@ -0,0 +1,2 @@
gImage_mixer 34x33
gImage_SystemSetting 33x33

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

186
tools/flash_haitian_res.py Normal file
View File

@ -0,0 +1,186 @@
#!/usr/bin/env python3
"""Flash Doc/音色文件/0907/2.res (or --bin) to W25Q128 @ FLASH_ADDR_SONG_HAITIAN via RTT."""
from __future__ import annotations
import argparse
import os
import sys
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", "音色文件", "0907", "2.res")
FLASH_ADDR_SONG_HAITIAN = 0x0009D07D
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)
print(f"bin={args.bin} size={size} addr=0x{FLASH_ADDR_SONG_HAITIAN: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 haitian {size}\n".encode("ascii")
send_bytes(jlink, cmd)
print("sent flash command, waiting GO...")
log = wait_for(jlink, "FLASH_HAITIAN_GO", 20.0)
print(log.strip().splitlines()[-1])
time.sleep(0.2)
chunk = 256
sent = 0
t0 = time.time()
receiving = False
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 (not receiving) and ("FLASH_RX" in log or "FLASH_ACK" in log):
receiving = True
print("device receiving...")
if "FLASH_HAITIAN_OK" in log:
print(log.strip().splitlines()[-1])
print("Done. Local song bank 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_HAITIAN_OK", 60.0)
print(log.strip().splitlines()[-1])
print("Done. Local song bank updated.")
finally:
try:
jlink.rtt_stop()
except Exception:
pass
jlink.close()
if __name__ == "__main__":
main()

View File

@ -25,7 +25,7 @@ MAP_TXT = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
IAP_DIR = REPO / "升级" / "MCU主控升级" IAP_DIR = REPO / "升级" / "MCU主控升级"
OUT_ROOT = REPO / "tools" / "out" OUT_ROOT = REPO / "tools" / "out"
FW_VER = "0.2.2" FW_VER = "0.2.3"
TONE_RES_VER = "0903" TONE_RES_VER = "0903"
UI0902_RES_BASE = 0x00100000 UI0902_RES_BASE = 0x00100000
@ -126,8 +126,8 @@ def main() -> None:
"", "",
"外部 Flash 分区:", "外部 Flash 分区:",
" 0x00000000 音色/充电图 (toneRes)", " 0x00000000 音色/充电图 (toneRes)",
" logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin + 3.bin", " logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin(HKTK/2s) + 3.bin",
" 0x00100000 UI0902 图片 (模式选择大图全屏开机 Logo、设置/调音台素材等)", " 0x00100000 UI0902 图片 (模式选择、开机 Logo、充电、烧录模式、设置/调音台等)",
"", "",
f" {comb_name} size={comb_dst.stat().st_size} @ 0x00000000", f" {comb_name} size={comb_dst.stat().st_size} @ 0x00000000",
f" sha256={sha256(comb_dst)}", f" sha256={sha256(comb_dst)}",

View File

@ -1,3 +1,4 @@
HideDeviceSelection 1
si SWD si SWD
speed 4000 speed 4000
device Cortex-M4 device Cortex-M4