历史上的今天
返回首页

历史上的今天

今天是:2024年11月04日(星期一)

正在发生

2020年11月04日 | STM32 CustomHID 的实现

2020-11-04 来源:eefocus

如何建立一个自定义的HID工程呢?下面就来讲讲。

首先先介绍下工程的架构,工程的总体架构下图所示,按照下图架构建工程:

STM32 CustomHID 的实现 - ziye334 - ziye334的博客


分析下工程布局,首先是APP,这个组里存放着主文件mian.c,管理所有中断服务程序stm3210x_it.c,及其管理外设库头文件的stm32f10x_conf.h。BSP这个组里存放着BSP.c,外设的洗衣初始化都在这个函数中定义,比如说串口的配置,LED灯的配置,系统时钟的配置,各类NVIC的中断配置。在这个文件中,会定义一个BSP_Init()函数,所有配置的都在这个函数中调用,例如:

void BSP_Init(void)

{

RCC_Configuration();

Set_USBClock();

USB_Init();

USART1_Configuration(115200);

LED_Configuration();

NVIC_Configuration();

USB_Interrupts_Config();

}


而这个BSP_Init()函数在main中调用,这样就使主函数简洁漂亮了。至于CMSIS这个组则是关于Cotex-M3内核的相关文件看,如core_cm3.c和system_stm32f10x.c。StartUp这个组放置系统的启动文件,不同系类的处理器使用不同的启动文件,这里有必要了解:

- startup_stm32f10x_ld_vl.s: for STM32 Low density Value line devices

- startup_stm32f10x_ld.s: for STM32 Low density devices

- startup_stm32f10x_md_vl.s: for STM32 Medium density Value line devices

- startup_stm32f10x_md.s: for STM32 Medium density devices

- startup_stm32f10x_hd.s: for STM32 High density devices

- startup_stm32f10x_xl.s: for STM32 XL density devices

- startup_stm32f10x_cl.s: for STM32 Connectivity line devices


cl:互联型产品,stm32f105/107系列

vl:超值型产品,stm32f100系列

xl:超高密度产品,stm32f101/103系列

ld:低密度产品,FLASH小于64K

md:中等密度产品,FLASH=64 or 128

hd:高密度产品,FLASH大于128


我的这个工程选择高密度型的: startup_stm32f10x_hd.s。USB_User文件组放着USB控制与应用相关的文件,在之前的文章每个文件都详细介绍过。接着是USB-FS-Device_Driver这个组放着USB的驱动,在之前的文章页已经讲述过。最后一个组是STM32F10x_StdPeriph_Driver,它里面存放着外设库文件的驱动代码,很多人为了省事,会把所有的C文件都添加进来,我不建议这么做,还是根据需要添加对应的文件,就拿我们的这个CustomHID工程,我们用到了引脚GPIO、时钟的配置,串口的配置,所以只要添加这几个对应的C库文件就可以了。

  上面的各个文件大部分可以网上下载的。


 

接下去就讲述如何实现CustomHID功能的。

首先,最重要的文件当然是usb_desc.c这个文件了。这个文件存放着各种描述符,比如说设备描述符、配置描述符等,下面就一一介绍。

设备描述符符的定义如下:

/* USB标准设备描述符*/

const uint8_t CustomHID_DeviceDescriptor[CUSTOMHID_SIZ_DEVICE_DESC] =

{

    0x12,                       /*bLength:长度,设备描述符的长度为18字节*/

    USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType:类型,设备描述符的编号是0x01*/

    0x00,                       /*bcdUSB:所使用的USB版本为2.0*/

    0x02,

    0x00,                       /*bDeviceClass:设备所使用的类代码*/

    0x00,                       /*bDeviceSubClass:设备所使用的子类代码*/

    0x00,                       /*bDeviceProtocol:设备所使用的协议*/

    0x40,                       /*bMaxPacketSize:最大包长度为64字节*/

    0x34,                       /*idVendor:厂商ID为0x1234*/

    0x12,

    0x10,                       /*idProduct:产品ID为0x1010*/

    0x10,

    0x00,                       /*bcdDevice:设备的版本号为2.00*/

    0x02,

    1,                          /*iManufacturer:厂商字符串的索引*/

    2,                          /*iProduct:产品字符串的索引*/

    3,                          /*iSerialNumber:设备的序列号字符串索引*/

    0x01                        /*bNumConfiguration:设备有1种配置*/

}; /* CustomHID设备描述符 */


设备描述符的数组的长度一般为9个字节,该描述符定义了USB协议代号、厂商ID(VID),产品ID(PID)、设备的版本号、以及厂商产品序列号描述符的索引。在USB枚举阶段,USB设备需要通过端口0向USB主机发送设备描述符。

配置描述符集合里有着丰富的USB设备的信息,如用了几个接口,用了几个端点,USB设备做什么用等。代码如下:

/* USB配置描述符集合(配置、接口、端点、类、厂商)(Configuration, Interface, Endpoint, Class, Vendor */

const uint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =

{

    0x09,   /*bLength:长度,设备字符串的长度为9字节*/

    USB_CONFIGURATION_DESCRIPTOR_TYPE, /*bDescriptorType:类型,配置描述符的类型编号为0x2*/

    CUSTOMHID_SIZ_CONFIG_DESC,     /*wTotalLength:配置描述符的总长度为41字节*/    

    0x00,

    0x01,         /*bNumInterfaces:配置所支持的接口数量1个*/

    0x01,         /*bConfigurationValue:该配置的值*/

    0x00,         /*iConfiguration:该配置的字符串的索引值,该值为0表示没有字符串*/              

    0xC0,         /* bmAttributes:设备的一些特性,0xc0表示自供电,不支持远程唤醒

D7:保留必须为1,D6:是否自供电,D5:是否支持远程唤醒,D4~D0:保留设置为0*/

//    0x32,       /*从总线上获得的最大电流为100mA */

    0x96,         /*MaxPower:设备需要从总线上获取多少电流,单位为2mA,0x96表示300mA*/


    /**************  HID接口描述符****************/

    0x09,         /*bLength:长度,接口描述符的长度为9字节 */

    USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType:接口描述符的类型为0x4 */

    0x00,         /*bInterfaceNumber:该接口的编号*/

    0x00,         /*bAlternateSetting:该接口的备用编号 */

    0x02,         /*bNumEndpoints:该接口所使用的端点数*/

    0x03,         /*bInterfaceClass该接口所使用的类为HID*/

    0x00,         /*bInterfaceSubClass:该接口所用的子类 1=BOOT, 0=no boot */

    0x00,         /*nInterfaceProtocol :该接口使用的协议0=none, 1=keyboard, 2=mouse */

    0,            /*iInterface: 该接口字符串的索引 */


    /*****************HID描述符 ********************/

    0x09,         /*bLength: HID描述符的长度为9字节 */

    HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID的描述符类型为0x21 */

    0x10,         /*bcdHID: HID协议的版本为1.1 */

    0x01,

    0x00,         /*bCountryCode: 国家代号 */

    0x01,         /*bNumDescriptors: 下级描述符的数量*/

    0x22,         /*bDescriptorType:下级描述符的类型*/

    CUSTOMHID_SIZ_REPORT_DESC,/* wItemLength: 下一集描述符的长度*/

    0x00,


    /********************输入端点描述符******************/

    0x07,         /* bLength: 端点描述符的长度为7字节*/

    USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: 端点描述符的类型为0x21*/

    0x82,         /* bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/               

    0x03,         /* bmAttributes: 端点的属性为为中断端点.

D0~D1表示传输类型:0(控制传输),1(等时传输),2(批量传输),3(中断传输)

非等时传输端点:D2~D7:保留为0

等时传输端点:

D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步)

D4~D5表示用途:0(数据端点),1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留,*/

    0x40,         /* wMaxPacketSize: 该端点支持的最大包长度为64字节*/

    0x00,

    0x02,         /* bInterval: 轮询间隔(2 ms) */

    

/********************输出端点描述符******************/

    0x07,   /* 端点描述符的长度为7字节 */

    USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: 端点描述符的类型为0x21*/

    0x01,   /* bEndpointAddress: 该端点(输出)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/

    0x03,   /* bmAttributes: 端点的属性为为中断端点 */

    0x40,   /* wMaxPacketSize: 该端点支持的最大包长度为64字节  */

    0x00,

    0x02,   /* bInterval: 轮询间隔(2 ms) */

}; 



  



  

从上面的代码中可以看出,USB设备使用了1个接口、两个端点:一个中断传输输入端点,端点号为2;一个中断传输的输出端点,端点号为1、每个端点能通讯的最大数据包长度为64字节、USB的功能自定义等。配置描述符是在USB主机发送GET_CONFIGURATION请求时,USB设备发送的。

还有一个很重要的当然报告描述符了:

/* HID的报告描述符*/

const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] = 

