打印
[应用相关]

基于STM32CubeMX的片外SPIFLASH嵌入FATFS

[复制链接]
735|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
整体结构


这里我使用的平台是硬石的YS-F4Pro运动控制板,MCU是STM32F407IGTb,片外Flash使用的是16M的W25Q128,具体思路如下:

利用HAL库实现标准的MCU利用SPI与Flash通信进行读写;
此时基本功能【此时ID读取即可】
在STM32CubeMX使能FATFS;
在FATFS的用户接口中写入访问片外Flash的读写以及控制等函数;
此时FATFS是否能正常使用【测试标准FATFS读写】。


使用特权

评论回复
沙发
renzheshengui|  楼主 | 2021-7-1 11:59 | 只看该作者
操作步骤
  • 打开STM32CubeMX,选择芯片:



使用特权

评论回复
板凳
renzheshengui|  楼主 | 2021-7-1 12:00 | 只看该作者
2.配置系统Debug方式以及时钟来源

使用特权

评论回复
地板
renzheshengui|  楼主 | 2021-7-1 12:00 | 只看该作者
3.配置外部时钟

使用特权

评论回复
5
renzheshengui|  楼主 | 2021-7-1 12:00 | 只看该作者
4.配置SPI(这里要根据硬件电路对应的SPI来进行匹配,片选PI0,输出PC2,输入PI3,时钟PI1)


使用特权

评论回复
6
renzheshengui|  楼主 | 2021-7-1 12:00 | 只看该作者
5.配置片选信号PI0,该处不具体将SPI的通信,如有不解需参考SPI的通信说明

使用特权

评论回复
7
renzheshengui|  楼主 | 2021-7-1 12:00 | 只看该作者
6.为了后面不麻烦,这里把文件系统也配置进去,因为使用的是W25Q128,每个扇区大小为4096,所以此处将最大最小扇区定义为4096

使用特权

评论回复
8
renzheshengui|  楼主 | 2021-7-1 12:01 | 只看该作者
7.时钟树配置,主频为72M

使用特权

评论回复
9
renzheshengui|  楼主 | 2021-7-1 12:01 | 只看该作者
8.完成代码配置(选择必要的文件这项点上之后不会添加额外库文件,能减少无谓的编译时间)

使用特权

评论回复
10
renzheshengui|  楼主 | 2021-7-1 12:01 | 只看该作者
9.内嵌Flash芯片的驱动文件(这里直接贴我修改后的源码)
代码的具体意思我就不分析了,注释大部分都有

SPIFLASH.c


#ifndef __SPIFLASH_H__
#define __SPIFLASH_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include "SPIFLASH.h"
#include "spi.h"

#define W25Q80         0XEF13        
#define W25Q16         0XEF14
#define W25Q32         0XEF15
#define W25Q64         0XEF16
#define W25Q128        0XEF17
#define  M25P64_FLASH_ID        0x202017//C22017
#define  M25P40_FLASH_ID        0x202013

/* USER CODE BEGIN Includes */
#define sFLASH_CMD_WRITE          0x02  /*!< Write to Memory instruction */
#define sFLASH_CMD_WRSR           0x01  /*!< Write Status Register instruction */
#define sFLASH_CMD_WREN           0x06  /*!< Write enable instruction */
#define sFLASH_CMD_READ           0x03  /*!< Read from Memory instruction */
#define sFLASH_CMD_RDSR           0x05  /*!< Read Status Register instruction  */
#define sFLASH_CMD_RDID           0x9F  /*!< Read identification */
#define sFLASH_CMD_SE             0x20  /*!< Sector Erase instruction (4k)*/
#define sFLASH_CMD_BE             0xD8  /*!< Block Erase instruction (64k)*/
#define sFLASH_CMD_CE             0xC7  /*!< Chip Erase instruction (Chip Erase)*/
#define sFLASH_WIP_FLAG           0x01  /*!< Write In Progress (WIP) flag */
#define sFLASH_CMD_RDID           0x9F  /*!< Read identification */
#define sFLASH_CMD_DeviceID                            0xAB
#define sFLASH_CMD_ManufactDeviceID            0x90
#define sFLASH_CMD_JedecDeviceID                0x9F
#define sFLASH_DUMMY_BYTE         0xFF

uint8_t sFlashBuff[4096];

//片选CS拉低
void sFLASH_CS_LOW(void)
{
    HAL_GPIO_WritePin(SPIFLASH_CS_GPIO_Port,SPIFLASH_CS_Pin,GPIO_PIN_RESET);
}       
//片选CS拉高
void sFLASH_CS_HIGH(void)
{
    HAL_GPIO_WritePin(SPIFLASH_CS_GPIO_Port,SPIFLASH_CS_Pin,GPIO_PIN_SET);
}       

