CM4_SDMMC使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0 Initial release 05/21/2024

1. 概述

1.1 SD

SD卡(Secure Digital Card)使用的是sd卡协议,该协议规定了sd卡的物理接口、通信协议和命令结构,以实现数据的读取和写入。cm4 sd支持sd2.0通信协议.

1.2 SDIO

SDIO(Secure Digital Input/Output)通信协议是在sd卡协议基础上扩展而来的,它允许SD卡与外部设备进行输入和输出的通信。sdio通信协议定义了sdio卡的物理接口和通信协议,使得sd卡能够扩展为具备I/O功能的设备。cm4 sdio支持sdio2.0通信协议。


2. 关键字

sysdesc:

rtos用于描述外设硬件属性的文件,外设节点中包含的属性值可用于外设的配置,类似Linux的设备树文件

padmux:

引脚复用,用于将模块功能引脚连接到具体的外部引脚,打通信号连接。

sdmmc ip:

连接sd/sdio卡的host engine,cm4设计使用sdio ip。ip可以驱动不同pad mode,根据实际需要使用。


3. 功能描述

封装 SD卡 总线带宽 时钟范围 SD3.0支持的速率模式 SD2.0支持的速率模式 IP bank 产品
BGA/QFN SD0 Card 1,4 300k-48M - Default Speed,High Speed 0x1413(non-pm) iford
BGA/QFN WIFI Card 1,4 300k,12M,32M,48M - Default Speed,High Speed 0x42(pm) iford

注:pm sdio/sd clk仅有4个档位,需要准确设置。

总线带宽设置:

sd/sdio支持配置1 -1bit mode/4 – 4bit mode 两种总线带宽,默认使用4bit mode。sdio卡需要设置带宽为1 bit mode可以设置sysdesc外设硬件属性文件的slot_Sdio_Use_1bit_u8参数。

时钟设置:

iford cm4: sd/sdio card支持配置(300KHz,12MHz,32MHz,48MHz)范围内的时钟频率,可以通过修改外设硬件属性文件中的slot_max_clks_u32参数来设置最大时钟大小,最终设置的clock频率是当前bus speed支持的最大频率。

图3-1 SDMMC 总线速率模式


4. RTOS用法介绍

sdio通讯的时候,需要保证基本的如下步骤:

  • 硬件设备连接正常,供电稳定;
  • CONFIG支持sdio驱动;
  • SYSDESC文件配置需求属性;
  • PADMUX设定;
  • API调用,执行通讯。

4.1 DRIVER PATH

sc/driver/sysdriver/sdmmc/drv/inc
sc/driver/sysdriver/sdmmc/drv/pub
sc/driver/sysdriver/sdmmc/drv/src
sc/driver/sysdriver/sdmmc/drv/sdmmc_irq.h
sc/driver/sysdriver/sdmmc/drv/sdmmc_irq.c
sc/driver/sysdriver/sdmmc/drv/rtk_cfg.h
sc/driver/sysdriver/sdmmc/drv/sd.c
sc/driver/sysdriver/sdmmc/drv/sdio_cfg.h
sc/driver/sysdriver/sdmmc/drv/sdio.c
sc/driver/sysdriver/sdmmc/drv/sdio_test.c
sc/driver/sysdriver/sdmmc/drv/ms_sdmmc_lnx.c

4.2 CONFIG配置

#make menuconfig

BSP Driver Options --->
    <*> Support SDIO Card

4.3 SYSDESC配置

chipname.sys文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub

  <sdmmc>
      [camclk_u8] CAMCLK_sd, CAMCLK_VOID;
      [slotnum_u8] 1;
      [revcdz_u8] 0;
      [slot_ip_orders_u8] 0;
      [slot_max_clks_u32] 48000000;
      [slot_intcdzs_u8] 0;
      [slot_pwr_gpios_u16] PAD_PM_GPIO12;
      [slot_pwr_off_delay_u8] 130;
      [slot_pwr_on_delay_u8] 130;
      [slot_fakecdzs_u8] 1;
      [slot_pad_orders_u8] 0;
      [slot_cdzs_gpios_u16] PAD_PM_SDIO_INT;
      [slot_sdio_use_u8] 1;
      [slot_removable_u8] 0;
      [slot_mie_intr_u16] INT_IRQ_SDIO;
      [slot_cdz_intr_u16] INT_FIQ_PAD2SDIO_SD_CDZ;
      [slot_Sdio_Use_1bit_u8] 0;

