STM32CubeMX+FATFS+FREERTOS实现W25Q256虚拟U盘

news2024/12/28 1:48:19

一、软件硬件说明

软件:STM32CubeMX V6.6.1 /KEIL5 V5.29

硬件:正点原子阿波罗开发板,核心板STM32F429IGT6(176脚)

使用USB_SLAVE接口连接电脑USB,从而读取W25Q256里的文件

W25Q256:SPI5接口

二、STM32CubeMX配置

  1. RCC配置

  1. SYS配置,选择SW方式,因为要用到FREERTOS,时钟源选TIM7,其他定时器也可以。

  1. GPIO配置,PB0和PB1控制两个LED灯,用来指示程序运行;PF6为SPI5的CS引脚,初始化时拉高,即不选中。

  1. USART1配置,方便输出调试信息,参数默认

  1. SPI5配置,刚开始速度配置低一点,256分频。W25Q256可以使用模式0(CPOL =0 CPOA=0 ) 或者模式3(CPOL=1 CPOA=1),这里配置为模式0。模式3可自行测试。

  1. USB_OTG_FS配置,参数默认

  1. USB_Device配置,MSC_MEDIA_PACKET参数改为4096

  1. FREERTOS配置,将默认任务的堆栈大小改为1024

  1. FATFS配置,修改如下

  1. 时钟配置,外部晶振为25MHz,时钟配置为168MHz

  1. 工程配置,堆栈设置大一些

  1. 生成代码,点击GENERATE CODE,生成基础代码

三、代码说明

串口重映射

usart.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.c
  * @brief   This file provides code for the configuration
  *          of the USART instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

UART_HandleTypeDef huart1;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
#include "stdio.h"
//加入以下代码,支持printf函数

#pragma import(__use_no_semihosting)

//标准库需要的支持函数
struct __FILE
{
    int handle;
};

FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
    x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
    while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
    USART1->DR = (int) ch;
    return ch;
}
/* USER CODE END 1 */

w25qxx.c

#include "w25qxx.h"
#include "spi.h"

u16 W25QXX_TYPE=W25Q256;    //默认是W25Q256

//4Kbytes为一个Sector
//16个sector为1个Block
//W25Q256
//容量为32M字节,共有512个Block,8192个Sector 


//SPI速度设置函数
//SPI速度=fAPB1/分频系数
//@ref SPI_BaudRate_Prescaler:SPI_BAUDRATEPRESCALER_2~SPI_BAUDRATEPRESCALER_2 256
//fAPB1时钟一般为45Mhz:
void SPI5_SetSpeed(u8 SPI_BaudRatePrescaler)
{
    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    __HAL_SPI_DISABLE(&hspi5);            //关闭SPI
    hspi5.Instance->CR1&=0XFFC7;          //位3-5清零,用来设置波特率
    hspi5.Instance->CR1|=SPI_BaudRatePrescaler;//设置SPI速度
    __HAL_SPI_ENABLE(&hspi5);             //使能SPI
    
}

//SPI5 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI5_ReadWriteByte(u8 TxData)
{
    u8 Rxdata;
    HAL_SPI_TransmitReceive(&hspi5,&TxData,&Rxdata,1, 1000);       
     return Rxdata;                      //返回收到的数据        
}

                                                     
//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{ 
    u8 temp;
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_GPIOF_CLK_ENABLE();           //使能GPIOF时钟
    
    //PF6
    GPIO_Initure.Pin=GPIO_PIN_6;            //PF6
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_FAST;     //快速         
    HAL_GPIO_Init(GPIOF,&GPIO_Initure);     //初始化
    
    W25QXX_CS=1;                            //SPI FLASH不选中
    SPI5_SetSpeed(SPI_BAUDRATEPRESCALER_2); //设置为45M时钟,高速模式
    W25QXX_TYPE=W25QXX_ReadID();            //读取FLASH ID.
    if(W25QXX_TYPE==W25Q256)                //SPI FLASH为W25Q256
    {
        temp=W25QXX_ReadSR(3);              //读取状态寄存器3,判断地址模式
        if((temp&0X01)==0)                    //如果不是4字节地址模式,则进入4字节地址模式
        {
            W25QXX_CS=0;                     //选中
            SPI5_ReadWriteByte(W25X_Enable4ByteAddr);//发送进入4字节地址模式指令   
            W25QXX_CS=1;                       //取消片选   
        }
    }
}  

