你们好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给你们介绍的是超级下载算法开发笔记(3)之统一FlexSPI驱动访问。html
文接上篇 《超级下载算法(RT-UFL)开发笔记(2) - 识别当前i.MXRT型号》,如今超级算法已经可以识别到当前i.MXRT型号了,下一步就是找到一套统一的底层Flash驱动函数来实现外接串行NOR Flash的基本擦写操做,这套统一的底层Flash驱动至少要在API层面作到与i.MXRT型号无关,而且调用方式统一,这样就至关方便后续的上层算法层面的逻辑设计了。算法
本篇是开发笔记第三篇,我们就重点聊聊如何为超级下载算法设计一套统一的FlexSPI驱动接口及其访问方式。api
咱们知道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
如今咱们虽然找到了一套看似统一的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
首先讲开放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))
对于没有开放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);
如今不管是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主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就能够在手机上第一时间看了哦。