第3章 ARM指令系统
3.1 ARM处理器的指令格式
3.1.1 ARM指令集的特点
第3章 ARM指令系统
第3章 ARM指令系统
3.1 ARM处理器的指令格式
3.1.1 ARM指令集的特点
ARM内核属于RISC结构,所以其指令集有着一些独特的特点:指令长度固定,指令格式的种类少,寻址方式简单。由于ARM处理器采用固定长度的32位指令,因此处理器内部硬件设计能够被简化。ARM处理器内部的指令译码采用硬布线逻辑,不使用微程序控制,以减少指令的译码时间,大部分指令可以在一个时钟周期内完成。
ARM处理器的指令按功能可分为七大类:加载/存储指令、数据处理指令、乘法指令、跳转指令、程序状态寄存器处理指令、协处理器指令和异常中断指令。
需要特别指出的是,ARM处理器的指令集是加载/存储型的,也即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。
按照操作数的特点分,ARM指令可以分为无操作数指令、单操作数指令、双操作数指令和三操作数指令。每条指令都由操作码域、条件码域、条件码设置域、目标操作数、第一操作数寄存器和第二操作数组成。
3.1.2 ARM指令的格式
每条ARM指令都是32位的,其格式如下:
31 28 27 25 24 21 20 19 16 15 12 11 0
条件码 | 类别码 | 操作码 | S | 目的寄存器 | 第一操作数 | 第二操作数 |
用ARM指令助记符表示为:
<opcode> {<cond>} {S} <Rd>, <Rn>, <shift_op2>
每个域的含义如下:
1) <opcode>:操作码域,指令编码的助记符;
2) {<cond>}:条件码域,指令允许执行的条件编码。花括号表示此项可缺省。
ARM指令的一个重要特点是可以条件执行,每条ARM指令的条件码域包含4位条件码,共16种。几乎所有指令均根据CPSR中条件码的状态和指令条件码域的设置有条件的执行。当指令执行条件满足时,指令被执行,否则被忽略。指令条件码及其助记符后缀表示参见表3.1。
每种条件码可用两个字符表示,这两个字符可以作为后缀添加在指令助记符的后面和指令同时使用。例如,跳转指令B可以加上后缀EQ变为BEQ,表示“相等则跳转”,即当CPSR中的Z标志置位时发生跳转。
表3.1 指令的条件码
条件码 | 助记符后缀 | 标 志 | 含 义 |
0000 | EQ | Z置位 | 相等 |
0001 | NE | Z清零 | 不相等 |
0010 | CS | C置位 | 无符号数大于或等于 |
0011 | CC | C清零 | 无符号数小于 |
0100 | MI | N置位 | 负数 |
0101 | PL | N清零 | 正数或零 |
0110 | VS | V置位 | 溢出 |
0111 | VC | V清零 | 未溢出 |
1000 | HI | C置位Z清零 | 无符号数大于 |
1001 | LS | C清零Z置位 | 无符号数小于或等于 |
1010 | GE | N等于V | 带符号数大于或等于 |
1011 | LT | N不等于V | 带符号数小于 |
1100 | GT | Z清零且(N等于V) | 带符号数大于 |
1101 | LE | Z置位或(N不等于V) | 带符号数小于或等于 |
1110 | AL | 忽略 | 无条件执行 |
3) {S}:条件码设置域。这是一个可选项,当在指令中设置{S}域时,指令执行的结果将会影响程序状态寄存器CPSR中相应的状态标志。
例如:
ADD R0,R1,R2; R1与R2的和存放到R0寄存器中,不影响状态寄存器
ADDS R0,R1,R2; 执行加法的同时影响状态寄存器
指令中比较特殊的是CMP指令,它不需要加S后缀就默认地根据计算结构更改程序状态寄存器。
4) <Rd>:目的操作数。ARM指令中的目的操作数总是一个寄存器。如果<Rd>与第一操作数寄存器<Rn>相同,也必须要指明,不能缺省。
5) <Rn>:第一操作数。ARM指令中的第一操作数也必须是个寄存器。
6) <shift_op2>:第二操作数。在第二操作数中可以是寄存器、内存存储单元或者立即数。
由于第二操作数只有12个bit,用第二操作数表示立即数时,其取值范围为0~212-1,要表示超出这个范围的立即数,通常要依靠伪指令实现。
3.2 ARM指令的寻址方式
所谓寻址方式就是处理器根据指令中给出的地址信息来寻找物理地址的方式。目前ARM指令系统支持如下几种常见的寻址方式。
3.2.1 立即寻址
立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址。例如以下指令:
ADD R0,R0,#1 ;R0←R0+1
ADD R0,R0,#0x3f ;R0←R0+0x3f
在以上两条指令中,第二操作数即为立即数,要求以“#”为前缀,对于以十六进制表示的立即数,还要求在“#”后加上“0x”,以二进制表示的立即数,要求在“#”后加上“%”。
当立即数大于第二操作数的表示范围时,通常用以下伪指令实现:
LDR R0,=#0xffff0000
3.2.2 寄存器寻址
寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。以下指令:
ADD R0,R1,R2 ;R0←R1+R2
该指令的执行效果是将寄存器R1和R2的内容相加,其结果存放在寄存器R0中。
3.2.3 寄存器间接寻址
寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。例如以下指令:
ADD R0,R1,[R2] ;R0←R1+[R2]
LDR R0,[R1] ;R0←[R1]
STR R0,[R1] ;[R1]←R0
在第一条指令中,以寄存器R2的值作为操作数的地址,在存储器中取得一个操作数后与R1相加,结果存入寄存器R0中。
第二条指令将以R1的值为地址的存储器中的数据传送到R0中。
第三条指令将R0的值传送到以R1的值为地址的存储器中。
3.2.4 基址变址寻址
基址变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基地址附近的地址单元。采用变址寻址方式的指令常见有以下几种形式,如下所示:
LDR R0,[R1,#4] ;R0←[R1+4]
LDR R0,[R1,#4]! ;R0←[R1+4]、R1←R1+4
LDR R0,[R1] ,#4 ;R0←[R1]、R1←R1+4
LDR R0,[R1,R2] ;R0←[R1+R2]
在第一条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中。
在第二条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第三条指令中,以寄存器R1的内容作为操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第四条指令中,将寄存器R1的内容加上寄存器R2的内容形成操作数的有效地址,从而取得操作数存入寄存器R0中。
3.2.5 多寄存器寻址
多寄存器寻址是ARM处理器特有的一种寻址方式。由于ARM内核有较多的通用寄存器,采用多寄存器寻址方式,一条指令可以一次完成多个寄存器值的传送。这种寻址方式可以用一条指令完成传送最多16个通用寄存器的值。例如以下指令:
LDMIA R0,{R1,R2,R3,R4} ;R1←[R0]
;R2←[R0+4]
;R3←[R0+8]
;R4←[R0+12]
该指令的后缀IA表示在每次执行完加载/存储操作后,R0按字长度增加,因此,指令可将连续存储单元的值传送到R1~R4。
多个连续的寄存器可以用“-”符号连接;不连续的寄存器用“,”分隔书写,如上例可写成:
LDMIA R0,{R1-R4}
LDMIA R0,{R1-R3,R4}
3.2.6 寄存器移位寻址
寄存器移位寻址是ARM指令集特有的寻址方式。ARM处理器内嵌桶型移位器(Barrel Shifter),支持数据的各种移位操作。当第二操作数为寄存器时,可以加入移位操作选项对它进行各种移位操作。
移位操作包括如下6种类型:
1、LSL(或ASL)逻辑(算术)左移
寻址格式:
通用寄存器,LSL(或ASL) 操作数
完成对通用寄存器中的内容进行逻辑(或算术)的左移操作,按操作数所指定的数量向左移位,低位用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, LSL#2 ;将R1中的内容左移两位后传送到R0中。
2、LSR逻辑右移
寻址格式:
通用寄存器,LSR 操作数
完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, LSR#2 ;将R1中的内容右移两位后传送到R0中,左端用零来填充。
3、ASR算术右移
寻址格式:
通用寄存器,ASR 操作数
完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用第31位的值来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, ASR#2 ;将R1中的内容右移两位后传送到R0中,左端用第31位的值来填充。
4、ROR循环右移
寻址格式:
通用寄存器,ROR 操作数
完成对通用寄存器中的内容进行循环右移的操作,按操作数所指定的数量向右循环移位,左端用右端移出的位来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。显然,当进行32位的循环右移操作时,通用寄存器中的值不改变。
如:
MOV R0, R1, ROR#2 ;将R1中的内容循环右移两位后传送到R0中。
5、RRX带扩展的循环右移
寻址格式:
通用寄存器,RRX 操作数
完成对通用寄存器中的内容进行带扩展的循环右移的操作,按操作数所指定的数量向右循环移位,左端用进位标志位C来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, RRX#2 ;将R1中的内容进行带扩展的循环右移两位后传送到R0中。
3.2.7 相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。以下程序段完成子程序的调用和返回,跳转指令BL采用了相对寻址方式:
BL NEXT ;跳转到子程序NEXT处执行
……
NEXT
……
MOV PC,LR ;从子程序返回
3.2.8 堆栈寻址
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,根据堆栈的生成方式,又可以分为递增堆栈(Ascending Stack)和递减堆栈(Decending Stack)。当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。这样就有四种类型的堆栈工作方式,ARM微处理器支持这四种类型的堆栈工作方式,即:
1. 满递增堆栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生成。
2. 满递减堆栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生成。
3. 空递增堆栈(EA):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。
4. 空递减堆栈(ED):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。
3.3 ARM指令集
本节对ARM指令集的七大类指令进行详细的描述。
3.3.1 加载/存储指令
ARM处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。常用的加载存储指令如下:
1、LDR指令
LDR指令的格式为:
LDR{条件} 目的寄存器,<存储器地址>
LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,且寻址方式灵活多样,请读者认真掌握。
如:
LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0。
LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
LDR R0,[R1,R2] ! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将
;新地址R1+R2写入R1。
LDR R0,[R1,#8] ! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新
;地址R1+8写入R1。
LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地
;址R1+R2写入R1。
LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4的字数据读入寄存器R0,
;并将新地址R1+R2×4写入R1。
LDR R0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地
;址R1+R2×4写入R1。
2、STR指令
STR指令的格式为:
STR{条件} 源寄存器,<存储器地址>
STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR。
如:
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
LDR/STR指令都可以加B、H、SB、SH的后缀,分别表示加载/存储字节、半字、带符号的字节、带符号的半字。如LDRB指令表示从存储器加载一个字节进寄存器。当使用这些后缀时,要注意所使用的存储器要支持访问的数据宽度。
3、LDM(或STM)批量数据加载/存储指令
LDM(或STM)指令的格式为:
LDM(或STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}
LDM(或STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。其中,{类型}为以下几种情况:
IA 每次传送后地址加1;
IB 每次传送前地址加1;
DA 每次传送后地址减1;
DB 每次传送前地址减1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。
基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。
{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。
如:
STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈。
LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。
4、SWP数据交换指令
SWP指令的格式为:
SWP{条件} 目的寄存器,源寄存器1,[源寄存器2]
SWP指令用于将源寄存器2所指向的存储器中的字数据传送到目的寄存器中,同时将源寄存器1中的字数据传送到源寄存器2所指向的存储器中。显然,当源寄存器1和目的寄存器为同一个寄存器时,指令交换该寄存器和存储器的内容。
如:
SWP R0,R1,[R2] ;将R2所指向的存储器中的字数据传送到R0,同时将R1中的字数据传送到R2所指向的存储单元。
SWP R0,R0,[R1] ;该指令完成将R1所指向的存储器中的字数据与R0中的字数据交换。
3.3.2 数据处理指令
数据处理指令可分为数据传送指令、算术逻辑运算指令和比较指令等。
数据传送指令用于在寄存器和存储器之间进行数据的双向传输。
算术逻辑运算指令完成常用的算术与逻辑的运算,该类指令不但将运算结果保存在目的寄存器中,同时更新CPSR中的相应条件标志位。
比较指令不保存运算结果,只更新CPSR中相应的条件标志位。
1、 MOV指令
MOV指令的格式为:
MOV{条件}{S} 目的寄存器,源操作数
MOV指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。其中S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
如:
MOV R1,R0 ;将寄存器R0的值传送到寄存器R1
MOV PC,R14 ;将寄存器R14的值传送到PC,常用于子程序返回
MOV R1,R0,LSL#3 ;将寄存器R0的值左移3位后传送到R1
2、 MVN指令
MVN指令的格式为:
MVN{条件}{S} 目的寄存器,源操作数
MVN指令可完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。与MOV指令不同之处是在传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中。其中S决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
如:
MVN R0,#0 ;将立即数0取反传送到寄存器R0中,完成后R0=-1
3、 CMP指令
CMP指令的格式为:
CMP{条件} 操作数1,操作数2
CMP指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位表示的是操作数1与操作数2的关系(大、小、相等),例如,当操作数1大于操作操作数2,则此后的有GT 后缀的指令将可以执行。
如:
CMP R1,R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
CMP R1,#100 ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位
4、 CMN指令
CMN指令的格式为:
CMN{条件} 操作数1,操作数2
CMN指令用于把一个寄存器的内容和另一个寄存器的内容或立即数取反后进行比较,同时更新CPSR中条件标志位的值。该指令实际完成操作数1和操作数2相加,并根据结果更改条件标志位。
如:
CMN R1,R0 ;将寄存器R1的值与寄存器R0的值相加,并根据结果设置CPSR的标志位
CMN R1,#100 ;将寄存器R1的值与立即数100相加,并根据结果设置CPSR的标志位
5、 TST指令
TST指令的格式为:
TST{条件} 操作数1,操作数2
TST指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。操作数1是要测试的数据,而操作数2是一个位掩码,该指令一般用来检测是否设置了特定的位。
如:
TST R1,#%1 ;用于测试在寄存器R1中是否设置了最低位(%表示二进制数)
TST R1,#0xffe ;将寄存器R1的值与立即数0xffe按位与,并根据结果设置CPSR的标志位
6、 TEQ指令
TEQ指令的格式为:
TEQ{条件} 操作数1,操作数2
TEQ指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的异或运算,并根据运算结果更新CPSR中条件标志位的值。该指令通常用于比较操作数1和操作数2是否相等。
如:
TEQ R1,R2 ;将寄存器R1的值与寄存器R2的值按位异或,并根据结果设置CPSR的标志位
7、 ADD指令
ADD指令的格式为:
ADD{条件}{S} 目的寄存器,操作数1,操作数2
ADD指令用于把两个操作数相加,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
如:
ADD R0,R1,R2 ; R0 = R1 + R2
ADD R0,R1,#256 ; R0 = R1 + 256
ADD R0,R2,R3,LSL#1 ; R0 = R2 + (R3 << 1)
8、 ADC指令
ADC指令的格式为:
ADC{条件}{S} 目的寄存器,操作数1,操作数2
ADC指令用于把两个操作数相加,再加上CPSR中的C条件标志位的值,并将结果存放到目的寄存器中。它使用一个进位标志位,这样就可以做比32位大的数的加法,注意不要忘记设置S后缀来更改进位标志。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
以下指令序列完成两个128位数的加法,第一个数由高到低存放在寄存器R7~R4,第二个数由高到低存放在寄存器R11~R8,运算结果由高到低存放在寄存器R3~R0:
ADDS R0,R4,R8 ; 加低端的字
ADCS R1,R5,R9 ; 加第二个字,带进位
ADCS R2,R6,R10 ; 加第三个字,带进位
ADC R3,R7,R11 ; 加第四个字,带进位
9、 SUB指令
SUB指令的格式为:
SUB{条件}{S} 目的寄存器,操作数1,操作数2
SUB指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
如:
SUB R0,R1,R2 ; R0 = R1 - R2
SUB R0,R1,#256 ; R0 = R1 - 256
SUB R0,R2,R3,LSL#1 ; R0 = R2 - (R3 << 1)
10、SBC指令
SBC指令的格式为:
SBC{条件}{S} 目的寄存器,操作数1,操作数2
SBC指令用于把操作数1减去操作数2,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
如:
SUBS R0,R1,R2 ; R0 = R1 - R2 - !C,并根据结果设置CPSR的进位标志位
11、RSB指令
RSB指令的格式为:
RSB{条件}{S} 目的寄存器,操作数1,操作数2
RSB指令称为逆向减法指令,用于把操作数2减去操作数1,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
如:
RSB R0,R1,R2 ; R0 = R2 – R1
RSB R0,R1,#256 ; R0 = 256 – R1
RSB R0,R2,R3,LSL#1 ; R0 = (R3 << 1) - R2
12、RSC指令
RSC指令的格式为:
RSC{条件}{S} 目的寄存器,操作数1,操作数2
RSC指令用于把操作数2减去操作数1,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
如:
RSC R0,R1,R2 ; R0 = R2 – R1 - !C
13、AND指令
AND指令的格式为:
AND{条件}{S} 目的寄存器,操作数1,操作数2
AND指令用于在两个操作数上进行逻辑与运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于屏蔽操作数1的某些位。
如:
AND R0,R0,#3 ; 该指令保持R0的0、1位,其余位清零。
14、ORR指令
ORR指令的格式为:
ORR{条件}{S} 目的寄存器,操作数1,操作数2
ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位。
如:
ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。
15、EOR指令
EOR指令的格式为:
EOR{条件}{S} 目的寄存器,操作数1,操作数2
EOR指令用于在两个操作数上进行逻辑异或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于反转操作数1的某些位。
如:
EOR R0,R0,#3 ; 该指令反转R0的0、1位,其余位保持不变。
16、BIC指令
BIC指令的格式为:
BIC{条件}{S} 目的寄存器,操作数1,操作数2
BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。
如:
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。
3.3.3 乘法指令与乘加指令
ARM微处理器支持的乘法指令与乘加指令共有6条,可分为运算结果为32位和运算结果为64位两类。与前面的数据处理指令不同,指令中的所有操作数、目的寄存器必须为通用寄存器,不能对操作数使用立即数或被移位的寄存器,同时,目的寄存器和操作数1必须是不同的寄存器。
1、 MUL指令
MUL指令的格式为:
MUL{条件}{S} 目的寄存器,操作数1,操作数2
MUL指令完成将操作数1与操作数2的乘法运算,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数或无符号数。
如:
MUL R0,R1,R2 ;R0 = R1 × R2
MULS R0,R1,R2 ;R0 = R1 × R2,同时设置CPSR中的相关条件标志位
2、 MLA指令
MLA指令的格式为:
MLA{条件}{S} 目的寄存器,操作数1,操作数2,操作数3
MLA指令完成将操作数1与操作数2的乘法运算,再将乘积加上操作数3,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数或无符号数。
如:
MLA R0,R1,R2,R3 ;R0 = R1 × R2 + R3
MLAS R0,R1,R2,R3 ;R0 = R1 × R2 + R3,同时设置CPSR中的相关条件标志位
3、 SMULL指令
SMULL指令的格式为:
SMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数。
如:
SMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位
;R1 = (R2 × R3)的高32位
4、 SMLAL指令
SMLAL指令的格式为:
SMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
如:
SMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位 + R0
;R1 = (R2 × R3)的高32位 + R1
5、 UMULL指令
UMULL指令的格式为:
UMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
如:
UMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位
;R1 = (R2 × R3)的高32位
6、 UMLAL指令
UMLAL指令的格式为:
UMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
如:
UMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位 + R0
;R1 = (R2 × R3)的高32位 + R1
3.3.4 跳转指令
跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转:使用专门的跳转指令、直接向程序计数器PC写入跳转地址值。
直接向PC写入跳转地址值,可以实现在4GB的地址空间中任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。
使用跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转。
1、 B指令
B指令的格式为:
B{条件} 目标地址
B指令是最简单的跳转指令。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(前后32MB的地址空间)。
如:
B Label ;程序无条件跳转到标号Label处执行
CMP R1,#0 ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
BEQ Label
2、 BL指令
BL指令的格式为:
BL{条件} 目标地址
BL 是另一个跳转指令,但跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14 的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。
如:
BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中
Label标号处可以是一个子程序,在子程序的最后可以使用MOV PC,LR指令跳回BL Label指令处的下一条指令继续执行。
3、 BLX指令
BLX指令的格式为:
BLX 目标地址
BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态由ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中。因此,当子程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成。
4、 BX指令
BX指令的格式为:
BX{条件} 目标地址
BX指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是Thumb指令。
3.3.5 程序状态寄存器访问指令
ARM指令不允许直接操作程序状态寄存器CPSR和SPSR。可以通过程序状态寄存器访问指令,在程序状态寄存器和通用寄存器之间传送数据,然后在通用寄存器中进行处理。
1、 MRS指令
MRS指令的格式为:
MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)
MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。该指令一般用在以下几种情况:
1) 当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。
2) 当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。
如:
MRS R0,CPSR ;传送CPSR的内容到R0
MRS R0,SPSR ;传送SPSR的内容到R0
2、 MSR指令
MSR指令的格式为:
MSR{条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数
MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:
位[31:24]为条件标志位域,用f表示;
位[23:16]为状态位域,用s表示;
位[15:8]为扩展位域,用x表示;
位[7:0]为控制位域,用c表示;
该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
如:
MSR CPSR,R0 ;传送R0的内容到CPSR
MSR SPSR,R0 ;传送R0的内容到SPSR
MSR CPSR_c,R0 ;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域
3.3.6 协处理器指令
ARM微处理器可支持多达16个协处理器,用于各种协处理操作,在程序执行的过程中,每个协处理器只执行针对自身的协处理指令,忽略ARM处理器和其他协处理器的指令。
ARM的协处理器指令主要用于ARM处理器初始化ARM协处理器的数据处理操作,以及在ARM处理器的寄存器和协处理器的寄存器之间传送数据,和在ARM协处理器的寄存器和存储器之间传送数据。
1、CDP指令
CDP指令的格式为:
CDP{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
CDP指令用于ARM处理器通知ARM协处理器执行特定的操作,若协处理器不能成功完成特定的操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器和源寄存器均为协处理器的寄存器,指令不涉及ARM处理器的寄存器和存储器。
如:
CDP P3,2,C12,C10,C3,4 ;该指令完成协处理器P3的初始化
2、LDC指令
LDC指令的格式为:
LDC{条件}{L} 协处理器编码,目的寄存器,[源寄存器]
LDC指令用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
如:
LDC P3,C4,[R0] ;将ARM处理器的寄存器R0所指向的存储器中的字数据传送到协处理器P3的寄存器C4中。
3、STC指令
STC指令的格式为:
STC{条件}{L} 协处理器编码,源寄存器,[目的寄存器]
STC指令用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
如:
STC P3,C4,[R0] ;将协处理器P3的寄存器C4中的字数据传送到ARM处理器的寄存器R0所指向的存储器中。
4、MCR指令
MCR指令的格式为:
MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
MCR指令用于将ARM处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,源寄存器为ARM处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄存器。
如:
MCR P3,3,R0,C4,C5,6 ;该指令将ARM处理器寄存器R0中的数据传送到协处理器P3的寄存器C4和C5中。
5、MRC指令
MRC指令的格式为:
MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
MRC指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器为ARM处理器的寄存器,源寄存器1和源寄存器2均为协处理器的寄存器。
如:
MRC P3,3,R0,C4,C5,6 ;该指令将协处理器P3的寄存器中的数据传送到ARM处理器寄存器中。
3.3.7 异常中断指令
1、SWI指令
SWI指令的格式为:
SWI{条件} 24位的立即数
SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的异常处理程序中提供相应的系统服务,指令中24位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时,参数通过其他通用寄存器传递。
如:
SWI 0x02 ;该指令调用操作系统编号位02的系统例程。
2、BKPT指令
BKPT指令的格式为:
BKPT 16位的立即数
BKPT指令产生软件断点中断,可用于程序的调试。
3.4 Thumb指令集
为兼容数据总线宽度为16位的应用系统,ARM体系结构除了支持执行效率很高的32位ARM指令集以外,同时支持16位的Thumb指令集。Thumb指令集是ARM指令集的一个子集,允许指令编码为16位的长度。与等价的32位代码相比较,Thumb指令集在保留32位代码优势的同时,大大的节省了系统的存储空间。
所有的Thumb指令都有对应的ARM指令,而且Thumb的编程模型也对应于ARM的编程模型,在应用程序的编写过程中,只要遵循一定调用的规则,Thumb子程序和ARM子程序就可以互相调用。当处理器在执行ARM程序段时,称ARM处理器处于ARM工作状态,当处理器在执行Thumb程序段时,称ARM处理器处于Thumb工作状态。
与ARM指令集相比较,Thumb指令集中的数据处理指令的操作数仍然是32位,指令地址也为32位,但Thumb指令集为实现16位的指令长度,舍弃了ARM指令集的一些特性,如大多数的Thumb指令是无条件执行的,而几乎所有的ARM指令都是有条件执行的;大多数的Thumb数据处理指令的目的寄存器与其中一个源寄存器相同。
由于Thumb指令的长度为16位,即只用ARM指令一半的位数来实现同样的功能,所以,要实现特定的程序功能,所需的Thumb指令的条数较ARM指令多。在一般的情况下,Thumb指令与ARM指令的时间效率和空间效率关系为:
— Thumb代码所需的存储空间约为ARM代码的60%~70%
— Thumb代码使用的指令数比ARM代码多约30%~40%
— 若使用32位的存储器,ARM代码比Thumb代码快约40%
— 若使用16位的存储器,Thumb代码比ARM代码快约40%~50%
— 与ARM代码相比较,使用Thumb代码,存储器的功耗会降低约30%
显然,ARM指令集和Thumb指令集各有其优点,若对系统的性能有较高要求,应使用32位的存储系统和ARM指令集,若对系统的成本及功耗有较高要求,则应使用16位的存储系统和Thumb指令集。当然,若两者结合使用,充分发挥其各自的优点,会取得更好的效果。
3.5 伪指令
ARM编译器一般都支持汇编语言的程序设计和C/C++语言的程序设计,以及两者的混合编程。在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对应的操作码,通常称这些特殊指令助记符为伪指令,他们所完成的操作称为伪操作。伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,这些伪指令仅在汇编过程中起作用,一旦汇编结束,伪指令的使命就完成。
在ARM的汇编程序中,有如下几种伪指令:ARM伪指令、符号定义伪指令、数据定义伪指令、段定义伪指令、模块控制伪指令、汇编控制伪指令、宏处理伪指令等等。
需要特别指出的是,除了几条ARM伪指令以外,其它的伪指令依赖于编译器。也就是说,不同的ARM编译器的伪指令集是不相同的。例如,ADS编译器的段定义伪指令为AREA,而IAR编译器的段定义伪指令为RSEG和ASEG。这种情况使得不同编译器下编出的ARM汇编程序是不同的。读者在阅读不同学习材料时应注意分辨在不同编译器下ARM汇编程序的区别。
本书介绍的是IAR EWARM编译器支持的ARM汇编伪指令。
3.5.1 ARM伪指令
ARM伪指令不是ARM指令集中的指令。它可以象其它ARM指令一样使用,但在编译时这些指令将被等效的ARM指令所取代。
1、LDR-大范围地址读取
LDR伪指令的格式为:
LDR{条件} reg,=expr/label_expr
reg为加载的目的寄存器;expr为32位立即数;label_expr为地址表达式或外部表达式。
LDR伪指令将32位常量或一个32位地址加载到指定寄存器。
如:
LDR R0,=#0x12345 ;加载32位立即数0x12345到寄存器R0
LDR R0,=DATA_BUF+60 ;加载DATA_BUF地址+60
2、ADR-小范围地址读取
ADR伪指令的格式为:
ADR{条件} reg,expr
reg为加载的目的寄存器;expr为相对偏移表达式,非字对齐时取值范围为-255~255字节,字对齐时取值范围为-1020~1020字节。
ADR伪指令将基于当前PC相对偏移的地址值读取到寄存器中。
如:
Start: MOV R0,#10
ADR R4,start ;相当于SUB R4,PC,#0x0c
3、ADRL-中范围地址读取
ADRL伪指令与ADR类似,不同在expr的取值范围,非字对齐时取值范围为64KB,字对齐时取值范围为256KB。
4、NOP-空操作
3.5.2 数据定义伪指令
1、DCB和DC8
该伪指令的格式为:
标号 DCB或DC8 表达式
DCB和DC8伪指令用于分配一片连续的8位字节存储单元,并用伪指令中指定的表达式初始化。其中表达式可以为0~255的数字或字符串。
如:
Str DCB “This is a test!” ;分配一个字符串,每个字符8位字节
2、DCW和DC16、DCD和DC32
与DCB和DC8用法相同,不同的是分别分配16位半字节单元和32位字单元。
3、DF32和DF64
分别表示32位的单精度浮点数和64位的双精度浮点数。
4、DS8、DS16、DS24和DS32
分别用于保留8位字节、16位半字、24位字和32位字的存储器空间。
如:
Dataspace DS8 100 ;保留100个8位字节的存储器空间
3.5.3 符号定义伪指令
1、=、ALIAS和EQU
该伪指令的格式为:
标号 = 表达式
标号 ALIAS 表达式
标号 EQU 表达式
伪指令EQU和=可用于为程序模块中的常量、标号等赋值,定义的局部符号仅在其所在的模块内有效。伪指令ALIAS为符号起个别名。定义的符号采用PUBLIC伪指令声明其属性可使之被其它模块引用,引用其它模块内符号时必须采用EXTERN伪指令声明其属性。
如:
Test EQU 50 ;定义符号Test的值为50
2、ASSIGN、SET、SETA和VAR
用法与EQU等类似,可用于定义一个变量符号。采用VAR定义的变量符号不能用PUBLIC声明其属性。
3、DEFINE
用于定义在整个程序文件内都有效的全局符号。该符号可以被文件内的所有程序模块引用,但不能在同一文件内重新定义。
4、LIMIT
该伪指令的格式为:
LIMIT 表达式, 最小值, 最大值, 提示信息
用于检查表达式的值是否位于给定范围之内。如果表达式值的范围超限,则输出提示信息。
如:
Speed VAR 23 ;定义符号speed的值为23
LIMIT speed,10,30,…speed out of range… ;检查speed的值是否超限
5、EXTERN(或IMPORT)
该伪指令的格式为:
EXTERN 符号,[符号]……
EXTERN伪指令用于通知汇编器,要使用的符号在其它源文件中定义,但要在当前源文件中引用。
如:
Name Start ;程序模块Start
EXTERN Main ;告诉汇编器Main符号在其它源文件中定义
……
BL Main ;在本模块中引用Main符号
END
6、PUBLIC(或EXPORT)
该伪指令的格式为:
PUBLIC 符号,[符号]……
PUBLIC伪指令用于在程序中声明一个全局符号,该符号可在其它文件中引用。
7、REQUIRE
PUBLIC伪指令用于将一个符号标记为已经被引用。
3.5.4 段定义伪指令
1、ASEG和ASEGN
该伪指令的格式为:
ASEG [起始地址[(对齐)]]
ASEGN 段名[:存储器类型],地址
ASEG伪指令用于定义一个绝对段,并设置段的起始地址。不指定地址值时第一个段默认起始地址为0,后续段地址依次递增。ASEGN伪指令用于设置指定段的绝对起始地址,并允许规定段类型。存储器类型可以为CODE(代码段)、DATA(数据段)、STACK(堆栈段)。
如:
ASEG 0 ;定义一个绝对段,起始地址0
ASEGN CODE:CODE,0 ;定义一个名为CODE的代码段,起始地址0
2、RSEG
该伪指令的格式为:
RSEG 段名[:存储器类型][:(NO)ROOT|(NO)REORDER|SORT][(对齐)]
RSEG伪指令用于定义一个可重定位段,段的起始地址由汇编器临时分配。单个模块中最多可定义65536个可重定位段。
如:
RSEG CODE:CODE:ROOT(2) ;定义一个名为CODE的可重定位代码段,用户权限为ROOT(可读写),段内存储器对齐方式为4字节对齐
3、DATA
该伪指令的格式为:
DATA 段名[:存储器类型][(对齐)]
DATA伪指令可以在代码段内定义一个数据区。
如:
RSEG CODE:CODE:ROOT(2)
DATA
f1: DC32 subrtn
4、STACK
该伪指令的格式为:
COMMON 段名[:存储器类型][(对齐)]
STACK伪指令用于定义一个堆栈段,用作堆栈的存储器地址从高向低变化,而用作可重定位段的存储器地址是从低向高变化。
5、COMMON
该伪指令的格式为:
COMMON 段名[:存储器类型][(对齐)]
COMMON伪指令用于定义公共段,各源文件中同名的COMMON段共享同一段内存。它的典型应用是多个不同子程序共享一段数据存储区。中断向量表也可安排在COMMON段,以便允许从多个服务子程序访问。
6、CODE16和CODE32
CODE16伪指令用于告诉汇编器,其后的指令序列为16位的Thumb指令;CODE32伪指令用于告诉汇编器,其后的指令序列为32位的ARM指令。因此,在使用ARM指令和Thumb指令混合编程的代码中,可用这两条伪指令进行切换。但需要注意的是,它们只通知汇编器其后指令的类型,并不能对处理器进行状态切换。
7、ORG
该伪指令的格式为:
ORG 地址表达式
ORG伪指令用于设置段的起始地址。地址表达式的计算结果应与当前段的类型保持一致,如在RSEG(可重定位段)中,不要使用“ORG 10”,因为10是一个绝对地址,而应当使用“ORG .+10”,表示当前段偏移量为10的地址。另外,地址表达式中不能包括任何前向和外部引用。
8、ALIGNRAM和ALIGNROM
该伪指令的格式为:
ALIGNRAM 对齐
ALIGNROM 对齐[,填充值]
用于设置存储器地址边界的对齐方式,“对齐”是一个值为2~30的常数,并按22~30设定对齐地址。ALIGNRAM以数据增量方式对齐,ALIGNROM以填充0字节方式对齐。
9、EVEN和ODD
该伪指令的格式为:
EVEN [填充值]
ODD [填充值]
EVEN伪指令用于将程序计数器PC以偶数地址对齐(等价于ALIGNROM 1),ODD伪指令用于将程序计数器PC以奇数地址对齐。
3.5.5 模块控制伪指令
1、NAME和PROGRAM
该伪指令的格式为:
NAME 模块名
PROGRAM 模块名
NAME和PROGRAM伪指令用于定义一个程序模块。程序模块类似于C语言中的函数,是程序中相对独立的一个部分。程序模块即使没有被调用也会被无条件链接。
如:
NAME Main ;定义一个名为Main的程序模块
2、END和ENDMOD
END伪指令用于结束整个汇编语言程序,ENDMOD用于结束当前程序模块。每个汇编语言程序最后必须使用END伪指令通知汇编器已经到了源程序结尾,以结束汇编。
3、LIBRARY和MODULE
该伪指令用于定义多模块文件中的小模块,其中每个小模块代表一段子程序,从而可以方便地创建库模块文件。与NAME和PROGRAM不同的是,用LIBRARY和MODULE定义的模块只有在被调用时才会复制到链接代码中。
4、RTMODEL
该伪指令的格式为:
RTMODEL 关键字字符串,值字符串
该伪指令用于声明模块的运行模式属性,以强制模块之间的一致性。所有能被链接在一起的模块必须具有相同的关键字;值字符串要么具有相同的值,要么其值为星号“*”。
3.5.6 汇编控制伪指令
1、$和INCLUDE
该伪指令的格式为:
$ 文件名
INCLUDE 文件名
该伪指令用于给当前源文件加载头文件。
2、CASEOFF和CASEON
该伪指令用于源程序文件中禁止和允许大小写字符敏感。
3、LTORG
在使用ARM伪指令LDR加载地址数据时,要在适当的位置加入LTORG声明一个数据区,把要加载的数据保存在数据区内,再用LDR读出数据。LTORG伪指令通常放在无条件分支或子程序返回指令后面,这样处理器就不会错误的将数据区中的数据当作指令执行。
4、RADIX
该伪指令用于声明当前使用的数制形式。如:
RADIX 16D ;声明当前使用十六进制数
MOV R0,#12 ;此处#12为0x12
5、IF、ELSE和ENDIF
该伪指令的格式为:
IF 逻辑表达式
指令序列1
ELSE
指令序列2
ENDIF
条件汇编伪指令能根据设定条件的成立与否决定是否对指令序列进行汇编生成目标代码。若逻辑表达式为真,则对指令序列1汇编生成目标代码;否则对指令序列2汇编。其中还可以用ELSEIF伪指令设定新条件。
如:
DEFINE Test ;定义一个全局变量Test
……
IF Test = TRUE
指令序列1
ELSE
指令序列2
ENDIF
3.5.7 宏处理伪指令
1、MACRO和ENDM
该伪指令的格式为:
宏名 MACRO [,参数][ ,参数]……
指令序列
ENDM
MACRO伪指令用于定义一个宏,引用宏时必须使用定义的宏名,并可向宏中传递参数。ENDM伪指令用于结束宏定义。
如:
errmac MACRO text
BL abort
DATA
DC8 text,0
ENDM
包含在MACRO和ENDM之间的指令序列称为宏定义体。在宏定义体的第一行应声明宏的原型(包括宏名和所需的参数),然后就可以在汇编程序中通过宏名来调用该指令序列。在源程序被编译时,汇编器将宏调用展开,用宏定义中的指令序列代替程序中的宏调用,并将实际参数值传递给宏定义中的形式参数。
2、REPT和ENDR
该伪指令的格式为:
REPT 表达式
指令序列
ENDR
该伪指令用于指示汇编器将指定的指令序列进行重复汇编,重复次数由表达式的值确定。如果表达式的值为0,则不进行任何操作。
3、REPTC和ENDR
该伪指令的格式为:
REPTC 符号,替换字符串
指令序列
ENDR
该伪指令用于在宏展开时用替换字符串中的单个字符逐次替换符号。
4、REPTI和ENDR
该伪指令的格式为:
REPTI 符号,替换字符串[,替换字符串]……
指令序列
ENDR
该伪指令用于在宏展开时用整个替换字符串替换符号。
3.6 ARM汇编语言的语句格式
3.6.1 ARM汇编语言的语句格式
ARM(Thumb)汇编语言的语句格式为:
[标号[:]] 指令或伪指令 操作数 [;注释]
其中,方括号内的内容为可选项。
标号顶格书写时后面可不用冒号,非顶格书写时后面必须用冒号。
标号前加一个问号“?”前缀,表示该标号为外部标号,且仅能通过汇编语言访问;标号前加两个下划线“__”前缀,表示该标号为外部标号,能通过C语言和汇编语言访问;没有前缀的标号为局部标号,仅能在本模块内访问。
IAR汇编器对大小写字符敏感,一般指令和伪指令助记符使用大写,标号使用大小写混杂的方式以示区分。
同时,如果一条语句太长,可将该长语句分为若干行来书写,在行的末尾用“\”表示下一行与本行为同一条语句。
IAR汇编器规定汇编语言程序文件的默认扩展名为“.s79”,也可以用“.s”或“.asm”作为扩展名。
3.6.2 符号
在汇编语言程序设计中,经常使用各种符号代替地址、变量和常量等,以增加程序的可读性。尽管符号的命名由编程者决定,但并不是任意的,必须遵循以下的约定:
1.符号由大小写字母、数字及下划线组成,符号不能用数字开头。
2.符号区分大小写,同名的大、小写符号会被编译器认为是两个不同的符号。
3.符号在其作用范围内必须唯一。
4.自定义的符号名不能与系统的保留字相同。
5.符号名不应与指令或伪指令同名。
6. IAR汇编器内部预定义符号以双下划线开头和结尾。如:__IAR_SYSTEMS_ASM__。
3.6.3 常量和变量
1、 常量
程序中的常量是指其值在程序的运行过程中不能被改变的量。ARM(Thumb)汇编程序所支持的常量有数字常量、逻辑常量和字符串常量。
数字常量一般为32位的整数,当作为无符号数时,其取值范围为0~232-1,当作为有符号数时,其取值范围为-231~231-1。数字常量有4种表示形式:十进制数如123、-456等;十六进制数如0x123、0FFFFH等;八进制数如1234q等;二进制数如1010b等。
逻辑常量只有两种取值情况:TRUE和FALSE。
字符串常量为一个固定的字符串,一般用于程序运行时的信息提示。用法与标准C语言相同。
2、 变量
程序中的变量是指其值在程序的运行过程中可以改变的量。ARM(Thumb)汇编程序所支持的变量有数字变量、逻辑变量和字符串变量。
数字变量用于在程序的运行中保存数字值,但注意数字值的大小不应超出数字变量所能表示的范围。
逻辑变量用于在程序的运行中保存逻辑值,逻辑值只有两种取值情况:真或假。
字符串变量用于在程序的运行中保存一个字符串,但注意字符串的长度不应超出字符串变量所能表示的范围。
3.7 ARM汇编语言的程序结构
3.7.1 汇编语言的程序结构
在ARM(Thumb)汇编语言程序中,以程序段为单位组织代码。段是相对独立的指令或数据序列,具有特定的名称。段可以分为代码段和数据段,代码段的内容为执行代码,数据段存放代码运行时需要用到的数据。一个汇编程序至少应该有一个代码段,当程序较长时,可以分割为多个代码段和数据段,多个段在程序编译链接时最终形成一个可执行的映象文件。
可执行映象文件通常由以下几部分构成:
1. 1个或多个代码段,代码段的属性为只读。
2. 0个或多个包含初始化数据的数据段,数据段的属性为可读写。
3. 0个或多个不包含初始化数据的数据段,数据段的属性为可读写。
链接器根据系统默认或用户设定的规则,将各个段安排在存储器中的相应位置。因此源程序中段之间的相对位置与可执行的映象文件中段的相对位置一般不会相同。
3.7.2 一个简单的ARM汇编语言程序
以下是一个汇编语言源程序的基本结构:
代码清单3.1
NAME ASM_EXAMPL ;定义一个名为ARM_EXAMPL的程序模块
RSEG CODE:CODE:ROOT(2) ;定义一个可重定位的代码段
CODE32 ;执行32位ARM指令
ORG 0x1000 ;定义程序起始地址为0x1000
Start: LDR R0,=0x3FF5000 ;Start处的地址即为0x1000
LDR R1,=0xff
STR R1,[R0]
MOV R0,#0x10
MOV R1,#0x20
ADD R0,R0,R1
Stop: B Stop ;跳转到指令本身,程序停止运行
ENDMOD ;本程序模块结束
END ;本程序结束
程序很简单,它完成的功能并不重要,但它已经表示出了一个ARM汇编语言程序的基本结构。
3.8 ARM程序设计举例
3.8.1 分支程序
程序设计中的三种基本结构是:顺序结构、分支结构和循环结构。在C语言中可以使用if-else语句实现单分支和双分支结构,也可以通过switch-case语句实现多分支结构。但是在汇编语言中,分支结构一般是通过跳转指令结合标号来实现的。
在ARM汇编语言程序中,由于ARM指令支持条件执行,从而大大减少了分支程序的复杂程度。
例如:用两个整数辗转相减的方法求它们的最大公约数。注意体会B指令加条件码的执行方式。程序中使用的main标号是因为IAR汇编器一般默认从main标号处开始执行。
代码清单3.2
NAME GCD
PUBLIC main ;声明外部引用标号main
B main ;从main标号处开始执行
RSEG CODE:CODE
CODE32
main: MOV R0,#120
MOV R1,#96
Gcd: CMP R0,R1 ;比较两数的大小
BEQ Stop ;如果两数相等则跳到结束处
BLT Less ;如果R0<R1则跳到Less标号处
SUB R0,R0,R1 ;否则R0=R0-R1
B Gcd
Less: SUB R1,R1,R0 ;R1=R1-R0
B Gcd
Stop: B Stop ;跳转到指令本身,程序停止运行
ENDMOD ;本程序模块结束
END ;本程序结束
3.8.2 循环程序
通过跳转指令还可以实现程序的循环结构。
例如:求n=1+2+…+10累加的和。
代码清单3.3
NAME SUM
PUBLIC main
B main
RSEG CODE
CODE32
main: MOV R0,#10
MOV R1,R0 ;利用R1寄存器做循环计数器
Loop: SUBS R1,R1,1 ;循环次数减1
ADD R0,R0,R1
BNE Loop ;循环次数为0则结束循环
Stop: B Stop
ENDMOD
END
3.8.3 子程序调用
通过BL指令可以实现子程序调用,语法:BL子程序名。
在子程序的结束处,可以通过MOV PC,LR返回到主程序中。通常可以使用寄存器R0~R3完成传递参数到子程序和从子程序返回运算的结果。
以下是使用BL指令调用子程序的汇编语言源程序的例子,该程序编写了一个在内存里拷贝字符串的子程序,然后在主程序里调用它。
代码清单3.4
NAME STRCPY
PUBLIC main
B main
RSEG CODE
CODE32
main: LDR R1,=srcstr ;R1指向源字符串
LDR R0,=dststr ;R0指向目标字符串
BL strcopy ;调用strcopy子程序
stop: B stop ;程序停止
strcopy: ;子程序定义
LDRB R2,[R1],#1 ;读一个字符到R2,并更新源字符地址
STRB R2,[R0],#1 ;写一个字符,并更新目的字符地址
CMP R2,#0 ;是否结束。以数字0为标志
BNE strcopy ;循环执行
DATA ;数据区
srcstr DCB "First string - source ",0
dststr DCB "Second string - destination ",0
ENDMOD
END
3.8.4 查表法
查表法是编程中常用的一种技巧。当程序涉及到较多的数据、数据串或数据表格时,可以通过地址来对它们进行访问。通常有两种方法装载地址:(1)通过ADR和ADRL伪指令直接装载地址;(2)通过伪指令LDR Rd,=Label从数据表格中装载地址。
下面的程序设置了3个参数,arithfunc根据3个参数返回一个R0值。当R0=0时,R0=R1+R2;当R0=1时,R0=R1-R2;当R0>1时,R0=R1+R2。:
代码清单3.5
NAME JUMP
PUBLIC main
B main
Num EQU 2 ;跳转表格的入口数
RSEG CODE
CODE32
main: MOV R0,#0 ;以下设置3个参数
MOV R1,#3
MOV R2,#2
BL arithfunc ;调用子程序
stop: B stop ;程序停止
arithfunc:
CMP R0,#Num ;比较参数
BHS Doadd ;若R0>=2,则执行加法
ADR R3,jumptable ;装载跳转表格标号地址
LDR PC,[R3,R0,LSL #2] ;跳到相应子程序入口地址处
Jumptable:
DCD Doadd ;Doadd子程序的入口地址
DCD Dosub ;Dosub子程序的入口地址
Doadd: ADD R0,R1,R2 ;=0或>1时执行的操作
MOV PC,LR
Dosub: SUB R0,R1,R2 ;=1时执行的操作
MOV PC,LR
ENDMOD
END
3.8.5 汇编语言与C/C++的混合编程
在应用系统的程序设计中,若所有的编程任务均用汇编语言来完成,其工作量是可想而知的,同时,不利于系统升级或应用软件移植,事实上,ARM体系结构支持C/C+以及与汇编语言的混合编程,在一个完整的程序设计的中,除了初始化部分用汇编语言完成以外,其主要的编程任务一般都用C/C++ 完成。
汇编语言与C/C++的混合编程通常有以下几种方式:
1. 在C/C++代码中嵌入汇编指令。
在ARM C中,可以使用关键字__arm来标识一段汇编指令程序。格式如下:
__asm
{
汇编指令序列
}
即可在C语言源程序中直接执行ARM汇编指令。
2. 在汇编程序和C/C++的程序之间进行变量的互访。
3. 汇编程序、C/C++程序间的相互调用。
可以把汇编程序和C/C++程序中需要共享的变量或函数用PUBLIC或extern关键字分别声明为全局变量或全局函数,然后在其它程序文件中即可进行访问和调用。但是从好的编程风格来说,最好尽量减少全局变量和全局函数的使用。
混合编程中,必须遵守一定的调用规则,如物理寄存器的使用、参数的传递等。ARM专门为此制定了一个标准ATPCS(ARM-Thumb Procedure Call Standard,ARM-Thumb过程调用标准)。对于初学者来说,这是非常烦琐的,在实际工作中也没有太多必要。
在实际的编程应用中,使用较多的方式是:系统程序的初始化部分用汇编语言完成,然后用C/C++完成主要的编程任务,程序在执行时首先完成初始化过程,然后跳转到C/C++程序代码中。汇编程序和C/C++程序之间一般没有参数的传递,也没有频繁的相互调用,因此,整个程序的结构显得相对简单,容易理解。
以下是一个这种结构程序的基本示例。该程序非常简单,建立一个工程asm_c.eww,工程中包括一个汇编语言程序文件init.s79和一个C语言程序文件hello.c。
代码清单3.6——init.s79文件
NAME INIT
PUBLIC main
EXTERN Main ;声明引入C程序的Main()函数
B main
RSEG CODE
CODE32
main:
NOP ;此处可以插入用户自己编写的系统初始化代码
B Main ;转向C语言程序
ENDMOD
END
代码清单3.7——hello.c文件:
#include <stdio.h>
/*注意此处C语言程序的入口函数是大小写敏感的Main()函数,而不是常用的main()函数。这是为了跟汇编程序中的main入口区别开,以免造成工程有两个程序入口。*/
int Main(void)
{
printf("Hello, world!\n");
}
3.9 用ARM汇编语言编写系统启动程序
基于ARM内核的芯片多数为复杂的片上系统,这种复杂系统里的多数硬件模块都是可以配置的,需要由软件来设置其需要的工作状态。由于C语言具有模块性和可移植性的特点,大部分基于ARM的应用系统程序都采用C语言编写。但是当系统复位启动时,在进入C语言的main函数之前,需要有一段启动程序来完成对存储器配置、地址重映射和ARM芯片内部集成外围功能初始化等工作。这类工作直接面对处理器内核和硬件控制器进行编程,用C语言较难实现,因此一般采用汇编语言编写。
3.9.1 编写启动程序的一般规则
ARM内核的处理器在复位后,从0x00000000地址处开始读取指令。实现启动最简单的方法是将应用程序放在映射空间地址为0的ROM中。这样当执行第1条指令时,应用程序就从0x00000000处开始执行。但这种方法有很多缺点:ROM的存储宽度较小且速度较慢,会降低系统启动和处理器对异常处理的速度;异常向量表放在ROM中,程序将无法修改向量表,因此常将地址为0的空间映射成RAM,但RAM中的程序掉电无法保存,因此必须将ROM映射为0地址,以保证有效的复位向量,然后再使用重映射命令将RAM映射为0地址,ROM映射到其他地址空间,并将异常向量从ROM复制到RAM中。
编写启动程序应遵循以下一般规则:
1. 设置入口指针
启动程序首先必须定义入口指针,而且整个应用程序只有一个入口指针,通常应用程序的入口地址为0。
2. 设置异常向量
基于ARM7TDMI内核的处理器共支持7种异常,异常处理地址存放在地址0处的异常向量表中,共8×4字节的空间。异常向量表的内容参见2.4.2节。
异常向量表通常放在存储器底部,每个异常分配4个字节的空间。每个向量入口包含一条跳转指令或加载PC的指令,以执行适当的转移到具体的异常处理程序。如果ROM定位于0地址,则向量表由一系列固定的用以指向每个异常的指令组成;否则向量必须被动态初始化。可以在启动程序中添加一段代码,使其在运行时将向量表拷贝到0地址开始的存储器空间。对于没有使用的异常,使其指向一个只含返回指令的哑函数,以防止错误异常引起系统混乱。
3. 初始化片内集成外围功能
由于ARM公司仅设计内核并出售给其它半导体厂商,不同的厂商购买内核授权后加入自己的外围功能,从而导致ARM核处理器芯片丰富多样,但也使得不同芯片的启动代码在这一部分差别很大。编写这一部分时应根据芯片和应用系统要求对它们进行合适的初始化。比较重要的操作一般有:外部总线接口的初始化、配置时钟锁相环、配置中断控制器、禁用看门狗电路等。
4. 初始化存储系统
有些ARM核芯片可通过对寄存器编程来初始化系统存储器,而对于较复杂系统通常由存储管理单元MMU来管理内存空间。为正确运行应用程序,在初始化期间应将系统需要读写的数据和变量从ROM拷贝到RAM中;一些要求快速响应的程序,例如中断处理程序,也需要在RAM中运行;如果使用Flash,对Flash的擦除和写入操作也一定要在RAM中运行。
5. 初始化堆栈寄存器
系统堆栈初始化取决于用户使用了哪些中断,以及系统需要处理哪些错误类型。一般来说管理模式堆栈必须初始化。如果使用IRQ中断,则IRQ堆栈必须初始化,并且必须在允许中断之前进行。如果使用FIQ中断,则FIQ堆栈也必须初始化,并且必须在允许中断之前进行。一般在简单的嵌入式系统中不使用中止状态堆栈和未定义指令堆栈,但为了调试方便还是将其初始化。如果系统使用DRAM或其它外设,还需要设置相关寄存器,以确定其刷新频率、数据总线宽度等信息。
6. 改变处理器模式和状态
此时可以通过清除CPSR寄存器中的中断控制位来允许中断,这里是安全开启中断的最早地方。这个阶段处理器仍处于管理模式下。如果程序需要在用户模式下运行,可以在此处切换到用户模式并初始化用户模式堆栈指针。
7. 跳转到C语言主程序
在从启动程序跳转到C语言程序的main函数之前,还需要初始化数据存储空间。通常是加入一段循环代码对数据存储空间清0。这样做的主要原因是C语言中没有初值的变量默认值均为0。已经初始化变量的初值必须从ROM中复制到RAM中,其它变量的初值必须为0。
3.9.2 IAR EWARM软件包给出的一般启动程序
下面给出了IAR EWARM软件包提供的一般启动程序代码,实际应用中可以根据具体芯片及应用系统要求进行适当修改,以适应不同场合的需要。
代码清单3.8——IAR EWARM启动代码
;-----------------------------------------------------------------------------
; 文件中标号的命名规则:
; ?xxx - 仅能由汇编语言访问的外部标号
; __xxx - 可由C语言访问或定义的外部标号
; xxx - 单个模块中的局部标号(注意,本文件包含多个模块)
; main - 用户程序的起点
;---------------------------------------------------------------
; 适用于整个文件的宏和模式定义
;---------------------------------------------------------------
; 模式,对应于CPSR寄存器的0~5位
MODE_BITS DEFINE 0x1F ; 用于CPSR模式的位屏蔽
USR_MODE DEFINE 0x10 ; 用户模式
FIQ_MODE DEFINE 0x11 ; FIQ模式
IRQ_MODE DEFINE 0x12 ; IRQ模式
SVC_MODE DEFINE 0x13 ; 管理模式
ABT_MODE DEFINE 0x17 ; 中止模式
UND_MODE DEFINE 0x1B ; 未定义指令模式
SYS_MODE DEFINE 0x1F ; 系统模式
;---------------------------------------------------------------
; ?RESET
; 复位向量。通常INTVEC段被链接到地址0。为程序调试方便,也可以放在其它地址
;---------------------------------------------------------------
MODULE ?RESET
COMMON INTVEC:CODE:NOROOT(2)
PUBLIC __program_start
EXTERN ?cstartup
EXTERN undef_handler, swi_handler, prefetch_handler
EXTERN data_handler, irq_handler, fiq_handler
CODE32 ; 复位后始终为ARM模式
org 0x00
__program_start
ldr pc,[pc,#24] ; 绝对跳转地址范围为4GB
; ldr b,?cstartup ; 相对跳转允许重映射,限于32MB
; 可以去掉以下指令前的注释分号来允许异常向量
; 也可以在C语言中采用预编译命令“#pragma vector”
org 0x04
; ldr pc,[pc,#24] ; 跳转到undef_handler
org 0x08
; ldr pc,[pc,#24] ; 跳转到swi_handler
org 0x0c
; ldr pc,[pc,#24] ; 跳转到prefetch_handler
org 0x10
; ldr pc,[pc,#24] ; 跳转到data_handler
org 0x18
; ldr pc,[pc,#24] ; 跳转到irq_handler
org 0x1c
; ldr pc,[pc,#24] ; 跳转到fiq_handler
; 用于“ldr pc”指令的常数表入口定位于0x20
; 异常向量可以用C语言的预编译命令“#pragma vector”指定,也可以
; 在以下dc32指令后面填入向量地址。向量地址为ARM向量号+20
org 0x20
dc32 ?cstartup
org 0x24
; dc32 undef_handler
org 0x28
; dc32 swi_handler
org 0x2c
; dc32 prefetch_handler
org 0x30
; dc32 data_handler
org 0x38
; dc32 irq_handler
org 0x3c
; dc32 fiq_handler
LTORG
; ENDMOD __program_start
ENDMOD
;---------------------------------------------------------------
; ?CSTARTUP
;---------------------------------------------------------------
MODULE ?CSTARTUP
RSEG IRQ_STACK:DATA(2)
RSEG ABT_STACK:DATA:NOROOT(2)
RSEG UND_STACK:DATA:NOROOT(2)
RSEG FIR_STACK:DATA:NOROOT(2)
RSEG SVC_STACK:DATA:NOROOT(2)
RSEG CSTACK:DATA(2)
RSEG ICODE:CODE:NOROOT(2)
PUBLIC ?cstartup
EXTERN ?main
; 从这里开始执行
; 复位后为ARM管理模式,禁止中断
CODE32
?cstartup
; 需要时在这里加入建立堆栈指针之前的初始化指令
; 初始化堆栈指针
; 以下方式可用于任何异常堆栈:FIQ, IRQ, SVC, ABT, UND, SYS.
; 用户模式使用与SYS模式相同的堆栈
; 堆栈段必须在链接器命令文件中定义,并且已经在上面声明
mrs r0,cpsr ; 原PSR值
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#IRQ_MODE ; 置IRQ模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(IRQ_STACK)&0xFFFFFFF8 ; IRQ_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#ABT_MODE ; 置Abort模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(ABT_STACK)&0xFFFFFFF8 ; ABT_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#SVC_MODE ; 置Supervisor模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(SVC_STACK) & 0xFFFFFFF8 ; SVC_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#UND_MODE ; 置Undefined模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(UND_STACK) & 0xFFFFFFF8 ; FIR_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#FIQ_MODE ; 置FIR模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(FIR_STACK) & 0xFFFFFFF8 ; FIR_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#SYS_MODE ; 置System模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(CSTACK) & 0xFFFFFFF8 ; CSTACK结束
#ifdef __ARMVFP__
; 允许VFP协处理器
mov r0, #0x40000000 ; 置VFP的EN位
fmxr fpexc, r0 ; FPEXC, 清除其它
; 将缓冲区清0以禁止下溢出。为满足IEEE 754标准,应删除该指令并安装合适的异常句柄
mov r0, #0x01000000 ; 置VFP的FZ位
fmxr fpscr, r0 ; FPSCR, 清除其它
#endif
; 在这里可以添加更多的用户自定义初始化指令
; 跳转到?main标号的地方,继续IAR系统的启动程序
ldr r0,=?main
bx r0
LTORG
ENDMOD
END
习题
3.1 ARM7TDMI有几种寻址方式?LDR R1,[R0,#0x04]属于哪种寻址方式?
3.2 ARM指令的条件码有多少个?默认条件码是什么?
3.3 ARM指令中第二个操作数有哪几种形式?
3.4 请指出MOV指令与LDR加载指令的区别及用途.
3.5 CMP指令的功能是什么?写一个程序,判断R1的值是否大于0x30,是则将R1减去0x30。
3.6 调用子程序是用B还是用BL指令?请写出返回子程序的指令。
3.7 ARM状态与Thumb状态的切换指令是什么?请举例说明。
3.8 Thumb状态与ARM状态的寄存器有区别吗?Thumb指令对哪些寄存器的访问受到一定限制?
3.9 Thumb指令集的堆栈入栈、出栈指令是哪两条?
3.10 把下面的C代码转换成汇编代码。数组a和b分别存放在以0x4000和0x5000为起始地址的存储区内,类型为long型(32位)。
for(i=0;i<8;i++)
{
a[i] = b[7-i];
}
3.11 编写程序,将R1的高8位传送到R2的低8位
3.12 编写一段64位加法运算的程序,要求满足:[R1:R0]+[R3:R2],结果存入[R1:R0]中
3.13 编写程序将地址0x0000 1000到0x0000 1030的数据全部搬迁到0x0000 2000到0x0000 2030的区域中,并将源数据区清零。
第4章 LPC2400系列处理器原理
处理器的“体系结构”指从程序员角度观察到的处理器的组织方式,所以又称为处理器的编程模型。其主要内容为处理器内的寄存器组织、对存储器的寻址方式、指令系统等。本章将介绍ARM7TDMI程序员模型、工作状态与工作模式、ARM和Thumb状态的寄存器组织、存储器组织结构、异常及协处理器接口等一些基本概念。本章还要讲述ARM的编程基础,如ARM微处理器的基本工作原理、与程序设计相关的基本技术细节等。
4.1 LPC2400系列处理器简介
4.1.1 LPC2400系列处理器特性
LPC2400系列处理器包括LPC2468/LPC2470/LPC2478等多款芯片,是基于支持实时仿真和跟踪的16/32位ARM7TDMI-S内核的微控制器,它与所有NXP LPC 2000处理器具有相同的存储器映射、中断向量控制、Flash编程和更新机制,以及调试和仿真功能。LPC2468/LPC2478的512KB大容量嵌入式高速Flash存储器具有128位宽度的存储器接口和独特的加速结构,使得32位代码能够在最高时钟频率72MHz下运行。16位Thumb模式可以将代码规模降低30%以上,而性能损失却很小。LPC2470/LPC2478芯片内部还集成了LCD接口支持(最高1024×768像素、15阶灰度单色和每像素24位真彩色TFT面板),使得这两款芯片可以广泛应用于各种手持式设备中。
LPC2400系列处理器拥有丰富的片上资源和外设接口。这一系列芯片的共同特性有:
-ARM7TDMI-S内核,最高72MHz主频;
-98KB的片内静态存储器,其中64KB的片内SRAM,16KB SRAM用于以太网,16KB SRAM用于DMA控制器(也可用于USB控制器),2KB SRAM用于RTC实时时钟;
-512KB片内Flash程序存储器,片内Boot实现IAP和ISP片内Flash编程;
-可配置的外部存储器接口,最多支持8个Bank,支持外部RAM、ROM和Flash存储器扩展,每个Bank最大可支持到256MB,可支持8/16/32位字宽;
-高级向量中断控制器,支持32个向量中断,可配置优先级和向量地址;
-通用AHB DMA控制器(GPDMA)可以用于支持SSP、I2S和SD/MMC接口;
-10/100M以太网MAC接口;
-多个串行接口,包括4路UART、3路I2C串行总线接口和1个SPI接口;
-10位A/D和D/A转换器,转换时间低至2.44微秒;
-USB device/host/OTG接口;
-2个CAN总线接口;
-4个32位的定时器、2个PWM脉冲调制单元(每个6路输出)、实时时钟和看门狗;
-160个高速GPIO端口(可承受5V电压),4个独立外部中断引脚;
-标准ARM调试接口,兼容各种现有的调试工具;
-片内晶振频率范围1~24MHz;
-4个低功耗模式:空闲、睡眠、掉电和深度掉电模式;
-供电电压3.3V(3.0V~3.6V)。
在LPC2400系列芯片中,LPC2468是LPC2478的无LCD控制器版本,LPC2470是LPC2478的无片内Flash版本,芯片的大多数特性是完全相同的。所以在后面的章节中,本书一律采用LPC2478芯片为例进行讲解,请读者在实际工作中注意具体芯片的差别。
4.1.2 LPC2400系列处理器结构
LPC2400系列处理器包含一个支持仿真的ARM7TDMI-S CPU、与片内存储器控制器接口的ARM7局部总线、与中断控制器接口的AMBA高性能总线(AHB总线)和连接片内外设功能的AMBA外设总线(APB总线)。存储模式为小端模式。
AHB总线和APB总线都是ARM公司推出的AMBA片上总线规范的一部分。AHB(Advanced High performance Bus)系统总线主要用于高性能模块(如CPU、DMA和DSP等)之间的连接,一般用于片内高性能高速度的外设,如:外部存储器、USB接口、DMA控制器、以太网控制器、LCD液晶屏控制器以及高速GPIO控制器等。LPC2400中的AHB外设一共分配了2MB的地址范围,它位于4GB ARM存储器空间的最顶端。每个AHB外设都分配了16KB的地址空间。
LPC2400的外设功能模块都连接到APB总线。APB(Advanced Peripheral Bus)外围总线主要用于低带宽的周边外设之间的连接,如:UART、I2C、SPI、I2S、A/D、D/A、CAN等等。APB总线与AHB总线之间通过AHB到APB的桥相连。APB外设也分配了2MB的地址范围,每个APB外设在APB地址空间内都分配了16KB的地址空间。
片内外设与器件引脚的连接由引脚连接模块控制。软件可以通过控制该模块让引脚与特定的片内外设相连接。
LPC2400的结构框图如图4.1所示。
图4.1 LPC2400结构框图
4.2 处理器引脚配置
4.2.1引脚配置
LPC2400系列处理器共有208个引脚,一般提供两种封装形式:LQFP208和TFBGA208。其管脚封装如图4.2所示。
LQFP208 FBGA208
图4.2 LPC2400系列处理器管脚封装图
LQFP指封装本体厚度为1.4mm的薄型QFP(四侧引脚扁平封装quad flat package),它是一种表面贴装型封装,引脚从四个侧面引出呈L型,每个侧面52个引脚,引脚号分别为1~52、53~104、105~156、157~208。FBGA是塑料封装的BGA(Ball Grid Array Package),即球栅阵列封装,其引脚都在芯片底部,用英文字母行和数字列标识。由于LPC2400系列处理器在实际使用中更多使用QFP封装,本节引脚介绍以QFP封装为准。
从功能上,LPC2400的208个引脚分为P0口、P1口、P2口、P3口、P4口,以及电源、复位、晶振和其它管脚几部分。下面对这几个部分分别进行介绍。
1. P0口: P0口是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P0口管脚描述如表4.1所示。
表4.1 LPC2400的P0口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P0[0] |
94 | I/O | P0[0]:GPIO口 |
I | RD1:CAN1接收器输入 | ||
O | TXD3:UART3发送输出端 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P0[1] |
96 | I/O | P0[1]:GPIO口 |
O | TD1:CAN1发送器输出 | ||
I | RXD3:UART3接收输入端 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P0[2] |
202 | I/O | P0[2]:GPIO口 |
O | TXD0:UART0发送输出端 | ||
P0[3] |
204 | I/O | P0[3]:GPIO口 |
I | RXD0:UART0接收输入端 | ||
P0[4] |
168 | I/O | P0[4]:GPIO口 |
I/O | I2SRX_CLK:I2S总线接收时钟 | ||
I | RD2:CAN2接收输入端 | ||
I | CAP2[0]:Timer2的捕获输入通道0 | ||
P0[5] |
166 | I/O | P0[5]:GPIO口 |
I/O | I2SRX_WS:I2S总线接收字选择 | ||
I | TD2:CAN2发送输出端 | ||
I | CAP2[1]:Timer2的捕获输入通道1 | ||
P0[6] |
164 | I/O | P0[6]:GPIO口 |
I/O | I2SRX_SDA:I2S总线数据接收 | ||
I/O | SSEL1:SSP1从机选择 | ||
O | MAT2[0]:Timer2的匹配输出通道0 | ||
P0[7] |
162 | I/O | P0[7]:GPIO口 |
I/O | I2STX_CLK:I2S总线发送时钟 | ||
I/O | SCK1:SSP1串行时钟 | ||
O | MAT2[1]:Timer2的匹配输出通道1 | ||
P0[8] |
160 | I/O | P0[8]:GPIO口 |
I/O | I2STX_WS:I2S总线发送字选择 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
O | MAT2[2]:Timer2的匹配输出通道2 | ||
P0[9] |
158 | I/O | P0[9]:GPIO口 |
I/O | I2STX_SDA:I2S总线数据发送 | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
O | MAT2[3]:Timer2的匹配输出通道3 | ||
P0[10] |
98 | I/O | P0[10]:GPIO口 |
O | TXD2:UART2发送输出端 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
O | MAT3[0]:Timer3的匹配输出通道0 | ||
P0[11] |
100 | I/O | P0[11]:GPIO口 |
I | RXD2:UART2接收输入端 | ||
I/O | SCL2:I2C2时钟输入/输出 | ||
O | MAT3[1]:Timer3的匹配输出通道1 | ||
P0[12] |
41 | I/O | P0[12]:GPIO口 |
O | USB_PPWR2:USB端口2端口电源使能 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
I | AD0[6]:A/D转换器0输入6 | ||
P0[13] |
45 | I/O | P0[13]:GPIO口 |
O | USB_UP_LED2:USB端口2LED | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
I | AD0[7]:A/D转换器0输入7 | ||
P0[14] |
69 | I/O | P0[14]:GPIO口 |
O | USB_HSTEN2:USB端口2主机使能 | ||
O | USB_CONNECT2:USB端口2软件连接控制 | ||
I/O | SSEL1:SSP1从机选择 | ||
P0[15] |
128 | I/O | P0[15]:GPIO口 |
O | TXD1:UART1发送输出端 | ||
I/O | SCK0:SSP0串行时钟 | ||
I/O | SCK:SPI串行时钟 | ||
P0[16] |
130 | I/O | P0[16]:GPIO口 |
I | RXD1:UART1接收输入端 | ||
I/O | SSEL0:SSP0从机选择 | ||
I/O | SSEL:SPI从机选择 | ||
P0[17] |
126 | I/O | P0[17]:GPIO口 |
I | CTS1:UART1清除发送输入端 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
I/O | MISO:SPI主机输入从机输出 | ||
P0[18] |
124 | I/O | P0[18]:GPIO口 |
I | DCD1:UART1数据载波检测输入端 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
I/O | MOSI:SPI主机输出从机输入 | ||
P0[19] |
122 | I/O | P0[19]:GPIO口 |
I | DSR1:UART1数据设置就绪端 | ||
O | MCICLK:SD/MMC接口时钟输出线 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P0[20] |
120 | I/O | P0[20]:GPIO口 |
O | DTR1:UART1数据终止就绪端 | ||
I/O | MCICMD:SD/MMC接口命令线 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P0[21] |
118 | I/O | P0[21]:GPIO口 |
I | RI1:UART1铃响指示输入端 | ||
O | MCIPWR:SD/MMC电源供应使能 | ||
I | RD1:CAN1接收输入端 | ||
P0[22] |
116 | I/O | P0[22]:GPIO口 |
O | RTS1:UART1请求发送输出端 | ||
I/O | MCIDAT0:SD/MMC接口数据线0 | ||
O | TD1:CAN1发送输出端 | ||
P0[23] |
18 | I/O | P0[23]:GPIO口 |
I | AD0[0]:A/D转换器0输入0 | ||
I/O | I2SRX_CLK:I2S总线接收时钟 | ||
I | CAP3[0]:Timer3的捕获输入通道0 | ||
P0[24] |
16 | I/O | P0[24]:GPIO口 |
I | AD0[1]:A/D转换器0输入1 | ||
I/O | I2SRX_WS:I2S总线字选择 | ||
I | CAP3[1]:Timer3的捕获输入通道1 | ||
P0[25] |
14 | I/O | P0[25]:GPIO口 |
I | AD0[2]:A/D转换器0输入2 | ||
I/O | I2SRX_SDA:I2S总线数据接收 | ||
O | TXD3:UART3发送输出端 | ||
P0[26] |
12 | I/O | P0[26]:GPIO口 |
I | AD0[3]:A/D转换器0输入3 | ||
O | AOUT:D/A转换器输出 | ||
I | RXD3:UART3接收输入端 | ||
P0[27] |
50 | I/O | P0[27]:GPIO口 |
I/O | SDA0:I2C0数据输入/输出 | ||
P0[28] |
48 | I/O | P0[28]:GPIO口 |
I/O | SCL0:I2C0时钟输入/输出 | ||
P0[29] |
61 | I/O | P0[29]:GPIO口 |
I/O | USB_D+1:USB端口1双向D+线 | ||
P0[30] |
62 | I/O | P0[30]:GPIO口 |
I/O | USB_D-1:USB端口1双向D-线 | ||
P0[31] |
51 | I/O | P0[31]:GPIO口 |
I/O | USB_D+2:USB端口2双向D+线 |
2. P1口: P1口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P1口管脚描述如表4.2所示。
表4.2 LPC2400的P1口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P1[0] |
196 | I/O | P1[0]:GPIO口 |
O | ENET_TXD0:以太网发送数据0(RMII/MII接口) | ||
P1[1] |
194 | I/O | P1[1]:GPIO口 |
O | ENET_TXD1:以太网发送数据1(RMII/MII接口) | ||
P1[2] |
185 | I/O | P1[2]:GPIO口 |
O | ENET_TXD2:以太网发送数据2(RMII/MII接口) | ||
O | MCICLK:SD/MMC接口时钟输出线 | ||
O | PWM0[1]:脉宽调制器0输出1 | ||
P1[3] |
177 | I/O | P1[3]:GPIO口 |
O | ENET_TXD3:以太网发送数据3(RMII/MII接口) | ||
O | MCICMD:SD/MMC接口命令线 | ||
O | PWM0[2]:脉宽调制器0输出2 | ||
P1[4] |
192 | I/O | P1[4]:GPIO口 |
O | ENET_TX_EN:以太网发送数据使能(RMII/MII接口) | ||
P1[5] |
156 | I/O | P1[5]:GPIO口 |
O | ENET_TX_ER:以太网发送数据出错(MII接口) | ||
O | MCIPWR:SD/MMC电源供应使能 | ||
O | PWM0[3]:脉宽调制器0输出3 | ||
P1[6] |
171 | I/O | P1[6]:GPIO口 |
I | ENET_TX_CLK:以太网发送时钟(MII接口) | ||
I/O | MCIDAT0:SD/MMC接口数据线0 | ||
O | PWM0[4]:脉宽调制器0输出4 | ||
P1[7] |
153 | I/O | P1[7]:GPIO口 |
I | ENET_COL:以太网冲突检测(MII接口) | ||
I/O | MCIDAT1:SD/MMC接口数据线1 | ||
O | PWM0[5]:脉宽调制器0输出5 | ||
P1[8] |
190 | I/O | P1[8]:GPIO口 |
I | ENET_CRS_DV/ENET_CRS:以太网载波检测/数据有效(RMII接口)/以太网载波检测(MII接口) | ||
P1[9] |
188 | I/O | P1[9]:GPIO口 |
I | ENET_RXD0:以太网接收数据0(RMII/MII接口) | ||
P1[10] |
186 | I/O | P1[10]:GPIO口 |
I | ENET_RXD1:以太网接收数据1(RMII/MII接口) | ||
P1[11] |
163 | I/O | P1[11]:GPIO口 |
I | ENET_RXD2:以太网接收数据2(RMII/MII接口) | ||
I/O | MCIDAT2:SD/MMC接口数据线2 | ||
O | PWM0[6]:脉宽调制器0输出6 | ||
P1[12] |
157 | I/O | P1[12]:GPIO口 |
I | ENET_RXD3:以太网接收数据3(RMII/MII接口) | ||
I/O | MCIDAT3:SD/MMC接口数据线3 | ||
I | PCAP0[0]:脉宽调制器0捕获输入通道0 | ||
P1[13] |
147 | I/O | P1[13]:GPIO口 |
I | ENET_RX_DV:以太网接收数据有效(MII接口) | ||
P1[14] |
184 | I/O | P1[14]:GPIO口 |
I | ENET_RX_ER:以太网接收错误(MII接口) | ||
P1[15] |
182 | I/O | P1[15]:GPIO口 |
I | ENET_REF_CLK/ENET_RX_CLK:以太网参考时钟(RMII接口)/以太网接收时钟(MII接口) | ||
P1[16] |
180 | I/O | P1[16]:GPIO口 |
I | ENET_MDC:以太网MIIM时钟 | ||
P1[17] |
178 | I/O | P1[17]:GPIO口 |
I/O | ENET_MDIO:以太网MI数据输入输出 | ||
P1[18] |
66 | I/O | P1[18]:GPIO口 |
O | USB_UP_LED1:USB端口1LED | ||
O | PWM1[1]:脉宽调制器1输出1 | ||
I | CAP1[0]:Timer1捕获输入通道0 | ||
P1[19] |
68 | I/O | P1[19]:GPIO口 |
O | USB_TX_E1:USB端口1发送使能信号(OTG收发器) | ||
O | USB_PPWR1:USB端口1端口电源使能信号 | ||
I | CAP1[1]:Timer1捕获输入通道1 | ||
P1[20] |
70 | I/O | P1[20]:GPIO口 |
O | USB_TX_DP1:USB端口1D+数据发送(OTG收发器) | ||
O | PWM1[2]:脉宽调制器1输出2 | ||
I/O | SCK0:SSP0串行时钟 | ||
P1[21] |
72 | I/O | P1[21]:GPIO口 |
O | USB_TX_DM1:USB端口1D-数据发送(OTG收发器) | ||
O | PWM1[3]:脉宽调制器1输出3 | ||
I/O | SSEL0:SSP0从机选择 | ||
P1[22] |
74 | I/O | P1[22]:GPIO口 |
I | USB_RCV1:USB端口1差分数据接收(OTG收发器) | ||
I | USB_PWRD1:USB端口1电源状态(主机电源开关) | ||
O | MAT1[0]:Timer1匹配输出通道0 | ||
P1[23] |
76 | I/O | P1[23]:GPIO口 |
I | USB_RX_DP1:USB端口1D+数据接收(OTG收发器) | ||
O | PWM1[4]:脉宽调制器1输出4 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
P1[24] |
78 | I/O | P1[24]:GPIO口 |
I | USB_RX_DM1:USB端口1D-数据接收(OTG收发器) | ||
O | PWM1[5]:脉宽调制器1输出5 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
P1[25] |
80 | I/O | P1[25]:GPIO口 |
O | USB_LS1:USB端口1低速状态(OTG收发器) | ||
O | USB_HSTEN1:USB端口1主机使能状态 | ||
O | MAT1[1]:Timer1匹配输出通道1 | ||
P1[26] |
82 | I/O | P1[26]:GPIO口 |
O | USB_SSPND1:USB端口1总线悬挂状态(OTG收发器) | ||
O | PWM1[6]:脉宽调制器1输出6 | ||
I | CAP0[0]:Timer0捕获输入通道0 | ||
P1[27] |
88 | I/O | P1[27]:GPIO口 |
I | USB_INT1:USB端口1OTG ATX中断(OTG收发器) | ||
I | USB_OVRCR1:USB端口1过流状态 | ||
I | CAP0[1]:Timer0捕获输入通道1 | ||
P1[28] |
90 | I/O | P1[28]:GPIO口 |
I/O | USB_SCL1:USB端口1I2C串行时钟(OTG收发器) | ||
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
O | MAT0[0]:Timer0匹配输出通道0 | ||
P1[29] |
92 | I/O | P1[29]:GPIO口 |
I/O | USB_SDA1:USB端口1I2C串行数据(OTG收发器) | ||
I | PCAP1[1]:脉宽调制器1捕获输入通道1 | ||
O | MAT0[1]:Timer0匹配输出通道1 | ||
P1[30] |
42 | I/O | P1[30]:GPIO口 |
I | USB_PWRD2:USB端口2电源状态 | ||
I | VBUS:指示USB总线当前电源。注意:当USB复位时这个信号必须为高电平 | ||
I | AD0[4]:A/D转换器0输入4 | ||
P1[31] |
40 | I/O | P1[31]:GPIO口 |
I | USB_OVRCR2:USB端口2过流状态 | ||
I/O | SCK1:SSP1串行时钟 | ||
I | AD0[5]:A/D转换器0输入5 |
3. P2口: P2口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P2口管脚描述如表4.3所示。
表4.3 LPC2400的P2口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P2[0] |
154 | I/O | P2[0]:GPIO口 |
O | PWM1[1]:脉宽调制器1输出1 | ||
O | TXD1:UART1发送输出端 | ||
O | TRACECLK/LCDPWR:跟踪时钟/LCD面板电源使能 | ||
P2[1] |
152 | I/O | P2[1]:GPIO口 |
O | PWM1[2]:脉宽调制器1输出2 | ||
I | RXD1:UART1接收输入端 | ||
O | PIPESTAT0/LCDLE:流水线状态位0/LCD行结束信号 | ||
P2[2] |
150 | I/O | P2[2]:GPIO口 |
O | PWM1[3]:脉宽调制器1输出3 | ||
I | CTS1:UART1清除发送输入端 | ||
O | PIPESTAT1/LCDCP:流水线状态位1/LCD面板时钟 | ||
P2[3] |
144 | I/O | P2[3]:GPIO口 |
O | PWM1[4]:脉宽调制器1输出4 | ||
I | DCD1:UART1数据载波检测输入端 | ||
O | PIPESTAT2/LCDFP:流水线状态位2/LCD帧脉冲(STN)垂直同步脉冲(TFT) | ||
P2[4] |
142 | I/O | P2[4]:GPIO口 |
O | PWM1[5]:脉宽调制器1输出5 | ||
I | DSR1:UART1数据设置就绪端 | ||
O | TRACESYNC/LCDAC:跟踪同步/LCD交流斜线驱动(STN)数据使能输出(TFT) | ||
P2[5] |
140 | I/O | P2[5]:GPIO口 |
O | PWM1[6]:脉宽调制器1输出6 | ||
O | DTR1:UART1数据终止就绪端 | ||
O | TRACEPKT0/LCDAC:跟踪分组位0/LCD行同步脉冲(STN)水平同步脉冲(TFT) | ||
P2[6] |
138 | I/O | P2[6]:GPIO口 |
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
I | RI1:UART1响铃指示输入端 | ||
O | TRACEPKT1/LCD[0]/LCD[4]:跟踪分组位1/LCD数据 | ||
P2[7] |
136 | I/O | P2[7]:GPIO口 |
I | RD2:CAN2接收输入 | ||
O | RTS1:UART1请求发送输出端 | ||
O | TRACEPKT2/LCD[1]/LCD[5]:跟踪分组位1/LCD数据 | ||
P2[8] |
134 | I/O | P2[8]:GPIO口 |
O | TD2:CAN2发送输出 | ||
O | TXD2:UART2接收输入端 | ||
O | TRACEPKT3/LCD[2]/LCD[6]:跟踪分组位3/LCD数据 | ||
P2[9] |
132 | I/O | P2[9]:GPIO口 |
O | USB_CONNECT1:USB1软连接控制 | ||
I | RXD2:UART2接收输入 | ||
I | EXTINT0/LCD[3]/LCD[7]:外部触发中断输入/LCD数据 | ||
P2[10] |
110 | I/O | P2[10]:GPIO口 |
I | EINT0:外部中断0输入 | ||
P2[11] |
108 | I/O | P2[11]:GPIO口 |
I/O | EINT1:外部中断1输入/LCDCLKIN:LCD时钟 | ||
I/O | MCIDAT1:SD/MMC接口数据线1 | ||
I/O | I2STX_CLK:I2S传输时钟。 | ||
P2[12] |
106 | I/O | P2[12]:GPIO口 |
I/O | EINT2:外部中断2输入/输出:LCD[4]/LCD[3]/LCD[8]/LCD[18] | ||
I/O | MCIDAT2:SD/MMC接口数据线2 | ||
I/O | I2STX_WS:I2S传输字选择。 | ||
P2[13] |
102 | I/O | P2[13]:GPIO口 |
I/O | EINT3:外部中断3输入/输出:LCD[5]/LCD[9]/LCD[19] | ||
I/O | MCIDAT3:SD/MMC接口数据线3 | ||
I/O | I2STX_SDA:I2S传输数据。 | ||
P2[14] |
91 | I/O | P2[14]:GPIO口 |
O | CS2:低电平有效片选信号2 | ||
I | CAP2[0]:Tmer2捕获输入通道0 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P2[15] |
99 | I/O | P2[15]:GPIO口 |
O | CS3:低电平有效片选信号3 | ||
I | CAP2[1]:Tmer2捕获输入通道1 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P2[16] |
87 | I/O | P2[16]:GPIO口 |
O | CAS:低电平有效SDRAM列地址选择 | ||
P2[17] |
95 | I/O | P2[17]:GPIO口 |
O | RAS:低电平有效SDRAM行地址选择 | ||
P2[18] |
59 | I/O | P2[18]:GPIO口 |
O | CLKOUT0:SDRAM时钟0 | ||
P2[19] |
67 | I/O | P2[19]:GPIO口 |
O | CLKOUT1:SDRAM时钟1 | ||
P2[20] |
73 | I/O | P2[20]:GPIO口 |
O | DYCS0:SDRAM片选信号0 | ||
P2[21] |
81 | I/O | P2[21]:GPIO口 |
O | DYCS1:SDRAM片选信号1 | ||
P2[22] |
85 | I/O | P2[22]:GPIO口 |
O | DYCS2:SDRAM片选信号2 | ||
I | CAP3[0]:Timer3捕获输入通道0 | ||
I/O | SCK0:SSP0串行时钟 | ||
P2[23] |
64 | I/O | P2[23]:GPIO口 |
O | DYCS3:SDRAM片选信号3 | ||
I | CAP3[1]:Timer3捕获输入通道1 | ||
I/O | SSEL0:SSP0从机选择 | ||
P2[24] |
53 | I/O | P2[24]:GPIO口 |
O | CKEOUT0:SDRAM时钟使能信号0 | ||
P2[25] |
54 | I/O | P2[25]:GPIO口 |
O | CKEOUT1:SDRAM时钟使能信号1 | ||
P2[26] |
57 | I/O | P2[26]:GPIO口 |
O | CKEOUT2:SDRAM时钟使能信号2 | ||
O | MAT3[0]:Tmer3匹配输出通道0 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
P2[27] |
47 | I/O | P2[27]:GPIO口 |
O | CKEOUT3:SDRAM时钟使能信号3 | ||
O | MAT3[1]:Tmer3匹配输出通道1 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
P2[28] |
49 | I/O | P2[28]:GPIO口 |
O | DQMOUT0:用于SDRAM和静态设备的数据掩码0 | ||
P2[29] |
43 | I/O | P2[29]:GPIO口 |
O | DQMOUT1:用于SDRAM和静态设备的数据掩码1 | ||
P2[30] |
31 | I/O | P2[30]:GPIO口 |
O | DQMOUT2:用于SDRAM和静态设备的数据掩码2 | ||
O | MAT3[2]:Tmer3匹配输出通道2 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
P2[31] |
39 | I/O | P2[31]:GPIO口 |
O | DQMOUT3:用于SDRAM和静态设备的数据掩码3 | ||
O | MAT3[3]:Tmer3匹配输出通道3 | ||
I/O | SCL2:I2C2时钟输入/输出 |
4. P3口: P3口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P3口管脚描述如表4.4所示。
表4.4 LPC2400的P3口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P3[0] |
197 | I/O | P3[0]:GPIO口 |
I/O | D0:外部存储器数据线0 | ||
P3[1] |
201 | I/O | P3[1]:GPIO口 |
I/O | D1:外部存储器数据线1 | ||
P3[2] |
207 | I/O | P3[2]:GPIO口 |
I/O | D2:外部存储器数据线2 | ||
P3[3] |
3 | I/O | P3[3]:GPIO口 |
I/O | D3:外部存储器数据线3 | ||
P3[4] |
13 | I/O | P3[4]:GPIO口 |
I/O | D4:外部存储器数据线4 | ||
P3[5] |
17 | I/O | P3[5]:GPIO口 |
I/O | D5:外部存储器数据线5 | ||
P3[6] |
23 | I/O | P3[6]:GPIO口 |
I/O | D6:外部存储器数据线6 | ||
P3[7] |
27 | I/O | P3[7]:GPIO口 |
I/O | D7:外部存储器数据线7 | ||
P3[8] |
191 | I/O | P3[8]:GPIO口 |
I/O | D8:外部存储器数据线8 | ||
P3[9] |
199 | I/O | P3[9]:GPIO口 |
I/O | D9:外部存储器数据线9 | ||
P3[10] |
205 | I/O | P3[10]:GPIO口 |
I/O | D10:外部存储器数据线10 | ||
P3[11] |
208 | I/O | P3[11]:GPIO口 |
I/O | D11:外部存储器数据线11 | ||
P3[12] |
1 | I/O | P3[12]:GPIO口 |
I/O | D12:外部存储器数据线12 | ||
P3[13] |
7 | I/O | P3[13]:GPIO口 |
I/O | D13:外部存储器数据线13 | ||
P3[14] |
21 | I/O | P3[14]:GPIO口 |
I/O | D14:外部存储器数据线14 | ||
P3[15] |
28 | I/O | P3[15]:GPIO口 |
I/O | D15:外部存储器数据线15 | ||
P3[16] |
137 | I/O | P3[16]:GPIO口 |
I/O | D16:外部存储器数据线16 | ||
O | PWM0[1]:脉宽调制器0输出1 | ||
O | TXD1:UART1发送输出端 | ||
P3[17] |
143 | I/O | P3[17]:GPIO口 |
I/O | D17:外部存储器数据线17 | ||
O | PWM0[2]:脉宽调制器0输出2 | ||
I | RXD1:UART1接收输入端 | ||
P3[18] |
151 | I/O | P3[18]:GPIO口 |
I/O | D18:外部存储器数据线18 | ||
O | PWM0[3]:脉宽调制器0输出3 | ||
I | CTS1:UART1清除发送输入端 | ||
P3[19] |
161 | I/O | P3[19]:GPIO口 |
I/O | D19:外部存储器数据线19 | ||
O | PWM0[4]:脉宽调制器0输出4 | ||
I | DCD1:UART1数据载波检测输入端 | ||
P3[20] |
167 | I/O | P3[20]:GPIO口 |
I/O | D20:外部存储器数据线20 | ||
O | PWM0[5]:脉宽调制器0输出5 | ||
I | DSR1:UART1数据设置就绪端 | ||
P3[21] |
175 | I/O | P3[21]:GPIO口 |
I/O | D21:外部存储器数据线21 | ||
O | PWM0[6]:脉宽调制器0输出6 | ||
O | DTR1:UART1数据终端准备就绪输出端 | ||
P3[22] |
195 | I/O | P3[22]:GPIO口 |
I/O | D22:外部存储器数据线22 | ||
I | PCAP0[0]:脉宽调制器0捕获输入通道0 | ||
I | RI1:UART1响铃指示输入端 | ||
P3[23] |
65 | I/O | P3[23]:GPIO口 |
I/O | D23:外部存储器数据线23 | ||
I | CAP0[0]:Timer0捕获输入通道0 | ||
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
P3[24] |
58 | I/O | P3[24]:GPIO口 |
I/O | D24:外部存储器数据线24 | ||
I | CAP0[1]:Timer0捕获输入通道1 | ||
O | PWM1[1]:脉宽调制器1输出1 | ||
P3[25] |
56 | I/O | P3[25]:GPIO口 |
I/O | D25:外部存储器数据线25 | ||
O | MAT0[0]:Tmer0匹配输出通道0 | ||
O | PWM1[2]:脉宽调制器1输出2 | ||
P3[26] |
55 | I/O | P3[26]:GPIO口 |
I/O | D26:外部存储器数据线26 | ||
O | MAT0[1]:Tmer0匹配输出通道1 | ||
O | PWM1[3]:脉宽调制器1输出3 | ||
P3[27] |
203 | I/O | P3[27]:GPIO口 |
I/O | D27:外部存储器数据线27 | ||
I | CAP1[0]:Timer1捕获输入通道0 | ||
O | PWM1[4]:脉宽调制器1输出4 | ||
P3[28] |
5 | I/O | P3[28]:GPIO口 |
I/O | D28:外部存储器数据线28 | ||
I | CAP1[1]:Timer1捕获输入通道1 | ||
O | PWM1[5]:脉宽调制器1输出5 | ||
P3[29] |
11 | I/O | P3[29]:GPIO口 |
I/O | D29:外部存储器数据线29 | ||
O | MAT1[0]:Tmer1匹配输出通道0 | ||
O | PWM1[6]:脉宽调制器1输出6 | ||
P3[30] |
19 | I/O | P3[30]:GPIO口 |
I/O | D30:外部存储器数据线30 | ||
O | MAT1[1]:Tmer1匹配输出通道1 | ||
O | RTS1:UART1请求发送输出端 | ||
P3[31] |
25 | I/O | P3[31]:GPIO口 |
I/O | D31:外部存储器数据线31 | ||
O | MAT1[2]:Tmer1匹配输出通道2 |
5. P4口: P4口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P4口管脚描述如表4.5所示。
表4.5 LPC2400的P4口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P4[0] |
75 | I/O | P4[0]:GPIO口 |
I/O | A0:外部存储器地址线0 | ||
P4[1] |
79 | I/O | P4[1]:GPIO口 |
I/O | A1:外部存储器地址线1 | ||
P4[2] |
83 | I/O | P4[2]:GPIO口 |
I/O | A2:外部存储器地址线2 | ||
P4[3] |
97 | I/O | P4[3]:GPIO口 |
I/O | A3:外部存储器地址线3 | ||
P4[4] |
103 | I/O | P4[4]:GPIO口 |
I/O | A4:外部存储器地址线4 | ||
P4[5] |
107 | I/O | P4[5]:GPIO口 |
I/O | A5:外部存储器地址线5 | ||
P4[6] |
113 | I/O | P4[6]:GPIO口 |
I/O | A6:外部存储器地址线6 | ||
P4[7] |
121 | I/O | P4[7]:GPIO口 |
I/O | A7:外部存储器地址线7 | ||
P4[8] |
1271 | I/O | P4[8]:GPIO口 |
I/O | A8:外部存储器地址线8 | ||
P4[9] |
131 | I/O | P4[9]:GPIO口 |
I/O | A9:外部存储器地址线9 | ||
P4[10] |
135 | I/O | P4[10]:GPIO口 |
I/O | A10:外部存储器地址线10 | ||
P4[11] |
145 | I/O | P4[11]:GPIO口 |
I/O | A11:外部存储器地址线11 | ||
P4[12] |
149 | I/O | P4[12]:GPIO口 |
I/O | A12:外部存储器地址线12 | ||
P4[13] |
155 | I/O | P4[13]:GPIO口 |
I/O | A13:外部存储器地址线13 | ||
P4[14] |
159 | I/O | P4[14]:GPIO口 |
I/O | A14:外部存储器地址线14 | ||
P4[15] |
173 | I/O | P4[15]:GPIO口 |
I/O | A15:外部存储器地址线15 | ||
P4[16] |
101 | I/O | P4[16]:GPIO口 |
I/O | A16:外部存储器地址线16 | ||
P4[17] |
104 | I/O | P4[17]:GPIO口 |
I/O | A17:外部存储器地址线17 | ||
P4[18] |
105 | I/O | P4[18]:GPIO口 |
I/O | A18:外部存储器地址线18 | ||
P4[19] |
111 | I/O | P4[19]:GPIO口 |
I/O | A19:外部存储器地址线19 | ||
P4[20] |
109 | I/O | P4[20]:GPIO口 |
I/O | A20:外部存储器地址线20 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
I/O | SCK1:SSP1串行时钟 | ||
P4[21] |
115 | I/O | P4[21]:GPIO口 |
I/O | A21:外部存储器地址线21 | ||
I/O | SCL2:I2C2时钟输入/输出 | ||
I/O | SSEL1:SSP1从机选择 | ||
P4[22] |
123 | I/O | P4[22]:GPIO口 |
I/O | A22:外部存储器地址线22 | ||
O | TXD2:UART2发送输出端 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
P4[23] |
129 | I/O | P4[23]:GPIO口 |
I/O | A23:外部存储器地址线23 | ||
I | RXD2:UART2接收输入端 | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
P4[24] |
183 | I/O | P4[24]:GPIO口 |
O | OE:低电平有效输出使能信号 | ||
P4[25] |
179 | I/O | P4[25]:GPIO口 |
O | WE:低电平有效写使能信号 | ||
P4[26] |
119 | I/O | P4[26]:GPIO口 |
O | BLS0:低电平有效字节定位选择信号0 | ||
P4[27] |
139 | I/O | P4[27]:GPIO口 |
O | BLS1:低电平有效字节定位选择信号1 | ||
P4[28] |
170 | I/O | P4[28]:GPIO口 |
O | BLS2:低电平有效字节定位选择信号2 | ||
O | MAT2[0]/LCD[6]/LCD[10]/LCD[2]:Timer2匹配输出通道0/LCD数据 | ||
O | TXD3:UART3发送输出端 | ||
P4[29] |
176 | I/O | P4[29]:GPIO口 |
O | BLS3:低电平有效字节定位选择信号3 | ||
O | MAT2[1]/LCD[7]/LCD[11]/LCD[3]:Timer2匹配输出通道1/LCD数据 | ||
I | RXD3:UART3接收输入端 | ||
P4[30] |
187 | I/O | P4[30]:GPIO口 |
O | CS0:低电平有效片选信号0 | ||
P4[31] |
193 | I/O | P4[31]:GPIO口 |
O | CS1:低电平有效片选信号1 |
6. 电源、复位、晶振及其它管脚的描述如表4.6所示。
表4.6 LPC2400的其它管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
ALARM | 37 | O | RTC实时时钟控制输出。这是一个1.8V引脚,当RTC产生报警信号时此引脚变为高电平。 |
USB_D-2 | 52 | I/O | USB端口2双向D-线 |
DBGEN | 9 | I | JTAG接口控制信号,也用于边界扫描 |
TDO | 2 | O | JTAG接口测试数据输出 |
TDI | 4 | I | JTAG接口测试数据输入 |
TMS | 6 | I | JTAG接口测试模式选择 |
TRST | 8 | I | JTAG接口测试复位,低电平有效 |
TCK | 10 | I | JTAG接口测试时钟 |
RTCK | 206 | I/O | JTAG接口控制信号,当此引脚低电平时使能ETM引脚(P2[9:0]),用于复位后操作跟踪端口 |
RSTOUT | 29 | O | 这是个1.8V引脚,当此引脚低电平时表示LPC2478处于复位状态 |
RESET | 35 | I | 外部复位输入,低电平有效。该引脚具有迟滞作用的TTL电平,能承受5V电压,当此引脚低电平时器件复位,I/O口和外围功能进入默认状态,处理器从地址0开始执行程序 |
XTAL1 | 44 | I | 振荡器电路和内部时钟发生电路输入 |
XTAL2 | 46 | O | 振荡放大器输出 |
RTCX1 | 34 | I | RTC振荡器电路输入 |
RTCX2 | 36 | O | RTC振荡器电路输出 |
VSSIO | 33,63,77,93,114,133,148,169,189,200 | I | 地:数字IO脚的0V电压参考点 |
VSSCORE | 32,84,172 | I | 地:处理器内核的0V电压参考点 |
VSSA | 22 | I | 模拟地:0V电压参考点,与VSS电压相同,为了降低噪声和出错几率,两者应当隔离 |
VDD(3V3) | 15,60,71,89,112,125,146,165,181,198 | I | 3.3V供应电压:I/O口电源供应电压 |
NC | 30,117,141 | I | 未连接引脚 |
VDD(DCDC)(3V3) | 26,86,174 | I | 3.3V直流到直流转换供应电压:为片内DC-to-DC转换器提供电源 |
VDDA | 20 | I | 模拟3.3V供应电压:为DAC和ADC供电,与VDD(3V3)电压相同,为了降低噪声和出错几率,两者应当隔离 |
VREF | 24 | I | ADC参考电压:与VDD(3V3)电压相同,为了降低噪声和出错几率,两者应当隔离,为ADC和DAC提供参考电压 |
VBAT | 38 | I | RTC电源供应:3.3V,为RTC提供电源 |
注:1. 本引脚定义表以LPC2478为准,LPC2470与此相同,LPC2468没有引脚定义的LCD部分。
2. 类型表示引脚信号方向:I/O为输入/输出,I为输入,O为输出。
4.2.2 引脚连接模块
从表4.1~表4.6可以看到,LPC2400系列芯片的绝大部分引脚是复用的,每根引脚都有可能用于不同的外设功能。引脚具体用于什么外设功能是由引脚连接模块进行配置来实现的。当引脚选择了一个功能时,则其它功能无效。
在使用外设时,应当在激活外设以及使能任何相关的中断之前,将外设连接到相应的引脚上。否则,即使使用引脚连接模块激活外设,此激活也是无效的。
引脚连接模块共有21个寄存器,包括11个引脚功能选择寄存器和10个引脚模式寄存器。
1. 引脚功能选择寄存器(PINSEL0~PINSEL10)
引脚功能选择寄存器用于控制每个引脚的功能,每个寄存器32位,每2个bit用于控制1个引脚功能选择。以PINSEL0寄存器为例,寄存器的[1:0]位用于控制P0[0]引脚,[3:2]位用于控制P0[1]引脚,[31:30]位用于控制P0[15]引脚。而PINSEL1寄存器的[1:0]位用于控制P0[16]引脚,[3:2]位用于控制P0[17]引脚,[31:30]位用于控制P0[31]引脚。其余依次类推。
PINSEL0~PINSEL9寄存器,每两个寄存器用于一个端口组:PINSEL0寄存器用于P0口的[15:0]引脚,PINSEL1寄存器用于P0口的[31:30]引脚;PINSEL2寄存器用于P1口的[15:0]引脚,PINSEL3寄存器用于P1口的[31:30]引脚;PINSEL4寄存器用于P2口的[15:0]引脚,PINSEL5寄存器用于P2口的[31:30]引脚;PINSEL6寄存器用于P3口的[15:0]引脚,PINSEL7寄存器用于P3口的[31:30]引脚;PINSEL8寄存器用于P4口的[15:0]引脚,PINSEL9寄存器用于P4口的[31:30]引脚。
每一对比特设置引脚功能的定义如表4.7所示。
表4.7 引脚功能选择寄存器位
PINSEL0~PINSEL9值 | 功能 | 复位值 |
00 | 主功能(缺省),一般为GPIO口 | 00 |
01 | 第一备用功能 | |
10 | 第二备用功能 | |
11 | 第三备用功能 |
每个引脚默认为GPIO口,通过设置PINSEL的值来定义其引脚功能。以P0[0]脚为例,当PINSEL0寄存器的[1:0]位为00时,引脚功能为GPIO口;为01时,引脚功能为CAN1接收器输入;为10时,引脚功能为UART3发送输出端;为11时,引脚功能为I2C1数据输入/输出。
每个引脚的具体定义方法参见表4.1~表4.6。表格中的引脚功能按PINSEL值排列。某些引脚只有两种功能,此时只使用PINSEL值00和01,值10和11保留。
PINSEL10寄存器用于控制ETM接口引脚。该寄存器只使用了位3,其余位均保留。当第3位为0时,关闭ETM接口功能;为1时启用ETM跟踪接口功能,此时无论PINSEL4怎么定义,P2[0]~P2[8]脚均用于ETM跟踪功能。
引脚功能被选择为GPIO时,引脚的方向控制由GPIO方向寄存器IODIR控制。对于其它功能,引脚的方向是由引脚功能控制的。
2. 引脚模式寄存器(PINMODE0~PINMODE9)
引脚模式寄存器PINMODE为所有的GPIO端口控制片内上拉/下拉电阻特性。当使用片内上拉或下接电阻时,若引脚信号不确定,使用上拉时为高电平;而下拉时拉为低电平。
与PNSEL寄存器一样,PINMODE寄存器每2个bit控制1个引脚。每两个寄存器控制一个端口组。
PINMOD寄存器取值如表4.8所示。
表4.8 引脚模式寄存器位
PINMODE0~PINMODE9值 | 功能 | 复位值 |
00 | 使能引脚片内上拉电阻 | 00 |
01 | 保留 | |
10 | 既不使用上拉也不使用下拉 | |
11 | 使能引脚片内下拉电阻 |
引脚连接模块的寄存器总表如表4.9所示。
表4.9 引脚控制模块寄存器列表
寄存器名 | 描述 | 访问 | 复位值 | 地址 |
PINSEL0 | 引脚功能选择寄存器0 | 读/写 | 0x0000 0000 | 0xE002 C000 |
PINSEL1 | 引脚功能选择寄存器1 | 读/写 | 0x0000 0000 | 0xE002 C004 |
PINSEL2 | 引脚功能选择寄存器2 | 读/写 | 0x0000 0000 | 0xE002 C008 |
PINSEL3 | 引脚功能选择寄存器3 | 读/写 | 0x0000 0000 | 0xE002 C00C |
PINSEL4 | 引脚功能选择寄存器4 | 读/写 | 0x0000 0000 | 0xE002 C010 |
PINSEL5 | 引脚功能选择寄存器5 | 读/写 | 0x0000 0000 | 0xE002 C014 |
PINSEL6 | 引脚功能选择寄存器6 | 读/写 | 0x0000 0000 | 0xE002 C018 |
PINSEL7 | 引脚功能选择寄存器7 | 读/写 | 0x0000 0000 | 0xE002 C01C |
PINSEL8 | 引脚功能选择寄存器8 | 读/写 | 0x0000 0000 | 0xE002 C020 |
PINSEL9 | 引脚功能选择寄存器9 | 读/写 | 0x0000 0000 | 0xE002 C024 |
PINSEL10 | 引脚功能选择寄存器10 | 读/写 | 0x0000 0000 | 0xE002 C028 |
PINMODE0 | 引脚模式寄存器0 | 读/写 | 0x0000 0000 | 0xE002 C040 |
PINMODE1 | 引脚模式寄存器1 | 读/写 | 0x0000 0000 | 0xE002 C044 |
PINMODE2 | 引脚模式寄存器2 | 读/写 | 0x0000 0000 | 0xE002 C048 |
PINMODE3 | 引脚模式寄存器3 | 读/写 | 0x0000 0000 | 0xE002 C04C |
PINMODE4 | 引脚模式寄存器4 | 读/写 | 0x0000 0000 | 0xE002 C050 |
PINMODE5 | 引脚模式寄存器5 | 读/写 | 0x0000 0000 | 0xE002 C054 |
PINMODE6 | 引脚模式寄存器6 | 读/写 | 0x0000 0000 | 0xE002 C058 |
PINMODE7 | 引脚模式寄存器7 | 读/写 | 0x0000 0000 | 0xE002 C05C |
PINMODE8 | 引脚模式寄存器8 | 读/写 | 0x0000 0000 | 0xE002 C060 |
PINMODE9 | 引脚模式寄存器9 | 读/写 | 0x0000 0000 | 0xE002 C064 |
4.2.3 引脚连接模块的使用举例
LPC2400系列芯片外设功能在使用前必须先设置其引脚功能。引脚功能是通过对引脚连接模块编程来实现的。
例4.1:使用串口UART0
串口UART0只使用TXD0和RXD0两根引脚来进行数据的串行发送和接收,使用时需将对应的两根引脚P0[2]和P0[3]设置成TXD0和RXD0功能。查表4.1可知,两根引脚的对应PINSEL值均为01,因此写入PINSEL0寄存器的值为0x00000050。
相应程序行为:
PINSEL0 = 0x00000050;
或
PINSEL0 = 0x05<<4;
注意,由于PINSEL是可读写的寄存器,上述写法会使其它引脚的功能回到初始化默认配置。为了不影响其它引脚的功能配置,实用中更好的办法是:先读取寄存器值,然后进行逻辑与和逻辑或操作,再回写到寄存器。
PINSEL0 = (PINSEL0 &0xFFFFFF0F) | (0x05<<4);
其余的引脚外设功能均可以采用类似方法进行操作。
例4.2:启动代码中的相关部分
启动代码负责对芯片复位后的硬件功能进行初始化。芯片复位时,各PINSEL寄存器会自动设置为默认值,所以复位后芯片引脚的功能是确定的。
如果启动以后,硬件系统各外设功能使用情况比较固定,可以将对应的引脚功能设置写入启动代码以加快启动速度。否则,可以在启动时将所有引脚都配置成GPIO端口,具体使用某部分外设时再对相关引脚进行初始化。
……
PINSEL0 = 0x00000000;
PINSEL1 = 0x00000000;
PINSEL2 = 0x00000000;
PINSEL3 = 0x00000000;
PINSEL4 = 0x00000000;
PINSEL5 = 0x00000000;
PINSEL6 = 0x00000000;
PINSEL7 = 0x00000000;
PINSEL8 = 0x00000000;
PINSEL9 = 0x00000000;
PINSEL10 = 0x00000000;
……
4.3 存储器管理
LPC2400系列芯片集成了512KB的片内Flash存储器和64KB的静态SRAM(LPC2470没有片内Flash),其中Flash存储器可以用做代码和数据的固态存储。对Flash存储器的编程可以通过几种方法来实现:通过串口UART0进行的在系统编程(ISP),通过调用嵌入片内的固化代码进行的在应用编程(IAP)以及通过内置的JTAG接口编程。
SRAM支持8位、16位和32位访问。需要注意的是,SRAM控制器包含一个回写缓冲区,它用于防止CPU在连续的写操作时停止运行。回写缓冲区总是保存着软件发送到SRAM的最后1字节。数据只有在软件执行另外一次写操作时被写入SRAM。如果发生芯片复位,实际的SRAM内容将不会反映最近一次的写请求。任何在复位后检查SRAM内容的程序都必须注意这一点。
LPC2400系列芯片具备外部存储器接口,通过外部存储器控制器(EMC)可以扩展两组共8个Bank的存储器组(Static memory bank0 ~ bank3,Dynamic memory bank0 ~ bank3)。对于外扩的RAM存储器,使用ARM的LDR/STR指令即可进行数据的读写操作;而对于外扩的Flash(NOR)型,可以使用LDR指令读取数据,但是不能使用STR指令直接写数据,而是根据Flash芯片写操作时序进行控制,实现Flash的擦除编程。如果需要将程序代码烧写到扩展的Flash,则需要运行一个装载程序(Loader程序,一般由用户自行编写),然后由Loader程序对Flash存储器进行擦除和烧写。
4.3.1 存储器映射
ARM处理器共有4GB的寻址空间,LPC2400系列处理器将这个空间划分成了几个不同的存储器组。图4.3所示为复位后从用户角度所看到的系统存储器地址空间映射。
图4.3 LPC2400系统存储器映射
表4.10 LPC2400存储器使用和细节
地址范围 | 通用用途 | 地址范围细节 | 功能描述 |
0x0000 0000 - 0x3FFF FFFF | 片内非易失存储器和快速I/O | 0x0000 0000 – 0x0007 FFFF | Flash 存储器(512KB) |
0x3FFF C000 – 0x3FFF FFFF | 快速GPIO寄存器 | ||
0x4000 0000 - 0x7FFF FFFF | 片内存储器 | 0x4000 0000 – 0x4000 FFFF | RAM(64KB) |
0x7FE0 0000 – 0x7FE0 3FFF | 以太网RAM(16KB) | ||
0x7FD0 0000 – 0x7FD0 3FFF | USB RAM(16KB) | ||
0x8000 0000 - 0xDFFF FFFF
| 片外存储器 | 四个静态存储器bank,每个16MB | |
0x8000 0000 – 0x80FF FFFF | 静态存储器bank0 | ||
0x8100 0000 – 0x81FF FFFF | 静态存储器bank1 | ||
0x8200 0000 – 0x82FF FFFF | 静态存储器bank2 | ||
0x8300 0000 – 0x83FF FFFF | 静态存储器bank3 | ||
四个动态存储器bank,每个256MB | |||
0xA000 0000 – 0xAFFF FFFF | 动态存储器bank0 | ||
0xB000 0000 – 0x3FFF FFFF | 动态存储器bank1 | ||
0xC000 0000 – 0xCFFF FFFF | 动态存储器bank2 | ||
0xD000 0000 – 0xDFFF FFFF | 动态存储器bank3 | ||
0xE000 0000 - 0xEFFF FFFF | APB 外设 | 36个外设模块,每个16KB | |
0xF000 0000 - 0xFFFF FFFF | AHB 外设 |
|
1. 外设存储器映射
LPC2400系列处理器的外设根据内部总线分为AHB和APB外设两类。AHB外设和APB外设在存储空间里都占2MB的区域,可各自分配最多128个外设。每个外设空间的规格都为16KB。所有外设寄存器不管规格大小,都按照字地址进行分配(32位边界),且不管字还是半字寄存器都是一次性访问。例如,不可能对一个字寄存器的最高字节执行单独的读或写操作。
图4.4表示了AHB和APB外设占用存储空间示意图。
图4.4 外设存储器映射(AHB外设和APB外设)
图4.5所示为AHB外设占用存储空间示意图。其中0-4号外设分别分配给以太网控制器、GPDMA控制器、EMC控制器、USB控制器和LCD控制器,其它外设编号未使用。
图4.5 AHB外设存储器映射
表4.11所示为APB外设占用的存储空间示意图。
表4.11 APB外设存储器映射
APB 外设 | 基地址 | 外设名 |
0 | 0xE000 0000 | 看门狗 |
1 | 0xE000 4000 | 定时器0 |
2 | 0xE000 8000 | 定时器1 |
3 | 0xE000 C000 | UART0 |
4 | 0xE001 0000 | UART1 |
5 | 0xE001 4000 | PWM0 |
6 | 0xE001 8000 | PWM1 |
7 | 0xE001 C000 | I2C0 |
8 | 0xE002 0000 | SPI |
9 | 0xE002 4000 | 实时时钟RTC |
10 | 0xE002 8000 | GPIO |
11 | 0xE002 C000 | 引脚连接模块 |
12 | 0xE003 0000 | SSP1 |
13 | 0xE003 4000 | ADC |
14 | 0xE003 8000 | CAN接收滤波器RAM |
15 | 0xE003 C000 | CAN接收滤波器寄存器 |
16 | 0xE004 0000 | CAN通用寄存器 |
17 | 0xE004 4000 | CAN控制器1 |
18 | 0xE004 8000 | CAN控制器2 |
19 到 22 | 0xE004 C000 - 0xE005 8000 | 未使用 |
23 | 0xE005 C000 | I2C1 |
24 | 0xE006 0000 | 未使用 |
25 | 0xE006 4000 | 未使用 |
26 | 0xE006 8000 | SSP0 |
27 | 0xE006 C000 | DAC |
28 | 0xE007 0000 | 定时器2 |
29 | 0xE007 4000 | 定时器3 |
30 | 0xE007 8000 | UART2 |
31 | 0xE007 C000 | UART3 |
32 | 0xE008 0000 | I2C3 |
33 | 0xE008 4000 | 电池RAM |
34 | 0xE008 8000 | I2S |
35 | 0xE008 C000 | SD/MMC卡接口 |
36 到126 | 0xE009 0000 - 0xE01F BFFF | 未使用 |
127 | 0xE01F C000 | 系统控制模块 |
在对LPC2400系列芯片编程时,要注意不要对一个保留地址或未使用区域的地址进行寻址,否则LPC2400将产生一个数据中止异常。另外,对AHB或APB外设地址执行任何指令取指都会导致产生预取指中止异常。
2. 存储器重映射和boot ROM
存储器映射的一个基本概念是:每个存储器组在存储器映射中都有一个“物理上的”位置。它是一个地址范围,该范围内可写入程序代码,每一个存储器空间的容量都永久固定在同一个位置,这样就不需要将代码设计成在不同地址范围内运行。
因为ARM7处理器上的中断向量所处具体位置(地址0x0000 0000~0x0000 001C,见表4.12)的要求,Boot ROM和SRAM空间的一小部分空间需要重新映射,来实现在不同操作模式下对中断的不同使用。
表4.12 ARM 异常向量位置
地址 | 异常 |
0x0000 0000 | 复位 |
0x0000 0004 | 未定义指令 |
0x0000 0008 | 软件中断 |
0x0000 000C | 预取指中止(指令读取存储器出错) |
0x0000 0010 | 数据中止(数据访问存储器出错) |
0x0000 0014 | 保留 |
0x0000 0018 | IRQ |
0x0000 001C | FIQ |
为了与将来器件兼容,整个Boot ROM都被映射到片内存储器空间的顶端。在这种方式下,使用较大或较小的Flash模块都不需要改变Boot ROM(需要改变Boot装载程序自身的代码)的位置或改变Boot ROM中断向量的映射。除了中断向量之外的存储器空间都保持固定的位置。
存储器重新映射的部分允许在不同模式下处理中断。LPC2400共支持3种存储器映射模式,见表4.13。当处理器工作在用户Flash模式下时,不需要进行中断向量的重新映射,而在其它模式下则需要重新映射。它包括中断向量区(32 字节)和额外的32 字节,一共是64 字节。重新映射的代码位置与地址0x0000 0000~0x0000 003F 重叠,包含在SRAM、Flash 和Boot Block 中的向量必须包含跳转到实际中断处理程序的分支或者其它执行跳转到中断处理程序的转移指令。一个位于Flash 存储器中的典型用户程序可以将整个FIQ 处理程序放置在地址0x0000 001C 而不需要考虑存储器的边界。
表4.13 LPC2400存储器映射模式
模式 | 激活 | 用途 |
Boot装载程序模式 | 由任何复位硬件激活 | 在任何复位后都会执行Boot 装载程序。Boot ROM中断向量映射到存储器的底部以允许处理异常并在Boot 装载过程中使用中断。 |
用户Flash 模式 | 由Boot 代码软件激活 | 当在存储器中识别了一个有效的用户程序标识并且Boot 装载操作未被执行时,由Boot 装载程序激活。中断向量不作重新映射,它位于Flash 存储器的底部。 |
用户RAM 模式 | 由用户程序软件激活 | 由用户程序激活。中断向量重新映射到静态RAM 的底部。 |
选择这种配置有三个原因:
1) 使Flash 存储器中的FIQ处理程序不必考虑因为重新映射所导致的存储器边界问题;
2) 用来处理代码空间中段边界仲裁的SRAM 和Boot ROM向量的使用大大减少;
3) 为超过单字转移指令范围的跳转提供空间来保存常量。
重新映射的存储器组,包括Boot ROM和中断向量,除了重新映射的地址外,仍然继续出现在它们最初的位置。存储器重映射以后的存储器空间见图4.6所示。
图4.6已重新映射和可重新映射区域的低存储器空间
3. 存储器映射控制寄存器MEMMAP(Memory Mapping Control Register)
存储器映射控制寄存器用于改变从地址0x00000000开始的中断向量的映射。这允许运行在不同存储器空间中的代码对中断进行控制。MEMMAP是一个可读写寄存器,地址为0xE01FC040,功能为选择从Flash Boot Block、用户Flash 或RAM中读取ARM 中断向量。
表4.14 存储器映射控制寄存器MEMMAP
位 | 符号 | 功能描述 | 复位值 |
1:0 | MAP | 00:Boot 装载程序模式。中断向量从Boot Block 重新映射 01:用户Flash 模式。中断向量不重新映射,它位于Flash 中 10:用户RAM 模式。中断向量从静态RAM 重新映射 11:保留,不使用该选项 警告:不正确的设定会导致器件的错误操作 | 00 |
7:2 | 保留 |
| NA |
LPC2400的MAP位的硬件复位值为00。Boot装载程序会将用户看到的复位值更改,该程序总是在复位后立即运行。
存储器映射控制寄存器MEMMAP只从处理ARM异常(中断)必需的3 个数据源(FLASH中断向量、SRAM中断向量和Boot ROM中断向量,每个64 字节)中选择一个使用。
每当产生一个软件中断请求,ARM内核就从0x0000 0008 处取出32 位数据(见表4.12,“ARM异常向量位置”)。这就意味着当MEMMAP[1:0]=10(用户RAM模式)时,从0x0000 0008的读数/取指是对0x4000 0008单元进行操作。当MEMMAP[1:0]=00(Boot装载程序模式)时,从0x0000 0008的读数/取指是对0x7FFF E008 单元的数据进行操作(Boot ROM从片内Flash存储器重新映射)。
4.3.2 存储器加速模块
当LPC2400 在运行Flash存储器内的代码时,器件内部的存储器加速模块(MAM,Memory Accelerator Module)最大限度地提高了ARM处理器的性能,而此只需要使用一个简单的Flash组就可实现了。
1.操作
存储器加速模块(MAM)可以将需要的下一个ARM 指令锁存,以防止CPU 取指暂停。与以前的其它器件使用2个Flash组相比,LPC2400只使用一组Flash 存储器。这个Flash组包含3 个128位的缓冲区:预取指缓冲区、分支跟踪缓冲区和数据缓冲区。
当预取指缓冲区和分支跟踪缓冲区不能满足一次指令取指的需要,并且预取指还没有启动时,ARM在启动128位行的取指时暂停。如果预取指已经启动但还未完成,则ARM暂停的时间会更短一些。预取指在Flash结束前面的访问后立即启动,除非被数据访问中止。
预取指行被Flash 模块锁存,但MAM 不能在预取指缓冲区中捕获该行,直到ARM 内核给出预取指开始的地址。如果内核给出的地址与预取指地址不同,则预取指行丢弃。
每个预取指缓冲区和分支跟踪缓冲区包含4个32位ARM指令或8个16位Thumb指令。在连续执行代码时,通常预取指缓冲区包含当前指令和含有该指令的整个Flash 行。
分支和其它程序流的变化导致前面所讲述的连续指令取指出现中断。分支跟踪缓冲区捕获发生非连续中断的行。如果相同的分支再次出现,则从分支跟踪缓冲区内取出下条指令。当分支超出了预取指和分支跟踪缓冲区内容时,需要中止几个时钟来装载分支跟踪缓冲区。这样,不会再出现指令取指延时,直到一个新的和不同的分支出现。
2.MAM 结构
存储器加速器分成以下几个功能模块:1个Flash地址锁存和1个增量器功能用于预取指地址、1个128位的预取指缓冲区及其相关的地址锁存和比较器、1个128位的分支跟踪缓冲区及其相关的地址锁存和比较器。
图4.7 存储器加速器模块框图
3.MAM 的操作模式
MAM 定义了3种操作模式,用户可以在性能和可预测性之间进行选择:
1)MAM关闭。所有存储器请求都会导致Flash的读操作,无指令预取指。
2)MAM部分使能。如果数据可用,则从保持锁存区执行连续的指令访问。指令预取指使能。非连续的指令访问启动Flash读操作。这意味着所有的转移指令都会导致对存储器的取指。由于缓冲的数据访问时序很难预测并且非常依赖于所处的状况,因此所有数据操作都会导致Flash读操作。
3)MAM 完全使能。任何存储器请求(代码或数据),如果其值已经包含在其中一个保持锁存当中,那么从缓冲区执行该代码或数据的访问。指令预取指使能。Flash读操作用于指令的预取指和当前缓冲区所没有的代码或数据的访问。
在复位后,MAM 默认为禁止状态,软件可以随时将存储器访问加速打开或关闭。这样就可使大多数应用程序以最高速度运行,而某些要求更精确定时的功能可以较慢但更可预测的速度运行。
在启用MAM以后,Flash编程功能不受MAM的控制,而是作为一个独立的功能进行处理。Boot ROM扇区包含可作为应用程序的一部分调用的Flash 编程算法(即IAP 代码)和一个可对Flash 存储器进行串行编程的装载程序(即ISP 代码)。
4.寄存器描述
1)MAM控制寄存器-MAM Control Register(MAMCR - 0xE01FC000)。决定MAM的操作模式。两个配置位选择MAM的3种操作模式。在复位后,MAM功能被禁止。改变MAM操作模式会导致MAM所有的保持锁存内容无效,因此需要执行新的Flash读操作。
表4.15 存储器加速器模块控制寄存器(MAMCR)
MAMCR | 功能 | 描述 | 复位值 |
1:0 | MAM 模式控制 | 00-MAM 功能被禁止 01-MAM 功能部分使能 10-MAM 功能完全使能 11-保留 | 0 |
7:2 | 保留 | 保留 | NA |
2)MAM定时寄存器-MAM Timing Register(MAMTIM - 0xE01FC004)。决定Flash存储器取指所使用的时钟个数(1到7个处理器时钟),使用多少个cclk周期访问Flash存储器。这样可调整MAM时序使其匹配处理器操作频率。Flash访问时间可以从1到7个时钟,单个时钟的Flash访问实际上关闭了MAM,这种情况下可以选择MAM模式对功耗进行优化。
表4.16 存储器加速定时寄存器(MAMTIM)
MAMTIM | 功能 | 描述 | 复位值 |
2:0 |
MAM取指周期 | 000=0,保留 001=1,MAM 取指周期为1 个处理器时钟(cclk) 010=2,MAM 取指周期为2 个处理器时钟(cclk) 011=3,MAM 取指周期为3 个处理器时钟(cclk) 100=4,MAM 取指周期为4 个处理器时钟(cclk) 101=5,MAM 取指周期为5 个处理器时钟(cclk) 110=6,MAM 取指周期为6 个处理器时钟(cclk) 111=7,MAM 取指周期为7 个处理器时钟(cclk) 警告:不正确的设定会导致器件的错误操作! |
0x07 |
7:3 | 保留 | 保留 | NA |
5.MAM 使用注意事项
1)MAM 定时值问题
当改变MAM定时值时,必须先通过向MAMCR写入0来关闭MAM,然后将新值写入MAMTIM,最后,将需要的操作模式的对应值写入MAMCR,再次打开MAM。
对于低于20MHz的系统时钟,MAMTIM设定为1;对于20MHz到40MHz之间的系统时钟,建议将MAMTIM设定为2;而在高于40MHz的系统时钟下,建议使用3。
2)Flash 编程问题
在编程和擦除操作过程中不允许访问Flash存储器。如果在Flash模块忙时存储器请求访问Flash地址,MAM就必须强制CPU等待(这通过声明ARM7TDMI-S局部总线信号CLKEN来实现)。在某些情况下,代码执行的延迟会导致看门狗超时。用户必须注意到这种可能性,并采取措施来确保不会在编程或擦除Flash 存储器时出现非预期的看门狗复位,从而导致系统故障。
6.MAM使用举例
MAM功能在LPC2400的启动代码中实现,可以根据Fcclk的大小来自动设置MAM, (在target.c文件中)。首先要将MAM功能禁止,然后根据Fcclk的大小来设置MAM定时寄存器(通过条件编译实现,Fcclk的定义在target.h文件中),最后再使能MAM。
代码清单4.1
void TargetResetInit(void)
{ …
MAMCR = 0; // 禁止MAM 功能
#if Fcclk < 20000000
MAMTIM = 1; // 系统时钟低于20M,建议设置为1
#else
#if Fcclk < 40000000
MAMTIM = 2; // 系统时钟再20M~40M 之间,建议设置为2
#else
MAMTIM = 3; // 系统时钟高于40M,建议设置为3
#endif
MAMCR = 2; // 使能MAM
…}
4.3.3 外部存储器控制器
LPC2400的外部存储器控制器(EMC)是一个AMBA总线AHB从机模块,它支持多种不同结构的存储器,包括常用的异步静态存储器,如RAM、ROM和Flash等,也支持动态存储器,如单数据率SDRAM等。EMC模块可以同时支持多达8个单独配置的存储器组,其中静态存储器和动态存储器各4个Bank。静态存储器组由片选引脚CS0~CS3选中,每个组存储容量16MB,支持RAM、ROM、Flash和其它一些外部I/O部件,支持8位、16位和32位数据宽度。动态存储器组由片选引脚DYCS0~DYCS3选中,每个组存储容量256MB,支持单数据率SDRAM,支持16位和32位数据宽度,刷新模式可由软件控制。
1.EMC功能框图和引脚信号
图4.8 EMC功能框图
表4.17 外部存储器控制器引脚描述
引脚名称 | 类 型 | 复位值 | 描 述 |
A[23:0] | 输出 | 0x0000 0000 | 外部存储器地址线。SDRAM存储器只使用其中的位[14:0] |
D[31:0] | 输入/输出 | 0x0000 0000 | 外部存储器数据线 |
OE | 输出 | 1 | 静态存储器输出使能信号,低电平有效 |
BLS[3:0] | 输出 | 0x0F | 静态存储器字节定位选择信号,低电平有效 |
WE | 输出 | 1 | 写使能信号,低电平有效 |
CS[3:0] | 输出 | 0x0F | 静态存储器片选信号,低电平有效 |
DYCS[3:0] | 输出 | 0x0F | 动态存储器片选信号,低电平有效 |
CAS | 输出 | 1 | 动态存储器列地址选通信号,低电平有效 |
RAS | 输出 | 1 | 动态存储器行地址选通信号,低电平有效 |
CLKOUT[1:0] | 输出 | 来自CCLK | SDRAM时钟 |
CKEOUT[3:0] | 输出 | 0x0F | SDRAM时钟使能 |
DQMOUT[3:0] | 输出 | 0x0F | SDRAM输出掩码。也可用于静态存储器 |
2.外部存储器接口
外部存储器接口取决于存储器组的数据宽度(32位、16位或8位,由EMCStaticConfig寄存器的MW位选择)。如果一个存储器组被配置为32位宽度,地址线A0和A1可以用做非地址线;如果存储器组配置为16为宽度,则不需要A0;8位宽的存储器组则需要使用A0。通过引脚连接模块寄存器可以实现A0和A1的地址或非地址功能。
图4.9和图4.10所示为典型的32位总线宽度的存储器组的外部存储器接口硬件连接方式,其中符号“a_b”表示数据总线的最高位地址线,符号“a_m”表示使用外部存储器接口的存储器芯片的最高位地址线。
图4.9中,一个32位宽度的存储器组由4个8位存储器芯片扩展而成,因此这4个芯片用同一个片选信号CS选中有效,同时使用同一个写使能信号OE。第一个存储器芯片构成存储器组32位中的高8位,因此其写使能信号WE连接到字节选择信号的最高位BLS[3],8位数据线连接到数据总线的最高8位D[31:24]。其余三个芯片分别构成32位存储器组中的第2、3、4个字节。由于存储器组的数据宽度为32位,地址总线中的A0和A1不使用,只使用最高位a_b到bit2连接到存储芯片的地址线上。
图4.10中的16位芯片引脚UB和LB分别表示芯片中16位数据的高字节和低字节。32位芯片中的引脚B3~B0表示芯片中32位数据的四个字节。其芯片引脚与总线的连接方式与图4.9是类似的。
按照图4.9和图4.10的方法,读者不难得出16位和8位总线宽度的存储器组的外部存储器接口扩展方法。
图4.9 4个8位静态存储器芯片构成32位宽存储器组
图4.10 2个16位存储器芯片和1个32位存储器芯片构成32位宽存储器组
图4.11所示为使用SDRAM芯片构成32位动态存储器组的外部存储器接口连接方法。图中使用的SDRAM芯片为K4S561632H,这款芯片的存储容量为256Mb,32位宽度,存储空间分为4个组,使用时由组选择信号BA0和BA1选择。在连线时,除了要注意使用与静态存储器不同的动态存储器引脚如DYCS、CLKOUT、CKEOUT和DQMOUT之外,要格外注意地址线的连法。LPC2400的SDRAM地址线只使用地址线中的[14:0]这15根线,最高位两根A14和A13固定连接到芯片的组选择信号BA1和BA0,其余地址线从A0开始一一连接。这种连接方式是LPC2400的EMC特有的,与其它的ARM芯片,如三星的S3C系列芯片有很大区别,开发者在自己扩展SDRAM存储器时一定要特别注意。
图4.11 外部扩展存储器接口SDRAM
3.EMC相关寄存器
表4.18所示为EMC的相关寄存器。
表4.18 EMC寄存器汇总
寄存器名 | 类型 | 复位值 | 功能描述 | 地址 |
EMCControl | 读/写 | 0x3 | EMC控制操作 | 0xFFE0 8000 |
EMCStatus | 只读 | 0x5 | 提供EMC控制信息 | 0xFFE0 8004 |
EMCConfig | 读/写 | 0x0 | EMC配置操作 | 0xFFE0 8008 |
EMCDynamic Control | 读/写 | 0x6 | 控制动态存储器操作 | 0xFFE0 8020 |
EMCDynamic Refresh | 读/写 | 0x0 | 配置动态存储器刷新操作 | 0xFFE0 8024 |
EMCDynamicReadConfig | 读/写 | 0x0 | 配置动态存储器读策略 | 0xFFE0 8028 |
EMCDynamicRP | 读/写 | 0x0F | 选择预充电命令周期 | 0xFFE0 8030 |
EMCDynamicRAS | 读/写 | 0x0F | 选择激活预充电命令周期 | 0xFFE0 8034 |
EMCDynamicSREX | 读/写 | 0x0F | 选择刷新退出时间 | 0xFFE0 8038 |
EMCDynamicAPR | 读/写 | 0x0F | 选择最后数据输出激活命令时间 | 0xFFE0 803C |
EMCDynamicDAL | 读/写 | 0x0F | 选择数据输入激活命令时间 | 0xFFE0 8040 |
EMCDynamicWR | 读/写 | 0x0F | 选择写修复时间 | 0xFFE0 8044 |
EMCDynamicRC | 读/写 | 0x1F | 选择激活有效命令周期 | 0xFFE0 8048 |
EMCDynamicRFC | 读/写 | 0x1F | 选择自动刷新周期 | 0xFFE0 804C |
EMCDynamicXSR | 读/写 | 0x1F | 选择退出刷新有效命令时间 | 0xFFE0 8050 |
EMCDynamicRRD | 读/写 | 0x0F | 选择激活组A到组B延迟 | 0xFFE0 8054 |
EMCDynamicMRD | 读/写 | 0x0F | 选择有效命令时间的加载模式寄存器 | 0xFFE0 8058 |
EMCDynamicConfig0 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8100 |
EMCDynamicRasCas0 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8104 |
EMCDynamicConfig1 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8120 |
EMCDynamicRasCas1 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8124 |
EMCDynamicConfig2 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8140 |
EMCDynamicRasCas2 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8144 |
EMCDynamicConfig3 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8160 |
EMCDynamicRasCas3 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8164 |
EMCStaticConfig0 | 读/写 | 0x0 | 选择静态存储器芯片0配置 | 0xFFE0 8200 |
EMCStaticWaitWen0 | 读/写 | 0x0 | 选择写使能芯片0延迟 | 0xFFE0 8204 |
EMCStaticWaitOen0 | 读/写 | 0x0 | 选择输出使能芯片0延迟 | 0xFFE0 8208 |
EMCStaticWaitRd0 | 读/写 | 0x0 | 选择读访问芯片0延迟 | 0xFFE0 820C |
EMCStaticWaitPage0 | 读/写 | 0x1F | 选择芯片0异步页模式顺序访问延迟 | 0xFFE0 8210 |
EMCStaticWaitWr0 | 读/写 | 0x1F | 选择写访问芯片0延迟 | 0xFFE0 8214 |
EMCStaticWaitTurn0 | 读/写 | 0x0F | 选择芯片0总线翻转周期 | 0xFFE0 8218 |
EMCStaticConfig1 | 读/写 | 0x0 | 选择静态存储器芯片1配置 | 0xFFE0 8220 |
EMCStaticWaitWen1 | 读/写 | 0x0 | 选择写使能芯片1延迟 | 0xFFE0 8224 |
EMCStaticWaitOen1 | 读/写 | 0x0 | 选择输出使能芯片1延迟 | 0xFFE0 8228 |
EMCStaticWaitRd1 | 读/写 | 0x0 | 选择读访问芯片1延迟 | 0xFFE0 822C |
EMCStaticWaitPage1 | 读/写 | 0x1F | 选择芯片1异步页模式顺序访问延迟 | 0xFFE0 8230 |
EMCStaticWaitWr1 | 读/写 | 0x1F | 选择写访问芯片1延迟 | 0xFFE0 8234 |
EMCStaticWaitTurn1 | 读/写 | 0x0F | 选择芯片1总线翻转周期 | 0xFFE0 8238 |
EMCStaticConfig2 | 读/写 | 0x0 | 选择静态存储器芯片2配置 | 0xFFE0 8240 |
EMCStaticWaitWen2 | 读/写 | 0x0 | 选择写使能芯片2延迟 | 0xFFE0 8244 |
EMCStaticWaitOen2 | 读/写 | 0x0 | 选择输出使能芯片2延迟 | 0xFFE0 8248 |
EMCStaticWaitRd2 | 读/写 | 0x0 | 选择读访问芯片2延迟 | 0xFFE0 824C |
EMCStaticWaitPage2 | 读/写 | 0x1F | 选择芯片2异步页模式顺序访问延迟 | 0xFFE0 8250 |
EMCStaticWaitWr2 | 读/写 | 0x1F | 选择写访问芯片2延迟 | 0xFFE0 8254 |
EMCStaticWaitTurn2 | 读/写 | 0x0F | 选择芯片2总线翻转周期 | 0xFFE0 8258 |
EMCStaticConfig3 | 读/写 | 0x0 | 选择静态存储器芯片3配置 | 0xFFE0 8260 |
EMCStaticWaitWen3 | 读/写 | 0x0 | 选择写使能芯片3延迟 | 0xFFE0 8264 |
EMCStaticWaitOen3 | 读/写 | 0x0 | 选择输出使能芯片3延迟 | 0xFFE0 8268 |
EMCStaticWaitRd3 | 读/写 | 0x0 | 选择读访问芯片3延迟 | 0xFFE0 826C |
EMCStaticWaitPage3 | 读/写 | 0x1F | 选择芯片3异步页模式顺序访问延迟 | 0xFFE0 8270 |
EMCStaticWaitWr3 | 读/写 | 0x1F | 选择写访问芯片3延迟 | 0xFFE0 8274 |
EMCStaticWaitTurn3 | 读/写 | 0x0F | 选择芯片3总线翻转周期 | 0xFFE0 8278 |
EMCStaticExtendedWait | 读/写 | 0x0 | 静态存储器读写传输时间 | 0xFFE0 8880 |
各寄存器的详细配置方法,由于篇幅所限,不再赘述。在对系统进行外扩存储器开发时,需要仔细阅读外扩存储器芯片的相关文档,获得其准确的配置参数,再在初始化函数中对相关寄存器进行配置操作,才能正常使用外扩存储器。
4.4 系统控制模块
系统控制模块包括几个系统特性和控制寄存器,这些寄存器具有许多与特定外设器件无关的功能,每种类型的功能都有其自身的寄存器。
4.4.1 系统控制和状态寄存器
表4.19所示位系统控制和状态寄存器(System Controls and Status register)。
表4.19 系统控制和状态寄存器(SCS – 0xE01F C1A0)
位 | 标识 | 值 | 功能描述 | 类型 | 复位值 |
0 | GPIOM |
| GPIO访问模式选择 | 读/写 | 0 |
0 | GPIO组0和组1配置为与以前的LPC2000系列兼容的端口 | ||||
1 | GPIO组0和组1配置为高速端口,使用片内存储器 | ||||
位 | 标识 | 值 | 功能描述 | 类型 | 复位值 |
1 | EMC Reset Disable |
| 外部存储器控制器复位无效 | 读/写 | 0 |
0 | 复位时,EMC的所有寄存器和功能重新初始化 | ||||
1 | 只有上电和掉电时EMC重新初始化 | ||||
2 |
|
| 保留 |
|
|
3 | MCIPWR Active Level |
| MCIPWR有效电平 | 读/写 | 0 |
0 | MCIPWR引脚低电平 | ||||
1 | MCIPWR引脚高电平 | ||||
4 | OSCRANGE |
| 主晶振范围选择 | 读/写 | 0 |
0 | 主晶振频率范围从1MHz到20MHz | ||||
1 | 主晶振频率范围从15MHz到24MHz | ||||
5 | OSCEN |
| 主晶振使能 | 读/写 | 0 |
0 | 主晶振无效 | ||||
1 | 主晶振有效 | ||||
6 | OSCSTAT |
| 主晶振状态 | 只读 | 0 |
0 | 主晶振未准备好 | ||||
1 | 主晶振已准备好。可以通过OSCEN位设置作为一个时钟源使用 | ||||
31:7 |
|
| 保留 |
| NA |
SCS寄存器主要用于设置晶振使用方法和GPIO属性,在后续相关章节里会几次使用到该寄存器。
4.4.2 外部中断
1. 逻辑结构
LPC2400含有4个外部中断输入(作为可选的管脚功能),四个引脚分别为EINT0、EINT1、EINT2和EINT3。外部中断输入可用于将处理器从掉电模式唤醒。
可将多个管脚同时连接同一路外部中断,此时,外部中断逻辑根据方式位和极性位的不同,分别进行如下处理:
1) 低有效电平激活方式,选用EINT功能的全部管脚的状态都连接到一个正逻辑与门。
2) 高有效电平激活方式,选用EINT功能的全部管脚的状态都连接到一个正逻辑或门。
3) 边沿激活方式,使用GPIO 端口号最低的管脚,与管脚的极性无关。(边沿激活方式中选择使用多个EINT管脚被看作编程出错。)
外部中断逻辑逻辑原理图见图4.12。
图4.12 外部中断逻辑原理图
当多个EINT 管脚逻辑或时,可在中断服务程序中通过IO0PIN和IO1PIN寄存器从GPIO端口读出管脚状态来判断产生中断的管脚。
2. 寄存器描述
外部中断具有4 个相关的寄存器,如表4.20所示。EXTINT寄存器包含中断标志,INTWAKE寄存器包含使能唤醒位,可使能独立的外部中断输入将处理器从掉电模式唤醒,EXTMODE和EXTPOLAR寄存器用来指定管脚使用电平或边沿激活方式。
表4.20 外部中断寄存器
地址 | 寄存器名 | 功能描述 | 类型 |
0xE01FC140 | EXTINT | 外部中断标志寄存器,包含ENIT0、EINT1 、EINT2和EINT3的中断标志 | 读/写 |
0xE01FC144 | INTWAKE | 中断唤醒寄存器,指示哪些中断可以唤醒掉电的CPU,以及控制是否使能唤醒 | 读/写 |
0xE01FC148 | EXTMODE | 外部中断方式寄存器,控制每个管脚的边沿或电平激活 | 读/写 |
0xE01FC14C | EXTPOLAR | 外部中断极性寄存器,控制由每个管脚的哪种电平或边沿来产生中断 | 读/写 |
1) 外部中断标志寄存器-External Interrupt Flag Register(EXTINT - 0xE01FC140)
当一个管脚选择使用外部中断功能时,对应在EXTPOLAR 和EXTMODE 寄存器中的位选择的电平或边沿将置位EXTINT寄存器中的中断标志,向VIC 提出中断请求,如果管脚中断使能,将会产生中断。
向EXTINT 寄存器的位EINT0~位EINT3写入1可清除相应的外部中断标志。在电平激活方式下,只有在该管脚处于无效状态时才能清除相应的中断标志。
一旦EINT0~EINT3中的一位被置位并开始执行相应的代码(处理唤醒和/或外部中断),必须将该位清零,否则以后该EINT 管脚所触发的事件将不能再被识别。
例如,如果外部中断0 管脚的低电平将系统从掉电模式唤醒,为了将来还能进入掉电模式,唤醒后的程序必须将EINT0位复位。如果EINT0位仍保持置位状态,后来的唤醒掉电模式的任何操作都将失败,外部中断也不例外。
表4.21 外部中断标志寄存器
EXTINT | 功能 | 功能描述 | 复位值 |
0 | EINT0 | 电平激活方式下,如果管脚的EINT0 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT0 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
1 | EINT1 | 电平激活方式下,如果管脚的EINT1 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT1 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
2 | EINT2 | 电平激活方式下,如果管脚的EINT2 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT2 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
3 | EINT3 | 电平激活方式下,如果管脚的EINT3 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT3 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
7:4 |
| 保留 | NA |
2) 中断唤醒寄存器-Interrupt Wakeup Register(INTWAKE - 0xE01FC144)
INTWAKE寄存器(有时亦称为EXTWAKE,外部中断唤醒寄存器)中的使能位允许外部中断、以太网、USB、CAN、GPIO、BOD或者RTC中断将处理器从掉电模式唤醒。相关的EINTn功能必须映射到管脚才能实现掉电唤醒,但中断并不必要为了实现唤醒操作而在向量中断控制器中被使能。这样做的好处是允许外部中断输入将处理器从掉电模式唤醒,但不产生中断(只是简单地恢复操作),或者在掉电模式下使能中断而不将处理器唤醒(这样,当应用中并不需要唤醒特性时,也不必关闭中断)。
表4.22 外部中断唤醒寄存器
INTWAKE | 功能 | 功能描述 | 复位值 |
0 | EXTWAKE0 | 该位为1 时,使能EINT0将处理器从掉电模式唤醒 | 0 |
1 | EXTWAKE1 | 该位为1 时,使能EINT1将处理器从掉电模式唤醒 | 0 |
2 | EXTWAKE2 | 该位为1 时,使能EINT2将处理器从掉电模式唤醒 | 0 |
3 | EXTWAKE3 | 该位为3 时,使能EINT3将处理器从掉电模式唤醒 | 0 |
4 | ETHWAKE | 该位为1 时,使能以太网中断将处理器从掉电模式唤醒 | 0 |
5 | USBWAKE | 该位为1 时,使能USB中断将处理器从掉电模式唤醒 | 0 |
6 | CANWAKE | 该位为1 时,使能CAN总线中断将处理器从掉电模式唤醒 | 0 |
7 | GPIOWAKE | 该位为1 时,使能特殊GPIO引脚中断将处理器从掉电模式唤醒 | 0 |
13:8 |
| 保留 | NA |
14 | BODWAKE | 该位为1 时,使能BOD中断将处理器从掉电模式唤醒 | 0 |
15 | RTCWAKE | 该位为1 时,使能实时时钟RTC中断将处理器从掉电模式唤醒 | 0 |
要使器件进入掉电模式并允许总线或管脚上的一个或多个事件能使其恢复正常操作,软件应该对管脚的外部中断功能重新编程,选择中断合适的方式和极性以及掉电模式。唤醒时软件应恢复管脚复用的外围功能。
上述的所有总线或管脚都是低电平有效。如果软件要使器件退出掉电模式来响应多个管脚共用的同一个EINTi 通道的事件,中断通道必须编程设定为低电平激活方式,因为只有在电平方式中通道才能使信号逻辑或来唤醒器件。
3)外部中断方式寄存器-External Interrupt Mode Register(EXTMODE – 0xE01F C148)
EXTMODE寄存器中的位用来选择每个EINT脚是电平触发还是边沿触发。只有选择用作EINT功能(通过管脚连接模块)并已通过VICIntEnable(向量中断使能寄存器)使能的管脚才能产生外部中断(当然,如果管脚选择用作其它功能,则可能产生其它功能的中断)。
当某个中断在VICIntEnable 中被禁止时,软件应该只改变EXTMODE 寄存器中相应位的值。中断重新使能前,软件向EXTINT 写入1 来清除EXTINT 位,EXTINT 位可通过改变激活方式来置位。
表4.23 外部中断方式寄存器
EXTMODE | 功能 | 值 | 描述 | 复位值 |
0 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
1 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
2 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
3 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
7:4 |
|
| 保留 | NA |
4) 外部中断极性寄存器-External Interrupt Polarity Register(EXTPOLAR – 0xE01F C14C)
在电平激活方式中,EXTPOLAR寄存器用来选择相应管脚是高电平还是低电平有效;
在边沿激活方式中,EXTPOLAR寄存器用来选择管脚上升沿还是下降沿有效。只有选择用作EINT功能(通过管脚连接模块)并已通过VICIntEnable(向量中断使能寄存器)使能的管脚才能产生外部中断(当然,如果管脚选择用作其它功能,则可能产生其它功能的中断)。
当某个中断在VICIntEnable 中被禁止时,软件应该只改变EXTPOLAR 寄存器中相应位的值。中断重新使能前,软件向EXTINT 写入1 来清除EXTINT 位,EXTINT 位可通过改变中断极性来置位。
表4.24 外部中断极性寄存器
EXTPOLAR | 功能 | 值 | 描述 | 复位值 |
0 | EXTPOLAR0 | 0 | EINT0低电平或下降沿有效(由EXTMODE0决定) | 0 |
1 | EINT0 高电平或上升沿有效(由EXTMODE0决定) | |||
1 | EXTPOLAR1 | 0 | EINT1低电平或下降沿有效(由EXTMODE1决定) | 0 |
1 | EINT1高电平或上升沿有效(由EXTMODE1决定) | |||
2 | EXTPOLAR2 | 0 | EINT2低电平或下降沿有效(由EXTMODE2决定) | 0 |
1 | EINT2高电平或上升沿有效(由EXTMODE2决定) | |||
3 | EXTPOLAR3 | 0 | EINT3低电平或下降沿有效(由EXTMODE3决定) | 0 |
1 | EINT3高电平或上升沿有效(由EXTMODE3决定) | |||
7:4 |
|
| 保留 | NA |
有关中断向量、中断设置与中断服务子程序的内容详见4.6节。
4.5 时钟和功率控制
4.5.1 晶体振荡器
LPC2400含有3个独立的晶体振荡器:主晶振、内部RC晶振和RTC晶振。每个晶振针对不同应用需求有多种使用方法。复位后,LPC2400系列处理器使用内部RC晶振提供时钟进行操作,直到使用软件进行切换为止。这使得系统可以不依赖于外部时钟进行操作,而且使引导加载程序可以在一个确定的频率下进行操作。当Boot ROM转向用户程序之前,可以激活主晶振从而进入用户代码。
1.内部晶体振荡器(IRC,Internal RC Oscillator)
IRC可以用做看门狗定时器的时钟源,也可以作为时钟,驱动PLL锁相环提供给CPU。IRC的精度不够,因此不能用于USB接口。通常的IRC频率是4MHz。在开机或芯片复位时,LPC2400使用IRC作为时钟源,之后可以使用软件转为使用其它时钟源。
2.主晶振(Main Oscillator)
主晶振可用于为CPU提供时钟,其频率范围为1MHz~24MHz。这个频率可以通过PLL倍频为更高的频率成为CPU的主频。通常把主晶振输出的时钟称为OSCCLK,PLL输入引脚上的时钟称为PLLCLKIN,ARM处理器内核时钟频率称为CCLK。当使用主晶振提供时钟而不激活PLL时,这三个值是相等的。
由于芯片复位时使用IRC晶振,主晶振由软件启动(使用SCS寄存器中的OSCEN位),并且在某些应用中始终不会用到。通过SCS寄存器中的OSCSTAT状态位可以使软件判断主晶振是否运行和稳定,也可以通过SCS寄存器中的OSCRANGE位设置其频率范围。
LPC2400的振荡器可工作在两种模式下:从属模式和振荡模式。从属模式下,输入时钟信号XTAL1与一个100pF相连,其幅值不少于200mV,XTAL2管脚不连接。振荡模式下,由于片内集成了反馈电阻,只需在外部连接一个晶体和电容Cx1、Cx2 就可形成基本模式的振荡。
两种振荡器模式的示意见图4.13。
图4.13 振荡器模式
3.RTC晶振(RTC Oscillator)
RTC晶振的频率为32.768KHz,一般用于给RTC实时时钟提供时钟源。RTC晶振也可以用于看门狗定时器,通过驱动PLL也可以用于提供CPU主频。
4.时钟源选择
几个时钟源都可以用来驱动PLL从而给CPU和片内外设提供时钟。当PLL未连接时,系统可以通过CLKSRCSEL寄存器安全的改变时钟源。
表4.25 时钟源选择寄存器(CLKSRCSEL – 0xE01F C10C)
CLKSRCSEL | 功能 | 值 | 描述 | 复位值 |
1:0 | CLKSRC | 00 | 选择IRC晶振为PLL时钟源 | 00 |
01 | 选择主晶振为PLL时钟源 | |||
10 | 选择RTC晶振为PLL时钟源 | |||
11 | 保留 | |||
7:4 |
| 0 | 未使用,始终为0 | 0 |
4.5.2 PLL锁相环
PLL接受输入的时钟频率范围为32kHz~50MHz。输入频率通过一个预分频器分频成为PLL内部频率,预分频器的值用变量“N”表示。然后再通过一个电流控制振荡器(CCO)倍增到范围275MHz~550MHz,倍频器的值用变量“M”表示。CCO频率再通过CPU频率设置寄存器分频成为提供给CPU的CCLK时钟。
PLL 的激活由PLLCON寄存器控制,PLL倍频器和分频器的值由PLLCFG寄存器控制。为了防止PLL参数发生意外改变或PLL失效,对这两个寄存器进行了保护。当PLL提供芯片时钟时,由于芯片的所有操作,包括看门狗定时器在内都依赖于它,因此PLL设置的意外改变将导致CPU 执行不期望的动作。
1.PLL控制寄存器-PLL Control Register(PLLCON – 0xE01FC080)
PLLCON寄存器可用于使能和连接PLL,它是最新的PLL控制位的保持寄存器,写入该寄存器的值在有效的PLL 馈送序列执行之前不起作用。使能PLL将使PLL锁定到当前倍频器和分频器值的设定频率上。连接PLL将使处理器和所有片内功能都根据PLL输出时钟来运行。对PLLCON的更改只有在对PLLFEED寄存器执行了正确的PLL馈送序列后才生效。
表4.26 PLL 控制寄存器
PLLCON | 功能 | 描述 | 复位值 |
0 | PLLE | PLL使能。当该位为1 并且在有效的PLL馈送之后,该位将激活PLL并允许其锁定到指定的频率。 | 0 |
1 | PLLC | PLL连接。当PLLC和PLLE都为1 并且在有效的PLL馈送后,将PLL作为时钟源连接到LPC2400。否则,LPC2400直接使用振荡器时钟。 | 0 |
7:2 | 保留 | 保留 | NA |
PLL在作为时钟源之前必须进行设置、使能并锁定。将振荡器时钟切换到PLL输出或反过来操作时,内部电路对操作进行同步以确保不会产生干扰。硬件不能确保PLL在连接之前锁定或在PLL在失去锁定时自动断开连接。在PLL失去锁定的情况下,振荡器很可能已经变得不稳定,这样即使断开PLL也挽救不了这种状况。
2.PLL配置寄存器-PLL Configuration Register(PLLCFG – 0xE01FC084)
PLLCFG寄存器是最新的PLL配置值的保持寄存器,包含PLL倍频器和分频器值。在执行正确的PLL馈送序列之前改变PLLCFG寄存器的值不会生效。
表4.27 PLL配置寄存器
PLLCFG | 功能 | 描述 | 复位值 |
14:0 | MSEL | PLL倍频器值,在PLL频率计算中其值为M-1 | 0 |
15 | 保留 | 保留 | NA |
23:16 | NSEL | PLL预分配器值,在PLL频率计算中其值为N | NA |
31:24 | 保留 | 保留 | NA |
3.PLL状态寄存器-PLL Status Register(PLLSTAT – 0xE01FC088)
PLLSTAT为只读寄存器,它是PLL控制和配置信息的读回寄存器,反映了正在使用的真实PLL参数和状态。PLLSTAT可能与PLLCON和PLLCFG中的值不同,这是因为没有执行正确的PLL馈送序列,这两个寄存器中的值并未生效。
表4.28 PLL状态寄存器
PLLSTAT | 功能 | 描述 | 复位值 |
14:0 | MSEL | 读出的PLL倍频器值,这是PLL当前使用的值 | 0 |
15 | 保留 | 保留 | NA |
23:16 | NSEL | 读出的PLL预分频器值,这是PLL当前使用的值 | NA |
24 | PLLE | 读出的PLL使能位,该位为1时,PLL处于激活状态;为0时,PLL关闭;进入掉电模式时,该位自动清零 | 0 |
25 | PLLC | 读出的PLL连接位,当PLLC和PLLE都为1 时,PLL作为时钟源连接到LPC2400;当PLLC或PLLE位为0 时,PLL被旁路,LPC2400直接使用振荡器时钟;进入掉电模式时,该位自动清零 | 0 |
26 | PLOCK | 反映PLL的锁定状态,为0时,PLL未锁定;为1时,PLL锁定到指定的频率 |
|
31:27 | 保留 | 保留 | NA |
PLLSTAT 寄存器中的PLOCK位连接到中断控制器。这样可使用软件打开PLL并连接到其它功能,不需要等待PLL锁定。当发生中断时(PLOCK=1),可以连接PLL并禁止中断。只能通过禁止PLL中断的方式返回。
PLL有3种可能的工作模式,由PLLE和PLLC组合得到。
表4.29 PLL的工作模式
PLLC | PLLE | PLL 功能 |
0 | 0 | PLL 被关闭并断开连接,系统使用未更改的时钟输入 |
0 | 1 | PLL 被激活但是尚未连接,PLL 可在PLOCK 置位后连接 |
1 | 0 | 与00 组合相同,这样消除了PLL 已连接但没有使能的可能性 |
1 | 1 | PLL 已使能并连接到处理器作为系统时钟源 |
4.PLL馈送寄存器-PLL Feed Register(PLLFEED – 0xE01FC08C)
必须将正确的馈送序列写入PLLFEED寄存器才能使PLLCON和PLLCFG寄存器的更改生效。馈送序列如下:
1) 将值0xAA 写入PLLFEED;
2) 将值0x55 写入PLLFEED。
这两个写操作的顺序必须正确,而且必须是连续的APB总线周期,这意味着在执行PLL馈送操作时必须禁止中断。不管是写入的值不正确还是没有满足前两个条件,对PLLCON或PLLCFG寄存器的更改都不会生效。
表4.30 PLL 馈送寄存器
PLLFEED | 功能 | 描述 | 复位值 |
7:0 | PLLFEED | PLL馈送序列必须写入该寄存器才能使PLL配置和控制寄存器的更改生效 | 0x00 |
5.PLL和掉电模式
掉电模式会自动关闭并断开PLL。从掉电模式唤醒不会自动恢复PLL的设定,PLL的恢复必须由软件来完成。通常,一个将PLL激活并等待锁定,然后将PLL连接的子程序可以在任何中断服务程序的开始调用。有一点非常重要,那就是不要试图在掉电唤醒之后简单地执行馈送序列来重新启动PLL,这会出现在PLL锁定建立之前同时使能并连接PLL的危险。
6.PLL频率计算举例:
当一个LPC2400 ARM系统需要使用PLL,应当按照以下原则进行:
1) 确定处理器的时钟频率CCLK。这可以根据系统对处理器的整体要求来决定,外围器件的时钟频率可以低于处理器频率。
2) 确定PLL内部的时钟频率FCCO。FCCO的值应当在275MHz~550MHz之间,而且应当是CCLK的整数倍。
3) 确定晶体振荡器频率FIN。FIN的值应当在32kHz~50MHz之间。
PLL的输出频率公式为:FCCO = (2×M×FIN) / N
选择两个整数M和N便可得到合适的FCCO值。M的取值范围为6~512,N的取值范围为1~32。
例如:系统要求使用USB接口,CPU主频约为60MHz。外部晶振频率为4MHz。
由要求可知:USB总线要求精确的48MHz时钟,因此可以选择FCCO为480MHz。当N值为1时,M = 480 / (2×4) = 60。因此PLLCFG值为0x3B(N-1 = 0,M-1 = 0x3B)。
4.5.3 时钟分频
PLL的输出必须向下分频为更低频率的信号才能用于CPU和USB模块。提供给USB模块的分频器是独立的,因为USB的时钟要求必须是准确的48MHz而且有50%的占空比。分频给CPU的信号成为CCLK时钟,并且再分频成为各个片内外设的驱动时钟。
1.CPU时钟配置寄存器-CPU Clock Configuration Register(CCLKCFG – 0xE01F C104)
CCLKCFG寄存器控制PLL的分频输出提供给CPU。如果不使用PLL,分频值为1。
表4.31 CPU时钟配置寄存器
CCLKCFG | 功能 | 描述 | 复位值 |
7:0 | CCLKSEL | 分频器值,用于生成CPU时钟(CCLK)。本值只能为0或奇数值(1,3,5,…,255) | 0x00 |
CCLK值为PLL的输出频率除以CCLKSEL+1。当CCLKSEL值为1时,CCLK值为PLL输出频率的一半。
2.USB时钟配置寄存器-USB Clock Configuration Register(USBCLKCFG – 0xE01F C108)
USBCLKCFG寄存器控制PLL的分频输出提供给USB模块。如果不使用PLL,分频值为1。输出的频率应该为48MHz并且有50%的占空比。
表4.32 USB时钟配置寄存器
USBCLKCFG | 功能 | 描述 | 复位值 |
3:0 | USBSEL | 分频器值,用于生成USB时钟(CCLK) | 0x00 |
7:4 | - | 保留 | NA |
USB模块的时钟值为PLL的输出频率除以USBSEL+1。当USBSEL值为1时,USB时钟值为PLL输出频率的一半。
3.IRC整理寄存器-IRC Trim Register(IRCTRIM – 0xE01F C1A4)
这个寄存器用于整理片内4MHz的晶振。
表4.33 IRC整理寄存器
IRCtrim | 功能 | 描述 | 复位值 |
7:0 | IRCtrim | IRC整理值,用于控制片内4MHzIRC晶振频率 | 0xA0 |
15:8 | - | 保留 | NA |
4.外设时钟选择寄存器0和1-Peripheral Clock Selection registers 0 and 1(PCLKSEL0 – 0xE01F C1A8 and PCLKSEL1 – 0xE01F C1AC)
这一对寄存器中的每两位控制一个外设的时钟,其取值意义参见表4.36。
表4.34 外设时钟选择寄存器0
PCLKSEL0 | 功能 | 描述 | 复位值 |
1:0 | PCLK_WDT | 看门狗外设时钟选择 | 00 |
3:2 | PCLK_TIMER0 | 定时器0外设时钟选择 | 00 |
5:4 | PCLK_TIMER1 | 定时器1外设时钟选择 | 00 |
7:6 | PCLK_UART0 | 串口0外设时钟选择 | 00 |
9:8 | PCLK_UART1 | 串口1外设时钟选择 | 00 |
11:10 | PCLK_PWM0 | 脉宽调制器0外设时钟选择 | 00 |
13:12 | PCLK_PWM1 | 脉宽调制器1外设时钟选择 | 00 |
15:14 | PCLK_I2C0 | I2C0外设时钟选择 | 00 |
17:16 | PCLK_SPI | SPI外设时钟选择 | 00 |
19:18 | PCLK_RTC | 实时时钟外设时钟选择 | 00 |
21:20 | PCLK_SSP1 | SSP1外设时钟选择 | 00 |
23:22 | PCLK_DAC | DAC外设时钟选择 | 00 |
25:24 | PCLK_ADC | ADC外设时钟选择 | 00 |
27:26 | PCLK_CAN1 | CAN1外设时钟选择 | 00 |
29:28 | PCLK_CAN2 | CAN2外设时钟选择 | 00 |
31:30 | PCLK_ACF | CAN滤波器外设时钟选择 | 00 |
注:PCLK_RTC字段中,值“01”是无效的,试图写入“01”不会改变预设值。
表4.35 外设时钟选择寄存器1
PCLKSEL1 | 功能 | 描述 | 复位值 |
1:0 | PCLK_BAT_RAM | 电池支持RAM外设时钟选择 | 00 |
3:2 | PCLK_GPIO | GPIO外设时钟选择 | 00 |
5:4 | PCLK_PCB | 引脚连接模块外设时钟选择 | 00 |
7:6 | PCLK_I2C1 | I2C1外设时钟选择 | 00 |
9:8 | - | 保留,始终为0 | 00 |
11:10 | PCLK_SSP0 | SSP0外设时钟选择 | 00 |
13:12 | PCLK_TIMER2 | 定时器2外设时钟选择 | 00 |
15:14 | PCLK_TIMER3 | 定时器3外设时钟选择 | 00 |
17:16 | PCLK_UART2 | 串口2外设时钟选择 | 00 |
19:18 | PCLK_UART3 | 串口3外设时钟选择 | 00 |
21:20 | PCLK_I2C2 | I2C2外设时钟选择 | 00 |
23:22 | PCLK_I2S | I2S总线外设时钟选择 | 00 |
25:24 | PCLK_MCI | MCI外设时钟选择 | 00 |
27:26 | - | 保留,始终为0 | 00 |
PCLKSEL1 | 功能 | 描述 | 复位值 |
29:28 | PCLK_SYSCON | 系统控制模块外设时钟选择 | 00 |
31:30 | - | 保留,始终为0 | 00 |
表4.36 外设时钟选择寄存器位值
位 | 功能描述 | 复位值 |
00 | PCLK_xxx = CCLK / 4 | 00 |
01 | PCLK_xxx = CCLK | 00 |
10 | PCLK_xxx = CCLK / 2 | 00 |
11 | 在CAN1、CAN2、CAN滤波器部件中PCLK_xxx = HCLK / 6,其余部件中PCLK_xxx = HCLK / 8 | 00 |
4.5.4 功率控制
1. 节电模式
嵌入式系统一般使用电池供电,因此系统的耗电和待机时间是个重要指标。节电的方法主要是降低系统时钟频率:改变时钟源、重配置PLL值或者改变CPU时钟分频值。另外,也可以通过停止片内外设时钟的方法来关闭不使用的片内外设,进一步减少功耗。
LPC2400支持三种节电模式:空闲模式、睡眠模式和掉电模式。LPC2400有一个独立的功率控制单元用来给RTC和一个小的静态RAM供电,这个特性使得LPC2400可以将片内的其它大部分设备全部关闭。
1)空闲模式(Idel mode):指令的执行被挂起直到发生复位或中断为止。外设功能在空闲模式下继续保持并可产生中断使处理器恢复运行。空闲模式使处理器、存储器系统和相关控制器以及内部总线不再消耗功率。任何中断都可以将CPU从空闲模式下唤醒。
2)睡眠模式(Sleep mode):主晶振关闭,所有时钟停止。IRC的输出无效,但IRC并未关闭并且可以快速唤醒。32kHz的RTC晶振也未停止,因为RTC中断可以用来做唤醒的中断源。睡眠模式保持处理器状态和寄存器、外设寄存器和内部SRAM的值。芯片管脚的逻辑电平保持静态。复位或特定的不需要时钟仍能工作的中断可中止睡眠模式并使芯片恢复正常运行。由于睡眠模式使芯片所有的动态操作都挂起,因此芯片的功耗降低到几乎为零。芯片复位和特定的中断可以将CPU从睡眠模式下唤醒。
3)掉电模式(Power-down mode):掉电模式与睡眠模式类似,但不同的是掉电模式会将Flash存储器也关闭。在掉电模式下,主晶振和IRC以及所有内部时钟都停止,只有32kHz的RTC晶振继续工作。
2. 寄存器描述
外设的功率控制特性允许独立关闭应用中不需要的外设,这样可以进一步降低功耗。功率控制功能包含两个寄存器,分别是PCON和PCONP。
1) 功率控制寄存器-Power Control Register(PCON – 0xE01F C0C0)
表4.37 功率控制寄存器
PCON | 功能 | 描述 | 复位值 |
0 | PM0(IDL) | 功耗模式控制位0 | 0 |
1 | PM1(PD) | 功耗模式控制位1 | 0 |
2 | BODPDM | 低电压掉电模式。当该位为0时,进入掉电模式时仍然保持低电压检测功能;当该位为1时,低电压检测功能也被关闭,这样可以进一步减少功耗,但存在着电压过低而无法从掉电模式中唤醒的可能 | 0 |
3 | BOGD | 低电压全局无效。当该位为1时,低电压检测功能无效;该位为0时,低电压检测功能使能 | 0 |
4 | BORD | 低电压复位无效。当该位为1时,低电压检测(2.6V)不会导致芯片复位;当该位为0时,低电压检测(2.9V)使芯片复位 | 0 |
6:3 | - | 保留 | NA |
7 | PM2 | 功耗模式控制位2 | 0 |
利用PCON寄存器设置节电模式的方法详见表4.38。PCON寄存器中的三个比特PM2、PM1和PM0联合控制进入LPC2400节电模式的方式。
表4.38 节电模式控制位
PM2,PM1,PM0 | 功能描述 |
000 | 正常模式 |
001 | 空闲模式 |
101 | 睡眠模式 |
010 | 掉电模式 |
其它 | 保留 |
2)外设功率控制寄存器-Power Control for Peripherals Register(PCONP – 0xE01F C0C4)
PCONP寄存器允许将所选的外设功能关闭以实现节电的目的,通过关断特定外围模块的时钟源来实现。有少数外设功能不能被关闭(例如看门狗定时器、GPIO、引脚连接模块和系统控制模块)。
某些外设,特别是包含模拟功能的外设,它们的操作无需时钟,但会消耗功率。这些外设包含独立的禁能控制位,可以通过它们来关闭电路以降低功耗。
PCONP中的每个位都控制一个外设,每个位所对应的外设编号见LPC2400存储器寻址部分的APB外设映射一节。当位值为1时该外设启用,当位值为0时该外设时钟关闭。外设在外设功率控制寄存器的对应位见表4.11。
表4.39外设功率控制寄存器
PCONP | 功能 | 描述 | 复位值 |
0 | - | 未使用,始终为0 | 0 |
1 | PCTIM0 | 定时器0功率时钟控制位 | 1 |
2 | PCTIM1 | 定时器1功率时钟控制位 | 1 |
3 | PCUART0 | 串口0功率时钟控制位 | 1 |
4 | PCUART1 | 串口1功率时钟控制位 | 1 |
5 | PCPWM0 | 脉宽调制器0功率时钟控制位 | 1 |
6 | PCPWM1 | 脉宽调制器1功率时钟控制位 | 1 |
7 | PCI2C0 | I2C0功率时钟控制位 | 1 |
8 | PCSPI | SPI功率时钟控制位 | 1 |
9 | PCRTC | 实时时钟功率时钟控制位 | 1 |
10 | PCSSP1 | SSP1接口功率时钟控制位 | 1 |
11 | PCEMC | 外扩存储器控制器 | 1 |
12 | PCAD | A/D转换器功率时钟控制位。清零该位前先清零AD0CR 寄存器的PDN位,该位应当在置位PDN前被置位 | 0 |
13 | PCCAN1 | CAN1功率时钟控制位 | 0 |
14 | PCCAN2 | CAN2功率时钟控制位 | 0 |
18:15 | - | 保留 | NA |
19 | PCI2C1 | I2C1功率时钟控制位 | 1 |
20 | PCLCD | 液晶控制器功率时钟控制位 | 0 |
21 | PCSSP0 | SSP0功率时钟控制位 | 1 |
22 | PCTIM2 | 定时器2功率时钟控制位 | 0 |
23 | PCTIM3 | 定时器3功率时钟控制位 | 0 |
24 | PCUART2 | 串口2功率时钟控制位 | 0 |
25 | PCUART3 | 串口3功率时钟控制位 | 0 |
26 | PCI2C2 | I2C2功率时钟控制位 | 1 |
27 | PCI2S | I2S接口功率时钟控制位 | 0 |
28 | PCSDC | SD卡接口功率时钟控制位 | 0 |
29 | PCGPDMA | 通用DMA功能功率时钟控制位 | 0 |
30 | PCENET | 以太网模块功率时钟控制位 | 0 |
31 | PCUSB | USB接口功率时钟控制位 | 0 |
复位以后,PCONP寄存器按照默认值使能选中的接口和外设控制器。用户程序应当在启动代码中对PCONP寄存器编程用来启动所需要的外设功能,并关闭不需要的接口和外设,以达到降低功耗的要求。系统启动以后,除了对外设功能相关的寄存器进行配置外,用户应用程序应当不要再访问PCONP 寄存器从而启动使用片内的任何外围功能。
4.5.5 时钟和功率控制举例
1. 系统时钟设置
在LPC2400的启动代码中,系统时钟的设置是通过一个ConfigurePLL ( )函数来实现的。该函数首先关闭PLL,然后通过CLKSRCSEL寄存器选择主晶振为时钟源,再通过PLLCFG寄存器利用M值和N值设置CCO频率,用CCLKCFG寄存器分频为CPU时钟CCLK,最后使能PLL使设置生效。注意对PLLCON寄存器的每次操作都要用正确的馈送序列来实现。函数中的有关参数是在target.h头文件中定义的,其相关程序行如下:
代码清单4.2
……
#define Fosc 12000000
#define Fcclk 57600000
#define Fcco 288000000
#define Fpclk (Fcclk / 4)
#define PLL_MValue 11
#define PLL_NValue 0
#define CCLKDivValue 4
#define USBCLKDivValue 5
……
主晶振采用12MHz晶体振荡器,其宏定义Fosc值要跟实际的物理参数相同。由于M值为12,N值为1(注意实际参数要在设置值上加1),CCO频率Fcco = 2×M×Fosc / N = 288MHz。CPU时钟CCLK = Fcco / (CCLKDivValue+1) = 57.6MHz。而USB时钟USBCLK = Fcco / (USBCLKDivValue+1) = 48MHz,正好满足使用要求。
代码清单4.3
void ConfigurePLL ( void )
{
if ( PLLSTAT & (1 << 25) ) // PLL是否连接
{ PLLCON = 1; // 使能PLL并断开连接
PLLFEED = 0xaa; // PLL馈送
PLLFEED = 0x55; }
PLLCON = 0; // 关闭PLL并断开连接
PLLFEED = 0xaa;
PLLFEED = 0x55;
SCS |= 0x20; // 使能主晶振
while( !(SCS & 0x40) ); // 读主晶振状态直到主晶振可用
CLKSRCSEL = 0x1; // 选择12MHz主晶振作为PLL时钟源
PLLCFG = PLL_MValue | (PLL_NValue << 16); // 执行配置
PLLFEED = 0xaa;
PLLFEED = 0x55;
PLLCON = 1; // 使能PLL
PLLFEED = 0xaa;
PLLFEED = 0x55;
CCLKCFG = CCLKDivValue; // 设置时钟分频,设定CPU频率CCLK
#if USE_USB
USBCLKCFG = USBCLKDivValue; // usbclk = 288 MHz/6 = 48 MHz
#endif
while ( ((PLLSTAT & (1 << 26)) == 0) ); // 检查锁定位状态
PLLCON = 3; // 使能并连接PLL
PLLFEED = 0xaa;
PLLFEED = 0x55;
while ( ((PLLSTAT & (1 << 25)) == 0) ); // 检查连接状态位
return;
}
2. 外设分频
外设启动和分频应在启动代码中实现。LPC2400的启动代码使用一个TargetResetInit( )函数实现,其中相关代码如下:
代码清单4.4
……
#if USE_USB
PCONP |= 0x80000000; // 如果使用USB则打开USB PCLK
#endif
ConfigurePLL();
#if (Fpclk / (Fcclk / 4)) == 1
PCLKSEL0 = 0x00000000; // PCLK = 1/4 CCLK
PCLKSEL1 = 0x00000000;
#endif
#if (Fpclk / (Fcclk / 4)) == 2
PCLKSEL0 = 0xAAAAAAAA; // PCLK = 1/2 CCLK
PCLKSEL1 = 0xAAAAAAAA;
#endif
#if (Fpclk / (Fcclk / 4)) == 4
PCLKSEL0 = 0x55555555; // PCLK = CCLK
PCLKSEL1 = 0x55555555;
#endif
……
为了降低外设功耗,一般都将APB外设时钟设为CCLK时钟的1/4。
3. 进入Idle模式
代码清单4.5
……
PCON = 0x01; // 进入Idle状态,CPU停止
……
进入Sleep模式和Power Down模式采用不同设置值进行设置即可。
4.6 向量中断控制器
4.6.1 LPC2400中断特性
LPC2400使用了ARM PrimeCellTM技术的向量中断控制器,利用映射到AHB总线的地址空间实现快速访问。支持最大32个向量IRQ中断,拥有16个可编程中断优先级,并且每个可编程优先级对应固定硬件优先级,具有硬件优先级屏蔽逻辑。所有中断都可设置为FIQ中断,还可以产生软件中断。
4.6.2 功能概述
ARM处理器内核有两类中断:中断请求(IRQ)和快速中断请求(FIQ)。管理中断类型识别及优先级判断,并向ARM内核提供中断向量和中断信号的模块称为向量中断控制器(VIC)。VIC支持的32个中断可以编程设置为IRQ或FIQ中断类型。这样用户可以按照处理器外围模块的优先级别灵活设置中断的优先级别。
快速中断请求(FIQ)是优先级最高的中断。如果有一个以上中断被设置为FIQ,则VIC对中断输入进行“或”操作,最终向ARM内核产生一个FIQ信号。为了确保FIQ响应的最短延时,在实际应用中一般只设置一个中断源为FIQ类型。这样FIQ中断服务程序就可以直接处理对应模块。如果有多个中断源设置为FIQ,则在FIQ中断服务程序中要先读出VIC的FIQ中断状态字,从而判断真正发生的中断源才能处理对应的中断。
除了设置为FIQ的中断外,其余中断类型为向量IRQ。向量IRQ中断优先级可以编程设置。如果有一个以上IRQ中断分配相同优先级且同时产生中断请求,则连接到VIC的通道靠前的中断源先被应答服务。VIC通道数值分配见中断源小节表。
另外,VIC对所有向量IRQ进行“或”操作,最终向ARM内核产生一个IRQ信号。IRQ中断服务程序先读取VIC的IRQ中断状态字,确定中断源后执行相应中断服务。
4.6.3 中断控制器结构
向量中断控制器VIC的结构如图4.14所示。
图4.14 VIC的结构框图
4.6.4 寄存器描述
VIC内所有寄存器都为32位宽,具体名称及地址见表4.40。
表4.40 VIC寄存器映射表
名称 | 功能描述 | 访问方式 | 复位值[1] | 地址 |
VICIRQStatus | IRQ中断状态寄存器。该寄存器保存各个IRQ中断请求是否有效。 | 只读 | 0 | 0xFFFFF000 |
VICFIQStatus | FIQ中断状态寄存器。该寄存器保存各个FIQ中断请求是否有效。 | 只读 | 0 | 0xFFFFF004 |
VICRawIntr | 原始中断状态寄存器。该寄存器保存32个中断请求和软件中断是否有效,不管它们是否被使能。 | 只读 | - | 0xFFFFF008 |
VICIntSelect | 中断类型选择寄存器。该寄存器用于把中断请求设置为FIQ或者IRQ。 | 读/写 | 0 | 0xFFFFF00C |
VICIntEnable | 中断使能寄存器。该寄存器使能32个中断请求和软件中断产生IRQ或FIQ中断。 | 读/写 | 0 | 0xFFFFF010 |
VICIntEnClr | 中断使能清除寄存器。该寄存器可清除中断使能寄存器已使能的各中断位。 | 只写 | - | 0xFFFFF014 |
VICSoftInt | 软件中断寄存器。该寄存器内容与32个中断请求作相“或”操作。 | 读/写 | 0 | 0xFFFFF018 |
名称 | 功能描述 | 访问方式 | 复位值[1] | 地址 |
VICSoftIntClear | 软件中断清除寄存器。 | 只写 | - | 0xFFFFF01C |
VICProtection | VIC保护使能寄存器。该寄存器限制软件在特权模式下运行时访问VIC各个寄存器。 | 读/写 | 0 | 0xFFFFF020 |
VICSWPriorityMask | 软件优先级屏蔽寄存器。 | 读/写 | 0xFFFF | 0xFFFFF024 |
VICVectAddr0 | 向量地址0寄存器。该寄存器保存IRQ0的中断服务程序入口地址。IRQ0优先级最高,IRQ32最低。 | 读/写 | 0 | 0xFFFFF100 |
VICVectAddr1 | 向量地址1寄存器。 | 读/写 | 0 | 0xFFFFF104 |
VICVectAddr2 | 向量地址2寄存器。 | 读/写 | 0 | 0xFFFFF108 |
VICVectAddr3 | 向量地址3寄存器。 | 读/写 | 0 | 0xFFFFF10C |
VICVectAddr4 | 向量地址4寄存器。 | 读/写 | 0 | 0xFFFFF110 |
VICVectAddr5 | 向量地址5寄存器。 | 读/写 | 0 | 0xFFFFF114 |
VICVectAddr6 | 向量地址6寄存器。 | 读/写 | 0 | 0xFFFFF118 |
VICVectAddr7 | 向量地址7寄存器。 | 读/写 | 0 | 0xFFFFF11C |
VICVectAddr8 | 向量地址8寄存器。 | 读/写 | 0 | 0xFFFFF120 |
VICVectAddr9 | 向量地址9寄存器。 | 读/写 | 0 | 0xFFFFF124 |
VICVectAddr10 | 向量地址10寄存器。 | 读/写 | 0 | 0xFFFFF128 |
VICVectAddr11 | 向量地址11寄存器。 | 读/写 | 0 | 0xFFFFF12C |
VICVectAddr12 | 向量地址12寄存器。 | 读/写 | 0 | 0xFFFFF130 |
VICVectAddr13 | 向量地址13寄存器。 | 读/写 | 0 | 0xFFFFF134 |
VICVectAddr14 | 向量地址14寄存器。 | 读/写 | 0 | 0xFFFFF138 |
VICVectAddr15 | 向量地址15寄存器。 | 读/写 | 0 | 0xFFFFF13C |
VICVectAddr16 | 向量地址16寄存器。 | 读/写 | 0 | 0xFFFFF140 |
VICVectAddr17 | 向量地址17寄存器。 | 读/写 | 0 | 0xFFFFF144 |
VICVectAddr18 | 向量地址18寄存器。 | 读/写 | 0 | 0xFFFFF148 |
VICVectAddr19 | 向量地址19寄存器。 | 读/写 | 0 | 0xFFFFF14C |
VICVectAddr20 | 向量地址20寄存器。 | 读/写 | 0 | 0xFFFFF150 |
VICVectAddr21 | 向量地址21寄存器。 | 读/写 | 0 | 0xFFFFF154 |
VICVectAddr22 | 向量地址22寄存器。 | 读/写 | 0 | 0xFFFFF158 |
VICVectAddr23 | 向量地址23寄存器。 | 读/写 | 0 | 0xFFFFF15C |
VICVectAddr24 | 向量地址24寄存器。 | 读/写 | 0 | 0xFFFFF160 |
VICVectAddr25 | 向量地址25寄存器。 | 读/写 | 0 | 0xFFFFF164 |
VICVectAddr26 | 向量地址26寄存器。 | 读/写 | 0 | 0xFFFFF168 |
VICVectAddr27 | 向量地址27寄存器。 | 读/写 | 0 | 0xFFFFF16C |
VICVectAddr28 | 向量地址28寄存器。 | 读/写 | 0 | 0xFFFFF170 |
VICVectAddr29 | 向量地址29寄存器。 | 读/写 | 0 | 0xFFFFF174 |
VICVectAddr30 | 向量地址30寄存器。 | 读/写 | 0 | 0xFFFFF178 |
VICVectAddr31 | 向量地址31寄存器。 | 读/写 | 0 | 0xFFFFF17C |
VICVectPriority0 | 向量优先级0寄存器。该寄存器设置IRQ0的优先级。 | 读/写 | 0xF | 0xFFFFF200 |
VICVectPriority1 | 向量优先级1寄存器。 | 读/写 | 0xF | 0xFFFFF204 |
VICVectPriority2 | 向量优先级2寄存器。 | 读/写 | 0xF | 0xFFFFF208 |
VICVectPriority3 | 向量优先级3寄存器。 | 读/写 | 0xF | 0xFFFFF20C |
VICVectPriority4 | 向量优先级4寄存器。 | 读/写 | 0xF | 0xFFFFF210 |
VICVectPriority5 | 向量优先级5寄存器。 | 读/写 | 0xF | 0xFFFFF214 |
VICVectPriority6 | 向量优先级6寄存器。 | 读/写 | 0xF | 0xFFFFF218 |
VICVectPriority7 | 向量优先级7寄存器。 | 读/写 | 0xF | 0xFFFFF21C |
VICVectPriority8 | 向量优先级8寄存器。 | 读/写 | 0xF | 0xFFFFF220 |
VICVectPriority9 | 向量优先级9寄存器。 | 读/写 | 0xF | 0xFFFFF224 |
VICVectPriority10 | 向量优先级10寄存器。 | 读/写 | 0xF | 0xFFFFF228 |
VICVectPriority11 | 向量优先级11寄存器。 | 读/写 | 0xF | 0xFFFFF22C |
VICVectPriority12 | 向量优先级12寄存器。 | 读/写 | 0xF | 0xFFFFF230 |
VICVectPriority13 | 向量优先级13寄存器。 | 读/写 | 0xF | 0xFFFFF234 |
VICVectPriority14 | 向量优先级14寄存器。 | 读/写 | 0xF | 0xFFFFF238 |
VICVectPriority15 | 向量优先级15寄存器。 | 读/写 | 0xF | 0xFFFFF23C |
VICVectPriority16 | 向量优先级16寄存器。 | 读/写 | 0xF | 0xFFFFF240 |
VICVectPriority17 | 向量优先级17寄存器。 | 读/写 | 0xF | 0xFFFFF244 |
VICVectPriority18 | 向量优先级18寄存器。 | 读/写 | 0xF | 0xFFFFF248 |
VICVectPriority19 | 向量优先级19寄存器。 | 读/写 | 0xF | 0xFFFFF24C |
VICVectPriority20 | 向量优先级20寄存器。 | 读/写 | 0xF | 0xFFFFF250 |
VICVectPriority21 | 向量优先级21寄存器。 | 读/写 | 0xF | 0xFFFFF254 |
VICVectPriority22 | 向量优先级22寄存器。 | 读/写 | 0xF | 0xFFFFF258 |
VICVectPriority23 | 向量优先级23寄存器。 | 读/写 | 0xF | 0xFFFFF25C |
VICVectPriority24 | 向量优先级24寄存器。 | 读/写 | 0xF | 0xFFFFF260 |
VICVectPriority25 | 向量优先级25寄存器。 | 读/写 | 0xF | 0xFFFFF264 |
VICVectPriority26 | 向量优先级26寄存器。 | 读/写 | 0xF | 0xFFFFF268 |
VICVectPriority27 | 向量优先级27寄存器。 | 读/写 | 0xF | 0xFFFFF26C |
VICVectPriority28 | 向量优先级28寄存器。 | 读/写 | 0xF | 0xFFFFF270 |
VICVectPriority29 | 向量优先级29寄存器。 | 读/写 | 0xF | 0xFFFFF274 |
VICVectPriority30 | 向量优先级30寄存器。 | 读/写 | 0xF | 0xFFFFF278 |
VICVectPriority31 | 向量优先级31寄存器。 | 读/写 | 0xF | 0xFFFFF27C |
VICAddress | 向量地址寄存器。当发生IRQ中断时,该寄存器保存当前有效中断。 | 读/写 | 0 | 0xFFFFFF00 |
[1] 复位值只反映了使用位的数值,不包括保留位的内容。
下面将按照VIC逻辑中的使用顺序对VIC寄存器进行描述,该顺序为从与中断请求输入最密切的寄存器开始,到由软件所使用的最抽象的寄存器结束。对大多数人来说,这也是在学习VIC时读取寄存器的最佳顺序。
1、 软件中断寄存器VICSoftInt(0xFFFFF018)
软件中断寄存器用于产生软件中断。在执行任何逻辑操作之前,该寄存器的内容将与32个不同外设的中断请求相“或”。
表4.41 软件中断寄存器位描述
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 不产生中断请求。写0至该位无效。 | 0 |
1 | 强制产生与该位相关的中断请求。 |
2、软件中断清零寄存器VICSoftIntClear(0xFFFFF01C)
软件中断清零寄存器为只写寄存器。对该寄存器一个或多个位写1可以清除软件中断寄存器中的置1位。
表4.42 软件中断清零寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 写0无效果。 | 0 |
1 | 写1则软件中断寄存器中对应位被清除。 |
3、原始中断状态寄存器VICRawIntr(0xFFFFF008)
该只读寄存器读取所有32个中断请求和软件中断的状态,不管中断是否使能或分类。
表4.43 原始中断状态寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 对应位的中断请求或软件中断未声明。 | - |
1 | 对应位的中断请求或软件中断声明。 |
4、中断使能寄存器VICIntEnable(0xFFFFF010)
中断使能寄存器为读写寄存器。该寄存器使能分配位FIQ和IRQ的中断请求或软件中断。
表4.44 中断使能寄存器
位 | 功能描述 | 复位值 |
31:0 | 当读取该寄存器时,读1表示中断请求使能为FIQ或IRQ,写入1,使能中断请求或软件中断分配为FIQ或IRQ;写入0无效。 | 0 |
5、中断使能清零寄存器VICIntEnClr(0xFFFFF014)
该寄存器为只写寄存器。用于清除中断使能寄存器中一个或多个中断使能位。
表4.45中断使能清零寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 写0无效果。 | - |
1 | 写1则中断使能寄存器中对应位被清除。 |
6、中断选择寄存器VICIntSelect(0xFFFFF00C)
该寄存器将32个中断请求分别分配为FIQ或IRQ.
表4.46中断选择寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 表示对应位的中断请求类型为IRQ。 | 0 |
1 | 表示对应位的中断请求类型为FIQ。 |
7、IRQ状态寄存器VICIRQStatus(0xFFFFF000)
IRQ状态寄存器为只读寄存器。该寄存器读取使能并分配为IRQ的中断请求状态。
表4.47 IRQ状态寄存器
位 | 功能描述 | 复位值 |
31:0 | 某位读取出1代表该位中断请求使能且被分配为IRQ。 | 0 |
8、FIQ状态寄存器VICFIQStatus(0xFFFFF004)
FIQ状态寄存器为只读寄存器。该寄存器读取使能并分配为FIQ的中断请求状态。如果有超过一个请求分配为FIQ,FIQ服务程序可读取该寄存器来确定是哪一个(几个)请求被激活。
表4.48 FIQ状态寄存器
位 | 功能描述 | 复位值 |
31:0 | 某位读取出1代表该位中断请求使能且被分配为FIQ。 | 0 |
9、向量地址寄存器VICVectAddr0~31(0xFFFFF100~17C)
向量地址寄存器一共有32个,每个寄存器可读可写。这些寄存器对应保存32个向量IRQ中断的中断服务程序的入口地址。
表4.49 向量地址寄存器
位 | 功能描述 | 复位值 |
31:0 | 每个寄存器对应一个中断源,该寄存器保存该中断源服务程序入口地址,中断源见表4.54。 | 0x00000000 |
10、向量优先级寄存器VICVectPriority0~31(0xFFFFF200~27C)
向量优先级寄存器用于设置32个向量中断各自优先级。优先级从0~15,0为最高优先级,15最低。所有向量优先级寄存器复位值为最低优先级15,且寄存器允许写操作逐一更改32个向量中断优先级。当优先级相同的中断同时发生,向量地址寄存器VICVectAddr0~31数值小的优先被相应。
表4.50向量优先级寄存器
位 | 功能描述 | 复位值 |
3:0 | 设置相应向量中断优先级0~15。 | 0xF |
31:4 | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义。 | NA |
11、向量地址寄存器VICAddress(0xFFFFFF00)
当处理器响应一个IRQ中断后,该中断的中断服务程序(ISR)地址可以从向量地址寄存器VICAddress读出。而这个地址是由VIC从32向量地址寄存器VICVectPriority0~31其中一个读出装入进来的。
表4.51向量地址寄存器
位 | 功能描述 | 复位值 |
31:0 | 包含当前有效中断的ISR入口地址。该寄存器在ISR结束前必须被写入一个数值(任何值),以此更新VIC优先级硬件逻辑,其他时间对该寄存器写可能引起错误产生。 | 0 |
12、软件优先级屏蔽寄存器VICSWPrioriyMask(0xFFFFF024)
软件优先级屏蔽寄存器包含了16个中断优先级的屏蔽码。
表4.52软件优先级屏蔽寄存器
位 | 值 | 功能描述 | 复位值 |
15:0 | 0 | 中断优先级被屏蔽。 | 0xFFFF |
1 | 中断优先级未被屏蔽。 | ||
31:16 | - | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义。 | NA |
13、保护使能寄存器VICProtection(0xFFFFF020)
保护使能寄存器为可读写寄存器。该寄存器控制VIC寄存器是否能被用户软件在用户态下访问。且该寄存器本身只能在管态下访问。
表4.53保护使能寄存器
位 | 值 | 功能描述 | 复位值 |
0 | 0 | VIC寄存器可以在用户态或管态下访问。 | 0 |
1 | VIC寄存器只能在管态下访问。 | ||
31:1 | - | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义 | NA |
4.6.5 中断源
表4.54列出了每个外围模块的所有中断源。每个外围设备都有一条或多条中断线连接到向量中断控制器,而且每根中断线可能代表不止一种中断源。除了确定标准的ARM内核,中断线本身没有标志或优先级。
表4.54 连接VIC通道的中断源
功能模块 | 标志 | VIC通道 | 屏蔽码 |
WDT | 看门狗中断(WDINT) | 0 | 0x0000 0001 |
- | 软件中断保留 | 1 | 0x0000 0002 |
ARM内核 | 调试器接收命令中断 | 2 | 0x0000 0004 |
ARM内核 | 调试器发送命令中断 | 3 | 0x0000 0008 |
定时器0 | 匹配0~1(MR0,MR1),捕获0~1(CR0,CR1) | 4 | 0x0000 0010 |
功能模块 | 标志 | VIC通道 | 屏蔽码 |
定时器1 | 匹配0~2(MR0,MR1,MR2),捕获0~1(CR0,CR1) | 5 | 0x0000 0020 |
UART0 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 6 | 0x0000 0040 |
UART1 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI),Modem控制更改 | 7 | 0x0000 0080 |
PWM0, PWM1 | PWM0匹配0~6,PWM0捕获0,PWM1匹配0~6,PWM1捕获0~1 | 8 | 0x0000 0100 |
I2C0 | SI(状态改变) | 9 | 0x0000 0200 |
SPI,SSP0 | SPI中断标志(SPIF),模式错误(MODF),SSP0的Tx FIFO半空(TXRIS),SSP0的Rx FIFO 半满(RXRIS),SSP0接收超时(RTRIS),SSP0接收溢出(RORRIS) | 10 | 0x0000 0400 |
SSP1 | SSP1的Tx FIFO半空(TXRIS),SSP1的Rx FIFO 半满(RXRIS),SSP1接收超时(RTRIS),SSP1接收溢出(RORRIS) | 11 | 0x0000 0800 |
PLL | PLL锁定 | 12 | 0x0000 1000 |
RTC | 计数器增加(RTCCIF),报警(RTCALF),Sub-second中断(RTCSSF) | 13 | 0x0000 2000 |
系统控制 (外部中断) | 外部中断0(EINT0) | 14 | 0x0000 4000 |
外部中断1(EINT1) | 15 | 0x0000 8000 | |
外部中断2(EINT2) | 16 | 0x0001 0000 | |
外部中断3(EINT3) 注:EINT3与GPIO中断共享 | 17 | 0x0002 0000 | |
ADC0 | A/D转换器0 | 18 | 0x0004 0000 |
I2C1 | SI(状态改变) | 19 | 0x0008 0000 |
BOD | 掉电检测 | 20 | 0x0010 0000 |
以太网 | Wakeup,软件中断,传输成功,传输结束,传输错误,传输XX,接收成功,接收结束,接受错误,接受溢出 | 21 | 0x0020 0000 |
USB | USB_INT_REQ_LP,USB_INT_REQ_HP,USB_INT_REQ_DMA | 22 | 0x0040 0000 |
CAN | CAN命令,CAN0传输,CAN0接收,CAN1传输,CAN1接收 | 23 | 0x0080 0000 |
SD/MMC接口 | RxDataAvlbl ,TxDataAvlbl,RxFifoEmpty,TxFifoEmpty,RxFifoFull,TxFifoFull,RxFifoHalFull,TxFifoHalEmpty,RxActive,TxActive,CmdActive,DataBlockEnd,StartBitErr,DataEnd,CmdSent,CmdRespEnd,RxOverrun,TxUnderrun,DataTimeOut,CmdTimeOut,DataCrcFail,CmdCrcFail | 24 | 0x0100 0000 |
GP DMA | DMA通道0状态,DMA通道1状态 | 25 | 0x0200 0000 |
定时器2 | 匹配0~3,捕获0~1 | 26 | 0x0400 0000 |
定时器3 | 匹配0~3,捕获0~1 | 27 | 0x0800 0000 |
UART2 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 28 | 0x1000 0000 |
UART3 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 29 | 0x2000 0000 |
I2C2 | SI(状态改变) | 30 | 0x4000 0000 |
I2S | Irq_rx,Irq_tx | 31 | 0x8000 0000 |
4.6.6 VIC使用注意事项
1.VIC中断与片内RAM调试。如果在片内RAM中调试程序(JTAG调试)时需要使用中断,那么必须将中断向量重新映射到地址0x00000000,这样做是因为所有的异常向量都位于地址0x00000000及以上。通过将寄存器MEMMAP(位于系统控制模块当中)配置为用户RAM模式来实现这一点。另外,用户代码编译链接时应该使中断向量表装载到地址0x40000000。
2.多个FIQ中断。虽然可以选择多个中断源(通过设置VICIntSelect)为FIQ中断,但是只有一个专门的中断服务程序来响应所有出现的FIQ请求。因此,如果分配为FIQ的中断多于一个,FIQ中断服务程序就必须先读取VICFIQStatus的内容来识别具体有效的FIQ中断源,然后在进行相应中断处理。不过还是建议用户只设置一个FIQ中断,以确保FIQ中断延迟最小。
3.IRQ中断服务程序与VIC寄存器。在中断服务程序执行完毕后,对外设中断标准的清零将会对VIC寄存器(VICRawIntr,VICFIQStatus和VICIRQStatus)当中的对应位产生影响。另外,为了能够服务下次中断,必须在中断返回之前对VICVectAddr寄存器执行一次写操作(一般可写入0),该写操作将清零内部中断优先级硬件当中对应的标志。
4.VIC中断禁能操作。若要禁止VIC中断,则必须清零VICIntEnable寄存器中的对应位,这可以通过写VICIntEnClr寄存器实现。这同样应用于VICSoftInt和VICSoftIntClear。
4.6.7 应用举例
本节以实现按键的外部中断为例介绍向量中断控制器的使用,以及在IAR Embedded Workbench集成开发环境中编写中断服务程序的方法。
1、基本操作流程
设置IRQ/FIQ中断,若是IRQ中断,则可以设置为向量中断并分配中断优先级,然后设置中断使能,以及向量中断对应地址。当产生中断后,若是IRQ中断,则可以读取向量地址寄存器,然后跳转到相应服务代码。当退出中断时,对向量地址寄存器写0,通知VIC中断结束。
对于中断源(VIC通道)的IRQ/FIQ选择,由VICIntSelect寄存器控制,每个中断源于VICIntSelect的各位一一对应,比如VIC通道14(外部中断0)与VICIntSelect的Bit14位对应,设置该位为1,则分配为FIQ中断,否则为IRQ中断。
2、设置异常向量表
在LPC2400的启动代码中(在cstartup.s79文件中)首先设置异常向量表并设置各个模式下的堆栈指针,最后跳转到用户程序运行。异常向量表是一个包含8种异常情况的向量表,具体分配如表4.55所示。
表4.55 ARM异常向量表
地址 | 异常类型 |
0x0000 0000 | 复位 |
0x0000 0004 | 未定义指令 |
0x0000 0008 | 软件中断 |
0x0000 000C | 预取指令错误 |
0x0000 0010 | 取数据错误 |
0x0000 0014 | 保留 |
0x0000 0018 | IRQ中断 |
0x0000 001C | FIQ中断 |
系统一旦产生IRQ中断,LPC2400处理器会切换到IRQ模式,并且跳转到向量表0x00000018地址执行程序。如程序清单4.6②所示,在IRQ向量处使用的指令与其他向量不同,当CPU执行这条指令但还没有跳转时, [PC, # -0x0120]表示当前PC值减去0x0120 ,当前PC的值为0x00000020,减去0x0120为0xFFFFFF00。这是VIC特殊寄存器VICVectAddr的物理地址,该寄存器保存当前将要服务的IRQ中断服务程序的入口,所以用这条LDR指令就可以直接跳转到需要的中断服务程序中。
一旦产生FIQ中断,处理器会切换到FIQ模式,并且跳转到向量表0x0000001C地址执行程序。如程序清单4.6③所示,程序将跳到FIQ_Handler标号处,处理FIQ中断服务程序。
代码清单4.6 异常向量表设置
__program_start
LDR PC, =Reset_Handler ①
LDR PC, =Undef_Handler
LDR PC, =SWI_Handler
LDR PC, =PAbt_Handler
LDR PC, =DAbt_Handler
B .
LDR PC, [PC, # -0x0120] ②
LDR PC, =FIQ_Handler ③
3、VIC初始化
接下来,在LPC2400的启动代码中包含有VIC初始化程序,如代码清单4.7所列。程序首先禁止所有中断(代码清单4.7 ①),设置VICVectAddr寄存器的值为0,将所有中断设置为IRQ中断(代码清单4.7 ③)。最后在for循环中把所有向量地址寄存器内容设置为0(代码清单4.7 ④ ),向量优先级寄存器设置为0xF,最低中断优先级(代码清单4.7 ⑤)。
禁止所有中断是避免调试时一个中断没有响应就再次装入程序运行,而VIC状态错误不能正确识别中断。
代码清单4.7 init_VIC()——VIC初始化
VICIntEnClr = 0xffffffff; ①
VICVectAddr = 0; ②
VICIntSelect = 0; ③
/* set all the vector and vector control register to 0 */
for ( i = 0; i < VIC_SIZE; i++ ) // 32个中断服务向量复位
{
vect_addr = (DWORD *)(VIC_BASE_ADDR + VECT_ADDR_INDEX + i*4);
vect_cntl = (DWORD *)(VIC_BASE_ADDR + VECT_CNTL_INDEX + i*4);
*vect_addr = 0x0; //中断服务函数都指向开头 ④
*vect_cntl = 0xF; //优先级最低 ⑤
}
4、编写中断服务程序
IAR Embedded Workbench C/C++编译器支持ARM核的IRQ中断、FIQ快速中断和SWI软件中断,可以直接采用C语言编写中断函数。中断函数必须采用ARM模式编译,如果用户正在使用的是Thumb模式,应采用扩展关键字__arm或“#pragma type_attribute=__arm”指令将其转换到ARM模式。IRQ中断函数采用扩展字__irq或#pragma type_attribute=__irq”指令声明,如程序清单4.8 ①所示。FIQ中断函数采用扩展字__fiq或#pragma type_attribute=__fiq”指令声明。需要特别注意的是,IRQ和FIQ函数的返回值类型必须为void,并且不能带有参数。
在中断服务程序开始先清除外部中断标志寄存器EXTINT中EINT0位,之后进行中断服务处理,最后写VICVectAddr寄存器,更新VIC优先级逻辑,以相应下次外部中断。
代码清单4.8 EINT0_Handler()——外部中断服务函数
__irq __arm void EINT0_Handler (void) ①
{
EXTINT = EINT0; /* 清除EXTINT寄存器中EINT0位 */ ②
……/*中断服务*/
VICVectAddr = 0; /* 写VICVectAddr寄存器,更新VIC优先级逻辑 */ ③
}
5、安装外部中断服务程序
安装外部中断服务程序主要是初始化VIC的几个特别寄存器。Install_irq一共有3个参数:IntNumber为连接VIC的中断通道数,HandlerAddr为中断函数地址,Priority为该中断通道的优先级。
如代码清单4.9所示,函数首先设置中断使能清除寄存器VICIntEnClr的对应位,无效该中断。接着通过中断通道数IntNumber得到对应向量地址寄存器VICVectAddrX和向量优先级寄存器VICVectPriorityX地址。然后使用其余两个参数初始化这两个寄存器。最后置位中断使能寄存器VICIntEnable的对应,位使能该中断。
代码清单4.9 install_irq()——中断安装函数
DWORD install_irq( DWORD IntNumber, void *HandlerAddr, DWORD Priority )
{
DWORD *vect_addr;
DWORD *vect_cntl;
VICIntEnClr = 1 << IntNumber; /* Disable Interrupt */
vect_addr=(DWORD *)(VIC_BASE_ADDR+VECT_ADDR_INDEX+IntNumber*4);
vect_cntl=(DWORD *)(VIC_BASE_ADDR+VECT_CNTL_INDEX+IntNumber*4);
*vect_addr=(DWORD)HandlerAddr; /* set interrupt vector */
*vect_cntl=Priority;
VICIntEnable = 1 << IntNumber; /* Enable Interrupt */
}
代码清单4.10为调用install_irq函数安装EINT0_Handler中断服务函数的方法。
代码清单4.10安装EINT0_Handler函数
if ( install_irq( EINT0_INT, (void *)EINT0_Handler, HIGHEST_PRIORITY ) = = FALSE )
{
return (FALSE);
}
4.6 LPC2400最小系统
在嵌入式系统硬件开发过程中,直接设计和开发目标板硬件会有相当大的难度和风险,可以先通过设计最小系统,将所需IO引脚都引出到一个插针或者板板连接器(FPC)上。实际应用电路板(本文简称底板)另行设计,最小系统板可以同直插封装的器件一样与应用电路板想连接。下文将介绍LPC2400的最小系统。
如今如同LPC2400这样的MCU芯片将FLASH、SRAM以及一些总线等集成在一片芯片中,但是仍离不开一些外围电路的设计。这部分外围电路主要为MCU提供电源、时钟震荡、电压转换、I/O口保护和驱动等功能。LPC2400的最小系统如图4.15所示,这个最小系统此文暂称为核心板。
图4.15. LPC2400核心板
本核心板分为供电电路、时钟电路、复位电路以及外部存储器电路。现做简单介绍:
1.供电电路。核心板电源主要靠实际应用电路+3.3V提供,通过左右两排插针中的相关引脚提供。LPC2400芯片采用单电源(+3.3V)供电,这样可以简化电路设计,降低产品成本。电源纹波直接影响着整个电路的工作,为了得到稳定的电压,需要外接一些电容。这些电容分为两类,一类为储能电容,这些电容的电容值比较大,如1uF、10uF等。另一类为去耦电容,其电容值较小,如0.1uF、0.01uF等,它们可以达到抑制高频噪声的功用。
2.时钟电路。这部分电路主要有晶体振荡器、电容以及电阻组成。目前有些MCU已经将该部分集成到芯片内部,但是多是以RC振荡电路形式提供所需时钟,其稳定性得不到较高的保证。使用外部晶振可以使MCU得到稳定的时钟频率。
3.复位电路。虽然目前大部分的MCU都集成有上电复位电路,在系统上电时MCU会自动产生复位信号。但在设计初期可以加入手动复位电路以方便调试。外部复位电路可以采用阻容振荡电路,也可采用诸如MAX811或者SGM811之类的专用复位芯片。
4.外部存储器电路。目前中高档的MCU尤其是ARM内核的MCU都引出有外部总线。由于大中型软件系统对FLASH以及RAM的容量的需求以及内部集成FLASH造成成本偏高的现实,使得采用外部FLASH作为存储器件最为合适。
由于LPC2478与LPC2470、LPC2468以及LPC2460等恩智浦LPC2400系列ARM7单片机在引脚上是兼容的,所以LPC2400最小系统同样也适用于上述芯片。
如图4.15所示,左右两排插针引出了实际应用电路板上所需的功能引脚,上下两排焊盘引出了LPC2478所能提供的外部总线。
LPC2400最小系统包括一下几个部分:电源电路、时钟电路、复位电路、JTAG调试电路以及功能接口电路等。其中各个部分功能如下:
1、 时钟电路给MCU提供一个外部12MHz的以及一个32.768MHz的石英振荡器。
2、 复位电路是通过引脚的方式与底板上的手动复位相连。通过底板复位电路提供手动复位信号。
3、 JTAG电路可以让用户方便的通过仿真器调试或者下载程序。
4、 外部存储电路,即有板载外部存储器(如Nor Flash、Nand Flash和SDRAM),又将外部总线引出方便用户通过外部总线扩展其他器件。
根据电路板的工作环境,可能会对电路板提出不同的要求。诸如噪声以及干扰较强的场合,以及对系统稳定性、可靠性要求较高时,印刷电路板会采用多层板设计。一般地,6层板噪声比4层板低10dB,4层板比双面板的噪声低20dB。但板层越多,相应的成本也就越高。如图4.15所示的核心板采用6层板设计,为测试提供了稳定可靠的电路。
习题:
4.1 简单说明LPC2400系列芯片复位时的处理流程。
4.2 LPC2400系列芯片的存储器空间是如何分布的?
4.3 LPC2400芯片的引脚通常都是复用的,当要使用引脚的某个功能时,应如何进行设置?
4.4 简述使能PLL的工作过程。
4.5 如果LPC2400使用的外部晶振频率为12MHz,计算最大的系统时钟频率CCLK为多少,此时M值和N值各为多少,并编写设置PLL的程序段。
4.6 LPC2400有哪些降低功耗的措施?
4.7 如果要使用外部中断0来唤醒掉电的LPC2400,应设置哪些寄存器,各寄存器的值应为多少?写出其程序段。
3.1 ARM 处理器的指令格式
3.1.1 ARM指令集的特点
ARM内核属于RISC结构,所以其指令集有着一些独特的特点:指令长度固定,指令格式的种类少,寻址方式简单。由于ARM处理器采用固定长度的32位指令,因此处理器内部硬件设计能够被简化。ARM处理器内部的指令译码采用硬布线逻辑,不使用微程序控制,以减少指令的译码时间,大部分指令可以在一个时钟周期内完成。
ARM处理器的指令按功能可分为七大类:加载/存储指令、数据处理指令、乘法指令、跳转指令、程序状态寄存器处理指令、协处理器指令和异常中断指令。
需要特别指出的是,ARM处理器的指令集是加载/存储型的,也即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。
按照操作数的特点分,ARM指令可以分为无操作数指令、单操作数指令、双操作数指令和三操作数指令。每条指令都由操作码域、条件码域、条件码设置域、目标操作数、第一操作数寄存器和第二操作数组成。
3.1.2 ARM指令的格式
每条ARM指令都是32位的,其格式如下:
31 28 27 25 24 21 20 19 16 15 12 11 0
条件码 | 类别码 | 操作码 | S | 目的寄存器 | 第一操作数 | 第二操作数 |
用ARM指令助记符表示为:
<opcode> {<cond>} {S} <Rd>, <Rn>, <shift_op2>
每个域的含义如下:
1) <opcode>:操作码域,指令编码的助记符;
2) {<cond>}:条件码域,指令允许执行的条件编码。花括号表示此项可缺省。
ARM指令的一个重要特点是可以条件执行,每条ARM指令的条件码域包含4位条件码,共16种。几乎所有指令均根据CPSR中条件码的状态和指令条件码域的设置有条件的执行。当指令执行条件满足时,指令被执行,否则被忽略。指令条件码及其助记符后缀表示参见表3.1。
每种条件码可用两个字符表示,这两个字符可以作为后缀添加在指令助记符的后面和指令同时使用。例如,跳转指令B可以加上后缀EQ变为BEQ,表示“相等则跳转”,即当CPSR中的Z标志置位时发生跳转。
表3.1 指令的条件码
条件码 | 助记符后缀 | 标 志 | 含 义 |
0000 | EQ | Z置位 | 相等 |
0001 | NE | Z清零 | 不相等 |
0010 | CS | C置位 | 无符号数大于或等于 |
0011 | CC | C清零 | 无符号数小于 |
0100 | MI | N置位 | 负数 |
0101 | PL | N清零 | 正数或零 |
0110 | VS | V置位 | 溢出 |
0111 | VC | V清零 | 未溢出 |
1000 | HI | C置位Z清零 | 无符号数大于 |
1001 | LS | C清零Z置位 | 无符号数小于或等于 |
1010 | GE | N等于V | 带符号数大于或等于 |
1011 | LT | N不等于V | 带符号数小于 |
1100 | GT | Z清零且(N等于V) | 带符号数大于 |
1101 | LE | Z置位或(N不等于V) | 带符号数小于或等于 |
1110 | AL | 忽略 | 无条件执行 |
3) {S}:条件码设置域。这是一个可选项,当在指令中设置{S}域时,指令执行的结果将会影响程序状态寄存器CPSR中相应的状态标志。
例如:
ADD R0,R1,R2; R1与R2的和存放到R0寄存器中,不影响状态寄存器
ADDS R0,R1,R2; 执行加法的同时影响状态寄存器
指令中比较特殊的是CMP指令,它不需要加S后缀就默认地根据计算结构更改程序状态寄存器。
4) <Rd>:目的操作数。ARM指令中的目的操作数总是一个寄存器。如果<Rd>与第一操作数寄存器<Rn>相同,也必须要指明,不能缺省。
5) <Rn>:第一操作数。ARM指令中的第一操作数也必须是个寄存器。
6) <shift_op2>:第二操作数。在第二操作数中可以是寄存器、内存存储单元或者立即数。
由于第二操作数只有12个bit,用第二操作数表示立即数时,其取值范围为0~212-1,要表示超出这个范围的立即数,通常要依靠伪指令实现。
3.2 ARM指令的寻址方式
所谓寻址方式就是处理器根据指令中给出的地址信息来寻找物理地址的方式。目前ARM指令系统支持如下几种常见的寻址方式。
3.2.1 立即寻址
立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址。例如以下指令:
ADD R0,R0,#1 ;R0←R0+1
ADD R0,R0,#0x3f ;R0←R0+0x3f
在以上两条指令中,第二操作数即为立即数,要求以“#”为前缀,对于以十六进制表示的立即数,还要求在“#”后加上“0x”,以二进制表示的立即数,要求在“#”后加上“%”。
当立即数大于第二操作数的表示范围时,通常用以下伪指令实现:
LDR R0,=#0xffff0000
3.2.2 寄存器寻址
寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。以下指令:
ADD R0,R1,R2 ;R0←R1+R2
该指令的执行效果是将寄存器R1和R2的内容相加,其结果存放在寄存器R0中。
3.2.3 寄存器间接寻址
寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。例如以下指令:
ADD R0,R1,[R2] ;R0←R1+[R2]
LDR R0,[R1] ;R0←[R1]
STR R0,[R1] ;[R1]←R0
在第一条指令中,以寄存器R2的值作为操作数的地址,在存储器中取得一个操作数后与R1相加,结果存入寄存器R0中。
第二条指令将以R1的值为地址的存储器中的数据传送到R0中。
第三条指令将R0的值传送到以R1的值为地址的存储器中。
3.2.4 基址变址寻址
基址变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基地址附近的地址单元。采用变址寻址方式的指令常见有以下几种形式,如下所示:
LDR R0,[R1,#4] ;R0←[R1+4]
LDR R0,[R1,#4]! ;R0←[R1+4]、R1←R1+4
LDR R0,[R1] ,#4 ;R0←[R1]、R1←R1+4
LDR R0,[R1,R2] ;R0←[R1+R2]
在第一条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中。
在第二条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第三条指令中,以寄存器R1的内容作为操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第四条指令中,将寄存器R1的内容加上寄存器R2的内容形成操作数的有效地址,从而取得操作数存入寄存器R0中。
3.2.5 多寄存器寻址
多寄存器寻址是ARM处理器特有的一种寻址方式。由于ARM内核有较多的通用寄存器,采用多寄存器寻址方式,一条指令可以一次完成多个寄存器值的传送。这种寻址方式可以用一条指令完成传送最多16个通用寄存器的值。例如以下指令:
LDMIA R0,{R1,R2,R3,R4} ;R1←[R0]
;R2←[R0+4]
;R3←[R0+8]
;R4←[R0+12]
该指令的后缀IA表示在每次执行完加载/存储操作后,R0按字长度增加,因此,指令可将连续存储单元的值传送到R1~R4。
多个连续的寄存器可以用“-”符号连接;不连续的寄存器用“,”分隔书写,如上例可写成:
LDMIA R0,{R1-R4}
LDMIA R0,{R1-R3,R4}
3.2.6 寄存器移位寻址
寄存器移位寻址是ARM指令集特有的寻址方式。ARM处理器内嵌桶型移位器(Barrel Shifter),支持数据的各种移位操作。当第二操作数为寄存器时,可以加入移位操作选项对它进行各种移位操作。
移位操作包括如下6种类型:
1、LSL(或ASL)逻辑(算术)左移
寻址格式:
通用寄存器,LSL(或ASL) 操作数
完成对通用寄存器中的内容进行逻辑(或算术)的左移操作,按操作数所指定的数量向左移位,低位用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, LSL#2 ;将R1中的内容左移两位后传送到R0中。
2、LSR逻辑右移
寻址格式:
通用寄存器,LSR 操作数
完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, LSR#2 ;将R1中的内容右移两位后传送到R0中,左端用零来填充。
3、ASR算术右移
寻址格式:
通用寄存器,ASR 操作数
完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用第31位的值来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, ASR#2 ;将R1中的内容右移两位后传送到R0中,左端用第31位的值来填充。
4、ROR循环右移
寻址格式:
通用寄存器,ROR 操作数
完成对通用寄存器中的内容进行循环右移的操作,按操作数所指定的数量向右循环移位,左端用右端移出的位来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。显然,当进行32位的循环右移操作时,通用寄存器中的值不改变。
如:
MOV R0, R1, ROR#2 ;将R1中的内容循环右移两位后传送到R0中。
5、RRX带扩展的循环右移
寻址格式:
通用寄存器,RRX 操作数
完成对通用寄存器中的内容进行带扩展的循环右移的操作,按操作数所指定的数量向右循环移位,左端用进位标志位C来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, RRX#2 ;将R1中的内容进行带扩展的循环右移两位后传送到R0中。
3.2.7 相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。以下程序段完成子程序的调用和返回,跳转指令BL采用了相对寻址方式:
BL NEXT ;跳转到子程序NEXT处执行
……
NEXT
……
MOV PC,LR ;从子程序返回
3.2.8 堆栈寻址
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,根据堆栈的生成方式,又可以分为递增堆栈(Ascending Stack)和递减堆栈(Decending Stack)。当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。这样就有四种类型的堆栈工作方式,ARM微处理器支持这四种类型的堆栈工作方式,即:
1. 满递增堆栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生成。
2. 满递减堆栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生成。
3. 空递增堆栈(EA):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。
4. 空递减堆栈(ED):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。
3.3 ARM指令集
本节对ARM指令集的七大类指令进行详细的描述。
3.3.1 加载/存储指令
ARM处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。常用的加载存储指令如下:
1、LDR指令
LDR指令的格式为:
LDR{条件} 目的寄存器,<存储器地址>
LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,且寻址方式灵活多样,请读者认真掌握。
如:
LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0。
LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
LDR R0,[R1,R2] ! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将
;新地址R1+R2写入R1。
LDR R0,[R1,#8] ! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新
;地址R1+8写入R1。
LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地
;址R1+R2写入R1。
LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4的字数据读入寄存器R0,
;并将新地址R1+R2×4写入R1。
LDR R0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地
;址R1+R2×4写入R1。
2、STR指令
STR指令的格式为:
STR{条件} 源寄存器,<存储器地址>
STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR。
如:
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
LDR/STR指令都可以加B、H、SB、SH的后缀,分别表示加载/存储字节、半字、带符号的字节、带符号的半字。如LDRB指令表示从存储器加载一个字节进寄存器。当使用这些后缀时,要注意所使用的存储器要支持访问的数据宽度。
3、LDM(或STM)批量数据加载/存储指令
LDM(或STM)指令的格式为:
LDM(或STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}
LDM(或STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。其中,{类型}为以下几种情况:
IA 每次传送后地址加1;
IB 每次传送前地址加1;
DA 每次传送后地址减1;
DB 每次传送前地址减1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。
基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。
{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。
如:
STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈。
LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。
4、SWP数据交换指令
SWP指令的格式为:
SWP{条件} 目的寄存器,源寄存器1,[源寄存器2]
SWP指令用于将源寄存器2所指向的存储器中的字数据传送到目的寄存器中,同时将源寄存器1中的字数据传送到源寄存器2所指向的存储器中。显然,当源寄存器1和目的寄存器为同一个寄存器时,指令交换该寄存器和存储器的内容。
如:
SWP R0,R1,[R2] ;将R2所指向的存储器中的字数据传送到R0,同时将R1中的字数据传送到R2所指向的存储单元。
SWP R0,R0,[R1] ;该指令完成将R1所指向的存储器中的字数据与R0中的字数据交换。
3.3.2 数据处理指令
数据处理指令可分为数据传送指令、算术逻辑运算指令和比较指令等。
数据传送指令用于在寄存器和存储器之间进行数据的双向传输。
算术逻辑运算指令完成常用的算术与逻辑的运算,该类指令不但将运算结果保存在目的寄存器中,同时更新CPSR中的相应条件标志位。
比较指令不保存运算结果,只更新CPSR中相应的条件标志位。
1、 MOV指令
MOV指令的格式为:
MOV{条件}{S} 目的寄存器,源操作数
MOV指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。其中S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
如:
MOV R1,R0 ;将寄存器R0的值传送到寄存器R1
MOV PC,R14 ;将寄存器R14的值传送到PC,常用于子程序返回
MOV R1,R0,LSL#3 ;将寄存器R0的值左移3位后传送到R1
2、 MVN指令
MVN指令的格式为:
MVN{条件}{S} 目的寄存器,源操作数
MVN指令可完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。与MOV指令不同之处是在传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中。其中S决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
如:
MVN R0,#0 ;将立即数0取反传送到寄存器R0中,完成后R0=-1
3、 CMP指令
CMP指令的格式为:
CMP{条件} 操作数1,操作数2
CMP指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位表示的是操作数1与操作数2的关系(大、小、相等),例如,当操作数1大于操作操作数2,则此后的有GT 后缀的指令将可以执行。
如:
CMP R1,R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
CMP R1,#100 ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位
4、 CMN指令
CMN指令的格式为:
CMN{条件} 操作数1,操作数2
CMN指令用于把一个寄存器的内容和另一个寄存器的内容或立即数取反后进行比较,同时更新CPSR中条件标志位的值。该指令实际完成操作数1和操作数2相加,并根据结果更改条件标志位。
如:
CMN R1,R0 ;将寄存器R1的值与寄存器R0的值相加,并根据结果设置CPSR的标志位
CMN R1,#100 ;将寄存器R1的值与立即数100相加,并根据结果设置CPSR的标志位
5、 TST指令
TST指令的格式为:
TST{条件} 操作数1,操作数2
TST指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。操作数1是要测试的数据,而操作数2是一个位掩码,该指令一般用来检测是否设置了特定的位。
如:
TST R1,#%1 ;用于测试在寄存器R1中是否设置了最低位(%表示二进制数)
TST R1,#0xffe ;将寄存器R1的值与立即数0xffe按位与,并根据结果设置CPSR的标志位
6、 TEQ指令
TEQ指令的格式为:
TEQ{条件} 操作数1,操作数2
TEQ指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的异或运算,并根据运算结果更新CPSR中条件标志位的值。该指令通常用于比较操作数1和操作数2是否相等。
如:
TEQ R1,R2 ;将寄存器R1的值与寄存器R2的值按位异或,并根据结果设置CPSR的标志位
7、 ADD指令
ADD指令的格式为:
ADD{条件}{S} 目的寄存器,操作数1,操作数2
ADD指令用于把两个操作数相加,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
如:
ADD R0,R1,R2 ; R0 = R1 + R2
ADD R0,R1,#256 ; R0 = R1 + 256
ADD R0,R2,R3,LSL#1 ; R0 = R2 + (R3 << 1)
8、 ADC指令
ADC指令的格式为:
ADC{条件}{S} 目的寄存器,操作数1,操作数2
ADC指令用于把两个操作数相加,再加上CPSR中的C条件标志位的值,并将结果存放到目的寄存器中。它使用一个进位标志位,这样就可以做比32位大的数的加法,注意不要忘记设置S后缀来更改进位标志。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
以下指令序列完成两个128位数的加法,第一个数由高到低存放在寄存器R7~R4,第二个数由高到低存放在寄存器R11~R8,运算结果由高到低存放在寄存器R3~R0:
ADDS R0,R4,R8 ; 加低端的字
ADCS R1,R5,R9 ; 加第二个字,带进位
ADCS R2,R6,R10 ; 加第三个字,带进位
ADC R3,R7,R11 ; 加第四个字,带进位
9、 SUB指令
SUB指令的格式为:
SUB{条件}{S} 目的寄存器,操作数1,操作数2
SUB指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
如:
SUB R0,R1,R2 ; R0 = R1 - R2
SUB R0,R1,#256 ; R0 = R1 - 256
SUB R0,R2,R3,LSL#1 ; R0 = R2 - (R3 << 1)
10、SBC指令
SBC指令的格式为:
SBC{条件}{S} 目的寄存器,操作数1,操作数2
SBC指令用于把操作数1减去操作数2,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
如:
SUBS R0,R1,R2 ; R0 = R1 - R2 - !C,并根据结果设置CPSR的进位标志位
11、RSB指令
RSB指令的格式为:
RSB{条件}{S} 目的寄存器,操作数1,操作数2
RSB指令称为逆向减法指令,用于把操作数2减去操作数1,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
如:
RSB R0,R1,R2 ; R0 = R2 – R1
RSB R0,R1,#256 ; R0 = 256 – R1
RSB R0,R2,R3,LSL#1 ; R0 = (R3 << 1) - R2
12、RSC指令
RSC指令的格式为:
RSC{条件}{S} 目的寄存器,操作数1,操作数2
RSC指令用于把操作数2减去操作数1,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
如:
RSC R0,R1,R2 ; R0 = R2 – R1 - !C
13、AND指令
AND指令的格式为:
AND{条件}{S} 目的寄存器,操作数1,操作数2
AND指令用于在两个操作数上进行逻辑与运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于屏蔽操作数1的某些位。
如:
AND R0,R0,#3 ; 该指令保持R0的0、1位,其余位清零。
14、ORR指令
ORR指令的格式为:
ORR{条件}{S} 目的寄存器,操作数1,操作数2
ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位。
如:
ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。
15、EOR指令
EOR指令的格式为:
EOR{条件}{S} 目的寄存器,操作数1,操作数2
EOR指令用于在两个操作数上进行逻辑异或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于反转操作数1的某些位。
如:
EOR R0,R0,#3 ; 该指令反转R0的0、1位,其余位保持不变。
16、BIC指令
BIC指令的格式为:
BIC{条件}{S} 目的寄存器,操作数1,操作数2
BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。
如:
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。
3.3.3 乘法指令与乘加指令
ARM微处理器支持的乘法指令与乘加指令共有6条,可分为运算结果为32位和运算结果为64位两类。与前面的数据处理指令不同,指令中的所有操作数、目的寄存器必须为通用寄存器,不能对操作数使用立即数或被移位的寄存器,同时,目的寄存器和操作数1必须是不同的寄存器。
1、 MUL指令
MUL指令的格式为:
MUL{条件}{S} 目的寄存器,操作数1,操作数2
MUL指令完成将操作数1与操作数2的乘法运算,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数或无符号数。
如:
MUL R0,R1,R2 ;R0 = R1 × R2
MULS R0,R1,R2 ;R0 = R1 × R2,同时设置CPSR中的相关条件标志位
2、 MLA指令
MLA指令的格式为:
MLA{条件}{S} 目的寄存器,操作数1,操作数2,操作数3
MLA指令完成将操作数1与操作数2的乘法运算,再将乘积加上操作数3,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数或无符号数。
如:
MLA R0,R1,R2,R3 ;R0 = R1 × R2 + R3
MLAS R0,R1,R2,R3 ;R0 = R1 × R2 + R3,同时设置CPSR中的相关条件标志位
3、 SMULL指令
SMULL指令的格式为:
SMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数。
如:
SMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位
;R1 = (R2 × R3)的高32位
4、 SMLAL指令
SMLAL指令的格式为:
SMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
如:
SMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位 + R0
;R1 = (R2 × R3)的高32位 + R1
5、 UMULL指令
UMULL指令的格式为:
UMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
如:
UMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位
;R1 = (R2 × R3)的高32位
6、 UMLAL指令
UMLAL指令的格式为:
UMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
如:
UMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位 + R0
;R1 = (R2 × R3)的高32位 + R1
3.3.4 跳转指令
跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转:使用专门的跳转指令、直接向程序计数器PC写入跳转地址值。
直接向PC写入跳转地址值,可以实现在4GB的地址空间中任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。
使用跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转。
1、 B指令
B指令的格式为:
B{条件} 目标地址
B指令是最简单的跳转指令。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(前后32MB的地址空间)。
如:
B Label ;程序无条件跳转到标号Label处执行
CMP R1,#0 ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
BEQ Label
2、 BL指令
BL指令的格式为:
BL{条件} 目标地址
BL 是另一个跳转指令,但跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14 的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。
如:
BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中
Label标号处可以是一个子程序,在子程序的最后可以使用MOV PC,LR指令跳回BL Label指令处的下一条指令继续执行。
3、 BLX指令
BLX指令的格式为:
BLX 目标地址
BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态由ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中。因此,当子程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成。
4、 BX指令
BX指令的格式为:
BX{条件} 目标地址
BX指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是Thumb指令。
3.3.5 程序状态寄存器访问指令
ARM指令不允许直接操作程序状态寄存器CPSR和SPSR。可以通过程序状态寄存器访问指令,在程序状态寄存器和通用寄存器之间传送数据,然后在通用寄存器中进行处理。
1、 MRS指令
MRS指令的格式为:
MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)
MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。该指令一般用在以下几种情况:
1) 当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。
2) 当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。
如:
MRS R0,CPSR ;传送CPSR的内容到R0
MRS R0,SPSR ;传送SPSR的内容到R0
2、 MSR指令
MSR指令的格式为:
MSR{条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数
MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:
位[31:24]为条件标志位域,用f表示;
位[23:16]为状态位域,用s表示;
位[15:8]为扩展位域,用x表示;
位[7:0]为控制位域,用c表示;
该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
如:
MSR CPSR,R0 ;传送R0的内容到CPSR
MSR SPSR,R0 ;传送R0的内容到SPSR
MSR CPSR_c,R0 ;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域
3.3.6 协处理器指令
ARM微处理器可支持多达16个协处理器,用于各种协处理操作,在程序执行的过程中,每个协处理器只执行针对自身的协处理指令,忽略ARM处理器和其他协处理器的指令。
ARM的协处理器指令主要用于ARM处理器初始化ARM协处理器的数据处理操作,以及在ARM处理器的寄存器和协处理器的寄存器之间传送数据,和在ARM协处理器的寄存器和存储器之间传送数据。
1、CDP指令
CDP指令的格式为:
CDP{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
CDP指令用于ARM处理器通知ARM协处理器执行特定的操作,若协处理器不能成功完成特定的操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器和源寄存器均为协处理器的寄存器,指令不涉及ARM处理器的寄存器和存储器。
如:
CDP P3,2,C12,C10,C3,4 ;该指令完成协处理器P3的初始化
2、LDC指令
LDC指令的格式为:
LDC{条件}{L} 协处理器编码,目的寄存器,[源寄存器]
LDC指令用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
如:
LDC P3,C4,[R0] ;将ARM处理器的寄存器R0所指向的存储器中的字数据传送到协处理器P3的寄存器C4中。
3、STC指令
STC指令的格式为:
STC{条件}{L} 协处理器编码,源寄存器,[目的寄存器]
STC指令用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
如:
STC P3,C4,[R0] ;将协处理器P3的寄存器C4中的字数据传送到ARM处理器的寄存器R0所指向的存储器中。
4、MCR指令
MCR指令的格式为:
MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
MCR指令用于将ARM处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,源寄存器为ARM处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄存器。
如:
MCR P3,3,R0,C4,C5,6 ;该指令将ARM处理器寄存器R0中的数据传送到协处理器P3的寄存器C4和C5中。
5、MRC指令
MRC指令的格式为:
MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
MRC指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器为ARM处理器的寄存器,源寄存器1和源寄存器2均为协处理器的寄存器。
如:
MRC P3,3,R0,C4,C5,6 ;该指令将协处理器P3的寄存器中的数据传送到ARM处理器寄存器中。
3.3.7 异常中断指令
1、SWI指令
SWI指令的格式为:
SWI{条件} 24位的立即数
SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的异常处理程序中提供相应的系统服务,指令中24位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时,参数通过其他通用寄存器传递。
如:
SWI 0x02 ;该指令调用操作系统编号位02的系统例程。
2、BKPT指令
BKPT指令的格式为:
BKPT 16位的立即数
BKPT指令产生软件断点中断,可用于程序的调试。
3.4 Thumb指令集
为兼容数据总线宽度为16位的应用系统,ARM体系结构除了支持执行效率很高的32位ARM指令集以外,同时支持16位的Thumb指令集。Thumb指令集是ARM指令集的一个子集,允许指令编码为16位的长度。与等价的32位代码相比较,Thumb指令集在保留32位代码优势的同时,大大的节省了系统的存储空间。
所有的Thumb指令都有对应的ARM指令,而且Thumb的编程模型也对应于ARM的编程模型,在应用程序的编写过程中,只要遵循一定调用的规则,Thumb子程序和ARM子程序就可以互相调用。当处理器在执行ARM程序段时,称ARM处理器处于ARM工作状态,当处理器在执行Thumb程序段时,称ARM处理器处于Thumb工作状态。
与ARM指令集相比较,Thumb指令集中的数据处理指令的操作数仍然是32位,指令地址也为32位,但Thumb指令集为实现16位的指令长度,舍弃了ARM指令集的一些特性,如大多数的Thumb指令是无条件执行的,而几乎所有的ARM指令都是有条件执行的;大多数的Thumb数据处理指令的目的寄存器与其中一个源寄存器相同。
由于Thumb指令的长度为16位,即只用ARM指令一半的位数来实现同样的功能,所以,要实现特定的程序功能,所需的Thumb指令的条数较ARM指令多。在一般的情况下,Thumb指令与ARM指令的时间效率和空间效率关系为:
— Thumb代码所需的存储空间约为ARM代码的60%~70%
— Thumb代码使用的指令数比ARM代码多约30%~40%
— 若使用32位的存储器,ARM代码比Thumb代码快约40%
— 若使用16位的存储器,Thumb代码比ARM代码快约40%~50%
— 与ARM代码相比较,使用Thumb代码,存储器的功耗会降低约30%
显然,ARM指令集和Thumb指令集各有其优点,若对系统的性能有较高要求,应使用32位的存储系统和ARM指令集,若对系统的成本及功耗有较高要求,则应使用16位的存储系统和Thumb指令集。当然,若两者结合使用,充分发挥其各自的优点,会取得更好的效果。
3.5 伪指令
ARM编译器一般都支持汇编语言的程序设计和C/C++语言的程序设计,以及两者的混合编程。在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对应的操作码,通常称这些特殊指令助记符为伪指令,他们所完成的操作称为伪操作。伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,这些伪指令仅在汇编过程中起作用,一旦汇编结束,伪指令的使命就完成。
在ARM的汇编程序中,有如下几种伪指令:ARM伪指令、符号定义伪指令、数据定义伪指令、段定义伪指令、模块控制伪指令、汇编控制伪指令、宏处理伪指令等等。
需要特别指出的是,除了几条ARM伪指令以外,其它的伪指令依赖于编译器。也就是说,不同的ARM编译器的伪指令集是不相同的。例如,ADS编译器的段定义伪指令为AREA,而IAR编译器的段定义伪指令为RSEG和ASEG。这种情况使得不同编译器下编出的ARM汇编程序是不同的。读者在阅读不同学习材料时应注意分辨在不同编译器下ARM汇编程序的区别。
本书介绍的是IAR EWARM编译器支持的ARM汇编伪指令。
3.5.1 ARM伪指令
ARM伪指令不是ARM指令集中的指令。它可以象其它ARM指令一样使用,但在编译时这些指令将被等效的ARM指令所取代。
1、LDR-大范围地址读取
LDR伪指令的格式为:
LDR{条件} reg,=expr/label_expr
reg为加载的目的寄存器;expr为32位立即数;label_expr为地址表达式或外部表达式。
LDR伪指令将32位常量或一个32位地址加载到指定寄存器。
如:
LDR R0,=#0x12345 ;加载32位立即数0x12345到寄存器R0
LDR R0,=DATA_BUF+60 ;加载DATA_BUF地址+60
2、ADR-小范围地址读取
ADR伪指令的格式为:
ADR{条件} reg,expr
reg为加载的目的寄存器;expr为相对偏移表达式,非字对齐时取值范围为-255~255字节,字对齐时取值范围为-1020~1020字节。
ADR伪指令将基于当前PC相对偏移的地址值读取到寄存器中。
如:
Start: MOV R0,#10
ADR R4,start ;相当于SUB R4,PC,#0x0c
3、ADRL-中范围地址读取
ADRL伪指令与ADR类似,不同在expr的取值范围,非字对齐时取值范围为64KB,字对齐时取值范围为256KB。
4、NOP-空操作
3.5.2 数据定义伪指令
1、DCB和DC8
该伪指令的格式为:
标号 DCB或DC8 表达式
DCB和DC8伪指令用于分配一片连续的8位字节存储单元,并用伪指令中指定的表达式初始化。其中表达式可以为0~255的数字或字符串。
如:
Str DCB “This is a test!” ;分配一个字符串,每个字符8位字节
2、DCW和DC16、DCD和DC32
与DCB和DC8用法相同,不同的是分别分配16位半字节单元和32位字单元。
3、DF32和DF64
分别表示32位的单精度浮点数和64位的双精度浮点数。
4、DS8、DS16、DS24和DS32
分别用于保留8位字节、16位半字、24位字和32位字的存储器空间。
如:
Dataspace DS8 100 ;保留100个8位字节的存储器空间
3.5.3 符号定义伪指令
1、=、ALIAS和EQU
该伪指令的格式为:
标号 = 表达式
标号 ALIAS 表达式
标号 EQU 表达式
伪指令EQU和=可用于为程序模块中的常量、标号等赋值,定义的局部符号仅在其所在的模块内有效。伪指令ALIAS为符号起个别名。定义的符号采用PUBLIC伪指令声明其属性可使之被其它模块引用,引用其它模块内符号时必须采用EXTERN伪指令声明其属性。
如:
Test EQU 50 ;定义符号Test的值为50
2、ASSIGN、SET、SETA和VAR
用法与EQU等类似,可用于定义一个变量符号。采用VAR定义的变量符号不能用PUBLIC声明其属性。
3、DEFINE
用于定义在整个程序文件内都有效的全局符号。该符号可以被文件内的所有程序模块引用,但不能在同一文件内重新定义。
4、LIMIT
该伪指令的格式为:
LIMIT 表达式, 最小值, 最大值, 提示信息
用于检查表达式的值是否位于给定范围之内。如果表达式值的范围超限,则输出提示信息。
如:
Speed VAR 23 ;定义符号speed的值为23
LIMIT speed,10,30,…speed out of range… ;检查speed的值是否超限
5、EXTERN(或IMPORT)
该伪指令的格式为:
EXTERN 符号,[符号]……
EXTERN伪指令用于通知汇编器,要使用的符号在其它源文件中定义,但要在当前源文件中引用。
如:
Name Start ;程序模块Start
EXTERN Main ;告诉汇编器Main符号在其它源文件中定义
……
BL Main ;在本模块中引用Main符号
END
6、PUBLIC(或EXPORT)
该伪指令的格式为:
PUBLIC 符号,[符号]……
PUBLIC伪指令用于在程序中声明一个全局符号,该符号可在其它文件中引用。
7、REQUIRE
PUBLIC伪指令用于将一个符号标记为已经被引用。
3.5.4 段定义伪指令
1、ASEG和ASEGN
该伪指令的格式为:
ASEG [起始地址[(对齐)]]
ASEGN 段名[:存储器类型],地址
ASEG伪指令用于定义一个绝对段,并设置段的起始地址。不指定地址值时第一个段默认起始地址为0,后续段地址依次递增。ASEGN伪指令用于设置指定段的绝对起始地址,并允许规定段类型。存储器类型可以为CODE(代码段)、DATA(数据段)、STACK(堆栈段)。
如:
ASEG 0 ;定义一个绝对段,起始地址0
ASEGN CODE:CODE,0 ;定义一个名为CODE的代码段,起始地址0
2、RSEG
该伪指令的格式为:
RSEG 段名[:存储器类型][:(NO)ROOT|(NO)REORDER|SORT][(对齐)]
RSEG伪指令用于定义一个可重定位段,段的起始地址由汇编器临时分配。单个模块中最多可定义65536个可重定位段。
如:
RSEG CODE:CODE:ROOT(2) ;定义一个名为CODE的可重定位代码段,用户权限为ROOT(可读写),段内存储器对齐方式为4字节对齐
3、DATA
该伪指令的格式为:
DATA 段名[:存储器类型][(对齐)]
DATA伪指令可以在代码段内定义一个数据区。
如:
RSEG CODE:CODE:ROOT(2)
DATA
f1: DC32 subrtn
4、STACK
该伪指令的格式为:
COMMON 段名[:存储器类型][(对齐)]
STACK伪指令用于定义一个堆栈段,用作堆栈的存储器地址从高向低变化,而用作可重定位段的存储器地址是从低向高变化。
5、COMMON
该伪指令的格式为:
COMMON 段名[:存储器类型][(对齐)]
COMMON伪指令用于定义公共段,各源文件中同名的COMMON段共享同一段内存。它的典型应用是多个不同子程序共享一段数据存储区。中断向量表也可安排在COMMON段,以便允许从多个服务子程序访问。
6、CODE16和CODE32
CODE16伪指令用于告诉汇编器,其后的指令序列为16位的Thumb指令;CODE32伪指令用于告诉汇编器,其后的指令序列为32位的ARM指令。因此,在使用ARM指令和Thumb指令混合编程的代码中,可用这两条伪指令进行切换。但需要注意的是,它们只通知汇编器其后指令的类型,并不能对处理器进行状态切换。
7、ORG
该伪指令的格式为:
ORG 地址表达式
ORG伪指令用于设置段的起始地址。地址表达式的计算结果应与当前段的类型保持一致,如在RSEG(可重定位段)中,不要使用“ORG 10”,因为10是一个绝对地址,而应当使用“ORG .+10”,表示当前段偏移量为10的地址。另外,地址表达式中不能包括任何前向和外部引用。
8、ALIGNRAM和ALIGNROM
该伪指令的格式为:
ALIGNRAM 对齐
ALIGNROM 对齐[,填充值]
用于设置存储器地址边界的对齐方式,“对齐”是一个值为2~30的常数,并按22~30设定对齐地址。ALIGNRAM以数据增量方式对齐,ALIGNROM以填充0字节方式对齐。
9、EVEN和ODD
该伪指令的格式为:
EVEN [填充值]
ODD [填充值]
EVEN伪指令用于将程序计数器PC以偶数地址对齐(等价于ALIGNROM 1),ODD伪指令用于将程序计数器PC以奇数地址对齐。
3.5.5 模块控制伪指令
1、NAME和PROGRAM
该伪指令的格式为:
NAME 模块名
PROGRAM 模块名
NAME和PROGRAM伪指令用于定义一个程序模块。程序模块类似于C语言中的函数,是程序中相对独立的一个部分。程序模块即使没有被调用也会被无条件链接。
如:
NAME Main ;定义一个名为Main的程序模块
2、END和ENDMOD
END伪指令用于结束整个汇编语言程序,ENDMOD用于结束当前程序模块。每个汇编语言程序最后必须使用END伪指令通知汇编器已经到了源程序结尾,以结束汇编。
3、LIBRARY和MODULE
该伪指令用于定义多模块文件中的小模块,其中每个小模块代表一段子程序,从而可以方便地创建库模块文件。与NAME和PROGRAM不同的是,用LIBRARY和MODULE定义的模块只有在被调用时才会复制到链接代码中。
4、RTMODEL
该伪指令的格式为:
RTMODEL 关键字字符串,值字符串
该伪指令用于声明模块的运行模式属性,以强制模块之间的一致性。所有能被链接在一起的模块必须具有相同的关键字;值字符串要么具有相同的值,要么其值为星号“*”。
3.5.6 汇编控制伪指令
1、$和INCLUDE
该伪指令的格式为:
$ 文件名
INCLUDE 文件名
该伪指令用于给当前源文件加载头文件。
2、CASEOFF和CASEON
该伪指令用于源程序文件中禁止和允许大小写字符敏感。
3、LTORG
在使用ARM伪指令LDR加载地址数据时,要在适当的位置加入LTORG声明一个数据区,把要加载的数据保存在数据区内,再用LDR读出数据。LTORG伪指令通常放在无条件分支或子程序返回指令后面,这样处理器就不会错误的将数据区中的数据当作指令执行。
4、RADIX
该伪指令用于声明当前使用的数制形式。如:
RADIX 16D ;声明当前使用十六进制数
MOV R0,#12 ;此处#12为0x12
5、IF、ELSE和ENDIF
该伪指令的格式为:
IF 逻辑表达式
指令序列1
ELSE
指令序列2
ENDIF
条件汇编伪指令能根据设定条件的成立与否决定是否对指令序列进行汇编生成目标代码。若逻辑表达式为真,则对指令序列1汇编生成目标代码;否则对指令序列2汇编。其中还可以用ELSEIF伪指令设定新条件。
如:
DEFINE Test ;定义一个全局变量Test
……
IF Test = TRUE
指令序列1
ELSE
指令序列2
ENDIF
3.5.7 宏处理伪指令
1、MACRO和ENDM
该伪指令的格式为:
宏名 MACRO [,参数][ ,参数]……
指令序列
ENDM
MACRO伪指令用于定义一个宏,引用宏时必须使用定义的宏名,并可向宏中传递参数。ENDM伪指令用于结束宏定义。
如:
errmac MACRO text
BL abort
DATA
DC8 text,0
ENDM
包含在MACRO和ENDM之间的指令序列称为宏定义体。在宏定义体的第一行应声明宏的原型(包括宏名和所需的参数),然后就可以在汇编程序中通过宏名来调用该指令序列。在源程序被编译时,汇编器将宏调用展开,用宏定义中的指令序列代替程序中的宏调用,并将实际参数值传递给宏定义中的形式参数。
2、REPT和ENDR
该伪指令的格式为:
REPT 表达式
指令序列
ENDR
该伪指令用于指示汇编器将指定的指令序列进行重复汇编,重复次数由表达式的值确定。如果表达式的值为0,则不进行任何操作。
3、REPTC和ENDR
该伪指令的格式为:
REPTC 符号,替换字符串
指令序列
ENDR
该伪指令用于在宏展开时用替换字符串中的单个字符逐次替换符号。
4、REPTI和ENDR
该伪指令的格式为:
REPTI 符号,替换字符串[,替换字符串]……
指令序列
ENDR
该伪指令用于在宏展开时用整个替换字符串替换符号。
3.6 ARM汇编语言的语句格式
3.6.1 ARM汇编语言的语句格式
ARM(Thumb)汇编语言的语句格式为:
[标号[:]] 指令或伪指令 操作数 [;注释]
其中,方括号内的内容为可选项。
标号顶格书写时后面可不用冒号,非顶格书写时后面必须用冒号。
标号前加一个问号“?”前缀,表示该标号为外部标号,且仅能通过汇编语言访问;标号前加两个下划线“__”前缀,表示该标号为外部标号,能通过C语言和汇编语言访问;没有前缀的标号为局部标号,仅能在本模块内访问。
IAR汇编器对大小写字符敏感,一般指令和伪指令助记符使用大写,标号使用大小写混杂的方式以示区分。
同时,如果一条语句太长,可将该长语句分为若干行来书写,在行的末尾用“\”表示下一行与本行为同一条语句。
IAR汇编器规定汇编语言程序文件的默认扩展名为“.s79”,也可以用“.s”或“.asm”作为扩展名。
3.6.2 符号
在汇编语言程序设计中,经常使用各种符号代替地址、变量和常量等,以增加程序的可读性。尽管符号的命名由编程者决定,但并不是任意的,必须遵循以下的约定:
1.符号由大小写字母、数字及下划线组成,符号不能用数字开头。
2.符号区分大小写,同名的大、小写符号会被编译器认为是两个不同的符号。
3.符号在其作用范围内必须唯一。
4.自定义的符号名不能与系统的保留字相同。
5.符号名不应与指令或伪指令同名。
6. IAR汇编器内部预定义符号以双下划线开头和结尾。如:__IAR_SYSTEMS_ASM__。
3.6.3 常量和变量
1、 常量
程序中的常量是指其值在程序的运行过程中不能被改变的量。ARM(Thumb)汇编程序所支持的常量有数字常量、逻辑常量和字符串常量。
数字常量一般为32位的整数,当作为无符号数时,其取值范围为0~232-1,当作为有符号数时,其取值范围为-231~231-1。数字常量有4种表示形式:十进制数如123、-456等;十六进制数如0x123、0FFFFH等;八进制数如1234q等;二进制数如1010b等。
逻辑常量只有两种取值情况:TRUE和FALSE。
字符串常量为一个固定的字符串,一般用于程序运行时的信息提示。用法与标准C语言相同。
2、 变量
程序中的变量是指其值在程序的运行过程中可以改变的量。ARM(Thumb)汇编程序所支持的变量有数字变量、逻辑变量和字符串变量。
数字变量用于在程序的运行中保存数字值,但注意数字值的大小不应超出数字变量所能表示的范围。
逻辑变量用于在程序的运行中保存逻辑值,逻辑值只有两种取值情况:真或假。
字符串变量用于在程序的运行中保存一个字符串,但注意字符串的长度不应超出字符串变量所能表示的范围。
3.7 ARM汇编语言的程序结构
3.7.1 汇编语言的程序结构
在ARM(Thumb)汇编语言程序中,以程序段为单位组织代码。段是相对独立的指令或数据序列,具有特定的名称。段可以分为代码段和数据段,代码段的内容为执行代码,数据段存放代码运行时需要用到的数据。一个汇编程序至少应该有一个代码段,当程序较长时,可以分割为多个代码段和数据段,多个段在程序编译链接时最终形成一个可执行的映象文件。
可执行映象文件通常由以下几部分构成:
1. 1个或多个代码段,代码段的属性为只读。
2. 0个或多个包含初始化数据的数据段,数据段的属性为可读写。
3. 0个或多个不包含初始化数据的数据段,数据段的属性为可读写。
链接器根据系统默认或用户设定的规则,将各个段安排在存储器中的相应位置。因此源程序中段之间的相对位置与可执行的映象文件中段的相对位置一般不会相同。
3.7.2 一个简单的ARM汇编语言程序
以下是一个汇编语言源程序的基本结构:
代码清单3.1
NAME ASM_EXAMPL ;定义一个名为ARM_EXAMPL的程序模块
RSEG CODE:CODE:ROOT(2) ;定义一个可重定位的代码段
CODE32 ;执行32位ARM指令
ORG 0x1000 ;定义程序起始地址为0x1000
Start: LDR R0,=0x3FF5000 ;Start处的地址即为0x1000
LDR R1,=0xff
STR R1,[R0]
MOV R0,#0x10
MOV R1,#0x20
ADD R0,R0,R1
Stop: B Stop ;跳转到指令本身,程序停止运行
ENDMOD ;本程序模块结束
END ;本程序结束
程序很简单,它完成的功能并不重要,但它已经表示出了一个ARM汇编语言程序的基本结构。
3.8 ARM程序设计举例
3.8.1 分支程序
程序设计中的三种基本结构是:顺序结构、分支结构和循环结构。在C语言中可以使用if-else语句实现单分支和双分支结构,也可以通过switch-case语句实现多分支结构。但是在汇编语言中,分支结构一般是通过跳转指令结合标号来实现的。
在ARM汇编语言程序中,由于ARM指令支持条件执行,从而大大减少了分支程序的复杂程度。
例如:用两个整数辗转相减的方法求它们的最大公约数。注意体会B指令加条件码的执行方式。程序中使用的main标号是因为IAR汇编器一般默认从main标号处开始执行。
代码清单3.2
NAME GCD
PUBLIC main ;声明外部引用标号main
B main ;从main标号处开始执行
RSEG CODE:CODE
CODE32
main: MOV R0,#120
MOV R1,#96
Gcd: CMP R0,R1 ;比较两数的大小
BEQ Stop ;如果两数相等则跳到结束处
BLT Less ;如果R0<R1则跳到Less标号处
SUB R0,R0,R1 ;否则R0=R0-R1
B Gcd
Less: SUB R1,R1,R0 ;R1=R1-R0
B Gcd
Stop: B Stop ;跳转到指令本身,程序停止运行
ENDMOD ;本程序模块结束
END ;本程序结束
3.8.2 循环程序
通过跳转指令还可以实现程序的循环结构。
例如:求n=1+2+…+10累加的和。
代码清单3.3
NAME SUM
PUBLIC main
B main
RSEG CODE
CODE32
main: MOV R0,#10
MOV R1,R0 ;利用R1寄存器做循环计数器
Loop: SUBS R1,R1,1 ;循环次数减1
ADD R0,R0,R1
BNE Loop ;循环次数为0则结束循环
Stop: B Stop
ENDMOD
END
3.8.3 子程序调用
通过BL指令可以实现子程序调用,语法:BL子程序名。
在子程序的结束处,可以通过MOV PC,LR返回到主程序中。通常可以使用寄存器R0~R3完成传递参数到子程序和从子程序返回运算的结果。
以下是使用BL指令调用子程序的汇编语言源程序的例子,该程序编写了一个在内存里拷贝字符串的子程序,然后在主程序里调用它。
代码清单3.4
NAME STRCPY
PUBLIC main
B main
RSEG CODE
CODE32
main: LDR R1,=srcstr ;R1指向源字符串
LDR R0,=dststr ;R0指向目标字符串
BL strcopy ;调用strcopy子程序
stop: B stop ;程序停止
strcopy: ;子程序定义
LDRB R2,[R1],#1 ;读一个字符到R2,并更新源字符地址
STRB R2,[R0],#1 ;写一个字符,并更新目的字符地址
CMP R2,#0 ;是否结束。以数字0为标志
BNE strcopy ;循环执行
DATA ;数据区
srcstr DCB "First string - source ",0
dststr DCB "Second string - destination ",0
ENDMOD
END
3.8.4 查表法
查表法是编程中常用的一种技巧。当程序涉及到较多的数据、数据串或数据表格时,可以通过地址来对它们进行访问。通常有两种方法装载地址:(1)通过ADR和ADRL伪指令直接装载地址;(2)通过伪指令LDR Rd,=Label从数据表格中装载地址。
下面的程序设置了3个参数,arithfunc根据3个参数返回一个R0值。当R0=0时,R0=R1+R2;当R0=1时,R0=R1-R2;当R0>1时,R0=R1+R2。:
代码清单3.5
NAME JUMP
PUBLIC main
B main
Num EQU 2 ;跳转表格的入口数
RSEG CODE
CODE32
main: MOV R0,#0 ;以下设置3个参数
MOV R1,#3
MOV R2,#2
BL arithfunc ;调用子程序
stop: B stop ;程序停止
arithfunc:
CMP R0,#Num ;比较参数
BHS Doadd ;若R0>=2,则执行加法
ADR R3,jumptable ;装载跳转表格标号地址
LDR PC,[R3,R0,LSL #2] ;跳到相应子程序入口地址处
Jumptable:
DCD Doadd ;Doadd子程序的入口地址
DCD Dosub ;Dosub子程序的入口地址
Doadd: ADD R0,R1,R2 ;=0或>1时执行的操作
MOV PC,LR
Dosub: SUB R0,R1,R2 ;=1时执行的操作
MOV PC,LR
ENDMOD
END
3.8.5 汇编语言与C/C++的混合编程
在应用系统的程序设计中,若所有的编程任务均用汇编语言来完成,其工作量是可想而知的,同时,不利于系统升级或应用软件移植,事实上,ARM体系结构支持C/C+以及与汇编语言的混合编程,在一个完整的程序设计的中,除了初始化部分用汇编语言完成以外,其主要的编程任务一般都用C/C++ 完成。
汇编语言与C/C++的混合编程通常有以下几种方式:
1. 在C/C++代码中嵌入汇编指令。
在ARM C中,可以使用关键字__arm来标识一段汇编指令程序。格式如下:
__asm
{
汇编指令序列
}
即可在C语言源程序中直接执行ARM汇编指令。
2. 在汇编程序和C/C++的程序之间进行变量的互访。
3. 汇编程序、C/C++程序间的相互调用。
可以把汇编程序和C/C++程序中需要共享的变量或函数用PUBLIC或extern关键字分别声明为全局变量或全局函数,然后在其它程序文件中即可进行访问和调用。但是从好的编程风格来说,最好尽量减少全局变量和全局函数的使用。
混合编程中,必须遵守一定的调用规则,如物理寄存器的使用、参数的传递等。ARM专门为此制定了一个标准ATPCS(ARM-Thumb Procedure Call Standard,ARM-Thumb过程调用标准)。对于初学者来说,这是非常烦琐的,在实际工作中也没有太多必要。
在实际的编程应用中,使用较多的方式是:系统程序的初始化部分用汇编语言完成,然后用C/C++完成主要的编程任务,程序在执行时首先完成初始化过程,然后跳转到C/C++程序代码中。汇编程序和C/C++程序之间一般没有参数的传递,也没有频繁的相互调用,因此,整个程序的结构显得相对简单,容易理解。
以下是一个这种结构程序的基本示例。该程序非常简单,建立一个工程asm_c.eww,工程中包括一个汇编语言程序文件init.s79和一个C语言程序文件hello.c。
代码清单3.6——init.s79文件
NAME INIT
PUBLIC main
EXTERN Main ;声明引入C程序的Main()函数
B main
RSEG CODE
CODE32
main:
NOP ;此处可以插入用户自己编写的系统初始化代码
B Main ;转向C语言程序
ENDMOD
END
代码清单3.7——hello.c文件:
#include <stdio.h>
/*注意此处C语言程序的入口函数是大小写敏感的Main()函数,而不是常用的main()函数。这是为了跟汇编程序中的main入口区别开,以免造成工程有两个程序入口。*/
int Main(void)
{
printf("Hello, world!\n");
}
3.9 用ARM汇编语言编写系统启动程序
基于ARM内核的芯片多数为复杂的片上系统,这种复杂系统里的多数硬件模块都是可以配置的,需要由软件来设置其需要的工作状态。由于C语言具有模块性和可移植性的特点,大部分基于ARM的应用系统程序都采用C语言编写。但是当系统复位启动时,在进入C语言的main函数之前,需要有一段启动程序来完成对存储器配置、地址重映射和ARM芯片内部集成外围功能初始化等工作。这类工作直接面对处理器内核和硬件控制器进行编程,用C语言较难实现,因此一般采用汇编语言编写。
3.9.1 编写启动程序的一般规则
ARM内核的处理器在复位后,从0x00000000地址处开始读取指令。实现启动最简单的方法是将应用程序放在映射空间地址为0的ROM中。这样当执行第1条指令时,应用程序就从0x00000000处开始执行。但这种方法有很多缺点:ROM的存储宽度较小且速度较慢,会降低系统启动和处理器对异常处理的速度;异常向量表放在ROM中,程序将无法修改向量表,因此常将地址为0的空间映射成RAM,但RAM中的程序掉电无法保存,因此必须将ROM映射为0地址,以保证有效的复位向量,然后再使用重映射命令将RAM映射为0地址,ROM映射到其他地址空间,并将异常向量从ROM复制到RAM中。
编写启动程序应遵循以下一般规则:
1. 设置入口指针
启动程序首先必须定义入口指针,而且整个应用程序只有一个入口指针,通常应用程序的入口地址为0。
2. 设置异常向量
基于ARM7TDMI内核的处理器共支持7种异常,异常处理地址存放在地址0处的异常向量表中,共8×4字节的空间。异常向量表的内容参见2.4.2节。
异常向量表通常放在存储器底部,每个异常分配4个字节的空间。每个向量入口包含一条跳转指令或加载PC的指令,以执行适当的转移到具体的异常处理程序。如果ROM定位于0地址,则向量表由一系列固定的用以指向每个异常的指令组成;否则向量必须被动态初始化。可以在启动程序中添加一段代码,使其在运行时将向量表拷贝到0地址开始的存储器空间。对于没有使用的异常,使其指向一个只含返回指令的哑函数,以防止错误异常引起系统混乱。
3. 初始化片内集成外围功能
由于ARM公司仅设计内核并出售给其它半导体厂商,不同的厂商购买内核授权后加入自己的外围功能,从而导致ARM核处理器芯片丰富多样,但也使得不同芯片的启动代码在这一部分差别很大。编写这一部分时应根据芯片和应用系统要求对它们进行合适的初始化。比较重要的操作一般有:外部总线接口的初始化、配置时钟锁相环、配置中断控制器、禁用看门狗电路等。
4. 初始化存储系统
有些ARM核芯片可通过对寄存器编程来初始化系统存储器,而对于较复杂系统通常由存储管理单元MMU来管理内存空间。为正确运行应用程序,在初始化期间应将系统需要读写的数据和变量从ROM拷贝到RAM中;一些要求快速响应的程序,例如中断处理程序,也需要在RAM中运行;如果使用Flash,对Flash的擦除和写入操作也一定要在RAM中运行。
5. 初始化堆栈寄存器
系统堆栈初始化取决于用户使用了哪些中断,以及系统需要处理哪些错误类型。一般来说管理模式堆栈必须初始化。如果使用IRQ中断,则IRQ堆栈必须初始化,并且必须在允许中断之前进行。如果使用FIQ中断,则FIQ堆栈也必须初始化,并且必须在允许中断之前进行。一般在简单的嵌入式系统中不使用中止状态堆栈和未定义指令堆栈,但为了调试方便还是将其初始化。如果系统使用DRAM或其它外设,还需要设置相关寄存器,以确定其刷新频率、数据总线宽度等信息。
6. 改变处理器模式和状态
此时可以通过清除CPSR寄存器中的中断控制位来允许中断,这里是安全开启中断的最早地方。这个阶段处理器仍处于管理模式下。如果程序需要在用户模式下运行,可以在此处切换到用户模式并初始化用户模式堆栈指针。
7. 跳转到C语言主程序
在从启动程序跳转到C语言程序的main函数之前,还需要初始化数据存储空间。通常是加入一段循环代码对数据存储空间清0。这样做的主要原因是C语言中没有初值的变量默认值均为0。已经初始化变量的初值必须从ROM中复制到RAM中,其它变量的初值必须为0。
3.9.2 IAR EWARM软件包给出的一般启动程序
下面给出了IAR EWARM软件包提供的一般启动程序代码,实际应用中可以根据具体芯片及应用系统要求进行适当修改,以适应不同场合的需要。
代码清单3.8——IAR EWARM启动代码
;-----------------------------------------------------------------------------
; 文件中标号的命名规则:
; ?xxx - 仅能由汇编语言访问的外部标号
; __xxx - 可由C语言访问或定义的外部标号
; xxx - 单个模块中的局部标号(注意,本文件包含多个模块)
; main - 用户程序的起点
;---------------------------------------------------------------
; 适用于整个文件的宏和模式定义
;---------------------------------------------------------------
; 模式,对应于CPSR寄存器的0~5位
MODE_BITS DEFINE 0x1F ; 用于CPSR模式的位屏蔽
USR_MODE DEFINE 0x10 ; 用户模式
FIQ_MODE DEFINE 0x11 ; FIQ模式
IRQ_MODE DEFINE 0x12 ; IRQ模式
SVC_MODE DEFINE 0x13 ; 管理模式
ABT_MODE DEFINE 0x17 ; 中止模式
UND_MODE DEFINE 0x1B ; 未定义指令模式
SYS_MODE DEFINE 0x1F ; 系统模式
;---------------------------------------------------------------
; ?RESET
; 复位向量。通常INTVEC段被链接到地址0。为程序调试方便,也可以放在其它地址
;---------------------------------------------------------------
MODULE ?RESET
COMMON INTVEC:CODE:NOROOT(2)
PUBLIC __program_start
EXTERN ?cstartup
EXTERN undef_handler, swi_handler, prefetch_handler
EXTERN data_handler, irq_handler, fiq_handler
CODE32 ; 复位后始终为ARM模式
org 0x00
__program_start
ldr pc,[pc,#24] ; 绝对跳转地址范围为4GB
; ldr b,?cstartup ; 相对跳转允许重映射,限于32MB
; 可以去掉以下指令前的注释分号来允许异常向量
; 也可以在C语言中采用预编译命令“#pragma vector”
org 0x04
; ldr pc,[pc,#24] ; 跳转到undef_handler
org 0x08
; ldr pc,[pc,#24] ; 跳转到swi_handler
org 0x0c
; ldr pc,[pc,#24] ; 跳转到prefetch_handler
org 0x10
; ldr pc,[pc,#24] ; 跳转到data_handler
org 0x18
; ldr pc,[pc,#24] ; 跳转到irq_handler
org 0x1c
; ldr pc,[pc,#24] ; 跳转到fiq_handler
; 用于“ldr pc”指令的常数表入口定位于0x20
; 异常向量可以用C语言的预编译命令“#pragma vector”指定,也可以
; 在以下dc32指令后面填入向量地址。向量地址为ARM向量号+20
org 0x20
dc32 ?cstartup
org 0x24
; dc32 undef_handler
org 0x28
; dc32 swi_handler
org 0x2c
; dc32 prefetch_handler
org 0x30
; dc32 data_handler
org 0x38
; dc32 irq_handler
org 0x3c
; dc32 fiq_handler
LTORG
; ENDMOD __program_start
ENDMOD
;---------------------------------------------------------------
; ?CSTARTUP
;---------------------------------------------------------------
MODULE ?CSTARTUP
RSEG IRQ_STACK:DATA(2)
RSEG ABT_STACK:DATA:NOROOT(2)
RSEG UND_STACK:DATA:NOROOT(2)
RSEG FIR_STACK:DATA:NOROOT(2)
RSEG SVC_STACK:DATA:NOROOT(2)
RSEG CSTACK:DATA(2)
RSEG ICODE:CODE:NOROOT(2)
PUBLIC ?cstartup
EXTERN ?main
; 从这里开始执行
; 复位后为ARM管理模式,禁止中断
CODE32
?cstartup
; 需要时在这里加入建立堆栈指针之前的初始化指令
; 初始化堆栈指针
; 以下方式可用于任何异常堆栈:FIQ, IRQ, SVC, ABT, UND, SYS.
; 用户模式使用与SYS模式相同的堆栈
; 堆栈段必须在链接器命令文件中定义,并且已经在上面声明
mrs r0,cpsr ; 原PSR值
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#IRQ_MODE ; 置IRQ模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(IRQ_STACK)&0xFFFFFFF8 ; IRQ_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#ABT_MODE ; 置Abort模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(ABT_STACK)&0xFFFFFFF8 ; ABT_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#SVC_MODE ; 置Supervisor模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(SVC_STACK) & 0xFFFFFFF8 ; SVC_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#UND_MODE ; 置Undefined模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(UND_STACK) & 0xFFFFFFF8 ; FIR_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#FIQ_MODE ; 置FIR模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(FIR_STACK) & 0xFFFFFFF8 ; FIR_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#SYS_MODE ; 置System模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(CSTACK) & 0xFFFFFFF8 ; CSTACK结束
#ifdef __ARMVFP__
; 允许VFP协处理器
mov r0, #0x40000000 ; 置VFP的EN位
fmxr fpexc, r0 ; FPEXC, 清除其它
; 将缓冲区清0以禁止下溢出。为满足IEEE 754标准,应删除该指令并安装合适的异常句柄
mov r0, #0x01000000 ; 置VFP的FZ位
fmxr fpscr, r0 ; FPSCR, 清除其它
#endif
; 在这里可以添加更多的用户自定义初始化指令
; 跳转到?main标号的地方,继续IAR系统的启动程序
ldr r0,=?main
bx r0
LTORG
ENDMOD
END
习题
3.1 ARM7TDMI有几种寻址方式?LDR R1,[R0,#0x04]属于哪种寻址方式?
3.2 ARM指令的条件码有多少个?默认条件码是什么?
3.3 ARM指令中第二个操作数有哪几种形式?
3.4 请指出MOV指令与LDR加载指令的区别及用途.
3.5 CMP指令的功能是什么?写一个程序,判断R1的值是否大于0x30,是则将R1减去0x30。
3.6 调用子程序是用B还是用BL指令?请写出返回子程序的指令。
3.7 ARM状态与Thumb状态的切换指令是什么?请举例说明。
3.8 Thumb状态与ARM状态的寄存器有区别吗?Thumb指令对哪些寄存器的访问受到一定限制?
3.9 Thumb指令集的堆栈入栈、出栈指令是哪两条?
3.10 把下面的C代码转换成汇编代码。数组a和b分别存放在以0x4000和0x5000为起始地址的存储区内,类型为long型(32位)。
for(i=0;i<8;i++)
{
a[i] = b[7-i];
}
3.11 编写程序,将R1的高8位传送到R2的低8位
3.12 编写一段64位加法运算的程序,要求满足:[R1:R0]+[R3:R2],结果存入[R1:R0]中
3.13 编写程序将地址0x0000 1000到0x0000 1030的数据全部搬迁到0x0000 2000到0x0000 2030的区域中,并将源数据区清零。
第4章 LPC2400系列处理器原理
处理器的“体系结构”指从程序员角度观察到的处理器的组织方式,所以又称为处理器的编程模型。其主要内容为处理器内的寄存器组织、对存储器的寻址方式、指令系统等。本章将介绍ARM7TDMI程序员模型、工作状态与工作模式、ARM和Thumb状态的寄存器组织、存储器组织结构、异常及协处理器接口等一些基本概念。本章还要讲述ARM的编程基础,如ARM微处理器的基本工作原理、与程序设计相关的基本技术细节等。
4.1 LPC2400系列处理器简介
4.1.1 LPC2400系列处理器特性
LPC2400系列处理器包括LPC2468/LPC2470/LPC2478等多款芯片,是基于支持实时仿真和跟踪的16/32位ARM7TDMI-S内核的微控制器,它与所有NXP LPC 2000处理器具有相同的存储器映射、中断向量控制、Flash编程和更新机制,以及调试和仿真功能。LPC2468/LPC2478的512KB大容量嵌入式高速Flash存储器具有128位宽度的存储器接口和独特的加速结构,使得32位代码能够在最高时钟频率72MHz下运行。16位Thumb模式可以将代码规模降低30%以上,而性能损失却很小。LPC2470/LPC2478芯片内部还集成了LCD接口支持(最高1024×768像素、15阶灰度单色和每像素24位真彩色TFT面板),使得这两款芯片可以广泛应用于各种手持式设备中。
LPC2400系列处理器拥有丰富的片上资源和外设接口。这一系列芯片的共同特性有:
-ARM7TDMI-S内核,最高72MHz主频;
-98KB的片内静态存储器,其中64KB的片内SRAM,16KB SRAM用于以太网,16KB SRAM用于DMA控制器(也可用于USB控制器),2KB SRAM用于RTC实时时钟;
-512KB片内Flash程序存储器,片内Boot实现IAP和ISP片内Flash编程;
-可配置的外部存储器接口,最多支持8个Bank,支持外部RAM、ROM和Flash存储器扩展,每个Bank最大可支持到256MB,可支持8/16/32位字宽;
-高级向量中断控制器,支持32个向量中断,可配置优先级和向量地址;
-通用AHB DMA控制器(GPDMA)可以用于支持SSP、I2S和SD/MMC接口;
-10/100M以太网MAC接口;
-多个串行接口,包括4路UART、3路I2C串行总线接口和1个SPI接口;
-10位A/D和D/A转换器,转换时间低至2.44微秒;
-USB device/host/OTG接口;
-2个CAN总线接口;
-4个32位的定时器、2个PWM脉冲调制单元(每个6路输出)、实时时钟和看门狗;
-160个高速GPIO端口(可承受5V电压),4个独立外部中断引脚;
-标准ARM调试接口,兼容各种现有的调试工具;
-片内晶振频率范围1~24MHz;
-4个低功耗模式:空闲、睡眠、掉电和深度掉电模式;
-供电电压3.3V(3.0V~3.6V)。
在LPC2400系列芯片中,LPC2468是LPC2478的无LCD控制器版本,LPC2470是LPC2478的无片内Flash版本,芯片的大多数特性是完全相同的。所以在后面的章节中,本书一律采用LPC2478芯片为例进行讲解,请读者在实际工作中注意具体芯片的差别。
4.1.2 LPC2400系列处理器结构
LPC2400系列处理器包含一个支持仿真的ARM7TDMI-S CPU、与片内存储器控制器接口的ARM7局部总线、与中断控制器接口的AMBA高性能总线(AHB总线)和连接片内外设功能的AMBA外设总线(APB总线)。存储模式为小端模式。
AHB总线和APB总线都是ARM公司推出的AMBA片上总线规范的一部分。AHB(Advanced High performance Bus)系统总线主要用于高性能模块(如CPU、DMA和DSP等)之间的连接,一般用于片内高性能高速度的外设,如:外部存储器、USB接口、DMA控制器、以太网控制器、LCD液晶屏控制器以及高速GPIO控制器等。LPC2400中的AHB外设一共分配了2MB的地址范围,它位于4GB ARM存储器空间的最顶端。每个AHB外设都分配了16KB的地址空间。
LPC2400的外设功能模块都连接到APB总线。APB(Advanced Peripheral Bus)外围总线主要用于低带宽的周边外设之间的连接,如:UART、I2C、SPI、I2S、A/D、D/A、CAN等等。APB总线与AHB总线之间通过AHB到APB的桥相连。APB外设也分配了2MB的地址范围,每个APB外设在APB地址空间内都分配了16KB的地址空间。
片内外设与器件引脚的连接由引脚连接模块控制。软件可以通过控制该模块让引脚与特定的片内外设相连接。
LPC2400的结构框图如图4.1所示。
图4.1 LPC2400结构框图
4.2 处理器引脚配置
4.2.1引脚配置
LPC2400系列处理器共有208个引脚,一般提供两种封装形式:LQFP208和TFBGA208。其管脚封装如图4.2所示。
LQFP208 FBGA208
图4.2 LPC2400系列处理器管脚封装图
LQFP指封装本体厚度为1.4mm的薄型QFP(四侧引脚扁平封装quad flat package),它是一种表面贴装型封装,引脚从四个侧面引出呈L型,每个侧面52个引脚,引脚号分别为1~52、53~104、105~156、157~208。FBGA是塑料封装的BGA(Ball Grid Array Package),即球栅阵列封装,其引脚都在芯片底部,用英文字母行和数字列标识。由于LPC2400系列处理器在实际使用中更多使用QFP封装,本节引脚介绍以QFP封装为准。
从功能上,LPC2400的208个引脚分为P0口、P1口、P2口、P3口、P4口,以及电源、复位、晶振和其它管脚几部分。下面对这几个部分分别进行介绍。
1. P0口: P0口是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P0口管脚描述如表4.1所示。
表4.1 LPC2400的P0口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P0[0] |
94 | I/O | P0[0]:GPIO口 |
I | RD1:CAN1接收器输入 | ||
O | TXD3:UART3发送输出端 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P0[1] |
96 | I/O | P0[1]:GPIO口 |
O | TD1:CAN1发送器输出 | ||
I | RXD3:UART3接收输入端 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P0[2] |
202 | I/O | P0[2]:GPIO口 |
O | TXD0:UART0发送输出端 | ||
P0[3] |
204 | I/O | P0[3]:GPIO口 |
I | RXD0:UART0接收输入端 | ||
P0[4] |
168 | I/O | P0[4]:GPIO口 |
I/O | I2SRX_CLK:I2S总线接收时钟 | ||
I | RD2:CAN2接收输入端 | ||
I | CAP2[0]:Timer2的捕获输入通道0 | ||
P0[5] |
166 | I/O | P0[5]:GPIO口 |
I/O | I2SRX_WS:I2S总线接收字选择 | ||
I | TD2:CAN2发送输出端 | ||
I | CAP2[1]:Timer2的捕获输入通道1 | ||
P0[6] |
164 | I/O | P0[6]:GPIO口 |
I/O | I2SRX_SDA:I2S总线数据接收 | ||
I/O | SSEL1:SSP1从机选择 | ||
O | MAT2[0]:Timer2的匹配输出通道0 | ||
P0[7] |
162 | I/O | P0[7]:GPIO口 |
I/O | I2STX_CLK:I2S总线发送时钟 | ||
I/O | SCK1:SSP1串行时钟 | ||
O | MAT2[1]:Timer2的匹配输出通道1 | ||
P0[8] |
160 | I/O | P0[8]:GPIO口 |
I/O | I2STX_WS:I2S总线发送字选择 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
O | MAT2[2]:Timer2的匹配输出通道2 | ||
P0[9] |
158 | I/O | P0[9]:GPIO口 |
I/O | I2STX_SDA:I2S总线数据发送 | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
O | MAT2[3]:Timer2的匹配输出通道3 | ||
P0[10] |
98 | I/O | P0[10]:GPIO口 |
O | TXD2:UART2发送输出端 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
O | MAT3[0]:Timer3的匹配输出通道0 | ||
P0[11] |
100 | I/O | P0[11]:GPIO口 |
I | RXD2:UART2接收输入端 | ||
I/O | SCL2:I2C2时钟输入/输出 | ||
O | MAT3[1]:Timer3的匹配输出通道1 | ||
P0[12] |
41 | I/O | P0[12]:GPIO口 |
O | USB_PPWR2:USB端口2端口电源使能 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
I | AD0[6]:A/D转换器0输入6 | ||
P0[13] |
45 | I/O | P0[13]:GPIO口 |
O | USB_UP_LED2:USB端口2LED | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
I | AD0[7]:A/D转换器0输入7 | ||
P0[14] |
69 | I/O | P0[14]:GPIO口 |
O | USB_HSTEN2:USB端口2主机使能 | ||
O | USB_CONNECT2:USB端口2软件连接控制 | ||
I/O | SSEL1:SSP1从机选择 | ||
P0[15] |
128 | I/O | P0[15]:GPIO口 |
O | TXD1:UART1发送输出端 | ||
I/O | SCK0:SSP0串行时钟 | ||
I/O | SCK:SPI串行时钟 | ||
P0[16] |
130 | I/O | P0[16]:GPIO口 |
I | RXD1:UART1接收输入端 | ||
I/O | SSEL0:SSP0从机选择 | ||
I/O | SSEL:SPI从机选择 | ||
P0[17] |
126 | I/O | P0[17]:GPIO口 |
I | CTS1:UART1清除发送输入端 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
I/O | MISO:SPI主机输入从机输出 | ||
P0[18] |
124 | I/O | P0[18]:GPIO口 |
I | DCD1:UART1数据载波检测输入端 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
I/O | MOSI:SPI主机输出从机输入 | ||
P0[19] |
122 | I/O | P0[19]:GPIO口 |
I | DSR1:UART1数据设置就绪端 | ||
O | MCICLK:SD/MMC接口时钟输出线 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P0[20] |
120 | I/O | P0[20]:GPIO口 |
O | DTR1:UART1数据终止就绪端 | ||
I/O | MCICMD:SD/MMC接口命令线 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P0[21] |
118 | I/O | P0[21]:GPIO口 |
I | RI1:UART1铃响指示输入端 | ||
O | MCIPWR:SD/MMC电源供应使能 | ||
I | RD1:CAN1接收输入端 | ||
P0[22] |
116 | I/O | P0[22]:GPIO口 |
O | RTS1:UART1请求发送输出端 | ||
I/O | MCIDAT0:SD/MMC接口数据线0 | ||
O | TD1:CAN1发送输出端 | ||
P0[23] |
18 | I/O | P0[23]:GPIO口 |
I | AD0[0]:A/D转换器0输入0 | ||
I/O | I2SRX_CLK:I2S总线接收时钟 | ||
I | CAP3[0]:Timer3的捕获输入通道0 | ||
P0[24] |
16 | I/O | P0[24]:GPIO口 |
I | AD0[1]:A/D转换器0输入1 | ||
I/O | I2SRX_WS:I2S总线字选择 | ||
I | CAP3[1]:Timer3的捕获输入通道1 | ||
P0[25] |
14 | I/O | P0[25]:GPIO口 |
I | AD0[2]:A/D转换器0输入2 | ||
I/O | I2SRX_SDA:I2S总线数据接收 | ||
O | TXD3:UART3发送输出端 | ||
P0[26] |
12 | I/O | P0[26]:GPIO口 |
I | AD0[3]:A/D转换器0输入3 | ||
O | AOUT:D/A转换器输出 | ||
I | RXD3:UART3接收输入端 | ||
P0[27] |
50 | I/O | P0[27]:GPIO口 |
I/O | SDA0:I2C0数据输入/输出 | ||
P0[28] |
48 | I/O | P0[28]:GPIO口 |
I/O | SCL0:I2C0时钟输入/输出 | ||
P0[29] |
61 | I/O | P0[29]:GPIO口 |
I/O | USB_D+1:USB端口1双向D+线 | ||
P0[30] |
62 | I/O | P0[30]:GPIO口 |
I/O | USB_D-1:USB端口1双向D-线 | ||
P0[31] |
51 | I/O | P0[31]:GPIO口 |
I/O | USB_D+2:USB端口2双向D+线 |
2. P1口: P1口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P1口管脚描述如表4.2所示。
表4.2 LPC2400的P1口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P1[0] |
196 | I/O | P1[0]:GPIO口 |
O | ENET_TXD0:以太网发送数据0(RMII/MII接口) | ||
P1[1] |
194 | I/O | P1[1]:GPIO口 |
O | ENET_TXD1:以太网发送数据1(RMII/MII接口) | ||
P1[2] |
185 | I/O | P1[2]:GPIO口 |
O | ENET_TXD2:以太网发送数据2(RMII/MII接口) | ||
O | MCICLK:SD/MMC接口时钟输出线 | ||
O | PWM0[1]:脉宽调制器0输出1 | ||
P1[3] |
177 | I/O | P1[3]:GPIO口 |
O | ENET_TXD3:以太网发送数据3(RMII/MII接口) | ||
O | MCICMD:SD/MMC接口命令线 | ||
O | PWM0[2]:脉宽调制器0输出2 | ||
P1[4] |
192 | I/O | P1[4]:GPIO口 |
O | ENET_TX_EN:以太网发送数据使能(RMII/MII接口) | ||
P1[5] |
156 | I/O | P1[5]:GPIO口 |
O | ENET_TX_ER:以太网发送数据出错(MII接口) | ||
O | MCIPWR:SD/MMC电源供应使能 | ||
O | PWM0[3]:脉宽调制器0输出3 | ||
P1[6] |
171 | I/O | P1[6]:GPIO口 |
I | ENET_TX_CLK:以太网发送时钟(MII接口) | ||
I/O | MCIDAT0:SD/MMC接口数据线0 | ||
O | PWM0[4]:脉宽调制器0输出4 | ||
P1[7] |
153 | I/O | P1[7]:GPIO口 |
I | ENET_COL:以太网冲突检测(MII接口) | ||
I/O | MCIDAT1:SD/MMC接口数据线1 | ||
O | PWM0[5]:脉宽调制器0输出5 | ||
P1[8] |
190 | I/O | P1[8]:GPIO口 |
I | ENET_CRS_DV/ENET_CRS:以太网载波检测/数据有效(RMII接口)/以太网载波检测(MII接口) | ||
P1[9] |
188 | I/O | P1[9]:GPIO口 |
I | ENET_RXD0:以太网接收数据0(RMII/MII接口) | ||
P1[10] |
186 | I/O | P1[10]:GPIO口 |
I | ENET_RXD1:以太网接收数据1(RMII/MII接口) | ||
P1[11] |
163 | I/O | P1[11]:GPIO口 |
I | ENET_RXD2:以太网接收数据2(RMII/MII接口) | ||
I/O | MCIDAT2:SD/MMC接口数据线2 | ||
O | PWM0[6]:脉宽调制器0输出6 | ||
P1[12] |
157 | I/O | P1[12]:GPIO口 |
I | ENET_RXD3:以太网接收数据3(RMII/MII接口) | ||
I/O | MCIDAT3:SD/MMC接口数据线3 | ||
I | PCAP0[0]:脉宽调制器0捕获输入通道0 | ||
P1[13] |
147 | I/O | P1[13]:GPIO口 |
I | ENET_RX_DV:以太网接收数据有效(MII接口) | ||
P1[14] |
184 | I/O | P1[14]:GPIO口 |
I | ENET_RX_ER:以太网接收错误(MII接口) | ||
P1[15] |
182 | I/O | P1[15]:GPIO口 |
I | ENET_REF_CLK/ENET_RX_CLK:以太网参考时钟(RMII接口)/以太网接收时钟(MII接口) | ||
P1[16] |
180 | I/O | P1[16]:GPIO口 |
I | ENET_MDC:以太网MIIM时钟 | ||
P1[17] |
178 | I/O | P1[17]:GPIO口 |
I/O | ENET_MDIO:以太网MI数据输入输出 | ||
P1[18] |
66 | I/O | P1[18]:GPIO口 |
O | USB_UP_LED1:USB端口1LED | ||
O | PWM1[1]:脉宽调制器1输出1 | ||
I | CAP1[0]:Timer1捕获输入通道0 | ||
P1[19] |
68 | I/O | P1[19]:GPIO口 |
O | USB_TX_E1:USB端口1发送使能信号(OTG收发器) | ||
O | USB_PPWR1:USB端口1端口电源使能信号 | ||
I | CAP1[1]:Timer1捕获输入通道1 | ||
P1[20] |
70 | I/O | P1[20]:GPIO口 |
O | USB_TX_DP1:USB端口1D+数据发送(OTG收发器) | ||
O | PWM1[2]:脉宽调制器1输出2 | ||
I/O | SCK0:SSP0串行时钟 | ||
P1[21] |
72 | I/O | P1[21]:GPIO口 |
O | USB_TX_DM1:USB端口1D-数据发送(OTG收发器) | ||
O | PWM1[3]:脉宽调制器1输出3 | ||
I/O | SSEL0:SSP0从机选择 | ||
P1[22] |
74 | I/O | P1[22]:GPIO口 |
I | USB_RCV1:USB端口1差分数据接收(OTG收发器) | ||
I | USB_PWRD1:USB端口1电源状态(主机电源开关) | ||
O | MAT1[0]:Timer1匹配输出通道0 | ||
P1[23] |
76 | I/O | P1[23]:GPIO口 |
I | USB_RX_DP1:USB端口1D+数据接收(OTG收发器) | ||
O | PWM1[4]:脉宽调制器1输出4 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
P1[24] |
78 | I/O | P1[24]:GPIO口 |
I | USB_RX_DM1:USB端口1D-数据接收(OTG收发器) | ||
O | PWM1[5]:脉宽调制器1输出5 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
P1[25] |
80 | I/O | P1[25]:GPIO口 |
O | USB_LS1:USB端口1低速状态(OTG收发器) | ||
O | USB_HSTEN1:USB端口1主机使能状态 | ||
O | MAT1[1]:Timer1匹配输出通道1 | ||
P1[26] |
82 | I/O | P1[26]:GPIO口 |
O | USB_SSPND1:USB端口1总线悬挂状态(OTG收发器) | ||
O | PWM1[6]:脉宽调制器1输出6 | ||
I | CAP0[0]:Timer0捕获输入通道0 | ||
P1[27] |
88 | I/O | P1[27]:GPIO口 |
I | USB_INT1:USB端口1OTG ATX中断(OTG收发器) | ||
I | USB_OVRCR1:USB端口1过流状态 | ||
I | CAP0[1]:Timer0捕获输入通道1 | ||
P1[28] |
90 | I/O | P1[28]:GPIO口 |
I/O | USB_SCL1:USB端口1I2C串行时钟(OTG收发器) | ||
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
O | MAT0[0]:Timer0匹配输出通道0 | ||
P1[29] |
92 | I/O | P1[29]:GPIO口 |
I/O | USB_SDA1:USB端口1I2C串行数据(OTG收发器) | ||
I | PCAP1[1]:脉宽调制器1捕获输入通道1 | ||
O | MAT0[1]:Timer0匹配输出通道1 | ||
P1[30] |
42 | I/O | P1[30]:GPIO口 |
I | USB_PWRD2:USB端口2电源状态 | ||
I | VBUS:指示USB总线当前电源。注意:当USB复位时这个信号必须为高电平 | ||
I | AD0[4]:A/D转换器0输入4 | ||
P1[31] |
40 | I/O | P1[31]:GPIO口 |
I | USB_OVRCR2:USB端口2过流状态 | ||
I/O | SCK1:SSP1串行时钟 | ||
I | AD0[5]:A/D转换器0输入5 |
3. P2口: P2口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P2口管脚描述如表4.3所示。
表4.3 LPC2400的P2口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P2[0] |
154 | I/O | P2[0]:GPIO口 |
O | PWM1[1]:脉宽调制器1输出1 | ||
O | TXD1:UART1发送输出端 | ||
O | TRACECLK/LCDPWR:跟踪时钟/LCD面板电源使能 | ||
P2[1] |
152 | I/O | P2[1]:GPIO口 |
O | PWM1[2]:脉宽调制器1输出2 | ||
I | RXD1:UART1接收输入端 | ||
O | PIPESTAT0/LCDLE:流水线状态位0/LCD行结束信号 | ||
P2[2] |
150 | I/O | P2[2]:GPIO口 |
O | PWM1[3]:脉宽调制器1输出3 | ||
I | CTS1:UART1清除发送输入端 | ||
O | PIPESTAT1/LCDCP:流水线状态位1/LCD面板时钟 | ||
P2[3] |
144 | I/O | P2[3]:GPIO口 |
O | PWM1[4]:脉宽调制器1输出4 | ||
I | DCD1:UART1数据载波检测输入端 | ||
O | PIPESTAT2/LCDFP:流水线状态位2/LCD帧脉冲(STN)垂直同步脉冲(TFT) | ||
P2[4] |
142 | I/O | P2[4]:GPIO口 |
O | PWM1[5]:脉宽调制器1输出5 | ||
I | DSR1:UART1数据设置就绪端 | ||
O | TRACESYNC/LCDAC:跟踪同步/LCD交流斜线驱动(STN)数据使能输出(TFT) | ||
P2[5] |
140 | I/O | P2[5]:GPIO口 |
O | PWM1[6]:脉宽调制器1输出6 | ||
O | DTR1:UART1数据终止就绪端 | ||
O | TRACEPKT0/LCDAC:跟踪分组位0/LCD行同步脉冲(STN)水平同步脉冲(TFT) | ||
P2[6] |
138 | I/O | P2[6]:GPIO口 |
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
I | RI1:UART1响铃指示输入端 | ||
O | TRACEPKT1/LCD[0]/LCD[4]:跟踪分组位1/LCD数据 | ||
P2[7] |
136 | I/O | P2[7]:GPIO口 |
I | RD2:CAN2接收输入 | ||
O | RTS1:UART1请求发送输出端 | ||
O | TRACEPKT2/LCD[1]/LCD[5]:跟踪分组位1/LCD数据 | ||
P2[8] |
134 | I/O | P2[8]:GPIO口 |
O | TD2:CAN2发送输出 | ||
O | TXD2:UART2接收输入端 | ||
O | TRACEPKT3/LCD[2]/LCD[6]:跟踪分组位3/LCD数据 | ||
P2[9] |
132 | I/O | P2[9]:GPIO口 |
O | USB_CONNECT1:USB1软连接控制 | ||
I | RXD2:UART2接收输入 | ||
I | EXTINT0/LCD[3]/LCD[7]:外部触发中断输入/LCD数据 | ||
P2[10] |
110 | I/O | P2[10]:GPIO口 |
I | EINT0:外部中断0输入 | ||
P2[11] |
108 | I/O | P2[11]:GPIO口 |
I/O | EINT1:外部中断1输入/LCDCLKIN:LCD时钟 | ||
I/O | MCIDAT1:SD/MMC接口数据线1 | ||
I/O | I2STX_CLK:I2S传输时钟。 | ||
P2[12] |
106 | I/O | P2[12]:GPIO口 |
I/O | EINT2:外部中断2输入/输出:LCD[4]/LCD[3]/LCD[8]/LCD[18] | ||
I/O | MCIDAT2:SD/MMC接口数据线2 | ||
I/O | I2STX_WS:I2S传输字选择。 | ||
P2[13] |
102 | I/O | P2[13]:GPIO口 |
I/O | EINT3:外部中断3输入/输出:LCD[5]/LCD[9]/LCD[19] | ||
I/O | MCIDAT3:SD/MMC接口数据线3 | ||
I/O | I2STX_SDA:I2S传输数据。 | ||
P2[14] |
91 | I/O | P2[14]:GPIO口 |
O | CS2:低电平有效片选信号2 | ||
I | CAP2[0]:Tmer2捕获输入通道0 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P2[15] |
99 | I/O | P2[15]:GPIO口 |
O | CS3:低电平有效片选信号3 | ||
I | CAP2[1]:Tmer2捕获输入通道1 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P2[16] |
87 | I/O | P2[16]:GPIO口 |
O | CAS:低电平有效SDRAM列地址选择 | ||
P2[17] |
95 | I/O | P2[17]:GPIO口 |
O | RAS:低电平有效SDRAM行地址选择 | ||
P2[18] |
59 | I/O | P2[18]:GPIO口 |
O | CLKOUT0:SDRAM时钟0 | ||
P2[19] |
67 | I/O | P2[19]:GPIO口 |
O | CLKOUT1:SDRAM时钟1 | ||
P2[20] |
73 | I/O | P2[20]:GPIO口 |
O | DYCS0:SDRAM片选信号0 | ||
P2[21] |
81 | I/O | P2[21]:GPIO口 |
O | DYCS1:SDRAM片选信号1 | ||
P2[22] |
85 | I/O | P2[22]:GPIO口 |
O | DYCS2:SDRAM片选信号2 | ||
I | CAP3[0]:Timer3捕获输入通道0 | ||
I/O | SCK0:SSP0串行时钟 | ||
P2[23] |
64 | I/O | P2[23]:GPIO口 |
O | DYCS3:SDRAM片选信号3 | ||
I | CAP3[1]:Timer3捕获输入通道1 | ||
I/O | SSEL0:SSP0从机选择 | ||
P2[24] |
53 | I/O | P2[24]:GPIO口 |
O | CKEOUT0:SDRAM时钟使能信号0 | ||
P2[25] |
54 | I/O | P2[25]:GPIO口 |
O | CKEOUT1:SDRAM时钟使能信号1 | ||
P2[26] |
57 | I/O | P2[26]:GPIO口 |
O | CKEOUT2:SDRAM时钟使能信号2 | ||
O | MAT3[0]:Tmer3匹配输出通道0 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
P2[27] |
47 | I/O | P2[27]:GPIO口 |
O | CKEOUT3:SDRAM时钟使能信号3 | ||
O | MAT3[1]:Tmer3匹配输出通道1 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
P2[28] |
49 | I/O | P2[28]:GPIO口 |
O | DQMOUT0:用于SDRAM和静态设备的数据掩码0 | ||
P2[29] |
43 | I/O | P2[29]:GPIO口 |
O | DQMOUT1:用于SDRAM和静态设备的数据掩码1 | ||
P2[30] |
31 | I/O | P2[30]:GPIO口 |
O | DQMOUT2:用于SDRAM和静态设备的数据掩码2 | ||
O | MAT3[2]:Tmer3匹配输出通道2 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
P2[31] |
39 | I/O | P2[31]:GPIO口 |
O | DQMOUT3:用于SDRAM和静态设备的数据掩码3 | ||
O | MAT3[3]:Tmer3匹配输出通道3 | ||
I/O | SCL2:I2C2时钟输入/输出 |
4. P3口: P3口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P3口管脚描述如表4.4所示。
表4.4 LPC2400的P3口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P3[0] |
197 | I/O | P3[0]:GPIO口 |
I/O | D0:外部存储器数据线0 | ||
P3[1] |
201 | I/O | P3[1]:GPIO口 |
I/O | D1:外部存储器数据线1 | ||
P3[2] |
207 | I/O | P3[2]:GPIO口 |
I/O | D2:外部存储器数据线2 | ||
P3[3] |
3 | I/O | P3[3]:GPIO口 |
I/O | D3:外部存储器数据线3 | ||
P3[4] |
13 | I/O | P3[4]:GPIO口 |
I/O | D4:外部存储器数据线4 | ||
P3[5] |
17 | I/O | P3[5]:GPIO口 |
I/O | D5:外部存储器数据线5 | ||
P3[6] |
23 | I/O | P3[6]:GPIO口 |
I/O | D6:外部存储器数据线6 | ||
P3[7] |
27 | I/O | P3[7]:GPIO口 |
I/O | D7:外部存储器数据线7 | ||
P3[8] |
191 | I/O | P3[8]:GPIO口 |
I/O | D8:外部存储器数据线8 | ||
P3[9] |
199 | I/O | P3[9]:GPIO口 |
I/O | D9:外部存储器数据线9 | ||
P3[10] |
205 | I/O | P3[10]:GPIO口 |
I/O | D10:外部存储器数据线10 | ||
P3[11] |
208 | I/O | P3[11]:GPIO口 |
I/O | D11:外部存储器数据线11 | ||
P3[12] |
1 | I/O | P3[12]:GPIO口 |
I/O | D12:外部存储器数据线12 | ||
P3[13] |
7 | I/O | P3[13]:GPIO口 |
I/O | D13:外部存储器数据线13 | ||
P3[14] |
21 | I/O | P3[14]:GPIO口 |
I/O | D14:外部存储器数据线14 | ||
P3[15] |
28 | I/O | P3[15]:GPIO口 |
I/O | D15:外部存储器数据线15 | ||
P3[16] |
137 | I/O | P3[16]:GPIO口 |
I/O | D16:外部存储器数据线16 | ||
O | PWM0[1]:脉宽调制器0输出1 | ||
O | TXD1:UART1发送输出端 | ||
P3[17] |
143 | I/O | P3[17]:GPIO口 |
I/O | D17:外部存储器数据线17 | ||
O | PWM0[2]:脉宽调制器0输出2 | ||
I | RXD1:UART1接收输入端 | ||
P3[18] |
151 | I/O | P3[18]:GPIO口 |
I/O | D18:外部存储器数据线18 | ||
O | PWM0[3]:脉宽调制器0输出3 | ||
I | CTS1:UART1清除发送输入端 | ||
P3[19] |
161 | I/O | P3[19]:GPIO口 |
I/O | D19:外部存储器数据线19 | ||
O | PWM0[4]:脉宽调制器0输出4 | ||
I | DCD1:UART1数据载波检测输入端 | ||
P3[20] |
167 | I/O | P3[20]:GPIO口 |
I/O | D20:外部存储器数据线20 | ||
O | PWM0[5]:脉宽调制器0输出5 | ||
I | DSR1:UART1数据设置就绪端 | ||
P3[21] |
175 | I/O | P3[21]:GPIO口 |
I/O | D21:外部存储器数据线21 | ||
O | PWM0[6]:脉宽调制器0输出6 | ||
O | DTR1:UART1数据终端准备就绪输出端 | ||
P3[22] |
195 | I/O | P3[22]:GPIO口 |
I/O | D22:外部存储器数据线22 | ||
I | PCAP0[0]:脉宽调制器0捕获输入通道0 | ||
I | RI1:UART1响铃指示输入端 | ||
P3[23] |
65 | I/O | P3[23]:GPIO口 |
I/O | D23:外部存储器数据线23 | ||
I | CAP0[0]:Timer0捕获输入通道0 | ||
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
P3[24] |
58 | I/O | P3[24]:GPIO口 |
I/O | D24:外部存储器数据线24 | ||
I | CAP0[1]:Timer0捕获输入通道1 | ||
O | PWM1[1]:脉宽调制器1输出1 | ||
P3[25] |
56 | I/O | P3[25]:GPIO口 |
I/O | D25:外部存储器数据线25 | ||
O | MAT0[0]:Tmer0匹配输出通道0 | ||
O | PWM1[2]:脉宽调制器1输出2 | ||
P3[26] |
55 | I/O | P3[26]:GPIO口 |
I/O | D26:外部存储器数据线26 | ||
O | MAT0[1]:Tmer0匹配输出通道1 | ||
O | PWM1[3]:脉宽调制器1输出3 | ||
P3[27] |
203 | I/O | P3[27]:GPIO口 |
I/O | D27:外部存储器数据线27 | ||
I | CAP1[0]:Timer1捕获输入通道0 | ||
O | PWM1[4]:脉宽调制器1输出4 | ||
P3[28] |
5 | I/O | P3[28]:GPIO口 |
I/O | D28:外部存储器数据线28 | ||
I | CAP1[1]:Timer1捕获输入通道1 | ||
O | PWM1[5]:脉宽调制器1输出5 | ||
P3[29] |
11 | I/O | P3[29]:GPIO口 |
I/O | D29:外部存储器数据线29 | ||
O | MAT1[0]:Tmer1匹配输出通道0 | ||
O | PWM1[6]:脉宽调制器1输出6 | ||
P3[30] |
19 | I/O | P3[30]:GPIO口 |
I/O | D30:外部存储器数据线30 | ||
O | MAT1[1]:Tmer1匹配输出通道1 | ||
O | RTS1:UART1请求发送输出端 | ||
P3[31] |
25 | I/O | P3[31]:GPIO口 |
I/O | D31:外部存储器数据线31 | ||
O | MAT1[2]:Tmer1匹配输出通道2 |
5. P4口: P4口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P4口管脚描述如表4.5所示。
表4.5 LPC2400的P4口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P4[0] |
75 | I/O | P4[0]:GPIO口 |
I/O | A0:外部存储器地址线0 | ||
P4[1] |
79 | I/O | P4[1]:GPIO口 |
I/O | A1:外部存储器地址线1 | ||
P4[2] |
83 | I/O | P4[2]:GPIO口 |
I/O | A2:外部存储器地址线2 | ||
P4[3] |
97 | I/O | P4[3]:GPIO口 |
I/O | A3:外部存储器地址线3 | ||
P4[4] |
103 | I/O | P4[4]:GPIO口 |
I/O | A4:外部存储器地址线4 | ||
P4[5] |
107 | I/O | P4[5]:GPIO口 |
I/O | A5:外部存储器地址线5 | ||
P4[6] |
113 | I/O | P4[6]:GPIO口 |
I/O | A6:外部存储器地址线6 | ||
P4[7] |
121 | I/O | P4[7]:GPIO口 |
I/O | A7:外部存储器地址线7 | ||
P4[8] |
1271 | I/O | P4[8]:GPIO口 |
I/O | A8:外部存储器地址线8 | ||
P4[9] |
131 | I/O | P4[9]:GPIO口 |
I/O | A9:外部存储器地址线9 | ||
P4[10] |
135 | I/O | P4[10]:GPIO口 |
I/O | A10:外部存储器地址线10 | ||
P4[11] |
145 | I/O | P4[11]:GPIO口 |
I/O | A11:外部存储器地址线11 | ||
P4[12] |
149 | I/O | P4[12]:GPIO口 |
I/O | A12:外部存储器地址线12 | ||
P4[13] |
155 | I/O | P4[13]:GPIO口 |
I/O | A13:外部存储器地址线13 | ||
P4[14] |
159 | I/O | P4[14]:GPIO口 |
I/O | A14:外部存储器地址线14 | ||
P4[15] |
173 | I/O | P4[15]:GPIO口 |
I/O | A15:外部存储器地址线15 | ||
P4[16] |
101 | I/O | P4[16]:GPIO口 |
I/O | A16:外部存储器地址线16 | ||
P4[17] |
104 | I/O | P4[17]:GPIO口 |
I/O | A17:外部存储器地址线17 | ||
P4[18] |
105 | I/O | P4[18]:GPIO口 |
I/O | A18:外部存储器地址线18 | ||
P4[19] |
111 | I/O | P4[19]:GPIO口 |
I/O | A19:外部存储器地址线19 | ||
P4[20] |
109 | I/O | P4[20]:GPIO口 |
I/O | A20:外部存储器地址线20 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
I/O | SCK1:SSP1串行时钟 | ||
P4[21] |
115 | I/O | P4[21]:GPIO口 |
I/O | A21:外部存储器地址线21 | ||
I/O | SCL2:I2C2时钟输入/输出 | ||
I/O | SSEL1:SSP1从机选择 | ||
P4[22] |
123 | I/O | P4[22]:GPIO口 |
I/O | A22:外部存储器地址线22 | ||
O | TXD2:UART2发送输出端 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
P4[23] |
129 | I/O | P4[23]:GPIO口 |
I/O | A23:外部存储器地址线23 | ||
I | RXD2:UART2接收输入端 | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
P4[24] |
183 | I/O | P4[24]:GPIO口 |
O | OE:低电平有效输出使能信号 | ||
P4[25] |
179 | I/O | P4[25]:GPIO口 |
O | WE:低电平有效写使能信号 | ||
P4[26] |
119 | I/O | P4[26]:GPIO口 |
O | BLS0:低电平有效字节定位选择信号0 | ||
P4[27] |
139 | I/O | P4[27]:GPIO口 |
O | BLS1:低电平有效字节定位选择信号1 | ||
P4[28] |
170 | I/O | P4[28]:GPIO口 |
O | BLS2:低电平有效字节定位选择信号2 | ||
O | MAT2[0]/LCD[6]/LCD[10]/LCD[2]:Timer2匹配输出通道0/LCD数据 | ||
O | TXD3:UART3发送输出端 | ||
P4[29] |
176 | I/O | P4[29]:GPIO口 |
O | BLS3:低电平有效字节定位选择信号3 | ||
O | MAT2[1]/LCD[7]/LCD[11]/LCD[3]:Timer2匹配输出通道1/LCD数据 | ||
I | RXD3:UART3接收输入端 | ||
P4[30] |
187 | I/O | P4[30]:GPIO口 |
O | CS0:低电平有效片选信号0 | ||
P4[31] |
193 | I/O | P4[31]:GPIO口 |
O | CS1:低电平有效片选信号1 |
6. 电源、复位、晶振及其它管脚的描述如表4.6所示。
表4.6 LPC2400的其它管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
ALARM | 37 | O | RTC实时时钟控制输出。这是一个1.8V引脚,当RTC产生报警信号时此引脚变为高电平。 |
USB_D-2 | 52 | I/O | USB端口2双向D-线 |
DBGEN | 9 | I | JTAG接口控制信号,也用于边界扫描 |
TDO | 2 | O | JTAG接口测试数据输出 |
TDI | 4 | I | JTAG接口测试数据输入 |
TMS | 6 | I | JTAG接口测试模式选择 |
TRST | 8 | I | JTAG接口测试复位,低电平有效 |
TCK | 10 | I | JTAG接口测试时钟 |
RTCK | 206 | I/O | JTAG接口控制信号,当此引脚低电平时使能ETM引脚(P2[9:0]),用于复位后操作跟踪端口 |
RSTOUT | 29 | O | 这是个1.8V引脚,当此引脚低电平时表示LPC2478处于复位状态 |
RESET | 35 | I | 外部复位输入,低电平有效。该引脚具有迟滞作用的TTL电平,能承受5V电压,当此引脚低电平时器件复位,I/O口和外围功能进入默认状态,处理器从地址0开始执行程序 |
XTAL1 | 44 | I | 振荡器电路和内部时钟发生电路输入 |
XTAL2 | 46 | O | 振荡放大器输出 |
RTCX1 | 34 | I | RTC振荡器电路输入 |
RTCX2 | 36 | O | RTC振荡器电路输出 |
VSSIO | 33,63,77,93,114,133,148,169,189,200 | I | 地:数字IO脚的0V电压参考点 |
VSSCORE | 32,84,172 | I | 地:处理器内核的0V电压参考点 |
VSSA | 22 | I | 模拟地:0V电压参考点,与VSS电压相同,为了降低噪声和出错几率,两者应当隔离 |
VDD(3V3) | 15,60,71,89,112,125,146,165,181,198 | I | 3.3V供应电压:I/O口电源供应电压 |
NC | 30,117,141 | I | 未连接引脚 |
VDD(DCDC)(3V3) | 26,86,174 | I | 3.3V直流到直流转换供应电压:为片内DC-to-DC转换器提供电源 |
VDDA | 20 | I | 模拟3.3V供应电压:为DAC和ADC供电,与VDD(3V3)电压相同,为了降低噪声和出错几率,两者应当隔离 |
VREF | 24 | I | ADC参考电压:与VDD(3V3)电压相同,为了降低噪声和出错几率,两者应当隔离,为ADC和DAC提供参考电压 |
VBAT | 38 | I | RTC电源供应:3.3V,为RTC提供电源 |
注:1. 本引脚定义表以LPC2478为准,LPC2470与此相同,LPC2468没有引脚定义的LCD部分。
2. 类型表示引脚信号方向:I/O为输入/输出,I为输入,O为输出。
4.2.2 引脚连接模块
从表4.1~表4.6可以看到,LPC2400系列芯片的绝大部分引脚是复用的,每根引脚都有可能用于不同的外设功能。引脚具体用于什么外设功能是由引脚连接模块进行配置来实现的。当引脚选择了一个功能时,则其它功能无效。
在使用外设时,应当在激活外设以及使能任何相关的中断之前,将外设连接到相应的引脚上。否则,即使使用引脚连接模块激活外设,此激活也是无效的。
引脚连接模块共有21个寄存器,包括11个引脚功能选择寄存器和10个引脚模式寄存器。
1. 引脚功能选择寄存器(PINSEL0~PINSEL10)
引脚功能选择寄存器用于控制每个引脚的功能,每个寄存器32位,每2个bit用于控制1个引脚功能选择。以PINSEL0寄存器为例,寄存器的[1:0]位用于控制P0[0]引脚,[3:2]位用于控制P0[1]引脚,[31:30]位用于控制P0[15]引脚。而PINSEL1寄存器的[1:0]位用于控制P0[16]引脚,[3:2]位用于控制P0[17]引脚,[31:30]位用于控制P0[31]引脚。其余依次类推。
PINSEL0~PINSEL9寄存器,每两个寄存器用于一个端口组:PINSEL0寄存器用于P0口的[15:0]引脚,PINSEL1寄存器用于P0口的[31:30]引脚;PINSEL2寄存器用于P1口的[15:0]引脚,PINSEL3寄存器用于P1口的[31:30]引脚;PINSEL4寄存器用于P2口的[15:0]引脚,PINSEL5寄存器用于P2口的[31:30]引脚;PINSEL6寄存器用于P3口的[15:0]引脚,PINSEL7寄存器用于P3口的[31:30]引脚;PINSEL8寄存器用于P4口的[15:0]引脚,PINSEL9寄存器用于P4口的[31:30]引脚。
每一对比特设置引脚功能的定义如表4.7所示。
表4.7 引脚功能选择寄存器位
PINSEL0~PINSEL9值 | 功能 | 复位值 |
00 | 主功能(缺省),一般为GPIO口 | 00 |
01 | 第一备用功能 | |
10 | 第二备用功能 | |
11 | 第三备用功能 |
每个引脚默认为GPIO口,通过设置PINSEL的值来定义其引脚功能。以P0[0]脚为例,当PINSEL0寄存器的[1:0]位为00时,引脚功能为GPIO口;为01时,引脚功能为CAN1接收器输入;为10时,引脚功能为UART3发送输出端;为11时,引脚功能为I2C1数据输入/输出。
每个引脚的具体定义方法参见表4.1~表4.6。表格中的引脚功能按PINSEL值排列。某些引脚只有两种功能,此时只使用PINSEL值00和01,值10和11保留。
PINSEL10寄存器用于控制ETM接口引脚。该寄存器只使用了位3,其余位均保留。当第3位为0时,关闭ETM接口功能;为1时启用ETM跟踪接口功能,此时无论PINSEL4怎么定义,P2[0]~P2[8]脚均用于ETM跟踪功能。
引脚功能被选择为GPIO时,引脚的方向控制由GPIO方向寄存器IODIR控制。对于其它功能,引脚的方向是由引脚功能控制的。
2. 引脚模式寄存器(PINMODE0~PINMODE9)
引脚模式寄存器PINMODE为所有的GPIO端口控制片内上拉/下拉电阻特性。当使用片内上拉或下接电阻时,若引脚信号不确定,使用上拉时为高电平;而下拉时拉为低电平。
与PNSEL寄存器一样,PINMODE寄存器每2个bit控制1个引脚。每两个寄存器控制一个端口组。
PINMOD寄存器取值如表4.8所示。
表4.8 引脚模式寄存器位
PINMODE0~PINMODE9值 | 功能 | 复位值 |
00 | 使能引脚片内上拉电阻 | 00 |
01 | 保留 | |
10 | 既不使用上拉也不使用下拉 | |
11 | 使能引脚片内下拉电阻 |
引脚连接模块的寄存器总表如表4.9所示。
表4.9 引脚控制模块寄存器列表
寄存器名 | 描述 | 访问 | 复位值 | 地址 |
PINSEL0 | 引脚功能选择寄存器0 | 读/写 | 0x0000 0000 | 0xE002 C000 |
PINSEL1 | 引脚功能选择寄存器1 | 读/写 | 0x0000 0000 | 0xE002 C004 |
PINSEL2 | 引脚功能选择寄存器2 | 读/写 | 0x0000 0000 | 0xE002 C008 |
PINSEL3 | 引脚功能选择寄存器3 | 读/写 | 0x0000 0000 | 0xE002 C00C |
PINSEL4 | 引脚功能选择寄存器4 | 读/写 | 0x0000 0000 | 0xE002 C010 |
PINSEL5 | 引脚功能选择寄存器5 | 读/写 | 0x0000 0000 | 0xE002 C014 |
PINSEL6 | 引脚功能选择寄存器6 | 读/写 | 0x0000 0000 | 0xE002 C018 |
PINSEL7 | 引脚功能选择寄存器7 | 读/写 | 0x0000 0000 | 0xE002 C01C |
PINSEL8 | 引脚功能选择寄存器8 | 读/写 | 0x0000 0000 | 0xE002 C020 |
PINSEL9 | 引脚功能选择寄存器9 | 读/写 | 0x0000 0000 | 0xE002 C024 |
PINSEL10 | 引脚功能选择寄存器10 | 读/写 | 0x0000 0000 | 0xE002 C028 |
PINMODE0 | 引脚模式寄存器0 | 读/写 | 0x0000 0000 | 0xE002 C040 |
PINMODE1 | 引脚模式寄存器1 | 读/写 | 0x0000 0000 | 0xE002 C044 |
PINMODE2 | 引脚模式寄存器2 | 读/写 | 0x0000 0000 | 0xE002 C048 |
PINMODE3 | 引脚模式寄存器3 | 读/写 | 0x0000 0000 | 0xE002 C04C |
PINMODE4 | 引脚模式寄存器4 | 读/写 | 0x0000 0000 | 0xE002 C050 |
PINMODE5 | 引脚模式寄存器5 | 读/写 | 0x0000 0000 | 0xE002 C054 |
PINMODE6 | 引脚模式寄存器6 | 读/写 | 0x0000 0000 | 0xE002 C058 |
PINMODE7 | 引脚模式寄存器7 | 读/写 | 0x0000 0000 | 0xE002 C05C |
PINMODE8 | 引脚模式寄存器8 | 读/写 | 0x0000 0000 | 0xE002 C060 |
PINMODE9 | 引脚模式寄存器9 | 读/写 | 0x0000 0000 | 0xE002 C064 |
4.2.3 引脚连接模块的使用举例
LPC2400系列芯片外设功能在使用前必须先设置其引脚功能。引脚功能是通过对引脚连接模块编程来实现的。
例4.1:使用串口UART0
串口UART0只使用TXD0和RXD0两根引脚来进行数据的串行发送和接收,使用时需将对应的两根引脚P0[2]和P0[3]设置成TXD0和RXD0功能。查表4.1可知,两根引脚的对应PINSEL值均为01,因此写入PINSEL0寄存器的值为0x00000050。
相应程序行为:
PINSEL0 = 0x00000050;
或
PINSEL0 = 0x05<<4;
注意,由于PINSEL是可读写的寄存器,上述写法会使其它引脚的功能回到初始化默认配置。为了不影响其它引脚的功能配置,实用中更好的办法是:先读取寄存器值,然后进行逻辑与和逻辑或操作,再回写到寄存器。
PINSEL0 = (PINSEL0 &0xFFFFFF0F) | (0x05<<4);
其余的引脚外设功能均可以采用类似方法进行操作。
例4.2:启动代码中的相关部分
启动代码负责对芯片复位后的硬件功能进行初始化。芯片复位时,各PINSEL寄存器会自动设置为默认值,所以复位后芯片引脚的功能是确定的。
如果启动以后,硬件系统各外设功能使用情况比较固定,可以将对应的引脚功能设置写入启动代码以加快启动速度。否则,可以在启动时将所有引脚都配置成GPIO端口,具体使用某部分外设时再对相关引脚进行初始化。
……
PINSEL0 = 0x00000000;
PINSEL1 = 0x00000000;
PINSEL2 = 0x00000000;
PINSEL3 = 0x00000000;
PINSEL4 = 0x00000000;
PINSEL5 = 0x00000000;
PINSEL6 = 0x00000000;
PINSEL7 = 0x00000000;
PINSEL8 = 0x00000000;
PINSEL9 = 0x00000000;
PINSEL10 = 0x00000000;
……
4.3 存储器管理
LPC2400系列芯片集成了512KB的片内Flash存储器和64KB的静态SRAM(LPC2470没有片内Flash),其中Flash存储器可以用做代码和数据的固态存储。对Flash存储器的编程可以通过几种方法来实现:通过串口UART0进行的在系统编程(ISP),通过调用嵌入片内的固化代码进行的在应用编程(IAP)以及通过内置的JTAG接口编程。
SRAM支持8位、16位和32位访问。需要注意的是,SRAM控制器包含一个回写缓冲区,它用于防止CPU在连续的写操作时停止运行。回写缓冲区总是保存着软件发送到SRAM的最后1字节。数据只有在软件执行另外一次写操作时被写入SRAM。如果发生芯片复位,实际的SRAM内容将不会反映最近一次的写请求。任何在复位后检查SRAM内容的程序都必须注意这一点。
LPC2400系列芯片具备外部存储器接口,通过外部存储器控制器(EMC)可以扩展两组共8个Bank的存储器组(Static memory bank0 ~ bank3,Dynamic memory bank0 ~ bank3)。对于外扩的RAM存储器,使用ARM的LDR/STR指令即可进行数据的读写操作;而对于外扩的Flash(NOR)型,可以使用LDR指令读取数据,但是不能使用STR指令直接写数据,而是根据Flash芯片写操作时序进行控制,实现Flash的擦除编程。如果需要将程序代码烧写到扩展的Flash,则需要运行一个装载程序(Loader程序,一般由用户自行编写),然后由Loader程序对Flash存储器进行擦除和烧写。
4.3.1 存储器映射
ARM处理器共有4GB的寻址空间,LPC2400系列处理器将这个空间划分成了几个不同的存储器组。图4.3所示为复位后从用户角度所看到的系统存储器地址空间映射。
图4.3 LPC2400系统存储器映射
表4.10 LPC2400存储器使用和细节
地址范围 | 通用用途 | 地址范围细节 | 功能描述 |
0x0000 0000 - 0x3FFF FFFF | 片内非易失存储器和快速I/O | 0x0000 0000 – 0x0007 FFFF | Flash 存储器(512KB) |
0x3FFF C000 – 0x3FFF FFFF | 快速GPIO寄存器 | ||
0x4000 0000 - 0x7FFF FFFF | 片内存储器 | 0x4000 0000 – 0x4000 FFFF | RAM(64KB) |
0x7FE0 0000 – 0x7FE0 3FFF | 以太网RAM(16KB) | ||
0x7FD0 0000 – 0x7FD0 3FFF | USB RAM(16KB) | ||
0x8000 0000 - 0xDFFF FFFF
| 片外存储器 | 四个静态存储器bank,每个16MB | |
0x8000 0000 – 0x80FF FFFF | 静态存储器bank0 | ||
0x8100 0000 – 0x81FF FFFF | 静态存储器bank1 | ||
0x8200 0000 – 0x82FF FFFF | 静态存储器bank2 | ||
0x8300 0000 – 0x83FF FFFF | 静态存储器bank3 | ||
四个动态存储器bank,每个256MB | |||
0xA000 0000 – 0xAFFF FFFF | 动态存储器bank0 | ||
0xB000 0000 – 0x3FFF FFFF | 动态存储器bank1 | ||
0xC000 0000 – 0xCFFF FFFF | 动态存储器bank2 | ||
0xD000 0000 – 0xDFFF FFFF | 动态存储器bank3 | ||
0xE000 0000 - 0xEFFF FFFF | APB 外设 | 36个外设模块,每个16KB | |
0xF000 0000 - 0xFFFF FFFF | AHB 外设 |
|
1. 外设存储器映射
LPC2400系列处理器的外设根据内部总线分为AHB和APB外设两类。AHB外设和APB外设在存储空间里都占2MB的区域,可各自分配最多128个外设。每个外设空间的规格都为16KB。所有外设寄存器不管规格大小,都按照字地址进行分配(32位边界),且不管字还是半字寄存器都是一次性访问。例如,不可能对一个字寄存器的最高字节执行单独的读或写操作。
图4.4表示了AHB和APB外设占用存储空间示意图。
图4.4 外设存储器映射(AHB外设和APB外设)
图4.5所示为AHB外设占用存储空间示意图。其中0-4号外设分别分配给以太网控制器、GPDMA控制器、EMC控制器、USB控制器和LCD控制器,其它外设编号未使用。
图4.5 AHB外设存储器映射
表4.11所示为APB外设占用的存储空间示意图。
表4.11 APB外设存储器映射
APB 外设 | 基地址 | 外设名 |
0 | 0xE000 0000 | 看门狗 |
1 | 0xE000 4000 | 定时器0 |
2 | 0xE000 8000 | 定时器1 |
3 | 0xE000 C000 | UART0 |
4 | 0xE001 0000 | UART1 |
5 | 0xE001 4000 | PWM0 |
6 | 0xE001 8000 | PWM1 |
7 | 0xE001 C000 | I2C0 |
8 | 0xE002 0000 | SPI |
9 | 0xE002 4000 | 实时时钟RTC |
10 | 0xE002 8000 | GPIO |
11 | 0xE002 C000 | 引脚连接模块 |
12 | 0xE003 0000 | SSP1 |
13 | 0xE003 4000 | ADC |
14 | 0xE003 8000 | CAN接收滤波器RAM |
15 | 0xE003 C000 | CAN接收滤波器寄存器 |
16 | 0xE004 0000 | CAN通用寄存器 |
17 | 0xE004 4000 | CAN控制器1 |
18 | 0xE004 8000 | CAN控制器2 |
19 到 22 | 0xE004 C000 - 0xE005 8000 | 未使用 |
23 | 0xE005 C000 | I2C1 |
24 | 0xE006 0000 | 未使用 |
25 | 0xE006 4000 | 未使用 |
26 | 0xE006 8000 | SSP0 |
27 | 0xE006 C000 | DAC |
28 | 0xE007 0000 | 定时器2 |
29 | 0xE007 4000 | 定时器3 |
30 | 0xE007 8000 | UART2 |
31 | 0xE007 C000 | UART3 |
32 | 0xE008 0000 | I2C3 |
33 | 0xE008 4000 | 电池RAM |
34 | 0xE008 8000 | I2S |
35 | 0xE008 C000 | SD/MMC卡接口 |
36 到126 | 0xE009 0000 - 0xE01F BFFF | 未使用 |
127 | 0xE01F C000 | 系统控制模块 |
在对LPC2400系列芯片编程时,要注意不要对一个保留地址或未使用区域的地址进行寻址,否则LPC2400将产生一个数据中止异常。另外,对AHB或APB外设地址执行任何指令取指都会导致产生预取指中止异常。
2. 存储器重映射和boot ROM
存储器映射的一个基本概念是:每个存储器组在存储器映射中都有一个“物理上的”位置。它是一个地址范围,该范围内可写入程序代码,每一个存储器空间的容量都永久固定在同一个位置,这样就不需要将代码设计成在不同地址范围内运行。
因为ARM7处理器上的中断向量所处具体位置(地址0x0000 0000~0x0000 001C,见表4.12)的要求,Boot ROM和SRAM空间的一小部分空间需要重新映射,来实现在不同操作模式下对中断的不同使用。
表4.12 ARM 异常向量位置
地址 | 异常 |
0x0000 0000 | 复位 |
0x0000 0004 | 未定义指令 |
0x0000 0008 | 软件中断 |
0x0000 000C | 预取指中止(指令读取存储器出错) |
0x0000 0010 | 数据中止(数据访问存储器出错) |
0x0000 0014 | 保留 |
0x0000 0018 | IRQ |
0x0000 001C | FIQ |
为了与将来器件兼容,整个Boot ROM都被映射到片内存储器空间的顶端。在这种方式下,使用较大或较小的Flash模块都不需要改变Boot ROM(需要改变Boot装载程序自身的代码)的位置或改变Boot ROM中断向量的映射。除了中断向量之外的存储器空间都保持固定的位置。
存储器重新映射的部分允许在不同模式下处理中断。LPC2400共支持3种存储器映射模式,见表4.13。当处理器工作在用户Flash模式下时,不需要进行中断向量的重新映射,而在其它模式下则需要重新映射。它包括中断向量区(32 字节)和额外的32 字节,一共是64 字节。重新映射的代码位置与地址0x0000 0000~0x0000 003F 重叠,包含在SRAM、Flash 和Boot Block 中的向量必须包含跳转到实际中断处理程序的分支或者其它执行跳转到中断处理程序的转移指令。一个位于Flash 存储器中的典型用户程序可以将整个FIQ 处理程序放置在地址0x0000 001C 而不需要考虑存储器的边界。
表4.13 LPC2400存储器映射模式
模式 | 激活 | 用途 |
Boot装载程序模式 | 由任何复位硬件激活 | 在任何复位后都会执行Boot 装载程序。Boot ROM中断向量映射到存储器的底部以允许处理异常并在Boot 装载过程中使用中断。 |
用户Flash 模式 | 由Boot 代码软件激活 | 当在存储器中识别了一个有效的用户程序标识并且Boot 装载操作未被执行时,由Boot 装载程序激活。中断向量不作重新映射,它位于Flash 存储器的底部。 |
用户RAM 模式 | 由用户程序软件激活 | 由用户程序激活。中断向量重新映射到静态RAM 的底部。 |
选择这种配置有三个原因:
1) 使Flash 存储器中的FIQ处理程序不必考虑因为重新映射所导致的存储器边界问题;
2) 用来处理代码空间中段边界仲裁的SRAM 和Boot ROM向量的使用大大减少;
3) 为超过单字转移指令范围的跳转提供空间来保存常量。
重新映射的存储器组,包括Boot ROM和中断向量,除了重新映射的地址外,仍然继续出现在它们最初的位置。存储器重映射以后的存储器空间见图4.6所示。
图4.6已重新映射和可重新映射区域的低存储器空间
3. 存储器映射控制寄存器MEMMAP(Memory Mapping Control Register)
存储器映射控制寄存器用于改变从地址0x00000000开始的中断向量的映射。这允许运行在不同存储器空间中的代码对中断进行控制。MEMMAP是一个可读写寄存器,地址为0xE01FC040,功能为选择从Flash Boot Block、用户Flash 或RAM中读取ARM 中断向量。
表4.14 存储器映射控制寄存器MEMMAP
位 | 符号 | 功能描述 | 复位值 |
1:0 | MAP | 00:Boot 装载程序模式。中断向量从Boot Block 重新映射 01:用户Flash 模式。中断向量不重新映射,它位于Flash 中 10:用户RAM 模式。中断向量从静态RAM 重新映射 11:保留,不使用该选项 警告:不正确的设定会导致器件的错误操作 | 00 |
7:2 | 保留 |
| NA |
LPC2400的MAP位的硬件复位值为00。Boot装载程序会将用户看到的复位值更改,该程序总是在复位后立即运行。
存储器映射控制寄存器MEMMAP只从处理ARM异常(中断)必需的3 个数据源(FLASH中断向量、SRAM中断向量和Boot ROM中断向量,每个64 字节)中选择一个使用。
每当产生一个软件中断请求,ARM内核就从0x0000 0008 处取出32 位数据(见表4.12,“ARM异常向量位置”)。这就意味着当MEMMAP[1:0]=10(用户RAM模式)时,从0x0000 0008的读数/取指是对0x4000 0008单元进行操作。当MEMMAP[1:0]=00(Boot装载程序模式)时,从0x0000 0008的读数/取指是对0x7FFF E008 单元的数据进行操作(Boot ROM从片内Flash存储器重新映射)。
4.3.2 存储器加速模块
当LPC2400 在运行Flash存储器内的代码时,器件内部的存储器加速模块(MAM,Memory Accelerator Module)最大限度地提高了ARM处理器的性能,而此只需要使用一个简单的Flash组就可实现了。
1.操作
存储器加速模块(MAM)可以将需要的下一个ARM 指令锁存,以防止CPU 取指暂停。与以前的其它器件使用2个Flash组相比,LPC2400只使用一组Flash 存储器。这个Flash组包含3 个128位的缓冲区:预取指缓冲区、分支跟踪缓冲区和数据缓冲区。
当预取指缓冲区和分支跟踪缓冲区不能满足一次指令取指的需要,并且预取指还没有启动时,ARM在启动128位行的取指时暂停。如果预取指已经启动但还未完成,则ARM暂停的时间会更短一些。预取指在Flash结束前面的访问后立即启动,除非被数据访问中止。
预取指行被Flash 模块锁存,但MAM 不能在预取指缓冲区中捕获该行,直到ARM 内核给出预取指开始的地址。如果内核给出的地址与预取指地址不同,则预取指行丢弃。
每个预取指缓冲区和分支跟踪缓冲区包含4个32位ARM指令或8个16位Thumb指令。在连续执行代码时,通常预取指缓冲区包含当前指令和含有该指令的整个Flash 行。
分支和其它程序流的变化导致前面所讲述的连续指令取指出现中断。分支跟踪缓冲区捕获发生非连续中断的行。如果相同的分支再次出现,则从分支跟踪缓冲区内取出下条指令。当分支超出了预取指和分支跟踪缓冲区内容时,需要中止几个时钟来装载分支跟踪缓冲区。这样,不会再出现指令取指延时,直到一个新的和不同的分支出现。
2.MAM 结构
存储器加速器分成以下几个功能模块:1个Flash地址锁存和1个增量器功能用于预取指地址、1个128位的预取指缓冲区及其相关的地址锁存和比较器、1个128位的分支跟踪缓冲区及其相关的地址锁存和比较器。
图4.7 存储器加速器模块框图
3.MAM 的操作模式
MAM 定义了3种操作模式,用户可以在性能和可预测性之间进行选择:
1)MAM关闭。所有存储器请求都会导致Flash的读操作,无指令预取指。
2)MAM部分使能。如果数据可用,则从保持锁存区执行连续的指令访问。指令预取指使能。非连续的指令访问启动Flash读操作。这意味着所有的转移指令都会导致对存储器的取指。由于缓冲的数据访问时序很难预测并且非常依赖于所处的状况,因此所有数据操作都会导致Flash读操作。
3)MAM 完全使能。任何存储器请求(代码或数据),如果其值已经包含在其中一个保持锁存当中,那么从缓冲区执行该代码或数据的访问。指令预取指使能。Flash读操作用于指令的预取指和当前缓冲区所没有的代码或数据的访问。
在复位后,MAM 默认为禁止状态,软件可以随时将存储器访问加速打开或关闭。这样就可使大多数应用程序以最高速度运行,而某些要求更精确定时的功能可以较慢但更可预测的速度运行。
在启用MAM以后,Flash编程功能不受MAM的控制,而是作为一个独立的功能进行处理。Boot ROM扇区包含可作为应用程序的一部分调用的Flash 编程算法(即IAP 代码)和一个可对Flash 存储器进行串行编程的装载程序(即ISP 代码)。
4.寄存器描述
1)MAM控制寄存器-MAM Control Register(MAMCR - 0xE01FC000)。决定MAM的操作模式。两个配置位选择MAM的3种操作模式。在复位后,MAM功能被禁止。改变MAM操作模式会导致MAM所有的保持锁存内容无效,因此需要执行新的Flash读操作。
表4.15 存储器加速器模块控制寄存器(MAMCR)
MAMCR | 功能 | 描述 | 复位值 |
1:0 | MAM 模式控制 | 00-MAM 功能被禁止 01-MAM 功能部分使能 10-MAM 功能完全使能 11-保留 | 0 |
7:2 | 保留 | 保留 | NA |
2)MAM定时寄存器-MAM Timing Register(MAMTIM - 0xE01FC004)。决定Flash存储器取指所使用的时钟个数(1到7个处理器时钟),使用多少个cclk周期访问Flash存储器。这样可调整MAM时序使其匹配处理器操作频率。Flash访问时间可以从1到7个时钟,单个时钟的Flash访问实际上关闭了MAM,这种情况下可以选择MAM模式对功耗进行优化。
表4.16 存储器加速定时寄存器(MAMTIM)
MAMTIM | 功能 | 描述 | 复位值 |
2:0 |
MAM取指周期 | 000=0,保留 001=1,MAM 取指周期为1 个处理器时钟(cclk) 010=2,MAM 取指周期为2 个处理器时钟(cclk) 011=3,MAM 取指周期为3 个处理器时钟(cclk) 100=4,MAM 取指周期为4 个处理器时钟(cclk) 101=5,MAM 取指周期为5 个处理器时钟(cclk) 110=6,MAM 取指周期为6 个处理器时钟(cclk) 111=7,MAM 取指周期为7 个处理器时钟(cclk) 警告:不正确的设定会导致器件的错误操作! |
0x07 |
7:3 | 保留 | 保留 | NA |
5.MAM 使用注意事项
1)MAM 定时值问题
当改变MAM定时值时,必须先通过向MAMCR写入0来关闭MAM,然后将新值写入MAMTIM,最后,将需要的操作模式的对应值写入MAMCR,再次打开MAM。
对于低于20MHz的系统时钟,MAMTIM设定为1;对于20MHz到40MHz之间的系统时钟,建议将MAMTIM设定为2;而在高于40MHz的系统时钟下,建议使用3。
2)Flash 编程问题
在编程和擦除操作过程中不允许访问Flash存储器。如果在Flash模块忙时存储器请求访问Flash地址,MAM就必须强制CPU等待(这通过声明ARM7TDMI-S局部总线信号CLKEN来实现)。在某些情况下,代码执行的延迟会导致看门狗超时。用户必须注意到这种可能性,并采取措施来确保不会在编程或擦除Flash 存储器时出现非预期的看门狗复位,从而导致系统故障。
6.MAM使用举例
MAM功能在LPC2400的启动代码中实现,可以根据Fcclk的大小来自动设置MAM, (在target.c文件中)。首先要将MAM功能禁止,然后根据Fcclk的大小来设置MAM定时寄存器(通过条件编译实现,Fcclk的定义在target.h文件中),最后再使能MAM。
代码清单4.1
void TargetResetInit(void)
{ …
MAMCR = 0; // 禁止MAM 功能
#if Fcclk < 20000000
MAMTIM = 1; // 系统时钟低于20M,建议设置为1
#else
#if Fcclk < 40000000
MAMTIM = 2; // 系统时钟再20M~40M 之间,建议设置为2
#else
MAMTIM = 3; // 系统时钟高于40M,建议设置为3
#endif
MAMCR = 2; // 使能MAM
…}
4.3.3 外部存储器控制器
LPC2400的外部存储器控制器(EMC)是一个AMBA总线AHB从机模块,它支持多种不同结构的存储器,包括常用的异步静态存储器,如RAM、ROM和Flash等,也支持动态存储器,如单数据率SDRAM等。EMC模块可以同时支持多达8个单独配置的存储器组,其中静态存储器和动态存储器各4个Bank。静态存储器组由片选引脚CS0~CS3选中,每个组存储容量16MB,支持RAM、ROM、Flash和其它一些外部I/O部件,支持8位、16位和32位数据宽度。动态存储器组由片选引脚DYCS0~DYCS3选中,每个组存储容量256MB,支持单数据率SDRAM,支持16位和32位数据宽度,刷新模式可由软件控制。
1.EMC功能框图和引脚信号
图4.8 EMC功能框图
表4.17 外部存储器控制器引脚描述
引脚名称 | 类 型 | 复位值 | 描 述 |
A[23:0] | 输出 | 0x0000 0000 | 外部存储器地址线。SDRAM存储器只使用其中的位[14:0] |
D[31:0] | 输入/输出 | 0x0000 0000 | 外部存储器数据线 |
OE | 输出 | 1 | 静态存储器输出使能信号,低电平有效 |
BLS[3:0] | 输出 | 0x0F | 静态存储器字节定位选择信号,低电平有效 |
WE | 输出 | 1 | 写使能信号,低电平有效 |
CS[3:0] | 输出 | 0x0F | 静态存储器片选信号,低电平有效 |
DYCS[3:0] | 输出 | 0x0F | 动态存储器片选信号,低电平有效 |
CAS | 输出 | 1 | 动态存储器列地址选通信号,低电平有效 |
RAS | 输出 | 1 | 动态存储器行地址选通信号,低电平有效 |
CLKOUT[1:0] | 输出 | 来自CCLK | SDRAM时钟 |
CKEOUT[3:0] | 输出 | 0x0F | SDRAM时钟使能 |
DQMOUT[3:0] | 输出 | 0x0F | SDRAM输出掩码。也可用于静态存储器 |
2.外部存储器接口
外部存储器接口取决于存储器组的数据宽度(32位、16位或8位,由EMCStaticConfig寄存器的MW位选择)。如果一个存储器组被配置为32位宽度,地址线A0和A1可以用做非地址线;如果存储器组配置为16为宽度,则不需要A0;8位宽的存储器组则需要使用A0。通过引脚连接模块寄存器可以实现A0和A1的地址或非地址功能。
图4.9和图4.10所示为典型的32位总线宽度的存储器组的外部存储器接口硬件连接方式,其中符号“a_b”表示数据总线的最高位地址线,符号“a_m”表示使用外部存储器接口的存储器芯片的最高位地址线。
图4.9中,一个32位宽度的存储器组由4个8位存储器芯片扩展而成,因此这4个芯片用同一个片选信号CS选中有效,同时使用同一个写使能信号OE。第一个存储器芯片构成存储器组32位中的高8位,因此其写使能信号WE连接到字节选择信号的最高位BLS[3],8位数据线连接到数据总线的最高8位D[31:24]。其余三个芯片分别构成32位存储器组中的第2、3、4个字节。由于存储器组的数据宽度为32位,地址总线中的A0和A1不使用,只使用最高位a_b到bit2连接到存储芯片的地址线上。
图4.10中的16位芯片引脚UB和LB分别表示芯片中16位数据的高字节和低字节。32位芯片中的引脚B3~B0表示芯片中32位数据的四个字节。其芯片引脚与总线的连接方式与图4.9是类似的。
按照图4.9和图4.10的方法,读者不难得出16位和8位总线宽度的存储器组的外部存储器接口扩展方法。
图4.9 4个8位静态存储器芯片构成32位宽存储器组
图4.10 2个16位存储器芯片和1个32位存储器芯片构成32位宽存储器组
图4.11所示为使用SDRAM芯片构成32位动态存储器组的外部存储器接口连接方法。图中使用的SDRAM芯片为K4S561632H,这款芯片的存储容量为256Mb,32位宽度,存储空间分为4个组,使用时由组选择信号BA0和BA1选择。在连线时,除了要注意使用与静态存储器不同的动态存储器引脚如DYCS、CLKOUT、CKEOUT和DQMOUT之外,要格外注意地址线的连法。LPC2400的SDRAM地址线只使用地址线中的[14:0]这15根线,最高位两根A14和A13固定连接到芯片的组选择信号BA1和BA0,其余地址线从A0开始一一连接。这种连接方式是LPC2400的EMC特有的,与其它的ARM芯片,如三星的S3C系列芯片有很大区别,开发者在自己扩展SDRAM存储器时一定要特别注意。
图4.11 外部扩展存储器接口SDRAM
3.EMC相关寄存器
表4.18所示为EMC的相关寄存器。
表4.18 EMC寄存器汇总
寄存器名 | 类型 | 复位值 | 功能描述 | 地址 |
EMCControl | 读/写 | 0x3 | EMC控制操作 | 0xFFE0 8000 |
EMCStatus | 只读 | 0x5 | 提供EMC控制信息 | 0xFFE0 8004 |
EMCConfig | 读/写 | 0x0 | EMC配置操作 | 0xFFE0 8008 |
EMCDynamic Control | 读/写 | 0x6 | 控制动态存储器操作 | 0xFFE0 8020 |
EMCDynamic Refresh | 读/写 | 0x0 | 配置动态存储器刷新操作 | 0xFFE0 8024 |
EMCDynamicReadConfig | 读/写 | 0x0 | 配置动态存储器读策略 | 0xFFE0 8028 |
EMCDynamicRP | 读/写 | 0x0F | 选择预充电命令周期 | 0xFFE0 8030 |
EMCDynamicRAS | 读/写 | 0x0F | 选择激活预充电命令周期 | 0xFFE0 8034 |
EMCDynamicSREX | 读/写 | 0x0F | 选择刷新退出时间 | 0xFFE0 8038 |
EMCDynamicAPR | 读/写 | 0x0F | 选择最后数据输出激活命令时间 | 0xFFE0 803C |
EMCDynamicDAL | 读/写 | 0x0F | 选择数据输入激活命令时间 | 0xFFE0 8040 |
EMCDynamicWR | 读/写 | 0x0F | 选择写修复时间 | 0xFFE0 8044 |
EMCDynamicRC | 读/写 | 0x1F | 选择激活有效命令周期 | 0xFFE0 8048 |
EMCDynamicRFC | 读/写 | 0x1F | 选择自动刷新周期 | 0xFFE0 804C |
EMCDynamicXSR | 读/写 | 0x1F | 选择退出刷新有效命令时间 | 0xFFE0 8050 |
EMCDynamicRRD | 读/写 | 0x0F | 选择激活组A到组B延迟 | 0xFFE0 8054 |
EMCDynamicMRD | 读/写 | 0x0F | 选择有效命令时间的加载模式寄存器 | 0xFFE0 8058 |
EMCDynamicConfig0 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8100 |
EMCDynamicRasCas0 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8104 |
EMCDynamicConfig1 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8120 |
EMCDynamicRasCas1 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8124 |
EMCDynamicConfig2 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8140 |
EMCDynamicRasCas2 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8144 |
EMCDynamicConfig3 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8160 |
EMCDynamicRasCas3 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8164 |
EMCStaticConfig0 | 读/写 | 0x0 | 选择静态存储器芯片0配置 | 0xFFE0 8200 |
EMCStaticWaitWen0 | 读/写 | 0x0 | 选择写使能芯片0延迟 | 0xFFE0 8204 |
EMCStaticWaitOen0 | 读/写 | 0x0 | 选择输出使能芯片0延迟 | 0xFFE0 8208 |
EMCStaticWaitRd0 | 读/写 | 0x0 | 选择读访问芯片0延迟 | 0xFFE0 820C |
EMCStaticWaitPage0 | 读/写 | 0x1F | 选择芯片0异步页模式顺序访问延迟 | 0xFFE0 8210 |
EMCStaticWaitWr0 | 读/写 | 0x1F | 选择写访问芯片0延迟 | 0xFFE0 8214 |
EMCStaticWaitTurn0 | 读/写 | 0x0F | 选择芯片0总线翻转周期 | 0xFFE0 8218 |
EMCStaticConfig1 | 读/写 | 0x0 | 选择静态存储器芯片1配置 | 0xFFE0 8220 |
EMCStaticWaitWen1 | 读/写 | 0x0 | 选择写使能芯片1延迟 | 0xFFE0 8224 |
EMCStaticWaitOen1 | 读/写 | 0x0 | 选择输出使能芯片1延迟 | 0xFFE0 8228 |
EMCStaticWaitRd1 | 读/写 | 0x0 | 选择读访问芯片1延迟 | 0xFFE0 822C |
EMCStaticWaitPage1 | 读/写 | 0x1F | 选择芯片1异步页模式顺序访问延迟 | 0xFFE0 8230 |
EMCStaticWaitWr1 | 读/写 | 0x1F | 选择写访问芯片1延迟 | 0xFFE0 8234 |
EMCStaticWaitTurn1 | 读/写 | 0x0F | 选择芯片1总线翻转周期 | 0xFFE0 8238 |
EMCStaticConfig2 | 读/写 | 0x0 | 选择静态存储器芯片2配置 | 0xFFE0 8240 |
EMCStaticWaitWen2 | 读/写 | 0x0 | 选择写使能芯片2延迟 | 0xFFE0 8244 |
EMCStaticWaitOen2 | 读/写 | 0x0 | 选择输出使能芯片2延迟 | 0xFFE0 8248 |
EMCStaticWaitRd2 | 读/写 | 0x0 | 选择读访问芯片2延迟 | 0xFFE0 824C |
EMCStaticWaitPage2 | 读/写 | 0x1F | 选择芯片2异步页模式顺序访问延迟 | 0xFFE0 8250 |
EMCStaticWaitWr2 | 读/写 | 0x1F | 选择写访问芯片2延迟 | 0xFFE0 8254 |
EMCStaticWaitTurn2 | 读/写 | 0x0F | 选择芯片2总线翻转周期 | 0xFFE0 8258 |
EMCStaticConfig3 | 读/写 | 0x0 | 选择静态存储器芯片3配置 | 0xFFE0 8260 |
EMCStaticWaitWen3 | 读/写 | 0x0 | 选择写使能芯片3延迟 | 0xFFE0 8264 |
EMCStaticWaitOen3 | 读/写 | 0x0 | 选择输出使能芯片3延迟 | 0xFFE0 8268 |
EMCStaticWaitRd3 | 读/写 | 0x0 | 选择读访问芯片3延迟 | 0xFFE0 826C |
EMCStaticWaitPage3 | 读/写 | 0x1F | 选择芯片3异步页模式顺序访问延迟 | 0xFFE0 8270 |
EMCStaticWaitWr3 | 读/写 | 0x1F | 选择写访问芯片3延迟 | 0xFFE0 8274 |
EMCStaticWaitTurn3 | 读/写 | 0x0F | 选择芯片3总线翻转周期 | 0xFFE0 8278 |
EMCStaticExtendedWait | 读/写 | 0x0 | 静态存储器读写传输时间 | 0xFFE0 8880 |
各寄存器的详细配置方法,由于篇幅所限,不再赘述。在对系统进行外扩存储器开发时,需要仔细阅读外扩存储器芯片的相关文档,获得其准确的配置参数,再在初始化函数中对相关寄存器进行配置操作,才能正常使用外扩存储器。
4.4 系统控制模块
系统控制模块包括几个系统特性和控制寄存器,这些寄存器具有许多与特定外设器件无关的功能,每种类型的功能都有其自身的寄存器。
4.4.1 系统控制和状态寄存器
表4.19所示位系统控制和状态寄存器(System Controls and Status register)。
表4.19 系统控制和状态寄存器(SCS – 0xE01F C1A0)
位 | 标识 | 值 | 功能描述 | 类型 | 复位值 |
0 | GPIOM |
| GPIO访问模式选择 | 读/写 | 0 |
0 | GPIO组0和组1配置为与以前的LPC2000系列兼容的端口 | ||||
1 | GPIO组0和组1配置为高速端口,使用片内存储器 | ||||
位 | 标识 | 值 | 功能描述 | 类型 | 复位值 |
1 | EMC Reset Disable |
| 外部存储器控制器复位无效 | 读/写 | 0 |
0 | 复位时,EMC的所有寄存器和功能重新初始化 | ||||
1 | 只有上电和掉电时EMC重新初始化 | ||||
2 |
|
| 保留 |
|
|
3 | MCIPWR Active Level |
| MCIPWR有效电平 | 读/写 | 0 |
0 | MCIPWR引脚低电平 | ||||
1 | MCIPWR引脚高电平 | ||||
4 | OSCRANGE |
| 主晶振范围选择 | 读/写 | 0 |
0 | 主晶振频率范围从1MHz到20MHz | ||||
1 | 主晶振频率范围从15MHz到24MHz | ||||
5 | OSCEN |
| 主晶振使能 | 读/写 | 0 |
0 | 主晶振无效 | ||||
1 | 主晶振有效 | ||||
6 | OSCSTAT |
| 主晶振状态 | 只读 | 0 |
0 | 主晶振未准备好 | ||||
1 | 主晶振已准备好。可以通过OSCEN位设置作为一个时钟源使用 | ||||
31:7 |
|
| 保留 |
| NA |
SCS寄存器主要用于设置晶振使用方法和GPIO属性,在后续相关章节里会几次使用到该寄存器。
4.4.2 外部中断
1. 逻辑结构
LPC2400含有4个外部中断输入(作为可选的管脚功能),四个引脚分别为EINT0、EINT1、EINT2和EINT3。外部中断输入可用于将处理器从掉电模式唤醒。
可将多个管脚同时连接同一路外部中断,此时,外部中断逻辑根据方式位和极性位的不同,分别进行如下处理:
1) 低有效电平激活方式,选用EINT功能的全部管脚的状态都连接到一个正逻辑与门。
2) 高有效电平激活方式,选用EINT功能的全部管脚的状态都连接到一个正逻辑或门。
3) 边沿激活方式,使用GPIO 端口号最低的管脚,与管脚的极性无关。(边沿激活方式中选择使用多个EINT管脚被看作编程出错。)
外部中断逻辑逻辑原理图见图4.12。
图4.12 外部中断逻辑原理图
当多个EINT 管脚逻辑或时,可在中断服务程序中通过IO0PIN和IO1PIN寄存器从GPIO端口读出管脚状态来判断产生中断的管脚。
2. 寄存器描述
外部中断具有4 个相关的寄存器,如表4.20所示。EXTINT寄存器包含中断标志,INTWAKE寄存器包含使能唤醒位,可使能独立的外部中断输入将处理器从掉电模式唤醒,EXTMODE和EXTPOLAR寄存器用来指定管脚使用电平或边沿激活方式。
表4.20 外部中断寄存器
地址 | 寄存器名 | 功能描述 | 类型 |
0xE01FC140 | EXTINT | 外部中断标志寄存器,包含ENIT0、EINT1 、EINT2和EINT3的中断标志 | 读/写 |
0xE01FC144 | INTWAKE | 中断唤醒寄存器,指示哪些中断可以唤醒掉电的CPU,以及控制是否使能唤醒 | 读/写 |
0xE01FC148 | EXTMODE | 外部中断方式寄存器,控制每个管脚的边沿或电平激活 | 读/写 |
0xE01FC14C | EXTPOLAR | 外部中断极性寄存器,控制由每个管脚的哪种电平或边沿来产生中断 | 读/写 |
1) 外部中断标志寄存器-External Interrupt Flag Register(EXTINT - 0xE01FC140)
当一个管脚选择使用外部中断功能时,对应在EXTPOLAR 和EXTMODE 寄存器中的位选择的电平或边沿将置位EXTINT寄存器中的中断标志,向VIC 提出中断请求,如果管脚中断使能,将会产生中断。
向EXTINT 寄存器的位EINT0~位EINT3写入1可清除相应的外部中断标志。在电平激活方式下,只有在该管脚处于无效状态时才能清除相应的中断标志。
一旦EINT0~EINT3中的一位被置位并开始执行相应的代码(处理唤醒和/或外部中断),必须将该位清零,否则以后该EINT 管脚所触发的事件将不能再被识别。
例如,如果外部中断0 管脚的低电平将系统从掉电模式唤醒,为了将来还能进入掉电模式,唤醒后的程序必须将EINT0位复位。如果EINT0位仍保持置位状态,后来的唤醒掉电模式的任何操作都将失败,外部中断也不例外。
表4.21 外部中断标志寄存器
EXTINT | 功能 | 功能描述 | 复位值 |
0 | EINT0 | 电平激活方式下,如果管脚的EINT0 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT0 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
1 | EINT1 | 电平激活方式下,如果管脚的EINT1 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT1 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
2 | EINT2 | 电平激活方式下,如果管脚的EINT2 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT2 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
3 | EINT3 | 电平激活方式下,如果管脚的EINT3 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT3 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
7:4 |
| 保留 | NA |
2) 中断唤醒寄存器-Interrupt Wakeup Register(INTWAKE - 0xE01FC144)
INTWAKE寄存器(有时亦称为EXTWAKE,外部中断唤醒寄存器)中的使能位允许外部中断、以太网、USB、CAN、GPIO、BOD或者RTC中断将处理器从掉电模式唤醒。相关的EINTn功能必须映射到管脚才能实现掉电唤醒,但中断并不必要为了实现唤醒操作而在向量中断控制器中被使能。这样做的好处是允许外部中断输入将处理器从掉电模式唤醒,但不产生中断(只是简单地恢复操作),或者在掉电模式下使能中断而不将处理器唤醒(这样,当应用中并不需要唤醒特性时,也不必关闭中断)。
表4.22 外部中断唤醒寄存器
INTWAKE | 功能 | 功能描述 | 复位值 |
0 | EXTWAKE0 | 该位为1 时,使能EINT0将处理器从掉电模式唤醒 | 0 |
1 | EXTWAKE1 | 该位为1 时,使能EINT1将处理器从掉电模式唤醒 | 0 |
2 | EXTWAKE2 | 该位为1 时,使能EINT2将处理器从掉电模式唤醒 | 0 |
3 | EXTWAKE3 | 该位为3 时,使能EINT3将处理器从掉电模式唤醒 | 0 |
4 | ETHWAKE | 该位为1 时,使能以太网中断将处理器从掉电模式唤醒 | 0 |
5 | USBWAKE | 该位为1 时,使能USB中断将处理器从掉电模式唤醒 | 0 |
6 | CANWAKE | 该位为1 时,使能CAN总线中断将处理器从掉电模式唤醒 | 0 |
7 | GPIOWAKE | 该位为1 时,使能特殊GPIO引脚中断将处理器从掉电模式唤醒 | 0 |
13:8 |
| 保留 | NA |
14 | BODWAKE | 该位为1 时,使能BOD中断将处理器从掉电模式唤醒 | 0 |
15 | RTCWAKE | 该位为1 时,使能实时时钟RTC中断将处理器从掉电模式唤醒 | 0 |
要使器件进入掉电模式并允许总线或管脚上的一个或多个事件能使其恢复正常操作,软件应该对管脚的外部中断功能重新编程,选择中断合适的方式和极性以及掉电模式。唤醒时软件应恢复管脚复用的外围功能。
上述的所有总线或管脚都是低电平有效。如果软件要使器件退出掉电模式来响应多个管脚共用的同一个EINTi 通道的事件,中断通道必须编程设定为低电平激活方式,因为只有在电平方式中通道才能使信号逻辑或来唤醒器件。
3)外部中断方式寄存器-External Interrupt Mode Register(EXTMODE – 0xE01F C148)
EXTMODE寄存器中的位用来选择每个EINT脚是电平触发还是边沿触发。只有选择用作EINT功能(通过管脚连接模块)并已通过VICIntEnable(向量中断使能寄存器)使能的管脚才能产生外部中断(当然,如果管脚选择用作其它功能,则可能产生其它功能的中断)。
当某个中断在VICIntEnable 中被禁止时,软件应该只改变EXTMODE 寄存器中相应位的值。中断重新使能前,软件向EXTINT 写入1 来清除EXTINT 位,EXTINT 位可通过改变激活方式来置位。
表4.23 外部中断方式寄存器
EXTMODE | 功能 | 值 | 描述 | 复位值 |
0 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
1 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
2 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
3 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
7:4 |
|
| 保留 | NA |
4) 外部中断极性寄存器-External Interrupt Polarity Register(EXTPOLAR – 0xE01F C14C)
在电平激活方式中,EXTPOLAR寄存器用来选择相应管脚是高电平还是低电平有效;
在边沿激活方式中,EXTPOLAR寄存器用来选择管脚上升沿还是下降沿有效。只有选择用作EINT功能(通过管脚连接模块)并已通过VICIntEnable(向量中断使能寄存器)使能的管脚才能产生外部中断(当然,如果管脚选择用作其它功能,则可能产生其它功能的中断)。
当某个中断在VICIntEnable 中被禁止时,软件应该只改变EXTPOLAR 寄存器中相应位的值。中断重新使能前,软件向EXTINT 写入1 来清除EXTINT 位,EXTINT 位可通过改变中断极性来置位。
表4.24 外部中断极性寄存器
EXTPOLAR | 功能 | 值 | 描述 | 复位值 |
0 | EXTPOLAR0 | 0 | EINT0低电平或下降沿有效(由EXTMODE0决定) | 0 |
1 | EINT0 高电平或上升沿有效(由EXTMODE0决定) | |||
1 | EXTPOLAR1 | 0 | EINT1低电平或下降沿有效(由EXTMODE1决定) | 0 |
1 | EINT1高电平或上升沿有效(由EXTMODE1决定) | |||
2 | EXTPOLAR2 | 0 | EINT2低电平或下降沿有效(由EXTMODE2决定) | 0 |
1 | EINT2高电平或上升沿有效(由EXTMODE2决定) | |||
3 | EXTPOLAR3 | 0 | EINT3低电平或下降沿有效(由EXTMODE3决定) | 0 |
1 | EINT3高电平或上升沿有效(由EXTMODE3决定) | |||
7:4 |
|
| 保留 | NA |
有关中断向量、中断设置与中断服务子程序的内容详见4.6节。
4.5 时钟和功率控制
4.5.1 晶体振荡器
LPC2400含有3个独立的晶体振荡器:主晶振、内部RC晶振和RTC晶振。每个晶振针对不同应用需求有多种使用方法。复位后,LPC2400系列处理器使用内部RC晶振提供时钟进行操作,直到使用软件进行切换为止。这使得系统可以不依赖于外部时钟进行操作,而且使引导加载程序可以在一个确定的频率下进行操作。当Boot ROM转向用户程序之前,可以激活主晶振从而进入用户代码。
1.内部晶体振荡器(IRC,Internal RC Oscillator)
IRC可以用做看门狗定时器的时钟源,也可以作为时钟,驱动PLL锁相环提供给CPU。IRC的精度不够,因此不能用于USB接口。通常的IRC频率是4MHz。在开机或芯片复位时,LPC2400使用IRC作为时钟源,之后可以使用软件转为使用其它时钟源。
2.主晶振(Main Oscillator)
主晶振可用于为CPU提供时钟,其频率范围为1MHz~24MHz。这个频率可以通过PLL倍频为更高的频率成为CPU的主频。通常把主晶振输出的时钟称为OSCCLK,PLL输入引脚上的时钟称为PLLCLKIN,ARM处理器内核时钟频率称为CCLK。当使用主晶振提供时钟而不激活PLL时,这三个值是相等的。
由于芯片复位时使用IRC晶振,主晶振由软件启动(使用SCS寄存器中的OSCEN位),并且在某些应用中始终不会用到。通过SCS寄存器中的OSCSTAT状态位可以使软件判断主晶振是否运行和稳定,也可以通过SCS寄存器中的OSCRANGE位设置其频率范围。
LPC2400的振荡器可工作在两种模式下:从属模式和振荡模式。从属模式下,输入时钟信号XTAL1与一个100pF相连,其幅值不少于200mV,XTAL2管脚不连接。振荡模式下,由于片内集成了反馈电阻,只需在外部连接一个晶体和电容Cx1、Cx2 就可形成基本模式的振荡。
两种振荡器模式的示意见图4.13。
图4.13 振荡器模式
3.RTC晶振(RTC Oscillator)
RTC晶振的频率为32.768KHz,一般用于给RTC实时时钟提供时钟源。RTC晶振也可以用于看门狗定时器,通过驱动PLL也可以用于提供CPU主频。
4.时钟源选择
几个时钟源都可以用来驱动PLL从而给CPU和片内外设提供时钟。当PLL未连接时,系统可以通过CLKSRCSEL寄存器安全的改变时钟源。
表4.25 时钟源选择寄存器(CLKSRCSEL – 0xE01F C10C)
CLKSRCSEL | 功能 | 值 | 描述 | 复位值 |
1:0 | CLKSRC | 00 | 选择IRC晶振为PLL时钟源 | 00 |
01 | 选择主晶振为PLL时钟源 | |||
10 | 选择RTC晶振为PLL时钟源 | |||
11 | 保留 | |||
7:4 |
| 0 | 未使用,始终为0 | 0 |
4.5.2 PLL锁相环
PLL接受输入的时钟频率范围为32kHz~50MHz。输入频率通过一个预分频器分频成为PLL内部频率,预分频器的值用变量“N”表示。然后再通过一个电流控制振荡器(CCO)倍增到范围275MHz~550MHz,倍频器的值用变量“M”表示。CCO频率再通过CPU频率设置寄存器分频成为提供给CPU的CCLK时钟。
PLL 的激活由PLLCON寄存器控制,PLL倍频器和分频器的值由PLLCFG寄存器控制。为了防止PLL参数发生意外改变或PLL失效,对这两个寄存器进行了保护。当PLL提供芯片时钟时,由于芯片的所有操作,包括看门狗定时器在内都依赖于它,因此PLL设置的意外改变将导致CPU 执行不期望的动作。
1.PLL控制寄存器-PLL Control Register(PLLCON – 0xE01FC080)
PLLCON寄存器可用于使能和连接PLL,它是最新的PLL控制位的保持寄存器,写入该寄存器的值在有效的PLL 馈送序列执行之前不起作用。使能PLL将使PLL锁定到当前倍频器和分频器值的设定频率上。连接PLL将使处理器和所有片内功能都根据PLL输出时钟来运行。对PLLCON的更改只有在对PLLFEED寄存器执行了正确的PLL馈送序列后才生效。
表4.26 PLL 控制寄存器
PLLCON | 功能 | 描述 | 复位值 |
0 | PLLE | PLL使能。当该位为1 并且在有效的PLL馈送之后,该位将激活PLL并允许其锁定到指定的频率。 | 0 |
1 | PLLC | PLL连接。当PLLC和PLLE都为1 并且在有效的PLL馈送后,将PLL作为时钟源连接到LPC2400。否则,LPC2400直接使用振荡器时钟。 | 0 |
7:2 | 保留 | 保留 | NA |
PLL在作为时钟源之前必须进行设置、使能并锁定。将振荡器时钟切换到PLL输出或反过来操作时,内部电路对操作进行同步以确保不会产生干扰。硬件不能确保PLL在连接之前锁定或在PLL在失去锁定时自动断开连接。在PLL失去锁定的情况下,振荡器很可能已经变得不稳定,这样即使断开PLL也挽救不了这种状况。
2.PLL配置寄存器-PLL Configuration Register(PLLCFG – 0xE01FC084)
PLLCFG寄存器是最新的PLL配置值的保持寄存器,包含PLL倍频器和分频器值。在执行正确的PLL馈送序列之前改变PLLCFG寄存器的值不会生效。
表4.27 PLL配置寄存器
PLLCFG | 功能 | 描述 | 复位值 |
14:0 | MSEL | PLL倍频器值,在PLL频率计算中其值为M-1 | 0 |
15 | 保留 | 保留 | NA |
23:16 | NSEL | PLL预分配器值,在PLL频率计算中其值为N | NA |
31:24 | 保留 | 保留 | NA |
3.PLL状态寄存器-PLL Status Register(PLLSTAT – 0xE01FC088)
PLLSTAT为只读寄存器,它是PLL控制和配置信息的读回寄存器,反映了正在使用的真实PLL参数和状态。PLLSTAT可能与PLLCON和PLLCFG中的值不同,这是因为没有执行正确的PLL馈送序列,这两个寄存器中的值并未生效。
表4.28 PLL状态寄存器
PLLSTAT | 功能 | 描述 | 复位值 |
14:0 | MSEL | 读出的PLL倍频器值,这是PLL当前使用的值 | 0 |
15 | 保留 | 保留 | NA |
23:16 | NSEL | 读出的PLL预分频器值,这是PLL当前使用的值 | NA |
24 | PLLE | 读出的PLL使能位,该位为1时,PLL处于激活状态;为0时,PLL关闭;进入掉电模式时,该位自动清零 | 0 |
25 | PLLC | 读出的PLL连接位,当PLLC和PLLE都为1 时,PLL作为时钟源连接到LPC2400;当PLLC或PLLE位为0 时,PLL被旁路,LPC2400直接使用振荡器时钟;进入掉电模式时,该位自动清零 | 0 |
26 | PLOCK | 反映PLL的锁定状态,为0时,PLL未锁定;为1时,PLL锁定到指定的频率 |
|
31:27 | 保留 | 保留 | NA |
PLLSTAT 寄存器中的PLOCK位连接到中断控制器。这样可使用软件打开PLL并连接到其它功能,不需要等待PLL锁定。当发生中断时(PLOCK=1),可以连接PLL并禁止中断。只能通过禁止PLL中断的方式返回。
PLL有3种可能的工作模式,由PLLE和PLLC组合得到。
表4.29 PLL的工作模式
PLLC | PLLE | PLL 功能 |
0 | 0 | PLL 被关闭并断开连接,系统使用未更改的时钟输入 |
0 | 1 | PLL 被激活但是尚未连接,PLL 可在PLOCK 置位后连接 |
1 | 0 | 与00 组合相同,这样消除了PLL 已连接但没有使能的可能性 |
1 | 1 | PLL 已使能并连接到处理器作为系统时钟源 |
4.PLL馈送寄存器-PLL Feed Register(PLLFEED – 0xE01FC08C)
必须将正确的馈送序列写入PLLFEED寄存器才能使PLLCON和PLLCFG寄存器的更改生效。馈送序列如下:
1) 将值0xAA 写入PLLFEED;
2) 将值0x55 写入PLLFEED。
这两个写操作的顺序必须正确,而且必须是连续的APB总线周期,这意味着在执行PLL馈送操作时必须禁止中断。不管是写入的值不正确还是没有满足前两个条件,对PLLCON或PLLCFG寄存器的更改都不会生效。
表4.30 PLL 馈送寄存器
PLLFEED | 功能 | 描述 | 复位值 |
7:0 | PLLFEED | PLL馈送序列必须写入该寄存器才能使PLL配置和控制寄存器的更改生效 | 0x00 |
5.PLL和掉电模式
掉电模式会自动关闭并断开PLL。从掉电模式唤醒不会自动恢复PLL的设定,PLL的恢复必须由软件来完成。通常,一个将PLL激活并等待锁定,然后将PLL连接的子程序可以在任何中断服务程序的开始调用。有一点非常重要,那就是不要试图在掉电唤醒之后简单地执行馈送序列来重新启动PLL,这会出现在PLL锁定建立之前同时使能并连接PLL的危险。
6.PLL频率计算举例:
当一个LPC2400 ARM系统需要使用PLL,应当按照以下原则进行:
1) 确定处理器的时钟频率CCLK。这可以根据系统对处理器的整体要求来决定,外围器件的时钟频率可以低于处理器频率。
2) 确定PLL内部的时钟频率FCCO。FCCO的值应当在275MHz~550MHz之间,而且应当是CCLK的整数倍。
3) 确定晶体振荡器频率FIN。FIN的值应当在32kHz~50MHz之间。
PLL的输出频率公式为:FCCO = (2×M×FIN) / N
选择两个整数M和N便可得到合适的FCCO值。M的取值范围为6~512,N的取值范围为1~32。
例如:系统要求使用USB接口,CPU主频约为60MHz。外部晶振频率为4MHz。
由要求可知:USB总线要求精确的48MHz时钟,因此可以选择FCCO为480MHz。当N值为1时,M = 480 / (2×4) = 60。因此PLLCFG值为0x3B(N-1 = 0,M-1 = 0x3B)。
4.5.3 时钟分频
PLL的输出必须向下分频为更低频率的信号才能用于CPU和USB模块。提供给USB模块的分频器是独立的,因为USB的时钟要求必须是准确的48MHz而且有50%的占空比。分频给CPU的信号成为CCLK时钟,并且再分频成为各个片内外设的驱动时钟。
1.CPU时钟配置寄存器-CPU Clock Configuration Register(CCLKCFG – 0xE01F C104)
CCLKCFG寄存器控制PLL的分频输出提供给CPU。如果不使用PLL,分频值为1。
表4.31 CPU时钟配置寄存器
CCLKCFG | 功能 | 描述 | 复位值 |
7:0 | CCLKSEL | 分频器值,用于生成CPU时钟(CCLK)。本值只能为0或奇数值(1,3,5,…,255) | 0x00 |
CCLK值为PLL的输出频率除以CCLKSEL+1。当CCLKSEL值为1时,CCLK值为PLL输出频率的一半。
2.USB时钟配置寄存器-USB Clock Configuration Register(USBCLKCFG – 0xE01F C108)
USBCLKCFG寄存器控制PLL的分频输出提供给USB模块。如果不使用PLL,分频值为1。输出的频率应该为48MHz并且有50%的占空比。
表4.32 USB时钟配置寄存器
USBCLKCFG | 功能 | 描述 | 复位值 |
3:0 | USBSEL | 分频器值,用于生成USB时钟(CCLK) | 0x00 |
7:4 | - | 保留 | NA |
USB模块的时钟值为PLL的输出频率除以USBSEL+1。当USBSEL值为1时,USB时钟值为PLL输出频率的一半。
3.IRC整理寄存器-IRC Trim Register(IRCTRIM – 0xE01F C1A4)
这个寄存器用于整理片内4MHz的晶振。
表4.33 IRC整理寄存器
IRCtrim | 功能 | 描述 | 复位值 |
7:0 | IRCtrim | IRC整理值,用于控制片内4MHzIRC晶振频率 | 0xA0 |
15:8 | - | 保留 | NA |
4.外设时钟选择寄存器0和1-Peripheral Clock Selection registers 0 and 1(PCLKSEL0 – 0xE01F C1A8 and PCLKSEL1 – 0xE01F C1AC)
这一对寄存器中的每两位控制一个外设的时钟,其取值意义参见表4.36。
表4.34 外设时钟选择寄存器0
PCLKSEL0 | 功能 | 描述 | 复位值 |
1:0 | PCLK_WDT | 看门狗外设时钟选择 | 00 |
3:2 | PCLK_TIMER0 | 定时器0外设时钟选择 | 00 |
5:4 | PCLK_TIMER1 | 定时器1外设时钟选择 | 00 |
7:6 | PCLK_UART0 | 串口0外设时钟选择 | 00 |
9:8 | PCLK_UART1 | 串口1外设时钟选择 | 00 |
11:10 | PCLK_PWM0 | 脉宽调制器0外设时钟选择 | 00 |
13:12 | PCLK_PWM1 | 脉宽调制器1外设时钟选择 | 00 |
15:14 | PCLK_I2C0 | I2C0外设时钟选择 | 00 |
17:16 | PCLK_SPI | SPI外设时钟选择 | 00 |
19:18 | PCLK_RTC | 实时时钟外设时钟选择 | 00 |
21:20 | PCLK_SSP1 | SSP1外设时钟选择 | 00 |
23:22 | PCLK_DAC | DAC外设时钟选择 | 00 |
25:24 | PCLK_ADC | ADC外设时钟选择 | 00 |
27:26 | PCLK_CAN1 | CAN1外设时钟选择 | 00 |
29:28 | PCLK_CAN2 | CAN2外设时钟选择 | 00 |
31:30 | PCLK_ACF | CAN滤波器外设时钟选择 | 00 |
注:PCLK_RTC字段中,值“01”是无效的,试图写入“01”不会改变预设值。
表4.35 外设时钟选择寄存器1
PCLKSEL1 | 功能 | 描述 | 复位值 |
1:0 | PCLK_BAT_RAM | 电池支持RAM外设时钟选择 | 00 |
3:2 | PCLK_GPIO | GPIO外设时钟选择 | 00 |
5:4 | PCLK_PCB | 引脚连接模块外设时钟选择 | 00 |
7:6 | PCLK_I2C1 | I2C1外设时钟选择 | 00 |
9:8 | - | 保留,始终为0 | 00 |
11:10 | PCLK_SSP0 | SSP0外设时钟选择 | 00 |
13:12 | PCLK_TIMER2 | 定时器2外设时钟选择 | 00 |
15:14 | PCLK_TIMER3 | 定时器3外设时钟选择 | 00 |
17:16 | PCLK_UART2 | 串口2外设时钟选择 | 00 |
19:18 | PCLK_UART3 | 串口3外设时钟选择 | 00 |
21:20 | PCLK_I2C2 | I2C2外设时钟选择 | 00 |
23:22 | PCLK_I2S | I2S总线外设时钟选择 | 00 |
25:24 | PCLK_MCI | MCI外设时钟选择 | 00 |
27:26 | - | 保留,始终为0 | 00 |
PCLKSEL1 | 功能 | 描述 | 复位值 |
29:28 | PCLK_SYSCON | 系统控制模块外设时钟选择 | 00 |
31:30 | - | 保留,始终为0 | 00 |
表4.36 外设时钟选择寄存器位值
位 | 功能描述 | 复位值 |
00 | PCLK_xxx = CCLK / 4 | 00 |
01 | PCLK_xxx = CCLK | 00 |
10 | PCLK_xxx = CCLK / 2 | 00 |
11 | 在CAN1、CAN2、CAN滤波器部件中PCLK_xxx = HCLK / 6,其余部件中PCLK_xxx = HCLK / 8 | 00 |
4.5.4 功率控制
1. 节电模式
嵌入式系统一般使用电池供电,因此系统的耗电和待机时间是个重要指标。节电的方法主要是降低系统时钟频率:改变时钟源、重配置PLL值或者改变CPU时钟分频值。另外,也可以通过停止片内外设时钟的方法来关闭不使用的片内外设,进一步减少功耗。
LPC2400支持三种节电模式:空闲模式、睡眠模式和掉电模式。LPC2400有一个独立的功率控制单元用来给RTC和一个小的静态RAM供电,这个特性使得LPC2400可以将片内的其它大部分设备全部关闭。
1)空闲模式(Idel mode):指令的执行被挂起直到发生复位或中断为止。外设功能在空闲模式下继续保持并可产生中断使处理器恢复运行。空闲模式使处理器、存储器系统和相关控制器以及内部总线不再消耗功率。任何中断都可以将CPU从空闲模式下唤醒。
2)睡眠模式(Sleep mode):主晶振关闭,所有时钟停止。IRC的输出无效,但IRC并未关闭并且可以快速唤醒。32kHz的RTC晶振也未停止,因为RTC中断可以用来做唤醒的中断源。睡眠模式保持处理器状态和寄存器、外设寄存器和内部SRAM的值。芯片管脚的逻辑电平保持静态。复位或特定的不需要时钟仍能工作的中断可中止睡眠模式并使芯片恢复正常运行。由于睡眠模式使芯片所有的动态操作都挂起,因此芯片的功耗降低到几乎为零。芯片复位和特定的中断可以将CPU从睡眠模式下唤醒。
3)掉电模式(Power-down mode):掉电模式与睡眠模式类似,但不同的是掉电模式会将Flash存储器也关闭。在掉电模式下,主晶振和IRC以及所有内部时钟都停止,只有32kHz的RTC晶振继续工作。
2. 寄存器描述
外设的功率控制特性允许独立关闭应用中不需要的外设,这样可以进一步降低功耗。功率控制功能包含两个寄存器,分别是PCON和PCONP。
1) 功率控制寄存器-Power Control Register(PCON – 0xE01F C0C0)
表4.37 功率控制寄存器
PCON | 功能 | 描述 | 复位值 |
0 | PM0(IDL) | 功耗模式控制位0 | 0 |
1 | PM1(PD) | 功耗模式控制位1 | 0 |
2 | BODPDM | 低电压掉电模式。当该位为0时,进入掉电模式时仍然保持低电压检测功能;当该位为1时,低电压检测功能也被关闭,这样可以进一步减少功耗,但存在着电压过低而无法从掉电模式中唤醒的可能 | 0 |
3 | BOGD | 低电压全局无效。当该位为1时,低电压检测功能无效;该位为0时,低电压检测功能使能 | 0 |
4 | BORD | 低电压复位无效。当该位为1时,低电压检测(2.6V)不会导致芯片复位;当该位为0时,低电压检测(2.9V)使芯片复位 | 0 |
6:3 | - | 保留 | NA |
7 | PM2 | 功耗模式控制位2 | 0 |
利用PCON寄存器设置节电模式的方法详见表4.38。PCON寄存器中的三个比特PM2、PM1和PM0联合控制进入LPC2400节电模式的方式。
表4.38 节电模式控制位
PM2,PM1,PM0 | 功能描述 |
000 | 正常模式 |
001 | 空闲模式 |
101 | 睡眠模式 |
010 | 掉电模式 |
其它 | 保留 |
2)外设功率控制寄存器-Power Control for Peripherals Register(PCONP – 0xE01F C0C4)
PCONP寄存器允许将所选的外设功能关闭以实现节电的目的,通过关断特定外围模块的时钟源来实现。有少数外设功能不能被关闭(例如看门狗定时器、GPIO、引脚连接模块和系统控制模块)。
某些外设,特别是包含模拟功能的外设,它们的操作无需时钟,但会消耗功率。这些外设包含独立的禁能控制位,可以通过它们来关闭电路以降低功耗。
PCONP中的每个位都控制一个外设,每个位所对应的外设编号见LPC2400存储器寻址部分的APB外设映射一节。当位值为1时该外设启用,当位值为0时该外设时钟关闭。外设在外设功率控制寄存器的对应位见表4.11。
表4.39外设功率控制寄存器
PCONP | 功能 | 描述 | 复位值 |
0 | - | 未使用,始终为0 | 0 |
1 | PCTIM0 | 定时器0功率时钟控制位 | 1 |
2 | PCTIM1 | 定时器1功率时钟控制位 | 1 |
3 | PCUART0 | 串口0功率时钟控制位 | 1 |
4 | PCUART1 | 串口1功率时钟控制位 | 1 |
5 | PCPWM0 | 脉宽调制器0功率时钟控制位 | 1 |
6 | PCPWM1 | 脉宽调制器1功率时钟控制位 | 1 |
7 | PCI2C0 | I2C0功率时钟控制位 | 1 |
8 | PCSPI | SPI功率时钟控制位 | 1 |
9 | PCRTC | 实时时钟功率时钟控制位 | 1 |
10 | PCSSP1 | SSP1接口功率时钟控制位 | 1 |
11 | PCEMC | 外扩存储器控制器 | 1 |
12 | PCAD | A/D转换器功率时钟控制位。清零该位前先清零AD0CR 寄存器的PDN位,该位应当在置位PDN前被置位 | 0 |
13 | PCCAN1 | CAN1功率时钟控制位 | 0 |
14 | PCCAN2 | CAN2功率时钟控制位 | 0 |
18:15 | - | 保留 | NA |
19 | PCI2C1 | I2C1功率时钟控制位 | 1 |
20 | PCLCD | 液晶控制器功率时钟控制位 | 0 |
21 | PCSSP0 | SSP0功率时钟控制位 | 1 |
22 | PCTIM2 | 定时器2功率时钟控制位 | 0 |
23 | PCTIM3 | 定时器3功率时钟控制位 | 0 |
24 | PCUART2 | 串口2功率时钟控制位 | 0 |
25 | PCUART3 | 串口3功率时钟控制位 | 0 |
26 | PCI2C2 | I2C2功率时钟控制位 | 1 |
27 | PCI2S | I2S接口功率时钟控制位 | 0 |
28 | PCSDC | SD卡接口功率时钟控制位 | 0 |
29 | PCGPDMA | 通用DMA功能功率时钟控制位 | 0 |
30 | PCENET | 以太网模块功率时钟控制位 | 0 |
31 | PCUSB | USB接口功率时钟控制位 | 0 |
复位以后,PCONP寄存器按照默认值使能选中的接口和外设控制器。用户程序应当在启动代码中对PCONP寄存器编程用来启动所需要的外设功能,并关闭不需要的接口和外设,以达到降低功耗的要求。系统启动以后,除了对外设功能相关的寄存器进行配置外,用户应用程序应当不要再访问PCONP 寄存器从而启动使用片内的任何外围功能。
4.5.5 时钟和功率控制举例
1. 系统时钟设置
在LPC2400的启动代码中,系统时钟的设置是通过一个ConfigurePLL ( )函数来实现的。该函数首先关闭PLL,然后通过CLKSRCSEL寄存器选择主晶振为时钟源,再通过PLLCFG寄存器利用M值和N值设置CCO频率,用CCLKCFG寄存器分频为CPU时钟CCLK,最后使能PLL使设置生效。注意对PLLCON寄存器的每次操作都要用正确的馈送序列来实现。函数中的有关参数是在target.h头文件中定义的,其相关程序行如下:
代码清单4.2
……
#define Fosc 12000000
#define Fcclk 57600000
#define Fcco 288000000
#define Fpclk (Fcclk / 4)
#define PLL_MValue 11
#define PLL_NValue 0
#define CCLKDivValue 4
#define USBCLKDivValue 5
……
主晶振采用12MHz晶体振荡器,其宏定义Fosc值要跟实际的物理参数相同。由于M值为12,N值为1(注意实际参数要在设置值上加1),CCO频率Fcco = 2×M×Fosc / N = 288MHz。CPU时钟CCLK = Fcco / (CCLKDivValue+1) = 57.6MHz。而USB时钟USBCLK = Fcco / (USBCLKDivValue+1) = 48MHz,正好满足使用要求。
代码清单4.3
void ConfigurePLL ( void )
{
if ( PLLSTAT & (1 << 25) ) // PLL是否连接
{ PLLCON = 1; // 使能PLL并断开连接
PLLFEED = 0xaa; // PLL馈送
PLLFEED = 0x55; }
PLLCON = 0; // 关闭PLL并断开连接
PLLFEED = 0xaa;
PLLFEED = 0x55;
SCS |= 0x20; // 使能主晶振
while( !(SCS & 0x40) ); // 读主晶振状态直到主晶振可用
CLKSRCSEL = 0x1; // 选择12MHz主晶振作为PLL时钟源
PLLCFG = PLL_MValue | (PLL_NValue << 16); // 执行配置
PLLFEED = 0xaa;
PLLFEED = 0x55;
PLLCON = 1; // 使能PLL
PLLFEED = 0xaa;
PLLFEED = 0x55;
CCLKCFG = CCLKDivValue; // 设置时钟分频,设定CPU频率CCLK
#if USE_USB
USBCLKCFG = USBCLKDivValue; // usbclk = 288 MHz/6 = 48 MHz
#endif
while ( ((PLLSTAT & (1 << 26)) == 0) ); // 检查锁定位状态
PLLCON = 3; // 使能并连接PLL
PLLFEED = 0xaa;
PLLFEED = 0x55;
while ( ((PLLSTAT & (1 << 25)) == 0) ); // 检查连接状态位
return;
}
2. 外设分频
外设启动和分频应在启动代码中实现。LPC2400的启动代码使用一个TargetResetInit( )函数实现,其中相关代码如下:
代码清单4.4
……
#if USE_USB
PCONP |= 0x80000000; // 如果使用USB则打开USB PCLK
#endif
ConfigurePLL();
#if (Fpclk / (Fcclk / 4)) == 1
PCLKSEL0 = 0x00000000; // PCLK = 1/4 CCLK
PCLKSEL1 = 0x00000000;
#endif
#if (Fpclk / (Fcclk / 4)) == 2
PCLKSEL0 = 0xAAAAAAAA; // PCLK = 1/2 CCLK
PCLKSEL1 = 0xAAAAAAAA;
#endif
#if (Fpclk / (Fcclk / 4)) == 4
PCLKSEL0 = 0x55555555; // PCLK = CCLK
PCLKSEL1 = 0x55555555;
#endif
……
为了降低外设功耗,一般都将APB外设时钟设为CCLK时钟的1/4。
3. 进入Idle模式
代码清单4.5
……
PCON = 0x01; // 进入Idle状态,CPU停止
……
进入Sleep模式和Power Down模式采用不同设置值进行设置即可。
4.6 向量中断控制器
4.6.1 LPC2400中断特性
LPC2400使用了ARM PrimeCellTM技术的向量中断控制器,利用映射到AHB总线的地址空间实现快速访问。支持最大32个向量IRQ中断,拥有16个可编程中断优先级,并且每个可编程优先级对应固定硬件优先级,具有硬件优先级屏蔽逻辑。所有中断都可设置为FIQ中断,还可以产生软件中断。
4.6.2 功能概述
ARM处理器内核有两类中断:中断请求(IRQ)和快速中断请求(FIQ)。管理中断类型识别及优先级判断,并向ARM内核提供中断向量和中断信号的模块称为向量中断控制器(VIC)。VIC支持的32个中断可以编程设置为IRQ或FIQ中断类型。这样用户可以按照处理器外围模块的优先级别灵活设置中断的优先级别。
快速中断请求(FIQ)是优先级最高的中断。如果有一个以上中断被设置为FIQ,则VIC对中断输入进行“或”操作,最终向ARM内核产生一个FIQ信号。为了确保FIQ响应的最短延时,在实际应用中一般只设置一个中断源为FIQ类型。这样FIQ中断服务程序就可以直接处理对应模块。如果有多个中断源设置为FIQ,则在FIQ中断服务程序中要先读出VIC的FIQ中断状态字,从而判断真正发生的中断源才能处理对应的中断。
除了设置为FIQ的中断外,其余中断类型为向量IRQ。向量IRQ中断优先级可以编程设置。如果有一个以上IRQ中断分配相同优先级且同时产生中断请求,则连接到VIC的通道靠前的中断源先被应答服务。VIC通道数值分配见中断源小节表。
另外,VIC对所有向量IRQ进行“或”操作,最终向ARM内核产生一个IRQ信号。IRQ中断服务程序先读取VIC的IRQ中断状态字,确定中断源后执行相应中断服务。
4.6.3 中断控制器结构
向量中断控制器VIC的结构如图4.14所示。
图4.14 VIC的结构框图
4.6.4 寄存器描述
VIC内所有寄存器都为32位宽,具体名称及地址见表4.40。
表4.40 VIC寄存器映射表
名称 | 功能描述 | 访问方式 | 复位值[1] | 地址 |
VICIRQStatus | IRQ中断状态寄存器。该寄存器保存各个IRQ中断请求是否有效。 | 只读 | 0 | 0xFFFFF000 |
VICFIQStatus | FIQ中断状态寄存器。该寄存器保存各个FIQ中断请求是否有效。 | 只读 | 0 | 0xFFFFF004 |
VICRawIntr | 原始中断状态寄存器。该寄存器保存32个中断请求和软件中断是否有效,不管它们是否被使能。 | 只读 | - | 0xFFFFF008 |
VICIntSelect | 中断类型选择寄存器。该寄存器用于把中断请求设置为FIQ或者IRQ。 | 读/写 | 0 | 0xFFFFF00C |
VICIntEnable | 中断使能寄存器。该寄存器使能32个中断请求和软件中断产生IRQ或FIQ中断。 | 读/写 | 0 | 0xFFFFF010 |
VICIntEnClr | 中断使能清除寄存器。该寄存器可清除中断使能寄存器已使能的各中断位。 | 只写 | - | 0xFFFFF014 |
VICSoftInt | 软件中断寄存器。该寄存器内容与32个中断请求作相“或”操作。 | 读/写 | 0 | 0xFFFFF018 |
名称 | 功能描述 | 访问方式 | 复位值[1] | 地址 |
VICSoftIntClear | 软件中断清除寄存器。 | 只写 | - | 0xFFFFF01C |
VICProtection | VIC保护使能寄存器。该寄存器限制软件在特权模式下运行时访问VIC各个寄存器。 | 读/写 | 0 | 0xFFFFF020 |
VICSWPriorityMask | 软件优先级屏蔽寄存器。 | 读/写 | 0xFFFF | 0xFFFFF024 |
VICVectAddr0 | 向量地址0寄存器。该寄存器保存IRQ0的中断服务程序入口地址。IRQ0优先级最高,IRQ32最低。 | 读/写 | 0 | 0xFFFFF100 |
VICVectAddr1 | 向量地址1寄存器。 | 读/写 | 0 | 0xFFFFF104 |
VICVectAddr2 | 向量地址2寄存器。 | 读/写 | 0 | 0xFFFFF108 |
VICVectAddr3 | 向量地址3寄存器。 | 读/写 | 0 | 0xFFFFF10C |
VICVectAddr4 | 向量地址4寄存器。 | 读/写 | 0 | 0xFFFFF110 |
VICVectAddr5 | 向量地址5寄存器。 | 读/写 | 0 | 0xFFFFF114 |
VICVectAddr6 | 向量地址6寄存器。 | 读/写 | 0 | 0xFFFFF118 |
VICVectAddr7 | 向量地址7寄存器。 | 读/写 | 0 | 0xFFFFF11C |
VICVectAddr8 | 向量地址8寄存器。 | 读/写 | 0 | 0xFFFFF120 |
VICVectAddr9 | 向量地址9寄存器。 | 读/写 | 0 | 0xFFFFF124 |
VICVectAddr10 | 向量地址10寄存器。 | 读/写 | 0 | 0xFFFFF128 |
VICVectAddr11 | 向量地址11寄存器。 | 读/写 | 0 | 0xFFFFF12C |
VICVectAddr12 | 向量地址12寄存器。 | 读/写 | 0 | 0xFFFFF130 |
VICVectAddr13 | 向量地址13寄存器。 | 读/写 | 0 | 0xFFFFF134 |
VICVectAddr14 | 向量地址14寄存器。 | 读/写 | 0 | 0xFFFFF138 |
VICVectAddr15 | 向量地址15寄存器。 | 读/写 | 0 | 0xFFFFF13C |
VICVectAddr16 | 向量地址16寄存器。 | 读/写 | 0 | 0xFFFFF140 |
VICVectAddr17 | 向量地址17寄存器。 | 读/写 | 0 | 0xFFFFF144 |
VICVectAddr18 | 向量地址18寄存器。 | 读/写 | 0 | 0xFFFFF148 |
VICVectAddr19 | 向量地址19寄存器。 | 读/写 | 0 | 0xFFFFF14C |
VICVectAddr20 | 向量地址20寄存器。 | 读/写 | 0 | 0xFFFFF150 |
VICVectAddr21 | 向量地址21寄存器。 | 读/写 | 0 | 0xFFFFF154 |
VICVectAddr22 | 向量地址22寄存器。 | 读/写 | 0 | 0xFFFFF158 |
VICVectAddr23 | 向量地址23寄存器。 | 读/写 | 0 | 0xFFFFF15C |
VICVectAddr24 | 向量地址24寄存器。 | 读/写 | 0 | 0xFFFFF160 |
VICVectAddr25 | 向量地址25寄存器。 | 读/写 | 0 | 0xFFFFF164 |
VICVectAddr26 | 向量地址26寄存器。 | 读/写 | 0 | 0xFFFFF168 |
VICVectAddr27 | 向量地址27寄存器。 | 读/写 | 0 | 0xFFFFF16C |
VICVectAddr28 | 向量地址28寄存器。 | 读/写 | 0 | 0xFFFFF170 |
VICVectAddr29 | 向量地址29寄存器。 | 读/写 | 0 | 0xFFFFF174 |
VICVectAddr30 | 向量地址30寄存器。 | 读/写 | 0 | 0xFFFFF178 |
VICVectAddr31 | 向量地址31寄存器。 | 读/写 | 0 | 0xFFFFF17C |
VICVectPriority0 | 向量优先级0寄存器。该寄存器设置IRQ0的优先级。 | 读/写 | 0xF | 0xFFFFF200 |
VICVectPriority1 | 向量优先级1寄存器。 | 读/写 | 0xF | 0xFFFFF204 |
VICVectPriority2 | 向量优先级2寄存器。 | 读/写 | 0xF | 0xFFFFF208 |
VICVectPriority3 | 向量优先级3寄存器。 | 读/写 | 0xF | 0xFFFFF20C |
VICVectPriority4 | 向量优先级4寄存器。 | 读/写 | 0xF | 0xFFFFF210 |
VICVectPriority5 | 向量优先级5寄存器。 | 读/写 | 0xF | 0xFFFFF214 |
VICVectPriority6 | 向量优先级6寄存器。 | 读/写 | 0xF | 0xFFFFF218 |
VICVectPriority7 | 向量优先级7寄存器。 | 读/写 | 0xF | 0xFFFFF21C |
VICVectPriority8 | 向量优先级8寄存器。 | 读/写 | 0xF | 0xFFFFF220 |
VICVectPriority9 | 向量优先级9寄存器。 | 读/写 | 0xF | 0xFFFFF224 |
VICVectPriority10 | 向量优先级10寄存器。 | 读/写 | 0xF | 0xFFFFF228 |
VICVectPriority11 | 向量优先级11寄存器。 | 读/写 | 0xF | 0xFFFFF22C |
VICVectPriority12 | 向量优先级12寄存器。 | 读/写 | 0xF | 0xFFFFF230 |
VICVectPriority13 | 向量优先级13寄存器。 | 读/写 | 0xF | 0xFFFFF234 |
VICVectPriority14 | 向量优先级14寄存器。 | 读/写 | 0xF | 0xFFFFF238 |
VICVectPriority15 | 向量优先级15寄存器。 | 读/写 | 0xF | 0xFFFFF23C |
VICVectPriority16 | 向量优先级16寄存器。 | 读/写 | 0xF | 0xFFFFF240 |
VICVectPriority17 | 向量优先级17寄存器。 | 读/写 | 0xF | 0xFFFFF244 |
VICVectPriority18 | 向量优先级18寄存器。 | 读/写 | 0xF | 0xFFFFF248 |
VICVectPriority19 | 向量优先级19寄存器。 | 读/写 | 0xF | 0xFFFFF24C |
VICVectPriority20 | 向量优先级20寄存器。 | 读/写 | 0xF | 0xFFFFF250 |
VICVectPriority21 | 向量优先级21寄存器。 | 读/写 | 0xF | 0xFFFFF254 |
VICVectPriority22 | 向量优先级22寄存器。 | 读/写 | 0xF | 0xFFFFF258 |
VICVectPriority23 | 向量优先级23寄存器。 | 读/写 | 0xF | 0xFFFFF25C |
VICVectPriority24 | 向量优先级24寄存器。 | 读/写 | 0xF | 0xFFFFF260 |
VICVectPriority25 | 向量优先级25寄存器。 | 读/写 | 0xF | 0xFFFFF264 |
VICVectPriority26 | 向量优先级26寄存器。 | 读/写 | 0xF | 0xFFFFF268 |
VICVectPriority27 | 向量优先级27寄存器。 | 读/写 | 0xF | 0xFFFFF26C |
VICVectPriority28 | 向量优先级28寄存器。 | 读/写 | 0xF | 0xFFFFF270 |
VICVectPriority29 | 向量优先级29寄存器。 | 读/写 | 0xF | 0xFFFFF274 |
VICVectPriority30 | 向量优先级30寄存器。 | 读/写 | 0xF | 0xFFFFF278 |
VICVectPriority31 | 向量优先级31寄存器。 | 读/写 | 0xF | 0xFFFFF27C |
VICAddress | 向量地址寄存器。当发生IRQ中断时,该寄存器保存当前有效中断。 | 读/写 | 0 | 0xFFFFFF00 |
[1] 复位值只反映了使用位的数值,不包括保留位的内容。
下面将按照VIC逻辑中的使用顺序对VIC寄存器进行描述,该顺序为从与中断请求输入最密切的寄存器开始,到由软件所使用的最抽象的寄存器结束。对大多数人来说,这也是在学习VIC时读取寄存器的最佳顺序。
1、 软件中断寄存器VICSoftInt(0xFFFFF018)
软件中断寄存器用于产生软件中断。在执行任何逻辑操作之前,该寄存器的内容将与32个不同外设的中断请求相“或”。
表4.41 软件中断寄存器位描述
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 不产生中断请求。写0至该位无效。 | 0 |
1 | 强制产生与该位相关的中断请求。 |
2、软件中断清零寄存器VICSoftIntClear(0xFFFFF01C)
软件中断清零寄存器为只写寄存器。对该寄存器一个或多个位写1可以清除软件中断寄存器中的置1位。
表4.42 软件中断清零寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 写0无效果。 | 0 |
1 | 写1则软件中断寄存器中对应位被清除。 |
3、原始中断状态寄存器VICRawIntr(0xFFFFF008)
该只读寄存器读取所有32个中断请求和软件中断的状态,不管中断是否使能或分类。
表4.43 原始中断状态寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 对应位的中断请求或软件中断未声明。 | - |
1 | 对应位的中断请求或软件中断声明。 |
4、中断使能寄存器VICIntEnable(0xFFFFF010)
中断使能寄存器为读写寄存器。该寄存器使能分配位FIQ和IRQ的中断请求或软件中断。
表4.44 中断使能寄存器
位 | 功能描述 | 复位值 |
31:0 | 当读取该寄存器时,读1表示中断请求使能为FIQ或IRQ,写入1,使能中断请求或软件中断分配为FIQ或IRQ;写入0无效。 | 0 |
5、中断使能清零寄存器VICIntEnClr(0xFFFFF014)
该寄存器为只写寄存器。用于清除中断使能寄存器中一个或多个中断使能位。
表4.45中断使能清零寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 写0无效果。 | - |
1 | 写1则中断使能寄存器中对应位被清除。 |
6、中断选择寄存器VICIntSelect(0xFFFFF00C)
该寄存器将32个中断请求分别分配为FIQ或IRQ.
表4.46中断选择寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 表示对应位的中断请求类型为IRQ。 | 0 |
1 | 表示对应位的中断请求类型为FIQ。 |
7、IRQ状态寄存器VICIRQStatus(0xFFFFF000)
IRQ状态寄存器为只读寄存器。该寄存器读取使能并分配为IRQ的中断请求状态。
表4.47 IRQ状态寄存器
位 | 功能描述 | 复位值 |
31:0 | 某位读取出1代表该位中断请求使能且被分配为IRQ。 | 0 |
8、FIQ状态寄存器VICFIQStatus(0xFFFFF004)
FIQ状态寄存器为只读寄存器。该寄存器读取使能并分配为FIQ的中断请求状态。如果有超过一个请求分配为FIQ,FIQ服务程序可读取该寄存器来确定是哪一个(几个)请求被激活。
表4.48 FIQ状态寄存器
位 | 功能描述 | 复位值 |
31:0 | 某位读取出1代表该位中断请求使能且被分配为FIQ。 | 0 |
9、向量地址寄存器VICVectAddr0~31(0xFFFFF100~17C)
向量地址寄存器一共有32个,每个寄存器可读可写。这些寄存器对应保存32个向量IRQ中断的中断服务程序的入口地址。
表4.49 向量地址寄存器
位 | 功能描述 | 复位值 |
31:0 | 每个寄存器对应一个中断源,该寄存器保存该中断源服务程序入口地址,中断源见表4.54。 | 0x00000000 |
10、向量优先级寄存器VICVectPriority0~31(0xFFFFF200~27C)
向量优先级寄存器用于设置32个向量中断各自优先级。优先级从0~15,0为最高优先级,15最低。所有向量优先级寄存器复位值为最低优先级15,且寄存器允许写操作逐一更改32个向量中断优先级。当优先级相同的中断同时发生,向量地址寄存器VICVectAddr0~31数值小的优先被相应。
表4.50向量优先级寄存器
位 | 功能描述 | 复位值 |
3:0 | 设置相应向量中断优先级0~15。 | 0xF |
31:4 | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义。 | NA |
11、向量地址寄存器VICAddress(0xFFFFFF00)
当处理器响应一个IRQ中断后,该中断的中断服务程序(ISR)地址可以从向量地址寄存器VICAddress读出。而这个地址是由VIC从32向量地址寄存器VICVectPriority0~31其中一个读出装入进来的。
表4.51向量地址寄存器
位 | 功能描述 | 复位值 |
31:0 | 包含当前有效中断的ISR入口地址。该寄存器在ISR结束前必须被写入一个数值(任何值),以此更新VIC优先级硬件逻辑,其他时间对该寄存器写可能引起错误产生。 | 0 |
12、软件优先级屏蔽寄存器VICSWPrioriyMask(0xFFFFF024)
软件优先级屏蔽寄存器包含了16个中断优先级的屏蔽码。
表4.52软件优先级屏蔽寄存器
位 | 值 | 功能描述 | 复位值 |
15:0 | 0 | 中断优先级被屏蔽。 | 0xFFFF |
1 | 中断优先级未被屏蔽。 | ||
31:16 | - | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义。 | NA |
13、保护使能寄存器VICProtection(0xFFFFF020)
保护使能寄存器为可读写寄存器。该寄存器控制VIC寄存器是否能被用户软件在用户态下访问。且该寄存器本身只能在管态下访问。
表4.53保护使能寄存器
位 | 值 | 功能描述 | 复位值 |
0 | 0 | VIC寄存器可以在用户态或管态下访问。 | 0 |
1 | VIC寄存器只能在管态下访问。 | ||
31:1 | - | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义 | NA |
4.6.5 中断源
表4.54列出了每个外围模块的所有中断源。每个外围设备都有一条或多条中断线连接到向量中断控制器,而且每根中断线可能代表不止一种中断源。除了确定标准的ARM内核,中断线本身没有标志或优先级。
表4.54 连接VIC通道的中断源
功能模块 | 标志 | VIC通道 | 屏蔽码 |
WDT | 看门狗中断(WDINT) | 0 | 0x0000 0001 |
- | 软件中断保留 | 1 | 0x0000 0002 |
ARM内核 | 调试器接收命令中断 | 2 | 0x0000 0004 |
ARM内核 | 调试器发送命令中断 | 3 | 0x0000 0008 |
定时器0 | 匹配0~1(MR0,MR1),捕获0~1(CR0,CR1) | 4 | 0x0000 0010 |
功能模块 | 标志 | VIC通道 | 屏蔽码 |
定时器1 | 匹配0~2(MR0,MR1,MR2),捕获0~1(CR0,CR1) | 5 | 0x0000 0020 |
UART0 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 6 | 0x0000 0040 |
UART1 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI),Modem控制更改 | 7 | 0x0000 0080 |
PWM0, PWM1 | PWM0匹配0~6,PWM0捕获0,PWM1匹配0~6,PWM1捕获0~1 | 8 | 0x0000 0100 |
I2C0 | SI(状态改变) | 9 | 0x0000 0200 |
SPI,SSP0 | SPI中断标志(SPIF),模式错误(MODF),SSP0的Tx FIFO半空(TXRIS),SSP0的Rx FIFO 半满(RXRIS),SSP0接收超时(RTRIS),SSP0接收溢出(RORRIS) | 10 | 0x0000 0400 |
SSP1 | SSP1的Tx FIFO半空(TXRIS),SSP1的Rx FIFO 半满(RXRIS),SSP1接收超时(RTRIS),SSP1接收溢出(RORRIS) | 11 | 0x0000 0800 |
PLL | PLL锁定 | 12 | 0x0000 1000 |
RTC | 计数器增加(RTCCIF),报警(RTCALF),Sub-second中断(RTCSSF) | 13 | 0x0000 2000 |
系统控制 (外部中断) | 外部中断0(EINT0) | 14 | 0x0000 4000 |
外部中断1(EINT1) | 15 | 0x0000 8000 | |
外部中断2(EINT2) | 16 | 0x0001 0000 | |
外部中断3(EINT3) 注:EINT3与GPIO中断共享 | 17 | 0x0002 0000 | |
ADC0 | A/D转换器0 | 18 | 0x0004 0000 |
I2C1 | SI(状态改变) | 19 | 0x0008 0000 |
BOD | 掉电检测 | 20 | 0x0010 0000 |
以太网 | Wakeup,软件中断,传输成功,传输结束,传输错误,传输XX,接收成功,接收结束,接受错误,接受溢出 | 21 | 0x0020 0000 |
USB | USB_INT_REQ_LP,USB_INT_REQ_HP,USB_INT_REQ_DMA | 22 | 0x0040 0000 |
CAN | CAN命令,CAN0传输,CAN0接收,CAN1传输,CAN1接收 | 23 | 0x0080 0000 |
SD/MMC接口 | RxDataAvlbl ,TxDataAvlbl,RxFifoEmpty,TxFifoEmpty,RxFifoFull,TxFifoFull,RxFifoHalFull,TxFifoHalEmpty,RxActive,TxActive,CmdActive,DataBlockEnd,StartBitErr,DataEnd,CmdSent,CmdRespEnd,RxOverrun,TxUnderrun,DataTimeOut,CmdTimeOut,DataCrcFail,CmdCrcFail | 24 | 0x0100 0000 |
GP DMA | DMA通道0状态,DMA通道1状态 | 25 | 0x0200 0000 |
定时器2 | 匹配0~3,捕获0~1 | 26 | 0x0400 0000 |
定时器3 | 匹配0~3,捕获0~1 | 27 | 0x0800 0000 |
UART2 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 28 | 0x1000 0000 |
UART3 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 29 | 0x2000 0000 |
I2C2 | SI(状态改变) | 30 | 0x4000 0000 |
I2S | Irq_rx,Irq_tx | 31 | 0x8000 0000 |
4.6.6 VIC使用注意事项
1.VIC中断与片内RAM调试。如果在片内RAM中调试程序(JTAG调试)时需要使用中断,那么必须将中断向量重新映射到地址0x00000000,这样做是因为所有的异常向量都位于地址0x00000000及以上。通过将寄存器MEMMAP(位于系统控制模块当中)配置为用户RAM模式来实现这一点。另外,用户代码编译链接时应该使中断向量表装载到地址0x40000000。
2.多个FIQ中断。虽然可以选择多个中断源(通过设置VICIntSelect)为FIQ中断,但是只有一个专门的中断服务程序来响应所有出现的FIQ请求。因此,如果分配为FIQ的中断多于一个,FIQ中断服务程序就必须先读取VICFIQStatus的内容来识别具体有效的FIQ中断源,然后在进行相应中断处理。不过还是建议用户只设置一个FIQ中断,以确保FIQ中断延迟最小。
3.IRQ中断服务程序与VIC寄存器。在中断服务程序执行完毕后,对外设中断标准的清零将会对VIC寄存器(VICRawIntr,VICFIQStatus和VICIRQStatus)当中的对应位产生影响。另外,为了能够服务下次中断,必须在中断返回之前对VICVectAddr寄存器执行一次写操作(一般可写入0),该写操作将清零内部中断优先级硬件当中对应的标志。
4.VIC中断禁能操作。若要禁止VIC中断,则必须清零VICIntEnable寄存器中的对应位,这可以通过写VICIntEnClr寄存器实现。这同样应用于VICSoftInt和VICSoftIntClear。
4.6.7 应用举例
本节以实现按键的外部中断为例介绍向量中断控制器的使用,以及在IAR Embedded Workbench集成开发环境中编写中断服务程序的方法。
1、基本操作流程
设置IRQ/FIQ中断,若是IRQ中断,则可以设置为向量中断并分配中断优先级,然后设置中断使能,以及向量中断对应地址。当产生中断后,若是IRQ中断,则可以读取向量地址寄存器,然后跳转到相应服务代码。当退出中断时,对向量地址寄存器写0,通知VIC中断结束。
对于中断源(VIC通道)的IRQ/FIQ选择,由VICIntSelect寄存器控制,每个中断源于VICIntSelect的各位一一对应,比如VIC通道14(外部中断0)与VICIntSelect的Bit14位对应,设置该位为1,则分配为FIQ中断,否则为IRQ中断。
2、设置异常向量表
在LPC2400的启动代码中(在cstartup.s79文件中)首先设置异常向量表并设置各个模式下的堆栈指针,最后跳转到用户程序运行。异常向量表是一个包含8种异常情况的向量表,具体分配如表4.55所示。
表4.55 ARM异常向量表
地址 | 异常类型 |
0x0000 0000 | 复位 |
0x0000 0004 | 未定义指令 |
0x0000 0008 | 软件中断 |
0x0000 000C | 预取指令错误 |
0x0000 0010 | 取数据错误 |
0x0000 0014 | 保留 |
0x0000 0018 | IRQ中断 |
0x0000 001C | FIQ中断 |
系统一旦产生IRQ中断,LPC2400处理器会切换到IRQ模式,并且跳转到向量表0x00000018地址执行程序。如程序清单4.6②所示,在IRQ向量处使用的指令与其他向量不同,当CPU执行这条指令但还没有跳转时, [PC, # -0x0120]表示当前PC值减去0x0120 ,当前PC的值为0x00000020,减去0x0120为0xFFFFFF00。这是VIC特殊寄存器VICVectAddr的物理地址,该寄存器保存当前将要服务的IRQ中断服务程序的入口,所以用这条LDR指令就可以直接跳转到需要的中断服务程序中。
一旦产生FIQ中断,处理器会切换到FIQ模式,并且跳转到向量表0x0000001C地址执行程序。如程序清单4.6③所示,程序将跳到FIQ_Handler标号处,处理FIQ中断服务程序。
代码清单4.6 异常向量表设置
__program_start
LDR PC, =Reset_Handler ①
LDR PC, =Undef_Handler
LDR PC, =SWI_Handler
LDR PC, =PAbt_Handler
LDR PC, =DAbt_Handler
B .
LDR PC, [PC, # -0x0120] ②
LDR PC, =FIQ_Handler ③
3、VIC初始化
接下来,在LPC2400的启动代码中包含有VIC初始化程序,如代码清单4.7所列。程序首先禁止所有中断(代码清单4.7 ①),设置VICVectAddr寄存器的值为0,将所有中断设置为IRQ中断(代码清单4.7 ③)。最后在for循环中把所有向量地址寄存器内容设置为0(代码清单4.7 ④ ),向量优先级寄存器设置为0xF,最低中断优先级(代码清单4.7 ⑤)。
禁止所有中断是避免调试时一个中断没有响应就再次装入程序运行,而VIC状态错误不能正确识别中断。
代码清单4.7 init_VIC()——VIC初始化
VICIntEnClr = 0xffffffff; ①
VICVectAddr = 0; ②
VICIntSelect = 0; ③
/* set all the vector and vector control register to 0 */
for ( i = 0; i < VIC_SIZE; i++ ) // 32个中断服务向量复位
{
vect_addr = (DWORD *)(VIC_BASE_ADDR + VECT_ADDR_INDEX + i*4);
vect_cntl = (DWORD *)(VIC_BASE_ADDR + VECT_CNTL_INDEX + i*4);
*vect_addr = 0x0; //中断服务函数都指向开头 ④
*vect_cntl = 0xF; //优先级最低 ⑤
}
4、编写中断服务程序
IAR Embedded Workbench C/C++编译器支持ARM核的IRQ中断、FIQ快速中断和SWI软件中断,可以直接采用C语言编写中断函数。中断函数必须采用ARM模式编译,如果用户正在使用的是Thumb模式,应采用扩展关键字__arm或“#pragma type_attribute=__arm”指令将其转换到ARM模式。IRQ中断函数采用扩展字__irq或#pragma type_attribute=__irq”指令声明,如程序清单4.8 ①所示。FIQ中断函数采用扩展字__fiq或#pragma type_attribute=__fiq”指令声明。需要特别注意的是,IRQ和FIQ函数的返回值类型必须为void,并且不能带有参数。
在中断服务程序开始先清除外部中断标志寄存器EXTINT中EINT0位,之后进行中断服务处理,最后写VICVectAddr寄存器,更新VIC优先级逻辑,以相应下次外部中断。
代码清单4.8 EINT0_Handler()——外部中断服务函数
__irq __arm void EINT0_Handler (void) ①
{
EXTINT = EINT0; /* 清除EXTINT寄存器中EINT0位 */ ②
……/*中断服务*/
VICVectAddr = 0; /* 写VICVectAddr寄存器,更新VIC优先级逻辑 */ ③
}
5、安装外部中断服务程序
安装外部中断服务程序主要是初始化VIC的几个特别寄存器。Install_irq一共有3个参数:IntNumber为连接VIC的中断通道数,HandlerAddr为中断函数地址,Priority为该中断通道的优先级。
如代码清单4.9所示,函数首先设置中断使能清除寄存器VICIntEnClr的对应位,无效该中断。接着通过中断通道数IntNumber得到对应向量地址寄存器VICVectAddrX和向量优先级寄存器VICVectPriorityX地址。然后使用其余两个参数初始化这两个寄存器。最后置位中断使能寄存器VICIntEnable的对应,位使能该中断。
代码清单4.9 install_irq()——中断安装函数
DWORD install_irq( DWORD IntNumber, void *HandlerAddr, DWORD Priority )
{
DWORD *vect_addr;
DWORD *vect_cntl;
VICIntEnClr = 1 << IntNumber; /* Disable Interrupt */
vect_addr=(DWORD *)(VIC_BASE_ADDR+VECT_ADDR_INDEX+IntNumber*4);
vect_cntl=(DWORD *)(VIC_BASE_ADDR+VECT_CNTL_INDEX+IntNumber*4);
*vect_addr=(DWORD)HandlerAddr; /* set interrupt vector */
*vect_cntl=Priority;
VICIntEnable = 1 << IntNumber; /* Enable Interrupt */
}
代码清单4.10为调用install_irq函数安装EINT0_Handler中断服务函数的方法。
代码清单4.10安装EINT0_Handler函数
if ( install_irq( EINT0_INT, (void *)EINT0_Handler, HIGHEST_PRIORITY ) = = FALSE )
{
return (FALSE);
}
4.6 LPC2400最小系统
在嵌入式系统硬件开发过程中,直接设计和开发目标板硬件会有相当大的难度和风险,可以先通过设计最小系统,将所需IO引脚都引出到一个插针或者板板连接器(FPC)上。实际应用电路板(本文简称底板)另行设计,最小系统板可以同直插封装的器件一样与应用电路板想连接。下文将介绍LPC2400的最小系统。
如今如同LPC2400这样的MCU芯片将FLASH、SRAM以及一些总线等集成在一片芯片中,但是仍离不开一些外围电路的设计。这部分外围电路主要为MCU提供电源、时钟震荡、电压转换、I/O口保护和驱动等功能。LPC2400的最小系统如图4.15所示,这个最小系统此文暂称为核心板。
图4.15. LPC2400核心板
本核心板分为供电电路、时钟电路、复位电路以及外部存储器电路。现做简单介绍:
1.供电电路。核心板电源主要靠实际应用电路+3.3V提供,通过左右两排插针中的相关引脚提供。LPC2400芯片采用单电源(+3.3V)供电,这样可以简化电路设计,降低产品成本。电源纹波直接影响着整个电路的工作,为了得到稳定的电压,需要外接一些电容。这些电容分为两类,一类为储能电容,这些电容的电容值比较大,如1uF、10uF等。另一类为去耦电容,其电容值较小,如0.1uF、0.01uF等,它们可以达到抑制高频噪声的功用。
2.时钟电路。这部分电路主要有晶体振荡器、电容以及电阻组成。目前有些MCU已经将该部分集成到芯片内部,但是多是以RC振荡电路形式提供所需时钟,其稳定性得不到较高的保证。使用外部晶振可以使MCU得到稳定的时钟频率。
3.复位电路。虽然目前大部分的MCU都集成有上电复位电路,在系统上电时MCU会自动产生复位信号。但在设计初期可以加入手动复位电路以方便调试。外部复位电路可以采用阻容振荡电路,也可采用诸如MAX811或者SGM811之类的专用复位芯片。
4.外部存储器电路。目前中高档的MCU尤其是ARM内核的MCU都引出有外部总线。由于大中型软件系统对FLASH以及RAM的容量的需求以及内部集成FLASH造成成本偏高的现实,使得采用外部FLASH作为存储器件最为合适。
由于LPC2478与LPC2470、LPC2468以及LPC2460等恩智浦LPC2400系列ARM7单片机在引脚上是兼容的,所以LPC2400最小系统同样也适用于上述芯片。
如图4.15所示,左右两排插针引出了实际应用电路板上所需的功能引脚,上下两排焊盘引出了LPC2478所能提供的外部总线。
LPC2400最小系统包括一下几个部分:电源电路、时钟电路、复位电路、JTAG调试电路以及功能接口电路等。其中各个部分功能如下:
1、 时钟电路给MCU提供一个外部12MHz的以及一个32.768MHz的石英振荡器。
2、 复位电路是通过引脚的方式与底板上的手动复位相连。通过底板复位电路提供手动复位信号。
3、 JTAG电路可以让用户方便的通过仿真器调试或者下载程序。
4、 外部存储电路,即有板载外部存储器(如Nor Flash、Nand Flash和SDRAM),又将外部总线引出方便用户通过外部总线扩展其他器件。
根据电路板的工作环境,可能会对电路板提出不同的要求。诸如噪声以及干扰较强的场合,以及对系统稳定性、可靠性要求较高时,印刷电路板会采用多层板设计。一般地,6层板噪声比4层板低10dB,4层板比双面板的噪声低20dB。但板层越多,相应的成本也就越高。如图4.15所示的核心板采用6层板设计,为测试提供了稳定可靠的电路。
习题:
4.1 简单说明LPC2400系列芯片复位时的处理流程。
4.2 LPC2400系列芯片的存储器空间是如何分布的?
4.3 LPC2400芯片的引脚通常都是复用的,当要使用引脚的某个功能时,应如何进行设置?
4.4 简述使能PLL的工作过程。
4.5 如果LPC2400使用的外部晶振频率为12MHz,计算最大的系统时钟频率CCLK为多少,此时M值和N值各为多少,并编写设置PLL的程序段。
4.6 LPC2400有哪些降低功耗的措施?
4.7 如果要使用外部中断0来唤醒掉电的LPC2400,应设置哪些寄存器,各寄存器的值应为多少?写出其程序段。
ARM内核属于RISC结构,所以其指令集有着一些独特的特点:指令长度固定,指令格式的种类少,寻址方式简单。由于ARM处理器采用固定长度的32位指令,因此处理器内部硬件设计能够被简化。ARM处理器内部的指令译码采用硬布线逻辑,不使用微程序控制,以减少指令的译码时间,大部分指令可以在一个时钟周期内完成。
ARM处理器的指令按功能可分为七大类:加载/存储指令、数据处理指令、乘法指令、跳转指令、程序状态寄存器处理指令、协处理器指令和异常中断指令。
需要特别指出的是,ARM处理器的指令集是加载/存储型的,也即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。
按照操作数的特点分,ARM指令可以分为无操作数指令、单操作数指令、双操作数指令和三操作数指令。每条指令都由操作码域、条件码域、条件码设置域、目标操作数、第一操作数寄存器和第二操作数组成。
3.1.2 ARM指令的格式
每条ARM指令都是32位的,其格式如下:
31 28 27 25 24 21 20 19 16 15 12 11 0
条件码 | 类别码 | 操作码 | S | 目的寄存器 | 第一操作数 | 第二操作数 |
用ARM指令助记符表示为:
<opcode> {<cond>} {S} <Rd>, <Rn>, <shift_op2>
每个域的含义如下:
1) <opcode>:操作码域,指令编码的助记符;
2) {<cond>}:条件码域,指令允许执行的条件编码。花括号表示此项可缺省。
ARM指令的一个重要特点是可以条件执行,每条ARM指令的条件码域包含4位条件码,共16种。几乎所有指令均根据CPSR中条件码的状态和指令条件码域的设置有条件的执行。当指令执行条件满足时,指令被执行,否则被忽略。指令条件码及其助记符后缀表示参见表3.1。
每种条件码可用两个字符表示,这两个字符可以作为后缀添加在指令助记符的后面和指令同时使用。例如,跳转指令B可以加上后缀EQ变为BEQ,表示“相等则跳转”,即当CPSR中的Z标志置位时发生跳转。
表3.1 指令的条件码
条件码 | 助记符后缀 | 标 志 | 含 义 |
0000 | EQ | Z置位 | 相等 |
0001 | NE | Z清零 | 不相等 |
0010 | CS | C置位 | 无符号数大于或等于 |
0011 | CC | C清零 | 无符号数小于 |
0100 | MI | N置位 | 负数 |
0101 | PL | N清零 | 正数或零 |
0110 | VS | V置位 | 溢出 |
0111 | VC | V清零 | 未溢出 |
1000 | HI | C置位Z清零 | 无符号数大于 |
1001 | LS | C清零Z置位 | 无符号数小于或等于 |
1010 | GE | N等于V | 带符号数大于或等于 |
1011 | LT | N不等于V | 带符号数小于 |
1100 | GT | Z清零且(N等于V) | 带符号数大于 |
1101 | LE | Z置位或(N不等于V) | 带符号数小于或等于 |
1110 | AL | 忽略 | 无条件执行 |
3) {S}:条件码设置域。这是一个可选项,当在指令中设置{S}域时,指令执行的结果将会影响程序状态寄存器CPSR中相应的状态标志。
例如:
ADD R0,R1,R2; R1与R2的和存放到R0寄存器中,不影响状态寄存器
ADDS R0,R1,R2; 执行加法的同时影响状态寄存器
指令中比较特殊的是CMP指令,它不需要加S后缀就默认地根据计算结构更改程序状态寄存器。
4) <Rd>:目的操作数。ARM指令中的目的操作数总是一个寄存器。如果<Rd>与第一操作数寄存器<Rn>相同,也必须要指明,不能缺省。
5) <Rn>:第一操作数。ARM指令中的第一操作数也必须是个寄存器。
6) <shift_op2>:第二操作数。在第二操作数中可以是寄存器、内存存储单元或者立即数。
由于第二操作数只有12个bit,用第二操作数表示立即数时,其取值范围为0~212-1,要表示超出这个范围的立即数,通常要依靠伪指令实现。
3.2 ARM指令的寻址方式
所谓寻址方式就是处理器根据指令中给出的地址信息来寻找物理地址的方式。目前ARM指令系统支持如下几种常见的寻址方式。
3.2.1 立即寻址
立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址。例如以下指令:
ADD R0,R0,#1 ;R0←R0+1
ADD R0,R0,#0x3f ;R0←R0+0x3f
在以上两条指令中,第二操作数即为立即数,要求以“#”为前缀,对于以十六进制表示的立即数,还要求在“#”后加上“0x”,以二进制表示的立即数,要求在“#”后加上“%”。
当立即数大于第二操作数的表示范围时,通常用以下伪指令实现:
LDR R0,=#0xffff0000
3.2.2 寄存器寻址
寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。以下指令:
ADD R0,R1,R2 ;R0←R1+R2
该指令的执行效果是将寄存器R1和R2的内容相加,其结果存放在寄存器R0中。
3.2.3 寄存器间接寻址
寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。例如以下指令:
ADD R0,R1,[R2] ;R0←R1+[R2]
LDR R0,[R1] ;R0←[R1]
STR R0,[R1] ;[R1]←R0
在第一条指令中,以寄存器R2的值作为操作数的地址,在存储器中取得一个操作数后与R1相加,结果存入寄存器R0中。
第二条指令将以R1的值为地址的存储器中的数据传送到R0中。
第三条指令将R0的值传送到以R1的值为地址的存储器中。
3.2.4 基址变址寻址
基址变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基地址附近的地址单元。采用变址寻址方式的指令常见有以下几种形式,如下所示:
LDR R0,[R1,#4] ;R0←[R1+4]
LDR R0,[R1,#4]! ;R0←[R1+4]、R1←R1+4
LDR R0,[R1] ,#4 ;R0←[R1]、R1←R1+4
LDR R0,[R1,R2] ;R0←[R1+R2]
在第一条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中。
在第二条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第三条指令中,以寄存器R1的内容作为操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第四条指令中,将寄存器R1的内容加上寄存器R2的内容形成操作数的有效地址,从而取得操作数存入寄存器R0中。
3.2.5 多寄存器寻址
多寄存器寻址是ARM处理器特有的一种寻址方式。由于ARM内核有较多的通用寄存器,采用多寄存器寻址方式,一条指令可以一次完成多个寄存器值的传送。这种寻址方式可以用一条指令完成传送最多16个通用寄存器的值。例如以下指令:
LDMIA R0,{R1,R2,R3,R4} ;R1←[R0]
;R2←[R0+4]
;R3←[R0+8]
;R4←[R0+12]
该指令的后缀IA表示在每次执行完加载/存储操作后,R0按字长度增加,因此,指令可将连续存储单元的值传送到R1~R4。
多个连续的寄存器可以用“-”符号连接;不连续的寄存器用“,”分隔书写,如上例可写成:
LDMIA R0,{R1-R4}
LDMIA R0,{R1-R3,R4}
3.2.6 寄存器移位寻址
寄存器移位寻址是ARM指令集特有的寻址方式。ARM处理器内嵌桶型移位器(Barrel Shifter),支持数据的各种移位操作。当第二操作数为寄存器时,可以加入移位操作选项对它进行各种移位操作。
移位操作包括如下6种类型:
1、LSL(或ASL)逻辑(算术)左移
寻址格式:
通用寄存器,LSL(或ASL) 操作数
完成对通用寄存器中的内容进行逻辑(或算术)的左移操作,按操作数所指定的数量向左移位,低位用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, LSL#2 ;将R1中的内容左移两位后传送到R0中。
2、LSR逻辑右移
寻址格式:
通用寄存器,LSR 操作数
完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, LSR#2 ;将R1中的内容右移两位后传送到R0中,左端用零来填充。
3、ASR算术右移
寻址格式:
通用寄存器,ASR 操作数
完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用第31位的值来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, ASR#2 ;将R1中的内容右移两位后传送到R0中,左端用第31位的值来填充。
4、ROR循环右移
寻址格式:
通用寄存器,ROR 操作数
完成对通用寄存器中的内容进行循环右移的操作,按操作数所指定的数量向右循环移位,左端用右端移出的位来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。显然,当进行32位的循环右移操作时,通用寄存器中的值不改变。
如:
MOV R0, R1, ROR#2 ;将R1中的内容循环右移两位后传送到R0中。
5、RRX带扩展的循环右移
寻址格式:
通用寄存器,RRX 操作数
完成对通用寄存器中的内容进行带扩展的循环右移的操作,按操作数所指定的数量向右循环移位,左端用进位标志位C来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
如:
MOV R0, R1, RRX#2 ;将R1中的内容进行带扩展的循环右移两位后传送到R0中。
3.2.7 相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。以下程序段完成子程序的调用和返回,跳转指令BL采用了相对寻址方式:
BL NEXT ;跳转到子程序NEXT处执行
……
NEXT
……
MOV PC,LR ;从子程序返回
3.2.8 堆栈寻址
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,根据堆栈的生成方式,又可以分为递增堆栈(Ascending Stack)和递减堆栈(Decending Stack)。当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。这样就有四种类型的堆栈工作方式,ARM微处理器支持这四种类型的堆栈工作方式,即:
1. 满递增堆栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生成。
2. 满递减堆栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生成。
3. 空递增堆栈(EA):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。
4. 空递减堆栈(ED):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。
3.3 ARM指令集
本节对ARM指令集的七大类指令进行详细的描述。
3.3.1 加载/存储指令
ARM处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。常用的加载存储指令如下:
1、LDR指令
LDR指令的格式为:
LDR{条件} 目的寄存器,<存储器地址>
LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,且寻址方式灵活多样,请读者认真掌握。
如:
LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0。
LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
LDR R0,[R1,R2] ! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将
;新地址R1+R2写入R1。
LDR R0,[R1,#8] ! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新
;地址R1+8写入R1。
LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地
;址R1+R2写入R1。
LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4的字数据读入寄存器R0,
;并将新地址R1+R2×4写入R1。
LDR R0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地
;址R1+R2×4写入R1。
2、STR指令
STR指令的格式为:
STR{条件} 源寄存器,<存储器地址>
STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR。
如:
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
LDR/STR指令都可以加B、H、SB、SH的后缀,分别表示加载/存储字节、半字、带符号的字节、带符号的半字。如LDRB指令表示从存储器加载一个字节进寄存器。当使用这些后缀时,要注意所使用的存储器要支持访问的数据宽度。
3、LDM(或STM)批量数据加载/存储指令
LDM(或STM)指令的格式为:
LDM(或STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}
LDM(或STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。其中,{类型}为以下几种情况:
IA 每次传送后地址加1;
IB 每次传送前地址加1;
DA 每次传送后地址减1;
DB 每次传送前地址减1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。
基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。
{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。
如:
STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈。
LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。
4、SWP数据交换指令
SWP指令的格式为:
SWP{条件} 目的寄存器,源寄存器1,[源寄存器2]
SWP指令用于将源寄存器2所指向的存储器中的字数据传送到目的寄存器中,同时将源寄存器1中的字数据传送到源寄存器2所指向的存储器中。显然,当源寄存器1和目的寄存器为同一个寄存器时,指令交换该寄存器和存储器的内容。
如:
SWP R0,R1,[R2] ;将R2所指向的存储器中的字数据传送到R0,同时将R1中的字数据传送到R2所指向的存储单元。
SWP R0,R0,[R1] ;该指令完成将R1所指向的存储器中的字数据与R0中的字数据交换。
3.3.2 数据处理指令
数据处理指令可分为数据传送指令、算术逻辑运算指令和比较指令等。
数据传送指令用于在寄存器和存储器之间进行数据的双向传输。
算术逻辑运算指令完成常用的算术与逻辑的运算,该类指令不但将运算结果保存在目的寄存器中,同时更新CPSR中的相应条件标志位。
比较指令不保存运算结果,只更新CPSR中相应的条件标志位。
1、 MOV指令
MOV指令的格式为:
MOV{条件}{S} 目的寄存器,源操作数
MOV指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。其中S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
如:
MOV R1,R0 ;将寄存器R0的值传送到寄存器R1
MOV PC,R14 ;将寄存器R14的值传送到PC,常用于子程序返回
MOV R1,R0,LSL#3 ;将寄存器R0的值左移3位后传送到R1
2、 MVN指令
MVN指令的格式为:
MVN{条件}{S} 目的寄存器,源操作数
MVN指令可完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。与MOV指令不同之处是在传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中。其中S决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
如:
MVN R0,#0 ;将立即数0取反传送到寄存器R0中,完成后R0=-1
3、 CMP指令
CMP指令的格式为:
CMP{条件} 操作数1,操作数2
CMP指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位表示的是操作数1与操作数2的关系(大、小、相等),例如,当操作数1大于操作操作数2,则此后的有GT 后缀的指令将可以执行。
如:
CMP R1,R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
CMP R1,#100 ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位
4、 CMN指令
CMN指令的格式为:
CMN{条件} 操作数1,操作数2
CMN指令用于把一个寄存器的内容和另一个寄存器的内容或立即数取反后进行比较,同时更新CPSR中条件标志位的值。该指令实际完成操作数1和操作数2相加,并根据结果更改条件标志位。
如:
CMN R1,R0 ;将寄存器R1的值与寄存器R0的值相加,并根据结果设置CPSR的标志位
CMN R1,#100 ;将寄存器R1的值与立即数100相加,并根据结果设置CPSR的标志位
5、 TST指令
TST指令的格式为:
TST{条件} 操作数1,操作数2
TST指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。操作数1是要测试的数据,而操作数2是一个位掩码,该指令一般用来检测是否设置了特定的位。
如:
TST R1,#%1 ;用于测试在寄存器R1中是否设置了最低位(%表示二进制数)
TST R1,#0xffe ;将寄存器R1的值与立即数0xffe按位与,并根据结果设置CPSR的标志位
6、 TEQ指令
TEQ指令的格式为:
TEQ{条件} 操作数1,操作数2
TEQ指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的异或运算,并根据运算结果更新CPSR中条件标志位的值。该指令通常用于比较操作数1和操作数2是否相等。
如:
TEQ R1,R2 ;将寄存器R1的值与寄存器R2的值按位异或,并根据结果设置CPSR的标志位
7、 ADD指令
ADD指令的格式为:
ADD{条件}{S} 目的寄存器,操作数1,操作数2
ADD指令用于把两个操作数相加,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
如:
ADD R0,R1,R2 ; R0 = R1 + R2
ADD R0,R1,#256 ; R0 = R1 + 256
ADD R0,R2,R3,LSL#1 ; R0 = R2 + (R3 << 1)
8、 ADC指令
ADC指令的格式为:
ADC{条件}{S} 目的寄存器,操作数1,操作数2
ADC指令用于把两个操作数相加,再加上CPSR中的C条件标志位的值,并将结果存放到目的寄存器中。它使用一个进位标志位,这样就可以做比32位大的数的加法,注意不要忘记设置S后缀来更改进位标志。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
以下指令序列完成两个128位数的加法,第一个数由高到低存放在寄存器R7~R4,第二个数由高到低存放在寄存器R11~R8,运算结果由高到低存放在寄存器R3~R0:
ADDS R0,R4,R8 ; 加低端的字
ADCS R1,R5,R9 ; 加第二个字,带进位
ADCS R2,R6,R10 ; 加第三个字,带进位
ADC R3,R7,R11 ; 加第四个字,带进位
9、 SUB指令
SUB指令的格式为:
SUB{条件}{S} 目的寄存器,操作数1,操作数2
SUB指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
如:
SUB R0,R1,R2 ; R0 = R1 - R2
SUB R0,R1,#256 ; R0 = R1 - 256
SUB R0,R2,R3,LSL#1 ; R0 = R2 - (R3 << 1)
10、SBC指令
SBC指令的格式为:
SBC{条件}{S} 目的寄存器,操作数1,操作数2
SBC指令用于把操作数1减去操作数2,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
如:
SUBS R0,R1,R2 ; R0 = R1 - R2 - !C,并根据结果设置CPSR的进位标志位
11、RSB指令
RSB指令的格式为:
RSB{条件}{S} 目的寄存器,操作数1,操作数2
RSB指令称为逆向减法指令,用于把操作数2减去操作数1,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
如:
RSB R0,R1,R2 ; R0 = R2 – R1
RSB R0,R1,#256 ; R0 = 256 – R1
RSB R0,R2,R3,LSL#1 ; R0 = (R3 << 1) - R2
12、RSC指令
RSC指令的格式为:
RSC{条件}{S} 目的寄存器,操作数1,操作数2
RSC指令用于把操作数2减去操作数1,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
如:
RSC R0,R1,R2 ; R0 = R2 – R1 - !C
13、AND指令
AND指令的格式为:
AND{条件}{S} 目的寄存器,操作数1,操作数2
AND指令用于在两个操作数上进行逻辑与运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于屏蔽操作数1的某些位。
如:
AND R0,R0,#3 ; 该指令保持R0的0、1位,其余位清零。
14、ORR指令
ORR指令的格式为:
ORR{条件}{S} 目的寄存器,操作数1,操作数2
ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位。
如:
ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。
15、EOR指令
EOR指令的格式为:
EOR{条件}{S} 目的寄存器,操作数1,操作数2
EOR指令用于在两个操作数上进行逻辑异或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于反转操作数1的某些位。
如:
EOR R0,R0,#3 ; 该指令反转R0的0、1位,其余位保持不变。
16、BIC指令
BIC指令的格式为:
BIC{条件}{S} 目的寄存器,操作数1,操作数2
BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。
如:
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。
3.3.3 乘法指令与乘加指令
ARM微处理器支持的乘法指令与乘加指令共有6条,可分为运算结果为32位和运算结果为64位两类。与前面的数据处理指令不同,指令中的所有操作数、目的寄存器必须为通用寄存器,不能对操作数使用立即数或被移位的寄存器,同时,目的寄存器和操作数1必须是不同的寄存器。
1、 MUL指令
MUL指令的格式为:
MUL{条件}{S} 目的寄存器,操作数1,操作数2
MUL指令完成将操作数1与操作数2的乘法运算,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数或无符号数。
如:
MUL R0,R1,R2 ;R0 = R1 × R2
MULS R0,R1,R2 ;R0 = R1 × R2,同时设置CPSR中的相关条件标志位
2、 MLA指令
MLA指令的格式为:
MLA{条件}{S} 目的寄存器,操作数1,操作数2,操作数3
MLA指令完成将操作数1与操作数2的乘法运算,再将乘积加上操作数3,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数或无符号数。
如:
MLA R0,R1,R2,R3 ;R0 = R1 × R2 + R3
MLAS R0,R1,R2,R3 ;R0 = R1 × R2 + R3,同时设置CPSR中的相关条件标志位
3、 SMULL指令
SMULL指令的格式为:
SMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数。
如:
SMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位
;R1 = (R2 × R3)的高32位
4、 SMLAL指令
SMLAL指令的格式为:
SMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
SMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的有符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
如:
SMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位 + R0
;R1 = (R2 × R3)的高32位 + R1
5、 UMULL指令
UMULL指令的格式为:
UMULL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMULL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
如:
UMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位
;R1 = (R2 × R3)的高32位
6、 UMLAL指令
UMLAL指令的格式为:
UMLAL{条件}{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
UMLAL指令完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
如:
UMLAL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位 + R0
;R1 = (R2 × R3)的高32位 + R1
3.3.4 跳转指令
跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转:使用专门的跳转指令、直接向程序计数器PC写入跳转地址值。
直接向PC写入跳转地址值,可以实现在4GB的地址空间中任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。
使用跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转。
1、 B指令
B指令的格式为:
B{条件} 目标地址
B指令是最简单的跳转指令。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(前后32MB的地址空间)。
如:
B Label ;程序无条件跳转到标号Label处执行
CMP R1,#0 ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
BEQ Label
2、 BL指令
BL指令的格式为:
BL{条件} 目标地址
BL 是另一个跳转指令,但跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14 的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。
如:
BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中
Label标号处可以是一个子程序,在子程序的最后可以使用MOV PC,LR指令跳回BL Label指令处的下一条指令继续执行。
3、 BLX指令
BLX指令的格式为:
BLX 目标地址
BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态由ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中。因此,当子程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成。
4、 BX指令
BX指令的格式为:
BX{条件} 目标地址
BX指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是Thumb指令。
3.3.5 程序状态寄存器访问指令
ARM指令不允许直接操作程序状态寄存器CPSR和SPSR。可以通过程序状态寄存器访问指令,在程序状态寄存器和通用寄存器之间传送数据,然后在通用寄存器中进行处理。
1、 MRS指令
MRS指令的格式为:
MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)
MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。该指令一般用在以下几种情况:
1) 当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。
2) 当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。
如:
MRS R0,CPSR ;传送CPSR的内容到R0
MRS R0,SPSR ;传送SPSR的内容到R0
2、 MSR指令
MSR指令的格式为:
MSR{条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数
MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:
位[31:24]为条件标志位域,用f表示;
位[23:16]为状态位域,用s表示;
位[15:8]为扩展位域,用x表示;
位[7:0]为控制位域,用c表示;
该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
如:
MSR CPSR,R0 ;传送R0的内容到CPSR
MSR SPSR,R0 ;传送R0的内容到SPSR
MSR CPSR_c,R0 ;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域
3.3.6 协处理器指令
ARM微处理器可支持多达16个协处理器,用于各种协处理操作,在程序执行的过程中,每个协处理器只执行针对自身的协处理指令,忽略ARM处理器和其他协处理器的指令。
ARM的协处理器指令主要用于ARM处理器初始化ARM协处理器的数据处理操作,以及在ARM处理器的寄存器和协处理器的寄存器之间传送数据,和在ARM协处理器的寄存器和存储器之间传送数据。
1、CDP指令
CDP指令的格式为:
CDP{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
CDP指令用于ARM处理器通知ARM协处理器执行特定的操作,若协处理器不能成功完成特定的操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器和源寄存器均为协处理器的寄存器,指令不涉及ARM处理器的寄存器和存储器。
如:
CDP P3,2,C12,C10,C3,4 ;该指令完成协处理器P3的初始化
2、LDC指令
LDC指令的格式为:
LDC{条件}{L} 协处理器编码,目的寄存器,[源寄存器]
LDC指令用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
如:
LDC P3,C4,[R0] ;将ARM处理器的寄存器R0所指向的存储器中的字数据传送到协处理器P3的寄存器C4中。
3、STC指令
STC指令的格式为:
STC{条件}{L} 协处理器编码,源寄存器,[目的寄存器]
STC指令用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
如:
STC P3,C4,[R0] ;将协处理器P3的寄存器C4中的字数据传送到ARM处理器的寄存器R0所指向的存储器中。
4、MCR指令
MCR指令的格式为:
MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
MCR指令用于将ARM处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,源寄存器为ARM处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄存器。
如:
MCR P3,3,R0,C4,C5,6 ;该指令将ARM处理器寄存器R0中的数据传送到协处理器P3的寄存器C4和C5中。
5、MRC指令
MRC指令的格式为:
MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
MRC指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器为ARM处理器的寄存器,源寄存器1和源寄存器2均为协处理器的寄存器。
如:
MRC P3,3,R0,C4,C5,6 ;该指令将协处理器P3的寄存器中的数据传送到ARM处理器寄存器中。
3.3.7 异常中断指令
1、SWI指令
SWI指令的格式为:
SWI{条件} 24位的立即数
SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的异常处理程序中提供相应的系统服务,指令中24位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时,参数通过其他通用寄存器传递。
如:
SWI 0x02 ;该指令调用操作系统编号位02的系统例程。
2、BKPT指令
BKPT指令的格式为:
BKPT 16位的立即数
BKPT指令产生软件断点中断,可用于程序的调试。
3.4 Thumb指令集
为兼容数据总线宽度为16位的应用系统,ARM体系结构除了支持执行效率很高的32位ARM指令集以外,同时支持16位的Thumb指令集。Thumb指令集是ARM指令集的一个子集,允许指令编码为16位的长度。与等价的32位代码相比较,Thumb指令集在保留32位代码优势的同时,大大的节省了系统的存储空间。
所有的Thumb指令都有对应的ARM指令,而且Thumb的编程模型也对应于ARM的编程模型,在应用程序的编写过程中,只要遵循一定调用的规则,Thumb子程序和ARM子程序就可以互相调用。当处理器在执行ARM程序段时,称ARM处理器处于ARM工作状态,当处理器在执行Thumb程序段时,称ARM处理器处于Thumb工作状态。
与ARM指令集相比较,Thumb指令集中的数据处理指令的操作数仍然是32位,指令地址也为32位,但Thumb指令集为实现16位的指令长度,舍弃了ARM指令集的一些特性,如大多数的Thumb指令是无条件执行的,而几乎所有的ARM指令都是有条件执行的;大多数的Thumb数据处理指令的目的寄存器与其中一个源寄存器相同。
由于Thumb指令的长度为16位,即只用ARM指令一半的位数来实现同样的功能,所以,要实现特定的程序功能,所需的Thumb指令的条数较ARM指令多。在一般的情况下,Thumb指令与ARM指令的时间效率和空间效率关系为:
— Thumb代码所需的存储空间约为ARM代码的60%~70%
— Thumb代码使用的指令数比ARM代码多约30%~40%
— 若使用32位的存储器,ARM代码比Thumb代码快约40%
— 若使用16位的存储器,Thumb代码比ARM代码快约40%~50%
— 与ARM代码相比较,使用Thumb代码,存储器的功耗会降低约30%
显然,ARM指令集和Thumb指令集各有其优点,若对系统的性能有较高要求,应使用32位的存储系统和ARM指令集,若对系统的成本及功耗有较高要求,则应使用16位的存储系统和Thumb指令集。当然,若两者结合使用,充分发挥其各自的优点,会取得更好的效果。
3.5 伪指令
ARM编译器一般都支持汇编语言的程序设计和C/C++语言的程序设计,以及两者的混合编程。在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对应的操作码,通常称这些特殊指令助记符为伪指令,他们所完成的操作称为伪操作。伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,这些伪指令仅在汇编过程中起作用,一旦汇编结束,伪指令的使命就完成。
在ARM的汇编程序中,有如下几种伪指令:ARM伪指令、符号定义伪指令、数据定义伪指令、段定义伪指令、模块控制伪指令、汇编控制伪指令、宏处理伪指令等等。
需要特别指出的是,除了几条ARM伪指令以外,其它的伪指令依赖于编译器。也就是说,不同的ARM编译器的伪指令集是不相同的。例如,ADS编译器的段定义伪指令为AREA,而IAR编译器的段定义伪指令为RSEG和ASEG。这种情况使得不同编译器下编出的ARM汇编程序是不同的。读者在阅读不同学习材料时应注意分辨在不同编译器下ARM汇编程序的区别。
本书介绍的是IAR EWARM编译器支持的ARM汇编伪指令。
3.5.1 ARM伪指令
ARM伪指令不是ARM指令集中的指令。它可以象其它ARM指令一样使用,但在编译时这些指令将被等效的ARM指令所取代。
1、LDR-大范围地址读取
LDR伪指令的格式为:
LDR{条件} reg,=expr/label_expr
reg为加载的目的寄存器;expr为32位立即数;label_expr为地址表达式或外部表达式。
LDR伪指令将32位常量或一个32位地址加载到指定寄存器。
如:
LDR R0,=#0x12345 ;加载32位立即数0x12345到寄存器R0
LDR R0,=DATA_BUF+60 ;加载DATA_BUF地址+60
2、ADR-小范围地址读取
ADR伪指令的格式为:
ADR{条件} reg,expr
reg为加载的目的寄存器;expr为相对偏移表达式,非字对齐时取值范围为-255~255字节,字对齐时取值范围为-1020~1020字节。
ADR伪指令将基于当前PC相对偏移的地址值读取到寄存器中。
如:
Start: MOV R0,#10
ADR R4,start ;相当于SUB R4,PC,#0x0c
3、ADRL-中范围地址读取
ADRL伪指令与ADR类似,不同在expr的取值范围,非字对齐时取值范围为64KB,字对齐时取值范围为256KB。
4、NOP-空操作
3.5.2 数据定义伪指令
1、DCB和DC8
该伪指令的格式为:
标号 DCB或DC8 表达式
DCB和DC8伪指令用于分配一片连续的8位字节存储单元,并用伪指令中指定的表达式初始化。其中表达式可以为0~255的数字或字符串。
如:
Str DCB “This is a test!” ;分配一个字符串,每个字符8位字节
2、DCW和DC16、DCD和DC32
与DCB和DC8用法相同,不同的是分别分配16位半字节单元和32位字单元。
3、DF32和DF64
分别表示32位的单精度浮点数和64位的双精度浮点数。
4、DS8、DS16、DS24和DS32
分别用于保留8位字节、16位半字、24位字和32位字的存储器空间。
如:
Dataspace DS8 100 ;保留100个8位字节的存储器空间
3.5.3 符号定义伪指令
1、=、ALIAS和EQU
该伪指令的格式为:
标号 = 表达式
标号 ALIAS 表达式
标号 EQU 表达式
伪指令EQU和=可用于为程序模块中的常量、标号等赋值,定义的局部符号仅在其所在的模块内有效。伪指令ALIAS为符号起个别名。定义的符号采用PUBLIC伪指令声明其属性可使之被其它模块引用,引用其它模块内符号时必须采用EXTERN伪指令声明其属性。
如:
Test EQU 50 ;定义符号Test的值为50
2、ASSIGN、SET、SETA和VAR
用法与EQU等类似,可用于定义一个变量符号。采用VAR定义的变量符号不能用PUBLIC声明其属性。
3、DEFINE
用于定义在整个程序文件内都有效的全局符号。该符号可以被文件内的所有程序模块引用,但不能在同一文件内重新定义。
4、LIMIT
该伪指令的格式为:
LIMIT 表达式, 最小值, 最大值, 提示信息
用于检查表达式的值是否位于给定范围之内。如果表达式值的范围超限,则输出提示信息。
如:
Speed VAR 23 ;定义符号speed的值为23
LIMIT speed,10,30,…speed out of range… ;检查speed的值是否超限
5、EXTERN(或IMPORT)
该伪指令的格式为:
EXTERN 符号,[符号]……
EXTERN伪指令用于通知汇编器,要使用的符号在其它源文件中定义,但要在当前源文件中引用。
如:
Name Start ;程序模块Start
EXTERN Main ;告诉汇编器Main符号在其它源文件中定义
……
BL Main ;在本模块中引用Main符号
END
6、PUBLIC(或EXPORT)
该伪指令的格式为:
PUBLIC 符号,[符号]……
PUBLIC伪指令用于在程序中声明一个全局符号,该符号可在其它文件中引用。
7、REQUIRE
PUBLIC伪指令用于将一个符号标记为已经被引用。
3.5.4 段定义伪指令
1、ASEG和ASEGN
该伪指令的格式为:
ASEG [起始地址[(对齐)]]
ASEGN 段名[:存储器类型],地址
ASEG伪指令用于定义一个绝对段,并设置段的起始地址。不指定地址值时第一个段默认起始地址为0,后续段地址依次递增。ASEGN伪指令用于设置指定段的绝对起始地址,并允许规定段类型。存储器类型可以为CODE(代码段)、DATA(数据段)、STACK(堆栈段)。
如:
ASEG 0 ;定义一个绝对段,起始地址0
ASEGN CODE:CODE,0 ;定义一个名为CODE的代码段,起始地址0
2、RSEG
该伪指令的格式为:
RSEG 段名[:存储器类型][:(NO)ROOT|(NO)REORDER|SORT][(对齐)]
RSEG伪指令用于定义一个可重定位段,段的起始地址由汇编器临时分配。单个模块中最多可定义65536个可重定位段。
如:
RSEG CODE:CODE:ROOT(2) ;定义一个名为CODE的可重定位代码段,用户权限为ROOT(可读写),段内存储器对齐方式为4字节对齐
3、DATA
该伪指令的格式为:
DATA 段名[:存储器类型][(对齐)]
DATA伪指令可以在代码段内定义一个数据区。
如:
RSEG CODE:CODE:ROOT(2)
DATA
f1: DC32 subrtn
4、STACK
该伪指令的格式为:
COMMON 段名[:存储器类型][(对齐)]
STACK伪指令用于定义一个堆栈段,用作堆栈的存储器地址从高向低变化,而用作可重定位段的存储器地址是从低向高变化。
5、COMMON
该伪指令的格式为:
COMMON 段名[:存储器类型][(对齐)]
COMMON伪指令用于定义公共段,各源文件中同名的COMMON段共享同一段内存。它的典型应用是多个不同子程序共享一段数据存储区。中断向量表也可安排在COMMON段,以便允许从多个服务子程序访问。
6、CODE16和CODE32
CODE16伪指令用于告诉汇编器,其后的指令序列为16位的Thumb指令;CODE32伪指令用于告诉汇编器,其后的指令序列为32位的ARM指令。因此,在使用ARM指令和Thumb指令混合编程的代码中,可用这两条伪指令进行切换。但需要注意的是,它们只通知汇编器其后指令的类型,并不能对处理器进行状态切换。
7、ORG
该伪指令的格式为:
ORG 地址表达式
ORG伪指令用于设置段的起始地址。地址表达式的计算结果应与当前段的类型保持一致,如在RSEG(可重定位段)中,不要使用“ORG 10”,因为10是一个绝对地址,而应当使用“ORG .+10”,表示当前段偏移量为10的地址。另外,地址表达式中不能包括任何前向和外部引用。
8、ALIGNRAM和ALIGNROM
该伪指令的格式为:
ALIGNRAM 对齐
ALIGNROM 对齐[,填充值]
用于设置存储器地址边界的对齐方式,“对齐”是一个值为2~30的常数,并按22~30设定对齐地址。ALIGNRAM以数据增量方式对齐,ALIGNROM以填充0字节方式对齐。
9、EVEN和ODD
该伪指令的格式为:
EVEN [填充值]
ODD [填充值]
EVEN伪指令用于将程序计数器PC以偶数地址对齐(等价于ALIGNROM 1),ODD伪指令用于将程序计数器PC以奇数地址对齐。
3.5.5 模块控制伪指令
1、NAME和PROGRAM
该伪指令的格式为:
NAME 模块名
PROGRAM 模块名
NAME和PROGRAM伪指令用于定义一个程序模块。程序模块类似于C语言中的函数,是程序中相对独立的一个部分。程序模块即使没有被调用也会被无条件链接。
如:
NAME Main ;定义一个名为Main的程序模块
2、END和ENDMOD
END伪指令用于结束整个汇编语言程序,ENDMOD用于结束当前程序模块。每个汇编语言程序最后必须使用END伪指令通知汇编器已经到了源程序结尾,以结束汇编。
3、LIBRARY和MODULE
该伪指令用于定义多模块文件中的小模块,其中每个小模块代表一段子程序,从而可以方便地创建库模块文件。与NAME和PROGRAM不同的是,用LIBRARY和MODULE定义的模块只有在被调用时才会复制到链接代码中。
4、RTMODEL
该伪指令的格式为:
RTMODEL 关键字字符串,值字符串
该伪指令用于声明模块的运行模式属性,以强制模块之间的一致性。所有能被链接在一起的模块必须具有相同的关键字;值字符串要么具有相同的值,要么其值为星号“*”。
3.5.6 汇编控制伪指令
1、$和INCLUDE
该伪指令的格式为:
$ 文件名
INCLUDE 文件名
该伪指令用于给当前源文件加载头文件。
2、CASEOFF和CASEON
该伪指令用于源程序文件中禁止和允许大小写字符敏感。
3、LTORG
在使用ARM伪指令LDR加载地址数据时,要在适当的位置加入LTORG声明一个数据区,把要加载的数据保存在数据区内,再用LDR读出数据。LTORG伪指令通常放在无条件分支或子程序返回指令后面,这样处理器就不会错误的将数据区中的数据当作指令执行。
4、RADIX
该伪指令用于声明当前使用的数制形式。如:
RADIX 16D ;声明当前使用十六进制数
MOV R0,#12 ;此处#12为0x12
5、IF、ELSE和ENDIF
该伪指令的格式为:
IF 逻辑表达式
指令序列1
ELSE
指令序列2
ENDIF
条件汇编伪指令能根据设定条件的成立与否决定是否对指令序列进行汇编生成目标代码。若逻辑表达式为真,则对指令序列1汇编生成目标代码;否则对指令序列2汇编。其中还可以用ELSEIF伪指令设定新条件。
如:
DEFINE Test ;定义一个全局变量Test
……
IF Test = TRUE
指令序列1
ELSE
指令序列2
ENDIF
3.5.7 宏处理伪指令
1、MACRO和ENDM
该伪指令的格式为:
宏名 MACRO [,参数][ ,参数]……
指令序列
ENDM
MACRO伪指令用于定义一个宏,引用宏时必须使用定义的宏名,并可向宏中传递参数。ENDM伪指令用于结束宏定义。
如:
errmac MACRO text
BL abort
DATA
DC8 text,0
ENDM
包含在MACRO和ENDM之间的指令序列称为宏定义体。在宏定义体的第一行应声明宏的原型(包括宏名和所需的参数),然后就可以在汇编程序中通过宏名来调用该指令序列。在源程序被编译时,汇编器将宏调用展开,用宏定义中的指令序列代替程序中的宏调用,并将实际参数值传递给宏定义中的形式参数。
2、REPT和ENDR
该伪指令的格式为:
REPT 表达式
指令序列
ENDR
该伪指令用于指示汇编器将指定的指令序列进行重复汇编,重复次数由表达式的值确定。如果表达式的值为0,则不进行任何操作。
3、REPTC和ENDR
该伪指令的格式为:
REPTC 符号,替换字符串
指令序列
ENDR
该伪指令用于在宏展开时用替换字符串中的单个字符逐次替换符号。
4、REPTI和ENDR
该伪指令的格式为:
REPTI 符号,替换字符串[,替换字符串]……
指令序列
ENDR
该伪指令用于在宏展开时用整个替换字符串替换符号。
3.6 ARM汇编语言的语句格式
3.6.1 ARM汇编语言的语句格式
ARM(Thumb)汇编语言的语句格式为:
[标号[:]] 指令或伪指令 操作数 [;注释]
其中,方括号内的内容为可选项。
标号顶格书写时后面可不用冒号,非顶格书写时后面必须用冒号。
标号前加一个问号“?”前缀,表示该标号为外部标号,且仅能通过汇编语言访问;标号前加两个下划线“__”前缀,表示该标号为外部标号,能通过C语言和汇编语言访问;没有前缀的标号为局部标号,仅能在本模块内访问。
IAR汇编器对大小写字符敏感,一般指令和伪指令助记符使用大写,标号使用大小写混杂的方式以示区分。
同时,如果一条语句太长,可将该长语句分为若干行来书写,在行的末尾用“\”表示下一行与本行为同一条语句。
IAR汇编器规定汇编语言程序文件的默认扩展名为“.s79”,也可以用“.s”或“.asm”作为扩展名。
3.6.2 符号
在汇编语言程序设计中,经常使用各种符号代替地址、变量和常量等,以增加程序的可读性。尽管符号的命名由编程者决定,但并不是任意的,必须遵循以下的约定:
1.符号由大小写字母、数字及下划线组成,符号不能用数字开头。
2.符号区分大小写,同名的大、小写符号会被编译器认为是两个不同的符号。
3.符号在其作用范围内必须唯一。
4.自定义的符号名不能与系统的保留字相同。
5.符号名不应与指令或伪指令同名。
6. IAR汇编器内部预定义符号以双下划线开头和结尾。如:__IAR_SYSTEMS_ASM__。
3.6.3 常量和变量
1、 常量
程序中的常量是指其值在程序的运行过程中不能被改变的量。ARM(Thumb)汇编程序所支持的常量有数字常量、逻辑常量和字符串常量。
数字常量一般为32位的整数,当作为无符号数时,其取值范围为0~232-1,当作为有符号数时,其取值范围为-231~231-1。数字常量有4种表示形式:十进制数如123、-456等;十六进制数如0x123、0FFFFH等;八进制数如1234q等;二进制数如1010b等。
逻辑常量只有两种取值情况:TRUE和FALSE。
字符串常量为一个固定的字符串,一般用于程序运行时的信息提示。用法与标准C语言相同。
2、 变量
程序中的变量是指其值在程序的运行过程中可以改变的量。ARM(Thumb)汇编程序所支持的变量有数字变量、逻辑变量和字符串变量。
数字变量用于在程序的运行中保存数字值,但注意数字值的大小不应超出数字变量所能表示的范围。
逻辑变量用于在程序的运行中保存逻辑值,逻辑值只有两种取值情况:真或假。
字符串变量用于在程序的运行中保存一个字符串,但注意字符串的长度不应超出字符串变量所能表示的范围。
3.7 ARM汇编语言的程序结构
3.7.1 汇编语言的程序结构
在ARM(Thumb)汇编语言程序中,以程序段为单位组织代码。段是相对独立的指令或数据序列,具有特定的名称。段可以分为代码段和数据段,代码段的内容为执行代码,数据段存放代码运行时需要用到的数据。一个汇编程序至少应该有一个代码段,当程序较长时,可以分割为多个代码段和数据段,多个段在程序编译链接时最终形成一个可执行的映象文件。
可执行映象文件通常由以下几部分构成:
1. 1个或多个代码段,代码段的属性为只读。
2. 0个或多个包含初始化数据的数据段,数据段的属性为可读写。
3. 0个或多个不包含初始化数据的数据段,数据段的属性为可读写。
链接器根据系统默认或用户设定的规则,将各个段安排在存储器中的相应位置。因此源程序中段之间的相对位置与可执行的映象文件中段的相对位置一般不会相同。
3.7.2 一个简单的ARM汇编语言程序
以下是一个汇编语言源程序的基本结构:
代码清单3.1
NAME ASM_EXAMPL ;定义一个名为ARM_EXAMPL的程序模块
RSEG CODE:CODE:ROOT(2) ;定义一个可重定位的代码段
CODE32 ;执行32位ARM指令
ORG 0x1000 ;定义程序起始地址为0x1000
Start: LDR R0,=0x3FF5000 ;Start处的地址即为0x1000
LDR R1,=0xff
STR R1,[R0]
MOV R0,#0x10
MOV R1,#0x20
ADD R0,R0,R1
Stop: B Stop ;跳转到指令本身,程序停止运行
ENDMOD ;本程序模块结束
END ;本程序结束
程序很简单,它完成的功能并不重要,但它已经表示出了一个ARM汇编语言程序的基本结构。
3.8 ARM程序设计举例
3.8.1 分支程序
程序设计中的三种基本结构是:顺序结构、分支结构和循环结构。在C语言中可以使用if-else语句实现单分支和双分支结构,也可以通过switch-case语句实现多分支结构。但是在汇编语言中,分支结构一般是通过跳转指令结合标号来实现的。
在ARM汇编语言程序中,由于ARM指令支持条件执行,从而大大减少了分支程序的复杂程度。
例如:用两个整数辗转相减的方法求它们的最大公约数。注意体会B指令加条件码的执行方式。程序中使用的main标号是因为IAR汇编器一般默认从main标号处开始执行。
代码清单3.2
NAME GCD
PUBLIC main ;声明外部引用标号main
B main ;从main标号处开始执行
RSEG CODE:CODE
CODE32
main: MOV R0,#120
MOV R1,#96
Gcd: CMP R0,R1 ;比较两数的大小
BEQ Stop ;如果两数相等则跳到结束处
BLT Less ;如果R0<R1则跳到Less标号处
SUB R0,R0,R1 ;否则R0=R0-R1
B Gcd
Less: SUB R1,R1,R0 ;R1=R1-R0
B Gcd
Stop: B Stop ;跳转到指令本身,程序停止运行
ENDMOD ;本程序模块结束
END ;本程序结束
3.8.2 循环程序
通过跳转指令还可以实现程序的循环结构。
例如:求n=1+2+…+10累加的和。
代码清单3.3
NAME SUM
PUBLIC main
B main
RSEG CODE
CODE32
main: MOV R0,#10
MOV R1,R0 ;利用R1寄存器做循环计数器
Loop: SUBS R1,R1,1 ;循环次数减1
ADD R0,R0,R1
BNE Loop ;循环次数为0则结束循环
Stop: B Stop
ENDMOD
END
3.8.3 子程序调用
通过BL指令可以实现子程序调用,语法:BL子程序名。
在子程序的结束处,可以通过MOV PC,LR返回到主程序中。通常可以使用寄存器R0~R3完成传递参数到子程序和从子程序返回运算的结果。
以下是使用BL指令调用子程序的汇编语言源程序的例子,该程序编写了一个在内存里拷贝字符串的子程序,然后在主程序里调用它。
代码清单3.4
NAME STRCPY
PUBLIC main
B main
RSEG CODE
CODE32
main: LDR R1,=srcstr ;R1指向源字符串
LDR R0,=dststr ;R0指向目标字符串
BL strcopy ;调用strcopy子程序
stop: B stop ;程序停止
strcopy: ;子程序定义
LDRB R2,[R1],#1 ;读一个字符到R2,并更新源字符地址
STRB R2,[R0],#1 ;写一个字符,并更新目的字符地址
CMP R2,#0 ;是否结束。以数字0为标志
BNE strcopy ;循环执行
DATA ;数据区
srcstr DCB "First string - source ",0
dststr DCB "Second string - destination ",0
ENDMOD
END
3.8.4 查表法
查表法是编程中常用的一种技巧。当程序涉及到较多的数据、数据串或数据表格时,可以通过地址来对它们进行访问。通常有两种方法装载地址:(1)通过ADR和ADRL伪指令直接装载地址;(2)通过伪指令LDR Rd,=Label从数据表格中装载地址。
下面的程序设置了3个参数,arithfunc根据3个参数返回一个R0值。当R0=0时,R0=R1+R2;当R0=1时,R0=R1-R2;当R0>1时,R0=R1+R2。:
代码清单3.5
NAME JUMP
PUBLIC main
B main
Num EQU 2 ;跳转表格的入口数
RSEG CODE
CODE32
main: MOV R0,#0 ;以下设置3个参数
MOV R1,#3
MOV R2,#2
BL arithfunc ;调用子程序
stop: B stop ;程序停止
arithfunc:
CMP R0,#Num ;比较参数
BHS Doadd ;若R0>=2,则执行加法
ADR R3,jumptable ;装载跳转表格标号地址
LDR PC,[R3,R0,LSL #2] ;跳到相应子程序入口地址处
Jumptable:
DCD Doadd ;Doadd子程序的入口地址
DCD Dosub ;Dosub子程序的入口地址
Doadd: ADD R0,R1,R2 ;=0或>1时执行的操作
MOV PC,LR
Dosub: SUB R0,R1,R2 ;=1时执行的操作
MOV PC,LR
ENDMOD
END
3.8.5 汇编语言与C/C++的混合编程
在应用系统的程序设计中,若所有的编程任务均用汇编语言来完成,其工作量是可想而知的,同时,不利于系统升级或应用软件移植,事实上,ARM体系结构支持C/C+以及与汇编语言的混合编程,在一个完整的程序设计的中,除了初始化部分用汇编语言完成以外,其主要的编程任务一般都用C/C++ 完成。
汇编语言与C/C++的混合编程通常有以下几种方式:
1. 在C/C++代码中嵌入汇编指令。
在ARM C中,可以使用关键字__arm来标识一段汇编指令程序。格式如下:
__asm
{
汇编指令序列
}
即可在C语言源程序中直接执行ARM汇编指令。
2. 在汇编程序和C/C++的程序之间进行变量的互访。
3. 汇编程序、C/C++程序间的相互调用。
可以把汇编程序和C/C++程序中需要共享的变量或函数用PUBLIC或extern关键字分别声明为全局变量或全局函数,然后在其它程序文件中即可进行访问和调用。但是从好的编程风格来说,最好尽量减少全局变量和全局函数的使用。
混合编程中,必须遵守一定的调用规则,如物理寄存器的使用、参数的传递等。ARM专门为此制定了一个标准ATPCS(ARM-Thumb Procedure Call Standard,ARM-Thumb过程调用标准)。对于初学者来说,这是非常烦琐的,在实际工作中也没有太多必要。
在实际的编程应用中,使用较多的方式是:系统程序的初始化部分用汇编语言完成,然后用C/C++完成主要的编程任务,程序在执行时首先完成初始化过程,然后跳转到C/C++程序代码中。汇编程序和C/C++程序之间一般没有参数的传递,也没有频繁的相互调用,因此,整个程序的结构显得相对简单,容易理解。
以下是一个这种结构程序的基本示例。该程序非常简单,建立一个工程asm_c.eww,工程中包括一个汇编语言程序文件init.s79和一个C语言程序文件hello.c。
代码清单3.6——init.s79文件
NAME INIT
PUBLIC main
EXTERN Main ;声明引入C程序的Main()函数
B main
RSEG CODE
CODE32
main:
NOP ;此处可以插入用户自己编写的系统初始化代码
B Main ;转向C语言程序
ENDMOD
END
代码清单3.7——hello.c文件:
#include <stdio.h>
/*注意此处C语言程序的入口函数是大小写敏感的Main()函数,而不是常用的main()函数。这是为了跟汇编程序中的main入口区别开,以免造成工程有两个程序入口。*/
int Main(void)
{
printf("Hello, world!\n");
}
3.9 用ARM汇编语言编写系统启动程序
基于ARM内核的芯片多数为复杂的片上系统,这种复杂系统里的多数硬件模块都是可以配置的,需要由软件来设置其需要的工作状态。由于C语言具有模块性和可移植性的特点,大部分基于ARM的应用系统程序都采用C语言编写。但是当系统复位启动时,在进入C语言的main函数之前,需要有一段启动程序来完成对存储器配置、地址重映射和ARM芯片内部集成外围功能初始化等工作。这类工作直接面对处理器内核和硬件控制器进行编程,用C语言较难实现,因此一般采用汇编语言编写。
3.9.1 编写启动程序的一般规则
ARM内核的处理器在复位后,从0x00000000地址处开始读取指令。实现启动最简单的方法是将应用程序放在映射空间地址为0的ROM中。这样当执行第1条指令时,应用程序就从0x00000000处开始执行。但这种方法有很多缺点:ROM的存储宽度较小且速度较慢,会降低系统启动和处理器对异常处理的速度;异常向量表放在ROM中,程序将无法修改向量表,因此常将地址为0的空间映射成RAM,但RAM中的程序掉电无法保存,因此必须将ROM映射为0地址,以保证有效的复位向量,然后再使用重映射命令将RAM映射为0地址,ROM映射到其他地址空间,并将异常向量从ROM复制到RAM中。
编写启动程序应遵循以下一般规则:
1. 设置入口指针
启动程序首先必须定义入口指针,而且整个应用程序只有一个入口指针,通常应用程序的入口地址为0。
2. 设置异常向量
基于ARM7TDMI内核的处理器共支持7种异常,异常处理地址存放在地址0处的异常向量表中,共8×4字节的空间。异常向量表的内容参见2.4.2节。
异常向量表通常放在存储器底部,每个异常分配4个字节的空间。每个向量入口包含一条跳转指令或加载PC的指令,以执行适当的转移到具体的异常处理程序。如果ROM定位于0地址,则向量表由一系列固定的用以指向每个异常的指令组成;否则向量必须被动态初始化。可以在启动程序中添加一段代码,使其在运行时将向量表拷贝到0地址开始的存储器空间。对于没有使用的异常,使其指向一个只含返回指令的哑函数,以防止错误异常引起系统混乱。
3. 初始化片内集成外围功能
由于ARM公司仅设计内核并出售给其它半导体厂商,不同的厂商购买内核授权后加入自己的外围功能,从而导致ARM核处理器芯片丰富多样,但也使得不同芯片的启动代码在这一部分差别很大。编写这一部分时应根据芯片和应用系统要求对它们进行合适的初始化。比较重要的操作一般有:外部总线接口的初始化、配置时钟锁相环、配置中断控制器、禁用看门狗电路等。
4. 初始化存储系统
有些ARM核芯片可通过对寄存器编程来初始化系统存储器,而对于较复杂系统通常由存储管理单元MMU来管理内存空间。为正确运行应用程序,在初始化期间应将系统需要读写的数据和变量从ROM拷贝到RAM中;一些要求快速响应的程序,例如中断处理程序,也需要在RAM中运行;如果使用Flash,对Flash的擦除和写入操作也一定要在RAM中运行。
5. 初始化堆栈寄存器
系统堆栈初始化取决于用户使用了哪些中断,以及系统需要处理哪些错误类型。一般来说管理模式堆栈必须初始化。如果使用IRQ中断,则IRQ堆栈必须初始化,并且必须在允许中断之前进行。如果使用FIQ中断,则FIQ堆栈也必须初始化,并且必须在允许中断之前进行。一般在简单的嵌入式系统中不使用中止状态堆栈和未定义指令堆栈,但为了调试方便还是将其初始化。如果系统使用DRAM或其它外设,还需要设置相关寄存器,以确定其刷新频率、数据总线宽度等信息。
6. 改变处理器模式和状态
此时可以通过清除CPSR寄存器中的中断控制位来允许中断,这里是安全开启中断的最早地方。这个阶段处理器仍处于管理模式下。如果程序需要在用户模式下运行,可以在此处切换到用户模式并初始化用户模式堆栈指针。
7. 跳转到C语言主程序
在从启动程序跳转到C语言程序的main函数之前,还需要初始化数据存储空间。通常是加入一段循环代码对数据存储空间清0。这样做的主要原因是C语言中没有初值的变量默认值均为0。已经初始化变量的初值必须从ROM中复制到RAM中,其它变量的初值必须为0。
3.9.2 IAR EWARM软件包给出的一般启动程序
下面给出了IAR EWARM软件包提供的一般启动程序代码,实际应用中可以根据具体芯片及应用系统要求进行适当修改,以适应不同场合的需要。
代码清单3.8——IAR EWARM启动代码
;-----------------------------------------------------------------------------
; 文件中标号的命名规则:
; ?xxx - 仅能由汇编语言访问的外部标号
; __xxx - 可由C语言访问或定义的外部标号
; xxx - 单个模块中的局部标号(注意,本文件包含多个模块)
; main - 用户程序的起点
;---------------------------------------------------------------
; 适用于整个文件的宏和模式定义
;---------------------------------------------------------------
; 模式,对应于CPSR寄存器的0~5位
MODE_BITS DEFINE 0x1F ; 用于CPSR模式的位屏蔽
USR_MODE DEFINE 0x10 ; 用户模式
FIQ_MODE DEFINE 0x11 ; FIQ模式
IRQ_MODE DEFINE 0x12 ; IRQ模式
SVC_MODE DEFINE 0x13 ; 管理模式
ABT_MODE DEFINE 0x17 ; 中止模式
UND_MODE DEFINE 0x1B ; 未定义指令模式
SYS_MODE DEFINE 0x1F ; 系统模式
;---------------------------------------------------------------
; ?RESET
; 复位向量。通常INTVEC段被链接到地址0。为程序调试方便,也可以放在其它地址
;---------------------------------------------------------------
MODULE ?RESET
COMMON INTVEC:CODE:NOROOT(2)
PUBLIC __program_start
EXTERN ?cstartup
EXTERN undef_handler, swi_handler, prefetch_handler
EXTERN data_handler, irq_handler, fiq_handler
CODE32 ; 复位后始终为ARM模式
org 0x00
__program_start
ldr pc,[pc,#24] ; 绝对跳转地址范围为4GB
; ldr b,?cstartup ; 相对跳转允许重映射,限于32MB
; 可以去掉以下指令前的注释分号来允许异常向量
; 也可以在C语言中采用预编译命令“#pragma vector”
org 0x04
; ldr pc,[pc,#24] ; 跳转到undef_handler
org 0x08
; ldr pc,[pc,#24] ; 跳转到swi_handler
org 0x0c
; ldr pc,[pc,#24] ; 跳转到prefetch_handler
org 0x10
; ldr pc,[pc,#24] ; 跳转到data_handler
org 0x18
; ldr pc,[pc,#24] ; 跳转到irq_handler
org 0x1c
; ldr pc,[pc,#24] ; 跳转到fiq_handler
; 用于“ldr pc”指令的常数表入口定位于0x20
; 异常向量可以用C语言的预编译命令“#pragma vector”指定,也可以
; 在以下dc32指令后面填入向量地址。向量地址为ARM向量号+20
org 0x20
dc32 ?cstartup
org 0x24
; dc32 undef_handler
org 0x28
; dc32 swi_handler
org 0x2c
; dc32 prefetch_handler
org 0x30
; dc32 data_handler
org 0x38
; dc32 irq_handler
org 0x3c
; dc32 fiq_handler
LTORG
; ENDMOD __program_start
ENDMOD
;---------------------------------------------------------------
; ?CSTARTUP
;---------------------------------------------------------------
MODULE ?CSTARTUP
RSEG IRQ_STACK:DATA(2)
RSEG ABT_STACK:DATA:NOROOT(2)
RSEG UND_STACK:DATA:NOROOT(2)
RSEG FIR_STACK:DATA:NOROOT(2)
RSEG SVC_STACK:DATA:NOROOT(2)
RSEG CSTACK:DATA(2)
RSEG ICODE:CODE:NOROOT(2)
PUBLIC ?cstartup
EXTERN ?main
; 从这里开始执行
; 复位后为ARM管理模式,禁止中断
CODE32
?cstartup
; 需要时在这里加入建立堆栈指针之前的初始化指令
; 初始化堆栈指针
; 以下方式可用于任何异常堆栈:FIQ, IRQ, SVC, ABT, UND, SYS.
; 用户模式使用与SYS模式相同的堆栈
; 堆栈段必须在链接器命令文件中定义,并且已经在上面声明
mrs r0,cpsr ; 原PSR值
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#IRQ_MODE ; 置IRQ模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(IRQ_STACK)&0xFFFFFFF8 ; IRQ_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#ABT_MODE ; 置Abort模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(ABT_STACK)&0xFFFFFFF8 ; ABT_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#SVC_MODE ; 置Supervisor模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(SVC_STACK) & 0xFFFFFFF8 ; SVC_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#UND_MODE ; 置Undefined模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(UND_STACK) & 0xFFFFFFF8 ; FIR_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#FIQ_MODE ; 置FIR模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(FIR_STACK) & 0xFFFFFFF8 ; FIR_STACK结束
bic r0,r0,#MODE_BITS ; 清除模式位
orr r0,r0,#SYS_MODE ; 置System模式位
msr cpsr_c,r0 ; 改变模式
ldr sp,=SFE(CSTACK) & 0xFFFFFFF8 ; CSTACK结束
#ifdef __ARMVFP__
; 允许VFP协处理器
mov r0, #0x40000000 ; 置VFP的EN位
fmxr fpexc, r0 ; FPEXC, 清除其它
; 将缓冲区清0以禁止下溢出。为满足IEEE 754标准,应删除该指令并安装合适的异常句柄
mov r0, #0x01000000 ; 置VFP的FZ位
fmxr fpscr, r0 ; FPSCR, 清除其它
#endif
; 在这里可以添加更多的用户自定义初始化指令
; 跳转到?main标号的地方,继续IAR系统的启动程序
ldr r0,=?main
bx r0
LTORG
ENDMOD
END
习题
3.1 ARM7TDMI有几种寻址方式?LDR R1,[R0,#0x04]属于哪种寻址方式?
3.2 ARM指令的条件码有多少个?默认条件码是什么?
3.3 ARM指令中第二个操作数有哪几种形式?
3.4 请指出MOV指令与LDR加载指令的区别及用途.
3.5 CMP指令的功能是什么?写一个程序,判断R1的值是否大于0x30,是则将R1减去0x30。
3.6 调用子程序是用B还是用BL指令?请写出返回子程序的指令。
3.7 ARM状态与Thumb状态的切换指令是什么?请举例说明。
3.8 Thumb状态与ARM状态的寄存器有区别吗?Thumb指令对哪些寄存器的访问受到一定限制?
3.9 Thumb指令集的堆栈入栈、出栈指令是哪两条?
3.10 把下面的C代码转换成汇编代码。数组a和b分别存放在以0x4000和0x5000为起始地址的存储区内,类型为long型(32位)。
for(i=0;i<8;i++)
{
a[i] = b[7-i];
}
3.11 编写程序,将R1的高8位传送到R2的低8位
3.12 编写一段64位加法运算的程序,要求满足:[R1:R0]+[R3:R2],结果存入[R1:R0]中
3.13 编写程序将地址0x0000 1000到0x0000 1030的数据全部搬迁到0x0000 2000到0x0000 2030的区域中,并将源数据区清零。
第4章 LPC2400系列处理器原理
处理器的“体系结构”指从程序员角度观察到的处理器的组织方式,所以又称为处理器的编程模型。其主要内容为处理器内的寄存器组织、对存储器的寻址方式、指令系统等。本章将介绍ARM7TDMI程序员模型、工作状态与工作模式、ARM和Thumb状态的寄存器组织、存储器组织结构、异常及协处理器接口等一些基本概念。本章还要讲述ARM的编程基础,如ARM微处理器的基本工作原理、与程序设计相关的基本技术细节等。
4.1 LPC2400系列处理器简介
4.1.1 LPC2400系列处理器特性
LPC2400系列处理器包括LPC2468/LPC2470/LPC2478等多款芯片,是基于支持实时仿真和跟踪的16/32位ARM7TDMI-S内核的微控制器,它与所有NXP LPC 2000处理器具有相同的存储器映射、中断向量控制、Flash编程和更新机制,以及调试和仿真功能。LPC2468/LPC2478的512KB大容量嵌入式高速Flash存储器具有128位宽度的存储器接口和独特的加速结构,使得32位代码能够在最高时钟频率72MHz下运行。16位Thumb模式可以将代码规模降低30%以上,而性能损失却很小。LPC2470/LPC2478芯片内部还集成了LCD接口支持(最高1024×768像素、15阶灰度单色和每像素24位真彩色TFT面板),使得这两款芯片可以广泛应用于各种手持式设备中。
LPC2400系列处理器拥有丰富的片上资源和外设接口。这一系列芯片的共同特性有:
-ARM7TDMI-S内核,最高72MHz主频;
-98KB的片内静态存储器,其中64KB的片内SRAM,16KB SRAM用于以太网,16KB SRAM用于DMA控制器(也可用于USB控制器),2KB SRAM用于RTC实时时钟;
-512KB片内Flash程序存储器,片内Boot实现IAP和ISP片内Flash编程;
-可配置的外部存储器接口,最多支持8个Bank,支持外部RAM、ROM和Flash存储器扩展,每个Bank最大可支持到256MB,可支持8/16/32位字宽;
-高级向量中断控制器,支持32个向量中断,可配置优先级和向量地址;
-通用AHB DMA控制器(GPDMA)可以用于支持SSP、I2S和SD/MMC接口;
-10/100M以太网MAC接口;
-多个串行接口,包括4路UART、3路I2C串行总线接口和1个SPI接口;
-10位A/D和D/A转换器,转换时间低至2.44微秒;
-USB device/host/OTG接口;
-2个CAN总线接口;
-4个32位的定时器、2个PWM脉冲调制单元(每个6路输出)、实时时钟和看门狗;
-160个高速GPIO端口(可承受5V电压),4个独立外部中断引脚;
-标准ARM调试接口,兼容各种现有的调试工具;
-片内晶振频率范围1~24MHz;
-4个低功耗模式:空闲、睡眠、掉电和深度掉电模式;
-供电电压3.3V(3.0V~3.6V)。
在LPC2400系列芯片中,LPC2468是LPC2478的无LCD控制器版本,LPC2470是LPC2478的无片内Flash版本,芯片的大多数特性是完全相同的。所以在后面的章节中,本书一律采用LPC2478芯片为例进行讲解,请读者在实际工作中注意具体芯片的差别。
4.1.2 LPC2400系列处理器结构
LPC2400系列处理器包含一个支持仿真的ARM7TDMI-S CPU、与片内存储器控制器接口的ARM7局部总线、与中断控制器接口的AMBA高性能总线(AHB总线)和连接片内外设功能的AMBA外设总线(APB总线)。存储模式为小端模式。
AHB总线和APB总线都是ARM公司推出的AMBA片上总线规范的一部分。AHB(Advanced High performance Bus)系统总线主要用于高性能模块(如CPU、DMA和DSP等)之间的连接,一般用于片内高性能高速度的外设,如:外部存储器、USB接口、DMA控制器、以太网控制器、LCD液晶屏控制器以及高速GPIO控制器等。LPC2400中的AHB外设一共分配了2MB的地址范围,它位于4GB ARM存储器空间的最顶端。每个AHB外设都分配了16KB的地址空间。
LPC2400的外设功能模块都连接到APB总线。APB(Advanced Peripheral Bus)外围总线主要用于低带宽的周边外设之间的连接,如:UART、I2C、SPI、I2S、A/D、D/A、CAN等等。APB总线与AHB总线之间通过AHB到APB的桥相连。APB外设也分配了2MB的地址范围,每个APB外设在APB地址空间内都分配了16KB的地址空间。
片内外设与器件引脚的连接由引脚连接模块控制。软件可以通过控制该模块让引脚与特定的片内外设相连接。
LPC2400的结构框图如图4.1所示。
图4.1 LPC2400结构框图
4.2 处理器引脚配置
4.2.1引脚配置
LPC2400系列处理器共有208个引脚,一般提供两种封装形式:LQFP208和TFBGA208。其管脚封装如图4.2所示。
LQFP208 FBGA208
图4.2 LPC2400系列处理器管脚封装图
LQFP指封装本体厚度为1.4mm的薄型QFP(四侧引脚扁平封装quad flat package),它是一种表面贴装型封装,引脚从四个侧面引出呈L型,每个侧面52个引脚,引脚号分别为1~52、53~104、105~156、157~208。FBGA是塑料封装的BGA(Ball Grid Array Package),即球栅阵列封装,其引脚都在芯片底部,用英文字母行和数字列标识。由于LPC2400系列处理器在实际使用中更多使用QFP封装,本节引脚介绍以QFP封装为准。
从功能上,LPC2400的208个引脚分为P0口、P1口、P2口、P3口、P4口,以及电源、复位、晶振和其它管脚几部分。下面对这几个部分分别进行介绍。
1. P0口: P0口是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P0口管脚描述如表4.1所示。
表4.1 LPC2400的P0口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P0[0] |
94 | I/O | P0[0]:GPIO口 |
I | RD1:CAN1接收器输入 | ||
O | TXD3:UART3发送输出端 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P0[1] |
96 | I/O | P0[1]:GPIO口 |
O | TD1:CAN1发送器输出 | ||
I | RXD3:UART3接收输入端 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P0[2] |
202 | I/O | P0[2]:GPIO口 |
O | TXD0:UART0发送输出端 | ||
P0[3] |
204 | I/O | P0[3]:GPIO口 |
I | RXD0:UART0接收输入端 | ||
P0[4] |
168 | I/O | P0[4]:GPIO口 |
I/O | I2SRX_CLK:I2S总线接收时钟 | ||
I | RD2:CAN2接收输入端 | ||
I | CAP2[0]:Timer2的捕获输入通道0 | ||
P0[5] |
166 | I/O | P0[5]:GPIO口 |
I/O | I2SRX_WS:I2S总线接收字选择 | ||
I | TD2:CAN2发送输出端 | ||
I | CAP2[1]:Timer2的捕获输入通道1 | ||
P0[6] |
164 | I/O | P0[6]:GPIO口 |
I/O | I2SRX_SDA:I2S总线数据接收 | ||
I/O | SSEL1:SSP1从机选择 | ||
O | MAT2[0]:Timer2的匹配输出通道0 | ||
P0[7] |
162 | I/O | P0[7]:GPIO口 |
I/O | I2STX_CLK:I2S总线发送时钟 | ||
I/O | SCK1:SSP1串行时钟 | ||
O | MAT2[1]:Timer2的匹配输出通道1 | ||
P0[8] |
160 | I/O | P0[8]:GPIO口 |
I/O | I2STX_WS:I2S总线发送字选择 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
O | MAT2[2]:Timer2的匹配输出通道2 | ||
P0[9] |
158 | I/O | P0[9]:GPIO口 |
I/O | I2STX_SDA:I2S总线数据发送 | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
O | MAT2[3]:Timer2的匹配输出通道3 | ||
P0[10] |
98 | I/O | P0[10]:GPIO口 |
O | TXD2:UART2发送输出端 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
O | MAT3[0]:Timer3的匹配输出通道0 | ||
P0[11] |
100 | I/O | P0[11]:GPIO口 |
I | RXD2:UART2接收输入端 | ||
I/O | SCL2:I2C2时钟输入/输出 | ||
O | MAT3[1]:Timer3的匹配输出通道1 | ||
P0[12] |
41 | I/O | P0[12]:GPIO口 |
O | USB_PPWR2:USB端口2端口电源使能 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
I | AD0[6]:A/D转换器0输入6 | ||
P0[13] |
45 | I/O | P0[13]:GPIO口 |
O | USB_UP_LED2:USB端口2LED | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
I | AD0[7]:A/D转换器0输入7 | ||
P0[14] |
69 | I/O | P0[14]:GPIO口 |
O | USB_HSTEN2:USB端口2主机使能 | ||
O | USB_CONNECT2:USB端口2软件连接控制 | ||
I/O | SSEL1:SSP1从机选择 | ||
P0[15] |
128 | I/O | P0[15]:GPIO口 |
O | TXD1:UART1发送输出端 | ||
I/O | SCK0:SSP0串行时钟 | ||
I/O | SCK:SPI串行时钟 | ||
P0[16] |
130 | I/O | P0[16]:GPIO口 |
I | RXD1:UART1接收输入端 | ||
I/O | SSEL0:SSP0从机选择 | ||
I/O | SSEL:SPI从机选择 | ||
P0[17] |
126 | I/O | P0[17]:GPIO口 |
I | CTS1:UART1清除发送输入端 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
I/O | MISO:SPI主机输入从机输出 | ||
P0[18] |
124 | I/O | P0[18]:GPIO口 |
I | DCD1:UART1数据载波检测输入端 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
I/O | MOSI:SPI主机输出从机输入 | ||
P0[19] |
122 | I/O | P0[19]:GPIO口 |
I | DSR1:UART1数据设置就绪端 | ||
O | MCICLK:SD/MMC接口时钟输出线 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P0[20] |
120 | I/O | P0[20]:GPIO口 |
O | DTR1:UART1数据终止就绪端 | ||
I/O | MCICMD:SD/MMC接口命令线 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P0[21] |
118 | I/O | P0[21]:GPIO口 |
I | RI1:UART1铃响指示输入端 | ||
O | MCIPWR:SD/MMC电源供应使能 | ||
I | RD1:CAN1接收输入端 | ||
P0[22] |
116 | I/O | P0[22]:GPIO口 |
O | RTS1:UART1请求发送输出端 | ||
I/O | MCIDAT0:SD/MMC接口数据线0 | ||
O | TD1:CAN1发送输出端 | ||
P0[23] |
18 | I/O | P0[23]:GPIO口 |
I | AD0[0]:A/D转换器0输入0 | ||
I/O | I2SRX_CLK:I2S总线接收时钟 | ||
I | CAP3[0]:Timer3的捕获输入通道0 | ||
P0[24] |
16 | I/O | P0[24]:GPIO口 |
I | AD0[1]:A/D转换器0输入1 | ||
I/O | I2SRX_WS:I2S总线字选择 | ||
I | CAP3[1]:Timer3的捕获输入通道1 | ||
P0[25] |
14 | I/O | P0[25]:GPIO口 |
I | AD0[2]:A/D转换器0输入2 | ||
I/O | I2SRX_SDA:I2S总线数据接收 | ||
O | TXD3:UART3发送输出端 | ||
P0[26] |
12 | I/O | P0[26]:GPIO口 |
I | AD0[3]:A/D转换器0输入3 | ||
O | AOUT:D/A转换器输出 | ||
I | RXD3:UART3接收输入端 | ||
P0[27] |
50 | I/O | P0[27]:GPIO口 |
I/O | SDA0:I2C0数据输入/输出 | ||
P0[28] |
48 | I/O | P0[28]:GPIO口 |
I/O | SCL0:I2C0时钟输入/输出 | ||
P0[29] |
61 | I/O | P0[29]:GPIO口 |
I/O | USB_D+1:USB端口1双向D+线 | ||
P0[30] |
62 | I/O | P0[30]:GPIO口 |
I/O | USB_D-1:USB端口1双向D-线 | ||
P0[31] |
51 | I/O | P0[31]:GPIO口 |
I/O | USB_D+2:USB端口2双向D+线 |
2. P1口: P1口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P1口管脚描述如表4.2所示。
表4.2 LPC2400的P1口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P1[0] |
196 | I/O | P1[0]:GPIO口 |
O | ENET_TXD0:以太网发送数据0(RMII/MII接口) | ||
P1[1] |
194 | I/O | P1[1]:GPIO口 |
O | ENET_TXD1:以太网发送数据1(RMII/MII接口) | ||
P1[2] |
185 | I/O | P1[2]:GPIO口 |
O | ENET_TXD2:以太网发送数据2(RMII/MII接口) | ||
O | MCICLK:SD/MMC接口时钟输出线 | ||
O | PWM0[1]:脉宽调制器0输出1 | ||
P1[3] |
177 | I/O | P1[3]:GPIO口 |
O | ENET_TXD3:以太网发送数据3(RMII/MII接口) | ||
O | MCICMD:SD/MMC接口命令线 | ||
O | PWM0[2]:脉宽调制器0输出2 | ||
P1[4] |
192 | I/O | P1[4]:GPIO口 |
O | ENET_TX_EN:以太网发送数据使能(RMII/MII接口) | ||
P1[5] |
156 | I/O | P1[5]:GPIO口 |
O | ENET_TX_ER:以太网发送数据出错(MII接口) | ||
O | MCIPWR:SD/MMC电源供应使能 | ||
O | PWM0[3]:脉宽调制器0输出3 | ||
P1[6] |
171 | I/O | P1[6]:GPIO口 |
I | ENET_TX_CLK:以太网发送时钟(MII接口) | ||
I/O | MCIDAT0:SD/MMC接口数据线0 | ||
O | PWM0[4]:脉宽调制器0输出4 | ||
P1[7] |
153 | I/O | P1[7]:GPIO口 |
I | ENET_COL:以太网冲突检测(MII接口) | ||
I/O | MCIDAT1:SD/MMC接口数据线1 | ||
O | PWM0[5]:脉宽调制器0输出5 | ||
P1[8] |
190 | I/O | P1[8]:GPIO口 |
I | ENET_CRS_DV/ENET_CRS:以太网载波检测/数据有效(RMII接口)/以太网载波检测(MII接口) | ||
P1[9] |
188 | I/O | P1[9]:GPIO口 |
I | ENET_RXD0:以太网接收数据0(RMII/MII接口) | ||
P1[10] |
186 | I/O | P1[10]:GPIO口 |
I | ENET_RXD1:以太网接收数据1(RMII/MII接口) | ||
P1[11] |
163 | I/O | P1[11]:GPIO口 |
I | ENET_RXD2:以太网接收数据2(RMII/MII接口) | ||
I/O | MCIDAT2:SD/MMC接口数据线2 | ||
O | PWM0[6]:脉宽调制器0输出6 | ||
P1[12] |
157 | I/O | P1[12]:GPIO口 |
I | ENET_RXD3:以太网接收数据3(RMII/MII接口) | ||
I/O | MCIDAT3:SD/MMC接口数据线3 | ||
I | PCAP0[0]:脉宽调制器0捕获输入通道0 | ||
P1[13] |
147 | I/O | P1[13]:GPIO口 |
I | ENET_RX_DV:以太网接收数据有效(MII接口) | ||
P1[14] |
184 | I/O | P1[14]:GPIO口 |
I | ENET_RX_ER:以太网接收错误(MII接口) | ||
P1[15] |
182 | I/O | P1[15]:GPIO口 |
I | ENET_REF_CLK/ENET_RX_CLK:以太网参考时钟(RMII接口)/以太网接收时钟(MII接口) | ||
P1[16] |
180 | I/O | P1[16]:GPIO口 |
I | ENET_MDC:以太网MIIM时钟 | ||
P1[17] |
178 | I/O | P1[17]:GPIO口 |
I/O | ENET_MDIO:以太网MI数据输入输出 | ||
P1[18] |
66 | I/O | P1[18]:GPIO口 |
O | USB_UP_LED1:USB端口1LED | ||
O | PWM1[1]:脉宽调制器1输出1 | ||
I | CAP1[0]:Timer1捕获输入通道0 | ||
P1[19] |
68 | I/O | P1[19]:GPIO口 |
O | USB_TX_E1:USB端口1发送使能信号(OTG收发器) | ||
O | USB_PPWR1:USB端口1端口电源使能信号 | ||
I | CAP1[1]:Timer1捕获输入通道1 | ||
P1[20] |
70 | I/O | P1[20]:GPIO口 |
O | USB_TX_DP1:USB端口1D+数据发送(OTG收发器) | ||
O | PWM1[2]:脉宽调制器1输出2 | ||
I/O | SCK0:SSP0串行时钟 | ||
P1[21] |
72 | I/O | P1[21]:GPIO口 |
O | USB_TX_DM1:USB端口1D-数据发送(OTG收发器) | ||
O | PWM1[3]:脉宽调制器1输出3 | ||
I/O | SSEL0:SSP0从机选择 | ||
P1[22] |
74 | I/O | P1[22]:GPIO口 |
I | USB_RCV1:USB端口1差分数据接收(OTG收发器) | ||
I | USB_PWRD1:USB端口1电源状态(主机电源开关) | ||
O | MAT1[0]:Timer1匹配输出通道0 | ||
P1[23] |
76 | I/O | P1[23]:GPIO口 |
I | USB_RX_DP1:USB端口1D+数据接收(OTG收发器) | ||
O | PWM1[4]:脉宽调制器1输出4 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
P1[24] |
78 | I/O | P1[24]:GPIO口 |
I | USB_RX_DM1:USB端口1D-数据接收(OTG收发器) | ||
O | PWM1[5]:脉宽调制器1输出5 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
P1[25] |
80 | I/O | P1[25]:GPIO口 |
O | USB_LS1:USB端口1低速状态(OTG收发器) | ||
O | USB_HSTEN1:USB端口1主机使能状态 | ||
O | MAT1[1]:Timer1匹配输出通道1 | ||
P1[26] |
82 | I/O | P1[26]:GPIO口 |
O | USB_SSPND1:USB端口1总线悬挂状态(OTG收发器) | ||
O | PWM1[6]:脉宽调制器1输出6 | ||
I | CAP0[0]:Timer0捕获输入通道0 | ||
P1[27] |
88 | I/O | P1[27]:GPIO口 |
I | USB_INT1:USB端口1OTG ATX中断(OTG收发器) | ||
I | USB_OVRCR1:USB端口1过流状态 | ||
I | CAP0[1]:Timer0捕获输入通道1 | ||
P1[28] |
90 | I/O | P1[28]:GPIO口 |
I/O | USB_SCL1:USB端口1I2C串行时钟(OTG收发器) | ||
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
O | MAT0[0]:Timer0匹配输出通道0 | ||
P1[29] |
92 | I/O | P1[29]:GPIO口 |
I/O | USB_SDA1:USB端口1I2C串行数据(OTG收发器) | ||
I | PCAP1[1]:脉宽调制器1捕获输入通道1 | ||
O | MAT0[1]:Timer0匹配输出通道1 | ||
P1[30] |
42 | I/O | P1[30]:GPIO口 |
I | USB_PWRD2:USB端口2电源状态 | ||
I | VBUS:指示USB总线当前电源。注意:当USB复位时这个信号必须为高电平 | ||
I | AD0[4]:A/D转换器0输入4 | ||
P1[31] |
40 | I/O | P1[31]:GPIO口 |
I | USB_OVRCR2:USB端口2过流状态 | ||
I/O | SCK1:SSP1串行时钟 | ||
I | AD0[5]:A/D转换器0输入5 |
3. P2口: P2口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P2口管脚描述如表4.3所示。
表4.3 LPC2400的P2口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P2[0] |
154 | I/O | P2[0]:GPIO口 |
O | PWM1[1]:脉宽调制器1输出1 | ||
O | TXD1:UART1发送输出端 | ||
O | TRACECLK/LCDPWR:跟踪时钟/LCD面板电源使能 | ||
P2[1] |
152 | I/O | P2[1]:GPIO口 |
O | PWM1[2]:脉宽调制器1输出2 | ||
I | RXD1:UART1接收输入端 | ||
O | PIPESTAT0/LCDLE:流水线状态位0/LCD行结束信号 | ||
P2[2] |
150 | I/O | P2[2]:GPIO口 |
O | PWM1[3]:脉宽调制器1输出3 | ||
I | CTS1:UART1清除发送输入端 | ||
O | PIPESTAT1/LCDCP:流水线状态位1/LCD面板时钟 | ||
P2[3] |
144 | I/O | P2[3]:GPIO口 |
O | PWM1[4]:脉宽调制器1输出4 | ||
I | DCD1:UART1数据载波检测输入端 | ||
O | PIPESTAT2/LCDFP:流水线状态位2/LCD帧脉冲(STN)垂直同步脉冲(TFT) | ||
P2[4] |
142 | I/O | P2[4]:GPIO口 |
O | PWM1[5]:脉宽调制器1输出5 | ||
I | DSR1:UART1数据设置就绪端 | ||
O | TRACESYNC/LCDAC:跟踪同步/LCD交流斜线驱动(STN)数据使能输出(TFT) | ||
P2[5] |
140 | I/O | P2[5]:GPIO口 |
O | PWM1[6]:脉宽调制器1输出6 | ||
O | DTR1:UART1数据终止就绪端 | ||
O | TRACEPKT0/LCDAC:跟踪分组位0/LCD行同步脉冲(STN)水平同步脉冲(TFT) | ||
P2[6] |
138 | I/O | P2[6]:GPIO口 |
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
I | RI1:UART1响铃指示输入端 | ||
O | TRACEPKT1/LCD[0]/LCD[4]:跟踪分组位1/LCD数据 | ||
P2[7] |
136 | I/O | P2[7]:GPIO口 |
I | RD2:CAN2接收输入 | ||
O | RTS1:UART1请求发送输出端 | ||
O | TRACEPKT2/LCD[1]/LCD[5]:跟踪分组位1/LCD数据 | ||
P2[8] |
134 | I/O | P2[8]:GPIO口 |
O | TD2:CAN2发送输出 | ||
O | TXD2:UART2接收输入端 | ||
O | TRACEPKT3/LCD[2]/LCD[6]:跟踪分组位3/LCD数据 | ||
P2[9] |
132 | I/O | P2[9]:GPIO口 |
O | USB_CONNECT1:USB1软连接控制 | ||
I | RXD2:UART2接收输入 | ||
I | EXTINT0/LCD[3]/LCD[7]:外部触发中断输入/LCD数据 | ||
P2[10] |
110 | I/O | P2[10]:GPIO口 |
I | EINT0:外部中断0输入 | ||
P2[11] |
108 | I/O | P2[11]:GPIO口 |
I/O | EINT1:外部中断1输入/LCDCLKIN:LCD时钟 | ||
I/O | MCIDAT1:SD/MMC接口数据线1 | ||
I/O | I2STX_CLK:I2S传输时钟。 | ||
P2[12] |
106 | I/O | P2[12]:GPIO口 |
I/O | EINT2:外部中断2输入/输出:LCD[4]/LCD[3]/LCD[8]/LCD[18] | ||
I/O | MCIDAT2:SD/MMC接口数据线2 | ||
I/O | I2STX_WS:I2S传输字选择。 | ||
P2[13] |
102 | I/O | P2[13]:GPIO口 |
I/O | EINT3:外部中断3输入/输出:LCD[5]/LCD[9]/LCD[19] | ||
I/O | MCIDAT3:SD/MMC接口数据线3 | ||
I/O | I2STX_SDA:I2S传输数据。 | ||
P2[14] |
91 | I/O | P2[14]:GPIO口 |
O | CS2:低电平有效片选信号2 | ||
I | CAP2[0]:Tmer2捕获输入通道0 | ||
I/O | SDA1:I2C1数据输入/输出 | ||
P2[15] |
99 | I/O | P2[15]:GPIO口 |
O | CS3:低电平有效片选信号3 | ||
I | CAP2[1]:Tmer2捕获输入通道1 | ||
I/O | SCL1:I2C1时钟输入/输出 | ||
P2[16] |
87 | I/O | P2[16]:GPIO口 |
O | CAS:低电平有效SDRAM列地址选择 | ||
P2[17] |
95 | I/O | P2[17]:GPIO口 |
O | RAS:低电平有效SDRAM行地址选择 | ||
P2[18] |
59 | I/O | P2[18]:GPIO口 |
O | CLKOUT0:SDRAM时钟0 | ||
P2[19] |
67 | I/O | P2[19]:GPIO口 |
O | CLKOUT1:SDRAM时钟1 | ||
P2[20] |
73 | I/O | P2[20]:GPIO口 |
O | DYCS0:SDRAM片选信号0 | ||
P2[21] |
81 | I/O | P2[21]:GPIO口 |
O | DYCS1:SDRAM片选信号1 | ||
P2[22] |
85 | I/O | P2[22]:GPIO口 |
O | DYCS2:SDRAM片选信号2 | ||
I | CAP3[0]:Timer3捕获输入通道0 | ||
I/O | SCK0:SSP0串行时钟 | ||
P2[23] |
64 | I/O | P2[23]:GPIO口 |
O | DYCS3:SDRAM片选信号3 | ||
I | CAP3[1]:Timer3捕获输入通道1 | ||
I/O | SSEL0:SSP0从机选择 | ||
P2[24] |
53 | I/O | P2[24]:GPIO口 |
O | CKEOUT0:SDRAM时钟使能信号0 | ||
P2[25] |
54 | I/O | P2[25]:GPIO口 |
O | CKEOUT1:SDRAM时钟使能信号1 | ||
P2[26] |
57 | I/O | P2[26]:GPIO口 |
O | CKEOUT2:SDRAM时钟使能信号2 | ||
O | MAT3[0]:Tmer3匹配输出通道0 | ||
I/O | MISO0:SSP0主机输入从机输出 | ||
P2[27] |
47 | I/O | P2[27]:GPIO口 |
O | CKEOUT3:SDRAM时钟使能信号3 | ||
O | MAT3[1]:Tmer3匹配输出通道1 | ||
I/O | MOSI0:SSP0主机输出从机输入 | ||
P2[28] |
49 | I/O | P2[28]:GPIO口 |
O | DQMOUT0:用于SDRAM和静态设备的数据掩码0 | ||
P2[29] |
43 | I/O | P2[29]:GPIO口 |
O | DQMOUT1:用于SDRAM和静态设备的数据掩码1 | ||
P2[30] |
31 | I/O | P2[30]:GPIO口 |
O | DQMOUT2:用于SDRAM和静态设备的数据掩码2 | ||
O | MAT3[2]:Tmer3匹配输出通道2 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
P2[31] |
39 | I/O | P2[31]:GPIO口 |
O | DQMOUT3:用于SDRAM和静态设备的数据掩码3 | ||
O | MAT3[3]:Tmer3匹配输出通道3 | ||
I/O | SCL2:I2C2时钟输入/输出 |
4. P3口: P3口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P3口管脚描述如表4.4所示。
表4.4 LPC2400的P3口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P3[0] |
197 | I/O | P3[0]:GPIO口 |
I/O | D0:外部存储器数据线0 | ||
P3[1] |
201 | I/O | P3[1]:GPIO口 |
I/O | D1:外部存储器数据线1 | ||
P3[2] |
207 | I/O | P3[2]:GPIO口 |
I/O | D2:外部存储器数据线2 | ||
P3[3] |
3 | I/O | P3[3]:GPIO口 |
I/O | D3:外部存储器数据线3 | ||
P3[4] |
13 | I/O | P3[4]:GPIO口 |
I/O | D4:外部存储器数据线4 | ||
P3[5] |
17 | I/O | P3[5]:GPIO口 |
I/O | D5:外部存储器数据线5 | ||
P3[6] |
23 | I/O | P3[6]:GPIO口 |
I/O | D6:外部存储器数据线6 | ||
P3[7] |
27 | I/O | P3[7]:GPIO口 |
I/O | D7:外部存储器数据线7 | ||
P3[8] |
191 | I/O | P3[8]:GPIO口 |
I/O | D8:外部存储器数据线8 | ||
P3[9] |
199 | I/O | P3[9]:GPIO口 |
I/O | D9:外部存储器数据线9 | ||
P3[10] |
205 | I/O | P3[10]:GPIO口 |
I/O | D10:外部存储器数据线10 | ||
P3[11] |
208 | I/O | P3[11]:GPIO口 |
I/O | D11:外部存储器数据线11 | ||
P3[12] |
1 | I/O | P3[12]:GPIO口 |
I/O | D12:外部存储器数据线12 | ||
P3[13] |
7 | I/O | P3[13]:GPIO口 |
I/O | D13:外部存储器数据线13 | ||
P3[14] |
21 | I/O | P3[14]:GPIO口 |
I/O | D14:外部存储器数据线14 | ||
P3[15] |
28 | I/O | P3[15]:GPIO口 |
I/O | D15:外部存储器数据线15 | ||
P3[16] |
137 | I/O | P3[16]:GPIO口 |
I/O | D16:外部存储器数据线16 | ||
O | PWM0[1]:脉宽调制器0输出1 | ||
O | TXD1:UART1发送输出端 | ||
P3[17] |
143 | I/O | P3[17]:GPIO口 |
I/O | D17:外部存储器数据线17 | ||
O | PWM0[2]:脉宽调制器0输出2 | ||
I | RXD1:UART1接收输入端 | ||
P3[18] |
151 | I/O | P3[18]:GPIO口 |
I/O | D18:外部存储器数据线18 | ||
O | PWM0[3]:脉宽调制器0输出3 | ||
I | CTS1:UART1清除发送输入端 | ||
P3[19] |
161 | I/O | P3[19]:GPIO口 |
I/O | D19:外部存储器数据线19 | ||
O | PWM0[4]:脉宽调制器0输出4 | ||
I | DCD1:UART1数据载波检测输入端 | ||
P3[20] |
167 | I/O | P3[20]:GPIO口 |
I/O | D20:外部存储器数据线20 | ||
O | PWM0[5]:脉宽调制器0输出5 | ||
I | DSR1:UART1数据设置就绪端 | ||
P3[21] |
175 | I/O | P3[21]:GPIO口 |
I/O | D21:外部存储器数据线21 | ||
O | PWM0[6]:脉宽调制器0输出6 | ||
O | DTR1:UART1数据终端准备就绪输出端 | ||
P3[22] |
195 | I/O | P3[22]:GPIO口 |
I/O | D22:外部存储器数据线22 | ||
I | PCAP0[0]:脉宽调制器0捕获输入通道0 | ||
I | RI1:UART1响铃指示输入端 | ||
P3[23] |
65 | I/O | P3[23]:GPIO口 |
I/O | D23:外部存储器数据线23 | ||
I | CAP0[0]:Timer0捕获输入通道0 | ||
I | PCAP1[0]:脉宽调制器1捕获输入通道0 | ||
P3[24] |
58 | I/O | P3[24]:GPIO口 |
I/O | D24:外部存储器数据线24 | ||
I | CAP0[1]:Timer0捕获输入通道1 | ||
O | PWM1[1]:脉宽调制器1输出1 | ||
P3[25] |
56 | I/O | P3[25]:GPIO口 |
I/O | D25:外部存储器数据线25 | ||
O | MAT0[0]:Tmer0匹配输出通道0 | ||
O | PWM1[2]:脉宽调制器1输出2 | ||
P3[26] |
55 | I/O | P3[26]:GPIO口 |
I/O | D26:外部存储器数据线26 | ||
O | MAT0[1]:Tmer0匹配输出通道1 | ||
O | PWM1[3]:脉宽调制器1输出3 | ||
P3[27] |
203 | I/O | P3[27]:GPIO口 |
I/O | D27:外部存储器数据线27 | ||
I | CAP1[0]:Timer1捕获输入通道0 | ||
O | PWM1[4]:脉宽调制器1输出4 | ||
P3[28] |
5 | I/O | P3[28]:GPIO口 |
I/O | D28:外部存储器数据线28 | ||
I | CAP1[1]:Timer1捕获输入通道1 | ||
O | PWM1[5]:脉宽调制器1输出5 | ||
P3[29] |
11 | I/O | P3[29]:GPIO口 |
I/O | D29:外部存储器数据线29 | ||
O | MAT1[0]:Tmer1匹配输出通道0 | ||
O | PWM1[6]:脉宽调制器1输出6 | ||
P3[30] |
19 | I/O | P3[30]:GPIO口 |
I/O | D30:外部存储器数据线30 | ||
O | MAT1[1]:Tmer1匹配输出通道1 | ||
O | RTS1:UART1请求发送输出端 | ||
P3[31] |
25 | I/O | P3[31]:GPIO口 |
I/O | D31:外部存储器数据线31 | ||
O | MAT1[2]:Tmer1匹配输出通道2 |
5. P4口: P4口也是一个32位的双向多功能I/O口,每位的方向可单独控制,且每位的功能取决于管脚连接模块的管脚功能选择。LPC2400的P4口管脚描述如表4.5所示。
表4.5 LPC2400的P4口管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
P4[0] |
75 | I/O | P4[0]:GPIO口 |
I/O | A0:外部存储器地址线0 | ||
P4[1] |
79 | I/O | P4[1]:GPIO口 |
I/O | A1:外部存储器地址线1 | ||
P4[2] |
83 | I/O | P4[2]:GPIO口 |
I/O | A2:外部存储器地址线2 | ||
P4[3] |
97 | I/O | P4[3]:GPIO口 |
I/O | A3:外部存储器地址线3 | ||
P4[4] |
103 | I/O | P4[4]:GPIO口 |
I/O | A4:外部存储器地址线4 | ||
P4[5] |
107 | I/O | P4[5]:GPIO口 |
I/O | A5:外部存储器地址线5 | ||
P4[6] |
113 | I/O | P4[6]:GPIO口 |
I/O | A6:外部存储器地址线6 | ||
P4[7] |
121 | I/O | P4[7]:GPIO口 |
I/O | A7:外部存储器地址线7 | ||
P4[8] |
1271 | I/O | P4[8]:GPIO口 |
I/O | A8:外部存储器地址线8 | ||
P4[9] |
131 | I/O | P4[9]:GPIO口 |
I/O | A9:外部存储器地址线9 | ||
P4[10] |
135 | I/O | P4[10]:GPIO口 |
I/O | A10:外部存储器地址线10 | ||
P4[11] |
145 | I/O | P4[11]:GPIO口 |
I/O | A11:外部存储器地址线11 | ||
P4[12] |
149 | I/O | P4[12]:GPIO口 |
I/O | A12:外部存储器地址线12 | ||
P4[13] |
155 | I/O | P4[13]:GPIO口 |
I/O | A13:外部存储器地址线13 | ||
P4[14] |
159 | I/O | P4[14]:GPIO口 |
I/O | A14:外部存储器地址线14 | ||
P4[15] |
173 | I/O | P4[15]:GPIO口 |
I/O | A15:外部存储器地址线15 | ||
P4[16] |
101 | I/O | P4[16]:GPIO口 |
I/O | A16:外部存储器地址线16 | ||
P4[17] |
104 | I/O | P4[17]:GPIO口 |
I/O | A17:外部存储器地址线17 | ||
P4[18] |
105 | I/O | P4[18]:GPIO口 |
I/O | A18:外部存储器地址线18 | ||
P4[19] |
111 | I/O | P4[19]:GPIO口 |
I/O | A19:外部存储器地址线19 | ||
P4[20] |
109 | I/O | P4[20]:GPIO口 |
I/O | A20:外部存储器地址线20 | ||
I/O | SDA2:I2C2数据输入/输出 | ||
I/O | SCK1:SSP1串行时钟 | ||
P4[21] |
115 | I/O | P4[21]:GPIO口 |
I/O | A21:外部存储器地址线21 | ||
I/O | SCL2:I2C2时钟输入/输出 | ||
I/O | SSEL1:SSP1从机选择 | ||
P4[22] |
123 | I/O | P4[22]:GPIO口 |
I/O | A22:外部存储器地址线22 | ||
O | TXD2:UART2发送输出端 | ||
I/O | MISO1:SSP1主机输入从机输出 | ||
P4[23] |
129 | I/O | P4[23]:GPIO口 |
I/O | A23:外部存储器地址线23 | ||
I | RXD2:UART2接收输入端 | ||
I/O | MOSI1:SSP1主机输出从机输入 | ||
P4[24] |
183 | I/O | P4[24]:GPIO口 |
O | OE:低电平有效输出使能信号 | ||
P4[25] |
179 | I/O | P4[25]:GPIO口 |
O | WE:低电平有效写使能信号 | ||
P4[26] |
119 | I/O | P4[26]:GPIO口 |
O | BLS0:低电平有效字节定位选择信号0 | ||
P4[27] |
139 | I/O | P4[27]:GPIO口 |
O | BLS1:低电平有效字节定位选择信号1 | ||
P4[28] |
170 | I/O | P4[28]:GPIO口 |
O | BLS2:低电平有效字节定位选择信号2 | ||
O | MAT2[0]/LCD[6]/LCD[10]/LCD[2]:Timer2匹配输出通道0/LCD数据 | ||
O | TXD3:UART3发送输出端 | ||
P4[29] |
176 | I/O | P4[29]:GPIO口 |
O | BLS3:低电平有效字节定位选择信号3 | ||
O | MAT2[1]/LCD[7]/LCD[11]/LCD[3]:Timer2匹配输出通道1/LCD数据 | ||
I | RXD3:UART3接收输入端 | ||
P4[30] |
187 | I/O | P4[30]:GPIO口 |
O | CS0:低电平有效片选信号0 | ||
P4[31] |
193 | I/O | P4[31]:GPIO口 |
O | CS1:低电平有效片选信号1 |
6. 电源、复位、晶振及其它管脚的描述如表4.6所示。
表4.6 LPC2400的其它管脚描述
管脚名称 | 引脚号 | 类型 | 描 述 |
ALARM | 37 | O | RTC实时时钟控制输出。这是一个1.8V引脚,当RTC产生报警信号时此引脚变为高电平。 |
USB_D-2 | 52 | I/O | USB端口2双向D-线 |
DBGEN | 9 | I | JTAG接口控制信号,也用于边界扫描 |
TDO | 2 | O | JTAG接口测试数据输出 |
TDI | 4 | I | JTAG接口测试数据输入 |
TMS | 6 | I | JTAG接口测试模式选择 |
TRST | 8 | I | JTAG接口测试复位,低电平有效 |
TCK | 10 | I | JTAG接口测试时钟 |
RTCK | 206 | I/O | JTAG接口控制信号,当此引脚低电平时使能ETM引脚(P2[9:0]),用于复位后操作跟踪端口 |
RSTOUT | 29 | O | 这是个1.8V引脚,当此引脚低电平时表示LPC2478处于复位状态 |
RESET | 35 | I | 外部复位输入,低电平有效。该引脚具有迟滞作用的TTL电平,能承受5V电压,当此引脚低电平时器件复位,I/O口和外围功能进入默认状态,处理器从地址0开始执行程序 |
XTAL1 | 44 | I | 振荡器电路和内部时钟发生电路输入 |
XTAL2 | 46 | O | 振荡放大器输出 |
RTCX1 | 34 | I | RTC振荡器电路输入 |
RTCX2 | 36 | O | RTC振荡器电路输出 |
VSSIO | 33,63,77,93,114,133,148,169,189,200 | I | 地:数字IO脚的0V电压参考点 |
VSSCORE | 32,84,172 | I | 地:处理器内核的0V电压参考点 |
VSSA | 22 | I | 模拟地:0V电压参考点,与VSS电压相同,为了降低噪声和出错几率,两者应当隔离 |
VDD(3V3) | 15,60,71,89,112,125,146,165,181,198 | I | 3.3V供应电压:I/O口电源供应电压 |
NC | 30,117,141 | I | 未连接引脚 |
VDD(DCDC)(3V3) | 26,86,174 | I | 3.3V直流到直流转换供应电压:为片内DC-to-DC转换器提供电源 |
VDDA | 20 | I | 模拟3.3V供应电压:为DAC和ADC供电,与VDD(3V3)电压相同,为了降低噪声和出错几率,两者应当隔离 |
VREF | 24 | I | ADC参考电压:与VDD(3V3)电压相同,为了降低噪声和出错几率,两者应当隔离,为ADC和DAC提供参考电压 |
VBAT | 38 | I | RTC电源供应:3.3V,为RTC提供电源 |
注:1. 本引脚定义表以LPC2478为准,LPC2470与此相同,LPC2468没有引脚定义的LCD部分。
2. 类型表示引脚信号方向:I/O为输入/输出,I为输入,O为输出。
4.2.2 引脚连接模块
从表4.1~表4.6可以看到,LPC2400系列芯片的绝大部分引脚是复用的,每根引脚都有可能用于不同的外设功能。引脚具体用于什么外设功能是由引脚连接模块进行配置来实现的。当引脚选择了一个功能时,则其它功能无效。
在使用外设时,应当在激活外设以及使能任何相关的中断之前,将外设连接到相应的引脚上。否则,即使使用引脚连接模块激活外设,此激活也是无效的。
引脚连接模块共有21个寄存器,包括11个引脚功能选择寄存器和10个引脚模式寄存器。
1. 引脚功能选择寄存器(PINSEL0~PINSEL10)
引脚功能选择寄存器用于控制每个引脚的功能,每个寄存器32位,每2个bit用于控制1个引脚功能选择。以PINSEL0寄存器为例,寄存器的[1:0]位用于控制P0[0]引脚,[3:2]位用于控制P0[1]引脚,[31:30]位用于控制P0[15]引脚。而PINSEL1寄存器的[1:0]位用于控制P0[16]引脚,[3:2]位用于控制P0[17]引脚,[31:30]位用于控制P0[31]引脚。其余依次类推。
PINSEL0~PINSEL9寄存器,每两个寄存器用于一个端口组:PINSEL0寄存器用于P0口的[15:0]引脚,PINSEL1寄存器用于P0口的[31:30]引脚;PINSEL2寄存器用于P1口的[15:0]引脚,PINSEL3寄存器用于P1口的[31:30]引脚;PINSEL4寄存器用于P2口的[15:0]引脚,PINSEL5寄存器用于P2口的[31:30]引脚;PINSEL6寄存器用于P3口的[15:0]引脚,PINSEL7寄存器用于P3口的[31:30]引脚;PINSEL8寄存器用于P4口的[15:0]引脚,PINSEL9寄存器用于P4口的[31:30]引脚。
每一对比特设置引脚功能的定义如表4.7所示。
表4.7 引脚功能选择寄存器位
PINSEL0~PINSEL9值 | 功能 | 复位值 |
00 | 主功能(缺省),一般为GPIO口 | 00 |
01 | 第一备用功能 | |
10 | 第二备用功能 | |
11 | 第三备用功能 |
每个引脚默认为GPIO口,通过设置PINSEL的值来定义其引脚功能。以P0[0]脚为例,当PINSEL0寄存器的[1:0]位为00时,引脚功能为GPIO口;为01时,引脚功能为CAN1接收器输入;为10时,引脚功能为UART3发送输出端;为11时,引脚功能为I2C1数据输入/输出。
每个引脚的具体定义方法参见表4.1~表4.6。表格中的引脚功能按PINSEL值排列。某些引脚只有两种功能,此时只使用PINSEL值00和01,值10和11保留。
PINSEL10寄存器用于控制ETM接口引脚。该寄存器只使用了位3,其余位均保留。当第3位为0时,关闭ETM接口功能;为1时启用ETM跟踪接口功能,此时无论PINSEL4怎么定义,P2[0]~P2[8]脚均用于ETM跟踪功能。
引脚功能被选择为GPIO时,引脚的方向控制由GPIO方向寄存器IODIR控制。对于其它功能,引脚的方向是由引脚功能控制的。
2. 引脚模式寄存器(PINMODE0~PINMODE9)
引脚模式寄存器PINMODE为所有的GPIO端口控制片内上拉/下拉电阻特性。当使用片内上拉或下接电阻时,若引脚信号不确定,使用上拉时为高电平;而下拉时拉为低电平。
与PNSEL寄存器一样,PINMODE寄存器每2个bit控制1个引脚。每两个寄存器控制一个端口组。
PINMOD寄存器取值如表4.8所示。
表4.8 引脚模式寄存器位
PINMODE0~PINMODE9值 | 功能 | 复位值 |
00 | 使能引脚片内上拉电阻 | 00 |
01 | 保留 | |
10 | 既不使用上拉也不使用下拉 | |
11 | 使能引脚片内下拉电阻 |
引脚连接模块的寄存器总表如表4.9所示。
表4.9 引脚控制模块寄存器列表
寄存器名 | 描述 | 访问 | 复位值 | 地址 |
PINSEL0 | 引脚功能选择寄存器0 | 读/写 | 0x0000 0000 | 0xE002 C000 |
PINSEL1 | 引脚功能选择寄存器1 | 读/写 | 0x0000 0000 | 0xE002 C004 |
PINSEL2 | 引脚功能选择寄存器2 | 读/写 | 0x0000 0000 | 0xE002 C008 |
PINSEL3 | 引脚功能选择寄存器3 | 读/写 | 0x0000 0000 | 0xE002 C00C |
PINSEL4 | 引脚功能选择寄存器4 | 读/写 | 0x0000 0000 | 0xE002 C010 |
PINSEL5 | 引脚功能选择寄存器5 | 读/写 | 0x0000 0000 | 0xE002 C014 |
PINSEL6 | 引脚功能选择寄存器6 | 读/写 | 0x0000 0000 | 0xE002 C018 |
PINSEL7 | 引脚功能选择寄存器7 | 读/写 | 0x0000 0000 | 0xE002 C01C |
PINSEL8 | 引脚功能选择寄存器8 | 读/写 | 0x0000 0000 | 0xE002 C020 |
PINSEL9 | 引脚功能选择寄存器9 | 读/写 | 0x0000 0000 | 0xE002 C024 |
PINSEL10 | 引脚功能选择寄存器10 | 读/写 | 0x0000 0000 | 0xE002 C028 |
PINMODE0 | 引脚模式寄存器0 | 读/写 | 0x0000 0000 | 0xE002 C040 |
PINMODE1 | 引脚模式寄存器1 | 读/写 | 0x0000 0000 | 0xE002 C044 |
PINMODE2 | 引脚模式寄存器2 | 读/写 | 0x0000 0000 | 0xE002 C048 |
PINMODE3 | 引脚模式寄存器3 | 读/写 | 0x0000 0000 | 0xE002 C04C |
PINMODE4 | 引脚模式寄存器4 | 读/写 | 0x0000 0000 | 0xE002 C050 |
PINMODE5 | 引脚模式寄存器5 | 读/写 | 0x0000 0000 | 0xE002 C054 |
PINMODE6 | 引脚模式寄存器6 | 读/写 | 0x0000 0000 | 0xE002 C058 |
PINMODE7 | 引脚模式寄存器7 | 读/写 | 0x0000 0000 | 0xE002 C05C |
PINMODE8 | 引脚模式寄存器8 | 读/写 | 0x0000 0000 | 0xE002 C060 |
PINMODE9 | 引脚模式寄存器9 | 读/写 | 0x0000 0000 | 0xE002 C064 |
4.2.3 引脚连接模块的使用举例
LPC2400系列芯片外设功能在使用前必须先设置其引脚功能。引脚功能是通过对引脚连接模块编程来实现的。
例4.1:使用串口UART0
串口UART0只使用TXD0和RXD0两根引脚来进行数据的串行发送和接收,使用时需将对应的两根引脚P0[2]和P0[3]设置成TXD0和RXD0功能。查表4.1可知,两根引脚的对应PINSEL值均为01,因此写入PINSEL0寄存器的值为0x00000050。
相应程序行为:
PINSEL0 = 0x00000050;
或
PINSEL0 = 0x05<<4;
注意,由于PINSEL是可读写的寄存器,上述写法会使其它引脚的功能回到初始化默认配置。为了不影响其它引脚的功能配置,实用中更好的办法是:先读取寄存器值,然后进行逻辑与和逻辑或操作,再回写到寄存器。
PINSEL0 = (PINSEL0 &0xFFFFFF0F) | (0x05<<4);
其余的引脚外设功能均可以采用类似方法进行操作。
例4.2:启动代码中的相关部分
启动代码负责对芯片复位后的硬件功能进行初始化。芯片复位时,各PINSEL寄存器会自动设置为默认值,所以复位后芯片引脚的功能是确定的。
如果启动以后,硬件系统各外设功能使用情况比较固定,可以将对应的引脚功能设置写入启动代码以加快启动速度。否则,可以在启动时将所有引脚都配置成GPIO端口,具体使用某部分外设时再对相关引脚进行初始化。
……
PINSEL0 = 0x00000000;
PINSEL1 = 0x00000000;
PINSEL2 = 0x00000000;
PINSEL3 = 0x00000000;
PINSEL4 = 0x00000000;
PINSEL5 = 0x00000000;
PINSEL6 = 0x00000000;
PINSEL7 = 0x00000000;
PINSEL8 = 0x00000000;
PINSEL9 = 0x00000000;
PINSEL10 = 0x00000000;
……
4.3 存储器管理
LPC2400系列芯片集成了512KB的片内Flash存储器和64KB的静态SRAM(LPC2470没有片内Flash),其中Flash存储器可以用做代码和数据的固态存储。对Flash存储器的编程可以通过几种方法来实现:通过串口UART0进行的在系统编程(ISP),通过调用嵌入片内的固化代码进行的在应用编程(IAP)以及通过内置的JTAG接口编程。
SRAM支持8位、16位和32位访问。需要注意的是,SRAM控制器包含一个回写缓冲区,它用于防止CPU在连续的写操作时停止运行。回写缓冲区总是保存着软件发送到SRAM的最后1字节。数据只有在软件执行另外一次写操作时被写入SRAM。如果发生芯片复位,实际的SRAM内容将不会反映最近一次的写请求。任何在复位后检查SRAM内容的程序都必须注意这一点。
LPC2400系列芯片具备外部存储器接口,通过外部存储器控制器(EMC)可以扩展两组共8个Bank的存储器组(Static memory bank0 ~ bank3,Dynamic memory bank0 ~ bank3)。对于外扩的RAM存储器,使用ARM的LDR/STR指令即可进行数据的读写操作;而对于外扩的Flash(NOR)型,可以使用LDR指令读取数据,但是不能使用STR指令直接写数据,而是根据Flash芯片写操作时序进行控制,实现Flash的擦除编程。如果需要将程序代码烧写到扩展的Flash,则需要运行一个装载程序(Loader程序,一般由用户自行编写),然后由Loader程序对Flash存储器进行擦除和烧写。
4.3.1 存储器映射
ARM处理器共有4GB的寻址空间,LPC2400系列处理器将这个空间划分成了几个不同的存储器组。图4.3所示为复位后从用户角度所看到的系统存储器地址空间映射。
图4.3 LPC2400系统存储器映射
表4.10 LPC2400存储器使用和细节
地址范围 | 通用用途 | 地址范围细节 | 功能描述 |
0x0000 0000 - 0x3FFF FFFF | 片内非易失存储器和快速I/O | 0x0000 0000 – 0x0007 FFFF | Flash 存储器(512KB) |
0x3FFF C000 – 0x3FFF FFFF | 快速GPIO寄存器 | ||
0x4000 0000 - 0x7FFF FFFF | 片内存储器 | 0x4000 0000 – 0x4000 FFFF | RAM(64KB) |
0x7FE0 0000 – 0x7FE0 3FFF | 以太网RAM(16KB) | ||
0x7FD0 0000 – 0x7FD0 3FFF | USB RAM(16KB) | ||
0x8000 0000 - 0xDFFF FFFF
| 片外存储器 | 四个静态存储器bank,每个16MB | |
0x8000 0000 – 0x80FF FFFF | 静态存储器bank0 | ||
0x8100 0000 – 0x81FF FFFF | 静态存储器bank1 | ||
0x8200 0000 – 0x82FF FFFF | 静态存储器bank2 | ||
0x8300 0000 – 0x83FF FFFF | 静态存储器bank3 | ||
四个动态存储器bank,每个256MB | |||
0xA000 0000 – 0xAFFF FFFF | 动态存储器bank0 | ||
0xB000 0000 – 0x3FFF FFFF | 动态存储器bank1 | ||
0xC000 0000 – 0xCFFF FFFF | 动态存储器bank2 | ||
0xD000 0000 – 0xDFFF FFFF | 动态存储器bank3 | ||
0xE000 0000 - 0xEFFF FFFF | APB 外设 | 36个外设模块,每个16KB | |
0xF000 0000 - 0xFFFF FFFF | AHB 外设 |
|
1. 外设存储器映射
LPC2400系列处理器的外设根据内部总线分为AHB和APB外设两类。AHB外设和APB外设在存储空间里都占2MB的区域,可各自分配最多128个外设。每个外设空间的规格都为16KB。所有外设寄存器不管规格大小,都按照字地址进行分配(32位边界),且不管字还是半字寄存器都是一次性访问。例如,不可能对一个字寄存器的最高字节执行单独的读或写操作。
图4.4表示了AHB和APB外设占用存储空间示意图。
图4.4 外设存储器映射(AHB外设和APB外设)
图4.5所示为AHB外设占用存储空间示意图。其中0-4号外设分别分配给以太网控制器、GPDMA控制器、EMC控制器、USB控制器和LCD控制器,其它外设编号未使用。
图4.5 AHB外设存储器映射
表4.11所示为APB外设占用的存储空间示意图。
表4.11 APB外设存储器映射
APB 外设 | 基地址 | 外设名 |
0 | 0xE000 0000 | 看门狗 |
1 | 0xE000 4000 | 定时器0 |
2 | 0xE000 8000 | 定时器1 |
3 | 0xE000 C000 | UART0 |
4 | 0xE001 0000 | UART1 |
5 | 0xE001 4000 | PWM0 |
6 | 0xE001 8000 | PWM1 |
7 | 0xE001 C000 | I2C0 |
8 | 0xE002 0000 | SPI |
9 | 0xE002 4000 | 实时时钟RTC |
10 | 0xE002 8000 | GPIO |
11 | 0xE002 C000 | 引脚连接模块 |
12 | 0xE003 0000 | SSP1 |
13 | 0xE003 4000 | ADC |
14 | 0xE003 8000 | CAN接收滤波器RAM |
15 | 0xE003 C000 | CAN接收滤波器寄存器 |
16 | 0xE004 0000 | CAN通用寄存器 |
17 | 0xE004 4000 | CAN控制器1 |
18 | 0xE004 8000 | CAN控制器2 |
19 到 22 | 0xE004 C000 - 0xE005 8000 | 未使用 |
23 | 0xE005 C000 | I2C1 |
24 | 0xE006 0000 | 未使用 |
25 | 0xE006 4000 | 未使用 |
26 | 0xE006 8000 | SSP0 |
27 | 0xE006 C000 | DAC |
28 | 0xE007 0000 | 定时器2 |
29 | 0xE007 4000 | 定时器3 |
30 | 0xE007 8000 | UART2 |
31 | 0xE007 C000 | UART3 |
32 | 0xE008 0000 | I2C3 |
33 | 0xE008 4000 | 电池RAM |
34 | 0xE008 8000 | I2S |
35 | 0xE008 C000 | SD/MMC卡接口 |
36 到126 | 0xE009 0000 - 0xE01F BFFF | 未使用 |
127 | 0xE01F C000 | 系统控制模块 |
在对LPC2400系列芯片编程时,要注意不要对一个保留地址或未使用区域的地址进行寻址,否则LPC2400将产生一个数据中止异常。另外,对AHB或APB外设地址执行任何指令取指都会导致产生预取指中止异常。
2. 存储器重映射和boot ROM
存储器映射的一个基本概念是:每个存储器组在存储器映射中都有一个“物理上的”位置。它是一个地址范围,该范围内可写入程序代码,每一个存储器空间的容量都永久固定在同一个位置,这样就不需要将代码设计成在不同地址范围内运行。
因为ARM7处理器上的中断向量所处具体位置(地址0x0000 0000~0x0000 001C,见表4.12)的要求,Boot ROM和SRAM空间的一小部分空间需要重新映射,来实现在不同操作模式下对中断的不同使用。
表4.12 ARM 异常向量位置
地址 | 异常 |
0x0000 0000 | 复位 |
0x0000 0004 | 未定义指令 |
0x0000 0008 | 软件中断 |
0x0000 000C | 预取指中止(指令读取存储器出错) |
0x0000 0010 | 数据中止(数据访问存储器出错) |
0x0000 0014 | 保留 |
0x0000 0018 | IRQ |
0x0000 001C | FIQ |
为了与将来器件兼容,整个Boot ROM都被映射到片内存储器空间的顶端。在这种方式下,使用较大或较小的Flash模块都不需要改变Boot ROM(需要改变Boot装载程序自身的代码)的位置或改变Boot ROM中断向量的映射。除了中断向量之外的存储器空间都保持固定的位置。
存储器重新映射的部分允许在不同模式下处理中断。LPC2400共支持3种存储器映射模式,见表4.13。当处理器工作在用户Flash模式下时,不需要进行中断向量的重新映射,而在其它模式下则需要重新映射。它包括中断向量区(32 字节)和额外的32 字节,一共是64 字节。重新映射的代码位置与地址0x0000 0000~0x0000 003F 重叠,包含在SRAM、Flash 和Boot Block 中的向量必须包含跳转到实际中断处理程序的分支或者其它执行跳转到中断处理程序的转移指令。一个位于Flash 存储器中的典型用户程序可以将整个FIQ 处理程序放置在地址0x0000 001C 而不需要考虑存储器的边界。
表4.13 LPC2400存储器映射模式
模式 | 激活 | 用途 |
Boot装载程序模式 | 由任何复位硬件激活 | 在任何复位后都会执行Boot 装载程序。Boot ROM中断向量映射到存储器的底部以允许处理异常并在Boot 装载过程中使用中断。 |
用户Flash 模式 | 由Boot 代码软件激活 | 当在存储器中识别了一个有效的用户程序标识并且Boot 装载操作未被执行时,由Boot 装载程序激活。中断向量不作重新映射,它位于Flash 存储器的底部。 |
用户RAM 模式 | 由用户程序软件激活 | 由用户程序激活。中断向量重新映射到静态RAM 的底部。 |
选择这种配置有三个原因:
1) 使Flash 存储器中的FIQ处理程序不必考虑因为重新映射所导致的存储器边界问题;
2) 用来处理代码空间中段边界仲裁的SRAM 和Boot ROM向量的使用大大减少;
3) 为超过单字转移指令范围的跳转提供空间来保存常量。
重新映射的存储器组,包括Boot ROM和中断向量,除了重新映射的地址外,仍然继续出现在它们最初的位置。存储器重映射以后的存储器空间见图4.6所示。
图4.6已重新映射和可重新映射区域的低存储器空间
3. 存储器映射控制寄存器MEMMAP(Memory Mapping Control Register)
存储器映射控制寄存器用于改变从地址0x00000000开始的中断向量的映射。这允许运行在不同存储器空间中的代码对中断进行控制。MEMMAP是一个可读写寄存器,地址为0xE01FC040,功能为选择从Flash Boot Block、用户Flash 或RAM中读取ARM 中断向量。
表4.14 存储器映射控制寄存器MEMMAP
位 | 符号 | 功能描述 | 复位值 |
1:0 | MAP | 00:Boot 装载程序模式。中断向量从Boot Block 重新映射 01:用户Flash 模式。中断向量不重新映射,它位于Flash 中 10:用户RAM 模式。中断向量从静态RAM 重新映射 11:保留,不使用该选项 警告:不正确的设定会导致器件的错误操作 | 00 |
7:2 | 保留 |
| NA |
LPC2400的MAP位的硬件复位值为00。Boot装载程序会将用户看到的复位值更改,该程序总是在复位后立即运行。
存储器映射控制寄存器MEMMAP只从处理ARM异常(中断)必需的3 个数据源(FLASH中断向量、SRAM中断向量和Boot ROM中断向量,每个64 字节)中选择一个使用。
每当产生一个软件中断请求,ARM内核就从0x0000 0008 处取出32 位数据(见表4.12,“ARM异常向量位置”)。这就意味着当MEMMAP[1:0]=10(用户RAM模式)时,从0x0000 0008的读数/取指是对0x4000 0008单元进行操作。当MEMMAP[1:0]=00(Boot装载程序模式)时,从0x0000 0008的读数/取指是对0x7FFF E008 单元的数据进行操作(Boot ROM从片内Flash存储器重新映射)。
4.3.2 存储器加速模块
当LPC2400 在运行Flash存储器内的代码时,器件内部的存储器加速模块(MAM,Memory Accelerator Module)最大限度地提高了ARM处理器的性能,而此只需要使用一个简单的Flash组就可实现了。
1.操作
存储器加速模块(MAM)可以将需要的下一个ARM 指令锁存,以防止CPU 取指暂停。与以前的其它器件使用2个Flash组相比,LPC2400只使用一组Flash 存储器。这个Flash组包含3 个128位的缓冲区:预取指缓冲区、分支跟踪缓冲区和数据缓冲区。
当预取指缓冲区和分支跟踪缓冲区不能满足一次指令取指的需要,并且预取指还没有启动时,ARM在启动128位行的取指时暂停。如果预取指已经启动但还未完成,则ARM暂停的时间会更短一些。预取指在Flash结束前面的访问后立即启动,除非被数据访问中止。
预取指行被Flash 模块锁存,但MAM 不能在预取指缓冲区中捕获该行,直到ARM 内核给出预取指开始的地址。如果内核给出的地址与预取指地址不同,则预取指行丢弃。
每个预取指缓冲区和分支跟踪缓冲区包含4个32位ARM指令或8个16位Thumb指令。在连续执行代码时,通常预取指缓冲区包含当前指令和含有该指令的整个Flash 行。
分支和其它程序流的变化导致前面所讲述的连续指令取指出现中断。分支跟踪缓冲区捕获发生非连续中断的行。如果相同的分支再次出现,则从分支跟踪缓冲区内取出下条指令。当分支超出了预取指和分支跟踪缓冲区内容时,需要中止几个时钟来装载分支跟踪缓冲区。这样,不会再出现指令取指延时,直到一个新的和不同的分支出现。
2.MAM 结构
存储器加速器分成以下几个功能模块:1个Flash地址锁存和1个增量器功能用于预取指地址、1个128位的预取指缓冲区及其相关的地址锁存和比较器、1个128位的分支跟踪缓冲区及其相关的地址锁存和比较器。
图4.7 存储器加速器模块框图
3.MAM 的操作模式
MAM 定义了3种操作模式,用户可以在性能和可预测性之间进行选择:
1)MAM关闭。所有存储器请求都会导致Flash的读操作,无指令预取指。
2)MAM部分使能。如果数据可用,则从保持锁存区执行连续的指令访问。指令预取指使能。非连续的指令访问启动Flash读操作。这意味着所有的转移指令都会导致对存储器的取指。由于缓冲的数据访问时序很难预测并且非常依赖于所处的状况,因此所有数据操作都会导致Flash读操作。
3)MAM 完全使能。任何存储器请求(代码或数据),如果其值已经包含在其中一个保持锁存当中,那么从缓冲区执行该代码或数据的访问。指令预取指使能。Flash读操作用于指令的预取指和当前缓冲区所没有的代码或数据的访问。
在复位后,MAM 默认为禁止状态,软件可以随时将存储器访问加速打开或关闭。这样就可使大多数应用程序以最高速度运行,而某些要求更精确定时的功能可以较慢但更可预测的速度运行。
在启用MAM以后,Flash编程功能不受MAM的控制,而是作为一个独立的功能进行处理。Boot ROM扇区包含可作为应用程序的一部分调用的Flash 编程算法(即IAP 代码)和一个可对Flash 存储器进行串行编程的装载程序(即ISP 代码)。
4.寄存器描述
1)MAM控制寄存器-MAM Control Register(MAMCR - 0xE01FC000)。决定MAM的操作模式。两个配置位选择MAM的3种操作模式。在复位后,MAM功能被禁止。改变MAM操作模式会导致MAM所有的保持锁存内容无效,因此需要执行新的Flash读操作。
表4.15 存储器加速器模块控制寄存器(MAMCR)
MAMCR | 功能 | 描述 | 复位值 |
1:0 | MAM 模式控制 | 00-MAM 功能被禁止 01-MAM 功能部分使能 10-MAM 功能完全使能 11-保留 | 0 |
7:2 | 保留 | 保留 | NA |
2)MAM定时寄存器-MAM Timing Register(MAMTIM - 0xE01FC004)。决定Flash存储器取指所使用的时钟个数(1到7个处理器时钟),使用多少个cclk周期访问Flash存储器。这样可调整MAM时序使其匹配处理器操作频率。Flash访问时间可以从1到7个时钟,单个时钟的Flash访问实际上关闭了MAM,这种情况下可以选择MAM模式对功耗进行优化。
表4.16 存储器加速定时寄存器(MAMTIM)
MAMTIM | 功能 | 描述 | 复位值 |
2:0 |
MAM取指周期 | 000=0,保留 001=1,MAM 取指周期为1 个处理器时钟(cclk) 010=2,MAM 取指周期为2 个处理器时钟(cclk) 011=3,MAM 取指周期为3 个处理器时钟(cclk) 100=4,MAM 取指周期为4 个处理器时钟(cclk) 101=5,MAM 取指周期为5 个处理器时钟(cclk) 110=6,MAM 取指周期为6 个处理器时钟(cclk) 111=7,MAM 取指周期为7 个处理器时钟(cclk) 警告:不正确的设定会导致器件的错误操作! |
0x07 |
7:3 | 保留 | 保留 | NA |
5.MAM 使用注意事项
1)MAM 定时值问题
当改变MAM定时值时,必须先通过向MAMCR写入0来关闭MAM,然后将新值写入MAMTIM,最后,将需要的操作模式的对应值写入MAMCR,再次打开MAM。
对于低于20MHz的系统时钟,MAMTIM设定为1;对于20MHz到40MHz之间的系统时钟,建议将MAMTIM设定为2;而在高于40MHz的系统时钟下,建议使用3。
2)Flash 编程问题
在编程和擦除操作过程中不允许访问Flash存储器。如果在Flash模块忙时存储器请求访问Flash地址,MAM就必须强制CPU等待(这通过声明ARM7TDMI-S局部总线信号CLKEN来实现)。在某些情况下,代码执行的延迟会导致看门狗超时。用户必须注意到这种可能性,并采取措施来确保不会在编程或擦除Flash 存储器时出现非预期的看门狗复位,从而导致系统故障。
6.MAM使用举例
MAM功能在LPC2400的启动代码中实现,可以根据Fcclk的大小来自动设置MAM, (在target.c文件中)。首先要将MAM功能禁止,然后根据Fcclk的大小来设置MAM定时寄存器(通过条件编译实现,Fcclk的定义在target.h文件中),最后再使能MAM。
代码清单4.1
void TargetResetInit(void)
{ …
MAMCR = 0; // 禁止MAM 功能
#if Fcclk < 20000000
MAMTIM = 1; // 系统时钟低于20M,建议设置为1
#else
#if Fcclk < 40000000
MAMTIM = 2; // 系统时钟再20M~40M 之间,建议设置为2
#else
MAMTIM = 3; // 系统时钟高于40M,建议设置为3
#endif
MAMCR = 2; // 使能MAM
…}
4.3.3 外部存储器控制器
LPC2400的外部存储器控制器(EMC)是一个AMBA总线AHB从机模块,它支持多种不同结构的存储器,包括常用的异步静态存储器,如RAM、ROM和Flash等,也支持动态存储器,如单数据率SDRAM等。EMC模块可以同时支持多达8个单独配置的存储器组,其中静态存储器和动态存储器各4个Bank。静态存储器组由片选引脚CS0~CS3选中,每个组存储容量16MB,支持RAM、ROM、Flash和其它一些外部I/O部件,支持8位、16位和32位数据宽度。动态存储器组由片选引脚DYCS0~DYCS3选中,每个组存储容量256MB,支持单数据率SDRAM,支持16位和32位数据宽度,刷新模式可由软件控制。
1.EMC功能框图和引脚信号
图4.8 EMC功能框图
表4.17 外部存储器控制器引脚描述
引脚名称 | 类 型 | 复位值 | 描 述 |
A[23:0] | 输出 | 0x0000 0000 | 外部存储器地址线。SDRAM存储器只使用其中的位[14:0] |
D[31:0] | 输入/输出 | 0x0000 0000 | 外部存储器数据线 |
OE | 输出 | 1 | 静态存储器输出使能信号,低电平有效 |
BLS[3:0] | 输出 | 0x0F | 静态存储器字节定位选择信号,低电平有效 |
WE | 输出 | 1 | 写使能信号,低电平有效 |
CS[3:0] | 输出 | 0x0F | 静态存储器片选信号,低电平有效 |
DYCS[3:0] | 输出 | 0x0F | 动态存储器片选信号,低电平有效 |
CAS | 输出 | 1 | 动态存储器列地址选通信号,低电平有效 |
RAS | 输出 | 1 | 动态存储器行地址选通信号,低电平有效 |
CLKOUT[1:0] | 输出 | 来自CCLK | SDRAM时钟 |
CKEOUT[3:0] | 输出 | 0x0F | SDRAM时钟使能 |
DQMOUT[3:0] | 输出 | 0x0F | SDRAM输出掩码。也可用于静态存储器 |
2.外部存储器接口
外部存储器接口取决于存储器组的数据宽度(32位、16位或8位,由EMCStaticConfig寄存器的MW位选择)。如果一个存储器组被配置为32位宽度,地址线A0和A1可以用做非地址线;如果存储器组配置为16为宽度,则不需要A0;8位宽的存储器组则需要使用A0。通过引脚连接模块寄存器可以实现A0和A1的地址或非地址功能。
图4.9和图4.10所示为典型的32位总线宽度的存储器组的外部存储器接口硬件连接方式,其中符号“a_b”表示数据总线的最高位地址线,符号“a_m”表示使用外部存储器接口的存储器芯片的最高位地址线。
图4.9中,一个32位宽度的存储器组由4个8位存储器芯片扩展而成,因此这4个芯片用同一个片选信号CS选中有效,同时使用同一个写使能信号OE。第一个存储器芯片构成存储器组32位中的高8位,因此其写使能信号WE连接到字节选择信号的最高位BLS[3],8位数据线连接到数据总线的最高8位D[31:24]。其余三个芯片分别构成32位存储器组中的第2、3、4个字节。由于存储器组的数据宽度为32位,地址总线中的A0和A1不使用,只使用最高位a_b到bit2连接到存储芯片的地址线上。
图4.10中的16位芯片引脚UB和LB分别表示芯片中16位数据的高字节和低字节。32位芯片中的引脚B3~B0表示芯片中32位数据的四个字节。其芯片引脚与总线的连接方式与图4.9是类似的。
按照图4.9和图4.10的方法,读者不难得出16位和8位总线宽度的存储器组的外部存储器接口扩展方法。
图4.9 4个8位静态存储器芯片构成32位宽存储器组
图4.10 2个16位存储器芯片和1个32位存储器芯片构成32位宽存储器组
图4.11所示为使用SDRAM芯片构成32位动态存储器组的外部存储器接口连接方法。图中使用的SDRAM芯片为K4S561632H,这款芯片的存储容量为256Mb,32位宽度,存储空间分为4个组,使用时由组选择信号BA0和BA1选择。在连线时,除了要注意使用与静态存储器不同的动态存储器引脚如DYCS、CLKOUT、CKEOUT和DQMOUT之外,要格外注意地址线的连法。LPC2400的SDRAM地址线只使用地址线中的[14:0]这15根线,最高位两根A14和A13固定连接到芯片的组选择信号BA1和BA0,其余地址线从A0开始一一连接。这种连接方式是LPC2400的EMC特有的,与其它的ARM芯片,如三星的S3C系列芯片有很大区别,开发者在自己扩展SDRAM存储器时一定要特别注意。
图4.11 外部扩展存储器接口SDRAM
3.EMC相关寄存器
表4.18所示为EMC的相关寄存器。
表4.18 EMC寄存器汇总
寄存器名 | 类型 | 复位值 | 功能描述 | 地址 |
EMCControl | 读/写 | 0x3 | EMC控制操作 | 0xFFE0 8000 |
EMCStatus | 只读 | 0x5 | 提供EMC控制信息 | 0xFFE0 8004 |
EMCConfig | 读/写 | 0x0 | EMC配置操作 | 0xFFE0 8008 |
EMCDynamic Control | 读/写 | 0x6 | 控制动态存储器操作 | 0xFFE0 8020 |
EMCDynamic Refresh | 读/写 | 0x0 | 配置动态存储器刷新操作 | 0xFFE0 8024 |
EMCDynamicReadConfig | 读/写 | 0x0 | 配置动态存储器读策略 | 0xFFE0 8028 |
EMCDynamicRP | 读/写 | 0x0F | 选择预充电命令周期 | 0xFFE0 8030 |
EMCDynamicRAS | 读/写 | 0x0F | 选择激活预充电命令周期 | 0xFFE0 8034 |
EMCDynamicSREX | 读/写 | 0x0F | 选择刷新退出时间 | 0xFFE0 8038 |
EMCDynamicAPR | 读/写 | 0x0F | 选择最后数据输出激活命令时间 | 0xFFE0 803C |
EMCDynamicDAL | 读/写 | 0x0F | 选择数据输入激活命令时间 | 0xFFE0 8040 |
EMCDynamicWR | 读/写 | 0x0F | 选择写修复时间 | 0xFFE0 8044 |
EMCDynamicRC | 读/写 | 0x1F | 选择激活有效命令周期 | 0xFFE0 8048 |
EMCDynamicRFC | 读/写 | 0x1F | 选择自动刷新周期 | 0xFFE0 804C |
EMCDynamicXSR | 读/写 | 0x1F | 选择退出刷新有效命令时间 | 0xFFE0 8050 |
EMCDynamicRRD | 读/写 | 0x0F | 选择激活组A到组B延迟 | 0xFFE0 8054 |
EMCDynamicMRD | 读/写 | 0x0F | 选择有效命令时间的加载模式寄存器 | 0xFFE0 8058 |
EMCDynamicConfig0 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8100 |
EMCDynamicRasCas0 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8104 |
EMCDynamicConfig1 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8120 |
EMCDynamicRasCas1 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8124 |
EMCDynamicConfig2 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8140 |
EMCDynamicRasCas2 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8144 |
EMCDynamicConfig3 | 读/写 | 0x0 | 选择动态存储器芯片0配置信息 | 0xFFE0 8160 |
EMCDynamicRasCas3 | 读/写 | 0x303 | 选择动态存储器芯片0RASCAS延迟 | 0xFFE0 8164 |
EMCStaticConfig0 | 读/写 | 0x0 | 选择静态存储器芯片0配置 | 0xFFE0 8200 |
EMCStaticWaitWen0 | 读/写 | 0x0 | 选择写使能芯片0延迟 | 0xFFE0 8204 |
EMCStaticWaitOen0 | 读/写 | 0x0 | 选择输出使能芯片0延迟 | 0xFFE0 8208 |
EMCStaticWaitRd0 | 读/写 | 0x0 | 选择读访问芯片0延迟 | 0xFFE0 820C |
EMCStaticWaitPage0 | 读/写 | 0x1F | 选择芯片0异步页模式顺序访问延迟 | 0xFFE0 8210 |
EMCStaticWaitWr0 | 读/写 | 0x1F | 选择写访问芯片0延迟 | 0xFFE0 8214 |
EMCStaticWaitTurn0 | 读/写 | 0x0F | 选择芯片0总线翻转周期 | 0xFFE0 8218 |
EMCStaticConfig1 | 读/写 | 0x0 | 选择静态存储器芯片1配置 | 0xFFE0 8220 |
EMCStaticWaitWen1 | 读/写 | 0x0 | 选择写使能芯片1延迟 | 0xFFE0 8224 |
EMCStaticWaitOen1 | 读/写 | 0x0 | 选择输出使能芯片1延迟 | 0xFFE0 8228 |
EMCStaticWaitRd1 | 读/写 | 0x0 | 选择读访问芯片1延迟 | 0xFFE0 822C |
EMCStaticWaitPage1 | 读/写 | 0x1F | 选择芯片1异步页模式顺序访问延迟 | 0xFFE0 8230 |
EMCStaticWaitWr1 | 读/写 | 0x1F | 选择写访问芯片1延迟 | 0xFFE0 8234 |
EMCStaticWaitTurn1 | 读/写 | 0x0F | 选择芯片1总线翻转周期 | 0xFFE0 8238 |
EMCStaticConfig2 | 读/写 | 0x0 | 选择静态存储器芯片2配置 | 0xFFE0 8240 |
EMCStaticWaitWen2 | 读/写 | 0x0 | 选择写使能芯片2延迟 | 0xFFE0 8244 |
EMCStaticWaitOen2 | 读/写 | 0x0 | 选择输出使能芯片2延迟 | 0xFFE0 8248 |
EMCStaticWaitRd2 | 读/写 | 0x0 | 选择读访问芯片2延迟 | 0xFFE0 824C |
EMCStaticWaitPage2 | 读/写 | 0x1F | 选择芯片2异步页模式顺序访问延迟 | 0xFFE0 8250 |
EMCStaticWaitWr2 | 读/写 | 0x1F | 选择写访问芯片2延迟 | 0xFFE0 8254 |
EMCStaticWaitTurn2 | 读/写 | 0x0F | 选择芯片2总线翻转周期 | 0xFFE0 8258 |
EMCStaticConfig3 | 读/写 | 0x0 | 选择静态存储器芯片3配置 | 0xFFE0 8260 |
EMCStaticWaitWen3 | 读/写 | 0x0 | 选择写使能芯片3延迟 | 0xFFE0 8264 |
EMCStaticWaitOen3 | 读/写 | 0x0 | 选择输出使能芯片3延迟 | 0xFFE0 8268 |
EMCStaticWaitRd3 | 读/写 | 0x0 | 选择读访问芯片3延迟 | 0xFFE0 826C |
EMCStaticWaitPage3 | 读/写 | 0x1F | 选择芯片3异步页模式顺序访问延迟 | 0xFFE0 8270 |
EMCStaticWaitWr3 | 读/写 | 0x1F | 选择写访问芯片3延迟 | 0xFFE0 8274 |
EMCStaticWaitTurn3 | 读/写 | 0x0F | 选择芯片3总线翻转周期 | 0xFFE0 8278 |
EMCStaticExtendedWait | 读/写 | 0x0 | 静态存储器读写传输时间 | 0xFFE0 8880 |
各寄存器的详细配置方法,由于篇幅所限,不再赘述。在对系统进行外扩存储器开发时,需要仔细阅读外扩存储器芯片的相关文档,获得其准确的配置参数,再在初始化函数中对相关寄存器进行配置操作,才能正常使用外扩存储器。
4.4 系统控制模块
系统控制模块包括几个系统特性和控制寄存器,这些寄存器具有许多与特定外设器件无关的功能,每种类型的功能都有其自身的寄存器。
4.4.1 系统控制和状态寄存器
表4.19所示位系统控制和状态寄存器(System Controls and Status register)。
表4.19 系统控制和状态寄存器(SCS – 0xE01F C1A0)
位 | 标识 | 值 | 功能描述 | 类型 | 复位值 |
0 | GPIOM |
| GPIO访问模式选择 | 读/写 | 0 |
0 | GPIO组0和组1配置为与以前的LPC2000系列兼容的端口 | ||||
1 | GPIO组0和组1配置为高速端口,使用片内存储器 | ||||
位 | 标识 | 值 | 功能描述 | 类型 | 复位值 |
1 | EMC Reset Disable |
| 外部存储器控制器复位无效 | 读/写 | 0 |
0 | 复位时,EMC的所有寄存器和功能重新初始化 | ||||
1 | 只有上电和掉电时EMC重新初始化 | ||||
2 |
|
| 保留 |
|
|
3 | MCIPWR Active Level |
| MCIPWR有效电平 | 读/写 | 0 |
0 | MCIPWR引脚低电平 | ||||
1 | MCIPWR引脚高电平 | ||||
4 | OSCRANGE |
| 主晶振范围选择 | 读/写 | 0 |
0 | 主晶振频率范围从1MHz到20MHz | ||||
1 | 主晶振频率范围从15MHz到24MHz | ||||
5 | OSCEN |
| 主晶振使能 | 读/写 | 0 |
0 | 主晶振无效 | ||||
1 | 主晶振有效 | ||||
6 | OSCSTAT |
| 主晶振状态 | 只读 | 0 |
0 | 主晶振未准备好 | ||||
1 | 主晶振已准备好。可以通过OSCEN位设置作为一个时钟源使用 | ||||
31:7 |
|
| 保留 |
| NA |
SCS寄存器主要用于设置晶振使用方法和GPIO属性,在后续相关章节里会几次使用到该寄存器。
4.4.2 外部中断
1. 逻辑结构
LPC2400含有4个外部中断输入(作为可选的管脚功能),四个引脚分别为EINT0、EINT1、EINT2和EINT3。外部中断输入可用于将处理器从掉电模式唤醒。
可将多个管脚同时连接同一路外部中断,此时,外部中断逻辑根据方式位和极性位的不同,分别进行如下处理:
1) 低有效电平激活方式,选用EINT功能的全部管脚的状态都连接到一个正逻辑与门。
2) 高有效电平激活方式,选用EINT功能的全部管脚的状态都连接到一个正逻辑或门。
3) 边沿激活方式,使用GPIO 端口号最低的管脚,与管脚的极性无关。(边沿激活方式中选择使用多个EINT管脚被看作编程出错。)
外部中断逻辑逻辑原理图见图4.12。
图4.12 外部中断逻辑原理图
当多个EINT 管脚逻辑或时,可在中断服务程序中通过IO0PIN和IO1PIN寄存器从GPIO端口读出管脚状态来判断产生中断的管脚。
2. 寄存器描述
外部中断具有4 个相关的寄存器,如表4.20所示。EXTINT寄存器包含中断标志,INTWAKE寄存器包含使能唤醒位,可使能独立的外部中断输入将处理器从掉电模式唤醒,EXTMODE和EXTPOLAR寄存器用来指定管脚使用电平或边沿激活方式。
表4.20 外部中断寄存器
地址 | 寄存器名 | 功能描述 | 类型 |
0xE01FC140 | EXTINT | 外部中断标志寄存器,包含ENIT0、EINT1 、EINT2和EINT3的中断标志 | 读/写 |
0xE01FC144 | INTWAKE | 中断唤醒寄存器,指示哪些中断可以唤醒掉电的CPU,以及控制是否使能唤醒 | 读/写 |
0xE01FC148 | EXTMODE | 外部中断方式寄存器,控制每个管脚的边沿或电平激活 | 读/写 |
0xE01FC14C | EXTPOLAR | 外部中断极性寄存器,控制由每个管脚的哪种电平或边沿来产生中断 | 读/写 |
1) 外部中断标志寄存器-External Interrupt Flag Register(EXTINT - 0xE01FC140)
当一个管脚选择使用外部中断功能时,对应在EXTPOLAR 和EXTMODE 寄存器中的位选择的电平或边沿将置位EXTINT寄存器中的中断标志,向VIC 提出中断请求,如果管脚中断使能,将会产生中断。
向EXTINT 寄存器的位EINT0~位EINT3写入1可清除相应的外部中断标志。在电平激活方式下,只有在该管脚处于无效状态时才能清除相应的中断标志。
一旦EINT0~EINT3中的一位被置位并开始执行相应的代码(处理唤醒和/或外部中断),必须将该位清零,否则以后该EINT 管脚所触发的事件将不能再被识别。
例如,如果外部中断0 管脚的低电平将系统从掉电模式唤醒,为了将来还能进入掉电模式,唤醒后的程序必须将EINT0位复位。如果EINT0位仍保持置位状态,后来的唤醒掉电模式的任何操作都将失败,外部中断也不例外。
表4.21 外部中断标志寄存器
EXTINT | 功能 | 功能描述 | 复位值 |
0 | EINT0 | 电平激活方式下,如果管脚的EINT0 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT0 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
1 | EINT1 | 电平激活方式下,如果管脚的EINT1 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT1 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
2 | EINT2 | 电平激活方式下,如果管脚的EINT2 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT2 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
3 | EINT3 | 电平激活方式下,如果管脚的EINT3 功能被选用且管脚处于有效状态时,该位置位;边沿激活方式下,如果管脚的EINT3 功能被选用且管脚上出现所选极性,该位置位。 该位通过写入1 清除,但电平激活方式下管脚处于有效状态的情况除外。 | 0 |
7:4 |
| 保留 | NA |
2) 中断唤醒寄存器-Interrupt Wakeup Register(INTWAKE - 0xE01FC144)
INTWAKE寄存器(有时亦称为EXTWAKE,外部中断唤醒寄存器)中的使能位允许外部中断、以太网、USB、CAN、GPIO、BOD或者RTC中断将处理器从掉电模式唤醒。相关的EINTn功能必须映射到管脚才能实现掉电唤醒,但中断并不必要为了实现唤醒操作而在向量中断控制器中被使能。这样做的好处是允许外部中断输入将处理器从掉电模式唤醒,但不产生中断(只是简单地恢复操作),或者在掉电模式下使能中断而不将处理器唤醒(这样,当应用中并不需要唤醒特性时,也不必关闭中断)。
表4.22 外部中断唤醒寄存器
INTWAKE | 功能 | 功能描述 | 复位值 |
0 | EXTWAKE0 | 该位为1 时,使能EINT0将处理器从掉电模式唤醒 | 0 |
1 | EXTWAKE1 | 该位为1 时,使能EINT1将处理器从掉电模式唤醒 | 0 |
2 | EXTWAKE2 | 该位为1 时,使能EINT2将处理器从掉电模式唤醒 | 0 |
3 | EXTWAKE3 | 该位为3 时,使能EINT3将处理器从掉电模式唤醒 | 0 |
4 | ETHWAKE | 该位为1 时,使能以太网中断将处理器从掉电模式唤醒 | 0 |
5 | USBWAKE | 该位为1 时,使能USB中断将处理器从掉电模式唤醒 | 0 |
6 | CANWAKE | 该位为1 时,使能CAN总线中断将处理器从掉电模式唤醒 | 0 |
7 | GPIOWAKE | 该位为1 时,使能特殊GPIO引脚中断将处理器从掉电模式唤醒 | 0 |
13:8 |
| 保留 | NA |
14 | BODWAKE | 该位为1 时,使能BOD中断将处理器从掉电模式唤醒 | 0 |
15 | RTCWAKE | 该位为1 时,使能实时时钟RTC中断将处理器从掉电模式唤醒 | 0 |
要使器件进入掉电模式并允许总线或管脚上的一个或多个事件能使其恢复正常操作,软件应该对管脚的外部中断功能重新编程,选择中断合适的方式和极性以及掉电模式。唤醒时软件应恢复管脚复用的外围功能。
上述的所有总线或管脚都是低电平有效。如果软件要使器件退出掉电模式来响应多个管脚共用的同一个EINTi 通道的事件,中断通道必须编程设定为低电平激活方式,因为只有在电平方式中通道才能使信号逻辑或来唤醒器件。
3)外部中断方式寄存器-External Interrupt Mode Register(EXTMODE – 0xE01F C148)
EXTMODE寄存器中的位用来选择每个EINT脚是电平触发还是边沿触发。只有选择用作EINT功能(通过管脚连接模块)并已通过VICIntEnable(向量中断使能寄存器)使能的管脚才能产生外部中断(当然,如果管脚选择用作其它功能,则可能产生其它功能的中断)。
当某个中断在VICIntEnable 中被禁止时,软件应该只改变EXTMODE 寄存器中相应位的值。中断重新使能前,软件向EXTINT 写入1 来清除EXTINT 位,EXTINT 位可通过改变激活方式来置位。
表4.23 外部中断方式寄存器
EXTMODE | 功能 | 值 | 描述 | 复位值 |
0 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
1 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
2 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
3 | EXTMODE0 | 0 | EINT0 使用电平激活 | 0 |
1 | EINT0 使用边沿激活 | |||
7:4 |
|
| 保留 | NA |
4) 外部中断极性寄存器-External Interrupt Polarity Register(EXTPOLAR – 0xE01F C14C)
在电平激活方式中,EXTPOLAR寄存器用来选择相应管脚是高电平还是低电平有效;
在边沿激活方式中,EXTPOLAR寄存器用来选择管脚上升沿还是下降沿有效。只有选择用作EINT功能(通过管脚连接模块)并已通过VICIntEnable(向量中断使能寄存器)使能的管脚才能产生外部中断(当然,如果管脚选择用作其它功能,则可能产生其它功能的中断)。
当某个中断在VICIntEnable 中被禁止时,软件应该只改变EXTPOLAR 寄存器中相应位的值。中断重新使能前,软件向EXTINT 写入1 来清除EXTINT 位,EXTINT 位可通过改变中断极性来置位。
表4.24 外部中断极性寄存器
EXTPOLAR | 功能 | 值 | 描述 | 复位值 |
0 | EXTPOLAR0 | 0 | EINT0低电平或下降沿有效(由EXTMODE0决定) | 0 |
1 | EINT0 高电平或上升沿有效(由EXTMODE0决定) | |||
1 | EXTPOLAR1 | 0 | EINT1低电平或下降沿有效(由EXTMODE1决定) | 0 |
1 | EINT1高电平或上升沿有效(由EXTMODE1决定) | |||
2 | EXTPOLAR2 | 0 | EINT2低电平或下降沿有效(由EXTMODE2决定) | 0 |
1 | EINT2高电平或上升沿有效(由EXTMODE2决定) | |||
3 | EXTPOLAR3 | 0 | EINT3低电平或下降沿有效(由EXTMODE3决定) | 0 |
1 | EINT3高电平或上升沿有效(由EXTMODE3决定) | |||
7:4 |
|
| 保留 | NA |
有关中断向量、中断设置与中断服务子程序的内容详见4.6节。
4.5 时钟和功率控制
4.5.1 晶体振荡器
LPC2400含有3个独立的晶体振荡器:主晶振、内部RC晶振和RTC晶振。每个晶振针对不同应用需求有多种使用方法。复位后,LPC2400系列处理器使用内部RC晶振提供时钟进行操作,直到使用软件进行切换为止。这使得系统可以不依赖于外部时钟进行操作,而且使引导加载程序可以在一个确定的频率下进行操作。当Boot ROM转向用户程序之前,可以激活主晶振从而进入用户代码。
1.内部晶体振荡器(IRC,Internal RC Oscillator)
IRC可以用做看门狗定时器的时钟源,也可以作为时钟,驱动PLL锁相环提供给CPU。IRC的精度不够,因此不能用于USB接口。通常的IRC频率是4MHz。在开机或芯片复位时,LPC2400使用IRC作为时钟源,之后可以使用软件转为使用其它时钟源。
2.主晶振(Main Oscillator)
主晶振可用于为CPU提供时钟,其频率范围为1MHz~24MHz。这个频率可以通过PLL倍频为更高的频率成为CPU的主频。通常把主晶振输出的时钟称为OSCCLK,PLL输入引脚上的时钟称为PLLCLKIN,ARM处理器内核时钟频率称为CCLK。当使用主晶振提供时钟而不激活PLL时,这三个值是相等的。
由于芯片复位时使用IRC晶振,主晶振由软件启动(使用SCS寄存器中的OSCEN位),并且在某些应用中始终不会用到。通过SCS寄存器中的OSCSTAT状态位可以使软件判断主晶振是否运行和稳定,也可以通过SCS寄存器中的OSCRANGE位设置其频率范围。
LPC2400的振荡器可工作在两种模式下:从属模式和振荡模式。从属模式下,输入时钟信号XTAL1与一个100pF相连,其幅值不少于200mV,XTAL2管脚不连接。振荡模式下,由于片内集成了反馈电阻,只需在外部连接一个晶体和电容Cx1、Cx2 就可形成基本模式的振荡。
两种振荡器模式的示意见图4.13。
图4.13 振荡器模式
3.RTC晶振(RTC Oscillator)
RTC晶振的频率为32.768KHz,一般用于给RTC实时时钟提供时钟源。RTC晶振也可以用于看门狗定时器,通过驱动PLL也可以用于提供CPU主频。
4.时钟源选择
几个时钟源都可以用来驱动PLL从而给CPU和片内外设提供时钟。当PLL未连接时,系统可以通过CLKSRCSEL寄存器安全的改变时钟源。
表4.25 时钟源选择寄存器(CLKSRCSEL – 0xE01F C10C)
CLKSRCSEL | 功能 | 值 | 描述 | 复位值 |
1:0 | CLKSRC | 00 | 选择IRC晶振为PLL时钟源 | 00 |
01 | 选择主晶振为PLL时钟源 | |||
10 | 选择RTC晶振为PLL时钟源 | |||
11 | 保留 | |||
7:4 |
| 0 | 未使用,始终为0 | 0 |
4.5.2 PLL锁相环
PLL接受输入的时钟频率范围为32kHz~50MHz。输入频率通过一个预分频器分频成为PLL内部频率,预分频器的值用变量“N”表示。然后再通过一个电流控制振荡器(CCO)倍增到范围275MHz~550MHz,倍频器的值用变量“M”表示。CCO频率再通过CPU频率设置寄存器分频成为提供给CPU的CCLK时钟。
PLL 的激活由PLLCON寄存器控制,PLL倍频器和分频器的值由PLLCFG寄存器控制。为了防止PLL参数发生意外改变或PLL失效,对这两个寄存器进行了保护。当PLL提供芯片时钟时,由于芯片的所有操作,包括看门狗定时器在内都依赖于它,因此PLL设置的意外改变将导致CPU 执行不期望的动作。
1.PLL控制寄存器-PLL Control Register(PLLCON – 0xE01FC080)
PLLCON寄存器可用于使能和连接PLL,它是最新的PLL控制位的保持寄存器,写入该寄存器的值在有效的PLL 馈送序列执行之前不起作用。使能PLL将使PLL锁定到当前倍频器和分频器值的设定频率上。连接PLL将使处理器和所有片内功能都根据PLL输出时钟来运行。对PLLCON的更改只有在对PLLFEED寄存器执行了正确的PLL馈送序列后才生效。
表4.26 PLL 控制寄存器
PLLCON | 功能 | 描述 | 复位值 |
0 | PLLE | PLL使能。当该位为1 并且在有效的PLL馈送之后,该位将激活PLL并允许其锁定到指定的频率。 | 0 |
1 | PLLC | PLL连接。当PLLC和PLLE都为1 并且在有效的PLL馈送后,将PLL作为时钟源连接到LPC2400。否则,LPC2400直接使用振荡器时钟。 | 0 |
7:2 | 保留 | 保留 | NA |
PLL在作为时钟源之前必须进行设置、使能并锁定。将振荡器时钟切换到PLL输出或反过来操作时,内部电路对操作进行同步以确保不会产生干扰。硬件不能确保PLL在连接之前锁定或在PLL在失去锁定时自动断开连接。在PLL失去锁定的情况下,振荡器很可能已经变得不稳定,这样即使断开PLL也挽救不了这种状况。
2.PLL配置寄存器-PLL Configuration Register(PLLCFG – 0xE01FC084)
PLLCFG寄存器是最新的PLL配置值的保持寄存器,包含PLL倍频器和分频器值。在执行正确的PLL馈送序列之前改变PLLCFG寄存器的值不会生效。
表4.27 PLL配置寄存器
PLLCFG | 功能 | 描述 | 复位值 |
14:0 | MSEL | PLL倍频器值,在PLL频率计算中其值为M-1 | 0 |
15 | 保留 | 保留 | NA |
23:16 | NSEL | PLL预分配器值,在PLL频率计算中其值为N | NA |
31:24 | 保留 | 保留 | NA |
3.PLL状态寄存器-PLL Status Register(PLLSTAT – 0xE01FC088)
PLLSTAT为只读寄存器,它是PLL控制和配置信息的读回寄存器,反映了正在使用的真实PLL参数和状态。PLLSTAT可能与PLLCON和PLLCFG中的值不同,这是因为没有执行正确的PLL馈送序列,这两个寄存器中的值并未生效。
表4.28 PLL状态寄存器
PLLSTAT | 功能 | 描述 | 复位值 |
14:0 | MSEL | 读出的PLL倍频器值,这是PLL当前使用的值 | 0 |
15 | 保留 | 保留 | NA |
23:16 | NSEL | 读出的PLL预分频器值,这是PLL当前使用的值 | NA |
24 | PLLE | 读出的PLL使能位,该位为1时,PLL处于激活状态;为0时,PLL关闭;进入掉电模式时,该位自动清零 | 0 |
25 | PLLC | 读出的PLL连接位,当PLLC和PLLE都为1 时,PLL作为时钟源连接到LPC2400;当PLLC或PLLE位为0 时,PLL被旁路,LPC2400直接使用振荡器时钟;进入掉电模式时,该位自动清零 | 0 |
26 | PLOCK | 反映PLL的锁定状态,为0时,PLL未锁定;为1时,PLL锁定到指定的频率 |
|
31:27 | 保留 | 保留 | NA |
PLLSTAT 寄存器中的PLOCK位连接到中断控制器。这样可使用软件打开PLL并连接到其它功能,不需要等待PLL锁定。当发生中断时(PLOCK=1),可以连接PLL并禁止中断。只能通过禁止PLL中断的方式返回。
PLL有3种可能的工作模式,由PLLE和PLLC组合得到。
表4.29 PLL的工作模式
PLLC | PLLE | PLL 功能 |
0 | 0 | PLL 被关闭并断开连接,系统使用未更改的时钟输入 |
0 | 1 | PLL 被激活但是尚未连接,PLL 可在PLOCK 置位后连接 |
1 | 0 | 与00 组合相同,这样消除了PLL 已连接但没有使能的可能性 |
1 | 1 | PLL 已使能并连接到处理器作为系统时钟源 |
4.PLL馈送寄存器-PLL Feed Register(PLLFEED – 0xE01FC08C)
必须将正确的馈送序列写入PLLFEED寄存器才能使PLLCON和PLLCFG寄存器的更改生效。馈送序列如下:
1) 将值0xAA 写入PLLFEED;
2) 将值0x55 写入PLLFEED。
这两个写操作的顺序必须正确,而且必须是连续的APB总线周期,这意味着在执行PLL馈送操作时必须禁止中断。不管是写入的值不正确还是没有满足前两个条件,对PLLCON或PLLCFG寄存器的更改都不会生效。
表4.30 PLL 馈送寄存器
PLLFEED | 功能 | 描述 | 复位值 |
7:0 | PLLFEED | PLL馈送序列必须写入该寄存器才能使PLL配置和控制寄存器的更改生效 | 0x00 |
5.PLL和掉电模式
掉电模式会自动关闭并断开PLL。从掉电模式唤醒不会自动恢复PLL的设定,PLL的恢复必须由软件来完成。通常,一个将PLL激活并等待锁定,然后将PLL连接的子程序可以在任何中断服务程序的开始调用。有一点非常重要,那就是不要试图在掉电唤醒之后简单地执行馈送序列来重新启动PLL,这会出现在PLL锁定建立之前同时使能并连接PLL的危险。
6.PLL频率计算举例:
当一个LPC2400 ARM系统需要使用PLL,应当按照以下原则进行:
1) 确定处理器的时钟频率CCLK。这可以根据系统对处理器的整体要求来决定,外围器件的时钟频率可以低于处理器频率。
2) 确定PLL内部的时钟频率FCCO。FCCO的值应当在275MHz~550MHz之间,而且应当是CCLK的整数倍。
3) 确定晶体振荡器频率FIN。FIN的值应当在32kHz~50MHz之间。
PLL的输出频率公式为:FCCO = (2×M×FIN) / N
选择两个整数M和N便可得到合适的FCCO值。M的取值范围为6~512,N的取值范围为1~32。
例如:系统要求使用USB接口,CPU主频约为60MHz。外部晶振频率为4MHz。
由要求可知:USB总线要求精确的48MHz时钟,因此可以选择FCCO为480MHz。当N值为1时,M = 480 / (2×4) = 60。因此PLLCFG值为0x3B(N-1 = 0,M-1 = 0x3B)。
4.5.3 时钟分频
PLL的输出必须向下分频为更低频率的信号才能用于CPU和USB模块。提供给USB模块的分频器是独立的,因为USB的时钟要求必须是准确的48MHz而且有50%的占空比。分频给CPU的信号成为CCLK时钟,并且再分频成为各个片内外设的驱动时钟。
1.CPU时钟配置寄存器-CPU Clock Configuration Register(CCLKCFG – 0xE01F C104)
CCLKCFG寄存器控制PLL的分频输出提供给CPU。如果不使用PLL,分频值为1。
表4.31 CPU时钟配置寄存器
CCLKCFG | 功能 | 描述 | 复位值 |
7:0 | CCLKSEL | 分频器值,用于生成CPU时钟(CCLK)。本值只能为0或奇数值(1,3,5,…,255) | 0x00 |
CCLK值为PLL的输出频率除以CCLKSEL+1。当CCLKSEL值为1时,CCLK值为PLL输出频率的一半。
2.USB时钟配置寄存器-USB Clock Configuration Register(USBCLKCFG – 0xE01F C108)
USBCLKCFG寄存器控制PLL的分频输出提供给USB模块。如果不使用PLL,分频值为1。输出的频率应该为48MHz并且有50%的占空比。
表4.32 USB时钟配置寄存器
USBCLKCFG | 功能 | 描述 | 复位值 |
3:0 | USBSEL | 分频器值,用于生成USB时钟(CCLK) | 0x00 |
7:4 | - | 保留 | NA |
USB模块的时钟值为PLL的输出频率除以USBSEL+1。当USBSEL值为1时,USB时钟值为PLL输出频率的一半。
3.IRC整理寄存器-IRC Trim Register(IRCTRIM – 0xE01F C1A4)
这个寄存器用于整理片内4MHz的晶振。
表4.33 IRC整理寄存器
IRCtrim | 功能 | 描述 | 复位值 |
7:0 | IRCtrim | IRC整理值,用于控制片内4MHzIRC晶振频率 | 0xA0 |
15:8 | - | 保留 | NA |
4.外设时钟选择寄存器0和1-Peripheral Clock Selection registers 0 and 1(PCLKSEL0 – 0xE01F C1A8 and PCLKSEL1 – 0xE01F C1AC)
这一对寄存器中的每两位控制一个外设的时钟,其取值意义参见表4.36。
表4.34 外设时钟选择寄存器0
PCLKSEL0 | 功能 | 描述 | 复位值 |
1:0 | PCLK_WDT | 看门狗外设时钟选择 | 00 |
3:2 | PCLK_TIMER0 | 定时器0外设时钟选择 | 00 |
5:4 | PCLK_TIMER1 | 定时器1外设时钟选择 | 00 |
7:6 | PCLK_UART0 | 串口0外设时钟选择 | 00 |
9:8 | PCLK_UART1 | 串口1外设时钟选择 | 00 |
11:10 | PCLK_PWM0 | 脉宽调制器0外设时钟选择 | 00 |
13:12 | PCLK_PWM1 | 脉宽调制器1外设时钟选择 | 00 |
15:14 | PCLK_I2C0 | I2C0外设时钟选择 | 00 |
17:16 | PCLK_SPI | SPI外设时钟选择 | 00 |
19:18 | PCLK_RTC | 实时时钟外设时钟选择 | 00 |
21:20 | PCLK_SSP1 | SSP1外设时钟选择 | 00 |
23:22 | PCLK_DAC | DAC外设时钟选择 | 00 |
25:24 | PCLK_ADC | ADC外设时钟选择 | 00 |
27:26 | PCLK_CAN1 | CAN1外设时钟选择 | 00 |
29:28 | PCLK_CAN2 | CAN2外设时钟选择 | 00 |
31:30 | PCLK_ACF | CAN滤波器外设时钟选择 | 00 |
注:PCLK_RTC字段中,值“01”是无效的,试图写入“01”不会改变预设值。
表4.35 外设时钟选择寄存器1
PCLKSEL1 | 功能 | 描述 | 复位值 |
1:0 | PCLK_BAT_RAM | 电池支持RAM外设时钟选择 | 00 |
3:2 | PCLK_GPIO | GPIO外设时钟选择 | 00 |
5:4 | PCLK_PCB | 引脚连接模块外设时钟选择 | 00 |
7:6 | PCLK_I2C1 | I2C1外设时钟选择 | 00 |
9:8 | - | 保留,始终为0 | 00 |
11:10 | PCLK_SSP0 | SSP0外设时钟选择 | 00 |
13:12 | PCLK_TIMER2 | 定时器2外设时钟选择 | 00 |
15:14 | PCLK_TIMER3 | 定时器3外设时钟选择 | 00 |
17:16 | PCLK_UART2 | 串口2外设时钟选择 | 00 |
19:18 | PCLK_UART3 | 串口3外设时钟选择 | 00 |
21:20 | PCLK_I2C2 | I2C2外设时钟选择 | 00 |
23:22 | PCLK_I2S | I2S总线外设时钟选择 | 00 |
25:24 | PCLK_MCI | MCI外设时钟选择 | 00 |
27:26 | - | 保留,始终为0 | 00 |
PCLKSEL1 | 功能 | 描述 | 复位值 |
29:28 | PCLK_SYSCON | 系统控制模块外设时钟选择 | 00 |
31:30 | - | 保留,始终为0 | 00 |
表4.36 外设时钟选择寄存器位值
位 | 功能描述 | 复位值 |
00 | PCLK_xxx = CCLK / 4 | 00 |
01 | PCLK_xxx = CCLK | 00 |
10 | PCLK_xxx = CCLK / 2 | 00 |
11 | 在CAN1、CAN2、CAN滤波器部件中PCLK_xxx = HCLK / 6,其余部件中PCLK_xxx = HCLK / 8 | 00 |
4.5.4 功率控制
1. 节电模式
嵌入式系统一般使用电池供电,因此系统的耗电和待机时间是个重要指标。节电的方法主要是降低系统时钟频率:改变时钟源、重配置PLL值或者改变CPU时钟分频值。另外,也可以通过停止片内外设时钟的方法来关闭不使用的片内外设,进一步减少功耗。
LPC2400支持三种节电模式:空闲模式、睡眠模式和掉电模式。LPC2400有一个独立的功率控制单元用来给RTC和一个小的静态RAM供电,这个特性使得LPC2400可以将片内的其它大部分设备全部关闭。
1)空闲模式(Idel mode):指令的执行被挂起直到发生复位或中断为止。外设功能在空闲模式下继续保持并可产生中断使处理器恢复运行。空闲模式使处理器、存储器系统和相关控制器以及内部总线不再消耗功率。任何中断都可以将CPU从空闲模式下唤醒。
2)睡眠模式(Sleep mode):主晶振关闭,所有时钟停止。IRC的输出无效,但IRC并未关闭并且可以快速唤醒。32kHz的RTC晶振也未停止,因为RTC中断可以用来做唤醒的中断源。睡眠模式保持处理器状态和寄存器、外设寄存器和内部SRAM的值。芯片管脚的逻辑电平保持静态。复位或特定的不需要时钟仍能工作的中断可中止睡眠模式并使芯片恢复正常运行。由于睡眠模式使芯片所有的动态操作都挂起,因此芯片的功耗降低到几乎为零。芯片复位和特定的中断可以将CPU从睡眠模式下唤醒。
3)掉电模式(Power-down mode):掉电模式与睡眠模式类似,但不同的是掉电模式会将Flash存储器也关闭。在掉电模式下,主晶振和IRC以及所有内部时钟都停止,只有32kHz的RTC晶振继续工作。
2. 寄存器描述
外设的功率控制特性允许独立关闭应用中不需要的外设,这样可以进一步降低功耗。功率控制功能包含两个寄存器,分别是PCON和PCONP。
1) 功率控制寄存器-Power Control Register(PCON – 0xE01F C0C0)
表4.37 功率控制寄存器
PCON | 功能 | 描述 | 复位值 |
0 | PM0(IDL) | 功耗模式控制位0 | 0 |
1 | PM1(PD) | 功耗模式控制位1 | 0 |
2 | BODPDM | 低电压掉电模式。当该位为0时,进入掉电模式时仍然保持低电压检测功能;当该位为1时,低电压检测功能也被关闭,这样可以进一步减少功耗,但存在着电压过低而无法从掉电模式中唤醒的可能 | 0 |
3 | BOGD | 低电压全局无效。当该位为1时,低电压检测功能无效;该位为0时,低电压检测功能使能 | 0 |
4 | BORD | 低电压复位无效。当该位为1时,低电压检测(2.6V)不会导致芯片复位;当该位为0时,低电压检测(2.9V)使芯片复位 | 0 |
6:3 | - | 保留 | NA |
7 | PM2 | 功耗模式控制位2 | 0 |
利用PCON寄存器设置节电模式的方法详见表4.38。PCON寄存器中的三个比特PM2、PM1和PM0联合控制进入LPC2400节电模式的方式。
表4.38 节电模式控制位
PM2,PM1,PM0 | 功能描述 |
000 | 正常模式 |
001 | 空闲模式 |
101 | 睡眠模式 |
010 | 掉电模式 |
其它 | 保留 |
2)外设功率控制寄存器-Power Control for Peripherals Register(PCONP – 0xE01F C0C4)
PCONP寄存器允许将所选的外设功能关闭以实现节电的目的,通过关断特定外围模块的时钟源来实现。有少数外设功能不能被关闭(例如看门狗定时器、GPIO、引脚连接模块和系统控制模块)。
某些外设,特别是包含模拟功能的外设,它们的操作无需时钟,但会消耗功率。这些外设包含独立的禁能控制位,可以通过它们来关闭电路以降低功耗。
PCONP中的每个位都控制一个外设,每个位所对应的外设编号见LPC2400存储器寻址部分的APB外设映射一节。当位值为1时该外设启用,当位值为0时该外设时钟关闭。外设在外设功率控制寄存器的对应位见表4.11。
表4.39外设功率控制寄存器
PCONP | 功能 | 描述 | 复位值 |
0 | - | 未使用,始终为0 | 0 |
1 | PCTIM0 | 定时器0功率时钟控制位 | 1 |
2 | PCTIM1 | 定时器1功率时钟控制位 | 1 |
3 | PCUART0 | 串口0功率时钟控制位 | 1 |
4 | PCUART1 | 串口1功率时钟控制位 | 1 |
5 | PCPWM0 | 脉宽调制器0功率时钟控制位 | 1 |
6 | PCPWM1 | 脉宽调制器1功率时钟控制位 | 1 |
7 | PCI2C0 | I2C0功率时钟控制位 | 1 |
8 | PCSPI | SPI功率时钟控制位 | 1 |
9 | PCRTC | 实时时钟功率时钟控制位 | 1 |
10 | PCSSP1 | SSP1接口功率时钟控制位 | 1 |
11 | PCEMC | 外扩存储器控制器 | 1 |
12 | PCAD | A/D转换器功率时钟控制位。清零该位前先清零AD0CR 寄存器的PDN位,该位应当在置位PDN前被置位 | 0 |
13 | PCCAN1 | CAN1功率时钟控制位 | 0 |
14 | PCCAN2 | CAN2功率时钟控制位 | 0 |
18:15 | - | 保留 | NA |
19 | PCI2C1 | I2C1功率时钟控制位 | 1 |
20 | PCLCD | 液晶控制器功率时钟控制位 | 0 |
21 | PCSSP0 | SSP0功率时钟控制位 | 1 |
22 | PCTIM2 | 定时器2功率时钟控制位 | 0 |
23 | PCTIM3 | 定时器3功率时钟控制位 | 0 |
24 | PCUART2 | 串口2功率时钟控制位 | 0 |
25 | PCUART3 | 串口3功率时钟控制位 | 0 |
26 | PCI2C2 | I2C2功率时钟控制位 | 1 |
27 | PCI2S | I2S接口功率时钟控制位 | 0 |
28 | PCSDC | SD卡接口功率时钟控制位 | 0 |
29 | PCGPDMA | 通用DMA功能功率时钟控制位 | 0 |
30 | PCENET | 以太网模块功率时钟控制位 | 0 |
31 | PCUSB | USB接口功率时钟控制位 | 0 |
复位以后,PCONP寄存器按照默认值使能选中的接口和外设控制器。用户程序应当在启动代码中对PCONP寄存器编程用来启动所需要的外设功能,并关闭不需要的接口和外设,以达到降低功耗的要求。系统启动以后,除了对外设功能相关的寄存器进行配置外,用户应用程序应当不要再访问PCONP 寄存器从而启动使用片内的任何外围功能。
4.5.5 时钟和功率控制举例
1. 系统时钟设置
在LPC2400的启动代码中,系统时钟的设置是通过一个ConfigurePLL ( )函数来实现的。该函数首先关闭PLL,然后通过CLKSRCSEL寄存器选择主晶振为时钟源,再通过PLLCFG寄存器利用M值和N值设置CCO频率,用CCLKCFG寄存器分频为CPU时钟CCLK,最后使能PLL使设置生效。注意对PLLCON寄存器的每次操作都要用正确的馈送序列来实现。函数中的有关参数是在target.h头文件中定义的,其相关程序行如下:
代码清单4.2
……
#define Fosc 12000000
#define Fcclk 57600000
#define Fcco 288000000
#define Fpclk (Fcclk / 4)
#define PLL_MValue 11
#define PLL_NValue 0
#define CCLKDivValue 4
#define USBCLKDivValue 5
……
主晶振采用12MHz晶体振荡器,其宏定义Fosc值要跟实际的物理参数相同。由于M值为12,N值为1(注意实际参数要在设置值上加1),CCO频率Fcco = 2×M×Fosc / N = 288MHz。CPU时钟CCLK = Fcco / (CCLKDivValue+1) = 57.6MHz。而USB时钟USBCLK = Fcco / (USBCLKDivValue+1) = 48MHz,正好满足使用要求。
代码清单4.3
void ConfigurePLL ( void )
{
if ( PLLSTAT & (1 << 25) ) // PLL是否连接
{ PLLCON = 1; // 使能PLL并断开连接
PLLFEED = 0xaa; // PLL馈送
PLLFEED = 0x55; }
PLLCON = 0; // 关闭PLL并断开连接
PLLFEED = 0xaa;
PLLFEED = 0x55;
SCS |= 0x20; // 使能主晶振
while( !(SCS & 0x40) ); // 读主晶振状态直到主晶振可用
CLKSRCSEL = 0x1; // 选择12MHz主晶振作为PLL时钟源
PLLCFG = PLL_MValue | (PLL_NValue << 16); // 执行配置
PLLFEED = 0xaa;
PLLFEED = 0x55;
PLLCON = 1; // 使能PLL
PLLFEED = 0xaa;
PLLFEED = 0x55;
CCLKCFG = CCLKDivValue; // 设置时钟分频,设定CPU频率CCLK
#if USE_USB
USBCLKCFG = USBCLKDivValue; // usbclk = 288 MHz/6 = 48 MHz
#endif
while ( ((PLLSTAT & (1 << 26)) == 0) ); // 检查锁定位状态
PLLCON = 3; // 使能并连接PLL
PLLFEED = 0xaa;
PLLFEED = 0x55;
while ( ((PLLSTAT & (1 << 25)) == 0) ); // 检查连接状态位
return;
}
2. 外设分频
外设启动和分频应在启动代码中实现。LPC2400的启动代码使用一个TargetResetInit( )函数实现,其中相关代码如下:
代码清单4.4
……
#if USE_USB
PCONP |= 0x80000000; // 如果使用USB则打开USB PCLK
#endif
ConfigurePLL();
#if (Fpclk / (Fcclk / 4)) == 1
PCLKSEL0 = 0x00000000; // PCLK = 1/4 CCLK
PCLKSEL1 = 0x00000000;
#endif
#if (Fpclk / (Fcclk / 4)) == 2
PCLKSEL0 = 0xAAAAAAAA; // PCLK = 1/2 CCLK
PCLKSEL1 = 0xAAAAAAAA;
#endif
#if (Fpclk / (Fcclk / 4)) == 4
PCLKSEL0 = 0x55555555; // PCLK = CCLK
PCLKSEL1 = 0x55555555;
#endif
……
为了降低外设功耗,一般都将APB外设时钟设为CCLK时钟的1/4。
3. 进入Idle模式
代码清单4.5
……
PCON = 0x01; // 进入Idle状态,CPU停止
……
进入Sleep模式和Power Down模式采用不同设置值进行设置即可。
4.6 向量中断控制器
4.6.1 LPC2400中断特性
LPC2400使用了ARM PrimeCellTM技术的向量中断控制器,利用映射到AHB总线的地址空间实现快速访问。支持最大32个向量IRQ中断,拥有16个可编程中断优先级,并且每个可编程优先级对应固定硬件优先级,具有硬件优先级屏蔽逻辑。所有中断都可设置为FIQ中断,还可以产生软件中断。
4.6.2 功能概述
ARM处理器内核有两类中断:中断请求(IRQ)和快速中断请求(FIQ)。管理中断类型识别及优先级判断,并向ARM内核提供中断向量和中断信号的模块称为向量中断控制器(VIC)。VIC支持的32个中断可以编程设置为IRQ或FIQ中断类型。这样用户可以按照处理器外围模块的优先级别灵活设置中断的优先级别。
快速中断请求(FIQ)是优先级最高的中断。如果有一个以上中断被设置为FIQ,则VIC对中断输入进行“或”操作,最终向ARM内核产生一个FIQ信号。为了确保FIQ响应的最短延时,在实际应用中一般只设置一个中断源为FIQ类型。这样FIQ中断服务程序就可以直接处理对应模块。如果有多个中断源设置为FIQ,则在FIQ中断服务程序中要先读出VIC的FIQ中断状态字,从而判断真正发生的中断源才能处理对应的中断。
除了设置为FIQ的中断外,其余中断类型为向量IRQ。向量IRQ中断优先级可以编程设置。如果有一个以上IRQ中断分配相同优先级且同时产生中断请求,则连接到VIC的通道靠前的中断源先被应答服务。VIC通道数值分配见中断源小节表。
另外,VIC对所有向量IRQ进行“或”操作,最终向ARM内核产生一个IRQ信号。IRQ中断服务程序先读取VIC的IRQ中断状态字,确定中断源后执行相应中断服务。
4.6.3 中断控制器结构
向量中断控制器VIC的结构如图4.14所示。
图4.14 VIC的结构框图
4.6.4 寄存器描述
VIC内所有寄存器都为32位宽,具体名称及地址见表4.40。
表4.40 VIC寄存器映射表
名称 | 功能描述 | 访问方式 | 复位值[1] | 地址 |
VICIRQStatus | IRQ中断状态寄存器。该寄存器保存各个IRQ中断请求是否有效。 | 只读 | 0 | 0xFFFFF000 |
VICFIQStatus | FIQ中断状态寄存器。该寄存器保存各个FIQ中断请求是否有效。 | 只读 | 0 | 0xFFFFF004 |
VICRawIntr | 原始中断状态寄存器。该寄存器保存32个中断请求和软件中断是否有效,不管它们是否被使能。 | 只读 | - | 0xFFFFF008 |
VICIntSelect | 中断类型选择寄存器。该寄存器用于把中断请求设置为FIQ或者IRQ。 | 读/写 | 0 | 0xFFFFF00C |
VICIntEnable | 中断使能寄存器。该寄存器使能32个中断请求和软件中断产生IRQ或FIQ中断。 | 读/写 | 0 | 0xFFFFF010 |
VICIntEnClr | 中断使能清除寄存器。该寄存器可清除中断使能寄存器已使能的各中断位。 | 只写 | - | 0xFFFFF014 |
VICSoftInt | 软件中断寄存器。该寄存器内容与32个中断请求作相“或”操作。 | 读/写 | 0 | 0xFFFFF018 |
名称 | 功能描述 | 访问方式 | 复位值[1] | 地址 |
VICSoftIntClear | 软件中断清除寄存器。 | 只写 | - | 0xFFFFF01C |
VICProtection | VIC保护使能寄存器。该寄存器限制软件在特权模式下运行时访问VIC各个寄存器。 | 读/写 | 0 | 0xFFFFF020 |
VICSWPriorityMask | 软件优先级屏蔽寄存器。 | 读/写 | 0xFFFF | 0xFFFFF024 |
VICVectAddr0 | 向量地址0寄存器。该寄存器保存IRQ0的中断服务程序入口地址。IRQ0优先级最高,IRQ32最低。 | 读/写 | 0 | 0xFFFFF100 |
VICVectAddr1 | 向量地址1寄存器。 | 读/写 | 0 | 0xFFFFF104 |
VICVectAddr2 | 向量地址2寄存器。 | 读/写 | 0 | 0xFFFFF108 |
VICVectAddr3 | 向量地址3寄存器。 | 读/写 | 0 | 0xFFFFF10C |
VICVectAddr4 | 向量地址4寄存器。 | 读/写 | 0 | 0xFFFFF110 |
VICVectAddr5 | 向量地址5寄存器。 | 读/写 | 0 | 0xFFFFF114 |
VICVectAddr6 | 向量地址6寄存器。 | 读/写 | 0 | 0xFFFFF118 |
VICVectAddr7 | 向量地址7寄存器。 | 读/写 | 0 | 0xFFFFF11C |
VICVectAddr8 | 向量地址8寄存器。 | 读/写 | 0 | 0xFFFFF120 |
VICVectAddr9 | 向量地址9寄存器。 | 读/写 | 0 | 0xFFFFF124 |
VICVectAddr10 | 向量地址10寄存器。 | 读/写 | 0 | 0xFFFFF128 |
VICVectAddr11 | 向量地址11寄存器。 | 读/写 | 0 | 0xFFFFF12C |
VICVectAddr12 | 向量地址12寄存器。 | 读/写 | 0 | 0xFFFFF130 |
VICVectAddr13 | 向量地址13寄存器。 | 读/写 | 0 | 0xFFFFF134 |
VICVectAddr14 | 向量地址14寄存器。 | 读/写 | 0 | 0xFFFFF138 |
VICVectAddr15 | 向量地址15寄存器。 | 读/写 | 0 | 0xFFFFF13C |
VICVectAddr16 | 向量地址16寄存器。 | 读/写 | 0 | 0xFFFFF140 |
VICVectAddr17 | 向量地址17寄存器。 | 读/写 | 0 | 0xFFFFF144 |
VICVectAddr18 | 向量地址18寄存器。 | 读/写 | 0 | 0xFFFFF148 |
VICVectAddr19 | 向量地址19寄存器。 | 读/写 | 0 | 0xFFFFF14C |
VICVectAddr20 | 向量地址20寄存器。 | 读/写 | 0 | 0xFFFFF150 |
VICVectAddr21 | 向量地址21寄存器。 | 读/写 | 0 | 0xFFFFF154 |
VICVectAddr22 | 向量地址22寄存器。 | 读/写 | 0 | 0xFFFFF158 |
VICVectAddr23 | 向量地址23寄存器。 | 读/写 | 0 | 0xFFFFF15C |
VICVectAddr24 | 向量地址24寄存器。 | 读/写 | 0 | 0xFFFFF160 |
VICVectAddr25 | 向量地址25寄存器。 | 读/写 | 0 | 0xFFFFF164 |
VICVectAddr26 | 向量地址26寄存器。 | 读/写 | 0 | 0xFFFFF168 |
VICVectAddr27 | 向量地址27寄存器。 | 读/写 | 0 | 0xFFFFF16C |
VICVectAddr28 | 向量地址28寄存器。 | 读/写 | 0 | 0xFFFFF170 |
VICVectAddr29 | 向量地址29寄存器。 | 读/写 | 0 | 0xFFFFF174 |
VICVectAddr30 | 向量地址30寄存器。 | 读/写 | 0 | 0xFFFFF178 |
VICVectAddr31 | 向量地址31寄存器。 | 读/写 | 0 | 0xFFFFF17C |
VICVectPriority0 | 向量优先级0寄存器。该寄存器设置IRQ0的优先级。 | 读/写 | 0xF | 0xFFFFF200 |
VICVectPriority1 | 向量优先级1寄存器。 | 读/写 | 0xF | 0xFFFFF204 |
VICVectPriority2 | 向量优先级2寄存器。 | 读/写 | 0xF | 0xFFFFF208 |
VICVectPriority3 | 向量优先级3寄存器。 | 读/写 | 0xF | 0xFFFFF20C |
VICVectPriority4 | 向量优先级4寄存器。 | 读/写 | 0xF | 0xFFFFF210 |
VICVectPriority5 | 向量优先级5寄存器。 | 读/写 | 0xF | 0xFFFFF214 |
VICVectPriority6 | 向量优先级6寄存器。 | 读/写 | 0xF | 0xFFFFF218 |
VICVectPriority7 | 向量优先级7寄存器。 | 读/写 | 0xF | 0xFFFFF21C |
VICVectPriority8 | 向量优先级8寄存器。 | 读/写 | 0xF | 0xFFFFF220 |
VICVectPriority9 | 向量优先级9寄存器。 | 读/写 | 0xF | 0xFFFFF224 |
VICVectPriority10 | 向量优先级10寄存器。 | 读/写 | 0xF | 0xFFFFF228 |
VICVectPriority11 | 向量优先级11寄存器。 | 读/写 | 0xF | 0xFFFFF22C |
VICVectPriority12 | 向量优先级12寄存器。 | 读/写 | 0xF | 0xFFFFF230 |
VICVectPriority13 | 向量优先级13寄存器。 | 读/写 | 0xF | 0xFFFFF234 |
VICVectPriority14 | 向量优先级14寄存器。 | 读/写 | 0xF | 0xFFFFF238 |
VICVectPriority15 | 向量优先级15寄存器。 | 读/写 | 0xF | 0xFFFFF23C |
VICVectPriority16 | 向量优先级16寄存器。 | 读/写 | 0xF | 0xFFFFF240 |
VICVectPriority17 | 向量优先级17寄存器。 | 读/写 | 0xF | 0xFFFFF244 |
VICVectPriority18 | 向量优先级18寄存器。 | 读/写 | 0xF | 0xFFFFF248 |
VICVectPriority19 | 向量优先级19寄存器。 | 读/写 | 0xF | 0xFFFFF24C |
VICVectPriority20 | 向量优先级20寄存器。 | 读/写 | 0xF | 0xFFFFF250 |
VICVectPriority21 | 向量优先级21寄存器。 | 读/写 | 0xF | 0xFFFFF254 |
VICVectPriority22 | 向量优先级22寄存器。 | 读/写 | 0xF | 0xFFFFF258 |
VICVectPriority23 | 向量优先级23寄存器。 | 读/写 | 0xF | 0xFFFFF25C |
VICVectPriority24 | 向量优先级24寄存器。 | 读/写 | 0xF | 0xFFFFF260 |
VICVectPriority25 | 向量优先级25寄存器。 | 读/写 | 0xF | 0xFFFFF264 |
VICVectPriority26 | 向量优先级26寄存器。 | 读/写 | 0xF | 0xFFFFF268 |
VICVectPriority27 | 向量优先级27寄存器。 | 读/写 | 0xF | 0xFFFFF26C |
VICVectPriority28 | 向量优先级28寄存器。 | 读/写 | 0xF | 0xFFFFF270 |
VICVectPriority29 | 向量优先级29寄存器。 | 读/写 | 0xF | 0xFFFFF274 |
VICVectPriority30 | 向量优先级30寄存器。 | 读/写 | 0xF | 0xFFFFF278 |
VICVectPriority31 | 向量优先级31寄存器。 | 读/写 | 0xF | 0xFFFFF27C |
VICAddress | 向量地址寄存器。当发生IRQ中断时,该寄存器保存当前有效中断。 | 读/写 | 0 | 0xFFFFFF00 |
[1] 复位值只反映了使用位的数值,不包括保留位的内容。
下面将按照VIC逻辑中的使用顺序对VIC寄存器进行描述,该顺序为从与中断请求输入最密切的寄存器开始,到由软件所使用的最抽象的寄存器结束。对大多数人来说,这也是在学习VIC时读取寄存器的最佳顺序。
1、 软件中断寄存器VICSoftInt(0xFFFFF018)
软件中断寄存器用于产生软件中断。在执行任何逻辑操作之前,该寄存器的内容将与32个不同外设的中断请求相“或”。
表4.41 软件中断寄存器位描述
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 不产生中断请求。写0至该位无效。 | 0 |
1 | 强制产生与该位相关的中断请求。 |
2、软件中断清零寄存器VICSoftIntClear(0xFFFFF01C)
软件中断清零寄存器为只写寄存器。对该寄存器一个或多个位写1可以清除软件中断寄存器中的置1位。
表4.42 软件中断清零寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 写0无效果。 | 0 |
1 | 写1则软件中断寄存器中对应位被清除。 |
3、原始中断状态寄存器VICRawIntr(0xFFFFF008)
该只读寄存器读取所有32个中断请求和软件中断的状态,不管中断是否使能或分类。
表4.43 原始中断状态寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 对应位的中断请求或软件中断未声明。 | - |
1 | 对应位的中断请求或软件中断声明。 |
4、中断使能寄存器VICIntEnable(0xFFFFF010)
中断使能寄存器为读写寄存器。该寄存器使能分配位FIQ和IRQ的中断请求或软件中断。
表4.44 中断使能寄存器
位 | 功能描述 | 复位值 |
31:0 | 当读取该寄存器时,读1表示中断请求使能为FIQ或IRQ,写入1,使能中断请求或软件中断分配为FIQ或IRQ;写入0无效。 | 0 |
5、中断使能清零寄存器VICIntEnClr(0xFFFFF014)
该寄存器为只写寄存器。用于清除中断使能寄存器中一个或多个中断使能位。
表4.45中断使能清零寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 写0无效果。 | - |
1 | 写1则中断使能寄存器中对应位被清除。 |
6、中断选择寄存器VICIntSelect(0xFFFFF00C)
该寄存器将32个中断请求分别分配为FIQ或IRQ.
表4.46中断选择寄存器
位 | 值 | 功能描述 | 复位值 |
31:0 | 0 | 表示对应位的中断请求类型为IRQ。 | 0 |
1 | 表示对应位的中断请求类型为FIQ。 |
7、IRQ状态寄存器VICIRQStatus(0xFFFFF000)
IRQ状态寄存器为只读寄存器。该寄存器读取使能并分配为IRQ的中断请求状态。
表4.47 IRQ状态寄存器
位 | 功能描述 | 复位值 |
31:0 | 某位读取出1代表该位中断请求使能且被分配为IRQ。 | 0 |
8、FIQ状态寄存器VICFIQStatus(0xFFFFF004)
FIQ状态寄存器为只读寄存器。该寄存器读取使能并分配为FIQ的中断请求状态。如果有超过一个请求分配为FIQ,FIQ服务程序可读取该寄存器来确定是哪一个(几个)请求被激活。
表4.48 FIQ状态寄存器
位 | 功能描述 | 复位值 |
31:0 | 某位读取出1代表该位中断请求使能且被分配为FIQ。 | 0 |
9、向量地址寄存器VICVectAddr0~31(0xFFFFF100~17C)
向量地址寄存器一共有32个,每个寄存器可读可写。这些寄存器对应保存32个向量IRQ中断的中断服务程序的入口地址。
表4.49 向量地址寄存器
位 | 功能描述 | 复位值 |
31:0 | 每个寄存器对应一个中断源,该寄存器保存该中断源服务程序入口地址,中断源见表4.54。 | 0x00000000 |
10、向量优先级寄存器VICVectPriority0~31(0xFFFFF200~27C)
向量优先级寄存器用于设置32个向量中断各自优先级。优先级从0~15,0为最高优先级,15最低。所有向量优先级寄存器复位值为最低优先级15,且寄存器允许写操作逐一更改32个向量中断优先级。当优先级相同的中断同时发生,向量地址寄存器VICVectAddr0~31数值小的优先被相应。
表4.50向量优先级寄存器
位 | 功能描述 | 复位值 |
3:0 | 设置相应向量中断优先级0~15。 | 0xF |
31:4 | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义。 | NA |
11、向量地址寄存器VICAddress(0xFFFFFF00)
当处理器响应一个IRQ中断后,该中断的中断服务程序(ISR)地址可以从向量地址寄存器VICAddress读出。而这个地址是由VIC从32向量地址寄存器VICVectPriority0~31其中一个读出装入进来的。
表4.51向量地址寄存器
位 | 功能描述 | 复位值 |
31:0 | 包含当前有效中断的ISR入口地址。该寄存器在ISR结束前必须被写入一个数值(任何值),以此更新VIC优先级硬件逻辑,其他时间对该寄存器写可能引起错误产生。 | 0 |
12、软件优先级屏蔽寄存器VICSWPrioriyMask(0xFFFFF024)
软件优先级屏蔽寄存器包含了16个中断优先级的屏蔽码。
表4.52软件优先级屏蔽寄存器
位 | 值 | 功能描述 | 复位值 |
15:0 | 0 | 中断优先级被屏蔽。 | 0xFFFF |
1 | 中断优先级未被屏蔽。 | ||
31:16 | - | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义。 | NA |
13、保护使能寄存器VICProtection(0xFFFFF020)
保护使能寄存器为可读写寄存器。该寄存器控制VIC寄存器是否能被用户软件在用户态下访问。且该寄存器本身只能在管态下访问。
表4.53保护使能寄存器
位 | 值 | 功能描述 | 复位值 |
0 | 0 | VIC寄存器可以在用户态或管态下访问。 | 0 |
1 | VIC寄存器只能在管态下访问。 | ||
31:1 | - | 保留位,用户软件不应对保留进行写操作,读出的保留位值未定义 | NA |
4.6.5 中断源
表4.54列出了每个外围模块的所有中断源。每个外围设备都有一条或多条中断线连接到向量中断控制器,而且每根中断线可能代表不止一种中断源。除了确定标准的ARM内核,中断线本身没有标志或优先级。
表4.54 连接VIC通道的中断源
功能模块 | 标志 | VIC通道 | 屏蔽码 |
WDT | 看门狗中断(WDINT) | 0 | 0x0000 0001 |
- | 软件中断保留 | 1 | 0x0000 0002 |
ARM内核 | 调试器接收命令中断 | 2 | 0x0000 0004 |
ARM内核 | 调试器发送命令中断 | 3 | 0x0000 0008 |
定时器0 | 匹配0~1(MR0,MR1),捕获0~1(CR0,CR1) | 4 | 0x0000 0010 |
功能模块 | 标志 | VIC通道 | 屏蔽码 |
定时器1 | 匹配0~2(MR0,MR1,MR2),捕获0~1(CR0,CR1) | 5 | 0x0000 0020 |
UART0 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 6 | 0x0000 0040 |
UART1 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI),Modem控制更改 | 7 | 0x0000 0080 |
PWM0, PWM1 | PWM0匹配0~6,PWM0捕获0,PWM1匹配0~6,PWM1捕获0~1 | 8 | 0x0000 0100 |
I2C0 | SI(状态改变) | 9 | 0x0000 0200 |
SPI,SSP0 | SPI中断标志(SPIF),模式错误(MODF),SSP0的Tx FIFO半空(TXRIS),SSP0的Rx FIFO 半满(RXRIS),SSP0接收超时(RTRIS),SSP0接收溢出(RORRIS) | 10 | 0x0000 0400 |
SSP1 | SSP1的Tx FIFO半空(TXRIS),SSP1的Rx FIFO 半满(RXRIS),SSP1接收超时(RTRIS),SSP1接收溢出(RORRIS) | 11 | 0x0000 0800 |
PLL | PLL锁定 | 12 | 0x0000 1000 |
RTC | 计数器增加(RTCCIF),报警(RTCALF),Sub-second中断(RTCSSF) | 13 | 0x0000 2000 |
系统控制 (外部中断) | 外部中断0(EINT0) | 14 | 0x0000 4000 |
外部中断1(EINT1) | 15 | 0x0000 8000 | |
外部中断2(EINT2) | 16 | 0x0001 0000 | |
外部中断3(EINT3) 注:EINT3与GPIO中断共享 | 17 | 0x0002 0000 | |
ADC0 | A/D转换器0 | 18 | 0x0004 0000 |
I2C1 | SI(状态改变) | 19 | 0x0008 0000 |
BOD | 掉电检测 | 20 | 0x0010 0000 |
以太网 | Wakeup,软件中断,传输成功,传输结束,传输错误,传输XX,接收成功,接收结束,接受错误,接受溢出 | 21 | 0x0020 0000 |
USB | USB_INT_REQ_LP,USB_INT_REQ_HP,USB_INT_REQ_DMA | 22 | 0x0040 0000 |
CAN | CAN命令,CAN0传输,CAN0接收,CAN1传输,CAN1接收 | 23 | 0x0080 0000 |
SD/MMC接口 | RxDataAvlbl ,TxDataAvlbl,RxFifoEmpty,TxFifoEmpty,RxFifoFull,TxFifoFull,RxFifoHalFull,TxFifoHalEmpty,RxActive,TxActive,CmdActive,DataBlockEnd,StartBitErr,DataEnd,CmdSent,CmdRespEnd,RxOverrun,TxUnderrun,DataTimeOut,CmdTimeOut,DataCrcFail,CmdCrcFail | 24 | 0x0100 0000 |
GP DMA | DMA通道0状态,DMA通道1状态 | 25 | 0x0200 0000 |
定时器2 | 匹配0~3,捕获0~1 | 26 | 0x0400 0000 |
定时器3 | 匹配0~3,捕获0~1 | 27 | 0x0800 0000 |
UART2 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 28 | 0x1000 0000 |
UART3 | Rx线状态(RLS),发送保持寄存器空(THRE),Rx数据可用(RDA),字符超时指示(CTI) | 29 | 0x2000 0000 |
I2C2 | SI(状态改变) | 30 | 0x4000 0000 |
I2S | Irq_rx,Irq_tx | 31 | 0x8000 0000 |
4.6.6 VIC使用注意事项
1.VIC中断与片内RAM调试。如果在片内RAM中调试程序(JTAG调试)时需要使用中断,那么必须将中断向量重新映射到地址0x00000000,这样做是因为所有的异常向量都位于地址0x00000000及以上。通过将寄存器MEMMAP(位于系统控制模块当中)配置为用户RAM模式来实现这一点。另外,用户代码编译链接时应该使中断向量表装载到地址0x40000000。
2.多个FIQ中断。虽然可以选择多个中断源(通过设置VICIntSelect)为FIQ中断,但是只有一个专门的中断服务程序来响应所有出现的FIQ请求。因此,如果分配为FIQ的中断多于一个,FIQ中断服务程序就必须先读取VICFIQStatus的内容来识别具体有效的FIQ中断源,然后在进行相应中断处理。不过还是建议用户只设置一个FIQ中断,以确保FIQ中断延迟最小。
3.IRQ中断服务程序与VIC寄存器。在中断服务程序执行完毕后,对外设中断标准的清零将会对VIC寄存器(VICRawIntr,VICFIQStatus和VICIRQStatus)当中的对应位产生影响。另外,为了能够服务下次中断,必须在中断返回之前对VICVectAddr寄存器执行一次写操作(一般可写入0),该写操作将清零内部中断优先级硬件当中对应的标志。
4.VIC中断禁能操作。若要禁止VIC中断,则必须清零VICIntEnable寄存器中的对应位,这可以通过写VICIntEnClr寄存器实现。这同样应用于VICSoftInt和VICSoftIntClear。
4.6.7 应用举例
本节以实现按键的外部中断为例介绍向量中断控制器的使用,以及在IAR Embedded Workbench集成开发环境中编写中断服务程序的方法。
1、基本操作流程
设置IRQ/FIQ中断,若是IRQ中断,则可以设置为向量中断并分配中断优先级,然后设置中断使能,以及向量中断对应地址。当产生中断后,若是IRQ中断,则可以读取向量地址寄存器,然后跳转到相应服务代码。当退出中断时,对向量地址寄存器写0,通知VIC中断结束。
对于中断源(VIC通道)的IRQ/FIQ选择,由VICIntSelect寄存器控制,每个中断源于VICIntSelect的各位一一对应,比如VIC通道14(外部中断0)与VICIntSelect的Bit14位对应,设置该位为1,则分配为FIQ中断,否则为IRQ中断。
2、设置异常向量表
在LPC2400的启动代码中(在cstartup.s79文件中)首先设置异常向量表并设置各个模式下的堆栈指针,最后跳转到用户程序运行。异常向量表是一个包含8种异常情况的向量表,具体分配如表4.55所示。
表4.55 ARM异常向量表
地址 | 异常类型 |
0x0000 0000 | 复位 |
0x0000 0004 | 未定义指令 |
0x0000 0008 | 软件中断 |
0x0000 000C | 预取指令错误 |
0x0000 0010 | 取数据错误 |
0x0000 0014 | 保留 |
0x0000 0018 | IRQ中断 |
0x0000 001C | FIQ中断 |
系统一旦产生IRQ中断,LPC2400处理器会切换到IRQ模式,并且跳转到向量表0x00000018地址执行程序。如程序清单4.6②所示,在IRQ向量处使用的指令与其他向量不同,当CPU执行这条指令但还没有跳转时, [PC, # -0x0120]表示当前PC值减去0x0120 ,当前PC的值为0x00000020,减去0x0120为0xFFFFFF00。这是VIC特殊寄存器VICVectAddr的物理地址,该寄存器保存当前将要服务的IRQ中断服务程序的入口,所以用这条LDR指令就可以直接跳转到需要的中断服务程序中。
一旦产生FIQ中断,处理器会切换到FIQ模式,并且跳转到向量表0x0000001C地址执行程序。如程序清单4.6③所示,程序将跳到FIQ_Handler标号处,处理FIQ中断服务程序。
代码清单4.6 异常向量表设置
__program_start
LDR PC, =Reset_Handler ①
LDR PC, =Undef_Handler
LDR PC, =SWI_Handler
LDR PC, =PAbt_Handler
LDR PC, =DAbt_Handler
B .
LDR PC, [PC, # -0x0120] ②
LDR PC, =FIQ_Handler ③
3、VIC初始化
接下来,在LPC2400的启动代码中包含有VIC初始化程序,如代码清单4.7所列。程序首先禁止所有中断(代码清单4.7 ①),设置VICVectAddr寄存器的值为0,将所有中断设置为IRQ中断(代码清单4.7 ③)。最后在for循环中把所有向量地址寄存器内容设置为0(代码清单4.7 ④ ),向量优先级寄存器设置为0xF,最低中断优先级(代码清单4.7 ⑤)。
禁止所有中断是避免调试时一个中断没有响应就再次装入程序运行,而VIC状态错误不能正确识别中断。
代码清单4.7 init_VIC()——VIC初始化
VICIntEnClr = 0xffffffff; ①
VICVectAddr = 0; ②
VICIntSelect = 0; ③
/* set all the vector and vector control register to 0 */
for ( i = 0; i < VIC_SIZE; i++ ) // 32个中断服务向量复位
{
vect_addr = (DWORD *)(VIC_BASE_ADDR + VECT_ADDR_INDEX + i*4);
vect_cntl = (DWORD *)(VIC_BASE_ADDR + VECT_CNTL_INDEX + i*4);
*vect_addr = 0x0; //中断服务函数都指向开头 ④
*vect_cntl = 0xF; //优先级最低 ⑤
}
4、编写中断服务程序
IAR Embedded Workbench C/C++编译器支持ARM核的IRQ中断、FIQ快速中断和SWI软件中断,可以直接采用C语言编写中断函数。中断函数必须采用ARM模式编译,如果用户正在使用的是Thumb模式,应采用扩展关键字__arm或“#pragma type_attribute=__arm”指令将其转换到ARM模式。IRQ中断函数采用扩展字__irq或#pragma type_attribute=__irq”指令声明,如程序清单4.8 ①所示。FIQ中断函数采用扩展字__fiq或#pragma type_attribute=__fiq”指令声明。需要特别注意的是,IRQ和FIQ函数的返回值类型必须为void,并且不能带有参数。
在中断服务程序开始先清除外部中断标志寄存器EXTINT中EINT0位,之后进行中断服务处理,最后写VICVectAddr寄存器,更新VIC优先级逻辑,以相应下次外部中断。
代码清单4.8 EINT0_Handler()——外部中断服务函数
__irq __arm void EINT0_Handler (void) ①
{
EXTINT = EINT0; /* 清除EXTINT寄存器中EINT0位 */ ②
……/*中断服务*/
VICVectAddr = 0; /* 写VICVectAddr寄存器,更新VIC优先级逻辑 */ ③
}
5、安装外部中断服务程序
安装外部中断服务程序主要是初始化VIC的几个特别寄存器。Install_irq一共有3个参数:IntNumber为连接VIC的中断通道数,HandlerAddr为中断函数地址,Priority为该中断通道的优先级。
如代码清单4.9所示,函数首先设置中断使能清除寄存器VICIntEnClr的对应位,无效该中断。接着通过中断通道数IntNumber得到对应向量地址寄存器VICVectAddrX和向量优先级寄存器VICVectPriorityX地址。然后使用其余两个参数初始化这两个寄存器。最后置位中断使能寄存器VICIntEnable的对应,位使能该中断。
代码清单4.9 install_irq()——中断安装函数
DWORD install_irq( DWORD IntNumber, void *HandlerAddr, DWORD Priority )
{
DWORD *vect_addr;
DWORD *vect_cntl;
VICIntEnClr = 1 << IntNumber; /* Disable Interrupt */
vect_addr=(DWORD *)(VIC_BASE_ADDR+VECT_ADDR_INDEX+IntNumber*4);
vect_cntl=(DWORD *)(VIC_BASE_ADDR+VECT_CNTL_INDEX+IntNumber*4);
*vect_addr=(DWORD)HandlerAddr; /* set interrupt vector */
*vect_cntl=Priority;
VICIntEnable = 1 << IntNumber; /* Enable Interrupt */
}
代码清单4.10为调用install_irq函数安装EINT0_Handler中断服务函数的方法。
代码清单4.10安装EINT0_Handler函数
if ( install_irq( EINT0_INT, (void *)EINT0_Handler, HIGHEST_PRIORITY ) = = FALSE )
{
return (FALSE);
}
4.6 LPC2400最小系统
在嵌入式系统硬件开发过程中,直接设计和开发目标板硬件会有相当大的难度和风险,可以先通过设计最小系统,将所需IO引脚都引出到一个插针或者板板连接器(FPC)上。实际应用电路板(本文简称底板)另行设计,最小系统板可以同直插封装的器件一样与应用电路板想连接。下文将介绍LPC2400的最小系统。
如今如同LPC2400这样的MCU芯片将FLASH、SRAM以及一些总线等集成在一片芯片中,但是仍离不开一些外围电路的设计。这部分外围电路主要为MCU提供电源、时钟震荡、电压转换、I/O口保护和驱动等功能。LPC2400的最小系统如图4.15所示,这个最小系统此文暂称为核心板。
图4.15. LPC2400核心板
本核心板分为供电电路、时钟电路、复位电路以及外部存储器电路。现做简单介绍:
1.供电电路。核心板电源主要靠实际应用电路+3.3V提供,通过左右两排插针中的相关引脚提供。LPC2400芯片采用单电源(+3.3V)供电,这样可以简化电路设计,降低产品成本。电源纹波直接影响着整个电路的工作,为了得到稳定的电压,需要外接一些电容。这些电容分为两类,一类为储能电容,这些电容的电容值比较大,如1uF、10uF等。另一类为去耦电容,其电容值较小,如0.1uF、0.01uF等,它们可以达到抑制高频噪声的功用。
2.时钟电路。这部分电路主要有晶体振荡器、电容以及电阻组成。目前有些MCU已经将该部分集成到芯片内部,但是多是以RC振荡电路形式提供所需时钟,其稳定性得不到较高的保证。使用外部晶振可以使MCU得到稳定的时钟频率。
3.复位电路。虽然目前大部分的MCU都集成有上电复位电路,在系统上电时MCU会自动产生复位信号。但在设计初期可以加入手动复位电路以方便调试。外部复位电路可以采用阻容振荡电路,也可采用诸如MAX811或者SGM811之类的专用复位芯片。
4.外部存储器电路。目前中高档的MCU尤其是ARM内核的MCU都引出有外部总线。由于大中型软件系统对FLASH以及RAM的容量的需求以及内部集成FLASH造成成本偏高的现实,使得采用外部FLASH作为存储器件最为合适。
由于LPC2478与LPC2470、LPC2468以及LPC2460等恩智浦LPC2400系列ARM7单片机在引脚上是兼容的,所以LPC2400最小系统同样也适用于上述芯片。
如图4.15所示,左右两排插针引出了实际应用电路板上所需的功能引脚,上下两排焊盘引出了LPC2478所能提供的外部总线。
LPC2400最小系统包括一下几个部分:电源电路、时钟电路、复位电路、JTAG调试电路以及功能接口电路等。其中各个部分功能如下:
1、 时钟电路给MCU提供一个外部12MHz的以及一个32.768MHz的石英振荡器。
2、 复位电路是通过引脚的方式与底板上的手动复位相连。通过底板复位电路提供手动复位信号。
3、 JTAG电路可以让用户方便的通过仿真器调试或者下载程序。
4、 外部存储电路,即有板载外部存储器(如Nor Flash、Nand Flash和SDRAM),又将外部总线引出方便用户通过外部总线扩展其他器件。
根据电路板的工作环境,可能会对电路板提出不同的要求。诸如噪声以及干扰较强的场合,以及对系统稳定性、可靠性要求较高时,印刷电路板会采用多层板设计。一般地,6层板噪声比4层板低10dB,4层板比双面板的噪声低20dB。但板层越多,相应的成本也就越高。如图4.15所示的核心板采用6层板设计,为测试提供了稳定可靠的电路。
习题:
4.1 简单说明LPC2400系列芯片复位时的处理流程。
4.2 LPC2400系列芯片的存储器空间是如何分布的?
4.3 LPC2400芯片的引脚通常都是复用的,当要使用引脚的某个功能时,应如何进行设置?
4.4 简述使能PLL的工作过程。
4.5 如果LPC2400使用的外部晶振频率为12MHz,计算最大的系统时钟频率CCLK为多少,此时M值和N值各为多少,并编写设置PLL的程序段。
4.6 LPC2400有哪些降低功耗的措施?
4.7 如果要使用外部中断0来唤醒掉电的LPC2400,应设置哪些寄存器,各寄存器的值应为多少?写出其程序段。