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 條評論

發布
問題