Warning: The magic method Math_Captcha::__wakeup() must have public visibility in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php on line 87

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/includes/class-cookie-session.php on line 46

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/includes/class-cookie-session.php on line 49

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/includes/class-cookie-session.php on line 49

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/includes/class-cookie-session.php on line 49

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/includes/class-cookie-session.php on line 49

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/includes/class-cookie-session.php on line 49

Warning: Undefined array key "action" in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-recentcomments/core.php on line 7

Warning: Undefined array key "action" in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-recentcomments/core.php on line 12

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-content/plugins/all-in-one-seo-pack/app/Common/Meta/Robots.php on line 89

Warning: Cannot modify header information - headers already sent by (output started at /home/wwwroot/blog.gotocoding.com/wp-content/plugins/wp-math-captcha/wp-math-captcha.php:87) in /home/wwwroot/blog.gotocoding.com/wp-includes/feed-rss2.php on line 8
USB | 重归混沌的BLOG https://blog.gotocoding.com 师法天地, 道法自然 Sat, 11 Mar 2017 06:21:47 +0000 zh-Hans hourly 1 https://wordpress.org/?v=6.9 USB设备的插入检测 https://blog.gotocoding.com/archives/104?utm_source=rss&utm_medium=rss&utm_campaign=usb%25e8%25ae%25be%25e5%25a4%2587%25e7%259a%2584%25e6%258f%2592%25e5%2585%25a5%25e6%25a3%2580%25e6%25b5%258b https://blog.gotocoding.com/archives/104#respond Mon, 13 Jan 2014 06:52:00 +0000 http://localhost/wordpress/?p=16 阅读更多]]>   又被坑了,总算解决了,要睡了长话短说。首先,着重强调 Device Class GUID, Device Interface Class GUID, Interface GUID是有区别的,另外奉上两篇MSDN文章:

  

  http://msdn.microsoft.com/en-us/library/windows/hardware/ff553412(v=vs.85).aspx

  http://msdn.microsoft.com/en-us/library/windows/hardware/ff553428(v=vs.85).aspx

  Device Class GUID 是INF文件中Verison Section中指名的设备类GUID,他标志着此设备图标,此设备出现在哪一栏,电压电流等各种信息。

    Interface GUID是指驱动程序中的GUID, 用于使用API打开此设备进行一系列读写操作,API代码如下(FINDSTR_USB_DEVICE即为我驱动程序中使用的GUID):

 1     //得到设备的句柄
 2         HDEVINFO    info=SetupDiGetClassDevs((LPGUID)&FINDSTR_USB_DEVICE,NULL,NULL,DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);
 3         if(info==INVALID_HANDLE_VALUE)
 4         {
 5             AfxMessageBox("No HDEVINFO avaliable for this GUIDn");
 6             return    ;
 7         }
 8 
 9         SP_INTERFACE_DEVICE_DATA ifdata;
10         ifdata.cbSize=sizeof(ifdata);
11         DWORD instance=0;
12         if(!SetupDiEnumDeviceInterfaces(info,NULL,(LPGUID)&FINDSTR_USB_DEVICE,instance,&ifdata))
13         {
14             AfxMessageBox("No SP_INTERFACE_DEVICE_DATA available for this GUID instance");
15             SetupDiDestroyDeviceInfoList(info);
16             return    ;
17         }
18 
19         //得到符号链接名
20         DWORD ReqLen;
21         SetupDiGetDeviceInterfaceDetail(info,&ifdata,NULL,0,&ReqLen,NULL);
22         PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail=(PSP_INTERFACE_DEVICE_DETAIL_DATA)new char[ReqLen];
23         if(ifDetail==NULL)
24         {
25             AfxMessageBox("Error 1");
26             SetupDiDestroyDeviceInfoList(info);
27             return    ;
28         }
29         ifDetail->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
30         if(!SetupDiGetDeviceInterfaceDetail(info,&ifdata,ifDetail,ReqLen,NULL,NULL))
31         {
32             AfxMessageBox("Error 2");
33             SetupDiDestroyDeviceInfoList(info);
34             delete ifDetail;
35             return    ;
36         }
37         char buff[256]={0};
38         char * format="Symbolic link is %sn";
39         sprintf(buff,format,ifDetail->DevicePath);
40         HANDLE    rv=CreateFile(ifDetail->DevicePath,
41                         GENERIC_READ|GENERIC_WRITE,
42                         FILE_SHARE_READ|FILE_SHARE_WRITE,
43                         NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
44         //end of 得到设备句柄
45         delete    ifDetail;
46         SetupDiDestroyDeviceInfoList(info);
47 
48         hDevice=rv;
49         DWORD ThreadId;
50         hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)UpDate,this,0,&ThreadId);
51         m_EchoLed1.EnableWindow(TRUE);
52         m_EchoLed2.EnableWindow(TRUE);
53         m_OpenDevice.SetWindowText("关闭设备");

Device Interface Class GUID是Microsoft定义用来调用RegisterDeviceNotification时所使用的一类GUID,关于Microsoft所定义的所有Devcie Interface Class GUID 可以参看上面给出的网址,下面给出这类GUID的使用方法(用于动态检测某类USB设备的插入与拔出):

 1 //注册 某个USB设备接口类 事件
 2   GUID zz = {0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 
 3         0x51, 0xED};
 4     DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
 5 
 6     DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface);
 7     DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
 8     DevBroadcastDeviceInterface.dbcc_classguid = zz;
 9     RegisterDeviceNotification(m_hWnd, &DevBroadcastDeviceInterface,
10         DEVICE_NOTIFY_WINDOW_HANDLE);

注:上述三种GUID没有任何实质上的关联,不能混为一谈。

  GUID(A5DCBF10-6530-11D2-901F-00C04FB951ED)写成代码为

  static const GUID x_guid = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };

]]>
https://blog.gotocoding.com/archives/104/feed 0
关于stm32的USB学习笔记之usbcore.c https://blog.gotocoding.com/archives/85?utm_source=rss&utm_medium=rss&utm_campaign=%25e5%2585%25b3%25e4%25ba%258estm32%25e7%259a%2584usb%25e5%25ad%25a6%25e4%25b9%25a0%25e7%25ac%2594%25e8%25ae%25b0%25e4%25b9%258busbcore-c https://blog.gotocoding.com/archives/85#respond Mon, 26 Mar 2012 03:43:00 +0000 http://localhost/wordpress/?p=35 阅读更多]]> #include <stm32f10x_lib.h> #include "usbreg.h" #include "usbcore.h" #include "usbuser.h" #include "usbcfg.h" #include "usb.h" #include "usb_hw.h" #include "usbdesc.h" #include "hid.h" #include "hiduser.h" #define _DEBUG_ #include "debug.h" #pragma diag_suppress 111,1441 //用来指示USB设备的状态 WORD USB_DeviceStatus; //用来存储设备的地址 BYTE USB_DeviceAddress; //用来存储USB当前使用的设备的配置 BYTE USB_Configuration; //此配置使用端点 DWORD USB_EndPointMask; //用于标志此端点是否已经被停止0~15依次代表15个端点 DWORD USB_EndPointHalt; //此配置使用的接口数 BYTE USB_NumInterfaces; //每个接口可用的当前接口可替换值 BYTE USB_AltSetting[USB_IF_NUM]; /*用于临时存储控制传输时的数据包*/ USB_SETUP_PACKET SetupPacket; /*用于向主机发送数据的EP0的数据结构*/ USB_EP_DATA EP0Data; /*EP0Buf用于向USB发送数据时用的缓冲区*/ BYTE EP0Buf[USB_MAX_PACKET0]; /*功能:复位USB的一些数据标志 *参数:无 *返回值:无 */ void USB_ResetCore(void) { //默认为总线供电,因为我们的USB现在不都是插在电脑上才能工作的么&^_^ USB_DeviceStatus = USB_POWER; //在枚举之初地址当然是0 USB_DeviceAddress = 0; //配置描述符的标识从1开始,这里也先置为0 USB_Configuration = 0; //目前使用的是端点0 USB_EndPointMask = 0x00010001; //没有停止的端点 USB_EndPointHalt = 0x00000000; } /*功能:建立阶段,读取建立数据包 *参数:无 *返回值:无 */ void USB_SetupStage(void) { USB_ReadEP(0x00,(BYTE*)&SetupPacket); } /*功能:建立阶段,In握手包 *参数:无 *返回值:无 */ void USB_StatusInStage(void) { USB_WriteEP(0x80,NULL,0); } /*功能:建立阶段,Out握手包 *参数:无 *返回值:无 */ void USB_StatusOutStage(void) { USB_ReadEP(0x00,EP0Buf); } /*功能:数据In阶段 *参数:无 *返回值:无 */ void USB_DataInStage(void) { DWORD cnt; //先计算引次要发送多少数据 if(EP0Data.Count > USB_MAX_PACKET0) cnt = USB_MAX_PACKET0; else cnt = EP0Data.Count; //这里写端点却主机读,则将Dir位置1 cnt = USB_WriteEP(0x80,EP0Data.pData,cnt); EP0Data.pData += cnt; EP0Data.Count -= cnt; } /*功能:数据Out阶段 *参数:无 *返回值:无 */ void USB_DataOutStage(void) { DWORD cnt; cnt = USB_ReadEP(0x00,EP0Data.pData); EP0Data.pData+=cnt; EP0Data.Count-=cnt; } /*功能:获取USB设备的状态 *参数:无 *返回值:TRUE --->成功 * FALSE --->错误 */ __inline BOOL USB_GetStatus(void) { DWORD n,m; switch(SetupPacket.bmRequestType.BM.Recipient) { //接收端是设备 case REQUEST_TO_DEVICE: //返回设备状态给他 EP0Data.pData = (BYTE *)&USB_DeviceStatus; //将状态信息发送给主机 USB_DataInStage(); break; //接收端是接口 case REQUEST_TO_INTERFACE: /*配置描述符的标识从1开始,并且请求的接口号不能大于接口的数目,因为接口数目从0开始 *因为我们接口描述符中的接口号是一个字节所以这里wIndex中的数据中人低字节有效 */ if((USB_Configuration !=0)&&(SetupPacket.wIndex.WB.L < USB_NumInterfaces)) { //清0两个字节,因为根据USB协议此处必须返回0 *((__packed WORD *)EP0Buf) = 0; EP0Data.pData = EP0Buf; //发送给主机 USB_DataInStage(); } //此接口出现了错误 else return FALSE; break; case REQUEST_TO_ENDPOINT: //端点号高1位0方向,低4位为端点号 n = SetupPacket.wIndex.WB.L & 0x8f; //m的高16位代表in端点的标志,低16位代表out端点的标志 m = (n&0x80)?(1<<16 )<< (n&0x0f) :(1<<n); //如果配置标识不为0,或配置标识为0,但是是端点0时,才算正常 if((USB_Configuration !=0)||((n&0x0f)==0)&&(USB_EndPointMask & m)) { //查看此端点是否已经停用 *((__packed WORD *)EP0Buf) = (USB_EndPointHalt &m )?1:0; EP0Data.pData = EP0Buf; //将数据发送给主机 USB_DataInStage(); } //说明配置描述符出了问题 else return FALSE; break; default: return FALSE; } return TRUE; } /*功能:设置/清除USB设备的特征 *参数:sc 0----->清除 1----->设置 *返回值:TRUE --->成功 * FALSE --->错误 */ __inline BOOL USB_SetClrFeature(DWORD sc) { DWORD n,m; switch(SetupPacket.bmRequestType.BM.Recipient) { //接收端是设备,则清除或设置设备的特性 case REQUEST_TO_DEVICE: if(SetupPacket.wValue.W == USB_FEATURE_REMOTE_WAKEUP) { if(sc) { printf("设置设备远程唤醒特性rn"); //设置USB状态为使能远程唤醒 USB_DeviceStatus |= USB_GETSTATUS_REMOTE_WAKEUP; /*stm32硬件本身就支持远程唤醒,这里就不用设置了 *当然,软件支不支持在于对中断屏蔽位的设置 */ } else { printf("清除设备远程唤醒特性rn"); USB_DeviceStatus &= ~USB_GETSTATUS_REMOTE_WAKEUP; /*stm32硬件本身就支持远程唤醒,这里就不用设置了 *当然,软件支不支持在于对中断屏蔽位的设置 */ } } //不支持的请求 else { printf("不支持的请求rn"); return FALSE; } break; //接收端是接口 case REQUEST_TO_INTERFACE: //接口不支持清除特性 printf("清楚设置接口特征不被USB2.0支持rn"); return FALSE; //接收端是端点 case REQUEST_TO_ENDPOINT: //取得端点号 n = SetupPacket.wIndex.WB.L & 0x8F; // printf("请求的端点是:"); // printhex(n); // printf("USB_Configuration:"); // printhex(USB_Configuration); // printf("USB_EndPointMask"); // printhex(USB_EndPointMask); // printf("rn"); //将端点转化成与EndPointMask一样的格式 m = (n & 0x80)?((1<<16) << (n&0x0f)) : (1<<n); /*根据USB协议来说, *地址状态下只有端点0才能使用, *配置状态下可用 *不知道他这句是不是bug *原文件引用: *This request is valid when the device is in the Address state; *references to interfaces or to endpoints other than endpoint zero *shall cause the device to respond with a Request Error */ /* 此处认为只用配置状态下的非0端点才能清楚特征 ,至于地址状态下没有处理 * */ if((USB_Configuration !=0) && ((n&0x0f)!=0) && (USB_EndPointMask & m)) { //设置/清楚 某个端点stall状态 if(SetupPacket.wValue.W == USB_FEATURE_ENDPOINT_STALL) { //设置 if(sc) { printf("设置端点远程唤醒特性rn"); //设置请求的端点为stall USB_SetStallEP(n); //更新USB的状态位 USB_EndPointHalt |= m; } //清楚 else { printf("清楚端点远程唤醒特性rn"); USB_ClrStallEP(n); USB_EndPointHalt &= ~m; } } //未定义的请求 else { printf("不正确的端点请求rn"); return FALSE; } } //不正确的请求时机 else { printf("不正确的请求时机rn"); return FALSE; } break; //未定义的接收端类型 default: printf("未定义的接收端类型rn"); printf("接收端为:"); printhex(SetupPacket.bmRequestType.BM.Recipient); printf("rn"); return FALSE; } return TRUE; } /*功能:用于向主机发送描述符 *参数:无 *返回值:TRUE --->成功 * FALSE --->发生了错误 */ __inline BOOL USB_GetDescriptor() { BYTE * pD; DWORD len,n; switch (SetupPacket.bmRequestType.BM.Recipient) { //发给设备的请求 case REQUEST_TO_DEVICE: switch(SetupPacket.wValue.WB.H) { //获取设备描述符 case USB_DEVICE_DESCRIPTOR_TYPE: printf("获取设备描述符rn"); EP0Data.pData = (BYTE *)USB_DeviceDescriptor; len = USB_DEVICE_DESC_SIZE; break; //获取配置描述符 case USB_CONFIGURATION_DESCRIPTOR_TYPE: printf("获取USB配置描述符rn"); pD = (BYTE *)USB_ConfigDescriptor; /* 引用USB协议:The range of values used for a * descriptor index is from 0 to one less than * the number of descriptors of that type implemented by the device * 配置描述符中的标识是从1开始计的,但是,请求中的值是标识值为配置描述符 */ /*由于可能有几个配置描述符所以可能需要描述一下指针*/ for(n=0;n!= SetupPacket.wValue.WB.L;n++) { if(((USB_CONFIGURATION_DESCRIPTOR *)pD)-> bLength != 0) { pD+=((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength; } } //如果请求的描述符为空则返回stall if(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bLength == 0) return FALSE; //设置要传送的指针 EP0Data.pData = pD; //虽然配置描述符中多了一个字节,但是wTotalLength中没有算上,所以这里不用管,直接用就行了 len = ((USB_CONFIGURATION_DESCRIPTOR*)pD)->wTotalLength; break; //字符串描述符 case USB_STRING_DESCRIPTOR_TYPE: printf("获取字符串描述符rn"); EP0Data.pData = (BYTE *)USB_StringDescriptor + SetupPacket.wValue.WB.L; len = ((USB_STRING_DESCRIPTOR *)EP0Data.pData)->bLength; break; default: return FALSE; } break; //请求接收端接口 // case REQUEST_TO_INTERFACE: // printf("请求接收端接口rn"); // switch(SetupPacket.wValue.WB.H) // { // //Hid 描述符 // case HID_HID_DESCRIPTOR_TYPE: // printf("请求HID描rn"); // break; // case HID_REPORT_DESCRIPTOR_TYPE: // printf("请求报告描述符rn"); // //这里只有一个报告描述符,报告描述符的索引是从0开始计,跟数组是一样的,如是不是0则返回Stall // if(SetupPacket.wIndex.WB.L != 0) // return FALSE; // EP0Data.pData = (BYTE *)HID_ReportDescriptor; // printf("报告描述符请求的字节:"); // printhex(EP0Data.Count); // printf("报告描述符本来的长度:"); // printhex(HID_ReportDescSize); // printf("rn"); // len = HID_ReportDescSize; // break; // default: // printf("未识别的请求!!!###,类型是:"); // printhex(SetupPacket.wValue.WB.H); // printf("rn"); // break; // } // break; default: return FALSE; } printf("获取描述符成功rn"); //将数据发送给主机 if(EP0Data.Count > len) EP0Data.Count = len; USB_DataInStage(); return TRUE; } /*功能:设置配置请求 *参数:无 *返回值:TRUE --- >成功 * FALSE --- >失败 */ __inline BOOL USB_SetConfiguration(void) { USB_COMMON_DESCRIPTOR * pD; DWORD alt,n,m; printf("设置配置rn"); //配置值不能为0 if(SetupPacket.wValue.WB.L) { pD=(USB_COMMON_DESCRIPTOR *)USB_ConfigDescriptor; //循环到配置描述符中的最后一个端点描述符 while(pD->bLength) { switch(pD->bDescriptorType) { //此时是配置描述符 case USB_CONFIGURATION_DESCRIPTOR_TYPE: //如果此配置描述符是主机要设置的配置描述符 if(((USB_CONFIGURATION_DESCRIPTOR * )pD)->bConfigurationValue == SetupPacket.wValue.WB.L) { //保存起此时,设备所用的配置值 USB_Configuration = SetupPacket.wValue.WB.L; //保存起此时,设备所用的配置的接口数目 USB_NumInterfaces = ((USB_CONFIGURATION_DESCRIPTOR*)pD)->bNumInterfaces; //清0每个接口可替换设置值 for(n=0;n<USB_IF_NUM;n++) { USB_AltSetting[n]=0; } //我们将所有的以前可能使用的端点禁用,如果需要,在端点描述符中再使能就行了 //这样如果更改了配置就保证了,不会使用到以前配置使用,而本次配置没有使用的端点 //可能是为了防止干扰,猜的^-^! for(n=1;n<16;n++) { if(USB_EndPointMask & (1<<n)) USB_DisableEP(n); if(USB_EndPointMask & ((1<<16)<<n)) USB_DisableEP(n|0x80); } //还没有枚举到端点因此这里只使用的是端点0 USB_EndPointMask = 0x00010001; //刚配置,当然没有停止的端点啊,全新的&_^ USB_EndPointHalt = 0x00000000; /*在复位时,我们默认是总线供电,现在开始正式运行以前要因为主机会根据我们的配 *置描述符进行调整,一旦配置描述符返回后,配置描述符就生效了,所以这里必须来依据实际 *来重新设置 */ if(((USB_CONFIGURATION_DESCRIPTOR *)pD)->bmAttributes & USB_CONFIG_SELF_POWERED) USB_DeviceStatus |= USB_GETSTATUS_SELF_POWERED; else USB_DeviceStatus &= ~USB_GETSTATUS_SELF_POWERED; } //否则略过,下一个配置描述符 else { (BYTE *)pD += ((USB_CONFIGURATION_DESCRIPTOR *)pD)->wTotalLength; continue; } break; //下面是接口描述符 case USB_INTERFACE_DESCRIPTOR_TYPE: alt = ((USB_INTERFACE_DESCRIPTOR *)pD)->bAlternateSetting; break; case USB_ENDPOINT_DESCRIPTOR_TYPE: //我们仅仅需要处理多个子接口中的第一个就行了,(如果有子接口的话,没有的话刚好也是0) if(alt == 0) { //得到此端点 n = ((USB_ENDPOINT_DESCRIPTOR *)pD)->bEndpointAddress & 0x8f; //将端点号转化成bit以存储在USB_EndPointMask中去代表我们使用了此端点 m = (n&0x80)?((1<<16)<<(n&0x0f)):(1<<n); USB_EndPointMask |= m; //配置端点 USB_ConfigEP((USB_ENDPOINT_DESCRIPTOR *)pD); //使能端点 USB_EnableEP(n); //复位端点,使端点处于确定的端点 USB_ResetEP(n); } break; } (BYTE *)pD += pD->bLength; } } //如果请求的配置为0,则我们将所有的配置恢复到地址状态时的配置 else { USB_Configuration = 0; for(n=1;n<16;n++) { //如果上一个配置使用了此端点,则disable it if(USB_EndPointMask & (1<<n)) //OUT端点 USB_DisableEP(n); if(USB_EndPointMask & (1<<16)<<n) //IN端点 USB_DisableEP(n | 0x80); } //清除使用的全部端点,(当然0端点还是除外的) USB_EndPointMask = 0x00010001; USB_EndPointHalt = 0x00000000; } //我们根据USB_Configuration的值来查看我们到底时否找到了所需要的值 if(USB_Configuration == SetupPacket.wValue.WB.L) { printf("设置配置成功rn"); return TRUE; } else { printf("设置配置失败rn"); return FALSE; } } /*功能:用于处理端点0的输入和输出请求函数 *参数: USB_EVT_SETUP 枚举时上位机发送的数据包 * USB_EVT_OUT 普通传输时,上位机发送的数据包 * USB_EVT_IN 普通传输时,上位机请求发送数据 *返回值:无 */ void USB_EndPoint0 (DWORD event) { // printf("进入端点0处理函数rn"); switch (event) { //控制传输 case USB_EVT_SETUP: // printf("进入建立阶段rn"); //首先读取主机发来的请求 USB_SetupStage(); //设置EP0的数据结构 EP0Data.Count = SetupPacket.wLength; //识别是何请求 // printf("请求是"); // printhex(SetupPacket.bRequest); // printf(" 被请求对象:"); // printhex(SetupPacket.bmRequestType.B); // printf("rn"); //bRequestType.bit5~bit6代表请求类型 switch(SetupPacket.bmRequestType.BM.Type) { //标准请求 case REQUEST_STANDARD: switch(SetupPacket.bRequest) { //获取USB状态 case USB_REQUEST_GET_STATUS: //如果状态失败则停止0x81端点 if(!USB_GetStatus()) { printf("获取状态失败rn"); goto stall_i; } break; case USB_REQUEST_CLEAR_FEATURE: //如果清除特征失败则停止0x81端点 if(!USB_SetClrFeature(0)) { printf("清楚特征失败rn"); goto stall_i; } //成功了握一下手OK USB_StatusInStage(); break; case USB_REQUEST_SET_FEATURE: //如果状态失败则停止0x81端点 if(!USB_SetClrFeature(1)) { printf("设置特征失败rn"); goto stall_i; } //成功了握一下手OK USB_StatusInStage(); break; case USB_REQUEST_SET_ADDRESS: switch(SetupPacket.bmRequestType.BM.Recipient) { //只支持向设备设置地址 /*USB协议指出必须在改地址前完成检测并对此信息包进行响应 *即USB的设置地址阶段必须在主机接收到0长度数据包后才能进行 *当主机接收到状态阶段的0数据包时将会产生接收中断 *所以放到了USB_EVT_IN处*/ case REQUEST_TO_DEVICE: printf("设置地址rn"); USB_DeviceAddress = 0x80 | SetupPacket.wValue.WB.L; USB_StatusInStage(); break; default: printf("未识别的接收端,接收端是"); printhex(SetupPacket.bmRequestType.BM.Recipient); printf("rn"); goto stall_i; } break; //获取设置描述符 case USB_REQUEST_GET_DESCRIPTOR: if(!USB_GetDescriptor()) goto stall_i; break; //设置配置请求 case USB_REQUEST_SET_CONFIGURATION: switch(SetupPacket.bmRequestType.BM.Recipient) { //对设备 case REQUEST_TO_DEVICE: if(!USB_SetConfiguration()) { //如果失败则对主机返回stall goto stall_i; } //完成了请求,握个手OK USB_StatusInStage(); break; //不支持对其他类型请求 default: goto stall_i; } break; case USB_REQUEST_GET_INTERFACE: printf("请求接口rn"); break; default: printf("未识别的请求,请求代码是"); printhex(SetupPacket.bRequest); printf("rn"); break; } break; //类请求 case REQUEST_CLASS: printf("类请求rn"); switch (SetupPacket.bmRequestType.BM.Recipient) { //对接口请求 case REQUEST_TO_INTERFACE: if(SetupPacket.wIndex.WB.L == USB_HID_IF_NUM) { switch(SetupPacket.bRequest) { case HID_REQUEST_GET_REPORT: printf("获取报告描述符rn"); break; case HID_REQUEST_SET_REPORT: printf("设置报告描述符rn"); break; case HID_REQUEST_GET_IDLE: printf("获取空闲率rn"); break; case HID_REQUEST_SET_IDLE: printf("设置空闲rn"); break; case HID_REQUEST_GET_PROTOCOL: printf("查询报告协议是否在活动rn"); break; case HID_REQUEST_SET_PROTOCOL: printf("SetReprotrn"); break; default: printf("不支持的HID请求rn"); break; } } break; } break; //产商请求 case REQUEST_VENDOR: //未定义请求 default: stall_i: USB_SetStallEP(0x80); break; } break; //接收成功 case USB_EVT_OUT: printf("USB_EVT_OUTrn"); if(SetupPacket.bmRequestType.BM.Dir ==0) { printf(" 请求是:"); printhex(SetupPacket.bRequest); printf("rn"); } //返回握手 else { USB_StatusOutStage(); } break; //发送成功 case USB_EVT_IN: // printf("USB_EVT_INrn"); if(SetupPacket.bmRequestType.BM.Dir == 1) USB_DataInStage(); /*USB协议指出必须在改地址前完成检测并对此信息包进行响应 *即USB的设置地址阶段必须在主机接收到0长度数据包后才能进行 *当主机接收到状态阶段的0数据包时将会产生接收中断 *所以放到了USB_EVT_IN处*/ //原keil库没有加if判断,怀疑为bug,这里判断是否是由于设置地址的状态阶段成功而触发的中断 else// if(SetupPacket.bRequest == USB_REQUEST_SET_ADDRESS) { if(USB_DeviceAddress & 0x80) { USB_DeviceAddress &= 0x7f; USB_SetAddress(USB_DeviceAddress); } } break; } }

]]>
https://blog.gotocoding.com/archives/85/feed 0
关于stm32的USB学习笔记之USB_HW.c https://blog.gotocoding.com/archives/86?utm_source=rss&utm_medium=rss&utm_campaign=%25e5%2585%25b3%25e4%25ba%258estm32%25e7%259a%2584usb%25e5%25ad%25a6%25e4%25b9%25a0%25e7%25ac%2594%25e8%25ae%25b0%25e4%25b9%258busb_hw-c https://blog.gotocoding.com/archives/86#respond Wed, 14 Mar 2012 04:01:00 +0000 http://localhost/wordpress/?p=36 阅读更多]]> #include <stm32f10x_lib.h> #include <stm32f10x_map.h> #include "usbreg.h" #include "usbuser.h" #include "usbcore.h" #include "usb_hw.h" #define _DEBUG_ #include "debug.h" #define USB_EP_NUM 4 /*端点缓冲区的开始地址 *因为每个缓冲块都需要一个端点描术表 *而所有的端点描述表放在,USB缓冲区的首部 *此地址是相对于USB缓冲区的地址,我认为加上Offset更好些 *这里使用2个端点 *端点0与端点1 *此时EP_BUF_ADDR指向缓冲区的内容 */ #define EP_BUF_ADDR (sizeof(EP_BUF_DSCR)*USB_EP_NUM) /*USB缓冲区首地址包括缓冲区描述表,绝对地址*/ EP_BUF_DSCR * pBUF_DSCR = (EP_BUF_DSCR *) USB_PMA_ADDR; /*端点空闲缓冲区地址 *用于指示目前为止USB缓冲区中还没有分配的空闲地址的首地址*/ WORD FreeBufAddr; /*功能:用于初始化USB的时钟等部分 *参数:无 *返回值:无 */ void USB_Init(void) { printf("进入USB_Init,进行初始化rn"); //使能USB时钟 RCC->APB1ENR |= (1<<23); //使能USB中断 /*因为USB低优先级中断的中断号为20,而NVIC——IPRX *寄存器用四位来存储中断优先级,所以20/4=5 , *然后使能第20位中断*/ NVIC->IPR[5] |=0x10; NVIC->ISER[0]|=(1<<20); } /*功能:用于复位USB模块 *参数:无 *返回值:无 */ /*现在以我的水平还搞不懂双缓冲为何物,所以先不搞^-^*/ /*一些资料: *USB低优先级中断(通道20):可由所有USB事件触发(正确传输,USB复位等). *USB高优先级中断(通道19):仅能由同步和双缓冲批量传输事件触发,目的是保证最大的传输速率. *USB唤醒中断(通道42):由USB挂起模式的唤醒事件触发. OTG_FS_WKUP唤醒 * *复位要执行的内容可以参见rm0008 21.4.2节 */ void USB_Reset(void) { PrintS("USB_Resetrn"); /*复位了嘛,那所有以前产生的中断都没有用了,清了完事!*/ ISTR=0; /*通过设置CNTR来控制stm32的USB模块的工作方式 *所有的USB事件中断都是在低优先级中断(通道20)上处理的 *好吧就先使能这么多吧,先跑起来再说! */ CNTR= CNTR_CTRM | // 使能正确传输中断 CNTR_RESETM | //使能复位中断 CNTR_SUSPM | //使能挂起中断 CNTR_WKUPM ; //使能唤醒中断 FreeBufAddr = EP_BUF_ADDR; //此时FreeBuff指向第一个缓冲区首地址(不包括描述符表),相对地址 BTABLE = 0x00; //设置缓冲区描述表的位置仍是相对地址 /*为端点0设置缓冲区及各种控制位*/ pBUF_DSCR->ADDR_TX = FreeBufAddr; FreeBufAddr+=8; //端点0设置为8个字节,一般控制数据为8个字节 pBUF_DSCR->ADDR_RX = FreeBufAddr; FreeBufAddr+=8; /*在count_Rx字段中10~14bit用来表示缓冲区字节的快数 *而15bit用来表示块的大小 *0---2byte *1---1byte *我们这里使用了8个字节,bit15为0,所以应该((8<<10)>>1)即8<<9; *至于count_Rx我们在发送时再来赋值 */ pBUF_DSCR->COUNT_RX= 8 << 9; /*设置端点0为控制端点,接收缓冲区有效 *低四位代表端点地址 */ EPxREG(0) = EP_CONTROL | EP_RX_VALID; /*使能USB模块,并设置USB地址为0,以响应枚举*/ DADDR = DADDR_EF | 0; } /*功能:复位一个端点 *参数: 端点号 * EPNum:bit3~bit0 ----> 端点号 * EPNum:bit7 ----> 端点方向 * *返回值:无 */ /*其实就是将下一个要发送的数据包变成DATA0*/ void EP_Reset(DWORD EPNum) { DWORD num,var; PrintS("EP_Resetrn"); /*获得端点号,低四位为端点号*/ num = EPNum & 0x0F; var = EPxREG(num); /*如果bit7为1则将此端点的发送toggle置为0, *否则将此端点的接收toggle置为0 *因为数据总是从data0数据包开始发送的 */ if(EPNum & 0x80) EPxREG(num) = var & (EP_MASK | EP_DTOG_TX);/*输入端点*/ else EPxREG(num) = var & (EP_MASK | EP_DTOG_RX);/*输出端点*/ } /*功能:连接或断开USB功能 *参数:true -->连接USB * false-->关闭USB *返回值:无 */ void USB_Connect(BOOL turnon) { /*需要注意一点的事,所有的USB寄存器尽量用=而不要用与或非 *在编程手册上有明确表明,这样可能会导至出一些问题*/ printf("进入连接USB程序rn"); /*将USB强制复位*/ CNTR = CNTR_FRES; // printf("test1rn"); /*因为刚连接所以应该跟才启动一样,将所有没有处理的中断给清理掉*/ ISTR=0; // printf("test2rn"); if(turnon) { // printf("test3rn"); /*使能GPIOA,然后将PA.8置低,使USB *的D+加1.5K上接电阻,使USB集线器识别了高速设备 *这样才能让USB识别 */ RCC->APB2ENR |= (1 << 2); /* enable clock for GPIOA */ GPIOA->CRH &= ~(0x0f << 0 * 4); /* clear port PA8 */ GPIOA->CRH |= (0x03 << 0 * 4); /* PA6 General purpose output open-drain, max speed 50 MHz */ GPIOA->BRR = ( 1 << 8 ); /* reset PA8 (set to low) */ /*经过调试发现,这个语句的本意是:复位USB模块 *然后在此使能CNTR_RESETM即复位中断标志 *至于端点0的初始化留在USB低优先级中进行处理 *当然,我们也只开了低优先级中断^_^!*/ CNTR = CNTR_RESETM; /*此处只使能了复位中断,*/ } else CNTR = CNTR_FRES | CNTR_PDWN ;/*复位并关闭*/ } /*功能:设置端点状态 *参数:EPnum --->端点号 * stat --->要设置的状态值 *返回值:无 */ void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR * pEPD) { DWORD num,val; //取得端点号 num = pEPD->bEndpointAddress & 0xf; val = pEPD->wMaxPacketSize; //如果是IN端点 if(pEPD->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) { //此处我只想说pBUF_DSCR是指针,剩下的就是语法问题了 (pBUF_DSCR + num)->ADDR_TX = FreeBufAddr; /*取2的倍数,因为缓冲区都是字对齐的,注意此处如果大于1023会出现浪费现象 *因为USB_COUNTn_TX只能接收bit0~bit9 */ val = (val + 1)& ~1; } //输出端点 else { (pBUF_DSCR + num)->ADDR_RX = FreeBufAddr; /*因为USB_COUNTn_RX中存储只用bit10~bit14,如果BLSIZE=0(即块大小为2字节),那么只能是0~62个字节 *所以如果大于62,则应将块大小设置为BLSIZE=1(即32个字节) */ if(val > 62 ) { //块大小为32,则大小应该为32的倍数 val = (val +31)&~31; /*关于此计算公式,参见rm0008,21,5.3节 *(val >> 5)<<10 == val <<5 */ (pBUF_DSCR + num)->COUNT_RX = ((val << 5)-1) | 0x8000; } else { val = (val + 1) & ~1; (pBUF_DSCR + num)->COUNT_RX = val << 9; } } //修正空闲区域的起始地址 FreeBufAddr += val ; switch(pEPD->bmAttributes & USB_ENDPOINT_TYPE_MASK) { //控制端点 case USB_ENDPOINT_TYPE_CONTROL: val = EP_CONTROL; break; //同步端点 case USB_ENDPOINT_TYPE_ISOCHRONOUS: val = EP_ISOCHRONOUS; break; //块传输 case USB_ENDPOINT_TYPE_INTERRUPT: val = EP_INTERRUPT; break; default: printf("出错了,未识别的端点类型rn"); break; } val |= num; //设置端点寄存器 EPxREG(num) = val; } /*功能:设置端点状态 *参数:EPnum --->端点号 * stat --->要设置的状态值 *返回值:无 */ void EP_Status(DWORD EPNum,DWORD stat) { DWORD num,var; /*取得端点号*/ num = EPNum & 0x0f; var = EPxREG(num); /*此处用了一点小技巧,因为端点寄存器是写1反转,所以想设置相应的值只有使用异或*/ if(EPNum & 0x80) //输入端点 EPxREG(num)=(var ^ (stat & EP_STAT_TX)) & (EP_MASK | EP_STAT_TX); else //输出端点 EPxREG(num)=(var ^ (stat & EP_STAT_RX)) & (EP_MASK | EP_STAT_RX); } /*功能:复位端点 *参数:EPNum bit0~bit3为端点号 bit7 为端点方向 *返回值:无 */ void USB_ResetEP(DWORD EPNum) { EP_Reset(EPNum); } /*功能:设置端点为stall *参数:EPNum bit0~bit3为端点号 bit7 为端点方向 *返回值:无 */ void USB_SetStallEP(DWORD EPNum) { EP_Status(EPNum,EP_TX_STALL | EP_RX_STALL); } /*功能:设置端点为enable *参数:EPNum bit0~bit3为端点号 bit7 为端点方向 *返回值:无 */ void USB_EnableEP(DWORD EPNum) { EP_Status(EPNum,EP_TX_VALID | EP_RX_VALID); } /*功能:设置端点为disable *参数:EPNum bit0~bit3为端点号 bit7 为端点方向 *返回值:无 */ void USB_DisableEP(DWORD EPNum) { EP_Status(EPNum,EP_TX_DIS | EP_RX_DIS); } /*功能:清除端点的stall状态 *参数:EPNum bit0~bit3为端点号 bit7 为端点方向 *返回值:无 */ void USB_ClrStallEP(DWORD EPNum) { EP_Status(EPNum,EP_TX_VALID | EP_RX_VALID); } /*功能:设置USB地址 *参数:addr要设置的地址 *返回值:无 */ void USB_SetAddress(DWORD addr) { //DADDR的高1位是用来使能USB模块的 DADDR = DADDR_EF | addr; } /*功能:用于读端点缓冲区 *参数:EPnum --->端点号 * pData --->用于接收从端点缓冲区中读到的数据 * 此函数有些问题,应该加入一个参数显示缓冲区有多大 *返回值:读到的字节数 */ DWORD USB_ReadEP(DWORD EPnum,BYTE * pData) { DWORD num,cnt,*pv,n; //得到端点号 num = EPnum & 0xf; //取得些端点的缓冲区地址 pv=(DWORD *)(USB_PMA_ADDR + 2* ((pBUF_DSCR + num)->ADDR_RX)); //COUNT_RX低10位存放的是缓冲区的数据 cnt=(pBUF_DSCR + num)->COUNT_RX & EP_COUNT_MASK; for(n=0;n<(cnt+1)/2;n++) { /*pakced关键字用于单字节对齐,这在USB数据结构中的结构体中尤为重要 *因为stm32访问时使用32位,而USB访问时使用16位,所以pData为WORD,而 *pv为DWORD型指针 */ *((__packed WORD *)pData)=*pv++; /*这里pData为单字节指针所以,还是加2而不是加4*/ pData+=2; } /*OK,现在我们的端点又可以接收数据了,设置为VALID*/ EP_Status (EPnum,EP_RX_VALID); return cnt; } /*功能:用于写端点缓冲区 *参数:EPNum --->端点号 * pData --->指向要发送的数据缓冲区 * cnt --->要写入的字节数 *返回值:写入的字节数 */ DWORD USB_WriteEP(DWORD EPNum , BYTE * pData,DWORD cnt) { DWORD num,*pv,n; num = EPNum & 0x0f; pv=(DWORD *)(USB_PMA_ADDR + 2*((pBUF_DSCR+num)->ADDR_TX)); /*此处应该判断要写入的数据是否超量了,可能会产一个隐藏bug*/ for(n=0;n<(cnt + 1)/2;n++) { *pv++=*((__packed WORD*)pData); pData+=2; } //OK,现在USB发送缓冲区中已经有东西了,可以响应主机了 (pBUF_DSCR+num)->COUNT_TX=cnt; EP_Status(EPNum,EP_TX_VALID); return cnt; } /*功能:USB挂起 *参数:无 *返回值:无 */ void USB_Suspend(void) { GPIOE->BSRR |=1 ; /* Turn Off Suspend LED */ printf("进入挂起中断rn"); //强制挂起 CNTR |= CNTR_FSUSP; //进入低功耗模式 CNTR |= CNTR_LPMODE; } /*功能:USB唤醒 *参数:无 *返回值:无 */ void USB_WakeUp(void) { GPIOE->BRR |=1 ; /* Turn On Suspend LED */ printf("进入唤醒中断rn"); //唤醒了,当然得把这一位给清了 CNTR &= ~CNTR_FSUSP; //USB的唤醒事件会复位此位,我们这里不用管 //CNTR &= ~CNTR_LPMODE; } /*功能:USB低优先级中断服务程序 *参数:无 *返回值:无 */ void USB_LP_CAN_RX0_IRQHandler(void) { DWORD istr; DWORD num,var; istr=ISTR; //取得中断标志位 /*USB复位中断的处理*/ if(istr & ISTR_RESET) { //复位设备 USB_Reset(); //复位与USB协议有关的数据 USB_ResetCore(); ISTR = ~ISTR_RESET; /*已经处理完复位中断了,清楚复位中断标志*/ } /*USB挂起中断*/ /* if(istr & ******************************************************************** */ if (istr & ISTR_SUSP) { USB_Suspend(); ISTR = ~ISTR_SUSP; } /* USB Wakeup */ if (istr & ISTR_WKUP) { USB_WakeUp(); ISTR = ~ISTR_WKUP; } //****************************************************************** /*端点接中发送中断处理*/ while((istr = ISTR) & ISTR_CTR) { //清楚中断标志位 ISTR = ~ISTR_CTR; //取得发生中断的端点号 num=istr & ISTR_EP_ID; //取得端点寄存器的值 var = EPxREG(num); //正确的接收传输 if(var & EP_CTR_RX ) { //清0正确接收标志位 // printf("端点号为:"); // printhex((u8)num); EPxREG(num) = var & ~EP_CTR_RX & EP_MASK; //调用相应的端点进行处理 if(USB_P_EP[num]) { //如果是控制传输,则使有USG_EVT_SETUP if(var & EP_SETUP) USB_P_EP[num](USB_EVT_SETUP); else //否则就是普通的输出包 USB_P_EP[num](USB_EVT_OUT); } } //产生的是发送成功中断 if(var & EP_CTR_TX) { //清楚中断标志位 EPxREG(num) = var & ~EP_CTR_TX & EP_MASK; //调用对应的中断函数进行处理 if(USB_P_EP[num]) { //USB发送成功 USB_P_EP[num](USB_EVT_IN); } } } }

如有错误还请指正!!!

]]>
https://blog.gotocoding.com/archives/86/feed 0
使用STM32的USB模块中后对USB缓冲区的认识 https://blog.gotocoding.com/archives/87?utm_source=rss&utm_medium=rss&utm_campaign=%25e4%25bd%25bf%25e7%2594%25a8stm32%25e7%259a%2584usb%25e6%25a8%25a1%25e5%259d%2597%25e4%25b8%25ad%25e5%2590%258e%25e5%25af%25b9usb%25e7%25bc%2593%25e5%2586%25b2%25e5%258c%25ba%25e7%259a%2584%25e8%25ae%25a4%25e8%25af%2586 https://blog.gotocoding.com/archives/87#respond Wed, 14 Mar 2012 00:07:00 +0000 http://localhost/wordpress/?p=37 阅读更多]]> 最近在使用STM32的USB模块开发个项目,还以为挺简单,结果搞了快两天才把USB的包缓冲区的访问搞定,在此做个小总结吧。

  STM32的USB模块包缓冲区有512B,但是在STM32的参考手册中的存储器映像中却表明0x40006000-0x400063ff,整整多了512B,怎么会这样呢,同时在尝试着编程时也遇到了一个问题:

在usb_core.c文件的Setup0_Process(void)这个函数中,有这么一段:

uint16_t offset = 1; 
if (pInformation->ControlState != PAUSE)
  {
    pInformation->USBbmRequestType = *pBuf.b++; /* bmRequestType */
    pInformation->USBbRequest = *pBuf.b++; /* bRequest */
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwValue = ByteSwap(*pBuf.w++); /* wValue */
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwIndex  = ByteSwap(*pBuf.w++); /* wIndex */
    pBuf.w += offset;  /* word not accessed because of 32 bits addressing */
    pInformation->USBwLength = *pBuf.w; /* wLength */
  }  

这其中又不太明白为什么需要pBuf.w += offset;而且后面的解释也不太懂 /* word not accessed because of 32 bits addressing */
,我于是在这段之前加入调试以显示收到什么数
        #ifdef DEBUG
        UARTSend_String("***  端点0收到SETUP数据  ***rn");
        for(offset=0;offset<16;offset++)
        {
                UARTSend_Hex(*pBuf.b++);
        }
        #endif
结果串口调试显示如下:
***  USB总线复位  ***
***  USB总线CTR置位  ***
***  进入端点0  ***
***  端点0收到SETUP包  ***
***  端点0收到SETUP数据  ***
0x80 0x06 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x40 0x00 0x00 0x00

本来应该显示0x80 0x06 0x00 0x01 0x00 0x000x40 0x00才对,不太明白怎么0x80 0x06和0x00 0x01 后面多了两个 0x00 ,难道USB模块还会将收到的数据跳着放吗?

 后来反复看参考手册,问了些人,才知道原来STM32的USB缓冲区是一个双端口的RAM,CPU一端需要使用32位方式访问,但USB模块一端使用16位方式访问。也就是说每个USB模块中的地址*2才能对应到控制器中的实际地址,这样每四个字节地址空间后两个字节地址空间是空的。所以上面串口调试显示的数据每正确两个字节就会多出两个字节的0x00。

这里也对STM32的USB库函数中对缓冲区的操作函数做个说明:

在usb_men.c文件有这么一个函数

void PMAToUserBufferCopy(uint8_t *pbUsrBuf, uint16_t wPMABufAddr, uint16_t wNBytes)

  uint32_t n = (wNBytes + 1) >> 1;/* /2*/
  uint32_t i;
  uint32_t *pdwVal;
  pdwVal = (uint32_t *)(wPMABufAddr * 2 + PMAAddr);
  for (i = n; i != 0; i–)
  {
    *(uint16_t*)pbUsrBuf++ = *pdwVal++;
    pbUsrBuf++;
  }

它的作用是将缓冲区中的数据拷贝到你所定义的uint8_t Receive_Buffer[];数组中。其它地方都好理解,这里说说

  pdwVal = (uint32_t *)(wPMABufAddr * 2 + PMAAddr);
  for (i = n; i != 0; i–)
  {
    *(uint16_t*)pbUsrBuf++ = *pdwVal++;
    pbUsrBuf++;
  }
首先是pdwVal = (uint32_t *)(wPMABufAddr * 2 + PMAAddr);这里wPMABufAddr * 2之所以要*2就是前面所述的USB模块中的地址*2才能对应到控制器中的实际地址,在取得对应端点的缓冲区首地址后,将其(uint32_t*)强制指向uint32_t型,这样每次*pdwVal++,pdwVal的地址都增加4个字节,并且每次都会有四个字节的数据读出。

在for循环中,*(uint16_t*)pbUsrBuf++ = *pdwVal++;这句重点说说,++与指针*同优先级,结合顺序为至右向左结合,因此相当于*((uint16_t*)pbUsrBuf++),先*(uint16_t*)pbUsrBuf赋值只取*pdwVal++的32位数据的前16位,然后再将pbUsrBuf加上1个字节。到这里,前面定义的uint8_tReceive_Buffer[];数组中就有两个数组变量被赋值了,但这时地址还指增加一个字节,因此还需要pbUsrBuf++;让其指向Receive_Buffer[];的下下个数组变量。一次循环,知道读出所有数据。

转自http://blog.csdn.net/ringstart/article/details/6822377

]]>
https://blog.gotocoding.com/archives/87/feed 0
关于USB微帧与数据包包长与端点缓冲区的理解 https://blog.gotocoding.com/archives/70?utm_source=rss&utm_medium=rss&utm_campaign=%25e5%2585%25b3%25e4%25ba%258eusb%25e5%25be%25ae%25e5%25b8%25a7%25e4%25b8%258e%25e6%2595%25b0%25e6%258d%25ae%25e5%258c%2585%25e5%258c%2585%25e9%2595%25bf%25e4%25b8%258e%25e7%25ab%25af%25e7%2582%25b9%25e7%25bc%2593%25e5%2586%25b2%25e5%258c%25ba%25e7%259a%2584%25e7%2590%2586%25e8%25a7%25a3 https://blog.gotocoding.com/archives/70#respond Wed, 05 Oct 2011 18:18:00 +0000 http://localhost/wordpress/?p=57 阅读更多]]>        搞了这么久USB了,还是不知道DATA0,DATA1的最大包长,以及端点描述符中的最大包长与68013的端点缓冲区有什么关系,经过二小时的奋斗得出如下结论:
       1.DATA0或DATA1实际上就是一个端点描述符中的最大包长。换句话说,68013发一次数据包就是一个DATA0或             DATA1

2.微帧是用来作为传输时间基准的。在每个微帧间可以传一到多次DATA0/DATA1,但是根据DATA0/DATA1(也就是68013一次发送的数据的长度)的大小,每个微帧之间可以传送DATA数据包的个数也不同,DATA0/DATA1越大,每个微帧之间能传输的DATA的数据包的个数越少。

3.在USB2.0之中限定了描述符中的最大包长,是因为在一个微帧内由于物理条件的限制(物理带宽、bit率的原因),只能传输那么多。

4.固件使用的USB协议其实就是用来与上位机驱动进行通信的一种语言。换句话说,里面的描述符中的内容都是给上位机驱动看的。其中端点中的最大包长也就是指的是,硬件的端点缓冲区。如果端点缓冲区比描述符中的最大包长小(在USB2.0协议最大包长范围之内),那么USB硬件接收到的数据在一定情况下将会被截断或者不被接收,如果端点缓冲区比描述符中的最大包长大(在USB2.0协议最大包长范围之内),端点缓冲将会被浪费。

]]>
https://blog.gotocoding.com/archives/70/feed 0
关于接口描述符中bAlternateSetting的使用 https://blog.gotocoding.com/archives/71?utm_source=rss&utm_medium=rss&utm_campaign=%25e5%2585%25b3%25e4%25ba%258e%25e6%258e%25a5%25e5%258f%25a3%25e6%258f%258f%25e8%25bf%25b0%25e7%25ac%25a6%25e4%25b8%25adbalternatesetting%25e7%259a%2584%25e4%25bd%25bf%25e7%2594%25a8 https://blog.gotocoding.com/archives/71#respond Tue, 04 Oct 2011 20:05:00 +0000 http://localhost/wordpress/?p=58 阅读更多]]>         今天看CY7C68013开发手册时,突然想起,在USB接口描述符中有一个bAlternateSetting字段一直没用过,花了一上午时间也没找到相关资料!就在快中午时突然看到一句话:“bAlternateSetting用于在同一个接口中的多个描述符中进行切换". 一下子感觉抓住要点了。经过百度及查阅USB specification2.0得出如下结论:
       在USB协议中同一个配置中的不同接口不能共用端点。
       同一个接口可以有多个接口描述符,用bAlternateSetting来识别.
       同一个接口的不同接口描述符,可以拥有相同的端点。
       同一个接口中的不同描述符可以像其他接口描述符一样写在配置描述符里
       例如:我们在有些情况下需要对某个接口描述符中的某个端点的传输类型进行更改,那么我们就需要使用这种方式来达到我们的要求!
       如果事实真的这样的,那么上位机驱动在查找接口描述符中必然 要有一个参数bAlternateSetting来识别相同接口的不同描述符,我们来看一下

VOID 

  UsbBuildSelectInterfaceRequest(

    IN PURB  Urb,
    IN USHORT  Length,

    IN USBD_CONFIGURATION_HANDLE  ConfigurationHandle,

    IN UCHAR  InterfaceNumber,

    IN UCHAR  AlternateSetting

    );

PUSB_INTERFACE_DESCRIPTOR
  USBD_ParseConfigurationDescriptorEx(
    IN PUSB_CONFIGURATION_DESCRIPTOR  ConfigurationDescriptor,
    IN PVOID  StartPosition,
    IN LONG  InterfaceNumber,
    IN LONG  AlternateSetting,
    IN LONG  InterfaceClass,
    IN LONG  InterfaceSubClass,
    IN LONG  InterfaceProtocol
    );
这两个函数中都有一个共同的参数:AlternateSetting!
至此,基本可以说明得出的结论是正确的!


      
        
      

]]>
https://blog.gotocoding.com/archives/71/feed 0
USB的挂起和唤醒 (Suspend and Resume)〈转〉 https://blog.gotocoding.com/archives/60?utm_source=rss&utm_medium=rss&utm_campaign=usb%25e7%259a%2584%25e6%258c%2582%25e8%25b5%25b7%25e5%2592%258c%25e5%2594%25a4%25e9%2586%2592-suspend-and-resume%25e3%2580%2588%25e8%25bd%25ac%25e3%2580%2589 https://blog.gotocoding.com/archives/60#respond Mon, 03 Oct 2011 19:25:00 +0000 http://localhost/wordpress/?p=60 阅读更多]]>   USB协议的第9章讲到USB可见设备状态[Universal Serial Bus Specification, Section 9.1.1, Pg 239],分为连接(Attached),上电(Powered),默认(Default),地址(Address),配置(Configured)和挂起(Suspended)6个状态。所谓可见,即USB系统和主机可见的状态,其他状态属于USB设备内部而不可见。其中有关电源的,大致可分下面三类:
1. 连接状态(Attached):设备连接,但未提供电源。
2. 上电状态(Powered):设备被复位(Reset),或者说处于地址、配置状态。(参见USB枚举过程,USB Specification, page 241)
3. 挂起状态(Suspended):3 ms未见总线活动而处于省电状态,设备不可用,但仍然保持原有的USB地址和配置。

设备的挂起
我们知道,在USB系统中,正常状态下hub或root hub会一直周期性地发送SOF包(Start Of Frame,全速USB每1ms发送一个,高速USB则是125µs发送一个)。根据USB协议,如果USB线上一直处于空闲(Idle)状态超过3ms,设备应该把它当作一个挂起(Suspended)信号,要求设备在10ms内进入挂起状态,并把设备所需的电流大小降到规定的值(对于low-power设备,要求是500
µA,而对于high-power或支持远程唤醒(remote wakeup)功能的设备是2.5mA【Section 7.2.3, Pg176】)。在挂起状态中,设备必须继续向数据项D+/D-的上拉电阻提供电压以维持Idle状态。而对于USB2.0高速设备,还有些额外的要求:
1. 高速设备在
收到挂起信号(3ms空闲)后,应在0.125ms内切换到全速状态,也就是说要把终端电阻移除,并在D+数据线上重新挂上1.5k上拉电阻。
2. 设备在随后的100-875µs内检测数据线上的状态。如果该状态是一个
Full speed J,那么说明host发下来的是一个挂起信号;如果此时该状态是SE0,说明是host drive数据线D+到0,这是一个复位信号(复位信号会持续至少10ms时间)。
  要注意的是高速设备在挂起状态时处于高速模式,只是所用的是全速信号。一旦从挂起状态恢复回来,会马上进入高速工作状态而无需进行复位操作。


USB High Speed Suspend Signal

  如上图所示,Host在a点停止发送SOF,系统处于SE0状态,b点是3ms时间点,之后的125µs内,设备移除终端电阻,并挂上1.5k上拉。此时如host发送的是suspend信号,那它就不会不drive
D+数据线,导致D+被设备拉高,形成一个Full Speed J状态。在c点之后
100-875µs内设备检测此时host行为,发现是suspend信号,设备内部进入低功耗的挂起(suspend)状态。(假如hsot发送的是复位信号,那么当设备在c点挂1.5k电阻之后,由于host对D+线的drive作用,D+线无法被拉高,仍然处于SE0状态,设备在c点之后的检测,发现的还是SE0状态,等到10ms后就可判断这是一个复位信号,进行设备复位操作。)


设备的唤醒
  设备处于挂起状态时,任何总线上的活动(非空闲信号)都可以把设备唤醒/恢复,从而退出低功耗模式。(同样,设备也可以换醒host,比如电脑待机时通过USB键盘来换醒主机,这种功能称之为“远程唤醒”(remote wakeup),不在本文的讨论范围内。)
  因为设备挂起时处于全速信号,在当host需要把将设备退出suspend状态时,需要先发送一个持续时间超过20ms的Fulll Speed K状态。设备看到K状态结束的1.3us内醒过来,而host需要在3ms内发送uSOF信号以维持正常的高速信号模式,否则设备又将进入suspend。如下图所示:

原文转自:http://apps.hi.baidu.com/share/detail/17542952

]]>
https://blog.gotocoding.com/archives/60/feed 0
USB枚举过程〈转〉 https://blog.gotocoding.com/archives/61?utm_source=rss&utm_medium=rss&utm_campaign=usb%25e6%259e%259a%25e4%25b8%25be%25e8%25bf%2587%25e7%25a8%258b%25e3%2580%2588%25e8%25bd%25ac%25e3%2580%2589 https://blog.gotocoding.com/archives/61#respond Mon, 03 Oct 2011 17:35:00 +0000 http://localhost/wordpress/?p=61 阅读更多]]>                                 USB架构中, hub负责检测设备的连接和断开,利用其中断IN端点(Interrupt IN Endpoint)来向主机(Host)报告。在系统启动时,主机轮询它的根hub(Root Hub)的状态看是否有设备(包括子hub和子hub上的设备)连接。USB总线拓扑结构见下图(最顶端为主机的Root Hub):

 

 

USB总线拓扑结构
(USB Bus Topology


一旦获悉有新设备连接上来,主发送一系列的请求(Resqusts)给设备所挂载的hub,再由hub建立起一条连接主机(Host)和设备(Device)之间的通信通道。然后主机以控制传输(Control
Transfer)的方式,通过端点0(Endpoint 0)对设备
发送各种请求,设备收到主机发来的请求后回复相应的信息,进行枚举(Enumerate操作所有的USB设备必须支持标准请求(S
tandard
Requests),控制传输方式(Control
Transfer和端点0Endpoint 0


从用户角度来看,枚举过程是自动完成并不可见的。但很多初次使用的设备连接时,系统会弹出说新硬件检测到,设备安装成功,可以使用之类的消息提示框,而且有时还需要用户配合选择安装相关的驱动。


当枚举完成后,这个新添加的设备可在Windows的设备管理器里面看到,当用户删除这个设备/硬件时,系统把这个设备从设备管理器里删除。

对于一般的设备,固件(Firmware)内包含主机所要请求的信息,而有些设备则是完全由硬件来负责响应主机的请求。在主机方面则是由操作系统而非应用程序负责处理相关枚举操作。


枚举步骤

USB协议定义了设备的6种状态,仅在枚举过程种,设备就经历了4个状态的迁移:上电状态(Powered),默认状态(Default),地址状态(Address)和配置状态(Configured)(其他两种是连接状态和挂起状态(Suspend))。


下面步骤是Windows系统下典型的枚举过程,但是固件不能依此就认为所有的枚举操作都是按照这样一个流程行进。设备必须在任何时候都能正确处理所有的主机请求。


1.用户把USB设备插入USB端口或给系统启动时设备上电

    这里指的USB端口指的是主机下的根hub或主机下行端口上的hub端口。Hub给端口供电,连接着的设备处于上电状态。

 


2.Hub监测它各个端口数据线上(D+/D-)的电压

    在hub端,数据线D+和D-都有一个阻值在14.25k到24.8k的下拉电阻Rpd,在设备端,D+(全速,高速)和D-(低速)上有一个1.5k的上拉电阻Rpu。当设备插入到hub端口时,有上拉电阻的一根数据线被拉高到幅值的90%的电压(大致是3V)。hub检测到它的一根数据线是高电平,就认为是有设备插入,并能根据D+还是D-被拉高来判断到底是什么设备全速/低插入端口(全速、高速设备的区分在我将来的文章中描述)。如下图。

USB全速/高速设备上电连接

(Full-speed Device Cable and Resistor Connections)


    检测到设备后,hub继续给设备供电,但并不急于与设备进行USB传输。

 


3. Host了解连接的设备

 

  
每个hub利用它自己的中断端点向主机报告它的各个端口的状态(对于这个过程,设备是看不到的,也不必关心),报告的内容只是hub端口的设备连接断开的事件。如果有连接断开事件发生,那么host会发送一个
Get_Port_Status请求(request)以了解更多hub上的信息。Get_Port_Status等请求属于所有hub都要求支持的hub类标准请求(standard hub-class requests)。

 


4.Hub检测所插入的设备是高速还是低速设备

    hub通过检测USB总线空闲(Idle)时差分线的高低电压来判断所连接设备的速度类型,当host发来Get_Port_Status请求时,hub就可以将此设备的速度类型信息回复给host。(USB
2.0规范要求速度检测要先于复位(Reset)操作)。

 


5.hub复位设备

   
当主机获悉一个新的设备后,主机控制器就向hub发出一个 Set_Port_Feature请求让hub复位其管理的端口。hub通过驱动数据线到复位状态(D+和D-全为低电平 ),并持续至少10ms。当然,hub不会把这样的复位信号发送给其他已有设备连接的端口,所以其他连在该hub上的设备自然看不到复位信号,不受影响。


6.Host检测所连接的全速设备是否是支持高速模式

 

 

   
因为根据USB 2.0协议,高速(High Speed)设备在初始时是默认全速(Full Speed )状态运行,所以对于一个支持USB 2.0的高速hub,当它发现它的端口连接的是一个全速设备时,会进行高速检测,看看目前这个设备是否还支持高速传输,如果是,那就切到高速信号模式,否则就一直在全速状态下工作。

   
同样的,从设备的角度来看,如果是一个高速设备,在刚连接bub或上电时只能用全速信号模式运行(根据USB 2.0协议,高速设备必须向下兼容USB 1.1的全速模式)。随后hub会进行高速检测,之后这个设备才会切换到告诉模式下工作。假如所连接的hub不支持USB
2.0,即不是高速hub,不能进行高速检测,设备将一直以全速工作。

   
高速设备检测的过程在我另外一篇文章
USB2.0速度识别中有详细描述,这里不具体深入。

 


7.
Hub建立设备和主机之间的信息通道

    主机不停得向hub发送 Get_Port_Status请求,以查询设备是否复位成功。Hub返回的报告信息中有专门的一位用来标志设备的复位状态。

    当hub撤销了复位信号,设备就处于默认/空闲状态(Default state),准备着主机发来的请求。设备和主机之间的通信通过控制传输,默认地址0,端点号0进行。在此时,设备能从总线上得到的最大电流是100mA。

 


8.主机发送Get_Descriptor请求获取默认管道的最大包长度

   
默认管道(Default Pipe)在设备一端来看就是端点0。主机此时发送的请求是默认地址0,端点0,虽然所有位分配地址的设备都是通过地址0来获取主机发来的信息,但由于枚举过程不是多个设备并行处理,而是一次枚举一个设备的方式进行,所以不会发生多个设备同时响应主机发来的请求。

    设备描述符的第8字节代表设备端点0的最大包大小。对于Windows系统来说,Get_Descriptor请求中的wLength一项都会设为64,虽然说设备所返回的设备描述符(Device Descriptor)长度只有18字节,但系统也不在乎,此时,描述符的长度信息对它来说是最重要的,其他的瞄一眼就过了。Windows系统还有个怪癖,当完成第一次的控制传输后,也就是完成控制传输的状态阶段,系统会要求hub对设备进行再一次的复位操作(USB规范里面可没这要求)。再次复位的目的是使设备进入一个确定的状态。

 


9.主机给设备分配一个地址

    主机控制器通过Set_Address请求向设备分配一个唯一的地址。在完成这次传输之后,设备进入地址状态(Address state),之后就启用新地址继续与主机通信。这个地址对于设备来说是终生制的,设备在,地址在;设备消失(被拔出,复位,系统重启),地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。

 


10.主机获取设备的信息

   
主机发送 Get_Descriptor请求到新地址读取设备描述符,这次主机发送Get_Descriptor请求可算是诚心,它会认真解析设备描述符的内容。设备描述符内信息包括端点0的最大包长度,设备所支持的配置(Configuration)个数,设备类型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由厂商自己定制)等信息。
Get_Descriptor请求(Device
type)和设备描述符(已抹去VID,PID等信息)见下图:


 

标准Get_Descriptor请求

Get_Descriptor Request


 

 

设备描述符(Device Descriptor)

 

    之后主机发送Get_Descriptor请求,读取配置描述符(Configuration Descriptor),字符串等,逐一了解设备更详细的信息。事实上,对于配置描述符的标准请求中,有时wLength一项大于实际配置描述符的长度(9字节),比如255。这样的效果便是:主机发送了一个Get_Descriptor_Configuration
请求,设备会把接口描述符,端点描述符等后续描述符一并回给主机,主机则根据描述符头部的标志判断送上来的具体是何种描述符。


11.主机给设备挂载驱动(复合设备除外)

主机通过解析描述符后对设备有了足够的了解,会选择一个最合适的驱动给设备。在驱动的选择过程中,Windows系统会和系统inf文件里的厂商ID,产品ID,有时甚至用到设备返回来的产品版本号进行匹配。如果没有匹配的选项,Windows会根据设备返回来的类,子类,协议值信息选择。如果该设备以前在系统上成功枚举过,操作系统会根据以前记录的登记信息而非inf文件挂载驱动。当操作系统给设备指定了驱动之后,就由驱动来负责对设备的访问。

    对于复合设备,通常应该是不同的接口(Interface)配置给不同的驱动,因此,需要等到当设备被配置并把接口使能后才可以把驱动挂载上去。

    设备-配置-接口-端点关系见下图:

 

 

USB 设备-配置-接口-端点关系
(Device Configuration)

  

    实际情况没有上述关系复杂。一般来说,一个设备就一个配置,一个接口,如果设备是多功能符合设备,则有多个接口。端点一般都有好几个,比如Mass Storage设备一般就有两个端点(控制端点0除外)。


12. 设备驱动选择一个配置

    驱动(注意,这里是驱动,之后的事情都是有驱动来接管负责与设备的通信)根据前面设备回复的信息,发送Set_Configuration请求来正式确定选择设备的哪个配置(Configuration)作为工作配置(对于大多数设备来说,一般只有一个配置被定义)。至此,设备处于配置状态,当然,设备也应该使能它的各个接口(Interface)。

   对于复合设备,主机会在这个时候根据设备接口信息,给它们挂载驱动。

 


13. 设备可使用


********************

写在最后

   
写此文是为了自己对最近一段时间学习USB有个总结,也希望能让其他USB初学者有点小小的收获。本文主要参考来源是Jan
Axelson的
Enumeration: How the Host Learns about Devices(http://www.lvr.com/usbcenum.htm)和Danny Simpson的An Overview of the Universal Serial Bus (USB) (http://www.sss-mag.com/usb.html),并结合些自己的一些体会和理解,辅以图片。由于我自己也是刚接触USB不久,文中必有一些瑕疵,还请各位高手多多指教了:)

原文来自:http://apps.hi.baidu.com/share/detail/44244162

]]>
https://blog.gotocoding.com/archives/61/feed 0
USB2.0速度识别 https://blog.gotocoding.com/archives/72?utm_source=rss&utm_medium=rss&utm_campaign=usb2-0%25e9%2580%259f%25e5%25ba%25a6%25e8%25af%2586%25e5%2588%25ab https://blog.gotocoding.com/archives/72#respond Mon, 03 Oct 2011 17:34:00 +0000 http://localhost/wordpress/?p=62 阅读更多]]>   我们知道USB2.0向下兼容USB1.x,即高速2.0的hub能支持所有的速度类型的设备,而USB1.x的hub不能支持高速设备(High Speed Device)。因此,如果高速设备挂到USB1.x的hub上,那该设备只能工作在全速模式下。不管是hub还是设备(device),对于速度的区分是非常重要的,否则,后续的通信根本无法进行。

全速和低速识别
  
  根据规范,全速(Full Speed)和低速(Low Speed)很好区分,因为在设备端有一个1.5k的上拉电阻,当设备插入hub或上电(固定线缆的USB设备)时,有上拉电阻的那根数据线就会被拉高,hub根据D+/D-上的电平判断所挂载的是全速设备还是低速设备。如下两图:

USB全速设备上电连接

(Full-speed Device Cable and Resistor Connections)


USB低速设备上电连接

(Low-speed Device Cable and Resistor Connections)


高速识别

  
  USB全速/低速识别相当简单,但USB2.0,USB1.x就一对数据线,不能像全速/低速那样仅依靠数据线上拉电阻位置就能识别USB第三种速度:高速。因此对于高速设备的识别就显得稍微复杂些。

  高速设备初始是以一个全速设备的身份出现的,即和全速设备一样,D+线上有一个1.5k的上拉电阻。USB2.0的hub把它当作一个全速设备之后,hub和设备通过一系列握手信号确认双方的身份。在这里对速度的检测是双向的,比如高速的hub需要检测所挂上来的设备是高速、全速还是低速,高速的设备需要检测所连上的hub是USB2.0的还是1.x的,如果是前者,就进行一系列动作切到高速模式工作,如果是后者,就以全速模式工作。

  下图展示了一个高速设备连到USB2.0 hub上的情形:



  hub检测到有设备插入/上电时,向主机通报,主机发送Set_Port_Feature请求让hub复位新插入的设备。设备复位操作是hub通过驱动数据线到复位状态SE0(Single-ended 0,即D+和D-全为低电平),并持续至少10ms。

  高速设备看到复位信号后,通过内部的电流源向D-线持续灌大小为17.78mA电流。因为此时高速设备的1.5k上拉电阻还未撤销,在hub端,全速/低速驱动器形成一个阻抗为45欧姆(Ohm)的终端电阻,2电阻并联后仍是45欧姆左右的阻抗,所以在hub端看到一个约800mV的电压(45欧姆*17.78mA),这就是Chirp
K信号。
Chirp K信号的持续时间是1ms~7ms

  在hub端,虽然下达了复位信号,并一直驱动着SE0,但USB2.0的高速接收器一直在检测Chirp K信号,如果没有Chirp
K信号看到,就继续复位操作,直到复位结束,
之后就在全速模式下操作如果只是一个全速的hub,不支持高速操作,那么该hub不理会设备发送的Chirp K信号,之后设备也不会切换到高速模式。

  设备发送的Chirp K信号结束后100us内,hub必须开始回复一连串的KJKJKJ….序列,向设备表明这是一个USB2.0的hub。这里的KJ序列是连续的,中间不能间断,而且每个K或J的持续时间在40us~60us之间。KJ序列停止后的100~500us内结束复位操作。hub发送Chirp
KJ序列的方式和设备一样,通过电流源向差分数据线交替灌17.78mA的电流实现。


  再回到设备端来。设备检测到6个hub发出的Chirp信号后(3对KJ序列),它必须在500us内切换到高速模式。切换动作有:
1. 断开
1.5k的上拉电阻。
2. 连接D+/D-上的高速终端电阻(high-speed termination),实际上就是全速/低速差分驱动器。
3. 进入默认的高速状态。

  执行1,2两步后,USB信号线上看到的现象就发生变化了:hub发送出来的Chirp KJ序列幅值降到了原先的一半,400mV。这是因为设备端挂载新的终端电阻后,配上原先hub端的终端电阻,并联后的阻抗是22.5欧姆。400mV就是由17.78mA*22.5Ohm得来。以后高速操作的信号幅值就是400mV而不像全速/低速那样的3V。

  至此,高速设备与USB2.0 hub握手完毕,进行后续的480Mbps高速信号通信。

最后附上几幅实际USB高速识别的示波器抓图,图中蓝色信号是D+,黄色信号是D-。

1.数据线D+在T点之前挂上1.5K电阻,在T点被host拉成SE0状态。在近2ms后,设备发送第一个Chirp K,向host通知说:我是一个高速设备,如果可能,请用高速方式与我通信。其幅度是800mV(17.78mA * (45Ohm ||1.5kOhm)
= 800mV,见上文解释)。在这里,
Chirp K的持续时间是2.13ms(a,b两点之间)。


2.这幅图显示了host发出的chirp KJ信号的幅度,头几个KJ是800mv(a,b之间),随后的是400mV。图中可以看到设备在收到第三个chirp J(蓝色短条)后马上把1.5k电阻取消,导致chirp J的幅值下降到400mV。(17.78mA * (45Ohm ||45Ohm)
= 17.78mA * 22.5
Ohm = 400mV)


3.量测了一个chirp J的宽度:43.5us。

 

 

最后附上一张来自Don Anderson的USB System Architecture里的USB HS接口图:


原文来自 http://hi.baidu.com/doyanger/blog/item/af45d995dd64f419d31b704d.html

]]>
https://blog.gotocoding.com/archives/72/feed 0
windbg与虚拟机双机联调笔记 https://blog.gotocoding.com/archives/73?utm_source=rss&utm_medium=rss&utm_campaign=windbg%25e4%25b8%258e%25e8%2599%259a%25e6%258b%259f%25e6%259c%25ba%25e5%258f%258c%25e6%259c%25ba%25e8%2581%2594%25e8%25b0%2583%25e7%25ac%2594%25e8%25ae%25b0 https://blog.gotocoding.com/archives/73#respond Sat, 21 May 2011 22:54:00 +0000 http://localhost/wordpress/?p=63 阅读更多]]> 折腾了好几天,终于把双机联调给弄成功了,把参数与步骤放在这里,希望对与我一样的人有点帮助!

1.首先,安装windbg,并把它的快捷方式发送到桌面上并把中的内容改为:

D:/WinDDK/7600.16385.1/Debuggers/windbg.exe -b -k com:pipe,port=//./pipe/com_1,resets=0

然后点file->symbols file path,打开如下对话框

在里面输入上“srv*c:/symbols*http://msdl.microsoft.com/download/symbols;自已的工程路径”(以分号隔开);

2.然后点file->source file path会出现如下对话框

3.在其中输入要调试的源码文件所在的路径

然后点file->image file path会出现如下对话框

4.输入驱动文件的生成路径!

最后设置windows环境变量(不设环境变量有时会出现错误,可能我的设置有些问题)

变量名如上,变量值与Windbg中的第一个值一样也是“ srv*c:/symbols*http://msdl.microsoft.com/download/symbols”

5.最后为虚拟机设置串口

虚拟串口如上。

6.进入虚拟机系统

右键我的电脑->属性

 

在如下位置单击设置

 

 

 

 

进入如下输入并单击编辑

 

会打开boot.ini文件,拷入以下代码:

 [boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)/WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)/WINDOWS=”Microsoft Windows XP Professional” /noexecute=optin /fastdetect /noguiboot
multi(0)disk(0)rdisk(0)partition(1)/WINDOWS=”Microsoft Windows XP Professional” /noexecute=optin /fastdetect /debug /debugport=com1 /baudrate=115200 /noguiboot

保存重启即可!

7.重启动在虚拟机中选择调试模式引导

然后打开windbg

在出现一如下界面

 

 

依次在”kd>”中输入.!smy noisy

.sympath+ srv*c:/symbols*http://msdl.microsoft.com/download/symbols

.reload /f

来更新符号包,这时windbg会从网上下载符号包,windbg会表现出死机一样的现象,但不能关闭!可能一次会更新不完,有可能需要多次更新!

8.在最后调试时

使用“SRVINSTW_内核驱动安装.EXE”工具或“driver studio”附带的工具”driver monitor”时一定要先把生成的驱动文件拷到C:/windows/system32/drivers后再使用

 

“SRVINSTW_内核驱动安装.EXE”工具安装服务(目标文件要选择拷到C:/windows/system32/drivers目录下的我们自己的驱动文件),才会成功安装!要不然使用“net start 服务名”时会出现找不到路径!

]]>
https://blog.gotocoding.com/archives/73/feed 0