本帖最后由 正点原子 于 2013-4-5 22:58 编辑
//读扇区 //drv:磁盘编号0~9 //*buff:数据接收缓冲首地址 //sector:扇区地址 //count:需要读取的扇区数 DRESULT disk_read ( BYTE drv, /* Physical drive nmuber (0..) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address (LBA) */ BYTE count /* Number of sectors to read (1..255) */ ) { u8 res=0; if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误 switch(drv) { case SD_CARD://SD卡 res=SD_ReadDisk(buff,sector,count); if(res) //STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句, { //可能导致SPI读写异常 SD_SPI_SpeedLow(); SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟 SD_SPI_SpeedHigh(); } break; case EX_FLASH://外部flash for(;count>0;count--) { SPI_Flash_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE); sector++; buff+=FLASH_SECTOR_SIZE; } res=0; break; default: res=1; } //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值 if(res==0x00)return RES_OK; else return RES_ERROR; } //写扇区 //drv:磁盘编号0~9 //*buff:发送数据首地址 //sector:扇区地址 //count:需要写入的扇区数 #if _READONLY == 0 DRESULT disk_write ( BYTE drv, /* Physical drive nmuber (0..) */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address (LBA) */ BYTE count /* Number of sectors to write (1..255) */ ) { u8 res=0; if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误 switch(drv) { case SD_CARD://SD卡 res=SD_WriteDisk((u8*)buff,sector,count); break; case EX_FLASH://外部flash for(;count>0;count--) { SPI_Flash_Write((u8*)buff,sector*FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE); sector++; buff+=FLASH_SECTOR_SIZE; } res=0; break; default: res=1; } //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值 if(res == 0x00)return RES_OK; else return RES_ERROR; } #endif /* _READONLY */ //其他表参数的获得 //drv:磁盘编号0~9 //ctrl:控制代码 //*buff:发送/接收缓冲区指针 DRESULT disk_ioctl ( BYTE drv, /* Physical drive nmuber (0..) */ BYTE ctrl, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; if(drv==SD_CARD)//SD卡 { switch(ctrl) { case CTRL_SYNC: SD_CS=0; if(SD_WaitReady()==0)res = RES_OK; else res = RES_ERROR; SD_CS=1; break; case GET_SECTOR_SIZE: *(WORD*)buff = 512; res = RES_OK; break; case GET_BLOCK_SIZE: *(WORD*)buff = 8; res = RES_OK; break; case GET_SECTOR_COUNT: *(DWORD*)buff = SD_GetSectorCount();res = RES_OK; break; default: res = RES_PARERR; break; } }else if(drv==EX_FLASH) //外部FLASH { switch(ctrl) { case CTRL_SYNC: res = RES_OK; break; case GET_SECTOR_SIZE: *(WORD*)buff = FLASH_SECTOR_SIZE; res = RES_OK; break; case GET_BLOCK_SIZE: *(WORD*)buff = FLASH_BLOCK_SIZE; res = RES_OK; break; case GET_SECTOR_COUNT: *(DWORD*)buff = FLASH_SECTOR_COUNT; res = RES_OK; break; default: res = RES_PARERR; break; } }else res=RES_ERROR;//其他的不支持 return res; } //获得时间 //User defined function to give a current time to fatfs module */ //31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ //15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ DWORD get_fattime (void) { return 0; } //动态分配内存 void *ff_memalloc (UINT size) { return (void*)mymalloc(SRAMIN,size); } //释放内存 void ff_memfree (void* mf) { myfree(SRAMIN,mf); }
该函数实现了我们45.1节提到的6个函数,同时因为在ffconf.h里面设置对长文件名的支持为方法3,所以必须实现ff_memalloc和ff_memfree这两个函数。本章,我们用FATFS管理了2个磁盘:SD卡和SPI FLASH。SD卡比较好说,但是SPI FLASH,因为其扇区是4K字节大小,我们为了方便设计,强制将其扇区定义为512字节,这样带来的好处就是设计使用相对简单,坏处就是擦除次数大增,所以不要随便往SPI FLASH里面写数据,非必要最好别写,如果频繁写的话,很容易将SPI FLASH写坏。 保存diskio.c,然后打开ffconf.h,修改相关配置,并保存,此部分就不贴代码了,请大家参考光盘源码。 前面提到,我们在FATFS文件夹下还新建了一个exfuns的文件夹,该文件夹用于保存一些FATFS一些针对FATFS的扩展代码,本章,我们编写了4个文件,分别是:exfuns.c、exfuns.h、fattester.c和fattester.h。其中exfuns.c主要定义了一些全局变量,方便FATFS的使用,同时实现了磁盘容量获取等函数。而fattester.c文件则主要是为了测试FATFS用,因为FATFS的很多函数无法直接通过USMART调用,所以我们在fattester.c里面对这些函数进行了一次再封装,使得可以通过USMART调用。这几个文件的代码,我们就不贴出来了,请大家参考光盘源码,我们将exfuns.c和fattester.c加入FATFS组下,同时将exfuns文件夹加入头文件包含路径。 然后,我们打开test.c,修改main函数如下: int main(void) { u32 total,free; u8 t=0; Stm32_Clock_Init(9); //系统时钟设置 delay_init(72); //延时初始化 uart_init(72,9600); //串口1初始化 exfuns_init(); //为fatfs相关变量申请内存 LCD_Init(); //初始化液晶 LED_Init(); //LED初始化 usmart_dev.init(72); mem_init(SRAMIN); //初始化内部内存池 POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"WarShip STM32"); LCD_ShowString(60,70,200,16,16,"FATFS TEST"); LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(60,110,200,16,16,"Use USMART for test"); LCD_ShowString(60,130,200,16,16,"2012/9/18"); while(SD_Initialize()) //检测SD卡 { LCD_ShowString(60,150,200,16,16,"SD Card Error!"); delay_ms(200); LCD_Fill(60,150,240,150+16,WHITE);//清除显示 delay_ms(200); LED0=!LED0;//DS0闪烁 } exfuns_init(); //为fatfs相关变量申请内存 f_mount(0,fs[0]); //挂载SD卡 f_mount(1,fs[1]); //挂载FLASH. while(exf_getfree("0",&total,&free)) //得到SD卡的总容量和剩余容量 { LCD_ShowString(60,150,200,16,16,"Fatfs Error!"); delay_ms(200); LCD_Fill(60,150,240,150+16,WHITE);//清除显示 delay_ms(200); LED0=!LED0;//DS0闪烁 } POINT_COLOR=BLUE;//设置字体为蓝色 LCD_ShowString(60,150,200,16,16,"FATFS OK!"); LCD_ShowString(60,170,200,16,16,"SD Total Size: MB"); LCD_ShowString(60,190,200,16,16,"SD Free Size: MB"); LCD_ShowNum(172,170,total>>10,5,16); //显示SD卡总容量 MB LCD_ShowNum(172,190,free>>10,5,16); //显示SD卡剩余容量 MB while(1) { t++; delay_ms(200); LED0=!LED0; } } 在main函数里面,我们为SD卡和FLASH都注册了工作区(挂载),在初始化SD卡并显示其容量信息后,进入死循环,等待USMART测试。 最后,我们在usmart_config.c里面的usmart_nametab数组添加如下内容: (void*)mf_mount,"u8 mf_mount(u8 drv)", (void*)mf_open,"u8 mf_open(u8*path,u8 mode)", (void*)mf_close,"u8 mf_close(void)", (void*)mf_read,"u8 mf_read(u16 len)", (void*)mf_write,"u8 mf_write(u8*dat,u16 len)", (void*)mf_opendir,"u8 mf_opendir(u8* path)", (void*)mf_readdir,"u8 mf_readdir(void)", (void*)mf_scan_files,"u8 mf_scan_files(u8 * path)", (void*)mf_showfree,"u32 mf_showfree(u8 *drv)", (void*)mf_lseek,"u8 mf_lseek(u32 offset)", (void*)mf_tell,"u32 mf_tell(void)", (void*)mf_size,"u32 mf_size(void)", (void*)mf_mkdir,"u8 mf_mkdir(u8*pname)", (void*)mf_fmkfs,"u8 mf_fmkfs(u8 drv,u8 mode,u16 au)", (void*)mf_unlink,"u8 mf_unlink(u8 *pname)", (void*)mf_rename,"u8 mf_rename(u8 *oldname,u8* newname)", (void*)mf_gets,"void mf_gets(u16 size)", (void*)mf_putc,"u8 mf_putc(u8 c)", (void*)mf_puts,"u8 mf_puts(u8*c)", 这些函数均是在fattester.c里面实现,通过调用这些函数,即可实现对FATFS对应API函数的测试。 至此,软件设计部分就结束了。 45.4 下载验证 在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD显示如图45.4.1所示的内容(默认SD卡已经接上了):
图45.4.1 程序运行效果图
打开串口调试助手,我们就可以串口调用前面添加的各种FATFS测试函数了,比如我们输入mf_scan_files("0:"),即可扫描SD卡根目录的所有文件,如图45.4.2所示:
图45.4.2 扫描SD卡根目录所有文件
其他函数的测试,用类似的办法即可实现。注意这里0代表SD卡,1代表SPI FLASH。另外,提醒大家,mf_unlink函数,在删除文件夹的时候,必须保证文件夹是空的,才可以正常删除,否则不能删除。 |