//FLASH 发送一个字节
char sFLASH_SendByte(uint8_t byte)
{
//        unsigned char dr;
//  /*!< Loop while DR register in not emplty */
//  //while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
//        while((SPI2->SR & SPI_SR_TXE) == 0);

//  /*!< Send byte through the SPI1 peripheral */
//  //SPI_I2S_SendData(SPI1, byte);
//        SPI2->DR = byte;

//  /*!< Wait to receive a byte */
//  //while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
//        while((SPI2->SR & SPI_SR_RXNE) == 0);

//  /*!< Return the byte read from the SPI bus */
//  //return SPI_I2S_ReceiveData(SPI1);
//        dr = SPI2->DR;
//        return dr;
        uint8_t d_read,d_send = byte;
        if(HAL_SPI_TransmitReceive(&hspi2,&d_send,&d_read,1,0xFFFF) != HAL_OK)
        {
                d_read = sFLASH_DUMMY_BYTE;
        }
        return d_read;
}

//FLASH写使能
void sFLASH_WriteEnable(void)
{
  /*!< Select the FLASH: Chip Select low */
  sFLASH_CS_LOW();
  /*!< Send "Write Enable" instruction */
  sFLASH_SendByte(sFLASH_CMD_WREN);

  /*!< Deselect the FLASH: Chip Select high */
  sFLASH_CS_HIGH();
}

//FLASH读取一个字节
uint8_t sFLASH_ReadByte(void)
{
  return (sFLASH_SendByte(sFLASH_DUMMY_BYTE));
}
//FLASH等待写完成
void sFLASH_WaitForWriteEnd(void)
{
    uint8_t flashstatus = 0;
    HAL_Delay(1);
    /*!< Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();
    HAL_Delay(1);
    /*!< Send "Read Status Register" instruction */
    sFLASH_SendByte(sFLASH_CMD_RDSR);

    /*!< Loop as long as the memory is busy with a write cycle */
    do
    {
    /*!< Send a dummy byte to generate the clock needed by the FLASH
    and put the value of the status register in FLASH_Status variable */
    flashstatus = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    }
    while ((flashstatus & sFLASH_WIP_FLAG) == SET); /* Write in progress */

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();
}

