STM32嵌入式微控制器快速上手
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第2章 Cortex-M3体系结构

【前导知识】 微处理器 微型计算机 微型计算机系统 寄存器RAM ROM堆栈Flash总线 地址总线 数据总线 控制总线 指令集

2.1 CM3微处理器核结构

CM3处理器内核是ARMv7-M体系结构,如图2-1所示,它集成了CM3核的中央处理器和先进的系统外设,还集成中断控制、内存保护,以及系统调试和跟踪功能。这些外设可进行高度配置,使得CM3处理器更贴近系统的需求。CM3内核和集成部件均经过专门设计,以满足使用内存小、减少引脚数目和降低功耗的要求。CM3内核是建立在一个高性能哈佛结构的三级流水线基础上的,由于指令和数据可以从内存中同时读取,CM3对多个操作并行执行,加快了应用程序的执行速度。CM3内核还包含一个使用于传统Thumb和新型Thumb-2指令的译码器、一个支持硬件乘法和硬件除法的先进ALU、控制逻辑和用于连接处理器其他部件的接口。CM3处理器是一个32位处理器,带有32位宽的数据路径、寄存器库和内存接口。其中有13个通用寄存器、2个堆栈指针、1个链接寄存器、1个程序计数器和一系列包含编程状态寄存器的特殊寄存器。

图2-1 CM3核结构

2.2 处理器的工作模式及状态

手机的辐射会对飞机的飞行安全造成影响,因此乘客在飞机起飞前必须关掉手机。这样做虽然消除了安全隐患,但手机的其他功能如拍照、看电影、听音乐、玩游戏就不能使用了,这些都是乘客打发旅途无聊时间的娱乐功能,而且不会对安全造成影响。为了解决这个问题,有别于手机正常待机模式的飞行模式应运而生。当手机处于飞行模式时,手机的正常通话功能被关闭,手机不再产生辐射,但飞行模式的手机娱乐功能可以照常使用。飞行模式解决了手机娱乐功能在空中无法使用的问题。通常情况下,手机会一直处于正常待机模式,因为为用户提供通话功能是手机的基本用途之一,飞行模式仅在飞机上使用。不同的应用会有不同的模式,不同的模式解决不同的问题,模式代表着一组特定的应用或某类问题的解决方案。CM3中引入模式是用于区别普通应用程序的代码与异常和中断服务例程的代码。

CM3中提供一种存储器访问的保护机制,使得普通的用户程序代码不能意外地或恶意地执行涉及要害的操作,因此处理器为程序赋予两种权限,分别为特权级和用户级。特权执行可以访问所有资源。非特权执行时对有些资源的访问受到限制或不允许访问。

CM3下的操作模式和特权级别见表2-1,操作模式转换图如图2-2所示。

表2-1 CM3下的操作模式和特权级别

图2-2 操作模式转换图

在CM3运行主应用程序时(线程模式),既可以使用特权级,也可以使用用户级;但是异常服务例程必须在特权级下执行。复位后,处理器默认进入线程模式,特权级访问。在特权级下,程序可以访问所有范围的存储器,并且可以执行所有指令,但如果有存储器保护单元MPU,MPU规定的禁地不能被访问。在特权级下的程序可做的事情比用户级多一些。一旦进入用户级,用户级的程序不能简单地试图改写CONTROL寄存器(详见2.3节)而回到特权级,它必须先执行一条系统调用指令(SVC),这会触发SVC异常,然后由异常服务例程(通常是操作系统的一部分)接管,如果批准了进入,则异常服务例程修改CONTROL寄存器,才能在用户级的线程模式下重新进入特权级。

事实上,从用户级到特权级的唯一途径就是异常,如果在程序执行过程中触发了一个异常,处理器先切换入特权级,并且在异常服务例程执行完毕时退出时,返回先前的状态;也可以手工指定返回的状态。

通过引入特权级和用户级,能够在硬件上限制某些不受信任的或还没有调试好的程序,不让它们随便地配置重要的寄存器,因而系统的可靠性得到了提高。进一步地,如果配了MPU,它还可以作为特权机制的补充——保护关键的存储区域不被破坏,这些区域通常是操作系统的区域。举例来说,操作系统的内核通常都在特权级下执行,所有没有被MPU禁止掉的存储器都可以访问。在操作系统开启了一个用户程序后,通常都会让它在用户级下执行,从而使系统不会因某个程序的崩溃或恶意破坏而受损。

CM3还有Thumb状态和调试状态两种状态。Thumb状态是16位和32位半字对齐的Thumb和Thumb-2指令的正常执行状态。当处理器调试时进入调试状态。

2.3 寄存器

CM3寄存器如图2-3所示。

图2-3 CM3寄存器

1. 通用寄存器

通用寄存器包括R0~R12。R0~R7也被称为低组寄存器。它们的字长全是32位。所有指令都能访问它们。复位后的初始值是随机的。

R8~R12也被称为高组寄存器。它们的字长也是32位。只有很少的16位Thumb指令能访问它们,32位的Thumb-2指令则不受限制。复位后的初始值是随机的。

2. 堆栈指针R13

