1. 程式人生 > >痞子衡嵌入式:超級下載演算法(RT-UFL)開發筆記(3) - 統一FlexSPI驅動訪問

痞子衡嵌入式:超級下載演算法(RT-UFL)開發筆記(3) - 統一FlexSPI驅動訪問

----   大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是**超級下載演算法開發筆記(3)之統一FlexSPI驅動訪問**。   文接上篇 [《超級下載演算法(RT-UFL)開發筆記(2) - 識別當前i.MXRT型號》](https://www.cnblogs.com/henjay724/p/13971756.html),現在超級演算法已經能夠識別到當前i.MXRT型號了,下一步就是找到一套統一的底層Flash驅動函式來實現外接序列NOR Flash的基本擦寫操作,這套統一的底層Flash驅動至少要在API層面做到與i.MXRT型號無關,並且呼叫方式統一,這樣就相當方便後續的上層演算法層面的邏輯設計了。   本篇是開發筆記第三篇,咱們就重點聊聊如何為超級下載演算法設計一套統一的FlexSPI驅動介面及其訪問方式。 ### 一、找到統一的FlexSPI驅動   我們知道i.MXRT系列內部用於連線NOR Flash的外設名字叫FlexSPI,這個外設在不同i.MXRT型號上差異很小,這對於設計通用Flash驅動函式來說方便了很多,這也是痞子衡做i.MXRT超級演算法的最初動機。   說到FlexSPI這個外設,其實就是Kinetis系列的QuadSPI外設的升級,在恩智浦MCUX SDK包裡提供了一套標準的FlexSPI驅動,這個驅動寫得還挺完善的,但是痞子衡並沒有選擇SDK標準驅動作為超級下載演算法的底層Flash驅動。 ```text \SDK_2.x.x\devices\MIMXRTxxxx\drivers\fsl_flexspi.c \SDK_2.x.x\devices\MIMXRTxxxx\drivers\fsl_flexspi.h \SDK_2.x.x\components\flash\nor\flexspi\fsl_flexspi_nor_flash.c \SDK_2.x.x\components\flash\nor\flexspi\fsl_flexspi_nor_flash.h ```   我們知道i.MXRT系列都是包含BootROM的,BootROM都支援從外部序列NOR Flash啟動,這意味著BootROM中也是集成了FlexSPI驅動的(驅動原始碼也開源在SDK裡了),BootROM裡這套驅動與MCUX SDK裡的驅動大體上差不多,但是細節上有差異,痞子衡最終選擇了BootROM裡的FlexSPI驅動作為超級演算法的底層Flash驅動,原因下一節會講。 ```text \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi\bl_flexspi.c \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi\bl_flexspi.h \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi_nor\flexspi_nor_flash.c \SDK_2.x.x\middleware\mcu-boot\src\drivers\flexspi_nor\flexspi_nor_flash.h ``` ### 二、統一FlexSPI驅動訪問方式   現在我們雖然找到了一套看似統一的FlexSPI驅動,但事情遠不是這麼簡單。BootROM版本的FlexSPI驅動從API介面本身而言是幾乎一致的,痞子衡之前也為此寫過文章 [《利用i.MXRT系列ROM提供的FlexSPI driver API可輕鬆IAP》](https://www.cnblogs.com/henjay724/p/13202824.html),但是在不同i.MXRT型號上呼叫方式不統一(在開放API的i.MXRT型號上API函式地址不一,在不開放API的i.MXRT型號上需要手動移植mcu-boot裡的原始碼),因此我們需要對所有i.MXRT型號下的BootROM FlexSPI驅動呼叫方式做一個統一。 #### 2.1 ROM API介面方式   首先講開放ROM API的幾款i.MXRT型號(RT500/RT600/RT1060/RT1064/RT1170),這裡順便先解釋一下上一節的遺留問題,為何選擇BootROM版本FlexSPI驅動而不是SDK標準驅動?當然是因為有這個ROM API的存在,畢竟超級下載演算法最終可執行檔案越小越好,能呼叫ROM API可以極大地減小超級下載演算法的最終程式碼長度。   關於ROM API的細節,痞子衡不予贅述,我們按照如下格式準備好全部的g_bootloaderTree_imxrt巨集待用(程式碼僅示例了i.MXRT1060) ```C #define RT106X_ROM_API_TREE_ADDR (0x0020001cu) typedef struct _bootloader_tree_imxrt106x { const uint32_t version; const char *copyright; void (*runBootloader)(void *arg); const uint32_t reserved0; const flexspi_nor_flash_driver_imxrt106x_t *flexspiNorDriver; } bootloader_tree_imxrt106x_t; #define g_bootloaderTree_imxrt106x (*(bootloader_tree_imxrt106x_t **)(RT106X_ROM_API_TREE_ADDR)) ``` #### 2.2 原始碼(庫)介面方式   對於沒有開放ROM API的幾款i.MXRT型號(RT1010/1015/1020/1024/1050),咱們就必須一一移植mcu-boot裡的FlexSPI相關程式碼了,需移植的程式碼包含兩部分:FlexSPI外設本身驅動,FlexSPI BSP驅動。前者移植起來倒是比較簡單(直接找一個最完善的版本即可),但是後者涉及到了clock和pinmux配置,因i.MXRT型號而異,這部分程式碼差異較大,移植起來比較麻煩。   FlexSPI外設本身驅動就是最終提供如下幾個通用的函式即可,這部分是共用的原始碼: ```text status_t flexspi_nor_drv_flash_init(uint32_t instance, flexspi_nor_config_t *config); status_t flexspi_nor_drv_flash_page_program(uint32_t instance, flexspi_nor_config_t *config, uint32_t dstAddr, const uint32_t *src); status_t flexspi_nor_drv_flash_erase_all(uint32_t instance, flexspi_nor_config_t *config); status_t flexspi_nor_drv_flash_erase(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, uint32_t length); status_t flexspi_nor_drv_flash_read( uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t start, uint32_t bytes); status_t flexspi_nor_drv_get_config(uint32_t instance, flexspi_nor_config_t *config, serial_nor_config_option_t *option); ```   在移植FlexSPI BSP驅動過程中遇到了一個最頭疼的事情,就是clock和pinmux程式碼需使用SDK裡的基礎驅動,而SDK驅動依賴i.MXRT晶片標頭檔案,但是最終超級下載演算法只有一個工程,這個工程幾乎無法同時包含多個i.MXRT標頭檔案。如果不用i.MXRT標頭檔案,clock和pinmux程式碼全部改為裸寫暫存器地址,工作量又太大,也不利於後期維護,最終想到的解決方案就是為每個i.MXRT型號的FlexSPI BSP驅動製作一個庫工程,在庫工程裡各自使用自己的標頭檔案,然後生成一個庫檔案作為超級下載演算法工程的原始檔。   下面是示例的i.MXRT1050庫檔案裡需提供的BSP函式列表,這也是綜合多個型號SDK包裡mcu-boot程式碼後提煉出來的: ```C void flexspi_iomux_config_rt1050(uint32_t instance, flexspi_mem_config_t *config); void flexspi_update_padsetting_rt1050(flexspi_mem_config_t *config, uint32_t driveStrength); void flexspi_clock_config_rt1050(uint32_t instance, uint32_t freq, uint32_t sampleClkMode); status_t flexspi_set_failsafe_setting_rt1050(flexspi_mem_config_t *config); status_t flexspi_get_max_supported_freq_rt1050(uint32_t instance, uint32_t *freq, uint32_t clkMode); uint32_t CLOCK_GetCPUFreq_RT1050(void); status_t flexspi_get_clock_rt1050(uint32_t instance, flexspi_clock_type_t type, uint32_t *freq); void flexspi_clock_gate_enable_rt1050(uint32_t instance); void flexspi_clock_gate_disable_rt1050(uint32_t instance); status_t flexspi_nor_write_persistent_rt1050(const uint32_t data); status_t flexspi_nor_read_persistent_rt1050(uint32_t *data); ``` #### 2.3 兩種不同方式的驅動統一   現在無論是ROM API介面方式,還是原始碼(庫)介面方式,所有的i.MXRT型號下基礎FlexSPI驅動已經準備完畢了,到了最關鍵的統一階段了,我們首先可以定義一個如下ufl_target_desc_t結構體及其全域性變數g_uflTargetDesc,這個結構體由FlexSPI擦寫API函式指標(flexspi_nor_flash_driver_t)以及BSP函式指標(flexspi_bsp_driver_t)組成: ```C typedef struct _target_desc { uint32_t imxrtChipId; flexspi_nor_flash_driver_t flashDriver; flexspi_bsp_driver_t flexspiBsp; } ufl_target_desc_t; ufl_target_desc_t g_uflTargetDesc; ```   然後我們定義一個ufl_fill_flash_api()函式,該函式的功能就是根據識別出來的i.MXRT型號來具體填充ufl_target_desc_t型全域性結構體變數裡的成員值。如果是原始碼介面方式,則填入對應函式名;如果是ROM API介面方式,則根據g_bootloaderTree_imxrt巨集找到對應函式地址,最終我們在g_uflTargetDesc全域性變數裡統一了FlexSPI驅動訪問方式(下述程式碼僅示例了RT1050和RT1060)。 ```C static void ufl_fill_flash_api(void) { rt_chip_id_t chipId = (rt_chip_id_t)g_uflTargetDesc.imxrtChipId; ufl_target_desc_t *uflTargetDesc = (ufl_target_desc_t *)&g_uflTargetDesc; switch (chipId) { case kChipId_RT105x: uflTargetDesc->flashDriver.init = flexspi_nor_drv_flash_init; uflTargetDesc->flashDriver.page_program = flexspi_nor_drv_flash_page_program; uflTargetDesc->flashDriver.erase_all = flexspi_nor_drv_flash_erase_all; uflTargetDesc->flashDriver.erase = flexspi_nor_drv_flash_erase; uflTargetDesc->flashDriver.read = flexspi_nor_drv_flash_read; uflTargetDesc->flashDriver.set_clock_source = NULL; uflTargetDesc->flashDriver.get_config = flexspi_nor_drv_get_config; uflTargetDesc->flexspiBsp.flexspi_iomux_config = flexspi_iomux_config_rt1050; uflTargetDesc->flexspiBsp.flexspi_update_padsetting = flexspi_update_padsetting_rt1050; uflTargetDesc->flexspiBsp.flexspi_clock_config = flexspi_clock_config_rt1050; uflTargetDesc->flexspiBsp.flexspi_set_failsafe_setting = flexspi_set_failsafe_setting_rt1050; uflTargetDesc->flexspiBsp.CLOCK_GetCPUFreq = CLOCK_GetCPUFreq_RT1050; uflTargetDesc->flexspiBsp.flexspi_get_max_supported_freq = flexspi_get_max_supported_freq_rt1050; uflTargetDesc->flexspiBsp.flexspi_clock_gate_enable = flexspi_clock_gate_enable_rt1050; uflTargetDesc->flexspiBsp.flexspi_clock_gate_disable = flexspi_clock_gate_disable_rt1050; uflTargetDesc->flexspiBsp.flexspi_nor_write_persistent = flexspi_nor_write_persistent_rt1050; uflTargetDesc->flexspiBsp.flexspi_get_clock = flexspi_get_clock_rt1050; uflTargetDesc->flexspiBsp.flexspi_nor_read_persistent = flexspi_nor_read_persistent_rt1050; break; case kChipId_RT106x: uflTargetDesc->flashDriver.init = g_bootloaderTree_imxrt106x->flexspiNorDriver->init; uflTargetDesc->flashDriver.page_program = g_bootloaderTree_imxrt106x->flexspiNorDriver->program; uflTargetDesc->flashDriver.erase_all = g_bootloaderTree_imxrt106x->flexspiNorDriver->erase_all; uflTargetDesc->flashDriver.erase = g_bootloaderTree_imxrt106x->flexspiNorDriver->erase; uflTargetDesc->flashDriver.read = g_bootloaderTree_imxrt106x->flexspiNorDriver->read; uflTargetDesc->flashDriver.set_clock_source = NULL; uflTargetDesc->flashDriver.get_config = g_bootloaderTree_imxrt106x->flexspiNorDriver->get_config; break; case kChipId_Invalid: default: break; } } ```   有了g_uflTargetDesc全域性變數,此時再包一層API驅動給最終下載演算法上層邏輯呼叫就非常簡單了。 ```C status_t flexspi_nor_flash_init(uint32_t instance, flexspi_nor_config_t *config) { return g_uflTargetDesc.flashDriver.init(instance, config); } void flexspi_iomux_config(uint32_t instance, flexspi_mem_config_t *config) { g_uflTargetDesc.flexspiBsp.flexspi_iomux_config(instance, config); } ```   至此,超級下載演算法開發筆記(3)之統一FlexSPI驅動訪問痞子衡便介紹完畢了,掌聲在哪裡~~~ ### 歡迎訂閱 文章會同時釋出到我的 [部落格園主頁](https://www.cnblogs.com/henjay724/)、[CSDN主頁](https://blog.csdn.net/henjay724)、[知乎主頁](https://www.zhihu.com/people/henjay724)、[微信公眾號](http://weixin.sogou.com/weixin?type=1&query=痞子衡嵌入式) 平臺上。 微信搜尋"__痞子衡嵌入式__"或者掃描下面二維碼,就可以在手機上第一時間看了哦。 ![](http://henjay724.com/image/github/pzhMcu_qrcode_258x2