0 引言
USB接口现在几乎成为每一个电子设备的必备接口。本文介绍如何在基于AT91SAM7SE的设备上实现USB协议,使之具有USB传输功能。AT91SAM7SE是ATMEL公司生产的基于ARM7内核的ARM处理器系列,包括AT91SAM7SE512、AT91SAM7SE256、AT91SAM7SE32三种型号,高性能32位RISC结构设计,分别具有512k字节、256k字节、32k字节的集成Flash存储器。论文参考,USB协议。具有丰富的外设资源,包括IO口、SPI、PWM、ADC等。由于该处理器具有UDP(USB DevicePort)接口,因此可以使用该处理器方便的构建USB设备。本文将重点介绍如何在该处理器上实现USB协议。
1UDP控制器描述
AT91SAM7SE处理器UDP接口兼容USB2.0,支持8个端口,每个端口的大小及传输类型如表1所示:
表1 UDP端口描述
端点号 | 缓存大小 | 支持传输类型 | 0 | 64 | 控制/批量/中断 | 1 | 64 | 批量/同步/中断 | 2 | 64 | 批量/同步/中断 | 3 | 64 | 控制/批量/中断 | 4 | 512 | 批量/同步/中断 | 5 | 512 | 批量/同步/中断 | 6 | 64 | 批量/同步/中断 | 7 | 64 | 批量/同步/中断 |
UDP每个端口有两个FIFO缓存器,用于存储当前已接收或将发送的数据。FIFO缓存器可以使用其中的一个或两个。两个FIFO缓存同时使用时,一个FIFO可以通过处理器进行读写,另一个同时被UDP外设读写,提高了数据传输的效率。对于同步传输端点,必须使用双FIFO缓存。
UDP端口对外提供两个USB引脚DP和DM,和USB连接器的连接可参考图1。
图1 USB接口连接图
2 软件设计
2.1 软件结构
为了能够合理的进行USB协议的实现,我们按照图2所示框架进行USB协议的分层设计:
图2 USB协议实现框架
硬件层用来实现底层USB控制器的操作。USB API相对于硬件独立,提供了标准的USB数据结构以及方法函数。USB类驱动提供最顶层的USB方法。应用程序层主要用来实现设备功能。下面具体分析实现图2结构所用的一些结构体和函数,对关键结构和函数给出部分代码:
(1)硬件层,主要实现对USB控制器操作函数。函数针对具体的AT91SAM7SE芯片UDP端口设计,包括使能/禁止D+上拉电阻、使能/禁止UDP收发器、端点FIFO缓存读写、端点中断控制等。一个UDP控制器可用名为UdpController的结构来描述:
typedef struct _UdpController
{
void *pInterface; //UDP控制器地址
int dID; //UDP外设ID
int dPmc; //UDP对应时钟管理ID
} UdpController;
(2)USB API层,该层提供了USB协议操作的一些方法: USB_Attach连接操作,USB设备通过检测+5V电源线来判断和主设备的连接与断开;USB_SetAddress地址设置,接收主机分配的唯一地址,使设备进入地址状态;USB_SetConfiguration配置操作,主机通过标准的USB请求命令获取设备的配置描述符,并对设备进行配置,使USB设备进入配置状态。
在API层定义了一个传输结构,用来描述每个端点的每一次传输,定义如下:
typedef struct _Transfer
{
Char *pData; //指向数据指针
int buffered; //FIFO中的字节数
int transferred; //已经发送(接收)的字节数
int remaining; //剩余的字节数
CallbackfCallback; //回调函数
void *pArgument; //回调函数参数
} Transfer;
该层同时定义了对通信端点的描述,用结构体UsbEndPointsState描述:
typedef struct _UsbEndpointState
{
int wMaxPacketSize; //端点最大包
int dNumFIFO; //使用的FIFO索引
intState; //端点状态
Transfer transfer; //端点对应的传输
}UsbEndpointState;
在该层中,使用UsbEndpointState结构构成一个端点列表,该列表指针存储在USB类驱动层。
(3)USB类驱动层。论文参考,USB协议。论文参考,USB协议。该层位于USB设计框架的最高层。定义了标准的USB请求结构,由设备描述符、配置描述符、接口描述符、端点描述符组成。这些结构体的实现可参考USB协议规范,在此不做描述。论文参考,USB协议。在该层定义一个总体的USB设备驱动类,其结构如下:
typedef struct_UsbDeviceDriver;
{
UdpController*pUdpCtrl;//UDP控制器
UsbEndpointState*pEndpts;//端点状态列表
UsbStandardRequest *pStandardRequeat;
/ /最新收到标准请求包
Int DeviceState; //设备状态
int nCurCfgNum; //对应配置
}UsbDeviceDriver;
在USB通信中,每一次读写操作的状态都包含在UsbDeviceDriver结构中,读写操作函数以该结构作为输入。
2.2 读写流程
对于一次读写操作,要确定使用的端口号,数据缓存指针,读写完毕的回调函数。读写函数简介如下:
char USB_Read(
UsbDeviceDriver *pUsbDriver, //USB类驱动
unsigned char nEpIdx, //使用端点索引
void*pData,//数据存储地址
unsigned int dLength,//数据长度
Callback fCallback, //完成时的回调函数
void *pArgument); //回调函数参数
在函数内部首先通过端点索引从pUsbDriver指向的类驱动中获得对应端点的描述结构体UsbEndpointState,把数据传输信息写到该结构体的Transfer中。最后调用硬件层中的UDP控制器函数,开启相应端口中断,等待主机数据的发送。对于写操作具有和读操作一样的函数输入参数,不同的是最后对硬件层的UDP控制器操作为写FIFO缓存。读操作流程如图3所示。在读写操作函数中,要初始化端点对应的Transfer结构数据。以传输100个字节数据为例,字节数据放入data[100]数组中。使用端口2进行传输。则端口2 的UsbEndpointState结构中对应的Transfer结构初始化如下:
pTransfer->pData = data;
pTransfer->remaining = 100;
pTransfer->buffered = 0;
pTransfer->transferred = 0;
每完成一次端点包的传输pTransfer->remaining相应减少,pTransfer->transferred相应增加。pTransfer->buffered中的数据为在端点FIFO中还未读入或还未发送的字节数。
图3 USB读写流程图
读写流程完成后,整个USB协议层就可以进行读写了,此时可以在应用程序层上根据具体的应用编写自己的通信协议。
2.3. USB验证
要想使USB设备和主机进行通信,在主机端还要有USB设备的驱动程序,对于驱动程序的设计本文不做论述。将USB设备插入主机,若在主机管理器中能识别该USB设备,说明该USB设备能正常枚举。论文参考,USB协议。作者使用该处理器实现一个矢量网络分析仪中的电子校准件设备,插入主机后,设备管理器中显示如图4显示。
图4 USB设备识别例图
3 结束语
本文给出了基于AT91SAM7SE处理器的USB协议编程方法,使用该设计方法,软件结构清晰,可维护性强。论文参考,USB协议。对于其他ARM处理器具可参考此方法进行设计。
|