第七章Bulk设备
小川工作室编写,本书为LM3S的USB芯片编写,上传的均为草稿,还有没修改,可能还有很多地方不足,希望各位网友原谅! QQ:2609828265 TEL:15882446438 E-mail:paulhyde@126.com 第七章Bulk设备 7.1 bulk设备介绍 USB通道的数据传输格式有两种,而且这两种格式还是互斥的。有消息和流两种对于流状态,不具有usb数据的格式,遵循的规则就是先进先出。对于消息通道,它的通信模式符合usb的数据格式,一般有三个阶段组成首先是建立阶段,数据阶段,确认阶段。所有的通信的开始都是由主机方面发起的。 USB协议制定时,为了方便不同设备的开发商基于USB进行设计,定义了不同的设备类来支持不同类型的设备。Bulk也是其中一种,Bulk设备使用端点Bulk传输模式,可以快速传输大量数据。 批量传输(BULK)采用的是流状态传输,批量传送的一个特点就是支持不确定时间内进行大量数据传输,能够保证数据一定可以传输,但是不能保证传输的带宽和传输的延迟。而且批量传输是一种单向的传输,要进行双向传输必须要使用两个通道。本章将介绍双向BULK的批量传输。 7.2 bulk数据类型 usbdbulk.h、usblib.h中已经定义好Bulk设备类中使用的所有数据类型和函数,下面介绍Bulk设备类使用的数据类型。 typedef struct { //定义本Buffer是用于传输还是接收,True为接收,False为发送。 tBoolean bTransmitBuffer; //Callback函数,用于Buffer数据处理完成后 tUSBCallback pfnCallback; //Callback第一个输入参数 void *pvCBData; //数据传输或者接收时调用的函数,用于完成发送或者接收的函数。 tUSBPacketTransfer pfnTransfer; //数据传输或者接收时调用的函数。 //发送时,用于检查是否有足够空间;接收时,用于检查可以接收的数量。 tUSBPacketAvailable pfnAvailable; //在设备模式下,设备类指针 void *pvHandle; //用于存放发送或者接收的数据. unsigned char *pcBuffer; //发送或者接收数据大小 unsigned long ulBufferSize; //RAM Buffer void *pvWorkspace; } tUSBBuffer; tUSBBuffer,数据缓存控制结构体,定义在usblib.h中,用于在Bulk传输过程中,发送数据或者接收数据。是Bulk设备传输的主要载体,结构体内部包含数据发送、接收、处理函数等。 typedef enum { //Bulk 状态没定义 BULK_STATE_UNCONFIGURED, //空闲状态 BULK_STATE_IDLE, //等待数据发送或者结束 BULK_STATE_WAIT_DATA, //等待数据处理. BULK_STATE_WAIT_CLIENT } tBulkState; tBulkState,定义Bulk端点状态。定义在usbdbulk.h。用于端点状态标记与控制,可以保证数据传输不相互冲突。 typedef struct { //USB基地址 unsigned long ulUSBBase; //设备信息 tDeviceInfo *psDevInfo; //配置信息 tConfigDescriptor *psConfDescriptor; //Bulk 接收端点状态 volatile tBulkState eBulkRxState; //Bulk 发送端点状态 volatile tBulkState eBulkTxState; //标志位 volatile unsigned short usDeferredOpFlags; //最后一次发送数据大小 unsigned short usLastTxSize; //连接是否成功 volatile tBoolean bConnected; //IN端点号 unsigned char ucINEndpoint; //OUT端点号 unsigned char ucOUTEndpoint; //接口号 unsigned char ucInterface; } tBulkInstance; tBulkInstance,Bulk设备类实例。定义了Bulk设备类的USB基地址、设备信息、IN端点、OUT端点等信息。 typedef struct { //VID unsigned short usVID; //PID unsigned short usPID; //最大耗电量 unsigned short usMaxPowermA; //电源属性 unsigned char ucPwrAttributes; //接收回调函数,主要用于接收数据处理 tUSBCallback pfnRxCallback; //接收回调函数的第一个参数。 void *pvRxCBData; //发送回调函数,主要用于发送数据处理 tUSBCallback pfnTxCallback; //发送回调函数的第一个参数。 void *pvTxCBData; //字符串描述符集合 const unsigned char * const *ppStringDescriptors; //字符串描述符个数 unsigned long ulNumStringDescriptors; //Bulk设备实例 tBulkInstance *psPrivateBulkData; } tUSBDBulkDevice; tUSBDBulkDevice,Bulk设备类,定义了VID、PID、电源属性、字符串描述符等,还包括了一个Bulk设备类实例。其它设备描述符、配置信息通过API函数储入tBulkInstance定义的Bulk设备实例中。 7.3 API函数 在Bulk设备类API库中定义了11个函数,完成USB Bulk设备初始化、配置及数据处理。以及 11个Buffer操作函数,下面为usbdbulk.h中定义的API函数: void *USBDBulkInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice); void *USBDBulkCompositeInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice); unsigned long USBDBulkPacketWrite(void *pvInstance, unsigned char *pcData, unsigned long ulLength, tBoolean bLast); unsigned long USBDBulkPacketRead(void *pvInstance, unsigned char *pcData, unsigned long ulLength, tBoolean bLast); unsigned long USBDBulkTxPacketAvailable(void *pvInstance); unsigned long USBDBulkRxPacketAvailable(void *pvInstance); void USBDBulkTerm(void *pvInstance); void *USBDBulkSetRxCBData(void *pvInstance, void *pvCBData); void *USBDBulkSetTxCBData(void *pvInstance, void *pvCBData); void USBDBulkPowerStatusSet(void *pvInstance, unsigned char ucPower); tBoolean USBDBulkRemoteWakeupRequest(void *pvInstance); void *USBDBulkInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice); 作用:初始化Bulk设备硬件、协议,把其它配置参数填入psDevice实例中。 参数:ulIndex,USB模块代码,固定值:USB_BASE0。psDevice,Bulk设备类。 返回:指向配置后的tUSBDBulkDevice。 void *USBDBulkCompositeInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice); 作用:初始化Bulk设备协议,本函数在USBDBulkInit中已经调用。 参数:ulIndex,USB模块代码,固定值:USB_BASE0。psDevice,Bulk设备类。 返回:指向配置后的tUSBDBulkDevice。 unsigned long USBDBulkPacketWrite(void *pvInstance, unsigned char *pcData, unsigned long ulLength, tBoolean bLast); 作用:通过Bulk传输发送一个包数据,底层驱动,在Buffer中使用。 参数:pvInstance,tUSBDBulkDevice设备指针。pcData,待写入的数据指针。ulLength,待写入数据的长度。bLast,是否传输结束包。 返回:成功发送长度,可能与ulLength长度不一样。 unsigned long USBDBulkPacketRead(void *pvInstance, unsigned char *pcData, unsigned long ulLength, tBoolean bLast); 作用:通过Bulk传输接收一个包数据,底层驱动,在Buffer中使用。 参数:pvInstance,tUSBDBulkDevice设备指针。pcData,读出数据指针。ulLength,读出数据的长度。bLast,是否是束包。 返回:成功接收长度,可能与ulLength长度不一样。 unsigned long USBDBulkTxPacketAvailable(void *pvInstance); 作用:获取可用发送数据长度。 参数:pvInstance,tUSBDBulkDevice设备指针。 返回:发送包大小,用发送数据长度。 unsigned long USBDBulkRxPacketAvailable(void *pvInstance); 作用:获取接收数据长度。 参数:pvInstance,tUSBDBulkDevice设备指针。 返回:可用接收数据个数,可读取的有效数据。 void USBDBulkTerm(void *pvInstance); 作用:结束Bulk设备。 参数:pvInstance,指向tUSBDBulkDevice。 返回:无。 void *USBDBulkSetRxCBData(void *pvInstance, void *pvCBData); 作用:改变接收回调函数的第一个参数。 参数:pvInstance,指向tUSBDBulkDevice。pvCBData,用于替换的参数 返回:旧参数指针。 void *USBDBulkSetTxCBData(void *pvInstance, void *pvCBData); 作用:改变发送回调函数的第一个参数。 参数:pvInstance,指向tUSBDBulkDevice。pvCBData,用于替换的参数 返回:旧参数指针。 void USBDBulkPowerStatusSet(void *pvInstance, unsigned char ucPower); 作用:修改电源属性、状态。 参数:pvInstance,指向tUSBDBulkDevice。ucPower,电源属性。 返回:无。 tBoolean USBDBulkRemoteWakeupRequest(void *pvInstance); 作用:唤醒请求。 参数:pvInstance,指向tUSBDBulkDevice。 返回:无。 在这些函数中USBDBulkInit和USBDBulkPacketWrite、USBDBulkPacketRead、USBDBulkTxPacketAvailable、USBDBulkRxPacketAvailable函数最重要并且使用最多,USBDBulkInit第一次使用Bulk设备时,用于初始化Bulk设备的配置与控制。USBDBulkPacketRead、USBDBulkPacketWrite、USBDBulkTxPacketAvailable、USBDBulkRxPacketAvailable为Bulk传输数据的底层驱动函数用于驱动Buffer。 usblib.h中定义为11个Buffer操作函数,用于数据发送、接收、以及回调其它函数,下面介绍11个Buffer操作函数: const tUSBBuffer *USBBufferInit(const tUSBBuffer *psBuffer); void USBBufferInfoGet(const tUSBBuffer *psBuffer, tUSBRingBufObject *psRingBuf); unsigned long USBBufferWrite(const tUSBBuffer *psBuffer, const unsigned char *pucData, unsigned long ulLength); void USBBufferDataWritten(const tUSBBuffer *psBuffer, unsigned long ulLength); void USBBufferDataRemoved(const tUSBBuffer *psBuffer, unsigned long ulLength); void USBBufferFlush(const tUSBBuffer *psBuffer); unsigned long USBBufferRead(const tUSBBuffer *psBuffer, unsigned char *pucData, unsigned long ulLength); unsigned long USBBufferDataAvailable(const tUSBBuffer *psBuffer); unsigned long USBBufferSpaceAvailable(const tUSBBuffer *psBuffer); void *USBBufferCallbackDataSet(tUSBBuffer *psBuffer, void *pvCBData); unsigned long USBBufferEventCallback(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData); const tUSBBuffer *USBBufferInit(const tUSBBuffer *psBuffer); 作用:初始化Buffer,把它加入到当前设备中。首次使用Buffer必须使用此函数。 参数:psBuffer,待初始化的Buffer。 返回:指向配置后的Buffer。 void USBBufferInfoGet(const tUSBBuffer *psBuffer, tUSBRingBufObject *psRingBuf); 作用:获取Buffer信息。psRingBuf与psBuffer建立关系。 参数:psBuffer,操作的目标Buffer。psRingBuf,申明一个tUSBRingBufObject 变量。 返回:无。 unsigned long USBBufferWrite(const tUSBBuffer *psBuffer, const unsigned char *pucData, unsigned long ulLength); 作用:写入一组数据。直接写入。 参数:psBuffer,目标Buffer。pucData,待写入数据指针。ulLength,写入长度。 返回:写入的数据长度。 void USBBufferDataWritten(const tUSBBuffer *psBuffer, unsigned long ulLength); 作用:写入一组数据。使用前要调用USBBufferInfoGet。 参数:psBuffer,目标Buffer。ulLength,写入长度。 返回:写入的数据长度。 void USBBufferDataRemoved(const tUSBBuffer *psBuffer, unsigned long ulLength); 作用:从Buffer中移出数据。 参数:psBuffer,目标Buffer。ulLength,移出数据个数。 返回:无。 void USBBufferFlush(const tUSBBuffer *psBuffer); 作用:清除Buffer中数据。 参数:psBuffer,目标Buffer。 返回:无。 unsigned long USBBufferRead(const tUSBBuffer *psBuffer, unsigned char *pucData, unsigned long ulLength); 作用:读取数据。 参数:psBuffer,目标Buffer。pucData,数据存放指针。ulLength,读取个数。 返回:读取数据个数。 unsigned long USBBufferDataAvailable(const tUSBBuffer *psBuffer); 作用:可读取数据个数。 参数:psBuffer,目标Buffer。 返回:可读取数据个数。 unsigned long USBBufferSpaceAvailable(const tUSBBuffer *psBuffer); 作用:可用数据空间大小。 参数:psBuffer,目标Buffer。 返回:数据空间大小。 void *USBBufferCallbackDataSet(tUSBBuffer *psBuffer, void *pvCBData); 作用:修改设备Buffer。 参数:psBuffer,用于替换的新Buffer指针。 返回:旧Buffer指针。 unsigned long USBBufferEventCallback(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData); 作用:Buffer事件调用函数。 参数:pvCBData,设备指针。ulEvent,Buffer事务。ulMsgValue,数据长度。pvMsgData数据指针。 返回:函数是否成功执行。 以上是11个Buffer处理函数,用于Buffer数据接收、发送及处理。在Bulk传输中大量使用。 7.4 Bulk设备开发 Bulk设备开发只需要4步就能完成。如图2所示,Bulk设备配置(主要是字符串描述符)、callback函数编写、USB处理器初始化、数据处理。 <ignore_js_op> 图2 第一步:Bulk设备配置(主要是字符串描述符),按字符串描述符标准完成串描述符配置,进而完成Bulk设备配置。 #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_sysctl.h" #include "inc/hw_udma.h" #include "inc/hw_gpio.h" #include "driverlib/gpio.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/usb.h" #include "usblib/usblib.h" #include "usblib/usb-ids.h" #include "usblib/device/usbdevice.h" #include "usblib/device/usbdbulk.h" #include "uartstdio.h" #include "ustdlib.h" //每次传输数据大小 #define BULK_BUFFER_SIZE 256 unsigned long RxHandler(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData); unsigned long TxHandler(void *pvlCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData); unsigned long EchoNewDataToHost(tUSBDBulkDevice *psDevice, unsigned char *pcData, unsigned long ulNumBytes); #define COMMAND_PACKET_RECEIVED 0x00000001 #define COMMAND_STATUS_UPDATE 0x00000002 volatile unsigned long g_ulFlags = 0; char *g_pcStatus; static volatile tBoolean g_bUSBConfigured = false; volatile unsigned long g_ulTxCount = 0; volatile unsigned long g_ulRxCount = 0; const tUSBBuffer g_sRxBuffer; const tUSBBuffer g_sTxBuffer; //***************************************************************************** // 设备语言描述符. //***************************************************************************** const unsigned char g_pLangDescriptor[] = { 4, USB_DTYPE_STRING, USBShort(USB_LANG_EN_US) }; //***************************************************************************** // 制造商 字符串 描述符 //***************************************************************************** const unsigned char g_pManufacturerString[] = { (17 + 1) * 2, USB_DTYPE_STRING, 'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, 's', 0, 't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 's', 0, }; //***************************************************************************** //产品 字符串 描述符 //***************************************************************************** const unsigned char g_pProductString[] = { (19 + 1) * 2, USB_DTYPE_STRING, 'G', 0, 'e', 0, 'n', 0, 'e', 0, 'r', 0, 'i', 0, 'c', 0, ' ', 0, 'B', 0, 'u', 0, 'l', 0, 'k', 0, ' ', 0, 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0 }; //***************************************************************************** // 产品 序列号 描述符 //***************************************************************************** const unsigned char g_pSerialNumberString[] = { (8 + 1) * 2, USB_DTYPE_STRING, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0 }; //***************************************************************************** // 设备接口字符串描述符 //***************************************************************************** const unsigned char g_pDataInterfaceString[] = { (19 + 1) * 2, USB_DTYPE_STRING, 'B', 0, 'u', 0, 'l', 0, 'k', 0, ' ', 0, 'D', 0, 'a', 0, 't', 0, 'a', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0 }; //***************************************************************************** // 设备配置字符串描述符 //***************************************************************************** const unsigned char g_pConfigString[] = { (23 + 1) * 2, USB_DTYPE_STRING, 'B', 0, 'u', 0, 'l', 0, 'k', 0, ' ', 0, 'D', 0, 'a', 0, 't', 0, 'a', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0, 'f', 0, 'i', 0, 'g', 0, 'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0, 'o', 0, 'n', 0 }; //***************************************************************************** // 字符串描述符集合 //***************************************************************************** const unsigned char * const g_pStringDescriptors[] = { g_pLangDescriptor, g_pManufacturerString, g_pProductString, g_pSerialNumberString, g_pDataInterfaceString, g_pConfigString }; #define NUM_STRING_DESCRIPTORS (sizeof(g_pStringDescriptors) / \ sizeof(unsigned char *)) //***************************************************************************** // 定义Bulk设备实例 //***************************************************************************** tBulkInstance g_sBulkInstance; //***************************************************************************** // 定义Bulk设备 //***************************************************************************** const tUSBDBulkDevice g_sBulkDevice = { 0x1234, USB_PID_BULK, 500, USB_CONF_ATTR_SELF_PWR, USBBufferEventCallback, (void *)&g_sRxBuffer, USBBufferEventCallback, (void *)&g_sTxBuffer, g_pStringDescriptors, NUM_STRING_DESCRIPTORS, &g_sBulkInstance }; //***************************************************************************** // 定义Buffer //***************************************************************************** unsigned char g_pucUSBRxBuffer[BULK_BUFFER_SIZE]; unsigned char g_pucUSBTxBuffer[BULK_BUFFER_SIZE]; unsigned char g_pucTxBufferWorkspace[USB_BUFFER_WORKSPACE_SIZE]; unsigned char g_pucRxBufferWorkspace[USB_BUFFER_WORKSPACE_SIZE]; const tUSBBuffer g_sRxBuffer = { false, // This is a receive buffer. RxHandler, // pfnCallback (void *)&g_sBulkDevice, // Callback data is our device pointer. USBDBulkPacketRead, // pfnTransfer USBDBulkRxPacketAvailable, // pfnAvailable (void *)&g_sBulkDevice, // pvHandle g_pucUSBRxBuffer, // pcBuffer BULK_BUFFER_SIZE, // ulBufferSize g_pucRxBufferWorkspace // pvWorkspace }; const tUSBBuffer g_sTxBuffer = { true, // This is a transmit buffer. TxHandler, // pfnCallback (void *)&g_sBulkDevice, // Callback data is our device pointer. USBDBulkPacketWrite, // pfnTransfer USBDBulkTxPacketAvailable, // pfnAvailable (void *)&g_sBulkDevice, // pvHandle g_pucUSBTxBuffer, // pcBuffer BULK_BUFFER_SIZE, // ulBufferSize g_pucTxBufferWorkspace // pvWorkspace }; tUSBDBulkDevice g_sBulkDevice、tUSBBuffer g_sTxBuffer、tUSBBuffer g_sRxBuffer是管理Bulk设备的主要结构体,它们三者关系如下图: <ignore_js_op> tUSBDBulkDevice g_sBulkDevice主要进行上层协议与通信管理控制,通过USBBufferEventCallback函数处理Buffer数据:数据发送、接收、控制事件,通过g_sTxBuffer中的USBDBulkPacketWrite和USBDBulkTxPacketAvailable实现底层数据发送,并通过TxHandler返回处理结果;通过g_sRxBuffer中的USBDBulkPacketRead和USBDBulkRxPacketAvailable实现底层数据接收,并通过RxHandler返回处理结果。在g_sBulkDevice层可以直接使用Buffer函数对Buffer层操作,并通过TxHandler和RxHandler返回处理结果。所有处理过程中的数据都保存在Buffer层的g_pucUSBTxBuffer或者g_pucUSBRxBuffer,隶属于g_sBulkDevice的一部分。注意:USBDBulkPacketWrite、USBDBulkTxPacketAvailable、USBDBulkPacketRead、USBDBulkRxPacketAvailable由Bulk设备类API定义,可以直接使用。USBBufferEventCallback为Buffer层定义的标准API,用于处理、调用USBDBulkPacketWrite、USBDBulkTxPacketAvailable、USBDBulkPacketRead、USBDBulkRxPacketAvailable、TxHandler和RxHandler完成g_sBulkDevice层发送的数据接收与发送命令。 第二步:完成Callback函数。Callback函数用于处理输出端点、输入端点数据事务。Bulk设备接收回调函数包含以下事务:USB_EVENT_CONNECTED、USB_EVENT_DISCONNECTED、USB_EVENT_RX_AVAILABLE、USB_EVENT_SUSPEND、USB_EVENT_RESUME、USB_EVENT_ERROR。Bulk设备发送回调函数包含了以下事务:USB_EVENT_TX_COMPLETE。如下表:
表2. Bulk事务 根据以上事务编写Callback函数: //***************************************************************************** //USB Bulk设备类返回事件处理函数(callback). //***************************************************************************** unsigned long TxHandler(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData) { //发送完成事件 if(ulEvent == USB_EVENT_TX_COMPLETE) { g_ulTxCount += ulMsgValue; } return(0); } unsigned long RxHandler(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData) { // 接收事件 switch(ulEvent) { //连接成功 case USB_EVENT_CONNECTED: { GPIOPinWrite(GPIO_PORTF_BASE,0x40,0x40); g_bUSBConfigured = true; g_pcStatus = "Host connected."; g_ulFlags |= COMMAND_STATUS_UPDATE; // Flush our buffers. USBBufferFlush(&g_sTxBuffer); USBBufferFlush(&g_sRxBuffer); break; } // 断开连接. case USB_EVENT_DISCONNECTED: { GPIOPinWrite(GPIO_PORTF_BASE,0x40,0x00); g_bUSBConfigured = false; g_pcStatus = "Host disconnected."; g_ulFlags |= COMMAND_STATUS_UPDATE; break; } // 有可能数据接收. case USB_EVENT_RX_AVAILABLE: { tUSBDBulkDevice *psDevice; psDevice = (tUSBDBulkDevice *)pvCBData; // 把接收到的数据发送回去。 return(EchoNewDataToHost(psDevice, pvMsgData, ulMsgValue)); } //挂起,唤醒 case USB_EVENT_SUSPEND: case USB_EVENT_RESUME:break; default:break; } return(0); } //****************************************************************************** //EchoNewDataToHost函数 //****************************************************************************** unsigned long EchoNewDataToHost(tUSBDBulkDevice *psDevice, unsigned char *pcData, unsigned long ulNumBytes) { unsigned long ulLoop, ulSpace, ulCount; unsigned long ulReadIndex; unsigned long ulWriteIndex; tUSBRingBufObject sTxRing; // 获取Buffer信息. USBBufferInfoGet(&g_sTxBuffer, &sTxRing); // 有多少可能空间 ulSpace = USBBufferSpaceAvailable(&g_sTxBuffer); // 改变数据 ulLoop = (ulSpace < ulNumBytes) ? ulSpace : ulNumBytes; ulCount = ulLoop; // 更新接收到的数据个数 g_ulRxCount += ulNumBytes; ulReadIndex = (unsigned long)(pcData - g_pucUSBRxBuffer); ulWriteIndex = sTxRing.ulWriteIndex; while(ulLoop) { //更新接收的数据 if((g_pucUSBRxBuffer[ulReadIndex] >= 'a') && (g_pucUSBRxBuffer[ulReadIndex] <= 'z')) { //转换 g_pucUSBTxBuffer[ulWriteIndex] = (g_pucUSBRxBuffer[ulReadIndex] - 'a') + 'A'; } else { //转换 if((g_pucUSBRxBuffer[ulReadIndex] >= 'A') && (g_pucUSBRxBuffer[ulReadIndex] <= 'Z')) { //转换 g_pucUSBTxBuffer[ulWriteIndex] = (g_pucUSBRxBuffer[ulReadIndex] - 'Z') + 'z'; } else { //转换 g_pucUSBTxBuffer[ulWriteIndex] = g_pucUSBRxBuffer[ulReadIndex]; } } // 更新指针 ulWriteIndex++; ulWriteIndex = (ulWriteIndex == BULK_BUFFER_SIZE) ? 0 : ulWriteIndex; ulReadIndex++; ulReadIndex = (ulReadIndex == BULK_BUFFER_SIZE) ? 0 : ulReadIndex; ulLoop--; } // 发送数据 USBBufferDataWritten(&g_sTxBuffer, ulCount); return(ulCount); } 第三步:系统初始化,配置内核电压、系统主频、使能端口、LED控制等,本例中使用4个LED进行指示数据传输。在这个例子中,Bulk传输接收的数据发送给主机。原理图如图3所示: <ignore_js_op> 图3 系统初始化: unsigned long ulTxCount = 0; unsigned long ulRxCount = 0; // char pcBuffer[16]; //设置内核电压、主频 50Mhz SysCtlLDOSet(SYSCTL_LDO_2_75V); SysCtlClockSet(SYSCTL_XTAL_8MHZ | SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN ); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,0xf0); GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,0x0f); HWREG(GPIO_PORTF_BASE+GPIO_O_PUR) |= 0x0f; // 初始化发送与接收Buffer. USBBufferInit((tUSBBuffer *)&g_sTxBuffer); USBBufferInit((tUSBBuffer *)&g_sRxBuffer); // 初始化Bulk设备 USBDBulkInit(0, (tUSBDBulkDevice *)&g_sBulkDevice); 第四步:数据处理。主要使用11个Buffer处理函数,用于Buffer数据接收、发送及处理。在Bulk传输中大量使用。 while(1) { //等待连接结束. while((g_ulFlags & FLAG_CONNECTED) == 0) { } //初始化Buffer g_sBuffer.pucFill = g_sBuffer.pucBuffer; g_sBuffer.pucPlay = g_sBuffer.pucBuffer; g_sBuffer.ulFlags = 0; //从Bulk设备类中获取数据 if(USBBulkBufferOut(g_pvBulkDevice, (unsigned char *)g_sBuffer.pucFill, BULK_PACKET_SIZE, USBBufferCallback) == 0) { //标记数据放入buffer中. g_sBuffer.ulFlags |= SBUFFER_FLAGS_FILLING; } //设备连接到主机. while(g_ulFlags & FLAG_CONNECTED) { // 检查音量是否有改变. if(g_ulFlags & FLAG_VOLUME_UPDATE) { // 清除更新音量标志. g_ulFlags &= ~FLAG_VOLUME_UPDATE; // 修改音量,自行添加代码.在此以LED灯做指示。 //UpdateVolume(); GPIOPinWrite(GPIO_PORTF_BASE,0x40,~GPIOPinRead(GPIO_PORTF_BASE,0x40)); } //是否静音 if(g_ulFlags & FLAG_MUTE_UPDATE) { //修改静音状态,自行添加函数.在此以LED灯做指示。 //UpdateMute(); if(g_ulFlags & FLAG_MUTED) GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x20); else GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x00); // 清除静音标志 g_ulFlags &= ~FLAG_MUTE_UPDATE; } } } //****************************************************************************** //USBBulkBufferOut 的Callback入口参数 //****************************************************************************** void USBBufferCallback(void *pvBuffer, unsigned long ulParam, unsigned long ulEvent) { //数据处理,自行加入代码。 // Your Codes ......... //再一次获取数据. USBBulkBufferOut(g_pvBulkDevice, (unsigned char *)g_sBuffer.pucFill, BULK_PACKET_SIZE, USBBufferCallback); } 使用上面四步就完成Bulk设备开发。Bulk设备开发时要加入两个lib库函数: usblib.lib和DriverLib.lib,在启动代码中加入USB0DeviceIntHandler中断服务函数。以上Bulk设备开发完成,在Win xp下运行效果如下图所示: <ignore_js_op> <ignore_js_op> 驱动安装完成 在枚举过程中可以看出,在电脑右下脚可以看到“Generic Bulk Device”字样,标示正在进行枚举,并手手动安装驱动。枚举成功后,在“设备管理器”的“Stellaris Bulk Device”中看到“Generic Bulk Device”设备,如下图。现在Bulk设备可以正式使用。 <ignore_js_op> Bulk设备要配合上位机使用,上位机发送字符串通过USB Bulk设备转换后发送给主机。运行图如下: <ignore_js_op> 上位机源码如下: #include <windows.h> #include <strsafe.h> #include <initguid.h> #include "lmusbdll.h" #include "luminary_guids.h" //**************************************************************************** // Buffer size definitions. //**************************************************************************** #define MAX_STRING_LEN 256 #define MAX_ENTRY_LEN 256 #define USB_BUFFER_LEN 1216 //**************************************************************************** // The build version number //**************************************************************************** #define BLDVER "6075" //**************************************************************************** // The number of bytes we read and write per transaction if in echo mode. //**************************************************************************** #define ECHO_PACKET_SIZE 1216 //**************************************************************************** // Buffer into which error messages are written. //**************************************************************************** TCHAR g_pcErrorString[MAX_STRING_LEN]; //**************************************************************************** // The number of bytes transfered in the last measurement interval. //**************************************************************************** ULONG g_ulByteCount = 0; //**************************************************************************** // The total number of packets transfered. //**************************************************************************** ULONG g_ulPacketCount = 0; //**************************************************************************** LPTSTR GetSystemErrorString(DWORD dwError) { DWORD dwRetcode; // Ask Windows for the error message description. dwRetcode = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, "%0", dwError, 0, g_pcErrorString, MAX_STRING_LEN, NULL); if(dwRetcode == 0) { return((LPTSTR)L"Unknown"); } else { // Remove the trailing "\n\r" if present. if(dwRetcode >= 2) { if(g_pcErrorString[dwRetcode - 2] == '\r') { g_pcErrorString[dwRetcode - 2] = '\0'; } } return(g_pcErrorString); } } //**************************************************************************** // Print the throughput in terms of Kbps once per second. //**************************************************************************** void UpdateThroughput(void) { static ULONG ulStartTime = 0; static ULONG ulLast = 0; ULONG ulNow; ULONG ulElapsed; SYSTEMTIME sSysTime; // Get the current system time. GetSystemTime(&sSysTime); ulNow = (((((sSysTime.wHour * 60) + sSysTime.wMinute) * 60) + sSysTime.wSecond) * 1000) + sSysTime.wMilliseconds; // If this is the first call, set the start time. if(ulStartTime == 0) { ulStartTime = ulNow; ulLast = ulNow; return; } // How much time has elapsed since the last measurement? ulElapsed = (ulNow > ulStartTime) ? (ulNow - ulStartTime) : (ulStartTime - ulNow); // We dump a new measurement every second. if(ulElapsed > 1000) { printf("\r%6dKbps Packets: %10d ", ((g_ulByteCount * 8) / ulElapsed), g_ulPacketCount); g_ulByteCount = 0; ulStartTime = ulNow; } } //**************************************************************************** // The main application entry function. //**************************************************************************** int main(int argc, char *argv[]) { BOOL bResult; BOOL bDriverInstalled; BOOL bEcho; char szBuffer[USB_BUFFER_LEN]; ULONG ulWritten; ULONG ulRead; ULONG ulLength; DWORD dwError; LMUSB_HANDLE hUSB; // Are we operating in echo mode or not? The "-e" parameter tells the // app to echo everything it receives back to the device unchanged. bEcho = ((argc > 1) && (argv[1][1] == 'e')) ? TRUE : FALSE; // Print a cheerful welcome. printf("\nStellaris Bulk USB Device Example\n"); printf( "---------------------------------\n\n"); printf("Version %s\n\n", BLDVER); if(!bEcho) { printf("This is a partner application to the usb_dev_bulk example\n"); printf("shipped with StellarisWare software releases for USB-enabled\n"); printf("boards. Strings entered here are sent to the board which\n"); printf("inverts the case of the characters in the string and returns\n"); printf("them to the host.\n\n"); } else { printf("If run with the \"-e\" command line switch, this application\n"); printf("echoes all data received on the bulk IN endpoint to the bulk\n"); printf("OUT endpoint. This feature may be helpful during development\n"); printf("and debug of your own USB devices. Note that this will not\n"); printf("do anything exciting if run with the usb_dev_bulk example\n"); printf("device attached since it expects the host to initiate transfers.\n\n"); } // Find our USB device and prepare it for communication. hUSB = InitializeDevice(BULK_VID, BULK_PID, (LPGUID)&(GUID_DEVINTERFACE_STELLARIS_BULK), &bDriverInstalled); if(hUSB) { // Are we operating in echo mode or not? The "-e" parameter tells the // app to echo everything it receives back to the device unchanged. if(bEcho) { printf("Running in echo mode. Press Ctrl+C to exit.\n\n" "Throughput: 0Kbps Packets: 0"); while(1) { // Read a block of data from the device. dwError = ReadUSBPacket(hUSB, szBuffer, USB_BUFFER_LEN, &ulRead, INFINITE, NULL); if(dwError != ERROR_SUCCESS) { // We failed to read from the device. printf("\n\nError %d (%S) reading from bulk IN pipe.\n", dwError, GetSystemErrorString(dwError)); break; } else { // Update our byte and packet counters. g_ulByteCount += ulRead; g_ulPacketCount++; // Write the data back out to the device. bResult = WriteUSBPacket(hUSB, szBuffer, ulRead, &ulWritten); if(!bResult) { // We failed to write the data for some reason. dwError = GetLastError(); printf("\n\nError %d (%S) writing to bulk OUT pipe.\n", dwError, GetSystemErrorString(dwError)); break; } // Display the throughput. UpdateThroughput(); } } } else { // We are running in normal mode. Keep sending and receiving // strings until the user indicates that it is time to exit. while(1) { // The device was found and successfully configured. Now get a string from // the user... do { printf("\nEnter a string (EXIT to exit): "); fgets(szBuffer, MAX_ENTRY_LEN, stdin); printf("\n"); // How many characters were entered (including the trailing '\n')? ulLength = (ULONG)strlen(szBuffer); if(ulLength <= 1) { printf("\nPlease enter some text.\n"); ulLength = 0; } else { // Get rid of the trailing '\n' if there is one there. if(szBuffer[ulLength - 1] == '\n') { szBuffer[ulLength - 1] = '\0'; ulLength--; } } } while(ulLength == 0); if(!(strcmp("EXIT", szBuffer))) { printf("Exiting on user request.\n"); break; } // Write the user's string to the device. bResult = WriteUSBPacket(hUSB, szBuffer, ulLength, &ulWritten); if(!bResult) { dwError = GetLastError(); printf("Error %d (%S) writing to bulk OUT pipe.\n", dwError, GetSystemErrorString(dwError)); } else { // We wrote data successfully so now read it back. printf("Wrote %d bytes to the device. Expected %d\n", ulWritten, ulLength); // We expect the same number of bytes as we just sent. dwError = ReadUSBPacket(hUSB, szBuffer, ulWritten, &ulRead, INFINITE, NULL); if(dwError != ERROR_SUCCESS) { // We failed to read from the device. printf("Error %d (%S) reading from bulk IN pipe.\n", dwError, GetSystemErrorString(dwError)); } else { szBuffer[ulRead] = '\0'; printf("Read %d bytes from device. Expected %d\n", ulRead, ulWritten); printf("\nReturned string: \"%s\"\n", szBuffer); } } } } } else { // An error was reported while trying to connect to the device. dwError = GetLastError(); printf("\nUnable to initialize the Stellaris Bulk USB Device.\n"); printf("Error code is %d (%S)\n\n", dwError, GetSystemErrorString(dwError)); printf("Please make sure you have a Stellaris USB-enabled evaluation\n"); printf("or development kit running the usb_dev_bulk example\n"); printf("application connected to this system via the \"USB OTG\" or\n"); printf("\"USB DEVICE\" connectors. Once the device is connected, run\n"); printf("this application again.\n\n"); printf("\nPress \"Enter\" to exit: "); fgets(szBuffer, MAX_STRING_LEN, stdin); printf("\n"); return(2); } TerminateDevice(hUSB); return(0); } Bulk设备开发源码如下: #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_sysctl.h" #include "inc/hw_udma.h" #include "inc/hw_gpio.h" #include "driverlib/gpio.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/usb.h" #include "usblib/usblib.h" #include "usblib/usb-ids.h" #include "usblib/device/usbdevice.h" #include "usblib/device/usbdbulk.h" #include "uartstdio.h" #include "ustdlib.h" //每次传输数据大小 #define BULK_BUFFER_SIZE 256 unsigned long RxHandler(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData); unsigned long TxHandler(void *pvlCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData); unsigned long EchoNewDataToHost(tUSBDBulkDevice *psDevice, unsigned char *pcData, unsigned long ulNumBytes); #define COMMAND_PACKET_RECEIVED 0x00000001 #define COMMAND_STATUS_UPDATE 0x00000002 volatile unsigned long g_ulFlags = 0; char *g_pcStatus; static volatile tBoolean g_bUSBConfigured = false; volatile unsigned long g_ulTxCount = 0; volatile unsigned long g_ulRxCount = 0; const tUSBBuffer g_sRxBuffer; const tUSBBuffer g_sTxBuffer; //***************************************************************************** // 设备语言描述符. //***************************************************************************** const unsigned char g_pLangDescriptor[] = { 4, USB_DTYPE_STRING, USBShort(USB_LANG_EN_US) }; //***************************************************************************** // 制造商 字符串 描述符 //***************************************************************************** const unsigned char g_pManufacturerString[] = { (17 + 1) * 2, USB_DTYPE_STRING, 'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, 's', 0, 't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 's', 0, }; //***************************************************************************** //产品 字符串 描述符 //***************************************************************************** const unsigned char g_pProductString[] = { (19 + 1) * 2, USB_DTYPE_STRING, 'G', 0, 'e', 0, 'n', 0, 'e', 0, 'r', 0, 'i', 0, 'c', 0, ' ', 0, 'B', 0, 'u', 0, 'l', 0, 'k', 0, ' ', 0, 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0 }; //***************************************************************************** // 产品 序列号 描述符 //***************************************************************************** const unsigned char g_pSerialNumberString[] = { (8 + 1) * 2, USB_DTYPE_STRING, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0 }; //***************************************************************************** // 设备接口字符串描述符 //***************************************************************************** const unsigned char g_pDataInterfaceString[] = { (19 + 1) * 2, USB_DTYPE_STRING, 'B', 0, 'u', 0, 'l', 0, 'k', 0, ' ', 0, 'D', 0, 'a', 0, 't', 0, 'a', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0 }; //***************************************************************************** // 设备配置字符串描述符 //***************************************************************************** const unsigned char g_pConfigString[] = { (23 + 1) * 2, USB_DTYPE_STRING, 'B', 0, 'u', 0, 'l', 0, 'k', 0, ' ', 0, 'D', 0, 'a', 0, 't', 0, 'a', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0, 'f', 0, 'i', 0, 'g', 0, 'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0, 'o', 0, 'n', 0 }; //***************************************************************************** // 字符串描述符集合 //***************************************************************************** const unsigned char * const g_pStringDescriptors[] = { g_pLangDescriptor, g_pManufacturerString, g_pProductString, g_pSerialNumberString, g_pDataInterfaceString, g_pConfigString }; #define NUM_STRING_DESCRIPTORS (sizeof(g_pStringDescriptors) / \ sizeof(unsigned char *)) //***************************************************************************** // 定义Bulk设备实例 //***************************************************************************** tBulkInstance g_sBulkInstance; //***************************************************************************** // 定义Bulk设备 //***************************************************************************** const tUSBDBulkDevice g_sBulkDevice = { 0x1234, USB_PID_BULK, 500, USB_CONF_ATTR_SELF_PWR, USBBufferEventCallback, (void *)&g_sRxBuffer, USBBufferEventCallback, (void *)&g_sTxBuffer, g_pStringDescriptors, NUM_STRING_DESCRIPTORS, &g_sBulkInstance }; //***************************************************************************** // 定义Buffer //***************************************************************************** unsigned char g_pucUSBRxBuffer[BULK_BUFFER_SIZE]; unsigned char g_pucUSBTxBuffer[BULK_BUFFER_SIZE]; unsigned char g_pucTxBufferWorkspace[USB_BUFFER_WORKSPACE_SIZE]; unsigned char g_pucRxBufferWorkspace[USB_BUFFER_WORKSPACE_SIZE]; const tUSBBuffer g_sRxBuffer = { false, // This is a receive buffer. RxHandler, // pfnCallback (void *)&g_sBulkDevice, // Callback data is our device pointer. USBDBulkPacketRead, // pfnTransfer USBDBulkRxPacketAvailable, // pfnAvailable (void *)&g_sBulkDevice, // pvHandle g_pucUSBRxBuffer, // pcBuffer BULK_BUFFER_SIZE, // ulBufferSize g_pucRxBufferWorkspace // pvWorkspace }; const tUSBBuffer g_sTxBuffer = { true, // This is a transmit buffer. TxHandler, // pfnCallback (void *)&g_sBulkDevice, // Callback data is our device pointer. USBDBulkPacketWrite, // pfnTransfer USBDBulkTxPacketAvailable, // pfnAvailable (void *)&g_sBulkDevice, // pvHandle g_pucUSBTxBuffer, // pcBuffer BULK_BUFFER_SIZE, // ulBufferSize g_pucTxBufferWorkspace // pvWorkspace }; //***************************************************************************** //USB Bulk设备类返回事件处理函数(callback). //***************************************************************************** unsigned long TxHandler(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData) { //发送完成事件 if(ulEvent == USB_EVENT_TX_COMPLETE) { g_ulTxCount += ulMsgValue; } return(0); } unsigned long RxHandler(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue, void *pvMsgData) { // 接收事件 switch(ulEvent) { //连接成功 case USB_EVENT_CONNECTED: { GPIOPinWrite(GPIO_PORTF_BASE,0x40,0x40); g_bUSBConfigured = true; g_pcStatus = "Host connected."; g_ulFlags |= COMMAND_STATUS_UPDATE; // Flush our buffers. USBBufferFlush(&g_sTxBuffer); USBBufferFlush(&g_sRxBuffer); break; } // 断开连接. case USB_EVENT_DISCONNECTED: { GPIOPinWrite(GPIO_PORTF_BASE,0x40,0x00); g_bUSBConfigured = false; g_pcStatus = "Host disconnected."; g_ulFlags |= COMMAND_STATUS_UPDATE; break; } // 有可能数据接收. case USB_EVENT_RX_AVAILABLE: { tUSBDBulkDevice *psDevice; psDevice = (tUSBDBulkDevice *)pvCBData; // 把接收到的数据发送回去。 return(EchoNewDataToHost(psDevice, pvMsgData, ulMsgValue)); } //挂起,唤醒 case USB_EVENT_SUSPEND: case USB_EVENT_RESUME:break; default:break; } return(0); } //****************************************************************************** //EchoNewDataToHost函数 //****************************************************************************** unsigned long EchoNewDataToHost(tUSBDBulkDevice *psDevice, unsigned char *pcData, unsigned long ulNumBytes) { unsigned long ulLoop, ulSpace, ulCount; unsigned long ulReadIndex; unsigned long ulWriteIndex; tUSBRingBufObject sTxRing; // 获取Buffer信息. USBBufferInfoGet(&g_sTxBuffer, &sTxRing); // 有多少可能空间 ulSpace = USBBufferSpaceAvailable(&g_sTxBuffer); // 改变数据 ulLoop = (ulSpace < ulNumBytes) ? ulSpace : ulNumBytes; ulCount = ulLoop; // 更新接收到的数据个数 g_ulRxCount += ulNumBytes; ulReadIndex = (unsigned long)(pcData - g_pucUSBRxBuffer); ulWriteIndex = sTxRing.ulWriteIndex; while(ulLoop) { //更新接收的数据 if((g_pucUSBRxBuffer[ulReadIndex] >= 'a') && (g_pucUSBRxBuffer[ulReadIndex] <= 'z')) { //转换 g_pucUSBTxBuffer[ulWriteIndex] = (g_pucUSBRxBuffer[ulReadIndex] - 'a') + 'A'; } else { //转换 if((g_pucUSBRxBuffer[ulReadIndex] >= 'A') && (g_pucUSBRxBuffer[ulReadIndex] <= 'Z')) { //转换 g_pucUSBTxBuffer[ulWriteIndex] = (g_pucUSBRxBuffer[ulReadIndex] - 'Z') + 'z'; } else { //转换 g_pucUSBTxBuffer[ulWriteIndex] = g_pucUSBRxBuffer[ulReadIndex]; } } // 更新指针 ulWriteIndex++; ulWriteIndex = (ulWriteIndex == BULK_BUFFER_SIZE) ? 0 : ulWriteIndex; ulReadIndex++; ulReadIndex = (ulReadIndex == BULK_BUFFER_SIZE) ? 0 : ulReadIndex; ulLoop--; } // 发送数据 USBBufferDataWritten(&g_sTxBuffer, ulCount); return(ulCount); } //***************************************************************************** // 应用主函数. //***************************************************************************** int main(void) { unsigned long ulTxCount = 0; unsigned long ulRxCount = 0; // char pcBuffer[16]; //设置内核电压、主频 50Mhz SysCtlLDOSet(SYSCTL_LDO_2_75V); SysCtlClockSet(SYSCTL_XTAL_8MHZ | SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN ); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,0xf0); GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,0x0f); HWREG(GPIO_PORTF_BASE+GPIO_O_PUR) |= 0x0f; // 初始化发送与接收Buffer. USBBufferInit((tUSBBuffer *)&g_sTxBuffer); USBBufferInit((tUSBBuffer *)&g_sRxBuffer); // 初始化Bulk设备 USBDBulkInit(0, (tUSBDBulkDevice *)&g_sBulkDevice); while(1) { if(g_ulFlags & COMMAND_STATUS_UPDATE) { //清除更新标志 g_ulFlags &= ~COMMAND_STATUS_UPDATE; GPIOPinWrite(GPIO_PORTF_BASE,0x30,0x30); } // 发送完成 if(ulTxCount != g_ulTxCount) { ulTxCount = g_ulTxCount; GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x10); //usnprintf(pcBuffer, 16, " %d ", ulTxCount); } // 接收完成 if(ulRxCount != g_ulRxCount) { ulRxCount = g_ulRxCount; GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x20); } } } |
第七章Bulk设备的更多相关文章
- 第七章 LED将为我们闪烁:控制发光二极管
第七章 LED将为我们闪烁:控制发光二极管 本章我们将会看到一个完整的linux驱动程序,通过linux驱动程序控制LED的四个小灯,通俗的说就是通过向linux驱动程序来控制LED小灯的开关.用到 ...
- apue第七章学习总结
apue第七章学习总结 1.main函数 程序是如何执行有关的c程序的? C程序总是从main函数开始执行.main函数的原型是 int main(int argc,char *argv[]); 其中 ...
- (转)iOS Wow体验 - 第七章 - 操作图例与触屏人机工学
本文是<iOS Wow Factor:Apps and UX Design Techniques for iPhone and iPad>第七章译文精选,其余章节将陆续放出.上一篇:Wow ...
- Testlink1.9.17使用方法(第七章 测试用例集管理)
第七章 测试用例集管理 QQ交流群:585499566 测试用例准备好以后,可以对测试用例集进行相关的操作. 一. 添加测试用例到测试计划中 在主页的“当前测试计划”下拉列表里-->选择一个测试 ...
- 《Linux内核设计与实现》第七章读书笔记
第七章.中断和中断处理 7.1中断 中断使得硬件得以发出通知给处理器.中断随时可以产生,内核随时可能因为新来到的中断而被打断. 不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标志.操作系统给 ...
- RHCE学习笔记 管理1 (第六章 第七章)
第六章 利用linux 文件系统权限文件访问 1.linux文件系统权限 文件的权限分为: rwx 读/写/执行 ls -l /home 查看/home下文件 ls -ld /home ...
- 第七章终结篇——8251A的总结
总算把这个第七章复习完了,我把剩下一点关于8251A的发上来吧 本来在讲解8251A书本上还有关于RS232和串口通信的讲解,但是太浅了,就不放了,有兴趣的朋友可以自行参考其他文章 串行通信芯片825 ...
- 【知识强化】第七章 输入/输出系统 7.3 I/O接口
下面我们进入第七章的第三节,I/O接口. I/O接口呢就是解决了外设和主机之间的一个连接的问题.那么我们这一节就要来看一下I/O接口它有哪些功能,以及它是怎么组成的,还有就是我们主机如何来定位到那样一 ...
- 【知识强化】第七章 输入/输出系统 7.1 I/O系统基本概念
那么下面,我们将要进入计算机组成原理的最后一章,也就是我们的第七章,输入输出系统的学习.那么这一部分内容呢,我们之前呢一直在提,但是并没有详细地讲解,那么进入到我们第七章输入输出系统这一部分,我们就要 ...
随机推荐
- 使用SQLdiag Utility搜集SQL Server诊断信息
SQLdiag Utility用于搜集诊断信息,给Microsoft技术支持人员做为判断依据. 使用SQLdiag 会进行信息搜集类型 Windows 系统性能日志 Windows 系统日志 SQL ...
- 如何给windows窗体程序打包成一个安装包
http://blog.csdn.net/xyy410874116/article/details/6341787 给windows窗体程序打包成一个安装包:具体操作在:http://hi.baidu ...
- java String的equals,intern方法(转载)
JAVA中的equals和==的区别 ==比较的是2个对象的地址,而equals比较的是2个对象的内容. 显然,当equals为true时,==不一定为true: 基础知识的重要性,希望引起大家的重视 ...
- Google maps not working IE11
参考原因: http://www.easypagez.eu/maps/ieworking.html 如果还不行的话,在map的样式上加上width:100%;height:100% ;position ...
- MBR与分区表备份与恢复
常用工具列表 dd 数据复制,转换实用工具 tar GNU磁盘存档实用工具 cpio 数据存档实用工 ...
- Java实战之02Hibernate-08二级缓存
十四.Hibernate的二级缓存 1.Hibernate的缓存结构 2.由于二级缓存被多线程共享,就必须有一定的事务访问策略 非严格读写:READ UNCOMMITTED 读写型:READ COMM ...
- How to: Extract files from a compiled setup.exe created by Inno setup
Use innounp.exe to unpack setup.exe created by using Inno setup: for example, unpack all the files w ...
- NFS vs. CIFS
1. CIFS协议分析 CIFS(Common Internet File System,公共互联网文件系统)是当前主流异构平台共享文件系统之一.主要应用在NT/Windows环境下,是由Micro ...
- Linux C 程序 基础语法(1)
1.Linux 下第一支C程序,控制台打印一句话. vi first.c //linux新建文件 #include<stdio.h> int main() { printf("w ...
- jQuery—一些常见方法(3)【width(),innerWidth(),outerWidth()】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...