【轉載】core_cm3文件函數一覽 (uCOS-II C嵌入彙編)

【原文地址:  http://blog.csdn.net/fovwin/article/details/11021155  】

個人筆記:

1. ARM有默認的規定,傳入的參數從左至右依次放入R0-R4中;

2. 請參考《編譯器用戶指南》和《ARM Cortex-M3權威指南》;

------------------------------------------------------------------------------------------------------

       core_cm3是ARM公司推出來的統一規定,這是對下游芯片廠商的統一規定,因此可以再Cortex-M3(CM3)之間進行移植。此文件中定義了一些對特殊功能寄存器的C語言形式的操作,本質上是內斂彙編和嵌入式彙編。本文均已μC/OS-II移植爲例進行舉例。


       那麼先通過幾個例子介紹下內斂彙編和嵌入式彙編的形式吧,,因爲下面要用到,看完這幾個例子就能看懂了,但是如果需要詳細學習,請參考文末的參考資料,因爲與真正的彙編還是有不少區別的,比如在內斂彙編中我們操作的都是虛擬寄存器(那麼它是如何轉到真正的寄存器的呢?不曉得,⊙﹏⊙b汗),“pc(r15)、lr(r14) 和 sp(r13) 寄存器根本不能訪問。訪問這些寄存器時,會產生錯誤消息。”等等。
1. 單行內斂彙編
 C++ Code 
1
2
#define OS_ENTER_CRITICAL()         __asm("CPSID   I")
#define OS_EXIT_CRITICAL()          __asm("CPSIE   I")
主要是小括號+雙引號。

2.多行內斂彙編
 C++ Code 
1
2
3
4
__asm
{
    //原汁原味的彙編語句
}
用大括號取代了小括號,並且不需要雙引號了,直接加上就可以了。不過要是用到R0寄存器不聲明的話,會有個warning,可以在前面int R0聲明下。
這裏用的時候我產生了一個小疑問,當我用到R0,R1這些寄存器的時候,需不需要先PUSH,用完之後在POP呢?看完《編譯器用戶指南》之後,它說了,不用,編譯器幫我們做了。

3.嵌入式彙編
 C++ Code 
1
2
3
4
5
__asm uint32_t __get_PSP(void)
{
  mrs r0, psp
  bx lr
}
在前面加上__asm關鍵字即可。不過要注意的是需要在最後加上bx lr顯式返回,我之前就忘了返回,然後就HardFault_Handler了。


直接看代碼:
 編譯器廠商宏定義選擇
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* define compiler specific symbols */
#if defined ( __CC_ARM   )
  #define __ASM            __asm                                      /*!< asm keyword for ARM Compiler          */
  #define __INLINE         __inline                                   /*!< inline keyword for ARM Compiler       */

#elif defined ( __ICCARM__ )
  #define __ASM           __asm                                       /*!< asm keyword for IAR Compiler          */
  #define __INLINE        inline                                      /*!< inline keyword for IAR Compiler. Only avaiable in High optimization mode! */

#elif defined   (  __GNUC__  )
  #define __ASM            __asm                                      /*!< asm keyword for GNU Compiler          */
  #define __INLINE         inline                                     /*!< inline keyword for GNU Compiler       */

#elif defined   (  __TASKING__  )
  #define __ASM            __asm                                      /*!< asm keyword for TASKING Compiler      */
  #define __INLINE         inline                                     /*!< inline keyword for TASKING Compiler   */

#endif
就我所知,對ARM芯片進行編程,市面上大抵有三款編譯器可供選擇:ARM自家的ARM Compiler,第三方的IAR Compiler和GNU針對ARM的Compiler。最後一個木有見過,TASKING Compiler  Embedded software development tools)?
不同的編譯器具有的不同關鍵字形式,不過貌似就ARM自家的內斂關鍵字的前面多兩個下劃線,但是人家形式看上去更加統一一點,這對於有“強迫症”的工程師是不錯的。

然後就是針對不同編譯器的函數定義,也是通過與以上形式一致的方式來實現的,給出個框架先。用哪個編譯器就用編譯相對應的代碼即可。
 core_cm3.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

/* ###################  Compiler specific Intrinsics  ########################### */

#if defined ( __CC_ARM   ) /*------------------RealView Compiler -----------------*/
/* ARM armcc specific functions */


#elif (defined (__ICCARM__)) /*------------------ ICC Compiler -------------------*/
/* IAR iccarm specific functions */


#elif (defined (__GNUC__)) /*------------------ GNU Compiler ---------------------*/


#elif (defined (__TASKING__)) /*------------------ TASKING Compiler ---------------------*/
/* TASKING carm specific functions */


/*
 * The CMSIS functions have been implemented as intrinsics in the compiler.
 * Please use "carm -?i" to get an up to date list of all instrinsics,
 * Including the CMSIS ones.
 */


#endif


