STM32F446移植USB驅動,實現外部FLASH模擬U盤

USB(Universal Serial Bus通用串行總線)是一個外部總線標準,用於規範電腦與外部設備的連接和通訊,是應用在PC領域的接口技術,支持設備的即插即用和熱插拔功能。STM32芯片自帶有USB功能,帶來了更多的可玩性,但也更加複雜,之前在STM32F103上實現了USB庫的移植,由於F103的USB功能簡單,移植比較順利,具體的方法見本人的博客:STM32F1移植USB庫實現外部FLASH模擬U盤功能
相較於 STMF1,STM32F4 的功能大爲增強,引入了 OTG 功能,最近入手了一塊 NUCLEO-64 開發板,板載芯片是 STM32F446RET6,嘗試在上面移植 USB 的 MSC 驅動。
在參考了野火和原子的例程後,沒能成功移植官網的USB庫,其中一個原因在於例程所使用的芯片是 STM32F429,這一芯片的時鐘樹結構與 STM32F446 存在差異。從 CubeMX 上可以看出,STM32F429 的 USB 時鐘只能來自PLL,如圖1,無法在運行於推薦的180MHz主頻的情況下獲得48MHz的USB 時鐘,只能採取降頻至144MHz,或超頻至192MHz(野火和原子的例程採用的方法)。
圖1
圖1. STM32F429IGTx時鐘樹
相比於此,STM32F446RE 的 USB 時鐘來源可以選擇 PLLSAIP,由 PLLSAI 倍頻產生,這就同時滿足了180MHz的運行頻率和 USB 的48MHz時鐘的要求。
在這裏插入圖片描述
圖2. STM32F446RETx時鐘樹
由於與 PLLSAI 相關的資料和程序很少,而且 USB 庫的移植相對來說確實麻煩,所以採用 CubeMX 來配置HAL庫,實現外部FLASH 模擬U盤的功能。

移植過程
1、在 CubeMX 中選擇相對應的芯片,配置 RCC,如下圖:
在這裏插入圖片描述
2、在 Connectivity 中選擇 USB_OTG_FS(全速模式),配置爲 Device_Only 模式,如下圖:
在這裏插入圖片描述
3、在 Middleware 中選擇 USB_DEVICE,在 Class For FS IP 中選擇 Mass Storage Class,由於採用的存儲介質是 FLASH,所以MSC_MEDIA_PACKET 配置爲 4096 bytes,如果是SD卡採用默認的 512 bytes 配置,如下圖:
在這裏插入圖片描述
4、配置 USB 的 中斷優先級爲5,如下圖:
在這裏插入圖片描述
5、配置時鐘樹,如下圖:
在這裏插入圖片描述
6、配置堆空間 Minimum Heap Size 爲 0x1200,作爲 USB 的緩衝,如下圖:
在這裏插入圖片描述
7、由於之前已經寫好了外部 FLASH 移植 FATFS 的程序,不想重複折騰,這裏將與 USB 驅動相關的文件拷貝出來,直接添加到之前的工程中。CubeMX 生成的與USB相關的文件位於:
“…\Middlewares\ST\STM32_USB_Device_Library”
在這裏插入圖片描述
…\Src中的文件
在這裏插入圖片描述
…\Inc中的文件
在這裏插入圖片描述
將上述文件拷貝至之前的移植FATFS文件系統的工程文件下,如下圖
在這裏插入圖片描述
並將stm32f4xx_hal_msp.c文件也拷貝到User文件夾,打開工程添加USB庫文件代碼如下:
在這裏插入圖片描述
8、編譯後存在兩個錯誤:
在這裏插入圖片描述
這兩個錯誤都是由於將函數定義爲了靜態函數,在 main.c 文件中將對應函數的 static 關鍵字去掉,並在 main.h 中聲明 Error_Handler() 函數。
在 main.c 中的時鐘初始化函數添加PLLSAI的設置,可以複製CubeMX生成工程中的 main.c 文件中的時鐘初始化函數

