簡介
1.由來
通常我們使用stm32與pc通信的方式分為無線和有線,無線方式用wifi或藍牙模塊,我使用過程中一直無法接受這樣的連接因為這樣很不穩定,常常需要重啟下位機或者上位機重新連接。
而有線方式我們會用到URAT,或是USB的虛擬串口,這兩種方式中UART需要再接CH340類似的模塊,並且兩種方式都需要pc安裝驅動。
於是我打算做一個不需要轉接模塊,也不需要上位機額外安裝驅動的基於USB-HID的連接通信。
2.工具
1.硬件采用stm32F407ZGT6的usb外設,做從機,使用自定義HID類。
2.軟件使用stm32cubeide生成代碼編寫業務代碼,上位機配置java環境,使用java的JNA技術調用系統HID接口驅動HID設備。
3.注意
1.stm32的usb外設可選高速的USB2.0,和全速的USB1.0,由於stm32F4不自帶usb的高速PHY,使用高速模式需要外接USB3300之類的模塊,這里我們不要求通信速度,於是使用全速模式。
usb集線器向下兼容,全速模式可接幾乎所有的usb擴展口。
2.USB主機在D+,D-線都會接15k的下拉電阻,而USB全速模式的從機需要在D+線上接1.5k的上拉電阻,USB集線器正是通過差分線的上下拉狀態來選擇與設備通信方式的,也可以解決熱拔插的問題。
使用時需要檢查開發板原理圖是否有上拉,一般購買的開發板都會做好這些,用一個GPIO打開開關管即可實現上拉。
下位機實現
1.cubeide代碼生成
首先創建項目,在內置的cubemx配置中,SYS下配置debug方式,可以是swd或jtag,這取決於硬件連接。
使能內外部晶振,配置時鍾樹,主頻在168Mhz,usb外設48MHz。
在project manager中勾選以.c/.h為外設生成文件。
開啟usb外設,作為從機,選擇device_only,配置參數默認即可。
開啟中間件USB_DEVICE,選擇usb類為自定義HID(custom HID),BINTERVAL為響應主機發送數據的延時時間,盡量越小越好。
另外兩個參數是后面描述符所需要的大小,現在就修改即可。
設備描述符中的內容均可修改,VID和PID是上位機識別HID設備的識別碼,需要記住。
以上步驟確認無誤后,ctrl+s保存並生成代碼即可。
2.代碼修改實現收發
生成代碼后,進入目錄USB_DEVICE/App/下打開usbd_custom_hid_if.c文件,添加端點描述符,
代碼如下
0x05,0x8c, /* USAGE_PAGE (ST Page) */
0x09,0x01, /* USAGE (Demo Kit) */
0xa1,0x01, /* COLLECTION (Application) */
// The Input report
0x09,0x03, // USAGE ID - Vendor defined
0x15,0x00, // LOGICAL_MINIMUM (0)
0x26,0x00, 0xFF, // LOGICAL_MAXIMUM (255)
0x75,0x08, // REPORT_SIZE (8bit)
0x95,0x40, // REPORT_COUNT (64Byte)
0x81,0x02, // INPUT (Data,Var,Abs)
// The Output report
0x09,0x04, // USAGE ID - Vendor defined
0x15,0x00, // LOGICAL_MINIMUM (0)
0x26,0x00,0xFF, // LOGICAL_MAXIMUM (255)
0x75,0x08, // REPORT_SIZE (8bit)
0x95,0x40, // REPORT_COUNT (64Byte)
0x91,0x02, // OUTPUT (Data,Var,Abs)
添加之后,下載運行,在pc的設備管理器中就可以看到此外設。
我們有hal庫提供的接收數據和發送數據的接口:
uint8_t USBD_CUSTOM_HID_SendReport(USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len); //發送
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state); //接收
我希望能在main函數中發送,發送函數的第一個參數需要一個設備指針,我沒有找到能引用到設備變量的頭文件,於是自己添加在USB_DEVICE/App/下usbd_custom_hid_if.h中,
/* USER CODE BEGIN INCLUDE */
extern USBD_HandleTypeDef hUsbDeviceFS;
同時在main函數中包含這個頭文件,接收函數也有被這個頭文件包含的頭文件聲明,一舉兩得。
看接收函數的接口,發現每次只能接收2Byte,而發送也每次只能發送6byte,這是HID報告長度固定導致的,於是我在這倆函數之上再封裝一層。
對於發送我希望發送任意長度字符串,而上位機的接收每次緩沖到64byte才會完成接收,於是每次發送超過64byte,多余的補0即可。
代碼:
/* USER CODE BEGIN PFP */
void USB_SentStr(uint8_t *str);
/* USER CODE END PFP */
/* USER CODE BEGIN 4 */
void USB_SentStr(uint8_t *str) {
int strSize = strlen(str); //get length
if(strSize > 64) return; //if too long
uint8_t temp[66] = {0}; //buffer string
for(int i = 0; i < strSize; i++) {
temp[i] = str[i]; //move to buffer
}
for(int j = 0; j < 11; j++) {
while(((USBD_CUSTOM_HID_HandleTypeDef*)(hUsbDeviceFS.pClassData))->state == CUSTOM_HID_BUSY); //if dma trans busy
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, &temp[j * 6], 6); // sent data
}
}
/* USER CODE END 4 */
調用此函數即可發送任意長度的串。
而對於接收,每次只能接收2byte,於是封裝一層,接收到\0表示一行結束,將flag拉高,同時也在usbd_custom_hid_if.h中extern出去給main函數處理數據,
在usbd_custom_hid_if.c中添加如下
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
uint8_t USB_DataBuffer[50] = {0};
uint8_t USB_GetDataFlag = 0;
uint8_t USB_DataBufferIndex = 0;
/* USER CODE END PV */
/* USER CODE BEGIN 6 */
if(USB_GetDataFlag == 0) {
if(event_idx == 0) {
USB_GetDataFlag = 1;
USB_DataBuffer[USB_DataBufferIndex] = 0;
USB_DataBufferIndex = 0;
} else if(state == 0) {
USB_GetDataFlag = 1;
USB_DataBuffer[USB_DataBufferIndex] = event_idx;
USB_DataBuffer[USB_DataBufferIndex + 1] = 0;
USB_DataBufferIndex = 0;
} else {
USB_DataBuffer[USB_DataBufferIndex] = event_idx;
USB_DataBufferIndex++;
USB_DataBuffer[USB_DataBufferIndex] = state;
USB_DataBufferIndex++;
}
}
在usbd_custom_hid_if.c中添加
extern uint8_t USB_DataBuffer[50];
extern uint8_t USB_GetDataFlag;
/* USER CODE END INCLUDE */
在main函數中根據USB_GetDataFlag被拉高知道有數據到了USB_DataBuffer,處理完后拉低即可。
上位機實現
上位機采用java的JNA調用hidapi.dll,也可選linux下的libhidapi.so(我沒試過),win轉linux跨平台應該只需要改少量代碼就可以實現,並且不需要額外驅動支持,因為hid驅動系統會本身自帶的有。
數據收發調用動態鏈接庫提供的api即可,發送時需要將待發送的串拆分每次發送2byte,這樣下位機才能完整接收到。
同時收發的實現就是開多線程即可,我的方式是開兩個線程一個收一個發,而主線程進while死循環。
程序代碼
上位機java程序代碼:https://github.com/untitledx6/java-stm32HID
下位機stm32項目代碼:https://github.com/untitledx6/CubeIDE_Work/tree/master/F4_USBdata