你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【安富莱STM32F407之uCOS-III教程】第4章 Cortex-M处理器的OS特性

[复制链接]
baiyongbin2009 发布时间:2014-12-18 20:17
本帖最后由 baiyongbin2009 于 2014-12-18 20:48 编辑

特别说明:
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.1.jpg
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异常。

收藏 评论6 发布时间:2014-12-18 20:17

举报

6个回答
baiyongbin2009 回答时间:2014-12-18 20:27:47
本帖最后由 baiyongbin2009 于 2014-12-18 20:30 编辑

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”。下图是三个子状态分别使用的那几位。
4.2.png


这里特别注意GE(这个是CM4新加的,CM3没有)和中断号PSR(IPSR),后面将SVC异常的时候要用到。
l  中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI)
这三个寄存器非常重要,初学者一定要了解,能记住最好。这里建立一个简单的表格,方便大家查看。
4.3.png

对于时间-关键任务而言,恰如其分地使用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   RIMASK=1,     ;关中断
CPSIE  I   RIMASK=0,     ;开中断
CPSID  F   ;FAULTMASK=1,    ;关异常
CPSIE  F   ;FAULTMASK=0     ;开异常
l  控制寄存器(CONTROL)
控制寄存器有两个用途,其一用于定义特权级别,其二用于选择当前使用哪个堆栈指针。由两个比特来行使这两个职能。这个寄存器也十分的重要,需认真学习。
4.4.png
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位段详解。
4.5.png


总结一下上面的表,可以得出,合法的EXC_RETURN值共3个
4.6.png

    如果主程序在线程模式下运行,并且在使用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双精度)进行访问。
4.7.png

baiyongbin2009 回答时间:2014-12-18 20:33:14
4.3  操作模式和特权等级
        Cortex-M3/M4支持两个模式(handler模式,也就是中断模式和线程模式)和两个特权等级(特权级和非特权级,这里将其称为用户级):

特权级
用户级
异常handler的代码
Handler
错误的模式
主应用程序的代码
线程模式
线程模式
      当处理器处在线程状态下时,既可以使用特权级,也可以使用用户级;另一方面,handler模式总是特权级的。在复位后,处理器进入线程模式+特权级。
        在特权级下的代码可以通过置位CONTROL[0]来进入用户级。而不管是任何原因产生了任何异常,处理器都将以特权级来运行其服务例程,异常返回后,系统将回到产生异常时所处的级别。用户级下的代码不能再试图修改CONTROL[0]来回到特权级。它必须通过一个异常handler,由那个异常handler来修改CONTROL[0],才能在返回到线程模式后拿到特权级。下图是特权级线程模式和用户级线程模式的切换图:
4.8.png
                              
      一些简单的应用程序是不需要用户级线程模式的,如下图所示:
4.9.png
      把代码按特权级和用户级分开对待(当前流行的RTOS中,RTX支持此功能,用户可根据自己的需要进行配置),有利于使CM3/CM4的架构更加安全和健壮。例如,当某个用户程序代码出问题时,不会让它成为害群之马,因为用户级的代码是禁止写特殊功能寄存器和NVIC中断寄存器的。另外,如果还配有MPU,保护力度就更大,甚至可以阻止用户代码访问不属于它的内存区域。
      为了避免系统堆栈因应用程序的错误使用而毁坏,我们可以给应用程序专门配一个堆栈,不让它共享操作系统内核的堆栈。在这个管理制度下,运行在线程模式的用户代码使用PSP,而异常服务例程则使用MSP。这两个堆栈指针的切换是智能全自动的,就在异常服务的始末由硬件处理。
        如前所述,特权等级和堆栈指针的选择均由CONTROL负责。当CONTROL[0]=0时,在异常处理的始末,只发生了处理器模式的转换,如下图所示。
4.10.png

       但若CONTROL[0]=1(线程模式+用户级),则在中断响应的始末,both 处理器模式和特权等极都要发生变化,如下图所示。
4.11.png

      CONTROL[0]只有在特权级下才能访问。用户级的程序如想进入特权级,通常都是使用一条“系统服务呼叫指令(SVC)”来触发“SVC异常”,该异常的服务例程可以视具体情况而修改CONTROL[0]。
        关于操作模式和特权级就跟大家说这么多,在讲解RTOS时,再跟大家详细讲解。有了上面的这些知识我们就可以讲解双堆栈机制了。

