数组name中保存了具体姓名对应的英文字符串,为了能够表达世界上所有语言的字符,我们采取了目前通用的Unicode,它采用双字节16位进行编号,可最多编码65536个字符。英文字母的ASCII补0(一个字节)就是相应的Unicode(低位在前)。例如,字符“A”的ASCII为0x41,则相应的Unicode编码为0x0041。而在数组longhu中,“姓名”字段则只保存name数组中对应的索引即可。我们再约定“0”代表女性,“1”代表男性,所以“性别”字段对应的数值就是1。年龄为35自然不用多说。需要特别注意的是,我们再次约定使用两个字节的BCD码来表示工号,并且低2位在前面,高2位在后面,所以相应的工号为“7259”。我们也可以进一步增加更多信息,这样数组longhu就可以完整地表达员工的信息(描述符)了。
USB规范定义的描述符也是类似的,常用的标准描述符就包括设备描述符(Device Descriptor)、配置描述符(Configuration Descriptor)、接口描述符(Interface Descriptor)、端点描述符(Endpoint Descriptor)、字符串描述符(String Descriptor),它们定义的字段比之前的员工描述符更复杂一些,但基本的原理仍然相同。也就是说,我们总是会使用类似常量整数数组的方式保存关于USB设备的一些信息,主机会通过特定的命令来查询,USB设备只需要在必要的时候响应主机的命令即可。当然,我们没有必要对所有细节都亲力亲为,厂商已经将底层细节实现打包成了固件库,甚至发布好了经过测试的例程,我们只需要找到描述符定义所在的源文件,再根据USB规范修改一些常量数据即可。如果这些常量数据修改正确,厂商提供的固件库会自动根据主机发送的命令提交描述符信息,这样主机就能够正确识别你的USB设备,也就完成了整个总线枚举过程。
与使用单片机控制元器件一样,我们还需要清楚元器件所在的状态。例如,不能在元器件处于复位期间发送命令,也不能在休眠状态下发送它无法响应的命令。在枚举与正常工作过程中,USB设备也会处于不同的状态,主机也必须清楚设备当前所处的状态。USB规范定义了6种USB设备的状态,如表10.1所示(符号“—”表示不关心)。
表10.1 USB设备的状态
USB设备可以与主机连接(Attached)或断开(Detached),连接状态指的是物理层面的状态,此时还没有直接与USB接口的信号线连通,而供电状态(Powered)则可以理解为仅电源连接(信号线未连接)。我们前面已经提过,为了实现USB设备热插拔,USB物理接口中的电源线比数据线要长一些,所以才会存在供电状态。当然,你也可以认为供电状态是USB设备的数据线已经与主机连接,但是还没有对总线有任何响应之前的状态。
默认状态(Default)是USB设备的数据线与主机连接后,主机对设备进行复位后的状态。所有USB设备在供电与复位以后都使用默认地址(也就是0),此时设备处于可编址状态(Addressable),图10.2展示了未连接、连接、供电、默认4个状态(仅用于状态理解示意)。
图10.2 设备状态
设备处于可编址状态(默认状态)后,USB主机给设备分配一个唯一的地址,设备进入了编址状态(Address),而在USB设备正常工作前还必须被正确配置,这样成功配置后的USB设备才会最终处于已配置状态(Configured)。挂起状态(Suspended)主要是为节省电源,所有设备在一段特定时间(典型值为3ms)内没有总线活动时会自动进入该状态,无论设备是否已经分配地址或者完成配置,此时USB设备保持本身的内部状态(包括地址及配置)。另外,如果USB设备所连接的集线器端口失效(例如,使用命令禁用某个端口),USB设备也会进入挂起状态,这在USB规范中属于选择性挂起(Selective Suspend)。相反,当总线有活动时,设备会被唤醒(Wakeup)而退出挂起状态,也称从挂起状态恢复(Resume)。
USB规范给出了设备状态的转换关系,如图10.3所示,填充圆圈中的状态对主机是可见的。
图10.3 设备状态的转换关系
对于我们设计的基于STM32单片机的USB设备,所有状态被定义在usb_pwr.h头文件中,如清单10.3所示。其中,DEVICE_STATE枚举类型中定义了6种状态,当USB设备未与主机相连时,默认处于未连接状态(UNCONNECTED)。只有当USB设备处于已配置状态(CONFIGURED)时,USB设备才是可用的,所以在清单9.1中,每次读取按键状态前必须先判断设备的状态(bDeviceState)是否处于已配置状态,因为在USB设备尚未完成配置前,即使有按键按下的情况发生,也无法正常给主机发送数据。另外,需要特别注意的是,DEVICE_STATE枚举类型中定义的ATTACHED对应表10.1中的默认状态,后续在阅读代码时就会知道。
清单10.3 usb_pwr.h头文件