void SystemClock_Config(void)
{
  	RCC_ClkInitTypeDef RCC_ClkInitStruct;
  	RCC_OscInitTypeDef RCC_OscInitStruct;
	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
	
	/** Configure the main internal regulator output voltage 
  	*/
  	__HAL_RCC_PWR_CLK_ENABLE();
  	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
	
	/* 使能HSE,配置HSE爲PLL的時鐘源,配置PLL的各種分頻因子M N P Q 
	 * PLLCLK = HSE/M*N/P = 12M / 25 *360 / 2 = 180M
	 */
	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 = 8;
  	RCC_OscInitStruct.PLL.PLLN = 360;
  	RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
 	RCC_OscInitStruct.PLL.PLLQ = 7;
 	RCC_OscInitStruct.PLL.PLLR = 2;
	
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  	{
   		Error_Handler();
  	}	
	
	/** Activate the Over-Drive mode 
  	*/
  	if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  	{
    	Error_Handler();
  	}
	
	 /** Initializes the CPU, AHB and APB busses clocks 
  	*/
	/* 選擇PLLCLK作爲SYSCLK,並配置 HCLK, PCLK1 and PCLK2 的時鐘分頻因子 
	 * SYSCLK = PLLCLK     = 180M
	 * HCLK   = SYSCLK / 1 = 180M
	 * PCLK2  = SYSCLK / 2 = 90M
	 * PCLK1  = SYSCLK / 4 = 45M
	 */
  	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();
  	}
	
	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_CLK48;
  	PeriphClkInitStruct.PLLSAI.PLLSAIM = 4;
  	PeriphClkInitStruct.PLLSAI.PLLSAIN = 96;
  	PeriphClkInitStruct.PLLSAI.PLLSAIQ = 2;
  	PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV4;
  	PeriphClkInitStruct.PLLSAIDivQ = 1;
  	PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48CLKSOURCE_PLLSAIP;
  	if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  	{
    	Error_Handler();
  	}
}

9、在 usbd_storage_if.c 中添加底層讀寫 FLASH 的代碼:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : usbd_storage_if.c
  * @version        : v1.0_Cube
  * @brief          : Memory management layer.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 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 */

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

/* USER CODE BEGIN INCLUDE */
#include "./flash/bsp_spi_flash.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                  4096//0x10000
#define STORAGE_BLK_SIZ                  4096//0x200

/* USER CODE BEGIN PRIVATE_DEFINES */

/* 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 over USB FS IP
  * @param  lun:
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Init_FS(uint8_t lun)
{
	/* USER CODE BEGIN 2 */
	SPI_FLASH_Init();
  	if (SPI_FLASH_ReadID() != sFLASH_ID)
  	{
		//printf("error\r\n");
    	return (USBD_FAIL);
  	}
  	return (USBD_OK);
  	/* USER CODE END 2 */
}

/**
  * @brief  .
  * @param  lun: .
  * @param  block_num: .
  * @param  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 */
 	*block_num  = STORAGE_BLK_NBR;
  	*block_size = STORAGE_BLK_SIZ;
  	return (USBD_OK);
  	/* USER CODE END 3 */
}

/**
  * @brief  .
  * @param  lun: .
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
	/* USER CODE BEGIN 4 */
	if (SPI_FLASH_ReadID() == sFLASH_ID)
  	{
		//printf("error\r\n");
    	return (USBD_OK);
  	}
	else
	{
		return (USBD_FAIL);
	}
  //return (USBD_OK);
  /* USER CODE END 4 */
}

/**
  * @brief  .
  * @param  lun: .
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
{
  /* USER CODE BEGIN 5 */
  return (USBD_OK);
  /* USER CODE END 5 */
}

/**
  * @brief  .
  * @param  lun: .
  * @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 */
	SPI_FLASH_BufferRead(buf, blk_addr*STORAGE_BLK_SIZ, blk_len*STORAGE_BLK_SIZ);
  	return (USBD_OK);
  	/* USER CODE END 6 */
}

/**
  * @brief  .
  * @param  lun: .
  * @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 */
	SPI_FLASH_SectorErase(blk_addr*STORAGE_BLK_SIZ);
	SPI_FLASH_BufferWrite(buf, blk_addr*STORAGE_BLK_SIZ, blk_len*STORAGE_BLK_SIZ);
  	return (USBD_OK);
  	/* USER CODE END 7 */
}

/**
  * @brief  .
  * @param  None
  * @retval .
  */
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 */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

10、修改啓動文件中的堆空間大小
在這裏插入圖片描述
11、在 stm32f4xx_it.c 添加 USB 中斷服務函數

