我按照该文章配置STM32F103ZE开发板可行。
有两点补充:
一、调试的时候要注意将编译等级修改为0级;
二、CUSTOM_HID_EPIN_SIZE及CUSTOM_HID_EPOUT_SIZE宏定义修改为0X40。
以下为转载内容:
每次用串口调试感觉麻烦死了,尤其是电脑上没有串口,usb转串口线一大坨
为什么用usb 的 hid 功能?
hid 是免驱的,任何电脑上都内置的有他的驱动.
插上去就可以用.
对于调试来说,hid的最大速度64k/s完全够用了,但是,这个速度是理论的,实际中根本不可能达到!!
但是缩减下,能达到串口的115200bit/s就比串口好用了.
记录开始:
首先上配置:
我的板卡是stm32f407discovery板,晶振8M
led是PD12/PD13
按键是PA0
首先选中外部时钟,然后去时钟界面输入外部时钟为8M
选中usb 的fs模式, 选择custom hid接口功能
然后去打开usb_fs配置 ,其实就是默认的
中断号配置界面
usb device配置图(备注:下图中的2可修改为33.)
usb 端口描述图
还有led的IO口设置,这个就不再描述了.
这样配置结束了,然后生成工程,打开,编译!
编译之后,直接烧录到板卡,插入otg口连接电脑,
看到设备管理器里面有个叹号,这个不用着急,因为咱们还没有配置端点造成的.
就是上面这个地方,
我是准备让他弄成串口类似的功能,所以不想定义什么乱七八糟的id
直接用数组写和读,然后接收后用自己定的协议来处理
所以,我参考网上的帖子对这个描述的例子,代码如下:
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
/* USER CODE BEGIN 0 */
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)
/* USER CODE END 0 */
0xC0 /* END_COLLECTION */
};
我也不知道会不会又被编辑器吞代码,所以,上图
查看这个函数的数组长度定义,改为33
#define USBD_CUSTOM_HID_REPORT_DESC_SIZE 33
如果你不知道怎么查看,就去看usbd_conf.h里面修改吧.
这个时候再进行编译,烧录到板子.
这个时候可以看到设备管理器里面的hid已经多了两个
HID-compliant device 和 usb 输入设备
我们现在开始看生成的工程代码,哪些是需要自己写的,哪些是系统写好的可以直接用的.
void MX_USB_DEVICE_Init(void)
{
/* Init Device Library,Add Supported Class and Start the library*/
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_CUSTOM_HID);
USBD_CUSTOM_HID_RegisterInterface(&hUsbDeviceFS, &USBD_CustomHID_fops_FS);
USBD_Start(&hUsbDeviceFS);
}
系统完成了这么多,
只有这个事用户可以操作的,其他的就是系统完成usb的对接的
所以去查看下这个定义
USBD_CustomHID_fops_FS
USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
CUSTOM_HID_ReportDesc_FS,
CUSTOM_HID_Init_FS,
CUSTOM_HID_DeInit_FS,
CUSTOM_HID_OutEvent_FS,
};
里面有刚才写的报告描述符函数
有通过hid实现其他工程用的初始化函数
有一个通过hid实现其他工程用的失能函数
还有通过hid控制一个貌似叫输出事件的函数
一步一步来,先来看看这个输出事件是怎么回事?
于是直接在工程中查找名字叫CUSTOM_HID_OutEvent_FS 的位置,
发现貌似没有什么值得注意的被调用的地方,所以初步判断这个应该是利用了其他方法调用的,比如:指针
但是尽管事其他类似指针的调用,也要找出来,要不然不知道这个东西怎么用啊,
回到MX_USB_DEVICE_Init这个函数,
我们查看下USBD_CUSTOM_HID这个定义
于是我找到了它
USBD_ClassTypeDef USBD_CUSTOM_HID =
{
USBD_CUSTOM_HID_Init,
USBD_CUSTOM_HID_DeInit,
USBD_CUSTOM_HID_Setup,
NULL, /*EP0_TxSent*/
USBD_CUSTOM_HID_EP0_RxReady, /*EP0_RxReady*/ /* STATUS STAGE IN */
USBD_CUSTOM_HID_DataIn, /*DataIn*/
USBD_CUSTOM_HID_DataOut,
NULL, /*SOF */
NULL,
NULL,
USBD_CUSTOM_HID_GetCfgDesc,
USBD_CUSTOM_HID_GetCfgDesc,
USBD_CUSTOM_HID_GetCfgDesc,
USBD_CUSTOM_HID_GetDeviceQualifierDesc,
};
看到里面的
USBD_CUSTOM_HID_DataOut
和
USBD_CUSTOM_HID_DataIn
,从名称可以看出来,这个就是数据处理的地方了.
去查看OUT的函数
static uint8_t USBD_CUSTOM_HID_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)pdev->pClassData;
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);
USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR , hhid->Report_buf,
USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
return USBD_OK;
}
可以看到,从usb获取的数据,利用
USBD_CUSTOM_HID_ItfTypeDef
这个函数指针进行了一系列的处理
其中的这句话
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);
说明我们准备用的这个函数
CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
这个里面的事件和状态都是来自此处的buf[0]和buf[1]
于是,我可以这样测试一下,我写个代码在
CUSTOM_HID_OutEvent_FS这个函数里
static int8_t CUSTOM_HID_OutEvent_FS (uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN 6 */
switch(event_idx)
{
case 0:
if(state == 0x05)
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_12);
else if(state == 0x06)
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_13);
break;
default:
break;
}
return (0);
/* USER CODE END 6 */
}
上面的意思是如果PC端的hid上位机发送0x00 0x05 那么PD12的led进行反转
如果发送的是0x00 0x06 那么PD13的led进行反转
这样就可以测试.hid是不是可用了.
于是我进行了烧录,和测试
结果表明确实可以控制板子上面的led的亮灭,不过,感觉不是实时的,总有些延时在里面,
判断是usb的hid方面利用的是poll查询发送模式,所以,可以修改这个查询的时间,
我们打开,工程中的 usbd_customhid.c 文件
找到控制时间的变量,改为 0x01 即1ms
再次烧录,现在基本感觉不出有任何动作延时了..
大晚上,睡不着,再更新一点吧
透过上面的
int8_t CUSTOM_HID_OutEvent_FS (uint8_t event_idx, uint8_t state);
这个函数的构造,
我们只能得到两个字节的数据使用,那么我们有方式把更多的字节取出来用吗?而不用对底层的usb打动干戈?
猜想可以定义一个类似下面的代码函数
static int8_t CUSTOM_HID_OutDulBuf_FS (uint8_t* DulBuf)
{
return (0);
}
利用指针把需要的数据都取出来,然后处理啥的都可以自己随便弄了
我们要是准备这么用肯定要把这个函数注册到包含
CUSTOM_HID_OutEvent_FS()这个函数的结构体里面去.
由上面我们知道这个函数是在
USBD_CustomHID_fops_FS()
里面注册的,所以我们要再这个函数结构体里面进行添加
USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
CUSTOM_HID_ReportDesc_FS,
CUSTOM_HID_Init_FS,
CUSTOM_HID_DeInit_FS,
CUSTOM_HID_OutEvent_FS,
CUSTOM_HID_OutDulBuf_FS,
};
这样我们初始化usb的时候,这个函数就注册到usb里面了,
但是它是由
USBD_CUSTOM_HID_ItfTypeDef来定义的结构体,所以我们也需要在这个定义里添加
typedef struct _USBD_CUSTOM_HID_Itf
{
uint8_t *pReport;
int8_t (* Init) (void);
int8_t (* DeInit) (void);
int8_t (* OutEvent) (uint8_t, uint8_t );
//
int8_t (* OutDulBuf) (uint8_t*);
//
}USBD_CUSTOM_HID_ItfTypeDef;
但是还不够,我们并没有在usb的底层程序里调用,
我们找到上面楼层里提到的USBD_CUSTOM_HID_DataOut()这个函数
这个函数在usbd_customhid.c 文件里
函数的原型是:
static uint8_t USBD_CUSTOM_HID_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)pdev->pClassData;
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);
USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR , hhid->Report_buf,
USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
return USBD_OK;
}
我们准备再这个里进行调用,就在那些buf[0]和buf[1]之后一行.添加
//-------------------------------
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutDulBuf(hhid->Report_buf);
//-------------------------------
这样当usb接收到数据,就会利用函数指针调用,我们添加的函数进行各种操作了
例如下面的测试代码:
static int8_t CUSTOM_HID_OutDulBuf_FS (uint8_t* DulBuf)
{
uint8_t outdata[64];
outdata[0] = *(DulBuf++);
outdata[1] = *(DulBuf++);
outdata[2] = *(DulBuf++);
outdata[3] = *(DulBuf++);
if(outdata[0] != 3)
HAL_Delay(100);
return (0);
}
我们取出usb的数据传递给了一个数组,利用数组可以进行一系列的数据处理了.
假定我们发送一个64字节数据通过hid,
通过keil查看Buff这个内存地址,可以看到内存的数据和发送完全一致!
那么hid的数据接收方面就测试完毕!
至于hid的发送,已经集成到了里面,不过被官方给注释掉了.所以,取消掉这个注释
在文件 usbd_custom_hid_if.c中最后一行
/**
* @brief USBD_CUSTOM_HID_SendReport_FS
* Send the report to the Host
* @param report: the report to be sent
* @param len: the report length
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t USBD_CUSTOM_HID_SendReport_FS ( uint8_t *report,uint16_t len)
{
return USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, report, len);
}
我们就可以调用这个发送字节到pc了.
为了测试方便,同时懒省事,我将楼上CUSTOM_HID_OutDulBuf_FS 这个刚刚添加的修改下
static int8_t CUSTOM_HID_OutDulBuf_FS (uint8_t *Buff)
{
USBD_CUSTOM_HID_SendReport_FS(Buff,3);
return (0);
}
意思是接收到数据之后返回接收到数据的前三个字节.
当然实际使用中,你可以定义一个协议,这样就知道需要返回多少长度的字节了.
ok,hid的初步使用介绍完了.
希望能帮到看到此贴的人,
同时希望大家踊跃分享自己的心得和体会.