W801的SDIO驱动与FATFS优化

发布于 2022-04-22 18:09:10

一.项目概述

在使用默认的SDK的SDIO与FATFS过程中,发现原SDK存在如下几个问题:
【1】无法识别大小为2G以下的卡
【2】fatfs无法正常挂载2G以下的卡
【3】将主频修改至240M后,卡片读写会出现问题
因此我对原来SDK的SDIO驱动和Fatfs的移植部分进行了修改,并解决了这几个问题。

二.主频问题

【1】问题描述,在SDK的wm_main.c中存在如下的函数,是用来设置CPU的总线频率,最高为240M。为了更好的释放性能,我将其调整至了240M后出现了SD卡的读写不正常的问题。

tls_sys_clk_set(CPU_CLK_240M);

【2】问题的原因:SDIO在mmc模式下,且使用的SD卡片为默认速度配置的话,最高的输出频率只能为20M。W801的SDIO挂载在CPU的时钟总线上,通过分频得到,原来SDK中的默认分频数为1/6,当修改主频后,SDIO的总线频率也就变成了40M,超过上限无法正常读写。
【3】解决的办法:解决办法有两种,一个是修改分频寄存器的值,一个是修改SD卡的速度配置,我这里选择的是前者。SDIO分频的值对应表如下图所示;
9.png
因此当cpu主频设置为240M时,需要讲分频改为1/12才能不超过20M的上限,修改的位置在下面这个初始化配置函数中进行修改:

int wm_sdh_config(void)
{
    tls_sys_clk sysclk;    

    tls_sys_clk_get(&sysclk);
    SDIO_HOST->MMC_CARDSEL = 0xC0 | (sysclk.cpuclk / 2 - 1);//0xd3; //enable module, enable mmcclk
    SDIO_HOST->MMC_CTL = 0xEB;  //原来SDK中为D3,这里要实现分频为1/12,需要修改为EB,当主频变化时,这里也需要对应改变保证不超过20M
    SDIO_HOST->MMC_INT_MASK = 0x100; //unmask sdio data interrupt.
    SDIO_HOST->MMC_CRCCTL = 0xC0; //
    SDIO_HOST->MMC_TIMEOUTCNT = 0xff;
    return 0;
}

【修改后试验结果】
10.png

三.SDIO驱动与Fatfs修改

1.SD卡相关概念

【1】SD卡的分类:
SD卡由mmc卡发展而来,根据协议版本,容量大小可以分为若干种,因为W801最高支持sd2.0协议,因此我们面对的如下4类卡片:
1.mmc卡
2.SD卡:协议版本为SD1.0,容量大小0-2G
3.SDSC卡:协议版本为SD2.0,容量大小0-2G
4.SDHC卡:协议版本为SD2.0,容量大小2-32G
7.jpg

【2】不同SD卡的读写区别
2G以内的SD卡是字节寻址的,而2G以上的卡只能为块寻址。举个例子,当地址为0x01时,2G内的卡会判定为是从字节地址0x01开始读写,而2G以外的卡会判定成为从第一个块开始读写。

【3】SD卡的初始化与识别
根据SD2.0协议,上述四类卡片的识别过程如下图所示,我也写了一个中文版的导图附上。
5.png
6.png

2.SDK的驱动缺陷

如上面说到的初始化过程,主要通过cmd8以及acmd41返回的OCR寄存器的值来识别那四类卡片。这里我们看一下SDK的原代码可以发现,原SDK中对于CMD8命令是否响应未做区分,如果未读到response则直接判定为初始化失败。同时后续也未对OCR寄存器的CCS位做判断,导致其无法判断是为2G容量以内的SDSC卡还是2-32G的SDHC卡,将其统一识别为了SDHC卡,如上面的不同SD卡读写区别中可以看到2G以内的卡,是按字节寻址而2G以外的卡,为块寻址,因为原SDK不做区分导致2G以内的卡能成功初始化,但是会出现读写失败的问题。驱动有问题也会导致fatfs出现问题。

