STM32CubeMX+FATFS+FREERTOS实现W25Q256虚拟U盘
创始人
2024-05-24 04:52:58
0

一、软件硬件说明

软件: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 ConfigurationPA9     ------> USART1_TXPA10     ------> 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 ConfigurationPA9     ------> USART1_TXPA10     ------> 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);            //关闭SPIhspi5.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时钟//PF6GPIO_Initure.Pin=GPIO_PIN_6;            //PF6GPIO_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>24)); }SPI5_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    SPI5_ReadWriteByte((u8)((WriteAddr)>>8));   SPI5_ReadWriteByte((u8)WriteAddr);   for(i=0;ipageremain{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;i4096)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 0XEF18extern 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      0xE9void 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控制功能
//具体实现思想,参考<>第五章(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** 

© Copyright (c) 2021 STMicroelectronics.* All rights reserved.

** 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 #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_SIZEFRESULT 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;}elsereturn FR_DISK_ERR;}elsereturn FR_DISK_ERR;}elsereturn 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 == 1DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count); #endif /* _USE_WRITE == 1 */ #if _USE_IOCTL == 1DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff); #endif /* _USE_IOCTL == 1 */Diskio_drvTypeDef USER_Driver = {USER_initialize,USER_status,USER_read, #if _USE_WRITEUSER_write, #endif /* _USE_WRITE == 1 */ #if _USE_IOCTL == 1USER_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);elsereturn -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

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...