跳转至

SDIO

SDIO Slave 驱动 | 无 sample

学习目标

  • 理解 SDIO 从机模式——WS63 作为从设备通过 SDIO 总线与主机通信
  • 掌握回调驱动架构——7 个回调覆盖读/写/消息/复位全流程
  • 掌握消息传递机制——sched_msg 准备消息、sync_msg 同步发送、process_msg 原子替换
  • 能够在 WS63 端通过 SDIO 从机向 Linux/Android 主机上报数据

基本概念

SDIO 从机做什么

SDIO(Secure Digital Input/Output)是标准化的芯片间高速通信接口,物理层包含 CLK、CMD、DATA0~DATA3 共 6 条线。WS63 在 SDIO 总线上充当从设备——所有传输由主机发起,从机响应。典型场景:WiFi/BT 模组通过 SDIO 向应用处理器上报数据,速率可达数十 MB/s。

flowchart LR
    H[主机 AP] -->|CLK + CMD| S[SDIO 控制器]
    H -->|DATA0~3 读写| S
    S -->|回调通知| A[WS63 应用层]
    A -->|sched_msg| S
    S -->|消息就绪| H

通道状态机

SDIO 从机通道有明确的状态迁移:复位 → 初始化 → 睡眠 ↔ 唤醒 ↔ 工作。只有处于 SDIO_CHANNEL_WORK 状态时才能收发消息。

stateDiagram-v2
    [*] --> RESET: 上电
    RESET --> INIT: uapi_sdio_slave_init
    INIT --> WORK: 主机 CLK 就绪
    WORK --> SLEEP: 主机允许休眠
    SLEEP --> WAKE: 主机唤醒
    WAKE --> WORK: 通道恢复
    WORK --> RESET: 软复位

回调驱动架构

从机端不主动发起传输——所有传输由主机触发,从机通过回调感知事件。sdio_callback_func_t 包含 7 个回调:

回调 触发时机 返回值
read_start_callback 主机发起读操作 uint32_t:待发送数据长度
read_over_callback 主机读操作完成 uint32_t
read_err_callback 主机读操作出错 void
write_start_callback 主机发起写操作 uint32_t:可接收数据长度
write_over_callback 主机写操作完成 uint32_t
process_msg_callback 主机发来消息 void
soft_rst_callback 主机发起软复位 void

消息传递机制

WS63 SDIO 从机使用 32 位 bitmask 传递消息——bit0~bit31 对应消息 0~31。每条消息可处于三种状态:挂起(pending)、发送中(sending)、就绪(ready)。

接口 行为 阻塞
sched_msg 扫描 pending 队列,调度一条消息发送
sync_msg 将消息加入队列并同步发送
send_msg_ack 覆盖当前发送中的消息
process_msg 清除一条消息,发送另一条(原子操作)

涉及 API

API 用途 头文件
uapi_sdio_slave_init(sdio_bus_t bus) 初始化 SDIO 从机通道 sdio_slave.h
uapi_sdio_slave_deinit(sdio_bus_t bus) 去初始化 sdio_slave.h
uapi_sdio_slave_register_callback(sdio_bus_t bus, const sdio_callback_func_t *fun) 注册回调集 sdio_slave.h
uapi_sdio_slave_sched_msg(sdio_bus_t bus) 调度 pending 消息发送 sdio_slave.h
uapi_sdio_slave_sync_msg(sdio_bus_t bus, uint32_t msg) 将消息加入队列并发送 sdio_slave.h
uapi_sdio_slave_send_msg_ack(sdio_bus_t bus, uint32_t msg) 覆盖当前发送中消息 sdio_slave.h
uapi_sdio_slave_process_msg(sdio_bus_t bus, uint32_t send_msg, uint32_t clear_msg) 原子替换消息 sdio_slave.h
uapi_sdio_slave_is_pending_msg(sdio_bus_t bus, uint32_t msg) 查询消息是否 pending sdio_slave.h
uapi_sdio_slave_is_sending_msg(sdio_bus_t bus, uint32_t msg) 查询消息是否正在发送 sdio_slave.h
uapi_sdio_slave_register_notify_message_callback(notify_host_event_t event_callback) 注册通知 Host 事件回调 sdio_slave.h
uapi_sdio_slave_get_status(sdio_bus_t bus, sdio_status_info_t *status_info) 获取通道状态 sdio_slave.h
uapi_sdio_slave_prepare_send_data(sdio_bus_t bus, uint32_t data_len) 启动 DMA 数据发送 sdio_slave.h

回调中不可阻塞、不可调用耗时操作——应通过消息队列或信号量将事件传递给任务处理。

案例说明

做什么

初始化 SDIO 从机通道,注册全套回调(读/写/消息/复位),准备消息 1 供主机读取,通过串口打印回调触发情况。

规格与功能