/**
  ******************************************************************************
  * @file    Templates/Src/stm32f4xx_it.c 
  * @author  MCD Application Team
  * @brief   Main Interrupt Service Routines.
  *          This file provides template for all exceptions handler and 
  *          peripherals interrupt service routine.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

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

extern PCD_HandleTypeDef hpcd_USB_OTG_FS;
/** @addtogroup STM32F4xx_HAL_Examples
  * @{
  */

/** @addtogroup Templates
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/******************************************************************************/
/*            Cortex-M4 Processor Exceptions Handlers                         */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Memory Manage exception.
  * @param  None
  * @retval None
  */
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Bus Fault exception.
  * @param  None
  * @retval None
  */
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Usage Fault exception.
  * @param  None
  * @retval None
  */
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles Debug Monitor exception.
  * @param  None
  * @retval None
  */
void DebugMon_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  HAL_IncTick();
}

/******************************************************************************/
/*                 STM32F4xx Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_stm32f4xx.s).                                               */
/******************************************************************************/

/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
/*void PPP_IRQHandler(void)
{
}*/

/**
  * @}
  */ 

/**
  * @}
  */
	
/**
  * @brief This function handles USB On The Go FS global interrupt.
  */
void OTG_FS_IRQHandler(void)
{
  /* USER CODE BEGIN OTG_FS_IRQn 0 */
	//printf("111\r\n");
  /* USER CODE END OTG_FS_IRQn 0 */
  HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
  /* USER CODE BEGIN OTG_FS_IRQn 1 */

  /* USER CODE END OTG_FS_IRQn 1 */
}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

12、在 main.c 中添加 USB 庫相關頭文件,並初始化 USB 外設

/**
  ******************************************************************************
  * @file    main.c
  * @author  Asher
  * @version V1.0
  * @date    2020-05-16
  * @brief   外部FLASH模擬U盤
  ******************************************************************************
  * @attention
  *
  * 實驗平臺:  STM32F446RET6 
  * 
  * 
  *
  ******************************************************************************
  */


/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "./usart/bsp_usart.h"
#include "./flash/bsp_spi_flash.h"
#include "ff.h"
#include "usb_device.h"
#include "usbd_core.h"

//函數聲明
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);

//全局變量
FATFS fs;							/* FatFs文件系統對象 */
FIL fnew;							/* 文件對象 */
FRESULT res_flash;                	/* 文件操作結果 */
UINT fnum;            				/* 文件成功讀寫數量 */
BYTE ReadBuffer[1024]={0};        	/* 讀緩衝區 */
BYTE WriteBuffer[] = "FatFs文件系統測試數據。\r\n"; 	/* 寫緩衝區*/
BYTE work[FF_MAX_SS];

