S32 SDK軟體程式設計思想詳解

2020-08-10 15:13:51

內容提要

 

        引言

        1. S32 SDK的軟體分層架構介紹

            1.1 外設驅動層(PD--Periperal Driver)

            1.2 外設抽象層(PAL--Peripheral Abstract Layer)

            1.3 中介軟體層(Middleware)

            1.4 實時操作系統層(RTOS)

            1.5 S32 SDK底層驅動軟體分層的優點

        2.  S32 SDK的外設驅動元件全域性狀態結構體解析

        3. S32 SDK外設底層驅動中斷處理機制 機製詳解

        4. S32 SDK的通訊外設驅動元件API函數的阻塞與非阻塞發送與接收API函數差別與使用詳解;

        5.  S32 SDK的外設驅動與RTOS之間的互動方式

        總結

 

    引言

 

      S32 SDK(Software Development Kit,軟件開發套件)借鑑了AutoSAR軟體和ARM Cortext M系列MCU軟體介面標準(CMSIS--Cortex Microcontroller Software Interface Standard)的程式設計思想,通過對內核和底層外設硬體驅動的抽象化,對應用層提供統一的API,不但大幅度地降低了MCU使用者的軟件開發難度和程式設計門檻,同時也大大提高程式碼的重用率和可移植性及可讀性。使得基於MCU的嵌入式軟體程式設計變得更加容易。

 

      然而,相較於使用傳統MCU--比如51微控制器、S08和S12(X) MCU的底層軟件開發, S32 SDK的軟體程式設計模式和程式設計思想發生了很大的變化, 以適應日益複雜的MCU內核和外設資源。如果之前沒有接觸過系統的軟體架構和軟體程式設計思想知識體系的學習,也未使用過AutoSAR 軟體和CMSIS庫,則在使用S32 SDK時,將會發現很多困惑,不知道如何在應用層高效地呼叫SDK的API、如何通過事件通知(Event  Notification)與底層驅動進行互動,以及如何在使用RTOS的系統中呼叫SDK提供的底層驅動API等?

 

鑑於此,本文將以S32K SDK爲例,詳細介紹S32 SDK的軟體程式設計思想,以爲大家解開以上疑惑,幫助大家更好的使用S32 SDK並真正理解其程式設計優勢。

 

 1. S32 SDK的軟體分層架構介紹

 

    

S32 SDK的軟體架構如下圖所示,從上一篇文章--《S32K SDK使用詳解之S32 SDK軟體架構詳解》解讀中(點選文章標題即可快速跳轉閱讀),

可以看到S32 SDK除了提供底層驅動(LLD--Low Level Drivers)和中介軟體(Middleware)之外,還提供了不同工具鏈編譯器的啓動(Start-up)和鏈接檔案(Linker filers) ,移植好的FreeRTOS和RTOS介面層(OSIF)以及處理器專家(Processor Expert)設定GUI和豐富的demo程式和詳細的幫助文件:

 

        具體來看,S32 SDK的軟體分層情況如下:

 

        1.1 外設驅動層(PD--Periperal Driver)

 

        PD層元件通過呼叫對外設暫存器操作的硬體存取層(Hardware Access)API(一般爲宏定義或者inline函數,且爲靜態static程式碼,僅PD層檔案內部使用),讀寫不同S32系列MCU底層硬體外設IP模組暫存器,從而完成對外設的初始化設定、狀態讀取--即硬體互動;

 

        在此基礎上,根據不同的外設模組功能特性,編寫相應的初始化函數、功能設定/使用函數以及中斷處理常式(ISR),從而構成完成的外設底層驅動。這裏麪包含了很多外設初始化設定的時序/順序要求(設定外設的initialization狀態、Freeze狀態、STOP、Standby/Shutdown以及nomal work狀態)和特定暫存器的操作許可權管理(比如管理員模式還是使用者模式可以讀寫許可權)、和暫存器操作方式控制(比如清除外設中斷標誌的方法是寫1清零還是讀清零)等。

 

        Tips: S32 SDK的PD層包含了所支援MCU的幾乎所有片上外設底層驅動,由於其程式碼直接操作外設IP暫存器,所以其效率是最高的,但不能實現跨MCU平臺/系列使用。 如下爲S32K SDK的PD層外設驅動元件列表:

 

 

        1.2 外設抽象層(PAL--Peripheral Abstract Layer)

 

        在PD層的基礎上, S32 SDK封裝了一個外設抽象層,將能夠實現相同功能的外設PD元件封裝爲一個PAL功能元件,比如實現UART序列通訊的外設,在S32K1xx系列上硬體外設爲LPUART,在Qorivva MPC57xx和S32V234上則是LINFlex,在PAL就抽象了一個UART_PAL,對應用層提供統一的API函數,從而對應用層軟件開發人員遮蔽了底層硬體外設的差別:

 

        Tips: 使用S32 SDK的PAL層能夠實現程式碼的跨平臺移植和使用,典型的PAL元件包括CAN、UART、I2C、SPI和I2C等通訊功能外設模組,PWM、OC及TIMING等定時器功能模組,以及MPU/MMU記憶體保護/管理功能模組以及看門狗和資訊保安等系統級功能模組。 如下爲S32K SDK的PD層外設驅動元件列表:

 

        1.3 中介軟體層(Middleware)

 

        S32 SDK的中介軟體主要包括三大類:①功能靜態庫--比如電機控制庫(AMMCLib)、觸控感應庫(Touch Sensing)等和②軟體協定棧(stack)--比如乙太網協定棧(TCP/IP)、LIN協定棧和USB及檔案系統等,以及③ 複雜外圍晶片驅動程式--比如SBC(UJA1169和UJA113x)系列驅動程式。

 

        Tips: 如下爲S32K SDK的Middleware元件列表,其中Touch Sense, ISELED和NFC Middleware需要聯繫NXP Sale/FAE申請license,在安裝相應的SDK版本之後單獨下載安裝:

 

        1.4 實時操作系統層(RTOS)

 

        爲了管理衆多的MCU外設資源和記憶體資源並進行實時的應用程式任務排程,在S32 SDK中還移植了實時操作系統--FreeRTOS,並未裸寫程式碼提供了操作系統介面服務(Osif元件)。

 

        Tips: 如下爲S32K SDK的提供的FreeRTOS,在S32K SDK RTM 2.0.0中是FreeRTOS的版本爲v8.2.1,在最新的Beta 2.9.0(及更高版本)中已經update到FreeRTOS v10.0.1了。

 

        1.5 S32 SDK底層驅動軟體分層的優點

 

        綜上所述,S32 SDK提供的底層外設驅動分層,具有如下優點:

 

        ① 分層提高了程式碼的可讀性;

 

        ② 使用PD層,對保證對外設模組的操作實時性和程式碼執行效率;

 

        ③ 通過PAL層對底層硬體外設的抽象,對應用層提供了統一的API,保證了底層驅動程式碼在不同S32 MCU硬體平臺上的通用性,提高了底層驅動程式碼的可移植性和使用效率;

 

        ④ 整合豐富的中介軟體,極大地提高了S32 MCU平臺應用層軟體的可延伸性和易用性;

 

        ⑤ 通過OSIF隔離硬體外設底層驅動程式與RTOS內核,保證底層驅動對不同RTOS的支援;

 

        ⑥ 通過提供針對不同軟件開發工具鏈的啓動檔案(start-up)和鏈接檔案(linker file),支援主流工具鏈--比如GNU GCC(S32DS IDE自帶工具鏈)、DIAB、GHS和IAR等;

 

        2. S32 SDK的外設驅動元件全域性狀態結構體解析

 

        在S32 SDK中每一個外設驅動元件都有一個元件的全域性狀態結構體,以<元件/外設名>_state_t命名,用於存放該外設/元件的工作狀態和硬體資源使用資訊,主要包含以下資訊:

 

        ① 外設模組的設定資訊;

 

        ② 通訊外設收發器的工作狀態(isTxBbusy/isRxBusy);

 

        ③ 通訊外設收發器是否爲阻塞方式工作(isTxBlocking/isRxBlocking);

 

        ④ 中斷回撥函數指針(*callback)及參數(*callbackParam)定義;

 

        ⑤ 外設工作/通訊使用的信號(semaphore)(TxComplete/RxComplete)和通訊狀態(transmitStatus/receiveStatus);

 

        ⑥ 通訊外設收發數據的快取(txBuff/rxBuff)和大小(txSize/rxSize);

 

        ⑦ 外設使用的DMA通道號;

 

        以下是S32K SDK中LPUART的全域性狀態結構體lpuart_state_t的定義:

 

    而非通訊外設的時鐘管理器全域性狀態結構體clock_manager_state_t如下:

 

        Tips:通常在SDK外設驅動(PD)層定相應硬體外設的全域性狀態結構體結構體指針陣列,而在外設抽象層(PAL)爲其定義外設驅動工作時使用的全域性狀態結構體(如果包含多個相同的外設實體(instance),比如LPUART0/1/2或者FlexCAN0/1/2則爲結構體陣列,每個成員對應相應的外設實體):

 

        比如S32K SDK中在LPUART的外設驅動層定義其工作時的全域性狀態結構體指針陣列:

        而在外設抽象層中,才定義其使用的全域性狀態結構體陣列:

 

        FlexCAN/CAN_PAL的驅動也是如此:

  

      因此,如果使用PD層元件,使用者還需元件定義全域性狀態結構體,而使用PAL層元件時,其將會定義該外設要使用的全域性狀態結構體,無需使用者定義。

