libusb开发流程
前言
USB的用途就不多说了,下面的内容主要就是讲解如何利用ST提供的USB驱动库和libusb上位机驱动库实现一个USB数据传输功能,为了降低开发难度,我们仅仅讲解Bulk传输模式,当然这也是用得比较多的传输模式。
开发流程
1,完成STM32单片机端的USB程序;
2,利用linusb自带的inf-wizard工具生成USB驱动;
3,基于libusb编写USB通信程序;
4,测试PC和单片机的数据通信;
STM32程序编写
1,完成描述符的修改,修改后的描述符如下(在usb_desc.c文件中)
设备描述符:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | const { 0x12, /*bLength USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/ 0x00, /*bcdUSB 0x02, 0x00, /*bDeviceClass*/ 0x00, /*bDeviceSubClass*/ 0x00, /*bDeviceProtocol*/ 0x40, /*bMaxPacketSize40*/ LOBYTE(USBD_VID), /*idVendor*/ HIBYTE(USBD_VID), /*idVendor*/ LOBYTE(USBD_PID), /*idVendor*/ HIBYTE(USBD_PID), /*idVendor*/ 0x00, /*bcdDevice 0x02, 1, /*Index 2, /*Index 3, /*Index 0x01 /*bNumConfigurations*/ }; /* |
配置描述符:
| 01 02 03 04 05 06 07 08 09 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 | const { 0x09, /* USB_CONFIGURATION_DESCRIPTOR_TYPE, /* CUSTOMHID_SIZ_CONFIG_DESC, /* 0x00, 0x01, /* 0x01, /* 0x00, /* the 0xE0, /* /*Bus 0xFA, /* /************** /* 0x09, /* USB_INTERFACE_DESCRIPTOR_TYPE, /* 0x00, /* 0x00, /* 0x04, /* 0xDC, /* 0xA0, /* 0xB0, /* 0, /* /******************** /* 0x07, /* USB_ENDPOINT_DESCRIPTOR_TYPE, /* 0x81, /* 0x02, /* 0x40,0x00, /* 0x00, /* 0x07, /* USB_ENDPOINT_DESCRIPTOR_TYPE, /* 0x01, /* 0x02, /* 0x40,0x00, /* 0x00, /* 0x07, /* USB_ENDPOINT_DESCRIPTOR_TYPE, /* 0x82, /* 0x02, /* 0x40,0x00, /* 0x00, /* 0x07, /* USB_ENDPOINT_DESCRIPTOR_TYPE, /* 0x02, /* 0x02, /* 0x40,0x00, /* 0x00, /* }; /* |
配置描述符就包含了端点描述符,我们用了4个端点,两个BULK-OUT端点,两个BULK-IN端点。
其他的描述符:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /* const { CUSTOMHID_SIZ_STRING_LANGID, USB_STRING_DESCRIPTOR_TYPE, 0x09, 0x04 }; /* const { CUSTOMHID_SIZ_STRING_VENDOR, /* USB_STRING_DESCRIPTOR_TYPE, /* // 'M' , 'y' , 'U' , 'S' , 'B' , '_' , 'H' , 'I' ,0, 'D' ,0 }; const { CUSTOMHID_SIZ_STRING_PRODUCT, /* USB_STRING_DESCRIPTOR_TYPE, /* 'B' , 'y' , ' , 'e' , 'm' , 'b' , 'e' ,0, 'd' ,0, '-' ,0, 'n' ,0, 'e' ,0, 't' ,0 }; uint8_t { CUSTOMHID_SIZ_STRING_SERIAL, /* USB_STRING_DESCRIPTOR_TYPE, /* 'x' , 'x' , 'x' , 'x' , 'x' , 'x' , 'x' , }; |
2,根据端点缓冲区大小配置端点缓冲区地址,配置信息如下(在usb_conf.h文件中):
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | /* #define /* /* #define #define /* /* //地址为32位对其,位4的倍数,不能超过 //EP1 #define #define EP2 #define #define |
3,初始化每个端点(在usb_prop.c文件中的CustomHID_Reset函数中)
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* SetEPType(ENDP0, SetEPTxStatus(ENDP0, SetEPRxAddr(ENDP0, SetEPTxAddr(ENDP0, Clear_Status_Out(ENDP0); SetEPRxCount(ENDP0, SetEPRxValid(ENDP0); /* SetEPType(ENDP1, SetEPRxAddr(ENDP1, SetEPTxAddr(ENDP1, SetEPRxCount(ENDP1, SetEPRxStatus(ENDP1, SetEPTxStatus(ENDP1, /* SetEPType(ENDP2, SetEPRxAddr(ENDP2, SetEPTxAddr(ENDP2, SetEPRxCount(ENDP2, SetEPRxStatus(ENDP2, SetEPTxStatus(ENDP2, |
4,实现端点的回调函数(需要在usb_conf.h中注释掉对应的回调函数宏定义)
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /******************************************************************************* * * * * * *******************************************************************************/ void void ) { EP1_ReceivedCount PMAToUserBufferCopy(USB_Receive_Buffer, SetEPRxStatus(ENDP1, } /******************************************************************************* * * * * * *******************************************************************************/ void void ) { EP2_ReceivedCount PMAToUserBufferCopy(USB_Receive_Buffer, SetEPRxStatus(ENDP2, } |
5,完成主函数的测试程序
| 01 02 03 04 05 06 07 08 09 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 | int void ) { uint8_t uint32_t Set_System(); //系统时钟初始化 USART_Configuration(); //串口1初始化 printf ( "\x0c\0" ); printf ( "\x0c\0" ); //超级终端清屏 printf ( "\033[1;40;32m" ); //设置超级终端背景为黑色,字符为绿色 printf ( "\r\n*******************************************************************************" ); printf ( "\r\n************************ ); printf ( "\r\n*************************** ); printf ( "\r\n***************************** ); printf ( "\r\n*******************************************************************************" ); printf ( "\r\n" ); USB_Interrupts_Config(); Set_USBClock(); USB_Init(); while (1) { if (EP1_ReceivedCount USB_GetData(ENDP1,data,EP1_ReceivedCount); USB_SendData(ENDP1,data,EP1_ReceivedCount); printf ( "usb ,EP1_ReceivedCount); for (i=0;i printf ( "0x%02X ,data[i]); } printf ( "\n\r" ); EP1_ReceivedCount=0; } if (EP2_ReceivedCount USB_GetData(ENDP2,data,EP2_ReceivedCount); USB_SendData(ENDP2,data,EP2_ReceivedCount); printf ( "usb ,EP2_ReceivedCount); for (i=0;i printf ( "0x%02X ,data[i]); } printf ( "\n\r" ); EP2_ReceivedCount=0; } } } |
到此,STM32的程序基本上编写完成,然后编译下载程序,如果一切顺利,系统会检测到一个新的设备并试图加载对应的驱动,由于我们还没做驱动程序,所以肯定会加载驱动失败,如下图所示:
驱动程序生成
下面我们就利用libusb自带的inf-wizard工具生成USB驱动程序,该工具可以到本文章的附件下载,其具体过程如下:
运行该程序,出现下图对话框,点击“Next”;
出现下图对话框后选择我们需要生成驱动程序的设备;
这里可以写该Device Name,我们保持默认值,其他的都不需要修改;
点击Next后出现下图对话框,我们选择一个目录保存这个inf文件;
保存后的文件
若要立即安装驱动,可以点击下面对话框的红色框按钮;
Win7下可能会出现如下对话框,点击始终安装;
到此,USB驱动程序自动生成完毕,若安装了驱动,则在设备管理器里面会看到如下信息
基于libusb的上位机驱动程序编写
首先建立一个驱动程序工程,然后将libusb的库(附件有下载)添加到工程里面,编写以下几个函数
设备扫描函数,该函数用来找到插入电脑上的USB设备
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | /** * * * */ int int { if (NeedInit){ usb_init(); /* usb_find_busses(); /* usb_find_devices(); /* } return } |
打开设备
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | /** * * * */ int int { pBoardHandle[DevIndex] if (pBoardHandle[DevIndex]==NULL){ return } else { return } } |
关闭设备
| 01 02 03 04 05 06 07 08 09 | /** * * * */ int int { return } |
BULK端点写数据
| 01 02 03 04 05 06 07 08 09 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 | /** * * * * * * * */ int int int char int int { int if (pBoardHandle[nBoardID] return } #ifdef if { usb_close(pBoardHandle[nBoardID]); return } #endif #ifdef if { usb_close(pBoardHandle[nBoardID]); return } #endif #if // ret #else ret /*if((len%64) usb_bulk_write(pBoardHandle[nBoardID], }*/ #endif #ifdef usb_release_interface(pBoardHandle[nBoardID], #endif return } |
BULK端点读数据
| 01 02 03 04 05 06 07 08 09 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 | /** * * * * * * * */ int int int char int int { int if (pBoardHandle[nBoardID] return } #ifdef if { usb_close(pBoardHandle[nBoardID]); return } #endif #ifdef if { usb_close(pBoardHandle[nBoardID]); return } #endif #if // ret #else ret #endif #ifdef usb_release_interface(pBoardHandle[nBoardID], #endif return } |
到此,PC端的驱动程序编写基本完成,下面就是驱动程序的测试,我们可以把之前这个程序生成为一个dll文件,然后单独建立一个测试工程来测试这个dll文件中的函数,测试程序如下:
| 01 02 03 04 05 06 07 08 09 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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | // // #include #define #define int int { int int char char for ( int WriteTestData[i] } //扫描设备连接数,需要初始化 DevNum printf ( "设备连接数为:%d\n" ,DevNum); //打开设备0 ret if (ret printf ( "打开设备失败!\n" ); return } else { printf ( "打开设备成功!\n" ); } //端点1写数据 ret if (ret printf ( "端点1写数据失败!%d\n" ,ret); return } else { printf ( "端点1写数据成功!\n" ); } //端点1读数据 ret if (ret printf ( "端点1读数据失败!%d\n" ,ret); return } else { printf ( "端点1读数据成功!\n" ); for ( int printf ( "%02X ,ReadTestData[i]); if (((i+1)%16)==0){ printf ( "\n" ); } } printf ( "\n" ); } Sleep(100); //端点2写数据 ret if (ret printf ( "端点2写数据失败!%d\n" ,ret); return } else { printf ( "端点2写数据成功!\n" ); } //端点2读数据 ret if (ret printf ( "端点2读数据失败!%d\n" ,ret); return } else { printf ( "端点2读数据成功!\n" ); for ( int printf ( "%02X ,ReadTestData[i]); if (((i+1)%16)==0){ printf ( "\n" ); } } printf ( "\n" ); } getchar (); return } |
到此,整个开发流程基本完成,下面是本套程序的测试图片
串口打印输出
PC端测试程序输出
Bus Hound抓取到的USB数据
转载于:https://www.cnblogs.com/AijunHe/p/7596123.html
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
