原创 D12的USB C51驱动程序开发

2008-5-6 22:48 3279 4 4 分类: 通信

浅入深开展应用D12的USB C51驱动程序开发




USB设备启动流程:

1.USB设备接入USB口,发出连接USB命令
2.
主机发出读设备描述符两次。

3.
主机根据设备描述符——厂商ID、产品ID,启动相应设备驱动程序。

4.
设备驱动程序初始化USB设备:

  a.读设备描述符

  b.读配置描述符

  c.选择接口、端点(管道),确定传输方式

D12固件驱动:

//指向外部D12访问地址
#define D12_COMMAND (*(unsigned char xdata *)0x8000)
#define D12_DATA (*(unsigned char xdata *)0x0000)

D12_COMMAND = 0xf3;D12_DATA = 0x0e;D12_DATA = 0x03;//初始化频率 12MHz
D12_COMMAND = 0xd0;D12_DATA = 0x80;//
设置地址 0 使能

D12_COMMAND = 0xf3;D12_DATA = 0x1e;//
连接主机

D12_COMMAND = 0xf4;//
读中断寄存器

if(D12_DATA & 0x01)//
收到 SETUP

{
D12_COMMAND = 0x40;//
OUT 最后状态

if(D12_DATA & 0x20)
{
SETUP_read();
HandleSetup();
}
}
D12缓冲区

unsigned char SetupBuf[8];

void SETUP_read(void)
{
    unsigned char i;
    unsigned char * j;
    j = SetupBuf;
    D12_COMMAND = 0xf0;//
读标准控制码

    *j = D12_DATA;
    *(j+1) = D12_DATA;
    for(i=0;i<8;i++)
    {
       *(j + i) = D12_DATA;
    }
    D12_COMMAND = 0xf1;//
应答SETUP,使能( OUT 缓冲区、使能 IN 缓冲区)命令

    D12_COMMAND = 0xf2;//
OUT 缓冲区

}

D12缓冲区

void USB_submit(void)
{
    unsigned char i;
    D12_COMMAND = 0xf0;//
写缓冲区

    D12_DATA = 0x00;
    D12_DATA = XmtBuff.bLength;

    for(i=0;i<16;i++)
    {
        D12_DATA = *(XmtBuff.p++);
        if (!--XmtBuff.bLength)
        {
            break;
        }
    }
    D12_COMMAND = 0xfa;//
设置 IN 缓冲区有效

}


 
先介绍一下D12个管脚功能

1. D12的命令/数据切换有两种方式
 a. ALE 接地,由A0进行命令/数据切换,A0 1 时可以向D12写命令,A0 0 时可以向D12写数据。这时可以将A0P2口一个管脚(这里是A0P27),通过读写外部数据时,发出的地址来将A0 1/ 0

 b. A0 接高电平,由ALE进行命令/数据切换,将D12ALE51ALE(D12CS_N进行片选),当读写外部数据时,P0口发出奇数地址时,可向D12写入一个命令;P0口发出偶数地址时,可向D12读写数据。

2. D12SUSPEND
  当不能对D12操作时,不要忘了检查SUSPEND状态。如果SUSPEND为高表示D12已挂起,可将其置低,并发出恢复命令,唤醒主机。

3. D12INT_N
  可以说INT_N总是有效(由中断模式设置有关),一般D12挂起和忙时无效。

4. D12DMA传输有关管脚
  DMREQ 51发出DMA请求,51可以DMA要求读写数据

  DMACK_N 51D12确认DMA操作,D12完成DMA操作

  EOT_N 51D12发出结束DMA操作,同时要求DMACK_N置低,并发出读或写的动作。

引脚参数定义:

sbit D12_suspend=P1^0;
sbit D12_int_n=P1^1;
sbit D12_eot_n=P1^2;
sbit D12_DMAck_n=P1^3;
sbit D12_DMAreq=P1^4;


面对主机USB设备首先要处理的是主机标准控制请求。

下面代码可作为开发USB设备面对主机的模板。

[1] 处理主机标准控制请求:

unsigned char ENDPOINT_A0_FIFO[8]
//
判断输入的是SETUP请求,并将其读入缓冲区ENDPOINT_A0_FIFO

...
if((ENDPOINT_A0_FIFO[0] & 0b01100000)==0x00)
{
if (ENDPOINT_A0_FIFO[1] <= 0x0C)
{
(*StandardFunctionTable[ENDPOINT_A0_FIFO[1]])();
return;
}
}
...

