(请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365)

YIE002USB开发板之制作HID设备-编程

  • 1 YIE002-STM32的USB编程
  • 2 调整示例工程Custom_HID
  • 3 修改代码
    • 3.1 准备描述符
    • 3.2 支持ReadFile()和WriteFile()方式的代码
    • 3.3 支持Input报告和Output报告的方式、以及Feature报告的代码
      • 3.3.1 准备通信用的标志和缓冲区
      • 3.3.2 修改RESULT CustomHID_Data_Setup(uint8_t RequestNo)
      • 3.3.3 修改Get_Report和Set_Report的处理函数
  • 4 测试

在介绍完所有背景知识后,终于可以进入实质的嵌入式编程了。

本篇拟使用YIE002开发板,制作一个USB HID设备,支持三种通信方式,以对应UEFI开发探索73和74所讨论的三种上位机通信。

这个系列的博客,主要还是偏向UEFI编程的探索,对于嵌入式的编程,不想讨论过多。对于YIE002的嵌入式开发,请移步我另外一个专栏“嵌入式开发”,在其中我开了一个新坑,用来探索YIE002的编程。

1 YIE002-STM32的USB编程

这款开发板的主芯片是STM32F103C8T6,是我出差最常带的版型。本篇所写的代码,适用于所有F1系列作为主芯片的开发板,比如正点原子的战舰开发板。

从意法的官方资料可知,STM32 MCU有如下USB IP:

  1. USB IP 可作为全速USB设备,存在于STM32F102、STM32F103;
  2. USB+ IP 可作为全速USB设备,存在于STM32F0x2;
  3. FS OTG IP 可作为全速和低速USB主机、全速USB设备,存在于STM32F105、STM32F107、STM32F2、STM32F4;
  4. HS OTG IP 可作为高速、全速和低速USB主机,可作为高速和全速USB设备,存在于STM32F2、STM32F4。

官方也提供了不同的USB库,以适应开发需求。比如针对USB IP和USB+ IP,提供的库如图1所示。

图1 USB(+) IP对应的USB库

因此,对F103系的MCU而言,可以选择Legacy library进行开发,也可以使用Cube library进行开发。

本篇的开发,是基于Legacy library的示例工程Custom_HID改造的。使用Cube library开发的方式,可以博客中参考嵌入式开发的专栏。

2 调整示例工程Custom_HID

我平常开发嵌入式产品,主要使用的是MDK Keil工具。Legacy library(STSW-STM32121)中提供的示例工程,支持各种编译工具。当然,也带来很多我不需要的冗余代码。另外,由于共用了外围设备库和评估板的各种文件,Legacy library组织了很好的代码结构,但对我而言是累赘,总不能每次都在库的文件夹下进行修改吧。

因此,我对示例工程Custom_HID进行了调整,删除了很多不需要的代码,重新组织了代码结构。

如图2所示,是调整过的代码结构。

图2 调整过的代码结构

另一个需要注意的地方,是头文件的路径,如图3所示。

图3 头文件包含路径

另外,在选择设备的时候,选择STM32F103C8,编译针对YIE002-STM32型开发板的生成文件。

其他的细节,请以本篇博客提供的工程,对照原始的Custom_HID工程,即可了解。

3 修改代码

修改代码的过程主要包括:

  1. 准备各种描述符,包括设备描述符、配置描述符和报表描述符等;
  2. 实现三种通信方式的代码支持;

详细过程如下。

3.1 准备描述符

描述符主要在文件usb_desc.c中修改。配置描述符比较简单,主要将厂商ID和产品ID修改为自己需要的值。主要的修改在配置描述符中,需要注意的是,端点描述符和接口描述符与配置描述符在同一数组内。

示例1 配置描述符

const uint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
{……//前略
/******************** Descriptor of Custom HID endpoints ******************//* 27 */0x07,          /* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */0x81,          /* bEndpointAddress: Endpoint Address (IN) */0x03,          /* bmAttributes: Interrupt endpoint */0x40,          /* wMaxPacketSize: 64 Bytes max */0x00,0x20,          /* bInterval: Polling Interval (32 ms) *//* 34 */0x07,  /* bLength: Endpoint Descriptor size */USB_ENDPOINT_DESCRIPTOR_TYPE,    /* bDescriptorType: *//*    Endpoint descriptor type */0x01,    /* bEndpointAddress: *//*   Endpoint Address (OUT) */0x03,  /* bmAttributes: Interrupt endpoint */0x40, /* wMaxPacketSize: 64 Bytes max  */0x00,
0x20,   /* bInterval: Polling Interval (20 ms) */
}

从示例1可以看出,通信用的端点号为1,可以输入输出。包括缺省的端点0在内,使用了两个端点号,因此在接口描述符中,bNumEndpoints必须为2(示例1中没有列出)。

而报表描述符,设置了16字节的通道,如示例2所示。

示例2 报表描述符

const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
{0x05, 0x01, // USAGE_PAGE (Generic Desktop)0x09, 0x00, // USAGE (0) 0xa1, 0x01, // COLLECTION (Application)0x15, 0x00, //     LOGICAL_MINIMUM (0)0x25, 0xff, //     LOGICAL_MAXIMUM (255)0x19, 0x01, //     USAGE_MINIMUM (1)0x29, 0x10, //     USAGE_MAXIMUM (16) 0x95, 0x10, //     REPORT_COUNT (16)0x75, 0x08, //     REPORT_SIZE (8)0x81, 0x02, //     INPUT (Data,Var,Abs)0x19, 0x01, //     USAGE_MINIMUM (1)0x29, 0x10, //     USAGE_MAXIMUM (16) 0x91, 0x02, //   OUTPUT (Data,Var,Abs)0x19, 0x01, //     USAGE_MINIMUM (1)0x29, 0x10, //     USAGE_MAXIMUM (16)0xB1, 0x02, // Feature(Data, Variable, Absolute)0xc0        // END_COLLECTION
}; /* CustomHID_ReportDescriptor */

从中可以看出,构建的通道中,Input报告、Output报告和Feature报告均为16字节长。

3.2 支持ReadFile()和WriteFile()方式的代码

在源文件usb_prop.c中,修改端点的通信能力:

  SetEPTxCount(ENDP1, 0x40);   //robin: 修改为64字节大小SetEPRxCount(ENDP1, 0x40);   //robin: 修改为64字节大小

修改通信函数,在源文件usb_endp.c中进行修改,代码如下:

uint8_t Receive_Buffer[0xff];
void EP1_OUT_Callback(void)
{//  BitAction Led_State;uint32_t DataLength = 0;/* Read received data (2 bytes) */  DataLength=USB_SIL_Read(EP1_OUT, Receive_Buffer); //读取端点得到的数据SetEPRxStatus(ENDP1, EP_RX_VALID);if (Receive_Buffer[0] == 0xA0)//将第二个字节改为1返回,表示是采用端点发送的方式{Receive_Buffer[1]=0x1;}USB_SIL_Write(EP1_IN,Receive_Buffer,DataLength);SetEPTxStatus(ENDP1,EP_TX_VALID);
}

其他代码不用动,并将usb_prop.c下的 void CustomHID_Status_In(void)函数中的内容全部注释掉。

至此,就完成了这种通信方式的编写。

3.3 支持Input报告和Output报告的方式、以及Feature报告的代码

为了支持这两种通信方式,需要实现Set_Report和Get_Report类命令。这两个类命令,有三种报告使用,包括Input报告、Output报告和Feature报告。其中,Input报告和Output报告作为输入输出通信,对应上层的函数为HidD_GetInputReport()和HidD_SetOutputReport();Feature报告对应上层的函数为HidD_GetFeature()和HidD_SetFeature()。

这三种报告都是使用Set_Report 和Get_Report命令来传输数据的,只是通过类命令中的wValue来区分报告类型:1为Input报告、2为Output报告、3为Feature报告。

