该文档使用USB固件库,在其基础上进行了自己的定制,完成了一个USB-HID设备,首先是usb_desc.c文件,里面存放了usb各种描述符的存在

#include "usb_desc.h"

//usb标准设备描述符

const u8 DinkUsbDeviceDescriptor[DINK_USB_SIZ_DEVICE_DESC] = {

USB_DEVICE_DESC_SIZE,             //bLength字段。设备描述符的长度为18(0x12)字节

USB_DEVICE_DESCRIPTOR_TYPE,           //bDescriptorType字段。设备描述符的编号为0x01

WBVAL(0x0200),                       //bcdUSB字段。这里设置版本为USB1.1,即0x0110。

0x00,                             //bDeviceClass字段。我们不在设备描述符中定义设备类,

0x00,                              //bDeviceSubClass字段。bDeviceClass字段为0时,该字段也为0。

0x00,                              //bDeviceProtocol字段。bDeviceClass字段为0时,该字段也为0。

0x40,                               //bMaxPacketSize0字段。端点0的最大包长度。

WBVAL(0x7777),                       //idVender字段。厂商ID号,我们这里取0x8888,仅供实验用。

WBVAL(0x8888),                       //idProduct字段。产品ID号,由于是第一个实验,我们这里取0x0001。\。

WBVAL(0x0100),                     // 设备的版本

0x01,                             //iManufacturer字段。厂商字符串的索引值,为了方便记忆和管理

0x02,                             //iProduct字段。产品字符串的索引值。刚刚用了1,这里就取2吧。

0x03,                              //iSerialNumber字段。设备的序列号字符串索引值。

0x01                                //bNumConfigurations字段。该设备所具有的配置数。

};

//USB报告描述符的定义

const u8 HID_ReportDescriptor[]=

{

0x06,0xA0,0xFF,//用法页(FFA0h, vendor defined)

0x09, 0x01,//用法(vendor defined)

0xA1, 0x01,//集合(Application)

0x09, 0x02 ,//用法(vendor defined)

0xA1, 0x00,//集合(Physical)

0x06,0xA1,0xFF,//用法页(vendor defined)

//输入报告

0x09, 0x03 ,//用法(vendor defined)

0x09, 0x04,//用法(vendor defined)

0x15, 0x80,//逻辑最小值(0x80 or -128)

0x25, 0x7F,//逻辑最大值(0x7F or 127)

0x35, 0x00,//物理最小值(0)

0x45,0xFF,//物理最大值(255)

0x75, 0x08,//报告长度Report size (8位)

0x95, 0x40,//报告数值(64 fields)

0x81, 0x02,//输入(data, variable, absolute)

//输出报告

0x09, 0x05,//用法(vendor defined)

0x09, 0x06,//用法(vendor defined)

0x15, 0x80,//逻辑最小值(0x80 or -128)

0x25, 0x7F,//逻辑最大值(0x7F or 127)

0x35, 0x00,//物理最小值(0)

0x45,0xFF,//物理最大值(255)

0x75,0x08,//报告长度(8位)

0x95, 0x40,//报告数值(64 fields)

0x91, 0x02,//输出(data, variable, absolute)

0xC0,//集合结束(Physical)

0xC0//集合结束(Application)

};

//通过上面的报告描述符的定义,我们知道返回的输入报告具有8字节。

//输出报告也有64字节。至于这64字节的数据是干什么用的,就要由用户

//自己来决定了。

///报告描述符完毕

//usb配置描述符

