贵公司有没有CH376S读取USB鼠标的示例程序或者资料啊?搞的有点晕了....能有SPI模式操作的就好了....
我看了帖子好像有这个 http://www.wch.cn/bbs/View.asp?S=101&I=19463 讲解的,但是好像被删掉了。我的硬件连接好了,能够读取USB鼠标
CH376操作鼠标例程请链接 http://www.wch.cn/bbs/View.asp?S=101&I=19463 需要你改一下接口方式,移植到STM32上。
非常感谢....有这个应该简单多了
我在//SETUP阶段操作成功 老成功不了呢,中断返回0x22 我把代码贴出来 u8 Get_Dev_Descr( ) { u8 descr_len; u8 statu; u8 *p=data_buf; send_mode=0x00;
printf(" Get_Dev_Descr start \n"); WR_USB_DATA(8,SetupGetDevDescr);
issue_token(send_mode,( 0 << 4 ) | DEF_USB_PID_SETUP); printf("issue_token Done\n");
statu=mWaitInterrupt(); if(statu==USB_INT_SUCCESS) //SETUP阶段操作成功 { receive_mode=0x80; printf("SETUP successful\n"); } else { printf("SETUP fail_%02x \n",statu); //这里输出0x22 return(0); }
issue_token(receive_mode,( 0 << 4 ) | DEF_USB_PID_IN); status=mWaitInterrupt();
if(status==USB_INT_SUCCESS) //DATA阶段操作成功 { descr_len=data_buf[0]-RD_USB_DATA(data_buf); while(descr_len>0) { receive_mode ^= 0x80; p+=0x08; issue_token(receive_mode,( 0 << 4 ) | DEF_USB_PID_IN);status=mWaitInterrupt(); if(status==USB_INT_SUCCESS) { descr_len-=RD_USB_DATA(p); //DATA阶段操作成功 printf("DATA successful\n"); } else { printf("DATA successful\n"); return(0); } } } else return(0);
send_mode=0x40; WR_USB_DATA(0,SetupGetDevDescr); issue_token(send_mode,( 0 << 4 ) | DEF_USB_PID_OUT);status=mWaitInterrupt(); if(status==USB_INT_SUCCESS) //状态阶段操作成功 return(1); else return(0); }
SetupGetDevDescr这个缓冲区数据是多少?确认是80 06 00 01 00 00 12 00,0X12不一定是固定的,可以是小于0X12的值。issue_token(send_mode,( 0 << 4 ) | DEF_USB_PID_SETUP);这个函数里面“send_mode”这个是什么意思?需要看下完成的这个函数。
数据缓冲区数据完全参照,51并口的那个 // 获取设备描述符 const unsigned char code SetupGetDevDescr[] = { 0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00 }; // 获取配置描述符 const unsigned char code SetupGetCfgDescr[] = { 0x80, 0x06, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00 }; // 设置USB地址 const unsigned char code SetupSetUsbAddr[] = { 0x00, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 设置USB配置 //const unsigned char code SetupSetUsbConfig[] = { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // SET IDLE const unsigned char code SetupSetidle[]={0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00}; // 获取HID 报告描述符 const unsigned char code SetupGetHidDes[]={0x81,0x06,0x00,0x22,0x00,0x00,0x81,0x00}; // SET REPORT const unsigned char code SetupSetReport[]={0x21,0x09,0x00,0x02,0x00,0x00,0x01,0x00}; send_mode 也是并口程序里面的,这里就是0x00; void issue_token(u8 endptog, u8 endp_and_pid ) { xWriteCH376Cmd( CMD2H_ISSUE_TKN_X ); xWriteCH376Data( endptog ); xWriteCH376Data( endp_and_pid ); xEndCH376Cmd( ); mDelay0_5uS(); }
那你把所有的这些变量定义为unsigned char 型变量。这样参数传递应该是没有问题的。建议你这样修改看下。我是担心你传递的数据会不会有问题。
我全部贴出来吧:方便分析.... 整个工程我共享在了百度云盘:http://pan.baidu.com/share/link?shareid=1779702583&uk=671867159 /***********************************main.c**********************/ #include "ch376inc.h" #include "ch376.h" #include "FILE_SYS.H" #include #include
#ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif /* __GNUC__ */
u8 receive_mode = 0x00,send_mode = 0x00; u8 data_buf[160]; u8 status; u8 flag_config_2; //第二次获取配置描述符标志bit u8 report_cou; //实际获取的报表长度 char buf[250]; // 获取设备描述符 u8 SetupGetDevDescr[] = { 0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00 }; // 获取配置描述符 u8 SetupGetCfgDescr[] = { 0x80, 0x06, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00 }; // 设置USB地址 u8 SetupSetUsbAddr[] = { 0x00, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 设置USB配置 //const unsigned char SetupSetUsbConfig[] = { 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // SET IDLE u8 SetupSetidle[]={0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00}; // 获取HID 报告描述符 u8 SetupGetHidDes[]={0x81,0x06,0x00,0x22,0x00,0x00,0x81,0x00}; // SET REPORT u8 SetupSetReport[]={0x21,0x09,0x00,0x02,0x00,0x00,0x01,0x00};
void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; //PC5 作为模拟通道10输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Configure USART1 Tx (PA.09) as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure USART1 Rx (PA.10) as input floating */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); }
//系统中断管理 void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Configure the NVIC Preemption Priority Bits */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
#ifdef VECT_TAB_RAM /* Set the Vector Table base location at 0x20000000 */ NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ /* Set the Vector Table base location at 0x08000000 */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif
/* Enable the USARTy Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
//配置系统时钟,使能各外设时钟 void RCC_Configuration(void) { SystemInit(); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE |RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO |RCC_APB2Periph_SPI1, ENABLE ); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 |RCC_APB1Periph_USART3|RCC_APB1Periph_TIM2 , ENABLE ); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); }
//配置所有外设 void Init_All_Periph(void) {
RCC_Configuration(); GPIO_Configuration(); NVIC_Configuration(); USART1_Configuration(); mDelaymS(50); //CH376在上电时延时40ms左右 CH376_Init(); while(mInitCH376Host()!= USB_INT_SUCCESS); //等待到握手成功为止 }
void issue_token(u8 endptog, u8 endp_and_pid ) { xWriteCH376Cmd( CMD2H_ISSUE_TKN_X ); xEndCH376Cmd( ); xWriteCH376Data( endptog ); xWriteCH376Data( endp_and_pid ); mDelay0_5uS(); } //***************************************************** //* NAME: RD_USB_DATA( UINT8 *buf ) //* FUCTION: 从CH376的端点缓冲区读取接收到的数据 //* 输入参数: 数据缓冲区的地址 //* 输出参数:返回接收的数据长度 //* 说明: 从CH376的主机端点缓冲区中读取接收到的数据 //******************************************************
u8 RD_USB_DATA( u8 *buf ) { UINT8 i, len; xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); // 从CH37X读取数据块 len=xReadCH376Data(); xEndCH376Cmd( ); // 后续数据长度 for ( i=0; i!=len; i++ ) *buf++=xReadCH376Data(); xEndCH376Cmd( ); return( len ); }
//***************************************************** //* NAME: WR_USB_DATA( UINT8 len, UINT8 *buf ) //* FUCTION: 往CH376的端点缓冲区写入数据块 //* 输入参数: 要写入数据块的长度,写入数据缓冲区的地址 //* 输出参数:无 //* 说明: 往CH376的主机端点缓冲区中写入要发送的数据块 //****************************************************** void WR_USB_DATA( u8 len, u8 *buf ) { xWriteCH376Cmd( CMD10_WR_HOST_DATA ); // 0x2C 向CH376的端点缓冲区写入准备发送的数据 xWriteCH376Data( len ); // 8 后续数据长度, len不能大于64 xEndCH376Cmd( ); // mDelay0_5uS(); while( len-- ) {xWriteCH376Data( *buf++ );mDelay0_5uS();} }
//***************************************************** //* NAME: Get_Dev_Descr( ) //* FUCTION: 获取设备描述符 //* 输入参数: 无 //* 输出参数:成功返回1,否则返回0 //* 说明: 该程序采用外置固件模式获取设备描述符 //****************************************************** u8 Get_Dev_Descr( ) { u8 descr_len; u8 statu; u8 *p=data_buf; send_mode=0x00;
printf(" Get_Dev_Descr start \n"); WR_USB_DATA(8,SetupGetDevDescr);
issue_token(send_mode,( 0 << 4 ) | DEF_USB_PID_SETUP); printf("issue_token Done\n");
statu=Wait376Interrupt(); if(statu==USB_INT_SUCCESS) //SETUP阶段操作成功 { receive_mode=0x80; printf("SETUP successful\n"); } else { printf("SETUP fail_%02x \n",statu); return(0); }
issue_token(receive_mode,( 0 << 4 ) | DEF_USB_PID_IN); statu=Wait376Interrupt();
if(statu==USB_INT_SUCCESS) //USB_INT_SUCCESS //DATA阶段操作成功 { descr_len=data_buf[0]-RD_USB_DATA(data_buf); while(descr_len>0) { receive_mode ^= 0x80; p+=0x08; issue_token(receive_mode,( 0 << 4 ) | DEF_USB_PID_IN);status=Wait376Interrupt(); if(status==USB_INT_SUCCESS) //USB_INT_SUCCESS { descr_len-=RD_USB_DATA(p); //DATA阶段操作成功 printf("DATA successful\n"); } else { printf("DATA failtural _%02x\n",statu); return(0); } } } else { printf("XXXXXXfailtural _%02x\n",statu); return(0); }
send_mode=0x40; WR_USB_DATA(0,SetupGetDevDescr); issue_token(send_mode,( 0 << 4 ) | DEF_USB_PID_OUT); statu=Wait376Interrupt(); if(status==USB_INT_SUCCESS) //状态阶段操作成功 { printf("fial success\n"); return(1); } else { printf("final failtural\n"); return(0); } }
int main(void) { u8 i; //,statu Init_All_Periph(); set_freq(); //切换CH376进入低速模式 printf("\r\n"); printf("Main START\r\n"); while(1) { printf(" \n wait usb device : \n"); while (Wait376Interrupt()!=USB_INT_CONNECT ); /* 检测到USB设备连接事件, 可能是新连接或者断开后重新连接 */ mDelaymS(50); Reset_Device(); //复位usb设备 mDelaymS(50); set_freq( ); //使376进入低速模式 mDelaymS(20); //获取设备描述符 测试 这里返回0x2C或者0x28 // xWriteCH376Cmd(0x46); // xWriteCH376Data( 0x01 ); // xEndCH376Cmd( ); // statu=Wait376Interrupt(); // printf("Test ID:%02x \n",statu); //获取设备描述符 if(Get_Dev_Descr()==1) { for(i=0;i!=data_buf[0];i++) printf("%02x ",(unsigned int)data_buf[i]); printf("\n"); } else { printf("\n get device descr failed \n"); } mDelaymS(0x1FFFF); } }
/******************************************************************************* * Function Name : PUTCHAR_PROTOTYPE * Description : Retargets the C library printf function to the USART. * Input : None * Output : None * Return : None *******************************************************************************/ PUTCHAR_PROTOTYPE { /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ USART_SendData(USART1, (u8) ch);
/* Loop until the end of transmission */ while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}
return ch; }
/*********************CH376.c*******************/ #include #include "stm32f10x.h" #include "CH376INC.h" #include "spi.h" #include "ch376.h" #include "USART.h"
/*****************************************************
* Name: mInitCH376Host
* Function: 初始化CH376
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ u8 mInitCH376Host( void ) { u8 res; xWriteCH376Cmd( CMD_CHECK_EXIST ); /* 测试单片机与CH376之间的通讯接口 */ xWriteCH376Data( 0x65 ); res = xReadCH376Data( ); xEndCH376Cmd( ); printf("\r\n"); // 结束通信测试 if ( res != 0x9A ) //返回自己发送的值的取反值0x65-0x9A { //USART1_SendByte(0xe1); printf("hands fail_%02d\n",res); return( ERR_USB_UNKNOWN ); /* 通讯接口不正常,可能原因有:接口连接异常,其它设备影响(片选不唯一),串口波特率,一直在复位,晶振不工作 */ } else printf("hands successful\n");
xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */ xWriteCH376Data( 0x06 ); // 模式代码为0x06,表示切换到已启用的USB主机方式,自动产生SOF包 mDelayuS( 20 ); res = xReadCH376Data( ); // 返回操作状态 xEndCH376Cmd( ); // 工作模式设置结束
xWriteCH376Cmd( CMD20_SET_SDO_INT ); /* 设置SPI的SDO引脚的中断方式 */ xWriteCH376Data( 0x16 ); xWriteCH376Data( 0x90 ); /* SDO引脚在SCS片选无效时兼做中断请求输出 */ xEndCH376Cmd( ); // 结束设置SDO引脚方式
if ( res == CMD_RET_SUCCESS ) return( USB_INT_SUCCESS ); else { //USART1_SendByte(0xe2); printf("CMD_RET Fail _%02d\n",res); return( ERR_USB_UNKNOWN ); /* 设置模式错误 */ } } //********************************************** //* NAME: Set_USB_Mode( UINT8 mode ) //* FUCTION: 设置CH376的工作模式 0x06 为主机模式 //* 输入参数:模式代码 //* 输出参数:操作状态 TRUE:成功,FALSE失败 //* 说明: 设置CH376的工作模式 //********************************************** u8 Set_USB_Mode( u8 mode ) { u8 res; xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */ xWriteCH376Data( 0x06 ); // 模式代码为0x06,表示切换到已启用的USB主机方式,自动产生SOF包 mDelayuS( 20 ); res = xReadCH376Data( ); // 返回操作状态 xEndCH376Cmd( );
if ( res == CMD_RET_SUCCESS ) return( TRUE ); else { //USART1_SendByte(0xe2); printf("Set_USB_Mode _%02d\n",res); return( FALSE ); /* 设置模式错误 */ } } // 工作模式设置结束 //***************************************************** //* NAME: set_freq(void) //* FUCTION: 设置CH376的进入低速模式 //* 输入参数:无 //* 输出参数:无 //* 说明: 对于鼠标键盘等低速设备,要先设置ch376为低速模式 //******************************************************
void set_freq(void) { xWriteCH376Cmd(0x0B); // 切换使375B进入低速模式 xWriteCH376Data(0x17); xWriteCH376Data(0xD8); xEndCH376Cmd( ); //while ( mWaitInterrupt()!=USB_INT_CONNECT ); //等待复位之后的设备端再次连接上来 //printf("set_freq successful\n"); }
void Reset_Device( ) { Set_USB_Mode( 7 ); //复位USB设备,CH376向USB信号线的D+和D-输出低电平 mDelaymS(10); Set_USB_Mode( 6 ); //结束复位,将CH376设置成主机模式 while ( mWaitInterrupt()!=USB_INT_CONNECT ); //等待复位之后的设备端再次连接上来 printf("Reset_Device successful\n"); }
/*****************************************************
* Name: CH376_PORT_INIT
* Function: 初始化CH376的SPI IO!
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ u8 mWaitInterrupt( void ) { /* 等待CH376中断并获取状态,主机端等待操作完成,返回操作状态 */ while ( Query376Interrupt( ) == FALSE ); /* 一直等中断 */ xWriteCH376Cmd( CMD_GET_STATUS ); /* 产生操作完成中断,获取中断状态 */ return( xReadCH376Data( ) ); }
/*****************************************************
* Name: xWriteCH376Cmd
* Function: 写命令到CH376去
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ void xWriteCH376Cmd( u8 mCmd ) /* 向CH376写命令 */ { GPIO_SetBits(GPIOA,GPIO_Pin_1); /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */ mDelay0_5uS( ); /* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCS,SPI_SCK,SPI_SDI为输出方向,SPI_SDO为输入方向 */ GPIO_ResetBits(GPIOA,GPIO_Pin_1); /* SPI片选有效 */ CH376_ReadWrite( mCmd ); /* 发出命令码 */
// DelayXms(1);; /* 延时1.5uS确保读写周期大于1.5uS,或者用上面一行的状态查询代替 */
} /*****************************************************
* Name: xWriteCH376Data
* Function: 写1字节数据到CH376
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ void xWriteCH376Data( u8 mData ) /* 向CH376写数据 */ { CH376_ReadWrite( mData ); // mDelay0_5uS( ); /* 确保读写周期大于0.6uS */ }
/*****************************************************
* Name: xReadCH376Data
* Function: 从CH376读取1字节数据
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ u8 xReadCH376Data( void ) /* 从CH376读数据 */ { // mDelay0_5uS( ); /* 确保读写周期大于0.6uS */ return( CH376_ReadWrite( 0xff) ); } /*****************************************************
* Name: Query376Interrupt
* Function: 查询CH376中断(INT#低电平)
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ u8 Query376Interrupt( void ) { return( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)? FALSE : TRUE ); /* PC0 中断 PA6如果未连接CH376的中断引脚则查询兼做中断输出的SDO引脚状态 */ }
/*****************************************************
* Name: mDelayuS
* Function: delay
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ void mDelayuS(u8 us) { while(us--); }
/*****************************************************
* Name: mDelayuS
* Function: delay
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/
void mDelaymS(u32 ms) { unsigned int i; for(;ms>0;ms--) for(i=0;i<940;i++); }
/*****************************************************
* Name: mDelayuS
* Function: delay
* Input: no
* Output: no
* Author: PowerAVR / 2009-12-19
* Update:
*****************************************************/ void mDelay0_5uS( void ) /* 至少延时0.5uS,根据单片机主频调整 */ { u8 i; i=20; while(i--); }
好的,这里的U8应该是没问题的啊,前面连接测试之类的能成功呢...
set_freq( ); //使376进入低速模式 把这个函数调用去掉试试。还有在检测到设备连接之后,用万用表测试下UD+和UD-对地的电压是多少?