Fix teacher accomp issues and add ExtFlash chip-erase helper.

Pick fires on downstroke; #IV drops an octave; local songs use 2.bin; transpose updates notes without restart; intro starts on key I; boot string volume/PC corrected.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-04 17:06:02 +08:00
parent c724ddd2c9
commit f179a46c5d
9 changed files with 311 additions and 121 deletions

View File

@ -125,6 +125,8 @@ static void app_adc_pick_process(void)
{ {
pick_hold_tick = now; pick_hold_tick = now;
pick_fsm = PICK_FSM_STATE_DOWN; pick_fsm = PICK_FSM_STATE_DOWN;
/* 音师:拨片下拨即响(进入 DOWN 触发),不要等复位 */
MainTask_Sendmsg(MSG_ID_BP_KEY,PICK_STATE_DOWN,0,0);
} }
if(curstate == PICK_STATE_UP) if(curstate == PICK_STATE_UP)
{ {
@ -136,8 +138,6 @@ static void app_adc_pick_process(void)
if(curstate != PICK_STATE_DOWN) if(curstate != PICK_STATE_DOWN)
{ {
pick_fsm = PICK_FSM_STATE_IDLE; pick_fsm = PICK_FSM_STATE_IDLE;
//发送下拨消息
MainTask_Sendmsg(MSG_ID_BP_KEY,PICK_STATE_DOWN,0,0);
} }
break; break;
case PICK_FSM_STATE_UP: case PICK_FSM_STATE_UP:

View File

@ -264,6 +264,14 @@ 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 erase chip", 16) == 0) {
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERASE_BEGIN\n");
LOG_I("FLASH", "W25Q128 chip erase start");
W25Q128_Erase_Chip();
LOG_I("FLASH", "W25Q128 chip erase done");
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERASE_OK\n");
return 1U;
}
if (strncmp(cmd, "ui boot", 7) == 0) { if (strncmp(cmd, "ui boot", 7) == 0) {
LCD_BLK_Set(); LCD_BLK_Set();
LCD_WR_PIC_FROM_FLASH(0, 0, UI0902_BOOT_LOGO_W, UI0902_BOOT_LOGO_H, LCD_WR_PIC_FROM_FLASH(0, 0, UI0902_BOOT_LOGO_W, UI0902_BOOT_LOGO_H,

View File

@ -25,8 +25,8 @@ GUI_SWITCH mGuiData[] = {
}; };
GUI_SWITCH ParamGuiData[] = GUI_SWITCH ParamGuiData[] =
{ {
{ 0, 0, 30, SONG_MODE_PARAM}, // 0 { 0, 0, 30, SONG_MODE_PARAM}, // 0 节奏类型1.bin约 31 首)
{ 0, 0, 30, EXPRESS_MODE_PARAM}, // 1 { 0, 0, 0, EXPRESS_MODE_PARAM}, // 1 本地曲目2.bin先 1 首可扩)
{ 0, 0, 2, ALL_MODE_PARAM}, // 2 { 0, 0, 2, ALL_MODE_PARAM}, // 2
}; };
@ -360,28 +360,49 @@ void Send_volume(uint8_t vol)
SendMidiDataToDreamDSP(mMasterSysExVolume,12); SendMidiDataToDreamDSP(mMasterSysExVolume,12);
} }
void Accomp_UpdatePlayingChord(void)
{
uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = 0;
uint8_t i;
if (!StartFlag)
return;
if (KEY_ID_1629 == 0 || KEY_ID_1629 == 22 || KEY_ID_1629 == 23)
return;
note_cnt = GetChordNotesByType(KEY_ID_1629,
chord_type_index_map[KEY_ID_1629].type,
midi_note_buff);
for (i = 0; i < note_cnt; i++)
AutoBandTop1_Note_On(midi_note_buff[i], 0x50);
}
void ADC_IN1_KEY_Handle(uint8_t key,bool on) void ADC_IN1_KEY_Handle(uint8_t key,bool on)
{ {
uint8_t midi_note_buff[4] = {0,}; uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = 0; uint8_t note_cnt = 0;
uint8_t is_preamble = 0;
key_toggle[key] = !key_toggle[key]; key_toggle[key] = !key_toggle[key];
if(key_toggle[key] == 1) if(key_toggle[key] == 1)
{ {
//ResetAutoPowerCount(); //ResetAutoPowerCount();
switch(key) switch(key)
{ {
case 3: AutoBandTop1_Preamble(0); break; case 3: AutoBandTop1_Preamble(0); is_preamble = 1; break;
case 0: AutoBandTop1_Preamble(1); break; case 0: AutoBandTop1_Preamble(1); is_preamble = 1; break;
case 1: AutoBandTop1_Postamble(0); break; case 1: AutoBandTop1_Postamble(0); break;
case 2: AutoBandTop1_Postamble(1); break; case 2: AutoBandTop1_Postamble(1); break;
} }
//AutoBandTop1_Start(); //AutoBandTop1_Start();
AutoBandTop1_SyncStart(); AutoBandTop1_SyncStart();
wk_delay_ms(50); wk_delay_ms(50);
//AutoBandTop1_Note_On(USE_MIDI.Midi_5, 0x50); /* 音师:前奏起始 = 当前调一级和弦第一把位index 1 + maj再经移调 */
//AutoBandTop1_SetPiecePlayMode(1,0,1920); if (is_preamble)
//AutoBandTop1_StartWithNotes(midi_note_buff,GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff)); {
//AutoBandTop1_SetLoopMode(0,1); KEY_ID_1629 = 1;
chord_type_index_map[1].type = 0;
}
note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff); note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
for(uint8_t i = 0;i < note_cnt;i++) for(uint8_t i = 0;i < note_cnt;i++)
{ {
@ -880,6 +901,11 @@ void BT_Chord_offset_map(uint8_t pos_offset,uint8_t chord_offset)
uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff) uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
{ {
uint8_t n = 0;
uint8_t i;
uint8_t root;
buff[0] = buff[1] = buff[2] = buff[3] = 0;
switch(MIDI_Index) switch(MIDI_Index)
{ {
case 1:case 2:case 3: case 1:case 2:case 3:
@ -1109,49 +1135,41 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
break; break;
} }
break; break;
default:
return 0;
} }
if(buff[3]!= 0)
n = (buff[3] != 0) ? 4 : 3;
switch (chord_type_index_map[MIDI_Index].PitchOffset)
{ {
switch(chord_type_index_map[MIDI_Index].PitchOffset) case 2: /* # 升:整体 +1 半音 */
{ for (i = 0; i < n; i++)
case 1: buff[i] += 1;
buff[0] += 0; break;
buff[1] += 0; default:
buff[2] += 0; break;
buff[3] += 0;
break;
case 2:
buff[0] += 1;
buff[1] += 1;
buff[2] += 1;
buff[3] += 1;
break;
}
buff[0] += mGuiData[GUI_TRANSPOSE].Current;
buff[1] += mGuiData[GUI_TRANSPOSE].Current;
buff[2] += mGuiData[GUI_TRANSPOSE].Current;
buff[3] += mGuiData[GUI_TRANSPOSE].Current;
return 4;
}else
{
switch(chord_type_index_map[MIDI_Index].PitchOffset)
{
case 1:
buff[0] += 0;
buff[1] += 0;
buff[2] += 0;
break;
case 2:
buff[0] += 1;
buff[1] += 1;
buff[2] += 1;
break;
}
buff[0] += mGuiData[GUI_TRANSPOSE].Current;
buff[1] += mGuiData[GUI_TRANSPOSE].Current;
buff[2] += mGuiData[GUI_TRANSPOSE].Current;
return 3;
} }
for (i = 0; i < n; i++)
buff[i] += mGuiData[GUI_TRANSPOSE].Current;
/* 音师:所有调的 #四级 — 整组和弦低八度,音域压在一个八度内 */
if (chord_type_index_map[MIDI_Index].PitchOffset == 2)
{
for (i = 0; i < n; i++)
{
if (buff[i] >= 12)
buff[i] -= 12;
}
root = buff[0];
for (i = 1; i < n; i++)
{
while (buff[i] >= (uint8_t)(root + 12))
buff[i] -= 12;
while (buff[i] < root)
buff[i] += 12;
}
}
return n;
} }
void Read_Cur_Measure() void Read_Cur_Measure()

View File

@ -221,6 +221,8 @@ void Loop_Light_Handle(uint8_t measure);
void BL_Set_led(uint8_t Led, uint8_t chord_type); void BL_Set_led(uint8_t Led, uint8_t chord_type);
uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff); uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff);
/* 伴奏已在播时仅更新和弦音,不 Restart快转调 */
void Accomp_UpdatePlayingChord(void);
void BT_Chord_offset_map(uint8_t pos_offset,uint8_t chord_offset); void BT_Chord_offset_map(uint8_t pos_offset,uint8_t chord_offset);
void BT_Pitch_offset_map(uint8_t pos_offset,uint8_t pitch_offset); void BT_Pitch_offset_map(uint8_t pos_offset,uint8_t pitch_offset);
void Read_Cur_Measure(); void Read_Cur_Measure();

View File

@ -113,7 +113,7 @@ void LoadConfig()
{ {
SendProgramChangeMidiEvent(0,Timbre_index[mGuiData[GUI_TIMBRE_SELECT].Current]); SendProgramChangeMidiEvent(0,Timbre_index[mGuiData[GUI_TIMBRE_SELECT].Current]);
uint8_t val_127 = (faders[0].val * 127) / 20; uint8_t val_127 = (faders[0].val * 127) / 10;
SendVolumeChangeMidiEvent(1,val_127); SendVolumeChangeMidiEvent(1,val_127);
SendVolumeChangeMidiEvent(2,val_127); SendVolumeChangeMidiEvent(2,val_127);
SendVolumeChangeMidiEvent(3,val_127); SendVolumeChangeMidiEvent(3,val_127);
@ -130,7 +130,7 @@ void LoadConfig()
SendVolumeChangeMidiEvent(14,val_127); SendVolumeChangeMidiEvent(14,val_127);
SendVolumeChangeMidiEvent(15,val_127); SendVolumeChangeMidiEvent(15,val_127);
SendVolumeChangeMidiEvent(0,faders[1].val); SendVolumeChangeMidiEvent(0,(faders[1].val * 127) / 10);
AW816_Set_Volume(MIC_VOL_MAP[faders[2].val]); AW816_Set_Volume(MIC_VOL_MAP[faders[2].val]);
@ -147,25 +147,21 @@ void Touch_Mode_Select_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex)
InModeFlag = true; InModeFlag = true;
/* 进入子页后抑制残留按下,否则易误触底栏 GUI_TAB_INDEX(id=5)→设置 */ /* 进入子页后抑制残留按下,否则易误触底栏 GUI_TAB_INDEX(id=5)→设置 */
app_touch_suppress(500); app_touch_suppress(500);
/* 开机后再次确认弦音色DSP 已就绪) */
SendProgramChangeMidiEvent(0,Timbre_index[mGuiData[GUI_TIMBRE_SELECT].Current]);
switch (ID) switch (ID)
{ {
case 0: /* 万能 → 3.bin */ case 0: /* 万能 → 3.bin / 本地→2.bin */
if (mGuiData[GUI_AUTOBAND_SW].Current) UI_ApplyToneAddress();
ADDRESS = FLASH_ADDR_AUTOBAND_LEGACY;
else
ADDRESS = FLASH_ADDR_MODE_UNIVERSAL;
MenuPage_SongMode_Proc(); MenuPage_SongMode_Proc();
break; break;
case 1: /* 专业 → 1.bin */ case 1: /* 专业 → 1.bin / 本地→2.bin */
if (mGuiData[GUI_AUTOBAND_SW].Current) UI_ApplyToneAddress();
ADDRESS = FLASH_ADDR_AUTOBAND_LEGACY;
else
ADDRESS = FLASH_ADDR_MODE_EXPERT;
MenuPage_ExpertMode_Proc(); MenuPage_ExpertMode_Proc();
break; break;
case 2: /* 普通 → 1.bin */ case 2: /* 普通 → 1.bin */
ADDRESS = FLASH_ADDR_MODE_NORMAL; UI_ApplyToneAddress();
MenuPage_FreeMode_Proc(); MenuPage_FreeMode_Proc();
break; break;
default: default:

View File

@ -72,7 +72,6 @@ int8_t delta = 0;
void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value) void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
{ {
uint8_t last_value; uint8_t last_value;
uint8_t midi_note_buff[4] = {0,};
NvmParam_Type * nvm = drv_nvm_param_ptr(); NvmParam_Type * nvm = drv_nvm_param_ptr();
if(FLAG == 1 || FLAG == -1) if(FLAG == 1 || FLAG == -1)
{ {
@ -116,17 +115,9 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
} }
break; break;
case 1: case 1:
{
AutoBandTop1_SetPiecePlayMode(1,0,480); // AutoBand 模块未加入工程
AutoBandTop1_StartWithNotes(midi_note_buff,GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff));
AutoBandTop1_SetLoopMode(0,1);
}
break;
case 0: case 0:
{ /* 音师:快转调只换和弦音,伴奏不重头 */
AutoBandTop1_SetPiecePlayMode(1,0,480); // AutoBand 模块未加入工程 Accomp_UpdatePlayingChord();
AutoBandTop1_StartWithNotes(midi_note_buff,GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff));
}
break; break;
} }
} }
@ -181,10 +172,12 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
case 0: case 1: case 0: case 1:
if(mGuiData[GUI_AUTOBAND_SW].Current) if(mGuiData[GUI_AUTOBAND_SW].Current)
{ {
AutoBandTop1_LoadPresetItemFromFlash(0); UI_ApplyToneAddress();
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[EXPRESS_MODE_PARAM].Current);
} }
else else
{ {
UI_ApplyToneAddress();
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[SONG_MODE_PARAM].Current); AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[SONG_MODE_PARAM].Current);
} }
@ -222,20 +215,7 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
if(ID == GUI_AUTOBAND_SW) if(ID == GUI_AUTOBAND_SW)
{ {
mGuiData[ID].Current = value; mGuiData[ID].Current = value;
UI_ReloadTonePreset();
if(mGuiData[GUI_AUTOBAND_SW].Current)
{
ADDRESS = FLASH_ADDR_AUTOBAND_LEGACY;
}else
{
/* 关闭 AutoBand回到当前 TAB 对应音色库 */
switch (mGuiData[GUI_TAB_INDEX].Current)
{
case 1: ADDRESS = FLASH_ADDR_MODE_EXPERT; break;
case 2: ADDRESS = FLASH_ADDR_MODE_NORMAL; break;
default: ADDRESS = FLASH_ADDR_MODE_UNIVERSAL; break;
}
}
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]);
return; return;
@ -269,26 +249,20 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
app_tm1629_all_on(LED_COLOR_G); app_tm1629_all_on(LED_COLOR_G);
switch(mGuiData[ID].Current) switch(mGuiData[ID].Current)
{ {
case 0: /* 万能 → 3.bin */ case 0: /* 万能 → 3.bin / 本地→2.bin */
if(mGuiData[GUI_TAB_LAST_INDEX].Current == mGuiData[ID].Current) if(mGuiData[GUI_TAB_LAST_INDEX].Current == mGuiData[ID].Current)
break; break;
StartFlag = 0; StartFlag = 0;
AutoBandTop1_Stop(); AutoBandTop1_Stop();
if(mGuiData[GUI_AUTOBAND_SW].Current) UI_ApplyToneAddress();
ADDRESS = FLASH_ADDR_AUTOBAND_LEGACY;
else
ADDRESS = FLASH_ADDR_MODE_UNIVERSAL;
break; break;
case 1: /* 专业 → 1.bin */ case 1: /* 专业 → 1.bin / 本地→2.bin */
if(mGuiData[GUI_TAB_LAST_INDEX].Current == mGuiData[ID].Current) if(mGuiData[GUI_TAB_LAST_INDEX].Current == mGuiData[ID].Current)
break; break;
StartFlag = 0; StartFlag = 0;
AutoBandTop1_Stop(); AutoBandTop1_Stop();
if(mGuiData[GUI_AUTOBAND_SW].Current) UI_ApplyToneAddress();
ADDRESS = FLASH_ADDR_AUTOBAND_LEGACY;
else
ADDRESS = FLASH_ADDR_MODE_EXPERT;
break; break;
case 2: /* 普通 → 1.bin */ case 2: /* 普通 → 1.bin */
@ -296,7 +270,7 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
break; break;
StartFlag = 0; StartFlag = 0;
AutoBandTop1_Stop(); AutoBandTop1_Stop();
ADDRESS = FLASH_ADDR_MODE_NORMAL; UI_ApplyToneAddress();
break; break;
} }
Refresh_TabSelect_ui(&UI_Label[ID], &mGuiData[ID]); Refresh_TabSelect_ui(&UI_Label[ID], &mGuiData[ID]);
@ -325,7 +299,6 @@ void UI_SongMode_Init(void)
void UI_SongMode_Process(DisplayTaskMessage_Type msg) void UI_SongMode_Process(DisplayTaskMessage_Type msg)
{ {
uint8_t midi_note_buff[4] = {0,};
NvmParam_Type * nvm = drv_nvm_param_ptr(); NvmParam_Type * nvm = drv_nvm_param_ptr();
switch(msg.MessageType) switch(msg.MessageType)
@ -475,10 +448,8 @@ void UI_SongMode_Process(DisplayTaskMessage_Type msg)
USE_MIDI.Midi_6 += delta; USE_MIDI.Midi_6 += delta;
if(/*KEY_ID_1629 != 0 && */StartFlag && KEY_ID_1629 != 22 && KEY_ID_1629 != 23) if(/*KEY_ID_1629 != 0 && */StartFlag && KEY_ID_1629 != 22 && KEY_ID_1629 != 23)
{ {
AutoBandTop1_SetPiecePlayMode(1,0,480); /* 音师:快转调只换和弦音,伴奏不重头 */
AutoBandTop1_StartWithNotes(midi_note_buff,GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff)); Accomp_UpdatePlayingChord();
AutoBandTop1_SetLoopMode(0,1);
AutoBandTop1_StartWithNotes(midi_note_buff,GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff));
} }
ResetAutoPowerCount(); ResetAutoPowerCount();
} }

