在day22章節的基礎上添加FatFs模塊:https://www.cnblogs.com/josephcnblog/articles/9249787.html
在本章的末尾會附上所有的代碼
運行原理:方便代碼移植,調用底層接口函數:f_mount(),此函數在ff.c文件中
工程結構:
1、去FatFs文件系統官網下載文件系統庫函數源碼:http://elm-chan.org/fsw/ff/00index_e.html
2、解壓后
3、新建工程,在User目錄下創建文件夾,命名為fatfs,拷貝2中的所有文件和目錄到fatfs目錄下
4、批量導入文件。回到工程,點下面的圖標,創建新組FatFs,並添加文件
創建了一個新組:
5、添加頭文件路徑,點下面的圖標,添加fatfs目錄
6、編譯本文件,出錯:找不到文件,把下面三個.h文件注釋掉。
再編譯,把warning的都注釋掉:
再編譯,還是有warning,不管。編譯所有文件,出錯:diskio.c文件中沒有定義get_fattime()函數
從FatFs文件系統官網知道,需要包含如下函數接口:
查看diskio.c文件沒有get_fattime函數,所以需要添加該函數,打開diskio.h文件:只有5個函數接口,並沒有get_fattime
添加:(點擊官網的get_fattime鏈接進入函數),添加進來
在diskio.c中添加:
// 返回時間 DWORD get_fattime (void) { return 0; }
再編譯,把warning的全部注釋掉,再編譯就不會報錯和報警了。
7、在diskio.c中修改下面代碼
改成:
並把報錯的地方都改過來
(1)獲取設備狀態:修改diskio.c
/*-----------------------------------------------------------------------*/ /* Get Drive Status */ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { uint32_t FlashID = 0; DSTATUS stat = 0; // int result; switch (pdrv) { case SD_CARD : // result = ATA_disk_status(); // translate the reslut code here return stat; case SPI_FLASH : // result = MMC_disk_status(); /* ======================== 添加的代碼 ======================== */ /* 獲取SPI_FLASH設備的狀態 */ FlashID = SPI_FLASH_Read_Flash_ID(); /* 判斷設備是否初始化 */ if(FlashID == sFLASH_ID) { stat = 0; // 成功 } else { // 失敗,“或上”是因為失敗的原因有很多種,可以同時表達很多狀態 stat |= STA_NOINIT; } /* ======================== 添加的代碼 ======================== */ return stat; case USB : // result = USB_disk_status(); // translate the reslut code here return stat; } return STA_NOINIT; }
(2)初始化設備
/*-----------------------------------------------------------------------*/ /* Inidialize a Drive */ /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { DSTATUS stat; // int result; switch (pdrv) { case SD_CARD : // result = ATA_disk_initialize(); // translate the reslut code here return stat; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ /* SPI_FLASH設備初始化 */ SPI_FLASH_Init(); // SPI_FLASH:物理設備號,還有SD_CARD、USB兩種 return disk_status(SPI_FLASH); /* ======================== 添加的代碼 ======================== */ case USB : // result = USB_disk_initialize(); // translate the reslut code here return stat; } return STA_NOINIT; }
(3)讀取SPI_FLASH的數據
/*-----------------------------------------------------------------------*/ /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT disk_read ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to read */ ) { DRESULT res; // int result; switch (pdrv) { case SD_CARD : // translate the arguments here // result = ATA_disk_read(buff, sector, count); // translate the reslut code here return res; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ /* buff:讀取SPI_FLASH數據,buff是將讀取的數據放到buff指針變量中 sector:扇區,一個扇區有4096個字節,比如要讀第0扇區,那么讀數據的開始地址是:0*4096 如果要讀第1扇區,那么讀數據的開始地址是:1*4096 count:是要讀取的數據長度,也要乘以4096,表示讀的是多少個字節 */ SPI_FLASH_BufferRead(buff, sector*4096, count*4096); // 讀取完就返回OK return RES_OK; /* ======================== 添加的代碼 ======================== */ case USB : // translate the arguments here // result = USB_disk_read(buff, sector, count); // translate the reslut code here return res; } return RES_PARERR; }
(4)SPI_FLASH寫入數據
/*-----------------------------------------------------------------------*/ /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ #if _USE_WRITE DRESULT disk_write ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to write */ ) { DRESULT res; // int result; switch (pdrv) { case SD_CARD : // translate the arguments here // result = ATA_disk_write(buff, sector, count); // translate the reslut code here return res; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ // 寫之前先擦除 SPI_FLASH_Erase_Sector(sector*4096); // 寫入數據 SPI_FLASH_BufferWrite((u8*)buff, sector*4096, count*4096); // 如果要寫的嚴謹,這里要判斷成功或失敗,返回不同的狀態碼,這里省事就不深究了 return RES_OK; /* ======================== 添加的代碼 ======================== */ case USB : // translate the arguments here // result = USB_disk_write(buff, sector, count); // translate the reslut code here return res; } return RES_PARERR; } #endif
(5)disk_ioctl函數
5.1 打開ff.c下的ffconf.h,修改:這是支持格式化操作的命令
5.2扇區大小,在下面可以修改扇區的大小,這里把最大的扇區改成4096,最小的保持不變
改成:
(6)disk_ioctl函數:ioctl是設備驅動程序中對設備的I/O通道進行管理的函數
此函數用於操作底層的屬性,比如扇區數目,扇區大小,塊大小等等的屬性設置。
/*-----------------------------------------------------------------------*/ /* Miscellaneous Functions ioctl是設備驅動程序中對設備的I/O通道進行管理的函數 */ /*-----------------------------------------------------------------------*/ #if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; // int result; switch (pdrv) { case SD_CARD : // Process of the command for the ATA drive return res; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ switch(cmd) { case GET_SECTOR_COUNT: // 多少個扇區 // buff是void*類型指針(空指針),根據官網的disk_ioctl函數參數轉換不同返回指針 *(DWORD*)buff = 2048; // SPI_FLASH有8M字節,每個扇區有4096個字節,即有2048個扇區 break; case GET_SECTOR_SIZE: // 每個扇區大小 *(WORD*)buff = 4096; // 每個扇區有4096個字節 break; case GET_BLOCK_SIZE: // 塊大小 *(DWORD*)buff = 1; // 設置為1,表示每次擦除1個扇區 break; } res = RES_OK; return res; /* ======================== 添加的代碼 ======================== */ case USB : // Process of the command the USB drive return res; } return RES_PARERR; } #endif
編譯,沒有錯誤,沒有警告,接下來就是寫文件系統的移植測試程序了,主要是往SPI_FLASH中寫入數據,讀取數據等操作。
============================== FatFs文件系統移植測試
main.c
#include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./spi/bsp_spi_flash.h" #include "ff.h" // 添加文件系統的庫文件 FATFS fs; FRESULT res; int main(void) { /* ============================ 初始化 =========================== */ // 串口初始化 DEBUG_USART_Config(); printf("\r\n 這是文件系統移植實驗 \r\n"); /* 移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口 fs: 文件系統指針 path: SD_CARD(0)/SPI_FLASH(1)/USB(2) opt: 0:Do not mount (delayed mount), 1:Mount immediately */ res = f_mount(&fs, "1:", 1); if(res != FR_OK) { printf("\r\n mount res = %d \r\n", res); } while(1) { } }
燒錄進去,測試結果:按復位鍵RESET
輸出11,查看源碼知道,說的是無效的設備,這是因為文件系統支持的設備數量不夠。
查看ffconf.h文件:這里的#define _VOLUMES 1 ,表示文件系統支持的設備數量,
因為我們在diskio.c中設置了如下的設備:0表示SD卡,1表示SPI_FLASH,2表示USB,總共支持3中類型的設備,但是SD卡排在SPI_FLASH的前面,支持的數量又只有1個,所以
就默認是SD卡了
需要把支持的設備數量改成大於2才會讀取到SPI_FLASH,所以在ffconf.h中改成:
保存,再編譯,燒錄到板子,測試結果:
不再輸出11,說明上面的問題解決了。現在輸出了13,查看源碼:說的是SPI_FLASH這個設備沒有掛在文件系統
解決方法:需要先對文件系統進行格式化操作,才能實現文件系統掛在到SPI_FLASH
查看FatFs文件系統源碼,使用函數f_mkfs進行格式化。
main.c
#include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./spi/bsp_spi_flash.h" #include "ff.h" // 添加文件系統的庫文件 FATFS fs; FRESULT res; int main(void) { /* ============================ 初始化 =========================== */ // 串口初始化 DEBUG_USART_Config(); printf("\r\n 這是文件系統移植實驗 \r\n"); /* 移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口 fs: 文件系統指針 path: SD_CARD(0)/SPI_FLASH(1)/USB(2) opt: 0:Do not mount (delayed mount), 1:Mount immediately */ res = f_mount(&fs, "1:", 1); if(res == FR_NO_FILESYSTEM) // SPI_FLASH設備沒有掛在文件系統 { // 文件系統格式化 res = f_mkfs("1:", 0, 0); // 再次判斷 if(res != FR_OK) { printf("\r\n f_mkfs res = %d \r\n", res); } } while(1) { } }
實驗結果:
說明格式化成功。
下面是使用文件系統函數對文件的操作
main.c
#include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./spi/bsp_spi_flash.h" #include "ff.h" // 添加文件系統的庫文件 FATFS fs; FRESULT res; FIL fp; char write_buf[] = "這是文件系統測試寫入的數據"; UINT bw; int main(void) { /* ============================ 初始化 =========================== */ // 串口初始化 DEBUG_USART_Config(); printf("\r\n 這是文件系統移植實驗 \r\n"); /* 移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口 fs: 文件系統指針 path: SD_CARD(0)/SPI_FLASH(1)/USB(2) opt: 0:Do not mount (delayed mount), 1:Mount immediately */ res = f_mount(&fs, "1:", 1); if(res == FR_NO_FILESYSTEM) // SPI_FLASH設備沒有掛在文件系統 { printf("\r\n before f_mkfs:res = %d\r\n", res); // 文件系統格式化 res = f_mkfs("1:", 0, 0); // 再次判斷 if(res != FR_OK) { printf("\r\n f_mkfs res = %d \r\n", res); } } else if(res == FR_OK) { // 文件系統移植成功,下面是對文件的操作:創建,寫入,關閉,,, printf("\r\n f_mount res = %d\r\n", res); /* f_open函數:創建文件 fp:文件指針 1:test.txt:文件名 第三個參數是文件操作的權限,讀/寫/不管文件是否存在都創建(存在就覆蓋) */ res = f_open(&fp, "1:test.txt", FA_READ|FA_WRITE|FA_CREATE_ALWAYS); printf("\r\n f_open res = %d\r\n", res); /* f_write函數:向文件中寫入數據 fp:文件指針 write_buf:要寫入文件的數據 第三個參數:寫入多少個字節 第四個參數:指針,返回已經寫入到文件的字節數 */ res = f_write(&fp, write_buf, sizeof(write_buf), &bw); printf("\r\n f_write res = %d\r\n", res); /* f_close:關閉文件,使用f_open必須使用f_close,不然會有緩存 fp:文件指針 */ res = f_close(&fp); printf("\r\n f_close res = %d\r\n", res); } while(1) { } }
實驗結果:res全部返回0,說明f_mount,f_open,f_write,f_close這四個函數都執行成功了,現在SPI_FLASH設備掛載的FatFs文件系統
已經起作用了,而且在SPI_FLASH設備上的第0扇區中創建了test.txt文件,並寫入了數據。
讀文件數據
#include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./spi/bsp_spi_flash.h" #include "ff.h" // 添加文件系統的庫文件 FATFS fs; FRESULT res; FIL fp; char write_buf[] = "這是文件系統測試寫入的數據"; UINT bw; char read_buf[1024] = {0}; UINT br; char filename[] = "1:test.txt"; int main(void) { /* ============================ 初始化 =========================== */ // 串口初始化 DEBUG_USART_Config(); printf("\r\n 這是文件系統移植實驗 \r\n"); /* 移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口 fs: 文件系統指針 path: SD_CARD(0)/SPI_FLASH(1)/USB(2) opt: 0:Do not mount (delayed mount), 1:Mount immediately */ res = f_mount(&fs, "1:", 1); if(res == FR_NO_FILESYSTEM) // SPI_FLASH設備沒有掛在文件系統 { printf("\r\n before f_mkfs:res = %d\r\n", res); // 文件系統格式化 res = f_mkfs("1:", 0, 0); // 再次判斷 if(res != FR_OK) { printf("\r\n f_mkfs res = %d \r\n", res); } } else if(res == FR_OK) { // 文件系統移植成功,下面是對文件的操作:創建,寫入,關閉,,, printf("\r\n f_mount res = %d\r\n", res); /* f_open函數:創建文件 fp:文件指針 1:test.txt:文件名 第三個參數是文件操作的權限,讀/寫/不管文件是否存在都創建(存在就覆蓋) */ res = f_open(&fp, filename, FA_READ|FA_WRITE|FA_CREATE_ALWAYS); printf("\r\n f_open res = %d\r\n", res); /* f_write函數:向文件中寫入數據 fp:文件指針 write_buf:要寫入文件的數據 第三個參數:寫入多少個字節 第四個參數:指針,返回已經寫入到文件的字節數 */ res = f_write(&fp, write_buf, sizeof(write_buf), &bw); printf("\r\n f_write res = %d\r\n", res); /* f_close:關閉文件,使用f_open必須使用f_close,不然會有緩存 fp:文件指針 */ res = f_close(&fp); printf("\r\n f_close res = %d\r\n", res); /* f_read函數:讀文件數據,讀剛剛寫入的數據 fp:文件指針 read_buf:將讀取到的數據存儲到這個數組中 1024:打算讀取多少個字節數據 br:實際讀取回來的數據字節數 */ // 讀之前需要打開文件:只讀權限(文件存在就讀取) f_open(&fp, filename, FA_READ|FA_OPEN_EXISTING); // 開始讀取文件數據 res = f_read(&fp, read_buf, 1024, &br); printf("\r\n f_read res = %d,br = %d,sizeof write_buf = %d,", res, br, sizeof(write_buf)); printf("read_buf = %s\r\n", read_buf); // 讀取完文件后要關閉文件 res = f_close(&fp); printf("\r\n f_close res = %d\r\n", res); } while(1) { } }
實驗結果:f_read函數執行成功,成功將上面寫入到SPI_FLASH第0扇區寫入的文件數據讀取回來
如果將文件名設置的很長:
char filename[] = "1:testhhhhhhhhhhhhhhhhhh.txt";
實驗結果:在f_open的時候就出錯,出錯代碼:6,查看源碼:FR_INVALID_NAME /* (6) The path name format is invalid */,說是無效的文件名
解決方法:在ffconf.h中修改,改成1就支持長文件名
編譯報錯:
在FatFs目錄先添加文件:User\fatfs\option\ccsbcs.c
打開ffconf.h文件,並修改:表示支持英文形式的長文件名
再編譯,就不會報錯了。再測試:可以正常讀取了
============================== 程序清單 =============================================
工程結構:
bsp_usart.h
#ifndef __BSP_USART_H__ #define __BSP_USART_H__ #include "stm32f10x.h" #include "stdio.h" // ----------------------- 串口1-USART1 // 使用哪個串口(串口1..5) #define DEBUG_USARTx USART1 // APB2串口的同步時鍾 #define DEBUG_USART_CLK RCC_APB2Periph_USART1 // APB2系統時鍾(因為串口USART1是掛載到APB2總線上的,所以要打開APB2總線的時鍾) #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd // 串口通信的波特率 #define DEBUG_USART_BAUDRATE 19200 // ----------------------- USART GPIO 引腳宏定義 // GPIO引腳 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA) // APB2系統時鍾(因為串口USART1是掛載到APB2總線上的,所以要打開APB2總線的時鍾) #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd // GPIO引腳,發送接PA9,接收接PA10 #define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9 #define DEBUG_USART_RX_GPIO_PORT GPIOA #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10 #define DEBUG_USART_IRQ USART1_IRQn #define DEBUG_USART_IRQHandler USART1_IRQHandler /* 串口調試配置函數:配置串口的相關參數,使能串口 */ void DEBUG_USART_Config(void); /* 發送一個字節 */ void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch); /* 發送字符串 */ void Usart_SendString(USART_TypeDef* pUSARTx, char* str); #endif /* __BSP_USART_H__ */
bsp_usart.c
#include "./usart/bsp_usart.h" /* 串口中斷配置函數 */ //static void NVIC_Configuration(void) //{ // NVIC_InitTypeDef NVIC_InitStructure; // // /* 嵌套向量中斷控制器組選擇 */ // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // // /* 配置USART為中斷源 */ // NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ; // /* 搶斷優先級*/ // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // /* 子優先級 */ // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // /* 使能中斷 */ // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // // /* 初始化配置NVIC */ // NVIC_Init(&NVIC_InitStructure); //} /* 串口調試配置函數:配置串口的相關參數,使能串口 */ void DEBUG_USART_Config(void) { /* 結構體變量聲明 */ GPIO_InitTypeDef GPIO_InitStructure; // GPIO USART_InitTypeDef USART_InitStructure; // USART /* ------------ 第一步:初始化GPIO */ // 打開串口GPIO的時鍾 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE); // 將USART Tx的GPIO配置為推挽復用模式 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN; // 引腳 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 速率 GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure); // 初始化結構體 // 將USART Rx的GPIO配置為浮空輸入模式 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); /* ------------ 第二步:配置串口的初始化結構體 */ // 打開串口外設的時鍾 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE); /* 配置串口的工作參數 */ // 波特率 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; // 針數據字長 USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 停止位 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 校驗位 USART_InitStructure.USART_Parity = USART_Parity_No ; // 硬件流控制 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 工作模式,收發一起 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 完成串口的初始化配置 USART_Init(DEBUG_USARTx, &USART_InitStructure); /* -------------------------------------------------------- */ // 串口中斷優先級配置 //NVIC_Configuration(); // 使能串口接收中斷 //USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE); /* -------------------------------------------------------- */ /* ------------ 第三步:使能串口 */ USART_Cmd(DEBUG_USARTx, ENABLE); } /* 發送一個字節 */ void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch) { /* 發送一個字節數據到USART */ USART_SendData(pUSARTx, ch); /* 等待發送數據寄存器為空 */ while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); } /* 發送字符串 */ void Usart_SendString(USART_TypeDef* pUSARTx, char* str) { unsigned int k=0; do { Usart_SendByte(pUSARTx, *(str + k)); k++; } while(*(str + k)!='\0'); /* 等待發送完成 */ while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET); } /* 重定向c庫函數printf到串口,重定向后可使用printf函數 */ int fputc(int ch, FILE *f) { /* 發送一個字節數據到串口 */ USART_SendData(DEBUG_USARTx, (uint8_t) ch); /* 等待發送完畢 */ while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); return (ch); } /* 重定向c庫函數scanf到串口,重寫向后可使用scanf、getchar等函數 */ int fgetc(FILE *f) { /* 等待串口輸入數據 */ while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET); return (int)USART_ReceiveData(DEBUG_USARTx); }
bsp_spi_flash.h
#ifndef __BSP_SPI_FLASH_H__ #define __BSP_SPI_FLASH_H__ #include "stm32f10x.h" #include "stdio.h" /* =================== GPIO引腳定義 ==================== */ #define SPI_FLASH_GPIO_CLK RCC_APB2Periph_GPIOA #define SPI_FLASH_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd /* =================== SPI外設接口定義 ==================== */ #define SPI_FLASHx SPI1 #define SPI_FLASH_CLK RCC_APB2Periph_SPI1 #define SPI_FLASH_APBxClkCmd RCC_APB2PeriphClockCmd /* =================== SPI-FLASH引腳定義 ==================== */ // CS(NSS)引腳 #define SPI_FLASH_NSS_GPIO_PORT GPIOA #define SPI_FLASH_NSS_GPIO_PIN GPIO_Pin_4 // SCK引腳 #define SPI_FLASH_SCK_GPIO_PORT GPIOA #define SPI_FLASH_SCK_GPIO_PIN GPIO_Pin_5 // MISO引腳 #define SPI_FLASH_MISO_GPIO_PORT GPIOA #define SPI_FLASH_MISO_GPIO_PIN GPIO_Pin_6 // MOSI引腳 #define SPI_FLASH_MOSI_GPIO_PORT GPIOA #define SPI_FLASH_MOSI_GPIO_PIN GPIO_Pin_7 /* =================== 常量定義 ==================== */ #define SPI_FLASH_WAIT_TIMEOUT 10000 // SPI-FLASH超時時間 #define SPI_FLASH_PageSize 256 #define SPI_FLASH_PerWritePageSize 256 /* =================== FLASH ID ==================== */ //#define sFLASH_ID 0xef3015 //W25X16 //#define sFLASH_ID 0xef4015 //W25Q16 //#define sFLASH_ID 0Xef4018 //W25Q128 #define sFLASH_ID 0Xef4017 //W25Q64 /***************** 指令定義-開頭 *****************/ #define W25X_WriteEnable 0x06 #define W25X_WriteDisable 0x04 #define W25X_ReadStatusReg 0x05 #define W25X_WriteStatusReg 0x01 #define W25X_ReadData 0x03 #define W25X_FastReadData 0x0B #define W25X_FastReadDual 0x3B #define W25X_PageProgram 0x02 #define W25X_BlockErase 0xD8 #define W25X_SectorErase 0x20 #define W25X_ChipErase 0xC7 #define W25X_PowerDown 0xB9 #define W25X_ReleasePowerDown 0xAB #define W25X_DeviceID 0xAB #define W25X_ManufactDeviceID 0x90 #define W25X_JedecDeviceID 0x9F /* =================== WIP(busy)標志,FLASH內部正在寫入 ==================== */ #define WIP_Flag 0x01 #define Dummy_Byte 0xFF /* =================== 函數宏定義 ==================== */ #define SPI_FLASH_NSS_LOW() GPIO_ResetBits(SPI_FLASH_NSS_GPIO_PORT,SPI_FLASH_NSS_GPIO_PIN); #define SPI_FLASH_NSS_HIGH() GPIO_SetBits(SPI_FLASH_NSS_GPIO_PORT,SPI_FLASH_NSS_GPIO_PIN); /*信息輸出*/ #define FLASH_DEBUG_ON 1 #define FLASH_INFO(fmt,arg...) printf("<<-FLASH-INFO->> "fmt"\n",##arg) #define FLASH_ERROR(fmt,arg...) printf("<<-FLASH-ERROR->> "fmt"\n",##arg) #define FLASH_DEBUG(fmt,arg...) do{\ if(FLASH_DEBUG_ON)\ printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\ }while(0) /* =================== SPI-FLASH相關函數 ==================== */ /* SPI-FLASH初始化 */ void SPI_FLASH_Init(void); /* 發送一個字節 */ uint8_t SPI_FLASH_Send_Data(uint8_t data); /* 接收一個字節 */ uint8_t SPI_FLASH_Receive_Data(void); /* 讀取FLASH_ID */ uint32_t SPI_FLASH_Read_Flash_ID(void); /* 讀取Device ID */ uint32_t SPI_FLASH_Read_Device_ID(void); /* 擦除一個扇區 */ void SPI_FLASH_Erase_Sector(uint32_t addr); /* 寫使能 */ void SPI_FLASH_Write_Enable(void); /** * @brief 讀取FLASH數據 * @param pBuffer,存儲讀出數據的指針 * @param ReadAddr,讀取地址 * @param NumByteToRead,讀取數據長度 * @retval 無 */ void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead); /* 向SPI扇區寫入數據(一次最多寫入256個字節) */ void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite); /** * @brief 對FLASH寫入數據,調用本函數寫入數據前需要先擦除扇區 * @param pBuffer,要寫入數據的指針 * @param WriteAddr,寫入地址 * @param NumByteToWrite,寫入數據長度 * @retval 無 */ void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite); #endif /* __BSP_SPI_FLASH_H__ */
bsp_spi_flash.c
#include "./spi/bsp_spi_flash.h" uint16_t time_out; /* SPI-FLASH初始化 */ void SPI_FLASH_Init(void) { // 結構體變量聲明 GPIO_InitTypeDef GPIO_InitStructure; // GPIO SPI_InitTypeDef SPI_InitStructure; // SPI /* =========================== 第一步:打開時鍾 =========================== */ // 打開SPI GPIO的時鍾 SPI_FLASH_GPIO_APBxClkCmd(SPI_FLASH_GPIO_CLK, ENABLE); // 打開SPI外設的時鍾 SPI_FLASH_APBxClkCmd(SPI_FLASH_CLK, ENABLE); /* =========================== 第二步:配置引腳 =========================== */ // 配置CS(NSS)引腳 GPIO_InitStructure.GPIO_Pin = SPI_FLASH_NSS_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(SPI_FLASH_NSS_GPIO_PORT, &GPIO_InitStructure); // 配置SCK引腳 GPIO_InitStructure.GPIO_Pin = SPI_FLASH_SCK_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(SPI_FLASH_SCK_GPIO_PORT, &GPIO_InitStructure); // MISO引腳 GPIO_InitStructure.GPIO_Pin = SPI_FLASH_MISO_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(SPI_FLASH_MISO_GPIO_PORT, &GPIO_InitStructure); // MOSI引腳 GPIO_InitStructure.GPIO_Pin = SPI_FLASH_MOSI_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(SPI_FLASH_MOSI_GPIO_PORT, &GPIO_InitStructure); /* =========================== 第三步:配置SPI工作模式 =========================== */ SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 二分頻 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第一邊沿采樣 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // CPOL和CPHA都為0,即模式0 SPI_InitStructure.SPI_CRCPolynomial = 0; // 這個參數不要求,但也要配置,否則報錯 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8個數據位 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 雙線全雙工 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位先行 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // STM32配置成主機,FLASH等其他外設為從機 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 軟件控制 // SPI初始化 SPI_Init(SPI_FLASHx, &SPI_InitStructure); /* =========================== 第四步:使能SPI =========================== */ SPI_Cmd(SPI_FLASHx, ENABLE); } /* 出錯時調用回調函數返回錯誤代碼(錯誤信息) */ uint8_t SPI_Timeout_CallBack(uint8_t data) { printf("\r\n SPI檢測超時,錯誤代碼:%d \r\n", data); return 0; } /* 功能:發送一個字節 data:要發送的數據 返回:發送過程中接收回來的數據 */ uint8_t SPI_FLASH_Send_Data(uint8_t data) { uint8_t read_temp; // 檢測TXE time_out = SPI_FLASH_WAIT_TIMEOUT; while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) // 發送緩沖區為空,可以向里面發送數據 { if(time_out-- == 0) // 超時 { return SPI_Timeout_CallBack(1); } } SPI_I2S_SendData(SPI1, data); // 發送數據 // 檢測RXNE time_out = SPI_FLASH_WAIT_TIMEOUT; while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) // 接收緩沖區為空,可以讀取里面的數據 { if(time_out-- == 0) // 超時 { return SPI_Timeout_CallBack(2); } } read_temp = (uint8_t)SPI_I2S_ReceiveData(SPI1); // 接收數據 return read_temp; } /* 功能:接收一個字節 返回:接收到的數據 說明:根據時序圖寫 */ uint8_t SPI_FLASH_Receive_Data(void) { return SPI_FLASH_Send_Data(Dummy_Byte); } /* 讀取JEDEC_ID */ uint32_t SPI_FLASH_Read_Flash_ID(void) { uint32_t id; // 拉低NSS,開始讀取數據 SPI_FLASH_NSS_LOW(); // 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸 SPI_FLASH_Send_Data(W25X_JedecDeviceID); // 讀取數據 id = SPI_FLASH_Receive_Data(); // id左移八位,騰出低八位繼續接收數據 id <<= 8; // 繼續接收數據 id |= SPI_FLASH_Receive_Data(); // id左移八位,騰出低八位繼續接收數據 id <<= 8; // 繼續接收數據 id |= SPI_FLASH_Receive_Data(); // 拉高NSS,結束讀取數據 SPI_FLASH_NSS_HIGH(); // 返回讀取的id return id; } /* 讀取Device_ID */ uint32_t SPI_FLASH_Read_Device_ID(void) { u32 Temp = 0; /* Select the FLASH: Chip Select low */ SPI_FLASH_NSS_LOW(); /* Send "RDID " instruction */ SPI_FLASH_Send_Data(W25X_DeviceID); SPI_FLASH_Send_Data(Dummy_Byte); SPI_FLASH_Send_Data(Dummy_Byte); SPI_FLASH_Send_Data(Dummy_Byte); /* Read a byte from the FLASH */ Temp = SPI_FLASH_Send_Data(Dummy_Byte); /* Deselect the FLASH: Chip Select high */ SPI_FLASH_NSS_HIGH(); return Temp; } /* 寫使能 */ void SPI_FLASH_Write_Enable(void) { // 拉低NSS,開始讀取數據 SPI_FLASH_NSS_LOW(); // 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸 SPI_FLASH_Send_Data(W25X_WriteEnable); // 發送06h指令進行使能操作 // 拉高NSS,結束讀取數據 SPI_FLASH_NSS_HIGH(); } /* 功能:讀取狀態寄存器,用於檢測忙碌還是空閑 */ void SPI_FLASH_WaitForWriteEnd(void) { uint8_t status; // 拉低NSS,開始讀取數據 SPI_FLASH_NSS_LOW(); // 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸 SPI_FLASH_Send_Data(W25X_ReadStatusReg); // 發送05h指令進行讀取狀態操作 // 讀取狀態 do { status = SPI_FLASH_Receive_Data(); } while(status & WIP_Flag); // BUSY標志S7-S0,如果S0這個位是1表示忙碌,0表示空閑,所以只要檢測這一位即可 // 拉高NSS,結束讀取數據 SPI_FLASH_NSS_HIGH(); } /* 功能:擦除一個扇區 addr:扇區地址 說明:根據SPI說明文檔,在擦除或寫入之前,要先使能寫命令 */ void SPI_FLASH_Erase_Sector(uint32_t addr) { // 等待其他操作完成之后再進行擦除操作 SPI_FLASH_WaitForWriteEnd(); // 在擦除之前進行使能操作 SPI_FLASH_Write_Enable(); /* 發送要擦出的二十四位地址碼 */ // 拉低NSS,開始讀取數據 SPI_FLASH_NSS_LOW(); // 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸 SPI_FLASH_Send_Data(W25X_SectorErase); // 發送20h指令進行擦除操作 // 開始發送地址:一次發送八位,移位操作 SPI_FLASH_Send_Data((addr & 0xFF0000) >> 16); SPI_FLASH_Send_Data((addr & 0xFF00) >> 8); SPI_FLASH_Send_Data((addr & 0xFF)); // 拉高NSS,結束讀取數據 SPI_FLASH_NSS_HIGH(); // 檢測是否已經擦除完 SPI_FLASH_WaitForWriteEnd(); } /** * @brief 讀取FLASH數據 * @param pBuffer,存儲讀出數據的指針 * @param ReadAddr,讀取地址 * @param NumByteToRead,讀取數據長度 * @retval 無 */ void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) { /* 選擇FLASH: CS低電平 */ SPI_FLASH_NSS_LOW(); /* 發送 讀 指令 */ SPI_FLASH_Send_Data(W25X_ReadData); /* 發送 讀 地址高位 */ SPI_FLASH_Send_Data((ReadAddr & 0xFF0000) >> 16); /* 發送 讀 地址中位 */ SPI_FLASH_Send_Data((ReadAddr& 0xFF00) >> 8); /* 發送 讀 地址低位 */ SPI_FLASH_Send_Data(ReadAddr & 0xFF); /* 讀取數據 */ while (NumByteToRead--) /* while there is data to be read */ { /* 讀取一個字節*/ *pBuffer = SPI_FLASH_Send_Data(Dummy_Byte); /* 指向下一個字節緩沖區 */ pBuffer++; } /* 停止信號 FLASH: CS 高電平 */ SPI_FLASH_NSS_HIGH(); } /** * @brief 對FLASH寫入數據,調用本函數寫入數據前需要先擦除扇區 * @param pBuffer,要寫入數據的指針 * @param WriteAddr,寫入地址 * @param NumByteToWrite,寫入數據長度 * @retval 無 */ void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0; /*mod運算求余,若writeAddr是SPI_FLASH_PageSize整數倍,運算結果Addr值為0*/ Addr = WriteAddr % SPI_FLASH_PageSize; /*差count個數據值,剛好可以對齊到頁地址*/ count = SPI_FLASH_PageSize - Addr; /*計算出要寫多少整數頁*/ NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; /*mod運算求余,計算出剩余不滿一頁的字節數*/ NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; /* Addr=0,則WriteAddr 剛好按頁對齊 aligned */ if (Addr == 0) { /* NumByteToWrite < SPI_FLASH_PageSize */ if (NumOfPage == 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } else /* NumByteToWrite > SPI_FLASH_PageSize */ { /*先把整數頁都寫了*/ while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } /*若有多余的不滿一頁的數據,把它寫完*/ SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } /* 若地址與 SPI_FLASH_PageSize 不對齊 */ else { /* NumByteToWrite < SPI_FLASH_PageSize */ if (NumOfPage == 0) { /*當前頁剩余的count個位置比NumOfSingle小,一頁寫不完*/ if (NumOfSingle > count) { temp = NumOfSingle - count; /*先寫滿當前頁*/ SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); WriteAddr += count; pBuffer += count; /*再寫剩余的數據*/ SPI_FLASH_PageWrite( pBuffer, WriteAddr,temp); } else /*當前頁剩余的count個位置能寫完NumOfSingle個數據*/ { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } } else /* NumByteToWrite > SPI_FLASH_PageSize */ { /*地址不對齊多出的count分開處理,不加入這個運算*/ NumByteToWrite -= count; NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; /* 先寫完count個數據,為的是讓下一次要寫的地址對齊 */ SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); /* 接下來就重復地址對齊的情況 */ WriteAddr += count; pBuffer += count; /*把整數頁都寫了*/ while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } /*若有多余的不滿一頁的數據,把它寫完*/ if (NumOfSingle != 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } } } /** * @brief 對FLASH按頁寫入數據,調用本函數寫入數據前需要先擦除扇區 * @param pBuffer,要寫入數據的指針 * @param WriteAddr,寫入地址 * @param NumByteToWrite,寫入數據長度,必須小於等於SPI_FLASH_PerWritePageSize * @retval 無 */ void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { /* 發送FLASH寫使能命令 */ SPI_FLASH_Write_Enable(); /* 選擇FLASH: CS低電平 */ SPI_FLASH_NSS_LOW(); /* 寫頁寫指令*/ SPI_FLASH_Send_Data(W25X_PageProgram); /*發送寫地址的高位*/ SPI_FLASH_Send_Data((WriteAddr & 0xFF0000) >> 16); /*發送寫地址的中位*/ SPI_FLASH_Send_Data((WriteAddr & 0xFF00) >> 8); /*發送寫地址的低位*/ SPI_FLASH_Send_Data(WriteAddr & 0xFF); if(NumByteToWrite > SPI_FLASH_PerWritePageSize) { NumByteToWrite = SPI_FLASH_PerWritePageSize; FLASH_ERROR("SPI_FLASH_PageWrite too large!"); } /* 寫入數據*/ while (NumByteToWrite--) { /* 發送當前要寫入的字節數據 */ SPI_FLASH_Send_Data(*pBuffer); /* 指向下一字節數據 */ pBuffer++; } /* 停止信號 FLASH: CS 高電平 */ SPI_FLASH_NSS_HIGH(); /* 等待寫入完畢*/ SPI_FLASH_WaitForWriteEnd(); }
diskio.c
/*-----------------------------------------------------------------------*/ /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2014 */ /*-----------------------------------------------------------------------*/ /* If a working storage control module is available, it should be */ /* attached to the FatFs via a glue function rather than modifying it. */ /* This is an example of glue functions to attach various exsisting */ /* storage control modules to the FatFs module with a defined API. */ /*-----------------------------------------------------------------------*/ #include "diskio.h" /* FatFs lower layer API */ #include "./spi/bsp_spi_flash.h" //#include "usbdisk.h" /* Example: Header file of existing USB MSD control module */ //#include "atadrive.h" /* Example: Header file of existing ATA harddisk control module */ //#include "sdcard.h" /* Example: Header file of existing MMC/SDC contorl module */ /* Definitions of physical drive number for each drive */ #define SD_CARD 0 #define SPI_FLASH 1 #define USB 2 /*-----------------------------------------------------------------------*/ /* Get Drive Status */ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { uint32_t FlashID = 0; DSTATUS stat = 0; // int result; switch (pdrv) { case SD_CARD : // result = ATA_disk_status(); // translate the reslut code here return stat; case SPI_FLASH : // result = MMC_disk_status(); /* ======================== 添加的代碼 ======================== */ /* 獲取SPI_FLASH設備的狀態 */ FlashID = SPI_FLASH_Read_Flash_ID(); /* 判斷設備是否初始化 */ if(FlashID == sFLASH_ID) { stat = 0; // 成功 } else { // 失敗,“或上”是因為失敗的原因有很多種,可以同時表達很多狀態 stat |= STA_NOINIT; } /* ======================== 添加的代碼 ======================== */ return stat; case USB : // result = USB_disk_status(); // translate the reslut code here return stat; } return STA_NOINIT; } /*-----------------------------------------------------------------------*/ /* Inidialize a Drive */ /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { DSTATUS stat; // int result; switch (pdrv) { case SD_CARD : // result = ATA_disk_initialize(); // translate the reslut code here return stat; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ /* SPI_FLASH設備初始化 */ SPI_FLASH_Init(); // SPI_FLASH:物理設備號,還有SD_CARD、USB兩種 return disk_status(SPI_FLASH); /* ======================== 添加的代碼 ======================== */ case USB : // result = USB_disk_initialize(); // translate the reslut code here return stat; } return STA_NOINIT; } /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT disk_read ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to read */ ) { DRESULT res; // int result; switch (pdrv) { case SD_CARD : // translate the arguments here // result = ATA_disk_read(buff, sector, count); // translate the reslut code here return res; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ /* buff:讀取SPI_FLASH數據,buff是將讀取的數據放到buff指針變量中 sector:扇區,一個扇區有4096個字節,比如要讀第0扇區,那么讀數據的開始地址是:0*4096 如果要讀第1扇區,那么讀數據的開始地址是:1*4096 count:是要讀取的數據長度,也要乘以4096,表示讀的是多少個字節 */ SPI_FLASH_BufferRead(buff, sector*4096, count*4096); // 讀取完就返回OK return RES_OK; /* ======================== 添加的代碼 ======================== */ case USB : // translate the arguments here // result = USB_disk_read(buff, sector, count); // translate the reslut code here return res; } return RES_PARERR; } /*-----------------------------------------------------------------------*/ /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ #if _USE_WRITE DRESULT disk_write ( BYTE pdrv, /* Physical drive nmuber to identify the drive */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to write */ ) { DRESULT res; // int result; switch (pdrv) { case SD_CARD : // translate the arguments here // result = ATA_disk_write(buff, sector, count); // translate the reslut code here return res; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ // 寫之前先擦除 SPI_FLASH_Erase_Sector(sector*4096); // 寫入數據 SPI_FLASH_BufferWrite((u8*)buff, sector*4096, count*4096); // 如果要寫的嚴謹,這里要判斷成功或失敗,返回不同的狀態碼,這里省事就不深究了 return RES_OK; /* ======================== 添加的代碼 ======================== */ case USB : // translate the arguments here // result = USB_disk_write(buff, sector, count); // translate the reslut code here return res; } return RES_PARERR; } #endif /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions ioctl是設備驅動程序中對設備的I/O通道進行管理的函數 */ /*-----------------------------------------------------------------------*/ #if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; // int result; switch (pdrv) { case SD_CARD : // Process of the command for the ATA drive return res; case SPI_FLASH : /* ======================== 添加的代碼 ======================== */ switch(cmd) { case GET_SECTOR_COUNT: // 多少個扇區 // buff是void*類型指針(空指針),根據官網的disk_ioctl函數參數轉換不同返回指針 *(DWORD*)buff = 2048; // SPI_FLASH有8M字節,每個扇區有4096個字節,即有2048個扇區 break; case GET_SECTOR_SIZE: // 每個扇區大小 *(WORD*)buff = 4096; // 每個扇區有4096個字節 break; case GET_BLOCK_SIZE: // 塊大小 *(DWORD*)buff = 1; // 設置為1,表示每次擦除1個扇區 break; } res = RES_OK; return res; /* ======================== 添加的代碼 ======================== */ case USB : // Process of the command the USB drive return res; } return RES_PARERR; } #endif // 返回時間 DWORD get_fattime (void) { return 0; }
main.c
#include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./spi/bsp_spi_flash.h" #include "ff.h" // 添加文件系統的庫文件 FATFS fs; FRESULT res; FIL fp; char write_buf[] = "這是文件系統測試寫入的數據"; UINT bw; char read_buf[1024] = {0}; UINT br; char filename[] = "1:testhhhhhhhhhhhhhhhhhh.txt"; int main(void) { /* ============================ 初始化 =========================== */ // 串口初始化 DEBUG_USART_Config(); printf("\r\n 這是文件系統移植實驗 \r\n"); /* 移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口 fs: 文件系統指針 path: SD_CARD(0)/SPI_FLASH(1)/USB(2) opt: 0:Do not mount (delayed mount), 1:Mount immediately */ res = f_mount(&fs, "1:", 1); if(res == FR_NO_FILESYSTEM) // SPI_FLASH設備沒有掛在文件系統 { printf("\r\n before f_mkfs:res = %d\r\n", res); // 文件系統格式化 res = f_mkfs("1:", 0, 0); // 再次判斷 if(res != FR_OK) { printf("\r\n f_mkfs res = %d \r\n", res); } } else if(res == FR_OK) { // 文件系統移植成功,下面是對文件的操作:創建,寫入,關閉,,, printf("\r\n f_mount res = %d\r\n", res); /* f_open函數:創建文件 fp:文件指針 1:test.txt:文件名 第三個參數是文件操作的權限,讀/寫/不管文件是否存在都創建(存在就覆蓋) */ res = f_open(&fp, filename, FA_READ|FA_WRITE|FA_CREATE_ALWAYS); printf("\r\n f_open res = %d\r\n", res); /* f_write函數:向文件中寫入數據 fp:文件指針 write_buf:要寫入文件的數據 第三個參數:寫入多少個字節 第四個參數:指針,返回已經寫入到文件的字節數 */ res = f_write(&fp, write_buf, sizeof(write_buf), &bw); printf("\r\n f_write res = %d\r\n", res); /* f_close:關閉文件,使用f_open必須使用f_close,不然會有緩存 fp:文件指針 */ res = f_close(&fp); printf("\r\n f_close res = %d\r\n", res); /* f_read函數:讀文件數據,讀剛剛寫入的數據 fp:文件指針 read_buf:將讀取到的數據存儲到這個數組中 1024:打算讀取多少個字節數據 br:實際讀取回來的數據字節數 */ // 讀之前需要打開文件:只讀權限(文件存在就讀取) f_open(&fp, filename, FA_READ|FA_OPEN_EXISTING); // 開始讀取文件數據 res = f_read(&fp, read_buf, 1024, &br); printf("\r\n f_read res = %d,br = %d,sizeof write_buf = %d,", res, br, sizeof(write_buf)); printf("read_buf = %s\r\n", read_buf); // 讀取完文件后要關閉文件 res = f_close(&fp); printf("\r\n f_close res = %d\r\n", res); } while(1) { } }
本章節內容還沒有完,請繼續參考下一章節內容