移植FatFs至STM32F429ZIT有一點要特別注意,就是版本相容性,本人實驗過各個FatFs與STM32F4xx_DSP_StdPeriph_Lib的版本,只有以下版本能成功運行,
STM32F4xx_DSP_StdPeriph_Lib_V1.3.0
FatFs R0.09b
FatFs移植至STM32F429ZIT可參照[STM32F429ZIT-MCU學習筆記]FatFs移植與使用SD Card來進行,這裡只講解USB部分,USB可以非常多的應用,主要功能有兩種設定,一種是HOST,另一種是Device,這篇先介紹HOST,就是讀取USB裝置的資訊,可以用在USB隨身碟,USB鍵盤等周遭介面,USB選用STM32_USB-Host-Device_Lib_V2.1.0,下載點如下,
STM32_USB-Host-Device_Lib_V2.1.0
下載完成壓縮檔如下,
解完壓縮如下,
到\uubt-master\src\STM32_USB-Host-Device_Lib_V2.1.0\Libraries複製下圖這三個資料夾,
複製到專案目錄底下,
進入Keil uVision5引入HOST會用的.c與.h檔,建立STM32_USB_OTG_Driver,引入STM32_USB_OTG_Driver資料夾內的.c檔
建立STM32_USB_HOST_Library,引入STM32_USB_HOST_Library資料夾內的.c檔
注意usb_bsp_template.c要改成usb_bsp.c,usb_conf_template.h改成usb_conf.h,usbh_conf_template.h改成usbh_conf.h
在stm32f429 discovery開發版上有使用EMIF02-USB03F2這IC,所以在USB設定配置時須要用HS模式去做配置,
在Option for Target 'Target 1'切到C/C++,在Preprocessor Symbols中的Define增加,USE_USB_OTG_HS
在下面的Language/Code Generation設定如下,
.h檔增加路徑如下,
增加完成結果如下,
接下來要進行文件修改與配置,先修改usb_conf.h,增加#include "stm32f4xx.h",
將USB OTG HS CONFIGURATION多餘的設定移除,
將USB OTG FS CONFIGURATION多餘的設定移除,
將USE設定為USE_HOST_MODE,
將下圖的#error移除掉
修改usb_bsp.c,Includes如下設定
USB_OTG_BSP_Init如下設定
/**
* @brief USB_OTG_BSP_Init
* Initilizes BSP configurations
* @param None
* @retval None
*/
void USB_OTG_BSP_Init (USB_OTG_CORE_HANDLE *pdev)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Configuration for USB OTG HS used in FS mode with EMBEDDED PHY */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_OTG_HS_FS) ;
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_OTG_HS_FS) ;
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_OTG_HS_FS) ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_OTG_HS, ENABLE);
}
USB_OTG_BSP_EnableInterrupt如下設定
/**
* @brief USB_OTG_BSP_EnableInterrupt
* Enabele USB Global interrupt
* @param None
* @retval None
*/
void USB_OTG_BSP_EnableInterrupt (USB_OTG_CORE_HANDLE *pdev)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = OTG_HS_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
USB_OTG_BSP_DriveVBUS如下設定
/**
* @brief BSP_Drive_VBUS
* Drives the Vbus signal through IO
* @param speed : Full, Low
* @param state : VBUS states
* @retval None
*/
void USB_OTG_BSP_DriveVBUS(USB_OTG_CORE_HANDLE *pdev,uint8_t state)
{
if (state==0)
{
/* DISABLE is needed on output of the Power Switch */
GPIO_SetBits(GPIOC, GPIO_Pin_4);
}
else
{
/*ENABLE the Power Switch by driving the Enable LOW */
GPIO_ResetBits(GPIOC, GPIO_Pin_4);
}
}
USB_OTG_BSP_ConfigVBUS如下設定
/**
* @brief USB_OTG_BSP_ConfigVBUS
* Configures the IO for the Vbus and OverCurrent
* @param Speed : Full, Low
* @retval None
*/
void USB_OTG_BSP_ConfigVBUS(USB_OTG_CORE_HANDLE *pdev)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_4);
USB_OTG_BSP_mDelay(200);
}
USB_OTG_BSP_uDelay如下設定
/**
* @brief USB_OTG_BSP_uDelay
* This function provides delay time in micro sec
* @param usec : Value of delay required in micro sec
* @retval None
*/
void USB_OTG_BSP_uDelay (const uint32_t usec)
{
delay_us(usec);
}
USB_OTG_BSP_mDelay如下設定
/**
* @brief USB_OTG_BSP_mDelay
* This function provides delay time in milli sec
* @param msec : Value of delay required in milli sec
* @retval None
*/
void USB_OTG_BSP_mDelay (const uint32_t msec)
{
delay_ms(msec);
}
再增加OTG_HS_IRQHandler
void OTG_HS_IRQHandler(void)
{
USBH_OTG_ISR_Handler(&USB_OTG_HS_Core);
}
到\uubt-master\src\STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Host_Device_Examples\DRD\src取usbh_msc_usr.c
到\uubt-master\src\STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Host_Device_Examples\DRD\inc取usbh_msc_usr.h
將以上這兩隻程式移植\STM32F429_Configuration\src與\STM32F429_Configuration\inc
usbh_msc_usr.c裡可以完呈現USB進入系統的運作裝況,方便開發者去了解與修改,這裡只講解額外增加的部分,
USB_OTG_CORE_HANDLE USB_OTG_HS_Core;
uint8_t USBH_USR_ApplicationState;
USBH_HOST USBH_Host;
/*
程式名稱:USB初始化
程式版本:V1.0
程式撰寫者:Michael Jheng(鄭智遠)
程式撰寫日期:2018/6/23
程式修改日期:N/A
程式說明:
*/
uint8_t USBH_UDISK_Init(void){
return HCD_IsDeviceConnected(&USB_OTG_HS_Core);
}
/*
程式名稱:USB狀態
程式版本:V1.0
程式撰寫者:Michael Jheng(鄭智遠)
程式撰寫日期:2018/6/23
程式修改日期:N/A
程式說明:
*/
uint8_t USBH_UDISK_Status(void){
return USBH_USR_ApplicationState;
}
/*
程式名稱:USB讀取
程式版本:V1.0
程式撰寫者:Michael Jheng(鄭智遠)
程式撰寫日期:2018/6/23
程式修改日期:N/A
程式說明:
*/
uint8_t USBH_UDISK_Read(uint8_t* buffer, uint32_t sector, uint32_t cnt){
uint8_t res=1;
if(HCD_IsDeviceConnected(&USB_OTG_HS_Core) && USBH_USR_ApplicationState!=USH_USR_FS_INIT){
do{
res = USBH_MSC_Read10(&USB_OTG_HS_Core, buffer, sector, 512*cnt);
USBH_MSC_HandleBOTXfer(&USB_OTG_HS_Core, &USBH_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_HS_Core)){
res = 1;
break;
};
}while(res==USBH_MSC_BUSY);
}else{
res = 1;
}
if(res==USBH_MSC_OK){
res = 0;
}
return res;
}
/*
程式名稱:USB寫入
程式版本:V1.0
程式撰寫者:Michael Jheng(鄭智遠)
程式撰寫日期:2018/6/23
程式修改日期:N/A
程式說明:
*/
uint8_t USBH_UDISK_Write(uint8_t* buffer, uint32_t sector, uint32_t cnt){
uint8_t res=1;
if(HCD_IsDeviceConnected(&USB_OTG_HS_Core) && USBH_USR_ApplicationState!=USH_USR_FS_INIT){
do{
res = USBH_MSC_Write10(&USB_OTG_HS_Core, buffer, sector, 512*cnt);
USBH_MSC_HandleBOTXfer(&USB_OTG_HS_Core, &USBH_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_HS_Core)){
res = 1;
break;
};
}while(res==USBH_MSC_BUSY);
}else{
res = 1;
}
if(res==USBH_MSC_OK){
res = 0;
}
return res;
}
在usbh_msc_usr.h需增加
extern uint8_t USBH_USR_ApplicationState ;
extern USB_OTG_CORE_HANDLE USB_OTG_HS_Core;
extern USBH_HOST USBH_Host;
uint8_t USBH_UDISK_Init(void);//USB初始化
uint8_t USBH_UDISK_Status(void);//USB狀態
uint8_t USBH_UDISK_Read(uint8_t* buffer, uint32_t sector, uint32_t cnt);//USB讀取
uint8_t USBH_UDISK_Write(uint8_t* buffer, uint32_t sector, uint32_t cnt);//USB寫入
接下來要在FatFs/diskio.c增加USB的方法,
#include "diskio.h" /* FatFs lower layer API */
#include "sdio_config.h"
#include "usbh_msc_usr.h"
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber (0..) */
)
{
DSTATUS stat;
int result;
switch (pdrv) {
case ATA :
return stat;
case MMC :
result = SD_Init();
stat = result;
return stat;
case USB :
result = USBH_UDISK_Init();
if(result==1){
stat = RES_OK;
}else{
stat = RES_ERROR;
}
return stat;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber (0..) */
)
{
DSTATUS stat;
int result;
switch (pdrv) {
case ATA :
return stat;
case MMC :
result = SD_GetStatus();
stat = result;
return stat;
case USB :
result = USBH_UDISK_Status();
stat = result;
return stat;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..128) */
)
{
DRESULT res;
int result;
switch (pdrv) {
case ATA :
// translate the arguments here
//result = ATA_disk_read(buff, sector, count);
// translate the reslut code here
return res;
case MMC :
if(count>1){
result = SD_ReadMultiBlocks(buff, sector*BLOCK_SIZE, BLOCK_SIZE, count);
}else{
result = SD_ReadBlock(buff, sector*BLOCK_SIZE, BLOCK_SIZE);
}
/* Check if the Transfer is finished */
result = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
res = result;
return res;
case USB :
result = USBH_UDISK_Read(buff, sector, count);
res = result;
return res;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..128) */
)
{
DRESULT res;
int result;
switch (pdrv) {
case ATA :
// translate the arguments here
//result = ATA_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case MMC :
if(count>1){
result = SD_WriteMultiBlocks((uint8_t*)buff, sector*BLOCK_SIZE, BLOCK_SIZE, count);
}else{
result = SD_WriteBlock((uint8_t*)buff, sector*BLOCK_SIZE, BLOCK_SIZE);
}
/* Check if the Transfer is finished */
result = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
res = result;
return res;
case USB :
result = USBH_UDISK_Write((uint8_t*)buff, sector, count);
res = result;
return res;
}
return RES_PARERR;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
#if _USE_IOCTL
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
int result;
switch (pdrv) {
case ATA :
// pre-process here
//result = ATA_disk_ioctl(cmd, buff);
// post-process here
return res;
case MMC :
switch(cmd){
case CTRL_SYNC:
result = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 512;
result = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = 512;
result = RES_OK;
break;
default:
result = RES_PARERR;
break;
}
res = result;
return res;
case USB :
switch(cmd){
case CTRL_SYNC:
result = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
result = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 512;
result = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = USBH_MSC_Param.MSCapacity;
result = RES_OK;
break;
default:
result = RES_PARERR;
break;
}
res = result;
return res;
}
return RES_PARERR;
}
#endif
以上程式撰寫完成後,就要來寫測試程式,在ExtDevices_Configuration創造usbdisk.c與usbdisk.h,在裡面寫USB初始化程式,
/*
程式名稱:USB Disk初始化
程式版本:V1.0
程式撰寫者:Michael Jheng(鄭智遠)
程式撰寫日期:2018/6/23
程式修改日期:N/A
程式說明:
*/
uint8_t USB_DiskInit(void){
uint8_t res=0;
res = f_mount(2, &usbDisk_Ptr->fatfs);
if(res!=0){
USART1_DMA_Send((uint8_t*)"f_mount ERROR\r\n", 15);
}else{
USART1_DMA_Send((uint8_t*)"f_mount OK\r\n", 12);
}
USBH_Init(&USB_OTG_HS_Core, USB_OTG_HS_CORE_ID, &USBH_Host, &USBH_MSC_cb, &USR_USBH_MSC_cb);
return res;
}
在User/bsp.c,增加uint8_t USB_DiskInit(void)來進行初始化,
我的測試方式是使用一個按鍵,來觸發寫進USB資料的程式,會在外部中斷增加按鍵式觸發,在main.c增加要寫進USB資料的程式,如下所示,
if(flag==1){
res = f_open(&usbDisk_Ptr->fil, "2:/123.txt", FA_WRITE | FA_CREATE_ALWAYS);
if(res!=0){
USART1_DMA_Send((uint8_t*)"f_open ERROR\r\n", 14);
}else{
USART1_DMA_Send((uint8_t*)"f_open OK\r\n", 11);
}
res = f_write(&usbDisk_Ptr->fil, "ABCDEF", 6, &usbDisk_Ptr->bw);
if(res!=0){
USART1_DMA_Send((uint8_t*)"f_write ERROR\r\n", 15);
}else{
USART1_DMA_Send((uint8_t*)"f_write OK\r\n", 12);
}
f_close(&usbDisk_Ptr->fil);
flag = 0;
}
實測結果如下,初始化訊息,
按下寫入按鍵執行結果,
USB寫入的資料,