S32 SDK的LLD元件外設全域性狀態結構體在SDK元件的初始化API函數中被初始化,並與其全域性狀態結構體指針陣列關聯起來。比如在S32K SDK的LPUART驅動中,其全域性狀態結構體就是其初始化函數--LPUART_DRV_Init()中完成初始化的:

        當執行某一個SDK元件的反初始化函數(Deinit)時,其全域性狀態結構體也將被清除(指向空指針NULL)。比如在S32K SDK的LPUART驅動中,其反初始化函數--LPUART_DRV_Deinit()實現程式碼如下:

 

        每一個硬體外設模組工作時,只能有一個全域性狀態結構體與其對應,其使用生命週期是初始化完成(呼叫SDK元件Init()函數)到反初始化函數執行(呼叫SDK元件Deinit()函數)。

 

        Tips:使用S32 SDK的外設底層驅動(LLD)時,推薦大家使用PAL元件,而不是PD元件;

 

        Tips: S32 SDK的LLD元件外設全域性狀態結構體爲定義在MCU SARM靜態區(static)的全域性變數,其包含對應外設模組的完整工作狀態資訊,並實時記錄外設的工作狀態,是偵錯LLD時最爲重要的一個資訊源,建議將其新增到變數(variable/expression)視窗檢視以跟蹤偵錯問題。

 

        3. S32 SDK外設底層驅動中斷處理機制 機製詳解

 

        在基於MCU的嵌入式系統軟件開發中,外設中斷是保證系統軟體工作實時性和內核效率最重要的工作機制 機製----在外設事件(比如通訊外設的數據接收完成,GPIO狀態改變以及定時週期滿等),CPU內核將停止當前程式指令的執行,及時響應外設中斷請求並做相應的處理(比如搬移數據,填充緩衝,改變程式執行條件/狀態、啓用新任務等)。

 

        在傳統的嵌入式MCU底層外設驅動程式開發中,對於外設中斷的處理流程/機制 機製如下:

 

        ① POR復位後,在main()函數最開始完成系統時鐘初始化後,初始化外設模組時,就設定使能相應的外設中斷;

 

        ② 編寫中斷服務函數ISR,在其中處理中斷事件,置位全域性事件標誌通知應用程式做後續處理, 並清除中斷標誌;

 

        ③ 將中斷服務函數放到MCU的中斷向量表中,這一步可能是通過關鍵詞(interrupt)+中斷向量號 + ISR定義的方式實現,工具鏈的編譯器和鏈接器自動識別並載入到MCU中斷向量表中;

 

        ④ 使能全域性中斷;

 

        在整個MCU正常工作期間,除非特殊應用需要,不會關心外設中斷的關閉與使能,

 

        而在S32 SDK中,其進行瞭如下優化:

 

        ①外設初始化Init()函數中,僅使能晶片級中斷控制器中對應的中斷源(比如S32K的ARM Cortex M系列內核的NVIC,和Qorivva MPC57xx/S32R的PowerPC e200系列內核的INTC模組)並不使能相應外設的IP級中斷(比如LPUART的TX和RX以及ERROR中斷);

 

        在具體使用外設的特定功能(比如LPUART的數據發送和接收)時,才使能該特定功能的中斷(比如LPUART的發送中斷和接收中斷),在中斷髮生後,若相應的中斷已經完全發生(比如發送10位元組數據,全部發送完成),則在ISR的最後關閉該特定功能中斷;

 

        這樣做的好處是,避免意外的中斷浪費寶貴的CPU資源,比如由於程式跑飛導致的LPUART意外數據發送和接收;

 

        ② 在S32 SDK的PD層爲絕大多數硬體外設模組提供了中斷ISR函數,(對應S32K SDK來說,僅PD層的LPIT元件和GPIO的IRQ中斷,需要使用者自己使能NVIC中斷並編寫自己的中斷ISR)無需使用者自己編寫;

 

        在S32 SDK的元件中,相同外設的不同實體(比如LPUART0/1/2)共用同一個中斷ISR,依靠外設實體號(instance)來區分不同的處理,由於相同外設的工作流程和中斷處理流程相同,這樣的好處是可以提高程式碼的複用效率:

如下是S32K144的SDK程式碼,其有LPUART0/1/2三個串列埠外設,其中斷ISR定義如下:

 

        而在真正的LPUART中斷處理常式中依靠不同的instance來區別:

 

        ③ S32 SDK預設在startup過程中將外設中斷向量表拷貝到SARM中,這樣執行使用者可以在程式執行時動態的修改中斷ISR;

 

        ④ S32 SDK中爲大部分外設都提供了中斷處理常式(ISR)—位於外設驅動PD的中斷處理檔案(<Peripheral_Name>_irq.c/h)中,並在外設初始化時設定了相應的中斷,其外設中斷ISR屬於SDK PD層驅動非常重要的一部分程式碼,不允許使用者修改,因此提供了在外設中斷ISR中呼叫使用者中斷回撥函數的方式給使用者提供中斷處理介面。

 

        Tips:在使用者中斷回撥函數中,無需使用者自己清除外設中斷標誌,因爲在SDK提供的外設中斷ISR中已經做了相應的處理。只有需要使用者呼叫SDK中斷管理器(interrupt_manager)的API--INT_SYS_InstallHandler()手動註冊安裝的外設中斷ISR,才需要使用者自己清外設中斷標誌,比如典型的GPIO的IRQ中斷和PIT定時器中斷。

 

        Tips:使用PD層外設驅動元件,需要使用者自己呼叫中斷回撥函數API自己安裝中斷Callback:

 

        而若使用PAL層外設驅動元件,則可以在其Processor Expert圖形化設定介面中新增中斷Callback函數名,然後再使用者程式碼中實現該Callback即可,無需使用者自己呼叫中斷回撥函數安裝API。

 

        4. S32 SDK的通訊外設驅動元件API函數的阻塞與非阻塞發送與接收API函數差別與使用詳解;

 

        在S32 SDK,通訊外設元件都提供了阻塞(Blocking)與非阻塞兩類發送與接收API函數,比如:

 

        UART_PAL元件的API函數:

 

        CAN_PAL元件的API函數:

 

        其差別如下:

 

        非阻塞類數據收發API,只是將要發送的數據buffer和長度size設定到對應的通訊外設驅動的全域性狀態結構體中,使能數據發送器(transmiter)和接收器(receiver),開啓收發和錯誤中斷/若使用DMA收發,則設定相應的DMA通道TCD,使能DMA中斷,安裝中斷回撥函數,啓動DMA傳輸, 然後就立即返回了,因此非阻塞類API的返回值總是成功(STATUS_SUCCESS)。

 

        比如S32K SDK的LPUART非阻塞發送API--LPUART_DRV_SendData() 實現如下:

 

        而阻塞(Blocking)類數據收發API,函數參數多了一個超時timeout(單位爲ms)則在完成以上非阻塞API的工作後,還會呼叫OSIF元件的API函數--OSIF_SemaWait()檢查外設驅動全域性狀態結構體中的數據收發號志(semaphore),等待數據收發完成,若在設定的timeout時間內未完成數據傳輸,則返回超時(STATUS_TIMEOUT),若在timeout時間內數據收發完成,則返回成功(STATUS_SUCCESS)。

 

        比如S32K SDK的LPUART阻塞(blocking)發送API--LPUART_DRV_SendDataBlocking()實現如下:

       

        Tips:結合前面S32 SDK外設底層驅動中斷處理機制 機製的介紹可知,無論呼叫阻塞函數非阻塞數據收發函數,在成功接收完指定長度的數據後,都會將通訊外設的接收器關閉,若想通訊外設一直接收數據,則需要在該驅動元件的中斷回撥函數Callback中,設定新的數據接收buffer,從而避免在中斷ISR最後,驅動程式關閉數據接收器:

 

        比如使用S32K的LPUART接收GPS數據時,相應的數據接收中斷回撥函數定義如下,其中使用雙buffer,在一個buffer接收完成後,呼叫UART_SetRxBuffer()切換新的buffer繼續接收數據:

 

        Tips:由於在阻塞API函數中,呼叫了OSIF元件的API函數--OSIF_SemaWait()檢查外設驅動全域性狀態結構體中的數據收發號志(semaphore), 若在應用工程中使用了RTOS,比如FreeRTOS,則呼叫該阻塞API函數的任務將被阻塞,讓出CPU資源執行其他就緒的高優先順序認爲,直至收據收發完成(號志釋放)或者超時,這樣CPU的執行效率將大大提高;而若應用工程中沒有使用RTOS,則阻塞API函數將一直佔有CPU,造成CPU資源浪費;

綜上, 若應用工程使用了RTOS,則推薦使用阻塞API函數;若應用工程未使用RTOS,則推薦使用非阻塞API函數,並配閤中斷回撥函數,判斷數據收發狀態並做相應處理。

 

        5.  S32 SDK的外設驅動與RTOS之間的互動方式

 

        在S32 SDK中,所有的外設驅動(PD和PAL元件)以及中介軟體(Middleware)的軟體程式碼實現,都使用了OSIF元件提供的操作系統介面API對外設驅動工作中的數據進行同步和事件時間延遲,從而保證了S32 SDK程式碼在不同RTOS之間的相容:

        其中使用的號志都在相應的元件全域性狀態結構體中定義,在元件初始化(元件_Init()函數)時,呼叫了OSIF元件的API函數--OSIF_SemaCreate()建立,比如S32K SDK的LPUART驅動用於數據收發的號志rxComplete/txComplete,就在其初始化函數LPUART_DRV_Init()中建立的:

        在元件的阻塞(Blocking)API函數中呼叫OSIF元件的API函數--OSIF_SemaWait()檢查外設驅動全域性狀態結構體中定義的號志(semaphore)以完成事件同步。比如S32K SDK的LPUART驅動中數據發送完成號志txComplete,就在其阻塞數據發送API函數LPUART_SendDataBlocking()中使用的:

        在元件反初始化(元件_Deinit()函數)時,呼叫呼叫了OSIF元件的API函數--OSIF_SemaDestroy()釋放。比如S32K SDK的LPUART驅動用於數據收發的號志rxComplete/txComplete,就在其反初始化函數LPUART_DRV_Deinit()中被銷燬的:

        從以上分析可知,通過在SDK元件全域性狀態結構體中引入號志(Mutex/Semaphore)和使用操作系統介面OSIF元件API,既保證了S32 SDK外設底層驅動程式和中介軟體軟體工作的同步和效率,又提高了SDK軟體程式碼的在不同RTOS之間的可移植性,使用不同RTOS時,只需要關心和移植OSIF元件提供的API函數,即可保證S32 SDK外設底層驅動和中介軟體軟體程式碼在該RTOS中的正常工作。

 

        總結

 

        雖然我已經力求儘量把它寫的深入淺出,通俗易懂,閱讀本文介紹的知識要點仍然需要一定的程式設計基礎和嵌入式底層驅動程式開發經驗積累,或者使用過一段時間的S32 SDK軟體。不過沒關係,在後續的《S32K SDK使用詳解》系列技術分享文章中,我還將結合S32K1xx系列MCU的內核和外設功能特性,爲大家帶來更多的S32 SDK使用細節分解,希望大家可以持續關注和學習。