{

/*short Item   D7~D4:bTag;D3~D2:bType;D1~D0:bSize

**bTag ---主条目 1000:输入(Input) 1001:输出(Output) 1011:特性(Feature) 1010:集合(Collection) 1100:关集合(End Collection) 

** 全局条目 0000:用途页(Usage Page) 0001:逻辑最小值(Logical Minimum) 0010:逻辑最大值(Logical Maximum) 0011:物理最小值(Physical Minimum)

** 0100:物理最大值(Physical Maximum) 0101:单元指数(Unit Exponet) 0110:单元(Unit) 0111:数据域大小(Report Size)

** 1000:报告ID(Report ID) 1001:数据域数量(Report Count) 1010:压栈(Push) 1011:出栈(Pop) 1100~1111:保留(Reserved)

** 局部条目 0000:用途(Usage) 0001:用途最小值(Usage Minimum) 0010:用途最大值(Usage Maximum) 0011:标识符索引(Designator Index)

** 0100:标识符最小值(Designator Minimum) 0101:标识符最大值(Designator Maximum) 0111:字符串索引(String Index) 1000:字符串最小值(String Minimum)   

** 1001:字符串最大值(String Maximum) 1010:分隔符(Delimiter) 其他:保留(Reserved)

**bType---00:主条目(main)  01:全局条目(globle)  10:局部条目(local)  11:保留(reserved)

**bSize---00:0字节  01:1字节  10:2字节  11:4字节*/

//0x05:0000 01 01 这是个全局条目,用途页为ST页

0x05, 0x8c, /* USAGE_PAGE (ST Page) */ 

//0x09:0000 10 01 这是个局部变量,用途为Demo Kit

0x09, 0x01, /* USAGE (Demo Kit) */ 

//0xa1:1010 00 01 这是一个主条目,集合为应用集合

0xa1, 0x01, /* COLLECTION (Application) */ 

/* 输入报告*/ 

//0x09:0000 10 01 这是个局部条目,用途为厂商ID

0x09,0x03, // USAGE ID - Vendor defined 

//0x15:0001 01 01 这是个全局条目,逻辑最小值为0

0x15,0x00, // LOGICAL_MINIMUM (0) 

//0x26:0010 01 10 这是个全局条目,逻辑最大值为255

0x26,0x00, 0xFF, // LOGICAL_MAXIMUM (255) 

//0x75:0111 01 01 这是个全局条目,报告大小为8位

0x75,0x08, // REPORT_SIZE (8bit) 

//0x95:1001 01 01 这是个全局条目,报告数量为64

0x95,0x40, // REPORT_COUNT (64Byte) 

//0x81:1000 00 01 这是个主条目,做输入,Data表示这些数据可变,Var表示这些徐居于是独立的变量,Abs表示绝对值

0x81,0x02, // INPUT (Data,Var,Abs) 


/*输出报告*/ 

//0x09:0000 10 01 这是个局部条目,用途为厂商ID

0x09,0x04, // USAGE ID - Vendor defined 

//0x15:0001 01 01 这是个全局条目,逻辑最小值为0

0x15,0x00, // LOGICAL_MINIMUM (0) 

//0x26:0010 01 10 这是个全局条目,逻辑最大值为255

0x26,0x00,0xFF, // LOGICAL_MAXIMUM (255) 

//0x75:0111 01 01 这是个全局条目,报告大小为8位

0x75,0x08, // REPORT_SIZE (8bit) 

//0x95:1001 01 01 这是个全局条目,报告数量为64

0x95,0x40, // REPORT_COUNT (64Byte) 

//0x91:1001 00 01 这是个全局条目,做输出,Data表示这些数据可变,Var表示这些徐居于是独立的变量,Abs表示绝对值

推荐阅读

史海拾趣

Hama公司的发展小趣事

H&D Wireless公司成立于2009年,总部位于瑞典斯德哥尔摩。成立初期,公司专注于物联网技术的研发,致力于提供智能家居Wi-Fi组件和无线多媒体解决方案。2016年,H&D Wireless宣布获得了一笔300万美元的风险投资,投资方包括Blasieholmen Investment Group及旗下网络内的40名瑞典和欧洲企业家与私人投资家。这笔资金为公司后续的全球业务扩张和物联网云服务平台的发布奠定了坚实基础。

Apex Tool Group公司的发展小趣事

H&D Wireless始终将产品创新和客户服务放在首位。公司不断推出新的物联网解决方案,以满足不同行业客户的需求。同时,H&D Wireless还建立了完善的客户服务体系,为客户提供全方位的技术支持和解决方案咨询。这种以客户为中心的经营理念赢得了广大客户的信赖和支持,也为公司的持续发展奠定了坚实基础。

以上五个故事展示了H&D Wireless公司在电子行业中的发展历程和成就,体现了其在物联网领域的创新能力和市场竞争力。

安谱隆(Ampleon)公司的发展小趣事

2015年,随着恩智浦和飞思卡尔的合并,恩智浦决定将其射频业务剥离出来。这一决策旨在使两家公司能够更专注于各自的核心业务,同时也为射频业务的发展打开新的篇章。安谱隆作为这一剥离业务的承接者,应运而生。此次剥离不仅为安谱隆带来了丰富的技术资源和市场基础,也为其未来的发展奠定了坚实的基础。

GHI Electronics公司的发展小趣事

随着业务的不断拓展,GHI Electronics开始实施全球化战略。公司积极寻求与全球合作伙伴的合作机会,共同开拓新市场。同时,GHI Electronics还在多个国家和地区设立了分支机构或研发中心,以便更好地服务当地客户并快速响应市场需求。这种全球化战略布局不仅增强了GHI Electronics的市场竞争力,也为其带来了更多的商业机会和发展空间。

CHINFA公司的发展小趣事

在环保日益受到重视的今天,CHINFA公司积极响应国家号召,践行绿色环保理念。公司致力于研发和生产节能、环保的电子产品,通过技术创新和材料替代,减少了对环境的污染和破坏。同时,公司还加强了对生产废弃物的处理和回收利用,实现了资源的循环利用和可持续发展。

Amveco Toroidal Power Products公司的发展小趣事

随着电子行业的快速发展,客户对环形变压器的性能要求也越来越高。Amveco公司敏锐地捕捉到了这一市场变化,加大了在技术创新和产品升级上的投入。公司研发团队不断攻克技术难关,成功开发出了一系列具有更高效率、更低损耗的环形变压器产品,满足了客户对高性能产品的需求。同时,公司还不断优化生产工艺,提高了产品的可靠性和稳定性。

问答坊 | AI 解惑

几个常用的模拟开关

常见的模拟开关资料…

查看全部问答>

半波整流 — 滤波电路的两种不同解法

结合半波整流 — 滤波电路 ,分别采用牛顿 — 拉夫逊法和 PSPICE进行求解 ,均得出了正确的结果.经比较 ,两种方法各有所长.尤其是当电路中的非线性元件比较多 ,元件之间的非线性关系比较复杂 ,采用牛顿 — 拉夫逊法难以得到递推表达式时 ,用 PSPICE求 ...…

查看全部问答>

omap3530 BSP中的Display驱动

板子是OMAP3530,BSP是从网上下的TI_EVM_3530,BSP中Device Drivers中的VGA Linear Framebuffer只支持x86的cpu,请问我要想添加这个组件的话该怎么办?必须要自己写一个能支持arm的vga驱动吗?我装vs2005的时候只选了ARMV4I,是不是再装上x86部分就 ...…

查看全部问答>

汇编有些指令在开启分段前后所作的事是不是不一样?

比如像lss、lds之类的指令在分段前得到的段地址和段内偏移,分段后得到的段选择符和段内偏移。是不是这样理解的?这些指令能做到吗?段选择符能直接这样就能得到吗?(请见谅,我没有分数不能给分,我最近在看linux内核,知道的大哥发表些见解)…

查看全部问答>

一直在用 EVC做界面,感觉提高的很慢,做的东西也是老样子,界面界面再界面。怎么样能让自己提高的快些呢??

一直在做用 EVC做界面,感觉提高的很慢,做的东西也是老样子,界面界面再界面。怎么样能让自己提高的快些呢?? 平台式 WIN CE 5.0…

查看全部问答>

tcpmp编译错误???

错误信息为:fatal error C1083: Cannot open compiler intermediate file: \'C:\\DOCUME~1\\smc00618\\LOCALS~1\\Temp\\a02420ex\': Permission denied 那个大侠做过TCPMP的编译,帮我看看,我用的环境是EVC4.0+SP4+Smartphone SDK ,Active Proje ...…

查看全部问答>

使用微软的mediaplayer控件编写播放器的问题

各位大侠:     怎么才可以把微软的mediaplayer控件里面的微软会标,不显示出来。 或者说,怎么才能把mediaplayer输出的视频流,才我自己定义的窗口内播放呢?…

查看全部问答>

CRC校验码生成算法

前段时间写了一篇关于建议初学者求助时提供程序流程描述而非简单贴源代码的帖子,下面我给出一个具体的流程描述的例子,当然本例更注重于方法描述,故较为详细,作为流程描述可以适当简化。   1.预置1个16位的寄存器(下称CRC寄存器),初 ...…

查看全部问答>

SPI3,调试了一阵子依然没调通,期望大侠的指点.

两块板子,通过SPI3通讯,双线双向,一主一从,问题有一大堆:1.连接JLINK调试,发送数据可以被正确接收,发回的数据不正确(全是0xFF);2.去掉JLINK直接上电运行,收发都不行;另两个工程是同一个,只不过一个SPI3配置为主,一个配置为从,GPIO配 ...…

查看全部问答>

DIY简易太阳能热水器模型

  太阳能热水器已成为广泛普及的家庭用品。做一个太阳能热水器小制作模型,可以体验太阳能热水器收集太阳能,让太阳能服务人类的效益。   工具/原料:   纸板、锡纸、木片或木条、剪刀、手工锯、白胶、玻璃管或透明塑料管   步骤: ...…

查看全部问答>