该文档使用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

USB自定义HID设备实现-STM32的更多相关文章

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

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

  2. STM32 USB开发(三) 基于F105RBT6核心板开发的自定义HID收发(FS)

    硬件设计 该核心板的USB插口有两个,一个是用于USB Slave的,可以用来做HID设备,把模拟STM32模拟为U盘等:另一个是USB Host设备,可以对插上的U盘的数据进行读写. 图中J2是Mi ...

  3. C# 访问USB(HID)设备

    原文:C# 访问USB(HID)设备 二话不说,直接给代码,如果您真想做这方面的东西,还是稍微研究下,没有现成的好类用,就需要自己了解其原理 //引用空间 using System; using Sy ...

  4. 转载 STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

    STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发  本文转载自 https://www.cnblogs.com/xingboy/p/9913963.html 这里我主要说一 ...

  5. STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

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

  6. Windows与自定义USB HID设备通信说明.

    1 .   所使用的典型 Windows API CreateFile ReadFile WriteFile 以下函数是 DDK 的内容: HidD_SetFeature HidD_GetFeatur ...

  7. 2--STM32+USB移植+HID 与AUDIO类MIDI设备组成的复合设备(原创)

      前期准备: 一.硬件资源:STM32F103,USB-FS固件库. 链接: STM32 之 标准外设版USB驱动库详解(架构+文件+函数+使用说明+示例程序) https://blog.csdn. ...

  8. STM32利用CUBEMX建立自定义HID工程,并且完成64字节的IN,OUT传输功能。

    STM32 Customed HID开发流程 本文介绍的是STM32的cubeMX自定义HID的开发流程 cubeMX配置customed HID模式.更多详细配置壳查看代码CubeMX的配置文件. ...

  9. Android USB Host 与 Hid 设备通信bulkTransfer()返回-1问题的原因

    近期一直在做Android USB Host 与USB Hid设备(STM32FXXX)的通信,遇到了很多问题.项目源码以及所遇到的其他问题可以见本博客其他相关文章,这里重点讲一下bulkTransf ...

随机推荐

  1. [WCF编程]5.绑定概述

    一.绑定概述 WCF提供了一个编程框架,可以抽象化服务创建的复杂过程.绑定允许开发人员将精力集中在问题本身上,而无需考虑如何创建允许系统运行的架构,因为WCF已经创建了架构. 绑定类型是开发人员控制W ...

  2. [SourceControl 有关SVN的目录:trunk、branches、tags]

    原文链接:http://techlife.blog.51cto.com/212583/223704/

  3. 哪些函数不能为virtual函数

    1> 内联函数 内联函数是在编译时期展开,而虚函数的特性是运行时才动态联编,所以两者矛盾,不能定义内联函数为虚函数. 2> 构造函数 构造函数用来创建一个新的对象,而虚函数的运行是建立在对 ...

  4. iOS下Html页面中input获取焦点弹出键盘时挡住input解决方案

    问题描述 iOS系统下,移动web页面,inpu获取焦点弹出系统虚拟键盘时,偶尔会出现挡住input的情况,尽管概率不大,但是十分影响用户体验. 问题重现 原始页面:页面中有header.main.f ...

  5. Python并发复习1 - 多线程

    一.基本概念 程序: 指令集,静态, 进程: 当程序运行时,会创建进程,是操作系统资源分配的基本单位 线程: 进程的基本执行单元,每个进程至少包含一个线程,是任务调度和执行的基本单位 > 进程和 ...

  6. TCP/IP 笔记 - 域名解析和域名系统

    由于IP地址的烦琐导致的记忆和使用困难,互联网支持使用主机名称来识别包括客户机和服务器在内的主机.同时为了使用一系列协议,主机名称通过称为"名称解析"的过程转换成对应IP地址. 互 ...

  7. JS的防抖,节流,柯里化和反柯里化

    今天我们来搞一搞节流,防抖,柯里化和反柯里化吧,是不是一看这词就觉得哎哟wc,有点高大上啊.事实上,我们可以在不经意间用过他们但是你却不知道他们叫什么,没关系,相信看了今天的文章你会有一些收获的 节流 ...

  8. java包的所有类生成class

    javac的编译单位其实就是单个的java文件,为了达到同时编译多个java文件的目的,可以将所需编译的java文件路径保存在一个txt中,比如sourcelist.txt,以换行为分隔符(这个过程称 ...

  9. VS Code 管理 .NET Core解决方案

    本练习要使用Visual studio code完成一个包含多个项目的解决方案,包括类库和Web项目.结合Visual Studio Code和.NET Core CLI,创建项目结构如下: pied ...

  10. swiper4 一个页面多个轮播

    <div class="swiper-container"> <div class="swiper-wrapper"> <div ...