在上一节,介绍了eCos STM32 SD/MMC Card driver前面2个步骤的调试情况,SD卡可以mount成功并能成功打开目录和读取SD卡目录文件列表信息。但是在SD卡中创建新目录和文件时则会失败,严重的是还破坏了SD卡中文件系统数据,导致SD卡目录和文件数据的丢失。
这是怎么一回事呢?本节除了解决这个问题外,还修复了eCos Disk IO层和eCos FAT文件系统中存在的bug,以及更新了读取SD卡存储信息的相关代码。
读取SD卡信息
eCos带的fatfs测试程序fatfs1.c中,有读取SD卡信息(SD卡总大小、可用空间、块大小等信息)相关代码段,但是这段代码使用的一些宏和定义是旧的,所以是不可用的。具体可参考:http://www.sourceware.org/ml/ecos-discuss/2012-07/msg00006.html
根据eCos图形配置工具可知,读取SD卡信息对应的配置项是:CYGSEM_FILEIO_INFO_DISK_USAGE而不是CYGSEM_FILEIO_BLOCK_USAGE,如下图选中的配置项所示:
通过搜索CYGSEM_FILEIO_INFO_DISK_USAGE可知,读取SD卡信息的KEY是FS_INFO_DISK_USAGE而非FS_INFO_BLOCK_USAGE,所用的结构体是struct cyg_fs_disk_usage而非struct cyg_fs_block_usage。
修改fatfs.c中fatfs_getinfo()函数,如下代码所示:
#if defined(/*CYGSEM_FILEIO_BLOCK_USAGE*/CYGSEM_FILEIO_INFO_DISK_USAGE)// Modify by reille 2013.07.28 case /*FS_INFO_BLOCK_USAGE*/FS_INFO_DISK_USAGE: {// Modify by reille 2013.07.28 cyg_uint32 total_clusters; cyg_uint32 free_clusters; // Modify by reille 2013.07.28 // struct cyg_fs_block_usage *usage = (struct cyg_fs_block_usage *) buf; struct cyg_fs_disk_usage *usage = (struct cyg_fs_disk_usage *) buf; fatfs_disk_t *disk = (fatfs_disk_t *) mte->data; err = fatfs_get_disk_usage(disk, &total_clusters, &free_clusters); if (err) return err; usage->total_blocks = total_clusters; usage->free_blocks = free_clusters; usage->block_size = disk->cluster_size; break; } #endif
同样,fatfs1.c中相关代码也需要修改过来,在此就不多说了。
eCos Disk IO层的bug
在调试SD卡驱动时发现eCos Disk IO层存在bug。该bug正常情况是不会触发的,但一旦SD卡驱动返回出错时则会触发,并导致整个eCos Disk的崩溃。该bug主要存在于disk_bread()和disk_bwrite()两函数中。
以disk_bread()函数来说明。
disk_bread()函数开头会去检测SD卡对应的controller是否busy?如果busy则一直等待,否则一旦取得执行权,立即置busy信号,如下代码:
cyg_drv_mutex_lock( &ctlr->lock ); while( ctlr->busy ) cyg_drv_cond_wait( &ctlr->queue ); if (info->connected && chan->valid) { ctlr->busy = true; if (NULL != chan->partition) {
再往下面看,调用SD卡驱动read()方法去读取SD block数据:
cyg_drv_dsr_lock(); res = (funs->read)(chan, (void*)bbuf, tfr, pos); if( res == -EWOULDBLOCK ) { // If the driver replys EWOULDBLOCK, then the transfer is // being handled asynchronously and when it is finished it // will call disk_transfer_done(). This will wake us up here // to continue. while( ctlr->result == -EWOULDBLOCK ) cyg_drv_cond_wait( &ctlr->async ); res = ctlr->result; } cyg_drv_dsr_unlock(); if (ENOERR != res) { goto done; }
是否注意:如果res返回出错,即res值不等于ENOERR时,则直接goto done了,而在这里呢,没有清除SD卡controller的busy标志。这种情况下,一旦出现SD卡底层驱动read返回错误情况,则下次read时会一直等待busy标志,导致死等。disk_bwrite()函数亦是如此。
这就是eCos Disk IO层存在的bug。解决方法不难,当res返回出错且goto之前清除busy标志。
SD卡创建目录和文件失败问题
因为SD卡可以mount成功,可以读取SD卡信息、目录和文件列表信息,最重要的是用WinHex工具(下载地址:http://download.csdn.net/detail/rill_zhen/5776013)查看写block数据是正确的,所以一直把这个问题定位在eCos fatfs中,没有想过会是SD卡驱动问题的原因。所以这个问题排查了很久。
后面添加了很多打印信息加以分析,FAT信息、FAT分配表、FAT根目录、读写block数据都打印出来,并且根据FAT文件系统格式分析当创建新文件时,所要更新哪些分配表、根目录数据、以及大概写入的数据块等。
这样分析后发现,umount之前,所有的数据行为都是正确的。umount之后,会把数据写入到相应的block中(这是因为eCos的FATFS采用了类似linux中的块缓存机制,主要用于提高块设备数据读写性能以及降低对块设备的频繁读写,进而延长块设备寿命),但是当更新SD卡FAT的根目录数据时,发现写入的数据实际上是FAT数据区中的数据。
进一步排查发现,原来是stm32_dma_transaction_begin()函数中disk->is_tx标志使用的问题(注意下面代码中文注释部分)。
// Perform transfer via the bounce buffers. if (disk->setup->bbuf_size != 0) { // If the requested transfer is too large for the bounce buffer we assert // in debug builds and truncate in production builds. if (count > disk->setup->bbuf_size) { CYG_ASSERT(false, "STM32 SDIO : Transfer exceeds bounce buffer size."); count = disk->setup->bbuf_size; } local_data = disk->setup->bbuf; if (is_tx) {// 这里原来是:if (disk->is_tx),而disk->is_tx标志的更新放在了后面,导致write时,数据源错误 memcpy (local_data, data, count); } } else { local_data = data; } // Set runtime data disk->is_tx = is_tx;
FAT32兼容问题
上一节,谈到了mount失败是因为fatfs_supp.c中read_boot_record()函数读取SD卡MBR失败引起的,通过修改fatfs_supp.c中read_boot_record()、disk_write()和disk_read()以及在struct fatfs_disk_s结构体添加两成员变量才得以解决。
实际上,这份修改是不完全的。我们知道,在fat32中,读写数据时是以cluster为单位的,所以,当SD卡是FAT32时,还是会出现mount失败。解决办法是修改cluster_to_block_pos()函数,在该函数末尾添加以下一行代码,也就是把block加上SD卡DBR前的隐藏扇区数:
*block += disk->start_block_num;
读取SCR出错
在调试SD卡驱动的时候,会出现读取SCR出错的情况。没有找到原因,网上搜索都说是要加个延时。但即使加了延时,我发现还是会有很小概率出现。如果有网友遇到过,恳请赐教下。
总结
一路下来发现,SD卡不仅本身驱动稍显复杂,而且还涉及eCos Disk驱动体系、FAT文件系统、块缓存等多方面内容,所以像SD卡这种块设备还是有一定难度的。在这里,最重要的是:有难度不要害怕,而要迎难而上。
整个SD卡章节,如果带上SD卡和FAT文件系统知识介绍,多达6个章节。本身的目的是想采纳我同事的建议,叙述时既要有实践经验,更要有理论知识做铺垫。此外,本次同样花了大量篇幅来介绍我的调试过程,最主要是展现我的一个调试过程、调试方法、调试思路。
水平有限,如有更好建议,请留言,本人将积极采纳!
整个代码下载地址:http://52ecos.net/thread-600-1-1.html