本帖最后由 Eric2013 于 2014-12-17 15:50 编辑
特别说明:
1. 本教程是安富莱电子原创。
2. 安富莱STM32F407开发板资料已经全部开源,开源地址:地址链接
3. 当前共配套300多个实例,4套用户手册。
第4章 Cortex-M处理器的OS特性
本期教程带领大家学习Cortex-M处理器的OS特性,主要是M3和M4,M4和M3反应在RTOS上,主要区别是M4多了一个浮点单元,用户可以根据需要选择是否使用浮点单元。本期教程主要学CM内核的操作模式,特权等级和双堆栈机制。这部分知识学习起来比较的枯燥,但是作为初学者一定要将本期教程认真的多看几遍,有兴趣继续学的,还需上ARM的官网多找些相关的资料进行学习,可以这么说,这部分知识直接关系着你对一款RTOS的认识程度。 4.1 内核的OS特性 4.2寄存器组 4.3操作模式和特权等级 4.4双堆栈机制 4.5内核相关的API函数 4.6总结 4.1 内核的OS特性 为了更好的支持RTOS的运行,Cortex-M系列处理器在这方面做出了很多的努力。当前至少有30多款RTOS支持Cortex-M系列处理器,这个数量还在增加,下面总结了几条ARM公司为了使Cortex-M系列处理器更好的支持RTOS运行所做的努力: l 双堆栈指针:MSP指针用于OS内核和中断,PSP指针用于任务。 l SysTick定时器:可以很方便的用于OS的时钟节拍,嘀嗒定时器在第10章:SysTick实验有详细的讲解。 l SVC和PendSV异常:实现RTOS,这两个异常是必须的,特别是任务切换的实现。 l 非特权级执行模式:程序运行在非特权级模式可以限制应用任务的访问权限,特权级模式和非特权级模式配合MPU(Memory Protection Unit 内存保护单元)可以有效的提高系统的鲁棒性。 l 独占式访问:独占式的存储和加载指令对于RTOS中的信号量和互斥操作的实现非常有用。 l 另外,低的中断延迟(可以降低任务切换的开销)和指令的各种特性使得RTOS可以有效的工作。 上面的这些特性对于提高RTOS的性能十分重要,当然,也不是每个RTOS都会用全这些特性,后面会跟大家具体的讲述,本期教程先把部分特性详细的讲解下。 4.2 寄存器组 Cortex-M4/M3处理器拥有R0-R15的寄存器组。其中R13作为堆栈指针SP。SP有两个,但在同一时刻只能有一个可以看到,这也就是所谓的“banked”寄存器。
4.2.1 通用寄存器 寄存器从R0到R12是通用寄存器。R0-R7被称作lowregisters,由于指令集有限的可用空间,很多的16位指令只能访问这些寄存器。R8-R12被称作high registers,这些寄存器可用于32位指令和部分的16位指令,比如MOV指令。R0-R12的初始值是不定的。 4.2.2 堆栈指针寄存器 Cortex-M3/M4拥有两个堆栈指针,然而它们是banked,因此任一时刻只能使用其中的一个,指针的切换通过特殊寄存器CONTROL实现。当引用R13(或写作SP)时,引用到的是当前正在使用的那一个,另一个必须用特殊的指令来访问(MRS,MSR指令)。这两个堆栈指针分别是: l 主堆栈指针(MSP):这是缺省的堆栈指针,它由OS内核、异常服务例程以及所有需要特权访问的应用程序代码来使用。 l 进程堆栈指针(PSP):用于常规的应用程序代码(不处于异常服用例程中时)。 说起堆栈指针就必须得说一下堆栈(在前面3.1.2小节有简单讲解和举例,后面会详细展开讨论),堆栈指针用于访问堆栈,并且PUSH指令和POP指令默认使用SP。在Cortex-M3/M4中,有专门的指令负责堆栈操作——PUSH和POP。它俩的汇编语言语法如下例所演示: PUSH {R0} ; *(--R13)=R0。R13是long*的指针 POP {R0} ; R0= *R13++ 请注意后面程序的注释,它诠释了所谓的“向下生长的满栈”(在本期教程后面讲到任务切换时还要展开论述),Cortex-M3/M4就是以这种方式使用堆栈的。因此,在PUSH新数据时,堆栈指针先减一个单元。通常在进入一个子程序后,第一件事就是把寄存器的值先PUSH入堆栈中,在子程序退出前再POP曾经PUSH的那些寄存器。另外,PUSH和POP还能一次操作多个寄存器,如下所示: subroutine_1 PUSH {R0-R7, R12,R14} ; 保存寄存器列表 … ; 执行处理 POP {R0-R7, R12, R14} ; 恢复寄存器列表 BX R14 ; 返回到主调函数 在程序中为了突出重点,可以一直把R13写作SP。在程序代码中,both MSP和PSP都被称为R13/SP。不过,我们可以通过MRS/MSR指令来指名道姓地访问具体的堆栈指针。MSP,亦 写作SP_main,这是复位后缺省使用堆栈指针,服务于操作系统内核和异常服务例程;而PSP,亦写作SP_process,典型地用于普通的用户线程中。 寄存器的PUSH 和POP 操作永远都是4 字节对齐的——也就是说他们的地址必须是0x4,0x8,0xc,……。事实上,R13的最低两位被硬线连接到0,并且总是读出0(Read AsZero)。 这里有两点大家要特别注意: Ø 大多数情况下的应用,只需使用指针MSP,而PSP多用于RTOS中。 Ø 堆栈指针的最低两位永远是0,这意味着堆栈总是4字节对齐的。 4.2.3 连接寄存器 R14也称作Link Register (LR),当调用子程序或者函数时,这个函数用于保存返回地址。函数或者子程序结束时,程序将LR中的地址赋给PC返回到调用函数中。当调用子程序或者函数时,LR中的数值是自动更新的,如果此函数或者子程序嵌套调用其它函数或者子程序,需要保存LR中的数值到堆栈中,要不LR中的数值会由于函数调用而丢失。 在中断服务程序中,LR中的数值自动更新给EXC_RETURN((Exception Return),然后在中断服务程序的末尾触发异常返回。 尽管Cortex-M处理器的返回地址是偶数(bit 0 = 0,因为指令需要半字对齐),LR的BIT 0是可读可写的,一些分支和调用操作需要LR的bit 0置一来表示Thumb状态(这是历史遗留的产物,CM3/CM4只需此位可读写)。 4.2.4 程序计数器 R15是程序计数器,在汇编代码中一般我们都都叫它的外号“PC”。 因为 CM3/CM4内部使用了指令流水线,读PC时返回的值是当前指令的地址+4。比如说: 0x1000: MOV R0, PC ; R0 = 0x1004 如果向PC中写数据,就会引起一次程序的分支(但是不更新LR寄存器)。CM3/CM4中的指令至少是半字对齐的,所以PC的LSB总是读回0。然而,在分支时,无论是直接写PC的值还是使用分支指令,都必须保证加载到PC的数值是奇数(即LSB=1),用以表明这是在Thumb状态下执行。倘若写了0,则视为企图转入ARM模式,CM3将产生一个fault异常。 4.2.5 特殊功能寄存器 Cortex-M3/M4中的特殊功能寄存器包括: l 程序状态寄存器组(PSRs或曰xPSR) l 中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI) l 控制寄存器(CONTROL) 它们只能被专用的MSR/MRS指令访问,而且它们也没有与之相关联的访问地址。 MRS <gp_reg>, <special_reg> ;读特殊功能寄存器的值到通用寄存器。 MSR <special_reg>, <gp_reg> ;写通用寄存器的值到特殊功能寄存器。 这三组特殊功能寄存器都比较重要,初学者要有一定的了解。 l 程序状态寄存器组(PSRs或曰xPSR) 程序状态寄存器在其内部又被分为三个子状态寄存器: Ø 应用程序PSR(APSR) Ø 中断号PSR(IPSR) Ø 执行PSR(EPSR) 通过MRS/MSR指令,这3个PSRs即可以单独访问,也可以组合访问(2个组合,3个组合都可以)。当使用三合一的方式访问时,应使用名字“xPSR”或者“PSR”。下图是三个子状态分别使用的那几位。
这里特别注意GE(这个是CM4新加的,CM3没有)和中断号PSR(IPSR),后面将SVC异常的时候要用到。 l 中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI) 这三个寄存器非常重要,初学者一定要了解,能记住最好。这里建立一个简单的表格,方便大家查看。 对于时间-关键任务而言,恰如其分地使用PRIMASK和BASEPRI来暂时关闭一些中断是非常重要的(FreeRTOS中这个两个寄存器都有用到,μCOS-II和μCOS-III中只用到了PRIMASK)。 而FAULTMASK(这个寄存器在当前比较流行的RTOS中还没有用到过)则可以被OS用于暂时关闭fault处理机能,这种处理在某个任务崩溃时可能需要。因为在任务崩溃时,常常伴随着一大堆faults。在系统料理“后事”时,通常不再需要响应这些fault,总之FAULTMASK就是专门留给OS用的。 要访问PRIMASK, FAULTMASK以及BASEPRI,同样要使用MRS/MSR指令,如: MRS R0, BASEPRI ;读取BASEPRI到R0中 MRS R0, FAULTMASK ;同上 MRS R0, PRIMASK ;同上 MSR BASEPRI, R0 ;写入R0到BASEPRI中 MSR FAULTMASK, R0 ;同上 MSR PRIMASK, R0 ;同上 只有在特权级下,才允许访问这3个寄存器。 其实,为了快速地开关中断,CM3还专门设置了一条CPS指令,有4种用法(在RTOS中用的比较多) CPSID I ;PRIMASK=1, ;关中断 CPSIE I ;PRIMASK=0, ;开中断 CPSID F ;FAULTMASK=1, ;关异常 CPSIE F ;FAULTMASK=0 ;开异常 l 控制寄存器(CONTROL) 控制寄存器有两个用途,其一用于定义特权级别,其二用于选择当前使用哪个堆栈指针。由两个比特来行使这两个职能。这个寄存器也十分的重要,需认真学习。 CONTROL[1] 在Cortex-M3/M4的handler模式中,CONTROL[1]总是0。在线程模式中则可以为0或1。因此,仅当处于特权级的线程模式下,此位才可写,其它场合下禁止写此位。改变处理器的模式也有其它的方式:在异常返回时,通过修改LR的位2,也能实现模式切换。这是LR在异常返回时的特殊用法,颠覆了对LR的传统使用方式。 前面已经讲到,在进入异常服务程序后,将自动更新LR的值为特殊的EXC_RETURN。EXC_RETURN中高28位全为1的值,只有[3:0]的值有特殊含义,当异常服务例程把这个值送往PC时,就会启动处理器的中断返回序列。因为LR的值是由CM3/CM4自动设置的,所以只要没有特殊需求,就不要改动它。 下面是EXC_RETURN位段详解。 总结一下上面的表,可以得出,合法的EXC_RETURN值共3个
如果主程序在线程模式下运行,并且在使用MSP时被中断,则在服务例程中LR=0xFFFF_FFF9(主程序被打断前的LR已被自动入栈)。 如果主程序在线程模式下运行,并且在使用PSP时被中断,则在服务例程中LR=0xFFFF_FFFD(主程序被打断前的LR已被自动入栈)。 CONTROL[0] 仅当在特权级下操作时才允许写该位。一旦进入了用户级,唯一返回特权级的途径,就是触发一个(软)中断,再由服务例程改写该位。 CONTROL寄存器也是通过MRS和MSR指令来操作的: MRS R0, CONTROL MSR CONTROL, R0 4.2.6 浮点寄存器
Cortex-M4处理器提供了可选的浮点单元用于浮点数据处理,浮点单元还包含一个状态控制寄存器FPSCR,每个32位浮点寄存器S0-S31(S代表single precision单精度)可以通过浮点指令进行访问。也可以每两个为一对,也就是D0-D15(D代表double-precision双精度)进行访问。
|