IAP_2_IAP执行流程、实现思路

本文详细介绍了IAP(在线编程)程序更新的过程,包括程序执行流程、bin文件的应用及存储发送方式,并提供了一种基于STM32的IAP实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、IAP的程序执行流程

被更新的单片机要有两个程序,一个是“Bootloader”,一个是“APP”。

首先执行“Bootloader”,用来决定是否更新APP,接收串口数据,然后跳转到真正想运行的“APP”,具体运行流程图如下。

  1. 从栈顶地址开始,从“中断向量表1”找到复位中断,处理复位中断函数;
  2. 执行IAP的main函数;
  3. 跳转到“中断向量表2”,找到复位中断,处理复位中断函数;
  4. 执行APP的main函数;

在这里插入图片描述

2、关于bin文件

2.1、为什么要用bin文件

一般情况下,我们生成的是hex文件,hex文件是以ASCII文本形式保存编译后的二进制文件信息,使用各种烧录软件实际上就是把这些工具把hex文件转换成二进制的bin文件,烧录到单片机当中。

在IAP更新程序的时候,我们就相当于这些烧录软件,直接使用bin文件,跳过了hex转成bin文件的这一步。

hex文件包含了地址和校验的信息,利用hex文件加上烧录工具,会更加安全。

2.2、bin文件的存储和发送方式

可以用电脑的串口发送软件(有某些协议的上位机),也可以用单片机的串口发送。

如果用电脑来存储bin文件,无疑是非常简单的,不过发送bin文件的话,更新程序的时候就要带电脑,还要在电脑上进行操作,不太方便。

如果使用单片机的话,需要事先把bin文件存在Flash当中,或者SD卡当中,发送就通过串口。

3、IAP方案简介

用一个STM32F103做为存储和发送bin文件的地方,用的是正点原子的Mini板,有屏幕,方便显示信息,也有例程可以修改来用,就叫做“STM32_TX”。

另一个STM32F103就是需要被升级的了,不妨称为“STM32_RX”。

3.1、把bin文件存到STM32当中

把STM32外加的Flash或SD卡模拟成U盘,利用文件系统,当插入USB的时候,就相当于插入U盘,这时候把bin文件直接复制粘贴到U盘中就可以了。

需要用到的知识:读写外部Flash、读写SD卡、文件系统、USB等。

3.2、把bin文件发送出去

利用STM32串口,要考虑到分包、校验、重发机制。

分包的原因:一个简单的程序也往往是几十K字节的,一次性发送,接收端的SRAM没有那么大的缓存,所以需要将bin文件分成一个个数据包,比如1K大小。

校验:打算就先用CRC8校验。

重发:超时重发,出错重发,重发次数最大为10.

3.3、接收端的处理

每次发送1K字节,接收方接收1K字节,校验有误申请重发,校验无误之后写入自己的内部Flash。

当整个bin文件全部接收并写入完成之后,跳转到APP执行。