//FLASH擦除一个扇区
void sFLASH_EraseSector(uint32_t SectorAddr)
{
  /*!< Send write enable instruction */
        sFLASH_WriteEnable();
  /*!< Sector Erase */
  /*!< Select the FLASH: Chip Select low */
  sFLASH_CS_LOW();
  /*!< Send Sector Erase instruction */
        sFLASH_SendByte(sFLASH_CMD_SE);
  /*!< Send SectorAddr high nibble address byte */
  sFLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  /*!< Send SectorAddr medium nibble address byte */
  sFLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  /*!< Send SectorAddr low nibble address byte */
  sFLASH_SendByte(SectorAddr & 0xFF);
  /*!< Deselect the FLASH: Chip Select high */
  sFLASH_CS_HIGH();

  /*!< Wait the end of Flash writing */
  sFLASH_WaitForWriteEnd();
}
//FLASH擦除整个片
void sFLASH_EraseChip(void)
{
  /*!< Send write enable instruction */
  sFLASH_WriteEnable();

  /*!< Bulk Erase */
  /*!< Select the FLASH: Chip Select low */
  sFLASH_CS_LOW();
  /*!< Send Bulk Erase instruction  */
  sFLASH_SendByte(sFLASH_CMD_CE);
  /*!< Deselect the FLASH: Chip Select high */
  sFLASH_CS_HIGH();

  /*!< Wait the end of Flash writing */
  sFLASH_WaitForWriteEnd();
}
//FLASH写一个页
void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    sFLASH_WriteEnable();
    sFLASH_CS_LOW();
    sFLASH_SendByte(sFLASH_CMD_WRITE);
    sFLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    sFLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    sFLASH_SendByte(WriteAddr & 0xFF);

    while (NumByteToWrite--)
    {
        sFLASH_SendByte(*pBuffer);
        pBuffer++;
    }

    sFLASH_CS_HIGH();
    sFLASH_WaitForWriteEnd();
}
//FLASH读取0-65536个
void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
    sFLASH_CS_LOW();
    sFLASH_SendByte(sFLASH_CMD_READ);
    sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    sFLASH_SendByte((ReadAddr& 0xFF00) >> 8);
    sFLASH_SendByte(ReadAddr & 0xFF);

    while (NumByteToRead--) /*!< while there is data to be read */
    {
        *pBuffer = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
        pBuffer++;
    }

    sFLASH_CS_HIGH();
}
//读取FLASH ID
uint32_t sFLASH_ReadID(void)
{
    uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
    sFLASH_CS_LOW();
    HAL_Delay(1);
    sFLASH_SendByte(sFLASH_CMD_RDID);
    Temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
    Temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
    Temp2 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    sFLASH_CS_HIGH();
    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

    return Temp;
}
//无检验写SPI FLASH
void sFLASH_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)   
{                                           
        uint16_t pageremain;          
        pageremain=256-WriteAddr%256; //单页剩余的字节数                             
        if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
        while(1)
        {          
                sFLASH_WritePage(pBuffer,WriteAddr,pageremain);
                if(NumByteToWrite==pageremain)
        {
            break;//写入结束了
        }
                 else //NumByteToWrite>pageremain
                {
                        pBuffer+=pageremain;
                        WriteAddr+=pageremain;       

                        NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
                        if(NumByteToWrite>256)
            {
                pageremain=256; //一次可以写入256个字节
            }
                        else
            {
                pageremain=NumByteToWrite;           //不够256个字节了
            }
                }
        }
}
//带擦除的写0-65536个字节函数
void sFLASH_WriteBuffer(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)   
{
        uint32_t secpos;
        uint16_t secoff;
        uint16_t secremain;          
        uint16_t i;   
        uint8_t * psFlashBuff;          
  psFlashBuff=sFlashBuff;             
        secpos=WriteAddr/4096;//扇区地址  
        secoff=WriteAddr%4096;//在扇区内的偏移
        secremain=4096-secoff;//扇区剩余空间大小   
        if(NumByteToWrite<=secremain)
    {
        secremain=NumByteToWrite;//不大于4096个字节
    }

        while(1)
        {       
                sFLASH_ReadBuffer(psFlashBuff,secpos*4096,4096);//读出整个扇区的内容      
                sFLASH_EraseSector(secpos*4096);                //擦除这个扇区
                for(i=0;i<secremain;i++)                           //复制
                {
                        psFlashBuff[i+secoff]=pBuffer[i];          
                }
                sFLASH_Write_NoCheck(psFlashBuff,secpos*4096,4096);//写入整个扇区  

                if(NumByteToWrite==secremain)
        {
            break;//写入结束了
        }
                else//写入未结束
                {
      secpos++;//扇区地址增1
      secoff=0;//偏移位置为0          

      pBuffer+=secremain;                                  //指针偏移
      WriteAddr+=secremain;                                //写地址偏移          
      NumByteToWrite-=secremain;                        //字节数递减
      if(NumByteToWrite>4096)
      {
        secremain=4096;//下一个扇区还是写不完
      }
      else
      {
        secremain=NumByteToWrite;                //下一个扇区可以写完了
      }
                }         
        }
}

#endif



使用特权

评论回复
11
renzheshengui|  楼主 | 2021-7-1 12:02 | 只看该作者
SPIFLASH.h


#ifndef __SPIFLASH_H__
#define __SPIFLASH_H__

/**Includes************************************************************************************/
#include "stm32f4xx_hal.h"
#include "spi.h"

/**Function declaration************************************************************************/


void sFLASH_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void sFLASH_EraseChip(void);
void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
uint32_t sFLASH_ReadID(void);
char sFLASH_SendByte(uint8_t byte);

void FileTest(void);//该句代码为测试代码,函数在user_diskio.c中定义
#endif



使用特权

评论回复
12
renzheshengui|  楼主 | 2021-7-1 12:02 | 只看该作者
10.在main.c函数调用如下函数检查是否读取出Flash的ID值,读取成功则说明SPI设置没问题
uint32_t Flash_ID = 0;

Flash_ID = sFLASH_ReadID();


使用特权

评论回复
13
renzheshengui|  楼主 | 2021-7-1 12:04 | 只看该作者
11. 在文件系统接口函数user_diskio.c中,修改为如下内容,我直接贴文件全部内容,以防因未粘贴部分设置问题导致的无法使用:

user_diskio.c

/* USER CODE BEGIN Header */
/**
******************************************************************************
  * @file    user_diskio.c
  * @brief   This file includes a diskio driver skeleton to be completed by the user.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */
/* USER CODE END Header */

#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/*
* Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
* To be suppressed in the future.
* Kept to ensure backward compatibility with previous CubeMx versions when
* migrating projects.
* User code previously added there should be copied in the new user sections before
* the section contents can be deleted.
*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif

/* USER CODE BEGIN DECL */

/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"

#include "spi.h"
#include "SPIFLASH.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;

#define PAGE_SIZE       256
#define SECTOR_SIZE     4096
#define SECTOR_COUNT        200
#define BLOCK_SIZE        65536
#define FLASH_PAGES_PER_SECTOR        SECTOR_SIZE/PAGE_SIZE

