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


  你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們介紹的是超級下載算法開發筆記(3)之統一FlexSPI驅動訪問html

  文接上篇 《超級下載算法(RT-UFL)開發筆記(2) - 識別當前i.MXRT型號》,如今超級算法已經可以識別到當前i.MXRT型號了,下一步就是找到一套統一的底層Flash驅動函數來實現外接串行NOR Flash的基本擦寫操做,這套統一的底層Flash驅動至少要在API層面作到與i.MXRT型號無關,而且調用方式統一,這樣就至關方便後續的上層算法層面的邏輯設計了。算法

  本篇是開發筆記第三篇,我們就重點聊聊如何爲超級下載算法設計一套統一的FlexSPI驅動接口及其訪問方式。api

1、找到統一的FlexSPI驅動

  咱們知道i.MXRT系列內部用於鏈接NOR Flash的外設名字叫FlexSPI,這個外設在不一樣i.MXRT型號上差別很小,這對於設計通用Flash驅動函數來講方便了不少,這也是痞子衡作i.MXRT超級算法的最初動機。微信

  說到FlexSPI這個外設,其實就是Kinetis系列的QuadSPI外設的升級,在恩智浦MCUX SDK包裏提供了一套標準的FlexSPI驅動,這個驅動寫得還挺完善的,可是痞子衡並無選擇SDK標準驅動做爲超級下載算法的底層Flash驅動。函數

\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驅動,緣由下一節會講。flex

\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

2、統一FlexSPI驅動訪問方式

  如今咱們雖然找到了一套看似統一的FlexSPI驅動,但事情遠不是這麼簡單。BootROM版本的FlexSPI驅動從API接口自己而言是幾乎一致的,痞子衡以前也爲此寫過文章 《利用i.MXRT系列ROM提供的FlexSPI driver API可輕鬆IAP》,可是在不一樣i.MXRT型號上調用方式不統一(在開放API的i.MXRT型號上API函數地址不一,在不開放API的i.MXRT型號上須要手動移植mcu-boot裏的源代碼),所以咱們須要對全部i.MXRT型號下的BootROM FlexSPI驅動調用方式作一個統一。ui

2.1 ROM API接口方式

  首先講開放ROM API的幾款i.MXRT型號(RT500/RT600/RT1060/RT1064/RT1170),這裏順便先解釋一下上一節的遺留問題,爲什麼選擇BootROM版本FlexSPI驅動而不是SDK標準驅動?固然是由於有這個ROM API的存在,畢竟超級下載算法最終可執行文件越小越好,能調用ROM API能夠極大地減少超級下載算法的最終代碼長度。.net

  關於ROM API的細節,痞子衡不予贅述,咱們按照以下格式準備好所有的g_bootloaderTree_imxrt宏待用(代碼僅示例了i.MXRT1060)設計

#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外設自己驅動就是最終提供以下幾個通用的函數便可,這部分是共用的源代碼:

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代碼後提煉出來的:

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)組成:

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)。

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驅動給最終下載算法上層邏輯調用就很是簡單了。

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驅動訪問痞子衡便介紹完畢了,掌聲在哪裏~~~

歡迎訂閱

文章會同時發佈到個人 博客園主頁CSDN主頁知乎主頁微信公衆號 平臺上。

微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就能夠在手機上第一時間看了哦。

相關文章
相關標籤/搜索