这里我主要说一下如何做一个USB下位机,这里主要分3部分:1、建立工程;2、添加报文描述符;3、数据的传输。这里就不讲USB的理论知识了,有想要了解的自行百度一下就可以了。

1、首先打开USB

2、接着把USB设置为下图HID模式

3、选择外部时钟

4、配置时钟树

5、配置USB设置

下面的USB设置就有点讲究了,

6、确认后,生成代码

添加报文描述符:

这里只是建立了一个工程,你编译不会有错,不过PC还是无法识别你这个是USB设备的,因为缺少了关键的报文描述符,这里我就说一下报文描述符怎么写,这个你可以直接用HID descriptor Tool软件生成,如下图,下图就是一个报文的基本要素了,该描述符主要作用是告诉PC机一下USB的信息,简单来说就是告诉PC我有什么用。

这里我说一下这个报文的要素,第一部分你可以理解为报文头;第二部分你可以理解为USB告诉PC机问我要做什么,这里主要告诉PC机我要做一个接收与发送的设备;第三部分是结束标志。

============================================================

下面我插入一个对报文描述符进行一个简单的描述:

标签解析举例(Usage :0x50, 0x01):

(Usage Page标签0x0?)0x05=0000 0101 :

0x05表示前缀,0x01为数据部分,0x05转换成二进制,就是0000 01 01,按照HID类协议5.3 generic item format的定义,这个字节被分成3个部分:

bit0~bit1代表的是这个前缀后面跟的数据长度,这里就是后面0x01的长度,两位可以表示最大4字节的数据,即bsize

bit2~bit3代表的是这个前缀的类型,总共可以有三种类型:0=main,1=global,2=local,3=reserved;

bit4~bit7代表前tag,一般分为input(二进制的1000 00 nn,即bit4~bit7=1000,代表一个tag,bit2~bit3=00,代表main,bit0~bit1=nn,代表这个前缀后面还有nn所代表的数据),output(二进制的 1001 00 nn),feature(1011 00 nn),collection(1010 00 nn),end collection(1100 00 nn)

即:

0000:Usage Page

01: bType,全局(bType=0:主项目;bType=1:全局项目;bType=2:区域项目)

01:bSzie,1字节(bSzie为项目所需数据字节数目,bSzie可为1、2、4,注意bSzie不可为3)

(Page ID)0x01: 表示该Page为Generalic Desktop Controls(Usage ID 0为保留。ID 1到0x1F为”top level” collection保留,这些ID虽然对于Application不是必须,但可以用于识别通用设备类型)

Usage = (usage page:usage ID):其将数据的操控与它的用途作一对一的对应,所以解读报告后就可以知道每个数据作何种操作。所以“传输的数据”和“操作”只是一事件的两种描述方式。用途是以一个32位卷标(称作usage tag)来表示,高16位称作usage page(用途类页),低16位称为usage ID(用途识别名),文件universal serial Bus HID Usage Table完整列出所有的usage pages(用途类页)和usage ID(用途识别名),使用者必须遵照文件的规范来声明操作的用途。用途卷标只是报告描述符诸多标签的一个,利用这些卷标取可以清楚完整的描述符操作的用途。

这里其实说的并不清楚,因为作者有点懒就不细说了,想了解更多的可以百度《圈圈教你玩USB》,这里说的比我好。

==============================================================================

言归正传,我们生成了报文描述符后,保存为.h文件,我对这些报文做了一些很浅显的解析,可能表达不够准确,打开可以看到如下代码所示:

char ReportDescriptor[34] ={   //这里34就是前面建立工程第五点说的,报文描述符大小0x06, 0x00, 0xff, //USAGE_PAGE (Vendor Defined Page 1) 表示一个报文标签之类的用途类页

0x09, 0x01, //USAGE (Vendor Usage 1) 表示一个报告ID标志

0xa1, 0x01, //COLLECTION (Application) 表示应用集合,要以下面最后的0xc0结束它

0x09, 0x01, //USAGE (Vendor Usage 1)同下同名解析

0x15, 0x00, //LOGICAL_MINIMUM (0) 同下同名解析

0x26, 0xff, 0x00, //LOGICAL_MAXIMUM (255) 同下同名解析

0x95, 0x40, //REPORT_COUNT (64)  同下REPORT_COUNT

0x75, 0x08, //REPORT_SIZE (8)   同下REPORT_SIZE

0x81, 0x02, //INPUT (Data,Var,Abs) 表示USB要输入数据到PC的功能

0x09, 0x01, //USAGE (Vendor Usage 1) 每个功能的一个卷标志

0x15, 0x00, //LOGICAL_MINIMUM (0) 表示每个传输数据限定为0

0x26, 0xff, 0x00, //LOGICAL_MAXIMUM (255) 表示每个传输数据的最大值限定为255

0x95, 0x40, //REPORT_COUNT (64) 每次接收的数据长度,这里是64位

0x75, 0x08, //REPORT_SIZE (8) 传输字段的宽度为8bit,表示每个传输的数据范围为0~ffff ffff

0x91, 0x02, //OUTPUT (Data,Var,Abs) 表示USB设备要接收PC的数据的功能

0xc0 //END_COLLECTION  结束标志

};

到这里我们就可以下一步了,打开刚才建立的工程,在 usbd_custom_hid_if.c 文件里,找到 CUSTOM_HID_ReportDesc_FS 这个函数,把刚才生成的报文文件覆盖掉函数里面的文件,代码如下:

/** Usb HID report descriptor.*/__ALIGN_BEGINstatic uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END ={0x06, 0x00, 0xff, //USAGE_PAGE (Vendor Defined Page 1)

0x09, 0x01, //USAGE (Vendor Usage 1)

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

0x09, 0x01, //USAGE (Vendor Usage 1)

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

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

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

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

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

0x09, 0x01, //USAGE (Vendor Usage 1)

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

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

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

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

0x91, 0x02, //OUTPUT (Data,Var,Abs)

0xc0 //END_COLLECTION

};

然后再修改将usbd_conf.h做对应修改:

#define USBD_CUSTOMHID_OUTREPORT_BUF_SIZE      64

#define USBD_CUSTOM_HID_REPORT_DESC_SIZE       34

同时修改usbd_customhid.h文件中的发送与接收长度为64

#define CUSTOM_HID_EPIN_SIZE                  0x40

#define CUSTOM_HID_EPOUT_SIZE              0x40

到这里基本就算做出一个USB设备了,我们编译下载程序看看。

如果电脑显示了这个USB设备,但是有黄色感叹号,说明USB枚举成功,可是驱动安装失败,这时我们可以libusb自带的inf-wizard工具生成USB驱动程序,要怎么安装驱动解决这个问题可以参考驱动安装:https://blog.csdn.net/niepangu/article/details/44984325,驱动安装成功的话,到这里我们就可以看到USB正常列举出来啦,下面我们就可以写USB数据收发代码了。

关于数据传输,HID设备是采用轮询方式传输的,ST默认20ms速度实在不敢恭维,还得要改一下枚举时的声明,同样是usbd_customhid.c文件,

__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END ={

....

....0x20, /*bInterval: Polling Interval (20 ms)*/

/*34*/....

....0x20,/*bInterval: Polling Interval (20 ms)*/

/*41*/}

这两个地方随心来改,最小可以改到0x01。这就快很多啦。到这里数据传输准备工作就做好了,下面我们先来说一下USB发送:

先定义个发送BUFF:

uint8_t send_buf[64] = {//定义一个USB的发送BUFF

1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64};

再包括发送函数头文件以及声明一个外部定义:

#include "usbd_customhid.h" //包括发送函数头文件

extern USBD_HandleTypeDef hUsbDeviceFS; //外部声明USB发送函数

现在可以在main函数里添加发送代码了,我这里设置按一下按键就发送一次,同时led亮1s:

while (1)

{

/*USER CODE BEGIN 3*/

if(HAL_GPIO_ReadPin(GPIOE,k1_Pin)==0)

{//按键消抖

while(HAL_GPIO_ReadPin(GPIOE,k1_Pin)==0);//点亮指示灯

HAL_GPIO_WritePin(GPIOE,led1_Pin,GPIO_PIN_RESET);//USB发送数据

USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, send_buf, sizeof(send_buf));

//实际发送到上位机的数据有65个,一个报文ID:0 + 64个send_buf 数据,一共65个数据到上位机

HAL_Delay(1000);//关闭指示灯

HAL_GPIO_WritePin(GPIOE,led1_Pin,GPIO_PIN_SET);

}

}

发送函数就是这么简单,接下来说接收函数了,接收函数是中断接收的,建立工程时已经默认开启了。下面就说一下USB接收:

先在main.c定义个接收BUFF:

unsigned char USB_Recive_Buffer[64]; //USB接收缓存

unsigned char USB_Received_Count = 0;//USB接收数据计数

打开usbd_custom_hid_if.c文件,添加外部声明:

extern unsigned char USB_Recive_Buffer[64];extern unsigned char USB_Received_Count;

接着在usbd_custom_hid_if.c文件中找到 static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state) 函数,当USB接收完数据后,就会进入到这个函数,我们可以在这里添加接收函数,把函数修改如下:

/**

* @brief Manage the CUSTOM HID class events

* @param event_idx: Event index

* @param state: Event state

* @retval USBD_OK if all operations are OK else USBD_FAIL*/

//当芯片完成一组数据接收的时候,中断会调用CUSTOM_HID_OutEvent_FS这个回调函数

staticint8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)

{/*USER CODE BEGIN 6*/

chari;

HAL_GPIO_TogglePin(GPIOE,led1_Pin);/*查看接收数据长度

USB_Received_Count = USBD_GetRxCount( &hUsbDeviceFS,CUSTOM_HID_EPOUT_ADDR );

printf("USB_Received_Count = %d \r\n",USB_Received_Count);

USB_Received_Count = USBD_GetRxCount( &hUsbDeviceFS,CUSTOM_HID_EPIN_ADDR );

printf("USB_Received_Count_in = %d \r\n",USB_Received_Count);*/USBD_CUSTOM_HID_HandleTypeDef*hhid; //定义一个指向USBD_CUSTOM_HID_HandleTypeDef结构体的指针

hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;//得到USB接收数据的储存地址

for(i=0;i<64;i++)

{

USB_Recive_Buffer[i]=hhid->Report_buf[i]; //把接收到的数据送到自定义的缓存区保存(Report_buf[i]为USB的接收缓存区)

//printf("USB_Recive_Buffer[%d] = 0x%x \r\n",i,USB_Recive_Buffer[i]); //打印接收到的信息,确认是否正确,调试用

}returnUSBD_OK;/*USER CODE END 6*/}

这样就 USB_Recive_Buffer[i] 里面就保存到接收到的数据了,用Bus Hound可以看到已经成功啦。