View File

@ -109,6 +109,41 @@ void CallUI_RestoreSelect(void) {
LOG_I("UI", "page Restore"); LOG_I("UI", "page Restore");
} }
/* 音师节奏类型→1.bin本地曲目→2.bin万能节奏→3.bin不再用 AutoBand legacy */
void UI_ApplyToneAddress(void)
{
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
ADDRESS = FLASH_ADDR_SONG_HAITIAN; /* 本地曲目 → 2.bin */
return;
}
switch (mGuiData[GUI_TAB_INDEX].Current)
{
case 1: ADDRESS = FLASH_ADDR_MODE_EXPERT; break; /* 专业 → 1.bin */
case 2: ADDRESS = FLASH_ADDR_MODE_NORMAL; break; /* 普通 → 1.bin */
default: ADDRESS = FLASH_ADDR_MODE_UNIVERSAL; break; /* 万能 → 3.bin */
}
}
void UI_ReloadTonePreset(void)
{
UI_ApplyToneAddress();
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[EXPRESS_MODE_PARAM].Current);
return;
}
switch (mGuiData[GUI_TAB_INDEX].Current)
{
case 2:
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current);
break;
default:
AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[SONG_MODE_PARAM].Current);
break;
}
}
static uint8_t s_ui_focus_id = UI_FOCUS_NONE; static uint8_t s_ui_focus_id = UI_FOCUS_NONE;
static uint8_t UI_NormalizeFocusId(uint8_t widget_id) static uint8_t UI_NormalizeFocusId(uint8_t widget_id)
@ -236,19 +271,15 @@ void UI_GotoCurrentModePage(void)
switch (mGuiData[GUI_TAB_INDEX].Current) switch (mGuiData[GUI_TAB_INDEX].Current)
{ {
case 0: case 0:
ADDRESS = mGuiData[GUI_AUTOBAND_SW].Current UI_ApplyToneAddress();
? FLASH_ADDR_AUTOBAND_LEGACY
: FLASH_ADDR_MODE_UNIVERSAL;
MenuPage_SongMode_Proc(); MenuPage_SongMode_Proc();
break; break;
case 1: case 1:
ADDRESS = mGuiData[GUI_AUTOBAND_SW].Current UI_ApplyToneAddress();
? FLASH_ADDR_AUTOBAND_LEGACY
: FLASH_ADDR_MODE_EXPERT;
MenuPage_ExpertMode_Proc(); MenuPage_ExpertMode_Proc();
break; break;
case 2: case 2:
ADDRESS = FLASH_ADDR_MODE_NORMAL; UI_ApplyToneAddress();
MenuPage_FreeMode_Proc(); MenuPage_FreeMode_Proc();
break; break;
default: default:
@ -818,19 +849,15 @@ void UI_NavBar_Action(uint8_t value)
switch (value) switch (value)
{ {
case UI0902_NAV_UNIVERSAL: case UI0902_NAV_UNIVERSAL:
ADDRESS = mGuiData[GUI_AUTOBAND_SW].Current UI_ApplyToneAddress();
? FLASH_ADDR_AUTOBAND_LEGACY
: FLASH_ADDR_MODE_UNIVERSAL;
MenuPage_SongMode_Proc(); MenuPage_SongMode_Proc();
break; break;
case UI0902_NAV_NORMAL: case UI0902_NAV_NORMAL:
ADDRESS = FLASH_ADDR_MODE_NORMAL; UI_ApplyToneAddress();
MenuPage_FreeMode_Proc(); MenuPage_FreeMode_Proc();
break; break;
case UI0902_NAV_EXPERT: case UI0902_NAV_EXPERT:
ADDRESS = mGuiData[GUI_AUTOBAND_SW].Current UI_ApplyToneAddress();
? FLASH_ADDR_AUTOBAND_LEGACY
: FLASH_ADDR_MODE_EXPERT;
MenuPage_ExpertMode_Proc(); MenuPage_ExpertMode_Proc();
break; break;
case UI0902_NAV_SETTING: case UI0902_NAV_SETTING:

View File

@ -218,4 +218,7 @@ extern void RestoreSetting();
extern void Draw_System_Item_List(const LEBEL_UI *UI, GUI_SWITCH *Group); extern void Draw_System_Item_List(const LEBEL_UI *UI, GUI_SWITCH *Group);
/* 子页顶栏:左返回 + 居中标题(调音台/系统设置) */ /* 子页顶栏:左返回 + 居中标题(调音台/系统设置) */
extern void UI_DrawSubPageHeader(const uint8_t *title_gbk); extern void UI_DrawSubPageHeader(const uint8_t *title_gbk);
/* 节奏类型→1.bin / 本地曲目→2.bin / 万能→3.bin */
extern void UI_ApplyToneAddress(void);
extern void UI_ReloadTonePreset(void);
#endif #endif

View File

@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""Erase entire W25Q128 external Flash via RTT command: flash erase chip
Requires firmware that implements the RTT command (APP/app_log.c).
Chip erase typically takes 30120s; do not power-cycle mid-erase.
"""
from __future__ import annotations
import argparse
import time
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
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) -> str:
deadline = time.time() + timeout_s
buf = ""
last_print = 0.0
while time.time() < deadline:
chunk = rtt_read_text(jlink, 0.2)
if chunk:
buf += chunk
if marker in buf:
return buf
now = time.time()
if now - last_print >= 5.0:
elapsed = now - (deadline - timeout_s)
print(f" ... waiting erase ({elapsed:.0f}s)")
last_print = now
time.sleep(0.05)
raise SystemExit(f"Timeout waiting for {marker!r}\n--- RTT ---\n{buf[-1200:]}")
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("--device", default="Cortex-M4")
parser.add_argument("--no-reset", action="store_true")
parser.add_argument("--timeout", type=float, default=180.0,
help="seconds to wait for FLASH_ERASE_OK (default 180)")
args = parser.parse_args()
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)
print("Sending: flash erase chip")
send_bytes(jlink, b"flash erase chip\n")
log = wait_for(jlink, "FLASH_ERASE_BEGIN", 20.0)
print(log.strip().splitlines()[-1])
print(f"Chip erase running (timeout {args.timeout:.0f}s)...")
log = wait_for(jlink, "FLASH_ERASE_OK", args.timeout)
print(log.strip().splitlines()[-1])
print("ExtFlash chip erase done. Flash MCU + extflash_ALL*.res manually.")
finally:
try:
jlink.rtt_stop()
except Exception:
pass
jlink.close()
if __name__ == "__main__":
main()