backup codes
This commit is contained in:
2
Makefile
2
Makefile
@@ -40,7 +40,7 @@ CFLAGS += --stack-auto --noinduction --use-non-free
|
|||||||
## Disable lospre (workaround for bug 2673)
|
## Disable lospre (workaround for bug 2673)
|
||||||
#CFLAGS += --nolospre
|
#CFLAGS += --nolospre
|
||||||
LDFLAGS = -m$(ARCH) -l$(ARCH) --out-fmt-ihx
|
LDFLAGS = -m$(ARCH) -l$(ARCH) --out-fmt-ihx
|
||||||
OPTFLAGS = -Wl-bOPTION=0x4800 -Wl-bOPTION_BOOT=0x4812
|
OPTFLAGS = -Wl-bOPTION=0x4800 -Wl-bOPTION_BOOT=0x480D
|
||||||
|
|
||||||
# Conditionally add ENABLE_OPTION_BOOTLOADER macro
|
# Conditionally add ENABLE_OPTION_BOOTLOADER macro
|
||||||
ifneq ($(ENABLE_OPTION_BOOTLOADER),0)
|
ifneq ($(ENABLE_OPTION_BOOTLOADER),0)
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ __asm
|
|||||||
addw X, #3 ; [1C 00 03]
|
addw X, #3 ; [1C 00 03]
|
||||||
cpw X, (1,SP) ; [13 01]
|
cpw X, (1,SP) ; [13 01]
|
||||||
jrne _exit ; [26 01]
|
jrne _exit ; [26 01]
|
||||||
|
// save SP to 0x0000
|
||||||
|
ldw 0x0000, Y
|
||||||
// jump to ram
|
// jump to ram
|
||||||
ret ; [81]
|
ret ; [81]
|
||||||
_exit:
|
_exit:
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
;; M Code
|
;; M Code
|
||||||
;; option O0 O1 N1 O2 N2 O3 N3 O4 N4 O5 N5
|
;; option O0 O1 N1 O2 N2 O3 N3 O4 N4 O5 N5
|
||||||
;; 4800 00 00 ff 00 ff 00 ff 00 ff 00 ff -- -- -- -- --
|
;; 4800 00 00 ff 00 ff 00 ff 00 ff 00 ff -- -- 00 03 d0
|
||||||
;; 03D0|4810 -- -- 00 03 d5 a6 0d c7 52 32 c7 52 35 c7 52 31
|
;; 03D0|4810 a6 0d c7 52 32 c7 52 35 c7 52 31 a6 80 5f 5c 27
|
||||||
;; 03E0|4820 a6 80 5f 5c 27 14 72 0b 52 30 f8 3b 52 31 4c 26
|
;; 03E0|4820 17 72 0b 52 30 f8 3b 52 31 4c 26 f1 c7 52 31 96
|
||||||
;; 03F0|4830 f1 96 5c 5c 5c 13 01 26 e7 81 72 cc 48 3e 80 04
|
;; 03F0|4830 5c 5c 5c 13 01 26 e4 81 fe 94 72 cc 48 3e 80 04
|
||||||
|
|
||||||
;; UART1 register address definitions
|
;; UART1 register address definitions
|
||||||
UART1_SR = 0x5230 ; Status register
|
UART1_SR = 0x5230 ; Status register
|
||||||
@@ -20,7 +20,7 @@ _boot_start_data:
|
|||||||
;; Termination flag (copy process stops when encountering 0)
|
;; Termination flag (copy process stops when encountering 0)
|
||||||
.db 0x00 ; [00] Termination byte
|
.db 0x00 ; [00] Termination byte
|
||||||
|
|
||||||
;; [03 D5] RAM address for ret execution
|
;; [03 D0] RAM address for ret execution
|
||||||
.db (RAM_SIZE-(_boot_go_adr-_boot_start+2))>>8
|
.db (RAM_SIZE-(_boot_go_adr-_boot_start+2))>>8
|
||||||
.db (RAM_SIZE-(_boot_go_adr-_boot_start+2))&0xFF
|
.db (RAM_SIZE-(_boot_go_adr-_boot_start+2))&0xFF
|
||||||
|
|
||||||
@@ -50,6 +50,9 @@ _boot_rx_wait:
|
|||||||
inc A ; [4C] Increment A, used as receive counter
|
inc A ; [4C] Increment A, used as receive counter
|
||||||
jrne _boot_rx_byte ; [26 F1] If A is not 0, continue receiving
|
jrne _boot_rx_byte ; [26 F1] If A is not 0, continue receiving
|
||||||
|
|
||||||
|
;; Receive 128 bytes success
|
||||||
|
ld UART1_DR, A ; [C7 52 31] Send Ack 0x00
|
||||||
|
|
||||||
;; Check address
|
;; Check address
|
||||||
ldw X, SP ; [96]
|
ldw X, SP ; [96]
|
||||||
incw X ; [5c]
|
incw X ; [5c]
|
||||||
@@ -62,6 +65,8 @@ _boot_rx_wait:
|
|||||||
|
|
||||||
_boot_exit:
|
_boot_exit:
|
||||||
;; Timeout exit, jump to user program
|
;; Timeout exit, jump to user program
|
||||||
|
ldw X, (X) ; [FE]
|
||||||
|
ldw SP, X ; [94]
|
||||||
jp [_boot_go_adr] ; [72 CC 48 3E] Indirect jump
|
jp [_boot_go_adr] ; [72 CC 48 3E] Indirect jump
|
||||||
|
|
||||||
_boot_go_adr:
|
_boot_go_adr:
|
||||||
464
scripts/boot2.s
Normal file
464
scripts/boot2.s
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
; ================================================
|
||||||
|
; STM8 RAM Bootloader for SDCC - Optimized Version
|
||||||
|
;
|
||||||
|
; 命令帧格式:
|
||||||
|
; 字节0: 帧头 (0x5A/0xA5)
|
||||||
|
; 字节1: 命令类型 (见CMD_*常量)
|
||||||
|
; 字节2-3: 目标地址 (高字节在前)
|
||||||
|
; 字节4: 数据长度 (0-64)
|
||||||
|
; 字节5-68: 数据内容 (最多64字节)
|
||||||
|
; 字节69: 校验和 (所有字节XOR)
|
||||||
|
; ================================================
|
||||||
|
|
||||||
|
BOOT2_SP = 0x03CF ; start of boot1 ram address
|
||||||
|
|
||||||
|
;; Register address definitions
|
||||||
|
UART1_SR = 0x5230 ; Status register
|
||||||
|
UART1_DR = 0x5231 ; Data register
|
||||||
|
UART1_BRR1 = 0x5232 ; Baud rate register 1
|
||||||
|
UART1_BRR2 = 0x5233 ; Baud rate register 2
|
||||||
|
UART1_CR2 = 0x5235 ; Control register 2
|
||||||
|
|
||||||
|
FLASH_DUKR = 0x5064 ; Data EEPROM unprotect
|
||||||
|
FLASH_PUKR = 0x5062 ; Flash unprotect
|
||||||
|
FLASH_CR2 = 0x505B ; Flash control 2
|
||||||
|
FLASH_NCR2 = 0x505C ; Flash control 2 complement
|
||||||
|
FLASH_IAPSR = 0x505F ; Flash in-application program status
|
||||||
|
|
||||||
|
OPT0_ROP = 0x4800 ; Option byte: ROP
|
||||||
|
WWDG_CR = 0x50D1 ; WWDG control register
|
||||||
|
|
||||||
|
;; Const vars
|
||||||
|
CMD_READ = 0xF1 ; 读内存命令
|
||||||
|
CMD_WRITE = 0xF2 ; 写内存命令
|
||||||
|
CMD_ERASE = 0xF3 ; 整片擦除命令
|
||||||
|
CMD_RESET = 0xF4 ; 复位命令
|
||||||
|
CMD_GO = 0xF5 ; 跳转执行命令
|
||||||
|
|
||||||
|
CMD_HEADER = 0x5A ; 帧头
|
||||||
|
ACK_HEADER = 0xA5 ; 应答帧头
|
||||||
|
|
||||||
|
SUCCESS_CODE = 0x00 ; 成功响应码
|
||||||
|
ERR_CHECKSUM = 0xE1 ; 校验错误
|
||||||
|
ERR_INVCMD = 0xE2 ; 非法命令
|
||||||
|
ERR_PGDIS = 0xE3 ; 编程受保护的地址
|
||||||
|
|
||||||
|
MAX_DATA_SIZE = 64 ; 最大数据长度
|
||||||
|
|
||||||
|
;; Vars
|
||||||
|
DEFAULT_SP_H = 0x0000 ; ram top address
|
||||||
|
DEFAULT_SP_L = 0x0000 ; ram top address
|
||||||
|
tx_buffer = 0x0002 ; protocol tx buffer
|
||||||
|
rx_buffer = 0x0002 ; protocol rx buffer
|
||||||
|
rx_state = 72 ; 接收状态
|
||||||
|
rx_length = 73 ; 接收长度
|
||||||
|
tx_state = 74 ; 发送状态
|
||||||
|
tx_data_length = 75 ; 待发送的数据长度
|
||||||
|
calc_checksum = 76 ; 计算的校验和
|
||||||
|
temp_var1 = 77 ; 临时变量
|
||||||
|
temp_var2 = 78 ; 临时变量
|
||||||
|
temp_var3 = 79 ; 临时变量
|
||||||
|
|
||||||
|
;; Bootloader body (load in ram ?-0x03D2)
|
||||||
|
.area RAM_BOOT
|
||||||
|
|
||||||
|
.db (BOOT2_SP-(_end-_start)+1)>>8
|
||||||
|
.db (BOOT2_SP-(_end-_start)+1)&0xFF
|
||||||
|
|
||||||
|
_start:
|
||||||
|
; 配置UART1: 128000波特率, 8N1, 启用TX/RX
|
||||||
|
mov UART1_BRR2, #0
|
||||||
|
mov UART1_BRR1, #1
|
||||||
|
mov UART1_CR2, #0x0C ; TEN=1, REN=1
|
||||||
|
|
||||||
|
_main_loop:
|
||||||
|
; 接收命令帧
|
||||||
|
call receive_frame
|
||||||
|
|
||||||
|
; 验证校验和
|
||||||
|
call verify_checksum
|
||||||
|
jrne _checksum_error
|
||||||
|
|
||||||
|
; 根据命令类型跳转
|
||||||
|
ld A, rx_buffer+1 ; 命令类型
|
||||||
|
|
||||||
|
cp A, #CMD_READ
|
||||||
|
jreq _cmd_read
|
||||||
|
|
||||||
|
cp A, #CMD_WRITE
|
||||||
|
jreq _cmd_write
|
||||||
|
|
||||||
|
cp A, #CMD_ERASE
|
||||||
|
jreq _cmd_erase
|
||||||
|
|
||||||
|
cp A, #CMD_RESET
|
||||||
|
jreq _cmd_reset
|
||||||
|
|
||||||
|
cp A, #CMD_GO
|
||||||
|
jreq _cmd_go
|
||||||
|
|
||||||
|
_invalid_cmd_error:
|
||||||
|
; 未知命令,发送错误响应
|
||||||
|
mov tx_state, #ERR_INVCMD
|
||||||
|
call send_ack_state_response
|
||||||
|
jra _main_loop
|
||||||
|
|
||||||
|
_checksum_error:
|
||||||
|
; 校验和错误响应
|
||||||
|
mov tx_state, #ERR_CHECKSUM
|
||||||
|
call send_ack_state_response
|
||||||
|
jra _main_loop
|
||||||
|
|
||||||
|
_cmd_read:
|
||||||
|
; 读取内存命令
|
||||||
|
call read_memory
|
||||||
|
jra _main_loop
|
||||||
|
|
||||||
|
_cmd_write:
|
||||||
|
; 写入内存命令
|
||||||
|
call write_memory
|
||||||
|
jra _main_loop
|
||||||
|
|
||||||
|
_cmd_erase:
|
||||||
|
; 擦除命令
|
||||||
|
call erase_memory
|
||||||
|
jra _main_loop
|
||||||
|
|
||||||
|
_cmd_reset:
|
||||||
|
; 复位命令 - 软件复位
|
||||||
|
call software_reset
|
||||||
|
; 注意: software_reset 不返回
|
||||||
|
|
||||||
|
_cmd_go:
|
||||||
|
; 跳转执行命令
|
||||||
|
call jump_to_address
|
||||||
|
; 注意: jump_to_address 不返回
|
||||||
|
|
||||||
|
receive_frame:
|
||||||
|
; 初始化接收状态
|
||||||
|
clr rx_state
|
||||||
|
mov rx_length, #6
|
||||||
|
ldw X, #rx_buffer
|
||||||
|
|
||||||
|
_receive_data:
|
||||||
|
; 接收数据部分, 等待RXNE=1
|
||||||
|
btjf UART1_SR, #5, _receive_data
|
||||||
|
|
||||||
|
; 根据状态接收不同部分
|
||||||
|
ld A, rx_state
|
||||||
|
|
||||||
|
cp A, #0
|
||||||
|
jreq _receive_header
|
||||||
|
|
||||||
|
cp A, #4
|
||||||
|
jreq _receive_length
|
||||||
|
|
||||||
|
ld A, UART1_DR
|
||||||
|
_save_data:
|
||||||
|
ld (X), A
|
||||||
|
incw X
|
||||||
|
inc rx_state
|
||||||
|
|
||||||
|
dec rx_length
|
||||||
|
jrne _receive_data ; 还有数据,继续接收
|
||||||
|
|
||||||
|
; 数据接收完成
|
||||||
|
mov rx_length, rx_state
|
||||||
|
ret
|
||||||
|
|
||||||
|
_receive_header:
|
||||||
|
ld A, UART1_DR
|
||||||
|
cp A, #CMD_HEADER
|
||||||
|
jrne _receive_data
|
||||||
|
jra _save_data
|
||||||
|
_receive_length:
|
||||||
|
ld A, UART1_DR
|
||||||
|
cp A, #MAX_DATA_SIZE
|
||||||
|
; length<=MAX_DATA_SIZE
|
||||||
|
jrugt receive_frame
|
||||||
|
; save length
|
||||||
|
ld rx_length, A
|
||||||
|
inc rx_length
|
||||||
|
inc rx_length
|
||||||
|
jra _save_data
|
||||||
|
|
||||||
|
verify_checksum:
|
||||||
|
; 初始化
|
||||||
|
clr calc_checksum
|
||||||
|
|
||||||
|
; 计算需要校验的字节数
|
||||||
|
ld A, rx_length
|
||||||
|
dec A
|
||||||
|
ld temp_var1, A
|
||||||
|
|
||||||
|
; 设置指针
|
||||||
|
ldw X, #rx_buffer
|
||||||
|
|
||||||
|
_verify_loop:
|
||||||
|
ld A, (X)
|
||||||
|
xor A, calc_checksum
|
||||||
|
ld calc_checksum, A
|
||||||
|
incw X
|
||||||
|
dec temp_var1
|
||||||
|
jrne _verify_loop
|
||||||
|
|
||||||
|
; 比较校验和
|
||||||
|
ld A, calc_checksum
|
||||||
|
cp A, (X) ; X现在指向校验和位置
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
send_response_pkg:
|
||||||
|
clr calc_checksum
|
||||||
|
ldw X, #tx_buffer
|
||||||
|
ld A, tx_data_length
|
||||||
|
add A, #5
|
||||||
|
ld temp_var1, A
|
||||||
|
; send data
|
||||||
|
_send_loop:
|
||||||
|
ld A, (X)
|
||||||
|
ld UART1_DR, A
|
||||||
|
; calc checksum
|
||||||
|
xor A, calc_checksum
|
||||||
|
ld calc_checksum, A
|
||||||
|
_wait_tx1:
|
||||||
|
btjf UART1_SR, #7, _wait_tx1
|
||||||
|
; move to next
|
||||||
|
incw X
|
||||||
|
dec temp_var1
|
||||||
|
jrne _send_loop
|
||||||
|
; send checksum
|
||||||
|
ld A, calc_checksum
|
||||||
|
ld UART1_DR, A
|
||||||
|
_wait_tx2:
|
||||||
|
btjf UART1_SR, #7, _wait_tx2
|
||||||
|
; finish
|
||||||
|
ret
|
||||||
|
|
||||||
|
; 发送应答状态帧
|
||||||
|
send_ack_state_response:
|
||||||
|
; set header
|
||||||
|
ldw X, #tx_buffer
|
||||||
|
ld A, #ACK_HEADER
|
||||||
|
ld (X), A
|
||||||
|
|
||||||
|
; set length
|
||||||
|
addw X, #4
|
||||||
|
ld A, #1
|
||||||
|
ld (X), A
|
||||||
|
|
||||||
|
; set data
|
||||||
|
incw X
|
||||||
|
ld A, tx_state
|
||||||
|
ld (X), A
|
||||||
|
|
||||||
|
call send_response_pkg
|
||||||
|
ret
|
||||||
|
|
||||||
|
; 发送应答数据帧
|
||||||
|
send_ack_data_response:
|
||||||
|
; set header
|
||||||
|
ldw X, #tx_buffer
|
||||||
|
ld A, #ACK_HEADER
|
||||||
|
ld (X), A
|
||||||
|
|
||||||
|
; set length
|
||||||
|
addw X, #4
|
||||||
|
ld A, tx_data_length
|
||||||
|
ld (X), A
|
||||||
|
|
||||||
|
; already set data
|
||||||
|
|
||||||
|
call send_response_pkg
|
||||||
|
ret
|
||||||
|
|
||||||
|
read_memory:
|
||||||
|
; A = 读取长度
|
||||||
|
ldw X, #rx_buffer+5
|
||||||
|
ld A, (X)
|
||||||
|
ld tx_data_length, A
|
||||||
|
ld temp_var1, A
|
||||||
|
|
||||||
|
; Y = 缓存地址
|
||||||
|
ldw Y, X
|
||||||
|
|
||||||
|
; X = 读取地址
|
||||||
|
ldw X, #rx_buffer+2
|
||||||
|
ldw X, (X)
|
||||||
|
|
||||||
|
_read_loop:
|
||||||
|
; 读取字节
|
||||||
|
ld A, (X)
|
||||||
|
ld (Y), A
|
||||||
|
|
||||||
|
incw X
|
||||||
|
incw Y
|
||||||
|
dec temp_var1
|
||||||
|
jrne _read_loop
|
||||||
|
|
||||||
|
call send_ack_data_response
|
||||||
|
ret
|
||||||
|
|
||||||
|
; temp_var1: 待写入长度
|
||||||
|
; temp_var2: 单次写长度
|
||||||
|
; temp_var3: FLASH_IAPSR
|
||||||
|
write_memory:
|
||||||
|
; A = 写入长度
|
||||||
|
ldw X, #rx_buffer+4
|
||||||
|
ld A, (X)
|
||||||
|
ld temp_var1, A
|
||||||
|
; 检查长度为0直接返回
|
||||||
|
tnz A
|
||||||
|
jreq _write_success
|
||||||
|
|
||||||
|
; Y = src
|
||||||
|
incw X
|
||||||
|
ldw Y, X
|
||||||
|
|
||||||
|
; X = dst
|
||||||
|
ldw X, #rx_buffer+2
|
||||||
|
ldw X, (X)
|
||||||
|
|
||||||
|
; 检查是否为ram地址 (0x0000-0x3FFF)
|
||||||
|
ld A, XH
|
||||||
|
cp A, #0x40
|
||||||
|
jrult _mem_write
|
||||||
|
; 检查是否为flash地址 (0x8000-0xFFFF)
|
||||||
|
cp A, #0x80
|
||||||
|
jruge _flash_write
|
||||||
|
; 检查是否为eeprom/opt地址 (0x4000-0x4FFF)
|
||||||
|
cp A, #0x50
|
||||||
|
jrult _flash_write
|
||||||
|
|
||||||
|
_mem_write:
|
||||||
|
ld A, (Y)
|
||||||
|
ld (X), A
|
||||||
|
incw X
|
||||||
|
incw Y
|
||||||
|
dec temp_var1
|
||||||
|
jrne _mem_write
|
||||||
|
call send_ack_state_response
|
||||||
|
ret
|
||||||
|
|
||||||
|
_flash_write:
|
||||||
|
; unlock FLASH/DATA
|
||||||
|
call unlock_flash
|
||||||
|
|
||||||
|
; Word Program
|
||||||
|
mov FLASH_CR2, #0xC0
|
||||||
|
mov FLASH_NCR2, #0x3F
|
||||||
|
|
||||||
|
_word_write:
|
||||||
|
mov temp_var2, #4
|
||||||
|
_write_loop:
|
||||||
|
; Write a Word
|
||||||
|
ld A, (Y)
|
||||||
|
ld (X), A
|
||||||
|
incw X
|
||||||
|
incw Y
|
||||||
|
dec temp_var2
|
||||||
|
jrne _write_loop
|
||||||
|
; Wait write done
|
||||||
|
_wait_flash_done:
|
||||||
|
mov temp_var3, FLASH_IAPSR
|
||||||
|
btjt temp_var3, #0, _write_error
|
||||||
|
btjf temp_var3, #2, _wait_flash_done
|
||||||
|
|
||||||
|
ld A, temp_var1
|
||||||
|
cp A, #4
|
||||||
|
jrule _write_success
|
||||||
|
sub A, #4
|
||||||
|
ld temp_var1, A
|
||||||
|
jra _word_write
|
||||||
|
|
||||||
|
_write_error:
|
||||||
|
mov tx_state, #ERR_PGDIS
|
||||||
|
jra _write_end
|
||||||
|
_write_success:
|
||||||
|
mov tx_state, #SUCCESS_CODE
|
||||||
|
_write_end:
|
||||||
|
; Word programming off
|
||||||
|
mov FLASH_CR2, #0x00
|
||||||
|
mov FLASH_NCR2, #0xFF
|
||||||
|
; lock FLASH/DATA
|
||||||
|
call lock_flash
|
||||||
|
call send_ack_state_response
|
||||||
|
ret
|
||||||
|
|
||||||
|
unlock_flash:
|
||||||
|
; 检查是否为Flash地址 (0x8000-0xFFFF)
|
||||||
|
ld A, XH
|
||||||
|
cp A, #0x80
|
||||||
|
jrult _do_unlock_data
|
||||||
|
_do_unlock_flash:
|
||||||
|
mov FLASH_PUKR, #0x56 ; KEY1
|
||||||
|
mov FLASH_PUKR, #0xAE ; KEY2
|
||||||
|
ret
|
||||||
|
_do_unlock_data:
|
||||||
|
mov FLASH_DUKR, #0xAE ; KEY1
|
||||||
|
mov FLASH_DUKR, #0x56 ; KEY2
|
||||||
|
ret
|
||||||
|
|
||||||
|
lock_flash:
|
||||||
|
; 检查是否为Flash地址 (0x8000-0xFFFF)
|
||||||
|
ld A, XH
|
||||||
|
cp A, #0x80
|
||||||
|
jrult _do_lock_data
|
||||||
|
_do_lock_flash:
|
||||||
|
bres FLASH_IAPSR, #1
|
||||||
|
ret
|
||||||
|
_do_lock_data:
|
||||||
|
bres FLASH_IAPSR, #3
|
||||||
|
ret
|
||||||
|
|
||||||
|
; 两个字节长度
|
||||||
|
erase_memory:
|
||||||
|
; unlock option byte
|
||||||
|
mov FLASH_CR2, #0xC0
|
||||||
|
mov FLASH_NCR2, #0x3F
|
||||||
|
|
||||||
|
ld A, #0xAA ; lock chip
|
||||||
|
_write_rop:
|
||||||
|
ld OPT0_ROP, A
|
||||||
|
_erase_loop:
|
||||||
|
mov temp_var1, FLASH_IAPSR
|
||||||
|
btjt temp_var1, #0, _lock_opt
|
||||||
|
btjf temp_var1, #2, _erase_loop
|
||||||
|
|
||||||
|
cp A, #0
|
||||||
|
jreq _lock_opt
|
||||||
|
ld A, #0 ; unlock chip
|
||||||
|
jra _write_rop
|
||||||
|
|
||||||
|
_lock_opt:
|
||||||
|
; lock option byte
|
||||||
|
mov FLASH_CR2, #0x00
|
||||||
|
mov FLASH_NCR2, #0xFF
|
||||||
|
ret
|
||||||
|
|
||||||
|
software_reset:
|
||||||
|
; STM8软件复位: 写0x80到WWDG_CR
|
||||||
|
mov WWDG_CR, #0x80
|
||||||
|
; 复位需要时间,这里无限循环
|
||||||
|
jra software_reset
|
||||||
|
|
||||||
|
jump_to_address:
|
||||||
|
mov tx_state, #SUCCESS_CODE
|
||||||
|
call send_ack_state_response
|
||||||
|
|
||||||
|
; 使用X作为延时计数器
|
||||||
|
ldw X, #1000
|
||||||
|
_delay_loop:
|
||||||
|
decw X
|
||||||
|
jrne _delay_loop
|
||||||
|
|
||||||
|
; 读取跳转地址
|
||||||
|
ld A, rx_buffer+2
|
||||||
|
ld XH, A
|
||||||
|
ld A, rx_buffer+3
|
||||||
|
ld XL, A
|
||||||
|
|
||||||
|
; 跳转到指定地址
|
||||||
|
jp (X)
|
||||||
|
|
||||||
|
_end:
|
||||||
523
scripts/stm8_bootloader_test.py
Normal file
523
scripts/stm8_bootloader_test.py
Normal file
@@ -0,0 +1,523 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
STM8 RAM Bootloader测试脚本
|
||||||
|
用于测试bootloader功能
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. 默认加载同级目录下的boot2.bin发送,可通过选项指定
|
||||||
|
2. 执行后,等待用户按下开发板的复位键
|
||||||
|
3. MCU复位后,会以波特率9600发送0x00 0x0D到python
|
||||||
|
4. 收到后开始发送boot2.bin,从最后一块开始向前发送,每发送128字节后,MCU会应答一个0x01
|
||||||
|
5. 发送完成后进入交互shell,用户可以输入命令执行响应的操作(先不实现)
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python3 stm8_bootloader_test.py [-p PORT] [-b BAUDRATE] [--bin BIN_FILE]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import argparse
|
||||||
|
import serial
|
||||||
|
import struct
|
||||||
|
import colorama
|
||||||
|
from colorama import Fore, Style
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
colorama.init(autoreset=True)
|
||||||
|
|
||||||
|
class STM8BootloaderTester:
|
||||||
|
def __init__(self, port: str, baudrate: int = 9600, bin_file: str = None):
|
||||||
|
self.port = port
|
||||||
|
self.baudrate = baudrate
|
||||||
|
|
||||||
|
# 如果未指定bin文件,使用脚本同级目录下的boot2.bin
|
||||||
|
if bin_file is None:
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
self.bin_file = os.path.join(script_dir, "boot2.bin")
|
||||||
|
else:
|
||||||
|
self.bin_file = bin_file
|
||||||
|
|
||||||
|
self.ser = None
|
||||||
|
self.bootloader_size = 0
|
||||||
|
|
||||||
|
def open_serial(self) -> bool:
|
||||||
|
"""打开串口"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}打开串口 {self.port}, 波特率 {self.baudrate}...")
|
||||||
|
self.ser = serial.Serial(
|
||||||
|
port=self.port,
|
||||||
|
baudrate=self.baudrate,
|
||||||
|
bytesize=serial.EIGHTBITS,
|
||||||
|
parity=serial.PARITY_NONE,
|
||||||
|
stopbits=serial.STOPBITS_ONE,
|
||||||
|
timeout=1.0, # 读取超时1秒
|
||||||
|
write_timeout=1.0 # 写入超时1秒
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.ser.is_open:
|
||||||
|
print(f"{Fore.GREEN}串口打开成功!")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}串口打开失败!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except serial.SerialException as e:
|
||||||
|
print(f"{Fore.RED}串口错误: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def close_serial(self):
|
||||||
|
"""关闭串口"""
|
||||||
|
if self.ser and self.ser.is_open:
|
||||||
|
self.ser.close()
|
||||||
|
print(f"{Fore.YELLOW}串口已关闭")
|
||||||
|
|
||||||
|
def check_file_size(self, file_size: int, chunk_size: int = 128) -> bool:
|
||||||
|
"""检查文件大小是否为chunk_size的整数倍"""
|
||||||
|
if file_size % chunk_size != 0:
|
||||||
|
print(f"{Fore.RED}错误: 文件大小 {file_size} 不是 {chunk_size} 的整数倍!")
|
||||||
|
print(f"{Fore.YELLOW}请确保bootloader大小为 {chunk_size} 字节的整数倍")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def read_bin_file(self) -> Optional[bytes]:
|
||||||
|
"""读取bin文件"""
|
||||||
|
try:
|
||||||
|
if not os.path.exists(self.bin_file):
|
||||||
|
print(f"{Fore.RED}错误: 文件 {self.bin_file} 不存在!")
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open(self.bin_file, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
self.bootloader_size = len(data)
|
||||||
|
print(f"{Fore.GREEN}读取 {self.bootloader_size} 字节来自 {self.bin_file}")
|
||||||
|
|
||||||
|
# 检查文件大小是否为128的整数倍
|
||||||
|
if not self.check_file_size(self.bootloader_size):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 显示文件信息
|
||||||
|
if self.bootloader_size > 0:
|
||||||
|
print(f"{Fore.CYAN}文件首16字节: {data[:16].hex(' ')}")
|
||||||
|
if self.bootloader_size > 16:
|
||||||
|
print(f"{Fore.CYAN}文件尾16字节: {data[-16:].hex(' ')}")
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}读取文件错误: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def wait_for_mcu_ready(self, timeout: int = 30) -> bool:
|
||||||
|
"""等待MCU发送就绪信号 0x00 0x0D"""
|
||||||
|
print(f"\n{Fore.YELLOW}等待MCU就绪信号...")
|
||||||
|
print(f"{Fore.YELLOW}请按下开发板上的复位键!")
|
||||||
|
print(f"{Fore.YELLOW}等待超时: {timeout} 秒")
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
last_print_time = start_time
|
||||||
|
expected_bytes = b'\x00\x0D'
|
||||||
|
buffer = b''
|
||||||
|
|
||||||
|
try:
|
||||||
|
while time.time() - start_time < timeout:
|
||||||
|
current_time = time.time()
|
||||||
|
elapsed = int(current_time - start_time)
|
||||||
|
|
||||||
|
# 每5秒打印一次状态
|
||||||
|
if current_time - last_print_time >= 5:
|
||||||
|
print(f"{Fore.YELLOW}已等待 {elapsed} 秒...")
|
||||||
|
last_print_time = current_time
|
||||||
|
|
||||||
|
# 检查串口是否有数据
|
||||||
|
if self.ser.in_waiting > 0:
|
||||||
|
# 读取一个字节
|
||||||
|
byte = self.ser.read(self.ser.in_waiting)
|
||||||
|
if byte:
|
||||||
|
buffer += byte
|
||||||
|
|
||||||
|
# 检查是否匹配就绪信号
|
||||||
|
if buffer[-2:] == expected_bytes:
|
||||||
|
print(f"{Fore.GREEN}收到MCU就绪信号: {buffer.hex(' ')}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 显示接收到的数据(调试用)
|
||||||
|
if buffer:
|
||||||
|
print(f"{Fore.CYAN}[调试] 接收缓冲区: {buffer.hex(' ')}")
|
||||||
|
# 保留缓冲区最后一个字节
|
||||||
|
buffer = buffer[-1:]
|
||||||
|
|
||||||
|
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}等待MCU就绪时出错: {e}")
|
||||||
|
|
||||||
|
print(f"{Fore.RED}超时! 未收到MCU就绪信号")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def reverse_bytes_within_chunks(self, data: bytes, chunk_size: int = 128) -> bytes:
|
||||||
|
"""在每个128字节块内反转字节顺序"""
|
||||||
|
if len(data) % chunk_size != 0:
|
||||||
|
raise ValueError(f"数据长度({len(data)})必须是{chunk_size}的整数倍")
|
||||||
|
|
||||||
|
result = bytearray()
|
||||||
|
num_chunks = len(data) // chunk_size
|
||||||
|
|
||||||
|
for i in range(num_chunks):
|
||||||
|
start_idx = i * chunk_size
|
||||||
|
end_idx = start_idx + chunk_size
|
||||||
|
chunk = data[start_idx:end_idx]
|
||||||
|
# 反转块内字节顺序
|
||||||
|
reversed_chunk = bytes(reversed(chunk))
|
||||||
|
result.extend(reversed_chunk)
|
||||||
|
|
||||||
|
return bytes(result)
|
||||||
|
|
||||||
|
def send_bootloader_reverse(self, data: bytes, chunk_size: int = 128) -> bool:
|
||||||
|
"""反向发送bootloader数据(从最后一块开始,每块内字节也反转)"""
|
||||||
|
if not data:
|
||||||
|
print(f"{Fore.RED}错误: 没有数据可发送")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 首先在每个128字节块内反转字节顺序
|
||||||
|
reversed_data = self.reverse_bytes_within_chunks(data, chunk_size)
|
||||||
|
|
||||||
|
total_size = len(reversed_data)
|
||||||
|
num_chunks = total_size // chunk_size
|
||||||
|
|
||||||
|
print(f"\n{Fore.CYAN}开始反向发送bootloader...")
|
||||||
|
print(f"{Fore.CYAN}总大小: {total_size} 字节")
|
||||||
|
print(f"{Fore.CYAN}分块大小: {chunk_size} 字节")
|
||||||
|
print(f"{Fore.CYAN}总块数: {num_chunks}")
|
||||||
|
print(f"{Fore.CYAN}发送顺序: 从最后一块到第一块,每块内字节顺序已反转")
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 从最后一块开始发送
|
||||||
|
for i in range(num_chunks - 1, -1, -1):
|
||||||
|
start_idx = i * chunk_size
|
||||||
|
end_idx = start_idx + chunk_size
|
||||||
|
chunk = reversed_data[start_idx:end_idx]
|
||||||
|
|
||||||
|
# 原始块索引(反转前)
|
||||||
|
original_chunk_index = i
|
||||||
|
original_start_addr = original_chunk_index * chunk_size
|
||||||
|
|
||||||
|
print(f"\n{Fore.YELLOW}[块 {num_chunks-i}/{num_chunks}]")
|
||||||
|
print(f"{Fore.CYAN} 原始位置: 0x{original_start_addr:04X}-0x{original_start_addr+chunk_size-1:04X}")
|
||||||
|
print(f"{Fore.CYAN} 发送大小: {len(chunk)} 字节")
|
||||||
|
print(f"{Fore.CYAN} 数据(已反转): {chunk[:16].hex(' ')}" +
|
||||||
|
("..." if len(chunk) > 16 else ""))
|
||||||
|
|
||||||
|
# 发送数据块
|
||||||
|
bytes_sent = self.ser.write(chunk)
|
||||||
|
|
||||||
|
if bytes_sent != len(chunk):
|
||||||
|
print(f"{Fore.RED} 发送失败! 期望 {len(chunk)} 字节, 实际 {bytes_sent} 字节")
|
||||||
|
fail_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN} 发送成功: {bytes_sent} 字节")
|
||||||
|
|
||||||
|
# 等待MCU应答
|
||||||
|
print(f"{Fore.YELLOW} 等待MCU应答...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 读取应答
|
||||||
|
ack = self.ser.read(1)
|
||||||
|
if ack == b'\x00':
|
||||||
|
print(f"{Fore.GREEN} 收到确认: 0x00")
|
||||||
|
success_count += 1
|
||||||
|
elif ack:
|
||||||
|
print(f"{Fore.RED} 收到错误应答: {ack.hex()}")
|
||||||
|
fail_count += 1
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED} 应答超时!")
|
||||||
|
fail_count += 1
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED} 读取应答时出错: {e}")
|
||||||
|
fail_count += 1
|
||||||
|
|
||||||
|
# 显示进度
|
||||||
|
progress = (num_chunks - i) / num_chunks * 100
|
||||||
|
print(f"{Fore.CYAN} 进度: {progress:.1f}% ({num_chunks-i}/{num_chunks})")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}发送过程中出错: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"\n{Fore.CYAN}=== 发送完成 ===")
|
||||||
|
print(f"{Fore.GREEN}成功块数: {success_count}")
|
||||||
|
print(f"{Fore.RED}失败块数: {fail_count}")
|
||||||
|
|
||||||
|
return fail_count == 0
|
||||||
|
|
||||||
|
def interactive_shell(self):
|
||||||
|
"""交互式命令行"""
|
||||||
|
print(f"\n{Fore.CYAN}{'='*60}")
|
||||||
|
print(f"{Fore.CYAN}进入交互模式")
|
||||||
|
print(f"{Fore.CYAN}输入 'help' 查看可用命令")
|
||||||
|
print(f"{Fore.CYAN}输入 'exit' 退出")
|
||||||
|
print(f"{Fore.CYAN}{'='*60}")
|
||||||
|
|
||||||
|
commands = {
|
||||||
|
'help': self._cmd_help,
|
||||||
|
'read': self._cmd_read,
|
||||||
|
'write': self._cmd_write,
|
||||||
|
'erase': self._cmd_erase,
|
||||||
|
'reset': self._cmd_reset,
|
||||||
|
'go': self._cmd_go,
|
||||||
|
'echo': self._cmd_echo,
|
||||||
|
'info': self._cmd_info,
|
||||||
|
}
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# 获取用户输入
|
||||||
|
cmd_input = input(f"\n{Fore.GREEN}boot> ").strip()
|
||||||
|
|
||||||
|
if not cmd_input:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查是否为退出命令
|
||||||
|
if cmd_input.lower() in ['exit', 'quit', 'q']:
|
||||||
|
print(f"{Fore.YELLOW}退出交互模式")
|
||||||
|
break
|
||||||
|
|
||||||
|
# 分割命令和参数
|
||||||
|
parts = cmd_input.split()
|
||||||
|
cmd = parts[0].lower()
|
||||||
|
args = parts[1:] if len(parts) > 1 else []
|
||||||
|
|
||||||
|
# 执行命令
|
||||||
|
if cmd in commands:
|
||||||
|
commands[cmd](args)
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}未知命令: {cmd}")
|
||||||
|
print(f"{Fore.YELLOW}输入 'help' 查看可用命令")
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(f"\n{Fore.YELLOW}检测到Ctrl+C,退出交互模式")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}命令执行错误: {e}")
|
||||||
|
|
||||||
|
def _cmd_help(self, args):
|
||||||
|
"""显示帮助"""
|
||||||
|
print(f"{Fore.CYAN}可用命令:")
|
||||||
|
print(f"{Fore.YELLOW} help 显示此帮助信息")
|
||||||
|
print(f"{Fore.YELLOW} read <addr> <len> 读取内存")
|
||||||
|
print(f"{Fore.YELLOW} write <addr> <data> 写入内存")
|
||||||
|
print(f"{Fore.YELLOW} erase 擦除Flash")
|
||||||
|
print(f"{Fore.YELLOW} reset 复位MCU")
|
||||||
|
print(f"{Fore.YELLOW} go <addr> 跳转到指定地址执行")
|
||||||
|
print(f"{Fore.YELLOW} echo <text> 回显文本")
|
||||||
|
print(f"{Fore.YELLOW} info 显示信息")
|
||||||
|
print(f"{Fore.YELLOW} exit 退出交互模式")
|
||||||
|
|
||||||
|
def _cmd_read(self, args):
|
||||||
|
"""读取内存命令"""
|
||||||
|
if len(args) < 2:
|
||||||
|
print(f"{Fore.RED}用法: read <地址> <长度>")
|
||||||
|
print(f"{Fore.YELLOW}示例: read 0x8000 16")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
addr = int(args[0], 0)
|
||||||
|
length = int(args[1], 0)
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}读取内存:")
|
||||||
|
print(f"{Fore.CYAN} 地址: 0x{addr:04X}")
|
||||||
|
print(f"{Fore.CYAN} 长度: {length} 字节")
|
||||||
|
|
||||||
|
# TODO: 实现读取命令
|
||||||
|
print(f"{Fore.YELLOW}[待实现] 读取功能")
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"{Fore.RED}参数错误: {e}")
|
||||||
|
|
||||||
|
def _cmd_write(self, args):
|
||||||
|
"""写入内存命令"""
|
||||||
|
if len(args) < 2:
|
||||||
|
print(f"{Fore.RED}用法: write <地址> <数据>")
|
||||||
|
print(f"{Fore.YELLOW}示例: write 0x8000 0x01 0x02 0x03")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
addr = int(args[0], 0)
|
||||||
|
data = [int(x, 0) for x in args[1:]]
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}写入内存:")
|
||||||
|
print(f"{Fore.CYAN} 地址: 0x{addr:04X}")
|
||||||
|
print(f"{Fore.CYAN} 数据: {bytes(data).hex(' ')}")
|
||||||
|
print(f"{Fore.CYAN} 长度: {len(data)} 字节")
|
||||||
|
|
||||||
|
# TODO: 实现写入命令
|
||||||
|
print(f"{Fore.YELLOW}[待实现] 写入功能")
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"{Fore.RED}参数错误: {e}")
|
||||||
|
|
||||||
|
def _cmd_erase(self, args):
|
||||||
|
"""擦除Flash命令"""
|
||||||
|
print(f"{Fore.YELLOW}擦除Flash...")
|
||||||
|
# TODO: 实现擦除命令
|
||||||
|
print(f"{Fore.YELLOW}[待实现] 擦除功能")
|
||||||
|
|
||||||
|
def _cmd_reset(self, args):
|
||||||
|
"""复位命令"""
|
||||||
|
print(f"{Fore.YELLOW}复位MCU...")
|
||||||
|
# TODO: 实现复位命令
|
||||||
|
print(f"{Fore.YELLOW}[待实现] 复位功能")
|
||||||
|
|
||||||
|
def _cmd_go(self, args):
|
||||||
|
"""跳转执行命令"""
|
||||||
|
if len(args) < 1:
|
||||||
|
print(f"{Fore.RED}用法: go <地址>")
|
||||||
|
print(f"{Fore.YELLOW}示例: go 0x8000")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
addr = int(args[0], 0)
|
||||||
|
print(f"{Fore.YELLOW}跳转到地址: 0x{addr:04X}")
|
||||||
|
# TODO: 实现跳转命令
|
||||||
|
print(f"{Fore.YELLOW}[待实现] 跳转功能")
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"{Fore.RED}参数错误: {e}")
|
||||||
|
|
||||||
|
def _cmd_echo(self, args):
|
||||||
|
"""回显命令"""
|
||||||
|
if args:
|
||||||
|
text = ' '.join(args)
|
||||||
|
print(f"{Fore.CYAN}回显: {text}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}用法: echo <文本>")
|
||||||
|
|
||||||
|
def _cmd_info(self, args):
|
||||||
|
"""显示信息命令"""
|
||||||
|
print(f"{Fore.CYAN}Bootloader测试工具信息:")
|
||||||
|
print(f"{Fore.CYAN} 串口: {self.port}")
|
||||||
|
print(f"{Fore.CYAN} 波特率: {self.baudrate}")
|
||||||
|
print(f"{Fore.CYAN} Bootloader文件: {self.bin_file}")
|
||||||
|
print(f"{Fore.CYAN} Bootloader大小: {self.bootloader_size} 字节")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""运行测试"""
|
||||||
|
print(f"{Fore.CYAN}{'='*60}")
|
||||||
|
print(f"{Fore.CYAN}STM8 RAM Bootloader 测试工具")
|
||||||
|
print(f"{Fore.CYAN}{'='*60}")
|
||||||
|
|
||||||
|
# 1. 打开串口
|
||||||
|
if not self.open_serial():
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 2. 读取bin文件
|
||||||
|
bin_data = self.read_bin_file()
|
||||||
|
if not bin_data:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 3. 等待MCU就绪
|
||||||
|
if not self.wait_for_mcu_ready():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 4. 发送bootloader(反向发送)
|
||||||
|
if not self.send_bootloader_reverse(bin_data):
|
||||||
|
print(f"{Fore.RED}Bootloader发送失败!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 5. 进入交互模式
|
||||||
|
self.interactive_shell()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(f"\n{Fore.YELLOW}用户中断!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}运行时错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.close_serial()
|
||||||
|
|
||||||
|
def list_serial_ports():
|
||||||
|
"""列出可用串口"""
|
||||||
|
import serial.tools.list_ports
|
||||||
|
|
||||||
|
ports = serial.tools.list_ports.comports()
|
||||||
|
|
||||||
|
if not ports:
|
||||||
|
print(f"{Fore.YELLOW}未找到可用串口!")
|
||||||
|
return []
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}可用串口:")
|
||||||
|
for i, port in enumerate(ports):
|
||||||
|
print(f"{Fore.GREEN} [{i}] {port.device}: {port.description}")
|
||||||
|
|
||||||
|
return ports
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="STM8 RAM Bootloader测试工具")
|
||||||
|
parser.add_argument("-p", "--port", help="串口号 (如 COM3, /dev/ttyUSB0)")
|
||||||
|
parser.add_argument("-b", "--baudrate", type=int, default=9600,
|
||||||
|
help="串口波特率 (默认: 9600)")
|
||||||
|
parser.add_argument("--bin", default=None,
|
||||||
|
help="Bootloader bin文件路径 (默认: 脚本目录下的boot2.bin)")
|
||||||
|
parser.add_argument("--list", action="store_true",
|
||||||
|
help="列出可用串口")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 如果指定了--list,列出串口后退出
|
||||||
|
if args.list:
|
||||||
|
list_serial_ports()
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果没有指定串口,列出并让用户选择
|
||||||
|
if not args.port:
|
||||||
|
ports = list_serial_ports()
|
||||||
|
if not ports:
|
||||||
|
print(f"{Fore.RED}请使用 -p 参数指定串口!")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
choice = input(f"\n{Fore.CYAN}请选择串口号 [0-{len(ports)-1}]: ")
|
||||||
|
idx = int(choice)
|
||||||
|
if 0 <= idx < len(ports):
|
||||||
|
args.port = ports[idx].device
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}无效的选择!")
|
||||||
|
return
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
print(f"{Fore.RED}无效的输入!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 创建测试器并运行
|
||||||
|
tester = STM8BootloaderTester(
|
||||||
|
port=args.port,
|
||||||
|
baudrate=args.baudrate,
|
||||||
|
bin_file=args.bin
|
||||||
|
)
|
||||||
|
|
||||||
|
success = tester.run()
|
||||||
|
|
||||||
|
if success:
|
||||||
|
print(f"\n{Fore.GREEN}测试完成!")
|
||||||
|
else:
|
||||||
|
print(f"\n{Fore.RED}测试失败!")
|
||||||
|
|
||||||
|
# 等待用户按键退出
|
||||||
|
input(f"\n{Fore.YELLOW}按Enter键退出...")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -9,8 +9,13 @@ void main(void) {
|
|||||||
PB_DDR |= (1 << LED_PIN);
|
PB_DDR |= (1 << LED_PIN);
|
||||||
PB_CR1 |= (1 << LED_PIN);
|
PB_CR1 |= (1 << LED_PIN);
|
||||||
|
|
||||||
|
UART1_BRR1 = 0x0D;
|
||||||
|
UART1_BRR2 = 0x00;
|
||||||
|
UART1_CR2 = 0x0D;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
PB_ODR ^= (1 << LED_PIN);
|
PB_ODR ^= (1 << LED_PIN);
|
||||||
delay_ms(1000);
|
UART1_DR = *ptr++;
|
||||||
|
delay_ms(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user