然後針對ARM Compiler形式的函數進行討論,即對/* ARM armcc specific functions */討論下。
 進程堆棧:獲取PSP和設置PSP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * @brief  Return the Process Stack Pointer
 *
 * @return ProcessStackPointer
 *
 * Return the actual process stack pointer
 */

__ASM uint32_t __get_PSP(void)
{
  mrs r0, psp
  bx lr
}

/**
 * @brief  Set the Process Stack Pointer
 *
 * @param  topOfProcStack  Process Stack Pointer
 *
 * Assign the value ProcessStackPointer to the MSP 
 * (process stack pointer) Cortex processor register
 */

__ASM void __set_PSP(uint32_t topOfProcStack)
{
  msr psp, r0
  bx lr
}
這裏的兩個函數都是用R0的原因是ARM有默認的規定,傳入的參數從左至右依次放入R0-R4中,這裏也就是寫C函數的時候,輸入的參數不要過多,不然得PUSH到棧中,速度就下來;如果有返回值,則將R0的值返回。
這裏放一個自己發現的此函數的一個小例子,在CM3上移植μC/OS-II時,需要移植首次啓動時-OSStart函數中最後調用的OSStartHighRdy,一般採用彙編編寫,如下所示,不想看的直接跳過看下面的中文描述也可:
 os_cpu_a.asm:OSStartHighRdy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
;定義幾個常量,類似C語言中的#define預處理指令。     
NVIC_INT_CTRL       EQU     0xE000ED04  ; 中斷控制寄存器
NVIC_SYSPRI14       EQU     0xE000ED22  ; PendSV優先級寄存器的地址
NVIC_PENDSV_PRI     EQU     0x000000FF  ; PendSV中斷的優先級爲255(最低)
NVIC_PENDSVSET      EQU     0x10000000  ; 觸發軟件中斷的值,位28爲1.
;********************************************************************************************************
;                                         START MULTITASKING
;                                      void OSStartHighRdy(void)
;
; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause
;              the first task to start.
;
;           2) OSStartHighRdy() MUST:
;              a) Setup PendSV exception priority to lowest;
;              b) Set initial PSP to 0, to tell context switcher this is first run;
;              c) Set the main stack to OSRunning
;              d) Trigger PendSV exception;
;              e) Enable interrupts (tasks will run with interrupts enabled).
;********************************************************************************************************
OSStartHighRdy
        ;設置PendSV中斷的優先級
        LDR     R4, =NVIC_SYSPRI14      ; set the PendSV exception priority
        LDR     R5, =NVIC_PENDSV_PRI
        STR     R5, [R4]
        ;設置PSP爲0
        MOV     R4, #0                 ; set the PSP to 0 for initial context switch call
        MSR     PSP, R4
        ;設置OSRunning爲TRUE
        LDR     R4, =OSRunning         ; OSRunning = TRUE
        MOV     R5, #1
        STRB    R5, [R4]

        ;觸發PendSV中斷
        LDR     R4, =NVIC_INT_CTRL     ;rigger the PendSV exception (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]

        CPSIE   I                      ;enable interrupts at processor level
        ;死循環,應該不會到這裏
OSStartHang
        B       OSStartHang            ;should never get here
轉換成中文描述過程如下:
1. 設置PendSV優先級
2. 設置PSP爲0
3. 設置系統的運行狀態
4. 開始執行最高優先級任務
5. 開中斷
補充說明下:關於第1點,參考權威指南討論PendSV即可;關於第2點,其實不設置也是可以的,它的作用是省掉第一次上下文切換時候的R4-R11的入棧保護,僅此而已,這是事實沒錯,但是這個考慮會增加代碼的編寫,體現在此處和PendSV中斷函數的編寫上,不過這也表明作者的對OS過程的認識,思維的嚴謹。關於第5點,顯示聲明中斷要開着,沒有也沒關係,因爲本來中斷就是開着的,只要你不蛋疼的去把它關掉。

改成C語言形式如下:
 os_cpu_c.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define  NVIC_INT_CTRL              *((OS_CPU_SR *)0xE000ED04)  //中斷控制寄存器ICSR
#define  NVIC_PENDSVSET             0x10000000  //觸發軟件中斷的值,位28爲1.
#define  OS_TASK_SW()               NVIC_INT_CTRL = NVIC_PENDSVSET
#define  OSIntCtxSw()               NVIC_INT_CTRL = NVIC_PENDSVSET

#define OS_ENTER_CRITICAL()         __asm("CPSID   I");
#define OS_EXIT_CRITICAL()          __asm("CPSIE   I");

#define NVIC_SYSPRI14               *((OS_CPU_SR *)0xE000ED22)  //PendSV優先級寄存器的地址
#define NVIC_PENDSV_PRI             0x000000FF                  //PendSV中斷的優先級爲255(最低)
#define SET_PENDSV_FF()             NVIC_SYSPRI14=NVIC_PENDSV_PRI

void OSStartHighRdy(void)
{   
    SET_PENDSV_FF();         //這裏寫FF是因爲把它的中斷優先級設置爲最低255
    __set_PSP(0);
    OSRunning = 1;
    OS_TASK_SW();
    OS_EXIT_CRITICAL();
}