int wm_sd_card_initialize(uint32_t *rca)
{
    int ret = -1;
    uint32_t respCmd[4];
    int recnt = 5;
    
    wm_sdh_config();
    //======================================================
    // set up
    // Test:  Init sequence, With response check
    // CMD 0  Reset Card
    // CMD 8  Get voltage (Only 2.0 Card response to this)
    // CMD55  Indicate Next Command are Application specific
    // ACMD41 Get Voltage windows
    // CMD 2  CID reg
    // CMD 3  Get RCA.
    //======================================================
begin:
    wm_sdh_send_cmd(0, 0, 0x04); //Send CMD0
    sm_sdh_wait_interrupt(0, -1);
    delay_cnt(1000);
    wm_sdh_send_cmd(8, 0x1AA, 0x44); //Send CMD8
    sm_sdh_wait_interrupt(0, -1);
    wm_sdh_get_response(respCmd, 2);
    sh_dumpBuffer("CMD8 respCmd", (char *)respCmd, 5);
    if(respCmd[0] != 0x1AA || (respCmd[1] & 0xFF) != 8)
    {
        TEST_DEBUG("CMD8 Error\n");
        printf("CMD8 Error\n");
        if(recnt--)
            goto begin;
        goto end;                         //这里当5次读取cmd8后的response未成功时,会直接跳转到初始化失败,而未做区分
    }
    while(1)
    {
        wm_sdh_send_cmd(55, 0, 0x44); //Send CMD55
        sm_sdh_wait_interrupt(0, -1);
        wm_sdh_get_response(respCmd, 2);
        sh_dumpBuffer("CMD55 respCmd", (char *)respCmd, 5);
        if((respCmd[1] & 0xFF) != 55)
        {
            printf("respCmd Error\n");
            goto end;
        }
            

        wm_sdh_send_cmd(41, 0xC0100000, 0x44); //Send ACMD41
        sm_sdh_wait_interrupt(0, -1);
        sm_sdh_wait_interrupt(3, 1000); //由于sd规范中,Acmd41返回的crc永远是11111,也就是应该忽略crc;这里的crc错误应该忽略。
        wm_sdh_get_response(respCmd, 2);
        sh_dumpBuffer("ACMD41 respCmd", (char *)respCmd, 5);
        if((respCmd[1] & 0xFF) != 0x3F) //sd规范定义固定为0x3F,所以导致crc错误
        {
            printf("respCmd Error - 2\n");
            goto end;
        }
        if(respCmd[0] >> 31 & 0x1)
        {
            TEST_DEBUG("card is ready\n");         //这里未对CCS位进行判断,无法区分SDSC与SDHC
            printf("card is ready\n");
            break;
        }
    }

    wm_sdh_send_cmd(2, 0, 0x54); //Send CMD2
    sm_sdh_wait_interrupt(0, -1);
    sm_sdh_wait_interrupt(3, 1000);
    wm_sdh_get_response(respCmd, 4);
    sh_dumpBuffer("CMD2 respCmd", (char *)respCmd, 16);
    if((respCmd[3] >> 24 & 0xFF) != 0x3F) //sd规范定义固定为0x3F,所以导致crc错误
    {
        printf("respCmd Error - 3\n");
        goto end;
    }
    wm_sdh_send_cmd(3, 0, 0x44); //Send CMD3
    sm_sdh_wait_interrupt(0, -1);
    wm_sdh_get_response(respCmd, 2);
    sh_dumpBuffer("CMD3 respCmd", (char *)respCmd, 5);
    if((respCmd[1] & 0xFF) != 3)
    {
        printf("respCmd Error - 4\n");
        goto end;
    }
    *rca = respCmd[0] >> 16;
    TEST_DEBUG("RCA = %x\n", *rca);

    ret = 0;
end:
    return ret;
}

3.驱动修改

【1】修改wm_sdio_host.h,在该头文件的SD_CardInfo_t结构体中实际上已经定义了一个CardType的变量用来标识卡片类型,这里我们只需要添加3个宏定义来标明卡片类型。因为mmc卡片不太常用,因此我并没有增加mmc卡片的识别。

#define CardType_SD     0x01
#define CardType_SDSC     0x02
#define CardType_SDHC     0x03
//该结构体已经在头文件中定义,可以看到初始SDK已经定义了一个CardType的变量用来标识卡片类型
typedef struct
{
  long long CardCapacity;
  u32 CardBlockSize;
  u16 RCA;
  u8 CardType;
} SD_CardInfo_t;

【2】修改wm_sdio_host.c中的wm_sd_card_initialize函数,修改如下:

int wm_sd_card_initialize(uint32_t *rca)
{
    int ret = -1;
    uint32_t respCmd[4];
    int recnt = 5;
    
    u8 temp_type = 0x00;
    
    wm_sdh_config();
    //======================================================
    // set up
    // Test:  Init sequence, With response check
    // CMD 0  Reset Card
    // CMD 8  Get voltage (Only 2.0 Card response to this)
    // CMD55  Indicate Next Command are Application specific
    // ACMD41 Get Voltage windows
    // CMD 2  CID reg
    // CMD 3  Get RCA.
    //======================================================
begin:
    wm_sdh_send_cmd(0, 0, 0x04); //Send CMD0
    sm_sdh_wait_interrupt(0, -1);
    delay_cnt(1000);
    wm_sdh_send_cmd(8, 0x1AA, 0x44); //Send CMD8
    sm_sdh_wait_interrupt(0, -1);
    wm_sdh_get_response(respCmd, 2);
    sh_dumpBuffer("CMD8 respCmd", (char *)respCmd, 5);
    if(respCmd[0] != 0x1AA || (respCmd[1] & 0xFF) != 8)
    {
        TEST_DEBUG("CMD8 Error\n");
        if(recnt--)
            goto begin;
        temp_type =0x01; // 未收到回复则说明为SD1.0卡片 
    }
    while(1)
    {
        wm_sdh_send_cmd(55, 0, 0x44); //Send CMD55
        sm_sdh_wait_interrupt(0, -1);
        wm_sdh_get_response(respCmd, 2);
        sh_dumpBuffer("CMD55 respCmd", (char *)respCmd, 5);
        if((respCmd[1] & 0xFF) != 55)
            goto end;

        wm_sdh_send_cmd(41, 0xC0100000, 0x44); //Send ACMD41
        sm_sdh_wait_interrupt(0, -1);
        sm_sdh_wait_interrupt(3, 1000); //由于sd规范中,Acmd41返回的crc永远是11111,也就是应该忽略crc;这里的crc错误应该忽略。
        wm_sdh_get_response(respCmd, 2);
        sh_dumpBuffer("ACMD41 respCmd", (char *)respCmd, 5);
        if((respCmd[1] & 0xFF) != 0x3F) //sd规范定义固定为0x3F,所以导致crc错误
            goto end;
        if(respCmd[0] >> 31 & 0x1)
        {
            TEST_DEBUG("card is ready\n");
            // 根据CCS位来判断为哪一类型的卡片
            if ((respCmd[0] >> 30 == 0x3) && (temp_type == 0x0))
            {
                SDCardInfo.CardType = CardType_SDHC;
                printf("\nCardtype[%d]: SDHC\n", SDCardInfo.CardType);
            }
            else if ((respCmd[0] >> 30 == 0x2) && (temp_type == 0x0))
            {
                SDCardInfo.CardType = CardType_SDSC;
                printf("\nCardtype[%d]: SDSC\n", SDCardInfo.CardType);
            }
            else if (temp_type == 0x1)
            {
                SDCardInfo.CardType = CardType_SD;
                printf("\nCardtype[%d]: SD\n", SDCardInfo.CardType);
            }
            break;
        }
    }

    wm_sdh_send_cmd(2, 0, 0x54); //Send CMD2
    sm_sdh_wait_interrupt(0, -1);
    sm_sdh_wait_interrupt(3, 1000);
    wm_sdh_get_response(respCmd, 4);
    sh_dumpBuffer("CMD2 respCmd", (char *)respCmd, 16);
    if((respCmd[3] >> 24 & 0xFF) != 0x3F) //sd规范定义固定为0x3F,所以导致crc错误
        goto end;
    wm_sdh_send_cmd(3, 0, 0x44); //Send CMD3
    sm_sdh_wait_interrupt(0, -1);
    wm_sdh_get_response(respCmd, 2);
    sh_dumpBuffer("CMD3 respCmd", (char *)respCmd, 5);
    if((respCmd[1] & 0xFF) != 3)
        goto end;
    *rca = respCmd[0] >> 16;
    TEST_DEBUG("RCA = %x\n", *rca);

    ret = 0;
end:
    return ret;
}

