7种工作模式:
fiq/irq/abt/und/sys/usr/svc。通过"MSR cpsr_c,#0xdx"切换。上电时进入svc模式。
svc和usr的区别是:svc可以通过"MSR cpsr_c,#0xdx"自由切换到其它任何模式,但是usr不可以。
各模式下有自己的堆栈。要在程序启动后依次进入各个模式分别设置自己的堆栈,最后进入usr模式。
Prefetch Abort和Data Abort模式:
Prefetch Abort通常会发生在自修改指令之后。而Data Abort发生于向无效内存中取操作数时,通常是数据指针越过边界了。如果在scatter文件中不指定边界,若编译时内存分配超过了实际物理内存,一定会有Data Abort或Prefetch Abort发生。 Data Abort这个异常会经常出现,要注意。
存储器映射:
0-1G(0x0000,0000 - 0x3fff,ffff): 片内Flash.
1-2G(0x4000,0000 - 0x7fff,ffff): 片内RAM.
2-3.5G(0x8000,0000 - 0xbfff,ffff - 0xdfff,ffff): 片外存储器。
3.5G - 3.75G(0xe000,0000 - 0xefff,ffff): VPB外设。
3.75G - 4G(0xf000,0000 - 0xffff,ffff): AHB外设。
虽然ARM7的寻址空间为4G,但是LPC2200系列只提供A0~A23总共16M的地址。片选信号CS0 - CS3是A24和A25的译码输出,将片外存储区0x8000,0000 - 0x83ff,ffff划分为bank0 - bank3,共16M*4=64M. 这4个bank可以被分别配置为8/16/32位总线宽度。复位时,bank0的总线宽度由Boot1:0引脚决定, bank1为32位,bank2为16位,bank3为8位。
Boot Block
LPC2114/2214的BootBlock被固化在最高的Flash块中,运行时被映射到0x7FFF,E000 - 0x7FFFF,FFFF的区域。而LPC2210没有片内Flash,但它有8K片内ROM存储了BootBlock,也被映射到0x7FFF,E000处。BootBlock是上电以后芯片最早自动运行的程序,被固化在LPC22XX中,相当于LPC22xx自身的bootloader。
分散加载描述文件.scf的设置
Metrowerks Code Warrior V1.2的"Edit->Debug In ExRAM Settings",然后在"Linker->ARM Linker"的Output页中,选中Scatter选项。在Scatter的编辑框中选择写好的.scf文件。(Scatter-Loading description file).
简单应用时可以不写.scf文件。而在"Output"页中选择"Simple".然后填写"RO Base"和"RW Base"的起始地址。在"Lay Out"页中,填写Object/Symble: Startup.o, Section: Start.编写启动文件:Startup.s.
在"Option"页里的"Image Entry Point"填入起始地址。
Scatter-Load Description File的结构:
".scf"文件中的"+RW"对应".s"源文件中的"READWRITE".
".scf"文件中的"+ZI"对应".s"源文件中的"NOINIT".
".scf"文件中的"+RO"对应".s"源文件中的"READONLY".
在".s"源文件中有:
AREA area_name CODE/DATA,READONLY/NOINIT/READWRITE
END
".scf"的例子:
内容 | 注解 | |
ROM_LOAD 0x80000000 { | Name of Load Region, Start Address for Load Region and Maximum size of Load Region(省略了) | |
ROM_EXEC 0x80000000 0x20000 { | 片外存储区,从0x80000000开始,最多0x20000字节。 | |
Startup.o(Vector,+First) | Startup模块的Vector段放在最前面。注1 | |
*(+RO) | 其他所有模块中的所有代码和只读的数据放在这里。 | |
} | ||
IRAM 0x40000000 0x00004000 { | 片内RAM区,从0x40000000开始,最多0x4000字节 | |
Startup.o(MyStacks,+first) | 指定Startup.o中MyStacks放在最前面。 | |
Startup.o(+RW,+ZI) | Startup.o中的其他+RW/+ZI段。注1 | |
os_cpu_a.o(+RW,+ZI) | ||
} | ||
STACKS 0x40004000 UNINIT { | 片内16K RAM的顶端,存放不需要被"C library"初始化的段。 | |
Stack.o(+ZI) | 注2 | |
} | ||
ERAM 0x80040000 { | ||
*(+RW,+ZI) | ||
} | ||
HEAP +0 UNINIT { | "+0"表示接着上一段"ERAM"的结尾,继续安排存储区。 | |
Heap.o(+ZI) | 注3 | |
} | ||
} |
下面是在scf文件中引用过的源文件示意:
"Startup.s" code 32 area Vectors,CODE,READONLY entry ... end | 注1:在"Startup.o"里面会生成名为"Vectors"的段,段的属性为"READONLY" |
"Stack.s" area Stacks, DATA, NOINIT export StackUsr StackUsr SPACE 1 end | 注2: 在"Stack.o"里面会生成名为"Stacks"的段,段的属性为"NOINIT",该属性对应scf文件中的"+ZI". 该段不需要初始化或者可以被初始化为"0". |
"Heap.s" area Heap,DATA,NOINIT export bottom_of_heap bottom_of_heap SPACE 1 end | 注3: "Heap.o"里面名为"Heap"的段。 |
在Scatter文件中最好每一个Region都加一个Maximum参数,这样当编译时如果实际使用的空间大于Maximum Size,会有Error:16220E: Excution region xxx size (xxx bytes) exceeds limit (xx bytes)。如果地址有重复,会有Error: 16221E: Excution region xxx overlaps with excution region xxx。前一个Region的首地址 + Maximum > 后一个Region的首地址时不一定有Error。只有当一分配的内存出现覆盖时才会有Error。
Region的"UNINIT"之类的参数要放在"Maximum size"参数之前。
在一个Region中,RAM的分配不是按照罗列的顺序来的。要想让汇编中使用的变量有固定的位置,可以把所有汇编文件产生的".o"放在同一个Region中。如:
IRAM1 0x40000000
{
startup.o(+RW,+ZI)
ASMSourceCode1.o(+RW,+ZI)
ASMSourceCode2.o(+RW,+ZI)
}
IRAM2 +0
{
CSourceCode1.o(+RW,+ZI)
CSourceCode2.o(+RW,+ZI)
}
这样,所有汇编中定义的变量地址就相对集中了。
如果只有一个汇编文件如startup.s,也可以这样:
IRAM 0x40002000 0x1000
{
startup.o (Mystack,+first)
*(+RW,+ZI)
}
用一个"+first"强行将startup.s中的Mystack放在0x40002000位置。
编译器的全局变量和局部变量:
全局变量从Scatter文件规定的起始地址向地址增加的方向生长。
局部变量从堆栈中规划,向地址减小的方向生长。
实验如下:
1、在".c"中定义全局变量:
uint8 ucData2[17]="__/__ hh:mm:ss";
2、在main()函数中定义局部变量:
uint8 ucdata1[17]="abcdefghijklmnop";
3、运行程序,至"void main(){"处停下。观察0x40003000处为"__/__ hh:mm:ss"。堆栈指针SP(r13)=0x40004000。
4、观察地址0x40003c00处,没有蛛丝马迹。按"Step In"单步执行,指针停在"uint8 ucdata1[17]="abcdefghijklmnop";"处。 SP变为0x40003FC8。
5、再按"Step In"单步执行,进入Disassembly窗口。当前程序标号为"__rt_memcpy",应该是初始化局部变量的。
6、按"Step Out"退出Disassembly窗口。0x40003FCC~0x40003FDB被初始化为"abcdefghijklmnop"。
此时,SP(r13)仍旧为0x40003FC8,与在第4步时的内容相比,没有变化。说明在刚刚进入"void main()"以后,即在第3、4步中间,局部变量的地址就已经分配好了。
在安排全局变量的时候,若某一模块有被初始化的需求,则该模块放在前面,Scatter文件中的"+first"被移到所有有初始值得变量后面。"first"只是没有初始化要求的数据块的"first"。
我看来这篇文章写的很好,是笔记类型的,其中好多常见问题和主要事项都列了出来,不过是初学者还是老鸟都能从中或多或少的收益,因此摘录一部分记录。
上一篇:MCU内部参考电压妙用
下一篇:LPC2200系列bootloader实现过程:
推荐阅读最新更新时间:2024-03-16 16:22