当前位置: 首页 新闻资讯 技术问答

SDIO-TF CARD

SD NAND-贴片式TF卡-贴片式SD卡-免费测试2024-01-11465

  • 本SDIO例程,测试过程中会将SD卡的数据直接删除,相当于将SD卡格式化,请使用一张没有数据的SD卡进行测试。 数据丢失后果自负。

SD卡是我们日常使用的电子设备,例如用在相机、个人数据存储。部分手机也会使用TF卡作扩展存储使用。

SD存储卡是一种基于半导体快闪记忆器的新一代记忆设备,由于它体积小、数据传输速度快、可热插拔等优良的特性,被广泛地于便携式装置上使用,例如数码相机、个人数码助理(外语缩写PDA)和多媒体播放器等。

SD卡标准使用SDIO接口通信,也支持SPI接口。在日常使用情景,例如相机、读卡器等,都是使用SDIO通信,SDIO使用4个数据线,速度比SPI要快。 SD卡使用SDIO口通信过程遵循SD卡规范,这个规范不简单,涉及到较多的命令交互和状态转换。通常芯片厂家都会提供相关例程,用户不需要重新开发SDIO通信程序。本例程通过移植ST官方的例程实现与SD卡通信。

SDIO接口

../../_images/pic42.png

下图是SDIO框图,

../../_images/pic31.png

这是一个总框图,主要说明外部连接。 对于SDIO适配器,并没有详细说明。我猜测原因是,其实SDIO是一个比较复杂的设备,类似USB。 通常这种复杂外设,我们都不会自己开发驱动,都是用官方提供的例程。

  1. 总线的通信基于命令和数据传输。其实这是大部分外设的相同之处。

  2. SDIO接口总共有6根线(4位位宽),其中命令只通过CMD线传输。因此在调试的时候如果命令正常,读写数据不正常,说明仅仅是数据线问题。

SDID接口除了用于控制SD卡等卡之外,也可以控制SDIO接口的其他模块,例如SDIO接口的WIFI模块

SDIO接口存储卡

../../_images/pic52.png../../_images/pic6.png

其实在SD卡之前,最先出现的是MMC卡。 下图这种就是MMC卡,只有SD卡一半大小,通常我们都用一个卡尾拼成SD卡用。MMC(MULTI-MEDIA CARD,多媒体卡)由西门子公司SIEMENS和SANDISK于1997年推出。

../../_images/pic25.png

这些卡的协议基本都是兼容的。

../../_images/pic7.png


原理图

../../_images/pic17.jpg

SDIO接口使用6根管脚。 CLK是通信时钟 CMD是命令串行通信线。 DATA数据线有四根。 右边的SD_CARD_DET_N是卡插入检测 SDIO接口,CLK不需要上拉电阻,其他5根IO需要加上拉电阻。

驱动设计

前面说到,SDIO是一个复杂的协议,SD卡、TF卡是一个较复杂设备。要完全弄清楚,非常不容易,更加不要说自己写一套了。 通常,这种复杂的协议,我们不自己开发,都是芯片厂提供驱动代码。

前面几个章节的调试,我们直接参考标准库接口,再根据参考手册,就可以进行编码了。 但是类似USB,SD卡,网络等较复杂的设备,通常做法是从官方提供的库入手。 特别是USB通信,个人基本不可能写一套库出来,也没这个必要。

移植调试

在移植之前,先对官方例程进行分析学习。

例程分析

在标准库下面有SD卡例程,路径如下

STM32F4XX_DSP_STDPERIPH_LIB_V1.8.0PROJECTSTM32F4XX_STDPERIPH_EXAMPLESSDIOSDIO_USDCARD

在目录下有一个README.TXT文件。看东西,先从README入手。 从README中可以看出,STM32F407芯片的SDIO的驱动在 STM32F4XX_DSP_STDPERIPH_LIB_V1.8.0UTILITIESSTM32_EVALSTM3240_41_G_EVAL目录下, 名字叫STM324XG_EVAL_SDIO_SD.C和STM324XG_EVAL.C。 其中STM324XG_EVAL.C仅仅提供了较底层的初始化。 看来主要代码都在STM324XG_EVAL_SDIO_SD.C里面。