在CM3处理器内核中共有两种堆栈指针,支持两个堆栈,分别为进程堆栈或主堆栈,这两种堆栈都指向R13,因此在任何时候进程堆栈或主堆栈中只有一个是可见的。当引用R13(或写做SP)时,引用的是当前正在使用的那一个,另一个必须用特殊的指令来访问(MRS或MSR指令)。这两个堆栈指针分别是:

◆ 主堆栈指针(MSP),或者写做SP main。这是默认的堆栈指针,它由操作系统内核、异常服务例程以及所有需要特权访问的应用程序代码来使用。

◆ 进程堆栈指针(PSP),或者写做SP process。用于不处于异常服务例程中的常规的应用程序代码。处理模式和线程模式下,都可以使用MSP,只有线程模式可以使用PSP。

堆栈与微处理器模式的对应关系如图2-4所示。

图2-4 堆栈与微处理器模式的对应关系

3. 链接寄存器R14

R14是链接寄存器(LR)。在一个汇编程序中,可以把它写做LR或R14。LR用于在调用子程序时存储返回地址,也用于异常返回。

尽管PC是不可写的,LR的最低有效位却是可读/写的。这是历史遗留的产物。在以前,由位0来指示ARM/Thumb状态。因为其他有些ARM处理器支持ARM和Thumb状态并存,为了方便汇编程序移植,CM3需要允许最低有效位可读/写。

4. 程序计数器R15

R15是程序计数器,在汇编代码中一般称为PC(Program Counter)。因为CM3内部使用了指令流水线,读PC时返回的值是当前指令的地址+4。例如:

            0x1000;MOV R0,PC;R0=0x1004

如果向PC中写数据,就会引起一次程序的分支(但是不更新LR寄存器)。CM3中的指令至少是半字节对齐的,所以PC的最低有效位总是读回0。

5. 程序状态寄存器

程序状态寄存器在其内部又被分为3个子状态寄存器,即应用程序PSR(APSR)、中断号PSR(IPSR)和执行PSR(EPSR),如图2-5所示。通过MRS/MSR指令,这3个PSRs既可以单独访问,也可以组合访问(2个组合,3个组合都可以)。当使用三合一的方式访问时,应使用名字“xPSR”或“PSR”,如图2-6所示。程序状态寄存器各位域定义见表2-2。

图2-5 CM3中的程序状态寄存器(xPSR)

图2-6 合体后的程序状态寄存器(xPSR)

表2-2 程序状态寄存器位域定义

6. PRIMASK,FAULTMASK和BASEPRI寄存器

这3个寄存器用于控制异常的使能和除能,其功能描述见表2-3。

表2-3 异常的寄存器

7. 控制寄存器

控制寄存器有两个用途,其一用于定义特权级别,其二用于选择当前使用哪个堆栈指针。由两个位来行使这两个职能,见表2-4。

表2-4 CM3的CONTROL寄存器

因为处理者模式永远都是特权级的,因此CONTROL [0] 仅对线程模式操作。仅当在特权级下操作时才允许写CONTROL [0] 位。一旦进入了用户级,唯一返回特权级的途径,就是触发一个(软)中断,再由服务例程改写该位。

在CM3的处理者模式中,CONTROL [1] 总是0。在线程模式中则可以为0或1。因此,仅当处于特权级的线程模式下,此位才可写,其他场合下禁止写此位。

微处理器工作模式、堆栈、控制寄存器关系总结见表2-5。

表2-5 微处理器工作模式、堆栈、控制寄存器关系

CM3寄存器总结见表2-6。

表2-6 CM3寄存器总结

2.4 总线接口

片上总线标准繁多,而由ARM公司推出的AMBA片上总线受到广大开发商和SoC片上系统集成商的喜爱,已成为一种流行的工业片上结构。AMBA规范主要包括AHB(Ad-vanced High performance Bus)系统总线和APB(Advanced Performance Bus)外设总线。二者分别适用于高速与相对低速设备的连接。

由图2-7可以看出,处理器包含5个总线接口,即Icode存储器接口、Dcode存储器接口、系统接口、外部专用外设总线接口和内部专用外设总线接口。

图2-7 CM3内部结构及总线连接

Icode总线是32位的AHB总线。从程序存储器空间(0x00000000~0x1FFFFFFF)取指令和取向量在此总线上完成。所有取指令都是按字来操作的,每个字的取指令数目取决于运行的代码和存储器中代码的对齐情况。

Dcode总线是32位的AHB总线。从程序存储器空间(0x00000000~0x1FFFFFFF)取数据和调试访问在此总线上完成。数据访问的优先级比调试访问要高。因而当总线上同时出现内核和调试访问时,必须在内核访问结束后才开始调试访问。

系统接口是32位的AHB总线。对系统存储空间(0x20000000~0xDFFFFFFF,0 xE0100000~0 xFFFFFFFF)的取指令、取向量,以及数据和调试访问在此总线上完成。当该总线上同时出现上述访问时,仲裁顺序(按递减优先级)应该是:数据访问、取指令、取向量、调试。系统总线用于访问内存和外设,覆盖的区域包括SRAM、片上外设、片外RAM、片外扩展设备,以及系统级存储区的部分空间。系统接口包含处理不对齐访问、FPB重新映射访问、bit-band访问,以及流水线取指令的控制逻辑。

