|

楼主 |
发表于 2005-10-28 04:55:17
|
显示全部楼层
图1是PDIUSBD12的电路连接图。 < >USB固件程序程序由三部分组成:①初始化单片机和所有的外围电路(包括PDIUSBD12);②主循环部分,其任务是可以中断的;③中断服务程序,其任务是对时间敏感的,必须马上执行。根据USB协议,任何传输都是由主机(Host)开始的。单片机作它的前台工作,等待中断。主机首先要发令牌包给USB设备(这里是PDIUSBD12),PDIUSBD12接收到令牌包后就给单片机发中断。单片机进入中断服务程序,首先读PDIUSBD12的中断寄存器,判断USB令牌包的类型,然后执行相应的操作。在USB单片机程序中,要完成对各种令牌包的响应,其中比较难处理的是SETUP包,主要是端口0的编程。 </P>< >单片机与PDIUSBD12的通信主要是靠单片机给PDIUSBD12发命令和数据来实现的。PDIUSBD12的命令字分为三种:初始化命令字、数据流命令字和通用命令字。PDIUSBD12数据手册给出了各种命令的代码和地址。单片机先给PDIUSBD12的命令地址发命令,根据不同命令的要求再发送或读出不同的数据。因此,可以将每种命令做成函数,用函数实现各个命令,以后直接调用函数即可。 </P>< >下面的程序是处理主机的标准控制请求的一个模板: </P><P>unsigned char ENDPOINT_A0_FIFO[8]; </P><P>//判断输入的是SETUP请求,并将其读入缓冲区ENDPOINT_A0_FIFO </P><P>… </P><P>if((ENDPOINT_A0_FIFO[0] & 0b011000000)==0x00) </P><P>{ </P><P>if(ENDPOINT_A0_FIFO[1]<=0C) </P><P>{ </P><P>(*StandardFunctionTable[ENDPOINT_A0_FIFO[1]])(); </P><P>return; </P><P>} </P><P>} </P><P>… </P><P>const void (*StandardFunctionTable[])(void)= </P><P>{ </P><P>GetSatus,ClearFeature,USB_Reserved,SetFeature, </P><P>USB_Reserved,SetAddress,GetDescriptor,SetDescriptor, </P><P>GetConfiguration,SetConfiguration,GetInterface, </P><P>SetInterface,SynchFrame </P><P>}; </P><P>USB设备在正常使用以前,必须由主机配置设备。主机一般会从USB设备获取配置信息后再确定此设备有哪些功能。 </P><P>作为配置操作的一部分,主机会设备设备的配置值,如果必要的话会选择合适的接口备选设备。其初始化函数为: </P><P>void D12_int() </P><P>{ XmtBuff.pNum=16; </P><P>D12_COMMAND=0xf4;//读中断寄存器 </P><P>ist=D12_DATA; </P><P>ist=D12_DATA; </P><P>if(ist & 0x01) //ENDP0_OUT </P><P>{XmtBuff.out=0; </P><P>XmtBuff.in=1; </P><P>D12_COMMAND=0x40; //读OUT最后状态 </P><P>ist=D12_DATA; </P><P>if(ist & 0x20)//收到SETUP包 </P><P>{ Setup_read(); </P><P>Setup_control(); </P><P>} </P><P>else </P><P>{ Setup_read(); </P><P>} </P><P>} </P><P>else if(ist & 0x02)//ENDP0_IN </P><P>{ XmtBuff.in=1; </P><P>D12_COMMAND=0x41;//读in最后状态 </P><P>ist=D12_DATA; </P><P>USB_submit(); </P><P>} </P><P>else if(ist & 0x04)//ENDP1_OUT </P><P>{ XmtBuff.out=2; </P><P>XmtBuff.in=3; </P><P>D12_COMMAND=0x42;//读out最后状态 </P><P>ist=D12_DATA; </P><P>read_out(); </P><P>} </P><P>else if(ist & 0x08)//ENDP1_IN </P><P>{ XmtBuff.in=3; </P><P>D12_COMMAND=0x43;//读in最后状态 </P><P>ist=D12_DATA; </P><P>XmtBuff.b[0]=5; </P><P>XmtBuff.wrLength=1; </P><P>XmtBuff.p=XmtBuff.b; </P><P>USB_submit(); </P><P>} </P><P>…… </P><P>在发出连接USB命令后,主机先读取设备描述符,然后发出设置USB地址SETUP包,设置USB地址后,进行主机客户驱动与设备初始化。其余端点(ENDPOINT)依此类推。 </P><P>在其头文件里需定义USB规范中的各种描述符格式,包括设备描述表、配置描述表、接口描述表、端点描述表、字符串描述表以及描述表类型。这样,在发送配置[,接口(1),端点(1),接口(2),端点(2),…,类,厂商等]联合描述表时,主机USBD可以根据描述类型标识区分各种分描述表。 </P><P>下面是固件程序的主循环部分: </P><P>#include<reg51.h> </P><P>//指向外部D12访问地址 </P><P>#define D12_COMMAND(*(unsigned char xdata *)0xff01) </P><P>#define D12_DATA (*(unsigned char xdata *)0x7f02) </P><P>extern void D12_int(); </P><P>sbit D12_suspend=P1^0; </P><P>sbit D12_int_n=P1^1; </P><P>sbit D12_eot_n=P1^2; </P><P>sbit D12_DMAck_n=P1^3; </P><P>sbit D12_DMAreq=P1^4; </P><P>void main(void) </P><P>{ </P><P>unsigned char ist; </P><P>P1=0xff; </P><P>D12_COMMAND=0xf3; </P><P>D12_DATA=0x06;//设置模式0 </P><P>D12_DATA=0x03;//初始化频率12MHz </P><P>D12_COMMAND=0xd0; </P><P>D12_DATA=0x80;//设置地址0使能 </P><P>D12_COMMAND=0xf3;//连接主机 </P><P>D12_DATA=0x16; </P><P>while(1) </P><P>{ if(!D12_int_n) </P><P>{ </P><P>D12_int(); </P><P>} </P><P>} </P><P>} </P><P>在编写USB的固件程序时,需要注意: </P><P>①单片机的中断应设置为电平触发;中断后一定要读上次传输状态寄存器(命令40~45H),以清除中断寄存器中的中断标志。这样,PDIUSBD12的中断输出才能变回高电平,这一点非常重要。 </P><P>②在接收到Setup包后,一定要调用D8命令重新使能端口0。 </P><P>③在向IN端写完数据后,一定调用命令FAH,指明缓冲区中的数据有效,可以发送到主机。 </P><P>④读写数据后,一定调用命令F2H,以保证可以接收新的包。 </P><P>⑤可以通过调用命令FDH,检查PDIUSBD12是否工作。该命令要读两个字节数据。 </P><P>固件程序的编程是整个USB外设开发中非常重要的一环,它直接影响到设计开发的产品的数据传输速度。例如,采用不同的传输类型、设置不同的分组大小、是否采用DMA方式、传输缓冲区的大小等都会使得传输速率发生很大的变化。还有在高速情况下的超时处理等,也包含了很多的内容。 </P><P>总之,在USB技术应用越来越广泛的今天,只有掌握了固件程序的编写,才可能开发出一个好的USB产品。 </P> |
|