我现在可以用CH375B能读出键盘的8个字节数据,但是连续敲击键盘时候,总是丢失一些按键值,程序如下: /**************************************** ** Copyright (C) W.ch 1999-2004 ** ** Web: http://www.winchiphead.com ** **************************************** ** USB 1.1 Host Examples for CH375 ** ** KC7.0@MCS-51 ** **************************************** */ /* 用CH375操作HID设备,比如键盘,鼠标*/
#include #include #include #include #include "CH375INC.H" #include"USART.h" #include"ch375_host_int_para.h" #define TEST_LOW_SPEED 1 //unsigned char volatile xdata CH375_CMD_PORT _at_ 0xBDF1; /* CH375命令端口的I/O地址 */ //unsigned char volatile xdata CH375_DAT_PORT _at_ 0xBCF0; /* CH375数据端口的I/O地址 */ //sbit CH375_INT_WIRE = 0xB0^2; /* P3.2, INT0, 连接CH375的INT#引脚,用于查询中断状态 */ //sbit P1_0=P1^0; //sbit P1_2=P1^2; #define CH375_INT_WIRE (PINE&0x40)
#define TRUE 1 #define FALSE 0 unsigned char endp_int; //中断端点号 unsigned char num_interfaces; //接口数 unsigned char config_value; //配置值 unsigned char report_descr_len=0;//REPORT描述符长度 unsigned char flag_config_2=0; //第二次获取描述符标志位 unsigned char flag_interface_2=0; //多个接口标志位 unsigned char endp6_mode=0x80, endp7_mode=0x80;//同步标志位初值 unsigned char status=0xff; //全局状态 unsigned char idata, data_buf[96];//描述符缓冲区可以适当减小 union _REQUEST //请求包结构 { struct { unsigned char bmRequestType; unsigned char bRequest; unsigned int wValue; unsigned int wIndex; unsigned int wLength; }Req; unsigned char Req_buf[8]; }Request; unsigned char report_cou=0; //REPORT描述符长度计数
volatile unsigned char flag_output=0; //串口输出标志位 volatile unsigned char CH375_IN_BUFFER[96]; volatile unsigned char CH375_IN_BUFFER_Count;
unsigned char xdata ,data_in[1000];//串口输出缓冲区 unsigned char keyBoardIntit();
void delay2us( ) { _delay_us(2); } void delay1us( ) { _delay_us(1); } void delayms(unsigned char count) { unsigned char i; for(i=0;i} void mInitSTDIO( ) { PORTD|=0X70;//PD4=CS;PD5=WR;PD6=RD;PD7=A0; DDRD |=0xf0; PORTD=0xff; DDRB=0x00;
DDRE |=(1<PORTE &=~(1< DDRE&=~(1<PORTE |=(1< EICRB=0X00; EIMSK &=~(1<cli();
}
void CH375_WR_CMD_PORT( unsigned char cmd ) { /* 向CH375的命令端口写入命令,周期不小于4uS,如果单片机较快则延时 */ delay2us(); PORTD|=(1< PORTB=cmd; DDRB=0XFF; PORTD&=~(1<PORTD&=~(1< //PORTD|=(1< DDRB=0XFF; //该操作无意义,仅作延时,CH375要求读写脉冲宽度大于100nS PORTD|=0Xf0;//输出无效的控制信号, 完成操作CH375芯片, A0=1; CS=1; WR=1; RD=1; DDRB=0X00; /* 禁止数据输出 */ PORTD&=~(1<delay2us(); }
void CH375_WR_DAT_PORT( unsigned char dat ) { /* 向CH375的数据端口写入数据,周期不小于1.5uS,如果单片机较快则延时 */ PORTB=dat; DDRB=0XFF; PORTD&=0X4F;/* 输出有效写控制信号, 写CH375芯片的数据端口, A0=0; CS=0; WR=0; RD=1; */ DDRB=0XFF; //该操作无意义,仅作延时,CH375要求读写脉冲宽度大于100nS PORTD|=0Xf0;//输出无效的控制信号, 完成操作CH375芯片, A0=1; CS=1; WR=1; RD=1; DDRB=0X00; /* 禁止数据输出 */ delay1us(); }
unsigned int CH375_RD_DAT_PORT() { /* 从CH375的数据端口读出数据,周期不小于1.5uS,如果单片机较快则延时 */ unsigned int temp; delay1us(); DDRB=0X00; PORTD&=0X2F;/* 输出有效读控制信号, 读CH375芯片的数据端口, A0=0; CS=0; WR=1; RD=0; */ DDRB=0X00; //该操作无意义,仅作延时,CH375要求读写脉冲宽度大于100nS temp=PINB; PORTD|=0Xf0;//输出无效的控制信号, 完成操作CH375芯片, A0=1; CS=1; WR=1; RD=1; return(temp); }
unsigned char set_usb_mode( unsigned char mode ) { /* 设置CH37X的工作模式 */ unsigned char i; CH375_WR_CMD_PORT( CMD_SET_USB_MODE ); CH375_WR_DAT_PORT( mode ); endp6_mode=endp7_mode=0x80; /* 主机端复位USB数据同步标志 */ for( i=0; i!=100; i++ ) { /* 等待设置模式操作完成,不超过30uS */ if ( CH375_RD_DAT_PORT()==CMD_RET_SUCCESS ) return( TRUE ); /* 成功 */ } return( FALSE ); /* CH375出错,例如芯片型号错或者处于串口方式或者不支持 */ }
void set_freq(void) { CH375_WR_CMD_PORT(0x0b); /* 切换使375B进入低速模式 */ CH375_WR_DAT_PORT(0x17); CH375_WR_DAT_PORT(0xd8); }
/* 数据同步 */ /* USB的数据同步通过切换DATA0和DATA1实现: 在设备端, CH372/CH375可以自动切换; 在主机端, 必须由SET_ENDP6和SET_ENDP7命令控制CH375切换DATA0与DATA1. 主机端的程序处理方法是为SET_ENDP6和SET_ENDP7分别提供一个全局变量, 初始值均为80H, 每执行一次成功事务后将位6取反, 每执行一次失败事务后将其复位为80H. */
void toggle_recv() { /* 主机接收成功后,切换DATA0和DATA1实现数据同步 */ CH375_WR_CMD_PORT( CMD_SET_ENDP6 ); CH375_WR_DAT_PORT( endp6_mode ); endp6_mode^=0x40; delay2us(); }
void toggle_send() { /* 主机发送成功后,切换DATA0和DATA1实现数据同步 */ CH375_WR_CMD_PORT( CMD_SET_ENDP7 ); CH375_WR_DAT_PORT( endp7_mode ); endp7_mode^=0x40; delay2us(); }
//void clr_stall6() { /* 主机接收失败后,复位设备端的数据同步到DATA0 */ // CH375_WR_CMD_PORT( CMD_CLR_STALL ); // CH375_WR_DAT_PORT( 2 | 0x80 ); /* 如果设备端不是CH37X芯片,那么需要修改端点号 */ // endp6_mode=0x80; // status=0xff; //}
//void clr_stall7() { /* 主机发送失败后,复位设备端的数据同步到DATA0 */ // CH375_WR_CMD_PORT( CMD_CLR_STALL ); // CH375_WR_DAT_PORT( 2 ); /* 如果设备端不是CH37X芯片,那么需要修改端点号 */ // endp7_mode=0x80; // status=0xff; //}
unsigned char rd_usb_data( unsigned char *buf ) { /* 从CH37X读出数据块 */ unsigned char i, len; CH375_WR_CMD_PORT( CMD_RD_USB_DATA ); /* 从CH375的端点缓冲区读取接收到的数据 */ len=CH375_RD_DAT_PORT(); /* 后续数据长度 */ for ( i=0; i!=len; i++ ) *buf++=CH375_RD_DAT_PORT(); return( len ); }
void wr_usb_data( unsigned char len, unsigned char *buf ) { /* 向CH37X写入数据块 */ CH375_WR_CMD_PORT( CMD_WR_USB_DATA7 ); /* 向CH375的端点缓冲区写入准备发送的数据 */ CH375_WR_DAT_PORT( len ); /* 后续数据长度, len不能大于64 */ while( len-- ) CH375_WR_DAT_PORT( *buf++ ); }
void issue_token( unsigned char endp_and_pid ) { /* 执行USB事务 */ CH375_WR_CMD_PORT( CMD_ISSUE_TOKEN ); CH375_WR_DAT_PORT( endp_and_pid ); /* 高4位目的端点号, 低4位令牌PID */ status=0xff; }
ISR(INT6_vect) { unsigned char len_temp,i; CH375_IN_BUFFER_Count=0; CH375_WR_CMD_PORT( CMD_GET_STATUS ); /* 产生操作完成中断, 获取中断状态 */ UDR1=status=CH375_RD_DAT_PORT(); if(status!=USB_INT_SUCCESS)//&&((status&0xf0)==0x20)) { CH375_WR_CMD_PORT( CMD_CLR_STALL ); CH375_WR_DAT_PORT(1); /* 如果设备端不是CH37X芯片,那么需要修改端点号 */ endp6_mode=0x80; endp7_mode=0x80; toggle_recv(); issue_token(( endp_int << 4 ) | DEF_USB_PID_IN);//发送从中断端点读数据的令牌 } else { len_temp=rd_usb_data(data_buf); //键盘中断端点数据长度一般为8字节,鼠标为4字节 for(i=0;i!=len_temp;i++) {data_in[i]=data_buf[i]; CH375_IN_BUFFER[CH375_IN_BUFFER_Count++]=data_buf[i]; } // while( !( UCSR1A & (1< //UDR1= data_buf[2]; flag_output=1; toggle_recv(); issue_token(( endp_int << 4 ) | DEF_USB_PID_IN);//发送从中断端点读数据的令牌 } } unsigned char wait_interrupt() { /* 主机端等待操作完成, 返回操作状态 */ while( CH375_INT_WIRE ); /* 查询等待CH375操作完成中断(INT#低电平) */ CH375_WR_CMD_PORT( CMD_GET_STATUS ); /* 产生操作完成中断, 获取中断状态 */ return( CH375_RD_DAT_PORT() ); }
void set_retry(unsigned char num) { CH375_WR_CMD_PORT( CMD_SET_RETRY); CH375_WR_DAT_PORT( 0x25); CH375_WR_DAT_PORT( num); delay2us(); }
unsigned char set_config_ex() { Request.Req.bmRequestType=0x00; Request.Req.bRequest=0x09;config_value=config_value; Request.Req.wValue=0x0000|config_value; Request.Req.wIndex=0x0000; Request.Req.wLength=0x0000; endp7_mode=0x80; toggle_send(); wr_usb_data(8,Request.Req_buf);/* SETUP数据总是8字节 */ issue_token(( 0 << 4 ) | DEF_USB_PID_SETUP);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* SETUP阶段操作成功 */ { endp6_mode=0xc0; toggle_recv(); issue_token(( 0 << 4 ) | DEF_USB_PID_IN);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* 状态阶段操作成功 */ { if(rd_usb_data(data_buf)!=0) return(0); } else return(0); } else return(0); return(1) ; } unsigned char set_idle() { Request.Req.bmRequestType=0x21; Request.Req.bRequest=0x0a; Request.Req.wValue=0x0000; Request.Req.wIndex=0x0000;if(flag_interface_2==1)Request.Req.wIndex=0x0100; Request.Req.wLength=0x0000; endp7_mode=0x80; toggle_send(); wr_usb_data(8,Request.Req_buf);/* SETUP数据总是8字节 */ issue_token(( 0 << 4 ) | DEF_USB_PID_SETUP);status=wait_interrupt(); if(status==USB_INT_SUCCESS) /* SETUP阶段操作成功 */ { endp6_mode=0xc0; toggle_recv(); issue_token(( 0 << 4 ) | DEF_USB_PID_IN );status=wait_interrupt(); if(status==USB_INT_SUCCESS) /* 状态阶段操作成功 */ { if(rd_usb_data(data_buf)!=0) return(0); } else return(0); } else return(0); return(1); } unsigned char set_report() { Request.Req.bmRequestType=0x21; Request.Req.bRequest=0x09; Request.Req.wValue=0x0200; Request.Req.wIndex=0x0000; Request.Req.wLength=0x0001; endp7_mode=0x80; toggle_send(); wr_usb_data(8,Request.Req_buf);/* SETUP数据总是8字节 */ issue_token(( 0 << 4 ) | DEF_USB_PID_SETUP);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* SETUP阶段操作成功 */ { toggle_send(); Request.Req_buf[0]=0x01; wr_usb_data(1,Request.Req_buf); issue_token(( 0 << 4 ) | DEF_USB_PID_OUT);status=wait_interrupt(); if(status==USB_INT_SUCCESS) /* DATA阶段操作成功 */ { endp6_mode=0xc0; toggle_recv(); issue_token(( 0 << 4 ) | DEF_USB_PID_IN);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* 状态阶段操作成功 */ { if(rd_usb_data(data_buf)!=0) return(0); } else return(0); } else return(0); } else return(0); return(1); } unsigned char get_descr_ex() { unsigned char descr_len; unsigned char *p=data_buf; endp7_mode=0x80; toggle_send(); wr_usb_data(8,Request.Req_buf); issue_token(( 0 << 4 ) | DEF_USB_PID_SETUP);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* SETUP阶段操作成功 */ { endp6_mode=0xc0; toggle_recv(); } else return(0); issue_token(( 0 << 4 ) | DEF_USB_PID_IN);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* DATA阶段操作成功 */ { if(flag_config_2)descr_len=data_buf[2]-rd_usb_data(data_buf); else descr_len=data_buf[0]-rd_usb_data(data_buf); while(descr_len>0) { toggle_recv(); p+=0x08; issue_token(( 0 << 4 ) | DEF_USB_PID_IN);status=wait_interrupt(); if(status==USB_INT_SUCCESS) /* DATA阶段操作成功 */ descr_len-=rd_usb_data(p); else return(0); } } else return(0); endp7_mode=0xc0; toggle_send(); wr_usb_data(0,Request.Req_buf); issue_token(( 0 << 4 ) | DEF_USB_PID_OUT);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* 状态阶段操作成功 */ return(1); else return(0); } unsigned char get_report_descr_ex() { unsigned char descr_len; unsigned char *p=data_buf; unsigned char report_cou_temp=0; report_cou=0; endp7_mode=0x80; toggle_send(); wr_usb_data(8,Request.Req_buf); issue_token(( 0 << 4 ) | DEF_USB_PID_SETUP);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* SETUP阶段操作成功 */ { endp6_mode=0xc0; toggle_recv(); } else return(0); issue_token(( 0 << 4 ) | DEF_USB_PID_IN);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* DATA阶段操作成功 */ { rd_usb_data(data_buf); report_cou+=8; descr_len=Request.Req_buf[6]-0x08;/*剩余描述符长度计算*/ while(descr_len>0) { toggle_recv(); p+=0x08; issue_token(( 0 << 4 ) | DEF_USB_PID_IN);status=wait_interrupt(); if(status==USB_INT_SUCCESS) /* DATA阶段操作成功 */ { report_cou_temp=rd_usb_data(p); if(report_cou_temp!=0x08){report_cou+=report_cou_temp;break;} else {descr_len-=0x08;report_cou+=8;} } else return(0); } } else return(0); endp7_mode=0xc0; toggle_send(); wr_usb_data(0,Request.Req_buf); issue_token(( 0 << 4 ) | DEF_USB_PID_OUT);status=wait_interrupt(); if(status==USB_INT_SUCCESS)/* 状态阶段操作成功 */ return(1); else return(0); } void get_int_in() { endp6_mode=0x80; toggle_recv(); UDR1=endp_int; endp_int=0x01; issue_token(( endp_int << 4 ) | DEF_USB_PID_IN);//status=wait_interrupt();
} void reset_device() { /* USB规范中未要求在USB设备插入后必须复位该设备,但是计算机的WINDOWS总是这样做,所以有些USB设备也要求在插入后必须先复位才能工作 */ set_usb_mode( 7 ); /* 复位USB设备,CH375向USB信号线的D+和D-输出低电平 */ delayms(10); set_usb_mode( 6 ); /* 结束复位 */ while ( wait_interrupt()!=USB_INT_CONNECT ); /* 等待复位之后的设备端再次连接上来 */
} unsigned char get_descr( unsigned char type ) { /* 从设备端获取描述符 */ status=0xff; CH375_WR_CMD_PORT( CMD_GET_DESCR ); CH375_WR_DAT_PORT( type ); /* 描述符类型, 只支持1(设备)或者2(配置) */ status=wait_interrupt(); /* 等待CH375操作完成 */ if ( status==USB_INT_SUCCESS ) { /* 操作成功 */ unsigned char i, len; len=rd_usb_data( data_buf ); printf( "%s描述符是:", type==1?"设备":"配置" ); for ( i=0; i!=len; i++ ) printf( "%02x ", (unsigned int)data_buf[i] ); printf( "\n" ); } return( status ); } unsigned char set_addr( unsigned char addr ) { /* 设置设备端的USB地址 */ unsigned char status; CH375_WR_CMD_PORT( CMD_SET_ADDRESS ); /* 设置USB设备端的USB地址 */ CH375_WR_DAT_PORT( addr ); /* 地址, 从1到127之间的任意值, 常用2到20 */ status=wait_interrupt(); /* 等待CH375操作完成 */ if ( status==USB_INT_SUCCESS ) { /* 操作成功 */ CH375_WR_CMD_PORT( CMD_SET_USB_ADDR ); /* 设置USB主机端的USB地址 */ CH375_WR_DAT_PORT( addr ); /* 当目标USB设备的地址成功修改后,应该同步修改主机端的USB地址 */ } return( status ); } unsigned char set_config( unsigned char cfg ) { /* 设置设备端的USB配置 */ CH375_WR_CMD_PORT( CMD_SET_CONFIG ); /* 设置USB设备端的配置值 */ CH375_WR_DAT_PORT( cfg ); /* 此值取自USB设备的配置描述符中 */ return( wait_interrupt() ); /* 等待CH375操作完成 */ } void parse_config_descr() { unsigned char i; num_interfaces=data_buf[4]; //保存接口数 config_value=data_buf[5]; //保存配置值 for(i=0;i!=data_buf[2];i++) { if((data_buf[i]==0x09)&&(data_buf[i+1]==0x04))//接口描述符 { } if((data_buf[i]==0x09)&&(data_buf[i+1]==0x21))//HID描述符 { report_descr_len=data_buf[i+7];} //REPORT描述符长度 if((data_buf[i]==0x07)&&(data_buf[i+1]==0x05))//端点描述符 { endp_int=data_buf[i+2]&0x0f;} //中断端点号 } } unsigned char keyBoardIntit() {
unsigned char i; _delay_ms(100); mInitSTDIO(); USART1_INIT(); sei(); CH375_WR_CMD_PORT(0x06); CH375_WR_DAT_PORT(0xaa); UDR1=CH375_RD_DAT_PORT(); delayms(250);_delay_ms(500); set_usb_mode(6); /* 设置USB主机模式, 如果设备端是CH37X, 那么5和6均可 */ #ifdef TEST_LOW_SPEED set_freq( ); //使375B进入低速模式 #endif
while ( wait_interrupt()!=USB_INT_CONNECT ); /* 等待设备端连接上来 */ delayms(5); reset_device(); delayms(5); set_freq( ); //使375B进入低速模式 UDR1=get_descr(0x01);//获取设备描述符 //for(;;); // printf("device\n"); Request.Req.bmRequestType=0x80; Request.Req.bRequest=0x06; Request.Req.wValue=0x0100; Request.Req.wIndex=0x0000; Request.Req.wLength=0x0012; if(get_descr_ex()==1) { for(i=0;i!=data_buf[0];i++) { while( !( UCSR1A & (1< UDR1= data_buf[i]; } } //else ;//printf("get device descr failed\n"); _delay_ms(1); set_addr(5);//设置地址
UDR1=get_descr(0x02);//获取配置描述符 //printf("config\n"); Request.Req.bmRequestType=0x80; Request.Req.bRequest=0x06; Request.Req.wValue=0x0200; Request.Req.wIndex=0x0000; Request.Req.wLength=0x0009; if(get_descr_ex()==1) { for(i=0;i!=data_buf[0];i++) { while( !( UCSR1A & (1< UDR1= data_buf[i]; } if(data_buf[4]!=0x01)flag_interface_2=1;//有多个接口 } //else printf("get config descr failed\n"); _delay_ms(100); //printf("config 2\n"); if(data_buf[2]>0x09) { flag_config_2=1; Request.Req.bmRequestType=0x80; Request.Req.bRequest=0x06; Request.Req.wValue=0x0200; Request.Req.wIndex=0x0000; Request.Req.wLength=0x0000|data_buf[2]; if(get_descr_ex()==1) { for(i=0;i!=data_buf[2];i++) { while( !( UCSR1A & (1< UDR1= data_buf[i]; } flag_config_2=0; } //else printf("get device descr again failed\n"); } _delay_ms(1); parse_config_descr();//保存描述符中一些值
set_config(config_value);//设置配置 // printf("set config\n"); if(set_config_ex()!=1);//printf("set config failed\n"); //printf("set idle\n"); if(set_idle()!=1);//printf("set idle failed\n"); //printf("report\n"); Request.Req.bmRequestType=0x81; Request.Req.bRequest=0x06; Request.Req.wValue=0x2200; Request.Req.wIndex=0x0000; Request.Req.wLength=0x0000|(report_descr_len+0x40); if(get_report_descr_ex()==1) { for(i=0;i!=report_cou;i++) { {while( !( UCSR1A & (1< UDR1= data_buf[i]; }
} } //else printf("get report descr failed\n"); _delay_ms(1); // printf("set report\n"); //对于键盘这一步,是点亮指示灯 if(set_report()!=1){while( !( UCSR1A & (1< UDR1= 0xf9;} else UDR1=0xfa; delayms(250); EIMSK |=(1<EIFR |=(1<sei(); get_int_in(); //发送从中断端点读数据的令牌 return 0; }
之后就进入死循环了。