接口技术-USB驱动程序简要说明及应用例子

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

USB 驱动程序简要说明及应用例子
USB 总线主要用于USB 设备与主机之间的数据通信,特别为USB 设备与主机之间大量数据的传输提供了高速、可靠的传输协议。

例如:在嵌入式系统中,可以利用USB 设备与微控制器构成USB 设备。

USB 设备与PC 机USB 主控器相连就可以实现嵌入式系统与PC 机之间的通信了,也就可以实现诸如U 盘、移动硬盘、USB 接口打印机等功能。

本驱动程序利用Philips 公司生产的USB 芯片PDIUSBD12与微控制器构成USB 设备,在µC/OS-II 操作系统上实现该USB 设备与PC 机通信的USB 协议,为用户提供与PC 机通信的API 函数。

下面先简单介绍USB 芯片PDIUSBD12。

1.1 PDIUSBD12简介
PDIUSBD12是一款性价比很高的USB 器件,完全符合USB1.1版的规范。

具有以下特性:
高性能USB 接口器件,集成了SIE 、FIFO 存储器、收发器以及电压调整器; 可与任何外部微控制器/微处理实现高速并行接口(2M 字节/秒); 完全自治的直接内存存取(DMA )操作; 集成320字节多结构FIFO 存储器;
在批量模式和同步模式下均可实现1M 字节/秒的数据传输速率; 在挂起时可控制LazyClock 输出;
采用GoodLink 技术的连接指示器,在通讯时使LED 闪烁; 可编程的时钟频率输出;
符合ACPI 、OnNOW 和USB 电源管理的要求; 内部上电复位和低电压复位电路; 有SO28和TSSOP28封装;
工业级操作温度:-40℃~+85℃;
高于8kV 的在片静电防护电路,减少了额外元件的费用; 具有高错误恢复率(>99%)的全扫描设计确保了高品质; 双电源操作:3.3±0.3V 或扩展的5V 电源,范围为3.6~5.5V ;
PDIUSBD12管脚排列及内部功能框图如图1所示,表1为PDIUSBD12的管脚说明。

123456789
101112171819202122232425262728DATA<0>DA TA<1>DA TA<2>DA TA<3>
GND DA TA<4>DA TA<5>DA TA<6>DATA<7>
ALE CS_N A0V OUT3.3D+D–V DD XTAL2XTAL1RESET_N GL_N EOT_N DMACK_N DMREQ SUSPEND 1316WR_N CLKOUT 14
15RD_N
INT_N
图1 PDIUSBD12管脚排列及内部功能框图
如图1所示,PDIUSBD12芯片内部集成了串行接口引擎SIE、存储器管理单元(MMU)和集成RAM、模拟收发器以及电压调整器。

可与任何外部微处理器实现高速数据传输的并行接口(2MB/s)。

PDIUSBD12芯片提供3个端点,其中主端点(端点2)的双缓冲配置增加了数据吞吐量并轻松实现实时数据传输。

PDIUSBD12采用GoodLink技术的连接指示器,在通讯时使LED闪烁, 为通信状态提供了用户友好的指示, 方便用户调试。

SoftConnect TM技术允许系统微控制器可控制USB总线重新连接和重新初始化而不需要拔出电缆。

PDIUSBD12的详细介绍请见北航出版的<<PDIUSBD12 USB固件编程与驱动开发>>或到网站下载PDIUSBD12使用手册。

表1 PDIUSBD12管脚描述
管脚符号类型描述
1 DATA<0> IO
2 双向数据位0
2 DATA<1> IO2 双向数据位1
3 DATA<2> IO2 双向数据位2
4 DATA<3> IO2 双向数据位3
5 GND P 地
6 DATA<4> IO2 双向数据位4
7 DATA<5> IO2 双向数据位5
8 DATA<6> IO2 双向数据位6
9 DATA<7> IO2 双向数据位7
10 ALE I 地址锁存使能。

在多路地址/数据总线中,下降沿关闭地址信息锁存。

将其固定为低电平用于单地址/数据总线配置。

11 CS_N I 片选(低有效)
12 SUSPEND I,OD4 器件挂起状态,高电平表示器件处于挂起状态
13 CLKOUT O2 可编程时钟输出(可控分频)
14 INT_N OD4 中断(低有效)
15 RD_N I 读选通(低有效)
16 WR_N I 写选通(低有效)
17 DMREQ O4 DMA请求
18 DMACK_N I DMA应答(低有效)
19 EOT_N I DMA传输结束(低有效)。

