W801的SDK中的SDIO与Fatfs优化

发布者:TranquilVibes最新更新时间:2022-08-08 来源: csdn关键字:SDK  SDIO 手机看文章 扫描二维码
随时随地手机看文章

一.项目概述

在使用默认的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);

1

【2】问题的原因:SDIO在mmc模式下,且使用的SD卡片为默认速度配置的话,最高的输出频率只能为20M。W801的SDIO挂载在CPU的时钟总线上,通过分频得到,原来SDK中的默认分频数为1/6,当修改主频后,SDIO的总线频率也就变成了40M,超过上限无法正常读写。

【3】解决的办法:解决办法有两种,一个是修改分频寄存器的值,一个是修改SD卡的速度配置,我这里选择的是前者。SDIO分频的值对应表如下图所示;

在这里插入图片描述

因此当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;

}


【修改后试验结果】

在这里插入图片描述

三.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

在这里插入图片描述

【2】不同SD卡的读写区别

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


【3】SD卡的初始化与识别

根据SD2.0协议,上述四类卡片的识别过程如下图所示,我也写了一个中文版的导图附上。

在这里插入图片描述
在这里插入图片描述

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 Errorn");

printf("CMD8 Errorn");

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 Errorn");

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 - 2n");

goto end;

}

if(respCmd[0] >> 31 & 0x1)

{

TEST_DEBUG("card is readyn");         //这里未对CCS位进行判断,无法区分SDSC与SDHC

printf("card is readyn");

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 - 3n");

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 - 4n");

goto end;

}

*rca = respCmd[0] >> 16;

TEST_DEBUG("RCA = %xn", *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 Errorn");

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 readyn");

// 根据CCS位来判断为哪一类型的卡片

if ((respCmd[0] >> 30 == 0x3) && (temp_type == 0x0))

{

SDCardInfo.CardType = CardType_SDHC;

printf("nCardtype[%d]: SDHCn", SDCardInfo.CardType);

}

else if ((respCmd[0] >> 30 == 0x2) && (temp_type == 0x0))

{

SDCardInfo.CardType = CardType_SDSC;

printf("nCardtype[%d]: SDSCn", SDCardInfo.CardType);

}

else if (temp_type == 0x1)

{

SDCardInfo.CardType = CardType_SD;

printf("nCardtype[%d]: SDn", 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 = %xn", *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 %sn", 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 %dn", 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)

[1] [2]
关键字:SDK  SDIO 引用地址:W801的SDK中的SDIO与Fatfs优化

上一篇:W801的SDK无法成功驱动W25Q128的解决方法
下一篇:W801上电自动重连wifi并通过蓝牙更新账号密码

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved