STM32CbueMX之Fatfs移植到SPI_Flash

我使用的是SPI_Flash芯片是W25Q256,兼容MX25L256。驱动程序在https://blog.csdn.net/sudaroot/article/details/93158309

 

 

 

STM32CubeMX配置图:

STM32CbueMX之Fatfs移植到SPI_Flash_第1张图片

STM32CbueMX之Fatfs移植到SPI_Flash_第2张图片

解释一下选项:

1、支持中文简体编码格式就没什么好说的。

2、缓存工作区为什么放在栈?其实fatfs提供了三个选项:BSS,STACK , HEAP,根据个人情况选一个。

      在BSS上启用带有静态工作缓冲区的LFN,不能动态分配。

      如果选择了HEAP(堆)且自己有属于自己的malloc就去重写ff_memalloc  ff_memfree函数。如果是库的malloc就不需要。

      一般都选择使用STACK(栈),能动态分配。

     当使用堆栈作为工作缓冲区时,请注意堆栈溢出。

3、为什么最大扇区大小是4096Byte?一般别人都是512Byte?   其实这个是根据你自己使用的存储芯片和驱动相关的。因为我使用的W25Q256这款芯片是最小擦除单位是4096。不使用512byte是因为效率大大降低但是优点是空间利用率会大大提高。比如你文件系统最大分区是512,但是芯片最小擦除单位是4096,那么你在驱动就要实现先用缓存区把整个扇区4096byte全部读出来,然后判读其中写入512byte中有没有擦除过(即全0xFF),没有的话先擦除,在把数据写入缓存区最后写入芯片。所以步骤繁琐效率低,但是优点就是存储空间的利用率会大大提高,避免太多浪费。  我很懒,所以选择使用4096。  

4、VOLUMES(逻辑驱动器):如果你是有多块储存芯片,每块存储芯片都是一个逻辑驱动器可以根据自己选择,我这个只有一块外挂的SPI flash存储芯片,故文章多块逻辑驱动器不做讨论(其实都过程差不多)。

 

STM32CbueMX之Fatfs移植到SPI_Flash_第3张图片

我缓存区使用的STACK,所以我给了0x400(1024byte)。随便给的,不溢出就行了。

其实这个可以算一下,一般我们stack给0x200(512byte),带了文件系统的话,因为是启用了_USE_LFN对长文件名的支持,所以需要会大一下,我们最大支持_MAX_LFN个字节的文件名,我选的是支持_MAX_LFN = 255;

工作缓冲区在非exFAT时占用(_MAX_LFN + 1)* 2个字节;

工作缓冲区在启用exFAT时占用(_MAX_LFN + 1)* 2个字节和额外的608个字节。

 

计算过程如下:

一般我们格式化都是FAT12/16/32 所需stack = 0x200 +  (_MAX_LFN + 1) * 2  = 0x200 + (255  + 1) * 2 = 1024byte = 0x400

启用exFAT时最大所需stack = 0x200 +  (_MAX_LFN + 1) * 2 + 608 = 0x200 + (255  + 1) * 2 + 608 = 1632byte

 

生成工程。打开user_diskio.c

 

DSTATUS USER_initialize (BYTE pdrv)是初始化驱动盘。

因为我只有0号盘,spi_flash初始化就是初始化spi协议,所以简单读取一下ID号,正确就行了。

/**
  * @brief  Initializes a Drive
  * @param  pdrv: Physical drive number (0..)
  * @retval DSTATUS: Operation status
  */
DSTATUS USER_initialize (
    BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{
    /* USER CODE BEGIN INIT */
    uint8_t id[2] = {0};

    if(pdrv != 0)   return STA_NOINIT;

    BSP_W25Q256_Init();
    BSP_W25Qx_Read_ID(id);

    if(id[0] == 0xEF && id[1] == 0x18) Stat = !STA_NOINIT;
    else   Stat = STA_NOINIT;

    return Stat;
    /* USER CODE END INIT */
}

 

USER_status(BYTE pdrv)获取硬盘状态函数(简单些)

/**
  * @brief  Gets Disk Status
  * @param  pdrv: Physical drive number (0..)
  * @retval DSTATUS: Operation status
  */
DSTATUS USER_status (
    BYTE pdrv       /* Physical drive number to identify the drive */
)
{
    /* USER CODE BEGIN STATUS */
    if(pdrv != 0)   return STA_NOINIT;

    return Stat;
    /* USER CODE END STATUS */
}

读写函数

/**
  * @brief  Reads Sector(s)
  * @param  pdrv: Physical drive number (0..)
  * @param  *buff: Data buffer to store read data
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to read (1..128)
  * @retval DRESULT: Operation result
  */
DRESULT USER_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 */
)
{
    /* USER CODE BEGIN READ */
    if(pdrv != 0 || count == 0)   return RES_PARERR;

    if(BSP_W25Qx_Read(buff, sector * 4096, count * 4096) == W25Qx_OK)
        return RES_OK;
    else return RES_ERROR;

    /* USER CODE END READ */
}

/**
  * @brief  Writes Sector(s)
  * @param  pdrv: Physical drive number (0..)
  * @param  *buff: Data to be written
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to write (1..128)
  * @retval DRESULT: Operation result
  */
#if _USE_WRITE == 1
DRESULT USER_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 */
)
{
    /* USER CODE BEGIN WRITE */
    /* USER CODE HERE */
    uint8_t i = 0;

    if(pdrv != 0 || count == 0)   return RES_PARERR;

    for(i = 0; i < count; i++)
    {
        if(BSP_W25Qx_Erase_Sector((sector + i) * 4096) != W25Qx_OK)
            return RES_ERROR;

        if(BSP_W25Qx_Write((uint8_t*)buff + (i * 4096), (sector + i) * 4096, 4096) != W25Qx_OK)
            return RES_ERROR;
    }

    return RES_OK;
    /* USER CODE END WRITE */
}

 

这个USER_ioctl函数很重要。

