以赛元单片机为例讲解:
IAP(In-Application Programming,在应用中编程) 是一种允许微控制器在运行用户程序的同时,通过软件手段对自身FLASH存储器进行编程(擦除/写入) 的技术。
一、IAP的核心定义
与传统编程方式的对比
| 编程方式 | 如何编程 | 是否需要外部工具 | 应用场景 |
|---|---|---|---|
| ICP (In-Circuit Programming) | 通过编程器/调试器 | 需要专用编程器 | 工厂量产、初次烧录 |
| ISP (In-System Programming) | 通过BootLoader+通信接口 | 需要上位机软件 | 板级测试、维修升级 |
| IAP (In-Application Programming) | 用户程序自身操作 | 无需外部工具 (可通过网络等) | 远程升级、参数存储 |
关键区别:
-
ICP/ISP:需要停止用户程序,进入特殊模式
-
IAP:不停止 用户程序,在运行中更新FLASH
二、IAP的典型应用场景
1. 固件在线升级(OTA/FOTA)
// 用户程序中的IAP升级流程
void UserApp_Main(void) {
while(1) {
// 正常执行应用程序功能
Process_User_Tasks();
// 检查是否需要升级(如通过网络、串口)
if(Check_Upgrade_Request()) {
// 调用IAP函数,自我更新
Prepare_For_IAP_Update();
IAP_Erase_And_Program_New_Firmware();
Reboot_System(); // 重启运行新程序
}
}
}
2. 动态数据存储
// 使用IAP功能存储运行参数
void Save_Runtime_Parameters(void) {
// 在FLASH中存储数据(非易失性)
IAP_Erase_Sector(DATA_SECTOR_ADDR);
IAP_Program(DATA_SECTOR_ADDR, parameters, sizeof(parameters));
// 可以继续运行,无需重启
Continue_Normal_Operation();
}
3. 自编程实现功能切换
// 通过IAP修改程序自身行为
void Dynamic_Feature_Enable(void) {
// 例如:解锁高级功能
if(license_valid) {
// 修改特定FLASH位置,使能功能
IAP_Program(FEATURE_FLAG_ADDR, ENABLE_CODE, 4);
}
}
三、IAP的工作原理
1. 硬件基础
-
自编程能力 :芯片FLASH控制器支持自我编程
-
独立总线:指令总线和数据总线分离(哈佛架构)
-
特殊寄存器:IAP操作专用控制寄存器
2. 操作流程(以赛元单片机为例)
// 典型的IAP操作序列
uint8_t IAP_Program_Flash(uint32_t addr, uint8_t *data, uint16_t len) {
// 步骤1:解锁IAP功能(特定序列)
IAP_CONTR = 0x5A; // 解锁密钥1
IAP_CONTR |= IAP_EN; // 使能IAP
// 步骤2:设置操作模式
IAP_CMD = IAP_PROGRAM_CMD; // 编程命令
IAP_ADDRH = (addr >> 8); // 地址高字节
IAP_ADDRL = (addr & 0xFF); // 地址低字节
// 步骤3:写入数据
for(uint16_t i = 0; i < len; i++) {
// 注意:不同架构写法不同
*((__xdata uint8_t *)addr + i) = data[i];
}
// 步骤4:触发操作(特定序列)
IAP_TRIG = 0xA5;
IAP_TRIG = 0x5A; // 立即执行
// 步骤5:等待完成并检查状态
while(IAP_CONTR & IAP_BUSY); // 等待完成
// 步骤6:重新锁定
IAP_CONTR = 0; // 禁用IAP
return IAP_STATUS; // 返回操作状态
}
3. 关键限制与注意事项
// IAP操作的重要限制
void IAP_Important_Limits(void) {
// 1. **不能擦除/写入正在执行的代码**
// 会导致崩溃或未定义行为
// 2. **必须按页/扇区操作**
// FLASH最小擦除单位是扇区(如512字节)
// 3. **需要特殊电压/时钟条件**
// 有些芯片要求特定电压下才能IAP
// 4. **操作期间中断影响**
// 通常需要关闭中断或使用特殊处理
}
四、IAP vs BootLoader
很多人混淆这两个概念,其实它们不同但相关:
1. BootLoader + IAP 组合方案
┌─────────────────────────────────────────────────┐
│ FLASH 存储器 │
├──────────────┬────────────────┬─────────────────┤
│ BootLoader │ 应用程序区 │ 备份区/参数区 │
│ (8KB) │ (56KB) │ (8KB) │
└──────────────┴────────────────┴─────────────────┘
↑ ↑ ↑
启动代码 通过IAP更新自己 通过IAP读写数据
2. 工作模式对比
/* 方案A:传统BootLoader更新(ISP)*/
上电 → BootLoader → 检查升级 → 接收新固件 → 写入APP区 → 跳转APP
/* 方案B:IAP自我更新 */
APP运行 → 检测升级 → 接收新固件 → IAP写入自身 → 重启验证
3. 混合方案(推荐)
// 分阶段IAP实现
void Smart_IAP_System(void) {
// 阶段1:BootLoader(不可更新)
// 负责最基础的恢复和紧急更新
// 阶段2:应用程序(可IAP更新)
// 包含主要功能,支持自我更新
// 阶段3:备份应用程序
// 当主应用程序损坏时回滚
}
五、IAP的具体实现步骤
1. 安全准备阶段
bool Prepare_For_IAP_Update(void) {
// 1. 验证新固件的合法性
if(!Verify_Firmware_Signature(new_firmware)) {
return false;
}
// 2. 检查FLASH空间
if(!Check_Flash_Space_Available()) {
return false;
}
// 3. 保存关键状态到RAM
Save_Critical_State_To_RAM();
// 4. 禁用不必要的中断和外设
Disable_Interrupts_And_Peripherals();
return true;
}
2. 双缓冲区更新策略
// 避免更新失败变砖的常用方案
void Safe_IAP_Update(void) {
// FLASH分区布局:
// 区A: 当前运行程序 (v1.0)
// 区B: 下载缓存区 (空)
// 区C: 备份程序 (v1.0备份)
// 步骤1:下载新固件到区B
Download_To_Sector_B(new_firmware);
// 步骤2:验证区B的程序
if(Verify_Sector_B() == PASS) {
// 步骤3:复制当前程序到区C(备份)
Copy_Sector_A_To_Sector_C();
// 步骤4:擦除区A并写入新程序
IAP_Erase_Sector_A();
IAP_Program_Sector_A_From_Sector_B();
// 步骤5:验证并决定
if(Verify_Sector_A() == PASS) {
// 成功:运行新程序
Reboot_And_Run_New_Firmware();
} else {
// 失败:从区C恢复
Restore_From_Sector_C();
}
}
}
3. 通信协议设计
// IAP通信协议示例
typedef struct {
uint8_t start_flag; // 0xAA
uint8_t command; // 命令字
uint32_t address; // FLASH地址
uint16_t length; // 数据长度
uint8_t data[256]; // 数据载荷
uint16_t crc16; // CRC校验
uint8_t end_flag; // 0x55
} IAP_Packet_t;
// 常用命令集
#define IAP_CMD_CONNECT 0x01 // 连接
#define IAP_CMD_ERASE 0x02 // 擦除扇区
#define IAP_CMD_WRITE 0x03 // 写入数据
#define IAP_CMD_READ 0x04 // 读取验证
#define IAP_CMD_EXECUTE 0x05 // 执行新程序
#define IAP_CMD_RESET 0x06 // 复位系统
六、实际开发注意事项
1. 中断处理策略
// 方法1:完全禁用中断(简单但可能丢数据)
void IAP_Operation_With_IRQ_Disabled(void) {
EA = 0; // 关闭全局中断
Perform_IAP_Operation();
EA = 1; // 恢复中断
}
// 方法2:重映射中断向量到RAM(高级方案)
void IAP_Operation_With_IRQ_Remapped(void) {
// 1. 复制中断向量表到RAM
Copy_IVT_To_RAM();
// 2. 重映射中断到RAM中的向量表
Remap_Interrupt_Vector_To_RAM();
// 3. 执行IAP操作(中断可正常响应)
Perform_IAP_Operation();
// 4. 恢复原中断向量
Restore_Original_IVT();
}
2. 电源稳定性要求
// IAP期间必须保证电源稳定
void Check_Power_Before_IAP(void) {
// 1. 检查电压
if(Get_VCC_Voltage() < MIN_IAP_VOLTAGE) {
Postpone_IAP_Operation();
return;
}
// 2. 启用看门狗防止死锁
Enable_Watchdog_Timeout(IAP_TIMEOUT);
// 3. 可能需要的特殊操作
if(Chip_Requires_Special_IAP_Voltage()) {
Enable_IAP_Voltage_Regulator();
Wait_For_Voltage_Stable();
}
}
3. 错误恢复机制
// 实现可靠的IAP错误处理
IAP_Status_t Robust_IAP_Update(void) {
IAP_Status_t status = IAP_SUCCESS;
for(uint8_t retry = 0; retry < MAX_RETRIES; retry++) {
status = Try_IAP_Operation();
switch(status) {
case IAP_SUCCESS:
return IAP_SUCCESS;
case IAP_VOLTAGE_ERROR:
Adjust_Power_Supply();
break;
case IAP_VERIFY_ERROR:
// 验证失败,重新传输
Request_Data_Retransmission();
break;
case IAP_ADDRESS_ERROR:
// 地址错误,不可恢复
return IAP_FATAL_ERROR;
default:
Delay_ms(RETRY_DELAY_MS);
break;
}
}
// 所有重试失败
Enter_Recovery_Mode();
return IAP_MAX_RETRIES_EXCEEDED;
}
七、IAP的高级应用
1. 差分升级(减少数据传输量)
// 只传输变化的部分,不是整个固件
void Differential_IAP_Update(void) {
// 1. 计算当前固件和新固件的差异
Calculate_Delta(current_firmware, new_firmware, &delta);
// 2. 仅传输差异数据
Transmit_Delta_Data(delta);
// 3. 在设备端应用差异
Apply_Delta_To_Current_Firmware(delta);
}
2. A/B分区无缝切换
// 实现无感知固件更新
void Seamless_A_B_Switching(void) {
// 分区A: v1.0 (当前运行)
// 分区B: v2.0 (已更新)
// 1. 在后台更新分区B
Update_Partition_B_Background();
// 2. 验证分区B的完整性
if(Verify_Partition_B() == PASS) {
// 3. 更新引导标志(下次启动到B)
Update_Boot_Flag_TO_B();
// 4. 正常完成本次运行
// 下次重启自动运行新版本
}
}
3. 远程IAP(物联网设备)
// 通过网络进行IAP更新
void Remote_IAP_Over_Network(void) {
// 1. 接收更新通知(MQTT/HTTP/CoAP)
if(Received_OTA_Update_Command()) {
// 2. 安全验证
if(Authenticate_Update_Server()) {
// 3. 分段下载固件
while(!Download_Complete()) {
Download_Next_Fragment();
Store_Fragment_To_Flash();
}
// 4. 验证并切换
if(Verify_Downloaded_Firmware()) {
Schedule_Reboot_For_Update();
}
}
}
}
八、总结:IAP的核心价值
-
现场升级能力:无需返厂或使用专用工具
-
产品生命周期管理:可以持续改进和修复bug
-
参数非易失存储:替代部分EEPROM功能
-
功能动态配置:通过更新改变设备行为
-
降低维护成本:远程解决问题,减少现场服务
关键要点:
-
IAP是自我编程能力,不是特定的编程方法
-
必须考虑安全性 和可靠性,防止设备变砖
-
通常与BootLoader配合实现更稳健的更新机制
-
需要深入了解芯片的FLASH架构 和限制
IAP是现代嵌入式系统的标配功能,尤其对于需要远程维护或长期现场运行的设备至关重要。