正點原子探索者原理圖_正點原子STM32F407探索者開發板資料連載第五十九章 USB 鍵鼠實驗...
技術標籤:正點原子探索者原理圖
1)實驗平臺:alientek 阿波羅 STM32F767 開發板
2)摘自《STM32F7 開發指南(HAL 庫版)》關注官方微訊號公眾號,獲取更多資料:正點原子
http://weixin.qq.com/r/hEhUTLbEdesKrfIv9x2W (二維碼自動識別)
第五十九章 USB 滑鼠鍵盤(Host)實驗
上一章我們向大家介紹瞭如何利用 STM32F4 的 USB HOST 介面來驅動 U 盤,本章,我們
將利用 STM32F4 的 USB 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 例程,官方例程路徑:光碟→8,
STM32 參考資料→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 的連線,要通過跳線帽連線 PA11 和 D-以及 PA12 和 D+。
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->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 錯誤,直接重啟 USB.
{
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-1,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);
//printf("btn,X,Y,Z:0x%x,%d,%d,%drn",data->button,(signed char)data->x,
//(signed char)data->y,(signed char)data->z);
}
//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);
}
//printf("KEY Board Value:%02Xrn",data);
//printf("KEY Board Char:%crn",data);
}
ST 官方的 USB HID 例程,僅僅是能用,很多地方還要改善,比如識別率低,容易宕機(枚
舉/通訊都可能宕機)等問題,這裡:USBH_Check_EnumeDead 和 USBH_Check_HIDCommDead
這兩個函式,就是我們針對官方 HID 例程現有 bug 做出的改進處理,通過這兩個函式,可以檢
測列舉/通訊是否正常,當出現異常時,直接重啟 USB 核心,重新連線裝置,這樣可以防止死
機造成的程式無響應情況。
另外,為了提高對滑鼠鍵盤的識別率和相容性,對 usbh_hid_core.c 裡面的兩處程式碼進行了
修改:
1,USBH_HID_ClassRequest 函式,修改程式碼(351 行)為:
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 滑鼠/
鍵盤的識別率,相容性好很多。
還有,在 usbh_hid_keybd.h 裡面,要修改鍵盤型別的定義,改為:
#define QWERTY_KEYBOARD
//通用鍵盤
//#define AZERTY_KEYBOARD
//法國版鍵盤
ST 官方例程,是使用的法國版鍵盤,一般我們國內用的是通用鍵盤,所以,需要換一個巨集
定義(換成:QWERTY_KEYBOARD)。
最後,在 usbh_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)//5/6 位元組長度 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);
}
再回到 usbh_usr.c,USR_MOUSE_Init 和 USR_MOUSE_ProcessData 用於處理滑鼠資料,這
兩個函式在 usbh_hid_mouse.c 裡面被呼叫,USR_MOUSE_Init 在滑鼠初始化的時候被呼叫,而
USR_MOUSE_ProcessData 函式,則在滑鼠初始化成功,輪詢資料的時候呼叫,處理滑鼠資料,
該函式將得到的滑鼠資料顯示在 LCD 上面。
同樣,USR_KEYBRD_Init 和 USR_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
__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)
{
u32 t;
HAL_Init();
//初始化 HAL 庫
Stm32_Clock_Init(336,8,2,7);
//設定時鐘,168Mhz
delay_init(168);
//初始化延時函式
uart_init(115200);
//初始化 USART
usmart_dev.init(84);
//初始化 USMART
LED_Init();
//初始化 LED
KEY_Init();
//初始化 KEY
LCD_Init(); //初始化 LCD
SRAM_Init();
//初始化外部 SRAM
W25QXX_Init();
//初始化 W25Q128
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,"[email protected]");
LCD_ShowString(30,110,200,16,16,"2017/5/16");
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=!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 所示:
圖 59.4.1 USB 滑鼠測試
圖 59.4.2 USB 鍵盤測試
其中,圖 59.4.1 是 USB 滑鼠測試介面,圖 59.4.2 是 USB 鍵盤測試介面。
最後,特別提醒大家,由於例程的 HID 核心,只處理了第一個介面描述符,所以對於 USB
符合裝置,只能識別第一個描述符所代表的裝置。體現到實際使用中,就是:USB 無線滑鼠,
一般是無法使用(被識別為鍵盤),而 USB 無線鍵盤,可以使用,因為鍵盤在第一個描述符,
滑鼠在第二個描述符。
如果想支援 USB 無線滑鼠,可以通過修改 usbh_hid_core.c 裡面的 USBH_HID_InterfaceInit
函式來支援。