尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
GD32F103 IAP升级实战:Bootloader与App分区、跳转及Flash写入避坑指南
简介这份资源是面向ARM嵌入式与单片机开发者的GD32F103 IAP在线升级源代码工程适合已掌握STM32/GD32基础外设、希望深入理解Bootloader跳转与固件更新流程的中级开发者。由于GD32与STM32内核一致IAP升级路径主要依赖内核机制因此该工程对两类芯片的升级方案具有通用参考价值。压缩包共164个文件以61个C源文件、75个头文件为主辅以8个汇编启动文件、2个Keil工程文件uvprojx/uvoptx、2个分散加载脚本sct及hex、bin固件整体约624KB。工程针对GD32F103C8T6的64K Flash做了明确划分Bootloader占用0x8000000至0x80077FF共30K用户程序占用0x8007800至0x800FFFF共34K扇区大小1024字节并配套bin程序便于直接烧录验证。目前已有5182人学习下载读者可从中获取完整的Flash空间分配思路、跳转实现与工程配置模板快速搭建自己的IAP升级框架。1. GD32F103 IAP 升级源代码把 Bootloader 和 App 拆开看升级这件事才算落地GD32F103 的 IAP 升级源代码解决的是一个很具体的问题设备已经装在现场程序要更新但没人愿意拆机壳去接调试器。IAP 就是让单片机自己擦写自己的 Flash通过串口、CAN 或者无线模块把新固件收进来跳转执行。这套源码通常包含 Bootloader 工程和 App 工程两部分Bootloader 负责判断升级标志、接收数据、写 Flash、跳转App 负责业务逻辑并在需要时置位标志、复位回 Bootloader。适合谁做工业控制、传感器节点、电机驱动这些不方便拆机的嵌入式从业者。如果你手里正好有 GD32F103 的板子想跑通一次完整的串口升级这份代码能省掉你从零搭框架的时间。但别急着烧进去先搞清楚它的分区逻辑和跳转条件否则很容易卡在“升级完不运行”这种玄学问题上。2. 分区规划与中断向量表重映射IAP 能不能跑起来全看这两步2.1 Flash 分区怎么切才不打架GD32F103 的 Flash 起步 128KB页大小 1KB 或 2KB具体看型号常见做法是按 2KB 对齐。IAP 方案里Bootloader 占最前面一段App 占后面一段中间可能还要留一个参数区存升级标志和固件信息。我一般会这样切区域起始地址大小用途Bootloader0x0800000016KB启动判断、通信、擦写参数区0x080040002KB升级标志、固件长度、CRCApp0x08004800剩余空间业务代码注意参数区要单独占一页因为 Flash 擦除是按页来的你不可能只改几个字节。升级标志通常用魔数比如 0x5A5A5A5A 表示“需要升级”写进去之后复位Bootloader 读到这个值就进升级流程否则直接跳 App。2.2 中断向量表重映射不搬这张表App 里所有中断都会跑飞App 的起始地址不再是 0x08000000但 Cortex-M3 内核复位后默认从 0x08000000 取向量表。所以 App 里必须做两件事第一在启动文件里把 VECT_TAB_OFFSET 改成相对于 Bootloader 的偏移第二在 main 函数开头调用nvic_vector_table_set()把向量表搬到 App 的起始地址。GD32 的固件库提供了这个函数参数是 App 起始地址和偏移。/* App 的 main 函数开头重映射中断向量表 */ #define APP_START_ADDR 0x08004800U int main(void) { /* 把向量表搬到 App 起始地址偏移 0 */ nvic_vector_table_set(NVIC_VECTTAB_FLASH, APP_START_ADDR); /* 开全局中断后续外设初始化按正常流程走 */ __enable_irq(); ... }逻辑说明nvic_vector_table_set实际写的是 SCB-VTOR 寄存器告诉内核“中断向量表在 APP_START_ADDR 这里”。参数NVIC_VECTTAB_FLASH表示向量表在 Flash 里第二个参数是基地址。如果你不调这一步App 里一开定时器中断内核还是去 0x08000000 找中断服务函数结果跳进 Bootloader 的代码里现象就是“程序跑着跑着就复位”或者“中断完全不响应”。这个坑我见过太多次血泪经验就是App 里第一行不搬向量表后面全是白搭。2.3 Bootloader 跳转前必须关中断、清标志Bootloader 在跳转到 App 之前要把自己开的中断全关掉尤其是 SysTick 和串口中断。否则跳过去之后App 还没来得及重映射向量表Bootloader 的中断就触发了直接跑飞。常见做法是/* Bootloader 跳转前清理现场 */ void jump_to_app(uint32_t app_addr) { /* 关全局中断防止跳转过程中被中断打断 */ __disable_irq(); /* 关闭 SysTick避免跳过去之后还在跑 Bootloader 的 tick */ SysTick-CTRL 0; SysTick-LOAD 0; SysTick-VAL 0; /* 清除所有挂起的中断标志 */ for (uint8_t i 0; i 8; i) { NVIC-ICER[i] 0xFFFFFFFF; NVIC-ICPR[i] 0xFFFFFFFF; } /* 取 App 的栈顶地址和复位向量 */ uint32_t stack_top *(volatile uint32_t *)app_addr; uint32_t reset_vec *(volatile uint32_t *)(app_addr 4); /* 设置主栈指针 */ __set_MSP(stack_top); /* 跳转到 App 的复位处理函数 */ void (*app_entry)(void) (void (*)(void))reset_vec; app_entry(); }参数说明app_addr是 App 的起始地址比如 0x08004800。stack_top是 App 向量表的第一个字reset_vec是第二个字。__set_MSP把主栈指针设成 App 的栈顶这一步不能省否则 App 用的还是 Bootloader 的栈容易踩内存。跳转函数执行完就不会返回了所以后面不用写 while(1)。3. 串口接收与 Flash 写入数据怎么收、怎么写、怎么校验3.1 串口协议帧格式自定义一个够用的就行IAP 升级不需要复杂的协议但帧头、长度、校验、帧尾这四样最好都有。我一般用这样的格式字段长度说明帧头2 字节0xAA 0x55命令1 字节0x01 升级请求0x02 数据帧0x03 结束长度2 字节数据段长度小端数据N 字节固件内容CRC162 字节从命令到数据的校验帧尾2 字节0x55 0xAA接收的时候用一个状态机别在中断里直接写 Flash。中断只负责把字节塞进环形缓冲区主循环再解析帧、写 Flash。Flash 写入期间 CPU 会阻塞如果放在中断里串口接收会丢数据。3.2 Flash 擦写GD32F103 的页大小和写入对齐GD32F103 的 Flash 编程和 STM32F103 很像但寄存器名字和库函数有差别。擦除按页写入按半字16 位或字32 位。常见做法是先解锁 Flash擦除目标页然后逐半字写入最后上锁。写之前要确保目标地址已经擦过否则写进去的数据不对。/* 写一帧固件数据到 Flashaddr 必须 2 字节对齐 */ int flash_write(uint32_t addr, uint8_t *buf, uint32_t len) { /* 解锁 Flash 控制器 */ fmc_unlock(); /* 按半字写入len 必须是偶数 */ for (uint32_t i 0; i len; i 2) { uint16_t half_word buf[i] | (buf[i 1] 8); /* 调用 GD32 库函数写半字 */ if (fmc_halfword_program(addr i, half_word) ! FMC_READY) { fmc_lock(); return -1; /* 写入失败返回错误 */ } } /* 上锁防止误写 */ fmc_lock(); return 0; }逻辑说明fmc_unlock解除写保护fmc_halfword_program写一个半字fmc_lock重新上锁。参数addr是目标地址必须 2 字节对齐len是数据长度必须是偶数因为半字写入。如果写入返回不是 FMC_READY说明目标页没擦干净或者地址越界了。注意擦除一页需要的时间大概 20~40ms这段时间串口如果还在收数据缓冲区要够大否则丢包。3.3 校验与跳转条件CRC 不对就别跳每收完一帧数据算一次 CRC16和帧里的校验值比对。全部数据收完之后再对整个固件算一次 CRC32和升级请求里带的固件 CRC 比对。只有全部通过才把升级标志写成“完成”然后复位。如果中间任何一步失败标志保持“需要升级”下次复位继续进 Bootloader不会跳到一个半成品 App 里。这个设计很关键否则一次通信干扰就能让设备变砖。4. 避坑与排查升级失败、跳转不跑、Flash 写不进去4.1 现象升级完复位App 不运行串口无输出原因App 的向量表没重映射或者 Bootloader 跳转前没关中断。也有可能是 App 的链接脚本里起始地址还是 0x08000000编译出来的复位向量指向了错误的位置。解决检查 App 的nvic_vector_table_set是否在 main 第一行调用检查 Bootloader 跳转函数里__disable_irq和SysTick-CTRL 0是否执行检查 App 的分散加载文件.sct或链接脚本里 ROM 起始地址是否改成了 0x08004800。4.2 现象Flash 写入返回错误或者写进去读出来全是 0xFF原因目标页没擦除或者擦除的页号和写入地址不匹配。GD32F103 的页大小可能是 1KB 也可能是 2KB如果按 1KB 算页号实际是 2KB 一页就会擦错页。解决先确认芯片具体型号的页大小用fmc_page_erase的时候页地址要按实际页大小对齐。写之前先读一下目标地址如果是 0xFF 说明擦干净了否则先擦再写。4.3 现象串口接收丢数据升级到一半卡死原因在中断里直接写 Flash导致中断响应被阻塞后续字节丢失。或者环形缓冲区太小一帧数据超过缓冲区容量。解决中断只做接收和入队主循环出队解析。缓冲区至少 1KB如果固件包大可以分帧传输每帧不超过 512 字节。写 Flash 的时候先把串口接收关掉写完再开或者用 DMA 接收。4.4 现象跳转到 App 后第一次中断就复位原因App 的栈顶地址设置不对或者 App 的启动文件里堆栈大小不够。Bootloader 跳转时__set_MSP用的是 App 向量表第一个字如果 App 编译出来的栈顶地址是 0x20000000 但实际 RAM 没那么多一压栈就硬件错误。解决检查 App 的启动文件里 Stack_Size 是否合理通常 0x400 到 0x800 够用。检查__set_MSP传入的地址是否在 RAM 范围内。可以在跳转前打印一下 stack_top 的值确认它在 0x20000000 到 0x20005000 之间。4.5 现象升级标志写不进去每次复位都进 Bootloader原因参数区所在的页没有擦除就直接写或者写标志的时候没解锁 Flash。也有可能是参数区和 App 区重叠了写标志的时候把 App 的代码擦了。解决参数区单独占一页写之前先擦这一页。写标志用fmc_halfword_program或者fmc_word_program别用 memcpy。检查分区表确保参数区地址和 App 起始地址不重叠。5. 进阶技巧用 YMODEM 协议传固件以及一个验证跳转是否成功的小方法串口 IAP 最常用的传输协议是 YMODEM比自定义帧格式省事因为 SecureCRT、Xshell 这些终端都支持发送 YMODEM 文件。Bootloader 里实现一个精简版 YMODEM 接收端大概 200 行代码支持 1KB 数据包和 CRC 校验。关键点YMODEM 每包 1024 字节第一包是文件名和大小最后一包是空包。接收的时候按包写 Flash写完一包回 ACK超时回 NAK。这样升级 100KB 的固件大概 20 秒比自定义协议稳。验证跳转是否成功我一般会在 App 的 main 函数开头翻转一个 GPIO用示波器或者 LED 看。如果跳转成功这个 GPIO 会立刻有波形如果没反应说明跳转失败或者向量表没搬对。另一个方法是读 SCB-VTOR 寄存器的值在 App 里打印出来确认它等于 App 起始地址。这两个方法比串口打印更直接因为串口本身可能还没初始化。从那以后我每次做 IAP都强制走一遍“擦除-写入-校验-跳转-点灯”的完整流程不看到 LED 闪绝不往下调业务代码。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

