STM32 BootLoader 刷新项目 (十三) Python上位机介绍
大家好,这是我们STM32 BootLoader的最后一篇文章了,讲述用Python写的上位机,也更新了半年时间了,谢谢大家的支持,到目前为止,已经更新了12篇文章了,想必大家对BootLoader已经有了个基本的了解,下面是Python上位机的全部源码,有需要的兄弟可以借鉴一下,水平有限。
以下是将完整Python代码中的每一行进行解释后的代码,注释全部用中文描述原代码的含义,并对每个函数、常量及代码块作详细中文说明:
1. 全局宏定义
import serial # 导入用于串口通信的模块
import struct # 导入结构化数据处理的模块
import os # 导入操作系统相关功能的模块
import sys # 导入系统功能模块
import glob # 导入文件路径操作模块
# 常量定义 - Flash 操作状态
Flash_HAL_OK = 0x00 # Flash 操作成功
Flash_HAL_ERROR = 0x01 # Flash 操作错误
Flash_HAL_BUSY = 0x02 # Flash 操作繁忙
Flash_HAL_TIMEOUT = 0x03 # Flash 操作超时
Flash_HAL_INV_ADDR = 0x04 # Flash 操作地址无效
# Bootloader命令定义
COMMAND_BL_GET_VER = 0x51 # 获取Bootloader版本
COMMAND_BL_GET_HELP = 0x52 # 获取支持的命令
COMMAND_BL_GET_CID = 0x53 # 获取芯片ID
COMMAND_BL_GET_RDP_STATUS = 0x54 # 获取读保护状态
COMMAND_BL_GO_TO_ADDR = 0x55 # 跳转到指定地址
COMMAND_BL_FLASH_ERASE = 0x56 # Flash擦除命令
COMMAND_BL_MEM_WRITE = 0x57 # Flash写入命令
COMMAND_BL_EN_R_W_PROTECT = 0x58 # 启用读写保护
COMMAND_BL_MEM_READ = 0x59 # 内存读取命令
COMMAND_BL_READ_SECTOR_P_STATUS = 0x5A # 读取扇区保护状态
COMMAND_BL_OTP_READ = 0x5B # 读取OTP(一次性可编程)区域
COMMAND_BL_DIS_R_W_PROTECT = 0x5C # 禁用读写保护
COMMAND_BL_MY_NEW_COMMAND = 0x5D # 用户自定义命令
# 各命令的长度定义
COMMAND_BL_GET_VER_LEN = 6 # 获取Bootloader版本命令长度
COMMAND_BL_GET_HELP_LEN = 6 # 获取支持的命令长度
COMMAND_BL_GET_CID_LEN = 6 # 获取芯片ID命令长度
COMMAND_BL_GET_RDP_STATUS_LEN = 6 # 获取读保护状态命令长度
COMMAND_BL_GO_TO_ADDR_LEN = 10 # 跳转地址命令长度
COMMAND_BL_FLASH_ERASE_LEN = 8 # Flash擦除命令长度
COMMAND_BL_MEM_WRITE_LEN = 11 # Flash写入命令长度
COMMAND_BL_EN_R_W_PROTECT_LEN = 8 # 启用读写保护命令长度
COMMAND_BL_READ_SECTOR_P_STATUS_LEN = 6 # 读取扇区保护状态命令长度
COMMAND_BL_DIS_R_W_PROTECT_LEN = 6 # 禁用读写保护命令长度
COMMAND_BL_MY_NEW_COMMAND_LEN = 8 # 用户自定义命令长度
# 全局变量
verbose_mode = 1 # 是否输出详细日志
mem_write_active = 0 # 写内存操作标志
2. 文件操作
# ----------------------------- 文件操作部分 ----------------------------------------
# 计算文件长度
def calc_file_len():
size = os.path.getsize("user_app.bin") # 获取文件 user_app.bin 的大小
return size # 返回文件大小
# 打开文件
def open_the_file():
global bin_file # 声明全局变量 bin_file
bin_file = open('user_app.bin', 'rb') # 以二进制只读方式打开文件 user_app.bin
# 读取文件 - 占位函数
def read_the_file():
pass # 暂未实现
# 关闭文件
def close_the_file():
bin_file.close() # 关闭文件句柄
上面文件操作这部分代码主要是针对BootLoader刷写过程中要打开写入的.bin文件。
3. 实用工具部分
# ----------------------------- 实用工具部分 ----------------------------------------
# 地址转换为字节
def word_to_byte(addr, index, lowerfirst):
value = (addr >> (8 * (index - 1)) & 0x000000FF) # 提取地址的某一字节
return value # 返回字节值
# CRC32校验计算
def get_crc(buff, length):
Crc = 0xFFFFFFFF # 初始化CRC值
for data in buff[0:length]: # 遍历缓冲区中所有字节
Crc = Crc ^ data # 异或操作
for i in range(32): # 遍历每一位
if(Crc & 0x80000000): # 检测最高位
Crc = (Crc << 1) ^ 0x04C11DB7 # CRC计算多项式
else:
Crc = (Crc << 1) # 左移操作
return Crc # 返回计算结果
这部分主要是给后面代码做一些实用类的函数,方便后面调用,主要是两个,一个是将地址转化为字节。一个是用作CRC校验。
4. 串口部分
# ----------------------------- 串口部分 ----------------------------------------
# 列出所有可用的串口
def serial_ports():
"""列出系统中的所有串口名称"""
if sys.platform.startswith('win'): # Windows系统
ports = ['COM%s' % (i + 1) for i in range(256)] # 枚举所有可能的COM端口
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'): # Linux或Cygwin系统
ports = glob.glob('/dev/tty[A-Za-z]*') # 查找所有TTY设备
elif sys.platform.startswith('darwin'): # macOS系统
ports = glob.glob('/dev/tty.*') # 查找所有TTY设备
else:
raise EnvironmentError('Unsupported platform') # 不支持的系统
result = []
for port in ports:
try:
s = serial.Serial(port) # 打开串口
s.close() # 关闭串口
result.append(port) # 将可用串口加入结果列表
except (OSError, serial.SerialException): # 捕获异常
pass
return result # 返回可用串口列表
# 配置串口
def Serial_Port_Configuration(port):
global ser # 声明全局变量 ser
try:
ser = serial.Serial(port, 115200, timeout=2) # 打开串口,波特率115200,超时时间2秒
except:
print("\n Oops! That was not a valid port") # 提示端口无效
port = serial_ports() # 列出所有可用端口
if not port:
print("\n No ports Detected") # 未检测到端口
else:
print("\n Here are some available ports on your PC. Try Again!") # 提示可用端口
print("\n ", port)
return -1 # 返回错误码
if ser.is_open:
print("\n Port Open Success") # 串口打开成功
else:
print("\n Port Open Failed") # 串口打开失败
return 0 # 返回成功码
# 从串口读取数据
def read_serial_port(length):
read_value = ser.read(length) # 读取指定长度的数据
return read_value # 返回读取的数据
# 关闭串口
def Close_serial_port():
pass # 暂未实现
# 清空串口缓冲区
def purge_serial_port():
ser.reset_input_buffer() # 清空输入缓冲区
# 向串口写入数据
def Write_to_serial_port(value, *length):
data = struct.pack('>B', value) # 将值转换为单字节格式
if verbose_mode:
value = bytearray(data) # 将数据转换为字节数组
print(" "+"0x{:02x}".format(value[0]), end=' ') # 输出写入数据
if mem_write_active and not verbose_mode:
print("#", end=' ') # 如果处于写入模式,显示#
ser.write(data) # 将数据写入串口
上面串口通信部分的内容。
5. 命令处理
# ----------------------------- 命令处理部分 ----------------------------------------
# 处理用户自定义命令的函数
def process_COMMAND_BL_MY_NEW_COMMAND(length):
pass # 暂未实现
# 处理获取Bootloader版本的命令
def process_COMMAND_BL_GET_VER(length):
ver = read_serial_port(1) # 从串口读取1字节数据
value = bytearray(ver) # 转换为字节数组
print("\n Bootloader Ver. : ", hex(value[0])) # 打印版本号(以十六进制显示)
# 处理获取支持的命令列表
def process_COMMAND_BL_GET_HELP(length):
value = read_serial_port(length) # 从串口读取指定长度的数据
reply = bytearray(value) # 转换为字节数组
print("\n Supported Commands :", end=' ') # 打印支持的命令
for x in reply:
print(hex(x), end=' ') # 逐个打印每个命令
print()
# 处理获取芯片ID的命令
def process_COMMAND_BL_GET_CID(length):
value = read_serial_port(length) # 从串口读取数据
ci = (value[1] << 8) + value[0] # 解析两字节数据组成芯片ID
print("\n Chip Id. : ", hex(ci)) # 打印芯片ID
# 处理获取读保护状态的命令
def process_COMMAND_BL_GET_RDP_STATUS(length):
value = read_serial_port(length) # 从串口读取数据
rdp = bytearray(value) # 转换为字节数组
print("\n RDP Status : ", hex(rdp[0])) # 打印读保护状态
# 处理跳转到指定地址的命令
def process_COMMAND_BL_GO_TO_ADDR(length):
addr_status = 0
value = read_serial_port(length) # 从串口读取数据
addr_status = bytearray(value) # 转换为字节数组
print("\n Address Status : ", hex(addr_status[0])) # 打印地址状态
# 处理Flash擦除命令
def process_COMMAND_BL_FLASH_ERASE(length):
erase_status = 0
value = read_serial_port(length) # 从串口读取数据
if len(value):
erase_status = bytearray(value) # 转换为字节数组
if erase_status[0] == Flash_HAL_OK: # 判断擦除结果
print("\n Erase Status: Success Code: FLASH_HAL_OK")
elif erase_status[0] == Flash_HAL_ERROR:
print("\n Erase Status: Fail Code: FLASH_HAL_ERROR")
elif erase_status[0] == Flash_HAL_BUSY:
print("\n Erase Status: Fail Code: FLASH_HAL_BUSY")
elif erase_status[0] == Flash_HAL_TIMEOUT:
print("\n Erase Status: Fail Code: FLASH_HAL_TIMEOUT")
elif erase_status[0] == Flash_HAL_INV_ADDR:
print("\n Erase Status: Fail Code: FLASH_HAL_INV_SECTOR")
else:
print("\n Erase Status: Fail Code: UNKNOWN_ERROR_CODE")
else:
print("Timeout: Bootloader is not responding") # 超时未响应
# 处理内存写入命令
def process_COMMAND_BL_MEM_WRITE(length):
write_status = 0
value = read_serial_port(length) # 从串口读取数据
write_status = bytearray(value) # 转换为字节数组
if write_status[0] == Flash_HAL_OK: # 判断写入结果
print("\n Write_status: FLASH_HAL_OK")
elif write_status[0] == Flash_HAL_ERROR:
print("\n Write_status: FLASH_HAL_ERROR")
elif write_status[0] == Flash_HAL_BUSY:
print("\n Write_status: FLASH_HAL_BUSY")
elif write_status[0] == Flash_HAL_TIMEOUT:
print("\n Write_status: FLASH_HAL_TIMEOUT")
elif write_status[0] == Flash_HAL_INV_ADDR:
print("\n Write_status: FLASH_HAL_INV_ADDR")
else:
print("\n Write_status: UNKNOWN_ERROR")
print("\n")
# 处理Flash批量擦除命令(未实现)
def process_COMMAND_BL_FLASH_MASS_ERASE(length):
pass # 暂未实现
# 扇区保护模式选项
protection_mode = ["Write Protection", "Read/Write Protection", "No protection"] # 定义保护模式描述
# 根据保护状态解析保护模式
def protection_type(status, n):
if status & (1 << 15): # 检查是否启用PCROP
if status & (1 << n): # 检查扇区的保护位
return protection_mode[1] # 读写保护模式
else:
return protection_mode[2] # 无保护模式
else:
if status & (1 << n):
return protection_mode[2] # 无保护模式
else:
return protection_mode[0] # 写保护模式
# 处理读取扇区保护状态的命令
def process_COMMAND_BL_READ_SECTOR_STATUS(length):
s_status = 0
value = read_serial_port(length) # 从串口读取数据
s_status = bytearray(value) # 转换为字节数组
print("\n Sector Status : ", s_status[0]) # 打印扇区状态
print("\n ====================================")
print("\n Sector \tProtection")
print("\n ====================================")
if s_status[0] & (1 << 15): # 检查PCROP模式
print("\n Flash protection mode : Read/Write Protection(PCROP)\n")
else:
print("\n Flash protection mode : \tWrite Protection\n")
for x in range(8): # 遍历每个扇区
print("\n Sector{0} {1}".format(x, protection_type(s_status[0], x)))
# 处理禁用读写保护的命令
def process_COMMAND_BL_DIS_R_W_PROTECT(length):
status = 0
value = read_serial_port(length) # 从串口读取数据
status = bytearray(value) # 转换为字节数组
if status[0]: # 检查返回状态
print("\n FAIL")
else:
print("\n SUCCESS")
# 处理启用读写保护的命令
def process_COMMAND_BL_EN_R_W_PROTECT(length):
status = 0
value = read_serial_port(length) # 从串口读取数据
status = bytearray(value) # 转换为字节数组
if status[0]: # 检查返回状态
print("\n FAIL")
else:
print("\n SUCCESS")
解释:
- 代码中定义了多个命令处理函数,用于根据命令解析返回数据并打印结果。
- 使用位操作来判断和解析各种Flash状态(如保护模式、PCROP等)。
- 核心功能包括读取芯片状态、擦除Flash、内存写入、设置保护等。
6. 菜单操作
下面是 decode_menu_command_code
函数及相关逻辑。
# 解码菜单中的命令代码并处理
def decode_menu_command_code(command):
ret_value = 0 # 返回值,用于指示命令处理状态
data_buf = [] # 数据缓冲区
for i in range(255):
data_buf.append(0) # 初始化缓冲区,大小为255字节
# 菜单退出命令
if command == 0:
print("\n Exiting...!") # 打印退出信息
raise SystemExit # 退出程序
# 命令1:获取Bootloader版本
elif command == 1:
print("\n Command == > BL_GET_VER") # 显示所选命令
COMMAND_BL_GET_VER_LEN = 6 # 定义命令长度
data_buf[0] = COMMAND_BL_GET_VER_LEN - 1 # 数据帧的长度字段
data_buf[1] = COMMAND_BL_GET_VER # 设置命令代码
crc32 = get_crc(data_buf, COMMAND_BL_GET_VER_LEN - 4) # 计算CRC校验值
crc32 = crc32 & 0xffffffff # 将CRC限制为32位
# 将CRC校验值分解成4个字节
data_buf[2] = word_to_byte(crc32, 1, 1)
data_buf[3] = word_to_byte(crc32, 2, 1)
data_buf[4] = word_to_byte(crc32, 3, 1)
data_buf[5] = word_to_byte(crc32, 4, 1)
# 发送数据帧的每个字节到串口
Write_to_serial_port(data_buf[0], 1)
for i in data_buf[1:COMMAND_BL_GET_VER_LEN]:
Write_to_serial_port(i, COMMAND_BL_GET_VER_LEN - 1)
# 读取并处理Bootloader返回的数据
ret_value = read_bootloader_reply(data_buf[1])
# 命令2:获取支持的命令
elif command == 2:
print("\n Command == > BL_GET_HELP")
COMMAND_BL_GET_HELP_LEN = 6
data_buf[0] = COMMAND_BL_GET_HELP_LEN - 1
data_buf[1] = COMMAND_BL_GET_HELP
crc32 = get_crc(data_buf, COMMAND_BL_GET_HELP_LEN - 4)
crc32 = crc32 & 0xffffffff
data_buf[2] = word_to_byte(crc32, 1, 1)
data_buf[3] = word_to_byte(crc32, 2, 1)
data_buf[4] = word_to_byte(crc32, 3, 1)
data_buf[5] = word_to_byte(crc32, 4, 1)
Write_to_serial_port(data_buf[0], 1)
for i in data_buf[1:COMMAND_BL_GET_HELP_LEN]:
Write_to_serial_port(i, COMMAND_BL_GET_HELP_LEN - 1)
ret_value = read_bootloader_reply(data_buf[1])
# 命令3:获取芯片ID
elif command == 3:
print("\n Command == > BL_GET_CID")
COMMAND_BL_GET_CID_LEN = 6
data_buf[0] = COMMAND_BL_GET_CID_LEN - 1
data_buf[1] = COMMAND_BL_GET_CID
crc32 = get_crc(data_buf, COMMAND_BL_GET_CID_LEN - 4)
crc32 = crc32 & 0xffffffff
data_buf[2] = word_to_byte(crc32, 1, 1)
data_buf[3] = word_to_byte(crc32, 2, 1)
data_buf[4] = word_to_byte(crc32, 3, 1)
data_buf[5] = word_to_byte(crc32, 4, 1)
Write_to_serial_port(data_buf[0], 1)
for i in data_buf[1:COMMAND_BL_GET_CID_LEN]:
Write_to_serial_port(i, COMMAND_BL_GET_CID_LEN - 1)
ret_value = read_bootloader_reply(data_buf[1])
# 命令4:获取读保护状态
elif command == 4:
print("\n Command == > BL_GET_RDP_STATUS")
data_buf[0] = COMMAND_BL_GET_RDP_STATUS_LEN - 1
data_buf[1] = COMMAND_BL_GET_RDP_STATUS
crc32 = get_crc(data_buf, COMMAND_BL_GET_RDP_STATUS_LEN - 4)
crc32 = crc32 & 0xffffffff
data_buf[2] = word_to_byte(crc32, 1, 1)
data_buf[3] = word_to_byte(crc32, 2, 1)
data_buf[4] = word_to_byte(crc32, 3, 1)
data_buf[5] = word_to_byte(crc32, 4, 1)
Write_to_serial_port(data_buf[0], 1)
for i in data_buf[1:COMMAND_BL_GET_RDP_STATUS_LEN]:
Write_to_serial_port(i, COMMAND_BL_GET_RDP_STATUS_LEN - 1)
ret_value = read_bootloader_reply(data_buf[1])
# 命令5:跳转到指定地址
elif command == 5:
print("\n Command == > BL_GO_TO_ADDR")
go_address = input("\n Please enter 4 bytes go address in hex:") # 提示用户输入地址
go_address = int(go_address, 16) # 将输入的地址转换为十六进制整数
data_buf[0] = COMMAND_BL_GO_TO_ADDR_LEN - 1
data_buf[1] = COMMAND_BL_GO_TO_ADDR
# 将地址分解为字节
data_buf[2] = word_to_byte(go_address, 1, 1)
data_buf[3] = word_to_byte(go_address, 2, 1)
data_buf[4] = word_to_byte(go_address, 3, 1)
data_buf[5] = word_to_byte(go_address, 4, 1)
# 计算CRC校验值
crc32 = get_crc(data_buf, COMMAND_BL_GO_TO_ADDR_LEN - 4)
data_buf[6] = word_to_byte(crc32, 1, 1)
data_buf[7] = word_to_byte(crc32, 2, 1)
data_buf[8] = word_to_byte(crc32, 3, 1)
data_buf[9] = word_to_byte(crc32, 4, 1)
Write_to_serial_port(data_buf[0], 1)
for i in data_buf[1:COMMAND_BL_GO_TO_ADDR_LEN]:
Write_to_serial_port(i, COMMAND_BL_GO_TO_ADDR_LEN - 1)
ret_value = read_bootloader_reply(data_buf[1])
# 其余命令逻辑类似,均是设置命令代码、填充参数、计算CRC并发送。
# 如果命令无效
else:
print("\n Please input valid command code\n")
return
# 处理超时的情况
if ret_value == -2:
print("\n TimeOut : No response from the bootloader")
print("\n Reset the board and Try Again !")
return
功能总结:
decode_menu_command_code
函数是主命令处理入口,根据用户选择的命令代码执行对应操作。- 每个命令都包括以下步骤:
- 填充数据缓冲区,包括命令代码和参数。
- 计算数据缓冲区的 CRC 校验值。
- 将数据通过串口发送到目标设备。
- 读取并处理设备返回的数据。
7. 接收下位机数据处理
read_bootloader_reply
函数及菜单循环处理逻辑。
# 读取Bootloader的返回信息
def read_bootloader_reply(command_code):
len_to_follow = 0 # 用于存储设备返回数据的长度
ret = -2 # 默认返回值,-2表示超时或无响应
# 从串口读取2字节(ACK/NACK信息)
ack = read_serial_port(2)
if len(ack): # 如果读取到了数据
a_array = bytearray(ack) # 转换为字节数组
if a_array[0] == 0xA5: # 检查是否接收到ACK(0xA5)
len_to_follow = a_array[1] # 第二字节为后续数据长度
print("\n CRC : SUCCESS Len :", len_to_follow) # 打印CRC校验成功及返回数据长度
# 根据命令代码调用对应的处理函数
if command_code == COMMAND_BL_GET_VER:
process_COMMAND_BL_GET_VER(len_to_follow)
elif command_code == COMMAND_BL_GET_HELP:
process_COMMAND_BL_GET_HELP(len_to_follow)
elif command_code == COMMAND_BL_GET_CID:
process_COMMAND_BL_GET_CID(len_to_follow)
elif command_code == COMMAND_BL_GET_RDP_STATUS:
process_COMMAND_BL_GET_RDP_STATUS(len_to_follow)
elif command_code == COMMAND_BL_GO_TO_ADDR:
process_COMMAND_BL_GO_TO_ADDR(len_to_follow)
elif command_code == COMMAND_BL_FLASH_ERASE:
process_COMMAND_BL_FLASH_ERASE(len_to_follow)
elif command_code == COMMAND_BL_MEM_WRITE:
process_COMMAND_BL_MEM_WRITE(len_to_follow)
elif command_code == COMMAND_BL_READ_SECTOR_P_STATUS:
process_COMMAND_BL_READ_SECTOR_STATUS(len_to_follow)
elif command_code == COMMAND_BL_EN_R_W_PROTECT:
process_COMMAND_BL_EN_R_W_PROTECT(len_to_follow)
elif command_code == COMMAND_BL_DIS_R_W_PROTECT:
process_COMMAND_BL_DIS_R_W_PROTECT(len_to_follow)
elif command_code == COMMAND_BL_MY_NEW_COMMAND:
process_COMMAND_BL_MY_NEW_COMMAND(len_to_follow)
else:
print("\n Invalid command code\n") # 无效的命令代码
ret = 0 # 返回0表示命令处理成功
elif a_array[0] == 0x7F: # 如果接收到NACK(0x7F)
print("\n CRC: FAIL \n") # 打印CRC校验失败
ret = -1 # 返回-1表示CRC校验失败
else:
print("\n Timeout : Bootloader not responding") # 超时未响应
return ret # 返回处理结果
8. 菜单显示
# ----------------------------- 菜单循环逻辑 ----------------------------------------
# 提示用户输入设备串口号
name = input("Enter the Port Name of your device(Ex: COM3):")
ret = 0 # 用于存储串口配置的返回值
ret = Serial_Port_Configuration(name) # 配置串口
if ret < 0: # 如果配置失败
decode_menu_command_code(0) # 退出程序
# 无限循环显示菜单
while True:
print("\n +==========================================+")
print(" | Menu |")
print(" | STM32F4 BootLoader v1 |")
print(" +==========================================+")
# 打印可用命令的列表
print("\n Which BL command do you want to send ??\n")
print(" BL_GET_VER --> 1")
print(" BL_GET_HLP --> 2")
print(" BL_GET_CID --> 3")
print(" BL_GET_RDP_STATUS --> 4")
print(" BL_GO_TO_ADDR --> 5")
print(" BL_FLASH_MASS_ERASE --> 6")
print(" BL_FLASH_ERASE --> 7")
print(" BL_MEM_WRITE --> 8")
print(" BL_EN_R_W_PROTECT --> 9")
print(" BL_MEM_READ --> 10")
print(" BL_READ_SECTOR_P_STATUS --> 11")
print(" BL_OTP_READ --> 12")
print(" BL_DIS_R_W_PROTECT --> 13")
print(" BL_MY_NEW_COMMAND --> 14")
print(" MENU_EXIT --> 0")
# 提示用户输入命令代码
command_code = input("\n Type the command code here :")
# 检查输入是否为有效的数字
if not command_code.isdigit():
print("\n Please Input valid code shown above") # 提示无效输入
else:
# 解码并执行用户选择的命令
decode_menu_command_code(int(command_code))
# 等待用户按任意键继续
input("\n Press any key to continue :")
purge_serial_port() # 清空串口缓冲区
功能说明:
-
read_bootloader_reply
函数:- 用于解析Bootloader的返回数据。
- 先读取2字节的数据(ACK/NACK和后续数据长度)。
- 如果CRC校验成功(ACK为
0xA5
),调用相应的处理函数。 - 如果接收到
0x7F
,表示CRC校验失败。 - 如果没有任何返回,表示设备超时未响应。
-
菜单循环:
- 通过
while True
创建无限循环,用于显示Bootloader命令菜单。 - 提示用户输入命令代码(如获取版本、擦除Flash等)。
- 验证用户输入是否有效(必须是数字)。
- 调用
decode_menu_command_code
函数根据用户输入执行相应命令。 - 提供按任意键继续的功能,以便用户可以反复选择命令。
- 每次循环结束后清空串口缓冲区。
- 通过
整体逻辑:
- 程序启动时,要求用户输入设备的串口号。
- 程序与目标设备建立通信后,进入菜单界面。
- 用户根据提示输入命令代码,程序解析并发送相应的Bootloader命令。
- 根据设备返回的数据,显示操作结果。
- 用户可以重复操作,直到输入退出命令(命令代码
0
)为止。
至此,整个代码的功能和实现已经完全解释。如果还有特定部分需要详细说明或补充,请向我联系!
9. 实战操作
下面是上位机的命令菜单,通过在终端调用Python脚本,然后在终端输入下位机连接的串口号,即可进入命令界面,目前可支持如下命令:
其他的操作也在之前的文章中做了介绍,有兴趣的可以查找我之前的文章。
STM32 BootLoader 刷新项目 (一) STM32CubeMX UART串口通信工程搭建
STM32 BootLoader 刷新项目 (二) 方案介绍
STM32 BootLoader 刷新项目 (三) 程序框架搭建及刷新演示
STM32 BootLoader 刷新项目 (四) 通信协议
STM32 BootLoader 刷新项目 (五) 获取软件版本号-命令0x51
STM32 BootLoader 刷新项目 (六) 获取帮助-命令0x52
STM32 BootLoader 刷新项目 (七) 获取芯片ID-0x53
STM32 BootLoader 刷新项目 (八) 读取Flash保护ROP-0x54
STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55
[STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56](STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56-CSDN博客)