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

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

[复制链接]
baiyongbin2009 发布时间:2014-12-18 20:17
本帖最后由 baiyongbin2009 于 2014-12-18 20:48 编辑 . [) p/ x- u+ U& [/ b& J
. G1 s: U: X* n& H: J6 [
特别说明:
3 S- n1 X) p& F/ ^- z, }1.  本教程是安富莱电子原创。
1 ^6 L& P8 a7 A2 f  o/ q2.  安富莱STM32F407开发板资料已经全部开源,开源地址:地址链接
) J5 W. f6 I, ]; N# s2 C2 y3.  当前共配套300多个实例,4套用户手册。
8 E# h3 j' E# w$ [, ^
第4章  Cortex-M处理器的OS特性
# f0 Y& q7 h6 Q* T* [  ~" @- w$ g
    本期教程带领大家学习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的初始值是不定的。

% W4 {" e# O8 F5 M9 m9 b3 j* J4.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字节对齐的。

7 |  C& o8 H4 J( \& y( Y4.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只需此位可读写)。
+ d% n, s/ N0 ]. H0 y( s% V, R
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异常。

8 D% R6 T1 a( ]0 f7 r
赞 收藏 评论6 发布时间:2014-12-18 20:17

举报

6个回答
baiyongbin2009 回答时间:2014-12-18 20:27:47
本帖最后由 baiyongbin2009 于 2014-12-18 20:30 编辑
) Q. [2 k$ W) i8 p. k+ m3 G& t3 i9 J) h: l  n$ E  x5 X$ X$ J7 `( D' l
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
1 ?2 ], j, [$ [: B- i  ~5 V& a8 [' n

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

1 T/ c+ d0 H' ~! c4 D/ n9 V2 `
对于时间-关键任务而言,恰如其分地使用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

2 K$ A# V8 E2 Y& c
0 |+ N1 ]5 @0 x9 F5 M
总结一下上面的表,可以得出,合法的EXC_RETURN值共3个
4.6.png
7 C" a7 |% j: c
    如果主程序在线程模式下运行,并且在使用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

7 t7 ^! v. U& \% G6 m% ^$ H  C2 H
baiyongbin2009 回答时间:2014-12-18 20:33:14
4.3  操作模式和特权等级
        Cortex-M3/M4支持两个模式(handler模式,也就是中断模式和线程模式)和两个特权等级(特权级和非特权级,这里将其称为用户级):

$ Q. M9 R3 B# L+ p( a
特权级
用户级
异常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
; \7 M5 c  r0 u* X
       但若CONTROL[0]=1(线程模式+用户级),则在中断响应的始末,both 处理器模式和特权等极都要发生变化,如下图所示。
4.11.png
7 b. z1 F( F* ]
      CONTROL[0]只有在特权级下才能访问。用户级的程序如想进入特权级,通常都是使用一条“系统服务呼叫指令(SVC)”来触发“SVC异常”,该异常的服务例程可以视具体情况而修改CONTROL[0]。
        关于操作模式和特权级就跟大家说这么多,在讲解RTOS时,再跟大家详细讲解。有了上面的这些知识我们就可以讲解双堆栈机制了。

9 e5 R2 h$ h$ R: K5 D
baiyongbin2009 回答时间:2014-12-18 20:37:52
本帖最后由 baiyongbin2009 于 2014-12-18 20:43 编辑 & B9 F( {$ X7 Y6 u
( O9 i4 o4 y4 k3 }; ^  K$ v7 z9 U
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小节有说明。

$ b3 e) Q2 Y$ Y4 S
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

) i3 {7 m$ ^: `9 `
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)

% |1 [4 d- \2 D$ v; i! i* a
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)
: M5 d0 q. J0 }
Memory Map of the image

* M) E; o+ d4 j0 Q4 A$ z% @
   Execution Region RW_IRAM1 (Base: 0x20000000,Size: 0x000065c0, Max: 0x00020000, ABSOLUTE)
7 [0 ^" Q0 i" Y# Q2 |% ?* v
    Base Addr   Size         Type   Attr     Idx    E Section Name        Object
2 W4 }; L8 @. y  O
    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
     这里就跟大家讲这么多,后面的教程中继续的完善。

