/******************************************************************
本程序只供学习使用,未经作者许可,不得用于其它任何用途
欢迎访问我的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