feat: verify assembly bootloader factory recovery

This commit is contained in:
十七
2026-11-05 21:17:16 +08:00
parent 72393b6ef7
commit 12f6bb62f2
7 changed files with 892 additions and 38 deletions
+60 -34
View File
@@ -50,11 +50,18 @@ int _Asm_TIM4Vector
; 因此从0x8501开始读取三字节入口地址,避免写死主程序启动地址。
_Asm_Reset:
sim
; STM8复位后的默认HSI时钟为16MHz/8,约2MHz。
; Bootloader和应用程序均使用16MHz系统时钟。
ldw x, #0x03FF
ldw sp, x
mov 0x50C6, #0x00
call _Asm_SPI_Init
call _Asm_SPI_CheckJedec
; 外部Flash故障时不阻止已有主程序运行。
tnz a
jrne _Asm_JumpApplication
jreq _Asm_SpiOK
jpf _Asm_JumpApplication
_Asm_SpiOK:
; PA3配置为带上拉输入,按键按下时读取为低电平。
mov 0x5002, #0x00
mov 0x5003, #0x08
@@ -84,7 +91,9 @@ _Asm_WaitOneMs:
jrne _Asm_KeyWaitLoop
; 长按已超过5秒,优先执行出厂固件恢复。
call _Asm_CheckFactoryMetadata
jrne _Asm_NormalBoot
jreq _Asm_FactoryKeyMetaOK
jpf _Asm_NormalBoot
_Asm_FactoryKeyMetaOK:
jpf _Asm_FactoryRecovery
_Asm_NormalBoot:
@@ -223,21 +232,21 @@ _Asm_CheckFactoryMetadata:
; 读取4字节固件长度,只接受不超过应用区的长度。
ldw x, #0x2004
call _Asm_SPI_ReadByteX
; 长度为高字节在前:前两个字节必须为0。
tnz a
jrne _Asm_FactoryMetadataInvalid
incw x
call _Asm_SPI_ReadByteX
tnz a
jrne _Asm_FactoryMetadataInvalid
incw x
call _Asm_SPI_ReadByteX
ld 0x0000, a
ld 0x000E, a
incw x
call _Asm_SPI_ReadByteX
ld 0x0001, a
ld 0x000F, a
incw x
call _Asm_SPI_ReadByteX
; 当前应用区小于64KB,高16位必须为0。
tnz a
jrne _Asm_FactoryMetadataInvalid
incw x
call _Asm_SPI_ReadByteX
tnz a
jrne _Asm_FactoryMetadataInvalid
; 长度不能为0,也不能超过0x1800字节的应用空间。
ld a, 0x0000
or a, 0x0001
@@ -287,20 +296,21 @@ _Asm_CheckOtaMetadata:
jrne _Asm_OtaMetadataInvalid
ldw x, #0x5004
call _Asm_SPI_ReadByteX
; OTA长度同样为4字节高字节在前,前两个字节必须为0。
tnz a
jrne _Asm_OtaMetadataInvalid
incw x
call _Asm_SPI_ReadByteX
tnz a
jrne _Asm_OtaMetadataInvalid
incw x
call _Asm_SPI_ReadByteX
ld 0x0000, a
ld 0x000E, a
incw x
call _Asm_SPI_ReadByteX
ld 0x0001, a
ld 0x000F, a
incw x
call _Asm_SPI_ReadByteX
tnz a
jrne _Asm_OtaMetadataInvalid
incw x
call _Asm_SPI_ReadByteX
tnz a
jrne _Asm_OtaMetadataInvalid
ld a, 0x0000
or a, 0x0001
jreq _Asm_OtaMetadataInvalid
@@ -460,7 +470,9 @@ _Asm_CrcBitLoop:
srl 0x000B
rrc 0x000A
ld a, 0x000A
xor a, #0xA1
; CRC-16/MODBUS反射算法多项式为0xA001,拆成寄存器操作时:
; 高字节异或0xA0,低字节异或0x01。
xor a, #0x01
ld 0x000A, a
ld a, 0x000B
xor a, #0xA0
@@ -493,7 +505,8 @@ _Asm_ValidateFactoryImage:
ld a, #0xFF
ld 0x000A, a
ld 0x000B, a
ldw x, 0x0004
; 出厂固件镜像从外部Flash 0x000000开始,不能跳过前4字节向量表。
ldw x, #0x0000
ldw y, 0x0000
_Asm_ValidateLoop:
ld a, 0x0000
@@ -518,26 +531,32 @@ _Asm_ValidateFail:
ret
;-------------------------------------------------------------------------------
; 擦除内部应用区0x8500~0x9FFF,每次擦除一个64字节块。
; 擦除内部应用区0x8500~0x9FFF。
; 这里采用STM8官方支持的字节擦除方式:逐地址写入0x00并等待EOP。
; 不使用块擦除模式,避免块擦除关键步骤必须从RAM执行的问题。
;-------------------------------------------------------------------------------
_Asm_EraseApplication:
ld a, #0x56
ld 0x5062, a
ld a, #0xAE
ld 0x5062, a
ldw x, #0x8500
_Asm_EraseBlock:
ld a, #0x20
; 退出块擦除/块编程模式,恢复普通字节擦除/编程方式。
ld a, #0x00
ld 0x505B, a
ld a, #0xDF
ld a, #0xFF
ld 0x505C, a
ldw x, #0x8500
_Asm_EraseByte:
; 字节擦除由向目标地址写入0x00触发;写指令后CPU会等待本次操作完成。
ld a, #0x00
ld (x), a
_Asm_EraseWait:
; 本项目STM8S003寄存器定义中,FLASH_IAPSR.EOP为bit2(掩码0x04)。
; 置1表示当前擦除操作已完成。
btjf 0x505F, #2, _Asm_EraseWait
addw x, #64
incw x
cpw x, #0xA000
jrne _Asm_EraseBlock
jrne _Asm_EraseByte
ld a, #0x00
ret
@@ -549,7 +568,15 @@ _Asm_CopyFactoryImage:
ld 0x0000, a
ld a, 0x000F
ld 0x0001, a
ldw x, 0x0004
; 搬运阶段采用STM8的单字节编程方式:
; 只向目标地址执行一次写操作,不设置块编程CR2/NCR2模式。
; 这样每个字节写入后都可以单独等待EOP完成。
ld a, #0x00
ld 0x505B, a
ld a, #0xFF
ld 0x505C, a
; 搬运必须从出厂镜像第0字节开始,保持应用向量表与0x8500对齐。
ldw x, #0x0000
ldw y, #0x8500
_Asm_CopyLoop:
ld a, 0x0000
@@ -557,13 +584,11 @@ _Asm_CopyLoop:
jreq _Asm_CopyOK
call _Asm_SPI_ReadByteX
ld 0x000C, a
ld a, #0x01
ld 0x505B, a
ld a, #0xFE
ld 0x505C, a
ld a, 0x000C
ld (y), a
_Asm_CopyWait:
; 本项目STM8S003寄存器定义中,FLASH_IAPSR.EOP为bit2(掩码0x04)。
; 置1表示当前字节编程已完成。
btjf 0x505F, #2, _Asm_CopyWait
incw x
incw y
@@ -582,7 +607,8 @@ _Asm_VerifyApplication:
ld 0x0000, a
ld a, 0x000F
ld 0x0001, a
ldw x, 0x0004
; 回读校验与写入过程使用同一个外部Flash起始地址0x000000。
ldw x, #0x0000
ldw y, #0x8500
_Asm_VerifyLoop:
ld a, 0x0000