【IDE】Linux下使用openocd烧录bin文件

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

相关推荐
YongCheng_Liang2 小时前
Linux 基础命令的 7 大核心模块
linux·运维·服务器
tt5555555555553 小时前
嵌入式启动全流程详解:从SPL到内核
linux
Madison-No74 小时前
【Linux】基础开发工具---yum / apt
linux·运维·服务器
dot to one4 小时前
应用层:Http、Https
linux·c++·网络协议
K_i1344 小时前
Linux的几种版本详细介绍
linux
東雪蓮☆5 小时前
LNMP 环境部署 WordPress
linux·运维·mysql·nginx·php
名誉寒冰5 小时前
# 深入理解Linux内核与用户态通信:Netlink机制实战
linux·服务器·windows
薰衣草23336 小时前
linux-1
linux·运维·服务器
egoist20236 小时前
[linux仓库]System V 进程通信详解:System V消息队列、信号量
linux·c语言·消息队列·pv·信号量