• 正文
    • 一、事務型驅動函數(shù)簡介
    • 二、事務型中斷處理函數(shù)設計
    • 三、重定向事務型中斷處理函數(shù)
  • 相關推薦
申請入駐 產業(yè)圖譜

何為MCU外設驅動文件里的事務型API?

2024/11/13
1504
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是SDK2.0里事務型中斷處理函數(shù)(DriverIRQHandler)的重定向注意事項。

最近有一個 i.MXRT 客戶在使用官方 SDK 外設驅動里的中斷處理函數(shù)時遇到了代碼重定向失效問題,客戶用得是一個 XIP Flash 工程,想把程序中斷向量表以及相關外設的驅動函數(shù)全部重定向到 RAM 中以提高系統(tǒng)性能,但實測發(fā)現(xiàn)中斷發(fā)生時,仍然存在 Flash 訪問行為。這本來不是個大問題,因為 SDK 在設計時已經從中斷處理函數(shù)命名上就做了明確提醒,但是很多客戶并沒有意識到,今天痞子衡就來聊聊這個話題:

一、事務型驅動函數(shù)簡介

恩智浦 SDK 軟件包里的外設驅動(HAL級)正常來說提供的 API 都是面對外設配置(init、deinit、set_feature、get_status) 的通用功能函數(shù)。此外對于通信接口類外設,一般還會有阻塞式(blocking)的數(shù)據(jù)傳輸功能函數(shù)。以 LPUART 外設為例,其數(shù)據(jù)傳輸有以下四個 API:

//?寫入(發(fā)送)一個?Byte?數(shù)據(jù)(需在?FIFO?沒滿的情況下)
static?inline?void?LPUART_WriteByte(LPUART_Type?*base,?uint8_t?data);
//?讀取(接收)一個?Byte?數(shù)據(jù)(需在?FIFO?非空的情況下)
static?inline?uint8_t?LPUART_ReadByte(LPUART_Type?*base);
//?阻塞式寫入(發(fā)送)多個?Byte?數(shù)據(jù)
status_t?LPUART_WriteBlocking(LPUART_Type?*base,?const?uint8_t?*data,?size_t?length);
//?阻塞式讀取(接收)多個?Byte?數(shù)據(jù)
status_t?LPUART_ReadBlocking(LPUART_Type?*base,?uint8_t?*data,?size_t?length);

阻塞式數(shù)據(jù)傳輸 API 本質上就是獨占 CPU 時間進行查詢式傳輸,API 一旦調用,必須等到數(shù)據(jù)收發(fā)結束才會返回,這樣會導致 CPU 利用率不高,其一般不利用外設中斷。為了結合外設中斷進行高效數(shù)據(jù)傳輸(non-blocking),SDK2.0 中額外提供了如下事務型相關函數(shù)(僅列出了部分):

//?創(chuàng)建事務型數(shù)據(jù)傳輸句柄
void?LPUART_TransferCreateHandle(LPUART_Type?*base,
?????????????????????????????????lpuart_handle_t?*handle,
?????????????????????????????????lpuart_transfer_callback_t?callback,
?????????????????????????????????void?*userData);
//?非阻塞式寫入(發(fā)送)多個?Byte?數(shù)據(jù)
status_t?LPUART_TransferSendNonBlocking(LPUART_Type?*base,?lpuart_handle_t?*handle,?lpuart_transfer_t?*xfer)
//?非阻塞式讀取(接收)多個?Byte?數(shù)據(jù)
status_t?LPUART_TransferReceiveNonBlocking(LPUART_Type?*base,
???????????????????????????????????????????lpuart_handle_t?*handle,
???????????????????????????????????????????lpuart_transfer_t?*xfer,
???????????????????????????????????????????size_t?*receivedBytes);
//?事務型數(shù)據(jù)傳輸中斷處理函數(shù)
void?LPUART_TransferHandleIRQ(LPUART_Type?*base,?void?*irqHandle);

非阻塞式數(shù)據(jù)傳輸 API 顯然就是結合了外設中斷來做數(shù)據(jù)傳輸,API 調用后填入一些配置后會立刻返回,沒有過多消耗 CPU 時間,等外設中斷發(fā)生時再進一步處理數(shù)據(jù)。這類型 API 常常和應用設計緊相關,所以也稱為事務型函數(shù)(transactional API)。

SDK 里并不是所有外設驅動里包含事務性函數(shù),這類 API 常出現(xiàn)在傳輸接口類外設上。對于 i.MXRT 來說,支持此類 API 的外設有:DMA、LPUART、LPSPI、LPI2C、SAI、FLEXIO、FLEXSPI、USDHC、ENET、CAN、MIPI_DSI/CSI、SPDIF、ASRC、PDM 等。

二、事務型中斷處理函數(shù)設計

這里繼續(xù)以 LPUART 外設來具體介紹。如下 i.MXRT1011 SDK 里提供的 8 個 LPUART 例程中有 5 個是基于事務型驅動函數(shù)的,我們就以 interrupt_transfer 的 IAR 工程為例。

打開這個 lpuart_interrupt_transfer 工程,找到芯片啟動文件 startup_MIMXRT1011.s,在里面我們能找到 PUBWEAK 型的 LPUART1_IRQHandler() 函數(shù)定義,這個是大家比較常見的中斷處理函數(shù)名,其代碼里面就是簡單跳轉到另一個 PUBWEAK 型 LPUART1_DriverIRQHandler 函數(shù)。

在 fsl_lpuart.c/.h 驅動里,找不到 LPUART1_IRQHandler() 定義,但是有 LPUART1_DriverIRQHandler() 定義。這意味著 SDK 驅動設計時,將默認的 LPUART1_IRQHandler() 函數(shù)重寫的權利留給了用戶,而重新設計了 LPUART1_DriverIRQHandler() 函數(shù)來存放事務性中斷處理代碼,從而避免因用戶自己重寫中斷處理函數(shù)時發(fā)生函數(shù)名重定義而去修改 fsl_lpuart.c 驅動文件的麻煩。

三、重定向事務型中斷處理函數(shù)

現(xiàn)在我們嘗試重定向 lpuart_interrupt_transfer 工程,可以按照 《IAR下代碼重定向的三種方法》 一文里的方法,將 fsl_lpuart.o 和 lpuart_interrupt_transfer.o 兩個目標文件都重定向到 RAM 中,并且在 main 里加上拷貝 0x60002000 處開始的 1KB 中斷向量表數(shù)據(jù)到 SRAM 中并且將 SCB->VTOR 指向對應 SRAM 的代碼(這個過程可以參考 《Cortex-M中斷向量表重定向方法》 一文)。

上述改動完成之后,編譯工程查看 map 文件,我們發(fā)現(xiàn)所有的相關代碼都已經被鏈接在了 SRAM 里,但是 LPUART1_IRQHandler() 仍然在 Flash 里,很顯然這種情況下中斷發(fā)生時,仍然會有 Flash 訪問行為(暫不考慮 L1-Cache 生效的情況),這就是客戶遇到的問題。

那么如何解決這個問題?其實 SDK 已經為你考慮到了,在 fsl_common_arm.c 文件中定義了 InstallIRQHandler() 函數(shù)(僅在 ENABLE_RAM_VECTOR_TABLE 宏存在的情況下生效),查看其源碼,發(fā)現(xiàn)作用有兩個:一、如果 SCB->VTOR 指向得不是 SRAM,那么將中斷向量表從 Flash 拷貝到 SRAM 中,并且重置 VTOR;二、根據(jù)傳入?yún)?shù)修改 SRAM 中的某個中斷向量值。

因此 lpuart_interrupt_transfer 例程中,如果需要徹底重定向中斷處理函數(shù),記得在 main 函數(shù)里的 LPUART_TransferCreateHandle() 函數(shù)調用之后加上如下一句代碼,其作用除了重定向中斷向量表之外,還將表里的 LPUART1 中斷向量從 LPUART1_IRQHandler() 更換為了 LPUART1_DriverIRQHandler(),這樣代碼重定向就徹底了。

InstallIRQHandler(LPUART1_IRQn,?(uint32_t)LPUART1_DriverIRQHandler);

此時再編譯工程下載運行,發(fā)現(xiàn)出現(xiàn) hardfault,這是怎么回事?別急,因為 InstallIRQHandler() 函數(shù)里需要用到鏈接文件 MIMXRT1011xxxxx_flexspi_nor.icf 里定義的三個 Symbol,工程選項 Linker/Configuration file symbol 里必須添加 __ram_vector_table__=1 設置,那些 Symbol 才會真正產生重定向作用。

define symbol m_interrupts_start       = 0x60002000;
define symbol m_interrupts_ram_start   = 0x20000000;

define symbol __ram_vector_table_size__        = isdefinedsymbol(__ram_vector_table__) ? 0x00000400 : 0;
define exported symbol __VECTOR_TABLE          = m_interrupts_start;
define exported symbol __VECTOR_RAM            = isdefinedsymbol(__ram_vector_table__) ? m_interrupts_ram_start : m_interrupts_start;
define exported symbol __RAM_VECTOR_TABLE_SIZE = __ram_vector_table_size__;

至此,SDK2.0里事務型中斷處理函數(shù)(DriverIRQHandler)的重定向注意事項痞子衡便介紹完畢了,掌聲在哪里~~~

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

碩士畢業(yè)于蘇州大學電子信息學院,目前就職于恩智浦(NXP)半導體MCU系統(tǒng)部門,擔任嵌入式系統(tǒng)應用工程師。痞子衡會定期分享嵌入式相關文章