1. 程式人生 > 其它 >正點原子探索者原理圖_正點原子STM32F407探索者開發板資料連載第五十九章 USB 鍵鼠實驗...

正點原子探索者原理圖_正點原子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 所示的工程:

cadf42f76531aa2f2fc3e6da16978f76.png

圖 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 所示:

a2be5c0fe9218c8663d11b364370df36.png

圖 59.4.1 USB 滑鼠測試

2f0d9b51e4e047c16872cd956c6af4f2.png

圖 59.4.2 USB 鍵盤測試

其中,圖 59.4.1 是 USB 滑鼠測試介面,圖 59.4.2 是 USB 鍵盤測試介面。

最後,特別提醒大家,由於例程的 HID 核心,只處理了第一個介面描述符,所以對於 USB

符合裝置,只能識別第一個描述符所代表的裝置。體現到實際使用中,就是:USB 無線滑鼠,

一般是無法使用(被識別為鍵盤),而 USB 無線鍵盤,可以使用,因為鍵盤在第一個描述符,

滑鼠在第二個描述符。

如果想支援 USB 無線滑鼠,可以通過修改 usbh_hid_core.c 裡面的 USBH_HID_InterfaceInit

函式來支援。