需要注意的是,USB通信中,都是站在主机的角度来看通信过程的,所有命令也都是由主机发起的。Set_Report对设备而言,是接收数据;Get_Report对设备而言,是发送数据。

修改过程如下:

3.3.1 准备通信用的标志和缓冲区

uint8_t Report_Buf[16];     //Robin: 报告长度为16,见报告描述符
uint8_t Report_InOut_Flag=0; //Robin: Input报告和Output报告标志
uint8_t Report_Feature_Flag=0;//Robin: Feature报告标志

3.3.2 修改RESULT CustomHID_Data_Setup(uint8_t RequestNo)

......
/*** GET_PROTOCOL, GET_REPORT, SET_REPORT ***/else if ( (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) ){         switch( RequestNo ){case GET_PROTOCOL:CopyRoutine = CustomHID_GetProtocolValue;break;case SET_REPORT:CopyRoutine = CustomHID_SetReport_Feature;Request = SET_REPORT;break;//robin add for get_reportcase GET_REPORT:if((Report_InOut_Flag==0)&&(Report_Feature_Flag==0))return USB_NOT_READY;   //Robin: Inform the host that the data is not readyCopyRoutine = CustomHID_GetReport_Feature;Request = GET_REPORT;break;default:break;}}if (CopyRoutine == NULL){return USB_UNSUPPORT;}pInformation->Ctrl_Info.CopyData = CopyRoutine;pInformation->Ctrl_Info.Usb_wOffset = 0;(*CopyRoutine)(0);return USB_SUCCESS;
}

也即增加了Get_Report和Set_Report的处理函数。

3.3.3 修改Get_Report和Set_Report的处理函数

/*******************************************************************************
* Function Name  : CustomHID_SetReport_Feature
* Description    : Set Feature request handling
* Input          : Length.
* Output         : None.
* Return         : Buffer
*******************************************************************************/
uint8_t *CustomHID_SetReport_Feature(uint16_t Length)
{if(pInformation->USBwValues.bw.bb1 == OUT_REPORT)Report_InOut_Flag=1;else if(pInformation->USBwValues.bw.bb1 == FEATURE_REPORT)Report_Feature_Flag=1;if (Length == 0){pInformation->Ctrl_Info.Usb_wLength = 16;//2; //robinreturn NULL;}else{//    return Report_Buf;return &Report_Buf[pInformation->Ctrl_Info.Usb_wOffset];}
}
/*************************************************************************
* Function Name  : CustomHID_GetReport_Feature
* Description    : Set Feature request handling
* Input          : Length.
* Output         : None.
* Return         : Buffer
*************************************************************************/
uint8_t *CustomHID_GetReport_Feature(uint16_t Length)
{if(pInformation->USBwValues.bw.bb1 == IN_REPORT)Report_InOut_Flag=0;else if(pInformation->USBwValues.bw.bb1 == FEATURE_REPORT)Report_Feature_Flag=0;if (Length == 0) //此处报告需要发送的长度{pInformation->Ctrl_Info.Usb_wLength = 16;//2; //robinreturn NULL;
//    return (uint8_t *)16;}else  //此处返回需要处理的数据{if(pInformation->USBwValues.bw.bb1 == FEATURE_REPORT){if(Report_Buf[0] == 0xA0)Report_Buf[1]=0x3;}else{if(Report_Buf[0] == 0xA0)Report_Buf[1]=0x2;}return Report_Buf;}
}

从中很容易看出,针对不同的报告,函数进行了不同的处理。为了方便上位机测试,简单地将收到的数据,进行了局部修改,然后返回。

4 测试

将固件代码编译,下载到YIE002开发板中,使用上位机工具进行测试,如图4所示。

图4 使用UsbHID工具测试HID设备

根据HID设备的固件代码,不同的通信方式,在第一个字节为0xA0的时候,返回的第二个字节分别为0x1、0x2和0x3。从图3中可以看出,是按照代码设定的逻辑在正常工作的。

至此,我们完成了USB HID设备的编写。下一篇开始,将在UEFI环境下对HID设备进行访问。

Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/84 YIE2HID下


UEFI开发探索85- YIE002USB开发板(08 制作HID设备)相关推荐

  1. UEFI开发探索81- YIE002USB开发板(04 制作HID设备)

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) YIE002USB开发板之制作HID设备-USB系统概述 1 USB规范简介 2 软件工程师眼中 ...

  2. UEFI开发探索82- YIE002USB开发板(05 制作HID设备)

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) YIE002USB开发板之制作HID设备-USB标准描述符 1 USB描述符概述 2 USB标准 ...

  3. UEFI开发探索72- YIE002USB开发板(01 开篇)

    (请保留-> 作者: 罗冰   https://blog.csdn.net/luobing4365) 最近把USB的各个方面都研究了一遍,也在UEFI下实现了USB设备的访问.趁着这热乎劲,我计 ...

  4. UEFI开发探索74- YIE002USB开发板(03 Windows编程)

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) YIE002USB开发板之Windows编程 1 添加库文件 2 枚举HID设备 2.1 Set ...

  5. UEFI开发探索02 – 环境搭建1

    (请保留->作者:罗冰 ) 开发初期的目的就是做出可以在pci rom上跑的Oprom,当然是在uefi bios下.我的计划大致如下: 1 搭建完整的编译环境,了解使用哪些库进行编译: 2 我 ...

  6. UEFI开发探索97 – EDK2模拟器搭建网络环境

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) EDK2模拟器搭建网络环境 1 搭建EDK2开发环境 1)工具安装 2)下载代码库 3)更新子模 ...

  7. UEFI开发探索94 – 迷宫小游戏

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) UEFI下的迷宫小游戏 1 Maze程序结构分析 1)定义全局变量 2)设置迷宫 3) 游戏控制 ...

  8. UEFI开发探索98 – 硬盘访问Diskdump

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) 硬盘访问Diskdump 1 UEFI的存储介质访问栈 2 编写Diskdump程序 2.1 B ...

  9. UEFI开发探索100 – 《UEFI编程实践》发布啦

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) <UEFI编程实践>发布 1 内容简介 第一部分 UEFI环境搭建及UEFI应用构建 ...

