基于CY7C68013A usb转mdio win10 64bit
1、芯片简介:
目前市场上主流的实现USB通信的方案主要是基于stm32(基于目前比较流行的DAPLink方案)/ft232/ch341等,CY7C68013A芯片历史较久,价格也相对偏高但USB通信设计的方法应该都是一致的。
手上正好有一块下图的开发板:说明:图中蓝色的双刀按钮存在明显接触不良的现象,建议直接将其短路

说明:目前使用win10 64bit的机器开发上述开发板并未发现有任何驱动问题
CY7C68013A:可以简单理解为带有USB接口8051芯片


开发环境配置:
使用的还是2008年的版本
也有更新一点的版本:
需要去官网查找,感觉用法应该差不多
建议默认安装在c盘,这样自带的示例可以直接在keil2中编译,不需要需改工程路径

2、USB 4种通信方式及端点配置:

USB设备驱动向USB控制器驱动请求的每次传输被称为一个事务(Transaction),事务有四种类型,分别是Bulk Transaction、Control Transaction、Interrupt Transaction和Isochronous Transaction。

可以看到内部空间划分如下:

端点配置:
全速只用64字节,高速用512,一共支持12中配置模式如下:一般端点0用于控制,EP1用于中断,EP2468用于bulk,iso.

12种配置方式:


3、实现方式:
bulkloop入门
使用的参考例程基于bulkloop修改而来:
但没有使用bulk的通信模式,而是利用的control通信模式
打开bulkloop的keil工程,这样的模板里我们只需要关注两个函数:
TD_Init()函数,只执行一次,在末尾增加我们需要的初始化代码(比如Port的设置):
void TD_Init(void) // Called once at startup
{// set the CPU clock to 48MHzCPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ;// set the slave FIFO interface to 48MHzIFCONFIG |= 0x40;// 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// we are just using the default values, yes this is not necessary...EP1OUTCFG = 0xA0;EP1INCFG = 0xA0;SYNCDELAY; EP2CFG = 0xA2;SYNCDELAY; EP4CFG = 0xA0;SYNCDELAY; EP6CFG = 0xE2;SYNCDELAY; EP8CFG = 0xE0;// out endpoints do not come up armed// since the defaults are double buffered we must write dummy byte counts twiceSYNCDELAY; 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 featureAUTOPTRSETUP |= 0x01;
//OEA=0x7F;IOA=0xFc;}
TD_Poll()函数,会被一直循环,可以不断查询状态寄存器,根据其结果执行需要的功能,bulkloop便是基于此方式实现
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 emptyif(!(EP2468STAT & bmEP6FULL)){ // check EP6 FULL(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is fullAPTR1H = MSB( &EP2FIFOBUF );APTR1L = LSB( &EP2FIFOBUF );AUTOPTRH2 = MSB( &EP6FIFOBUF );AUTOPTRL2 = LSB( &EP6FIFOBUF );count = (EP2BCH << 8) + EP2BCL;// loop EP2OUT buffer data to EP6INfor( i = 0x0000; i < count; i++ ){// setup to transfer EP2OUT buffer to EP6IN buffer using AUTOPOINTER(s)EXTAUTODAT2 = EXTAUTODAT1;}////EP6BCH = EP2BCH; SYNCDELAY; EP6BCL = EP2BCL; // arm EP6INSYNCDELAY; EP2BCL = 0x80; // re(arm) EP2OUT}}if(!(EP2468STAT & bmEP4EMPTY)){ // check EP4 EMPTY(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is emptyif(!(EP2468STAT & bmEP8FULL)){ // check EP8 FULL(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is fullAPTR1H = MSB( &EP4FIFOBUF );APTR1L = LSB( &EP4FIFOBUF );AUTOPTRH2 = MSB( &EP8FIFOBUF );AUTOPTRL2 = LSB( &EP8FIFOBUF );count = (EP4BCH << 8) + EP4BCL;// loop EP4OUT buffer data to EP8INfor( i = 0x0000; i < count; i++ ){// setup to transfer EP4OUT buffer to EP8IN buffer using AUTOPOINTER(s)EXTAUTODAT2 = EXTAUTODAT1;}EP8BCH = EP4BCH; SYNCDELAY; EP8BCL = EP4BCL; // arm EP8INSYNCDELAY; EP4BCL = 0x80; // re(arm) EP4OUT}}
}
上位机与固件修改:
使用控制节点发送verdor req命令执行:简单测试并不需要编写上位机及驱动直接利用自带的开发工具即可
固件:对DR_VendorCmnd()函数做修改来实现mdio的读写命令,该命令对应的操作按钮为:
![]()

有一些命令似乎芯片默认已经使用,如下表,因此选择了0x20(仅测试返回固定值)开始,写0x21/读0x22

修改如下:
BOOL DR_VendorCmnd(void)
{WORD w0;WORD addr, len, bc;WORD ChipRev;WORD i;switch(SETUPDAT[1]){ //TPM handle new commandscase 0x20:*EP0BUF = 0x15;EP0BCH = 0;EP0BCL = 1; // Arm endpoint with # bytes to transferEP0CS |= bmHSNAK; // Acknowledge handshake phase of device requestbreak;case 0x21://write eth mdio*EP0BUF = SETUPDAT[2];*(EP0BUF+1) = SETUPDAT[3];*(EP0BUF+2) = SETUPDAT[4];*(EP0BUF+3) = SETUPDAT[5];*(EP0BUF+4) = SETUPDAT[6];*(EP0BUF+5) = SETUPDAT[7];*(EP0BUF+6) = g_data0;*(EP0BUF+7) = g_data0>>8;//w0 = 0;w0 = SETUPDAT[5];w0 = w0<<8;w0 = w0 + SETUPDAT[4];eth_mdio(1,SETUPDAT[2],SETUPDAT[3],w0);EP0BCH = 0;EP0BCL = 8; // Arm endpoint with # bytes to transferEP0CS |= bmHSNAK; // Acknowledge handshake phase of device requestbreak;case 0x22://read eth mdioeth_mdio(0,SETUPDAT[2],SETUPDAT[3],0);*EP0BUF = SETUPDAT[2];*(EP0BUF+1) = SETUPDAT[3];*(EP0BUF+2) = SETUPDAT[4];*(EP0BUF+3) = SETUPDAT[5];*(EP0BUF+4) = SETUPDAT[6];*(EP0BUF+5) = SETUPDAT[7];*(EP0BUF+6) = eth_mdio_rdata>>8;*(EP0BUF+7) = eth_mdio_rdata;//data0//*(EP0BUF+6) = g_data0;//*(EP0BUF+7) = g_data0>>8;EP0BCH = 0;EP0BCL = 8; // Arm endpoint with # bytes to transferEP0CS |= bmHSNAK; // Acknowledge handshake phase of device requestbreak;default:break;}return(FALSE); // no error; command handled OK
}
注意点:

最终实现接口如下:

读一个mdio地址


写一个mdio地址:

实际测试结果:和FPGA读出的数据一致,功能正常实现,基于此方案还可以类似开发spi/i2c等接口功能

mdio时序:
类似于i2c但又不是,所以直接使用gpio模拟实现,而没有使用i2c控制器
简单概括就是下降沿写数据,上升沿读数据,由于我们的时钟不是一直有而是通过固定的循环模拟出来的,所以
同时注意在读写的前后多插入一些时钟,手册要求至少32个时钟的Pre
另外注意SDA的空闲状态默认设置为输入,68013芯片端口结构如下:(读写数据时需要切换端口的输入输出方向) /C:\Cypress\USB\doc\FX2LPEZ-USB_TRM.pdf



gpio-mdio example:其中PA7-SDA PA6-SCL
WORD eth_mdio_rdata = 0;
WORD g_data0;
//40kHz
void eth_mdio(unsigned char wr,unsigned char phyaddr,unsigned char regaddr,WORD w0)
{WORD a=0;WORD data0;WORD data1;unsigned char b =0;unsigned char c = 0;unsigned char d = 0;unsigned char e =0;unsigned char f =0;unsigned char g =0;unsigned char t0;unsigned char t1;unsigned char t2;//wr+phyaddr+reg+data or read +phyaddr+reg//PA7 SDA//PA6 SCL out OEA=0xFF;IOA=0xFe;//phyaddr = phyaddr & 0x1f;t1 = (phyaddr >> 1) & 0xf;if(wr == 1){t0 = 0x50+t1;}else {t0 = 0x60+t1;}if( (phyaddr & 0x01) == 0x1)t2 = 0x80;elset2 = 0;//regaddr = regaddr & 0x1f;regaddr = regaddr << 2;t2 = t2 + regaddr ;t2 = t2 | 0x02;// data0 = t0;data0 = data0 <<8;data0 += t2;//g_data0 = data0;//f= 0;g= 0;for(a=0;a<500;a++){//if(b==0) IOA |= (1<<6); //1else IOA &= (~(1<<6)); //0if(c) b = ~b;c = ~c;//if(e>=33){if( g==0 ){if(d == 1) //negedge out data{if(f == 14 ){if(wr ==0) OEA=0x7F;//IOA=0xFe;}else if(f == 15 ){if(wr ==0) g = 1;}else if(f == 16 ){if(wr ==1) data0 = w0;}else ;//if( (data0 & 0x8000) == 0x8000)IOA |= (1<<7); //1elseIOA &= (~(1<<7));//0 //data0 = data0 << 1;f = f+1; }}else{// read dataif(d == 0){if( (IOA & 0x80) == 0x80 )data1 |= 0x0001;else data1 &= 0xfffe;//if(f < 31)data1 = data1 <<1;f = f+1;}}} //d= d+1;if(d == 4) {d =0;e = e+1;if(f == 32) goto ENDA;}}ENDA:for(a=0,b=0,c=0;a<4*32;a++){//if(b==0) IOA |= (1<<6); //1else IOA &= (~(1<<6)); //0if(c) b = ~b;c = ~c;}eth_mdio_rdata = data1;//OEA=0x7F;IOA=0xFF;}
需要注意的是:对于keil2工具在做判断时不能写成
if( (phyaddr & 0x01) )
而应该
if( (phyaddr & 0x01) == 0x1)
个人觉得两者本身没有区别,但编译器存在bug只有第二种可正常执行。
4、操作步骤:


首先断开外部E2PROM(也就是把J2接上使芯片识别不到,使用的24LC128的寻址为001,短上后就变成000),这是后板卡会被芯片按默认状态识别:
![]()
当板卡被正常识别后如果需要固化到E2PROM我们还要再将J2拔掉(否则提示没有EEPROM),点击
选择hex转换成iic文件来固化程序
(c:\cypress\usb\bin\hex2bix -i -f 0xC2 -o bulkloop.iic bulkloop.hex)。


安装包配套资料还是比较全面的,我们主要用的就两个一个Download(选择hex,下载RAM中调试,只要不断电程序会一致保留)/一个Lg EEPROM(选择iic,固化程序,需要接上EEPROM)

芯片启动方式说明:
上电后芯片会检测EEPROM,如果第一个字节是0xc0就使用EEPROM中的VID/PID/DID,如果是0xc2就会将EEPROM加载到内部RAM执行,
否则使用内部的默认VID/PID/DID启动。


5、参考:
4种通信方式:
https://blog.csdn.net/clarkness/article/details/87349669
https://blog.csdn.net/kof2019/article/details/77774679

