sdio驱动中支持配置的属性如下表:

属性 描述 设定值 备注
camclk_u8 sdio使用的clock源 不需要修改
slotnum_u8 sdio槽位数 不需要更改,当前cm4 sdio只有一个槽位
revcdz_u8 配置是否颠倒Card detect电平条件 1-开启;0-关闭 可根据需要修改
slot_ip_orders_u8 配置对应卡槽的sdio engine编号 不需要修改,当前cm4 sdio只有一个槽位
slot_max_clks_u32 配置对应卡槽支持的最大时钟频率 支持300KHz, 12MHz, 32MHz, 48MHz 可根据需要修改
slot_intcdzs_u8 配置是否使用cdz中断 1-开启;0-关闭 可根据需要修改
slot_pwr_gpios_u16 配置sdio电源pad 不需要修改
slot_pwr_off_delay_u8 配置sdio的下电延迟时间 初始化过程下电动作保持时间
slot_pwr_on_delay_u8 配置sdio的上电延迟时间 初始化过程上电动作保持时间 SDIO设备一般需要配置delay时间以便SDIO设备加载固件及状态ready,具体时间以SDIO设备厂家的建议为准
slot_fakecdzs_u8 配置是否忽视Card Detect 开启后默认non-removable 可根据需要修改,与slot_removable搭配使用
slot_pad_orders_u8 配置对应卡槽的padmux mode 不需要更改,当前cm4 sdio只有一个槽位
slot_cdzs_gpios_u16 配置对应卡槽的CDZ pin脚 不需要修改
slot_sdio_use_u8 配置是否使用sdio device 1-enable;0-disable 可根据需要修改
slot_removable_u8 配置是否removable设备 1-enable;0-disable 可根据需要修改,与slot_fakecdz搭配使用
slot_mie_intr_u16 配置sdio中断信息 不需要修改,此为sdio传输过程使用中断来获取事件
slot_cdz_intr_u16 配置sdio热插拔中断信息 不需要修改
slot_Sdio_Use_1bit_u8 配置sdio总线位宽 1-1bit 0-4bit, default 4bit 可根据需要修改

详细说明:

根据cm4时钟源定制,当前sdio可选时钟范围:

slot_max_clks_u32 == 48000000, 时钟源为50MHz;

slot_max_clks_u32 == 32000000, 时钟源为33.3MHz;

slot_max_clks_u32 == 12000000, 时钟源为12MHz;

slot_max_clks_u32 == 300000, 时钟源为300KHz;

4.4 SD card API说明

头文件位于sc/driver/sysdriver/sdmmc/drv/pub/sd.h

4.4.0 sd 结构体定义

struct msSt_SDIO_Data
{
    U16_T  u16BlkCnt;
    U16_T  u16BlkSize;
    volatile char   *pu8Buf;
    U16_T  u16StorType;
};

typedef enum
{
    EV_IMI     = 0x0,
    EV_PSRAM   = 0x1,
    EV_MIU     = 0x2,
} IPStorageEmType;

