computer00的一些code

来源:互联网 发布:611资源网新域名 编辑:程序博客网 时间:2024/06/08 00:55

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途

        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

UART.H  file

作者:电脑圈圈
建立日期: 2008.06.27
修改日期: 2008.08.05
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#ifndef __UART_C__
#define __UART_C__

#include "MyType.h"

void InitUART(void);
void UartPutChar(uint8);
void Prints(uint8 *);
void PrintLongInt(uint32);
void PrintShortIntHex(uint16 x);
void Printc(uint8);
void PrintHex(uint8 x);
uint32 UartSetBitRate(uint32 BitRate);

//定义缓冲区的大小,这里使用64字节。
//对于RAM小的单片机,可以修改该定义来减少RAM的使用量。
//但是还要记得修改端点最大长度,否则接收数据可能会溢出。
#define BUF_LEN 64

extern idata uint8 UartBuffer[BUF_LEN];
extern idata uint8 UsbEp2Buffer[BUF_LEN];
extern uint8 UartBufferOutputPoint;
extern uint8 UartBufferInputPoint;
extern uint8 UartByteCount;
extern uint8 UsbEp2ByteCount;
extern uint8 UsbEp2BufferOutputPoint;

#endif

 

 

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途

        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

UART.C  file

作者:电脑圈圈
建立日期: 2008.06.27
修改日期: 2008.08.05
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#include <at89x52.H>

#include "UART.h"
#include "MyType.h"
#include "config.h"

idata uint8 UartBuffer[BUF_LEN];
idata uint8 UsbEp2Buffer[BUF_LEN];

uint8 UartBufferOutputPoint;
uint8 UartBufferInputPoint;
uint8 UsbEp2BufferOutputPoint;

uint8 UartByteCount;
uint8 UsbEp2ByteCount;

volatile uint8 Sending;

//使用T2做波特率发生器可以获得更多的波特率设置。
//删除本行将使用T1作为波特率发生器,最低波特率为300bps,最高为115200bps。
#define USE_T2