外部专用外设总线接口是APB总线。对外部外设存储空间(0 xE0040000~0 x1E00 FFFF)的取数据和调试访问在此总线上完成。该总线不支持等待状态。数据访问的优先级比调试访问要高。因而当总线上同时出现内核和调试访问时,必须在内核访问结束后才开始调试访问。

内部专用外设总线接口用于访问嵌套向量中断控制器NVIC、数据观察和触发(DWT)、Flash修补和断点(FPB),以及存储器保护单元(MPU)。

2.5 存储器的组织与映射

1. 存储器格式

ARM体系结构将存储器看做从0地址开始的字节的线性组合。从0字节到3字节放置第一个存储的字数据,从第4个字节到第7个字节放置第2个存储的字数据,依次排列。作为32位的微处理器,ARM体系结构所支持的最大寻址空间为4GB(232字节)。

ARM体系结构可以用两种方法存储字数据,称为大端格式和小端格式,具体说明如下。

1)大端格式 在这种格式中,字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。

2)小端格式 与大端存储格式相反,在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节。图2-8所示为0x12345678字数据的大、小端存储方式。

图2-8 大端格式和小端格式

2. ARM存储器层次结构

ARM存储系统有非常灵活的体系结构,可以适应不同的嵌入式应用系统的需要,如图2-9所示。

图2-9 存储器层次结构

3. CM3存储器组织

CM3的存储系统采用统一的编址方式,如图2-10所示。CM3预先定义好了“粗线条的”存储器映射,通过把片上外设的寄存器映射到外设区,就可以简单地以访问内存的方式来访问这些外设的寄存器,从而控制外设的工作。这种预定义的映射关系可以对访问速度进行优化,而且对于片上系统的设计更易集成。片上外设可以使用C语言来操作。

图2-10 CM3存储器组织

CM3处理器为高达4GB的可寻址存储空间提供简单和固定的存储器映射。程序可以在代码区、内部SRAM区及外部RAM区中执行。但是,由于指令总线与数据总线是分开的,最理想的办法是把程序放到代码区,从而使取指令和数据访问各自使用自己的总线来并行处理。

CM3片上SRAM区的大小是512 MB,这个区通过系统总线来访问。如图2-10所示,在这个区的下部,有一个1 MB的区间,被称为“位绑定区(bit-band)”。该位绑定区还有一个对应的32 MB的“位绑定别名(alias)区”,容纳了8 M个位变量。位绑定区对应的是最低的1 MB地址范围,而位绑定别名区里面的每个字对应位绑定区的1位。通过位绑定功能,可以把一个布尔型数据打包在一个单一的字中,从位绑定别名区中,像访问普通内存一样使用它们。位绑定别名区中的访问操作是原子的(不可分割),省去了传统的“读—修改—写”3个步骤。

与SRAM相邻的512 MB范围由片上外设的寄存器使用。这个区中也有一个32 MB的位绑定别名区,以便于快捷地访问外设寄存器,用法与片上SRAM区中的位绑定相同。

还有两个1 GB的范围,分别用于连接片外RAM和片外外设。

最后还剩下512 MB的隐秘区域,包括了系统及组件、内部私有外设总线、外部私有外设总线,以及由芯片供应商提供定义的系统外设,数据字节以小端格式存放在存储器中。

私有外设总线有两条:AHB私有外设总线,只用于CM3内部的AHB外设,它们是NVIC、FPB、DWT和ITM;APB私有外设总线,既用于CM3内部的APB设备,也用于片外外设(详见2.9节)。CM3允许器件制造商再添加一些片上APB外设到APB私有总线上,它们通过APB接口来访问,如图2-10所示。

4. STM32存储器映射

STM32将可访问的存储器空间分成8个块,每块为512 MB。其他没有分配给片上存储器和外设的空间都是保留的地址空间,如图2-11所示。8个块分别如下所述。

图2-11 片上外设区存储结构

(1)代码区(0x00000000~0x1FFF FFFF):该区可以存放程序。

(2)SRAM区(0 x20000000~0 x3 FFF FFFF):用于片内SRAM。此区也可以存放程序,用于固件升级等维护工作。

(3)片上外设区(0x40000000~0x5FFF FFFF):用于片上外设。片上外设区存储结构如图2-11所示。STM32分配给片上各个外围设备的地址空间按总线分成3类,APB1总线外设存储地址见表2-7,APB2总线外设存储地址见表2-8,AHB总线外设存储地址见表2-9。如果某款控制器不带有某个片上外设,则该地址范围保留。

表2-7 APB1 总线外设存储地址表

表2-8 APB2总线外设存储地址表

表2-9 AHB总线外设存储地址表

(4)外部RAM区的前半段(0 x60000000~0 x7 FFF FFFF):该区地址指向片上RAM或片外RAM。

(5)外部RAM区的后半段(0x80000000~0x9FFF FFFF):同前半段。

(6)外部外设区的前半段(0xA0000000~0xBFFF FFFF):用于片外外设的寄存器,也用于多核系统中的共享内存。

(7)外部外设区的后半段(0 xC0000000~0 xDFFF FFFF):目前与前半段的功能完全一致。

