完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:alientek 阿波罗 STM32F767 开发板
2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子 第六十五章 USB 鼠标键盘(Host)实验 上一章我们向大家介绍了如何利用 STM32F767 的 USB HOST 接口来驱动 U 盘,本章,我 们将利用 STM32F767 的 USB HOST 来驱动 USB 鼠标/键盘。本章分为如下几个部分: 65.1 USB 鼠标键盘简介 65.2 硬件设计 65.3 软件设计 65.4 下载验证 65.1 USB 鼠标键盘简介 传统的鼠标和键盘是采用 PS/2 接口和电脑通信的,但是现在 PS/2 接口在电脑上逐渐消失, 所以现在越来越多的鼠标键盘采用的是 USB 接口,而不是 PS/2 接口的了。 USB 鼠标键盘属于 USB HID 设备。USB HID 即:Human Interface Device(人机交互设备) 的缩写,键盘、鼠标与游戏杆等都属于此类设备。不过 HID 设备并不一定要有人机接口,只要 符合 HID 类别规范的设备都是 HID 设备。关于 USB HID 的知识,我们这里就不详细介绍了, 请大家自行百度学习。 本章,我们同上一章一样,我们直接移植官方的 USB HID 例程,官方例程路径:光盘8, STM32 参考资料STM32 USB 学习资料STM32_USB-Host-Device_Lib_V2.2.0Project USB_Host_ExamplesHID,该例程支持 USB 鼠标和键盘等 USB HID 设备,本章我们将移植这 个例程到阿波罗 STM32 开发板上。 65.2 硬件设计 本节实验功能简介:开机的时候先显示一些提示信息,然后初始化 USB HOST,并不断轮 询。当检测到 USB 鼠标/键盘的插入后,显示设备类型,并显示设备输入数据, 如果是 USB 鼠标:将显示鼠标移动的坐标(X,Y 坐标),滚轮滚动数值(Z 坐标)以及 按键(左中右)。 如果是 USB 键盘:将显示键盘输入的数字/字母等内容(不是所有按键都支持,部分按键 没有做解码支持,比如 F1~F12)。 最后,还是用 DS0 提示程序正在运行。 所要用到的硬件资源如下: 1) 指示灯 DS0 2) 串口 3) LCD 模块 4) USB HOST 接口 这几个部分,在之前的实例中都已经介绍过了,我们在此就不多说了。这里再次提醒大家, P10 的连接,要通过跳线帽连接 PA11 和 D-以及 PA12 和 D+。 65.3 软件设计 本章,我们在第二十章实验 (实验 15 LTDC LCD(RGB 屏)实验)的基础上修改,先打 开实验 15 的工程,在 HARDWARE 文件夹所在文件夹下新建一个 USB 的文件夹,对照官方 HID 例子,将相关文件拷贝到 USB 文件夹下。 然后,我们在工程里面添加 USB HID 相关代码,最终得到如图 65.3.1 所示的工程: 图 62.3.1 USB 鼠标键盘工程截图 注意:为了支持 STM32F7,USB OTG 库部分代码要做修改,详见 61.3 节的介绍(USB HOST 实验只需要修改 u***_core.c 这一个文件就可以支持 STM32F7 了)。 可以看到,USB 部分代码,同上一章的在结构上是一模一样的,只是.c 文件稍微有些变化。 同样,我们移植需要修改的代码,就是 USB_APP 里面的这两个.c 文件了。 其中 u***_bsp.c 的代码,和之前的章节一模一样,可以用上一章的代码直接替换即可正常使 用。 u***h_usr.c 里面的代码,则有所变化,重点代码如下: //下面两个函数,为 ALIENTEK 添加,以防止 USB 死机 //USB 枚举状态死机检测,防止 USB 枚举失败导致的死机 //phost:USB_HOST 结构体指针 //返回值:0,没有死机 // 1,死机了,外部必须重新启动 USB 连接. u8 USBH_Check_EnumeDead(USBH_HOST *phost) { static u16 errcnt=0; //这个状态,如果持续存在,则说明 USB 死机了. if(phost->gState==HOST_CTRL_XFER&&(phost->EnumState==ENUM_IDLE|| phost->EnumState==ENUM_GET_FULL_DEV_DESC)) { errcnt++; if(errcnt>2000)//死机了 { errcnt=0; RCC->AHB2RSTR|=1<<7; //USB OTG FS 复位 delay_ms(5); RCC->AHB2RSTR&=~(1<<7); //复位结束 return 1; } }else errcnt=0; return 0; } //USB HID 通信死机检测,防止 USB 通信死机(暂时仅针对:DTERR,即 Data toggle error) //pcore:USB_OTG_Core_dev_HANDLE 结构体指针 //phidm:HID_Machine_TypeDef 结构体指针 //返回值:0,没有死机 // 1,死机了,外部必须重新启动 USB 连接. u8 USBH_Check_HIDCommDead(USB_OTG_CORE_HANDLE *pcore, HID_Machine_TypeDef *phidm) { if(pcore->host.HC_Status[phidm->hc_num_in]==HC_DATATGLERR)//DTERR 错误 { return 1; } return 0; } //USB 键盘鼠标数据处理 //鼠标初始化 void USR_MOUSE_Init(void) { USBH_Msg_Show(2); //USB 鼠标 USB_FIRST_PLUGIN_FLAG=1;//标记第一次插入 } //键盘初始化 void USR_KEYBRD_Init(void) { USBH_Msg_Show(1); //USB 键盘 USB_FIRST_PLUGIN_FLAG=1;//标记第一次插入 } //临时数组,用于存放鼠标坐标/键盘输入内容(4.3 屏,最大可以输入 2016 字节) __align(4) u8 tbuf[2017]; //USB 鼠标数据处理 //data:USB 鼠标数据结构体指针 void USR_MOUSE_ProcessData(HID_MOUSE_Data_TypeDef *data) { static signed short x,y,z; if(USB_FIRST_PLUGIN_FLAG)//第一次插入,将数据清零 { USB_FIRST_PLUGIN_FLAG=0; x=y=z=0; } x+=(signed char)data->x; if(x>9999)x=9999; if(x<-9999)x=-9999; y+=(signed char)data->y; if(y>9999)y=9999; if(y<-9999)y=-9999; z+=(signed char)data->z; if(z>9999)z=9999; if(z<-9999)z=-9999; POINT_COLOR=BLUE; sprintf((char*)tbuf,"BUTTON:"); if(data->button&0X01)strcat((char*)tbuf,"LEFT"); if((data->button&0X03)==0X02)strcat((char*)tbuf,"RIGHT"); else if((data->button&0X03)==0X03)strcat((char*)tbuf,"+RIGHT"); if((data->button&0X07)==0X04)strcat((char*)tbuf,"MID"); else if((data->button&0X07)>0X04)strcat((char*)tbuf,"+MID"); LCD_Fill(30+56,180,lcddev.width,180+16,WHITE); LCD_ShowString(30,180,210,16,16,tbuf); sprintf((char*)tbuf,"X POS:%05d",x); LCD_ShowString(30,200,200,16,16,tbuf); sprintf((char*)tbuf,"Y POS:%05d",y); LCD_ShowString(30,220,200,16,16,tbuf); sprintf((char*)tbuf,"Z POS:%05d",z); LCD_ShowString(30,240,200,16,16,tbuf); } //USB 键盘数据处理 //data:USB 键盘数据结构体指针 void USR_KEYBRD_ProcessData (uint8_t data) { static u16 pos; static u16 endx,endy; static u16 maxinputchar; u8 buf[4]; if(USB_FIRST_PLUGIN_FLAG)//第一次插入,将数据清零 { USB_FIRST_PLUGIN_FLAG=0; endx=((lcddev.width-30)/8)*8+30; //得到 endx 值 endy=((lcddev.height-220)/16)*16+220; //得到 endy 值 maxinputchar=((lcddev.width-30)/8); maxinputchar*=(lcddev.height-220)/16;//当前 LCD 最大可以显示的字符数. pos=0; } POINT_COLOR=BLUE; sprintf((char*)buf,"%02X",data); LCD_ShowString(30+56,180,200,16,16,buf);//显示键值 if(data>=' '&&data<='~') { tbuf[pos++]=data; tbuf[pos]=0; //添加结束符. if(pos>maxinputchar)pos=maxinputchar;//最大输入这么多 }else if(data==0X0D) //退格键 { if(pos)pos--; tbuf[pos]=0; //添加结束符. } if(pos<=maxinputchar) //没有超过显示区 { LCD_Fill(30,220,endx,endy,WHITE); LCD_ShowString(30,220,endx-30,endy-220,16,tbuf); } }ST 官方的 USB HID 例程,仅仅是能用,很多地方还要改善,比如识别率低,容易死机(枚 举/通信都可能死机)等问题,这里:USBH_Check_EnumeDead 和 USBH_Check_HIDCommDead 这两个函数,就是我们针对官方 HID 例程现有 bug 做出的改进处理,通过这两个函数,可以检 测枚举/通信是否正常,当出现异常时,直接重启 USB 内核,重新连接设备,这样可以防止死 机造成的程序无响应情况。 另外,为了提高对鼠标键盘的识别率和兼容性,对 u***h_hid_core.c 里面的两处代码进行了 修改: 1,USBH_HID_ClassRequest 函数,修改代码(394 行)为: classReqStatus = USBH_Set_Idle (pdev, pphost, 100, 0);//这里 duration 官方设置的是 0,修改为 //100,提高兼容性 2,USBH_Set_Idle 函数,修改代码(542 行)为: phost->Control.setup.b.wLength.w = 100; //官方的这里设置的是 0,导致部分鼠标无法识别, //这里修改为 100 以后,识别率明显提高. 以上两处地方,官方默认值都是设置的 0,我们修改为 100 后,可以明显提高 USB 鼠标/ 键盘的识别率,兼容性好很多。 还有,在 u***h_hid_keybd.h 里面,要修改键盘类型的定义,改为: #define QWERTY_KEYBOARD //通用键盘 //#define AZERTY_KEYBOARD //法国版键盘 ST 官方例程,是使用的法国版键盘,一般我们国内用的是通用键盘,所以,需要换一个宏 定义(换成:QWERTY_KEYBOARD)。 最后,在 u***h_hid_mouse.c 里面,MOUSE_Decode 函数用于鼠标数据解析,但是 ST 官方 例程仅对 4 字节鼠标数据做了解析,而忽略了 5 字节/6 字节鼠标数据的处理,所以,需要修改 该函数为: extern HID_Machine_TypeDef HID_Machine; static void MOUSE_Decode(uint8_t *data) { if(HID_Machine.length==5||HID_Machine.length==6||HID_Machine.length==8) //5/6/8 字节长度的 USB 鼠标数据处理 { HID_MOUSE_Data.button = data[0]; HID_MOUSE_Data.x = data[1]; HID_MOUSE_Data.y = data[3]<<4|data[2]>>4; HID_MOUSE_Data.z = data[4]; }else if(HID_Machine.length==4) //4 字节长度的 USB 鼠标数据处理 { HID_MOUSE_Data.button = data[0]; HID_MOUSE_Data.x = data[1]; HID_MOUSE_Data.y = data[2]; HID_MOUSE_Data.z = data[3]; } USR_MOUSE_ProcessData(&HID_MOUSE_Data); } 再回到 u***h_usr.c,USR_MOUSE_Init 和 USR_MOUSE_ProcessData 用于处理鼠标数据,这 两个函数在 u***h_hid_mouse.c 里面被调用,USR_MOUSE_Init 在鼠标初始化的时候被调用,而 USR_MOUSE_ProcessData 函数,则在鼠标初始化成功,轮询数据的时候调用,处理鼠标数据, 该函数将得到的鼠标数据显示在 LCD 上面。 同样,USR_KEYBRD_Init 和 USR_KEYBRD_ProcessData 用于处理键盘数据,这两个函数 在 u***h_hid_keybd.c 里面被调用,USR_KEYBRD_Init 在键盘初始化的时候被调用,而 USR_KEYBRD_ProcessData 函数,则在键盘初始化成功,轮询数据的时候调用,处理键盘数据, 该函数将键盘输入的字符显示在 LCD 上面。 其他代码,我们就不再介绍了,请大家参考开发板光盘本例程源码。 最后,来看看 main.c 里面的代码,如下: USBH_HOST USB_Host; USB_OTG_CORE_HANDLE USB_OTG_Core_dev; extern HID_Machine_TypeDef HID_Machine; //USB 信息显示 //msgx:0,USB 无连接 // 1,USB 键盘 // 2,USB 鼠标 // 3,不支持的 USB 设备 void USBH_Msg_Show(u8 msgx) { POINT_COLOR=RED; switch(msgx) { case 0: //USB 无连接 LCD_ShowString(30,130,200,16,16,"USB Connecting..."); LCD_Fill(0,150,lcddev.width,lcddev.height,WHITE); break; case 1: //USB 键盘 LCD_ShowString(30,130,200,16,16,"USB Connected "); LCD_ShowString(30,150,200,16,16,"USB KeyBoard"); LCD_ShowString(30,180,210,16,16,"KEYVAL:"); LCD_ShowString(30,200,210,16,16,"INPUT STRING:"); break; case 2: //USB 鼠标 LCD_ShowString(30,130,200,16,16,"USB Connected "); LCD_ShowString(30,150,200,16,16,"USB Mouse"); LCD_ShowString(30,180,210,16,16,"BUTTON:"); LCD_ShowString(30,200,210,16,16,"X POS:"); LCD_ShowString(30,220,210,16,16,"Y POS:"); LCD_ShowString(30,240,210,16,16,"Z POS:"); break; case 3: //不支持的 USB 设备 LCD_ShowString(30,130,200,16,16,"USB Connected "); LCD_ShowString(30,150,200,16,16,"Unknow Device"); break; } } //HID 重新连接 void USBH_HID_Reconnect(void) { //关闭之前的连接 USBH_DeInit(&USB_OTG_Core_dev,&USB_Host); //复位 USB HOST USB_OTG_StopHost(&USB_OTG_Core_dev); //停止 USBhost if(USB_Host.usr_cb->DeviceDisconnected) //存在,才禁止 { USB_Host.usr_cb->DeviceDisconnected(); //关闭 USB 连接 USBH_DeInit(&USB_OTG_Core_dev, &USB_Host); USB_Host.usr_cb->DeInit(); USB_Host.class_cb->DeInit(&USB_OTG_Core_dev,&USB_Host.device_prop); } USB_OTG_DisableGlobalInt(&USB_OTG_Core_dev);//关闭所有中断 //重新复位 USB __HAL_RCC_USB_OTG_FS_FORCE_RESET();//USB OTG FS 复位 delay_ms(5); __HAL_RCC_USB_OTG_FS_RELEASE_RESET();//复位结束 memset(&USB_OTG_Core_dev,0,sizeof(USB_OTG_CORE_HANDLE)); memset(&USB_Host,0,sizeof(USB_Host)); //重新连接 USB HID 设备 USBH_Init(&USB_OTG_Core_dev,USB_OTG_FS_CORE_ID,&USB_Host, &HID_cb,&USR_Callbacks); } int main(void) { u8 t; Cache_Enable(); //打开 L1-Cache HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz delay_init(216); //延时初始化 uart_init(115200); //串口初始化 LED_Init(); //初始化 LED KEY_Init(); //初始化按键 SDRAM_Init(); //初始化 SDRAM LCD_Init(); //初始化 LCD W25QXX_Init(); //初始化 W25Q256 PCF8574_Init(); //初始化 PCF8574 POINT_COLOR=RED; LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7"); LCD_ShowString(30,70,200,16,16,"USB MOUSE/KEYBOARD TEST"); LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(30,110,200,16,16,"2016/8/11"); LCD_ShowString(30,130,200,16,16,"USB Connecting..."); //初始化 USB 主机 USBH_Init(&USB_OTG_Core_dev,USB_OTG_FS_CORE_ID, &USB_Host,&HID_cb,&USR_Callbacks); while(1) { USBH_Process(&USB_OTG_Core_dev, &USB_Host); if(bDeviceState==1)//连接建立了 {int main(void) { u8 t; Cache_Enable(); //打开 L1-Cache HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz delay_init(216); //延时初始化 uart_init(115200); //串口初始化 LED_Init(); //初始化 LED KEY_Init(); //初始化按键 SDRAM_Init(); //初始化 SDRAM LCD_Init(); //初始化 LCD W25QXX_Init(); //初始化 W25Q256 PCF8574_Init(); //初始化 PCF8574 POINT_COLOR=RED; LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7"); LCD_ShowString(30,70,200,16,16,"USB MOUSE/KEYBOARD TEST"); LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(30,110,200,16,16,"2016/8/11"); LCD_ShowString(30,130,200,16,16,"USB Connecting..."); //初始化 USB 主机 USBH_Init(&USB_OTG_Core_dev,USB_OTG_FS_CORE_ID, &USB_Host,&HID_cb,&USR_Callbacks); while(1) { USBH_Process(&USB_OTG_Core_dev, &USB_Host); if(bDeviceState==1)//连接建立了 { if(USBH_Check_HIDCommDead(&USB_OTG_Core_dev,&HID_Machine)) //检测 USB HID 通信,是否还正常? { USBH_HID_Reconnect();//重连 } }else //连接未建立的时候,检测 { if(USBH_Check_EnumeDead(&USB_Host)) //检测 USB HOST 枚举是否死机了?死机了,则重新初始化 { USBH_HID_Reconnect();//重连 } } t++; if(t==200000) { LED0_Toggle; t=0; } } } 这里总共三个函数:USBH_Msg_Show 用于显示一些提示信息,在 u***h_usr.c 里面被相关函数 调用。USBH_HID_Reconnect 则用于 USB HID 重新连接,当发现枚举/通信死机的时候,调用 该函数实现 USB 复位重启,以重新连接;最后,main 函数就比较简单了,处理方式和上一章 几乎一样,只是多了一些通信死机处理。 软件设计部分就为大家介绍到这里。 65.4 下载验证 在代码编译成功之后,我们下载到阿波罗 STM32 开发板上,然后在 USB_HOST 端子插入 USB 鼠标/键盘,注意:此时 USB SLAVE 口不要插 USB 线到电脑,否则会干扰!! 等 USB 鼠标/键盘成功识别后,便可以看到 LCD 显示 USB Connected,并显示设备类型: USB Mouse 或者 USB KeyBoard,同时也会显示输入的数据,如图 65.4.1 和图 65.4.2 所示: 图 65.4.1 USB 鼠标测试 图 65.4.2 USB 键盘测试 其中,图 65.4.1 是 USB 鼠标测试界面,图 65.4.2 是 USB 键盘测试界面。 最后,特别提醒大家,由于例程的 HID 内核,只处理了第一个接口描述符,所以对于 USB 符合设备,只能识别第一个描述符所代表的设备。体现到实际使用中,就是:USB 无线鼠标, 一般是无法使用(被识别为键盘),而 USB 无线键盘,可以使用,因为键盘在第一个描述符, 鼠标在第二个描述符。 如果想支持 USB 无线鼠标,可以通过修改 u***h_hid_core.c 里面的 USBH_HID_InterfaceInit 函数来支持 |
|
相关推荐
|
|
2255 浏览 1 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
2071 浏览 3 评论
4687 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
2215 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
2757 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 10:28 , Processed in 0.818068 second(s), Total 63, Slave 46 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号