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 <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-26 12:42:29 +08:00
parent 968f8ae1ec
commit fa5aa85bee
5 changed files with 62 additions and 18 deletions

View File

@ -180,12 +180,14 @@ int main(void)
TM1617_GPIO_Init(); TM1617_GPIO_Init();
/* ????????????????????? App?? /* 按住圆键 4(KEY_MAIN_A),或 App 确认了 USB 更新,都停在烧录。
* ???????????? KEY_MAIN_A ????? USB ???????? * USB 更新有两处标志:Flash 里的 UsbMode,以及复位后仍在的备份寄存器。
* ??????????? KEY_NULL ???? App????<EFBFBD><EFBFBD>??????????????? Boot ?????? */ * 没按键且两处都没有标志才跳进 App。 */
wk_delay_ms(50); wk_delay_ms(50);
TM1617KeyValue = TM1617_ScanFkey(); TM1617KeyValue = TM1617_ScanFkey();
if (TM1617KeyValue != KEY_MAIN_A && !Boot_NvmUsbUpdateRequested()) if (TM1617KeyValue != KEY_MAIN_A &&
!Boot_NvmUsbUpdateRequested() &&
!drv_nvm_usb_boot_armed())
{ {
JumpToApplication(); JumpToApplication();
} }
@ -222,11 +224,14 @@ int main(void)
LCD_ALIGN_CENTER, WHITE, BLACK, 0, 24); LCD_ALIGN_CENTER, WHITE, BLACK, 0, 24);
} }
} }
/* 画面出来后再清标志。擦写与 Boot 同在 Flash Bank1,期间关掉中断 */ /* 画面出来后再清标志,避免还没进烧录就被下一次复位跳回 App。
if (Boot_NvmUsbUpdateRequested()) * 擦写与 Boot 同在 Flash Bank1,期间关掉中断。 */
if (Boot_NvmUsbUpdateRequested() || drv_nvm_usb_boot_armed())
{ {
__disable_irq(); __disable_irq();
if (Boot_NvmUsbUpdateRequested())
Boot_NvmClearUsbUpdate(); Boot_NvmClearUsbUpdate();
drv_nvm_clear_usb_boot_arm();
__enable_irq(); __enable_irq();
} }

View File

@ -62,8 +62,12 @@ void UI_UsbModeConfirm_Process(DisplayTaskMessage_Type msg)
case GUI_CONFIRM: case GUI_CONFIRM:
nvm = drv_nvm_param_ptr(); nvm = drv_nvm_param_ptr();
nvm->param.UsbMode = NVM_USB_UPDATE; nvm->param.UsbMode = NVM_USB_UPDATE;
__disable_irq(); /* 先记备份寄存器,再写 Flash。系统复位不清 BPR,
* Boot 不按圆键 4 也会留下烧录。关中断放在保存之后,
* 避免日志互斥在关中断后永远等不到超时。 */
drv_nvm_arm_usb_boot();
drv_nvm_save_to_flash(); drv_nvm_save_to_flash();
__disable_irq();
nvic_system_reset(); nvic_system_reset();
break; break;
default: default:

View File

@ -11,21 +11,22 @@
#ifndef FLASH_BASE #ifndef FLASH_BASE
#define FLASH_BASE 0x08000000UL #define FLASH_BASE 0x08000000UL
#endif #endif
#ifndef SECTOR_SIZE
#define NVM_SECTOR_SZ 1024UL
#else
#define NVM_SECTOR_SZ ((uint32_t)SECTOR_SIZE)
#endif
#define fwname "BRS08L\x00" #define fwname "BRS08L\x00"
#define fwdata __DATE__ #define fwdata __DATE__
#define fwtime __TIME__ #define fwtime __TIME__
/* AT32F403A 扇区为 2KB。参数仍写在 256KB 末尾倒数第 2 个 1KB, /* 参数固定在 0x0803FC00(256KB 末尾倒数第 2 个 1KB)。
* 擦除必须对齐到扇区起点,否则 0->1(MIDI->更新)写不进去。 */ * 地址不能跟 SECTOR_SIZE 走:App 与 Boot 对该宏不一致时,Boot 会读错,
* USB 更新标志对不上。AT32F403A 扇区是 2KB,擦除必须对齐扇区起点,
* 否则 0->1(MIDI->更新)写不进去。应用程序链接范围必须停在该扇区之前。 */
#define NVM_FLASH_SECTOR 2048UL #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)) #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) #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); 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_ #endif // _DRV_NVM_H_

View File

@ -5,7 +5,8 @@
define symbol __ICFEDIT_intvec_start__ = 0x08008000; define symbol __ICFEDIT_intvec_start__ = 0x08008000;
/*-Memory Regions-*/ /*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x08008000; 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; define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
/* AT32F403ARCT7: 96KB SRAM (0x20000000-0x20017FFF). Do NOT use 224KB. */ /* AT32F403ARCT7: 96KB SRAM (0x20000000-0x20017FFF). Do NOT use 224KB. */
define symbol __ICFEDIT_region_RAM_end__ = 0x20017FFF; define symbol __ICFEDIT_region_RAM_end__ = 0x20017FFF;

View File

@ -5,7 +5,8 @@
define symbol __ICFEDIT_intvec_start__ = 0x08008000; define symbol __ICFEDIT_intvec_start__ = 0x08008000;
/*-Memory Regions-*/ /*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x08000000; 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_start__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x20037FFF; define symbol __ICFEDIT_region_RAM_end__ = 0x20037FFF;
define symbol __ICFEDIT_boot_start__ = 0x08000000; define symbol __ICFEDIT_boot_start__ = 0x08000000;