历史上的今天
今天是:2024年10月14日(星期一)
2019年10月14日 | LPC1768 IAP升级方法
2019-10-14 来源:eefocus
1、IAP介绍
IAP即“in applicatinprogramming”在应用编程的缩写,指MCU可以在系统中获取新代码并对自己重新编程,即改变应用程序。它与我们所熟悉的ISP编程不同,
LPC1768 的ISP编程接口为串口1,如果使用其他的串口或其他总线则不能对其进行编程。而我们这里所说的IAP通过下载一段引导程序Bootloader程序,如果我们想要从串口2或网口更新应用程序,在Bootloader中初始化相应的串口或网口,使其接收应用程序,将接收到的应用程序写入到Flash里面,IAP完成后跳转到应用程序入口执行应用程序。所以现在的IAP程序涉及到两个概念:Bootloader和应用程序。
Bootloader:BootLoader就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。这里我们所说的Bootloader也是系统开机前的一段小程序,其主要任务是用来初始化串口和IAP端口(网口CAN接口等)的,通过判断状态是否需要从IAP端口进行更新应用程序,若需要更新则从端口接收应用程序,并存放到指定的Flash里面,更新完成后则跳入到指定的Flash里面执行应用程序。
应用程序:即我们需要开发板实现功能的程序,其中应用程序主要分为两种:hex文件和bin文件。在我们经常使用的KEIL中默认编译生成的可执行文件(应用程序)为hex格式的,若需要编译生成bin格式需要做如下修改,加入“D:KeilARMARMCCbinfromelf.exe --bin --output ./Obj/Can_Updata.bin ./Obj/test.axf”,重新编译生成的Can_Updata.bin文件存放在Obj文件夹下。
2、bin格式文件与hex格式文件的区别
bin格式文件是纯粹的二进制文件,使用下载其将其下载到开发板时其内容完全不变,所以对于IAP下载使用bin格式文件是比较方便的,如下图是bin文件的内容与写入到开发板后使用仿真器观察到Flash存放的内容(这段程序当然是可以执行的)。
Hex格式文件:Hex全称(Intel HEX)文件是由一行行符合Intel HEX文件格式的文本所构成的ASCII文本文件。在Intel HEX文件中,每一行包含一个HEX记录。这些记录由对应机器语言码和/或常量数据的十六进制数数字组成。如下图是hex文件的部分数据,其组成由“:CCAAAARR...ZZ ”,CC=10代表长度为16字节,AAAA=0000本条记录中的数据在存储区中的起始地址,RR=00,数据区,ZZ=38为校验,这里就不做仔细说明了。
3、LPC1768 IAP原理
LPC1768复位后开始执行Boot代码,Boot代码可以执行ISP程序或用户的应用代码。发生硬件复位后,P2.10 引脚为低电平,这就被当作启动ISP命令处理器的外部硬件请求。假定在/RESET 引脚上出现上升沿时,电源引脚出现正确的信号,那么在采样P2.10 之前有3ms的时间决定是执行用户代码还是ISP 处理程序。如果P2.10 为低电平且看门狗溢出标志置位,那么忽略启动ISP 命令处理器的外部硬件请求。在没有ISP 命令处理器的请求(硬件复位后P2.10引脚为高电平)时,将搜索有效的用户程序。若发现有效的用户程序,执行控制权就被转移给用户程序。若没有找到有效的用户程序,就将调用自动波特率程序。这里不讨论ISP下载及命令,有兴趣的朋友可以查看LPC1768技术手册第三十二章ISP命令。
在IAP升级中,程序正常执行即用户代码(这里的用户代码是我们所说的IAP引导程序),如下是IAP升级流程图,程序将预留端口(这里提供有串口和CAN总线接口两种)接收到的APP程序bin文件,将接收到的数据写入到指定的Flash区域(例程APP地址为0x0001 0000),程序通过IAP命令将数据写入到Flash里面,LPC1768提供了一系列IAP命令对片内Flash进行擦除编写等
4、IAP命令
LPC1768通过IAP函数对片内Flash进行操作,IAP函数是固化在0x1FFF1FF1处的一个有传入参数和返回参数的一个函数,在LPC1768技术手册第三十二章IAP命令中有有详细的说明。主要提供有如下命令:准备下操作扇区、将RAM内容复制到Flash、清除扇区、扇区查空、读器件ID、读boot版本、比较、重新调用ISP等。
5、串口IAP升级
本例程是根据官方提供的串口IAP更新图片进行修改而来,直接使用官方的IAP.c文件,该文件中提供了如上图IAP命令的各种函数,其具体参数可以参考IAP命令。根据官方例程里面将bmp图片经过串口采用Xmodem1K协议发送到开发板存放在地址0x0001 0000,如下图是LPC1768 Flash分配地址,第16~21扇区为应用程序存放空间。这里我们将要传送的bmp图片改为传输应用程序bin文件
6、串口IAP程序分析
例程通过按键对开发板进行控制,INT0键擦除Flash,确认键等待串口IAP,向上键显示菜单,向下键执行应用程序,使用LCD来开发板状态
当程序全部写入到Flash后,按下向下按键,跳转到应用程序,首先修改中断向量表然后进入应用程序
void Boot( void )
{
SCB->VTOR = IMG_START_SECTOR & 0x1FFFFF80; //修改中断向量表
JMP_Boot(IMG_START_SECTOR);
}
堆栈地址更新,PC地址更新
__asm void JMP_Boot( uint32_t address ){
LDR SP, [R0] ;堆栈地址更新
LDR PC, [R0, #4] ;进入应用程序
}
7、操作步骤及实验现象
1、下载“宝马开发板串口IAP升级”例程,插上USB转串口线,打开超级终端,复位开发板。
2、按下按键INT0按键--擦除扇区
3、按下方向键确认键(即向下按)--等待接收串口程序
4、串口打印’C’字符等待接收数据
5、串口发送文件,选择“1K Xmodem”协议,选择要下载的应用程序bin文件,这里使用DAC例程作为测试。
6、点击“发送”,发送文件
7、发送完成
8、按下方向键向下键开始执行应用程序,这时我们可以使用示波器测试P0.26口输出正弦波信号
bin文件生成方法及设置:
打开要更新应用程序工程,这里使用“IAP升级DAC转换”程序,设置ROM空间地址(程序下载到Flash的地址),这里也是我们应用程序的入口地址0x10000
打开User选项,利用Keil自带的fromelf.exe生成bin文件,bin文件保存在Obj文件夹中,如下图添加“D:KeilARMARMCCbinfromelf.exe --bin --output ./Obj/app.bin ./Obj/app.axf”,输入文件为app.axf,所以工程编译生成输出文件名设置为app,命令执行生成app.bin文件
打开Asm选项,定义“NO_CRP”,我们可以打开启动文件,当定义了“NO_CRP”后,那么我们后面的代码也就不起作用了,所以在需要加密的时候前面就一定不能再定义了代码读保护,也就是加密的关键字,经过加密后芯片再也无法擦除,由于我们这里程序需要使用到IAP升级,因此添加此定义
编译即可生成app.bin
lpc1768 IAP疑点全解释
IAP简介:
IAP为在应用编程的简称,其作用是用户自己的程序在运行过程中对用户程序所在的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的程序进行更新升级。
Lpc1768存储器空间分配:
整体Flash布局:
分散加载描述文件:
分散加载描述文件是arm连接器提供的可以将程序中的代码段、数据段定位到flash中特定的物理地址的一种机制。通过此机制我们可以把给IAP程序和用户程序分别分配一部分空间并制定各自的起始地址以保证IAP程序和用户程序不会重叠。关于分散加载描述文件详细文档,请参考《RealView编译工具----链接器参考指南》第三章,对于IAP涉及到的分散加载文件的知识,我们只需知道以下几点。以本次IAP工程为例,我们给IAP升级代码留32K的空间(0x0000_0000~0x0000_7FFFF),剩余的给用户程序空间(即用户程序从地址0x0000_8000开始)。对于IAP程序部分的分散加载文件不做修改,对于用户程序部分修改如下:
![]()

我们仅仅对第5行和第6行做了修改,改动的地方做出了标注,其具体表示意思是:
Line5:0x0000_8000表示加载域的起始地址,即放在flash0x0000_8000地址处开始放置,0x0008_0000表示代码区、数据区的总共大小的最大值,程序文件超过此值将会报错,这里取默认值0x0008_0000即可。
Line6: 0x0000_8000表示执行域的起始地址,即程序从0x0000_8000地址处开始执行,0x0008_0000代表的意思参考上一行。
Line7:此行的作用是把中断向量表定位在起始地址处(这里是0x0000_8000).
要使用此分散加载描述文件,还需要将Target Opitions…->Linker下的Use Memory Layout from Target Dialog前的“√”去掉。
![]()
IAP函数的使用
Iap函数是固化在Boot Rom中地址0x1FFF1FF1处的一个有传入参数和返回参数的一个函数。对于不同的传入参数,iap函数实现不同的功能。关于这些功能的详细介绍,《LPC1768 user manual》32章第8节IAP commands一节中有详细介绍,这里不赘述。远程升级中我们常用到的几个iap命令是:读器件标识号、准备写操作扇区、擦除扇区、扇区查空、将RAM内容复制到Flash、比较<地址1><地址2><字节数>。
以准备写操作扇区为例说明iap函数的写法。首先定义iap函数的入口地址:
接着声明函数类型指针IAP_Entry:
初始化IAP函数指针使其指向IAP函数入口地址:
由用户手册可知iap命令汇总如下图:

Iap状态码汇总如下图:
![]()

据此写出IAP命令字和状态码宏定义如下:(PS:Command Code中的数字10表示十进制,如:Read part ID的命令字为5410,表示其命令字为十进制的54,见上图)

准备写操作扇区的命令解释如下图:

由上图可知准备写操作扇区需要三个参数,分别是Command code、Param0、Param1,返回状态码的可能取值为CMD_SUCCESS、BUSY、INVALID_SECTOR。据此我们写处准备写操作扇区的命令函数如下:
其中paramin[]、paramout[]为定义的uint32型全局数组。调用此函数时只需将起始扇区号传给arg1,结束扇区号传给arg2即可。其他命令的函数书写于此大同小异。
这里给出一个远程升级的iap流程:读取器件标识码确定是当前芯片→确定待升级程序(用户程序)占用的起始扇区号与结束扇区号→准备需占用扇区→擦除需占用扇区→扇区查空确定需占用扇区已成功擦除→执行将RAM复制到Flash命令将数据块复制到Flash→执行比较命令校验数据是否正确→如果正确执行下一数据块的复制。
需要说明的一点是:在还行IAP命令的时候,需要关闭中断以保证IAP命令的正确执行。幸运的是Cortex-M3提供了关闭/打开中断的指令CPSID I 和CPSIE I,而且在core_cm3.h中也提供这样的开关中断的函数__enable_irq()和__disable_irq(),需要的时候直接去调用就可以。
从bootloader到UsrApp的跳转:
这里我们把引导cpu进入用户代码区的程序称作bootloader,把用户实际实现相应功能的程序称作UsrApp,它们是一个完整的程序,下文提到的bootloader、UsrApp均指这些。
bootloader到UsrApp的跳转需要熟知两方面的知识:一个是中断向量表的重映射,另一个是一段完整的程序的入口是如何定义的。
中断向量表重映射:
在LPC1768中,位于地址0xE000_ED08处有一个向量表偏移寄存器VTOR,通过修改此寄存器可以设定向量表基址位于Code区或是RAM区以及向量表的基址偏移域,以此达到中断向量表重映射的目的。比如我们的UsrApp起始地址为0x8000;为使UsrApp在发生中断行为时不会产生错误或异常,我们可以通过以下代码段将中断向量表重映射到地址0x8000处Code区。
SCB->VTOR由core_cm3.c提供,对应向量表偏移寄存器地址0xE000_ED08。USR_APP_START_ADDR为用户代码区的起始地址,和数值0x1FFFFF80按位与是保证寄存器的保留位为0和向量表基址位于Code区。此寄存器的详细说明请参考《cortex-M3技术参考手册》第八章第二节的NVIC寄存器描述或《LPC17XX User manual》英文版34.4.3.5章节。,此处不再贴出。对于用户程序,在bootloader中的向量表偏移设置并不起作用,需要在用户程序中重新设置向量表偏移寄存器。原因是CM3器件进入main()函数即要求调用SystemInit(void)进行系统初始化,系统初始化的时候将向量表偏移寄存器清零了,所以需要在调用SystemInit()之后重新设置向量表偏移寄存器。有网文称对于应用了OS的用户程序需要这样做,其实对于开启了中断的的用户程序都需要这样,你的简单的IAP测试程序之所以在没有这样做的情况下通过了测试,是因为你的用户程序测试代码中并没有用到中断。
一个完整镜像的程序入口:
对于在flash中存储的一个完整的程序代码,其起始部分应该为向量表,向量表的内容格式固定如下表(参考《cortex-M3权威指南》7.3节)
上电后的向量表:

由表可知,对于起始存储地址为0的一段完整程序,其首地址处存放的是MSP的初始值,偏移4字节的地址处存放的是PC指针的初始值,我们要运行这段完整的程序,只需将这段完整程序的SP、PC初始值赋给SP和PC寄存器即可,具体实现的函数如下:
对于此函数的解释:__asm是MDK的编译器提供的嵌入汇编的指令(RealView C编译器3.0以上版本提供)。函数体中两行汇编代码的功能分别为:
LDR SP, [R0]:把R0中的值作为地址,将此地址中的值赋给SP
LDR PC ,[R0, #4]:把R0中的值加4作为地址,将此地址中的值赋给SP
这里涉及到一个问题,r0中的值是什么?我们根据ATPCS(ARM-THUMBprocedure call standard)可知,对于参数少于等于4的函数,参数是通过R0~R3传递的,第一个参数放在R0中,依次类推。所以这里的R0存放的正式UsrApp的起始地址,回过头再看前面的两行汇编代码,它们做的事正是将UsrApp的SP、PC初始值赋给相应寄存器,达到开始运行UsrApp的目的。
UsrApp的烧写:
因为在片内flash的起始地址处烧写的是我们的iap处理程序,用户程序的起始地址不是0x0000_0000,在通过keil用jlink下载程序的时候,还需做一个设置。在Option for Target→Debug→Settings→Flash Download中,设置下载程序时的起始地址为用户程序的起始地址。如本例中用户程序的起始地址是0x0000_8000,我们设置下载程序的起始地址为0x0000_8000,如下图。如果不这么做的话,通过jlink下载程序的时候会从地址0x0000_0000开始擦除。![]()
遗留的问题:
疑问1:《lpc1768 user manual》34.4.3.5章节指出中断向量表偏移寄存器的向量表基址偏移域为[28:8]位,为什么将偏移量实际赋值给此寄存器的时候没有做’<<8’的处理而是直接赋值过来?PS:《Cortex-M3技术参考手册》第8章的表8-15指出中断向量偏移寄存器的向量表基址偏移域为[28:7]
疑问2:手册中指出使用iap的时候RAM顶部32字节空出,这该怎么理解?iap占用ram顶端32字节,即使我不去刻意留出也不影响iap使用这32字节的空间,我能想到的唯一解释是编译器可能会把程序中的全局变量放到ram顶端32字节区域进而调用iap函数会引起全局变量被修改,是这样么?
如果你读到了这里并且你知道这些答案,敬请告知解惑。
参考过的文献:
《arm汇编指令集》--------网文
《arm启动代码的探究-郑远超》
《arm体系结构与编程》------杜春雷(PS:重点第11章)
《map文件认识初步》-------网文
《汇编器指南》------RealviewMDK
《链接器指南》------RealviewMDK
《中断向量表重映射与复制》------网文
《cortex-m3技术参考手册》
之前说了stm32的iap编程,今天天气真好,顺手就来说说lpc1788的iap编程(没看前面的请查看stm笔记下的内容)
首先是flash的算法,lpc1768并没有寄存器来让我们操作flash,他内置了iap的flash算法,在技术手册的525页有如下说明

其支持的iap命令有这些

这样我们就能够做出相关的flash读写借口呢(具体请查看lpc1768的技术手册)
unsigned param_table[5];//传递参数列表
unsigned result_table[5];//返回结果列表
//调用iap命令
void iap_entry(unsigned param_tab[],unsigned result_tab[])
{
void (*iap)(unsigned [],unsigned []);
iap = (void (*)(unsigned [],unsigned []))IAP_ADDRESS;
iap(param_tab,result_tab);
}
通过这种手段就能够调用iap命令,我们演示性的看一个命令
//扇区准备好指令
//起始扇区号 结束扇区号 系统时钟
void prepare_sector(unsigned start_sector,unsigned end_sector,unsigned cclk)
{
param_table[0] = PREPARE_SECTOR_FOR_WRITE;
param_table[1] = start_sector;
param_table[2] = end_sector;
param_table[3] = cclk;
iap_entry(param_table,result_table);
}
该指令在写flash和擦除flash之前必须调用
具体的完整flash代码请查看工程文件,会在文章末尾上传
然后依旧是五个指令
"iap_down"
"iap_jump_app"
"iap_over"
"iap_set_flag"
"iap_clear_flag"
功能和之前的stm32差不多,但是下载算法变化了,因为stm32支持的写入是每次写入一个十六位数据,而lpc1768每次写入8位数据,而且每次写入数据的量为128/256/512/1024/4096,正好没有我们之前所用的2048,所以算法修改成如下的样子
u8 iapbuf[1024] = {0}; //用于缓存数据的数组
u16 receiveDataCur = 0; //当前iapbuffer中已经填充的数据长度,一次填充满了之后写入flash并清零
u32 addrCur = FLASH_APP1_ADDR; //当前系统写入地址,每次写入之后地址增加2048
#define vu32 volatile unsigned int
//开始下载
void iap_down_s(void)
{
u16 i = 0;
u16 receiveCount;
if(erase_user_flash())
{
printf("errorrn");
下一篇:AD9850函数信号发生器制作
史海拾趣
|
本帖最后由 jameswangsynnex 于 2015-3-3 19:59 编辑 数字电视地面标准推迟出台 不含手机电视部分 2006-7-5 虽然已进入7月,原来“铁定”在6月底前出台的数字电视标准(即数字电视地面传输标准)却仍然迟迟不见踪影。 负 ...… 查看全部问答> |
|
我想用一个红外对射开关控制欧姆龙MY2NJ继电器工作,对射开关的型号见图片 我用黄颜色的线接到继电器,继电器不能工作,继电器都是好的,我用12V电压直接控制继电器,是可以工作的,但用感应的不知道怎么接法。 红外对射开关工作电压是5V,继电器 ...… 查看全部问答> |
|
想在动车组两个车厢中实现数据的无线传输。 也考虑到一些实现方法,但是由于动车组有屏蔽大,速度快特点,造成无线数据包传输难度很大。 请问各位高手,用什么方式能达到这个目的呢?前提是不对车厢作出改造,比如车体外安装天线等 … 查看全部问答> |
|
现在软键盘基本上可以用了,但还有一个字符:小数点\'.\'不能用,发送字符小数点消息,可在我的应用程序里收到的消息却是: 第一个消息是:pMsg->hwnd为编辑框的句柄,但pMsg->message为15,也就是WM_PAINT,wParam和lParam为0, & ...… 查看全部问答> |
|
Fluke 27-II 工业多用表 新型 Fluke 27 II数字多用表树立了在困难情况下进行测量的新标准,它具有优异的功能和准确度,可以轻松解决大多数电气故障。 这款多用表均符合 IP 67(防水和防尘)规格,正在接受 MSHA 的认证审批,并且拥有更广的工作温 ...… 查看全部问答> |




