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. 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>© 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>© 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