第五十八章 USB U盘(Host)实验
[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0[/mw_shl_code]
前面两章,我们介绍了STM32F407的USB SLAVE应用,本章我们介绍STM32F407的USB HOST应用,即通过USB HOST功能,实现读写U盘/读卡器等大容量USB存储设备。本章分为如下几个部分:
58.1 U盘简介
58.2 硬件设计
58.3 软件设计
58.4 下载验证
58.1 U盘简介
U盘,全称USB闪存盘,英文名“USB flash disk”。它是一种使用USB接口的无需物理驱动器的微型高容量移动存储产品,通过USB接口与主机连接,实现即插即用,是最常用的移动存储设备之一。
STM32F4的USB OTG FS支持U盘,并且ST官方提供了USB HOST大容量存储设备(MSC)例程,ST官方例程路径:光盘à8,STM32参考资料àSTM32 USB 学习资料àSTM32_USB-Host-Device_Lib_V2.1.0àProjectàUSB_Host_ExamplesàMSC。本章代码,我们就要移植该例程到探索者STM32F4开发板上,以通过STM32F4的USB HOST接口,读写U盘或SD卡读卡器等设备。
58.2 硬件设计
本章实验功能简介:开机后,检测字库,然后初始化USB
HOST,并不断轮询。当检测并识别U盘后,在LCD上面显示U盘总容量和剩余容量,此时便可以通过USMART调用FATFS相关函数,来测试U盘数据的读写了,方法同FATFS实验一模一样。当U盘没插入的时候,DS0闪烁,提示程序运行,当U盘插入后,DS1闪烁,提示可以通过USMART测试了。
所要用到的硬件资源如下:
1) 指示灯DS0 、DS1。
2) 串口
3) TFTLCD模块
4) SD卡(非必须)
5) SPI FLASH
6) USB HOST接口
前面5部分,在之前的实例中都介绍过了,我们在此就不介绍了。接下来看看我们电脑USB与STM32的USB HOST连接口。
ALIENTEK探索者STM32F4开发板的USB HOST接口采用的是侧式USB-A座,它和USB SLAVE的5PIN MiniUSB接头是共用USB_DM和USB_DP信号的,所以USB HOST和USB SLAVE不能同时使用。
USB HOST同STM32F4的连接原理图,如图58.2.1所示:
图58.2.1 USB HOST接口与STM32F4的连接原理图
从上图可以看出,USB_HOST和USB_SLAVE共用USB_DM/DP信号,通过P11连接到STM32F4。所以我们需要通过跳线帽将PA11和PA12分别连接到D-和D+,如图58.2.2所示:
图58.2.2 硬件连接示意图
图58.2.1中,我们还有一个USB_PWR的控制信号,用于控制给USB设备供电,该信号连接在PA15上面,和JTAG 的JTDI信号共用,所以我们建议大家使用SWD模式调试,这样PA15就解放了,可以用于USB_PWR的控制。
使用USB HOST驱动外部USB设备的时候,必须要先控制USB_PWR输出1,给外部设备供电,之后才可以识别到外部设备!
58.3 软件设计
本章,我们在:实验41 图片显示实验 的基础上修改,代码移植自ST官方例程:STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Host_Examples\MSC,我打开该例程即可知道USB相关的代码有哪些,如图58.3.1所示:
图58.3.1 ST官方例程USB相关代码
有了这个官方例程做指引,我们就知道具体需要哪些文件,从而实现本章例程。
从上图可以看出,这里并没有像第五十六章图56.3.1那样,区分不同分组,而是都放到USB_Host组下,看起来有点小乱,我们移植的时候,还是以56章的方式,分不同分组添加代码,方便阅读和管理。
这里面usbh_msc_fatfs.c,是为了支持fatfs而写的一些底层接口函数,我们例程就直接放到diskio.c里面了,方便统一管理。
本例程的具体移植步骤,我们这里就不一一介绍了,最终移植好之后的工程截图,如图58.3.2所示:
图58.3.2 添加USB驱动等相关代码
移植时,我们重点要修改的就是USB_APP文件夹下面的代码。其他代码(USB_OTG和USB_HOST文件夹下的代码)一般不用修改。
usb_bsp.c的代码,和上一章的一样,可以用上一章的代码直接替换即可正常使用。
usbh_usr.c提供用户应用层接口函数,相比前两章例程,USB HOST通信的回调函数更多一些,这里重点介绍3个函数,代码如下:
extern u8 USH_User_App(void); //用户测试主程序
//USB HOST MSC类用户应用程序
int USBH_USR_MSC_Application(void)
{
u8
res=0;
switch(AppState)
{
case USH_USR_FS_INIT://初始化文件系统
printf("开始执行用户程序!!!\r\n");
AppState=USH_USR_FS_TEST;
break;
case USH_USR_FS_TEST: //执行USB OTG 测试主程序
res=USH_User_App();
//用户主程序
res=0;
if(res)AppState=USH_USR_FS_INIT;
break;
default:break;
}
return
res;
}
//用户定义函数,实现fatfs diskio的接口函数
extern USBH_HOST USB_Host;
//读U盘
//buf:读数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
u8 USBH_UDISK_Read(u8* buf,u32 sector,u32 cnt)
{
u8
res=1;
if(HCD_IsDeviceConnected(&USB_OTG_Core)&&AppState==USH_USR_FS_TEST)
//连接还存在,且是APP测试状态
{
do
{
res=USBH_MSC_Read10(&USB_OTG_Core,buf,sector,512*cnt);
USBH_MSC_HandleBOTXfer(&USB_OTG_Core
,&USB_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_Core))
{
res=1;//读写错误
break;
};
}while(res==USBH_MSC_BUSY);
}else
res=1;
if(res==USBH_MSC_OK)res=0;
return
res;
}
//写U盘
//buf:写数据缓存区
//sector:扇区地址
//cnt:扇区个数
//返回值:错误状态;0,正常;其他,错误代码;
u8 USBH_UDISK_Write(u8* buf,u32 sector,u32 cnt)
{
u8
res=1;
if(HCD_IsDeviceConnected(&USB_OTG_Core)&&AppState==USH_USR_FS_TEST)
//连接还存在,且是APP测试状态
{
do
{
res=USBH_MSC_Write10(&USB_OTG_Core,buf,sector,512*cnt);
USBH_MSC_HandleBOTXfer(&USB_OTG_Core
,&USB_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_Core))
{
res=1;//读写错误
break;
};
}while(res==USBH_MSC_BUSY);
}else
res=1;
if(res==USBH_MSC_OK)res=0;
return
res;
}
其中,USBH_USR_MSC_Application函数通过状态机的方式,处理相关事务,执行到这个函数,说明U盘已经被成功识别了,此时用户可以执行一些自己想要做的事情,比如读取U盘文件什么的,这里我们直接进入到USH_User_App函数,执行各种处理,后续会介绍该函数。
USBH_UDISK_Read和USBH_UDISK_Write这两个函数,用于U盘读写,从指定扇区地址读写指定个数的扇区数据,这两个函数,再配合fatfs,即可实现对U盘的文件读写访问。
其他代码,我们就不详细讲解了,请大家参考光盘本例程源码,最后修改main.c里面代码如下:
USBH_HOST
USB_Host;
USB_OTG_CORE_HANDLE USB_OTG_Core;
//用户测试主程序
//返回值:0,正常
// 1,有问题
u8 USH_User_App(void)
{
u32
total,free;u8 res=0;
Show_Str(30,140,200,16,"设备连接成功!.",16,0);
f_mount(fs[2],"2:",1); //重新挂载U盘
res=exf_getfree("2:",&total,&free);
if(res==0)
{
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(30,160,200,16,16,"FATFS
OK!");
LCD_ShowString(30,180,200,16,16,"U
Disk Total Size: MB");
LCD_ShowString(30,200,200,16,16,"U
Disk Free Size: MB");
LCD_ShowNum(174,180,total>>10,5,16);
//显示U盘总容量 MB
LCD_ShowNum(174,200,free>>10,5,16);
}
while(HCD_IsDeviceConnected(&USB_OTG_Core))//设备连接成功,死循环
{
LED1=!LED1;
delay_ms(200);
}
f_mount(0,"2:",1); //卸载U盘
POINT_COLOR=RED;//设置字体为红色
Show_Str(30,140,200,16,"设备连接中...",16,0);
LCD_Fill(30,160,239,220,WHITE);
return
res;
}
int main(void)
{
u8 t;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //按键
LCD_Init(); //初始化LCD
W25QXX_Init(); //SPI FLASH初始化
usmart_dev.init(84);
//初始化USMART
my_mem_init(SRAMIN);//初始化内部内存池
exfuns_init(); //为fatfs相关变量申请内存
piclib_init(); //初始化画图
f_mount(fs[0],"0:",1); //挂载SD卡
f_mount(fs[1],"1:",1); //挂载外部SPI FLASH盘
POINT_COLOR=RED;
while(font_init()) //检查字库
{
LCD_ShowString(60,50,200,16,16,"Font
Error!"); delay_ms(200);
LCD_Fill(60,50,240,66,WHITE);
delay_ms(200);//清除显示
}
Show_Str(30,50,200,16,"探索者STM32F407开发板",16,0);
Show_Str(30,70,200,16,"USB
U盘实验",16,0);
Show_Str(30,90,200,16,"2014年7月22日",16,0);
Show_Str(30,110,200,16,"正点原子@ALIENTEK",16,0);
Show_Str(30,140,200,16,"设备连接中...",16,0);
//初始化USB主机
USBH_Init(&USB_OTG_Core,USB_OTG_FS_CORE_ID,&USB_Host,&USBH_MSC_cb
,&USR_Callbacks);
while(1)
{
USBH_Process(&USB_OTG_Core,
&USB_Host);
delay_ms(1);
t++;
if(t==200){
LED0=!LED0; t=0;}
}
}
相比USB SLAVE例程,我们这里多了一个USB_HOST的结构体定义:USB_Host,用于存储主机相关状态。所以,使用USB主机的时候,需要两个结构体:USB_OTG_CORE_HANDLE和USB_HOST。
然后,USB初始化,使用的是USBH_Init,用于USB主机初始化,包括对USB硬件和USB驱动库的初始化。如果是:USB SLAVE通信,在只需要调用USBD_Init函数即可,不过USB HOST则还需要调用另外一个函数USBH_Process,该函数用于实现USB主机通信的核心状态机处理,该函数必须在主函数里面,被循环调用,而且调用频率得比较快才行(越快越好),以便及时处理各种事务。注意,USBH_Process函数仅在U盘识别阶段,需要频繁反复调用,但是当U盘被识别后,剩下的操作(U盘读写),都可以由USB中断处理。
以上代码,main函数十分简单,就不多做介绍了,这里主要看看USH_User_App函数,该函数前面有提到,是在USBH_USR_MSC_Application函数里面被调用,用于实现U盘插入后,用户想要实现的功能,一旦进入到该函数,即表示U盘已经成功识别了,所以,函数里面提示设备连接成功,挂载U盘(U盘盘符为2,0代表SD卡,1代表SPI FLASH)并读取U盘总容量和剩余容量,显示在LCD上面,然后,进入死循环,只要USB连接一直存在,则一直死循环,同时控制LED1闪烁,提示U盘已经准备好了。
当U盘拔出来后,卸载U盘,然后再次提示设备连接中,会到main函数死循环,等待U盘再次连上。
最后,我们需要将FATFS相关测试函数(mf_open/ mf_close等函数),加入USMART管理,这里同第四十四章(FATFS实验)一模一样,可以参考第四十四章的方法操作。
软件设计部分,就给大家介绍到这里。
58.4 下载验证
在代码编译成功之后,我们下载到探索者STM32F4开发板上,然后在USB_HOST端子插入U盘/读卡器(带卡),注意:此时USB SLAVE口不要插USB线到电脑,否则会干扰!!
等U盘成功识别后,便可以看到LCD显示U盘容量等信息,如图58.4.1所示:
图58.4.1 U盘识别成功
此时,我们便可以通过USMART来测试U盘读写了,如图58.4.2和图58.4.3所示:
图58.4.2 测试读取U盘读取
图58.4.3 测试U盘写入
图58.4.2通过发送:mf_scan_files("2:"),扫描U盘根目录所有文件,然后通过ai_load_picfile
("2:/测试用图片.jpg",0,0,480,800,1),解码图片,并显示在LCD上面。说明读U盘是没问题的。
图58.4.3通过发送:mf_open("2:test u
disk.txt",7),在U盘根目录创建test u disk.txt这个文件,然后发送:mf_write("This is a
test",14),写入This is a
test到这个文件里面,然后发送:mf_close(),关闭文件,完成一次文件创建。最后,发送:mf_scan_files("2:"),扫描U盘根目录文件,发现比图58.4.2所示多出了一个test u
disk.txt的文件,说明U盘写入成功。
这样,就完成了本实验的设计目的:实现U盘的读写操作。最后,大家还可以调用其他函数,实现相关功能测试,这里就不给大家一一演示了,测试方法同:FATFS实验(第四十四章)。
正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779
|