怎麼樣,優雅多了吧,而且也比較明瞭。此處我把它放在os_cpu_c.c文件中,當然一般的宏定義還是會放在頭文件oc_cpu.h中的。
要是與彙編形式的再像一點,在最後加上死循環while(1);則完全一致了,只不過不會在這裏的,因爲它馬上就去優先級最高的任務那裏去了。
這裏還要說一下,在我們板子啓動的時候,使用的MSP,要是我們使用前後臺的形式來開發程序,不管在用戶程序還是中斷程序裏面,我們就用一個MSP就ok了,因爲就相當於一個線程在那裏不停的跑。而當我們使用RTOS,如μC/OS-II,那麼我們在所有的用戶任務中,使用的PSP,這也是我之前沒注意的地方,

扯遠了,看下一個:
 主堆棧:獲取MSP和設置MSP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * @brief  Return the Main Stack Pointer
 *
 * @return Main Stack Pointer
 *
 * Return the current value of the MSP (main stack pointer)
 * Cortex processor register
 */

__ASM uint32_t __get_MSP(void)
{
  mrs r0, msp
  bx lr
}

/**
 * @brief  Set the Main Stack Pointer
 *
 * @param  topOfMainStack  Main Stack Pointer
 *
 * Assign the value mainStackPointer to the MSP 
 * (main stack pointer) Cortex processor register
 */

__ASM void __set_MSP(uint32_t mainStackPointer)
{
  msr msp, r0
  bx lr
}
使用尚待開發。


 反轉無符號16位值和有符號16位值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * @brief  Reverse byte order in unsigned short value
 *
 * @param   value  value to reverse
 * @return         reversed value
 *
 * Reverse byte order in unsigned short value
 */

__ASM uint32_t __REV16(uint16_t value)
{
  rev16 r0, r0
  bx lr
}

/**
 * @brief  Reverse byte order in signed short value with sign extension to integer
 *
 * @param   value  value to reverse
 * @return         reversed value
 *
 * Reverse byte order in signed short value with sign extension to integer
 */

__ASM int32_t __REVSH(int16_t value)
{
  revsh r0, r0
  bx lr
}
使用尚待開發。


 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
 * @brief  Remove the exclusive lock created by ldrex
 *
 * Removes the exclusive lock which is created by ldrex.
 */

__ASM void __CLREX(void)
{
  clrex
}

/**
 * @brief  Return the Base Priority value
 *
 * @return BasePriority
 *
 * Return the content of the base priority register
 */

__ASM uint32_t  __get_BASEPRI(void)
{
  mrs r0, basepri
  bx lr
}

/**
 * @brief  Set the Base Priority value
 *
 * @param  basePri  BasePriority
 *
 * Set the base priority register
 */

__ASM void __set_BASEPRI(uint32_t basePri)
{
  msr basepri, r0
  bx lr
}

/**
 * @brief  Return the Priority Mask value
 *
 * @return PriMask
 *
 * Return state of the priority mask bit from the priority mask register
 */

__ASM uint32_t __get_PRIMASK(void)
{
  mrs r0, primask
  bx lr
}

/**
 * @brief  Set the Priority Mask value
 *
 * @param  priMask  PriMask
 *
 * Set the priority mask bit in the priority mask register
 */

__ASM void __set_PRIMASK(uint32_t priMask)
{
  msr primask, r0
  bx lr
}

/**
 * @brief  Return the Fault Mask value
 *
 * @return FaultMask
 *
 * Return the content of the fault mask register
 */

__ASM uint32_t  __get_FAULTMASK(void)
{
  mrs r0, faultmask
  bx lr
}

/**
 * @brief  Set the Fault Mask value
 *
 * @param  faultMask  faultMask value
 *
 * Set the fault mask register
 */

__ASM void __set_FAULTMASK(uint32_t faultMask)
{
  msr faultmask, r0
  bx lr
}

/**
 * @brief  Return the Control Register value
 * 
 * @return Control value
 *
 * Return the content of the control register
 */

__ASM uint32_t __get_CONTROL(void)
{
  mrs r0, control
  bx lr
}

/**
 * @brief  Set the Control Register value
 *
 * @param  control  Control value
 *
 * Set the control register
 */

__ASM void __set_CONTROL(uint32_t control)
{
  msr control, r0
  bx lr
}
以上的嵌入彙編可以拿來對照權威指南學習,看看寄存器的變化過程以及造成的影響,主要是P14頁的”特殊功能寄存器“中的那些個特殊功能寄存器。使用尚待開發。




如果文中有描述不清楚或者錯誤的地方,請參考《編譯器用戶指南》和《ARM Cortex-M3權威指南》,並以此爲準。

另外,雖然μC/OS-II的移植已經被講爛了,但是看了這個還是有點搞頭的,新版的正在緊張改寫中,它更加簡潔明瞭。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章