baiyongbin2009 回答时间:2014-12-18 20:37:52
本帖最后由 baiyongbin2009 于 2014-12-18 20:43 编辑

4.4  双堆栈机制
     讲解双堆栈机制之前,大家要先对CM3/CM4对堆栈的处理方式有一个基本的了解。在CM3/CM4中,除了可以使用PUSH和POP指令来处理堆栈外,内核还会在异常处理的始末自动地执行PUSH与POP操作。
4.4.1      堆栈的基本操作
    笼统地讲,堆栈是一种存储器的使用模型。它由一块连续的内存和一个栈顶指针组成,用于实现“后进先出”的缓冲区(堆栈是LIFO的模式,和咱们前面讲的按键FIFO和串口FIFO不一样,别搞混了)。其最典型的应用,就是在数据处理前先保存寄存器的值,再在处理任务完成后从中恢复先前保护的这些值。堆栈操作就是对内存的读写操作,但是访问地址由SP给出。寄存器的数据通过PUSH操作存入堆栈,以后用POP操作从堆栈中取回。    在PUSH与POP的操作中,SP的值会按堆栈的使用法则自动调整,以保证后续的PUSH不会破坏先前PUSH进去的内容。下面的图片摘自Cortex-M4权威指南,看起来更形象。简单演示了PUSH操作和POP操作过程,堆栈地址由下往上表示从低地址到高地址,也就是所谓的“向下生长的满栈”模型。
    Cortex-M3/M4使用的就是“向下生长的满栈”模型(这个要谨记,后面讲RTOS任务切换的时候要用到)。堆栈指针SP指向最后一个被压入堆栈的32位数值。在下一次压栈时,SP先自减4,再存入新的数值。
4.12.png
    堆栈的功能就是把寄存器的数据临时备份在内存中,以便将来能恢复之——在一个任务或一段子程序执行完毕后恢复。正常情况下,PUSH与POP必须成对使用,而且参与的寄存器,不论是身份还是先后顺序都必须完全一致。当PUSH/POP指令执行时,SP指针的值也根着自减/自增。
主程序                            子程序
; R0=X, R1=Y,R2=Z
BL   Fx1 ------------------------------->
                                                    Fx1
                                                         PUSH  {R0 } ;把R0存入栈 & 调整SP
                                                         PUSH  {R1}  ;把R1存入栈 & 调整SP
                                                         PUSH  {R2}  ;把R2存入栈 & 调整SP
                                                         …        ;执行Fx1的功能,中途可以改变R0-R2的值
                                                         POP   {R2} ;恢复R2早先的值 & 再次调整SP
                                                         POP   {R1} ;恢复R1早先的值 & 再次调整SP
                                                         POP   {R0} ;恢复R0早先的值 & 再次调整SP
                                                         BX   LR   ;返回
<-------------------------------------------
;回到主程序
;R0=X, R1=Y,R2=Z (调用Fx1的前后R0-R2的值完好无损,从寄存器上下文来看,就好像什么都没发生过一样)。
PUSH/POP指令支持一次操作多个寄存器。在前面1.2.2小节有说明。

baiyongbin2009 回答时间:2014-12-18 20:44:43
4.4.2      堆栈空间的分配
    为了让大家对堆栈有一个深入的了解,这里以STM32-V5开发板提高篇的第一个例子F4-001_按键检测和LED控制例程(V1.1)跟大家讲解下。在启动文件中定义了堆栈的大小:
Stack_Size      EQU    0x00004000
Heap_Size      EQU    0x00000400
    在main函数中添加malloc函数的调用,要不无法看到heap的使用情况。
uint32_t *p;
p = (uint32_t*)malloc(sizeof(uint32_t)*100);  //申请400个字节
    首先用MDK全编译工程,打开.map文件:
4.13.png
    工程文件生成的map文件信息量很大,涉及到的知识也很多,这里就先说下我们关系的堆栈空间的分配,看之前简易初学者再看看第6章STM32F4XX启动过程详解。
Image Symbol Table

Local Symbols
Symbol Name                              Value       OvType        Size  Object(Section)
HEAP                                   0x200021c0   Section        1024 startup_stm32f4xx.o(HEAP)
STACK                                  0x200025c0   Section       16384  startup_stm32f4xx.o(STACK)