(8)系统区(0 xE0000000~0 xFFFF FFFF):此区是私有外设和供应商指定功能区。此区不可执行代码。系统区涉及很多关键部位。

5. 位绑定操作

1)位绑定操作定义 在51单片机中,以位(bit)为数据对象的操作称为位操作,例如:

P1.2=0;//把P1口的第3个脚(bit2)置0

P1.2=1;//把P1口的第3个脚(bit2)置1

类似位操作,位绑定操作把一个地址单元的32位变量中的每一位,通过一个简单的地址变换算法,映射到另一个地址空间,每一位占用一个地址,对此地址空间的操作,只有数据的最低位是有效的。这样对某空间位操作时,就可以不用屏蔽操作,优化了RAM和I/O寄存器的读/写,提高了位操作的速度。

CM3中支持位绑定操作的地址区称为位绑定区。在寻址空间的另一地方,有一个“别名区”空间,从这个地址开始处,每一个字(32 bit)就对应位绑定区的一位,而在位绑定区中,每个bit都映射到别名地址区的一个字,对别名地址的访问最终会变换成对位绑定区的访问。

位绑定操作可以把代码缩小,速度更快,效率更高,更安全。一般操作是“读—改—写”的方式,而位绑定别名区是“写”操作,防止中断对“读—改—写”方式的影响。

位绑定还能用来简化跳转程序。以前依据某个位跳转时,必须这样做:① 读取整个寄存器;② 掩蔽不需要的位;③ 比较并跳转。有了位绑定操作,可以从位绑定别名区读取状态位;比较并跳转。除此之外,其他总线活动不能中断位绑定操作。

2)支持位绑定操作的两个内存区 支持位绑定操作的两个内存区的范围是:

0x20000000~0x200F FFFF(SRAM区中的最低1MB)

0x40000000~0x400F FFFF(片上外设区中的最低1MB)

对于SRAM内存区,位绑定地址映射见表2-10。对于片上外设区中的位绑定地址映射关系见表2-11。

表2-10 SRAM区中的位绑定地址映射

表2-11 片上外设区中的位绑定地址映射

3)位绑定区与位绑定别名区的对应关系 对于SRAM位绑定区的某个bit,若它所在字节地址为A,位序号为n(0≤n≤7),则该bit在别名区的地址为

对于片上外设位绑定区的某个bit,若它所在字节的地址为A,位序号为n(0≤n≤7),则该bit在别名区的地址为

上述两式中,“×4”表示一个字为4个字节,“×8”表示一个字节中有8个bit。

下面的映射公式统一给出了别名区中的每个字是如何对应位绑定区的相应位的:

            bit word addr=bit band base+(byte offset×32)+(bit number×4)

其中,bit word addr是别名存储器区中字的地址,它映射到某个目标位;bit band base是别名区的起始地址,对于SRAM位绑定区为0x22000000,对于片上外设位绑定区为0x42000000;byte offset是包含目标位的字节在位段里的序号;bit number是目标位所在位置(0→7)。

【例2-1】 写出图2-12中位绑定区与位绑定别名区的对应关系。

图2-12 位绑定区与位绑定别名区的对应关系图

图2-12中,位地址和别名区关系如下所述。

(1)地址0x23FFFFE0的别名字映射为0x200FFFFF的bit-band字节的位0:

            0x23FFFFE0=0x22000000+(0xFFFFF×32)+0×4

(2)地址0 x23 FFFFFC的别名字映射为0 x200 FFFFF的bit-band字节的位7:

            0x23FFFFEC=0x22000000+(0xFFFFF×32)+7×4

(3)地址0x22000000的别名字映射为0x20000000的bit-band字节的位0:

            0x22000000=0x22000000+(0×32)+0×4

(4)地址0 x220001 C的别名字映射为0 x20000000的bit-band字节的位7:

            0x2200001C=0x22000000+(0×32)+7×4

【例2-2】 SRAM地址为0x20000300的字节中的第2位,对应别名区中地址是多少?

因为:0 x22006008=0 x22000000+(0 x300×32)+(2×4)

所以:对0 x22006008地址的写操作与对SRAM中地址0 x20000300字节的第2位执行“读—改—写”操作有着相同的效果。读0 x22006008地址返回SRAM中地址0 x20000300字节的第2位的值(0 x01或0 x00)。

【例2-3】 在SRAM的0x20004000地址定义一个长度为512字节的数组:

            #pragma location=0x20004000  root  no init u8 Buffer[512];

该数组首字节的bit0对应的Bit-band地址为

0 x22000000+(0 x4000×32)+(0×4)=0 x22080000

GPIOA的端口输出数据寄存器位于地址0 x4001080 C,对于GPIOA的PIN0来说,控制其输出电平的比特位的Bit-band地址为

0 x42000000+(0 x1080 C×32)+(0×4)=0 x42210180