const void (* StandardFunctionTable[])(void)=
{
GetStatus,ClearFeature,USB_Reserved,SetFeature,
USB_Reserved,SetAddress,GetDescriptor,SetDescriptor,
GetConfiguration,SetConfiguration,GetInterface,
SetInterface,SynchFrame
};

void GetStatus(void)
{
switch (ENDPOINT_A0_FIFO[0])
   {
case (0b10000000)://
返回设备状态

         //
发送两个字节数据:第一字节D1 1支持远程唤醒、为0不支持远程唤醒,D0 1设备自己供电、为0 USB总线供电,其它位为0;第二字节为0

break;
   case (0b100000001)://
返回接口状态

//
发送两个字节数据:第一字节为0;第二字节为0

break;
   case (0b10000010)://
返回端点状态

//
发送两个字节数据:第一字节D01端点处于暂停,否则D00,其它位为0;第二字节为0

break;
   }
}

void ClearFeature(void)
{
    if ((ENDPOINT_A0_FIFO[0] ==0b00000000)
        && (ENDPOINT_A0_FIFO[2] == 1) &&
           ( !ENDPOINT_A0_FIFO[3]))
        {
            //
清除设备远程唤醒功能

            return;
        }
    if ((ENDPOINT_A0_FIFO[0] ==0b00000001)
        && (ENDPOINT_A0_FIFO[2] == 0) &&
           ( !ENDPOINT_A0_FIFO[3]))
        {
            //
清除设备接口特殊功能 ENDPOINT_A0_FIFO[4] 为接口号

            return;
        }
    if ((ENDPOINT_A0_FIFO[0] == 0b00000010)
&& (ENDPOINT_A0_FIFO[2] == 0) &&
           (!ENDPOINT_A0_FIFO[3])) 
        {
            //
清除设备端点暂停功能 ENDPOINT_A0_FIFO[4] D7为端点方向,D3~D0为端点号

      return;
       }
}

void USB_Reserved(void)
{
//
只发确认信息。

}

void SetFeature(void)
{
if ((ENDPOINT_A0_FIFO[0] ==0b00000000)
&& (ENDPOINT_A0_FIFO[2] == 1) &&
( !ENDPOINT_A0_FIFO[3]))
{
//
设置设备远程唤醒功能

return;
}
if ((ENDPOINT_A0_FIFO[0] ==0b00000001)
&& (ENDPOINT_A0_FIFO[2] == 0) &&
( !ENDPOINT_A0_FIFO[3]))
{
//
设置设备接口特殊功能 ENDPOINT_A0_FIFO[4] 为接口号

return;
}
if ((ENDPOINT_A0_FIFO[0] == 0b00000010)
&& (ENDPOINT_A0_FIFO[2] == 0) &&
(!ENDPOINT_A0_FIFO[3]))
{
//
设置设备端点暂停 ENDPOINT_A0_FIFO[4] D7为端点方向,D3~D0为端点号

return;
}
}void SetAddress(void)
{
    if (ENDPOINT_A0_FIFO[0] == 0b00000000)
    {
        //
保存USB地址
0x80 | ENDPOINT_A0_FIFO[2];
    }
}
/*
注:

    SetAddress
请求实际可分成三个阶段。第一阶段,Setup包被送至设备,第二个可有无的阶段,数据在设备与主机之间传送,第三阶段,状态信息在主机与设备之间传送。

   
数据与状态传送的方向要看是主机发数据给设备还是设备发数据给主机。状态的传送方向总是与数据传送方向是相反的,如果没有数据传输阶段则状态由设备传向主机的。

    Setup
包传送以后的两个阶段的地址保持与Setup包传送阶段一致。USB设备只有在Status阶段过后才能改变设备地址。

*/

void GetDescriptor(void)
{
     if ( (ENDPOINT_A0_FIFO[0] != 0b10000000 DEVICE)) && 
         (ENDPOINT_A0_FIFO[0] != 0b10000001  INTERFACE)) &&
         (ENDPOINT_A0_FIFO[0] != 0b10000010  ENDPOINT)))        
    {
            return;
    }
    switch (ENDPOINT_A0_FIFO[3]) 
    {
        case 1 :
            //
发送设备描述表。 发送数据
<= ENDPOINT_A0_FIFO[6,7]
            break;
        case 2 :
            //
发送配置 [,接口(1),端点(1),接口(2),端点(2),...,,厂商等] 描述表

            break;
        case 3 :
            switch(ENDPOINT_A0_FIFO[2])
            {
            case 0x00 :
                //
发送字符串0描述表

                break;
            case 0x01 :
                //
发送字符串1描述表

               break;
            default   :
                return;
            }
            break;
        case 4 :
            //
发送接口描述表

            break;
        case 5 :
            //
发送端点描述表

            break;
    }
}