cubemx 读卡器_STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发相关推荐

  1. 【STM32+cubemx】0017 HAL库开发:usb HID鼠标设备实现

    Cubemx可以生成多种usb设备的初始化代码,本节我们就使用它来生成一个usb HID鼠标的工程,在开发板上运行以后,可以模拟usb鼠标的功能. 1)生成USB HID工程 仍然使用串口工程,如下图 ...

  2. C++ MFC界面读写USB HID设备数据程序

    C++ MFC界面读写USB HID设备数据程序 发一个简单易用的界面,用来对USB HID设备(比如说游戏手柄,控制面板等)读写数据,一般情况下面板上有一些LED,可以帮助我们测试读写是否正确.另外 ...

  3. 嵌入式linux作为hid设备,基于嵌入式系统的USB(HID)设备

    基于嵌入式系统的USB(HID)设备 目前嵌入式系统在数字化电子产品领域应用越来越广泛.随着其成本的降低,大有取代单片机的趋势. USB设备以其小巧.便携.即插即用.成本低廉等优势在当前的桌面应用中有 ...

  4. hid python uhf_简单的读写Python中的USB HID设备?

    我有一个相当简单的USB HID设备,我一直在试图弄清楚如何读取和写入使用Python.我已经能够使用PyWinUSB从它读取,但是当我尝试写入它时,问题就出现了.试图写入它会使事情爆炸.简单的读写P ...

  5. C# 访问USB(HID)设备方法一

    这几天捣腾C# 访问USB(HID)设备的方式,看了好多资料,发现有两种方式访问USB设备,废话少说直接看核心代码 h = Win32Wrapper.SetupDiGetClassDevs(IntPt ...

  6. 实践 C++ MFC界面读写USB HID设备数据程序

    在开发了一个多线程USB烧写程序后,得到新的任务,要将HID的内容放到里面,没有人带着做细致的讲解,靠自己自生自灭: 1, 什么是HID? (项目要的急,没有什么了解就开始了!只知道,在Windows ...

  7. USB HID 设备类协议入门

    一.应用场合 USB HID类是比较大的一个类,HID类设备属于人机交互操作的设备.用于控制计算机操作的一些方面,如USB鼠标,USB键盘,USB游戏操纵杆,USB触摸板,USB轨迹球.电话拨号设备. ...

  8. hid设备驱动linux,linux usb hid设备驱动(3)

    本文分析了蓝牙bluez协议栈中HID协议的实现. 1. 基本概念 HID协议用于人机输入设备.Bluez中关于HID的实现代码在其根目录下的input目录.蓝牙规范中包含关于HID的profile, ...

  9. c# hid usb 卡死_C# 访问USB(HID)设备

    //代码暂时没有整理,传入参数是设备序号,//有些USB设备其实有很多HID设备,就是一个接口上有几个设备,这个时候需要//用index++来逐个循环,直到获取设备返回false后,跳出去,把获取的设 ...

最新文章

  1. AlphaGo Zero,一次成功的炒作而已?
  2. Java 流(Stream)、文件(File)和IO -- Java ByteArrayOutputStream类
  3. linux java 权限不够_​Linux安装jdk时提示权限不够怎么解决
  4. 严格模式 华为_华为千元机虽然好,但是千万别乱选,这三款才是内行人的最爱...
  5. 容器编排技术 -- Kubernetes设计架构
  6. 项目发布Debug和Release版的区别
  7. 访问控制列表(二)配置命令
  8. 14005.xilinx移植spi版本flash
  9. SpringBoot项目中图片的引用
  10. 通过F12控制台退订育碧(Ubisoft)邮件
  11. 第一个Eureka Service
  12. AI芯片发展的前世今生
  13. 什么是即席查询及即席查询实现
  14. 其实我(微笑哥)是个正经男人!
  15. zcmu-1359 NBA (水题)
  16. php 禁止抓取,禁止抓取.php的写法哪个对:Disallow: /*.php$和Disallow: /.php$ - 搜外SEO问答...
  17. snes9x 源码_仅64kb的SNES游戏如何制作优美的音乐
  18. 原型图都可以用什么软件做?分享这9款给你
  19. PAT 1006 换个格式输出整数 (15 分)(Java)
  20. UT8 编码引发的血案

热门文章

  1. 使用Modernizr进行兼容性检查
  2. EJB应用服务器集群技术分析
  3. 下四国的排长和下四国的小兵 (转载)
  4. 视频智能分析系统EasyCVR视频流媒体安防监控云服务实现城市视频智能化应用
  5. 四、数据透视表公式与函数
  6. 【前端】Vue体系(四)
  7. 中国IT领袖峰会首度在深举行
  8. UnicodeConverteUtil: Java unicode 与 中文相互转换
  9. 电信4g网速测试软件,编辑京城实地亲测 电信4G网速究竟如何
  10. linux服务器更改欢迎界面