Global Symbols
Symbol Name                              Value       OvType        Size  Object(Section)
__heap_base                              0x200021c0   Data           0 startup_stm32f4xx.o(HEAP)
__heap_limit                             0x200025c0   Data           0 startup_stm32f4xx.o(HEAP)
__initial_sp                             0x200065c0   Data           0 startup_stm32f4xx.o(STACK)

Memory Map of the image

   Execution Region RW_IRAM1 (Base: 0x20000000,Size: 0x000065c0, Max: 0x00020000, ABSOLUTE)

    Base Addr   Size         Type   Attr     Idx    E Section Name        Object

    0x20000000  0x0000000d   Data   RW         322    .data               bsp_timer.o
    0x2000000d  0x00000001   Data   RW         423    .data               bsp_uart_fifo.o
    0x2000000e  0x00000010   Data   RW         694    .data               stm32f4xx_rcc.o
    0x2000001e   0x00000002  PAD
    0x20000020  0x00000014   Data   RW        1293    .data               system_stm32f4xx.o
    0x20000034  0x00000004   Data   RW        1621    .data               mc_w.l(stdout.o)
    0x20000038  0x00000004   Data   RW        1630    .data               mc_w.l(mvars.o)
    0x2000003c  0x00000004   Data   RW        1631    .data               mc_w.l(mvars.o)
    0x20000040  0x000000ad   Zero   RW         174    .bss                bsp_key.o
    0x200000ed  0x00000003   PAD
   0x200000f0   0x00000030   Zero  RW          320    .bss                bsp_timer.o
    0x20000120  0x000020a0   Zero   RW         422    .bss                bsp_uart_fifo.o
    0x200021c0  0x00000400   Zero   RW         643    HEAP                startup_stm32f4xx.o
    0x200025c0  0x00004000   Zero   RW         642    STACK               startup_stm32f4xx.o
    上面的这些信息还不够直接,这里借助Cortex-M3权威指南中的一个截图进行说明。
4.14.png
    先解释下这个截图的含义:在离开复位状态后,CM3做的第一件事就是读取下列两个32位整数的值
  l  从地址0x0000,0000处取出MSP的初始值(也就是上面的0x20008000)。
  l  从地址0x0000,0004处取出PC的初始值——这个值是复位向量,LSB必须是1。然后从这个值所对应的地址处取指。
    请注意,这与传统的ARM架构不同——其实也和绝大多数的其它单片机不同。传统的ARM架构总是从0地址开始执行第一条指令。它们的0地址处总是一条跳转指令。在CM3/CM4中,在0地址处提供MSP的初始值,然后紧跟着就是向量表(向量表在以后还可以被移至其它位置) 。向量表中的数值是32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令。
    因为CM3/CM4使用的是向下生长的满栈,所以MSP的初始值必须是堆栈内存的末地址加1。举例来说,如果你的堆栈区域在0x20007C00-0x20007FFF之间,那么MSP的初始值就必须是0x20008000。
    向量表跟随在MSP的初始值之后——也就是第2个表目。要注意因为CM3/CM4是在Thumb态下执行,所以向量表中的每个数值都必须把LSB置1(也就是奇数)。正是因为这个原因,上图中使用0x101来表达地址0x100。当0x100处的指令得到执行后,就正式开始了程序的执行。在此之前初始化MSP是必需的,因为可能第1条指令还没来得及执行,就发生了NMI或是其它fault。MSP初始化好后就已经为它们的服务例程准备好了堆栈。
    对于不同的开发工具,需要使用不同的格式来设置MSP初值和复位向量。有些则由开发工具自行计算并生成。现在接着上面的.map文件进行说明,这个文件里面有一项是:
__initial_sp                             0x200065c0
    其实__initial_sp就是MSP的初始值,下面这个是Debug状态下的截图,MSP初始值和__initial_sp是一样的,也就是MDK帮我们计算好了MSP初始值。
4.15.png
     这里就跟大家讲这么多,后面的教程中继续的完善。


