fix boot2.s
This commit is contained in:
@@ -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 -- -- 00 03 d0
|
;; 4800 00 00 ff 00 ff 00 ff 00 ff 00 ff -- -- 00 03 d0
|
||||||
;; 03D0|4810 a6 0d c7 52 32 c7 52 35 c7 52 31 a6 80 5f 5c 27
|
;; 03D0|4810 a6 0d c7 52 32 c7 52 35 c7 52 31 4f 5f 5c 27 0b
|
||||||
;; 03E0|4820 17 72 0b 52 30 f8 3b 52 31 4c 26 f1 c7 52 31 96
|
;; 03E0|4820 72 0b 52 30 f8 3b 52 31 4c 20 f1 4d 27 09 96 5c
|
||||||
;; 03F0|4830 5c 5c 5c 13 01 26 e4 81 fe 94 72 cc 48 3e 80 04
|
;; 03F0|4830 5c 5c 13 01 26 01 81 5f 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
|
||||||
@@ -33,14 +33,12 @@ _boot_start:
|
|||||||
;; Send BREAK signal and version number $0D
|
;; Send BREAK signal and version number $0D
|
||||||
ld UART1_DR, A ; [C7 52 31] Send version number 0x0D
|
ld UART1_DR, A ; [C7 52 31] Send version number 0x0D
|
||||||
|
|
||||||
;; Receive 128-byte data block and push onto stack
|
clr A
|
||||||
_boot_rx_block:
|
|
||||||
ld A, #0x80 ; [A6 80]
|
|
||||||
_boot_rx_byte:
|
_boot_rx_byte:
|
||||||
clrw X ; [5F] Reset X (for timeout detection)
|
clrw X ; [5F] Reset X (for timeout detection)
|
||||||
_boot_rx_wait:
|
_boot_rx_wait:
|
||||||
incw X ; [5C] Increment X, check for overflow (timeout detection)
|
incw X ; [5C] Increment X, check for overflow (timeout detection)
|
||||||
jreq _boot_exit ; [27 14] If X overflows (receive timeout), exit
|
jreq _boot_timeout ; [27 14] If X overflows (receive timeout), exit
|
||||||
|
|
||||||
;; Wait for data reception
|
;; Wait for data reception
|
||||||
btjf UART1_SR, #5, _boot_rx_wait ;[72 0B 52 30 F8]
|
btjf UART1_SR, #5, _boot_rx_wait ;[72 0B 52 30 F8]
|
||||||
@@ -48,23 +46,24 @@ _boot_rx_wait:
|
|||||||
;; Data received, push onto stack
|
;; Data received, push onto stack
|
||||||
push UART1_DR ; [3B 52 31]
|
push UART1_DR ; [3B 52 31]
|
||||||
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
|
jra _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
|
|
||||||
|
|
||||||
|
_boot_timeout:
|
||||||
|
tnz A
|
||||||
|
jreq _boot_exit
|
||||||
;; Check address
|
;; Check address
|
||||||
ldw X, SP ; [96]
|
ldw X, SP ; [96]
|
||||||
incw X ; [5c]
|
incw X ; [5c]
|
||||||
incw X ; [5c]
|
incw X ; [5c]
|
||||||
incw X ; [5c]
|
incw X ; [5c]
|
||||||
cpw X, (1, SP) ; [13 01]
|
cpw X, (1, SP) ; [13 01]
|
||||||
jrne _boot_rx_block ; [26 E7]
|
jrne _boot_exit ; [26 E7]
|
||||||
;; Reception complete, jump to received code
|
;; Reception complete, jump to received code
|
||||||
ret ; [81] Jump to address at top of stack via ret instruction
|
ret ; [81] Jump to address at top of stack via ret instruction
|
||||||
|
|
||||||
_boot_exit:
|
_boot_exit:
|
||||||
;; Timeout exit, jump to user program
|
;; Timeout exit, jump to user program
|
||||||
|
clrw X
|
||||||
ldw X, (X) ; [FE]
|
ldw X, (X) ; [FE]
|
||||||
ldw SP, X ; [94]
|
ldw SP, X ; [94]
|
||||||
jp [_boot_go_adr] ; [72 CC 48 3E] Indirect jump
|
jp [_boot_go_adr] ; [72 CC 48 3E] Indirect jump
|
||||||
|
|||||||
114
scripts/boot2.s
114
scripts/boot2.s
@@ -10,7 +10,7 @@
|
|||||||
; 字节69: 校验和 (所有字节XOR)
|
; 字节69: 校验和 (所有字节XOR)
|
||||||
; ================================================
|
; ================================================
|
||||||
|
|
||||||
BOOT2_SP = 0x03CF ; start of boot1 ram address
|
BOOT2_SP = 0x03CD ; start of boot1 ram address
|
||||||
|
|
||||||
;; Register address definitions
|
;; Register address definitions
|
||||||
UART1_SR = 0x5230 ; Status register
|
UART1_SR = 0x5230 ; Status register
|
||||||
@@ -31,9 +31,7 @@ WWDG_CR = 0x50D1 ; WWDG control register
|
|||||||
;; Const vars
|
;; Const vars
|
||||||
CMD_READ = 0xF1 ; 读内存命令
|
CMD_READ = 0xF1 ; 读内存命令
|
||||||
CMD_WRITE = 0xF2 ; 写内存命令
|
CMD_WRITE = 0xF2 ; 写内存命令
|
||||||
CMD_ERASE = 0xF3 ; 整片擦除命令
|
CMD_GO = 0xF3 ; 跳转执行命令
|
||||||
CMD_RESET = 0xF4 ; 复位命令
|
|
||||||
CMD_GO = 0xF5 ; 跳转执行命令
|
|
||||||
|
|
||||||
CMD_HEADER = 0x5A ; 帧头
|
CMD_HEADER = 0x5A ; 帧头
|
||||||
ACK_HEADER = 0xA5 ; 应答帧头
|
ACK_HEADER = 0xA5 ; 应答帧头
|
||||||
@@ -50,16 +48,16 @@ DEFAULT_SP_H = 0x0000 ; ram top address
|
|||||||
DEFAULT_SP_L = 0x0000 ; ram top address
|
DEFAULT_SP_L = 0x0000 ; ram top address
|
||||||
tx_buffer = 0x0002 ; protocol tx buffer
|
tx_buffer = 0x0002 ; protocol tx buffer
|
||||||
rx_buffer = 0x0002 ; protocol rx buffer
|
rx_buffer = 0x0002 ; protocol rx buffer
|
||||||
rx_state = 72 ; 接收状态
|
rx_state = 72 ; 接收状态
|
||||||
rx_length = 73 ; 接收长度
|
rx_length = 73 ; 接收长度
|
||||||
tx_state = 74 ; 发送状态
|
tx_state = 74 ; 发送状态
|
||||||
tx_data_length = 75 ; 待发送的数据长度
|
tx_data_length = 75 ; 待发送的数据长度
|
||||||
calc_checksum = 76 ; 计算的校验和
|
calc_checksum = 76 ; 计算的校验和
|
||||||
temp_var1 = 77 ; 临时变量
|
temp_var1 = 77 ; 临时变量
|
||||||
temp_var2 = 78 ; 临时变量
|
temp_var2 = 78 ; 临时变量
|
||||||
temp_var3 = 79 ; 临时变量
|
temp_var3 = 79 ; 临时变量
|
||||||
|
|
||||||
;; Bootloader body (load in ram ?-0x03D2)
|
;; Bootloader body (load in ram ?-0x03D0)
|
||||||
.area RAM_BOOT
|
.area RAM_BOOT
|
||||||
|
|
||||||
.db (BOOT2_SP-(_end-_start)+1)>>8
|
.db (BOOT2_SP-(_end-_start)+1)>>8
|
||||||
@@ -67,16 +65,15 @@ temp_var3 = 79 ; 临时变量
|
|||||||
|
|
||||||
_start:
|
_start:
|
||||||
; 配置UART1: 128000波特率, 8N1, 启用TX/RX
|
; 配置UART1: 128000波特率, 8N1, 启用TX/RX
|
||||||
mov UART1_BRR2, #0
|
|
||||||
mov UART1_BRR1, #1
|
mov UART1_BRR1, #1
|
||||||
mov UART1_CR2, #0x0C ; TEN=1, REN=1
|
;mov UART1_BRR2, #0
|
||||||
|
;mov UART1_CR2, #0x0C ; TEN=1, REN=1
|
||||||
_main_loop:
|
_main_loop:
|
||||||
; 接收命令帧
|
; 接收命令帧
|
||||||
call receive_frame
|
callr receive_frame
|
||||||
|
|
||||||
; 验证校验和
|
; 验证校验和
|
||||||
call verify_checksum
|
callr verify_checksum
|
||||||
jrne _checksum_error
|
jrne _checksum_error
|
||||||
|
|
||||||
; 根据命令类型跳转
|
; 根据命令类型跳转
|
||||||
@@ -88,12 +85,6 @@ _main_loop:
|
|||||||
cp A, #CMD_WRITE
|
cp A, #CMD_WRITE
|
||||||
jreq _cmd_write
|
jreq _cmd_write
|
||||||
|
|
||||||
cp A, #CMD_ERASE
|
|
||||||
jreq _cmd_erase
|
|
||||||
|
|
||||||
cp A, #CMD_RESET
|
|
||||||
jreq _cmd_reset
|
|
||||||
|
|
||||||
cp A, #CMD_GO
|
cp A, #CMD_GO
|
||||||
jreq _cmd_go
|
jreq _cmd_go
|
||||||
|
|
||||||
@@ -119,19 +110,10 @@ _cmd_write:
|
|||||||
call write_memory
|
call write_memory
|
||||||
jra _main_loop
|
jra _main_loop
|
||||||
|
|
||||||
_cmd_erase:
|
|
||||||
; 擦除命令
|
|
||||||
call erase_memory
|
|
||||||
jra _main_loop
|
|
||||||
|
|
||||||
_cmd_reset:
|
|
||||||
; 复位命令 - 软件复位
|
|
||||||
call software_reset
|
|
||||||
; 注意: software_reset 不返回
|
|
||||||
|
|
||||||
_cmd_go:
|
_cmd_go:
|
||||||
; 跳转执行命令
|
; 跳转执行命令
|
||||||
call jump_to_address
|
ldw X, rx_buffer+2
|
||||||
|
jp (X)
|
||||||
; 注意: jump_to_address 不返回
|
; 注意: jump_to_address 不返回
|
||||||
|
|
||||||
receive_frame:
|
receive_frame:
|
||||||
@@ -252,7 +234,7 @@ send_ack_state_response:
|
|||||||
ld A, tx_state
|
ld A, tx_state
|
||||||
ld (X), A
|
ld (X), A
|
||||||
|
|
||||||
call send_response_pkg
|
callr send_response_pkg
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; 发送应答数据帧
|
; 发送应答数据帧
|
||||||
@@ -269,7 +251,7 @@ send_ack_data_response:
|
|||||||
|
|
||||||
; already set data
|
; already set data
|
||||||
|
|
||||||
call send_response_pkg
|
callr send_response_pkg
|
||||||
ret
|
ret
|
||||||
|
|
||||||
read_memory:
|
read_memory:
|
||||||
@@ -296,7 +278,7 @@ _read_loop:
|
|||||||
dec temp_var1
|
dec temp_var1
|
||||||
jrne _read_loop
|
jrne _read_loop
|
||||||
|
|
||||||
call send_ack_data_response
|
callr send_ack_data_response
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; temp_var1: 待写入长度
|
; temp_var1: 待写入长度
|
||||||
@@ -337,12 +319,12 @@ _mem_write:
|
|||||||
incw Y
|
incw Y
|
||||||
dec temp_var1
|
dec temp_var1
|
||||||
jrne _mem_write
|
jrne _mem_write
|
||||||
call send_ack_state_response
|
callr send_ack_state_response
|
||||||
ret
|
ret
|
||||||
|
|
||||||
_flash_write:
|
_flash_write:
|
||||||
; unlock FLASH/DATA
|
; unlock FLASH/DATA
|
||||||
call unlock_flash
|
callr unlock_flash
|
||||||
|
|
||||||
; Word Program
|
; Word Program
|
||||||
mov FLASH_CR2, #0xC0
|
mov FLASH_CR2, #0xC0
|
||||||
@@ -381,7 +363,7 @@ _write_end:
|
|||||||
mov FLASH_CR2, #0x00
|
mov FLASH_CR2, #0x00
|
||||||
mov FLASH_NCR2, #0xFF
|
mov FLASH_NCR2, #0xFF
|
||||||
; lock FLASH/DATA
|
; lock FLASH/DATA
|
||||||
call lock_flash
|
callr lock_flash
|
||||||
call send_ack_state_response
|
call send_ack_state_response
|
||||||
ret
|
ret
|
||||||
|
|
||||||
@@ -411,54 +393,4 @@ _do_lock_data:
|
|||||||
bres FLASH_IAPSR, #3
|
bres FLASH_IAPSR, #3
|
||||||
ret
|
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:
|
_end:
|
||||||
|
|||||||
@@ -1,523 +0,0 @@
|
|||||||
#!/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,13 +9,14 @@ void main(void) {
|
|||||||
PB_DDR |= (1 << LED_PIN);
|
PB_DDR |= (1 << LED_PIN);
|
||||||
PB_CR1 |= (1 << LED_PIN);
|
PB_CR1 |= (1 << LED_PIN);
|
||||||
|
|
||||||
|
/* 9600bps, 8N1, TEN, REN */
|
||||||
UART1_BRR1 = 0x0D;
|
UART1_BRR1 = 0x0D;
|
||||||
UART1_BRR2 = 0x00;
|
UART1_BRR2 = 0x00;
|
||||||
UART1_CR2 = 0x0D;
|
UART1_CR2 = 0x0C;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
PB_ODR ^= (1 << LED_PIN);
|
PB_ODR ^= (1 << LED_PIN);
|
||||||
UART1_DR = *ptr++;
|
UART1_DR = PB_ODR;
|
||||||
delay_ms(500);
|
delay_ms(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user