最近移植了fatfs到stm32,昨晚写进了第一个txt文件,然后就睡觉了,以为移植差不多了,今晚试了一下,发现不好用了,
res = f_open(&file, "text.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE); 但是复位两次就可以用了,跟踪发现的确是正好两次;
查网上有人说把MISO改为上拉,我试了下没有效果,决定自己跟踪找错误;
跟踪发现 f_open res返回1,对应FR_DISK_ERR 底层驱动错误
继续跟踪发现是f_open调用的chk_mounted的这一句fmt = check_fs(fs, bsect);检查文件系统错误;
继续跟踪发现 check_fs里的第一句if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK) return 3;直接错误返回3;
继续跟踪disk_read发现是读扇区函数res = MSD_ReadSingleBlock(sector, buff); 返回res=1
继续跟踪MSD_ReadSingleBlock发现是
.......................................
............................
.................................
............................................
就是读数据时候错误返回1;
而当我想跟踪_read_buffer的时候发现只要跟踪进去,就不会返回错误1,正确的返回0,很是头大,然后仔细想一想,在 r1 = _read_buffer这里设置断点 跟踪就没错误,应该是和这里的时序有关,因为调试和实际运行直接时间的差别;
然后看了下 _send_command(CMD17, sector, 0)时序图
断点设置的是画红圈的那里,说明延长这个时间,问题就会解决;
_read_buffer里是通过以下一段延时的
.......................................
............................
res = f_open(&file, "text.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE); 但是复位两次就可以用了,跟踪发现的确是正好两次;
查网上有人说把MISO改为上拉,我试了下没有效果,决定自己跟踪找错误;
跟踪发现 f_open res返回1,对应FR_DISK_ERR 底层驱动错误
继续跟踪发现是f_open调用的chk_mounted的这一句fmt = check_fs(fs, bsect);检查文件系统错误;
继续跟踪发现 check_fs里的第一句if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK) return 3;直接错误返回3;
继续跟踪disk_read发现是读扇区函数res = MSD_ReadSingleBlock(sector, buff); 返回res=1
继续跟踪MSD_ReadSingleBlock发现是
.......................................
............................
r1 = _send_command(CMD17, sector, 0);//独一块命令
if(r1 != 0x00)//r1!=0x00,则接收命令错误
{
return 1;//返回写块错误
}
// Start read and return the result
r1 = _read_buffer(buffer, MSD_BLOCKSIZE, RELEASE);//读MSD_BLOCKSIZE(512)个字节数据到buffer,读完后release
.................................
............................................
就是读数据时候错误返回1;
而当我想跟踪_read_buffer的时候发现只要跟踪进去,就不会返回错误1,正确的返回0,很是头大,然后仔细想一想,在 r1 = _read_buffer这里设置断点 跟踪就没错误,应该是和这里的时序有关,因为调试和实际运行直接时间的差别;
然后看了下 _send_command(CMD17, sector, 0)时序图
![](http://www.openedv.com/upload/2012/11/13/e8caa914587b5c626c7d36fa9cb0be28_459.jpg)
断点设置的是画红圈的那里,说明延长这个时间,问题就会解决;
_read_buffer里是通过以下一段延时的
.......................................
............................
for(retry=0; retry<2000; retry++)
{
r1 = _spi_read_write(DUMMY_BYTE);
if(r1 == 0xFE)//成功
{
retry = 0;//成功标志
break;
}
}
// Timeout return超时
if(retry == 2000)
{
_card_disable();//关片选
return 1;//返回超时错误
}
.......................................
............................
程序为retry自加到2000就返回r1超时错误,设置断点在 return 1;//返回超时错误 这一句,也的确会执行到这里;
于是把程序改为for(retry=0; retry<4000; retry++),错误解决了,后来跟踪发现这里复位次retry都会自加到0x855(2133),正好超出2000!
整个错误按理说应该很好查,但是由于是复位前两次,而且错误和时序有关,设置断点的位置会影响错误存在与否,所以每一次调试都要断电,上电,打开调试,设置断点,所以跟踪到这个错误非常麻烦,也用了我一晚上的时间,写下这个,希望其他人有类似的错误可以参考下。
还有不明白的地方是:
1.为什么只在复位前两次出现这个问题?
2. chk_mounted里两次调用 check_fs,也就是两次调用 disk_read,同样就是两次调用_read_buffer,跟踪发现总是第二次才出现这个错误,第一次和第二次唯一的区别是第一次读的是物理0扇区,第二次读的是物理8192扇区(我的8G SDHC,逻辑0扇区在物理扇区的8192扇区),是不是因为扇区地址大,寻址时间长了?那同样读更大的扇区会不会耗时更长?
.......................................
............................
程序为retry自加到2000就返回r1超时错误,设置断点在 return 1;//返回超时错误 这一句,也的确会执行到这里;
于是把程序改为for(retry=0; retry<4000; retry++),错误解决了,后来跟踪发现这里复位次retry都会自加到0x855(2133),正好超出2000!
整个错误按理说应该很好查,但是由于是复位前两次,而且错误和时序有关,设置断点的位置会影响错误存在与否,所以每一次调试都要断电,上电,打开调试,设置断点,所以跟踪到这个错误非常麻烦,也用了我一晚上的时间,写下这个,希望其他人有类似的错误可以参考下。
还有不明白的地方是:
1.为什么只在复位前两次出现这个问题?
2. chk_mounted里两次调用 check_fs,也就是两次调用 disk_read,同样就是两次调用_read_buffer,跟踪发现总是第二次才出现这个错误,第一次和第二次唯一的区别是第一次读的是物理0扇区,第二次读的是物理8192扇区(我的8G SDHC,逻辑0扇区在物理扇区的8192扇区),是不是因为扇区地址大,寻址时间长了?那同样读更大的扇区会不会耗时更长?