內容提要

 

        引言

        1. S32 SDK的軟體分層架構介紹

            1.1 外設驅動層(PD--Periperal Driver)

            1.2 外設抽象層(PAL--Peripheral Abstract Layer)

            1.3 中介軟體層(Middleware)

            1.4 實時操作系統層(RTOS)

            1.5 S32 SDK底層驅動軟體分層的優點

        2.  S32 SDK的外設驅動元件全域性狀態結構體解析

        3. S32 SDK外設底層驅動中斷處理機制 機製詳解

        4. S32 SDK的通訊外設驅動元件API函數的阻塞與非阻塞發送與接收API函數差別與使用詳解;

        5.  S32 SDK的外設驅動與RTOS之間的互動方式

        總結

 

    引言

 

      S32 SDK(Software Development Kit,軟件開發套件)借鑑了AutoSAR軟體和ARM Cortext M系列MCU軟體介面標準(CMSIS--Cortex Microcontroller Software Interface Standard)的程式設計思想,通過對內核和底層外設硬體驅動的抽象化,對應用層提供統一的API,不但大幅度地降低了MCU使用者的軟件開發難度和程式設計門檻,同時也大大提高程式碼的重用率和可移植性及可讀性。使得基於MCU的嵌入式軟體程式設計變得更加容易。

 

      然而,相較於使用傳統MCU--比如51微控制器、S08和S12(X) MCU的底層軟件開發, S32 SDK的軟體程式設計模式和程式設計思想發生了很大的變化, 以適應日益複雜的MCU內核和外設資源。如果之前沒有接觸過系統的軟體架構和軟體程式設計思想知識體系的學習,也未使用過AutoSAR 軟體和CMSIS庫,則在使用S32 SDK時,將會發現很多困惑,不知道如何在應用層高效地呼叫SDK的API、如何通過事件通知(Event  Notification)與底層驅動進行互動,以及如何在使用RTOS的系統中呼叫SDK提供的底層驅動API等?

 

鑑於此,本文將以S32K SDK爲例,詳細介紹S32 SDK的軟體程式設計思想,以爲大家解開以上疑惑,幫助大家更好的使用S32 SDK並真正理解其程式設計優勢。

 

 1. S32 SDK的軟體分層架構介紹

 

    

S32 SDK的軟體架構如下圖所示,從上一篇文章--《S32K SDK使用詳解之S32 SDK軟體架構詳解》解讀中(點選文章標題即可快速跳轉閱讀),

可以看到S32 SDK除了提供底層驅動(LLD--Low Level Drivers)和中介軟體(Middleware)之外,還提供了不同工具鏈編譯器的啓動(Start-up)和鏈接檔案(Linker filers) ,移植好的FreeRTOS和RTOS介面層(OSIF)以及處理器專家(Processor Expert)設定GUI和豐富的demo程式和詳細的幫助文件:

 

        具體來看,S32 SDK的軟體分層情況如下:

 

        1.1 外設驅動層(PD--Periperal Driver)

 

        PD層元件通過呼叫對外設暫存器操作的硬體存取層(Hardware Access)API(一般爲宏定義或者inline函數,且爲靜態static程式碼,僅PD層檔案內部使用),讀寫不同S32系列MCU底層硬體外設IP模組暫存器,從而完成對外設的初始化設定、狀態讀取--即硬體互動;

 

        在此基礎上,根據不同的外設模組功能特性,編寫相應的初始化函數、功能設定/使用函數以及中斷處理常式(ISR),從而構成完成的外設底層驅動。這裏麪包含了很多外設初始化設定的時序/順序要求(設定外設的initialization狀態、Freeze狀態、STOP、Standby/Shutdown以及nomal work狀態)和特定暫存器的操作許可權管理(比如管理員模式還是使用者模式可以讀寫許可權)、和暫存器操作方式控制(比如清除外設中斷標誌的方法是寫1清零還是讀清零)等。

 

        Tips: S32 SDK的PD層包含了所支援MCU的幾乎所有片上外設底層驅動,由於其程式碼直接操作外設IP暫存器,所以其效率是最高的,但不能實現跨MCU平臺/系列使用。 如下爲S32K SDK的PD層外設驅動元件列表:

 

 

        1.2 外設抽象層(PAL--Peripheral Abstract Layer)

 

        在PD層的基礎上, S32 SDK封裝了一個外設抽象層,將能夠實現相同功能的外設PD元件封裝爲一個PAL功能元件,比如實現UART序列通訊的外設,在S32K1xx系列上硬體外設爲LPUART,在Qorivva MPC57xx和S32V234上則是LINFlex,在PAL就抽象了一個UART_PAL,對應用層提供統一的API函數,從而對應用層軟件開發人員遮蔽了底層硬體外設的差別:

 

        Tips: 使用S32 SDK的PAL層能夠實現程式碼的跨平臺移植和使用,典型的PAL元件包括CAN、UART、I2C、SPI和I2C等通訊功能外設模組,PWM、OC及TIMING等定時器功能模組,以及MPU/MMU記憶體保護/管理功能模組以及看門狗和資訊保安等系統級功能模組。 如下爲S32K SDK的PD層外設驅動元件列表:

 

        1.3 中介軟體層(Middleware)

 

        S32 SDK的中介軟體主要包括三大類:①功能靜態庫--比如電機控制庫(AMMCLib)、觸控感應庫(Touch Sensing)等和②軟體協定棧(stack)--比如乙太網協定棧(TCP/IP)、LIN協定棧和USB及檔案系統等,以及③ 複雜外圍晶片驅動程式--比如SBC(UJA1169和UJA113x)系列驅動程式。

 

        Tips: 如下爲S32K SDK的Middleware元件列表,其中Touch Sense, ISELED和NFC Middleware需要聯繫NXP Sale/FAE申請license,在安裝相應的SDK版本之後單獨下載安裝:

 

        1.4 實時操作系統層(RTOS)

 

        爲了管理衆多的MCU外設資源和記憶體資源並進行實時的應用程式任務排程,在S32 SDK中還移植了實時操作系統--FreeRTOS,並未裸寫程式碼提供了操作系統介面服務(Osif元件)。

 

        Tips: 如下爲S32K SDK的提供的FreeRTOS,在S32K SDK RTM 2.0.0中是FreeRTOS的版本爲v8.2.1,在最新的Beta 2.9.0(及更高版本)中已經update到FreeRTOS v10.0.1了。

 

        1.5 S32 SDK底層驅動軟體分層的優點

 

        綜上所述,S32 SDK提供的底層外設驅動分層,具有如下優點:

 

        ① 分層提高了程式碼的可讀性;

 

        ② 使用PD層,對保證對外設模組的操作實時性和程式碼執行效率;

 

        ③ 通過PAL層對底層硬體外設的抽象,對應用層提供了統一的API,保證了底層驅動程式碼在不同S32 MCU硬體平臺上的通用性,提高了底層驅動程式碼的可移植性和使用效率;

 

        ④ 整合豐富的中介軟體,極大地提高了S32 MCU平臺應用層軟體的可延伸性和易用性;

 

        ⑤ 通過OSIF隔離硬體外設底層驅動程式與RTOS內核,保證底層驅動對不同RTOS的支援;

 

        ⑥ 通過提供針對不同軟件開發工具鏈的啓動檔案(start-up)和鏈接檔案(linker file),支援主流工具鏈--比如GNU GCC(S32DS IDE自帶工具鏈)、DIAB、GHS和IAR等;

 

        2. S32 SDK的外設驅動元件全域性狀態結構體解析

 

        在S32 SDK中每一個外設驅動元件都有一個元件的全域性狀態結構體,以<元件/外設名>_state_t命名,用於存放該外設/元件的工作狀態和硬體資源使用資訊,主要包含以下資訊:

 

        ① 外設模組的設定資訊;

 

        ② 通訊外設收發器的工作狀態(isTxBbusy/isRxBusy);

 

        ③ 通訊外設收發器是否爲阻塞方式工作(isTxBlocking/isRxBlocking);

 

        ④ 中斷回撥函數指針(*callback)及參數(*callbackParam)定義;

 

        ⑤ 外設工作/通訊使用的信號(semaphore)(TxComplete/RxComplete)和通訊狀態(transmitStatus/receiveStatus);

 

        ⑥ 通訊外設收發數據的快取(txBuff/rxBuff)和大小(txSize/rxSize);

 

        ⑦ 外設使用的DMA通道號;

 

        以下是S32K SDK中LPUART的全域性狀態結構體lpuart_state_t的定義:

 

    而非通訊外設的時鐘管理器全域性狀態結構體clock_manager_state_t如下:

 

        Tips:通常在SDK外設驅動(PD)層定相應硬體外設的全域性狀態結構體結構體指針陣列,而在外設抽象層(PAL)爲其定義外設驅動工作時使用的全域性狀態結構體(如果包含多個相同的外設實體(instance),比如LPUART0/1/2或者FlexCAN0/1/2則爲結構體陣列,每個成員對應相應的外設實體):

 

        比如S32K SDK中在LPUART的外設驅動層定義其工作時的全域性狀態結構體指針陣列:

        而在外設抽象層中,才定義其使用的全域性狀態結構體陣列:

 

        FlexCAN/CAN_PAL的驅動也是如此:

  

      因此,如果使用PD層元件,使用者還需元件定義全域性狀態結構體,而使用PAL層元件時,其將會定義該外設要使用的全域性狀態結構體,無需使用者定義。