const u8 DinkUsbConfigDescriptor[DINK_USB_SIZ_CONFIG_DESC] = {

/***************配置描述符***********************/

USB_CONFIGUARTION_DESC_SIZE,       //bLength字段。配置描述符的长度为9字节。

USB_CONFIGURATION_DESCRIPTOR_TYPE, //bDescriptorType字段。配置描述符编号为0x02。

//wTotalLength字段。配置描述符集合的总长度,

//包括配置描述符本身、接口描述符、类描述符、端点描述符等。

WBVAL(

USB_CONFIGUARTION_DESC_SIZE +           //配置描述符

USB_INTERFACE_DESC_SIZE     +        //接口1描述符

9                           +           //hid描述符

USB_ENDPOINT_DESC_SIZE      +           //端点描述符

USB_ENDPOINT_DESC_SIZE                  //端点描述符

),

0x01,                                   //bNumInterfaces字段。该配置包含的接口数,只有一个接口。

0x01,                                   //bConfiguration字段。该配置的值为1。

0x00,                                  //iConfigurationz字段,该配置的字符串索引。这里没有,为0。

USB_CONFIG_BUS_POWERED ,                //bmAttributes字段,该设备的属性

USB_CONFIG_POWER_MA(500),                  //bMaxPower字段,该设备需要的最大电流量

/*********************第一个接口描述符,hid设备**********************/

USB_INTERFACE_DESC_SIZE,              //bLength字段。接口描述符的长度为9字节。

USB_INTERFACE_DESCRIPTOR_TYPE,           //bDescriptorType字段。接口描述符的编号为0x04。

0x00,                                  //bInterfaceNumber字段。该接口的编号,第一个接口,编号为0。

0x00,                                  //bAlternateSetting字段。该接口的备用编号,为0。

0x02,                                   //bNumEndpoints字段。非0端点的数目。该接口有2个批量端点

USB_DEVICE_CLASS_HUMAN_INTERFACE,       //bInterfaceClass字段。该接口所使用的类。大容量存储设备接口类的代码为0x08。,

0x00,                                   //bInterfaceSubClass字段。该接口所使用的子类。在HID1.1协议中,

//只规定了一种子类:支持BIOS引导启动的子类。

//USB键盘、鼠标属于该子类,子类代码为0x01。

//但这里我们是自定义的HID设备,所以不使用子类。

0x00,                                   //bInterfaceProtocol字段。如果子类为支持引导启动的子类,

//则协议可选择鼠标和键盘。键盘代码为0x01,鼠标代码为0x02。

//自定义的HID设备,也不使用协议。

0x00,                                   //iConfiguration字段。该接口的字符串索引值。这里没有,为0。

/*********************HID报告描述符*************************/

//bLength字段。本HID描述符下只有一个下级描述符。所以长度为9字节。

0x09,

//bDescriptorType字段。HID描述符的编号为0x21。

0x21,

//bcdHID字段。本协议使用的HID1.1协议。注意低字节在先。

0x10,

0x01,

//bCountyCode字段。设备适用的国家代码,这里选择为美国,代码0x21。

0x21,

//bNumDescriptors字段。下级描述符的数目。我们只有一个报告描述符。

0x01,

//bDescriptorType字段。下级描述符的类型,为报告描述符,编号为0x22。

0x22,

//bDescriptorLength字段。下级描述符的长度。下级描述符为报告描述符。

sizeof(HID_ReportDescriptor)&0xFF,

(sizeof(HID_ReportDescriptor)>>8)&0xFF,

/*********************端点描述符**********************************/

/* 端点描述符 */

USB_ENDPOINT_DESC_SIZE,                //bLength字段。端点描述符长度为7字节。

USB_ENDPOINT_DESCRIPTOR_TYPE,            //bDescriptorType字段。端点描述符编号为0x05。

USB_ENDPOINT_IN(1),                     //bEndpointAddress字段。端点的地址。我们使用D12的输入端点1。

USB_ENDPOINT_TYPE_INTERRUPT,              //bmAttributes字段。D1~D0为端点传输类型选择。

WBVAL(0x0040),                           //wMaxPacketSize字段。该端点的最大包长。最大包长为64字节。

0x01,                                        //bInterval字段。端点查询的时间,端点查询的时间,此处无意义。

/***********************端点描述符*******************************************/

USB_ENDPOINT_DESC_SIZE,                //bLength字段。端点描述符长度为7字节。

USB_ENDPOINT_DESCRIPTOR_TYPE,            //bDescriptorType字段。端点描述符编号为0x05。

USB_ENDPOINT_OUT(1),                    //bEndpointAddress字段。端点的地址。我们使用D12的输入端点1。

USB_ENDPOINT_TYPE_INTERRUPT,              //bmAttributes字段。D1~D0为端点传输类型选择。

WBVAL(0x0040),                           //wMaxPacketSize字段。该端点的最大包长。最大包长为64字节。

0x01,                                        //bInterval字段。端点查询的时间,端点查询的时间,此处无意义。

};

