基于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 48MHz
CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ;
// set the slave FIFO interface to 48MHz
IFCONFIG |= 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 twice
SYNCDELAY;
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 |= 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 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
}
}
}
上位机与固件修改:
使用控制节点发送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 commands
case 0x20:
*EP0BUF = 0x15;
EP0BCH = 0;
EP0BCL = 1; // Arm endpoint with # bytes to transfer
EP0CS |= bmHSNAK; // Acknowledge handshake phase of device request
break;
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 transfer
EP0CS |= bmHSNAK; // Acknowledge handshake phase of device request
break;
case 0x22://read eth mdio
eth_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 transfer
EP0CS |= bmHSNAK; // Acknowledge handshake phase of device request
break;
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;
else
t2 = 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); //1
else IOA &= (~(1<<6)); //0
if(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); //1
else
IOA &= (~(1<<7));//0
//
data0 = data0 << 1;
f = f+1;
}
}
else
{
// read data
if(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); //1
else IOA &= (~(1<<6)); //0
if(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