//读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
//状态寄存器1:
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
//状态寄存器2:
//BIT7  6   5   4   3   2   1   0
//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
//状态寄存器3:
//BIT7      6    5    4   3   2   1   0
//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
//regno:状态寄存器号,范:1~3
//返回值:状态寄存器值
u8 W25QXX_ReadSR(u8 regno)   
{  
    u8 byte=0,command=0; 
    switch(regno)
    {
        case 1:
            command=W25X_ReadStatusReg1;    //读状态寄存器1指令
            break;
        case 2:
            command=W25X_ReadStatusReg2;    //读状态寄存器2指令
            break;
        case 3:
            command=W25X_ReadStatusReg3;    //读状态寄存器3指令
            break;
        default:
            command=W25X_ReadStatusReg1;    
            break;
    }    
    W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(command);            //发送读取状态寄存器命令    
    byte=SPI5_ReadWriteByte(0Xff);          //读取一个字节  
    W25QXX_CS=1;                            //取消片选     
    return byte;   
} 
//写W25QXX状态寄存器
void W25QXX_Write_SR(u8 regno,u8 sr)   
{   
    u8 command=0;
    switch(regno)
    {
        case 1:
            command=W25X_WriteStatusReg1;    //写状态寄存器1指令
            break;
        case 2:
            command=W25X_WriteStatusReg2;    //写状态寄存器2指令
            break;
        case 3:
            command=W25X_WriteStatusReg3;    //写状态寄存器3指令
            break;
        default:
            command=W25X_WriteStatusReg1;    
            break;
    }   
    W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(command);            //发送写取状态寄存器命令    
    SPI5_ReadWriteByte(sr);                 //写入一个字节  
    W25QXX_CS=1;                            //取消片选               
}   
//W25QXX写使能    
//将WEL置位   
void W25QXX_Write_Enable(void)   
{
    W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_WriteEnable);   //发送写使能  
    W25QXX_CS=1;                            //取消片选               
} 
//W25QXX写禁止    
//将WEL清零  
void W25QXX_Write_Disable(void)   
{  
    W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令    
    W25QXX_CS=1;                            //取消片选               
} 

