关于2440的裸跑程序中SD卡读后不能成功写入问题的讨论

发布者:cocolang最新更新时间:2017-01-17 来源: eefocus关键字:裸跑程序  SD卡 手机看文章 扫描二维码
随时随地手机看文章

问题描述:  

  TQ2440的官方裸跑程序中,对SD卡先进行读操作,然后再写,发现不能程序卡死。倘若对SD卡先写后读,程序可以正常运行,奇哉怪哉?

写数据的关键代码-->

while(i < BlockSize)
{         
    //开始传递数据到缓冲区
    status=rSDIFSTA;    if((status&0x2000)==0x2000)
    { //如果发送FIFO可用,即FIFO未满
        rSDIDAT = *TxBuffer;
        TxBuffer++;
        i++;    
    }
}


调试与问题分析:

  调试的时候发现,当不能在写的时候,FIFO available detect for Tx (TFDET)为0,也即是说是fifo满了。此时,程序循环了16次(i=0x10)。循环一次写入4个字节,16次刚好是fifo的最大容量64字节。这证明了,写入fifo中的数据,本应该发送给SD卡,腾空fifo以供用户继续写,却被搁置在fifo中,有进无出。就像下水道中转站被堵,上游的污水就不能继续排放一行的道理。

    先写后读是可以正常工作的,我打印了执行写函数之后部分寄存器的值,如左图所示。可以发现写后的寄存器rSDIDCNT、rSDIDSTA都恢复到了初始值。右图是执行读函数之后寄存器的值,可以发现执行读函数之后,rSDIDCNT、rSDIDSTA都没有回到初始值,都仍然停留在读函数执行的状态中。也就是说,读函数没有执行彻底,SDMMC模块没有进入到空闲状态。在没有准备好的情况下,继续进行写操作,是不可能成功的。

 

修复

  修复的方法主要是无论读操作,还是写操作,都确认SDIO总线空闲时,然后再才退出当前的函数。这样可以保证在随后的操作中,SDMMC模块处于准备好的状态,而非遗留状态。

读函数 

/**********************************************************************************

功  能:该函数用于从SD卡中读出指定块起始地址的单个数据块

参  数:

 U32  Addr  被读块的起始地址

 U8* RxBuffer 用于接收读出数据的缓冲区

返回值:

 0 读块操作不成功

 1 读块操作成功

举  例:

 在主调函数中定义一个数组作为接收缓冲区,如U8 Rx_buffer[BlockSize];

 然后开始调用Read_One_Block(addr,Rx_buffer);

**********************************************************************************/

U8 Read_One_Block(U32 Addr,U8 * RxBuffer)

{

    U16 i=0;

    U32 status=0;

    U16 BlockSize;                        //定义块大小

    U16 BlockNumber;

    

    BlockSize=1 << SDCard_BlockSize;     //以byte为单位

    BlockNumber = ((Addr >>  SDCard_BlockSize) + 1) &0x0fff;

    

    rSDIDTIMER=0x7fffff;                // Set timeout count

    rSDIBSIZE=0x200;                    // 512byte(128word)

    rSDIFSTA=rSDIFSTA|(1<<16);            // FIFO reset

    rSDIDCON = (BlockNumber<<0)|(2<<12)|(1<<14)|(1<<16)|(1<<17)|(1<<19)|(0<<22);

    

    while(CMD17(Addr)!=1)                //发送读单个块指令

    {

#ifdef __SD_MMC_DEBUG__

        Uart_Printf("Send read addr failed!\n");

#endif

    }

    

    /* 开始接收数据到缓冲区 */

    while(i

    { 

        status = rSDIDSTA;

        if(status&0x60)                    //检查是否超时和CRC校验是否出错

        {     

            rSDIDSTA=(0x3<<0x5);         //清除超时标志和CRC错误标志

#ifdef __SD_MMC_DEBUG__

        Uart_Printf("there is wrong when reading: %s.\n",status&0x20 ? "time out" :"CRC error");

#endif

            return 0;

        }        

        status=rSDIFSTA;

        if((status&0x1000)==0x1000)        //如果接收FIFO中有数据

        { 

            *RxBuffer=rSDIDAT;

            RxBuffer++;

            i++;

        }

    status = rSDIDSTA;

    Delay(2);                            //延时2ms

    rSDIDCON=rSDIDCON&~(7<<12);            //结束SDMMC模块的接收

    rSDIDSTA = status;                     //清状态标志位


    /* 确认SD卡进入了空闲状态--SDIO总线空闲 */

    rSDIDCON=(0<<18)|(1<<17)|(1<<16)|(1<<14)|(1<<12)|(BlockNumber<<0);

    rSDIDTIMER=0x7fffff;

    status = rSDIDSTA;

    while( !( ((status&0x08)==0x08) | ((status&0x20)==0x20)| ((status&0x800)==0x800) )){

        status=rSDIDSTA;

    }

    

    if( (status&0x20) == 0x20 ){

        rSDIDSTA = status; 

        return 0;

    }    

    rSDIDSTA = status;    

    return 1;

}


写函数

/**********************************************************************************

功  能:该函数用于向SD卡的一个数据块写入数据

参  数:

 U32  Addr  被写块的起始地址

 U8* TxBuffer 用于发送数据的缓冲区

返回值:

 0 数据写入操作失败

 1 数据写入操作成功

举  例:

 在主调函数中定义一个数组作为发送缓冲区,如U8 Tx_buffer[BlockSize];

 然后开始调用Write_One_Block(addr,Tx_buffer);

**********************************************************************************/

U8 Write_One_Block(U32 Addr,const U8 * TxBuffer)

{

    U16 i = 0;

    U32 status = 0;

    U16 BlockSize;                             //定义块大小

    U16 BlockNumber;

    

    BlockSize = 1<< SDCard_BlockSize;         //以byte为单位

    BlockNumber = ((Addr >>  SDCard_BlockSize) +1) &0x0fff;

    

    rSDIDTIMER=0x7fffff;                    // Set timeout count

    rSDIBSIZE=0x200;                        // 512 byte(128 word)

    rSDIFSTA = rSDIFSTA|(1<<16);             // FIFO reset

    rSDIDCON = BlockNumber|(3<<12)|(1<<14)|(1<<16)|(1<<17)|(1<<20)|(0<<22);


    while(CMD24(Addr)!=1)                     //发送写单块操作指令

    {

#ifdef __SD_MMC_DEBUG__

        Uart_Printf("Send write addr failed!\n");

#endif

    }

    /* 开始传递数据到缓冲区 */

    while(i < BlockSize)

    {         

        

        status=rSDIFSTA;

        if((status&0x2000)==0x2000)            //如果发送FIFO可用,即FIFO未满

        { 

            rSDIDAT = *TxBuffer;

            TxBuffer++;

            i++;    

        }

    }    

    

    status = rSDIDSTA;

    Delay(5);

    rSDIDCON=rSDIDCON&~(7<<12);                //结束SDMMC模块的发送

    rSDIDSTA = status; 

    

    /* 确认SD卡进入了空闲状态--SDIO总线空闲 */

    rSDIDCON=(0<<18)|(1<<17)|(1<<16)|(1<<14)|(1<<12)|(BlockNumber<<0);

    rSDIDTIMER=0x7fffff;                    // Set timeout count

    status = rSDIDSTA;

    while( !( ((status&0x08)==0x08) | ((status&0x20)==0x20)| ((status&0x800)==0x800) )){

        status=rSDIDSTA;

    }

    

    if( (status&0x20) == 0x20 ){

        rSDIDSTA = status; 

        return 0;

    }    

    rSDIDSTA = status;        

      return 1;

}


测试效果

  以下操作都得到成功验证:

  • 单块读

  • 单块写

  • 多块读(调用单块读函数实现)

  • 多块写(调用单块写函数实现)

  • 先读后写

  • 先写后读

    移植fatfs文件系统测试:

  大多数操作没有故障出现,偶尔也会出现写函数卡死的情况

 

仍然存在的Bug

  1、读函数在确认SDIO总线空闲时候,经常进入超时状态,这导致读函数的速度很慢。

  2、移植fatfs文件系统测试,偶尔也会出现写函数卡死的情况。由于底层驱动运行缓慢,所以文件系统很卡。保存一张

482KB的bmp文件,耗费了十几秒种。



关键字:裸跑程序  SD卡 引用地址:关于2440的裸跑程序中SD卡读后不能成功写入问题的讨论

上一篇:TQ2440开发板网络配置方式
下一篇:STM32与S3C2440的区别

推荐阅读最新更新时间:2024-03-16 15:31

ARM S3C2440 时钟初始化流程
1.设置lock time 2.设置分频系数 3.设置CPU到异步工作模式 4.设置 FCLK 了解 芯片的时钟原理图,以及寄存器的作用 了解芯片的晶振频率,锁相环,分频系数,以及有哪些时钟
[单片机]
S3C2440裸机------代码重定位
1.段的概念 我们的cpu可以直接把地址通过内存控制器发送到norflash,sram,sdram,但是我们的cpu不能直接到达nandflash,只能发送到nandflash控制器,我们的程序可以放到norflash以及sdram上面,它可以直接运行,但是nandflash的程序是不能直接运行的,但是我们仍然可以把程序放到nandflash上面,因为一上电2440内部的硬件会把nandflash的前4K代码复制到片内的SRAM,这个是由硬件做的,然后cpu从0地址开始运行,此时的0地址对应的是sram。那么如果我的程序超过4K怎么办,如果bin文件在nandflash上面超过4K,那么我们不能只复制前面4K代码,所以显然我们前面
[单片机]
S3C<font color='red'>2440</font>裸机------代码重定位
S3C2440的七种模式之——未定义模式(去掉bl print1 bug解决)
现在做第一个实验,模拟未定义模式。 未定义模式,是cpu遇到自己不能识别的指令时候做出的异常处理。 arm指令的机器码一定是按照某种规范要求的,不然你随意写一条指令,cpu不是都可以执行吗?在cpu没有定义该条指令含义的情况下,我们执行了这样未定义的指令,就会进入未定义异常。 现在我们要模拟一个未定义异常,所以我们只要写出一个cpu无法识别的指令即可。 在这之前,要明白一个道理,在内存中执行的机器码,只有0,1两个值,不同的指令被分解为不同的0,1信号的机器码。 所以,我们在运行内存中存放一个32bit的值,这个值又恰恰是上图所不能表示的指令,那么这样,就可以测试未定义异常了。我们采用.word 关键字,.word ex
[单片机]
S3C<font color='red'>2440</font>的七种模式之——未定义模式(去掉bl print1 bug解决)
S3C2440裸机复习------GPIO
第一遍看完S3C2440裸机后,有些遗忘了,再挑选几个复习一下,首先是GPIO。 1 原理图 首先需要看一下原理图,可以看到我们把GPF4设置为低电平就可以让LED1点亮。 2 芯片手册 从芯片手册可以看到,我们要把GPFCON寄存器的 设置为01,然后GPFDAT的 设置为0. 3 汇编语言点亮LED 3.1 start.S .test .global _start mov 0x56000050 #0x100 mov ox56000054 #0x10 .halt b halt 3.2 makefile all: arm-linux-gcc -c start.S -o start.o
[单片机]
S3C<font color='red'>2440</font>裸机复习------GPIO
s3c2440裸机-电阻触摸屏编程(2.触摸屏TS控制器)
触摸屏接口模式 Normal Conversion Mode: 正常转换模式,一般情况下可以配置ADCCON和ADCDAT0来读取数据。 Separate X/Y position conversion Mode: x,y坐标分离转换格式,x坐标会写入ADCDAT0, y坐标会写入ADCDAT1,所以会产生2次中断开分开完成x,y的坐标转换。 Auto(Sequential) X/Y Position Conversion Mode 自动转换模式,当触摸屏按下后,会一次性对x,y方向的坐标进行转换,x坐标会写入ADCDAT0, x坐标会写入ADCDAT1。会产生一次中断进行x,y坐标的自动转换。 Waiting
[单片机]
s3c<font color='red'>2440</font>裸机-电阻触摸屏编程(2.触摸屏TS控制器)
S3C2440 MMU详解
序言 在曾经的很久一段时间,我对于MMU都有一种莫名的敬畏。来自于对其的认知不够透彻,因此我必须在此把MMU的原理完整的记录下来,作为对过去的一个了结。 MMU的意义 我相信关于MMU存在的必要性的说明都已经的烂大街了。我想强调的是在现代大型操作系统对硬件的基本要求中MMU就是其中一项。在操作系统中所有进程都有自己专属的MMU页表,这些页表会在任务调度中动态切换,以避免黑客程序恶意攻击 以及 程序设计无需考虑如何分配到空闲内存的目的。 MMU的原理 我认为MMU的设计有如下目的: 能覆盖全范围的地址寻址 基于页表进行地址索引 以下图为例,TTB是ARM的寄存器,存放着一级页表的基地址,上面所说的任务调度中所谓切换的MM
[单片机]
使用u-boot_2016_01启动mini2440(一)启动代码
mini2440的soc用的是s3c2440,因为板子买回来烧写过NOR,而且NOR启动也过于简单,所以我主要记录下NAND启动。s3c2440的NAND启动原理,是硬件通过引脚判断NAND启动后,会搬运NAND从0地址开始的前4K内容到SRAM上,然后在SRAM上运行起来,这段4K的代码,必须至少要初始化DDR,并且搬运bootloader到DDR,才能运行接下来的代码。普通的arm bootloader的启动流程来看,board_init_f应该在relocate_code之前运行,用来计算将要搬运的目的地址和栈寄存器地址等等,但是因为board_init_f代码段太大,编译不进前4K,所以,网上通用的做法好像是先预设relo
[单片机]
基于S3C2440的WinCE Bootloader的分析与设计
Bootloader的开发是嵌入式系统开发必不可少的环节,一个好的Bootloader不仅可以给项目的后续开发工作带来很大便利,而且在项目开发结束后对用户使用产品也提供了很多方便。但是,由于嵌入式的硬件是无标准、非规范的,Bootloader的功能又是直接与微处理系统相关的,所以给开发人员的工作带来了许多不便。在实际的项目开发中,一般都需要对特定的硬件系统进行Bootloader的设计,可是从头开发一套系统的Bootloader是非常复杂并且耗时的,针对这一难点,微软公司推出的面向嵌入式应用领域的操作系统Windows CE体现了非常大的优势,Windows CE具有强大的操作系统功能、稳定可靠的性能、高度的模块化、可定制性、
[单片机]
基于S3C<font color='red'>2440</font>的WinCE Bootloader的分析与设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

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