SD 卡在上电后的完整加载流程:从中断信号到文件系统挂载

SD 卡在上电后的完整加载流程:从中断信号到文件系统挂载

一篇讲清 SD 卡驱动在上电阶段的完整工作链路------从 CPU 如何访问硬件寄存器,到 CD 引脚电平检测、中断向量跳转、识别线程发送的每一条命令,最终到文件系统挂载。全文面向具备 RT-Thread 基础、但尚未深入外设中断与 MMIO 机制的嵌入式开发者。

引言

很多人调试 SD 卡时,只会在 rt_device_find("sd0") 成功后开始读写,却说不清"卡到底是怎么被挂载上去的"。这篇文章不讨论读写性能,聚焦上电时刻 驱动从无到有、最终让 /sd/ 可用的完整链路。

整条链路可以拆成四段:

  1. 地基:软件如何"够到"硬件寄存器(MMIO 与寄存器写入)
  2. 搭建:上电时驱动如何被内核自动调用,如何建立 host 与事件对象
  3. 检测:SD 卡插入状态如何被判定(CD 引脚电平、去抖、判位)
  4. 挂载:中断/事件如何驱动识别线程逐条发命令、最终把卡挂载为文件系统

先交代一个容易忽略的前提:卡插入状态与"有没有发生中断"是两回事,这决定了上电与热插拔走的是两条不同的触发路径,后面会专门对比。

开篇先用一段判定流程回答读者最关心的两个问题------"这个中断是什么"以及"已插卡的上电是否会触发中断":
#mermaid-svg-Y7G9UOOUJeYFOjDx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Y7G9UOOUJeYFOjDx .error-icon{fill:#552222;}#mermaid-svg-Y7G9UOOUJeYFOjDx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Y7G9UOOUJeYFOjDx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .marker.cross{stroke:#333333;}#mermaid-svg-Y7G9UOOUJeYFOjDx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Y7G9UOOUJeYFOjDx p{margin:0;}#mermaid-svg-Y7G9UOOUJeYFOjDx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cluster-label text{fill:#333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cluster-label span{color:#333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cluster-label span p{background-color:transparent;}#mermaid-svg-Y7G9UOOUJeYFOjDx .label text,#mermaid-svg-Y7G9UOOUJeYFOjDx span{fill:#333;color:#333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .node rect,#mermaid-svg-Y7G9UOOUJeYFOjDx .node circle,#mermaid-svg-Y7G9UOOUJeYFOjDx .node ellipse,#mermaid-svg-Y7G9UOOUJeYFOjDx .node polygon,#mermaid-svg-Y7G9UOOUJeYFOjDx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Y7G9UOOUJeYFOjDx .rough-node .label text,#mermaid-svg-Y7G9UOOUJeYFOjDx .node .label text,#mermaid-svg-Y7G9UOOUJeYFOjDx .image-shape .label,#mermaid-svg-Y7G9UOOUJeYFOjDx .icon-shape .label{text-anchor:middle;}#mermaid-svg-Y7G9UOOUJeYFOjDx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Y7G9UOOUJeYFOjDx .rough-node .label,#mermaid-svg-Y7G9UOOUJeYFOjDx .node .label,#mermaid-svg-Y7G9UOOUJeYFOjDx .image-shape .label,#mermaid-svg-Y7G9UOOUJeYFOjDx .icon-shape .label{text-align:center;}#mermaid-svg-Y7G9UOOUJeYFOjDx .node.clickable{cursor:pointer;}#mermaid-svg-Y7G9UOOUJeYFOjDx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .arrowheadPath{fill:#333333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Y7G9UOOUJeYFOjDx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y7G9UOOUJeYFOjDx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Y7G9UOOUJeYFOjDx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y7G9UOOUJeYFOjDx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Y7G9UOOUJeYFOjDx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cluster text{fill:#333;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cluster span{color:#333;}#mermaid-svg-Y7G9UOOUJeYFOjDx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Y7G9UOOUJeYFOjDx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Y7G9UOOUJeYFOjDx rect.text{fill:none;stroke-width:0;}#mermaid-svg-Y7G9UOOUJeYFOjDx .icon-shape,#mermaid-svg-Y7G9UOOUJeYFOjDx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y7G9UOOUJeYFOjDx .icon-shape p,#mermaid-svg-Y7G9UOOUJeYFOjDx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Y7G9UOOUJeYFOjDx .icon-shape .label rect,#mermaid-svg-Y7G9UOOUJeYFOjDx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y7G9UOOUJeYFOjDx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Y7G9UOOUJeYFOjDx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Y7G9UOOUJeYFOjDx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cd>*{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cd span{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .cd tspan{fill:#0b3d22!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .irq>*{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .irq span{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .irq tspan{fill:#2a1a5e!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .evt>*{fill:#d2f4f4!important;stroke:#1c9e9e!important;color:#083d3d!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .evt span{fill:#d2f4f4!important;stroke:#1c9e9e!important;color:#083d3d!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .evt tspan{fill:#083d3d!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .hw>*{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .hw span{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-Y7G9UOOUJeYFOjDx .hw tspan{fill:#103a80!important;} 上电已在位

电平稳定无跳变
插拔边沿跳变
SD 卡插入
上电 or 热插拔?
主动查询 CDETECT
硬件中断 SDIO_INT_CARD_DET
软发事件 rt_event_send
CLIC 向量跳转 → ISR
检测线程 → 加载