将数组中的数据通过GPIOA的PIN0口输出,若不使用Bit-band功能,代码为

            U8 *pBuffer=(u8*)0x20004000;
            for(u16 cnt=0;cnt<512;cnt++)
            {
            for(u8 num=0;num<8;num++)
            {
            if((Buffer[cnt] >>num)&0x01)
              GPIOA->BSRR=1;
            else
            GPIOA->BRR=1;
              }
              pBuffer++;
            }
              使用Bit-band功能,代码为:
              u32 *pBuffer=(u32*)0x22080000;
              u16 cnt=512 *8;
              While(cnt- -)
              {
             (*((u32*)0x42210180))=*pBuffer++;
            }

可见,使用了bit-band功能,运算量和代码量都大大减少。

2.6 指令集

大家从出生就开始了人类语言的学习,例如:

到两岁时:妈妈,车车来了。

再到5岁时:妈妈,我今天看到一辆好长好长的火车。

最后到小学以后:今天我和妈妈去了新的北京南站,看到了来来往往或整装待发的长长的火车。

计算机编程语言和人类语言一样,也包括字、词、句、段。例如,C语言中,各种类型的变量、常量、运算符(如赋值符=,大于>等),关键字(如if,into等),都是“字”;表达式即为“词”;语句即为“句”;函数、宏定义即为“段”。运算符、关键字就是“动词”,变量、常量就是“名词”。ARM汇编语言也离不开这4个字,操作数(寄存器、立即数)、操作码,条件描述,是“字”;地址模式,带有条件描述的指令,是表达式,是“词”;每条汇编指令是“句”;函数及宏是“段”。计算机编程语言是软件的载体,而软件和硬件的是通过指令集联系的,即指令集是计算机硬件和软件的接口,如图2-13所示。

图2-13 软件、硬件和指令集的关系

1. ARM指令集

ARM微处理器的指令集是加载/存储(Load/Store)型的32位指令集,也就是说,指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。ARM微处理器的指令集可以分为跳转指令、数据处理指令、程序状态寄存器(PSR)处理指令、加载/存储指令、协处理器指令和异常产生指令六大类。除了Load-Store指令外,其他部分和x86指令集是比较类似的。但和x86相比,ARM指令最显著的特点在于它们都是32-bit定长的。另外,由于ARM是基于RISC指令集的,所以微处理器只处理寄存器中的数据,并通过独立的Load-Store指令在内存和寄存器之间进行数据的传递。

在使用方面,ARM指令的格式也要比Intel的复杂一些。一般说来,一条ARM指令有如下的形式:

            <Instruction>{S} [Rd],[Rn],[Rm]

其中:

◆ <Instruction>:指令操作码;

◆ {S}:加上这个后缀的指令会更新CPSR寄存器;

◆ [Rd]:目的寄存器;

◆ [Rn]/[Rm]:源寄存器。

一般来说,ARM指令有3个操作数,其中Rm寄存器在执行指令前可以进入桶形移位器进行移位操作,而Rn则会直接进入ALU单元。如果一条ARM指令只有两个操作数,那么源寄存器按照Rm来处理。例如,一条加法指令:

            add r0,r1,#1;//把r1+1的结果存放到r0中

ARM的寻址方式分为立即寻址、寄存器寻址、寄存器间接寻址、基址加变址寻址、堆栈寻址、复制寻址和相对寻址。

ARM指令系统是RISC指令集,指令系统优先选取使用频率高的指令,以及一些有用但不复杂的指令,指令长度固定,指令格式种类少,寻址方式少,只有存取指令访问存储器,其他的指令都在寄存器之间操作,且大部分指令都在一个时钟周期内完成,以硬布线控制逻辑为主,不用或少用微码控制。ARM更容易实现流水线等操作。同时,ARM采用长乘法指令,增强的DSP指令等指令,使得ARM集合了RISC和CISC的优势。又因为ARM采用了快速中断响应,虚拟存储系统支持,高级语言支持,定义不同的操作模式等,使得ARM的功能更为强大。

在熟悉了基本的汇编格式后,读者就可以自行去查询基本的ARM汇编指令了。

2. 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指令的条数较ARM指令多,相同的工作,ARM可能只用一条语句,而Thumb需要用多条指令。在一般的情况下,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. Thumb-2指令集

ARM指令集的发展如图2-14所示。由图2-14可见,每一代体系结构都会增加新技术。为兼容数据总线宽度为16位的应用系统,ARM体系结构除了支持执行效率很高的32位ARM指令集外,同时支持16位的Thumb指令集,称为Thumb-2指令集。CM3只使用Thumb-2指令集。这是个很大的突破,因为它允许32位指令和16位指令优势互补,代码密度与处理性能兼顾。

图2-14 ARM指令集的发展

Thumb-2是一个突破性的指令集。它强大、易用、高效。Thumb-2是16位Thumb指令集的一个超集,在Thumb-2中,16位指令首次与32位指令并存,结果在Thumb状态下可以做的事情一下子丰富了许多,同样工作需要的指令周期数也明显下降。Thumb-2指令集可以在单一的操作模式下完成所有处理,它使CM3在许多方面都比传统的ARM处理器更先进:没有状态切换的额外开销,节省了执行时间和指令空间;不再需要把源代码文件分成按ARM编译的和按Thumb编译的,软件开发的管理大大减负;无需再反复地求证和测试,究竟该在何时何地切换到何种状态下,程序才最有效率,开发软件容易多了。