/********************************************************************
函数功能:设置串口波特率。
入口参数:BitRate: 需要设置的波特率。
返    回:实际设置的拨特率。
备    注:无。
********************************************************************/
uint32 UartSetBitRate(uint32 BitRate)
{
#ifdef USE_T2
 if(BitRate<=230400)
 {
  RCAP2L=0x10000-(Fclk/(BitRate*32));
  RCAP2H=(0x10000-(Fclk/(BitRate*32)))>>8;
 }
 BitRate=(Fclk/32)/(0x10000-((((uint32)RCAP2H)<<8)+RCAP2L));
#else
 if(BitRate<225)
 {
  BitRate=225;
  PCON&=~0x80;  //波特率不加倍
  TH1=256-Fclk/(BitRate*12*16*2);
  TL1=256-Fclk/(BitRate*12*16*2);
  BitRate=(Fclk/12/32)/(0x100-((uint32)TH1));
 }
 else if(BitRate<1200)
 {
  PCON&=~0x80;  //波特率不加倍
  TH1=256-Fclk/(BitRate*12*16*2);
  TL1=256-Fclk/(BitRate*12*16*2);
  BitRate=(Fclk/12/32)/(0x100-((uint32)TH1));
 }
 else if(BitRate<=115200)
 {
  PCON|=0x80;  //波特率加倍
  TH1=256-Fclk/(BitRate*12*16);
  TL1=256-Fclk/(BitRate*12*16);
  BitRate=(Fclk/12/16)/(0x100-((uint32)TH1));
 }
 else
 {
  BitRate=115200;
  PCON|=0x80;  //波特率加倍
  TH1=256-Fclk/(BitRate*12*16);
  TL1=256-Fclk/(BitRate*12*16);
  BitRate=(Fclk/12/16)/(0x100-((uint32)TH1));
 }
#endif
 return BitRate;
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:串口初始化。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void InitUART(void)
{
 EA=0;
 
#ifndef USE_T2
 TMOD&=0x0F;
 TMOD|=0x20;    //定时器1工作在模式2
 TCON=0x05;
#endif

 SCON=0x50;       //串口工作在模式1
 
 UartSetBitRate(9600);  //波特率初始化为9600

#ifdef USE_T2
 T2CON=0x34;     //使用T2作为波特率发生器
#endif

 ES=1;         //串行中断允许

#ifndef USE_T2
 TR1=1;        //启动定时器1
#endif

 REN=1;        //允许接收
 EA=1;         //允许中断
 Sending=0;    //允许发送
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:串口中断处理。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UartISR(void) interrupt 4
{
 if(RI)    //收到数据
 {
  RI=0;   //清中断请求
  //从SBUF读回一字节数据保存在缓冲区中
  UartBuffer[UartBufferInputPoint]=SBUF;
  //将输入位置下移
  UartBufferInputPoint++;
  //如果已经到达缓冲区末尾,则切换到缓冲区开头
  if(UartBufferInputPoint>=BUF_LEN)
  {
   UartBufferInputPoint=0;
  }
  //接收字节数加1
  UartByteCount++;
 }
 else      //发送完一字节数据
 {
  TI=0;
  Sending=0;  //清正在发送标志
 }
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:往串口发送一字节数据。
入口参数:d: 要发送的字节数据。
返    回:无。
备    注:无。
********************************************************************/
void UartPutChar(uint8 d)
{
 SBUF=d; //将数据写入到串口缓冲
 Sending=1;     //设置发送标志
 while(Sending); //等待发送完毕
}
////////////////////////End of function//////////////////////////////

#if (defined DEBUG0)||(defined DEBUG1)

code uint8 HexTable[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

/********************************************************************
函数功能:发送一个byte的数据。
入口参数:待发送的数据。
返    回:无。
备    注:无。
********************************************************************/
void Printc(uint8 x)
{
 Sending=1;
 SBUF=x;
 while(Sending);
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:以HEX格式发送一个byte的数据。
入口参数:待发送的数据
返    回:无。
备    注:无。
********************************************************************/
void PrintHex(uint8 x)
{
 Printc('0');
 Printc('x');
 Printc(HexTable[x>>4]);
 Printc(HexTable[x&0xf]);
 Printc(' ');
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:发送一个字符串。
入口参数:pd:要发送的字符串指针。
返    回:无。
备    注:无。
********************************************************************/
void Prints(uint8 * pd)
{
 while((*pd)!='/0') //发送字符串,直到遇到0才结束
 {
  UartPutChar(*pd); //发送一个字符
  pd++;  //移动到下一个字符
 }
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:将整数转按十进制字符串发送。
入口参数:x:待显示的整数。
返    回:无。
备    注:无。
********************************************************************/
void PrintLongInt(uint32 x)
{
 int8 i;
 uint8 display_buffer[10];

 for(i=9;i>=0;i--)
 {
  display_buffer[i]='0'+x%10;
  x/=10;
 }
 for(i=0;i<9;i++)
 {
  if(display_buffer[i]!='0')break;
 }
 for(;i<10;i++)UartPutChar(display_buffer[i]);
}
////////////////////////End of function//////////////////////////////

#endif

#ifdef DEBUG0
/********************************************************************
函数功能:将短整数按十六进制发送。
入口参数:待发送的整数。
返    回:无。
备    注:无。
********************************************************************/
void PrintShortIntHex(uint16 x)
{
 uint8 i;
 uint8 display_buffer[7];
 display_buffer[6]=0;
 display_buffer[0]='0';
 display_buffer[1]='x';
 for(i=5;i>=2;i--) //将整数转换为4个字节的HEX值
 {
  display_buffer[i]=HexTable[(x&0xf)];
  x>>=4;
 }
 Prints(display_buffer);
}
////////////////////////End of function//////////////////////////////
#endif

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途
           
        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

MyType.h file

作者:电脑圈圈
建立日期: 2008.06.27
修改日期: 2008.06.27
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#ifndef __MY_TYPE_H__
#define __MY_TYPE_H__

#define uint8    unsigned char
#define uint16   unsigned short int
#define uint32   unsigned long int
#define int8     signed char
#define int16    signed short int
#define int32    signed long int
#define uint64   unsigned long long int
#define int64    signed long long int

#endif

 

 

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途
           
        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

main.c file

作者:电脑圈圈
建立日期: 2008.06.27
修改日期: 2008.08.05
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#include <AT89X52.H>  //头文件
#include "UART.h"
#include "pdiusbd12.h"
#include "UsbCore.h"
#include "config.h"

#ifdef DEBUG0
code uint8 HeadTable[][74]={
"********************************************************************/r/n",
"******             《圈圈教你玩USB》之 USB转串口              ******/r/n",
"******                      AT89S52 CPU                       ******/r/n",
"******                  建立日期:",__DATE__,"                 ******/r/n",
"******                   建立时间:",__TIME__,"                   ******/r/n",
"******                    作者:电脑圈圈                      ******/r/n",
"******                    欢迎访问作者的                      ******/r/n",
"******           USB专区:http://group.ednchina.com/93/       ******/r/n",
"******      BLOG1:http://www.ednchina.com/blog/computer00    ******/r/n",
"******                BLOG2:http://computer00.21ic.org       ******/r/n",
"********************************************************************/r/n",
};
#endif

/********************************************************************
函数功能:将串口缓冲区中的数据发送到端点2的函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void SendUartDataToEp2(void)
{
 uint8 Len;
 
 //暂时禁止串行中断,防止UartByteCount在中断中修改而导致不同步
 ES=0;
 //将串口缓冲区接收到的字节数复制出来
 Len=UartByteCount;
 //检查长度是否为0,如果没有收到数据,则不需要处理,直接返回
 if(Len==0)
 {
  ES=1; //记得打开串口中断
  return;
 }
 //检查Len字节个数据是否跨越了缓冲区边界,如果跨越了,那么本次只发送
 //跨越边界之前的数据,剩余的数据留待下次发送。否则,可以一次发送全部。
 if((Len+UartBufferOutputPoint)>BUF_LEN)
 {
  Len=BUF_LEN-UartBufferOutputPoint;
 }
 //修改缓冲区数据字节数
 UartByteCount-=Len;
 
 //到这里可以打开串口中断了
 ES=1;
 
 //将数据写入到端点2输入缓冲区
 D12WriteEndpointBuffer(5,Len,UartBuffer+UartBufferOutputPoint);
 //修改输出数据的位置
 UartBufferOutputPoint+=Len;
 //如果已经到达缓冲区末尾,则设置回开头
 if(UartBufferOutputPoint>=BUF_LEN)
 {
  UartBufferOutputPoint=0;
 }
 //只有两个缓冲区都满时,才设置端点2输入忙
 if((D12ReadEndpointStatus(5)&0x60)==0x60)
 {
  Ep2InIsBusy=1;
 }
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:主函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void main(void)  //主函数
{
#ifdef DEBUG0
 uint8 i;
#endif

 uint16 id;
 uint8 InterruptSource;
 
 EA=1; //打开中断
 InitUART();  //初始化串口

#ifdef DEBUG0
 for(i=0;i<15;i++)      //显示信息
 {
  Prints(HeadTable[i]);
 }
#endif

 id=D12ReadID();

#ifdef DEBUG0
 Prints("Your D12 chip/'s ID is: ");
 PrintShortIntHex(id);

 if(id==0x1012)
 {
  Prints(". ID is correct! Congratulations!/r/n/r/n");
 }
 else
 {
  Prints(". ID is incorrect! What a pity!/r/n/r/n");
 }
#endif

 UsbDisconnect();  //先断开USB连接
 UsbConnect();  //将USB连接上
 ConfigValue=0; //配置值初始化为0
 
 while(1)  //死循环
 {
  if(D12GetIntPin()==0) //如果有中断发生
  {
   D12WriteCommand(READ_INTERRUPT_REGISTER);  //写读中断寄存器的命令
   InterruptSource=D12ReadByte(); //读回第一字节的中断寄存器
   if(InterruptSource&0x80)UsbBusSuspend(); //总线挂起中断处理
   if(InterruptSource&0x40)UsbBusReset();   //总线复位中断处理
   if(InterruptSource&0x01)UsbEp0Out();     //端点0输出中断处理
   if(InterruptSource&0x02)UsbEp0In();      //端点0输入中断处理
   if(InterruptSource&0x04)UsbEp1Out();     //端点1输出中断处理
   if(InterruptSource&0x08)UsbEp1In();      //端点1输入中断处理
   if(InterruptSource&0x10)UsbEp2Out();     //端点2输出中断处理
   if(InterruptSource&0x20)UsbEp2In();      //端点2输入中断处理
  }
  if(ConfigValue!=0) //如果已经设置为非0的配置,则可以返回和发送串口数据
  {
   if(Ep2InIsBusy==0)  //如果端点2空闲,则发送串口数据到端点2
   {
    SendUartDataToEp2();  //调用函数将缓冲区数据发送到端点2
   }
   if(UsbEp2ByteCount!=0) //端点2接收缓冲区中还有数据未发送,则发送到串口
   {
    //发送一字节到串口
    UartPutChar(UsbEp2Buffer[UsbEp2BufferOutputPoint]);
    UsbEp2BufferOutputPoint++; //发送位置后移1
    UsbEp2ByteCount--;   //计数值减1
   }
  }
 }
}
////////////////////////End of function//////////////////////////////

 

 

 

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途
           
        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

config.h file

作者:电脑圈圈
建立日期: 2008.06.27
修改日期: 2008.07.10
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#ifndef __CONFIG_H__
#define __CONFIG_H__

#define Fclk      22118400UL         /*使用22.1184M晶体*/

//#define DEBUG0
//#define DEBUG1

#endif

 

 

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途
           
        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

UsbCore.c file

作者:电脑圈圈
建立日期: 2008.06.29
修改日期: 2008.08.05
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#include "config.h"
#include "pdiusbd12.h"
#include "uart.h"
#include "usbcore.h"

idata uint8 Buffer[16];  //读端点0用的缓冲区


//USB设备请求的各字段
uint8  bmRequestType;
uint8  bRequest;
uint16 wValue;
uint16 wIndex;
uint16 wLength;
//当前发送数据的位置
uint8 * pSendData;
//需要发送数据的长度
uint16 SendLength;
//是否需要发送0数据包的标志。在USB控制传输的数据过程中,
//当返回的数据包字节数少于最大包长时,会认为数据过程结束。
//当请求的字节数比实际需要返回的字节数长,而实际返回的字节
//数又刚好是端点0大小的整数倍时,就需要返回一个0长度的数据包
//来结束数据过程。因此这里增加一个标志,供程序决定是否需要返回
//一个0长度的数据包。
uint8 NeedZeroPacket;

//当前的配置值。只有在设置非0配置后
uint8 ConfigValue;

//端点1缓冲是否忙的标志。当缓冲区中有数据时,该标志为真。
//当缓冲区中空闲时,该标志为假。
uint8 Ep1InIsBusy;

//端点2缓冲是否忙的标志。当缓冲区中有数据时,该标志为真。
//当缓冲区中空闲时,该标志为假。
uint8 Ep2InIsBusy;

//LineCoding数组,用来保存波特率、停止位等串口属性。
//初始化波特率为9600,1停止位,无校验,8数据位。
uint8 LineCoding[7]={0x80,0x25,0x00,0x00,0x00,0x00,0x08};

//USB设备描述符的定义
code uint8 DeviceDescriptor[0x12]=  //设备描述符为18字节
{
//bLength字段。设备描述符的长度为18(0x12)字节
 0x12,
 
//bDescriptorType字段。设备描述符的编号为0x01
 0x01,
 
//bcdUSB字段。这里设置版本为USB1.1,即0x0110。
//由于是小端结构,所以低字节在先,即0x10,0x01。
 0x10,
 0x01,
 
//bDeviceClass字段。本设备必须在设备描述符中指定设备的类型,
//否则,由于在配置集合中有两个接口,就会被系统认为是一个USB
//复合设备,从而导致设备工作不正常。0x02为通信设备类的类代码。
 0x02,
 
//bDeviceSubClass字段。必须为0。
 0x00,
 
//bDeviceProtocol字段。必须为0。
 0x00,
 
//bMaxPacketSize0字段。PDIUSBD12的端点0大小的16字节。
 0x10,
 
//idVender字段。厂商ID号,我们这里取0x8888,仅供实验用。
//实际产品不能随便使用厂商ID号,必须跟USB协会申请厂商ID号。
//注意小端模式,低字节在先。
 0x88,
 0x88,
 
//idProduct字段。产品ID号,由于是第七个实验,我们这里取0x0007。
//注意小端模式,低字节应该在前。
 0x07,
 0x00,
 
//bcdDevice字段。取1.0版,即0x0100。
//小端模式,低字节在先。
 0x00,
 0x01,
 
//iManufacturer字段。厂商字符串的索引值,为了方便记忆和管理,
//字符串索引就从1开始。
 0x01,

//iProduct字段。产品字符串的索引值。刚刚用了1,这里就取2吧。
//注意字符串索引值不要使用相同的值。
 0x02,
 
//iSerialNumber字段。设备的序列号字符串索引值。
//这里取3就可以了。
 0x03,

//bNumConfigurations字段。该设备所具有的配置数。
//我们只需要一种配置就行了,因此该值设置为1。
 0x01
};
//////////////////////////设备描述符完毕//////////////////////////////


//USB配置描述符集合的定义
//配置描述符总长度为9+9+9+7+7+9+9+7字节
code uint8 ConfigurationDescriptor[9+9+5+5+4+5+7+9+7+7]=
{
 /***************配置描述符***********************/
 //bLength字段。配置描述符的长度为9字节。
 0x09,
 
 //bDescriptorType字段。配置描述符编号为0x02。
 0x02,
 
 //wTotalLength字段。配置描述符集合的总长度,
 //包括配置描述符本身、接口描述符、类描述符、端点描述符等。
 sizeof(ConfigurationDescriptor)&0xFF, //低字节
 (sizeof(ConfigurationDescriptor)>>8)&0xFF, //高字节
 
 //bNumInterfaces字段。该配置包含的接口数,有两个接口。
 0x02,
 
 //bConfiguration字段。该配置的值为1。
 0x01,
 
 //iConfigurationz字段,该配置的字符串索引。这里没有,为0。
 0x00,
 
 //bmAttributes字段,该设备的属性。由于我们的板子是总线供电的,
 //并且我们不想实现远程唤醒的功能,所以该字段的值为0x80。
 0x80,
 
 //bMaxPower字段,该设备需要的最大电流量。由于我们的板子
 //需要的电流不到100mA,因此我们这里设置为100mA。由于每单位
 //电流为2mA,所以这里设置为50(0x32)。
 0x32,
 
 /*******************CDC类接口描述符*********************/
 //bLength字段。接口描述符的长度为9字节。
 0x09,
 
 //bDescriptorType字段。接口描述符的编号为0x04。
 0x04,
 
 //bInterfaceNumber字段。该接口的编号,第一个接口,编号为0。
 0x00,
 
 //bAlternateSetting字段。该接口的备用编号,为0。
 0x00,
 
 //bNumEndpoints字段。非0端点的数目。CDC接口只使用一个中断
 //输入端点。
 0x01,
 
 //bInterfaceClass字段。该接口所使用的类。CDC类的类代码为0x02。
 0x02,
 
 //bInterfaceSubClass字段。该接口所使用的子类。要实现USB转串口,
 //就必须使用Abstract Control Model(抽象控制模型)子类。它的
 //编号为0x02。
 0x02,
 
 //bInterfaceProtocol字段。使用Common AT Commands(通用AT命令)
 //协议。该协议的编号为0x01。
 0x01,
 
 //iConfiguration字段。该接口的字符串索引值。这里没有,为0。
 0x00,
 
 /***************以下为功能描述符****************/
 /********* Header Functional Descriptor ********/
 //bFunctionLength字段。该描述符长度为5字节
 0x05,
 
 //bDescriptorType字段。描述符类型为类特殊接口(CS_INTERFACE)
 //编号为0x24。
 0x24,
 
 //bDescriptorSubtype字段。描述符子类为Header Functional Descriptor
 //编号为0x00。
 0x00,
 
 //bcdCDC字段。CDC版本号,为0x0110(低字节在先)
 0x10,
 0x01,
 
 /**** Call Management Functional Descriptor ****/
 //bFunctionLength字段。该描述符长度为5字节
 0x05,
 
 //bDescriptorType字段。描述符类型为类特殊接口(CS_INTERFACE)
 //编号为0x24。
 0x24,
 
 //bDescriptorSubtype字段。描述符子类为Call Management
 //functional descriptor,编号为0x01。
 0x01,
 
 //bmCapabilities字段。设备自己不管理call management
 0x00,
 
 //bDataInterface字段。没有数据类接口用作call management
 0x00,

 /*** Abstract Control Management Functional Descriptor ***/
 //bFunctionLength字段。该描述符长度为4字节
 0x04,
 
 //bDescriptorType字段。描述符类型为类特殊接口(CS_INTERFACE)
 //编号为0x24。
 0x24,
 
 //bDescriptorSubtype字段。描述符子类为Abstract Control
 //Management functional descriptor,编号为0x02。
 0x02,

 //bmCapabilities字段。支持Set_Line_Coding、Set_Control_Line_State、
 //Get_Line_Coding请求和Serial_State通知
 0x02,

 /***  Union Functional Descriptor  **/
 //bFunctionLength字段。该描述符长度为5字节。
 0x05,

 //bDescriptorType字段。描述符类型为类特殊接口(CS_INTERFACE)
 //编号为0x24。
 0x24,
 
 //bDescriptorSubtype字段。描述符子类为
 //Union functional descriptor,编号为0x06。
 0x06,
 
 //MasterInterface字段。这里为前面编号为0的CDC接口。
 0x00,
 
 //SlaveInterface字段,这里为接下来编号为1的数据类接口。
 0x01,

 /***********  以下为接口0的端点描述符  *******/
 //bLength字段。端点描述符长度为7字节。
 0x07,
 
 //bDescriptorType字段。端点描述符编号为0x05。
 0x05,
 
 //bEndpointAddress字段。端点的地址。这里使用D12的输入端点1。
 //D7位表示数据方向,输入端点D7为1。所以输入端点1的地址为0x81。
 0x81,
 
 //bmAttributes字段。D1~D0为端点传输类型选择。
 //该端点为中断端点。中断端点的编号为3。其它位保留为0。
 0x03,
 
 //wMaxPacketSize字段。该端点的最大包长。端点1的最大包长为16字节。
 //注意低字节在先。
 0x10,
 0x00,
 
 //bInterval字段。端点查询的时间,这里设置为10个帧时间,即10ms。
 0x0A,
 
 /*********  以下为接口1(数据接口)的接口描述符  *********/
 //bLength字段。接口描述符的长度为9字节。
 0x09,
 
 //bDescriptorType字段。接口描述符的编号为0x04。
 0x04,
 
 //bInterfaceNumber字段。该接口的编号,第二个接口,编号为1。
 0x01,
 
 //bAlternateSetting字段。该接口的备用编号,为0。
 0x00,
 
 //bNumEndpoints字段。非0端点的数目。该设备需要使用一对批量端点,设置为2。
 0x02,
 
 //bInterfaceClass字段。该接口所使用的类。数据类接口的代码为0x0A。
 0x0A,
 
 //bInterfaceSubClass字段。该接口所使用的子类为0。
 0x00,
 
 //bInterfaceProtocol字段。该接口所使用的协议为0。
 0x00,
 
 //iConfiguration字段。该接口的字符串索引值。这里没有,为0。
 0x00,
 
 /*****  以下为接口1(数据类接口)的端点描述符  *****/
 /*************** 批量输入端点2描述符 ******************/
 //bLength字段。端点描述符长度为7字节。
 0x07,
 
 //bDescriptorType字段。端点描述符编号为0x05。
 0x05,
 
 //bEndpointAddress字段。端点的地址。我们使用D12的输入端点2。
 //D7位表示数据方向,输入端点D7为1。所以输入端点2的地址为0x82。
 0x82,
 
 //bmAttributes字段。D1~D0为端点传输类型选择。
 //该端点为批量端点,批量端点的编号为0x02。其它位保留为0。
 0x02,
 
 //wMaxPacketSize字段。该端点的最大包长。端点2的最大包长为64字节。
 //注意低字节在先。
 0x40,
 0x00,
 
 //bInterval字段。端点查询的时间,这里对批量端点无效。
 0x00,
 
 /*************** 批量输出端点2描述符 ******************/
 //bLength字段。端点描述符长度为7字节。
 0x07,
 
 //bDescriptorType字段。端点描述符编号为0x05。
 0x05,
 
 //bEndpointAddress字段。端点的地址。我们使用D12的输出端点2。
 //D7位表示数据方向,输出端点D7为0。所以输出端点2的地址为0x02。
 0x02,
 
 //bmAttributes字段。D1~D0为端点传输类型选择。
 //该端点为批量端点,批量端点的编号为0x02。其它位保留为0。
 0x02,
 
 //wMaxPacketSize字段。该端点的最大包长。端点2的最大包长为64字节。
 //注意低字节在先。
 0x40,
 0x00,
 
 //bInterval字段。端点查询的时间,这里对批量端点无效。
 0x00
};
////////////////////////配置描述符集合完毕//////////////////////////

/************************语言ID的定义********************/
code uint8 LanguageId[4]=
{
 0x04, //本描述符的长度
 0x03, //字符串描述符
 //0x0409为美式英语的ID
 0x09,
 0x04
};
////////////////////////语言ID完毕//////////////////////////////////

/**************************************************/
/*********        本转换结果来自         **********/
/********* Http://computer00.21ic.org    **********/
/*********        作者: 电脑圈圈         **********/
/*********         欢迎大家使用          **********/
/*********    版权所有,盗版请写明出处   **********/
/**************************************************/

//http://computer00.21ic.org/user1/2198/archives/2007/42769.html
//字符串“电脑圈圈的USB专区 Http://group.ednchina.com/93/”的Unicode编码
//8位小端格式
code uint8 ManufacturerStringDescriptor[82]={
82,         //该描述符的长度为82字节
0x03,       //字符串描述符的类型编码为0x03
0x35, 0x75, //电
0x11, 0x81, //脑
0x08, 0x57, //圈
0x08, 0x57, //圈
0x84, 0x76, //的
0x55, 0x00, //U
0x53, 0x00, //S
0x42, 0x00, //B
0x13, 0x4e, //专
0x3a, 0x53, //区
0x20, 0x00, //
0x48, 0x00, //H
0x74, 0x00, //t
0x74, 0x00, //t
0x70, 0x00, //p
0x3a, 0x00, //:
0x2f, 0x00, ///
0x2f, 0x00, ///
0x67, 0x00, //g
0x72, 0x00, //r
0x6f, 0x00, //o
0x75, 0x00, //u
0x70, 0x00, //p
0x2e, 0x00, //.
0x65, 0x00, //e
0x64, 0x00, //d
0x6e, 0x00, //n
0x63, 0x00, //c
0x68, 0x00, //h
0x69, 0x00, //i
0x6e, 0x00, //n
0x61, 0x00, //a
0x2e, 0x00, //.
0x63, 0x00, //c
0x6f, 0x00, //o
0x6d, 0x00, //m
0x2f, 0x00, ///
0x39, 0x00, //9
0x33, 0x00, //3
0x2f, 0x00  ///
};
/////////////////////////厂商字符串结束/////////////////////////////

//字符串“《圈圈教你玩USB》之 USB转串口”的Unicode编码
//8位小端格式
code uint8 ProductStringDescriptor[38]={
38,         //该描述符的长度为38字节
0x03,       //字符串描述符的类型编码为0x03
0x0a, 0x30, //《
0x08, 0x57, //圈
0x08, 0x57, //圈
0x59, 0x65, //教
0x60, 0x4f, //你
0xa9, 0x73, //玩
0x55, 0x00, //U
0x53, 0x00, //S
0x42, 0x00, //B
0x0b, 0x30, //》
0x4b, 0x4e, //之
0x20, 0x00, //
0x55, 0x00, //U
0x53, 0x00, //S
0x42, 0x00, //B
0x6c, 0x8f, //转
0x32, 0x4e, //串
0xe3, 0x53  //口
};
////////////////////////产品字符串结束////////////////////////////

//字符串“2008-08-03”的Unicode编码
//8位小端格式
code uint8 SerialNumberStringDescriptor[22]={
22,         //该描述符的长度为22字节
0x03,       //字符串描述符的类型编码为0x03
0x32, 0x00, //2
0x30, 0x00, //0
0x30, 0x00, //0
0x38, 0x00, //8
0x2d, 0x00, //-
0x30, 0x00, //0
0x38, 0x00, //8
0x2d, 0x00, //-
0x30, 0x00, //0
0x33, 0x00  //3
};
//////////////////////产品序列号字符串结束/////////////////////////

/********************************************************************
函数功能:延时x毫秒函数。
入口参数:x:延时的毫秒数。
返    回:无。
备    注:无。
********************************************************************/
void DelayXms(uint16 x)               
{
 uint16 i;
 uint16 j;
 for(i=0;i<x;i++)
 for(j=0;j<227;j++); //循环语句延时
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:USB断开连接函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbDisconnect(void)
{
#ifdef DEBUG0
 Prints("断开USB连接。/r/n");
#endif
 D12WriteCommand(D12_SET_MODE);  //写设置模式命令
 D12WriteByte(0x06); //设置模式的第一字节
 D12WriteByte(0x47); //设置模式的第二字节
 DelayXms(1000);  //延迟1秒
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:USB连接函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbConnect(void)
{
#ifdef DEBUG0
 Prints("连接USB。/r/n");
#endif
 D12WriteCommand(D12_SET_MODE);  //写设置模式命令
 D12WriteByte(0x16); //设置模式的第一字节
 D12WriteByte(0x47); //设置模式的第二字节
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:总线挂起中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbBusSuspend(void)
{
#ifdef DEBUG0
 Prints("USB总线挂起。/r/n");
#endif
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:总线复位中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbBusReset(void)
{
#ifdef DEBUG0
 Prints("USB总线复位。/r/n");
#endif
 Ep1InIsBusy=0; //复位后端点1输入缓冲区空闲。
 Ep2InIsBusy=0; //复位后端点2输入缓冲区空闲。
 UartBufferOutputPoint=0;
 UartBufferInputPoint=0;
 UartByteCount=0;
 UsbEp2ByteCount=0;
 UsbEp2BufferOutputPoint=0;
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:根据pData和SendLength将数据发送到端点0的函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbEp0SendData(void)
{
 //将数据写到端点中去准备发送
 //写之前要先判断一下需要发送的数据是否比端点0
 //最大长度大,如果超过端点大小,则一次只能发送
 //最大包长的数据。端点0的最大包长在DeviceDescriptor[7]
 if(SendLength>DeviceDescriptor[7])
 {
  //按最大包长度发送
  D12WriteEndpointBuffer(1,DeviceDescriptor[7],pSendData);
  //发送后剩余字节数减少最大包长
  SendLength-=DeviceDescriptor[7];
  //发送一次后指针位置要调整
  pSendData+= DeviceDescriptor[7];
 }
 else
 {
  if(SendLength!=0)
  {
   //不够最大包长,可以直接发送
   D12WriteEndpointBuffer(1,SendLength,pSendData);
   //发送完毕后,SendLength长度变为0
   SendLength=0;
  }
  else //如果要发送的数据包长度为0
  {
   if(NeedZeroPacket==1) //如果需要发送0长度数据
   {
    D12WriteEndpointBuffer(1,0,pSendData); //发送0长度数据包
    NeedZeroPacket=0; //清需要发送0长度数据包标志
   }
  }
 }
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:USB端点0数据过程数据处理函数。
入口参数:无。
返    回:无。
备    注:该函数用来处理0端点控制传输的数据或状态过程。
********************************************************************/
void UsbEp0DataOut(void)
{
 //由于本程序中只有一个请求输出数据,所以可以直接使用if语句判断条件,
 //如果有很多请求的话,使用if语句就不方便了,而应该使用switch语句散转。
 if((bmRequestType==0x21)&&(bRequest==SET_LINE_CODING))
 {
  uint32 BitRate;
  uint8 Length;
 
  //读回7字节的LineCoding值
  Length=D12ReadEndpointBuffer(0,7,LineCoding);
  D12ClearBuffer(); //清除缓冲区
 
  if(Length==7) //如果长度正确
  {
   //从LineCoding计算设置的波特率
   BitRate=LineCoding[3];
   BitRate=(BitRate<<8)+LineCoding[2];
   BitRate=(BitRate<<8)+LineCoding[1];
   BitRate=(BitRate<<8)+LineCoding[0];
  #ifdef DEBUG0
   Prints("波特率设置为:");
   PrintLongInt(BitRate);
   Prints("bps/r/n");
  #endif
   //设置串口的波特率
   BitRate=UartSetBitRate(BitRate);
  
   //将LineCoding的值设置为实际的设置值
   LineCoding[0]=BitRate&0xFF;
   LineCoding[1]=(BitRate>>8)&0xFF;
   LineCoding[2]=(BitRate>>16)&0xFF;
   LineCoding[3]=(BitRate>>24)&0xFF;
 
   //由于只支持一停止位、无校验、8位数据位,
   //所以固定这些数据。
   LineCoding[4]=0x00;
   LineCoding[5]=0x00;
   LineCoding[6]=0x08;
  }
  //返回0长度的状态数据包。
  D12WriteEndpointBuffer(1,0,0);
 }
 else  //其它请求的数据过程或者状态过程
 {
  D12ReadEndpointBuffer(0,16,Buffer);
  D12ClearBuffer();
 }
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:端点0输出中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbEp0Out(void)
{
#ifdef DEBUG0
 Prints("USB端点0输出中断。/r/n");
#endif
 //读取端点0输出最后传输状态,该操作清除中断标志
 //并判断第5位是否为1,如果是,则说明是建立包
 if(D12ReadEndpointLastStatus(0)&0x20)
 {
  D12ReadEndpointBuffer(0,16,Buffer); //读建立过程数据
  D12AcknowledgeSetup(); //应答建立包
  D12ClearBuffer(); //清缓冲区
  //将缓冲数据填到设备请求的各字段中
  bmRequestType=Buffer[0];
  bRequest=Buffer[1];
  wValue=Buffer[2]+(((uint16)Buffer[3])<<8);
  wIndex=Buffer[4]+(((uint16)Buffer[5])<<8);
  wLength=Buffer[6]+(((uint16)Buffer[7])<<8);
  //下面的代码判断具体的请求,并根据不同的请求进行相关操作
  //如果D7位为1,则说明是输入请求
  if((bmRequestType&0x80)==0x80)
  {
   //根据bmRequestType的D6~5位散转,D6~5位表示请求的类型
   //0为标准请求,1为类请求,2为厂商请求。
   switch((bmRequestType>>5)&0x03)
   {
    case 0:  //标准请求
     #ifdef DEBUG0
      Prints("USB标准输入请求:");
     #endif
     //USB协议定义了几个标准输入请求,我们实现这些标准请求即可
     //请求的代码在bRequest中,对不同的请求代码进行散转
     //事实上,我们还需要对接收者进行散转,因为不同的请求接收者
     //是不一样的。接收者在bmRequestType的D4~D0位中定义。
     //我们这里为了简化操作,有些就省略了对接收者的判断。
     //例如获取描述符的请求,只根据描述符的类型来区别。
     switch(bRequest)
     {
      case GET_CONFIGURATION: //获取配置
       #ifdef DEBUG0
        Prints("获取配置。/r/n");
       #endif
      break;
     
      case GET_DESCRIPTOR:  //获取描述符
       #ifdef DEBUG0
        Prints("获取描述符——");
       #endif
       //对描述符类型进行散转,对于全速设备,
       //标准请求只支持发送到设备的设备、配置、字符串三种描述符
       switch((wValue>>8)&0xFF)
        {
         case DEVICE_DESCRIPTOR: //设备描述符
          #ifdef DEBUG0
           Prints("设备描述符。/r/n");
          #endif
          pSendData=DeviceDescriptor;  //需要发送的数据
          //判断请求的字节数是否比实际需要发送的字节数多
          //这里请求的是设备描述符,因此数据长度就是
          //DeviceDescriptor[0]。如果请求的比实际的长,
          //那么只返回实际长度的数据
          if(wLength>DeviceDescriptor[0])
          {
           SendLength=DeviceDescriptor[0];
           if(SendLength%DeviceDescriptor[7]==0) //并且刚好是整数个数据包时
           {
            NeedZeroPacket=1; //需要返回0长度的数据包
           }
          }
          else
          {
           SendLength=wLength;
          }
          //将数据通过EP0返回
          UsbEp0SendData();
         break;
        
         case CONFIGURATION_DESCRIPTOR:  //配置描述符
          #ifdef DEBUG0
           Prints("配置描述符。/r/n");
          #endif
          pSendData=ConfigurationDescriptor; //需要发送的数据为配置描述符
          //判断请求的字节数是否比实际需要发送的字节数多
          //这里请求的是配置描述符集合,因此数据长度就是
          //ConfigurationDescriptor[3]*256+ConfigurationDescriptor[2]。
          //如果请求的比实际的长,那么只返回实际长度的数据
          SendLength=ConfigurationDescriptor[3];
          SendLength=SendLength*256+ConfigurationDescriptor[2];
          if(wLength>SendLength)
          {
           if(SendLength%DeviceDescriptor[7]==0) //并且刚好是整数个数据包时
           {
            NeedZeroPacket=1; //需要返回0长度的数据包
           }
          }
          else
          {
           SendLength=wLength;
          }
          //将数据通过EP0返回
          UsbEp0SendData();
         break;
        
         case STRING_DESCRIPTOR:  //字符串描述符
          #ifdef DEBUG0
           Prints("字符串描述符");
          #endif
          switch(wValue&0xFF)  //根据wValue的低字节(索引值)散转
          {
           case 0:  //获取语言ID
            #ifdef DEBUG0
             Prints("(语言ID)。/r/n");
            #endif
            pSendData=LanguageId;
            SendLength=LanguageId[0];
           break;
          
           case 1:  //厂商字符串的索引值为1,所以这里为厂商字符串
           #ifdef DEBUG0
             Prints("(厂商描述)。/r/n");
            #endif
            pSendData=ManufacturerStringDescriptor;
            SendLength=ManufacturerStringDescriptor[0];
           break;
          
           case 2:  //产品字符串的索引值为2,所以这里为产品字符串
           #ifdef DEBUG0
             Prints("(产品描述)。/r/n");
            #endif
            pSendData=ProductStringDescriptor;
            SendLength=ProductStringDescriptor[0];
           break;
          
           case 3:  //产品序列号的索引值为3,所以这里为序列号
           #ifdef DEBUG0
             Prints("(产品序列号)。/r/n");
            #endif
            pSendData=SerialNumberStringDescriptor;
            SendLength=SerialNumberStringDescriptor[0];
           break;
          
           default :
            #ifdef DEBUG0
             Prints("(未知的索引值)。/r/n");
            #endif
            //对于未知索引值的请求,返回一个0长度的包
            SendLength=0;
            NeedZeroPacket=1;
           break;
          }
          //判断请求的字节数是否比实际需要发送的字节数多
          //如果请求的比实际的长,那么只返回实际长度的数据
          if(wLength>SendLength)
          {
           if(SendLength%DeviceDescriptor[7]==0) //并且刚好是整数个数据包时
           {
            NeedZeroPacket=1; //需要返回0长度的数据包
           }
          }
          else
          {
           SendLength=wLength;
          }
          //将数据通过EP0返回
          UsbEp0SendData();        
         break;
                
         default:  //其它描述符
          #ifdef DEBUG0
           Prints("其他描述符,描述符代码:");
           PrintHex((wValue>>8)&0xFF);
           Prints("/r/n");
          #endif
         break;
        }
       break;
     
      case GET_INTERFACE: //获取接口
       #ifdef DEBUG0
        Prints("获取接口。/r/n");
       #endif
      break;
     
      case GET_STATUS: //获取状态
       #ifdef DEBUG0
        Prints("获取状态。/r/n");
       #endif
      break;
     
      case SYNCH_FRAME: //同步帧
       #ifdef DEBUG0
        Prints("同步帧。/r/n");
       #endif
      break;
     
      default:  //未定义的标准请求
       #ifdef DEBUG0
        Prints("错误:未定义的标准输入请求。/r/n");
       #endif      
      break;
     }
    break;
   
    case 1:  //类请求
     #ifdef DEBUG0
      Prints("USB类输入请求:");
     #endif
     switch(bRequest)
     {
      case GET_LINE_CODING: //GET_LINE_CODING请求
       #ifdef DEBUG0
        Prints("GET_LINE_CODING。/r/n");
       #endif
       SendLength=0x07; //7字节的LineCoding
       pSendData=LineCoding;
      break;
     
      case SERIAL_STATE: //获取SERIAL_STATE请求
       //本来该请求是获取串口状态的,但是圈圈在实际使用中,
       //发现主机从来未发送过该请求,因而这里并不对它进行处理,
       //只是简单地发送一个0长度的数据包。
       #ifdef DEBUG0
        Prints("SERIAL_STATE。/r/n");
       #endif      
       SendLength=0;
       NeedZeroPacket=1;
      break;
     
      default:
       #ifdef DEBUG0
        Prints("未知类请求。/r/n");
       #endif
       SendLength=0;
       NeedZeroPacket=1;
      break;
     }
    //判断请求的字节数是否比实际需要发送的字节数多
    //如果请求的比实际的长,那么只返回实际长度的数据
    if(wLength>SendLength)
    {
     if(SendLength%DeviceDescriptor[7]==0) //并且刚好是整数个数据包时
     {
      NeedZeroPacket=1; //需要返回0长度的数据包
     }
    }
    else
    {
     SendLength=wLength;
    }
    //将数据通过EP0返回
    UsbEp0SendData();
    break;
   
    case 2:  //厂商请求
     #ifdef DEBUG0
      Prints("USB厂商输入请求:/r/n");
     #endif
    break;
   
    default: //未定义的请求。这里只显示一个报错信息。
     #ifdef DEBUG0
      Prints("错误:未定义的输入请求。/r/n");
     #endif
    break;
   }
  }
  //否则说明是输出请求
  else //if(bmRequestType&0x80==0x80)之else
  {
   //根据bmRequestType的D6~5位散转,D6~5位表示请求的类型
   //0为标准请求,1为类请求,2为厂商请求。
   switch((bmRequestType>>5)&0x03)
   {
    case 0:  //标准请求
     #ifdef DEBUG0
      Prints("USB标准输出请求:");
     #endif
     //USB协议定义了几个标准输出请求,我们实现这些标准请求即可
     //请求的代码在bRequest中,对不同的请求代码进行散转
     switch(bRequest)
     {
      case CLEAR_FEATURE: //清除特性
       #ifdef DEBUG0
        Prints("清除特性。/r/n");
       #endif
      break;
     
      case SET_ADDRESS:  //设置地址
       #ifdef DEBUG0
        Prints("设置地址。地址为:");
        PrintHex(wValue&0xFF); //显示所设置的地址
        Prints("/r/n");
       #endif
       D12SetAddress(wValue&0xFF); //wValue中的低字节是设置的地址值
       //设置地址没有数据过程,直接进入到状态过程,返回一个0长度的数据包
       SendLength=0;
       NeedZeroPacket=1;
       //将数据通过EP0返回
       UsbEp0SendData();
      break;
     
      case SET_CONFIGURATION: //设置配置
       #ifdef DEBUG0
        Prints("设置配置。/r/n");
       #endif
       //使能非0端点。非0端点只有在设置为非0的配置后才能使能。
       //wValue的低字节为配置的值,如果该值为非0,才能使能非0端点。
       //保存当前配置值
       ConfigValue=wValue&0xFF;
       D12SetEndpointEnable(ConfigValue);
       //返回一个0长度的状态数据包
       SendLength=0;
       NeedZeroPacket=1;
       //将数据通过EP0返回
       UsbEp0SendData();
      break;
     
      case SET_DESCRIPTOR: //设置描述符
       #ifdef DEBUG0
        Prints("设置描述符。/r/n");
       #endif
      break;
     
      case SET_FEATURE: //设置特性
       #ifdef DEBUG0
        Prints("设置特性。/r/n");
       #endif
      break;

      case SET_INTERFACE: //设置接口
       #ifdef DEBUG0
        Prints("设置接口。/r/n");
       #endif
      break;     
     
      default:  //未定义的标准请求
       #ifdef DEBUG0
        Prints("错误:未定义的标准输出请求。/r/n");
       #endif      
      break;
     }
    break;
   
    case 1:  //类请求
     #ifdef DEBUG0
      Prints("USB类输出请求:");
     #endif
     switch(bRequest)
     {
      case SET_CONTROL_LINE_STATE:
       #ifdef DEBUG0
        Prints("SET_CONTROL_LINE_STATE。/r/n");
       #endif
       //该请求没有数据输出阶段,其中wValue字段的D0位表示DTR,
       //D1位表示RTS。但是我们的板上的串口并没有这两引脚,因而
       //对该请求我们仅是简单地返回一个0长度的状态过程数据包即可
       SendLength=0;
       NeedZeroPacket=1;
       //将数据通过EP0返回
       UsbEp0SendData();
      break;
     
      case SET_LINE_CODING:
      //该请求设置串口的属性,但是实际的数据并不在设置过程发出,
      //而是在之后的数据过程发出。这里不用做任何处理,在数据过程
      //完成后返回0长度的状态包。
       #ifdef DEBUG0
        Prints("SET_LINE_CODING。/r/n");
       #endif
       break;
      
      default:
       #ifdef DEBUG0
        Prints("未知请求。/r/n");
       #endif
      break;
     }
    break;
   
    case 2:  //厂商请求
     #ifdef DEBUG0
      Prints("USB厂商输出请求:/r/n");
     #endif
    break;
   
    default: //未定义的请求。这里只显示一个报错信息。
     #ifdef DEBUG0
      Prints("错误:未定义的输出请求。/r/n");
     #endif
    break;
   }
  }
 }
 //普通数据输出
 else //if(D12ReadEndpointLastStatus(0)&0x20)之else
 {
  UsbEp0DataOut();
 }
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:端点0输入中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbEp0In(void)
{
#ifdef DEBUG0
 Prints("USB端点0输入中断。/r/n");
#endif
 //读最后发送状态,这将清除端点0的中断标志位
 D12ReadEndpointLastStatus(1);
 //发送剩余的字节数
 UsbEp0SendData();
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:端点1输出中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbEp1Out(void)
{
#ifdef DEBUG0
 Prints("USB端点1输出中断。/r/n");
#endif
 //读端点最后状态,这将清除端点1输出的中断标志位
 D12ReadEndpointLastStatus(2);
 //清除端点缓冲区
 D12ClearBuffer();
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:端点1输入中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbEp1In(void)
{
#ifdef DEBUG0
 Prints("USB端点1输入中断。/r/n");
#endif
 //读最后发送状态,这将清除端点1输入的中断标志位
 D12ReadEndpointLastStatus(3);
 //端点1输入处于空闲状态
 Ep1InIsBusy=0;
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:端点2输出中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbEp2Out(void)
{
#ifdef DEBUG0
 Prints("USB端点2输出中断。/r/n");
#endif
 //如果缓冲区中的数据还未通过串口发送完毕,则暂时不处理该中断,直接返回。
 if(UsbEp2ByteCount!=0) return;
 
 //读最后接收状态,这将清除端点2输出的中断标志位。
 //注意端点2有个双缓冲机制,在清除中断之前,先检查是否两个缓冲区
 //是否全满了,如果两个缓冲区全满的话,就不用清除中断标志。只有当
 //两个缓冲区不全满的时候才需要清除中断标志。
 if((D12ReadEndpointStatus(4)&0x60)!=0x60)
 {
  D12ReadEndpointLastStatus(4);
 }
 
 //读取端点2的数据。返回值为实际读到的数据字节数
 UsbEp2ByteCount=D12ReadEndpointBuffer(4,BUF_LEN,UsbEp2Buffer);
 //清除端点缓冲区
 D12ClearBuffer();
 
 //输出位置设为0
 UsbEp2BufferOutputPoint=0;
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:端点2输入中断处理函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void UsbEp2In(void)
{
#ifdef DEBUG0
 Prints("USB端点2输入中断。/r/n");
#endif
 //读最后发送状态,这将清除端点2输入的中断标志位
 D12ReadEndpointLastStatus(5);
 //端点2输入处于空闲状态
 Ep2InIsBusy=0;
}
////////////////////////End of function//////////////////////////////

 

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途

        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

PDIUSBD12.C  file

作者:电脑圈圈
建立日期: 2008.06.27
修改日期: 2008.08.05
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#include <AT89x52.H>
#include "MyType.h"
#include "PDIUSBD12.H"
#include "config.h"
#include "UART.h"

/********************************************************************
函数功能:D12写命令。
入口参数:Command:一字节命令。
返    回:无。
备    注:无。
********************************************************************/
void D12WriteCommand(uint8 Command)
{
 D12SetCommandAddr();  //设置为命令地址
 D12ClrWr(); //WR置低
 D12SetPortOut(); //将数据口设置为输出状态(注意这里为空宏,移植时可能有用)
 D12SetData(Command);  //输出命令到数据口上
 D12SetWr(); //WR置高
 D12SetPortIn(); //将数据口设置为输入状态,以备后面输入使用
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:读一字节D12数据。
入口参数:无。
返    回:读回的一字节。
备    注:无。
********************************************************************/
uint8 D12ReadByte(void)
{
 uint8 temp;
 D12SetDataAddr(); //设置为数据地址
 D12ClrRd(); //RD置低
 temp=D12GetData(); //读回数据
 D12SetRd(); //RD置高
 return temp; //返回读到数据
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:读D12的ID。
入口参数:无。
返    回:D12的ID。
备    注:无。
********************************************************************/
uint16 D12ReadID(void)
{
 uint16 id;
 D12WriteCommand(Read_ID); //写读ID命令
 id=D12ReadByte(); //读回ID号低字节
 id|=((uint16)D12ReadByte())<<8; //读回ID号高字节
 return id;
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:写一字节D12数据。
入口参数:Value:要写的一字节数据。
返    回:无。
备    注:无。
********************************************************************/
void D12WriteByte(uint8 Value)
{
 D12SetDataAddr(); //设置为数据地址
 D12ClrWr(); //WR置低
 D12SetPortOut(); //将数据口设置为输出状态(注意这里为空宏,移植时可能有用)
 D12SetData(Value); //写出数据
 D12SetWr(); //WR置高
 D12SetPortIn(); //将数据口设置为输入状态,以备后面输入使用
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:读取D12最后传输状态寄存器的函数。
入口参数:Endp:端点号。
返    回:端点的最后传输状态。
备    注:该操作将清除该端点的中断标志位。
********************************************************************/
uint8 D12ReadEndpointLastStatus(uint8 Endp)
{
 D12WriteCommand(0x40+Endp); //读取端点最后状态的命令
 return D12ReadByte();
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:选择端点的函数,选择一个端点后才能对它进行数据操作。
入口参数:Endp:端点号。
返    回:无。
备    注:无。
********************************************************************/
void D12SelectEndpoint(uint8 Endp)
{
 D12WriteCommand(0x00+Endp); //选择端点的命令
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:清除接收端点缓冲区的函数。
入口参数:无。
返    回:无。
备    注:只有使用该函数清除端点缓冲后,该接收端点才能接收新的数据包。
********************************************************************/
void D12ClearBuffer(void)
{
 D12WriteCommand(D12_CLEAR_BUFFER);
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:应答建立包的函数。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/
void D12AcknowledgeSetup(void)
{
 D12SelectEndpoint(1); //选择端点0输入
 D12WriteCommand(D12_ACKNOWLEDGE_SETUP); //发送应答设置到端点0输入
 D12SelectEndpoint(0); //选择端点0输出
 D12WriteCommand(D12_ACKNOWLEDGE_SETUP); //发送应答设置到端点0输出
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:读取端点缓冲区函数。
入口参数:Endp:端点号;Len:需要读取的长度;Buf:保存数据的缓冲区。
返    回:实际读到的数据长度。
备    注:无。
********************************************************************/
uint8 D12ReadEndpointBuffer(uint8 Endp, uint8 Len, uint8 *Buf)
{
 uint8 i,j;
 D12SelectEndpoint(Endp); //选择要操作的端点缓冲
 D12WriteCommand(D12_READ_BUFFER); //发送读缓冲区的命令
 D12ReadByte();   //该字节数据是保留的,不用。
 j=D12ReadByte(); //这里才是实际的接收到的数据长度
 if(j>Len) //如果要读的字节数比实际接收到的数据长
 {
  j=Len;  //则只读指定的长度数据
 }
#ifdef DEBUG1 //如果定义了DEBUG1,则需要显示调试信息
 Prints("读端点");
 PrintLongInt(Endp/2); //端点号。由于D12特殊的端点组织形式,
                       //这里的0和1分别表示端点0的输出和输入;
                       //而2、3分别表示端点1的输出和输入;
                       //3、4分别表示端点2的输出和输入。
                       //因此要除以2才显示对应的端点。
 Prints("缓冲区");
 PrintLongInt(j);      //实际读取的字节数
 Prints("字节。/r/n");
#endif
 for(i=0;i<j;i++)
 {
  //这里不直接调用读一字节的函数,而直接在这里模拟时序,可以节省时间
  D12ClrRd();  //RD置低
  *(Buf+i)=D12GetData(); //读一字节数据
  D12SetRd();  //RD置高
#ifdef DEBUG1
  PrintHex(*(Buf+i)); //如果需要显示调试信息,则显示读到的数据
  if(((i+1)%16)==0)Prints("/r/n"); //每16字节换行一次
#endif
 }
#ifdef DEBUG1
 if((j%16)!=0)Prints("/r/n"); //换行。
#endif
 return j; //返回实际读取的字节数。
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:使能发送端点缓冲区数据有效的函数。
入口参数:无。
返    回:无。
备    注:只有使用该函数使能发送端点数据有效之后,数据才能发送出去。
********************************************************************/
void D12ValidateBuffer(void)
{
 D12WriteCommand(D12_VALIDATE_BUFFER);
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:将数据写入端点缓冲区函数。
入口参数:Endp:端点号;Len:需要发送的长度;Buf:保存数据的缓冲区。
返    回:Len的值。
备    注:无。
********************************************************************/
uint8 D12WriteEndpointBuffer(uint8 Endp,uint8 Len,uint8 * Buf)
{
 uint8 i;
 D12SelectEndpoint(Endp); //选择端点
 D12WriteCommand(D12_WRITE_BUFFER); //写Write Buffer命令
 D12WriteByte(0); //该字节必须写0
 D12WriteByte(Len);  //写需要发送数据的长度
 
#ifdef DEBUG1 //如果定义了DEBUG1,则需要显示调试信息
 Prints("写端点");
 PrintLongInt(Endp/2); //端点号。由于D12特殊的端点组织形式,
                       //这里的0和1分别表示端点0的输出和输入;
                       //而2、3分别表示端点1的输出和输入;
                       //3、4分别表示端点2的输出和输入。
                       //因此要除以2才显示对应的端点。
 Prints("缓冲区");
 PrintLongInt(Len);    //写入的字节数
 Prints("字节。/r/n");
#endif
 D12SetPortOut(); //将数据口设置为输出状态(注意这里为空宏,移植时可能有用)
 for(i=0;i<Len;i++)
 {
  //这里不直接调用写一字节的函数,而直接在这里模拟时序,可以节省时间
  D12ClrWr(); //WR置低 
  D12SetData(*(Buf+i)); //将数据放到数据线上
  D12SetWr();  //WR置高,完成一字节写
#ifdef DEBUG1
  PrintHex(*(Buf+i));  //如果需要显示调试信息,则显示发送的数据
  if(((i+1)%16)==0)Prints("/r/n"); //每16字节换行一次
#endif
  }
#ifdef DEBUG1
 if((Len%16)!=0)Prints("/r/n"); //换行
#endif
 D12SetPortIn(); //数据口切换到输入状态
 D12ValidateBuffer(); //使端点数据有效
 return Len; //返回Len
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:设置地址函数。
入口参数:Addr:要设置的地址值。
返    回:无。
备    注:无。
********************************************************************/
void D12SetAddress(uint8 Addr)
{
 D12WriteCommand(D12_SET_ADDRESS_ENABLE); //写设置地址命令
 D12WriteByte(0x80 | Addr); //写一字节数据:使能及地址
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:使能端点函数。
入口参数:Enable: 是否使能。0值为不使能,非0值为使能。
返    回:无。
备    注:无。
********************************************************************/
void D12SetEndpointEnable(uint8 Enable)
{
 D12WriteCommand(D12_SET_ENDPOINT_ENABLE);
 if(Enable!=0)
 {
  D12WriteByte(0x01); //D0为1使能端点
 }
 else
 {
  D12WriteByte(0x00); //不使能端点
 }
}
////////////////////////End of function//////////////////////////////

/********************************************************************
函数功能:读取D12端点状态函数。
入口参数:Endp:端点号。
返    回:端点状态寄存器的值。
备    注:无。
********************************************************************/
uint8 D12ReadEndpointStatus(uint8 Endp)
{
 D12WriteCommand(0x80+Endp); //读取端点状态命令
 return D12ReadByte();
}
////////////////////////End of function//////////////////////////////

 

 

 

 

 

 

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途
           
        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

UsbCore.h file

作者:电脑圈圈
建立日期: 2008.06.29
修改日期: 2008.07.10
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/

#ifndef __USBCORE_H__
#define __USBCORE_H__

#define GET_STATUS         0
#define CLEAR_FEATURE      1
#define SET_FEATURE        3
#define SET_ADDRESS        5
#define GET_DESCRIPTOR     6
#define SET_DESCRIPTOR     7
#define GET_CONFIGURATION  8
#define SET_CONFIGURATION  9
#define GET_INTERFACE      10
#define SET_INTERFACE      11
#define SYNCH_FRAME        12

#define DEVICE_DESCRIPTOR         1
#define CONFIGURATION_DESCRIPTOR  2
#define STRING_DESCRIPTOR         3
#define INTERFACE_DESCRIPTOR      4
#define ENDPOINT_DESCRIPTOR       5
#define REPORT_DESCRIPTOR         0x22

#define SET_IDLE 0x0A

#define GET_LINE_CODING         0x21
#define SERIAL_STATE            0x20
#define SET_LINE_CODING         0x20
#define SET_CONTROL_LINE_STATE  0x22
#define SEND_BREAK              0x23

void UsbDisconnect(void); //USB断开连接
void UsbConnect(void);    //USB连接
void UsbBusSuspend(void); //总线挂起中断处理
void UsbBusReset(void);   //总线复位中断处理
void UsbEp0Out(void);     //端点0输出中断处理
void UsbEp0In(void);      //端点0输入中断处理
void UsbEp1Out(void);     //端点1输出中断处理
void UsbEp1In(void);      //端点1输入中断处理
void UsbEp2Out(void);     //端点2输出中断处理
void UsbEp2In(void);      //端点2输入中断处理

extern uint8 ConfigValue;  //当前配置值
extern uint8 Ep1InIsBusy;  //端点1输入是否忙
extern uint8 Ep2InIsBusy;  //端点2输入是否忙

#endif

 

 

 

 

 

/******************************************************************
   本程序只供学习使用,未经作者许可,不得用于其它任何用途

        欢迎访问我的USB专区:http://group.ednchina.com/93/
        欢迎访问我的blog:   http://www.ednchina.com/blog/computer00
                             http://computer00.21ic.org

        感谢PCB赞助商——电子园: http://bbs.cepark.com/

PDIUSBD12.H  file

作者:电脑圈圈
建立日期: 2008.06.27
修改日期: 2008.07.10
版本:V1.1
版权所有,盗版必究。
Copyright(C) 电脑圈圈 2008-2018
All rights reserved           
*******************************************************************/


#ifndef __PDIUSBD12_H__
#define __PDIUSBD12_H__

#include <at89x52.h>
#include "MyType.h"

//命令地址和数据地址
#define D12_COMMAND_ADD           1
#define D12_DATA_ADD              0

//PDIUSBD12芯片连接引脚
#define D12_DATA                  P0
#define D12_A0                    P3_5
#define D12_WR                    P3_6
#define D12_RD                    P3_7
#define D12_INT                   P3_2

//选择命令或数据地址
#define D12SetCommandAddr() D12_A0=D12_COMMAND_ADD
#define D12SetDataAddr()    D12_A0=D12_DATA_ADD
//WR控制
#define D12SetWr() D12_WR=1
#define D12ClrWr() D12_WR=0
//RD控制
#define D12SetRd() D12_RD=1
#define D12ClrRd() D12_RD=0
//获取中断引脚电平状态
#define D12GetIntPin()  D12_INT
//读写数据
#define D12GetData() D12_DATA
#define D12SetData(Value) D12_DATA=(Value)

//将数据口设置为输入状态,51单片机端口写1就是为输入状态
#define D12SetPortIn() D12_DATA=0xFF

//将数据口设置为输出状态,由于51单片机是准双向IO口,所以不用切换,为空宏
#define D12SetPortOut()

//D12的读ID命令
#define Read_ID  0xFD

//D12的设置模式命令
#define D12_SET_MODE  0xF3

//D12的读中断寄存器命令
#define READ_INTERRUPT_REGISTER  0xF4

//D12读端点缓冲区的命令
#define D12_READ_BUFFER 0xF0

//D12写端点缓冲区的命令
#define D12_WRITE_BUFFER 0xF0

//D12清除接收端点缓冲区的命令
#define D12_CLEAR_BUFFER    0xF2

//D12使能发送端点缓冲区的命令
#define D12_VALIDATE_BUFFER 0xFA

//D12的应答设置包命令
#define D12_ACKNOWLEDGE_SETUP 0xF1

//D12的设置地址/使能命令
#define D12_SET_ADDRESS_ENABLE 0xD0

//D12的使能端点命令
#define D12_SET_ENDPOINT_ENABLE 0xD8

//函数声明
void D12WriteCommand(uint8);
uint8 D12ReadByte(void);
uint16 D12ReadID(void);
void D12WriteByte(uint8);
void DelayXms(uint16);
uint8 D12ReadEndpointBuffer(uint8 Endp, uint8 Len, uint8 *Buf);
uint8 D12WriteEndpointBuffer(uint8 Endp, uint8 Len, uint8 *Buf);
void D12ClearBuffer(void);
uint8 D12ReadEndpointLastStatus(uint8 Endp);
void D12AcknowledgeSetup(void);
void D12SetAddress(uint8 Addr);
void D12SetEndpointEnable(uint8 Enable);
uint8 D12ReadEndpointStatus(uint8 Endp);

#endif

 

 

 

 

原创粉丝点击