6 p" P- O  W" A( _5 L) e5 _- T" h( Z6 d) h# T' ?5 \  E
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
    5 B3 @) s8 ]/ w0 |! G
  2. 9 W/ f0 o5 \! \) S1 a
  3.     This function returns the content of the Control Register.; r2 B( e& R$ T) X2 R
  4. 8 M# B- F$ X+ n0 O: Q7 m
  5.     \return               Control Register value( h- Z3 c2 c+ q1 N
  6. */
    $ h3 B+ G% X- t3 w# t1 U/ _- M9 K
  7. __STATIC_INLINE uint32_t __get_CONTROL(void)2 W% C; }. f, @% r, @% B! ?
  8. {' {1 S9 X) [, A) W
  9.   register uint32_t __regControl         __ASM("control");" V7 C. ?7 v$ M- J; E( S
  10.   return(__regControl);3 O, M5 M3 I2 g' G) K% b# _/ a
  11. }
    - a, n" H. k9 a7 r6 J

  12. ( W1 O; C& N6 \  S+ ~  v/ Z

  13. # z3 u0 q2 F  J# r/ B
  14. /** \brief  Set Control Register0 C' @) C' \2 V8 ], S- L
  15. 9 f* G# E  Q& s1 d% |
  16.     This function writes the given value to the Control Register.9 J7 u( B# K( ?! n, Z' k0 `4 ~  v

  17. ) r( t  h0 j7 J2 P" U' R# r; V, q  q
  18.     \param [in]    control  Control Register value to set1 _% p1 }: k; c! @" O  h3 L- U$ @
  19. */7 w: \* @; C3 r( ?. x4 B7 `
  20. __STATIC_INLINE void __set_CONTROL(uint32_t control)
    0 z1 F- ~- s5 B% ?3 a( ~
  21. {
    + b- Q; C" b; r. f
  22.   register uint32_t __regControl         __ASM("control");
    ! [6 s$ i/ N% t# d& X  q
  23.   __regControl = control;* y; K7 W- x, F3 L9 z6 Q
  24. }
    9 N* P6 c: r( b" ~. }
  25. 8 A" }2 }7 [$ P- Z' e

  26. 1 s3 y0 s! Z  N5 Z5 l  j% V( l0 \0 k
  27. /** \brief  Get IPSR Register
    8 O2 y. c0 u! p/ Y6 `
  28. $ k) S/ a& y9 T, t+ |+ n% h
  29.     This function returns the content of the IPSR Register.5 h- @! |6 T2 {" u+ Q/ T
  30. - ^. \6 x! l, k; G, l% q
  31.     \return               IPSR Register value
    8 ]3 {) C) m. s/ l- d  ]! \
  32. */
    4 Q4 K! B  E0 b  S) M5 `. d
  33. __STATIC_INLINE uint32_t __get_IPSR(void)0 Q6 n& m! a( R- W8 n
  34. {
    5 q3 l8 P) V0 T. T1 }5 |, D
  35.   register uint32_t __regIPSR          __ASM("ipsr");
    # W6 n+ H1 @2 q: v! Z4 u+ N
  36.   return(__regIPSR);
    4 W$ U" G1 z7 u! q( |1 |) W
  37. }, Z% E$ G  G  S( M" I
  38. 9 e2 k2 R. s6 v1 h

  39. $ D' T' R& z4 _# b
  40. /** \brief  Get APSR Register; r4 F- ^2 r& d5 T! W. W: Q# x

  41. ( C+ I! d- [5 W- y$ {: J, x* N  ~
  42.     This function returns the content of the APSR Register.0 Q. T5 ~5 n) G0 X2 \1 \

  43. & s+ s9 E$ t) k+ r" R
  44.     \return               APSR Register value  h0 D1 e  y; g8 G! J
  45. */9 q5 {1 B5 H5 H& E. `
  46. __STATIC_INLINE uint32_t __get_APSR(void)# m+ P& q* S# o/ j( Z) A: m9 M
  47. {  Q: x$ Y" B0 a# C/ m) U* u+ Q
  48.   register uint32_t __regAPSR          __ASM("apsr");
    5 Q+ y$ u# G" c8 p3 _" p
  49.   return(__regAPSR);
    ( S0 U% f: I7 g- k$ l
  50. }: C7 G  h. e% T5 Q; n" u

  51. * h$ t! k+ d3 Y+ r4 v  e

  52. : |! g0 }8 n$ L7 ~5 B* ?
  53. /** \brief  Get xPSR Register( j7 r7 e3 t  z3 N" Y! c+ }% E

  54. ) {: T' M7 t5 h; K7 p! K
  55.     This function returns the content of the xPSR Register.# w" K& F4 R$ s( l. c1 E
  56. / t* ^" M4 q! s. b
  57.     \return               xPSR Register value4 u+ S* T2 |8 T! D" n
  58. */
    - I4 g- W& Y- S4 ?) Q7 Z
  59. __STATIC_INLINE uint32_t __get_xPSR(void)( T7 }8 |& n2 u, h/ x) ]
  60. {
    2 ]+ {$ {4 x4 s% Q0 {8 `
  61.   register uint32_t __regXPSR          __ASM("xpsr");
    : v! G3 M' D7 N* ]. F4 o) A
  62.   return(__regXPSR);
    3 b! X6 w- i% }7 E! ]. O  P: Q
  63. }
    , C; }9 Q0 d. i5 p+ u8 E

  64. ( X; T4 ]5 U  t, A5 w

  65. 6 U2 i' s1 L$ [
  66. /** \brief  Get Process Stack Pointer3 f; }. T8 _! m$ {
  67. + H5 c0 ^: w5 E; Q6 e1 S2 B" Q
  68.     This function returns the current value of the Process Stack Pointer (PSP).
    ' \, H0 t" v* J5 _/ n- q; p

  69. " M7 ?7 W9 y" F
  70.     \return               PSP Register value* N, l0 |/ j) |
  71. */5 O- q3 i9 ?% F6 [8 B# D
  72. __STATIC_INLINE uint32_t __get_PSP(void)+ J$ B4 F9 W. R( V
  73. {( l; f4 g( x# p0 n9 m( J# ~
  74.   register uint32_t __regProcessStackPointer  __ASM("psp");
    5 t1 b' n, S1 l6 y% r
  75.   return(__regProcessStackPointer);# r, J9 c/ k5 N5 ?
  76. }- L9 a9 X' B: J0 U8 d4 q

  77. 8 @( t6 ^+ z8 y! b
  78. " ]) ~! g9 B, r' g/ g
  79. /** \brief  Set Process Stack Pointer
    % H0 R0 I$ n6 Y# D4 ^6 [8 v* t! k
  80. 8 T7 o& e* m- o- _
  81.     This function assigns the given value to the Process Stack Pointer (PSP).
    4 b* b& M' C, {

  82. % H. z  P4 _/ d. u# C2 T! I' [
  83.     \param [in]    topOfProcStack  Process Stack Pointer value to set' a3 c! X/ L6 H
  84. */
    1 o5 n, S. |/ n( S
  85. __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack)! R, V  S/ \" P% S. k; M+ R
  86. {
    3 Q: y1 ~, I. t1 U, T7 v8 z% m! l
  87.   register uint32_t __regProcessStackPointer  __ASM("psp");+ \# f' C, N. g' W9 T" p
  88.   __regProcessStackPointer = topOfProcStack;1 J- L, R' z1 H
  89. }4 b: J2 h5 d3 Q; n

  90.   z4 M+ d2 p3 Q) s- |8 W

  91. ) I& P; U' S$ j6 Q
  92. /** \brief  Get Main Stack Pointer
    1 E" k! b  r% _$ ?: b# }
  93. 6 J6 m+ n* o! _! u% O6 s
  94.     This function returns the current value of the Main Stack Pointer (MSP).
    6 f$ e$ b4 t( w3 m8 v
  95. 2 j- f) w  }7 B6 Q* y- R: k
  96.     \return               MSP Register value% h- l, K9 u! c; g3 E$ D' z/ h: r
  97. */2 R0 j$ T. s0 ~' e/ s
  98. __STATIC_INLINE uint32_t __get_MSP(void)3 C2 i$ ?9 D: x/ l/ b
  99. {( F) n* |2 X. I0 |+ R3 s6 s& X
  100.   register uint32_t __regMainStackPointer     __ASM("msp");( t' x3 Q8 s# M
  101.   return(__regMainStackPointer);
    # C' Q' f: s0 i' G! T
  102. }* n% j( l! e1 V
  103. 8 O, W4 r8 F. k
  104.   r* b% y" c4 {5 @% {
  105. /** \brief  Set Main Stack Pointer7 T1 n2 l1 E* x) a
  106. 8 f3 V7 N" Z8 _0 |  v! y
  107.     This function assigns the given value to the Main Stack Pointer (MSP).& M% x* z3 J$ w

  108.   @( e* `$ R8 p, D1 i
  109.     \param [in]    topOfMainStack  Main Stack Pointer value to set1 l* T- q( k3 A9 Z" I
  110. */
    " y5 L4 T& \- }! F' o
  111. __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack)* l2 D1 B7 F. g6 V, o6 y2 Q! Y: Y
  112. {
    7 `% N- _$ H$ ?3 E5 D/ V0 b7 q- i5 K
  113.   register uint32_t __regMainStackPointer     __ASM("msp");0 S+ v( U4 \2 s7 K/ @$ R; x
  114.   __regMainStackPointer = topOfMainStack;9 v6 `& F0 l! ?8 F. F7 @7 z7 R
  115. }
    ) Z7 H0 l" V: k2 k$ v

  116. 8 K0 S4 D$ [& {+ Q" D; s
  117. . R7 G  g6 {3 v8 g
  118. /** \brief  Get Priority Mask
    9 N: O' U4 b$ e8 s# Q, u- E
  119. # I3 x, ?3 T& a) M
  120.     This function returns the current state of the priority mask bit from the Priority Mask Register.
    $ A/ Z: m. z7 l! ]. \! a+ {: ?
  121. 8 s% T7 F3 g3 D) ]$ s. n. h0 Z
  122.     \return               Priority Mask value3 v- }6 C" `4 M
  123. */, c8 I$ j$ j% G$ q
  124. __STATIC_INLINE uint32_t __get_PRIMASK(void)* k, Y6 l: @* v. ^" a* \
  125. {: s1 O' ^6 c# @! i2 h
  126.   register uint32_t __regPriMask         __ASM("primask");/ P; c4 ]: s0 Z! a
  127.   return(__regPriMask);' }, B) e0 m% t. y  c
  128. }" w6 @4 v% Q, p9 v5 z8 \! }4 f! Y
  129. * _& @9 {2 \" f. E( ~5 O) ]( E3 U) o

  130. . C- t1 ]' ~- H7 @6 t
  131. /** \brief  Set Priority Mask
    * @1 e7 {' v* a0 V( ]/ f

  132. 6 K& F: R3 v1 {4 |  d
  133.     This function assigns the given value to the Priority Mask Register.
    ' k: S2 D6 M5 T" `
  134. % }, B! B) n! J# p1 p) H! u
  135.     \param [in]    priMask  Priority Mask
    ) A1 L) I7 e% Z$ a( ]3 Z& A
  136. */
    " Y1 u7 n. c' ^6 ~+ W
  137. __STATIC_INLINE void __set_PRIMASK(uint32_t priMask)# |2 \& w% |$ f6 P5 }
  138. {
    ' _( u8 p$ X' B0 S% o7 O* `
  139.   register uint32_t __regPriMask         __ASM("primask");
    . ]( t% z7 R% |0 O
  140.   __regPriMask = (priMask);4 c* N4 }* H7 K( g$ Q  G/ G
  141. }
    . K1 D& T2 L1 \! ~
  142. ( i/ b! |7 j7 K# W* V: }
  143. # N) R0 Y8 {, p( k6 w9 w/ f3 X4 i, ?& m
  144. #if       (__CORTEX_M >= 0x03)
    1 Q# _( O9 V0 f3 }) N; O" G

  145. / Q; I: v+ d( i" w' \0 c! x+ {$ P
  146. /** \brief  Enable FIQ1 N7 \7 B4 L9 F: Y! k: n

  147. ) {! a! G% R' C2 c( r4 r# r
  148.     This function enables FIQ interrupts by clearing the F-bit in the CPSR.
    9 q' `$ |" t4 C, A% f) [
  149.     Can only be executed in Privileged modes.
    ' t& d- X' q7 ]$ g; h9 k# a& s$ _  }
  150. */
    / k1 P$ L2 q7 Q5 W3 V
  151. #define __enable_fault_irq                __enable_fiq
    5 l2 y" v- H* D5 }
  152. 2 m0 Z+ p1 C& D' B3 K
  153. 7 O/ A! t$ w! U+ U
  154. /** \brief  Disable FIQ+ r4 [3 C- M& E

  155. 3 k0 W1 o9 Q8 e4 W5 _
  156.     This function disables FIQ interrupts by setting the F-bit in the CPSR.; E$ h, X0 J& O6 {+ p4 A' i
  157.     Can only be executed in Privileged modes.
    7 C4 M1 A! K3 ]8 a0 m1 y. \
  158. */
    . H, Z8 i' ^. }: X- D9 O
  159. #define __disable_fault_irq               __disable_fiq+ Q& x. q+ D1 F3 N  N1 g! f* l
  160. ( E. P8 ^: F  Y( K! K

  161. 3 B7 C: p4 H2 u; C
  162. /** \brief  Get Base Priority" O/ o9 q9 d( ^3 M9 n

  163. 7 M; ~9 D$ t1 e) a4 R; b
  164.     This function returns the current value of the Base Priority register.  m8 ?! `$ u$ r: p1 }) c: a# R  X

  165. . L3 K- O) h$ I9 U
  166.     \return               Base Priority register value
    $ j! P( l# O4 R% k/ n
  167. */9 D9 R' j- r$ Z8 G' E
  168. __STATIC_INLINE uint32_t  __get_BASEPRI(void)9 \) {4 c" O. S
  169. {
    0 J% q* w) Y- Q0 h
  170.   register uint32_t __regBasePri         __ASM("basepri");* x& I' r$ E- w: m& {
  171.   return(__regBasePri);0 v1 t- W; A0 [' l2 e
  172. }
    ! _; j/ K1 T4 d

  173. / ?3 L! j3 f3 b& {
  174. ) `8 ~3 h" c, F3 t
  175. /** \brief  Set Base Priority
    ) b" ~" V$ ^+ ^1 q

  176. % S. R$ g$ \4 E. J
  177.     This function assigns the given value to the Base Priority register.; T4 ~1 N7 S% V0 n
  178. $ q7 _) e0 P; J/ R1 v9 _0 L
  179.     \param [in]    basePri  Base Priority value to set. W- [" z, f9 v, O  X1 Z! x# w
  180. */
    8 f6 K1 I" K5 H6 }% l) h
  181. __STATIC_INLINE void __set_BASEPRI(uint32_t basePri). J; X& \0 N5 ^4 T0 t2 p5 E
  182. {
    ; O5 I3 p* D: |
  183.   register uint32_t __regBasePri         __ASM("basepri");( a6 t9 L6 ?  e
  184.   __regBasePri = (basePri & 0xff);) z1 F$ x) I+ h$ L
  185. }
      U2 |- E- n& P  p* J+ Y. B
  186. 9 c  Y3 j# U! j6 H; {; n# S
  187. # [/ m5 e) P0 x) x. N# `. U
  188. /** \brief  Get Fault Mask
    ( S( g5 q2 o3 v+ ^3 r, R0 E6 m9 u
  189. " s- o8 ?; H1 p* g6 Z
  190.     This function returns the current value of the Fault Mask register.: _4 K+ V" E. u' C

  191. - h5 ~4 \2 x, Q/ J$ L( e% F1 N
  192.     \return               Fault Mask register value
    : [$ w/ q/ }- G/ x$ A$ }
  193. */
    + R) [8 S! }( G0 j
  194. __STATIC_INLINE uint32_t __get_FAULTMASK(void)
    * v0 ?; F. ~* F! f* P# [9 m& Q
  195. {
    " g7 z) I) \' H% _& N6 F! S
  196.   register uint32_t __regFaultMask       __ASM("faultmask");/ v4 i" I# l; W4 C1 b
  197.   return(__regFaultMask);
    + H  R) R+ ^* F  f# O' O1 h# S4 X
  198. }' H6 \$ p5 P0 Y

  199. + j& v0 b9 a; R9 j  A
  200. " r6 K1 E5 {. P1 D, o2 k
  201. /** \brief  Set Fault Mask
    - F- O6 j( e" R- V+ j) N

  202. $ a8 ^8 q  y) }. `$ y
  203.     This function assigns the given value to the Fault Mask register.
    & d# b3 X6 X. R/ |! J- {; `$ {
  204. # G/ O" B  r3 F4 F( |  t& B
  205.     \param [in]    faultMask  Fault Mask value to set9 H4 r# d6 U7 f: }1 b" Q) Y
  206. */. G! W( M4 @% I, X" z& F. z
  207. __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask)
    5 L5 x* ], p% L1 T8 Y
  208. {
    3 N# c. ]$ \6 P3 z! k) C3 e
  209.   register uint32_t __regFaultMask       __ASM("faultmask");
    & S5 p; j7 R1 o; b) [
  210.   __regFaultMask = (faultMask & (uint32_t)1);1 u. ]$ O& I4 Y* L5 ^
  211. }) ~: k$ T6 T. C5 X1 Z/ {2 b! I" t
  212. & x4 k# \+ m$ i" |" J/ i
  213. #endif /* (__CORTEX_M >= 0x03) */- K1 R; q! H) E$ A; R2 C
  214. 6 }! D  }/ \# C! l7 ~4 k4 m2 S' |
  215. + {4 q' x: }9 _0 f+ g& @
  216. #if       (__CORTEX_M == 0x04)- K6 {4 B) P: [. T4 D
  217. - N9 D$ p# W7 T" @; q$ c" Y$ Z
  218. /** \brief  Get FPSCR
    * {$ u- v) [1 k5 o& _
  219. ; k9 H% T- v1 b. X
  220.     This function returns the current value of the Floating Point Status/Control register.
    5 s; t, l/ ^3 C. k- V

  221. + x& N; X/ S& H. o3 f
  222.     \return               Floating Point Status/Control register value2 R6 d7 y! h3 `& h
  223. */" l5 m) n5 R* o  q4 N
  224. __STATIC_INLINE uint32_t __get_FPSCR(void)
    * t, z$ [+ V: j) w
  225. {+ Y3 Z  ]& e$ W
  226. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    ! q/ _$ l+ g8 A5 j& m' x
  227.   register uint32_t __regfpscr         __ASM("fpscr");. |/ h  g- n) {0 [6 P/ K* G: @, W
  228.   return(__regfpscr);3 X$ L' l/ n! d7 Q
  229. #else
    6 R1 y7 U6 f& b0 ?
  230.    return(0);3 a# e3 h* z0 W, C9 ?
  231. #endif9 X! h( F! f: g
  232. }8 P) H# I% P0 I2 B. B; S' g: v

  233. 0 s6 }# ~$ B7 S1 Q7 a4 I8 z$ _1 C

  234. 7 }" d* A7 {8 j
  235. /** \brief  Set FPSCR' L- V( w2 p; Z; W/ n4 D0 ^

  236. & ?. m4 O+ Q2 c0 c( r: Y+ q, ~
  237.     This function assigns the given value to the Floating Point Status/Control register.
    : s$ n4 }; [( F: P0 b' s

  238. 2 O# t8 e, U* _: s
  239.     \param [in]    fpscr  Floating Point Status/Control value to set
    0 l6 ]- @8 }$ J2 P3 W; _) r- S
  240. */( i' ?  M! c. F/ |, Q. U
  241. __STATIC_INLINE void __set_FPSCR(uint32_t fpscr)6 I& u! s( ^) ?: M4 Z7 X" G& h
  242. {
    6 L) m: Y6 f* C
  243. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    ; \, G1 n1 W9 c" f4 l# R
  244.   register uint32_t __regfpscr         __ASM("fpscr");
    ! ?& i( L3 m9 L) x/ V- F' y9 ?* k5 L
  245.   __regfpscr = (fpscr);
    2 v+ D. `2 [, n; O
  246. #endif
    8 f5 }+ Z0 }& C' ^
  247. }
复制代码

2 k, S9 E# G7 Q! ?
7 B% X7 a7 j% R, d
baiyongbin2009 回答时间:2014-12-18 20:47:01
4.6  总结
    对于初学者一下子掌握这么多的东西比较的困难,这里总结一下主要的几点,一般应用记住这几点即可,需要的时候再查阅这个知识点相关的内容:
  l  系统复位后默认使用的是MSP,复位后的状态是特权级线程状态,在这个状态下是允许修改寄存器的。进入到用户特权以后就不能修改这些寄存器了。
  l  用户特权的情况(也就是用户建立的非中断服务程序)下可以使用MSP或PSP,特权模式(中断服务程序)只能使用MSP。
  l  还有很重要的一条就是。假如在用户模式下使用的是PSP,那么寄存器的数值被保存到任务堆栈的空间,! j# s0 q; ]1 X+ q6 W8 j
进入中断程序后就开始使用MSP,如果还有一个高优先级的中断难么就继续的使用MSP,在程序推出最后一级中断的时候就用用户堆栈恢复寄存器。
  l  Cortex-M4的堆栈模型是向下生长的满栈。
    下期教程会做两个相关的例子帮大家深入的理解CM4内核方面的知识。
2 f% A2 P5 W3 y; B0 H( }
8 a/ Z# S2 L, \1 Y
$ I5 k8 o5 c' Y
参考资料:
1.    Patterns fortime-triggered embedded systems英文版和中文版
2.    Cortex-M3权威指南中文版
3.    TheDefinitive Guide to Arm Cortex-M3 and Cortex-M4 Processors(M4权威指南)

/ H$ s$ P+ d  s/ n/ P

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版