#ifndef _EEPROM_H_ #define _EEPROM_H_ #define SECTOR_SIZE 512 sfr ISP_DATA=0xe2; sfr ISP_ADDRH=0xe3; sfr ISP_ADDRL=0xe4; sfr ISP_CMD=0xe5; sfr ISP_TRIG=0xe6; sfr ISP_CONTR=0xe7; void Read_FixTime(unsigned int addr1,unsigned char Fixtime[][10]); void Save_FixedTime(unsigned int addr2,unsigned char Savetime[][10]); #endif#include <reg52.h> #include "EEPROM.h" #include "Delay.h" void IAP_Dsiable(void); static void IAP_Trigger(void); void IAP_EraseSector(unsigned int sector_addr); void IAP_data_w(unsigned char dat,unsigned int addr); void IAP_Dsiable(void) { ISP_CMD = 0x00; ISP_TRIG = 0x00; ISP_CONTR = 0x00; } static void IAP_Trigger(void) { ISP_TRIG = 0x46; ISP_TRIG = 0xB9; } void IAP_EraseSector(unsigned int sector_addr) { // 扇区地址应该是扇区的起始地址,必须是512字节对齐 unsigned int sector_start = sector_addr & 0xFE00; EA=0; ISP_CONTR = 0x81; // 使能IAP ISP_CMD = 0x03; // 擦除命令 ISP_ADDRL = sector_addr; ISP_ADDRH = sector_addr >> 8; IAP_Trigger(); // 触发擦除 IAP_Dsiable(); EA=1; } void IAP_data_w(unsigned char dat,unsigned int addr) { ISP_CONTR=0x81; ISP_CMD=0x02; ISP_ADDRL=addr; ISP_ADDRH=addr>>8; ISP_DATA=dat; IAP_Trigger(); IAP_Dsiable(); } //读取EEPORM时间 void Read_FixTime(unsigned int addr1,unsigned char Fixtime[][10]) { unsigned int i,j; EA=0; for(i=0;i < 12;i++) { for(j=0;j <10 ;j++) { ISP_CONTR = 0x81; ISP_CMD = 0x01; ISP_ADDRL = addr1; ISP_ADDRH = addr1 >> 8; IAP_Trigger(); Fixtime[i][j] = ISP_DATA; IAP_Dsiable(); if(Fixtime[i][j] == '\0') { j++; break; } addr1++; } if(j<10) { Fixtime[i][j]='\0';} } EA=1; } //存储EEPORM时间 void Save_FixedTime(unsigned int addr2,unsigned char Savetime[][10]) { unsigned int i,j; IAP_EraseSector(addr2); EA=0; for(i=0;i < 12 ;i++) { for(j=0;j < 10 && Savetime[i][j]!= '\0';j++) { IAP_data_w(Savetime[i][j],addr2); addr2 ++; } if (j < 10) { ISP_CONTR = 0x81; ISP_CMD = 0x02; ISP_ADDRL = addr2 ; ISP_ADDRH = addr2 >> 8; ISP_DATA = '\0'; IAP_Trigger(); IAP_Dsiable(); addr2++; } } EA=1; }在不改变原变量情况下优化该代码
07-10
#include<reg52.h> #include "Delay.h" #include "LED.h" #include "Segment.h" #include "Init.h" #include "Time.h" #include "Serial.h" #include "EEPROM.h" static unsigned char Curtime[][10]={"2025","-","07","-","02"," ","13",":","12",":","25","Wed"}; static unsigned int Channel_Index=0; static unsigned int Count=0; void main() { int n=0; int* p=&Channel_Index; Clock_Init(); Interrupt_Init(); En_Init(); Read_FixTime(0x2000,Curtime); while(1) { // Led(); Segdisp(Channel_Index,Curtime); if(Count>=19) { n++; if(n>=60) { Save_FixedTime(0x2000,Curtime); n=0; } } } } //外部中断KEY1 KEY2 void Key1_Hanlder() interrupt 0 { Channel_Index=0; } void Key2_Hanlder() interrupt 2 { Channel_Index=1; } void Clock_interrput() interrupt 1 { static int Count=0; TH0 = 0x3C; TL0 = 0xB0; Count++; if(Count>=20) { Update_time(Curtime); Serial_send(Curtime); Count=0; } } void Serial_interrput() interrupt 4 { static unsigned char frame_buffer[24]; // 缓冲区(22数据+1结束符) static unsigned int frame_index = 0; unsigned char ch; // 缓冲区索引 if (RI) // 处理接收中断 { RI = 0; // 清除标志 ch = SBUF; // 读取字符 // 结束信号检测 if (frame_index >=22&&ch=='*') //设定结束符号 { frame_buffer[frame_index] = ch; // 年份 (4字节) Curtime[0][0] = frame_buffer[0]; Curtime[0][1] = frame_buffer[1]; Curtime[0][2] = frame_buffer[2]; Curtime[0][3] = frame_buffer[3]; Curtime[0][4] = '\0'; // 结束符 // 分隔符 (1字节) Curtime[1][0] = frame_buffer[4]; Curtime[1][1] = '\0'; // 月份 (2字节) Curtime[2][0] = frame_buffer[5]; Curtime[2][1] = frame_buffer[6]; Curtime[2][2] = '\0'; // 分隔符 (1字节) Curtime[3][0] = frame_buffer[7]; Curtime[3][1] = '\0'; // 日期 (2字节) Curtime[4][0] = frame_buffer[8]; Curtime[4][1] = frame_buffer[9]; Curtime[4][2] = '\0'; // 空格 (1字节) Curtime[5][0] = frame_buffer[10]; Curtime[5][1] = '\0'; // 小时 (2字节) Curtime[6][0] = frame_buffer[11]; Curtime[6][1] = frame_buffer[12]; Curtime[6][2] = '\0'; // 冒号 (1字节) Curtime[7][0] = frame_buffer[13]; Curtime[7][1] = '\0'; // 分钟 (2字节) Curtime[8][0] = frame_buffer[14]; Curtime[8][1] = frame_buffer[15]; Curtime[8][2] = '\0'; // 冒号 (1字节) Curtime[9][0] = frame_buffer[16]; Curtime[9][1] = '\0'; // 秒钟 (2字节) Curtime[10][0] = frame_buffer[17]; Curtime[10][1] = frame_buffer[18]; Curtime[10][2] = '\0'; // 星期 (3字节) Curtime[11][0] = frame_buffer[19]; Curtime[11][1] = frame_buffer[20]; Curtime[11][2] = frame_buffer[21]; Curtime[11][3] = '\0'; frame_index = 0; // 重置索引 Serial_send(Curtime); } /** else if (frame_index >=22&&ch=='+') //设定闹钟结束符号 { frame_buffer[frame_index] = ch; // 年份 (4字节) Alarm[0][0] = frame_buffer[0]; Alarm[0][1] = frame_buffer[1]; Alarm[0][2] = frame_buffer[2]; Alarm[0][3] = frame_buffer[3]; Alarm[0][4] = '\0'; // 结束符 // 分隔符 (1字节) Alarm[1][0] = frame_buffer[4]; Alarm[1][1] = '\0'; // 月份 (2字节) Alarm[2][0] = frame_buffer[5]; Alarm[2][1] = frame_buffer[6]; Alarm[2][2] = '\0'; // 分隔符 (1字节) Alarm[3][0] = frame_buffer[7]; Alarm[3][1] = '\0'; // 日期 (2字节) Alarm[4][0] = frame_buffer[8]; Alarm[4][1] = frame_buffer[9]; Alarm[4][2] = '\0'; // 空格 (1字节) Alarm[5][0] = frame_buffer[10]; Alarm[5][1] = '\0'; // 小时 (2字节) Alarm[6][0] = frame_buffer[11]; Alarm[6][1] = frame_buffer[12]; Alarm[6][2] = '\0'; // 冒号 (1字节) Alarm[7][0] = frame_buffer[13]; Alarm[7][1] = '\0'; // 分钟 (2字节) Alarm[8][0] = frame_buffer[14]; Alarm[8][1] = frame_buffer[15]; Alarm[8][2] = '\0'; // 冒号 (1字节) Alarm[9][0] = frame_buffer[16]; Alarm[9][1] = '\0'; // 秒钟 (2字节) Alarm[10][0] = frame_buffer[17]; Alarm[10][1] = frame_buffer[18]; Alarm[10][2] = '\0'; // 星期 (3字节) Alarm[11][0] = frame_buffer[19]; Alarm[11][1] = frame_buffer[20]; Alarm[11][2] = frame_buffer[21]; Alarm[11][3] = '\0'; frame_index = 0; // 重置索引 } **/ else { // 存储有效数据 frame_buffer[frame_index++] = ch; } // 缓冲区溢出保护 if (frame_index >= 23 ) { frame_index = 0; // 重置缓冲区 return; } } if (TI) TI = 0; // 清除发送中断标志 }#include <reg52.h> #include "EEPROM.h" #include "Delay.h" void IAP_Dsiable(void); static void IAP_Trigger(void); void IAP_EraseSector(unsigned int sector_addr); void IAP_data_w(unsigned char dat,unsigned int addr); void IAP_Dsiable(void) { ISP_CMD = 0x00; ISP_TRIG = 0x00; ISP_CONTR = 0x00; } static void IAP_Trigger(void) { ISP_TRIG = 0x46; ISP_TRIG = 0xB9; } void IAP_data_w(unsigned char dat,unsigned int addr) { EA=0; ISP_CONTR=0x81; ISP_CMD=0x02; ISP_ADDRL=addr; ISP_ADDRH=addr>>8; ISP_DATA=dat; IAP_Trigger(); IAP_Dsiable(); EA=1;// 打开总中断 } void IAP_EraseSector(unsigned int sector_addr) { ISP_CONTR = 0x81; // 使能IAP ISP_CMD = 0x03; // 擦除命令 ISP_ADDRL = sector_addr; ISP_ADDRH = sector_addr >> 8; IAP_Trigger(); // 触发擦除 IAP_Dsiable(); } unsigned char IAP_data_r(unsigned int addr0) { unsigned char dat; EA=0; ISP_CONTR = 0x81; // 使能IAP,设置写命令 ISP_CMD = 0x01; ISP_ADDRL = addr0; ISP_ADDRH = addr0 >> 8; IAP_Trigger(); dat = ISP_DATA; IAP_Dsiable(); EA=1; return dat; } //读取EEPORM时间 bit Read_FixTime(unsigned int addr1,unsigned char Fixtime[][10]) { unsigned int i,j; const unsigned int field_len[] = {5,2,3,2,3,2,3,2,3,2,3,4}; // 各字段实际长度 if(IAP_data_r(addr1++) != TIME_MAGIC_NUMBER) { return 0; } for(i=0;i < 12;i++) { for(j=0;j <field_len[i] ;j++) { Fixtime[i][j] = IAP_data_r(addr1); addr1++; if(Fixtime[i][j]=='\0') { break; } } } return 1; } //存储EEPORM时间 void Save_FixedTime(unsigned int addr2, unsigned char Savetime[][10]) { const unsigned int field_len[] = {5,2,3,2,3,2,3,2,3,2,3,4}; // 各字段实际长度 unsigned int i, j; IAP_EraseSector(addr2); // 计算扇区起始地址 IAP_data_w(TIME_MAGIC_NUMBER,addr2++); for (i = 0; i < 12; i++) { for (j = 0; j < field_len[i]; j++) { if (j==(field_len[i]-1)) { IAP_data_w('\0',addr2++); } else { IAP_data_w(Savetime[i][j],addr2); addr2++; } } // 注意:如果该行没有结束符,我们会写入10个非结束符,这样读取时该行就没有结束符,但调用者应该保证每行有结束符。 } }#include<reg52.h> #include "Time.h" void Update_Time(unsigned char Time[][10]) { // 秒处理 (Time[10]) Time[10][1]++; if(Time[10][1] > '9') { Time[10][1] = '0'; Time[10][0]++; // 秒进位到分 (Time[8]) if(Time[10][0] >= '6') { Time[10][0] = '0'; Time[8][1]++; // 分进位到时 (Time[6]) if(Time[8][1] > '9') { Time[8][1] = '0'; Time[8][0]++; if(Time[8][0] >= '6') { Time[8][0] = '0'; Time[6][1]++; // 时进位到日 (Time[4]) if(Time[6][0] == '2') { if(Time[6][1] >= '4') { // 24时制 Time[6][1] = '0'; Time[6][0] = '0'; Time[4][1]++; // 日进位处理 if(Time[4][1] > '9') { Time[4][1] = '0'; Time[4][0]++; } if(Time[4][0] >= '3' && Time[4][1] > '1') { Time[4][0] = '1'; Time[2][1]++; // 月更新 } } } // 时十位<2时的进位 else if(Time[6][1] > '9') { Time[6][1] = '0'; Time[6][0]++; } } } } } }不改变原代码变量函数名能将断电时的日期与时间保存至 EEPROM,并且能够在重新上电后恢复断电前的日期与时间,继续计 时
07-10
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值