说明 CM3并不支持所有的Thumb-2指令,ARMv7-M的说明书只要求实现Thumb-2的一个子集,如图2-15所示。举例来说,协处理器指令就被裁掉了(可以使用外部的数据处理引擎来替代)。CM3也没有实现SIMD指令集。以前的一些Thumb指令不再需要,因此也被排除。不支持指令还包括v6中引入的SETEND指令。Thumb-2指令表详见附录B。

图2-15 Thumb-2指令集与Thumb指令集的关系

对于Thumb-2指令集,建议初学者利用英文还原法记忆指令功能,要求借助表格能读懂基本汇编程序,但不要求编程,因为精力要更多地放到C语言的学习应用中。

2.7 流水线

指令是如何被执行的?微处理器的功能是什么?Tom Shanley在其名著《奔腾4大全》中的第1章给出了这两个问题的答案:微处理器就是一个从内存中读取指令(Fetch),然后解码(Decode)和执行(Execute)的引擎。

计算机中一条指令的执行可分为若干个阶段,由于每个阶段的操作都是相对独立的。因此可以采用流水线的重叠技术来大大提高系统的性能。在流水线装满后,几个指令可以并行执行。这样可充分利用现有硬件资源,提高了微处理器的运行效率。

指令流水线的思想类似于现代化工厂的生产流水线。在工厂的生产流水线上,把生产装配的某个产品的过程,分解成若干个工序;每个工序用同样的时间单位,在各自的工位上完成各自工序的工作。这样,若干个产品可以在不同的工序上同时被装配,每个单位时间都能完成一个产品的装配,生产出一个成品,即单位时间的成品流出率大大提高了。

CM3处理器使用一个三级流水线。流水线的3个级分别是:取指令、解码和执行,如图2-16所示。

图2-16 CM3的三级流水线

当运行的指令大多数都是16位时,可以发现处理器会每隔一个周期做一次取指令操作。这是因为CM3有时可以一次取出两条指令来(一次能取32位),因此在第一条16位指令取出来时,也把第二条16位指令取出来了。此时总线接口就可以先歇一个周期再取指令。或者如果缓冲区是满的,总线接口就空闲下来了。有些指令的执行需要多个周期,在这期间流水线就会暂停。

当遇到分支指令时,译码阶段也包含预测的指令取用,这就提高了执行的速度。处理器在译码阶段期间自行对分支目的地指令进行取指令。在稍后的执行过程中,处理完分支指令后便知道下一条要执行的指令。如果分支不跳转,那么紧跟着的下一条指令随时可供使用。如果分支跳转,那么在跳转的同时分支指令可供使用,空闲时间限制为一个周期。

以上论述了CM3处理器的知识,主要包括以下方面(其他处理器类似):

◆ 处理器的工作模式;

◆ 处理器的寄存器系统;

◆ 处理器一次可以访问的内存大小;

◆ 处理器可访问的地址空间;

◆ 处理器程序、数据存储空间分开编址(独立编址,哈佛结构),还是统一编址(普林斯顿结构);

◆ 处理器I/O端口与存储器分开编址(独立编址),还是统一编址;

◆ 处理器是否具有流水线;

◆ 处理器每次执行的指令长度(1、2、4或8字节)及指令是否是定长的;

◆ 处理器的指令周期大小,以及是否定长;

◆ 处理器的指令集是RISC还是CISC。

2.8 异常和中断

1. 异常和中断的概念

异常通常定义为,当正常的程序执行流程发生暂时的停止并转向相应的处理,包括ARM内核产生复位,取指令或存储器访问失败,遇到未定义指令,执行软件中断指令,或者出现外部中断等。大多数异常都对应一个软件的异常处理程序,也就是在异常发生时执行的软件程序。在处理异常前,当前处理器的状态必须保留,这样当异常处理完成后,当前程序可以继续执行。处理器允许多个异常同时发生,它们将会按固定的优先级进行处理。

在本书中,经常混合使用术语“中断”与“异常”。若不加说明,则强调的都是它们对主程序所体现出来的“中断”性质,与以前学单片机时所讲的概念是相同的。但中断与异常的区别在于,中断对CM3核来说都是“意外突发事件”,即该请求信号来自CM3内核的外面,来自各种片上外设和外扩的外设;而异常则是因CM3内核的活动产生的,即在执行指令或访问存储器时产生。

CM3有15个异常,类型编号为1~15的系统异常见表2-12(注意:没有编号为0的异常);有240个中断源。因为芯片设计者可以修改CM3的硬件描述源代码,所以做成芯片后,支持的中断源数目常常不到240个,并且优先级的位数也由芯片厂商最终决定。

表2-12 CM3异常表

在CM3中,优先级的数值越小,则优先级越高。CM3支持中断嵌套,使得高优先级异常会抢占(preempt)低优先级异常。有3个系统异常:复位、NMI及硬fault,它们有固定的优先级,并且它们的优先级号是负数,从而高于所有其他异常。所有其他异常的优先级则都是可编程的(但不能被编程为负数)。关于CM3优先级的详细内容参见第6章。

