From fcb0ddd7f537955bc2842d482ce5b9ad55540c91 Mon Sep 17 00:00:00 2001 From: Steven Dan Date: Thu, 26 Mar 2026 23:22:40 +0800 Subject: [PATCH] hid dfu --- .../CMakeLists.txt | 3 + .../gen_update.bat | 3 +- .../src/extensions/audiohw.xc | 161 +++- .../src/extensions/dfu_flash_interface.c | 273 ++++++ .../src/extensions/dfu_flash_interface.h | 34 + .../src/extensions/dfu_flashlib_user.c | 129 +++ .../src/extensions/dfu_upgrade.c | 436 +++++++++ .../src/extensions/dfu_upgrade.h | 156 ++++ .../src/extensions/eq.c | 48 +- .../src/extensions/eq_designer_new.py | 871 +++++++++++++++--- .../src/extensions/eq_hid_protocol.md | 130 +++ .../src/extensions/main.xc | 23 +- 12 files changed, 2095 insertions(+), 172 deletions(-) create mode 100644 sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.c create mode 100644 sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.h create mode 100644 sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flashlib_user.c create mode 100644 sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.c create mode 100644 sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/CMakeLists.txt b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/CMakeLists.txt index c625303..d1df5b6 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/CMakeLists.txt +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/CMakeLists.txt @@ -156,6 +156,7 @@ set(APP_COMPILER_FLAGS_f1_music_uac2 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC -DOUTPUT_VOLUME_CONTROL=1 #-DDEBUG_MEMORY_LOG_ENABLED=1 -DXUA_DFU_EN=1 + -DHID_DFU_EN=1 #-DIR_SWITCHING_MODE -DHID_CONTROLS=1) @@ -181,6 +182,7 @@ set(APP_COMPILER_FLAGS_f3_f4_fps_uac2 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 -DOUTPUT_VOLUME_CONTROL=1 #-DDEBUG_MEMORY_LOG_ENABLED=1 -DXUA_DFU_EN=1 + -DHID_DFU_EN=1 -DIR_SWITCHING_MODE -DHID_CONTROLS=1) set(APP_COMPILER_FLAGS_f6_f7_fps_uac1 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 @@ -204,6 +206,7 @@ set(APP_COMPILER_FLAGS_f6_f7_fps_uac1 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 -DOUTPUT_VOLUME_CONTROL=1 #-DDEBUG_MEMORY_LOG_ENABLED=1 -DXUA_DFU_EN=1 + -DHID_DFU_EN=1 -DIR_SWITCHING_MODE -DHID_CONTROLS=1) diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_update.bat b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_update.bat index 1f280a7..ca4e831 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_update.bat +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_update.bat @@ -1,2 +1 @@ -xflash --factory-version 15.3 --target-file src/core/synido.xn --upgrade 2 bin\ex3d_UAC1\app_usb_aud_phaten_gs_ex3d_UAC1.xe --upgrade 4 bin/ex3d_stereo_2k/app_usb_aud_phaten_gs_ex3d_stereo_2k.xe --upgrade 1 bin\ex3d_71_game\app_usb_aud_phaten_gs_ex3d_71_game.xe --upgrade 3 bin\ex3d_71_movie\app_usb_aud_phaten_gs_ex3d_71_movie.xe -o %1 - +xflash --factory-version 15.3 --target-file src/core/synido.xn --upgrade 2 bin/f5_music_uac1/app_usb_aud_sy102_f5_music_uac1.xe --upgrade 3 bin/f1_music_uac2/app_usb_aud_sy102_f1_music_uac2.xe --upgrade 1 bin/f3_f4_fps_uac2/app_usb_aud_sy102_f3_f4_fps_uac2.xe -o %1 diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiohw.xc b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiohw.xc index 40ca12e..31dc09d 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiohw.xc +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiohw.xc @@ -80,9 +80,6 @@ unsigned g_volume_level = 29, g_saved_volume_level = 29; unsigned g_request_volume_set = 0; unsigned g_init_saved_settings = 0; unsigned g_host_volume = 0x0; -// HID主动上报缓冲区(被eq.c通过extern引用) -unsigned char g_hid_status_report_data[63] = {0}; -unsigned int g_hid_status_report_index = 0; unsigned g_last_volume_level = 0xFF; // 上次已上报的音量级别,0xFF表示初始化未完成 unsigned g_mic_volume_level = 37; // 麦克风PGA增益级别(0=mute, 1-37=0dB~36dB,HID可见范围) unsigned g_request_mic_volume_set = 0; @@ -103,16 +100,12 @@ unsigned g_disable_i2c = 0; unsigned g_dac_mode = 10; unsigned g_new_dac_mode = 0; unsigned g_samfreq = 48000; +unsigned g_dsd_mode = 0; unsafe chanend uc_audiohw; // tile[1] end: AudioHwConfig → button_task (tile[0]) -// 改动原因:添加DSD模式全局变量,用于HID命令读取采样率和格式信息(已去掉DAC采样分辨率) -unsigned g_dsd_mode = 0; // DSD模式:0=PCM, >0=DSD -unsigned g_gain_mode = 0; // 0: 低阻, 1: 高阻 -// 改动原因:支持8种滤波器模式(0-7),与g_filter_map[8]数组定义一致 -unsigned g_filter_mode = 0; // 0-7: 对应8种滤波器模式 +#if HID_DFU_EN +unsafe streaming chanend uc_dfu; // tile[1] send end: hid_button_task → button_task (tile[0]) +#endif // 改动原因:添加增益模式和滤波器模式的请求变量,用于HID命令设置模式 -unsigned g_request_gain_mode = -1; // -1表示无请求,0=低阻,1=高阻 -// 改动原因:支持8种滤波器模式(0-7),与g_filter_map[8]数组定义一致 -unsigned g_request_filter_mode = -1; // -1表示无请求,0-7=滤波器模式 unsigned g_request_game_mode = -1; #if EQ_EN @@ -294,9 +287,36 @@ enum {IR_OFF=0, IR_GAME=1, IR_MUSIC=2, IR_MOVIE=3, IR_7_1_GAME=4, IR_7_1_MUSIC=5 #ifndef HID_MAX_DATA_BYTES #define HID_MAX_DATA_BYTES ( 64 ) #endif -unsigned hidSendData_local[HID_MAX_DATA_BYTES / 4]; +extern unsigned hidSendData[]; -void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx) +#if HID_DFU_EN +#include "dfu_upgrade.h" +/* 将HID收到的63字节固件升级START命令打包为16个unsigned word, + * 通过uc_dfu streaming channel发送到button_task (tile[0])执行。 + * 从eq.c的process_send_params中对0xA7命令调用此函数。 */ +void dfu_chan_forward(unsigned char data[], unsigned len) +{ + unsafe + { + for (int i = 0; i < 16; i++) { + unsigned word = 0; + for (int j = 0; j < 4; j++) { + int idx = i * 4 + j; + if (idx < 63) { + word |= ((unsigned)data[idx]) << (j * 8); + } + } + uc_dfu <: word; + } + } +} +#endif + +void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx +#if HID_DFU_EN + , streaming chanend c_dfu_rx +#endif + ) { //hwtimer_t timer = hwtimer_alloc(); unsigned time = 0; @@ -633,8 +653,6 @@ void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, { case c_hidSendData :> hidData0: { - unsigned *reportData = hidSendData_local; - reportData[0] = hidData0; if (hidData0 == 0xffffffff) { debug_printf("receive end data\n"); @@ -727,12 +745,20 @@ void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, #endif break; } -#if (HID_CONTROLS == 1) - for (int i=1; i<(HID_MAX_DATA_BYTES/4); i++) { - c_hidSendData :> reportData[i]; - } - hidSetChangePending(1); + +#if HID_DFU_EN + if (!g_in_fw_upgrade) #endif + { +#if (HID_CONTROLS == 1) + unsigned *reportData = hidSendData; + reportData[0] = hidData0; + for (int i=1; i<(HID_MAX_DATA_BYTES/4); i++) { + c_hidSendData :> reportData[i]; + } + hidSetChangePending(1); +#endif + } break; } @@ -780,21 +806,46 @@ void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, c_audiohw_rx <: 0xff; #if HID_CONTROLS > 0 - g_hid_status_report_data[0] = 0x77; - g_hid_status_report_data[1] = 0x9F; - g_hid_status_report_data[2] = (unsigned char)(new_samfreq & 0xFF); - g_hid_status_report_data[3] = (unsigned char)((new_samfreq >> 8) & 0xFF); - g_hid_status_report_data[4] = (unsigned char)((new_samfreq >> 16) & 0xFF); - g_hid_status_report_data[5] = (unsigned char)((new_samfreq >> 24) & 0xFF); - g_hid_status_report_data[6] = (unsigned char)new_dsd_mode; - for (int i = 7; i < 63; i++) - g_hid_status_report_data[i] = 0x00; - g_hid_status_report_index = 63; - hidSetChangePending(0x1); +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + { + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0x9F; + ptr[3] = (unsigned char)(new_samfreq & 0xFF); + ptr[4] = (unsigned char)((new_samfreq >> 8) & 0xFF); + ptr[5] = (unsigned char)((new_samfreq >> 16) & 0xFF); + ptr[6] = (unsigned char)((new_samfreq >> 24) & 0xFF); + ptr[7] = (unsigned char)new_dsd_mode; + for (int i = 8; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } + hidSetChangePending(0x1); + } #endif break; } +#if HID_DFU_EN + case c_dfu_rx :> unsigned dfu_first_word: + { + // 接收来自tile[1] hid_button_task转发的固件升级START命令(16个word) + unsigned dfu_words[16]; + dfu_words[0] = dfu_first_word; + for (int i = 1; i < 16; i++) + c_dfu_rx :> dfu_words[i]; + unsigned char cmd_buf[63]; + for (int i = 0; i < 63; i++) + cmd_buf[i] = (dfu_words[i / 4] >> ((i % 4) * 8)) & 0xFF; + if (cmd_buf[1] == FIRMWARE_UPGRADE_START) + handle_firmware_upgrade_start(cmd_buf, 63); + break; + } +#endif + case tmr when timerafter(time) :> void : { //hwtimer_change_trigger_time(timer, hwtimer_get_time(timer) + KEY_POLLING_INTERVAL); @@ -1512,13 +1563,19 @@ void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, // HID监听音量变化主动上报(编码器旋转或HID命令导致的音量变化) { unsigned current_volume_level = g_volume_level; +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif if (g_last_volume_level != current_volume_level && g_last_volume_level != 0xFF) { - g_hid_status_report_data[0] = 0x77; // 同步头1 - g_hid_status_report_data[1] = 0x94; // GET_VOLUME命令码 - g_hid_status_report_data[2] = (unsigned char)current_volume_level; - for (int i = 3; i < 63; i++) - g_hid_status_report_data[i] = 0x00; - g_hid_status_report_index = 63; + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0x94; + ptr[3] = (unsigned char)current_volume_level; + for (int i = 4; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } hidSetChangePending(0x1); debug_printf("Volume changed: %d -> %d, HID report sent\n", g_last_volume_level, current_volume_level); @@ -1529,13 +1586,19 @@ void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, // HID麦克风增益变化主动上报 { unsigned current_mic_level = g_mic_volume_level; +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif if (g_last_mic_volume_level != current_mic_level && g_last_mic_volume_level != 0xFF) { - g_hid_status_report_data[0] = 0x77; // 同步头1 - g_hid_status_report_data[1] = 0x83; // GET_MIC_VOLUME命令码 - g_hid_status_report_data[2] = (unsigned char)current_mic_level; - for (int i = 3; i < 63; i++) - g_hid_status_report_data[i] = 0x00; - g_hid_status_report_index = 63; + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0x83; + ptr[3] = (unsigned char)current_mic_level; + for (int i = 4; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } hidSetChangePending(0x1); debug_printf("Mic volume changed: %d -> %d, HID report sent\n", g_last_mic_volume_level, current_mic_level); @@ -1600,7 +1663,11 @@ void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, } } -void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx) +void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx +#if HID_DFU_EN + , streaming chanend c_dfu_rx +#endif + ) { i2c_master_if i2c[1]; board_setup(); @@ -1608,7 +1675,11 @@ void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vo i2c_master(i2c, 1, p_scl, p_sda, 300); { unsafe {i_i2c_client = i2c[0];} - button_task(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw_rx); + button_task(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw_rx +#if HID_DFU_EN + , c_dfu_rx +#endif + ); } } } diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.c b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.c new file mode 100644 index 0000000..83b157c --- /dev/null +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.c @@ -0,0 +1,273 @@ +// Copyright 2011-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#define DEBUG_PRINT_ENABLE 0 +#include +#include +#include +#include +#include +#include +#include "debug_print.h" + +#include "xua.h" + +#if HID_DFU_EN + +#ifndef DFU_FLASH_MAX_UPGRADE_SIZE +#define DFU_FLASH_MAX_UPGRADE_SIZE (1600 * 1024) +#endif + +#define DFU_FLASH_ERROR() + +static int dfu_flash_device_open = 0; +static fl_BootImageInfo dfu_factory_image; +static fl_BootImageInfo dfu_upgrade_image; + +static int dfu_upgrade_image_valid = 0; +static int dfu_current_flash_subpage_index = 0; +static unsigned char dfu_current_flash_page_data[256]; + +int dfu_flash_cmd_enable_ports() __attribute__ ((weak)); +int dfu_flash_cmd_enable_ports() { + return 0; +} + +int dfu_flash_cmd_disable_ports() __attribute__ ((weak)); +int dfu_flash_cmd_disable_ports() { + return 0; +} + +void DFUCustomFlashEnable() __attribute__ ((weak)); +void DFUCustomFlashEnable() +{ + return; +} + +void DFUCustomFlashDisable() __attribute__ ((weak)); +void DFUCustomFlashDisable() +{ + return; +} + +/* Returns non-zero for error */ +int dfu_flash_cmd_init(void) +{ + debug_printf("dfu_flash_cmd_init: dfu_flash_cmd_init\n"); + fl_BootImageInfo image; + + if (!dfu_flash_device_open) + { + if (dfu_flash_cmd_enable_ports()) + dfu_flash_device_open = 1; + debug_printf("dfu_flash_cmd_init: dfu_flash_device_open = 1\n"); + } + + if (!dfu_flash_device_open) + { + debug_printf("dfu_flash_cmd_init: dfu_flash_device_open = 0\n"); + return 1; + } + +#if (!XUA_QUAD_SPI_FLASH) + // Disable flash protection + fl_setProtection(0); +#endif + + if (fl_getFactoryImage(&image) != 0) + { + return 1; + } + + dfu_factory_image = image; + + if (fl_getNextBootImage(&image) == 0) + { + dfu_upgrade_image_valid = 1; + dfu_upgrade_image = image; + debug_printf("dfu_flash_cmd_init: dfu_upgrade_image_valid = 1\n"); + } + + debug_printf("dfu_flash_cmd_init: return 0\n"); + return 0; +} + +int dfu_flash_cmd_deinit(void) +{ + if (!dfu_flash_device_open) + return 0; + + dfu_flash_cmd_disable_ports(); + dfu_flash_device_open = 0; + return 0; +} + +int dfu_flash_cmd_read_page(unsigned char *data) +{ + if (!dfu_upgrade_image_valid) + { + *(unsigned int *)data = 1; + return 4; + } + + if (*(unsigned int *)data == 0) + { + fl_startImageRead(&dfu_upgrade_image); + } + + dfu_current_flash_subpage_index = 0; + + if (fl_readImagePage(dfu_current_flash_page_data) == 0) + { + *(unsigned int *)data = 0; + } + else + { + *(unsigned int *)data = 1; + } + return 4; +} + +int dfu_flash_cmd_read_page_data(unsigned char *data) +{ + unsigned char *page_data_ptr = &dfu_current_flash_page_data[dfu_current_flash_subpage_index * 64]; + memcpy(data, page_data_ptr, 64); + + dfu_current_flash_subpage_index++; + + return 64; +} + +static int begin_write() +{ + int result; + // TODO this will take a long time. To minimise the amount of time spent + // paused on this operation it would be preferable to move to this to a + // seperate command, e.g. start_write. + do + { + result = fl_startImageAdd(&dfu_factory_image, DFU_FLASH_MAX_UPGRADE_SIZE, 0); + } while (result > 0); + + if (result < 0) + { + DFU_FLASH_ERROR(); + } + return result; +} + +static int pages_written = 0; + +int dfu_flash_cmd_write_page(unsigned char *data) +{ + /* 改动原因:协议仅约定第一个字节为 flag(0=第一页,1=后续页,2=结束); + * 若用 *(unsigned int *)data 读取 4 字节,而调用处只设置 data[0]=0 且未清零 data[1..3], + * 则 flag 可能非 0,导致不进入 case 0,begin_write() 被跳过。改为仅用首字节。 */ + unsigned int flag = (unsigned int)data[0]; + if (flag == 0) + debug_printf("dfu_flash_cmd_write_page: flag = %d\n", flag); + + if (dfu_upgrade_image_valid) + { + debug_printf("dfu_flash_cmd_write_page: dfu_upgrade_image_valid = 1\n"); + return 0; + } + + switch (flag) + { + case 0: + // First page. + debug_printf("dfu_flash_cmd_write_page: begin_write\n"); + int result = begin_write(); + if (result < 0) + return result; + pages_written = 0; + // fallthrough + case 1: + // Do nothing. + break; + case 2: + // Termination. + if (fl_endWriteImage() != 0) + DFU_FLASH_ERROR(); + + // Sanity check + fl_BootImageInfo image = dfu_factory_image; + if (fl_getNextBootImage(&image) != 0) + DFU_FLASH_ERROR(); + break; + } + dfu_current_flash_subpage_index = 0; + + return 0; +} + +int dfu_flash_cmd_write_page_data(unsigned char *data) +{ + unsigned char *page_data_ptr = &dfu_current_flash_page_data[dfu_current_flash_subpage_index * 64]; + + if (dfu_upgrade_image_valid) + { + return 0; + } + + if (dfu_current_flash_subpage_index >= 4) + { + return 0; + } + + memcpy(page_data_ptr, data, 64); + + dfu_current_flash_subpage_index++; + + if (dfu_current_flash_subpage_index == 4) + { + if (fl_writeImagePage(dfu_current_flash_page_data) != 0) + DFU_FLASH_ERROR(); + pages_written++; + } + + return 0; +} + + +int dfu_flash_cmd_erase_all(void) +{ + fl_BootImageInfo tmp_image = dfu_upgrade_image; + + if (dfu_upgrade_image_valid) + { + if (fl_deleteImage(&dfu_upgrade_image) != 0) + { + DFU_FLASH_ERROR(); + } + + // Keep deleting all upgrade images + // TODO Perhaps using replace would be nicer... + while(1) + { + if (fl_getNextBootImage(&tmp_image) == 0) + { + if (fl_deleteImage(&tmp_image) != 0) + { + DFU_FLASH_ERROR(); + } + } + else + { + break; + } + } + + dfu_upgrade_image_valid = 0; + } + /* 改动原因:强制升级流程中,erase 后必须允许后续第一页写执行 begin_write(); + * 若仅在上面 if 块内清零,某些路径(如删除失败或 while 未正确退出)可能导致 + * dfu_upgrade_image_valid 仍为 1,write_page(flag=0) 会直接 return 0 而不调用 + * begin_write(),导致 begin_write 未执行。此处无条件清零,保证 erase 后下一 + * 次 write_page(0) 必定执行 begin_write。 */ + dfu_upgrade_image_valid = 0; + debug_printf("dfu_flash_cmd_erase_all: set dfu_upgrade_image_valid = 0\n"); + return 0; +} +#endif + diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.h b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.h new file mode 100644 index 0000000..500e8d4 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.h @@ -0,0 +1,34 @@ +// Copyright 2011-2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _dfu_flash_interface_h_ +#define _dfu_flash_interface_h_ + +int dfu_flash_cmd_init(void); +/** + * Prepare to write a page of a new upgrade image. + * The first word of data should be set to 0 if it is the first page, + * 1 for all other pages and 2 to terminate the write (no further data is sent). + */ +int dfu_flash_cmd_write_page(unsigned char []); +/** + * Provide upgrade image data. flash_cmd_write_page() must be called previously. + * Once a page of data has been provided it is written to the device. + */ +int dfu_flash_cmd_write_page_data(unsigned char []); +/** + * Read a page of data from the upgrade image. + * If the first word of data is 0 the page is read from the start of the + * upgrade image, otherwise the next page in the image will be read. + * On return the first word of data is written with 1 if there is nothing to + * read and 0 otherwise. + */ +int dfu_flash_cmd_read_page(unsigned char []); +/** + * Get data previously read by flash_cmd_read_page(). + */ +int dfu_flash_cmd_read_page_data(unsigned char []); +int dfu_flash_cmd_erase_all(void); +int dfu_flash_cmd_reboot(void); +int dfu_flash_cmd_init(void); +int dfu_flash_cmd_deinit(void); +#endif /*_dfu_flash_interface_h_*/ diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flashlib_user.c b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flashlib_user.c new file mode 100644 index 0000000..b432a27 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flashlib_user.c @@ -0,0 +1,129 @@ +// Copyright 2012-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include "xua.h" +#if (XUA_DFU_EN == 1 || UAC1 == 1) +#include "uac_hwresources.h" +#include +#include +#if (XUA_QUAD_SPI_FLASH) +#include +#else +#include +#endif +#include + +#define settw(a,b) {__asm__ __volatile__("settw res[%0], %1": : "r" (a) , "r" (b));} +#define setc(a,b) {__asm__ __volatile__("setc res[%0], %1": : "r" (a) , "r" (b));} +#define setclk(a,b) {__asm__ __volatile__("setclk res[%0], %1": : "r" (a) , "r" (b));} +#define portin(a,b) {__asm__ __volatile__("in %0, res[%1]": "=r" (b) : "r" (a));} +#define portout(a,b) {__asm__ __volatile__("out res[%0], %1": : "r" (a) , "r" (b));} + +#ifdef DFU_FLASH_DEVICE + +#if (XUA_QUAD_SPI_FLASH) +/* Using specified flash device rather than all supported in tools */ +fl_QuadDeviceSpec dfu_flash_devices[] = {DFU_FLASH_DEVICE}; +#else +/* Using specified flash device rather than all supported in tools */ +fl_DeviceSpec dfu_flash_devices[] = {DFU_FLASH_DEVICE}; +#endif +#endif + +#if (XUA_QUAD_SPI_FLASH) +/* +typedef struct { + out port qspiCS; + out port qspiSCLK; + out buffered port:32 qspiSIO; + clock qspiClkblk; +} fl_QSPIPorts; +*/ +fl_QSPIPorts dfu_p_qflash = +{ + XS1_PORT_1B, + XS1_PORT_1C, + XS1_PORT_4B, + CLKBLK_FLASHLIB +}; +#else +fl_PortHolderStruct p_flash = +{ + XS1_PORT_1A, + XS1_PORT_1B, + XS1_PORT_1C, + XS1_PORT_1D, + CLKBLK_FLASHLIB +}; +#endif + +/* return 1 for opened ports successfully */ +int dfu_flash_cmd_enable_ports() +{ + int result = 0; +#if (XUA_QUAD_SPI_FLASH) + /* Ports not shared */ +#else + setc(p_flash.spiMISO, XS1_SETC_INUSE_OFF); + setc(p_flash.spiCLK, XS1_SETC_INUSE_OFF); + setc(p_flash.spiMOSI, XS1_SETC_INUSE_OFF); + setc(p_flash.spiSS, XS1_SETC_INUSE_OFF); + setc(p_flash.spiClkblk, XS1_SETC_INUSE_OFF); + + setc(p_flash.spiMISO, XS1_SETC_INUSE_ON); + setc(p_flash.spiCLK, XS1_SETC_INUSE_ON); + setc(p_flash.spiMOSI, XS1_SETC_INUSE_ON); + setc(p_flash.spiSS, XS1_SETC_INUSE_ON); + setc(p_flash.spiClkblk, XS1_SETC_INUSE_ON); + setc(p_flash.spiClkblk, XS1_SETC_INUSE_ON); + + setclk(p_flash.spiMISO, XS1_CLKBLK_REF); + setclk(p_flash.spiCLK, XS1_CLKBLK_REF); + setclk(p_flash.spiMOSI, XS1_CLKBLK_REF); + setclk(p_flash.spiSS, XS1_CLKBLK_REF); + + setc(p_flash.spiMISO, XS1_SETC_BUF_BUFFERS); + setc(p_flash.spiMOSI, XS1_SETC_BUF_BUFFERS); + + settw(p_flash.spiMISO, 8); + settw(p_flash.spiMOSI, 8); +#endif + +#ifdef DFU_FLASH_DEVICE +#if (XUA_QUAD_SPI_FLASH) + result = fl_connectToDevice(&dfu_p_qflash, dfu_flash_devices, sizeof(dfu_flash_devices) / sizeof(fl_QuadDeviceSpec)); +#else + result = fl_connectToDevice(&dfu_p_flash, dfu_flash_devices, sizeof(dfu_flash_devices) / sizeof(fl_DeviceSpec)); +#endif +#else + /* Use default flash list */ +#if (XUA_QUAD_SPI_FLASH) + result = fl_connect(&dfu_p_qflash); +#else + result = fl_connect(&dfu_p_flash); +#endif +#endif + if (!result) + { + /* All okay.. */ + return 1; + } + else + { + return 0; + } +} + +int dfu_flash_cmd_disable_ports() +{ + fl_disconnect(); + +#if (!XUA_QUAD_SPI_FLASH) + setc(p_flash.spiMISO, XS1_SETC_INUSE_OFF); + setc(p_flash.spiCLK, XS1_SETC_INUSE_OFF); + setc(p_flash.spiMOSI, XS1_SETC_INUSE_OFF); + setc(p_flash.spiSS, XS1_SETC_INUSE_OFF); +#endif + + return 1; +} +#endif diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.c b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.c new file mode 100644 index 0000000..9b09cfb --- /dev/null +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.c @@ -0,0 +1,436 @@ +// Copyright 2026 Shanling. +// HID固件升级实现文件 (adapted for app_usb_aud_phaten_golden_6ch) + +#define DEBUG_PRINT_ENABLE 0 +#include "dfu_upgrade.h" +#include "user_func.h" +#include "dfu_flash_interface.h" +#include +#include +#include "debug_print.h" + +#if HID_DFU_EN +// 外部函数声明 +extern void hidSetChangePending(unsigned int id); +extern unsigned char check_sum(unsigned char *pbuf, unsigned len); +extern void device_reboot(void); +extern unsigned hidSendData[]; + +// 固件升级通知MCU的标志(phaten无UART MCU,保留为静态变量供内部使用) +static unsigned g_firmware_upgrade_mcu_notify = 0; +unsigned g_in_fw_upgrade = 0; // 升级进行中标志,供其他模块检查以阻止非升级HID上报 + +// 升级状态(静态全局变量) +static upgrade_status_t g_upgrade_status; + +// 全局标志:第一页写命令是否已执行 +static uint8_t g_first_page_write_executed = 0; +// 全局标志:是否已执行dfu_flash_cmd_erase_all +static uint8_t g_flash_erased = 0; + +// 初始化固件升级模块 +void firmware_upgrade_init(void) +{ + memset(&g_upgrade_status, 0, sizeof(upgrade_status_t)); + g_upgrade_status.state = UPGRADE_IDLE; + g_upgrade_status.expected_block_num = 0; + debug_printf("Firmware upgrade module initialized\n"); +} + +// 获取当前升级状态 +upgrade_state_t firmware_upgrade_get_state(void) +{ + return g_upgrade_status.state; +} + +// 重置升级状态 +void firmware_upgrade_reset(void) +{ + memset(&g_upgrade_status, 0, sizeof(upgrade_status_t)); + g_upgrade_status.state = UPGRADE_IDLE; + g_upgrade_status.expected_block_num = 0; + g_upgrade_status.first_page_written = 0; + g_first_page_write_executed = 0; + g_flash_erased = 0; +} + +// 发送固件升级响应(主动上报) +// 填充hidSendData,通过UserHIDGetData上报给host +void send_firmware_upgrade_response(uint8_t cmd, uint8_t *response_data, uint16_t data_len) +{ + unsigned char *ptr = (unsigned char *)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; // 同步头 + ptr[2] = cmd; // 命令码 + + if (response_data != NULL && data_len > 0) { + uint16_t copy_len = (data_len < 61) ? data_len : 61; + memcpy(&ptr[3], response_data, copy_len); + } + + for (int i = 3 + (int)data_len; i < 64; i++) { + ptr[i] = 0x00; + } + + hidSetChangePending(0x1); +} + +// 处理START命令 +unsigned char handle_firmware_upgrade_start(uint8_t data[], uint16_t len) +{ + uint8_t response[64] = {0}; + + if (g_upgrade_status.state != UPGRADE_IDLE) { + debug_printf("Firmware upgrade error: not in IDLE state (current: %d)\n", g_upgrade_status.state); + response[0] = STATUS_FAIL; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_START, response, 1); + return STATUS_FAIL; + } + + uint32_t firmware_size = data[2] | (data[3] << 8) | (data[4] << 16) | (data[5] << 24); + + debug_printf("g_first_page_write_executed: %d\n", g_first_page_write_executed); + + if (firmware_size == 0 || firmware_size > FLASH_MAX_UPGRADE_SIZE) { + debug_printf("Firmware upgrade error: invalid size %lu\n", firmware_size); + response[0] = STATUS_SIZE_INVALID; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_START, response, 1); + return STATUS_SIZE_INVALID; + } + + uint32_t aligned_size = ((firmware_size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE; + + if (dfu_flash_cmd_init() != 0) { + debug_printf("Firmware upgrade error: flash init failed\n"); + response[0] = STATUS_FAIL; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_START, response, 1); + return STATUS_FAIL; + } + + if (g_first_page_write_executed == 0 && g_flash_erased == 0) { + debug_printf("Force upgrade: erasing existing upgrade image\n"); + dfu_flash_cmd_erase_all(); + g_flash_erased = 1; + } else { + debug_printf("Force upgrade: erase skipped (already erased or begin_write started)\n"); + } + + g_upgrade_status.state = UPGRADE_PREPARING; + g_upgrade_status.firmware_size = aligned_size; + g_upgrade_status.total_blocks = (aligned_size + MAX_DATA_BLOCK_SIZE - 1) / MAX_DATA_BLOCK_SIZE; + g_upgrade_status.received_blocks = 0; + g_upgrade_status.expected_block_num = 0; + g_upgrade_status.error_code = 0; + + memset(g_upgrade_status.page_buf.buffer, 0, FLASH_PAGE_SIZE); + g_upgrade_status.page_buf.offset = 0; + g_upgrade_status.page_buf.total_pages = aligned_size / FLASH_PAGE_SIZE; + g_upgrade_status.page_buf.written_pages = 0; + + if (g_first_page_write_executed == 0) { + debug_printf("Firmware upgrade: executing first page write command (flag=0) in START...\n"); + unsigned char cmd_data[16]; + memset(cmd_data, 0, sizeof(cmd_data)); + cmd_data[0] = 0; + if (dfu_flash_cmd_write_page(cmd_data) != 0) { + debug_printf("Firmware upgrade error: first page write command failed (begin_write error)\n"); + dfu_flash_cmd_deinit(); + g_upgrade_status.state = UPGRADE_IDLE; + response[0] = STATUS_FAIL; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_START, response, 1); + return STATUS_FAIL; + } + g_first_page_write_executed = 1; + debug_printf("Firmware upgrade: first page write command completed\n"); + } else { + debug_printf("Firmware upgrade: first page write command already executed in ERASE, skipping %d\n", g_first_page_write_executed); + } + g_upgrade_status.first_page_written = 1; + + response[0] = STATUS_SUCCESS; + response[1] = (aligned_size >> 0) & 0xFF; + response[2] = (aligned_size >> 8) & 0xFF; + response[3] = (aligned_size >> 16) & 0xFF; + response[4] = (aligned_size >> 24) & 0xFF; + response[5] = (g_upgrade_status.total_blocks >> 0) & 0xFF; + response[6] = (g_upgrade_status.total_blocks >> 8) & 0xFF; + + send_firmware_upgrade_response(FIRMWARE_UPGRADE_START, response, 7); + g_firmware_upgrade_mcu_notify = 1; + g_in_fw_upgrade = 1; + return STATUS_SUCCESS; +} + +// 处理DATA命令 +unsigned char handle_firmware_upgrade_data(uint8_t data[], uint16_t len) +{ + uint8_t response[64] = {0}; + + if (g_upgrade_status.state != UPGRADE_PREPARING && g_upgrade_status.state != UPGRADE_TRANSFERRING) { + debug_printf("Firmware upgrade error: not in PREPARING or TRANSFERRING state (current: %d)\n", g_upgrade_status.state); + response[0] = STATUS_FAIL; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_DATA, response, 1); + return STATUS_FAIL; + } + + uint16_t block_num = data[2] | (data[3] << 8); + uint8_t data_len = data[4]; + + if (data_len != MAX_DATA_BLOCK_SIZE) { + debug_printf("Firmware upgrade error: invalid data length %d (must be %d)\n", data_len, MAX_DATA_BLOCK_SIZE); + response[0] = STATUS_SIZE_INVALID; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_DATA, response, 1); + return STATUS_SIZE_INVALID; + } + + if (block_num != g_upgrade_status.expected_block_num) { + debug_printf("Firmware upgrade error: block number mismatch (expected: %d, got: %d)\n", + g_upgrade_status.expected_block_num, block_num); + response[0] = STATUS_BLOCK_NUM_ERROR; + response[1] = (g_upgrade_status.expected_block_num >> 0) & 0xFF; + response[2] = (g_upgrade_status.expected_block_num >> 8) & 0xFF; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_DATA, response, 3); + return STATUS_BLOCK_NUM_ERROR; + } + + uint8_t received_checksum = data[5 + data_len]; + uint8_t calculated_checksum = check_sum(data, 5 + data_len); + + if (received_checksum != calculated_checksum) { + debug_printf("Firmware upgrade error: checksum mismatch (expected: 0x%02x, got: 0x%02x)\n", + calculated_checksum, received_checksum); + response[0] = STATUS_CHECKSUM_ERROR; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_DATA, response, 1); + return STATUS_CHECKSUM_ERROR; + } + + if (g_upgrade_status.state == UPGRADE_PREPARING) { + g_upgrade_status.state = UPGRADE_TRANSFERRING; + + if (block_num == 0) { + if (g_first_page_write_executed == 0 && g_flash_erased == 0) { + debug_printf("Firmware upgrade: erasing existing upgrade image\n"); + dfu_flash_cmd_erase_all(); + g_flash_erased = 1; + } + } + } + + uint16_t buf_offset = g_upgrade_status.page_buf.offset; + uint16_t remaining = FLASH_PAGE_SIZE - buf_offset; + uint16_t to_copy = (data_len < remaining) ? data_len : remaining; + + memcpy(&g_upgrade_status.page_buf.buffer[buf_offset], &data[5], to_copy); + g_upgrade_status.page_buf.offset += to_copy; + + if (g_upgrade_status.page_buf.offset >= FLASH_PAGE_SIZE) { + unsigned char cmd_data[16]; + + if (g_upgrade_status.page_buf.written_pages == 0) { + debug_printf("Firmware upgrade: writing first page data (flag=0 already executed in START)\n"); + } else { + cmd_data[0] = 1; + dfu_flash_cmd_write_page(cmd_data); + } + + for (int i = 0; i < 4; i++) { + dfu_flash_cmd_write_page_data(&g_upgrade_status.page_buf.buffer[i * 64]); + } + + g_upgrade_status.page_buf.written_pages++; + g_upgrade_status.page_buf.offset = 0; + } + + if (to_copy < data_len) { + uint16_t remaining_data = data_len - to_copy; + memcpy(&g_upgrade_status.page_buf.buffer[0], &data[5 + to_copy], remaining_data); + g_upgrade_status.page_buf.offset = remaining_data; + } + + g_upgrade_status.received_blocks++; + g_upgrade_status.expected_block_num++; + + response[0] = STATUS_SUCCESS; + response[1] = (block_num >> 0) & 0xFF; + response[2] = (block_num >> 8) & 0xFF; + + send_firmware_upgrade_response(FIRMWARE_UPGRADE_DATA, response, 3); + return STATUS_SUCCESS; +} + +// 处理END命令 +unsigned char handle_firmware_upgrade_end(uint8_t data[], uint16_t len) +{ + uint8_t response[64] = {0}; + + if (g_upgrade_status.state != UPGRADE_TRANSFERRING) { + debug_printf("Firmware upgrade error: not in TRANSFERRING state (current: %d)\n", g_upgrade_status.state); + response[0] = STATUS_FAIL; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_END, response, 1); + return STATUS_FAIL; + } + + g_upgrade_status.state = UPGRADE_COMPLETING; + + uint32_t firmware_size = data[2] | (data[3] << 8) | (data[4] << 16) | (data[5] << 24); + debug_printf("Firmware upgrade END: firmware_size=%lu\n", firmware_size); + + if (g_upgrade_status.page_buf.offset > 0) { + for (uint16_t i = g_upgrade_status.page_buf.offset; i < FLASH_PAGE_SIZE; i++) { + g_upgrade_status.page_buf.buffer[i] = 0x00; + } + + unsigned char cmd_data[16]; + cmd_data[0] = 1; + dfu_flash_cmd_write_page(cmd_data); + + for (int i = 0; i < 4; i++) { + dfu_flash_cmd_write_page_data(&g_upgrade_status.page_buf.buffer[i * 64]); + } + + g_upgrade_status.page_buf.written_pages++; + debug_printf("Firmware upgrade: last page written (total pages: %d)\n", + g_upgrade_status.page_buf.written_pages); + } + + unsigned char cmd_data[16]; + cmd_data[0] = 2; + int result = dfu_flash_cmd_write_page(cmd_data); + + if (result != 0) { + debug_printf("Firmware upgrade error: flash write termination failed\n"); + g_upgrade_status.state = UPGRADE_ERROR; + g_upgrade_status.error_code = STATUS_FAIL; + response[0] = STATUS_FAIL; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_END, response, 1); + return STATUS_FAIL; + } + + debug_printf("Firmware upgrade: image verification passed\n"); + g_upgrade_status.state = UPGRADE_COMPLETED; + + response[0] = STATUS_SUCCESS; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_END, response, 1); + debug_printf("Firmware upgrade completed successfully\n"); + g_firmware_upgrade_mcu_notify = 2; + g_in_fw_upgrade = 0; + g_upgrade_status.state = UPGRADE_IDLE; + return STATUS_SUCCESS; +} + +// 处理STATUS命令 +unsigned char handle_firmware_upgrade_status(uint8_t data[], uint16_t len) +{ + uint8_t response[64] = {0}; + + response[0] = (uint8_t)g_upgrade_status.state; + response[1] = (g_upgrade_status.received_blocks >> 0) & 0xFF; + response[2] = (g_upgrade_status.received_blocks >> 8) & 0xFF; + response[3] = (g_upgrade_status.total_blocks >> 0) & 0xFF; + response[4] = (g_upgrade_status.total_blocks >> 8) & 0xFF; + response[5] = (g_upgrade_status.page_buf.written_pages >> 0) & 0xFF; + response[6] = (g_upgrade_status.page_buf.written_pages >> 8) & 0xFF; + response[7] = (g_upgrade_status.page_buf.total_pages >> 0) & 0xFF; + response[8] = (g_upgrade_status.page_buf.total_pages >> 8) & 0xFF; + + uint32_t bytes_transferred = g_upgrade_status.received_blocks * MAX_DATA_BLOCK_SIZE; + response[9] = (bytes_transferred >> 0) & 0xFF; + response[10] = (bytes_transferred >> 8) & 0xFF; + response[11] = (bytes_transferred >> 16) & 0xFF; + response[12] = (bytes_transferred >> 24) & 0xFF; + + response[13] = (g_upgrade_status.firmware_size >> 0) & 0xFF; + response[14] = (g_upgrade_status.firmware_size >> 8) & 0xFF; + response[15] = (g_upgrade_status.firmware_size >> 16) & 0xFF; + response[16] = (g_upgrade_status.firmware_size >> 24) & 0xFF; + + response[17] = g_upgrade_status.error_code; + + send_firmware_upgrade_response(FIRMWARE_UPGRADE_STATUS, response, 18); + return STATUS_SUCCESS; +} + +// 处理ABORT命令 +unsigned char handle_firmware_upgrade_abort(uint8_t data[], uint16_t len) +{ + uint8_t response[64] = {0}; + + uint8_t erase_flag = data[2]; + debug_printf("Firmware upgrade ABORT: erase_flag=0x%02x\n", erase_flag); + + if (erase_flag == 0x01) { + debug_printf("Firmware upgrade: erasing written data\n"); + dfu_flash_cmd_erase_all(); + } + + dfu_flash_cmd_deinit(); + firmware_upgrade_reset(); + g_upgrade_status.state = UPGRADE_ABORTED; + + response[0] = STATUS_SUCCESS; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_ABORT, response, 1); + g_firmware_upgrade_mcu_notify = 3; + g_in_fw_upgrade = 0; + g_upgrade_status.state = UPGRADE_IDLE; + return STATUS_SUCCESS; +} + +// 处理ERASE命令 +unsigned char handle_firmware_upgrade_erase(uint8_t data[], uint16_t len) +{ + uint8_t response[64] = {0}; + + debug_printf("Firmware upgrade ERASE: erasing all upgrade images\n"); + + if (dfu_flash_cmd_init() != 0) { + debug_printf("Firmware upgrade error: flash init failed\n"); + response[0] = STATUS_FAIL; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_ERASE, response, 1); + return STATUS_FAIL; + } + + if (g_first_page_write_executed == 0 && g_flash_erased == 0) { + dfu_flash_cmd_erase_all(); + g_flash_erased = 1; + } else { + debug_printf("Firmware upgrade ERASE: erase skipped (already erased or begin_write started)\n"); + } + + dfu_flash_cmd_deinit(); + + response[0] = STATUS_SUCCESS; + send_firmware_upgrade_response(FIRMWARE_UPGRADE_ERASE, response, 1); + return STATUS_SUCCESS; +} + +// 固件升级主处理函数(分发0xA7-0xAC,0xAE由eq.c直接处理) +unsigned char process_firmware_upgrade_cmd(uint8_t data[], uint16_t len) +{ + if (data[0] != 0x77) { + debug_printf("Firmware upgrade error: invalid sync header 0x%02x\n", data[0]); + return STATUS_FAIL; + } + + uint8_t cmd = data[1]; + debug_printf("Firmware upgrade command: 0x%02x\n", cmd); + + switch (cmd) { + case FIRMWARE_UPGRADE_START: + return handle_firmware_upgrade_start(data, len); + case FIRMWARE_UPGRADE_DATA: + return handle_firmware_upgrade_data(data, len); + case FIRMWARE_UPGRADE_END: + return handle_firmware_upgrade_end(data, len); + case FIRMWARE_UPGRADE_STATUS: + return handle_firmware_upgrade_status(data, len); + case FIRMWARE_UPGRADE_ABORT: + return handle_firmware_upgrade_abort(data, len); + case FIRMWARE_UPGRADE_ERASE: + return handle_firmware_upgrade_erase(data, len); + default: + debug_printf("Firmware upgrade error: unknown command 0x%02x\n", cmd); + return STATUS_FAIL; + } +} + +#endif // HID_DFU_EN diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.h b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.h new file mode 100644 index 0000000..da5da73 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.h @@ -0,0 +1,156 @@ +// Copyright 2026 Shanling. +// HID固件升级头文件 +// 改动原因:实现通过HID协议进行固件升级的功能,提供高效、稳定、安全的升级方案 + +#ifndef _DFU_UPGRADE_H_ +#define _DFU_UPGRADE_H_ + +#include + +// 固件升级命令定义 +#define FIRMWARE_UPGRADE_START 0xA7 // 开始固件升级 +#define FIRMWARE_UPGRADE_DATA 0xA8 // 传输固件数据块 +#define FIRMWARE_UPGRADE_END 0xA9 // 结束固件升级 +#define FIRMWARE_UPGRADE_STATUS 0xAA // 获取升级状态 +#define FIRMWARE_UPGRADE_ABORT 0xAB // 中止固件升级 +#define FIRMWARE_UPGRADE_ERASE 0xAC // 擦除现有升级镜像 +#define DEVICE_REBOOT 0xAE // 设备重启(通用命令,不仅限于固件升级) + +// 升级状态定义 +typedef enum { + UPGRADE_IDLE = 0x00, // 空闲状态,未开始升级 + UPGRADE_PREPARING = 0x01, // 准备状态,已调用START + UPGRADE_TRANSFERRING = 0x02, // 传输状态,正在接收数据 + UPGRADE_COMPLETING = 0x03, // 完成中,正在写入最后一页 + UPGRADE_COMPLETED = 0x04, // 已完成,升级成功 + UPGRADE_ERROR = 0x05, // 错误状态,升级失败 + UPGRADE_ABORTED = 0x06 // 已中止,升级被中止 +} upgrade_state_t; + +// 状态码定义 +#define STATUS_SUCCESS 0x00 // 成功 +#define STATUS_FAIL 0x01 // 失败 +#define STATUS_SPACE_INSUFFICIENT 0x02 // Flash空间不足 +#define STATUS_SIZE_INVALID 0x03 // 大小无效 +#define STATUS_BLOCK_NUM_ERROR 0x04 // 块序号错误 +#define STATUS_CHECKSUM_ERROR 0x05 // Checksum错误 +#define STATUS_DATA_INCOMPLETE 0x06 // 数据不完整 +#define STATUS_VERSION_INVALID 0x08 // 版本号无效 +#define STATUS_SIZE_MISMATCH 0x09 // 大小不匹配 + +// Flash参数 +#define FLASH_PAGE_SIZE 256 // Flash页大小 +// 改动原因:修正最大数据块大小,HID数据包63字节(不包括Report ID) +// 数据包格式:同步头(1) + 命令码(1) + 块序号(2) + 数据长度(1) + 数据(N) + Checksum(1) +// 所以最大数据长度 = 63 - 5(头部) - 1(Checksum) = 57字节 +#define MAX_DATA_BLOCK_SIZE 57 // 最大数据块大小(63字节HID数据包 - 5字节头部 - 1字节Checksum) +#define FLASH_MAX_UPGRADE_SIZE (2000 * 1024) // 最大固件大小1400KB + +// 页缓冲区结构 +typedef struct { + uint8_t buffer[FLASH_PAGE_SIZE]; // Flash页缓冲区 + uint16_t offset; // 当前偏移 + uint16_t total_pages; // 总页数 + uint16_t written_pages; // 已写入页数 +} page_buffer_t; + +// 升级状态结构 +typedef struct { + upgrade_state_t state; // 当前状态 + uint32_t firmware_size; // 固件总大小 + uint16_t total_blocks; // 总块数 + uint16_t received_blocks; // 已接收块数 + uint16_t expected_block_num; // 期望的块序号 + // 改动原因:去掉固件版本号字段,不再存储版本号 + uint8_t error_code; // 错误码 + page_buffer_t page_buf; // 页缓冲区 + uint8_t first_page_written; // 第一页写命令是否已执行(0=未执行,1=已执行) +} upgrade_status_t; + +// 固件升级主处理函数 +// 改动原因:处理HID固件升级命令,支持START、DATA、END等命令 +// 参数: +// - data: HID数据包(63字节,不包括Report ID) +// - len: 数据长度 +// 返回值:0=成功,非0=失败 +unsigned char process_firmware_upgrade_cmd(uint8_t data[], uint16_t len); + +/* 改动原因:声明 DFU channel 转发函数,供 eq.c (process_send_params) 调用, + * 将 HID 固件升级命令通过 streaming channel 转发到 uart_handler 处理。 + * 实现在 audiohw.xc 中。 */ +void dfu_chan_forward(unsigned char data[], unsigned len); + +// 各个固件升级命令的独立处理函数 +// 改动原因:在eq.c中为每个命令添加独立处理,支持HID一条一条指令发送 +// 参数: +// - data: HID数据包(63字节,不包括Report ID) +// - len: 数据长度 +// 返回值:0=成功,非0=失败(状态码) +unsigned char handle_firmware_upgrade_start(uint8_t data[], uint16_t len); +unsigned char handle_firmware_upgrade_data(uint8_t data[], uint16_t len); +unsigned char handle_firmware_upgrade_end(uint8_t data[], uint16_t len); +unsigned char handle_firmware_upgrade_status(uint8_t data[], uint16_t len); +unsigned char handle_firmware_upgrade_abort(uint8_t data[], uint16_t len); +unsigned char handle_firmware_upgrade_erase(uint8_t data[], uint16_t len); + +// 发送固件升级响应(主动上报) +// 改动原因:通过HID主动上报机制发送响应,与音量上报机制一致 +// 参数: +// - cmd: 命令码 +// - response_data: 响应数据 +// - data_len: 响应数据长度 +void send_firmware_upgrade_response(uint8_t cmd, uint8_t *response_data, uint16_t data_len); + +// 升级进行中标志(升级期间阻止其他HID主动上报) +extern unsigned g_in_fw_upgrade; + +// 初始化固件升级模块 +// 改动原因:初始化升级状态和缓冲区 +void firmware_upgrade_init(void); + +// 获取当前升级状态 +// 改动原因:供外部查询当前升级状态 +upgrade_state_t firmware_upgrade_get_state(void); + +// 重置升级状态 +// 改动原因:升级完成或中止后重置状态 +void firmware_upgrade_reset(void); + +// ====================== UART DFU 友好接口 ====================== +// 改动原因:为 UART 串口升级提供不依赖 g_hid_pass_data 的接口, +// 内部仍然复用 HID DFU 的状态机和 Flash 写入流程。 +// +// 这些函数只操作内部的 g_upgrade_status,并通过参数返回需要的字段, +// 方便 user_uart.xc 根据 UART 协议打包响应。 + +// UART 版 START:输入固件原始大小,返回对齐后的大小和总块数 +unsigned char uart_firmware_upgrade_start(uint32_t firmware_size, + uint32_t *aligned_size_out, + uint16_t *total_blocks_out); + +// UART 版 DATA:输入块号和数据,返回状态码和“确认的块号”(成功时等于块号,序号错误时为期望块号) +unsigned char uart_firmware_upgrade_data(uint16_t block_num, + uint8_t data_len, + const uint8_t *data, + uint16_t *confirmed_block_out); + +// UART 版 END:结束升级,写入最后一页并终止 Flash 写入 +unsigned char uart_firmware_upgrade_end(uint32_t firmware_size); + +// UART 版 STATUS:直接从 g_upgrade_status 中读取当前状态和进度 +unsigned char uart_firmware_upgrade_status(uint8_t *state_out, + uint16_t *received_blocks_out, + uint16_t *total_blocks_out, + uint16_t *written_pages_out, + uint16_t *total_pages_out, + uint32_t *bytes_transferred_out, + uint32_t *firmware_size_out, + uint8_t *error_code_out); + +// UART 版 ABORT:中止升级,可选择是否擦除已写入数据 +unsigned char uart_firmware_upgrade_abort(uint8_t erase_flag); + +// UART 版 ERASE:擦除现有 upgrade image 并执行 begin_write 初始化 +unsigned char uart_firmware_upgrade_erase(void); + +#endif // _DFU_UPGRADE_H_ diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.c b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.c index 3468196..fd6fed6 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.c +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.c @@ -9,6 +9,10 @@ extern void device_reboot(void); +#if HID_DFU_EN +#include "dfu_upgrade.h" +#endif + // 常量定义 #define EQ_DISABLED_MODE 10 // 禁用EQ的模式编号 @@ -805,6 +809,39 @@ unsigned char process_send_params(uint8_t data[], uint16_t len) { } #endif // #if EQ_EN (for process_send_params EQ commands section starting at line 1228) + +#if HID_DFU_EN + // 0xA7 START: 转发到button_task (tile[0])执行,因为flash访问需要在tile[0]进行 + if (data[1] == FIRMWARE_UPGRADE_START) { + dfu_chan_forward(data, len); + return true; + } + // 0xA8 DATA / 0xA9 END / 0xAA STATUS / 0xAB ABORT / 0xAC ERASE: 直接处理 + if (data[1] >= FIRMWARE_UPGRADE_DATA && data[1] <= FIRMWARE_UPGRADE_ERASE) { + unsigned char result; + if (data[1] == FIRMWARE_UPGRADE_DATA) + result = handle_firmware_upgrade_data(data, len); + else if (data[1] == FIRMWARE_UPGRADE_END) + result = handle_firmware_upgrade_end(data, len); + else if (data[1] == FIRMWARE_UPGRADE_STATUS) + result = handle_firmware_upgrade_status(data, len); + else if (data[1] == FIRMWARE_UPGRADE_ABORT) + result = handle_firmware_upgrade_abort(data, len); + else + result = handle_firmware_upgrade_erase(data, len); + (void)result; + return true; + } +#endif + + // 0xAE DEVICE_REBOOT (通用命令,不仅限于固件升级) + if (data[1] == 0xAE) { + debug_printf("Received device REBOOT command (0xAE)\n"); + device_reboot(); + while(1); + return true; + } + return true; } @@ -880,17 +917,6 @@ void init_mode_info(void) { #endif void get_key_ret(uint8_t *buffer); -extern unsigned char g_hid_status_report_data[63]; -extern unsigned int g_hid_status_report_index; -void user_read_hid_status(unsigned char hidPassData[]) -{ - if (g_hid_status_report_index > 0) { - memcpy(hidPassData, g_hid_status_report_data, 63); - g_hid_status_report_index = 0; - } else { - memset(hidPassData, 0, 63); - } -} // process_read_params: build HID response from pending request state unsigned char process_read_params(uint8_t response[]) { diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.py b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.py index e8330f1..565fae2 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.py +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.py @@ -2,7 +2,7 @@ from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QComboBox, QLabel, QScrollArea, QSplitter, QGroupBox, QFormLayout, QDoubleSpinBox, QCheckBox, QLineEdit, QSpinBox, - QMessageBox, QMenuBar, QMenu) + QMessageBox, QMenuBar, QMenu, QFileDialog, QProgressBar) from PySide6.QtCore import Qt, QObject, Signal, Slot, QTimer import numpy as np import matplotlib @@ -55,6 +55,14 @@ from filter_utils import (BiquadFilterCalculator, make_biquad_bypass, make_biqua MAX_EQ_BANDS = 8 # 最大EQ滤波器数量 (0-7) NUM_EQ_MODES = 12 # EQ模式数量(10预设 + 2用户) EQ_DISABLED_MODE = 12 # 禁用EQ的模式编号 +# HID 固件升级命令码(使用命令名代替裸命令码) +FIRMWARE_UPGRADE_START = 0xA7 # 开始固件升级 +FIRMWARE_UPGRADE_DATA = 0xA8 # 传输固件数据块 +FIRMWARE_UPGRADE_END = 0xA9 # 结束固件升级 +FIRMWARE_UPGRADE_STATUS = 0xAA # 获取升级状态 +FIRMWARE_UPGRADE_ABORT = 0xAB # 中止固件升级 +FIRMWARE_UPGRADE_ERASE = 0xAC # 擦除现有升级镜像 +DEVICE_REBOOT = 0xAE # 设备重启(通用命令) class BandFilter(QGroupBox): """单个滤波器带控件""" @@ -457,7 +465,7 @@ class EQDesigner(QMainWindow): sample_rate_layout = QHBoxLayout() sample_rate_layout.addWidget(QLabel("采样率:")) self.sample_rate = QComboBox() - self.sample_rate.addItems(["44100", "48000", "88200", "96000", "176400", "192000"]) + self.sample_rate.addItems(["44100"]) # 只保留44100Hz,其他采样率使用相同参数 self.sample_rate.currentTextChanged.connect(self.on_sample_rate_changed) sample_rate_layout.addWidget(self.sample_rate) left_content_layout.addLayout(sample_rate_layout) @@ -734,7 +742,68 @@ class EQDesigner(QMainWindow): left_content_layout.addWidget(eq_enable_group) - # 添加EX3D控制组 + + + # 添加固件升级控制组 + # 改动原因:添加HID固件升级功能,支持通过HID协议升级设备固件 + firmware_upgrade_group = QGroupBox("固件升级") + self.ui_groups['firmware'] = firmware_upgrade_group # 保存引用 + firmware_upgrade_layout = QFormLayout(firmware_upgrade_group) + + # 固件文件选择 + firmware_file_layout = QHBoxLayout() + self.firmware_file_edit = QLineEdit() + self.firmware_file_edit.setPlaceholderText("选择固件文件...") + firmware_file_layout.addWidget(self.firmware_file_edit) + browse_btn = QPushButton("浏览") + browse_btn.clicked.connect(self.on_browse_firmware) + firmware_file_layout.addWidget(browse_btn) + firmware_upgrade_layout.addRow("固件文件:", firmware_file_layout) + + # 升级进度条 + self.firmware_progress = QProgressBar() + self.firmware_progress.setRange(0, 100) + self.firmware_progress.setValue(0) + firmware_upgrade_layout.addRow("升级进度:", self.firmware_progress) + + # 状态标签 + self.firmware_status_label = QLabel("状态: 空闲") + firmware_upgrade_layout.addRow("当前状态:", self.firmware_status_label) + + # 固件升级按钮 + firmware_buttons_layout = QVBoxLayout() + + # 擦除upgrade image按钮 + self.erase_upgrade_btn = QPushButton("擦除现有升级镜像") + self.erase_upgrade_btn.clicked.connect(self.on_erase_upgrade_image) + firmware_buttons_layout.addWidget(self.erase_upgrade_btn) + + # 开始升级按钮 + self.start_upgrade_btn = QPushButton("开始固件升级") + self.start_upgrade_btn.clicked.connect(self.on_start_firmware_upgrade) + firmware_buttons_layout.addWidget(self.start_upgrade_btn) + + # 查询升级状态按钮 + self.status_upgrade_btn = QPushButton("查询升级状态") + self.status_upgrade_btn.clicked.connect(self.on_get_firmware_upgrade_status) + firmware_buttons_layout.addWidget(self.status_upgrade_btn) + + # 中止升级按钮 + self.abort_upgrade_btn = QPushButton("中止固件升级") + self.abort_upgrade_btn.clicked.connect(self.on_abort_firmware_upgrade) + firmware_buttons_layout.addWidget(self.abort_upgrade_btn) + + # 验证镜像按钮 + + # 重启设备按钮 + self.reboot_device_btn = QPushButton("重启设备") + self.reboot_device_btn.clicked.connect(self.on_reboot_device) + firmware_buttons_layout.addWidget(self.reboot_device_btn) + + firmware_upgrade_layout.addRow(firmware_buttons_layout) + + left_content_layout.addWidget(firmware_upgrade_group) + ex3d_group = QGroupBox("EX3D控制") self.ui_groups['ex3d'] = ex3d_group # 保存引用 ex3d_layout = QVBoxLayout(ex3d_group) @@ -880,7 +949,8 @@ class EQDesigner(QMainWindow): 'volume': '音量控制', 'device_mode': '音效模式控制', 'eq_enable': 'EQ使能控制', - 'ex3d': 'EX3D控制' + 'ex3d': 'EX3D控制', + 'firmware': '固件升级' } # 为每组创建切换可见性的动作 @@ -907,7 +977,8 @@ class EQDesigner(QMainWindow): 'volume': '音量控制', 'device_mode': '音效模式控制', 'eq_enable': 'EQ使能控制', - 'ex3d': 'EX3D控制' + 'ex3d': 'EX3D控制', + 'firmware': '固件升级' } name = group_names.get(group_key, group_key) self.view_actions[group_key].setText(f"{'显示' if visible else '隐藏'} {name}") @@ -1662,108 +1733,17 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ band.update_coefficients() def apply_to_all_sample_rates(self): - """将当前EQ参数应用到所有采样率""" + """将当前EQ参数应用到所有采样率 + 改动原因:现在所有采样率共用44100Hz参数,此函数只需保存44100Hz参数即可 + """ try: - # 获取当前模式 - current_mode = self.current_mode - - # 获取所有可用的采样率 - sample_rates = [float(self.sample_rate.itemText(i)) for i in range(self.sample_rate.count())] - - # 保存当前采样率 - current_fs = float(self.sample_rate.currentText()) - - # 遍历所有采样率 - for fs in sample_rates: - log_message(LOG_LEVEL_INFO, f"正在处理采样率: {fs}Hz", self.log_level) - - # 临时切换到目标采样率 - self.sample_rate.setCurrentText(str(int(fs))) - - # 计算总的bshift - total_bshift = sum(band.get_bshift() for band in self.bands) - - # 生成JSON文件名 - json_filename = f"eq_parameters_{int(fs)}_mode{current_mode}.json" - - # 读取现有的JSON文件(如果存在) - all_params = {} - if os.path.exists(json_filename): - with open(json_filename, "r", encoding='utf-8') as f: - all_params = json.load(f) - - # 更新当前mode的参数 - params = { - "sample_rate": fs, - "mode": current_mode, # 保存当前模式 - "total_bshift": total_bshift, - "post_gain_db": int(self.post_gain_spin.value()), - "bands": [] - } - - # 计算每个band的参数 - for band in self.bands: - band_params = band.get_parameters() - if band_params["enabled"]: - # 计算浮点系数 - coeffs = band.calculate_coefficients(fs) - if coeffs is not None: - # 反转 b1 和 b2 的符号 - coeffs = np.array([ - coeffs[0], # a0 - coeffs[1], # a1 - coeffs[2], # a2 - -coeffs[3], # b1 - -coeffs[4] # b2 - ]) - band_scale_factor = band.get_scale_factor() - # 只对 a0, a1, a2 乘以 scale_factor - coeffs_scaled = np.array([ - coeffs[0] * band_scale_factor, - coeffs[1] * band_scale_factor, - coeffs[2] * band_scale_factor, - coeffs[3], - coeffs[4] - ]) - coeffs_q30 = (coeffs_scaled * 2**30).astype(np.int32) - band_params["coefficients"] = { - "float": coeffs.tolist(), - "q30": [f"0x{int(x):08x}" if x >= 0 else f"-0x{int(-x):08x}" for x in coeffs_q30] - } - - params["bands"].append(band_params) - - # 保存当前mode的参数 - all_params[f"mode_{current_mode}"] = params - - # 保存JSON文件 - with open(json_filename, "w", encoding='utf-8') as f: - json.dump(all_params, f, indent=2, ensure_ascii=False) - - # 生成头文件 - self.save_parameters() # 这会自动生成头文件 - - log_message(LOG_LEVEL_INFO, f"已保存采样率 {fs}Hz 的参数", self.log_level) - - # 恢复原始采样率 - self.sample_rate.setCurrentText(str(int(current_fs))) - - log_message(LOG_LEVEL_INFO, "所有采样率的参数已保存完成", self.log_level) - - # 检查是否所有模式都已生成 - all_modes_generated = True - for mode in range(NUM_EQ_MODES): - mode_file = f"eq_params_{int(current_fs)}_mode{mode}.h" - if not os.path.exists(mode_file): - all_modes_generated = False - break - - # 如果所有模式都已生成,合并成一个完整的头文件 - if all_modes_generated: - self.merge_mode_files(current_fs) + # 改动原因:所有采样率共用44100Hz参数,直接保存当前参数即可 + log_message(LOG_LEVEL_INFO, "现在所有采样率共用44100Hz参数,直接保存当前参数", self.log_level) + self.save_parameters() # 直接保存当前参数(44100Hz) + log_message(LOG_LEVEL_INFO, "参数已保存(适用于所有采样率)", self.log_level) except Exception as e: - log_message(LOG_LEVEL_ERROR, f"应用到所有采样率时出错: {str(e)}", self.log_level) + log_message(LOG_LEVEL_ERROR, f"保存参数时出错: {str(e)}", self.log_level) import traceback log_message(LOG_LEVEL_ERROR, "详细错误信息:", self.log_level) traceback.print_exc() @@ -3497,6 +3477,671 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ except Exception as e: log_message(LOG_LEVEL_ERROR, f"读取固件版本时出错: {str(e)}", self.log_level) + def on_browse_firmware(self): + """浏览选择固件文件""" + # 改动原因:打开文件对话框选择固件文件 + filename, _ = QFileDialog.getOpenFileName( + self, + "选择固件文件", + "", + "Firmware Files (*.bin *.xe);;All Files (*.*)" + ) + if filename: + self.firmware_file_edit.setText(filename) + log_message(LOG_LEVEL_INFO, f"已选择固件文件: {filename}", self.log_level) + + def calculate_checksum(self, data): + """计算checksum(参考user_func.c实现)""" + # 改动原因:实现checksum算法,与设备端check_sum函数一致 + sum_value = 0 + for byte in data: + sum_value += byte + return sum_value % 256 + + def debug_print_hex(self, data, prefix="", max_bytes=64): + """以16进制形式打印数据包 + 改动原因:添加调试打印功能,方便查看发送的命令和收到的响应 + 参数: + data: 要打印的数据(bytes或bytearray) + prefix: 前缀字符串(如"发送"、"接收") + max_bytes: 最大打印字节数 + """ + if not data: + log_message(LOG_LEVEL_DEBUG, f"{prefix}: (空数据)", self.log_level) + return + + # 限制打印长度 + print_data = data[:max_bytes] if len(data) > max_bytes else data + + # 转换为16进制字符串 + hex_str = ' '.join([f'{b:02X}' for b in print_data]) + + # 如果数据被截断,添加提示 + if len(data) > max_bytes: + hex_str += f" ... (共{len(data)}字节,仅显示前{max_bytes}字节)" + + log_message(LOG_LEVEL_DEBUG, f"{prefix}: {hex_str}", self.log_level) + + def wait_for_hid_response(self, h, expected_cmd, timeout=180.0, max_retries=5000): + """等待设备主动上报的HID响应 + 改动原因:实现可靠的响应等待机制,确保每个命令发送后都等待设备响应后再继续 + 参数: + h: HID设备对象 + expected_cmd: 期望的命令码(如0xA7, 0xA8等) + timeout: 超时时间(秒) + max_retries: 最大重试次数 + 返回: + 响应数据包(64字节),如果超时返回None + """ + import time + start_time = time.time() + retry_count = 0 + + while retry_count < max_retries: + # 检查超时 + if time.time() - start_time > timeout: + log_message(LOG_LEVEL_ERROR, f"等待命令0x{expected_cmd:02x}响应超时({timeout}秒)", self.log_level) + return None + + # 改动原因:使用中断端点读取主动上报数据,避免HID_GET_REPORT返回非预期数据 + # 使用hid.read读取中断IN端点数据(长度64,单位ms超时) + reply = h.read(64, 50) + + if reply and len(reply) == 64: + # 改动原因:添加调试打印,显示所有收到的HID报告(用于调试) + if retry_count % 10 == 0: # 每10次重试打印一次,避免输出过多 + self.debug_print_hex(reply, f"[等待响应] 收到HID报告 (期望命令0x{expected_cmd:02x})", max_bytes=64) + + # 检查Report ID和同步头 + if reply[0] == 0x01 and reply[1] == 0x77: + # 检查命令码 + if reply[2] == expected_cmd: + log_message(LOG_LEVEL_DEBUG, f"收到命令0x{expected_cmd:02x}的响应", self.log_level) + return reply + else: + # 收到其他命令的响应,继续等待 + log_message(LOG_LEVEL_DEBUG, f"收到其他命令0x{reply[2]:02x}的响应,继续等待0x{expected_cmd:02x}", self.log_level) + + # 短暂延迟后重试 + time.sleep(0.05) # 50ms延迟 + retry_count += 1 + + log_message(LOG_LEVEL_ERROR, f"等待命令0x{expected_cmd:02x}响应失败,已重试{max_retries}次", self.log_level) + return None + + def on_erase_upgrade_image(self): + """擦除现有升级镜像(发送FIRMWARE_UPGRADE_ERASE命令)""" + # 改动原因:擦除Flash中的现有upgrade image,为新固件腾出空间 + if self.device_combo.currentData() is None: + log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level) + QMessageBox.warning(self, "错误", "请先选择设备") + return + + # 确认对话框 + reply = QMessageBox.question( + self, + "确认擦除", + "确定要擦除现有的升级镜像吗?\n\n" + "这将删除Flash中的upgrade image,\n" + "但不会影响当前运行的固件。\n\n" + "是否继续?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + + if reply != QMessageBox.Yes: + return + + try: + device_info = self.device_combo.currentData() + h = hid.device() + h.open(device_info['vendor_id'], device_info['product_id']) + h.set_nonblocking(1) + + # 构建FIRMWARE_UPGRADE_ERASE命令数据包 + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = FIRMWARE_UPGRADE_ERASE # 命令码 + + log_message(LOG_LEVEL_INFO, "正在擦除现有升级镜像...", self.log_level) + + # 改动原因:添加调试打印,显示发送的ERASE命令 + send_packet = [0x01] + list(data) + self.debug_print_hex(send_packet, f"[发送] ERASE命令 (FIRMWARE_UPGRADE_ERASE)", max_bytes=64) + + h.write(send_packet) + + # 改动原因:等待设备主动上报ERASE命令响应 + reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_ERASE, timeout=180.0) + + # 改动原因:添加调试打印,显示收到的ERASE响应 + if reply: + self.debug_print_hex(reply, f"[接收] ERASE响应 (FIRMWARE_UPGRADE_ERASE)", max_bytes=64) + if reply: + status = reply[3] + if status == 0x00: + log_message(LOG_LEVEL_INFO, "升级镜像擦除成功", self.log_level) + QMessageBox.information(self, "成功", "升级镜像已擦除") + else: + log_message(LOG_LEVEL_ERROR, f"升级镜像擦除失败,状态码: 0x{status:02x}", self.log_level) + QMessageBox.warning(self, "失败", f"升级镜像擦除失败\n状态码: 0x{status:02x}") + else: + log_message(LOG_LEVEL_ERROR, "ERASE命令响应超时", self.log_level) + QMessageBox.warning(self, "超时", "擦除命令响应超时") + + h.close() + + except Exception as e: + log_message(LOG_LEVEL_ERROR, f"擦除升级镜像时出错: {str(e)}", self.log_level) + QMessageBox.critical(self, "错误", f"擦除升级镜像时出错:\n{str(e)}") + + def on_start_firmware_upgrade(self): + """开始固件升级流程""" + # 改动原因:实现HID固件升级的完整流程,包括START、循环DATA、END命令 + if self.device_combo.currentData() is None: + log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level) + QMessageBox.warning(self, "错误", "请先选择设备") + return + + firmware_file = self.firmware_file_edit.text() + if not firmware_file or not os.path.exists(firmware_file): + log_message(LOG_LEVEL_ERROR, "请选择有效的固件文件", self.log_level) + QMessageBox.warning(self, "错误", "请选择有效的固件文件") + return + + # 确认对话框 + reply = QMessageBox.question( + self, + "确认升级", + f"确定要升级固件吗?\n\n" + f"固件文件: {firmware_file}\n" + f"警告:升级过程中请勿断电或拔出设备!\n\n" + f"是否继续?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + + if reply != QMessageBox.Yes: + return + + try: + import time + + # 读取固件文件 + with open(firmware_file, 'rb') as f: + firmware_data = f.read() + + file_size = len(firmware_data) + # 页对齐到256字节 + page_size = 256 + file_size_padded = ((file_size + page_size - 1) // page_size) * page_size + + log_message(LOG_LEVEL_INFO, f"固件文件大小: {file_size} 字节", self.log_level) + log_message(LOG_LEVEL_INFO, f"页对齐后大小: {file_size_padded} 字节", self.log_level) + + device_info = self.device_combo.currentData() + h = hid.device() + h.open(device_info['vendor_id'], device_info['product_id']) + h.set_nonblocking(1) + + # 步骤1: 发送START命令 (FIRMWARE_UPGRADE_START) + log_message(LOG_LEVEL_INFO, "发送START命令...", self.log_level) + self.firmware_status_label.setText("状态: 初始化中...") + QApplication.processEvents() + + # 改动原因:去掉固件版本号和升级标志位,所有实现都是强制升级方式 + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = FIRMWARE_UPGRADE_START # 命令码 + # 固件大小(4字节小端序) + data[2] = (file_size_padded >> 0) & 0xFF + data[3] = (file_size_padded >> 8) & 0xFF + data[4] = (file_size_padded >> 16) & 0xFF + data[5] = (file_size_padded >> 24) & 0xFF + # 其余字节为0(保留字节) + + # 改动原因:添加调试打印,显示发送的START命令 + send_packet = [0x01] + list(data) + self.debug_print_hex(send_packet, f"[发送] START命令 (FIRMWARE_UPGRADE_START)", max_bytes=64) + + h.write(send_packet) + + # 改动原因:等待设备主动上报START命令响应,确保收到响应后再继续 + log_message(LOG_LEVEL_DEBUG, "等待START命令响应...", self.log_level) + reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_START, timeout=180.0) + + # 改动原因:添加调试打印,显示收到的START响应 + if reply: + self.debug_print_hex(reply, f"[接收] START响应 (0xA7)", max_bytes=64) + if not reply: + raise Exception("START命令响应超时或无效") + + status = reply[3] + if status != 0x00: + error_msgs = { + 0x01: "初始化失败", + 0x02: "Flash空间不足", + 0x03: "固件大小无效" + } + raise Exception(f"START命令失败: {error_msgs.get(status, f'状态码0x{status:02x}')}") + + # 改动原因:修正总块数读取位置,根据协议响应格式: + # reply[0]=Report ID, reply[1]=0x77, reply[2]=FIRMWARE_UPGRADE_START, reply[3]=状态码 + # reply[4-7]=aligned_size(4字节), reply[8-9]=total_blocks(2字节) + total_blocks = reply[8] | (reply[9] << 8) + log_message(LOG_LEVEL_INFO, f"升级初始化成功,总块数: {total_blocks}", self.log_level) + + # 步骤2: 循环发送DATA命令 + # 改动原因:HID数据包只有63字节(不包括Report ID),需要为Checksum留出空间 + # 数据包格式:同步头(1) + 命令码(1) + 块序号(2) + 数据长度(1) + 数据(N) + Checksum(1) + # 所以最大数据长度 = 63 - 5(头部) - 1(Checksum) = 57字节 + max_data_block_size = 57 # 最大数据块大小(考虑Checksum空间) + block_number = 0 + sent_bytes = 0 + + self.firmware_status_label.setText("状态: 传输中...") + QApplication.processEvents() + + while sent_bytes < file_size_padded: + # 改动原因:数据长度固定为57字节(最后一个块可能不足57字节,用0填充) + remaining = file_size_padded - sent_bytes + # 数据长度固定为57字节 + data_len = max_data_block_size # 固定57字节 + + # 读取实际数据(如果还有数据的话) + if sent_bytes < file_size: + actual_read_len = min(data_len, file_size - sent_bytes) + block_data = firmware_data[sent_bytes:sent_bytes + actual_read_len] + # 如果实际读取的数据不足57字节,用0填充到57字节 + if actual_read_len < data_len: + block_data += bytes(data_len - actual_read_len) # 填充0 + else: + # 如果已经超过实际文件大小,全部填充0 + block_data = bytes(data_len) # 全部填充0 + + # 构建DATA命令数据包 + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = FIRMWARE_UPGRADE_DATA # 命令码 + data[2] = (block_number >> 0) & 0xFF # 块序号(小端序) + data[3] = (block_number >> 8) & 0xFF + data[4] = data_len # 数据长度 + data[5:5+data_len] = block_data # 固件数据 + + # 计算Checksum(同步头+命令码+块序号+数据长度+数据) + checksum_data = data[0:5+data_len] + checksum = self.calculate_checksum(checksum_data) + data[5+data_len] = checksum # Checksum位置:5 + data_len(最大62,不会越界) + + # 改动原因:添加调试打印,显示发送的DATA命令(每10个块打印一次,避免输出过多) + send_packet = [0x01] + list(data) + if block_number % 10 == 0 or block_number < 3: + self.debug_print_hex(send_packet, f"[发送] DATA命令 (FIRMWARE_UPGRADE_DATA) 块{block_number}", max_bytes=64) + + # 发送DATA命令 + h.write(send_packet) + + # 改动原因:等待设备主动上报DATA命令响应,确保收到响应后再发送下一个数据块 + reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_DATA, timeout=180.0) + + # 改动原因:添加调试打印,显示收到的DATA响应(每10个块打印一次) + if reply and (block_number % 10 == 0 or block_number < 3): + self.debug_print_hex(reply, f"[接收] DATA响应 (FIRMWARE_UPGRADE_DATA) 块{block_number}", max_bytes=64) + if not reply: + raise Exception(f"DATA命令响应超时(块序号: {block_number})") + + # 改动原因:响应格式已简化,只包含状态码和已确认的块序号,删除了已写入页数和总页数 + # 验证响应 + status = reply[3] + confirmed_block = reply[4] | (reply[5] << 8) + + if status != 0x00: + error_msgs = { + 0x01: "写入Flash失败", + 0x02: f"块序号错误(期望: {confirmed_block})", + 0x03: "Checksum校验失败", + 0x04: "数据长度错误(必须为57字节)" + } + raise Exception(f"DATA命令失败: {error_msgs.get(status, f'状态码0x{status:02x}')}") + + if confirmed_block != block_number: + raise Exception(f"块序号不匹配(发送: {block_number}, 确认: {confirmed_block})") + + log_message(LOG_LEVEL_DEBUG, f"块 {block_number} 传输成功", self.log_level) + + # 更新进度 + sent_bytes += data_len + block_number += 1 + progress = int((sent_bytes / file_size_padded) * 100) + self.firmware_progress.setValue(progress) + self.firmware_status_label.setText(f"状态: 传输中 ({block_number}/{total_blocks})") + QApplication.processEvents() + + if block_number % 10 == 0: + log_message(LOG_LEVEL_INFO, f"已发送 {block_number}/{total_blocks} 块", self.log_level) + + log_message(LOG_LEVEL_INFO, "数据传输完成", self.log_level) + + # 步骤3: 发送END命令 + self.firmware_status_label.setText("状态: 完成中...") + QApplication.processEvents() + + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = FIRMWARE_UPGRADE_END # 命令码 + # 固件总大小(4字节小端序) + data[2] = (file_size_padded >> 0) & 0xFF + data[3] = (file_size_padded >> 8) & 0xFF + data[4] = (file_size_padded >> 16) & 0xFF + data[5] = (file_size_padded >> 24) & 0xFF + + log_message(LOG_LEVEL_INFO, "发送END命令...", self.log_level) + + # 改动原因:添加调试打印,显示发送的END命令 + send_packet = [0x01] + list(data) + self.debug_print_hex(send_packet, f"[发送] END命令 (FIRMWARE_UPGRADE_END)", max_bytes=64) + + h.write(send_packet) + + # 改动原因:等待设备主动上报END命令响应,END命令需要更长时间(写入最后一页并验证) + log_message(LOG_LEVEL_DEBUG, "等待END命令响应(可能需要较长时间)...", self.log_level) + reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_END, timeout=180.0) # END命令需要更长时间 + + # 改动原因:添加调试打印,显示收到的END响应 + if reply: + self.debug_print_hex(reply, f"[接收] END响应 (FIRMWARE_UPGRADE_END)", max_bytes=64) + if not reply: + raise Exception("END命令响应超时或无效") + + # 改动原因:响应格式已简化,只包含状态码,删除了已写入总块数和总页数 + status = reply[3] + if status != 0x00: + error_msgs = { + 0x01: "写入失败", + 0x02: "数据不完整", + 0x03: "镜像验证失败", + 0x04: "版本号无效" + } + raise Exception(f"END命令失败: {error_msgs.get(status, f'状态码0x{status:02x}')}") + + log_message(LOG_LEVEL_INFO, "升级完成", self.log_level) + + h.close() + + # 更新UI + self.firmware_progress.setValue(100) + self.firmware_status_label.setText("状态: 升级完成") + + QMessageBox.information( + self, + "升级成功", + f"固件升级完成!\n\n" + f"请点击\"重启设备\"按钮重启设备以加载新固件。" + ) + + except Exception as e: + log_message(LOG_LEVEL_ERROR, f"固件升级失败: {str(e)}", self.log_level) + self.firmware_progress.setValue(0) + self.firmware_status_label.setText("状态: 升级失败") + QMessageBox.critical(self, "升级失败", f"固件升级失败:\n{str(e)}") + + def on_verify_firmware_image(self): + """验证固件镜像(发送0xAD命令)""" + # 改动原因:验证Flash中已写入的固件镜像完整性 + if self.device_combo.currentData() is None: + log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level) + QMessageBox.warning(self, "错误", "请先选择设备") + return + + try: + device_info = self.device_combo.currentData() + h = hid.device() + h.open(device_info['vendor_id'], device_info['product_id']) + h.set_nonblocking(1) + + # 构建0xAD命令数据包 + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = 0xAD # 命令码 (FIRMWARE_UPGRADE_VERIFY) + # 固件大小(0表示不校验大小) + data[2] = 0x00 + data[3] = 0x00 + data[4] = 0x00 + data[5] = 0x00 + + log_message(LOG_LEVEL_INFO, "正在验证固件镜像...", self.log_level) + + # 改动原因:添加调试打印,显示发送的VERIFY命令 + send_packet = [0x01] + list(data) + self.debug_print_hex(send_packet, f"[发送] VERIFY命令 (0xAD)", max_bytes=64) + + h.write(send_packet) + + # 改动原因:等待设备主动上报VERIFY命令响应 + reply = self.wait_for_hid_response(h, 0xAD, timeout=180.0) + + # 改动原因:添加调试打印,显示收到的VERIFY响应 + if reply: + self.debug_print_hex(reply, f"[接收] VERIFY响应 (0xAD)", max_bytes=64) + verify_result = reply[3] + image_size = reply[4] | (reply[5] << 8) | (reply[6] << 16) | (reply[7] << 24) + ver_major = reply[13] + ver_minor = reply[14] + ver_patch = reply[15] + + if verify_result == 0x00: + log_message(LOG_LEVEL_INFO, "固件镜像验证通过", self.log_level) + QMessageBox.information( + self, + "验证成功", + f"固件镜像验证通过!\n\n" + f"镜像大小: {image_size} 字节\n" + f"镜像版本: {ver_major}.{ver_minor}.{ver_patch}" + ) + else: + error_msgs = { + 0x01: "镜像验证失败", + 0x02: "版本号无效", + 0x03: "大小不匹配" + } + log_message(LOG_LEVEL_ERROR, f"固件镜像验证失败: {error_msgs.get(verify_result, f'状态码0x{verify_result:02x}')}", self.log_level) + QMessageBox.warning( + self, + "验证失败", + f"固件镜像验证失败\n\n{error_msgs.get(verify_result, f'状态码0x{verify_result:02x}')}" + ) + + h.close() + + except Exception as e: + log_message(LOG_LEVEL_ERROR, f"验证固件镜像时出错: {str(e)}", self.log_level) + QMessageBox.critical(self, "错误", f"验证固件镜像时出错:\n{str(e)}") + + def on_reboot_device(self): + """重启设备(发送DEVICE_REBOOT命令)""" + # 改动原因:发送重启命令,设备会重启并加载新的upgrade image + if self.device_combo.currentData() is None: + log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level) + QMessageBox.warning(self, "错误", "请先选择设备") + return + + # 确认对话框 + reply = QMessageBox.question( + self, + "确认重启", + "确定要重启设备吗?\n\n" + "设备将重启并加载新的固件\n" + "(如果upgrade image有效)。\n\n" + "是否继续?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + + if reply != QMessageBox.Yes: + return + + try: + device_info = self.device_combo.currentData() + h = hid.device() + h.open(device_info['vendor_id'], device_info['product_id']) + h.set_nonblocking(1) + + # 改动原因:将固件升级专用的REBOOT命令改为通用的设备重启命令(DEVICE_REBOOT) + # 构建DEVICE_REBOOT命令数据包(通用设备重启命令) + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = DEVICE_REBOOT # 命令码 + + log_message(LOG_LEVEL_INFO, "正在重启设备...", self.log_level) + + # 改动原因:添加调试打印,显示发送的DEVICE_REBOOT命令 + send_packet = [0x01] + list(data) + self.debug_print_hex(send_packet, f"[发送] DEVICE_REBOOT命令 (DEVICE_REBOOT)", max_bytes=64) + + h.write(send_packet) + + # 改动原因:DEVICE_REBOOT不需要返回,发送后设备立即重启 + # 注意:设备不会返回响应,主机不等待 + + h.close() + + QMessageBox.information( + self, + "重启设备", + "重启命令已发送\n\n" + "设备将在几秒内重启。\n" + "请等待设备重新连接。" + ) + + except Exception as e: + log_message(LOG_LEVEL_ERROR, f"重启设备时出错: {str(e)}", self.log_level) + QMessageBox.critical(self, "错误", f"重启设备时出错:\n{str(e)}") + + def on_get_firmware_upgrade_status(self): + """查询固件升级状态(发送FIRMWARE_UPGRADE_STATUS命令)""" + # 改动原因:支持查询升级状态,便于诊断升级进度和错误 + if self.device_combo.currentData() is None: + log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level) + QMessageBox.warning(self, "错误", "请先选择设备") + return + + try: + device_info = self.device_combo.currentData() + h = hid.device() + h.open(device_info['vendor_id'], device_info['product_id']) + h.set_nonblocking(1) + + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = FIRMWARE_UPGRADE_STATUS # 命令码 + + log_message(LOG_LEVEL_INFO, "正在查询升级状态...", self.log_level) + + send_packet = [0x01] + list(data) + self.debug_print_hex(send_packet, f"[发送] STATUS命令 (FIRMWARE_UPGRADE_STATUS)", max_bytes=64) + + h.write(send_packet) + + reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_STATUS, timeout=180.0) + if reply: + self.debug_print_hex(reply, f"[接收] STATUS响应 (FIRMWARE_UPGRADE_STATUS)", max_bytes=64) + if not reply: + raise Exception("STATUS命令响应超时或无效") + + # 响应解析(Report ID + 同步头 + 命令码 + payload) + state = reply[3] + received_blocks = reply[4] | (reply[5] << 8) + total_blocks = reply[6] | (reply[7] << 8) + written_pages = reply[8] | (reply[9] << 8) + total_pages = reply[10] | (reply[11] << 8) + bytes_transferred = reply[12] | (reply[13] << 8) | (reply[14] << 16) | (reply[15] << 24) + total_bytes = reply[16] | (reply[17] << 8) | (reply[18] << 16) | (reply[19] << 24) + error_code = reply[20] + + state_map = { + 0x00: "IDLE", + 0x01: "PREPARING", + 0x02: "TRANSFERRING", + 0x03: "COMPLETING", + 0x04: "COMPLETED", + 0x05: "ERROR", + 0x06: "ABORTED" + } + + msg = ( + f"状态: {state_map.get(state, f'0x{state:02x}')}\n" + f"块进度: {received_blocks}/{total_blocks}\n" + f"页进度: {written_pages}/{total_pages}\n" + f"字节进度: {bytes_transferred}/{total_bytes}\n" + f"错误码: 0x{error_code:02x}" + ) + log_message(LOG_LEVEL_INFO, f"升级状态: {msg}", self.log_level) + QMessageBox.information(self, "升级状态", msg) + + h.close() + + except Exception as e: + log_message(LOG_LEVEL_ERROR, f"查询升级状态时出错: {str(e)}", self.log_level) + QMessageBox.critical(self, "错误", f"查询升级状态时出错:\n{str(e)}") + + def on_abort_firmware_upgrade(self): + """中止固件升级(发送FIRMWARE_UPGRADE_ABORT命令)""" + # 改动原因:支持中止升级流程,并可选择是否擦除已写入数据 + if self.device_combo.currentData() is None: + log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level) + QMessageBox.warning(self, "错误", "请先选择设备") + return + + erase_reply = QMessageBox.question( + self, + "中止升级", + "是否擦除已写入的数据?\n\n" + "选择“是”:擦除已写入数据\n" + "选择“否”:保留已写入数据", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + erase_flag = 0x01 if erase_reply == QMessageBox.Yes else 0x00 + + try: + device_info = self.device_combo.currentData() + h = hid.device() + h.open(device_info['vendor_id'], device_info['product_id']) + h.set_nonblocking(1) + + data = bytearray(63) + data[0] = 0x77 # 同步头 + data[1] = FIRMWARE_UPGRADE_ABORT # 命令码 + data[2] = erase_flag # 清理标志 + + log_message(LOG_LEVEL_INFO, "正在中止升级...", self.log_level) + + send_packet = [0x01] + list(data) + self.debug_print_hex(send_packet, f"[发送] ABORT命令 (FIRMWARE_UPGRADE_ABORT)", max_bytes=64) + + h.write(send_packet) + + reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_ABORT, timeout=180.0) + if reply: + self.debug_print_hex(reply, f"[接收] ABORT响应 (FIRMWARE_UPGRADE_ABORT)", max_bytes=64) + if not reply: + raise Exception("ABORT命令响应超时或无效") + + status = reply[3] + if status == 0x00: + log_message(LOG_LEVEL_INFO, "升级已中止", self.log_level) + QMessageBox.information(self, "成功", "升级已中止") + else: + log_message(LOG_LEVEL_ERROR, f"中止升级失败,状态码: 0x{status:02x}", self.log_level) + QMessageBox.warning(self, "失败", f"中止升级失败\n状态码: 0x{status:02x}") + + h.close() + + except Exception as e: + log_message(LOG_LEVEL_ERROR, f"中止升级时出错: {str(e)}", self.log_level) + QMessageBox.critical(self, "错误", f"中止升级时出错:\n{str(e)}") + + def on_send_ex3d_cmd(self): """发送EX3D SET命令(0xB0命令)""" if self.device_combo.currentData() is None: @@ -3507,7 +4152,7 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ # 获取命令码和类型 base_cmd = self.ex3d_cmd_combo.currentData() cmd_type = self.ex3d_cmd_type_combo.currentData() - + # 构建EX3D命令码:SET命令清除第9位,GET命令设置第9位 if cmd_type == "SET": ex3d_cmd = base_cmd & ~0x0100 # 清除第9位 @@ -3537,19 +4182,19 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ param1 = self.ex3d_param1.value() param2 = self.ex3d_param2.value() param3 = self.ex3d_param3.value() - + # 参数1 (4字节,小端序) data[6] = (param1 >> 0) & 0xFF data[7] = (param1 >> 8) & 0xFF data[8] = (param1 >> 16) & 0xFF data[9] = (param1 >> 24) & 0xFF - + # 参数2 (4字节,小端序) data[10] = (param2 >> 0) & 0xFF data[11] = (param2 >> 8) & 0xFF data[12] = (param2 >> 16) & 0xFF data[13] = (param2 >> 24) & 0xFF - + # 参数3 (4字节,小端序) data[14] = (param3 >> 0) & 0xFF data[15] = (param3 >> 8) & 0xFF @@ -3612,7 +4257,7 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ # 获取命令码 base_cmd = self.ex3d_cmd_combo.currentData() cmd_type = self.ex3d_cmd_type_combo.currentData() - + # 构建EX3D命令码:GET命令设置第9位 if cmd_type == "GET": ex3d_cmd = base_cmd | 0x0100 # 设置第9位 @@ -3638,7 +4283,7 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ # 参数(某些GET命令需要参数,如通道号、索引等) param1 = self.ex3d_param1.value() - + # 参数1 (4字节,小端序) data[6] = (param1 >> 0) & 0xFF data[7] = (param1 >> 8) & 0xFF @@ -3658,7 +4303,7 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xB1: # 解析回显的命令码(位置3-6,4字节小端序) echo_cmd = reply[3] | (reply[4] << 8) | (reply[5] << 16) | (reply[6] << 24) - + # 解析返回值(从reply[7]开始,最多14个uint32值) return_values = [] for i in range(0, 56, 4): # 最多14个uint32值 @@ -3670,12 +4315,12 @@ eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{ break log_message(LOG_LEVEL_INFO, f"EX3D GET命令响应: 0x{echo_cmd:04X}, 返回值: {return_values}", self.log_level) - + # 格式化返回值显示 values_str = ", ".join([f"0x{val:08X}" for val in return_values[:4]]) # 只显示前4个值 if len(return_values) > 4: values_str += f" ... (共{len(return_values)}个值)" - + QMessageBox.information( self, "EX3D GET命令响应", diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_hid_protocol.md b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_hid_protocol.md index a6eb9c7..cbd2b8d 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_hid_protocol.md +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_hid_protocol.md @@ -789,3 +789,133 @@ 4. **错误恢复**: 通信失败时需要重新建立连接并重试 5. **参数验证**: 设备端会验证参数范围,超出范围的值会被限制 +--- + +## 8. 固件升级命令 (0xA7–0xAE) + +本节定义通过HID进行固件升级的命令。最大数据块57字节,Flash页256字节,字节序小端序。 + +### 8.1 固件升级命令列表 + +| 指令码 | 命令名称 | 功能 | 方向 | +|--------|----------|------|------| +| 0xA7 | FIRMWARE_UPGRADE_START | 开始固件升级 | 主机→设备 | +| 0xA8 | FIRMWARE_UPGRADE_DATA | 传输固件数据块(固定57字节) | 主机→设备 | +| 0xA9 | FIRMWARE_UPGRADE_END | 结束固件升级,验证镜像 | 主机→设备 | +| 0xAA | FIRMWARE_UPGRADE_STATUS | 获取升级状态和进度 | 主机→设备 | +| 0xAB | FIRMWARE_UPGRADE_ABORT | 中止固件升级 | 主机→设备 | +| 0xAC | FIRMWARE_UPGRADE_ERASE | 擦除现有升级镜像 | 主机→设备 | +| 0xAE | DEVICE_REBOOT | 设备重启(通用命令) | 主机→设备 | + +### 8.2 0xA7 - FIRMWARE_UPGRADE_START + +**请求**: +``` +字节 0: 0x77 同步头 +字节 1: 0xA7 命令码 +字节 2-5: uint32 固件大小(字节,小端序) +字节 6-62: 0x00 保留 +``` + +**响应**(主动上报): +``` +字节 0: 0x77 +字节 1: 0xA7 +字节 2: 状态码 (0x00=成功, 0x01=失败, 0x03=大小无效) +字节 3-6: uint32 页对齐后的实际大小 +字节 7-8: uint16 总块数 +字节 9-62: 0x00 +``` + +### 8.3 0xA8 - FIRMWARE_UPGRADE_DATA + +**请求**(数据块固定57字节,不足补0): +``` +字节 0: 0x77 +字节 1: 0xA8 +字节 2-3: uint16 块序号(从0开始,小端序) +字节 4: uint8 数据长度(固定57) +字节 5-61: uint8[57] 固件数据 +字节 62: uint8 Checksum(字节0-61累加 mod 256) +``` + +**响应**(主动上报): +``` +字节 0: 0x77 +字节 1: 0xA8 +字节 2: 状态码 (0x00=成功, 0x04=块号错误, 0x05=Checksum错误) +字节 3-4: uint16 已确认块序号 +字节 5-62: 0x00 +``` + +### 8.4 0xA9 - FIRMWARE_UPGRADE_END + +**请求**: +``` +字节 0: 0x77 +字节 1: 0xA9 +字节 2-5: uint32 固件总大小(用于验证) +字节 6-62: 0x00 +``` + +**响应**(主动上报): +``` +字节 0: 0x77 +字节 1: 0xA9 +字节 2: 状态码 (0x00=成功, 0x01=失败) +字节 3-62: 0x00 +``` + +### 8.5 0xAA - FIRMWARE_UPGRADE_STATUS + +**请求**: 字节0=0x77, 字节1=0xAA, 其余0x00 + +**响应**(主动上报): +``` +字节 0: 0x77 +字节 1: 0xAA +字节 2: 升级状态 (0=空闲, 1=准备中, 2=传输中, 3=完成中, 4=已完成, 5=错误, 6=已中止) +字节 3-4: uint16 已接收块数 +字节 5-6: uint16 总块数 +字节 7-8: uint16 已写页数 +字节 9-10: uint16 总页数 +字节 11-14: uint32 已传输字节数 +字节 15-18: uint32 总字节数 +字节 19: 错误码 +字节 20-62: 0x00 +``` + +### 8.6 0xAB - FIRMWARE_UPGRADE_ABORT + +**请求**: +``` +字节 0: 0x77 +字节 1: 0xAB +字节 2: 清理标志 (0x00=保留数据, 0x01=擦除数据) +字节 3-62: 0x00 +``` + +**响应**(主动上报): 字节0=0x77, 字节1=0xAB, 字节2=状态码(0x00=成功) + +### 8.7 0xAC - FIRMWARE_UPGRADE_ERASE + +**请求**: 字节0=0x77, 字节1=0xAC, 其余0x00 + +**响应**(主动上报): 字节0=0x77, 字节1=0xAC, 字节2=状态码(0x00=成功) + +### 8.8 0xAE - DEVICE_REBOOT + +**请求**: 字节0=0x77, 字节1=0xAE, 其余0x00 + +**响应**: 无响应,设备立即重启。 + +### 8.9 标准升级流程 + +``` +1. (可选) ERASE — 擦除现有upgrade image +2. START — 设置固件大小,等待响应(SUCCESS) +3. 循环 DATA — 逐块发送,每块等待响应确认 +4. END — 完成写入,等待响应(SUCCESS) +5. REBOOT — 重启加载新固件 +``` + diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/main.xc b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/main.xc index 96029e3..a54fffa 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/main.xc +++ b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/main.xc @@ -431,13 +431,20 @@ extern int dsp_worker_tile(chanend c_dsp_to_ex3d, int worker_id); //extern int dsp_worker_tile_1(chanend c_dsp_to_ex3d, int worker_id); extern void ex3d_task(); extern void hid_button_task(chanend cc_mic_level, chanend c_hid, chanend c_hidSendData, chanend c_uac_vol, chanend c_ex3d_hid_cmd); +#if HID_DFU_EN +extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx, streaming chanend c_dfu_rx); +#else extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx); +#endif extern void dnr_dsp_proc_task(void); extern unsafe chanend uc_dsp_to_ex3d[DSP_WORKER_COUNT]; extern unsafe chanend uc_dsp_to_dnr_t1; extern unsafe chanend uc_key_to_ubm_t0; extern unsafe chanend uc_audiohw; +#if HID_DFU_EN +extern unsafe streaming chanend uc_dfu; +#endif extern void key_sender(chanend c_key); extern void key_receiver(chanend c_key); @@ -535,6 +542,9 @@ int main() chan c_dsp_to_ex3d[DSP_WORKER_COUNT]; chan cc_mic_level; chan c_audiohw; +#if HID_DFU_EN + streaming chan c_dfu; +#endif chan c_key; chan c_hidSendData; chan c_hidRcvData; chan c_eq_data; @@ -572,13 +582,24 @@ int main() #if USE_EX3D == 1 unsafe { key_receiver(c_key); } #endif - AudioHwRemote(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw); + AudioHwRemote(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw +#if HID_DFU_EN + , c_dfu +#endif + ); } } } #if EQ_EN == 1 on tile[0] : { { + +#if HID_DFU_EN + unsafe { + uc_dfu = (streaming chanend)c_dfu; + } +#endif + dsp_core0(); } }