/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{
  	HAL_Init();
  	SystemClock_Config();
	//初始化USB
	MX_GPIO_Init();
	MX_USB_DEVICE_Init();
	//初始化串口
	USART_Config();
	
	printf("****** FatFs文件系統實驗 ******\r\n");
	//在外部SPI Flash掛載文件系統,文件系統掛載時會對SPI設備初始化
	//初始化函數調用流程如下
	//f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()
	res_flash = f_mount(&fs,"0:",1);
	
	/*----------------------- 格式化測試 -----------------*/  
	/* 如果沒有文件系統就格式化創建創建文件系統 */
	if(res_flash == FR_NO_FILESYSTEM)
	{
		printf("》FLASH還沒有文件系統,即將進行格式化...\r\n");
    	/* 格式化 */
		res_flash = f_mkfs("0:",0,work,sizeof work);			
		
		if(res_flash == FR_OK)
		{
			printf("》FLASH已成功格式化文件系統。\r\n");

      		/* 格式化後,先取消掛載 */
			res_flash = f_mount(NULL,"0:",1);			
      		/* 重新掛載*/			
			res_flash = f_mount(&fs,"0:",1);
		}
		else
		{
			printf("《《格式化失敗。(%d)》》\r\n",res_flash);
			while(1);
		}
	}
  	else if(res_flash!=FR_OK)
  	{
    	printf("!!外部Flash掛載文件系統失敗。(%d)\r\n",res_flash);
    	printf("!!可能原因:SPI Flash初始化不成功。\r\n");
		while(1);
  	}
  	else
  	{
    	printf("》文件系統掛載成功,可以進行讀寫測試\r\n");
 	}
  
	/*----------------------- 文件系統測試:寫測試 -------------------*/
	/* 打開文件,每次都以新建的形式打開,屬性爲可寫 */
	printf("\r\n****** 即將進行文件寫入測試... ******\r\n");	
	res_flash = f_open(&fnew, "0:FatFs讀寫測試文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
	if ( res_flash == FR_OK )
	{
		printf("》打開/創建FatFs讀寫測試文件.txt文件成功,向文件寫入數據。\r\n");
    	/* 將指定存儲區內容寫入到文件內 */
		res_flash=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
    	if(res_flash==FR_OK)
    	{
      		printf("》文件寫入成功,寫入字節數據:%d\n",fnum);
      		printf("》向文件寫入的數據爲:\r\n%s\r\n",WriteBuffer);
    	}
    	else
    	{
      		printf("!!文件寫入失敗:(%d)\n",res_flash);
    	}    
		/* 不再讀寫,關閉文件 */
    	f_close(&fnew);
	}
	else
	{	
		printf("!!打開/創建文件失敗。(%d)\r\n",res_flash);
	}
	
	/*------------------- 文件系統測試:讀測試 --------------------------*/
	printf("****** 即將進行文件讀取測試... ******\r\n");
	res_flash = f_open(&fnew, "0:FatFs讀寫測試文件.txt",FA_OPEN_EXISTING | FA_READ); 	 
	if(res_flash == FR_OK)
	{
		printf("》打開文件成功。\r\n");
		res_flash = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum); 
    	if(res_flash==FR_OK)
    	{
      		printf("》文件讀取成功,讀到字節數據:%d\r\n",fnum);
      		printf("》讀取得的文件數據爲:\r\n%s \r\n", ReadBuffer);	
    	}
		else
    	{
      		printf("!!文件讀取失敗:(%d)\n",res_flash);
    	}		
	}
	else
	{
		printf("!!打開文件失敗。\r\n");
	}
	/* 不再讀寫,關閉文件 */
	f_close(&fnew);	
  
	/* 不再使用文件系統,取消掛載文件系統 */
	//f_mount(NULL,"0:",1);

  /* Infinite loop */
  while (1)
  {
  }
}


/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow : 
  *            System Clock source            = PLL (HSI)
  *            SYSCLK(Hz)                     = 180000000
  *            HCLK(Hz)                       = 180000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 4
  *            APB2 Prescaler                 = 2
  *            HSE Frequency(Hz)              = 8000000
  *            PLL_M                          = 8
  *            PLL_N                          = 360
  *            PLL_P                          = 2
  *            PLL_Q                          = 7
  *            PLL_R                          = 2
  *            VDD(V)                         = 3.3
  *            Main regulator output voltage  = Scale1 mode
  *            Flash Latency(WS)              = 5
  * @param  None
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_OscInitTypeDef RCC_OscInitStruct;
	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
	
	/** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
	
	/* 使能HSE,配置HSE爲PLL的時鐘源,配置PLL的各種分頻因子M N P Q 
	 * PLLCLK = HSE/M*N/P = 12M / 25 *360 / 2 = 180M
	 */
  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 = 8;
  RCC_OscInitStruct.PLL.PLLN = 360;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  RCC_OscInitStruct.PLL.PLLR = 2;
	
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
	
	/** Activate the Over-Drive mode 
  */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    Error_Handler();
  }
	
	 /** Initializes the CPU, AHB and APB busses clocks 
  */
	/* 選擇PLLCLK作爲SYSCLK,並配置 HCLK, PCLK1 and PCLK2 的時鐘分頻因子 
	 * SYSCLK = PLLCLK     = 180M
	 * HCLK   = SYSCLK / 1 = 180M
	 * PCLK2  = SYSCLK / 2 = 90M
	 * PCLK1  = SYSCLK / 4 = 45M
	 */
  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();
  }
	
	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_CLK48;
  PeriphClkInitStruct.PLLSAI.PLLSAIM = 4;
  PeriphClkInitStruct.PLLSAI.PLLSAIN = 96;
  PeriphClkInitStruct.PLLSAI.PLLSAIQ = 2;
  PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV4;
  PeriphClkInitStruct.PLLSAIDivQ = 1;
  PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48CLKSOURCE_PLLSAIP;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void Error_Handler(void)
{
  /* User may add here some code to deal with this error */
  while(1)
  {
  }
}

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

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

/**
  * @}
  */ 

/**
  * @}
  */ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

編譯下載後可以在 PC 上看到模擬出的U盤:
在這裏插入圖片描述
END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章