/**
  * @brief  I/O control operation
  * @param  pdrv: Physical drive number (0..)
  * @param  cmd: Control code
  * @param  *buff: Buffer to send/receive control data
  * @retval DRESULT: Operation result
  */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (
    BYTE pdrv,      /* Physical drive nmuber (0..) */
    BYTE cmd,       /* Control code */
    void *buff      /* Buffer to send/receive control data */
)
{
    /* USER CODE BEGIN IOCTL */
    DRESULT res = RES_ERROR;

    if(pdrv != 0)   return RES_PARERR;

    switch(cmd)
    {
        case CTRL_SYNC:
            res = RES_OK;
            break;

        case GET_SECTOR_COUNT:
            *(DWORD*)buff = 8192;
            res = RES_OK;
            break;

        case GET_SECTOR_SIZE:
            *(WORD*)buff = 4096;
            res = RES_OK;
            break;

        case GET_BLOCK_SIZE:
            *(DWORD*)buff = 1;
            res = RES_OK;
            break;

        default:
            res = RES_PARERR;
            break;

    }

    return res;
    /* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */

STM32CbueMX之Fatfs移植到SPI_Flash_第4张图片

 

main.c

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include 
#include 
#include 
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
FATFS spi_fs;
FIL fil;
unsigned int count = 0;
unsigned char work[4096] = {0};
unsigned char read_buf[50] = {0};
unsigned char write_buf[50] = "hello sudaroot\r\n";
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi5;

UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI5_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE* FILE)
{
    HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
    return ch;
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
    uint32_t res = 0;
    
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI5_Init();
  MX_USART1_UART_Init();
  MX_FATFS_Init();
  /* USER CODE BEGIN 2 */
    HAL_Delay(1000);
    printf("sduaroot fatfs test\r\n");
    
    printf("****** 擦除SPI_FLASH 扇区0 ******\r\n");
    BSP_W25Qx_Erase_Sector(0);
    HAL_Delay(1000);
    /*----------------------- 挂载测试 ---------------------------*/
    printf("****** 这是一个SPI FLASH 文件系统实验 ******\r\n");
    res = f_mount(&spi_fs, "0:", 1);

    if(res != FR_OK)
    {
        if(res == FR_NO_FILESYSTEM)
        {
            printf("f_mount 没有文件系统,开始格式化spi-flash\r\n");
            res = f_mkfs("0:", FM_ANY, 0, &work, 4096);

            if(res != FR_OK)
            {
                printf("f_mkfs 格式化失败,err = %d\r\n", res);

                while(1);
            }
            else
            {
                printf("格式化成功,开始重新挂载spi-flash\r\n");
                res = f_mount(&spi_fs, "0:", 1);

                if(res != FR_OK)
                {
                    printf("f_mount 发生错误,err = %d\r\n", res);
                }
                else printf("spi-flash文件系统挂载成功\r\n");

            }
        }
        else
        {
            printf("f_mount 发生其他错误,err = %d\r\n", res);

            while(1);
        }
    }
    else printf("spi-flash文件系统挂载成功\r\n");


    /*----------------------- 文件系统测试:写测试 -----------------------------*/
    printf("\r\n****** 即将进行文件写入测试... ******\r\n");
    res = f_open(&fil, "0:sudaroot.txt", FA_OPEN_ALWAYS | FA_WRITE);

    if(res == FR_OK)
    {
        printf("打开/创建sudaroot.txt文件成功,向文件写入数据。\r\n");
        res = f_write(&fil, write_buf, strlen((const char *)write_buf), &count);

        if(res != FR_OK)
        {
            printf("f_write 发生错误,err = %d\r\n", res);
            printf("关闭打开的sudaroot.txt文件\r\n");
            count = 0;
            f_close(&fil);
        }
        else
        {
            printf("文件写入成功,写入字节数据:%d\n", count);
            printf("向文件写入的数据为:\r\n%s\r\n", write_buf);
            printf("关闭打开的sudaroot.txt文件\r\n");
            count = 0;
            f_close(&fil);
        }
    }
    else printf("打开/创建sudaroot.txt文件失败,err = %d\r\n", res);

    /*------------------- 文件系统测试:读测试 ------------------------------------*/
    printf("****** 即将进行文件读取测试... ******\r\n");
    res = f_open(&fil, "0:sudaroot.txt", FA_OPEN_EXISTING | FA_READ);

    if(res == FR_OK)
    {
        printf("打开sudaroot.txt文件成功\r\n");
        res = f_read(&fil, read_buf, sizeof(read_buf), &count);

        if(res != FR_OK)
        {
            printf("f_read 发生错误,err = %d\r\n", res);
            printf("关闭打开的sudaroot.txt文件\r\n");
            f_close(&fil);
        }
        else
        {
            printf("文件读取成功,读取字节数据:%d\n", count);
            printf("向文件读取的数据为:\r\n%s\r\n", read_buf);
            printf("关闭打开的sudaroot.txt文件\r\n");
            f_close(&fil);
        }
    }
    else printf("打开sudaroot.txt文件失败,err = %d\r\n", res);



    /*------------------- 不再使用文件系统,取消挂载文件系统 ------------------------------------*/
    printf("不再使用文件系统,取消挂载文件系统\r\n");
    res = f_mount(NULL, "0:", 1);

    if(res == FR_OK) printf("取消挂载文件系统成功\r\n");
    else    printf("取消挂载文件系统失败,err = %d\r\n", res);

    printf("文件系统测试结束\r\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while (1)
    {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
        HAL_Delay(500);
        HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);
    }

  /* USER CODE END 3 */
}

 

效果:

STM32CbueMX之Fatfs移植到SPI_Flash_第5张图片

 

 

全篇完。

本人博客仅仅代表我个人见解方便记录成长笔记。

若有与 看官老爷见解有冲突,我坚信看官老爷见解是对的,我的是错的。

感谢~!

你可能感兴趣的:(STM32)