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写数据。这时可以将A0接P2口一个管脚(这里是A0接P27),通过读写外部数据时,发出的地址来将A0 置 1/置 0。
b.将 A0 接高电平,由ALE进行命令/数据切换,将D12的ALE接51的ALE(由D12的CS_N进行片选),当读写外部数据时,P0口发出奇数地址时,可向D12写入一个命令;P0口发出偶数地址时,可向D12读写数据。
2. D12的SUSPEND
当不能对D12操作时,不要忘了检查SUSPEND状态。如果SUSPEND为高表示D12已挂起,可将其置低,并发出恢复命令,唤醒主机。
3. D12的INT_N
可以说INT_N总是有效(由中断模式设置有关),一般D12挂起和忙时无效。
4. 与D12的DMA传输有关管脚
DMREQ 向51发出DMA请求,51可以DMA要求读写数据
DMACK_N 是51向D12确认DMA操作,D12完成DMA操作
EOT_N 是51向D12发出结束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)://返回端点状态
//发送两个字节数据:第一字节D0为1端点处于暂停,否则D0为0,其它位为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
文章评论(0条评论)
登录后参与讨论