4.4.1. sd_card_init

  • 描述

    sd host识别device初始化

  • 语法

    int sd_card_init(u8 u8_slot);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.4.2. sd_card_reset

  • 描述

    sd host重新初始化识别device

  • 语法

    int sd_card_reset(u8 u8_slot);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.4.3. sd_card_remove

  • 描述

    sd host移除device

  • 语法

    void sd_card_remove(u8 u8_slot);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.4.4. sd_read_block

  • 描述

    sd host读取单块block

  • 语法

    int sd_read_block(u8 slot, u32 addr, volatile u8* r_buf, u16 u16StorType);

  • 参数

    参数名称 描述
    slot sdio使用槽位,默认为0
    addr 读取device的blk地址
    r_buf 存放data的buf
    u16StorType 指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.4.5. sd_write_block

  • 描述

    sd host写入单块block

  • 语法

    int sd_write_block(u8 slot, u32 addr, volatile u8* r_buf, u16 u16StorType);

  • 参数

    参数名称 描述
    slot sdio使用槽位,默认为0
    addr 写入device的blk地址
    r_buf 存放data的buf
    u16StorType 指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.4.6. sd_read_block_multi

  • 描述

    sd host读取多块block

  • 语法

    int sd_read_block_multi(u8 slot, u32 addr, u16 u61BlkCnt, volatile u8* r_buf, u16 u16StorType);

  • 参数

    参数名称 描述
    slot sdio使用槽位,默认为0
    addr 读取device的blk地址
    u61BlkCnt 读取device的blk数
    r_buf 存放data的buf
    u16StorType 指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.4.7. sd_write_block_multi

  • 描述

    sd host写入多块block

  • 语法

    int sd_write_block_multi(u8 slot, u32 addr, u16 u16BlkCnt, volatile u8* r_buf, u16 u16StorType);

  • 参数

    参数名称 描述
    slot sdio使用槽位,默认为0
    addr 写入device的blk地址
    u61BlkCnt 写入device的blk数
    r_buf 存放data的buf
    u16StorType 指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.4.8. sd_get_capacity

  • 描述

    sd host获取device容量

  • 语法

    u64 sd_get_capacity(u8 slot);

  • 参数

    参数名称 描述
    slot sdio使用槽位,默认为0
  • 返回值

    结果 描述
    成功 返回容量值
    失败 返回-1

4.5 SDIO card API说明

头文件位于sc/driver/sysdriver/sdmmc/drv/pub/sdio.h

4.5.0 sdio 数据结构

struct msSt_SDIO_Data
{
    U16_T  u16BlkCnt;
    U16_T  u16BlkSize;
    volatile char   *pu8Buf;
    U16_T  u16StorType;
};

typedef int sdio_irq_callback(u8 u8_slot);

4.5.1. sdio_card_init

  • 描述

    sdio host识别device初始化

  • 语法

    int sdio_card_init(u8 u8_slot);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.2. sdio_card_reset

  • 描述

    sdio host重新初始化识别device

  • 语法

    int sdio_card_reset(u8 u8_slot);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.3. sdio_read_byte

  • 描述

    sdio host读取寄存器信息,非data传输

  • 语法

    int sdio_read_byte(u8 u8_slot, u8 func, u32 addr, u8* r_buf);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
    func 选择sdio func
    addr 读取device的寄存器地址
    r_buf 存放结果的buf
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.4. sdio_read_byte

  • 描述

    sdio host设置寄存器信息,非data传输

  • 语法

    int sdio_write_byte(u8 u8_slot, u8 func, u32 addr, u8 w_value);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
    func 选择sdio func
    addr 写入device的寄存器地址
    w_value 待设值
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.5. sdio_read_byte_multi

  • 描述

    sdio host按字节读取数据

  • 语法

    int sdio_read_byte_multi(u8 u8_slot, u8 func, u8 op_code, u32 addr, struct msSt_SDIO_Data *pstData);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
    func 选择sdio func
    op_code 选择地址模式:0-固定地址 1-递增地址
    addr 读取device i/o的起始地址
    pstData 传输参数: u16BlkCnt:[0,0x1ff]; u16BlkSize:512; pu8Buf:存放结果的buf; u16StorType:指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.6. sdio_write_byte_multi

  • 描述

    sdio host按字节写入数据

  • 语法

    int sdio_write_byte_multi(u8 u8_slot, u8 func, u8 op_code, u32 addr, struct msSt_SDIO_Data *pstData);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
    func 选择sdio func
    op_code 选择地址模式:0-固定地址 1-递增地址
    addr 写入device i/o的起始地址
    pstData 传输参数: u16BlkCnt:[0,0x1ff]; u16BlkSize:512; pu8Buf:存放结果的buf; u16StorType:指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.7. sdio_read_block_multi

  • 描述

    sdio host按block读取数据

  • 语法

    int sdio_read_block_multi(u8 u8_slot, u8 func, u8 op_code, u32 addr, struct msSt_SDIO_Data *pstData);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
    func 选择sdio func
    op_code 选择地址模式:0-固定地址 1-递增地址
    addr 读取device i/o的起始地址
    pstData 传输参数: u16BlkCnt:[0,0x1ff]; u16BlkSize:512; pu8Buf:存放结果的buf; u16StorType:指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.8. sdio_write_block_multi

  • 描述

    sdio host按block写入数据

  • 语法

    int sdio_write_block_multi(u8 u8_slot, u8 func, u8 op_code, u32 addr, struct msSt_SDIO_Data *pstData);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
    func 选择sdio func
    op_code 选择地址模式:0-固定地址 1-递增地址
    addr 读取device i/o的起始地址
    pstData 传输参数: u16BlkCnt:[0,0x1ff]; u16BlkSize:512; pu8Buf:存放结果的buf; u16StorType:指定当前cm4使用的storage类型,参见IPStorageEmType
  • 返回值

    结果 描述
    成功 返回容量值
    失败 返回-1

