Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
SDIO总线
SD卡_TF卡
设备驱动框架
rt-thread SDIO驱动框架分析之SD卡驱动
发布于 2023-03-07 00:09:09 浏览:2285
订阅该版
[tocm] # rt-thread SDIO驱动框架分析之SD卡驱动 > 本文与本人CSDN博客一同发布,CSDN账号:**爱出名的狗腿子** > 原创文章,转载请注明出处! > CSDN博客首页:https://blog.csdn.net/qq_43332314?type=blog > 本文对应CSDN博客地址:http://t.csdn.cn/AWQmY ## 1. 前言 RT-Thread是一款国产化的嵌入式操作系统,目前在嵌入式领域得到广泛应用,其强大的扩展功能以及通用的外设驱动框架备受大家追捧。 关于基本的外设驱动,其官网上基本也都有部分描述,但是关于SDIO设备驱动目前为止还没有相关文档说明,因此本文笔者将根据自己的调试使用经验,与大家分享下rtthread的通用SDIO设备驱动的实现。 > RT-Thread github开源地址:https://github.com/RT-Thread/rt-thread > 本文基于代码仓库 [rt-thread/bsp/stm32/stm32f103-fire-arbitrary](https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32f103-fire-arbitrary) 分析代码 > + 分支:main > + commit: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的完整读写逻辑等抽离出来 + 设备驱动层:完成对应芯片的外设驱动程序编写,实现设备驱动框架层的具体接口 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/aead540b41a4f705d67fa929e206dd01.png.webp) 对于SDIO外设亦是如此: + 在设备驱动框架层中,实现SD卡、SDIO卡、MMC卡的通用外设驱动逻辑,如卡的识别、卡的模块切换、卡的读写操作等,这些都是通用的,遵循SD标准协议; + 在设备驱动层中,根据对应的硬件,完成具体芯片的SDIO外设配置,并实现设备驱动框架层所需要实现的具体接口,如发送CMD命令等。 + 在应用层实现具体的应用,应用层与驱动层解耦 通过这种方式,这样便可以轻松的做到: 1. 需要驱动具体的SD、SDIO、MMC时,根据具体的芯片实现对应的SDIO驱动接口即可 2. 应用层可直接移植,如出现方案芯片替代时,只需完成设备驱动层适配即可 这也就是RT-Thread让众多开发者疯狂追捧的重大原因了,接下来,我们将具体分析关于SD卡的具体框架层实现,关于SDIO卡、MMC卡,由于使用不多,本文不做深入分析。 ## 3. 文件架构分析 首先我们先来看下SDIO驱动框架有关文件及架构 + SDIO驱动框架文件: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/0e1edd3be1be49f16f155963574060ee.png) + SDIO驱动框架文件架构 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/858c8facb65c2fa2d177c05d6dce6715.png.webp) ## 4. SDIO设备驱动分析 设备驱动与驱动框架文件在不同的目录,设备驱动一般在 `bsp` 目录中 通常设备驱动完成以下几个事情: 1. 初始化具体外设有关数据结构; 2. 完成具体外设初始化程序编写; 3. 实现设备框架层的具体接口,如:`open`,`read`,`write`,`close`,`control` 等; 4. 将具体设备注册到内核中; 需要注意的是,SDIO设备驱动会有些许区别,在SDIO设备驱动程序中,主要完成以下几件事: 1. 初始化具体外设有关数据结构; 2. SDIO外设的初始化配置; 3. 实现设备框架层的以下几个接口: ```c 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](https://github.com/RT-Thread/rt-thread/blob/master/bsp/stm32/libraries/HAL_Drivers/drv_sdio.c) 程序为例,SDIO驱动层程序从 `rt_hw_sdio_init` 函数开始,由于使能了自动初始化,此函数由 `INIT_DEVICE_EXPORT(rt_hw_sdio_init);` 宏实现初始化调用 (关于自动初始化如何实现的细节,可参考笔者另外一篇博文对自动初始化的详细分析:[代码自动初始化(点击跳转)](https://blog.csdn.net/qq_43332314/article/details/126033631)) --- 在 `rt_hw_sdio_init` 函数中,驱动程序主要初始化以下几个结构体: + stm32外设HAL库配置结构体 `SD_HandleTypeDef hsd` + stm32 sdio 设备结构体 `struct stm32_sdio_des sdio_des` + sdio硬件外设结构体 `struct rthw_sdio *sdio` + mmc sd host结构体`struct rt_mmcsd_host` 其关系如下图所示: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/755917abda0435378f34d9aa795e2ba4.png.webp) 结构体数据初始化完成以后,调用 `mmcsd_change()` 函数,触发框架层逻辑 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/dee22161e2921ea32a5ca8e3c50bb4e5.png) --- 此外,在设备驱动层提供的操作函数主要有: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/1444c1bb58f4d57d226f0bd30bd935f0.png) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/2c1130a0b0a0392f5f8c05d2b20f4326.png) ```c 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外设中断的开关配置 --- 函数调用顺序如下: ```c /* 函数调用顺序 */ rt_hw_sdio_init() -> sdio_host_create(&sdio_des) -> mmcsd_change(host) ``` ## 5. SDIO设备驱动架构分析 设备驱动架构层,也就是中间层,文件框架如下图所示: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230312/0e10cbe19be8776cc46128c50b460a13.png.webp) 我们首先来看下 `mmcsd_core.c` 这个文件: 1. `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`; 2. 在线程`mmcsd_detect_thread` 中,等待`mmcsd_detect_mb`邮箱唤醒; 3. 当SDIO驱动层完成初始化话之后,通过调用 `mmcsd_change(host)` 函数,将`mmcsd_detect_thread`线程唤醒,开始进行mmc、sd卡、sdio卡的识别过程 + `mmcsd_core_init()` 函数内容如下: ```c 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模式应用流程(点击跳转)](https://blog.csdn.net/qq_43332314/article/details/128592423) + 具体流程见下述函数描述,对应步骤已补充注释描述 ```c 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) { LOG_W("unsupport sdio card plug out!"); } else { rt_mmcsd_blk_remove(host->card); rt_free(host->card); host->card = RT_NULL; } mmcsd_host_unlock(host); /* 释放锁 */ rt_mb_send(&mmcsd_hotpluge_mb, (rt_uint32_t)host); } } } } ``` + 在 `mmcsd_detect()` 函数内完成SD卡的初步识别之后,之后将调用`sd.c`文件内的`init_sd()` 函数完成 sd 卡的完整识别过程 ```c /* * Starting point for SD card init. */ rt_int32_t init_sd(struct rt_mmcsd_host *host, rt_uint32_t ocr) { rt_int32_t err; rt_uint32_t current_ocr; /* * We need to get OCR a different way for SPI. */ if (controller_is_spi(host)) /* 判断是否采用SPI模式访问SD卡 */ { mmcsd_go_idle(host); err = mmcsd_spi_read_ocr(host, 0, &ocr); if (err) goto err; } if (ocr & VDD_165_195) { LOG_I(" SD card claims to support the " "incompletely defined 'low voltage range'. This " "will be ignored."); ocr &= ~VDD_165_195; } current_ocr = mmcsd_select_voltage(host, ocr); /* 配置SDIO外设设置为合适的电压,对于stm32、gd32等相关控制器,实际是不支持不同等级电压配置的,所以这里可以忽略,不过你需要注意你所使用的sd卡的电源在硬件上是匹配的 */ /* * Can we support the voltage(s) of the card(s)? */ if (!current_ocr) { err = -RT_ERROR; goto err; } /* * Detect and init the card. */ err = mmcsd_sd_init_card(host, current_ocr); /* 完整的SD卡初始化流程在此函数内实现 */ if (err) goto err; mmcsd_host_unlock(host); /* 释放锁 */ err = rt_mmcsd_blk_probe(host->card); /* 注册块设备 */ if (err) /* 如果注册块设备失败,将移除卡 */ goto remove_card; mmcsd_host_lock(host); /* 获取锁 */ return 0; remove_card: mmcsd_host_lock(host); /* 获取锁 */ rt_mmcsd_blk_remove(host->card); /* 移除块设备 */ rt_free(host->card); /* 释放对应的内存 */ host->card = RT_NULL; err: LOG_D("init SD card failed!"); return err; } ``` + 调用 `mmcsd_sd_init_card()` 函数完成SD卡检测以及初始化配置 ```c static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host, rt_uint32_t ocr) { struct rt_mmcsd_card *card; rt_int32_t err; rt_uint32_t resp[4]; rt_uint32_t max_data_rate; mmcsd_go_idle(host); /* 发送CMD0,复位SD卡,使卡进入空闲模式 */ /* * If SD_SEND_IF_COND indicates an SD 2.0 * compliant card and we should set bit 30 * of the ocr to indicate that we can handle * block-addressed SDHC cards. */ err = mmcsd_send_if_cond(host, ocr); /* 发送CMD8指令,判断是否为V2.0或V2.0以上的卡,并获取OCR寄存器值 */ if (!err) /* 如果是V2.0及以上版本的卡,将置为OCR的bit30位,表明主机支持高容量SDHC卡(OCR将在ACMD41指令时作为参数发送给卡) */ ocr |= 1 << 30; err = mmcsd_send_app_op_cond(host, ocr, RT_NULL); /* 发送ACMD41(ACMD41 = CMD55+CMD41)指令,发送主机容量支持信息,并询问卡的操作条件 */ if (err) goto err; if (controller_is_spi(host)) /* 判断是否使用SPI方式访问SD卡 */ err = mmcsd_get_cid(host, resp); /* 采用SPI方式获取CID寄存器值 */ else err = mmcsd_all_get_cid(host, resp);/* 发送CMD2命令,获取CID寄存器值 */ if (err) goto err; card = rt_malloc(sizeof(struct rt_mmcsd_card)); /* 创建rt_mmcsd_card结构体,用于存储对应SD卡的CID寄存器内容 */ if (!card) { LOG_E("malloc card failed!"); err = -RT_ENOMEM; goto err; } rt_memset(card, 0, sizeof(struct rt_mmcsd_card)); card->card_type = CARD_TYPE_SD; card->host = host; rt_memcpy(card->resp_cid, resp, sizeof(card->resp_cid)); /* * For native busses: get card RCA and quit open drain mode. */ if (!controller_is_spi(host)) /* 如果不是采用SPI方式访问SD卡 */ { err = mmcsd_get_card_addr(host, &card->rca); /* 发送CMD3命令,获取RCA地址 */ if (err) goto err1; mmcsd_set_bus_mode(host, MMCSD_BUSMODE_PUSHPULL);/* 设置CMD总线为推挽输出模式,需要注意的是,MMC卡V3.31版本以前的卡,初始化阶段,CMD总线需要为开路模式,对于SD/SD I/O卡和MMC V4.2在初始化时也使用推挽驱动 */ } err = mmcsd_get_csd(card, card->resp_csd); /* 发送CMD9命令,获取CSD寄存器值 */ if (err) goto err1; err = mmcsd_parse_csd(card); /* 解析CSD寄存器值,将解析完成的数据存放在刚刚申请的card结构体内 */ if (err) goto err1; if (!controller_is_spi(host)) /* 如果不是采用SPI方式访问SD卡 */ { err = mmcsd_select_card(card); /* 发送CMD7命令,选择卡 */ if (err) goto err1; } err = mmcsd_get_scr(card, card->resp_scr); /* 发送CMD9命令,获取SCR寄存器值,并保存在刚刚申请的card结构体内 */ if (err) goto err1; mmcsd_parse_scr(card); /* 解析SCR寄存器的值,并将解析结果存放在在card结构体内 */ if (controller_is_spi(host)) { err = mmcsd_spi_use_crc(host, 1); if (err) goto err1; } /* * change SD card to high-speed, only SD2.0 spec */ err = mmcsd_switch(card); /* 发送CMD6指令,切换卡访问速率由默认的12.5MB/Sec为25MB/Sec高速接口 */ if (err) goto err1; /* set bus speed */ max_data_rate = (unsigned int)-1; if (card->flags & CARD_FLAG_HIGHSPEED) { if (max_data_rate > card->hs_max_data_rate) max_data_rate = card->hs_max_data_rate; } else if (max_data_rate > card->max_data_rate) { max_data_rate = card->max_data_rate; } mmcsd_set_clock(host, max_data_rate); /* 修改SDIO外设时钟速度 */ /*switch bus width*/ if ((host->flags & MMCSD_BUSWIDTH_4) && (card->scr.sd_bus_widths & SD_SCR_BUS_WIDTH_4)) /* 根据SD卡的SCR寄存器反馈的值,判断SD卡是否支持4线宽度访问模式,如果支持则切换为4线宽度访问模式 */ { err = mmcsd_app_set_bus_width(card, MMCSD_BUS_WIDTH_4); /* 发送ACMD6(ACMD6=CMD55+CMD6)指令,通知SD卡切换为4线访问模式 */ if (err) goto err1; mmcsd_set_bus_width(host, MMCSD_BUS_WIDTH_4); /* 修改SDIO外设配置为4线访问模式 */ } host->card = card; /* 将card结构体数据与host结构体建立绑定关系 */ return 0; err1: rt_free(card); err: return err; } ``` --- ## 6. 调试记录 RT-Thread的SDIO驱动,默认上层使用到了 `elm-fatfs` 文件系统,因此通常我们配置好对应的芯片的SDIO驱动之后,直接就可以快速使用文件系统来操作访问SD Nand了,关于文件系统的有关内容,不在此文中做过多描述,有兴趣的同学可以关注本人博客,后续将及时更新。 此外,在实际使用中有一点需要注意,当我们首次使用芯片的时候,sd nand内还未写入任何数据,此时通常是没有文件系统的,所以当一次执行之后你会见到如下错误: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230314/e0295f1df6b3574cb208623f87247de1.png) 这是由于SD nand内没有挂载文件系统导致,解决此问题有以下两个方法: + 方法一: + 在命令终端使用`mkfs`挂载文件系统,具体命令步骤如下: + 使用list_device查看sd nand对应的设备名 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230314/f91aa1b6c8c2697cf1872f62f4428c9f.png) + 使用 `mkfs` 命令格式化sd nand:`mkfs -t elm sd0` (`-t` 指定文件系统类型为elm-FAT文件系统,对sd0设备操作) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230314/cc84d6359e334d136db8416a90bbe7b7.png) + 方法二:将SD nand通过读卡器,插入电脑,在电脑上进行格式化U盘操作,不过此操作需要SD nand的转接板,如下图所示: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230316/db766b3dd152470c0c13fc01378e4a39.png.webp) ## 7. 总结 以上便是SD卡的识别与初始化流程,整体流程简单的梳理一下,大致如下: 1. 由 `drv_sdio.c` 外设驱动或其他调用 `mmcsd_change()` 触发 `mmcsd_detect()` 检测 2. 在 `mmcsd_detect ()` 任务中,实现对SD卡、SD I/O卡、MMC卡的初步识别(发送对应卡特有命令,并判断是否正确响应),之后根据卡片类型调用不同类型卡片驱动文件内的初始化程序 *如针对SD卡,则调用`sd.c`文件内的 `init_sd()` 函数完成* 3. 在`init_sd()`函数内调用 `mmcsd_sd_init_card()` 完成SD卡的完整识别流程以及初始化流程,同时同步修改SDIO外设配置 4. SD卡初始化完成之后,调用 `rt_mmcsd_blk_probe()` 将sd卡注册为块设备 5. 至此SD的识别与初始化流程顺利完成 **相关文章推荐:** + [(点击跳转)SD Nand 与 SD卡 SDIO模式应用流程](https://blog.csdn.net/qq_43332314/article/details/128592423) + [(点击跳转)SD nand与SD卡 SPI模式驱动](https://blog.csdn.net/qq_43332314/article/details/128363320) --- **创作不易,转载请注明出处!** **关注、点赞+收藏,可快速查看后续分享哦!** > 本文与本人CSDN博客一同发布,CSDN账号:**爱出名的狗腿子** > 原创文章,转载请注明出处! > CSDN博客首页:https://blog.csdn.net/qq_43332314?type=blog > 本文对应CSDN博客地址:http://t.csdn.cn/AWQmY
2
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
jaffer
You can contact me by Email, jaffer.work@foxmail.com, if you want.
文章
4
回答
10
被采纳
0
关注TA
发私信
相关文章
1
代码分享,stm32f407的sdio驱动。
2
求个在STM32F103 RTT上使用SDIO接口的例子
3
是否能增加一个 SDIO AP61xx 的驱动呢
4
sdio和fsmc驱动lcd冲突
5
发现了sdio_unregister_driver里的一个bug
6
用sdio框架驱动过w8801(wifi模块)的坛友请进(已解决)
7
关于rt-thread系统sdio驱动框架使用
8
问题请教,关于在rt-thread中调试RT1052中调试sdio wifi模块
9
【文件系统学习】+海中陆地+RT-Thread文件系统学习挂载SDIO...
10
sdio驱动是怎么使用的?也没有文档和例程啊
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
5
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部