//读取芯片ID
//返回值如下:                   
//0XEF13,表示芯片型号为W25Q80  
//0XEF14,表示芯片型号为W25Q16    
//0XEF15,表示芯片型号为W25Q32  
//0XEF16,表示芯片型号为W25Q64 
//0XEF17,表示芯片型号为W25Q128       
//0XEF18,表示芯片型号为W25Q256
u16 W25QXX_ReadID(void)
{
    u16 Temp = 0;      
    W25QXX_CS=0;                    
    SPI5_ReadWriteByte(0x90);//发送读取ID命令        
    SPI5_ReadWriteByte(0x00);         
    SPI5_ReadWriteByte(0x00);         
    SPI5_ReadWriteByte(0x00);                     
    Temp|=SPI5_ReadWriteByte(0xFF)<<8;  
    Temp|=SPI5_ReadWriteByte(0xFF);     
    W25QXX_CS=1;                    
    return Temp;
}               
//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
{ 
     u16 i;                                               
    W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_ReadData);      //发送读取命令  
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI5_ReadWriteByte((u8)((ReadAddr)>>24));    
    }
    SPI5_ReadWriteByte((u8)((ReadAddr)>>16));   //发送24bit地址    
    SPI5_ReadWriteByte((u8)((ReadAddr)>>8));   
    SPI5_ReadWriteByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
    { 
        pBuffer[i]=SPI5_ReadWriteByte(0XFF);    //循环读数  
    }
    W25QXX_CS=1;                                
}  
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!     
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
     u16 i;  
    W25QXX_Write_Enable();                  //SET WEL 
    W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_PageProgram);   //发送写页命令   
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI5_ReadWriteByte((u8)((WriteAddr)>>24)); 
    }
    SPI5_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    
    SPI5_ReadWriteByte((u8)((WriteAddr)>>8));   
    SPI5_ReadWriteByte((u8)WriteAddr);   
    for(i=0;i<NumByteToWrite;i++)SPI5_ReadWriteByte(pBuffer[i]);//循环写数  
    W25QXX_CS=1;                            //取消片选 
    W25QXX_Wait_Busy();                       //等待写入结束
} 
//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{                       
    u16 pageremain;       
    pageremain=256-WriteAddr%256; //单页剩余的字节数                 
    if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
    while(1)
    {       
        W25QXX_Write_Page(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个字节了
        }
    };        
} 
//写SPI FLASH  
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)                        
//NumByteToWrite:要写入的字节数(最大65535)   
u8 W25QXX_BUFFER[4096];         
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 
    u32 secpos;
    u16 secoff;
    u16 secremain;       
     u16 i;    
    u8 * W25QXX_BUF;      
       W25QXX_BUF=W25QXX_BUFFER;         
     secpos=WriteAddr/4096;//扇区地址  
    secoff=WriteAddr%4096;//在扇区内的偏移
    secremain=4096-secoff;//扇区剩余空间大小   
     //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
     if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
    while(1) 
    {    
        W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
        for(i=0;i<secremain;i++)//校验数据
        {
            if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除        
        }
        if(i<secremain)//需要擦除
        {
            W25QXX_Erase_Sector(secpos);//擦除这个扇区
            for(i=0;i<secremain;i++)       //复制
            {
                W25QXX_BUF[i+secoff]=pBuffer[i];      
            }
            W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  

        }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                    
        if(NumByteToWrite==secremain)break;//写入结束了
        else//写入未结束
        {
            secpos++;//扇区地址增1
            secoff=0;//偏移位置为0      

               pBuffer+=secremain;  //指针偏移
            WriteAddr+=secremain;//写地址偏移       
               NumByteToWrite-=secremain;                //字节数递减
            if(NumByteToWrite>4096)secremain=4096;    //下一个扇区还是写不完
            else secremain=NumByteToWrite;            //下一个扇区可以写完了
        }     
    };     
}
//擦除整个芯片          
//等待时间超长...
void W25QXX_Erase_Chip(void)   
{                                   
    W25QXX_Write_Enable();                  //SET WEL 
    W25QXX_Wait_Busy();   
      W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令  
    W25QXX_CS=1;                            //取消片选               
    W25QXX_Wait_Busy();                      //等待芯片擦除结束
}   
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25QXX_Erase_Sector(u32 Dst_Addr)   
{  
    //监视falsh擦除情况,测试用   
     //printf("fe:%x\r\n",Dst_Addr);      
     Dst_Addr*=4096;
    W25QXX_Write_Enable();                  //SET WEL      
    W25QXX_Wait_Busy();   
      W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_SectorErase);   //发送扇区擦除指令 
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI5_ReadWriteByte((u8)((Dst_Addr)>>24)); 
    }
    SPI5_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    
    SPI5_ReadWriteByte((u8)((Dst_Addr)>>8));   
    SPI5_ReadWriteByte((u8)Dst_Addr);  
    W25QXX_CS=1;                            //取消片选               
    W25QXX_Wait_Busy();                       //等待擦除完成
}  
//等待空闲
void W25QXX_Wait_Busy(void)   
{   
    while((W25QXX_ReadSR(1)&0x01)==0x01);   // 等待BUSY位清空
}  
//进入掉电模式
void W25QXX_PowerDown(void)   
{ 
      W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_PowerDown);     //发送掉电命令  
    W25QXX_CS=1;                            //取消片选               
    HAL_Delay(3);                            //等待TPD  
}   
//唤醒
void W25QXX_WAKEUP(void)   
{  
      W25QXX_CS=0;                                //使能器件   
    SPI5_ReadWriteByte(W25X_ReleasePowerDown);  //  send W25X_PowerDown command 0xAB    
    W25QXX_CS=1;                                //取消片选               
    HAL_Delay(3);                                //等待TRES1
}   

w25qxx.h

#ifndef __W25QXX_H
#define __W25QXX_H

#include "main.h"
    
//W25X系列/Q系列芯片列表       
#define W25Q80     0XEF13     
#define W25Q16     0XEF14
#define W25Q32     0XEF15
#define W25Q64     0XEF16
#define W25Q128    0XEF17
#define W25Q256 0XEF18

extern u16 W25QXX_TYPE;                    //定义W25QXX芯片型号           

#define    W25QXX_CS         PFout(6)          //W25QXX的片选信号