【3】对于wm_sdio_host_demo.c的修改,这个demo没有对不同卡片进行区别采用不同操作,这里我们通过卡片类型来细化,使得所有卡片都能通过块读写的方式来进行读写。这里的两个读写函数都需要修改,这里展示sdh_card_wr_sb函数的修改,另一个函数同理。

static int sdh_card_wr_sb(uint32_t rca, uint8_t bus_width, const uint32_t tsize)
{
    int ret = -1;
    int i = 0;
    char* buf = NULL;
    char* bufR = NULL;

    buf = tls_mem_alloc(512);
    if(buf == NULL)
        goto end;
    bufR = tls_mem_alloc(512);
    if(bufR == NULL)
        goto end;
    random_get_bytes(buf, 512);
    TEST_DEBUG("bus width %s\n", bus_width == 0 ? "1bit" : "4bits");
    ret = wm_sd_card_set_bus_width(rca, bus_width);
    if(ret)
        goto end;
    ret = wm_sd_card_set_blocklen(0x200); //512
    if(ret)
        goto end;

    for(i=0; i<(tsize/512); i++)
    {
        //判断是否是SDHC卡,若是则为块寻址,否则为字节寻址,需要乘以512
        if (SDCardInfo.CardType == CardType_SDHC)
        {
            ret = wm_sd_card_block_write(rca, i, buf);
        }
        else if ((SDCardInfo.CardType == CardType_SDSC) || (SDCardInfo.CardType == CardType_SD))
        {
            ret = wm_sd_card_block_write(rca, i * 512, buf);
        }
        if(ret)
            goto end;
    }
    ret = wm_sd_card_query_status(rca, NULL);
    if(ret)
        goto end;
    for(i=0; i<(tsize/512); i++)
    {
        if (SDCardInfo.CardType == CardType_SDHC)
        {
            ret = wm_sd_card_block_read(rca, i, bufR);
        }
        else if ((SDCardInfo.CardType == CardType_SDSC) || (SDCardInfo.CardType == CardType_SD))
        {
            ret = wm_sd_card_block_read(rca, i * 512, bufR);
        }
        if(ret)
            goto end;
        if(memcmp(buf, bufR, 512))
        {
            ret = -2;
            goto end;
        }
    }

    ret = 0;
end:
    if(buf)
    {
        tls_mem_free(buf);
    }
    if(bufR)
    {
        tls_mem_free(bufR);
    }
    TEST_DEBUG("ret %d\n", ret);
    return ret;
}

【4】fatfs的diskio.c的修改,原来移植的fatfs文件系统读写函数也未对块读写和字节读写做区分,会无法成功挂载2G以内的卡需要对disk_write和disk_write函数做如下修改:

