第五十六章 USB读卡器(Slave)实验
	      
	
[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0[/mw_shl_code]
	 
 
 
	STM32F407系列芯片都自带了USB OTG FS和USB OTG HS(HS需要外扩高速PHY芯片实现,速度可达480Mbps),支持USB Host和USB Device,探索者STM32F4开发板没有外扩高速PHY芯片,仅支持USB OTG FS(FS,即全速,12Mbps),所有USB相关例程,均使用USB OTG FS实现。
 
	本章,我们将向大家介绍如何利用USB OTG FS在ALIENTEK探索者STM32F4开发板实现一个USB读卡器。本章分为如下几个部分:
 
	56.1 USB简介
 
	56.2 硬件设计
 
	56.3 软件设计
 
	56.4 下载验证
 
	 
 
	56.1 USB简介
	USB ,是英文Universal Serial BUS(通用串行总线)的缩写,而其中文简称为“通串线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在PC领域的接口技术。USB接口支持设备的即插即用和热插拔功能。USB是在1994年底由英特尔、康柏、IBM、Microsoft等多家公司联合提出的。
 
	USB发展到现在已经有USB1.0/1.1/2.0/3.0等多个版本。目前用的最多的就是USB1.1和USB2.0,USB3.0目前已经开始普及。STM32F407自带的USB符合USB2.0规范。
 
	标准USB共四根线组成,除VCC/GND外,另外为D+和D-,这两根数据线采用的是差分电压的方式进行数据传输的。在USB主机上,D-和D+都是接了15K的电阻到地的,所以在没有设备接入的时候,D+、D-均是低电平。而在USB设备中,如果是高速设备,则会在D+上接一个1.5K的电阻到VCC,而如果是低速设备,则会在D-上接一个1.5K的电阻到VCC。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。接下来,我们简单介绍一下STM32的USB控制器。
 
	STM32F407系列芯片自带有USB OTG FS(全速)和USB OTG HS(高速),其中HS需要外扩高速PHY芯片实现,我们这里不做介绍。
 
	STM32F407的USB OTG FS是一款双角色设备 (DRD) 控制器,同时支持从机功能和主机功能,完全符合USB 2.0规范的On-The-Go补充标准。此外,该控制器也可配置为“仅主机”模式或“仅从机”
模式,完全符合USB 2.0规范。在主机模式下,OTG FS支持全速(FS,12 Mb/s)和低速(LS,1.5 Mb/s)收发器,而从机模式下则仅支持全速(FS,12 Mb/s)收发器。OTG FS同时支持HNP和SRP。
 
	STM32F407的USB OTG FS主要特性可分为三类:通用特性、主机模式特性和从机模式特性。
 
	1,通用特性 
 
	? 
经USB-IF认证,符合通用串行总线规范第2.0版
 
	? 
集成全速PHY,且完全支持定义在标准规范OTG补充第1.3版中的OTG协议
 
	1,支持A-B器件识别(ID线)
 
	2,支持主机协商协议(HNP)和会话请求协议(SRP)
 
	3,允许主机关闭VBUS以在OTG应用中节省电池电量
 
	4,支持通过内部比较器对VBUS电平采取监控
 
	5,支持主机到从机的角色动态切换
 
	? 
可通过软件配置为以下角色:
 
	1,  具有SRP功能的USB FS从机(B器件)
 
	2,  具有SRP功能的USB FS/LS主机(A器件)
 
	3,USB On-The-Go全速双角色设备
 
	? 
支持FS SOF和LS Keep-alive令牌
 
	1,SOF脉冲可通过PAD输出
 
	2,SOF脉冲从内部连接到定时器 2 (TIM2)
 
	3,可配置的帧周期
 
	3,  可配置的帧结束中断
 
	? 
具有省电功能,例如在USB挂起期间停止系统、关闭数字模块时钟、对PHY和DFIFO电源加以管理
 
	? 
具有采用高级FIFO控制的1.25 KB专用RAM
 
	1,可将 RAM 空间划分为不同FIFO,以便灵活有效地使用RAM
 
	2,每个FIFO可存储多个数据包
 
	3,动态分配存储区
 
	4,FIFO大小可配置为非2的幂次方值,以便连续使用存储单元
 
	? 
一帧之内可以无需要应用程序干预,以达到最大 USB 带宽
 
	2,主机(Host)模式特性 
 
	? 
通过外部电荷泵生成VBUS电压。
 
	? 
多达8个主机通道(管道):每个通道都可以动态实现重新配置,可支持任何类型的USB 传输。
 
	? 
内置硬件调度器可:
 
	1,在周期性硬件队列中存储多达8个中断加同步传输请求
 
	2,在非周期性硬件队列中存储多达8个控制加批量传输请求
 
	? 
管理一个共享RX FIFO、一个周期性TX FIFO和一个非周期性TX FIFO,以有效使用USB数据RAM。
 
	3,从机(Slave/Device)模式特性 
 
	?  1个双向控制端点0
 
	?  3个IN 端点 (EP),可配置为支持批量传输、中断传输或同步传输
 
	?  3个OUT 端点(EP),可配置为支持批量传输、中断传输或同步传输
 
	? 
管理一个共享Rx FIFO和一个Tx-OUT FIFO,以高效使用USB数据RAM
 
	? 
管理多达4个专用Tx-IN
FIFO(分别用于每个使能的IN EP),降低应用程序负荷支持软断开功能。
 
	STM32F407 USB OTG FS框图如图56.1.1所示: 
	 
	
 
	图56.1.1 USB OTG框图
 
	对于USB OTG FS功能模块,STM32F4通过AHB总线访问(AHB频率必须大于14.2Mhz),其中48Mhz的USB时钟,是来自时钟树图里面的PLL48CK(和SDIO共用)。
 
	STM32F4 USB OTG FS的其他介绍,请大家参考《STM32F4xx中文参考手册》第30章内容,我们这里就不再详细介绍了。
 
	要正常使用STM32F4的USB,就得编写USB驱动,而整个USB通信的详细过程是很复杂的,本书篇幅有限,不可能在这里详细介绍,有兴趣的朋友可以去看看电脑圈圈的《圈圈教你玩USB》这本书,该书对USB通信有详细讲解。如果要我们自己编写USB驱动,那是一件相当困难的事情,尤其对于从没了解过USB的人来说,基本上不花个一两年时间学习,是没法搞定的。不过,ST提供了我们一个完整的USB OTG 驱动库(包括主机和设备),通过这个库,我们可以很方便的实现我们所要的功能,而不需要详细了解USB的整个驱动,大大缩短了我们的开发时间和精力。
 
	ST提供的USB OTG库,可以在: http://www.stmcu.org/download/index.php?act=ziliao
 
	&id=150这里下载到(UM1021)。不过,我们已经帮大家下载到开发板光盘:8,STM32参考资料àSTM32 USB 学习资料,文件名:stm32_f105-07_f2_f4_usb-host-device_lib.zip。该库包含了STM32F4 USB 主机(Host)和从机(Device)驱动库,并提供了10个例程供我们参考,如图56.1.2所示:
 
	
  
 
 
	图56.1.2 ST提供的USB OTG例程
 
	    如图56.1.2所示,ST提供了3类例程:①即设备类(Device,即Slave)、②主从一体类(Host_Device)和③主机类(Host),总共10个例程。整个USB OTG库还有一个说明文档:CD00289278.pdf(在光盘有提供),即UM1021,该文档详细介绍了USB
OTG库的各个组成部分以及所提供的例程使用方法,有兴趣学习USB的朋友,这个文档是必须仔细看的。
 
	    这10个例程,虽然都是基于官方EVAL板的,但是很容易移植到我们的探索者STM32F407开发板上,本章我们就是移植:STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Device_
 
	Examples\MSC这个例程,以实现USB读卡器功能。
 
	56.2 硬件设计
	本章实验功能简介:开机的时候先检测SD卡和SPI FLASH是否存在,如果存在则获取其容量,并显示在LCD上面(如果不存在,则报错)。之后开始USB配置,在配置成功之后就可以在电脑上发现两个可移动磁盘。我们用DS1来指示USB正在读写,并在液晶上显示出来,同样,我们还是用DS0来指示程序正在运行。
 
	所要用到的硬件资源如下:
 
	1)  指示灯DS0 、DS1 
 
	2)  串口
 
	3)  TFTLCD模块
 
	4)  SD卡
 
	5)  SPI FLASH
 
	6)  USB SLAVE接口 
 
	前面5部分,在之前的实例中都介绍过了,我们在此就不介绍了。接下来看看我们电脑USB与STM32的USB SLAVE连接口。ALIENTEK探索者STM32F4开发板采用的是5PIN的MiniUSB接头,用来和电脑的USB相连接,连接电路如图56.2.1所示:
 
	
  
 
 
	                     图56.2.1 MiniUSB接口与STM32的连接电路图
 
	从上图可以看出,USB座没有直接连接到STM32F4上面,而是通过P11转接,所以我们需要通过跳线帽将PA11和PA12分别连接到D-和D+,如图56.2.2所示:
 
	
  
 
 
	图56.2.2 硬件连接示意图
 
	不过这个MiniUSB座和USB-A座(USB_HOST)是共用D+和D-的,所以他们不能同时使用。这个在使用的时候,要特别注意!!本实验测试时,USB_HOST不能插入任何USB设备!
 
	56.3 软件设计
	本章,我们在:实验38 SD卡实验 的基础上修改,代码移植自ST官方例程:STM32_USB-
 
	Host-Device_Lib_V2.1.0\Project\USB_Device_Examples\MSC,我打开该例程即可知道USB相关的代码有哪些,如图56.3.1所示:
 
	
  
 
 
	图56.3.1 ST官方例程USB相关代码
 
	       有了这个官方例程做指引,我们就知道具体需要哪些文件,从而实现本章例程。
 
	首先,在本章例程(即实验38 SD卡实验)的工程文件夹下面,新建USB文件夹,并拷贝官方USB驱动库相关代码到该文件夹下,即拷贝:光盘à 8,STM32参考资料àSTM32 USB 学习资料àSTM32_USB-Host-Device_Lib_V2.1.0àLibraries文件夹下的STM32_USB_Device_Libr
 
	ary、STM32_USB_HOST_Library和STM32_USB_OTG_Driver等三个文件夹的源码到该文件夹下面。
 
	       然后,在USB文件夹下,新建USB_APP文件夹存放MSC实现相关代码,即:STM32_USB
 
	-Host-Device_Lib_V2.1.0àProjectàUSB_Device_ExamplesàMSCàsrc下的部分代码:usb_bsp.c
 
	、usbd_storage_msd.c、usbd_desc.c和usbd_usr.c等4个.c文件,同时拷贝STM32_USB-Host-Device
 
	_Lib_V2.1.0àProjectàUSB_Device_ExamplesàMSCàinc下面的:usb_conf.h、usbd_conf.h和usbd_desc.h等三个文件到USB_APP文件夹下,最后USB_APP文件夹下的文件如图56.3.2所示:
 
	
  
 
 
	图56.3.2 USB_APP代码
 
	       之后,根据ST官方MSC例程,在我们本章例程的基础上新建分组添加相关代码,具体细节,这里就不详细介绍了,添加好之后,如图56.3.3所示:
 
	
  
 
 
	图56.3.3 添加USB驱动等相关代码
 
	       移植时,我们重点要修改的就是USB_APP文件夹下面的代码。其他代码(USB_OTG和USB_DEVICE文件夹下的代码)一般不用修改。
 
	       usb_bsp.c提供了几个USB库需要用到的底层初始化函数,包括:IO设置、中断设置、VBUS配置以及延时函数等,需要我们自己实现。USB Device(Slave)和USB Host共用这个.c文件。
 
	       usbd_desc.c提供了USB设备类的描述符,直接决定了USB设备的类型、断点、接口、字符串、制造商等重要信息。这个里面的内容,我们一般不用修改,直接用官方的即可。注意,这里:usbd_desc.c里面的:usbd即device类,同样:usbh即host类,所以通过文件名我们可以很容易区分该文件是用在device还是host,而只有usb字样的那就是device和host可以共用的。
 
	       usbd_usr.c提供用户应用层接口函数,即USB设备类的一些回调函数,当USB状态机处理完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道USB当前状态,比如:是否枚举成功了?是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以执行不同操作,完成特定功能。
 
	       usbd_storage_msd.c提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化和读写等函数。本章我们设置了2个磁盘:SD卡和SPI FLASH。
 
	      以上4个.c文件里面的函数,基本上都是以回调函数的形式,被USB驱动库调用的。这些代码的具体修改过程,我们这里不详细介绍,请大家参考光盘本例程源码,这里只提几个重点地方讲解下:
 
	1,要使用USB OTG FS,必须在MDK编译器的全局宏定义里面,定义:USE_USB_OTG_FS宏,如图56.3.4所示:
 
	
  
 
 
	图56.3.4 定义全局宏USE_USB_OTG_FS
 
	2,因为探索者STM32F407开发板没有用到VUSB电压检测,所以要在usb_conf.h里面,将宏定义:#define VBUS_SENSING_ENABLED,屏蔽掉。
 
	3,通过修改usbd_conf.h里面的MSC_MEDIA_PACKET定义值大小,可以一定程度提高USB读写速度(越大越快),本例程我们设置12*1024,也就是12K大小。
 
	4,官方例程不支持大于4G的SD卡,得修改usbd_msc_scsi.c里面的SCSI_blk_addr类型为uint64_t,才可以支持大于4G的卡,官方默认是uint32_t,最大只能支持4G卡。注意:usbd_msc_scsi.c文件,是只读的,得先修改属性,去掉只读属性,才可以更改。
 
	以上4点,就是我们移植的时候需要特别注意的,其他我们就不详细介绍了(USB相关源码解释,请参考:CD00289278.pdf这个文档),最后修改main.c里面代码如下:
 
	USB_OTG_CORE_HANDLE USB_OTG_dev;
 
	extern vu8 USB_STATUS_REG;          //USB状态
 
	extern vu8 bDeviceState;                     //USB连接 情况 
 
	int main(void)
 
	{         
 
	       u8
offline_cnt=0; u8 tct=0;    
 
	       u8
Divece_STA; u8 USB_STA;
 
	       NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
 
	       delay_init(168);  //初始化延时函数
 
	       uart_init(115200);         //初始化串口波特率为115200
 
	       LED_Init();                         //初始化LED  
 
	      LCD_Init();                         //LCD初始化  
 
	      KEY_Init();                         //按键初始化  
 
	       W25QXX_Init();                  //初始化W25Q128    
 
	      POINT_COLOR=RED;//设置字体为红色      
 
	       LCD_ShowString(30,50,200,16,16,"Explorer
STM32F4");       
 
	       LCD_ShowString(30,70,200,16,16,"USB
Card Reader TEST"); 
 
	       LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
 
	       LCD_ShowString(30,110,200,16,16,"2014/7/21");     
 
	       if(SD_Init())LCD_ShowString(30,130,200,16,16,"SD
Card Error!");       //检测SD卡错误
 
	       else
//SD 卡正常
 
	       {                                                                                                              
 
	              LCD_ShowString(30,130,200,16,16,"SD
Card Size:     MB"); 
 
	             LCD_ShowNum(134,130,SDCardInfo.CardCapacity>>20,5,16);       //显示SD卡容量
 
	      }
 
	       if(W25QXX_ReadID()!=W25Q128)
 
	LCD_ShowString(30,130,200,16,16,"W25Q128 Error!");  //检测W25Q128错误
 
	       else
LCD_ShowString(30,150,200,16,16,"SPI FLASH Size:12MB");       //SPI FLASH 正常 
 
	      LCD_ShowString(30,170,200,16,16,"USB
Connecting...");//提示正在建立连接        
 
	       USBD_Init(&USB_OTG_dev,USB_OTG_FS_CORE_ID,&USR_desc,&USBD_MSC_cb,
 
	&USR_cb);
 
	       delay_ms(1800);    
 
	       while(1)
 
	       {     
 
	              delay_ms(1);                          
 
	              if(USB_STA!=USB_STATUS_REG)//状态改变了 
 
	              {                                                 
 
	                     LCD_Fill(30,190,240,190+16,WHITE);//清除显示                           
 
	                     if(USB_STATUS_REG&0x01)//正在写                
 
	                     {
 
	                            LED1=0;
 
	                            LCD_ShowString(30,190,200,16,16,"USB
Writing...");//USB正在写数据      
 
	                     }
 
	                     if(USB_STATUS_REG&0x02)//正在读
 
	                     {
 
	                            LED1=0;
 
	                            LCD_ShowString(30,190,200,16,16,"USB
Reading...");//USB正在读数据
 
	                     }                                                                            
 
	                     if(USB_STATUS_REG&0x04)
 
	LCD_ShowString(30,210,200,16,16,"USB Write Err ");//提示写入错误
 
	                     else
LCD_Fill(30,210,240,210+16,WHITE);//清除显示       
 
	                     if(USB_STATUS_REG&0x08)
 
	LCD_ShowString(30,230,200,16,16,"USB Read  Err ");//提示读出错误
 
	                     else
LCD_Fill(30,230,240,230+16,WHITE);//清除显示    
 
	                     USB_STA=USB_STATUS_REG;//记录最后的状态
 
	              }
 
	              if(Divece_STA!=bDeviceState) 
 
	              {
 
	                     if(bDeviceState==1)LCD_ShowString(30,170,200,16,16,"USB
Connected    "); 
 
	                     else
LCD_ShowString(30,170,200,16,16,"USB DisConnected ");//USB被拔出了
 
	                     Divece_STA=bDeviceState;
 
	              }
 
	              tct++;
 
	              if(tct==200)
 
	              {
 
	                     tct=0;
LED1=1;
 
	                     LED0=!LED0;//提示系统在运行
 
	                     if(USB_STATUS_REG&0x10)
 
	                     {
 
	                            offline_cnt=0;//USB连接了,则清除offline计数器
 
	                            bDeviceState=1;
 
	                     }else//没有得到轮询 
 
	                     {
 
	                            offline_cnt++;  
 
	                            if(offline_cnt>10)bDeviceState=0;//2s内没收到在线标记,则USB被拔出了
 
	                     }
 
	                     USB_STATUS_REG=0;
 
	              }
 
	       };  
 
	} 
 
	其中,USB_OTG_CORE_HANDLE是一个全局结构体类型,用于存储USB通信中USB内核需要使用的的各种变量、状态和缓存等,任何USB通信(不论主机,还是从机),我们都必须定义这么一个结构体以实现USB通信,这里定义成:USB_OTG_dev。
 
	然后,USB初始化非常简单,只需要调用USBD_Init函数即可,顾名思义,该函数是USB设备类初始化函数,本章的USB读卡器属于USB设备类,所以使用该函数。该函数初始化了USB设备类处理的各种回调函数,以便USB驱动库调用。执行完该函数以后,USB就启动了,所有USB事务,都是通过USB中断触发,并由USB驱动库自动处理。USB中断服务函数在usbd_usr.c里面:
 
	//USB OTG 中断服务函数   处理所有USB中断
 
	void OTG_FS_IRQHandler(void)
 
	{
 
	      USBD_OTG_ISR_Handler(&USB_OTG_dev);
 
	}  
 
	该函数调用USBD_OTG_ISR_Handler函数来处理各种USB中断请求。因此在main函数里面,我们的处理过程就非常简单,main函数里面通过两个全局状态变量(USB_STATUS_REG和bDeviceState),来判断USB状态,并在LCD上面显示相关提示信息。
 
	USB_STATUS_REG在usbd_storage_msd.c里面定义的一个全局变量,不同的位表示不同状态,用来指示当前USB的读写等操作状态。
 
	bDeviceState是在usbd_usr.c里面定义的一个全局变量,0表示USB还没有连接;1表示USB已经连接。 
 
	软件设计部分,就给大家介绍到这里。
 
	56.4 下载验证
	在代码编译成功之后,我们下载到探索者STM32F4开发板上,在USB配置成功后(假设已经插入SD卡,注意:USB数据线,要插在USB_SLAVE口!不是USB_232端口!另外,USB_HOST接口,也不要插入任何设备,否则会干扰!!),LCD显示效果如图56.4.1所示:
 
	 
 
	图56.4.1 USB连接成功
 
	此时,电脑提示发现新硬件,并开始自动安装驱动,如图56.4.2所示:
 
	
  
 
 
	图56.4.2 USB读卡器被电脑找到
 
	    等USB配置成功后,DS1不亮,DS0闪烁,并且在电脑上可以看到我们的磁盘,如图56.4.3所示:
 
	
  
 
 
	图56.4.3 电脑找到USB读卡器的两个盘符
 
	我们打开设备管理器,在通用串行总线控制器里面可以发现多出了一个USB 大容量存储设备,同时看到磁盘驱动器里面多了2个磁盘,如图56.4.4所示:
 
	
  
 
 
	图56.4.4 通过设备管理器查看磁盘驱动器
 
	此时,我们就可以通过电脑读写SD卡或者SPI FLASH里面的内容了。在执行读写操作的时候,就可以看到DS1亮,并且会在液晶上显示当前的读写状态。
 
	注意,在对SPI FLASH操作的时候,最好不要频繁的往里面写数据,否则很容易将SPI FLASH写爆!! 
 
	 
	
		
	 
	
		正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779 
	 
	
		   
	 
 |