4.5.9. sdio_irq_enable

  • 描述

    sdio host使能sdio中断

  • 语法

    int sdio_irq_enable(u8 u8_slot, int enable);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.5.10. sdio_set_irq_callback

  • 描述

    sdio host注册sdio中断回调

  • 语法

    int sdio_set_irq_callback(u8 u8_slot, sdio_irq_callback* irq_cb);

  • 参数

    参数名称 描述
    u8_slot sdio使用槽位,默认为0
    irq_cb sdio中断回调函数
  • 返回值

    结果 描述
    成功 返回0
    失败 返回非0

4.6 DEMO

demo源码位于sc/driver/sysdriver/sdmmc/drv/sdio_test.c

void sdtest(U8_T u8_slot)
{
    U64_T sd_capt;
    U32_T sd_addr, err, blkcnt, loop = 0, i;
    U8_T *r_buf, *w_buf, flag, compare = 0;
    IPStorageEmType eStor[] = { EV_IMI, EV_IMI, EV_PSRAM };

    // card detect
    if(MS_SD_Card_Detect(u8_slot))
    {
        CamOsPrintf("card alive !\n");
        sd_capt = sd_get_capacity(u8_slot);
        CamOsPrintf(" !\n");
        if (sd_capt == -1) {
            CamOsPrintf("card init fail !\n");
            return;
        }
        CamOsPrintf("SD card capacity: %lld %s -- %d %s \n", (unsigned long long)sd_capt, "B",  (int)(sd_capt/1024/1024/1024) , "GB");
    }

    // alloc buf
    sd_addr = 0x233;
    r_buf = CamOsMemAlloc(512);
    w_buf = CamOsMemAlloc(512);
    // set buf pattern
    for(i = 0; i < 512; i++)
    {
        r_buf[i] = i;
        w_buf[i] = ~i;
    }

    // write single block
    err = sd_write_block(u8_slot, sd_addr, w_buf, eStor[loop]);
    if(err)
        CamOsPrintf("write block error\n");
    // read single block
    err = sd_read_block(u8_slot, sd_addr, r_buf, eStor[loop]);
    if(err)
        CamOsPrintf("read block error\n");
    // check result
    for(i = 0; i < 512; i++)
    {
        if(r_buf[i] != w_buf[i])
        {
            compare = 1;
            break;
        }
    }

    if(compare)
    {
        compare = 0;
        _DUMP_MEM_ARR(w_buf, 512);
        _DUMP_MEM_ARR(r_buf, 512);
        CamOsPrintf("signal block r/w test fail \n");
    }
    else
        CamOsPrintf("signal block r/w test pass \n");
    // release buf
    CamOsMemRelease(r_buf);
    CamOsMemRelease(w_buf);

    // test multi block
    sd_addr = 0x122;
    blkcnt = 2;
    r_buf = CamOsMemAlloc(512*blkcnt);
    w_buf = CamOsMemAlloc(512*blkcnt);

    for(i = 0; i < 512*blkcnt; i++)
    {
        r_buf[i] = i;
        w_buf[i] = ~i;
    }

    err = sd_write_block_multi(u8_slot, sd_addr, blkcnt, w_buf, eStor[loop]);
    if(err)
        CamOsPrintf("write block error\n");

    err = sd_read_block_multi(u8_slot, sd_addr, blkcnt, r_buf, eStor[loop]);
    if(err)
        CamOsPrintf("read block error\n");

    for(i = 0; i < 512*blkcnt; i++)
    {
        if(r_buf[i] != w_buf[i])
        {
            compare = 1;
            break;
        }
    }
    if(compare)
    {
        compare = 0;
        CamOsPrintf("multi block r/w test fail \n");
    }
    else
        CamOsPrintf("multi block r/w test pass \n");

    CamOsMemRelease(r_buf);
    CamOsMemRelease(w_buf);
}

