EZ_USB salveFIFO
本文实现一种FPGA与PC通信的方法,采用USB2.0,即Cypress公司的EZ_USB FX2LP系列的CY7C68013A芯片。下面先介绍一下这款芯片。
CY7C68013A是一款含高速USB2.0协议及兼容的8051控制的USB2.0芯片。芯片内部含有16KB的RAM,不需要外加存储器就可以运行固件程序。内部的8051处理器的工作频率可以采用48MHz、24MHz、12MHz。芯片内部集成了4个FIFO,共4K字节。用户可以通过设置使其工作在Master或者Slave模式下。CY7C68013A内部有一个I2C接口用于连接外部的EEPROM,用户可以下载固件程序到EEPROM中,CY7C68013A上电会自动读取EEPROM中的程序到RAM中运行。芯片内部结构如图:
要从FPGA发送数据到PC上,一种简单的方法是配置成Slave mode,相当于直接向FIFO中写数据,在固件中配置数据自动转发到PC。下面先大概说一下固件程序中的配置:
void TD_Init(void) { CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1);//设置CPU时钟频率为48M //USBCS = Ox80; //set high speed mode 添加此语句时工程编译不通过 IFCONFIG = 0XCB; //选择内部时钟频率为48M,FIFO/GPIF采用异步操作模式,SLAVE FIFO接口(外部主控制器) SYNCDELAY; //同步延时 REVCTL = 0X03; //0x01 //版本号 SYNCDELAY; FIFOPINPOLAR = 0X00; //所有引脚为低电平有效 SYNCDELAY; FIFORESET = 0X80; //对从主机发送的数据进行握手 SYNCDELAY; FIFORESET = 0X02; //复位FIFO SYNCDELAY; FIFORESET = 0X04; //复位FIFO SYNCDELAY; FIFORESET = 0X06; //复位FIFO SYNCDELAY; FIFORESET = 0X08; //复位FIFO SYNCDELAY; FIFORESET = 0X00; //保存操作 SYNCDELAY; EP6CFG =0xE0; //使能端点6,端点方向为输入,块传输方式,端点大小512字节,端点为4重缓冲 SYNCDELAY; EP6FIFOCFG = 0X0C; // AUTOIN=1 ZEROLEN=1 WORDIDE=0 设置端点6为自动输入,选择总线数据宽度为8位 SYNCDELAY; PINFLAGSAB = 0X00; //确定引脚FLAGA为EPX的PF标志,FLAGB为EPX的FF标志,FLAGC为EPX的EF标志/// SYNCDELAY; PINFLAGSCD = 0X00; //确定引脚FLAGA为EPX的PF标志,FLAGB为EPX的FF标志,FLAGC为EPX的EF标志/// SYNCDELAY; PORTACFG = 0X40; //PORTACFG.6=1 设置IOPA7为CS引脚 SYNCDELAY; EP6AUTOINLENH = 0X02; //端点6 AUTOIN包长度高字节 SYNCDELAY; EP6AUTOINLENL = 0X00; //端点6 AUTOIN包长度高字节 SYNCDELAY; EP6BCH = 0x02; //512byte///* SYNCDELAY; EP6BCL = 0x00; //512byte///* SYNCDELAY; EP6FIFOPFH = 0X80; //端点6/SLAVE FIFO 可编程成级标志的高位(高速方式和全速方式) SYNCDELAY; EP6FIFOPFL = 0X00; //端点6/SLAVE FIFO 可编程成级标志的高位(高速方式和全速方式) SYNCDELAY; /* OUTPKTEND =0X82; //启动OUT缓冲区 SYNCDELAY; OUTPKTEND =0X82; //启动OUT缓冲区 SYNCDELAY; */ //AUTOPTRSETUP |= 0x01; //使能自动指针 Rwuen = TRUE; }
void TD_Poll(void) // Called repeatedly while the device is idle
{
}
TD_Init()这个函数是初始化函数,上电执行一次。函数内部就是对寄存器的配置。在这里我们使用EP6作为输入端,FPGA将数据写入EP6,EP6自动转发数据到PC。配置FLAGB为满标志,这里作为EP6的满标志。
TD_Poll()这个函数是循环执行的函数,在里面可以进行一些操作,如进行端点数据的交换等。这里我们不用8051CPU处理数据,故可以为空。
配置好之后就可以去FPGA中向EP6(FIFO)写入数据。采用异步写入的方式,即使用slwr信号作为写入信号。下图为异步写的时序图:
可以看得出来这个时序很简单了,只要保证fd数据总线在slwr的上升沿保持稳定即可。
/* Generate USB read/write access request*/ always @(negedge Clk_5M or negedge reset_n) begin if (~reset_n ) begin access_req<=1'b0; end else begin if (usb_flagb & (bus_busy==1'b0)) //EP6的FIFO不满,而且状态为idle access_req<=1'b1; //USB写请求 else access_req<=1'b0; end end /* Generate USB write command*/ always @(posedge Clk_5M or negedge reset_n) begin if (~reset_n) begin usb_fifoaddr<=2'b10; usb_slcs<=1'b0; usb_sloe<=1'b1; usb_slrd<=1'b1; usb_slwr<=1'b1; usb_fd_en<=1'b0; usb_state<=IDLE; end else begin case(usb_state) IDLE:begin usb_fifoaddr<=2'b10; i<=0; usb_fd_en<=1'b0; if (access_req==1'b1) begin usb_state<=EP6_GET_DATA; //开始GET数据 bus_busy<=1'b1; //状态变忙 end else begin bus_busy<=1'b0; usb_state<=IDLE; end end EP6_GET_DATA:begin if(i==8) begin i<=0; usb_state<=EP6_GET_OVER; // data_reg<=shift_reg; //读取数据 data_reg<=oe_ranNum; end else begin i<=i+1'b1; end end EP6_GET_OVER:begin if(i==4) begin i<=0; usb_fifoaddr<=2'b10; usb_state<=EP6_WR_CMD; end else begin i<=i+1'b1; end end EP6_WR_CMD:begin if(i==8) begin usb_slwr<=1'b1; i<=0; usb_state<=EP6_WR_OVER; end else begin usb_slwr<=1'b0; usb_fd_en<=1'b1; //数据输出 i<=i+1'b1; end end EP6_WR_OVER:begin if(i==4) begin usb_fd_en<=1'b0; bus_busy<=1'b0; i<=0; usb_state<=IDLE; end else begin i<=i+1'b1; end end default:usb_state<=IDLE; endcase end end assign usb_fd = usb_fd_en?data_reg:8'bz;
完成之后将.bit烧入FPGA中,打开Cyconsole,Options -->EZ-USB Interface,将Bulk Trans这一行的Pipe切换为Endpoint 6 IN,再单击Bulk Trans就可以看到FPGA的数据已经发送到PC啦!