我現(xiàn)在可以用CH375B能讀出鍵盤的8個(gè)字節(jié)數(shù)據(jù),但是連續(xù)敲擊鍵盤時(shí)候,總是丟失一些按鍵值,程序如下: /**************************************** ** Copyright (C) W.ch 1999-2004 ** ** Web: http://www.winchiphead.com ** **************************************** ** USB 1.1 Host Examples for CH375 ** ** KC7.0@MCS-51 ** **************************************** */ /* 用CH375操作HID設(shè)備,比如鍵盤,鼠標(biāo)*/
#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數(shù)據(jù)端口的I/O地址 */ //sbit CH375_INT_WIRE = 0xB0^2; /* P3.2, INT0, 連接CH375的INT#引腳,用于查詢中斷狀態(tài) */ //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; //中斷端點(diǎn)號 unsigned char num_interfaces; //接口數(shù) unsigned char config_value; //配置值 unsigned char report_descr_len=0;//REPORT描述符長度 unsigned char flag_config_2=0; //第二次獲取描述符標(biāo)志位 unsigned char flag_interface_2=0; //多個(gè)接口標(biāo)志位 unsigned char endp6_mode=0x80, endp7_mode=0x80;//同步標(biāo)志位初值 unsigned char status=0xff; //全局狀態(tài) unsigned char idata, data_buf[96];//描述符緩沖區(qū)可以適當(dāng)減小 union _REQUEST //請求包結(jié)構(gòu) { 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描述符長度計(jì)數(shù)
volatile unsigned char flag_output=0; //串口輸出標(biāo)志位 volatile unsigned char CH375_IN_BUFFER[96]; volatile unsigned char CH375_IN_BUFFER_Count;
unsigned char xdata ,data_in[1000];//串口輸出緩沖區(qū) 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,如果單片機(jī)較快則延時(shí) */ delay2us(); PORTD|=(1< PORTB=cmd; DDRB=0XFF; PORTD&=~(1<PORTD&=~(1< //PORTD|=(1< DDRB=0XFF; //該操作無意義,僅作延時(shí),CH375要求讀寫脈沖寬度大于100nS PORTD|=0Xf0;//輸出無效的控制信號, 完成操作CH375芯片, A0=1; CS=1; WR=1; RD=1; DDRB=0X00; /* 禁止數(shù)據(jù)輸出 */ PORTD&=~(1<delay2us(); }
void CH375_WR_DAT_PORT( unsigned char dat ) { /* 向CH375的數(shù)據(jù)端口寫入數(shù)據(jù),周期不小于1.5uS,如果單片機(jī)較快則延時(shí) */ PORTB=dat; DDRB=0XFF; PORTD&=0X4F;/* 輸出有效寫控制信號, 寫CH375芯片的數(shù)據(jù)端口, A0=0; CS=0; WR=0; RD=1; */ DDRB=0XFF; //該操作無意義,僅作延時(shí),CH375要求讀寫脈沖寬度大于100nS PORTD|=0Xf0;//輸出無效的控制信號, 完成操作CH375芯片, A0=1; CS=1; WR=1; RD=1; DDRB=0X00; /* 禁止數(shù)據(jù)輸出 */ delay1us(); }
unsigned int CH375_RD_DAT_PORT() { /* 從CH375的數(shù)據(jù)端口讀出數(shù)據(jù),周期不小于1.5uS,如果單片機(jī)較快則延時(shí) */ unsigned int temp; delay1us(); DDRB=0X00; PORTD&=0X2F;/* 輸出有效讀控制信號, 讀CH375芯片的數(shù)據(jù)端口, A0=0; CS=0; WR=1; RD=0; */ DDRB=0X00; //該操作無意義,僅作延時(shí),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 ) { /* 設(shè)置CH37X的工作模式 */ unsigned char i; CH375_WR_CMD_PORT( CMD_SET_USB_MODE ); CH375_WR_DAT_PORT( mode ); endp6_mode=endp7_mode=0x80; /* 主機(jī)端復(fù)位USB數(shù)據(jù)同步標(biāo)志 */ for( i=0; i!=100; i++ ) { /* 等待設(shè)置模式操作完成,不超過30uS */ if ( CH375_RD_DAT_PORT()==CMD_RET_SUCCESS ) return( TRUE ); /* 成功 */ } return( FALSE ); /* CH375出錯(cuò),例如芯片型號錯(cuò)或者處于串口方式或者不支持 */ }
void set_freq(void) { CH375_WR_CMD_PORT(0x0b); /* 切換使375B進(jìn)入低速模式 */ CH375_WR_DAT_PORT(0x17); CH375_WR_DAT_PORT(0xd8); }
/* 數(shù)據(jù)同步 */ /* USB的數(shù)據(jù)同步通過切換DATA0和DATA1實(shí)現(xiàn): 在設(shè)備端, CH372/CH375可以自動切換; 在主機(jī)端, 必須由SET_ENDP6和SET_ENDP7命令控制CH375切換DATA0與DATA1. 主機(jī)端的程序處理方法是為SET_ENDP6和SET_ENDP7分別提供一個(gè)全局變量, 初始值均為80H, 每執(zhí)行一次成功事務(wù)后將位6取反, 每執(zhí)行一次失敗事務(wù)后將其復(fù)位為80H. */
void toggle_recv() { /* 主機(jī)接收成功后,切換DATA0和DATA1實(shí)現(xiàn)數(shù)據(jù)同步 */ CH375_WR_CMD_PORT( CMD_SET_ENDP6 ); CH375_WR_DAT_PORT( endp6_mode ); endp6_mode^=0x40; delay2us(); }
void toggle_send() { /* 主機(jī)發(fā)送成功后,切換DATA0和DATA1實(shí)現(xiàn)數(shù)據(jù)同步 */ CH375_WR_CMD_PORT( CMD_SET_ENDP7 ); CH375_WR_DAT_PORT( endp7_mode ); endp7_mode^=0x40; delay2us(); }
//void clr_stall6() { /* 主機(jī)接收失敗后,復(fù)位設(shè)備端的數(shù)據(jù)同步到DATA0 */ // CH375_WR_CMD_PORT( CMD_CLR_STALL ); // CH375_WR_DAT_PORT( 2 | 0x80 ); /* 如果設(shè)備端不是CH37X芯片,那么需要修改端點(diǎn)號 */ // endp6_mode=0x80; // status=0xff; //}
//void clr_stall7() { /* 主機(jī)發(fā)送失敗后,復(fù)位設(shè)備端的數(shù)據(jù)同步到DATA0 */ // CH375_WR_CMD_PORT( CMD_CLR_STALL ); // CH375_WR_DAT_PORT( 2 ); /* 如果設(shè)備端不是CH37X芯片,那么需要修改端點(diǎn)號 */ // endp7_mode=0x80; // status=0xff; //}
unsigned char rd_usb_data( unsigned char *buf ) { /* 從CH37X讀出數(shù)據(jù)塊 */ unsigned char i, len; CH375_WR_CMD_PORT( CMD_RD_USB_DATA ); /* 從CH375的端點(diǎn)緩沖區(qū)讀取接收到的數(shù)據(jù) */ len=CH375_RD_DAT_PORT(); /* 后續(xù)數(shù)據(jù)長度 */ for ( i=0; i!=len; i++ ) *buf++=CH375_RD_DAT_PORT(); return( len ); }
void wr_usb_data( unsigned char len, unsigned char *buf ) { /* 向CH37X寫入數(shù)據(jù)塊 */ CH375_WR_CMD_PORT( CMD_WR_USB_DATA7 ); /* 向CH375的端點(diǎn)緩沖區(qū)寫入準(zhǔn)備發(fā)送的數(shù)據(jù) */ CH375_WR_DAT_PORT( len ); /* 后續(xù)數(shù)據(jù)長度, len不能大于64 */ while( len-- ) CH375_WR_DAT_PORT( *buf++ ); }
void issue_token( unsigned char endp_and_pid ) { /* 執(zhí)行USB事務(wù) */ CH375_WR_CMD_PORT( CMD_ISSUE_TOKEN ); CH375_WR_DAT_PORT( endp_and_pid ); /* 高4位目的端點(diǎn)號, 低4位令牌PID */ status=0xff; }
ISR(INT6_vect) { unsigned char len_temp,i; CH375_IN_BUFFER_Count=0; CH375_WR_CMD_PORT( CMD_GET_STATUS ); /* 產(chǎn)生操作完成中斷, 獲取中斷狀態(tài) */ 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); /* 如果設(shè)備端不是CH37X芯片,那么需要修改端點(diǎn)號 */ endp6_mode=0x80; endp7_mode=0x80; toggle_recv(); issue_token(( endp_int << 4 ) | DEF_USB_PID_IN);//發(fā)送從中斷端點(diǎn)讀數(shù)據(jù)的令牌 } else { len_temp=rd_usb_data(data_buf); //鍵盤中斷端點(diǎn)數(shù)據(jù)長度一般為8字節(jié),鼠標(biāo)為4字節(jié) 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);//發(fā)送從中斷端點(diǎn)讀數(shù)據(jù)的令牌 } } unsigned char wait_interrupt() { /* 主機(jī)端等待操作完成, 返回操作狀態(tài) */ while( CH375_INT_WIRE ); /* 查詢等待CH375操作完成中斷(INT#低電平) */ CH375_WR_CMD_PORT( CMD_GET_STATUS ); /* 產(chǎn)生操作完成中斷, 獲取中斷狀態(tài) */ 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數(shù)據(jù)總是8字節(jié) */ 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)/* 狀態(tài)階段操作成功 */ { 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數(shù)據(jù)總是8字節(jié) */ 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) /* 狀態(tài)階段操作成功 */ { 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數(shù)據(jù)總是8字節(jié) */ 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)/* 狀態(tài)階段操作成功 */ { 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)/* 狀態(tài)階段操作成功 */ 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;/*剩余描述符長度計(jì)算*/ 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)/* 狀態(tài)階段操作成功 */ 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規(guī)范中未要求在USB設(shè)備插入后必須復(fù)位該設(shè)備,但是計(jì)算機(jī)的WINDOWS總是這樣做,所以有些USB設(shè)備也要求在插入后必須先復(fù)位才能工作 */ set_usb_mode( 7 ); /* 復(fù)位USB設(shè)備,CH375向USB信號線的D+和D-輸出低電平 */ delayms(10); set_usb_mode( 6 ); /* 結(jié)束復(fù)位 */ while ( wait_interrupt()!=USB_INT_CONNECT ); /* 等待復(fù)位之后的設(shè)備端再次連接上來 */
} unsigned char get_descr( unsigned char type ) { /* 從設(shè)備端獲取描述符 */ status=0xff; CH375_WR_CMD_PORT( CMD_GET_DESCR ); CH375_WR_DAT_PORT( type ); /* 描述符類型, 只支持1(設(shè)備)或者2(配置) */ status=wait_interrupt(); /* 等待CH375操作完成 */ if ( status==USB_INT_SUCCESS ) { /* 操作成功 */ unsigned char i, len; len=rd_usb_data( data_buf ); printf( "%s描述符是:", type==1?"設(shè)備":"配置" ); for ( i=0; i!=len; i++ ) printf( "%02x ", (unsigned int)data_buf[i] ); printf( "\n" ); } return( status ); } unsigned char set_addr( unsigned char addr ) { /* 設(shè)置設(shè)備端的USB地址 */ unsigned char status; CH375_WR_CMD_PORT( CMD_SET_ADDRESS ); /* 設(shè)置USB設(shè)備端的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 ); /* 設(shè)置USB主機(jī)端的USB地址 */ CH375_WR_DAT_PORT( addr ); /* 當(dāng)目標(biāo)USB設(shè)備的地址成功修改后,應(yīng)該同步修改主機(jī)端的USB地址 */ } return( status ); } unsigned char set_config( unsigned char cfg ) { /* 設(shè)置設(shè)備端的USB配置 */ CH375_WR_CMD_PORT( CMD_SET_CONFIG ); /* 設(shè)置USB設(shè)備端的配置值 */ CH375_WR_DAT_PORT( cfg ); /* 此值取自USB設(shè)備的配置描述符中 */ return( wait_interrupt() ); /* 等待CH375操作完成 */ } void parse_config_descr() { unsigned char i; num_interfaces=data_buf[4]; //保存接口數(shù) 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))//端點(diǎn)描述符 { endp_int=data_buf[i+2]&0x0f;} //中斷端點(diǎn)號 } } 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); /* 設(shè)置USB主機(jī)模式, 如果設(shè)備端是CH37X, 那么5和6均可 */ #ifdef TEST_LOW_SPEED set_freq( ); //使375B進(jìn)入低速模式 #endif
while ( wait_interrupt()!=USB_INT_CONNECT ); /* 等待設(shè)備端連接上來 */ delayms(5); reset_device(); delayms(5); set_freq( ); //使375B進(jìn)入低速模式 UDR1=get_descr(0x01);//獲取設(shè)備描述符 //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);//設(shè)置地址
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;//有多個(gè)接口 } //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);//設(shè)置配置 // 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"); //對于鍵盤這一步,是點(diǎn)亮指示燈 if(set_report()!=1){while( !( UCSR1A & (1< UDR1= 0xf9;} else UDR1=0xfa; delayms(250); EIMSK |=(1<EIFR |=(1<sei(); get_int_in(); //發(fā)送從中斷端點(diǎn)讀數(shù)據(jù)的令牌 return 0; }
之后就進(jìn)入死循環(huán)了。