void SdioTest(U8_T u8_slot)
{
    unsigned int test_loop = 0, test_temp = 0, i = 0;
    unsigned int test_loop = 0;

    unsigned short ret;
    u8 *r_buf, *w_buf;
    u8 func;
    u32 addr;

    struct msSt_SDIO_Data stData;
    u32 count;
    u32 i;
    u8 op_code;
    u32 blk_size;

    CamOsPrintf("****** SDIO Test ******\r\n");

    // register sdio intr callback and enable
    sdio_set_irq_callback(u8_slot, Sdio_test_irq_callback);
    sdio_irq_enable(u8_slot, TRUE);

    // sdio init
    if (sdio_card_init(u8_slot) != 0) {
        CamOsPrintf("--- SDIO Init fail ---\r\n");
        return;
    }

    CamOsPrintf("--- SDIO Init OK ---\r\n");

    // alloc buf
    r_buf = CamOsMemAlloc(512);
    w_buf = CamOsMemAlloc(512);

    // read register info
    func = 0;
    addr = 0x13;
    ret = sdio_read_byte(u8_slot, func, addr, r_buf);
    if (ret)
    {
        CamOsPrintf("- sdio_read_byte ERROR\r\n");
    }

    // write register info
    w_buf[0] = r_buf[0];
    func = 0;
    addr = 0x13;
    ret = sdio_write_byte(u8_slot, func, addr, w_buf[0]);
    if (ret)
    {
        CamOsPrintf("- sdio_write_byte ERROR\r\n");
    }

    // write by byte and addr fixed
    memset(w_buf, 0, 512);
    memset(&stData, 0, sizeof(struct msSt_SDIO_Data));
    func = 0;
    addr = 0xf0; //16
    op_code = 0;
    count = 16;

    stData.u16BlkCnt = count;
    stData.pu8Buf = (volatile char *)w_buf;
    stData.u16StorType = EV_IMI; // storage type
    for (i = 0; i < count; i++)
    {
        w_buf[i] = test_loop + i;
    }

    ret = sdio_write_byte_multi(u8_slot, func, op_code, addr, &stData);
    if (ret)
    {
        CamOsPrintf("- sdio_write_byte_multi ERROR\r\n");
    }

    // read by byte and addr increment
    memset(r_buf, 0, 512);

    memset(&stData, 0, sizeof(struct msSt_SDIO_Data));
    func = 1;
    addr = (0x09);
    op_code = 1;
    count = 3;

    stData.u16BlkCnt = count;
    stData.pu8Buf = (volatile char *)r_buf;
    stData.u16StorType = EV_IMI;

    ret = sdio_read_byte_multi(u8_slot, func, op_code, addr, &stData);
    if (ret)
    {
        CamOsPrintf("- sdio_read_byte_multi ERROR\r\n");
    }

    // write by block
    memset(w_buf, 0, 512);
    memset(&stData, 0, sizeof(struct msSt_SDIO_Data));

    func = 1;
    addr = (0x1c); //1;
    op_code = 1;
    count = 1;//12; // 12 2;
    blk_size = 512;

    stData.u16BlkCnt = count;
    stData.u16BlkSize = blk_size;
    stData.pu8Buf = (volatile char *)w_buf;
    stData.u16StorType = EV_IMI;

    for (i = 0; i < (count * blk_size); i++)
    {
        w_buf[i] = test_loop + i;
    }

    ret = sdio_write_block_multi(u8_slot, func, op_code, addr, &stData);
    if (ret)
    {
        CamOsPrintf("- sdio_write_block_multi ERROR\r\n");
    }

    // read by block
    memset(w_buf, 0, 512);
    memset(&stData, 0, sizeof(struct msSt_SDIO_Data));

    func = 1;
    addr = 0x1c; //1;
    op_code = 1;
    count = 1;
    blk_size = 512;

    stData.u16BlkCnt = count;
    stData.u16BlkSize = blk_size;
    stData.pu8Buf = (volatile char *)r_buf;
    stData.u16StorType = EV_IMI;

    ret = sdio_read_block_multi(u8_slot, func, op_code, addr, &stData);
    if (ret)
    {
        CamOsPrintf("- sdio_read_block_multi ERROR\r\n");
    }

    release buf
    CamOsMemRelease(r_buf);
    CamOsMemRelease(w_buf);

    return;
}