FATFS fs;
FIL file;                                                  /* 文件对象 */
FRESULT f_res;                    /* 文件操作结果 */
UINT fnum;                                                      /* 文件成功读写数量 */
BYTE ReadBuffer[1024]={0};        /* 读缓冲区 */
BYTE WriteBuffer[]= "WWB is the high hand\n";
BYTE work[4096];

char USER_Path[4];

void mount_disk(void)
{
   f_res = f_mount(&fs, USER_Path, 0);
   return;

}

void format_disk(void)
{
  uint8_t res = 0;
  //这里根据版本不同函数输入参数不一样
  f_res = f_mkfs(USER_Path, 1, 4096, work, sizeof(work));
}

UINT bw;
void create_file(void)
{
  FIL file;
  FIL *pf = &file;
  uint8_t res;
  f_res = f_open(pf, "0:/test.txt", FA_OPEN_ALWAYS | FA_WRITE);
  f_res = f_write(&file, WriteBuffer, sizeof(WriteBuffer), &bw);
  f_res = f_close(pf);
}

void read_file(void)
{
  FIL file;
  FRESULT res;
  uint8_t rbuf[100] = {0};
  f_res = f_open(&file, "0:/test.txt", FA_READ);
  f_res = f_read(&file, ReadBuffer, sizeof(WriteBuffer), &bw);
  f_res = f_close(&file);
}

void FileTest(void)
{
        mount_disk();                //文件系统注册
        format_disk();                //格式化文件系统
        create_file();                //建立文件并写入"WWB is the high hand\n"
        read_file();                //读取文件放到ReadBuffer中
}
/* USER CODE END DECL */

/* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
//#if _USE_WRITE == 1
  DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
//#endif /* _USE_WRITE == 1 */
//#if _USE_IOCTL == 1
  DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
//#endif /* _USE_IOCTL == 1 */

Diskio_drvTypeDef  USER_Driver =
{
  USER_initialize,
  USER_status,
  USER_read,
#if  _USE_WRITE == 1
  USER_write,
#endif  /* _USE_WRITE == 1 */
#if  _USE_IOCTL == 1
  USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};

/* Private functions ---------------------------------------------------------*/

/**
  * @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 */
    Stat = STA_NOINIT;
        if(sFLASH_ReadID() != 0)
                {
                        Stat &= ~STA_NOINIT;
                }       
    return Stat;
  /* USER CODE END INIT */
}

/**
  * @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 */
    Stat &= ~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 */
        DRESULT res = RES_ERROR;
        UINT i;

        for(i = 0;i < count;i++)
        {
                sFLASH_ReadBuffer(buff + i * 4096,sector * 4096 + i * 4096,4096 );
        }

        return RES_OK;
  /* 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 */
        DRESULT res = RES_ERROR;
       
        UINT i;

        for(i = 0;i < count;i++)
        {
                sFLASH_WriteBuffer((void *)(buff + i * 4096),sector * 4096 + i * 4096,4096 );
        }
       
        res = RES_OK;
  /* USER CODE HERE */
    return res;
  /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */

/**
  * @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_OK;
        switch(cmd)
        {
                case CTRL_SYNC :
                        break;       
         
                case CTRL_TRIM:
                        break;
                       
                case GET_BLOCK_SIZE:
                *(DWORD*)buff = BLOCK_SIZE;
                break;
                       
                case GET_SECTOR_SIZE:
                *(DWORD*)buff = SECTOR_SIZE;
                        break;
                       
                case GET_SECTOR_COUNT:
                *(DWORD*)buff = SECTOR_COUNT;
                break;
                               
                default:
                res = RES_PARERR;
                break;
          }
    return res;
  /* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */



使用特权

评论回复
14
renzheshengui|  楼主 | 2021-7-1 12:04 | 只看该作者
12.在main.c中调用FileTest();函数


使用特权

评论回复
15
renzheshengui|  楼主 | 2021-7-1 12:06 | 只看该作者
测试

编译下载后进入Debug模式,监视ReadBuffer数组【数据为空】和WriteBuffer数组配【数据为需要写入的数据】



使用特权

评论回复
16
renzheshengui|  楼主 | 2021-7-1 12:06 | 只看该作者

点击左上角运行代码,查看ReadBuffer数组是否读取到WriteBuffer中的写入值


使用特权

评论回复
17
renzheshengui|  楼主 | 2021-7-1 12:06 | 只看该作者
点击之后,稍微等待2秒左右【期间要初始化各种外设以及文件系统,再与Flash通信】得到如下结果,ReadBuffer内读取到了之前写入的内容

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

4079

帖子

2

粉丝