在嵌套的向量式中断控制器(NVIC)的中断控制及状态寄存器中,有一个VECTAC-TIVE位段;另外,还有一个特殊功能寄存器IPSR。在它们二者的里面,都记录了当前正在服务的异常,给出了它的编号。

2. 嵌套的向量式中断控制器

嵌套的向量式中断控制器NVIC基本功能为:支持向量中断;可屏蔽中断;支持嵌套中断;中断延迟很短;支持动态优先级调整。动态优先级调整指的是软件可以在运行期间更改中断的优先级。

如果一个发生的异常不能被立即响应,就称它被挂起。如果某中断服务程序中修改了自己所对应中断的优先级,则这个中断可被更高级的中断挂起(pending),详见6.2节。

少数故障异常是不允许被挂起的。一个异常被挂起的原因,可能是系统当前正在执行一个更高优先级异常的服务程序,或者因相关屏蔽位的置位导致该异常被禁止。对于每个异常源,在被挂起的情况下,都会有一个对应的“挂起状态寄存器”保存其异常请求。等到该异常能够响应时,执行其服务程序,这与传统的ARM是完全不同的。传统的ARM中断系统是由产生中断的设备保持住请求信号,CM3则由NVIC的挂起状态寄存器来解决这个问题,即使设备在后来已经释放了请求信号,曾经的中断请求也不会丢失。

CM3处理器使用一个可以重复定位的向量表,表中包含了将要执行的函数的地址,可供具体的中断使用。中断被接受后,处理器通过指令总线接口从向量表中获取地址。向量表复位时指向0,编程控制寄存器可以使向量表重新定位到中断服务程序。

为了提高系统灵活性,当异常发生时,程序计数器、程序状态寄存器、链接寄存器和R0~R3、R12等通用寄存器将被压进堆栈。在数据总线对寄存器压栈的同时,指令总线从程序存储器中取出异常向量,并获取异常代码的第1条指令。一旦压栈和取指完成,中断服务程序或故障处理程序就开始执行,随后寄存器自动恢复,中断了的程序也因此恢复正常的执行。由于在硬件中处理堆栈操作,CM3处理器免去了在传统的C语言中断服务程序中为了完成堆栈处理所要编写的汇编程序,这使应用程序的开发变得更加简单。

NVIC还采用了支持内置睡眠模式的电源管理方案。立即睡眠模式(Sleep Now Mode)被等待中断(WFI)或等待事件(WFE)其中一个指令调用,这些指令可以使内核立即进入低功耗模式,异常被挂起。退出时睡眠模式(Sleep onExit Mode)在系统退出最低优先级的中断服务程序时使其进入低功耗模式。内核保持睡眠状态直到遇上另一个异常。由于只有一个中断可以退出该模式,所以系统状态不会被恢复。如果系统控制寄存器中的SLEEP-DEEP位被置位,那么该位可以用于控制内核及其他系统部件,以获得最理想的节电方案。

NVIC还集成一个递减的24位系统Systick定时器,它定时产生中断,提供理想的时钟来驱动实时操作系统或其他预定服务,详见8.2节。

3. 中断、异常过程

当CM3开始响应一个异常时,基本过程如图2-17所示。

图2-17中断、异常的响应过程

4. 占先

在异常处理程序中,一个新的异常比当前的异常优先级更高,处理器打断当前的流程,响应优先级更高的异常,此时产生中断嵌套,即占先,如图2-18所示。

图2-18 占先

5. 末尾连锁(tail-chaining)

当处理器在响应某中断时,如果又发生其他中断,但其他中断优先级不高,则它们被挂起。在当前的中断执行返回后,系统处理挂起的中断时,传统的方法是先弹栈,然后又把弹出的内容压栈回去,如图2-19所示。此时压栈2与出栈1的内容完全相同,因此CM3不再弹栈这些寄存器,而是继续使用上一个中断已经压栈好的成果,如图2-20所示,看上去好像后一个中断和前一个中断连接起来了,前后只执行了一次入栈/出栈操作,称为末尾连锁。末尾连锁是处理器用于加速中断响应的一种机制。

图2-19 不用末尾连锁的情况

图2-20 末尾连锁

6. 迟来(Late-arriving)

如果前一个中断服务程序还没有进入执行阶段,并且迟来中断的优先级比前一个中断的优先级高,则迟来中断能够抢占前一个中断得到优先服务,这种现象称为迟来。例如,若在响应某低优先级中断1(优先级为3)的早期,检测到了高优先级中断2(优先级为2),就能以“迟来”的方式处理——在入栈完毕后采用末尾连锁技术,执行服务程序2,如图2-21所示。因此可以说迟来是加速占先的一个机制。

图2-21 迟来

7. 返回

在没有挂起中断或没有比被压栈的中断优先级更高的中断时,处理器执行出栈操作,并返回到被压栈的中断服务程序或线程模式,即在响应中断服务程序后,处理器通过出栈操作自动将处理器状态恢复为进入中断服务程序前的状态。如果在状态恢复过程中出现一个新的中断,并且该中断的优先级比正在返回的中断服务程序或线程更高,则处理器放弃状态恢复操作,并将新的中断作为末尾连锁处理。

异常基于优先级而采取4种动作,即占先、末尾连锁、返回和迟来,其区别见表2-13,这4种方式最大的差别在于中断出现的时刻不同。

