【正点原子探索者STM32F407开发板例程连载+教学】第59章 USB鼠标键盘(Host)实验

2019-07-20 03:20发布

第五十九章 USB鼠标键盘(Host)实验

  1.硬件平台:正点原子探索者STM32F407开发板 2.软件平台:MDK5.1 3.固件库版本:V1.4.0

上一章我们向大家介绍了如何利用STM32F4USB HOST接口来驱动U盘,本章,我们将利用STM32F4USB HOST来驱动USB鼠标/键盘。本章分为如下几个部分: 59.1 USB鼠标键盘简介 59.2 硬件设计 59.3 软件设计 59.4 下载验证   59.1 USB鼠标键盘简介 传统的鼠标和键盘是采用PS/2接口和电脑通信的,但是现在PS/2接口在电脑上逐渐消失,所以现在越来越多的鼠标键盘采用的是USB接口,而不是PS/2接口的了。 USB鼠标键盘属于USB HID设备。USB HID即:Human Interface Device(人机交互设备)的缩写,键盘、鼠标与游戏杆等都属于此类设备。不过HID设备并不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。关于USB HID的知识,我们这里就不详细介绍了,请大家自行百度学习。 本章,我们同上一章一样,我们直接移植官方的USB HID例程,官方例程路径:光盘à8STM32参考资料àSTM32 USB 学习资料àSTM32_USB-Host-Device_Lib_V2.1.0àProjectà USB_Host_ExamplesàHID,该例程支持USB鼠标和键盘等USB HID设备,本章我们将移植这个例程到探索者STM32F407开发板上。 59.2 硬件设计 本节实验功能简介:开机的时候先显示一些提示信息,然后初始化USB HOST,并不断轮询。当检测到USB鼠标/键盘的插入后,显示设备类型,并显示设备输入数据, 如果是USB鼠标:将显示鼠标移动的坐标(X,Y坐标),滚轮滚动数值(Z坐标)以及按键(左中右)。 如果是USB键盘:将显示键盘输入的数字/字母等内容(不是所有按键都支持,部分按键没有做解码支持,比如F1~F12)。 最后,还是用DS0提示程序正在运行。 所要用到的硬件资源如下: 1)  指示灯DS0 2)  串口 3)  TFTLCD模块 4)  USB HOST接口  这几个部分,在之前的实例中都已经介绍过了,我们在此就不多说了。这里再次提醒大家,P11的连接,要通过跳线帽连接PA11D-以及PA12D+ 59.3 软件设计 本章,我们在第十八章实验 (实验13 TFTLCD显示实验 )的基础上修改,先打开实验13的工程,在HARDWARE文件夹所在文件夹下新建一个USB的文件夹,对照官方HID例子,将相关文件拷贝到USB文件夹下。 然后,我们在工程里面添加USB HID相关代码,最终得到如图59.3.1所示的工程:
59.3.1 USB鼠标键盘工程截图 可以看到,USB部分代码,同上一章的在结构上是一模一样的,只是.c文件稍微有些变化。同样,我们移植需要修改的代码,就是USB_APP里面的这两个.c文件了。     其中usb_bsp.c的代码,和之前的章节一模一样,可以用上一章的代码直接替换即可正常使用。 usbh_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_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS,ENABLE); //USB OTG FS 复位          delay_ms(5);                      RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS,DISABLE);     //复位结束                       return 1;               }        }else errcnt=0;        return 0; } //USB HID通信死机检测,防止USB通信死机(暂时仅针对TERR,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,endx,endy,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_EnumeDeadUSBH_Check_HIDCommDead这两个函数,就是我们针对官方HID例程现有bug做出的改进处理,通过这两个函数,可以检测枚举/通信是否正常,当出现异常时,直接重启USB内核,重新连接设备,这样可以防止死机造成的程序无响应情况。 另外,为了提高对鼠标键盘的识别率和兼容性,对usbh_hid_core.c里面的两处代码进行了修改: 1USBH_HID_ClassRequest函数,修改代码(351行)为: classReqStatus = USBH_Set_Idle (pdev, pphost, 100, 0);//这里duration官方设置的是0,修改为 //100,提高兼容性      2USBH_Set_Idle函数,修改代码(542行)为: phost->Control.setup.b.wLength.w = 100;    //官方的这里设置的是0,导致部分鼠标无法识别, //这里修改为100以后,识别率明显提高. 以上两处地方,官方默认值都是设置的0,我们修改为100后,可以明显提高USB鼠标/键盘的识别率,兼容性好很多。        再回到usbh_usr.cUSR_MOUSE_InitUSR_MOUSE_ProcessData用于处理鼠标数据,这两个函数在usbh_hid_mouse.c里面被调用,USR_MOUSE_Init在鼠标初始化的时候被调用,而USR_MOUSE_ProcessData函数,则在鼠标初始化成功,轮询数据的时候调用,处理鼠标数据,该函数将得到的鼠标数据显示在LCD上面。        同样,USR_KEYBRD_InitUSR_KEYBRD_ProcessData用于处理键盘数据,这两个函数在usbh_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        RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS,ENABLE);//复位        delay_ms(5);        RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS,DISABLE);   //复位结束           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) {           u32 t;        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2        delay_init(168);  //初始化延时函数        uart_init(115200);         //初始化串口波特率为115200        LED_Init();                         //初始化LED       LCD_Init();                       //初始化LCD        POINT_COLOR=RED;        LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");              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,"2014/7/23");            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       //连接未建立的时候,检测               { //检测USB HOST 枚举是否死机了?死机了,则重新初始化                      if(USBH_Check_EnumeDead(&USB_Host)) USBH_HID_Reconnect();//重连               }               t++;               if(t==200000){ LED0=!LED0; t=0;}        } }        这里总共三个函数:USBH_Msg_Show用于显示一些提示信息,在usbh_usr.c里面被相关函数调用。USBH_HID_Reconnect则用于USB HID重新连接,当发现枚举/通信死机的时候,调用该函数实现USB复位重启,以重新连接;最后,main函数就比较简单了,处理方式和上一章几乎一样,只是多了一些通信死机处理。  软件设计部分就为大家介绍到这里。 59.4 下载验证 在代码编译成功之后,我们下载到探索者STM32F4开发板上,然后在USB_HOST端子插入USB鼠标/键盘,注意:此时USB SLAVE口不要插USB线到电脑,否则会干扰!!USB鼠标/键盘成功识别后,便可以看到LCD显示USB Connected,并显示设备类型:USB Mouse或者USB KeyBoard,同时也会显示输入的数据,如图58.4.1和图58.4.2所示: 说明: 说明: C:UsersAdministratorDesktopF407教程文档资料例程图片USB鼠标测试.jpg59.4.1 USB鼠标测试 说明: 说明: C:UsersAdministratorDesktopF407教程文档资料例程图片USB键盘.jpg59.4.2 USB键盘测试        其中,图59.4.1USB鼠标测试界面,图59.4.2USB键盘测试界面。        最后,特别提醒大家,由于例程的HID内核,只处理了第一个接口描述符,所以对于USB符合设备,只能识别第一个描述符所代表的设备。体现到实际使用中,就是:USB无线鼠标,一般是无法使用(被识别为键盘),而USB无线键盘,可以使用,因为键盘在第一个描述符,鼠标在第二个描述符。        如果想支持USB无线鼠标,可以通过修改usbh_hid_core.c里面的USBH_HID_InterfaceInit函数来支持。
  实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm  正点原子探索者STM32F407开发板购买地址http://item.taobao.com/item.htm?id=41855882779
  

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。