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

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

[复制链接]
baiyongbin2009 发布时间:2014-12-18 20:17
本帖最后由 baiyongbin2009 于 2014-12-18 20:48 编辑
! Z) d6 Y) j6 D" r+ E/ a$ X; [  K8 @9 L- _% Y3 [3 J
特别说明:) T. U" o0 n& y7 d
1.  本教程是安富莱电子原创。6 I3 v! {& a$ }7 H* D( c& R0 i$ J
2.  安富莱STM32F407开发板资料已经全部开源,开源地址:地址链接9 Z3 t. L: y4 a+ V5 b# F
3.  当前共配套300多个实例,4套用户手册。

9 Q) [' s( m" h4 @# B
第4章  Cortex-M处理器的OS特性

. f% g0 M9 w3 r3 W0 H1 [% N! @! n
    本期教程带领大家学习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的初始值是不定的。

! C" |* M, }# ~* E  [; {/ q4.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字节对齐的。
) |, j( e2 J5 A$ A  C
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只需此位可读写)。
& Q& }$ {5 g( s; L" O# V
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异常。
$ h2 l1 W7 e, s1 P! Q) J! f  F
收藏 评论6 发布时间:2014-12-18 20:17

举报

6个回答
baiyongbin2009 回答时间:2014-12-18 20:27:47
本帖最后由 baiyongbin2009 于 2014-12-18 20:30 编辑 1 U8 h6 J# B8 f4 l2 P) ]" T
6 }7 g+ [1 w' E* |# T" O- {
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
6 [4 i3 b7 J$ E3 I5 @6 l2 G
! \+ y8 J" s7 U4 V+ B
这里特别注意GE(这个是CM4新加的,CM3没有)和中断号PSR(IPSR),后面将SVC异常的时候要用到。
l  中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI)
这三个寄存器非常重要,初学者一定要了解,能记住最好。这里建立一个简单的表格,方便大家查看。
4.3.png
% ?, Y* t; [4 h8 Q6 ]1 {# q. u/ N
对于时间-关键任务而言,恰如其分地使用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

, f, {- E2 {+ B9 p; @/ e: ~" Z% f" E6 w
% m" K' ~, @7 T; h% l% n) M- s
总结一下上面的表,可以得出,合法的EXC_RETURN值共3个
4.6.png
  Q% ^/ |+ z0 T5 t* h4 g
    如果主程序在线程模式下运行,并且在使用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

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

# n7 d* d6 o9 i) u: N# h( c
特权级
用户级
异常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
' `! V* p0 F/ L( l1 q' `
       但若CONTROL[0]=1(线程模式+用户级),则在中断响应的始末,both 处理器模式和特权等极都要发生变化,如下图所示。
4.11.png

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

. S3 i7 z) G" b; T  T
baiyongbin2009 回答时间:2014-12-18 20:37:52
本帖最后由 baiyongbin2009 于 2014-12-18 20:43 编辑 $ a' ~! S; X: T8 r% v

7 q, E" p% u6 \
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小节有说明。
* X5 C! w9 Y3 T
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

8 \9 E! y/ Z- g& K7 C. C
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)
, g4 `9 B2 x# V, L
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)
" X' d$ m1 R/ u8 i6 ~4 w$ O7 U
Memory Map of the image
' X! ^; |% @. ]$ P% }
   Execution Region RW_IRAM1 (Base: 0x20000000,Size: 0x000065c0, Max: 0x00020000, ABSOLUTE)

$ Q" [' M+ _  c
    Base Addr   Size         Type   Attr     Idx    E Section Name        Object

" O* z5 _- U& X. J2 r9 ^7 j" Z
    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
     这里就跟大家讲这么多,后面的教程中继续的完善。
, c2 T8 }5 P6 A6 O( n
4 _! ]8 h0 B. T5 _/ e7 ]
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
    % i$ P1 E: ~3 d0 X" B* L5 _
  2. : i- l. c8 ?  E. _
  3.     This function returns the content of the Control Register.: `/ |# a6 X; V

  4. % s* q  d. k/ }
  5.     \return               Control Register value+ p9 P$ _0 Y) g: n$ i/ n2 v
  6. */
    " N- v$ u/ ?. L. F; M$ X8 _+ e5 H
  7. __STATIC_INLINE uint32_t __get_CONTROL(void)  y. q  C7 C0 `; e5 E
  8. {0 {& M  k! }) P3 C. n. m
  9.   register uint32_t __regControl         __ASM("control");+ F# t" e0 d# ]6 n! V4 [
  10.   return(__regControl);
    ! `9 t. G& D8 i# j3 _0 S) X4 n5 p" J
  11. }
    ) h. f+ s; x& K  y7 v* R) P# O

  12. 8 F, q; Z% I% y3 j1 ]& ]5 n9 h
  13. 9 w, H4 B# u9 `" V
  14. /** \brief  Set Control Register* q7 S$ r6 R8 h
  15. " o! B8 w5 w3 T
  16.     This function writes the given value to the Control Register.! u6 A& @- y7 F* H
  17. : Z1 A, s" d  E9 U0 Z! \8 E
  18.     \param [in]    control  Control Register value to set
    * L- ?: U/ G9 {- L/ x  G3 M  R
  19. */
    ' n- z( @5 V1 V
  20. __STATIC_INLINE void __set_CONTROL(uint32_t control)2 M- o5 _7 {- s+ O
  21. {% C' d, \: k- {
  22.   register uint32_t __regControl         __ASM("control");! g  H( P! b6 {, R+ [& F
  23.   __regControl = control;
    4 _6 N9 H- n: H. S: p: L1 g
  24. }$ }1 T8 X- ]4 R& Q
  25. ) E. N% Z4 {+ v% |! g& _4 r/ D
  26. 5 k/ U( K" V) d1 F
  27. /** \brief  Get IPSR Register
    ' C1 ?1 J) A; Q: a5 W% |- H

  28. # k' E8 V8 }2 W1 \. m
  29.     This function returns the content of the IPSR Register.- \) X& s, k: F# F' v8 N1 o
  30. 6 }6 [( D$ e, l
  31.     \return               IPSR Register value
    / _; @& R& O2 Z: e0 [* A  I* V
  32. */; z" n& Q3 y1 W. T- m4 O9 {& R
  33. __STATIC_INLINE uint32_t __get_IPSR(void)5 }8 w( W8 |  p- s  j& s
  34. {' ~' Q7 y, n! N4 Y' m
  35.   register uint32_t __regIPSR          __ASM("ipsr");+ N$ v0 A# t6 T
  36.   return(__regIPSR);
    / C7 q) O+ ~. [! w
  37. }- |+ h; g$ P/ t; X" p$ z5 h
  38. 0 Q  I- F) s# i: K/ K2 I/ \

  39. % E8 W, v; u$ ?% G0 y" w
  40. /** \brief  Get APSR Register& u3 L7 b6 o7 y( `* y- k% M

  41. / S. L- j' O( c0 f9 r4 r$ H' D
  42.     This function returns the content of the APSR Register.
    9 p" e# k$ I) M- l3 V4 z
  43. 0 X5 Z- `2 Z1 K0 z4 `( L
  44.     \return               APSR Register value
    " L3 \4 e+ q! F, L- @& e3 A) I
  45. */
    0 e+ K1 [, P' j. x
  46. __STATIC_INLINE uint32_t __get_APSR(void)7 }. [& \) x; T9 L' Q& q. C
  47. {
      H' R$ s5 M  y$ t1 _# A
  48.   register uint32_t __regAPSR          __ASM("apsr");$ G( e. z  f  o) U
  49.   return(__regAPSR);. q% r1 c5 {+ d: J/ T/ X
  50. }5 B" U) G8 c3 m
  51. # B7 T* T* x) \! H  e/ f
  52. 1 B3 X) o! C$ u# G, ^; r$ m
  53. /** \brief  Get xPSR Register
    & y6 ?7 q" ^- l

  54. % `6 d. o! l% r) _$ Q+ h
  55.     This function returns the content of the xPSR Register.
    , L) t5 V. N# O, A* g7 u

  56. ; a) h& f( Z' \( H5 l, r+ Z
  57.     \return               xPSR Register value
    6 G. O5 |# ^' ^
  58. */  `9 S0 `4 Z  p! m
  59. __STATIC_INLINE uint32_t __get_xPSR(void)
    ; f/ M/ y. n3 x8 @6 o- n$ m/ ?
  60. {3 ?6 Q& e% D6 m5 W
  61.   register uint32_t __regXPSR          __ASM("xpsr");
    - S( D5 z* A+ @- c! T! S, _- q
  62.   return(__regXPSR);
    % P5 `- _; P( i
  63. }
    + u' H% ^! o2 O- G( P% w. ~) ^# I- {
  64. + B( t3 }$ _& Q; c/ _

  65. % M4 P5 |, W! W( Q  z# a( z
  66. /** \brief  Get Process Stack Pointer
    ) G) W7 R& g2 i1 F6 f9 w& m4 B
  67. ( o( _3 T) ^$ B3 c
  68.     This function returns the current value of the Process Stack Pointer (PSP).( c9 A6 t$ c9 h/ A+ D1 P
  69. # {9 Z2 P( J" z8 M" C8 A0 _/ C
  70.     \return               PSP Register value* m  e) ?: z& [) P4 k
  71. */% ?+ w& F, I+ w/ Y3 v
  72. __STATIC_INLINE uint32_t __get_PSP(void)
    . B/ @) j7 l3 B" J
  73. {
    ( I3 M" @! G! [& M' _
  74.   register uint32_t __regProcessStackPointer  __ASM("psp");
    / B# w; }0 l# b8 J9 V- [/ L
  75.   return(__regProcessStackPointer);9 o. |, T  Z( u+ b. x
  76. }3 v* b( o" x( N* k  \

  77. 1 y5 Z6 [+ i3 L, Q+ g8 F: g1 d

  78. 8 z* u# @0 \3 j) a+ H( l' j
  79. /** \brief  Set Process Stack Pointer  m) `! W; f; T" U: a
  80. ) y( I' F$ K. v2 f: Y9 t( H
  81.     This function assigns the given value to the Process Stack Pointer (PSP).
    1 d* k6 v: B# }
  82. 9 f6 ]+ W9 C$ j  u- x& h
  83.     \param [in]    topOfProcStack  Process Stack Pointer value to set+ T- A. Y% _& O/ f
  84. */9 B+ S+ D: y6 w; ^! v2 b$ Q
  85. __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack)# B. u0 t% B. `7 V" g2 U) \4 ^5 F
  86. {/ @3 Y1 O* {* Z& m; ?' d) p
  87.   register uint32_t __regProcessStackPointer  __ASM("psp");7 K% m1 O/ E5 ]1 |6 N" J
  88.   __regProcessStackPointer = topOfProcStack;2 d# g& x5 a4 a9 ~, a2 Z8 f; i
  89. }
    , d) n+ m" H6 b# X
  90. 4 Q7 V8 C9 v% N& C6 P" I

  91. & a5 P  T# U6 }" E
  92. /** \brief  Get Main Stack Pointer) z3 s( E# e* V  s& `) Q3 C5 `9 {" ?
  93. * \6 X* I; V5 B/ H- L' n( _+ \& }
  94.     This function returns the current value of the Main Stack Pointer (MSP).0 @* s& T/ a& w$ \6 y; [  ~# i

  95. * q( e$ s, t- o8 W4 X) v$ Y
  96.     \return               MSP Register value; {  k5 F- I: A4 {4 H) w9 t1 f$ U. H2 h
  97. */
    1 I! k4 V" R6 x' W9 v1 S: R
  98. __STATIC_INLINE uint32_t __get_MSP(void)& F$ T/ H* J+ S9 o2 z; |+ N; K
  99. {' M+ D- A; q1 ^1 q7 c  |( T" {4 P
  100.   register uint32_t __regMainStackPointer     __ASM("msp");; o' c% h. a; V! @- [
  101.   return(__regMainStackPointer);
    - r' e- t' q. t7 {6 P5 p, Z
  102. }. V* _  Q6 @" f5 B5 g( ]$ X1 j1 w% J
  103. 1 }( f8 |/ G8 n3 s! l1 I: a. E5 A

  104. " W: ^! z8 ]: E  q7 Q- C8 N
  105. /** \brief  Set Main Stack Pointer/ c/ F7 \' y% ^

  106. ; Y9 ?2 b) @& F2 O
  107.     This function assigns the given value to the Main Stack Pointer (MSP).
    1 `$ d/ O, a- u, I4 S. }8 |

  108. - ?: S) v0 j9 Z- r4 a  Z- I& W. R4 L
  109.     \param [in]    topOfMainStack  Main Stack Pointer value to set, }- Y' i: {# j& o! {5 O
  110. */
    6 o8 H9 O+ C0 T4 J2 h
  111. __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack)
      h8 a! N( R$ Y" ~0 J
  112. {
    + u$ E' G6 z3 m( {  ^$ o5 A
  113.   register uint32_t __regMainStackPointer     __ASM("msp");+ L& [; P% L' G/ N' j
  114.   __regMainStackPointer = topOfMainStack;4 c8 x2 y' q1 \! K
  115. }
    " F  e/ H9 N7 U) _7 Q; X6 e

  116. + V8 p. s0 ^' v! F1 l

  117. 4 i% Q8 Z$ n8 s- w
  118. /** \brief  Get Priority Mask3 T+ Y+ F. i. B. N4 [6 {5 F

  119. ; c) c# Q5 I; E) p" c
  120.     This function returns the current state of the priority mask bit from the Priority Mask Register.! c. P, t8 _2 n! i$ z/ x' x6 [+ L
  121. ) O; E/ P' o" D; z4 m1 ~
  122.     \return               Priority Mask value
    6 ^6 ]" Z; k" `: @% q# w, O" m) H7 K
  123. */$ w0 r7 F% Y) [5 p- p! l8 d
  124. __STATIC_INLINE uint32_t __get_PRIMASK(void)/ W' t! j$ \: Q% a+ }$ y$ y
  125. {$ v( z" R2 T/ x
  126.   register uint32_t __regPriMask         __ASM("primask");
    % M" S! Q! F/ C
  127.   return(__regPriMask);7 o; T. t6 @) l% k; i/ R+ l. O
  128. }
      U& f% u; c- j; i7 O+ }& J# j
  129. ( x8 |. m3 f% S9 d

  130. / u* Y0 A' F& a- R* J$ U' i: n
  131. /** \brief  Set Priority Mask
    2 m' s* c& \  s# |# @+ u* g8 T
  132. 1 I7 r4 p. u$ O8 S4 Z+ E
  133.     This function assigns the given value to the Priority Mask Register.
    % T, E, P  j0 m8 C

  134. 8 ?# n5 q* m, b" G" U$ L+ ~/ z
  135.     \param [in]    priMask  Priority Mask
    8 m! y8 Y, b/ k. A
  136. */
    : M, d( G- N- x( s1 s  ]7 z
  137. __STATIC_INLINE void __set_PRIMASK(uint32_t priMask)
    + |# s* p8 `( Z) t2 E1 a; D* B
  138. {' E4 A1 N: p/ G+ o
  139.   register uint32_t __regPriMask         __ASM("primask");- ^; H' X- K: y' ?
  140.   __regPriMask = (priMask);$ w5 I  o9 }, O  s/ Y1 ~9 D3 D
  141. }7 g2 s/ b2 C& }* I9 S; ?
  142. ! X. w  X+ [& i! H/ |
  143. - F% i! C. Z2 v) V! h3 ]: L
  144. #if       (__CORTEX_M >= 0x03); x- o4 u. h- p6 d" O

  145. & L3 N  f0 e" Y
  146. /** \brief  Enable FIQ
    # @5 D" I! r- m' r
  147. 1 M/ w0 M1 D) e
  148.     This function enables FIQ interrupts by clearing the F-bit in the CPSR.
    " `9 K- J6 R2 [+ _2 N# p
  149.     Can only be executed in Privileged modes.
    2 N/ O! V4 e" `% f. ]# p
  150. */
    , O& c# w" g8 B" k! v
  151. #define __enable_fault_irq                __enable_fiq2 D+ j7 T. d3 f  J/ A9 w* _

  152. ! w$ e: ^9 \, b1 u2 d
  153. 5 G7 R+ |8 a' n2 g7 \
  154. /** \brief  Disable FIQ5 o& p5 v  R% ?8 X. }

  155. & ~- s3 `& s' j( [7 ~
  156.     This function disables FIQ interrupts by setting the F-bit in the CPSR.
    3 i8 N+ j9 j' Z; ]4 q4 @# b
  157.     Can only be executed in Privileged modes.5 `$ x) N2 o  h0 r  y
  158. */
    # i0 V7 d; b2 k7 ]
  159. #define __disable_fault_irq               __disable_fiq
    # B" P7 _- u4 P

  160. 9 y" n' |; i5 y

  161. 0 S+ ?! G' T/ w' \
  162. /** \brief  Get Base Priority% E( c# k5 V5 W, C+ X, @6 e

  163. 5 d# S6 \$ f  _0 |/ u4 ^' h
  164.     This function returns the current value of the Base Priority register.
    # q' T, j% M/ T9 z# t/ a

  165. 0 v% O6 i" O, m8 ?8 x  P- I4 {3 r- J) ^5 F
  166.     \return               Base Priority register value" i0 l: \; ~% ^4 K% n, t( o
  167. */
    9 O" \( g; l5 Q  Q& @
  168. __STATIC_INLINE uint32_t  __get_BASEPRI(void)' m/ \& l' J0 Y: D
  169. {1 m! ]5 b5 f. d' n2 i
  170.   register uint32_t __regBasePri         __ASM("basepri");
    2 p7 i7 _6 [% b$ _) p5 `
  171.   return(__regBasePri);2 P: o: K! f" E' b+ B
  172. }
    0 ^) k) E6 G5 e0 x9 X3 {, V- b; A# U  i

  173. 8 l3 q- ~4 s+ G! C$ [- j

  174. & J1 u9 z% W2 @4 A- R: S
  175. /** \brief  Set Base Priority
      ]* O* z! q8 Z

  176. - z' P+ J) Z0 ]
  177.     This function assigns the given value to the Base Priority register.0 M  c# c, u% P- v7 |

  178. ' M  B4 u  J' O0 c$ L7 s8 `  _
  179.     \param [in]    basePri  Base Priority value to set
    # `- }1 w6 d7 Z3 z& v' n. w
  180. */
    4 a' Z9 I9 N% D; P6 q6 y* ]4 _
  181. __STATIC_INLINE void __set_BASEPRI(uint32_t basePri)
    - X1 f8 C$ B) l. N
  182. {( i! v( K9 F- Z3 ]( N9 U0 _
  183.   register uint32_t __regBasePri         __ASM("basepri");$ ~: e! ]# F. A7 V
  184.   __regBasePri = (basePri & 0xff);6 v4 A! ~8 m0 ^- Y$ g  w
  185. }) e( K4 t$ L; x* a! C2 ]$ i, j
  186. 4 s/ I# Z  }9 ^1 G1 B0 [" S2 B3 p
  187. 3 S& v0 h; F$ g& s1 ~8 ?, _( j
  188. /** \brief  Get Fault Mask
    ( g! S6 X  N& z7 ]# B: m

  189. " F5 s  ]9 z; s2 K( S: }, C
  190.     This function returns the current value of the Fault Mask register.1 n" v" P9 V2 P. Q5 x
  191. - M/ W3 ?; h9 q1 A( x
  192.     \return               Fault Mask register value: }  |. D' d& Q# ?+ u
  193. */
    # u' L9 |0 c$ q6 \4 o9 L+ t( {- ?: N
  194. __STATIC_INLINE uint32_t __get_FAULTMASK(void)" V5 y2 q& H, ^. O
  195. {
    5 A. G7 w0 H6 v+ J. d& e8 e- y& T
  196.   register uint32_t __regFaultMask       __ASM("faultmask");; Q/ M3 Q, h$ |6 g2 K* F* u
  197.   return(__regFaultMask);5 a3 A4 v/ I, q. y3 W2 Q
  198. }; D% o% T1 e, R  K9 @% K" y  f! ?4 f

  199. % \$ w/ ]! K8 `1 _% K) d
  200. % I# f4 ?  H$ w+ X' _
  201. /** \brief  Set Fault Mask; W8 k4 [! \: ~
  202. - e) d7 M$ {; w
  203.     This function assigns the given value to the Fault Mask register.
    - L  R- ?" U  Z' v; H' _

  204. * o$ q2 v; F2 B1 f' u; l
  205.     \param [in]    faultMask  Fault Mask value to set
    ( }9 N5 v; t6 l
  206. */
    & F! }# Y' j5 K
  207. __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask), M1 m( l  [5 l; N( x1 n, @$ ^9 \& u1 k
  208. {
    8 u0 K- _; P1 j8 r4 u% |; c' Z$ r
  209.   register uint32_t __regFaultMask       __ASM("faultmask");& \9 c1 w0 ]  @1 b; f" G
  210.   __regFaultMask = (faultMask & (uint32_t)1);* D  Y# q1 \, e2 P6 }
  211. }
    7 v$ m7 X$ y" q% e

  212.   k0 W2 T9 Q2 E/ b$ P+ c! w" @2 @
  213. #endif /* (__CORTEX_M >= 0x03) */
    ! r0 f7 Z: z$ W% f' B0 r# d
  214. : M/ u8 R- E  \+ g# V

  215. 7 p' ~$ ^( X5 O7 i
  216. #if       (__CORTEX_M == 0x04)
    4 u* `' E) w. }$ X0 A* J" W

  217. % `/ m: \: Y% t' r* t. h
  218. /** \brief  Get FPSCR
    ) ~( X9 r4 [7 @( `' d5 B5 x8 r3 S$ B4 ^
  219. 8 V" X) t0 y. v- g
  220.     This function returns the current value of the Floating Point Status/Control register.6 [1 e  p4 X1 D$ x$ v

  221. ; W7 x& j3 y, P, b4 G+ T
  222.     \return               Floating Point Status/Control register value: i" n  A; f# X! W5 J/ C+ [/ Z# J
  223. */( P9 @9 y  S$ O3 k$ O* x
  224. __STATIC_INLINE uint32_t __get_FPSCR(void)! ^4 ~! U. L% _7 F8 A3 k
  225. {
    8 v3 b& k4 }  B
  226. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)* V* `+ c# o. Q+ ~9 @; [
  227.   register uint32_t __regfpscr         __ASM("fpscr");
    . R; x8 B5 U4 k( K1 f! k9 d9 \
  228.   return(__regfpscr);1 v, o& g: w) t# m2 t$ g1 F
  229. #else' P1 p( M6 T+ g3 U% d
  230.    return(0);) b" [0 e/ |9 h5 i; K
  231. #endif) F: h- t( ~. a3 E* @" p
  232. }
    2 P) G3 N$ |9 }7 a+ r( C9 m
  233. : s; f( ^+ A! W7 f: Z0 N
  234. 7 N6 K* i) J$ Q) P) F" I) s" _
  235. /** \brief  Set FPSCR: L$ O" Y) ]6 i9 D+ q  s: s; @

  236. , V% J; a( X3 y; O
  237.     This function assigns the given value to the Floating Point Status/Control register." n* e: `7 v8 M! b
  238. % O! W0 A* _; ?; x5 _* B
  239.     \param [in]    fpscr  Floating Point Status/Control value to set
    / I- y" u0 c# G
  240. */6 I3 N( w: O$ }9 E5 m
  241. __STATIC_INLINE void __set_FPSCR(uint32_t fpscr)
    + N6 Y9 _6 h( ?* w0 C# [
  242. {
    ( {3 B9 ]& R  s: S  Y
  243. #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)" ]* O; j: z. K/ e! W1 v
  244.   register uint32_t __regfpscr         __ASM("fpscr");
    , O1 W) S! N. |" m2 J# E# c
  245.   __regfpscr = (fpscr);
    9 I+ S8 a2 \4 J% Y) {" S
  246. #endif
    : X1 j1 c) m9 [* V, h
  247. }
复制代码

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

/ o8 `  p0 `$ @) y& r" S, \% w2 M0 I) F! ]
; M) P' f/ \  A, d: V; I" G
参考资料:
1.    Patterns fortime-triggered embedded systems英文版和中文版
2.    Cortex-M3权威指南中文版
3.    TheDefinitive Guide to Arm Cortex-M3 and Cortex-M4 Processors(M4权威指南)
; H( O/ k. P6 q# w
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版