/************************语言ID的定义********************/

const u8 DinkUsbLanguageId[DINK_USB_SIZ_STRING_LANGID]=

{

0x04, //本描述符的长度

0x03, //字符串描述符

//0x0409为美式英语的ID

0x09,

0x04

};

语言ID完毕//

//Unicode 字符串描述符

//邓小俊的usb鼠标

const u8 DinkUsbManufacturerStringDescriptor[DINK_USB_SIZ_STRING_VENDOR]=

{

32,         //该描述符的长度为32字节

0x03,       //字符串描述符的类型编码为0x03

0x44, 0x00, //D

0x49, 0x00, //I

0x4e, 0x00, //N

0x4b, 0x00, //K

0x5f, 0x00, //_

0x48, 0x00, //H

0x49, 0x00, //I

0x44, 0x00, //D

0x5f, 0x00, //_

0x44, 0x00, //D

0x45, 0x00, //E

0x56, 0x00, //V

0x49, 0x00, //I

0x43, 0x00, //C

0x45, 0x00  //E

};

/厂商字符串结束/

//产品字符串描述符

const u8 DinkUsbProductStringDescriptor[DINK_USB_SIZ_STRING_PRODUCT]=

{

32,         //该描述符的长度为32字节

0x03,       //字符串描述符的类型编码为0x03

0x44, 0x00, //D

0x49, 0x00, //I

0x4e, 0x00, //N

0x4b, 0x00, //K

0x5f, 0x00, //_

0x48, 0x00, //H

0x49, 0x00, //I

0x44, 0x00, //D

0x5f, 0x00, //_

0x44, 0x00, //D

0x45, 0x00, //E

0x56, 0x00, //V

0x49, 0x00, //I

0x43, 0x00, //C

0x45, 0x00  //E

};

产品字符串结束

//字符串“2008-07-07”的Unicode编码

//8位小端格式

const u8 DinkUsbSerialNumberStringDescriptor[DINK_USB_SIZ_STRING_SERIAL]={

22,         //该描述符的长度为22字节

0x03,       //字符串描述符的类型编码为0x03

0x32, 0x00, //2

0x30, 0x00, //0

0x31, 0x00, //1

0x35, 0x00, //5

0x2d, 0x00, //-

0x30, 0x00, //0

0x33, 0x00, //3

0x2d, 0x00, //-

0x32, 0x00, //2

0x31, 0x00  //1

};

//产品序列号字符串结束/

//产品序列号

u8 DinkUsbStringSerialUniqueId[DINK_USB_SIZ_STRING_SERIAL_UNIQUE_ID] =

{

DINK_USB_SIZ_STRING_SERIAL_UNIQUE_ID,        //描述符长度

0x03                                                      //描述符类型编码

/* Serial number该编码将会

可以通过修改该文件实现不同的设备,第二是usb_prop.c文件,定义了一系列的回调函数,在usb枚举阶段使用

#include "usb_prop.h"

u32 ProtocolValue;

//表明有多少端点,多少种配置

DEVICE Device_Table =

{

EP_NUM,

1

};

//static u8 s_Request = 0;//记录当前请求值

//设备描述符

ONE_DESCRIPTOR Device_Descriptor =

{

(u8*)DinkUsbDeviceDescriptor,

DINK_USB_SIZ_DEVICE_DESC

};

//配置描述符

ONE_DESCRIPTOR Config_Descriptor =

{

(u8*)DinkUsbConfigDescriptor,

DINK_USB_SIZ_CONFIG_DESC

};

//报告描述符

ONE_DESCRIPTOR DinkUsb_Report_Descriptor =

{

(u8*)HID_ReportDescriptor,

HID_ReportDescSize

};

//报告描述符

ONE_DESCRIPTOR DinkUsb_Hid_Descriptor =

{

(u8*)(DinkUsbConfigDescriptor+9),

9

};

//字符串描述符

ONE_DESCRIPTOR String_Descriptor[4] =

{

{(u8*)DinkUsbLanguageId, DINK_USB_SIZ_STRING_LANGID},

{(u8*)DinkUsbManufacturerStringDescriptor, DINK_USB_SIZ_STRING_VENDOR},

{(u8*)DinkUsbProductStringDescriptor, DINK_USB_SIZ_STRING_PRODUCT},

{(u8*)DinkUsbSerialNumberStringDescriptor, DINK_USB_SIZ_STRING_SERIAL}

};

//USB过程处理函数数组

DEVICE_PROP Device_Property =

{

DinkUsbInit,

DinkUsbReset,

DinkUsbStatus_In,

DinkUsbStatus_Out,

DinkUsbData_Setup,

DinkUsbNoDataSetup,

DinkUsbGetInterfaceSetting,

DinkUsbGetDeviceDescriptor,

DinkUsbGetConfigDescriptor,

DinkUsbGetStringDescriptor,

0,

0x40 /*MAX PACKET SIZE*/

};

//usb标准数据请求结构体

//只实现了两个,剩下的用nop方式解决了

USER_STANDARD_REQUESTS User_Standard_Requests =

{

DinkUsbGetConfiguration,

DinkUsbSetConfiguration,

DinkUsbGetInterface,

DinkUsbSetInterface,

DinkUsbGetStatus,

DinkUsbClearFeature,

DinkUsbSetEndPointFeature,

DinkUsbSetDeviceFeature,

DinkUsbSetDeviceAddress

};

//设备初始化

void DinkUsbInit(void)

{

Get_SerialNum();        //构建字符串描述符

pInformation->Current_Configuration = 0;      //当前选择的配置为0

PowerOn();          //连接USB

_SetISTR(0);

wInterrupt_Mask = IMR_MSK;

_SetCNTR(wInterrupt_Mask);

bDeviceState = UNCONNECTED;   //设备状态初始化为未连接状态

usb_debug_printf("USB Init\r\n");

}

//设备复位

void DinkUsbReset(void)

{

Device_Info.Current_Configuration = 0;  //选择当前配置为0

pInformation->Current_Feature = DinkUsbConfigDescriptor[7]; //获取配置描述符中当前设备属性

pInformation->Current_Interface = 0;//设置当前设备接口

SetBTABLE(BTABLE_ADDRESS);//设置缓冲区地址

SetEPType(ENDP0, EP_CONTROL);//控制端点

SetEPTxStatus(ENDP0, EP_TX_STALL);

SetEPRxAddr(ENDP0, ENDP0_RXADDR);//设置端点缓冲区地址

SetEPTxAddr(ENDP0, ENDP0_TXADDR);

Clear_Status_Out(ENDP0);

SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);//设置接收最大长度

SetEPRxValid(ENDP0);

SetEPType(ENDP1, EP_INTERRUPT);//初始化端点1为中断传输模式,用来报告一些状态

SetEPTxAddr(ENDP1, ENDP1_TXADDR);//设置端点地址

SetEPRxAddr(ENDP1, ENDP1_RXADDR);//设置端点地址

SetEPRxStatus(ENDP1, EP_RX_VALID);//使能接收

SetEPTxStatus(ENDP1, EP_TX_NAK);  //不使能发送

SetEPRxCount(ENDP1, 64);//设置接收最大长度

Clear_Status_Out(ENDP1);

bDeviceState = ATTACHED;//设备插入

SetDeviceAddress(0);//设置当前地址为0

usb_debug_printf("USB Reset\r\n");

}

//不知道干嘛的

void DinkUsbStatus_In(void)

{

return;

}

//不知道干嘛的

void DinkUsbStatus_Out(void)

{

return;

}

u8 *DinkUsbGetReportDescriptor(u16 Length)

{

usb_debug_printf("获取报告描述符\r\n");

return Standard_GetDescriptorData(Length, &DinkUsb_Report_Descriptor);

}

u8 *DinkUsbGetHIDDescriptor(u16 Length)

{

usb_debug_printf("获取HID述符\r\n");

return Standard_GetDescriptorData(Length, &DinkUsb_Hid_Descriptor);

}

u8 *DinkUsbGetProtocolValue(u16 Length)

{

usb_debug_printf("获取协议\r\n");

if (Length == 0)

{

pInformation->Ctrl_Info.Usb_wLength = 1;

return NULL;

}

else

{

return (u8 *)(&ProtocolValue);

}

}

RESULT DinkUsbData_Setup(u8 RequestNo)

{

u8 *(*CopyRoutine)(u16);

CopyRoutine = NULL;

if ((RequestNo == GET_DESCRIPTOR)

&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))

&& (pInformation->USBwIndex0 == 0))

{

//获取报告描述符

if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)

{

CopyRoutine = DinkUsbGetReportDescriptor;

}

//获取HID描述符

else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)

{

CopyRoutine = DinkUsbGetHIDDescriptor;

}

}

/*** GET_PROTOCOL ***/

else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))

&& RequestNo == GET_PROTOCOL)

{

CopyRoutine = DinkUsbGetProtocolValue;//获取协议值

}

if (CopyRoutine == NULL)

{

return USB_UNSUPPORT;

}

pInformation->Ctrl_Info.CopyData = CopyRoutine;

pInformation->Ctrl_Info.Usb_wOffset = 0;

(*CopyRoutine)(0);

return USB_SUCCESS;

}

RESULT DinkUsbSetProtocol(void)

{

u8 wValue0 = pInformation->USBwValue0;

ProtocolValue = wValue0;

return USB_SUCCESS;

}

RESULT DinkUsbNoDataSetup(u8 RequestNo)

{

if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))

&& (RequestNo == SET_PROTOCOL))

{

usb_debug_printf("设置协议\r\n");

return DinkUsbSetProtocol();

}

else

{

return USB_UNSUPPORT;

}

}

RESULT DinkUsbGetInterfaceSetting(u8 Interface, u8 AlternateSetting)

{

if (AlternateSetting > 0)//配置数量

{

usb_debug_printf("设置配置\r\n");

return USB_UNSUPPORT;

}

else if (Interface > 1)//接口数量

{

usb_debug_printf("设置接口\r\n");

return USB_UNSUPPORT;

}

return USB_SUCCESS;

}

//获取设备描述符

u8 *DinkUsbGetDeviceDescriptor(u16 Length)

{

usb_debug_printf("获取设备描述符\r\n");

return Standard_GetDescriptorData(Length, &Device_Descriptor);

}

//配置描述符

u8 *DinkUsbGetConfigDescriptor(u16 Length)

{

usb_debug_printf("获取配置描述符\r\n");

return Standard_GetDescriptorData(Length, &Config_Descriptor);

}

//字符串描述符

u8 *DinkUsbGetStringDescriptor(u16 Length)

{

u8 wValue0 = pInformation->USBwValue0;

usb_debug_printf("获取字符串描述符 %d\r\n",wValue0);

if (wValue0 > 4)

{

return NULL;

}

else

{

return Standard_GetDescriptorData(Length, &String_Descriptor[wValue0]); //返回字符串描述符

}

}

//将设备状态上传到配置数据中

void DinkUsbSetConfiguration(void)

{

DEVICE_INFO *pInfo = &Device_Info;

usb_debug_printf("设置配置\r\n");

if (pInfo->Current_Configuration != 0)

{

bDeviceState = CONFIGURED;

}

}

//将地址设置上传

void DinkUsbSetDeviceAddress (void)

{

usb_debug_printf("设置地址\r\n");

bDeviceState = ADDRESSED;

}

其中最核心的两个函数分别是复位和初始化,复位的时候要将端点配置好,并且接受最好要使能,否则无法接收数据(后期自己使能也可以),然后就是端点的处理函数了usb_endp.c

#include "usb_endp.h"

//发送完成置1 发送未完成置0

u8 sendOk = 1;

//接收到数据该设置为1,数据处理完成之后修改为0

u8 ReceiveOk = 0;

void EP1_IN_Callback(void)

{

//设备向主机发送数据的回调函数

sendOk = 1;//发送成功为1

SetEPTxStatus(ENDP1, EP_TX_NAK);//发送成功等待第二次设置为valid

}

void EP1_OUT_Callback(void)

{

//接收了一次数据之后等待数据处理,将接受响应设置为NAK

//处理完成之后再设置为VALID

SetEPRxStatus(ENDP1, EP_RX_NAK);//NAK接收

ReceiveOk = 1;//有数据标志为1

}

要想使能这些函数,需要将端点响应函数打开

另外,单片机应当来处理或者发送数据,依靠usb_data_process.h文件完成

#include "usb_data_process.h"

//HID发送数据

//返回1发送失败 返回0发送成功

u8 HID_Send_Data(u8* buffer,u8 length)

{

if(sendOk == 1)

{

if(length == 0)

{

SetEPTxStatus(ENDP1, EP_TX_NAK);//不发送

}

else

{

UserToPMABufferCopy(buffer, GetEPTxAddr(ENDP1), length);

SetEPTxCount(ENDP1, length);

SetEPTxValid(ENDP1);//使能发送

sendOk = 0;//设置发送未完成状态,等待发送回调函数将数据发送到主机

}

return 0;

}

else

{

return 1;//上一次的数据还没发送出去,所以这次发送失败

}

}

//HID接收数据处理

u8 HID_Receive_Data(u8* buffer)

{

u16 length = 0;//获取接收到的数据长度

u8 i = 0;

if(ReceiveOk == 1)//有数据

{

length = GetEPRxCount(ENDP1);

if(length == 0)return 0;

else

{

PMAToUserBufferCopy(buffer, GetEPRxAddr(ENDP1), length);

SetEPRxValid(ENDP1);//使能接收

ReceiveOk = 0;

printf("hid receive : ");

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

{

printf("%c ",buffer[i]);

}

printf("\r\n");

return length;//返回接收到的数据

}

}

else

{

//没有数据,直接为0

return 0;

}

}

做好这里,基本上就能实现通讯了,详细工程请查看文章最后的链接

http://download.csdn.net/detail/dengrengong/8523351

转载于:https://www.cnblogs.com/dengxiaojun/p/4357720.html

USB自定义HID设备实现-STM32相关推荐

  1. USB自定义HID设备实现-LPC1768

    首先在之前鼠标的基础上修改设备描述符 #include "usbdesc.h" //usb标准设备描述符 const U8 USB_DeviceDescriptor[] = { U ...

  2. STM32的USB例程JoyStickMouse改成自定义HID设备

    简介 USB HID类是USB设备的一个标准设备类,包括的设备非常多.HID类设备定义它属于人机交互操作的设备,用于控制计算机操作的一些方面,如USB鼠标.USB键盘.USB游戏操纵杆等.但HID设备 ...

  3. 沁恒CH583 USB 自定义HID调试记录

    使用USB HID主要是为了免驱,通过自定义USB HID可以利用USB口来做很多事,比如串口打印,串口升级都可以通过usb口来实现,这样可以省去一个USB转串口器件同时也不用装驱动,如下实现可以通过 ...

  4. STM32 USB复合设备,USB键盘+自定义HID设备

    上面是我的微信和QQ群,欢迎新朋友的加入. 最近研究USB复合设备 主要是把键盘和电脑音量控制两个东西给合并成一个设备 首先要知道的一点的是,不论是键盘.鼠标.或者这个标准的HID用户控制设备,本质都 ...

  5. 62 stm32 usb自定义hid复合设备修改实验

    1.引言 最近因为项目需要,我们希望单片机既能有hid键盘功能,又能有hid设备的功能.即单片机的一个usb接口插入电脑后,电脑能识别出键盘设备和hid设备,两者同时存在的. 基于项目只是要求实现功能 ...

  6. STM32配置CH375B成HID Host模式读取自定义HID设备的数据 ——STM32端口初始化

    最近产品需要一个USB主机测试治具,所以需要做一个USB HOST去读取HID设备的数据,由于以前也没做过USB方面的项目,对这一块也不是很熟悉,因此遇到了很多困难,所幸的是经过两天半的努力,最终完成 ...

  7. STM32CubeMX生成STM32F072 USB 自定义HID Device

    使用STM32CubeMX生成HID工程 1. 使用STM32CubeMX生成USB HID工程 2. 修改新建的工程 修改报告描述表 修改端点大小 3. 上下传数据 EP1上传数据 EP2下传数据 ...

  8. STM32配置CH375B成HID Host模式读取自定义HID设备的数据 ——STM32配置CH375B接口函数

    接着上一篇上传,这个是STM32配置CH375B时用到的接口函数 头文件: #ifndef __BSP_CH375_H__ #define __BSP_CH375_H__#include " ...

  9. stm32——自定义HID设备

    一.开发环境 硬件平台:stm32f767 软件平台:keil5,cubmx 二.cubemx创建工程 1.配置RCC 2.USB_OTG_FS,选择Device_Only模式 3.Middlewar ...

最新文章

  1. Udacity机器人软件工程师课程笔记(六)-样本搜索和找回-基于漫游者号模拟器-优化和样本找回
  2. python 进程池 freeze_support_Python 多进程并发操作中进程池Pool的实例
  3. ORA-01031: insufficient privileges的解决方法
  4. 90行代码!大一学生自学编程,自创搜题网站,已在GitHub开源
  5. Opencv 去高光或镜面反射(illuminationChange)
  6. C# try catch finally 执行
  7. C++文本操作.Vs.Python
  8. STM32连续采样_STM32 - 利用双缓冲实现实时曲线显示(续)
  9. 【R】ployroot函数求解多项式方程
  10. 为什么css效果在本地测试没问题,上传到服务器后却显示位置乱动,css下拉菜单本地正常,上传后360浏览器中显示错位,火狐、IE8显示正常...
  11. 我乃平常客,本持平常心| 2021 年中总结
  12. VMware Converter Standalone结合TrueImage 迁移HyperV虚机
  13. 一家旅游互联网公司技术的发展史
  14. 航空摄影与正射摄影的区别
  15. 腾讯云服务器php设置,Windows 腾讯云服务器的 PHP 配置
  16. 腾讯大王卡、天王卡代申请
  17. java用poi操作excel,2003,2007,2010
  18. 用无线插板控制RC(远程遥控)小车
  19. 用数学计算1.01的365次方等于37.8来说明积跬步至千里是否合理? 1.01^365=37.8 0.99^365=0.03
  20. python整数和浮点数

热门文章

  1. 关注书籍和教程(更新中)
  2. Spring.NET学习笔记(4)-对象作用域和类型转换
  3. 在WEBSTART中实现串口通信(WINDOWS版)
  4. How to become an expert in the IP industry? Here is where you should start
  5. 快速编辑 Shell 命令行
  6. 别人家的程序员是如何使用 Java 进行 Web 抓取的?
  7. 阿里云服务器Svn-Server无法连接,阿里云服务器SVNServer配置
  8. Java进阶篇(六)——Swing程序设计(上)
  9. 2星|《深度模仿》:陷入锤子模式,案例太少,洞察力欠缺
  10. Java中的Atomic包