上一节,完成了eCos STM32 SD driver代码的编写,展现了如何借鉴第三方驱动快速部署驱动代码的实例。与时同时,再次看到了eCos DMA的运用。

本节主要介绍eCos STM32 SD卡驱动的调试,再次完整地呈现了eCos驱动的调试过程。如果说编写代码大多数是复制粘贴,那么通过调试,则让我了解了SD卡存储结构、FAT文件系统知识及其数据分析,最重要的是了解了eCos的FAT和Block library(块缓存)程序。

调试必备

1. 调试之前,建议先了解下SD卡存储结构和FAT文件系统的知识,可参考本博客的文章:

2. 调试开关,调试开关有disk设备驱动、disk IO、FAT、Block lib等调试开关。

disk设备驱动位于mmc_stm32.c文件中开关定义的宏:#define DEBUG   0,这是个分级的调试开关,为1时,打印一般的调试信息;为2时,会打印SD block读写数据。

disk IO、FAT、Block lib等调试开关,分别位于相应源文件中:diskio.c、fatfs.h、blib.c,可视需要打开。

3. 调试方法,主要采用打印调试方法,可根据需要使用仿真器进行调试。

4. 调试顺序,总共有3个阶段:

  1. 初步运行驱动,如果没有问题,进入第2阶段;
  2. SD卡mount,这是调试重点,涉及SD卡初始化及其识别调试、STM32 SDIO DMA传输、FAT文件系统初始化等;
  3. 在SD卡中调试目录、文件的打开、数据读写等操作,以及SD卡磁盘信息的读取等调试;

限于篇幅,本节主要介绍前面2个阶段的调试过程。

初步运行

编译eCos STM32 SD卡驱动及其测试程序,生成可执行映像文件并启动运行。我是通过往串口输入字符来启动测试程序的。

启动运行后,观察mmc_stm32_disk_init()函数运行情况。然后启动应用测试程序,执行mount(),进入第2个阶段的调试。

mount阶段调试

开始调试的时候,mount SD卡百分百出问题。测试程序执行mount后会返回以下错误信息(基于eCos自带的fatfs.c测试程序):

<FAIL>: mount() returned -1 Invalid argument

对于这个错误信息,开始的时候,我是一头误水,甚至还从mount()函数一层层去追。对于fat文件系统,mount的真正入口点是fatfs.c源文件中的fatfs_mount()函数(类似,open/read/write/close等也对应该源文件中的相关函数)。

在fatfs_mount()函数中,先lookup对应的设备,实际上就是执行mmc_stm32.c中的mmc_stm32_disk_lookup()函数。OK,从这里开始慢慢查找出现上述错误的原因吧。

程序卡死

在执行stm32_mmc_init()函数中,会有卡死现象(因为驱动中有很多死循环)。对于卡死的问题,根据打印信息先定位被卡住的代码段,然后与STM32官方库代码实现进行对比,这样基本上可以解决该问题。

FILE FORMAT GROUP问题

在下面代码中会直接返回:

    // There is information available about the file format, e.g.
    // partitioned vs. simple FAT. With the current version of the
    // generic disk code this needs to be known statically, via
    // the mbr field of the disk channel structure. If the card
    // is inappropriately formatted, reject the mount request.
    if ((0 != MMC_CSD_REGISTER_FILE_FORMAT_GROUP(csd)) ||
        (0 != MMC_CSD_REGISTER_FILE_FORMAT(csd))) {
        DEBUG1("Error, NOTDIR!\n");
//      return -ENOTDIR;
    }

这段代码是从mmc_spi.c中拷贝过来的。但在我的驱动中,由于MMC_CSD_REGISTER_FILE_FORMAT_GROUP(csd)值为1,所以就直接返回了。至于是什么错误,没有去研究,反正把return注释掉也没有影响。

DMA传输问题

在stm32_mmc_check_for_disk()函数中,执行完SD卡初始化和识别后,就会调用stm32_mmc_read_disk_block()函数读取block 0数据块。但读SD卡数据块时,出现了异常现象:表面现象是DMA传输不稳定(一会儿可以一会儿失败),实质是DMA读取数据失败。DMA打印信息如下(注意加粗部分):

DMA: stm32_dma_channel_setup[1639]: Bytes to transfer : 128 * 4, (count = 512)
DMA: stm32_dma_channel_setup[1641]: ctlr 40020400 stream 4 chan 0
DMA: stm32_dma_channel_setup[1642]: vector 60 stream->ccr 00002a81
DMA: stm32_dma_channel_setup[1645]: DMA   ISR:   00000000
DMA: stm32_dma_channel_setup[1647]: DMA 4 CCR:   00002a81
DMA: stm32_dma_channel_setup[1648]: DMA 4 CNDTR: 00000067
DMA: stm32_dma_channel_setup[1649]: DMA 4 CPAR:  40018080
DMA: stm32_dma_channel_setup[1650]: DMA 4 CMAR:  20000c0c
***************************************************************
DMA 4 CNDTR: 00000067
ctlr 40020400 stream 4 chan 0 isr 00000000
STM32 SDIO DMA transfer RXOVERR(Received FIFIO overrun error)
STM32 SDIO : Transfer error! status = 0x00202020

或者如下:

DMA: stm32_dma_channel_setup[1639]: Bytes to transfer : 128 * 4, (count = 512)
DMA: stm32_dma_channel_setup[1641]: ctlr 40020400 stream 4 chan 0
DMA: stm32_dma_channel_setup[1642]: vector 60 stream->ccr 00002a81
DMA: stm32_dma_channel_setup[1645]: DMA   ISR:   00007000
DMA: stm32_dma_channel_setup[1647]: DMA 4 CCR:   00002a81
DMA: stm32_dma_channel_setup[1648]: DMA 4 CNDTR: 00000000
DMA: stm32_dma_channel_setup[1649]: DMA 4 CPAR:  40018080
DMA: stm32_dma_channel_setup[1650]: DMA 4 CMAR:  20000c0c
***************************************************************
DMA Callbacks, count = 0
DMA 4 CNDTR: 00000000
ctlr 40020400 stream 4 chan 0 isr 00000000
STM32 SDIO DMA transfer DATAEND(Data end (data counter, SDIO_DCOUNT, is zero))
STM32 SDIO DMA transfer(read) OK!

DMA焦点问题是,为什么出现RXOVERR错误(偶尔有DCRCFAIL错误)。

网上大多数的说法是,是SDIO的CLK频率过高的原因,但即使我把它从24MHz改成18MHz、从4bit传输改成1bit传输,问题依旧。后来找到一个十分有用的帖子:http://www.eefocus.com/bbs/article_244_189560.html

受该帖子的启发,从3方面着手尝试解决该问题:

  1. 改写DMA实现,原先是参考eCos STM32 SPI DMA以及自己写的I2S DMA的实现。改写成:在发送CMD17命令给SD卡以启动SD卡发送数据之前,先启动STM32 SDIO的DMA通道,对应现有驱动的stm32_dma_transaction_begin()函数,然后再发送CMD17命令,最后等待DMA传输完成和数据处理工作,分别对应stm32_dma_transaction()函数和stm32_dma_transaction_end()函数。
  2. 根据上述帖子的方法,开启SDIO的硬件流控功能。
  3. 降低SDIO的CLK频率,使用18MHz频率。

以上3方面,主要是避免SDIO FIFO出现溢出即避免RXOVERR错误。其中第1条,对于read,先启动SDIO的DMA通道传输数据,然后再启动SD卡的数据传输;对于write,则先启动SD卡准备接收数据,然后再启动SDIO的DMA通道传输数据,最后使能SDIO开始发送数据。经过上述改进后,DMA传输问题终于解决了,能正确读取到SD卡block 0数据了。但并没有解决mount失败的问题。

扇区大小问题

stm32_mmc_check_for_disk()函数中,会从SD卡的CSD数据中提取出SD卡的读写块长度,但发觉有异常,所以就修改了下,如下代码所示:

    // Assume for now that the block length is 512 bytes. This is
    // probably a safe assumption since we have just got the card
    // initialized out of idle state. If it ever proves to be a problem
    // the SET_BLOCK_LEN command can be used.
    // Nevertheless store the underlying block sizes
#if 0 // mmc_spi.c中的代码
    disk->mmc_read_block_length  = 0x01 << MMC_CSD_REGISTER_READ_BL_LEN(csd);
    disk->mmc_write_block_length = 0x01 << MMC_CSD_REGISTER_WRITE_BL_LEN(csd);
#else // 修改的代码
    disk->mmc_read_block_length = MMC_STM32_BLOCK_SIZE;
    disk->mmc_write_block_length= MMC_STM32_BLOCK_SIZE;
#endif

LBA格式与CHS格式

在stm32_mmc_check_for_disk()函数中,读取SD卡block 0数据(对应SD卡MBR)后,会把MBR中的分区信息提取出来,分别取出CHS格式(cylinder/head/sector)和LBA格式(Logic Block Address)数据。调试时,因为这些数据在mmc_stm32_disk_lookup()中有用到,所以我一度以为mount失败问题与此有关。实际上是没用的。

read_boot_record()函数的bug

分析完上述几个问题后,mount依然是失败。在fatfs_mount()函数中执行完lookup后,会执行fatfs_init(),之后调用read_boot_record()函数,即去读取SD卡DBR数据(引导扇区数据)。发现执行完这个函数后,就返回出错了。看来问题点应该就是在这里了。

很幸运的是,当我以read_boot_record()关键字搜索的时候,发现有人遇到了同样的问题。见文章:http://blog.csdn.net/rill_zhen/article/details/9365477

eCos默认是从SD卡的第0个sector读取boot record,但是并不是所有的SD卡的文件系统格式化信息都存在那里,所以,如果你的SD卡的文件系统信息没有存在第0个sector,eCos就无法挂载。

如果按照上述链接文章所给的方法进行修改,我觉得修改的并不是很完整。所以我的修改如下:

修改fatfs_supp.c源文件的read_boot_record()函数,在该函数局部变量定义后面添加如下代码:

    // Added by reille 2013.07.26
    len = 4;
    disk->start = (0 * 512);// Read MBR resided block 0;
    err = disk_read(disk, (void*)data, &len, 0x01c6);
    if (err != ENOERR) {
        return err;
    }
    disk->start_block_num = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
    disk->start = (disk->start_block_num * 512);
//  CYG_TRACE2(TDE, "Start: block %d, offset: 0x%08X)", start, (start * 512));

同时在结构体fatfs_disk_s(fatfs.h文件中)中添加两个成员变量的定义,如下:

    cyg_uint32 start;   // first sector byte address | Added by reille 2013.07.27
    cyg_uint32 start_block_num;

修改fatfs_supp.c源文件中的disk_write()和disk_read()两函数,如下:

// -------------------------------------------------------------------------
// disk_write()
// Writes data to disk.

static __inline__ int
disk_write(fatfs_disk_t *disk,
           void         *buf,
           cyg_uint32   *len,
           cyg_uint32    pos)
{
//  return cyg_blib_write(&disk->blib, buf, len, 0, pos);// Mask by reille 2013.07.27
    // Added by reille 2013.07.27
    //diag_printf("disk_write, (pos + disk->start) = %d\n", (pos + disk->start));
    return cyg_blib_write(&disk->blib, buf, len, 0, (pos + disk->start));
}

// -------------------------------------------------------------------------
// disk_read()
// Reads data from disk.

static __inline__ int
disk_read(fatfs_disk_t *disk,
          void         *buf,
          cyg_uint32   *len,
          cyg_uint32    pos)
{
//  return cyg_blib_read(&disk->blib, buf, len, 0, pos);// Mask by reille 2013.07.27
    // Added by reille 2013.07.27
    //diag_printf("disk_read, (pos + disk->start) = %d\n", (pos + disk->start));
    return cyg_blib_read(&disk->blib, buf, len, 0, (pos + disk->start));
}

经上述修改后,运行fatfs1.c测试程序或者我写的测试程序(见http://52ecos.net/thread-600-1-1.html),不仅可以mount成功,而且能成功打开SD卡目录和列出SD卡的目录文件信息。但是遗憾的是,当在SD卡中建立目录或者文件时,则会破坏SD卡中的文件系统格式数据,导致原SD卡的目录和文件都找不到了。这个问题我定位了很久,到最后才发现,原来是在DMA函数中范了个错误,详细情况请关注下节。

» 文章出处: reille博客—http://velep.com , 如果没有特别声明,文章均为reille博客原创作品
» 郑重声明: 原创作品未经允许不得转载,如需转载请联系reille#qq.com(#换成@)
分享到:

 Leave a Reply

(必须)

(我会替您保密的)(必须)

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

   
© 2012 velep.com | reille blog | 管理| 粤ICP备15065318号-2| 谷歌地图| 百度地图| Suffusion theme|Sayontan Sinha

无觅相关文章插件,快速提升流量