IAP (In-Application Programming):應用程式運行期間,透過外部介面 (UART/USB/CAN/Ethernet/Wireless) 接收新固件,**自行寫入內部 Flash**、**驗證**、**跳轉執行**,**無需外部程式器、無需硬體跳線、無需進入 ISP 模式**。
區別:
SCB->VTOR)。0x0800 0000 ┌────────────────────────┐
│ IAP Bootloader (16~64KB)│ 起始 0x0800 0000
│ - 初始化外設 │
│ - 檢測升級條件 (按鍵/標誌) │
│ - 接收固件寫入 App 區 │
│ - 校驗 + 跳轉 App │
0x0800 4000 ├────────────────────────┤ ← APP_START_ADDR (例 0x0800 4000, 16KB 對齊)
│ User Application │
│ - 向量表在 0x0800 4000 │
│ - 主程式邏輯 │
│ - 可再含 IAP 客戶端邏輯 │
0x080F FFFF └────────────────────────┘ (視 Flash 大小)
兩大鐵律:
SCB->VTOR = APP_START_ADDR,否則中斷會跑到 IAP 區導致崩潰。上電復位
│
▼
┌─────────────────┐
│ IAP Bootloader │
│ 初始化 UART/USB │
│ 檢測升級標誌/按鍵│
└────────┬────────┘
│
┌────┴────┐
▼ ▼
升級 無升級/超時
│ │
▼ ▼
┌────────┐ ┌──────────────────┐
│ 接收固件│ │ 跳轉至 App │
│ 寫入Flash│ │ SCB->VTOR=APP │
│ 校驗CRC │ │ 跳轉 ResetHandler│
└────┬───┘ └────────┬─────────┘
│ │
▼ ▼
重新復位 ──────► 運行 App
(或軟體跳轉) │
▼
┌──────────────┐
│ App 主循環 │
│ 可觸發 IAP │
│ 請求復位升級 │
└──────────────┘
STM32F103_IAP.ld (或 KEIL Target 選項 IROM1 Start=0x08000000 Size=0x4000 (IAP), IROM2 Start=0x08004000 Size=0x3C000 (App))
#define APP_START_ADDR 0x08004000
#define APP_MAX_SIZE (256*1024 - 0x4000) // 256KB Flash - 16KB IAP
typedef void (*pFunction)(void);
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init(); // 或 USB/CAN
// 檢測升級條件:按鍵按下 或 特定標誌位
if (HAL_GPIO_ReadPin(KEY_GPIO_PORT, KEY_PIN) == GPIO_PIN_RESET
|| checkUpgradeFlag()) {
clearUpgradeFlag();
iapProcess(); // 進入升級流程
}
// 無升級需求,跳轉 App
jumpToApp();
while(1); // 不會返回
}
void jumpToApp(void) {
// 1. 檢查 App 區向量表合法性 (堆疊指標在 SRAM 範圍內)
uint32_t app_sp = *(__IO uint32_t*)APP_START_ADDR;
if ((app_sp & 0x2FFE0000) != 0x20000000) return; // 非法,停在 IAP
// 2. 關閉所有中斷、外設、SysTick
__disable_irq();
HAL_RCC_DeInit();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
// 3. 設定 MSP 並跳轉
__set_MSP(app_sp);
pFunction app_reset = (pFunction)*( (__IO uint32_t*)(APP_START_ADDR + 4) );
app_reset();
}
void iapProcess(void) {
uint32_t flash_addr = APP_START_ADDR;
uint8_t buffer[1024];
uint16_t len;
uint32_t total = 0;
// 擦除 App 區所有扇區
FLASH_EraseAppArea(APP_START_ADDR, APP_MAX_SIZE);
// 循環接收數據包 (簡化版,實際需 XMODEM 協議)
while (1) {
len = uartReceivePacket(buffer, sizeof(buffer), 5000); // 5s 超時
if (len == 0) break; // 超時或傳輸結束
if (verifyPacket(buffer, len)) {
// 寫入 Flash
if (FLASH_Write(flash_addr, buffer, len) != HAL_OK) {
sendNAK(); break;
}
flash_addr += len;
total += len;
sendACK();
} else {
sendNAK();
}
}
if (total > 0 && verifyAppCRC(APP_START_ADDR, total)) {
setUpgradeSuccessFlag(); // 標誌升級成功
NVIC_SystemReset(); // 復位跳轉 App
}
}
SystemInit() 之後、main() 之前)// system_stm32f1xx.c 或啟動檔中
void SystemInit(void) {
// ... 原有時鐘初始化 ...
// IAP App 向量表偏移
#ifdef VECT_TAB_OFFSET
SCB->VTOR = VECT_TAB_OFFSET; // 定義為 0x08004000
#endif
}
KEIL 中:Options for Target → Target → IROM1 Start: 0x08004000,並在啟動檔 startup_stm32f10x.s 或 system_stm32f1xx.c 設定 VECT_TAB_OFFSET。
void requestIAPUpgrade(void) {
setUpgradeFlag(); // 寫入備份寄存器或 Flash 標誌位
NVIC_SystemReset(); // 系統復位,IAP 檢測標誌進入升級
}
// 備份寄存器標誌 (RTC 備份域,掉電保持)
#define UPGRADE_FLAG_REG BKP_DR1
#define UPGRADE_FLAG_VAL 0xA5A5
void setUpgradeFlag(void) {
HAL_PWR_EnableBkUpAccess();
HAL_RTCEx_BKUPWrite(&hrtc, UPGRADE_FLAG_REG, UPGRADE_FLAG_VAL);
}
uint8_t checkUpgradeFlag(void) {
return (HAL_RTCEx_BKUPRead(&hrtc, UPGRADE_FLAG_REG) == UPGRADE_FLAG_VAL);
}
| 風險 | 對策 |
|---|---|
| 傳輸中斷導致 App 損壞 | **雙 Bank / 備份區** (F4/F7/H7 支援) 或 **A/B 區雙映像**:IAP 寫入備用區,校驗通過再切換啟動位址或交換 Bank。 |
| 固件被竄改/偽造 | **簽名驗證**:RSA/ECDSA 簽名 + 公鑰內建;**加密傳輸**:AES-GCM。 |
| 升級過程掉電 | **斷點續傳**:記錄已寫入位址;**原子性切換**:雙映像 + 狀態機。 |
| 版本回滾攻擊 | **版本號單調遞增**,拒絕舊版本;**防重放**:Nonce/時間戳。 |
| 通訊介面被佔用 | **多通道備援**:UART + USB + CAN 同時監聽。 |
IROM1 Start=0x08000000, Size=0x4000 (16KB)。IROM1 Start=0x08004000, Size=0x3C000 (剩餘空間)。Options for Target → Linker → Scatter File 使用相同散佈檔或手動設定起始位址。startup_stm32f10x.s,App 專案啟用 VECT_TAB_OFFSET 宏定義。STM32 IAP 核心在於:**記憶體劃分合理、向量表正確重定向、Flash 擦寫可靠、通訊協議健壯、雙映像/簽名保障安全**。掌握此技術,能讓產品擁有「終身進化」能力,從容應對現場 Bug 修復、功能迭代、安全補丁,是嵌入式工程師從「寫驅動」邁向「架構產品」的關鍵里程碑。
Debian Linux 實現 ADSL 撥號上網與動態域名自動更新
Bash PS1 提示符定制完全指南:變量含義、顏色設置、持久化配置
分散式計算開源框架 Hadoop 入門實戰:從單機到叢集部署完整紀錄
Linux TLB (Translation Lookaside Buffer) 核心機制與效能影響深度解析
Linux 系統備份方案對決:tar 指令 vs Clonezilla 完整優缺點分析與實作步驟
RHEL 5.4 下安裝配置 JDK 完整步驟:RPM 安裝與環境變量設置
Linux LVM 安全移除物理卷 (PV) 完整實戰:縮減邏輯卷、pvchange 離線、pvmove 資料遷移、vgreduce 移除、掛載驗證零資料遺失