OpenClaw:从云端对话到本地数字员工的开源AI助手

OpenClaw:从云端对话到本地数字员工的开源AI助手

1. 从“云端对话盒子”到“本地数字员工”:OpenClaw想解决的问题159K Star的开源AI助手,放在整个开源托管平台上都是相当罕见的量级。我最初看到这个数字时,第一反应是:又一个把大模型API包一层壳的聊天项目。直到我把OpenClaw拉下…

📅 2026/10/21 13:22:15
Python微博数据爬取实战:从登录状态到JSON解析的完整工程

Python微博数据爬取实战:从登录状态到JSON解析的完整工程

简介:这是一份面向Python爬虫爱好者的微博数据抓取实战资源,覆盖评论、转发、用户搜索三类典型采集任务,既适合入门者理解爬虫原理,也能为开发者提供可复用的代码范例。资源提供可直接运行的脚本,以及项目说明和依赖清…

📅 2026/10/21 13:17:14
Claude Code编程实战:从快速上手到高效工作流

Claude Code编程实战:从快速上手到高效工作流

1. 从一个问题开始:Claude Code到底能帮我做什么很多开发者第一次接触Claude Code的时候,心里其实就一个疑问:这玩意儿和我直接在网页上跟AI聊天有什么区别?我最初也是这么想的——网页上能写代码、能解释代码、能生成注释&#x…