S32 SDK的LLD元件外設全域性狀態結構體在SDK元件的初始化API函數中被初始化,並與其全域性狀態結構體指針陣列關聯起來。比如在S32K SDK的LPUART驅動中,其全域性狀態結構體就是其初始化函數--LPUART_DRV_Init()中完成初始化的:

        當執行某一個SDK元件的反初始化函數(Deinit)時,其全域性狀態結構體也將被清除(指向空指針NULL)。比如在S32K SDK的LPUART驅動中,其反初始化函數--LPUART_DRV_Deinit()實現程式碼如下:

 

        每一個硬體外設模組工作時,只能有一個全域性狀態結構體與其對應,其使用生命週期是初始化完成(呼叫SDK元件Init()函數)到反初始化函數執行(呼叫SDK元件Deinit()函數)。

 

        Tips:使用S32 SDK的外設底層驅動(LLD)時,推薦大家使用PAL元件,而不是PD元件;

 

        Tips: S32 SDK的LLD元件外設全域性狀態結構體爲定義在MCU SARM靜態區(static)的全域性變數,其包含對應外設模組的完整工作狀態資訊,並實時記錄外設的工作狀態,是偵錯LLD時最爲重要的一個資訊源,建議將其新增到變數(variable/expression)視窗檢視以跟蹤偵錯問題。

 

        3. S32 SDK外設底層驅動中斷處理機制 機製詳解

 

        在基於MCU的嵌入式系統軟件開發中,外設中斷是保證系統軟體工作實時性和內核效率最重要的工作機制 機製----在外設事件(比如通訊外設的數據接收完成,GPIO狀態改變以及定時週期滿等),CPU內核將停止當前程式指令的執行,及時響應外設中斷請求並做相應的處理(比如搬移數據,填充緩衝,改變程式執行條件/狀態、啓用新任務等)。

 

        在傳統的嵌入式MCU底層外設驅動程式開發中,對於外設中斷的處理流程/機制 機製如下:

 

        ① POR復位後,在main()函數最開始完成系統時鐘初始化後,初始化外設模組時,就設定使能相應的外設中斷;

 

        ② 編寫中斷服務函數ISR,在其中處理中斷事件,置位全域性事件標誌通知應用程式做後續處理, 並清除中斷標誌;

 

        ③ 將中斷服務函數放到MCU的中斷向量表中,這一步可能是通過關鍵詞(interrupt)+中斷向量號 + ISR定義的方式實現,工具鏈的編譯器和鏈接器自動識別並載入到MCU中斷向量表中;

 

        ④ 使能全域性中斷;

 

        在整個MCU正常工作期間,除非特殊應用需要,不會關心外設中斷的關閉與使能,

 

        而在S32 SDK中,其進行瞭如下優化:

 

        ①外設初始化Init()函數中,僅使能晶片級中斷控制器中對應的中斷源(比如S32K的ARM Cortex M系列內核的NVIC,和Qorivva MPC57xx/S32R的PowerPC e200系列內核的INTC模組)並不使能相應外設的IP級中斷(比如LPUART的TX和RX以及ERROR中斷);

 

        在具體使用外設的特定功能(比如LPUART的數據發送和接收)時,才使能該特定功能的中斷(比如LPUART的發送中斷和接收中斷),在中斷髮生後,若相應的中斷已經完全發生(比如發送10位元組數據,全部發送完成),則在ISR的最後關閉該特定功能中斷;

 

        這樣做的好處是,避免意外的中斷浪費寶貴的CPU資源,比如由於程式跑飛導致的LPUART意外數據發送和接收;

 

        ② 在S32 SDK的PD層爲絕大多數硬體外設模組提供了中斷ISR函數,(對應S32K SDK來說,僅PD層的LPIT元件和GPIO的IRQ中斷,需要使用者自己使能NVIC中斷並編寫自己的中斷ISR)無需使用者自己編寫;

 

        在S32 SDK的元件中,相同外設的不同實體(比如LPUART0/1/2)共用同一個中斷ISR,依靠外設實體號(instance)來區分不同的處理,由於相同外設的工作流程和中斷處理流程相同,這樣的好處是可以提高程式碼的複用效率:

如下是S32K144的SDK程式碼,其有LPUART0/1/2三個串列埠外設,其中斷ISR定義如下:

 

        而在真正的LPUART中斷處理常式中依靠不同的instance來區別:

 

        ③ S32 SDK預設在startup過程中將外設中斷向量表拷貝到SARM中,這樣執行使用者可以在程式執行時動態的修改中斷ISR;

 

        ④ S32 SDK中爲大部分外設都提供了中斷處理常式(ISR)—位於外設驅動PD的中斷處理檔案(<Peripheral_Name>_irq.c/h)中,並在外設初始化時設定了相應的中斷,其外設中斷ISR屬於SDK PD層驅動非常重要的一部分程式碼,不允許使用者修改,因此提供了在外設中斷ISR中呼叫使用者中斷回撥函數的方式給使用者提供中斷處理介面。

 

        Tips:在使用者中斷回撥函數中,無需使用者自己清除外設中斷標誌,因爲在SDK提供的外設中斷ISR中已經做了相應的處理。只有需要使用者呼叫SDK中斷管理器(interrupt_manager)的API--INT_SYS_InstallHandler()手動註冊安裝的外設中斷ISR,才需要使用者自己清外設中斷標誌,比如典型的GPIO的IRQ中斷和PIT定時器中斷。

 

        Tips:使用PD層外設驅動元件,需要使用者自己呼叫中斷回撥函數API自己安裝中斷Callback:

 

        而若使用PAL層外設驅動元件,則可以在其Processor Expert圖形化設定介面中新增中斷Callback函數名,然後再使用者程式碼中實現該Callback即可,無需使用者自己呼叫中斷回撥函數安裝API。

 

        4. S32 SDK的通訊外設驅動元件API函數的阻塞與非阻塞發送與接收API函數差別與使用詳解;

 

        在S32 SDK,通訊外設元件都提供了阻塞(Blocking)與非阻塞兩類發送與接收API函數,比如:

 

        UART_PAL元件的API函數:

 

        CAN_PAL元件的API函數:

 

        其差別如下:

 

        非阻塞類數據收發API,只是將要發送的數據buffer和長度size設定到對應的通訊外設驅動的全域性狀態結構體中,使能數據發送器(transmiter)和接收器(receiver),開啓收發和錯誤中斷/若使用DMA收發,則設定相應的DMA通道TCD,使能DMA中斷,安裝中斷回撥函數,啓動DMA傳輸, 然後就立即返回了,因此非阻塞類API的返回值總是成功(STATUS_SUCCESS)。

 

        比如S32K SDK的LPUART非阻塞發送API--LPUART_DRV_SendData() 實現如下:

 

        而阻塞(Blocking)類數據收發API,函數參數多了一個超時timeout(單位爲ms)則在完成以上非阻塞API的工作後,還會呼叫OSIF元件的API函數--OSIF_SemaWait()檢查外設驅動全域性狀態結構體中的數據收發號志(semaphore),等待數據收發完成,若在設定的timeout時間內未完成數據傳輸,則返回超時(STATUS_TIMEOUT),若在timeout時間內數據收發完成,則返回成功(STATUS_SUCCESS)。

 

        比如S32K SDK的LPUART阻塞(blocking)發送API--LPUART_DRV_SendDataBlocking()實現如下:

       

        Tips:結合前面S32 SDK外設底層驅動中斷處理機制 機製的介紹可知,無論呼叫阻塞函數非阻塞數據收發函數,在成功接收完指定長度的數據後,都會將通訊外設的接收器關閉,若想通訊外設一直接收數據,則需要在該驅動元件的中斷回撥函數Callback中,設定新的數據接收buffer,從而避免在中斷ISR最後,驅動程式關閉數據接收器:

 

        比如使用S32K的LPUART接收GPS數據時,相應的數據接收中斷回撥函數定義如下,其中使用雙buffer,在一個buffer接收完成後,呼叫UART_SetRxBuffer()切換新的buffer繼續接收數據:

 

        Tips:由於在阻塞API函數中,呼叫了OSIF元件的API函數--OSIF_SemaWait()檢查外設驅動全域性狀態結構體中的數據收發號志(semaphore), 若在應用工程中使用了RTOS,比如FreeRTOS,則呼叫該阻塞API函數的任務將被阻塞,讓出CPU資源執行其他就緒的高優先順序認爲,直至收據收發完成(號志釋放)或者超時,這樣CPU的執行效率將大大提高;而若應用工程中沒有使用RTOS,則阻塞API函數將一直佔有CPU,造成CPU資源浪費;

綜上, 若應用工程使用了RTOS,則推薦使用阻塞API函數;若應用工程未使用RTOS,則推薦使用非阻塞API函數,並配閤中斷回撥函數,判斷數據收發狀態並做相應處理。

 

        5.  S32 SDK的外設驅動與RTOS之間的互動方式

 

        在S32 SDK中,所有的外設驅動(PD和PAL元件)以及中介軟體(Middleware)的軟體程式碼實現,都使用了OSIF元件提供的操作系統介面API對外設驅動工作中的數據進行同步和事件時間延遲,從而保證了S32 SDK程式碼在不同RTOS之間的相容:

        其中使用的號志都在相應的元件全域性狀態結構體中定義,在元件初始化(元件_Init()函數)時,呼叫了OSIF元件的API函數--OSIF_SemaCreate()建立,比如S32K SDK的LPUART驅動用於數據收發的號志rxComplete/txComplete,就在其初始化函數LPUART_DRV_Init()中建立的:

        在元件的阻塞(Blocking)API函數中呼叫OSIF元件的API函數--OSIF_SemaWait()檢查外設驅動全域性狀態結構體中定義的號志(semaphore)以完成事件同步。比如S32K SDK的LPUART驅動中數據發送完成號志txComplete,就在其阻塞數據發送API函數LPUART_SendDataBlocking()中使用的:

        在元件反初始化(元件_Deinit()函數)時,呼叫呼叫了OSIF元件的API函數--OSIF_SemaDestroy()釋放。比如S32K SDK的LPUART驅動用於數據收發的號志rxComplete/txComplete,就在其反初始化函數LPUART_DRV_Deinit()中被銷燬的:

        從以上分析可知,通過在SDK元件全域性狀態結構體中引入號志(Mutex/Semaphore)和使用操作系統介面OSIF元件API,既保證了S32 SDK外設底層驅動程式和中介軟體軟體工作的同步和效率,又提高了SDK軟體程式碼的在不同RTOS之間的可移植性,使用不同RTOS時,只需要關心和移植OSIF元件提供的API函數,即可保證S32 SDK外設底層驅動和中介軟體軟體程式碼在該RTOS中的正常工作。

 

        總結

 

        雖然我已經力求儘量把它寫的深入淺出,通俗易懂,閱讀本文介紹的知識要點仍然需要一定的程式設計基礎和嵌入式底層驅動程式開發經驗積累,或者使用過一段時間的S32 SDK軟體。不過沒關係,在後續的《S32K SDK使用詳解》系列技術分享文章中,我還將結合S32K1xx系列MCU的內核和外設功能特性,爲大家帶來更多的S32 SDK使用細節分解,希望大家可以持續關注和學習。