../../_images/14.png

STM324XG_EVAL_SDIO_SD.C 例程还包括以下文件

路径:STM32F4XX_DSP_STDPERIPH_LIB_V1.8.0PROJECTSTM32F4XX_STDPERIPH_EXAMPLESSDIOSDIO_USDCARD

  • SDIO/SDIO_USDCARD/SYSTEM_STM32F4XX.C STM32F4XX SYSTEM CLOCK CONFIGURATION FILE

  • SDIO/SDIO_USDCARD/STM32F4XX_CONF.H LIBRARY CONFIGURATION FILE

  • SDIO/SDIO_USDCARD/STM32F4XX_IT.C INTERRUPT HANDLERS

  • SDIO/SDIO_USDCARD/STM32F4XX_IT.H INTERRUPT HANDLERS HEADER FILE

  • SDIO/SDIO_USDCARD/MAIN.C MAIN PROGRAM

  • SDIO/SDIO_USDCARD/MAIN.H MAIN PROGRAM HEADER FILE

下一步,我们就浏览这六个文件,熟悉其中的流程与调用关系。 使用SI创建一个SDIO_EVAL工程,将以上提到的文件添加到工程,用SI分析程序相当方便。 为了防止无意中修改库文件,我们把文件拷贝一份

../../_images/24.png

SD卡例程源文件 从MAIN函数入手,程序首先对SD进行初始化。然后进行块测试,分别进行擦块,单块测试,多块测试。


  /*------------------------------ SD INIT ----------------- */
  IF((STATUS = SD_INIT()) != SD_OK)
  {
    STM_EVAL_LEDON(LED4);
  }

  WHILE((STATUS == SD_OK) && (UWSDCARDOPERATION != SD_OPERATION_END)
        && (SD_DETECT()== SD_PRESENT))
  {
    SWITCH(UWSDCARDOPERATION)
    {
      /*-------------------------- SD ERASE TEST --------- */
      CASE (SD_OPERATION_ERASE):
      {
        SD_ERASETEST();
        UWSDCARDOPERATION = SD_OPERATION_BLOCK;
        BREAK;
      }
      /*------------------- SD SINGLE BLOCK TEST --------- */
      CASE (SD_OPERATION_BLOCK):
      {
        SD_SINGLEBLOCKTEST();
        UWSDCARDOPERATION = SD_OPERATION_MULTI_BLOCK;
        BREAK;
      }       
      /*------------------- SD MULTI BLOCKS TEST --------- */
      CASE (SD_OPERATION_MULTI_BLOCK):
      {
        SD_MULTIBLOCKTEST();
        UWSDCARDOPERATION = SD_OPERATION_END;
        BREAK;
      }              
    }
  }

从函数SD_ERROR SD_INIT(VOID)跟下去。 函数首先调用了SDIO底层初始化,然后读卡,进行POWER ON了,上电成功就初始化。 先看看SDIO底层初始化了什么。