📅 2026/10/21 13:17:14
MORE NEWS

更多资讯

📰

1 个 AI 项目从 0 到 1:完整流程——好的开始,是成功的一半

1 个 AI 项目从 0 到 1:完整流程——好的开始,是成功的一半 这是《AI数字员工系统设计与企业自动化实战》日志连载第 29 篇 作者:老高FDE-AI实战前沿 深度聚焦:企业AI落地实战、AI-Workforce、AI数字员工实战、企业Agent组织设计、…

📰

深入理解Cargo与crates.io:从构建优化到发布实践

从 cargo new 跑通第一个 hello world,到能熟练地在项目里加依赖、跑测试,这中间其实隔着一道很有意思的分水岭。很多人对 Cargo 的印象停留在“包管理工具”这六个字上,知道 cargo build 、 cargo run 、 cargo add 这些高频命令&am…

📰

团队目标管理PPT:从OKR拆解到自动看板的落地闭环

简介:这份《团队目标管理.pptx》面向企业管理者、团队负责人及希望提升目标管理能力的职场人,围绕团队目标的作用、常见目标失效原因、目标制定方法与执行落地展开,帮助解决目标模糊、频繁变动、执行脱节等实际问题。资源包内含1个pptx文件&a…

📰

团队目标管理.pptx:从对齐到复盘的落地工具与避坑指南

简介:这份《团队目标管理.pptx》面向企业管理者、团队负责人及希望提升目标管理能力的职场人,围绕团队目标的作用、常见目标失效原因、目标制定方法与执行落地展开,帮助解决目标模糊、频繁变动、执行脱节等实际问题。资源包内含1个pptx文件&a…

📰

多旋翼无人机鲁棒姿态控制:从PID到H∞的实战指南

去年夏天,我把一套H∞姿态控制器搬上模拟项目X的试验台,看着屏幕上滚转角曲线终于不再乱甩的时候,第一反应不是兴奋,而是有点想骂自己——为了调好之前那套PID参数,我前前后后改了十几个版本,加陷波器、加前…

📰

USB供电充电电路设计:从原理到TP4056实战与避坑指南

简介:这是一份面向电子工程师、硬件设计初学者及便携设备开发者的USB供电充电电路技术资料,围绕USB总线取电为锂电池充电这一核心问题,梳理从原理到电路实现的完整思路。资源为单个PDF文档,压缩包约684KB,内容以电路图…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