核心结论:"中断"指的是 SDIO 控制器的卡检测中断(SDIO_INT_CARD_DET已插卡的上电不走中断,走主动查询,中断留给真正产生边沿的热插拔。


一、地基:软件如何访问硬件寄存器

1.1 内存映射输入输出(MMIO)

CPU 执行读写指令时,它只负责把目标地址放到地址总线 上,并通过控制信号声明本次是读还是写。CPU 自身并不区分地址背后是内存、是 CLIC 中断控制器、还是 UART------这个裁决由总线上的一段译码电路完成。

这段电路是地址译码器(Address Decoder) :它比较地址总线的高位,判断本次地址落入哪台设备的地址区间,并只对该设备发出有效的片选信号 。片选无效的设备,其数据输出处于高阻态,不参与总线响应。

于是就有了内存映射输入/输出(MMIO)的定义:外设寄存器以内存地址的形式暴露给 CPU,软件用普通的读地址、写地址指令即可操作外设。 例如某设备的中断控制器寄存器组被映射在地址空间的某一段,软件通过指针访问它:

c 复制代码
#define XXX_BASE  0x????0000UL          /* 中断控制器基地址------具体值由芯片决定 */
#define XXX       ((XXX_Type *)XXX_BASE)

此后 XXX->REG = value 就等价于向那片寄存器写入一个值。

1.2 寄存器与"写寄存器"

寄存器是一组能保存 0/1 的存储单元 ,硬件上由多个 D 触发器(Flip-Flop) 构成------每个 D 触发器保存 1 bit,8 个即组成一个 8 位寄存器。D 触发器的行为是:在时钟有效沿到来时,把数据输入端的 0/1 抄入并保持;时钟不来则维持原值。

由此,"写寄存器"的定义清晰起来:

写寄存器 = 触发目标 D 触发器的时钟,把数据总线上的值锁存进该存储单元,并让其持续保持。

它是一次状态设定而非一次性的数据传递------写入的 0/1 会一直保持,直到下次被改写。这也解释了为何软件写一次中断使能位后,那个中断会持续可用。

一次完整的"写寄存器"在硬件上按以下顺序发生:

  1. CPU 指令给出地址、数据与写使能标记
  2. 地址译码器按地址选中目标外设并置其片选有效
  3. 目标寄存器的 D 触发器在(片选 ∧ 写使能)共同产生的时钟沿上,锁存数据总线上的值
  4. 锁存值经内部组合逻辑持续控制对应功能(如接通/断开某条中断请求通道)

理解这一点,后面所有"打开中断""清除标志"的代码才不是黑盒。


二、搭建:驱动如何被内核自动加载

2.1 自动初始化机制

RT-Thread 的驱动不是在上电后手动调用的,而是靠自动初始化 。内核按固定等级顺序执行 INIT_*_EXPORT 宏注册的所有函数:

初始化等级 就绪内容
板级硬件 INIT_BOARD_EXPORT GPIO、时钟
组件 INIT_COMPONENT_EXPORT 内核线程工具
环境 INIT_ENV_EXPORT 文件系统等组件
应用 INIT_APP_EXPORT SD 卡驱动在此执行

SD 卡驱动通过 INIT_APP_EXPORT(rt_hw_sdio_init) 挂入链表,上电到应用阶段时内核自动调用它。

2.2 host 与事件对象

rt_hw_sdio_init 对每个 SDIO 控制器执行 sdio_host_create,其核心工作有两项,分别对应两个关键数据结构:

  • host(主机控制器) :即 rt_mmcsd_host 结构体,描述该 SDIO 控制器与 SD 卡的关联。创建过程会分配内存、绑定一组操作回调 (发命令、配总线、读卡检测、开关中断),并把 host 与底层硬件句柄 hw_sdio 关联。
  • 事件对象(event) :由 rt_event_init(&hw_sdio->event, ...) 创建的内核 IPC 对象,用于跨线程通知。检测线程阻塞等待该事件,ISR 或初始化代码向其发送标志。

它们在源码中的作用可以对应到如下关键行:

c 复制代码
/* 创建并启动检测线程,它随后阻塞等待卡检测事件 */
hw_sdio->detect_thread =
    rt_thread_create(name, rthw_mmcsd_detect, host,
                     RT_MMCSD_DETECT_STACK_SIZE, 25, 10);
rt_thread_startup(hw_sdio->detect_thread);

/* 初始化事件对象 */
rt_event_init(&hw_sdio->event, name, RT_IPC_FLAG_PRIO);

/* host 绑定能力:操作回调表 + 基础配置 */
host->ops = &ops;           /* {发命令, 配总线, 读卡检测, 开关中断} */
host->freq_min = 400000;    /* SD 协议规定的启动时钟 */
host->valid_ocr = ...;      /* 支持的电压范围 */

需要强调的是:本阶段只完成资源与能力登记,尚未读写卡片。 真正的检测发生在下一段的 rthw_sd_detect()

上电后从 INIT_APP_EXPORT 到挂载前的执行步骤可概括为下列流程:
#mermaid-svg-oD7GkgesolPkk7JK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oD7GkgesolPkk7JK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oD7GkgesolPkk7JK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oD7GkgesolPkk7JK .error-icon{fill:#552222;}#mermaid-svg-oD7GkgesolPkk7JK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oD7GkgesolPkk7JK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oD7GkgesolPkk7JK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oD7GkgesolPkk7JK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oD7GkgesolPkk7JK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oD7GkgesolPkk7JK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oD7GkgesolPkk7JK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oD7GkgesolPkk7JK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oD7GkgesolPkk7JK .marker.cross{stroke:#333333;}#mermaid-svg-oD7GkgesolPkk7JK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oD7GkgesolPkk7JK p{margin:0;}#mermaid-svg-oD7GkgesolPkk7JK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oD7GkgesolPkk7JK .cluster-label text{fill:#333;}#mermaid-svg-oD7GkgesolPkk7JK .cluster-label span{color:#333;}#mermaid-svg-oD7GkgesolPkk7JK .cluster-label span p{background-color:transparent;}#mermaid-svg-oD7GkgesolPkk7JK .label text,#mermaid-svg-oD7GkgesolPkk7JK span{fill:#333;color:#333;}#mermaid-svg-oD7GkgesolPkk7JK .node rect,#mermaid-svg-oD7GkgesolPkk7JK .node circle,#mermaid-svg-oD7GkgesolPkk7JK .node ellipse,#mermaid-svg-oD7GkgesolPkk7JK .node polygon,#mermaid-svg-oD7GkgesolPkk7JK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oD7GkgesolPkk7JK .rough-node .label text,#mermaid-svg-oD7GkgesolPkk7JK .node .label text,#mermaid-svg-oD7GkgesolPkk7JK .image-shape .label,#mermaid-svg-oD7GkgesolPkk7JK .icon-shape .label{text-anchor:middle;}#mermaid-svg-oD7GkgesolPkk7JK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oD7GkgesolPkk7JK .rough-node .label,#mermaid-svg-oD7GkgesolPkk7JK .node .label,#mermaid-svg-oD7GkgesolPkk7JK .image-shape .label,#mermaid-svg-oD7GkgesolPkk7JK .icon-shape .label{text-align:center;}#mermaid-svg-oD7GkgesolPkk7JK .node.clickable{cursor:pointer;}#mermaid-svg-oD7GkgesolPkk7JK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oD7GkgesolPkk7JK .arrowheadPath{fill:#333333;}#mermaid-svg-oD7GkgesolPkk7JK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oD7GkgesolPkk7JK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oD7GkgesolPkk7JK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oD7GkgesolPkk7JK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oD7GkgesolPkk7JK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oD7GkgesolPkk7JK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oD7GkgesolPkk7JK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oD7GkgesolPkk7JK .cluster text{fill:#333;}#mermaid-svg-oD7GkgesolPkk7JK .cluster span{color:#333;}#mermaid-svg-oD7GkgesolPkk7JK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oD7GkgesolPkk7JK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oD7GkgesolPkk7JK rect.text{fill:none;stroke-width:0;}#mermaid-svg-oD7GkgesolPkk7JK .icon-shape,#mermaid-svg-oD7GkgesolPkk7JK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oD7GkgesolPkk7JK .icon-shape p,#mermaid-svg-oD7GkgesolPkk7JK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oD7GkgesolPkk7JK .icon-shape .label rect,#mermaid-svg-oD7GkgesolPkk7JK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oD7GkgesolPkk7JK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oD7GkgesolPkk7JK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oD7GkgesolPkk7JK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-oD7GkgesolPkk7JK .hw>*{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-oD7GkgesolPkk7JK .hw span{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-oD7GkgesolPkk7JK .hw tspan{fill:#103a80!important;}#mermaid-svg-oD7GkgesolPkk7JK .cd>*{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-oD7GkgesolPkk7JK .cd span{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-oD7GkgesolPkk7JK .cd tspan{fill:#0b3d22!important;}#mermaid-svg-oD7GkgesolPkk7JK .evt>*{fill:#d2f4f4!important;stroke:#1c9e9e!important;color:#083d3d!important;}#mermaid-svg-oD7GkgesolPkk7JK .evt span{fill:#d2f4f4!important;stroke:#1c9e9e!important;color:#083d3d!important;}#mermaid-svg-oD7GkgesolPkk7JK .evt tspan{fill:#083d3d!important;} 是

上电复位
INIT_APP_EXPORT

rt_hw_sdio_init
创建 host + 事件 + 检测线程
CD 引脚在位?
发送 CARD_DET 事件
使能中断

待热插拔
检测线程 → 加载


三、检测:SD 卡插入状态如何被判定

3.1 CD 引脚与机械开关

SD 卡插座上有一根专门的引脚 CD(Card Detect) ,它连接插座内的机械行程开关,随插卡动作改变通断:

  • 未插卡 :开关断开(开路),由上拉电阻 把 CD 引脚保持在高电平
  • 完全插入 :卡片边缘压合开关(短路到地),CD 引脚被拉到低电平
  • 半插入:触点反复接通/断开,引脚电平在高、低之间抖动------这正是需要"去抖"的原因。

因此,"卡是否插好"的本质判定,是检测 CD 引脚的电平是否为低

CD 引脚的电平状态由机械开关与上拉电阻共同决定,其判定关系如下:
#mermaid-svg-oWhe3gFGa2AlLT1Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oWhe3gFGa2AlLT1Y .error-icon{fill:#552222;}#mermaid-svg-oWhe3gFGa2AlLT1Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oWhe3gFGa2AlLT1Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .marker.cross{stroke:#333333;}#mermaid-svg-oWhe3gFGa2AlLT1Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oWhe3gFGa2AlLT1Y p{margin:0;}#mermaid-svg-oWhe3gFGa2AlLT1Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .cluster-label text{fill:#333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .cluster-label span{color:#333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .cluster-label span p{background-color:transparent;}#mermaid-svg-oWhe3gFGa2AlLT1Y .label text,#mermaid-svg-oWhe3gFGa2AlLT1Y span{fill:#333;color:#333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .node rect,#mermaid-svg-oWhe3gFGa2AlLT1Y .node circle,#mermaid-svg-oWhe3gFGa2AlLT1Y .node ellipse,#mermaid-svg-oWhe3gFGa2AlLT1Y .node polygon,#mermaid-svg-oWhe3gFGa2AlLT1Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oWhe3gFGa2AlLT1Y .rough-node .label text,#mermaid-svg-oWhe3gFGa2AlLT1Y .node .label text,#mermaid-svg-oWhe3gFGa2AlLT1Y .image-shape .label,#mermaid-svg-oWhe3gFGa2AlLT1Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-oWhe3gFGa2AlLT1Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oWhe3gFGa2AlLT1Y .rough-node .label,#mermaid-svg-oWhe3gFGa2AlLT1Y .node .label,#mermaid-svg-oWhe3gFGa2AlLT1Y .image-shape .label,#mermaid-svg-oWhe3gFGa2AlLT1Y .icon-shape .label{text-align:center;}#mermaid-svg-oWhe3gFGa2AlLT1Y .node.clickable{cursor:pointer;}#mermaid-svg-oWhe3gFGa2AlLT1Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .arrowheadPath{fill:#333333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oWhe3gFGa2AlLT1Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oWhe3gFGa2AlLT1Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oWhe3gFGa2AlLT1Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oWhe3gFGa2AlLT1Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oWhe3gFGa2AlLT1Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oWhe3gFGa2AlLT1Y .cluster text{fill:#333;}#mermaid-svg-oWhe3gFGa2AlLT1Y .cluster span{color:#333;}#mermaid-svg-oWhe3gFGa2AlLT1Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oWhe3gFGa2AlLT1Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oWhe3gFGa2AlLT1Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-oWhe3gFGa2AlLT1Y .icon-shape,#mermaid-svg-oWhe3gFGa2AlLT1Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oWhe3gFGa2AlLT1Y .icon-shape p,#mermaid-svg-oWhe3gFGa2AlLT1Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oWhe3gFGa2AlLT1Y .icon-shape .label rect,#mermaid-svg-oWhe3gFGa2AlLT1Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oWhe3gFGa2AlLT1Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oWhe3gFGa2AlLT1Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oWhe3gFGa2AlLT1Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-oWhe3gFGa2AlLT1Y .on>*{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .on span{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .on tspan{fill:#0b3d22!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .off>*{fill:#fde8d6!important;stroke:#e07e2f!important;color:#6b3209!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .off span{fill:#fde8d6!important;stroke:#e07e2f!important;color:#6b3209!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .off tspan{fill:#6b3209!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .jit>*{fill:#fdf3cf!important;stroke:#d4aa2c!important;color:#5a4a06!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .jit span{fill:#fdf3cf!important;stroke:#d4aa2c!important;color:#5a4a06!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .jit tspan{fill:#5a4a06!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .dec>*{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .dec span{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-oWhe3gFGa2AlLT1Y .dec tspan{fill:#2a1a5e!important;} 未插卡

开关断开
上拉

引脚=高
完全插入

压合到地
引脚=低
半插入

触点抖动
高/低抖动
CDETECT=1

无卡
CDETECT=0

有卡
需去抖后判定

3.2 去抖

机械开关的抖动会导致电平在插入瞬间不稳定。驱动通过写 SDIO 控制器的去抖寄存器来过滤:

c 复制代码
HAL_SDIO_DEBNCE_SET(&hsdio, Hal_Sys_AhbClkGet() / 50u);  /* 约 20ms */

DEBNCE 寄存器设置了一个约 20ms 的窗口:只有当 CD 引脚电平连续稳定 20ms,才认定该电平有效。 半插入状态下触点持续抖动,无法通过这一判决,从而避免误判。

3.3 判位:读 CDETECT 寄存器

驱动层的 rthw_sd_detect 通过 HAL 函数读取控制器内部的卡检测状态:

c 复制代码
/* rthw_sd_detect */
if (Hal_Sdio_CardIsPresent(&hsdio)) {
    ret = 0x01;   /* 卡在位 */
} else {
    ret = 0x00;   /* 无卡 */
}

HAL 层的实现读取的是 CDETECT 寄存器(而非直接读某个通用 GPIO):

c 复制代码
rs_uint8_t Hal_Sdio_CardIsPresent(SDIO_HandleTypeDef *hsdio)
{
    rs_uint8_t isPresent = FALSE;
    if ((hsdio != NULL) && (hsdio->Index < HAL_SDIO_INDEX_MAX)) {
        if (0 == Hal_RegRead(SDIO_REG(hsdio->Index, CDETECT), REG_MASK_ALL, 0u)) {
            isPresent = TRUE;   /* CDETECT bit0 = 低 → 卡在位 */
        }
    }
    return isPresent;
}

因此判位逻辑非常简单:CDETECT.bit0 == 0(引脚被拉低)即判定卡在位。 整个判断收敛在 HAL 那一个条件里。

关于"是哪个引脚"的澄清:在该 SoC 上,卡检测走的是 SDIO 控制器自带的 CD 检测通道(CDETECT 寄存器),而非普通 GPIO 的可编程引脚。SD 的 CLK/CMD/DAT 引脚复用交由板级/FPGA 实现,软件层并不配置它们的 PIN。

从插卡到中断的完整信号链------五个环节依次传导,如图:
#mermaid-svg-CAqS6vQyGOj5sB1h{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CAqS6vQyGOj5sB1h .error-icon{fill:#552222;}#mermaid-svg-CAqS6vQyGOj5sB1h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CAqS6vQyGOj5sB1h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CAqS6vQyGOj5sB1h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CAqS6vQyGOj5sB1h .marker.cross{stroke:#333333;}#mermaid-svg-CAqS6vQyGOj5sB1h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CAqS6vQyGOj5sB1h p{margin:0;}#mermaid-svg-CAqS6vQyGOj5sB1h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CAqS6vQyGOj5sB1h .cluster-label text{fill:#333;}#mermaid-svg-CAqS6vQyGOj5sB1h .cluster-label span{color:#333;}#mermaid-svg-CAqS6vQyGOj5sB1h .cluster-label span p{background-color:transparent;}#mermaid-svg-CAqS6vQyGOj5sB1h .label text,#mermaid-svg-CAqS6vQyGOj5sB1h span{fill:#333;color:#333;}#mermaid-svg-CAqS6vQyGOj5sB1h .node rect,#mermaid-svg-CAqS6vQyGOj5sB1h .node circle,#mermaid-svg-CAqS6vQyGOj5sB1h .node ellipse,#mermaid-svg-CAqS6vQyGOj5sB1h .node polygon,#mermaid-svg-CAqS6vQyGOj5sB1h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CAqS6vQyGOj5sB1h .rough-node .label text,#mermaid-svg-CAqS6vQyGOj5sB1h .node .label text,#mermaid-svg-CAqS6vQyGOj5sB1h .image-shape .label,#mermaid-svg-CAqS6vQyGOj5sB1h .icon-shape .label{text-anchor:middle;}#mermaid-svg-CAqS6vQyGOj5sB1h .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CAqS6vQyGOj5sB1h .rough-node .label,#mermaid-svg-CAqS6vQyGOj5sB1h .node .label,#mermaid-svg-CAqS6vQyGOj5sB1h .image-shape .label,#mermaid-svg-CAqS6vQyGOj5sB1h .icon-shape .label{text-align:center;}#mermaid-svg-CAqS6vQyGOj5sB1h .node.clickable{cursor:pointer;}#mermaid-svg-CAqS6vQyGOj5sB1h .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CAqS6vQyGOj5sB1h .arrowheadPath{fill:#333333;}#mermaid-svg-CAqS6vQyGOj5sB1h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CAqS6vQyGOj5sB1h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CAqS6vQyGOj5sB1h .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CAqS6vQyGOj5sB1h .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CAqS6vQyGOj5sB1h .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CAqS6vQyGOj5sB1h .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CAqS6vQyGOj5sB1h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CAqS6vQyGOj5sB1h .cluster text{fill:#333;}#mermaid-svg-CAqS6vQyGOj5sB1h .cluster span{color:#333;}#mermaid-svg-CAqS6vQyGOj5sB1h div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CAqS6vQyGOj5sB1h .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CAqS6vQyGOj5sB1h rect.text{fill:none;stroke-width:0;}#mermaid-svg-CAqS6vQyGOj5sB1h .icon-shape,#mermaid-svg-CAqS6vQyGOj5sB1h .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CAqS6vQyGOj5sB1h .icon-shape p,#mermaid-svg-CAqS6vQyGOj5sB1h .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CAqS6vQyGOj5sB1h .icon-shape .label rect,#mermaid-svg-CAqS6vQyGOj5sB1h .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CAqS6vQyGOj5sB1h .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CAqS6vQyGOj5sB1h .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CAqS6vQyGOj5sB1h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-CAqS6vQyGOj5sB1h .g>*{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .g span{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .g tspan{fill:#0b3d22!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .y>*{fill:#fdf3cf!important;stroke:#d4aa2c!important;color:#5a4a06!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .y span{fill:#fdf3cf!important;stroke:#d4aa2c!important;color:#5a4a06!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .y tspan{fill:#5a4a06!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .bl>*{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .bl span{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .bl tspan{fill:#103a80!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .pu>*{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .pu span{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-CAqS6vQyGOj5sB1h .pu tspan{fill:#2a1a5e!important;} 插卡

开关压合
CD 引脚拉低
去抖 20ms
判位 CDETECT=0
置中断 CARD_DET


四、中断机制:从信号到 ISR

4.1 谁负责中断:RISC-V 的 CLIC

该 SoC 的 CPU 是 RISC-V 内核(T-Head E906),自带 CLIC(Core-Local Interrupt Controller)。所有外设(SDIO、UART、GPIO、DMA、ISP...)的中断请求汇入 CLIC 仲裁后进入 CPU。

CLIC 初始化时,为所有中断源启用向量中断模式

c 复制代码
for (i = 0; i < MAX_IRQn; i++) {
    CLIC->CLICINT[i].IP = 0;         /* 清除挂起位 */
    CLIC->CLICINT[i].ATTR |= 1;      /* SHV=1 → 向量跳转 */
}

SHV(Selectable Hardware Vector)位是关键:它让 CLIC 在中断被判定有效后,依据中断源编号直接向 CPU 提供处理函数地址 ,CPU 据此跳转------不经过软件查表

4.2 中断请求信号的本质

"外设向 CLIC 发送中断请求"在物理上不是发送数据,而是驱动一根专用中断请求线的电平。通常有两种形式:

  • 电平触发:线被持续驱动到有效电平,直到被清除才恢复。
  • 边沿触发:线上产生一次电平跳变(上升沿或下降沿)。

SD 卡检测属于电平锁存型 ------SD 卡中断标志由软件主动清除(__HAL_SDIO_CLEAR_INT),中断请求线在清除前持续有效。这印证了该信号是电平状态,而非一次性脉冲。

中断信号只承载"有事件发生"这一事实,不携带事件内容。 具体是哪类事件(卡插入、数据传输完成、超时...),CPU 在进入 ISR 后读取中断状态寄存器,逐位判断得出。

4.3 向量跳转:谁"调用"了 ISR

以 SDIO0 为例,SDIO0_IRQHandler 通过 ATTRIBUTE_ISR(即 __attribute__((interrupt("machine"))))声明,让编译器自动生成机器模式中断的现场保存/恢复与 mret 返回:

c 复制代码
ATTRIBUTE_ISR void SDIO0_IRQHandler(void)
{
    rt_interrupt_enter();
    if (host[HAL_SDIO_INDEX_0] != NULL)
        rthw_sdio_irq_process(host[HAL_SDIO_INDEX_0]);
    rt_interrupt_leave();
}

SDIO0_IRQHandler 并非由软件函数调用,而是由 CLIC 硬件向量跳转进入。 中断到来时,CLIC 依据 SDIO0 的中断源编号直接让 CPU 跳到该函数地址,CPU 硬件完成现场切换。ISR 内部 rt_interrupt_enter() / rt_interrupt_leave() 只是通知内核当前处于中断上下文。

ISR 内 rthw_sdio_irq_process 的处理极短:读中断状态寄存器 → 清除标志 → 逐位判断事件 → 若是卡检测位则通过 rt_event_send 发送事件标志。ISR 不执行耗时操作,仅负责发信号。

4.4 中断源的规模与函数对应关系

该 SoC 的中断源枚举覆盖了从内核定时器中断到十余类外设中断,数量上百。要澄清的一点是:每个中断源是否拥有专属处理函数,取决于驱动是否实现了同名的强符号。

  • 启动代码(vectors.S)用宏为每个中断源预置 .weak 弱符号,默认指向兜底的 Default_Handler
  • 有驱动实现的 (如 SDIO0_IRQHandlerUART0_IRQHandler)以强符号覆盖 weak,成为真正的处理函数。
  • 未实现的中断 停在 Default_Handler(检查是否非屏蔽中断,否则进入异常流程)。
  • GPIO 采用分组中断:每 16 根引脚共用一个中断源编号,共 8 个中断处理函数管理全部 GPIO 引脚。进 ISR 后仍需读取引脚状态做进一步细分。

因此"每个中断都有专属函数"并不绝对------架构上每个中断源占一个向量位,真正有专属函数的只有驱动写过的那些

SDIO 外设中断从发生到进入 ISR 的消息时序如下(注意全程由 CLIC 硬件仲裁与向量跳转,非软件分发):
ISR CPU 向量跳转 CLIC 中断 SDIO 控制器 ISR CPU 向量跳转 CLIC 中断 SDIO 控制器 #mermaid-svg-8aREbDRkVwsoWRE1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8aREbDRkVwsoWRE1 .error-icon{fill:#552222;}#mermaid-svg-8aREbDRkVwsoWRE1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8aREbDRkVwsoWRE1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8aREbDRkVwsoWRE1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8aREbDRkVwsoWRE1 .marker.cross{stroke:#333333;}#mermaid-svg-8aREbDRkVwsoWRE1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8aREbDRkVwsoWRE1 p{margin:0;}#mermaid-svg-8aREbDRkVwsoWRE1 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8aREbDRkVwsoWRE1 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-8aREbDRkVwsoWRE1 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8aREbDRkVwsoWRE1 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-8aREbDRkVwsoWRE1 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-8aREbDRkVwsoWRE1 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-8aREbDRkVwsoWRE1 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-8aREbDRkVwsoWRE1 .sequenceNumber{fill:white;}#mermaid-svg-8aREbDRkVwsoWRE1 #sequencenumber{fill:#333;}#mermaid-svg-8aREbDRkVwsoWRE1 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-8aREbDRkVwsoWRE1 .messageText{fill:#333;stroke:none;}#mermaid-svg-8aREbDRkVwsoWRE1 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8aREbDRkVwsoWRE1 .labelText,#mermaid-svg-8aREbDRkVwsoWRE1 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-8aREbDRkVwsoWRE1 .loopText,#mermaid-svg-8aREbDRkVwsoWRE1 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-8aREbDRkVwsoWRE1 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8aREbDRkVwsoWRE1 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-8aREbDRkVwsoWRE1 .noteText,#mermaid-svg-8aREbDRkVwsoWRE1 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-8aREbDRkVwsoWRE1 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8aREbDRkVwsoWRE1 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8aREbDRkVwsoWRE1 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8aREbDRkVwsoWRE1 .actorPopupMenu{position:absolute;}#mermaid-svg-8aREbDRkVwsoWRE1 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-8aREbDRkVwsoWRE1 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8aREbDRkVwsoWRE1 .actor-man circle,#mermaid-svg-8aREbDRkVwsoWRE1 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-8aREbDRkVwsoWRE1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 校验 IE + 优先级仲裁(SHV 向量模式) rt_event_send(CARD_DET) 唤醒检测线程 驱动中断请求线 置 IP 位提供处理函数地址硬件向量跳转


五、关键区分:上电插入 与 热插拔

这是整条链路最容易混淆的地方。两者最终都触发加载,但触发路径完全不同

维度 上电时已插卡 使用中热插拔
触发源 软件主动查询 硬件中断
是否走中断
触发机制 创建 host 时主动读 CDETECT CD 引脚边沿 → CLIC 向量跳转
时序前提 发生在中断使能之前 发生在中断使能之后
后续路径 殊途同归:均触发检测线程 → 识别 → 挂载 同左

为什么上电时已插卡不触发中断? 因为卡早在上电前就在位,CD 引脚电平是稳定态,没有边沿跳变,CLIC 不会据此产生中断。此时驱动选择在创建 host 的末尾主动调用一次检测

c 复制代码
if (rthw_sd_detect(host) == 0x01) {
    rt_event_send(&hw_sdio->event, SDIO_INT_CARD_DET);  /* 卡在位 → 直接发事件 */
}

于是上电走了"主动查询 + 直接发事件"的路径;而 SDIO_INT_CARD_DET 向量中断留给"使用中突然插入/拔出"这类真正的边沿事件。两者最终汇合于同一条加载流程。


六、挂载:从事件到文件系统

加载过程实际由多条线程协作完成------启动线程建立资源、检测线程等待并分发事件、识别线程下发命令。三者通过事件与邮箱传递信号,时序如下:
识别线程 检测线程 启动线程 识别线程 检测线程 启动线程 #mermaid-svg-7bggiEZAtyPtioO0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7bggiEZAtyPtioO0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7bggiEZAtyPtioO0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7bggiEZAtyPtioO0 .error-icon{fill:#552222;}#mermaid-svg-7bggiEZAtyPtioO0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7bggiEZAtyPtioO0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7bggiEZAtyPtioO0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7bggiEZAtyPtioO0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7bggiEZAtyPtioO0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7bggiEZAtyPtioO0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7bggiEZAtyPtioO0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7bggiEZAtyPtioO0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7bggiEZAtyPtioO0 .marker.cross{stroke:#333333;}#mermaid-svg-7bggiEZAtyPtioO0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7bggiEZAtyPtioO0 p{margin:0;}#mermaid-svg-7bggiEZAtyPtioO0 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7bggiEZAtyPtioO0 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-7bggiEZAtyPtioO0 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7bggiEZAtyPtioO0 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-7bggiEZAtyPtioO0 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-7bggiEZAtyPtioO0 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-7bggiEZAtyPtioO0 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-7bggiEZAtyPtioO0 .sequenceNumber{fill:white;}#mermaid-svg-7bggiEZAtyPtioO0 #sequencenumber{fill:#333;}#mermaid-svg-7bggiEZAtyPtioO0 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-7bggiEZAtyPtioO0 .messageText{fill:#333;stroke:none;}#mermaid-svg-7bggiEZAtyPtioO0 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7bggiEZAtyPtioO0 .labelText,#mermaid-svg-7bggiEZAtyPtioO0 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-7bggiEZAtyPtioO0 .loopText,#mermaid-svg-7bggiEZAtyPtioO0 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-7bggiEZAtyPtioO0 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7bggiEZAtyPtioO0 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-7bggiEZAtyPtioO0 .noteText,#mermaid-svg-7bggiEZAtyPtioO0 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-7bggiEZAtyPtioO0 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7bggiEZAtyPtioO0 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7bggiEZAtyPtioO0 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7bggiEZAtyPtioO0 .actorPopupMenu{position:absolute;}#mermaid-svg-7bggiEZAtyPtioO0 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-7bggiEZAtyPtioO0 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7bggiEZAtyPtioO0 .actor-man circle,#mermaid-svg-7bggiEZAtyPtioO0 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-7bggiEZAtyPtioO0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 收到事件,开始加载 识别 CMD0/ACMD41 建档 dfs_mount("/sd/") 挂载 rt_event_send(CARD_DET)mmcsd_change 发邮箱消息mmcsd_hotpluge_mb 通知完成

6.1 检测线程

事件标志 SDIO_INT_CARD_DET 唤醒的就是检测线程 rthw_mmcsd_detect。它一直阻塞在 rt_event_recv 上:

c 复制代码
while (1) {
    /* 阻塞等待卡检测事件,无事件时不占用 CPU */
    rt_event_recv(&hw_sdio->event, SDIO_INT_CARD_DET,
                  RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
                  RT_WAITING_FOREVER, NULL);

    hw_sdio->sdio_dev->use_hold_reg = 1;
    mmcsd_change(host);                    /* 通知识别线程 */
    ret = mmcsd_wait_cd_changed(300);      /* 等待识别结果 */

    if ((ret == MMCSD_HOST_PLUGED) && (rthw_sd_detect(host) == 0x01)) {
        dev = rt_device_find("sd0");
        if (dev && (dfs_mount("sd0", "/sd/", "elm", 0, 0) == 0)) {
            LOG_I("sd0 mount to /sd/ !");
            mmcsd_mount_over();
        }
    } else if ((ret == MMCSD_HOST_UNPLUGED) && (rthw_sd_detect(host) == 0x00)) {
        Hal_Sdio_DeInit(&hsdio);           /* 拔卡 → 反初始化 */
    }
}

这里 mmcsd_change 本身不处理识别,它只是向接收邮箱 mmcsd_detect_mb 投递一条消息,真正的识别在识别线程。

6.2 识别线程与命令序列

识别线程 mmcsd_detect 收到邮箱消息后,若当前 host->card == NULL(尚未初始化卡片),则依次下发识别命令。一套识别按 SDIO → SD → MMC 的顺序尝试:

c 复制代码
while (1) {
    rt_mb_recv(&mmcsd_detect_mb, &host, RT_WAITING_FOREVER);

    if (host->card == NULL) {
        mmcsd_power_up(host);                    /* 上电 + 延时稳定 */
        mmcsd_go_idle(host);                     /* CMD0 复位,至少 74 个时钟 */

        err = sdio_io_send_op_cond(host, 0, &ocr);  /* 先试 SDIO 卡 */
        if (!err) { if (init_sdio(host, ocr)) ...; continue; }

        mmcsd_go_idle(host);
        err = mmcsd_send_app_op_cond(host, 0, &ocr); /* 再试 SD 卡:CMD55 + ACMD41 */
        if (!err) {
            if (init_sd(host, ocr)) ...          /* 是 SD 卡 → 读 CID/CSD 建档 */
            rt_mb_send(&mmcsd_hotpluge_mb, &host);  /* 通知"卡已插好" */
            continue;
        }

        err = mmc_send_op_cond(host, 0, &ocr);   /* 最后试 MMC 卡 */
        ...
    }
}

识别原理:一张卡无法凭外观判断类型,只能靠下发命令"试探" 。SD 卡的关键命令是 ACMD41 ,其响应携带该卡支持的工作电压范围,据此确认是 SD 卡。init_sd 读取 CID/CSD 并建立块设备 sd0

6.3 挂载

识别完成后,检测线程等待在 mmcsd_wait_cd_changed(300),确认卡在位后执行 dfs_mount("sd0", "/sd/", "elm", 0, 0),把块设备 sd0 以 FAT(elm = Elm-Chan FatFs)文件系统挂载到路径 /sd/。至此,上层代码可通过 open("sd0/...") 读写 SD 卡文件。


七、完整链路与分层设计

将以上四段串起来,整条链路为:

复制代码
上电复位
  → INIT_APP_EXPORT 自动初始化
  → sdio_host_create:host + 事件对象 + 检测线程
  → 主动读 CD 引脚(CDETECT):卡在位 → 直接发事件
  → 检测线程收到事件 → 发邮箱
  → 识别线程:上电 → CMD0 → 试探 SDIO/SD/MMC → 建档 sd0
  → dfs_mount("sd0", "/sd/", "elm")
  → /sd/ 可读写

热插拔分支(另一张卡):
  → CD 引脚边沿 → CLIC 向量跳转 SDIO0_IRQHandler → 发事件
  → 汇入同上加载流程

值得强调的分层原则是:中断上下文只负责最小操作(读状态、清标志、发事件),真正的耗时工作(上电、逐条命令、读卡识别、文件系统挂载)都在普通线程上下文完成。 这是嵌入式设计中普遍遵循的"中断做最小、耗时进线程"原则,它保证了中断响应及时、系统不被长 ISR 阻塞。

完整流程的"一图流"汇总如下(涵盖上电与热插拔两条路径):
#mermaid-svg-4KoEjHdv2aYSUEtz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4KoEjHdv2aYSUEtz .error-icon{fill:#552222;}#mermaid-svg-4KoEjHdv2aYSUEtz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4KoEjHdv2aYSUEtz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4KoEjHdv2aYSUEtz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4KoEjHdv2aYSUEtz .marker.cross{stroke:#333333;}#mermaid-svg-4KoEjHdv2aYSUEtz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4KoEjHdv2aYSUEtz p{margin:0;}#mermaid-svg-4KoEjHdv2aYSUEtz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4KoEjHdv2aYSUEtz .cluster-label text{fill:#333;}#mermaid-svg-4KoEjHdv2aYSUEtz .cluster-label span{color:#333;}#mermaid-svg-4KoEjHdv2aYSUEtz .cluster-label span p{background-color:transparent;}#mermaid-svg-4KoEjHdv2aYSUEtz .label text,#mermaid-svg-4KoEjHdv2aYSUEtz span{fill:#333;color:#333;}#mermaid-svg-4KoEjHdv2aYSUEtz .node rect,#mermaid-svg-4KoEjHdv2aYSUEtz .node circle,#mermaid-svg-4KoEjHdv2aYSUEtz .node ellipse,#mermaid-svg-4KoEjHdv2aYSUEtz .node polygon,#mermaid-svg-4KoEjHdv2aYSUEtz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4KoEjHdv2aYSUEtz .rough-node .label text,#mermaid-svg-4KoEjHdv2aYSUEtz .node .label text,#mermaid-svg-4KoEjHdv2aYSUEtz .image-shape .label,#mermaid-svg-4KoEjHdv2aYSUEtz .icon-shape .label{text-anchor:middle;}#mermaid-svg-4KoEjHdv2aYSUEtz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4KoEjHdv2aYSUEtz .rough-node .label,#mermaid-svg-4KoEjHdv2aYSUEtz .node .label,#mermaid-svg-4KoEjHdv2aYSUEtz .image-shape .label,#mermaid-svg-4KoEjHdv2aYSUEtz .icon-shape .label{text-align:center;}#mermaid-svg-4KoEjHdv2aYSUEtz .node.clickable{cursor:pointer;}#mermaid-svg-4KoEjHdv2aYSUEtz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4KoEjHdv2aYSUEtz .arrowheadPath{fill:#333333;}#mermaid-svg-4KoEjHdv2aYSUEtz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4KoEjHdv2aYSUEtz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4KoEjHdv2aYSUEtz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4KoEjHdv2aYSUEtz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4KoEjHdv2aYSUEtz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4KoEjHdv2aYSUEtz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4KoEjHdv2aYSUEtz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4KoEjHdv2aYSUEtz .cluster text{fill:#333;}#mermaid-svg-4KoEjHdv2aYSUEtz .cluster span{color:#333;}#mermaid-svg-4KoEjHdv2aYSUEtz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4KoEjHdv2aYSUEtz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4KoEjHdv2aYSUEtz rect.text{fill:none;stroke-width:0;}#mermaid-svg-4KoEjHdv2aYSUEtz .icon-shape,#mermaid-svg-4KoEjHdv2aYSUEtz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4KoEjHdv2aYSUEtz .icon-shape p,#mermaid-svg-4KoEjHdv2aYSUEtz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4KoEjHdv2aYSUEtz .icon-shape .label rect,#mermaid-svg-4KoEjHdv2aYSUEtz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4KoEjHdv2aYSUEtz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4KoEjHdv2aYSUEtz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4KoEjHdv2aYSUEtz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-4KoEjHdv2aYSUEtz .hw>*{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .hw span{fill:#dbe7fd!important;stroke:#3a6fd8!important;color:#103a80!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .hw tspan{fill:#103a80!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .cd>*{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .cd span{fill:#d8f5e0!important;stroke:#2c9e5f!important;color:#0b3d22!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .cd tspan{fill:#0b3d22!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .ir>*{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .ir span{fill:#e9dffb!important;stroke:#7a5cc9!important;color:#2a1a5e!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .ir tspan{fill:#2a1a5e!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .mo>*{fill:#fde8d6!important;stroke:#e07e2f!important;color:#6b3209!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .mo span{fill:#fde8d6!important;stroke:#e07e2f!important;color:#6b3209!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .mo tspan{fill:#6b3209!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .ev>*{fill:#d2f4f4!important;stroke:#1c9e9e!important;color:#083d3d!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .ev span{fill:#d2f4f4!important;stroke:#1c9e9e!important;color:#083d3d!important;}#mermaid-svg-4KoEjHdv2aYSUEtz .ev tspan{fill:#083d3d!important;} 是 上电路径
否 等待插拔
上电复位
自动初始化

创建 host/事件/检测线程
CD 引脚在位?
主动发 CARD_DET 事件
CD 引脚边沿 → 中断
CLIC 向量跳转 → ISR
检测线程唤醒

向识别线程发邮箱
识别 CMD0/ACMD41

建档 sd0
dfs_mount 挂载 /sd/

总结

从上电到 SD 卡可读写,驱动实际经历了四层递进:

  1. 寄存器访问地基------MMIO 与 D 触发器,是一切外设操作的前提;
  2. 资源搭建------host、事件对象、检测线程的建立,只登记能力不动卡片;
  3. 插入检测------CD 引脚电平经去抖后形成 CDETECT 位,是判定卡在位的依据;
  4. 中断与加载------上电走主动查询,热插拔走 CLIC 向量中断,最终由检测线程与识别线程逐条发命令、建档、挂载文件系统。

理解这条链路的关键,在于分清**"电平状态"与"边沿事件" "中断只发信号"与"线程做工作"**这两组对照。搞清它们,再回头阅读 drv_sdio 驱动代码时,就只剩直接对应了。


本文依据实际嵌入式工程中的 SD 卡驱动源码整理,去除了项目代号与平台实现细节,保留通用原理。

相关推荐
2CM_Embed10 小时前
Altium Designer 原理图与 PCB 快速相互定位:Cross Probe 交叉探针
嵌入式硬件·pcb·altium designer·c2000
恒锐丰-罗生11 小时前
宽压大电流同步降压优选|CN3906 DC-DC 电源芯片,车载 / 工控 / IoT 全能供电方案
嵌入式硬件·硬件工程
虎王物联13 小时前
物联网协议网关设计:多协议融合与数据转换的工程实战
物联网·嵌入式·esp32
happygrilclh14 小时前
STM32,串口单字节中断接受,每次上电后,会收到一个0x00的解决办法。
单片机·嵌入式硬件
智闲电子设计15 小时前
STM32 独立看门狗 IWDG 实战:系统跑飞,它来兜底复位
stm32·单片机·嵌入式硬件
zcmodeltech17 小时前
智能制造教学实训沙盘模型多系统协同控制系统设计:基于STM32与Modbus RTU的工业机器人-智慧工厂-数字孪生全场景联动方案
数据库·stm32·嵌入式硬件·机器人·制造·多分类
程序员与背包客_CoderZ17 小时前
Linux D-Bus通信协议详解:从入门到C/C++编码实战
linux·服务器·c语言·c++·嵌入式硬件·嵌入式软件·dbus
嵌入式阿蔡18 小时前
HAL库 vs LL库 vs 寄存器:到底用哪个?
c语言·stm32·单片机·嵌入式硬件·嵌入式实时数据库
天空'之城19 小时前
单片机基础核心知识点汇总(三)
单片机·嵌入式硬件