基于CY7C68013A usb转mdio win10 64bit

基于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

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值