表2-13 异常基于优先级的动作

中断使用详见第6章。

2.9 STM32微控制器概述

1. STM32系列微控制器命名

STM32系列微控制器命名如图2-22所示。

图2-22 STM32系列微控制器命名

每种STM32的产品都由16个字母或数字构成的编号标示,用户向ST公司订货时必须使用这个编号指定需要的产品。这16个字符分为8个部分。下面通过一个例子说明它们的意义。

【例2-4】 说明STM32 F 103 C 6 T 7×××名字的含义。

第1部分:产品系列名,固定为STM32。

第2部分:产品类型,“F”表示这是Flash产品,目前没有其他选项。

第3部分:产品子系列,“103”表示增强型产品。

第4部分:引脚数目,C=48脚。

第5部分:闪存存储器容量,6=32 KB。

第6部分:封装信息,T=LQFP。

第7部分:工作温度范围,7=工业级,-40~+105℃。

第8部分:可选项;此部分可以没有,可以用于标示内部固件版本号。

本书所用开发板上微控制器型号为STM32 R8 T6,请读者按照上述命名规则说明一下这款微控制器的性能指标。

2. STM32内部资源

STM32系统由以下两部分构成。

◆ 4个驱动单元:CM3内核Dcode总线(D-bus)和系统总线(S-bus)、通用DMA1和通用DMA2。

◆4个被动单元:内部SRAM、内部闪存存储器、FSMC、AHB到APB的桥(AHB2APBx),它连接所有的APB设备。

这些都是通过一个多级的AHB总线构架相互连接的,如图2-23所示。

图2-23 系统结构

Icode总线将CM3内核的指令总线与闪存指令接口相连接。指令预取在此总线上完成。

Dcode总线将CM3内核的Dcode总线与闪存存储器的数据接口相连接(常量加载和调试访问),用于查表等操作。

系统总线连接CM3内核的系统总线(外设总线)到总线矩阵,系统总线用于访问内存和外设,覆盖的区域包括SRAM、片上外设、片外RAM、片外扩展设备,以及系统级存储区的部分空间。

DMA总线将DMA的AHB主控接口与总线矩阵相连,总线矩阵协调着CPU的Dcode和DMA到SRAM、闪存和外设的访问。

总线矩阵协调内核系统总线和DMA主控总线之间的访问仲裁。此仲裁利用轮换算法。此总线矩阵由4个驱动部件(CPU的Dcode、系统总线、DMA1总线和DMA2总线)和4个被动部件(闪存存储器接口(FLITF)、SRAM、FSMC和AHB2 APB桥)构成。AHB外设通过总线矩阵与系统总线相连,允许DMA访问。

AHB/APB桥(APB)在AHB和两个APB总线间提供同步连接。APB1操作速度限于36 MHz,APB2操作于全速(最高72 MHz)。当对APB寄存器进行8位或16位访问时,该访问会被自动转换成32位的访问;桥会自动将8位或32位的数据扩展以配合32位的向量。

STM32 F103系列微控制器模块框图如图2-24所示,其内部资源见表2-14。

表2-14 STM32 F103系列微控制器内部资源

图2-24 STM32F103模块框图

本书以STM32F103系列芯片中的STM32R8 T6为例进行讲解,其引脚如图2-25所示,其封装如图2-26所示。

图2-25 引脚图

图2-26 封装图

STM32F103 R8 T6内部资源概述如下所述。

(1)内核:ARM 32位的CM3微处理器;72MHz,1.25DMIPS/MHz(Dhrystone2.1),0等待周期的存储器;单周期乘法和硬件除法。

(2)存储器:128 KB的闪存程序存储器;20 KB的SRAM。

(3)时钟、复位和电源管理:2.0~3.6 V供电和I/O引脚;上电/断电复位(POR/PDR)、可编程电压监测器(PVD);内嵌4~16MHz高速晶体振荡器;内嵌经出厂调校的8MHz的RC振荡器;内嵌40kHz的RC振荡器;PLL供应CPU时钟;带校准功能的32kHz RTC振荡器。

(4)低功耗:VBAT(电池引脚)为RTC和后备寄存器供电;工作模式见表2-15。

表2-15 工作模式

(5)5组I/O口:5组多功能双向5V兼容的I/O口;所有I/O口可以映像到外部中断。

(6)DMA控制器:支持定时器、ADC、SPI、I2 C和USART等外设。

(7)两个12位模数转换器:1 μs转换时间(16通道);转换范围0~3.6 V;双采样和保持功能;温度传感器。

(8)多达9个通信接口:多达3个USART接口,支持ISO7816,LIN,IrDA接口和调制解调控制;多达两个I2 C接口(SMBus/PMBus);多达两个SPI同步串行接口(18Mb/s);1个N接口(2.0 B主动);1个USB 2.0全速接口。

(9)两个高级控制定时器,4个通用定时器,两个基本定时器,一个实时时钟、两个看门狗定时器和一个系统嘀嗒定时器(SysTick时钟)。

(10)调试模式:串行线调试(SWD)和JTAG接口。

以下各章分别对STM32微控制器内部资源的原理及应用进行详述。