lpc1768 IAP疑点全解释
2019-07-12 11:48发布
生成海报
本文档基于平台:LPC1768,开发环境:Keil4.10
Time :2013/8/6
Author:xj
E-mail:arm6410@126.com
IAP简介:
IAP为在应用编程的简称,其作用是用户自己的程序在运行过程中对用户程序所在的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的程序进行更新升级。
Lpc1768存储器空间分配:
整体Flash布局:
地址范围
地址说明
0x1000_0000 ~0x1000_7FFF
片上32K通用SRAM
0x2007_C000 ~0x2007_FFFF
片上16K以太网/USB静态SRAM,可用作通用SRAM
0x2008_0000 ~0x2008_3FFF
片上16K以太网/USB静态SRAM,可用作通用SRAM
0x1FFF_0000 ~0x1FFF_1FFF
片上 8K启动代码区
0x0000_0000 ~0x0007_FFFF
片上512K Flash存储器
其他地址
APB、AHB等寄存器映射区
分散加载描述文件:
分散加载描述文件是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函数的入口地址:
1
#define IAP_ENTER_ADR 0x1FFF1FF1/*IAP函数入口地址*/
接着声明函数类型指针IAP_Entry:
1
void (*IAP_Entry)(uint32 paramin[ ],uint32 paramout[ ]) ;
初始化IAP函数指针使其指向IAP函数入口地址:
1
2
3
4
void IAP_EntryInit(void)
{
IAP_Entry =(
void(*)() )IAP_ENTER_ADR ;
}
由用户手册可知iap命令汇总如下图:
Iap状态码汇总如下图:
据此写出IAP命令字和状态码宏定义如下:(PS:Command Code中的数字10表示十进制,如:Read part ID的命令字为5410,表示其命令字为十进制的54,见上图)
准备写操作扇区的命令解释如下图:
由上图可知准备写操作扇区需要三个参数,分别是Command code、Param0、Param1,返回状态码的可能取值为CMD_SUCCESS、BUSY、INVALID_SECTOR。据此我们写处准备写操作扇区的命令函数如下:
1
2
3
4
5
6
7
8
9
uint32 PreSector(uint8 arg1,uint8 arg2)
{
paramin[0] = IAP_SELECTOR ; //设置命令字
paramin[1] = arg1 ; //设置参数
paramin[2] = arg2 ;
(*IAP_Entry)(paramin, paramout) ; //调用IAP服务程序
Return (paramout[0]) ; //
返回状态码
}
其中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区。
1
SCB->VTOR = USR_APP_START_ADDR &0x1FFFFF80;
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寄存器即可,具体实现的函数如下:
1
2
3
4
5
__asm void boot_jump(uint32 address)
{
LDR SP, [R0]
LDR PC ,[R0, #4]
}
对于此函数的解释:__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函数会引起全局变量被修改,是这样么?
如果你读到了这里并且你知道这些答案,敬请告知解惑。E-mail:arm6410@126.com
参考过的文献:
《arm汇编指令集》--------网文
《arm启动代码的探究-郑远超》
《arm体系结构与编程》------杜春雷(PS:重点第11章)
《map文件认识初步》-------网文
《汇编器指南》------RealviewMDK
《链接器指南》------RealviewMDK
《中断向量表重映射与复制》------网文
《cortex-m3技术参考手册》
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