SD_ERROR SD_INIT(VOID){
  __IO SD_ERROR ERRORSTATUS = SD_OK;

  /* SDIO PERIPHERAL LOW LEVEL INIT */
   SD_LOWLEVEL_INIT();

  SDIO_DEINIT();

  ERRORSTATUS = SD_POWERON();

VOID SD_LOWLEVEL_INIT(VOID)函数就在前面提到过的STM324XG_EVAL.C文件内,也就是官方的DEMO板的初始化文件。 我个人觉得这些代码应该放到MCU_SDIO驱动内。 这个文件与SD卡有关的也就四个函数,前面两个是SDIO口初始化,后面两个是SDIO使用DMA的初始化。

VOID SD_LOWLEVEL_DEINIT(VOID);
VOID SD_LOWLEVEL_INIT(VOID);
VOID SD_LOWLEVEL_DMA_TXCONFIG(UINT32_T *BUFFERSRC, UINT32_T BUFFERSIZE);
VOID SD_LOWLEVEL_DMA_RXCONFIG(UINT32_T *BUFFERDST, UINT32_T BUFFERSIZE);

回到SD_INIT(VOID),后续调用的函数全部都在本文件了。 再回到MAIN,看测试程序在哪里。 就放在MAIN函数下面,这些函数我倒觉得应该放在SD卡驱动里面。当然,个人意见。

../../_images/62.png

SD卡测试函数 其他几个文件,前面例程已经在使用,我们就对比一下看看差异。

STM32F4XX_CONF.H,有差别,但是与SD卡无关。

../../_images/71.png

STM32F4XX_IT.C,多了两个中断处理,移植的时候记得拷贝。

../../_images/810.png

STM32F4XX_IT.C差异

STM32F4XX_IT.H多一个SDIO中断的声明,无关紧要

../../_images/97.png

STM32F4XX_IT.H差异

SYSTEM_STM32F4XX.C,差别较大,但是很多都是为了兼容其他芯片的条件编译。 但是这个文件主要是初始化时钟,如果移植后调试不顺利,有问题,需要回来认真分析这个文件。

../../_images/101.png

SYSTEM_STM32F4XX.C差异

OK,下一步我们就开始移植了。

移植调试过程

  1. 建立一个MCU_SDIO驱动,将STM324XG_EVAL.C里的四个函数作为MCU_SDIO驱动,拷贝到MCU_SDIO.C。 STM324XG_EVAL.H里面与SD卡相关的定义也要拷贝到MCU_SDIO.H里面。

  2. STM324XG_EVAL_SDIO_SD.C跟STM324XG_EVAL_SDIO_SD.H我们就直接使用。 作为BOARD_DEV驱动。

  3. 将MAIN.C里面的SD卡测试程序拷贝到STM324XG_EVAL_SDIO_SD.C最后,作为驱动测试程序使用。 原来的测试程序使用了一些官方硬件上的LED,我们全部改为串口调试信息输出。

  4. 有一个需要特别关注的地方就是STATIC VOID NVIC_CONFIGURATION(VOID)函数,这个函数的第一行就设置了NVIC的分组,也就是中断优先级分组,这个是ARM核相关。关于NVIC,前面章节有说明。

  5. 把STM32F4XX_IT.C里面的两个中断入口拷贝到我们的STM32F4XX_IT.C文件。

  6. 将新文件添加到工程,编译。21个错误,一个一个解决。挑几个看看 ..MCU_DEVMCU_SDIO.C(67): ERROR: #20: IDENTIFIER “SD_DETECT_GPIO_CLK” IS UNDEFINED 因为MCU_SDIO.C没有包含MCU_SDIO.H。增加#INCLUDE “MCU_SDIO.H”。 其实在我的个人认识当中,H文件应该是提供给外部使用。 哪些仅仅是驱动内部使用的定义,最好是定义到C文件内,不让其他文件看到。减少不必要的耦合。

  7. 重新编译,还有一个错误 ..BOARD_DEVSTM324XG_EVAL_SDIO_SD.H(39): ERROR: #5: CANNOT OPEN SOURCE INPUT FILE “STM324XG_EVAL.H”: NO SUCH FILE OR DIRECTORY 改为包含MCU_SDIO.H。

  8. 又有38个错误了。 ..BOARD_DEVSTM324XG_EVAL_SDIO_SD.H(128): ERROR: #20: IDENTIFIER “UINT32_T” IS UNDEFINED 应该是没包含STM32的头文件, 官方STM324XG_EVAL_SDIO_SD.H包含了#INCLUDE “STM324XG_EVAL.H”, 然后STM324XG_EVAL.H包含了#INCLUDE “STM32F4XX.H”和#INCLUDE “STM32_EVAL_LEGACY.H”,我们在MCU_SDIO.H中包含#INCLUDE “STM32F4XX.H”。

  9. 编译没有错误了。

  10. 开始做硬件移植,在MCU_SDIO.H第20行,例程用PH13做SD卡检测,我们都没有H口。我们用的是PC13.

  11. 在MCU_SDIO.C的初始化中,进行了初始化,个人认为这些跟硬件相关的,还是放到一处用宏定义较好,方便移植修改。 数据线,和我们的硬件一样,不需要修改。

/* CONFIGURE PC.08, PC.09, PC.10, PC.11 PINS: D0, D1, D2, D3 PINS */
命令线,也跟我们一样。
/* CONFIGURE PD.02 CMD LINE */
时钟线也一样。
/* CONFIGURE PC.12 PIN: CLK PIN */
  1. 那么就是检测脚不一样,我们看看这个管脚怎么用的。搜索后发现有一个SD_DETECT函数在使用。

/** * @BRIEF  DETECT IF SD CARD IS CORRECTLY PLUGGED IN THE MEMORY SLOT. * @PARAM  NONE * @RETVAL RETURN IF SD IS DETECTED OR NOT */UINT8_T SD_DETECT(VOID){
 __IO UINT8_T STATUS = SD_PRESENT;

 /*!< CHECK GPIO TO DETECT SD */
 IF (GPIO_READINPUTDATABIT(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != BIT_RESET)
 {
   STATUS = SD_NOT_PRESENT;
 }
 RETURN STATUS;}
  1. SDCARDSTATE SD_GETSTATE(VOID)和测试程序会使用SD_DETECT。到这里看出,检测管脚就是一个普通IO口,没有使用中断等其他功能,直接修改为我们的管脚即可。

  2. 重新编译,插上TF卡,下载程序运行。 初始化不成功。在初始化函数添加调试信息。 成功上电,进入初始化,卡信息也获取成功了,选卡也成功。

图片.png

选卡成功 我们先看一下卡信息,卡信息结构体如下:

/**  * @BRIEF SD CARD INFORMATION  */TYPEDEF STRUCT{
 SD_CSD SD_CSD;
 SD_CID SD_CID;
 UINT64_T CARDCAPACITY;  /*!< CARD CAPACITY */
 UINT32_T CARDBLOCKSIZE; /*!< CARD BLOCK SIZE */
 UINT16_T RCA;
 UINT8_T CARDTYPE;} SD_CARDINFO;

CSD跟CID是卡信息,包含比较多信息。例如卡什么卡,什么版本,V1.0,还是2.0等。 本处我们先把容量CAPACITY跟BLOCK SIZE打印出来,看跟我们的卡是不是一致。 调试代码如下,要注意的地方是容量的处理:如果直接使用UART_PRINTF打印容量,打印出来一个10,不正确,要拆分为两部分打印。

/*----------------- READ CSD/CID MSD REGISTERS ------------------*/
 ERRORSTATUS = SD_GETCARDINFO(&SDCARDINFO);
   UART_PRINTF("RN-------SD_GETCARDINFO OK----------RN");

UINT32_T *P;
P = (UINT32_T *)&(SDCARDINFO.CARDCAPACITY);
UART_PRINTF("RN-------CARDCAPACITY:%08X----------RN", *(P+1));
   UART_PRINTF("RN-------CARDCAPACITY:%08X----------RN", *(P+0));  
   UART_PRINTF("RN-------CARDBLOCKSIZE:%D ----------RN", SDCARDINFO.CARDBLOCKSIZE);

   UART_PRINTF("RN-------RCA:%D ----------RN", SDCARDINFO.RCA);
   UART_PRINTF("RN-------CARDTYPE:%D ----------RN", SDCARDINFO.CARDTYPE);

卡信息调试LOG:

——-DEV_SDIO_TEST———- ——-SD_POWERON OK———- ——-SD_INITIALIZECARDS OK———- ——-SD_CSD.DEVICESIZE:15271———- ——-SD_GETCARDINFO OK———- ——-CARDCAPACITY:00000001———- ——-CARDCAPACITY:DD400000———- ——-CARDBLOCKSIZE:512 ———- ——-RCA:2 ———- ——-CARDTYPE:2 ———-

还有,刚刚等了很久发现,初始化返回结果4,数据超时。

  1. 查看SD_ERROR SD_ENABLEWIDEBUSOPERATION(UINT32_T WIDEMODE),分析大概流程后加上调试信息。 应该是陷入STATIC SD_ERROR SDENWIDEBUS(FUNCTIONALSTATE NEWSTATE)超时。 源码1103行

 ELSE IF (SDIO_BUSWIDE_4B == WIDEMODE)
   {
     ERRORSTATUS = SDENWIDEBUS(ENABLE);
     UART_PRINTF("SDENWIDEBUS:%DRN", ERRORSTATUS);

经查,在FINDSCR处,源码2438行。

   UART_PRINTF("SDIO_GETRESPONSE OKRN");
 /*!< GET SCR REGISTER */
 ERRORSTATUS = FINDSCR(RCA, SCR);
   UART_PRINTF("FINDSCR:%DRN", ERRORSTATUS);

FINDSCR函数内有个WHILE,估计程序就是卡在这里,源码2760行

WHILE (!(SDIO->STA & (SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL
 | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR))){
   IF (SDIO_GETFLAGSTATUS(SDIO_FLAG_RXDAVL) != RESET)
   {
     *(TEMPSCR + INDEX) = SDIO_READDATA();
     INDEX++;
   }}
  1. 硬件修改后,SD卡初始化成功,但是测试失败。

——-DEV_SDIO_TEST———- ——-SD_POWERON OK———- ——-SD_INITIALIZECARDS OK———- ——-SD_CSD.DEVICESIZE:15271———- ——-SD_GETCARDINFO OK———- ——-CARDCAPACITY:00000001———- ——-CARDCAPACITY:DD400000———- ——-CARDBLOCKSIZE:512 ———- ——-RCA:2 ———- ——-CARDTYPE:2 ———- ——-SD_SELECTDESELECT OK———- SDIO_GETRESPONSE OK FINDSCR:0 2453 MDRESP1ERROR:0 2468 CMDRESP1ERROR:0 SDENWIDEBUS:0 ——-SD_ENABLEWIDEBUSOPERATION:0———- ——-SD_INIT OK———- ——-SD_ERASETEST….———-

查后,发现卡在等待传输结束的等待上。 SD_ERROR SD_WAITREADOPERATION(VOID)函数第一个等待就过不去。 前面读写已经没问题,唯一的区别就是这里使用了DMA。 这里的WHILE(1),就是等待中断或者DMA标志。源码349行

__IO SD_ERROR TRANSFERERROR = SD_OK;__IO UINT32_T TRANSFEREND = 0;//SDIO中断中会赋值0X01;__IO UINT32_T DMAENDOFTRANSFER = 0;//DMA中断中会赋值0X01;SD_CARDINFO SDCARDINFO;

网上百度,发现很多人说ST的DMA BUG,其中一个BUG是,在读写之前要发CMD16设置BLOCK大小,我们使用的库已经修改了这个BUG,源码1335行

  /*!< SET BLOCK SIZE FOR CARD */
 SDIO_CMDINITSTRUCTURE.SDIO_ARGUMENT = (UINT32_T) BLOCKSIZE;
 SDIO_CMDINITSTRUCTURE.SDIO_CMDINDEX = SD_CMD_SET_BLOCKLEN;
 SDIO_CMDINITSTRUCTURE.SDIO_RESPONSE = SDIO_RESPONSE_SHORT;
 SDIO_CMDINITSTRUCTURE.SDIO_WAIT = SDIO_WAIT_NO;
 SDIO_CMDINITSTRUCTURE.SDIO_CPSM = SDIO_CPSM_ENABLE;
 SDIO_SENDCOMMAND(&SDIO_CMDINITSTRUCTURE);

还有就是中断处理函数SD_ERROR SD_PROCESSIRQSRC(VOID),以前没有处理出错信息,现在已经处理了。 (从这里可以学一点,我们自己写的中断处理处理函数,最好也响应错误中断) 本处是DMA传输,DMA传输一般都要求字节对齐,否则会出错或者是死机。我们看下到底是死机了还是一直在等待

  1. 既然可能死机,我们就使用CMSIS DAP调试一下,发现死在中断入口了。

../../_images/321.png

../../_images/321.png

../../_images/331.png

../../_images/331.png

未修改对 说明有一个中断源一直在进中断,或者是我们没有处理。 我们在DMA中断中增加了调试信息,但是却没有输出,很奇怪。 源码2065行

VOID SD_PROCESSDMAIRQ(VOID){
 UART_PRINTF("-2-");
 IF(DMA2->LISR & SD_SDIO_DMA_FLAG_TCIF)
 {
   DMAENDOFTRANSFER = 0X01;
   DMA_CLEARFLAG(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_TCIF|SD_SDIO_DMA_FLAG_FEIF);
 }}

搜索中断入口函数VOID SD_SDIO_DMA_IRQHANDLER(VOID); 发现,这个并没有在中断向量中定义,而是在MCU_SDIO.H中用宏定义。 真正的中断句柄是DMA2_STREAM3_IRQHANDLER,在移植的时候我们并没有处理,而且也不了解这样做要如何处理。 先改回DMA2_STREAM3_IRQHANDLER试试。MCU_SDIO.H第46行

#IFDEF SD_SDIO_DMA_STREAM3
#DEFINE SD_SDIO_DMA_STREAM            DMA2_STREAM3
#DEFINE SD_SDIO_DMA_CHANNEL           DMA_CHANNEL_4
#DEFINE SD_SDIO_DMA_FLAG_FEIF         DMA_FLAG_FEIF3
#DEFINE SD_SDIO_DMA_FLAG_DMEIF        DMA_FLAG_DMEIF3
#DEFINE SD_SDIO_DMA_FLAG_TEIF         DMA_FLAG_TEIF3
#DEFINE SD_SDIO_DMA_FLAG_HTIF         DMA_FLAG_HTIF3
#DEFINE SD_SDIO_DMA_FLAG_TCIF         DMA_FLAG_TCIF3
#DEFINE SD_SDIO_DMA_IRQN              DMA2_STREAM3_IRQN
#DEFINE SD_SDIO_DMA_IRQHANDLER        DMA2_STREAM3_IRQHANDLER#ELIF DEFINED SD_SDIO_DMA_STREAM6

测试通过。SDIO硬件测试完成。

../../_images/36.png

SDIO测试通过 程序测试通过,那到底是不是真的测试成功了呢? 我们可以用WINHEX软件查看TF卡内容,开头的几个数据块,已经被我们改为0X00–00XFF顺序增加的数据了,说明操作是成功的。

../../_images/37.png

总结

调试SD卡这一段写了很多,主要是让大家看看平时调试的方法,很多时候就是加调试信息,顺藤摸瓜。

遗留问题,SDIO驱动用了一个很大的BUF,这个要修改优化。 目前我们只是为了验证硬件,没有挂载文件系统,后续再加上。

热门标签:SDNAND贴片式TF卡贴片式SD卡SD FLASHSLC NANDNAND FLASH


SD NAND-贴片式TF卡-贴片式SD卡-免费测试

深圳市芯存者科技有限公司

售前咨询
售前咨询
售后服务
售后服务
联系我们

电话:176-6539-0767

Q Q:135-0379-986

邮箱:1350379986@qq.com

地址:深圳市南山区蛇口街道后海大道1021号B C座C422W8

在线客服 在线客服 QQ客服 微信客服 淘宝店铺 联系我们 返回顶部