最新文章

  1. 深度学习之后会是啥?
  2. WinDBG加载符号表的一点心得体会
  3. Pycharm自定义包的导入
  4. 一份技术简历是如何搞定BAT的面试官?
  5. 使用HTML5 / Canvas / JavaScript拍摄浏览器内屏幕截图
  6. 总结了一份嵌入式硬件开发的具体流程
  7. 如何练好嗓子 让声音变得浑厚
  8. 如何建立数据指标体系
  9. 我国超级计算机的发展成就,中国最近的科技发展成就
  10. Python 练习005 ——疯狂填词程序
  11. win10连接android手机助手下载,完美win10手机助手-Win10手机助手v1.0.1 官方免费版下载_飞翔下载...
  12. 《疯狂Android讲义》第二版目录
  13. 整体压缩跟分开压缩哪个更小_2020年新能效发布,美的空调哪个系列好?美的空调推荐...
  14. 多重if 和 嵌套 if 选择结构
  15. 多线程设计模式-线程池模式
  16. matlab分数约分,分母
  17. 家用计算机的发展历史,计算机发展简史
  18. 普里姆算法解决修路问题
  19. 深入计算机组成原理(十五)浮点数和定点数(上):怎么用有限的bit标识尽可能多的信息?
  20. std::thread介绍

热门文章

  1. win10“诊断启动”启用后重启,显示“你的PIN”不可用,问题解决
  2. [微信小程序基础]Java Script从入门到放弃之首次接触Java Script
  3. C++面向对象编程 -- 继承、多态和文件操作
  4. 彩票算法系列讲座(一)
  5. JAVA转换器模式 耳机口转Type-C
  6. 真无图的城市NOA,比你想象中来得更快
  7. 从零开始理解DM368的H264视频编码过程(下)
  8. 在线考试智能题库平台
  9. c语言 循环赛日程表 n=2^k,循环赛日程表分治算法(c语言)
  10. css学习8:浮动和流体布局