查看:
512
|
回复:
0
|
rt-thread SDIO驱动框架分析(贴片SD卡flash驱动\SD Nand fla...
|
|
发表于2023-04-07 16:48:52
|
只看该作者
1#
电梯直达
文章目录rt-thread SDIO驱动框架分析之SD卡驱动1. 前言2. SDIO通用驱动框架介绍3. 文件架构分析4. SDIO设备驱动分析5. SDIO设备驱动架构分析6. 调试记录7. 总结 1. 前言RT-Thread是一款国产化的嵌入式操作系统,目前在嵌入式领域得到广泛应用,其强大的扩展功能以及通用的外设驱动框架备受大家追捧。 关于基本的外设驱动,其官网上基本也都有部分描述,但是关于SDIO设备驱动目前为止还没有相关文档说明,因此本文笔者将根据自己的调试使用经验,与大家分享下rtthread的通用SDIO设备驱动的实现。 RT-Thread github开源地址:https://github.com/RT-Thread/rt-thread本文基于代码仓库 rt-thread/bsp/stm32/stm32f103-fire-arbitrary 分析代码分支:maincommit:6808f48bdcf914f03ac757cc19b264a5d0db56de说明:main分支会有不断更新,但是SDIO驱动框架目前应该不会有大变更硬件介绍:控制器:STM32 基于手上为数不多的野火开发板吧SD卡:本次采用的并非SD卡,而是创世CS家的一颗SD Nand, CSNP4GCR01-AMW,有幸申请到了一颗样片这里多说几句,SD nand使用起来和SD卡完全一样,而且SD Nand相比SD卡感觉好用太多,贴片LGA-8封装,和SPI flash 差不多,完美的解决了SD卡松动导致系统不稳定的问题,而且容量又大,个人感觉以后必定是嵌入式存储应用上的主流 (除了价格贵点啥都好,哈哈)想要样片试试水的可以去找深圳雷龙公司官网申请下2. SDIO通用驱动框架介绍首先来介绍下 SDIO 通用驱动框架。 RT-Thread 区别于其他操作系统,如FreeRTOS,的一大重要特征是,RT-Thread 中引入了设备驱动框架,并且针对绝大多数外设基本上都已完成对应的设备驱动框架编写,所谓的设备驱动框架,也就是我们所说的建立在应用层与底层驱动层之间的中间件如下图所示:应用层:完成业务应用,调用通用接口操作设备驱动层设备驱动框架层:完成外设通用驱动框架设计,脱离具体的芯片,将驱动中相同部分,如针对SPI,关于SPI的完整读写逻辑等抽离出来设备驱动层:完成对应芯片的外设驱动程序编写,实现设备驱动框架层的具体接口SD NAND,贴片式TF卡,贴片式SD卡对于SDIO外设亦是如此:在设备驱动框架层中,实现SD卡、SDIO卡、MMC卡的通用外设驱动逻辑,如卡的识别、卡的模块切换、卡的读写操作等,这些都是通用的,遵循SD标准协议;在设备驱动层中,根据对应的硬件,完成具体芯片的SDIO外设配置,并实现设备驱动框架层所需要实现的具体接口,如发送CMD命令等。 在应用层实现具体的应用,应用层与驱动层解耦通过这种方式,这样便可以轻松的做到:需要驱动具体的SD、SDIO、MMC时,根据具体的芯片实现对应的SDIO驱动接口即可应用层可直接移植,如出现方案芯片替代时,只需完成设备驱动层适配即可这也就是RT-Thread让众多开发者疯狂追捧的重大原因了,接下来,我们将具体分析关于SD卡的具体框架层实现,关于SDIO卡、MMC卡,由于使用不多,本文不做深入分析。 3. 文件架构分析首先我们先来看下SDIO驱动框架有关文件及架构SDIO驱动框架文件:SD NAND,贴片式TF卡,贴片式SD卡SDIO驱动框架文件架构:SD NAND,贴片式TF卡,贴片式SD卡4. SDIO设备驱动分析设备驱动与驱动框架文件在不同的目录,设备驱动一般在 bsp 目录中通常设备驱动完成以下几个事情:初始化具体外设有关数据结构;完成具体外设初始化程序编写;实现设备框架层的具体接口,如:open,read,write,close,control 等;将具体设备注册到内核中;需要注意的是,SDIO设备驱动会有些许区别,在SDIO设备驱动程序中,主要完成以下几件事:初始化具体外设有关数据结构;SDIO外设的初始化配置;实现设备框架层的以下几个接口:struct rt_mmcsd_host_ops {void (*request)(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req);void (*set_iocfg)(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg);rt_int32_t (*get_card_status)(struct rt_mmcsd_host *host);void (*enable_sdio_irq)(struct rt_mmcsd_host *host, rt_int32_t en);};4.通知驱动框架层(此处demo程序默认上电前sd卡已接入);以 rt-thread/bsp/stm32/libraries/HAL_Drivers/drv_sdio.c 程序为例,SDIO驱动层程序从 rt_hw_sdio_init 函数开始,由于使能了自动初始化,此函数由 INIT_DEVICE_EXPORT(rt_hw_sdio_init); 宏实现初始化调用(关于自动初始化如何实现的细节,可参考笔者另外一篇博文对自动初始化的详细分析:代码自动初始化(点击跳转)) 在 rt_hw_sdio_init 函数中,驱动程序主要初始化以下几个结构体:stm32外设HAL库配置结构体 SD_HandleTypeDef hsdstm32 sdio 设备结构体 struct stm32_sdio_des sdio_dessdio硬件外设结构体 struct rthw_sdio *sdiommc sd host结构体struct rt_mmcsd_host其关系如下图所示:SD NAND,贴片式TF卡,贴片式SD卡结构体数据初始化完成以后,调用 mmcsd_change() 函数,触发框架层逻辑SD NAND,贴片式TF卡,贴片式SD卡此外,在设备驱动层提供的操作函数主要有:SD NAND,贴片式TF卡,贴片式SD卡SD NAND,贴片式TF卡,贴片式SD卡static const struct rt_mmcsd_host_ops ops ={rthw_sdio_request,rthw_sdio_iocfg,rthw_sd_detect,rthw_sdio_irq_update,};rthw_sdio_request 实现一次SDIO数据发送rthw_sdio_iocfg 实现SDIO外设配置,注意在SD识别过程中会反复调用,不断更新SDIO外设配置rthw_sd_detect 实现获取卡的状态获取,demo里这里实际没有实现rthw_sdio_irq_update 实现SDIO外设中断的开关配置函数调用顺序如下:/* 函数调用顺序 */rt_hw_sdio_init() -> sdio_host_create(&sdio_des) -> mmcsd_change(host) 5. SDIO设备驱动架构分析设备驱动架构层,也就是中间层,文件框架如下图所示:SD NAND,贴片式TF卡,贴片式SD卡我们首先来看下 mmcsd_core.c 这个文件:rt_mmcsd_core_init() 初始化函数通过 INIT_PREV_EXPORT(rt_mmcsd_core_init); 被初始化调用,同时初始化用于 mmc、sd、sdio检测的邮箱mmcsd_detect_mb,用于热插拔处理的 mmcsd_hotpluge_mb 以及 mmc、sd、sdio检测线程 mmcsd_detect_thread;在线程mmcsd_detect_thread 中,等待mmcsd_detect_mb邮箱唤醒;当SDIO驱动层完成初始化话之后,通过调用 mmcsd_change(host) 函数,将mmcsd_detect_thread线程唤醒,开始进行mmc、sd卡、sdio卡的识别过程mmcsd_core_init() 函数内容如下:int rt_mmcsd_core_init(void) {rt_err_t ret;/* initialize detect SD cart thread *//* initialize mailbox and create detect SD card thread */ret = rt_mb_init(&mmcsd_detect_mb, "mmcsdmb",&mmcsd_detect_mb_pool[0], sizeof(mmcsd_detect_mb_pool) / sizeof(mmcsd_detect_mb_pool[0]),RT_IPC_FLAG_FIFO);RT_ASSERT(ret == RT_EOK);ret = rt_mb_init(&mmcsd_hotpluge_mb, "mmcsdhotplugmb",&mmcsd_hotpluge_mb_pool[0], sizeof(mmcsd_hotpluge_mb_pool) / sizeof(mmcsd_hotpluge_mb_pool[0]),RT_IPC_FLAG_FIFO);RT_ASSERT(ret == RT_EOK);ret = rt_thread_init(&mmcsd_detect_thread, "mmcsd_detect", mmcsd_detect, RT_NULL,&mmcsd_stack[0], RT_MMCSD_STACK_SIZE, RT_MMCSD_THREAD_PREORITY, 20);if (ret == RT_EOK) {rt_thread_startup(&mmcsd_detect_thread);}rt_sdio_init();return 0;}INIT_PREV_EXPORT(rt_mmcsd_core_init);mmcsd_detect()线程以及 mmcsd_change() 函数如下:mmcsd_detect() 函数主要负责完成 SDIO卡、SD卡、MMC卡的初步识别,初步识别确认是哪种类型的卡接入之后,将会调用对应卡驱动文件(SD卡对应sd.c,SDIO卡对应sdio.c,MMC卡对应mmc.c)内的初始化函数,重新完成卡的完整识别流程如果对于SD卡识别流程不了解,建议先熟悉SD卡识别流程,参考 SD Nand 与 SD卡 SDIO模式应用流程(点击跳转) 具体流程见下述函数描述,对应步骤已补充注释描述void mmcsd_change(struct rt_mmcsd_host *host) {rt_mb_send(&mmcsd_detect_mb, (rt_uint32_t)host);}void mmcsd_detect(void *param) {struct rt_mmcsd_host *host;rt_uint32_t ocr;rt_int32_t err;while (1) {/* 首先等待 mmcsd_detect_mb 信号量,此信号量由 mmcsd_change() 函数发送过来 */if (rt_mb_recv(&mmcsd_detect_mb, (rt_ubase_t *)&host, RT_WAITING_FOREVER) == RT_EOK) {/* 通过判断 host->card 确认此次操作是识别卡还是移除卡 */if (host->card == RT_NULL) /* 识别卡 */{mmcsd_host_lock(host); /* 获取锁 */mmcsd_power_up(host); /* 配置SDIO外设电源控制器,power up, 即卡的时钟开启,同时配置SDIO外设时钟为低速模式 */mmcsd_go_idle(host); /* 发送CMD0指令,使卡进入空闲状态 */mmcsd_send_if_cond(host, host->valid_ocr); /* 发送CMD8命令,查询SD卡接口条件 (获取OCR寄存器) *//** 检测SDIO卡使用,SD卡不用管*/err = sdio_io_send_op_cond(host, 0, &ocr); /* 发送CMD5命令,此处是针对SDIO卡使用,SD卡不会响应 */if (!err) /* SD卡不会响应此指令,因此此条件不会成立 */{if (init_sdio(host, ocr)) mmcsd_power_off(host);mmcsd_host_unlock(host);continue;}/** 检测SD卡使用,使用SD卡重点关注此项!!! */err = mmcsd_send_app_op_cond(host, 0, &ocr); /* 发送ACMD41指令(ACMD41:CMD55+CMD41) SD卡将应答此指令 */if (!err) {if (init_sd(host, ocr)) /* 此函数内完成SD卡完整的识别流程 */mmcsd_power_off(host); /* 设置SDIO外设,电源关闭,卡的时钟停止 */mmcsd_host_unlock(host); /* 释放锁 */rt_mb_send(&mmcsd_hotpluge_mb, (rt_uint32_t)host); /* 发送邮箱,通知热插拔事件 */continue;}/** 检测MMC卡检测使用,SD卡不用管*/err = mmc_send_op_cond(host, 0, &ocr);if (!err) {if (init_mmc(host, ocr)) mmcsd_power_off(host);mmcsd_host_unlock(host);rt_mb_send(&mmcsd_hotpluge_mb, (rt_uint32_t)host);continue;}mmcsd_host_unlock(host); /* 识别失败,释放锁 */}else /* 移除卡 */{/* card removed */mmcsd_host_lock(host); /* 获取锁 */if (host->card->sdio_function_num != 0) |
|