USB自定义HID设备实现-STM32
该文档使用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相关推荐
- USB自定义HID设备实现-LPC1768
首先在之前鼠标的基础上修改设备描述符 #include "usbdesc.h" //usb标准设备描述符 const U8 USB_DeviceDescriptor[] = { U ...
- STM32的USB例程JoyStickMouse改成自定义HID设备
简介 USB HID类是USB设备的一个标准设备类,包括的设备非常多.HID类设备定义它属于人机交互操作的设备,用于控制计算机操作的一些方面,如USB鼠标.USB键盘.USB游戏操纵杆等.但HID设备 ...
- 沁恒CH583 USB 自定义HID调试记录
使用USB HID主要是为了免驱,通过自定义USB HID可以利用USB口来做很多事,比如串口打印,串口升级都可以通过usb口来实现,这样可以省去一个USB转串口器件同时也不用装驱动,如下实现可以通过 ...
- STM32 USB复合设备,USB键盘+自定义HID设备
上面是我的微信和QQ群,欢迎新朋友的加入. 最近研究USB复合设备 主要是把键盘和电脑音量控制两个东西给合并成一个设备 首先要知道的一点的是,不论是键盘.鼠标.或者这个标准的HID用户控制设备,本质都 ...
- 62 stm32 usb自定义hid复合设备修改实验
1.引言 最近因为项目需要,我们希望单片机既能有hid键盘功能,又能有hid设备的功能.即单片机的一个usb接口插入电脑后,电脑能识别出键盘设备和hid设备,两者同时存在的. 基于项目只是要求实现功能 ...
- STM32配置CH375B成HID Host模式读取自定义HID设备的数据 ——STM32端口初始化
最近产品需要一个USB主机测试治具,所以需要做一个USB HOST去读取HID设备的数据,由于以前也没做过USB方面的项目,对这一块也不是很熟悉,因此遇到了很多困难,所幸的是经过两天半的努力,最终完成 ...
- STM32CubeMX生成STM32F072 USB 自定义HID Device
使用STM32CubeMX生成HID工程 1. 使用STM32CubeMX生成USB HID工程 2. 修改新建的工程 修改报告描述表 修改端点大小 3. 上下传数据 EP1上传数据 EP2下传数据 ...
- STM32配置CH375B成HID Host模式读取自定义HID设备的数据 ——STM32配置CH375B接口函数
接着上一篇上传,这个是STM32配置CH375B时用到的接口函数 头文件: #ifndef __BSP_CH375_H__ #define __BSP_CH375_H__#include " ...
- stm32——自定义HID设备
一.开发环境 硬件平台:stm32f767 软件平台:keil5,cubmx 二.cubemx创建工程 1.配置RCC 2.USB_OTG_FS,选择Device_Only模式 3.Middlewar ...
最新文章
- Udacity机器人软件工程师课程笔记(六)-样本搜索和找回-基于漫游者号模拟器-优化和样本找回
- python 进程池 freeze_support_Python 多进程并发操作中进程池Pool的实例
- ORA-01031: insufficient privileges的解决方法
- 90行代码!大一学生自学编程,自创搜题网站,已在GitHub开源
- Opencv 去高光或镜面反射(illuminationChange)
- C# try catch finally 执行
- C++文本操作.Vs.Python
- STM32连续采样_STM32 - 利用双缓冲实现实时曲线显示(续)
- 【R】ployroot函数求解多项式方程
- 为什么css效果在本地测试没问题,上传到服务器后却显示位置乱动,css下拉菜单本地正常,上传后360浏览器中显示错位,火狐、IE8显示正常...
- 我乃平常客,本持平常心| 2021 年中总结
- VMware Converter Standalone结合TrueImage 迁移HyperV虚机
- 一家旅游互联网公司技术的发展史
- 航空摄影与正射摄影的区别
- 腾讯云服务器php设置,Windows 腾讯云服务器的 PHP 配置
- 腾讯大王卡、天王卡代申请
- java用poi操作excel,2003,2007,2010
- 用无线插板控制RC(远程遥控)小车
- 用数学计算1.01的365次方等于37.8来说明积跬步至千里是否合理? 1.01^365=37.8 0.99^365=0.03
- python整数和浮点数
热门文章
- 关注书籍和教程(更新中)
- Spring.NET学习笔记(4)-对象作用域和类型转换
- 在WEBSTART中实现串口通信(WINDOWS版)
- How to become an expert in the IP industry? Here is where you should start
- 快速编辑 Shell 命令行
- 别人家的程序员是如何使用 Java 进行 Web 抓取的?
- 阿里云服务器Svn-Server无法连接,阿里云服务器SVNServer配置
- Java进阶篇(六)——Swing程序设计(上)
- 2星|《深度模仿》:陷入锤子模式,案例太少,洞察力欠缺
- Java中的Atomic包