#include "includes.h" #include "git_user_fw_ver.h" // Э��֡ͷ/֡β #define FRAME_SYS_HEAD 0xF0 #define FRAME_SYS_TAIL 0xF7 #define FRAME_MIDI_NOTEON 0x90 #define FRAME_MIDI_NOTEOFF 0x80 #define FRAME_MIDI_PROCHANGE 0xC0 #define UART4_RCV_TIMEOUT_MS 50 /** * @brief UART4����״̬ö�� */ typedef enum { UART4_RCV_BUFF_IDLE, // ����״̬ UART4_RCV_BUFF_HEAD, // ����֡ͷ��̶��ֽ�(0x60/0x51) UART4_RCV_BUFF_CMD1, // ��ָ�� UART4_RCV_BUFF_CMD2, // ��ָ�� UART4_RCV_BUFF_DATA, // ������ UART4_RCV_BUFF_END, // ֡β���� UART4_RCV_BUFF_MIDI1, UART4_RCV_BUFF_MIDI2, UART4_RCV_BUFF_MIDIEND } UART4_RCV_StatusType; #define UART4_PROCESS_BUFF_SIZE 64 /* 02 04 full chord-map frame is 47 bytes */ // UART4����ȫ�ֱ��� static UART4_RCV_StatusType UART4_RCV_Status = UART4_RCV_BUFF_IDLE; static uint8_t UART4_Process_Buff[UART4_PROCESS_BUFF_SIZE]; static uint8_t UART4_RCV_cnt = 0; //static uint32_t UART4_RCV_Last_Tick = 0; //static uint8_t expect_data_len = 0; /** * @brief ָ����ṹ�壨��ȫ����UART3��� */ typedef struct { uint8_t cmd[2]; // ��ָ��+��ָ�� void (*handler)(uint8_t *data);// ��Ӧ�������� } BLE_SysExCmdItem; // ============================== // ָ�������������ȫ�� static�� // ============================== static void handleDevDisconnect(uint8_t *data); static void handleDevConnect(uint8_t *data); static void handleDevName(uint8_t *data); static void handleFwMainVer(uint8_t *data); static void handleSoundVer(uint8_t *data); static void handleUIVer(uint8_t *data); static void handleOtherInfo(uint8_t *data); static void handleDevCode(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 handlePitchOffset(uint8_t *data); static void handleChordOffset(uint8_t *data); static void handleResetChordMap(uint8_t *data); static void handleRhythmStyle(uint8_t *data); static void handleStringTimbre(uint8_t *data); static void handleBPM(uint8_t *data); static void handleTranspose(uint8_t *data); static void handleLED0(uint8_t *data); static void handleLED1(uint8_t *data); static void handleLED2(uint8_t *data); static void handleLED3(uint8_t *data); static void handleLED4(uint8_t *data); static void handleLED5(uint8_t *data); static void handleLED6(uint8_t *data); static void handleDeviceReset(uint8_t *data); static void handleIntro(uint8_t *data); static void handleInterlude(uint8_t *data); static void handleOutro(uint8_t *data); static void handleEnd(uint8_t *data); static void handleSectionA(uint8_t *data); static void handleSectionB(uint8_t *data); static void handleSectionC(uint8_t *data); static void handleSectionD(uint8_t *data); /************************************************* * ����ָ������ϸ����Э���ĵ��� *************************************************/ static const BLE_SysExCmdItem bleSysExCmdTable[] = { /*======== 1. device info 0x01 ========*/ {{0x01, 0x00}, handleDevDisconnect}, /* disconnect */ {{0x01, 0x01}, handleDevConnect}, /* connect */ {{0x01, 0x02}, handleDevName}, /* device name */ {{0x01, 0x03}, handleFwMainVer}, /* fw main version */ {{0x01, 0x04}, handleSoundVer}, /* sound version */ {{0x01, 0x05}, handleUIVer}, /* UI version */ {{0x01, 0x06}, handleOtherInfo}, /* other info */ {{0x01, 0x07}, handleDevCode}, /* device code (96bit UID) */ {{0x01, 0x0A}, handleAutoPowerOff}, /* auto power-off read (minutes) */ {{0x01, 0x0C}, handleUserFwVer}, /* user fw version */ {{0x01, 0x11}, handleAutoPowerOffSet}, /* auto power-off set (minutes) */ {{0x01, 0x0F}, handleFwCode}, /* fw code (MIDI/BLE-safe; replaces 01 FF) */ {{0x01, 0xFF}, handleFwCode}, /* fw code alias (raw UART only; 0xFF illegal in BLE-MIDI SysEx) */ /*======== 2. chord map 0x02 ========*/ {{0x02, 0x01}, handleReadRhythmMap}, /* read chord/pitch map */ {{0x02, 0x02}, handlePitchOffset}, /* pitch offset */ {{0x02, 0x03}, handleChordOffset}, /* chord offset */ {{0x02, 0x04}, handleResetChordMap}, /* write whole map (default reset) */ /*======== 3. guitar params 0x03 ========*/ {{0x03, 0x04}, handleRhythmStyle}, /* rhythm style r/w + user list */ {{0x03, 0x05}, handleStringTimbre}, /* string timbre r/w */ {{0x03, 0x06}, handleBPM}, /* BPM r/w */ {{0x03, 0x07}, handleTranspose}, /* transpose r/w */ /*======== 4. play / LED 0x04 ========*/ {{0x04, 0x00}, handleLED0}, /* LED 1 */ {{0x04, 0x01}, handleLED1}, /* LED 2 */ {{0x04, 0x02}, handleLED2}, /* LED 3 */ {{0x04, 0x03}, handleLED3}, /* LED 4 */ {{0x04, 0x04}, handleLED4}, /* LED 5 */ {{0x04, 0x05}, handleLED5}, /* LED 6 */ {{0x04, 0x06}, handleLED6}, /* LED 7 */ {{0x04, 0x07}, handleEnd}, /* end / stop play */ /*======== 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 */ }; // ָ������ #define BLE_SYS_EX_CMD_COUNT (sizeof(bleSysExCmdTable) / sizeof(BLE_SysExCmdItem)) /** * @brief ����SysExָ��ַ����� * @param data: ����֡���� * @param cnt: ֡���� */ void processBLESysEXData(uint8_t* data, uint8_t cnt) { // ֡У�� if (data[0] != FRAME_SYS_HEAD || data[cnt - 1] != FRAME_SYS_TAIL || cnt > UART4_PROCESS_BUFF_SIZE || data[1] != 0x60) { return; } /* App/BLE 活动视为用户操作,避免测协议时静置触发自动关机 */ ResetAutoPowerCount(); LOG_I("BLE", "sysex ok len=%u cmd=%02X %02X", (unsigned)cnt, data[2], data[3]); // ȡ��ָ�� + ��ָ�� uint8_t cmd[2] = {data[2], data[3]}; // ����ָ���ƥ�� for (uint8_t i = 0; i < BLE_SYS_EX_CMD_COUNT; i++) { if (memcmp(cmd, bleSysExCmdTable[i].cmd, 2) == 0) { if (bleSysExCmdTable[i].handler != NULL) { bleSysExCmdTable[i].handler(data); } return; } } } void UART4_Data_Process(volatile uint8_t* data) { uint8_t rcv = *data; uint32_t now = rt_tick_get(); static uint32_t last_tick; if(now - last_tick >= UART4_RCV_TIMEOUT_MS) { last_tick = now; memset(UART4_Process_Buff, 0, sizeof(UART4_Process_Buff)); UART4_RCV_cnt = 0; UART4_RCV_Status = UART4_RCV_BUFF_IDLE; } switch (UART4_RCV_Status) { case UART4_RCV_BUFF_IDLE: memset(UART4_Process_Buff, 0, sizeof(UART4_Process_Buff)); UART4_RCV_cnt = 0; switch(rcv & 0xF0) { case FRAME_SYS_HEAD: UART4_Process_Buff[UART4_RCV_cnt++] = rcv; UART4_RCV_Status = UART4_RCV_BUFF_HEAD; break; case FRAME_MIDI_NOTEON: case FRAME_MIDI_NOTEOFF: case FRAME_MIDI_PROCHANGE: UART4_Process_Buff[UART4_RCV_cnt++] = rcv; UART4_RCV_Status = UART4_RCV_BUFF_MIDI1; break; default:UART4_RCV_Status = UART4_RCV_BUFF_IDLE; return; break; } //UART4_Process_Buff[UART4_RCV_cnt++] = rcv; break; case UART4_RCV_BUFF_MIDI1: if((UART4_Process_Buff[0] & 0xF0) == FRAME_MIDI_PROCHANGE) { USART2_SendData(UART4_Process_Buff,UART4_RCV_cnt); UART4_RCV_Status = UART4_RCV_BUFF_IDLE; } else { UART4_Process_Buff[UART4_RCV_cnt++] = rcv; UART4_RCV_Status = UART4_RCV_BUFF_MIDI2; } break; case UART4_RCV_BUFF_MIDI2: UART4_Process_Buff[UART4_RCV_cnt++] = rcv; USART2_SendData(UART4_Process_Buff,UART4_RCV_cnt); UART4_RCV_Status = UART4_RCV_BUFF_IDLE; break; case UART4_RCV_BUFF_HEAD: if (UART4_RCV_cnt < UART4_PROCESS_BUFF_SIZE) { UART4_Process_Buff[UART4_RCV_cnt++] = rcv; } if (rcv == 0x60) { UART4_RCV_Status = UART4_RCV_BUFF_CMD1; } else { UART4_RCV_Status = UART4_RCV_BUFF_IDLE; } break; case UART4_RCV_BUFF_CMD1: // ��ָ�� if (UART4_RCV_cnt < UART4_PROCESS_BUFF_SIZE) { UART4_Process_Buff[UART4_RCV_cnt++] = rcv; } UART4_RCV_Status = UART4_RCV_BUFF_CMD2; break; case UART4_RCV_BUFF_CMD2: // ��ָ�� if (UART4_RCV_cnt < UART4_PROCESS_BUFF_SIZE) { UART4_Process_Buff[UART4_RCV_cnt++] = rcv; } UART4_RCV_Status = UART4_RCV_BUFF_DATA; break; case UART4_RCV_BUFF_DATA: // �����м����ݣ�ֱ��֡β 0xF7 if (UART4_RCV_cnt < UART4_PROCESS_BUFF_SIZE) { UART4_Process_Buff[UART4_RCV_cnt++] = rcv; } if (rcv == FRAME_SYS_TAIL) { // ִ��ָ��� processBLESysEXData(UART4_Process_Buff, UART4_RCV_cnt); // ������ɣ���λ״̬�� UART4_RCV_Status = UART4_RCV_BUFF_IDLE; } break; default: // δ֪״̬ǿ�Ƹ�λ UART4_RCV_Status = UART4_RCV_BUFF_IDLE; break; } } static void handleStringTimbre(uint8_t *data) { switch(data[4]) { case 0: { uint8_t ReturnTimbre[8] = {0xF0,0x60,0x03,0x05,0x00,0x00,0x00,0xF7}; ReturnTimbre[6] = mGuiData[GUI_TIMBRE_SELECT].Current; USART4_SendData(ReturnTimbre,sizeof(ReturnTimbre)); } break; case 1: { NvmParam_Type * nvm = drv_nvm_param_ptr(); uint8_t new_val = data[6]; // �õ�����ɫֵ // ���ֵû�䣬ֱ���˳�����ˢ�� if (new_val == mGuiData[GUI_TIMBRE_SELECT].Current) { return; } mGuiData[GUI_TIMBRE_SELECT].Current = new_val; if(mGuiData[GUI_TAB_INDEX].Current != 3) { //Refresh_StringTimbre(last_value, &UI_Label[GUI_TIMBRE_SELECT], &mGuiData[GUI_TIMBRE_SELECT]); } if(nvm->param.Timbre != mGuiData[GUI_TIMBRE_SELECT].Current) { nvm->param.Timbre = mGuiData[GUI_TIMBRE_SELECT].Current; drv_nvm_save_to_flash(); } // uint8_t buff[10] = {0,}; // LCD_ShowString(200,30, Num_To_String(mGuiData[GUI_TIMBRE_SELECT].Current,buff,4), RED, WHITE, 16, 0); } break; } } //extern int8_t delta; extern STRING_MIDI USE_MIDI; static void handleTranspose(uint8_t *data) { //uint8_t last_value; switch(data[4]) { case 0: { /* doc: F0 60 03 07 00 F7 (7 bytes total) */ uint8_t ReturnTranspose[7] = {0xF0,0x60,0x03,0x07,0x00,0x00,0xF7}; ReturnTranspose[5] = (uint8_t)mGuiData[GUI_TRANSPOSE].Current; USART4_SendData(ReturnTranspose,sizeof(ReturnTranspose)); } break; case 1: { NvmParam_Type * nvm = drv_nvm_param_ptr(); uint8_t new_val = data[5]; // �õ�����ɫֵ // ���ֵû�䣬ֱ���˳�����ˢ�� if (new_val == mGuiData[GUI_TRANSPOSE].Current) { return; } mGuiData[GUI_TRANSPOSE].Current = new_val; if(mGuiData[GUI_TAB_INDEX].Current != 3) { Refresh_Transpose(&UI_Label[GUI_TRANSPOSE], &mGuiData[GUI_TRANSPOSE]); } if(nvm->param.Transpose != mGuiData[GUI_TRANSPOSE].Current) { nvm->param.Transpose = mGuiData[GUI_TRANSPOSE].Current; drv_nvm_save_to_flash(); } // delta = new_trans - last_value; // USE_MIDI.Midi_1 += delta; // USE_MIDI.Midi_2 += delta; // USE_MIDI.Midi_3 += delta; // USE_MIDI.Midi_4 += delta; // USE_MIDI.Midi_5 += delta; // USE_MIDI.Midi_6 += delta; //AutoBandTop1_Note_On(USE_MIDI.Midi_6,0x50); } break; } } // �յ�˫�ֽ� hi, lo������ʵ��BPM uint16_t Calc_BPM(uint8_t bpmHi, uint8_t bpmLo) { return (uint16_t)bpmHi * 128 + bpmLo; } // ������������Ŀ��BPM����ֳ�hi��lo˫�ֽ� void Split_BPM(uint16_t target_bpm, uint8_t *hi_out, uint8_t *lo_out) { *hi_out = target_bpm / 128; *lo_out = target_bpm % 128; } static void handleBPM(uint8_t *data) { switch(data[4]) { case 0: { uint8_t ReturnSpeed[8] = {0xF0,0x60,0x03,0x06,0x00,0x00,0x00,0xF7}; ReturnSpeed[5] = mGuiData[GUI_SPEED].Current / 128; ReturnSpeed[6] = mGuiData[GUI_SPEED].Current % 128; USART4_SendData(ReturnSpeed,sizeof(ReturnSpeed)); } break; case 1: { NvmParam_Type * nvm = drv_nvm_param_ptr(); uint16_t new_val = Calc_BPM(data[5],data[6]); // ���ֵû�䣬ֱ���˳�����ˢ�� if (new_val == mGuiData[GUI_SPEED].Current) { return; } mGuiData[GUI_SPEED].Current = new_val; if(mGuiData[GUI_TAB_INDEX].Current != 3) { Refresh_Transpose(&UI_Label[GUI_SPEED], &mGuiData[GUI_SPEED]); } if(nvm->param.BPM != mGuiData[GUI_SPEED].Current) { nvm->param.BPM = mGuiData[GUI_SPEED].Current; drv_nvm_save_to_flash(); } AutoBandTop1_ChangeBPM(mGuiData[GUI_SPEED].Current); } break; } } static void handleLED0(uint8_t *data) { // DefaultTask_SendMsg(MSG_ID_LIGHT_LED, LED_L1, data[5], 0); BL_Sendmsg(MSG_ID_LIGHT_LED, LED_L1, data[5], 0); } static void handleLED1(uint8_t *data) { BL_Sendmsg(MSG_ID_LIGHT_LED, LED_L2, data[5], 0); } static void handleLED2(uint8_t *data) { BL_Sendmsg(MSG_ID_LIGHT_LED, LED_L3, data[5], 0); } static void handleLED3(uint8_t *data) { BL_Sendmsg(MSG_ID_LIGHT_LED, LED_L4, data[5], 0); } static void handleLED4(uint8_t *data) { BL_Sendmsg(MSG_ID_LIGHT_LED, LED_L5, data[5], 0); } static void handleLED5(uint8_t *data) { BL_Sendmsg(MSG_ID_LIGHT_LED, LED_L6, data[5], 0); } static void handleLED6(uint8_t *data) { BL_Sendmsg(MSG_ID_LIGHT_LED, LED_L7, data[5], 0); } static void handlePitchOffset(uint8_t *data) { uint8_t key = (uint8_t)(data[4] + 1u); if (key < 1u || key > 21u) return; BL_Sendmsg(MSG_ID_PITCH_OFFSET, key, data[5], 0); } static void handleChordOffset(uint8_t *data) { uint8_t key = (uint8_t)(data[4] + 1u); if (key < 1u || key > 21u) return; BL_Sendmsg(MSG_ID_CHORD_OFFSET, key, data[5], 0); } extern CHORD_TYPE_INDEX chord_type_index_map[22]; static void handleReadRhythmMap(uint8_t *data) { /* 静态缓冲:禁止在 TaskBTRecv 小栈上再开 47B */ static uint8_t ReadChordMap[47]; uint8_t chord_map = 0; uint8_t i; (void)data; memset(ReadChordMap, 0, sizeof(ReadChordMap)); ReadChordMap[0] = 0xF0; ReadChordMap[1] = 0x60; ReadChordMap[2] = 0x02; ReadChordMap[3] = 0x01; ReadChordMap[46] = 0xF7; for (i = 1; i < 22; i++) { ReadChordMap[3 + i] = chord_type_index_map[i].PitchOffset; } for (i = 25; i < 46; i++) { switch (chord_type_index_map[i - 24].type) { case 0: chord_map = 0; break; case 1: chord_map = 8; break; case 2: chord_map = 2; break; case 3: chord_map = 13; break; case 4: chord_map = 10; break; case 5: chord_map = 31; break; case 6: chord_map = 30; break; case 7: chord_map = 4; break; case 8: chord_map = 11; break; default: chord_map = 0; break; } ReadChordMap[i] = chord_map; } USART4_SendData(ReadChordMap, sizeof(ReadChordMap)); } static void handleIntro(uint8_t *data) { BL_Sendmsg(MSG_ID_ADCIN1KEY, 3, 0, 0); } static void handleInterlude(uint8_t *data) { BL_Sendmsg(MSG_ID_ADCIN1KEY, 0, 0, 0); } static void handleOutro(uint8_t *data) { BL_Sendmsg(MSG_ID_ADCIN1KEY, 1, 0, 0); } static void handleEnd(uint8_t *data) { //DefaultTask_SendMsg(MSG_ID_ADCIN1KEY, 2, 0, 0); // osMutexAcquire(Tm1629Mutex,osWaitForever); // TM1629D_AllLedOff(); // TM1629D_AllLedOn(LED_COLOR_G); // TM1629D_UpdateDisplay(0); // osMutexRelease(Tm1629Mutex); BL_Sendmsg(MSG_ID_ADCIN1KEY, 2, 0, 0); /* End -> Postamble(1), distinct from Outro(key=1) */ } static void handleSectionA(uint8_t *data) { BL_Sendmsg(MSG_ID_KEY_1617, 3, 0, 0); } static void handleSectionB(uint8_t *data) { BL_Sendmsg(MSG_ID_KEY_1617, 2, 0, 0); } static void handleSectionC(uint8_t *data) { BL_Sendmsg(MSG_ID_KEY_1617, 1, 0, 0); } static void handleSectionD(uint8_t *data) { BL_Sendmsg(MSG_ID_KEY_1617, 0, 0, 0); } static void BL_Sendmsg(uint16_t Data1, uint16_t Data2, uint16_t Data3, uint16_t Data4) { BLTask_Sendmsg(Data1, Data2, Data3, Data4); } /* ====================================================================== * Appended protocol handlers (per Doc/指令测试.docx) * ==================================================================== */ /*-------- 1. device info 0x01 --------*/ #define AT32_UID_BASE 0x1FFFF7E8UL /* 96-bit unique device ID */ /* 01 04 音源版本:YY.M.D = 音色资源日期(Dream 无版本查询,发版时手改) * 26.9.8 → 2026-09-08(Doc/音色文件/0908) */ static const uint8_t SOUND_VER[4] = {26, 9, 8, 0x01}; /* 01 05 UI 资源/界面日期:YY.M.D(与 MCU 逻辑版本 Version[] 分开维护) */ static const uint8_t UI_VER[4] = {26, 9, 9, 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) { static char name[BLE_DEVICE_NAME_LEN + 1]; static uint8_t resp[4 + BLE_DEVICE_NAME_LEN + 1]; /* head4 + name + F7 */ (void)data; BleBuildDeviceName(name); resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x01; resp[3] = 0x02; memcpy(&resp[4], name, BLE_DEVICE_NAME_LEN); resp[4 + BLE_DEVICE_NAME_LEN] = 0xF7; USART4_SendData(resp, (uint16_t)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) { /* 01 06: reserved — keep 8 zero bytes until product defines fields */ 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) { /* 96-bit UID as 24 hex ASCII — SysEx data must stay 7-bit for BLE-MIDI */ static const char hex[] = "0123456789ABCDEF"; uint8_t resp[29]; const uint8_t *uid = (const uint8_t *)AT32_UID_BASE; uint8_t i; (void)data; resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x01; resp[3] = 0x07; for (i = 0; i < 12u; i++) { resp[4u + 2u * i] = (uint8_t)hex[uid[i] >> 4]; resp[4u + 2u * i + 1u] = (uint8_t)hex[uid[i] & 0x0Fu]; } resp[28] = 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) { /* 用户固件版本 = "{branch}_{short6}"[+ '*'],如 develop_0aedb4(ASCII,BLE-MIDI 安全) */ uint8_t resp[4u + GIT_BUILD_ID_LEN + 1u]; (void)data; resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x01; resp[3] = 0x0C; memcpy(&resp[4], GIT_BUILD_ID, GIT_BUILD_ID_LEN); resp[4u + GIT_BUILD_ID_LEN] = 0xF7; USART4_SendData(resp, (uint16_t)sizeof(resp)); } /* F0 60 01 11 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) { /* 固件编码: 请求 01 0F(推荐)或 01 FF(仅裸串口);应答统一 01 0F + ASCII,避免 BLE-MIDI 吃掉 0xFF */ uint8_t resp[16]; uint8_t len = (uint8_t)strlen(fwname); (void)data; if (len > 10u) len = 10u; resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x01; resp[3] = 0x0F; memcpy(&resp[4], fwname, len); resp[4 + len] = 0xF7; USART4_SendData(resp, (uint16_t)(4u + len + 1u)); } /*-------- 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 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 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(); /* doc: single-byte bpm; clamp >127 (App should read exact bpm via 03 06) */ resp[5] = (mGuiData[GUI_SPEED].Current > 0x7F) ? 0x7F : (uint8_t)mGuiData[GUI_SPEED].Current; USART4_SendData(resp, sizeof(resp)); /* boot / song-entry special: F0 60 03 04 01 00 00 00 F7 doc: also push current BPM as active report F0 51 03 F7 */ if (data[5] == 0 && data[6] == 0 && data[7] == 0) { uint8_t bpmr[6] = {0xF0, 0x51, 0x03, 0x00, 0x00, 0xF7}; bpmr[3] = (uint8_t)(mGuiData[GUI_SPEED].Current / 128); bpmr[4] = (uint8_t)(mGuiData[GUI_SPEED].Current % 128); USART4_SendData(bpmr, sizeof(bpmr)); } } break; case 2: /* user style list page: F0 60 03 04 02 F7 reply: F0 60 03 04 02 [ ...] F7 */ { uint8_t resp[64]; uint32_t from = 0, to = LOCAL_SONG_COUNT; uint16_t i, n = 0, len; /* from/to: 24-bit big-endian, optional (doc example: 00 00 00 07) */ if (data[5] || data[6] || data[7]) { from = ((uint32_t)data[5] << 16) | ((uint32_t)data[6] << 8) | data[7]; to = ((uint32_t)data[8] << 16) | ((uint32_t)data[9] << 8) | data[10]; } if (from > LOCAL_SONG_COUNT) from = LOCAL_SONG_COUNT; if (to > LOCAL_SONG_COUNT || to <= from) to = LOCAL_SONG_COUNT; resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x03; resp[3] = 0x04; resp[4] = 0x02; len = 7; /* count filled after loop */ for (i = (uint16_t)from; i < to; i++) { if (len + 6 + 1 > sizeof(resp)) break; resp[len++] = (uint8_t)(i / 128); resp[len++] = (uint8_t)(i % 128); memcpy(&resp[len], LocalSongCode[i], 4); len += 4; n++; } resp[5] = (uint8_t)(n / 128); resp[6] = (uint8_t)(n % 128); resp[len++] = 0xF7; USART4_SendData(resp, len); } break; case 3: /* user style total: reply F0 60 03 04 00 F7 (doc: 00 00 1E) */ { uint8_t resp[9] = {0xF0, 0x60, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0xF7}; resp[5] = (uint8_t)((LOCAL_SONG_COUNT >> 16) & 0xFF); resp[6] = (uint8_t)((LOCAL_SONG_COUNT >> 8) & 0xFF); resp[7] = (uint8_t)(LOCAL_SONG_COUNT & 0xFF); 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; LOG_I("BLE", "05 00 ack then soft power off"); USART4_SendData(resp, sizeof(resp)); /* Soft-off cuts BT power immediately in PowerOff(); give UART4 + ATS2853 time to push the ACK Notify before requesting power-off. */ USART4_WaitTxIdle(50); rt_thread_mdelay(120); System_RequestPowerOff(); }