规格项 说明
总线 SDIO_BUS0
回调集 7 个回调全部注册
消息 消息 1(bit1)
模式 从机模式,消息被动上报
状态查询 定时打印通道状态

程序运行流程:init → register_callback → loop { sched_msg(1), get_status, delay } → 主机读取触发回调 → 回调打印事件。

案例流程

sequenceDiagram
    participant A as WS63应用
    participant S as SDIO从机
    participant H as 主机AP

    A->>S: uapi_sdio_slave_init
    A->>S: register_callback 7个回调
    A->>S: sync_msg msg=1
    Note over S: 消息1置为pending
    S->>S: sched_msg 调度发送
    H->>S: 主机发起读操作
    S->>A: read_start_cb 触发
    S->>H: 返回消息1数据
    S->>A: read_over_cb 触发
    H->>S: 主机写入消息
    S->>A: process_msg_cb 触发

案例操作指导

第一步:编译

fbb build ws63-liteos-app

第二步:烧录和验证

用 SDIO 排线连接 WS63 与主机开发板。启动主机端 SDIO 驱动后,WS63 串口输出各回调触发日志。若主机未就绪,可通过 uapi_sdio_slave_host_clk_ready() 轮询等待。

关键配置

参数 说明
消息编号范围 0~31 bitmask 限制
pending 消息调度 非阻塞 sched_msg 不在 WORK 状态直接返回错
回调耗时 < 50μs 回调在中断上下文执行
最大传输速率 数十 MB/s 受主机端驱动和 DMA 配置影响
初始化模式 阻塞 / 非阻塞 init 等待主机 CLK;init_no_wait 立即返回

L 位锁定

soft_rst_callback 触发后进入复位状态——需重新初始化才能恢复通信。状态机不处于 WORK 时 sched_msg/sync_msg 均返回失败。

代码详解

概念性代码,基于 SDK 头文件 sdio_slave.h 中的 API 签名。

#include "sdio_slave.h"
#include "osal_printk.h"
#include "app_init.h"

/* ---- 回调实现 ---- */
static uint32_t sdio_read_start_cb(uint32_t len, uint8_t *dma_tbl)
{
    osal_printk("SDIO: HOST start read, len=%u\n", len);
    return len;  /* 返回可发送的数据长度 */
}

static uint32_t sdio_read_over_cb(void)
{
    osal_printk("SDIO: HOST read over\n");
    return 0;
}

static void sdio_read_err_cb(void)
{
    osal_printk("SDIO: HOST read error\n");
}

static uint32_t sdio_write_start_cb(uint32_t len, uint8_t *dma_tbl)
{
    osal_printk("SDIO: HOST start write, len=%u\n", len);
    return len;
}

static uint32_t sdio_write_over_cb(void)
{
    osal_printk("SDIO: HOST write over\n");
    return 0;
}

static void sdio_process_msg_cb(uint32_t msg)
{
    osal_printk("SDIO: HOST sent msg=0x%08X\n", msg);
}

static void sdio_soft_rst_cb(void)
{
    osal_printk("SDIO: soft reset from HOST\n");
}

/* ---- 回调集 ---- */
static sdio_callback_func_t g_sdio_cb = {
    .read_start_callback    = sdio_read_start_cb,
    .read_over_callback     = sdio_read_over_cb,
    .read_err_callback      = sdio_read_err_cb,
    .write_start_callback   = sdio_write_start_cb,
    .write_over_callback    = sdio_write_over_cb,
    .process_msg_callback   = sdio_process_msg_cb,
    .soft_rst_callback      = sdio_soft_rst_cb,
};

/* ---- 主入口 ---- */
static void sdio_slave_entry(void)
{
    errcode_t ret;
    sdio_status_info_t status;

    /* 初始化 SDIO 从机通道 */
    ret = uapi_sdio_slave_init(SDIO_BUS0);
    if (ret != ERRCODE_SUCC) {
        osal_printk("SDIO init failed: %d\n", ret);
        return;
    }

    /* 注册 7 个回调 */
    ret = uapi_sdio_slave_register_callback(SDIO_BUS0, &g_sdio_cb);
    if (ret != ERRCODE_SUCC) {
        osal_printk("SDIO register cb failed: %d\n", ret);
        return;
    }

    /* 循环: 准备消息 + 查询状态 */
    while (1) {
        /* 调度 pending 消息发送 */
        uapi_sdio_slave_sched_msg(SDIO_BUS0);

        /* 获取通道状态 */
        if (uapi_sdio_slave_get_status(SDIO_BUS0, &status) == ERRCODE_SUCC) {
            osal_printk("SDIO status: work=%d tx=%d sleep=%d\n",
                        status.work_status, status.tx_status, status.sleep_status);
        }

        osal_msleep(1000);
    }
}

app_run(sdio_slave_entry);

sched_msg 仅在通道处于 WORK 状态时有效。若主机 CLK 未就绪,可通过 uapi_sdio_slave_host_clk_ready() 轮询等待。