在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)
{
}
}
本章節內容還沒有完,請繼續參考下一章節內容