baiyongbin2009 回答时间:2014-12-18 20:45:57
4.5  内核相关的API函数
    要访问上面提到的寄存器,可以通过ARM官方提供的CMSIS软件包,里面有个文件core_cmdFunc.h,相关API函数就在里面,提供了三个版本的函数支持:MDK,IAR,GCC。这里将MDK版本的函数摘录出来,方便大家查看。
  1. /** \brief  Get Control Register

  2.     This function returns the content of the Control Register.

  3.     \return               Control Register value
  4. */
  5. __STATIC_INLINE uint32_t __get_CONTROL(void)
  6. {
  7.   register uint32_t __regControl         __ASM("control");
  8.   return(__regControl);
  9. }


  10. /** \brief  Set Control Register

  11.     This function writes the given value to the Control Register.

  12.     \param [in]    control  Control Register value to set
  13. */
  14. __STATIC_INLINE void __set_CONTROL(uint32_t control)
  15. {
  16.   register uint32_t __regControl         __ASM("control");
  17.   __regControl = control;
  18. }


  19. /** \brief  Get IPSR Register

  20.     This function returns the content of the IPSR Register.

  21.     \return               IPSR Register value
  22. */
  23. __STATIC_INLINE uint32_t __get_IPSR(void)
  24. {
  25.   register uint32_t __regIPSR          __ASM("ipsr");
  26.   return(__regIPSR);
  27. }


  28. /** \brief  Get APSR Register

  29.     This function returns the content of the APSR Register.

  30.     \return               APSR Register value
  31. */
  32. __STATIC_INLINE uint32_t __get_APSR(void)
  33. {
  34.   register uint32_t __regAPSR          __ASM("apsr");
  35.   return(__regAPSR);
  36. }


  37. /** \brief  Get xPSR Register

  38.     This function returns the content of the xPSR Register.

  39.     \return               xPSR Register value
  40. */
  41. __STATIC_INLINE uint32_t __get_xPSR(void)
  42. {
  43.   register uint32_t __regXPSR          __ASM("xpsr");
  44.   return(__regXPSR);
  45. }


  46. /** \brief  Get Process Stack Pointer

  47.     This function returns the current value of the Process Stack Pointer (PSP).

  48.     \return               PSP Register value
  49. */
  50. __STATIC_INLINE uint32_t __get_PSP(void)
  51. {
  52.   register uint32_t __regProcessStackPointer  __ASM("psp");
  53.   return(__regProcessStackPointer);
  54. }


  55. /** \brief  Set Process Stack Pointer

  56.     This function assigns the given value to the Process Stack Pointer (PSP).

  57.     \param [in]    topOfProcStack  Process Stack Pointer value to set
  58. */
  59. __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack)
  60. {
  61.   register uint32_t __regProcessStackPointer  __ASM("psp");
  62.   __regProcessStackPointer = topOfProcStack;
  63. }


  64. /** \brief  Get Main Stack Pointer

  65.     This function returns the current value of the Main Stack Pointer (MSP).

  66.     \return               MSP Register value
  67. */
  68. __STATIC_INLINE uint32_t __get_MSP(void)
  69. {
  70.   register uint32_t __regMainStackPointer     __ASM("msp");
  71.   return(__regMainStackPointer);
  72. }


  73. /** \brief  Set Main Stack Pointer

  74.     This function assigns the given value to the Main Stack Pointer (MSP).

  75.     \param [in]    topOfMainStack  Main Stack Pointer value to set
  76. */
  77. __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack)
  78. {
  79.   register uint32_t __regMainStackPointer     __ASM("msp");
  80.   __regMainStackPointer = topOfMainStack;
  81. }


  82. /** \brief  Get Priority Mask

  83.     This function returns the current state of the priority mask bit from the Priority Mask Register.

  84.     \return               Priority Mask value
  85. */
  86. __STATIC_INLINE uint32_t __get_PRIMASK(void)
  87. {
  88.   register uint32_t __regPriMask         __ASM("primask");
  89.   return(__regPriMask);
  90. }


  91. /** \brief  Set Priority Mask

  92.     This function assigns the given value to the Priority Mask Register.

  93.     \param [in]    priMask  Priority Mask
  94. */
  95. __STATIC_INLINE void __set_PRIMASK(uint32_t priMask)
  96. {
  97.   register uint32_t __regPriMask         __ASM("primask");
  98.   __regPriMask = (priMask);
  99. }


  100. #if       (__CORTEX_M >= 0x03)

  101. /** \brief  Enable FIQ

  102.     This function enables FIQ interrupts by clearing the F-bit in the CPSR.
  103.     Can only be executed in Privileged modes.
  104. */
  105. #define __enable_fault_irq                __enable_fiq


  106. /** \brief  Disable FIQ

  107.     This function disables FIQ interrupts by setting the F-bit in the CPSR.
  108.     Can only be executed in Privileged modes.
  109. */
  110. #define __disable_fault_irq               __disable_fiq


  111. /** \brief  Get Base Priority

  112.     This function returns the current value of the Base Priority register.

  113.     \return               Base Priority register value
  114. */
  115. __STATIC_INLINE uint32_t  __get_BASEPRI(void)
  116. {
  117.   register uint32_t __regBasePri         __ASM("basepri");
  118.   return(__regBasePri);
  119. }


  120. /** \brief  Set Base Priority

  121.     This function assigns the given value to the Base Priority register.

  122.     \param [in]    basePri  Base Priority value to set
  123. */
  124. __STATIC_INLINE void __set_BASEPRI(uint32_t basePri)
  125. {
  126.   register uint32_t __regBasePri         __ASM("basepri");
  127.   __regBasePri = (basePri & 0xff);
  128. }


  129. /** \brief  Get Fault Mask

  130.     This function returns the current value of the Fault Mask register.

  131.     \return               Fault Mask register value
  132. */
  133. __STATIC_INLINE uint32_t __get_FAULTMASK(void)
  134. {
  135.   register uint32_t __regFaultMask       __ASM("faultmask");
  136.   return(__regFaultMask);
  137. }


  138. /** \brief  Set Fault Mask

  139.     This function assigns the given value to the Fault Mask register.

  140.     \param [in]    faultMask  Fault Mask value to set
  141. */
  142. __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask)
  143. {
  144.   register uint32_t __regFaultMask       __ASM("faultmask");
  145.   __regFaultMask = (faultMask & (uint32_t)1);
  146. }

  147. #endif /* (__CORTEX_M >= 0x03) */


  148. #if       (__CORTEX_M == 0x04)

  149. /** \brief  Get FPSCR

  150.     This function returns the current value of the Floating Point Status/Control register.

  151.     \return               Floating Point Status/Control register value
  152. */
  153. __STATIC_INLINE uint32_t __get_FPSCR(void)
  154. {
  155. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
  156.   register uint32_t __regfpscr         __ASM("fpscr");
  157.   return(__regfpscr);
  158. #else
  159.    return(0);
  160. #endif
  161. }


  162. /** \brief  Set FPSCR

  163.     This function assigns the given value to the Floating Point Status/Control register.

  164.     \param [in]    fpscr  Floating Point Status/Control value to set
  165. */
  166. __STATIC_INLINE void __set_FPSCR(uint32_t fpscr)
  167. {
  168. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
  169.   register uint32_t __regfpscr         __ASM("fpscr");
  170.   __regfpscr = (fpscr);
  171. #endif
  172. }
