嵌入式系统中USB主机控制器的设计
扫描二维码
随时随地手机看文章
过去USB仅应用于个人计算机,而在嵌入式系统领域的巨大潜力还没有开发出来,USB在嵌入式系统中的应用包括KVM开关、数码相机、PDA、打印机、机顶盒以及移动电话等。本文将介绍在嵌入式系统中应用USB时其主机控制器的设计。
嵌入式系统被定义为硬件和固件(独立的或作为更大型系统的一部分)通常带有某种操作系统,操作系统可以是Windows CE、VxWorks或其它由“自编代码”构成的更简单系统。根据这样的定义,可以认为任何带有处理器的电子装置均可以作为USB嵌入式主机。
嵌入式系统设计挑战
在基于PC的系统中,USB操作一般需要三种部件,分别是通常作为PCI子系统的主机控制器、USB堆栈以及USB类驱动器。
主机控制器是集成主板芯片组的一部分,USB堆栈则包含主板芯片及通用主机控制器接口(UCHI)和开放主机控制器接口(OHCI)驱动程序以及USB驱动程序(usbd.sys),在PC上实现USB需要上述领域的专门技术。
在嵌入式USB系统中,其主要组成部分与PC系统类似,如嵌入式主机控制器芯片、带OHCI堆栈的实时操作系统(RTOS)以及专用驱动程序。现有很多可供选择的主机控制器芯片,有些带有处理器,有些则是基于寄存器的,对器件的选择将影响到其下面两层。
很多公司都可提供RTOS,最好选择一个能配合在一起工作的处理器和RTOS,然后在其上添加应用代码。如果没有真正的RTOS,某些控制器则用一个“框架”,可在其上构造应用程序。我们后面将介绍这种框架以及如何在上面构建应用。
在PC上实现USB具有非常丰富的可用资源,包括高达512MB的存储器、20-60GB的硬盘以及2GHz或更快的微处理器。此外,多年来Windows、MAC OS以及Unix等操作系统也一直支持USB,而且世界上还有成千上万的工程师在设计基于PC的USB应用程序和设备驱动程序。而对嵌入式系统来说,通常只有不超过64K的存储器,以及运行于12MHz~33MHz的处理器,且没有硬盘。由于USB对嵌入式系统相对较新,因此可能只有为数不多的工程师拥有这方面的经验。
控制器与框架
下面我们以赛普拉斯EZ-Host为例介绍嵌入式系统USB控制器与框架结构。EZ-Host有两个“串行接口引擎”,每个引擎包含两个USB端口,因此无需使用额外硬件EZ-Host便可控制四个USB设备。
EZ-Host器件具有固件结构,可管理大多数USB主机的详细请求。该结构另一个特点是支持网络集线器。键盘/集线器组合在一起常常带来这样的问题,即它究竟是带有集线器的键盘还是带有键盘的集线器?答案应该是带有键盘的集线器。因此要了解集线器后面的键盘,还需要提供集线器支持。幸好,这里的框架代码包含了对集线器的支持。
EZ-Host框架包含所有实现USB主机功能所必需的固件,包括任务调度、设备枚举、带宽分配以及功率管理。另外应用程序作为固件的一部分,控制专用USB设备并将其数据传递给最终应用。
框架的核心是TD处理器。TD处理器的运行基于一种称为“任务描述器(TD)”的数据结构,使用其信息与USB硬件尤其是“串行接口引擎(SIE)”进行通信。需要注意的是每个SIE控制两个端口,而且每个SIE具有一个TD处理器。EZ-Host框架使用了多种数据结构实现其操作,这些结构包括TD和USB请求模块(URB)。
任务描述器是传递给硬件的数据结构,包含特定硬件接口(如SIE)和端口编号的数据字段、终点数、收发数据缓冲器长度、数据包ID编码以及URB结构指示器。
URB含有TD所需的逻辑信息,该逻辑信息包括USB设备缓冲器、安装软件包以及USB设备结构指示器。
进行USB事务处理时,URB带有事务处理分配及其装入的数据结构,而后URB提交给TD处理器,TD处理器再将URB加入TD列表。空闲时,TD处理器处理TD列表,安排传输时序,并将设定好的TD传送给EZ-Host硬件进行处理。[!--empirenews.page--]
为了执行控制转移,可以使用框架函数send_request(),send_request()函数将分配一个传递给TD处理器的URB结构。URB应该包含有关设置状态的信息,并且借助参数传递给send_request()。URB需要的信息包括:
* 请求类型:表明USB请求类型的字节,该字节包含表示传输方向、传输类型以及传输接受方的位。
* 请求:11种标准USB请求中的一种,这些请求包括:Clear_Feature、Get_Configuration、Get_Descriptor、Get_Interface、Get_Status、Set_Address、Set_Configuration、Set_Descriptor、Set_Feature、Set_Interface、Synch_Frame。
* 值:特殊请求字段。
* 索引:特殊请求字段。
* 长度:相关数据缓冲器的大小
载入用于URB的设置信息之后,便可将其它设备信息装入URB,如地址、速度、终点数以及传输方向,还有表示传输类型的字节和“回调”函数指示器。利用回调函数可以在框架内进行某些并行操作,也可在硬件处理USB操作的同时执行其它任务。TD需要USB传输类型信息,这样框架就可以安排正确的传输类型时序。此外,与批量或中断传输相反,框架一次只允许进行一个控制传输操作。
装入所有URB信息之后,URB便被提交给TD处理器。TD处理器是框架的组成部分,它与硬件直接通信,处理有关传输的低级详细资料。TD完成任务后,TD处理器将程序控制权由原始调用指定的“回调”函数转给send_request()函数。
图2显示了调用带回调函数的send_request()。这里我们使用控制传输以获得某键盘的国家代码,在get_country_code()函数中可看到send_request()的调用,注意send_request()调用中的最后一个参数是回调函数。在TD处理器确定硬件完成处理后执行该函数,此时回调函数将获得返回的数据缓冲器,将其与HID描述器结构相匹配,并存取与国家代码对应的字节,然后URB被释放。
EZ-Host框架值得注意的最后一个特点是设备驱动程序的使用。在执行过程中,设备驱动程序将执行三个功能,即停止、启动和运行。启动某设备驱动程序便是运行它的run()函数,该函数对某些数值进行初始化,并分配一个用于数据传输的重复出现的URB。对于鼠标或键盘,该URB将每隔10毫秒发生一次。数据传输完成后,TD处理器将控制转交给interrupt_in_complete()函数,通过检查URB可得知数据是来自键盘还是来自鼠标,该回调函数负责将键盘或鼠标数据发送至应用层。
当某驱动程序停止时,其重复出现的URB将从TD列表中除去并释放出空间,然后传送一个消息至应用层,去掉相关设备。如果停止的驱动程序相关设备是集线器,则与该集线器相连的所有设备也要去除,且驱动程序同时停止。当然,如果去除的设备中还有集线器,则与该集线器相连的设备将以同样方式去除。
在驱动程序运行期间,系统可执行各种任务。对于集线器设备驱动程序,要检查集线器的端口,以了解是否有设备插入和去除。这里键盘和鼠标驱动程序运行函数不起任何作用。[!--empirenews.page--]
框架数据流
框架代码执行过程是这样的:上电复位、微处理器对所有寄存器和计数器以及设备结构进行初始化,然后进入如下的循环:
1.检查主机USB端口是否存在状态改变(设备插入或去除)。
2.检查TD处理器,并获得两个SIE上运行的所有TD状态信息。
3.查看运行设备驱动程序列表并执行每个程序的运行函数。
检查主机USB端口以了解状态变化需要检查变量的改变,如果发生变化,可通过端口变化中断处理程序设定变量。如果端口发生改变,将执行枚举代码进行处理。
通过集线器至主机的中断传输完成相连集线器变化值检查,如果发生设备添加或去除,它们将像上面那样枚举出来。发现新设备时,需要找到设备驱动程序然后装入。根据设备寻找设备驱动程序的方法有很多种,框架代码将首先尝试将某驱动程序与某设备的供应商ID以及产品ID进行匹配,但只有存在特定的制造商且特定设备在特定驱动程序中运行这种方法才有效。如果没有实现匹配,框架代码会尝试对设备的种类和子类进行匹配,这可以利用更普通的驱动程序与设备匹配。
设备插入和去除检查还有两项额外的任务。如果连接的设备是集线器,则必须对其端口进行检查,以查看它们是否带有设备。如果去除的设备是集线器,那么所有与之连接的设备也必须去除。
通过中断传输还可以检查来自相连键盘和鼠标设备的新数据,这些传输每10毫秒种发生一次,由TD处理器安排时序。任务完成后,TD处理器将传输控制转给回调函数,这时可提取键盘和鼠标数据,并送至应用层。
构建应用
现在介绍如何构建一个简单的控制键盘和鼠标的嵌入式USB设计,该方案使用基于处理器的USB主机控制器,处理器的代码包含框架和应用固件。首先要做的是确定希望支持设备的数量和类型,确定设备数量后,可据此分配URB和驱动程序空间,通过修改名为fwxcfg.h的“个性化”文件完成URB分配。
每个键盘、鼠标或集线器均需要一个URB来处理传输中重复出现的中断。此外,在枚举和其它USB控制传输过程中,URB被分配并随后释放。一个较好的经验是为系统支持的每个设备分配两个URB,一个用于传输中重复出现的中断,另一个则用于任何可能的控制传输,这些传输可能发生在设备枚举或设备状态检查过程中。虽然每次只能处理一个控制传输,但框架可将其它传输排队,系统支持的URB数量应该等于所支持设备数量的两倍。
接下来,需要为键盘和鼠标创建驱动程序。由于这些设备的USB功能非常相近,所以两个设备可以使用一个驱动程序。该驱动程序可称为hid_driver(用于人机界面设备的驱动程序)。下一步是在驱动程序内添加开始、停止以及运行函数,以及查找驱动程序的设备种类编码,还需要将驱动程序函数的名称添加进文件drvrlist.h。包含集线器和hid driver的驱动程序声明如下: #define FWX_DRIVER_LIST {&hid_driver, &hubclass_driver}
hid_driver启动函数将分配传输中重复出现的中断,以获得键盘和鼠标数据。该函数内的编码将获取数据并将数据传给编码应用层;停止函数将释放重复出现的中断传输,并通知应用层设备已被去除;运行函数用来检查来自应用代码的输入指令。
然后需要为枚举通报函数添加一些代码,枚举通报函数是枚举代码使用的回调函数,用于报告设备枚举状态并处理可能的枚举错误。该代码可将新的枚举设备信息传给应用层。
在这里对集线器提供支持很容易,不需要添加任何代码,也不需要编写驱动程序,因为框架已包含驱动程序。所要做的是更改fwxcfg.h中的语句,即将
#undef FWX_INCLUDE_HUB_SUPPORT
改为
#define FWX_INCLUDE_HUB_SUPPORT
并将文件hubclass.c添加进项目形成文件,其余事务由框架处理。
本文小结
尽管与PC相比,USB主机系统在计算资源和经验方面存在不足,但开发人员仍然可以较为简单地将USB功能加入到嵌入式系统中。现有多种用于实现这种功能的USB主机IC可供选择,用户可购买或自行开发USB主机堆栈和实时操作系统。