📄 usb in a nutshell - chapter 7 - pdiusbd11 and pic16f87x example.htm
字号:
maximum data to send. In each case we check the actual length against
that of what the host has asked for and trim the size if required.
Then we call WriteBuffertoEndpoint which loads the first 8 bytes into
the endpoint buffer and increment the pointer ready for the next 8
byte packet. </P><PRE> case TYPE_STRING_DESCRIPTOR:
printf("\n\rString Descriptor: LANGID = 0x%04x, Index %d\n\r", \
SetupPacket->wIndex, SetupPacket->wValue & 0xFF);
switch (SetupPacket->wValue & 0xFF){
case 0 : pSendBuffer = (const unsigned char *)&LANGID_Descriptor;
BytesToSend = sizeof(LANGID_Descriptor);
break;
case 1 : pSendBuffer = (const unsigned char *)&Manufacturer_Descriptor;
BytesToSend = sizeof(Manufacturer_Descriptor);
break;
default : pSendBuffer = NULL;
BytesToSend = 0;
}
if (BytesToSend > SetupPacket->wLength)
BytesToSend = SetupPacket->wLength;
WriteBufferToEndPoint();
break;
</PRE>
<P>If any string descriptors are included, there must be a string
descriptor zero present which details what languages are supported by
the device. Any non zero string requests have a LanguageID specified
in wIndex telling what language to support. In our case we cheat
somewhat and ignore the value of wIndex (LANGID) returning the string,
no matter what language is asked for. </P><PRE> default:
ErrorStallControlEndPoint();
break;
}
}
void ErrorStallControlEndPoint(void)
{
unsigned char Buffer[] = { 0x01 };
/* 9.2.7 RequestError - return STALL PID in response to next DATA Stage Transaction */
D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP0_IN, Buffer, 1);
/* or in the status stage of the message. */
D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP0_OUT, Buffer, 1);
}
</PRE>
<P>When we are faced with an invalid request, invalid parameter or a
request the device doesn't support, we must report a request error.
This is defined in 9.2.7 of the specification. A request error will
return a STALL PID in response to the next data stage transaction or
in the status stage of the message. However it notes that to prevent
unnecessary bus traffic the error should be reported at the next data
stage rather than waiting until the status stage. </P><PRE>unsigned char D11ReadEndpoint(unsigned char Endpoint, unsigned char *Buffer)
{
unsigned char D11Header[2];
unsigned char BufferStatus = 0;
/* Select Endpoint */
D11CmdDataRead(Endpoint, &BufferStatus, 1);
/* Check if Buffer is Full */
if(BufferStatus & 0x01)
{
/* Read dummy header - D11 buffer pointer is incremented on each read */
/* and is only reset by a Select Endpoint Command */
D11CmdDataRead(D11_READ_BUFFER, D11Header, 2);
if(D11Header[1]) D11CmdDataRead(D11_READ_BUFFER, Buffer, D11Header[1]);
/* Allow new packets to be accepted */
D11CmdDataWrite(D11_CLEAR_BUFFER, NULL, 0);
}
return D11Header[1];
}
void D11WriteEndpoint(unsigned char Endpoint, const unsigned char *Buffer, unsigned char Bytes)
{
unsigned char D11Header[2];
unsigned char BufferStatus = 0;
D11Header[0] = 0x00;
D11Header[1] = Bytes;
/* Select Endpoint */
D11CmdDataRead(Endpoint, &BufferStatus, 1);
/* Write Header */
D11CmdDataWrite(D11_WRITE_BUFFER, D11Header, 2);
/* Write Packet */
if (Bytes) D11CmdDataWrite(D11_WRITE_BUFFER, Buffer, Bytes);
/* Validate Buffer */
D11CmdDataWrite(D11_VALIDATE_BUFFER, NULL, 0);
}
</PRE>
<P>D11ReadEndpoint and D11WriteEndpoint are PDIUSBD11 specific
functions. The PDIUSBD11 has two dummy bytes prefixing any data read
or write operation. The first byte is reserved, while the second byte
indicates the number of bytes received or to be transmitted. These two
functions take care of this header. </P><PRE>void WriteBufferToEndPoint(void)
{
if (BytesToSend == 0) {
/* If BytesToSend is Zero and we get called again, assume buffer is smaller */
/* than Setup Request Size and indicate end by sending Zero Lenght packet */
D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0);
} else if (BytesToSend >= 8) {
/* Write another 8 Bytes to buffer and send */
D11WriteEndpoint(D11_ENDPOINT_EP0_IN, pSendBuffer, 8);
pSendBuffer += 8;
BytesToSend -= 8;
} else {
/* Buffer must have less than 8 bytes left */
D11WriteEndpoint(D11_ENDPOINT_EP0_IN, pSendBuffer, BytesToSend);
BytesToSend = 0;
}
}
</PRE>
<P>As we have mentioned previously, WriteBufferToEndPoint is
responsible for loading data into the PDIUSBD11 in 8 byte chunks and
adjusting the pointers ready for the next packet. It is called once by
the handler of a request to load the first 8 bytes into the endpoint
buffer. The host will then send an IN token, read this data and the
PDIUSBD11 will generate an interrupt. The EP0 IN handler will then
call WriteBufferToEndpoint to load in the next packet in readiness for
the next IN token from the host. </P>
<P>A transfer is considered complete if all requested bytes have been
read, if a packet is received with a payload less than bMaxPacketSize
or if a zero length packet is returned. Therefore if the BytesToSend
counter hits zero, we assume the data to be sent was a multiple of 8
bytes and we send a zero length packet to indicate this is the last of
the data. However if we have less than 8 bytes left to send, we send
only the remaining bytes. There is no need to pad the data with zeros.
</P><PRE>void loadfromcircularbuffer(void)
{
unsigned char Buffer[10];
unsigned char count;
// Read Buffer Full Status
D11CmdDataRead(D11_ENDPOINT_EP1_IN, Buffer, 1);
if (Buffer[0] == 0){
// Buffer Empty
if (inpointer != outpointer){
// We have bytes to send
count = 0;
do {
Buffer[count++] = circularbuffer[outpointer++];
if (outpointer >= MAX_BUFFER_SIZE) outpointer = 0;
if (outpointer == inpointer) break; // No more data
} while (count < 8); // Maximum Buffer Size
// Now load it into EP1_In
D11WriteEndpoint(D11_ENDPOINT_EP1_IN, Buffer, count);
}
}
}
</PRE>
<P>The loadfromcircularbuffer() routine handles the loading of data
into the EP1 IN endpoint buffer. It is normally called after an EP1 IN
interrupt to reload the buffer ready for the next IN token on EP1.
However in order to send out first packet, we need to load the data
prior to receiving the EP1 IN interrupt. Therefore the routine is also
called after data is received on EP1 OUT. </P>
<P>By also calling the routine from the handler for EP1 OUT, we are
likely to overwrite data in the IN Buffer regardless of whether it has
been sent or not. To prevent this, we determine if the EP1 IN buffer
is empty, before we attempt to reload it with new data. </P><PRE>void D11CmdDataWrite(unsigned char Command, const unsigned char *Buffer, unsigned char Count)
{
I2C_Write(D11_CMD_ADDR, &Command, 1);
if(Count) I2C_Write(D11_DATA_ADDR_WRITE, Buffer, Count);
}
void D11CmdDataRead(unsigned char Command, unsigned char Buffer[], unsigned char Count)
{
I2C_Write(D11_CMD_ADDR, &Command, 1);
if(Count) I2C_Read(D11_DATA_ADDR_READ, Buffer, Count);
}
</PRE>
<P>D11CmdDataWrite and D11CmdDataRead are two PDIUSBD11 specific
functions which are responsible for sending the I2C Address/Command
first and then send or received data on the I2C Bus. Additional lower
level functions are included in the source code but have not been
reproduced here as it is the intend to focus on the USB specific
details. </P>
<P>This example can be used with the bulkUSB.sys example as part of
the Windows DDK's. To load the bulkUSB.sys driver either change the
code to identify itself with a VID of 0x045E and a PID of 0x930A or
change the bulkUSB.inf file accompanying bulkUSB.sys to match the
VID/PID combination you use in this example. </P>
<P>It is then possible to use the user mode console program,
rwbulk.exe to send and receive packets from the circular buffer. Use
</P><PRE> rwbulk -r 80 -w 80 -c 1 -i 1 -o 0
</PRE>
<P>to send 80 byte chunks of data to the PIC16F876. Using payloads
greater than 80 bytes is going to overflow the PIC's circular buffer
in BANK1. </P>
<P>This example has been coded for readability at the expense of code
size. It compiles in 3250 words of FLASH (39% capacity of the
PIC16F876). </P></UL><A name=Acknowledgments><FONT color=green
size=3><B>Acknowledgments</B></FONT></A>
<P>A special acknowledgment must go to Michael DeVault of <A
href="http://www.devasys.com/">DeVaSys Embedded Systems</A>. This
example has been based upon code written by Michael and been
effortlessly developed on the <A
href="http://www.devasys.com/pd11.htm">USBLPT-PD11</A> DeVaSys USB
development board</A> before being ported to the PIC. </P><A
name=SourceCode><FONT color=green size=3><B>Downloading the Source
Code</B></FONT></A>
<UL>
<P>
<LI><A
href="http://www.beyondlogic.org/usbnutshell/usbpicc12.zip">Version
1.2</A>, 14k bytes
<P></P>
<UL><B>Revision History</B><BR><BR>
<UL>
<LI>6th April 2002 - Version 1.2 - Increased I2C speed to match
that of comment. Improved PDIUSBD11 IRQ Handling
<LI>7th January 2002 - Version 1.1 - Added EP1 IN and EP1 OUT Bulk
handler routines and made descriptors load from FLASH
<LI>31st December 2001 - Version 1.0. </LI></UL></UL></LI></UL>
<CENTER>
<TABLE width="80%">
<TBODY>
<TR bgColor=green>
<TD width="50%"><A id=TITLEBLOCK
href="http://www.beyondlogic.org/usbnutshell/usb6.htm"> <IMG
src="USB in a NutShell - Chapter 7 - PDIUSBD11 and PIC16F87x Example.files/HM_More_white_left.gif"
border=0> <B> Chapter 6 : USB Requests</B></A> </TD>
<TD align=right width="50%"><A id=TITLEBLOCK
href="http://www.beyondlogic.org/usbnutshell/"><B>Chapter 8 : A
Generic USB Driver</B> <IMG
src="USB in a NutShell - Chapter 7 - PDIUSBD11 and PIC16F87x Example.files/HM_More_white_right.gif"
border=0> </A> </TD></TR>
<TR><BR><BR><BR><BR>
<TD vAlign=center bgColor=#ffff80>
<UL><BR>
<LI><A
href="http://www.beyondlogic.org/usbnutshell/usb6.htm#SetupPacket">The
Setup Packet</A>
<LI><A
href="http://www.beyondlogic.org/usbnutshell/usb6.htm#StandardDeviceRequests">Standard
Device Requests</A>
<LI><A
href="http://www.beyondlogic.org/usbnutshell/usb6.htm#StandardInterfaceRequests">Standard
Interface Requests</A>
<LI><A
href="http://www.beyondlogic.org/usbnutshell/usb6.htm#StandardEndpointRequests">Standard
Endpoint Requests</A></LI></UL></TD>
<TD vAlign=center bgColor=#ffff80><BR><BR>
<CENTER><FONT color=red>Coming Soon</FONT></CENTER>
<UL>
<LI>Chapter 8: Generic USB Driver
<LI>Chapter 9: HID Class Description and Example
</LI></UL><BR></TD></TR></TBODY></TABLE>
<TABLE width="80%">
<TBODY>
<TR bgColor=green>
<TD colSpan=2><FONT color=white>
<CENTER><B>Comments and Feedback?</B></CENTER></FONT></TD></TR>
<TR bgColor=#ffff80>
<TD align=right width="30%">
<FORM action=/cgi-sys/cgiemail/feedback.txt method=post><INPUT
type=hidden value=http://www.beyondlogic.org/thanks.html
name=success> Comments :<BR>Email Address : </TD>
<TD width="70%"><INPUT size=60 name=Comments><BR><INPUT size=40
name=EmailAddress> (Optional) <INPUT type=submit value=Send>
</TD></TR>
<TR bgColor=#ffff80>
<TD colSpan=2>
<CENTER><FONT size=2><I>Copyright 2001-2005 <A
href="http://www.beyondlogic.org/about.htm">Craig Peacock</A>,
15th June 2005.</I></FONT>
</CENTER></TD></TR></TBODY></TABLE></CENTER></UL></TD></TR></TBODY></TABLE></CENTER></FORM></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -