1. 程式人生 > >FreeRTOS系列第7篇---Cortex-M核心使用FreeRTOS特別注意事項

FreeRTOS系列第7篇---Cortex-M核心使用FreeRTOS特別注意事項

      在閱讀本文之前,有兩個定義在FreeRTOSConfig.h中的巨集,你必須先明白它們是什麼意思,《FreeRTOS核心配置說明》一文中,講解了這兩個巨集:

  • configKERNEL_INTERRUPT_PRIORITY
  • configMAX_SYSCALL_INTERRUPT_PRIORITY

       FreeRTOS與Cortex-M核心可謂是絕配,以至於讓移植和使用FreeRTOS都變得更簡單起來。根據FreeRTOS官方反饋,在Cortex-M核心上使用FreeRTOS大多數的問題點是由不正確的優先順序設定引起的。這個問題也是在意料之中的,因為儘管Cortex-M核心的中斷模式是非常強大的,但對於那些使用傳統中斷優先順序架構的工程師

來說,Cortex-M核心中斷機制也有點笨拙(或者是說使用比較繁瑣),並且違反直覺(這個主要是因為Cortex-M中斷優先順序數值越大代表的優先順序反而越小)。本章打算描述Cortex-M的中斷優先順序機制,並描述怎樣結合RTOS核心使用。

       說明:雖然Cortex-M核心的優先順序方案看上去比較複雜,但每一個官方釋出的FreeRTOS 介面包(在FreeRTOSV7.2.0\FreeRTOS\Source\portable資料夾中,一般為port.c)內都會有正確配置的演示例程,可以以此為參考。

1.有效優先順序

1.1Cortex-M 硬體詳述

       首先需要清楚有效優先順序的總數,這取決於微控制器製造商怎麼使用Cortex核心。所以,並不是所有的Cortex-M核心微處理器都具有相同的中斷優先順序級別。

       Cortex-M構架自身最多允許256級可程式設計優先順序(優先順序配置暫存器最多8位,所以優先順序範圍從0x00~0xFF),但是絕大多數微控制器製造商只是使用其中的一部分優先順序。比如,TI Stellaris Cortex-M3和Cortex-M4微控制器使用優先順序配置暫存器的3個位,能提供8級優先順序。再比如,NXP LPC17xx Cortex-M3微控制器使用優先順序配置暫存器的5個位,能提供32級優先順序。

1.2應用到RTOS

       RTOS中斷巢狀方案將有效的中斷優先順序分成兩組:一組可以通過RTOS臨界區遮蔽,另一組不受RTOS影響,永遠都是使能的。巨集configMAX_SYSCALL_INTERRUPT_PRIORITY在FreeRTOSConfig.h中配置,定義兩組中斷優先順序的邊界。邏輯優先順序高於此值的中斷不受RTOS影響。最優值取決於微控制器使用的優先順序配置暫存器的位數。

2.與數值相反的優先順序值和邏輯優先順序設定

2.1Cortex-M 硬體詳述

       有必要先解釋一下優先順序值和邏輯優先順序:在Cortex-M核心中,假如有8級優先順序,我們說優先順序值是0~7,但數值最大的優先順序7卻代表著最低的邏輯優先順序。很多使用傳統傳統中斷優先順序架構的工程師會覺得這樣比較繞,違反直覺。以下內容提到的優先順序要仔細區分是優先順序數值還是邏輯優先順序。

       接下來需要清楚的是,在Cortex-M核心中,一箇中斷的優先順序數值越低,邏輯優先順序卻越高。比如,中斷優先順序為2的中斷可以搶佔中斷優先順序為5的中斷,但反過來就不行。換句話說,中斷優先順序2比中斷優先順序5的優先順序更高。

       這是Cortex-M核心最容易讓人犯錯之處,因為大多數的非Cortex-M核心微控制器的中斷優先順序表述是與之相反的。

2.2應用到 RTOS

       以“FromISR”結尾的FreeRTOS函式是具有中斷呼叫保護的(執行這些函式會進入臨界區),但是就算是這些函式,也不可以被邏輯優先順序高於configMAX_SYSCALL_INTERRUPT_PRIORITY的中斷服務函式呼叫。(巨集configMAX_SYSCALL_INTERRUPT_PRIORITY定義在標頭檔案FreeRTOSConfig.h中)。因此,任何使用RTOSAPI函式的中斷服務例程的中斷優先順序數值大於等於configMAX_SYSCALL_INTERRUPT_PRIORITY巨集的值。這樣就能保證中斷的邏輯優先順序等於或低於configMAX_SYSCALL_INTERRUPT_PRIORITY。

       Cortex中斷預設情況下有一個數值為0的優先順序。大多數情況下0代表最高階優先順序。因此,絕對不可以在優先順序為0的中斷服務例程中呼叫RTOSAPI函式。

3.Cortex-M 內部優先順序概述

3.1Cortex-M 硬體詳述

       Cortex-M核心的中斷優先順序暫存器是以最高位(MSB)對齊的。比如,如果使用了3位來表達優先順序,則這3個位位於中斷優先順序暫存器的bit5、bit6、bit7位。剩餘的bit0~bit4可以設定成任何值,但為了相容,最好將他們設定成1.

       Cortex-M優先順序暫存器最多有8位,如果一個微控制器只使用了其中的3位,那麼這3位是以最高位對齊的,見下圖:


      某微控制器只使用了優先順序暫存器中的3位,下圖展示了優先順序數值5(二進位制101B)是怎樣在優先順序暫存器中儲存的。如果優先順序暫存器中未使用的位置1,下圖也展示了為什麼數值5(二進位制0000 0101B)可以看成數值191(二進位制1011 1111)的。


      某微控制器只使用了優先順序暫存器中的4位,下圖展示了優先順序數值5(二進位制101B)是怎樣在優先順序暫存器中儲存的。如果優先順序暫存器中未使用的位置1,下圖也展示了為什麼數值5(二進位制0000 0101B)可以看成數值95(二進位制0101 1111)的。


3.2應用到 RTOS

      上文中已經描述,那些在中斷服務例程中呼叫RTOS API函式的中斷邏輯優先順序必須低於或等於configMAX_SYSCALL_INTERRUPT_PRIORITY(低邏輯優先順序意味著高優先順序數值)。

      CMSIS以及不同的微控制器供應商提供了可以設定某個中斷優先順序的庫函式。一些庫函式的引數使用最低位對齊,另一些庫函式的引數可能使用最高位對齊,所以,使用時應該查閱庫函式的應用手冊進行正確設定。

      可以在FreeRTOSConfig.h中設定巨集configMAX_SYSCALL_INTERRUPT_PRIORITY和configKERNEL_INTERRUPT_PRIORITY的值。這兩個巨集需要根據Cortex-M核心自身的情況進行設定,要以最高有效位對齊。比如某微控制器使用中斷優先順序暫存器中的3位,設定configKERNEL_INTERRUPT_PRIORITY的值為5,則程式碼為:

#define     configKERNEL_INTERRUPT_PRIORITY  (5<<(8-3))

       巨集configKERNEL_INTERRUPT_PRIORITY指定RTOS核心使用的中斷優先順序,因為RTOS核心不可以搶佔使用者任務,因此這個巨集一般設定為硬體支援的最小優先順序。對於Cortex-M硬體,RTOS使用到硬體的PendSV和SysTick硬體中斷,在函式xPortStartScheduler()中(該函式在port.c中,由啟動排程器函式vTaskStartScheduler()呼叫),將PendSV和SysTick硬體中斷優先順序暫存器設定為巨集configKERNEL_INTERRUPT_PRIORITY指定的值。

       有關程式碼如下(位於port.c):

/*PendSV優先順序設定暫存器地址為0xe000ed22
 SysTick優先順序設定暫存器地址為0xe000ed23*/
#define portNVIC_SYSPRI2_REG     ( * ( ( volatile uint32_t * ) 0xe000ed20 ))
 
#define portNVIC_PENDSV_PRI ( ( (uint32_t)configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
#define portNVIC_SYSTICK_PRI ( ( (uint32_t)configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
/* …. */
/*確保PendSV 和SysTick為最低優先順序中斷 */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |=portNVIC_SYSTICK_PRI;

4.臨界區

4.1Cortex-M 硬體詳述

      RTOS核心使用Cortex-M核心的BASEPRI暫存器來實現臨界區(注:BASEPRI為優先順序遮蔽暫存器,優先順序數值大於或等於該暫存器的中斷都會被遮蔽,優先順序數值越大,邏輯優先順序越低,但是為零時不遮蔽任何中斷)。這允許RTOS核心可以只遮蔽一部分中斷,因此可以提供一個靈活的中斷巢狀模式。

      那些需要在中斷呼叫時保護的API函式,FreeRTOS使用暫存器BASEPRI實現中斷保護臨界區。當進入臨界區時,將暫存器BASEPRI的值設定成configMAX_SYSCALL_INTERRUPT_PRIORITY,當退出臨界區時,將暫存器BASEPRI的值設定成0。很多Bug反饋都提到,當退出臨界區時不應該將暫存器設定成0,應該恢復它之前的狀態(之前的狀態不一定是0)。但是Cortex-M NVIC決不會允許一個低優先順序中斷搶佔當前正在執行的高優先順序中斷,不管BASEPRI暫存器中是什麼值。與進入臨界區前先儲存BASEPRI的值,退出臨界區再恢復的方法相比,退出臨界區時將BASEPRI暫存器設定成0的方法可以獲得更快的執行速度。

4.2應用到RTOS kernel

      RTOS核心通過寫configMAX_SYSCALL_INTERRUPT_PRIORITY的值到BASEPRI暫存器的方法建立臨界區。中斷優先順序0(具有最高的邏輯優先順序)不能被BASEPRI暫存器遮蔽,因此,configMAX_SYSCALL_INTERRUPT_PRIORITY絕不可以設定成0。