// 
//指令表
#define W25X_WriteEnable        0x06 
#define W25X_WriteDisable        0x04 
#define W25X_ReadStatusReg1        0x05 
#define W25X_ReadStatusReg2        0x35 
#define W25X_ReadStatusReg3        0x15 
#define W25X_WriteStatusReg1    0x01 
#define W25X_WriteStatusReg2    0x31 
#define W25X_WriteStatusReg3    0x11 
#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 
#define W25X_Enable4ByteAddr    0xB7
#define W25X_Exit4ByteAddr      0xE9

void W25QXX_Init(void);
u16  W25QXX_ReadID(void);                  //读取FLASH ID
u8 W25QXX_ReadSR(u8 regno);             //读取状态寄存器 
void W25QXX_4ByteAddr_Enable(void);     //使能4字节地址模式
void W25QXX_Write_SR(u8 regno,u8 sr);   //写状态寄存器
void W25QXX_Write_Enable(void);          //写使能 
void W25QXX_Write_Disable(void);        //写保护
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写入flash
void W25QXX_Erase_Chip(void);              //整片擦除
void W25QXX_Erase_Sector(u32 Dst_Addr);    //扇区擦除
void W25QXX_Wait_Busy(void);               //等待空闲
void W25QXX_PowerDown(void);            //进入掉电模式
void W25QXX_WAKEUP(void);                //唤醒

#endif

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "fatfs.h"
#include "spi.h"
#include "usart.h"
#include "usb_device.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "w25qxx.h"
#include "stdio.h"
#include "user_diskio.h"
/* 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 */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
//要写入到W25Q16的字符串数组
const uint8_t TEXT_Buffer[]= {"STM32CubeMX FATFS W25Q256 Test"};
#define SIZE sizeof(TEXT_Buffer)
uint8_t datatemp[SIZE];

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* 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 */
  W25QXX_Init();//W25QXX初始化
//  W25QXX_Write((u8*)TEXT_Buffer,0,SIZE);
//  HAL_Delay(100);
//  W25QXX_Read(datatemp,0,SIZE);
//  printf("read data:\r\n");
//  printf("%s\r\n",datatemp);

  FatfsTest();
  /* USER CODE END 2 */

  /* Init scheduler */
  osKernelInitialize();  /* Call init function for freertos objects (in freertos.c) */
  MX_FREERTOS_Init();

  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while (1)
    {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
        HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0|GPIO_PIN_1);
        HAL_Delay(500);

    }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM7 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM7) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1)
    {
    }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
       ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

main.h

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

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

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/
#define F_CS_Pin GPIO_PIN_6
#define F_CS_GPIO_Port GPIOF
#define LED1_Pin GPIO_PIN_0
#define LED1_GPIO_Port GPIOB
#define LED0_Pin GPIO_PIN_1
#define LED0_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
///
//定义一些常用的数据类型短关键字 
typedef int32_t  s32;
typedef int16_t s16;
typedef int8_t  s8;

typedef const int32_t sc32;  
typedef const int16_t sc16;  
typedef const int8_t sc8;  

typedef __IO int32_t  vs32;
typedef __IO int16_t  vs16;
typedef __IO int8_t   vs8;

typedef __I int32_t vsc32;  
typedef __I int16_t vsc16; 
typedef __I int8_t vsc8;   

typedef uint32_t  u32;
typedef uint16_t u16;
typedef uint8_t  u8;

typedef const uint32_t uc32;  
typedef const uint16_t uc16;  
typedef const uint8_t uc8; 

typedef __IO uint32_t  vu32;
typedef __IO uint16_t vu16;
typedef __IO uint8_t  vu8;

typedef __I uint32_t vuc32;  
typedef __I uint16_t vuc16; 
typedef __I uint8_t vuc8;  
     
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014 
#define GPIOJ_ODR_ADDr    (GPIOJ_BASE+20) //0x40022414
#define GPIOK_ODR_ADDr    (GPIOK_BASE+20) //0x40022814

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
#define GPIOJ_IDR_Addr    (GPIOJ_BASE+16) //0x40022410 
#define GPIOK_IDR_Addr    (GPIOK_BASE+16) //0x40022810 

//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

#define PJout(n)   BIT_ADDR(GPIOJ_ODR_Addr,n)  //输出 
#define PJin(n)    BIT_ADDR(GPIOJ_IDR_Addr,n)  //输入

#define PKout(n)   BIT_ADDR(GPIOK_ODR_Addr,n)  //输出 
#define PKin(n)    BIT_ADDR(GPIOK_IDR_Addr,n)  //输入
/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