//设备描述表
const char USB_DEVICE_DEscriptOR[]=

  UCHAR bLength 
  UCHAR bDescriptorType 
  USHORT bcdUSB 
  UCHAR bDeviceClass 
  UCHAR bDeviceSubClass 
  UCHAR bDeviceProtocol 
  UCHAR bMaxPacketSize0 
  USHORT idVendor 
  USHORT idProduct 
  USHORT bcdDevice 
  UCHAR iManufacturer 
  UCHAR iProduct 
  UCHAR iSerialNumber 
  UCHAR bNumConfigurations 
};

//配置描述表
const char USB_CONFIGURATION_DEscriptOR[]=

  UCHAR bLength 
  UCHAR bDescriptorType 
  USHORT wTotalLength 
  UCHAR bNumInterfaces 
  .
  .
  UCHAR iConfiguration 
  UCHAR bmAttributes 
  UCHAR MaxPower 
};
//
接口描述表

const char USB_INTERFACE_DEscriptOR[]=

  UCHAR bLength 
  UCHAR bDescriptorType 
  UCHAR bInterfaceNumber 
  UCHAR bAlternateSetting 
  UCHAR bNumEndpoints 
  UCHAR bInterfaceClass 
  UCHAR bInterfaceSubClass 
  UCHAR bInterfaceProtocol 
  UCHAR iInterface 
};

//端点描述表
const char USB_ENDPOINT_DEscriptOR[]=

  UCHAR bLength 
  UCHAR bDescriptorType 
  UCHAR bEndpointAddress 
  UCHAR bmAttributes 
  USHORT wMaxPacketSize 
  UCHAR bInterval 
};

//字符串0描述表
const char USB_STRING_DEscriptOR[]=
{
  UCHAR bLength 
  UCHAR bDescriptorType 
  WCHAR bLangID[1] 
..
}

//字符串1描述表
const char USB_STRING_DEscriptOR[]=
{
  UCHAR bLength 
  UCHAR bDescriptorType 
  WCHAR bString[1] 
}


在发送配置 [,接口(1),端点(1),接口(2),端点(2),...,,厂商等] 联合描述表时,各描述表的先后顺序可随意,主机USBD根据描述表类型标识区分各种分描述表。

描述表类型:

USB_DEVICE_DEscriptOR_TYPE                0x01
USB_CONFIGURATION_DEscriptOR_TYPE        0x02
USB_STRING_DEscriptOR_TYPE                0x03
USB_INTERFACE_DEscriptOR_TYPE            0x04
USB_ENDPOINT_DEscriptOR_TYPE              0x05


//
标准设备描述表
const char USB_DEVICE_DEscriptOR[]=

  0x12, //
描述表长18字节

  0x01, //
设备描述表类型

  0x10,0x01, //
此设备与描述表兼容的USB设备说明版本号(BCD
)
  0x00, //
设备类码

  0x00, //
子类码

  0x00, //
协议码

  0x08, //
端点0的最大包大小(8,16,32,64为合法值
)
  0xb4,0x04,//
厂商ID(USB标准付值
)
  0x70,0x63,//
产品ID(由厂商付值
)
  0x01,0x00,//
设备发行号(BCD
)
  0x01,//
描述厂商信息的字串描述表索引

  0x02,//
描述产品信息的字串描述表索引

  0x00,//
描述设备序列号信息的字串描述表索引(不支持设为
0)
  0x01,//
可能的设置描述表数

};

USB设备类码(UNKNOWN设为0):

USB_DEVICE_CLASS_RESERVED           0x00
USB_DEVICE_CLASS_AUDIO              0x01
USB_DEVICE_CLASS_COMMUNICATIONS     0x02
USB_DEVICE_CLASS_HUMAN_INTERFACE    0x03
USB_DEVICE_CLASS_MONITOR            0x04
USB_DEVICE_CLASS_PHYSICAL_INTERFACE 0x05
USB_DEVICE_CLASS_POWER              0x06
USB_DEVICE_CLASS_PRINTER            0x07
USB_DEVICE_CLASS_STORAGE            0x08
USB_DEVICE_CLASS_HUB                0x09
USB_DEVICE_CLASS_VENDOR_SPECIFIC    0xFF

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
4
关闭 站长推荐上一条 /3 下一条