STM32Fxx的IDE已经在Linux下部署了,理论上来说,ARMv7、ARMv8...的开发环境都已经统一了。有一个问题:Windows下IDE可以在线刷写bin文件,Linux下可不可以呢?答案是肯定的,只不过我们需要在控制台通过相对复杂的指令来完成,为了简化控制台操作,我们直接通过脚本来实现。
给出脚本前要强调一点:当我们为STM32Fxxx等基于ARMv7的SOC开发程序的时候,我们使用arm-none-eabi-gdb实现elf文件的在线下载与仿真,如果要刷写bin文件的时候,直接使用openocd来完成。下面的脚本是刷写bin文件使用的。脚本可以完成bootloader、app_a与app_b刷写。
#!/bin/bash
# flash_all_openocd.sh
# 默认配置
BOOTLOADER_NAME="Bootloader/Build/Application/stm32f407_bl.bin"
BOOTLOADER_ADDR="0x08000000"
APP_NAME="Bootloader/Build/Application/stm32_app.bin"
APP_A_ADDR="0x08010000"
APP_B_ADDR="0x08090000"
OPENOCD_CFG="-f interface/stlink.cfg -f target/stm32f4x.cfg"
# 显示帮助信息
show_help() {
echo "=== OpenOCD 串行烧录脚本 ==="
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " -b, --bootloader <文件> 设置 bootloader 文件路径"
echo " -a, --app <文件> 设置应用程序文件路径"
echo " --bl-addr <地址> 设置 bootloader 烧写地址 (默认: $BOOTLOADER_ADDR)"
echo " --app-a-addr <地址> 设置应用程序 A 分区烧写地址 (默认: $APP_A_ADDR)"
echo " --app-b-addr <地址> 设置应用程序 B 分区烧写地址 (默认: $APP_B_ADDR)"
echo " -c, --config <配置> 设置 OpenOCD 配置"
echo " -h, --help 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 -b bootloader.bin -a app.bin"
echo " $0 --bootloader bl.bin --app app.bin --bl-addr 0x08000000"
echo ""
echo "当前默认配置:"
echo " Bootloader: $BOOTLOADER_NAME -> $BOOTLOADER_ADDR"
echo " App A: $APP_NAME -> $APP_A_ADDR"
echo " App B: $APP_NAME -> $APP_B_ADDR"
}
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-b|--bootloader)
BOOTLOADER_NAME="$2"
shift 2
;;
-a|--app)
APP_NAME="$2"
shift 2
;;
--bl-addr)
# 确保地址格式正确,移除可能的前导减号
BOOTLOADER_ADDR="${2#-}"
shift 2
;;
--app-a-addr)
APP_A_ADDR="${2#-}"
shift 2
;;
--app-b-addr)
APP_B_ADDR="${2#-}"
shift 2
;;
-c|--config)
OPENOCD_CFG="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
echo "错误: 未知选项 $1"
echo "使用 $0 --help 查看帮助信息"
exit 1
;;
esac
done
# 检查文件是否存在
check_file_exists() {
if [[ ! -f "$1" ]]; then
echo "错误: 文件不存在: $1"
exit 1
fi
}
# 获取文件大小
get_file_size() {
if [[ -f "$1" ]]; then
stat -c%s "$1" 2>/dev/null || stat -f%z "$1" 2>/dev/null
else
echo "0"
fi
}
# 清理地址格式(移除可能的前导减号)
clean_address() {
local addr="$1"
echo "${addr#-}"
}
# 显示旋转进度
show_spinner() {
local pid=$1
local delay=0.1
local spinstr='|/-\'
while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
local temp=${spinstr#?}
printf " [%c] " "$spinstr"
local spinstr=$temp${spinstr%"$temp"}
sleep $delay
printf "\b\b\b\b\b"
done
printf " \b\b\b\b"
}
# 烧录函数(带进度显示)
flash_image() {
local name="$1"
local file="$2"
local addr="$3"
# 清理地址格式
local clean_addr=$(clean_address "$addr")
local file_size=$(get_file_size "$file")
local file_kb=$((file_size / 1024))
echo "=== 烧录 $name ==="
echo "文件: $file"
echo "大小: ${file_kb} KB"
echo "地址: $clean_addr"
echo ""
# 显示烧录状态
echo -n "烧录中..."
# 创建临时文件记录输出
local temp_log=$(mktemp)
# 启动烧录进程
openocd $OPENOCD_CFG \
-c "init; reset halt; flash write_image erase $file $clean_addr; reset run; shutdown" > "$temp_log" 2>&1 &
local openocd_pid=$!
# 显示旋转进度
show_spinner $openocd_pid &
local spinner_pid=$!
# 等待烧录完成
wait $openocd_pid
local result=$?
# 停止旋转进度
kill $spinner_pid 2>/dev/null
wait $spinner_pid 2>/dev/null
# 检查结果
if [ $result -eq 0 ] && ! grep -q "Error\|error\|failed" "$temp_log"; then
echo " 完成!"
echo "✅ $name 烧录成功!"
# 显示烧录统计信息
if grep -q "wrote [0-9]\+ bytes" "$temp_log"; then
local written_bytes=$(grep -o "wrote [0-9]\+ bytes" "$temp_log" | head -1 | awk '{print $2}')
echo " 写入: $written_bytes 字节 ($((written_bytes / 1024)) KB)"
fi
if grep -q "erased" "$temp_log"; then
echo " 擦除: 完成"
fi
rm -f "$temp_log"
return 0
else
echo " 失败!"
echo "❌ $name 烧录失败!"
echo ""
echo "错误信息:"
cat "$temp_log"
rm -f "$temp_log"
return 1
fi
}
# 清理默认地址格式
BOOTLOADER_ADDR=$(clean_address "$BOOTLOADER_ADDR")
APP_A_ADDR=$(clean_address "$APP_A_ADDR")
APP_B_ADDR=$(clean_address "$APP_B_ADDR")
echo "=== OpenOCD 串行烧录 ==="
echo "配置信息:"
echo " Bootloader: $BOOTLOADER_NAME -> $BOOTLOADER_ADDR"
echo " App A: $APP_NAME -> $APP_A_ADDR"
echo " App B: $APP_NAME -> $APP_B_ADDR"
echo " OpenOCD配置: $OPENOCD_CFG"
echo ""
# 检查文件是否存在
check_file_exists "$BOOTLOADER_NAME"
check_file_exists "$APP_NAME"
# 显示文件信息
echo "文件信息:"
echo " Bootloader: $BOOTLOADER_NAME ($(( $(get_file_size "$BOOTLOADER_NAME") / 1024 )) KB)"
echo " App: $APP_NAME ($(( $(get_file_size "$APP_NAME") / 1024 )) KB)"
echo ""
# 开始烧录过程
echo "开始烧录..."
echo "=========================================="
# 烧录 Bootloader
if ! flash_image "Bootloader" "$BOOTLOADER_NAME" "$BOOTLOADER_ADDR"; then
echo "❌ Bootloader 烧录失败,退出!"
exit 1
fi
echo ""
echo "------------------------------------------"
# 烧录分区 A
if ! flash_image "分区 A" "$APP_NAME" "$APP_A_ADDR"; then
echo "❌ 分区 A 烧录失败,退出!"
exit 1
fi
echo ""
echo "------------------------------------------"
# 烧录分区 B
if ! flash_image "分区 B" "$APP_NAME" "$APP_B_ADDR"; then
echo "❌ 分区 B 烧录失败,退出!"
exit 1
fi
echo ""
echo "=========================================="
echo "=== 所有固件烧录完成 ==="
脚本测试ok