5. DEBUG方法

可以根据驱动报错log中的(E: 0xXXXX)信息查找对应的SD/SDIO卡状态来判断目前卡出现了什么问题。

根据SD/SDIO卡实际可能遇到的问题,分为以下几个类型:

识别卡失败

若卡识别失败,需要确定是响应获取失败还是传输信号不好有CRC问题,区分问题可以通过驱动打印的日志确定,具体区别以及debug方法如下:

  • SD/SDIO卡不回复命令响应

    现象:驱动log报(E: 0x0008)信息

    debug方法:首先确定电压和clock是否正常,其次可以抓波形看host是否有command发出去,若前面两个都没有问题,再确定卡是否有回复响应,若没有响应检查device状态。

    相关日志:

  • 命令响应存在CRC问题

    现象:驱动log报(E: 0x0010)信息

    debug方法:若有CRC问题,先需要排除硬件问题,比如:device接触是否良好,是否有外接干扰等。然后再尝试更改pad driving,若还有问题则考虑调整clock phase等。

    相关日志:

读写失败

若是在正常读写过程中遇到问题,需要确定是读写超时问题,还是信号不好有CRC问题,区分问题可以通过log确定,超时问题有timeout的字眼,debug方法如下:

  • 读写超时

    现象:驱动报错log里有timeout的字眼

    debug方法:首先需要确定当前clock频率以及bus width是否是期望配置的值,其次可以将驱动里的超时时间再加大试试,若还有超时问题,则需要抓波形详细分析。

    读写超时时间在驱动对应芯片目录的hal_card_platform_config.h文件中设定:

  • 读写CRC问题

    现象:驱动log报(E: 0x0001)或(E: 0x0002)信息

    debug方法:debug方法见前文,另外读写过程中的CRC问题,若对速率要求不是很高,可以考虑降频或者降bus width。

    相关日志:

卡读写速度慢

首先检查卡时钟频率和bus width是否正常,另外可以测试对比下不同型号卡的读写速度。

注:SD卡读写受命令及其响应的传输时间,卡busy时间,停止命令时间等多重因素影响,实际读写速率是低于其理论速率的; 读写速度测试应该测试应用层的读写带宽,即传输一个大文件进行测试。