大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是瑞薩RA系列FSP固件庫里的外設(shè)驅(qū)動(dòng)。
上一篇文章痞子衡帶大家快速體驗(yàn)了一下瑞薩 MCU 開發(fā)三大件(開發(fā)環(huán)境e2 studio、軟件包FSP、評(píng)估板EK),其中軟件包 FSP 為何不叫更通用的 SDK,痞子衡特地留了伏筆,今天就讓我們分析一下這個(gè) FSP 到底是什么來頭?(本篇主要分析其中外設(shè)驅(qū)動(dòng)部分)
一、固件包架構(gòu)對(duì)比
我們嘗試對(duì)比意法半導(dǎo)體、恩智浦以及瑞薩三家的固件包來看看它們的架構(gòu)差異。
1.1 ST STM32Cube MCU Packages
首先來看在固件包生態(tài)上建立得比較早的意法半導(dǎo)體,它家固件包全稱 STM32Cube MCU Packages,從下往上一共四層(MCU硬件、BSP&HAL驅(qū)動(dòng)、Middleware、App),另外 CMSIS 地位與 Milddeware 平齊,說明意法認(rèn)為 CMSIS 是相對(duì)通用的中間層代碼。
其中我們主要關(guān)注 BSP 和 HAL 驅(qū)動(dòng),BSP 即板級(jí)器件(比如 Codec、各種傳感器等)相關(guān)的驅(qū)動(dòng),HAL 則是 MCU 片內(nèi)外設(shè)驅(qū)動(dòng),在意法架構(gòu)里 BSP 和 HAL 是相同的層級(jí),但其實(shí)我們知道 BSP 功能也要基于 HAL 驅(qū)動(dòng)來具體實(shí)現(xiàn)。
關(guān)于這里的 HAL 驅(qū)動(dòng),有必要多展開一些,最早期的時(shí)候意法半導(dǎo)體主推得是標(biāo)準(zhǔn)庫(Standard Peripheral Libraries,簡稱 SPL),目前已經(jīng)不再維護(hù)更新,現(xiàn)在主推 HAL 庫(Hardware Abstraction Layer)和 LL 庫(Low-Layer),所以架構(gòu)圖里 HAL 實(shí)際上是統(tǒng)指 HAL 庫和 LL 庫,三者關(guān)系簡單理解就是 SPL = HAL + LL。
底層庫文件:xxxMCU_ll_xxxPeripheral.c/h,提供的 API 主要是對(duì)于片內(nèi)外設(shè)寄存器的單一設(shè)置操作,API 命名為 LL_PERIPHERAL_xxxAction()
???????????原型示例:ErrorStatus LL_USART_Init(USART_TypeDef *USARTx, LL_USART_InitTypeDef *USART_InitStruct)
抽象層文件:xxxMCU_hal_xxxPeripheral.c/h,提供的 API 主要是對(duì)于片內(nèi)外設(shè)具體功能的綜合操作,API 命名為 HAL_PERIPHERAL_xxxFunc()
???????????原型示例:HAL_StatusTypeDef HAL_USART_Init(USART_HandleTypeDef *husart)
標(biāo)準(zhǔn)庫文件:xxxMCU_xxxPeripheral.c/h,提供的 API 同時(shí)包含上述 LL 和 HAL 功能(但是實(shí)現(xiàn)豐富度稍低),API 命名為 PERIPHERAL_xxxFunc/Action()
???????????原型示例:void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
1.2 NXP MCUXpresso-SDK
再來看痞子衡東家恩智浦半導(dǎo)體,固件包全稱 MCUXpresso-SDK,從下往上一共五層(MCU硬件、CMSIS、HAL驅(qū)動(dòng)、Middleware&BSP、App),這樣的分層方式其實(shí)是 ARM 公司比較推薦的,與意法見解不同的是,這里 CMSIS 緊靠 MCU 硬件層,顯然恩智浦認(rèn)為 CMSIS 也是底層基礎(chǔ)代碼。
恩智浦架構(gòu)里 BSP 和 HAL 不在同一層,清晰地表明了 BSP 是在 HAL 基礎(chǔ)之上的代碼。恩智浦的 HAL 驅(qū)動(dòng)比較像意法半導(dǎo)體的早期標(biāo)準(zhǔn)庫 SPL,但是 API 功能豐富度遠(yuǎn)超 SPL。
抽象層文件:fsl_xxxPeripheral.c/h,提供的 API 同時(shí)包含片內(nèi)外設(shè)寄存器的單一設(shè)置操作以及外設(shè)具體功能的綜合操作,API 命名為 PERIPHERAL_xxxFunc/Action()
???????????原型示例:status_t LPUART_Init(LPUART_Type *base, const lpuart_config_t *config, uint32_t srcClock_Hz);
1.3 Renesas RA FSP
最后來看瑞薩家的 FSP,沒有表現(xiàn)出明顯的層次結(jié)構(gòu),但是能看出瑞薩架構(gòu)里 BSP 和 HAL 不在同一層,且 BSP 在 HAL 之下。這里的 BSP 也包含了 CMSIS,顯然瑞薩認(rèn)為 BSP 既包含了 MCU 內(nèi)核相關(guān)基礎(chǔ)硬件也包含板級(jí)器件硬件驅(qū)動(dòng)。
瑞薩 HAL 驅(qū)動(dòng)設(shè)計(jì)得比較有意思,不同于意法以及恩智浦,它對(duì)于外設(shè)功能抽象更為看重(也可以理解為更面向?qū)ο螅?,為此額外創(chuàng)建了一個(gè) r_xxxModule_api.h 文件,里面定義了 API 原型,原型重點(diǎn)強(qiáng)調(diào)外設(shè)的通用功能行為,而忽略具體外設(shè)的操作細(xì)節(jié)和差異,這個(gè)我們下一節(jié)會(huì)細(xì)聊。
抽象層文件:r_xxxModule_api.h,定義統(tǒng)一的外設(shè)模塊驅(qū)動(dòng) API 原型結(jié)構(gòu)體,適用于同類功能的不同外設(shè)情況(比如 UART 功能既可能是 SCI_USART 也可能是 SCI_UART 或者其它)
???????????r_xxxPeripheral.c/h,提供的?API?主要包含片內(nèi)外設(shè)具體功能的綜合操作,API?命名為?R_PERIPHERAL_xxxFunc()
???????????原型示例:fsp_err_t R_SCI_B_UART_Open (uart_ctrl_t * const p_api_ctrl, uart_cfg_t const * const p_cfg)
二、FSP里的外設(shè)驅(qū)動(dòng)結(jié)構(gòu)
在上篇文章示例工程 lpm_ek_ra8m1_ep 里,我們發(fā)現(xiàn)有如下 ra 文件夾,這就是 FSP 包相關(guān)的源文件,我們結(jié)合具體源文件來分析:
2.1 頭文件與啟動(dòng)文件
首先在系統(tǒng)文件(頭文件與啟動(dòng)文件)命名上,三家小有差異,不過差異最大的是型號(hào)頭文件里的外設(shè)寄存器定義,這和后面的 HAL 驅(qū)動(dòng)里代碼實(shí)現(xiàn)息息相關(guān)。
文件類型 | 意法半導(dǎo)體 | 恩智浦半導(dǎo)體 | 瑞薩電子 |
---|---|---|---|
系列頭文件 | stm32xxxx.h | fsl_device_registers.h | renesas.h |
型號(hào)頭文件 | xxxMcu.h | xxxMCU.h | xxxMCU.h |
啟動(dòng)文件 | startup_xxxMcu.s | startup_xxxMcu.s | startup.c |
初始化文件 | system_xxxMcu.c/h | system_xxxMcu.c/h | system.c/h |
在頭文件里的外設(shè)寄存器原型定義上,意法和恩智浦是一致的,每個(gè)寄存器均用一個(gè) uint32_t 類型存儲(chǔ),而瑞薩則用聯(lián)合體(union)來存儲(chǔ)每個(gè)寄存器,這樣不僅能整體訪問該寄存器,還能按 bit field 訪問寄存器中的具體功能位。
除此以外,三家均為外設(shè)寄存器的單/多 bit 功能位做了 mask 和 pos 定義便于代碼做相關(guān)位操作。而為了便于對(duì)多 bit 功能位區(qū)域的賦值,恩智浦和意法還有額外定義(以達(dá)到瑞薩用 union 定義外設(shè)寄存器原型的效果)。
xxxPERIPHERAL_xxxREGISTER_xxxFunc_Msk/MASK
xxxPERIPHERAL_xxxREGISTER_xxxFunc_Pos/SHIFT
// 恩智浦額外定義了如下宏用于賦值多 bit 功能位區(qū)域
xxxPERIPHERAL_xxxREGISTER_xxxFunc()
// 意法則直接用多個(gè)宏來輔助置位多 bit 功能位區(qū)域的每一位
xxxPERIPHERAL_xxxREGISTER_xxxFunc
xxxPERIPHERAL_xxxREGISTER_xxxFunc_0
xxxPERIPHERAL_xxxREGISTER_xxxFunc_1
...
2.2 HAL驅(qū)動(dòng)文件
關(guān)于 HAL 驅(qū)動(dòng)本身代碼結(jié)構(gòu)部分,我們主要分析三家 API 第一個(gè)形參定義即可知主要差別,其中恩智浦和意法 LL 庫均是指向外設(shè)原型結(jié)構(gòu)體的指針,而意法 HAL 庫和瑞薩則是指向自定義外設(shè)控制塊的指針,前者偏底層,后者偏應(yīng)用層。
。
參數(shù) | 意法半導(dǎo)體 | 恩智浦半導(dǎo)體 | 瑞薩電子 |
---|---|---|---|
第一個(gè) | LL庫:PERIPHERAL_TypeDef * HAL庫:PERIPHERAL_HandleTypeDef * |
PERIPHERAL_Type * | module_ctrl_t * const |
前面痞子衡說了瑞薩多了一個(gè) r_xxxModule_api.h 文件,我們就以 SCI 外設(shè)為例,其對(duì)應(yīng) r_uart_api.h 文件,該文件里定義了如下標(biāo)準(zhǔn) API 動(dòng)作集,這些動(dòng)作不太像一般的外設(shè)驅(qū)動(dòng)函數(shù)名(比如 init, deinit 等),更像是應(yīng)用層動(dòng)作。
/**?Shared?Interface?definition?for?UART?*/
typedef?struct?st_uart_api
{
????fsp_err_t?(*?open)(uart_ctrl_t?*?const?p_ctrl,?uart_cfg_t?const?*?const?p_cfg);
????fsp_err_t?(*?read)(uart_ctrl_t?*?const?p_ctrl,?uint8_t?*?const?p_dest,?uint32_t?const?bytes);
????fsp_err_t?(*?write)(uart_ctrl_t?*?const?p_ctrl,?uint8_t?const?*?const?p_src,?uint32_t?const?bytes);
????fsp_err_t?(*?baudSet)(uart_ctrl_t?*?const?p_ctrl,?void?const?*?const?p_baudrate_info);
????fsp_err_t?(*?infoGet)(uart_ctrl_t?*?const?p_ctrl,?uart_info_t?*?const?p_info);
????fsp_err_t?(*?communicationAbort)(uart_ctrl_t?*?const?p_ctrl,?uart_dir_t?communication_to_abort);
????fsp_err_t?(*?callbackSet)(uart_ctrl_t?*?const?p_ctrl,?void?(*?p_callback)(uart_callback_args_t?*),
??????????????????????????????void?const?*?const?p_context,?uart_callback_args_t?*?const?p_callback_memory);
????fsp_err_t?(*?close)(uart_ctrl_t?*?const?p_ctrl);
????fsp_err_t?(*?readStop)(uart_ctrl_t?*?const?p_ctrl,?uint32_t?*?remaining_bytes);
}?uart_api_t;
而在 r_sci_b_uart.c 文件里,將基于 SCI 外設(shè)實(shí)現(xiàn)的 UART 驅(qū)動(dòng)函數(shù)對(duì) uart_api_t 做了實(shí)例化,這樣上層應(yīng)用可以僅調(diào)用 uart_api_t 里的接口實(shí)現(xiàn)具體功能,而不必在意這些接口具體由哪個(gè)類型的外設(shè)來實(shí)現(xiàn)的。這樣設(shè)計(jì)的好處是便于代碼跨外設(shè)(跨MCU),移植起來方便,缺點(diǎn)是限制了 API 豐富度,難以展現(xiàn)外設(shè)間的差異化特性。
/*?UART?on?SCI?HAL?API?mapping?for?UART?interface?*/
const?uart_api_t?g_uart_on_sci_b?=
{
????.open???????????????=?R_SCI_B_UART_Open,
????.close??????????????=?R_SCI_B_UART_Close,
????.write??????????????=?R_SCI_B_UART_Write,
????.read???????????????=?R_SCI_B_UART_Read,
????.infoGet????????????=?R_SCI_B_UART_InfoGet,
????.baudSet????????????=?R_SCI_B_UART_BaudSet,
????.communicationAbort?=?R_SCI_B_UART_Abort,
????.callbackSet????????=?R_SCI_B_UART_CallbackSet,
????.readStop???????????=?R_SCI_B_UART_ReadStop,
};
至此,瑞薩RA系列FSP固件庫里的外設(shè)驅(qū)動(dòng)痞子衡便介紹完畢了,掌聲在哪里~~~