EOT_N仅当DMACK_N和RD_N或WR_N一起激活时才有效。

20 RESET_N I 复位(低有效且不同步)。

片内上电复位电路,该管脚可固定接V CC。

21 GL_N OD8 GoodLink LED指示器(低有效)
22 XTAL1 I 晶振连接端1(6MHz)
23 XTAL2 O 晶振连接端2(6MHz)。

如果采用外部时钟信号取代晶振,可连接XTAL1,XTAL2应当悬空。

24 V CC P 电源电压(4.0V~5.5V),要使器件工作在3.3V,对V CC和V OUT3.3脚都提供3.3V。

25 D- A USB D-数据线
26 D+ A USB D+数据线
(接上表)
27 V OUT3.3P 3.3V调整输出。

要使器件工作在 3.3V,对V CC和V OUT3.3脚都提供3.3V。

28 A0 I 地址位。

A0=1选择命令指令,A0=0选择数据。

该位在多路地址/数据总线配置时可忽略,应将其接高电平。

注: O2 :2mA驱动输出OD4 : 4mA驱动开漏输出
OD8 : 8mA驱动开漏输出IO2 : 4mA输出
在没有使用到DMA方式的时候,DMACK_N和EOT_N要求接上拉电阻。

明白了PDIUSBD12所提供的资源与硬件接口,便可以设计PDIUSBD12与LPC2200的硬件电路了。

1.2 PDIUSBD12与LPC2200的硬件电路设计
这里以LPC2200微控制器与PDIUSBD12构成USB设备,PDIUSBD12连接到LPC2200的硬件原理图如图2所示, 由该图可见PDIUSBD12各引脚(图2中的网络标号)与LPC2200的连接关系, 如表2所示。

表2PDIUSBD12与LPC2200连接关系
PDIUSBD12 功 能 LPC2200
D0 ~ D7 PDIUSBD12数据总线D0 ~ D7
AD0 PDIUSBD12地址总线A0
CS_USB PDIUSBD12片选线nCS2 RD PDIUSBD12读使能(低电平有效) nOE
WR PDIUSBD12写使能(低电平有效) nEW INT_USB PDIUSBD12中断输出信号P0.16_EINT0
RST_USB PDIUSBD12复位输入信号P0.10_RTS1
SUSP PDIUSBD12挂起输入信号P0.13_DTR1
由以上关系,可知PDIUSBD12使用LPC2200外部存储控制的Bank2部分,其地址如下:
数据地址 0x82000000(偶数地址)
命令地址 0x82000001(奇数地址)
RST_USB、SUSP为LPC2200输出引脚,PDIUSBD12中断信号为中断输入信号,且为外部中断0。

如图2所示C1、C2和X1为PDIUSBD12提供工作所需的时钟输入;发光二极管GoodLink在正常通信时闪烁;PDIUSBD12的AD0连接到LPC2200的A0,当LPC2200在A0引脚输出1时,表示输出到PDIUSBD12数据总线D0 ~ D7上的数据为命令字,当A0引脚输出0时,表示输出到PDIUSBD12数据总线D0 ~ D7的数据为数据字。

图2 PDIUSBD12与LPC2200硬件连接原理图
1.3 PDIUSBD12驱动软件构架
为了使驱动软件可移植性强、易维护,采用分层的方法编写PDIUSBD12的驱动程序。

综合考虑USB协议、PDIUSBD12硬件接线、µC/OS-II的结构来组织驱动软件的构架,如表3所示为USB驱动程序软件分层结构表。

表3 USB驱动程序软件包分层结构表
文件名简要说明相关性
D12HAL.c PDIUSBD12与MCU之间的硬件接口与硬件相关
D12CI.c PDIUSBD12命令接口与硬件无关
Chap_9.c USB协议层与µC/OS-II相关
Descriptor.c USB协议层与µC/OS-II相关
D12Driver.c USB应用层与µC/OS-II相关
其中Chap_9.c、Descriptor.c与D12Driver.c与µC/OS-II相关,以上各层关系可用图3用来表示。

图3 USB驱动分层结构图
1.4 USB驱动程序软件包的使用
本驱动软件包包括的文件见表4。

表中的所有文件保存于本文档目录下的D12 For uCos-II\USBDriver\D12目录中,目前本USB驱动程序软件包的版本号为1.0。

表4 USB驱动程序主要文件列表
文件名描述及作用
D12Config.h D12驱动程序的配置,包括所有与硬件相关的配置
D12HAL.c USB设备控制驱动,与具体使用的MCU及硬件相关
D12HAL.h USB设备控制驱动D12HAL.c的头文件
D12CI.c USB接口控制驱动,与具体使用的USB芯片相关
D12CI.h USB接口控制驱动D12CI.c的头文件
Descriptor.c USB协议层,PDIUSBD12所有描述符取值
Descriptor.h USB描述符结构体的定义,以及PDIUSBD12用到的一些宏定义取值
Chap_9.c USB协议层,完成所有与控制传输相关的工作
Chap_9.h USB协议层Chap_9.c的头文件,包含控制传输用到的结构体的定义
D12Driver.c USB应用层,包含提供给用户使用的API函数
D12Driver.h USB应用层D12Driver.c的头文件
D12 For uCos-II\USBDriver\目录的USBDriver.mcp工程提供了使用该驱动程序的一个例子。

该例子建立时使用的工程模板为:ARM Executable Image for UCOSII(for lpc2200)。

如果你的硬件配置与软件包的默认配置不同。

那么只须修改软件包中的D12Config.h文件,该文件主要是配置PDIUSBD12与LPC2200的引脚。

D12Config.h文件相关配置的修改的方法请见《嵌入式系统软件开发实例》中的《USB驱动程序开发》,这一章详细地说明了该软件包的开发方法及该文件的配置方法。

除此以外,还有几点需要注意:
(1)每个C文件的开始都包括了头文件(#include "config.h"),该头文件是工程模板ARM Executable Image for UCOSII(for lpc2200)的配置文件,软件包中默认已加上。

(2)在工程模板的IRQ.s文件的END语句之前加上下面的语句(例子中默认已加上): Usb_Handler HANDLER Usb_Exception (3)由于PDIUSBD12收发大量数据时中断比较频繁,因此必须修改工程模板中允许中断嵌套的层数,因此Startup.s文件的第二行应改为(例子中已进行修改):IRQ_STACK_LEGTH EQU 9*25 ;每层嵌套需要9个字堆栈,允许25层嵌套
(4)本例子使用了3个事件,控制传输处理使用了1个信号量,例子应用程序中使用了2个信号量。

因此os_cfg.h文件中操作系统最大事件数的定义应至少为3。

#define OS_MAX_EVENTS 3
(5)本例子不使用统计任务,因此把统计任务关闭(os_cfg.h文件中):
#define OS_TASK_STAT_EN 0
(6) PDIUSBD12使用了外部存储空间的Bank2部分,所以必须配置LPC2200的BCFG2寄存器,在模板的target.c文件的TargetInit()函数添加以下代码:
BCFG2 = 0;
BCFG2 |= (0x04 << 5)|(0x01 << 10)|(0x04 << 11); //配置BCFG2控制寄存器
(7)工程模板配置外部RAM和外部FLASH ROM总线的访问速度是适应于大部分型号的RAM与FLASH ROM的。

对于EasyARM2200开发板上的RAM与FLASH ROM的型号,必须重新配置控制总线有关参数的BCFG0和BCFG1寄存器,这样程序的运行速度才能跟上PDIUSBD12的运行速度。

这些值的配置的具体方法见LPC2200数据手册以及你所使用的RAM与FLASH ROM的数据手册。

程序的运行速度主要与CPU主频与外部RAM、FLASH速度相关。

这方面在模板的target.c的TargetResetInit()函数中修改。

如程序清单1所
示。

程序清单1 配置TargetResetInit ()函数
#ifdef __DEBUG //开发板上JP6接为:Bank0-RAM,Bank1-Flash,JP7接为:OUTSIDE MEMMAP = 0x3; //remap
BCFG0 = 0x10000400; //外部RAM
BCFG1 = 0x10000460; //外部Flash
#endif
#ifdef
__OUT_CHIP //开发板上JP6接为:Bank0-Flash,Bank1-RAM,JP7接为:OUTSIDE MEMMAP = 0x3; //remap
BCFG0 = 0x10000460; //外部FLASH
BCFG1 = 0x10000400; //外部RAM
#endif
#ifdef __IN_CHIP //开发板上JP6接为:Bank0-RAM,Bank1-Flash,JP7接为:INSIDE MEMMAP = 0x1; //remap
BCFG0 = 0x10000400; //外部RAM
BCFG1 = 0x10000460; //外部Flash
#endif
USB驱动程序软件包提供给用户6个API函数。

这6个函数都在D12Driver.c文件中。

下面说明这6个函数的作用以及入口参数,如表5到表10所示。

表5 Init_D12()函数
函数名称Init_D12
函数原型INT8U Init_D12(void)
功能描述设置D12与硬件的连接, 初始化D12, 复位D12,初始化相关变量
函数参数无
函数返回值 0:
初始化成功;1: 初始化失败
特殊说明和注意点该函数执行时间比较长,且执行时关闭了中断,所以只允许在µC/OS-II开始运行时调用。

表6 TaskSetup()函数
函数名称TaskSetup
函数原型void TaskSetup(void *pdata) 功能描述控制传输处理任务
函数参数 void
*pdata:任务入口参数函数返回值无
特殊说明和注意点在µC/OS-II开始运行时要创建该任务,该任务的堆栈为:OS_STK TaskSetupStk[128];该任务的优先级必须高于用户任务,以便USB能够及时完成枚举。

建议设置该任务优先级设为0。

表7 ReadPort1()函数
函数名称ReadPort1
函数原型INT8U ReadPort1(INT32U len,INT8U *recbuff,INT16U timeout) 功能描述从端点 1 读出 len个字节
函数参数INT32U len:要读出的字节数(取值范围为0x00000001 ~ 0xFFFFFFFF)INT8U *recbuff:接收数据指针(长度为len)
INT16U timeout:超时时间, 必须大于等于0
函数返回值 0:
读出成功;>
0:
读出失败(错误码,见表11)
特殊说明和注意点如果用户需要无限等待,那么请在该函数返回超时错误时再次调用该函数,直至函数返回0(读出成功)为止。

表8 ReadPort2()函数
函数名称ReadPort2
函数原型INT8U ReadPort2(INT32U len,INT8U *recbuff,INT16U timeout) 功能描述从端点 2 读出 len个字节
函数参数INT32U len:要读出的字节数(取值范围为0x00000001 ~ 0xFFFFFFFF)INT8U *recbuff:接收数据指针(长度为len)
INT16U timeout:超时时间, 必须大于等于0
函数返回值 0:
读出成功;>
0:
读出失败(错误码,见表11)
特殊说明和注意点如果用户需要无限等待,那么请在该函数返回超时错误时再次调用该函数,直至函数返回0(读出成功)为止。

表9 WritePort1()函数
函数名称WritePort1
函数原型INT8U WritePort1(INT32U len,INT8U *sendbuff,INT16U timeout) 功能描述用端点 1 发送len个字节
函数参数INT32U len: 发送的字节数(取值范围为0x00000001 ~ 0xFFFFFFFF)INT8U sendbuff: 发送缓冲区指针
INT16U timeout: 超时等待时间, 必须大于等于0
函数返回值 0:
发送成功;>
0:
发送失败(错误码,见表11)
特殊说明和注意点如果用户需要无限等待,那么请在该函数返回超时错误时再次调用该函数,直至函数返回0(发送成功)为止。

表10 WritePort2()函数
函数名称WritePort2
函数原型INT8U WritePort2(INT32U len,INT8U *sendbuff,INT16U timeout) 功能描述用端点 2 发送len个字节
函数参数INT32U len: 发送的字节数(取值范围为0x00000001 ~ 0xFFFFFFFF)INT8U sendbuff: 发送缓冲区指针
INT16U timeout: 超时等待时间, 必须大于等于0
(接上表)
函数返回值 0:
发送成功;>
0:
发送失败(错误码,见表11)
特殊说明和注意点如果用户需要无限等待,那么请在该函数返回超时错误时再次调用该函数,直至函数返回0(发送成功)为止。

表7至表10中提到的函数返回值所代表的错误码的定义如表11所示,这些错误码的定义在D12Driver.h文件中。

表11 读写端点API函数返回值
错误码宏定义宏定义值含义
USB_NO_ERR 0x00 函数执行成功
USB_ERR_NO_CONFIG 0x01 USB总线未配置完成错误
USB_ERR_BUFF_INV ALID0x02 接收或发送缓冲区错误
USB_ERR_WR_TIMEOUT 0x03 接收或发送超时错误
调用读写端点函数:WritePort1()、WritePort2()、ReadPort1()、ReadPort2()必须遵守以下规则:
读写端点的任务必须保证有机会能调用到以上函数,才能成功收发数据。

这是因为以上函数给出了接收数据的缓冲区或发送数据的缓冲区。

只有给出了缓冲区,从主机发来的数据才有“地方(接收缓冲区)”让它存放。

否则本驱动程序将收到的数据都“扔掉”。

同理,只有这些函数给出了需要发送的数据在什么“地方(发送缓冲区)”,驱动程序才能有目的地把数据发送出去。

1.5 软件包使用例子
下面举一个例子来说明怎样使用表5至表10的API函数,例子演示了两个任务之间在争着从端点2发送1024个字节。

编写USB应用程序需要编写两个程序:
(1)PDIUSBD12设备端程序。

(2)USB主机(PC机)端程序。

1. PDIUSBD12设备端程序分析
设备端的程序运行由µC/OS-II操作系统控制,它被动地等待来自PC机的命令,操作系统的启动任务为TaskStart(),它初始化PDIUSBD12以及创建各个任务以后,其运作过程如图4所示。

图中以及下面提到的RW_NUMS宏定义的值为1024。

其中优先级为0的任务为TaskSetup(),它负责完成USB的控制传输处理(枚举和标准设备请求)。

经过PDIUSBD12的初始化以及枚举成功之后,µC/OS-II操作系统的运作过程为:(1)任务1(TaskRec1)和任务2(TaskRec2)分别在等待信号量TaskRec1_Sem和TaskRec2_Sem。

而起动任务(TaskStart)一直在等待主机命令。

(2)TaskStart如果收到命令字0x01,就发送信号量给TaskRec1,使其就绪。

如果收到其它命令字,则发信号量给TaskRec2,使其就绪。

(3)任务1收到信号量以后,发一个字节ack给主机,作为应答。

主机收到应答后,发1024个字节给该任务,该任务用ReaPort2()接收这1024个字节。

然后延时1个时钟节拍,最后该任务将收到的1024个字节发送给PC机(WritePort2())。

(4)任务2收到信号量以后,它的动作也与任务1一样,只是任务2接收到1024字节后,没有延时就立即发送1024个字节给PC机。

起动任务TaskStart(优先级3)
图4 例子运行示意图
任务1和任务2发送1024个字节给PC机时,第一个字节为对应任务的优先级(Buff[0] = OSPrioCur)。

这样,主机就知道收到的数据来自哪一个任务了。

任务1收到信号量以后,还要给主机一个应答,而不是立即就接收来自主机的1024个字节,这是因为任务1收到信号量不一定就能很快取得运行的机会(可能它还在等待其它信号量,而且任务切换还要花一定的时间,因此切换时间具有不确定性),而采用应答机制就确保了ReadPort2(RW_NUMS,Buff,200)一定有机会运行了,该函数给出了接收数据的“地方(接收缓冲区Buff)”,有了地方存放数据,才能收取数据。

任务1为什么要延时1个时钟周期才发送数据呢?这是为了显示两个任务争着发送1024个字节的结果而特别使用的。

因为OSTimeDly(1)的延时是“不可靠”的,也就是说有可能存在着抖动。

这一点在北京航空航天大学出版社出版的《嵌入式实时操作系统µC/OS-II》(第2版)的第2.32小节中详细分析了这一点。

由于ARM的执行速度很快,要使OSTimeDly(1)抖动的可能性较大,就需要把µC/OS-II 每秒发生的中断次数减少,即改变os_cfg.h中宏定义OS_TICKS_PER_SEC的值:#define OS_TICKS_PER_SEC 100
那么怎样才看得出两个任务争着发送数据的结果呢? 这就需要USB主机端程序的配合了。

2. PC机端程序分析
PC机端的编程必须与设备端紧密结合,在本例子中,PC机端程序的流程如图5所示。

图5的虚线表示USB主机(PC机)与PDIUSBD12之间的发送与接收关系,箭头指向接收的一方。

图中,PC机为主机,主动发起了命令,PDIUSBD12设备对命令做出响应。

图5的1和2,PC机使任务1就绪,并且收到了任务1的应答。

图5的3和4,PC机使任务2就绪,并且收到了任务2的应答。

图5的5和6,PC机向设备端发送了两次1024个字节,第1次被任务1接收到,第2
次被任务2接收到。

USB主机PDIUSBD12设备端图5 USB 主机程序与PDIUSBD12设备端程序的关系
图5的7和8,由于任务1收完1024个字节以后,延时了1个时钟周期, 导致任务1或任务2谁先占用发送端点具有不确定性。

这时,我们就可以看出两个任务争用端点的效果了。

为了方便用户使用D12 PACK ,我们提供了PC 机端的PDIUSBD12的驱动程序,驱动程序在“D12驱动”目录下,请根据你的PC 机使用的操作系统选择安装不同的驱动程序。

对于端点1和端点2的读写,我们提供了EasyUSB.dll 动态库,该动态库在 “EasyUSB ”目录下,动态库的使用方法请见“EasyUSB ”目录下的“EasyUSB 动态库使用指南_EasyUSB_.pdf ”文件。

PDIUSBD12设备端驱动程序提供的API 函数与PC 机端EasyUSB.dll 动态库提供的函数的对应关系如表12所示。

表中每一行的两个函数是发送与接收的关系。

表12 设备端与主机端函数对应表
PDIUSBD12设备端驱动程序API 函数 PC 机主机端EasyUSB.dll INT8U ReadPort1(INT32U len,INT8U *recbuff,
INT16U timeout)
int WriteData(int pipenum,unsigned char *sendbuffer,
int len,int waittime=-1);
pipenum 取值为1
INT8U ReadPort2(INT32U len,INT8U *recbuff,
INT16U timeout)
int WriteData(int pipenum,unsigned char *sendbuffer,
int len,int waittime=-1);
pipenum 取值为3
INT8U WritePort1(INT32U len,INT8U *sendbuff,
INT16U timeout)
int ReadData(int pipenum,unsigned char *recbuffer,
int len,int waittime=-1);
pipenum 取值为0
INT8U WritePort2(INT32U len,INT8U *sendbuff,
INT16U timeout)
int ReadData(int pipenum,unsigned char *recbuffer,
int len,int waittime=-1);
pipenum 取值为2
注意:EasyUSB.dll 各函数的参数len 不得超过1024, PDIUSBD12设备端各函数的参数len 无此限制。

3.PDIUSBD12设备端源码分析
下面说明设备端程序编写的详细过程:
第一步包含驱动程序相关头文件及定义应用程序使用的堆栈及事件。

将PDIUSBD12驱动程序软件包加入到我们提供的工程模板中,然后请按照第1.4节(USB驱动程序软件包的使用)修改相关内容。

最后建立main.c文件,main.c文件的开始部分见程序清单2。

程序清单2 包含相关头文件、定义堆栈及事件
"config.h" //包含LPC2200模板的配置头文件
#include
"D12Driver.h" //包含PDIUSBD12驱动程序的头文件
#include
OS_STK TaskStartStk[128]; //操作系统起动任务堆栈
OS_STK TaskRec1Stk[400]; //任务1堆栈(400个字)
OS_STK TaskRec2Stk[400]; //任务2堆栈(400个字)
*TaskRec1_Sem; //任务1就绪信号量
OS_EVENT
*TaskRec2_Sem; //任务2就绪信号量
OS_EVENT
第二步 配置PDIUSBD12的中断优先级和设置PDIUSBD12的中服务程序地址。

如程序清单3所示。

程序清单3 配置PDIUSBD12的中断
void Init_D12Int(void)
{
VICVectCntl1 = (0x20 | 0x0E); //EINT0通道分配到IRQ slot 1
VICVectAddr1 = (INT32U)Usb_Handler; //设置EINT0向量地址
VICIntEnable = 1 << 14; //允许EINT0
}
第三步 建立int main(void)函数。

主函数初始化µC/OS-II操作系统并启动该操作系统。

该函数如程序清单4所示。

程序清单4 int main(void)函数
int main (void)
{
OSInit(); //初始化µC/OS-II
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[127], 3); //创建起动任务OSStart(); //开始多任务
}
第四步 初始化目标板及PDIUSBD12,运行起动任务。

这些工作由起动任务TaskStart()完成。

如程序清单5所示。

程序清单5 起动任务TaskStart()
void TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
Cmd,err;
INT8U
pdata = pdata; /* Prevent compiler warning */
TargetInit(); //LPC2200开发板初始化
err = Init_D12(); //PDIUSBD12初始化
Init_D12Int(); //配置PDIUSBD12的中断
TaskRec1_Sem = OSSemCreate(0); //创建两个信号量,信号量初值为0
TaskRec2_Sem = OSSemCreate(0);
0) //如果初始化PDIUSBD12成功,才创建控制传输处理任务
if
==
(err
OSTaskCreate(TaskSetup,(void *)0, &TaskSetupStk[127], 0);
OSTaskCreate(TaskRec1,(void *)0, &TaskRec1Stk[399], 1); //创建任务1和任务2
OSTaskCreate(TaskRec2,(void *)0, &TaskRec2Stk[399], 2);
for (;;){
ReadPort1(1,&Cmd,200); //等待来自PC机的命令 (1)
err
=
if (err == OS_NO_ERR){ //接收正确
0x01)
==
(Cmd
if
OSSemPost(TaskRec1_Sem); //使任务1就绪
else
OSSemPost(TaskRec2_Sem); //使任务2就绪
}
}
}
请注意程序清单5(1)等待来自PC机的命令的超时等待时间为200个时钟周期,虽然只是接收一个字节,但是,超时时间必须足够长,防止该任务频繁返回超时错误。

导致数据接收错误。

第五步任务接收数据及发送数据。

完成了前面的三步,下面就是用户应用程序的任务了。

这两个任务从PC机接收到1024个字节后,就争着向PC机发送数据。

这两个任务的程序框架在图4已说明得很清楚了。

但请注意任务接收与发送数据时超时时间的定义,超时时间太短容易导致收发失败,超时时间过长又容易导致错误发生后恢复时间过长,请用户根据实际需要来衡量。

程序清单6 任务1以及任务2
#define RW_NUMS 1024 //任务1和任务2收发数据字节数
void TaskRec1(void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr;
#endif
INT8U Buff[RW_NUMS]; //接收及发送缓冲区
INT8U ack = 0x01; //应答主机数值
INT8U err; //函数返回值
pdata = pdata; /* Prevent compiler warning */
for (;;){
OSSemPend(TaskRec1_Sem,0,&err); //等待TaskStart的命令
err = WritePort1(1,&ack,200); //应答USB主机
if (err == USB_NO_ERR){ //应答正确
err = ReadPort2(RW_NUMS,Buff,200); //接收数据
OSTimeDly(1); //延时一个时钟周期
if (err == USB_NO_ERR){ //接收正确
Buff[0] = OSPrioCur; //标识该任务
err = WritePort2(RW_NUMS,Buff,200); //发送数据
}
}
}
}
void TaskRec2(void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr;
#endif
Buff[RW_NUMS]; //接收及发送缓冲区
INT8U
0x02; //应答主机数值
=
ack
INT8U
INT8U err; //函数返回值
pdata = pdata; /* Prevent compiler warning */
for (;;){
OSSemPend(TaskRec2_Sem,0,&err); //等待TaskStart的命令
err = WritePort1(1,&ack,200); //应答USB主机
==
USB_NO_ERR){ //应答正确
if
(err
err = ReadPort2(RW_NUMS,Buff,200); //接收数据
OS_NO_ERR){ //接收正确
==
if
(err
0x02; //标识该任务
Buff[0]
=
err = WritePort2(RW_NUMS,Buff,200); //发送数据
}
}
}
}
第六步 编译程序并运行。

运行程序的方法见本目录下的readme.txt文件。

4.PC机端源码分析
PC机端我们提供了一个用VC++6.0写的一个例子,该例子在“PCTest”目录下。

当然也可以用VB,CB,Dephi等高级语言引用动态库EasyUSB.dll来实现。

下面是在VC++6.0下实现该例子的过程。

第一步:新建一个VC++6.0工程。

第二步:将“EasyUSB”目录下的所有文件拷贝到你的工程目录下。

将EasyUSB.lib和EasyUSB.h加入到你的工程中,在用到EasyUSB.dll函数的文件包含EasyUSB.h头文件。

第三步:使用EasyUSB.dll提供的函数编写程序。

程序清单7是图5中PC机端发送与接收数据的片断,用到的各变量为:
cmd1初值为1,字节型;
cmd2初值为2,字节型;
SendBuff1为发送缓冲区1,字节型,长度1024字节,负责将数据发送给任务1;
SendBuff2为发送缓冲区2,字节型,长度1024字节,负责将数据发送给任务2;
RecBuff1为接收缓冲区1,字节型,长度1024字节,负责接收来自任务1或任务2的数据。

RecBuff2为接收缓冲区2,字节型,长度1024字节,负责接收来自任务1或任务2的数据。

下面一共8句收发数据语句,请对照图5及PCTest.dsw工程中的PCTestDlg.cpp文件进行分析。

其中宏定义RW_NUMS的值为1024。

程序清单7 PC机数据收发
WriteData(1,&cmd1,1,1000); //发送命令字1,使设备端任务1就绪
=
ret
if (ret != 1){
MessageBox("发送命令字1失败"); return;
}
ReadData(0,&cmd1,1,1000);
//接收设备端任务1的响应
=
ret
if ((ret != 1)||(cmd1 != 1)){
MessageBox("任务1无响应或响应错误"); return;
}
WriteData(1,&cmd2,1,1000); //发送命令字2,使设备端任务2就绪
ret
=
if (ret != 1){
MessageBox("发送命令字1失败"); return;
}
ReadData(0,&cmd2,1,1000);
//接收设备端任务2的响应
=
ret
if ((ret != 1)||(cmd2 != 2)){
MessageBox("任务2无响应或响应错误"); return;
}
ret = WriteData(3,SendBuff1,RW_NUMS,1000); //发送1024个字节给任务1
if (ret != RW_NUMS){
MessageBox("发送数据给任务1错误"); return;
}
ret = WriteData(3,SendBuff2,RW_NUMS,1000); //发送1024个字节给任务2
if (ret != RW_NUMS){
MessageBox("发送数据给任务2错误"); return;
}
ret = ReadData(2,RecBuff1,RW_NUMS,1000); //接收任务1或任务2的1024个字节
if (ret != RW_NUMS){
MessageBox("接收任务1或2的数据错误"); return;
}
ret = ReadData(2,RecBuff2,RW_NUMS,1000); //接收任务1或任务2的1024个字节
if (ret != RW_NUMS){
MessageBox("接收任务1或2的数据错误"); return;
}
请注意EasyUSB.dll中ReadData()与WriteData()函数的len参数的最小取值为1(不能为0),最大取值为1024。

还有一点必须提醒:由于PC机有时候比较繁忙,可能导致EasyUSB.dll中的读写函数没有机会执行(写入或读出数据)而出现超时错误,这时可以再试读或试写一两次。

5.例子运行结果
运行例子的步骤为:
(1)将D12 PACK安装到EasyARM2200开发板的PACK接口,并用USB线将PC机机与D12 PACK连接起来。

(2)用ADS1.2运行“D12Driver”目录中的USBDriver.mcp工程,有关怎样运行例子程序的说明请见本目录的readme.txt文件。

(3)重新编译USBDriver.mcp工程并将程序下载到EasyARM2200开发板,运行该程序。

(4)运行程序后,PC机提示发现USB新硬件,提示安装新硬件的USB驱动程序,请根据你的操作系统安装相应的USB驱动程序,驱动程序在“D12驱动”目录下。

安装D12驱动程序成功后,D12 PACK上的绿灯应该点亮,即枚举成功。

(5)运行“PCTest”目录下的PCTest.exe文件。

在“发送数据”框填写要发送的两个字节(每次发送1024个相同的字节,共发两次),然后按“发送”按键。

这时,从PDIUSBD12设备端接收到2048个字节,如图6所示。

“接收数据”框中的第一个框为首先接收到的1024个字节,第二个框为随后接收到的1024个字节。

接收到的1024个字节的第1个字节说明发送这批数据(1024个字节)的任务优先级。

(6)继续按“发送”按键多几次,你会发现收到同一批数据的顺序不同了。

如图7所示。

但是,可以看到,尽管收到同一批数据的顺序不同,但数据内容并不发生变化。

这就可以说明:任务1与任务2争着往端点2发送数据,但它们都保持着数据的完整性。

这样就实现了我们驱动程序的设计目标。

图6 从PDIUSBD12接收到1024个字节
图7 按多次按发送按键的结果
运行于µC/OS-II上的PDIUSBD12驱动程序软件包的说明就介绍到此,用户可以在此驱动程序的基础上实现其它功能:
(1)在本驱动的协议层增加其它请求如厂商请求、大容量类请求。

这样,USB驱动程序的功能就更加强大了;
(2)通过该USB驱动程序来控制EasyARM2200开发板上的一些外设,实现在PC机对设备的控制,如控制数码管的显示等等。

总之,在本驱动程序的基础上,请用户发挥自己的想像力,动手编写程序实现更多的实用功能。

相关文档
最新文档