static int MMC_disk_write(    BYTE *buff, LBA_t sector, UINT count)
{
    int ret, i;
    int buflen = BLOCK_SIZE*count;
    BYTE *wrbuff = buff;
    
    if (((u32)buff)&0x3)
    {
        wrbuff = tls_mem_alloc(buflen);
        if (wrbuff == NULL) /*non aligned 4*/
        {
            return -1;
        }
        memcpy(wrbuff, buff, buflen);
    }
    
    for( i = 0; i < TRY_COUNT; i++ )
    {
        if(count == 1)
        {
            //判断是否是SDHC卡,若是则为块寻址,否则为字节寻址,需要乘以512
            if (SDCardInfo.CardType == CardType_SDHC)
            {
                ret = wm_sd_card_block_write(fs_rca, sector, (char *)wrbuff);
            }
            else if ((SDCardInfo.CardType == CardType_SDSC) || (SDCardInfo.CardType == CardType_SD))
            {
                ret = wm_sd_card_block_write(fs_rca, sector * BLOCK_SIZE, (char *)wrbuff);
            }
        }
        else if(count > 1)
        {
            if (SDCardInfo.CardType == CardType_SDHC)
            {
                ret = wm_sd_card_blocks_write(fs_rca, sector, (char *)wrbuff, buflen);
            }
            else if ((SDCardInfo.CardType == CardType_SDSC) || (SDCardInfo.CardType == CardType_SD))
            {
                for (int j = 0; j < count; j++)
                {
                    ret = wm_sd_card_blocks_write(fs_rca, sector + j* BLOCK_SIZE, (char *)wrbuff, BLOCK_SIZE);
                }
            }
        }
        if( ret == 0 ) 
        {
            break;
        }
    }

    if(wrbuff != buff)
    {
        tls_mem_free(wrbuff);
    }

    return ret;
}

static int MMC_disk_read(    BYTE *buff, LBA_t sector, UINT count)
{
    int ret, i;
    int buflen = BLOCK_SIZE*count;
    BYTE *rdbuff = buff;

    if (((u32)buff)&0x3) /*non aligned 4*/
    {
        rdbuff = tls_mem_alloc(buflen);
        if (rdbuff == NULL)
        {
            return -1;
        }
    }
    
    for( i=0; i<TRY_COUNT; i++ )
    {   
        if(count == 1)
        {
            //判断是否是SDHC卡,若是则为块寻址,否则为字节寻址,需要乘以512
            if (SDCardInfo.CardType == CardType_SDHC)
            {
                ret = wm_sd_card_block_read(fs_rca, sector, (char *)rdbuff);
            }
            else if ((SDCardInfo.CardType == CardType_SDSC) || (SDCardInfo.CardType == CardType_SD))
            {
                ret = wm_sd_card_block_read(fs_rca, sector * BLOCK_SIZE, (char *)rdbuff);
            }
        }
        else if(count > 1)
        {
            if (SDCardInfo.CardType == CardType_SDHC)
            {
                ret = wm_sd_card_blocks_read(fs_rca, sector, (char *)rdbuff, buflen);
            }
            else if ((SDCardInfo.CardType == CardType_SDSC) || (SDCardInfo.CardType == CardType_SD))
            {
                for (int j = 0; j < count; j++)
                {
                    ret = wm_sd_card_blocks_read(fs_rca, sector + j* BLOCK_SIZE, (char *)rdbuff, BLOCK_SIZE);
                }
            }
        }
        if( ret == 0 ) 
            break;
    }

    if(rdbuff != buff)
    {
        if(ret == 0)
        {
            memcpy(buff, rdbuff, buflen);
        }
        tls_mem_free(rdbuff);
    }

    return ret;
}

4.实验结果

【1】使用的SD卡:我手头目前只有两张SD卡,一张为4G的SDHC卡,另一张为128MB的SD卡,如下图所示。
8.jpg

【2】回显的结果:如下图所示,都能成功读写并挂载Fatfs
1.png
2.png
3.png
4.png

最后附上百度云链接

我个人测试下来没有啥问题了,如果有啥bug的话可以在评论区交流哈
链接:https://pan.baidu.com/s/1YFtnyO1YzOUhDGhLEiWuXw
提取码:e4fx

6 条评论

发布
问题