freertos.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* 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 */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 1024 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void *argument);

extern void MX_USB_DEVICE_Init(void);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

}

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* init code for USB_DEVICE */
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  for(;;)
  {
      HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0|GPIO_PIN_1);
      osDelay(500);
  }
  /* USER CODE END StartDefaultTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

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 "w25qxx.h"
#include "stdio.h"
#include "fatfs.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    4096   //8192
#define BLOCK_SIZE        65536
#define FLASH_PAGES_PER_SECTOR    SECTOR_SIZE/PAGE_SIZE

FRESULT f_res;                     
UINT bw;
BYTE ReadBuffer[1024]={0};       
BYTE WriteBuffer[]= "STM32CubeMX W25QXX FATFS FREERTOS Test\r\n";
BYTE work[4096];

void mount_disk(void)
{
   f_res = f_mount(&USERFatFS, USERPath, 0);
}

void format_disk(void)
{
  f_res = f_mkfs(USERPath, FM_FAT, 4096, work, sizeof(work));
}

void create_file(void)
{
    printf("write data is : %s\r\n",WriteBuffer);
    f_res = f_open(&USERFile, "test.txt", FA_OPEN_ALWAYS | FA_WRITE);
    f_res = f_write(&USERFile, WriteBuffer, sizeof(WriteBuffer), &bw);
    f_res = f_close(&USERFile);
}

void read_file(void)
{
    f_res = f_open(&USERFile, "test.txt", FA_READ);
    f_res = f_read(&USERFile, ReadBuffer, sizeof(WriteBuffer), &bw);
    printf("read data is : %s\r\n",ReadBuffer);
    f_res = f_close(&USERFile);
}

FRESULT  fileSystemInit()
{
    FRESULT res = FR_OK;
    res = f_mount(&USERFatFS, USERPath, 1);
    if (res != FR_OK)
    {
        //No Disk file system,format disk !
        res = f_mkfs(USERPath, FM_FAT, 4096, work, sizeof( work));
        if (res == FR_OK)
        {
            res = f_mount(&USERFatFS, USERPath, 1);
            if (res == 0)
            {
                return FR_OK;
            }
            else
                return FR_DISK_ERR;
        }
        else
            return FR_DISK_ERR;
    }
    else
        return FR_OK;
}

//第一次运行文件系统,需要先注册文件系统和格式化
void FatfsTest(void)
{
    fileSystemInit();
    create_file();            //创建TXT文件 
    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
  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(W25QXX_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++)
        {
            W25QXX_Read(buff + i * SECTOR_SIZE,sector * SECTOR_SIZE + i * SECTOR_SIZE,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++)
        {
            W25QXX_Write((void *)(buff + i * SECTOR_SIZE),sector * SECTOR_SIZE + i * SECTOR_SIZE,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 */

usbd_storage_if.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : usbd_storage_if.c
  * @version        : v1.0_Cube
  * @brief          : Memory management layer.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"

/* USER CODE BEGIN INCLUDE */
#include "w25qxx.h"
/* USER CODE END INCLUDE */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/

/* USER CODE END PV */

/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @brief Usb device.
  * @{
  */

/** @defgroup USBD_STORAGE
  * @brief Usb mass storage device module
  * @{
  */

/** @defgroup USBD_STORAGE_Private_TypesDefinitions
  * @brief Private types.
  * @{
  */

/* USER CODE BEGIN PRIVATE_TYPES */

/* USER CODE END PRIVATE_TYPES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Defines
  * @brief Private defines.
  * @{
  */

//#define STORAGE_LUN_NBR                  1
//#define STORAGE_BLK_NBR                  0x10000//65536
//#define STORAGE_BLK_SIZ                  0x200//512

/* USER CODE BEGIN PRIVATE_DEFINES */
#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  4096//8192
#define STORAGE_BLK_SIZ                  4096
/* USER CODE END PRIVATE_DEFINES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Macros
  * @brief Private macros.
  * @{
  */

/* USER CODE BEGIN PRIVATE_MACRO */

/* USER CODE END PRIVATE_MACRO */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Variables
  * @brief Private variables.
  * @{
  */

/* USER CODE BEGIN INQUIRY_DATA_FS */
/** USB Mass storage Standard Inquiry Data. */
const int8_t STORAGE_Inquirydata_FS[] = {/* 36 */

  /* LUN 0 */
  0x00,
  0x80,
  0x02,
  0x02,
  (STANDARD_INQUIRY_DATA_LEN - 5),
  0x00,
  0x00,
  0x00,
  'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
  'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product      : 16 Bytes */
  ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
  '0', '.', '0' ,'1'                      /* Version      : 4 Bytes */
};
/* USER CODE END INQUIRY_DATA_FS */

/* USER CODE BEGIN PRIVATE_VARIABLES */

/* USER CODE END PRIVATE_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Exported_Variables
  * @brief Public variables.
  * @{
  */

extern USBD_HandleTypeDef hUsbDeviceFS;

/* USER CODE BEGIN EXPORTED_VARIABLES */

/* USER CODE END EXPORTED_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_FunctionPrototypes
  * @brief Private functions declaration.
  * @{
  */

static int8_t STORAGE_Init_FS(uint8_t lun);
static int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
static int8_t STORAGE_IsReady_FS(uint8_t lun);
static int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
static int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_GetMaxLun_FS(void);

/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */

/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */

/**
  * @}
  */

USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
{
  STORAGE_Init_FS,
  STORAGE_GetCapacity_FS,
  STORAGE_IsReady_FS,
  STORAGE_IsWriteProtected_FS,
  STORAGE_Read_FS,
  STORAGE_Write_FS,
  STORAGE_GetMaxLun_FS,
  (int8_t *)STORAGE_Inquirydata_FS
};

/* Private functions ---------------------------------------------------------*/
/**
  * @brief  Initializes the storage unit (medium) over USB FS IP
  * @param  lun: Logical unit number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Init_FS(uint8_t lun)
{
  /* USER CODE BEGIN 2 */
 //UNUSED(lun);
  W25QXX_Init();
  return (USBD_OK);
  /* USER CODE END 2 */
}

/**
  * @brief  Returns the medium capacity.
  * @param  lun: Logical unit number.
  * @param  block_num: Number of total block number.
  * @param  block_size: Block size.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */
  //UNUSED(lun);

  *block_num  = STORAGE_BLK_NBR;
  *block_size = STORAGE_BLK_SIZ;
  return (USBD_OK);
  /* USER CODE END 3 */
}

/**
  * @brief   Checks whether the medium is ready.
  * @param  lun:  Logical unit number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
  /* USER CODE BEGIN 4 */
    //UNUSED(lun);
    if(W25QXX_ReadID() != 0)
        return (USBD_OK);
    else
        return -1;
  /* USER CODE END 4 */
}

/**
  * @brief  Checks whether the medium is write protected.
  * @param  lun: Logical unit number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
{
  /* USER CODE BEGIN 5 */
  //UNUSED(lun);

  return (USBD_OK);
  /* USER CODE END 5 */
}

/**
  * @brief  Reads data from the medium.
  * @param  lun: Logical unit number.
  * @param  buf: data buffer.
  * @param  blk_addr: Logical block address.
  * @param  blk_len: Blocks number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */
    //UNUSED(lun);
    uint32_t i = 0;
    
    for(i = 0;i < blk_len;i++)
    {
      W25QXX_Read(buf + i * STORAGE_BLK_SIZ,blk_addr * STORAGE_BLK_SIZ + i * STORAGE_BLK_SIZ,STORAGE_BLK_SIZ );
    }
    
    return (USBD_OK);

  /* USER CODE END 6 */
}

/**
  * @brief  Writes data into the medium.
  * @param  lun: Logical unit number.
  * @param  buf: data buffer.
  * @param  blk_addr: Logical block address.
  * @param  blk_len: Blocks number.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */
    //UNUSED(lun);
    uint32_t i = 0;

    for(i = 0;i < blk_len;i++)
    {
      W25QXX_Write((void *)(buf + i * STORAGE_BLK_SIZ),blk_addr * STORAGE_BLK_SIZ + i * STORAGE_BLK_SIZ,STORAGE_BLK_SIZ );
    }
    
    return (USBD_OK);

  /* USER CODE END 7 */
}

/**
  * @brief  Returns the Max Supported LUNs.
  * @param  None
  * @retval Lun(s) number.
  */
int8_t STORAGE_GetMaxLun_FS(void)
{
  /* USER CODE BEGIN 8 */
  return (STORAGE_LUN_NBR - 1);
  /* USER CODE END 8 */
}

/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */

/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */

/**
  * @}
  */

/**
  * @}
  */

四、程序运行截图

五、几点讨论

本次实验用的时W25Q256,芯片大小为32M字节,但是虚拟出来的U盘大小实际只有16M,尝试修改了SECTOR_COUNT的大小为8192,W25Q256有8192个扇区,每个扇区大小为4096,总容量=8192*4096/1024/1024 = 32M字节,改了后虚拟U盘大小还是16M.

user_diskio.c

usbd_storage_if.c

也尝试修改了STORAGE_BLK_NBR的大小为8192,虚拟U盘大小也是16M。知道如何设置U盘大小的朋友请留言,非常感谢。

六、完整工程链接

工程链接:https://download.csdn.net/download/chen18221987993/87431500

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/333459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lecture3 梯度下降(Gradient Descent)

目录 1 问题背景 2 批量梯度下降 (Batch Gradient Descent) 3 鞍点(Saddle Point) 3 随机梯度下降 (Stochastic Gradient Descent) 4 小批量梯度下降 (Mini-batch Gradient Descent) 1 问题背景 图1 上节课讲述的穷举法求最优权重值在Lecture2中&#xff0c;介绍了使用穷举…

python+django校园在线拍卖系统pycharm项目源码

登录页和注册页 管理员、用户和拍卖者都可以在此页面登录到该系统 拍卖者注册的页面&#xff0c;拍卖者如果没有账号可以点击注册进入到系统 开发语言&#xff1a;Python 框架&#xff1a;django Python版本&#xff1a;python3.7.7 数据库&#xff1a;mysql 数据库工具&…

在国内有几个CCIE考点?

笔试在VUE指定思科考试中心报考加考试; 实验考试在国内只有北京和香港两个考点。还有一些其他考点&#xff0c;下面让我们一起来看一下。 CCIE笔试考点 思科ccie认证的笔试考试地点都在Vue考试中心,Vue考试中心是思科官方授权的考试地点,在国内大部分城市都有分布 CCIE笔试报…

基于ssm的航空售票系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经从做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xf…

c# 将数据导出到EXCEL文件

第一步&#xff1a;项目中加入引用。 在鼠标右击项目&#xff0c;点击【添加】弹出菜单列表&#xff0c;选择【项目引用】弹出【引用管理器】对话框&#xff0c;选择【COM】-【Microsoft Excel 16.0 Object Library】&#xff0c;如图所示&#xff1a; 第二步&#xff0c;编辑…

【项目精选】基于Java的敬老院管理系统的设计和实现

本系统主要是针对敬老院工作人员即管理员和员工设计的。敬老院管理系统 将IT技术为养老院提供一个接口便于管理信息,存储老人个人信息和其他信息,查找 和更新信息的养老院档案,节省了员工的劳动时间,大大降低了成本。 其主要功能包括&#xff1a; 系统管理员用户功能介绍&#…

体系结构概述

目录1.系统模型操作模式和状态寄存器和特殊寄存器2.存储器系统3.栈空间操作4.异常和中断5.嵌套向量中断控制器&#xff08;NVIC&#xff09;1.系统模型 操作模式和状态 Cortex-M0处理器包含两种操作模式和两种状态。 处理器在运行程序时处于Thumb状态&#xff0c;在这种状态…

个人信息保护认证

个人信息保护认证是证明个人信息处理者在认证范围内开展的个人信息收集、存储、使用、加工、传输、提供、公开、删除以及跨境等处理活动符合认证依据标准要求。适用范围 本规则依据《中华人民共和国认证认可条例》制定&#xff0c;规定了对个人信息处理者开展个人信息收集、存储…

【漏洞复现】phpStudy 小皮 Windows面板 RCE漏洞

文章目录前言一、漏洞描述二、漏洞复现前言 本篇文章仅用于漏洞复现研究和学习&#xff0c;切勿从事非法攻击行为&#xff0c;切记&#xff01; 一、漏洞描述 Phpstudy小皮面板存在RCE漏洞&#xff0c;通过分析和复现方式发现其实本质上是一个存储型XSS漏洞导致的RCE。通过系…

关于IB课程,你需要知道些什么?

1IB课程到底是什么&#xff1f; IB课程 IB课程是由国际文凭组织IBO&#xff08;International Baccalaureate Organization&#xff09;开设的、针对3-19岁学生的、从幼儿园到大学预科的课程&#xff0c;服务对象为全球3-19岁的学生。IBO历史 1968年IBO组织在瑞士日内瓦成立&am…

php mysql保健品购物商城系统

目 录 1 绪论 1 1.1 开发背景 1 1.2 研究的目的和意义 1 1.3 研究现状 2 2 开发技术介绍 2 2.1 B/S体系结构 2 2.2 PHP技术 3 2.3 MYSQL数据库 4 2.4 Apache 服务器 5 2.5 WAMP 5 2.6 系统对软硬件要求 6 …

2.计算机基础-计算机网络面试题—基础知识、容器、面向对象、并发编程

本文目录如下&#xff1a;计算机基础-计算机网络 面试题一、基础知识简述 TCP 和 UDP 的区别&#xff1f;http与https的区别?Session 和 Cookie 有什么区别&#xff1f;URL是什么&#xff1f;由哪些部分组成&#xff1f;OSI 的 五层模型 都有哪些&#xff1f;get 和 post 请求…

CIAM 如何平衡数据安全与客户体验?| 身份云研究院

普华永道研究表明&#xff0c;32% 的用户会因为一次体验不佳而放弃使用一个产品。无独有偶&#xff0c;据数据分析公司 Preact 研究显示&#xff0c;首次注册、登录或验证是最主要的用户流失环节&#xff0c;占整体流失率的 22.9%。 对于任何在网上做生意的公司来说&#xff0…

MySQL为什么要改进LRU算法?

普通LRU算法 LRU算法介绍 LRU Least Recently Used&#xff08;最近最少使用&#xff09;&#xff1a;也就是末尾淘汰法&#xff0c;新数据从链表头部加入&#xff0c;释放空间时从末尾淘汰数据。 1.当要访问某个页时&#xff0c;如果不在Buffer Pool中&#xff0c;需要把该…

java实现电子发票中的发票税号等信息识别的几种可用方案

先说一下背景&#xff1a;今天领导突然说需要做一个电子发票中发票税号的识别&#xff0c;于是乎就开始去调研看有哪些方案&#xff0c;最先想到的就是OCR文字识别&#xff0c;自己去画框训练模型去识别税号等相关信息话不多说开整思路&#xff1a;思路一&#xff1a;百度AI平台…

逻辑仿真工具VCS的使用-Makefile

Gvim写RTL code&#xff0c;VCS仿真&#xff0c;Verdi看波形&#xff0c;DC做综合下约束&#xff0c;Primetime做STA&#xff0c;Spyglass做异步时序分析。 VCS全称Verilog Computer Simulation &#xff0c;VCS是逻辑仿真EDA工具的编译源代码的命令。要用VCS做编译仿…

C进阶:预处理

&#x1f916;本篇文章主要讲解预处理的知识&#xff0c;即使你是小白也可以看的懂&#xff0c;若你对预处理有所不解&#xff0c;确定不来看看吗&#xff1f;&#x1f63f; 目录 一.代码运行是的两种环境 二.翻译环境 三.预定义符号 四.#define 1.define 定义宏 2.带有…

有哪些必知人工智能著名应用?

当前有哪些人工智能的著名应用&#xff1f; 生成式人工智能 输入要求&#xff0c;输出结果 趣讲大白话&#xff1a;不要小看科技进步哦 *********** 人工智能引领智能时代 见图 【安志强趣讲信息科技】 掌握信息科技常识&#xff0c;未来竞争才不吃亏 世纪之问&#xff1a;做…

一篇文章读懂阿里云企业级数据库最佳实践

今天阿里数据库不再是简单的电商业务&#xff0c;而是涵盖了视频娱乐、IM、地图、在线零售、新零售、物流、在线旅游、音乐、IoT等纵多领域。2017年双十一交易额达1682亿&#xff0c;数据库交易峰值也以数十倍的速度在增长。超大规模的业务压力&#xff0c;在阿里巴巴内部淬炼出…

Chrome 浏览器的四大进程

一个进程就是一个程序的运行实例。详细解释就是&#xff0c;启动一个程序的时候&#xff0c;操作系统会为该程序创建一块内存&#xff0c;用来存放代码、运行中的数据和一个执行任务的主线程&#xff0c;我们把这样的一个运行环境叫进程。 总结来说&#xff0c;进程和线程之间的…