From fa5aa85bee6899927edf7fd502ae1fd42fbb8b88 Mon Sep 17 00:00:00 2001 From: yuquanjun Date: Sat, 26 Sep 2026 12:42:29 +0800 Subject: [PATCH] fix(usb): enter the bootloader after USB update confirm without key 4 Keep the NVM sector out of the application image and arm a backup register that survives reset, so confirm stays in flash mode even when round key 4 is not held. Co-authored-by: Cursor --- AT32F403ARCT7_BOOT/project/src/main.c | 19 +++++++---- UI/UI_UsbModeConfirm.c | 6 +++- driver/drv_nvm.h | 49 ++++++++++++++++++++++----- project/IAR_V7.4/AT32F403AxC.icf | 3 +- project/IAR_V7.4/YNGJ-GT1-M-WTB.icf | 3 +- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/AT32F403ARCT7_BOOT/project/src/main.c b/AT32F403ARCT7_BOOT/project/src/main.c index 1276693..2ad1713 100644 --- a/AT32F403ARCT7_BOOT/project/src/main.c +++ b/AT32F403ARCT7_BOOT/project/src/main.c @@ -180,12 +180,14 @@ int main(void) TM1617_GPIO_Init(); - /* ????????????????????? App?? - * ???????????? KEY_MAIN_A ????? USB ???????? - * ??????????? KEY_NULL ???? App????��??????????????? Boot ?????? */ + /* 按住圆键 4(KEY_MAIN_A),或 App 确认了 USB 更新,都停在烧录。 + * USB 更新有两处标志:Flash 里的 UsbMode,以及复位后仍在的备份寄存器。 + * 没按键且两处都没有标志才跳进 App。 */ wk_delay_ms(50); TM1617KeyValue = TM1617_ScanFkey(); - if (TM1617KeyValue != KEY_MAIN_A && !Boot_NvmUsbUpdateRequested()) + if (TM1617KeyValue != KEY_MAIN_A && + !Boot_NvmUsbUpdateRequested() && + !drv_nvm_usb_boot_armed()) { JumpToApplication(); } @@ -222,11 +224,14 @@ int main(void) LCD_ALIGN_CENTER, WHITE, BLACK, 0, 24); } } - /* 画面出来后再清标志。擦写与 Boot 同在 Flash Bank1,期间关掉中断 */ - if (Boot_NvmUsbUpdateRequested()) + /* 画面出来后再清标志,避免还没进烧录就被下一次复位跳回 App。 + * 擦写与 Boot 同在 Flash Bank1,期间关掉中断。 */ + if (Boot_NvmUsbUpdateRequested() || drv_nvm_usb_boot_armed()) { __disable_irq(); - Boot_NvmClearUsbUpdate(); + if (Boot_NvmUsbUpdateRequested()) + Boot_NvmClearUsbUpdate(); + drv_nvm_clear_usb_boot_arm(); __enable_irq(); } diff --git a/UI/UI_UsbModeConfirm.c b/UI/UI_UsbModeConfirm.c index ef606ce..cb6ac62 100644 --- a/UI/UI_UsbModeConfirm.c +++ b/UI/UI_UsbModeConfirm.c @@ -62,8 +62,12 @@ void UI_UsbModeConfirm_Process(DisplayTaskMessage_Type msg) case GUI_CONFIRM: nvm = drv_nvm_param_ptr(); nvm->param.UsbMode = NVM_USB_UPDATE; - __disable_irq(); + /* 先记备份寄存器,再写 Flash。系统复位不清 BPR, + * Boot 不按圆键 4 也会留下烧录。关中断放在保存之后, + * 避免日志互斥在关中断后永远等不到超时。 */ + drv_nvm_arm_usb_boot(); drv_nvm_save_to_flash(); + __disable_irq(); nvic_system_reset(); break; default: diff --git a/driver/drv_nvm.h b/driver/drv_nvm.h index 74453c0..1cdca7a 100644 --- a/driver/drv_nvm.h +++ b/driver/drv_nvm.h @@ -11,21 +11,22 @@ #ifndef FLASH_BASE #define FLASH_BASE 0x08000000UL #endif -#ifndef SECTOR_SIZE -#define NVM_SECTOR_SZ 1024UL -#else -#define NVM_SECTOR_SZ ((uint32_t)SECTOR_SIZE) -#endif #define fwname "BRS08L\x00" #define fwdata __DATE__ #define fwtime __TIME__ -/* AT32F403A 扇区为 2KB。参数仍写在 256KB 末尾倒数第 2 个 1KB, - * 擦除必须对齐到扇区起点,否则 0->1(MIDI->更新)写不进去。 */ +/* 参数固定在 0x0803FC00(256KB 末尾倒数第 2 个 1KB)。 + * 地址不能跟 SECTOR_SIZE 走:App 与 Boot 对该宏不一致时,Boot 会读错, + * USB 更新标志对不上。AT32F403A 扇区是 2KB,擦除必须对齐扇区起点, + * 否则 0->1(MIDI->更新)写不进去。应用程序链接范围必须停在该扇区之前。 */ #define NVM_FLASH_SECTOR 2048UL -#define INNER_FLASH_SAVE_ADDRESS (FLASH_BASE + 0x00040000UL - NVM_SECTOR_SZ) +#define INNER_FLASH_SAVE_ADDRESS (FLASH_BASE + 0x00040000UL - 1024UL) #define INNER_FLASH_SECTOR_ADDRESS (INNER_FLASH_SAVE_ADDRESS & ~(NVM_FLASH_SECTOR - 1UL)) +/* 备份寄存器在系统复位后仍保留。确认 USB 更新时先写下这个值, + * Boot 即使没读到 Flash 里的 UsbMode,不按圆键 4 也会停在烧录模式。 */ +#define NVM_USB_BOOT_MAGIC 0xB007u + #pragma pack(push, 1) @@ -89,4 +90,36 @@ void drv_nvm_load_default_calibration_static_value(NvmParam_Type * pData, uint8_ uint8_t drv_nvm_get_param_value_by_id (uint8_t nvmId); +#include "at32f403a_407_bpr.h" +#include "at32f403a_407_crm.h" +#include "at32f403a_407_pwc.h" + +static inline void drv_nvm_bpr_open(void) +{ + crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE); + crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE); + pwc_battery_powered_domain_access(TRUE); + while (PWC->ctrl_bit.bpwen == FALSE) + { + } +} + +static inline void drv_nvm_arm_usb_boot(void) +{ + drv_nvm_bpr_open(); + BPR->dt1 = NVM_USB_BOOT_MAGIC; +} + +static inline uint8_t drv_nvm_usb_boot_armed(void) +{ + drv_nvm_bpr_open(); + return ((BPR->dt1 & 0xFFFFu) == NVM_USB_BOOT_MAGIC) ? 1u : 0u; +} + +static inline void drv_nvm_clear_usb_boot_arm(void) +{ + drv_nvm_bpr_open(); + BPR->dt1 = 0; +} + #endif // _DRV_NVM_H_ diff --git a/project/IAR_V7.4/AT32F403AxC.icf b/project/IAR_V7.4/AT32F403AxC.icf index 603d715..5b5c411 100644 --- a/project/IAR_V7.4/AT32F403AxC.icf +++ b/project/IAR_V7.4/AT32F403AxC.icf @@ -5,7 +5,8 @@ define symbol __ICFEDIT_intvec_start__ = 0x08008000; /*-Memory Regions-*/ define symbol __ICFEDIT_region_ROM_start__ = 0x08008000; -define symbol __ICFEDIT_region_ROM_end__ = 0x0803FFFF; +/* 0x0803F800-0x0803FFFF 是 NVM 扇区,留给 UsbMode。代码放进来会被参数擦除毁掉。 */ +define symbol __ICFEDIT_region_ROM_end__ = 0x0803F7FF; define symbol __ICFEDIT_region_RAM_start__ = 0x20000000; /* AT32F403ARCT7: 96KB SRAM (0x20000000-0x20017FFF). Do NOT use 224KB. */ define symbol __ICFEDIT_region_RAM_end__ = 0x20017FFF; diff --git a/project/IAR_V7.4/YNGJ-GT1-M-WTB.icf b/project/IAR_V7.4/YNGJ-GT1-M-WTB.icf index ae265cf..cbd576b 100644 --- a/project/IAR_V7.4/YNGJ-GT1-M-WTB.icf +++ b/project/IAR_V7.4/YNGJ-GT1-M-WTB.icf @@ -5,7 +5,8 @@ define symbol __ICFEDIT_intvec_start__ = 0x08008000; /*-Memory Regions-*/ define symbol __ICFEDIT_region_ROM_start__ = 0x08000000; -define symbol __ICFEDIT_region_ROM_end__ = 0x0803FFFF; +/* 0x0803F800-0x0803FFFF 是 NVM 扇区,留给 UsbMode。 */ +define symbol __ICFEDIT_region_ROM_end__ = 0x0803F7FF; define symbol __ICFEDIT_region_RAM_start__ = 0x20000000; define symbol __ICFEDIT_region_RAM_end__ = 0x20037FFF; define symbol __ICFEDIT_boot_start__ = 0x08000000;