请问如何单片机通过usb协议发送数据控制电脑里面打开的ppt。采用的是CH375B作为USB接口芯片.内置固件的模式。 问题一:我不知道单片机发送的数据如何使电脑产生相应的反应。例如当你打开ppt后,是单片机通过usb协议发送ALT+F5的键值他就能关闭ppt吗,发送pagedown的键值他就会往下翻页吗?还是要编写上位机程序。(一般的实例中都是单片机发送数据,让上位机显示。) 问题二:有没有关于此项目的相关资料,其实主要问题还是单片机模拟usb键盘,有没有相关的usb键盘工作原理的资料,或者此工程的资料,分享一下。谢谢!
你要实现的就是使用CH372/375模拟一个键盘就可以了,当然需要使用CH372/375的外置固件模式,到下面的地址中下载事例程序 /bbs/View.asp?T=1&S=101&I=19463
我想采用内置固件减少编程量,难道不可以吗?一定要采用外置固件吗?请高手帮我解答一下,谢谢。
一定要使用外置固件,因为内置固件是无法模拟出键盘的。
如果采用1楼_study_的资料(如下),用外置固件,发送pagedown的键值就能使打开的ppt翻页吗?
375、372外置固件模拟键盘鼠标的例子程序: UploadImages/200842213304565.rar
首先一个问题就是你使用普通的键盘使用pagedown这个键就可以实现翻页吗?如果可以的话,那么使用CH372发送这个键值也是可以实现的,CH372模拟的键盘和普通键盘完全一样。
/*;CH372/CH375 USB device mode & external firmware ; U2(AT89C51) Program ; ; Website: http://winchiphead.com ; Email: 个人信息保护,已隐藏 ; Author: W.ch 2003.12, 2005.03 ; ;**************************************************************************** 关于程序的说明: 模拟键盘程序,主机枚举好后,发送K,可以在TXT文档中显示出来。作为演示,需要其他键值可以自己修改,如果不是连续发数据的 话,一般发送完毕键值后发送一包00,主机认为发送结束 */
/* MCS-51单片机C语言的示例程序 */ #include #include "CH375INC.H"
unsigned char idata buf[8]; typedef union _REQUEST_PACK{ unsigned char buffer[8]; struct{ unsigned char bmReuestType; //标准请求字 unsigned char bRequest; //请求代码 unsigned int wValue; //特性选择高 unsigned int wIndx; //索引 unsigned int wLength; //数据长度 }r; } mREQUEST_PACKET, *mpREQUEST_PACKET;
//设备描述符 unsigned char code DevDes[]={ 0x12 //描述符大小 , 0x01 //常数DEVICE , 0x10 //USB规范版本信息 , 0x01 , 0x00 //类别码, , 0x00 //子类别码 , 0x00 //协议码 , 0x08 //端点0的最大信息包大小 , 0x3c //厂商ID , 0x41 , 0x03 //产品ID , 0x20 , 0x00 //设备版本信息 , 0x02 , 0x01 //索引值 , 0x02 , 0x00 , 0x01 //可能配置的数目 }; //配置描述符 unsigned char code ConDes[]={ //配置描述符 0x09, 0x02, 0x22, 0x00, 0x01, 0x01, 0x04, 0xa0, 0x23,//配置描述符 0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x01, 0x05,//接口描述符 0x09, 0x21, 0x10, 0x01, 0x00, 0x01, 0x22, 0x41, 0x00,//HID类描述符 0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0x18 //端点描述符,只配置了输入端点,输出端点用0端点 }; //配置描述符
/*报表描述符*/ unsigned char code Hid_des[]={0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x03, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, 0x02, 0x95, 0x01, 0x75, 0x05, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00, 0xc0 }; /*关于全局变量的定义*/ unsigned char mVarSetupRequest; // ;USB请求码 unsigned char mVarSetupLength; // 后续数据长度 unsigned char code * VarSetupDescr; //描述符偏移地址 unsigned char VarUsbAddress ; sbit CH375_INT_WIRE = 0xB0^2; //P3.2, INT0, 连接CH375的INT#引脚,用于查询中断状态 */ sbit CH375ACT = P1^4; bit CH375FLAGERR; //错误清0 bit CH375CONFLAG; unsigned char idata UPDATA_FLAG; mREQUEST_PACKET request;
/*硬件定义,根据硬件修改*/ unsigned char volatile xdata CH375_CMD_PORT _at_ 0XB1FF;//0xBDF1; /* CH375命令端口的I/O地址 */ unsigned char volatile xdata CH375_DAT_PORT _at_ 0XB0FF;//0xBCF0; /* CH375数据端口的I/O地址 */
/* 延时2微秒,不精确 */ void Delay1us() { ; }
void Delay2us( ) { unsigned char i; #define DELAY_START_VALUE 0 /* 根据单片机的时钟选择初值,20MHz以下为0,30MHz以上为2 */ for ( i=DELAY_START_VALUE; i!=0; i-- ); }
/* 延时50毫秒,不精确 */ void Delay50ms( ) { unsigned char i, j; for ( i=200; i!=0; i-- ) for ( j=250; j!=0; j-- ); }
void CH375_WR_CMD_PORT( unsigned char cmd ) { /* 向CH375的命令端口写入命令,周期不小于4uS,如果单片机较快则延时 */ CH375_CMD_PORT=cmd; Delay2us( ); }
void CH375_WR_DAT_PORT( unsigned char dat ) { /* 向CH375的数据端口写入数据,周期不小于1.5uS,如果单片机较快则延时 */ CH375_DAT_PORT=dat; Delay1us(); /* 因为MCS51单片机较慢所以实际上无需延时 */ }
unsigned char CH375_RD_DAT_PORT() { /* 从CH375的数据端口读出数据,周期不小于1.5uS,如果单片机较快则延时 */ Delay1us( ); /* 因为MCS51单片机较慢所以实际上无需延时 */ return( CH375_DAT_PORT );
} /* CH375初始化子程序 */ void CH375_Init( ) { /* 设置USB工作模式, 必要操作 */ CH375_WR_CMD_PORT( CMD_SET_USB_MODE ); CH375_WR_DAT_PORT( 1 ); /* 设置为使用内置固件的USB设备方式 */ for ( ;; ) { /* 等待操作成功,通常需要等待10uS-20uS */ if ( CH375_RD_DAT_PORT( )==CMD_RET_SUCCESS )P1=0XAA;Delay50ms( );P1=0XFF; break;
} }
//端点0数据上传 void mCh375Ep0Up() { unsigned char i,len; if(mVarSetupLength) { //长度不为0传输具体长度的数据 if(mVarSetupLength<=8) { len=mVarSetupLength; mVarSetupLength=0; } //长度小于8则长输要求的长度 else { len=8; mVarSetupLength-=8; } //长度大于8则传输8个,切总长度减8 CH375_WR_CMD_PORT(CMD_WR_USB_DATA3); //发出写端点0的命令 CH375_WR_DAT_PORT(len); //写入长度 for(i=0;i!=len;i++) CH375_WR_DAT_PORT(request.buffer[i]); //循环写入数据 } else { CH375_WR_CMD_PORT(CMD_WR_USB_DATA3); //发出写端点0的命令 CH375_WR_DAT_PORT(0); //上传0长度数据,这是一个状态阶段 } }
//复制描述符以便上传 void mCh375DesUp() { unsigned char k; for (k=0; k!=8; k++ ) { request.buffer[k]=*VarSetupDescr; //依次复制8个描述符, VarSetupDescr++; } }
/*中断函数,但是本例用的是查询方式,只处理了输入端点1和端点0的事件,其余不处理*/ void mCH375Interrupt( ) { unsigned char InterruptStatus; unsigned char length, c1, len; CH375_WR_CMD_PORT(CMD_GET_STATUS); /* 获取中断状态并取消中断请求 */ InterruptStatus =CH375_RD_DAT_PORT(); /* 获取中断状态 */ switch(InterruptStatus){ // 分析中断状态 case USB_INT_EP1_OUT: //中断端点下传成功,未处理 CH375_WR_CMD_PORT(CMD_RD_USB_DATA); //发出读数据命令 if( length=CH375_RD_DAT_PORT( ) ) { //长度为0跳出 for(len=0;len!=length;len++)c1=CH375_RD_DAT_PORT(); //取出下传数据 } break; case USB_INT_EP0_SETUP: //控制端点建立成功 CH375_WR_CMD_PORT(CMD_RD_USB_DATA); length=CH375_RD_DAT_PORT(); for(len=0;len!=length;len++)request.buffer[len]=CH375_RD_DAT_PORT(); // 取出数据 if(length==0x08){ mVarSetupLength=request.buffer[6]; //控制传输数据长度最大设置为128 if((c1=request.r.bmReuestType)&0x40){ //厂商请求,未处理 } if((c1=request.r.bmReuestType)&0x20) { //类请求,未处理 if(request.buffer[1]==0x0a) { } //SET_IDLE else if(request.buffer[1]==0x09) { UPDATA_FLAG=1; } } if(!((c1=request.r.bmReuestType)&0x60)) { //标准请求 mVarSetupRequest=request.r.bRequest; //暂存标准请求码 switch(request.r.bRequest){ // 分析标准请求 case DEF_USB_CLR_FEATURE: //清除特性 if((c1=request.r.bmReuestType&0x1F)==0X02) { //不是端点不支持 switch(request.buffer[4]) { case 0x82: CH375_WR_CMD_PORT(CMD_SET_ENDP7); //清除端点2上传 CH375_WR_DAT_PORT(0x8E); //发命令清除端点 break; case 0x02: CH375_WR_CMD_PORT(CMD_SET_ENDP6); CH375_WR_DAT_PORT(0x80); //清除端点2下传 break; case 0x81: CH375_WR_CMD_PORT(CMD_SET_ENDP5); //清除端点1上传 CH375_WR_DAT_PORT(0x8E); break; case 0x01: CH375_WR_CMD_PORT(CMD_SET_ENDP4); //清除端点1下传 CH375_WR_DAT_PORT(0x80); break; default: break; } } else { CH375FLAGERR=1; //不支持的清除特性,置错误标志 } break; case DEF_USB_GET_STATUS: //获得状态 request.buffer[0]=0; request.buffer[1]=0; //上传状态 break; case D
你模拟的是键盘鼠标,你用的软件是CH372DRV驱动的测试程序,那是可以那才怪呢。 简单的说,你写了一个对U盘读写的应用程序,这是基于U盘驱动的,而你现在插一个鼠标,你的应用程序能读写这个鼠标吗??、
谢谢红桃六大哥的提点。刚开始接触这东西,还有点不知所措,还是小菜鸟。
请问我该选择什么驱动呢?到哪里下载?
我在网上看到说用HID设备枚举可以不用驱动。我在网上找到一个程序,这种使用HID枚举的方式吗?程序如下:
/*;CH372/CH375 USB device mode & external firmware ; U2(AT89C51) Program ; Website: http://winchiphead.com ; Email: 个人信息保护,已隐藏 ; Author: W.ch 2003.12, 2005.03 ;**************************************************************************** CH375 外部固件方式范例 ;此程序为USB设备,主机为计算机;主机要识别设备的类型、完成与设备的通讯必须先获取设备的描述符(用一组数据描述了 ;设备的详细内容), /* MCS-51单片机C语言的示例程序 */ #pragma NOAREGS #include #include "CH375INC.H" #include "stdio.h" unsigned char idata buf[64]; typedef union _REQUEST_PACK{ unsigned char buffer[8]; struct{ unsigned char bmReuestType; //标准请求字 unsigned char bRequest; //请求代码 unsigned int wValue; //特性选择高 unsigned int wIndx; //索引 unsigned int wLength; //数据长度 }r; } mREQUEST_PACKET, *mpREQUEST_PACKET; //设备的描述符包含设备描述符、配置描述符、字符描述符;识别描述符的类型就是根据它的第二个字节值来识别; //一般情况下设备描述符不需要用户去配置,因为可以直接用软件从现有的设备中读取;同种设备的描述符都相同;如优盘描述符都一样 /*以下是设备描述符,(为什么是这些数值,可查阅USB协议书籍)*/ unsigned char code DevDes[]={ 0x12 //描述符大小,即此数组的长度 ,0x01 //常数表示描述符的类型;(数值为0x01表示设备描述符) ,0x10 ,0x01 //USB规范版本信息;2个字节;0x10、0x01表示USB1.1;USB2.0为0X00、0X02; ,0x00 //类别码,数值0X01~0XFE为USB定义的类,如打印机等,0XFF是由厂商指定的类别;0为HID类,它的类别在配置描述符中的接口描述符指定; ,0x00 //子类别码;这些码值的具体含义根据bDeviceClass 域来看如bDeviceClass 域为零此域也须为零;如bDeviceClass 域为FFH此域的所有值保留 ,0x00 //协议码 ,0x08 //端点0的最大信息包大小 ,0x31 //厂商ID ,0x51 ,0x07 //产品ID ,0x20 ,0x00 //设备版本信息 ,0x03 ,0x01 //索引值 ,0x02 ,0x00 ,0x01 //可能配置的数目 }; //配置描述符;包括配置描述符、接口描述符、端点描述符,HID设备还包括HID描述符; unsigned char code ConDes[]={ 0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0xa0, 0x23,//配置描述符;第二字节为0X02 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x05,//接口描述符;第二字节为0X04 0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0x22, 0x00,//HID描述符;第二字节为0X21 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x18, //端点描述符;第二字节为0X05 0x07, 0x05, 0x02, 0x03, 0x40, 0x00, 0x18 //端点描述符; }; //字符串描述符,报表描述符; //作键盘设备时,报表描述符描述了数据格式;如键盘,则必须发送8个字节的数据;其中第3个字节是要在 //各种文本里显示的数据(如WORD文挡);其余字节全为0; unsigned char code Hid_des[]={ 0x06, 0x00,0xff, 0x09, 0x01, 0xa1, 0x01, //集合开始 0x09, 0x02, //Usage Page 用法 0x15, 0x00, //Logical Minimun 0x26, 0x00,0xff, //Logical Maximun 0x75, 0x08, //Report Size 0x95, 0x40, //Report Counet 0x81, 0x06, //Input 0x09, 0x02, //Usage Page 用法 0x15, 0x00, //Logical Minimun 0x26, 0x00,0xff, //Logical Maximun 0x75, 0x08, //Report Size 0x95, 0x40, //Report Counet 0x91, 0x06, //Output
0xC0 }; unsigned char code LangDes[]={0x04,0x03,0x09,0x04}; //语言描述符 unsigned char code SerDes[]={0x28,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x43,0x00,0x42 ,0x00,0x43,0x00,0x31,0x00,0x00,0x00,0x00,0x00,0x00 }; //字符串描述符
unsigned char mVarSetupRequest; // ;USB请求码 unsigned char mVarSetupLength; // ;后续数据长度 unsigned char code * VarSetupDescr;// ;描述符偏移地址 unsigned char buf1[8]; unsigned char VarUsbAddress ; // unsigned char Report_ID=0X01;
sbit key = P3^3; sbit CS = P3^0; sbit WR_DAT = P3^4; sbit RD_DAT = P3^5; sbit A0 = P3^7; sbit CH375_INT_WIRE = P3^2; /* P3.2, INT0, 连接CH375的INT#引脚,用于查询中断状态 */ bit CH375FLAGERR; //错误清0 bit CH375CONFLAG; unsigned char flag=0; mREQUEST_PACKET request;
unsigned char volatile xdata CH375_CMD_PORT _at_ 0xBDF1; /* CH375命令端口的I/O地址 */ unsigned char volatile xdata CH375_DAT_PORT _at_ 0xBCF0; /* CH375数据端口的I/O地址 */ void mInitSTDIO( ) { SCON = 0x50; PCON = 0x80; TMOD = 0x20; TH1 = 0xf3; /* 24MHz晶振, 9600bps */ TR1 = 1; TI = 1; } void delay10ms() { unsigned char i,j; for(i=10;i!=0;i--) for(j=100;j!=0;j--) ; }
void Delay1us(){;} /* 延时2微秒,不精确 */ void Delay2us( ) { unsigned char i; #define DELAY_START_VALUE 1 /* 根据单片机的时钟选择初值,20MHz以下为0,30MHz以上为2 */ for ( i=DELAY_START_VALUE; i!=0; i-- ); } /* 延时50毫秒,不精确 */ void Delay50ms( ) { unsigned char i, j; for ( i=200; i!=0; i-- ) for ( j=250; j!=0; j-- ); } /*以下是带P0、P1、P2口的单片机外部读写方式、地址的确定和外部扩展I/O口地址一样,见硬件电路*/ void CH375_WR_CMD_PORT( unsigned char cmd ) { //向CH375的命令端口写入命令,周期不小于4uS,如果单片机较快则延时 //delay2us(); CH375_CMD_PORT=cmd; Delay2us( ); }
void CH375_WR_DAT_PORT( unsigned char dat ) { // 向CH375的数据端口写入数据,周期不小于1.5uS,如果单片机较快则延时 CH375_DAT_PORT=dat; Delay1us(); // 因为MCS51单片机较慢所以实际上无需延时 }
unsigned char CH375_RD_DAT_PORT() { //从CH375的数据端口读出数据,周期不小于1.5uS,如果单片机较快则延时 Delay1us( ); // 因为MCS51单片机较慢所以实际上无需延时 return( CH375_DAT_PORT );
}
/* CH375初始化子程序; */ void CH375_Init( ) { /* 设置USB工作模式, 必要操作 */ CH375_WR_CMD_PORT( CMD_SET_USB_MODE );//向数据端口写设置CH375命令的数据CMD_SET_USB_MODE,数值大小在头文件中有定义,说明书中有规定值 CH375_WR_DAT_PORT( 1 ); /* 0X01设置为使用内置固件的USB设备方式,说明书中有定义 */ // for ( ;; ) { /* 等待操作成功,通常需要等待10uS-20uS */ // if ( CH375_RD_DAT_PORT( )==CMD_RET_SUCCESS ) break; // } } //串口发送程序,用于监视程序运行情况; void Uart_send( unsigned char i ) { TI=0; SBUF=i; while(TI==0); TI=0; } //********************************************************* //端点0数据上传;CH375有端点0、1、2等缓冲区与计算机通讯,个缓冲区大小不一样;详细见CH375说明书; void mCh375Ep0Up(){ unsigned char i,len; if(mVarSetupLength){ //长度不为0传输具体长度的数据 if(mVarSetupLength<=8){ len=mVarSetupLength; mVarSetupLength=0; } //长度小于8则长输要求的长度 else{ len=8; mVarSetupLength-=8; } //长度大于8则传输8个,切总长度减8 CH375_WR_CMD_PORT(CMD_WR_USB_DATA3); //发出写端点0的命令 CH375_WR_DAT_PORT(len); //写入长度 for(i=0;i!=len;i++) CH375_WR_DAT_PORT(request.buffer[i]); //循环写入数据 } else{ CH375_WR_CMD_PORT(CMD_WR_USB_DATA3); //发出写端点0的命令 CH375_WR_DAT_PORT(0); //上传0长度数据,这是一个状态阶段
} } //********************************************************* //复制描述符以便上传 void mCh375DesUp(){ unsigned char k; for (k=0; k!=8; k++ ) { request.buffer[k]=*VarSetupDescr; //依次复制8个描述符, VarSetupDescr++; } } /* CH375中断服务程序INT0,使用寄存器组1 */ void mCH375Interrupt( ) //interrupt 0 using 1 { unsigned char InterruptStatus; unsigned char length, c1, len; unsigned char mBuf[8]; CH375_WR_CMD_PORT(CMD_GET_STATUS); /* 向CH375芯片发送获取中断状态的命令;CMD_GET_STATUS为命令码,数值大小在CH375INC.H 头文件中*/ InterruptStatus =CH375_RD_DAT_PORT(); /* 读取中断状态 */ switch(InterruptStatus){ // 分析中断状态, case USB_INT_EP2_OUT: // 批量端点下传成功 C
你还是没理解我给你说的 你就是做一个键盘,测试很简单,你打开一个TXT文件,在你的硬件上做一个按键,按一个键传一个键码,不就可以测试出来了吗?
我是按你说的方法进行测试的。开始的测试只是测试电路是不是正确的,驱动是CH372DRV,单片机里面的程序我没有贴出来,是另外的程序。 为了不让大家误解,我卸载了CH37DRV.让后把上面8楼的程序烧到单片机里面。 当我重新把CH375通过USB线和电脑连接起来,发现单片机接收4次中断,都是0x07,也就是总线复位,再过5秒钟左右又开始中断2次,再过大约5秒钟又总线复位2次,再中断2次,再过大约5秒钟又总线复位2次。再过大约5秒钟设备管理器中通用串行总线控制器目录下出现未知设备(Unknow Device)。 Unknow Device没有驱动,我把这个设备卸载之后,再重新连接一下,依然会出现上述现象,但不同的是,最后电脑屏幕右下方发现新硬件,最后还出现硬件可以使用。
首先为什么总是总线复位呢? 然后为什么硬件可以使用?