1、准备材料 正点原子stm32f407探索者开发板V2.4
STM32CubeMX软件(Version 6.10.0 )
keil µVision5 IDE(MDK-Arm )
ST-LINK/V2驱动
野火DAP仿真器
XCOM V2.6串口助手
2、实验目标 使用STM32CubeMX软件配置STM32F407开发板使用FatFs中间件通过SPI通信协议对W25Q128芯片进行读写等操作
3、实验流程 3.0、前提知识 关于STM32F407使用SPI通信协议对W25Q128 FLASH芯片读写等操作涉及的SPI通信协议及W25Q128芯片相关知识请读者阅读STM32CubeMX教程20 SPI - W25Q128驱动 实验,本实验不再过多介绍
对于容量较小的存储设备可以使用底层库函数直接根据内存地址对设备来进行读写,但是一旦存储设备容量稍大,直接根据地址对设备来进行读写将变得比较困难
这个时候使用文件系统来对存储设备进行各种操作将比较方便,FatFs是适用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块,它与磁盘I/O层完全分离,可以独立于硬件平台,因此非常方便移植
STM32CubeMX Version 6.10.0 中在中间件和软件包 Middleware and Software Packs 中集成了R0.12c 版本的FatFs文件系统模块,这个中间件支持Extemal SRAM、SD Card、USB Disk和User-defined四种模式
其中外部SRAM需要启用FSMC连接SRAM功能后才可以勾选,SD卡需要启用SDIO功能之后才可以勾选,USB Disk需要配置USB为大容量存储主机类功能后才可以勾选,User-defined则任何时候都可以勾选,将FatFs配置在User-defined模式下就可以利用FatFs使用除上述提到的三种存储之外的设备,比如SPI FLASH等
我们可以通过其官网FatFs历史版本记录 找到 R0.12c 版本的FatFs源码,将其下载下来之后观察其源码目录结构如下图所示
其中ff.c/ff.h为FatF模块的源码;fconf.h文件为模块配置文件,可以通过宏定义来选择哪些功能开启,哪些功能关闭;integer.h文件为变量类型重命名文件,主要是为了兼容不同的变量类型命名;option文件夹中的文件为unicode编码文件和操作系统相关函数的文件;上面提到的这些文件用户一般无需修改
如果读者希望手动移植FatFs到自己的嵌入式系统上,则应重点关注源码中diskio.c/diskio.h两个文件 ,这两个文件中需要根据用户使用的RAM、MMC和USB这几个不同的内存类型来实现以下几个底层函数,函数如下列表所示,完成之后就可以直接通过FatFs提供的上层应用接口(eg:f_open())来对底层的存储设备进行操作
存储设备状态读取函数disk_status()
存储设备初始化函数disk_initialize()
存储设备读函数disk_read()
存储设备写函数disk_write()
存储设备IO控制操作函数disk_ioctl()
但是如果要使用 STM32CubeMX 配置的话就不需要自己下载和移植源码 ,通过配置 STM32CubeMX 的 FatFs ,在生成的工程代码中就已经将 FatFs 的框架准备好,用户只需在生成的 user_diskio.c 文件中添加底层驱动IO函数即可(仅仅对于 User-defined 模式需要自己添加,其他的模式底层代码会自动生成),具体请阅读本实验”3.2.3、添加其他必要代码“小节
在 FatFs 中,大多数的API都拥有一个名为 FRESULT 的结构体返回值,其包含了20个枚举对象,由于该返回值对于查找错误有很大帮助,因此笔者在这里列出来所有返回值并做了简单解释,具体如下源代码所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 typedef enum { FR_OK = 0 , FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_DENIED, FR_EXIST, FR_INVALID_OBJECT, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_MKFS_ABORTED, FR_TIMEOUT, FR_LOCKED, FR_NOT_ENOUGH_CORE, FR_TOO_MANY_OPEN_FILES, FR_INVALID_PARAMETER } FRESULT;
下图所示为带有 FatFs 模块的嵌入式系统的典型但非特定配置的依赖关系图,其中用户只需重点关注和实现”Low level disk I/O layer”,实现之后在实际应用中只需使用 “User Application”中提供的上层应用接口即可 (注释1)
3.1、CubeMX相关配置 3.1.0、工程基本配置 打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示
开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具体如下图所示
详细工程建立内容读者可以阅读“STM32CubeMX教程1 工程建立 ”
3.1.1、时钟树配置 系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示
3.1.2、外设参数配置 本实验需要初始化开发板上WK_UP、KEY2、KEY1和KEY0用户按键,具体配置步骤请阅读“STM32CubeMX教程3 GPIO输入 - 按键响应 ”
本实验需要初始化USART1作为输出信息渠道,具体配置步骤请阅读“STM32CubeMX教程9 USART/UART 异步通信 ”
本实验需要以STM32CubeMX教程20 SPI - W25Q128驱动 实验为基础,需要读者能够通过CubeMX软件配置STM32F407的SPI实现正常读写W25Q128芯片的功能,然后接下来只需要增加本实验需要的中间件FatFs即可
在Pinout & Configuration页面左边的功能分类栏中单击 Middleware and SoftwarePacks/FATFS,然后在右边的Mode下勾选 User-defined (目前只有该参数可以勾选) ,在下方Configuration/Set Defines对FatFs的功能进行配置,这个页面所有参数对应FatFs源码ffconf.h中的宏定义
这里我们将 CODE_PAGE(Code page on target) 参数修改为 Simplified Chinese (DBCS) ,然后将 MAX_SS (Maximum Sector Size) 参数修改为W25Q128芯片的扇区大小4096字节,其他所有参数不做修改 ,具体如下图所示
笔者将Set Defines页面的所有参数列为了一个表格,方便做简单介绍,具体如下图所示
3.1.3、外设中断配置 本实验无需配置任何中断
3.2、生成代码 3.2.0、配置Project Manager页面 单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,接着在链接设置中将最小栈大小修改为0x2000(8KB),之前所有实验该参数都为默认的0x0400(1KB),这是因为其他实验不需要占用太多的栈空间,但是本实验需要比较大的栈空间,不增加可能会导致FatFs读写文件失败卡死或者导致MCU复位的情况发生,读者可根据自己的情况自行设置栈大小
然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of ‘c/h’ files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示
详细Project Manager配置内容读者可以阅读“STM32CubeMX教程1 工程建立 ”实验3.4.3小节
3.2.1、外设初始化调用流程 关于SPI的初始化函数调用流程请读者阅读STM32CubeMX教程20 SPI - W25Q128驱动 实验的”3.2.1、外设初始化调用流程“小节,在此不再赘述
重点来看看FatFs中间件是如何被初始化并与W25Q128芯片底层操作联系在一起的 ,首先在CubeMX中勾选启用FatFs中间件之后,会在生成的工程代码中增加MX_FATFS_Init()初始化函数,在该函数中只调用了FATFS_LinkDriver()一个函数
这个FATFS_LinkDriver()函数将一个名为 xxx_Driver(根据所选的存储设备不同,生成的该变量名称也会改变,比如User_Driver,SD_Driver等)的 Diskio_drvTypeDef 类结构体链接到了FatFs管理的驱动器列表中,并将卷路径赋值为”0:/“
那么 xxx_Driver 是什么,为什么要将这个Diskio_drvTypeDef 类结构体的变量链接给FatFs管理呢?
跳转到 xxx_Driver 的定义处,我们发现该结构体变量中保存了五个函数指针,刚刚好就是我们需要实现的对存储设备进行底层读写等操作的函数,具体xxx_Driver定义如下述代码所示
1 2 3 4 5 6 7 8 Diskio_drvTypeDef USER_Driver = { USER_initialize, USER_status, USER_read, USER_write, USER_ioctl, };
至此我们知道了FatFs初始化就是将用户重新实现的与存储设备底层进行读写等操作的函数链接到FatFs管理的驱动器列表中,将这些底层函数交给FatFs管理 ,用户直接使用FatFs提供的上层API函数来操作即可,对于为什么可以这样需要分析FatFs源码,本文就不涉及了
3.2.2、外设中断调用流程 本实验无配置任何中断
3.2.3、添加其他必要代码 打开整个工程之后观察其文件结构目录,在CubeMX中启用FatFs之后在生成的工程代码目录中会增加FatFs源码文件夹(该文件夹中文件无需用户修改),同时增加App和Target两个文件夹,在App文件夹中的fatfs.c文件需要用户实现获取RTC时间的函数①get_fattime(),在App文件夹中的user_diskio.c中需要用户实现②USER_initialize()、③USER_status()、④USER_read()、⑤USER_write()和⑥USER_ioctl()共计六个函数 ,其中USER_initialize(),USER_status(),USER_read()三个函数必须实现,其他函数按需实现,其文件结构目录如下图所示
对于配置为User-defined模式的FatFs来说,上面App文件夹和Target文件夹中的内容均需要用户自己实现,因为CubeMX并不知道用户想要使用的存储设备,所以也无法自动生成底层读写的IO驱动函数,但是对于Extemal SRAM、SD Card和USB Disk这三种固定类型的存储,则无需用户在App文件夹和Target文件夹中重新实现上面提到的一共六个函数,CubeMX生成的工程代码中会自动实现
接下来我们来实现上面提到的六个函数 ,注意FatFs获取RTC时间需要开启STM32F407的RTC功能,关于RTC的具体使用方法,读者可以阅读STM32CubeMX教程10 RTC 实时时钟 - 周期唤醒、闹钟AB事件和备份寄存器 实验,如果不需要可以直接将函数体内容注释
对于Flash_ReadID()、Flash_ReadBytes()和Flash_WriteSector()三个函数是在STM32CubeMX教程20 SPI - W25Q128驱动 实验中实现的,请读者自行查阅,重新实现的六个函数源代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 #include "rtc.h" DWORD get_fattime (void ) { RTC_TimeTypeDef sTime; RTC_DateTypeDef sDate; if (HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK) { HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN); WORD date=(2000 +sDate.Year-1980 )<<9 ; date = date |(sDate.Month<<5 ) |sDate.Date; WORD time=sTime.Hours<<11 ; time = time | (sTime.Minutes<<5 ) | (sTime.Seconds>1 ); DWORD dt=(date<<16 ) | time; return dt; } else return 0 ; } DSTATUS USER_initialize ( BYTE pdrv ) { Stat = STA_NOINIT; Stat = USER_status(pdrv); return Stat; } DSTATUS USER_status ( BYTE pdrv ) { Stat = STA_NOINIT; if (Flash_ReadID() != 0 ) Stat &= ~STA_NOINIT; return Stat; } DRESULT USER_read ( BYTE pdrv, BYTE *buff, DWORD sector, UINT count ) { uint32_t globalAddr = sector << 12 ; uint16_t byteCount = count << 12 ; Flash_ReadBytes(globalAddr, (uint8_t *)buff, byteCount); return RES_OK; } DRESULT USER_write ( BYTE pdrv, const BYTE *buff, DWORD sector, UINT count ) { uint32_t globalAddr = sector<<12 ; uint16_t byteCount = count<<12 ; Flash_WriteSector(globalAddr, (uint8_t *)buff, byteCount); return RES_OK; } DRESULT USER_ioctl ( BYTE pdrv, BYTE cmd, void *buff ) { DRESULT res = RES_OK; switch (cmd) { case CTRL_SYNC: break ; case GET_SECTOR_COUNT: *(DWORD *)buff = FLASH_SECTOR_COUNT; break ; case GET_SECTOR_SIZE: *(DWORD *)buff = FLASH_SECTOR_SIZE; break ; case GET_BLOCK_SIZE: *(DWORD *)buff = 16 ; break ; default : res = RES_ERROR; } return res; }
然后增加使用FatFs库中API进行文件操作的函数,包括挂载文件系统、显示SD卡信息、读/写TXT文件、获取文件信息、扫描文件列表和删除文件等函数,笔者将其封装在了file_operate.c/file_operate.h文件中,具体的源代码如下所示
file_operate.c文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 #include "file_operate.h" BYTE workBuffer[4 *User_Sector];void Mount_FatFs (void ) { FRESULT retUSER = f_mount(&User_FatFs, User_SDPath, 1 ); if (retUSER != FR_OK) { if (retUSER == FR_NO_FILESYSTEM) { printf ("\r\n没有文件系统,开始格式化\r\n" ); retUSER = f_mkfs(User_SDPath, FM_FAT32, 0 , workBuffer, 4 *User_Sector); if (retUSER != FR_OK) { printf ("格式化失败,错误代码 = %d\r\n" , retUSER); } else { printf ("格式化成功,开始重新挂载\r\n" ); retUSER = f_mount(&User_FatFs, User_SDPath, 1 ); if (retUSER != FR_OK) { printf ("发生错误,错误代码 = %d\r\n" , retUSER); } else { printf ("*** 文件系统挂载成功 ***\r\n" ); } } } else { printf ("发生其他错误,错误代码 = %d\r\n" , retUSER); } } else { printf ("文件系统挂载成功\r\n" ); } }void FatFs_GetDiskInfo (void ) { FATFS *fs; DWORD fre_clust; FRESULT res = f_getfree("0:" , &fre_clust, &fs); if (res != FR_OK) { printf ("f_getfree() error\r\n" ); return ; } printf ("\r\n*** FAT disk info ***\r\n" ); DWORD tot_sect = (fs->n_fatent - 2 ) * fs->csize; DWORD fre_sect = fre_clust * fs->csize; #if _MAX_SS == _MIN_SS DWORD freespace= (fre_sect>>11 ); DWORD totalSpace= (tot_sect>>11 ); #else DWORD freespace= (fre_sect*fs->ssize)>>10 ; DWORD totalSpace= (tot_sect*fs->ssize)>>10 ; #endif printf ("FAT type = %d\r\n" ,fs->fs_type); printf ("[1=FAT12,2=FAT16,3=FAT32,4=exFAT]\r\n" ); printf ("Sector size(bytes) = " ); #if _MAX_SS == _MIN_SS printf ("%d\r\n" , _MIN_SS);#else printf ("%d\r\n" , fs->ssize);#endif printf ("Cluster size(sectors) = %d\r\n" , fs->csize); printf ("Total cluster count = %ld\r\n" , fs->n_fatent-2 ); printf ("Total sector count = %ld\r\n" , tot_sect); #if _MAX_SS == _MIN_SS printf ("Total space(MB) = %ld\r\n" , totalSpace);#else printf ("Total space(KB) = %ld\r\n" , totalSpace);#endif printf ("Free cluster count = %ld\r\n" ,fre_clust); printf ("Free sector count = %ld\r\n" , fre_sect); #if _MAX_SS == _MIN_SS printf ("Free space(MB) = %ld\r\n" , freespace);#else printf ("Free space(KB) = %ld\r\n" , freespace);#endif printf ("Get FAT disk info OK\r\n" ); }void FatFs_WriteTXTFile (TCHAR *filename,uint16_t year, uint8_t month, uint8_t day) { FIL file; printf ("\r\n*** Creating TXT file: %s ***\r\n" , filename); FRESULT res = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE); if (res == FR_OK) { TCHAR str[]="Line1: Hello, FatFs***\n" ; f_puts(str, &file); printf ("Write file OK: %s\r\n" , filename); } else { printf ("Open file error,error code: %d\r\n" , res); } f_close(&file); }void FatFs_ReadTXTFile (TCHAR *filename) { printf ("\r\n*** Reading TXT file: %s ***\r\n" , filename); FIL file; FRESULT res = f_open(&file, filename, FA_READ); if (res == FR_OK) { TCHAR str[100 ]; while (!f_eof(&file)) { f_gets(str,100 , &file); printf ("%s" , str); } printf ("\r\n" ); } else if (res == FR_NO_FILE) printf ("File does not exist\r\n" ); else printf ("f_open() error,error code: %d\r\n" , res); f_close(&file); }void FatFs_ScanDir (const TCHAR* PathName) { DIR dir; FILINFO fno; FRESULT res = f_opendir(&dir, PathName); if (res != FR_OK) { f_closedir(&dir); printf ("\r\nf_opendir() error,error code: %d\r\n" , res); return ; } printf ("\r\n*** All entries in dir: %s ***\r\n" , PathName); while (1 ) { res = f_readdir(&dir, &fno); if (res != FR_OK || fno.fname[0 ] == 0 ) break ; if (fno.fattrib & AM_DIR) { printf ("DIR: %s\r\n" , fno.fname); } else { printf ("FILE: %s\r\n" ,fno.fname); } } printf ("Scan dir OK\r\n" ); f_closedir(&dir); }void FatFs_GetFileInfo (TCHAR *filename) { printf ("\r\n*** File info of: %s ***\r\n" , filename); FILINFO fno; FRESULT fr = f_stat(filename, &fno); if (fr == FR_OK) { printf ("File size(bytes) = %ld\r\n" , fno.fsize); printf ("File attribute = 0x%x\r\n" , fno.fattrib); printf ("File Name = %s\r\n" , fno.fname); FatFs_PrintfFileDate(fno.fdate, fno.ftime); } else if (fr == FR_NO_FILE) printf ("File does not exist\r\n" ); else printf ("f_stat() error,error code: %d\r\n" , fr); }void FatFs_DeleteFile (TCHAR *filename) { printf ("\r\n*** Delete File: %s ***\r\n" , filename); FIL file; FRESULT res = f_open(&file, filename, FA_OPEN_EXISTING); if (res == FR_OK) { f_close(&file); printf ("open successfully!\r\n" ); } res = f_unlink(filename); if (res == FR_OK) { printf ("The file was deleted successfully!\r\n" ); } else { printf ("File deletion failed, error code:%d\r\n" , res); } }void FatFs_PrintfFileDate (WORD date, WORD time) { printf ("File data = %d/%d/%d\r\n" , ((date>>9 )&0x7F )+1980 , (date>>5 )&0xF , date&0x1F ); printf ("File time = %d:%d:%d\r\n" , (time>>11 )&0x1F , (time>>5 )&0x3F , time&0x1F ); }
file_operate.h文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #ifndef FILE_OPERATE_H #define FILE_OPERATE_H #include "main.h" #include "FatFs.h" #include "stdio.h" #define User_Sector 4096 #define User_FatFs USERFatFS #define User_SDPath USERPath void Mount_FatFs (void ) ;void FatFs_GetDiskInfo (void ) ;void FatFs_ScanDir (const TCHAR* PathName) ;void FatFs_ReadTXTFile (TCHAR *filename) ;void FatFs_WriteTXTFile (TCHAR *filename,uint16_t year, uint8_t month, uint8_t day) ;void FatFs_GetFileInfo (TCHAR *filename) ;void FatFs_DeleteFile (TCHAR *filename) ;void FatFs_PrintfFileDate (WORD date, WORD time) ;#endif
向工程中添加.c/.h文件的步骤请阅读“STM32CubeMX教程19 I2C - MPU6050驱动 ”实验3.2.3小节
最后在main.c文件中添加 ”file_operate.h“ 头文件,然后在主函数 main() 中调用文件系统挂载函数,实现按键控制逻辑程序,具体源代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include "file_operate.h" printf ("Reset,ID:0x%x\r\n" , Flash_ReadID()); Mount_FatFs(); FatFs_GetDiskInfo();if (HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin) == GPIO_PIN_SET) { HAL_Delay(50 ); if (HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin) == GPIO_PIN_SET) { FatFs_ScanDir("0:/" ); while (HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin)); } }if (HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET) { HAL_Delay(50 ); if (HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET) { FatFs_WriteTXTFile("test.txt" ,2016 ,11 ,15 ); while (!HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)); } }if (HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET) { HAL_Delay(50 ); if (HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET) { FatFs_ReadTXTFile("test.txt" ); FatFs_GetFileInfo("test.txt" ); while (!HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)); } }if (HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET) { HAL_Delay(50 ); if (HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET) { FatFs_DeleteFile("test.txt" ); while (!HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)); } }
4、烧录验证 烧录程序,开发板上电后会尝试在W25Q128 FLASH芯片上挂载文件系统,挂载成功后会输出读取到的 FLASH芯片的信息,接下来按照下面几个步骤使用FatFs文件系统对 FLASH芯片进行读写等测试
按下开发板上的WK_UP按键,扫描FLASH芯片根目录下所有文件,并通过串口将文件列表输出
按下开发板上的KEY2按键,在 FLASH芯片根目录创建一个”test.txt“文件,将一个字符串 ”Hello,OSnotes“ 写入该文件中,该字符串大小为15个字节(该字符串中末尾包括了一个’\n‘和一个‘\0’)
按下开发板上的KEY1按键,读取FLASH芯片根目录下名为”test.txt“的文件,将其中的内容通过串口输出,然后读取该文件的信息(大小,属性,名称),并通过串口输出
按下开发板上的KEY0按键,删除FLASH芯片根目录下名为”test.txt“的文件
整个实验过程串口具体的输出情况如下图所示
5、常用函数 FatFs的所有API函数详细介绍请参看FatFs官网 FatFs - Generic FAT Filesystem Module ,如下所示为笔者对其常用应用接口及其功能做简单介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 FRESULT f_mount (FatFs* fs, const TCHAR* path, BYTE opt) FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void * work, UINT len) FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FatFs** FatFs) FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode) int f_puts (const TCHAR* str, FIL* fp) int f_printf (FIL* fp, const TCHAR* fmt, ...) FRESULT f_close (FIL* fp) TCHAR* f_gets (TCHAR* buff, int len, FIL* fp) FRESULT f_opendir (DIR* dp, const TCHAR* path) FRESULT f_readdir (DIR* dp, FILINFO* fno) FRESULT f_closedir (DIR *dp) FRESULT f_stat (const TCHAR* path, FILINFO* fno) FRESULT f_unlink (const TCHAR* path) FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new) FRESULT f_mkdir (const TCHAR* path)
6、注释详解 注释1 :图片来源 FatFs Module Application Note (elm-chan.org)
参考资料 STM32Cube高效开发教程(高级篇)
更多内容请浏览 STM32CubeMX+STM32F4系列教程文章汇总贴