复制代码


baiyongbin2009 回答时间:2014-12-18 20:47:01
4.6  总结
    对于初学者一下子掌握这么多的东西比较的困难,这里总结一下主要的几点,一般应用记住这几点即可,需要的时候再查阅这个知识点相关的内容:
  l  系统复位后默认使用的是MSP,复位后的状态是特权级线程状态,在这个状态下是允许修改寄存器的。进入到用户特权以后就不能修改这些寄存器了。
  l  用户特权的情况(也就是用户建立的非中断服务程序)下可以使用MSP或PSP,特权模式(中断服务程序)只能使用MSP。
  l  还有很重要的一条就是。假如在用户模式下使用的是PSP,那么寄存器的数值被保存到任务堆栈的空间,
进入中断程序后就开始使用MSP,如果还有一个高优先级的中断难么就继续的使用MSP,在程序推出最后一级中断的时候就用用户堆栈恢复寄存器。
  l  Cortex-M4的堆栈模型是向下生长的满栈。
    下期教程会做两个相关的例子帮大家深入的理解CM4内核方面的知识。



参考资料:
1.    Patterns fortime-triggered embedded systems英文版和中文版
2.    Cortex-M3权威指南中文版
3.    TheDefinitive Guide to Arm Cortex-M3 and Cortex-M4 Processors(M4权威指南)

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版