Compare commits
2 Commits
4544f8706a
...
01bc2a592c
| Author | SHA1 | Date |
|---|---|---|
|
|
01bc2a592c | |
|
|
a6259b5fca |
|
|
@ -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,3}; /* major.minor.patch — 0.2.3: local song HKTK load/play + tone addr logs */
|
const uint8_t Version[3] = {0,2,4}; /* major.minor.patch — 0.2.4: Boot font 「烧录模式」+ release with Boot */
|
||||||
|
|
||||||
uint8_t Led = 8;
|
uint8_t Led = 8;
|
||||||
bool PressFlag = 0;
|
bool PressFlag = 0;
|
||||||
|
|
@ -259,6 +259,23 @@ static void PowerOff(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* App remote power-off request (BLE cmd 05 00): executed in main-loop context,
|
||||||
|
so StopFullTask() never detaches the calling BT thread itself. */
|
||||||
|
static volatile uint8_t s_sys_poweroff_req = 0;
|
||||||
|
void System_RequestPowerOff(void)
|
||||||
|
{
|
||||||
|
s_sys_poweroff_req = 1;
|
||||||
|
}
|
||||||
|
void System_PowerOff_Poll(void)
|
||||||
|
{
|
||||||
|
if (s_sys_poweroff_req)
|
||||||
|
{
|
||||||
|
s_sys_poweroff_req = 0;
|
||||||
|
if (powon)
|
||||||
|
PowerOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Power_Key_Scan()
|
void Power_Key_Scan()
|
||||||
{
|
{
|
||||||
flag_status status = BSP_GetPowerKey();
|
flag_status status = BSP_GetPowerKey();
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,8 @@ extern bool powon;
|
||||||
void Power_Key_Scan(void);
|
void Power_Key_Scan(void);
|
||||||
void AutoPowerOff_Scan(void);
|
void AutoPowerOff_Scan(void);
|
||||||
void System_PowerOn(void);
|
void System_PowerOn(void);
|
||||||
|
void System_RequestPowerOff(void);
|
||||||
|
void System_PowerOff_Poll(void);
|
||||||
extern uint8_t Led;
|
extern uint8_t Led;
|
||||||
extern bool PressFlag;
|
extern bool PressFlag;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ int main(void)
|
||||||
/* add user code begin 3 */
|
/* add user code begin 3 */
|
||||||
Power_Key_Scan();
|
Power_Key_Scan();
|
||||||
AutoPowerOff_Scan();
|
AutoPowerOff_Scan();
|
||||||
|
System_PowerOff_Poll();
|
||||||
LCD_Dump_Poll();
|
LCD_Dump_Poll();
|
||||||
#if !DEBUG_LCD_DUMP
|
#if !DEBUG_LCD_DUMP
|
||||||
app_log_poll();
|
app_log_poll();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ typedef enum
|
||||||
UART4_RCV_BUFF_MIDIEND
|
UART4_RCV_BUFF_MIDIEND
|
||||||
} UART4_RCV_StatusType;
|
} UART4_RCV_StatusType;
|
||||||
|
|
||||||
#define UART4_PROCESS_BUFF_SIZE 32
|
#define UART4_PROCESS_BUFF_SIZE 64 /* 02 04 full chord-map frame is 47 bytes */
|
||||||
|
|
||||||
// UART4<54><34><EFBFBD><EFBFBD>ȫ<EFBFBD>ֱ<EFBFBD><D6B1><EFBFBD>
|
// UART4<54><34><EFBFBD><EFBFBD>ȫ<EFBFBD>ֱ<EFBFBD><D6B1><EFBFBD>
|
||||||
static UART4_RCV_StatusType UART4_RCV_Status = UART4_RCV_BUFF_IDLE;
|
static UART4_RCV_StatusType UART4_RCV_Status = UART4_RCV_BUFF_IDLE;
|
||||||
|
|
@ -48,22 +48,25 @@ typedef struct
|
||||||
// ==============================
|
// ==============================
|
||||||
// ָ<><EFBFBD><EEB4A6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB> static<69><63>
|
// ָ<><EFBFBD><EEB4A6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȫ<EFBFBD><C8AB> static<69><63>
|
||||||
// ==============================
|
// ==============================
|
||||||
//static void handleDevDisconnect(uint8_t *data);
|
static void handleDevDisconnect(uint8_t *data);
|
||||||
//static void handleDevConnect(uint8_t *data);
|
static void handleDevConnect(uint8_t *data);
|
||||||
//static void handleDevName(uint8_t *data);
|
static void handleDevName(uint8_t *data);
|
||||||
//static void handleFwMainVer(uint8_t *data);
|
static void handleFwMainVer(uint8_t *data);
|
||||||
//static void handleSoundVer(uint8_t *data);
|
static void handleSoundVer(uint8_t *data);
|
||||||
//static void handleUIVer(uint8_t *data);
|
static void handleUIVer(uint8_t *data);
|
||||||
//static void handleOtherInfo(uint8_t *data);
|
static void handleOtherInfo(uint8_t *data);
|
||||||
//static void handleDevCode(uint8_t *data);
|
static void handleDevCode(uint8_t *data);
|
||||||
//static void handleAutoPowerOff(uint8_t *data);
|
static void handleAutoPowerOff(uint8_t *data);
|
||||||
|
static void handleUserFwVer(uint8_t *data);
|
||||||
|
static void handleAutoPowerOffSet(uint8_t *data);
|
||||||
|
static void handleFwCode(uint8_t *data);
|
||||||
|
|
||||||
static void handleReadRhythmMap(uint8_t *data);
|
static void handleReadRhythmMap(uint8_t *data);
|
||||||
static void handlePitchOffset(uint8_t *data);
|
static void handlePitchOffset(uint8_t *data);
|
||||||
static void handleChordOffset(uint8_t *data);
|
static void handleChordOffset(uint8_t *data);
|
||||||
//static void handleResetChordMap(uint8_t *data);
|
static void handleResetChordMap(uint8_t *data);
|
||||||
|
|
||||||
//static void handleRhythmStyle(uint8_t *data);
|
static void handleRhythmStyle(uint8_t *data);
|
||||||
static void handleStringTimbre(uint8_t *data);
|
static void handleStringTimbre(uint8_t *data);
|
||||||
static void handleBPM(uint8_t *data);
|
static void handleBPM(uint8_t *data);
|
||||||
static void handleTranspose(uint8_t *data);
|
static void handleTranspose(uint8_t *data);
|
||||||
|
|
@ -75,9 +78,8 @@ static void handleLED3(uint8_t *data);
|
||||||
static void handleLED4(uint8_t *data);
|
static void handleLED4(uint8_t *data);
|
||||||
static void handleLED5(uint8_t *data);
|
static void handleLED5(uint8_t *data);
|
||||||
static void handleLED6(uint8_t *data);
|
static void handleLED6(uint8_t *data);
|
||||||
//static void handleStopPlay(uint8_t *data);
|
|
||||||
|
|
||||||
//static void handleDeviceReset(uint8_t *data);
|
static void handleDeviceReset(uint8_t *data);
|
||||||
|
|
||||||
static void handleIntro(uint8_t *data);
|
static void handleIntro(uint8_t *data);
|
||||||
static void handleInterlude(uint8_t *data);
|
static void handleInterlude(uint8_t *data);
|
||||||
|
|
@ -98,52 +100,53 @@ static void handleSectionD(uint8_t *data);
|
||||||
*************************************************/
|
*************************************************/
|
||||||
static const BLE_SysExCmdItem bleSysExCmdTable[] =
|
static const BLE_SysExCmdItem bleSysExCmdTable[] =
|
||||||
{
|
{
|
||||||
// //======== 1. <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ 0x01 ========
|
/*======== 1. device info 0x01 ========*/
|
||||||
// {{0x01, 0x00}, handleDevDisconnect}, // <20>Ͽ<EFBFBD><CFBF>豸
|
{{0x01, 0x00}, handleDevDisconnect}, /* disconnect */
|
||||||
// {{0x01, 0x01}, handleDevConnect}, // <20><><EFBFBD><EFBFBD><EFBFBD>豸
|
{{0x01, 0x01}, handleDevConnect}, /* connect */
|
||||||
// {{0x01, 0x02}, handleDevName}, // <20><>ȡ<EFBFBD>豸<EFBFBD><E8B1B8>
|
{{0x01, 0x02}, handleDevName}, /* device name */
|
||||||
// {{0x01, 0x03}, handleFwMainVer}, // <20>̼<EFBFBD><CCBC><EFBFBD><EFBFBD>汾
|
{{0x01, 0x03}, handleFwMainVer}, /* fw main version */
|
||||||
// {{0x01, 0x04}, handleSoundVer}, // <20><>Դ<EFBFBD>汾
|
{{0x01, 0x04}, handleSoundVer}, /* sound version */
|
||||||
// {{0x01, 0x05}, handleUIVer}, // UI<55>汾
|
{{0x01, 0x05}, handleUIVer}, /* UI version */
|
||||||
// {{0x01, 0x06}, handleOtherInfo}, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ
|
{{0x01, 0x06}, handleOtherInfo}, /* other info */
|
||||||
// {{0x01, 0x07}, handleDevCode}, // <20>豸<EFBFBD><E8B1B8><EFBFBD><EFBFBD>
|
{{0x01, 0x07}, handleDevCode}, /* device code (96bit UID) */
|
||||||
// {{0x01, 0x0A}, handleAutoPowerOff}, // <20>Զ<EFBFBD><D4B6>ػ<EFBFBD>
|
{{0x01, 0x0A}, handleAutoPowerOff}, /* auto power-off read (minutes) */
|
||||||
//
|
{{0x01, 0x0C}, handleUserFwVer}, /* user fw version */
|
||||||
// //======== 2. ӳ<><D3B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0x02 ========
|
{{0x01, 0x11}, handleAutoPowerOffSet}, /* auto power-off set (minutes) */
|
||||||
{{0x02, 0x01}, handleReadRhythmMap}, // <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3><EFBFBD>
|
{{0x01, 0xFF}, handleFwCode}, /* fw code */
|
||||||
{{0x02, 0x02}, handlePitchOffset}, // Pitchƫ<68><C6AB>
|
|
||||||
{{0x02, 0x03}, handleChordOffset}, // Chordƫ<64><C6AB>
|
|
||||||
// {{0x02, 0x04}, handleResetChordMap}, // <20><><EFBFBD>ú<EFBFBD><C3BA><EFBFBD>ӳ<EFBFBD><D3B3>Ĭ<EFBFBD><C4AC>ֵ
|
|
||||||
|
|
||||||
//======== 3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д 0x03 ========
|
/*======== 2. chord map 0x02 ========*/
|
||||||
// {{0x03, 0x04}, handleRhythmStyle}, // <20><>ȡ/<2F><><EFBFBD>ý<EFBFBD><C3BD><EFBFBD><EFBFBD><EFBFBD>
|
{{0x02, 0x01}, handleReadRhythmMap}, /* read chord/pitch map */
|
||||||
{{0x03, 0x05}, handleStringTimbre}, // <20><>ȡ/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ
|
{{0x02, 0x02}, handlePitchOffset}, /* pitch offset */
|
||||||
{{0x03, 0x06}, handleBPM}, // <20><>ȡ/<2F><><EFBFBD><EFBFBD>BPM
|
{{0x02, 0x03}, handleChordOffset}, /* chord offset */
|
||||||
{{0x03, 0x07}, handleTranspose}, // <20><>ȡ/<2F><><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD>
|
{{0x02, 0x04}, handleResetChordMap}, /* write whole map (default reset) */
|
||||||
|
|
||||||
//======== 4. <20><><EFBFBD><EFBFBD>/LED<45><44><EFBFBD><EFBFBD> 0x04 ========
|
/*======== 3. guitar params 0x03 ========*/
|
||||||
{{0x04, 0x00}, handleLED0}, // <20><>1<EFBFBD><31>LED
|
{{0x03, 0x04}, handleRhythmStyle}, /* rhythm style r/w + user list */
|
||||||
{{0x04, 0x01}, handleLED1}, // <20><>2<EFBFBD><32>LED
|
{{0x03, 0x05}, handleStringTimbre}, /* string timbre r/w */
|
||||||
{{0x04, 0x02}, handleLED2}, // <20><>3<EFBFBD><33>LED
|
{{0x03, 0x06}, handleBPM}, /* BPM r/w */
|
||||||
{{0x04, 0x03}, handleLED3}, // <20><>4<EFBFBD><34>LED
|
{{0x03, 0x07}, handleTranspose}, /* transpose r/w */
|
||||||
{{0x04, 0x04}, handleLED4}, // <20><>5<EFBFBD><35>LED
|
|
||||||
{{0x04, 0x05}, handleLED5}, // <20><>6<EFBFBD><36>LED
|
|
||||||
{{0x04, 0x06}, handleLED6}, // <20><>7<EFBFBD><37>LED
|
|
||||||
// {{0x04, 0x07}, handleStopPlay}, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
|
|
||||||
// //======== 5. <20><>λ/<2F>ػ<EFBFBD> 0x05 ========
|
/*======== 4. play / LED 0x04 ========*/
|
||||||
// {{0x05, 0x00}, handleDeviceReset}, // <20>豸<EFBFBD><E8B1B8>λ
|
{{0x04, 0x00}, handleLED0}, /* LED 1 */
|
||||||
//
|
{{0x04, 0x01}, handleLED1}, /* LED 2 */
|
||||||
// //======== 6. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת 0x06 ========
|
{{0x04, 0x02}, handleLED2}, /* LED 3 */
|
||||||
{{0x06, 0x01}, handleIntro}, // ǰ<><C7B0>
|
{{0x04, 0x03}, handleLED3}, /* LED 4 */
|
||||||
{{0x06, 0x02}, handleInterlude}, // <20><><EFBFBD><EFBFBD>
|
{{0x04, 0x04}, handleLED4}, /* LED 5 */
|
||||||
{{0x06, 0x03}, handleOutro}, // β<><CEB2>
|
{{0x04, 0x05}, handleLED5}, /* LED 6 */
|
||||||
{{0x04, 0x07}, handleEnd}, // end
|
{{0x04, 0x06}, handleLED6}, /* LED 7 */
|
||||||
{{0x06, 0x05}, handleSectionA}, // A<><41>
|
{{0x04, 0x07}, handleEnd}, /* end / stop play */
|
||||||
{{0x06, 0x06}, handleSectionB}, // B<><42>
|
|
||||||
{{0x06, 0x07}, handleSectionC}, // C<><43>
|
|
||||||
{{0x06, 0x08}, handleSectionD}, // D<><44>
|
|
||||||
|
|
||||||
|
/*======== 5. reset / power off 0x05 ========*/
|
||||||
|
{{0x05, 0x00}, handleDeviceReset}, /* device reset -> soft power off */
|
||||||
|
|
||||||
|
/*======== 6. section jump 0x06 ========*/
|
||||||
|
{{0x06, 0x01}, handleIntro}, /* intro */
|
||||||
|
{{0x06, 0x02}, handleInterlude}, /* interlude */
|
||||||
|
{{0x06, 0x03}, handleOutro}, /* outro */
|
||||||
|
{{0x06, 0x05}, handleSectionA}, /* section A */
|
||||||
|
{{0x06, 0x06}, handleSectionB}, /* section B */
|
||||||
|
{{0x06, 0x07}, handleSectionC}, /* section C */
|
||||||
|
{{0x06, 0x08}, handleSectionD}, /* section D */
|
||||||
};
|
};
|
||||||
|
|
||||||
// ָ<><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
// ָ<><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
|
@ -482,7 +485,7 @@ static void handleChordOffset(uint8_t *data)
|
||||||
extern CHORD_TYPE_INDEX chord_type_index_map[22];
|
extern CHORD_TYPE_INDEX chord_type_index_map[22];
|
||||||
static void handleReadRhythmMap(uint8_t *data)
|
static void handleReadRhythmMap(uint8_t *data)
|
||||||
{
|
{
|
||||||
uint8_t ReadChordMap[47] = {0xF0, 0x60, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7};
|
uint8_t ReadChordMap[47] = {0xF0, 0x60, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7};
|
||||||
uint8_t chord_map = 0;
|
uint8_t chord_map = 0;
|
||||||
for(uint8_t i = 1;i < 22;i ++)
|
for(uint8_t i = 1;i < 22;i ++)
|
||||||
{
|
{
|
||||||
|
|
@ -546,7 +549,7 @@ static void handleEnd(uint8_t *data)
|
||||||
// TM1629D_AllLedOn(LED_COLOR_G);
|
// TM1629D_AllLedOn(LED_COLOR_G);
|
||||||
// TM1629D_UpdateDisplay(0);
|
// TM1629D_UpdateDisplay(0);
|
||||||
// osMutexRelease(Tm1629Mutex);
|
// osMutexRelease(Tm1629Mutex);
|
||||||
BL_Sendmsg(MSG_ID_ADCIN1KEY, 1, 0, 0);
|
BL_Sendmsg(MSG_ID_ADCIN1KEY, 2, 0, 0); /* End -> Postamble(1), distinct from Outro(key=1) */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleSectionA(uint8_t *data)
|
static void handleSectionA(uint8_t *data)
|
||||||
|
|
@ -570,3 +573,195 @@ static void BL_Sendmsg(uint16_t Data1, uint16_t Data2, uint16_t Data3, uint16_t
|
||||||
{
|
{
|
||||||
BLTask_Sendmsg(Data1, Data2, Data3, Data4);
|
BLTask_Sendmsg(Data1, Data2, Data3, Data4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ======================================================================
|
||||||
|
* Appended protocol handlers (per Doc/指令测试.docx)
|
||||||
|
* ==================================================================== */
|
||||||
|
|
||||||
|
/*-------- 1. device info 0x01 --------*/
|
||||||
|
#define BLE_DEVICE_NAME "PHON_145" /* must match BLE module broadcast name */
|
||||||
|
#define AT32_UID_BASE 0x1FFFF7E8UL /* 96-bit unique device ID */
|
||||||
|
|
||||||
|
/* placeholders: sound fw lives on Dream DSP side, UI/user-fw ver bumped per release */
|
||||||
|
static const uint8_t SOUND_VER[4] = {0x4D, 0x06, 0x7F, 0x01};
|
||||||
|
static const uint8_t UI_VER[4] = {0x21, 0x05, 0x7F, 0x01};
|
||||||
|
static const uint8_t USER_FW_VER[4] = {0x4D, 0x06, 0x7F, 0x01};
|
||||||
|
|
||||||
|
static void handleDevDisconnect(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[6] = {0xF0, 0x60, 0x01, 0x00, 0x01, 0xF7};
|
||||||
|
(void)data;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleDevConnect(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[6] = {0xF0, 0x60, 0x01, 0x01, 0x01, 0xF7};
|
||||||
|
(void)data;
|
||||||
|
ResetAutoPowerCount();
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleDevName(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[4 + sizeof(BLE_DEVICE_NAME)]; /* head4 + name + tail1 */
|
||||||
|
(void)data;
|
||||||
|
resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x01; resp[3] = 0x02;
|
||||||
|
memcpy(&resp[4], BLE_DEVICE_NAME, sizeof(BLE_DEVICE_NAME) - 1);
|
||||||
|
resp[sizeof(resp) - 1] = 0xF7;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleFwMainVer(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[9] = {0xF0, 0x60, 0x01, 0x03, Version[0], Version[1], Version[2], 0x01, 0xF7};
|
||||||
|
(void)data;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleSoundVer(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[9] = {0xF0, 0x60, 0x01, 0x04, SOUND_VER[0], SOUND_VER[1], SOUND_VER[2], SOUND_VER[3], 0xF7};
|
||||||
|
(void)data;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleUIVer(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[9] = {0xF0, 0x60, 0x01, 0x05, UI_VER[0], UI_VER[1], UI_VER[2], UI_VER[3], 0xF7};
|
||||||
|
(void)data;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleOtherInfo(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[13] = {0xF0, 0x60, 0x01, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0xF7};
|
||||||
|
(void)data;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleDevCode(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[17] = {0xF0, 0x60, 0x01, 0x07};
|
||||||
|
(void)data;
|
||||||
|
memcpy(&resp[4], (const void *)AT32_UID_BASE, 12);
|
||||||
|
resp[16] = 0xF7;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleAutoPowerOff(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[6] = {0xF0, 0x60, 0x01, 0x0A, 0x00, 0xF7};
|
||||||
|
(void)data;
|
||||||
|
resp[4] = AutoCloseTime;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleUserFwVer(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[9] = {0xF0, 0x60, 0x01, 0x0C, USER_FW_VER[0], USER_FW_VER[1], USER_FW_VER[2], USER_FW_VER[3], 0xF7};
|
||||||
|
(void)data;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* F0 60 01 11 <minutes> F7 : set auto power-off, 0 = disable */
|
||||||
|
static void handleAutoPowerOffSet(uint8_t *data)
|
||||||
|
{
|
||||||
|
NvmParam_Type *nvm = drv_nvm_param_ptr();
|
||||||
|
uint8_t resp[6] = {0xF0, 0x60, 0x01, 0x11, 0x00, 0xF7};
|
||||||
|
AutoCloseTime = data[4];
|
||||||
|
if (nvm->param.AutoCloseTime != AutoCloseTime)
|
||||||
|
{
|
||||||
|
nvm->param.AutoCloseTime = AutoCloseTime;
|
||||||
|
drv_nvm_save_to_flash();
|
||||||
|
}
|
||||||
|
ResetAutoPowerCount();
|
||||||
|
resp[4] = AutoCloseTime;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleFwCode(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[16];
|
||||||
|
uint8_t len = (uint8_t)strlen(fwname); /* "BRS08L" */
|
||||||
|
(void)data;
|
||||||
|
resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x01; resp[3] = 0xFF;
|
||||||
|
memcpy(&resp[4], fwname, len);
|
||||||
|
resp[4 + len] = 0xF7;
|
||||||
|
USART4_SendData(resp, (uint16_t)(4 + len + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------- 2. chord map 0x02 --------*/
|
||||||
|
/* F0 60 02 04 <21B pitch> <21B chord> F7 : write whole map; reply in 02 01 format */
|
||||||
|
static void handleResetChordMap(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t key;
|
||||||
|
for (key = 1; key <= 21; key++)
|
||||||
|
{
|
||||||
|
BT_Pitch_offset_map(key, data[3 + key]); /* data[4..24] pitch offsets */
|
||||||
|
BT_Chord_offset_map(key, data[24 + key]); /* data[25..45] chord types */
|
||||||
|
}
|
||||||
|
handleReadRhythmMap(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------- 3. rhythm style 0x03 0x04 --------*/
|
||||||
|
static void handleRhythmStyle(uint8_t *data)
|
||||||
|
{
|
||||||
|
switch (data[4])
|
||||||
|
{
|
||||||
|
case 0: /* read current style: F0 60 03 04 00 <src> <idHi> <idLo> F7 */
|
||||||
|
{
|
||||||
|
uint8_t resp[9] = {0xF0, 0x60, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0xF7};
|
||||||
|
uint8_t src = (uint8_t)(mGuiData[GUI_AUTOBAND_SW].Current ? 1 : 0);
|
||||||
|
uint16_t id = src ? ParamGuiData[EXPRESS_MODE_PARAM].Current
|
||||||
|
: ParamGuiData[SONG_MODE_PARAM].Current;
|
||||||
|
resp[5] = src;
|
||||||
|
resp[6] = (uint8_t)(id / 128);
|
||||||
|
resp[7] = (uint8_t)(id % 128);
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: /* set style: F0 60 03 04 01 <src:0 sys/1 user> <idHi> <idLo> F7, reply BPM */
|
||||||
|
{
|
||||||
|
uint8_t src = (data[5] != 0) ? 1 : 0;
|
||||||
|
uint16_t id = (uint16_t)data[6] * 128 + data[7];
|
||||||
|
GUI_SWITCH *slot = src ? &ParamGuiData[EXPRESS_MODE_PARAM]
|
||||||
|
: &ParamGuiData[SONG_MODE_PARAM];
|
||||||
|
uint8_t resp[7] = {0xF0, 0x60, 0x03, 0x04, 0x01, 0x00, 0xF7};
|
||||||
|
if (src)
|
||||||
|
slot->Max = (LOCAL_SONG_COUNT > 0) ? (uint16_t)(LOCAL_SONG_COUNT - 1) : 0;
|
||||||
|
if (id > slot->Max)
|
||||||
|
id = slot->Max;
|
||||||
|
mGuiData[GUI_AUTOBAND_SW].Current = src;
|
||||||
|
slot->Current = id;
|
||||||
|
StartFlag = 0;
|
||||||
|
AutoBandTop1_Stop();
|
||||||
|
UI_ReloadTonePreset();
|
||||||
|
resp[5] = (uint8_t)(mGuiData[GUI_SPEED].Current & 0x7F); /* doc: single-byte bpm */
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: /* user style list page: reply total count */
|
||||||
|
case 3: /* user style total */
|
||||||
|
{
|
||||||
|
uint8_t resp[8] = {0xF0, 0x60, 0x03, 0x04, 0x00, 0x00, 0x00, 0xF7};
|
||||||
|
resp[4] = data[4];
|
||||||
|
resp[5] = (uint8_t)(LOCAL_SONG_COUNT / 128);
|
||||||
|
resp[6] = (uint8_t)(LOCAL_SONG_COUNT % 128);
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------- 5. reset / power off 0x05 --------*/
|
||||||
|
/* F0 60 05 00 F7 : ack then soft power off (executed in main-loop context) */
|
||||||
|
static void handleDeviceReset(uint8_t *data)
|
||||||
|
{
|
||||||
|
uint8_t resp[6] = {0xF0, 0x60, 0x05, 0x00, 0x01, 0xF7};
|
||||||
|
(void)data;
|
||||||
|
USART4_SendData(resp, sizeof(resp));
|
||||||
|
System_RequestPowerOff();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,9 @@ void TaskBTHandleThread_entry(void* parameter)
|
||||||
case MSG_ID_LIGHT_LED : BL_Set_led(xEvent.ID,xEvent.HiByte); break;
|
case MSG_ID_LIGHT_LED : BL_Set_led(xEvent.ID,xEvent.HiByte); break;
|
||||||
case MSG_ID_PITCH_OFFSET : BT_Pitch_offset_map(xEvent.ID,xEvent.HiByte); break;
|
case MSG_ID_PITCH_OFFSET : BT_Pitch_offset_map(xEvent.ID,xEvent.HiByte); break;
|
||||||
case MSG_ID_CHORD_OFFSET : BT_Chord_offset_map(xEvent.ID,xEvent.HiByte); break;
|
case MSG_ID_CHORD_OFFSET : BT_Chord_offset_map(xEvent.ID,xEvent.HiByte); break;
|
||||||
|
/* BLE section / intro-outro-end commands (06 xx, 04 07) */
|
||||||
|
case MSG_ID_ADCIN1KEY : ADC_IN1_KEY_Handle((uint8_t)xEvent.ID, true); break;
|
||||||
|
case MSG_ID_KEY_1617 : TM1617_Handle((uint8_t)xEvent.ID); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,14 @@ def rgba_to_rgb565(im):
|
||||||
|
|
||||||
|
|
||||||
def rgba_to_rgb565_opaque(im):
|
def rgba_to_rgb565_opaque(im):
|
||||||
"""Full-screen boot/charge: keep dark background pixels (no near-black punch-through)."""
|
"""Full-screen boot/charge/flash: keep dark bg; flatten RGBA onto black (not white).
|
||||||
im = im.convert("RGB")
|
|
||||||
|
Design exports like 烧录模式.png are mostly transparent with dark glyphs; a bare
|
||||||
|
convert('RGB') drops alpha and turns transparent into white — wrong on device.
|
||||||
|
"""
|
||||||
|
rgba = im.convert("RGBA")
|
||||||
|
bg = Image.new("RGBA", rgba.size, (0, 0, 0, 255))
|
||||||
|
im = Image.alpha_composite(bg, rgba).convert("RGB")
|
||||||
px = im.load()
|
px = im.load()
|
||||||
w, h = im.size
|
w, h = im.size
|
||||||
out = bytearray(w * h * 2)
|
out = bytearray(w * h * 2)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Build K1 release zip: MCU .bin + ExtFlash resources + SoundWalkerIAP + docs.
|
"""Build K1 release zip: Boot + MCU APP + ExtFlash resources + SoundWalkerIAP + docs.
|
||||||
|
|
||||||
External Flash has TWO regions that must both be present on a blank/erased chip:
|
External Flash has TWO regions that must both be present on a blank/erased chip:
|
||||||
0x00000000 tone pack (logo pad + Charg + 1/2/3.bin) -> *.tone.res / combined
|
0x00000000 tone pack (logo pad + Charg + 1/2/3.bin) -> *.tone.res / combined
|
||||||
0x00100000 UI0902 bitmaps (mode rows, boot logo, ...) -> *.ui0902.res / combined
|
0x00100000 UI0902 bitmaps (mode rows, boot logo, ...) -> *.ui0902.res / combined
|
||||||
|
|
||||||
Prefer flashing the combined *.extflash.res @ 0x0 for colleague upgrades.
|
Prefer flashing the combined *.extflash.res @ 0x0 for colleague upgrades.
|
||||||
|
Boot must also be updated so USB IAP wait screen can show UI0902_FLASH_MODE.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -19,15 +20,26 @@ from pathlib import Path
|
||||||
REPO = Path(r"C:\Users\qjyu\Documents\SoundWalker\一诺国际吉他")
|
REPO = Path(r"C:\Users\qjyu\Documents\SoundWalker\一诺国际吉他")
|
||||||
PROJ = REPO / "Code" / "YNGJ-GT1-M - AT32F403ARCT7"
|
PROJ = REPO / "Code" / "YNGJ-GT1-M - AT32F403ARCT7"
|
||||||
EXE_BIN = PROJ / "project" / "IAR_V7.4" / "YNGJ-GT1-M" / "Exe" / "YNGJ-GT1-M.bin"
|
EXE_BIN = PROJ / "project" / "IAR_V7.4" / "YNGJ-GT1-M" / "Exe" / "YNGJ-GT1-M.bin"
|
||||||
|
BOOT_BIN = (
|
||||||
|
PROJ
|
||||||
|
/ "AT32F403ARCT7_BOOT"
|
||||||
|
/ "project"
|
||||||
|
/ "IAR_V7.4"
|
||||||
|
/ "AT32F403ARCT7_BOOT"
|
||||||
|
/ "Exe"
|
||||||
|
/ "AT32F403ARCT7_BOOT.bin"
|
||||||
|
)
|
||||||
TONE_BIN = PROJ / "tools" / "out" / "extflash_tone_0903.bin"
|
TONE_BIN = PROJ / "tools" / "out" / "extflash_tone_0903.bin"
|
||||||
UI0902_BIN = PROJ / "tools" / "out" / "ui0902_res.bin"
|
UI0902_BIN = PROJ / "tools" / "out" / "ui0902_res.bin"
|
||||||
MAP_TXT = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
|
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.3"
|
FW_VER = "0.2.4"
|
||||||
TONE_RES_VER = "0903"
|
TONE_RES_VER = "0903"
|
||||||
UI0902_RES_BASE = 0x00100000
|
UI0902_RES_BASE = 0x00100000
|
||||||
|
BOOT_FLASH_ADDR = 0x08000000
|
||||||
|
APP_FLASH_ADDR = 0x08008000
|
||||||
|
|
||||||
|
|
||||||
def git_info(cwd: Path) -> tuple[str, str]:
|
def git_info(cwd: Path) -> tuple[str, str]:
|
||||||
|
|
@ -58,6 +70,8 @@ def build_combined_extflash(tone: bytes, ui: bytes) -> bytes:
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
if not BOOT_BIN.is_file():
|
||||||
|
raise SystemExit(f"missing Boot bin: {BOOT_BIN}")
|
||||||
if not EXE_BIN.is_file():
|
if not EXE_BIN.is_file():
|
||||||
raise SystemExit(f"missing MCU bin: {EXE_BIN}")
|
raise SystemExit(f"missing MCU bin: {EXE_BIN}")
|
||||||
if not TONE_BIN.is_file():
|
if not TONE_BIN.is_file():
|
||||||
|
|
@ -86,12 +100,15 @@ def main() -> None:
|
||||||
ui_data = UI0902_BIN.read_bytes()
|
ui_data = UI0902_BIN.read_bytes()
|
||||||
combined = build_combined_extflash(tone_data, ui_data)
|
combined = build_combined_extflash(tone_data, ui_data)
|
||||||
|
|
||||||
|
boot_name = f"AT32F403ARCT7_BOOT_v{FW_VER}_{stamp}.bin"
|
||||||
mcu_name = f"YNGJ-GT1-M_MCU_v{FW_VER}_{stamp}.bin"
|
mcu_name = f"YNGJ-GT1-M_MCU_v{FW_VER}_{stamp}.bin"
|
||||||
comb_name = f"extflash_ALL_tone0903_ui0902_{stamp}.res"
|
comb_name = f"extflash_ALL_tone0903_ui0902_{stamp}.res"
|
||||||
|
|
||||||
|
boot_dst = pkg / boot_name
|
||||||
mcu_dst = pkg / mcu_name
|
mcu_dst = pkg / mcu_name
|
||||||
comb_dst = pkg / comb_name
|
comb_dst = pkg / comb_name
|
||||||
|
|
||||||
|
shutil.copy2(BOOT_BIN, boot_dst)
|
||||||
shutil.copy2(EXE_BIN, mcu_dst)
|
shutil.copy2(EXE_BIN, mcu_dst)
|
||||||
comb_dst.write_bytes(combined)
|
comb_dst.write_bytes(combined)
|
||||||
shutil.copy2(iap, pkg / "SoundWalkerIAP.exe")
|
shutil.copy2(iap, pkg / "SoundWalkerIAP.exe")
|
||||||
|
|
@ -99,6 +116,7 @@ def main() -> None:
|
||||||
if MAP_TXT.is_file():
|
if MAP_TXT.is_file():
|
||||||
shutil.copy2(MAP_TXT, pkg / "FLASH_MAP.txt")
|
shutil.copy2(MAP_TXT, pkg / "FLASH_MAP.txt")
|
||||||
|
|
||||||
|
shutil.copy2(boot_dst, OUT_ROOT / boot_name)
|
||||||
shutil.copy2(mcu_dst, OUT_ROOT / mcu_name)
|
shutil.copy2(mcu_dst, OUT_ROOT / mcu_name)
|
||||||
shutil.copy2(comb_dst, OUT_ROOT / "extflash_ALL_tone0903_ui0902.res")
|
shutil.copy2(comb_dst, OUT_ROOT / "extflash_ALL_tone0903_ui0902.res")
|
||||||
# Keep build intermediates under tools/out for local rebuilds; not shipped in package.
|
# Keep build intermediates under tools/out for local rebuilds; not shipped in package.
|
||||||
|
|
@ -115,12 +133,15 @@ def main() -> None:
|
||||||
f"Git subject {subject}",
|
f"Git subject {subject}",
|
||||||
f"FW version {FW_VER}",
|
f"FW version {FW_VER}",
|
||||||
"",
|
"",
|
||||||
"==== 整机升级请刷这两项 ====",
|
"==== 整机升级请刷这三项 ====",
|
||||||
f" 1) {mcu_name} -> MCU APP",
|
f" 1) {boot_name} -> Bootloader @ 0x{BOOT_FLASH_ADDR:08X}",
|
||||||
f" 2) {comb_name} -> 外部 Flash 从 0x0 起整包",
|
f" 2) {mcu_name} -> MCU APP @ 0x{APP_FLASH_ADDR:08X}",
|
||||||
|
f" 3) {comb_name} -> 外部 Flash 从 0x0 起整包",
|
||||||
" (= 音色区 + 填充 + UI0902 图片区)",
|
" (= 音色区 + 填充 + UI0902 图片区)",
|
||||||
"",
|
"",
|
||||||
"建议步骤:",
|
"建议步骤:",
|
||||||
|
" - 先刷 Boot(否则 USB 烧录等待画面仍是旧红字「升级模式」)",
|
||||||
|
" - 再刷 APP",
|
||||||
" - 先整片擦除外部 Flash,再刷上述 ALL .res @ 0x00000000",
|
" - 先整片擦除外部 Flash,再刷上述 ALL .res @ 0x00000000",
|
||||||
" - 勿只刷音色区,否则模式选择页会花屏(UI 图在 0x100000)",
|
" - 勿只刷音色区,否则模式选择页会花屏(UI 图在 0x100000)",
|
||||||
"",
|
"",
|
||||||
|
|
@ -128,13 +149,18 @@ def main() -> None:
|
||||||
" 0x00000000 音色/充电图 (toneRes)",
|
" 0x00000000 音色/充电图 (toneRes)",
|
||||||
" logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin(HKTK/2s) + 3.bin",
|
" logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin(HKTK/2s) + 3.bin",
|
||||||
" 0x00100000 UI0902 图片 (模式选择、开机 Logo、充电、烧录模式、设置/调音台等)",
|
" 0x00100000 UI0902 图片 (模式选择、开机 Logo、充电、烧录模式、设置/调音台等)",
|
||||||
|
" 0x001D2000 UI0902_FLASH_MODE 烧录模式全屏图(Boot 读取)",
|
||||||
"",
|
"",
|
||||||
|
f" {boot_name} size={boot_dst.stat().st_size} @ 0x{BOOT_FLASH_ADDR:08X}",
|
||||||
|
f" sha256={sha256(boot_dst)}",
|
||||||
|
f" {mcu_name} size={mcu_dst.stat().st_size} @ 0x{APP_FLASH_ADDR:08X}",
|
||||||
|
f" sha256={sha256(mcu_dst)}",
|
||||||
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)}",
|
||||||
"",
|
"",
|
||||||
f"MCU: {mcu_name} size={mcu_dst.stat().st_size} sha256={sha256(mcu_dst)}",
|
|
||||||
"",
|
|
||||||
"说明:",
|
"说明:",
|
||||||
|
" - Boot 进入 USB IAP 时显示 UI0902_FLASH_MODE;资源未烧录时白字黑底兜底。",
|
||||||
|
" - 烧录模式图来源:K1标准界面图 0904/烧录模式.png。",
|
||||||
" - 开机全屏 Logo、关机充电全屏画面在 UI0902,不在音色包里的 logo.bin/Charg.bin 占位。",
|
" - 开机全屏 Logo、关机充电全屏画面在 UI0902,不在音色包里的 logo.bin/Charg.bin 占位。",
|
||||||
" - AutoBand 0x9EB5F 本版未重排。",
|
" - AutoBand 0x9EB5F 本版未重排。",
|
||||||
"",
|
"",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,369 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
test_protocol_app_sim.py — 模拟手机 App 对吉他固件做全协议串口测试
|
||||||
|
|
||||||
|
协议来源: Doc/指令测试.docx (= 指令测试0907.docx)
|
||||||
|
通道: UART4 (BLE 桥), 115200 8N1; 帧格式 F0 60 <cmd1> <cmd2> [data...] F7
|
||||||
|
设备主动上报为 F0 51 ... F7 (测试时跳过, 不计为响应)
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python test_protocol_app_sim.py --port COM5 # 跑全部安全用例
|
||||||
|
python test_protocol_app_sim.py --port COM5 --allow-poweroff# 含 05 00 关机用例
|
||||||
|
python test_protocol_app_sim.py --selftest # 无硬件自检(帧编解码)
|
||||||
|
|
||||||
|
依赖: pyserial (pip install pyserial)
|
||||||
|
退出码: 0 = 全部通过, 1 = 有 FAIL
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
try:
|
||||||
|
import serial
|
||||||
|
except ImportError:
|
||||||
|
serial = None # --selftest 不需要 pyserial
|
||||||
|
|
||||||
|
HEAD, TAIL, APP_ID, DEV_ID = 0xF0, 0xF7, 0x60, 0x51
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- frame codec
|
||||||
|
def build(*payload):
|
||||||
|
"""App -> 设备: F0 60 <payload...> F7"""
|
||||||
|
return bytes([HEAD, APP_ID, *payload, TAIL])
|
||||||
|
|
||||||
|
|
||||||
|
class FrameParser:
|
||||||
|
"""字节流 -> 完整帧; 只收 F0...F7, 其余丢弃"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.buf = bytearray()
|
||||||
|
|
||||||
|
def feed(self, data: bytes):
|
||||||
|
"""返回本轮解析出的完整帧列表"""
|
||||||
|
out = []
|
||||||
|
for b in data:
|
||||||
|
if not self.buf:
|
||||||
|
if b == HEAD:
|
||||||
|
self.buf.append(b)
|
||||||
|
continue
|
||||||
|
self.buf.append(b)
|
||||||
|
if b == TAIL or len(self.buf) >= 64:
|
||||||
|
if b == TAIL:
|
||||||
|
out.append(bytes(self.buf))
|
||||||
|
self.buf.clear()
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def hexs(b):
|
||||||
|
return " ".join(f"{x:02X}" for x in b) if b else "(none)"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- serial wrap
|
||||||
|
class AppSim:
|
||||||
|
def __init__(self, port, baud=115200, timeout=0.15):
|
||||||
|
self.ser = serial.Serial(port, baud, bytesize=8, parity="N",
|
||||||
|
stopbits=1, timeout=timeout)
|
||||||
|
self.parser = FrameParser()
|
||||||
|
self.pending = []
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.ser.close()
|
||||||
|
|
||||||
|
def _pump(self):
|
||||||
|
data = self.ser.read(256)
|
||||||
|
if data:
|
||||||
|
self.pending.extend(self.parser.feed(data))
|
||||||
|
|
||||||
|
def send(self, frame: bytes):
|
||||||
|
self.ser.write(frame)
|
||||||
|
self.ser.flush()
|
||||||
|
|
||||||
|
def read_frame(self, timeout=1.0, want_dev=False):
|
||||||
|
"""读一帧; 默认跳过设备主动上报(0x51), want_dev=True 时只要 0x51"""
|
||||||
|
deadline = time.monotonic() + timeout
|
||||||
|
while True:
|
||||||
|
self._pump()
|
||||||
|
for i, f in enumerate(self.pending):
|
||||||
|
is_dev = len(f) > 1 and f[1] == DEV_ID
|
||||||
|
if is_dev == want_dev:
|
||||||
|
return self.pending.pop(i)
|
||||||
|
self.pending.clear() # 丢掉方向不符的帧
|
||||||
|
if time.monotonic() >= deadline:
|
||||||
|
return None
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
def drain(self, quiet=0.2):
|
||||||
|
while self.read_frame(timeout=quiet) is not None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def query(self, frame: bytes, timeout=1.0):
|
||||||
|
self.drain()
|
||||||
|
self.send(frame)
|
||||||
|
return self.read_frame(timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- test engine
|
||||||
|
class Results:
|
||||||
|
def __init__(self):
|
||||||
|
self.rows = []
|
||||||
|
|
||||||
|
def add(self, name, status, detail=""):
|
||||||
|
self.rows.append((name, status, detail))
|
||||||
|
print(f"[{status:>4}] {name}" + (f" | {detail}" if detail else ""))
|
||||||
|
|
||||||
|
def summary(self):
|
||||||
|
n = {s: sum(1 for r in self.rows if r[1] == s)
|
||||||
|
for s in ("PASS", "FAIL", "SKIP", "SENT")}
|
||||||
|
print("\n===== 汇总 =====")
|
||||||
|
for k in ("PASS", "FAIL", "SKIP", "SENT"):
|
||||||
|
print(f"{k:>5}: {n[k]}")
|
||||||
|
for name, status, detail in self.rows:
|
||||||
|
if status == "FAIL":
|
||||||
|
print(f" FAIL: {name} | {detail}")
|
||||||
|
return 1 if n["FAIL"] else 0
|
||||||
|
|
||||||
|
|
||||||
|
def expect(resp, prefix, length=None):
|
||||||
|
"""校验响应帧头/命令字/长度"""
|
||||||
|
if resp is None:
|
||||||
|
return False, "timeout, no reply"
|
||||||
|
if len(resp) < 5 or resp[0] != HEAD or resp[-1] != TAIL:
|
||||||
|
return False, f"bad frame: {hexs(resp)}"
|
||||||
|
if tuple(resp[1:1 + len(prefix)]) != tuple(prefix):
|
||||||
|
return False, f"prefix mismatch: {hexs(resp)}"
|
||||||
|
if length is not None and len(resp) != length:
|
||||||
|
return False, f"len {len(resp)} != {length}: {hexs(resp)}"
|
||||||
|
return True, hexs(resp)
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests(sim, allow_poweroff=False):
|
||||||
|
R = Results()
|
||||||
|
|
||||||
|
# ---------------- 1. 设备信息 0x01 ----------------
|
||||||
|
r = sim.query(build(0x01, 0x01))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0x01), 6)
|
||||||
|
R.add("01 01 连接设备", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
r = sim.query(build(0x01, 0x02))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0x02))
|
||||||
|
if ok:
|
||||||
|
name = bytes(r[4:-1])
|
||||||
|
ok = all(0x20 <= c < 0x7F for c in name)
|
||||||
|
d += f" name={name!r}"
|
||||||
|
R.add("01 02 设备名", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
for sub, name in ((0x03, "固件主版本"), (0x04, "音源版本"),
|
||||||
|
(0x05, "UI版本"), (0x0C, "用户固件版本")):
|
||||||
|
r = sim.query(build(0x01, sub))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, sub), 9)
|
||||||
|
R.add(f"01 {sub:02X} {name}", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
r = sim.query(build(0x01, 0x06))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0x06), 13)
|
||||||
|
R.add("01 06 其他信息", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
r = sim.query(build(0x01, 0x07))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0x07), 17)
|
||||||
|
R.add("01 07 设备编码(UID)", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
r = sim.query(build(0x01, 0xFF))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0xFF))
|
||||||
|
R.add("01 FF 固件编码", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
# 自动关机: 读 -> 设15 -> 读验证 -> 恢复原值
|
||||||
|
r0 = sim.query(build(0x01, 0x0A))
|
||||||
|
ok, d = expect(r0, (0x60, 0x01, 0x0A), 6)
|
||||||
|
R.add("01 0A 自动关机(读)", "PASS" if ok else "FAIL", d)
|
||||||
|
if ok:
|
||||||
|
orig = r0[4]
|
||||||
|
r = sim.query(build(0x01, 0x11, 15))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0x11, 15), 6)
|
||||||
|
R.add("01 11 自动关机(设15)", "PASS" if ok else "FAIL", d)
|
||||||
|
r = sim.query(build(0x01, 0x0A))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0x0A, 15), 6)
|
||||||
|
R.add("01 0A 回读=15", "PASS" if ok else "FAIL", d)
|
||||||
|
sim.query(build(0x01, 0x11, orig)) # restore
|
||||||
|
|
||||||
|
r = sim.query(build(0x01, 0x00))
|
||||||
|
ok, d = expect(r, (0x60, 0x01, 0x00), 6)
|
||||||
|
R.add("01 00 断开设备", "PASS" if ok else "FAIL", d)
|
||||||
|
sim.query(build(0x01, 0x01)) # 重新连接, 便于后续用例
|
||||||
|
|
||||||
|
# ---------------- 2. 和弦映射 0x02 ----------------
|
||||||
|
r = sim.query(build(0x02, 0x01))
|
||||||
|
ok, d = expect(r, (0x60, 0x02, 0x01), 47)
|
||||||
|
R.add("02 01 读和弦映射表", "PASS" if ok else "FAIL", d)
|
||||||
|
cur_map = bytes(r[4:-1]) if ok else None # 21B pitch + 21B chord
|
||||||
|
|
||||||
|
if cur_map:
|
||||||
|
key = 1
|
||||||
|
orig_pitch, orig_chord = cur_map[key - 1], cur_map[21 + key - 1]
|
||||||
|
|
||||||
|
sim.send(build(0x02, 0x02, key - 1, 0x02)) # pitch: 升位
|
||||||
|
time.sleep(0.1)
|
||||||
|
r = sim.query(build(0x02, 0x01))
|
||||||
|
ok = r is not None and len(r) == 47 and r[3] == 0x01 and r[4 + key - 1] == 0x02
|
||||||
|
R.add("02 02 Pitch偏移(写+读回)", "PASS" if ok else "FAIL", hexs(r))
|
||||||
|
sim.send(build(0x02, 0x02, key - 1, orig_pitch)) # restore
|
||||||
|
|
||||||
|
sim.send(build(0x02, 0x03, key - 1, 0x08)) # chord: 小三
|
||||||
|
time.sleep(0.1)
|
||||||
|
r = sim.query(build(0x02, 0x01))
|
||||||
|
ok = r is not None and len(r) == 47 and r[4 + 21 + key - 1] == 0x08
|
||||||
|
R.add("02 03 Chord偏移(写+读回)", "PASS" if ok else "FAIL", hexs(r))
|
||||||
|
sim.send(build(0x02, 0x03, key - 1, orig_chord)) # restore
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# 02 04 整表写入(回写当前表) -> 设备应回 02 01 格式整表
|
||||||
|
r = sim.query(build(0x02, 0x04, *cur_map), timeout=2.0)
|
||||||
|
ok, d = expect(r, (0x60, 0x02, 0x01), 47)
|
||||||
|
R.add("02 04 整表写入(回读帧)", "PASS" if ok else "FAIL", d)
|
||||||
|
r2 = sim.query(build(0x02, 0x01))
|
||||||
|
ok = r2 is not None and bytes(r2[4:-1]) == cur_map
|
||||||
|
R.add("02 04 写入后整表一致", "PASS" if ok else "FAIL", hexs(r2))
|
||||||
|
else:
|
||||||
|
for n in ("02 02 Pitch偏移", "02 03 Chord偏移", "02 04 整表写入"):
|
||||||
|
R.add(n, "SKIP", "读映射表失败, 级联跳过")
|
||||||
|
|
||||||
|
# ---------------- 3. 吉他参数 0x03 ----------------
|
||||||
|
r = sim.query(build(0x03, 0x04, 0x00))
|
||||||
|
ok, d = expect(r, (0x60, 0x03, 0x04, 0x00), 9)
|
||||||
|
R.add("03 04 读当前节奏风格", "PASS" if ok else "FAIL", d)
|
||||||
|
saved_style = bytes(r[5:8]) if ok else None
|
||||||
|
|
||||||
|
r = sim.query(build(0x03, 0x04, 0x01, 0x00, 0x00, 0x00))
|
||||||
|
ok, d = expect(r, (0x60, 0x03, 0x04, 0x01), 7)
|
||||||
|
R.add("03 04 设系统风格#0", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
r = sim.query(build(0x03, 0x04, 0x01, 0x01, 0x00, 0x00))
|
||||||
|
ok, d = expect(r, (0x60, 0x03, 0x04, 0x01), 7)
|
||||||
|
R.add("03 04 设用户风格#0", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
r = sim.query(build(0x03, 0x04, 0x02, 0x00, 0x00, 0x00, 0x07))
|
||||||
|
ok, d = expect(r, (0x60, 0x03, 0x04, 0x02), 8)
|
||||||
|
R.add("03 04 用户风格分页", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
r = sim.query(build(0x03, 0x04, 0x03))
|
||||||
|
ok, d = expect(r, (0x60, 0x03, 0x04, 0x03), 8)
|
||||||
|
R.add("03 04 用户风格总数", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
|
if saved_style:
|
||||||
|
sim.query(build(0x03, 0x04, 0x01, *saved_style)) # restore style
|
||||||
|
|
||||||
|
# 弦音色: 读 -> 写 -> 读 -> 恢复
|
||||||
|
r0 = sim.query(build(0x03, 0x05, 0x00))
|
||||||
|
ok, d = expect(r0, (0x60, 0x03, 0x05, 0x00), 8)
|
||||||
|
R.add("03 05 读弦音色", "PASS" if ok else "FAIL", d)
|
||||||
|
if ok:
|
||||||
|
orig = r0[6]
|
||||||
|
new = (orig + 1) % 5
|
||||||
|
sim.send(build(0x03, 0x05, 0x01, 0x00, new))
|
||||||
|
time.sleep(0.1)
|
||||||
|
r = sim.query(build(0x03, 0x05, 0x00))
|
||||||
|
ok = r is not None and len(r) == 8 and r[6] == new
|
||||||
|
R.add("03 05 写弦音色(写+读回)", "PASS" if ok else "FAIL", hexs(r))
|
||||||
|
sim.send(build(0x03, 0x05, 0x01, 0x00, orig)) # restore
|
||||||
|
|
||||||
|
# BPM: 读 -> 写120 -> 读 -> 恢复
|
||||||
|
r0 = sim.query(build(0x03, 0x06, 0x00))
|
||||||
|
ok, d = expect(r0, (0x60, 0x03, 0x06, 0x00), 8)
|
||||||
|
R.add("03 06 读BPM", "PASS" if ok else "FAIL", d)
|
||||||
|
if ok:
|
||||||
|
orig = r0[5] * 128 + r0[6]
|
||||||
|
sim.send(build(0x03, 0x06, 0x01, 120 // 128, 120 % 128))
|
||||||
|
time.sleep(0.1)
|
||||||
|
r = sim.query(build(0x03, 0x06, 0x00))
|
||||||
|
ok = r is not None and len(r) == 8 and (r[5] * 128 + r[6]) == 120
|
||||||
|
R.add("03 06 写BPM=120(写+读回)", "PASS" if ok else "FAIL", hexs(r))
|
||||||
|
sim.send(build(0x03, 0x06, 0x01, orig // 128, orig % 128)) # restore
|
||||||
|
|
||||||
|
# 移调: 读 -> 写 -> 读 -> 恢复
|
||||||
|
r0 = sim.query(build(0x03, 0x07, 0x00))
|
||||||
|
ok, d = expect(r0, (0x60, 0x03, 0x07, 0x00), 8)
|
||||||
|
R.add("03 07 读移调", "PASS" if ok else "FAIL", d)
|
||||||
|
if ok:
|
||||||
|
orig = r0[5]
|
||||||
|
new = (orig + 1) % 12
|
||||||
|
sim.send(build(0x03, 0x07, 0x01, new))
|
||||||
|
time.sleep(0.1)
|
||||||
|
r = sim.query(build(0x03, 0x07, 0x00))
|
||||||
|
ok = r is not None and len(r) == 8 and r[5] == new
|
||||||
|
R.add("03 07 写移调(写+读回)", "PASS" if ok else "FAIL", hexs(r))
|
||||||
|
sim.send(build(0x03, 0x07, 0x01, orig)) # restore
|
||||||
|
|
||||||
|
# ---------------- 4. LED / 播放控制 0x04 (无应答, 仅下发) ----------------
|
||||||
|
for i in range(7):
|
||||||
|
sim.drain()
|
||||||
|
sim.send(build(0x04, i, 0x02, 0x02))
|
||||||
|
r = sim.read_frame(timeout=0.3)
|
||||||
|
R.add(f"04 0{i} 点亮LED{i + 1}", "SENT" if r is None else "PASS",
|
||||||
|
"" if r is None else f"unexpected reply {hexs(r)}")
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
sim.drain()
|
||||||
|
sim.send(build(0x04, 0x07, 0x00, 0x00))
|
||||||
|
R.add("04 07 End/结束播放", "SENT", "无应答属正常")
|
||||||
|
|
||||||
|
# ---------------- 6. 段落跳转 0x06 (无应答, 仅下发) ----------------
|
||||||
|
for sub, name in ((0x01, "前奏"), (0x02, "间奏"), (0x03, "尾奏"),
|
||||||
|
(0x05, "A段"), (0x06, "B段"), (0x07, "C段"), (0x08, "D段")):
|
||||||
|
sim.drain()
|
||||||
|
sim.send(build(0x06, sub, 0x00))
|
||||||
|
R.add(f"06 {sub:02X} {name}", "SENT", "无应答属正常")
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
# ---------------- 5. 复位/关机 0x05 ----------------
|
||||||
|
if allow_poweroff:
|
||||||
|
r = sim.query(build(0x05, 0x00), timeout=2.0)
|
||||||
|
ok, d = expect(r, (0x60, 0x05, 0x00), 6)
|
||||||
|
R.add("05 00 复位/关机", "PASS" if ok else "FAIL", d + " (设备将软关机)")
|
||||||
|
else:
|
||||||
|
R.add("05 00 复位/关机", "SKIP", "需 --allow-poweroff")
|
||||||
|
|
||||||
|
return R.summary()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- selftest
|
||||||
|
def selftest():
|
||||||
|
R = Results()
|
||||||
|
f = build(0x03, 0x06, 0x01, 0x00, 0x40)
|
||||||
|
R.add("build frame", "PASS" if f == bytes.fromhex("F060030601004 0F7".replace(" ", "")) else "FAIL", hexs(f))
|
||||||
|
|
||||||
|
p = FrameParser()
|
||||||
|
out = p.feed(bytes.fromhex("AA F0 60 01 01")) + p.feed(bytes.fromhex("01 F7"))
|
||||||
|
R.add("parser chunked", "PASS" if out == [bytes.fromhex("F0600101 01F7".replace(" ", ""))] else "FAIL",
|
||||||
|
str([hexs(x) for x in out]))
|
||||||
|
|
||||||
|
p = FrameParser()
|
||||||
|
out = p.feed(bytes.fromhex("F0 51 05 0A F7 F0 60 01 01 01 F7".replace(" ", "")))
|
||||||
|
R.add("parser two frames", "PASS" if len(out) == 2 else "FAIL", str(len(out)))
|
||||||
|
return R.summary()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(description="模拟手机App的吉他协议全量测试")
|
||||||
|
ap.add_argument("--port", help="串口, 如 COM5 (UART4 115200 8N1)")
|
||||||
|
ap.add_argument("--baud", type=int, default=115200)
|
||||||
|
ap.add_argument("--allow-poweroff", action="store_true",
|
||||||
|
help="允许执行 05 00 关机用例")
|
||||||
|
ap.add_argument("--selftest", action="store_true", help="无硬件自检")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if args.selftest:
|
||||||
|
sys.exit(selftest())
|
||||||
|
if not args.port:
|
||||||
|
ap.error("需要 --port (或用 --selftest)")
|
||||||
|
if serial is None:
|
||||||
|
sys.exit("缺少 pyserial: pip install pyserial")
|
||||||
|
|
||||||
|
sim = AppSim(args.port, args.baud)
|
||||||
|
print(f"=== 协议测试开始 {args.port}@{args.baud} ===")
|
||||||
|
try:
|
||||||
|
code = run_tests(sim, allow_poweroff=args.allow_poweroff)
|
||||||
|
finally:
|
||||||
|
sim.close()
|
||||||
|
sys.exit(code)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue