嵌入式Linux SD/SDIO驱动核心:MMC框架、设备树与调试实战
搞了这么久嵌入式SD卡驱动这块总算是琢磨透了。这篇文章专门讲Linux下SD卡和SDIO驱动的核心部分内容比较细从框架到代码再到我踩过的那些坑一次说清楚。1. 先把整个SDIO子系统的大框架摸清楚刚接触SD/SDIO驱动的时候最容易犯的毛病就是一头扎进协议文档里死磕SD卡的命令时序结果折腾半天连驱动该往哪儿挂都不知道。其实在Linux里SD卡和SDIO设备走的是一条路——mmc子系统整个框架分得特别清楚就三层。最底层是host controller驱动对应你板子上的SDIO控制器一般是芯片内部的IP也可能走USB读卡器转出来的。中间那层是mmc core处理协议和状态机对上提供统一的接口。最上层是card驱动和块设备层SD卡走到block层变成/dev/mmcblk0SDIO WiFi或者蓝牙设备则走到各自的总线驱动去。这三层的关系用个比喻挺好理解的。host controller是银行柜台mmc core是银行大厅的引导流程card驱动就是你要办的业务。你不需要跟柜台后面的人直接喊话一切都走流程。这个架构设计的好处非常明显对驱动开发者来说真的是福音。各个芯片厂商的SDIO控制器硬件寄存器千差万别但只要按照host controller的接口把底层搞定上面那些协议处理、分区管理、卡检测机制全都复用内核现成的。所以你做驱动的核心工作集中在两点把host controller驱动写对把设备侧驱动的probe回调实现好。中间那个mmc core绝大多数情况下你只调用它的接口不用动它。2. 驱动入口的注册流程和设备树配置2.1 host controller驱动的注册核心看代码先从入口开始。一个标准的mmc host controller驱动注册路径大概是这样的static const struct of_device_id mymmc_of_match[] { { .compatible vendor,my-sdio-controller }, { } }; static struct platform_driver mymmc_driver { .probe mymmc_probe, .remove mymmc_remove, .driver { .name my-sdio, .of_match_table mymmc_of_match, .pm mymmc_pm_ops, }, }; module_platform_driver(mymmc_driver);这里有个细节.name字段和compatible字段不一定要一样但强烈建议保持一致。因为你调试的时候用ls /sys/bus/platform/drivers/看驱动列表名字对得上会省很多事。module_platform_driver这个宏其实就是把module_init和module_exit封装了等价于注册一个platform驱动。接着看probe函数这里面的流程就是个流水线static int mymmc_probe(struct platform_device *pdev) { struct sdhci_host *host; struct resource *res; struct mymmc_priv *priv; int ret; priv devm_kzalloc(pdev-dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; host sdhci_pltfm_init(pdev, my_sdhci_pdata, sizeof(*priv)); if (IS_ERR(host)) { dev_err(pdev-dev, sdhci pltfm init failed: %ld\n, PTR_ERR(host)); return PTR_ERR(host); } ... }注意这个sdhci_pltfm_init它是标准SDHCI控制器驱动的通用初始化入口。如果你的芯片用的是标准SDHCI IP直接用这一套就行能省掉很多麻烦。但如果你用的是私有协议或者simplified MMC控制器那就要手动去分配mmc_host结构了完全另一条路。probe里面几个关键的初始化动作一个都不能省使能时钟包括AHB总线的时钟和卡时钟。卡时钟频率初始化时一般先设为400KHz这是SD协议规定的初始化识别时钟频率等卡进入传输模式后才切到高速。申请中断或者使能DMA能力。SDIO控制器一般都支持SDMA或者ADMA2DMA这块建议直接把ADMA2配好性能差异非常大。填充sdhci_host的ops回调这些是真正干活的底层函数。调用sdhci_setup_host和sdhci_add_host把host添加到mmc core。注册完成后你在/sys/bus/platform/devices/目录对应的设备节点下就能看到mmc host的注册信息了。2.2 设备树配置的关键节点设备树这块是实际调试中最容易出问题的地方。一个基本的SDIO控制器节点长这样sdhci0 { compatible vendor,my-sdio-controller; reg 0x0 0xff370000 0x0 0x1000; interrupts 0 96 4; bus-width 8; non-removable; no-sdio; no-sd; cap-mmc-highspeed; mmc-hs200-1_8v; mmc-hs400-1_8v; vmmc-supply vmmc_regulator; vqmmc-supply vqmmc_regulator; status okay; };这里每个属性都在告诉内核你的板子长什么样子。bus-width是8说明板子上拉了8根数据线走的是eMMCnon-removable表示eMMC是焊死的不能拔no-sdio和no-sd则告诉内核这个控制器只接eMMC别把SD卡和SDIO设备的协议分支也开着能省不少初始化时间。关键是vqmmc-supply这是IO电平的供电。如果你的eMMC要跑HS200或者HS400模式IO电压需要切到1.8V这个regulator必须有。如果没有这个属性内核默认认为不能切电压就最高跑到HS模式性能上不去。提示改完设备树之后别只检查语法一定得看probe有没有真正跑起来。很多问题不出在你写的设备树语法上而是regulator没配置好导致probe失败。3. 请求链路的核心机制与数据结构流转3.1 mmc request的完整生命周期搞清楚框架之后最值得深入钻研的就是一个读写请求是怎么从用户态一路走到寄存器的。这条链路是SDIO驱动的核心脉络。先从应用层发一个read操作说起。VFS把请求交给块设备层块设备层合并、排序后生成一个struct request然后通过mmc block驱动转成struct mmc_request这里面的关键组件有struct mmc_command、struct mmc_data、struct mmc_host这几个结构体。其中mmc_command代表一条SD命令比如CMD17是单块读CMD18是多块读。mmc_data则描述数据传输的方向、块数、块大小、DMA内存地址等信息。这些结构体打包在一起交给host controller驱动去执行。执行过程是异步的。host controller拿到request后配置DMA描述符、设置寄存器然后写命令寄存器触发传输。传输完成后触发中断中断处理函数里调用mmc_cmd_done或者mmc_data_done一路回调上去最后唤醒等待的进程。这里要注意的是mmc core的请求队列是串行的同一时刻只有一个request在执行。这点和网络子系统不一样。它的设计是为了保证SD卡操作的顺序性因为你没法让卡同时执行两条命令。3.2 timeout值的把控和遗留中断问题实际写host驱动时timeout处理是最容易整出毛病的地方。内核每个request都带timeout时间如果硬件没能在预期时间内完成操作就要调用超时回调。你要做的就是在struct mmc_host_ops里把request和timeout这两个回调实现好。static void mymmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct mymmc_host *host mmc_priv(mmc); unsigned long flags; spin_lock_irqsave(host-lock, flags); /* 填充DMA描述符 */ /* 配置控制器寄存器 */ /* 触发命令 */ spin_unlock_irqrestore(host-lock, flags); }这里一个很反直觉的细节是这个函数在执行时可能已经持有spinlock了所以回调里面你不能再调用任何可能睡眠的函数。我之前就踩过这个坑在request回调里顺手调了msleep等待前一条命令完成结果直接死锁。正确做法是如果硬件还在忙就返回BUSY让mmc core稍后再重试。还有遗留中断的问题。新板子首次跑驱动的时候probe完成之后可能会立刻收到一个莫名其妙的中断。这种通常是上电时序导致的中断状态寄存器残留你必须在初始化时完全清空中断状态。检查一下sdhci_clear_set_irqs这个调用是否生效它的第二个参数要传ALL_INT_ENABLE确保把所有历史中断都清掉。3.3 DMA映射的精细处理DMA这块也比较阴。很多SDIO控制器支持的是ADMA2它的描述符格式有要求——每项描述符要设置valid、end标志位而且地址必须按8字节对齐。如果对齐不符合要求控制器就卡死在那里表现为请求提交后一直不产生完成中断。找到这个问题的过程很有代表性。最开始以为中断没配好从头到尾查了两遍中断控制器配置还是老样子。后来把DMA描述符地址打印出来才发现那块内存没对齐。可以这样修struct mmc_data *data mrq-data; if (data-sg_count 1) { /* 单段DMA直接映射 */ >dmesg | grep mmc如果你看到mmc0: error -110 whilst initialising SD card这种错误-110是ETIMEDOUT说明卡没有应答。这时候优先查时钟和供电是否正常。我调试时一般这样验证host控制器是否注册成功ls /sys/class/mmc_host/ ls /sys/bus/platform/drivers/my-sdio/ cat /sys/class/mmc_host/mmc0/device/uevent有时还会看/sys/kernel/debug/mmc0/下的信息需要先挂载debugfs。mmc-utils这个工具也很关键其中的mmc extcsd read可以读取eMMC的扩展寄存器mmc status get可以获取卡状态。你可以通过这些命令确认卡处于什么状态再对照数据手册排查问题。4.3 确认控制器工作模式的一些手段如果你不确定当前SD卡跑在哪个模式下面在Linux 5.x时代可以用/sys/kernel/debug/mmc0/ios查看当前的时序、电压、总线宽度cat /sys/kernel/debug/mmc0/ios它会输出类似clock: 200000000 Hz vdd: 21 (3.3 ~ 3.4 V) bus mode: 2 (push-pull) chip select: 0 (dont care) power mode: 2 (on) bus width: 2 (8-bit) timing spec: 9 (mmc hs400) signal voltage: 0 (1.8 V)看到timing spec: 9 (mmc hs400)和bus width: 2 (8-bit)就说明eMMC已经跑在HS400模式了。如果只有HS200说明mode切换没成功基本是信号完整性问题。5. SDIO设备侧的初始化与枚举机制5.1 设备驱动的匹配和probe执行顺序SDIO设备的驱动写起来跟普通驱动有些区别。它不挂在platform总线上而是挂在sdio总线上由mmc core管理。驱动结构一般是这样的static const struct sdio_device_id mywifi_id_table[] { { SDIO_DEVICE(0x6666, 0x1111) }, { } }; static struct sdio_driver mywifi_driver { .probe mywifi_probe, .remove mywifi_remove, .name mywifi, .id_table mywifi_id_table, }; module_driver(mywifi_driver, sdio_register_driver, sdio_unregister_driver);注意这里匹配的是VID和DID对应SDIO卡内部的FBRFunction Base Register里读出来的信息。每次复位后mmc core会重新扫描总线发现设备ID匹配就会执行probe。5.2 probe里不为人注意的IO延迟问题SDIO设备probe时最常见的坑是设备刚上电还没稳定你就去读写它的寄存器结果操作失败。解决办法是添加必要的延迟static int mywifi_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct mywifi_dev *dev; int ret; /* 等待设备完成内部初始化 */ msleep(50); dev kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; sdio_claim_host(func); ret sdio_enable_func(func); if (ret) goto err; ret sdio_set_block_size(func, 512); if (ret) goto err; sdio_release_host(func); /* 读设备信息确认通信正常 */ sdio_claim_host(func); ret sdio_readb(func, 0x00, err); sdio_release_host(func); ... }这里必须强调SDIO设备的所有I/O操作都要用sdio_claim_host和sdio_release_host包起来。这是因为SDIO_function是共享同一个物理host控制器的如果不claim就可能和卡访问冲突。很多刚入行的同事总是忘掉这一步probe有时灵有时不灵其实就是这个原因。sdio_set_block_size这一步也容易被忽略。默认的block size是512字节但某些SDIO设备要求以特定大小传输不设置会导致数据错误。根据设备手册配置正确的块大小这是在probe里必须完成的事情。5.3 IRQ线程化机制的取舍SDIO设备处理中断时一般用工作队列或者线程化IRQ。如果直接用request_irq中断上下文里只能做短暂操作大量数据传输还是要放到下半部。Linux对SDIO IRQ有一种特殊的机制——你可以通过sdio_claim_irq注册一个回调这个回调会在mmc core的专用线程里执行可以安全地调用sdio_readb、sdio_writeb这些函数。这个特性用起来非常方便。举个例子SDIO WiFi的中断往往是电平触发型的收到中断后必须主动读设备的中断状态寄存器才能清掉。直接用request_irq在中断上下文里操作SDIO函数寄存器会带来巨大的复杂度。换成sdio_claim_irq直接在线程里读寄存器轻松很多。但要注意不要两个一起注册否则会冲突导致probe失败。6. 几个典型问题的排查思路记录驱动开发里面最值钱的经验就是排错的方法论。根据我多次折腾SD卡和SDIO驱动的经历列几个典型的故障场景每个都附上排查思路大家可以直接当checklist来用。6.1 板子启动后完全没有mmc设备生成这种问题不用怀疑就是host controller层面没通。按这个顺序排查第一步确认probe有没有执行。在probe函数入口加一句dev_info打印看看驱动加载没有。如果没执行查设备树节点有没有写对compatible和驱动match表是否对应。第二步如果probe执行了但是后面报错看中断有没有正确注册。有些SDIO控制器的中断是wire到GIC的如果设备树里interrupt属性写错了等卡插入时无法感知设备就不会枚举。第三步确认时钟有没有使能。规范要求初始化时卡时钟是400KHz如果没使能或者频率不对卡完全不响应表现为mmc0: error -110 whilst initialising SD card。这种问题还有一种常见隐藏原因——供电。SD卡的VDD和VDDQ分开供电如果VDDQ没供上卡的IO口没有电平等于不存在。6.2 读卡速度上不去只能跑到默认速率这个现象一般是两个原因设备树没配置对应的speed mode属性或者硬件信号质量不够。先查设备树mmc-hs200-1_8v、mmc-hs400-1_8v这些属性一个都不能少。再查初始化日志里卡是否识别到了HS200/HS400。如果识别到了但实际速度上不去十有八九是PCB走线或者上拉电阻的问题。这里有个很有意思的经验SD卡的卡检测虽然也有时钟线但它能启动时靠的是CMD线的上拉。如果上拉电阻焊错了位置甚至漏焊卡也能枚举成功但跑到高速模式时会偶发出错。这类问题特别难查因为刚开始低速模式一切正常一上高速就随机挂。物理检查时重点看这几条线CLK线尽量短DATA线和CMD线不要交叉CLK线旁边不要走高频信号线。还有一点1.8V电平转换芯片的摆率不能太慢否则VCCO切换后时序余量不够跑HS400就挂。6.3 系统睡眠唤醒后设备丢失这种情况经常让没有电源管理经验的同事头疼不已。启动时一切正常跑起来读写也没问题但只要系统suspend唤醒后/dev/mmcblk0就消失了或者SDIO WiFi连不上了。排查方向放在两点一是检查驱动有没有实现pm_ops。标准驱动需要实现suspend和resume回调在resume的时候重新初始化硬件。很多芯片在睡眠掉电后之前配置的寄存器内容全丢了不重新初始化就完全没法用。另一个是供电链路的睡眠策略。如果vmmc-regulator在睡眠时被关掉了而resume时又没有重新拉起来设备自然就消失了。有一个容易忽略的细节mmc core有个功能叫runtime PM如果驱动的runtime suspend/resume没实现好系统suspend时也会触发runtime流程导致时序混乱。解决办法很简单要么完整实现runtime PM回调要么在设备树里加上mmc-pm-ignore-removable这类属性来绕过runtime检查。6.4 数据传输偶发CRC错误这个问题的两个主要方向时钟频率和信号质量。先降低时钟频率试一下。如果降到50MHz就稳定说明信号质量确实有瓶颈。如果50MHz也随机出错那要考虑DMA描述符的地址对齐问题前面提到过的ADMA2对齐要求。还可以看下卡的供电是否稳定特别是电流瞬态响应。可以用示波器抓一下VDD波形如果传输时电压跌落超过10%那就是电源问题加大电容或者换低内阻的LDO。我遇到的几次CRC错误最后发现几乎都是电源的锅。调试手段小结与体会写了这么长最后再说说调试工具的使用心得。除了/sys/kernel/debug/mmc0/ios之外抓协议分析波形我常用逻辑分析仪放在CLK/CMD/DAT0这几根线上。SD协议2.0以上的模式都比较容易解析逻辑分析仪价格也不高算是性价比最高的调试器。调试时尽量保持一个变量的原则不要同时改设备树、驱动代码和电压配置。改完一个层面验证一个层面不然出了问题没法定位。另外如果你手头有两种不同的SD卡或者eMMC颗粒建议都插上去试试。不同厂商的卡对时序的容错性不同。有的卡严格按规范来有点余量不足就失败有的卡容错性好能让问题晚暴露。多试几片卡能有效验证你的驱动是否可靠。SDIO驱动开发这活入门门槛其实不高但要真正调稳定了是非常吃经验的。框架理解清楚了原语用对了加上一套系统的排错思路大部分问题都会变得有迹可循。希望这篇内容能帮你少走点弯路。

相关新闻

C语言——makefile的使用

C语言——makefile的使用

makefile如果有多个.c文件要编译,用gcc编译每次都要重新输入,每次要编很多,如果出错改好又要全部都编一遍,如gcc main.c add.c sub.c来把文件拼接成a.out,这是很浪费时间和编译效率的这时候用makefile可以提升效率使用规则如下写m…

2026/10/17 10:46:08 阅读更多 →
学习算法之双指针篇(1)(c++版)

学习算法之双指针篇(1)(c++版)

一、概念和分类 双指针是一种‌用两个指针协同移动来减少遍历次数、降低时间复杂度的思路。 常见的有对撞指针和快慢指针。 1.对撞指针 • 对撞指针是从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。终⽌条件⼀般是两个…

2026/10/17 10:46:08 阅读更多 →
【高质量毕设开题】 基于微信小程序的智慧校园生活服务平台设计与实现(源码+报告)

【高质量毕设开题】 基于微信小程序的智慧校园生活服务平台设计与实现(源码+报告)

温馨提示: 本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示: 本人主页置顶文章(点我)开头有 CSDN平台官方提供的学长联系方式的名片! 温馨提示: 本人主页置顶文章(点我)开头有 CSDN 平…

2026/10/17 10:46:08 阅读更多 →

最新新闻

Argon架构解析:面向毫秒级SLA的AI服务三层解耦设计

Argon架构解析:面向毫秒级SLA的AI服务三层解耦设计

1. 项目概述:这不是一次简单的模型调用,而是一次系统级工程重构“Google 用 Gemini 4 Argon 构建案例”——这个标题乍看像一则新闻简报,但作为在AI基础设施一线摸爬滚打十多年的从业者,我第一反应是:这根本不是在讲“…

2026/10/17 11:32:28 阅读更多 →
n8n集成Gemini 4 Argon与MCP协议构建真Agent工作流

n8n集成Gemini 4 Argon与MCP协议构建真Agent工作流

1. 这不是“又一个AI工作流”,而是n8n生态里首次真正打通MCP协议的Gemini原生集成 我第一次在某跨平台自动化实验室看到这个标题时,下意识点开想确认是不是标题党——毕竟过去两年里,“用XX模型接入n8n”这类文章我至少扫过四十多篇&#xff…

2026/10/17 11:32:28 阅读更多 →
意图优先的版本控制系统:重构代码协作的认知范式

意图优先的版本控制系统:重构代码协作的认知范式

1. 项目概述:这不是又一个Git客户端,而是一次底层协作范式的重写“7个人、5个月,Anthropic造出了Git替代品”——这个标题在技术圈刷屏时,我正带着团队在做一个跨时区协同的嵌入式固件项目。每天早上打开终端,git pull…

2026/10/17 11:32:28 阅读更多 →
DVWA中CSP绕过原理与四类实战利用手法详解

DVWA中CSP绕过原理与四类实战利用手法详解

1. 项目概述:CSP不是铁壁,而是可被理解、可被分析、可被绕过的策略体系“CSP Bypass”这个词在渗透测试和Web安全研究圈里,几乎等同于一次对开发者安全意识的现场压力测试。它不制造漏洞,却能放大漏洞的价值;它不依赖服…

2026/10/17 11:32:28 阅读更多 →
Vue高德地图动态绘制路线与轨迹巡航实战:从坐标转换到动画性能优化

Vue高德地图动态绘制路线与轨迹巡航实战:从坐标转换到动画性能优化

做后台管理系统这么多年,我接到过最典型的“地图需求”就是标题里这个:高德地图动态绘制路线,轨迹巡航,还指定要用 Vue。第一次听可能觉得不就是画一条线加个小车跑吗,真做完才发现里面全是细节:坐标要转换…

2026/10/17 11:32:28 阅读更多 →
STM32F103移植W5500 ioLibrary:SPI驱动与socket通信实战

STM32F103移植W5500 ioLibrary:SPI驱动与socket通信实战

简介:面向基于STM32F103RCT6等MINISTM32平台的嵌入式开发者,这套W5500 ioLibrary移植工程包含完整的驱动适配代码,可直接参考使用。W5500是集成SPI接口的硬核以太网控制器,自带TCP/IP协议栈,配合STM32的SPI外设即可实现…

2026/10/17 11:31:27 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →