目录
1. USB的寄存器有哪些?大概的结构是怎么样的?
USB内部有8051控制器这个是肯定的,那么USB内部还有哪些存储区间呢?
主要有3个区域,内部数据存储器、外部数据存储器以及外部可编程的存储器。这里的外部并不是指IC的外部。
1)内部存储器
在内部的存储器中又有0X0000-0X3FFF的主RAM,和0XE000-0XE1FF的扩展RAM。片上RAM作为主RAM,可以作为程序和数据被访问,而扩展RAM只能作为数据访问。而有些系统是没有外部RAM的。例如56PIN的EZ-USB是没有片外数据和程序存储器,那么主RAM就需要既要存数据又要存程序,这时需要通过PSEN#和RD#去选通主RAM。对应到下图就是56PIN的USB没有右边的两列,只有左边的一列。左边地址分为0x0000-0x3FFF (main RAM) 、 0xE000-0xE1FF (Scratch RAM)、 0xE200-0xFFFF (Registers/Buffers)。EA PIN是外部访问PIN,=0表示主RAM可以数据和程序访问,仅在56或者100PIN的EZ-USB中,=1表示所有的程序在片外,而片上RAM包含主RAM只作为数据访问。
2)外部存储器
外部存储器:标准的8051是采用的哈佛结构,数据和程序是分开的,但是EZ-USB采用的是片外程序和数据是分开的,但是片上程序和数据统一的,采用冯诺依曼结构,这使得EZ-USB的片上RAM可以作为程序存储器加载外部资源。
如下是内部数据RAM,主要有upper 128, lower 128, and SFR 区域组成,SFR包含了EZ-USB的控制和状态寄存器。
下图是内部RAM地址在0XE000-0XFFFF的存储区间的寄存器内容。
2. 通过KEIL软件去配置的信息有哪些?
通过软件配置寄存器可以实现:操作模式的选择、终端设置,FIFO设置,备用配置,自动指针配置等,一般的寄存器采用默认值,具体设计时需要配置的PIN参考下面的部分;
1) CPU时钟
例如,将时钟配置为48M,将【b4,b3】设置为10。
2)模式选择寄存器
IFCONFIG: 配置FX1/FX2的工作模式是I/O端口、GPIF还是slave FIFO。
b7:设置GPIF和slave FIFO的时钟源,=0表示选择IFCLK的外部时钟,=1表示使用内部的30M或者48M时钟;
b6:=0表示使用30M,=1表示使用48M;
b5:IFCLK的输出使能,=0表示三态(关闭),=1表示Driver(打开);
b4:IFCLK时钟反转;
b3:slave FIFO异步模式,=0表示同步,=1表示异步;
b[1:0]:接口模式选择
00:PORT 模式
01:GPIF模式
11:SLAVE FIFO模式
以上三种模式的基本使用,以56PIN的CY7C6801A为例,如下图所示,在I/O模式下,使用的端口为左边的这些。这种模式下一般没有其他的外设,GPIF和slave FIFO机制也存在,只是无效。而GPIF模式是内部作为master,去控制外部设备时使用。slave FIFO时外部设备作为master,内部FIFO作为从设备时使用。在不同模式下使用的接口参考下图。
从下图可以看出FX里面包含3部分,USB的SIE引擎,8051内核和与FIFO交互的外设接口。USB域主要是SIE工作,SIE发送和接收FIFO数据包,将令牌包解码及编码。一般主机通过USB与外设进行数据通信,数据被外设的逻辑进行处理,此时,数据流通过FIFO作为不同时钟域的异步处理站,数据流一般不通过内核处理。而使用FIFO 就设计到时内部作为master或者是外部作为master。8051主要工作在手动模式下,作为外设与USB的桥梁。在8051的控制下,接收来自装满USB数据的buffer的中断,并连接buffer到终端。当buffer中的bytes数满足需要的水平时,8051控制发送数据。每一个域都有各自的参考时钟。USB域的参考时钟的IC外部的晶振产生的24M时钟。INTERFACE域上传和下载终端FIFO的数据,外部的设备可以使用自己的时钟与FIFO接口通讯,也可以使用内部接口时钟IFCLK
例如:IFCONFIG=0X4B,表示选择48MIFCLK时钟,slave FIFO的异步模式。IFCLK不使用。
以slave FIFO为例说明,下图是 slave FIFO模式下的FX结构体图:
需要配置的寄存器如下:
3)终端FIFO设置寄存器
EPxCFG:配置EPX是否使用、方向、深度以及buffer数量
bit7: VALID =1, activate an endpoint; =0, to deactivate it;
bit6: DIR =1, IN; =0,OUT;
bit[5:4]: YPRE =00, invalid; =01 isochronous;=10 BULK;=11 interrupt;
bit3: SIZE =1, 1024bytes; =0,512bytes;
bit2: reserved;
bit[1:0]: BUF =00,quad buffer; =01, invalid; =10 double buffer; =11, triple buffer;
buffer的概念:在FX中buffer只是FIFO作为数据缓冲时使用,通过上面的配置可以设定EPX使用2,3或者4个buffer,每个buffer一般为512 bytes,当外部主机往USB中传送数据时,如果一个FIFO满了,还需要继续传输数据的话,内部逻辑会自动控制切换使下一个buffer有效,主机将往有效的buffer中继续写入数据。这将使得在传入到上一个Buffer的数据被USB或者外部的设备使用时还能有空间去保存主机传过来的数据,而不至于数据丢失。而在此操作的同时,USB和外部设备时不知道哪个buffer有效的,他们只是把终端作为一个512bytes的存储设备FIFO在用,至于主控、USB、外部设备访问的终端的哪个buffer是由上面说的内部逻辑机制运行的。
在FX中有一个full-speed模式,在此模式下,如果一个终端被配置成bulk,不管实际的物理终端Buffer是否是512 bytes,8051内核和外部master只能将这个终端buffer看成64 bytes深度。因为在全速模式下,终端FIFO是quantum FIFO,数据包被分配成一个quantum FIFO的大小(可以使用EPXCFG的SIZE bit去设置每一个quantum FIFO的物理大小)。8051应将其FIFO访问限制为最大深度,该深度由wMaxPacketSize和FIFO大小(由大小位设置)中的较小者定义。对于bulk 端点,为64 bytes。基于此,在设置时,最好将FIFO 的buffer大小设置为512而不是1024bytes,更能减少未使用字节的浪费。
另外,在 ISOCHRONOUS full-speed模式下,可以使用wMaxPacketSize设置buffer深度为512或者1024 bytes。
上面提到的quantum FIFO,指的是在FX1结构下,数据包会立即在USB和FIFO之间传输(瞬间完成传输),而不需要内核的干预,内核只是起到配置接口的作用。每次传入到 FX1's FIFO的大小的USB-size的数据包,而不是一次一个byte。因此,当empty flag从1跳到0变成非空时,FIFO的数据量时USB包的大小,而不是增加了一个Bytes。通过这种方式,做到了高速传输。
FX1对于FIFO填满USB数据后如何处理EPX FIFO,有两种方式。这取决于端点FIFO的模式设置,由EPx-FIFOCFG寄存器(x=2,4,6,8)的位3和4设置,包括自动模式和手动模式。根据不同的模式,决定8051内核是否参与处理。在手动模式下,8051具有对端点FIFO缓冲区中数据的初始访问权。通过启动(ariming)端点,OUT endpoint buffer的控制权转移到USB域。当一个OUT端点处于待命(arming)状态时,它处于USB域的控制之下。USB内核收到主机发送的数据,并发送ACK。一旦内核发送了ACK,8051就可以访问FIFO缓冲区,并可以根据需要修改数据。然后8051可以将数据移动到接口域,也可以忽略它。一旦数据包被提交到接口域,8051就失去了对FIFO缓冲器的访问,接口域可以开始访问FIFO的数据。而在接口域中,FIFO可以被内部的master控制也可以被外部的master控制。(arming endpoint 意思是将数据包的控制权转移到USB域,8051也可能通过往bytes count register EPXBXH写bytes 数来启动终端,其放在数据包中发送给主机。IN 终端只能被8051启动。)
外部master可以提供时钟或读写使能信号进行同步操作,也可以通过选通信号进行异步操作。在这种slave FIFO模式下,外部设备可以通过各种控制信号访问FIFO。另一种就是被内部GPIF控制,直接连接FIFO并为外部逻辑接口生成用户编程控制信号。此外,通过在其RDY引脚上采样外部信号,可以使GPIF等待外部事件触发。GPIF的运行速度比FIFO数据速率快得多,为定时信号提供了良好的可编程分辨率。它可以通过内部FX1时钟或外部提供的时钟进行计时。
不同的域访问FIFO时会有不同的寄存器去指示FIFO的运行状态。EPXFIFOFLAGS寄存器指示的是外设控制下的FIFO状态。EP2468STAT指示的是8051控制下的FIFO状态。
4)AUTOPTRSETUP
在RAM中的终端数据,EPX,可以给CPU使用。在某些情况下,固件可以更快地访问EPx的数据,就像它在FIFO寄存器中一样。EZ-USB提供两个特殊的数据指针,称为“自动指针”,在每次字节传输后会自动递增。通过使用自动指针,固件可以作为FIFO去访问片内或片外数据存储器的连续块。
APTRxINC=0,固定地址指针,=1时,每次读写XAUTODAT将地址指针将自动增加。
APTREN=1,使能自动指针。
要使用自动指针读取或写入连续内存块(例如,端点缓冲区),请使用块的起始地址加载到自动指针的地址寄存器,然后重复读取或写入自动指针指向的数据寄存器。
5)EPxFIFOCFG
6)PKTEND
在看数据手册时,发现有一个引脚,在手册上是有的,但是实际并没有使用。PKTEND这个PIN。这里也简单介绍一下。
什么时候使用这个PIN呢?
外部master通过这个PIN将IN 包发送给USB,并不管这个包的长度大小,通常在master想要去发送一个短包时使用。例如当包的尺寸比寄存器EPxAUTOINLENH:L定义的都小时。假设EP4AUTOINLENH:L设置为512字节的默认值。如果AUTOIN=1,则外部主机可以连续将数据流传输到FIFO 4,并且(数据路径中没有任何瓶颈)只要FIFO充满512字节,EZ-USB就会自动将数据包提交到USB。如果主机想要发送不是512倍数的长度的数据流时,最后一个数据包不会自动提交到USB,因为它小于512字节。如果要提交最后一个数据包,主机可以通过以下两种方式:1. 可以用虚拟数据填充数据包,使其长度达到512字节;2. 可以将短数据包写入FIFO,然后激活 PKTEND引脚(默认是低电平)。通过FIFOPINPOLAR寄存器可以改变此引脚的电平。
使用时需注意:
1)在一个数据包无效时不可使用此PIN,即使需要发送一个0长度的数据包。判断一个数据包是否有效通过‘FULL’ FLAG。
2)在同步模式下,对于SLWR,PKTEND输入没有特定的时序要求。PKTEND可以随时输入。在异步模式下,SLWR和PKTEND不可同时输入。PKTEND信号必须在SLWR信号输入一段时间后才能输入,这段时间至少为最小脉冲宽度。在这两种模式中,在PKTEND引脚输入期间,FIFOADR[1:0]应保持不变。
3. KEIL如何去配置?
1) 所需工具及软件
keil --- 用于对固件库编程,生成USB工作的寄存器配置文件,IIC文件;(生成的.hex文件是烧录到8051内部RAM的,而.iic文件是烧录到外部的EEPROM的)
CYconsole的EZ-USB的上位机软件,用于将KEIL生成的IIC文件烧录到IC的外部EEPROM中,并连接主机和USB,进行数据的发送和读取;
带有CY7C68013A IC开发板;
USB数据线。
KEIL项目工程截图:
初次打开EZ-USB提供的工程会出现一些设置BUG,可以参考下面博客:
2)上电初始化需要配置内容
对寄存器CPUCS, IFCONFIG, PINFLAGSxx, PORTxCFG, EPxCFG, FIFOINPOLAR, EPxFIFOCFG,EPxBC, AUTOPTRSETUP等进行初始话配置;
以上主要包含CPU的时钟、USB的工作模式、终端的模式(选择哪个终端、方向、FIFO大小、buffer等)、FIFO接口有效电平设置。
同时,在程序中需要加入----同步延时:在两次往0xE600-0xE6FF地址的寄存器和下表的寄存器中写,或者一次往下表的寄存器中写与从0xE600-0xE6FF地址读之间需要进行延时。因为往CPU和FIFO相关的寄存器中读写数据,两个的参考时钟是不一样的。在KEIL程序中用MOVX指令去做延时。
void TD_Init(void) // Called once at startup
{
// set the CPU clock to 48MHz
CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ; //CPU时钟
// Set Slave FIFO mode
IFCONFIG |= 0x4B;
// Registers which require a synchronization delay, see section 15.14
// FIFORESET FIFOPINPOLAR
// INPKTEND OUTPKTEND
// EPxBCH:L REVCTL
// GPIFTCB3 GPIFTCB2
// GPIFTCB1 GPIFTCB0
// EPxFIFOPFH:L EPxAUTOINLENH:L
// EPxFIFOCFG EPxGPIFFLGSEL
// PINFLAGSxx EPxFIFOIRQ
// EPxFIFOIE GPIFIRQ
// GPIFIE GPIFADRH:L
// UDMACRCH:L EPxGPIFTRIG
// GPIFTRIG
// Note: The pre-REVE EPxGPIFTCH/L register are affected, as well...
// ...these have been replaced by GPIFTC[B3:B0] registers
// default: all endpoints have their VALID bit set
// default: TYPE1 = 1 and TYPE0 = 0 --> BULK
// default: EP2 and EP4 DIR bits are 0 (OUT direction)
// default: EP6 and EP8 DIR bits are 1 (IN direction)
// default: EP2, EP4, EP6, and EP8 are double buffered
SYNCDELAY; //同步延时
// PINFLAGSxx
PINFLAGSAB = 0xC8; // FLAGB - fixed EP2FF FLAGA - EP2EF
SYNCDELAY;
PINFLAGSCD = 0xDE; // FLAGD - unvalid FLAGC - fixed EP6FF
SYNCDELAY;
//PORTxCFG
PORTACFG = 0x40; // func. of PA7 pin is SLCS#
SYNCDELAY;
//FIFOINPOLAR
FIFOPINPOLAR = 0x00; // all signals active low, slave FIFO interface pins polarity
SYNCDELAY;
// we are just using the default values, yes this is not necessary...
//EPxCFG
EP1OUTCFG = 0xA0;
EP1INCFG = 0xA0;
SYNCDELAY; // see TRM section 15.14
EP2CFG = 0xA2; //EP2 OUT, 512BYTE BULK, DUBBLE BUFFER
SYNCDELAY;
EP4CFG = 0xA0; //EP4 OUT, BULK
SYNCDELAY;
EP6CFG = 0xE2; //EP6 IN, 512BYTE BULK, DUBBLE BUFFER
SYNCDELAY;
EP8CFG = 0xE0; //EP8 IN, BULK
// handle the case where we were already in AUTO mode...
//EPxFIFOCFG
EP2FIFOCFG = 0x00; // AUTOOUT=0, WORDWIDE=0
EP4FIFOCFG = 0x00; // AUTOOUT=0, WORDWIDE=0
SYNCDELAY;
EP2FIFOCFG = 0x11; // AUTOOUT=1, WORDWIDE=1
EP4FIFOCFG = 0x11; // AUTOOUT=1, WORDWIDE=1
SYNCDELAY;
EP6FIFOCFG = 0x00; // AUTOIN=0, WORDWIDE=0
EP8FIFOCFG = 0x00; // AUTOIN=0, WORDWIDE=0
SYNCDELAY;
EP6FIFOCFG = 0x09; // AUTOIN=1, WORDWIDE=1
EP8FIFOCFG = 0x09; // AUTOIN=1, WORDWIDE=1
SYNCDELAY;
// out endpoints do not come up armed
// since the defaults are double buffered we must write dummy byte counts twice
SYNCDELAY;
//EPxBCL
EP2BCL = 0x80; // arm EP2OUT by writing byte count w/skip.
SYNCDELAY;
EP2BCL = 0x80;
SYNCDELAY;
EP4BCL = 0x80; // arm EP4OUT by writing byte count w/skip.
SYNCDELAY;
EP4BCL = 0x80;
// enable dual autopointer feature
//AUTOPTRSETUP (sfr register)
AUTOPTRSETUP |= 0x01;
}
3) 调度
USB反复调用的程序部分,即功能实现部分代码。例如上篇博客实现的从EP2读取数据存放到EP6,反复执行。
void TD_Poll(void) // Called repeatedly while the device is idle
{
WORD i;
WORD count;
if(!(EP2468STAT & bmEP2EMPTY))
{ // check EP2 EMPTY(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is empty
if(!(EP2468STAT & bmEP6FULL))
{ // check EP6 FULL(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is full
APTR1H = MSB( &EP2FIFOBUF );
APTR1L = LSB( &EP2FIFOBUF );
AUTOPTRH2 = MSB( &EP6FIFOBUF );
AUTOPTRL2 = LSB( &EP6FIFOBUF );
count = (EP2BCH << 8) + EP2BCL;
// loop EP2OUT buffer data to EP6IN
for( i = 0x0000; i < count; i++ )
{
// setup to transfer EP2OUT buffer to EP6IN buffer using AUTOPOINTER(s)
EXTAUTODAT2 = EXTAUTODAT1;
}
EP6BCH = EP2BCH;
SYNCDELAY;
EP6BCL = EP2BCL; // arm EP6IN
SYNCDELAY;
EP2BCL = 0x80; // re(arm) EP2OUT
}
}
if(!(EP2468STAT & bmEP4EMPTY))
{ // check EP4 EMPTY(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is empty
if(!(EP2468STAT & bmEP8FULL))
{ // check EP8 FULL(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is full
APTR1H = MSB( &EP4FIFOBUF );
APTR1L = LSB( &EP4FIFOBUF );
AUTOPTRH2 = MSB( &EP8FIFOBUF );
AUTOPTRL2 = LSB( &EP8FIFOBUF );
count = (EP4BCH << 8) + EP4BCL;
// loop EP4OUT buffer data to EP8IN
for( i = 0x0000; i < count; i++ )
{
// setup to transfer EP4OUT buffer to EP8IN buffer using AUTOPOINTER(s)
EXTAUTODAT2 = EXTAUTODAT1;
}
EP8BCH = EP4BCH;
SYNCDELAY;
EP8BCL = EP4BCL; // arm EP8IN
SYNCDELAY;
EP4BCL = 0x80; // re(arm) EP4OUT
}
}
}
4) fw.c调用的代码
fw.c文件是USB固件根本,USB的协议部分都在这里。
5)其余文件
dscr.51:USB描述符文件
fx2.h:预定义,宏及函数声明
fx2regs.h:68013的寄存器地址定义。
syncdly.h:同步延时。在其他文件里经常调用的一个函数SYNCDELAY就是这里定义的。
intrins.h:C51一些数据类型及函数定义。
代码的注释可以参考下面的博文:
疑问:
1)EZ-USB中的quantum FIFO功能和普通功能怎么区分?什么时候使用哪个?对于FIFO什么时候是字节写入?什么时候是以USB包写入?
2)什么时候使用自动模式,什么时候使用手动模式?
在auto模式下,数据流实在USB和FIFO之间连续传输的,不需要CPU固件封装数据包。这时可以达到最大的传输带宽。
功能实现:
1. 上篇博文实现的异步读EP2的数据然后写到EP6功能是否可以提高速率?
2. 除了上述的功能之外,是否可以实现同步的读写?
3. 是否还有其他的功能?