?? usb in a nutshell - chapter 7 - pdiusbd11 and pic16f87x example.htm
字號:
the char array is fixed in the header and is not dynamic. </P><PRE>#define MAX_BUFFER_SIZE 80
bank1 unsigned char circularbuffer[MAX_BUFFER_SIZE];
unsigned char inpointer;
unsigned char outpointer;
unsigned char *pSendBuffer;
unsigned char BytesToSend;
unsigned char CtlTransferInProgress;
unsigned char DeviceAddress;
unsigned char DeviceConfigured;
#define PROGRESS_IDLE 0
#define PROGRESS_ADDRESS 3
void main (void)
{
TRISB = 0x03; /* Int & Suspend Inputs */
RB3 = 1; /* Device Not Configured (LED) */
RB2 = 0; /* Reset PDIUSBD11 */
InitUART();
printf("Initialising\n\r");
I2C_Init();
RB2 = 1; /* Bring PDIUSBD11 out of reset */
ADCON1 = 0x80; /* ADC Control - All 8 Channels Enabled, */
/* supporting upgrade to 16F877 */
USB_Init();
printf("PDIUSBD11 Ready for connection\n\r");
while(1)
if (!RB0) D11GetIRQ(); /* If IRQ is Low, PDIUSBD11 has an Interrupt Condition */
}
</PRE>
<P>The main function is example dependent. It's responsible for
initialising the direction of the I/O Ports, initialising the I2C
interface, Analog to Digital Converters and PDIUSBD11. Once everything
is configured it keeps calling D11GetIRQ which processes PDIUSBD11
Interrupt Requests. </P><PRE>void USB_Init(void)
{
unsigned char Buffer[2];
/* Disable Hub Function in PDIUSBD11 */
Buffer[0] = 0x00;
D11CmdDataWrite(D11_SET_HUB_ADDRESS, Buffer, 1);
/* Set Address to zero (default) and enable function */
Buffer[0] = 0x80;
D11CmdDataWrite(D11_SET_ADDRESS_ENABLE, Buffer, 1);
/* Enable function generic endpoints */
Buffer[0] = 0x02;
D11CmdDataWrite(D11_SET_ENDPOINT_ENABLE, Buffer, 1);
/* Set Mode - Enable SoftConnect */
Buffer[0] = 0x97; /* Embedded Function, SoftConnect, Clk Run, No LazyClk, Remote Wakeup */
Buffer[1] = 0x0B; /* CLKOut = 4MHz */
D11CmdDataWrite(D11_SET_MODE, Buffer, 2);
}
</PRE>
<P>The USB Init function initialises the PDIUSBD11. This
initialisation procedure has been omitted from the Philips PDIUSBD11
datasheet but is available from their <A
href="http://www.semiconductors.philips.com/buses/usb/products/device/pdiusbd11/faq/index.html">FAQ</A>.
The last command enables the soft-connect pull up resistor on D+
indicating it is a <A
href="http://www.beyondlogic.org/usbnutshell/usb2.htm#SpeedIdentification">full
speed</A> device but also advertises its presence on the universal
serial bus. </P><PRE>void D11GetIRQ(void)
{
unsigned short Irq;
unsigned char Buffer[1];
/* Read Interrupt Register to determine source of interrupt */
D11CmdDataRead(D11_READ_INTERRUPT_REGISTER, (unsigned char *)&Irq, 2);
if (Irq) printf("Irq = 0x%X: ",Irq);
</PRE>
<P>Main() keeps calling the D11GetIRQ in a loop. This function reads
the PDIUSBD11's Interrupt Register to establish if any interrupts are
pending. If this is the case it will act upon them, otherwise it will
continue to loop. Other USB devices may have a series of interrupt
vectors assigned to each endpoint. In this case each ISR will service
the appropriate interrupt removing the if statements. </P><PRE> if (Irq & D11_INT_BUS_RESET) {
printf("Bus Reset\n\r");
USB_Init();
}
if (Irq & D11_INT_EP0_OUT) {
printf("EP0_Out: ");
Process_EP0_OUT_Interrupt();
}
if (Irq & D11_INT_EP0_IN) {
printf("EP0_In: \n\r");
if (CtlTransferInProgress == PROGRESS_ADDRESS) {
D11CmdDataWrite(D11_SET_ADDRESS_ENABLE,&DeviceAddress,1);
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_IN, Buffer, 1);
CtlTransferInProgress = PROGRESS_IDLE;
}
else {
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_IN, Buffer, 1);
WriteBufferToEndPoint();
}
}
</PRE>
<P>The If statements work down in order of priority. The highest
priority interrupt is the bus reset. This simply calls USB_Init which
re-initialises the USB function. The next highest priority is the
default pipe consisting of EP0 OUT and EP1 IN. This is where all the
enumeration and control requests are sent. We branch to another
function to handle the EP0_OUT requests. </P>
<P>When a request is made by the host and it wants to receive data,
the PIC16F876 will send the PDIUSBD11 a 8 byte packet. As the USbus is
host controlled it cannot write the data when ever it desires, so the
PDIUSBD11 buffers the data and waits for an IN Token to be sent from
the host. When the PDIUSBD11 receives the IN Token it generates an
interrupt. This makes a good time to reload the next packet of data to
send. This is done by an additional function WriteBufferToEndpoint();
</P>
<P>The section under CtlTransferInProgress == PROGRESS_ADDRESS handles
the setting of the device's address. We detail this later. </P><PRE> if (Irq & D11_INT_EP1_OUT) {
printf("EP1_OUT\n\r");
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP1_OUT, Buffer, 1);
bytes = D11ReadEndpoint(D11_ENDPOINT_EP1_OUT, Buffer);
for (count = 0; count < bytes; count++) {
circularbuffer[inpointer++] = Buffer[count];
if (inpointer >= MAX_BUFFER_SIZE) inpointer = 0;
}
loadfromcircularbuffer(); //Kick Start
}
if (Irq & D11_INT_EP1_IN) {
printf("EP1_IN\n\r");
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP1_IN, Buffer, 1);
loadfromcircularbuffer();
}
</PRE>
<P>EP1 OUT and EP1 IN are implemented to read and write bulk data to
or from a circular buffer. This setup allows the code to be used in
conjunction with the BulkUSB example in the Windows DDK's. The
circular buffer is defined earlier in the code as being 80 bytes long
taking up all of bank1 of the PIC16F876's RAM. </P><PRE> if (Irq & D11_INT_EP2_OUT) {
printf("EP2_OUT\n\r");
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP2_OUT, Buffer, 1);
Buffer[0] = 0x01; /* Stall Endpoint */
D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP2_OUT, Buffer, 1);
}
if (Irq & D11_INT_EP2_IN) {
printf("EP2_IN\n\r");
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP2_IN, Buffer, 1);
Buffer[0] = 0x01; /* Stall Endpoint */
D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP2_IN, Buffer, 1);
}
if (Irq & D11_INT_EP3_OUT) {
printf("EP3_OUT\n\r");
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP3_OUT, Buffer, 1);
Buffer[0] = 0x01; /* Stall Endpoint */
D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP3_OUT, Buffer, 1);
}
if (Irq & D11_INT_EP3_IN) {
printf("EP3_IN\n\r");
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP3_IN, Buffer, 1);
Buffer[0] = 0x01; /* Stall Endpoint */
D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP3_IN, Buffer, 1);
}
}
</PRE>
<P>Endpoints two and three are not used at the moment, so we stall
them if any data is received. The PDIUSBD11 has a Set Endpoint Enable
Command which can be used to enable or disable function generic
endpoints (any endpoints other than the default control pipe). We
could use this command to diable the generic endpoints, if we were
planning on not using these later. However at the moment this code
provides a foundation to build upon. </P><PRE>void Process_EP0_OUT_Interrupt(void)
{
unsigned long a;
unsigned char Buffer[2];
USB_SETUP_REQUEST SetupPacket;
/* Check if packet received is Setup or Data - Also clears IRQ */
D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_OUT, &SetupPacket, 1);
if (SetupPacket.bmRequestType & D11_LAST_TRAN_SETUP) {
</PRE>
<P>The first thing we must do is determine is the packet we have
received on EP0 Out is a data packet or a <A
href="http://www.beyondlogic.org/usbnutshell/usb6.htm#SetupPacket">Setup
Packet</A>. A Setup Packet contains a request such as Get Descriptor
where as a data packet contains data for a previous request. We are
lucky that most requests do not send data packets from the host to the
device. A request that does is SET_DESCRIPTOR but is rarely
implemented. </P><PRE> /* This is a setup Packet - Read Packet */
D11ReadEndpoint(D11_ENDPOINT_EP0_OUT, &SetupPacket);
/* Acknowlegde Setup Packet to EP0_OUT & Clear Buffer*/
D11CmdDataWrite(D11_ACK_SETUP, NULL, 0);
D11CmdDataWrite(D11_CLEAR_BUFFER, NULL, 0);
/* Acknowlegde Setup Packet to EP0_IN */
D11CmdDataWrite(D11_ENDPOINT_EP0_IN, NULL, 0);
D11CmdDataWrite(D11_ACK_SETUP, NULL, 0);
/* Parse bmRequestType */
switch (SetupPacket.bmRequestType & 0x7F) {
</PRE>
<P>As we have seen in our description of <A
href="http://www.beyondlogic.org/usbnutshell/usb4.htm#Control">Control
Transfers</A>, a setup packet cannot be NAKed or STALLed. When the
PDIUSBD11 receives a Setup Packet it flushes the EP0 IN buffer and
disables the Validate Buffer and Clear Buffer commands. This ensures
the setup packet is acknowledged by the microcontroller by sending an
Acknowledge Setup command to both EP0 IN and EP0 OUT before a Validate
or Clear Buffer command is effective. The recept of a setup packet
will also un-stall a STALLed control endpoint. </P>
<P>Once the packet has been read into memory and the setup packet
acknowledged, we being the parse the request starting with the request
type. At the moment we are not interesting in the direction, so we AND
off this bit. The three requests all devices must process is the
Standard Device Request, Standard Interface Request and Standard
Endpoint Requests. We provide our functionality (Read Analog Inputs)
by the Vendor Request, so we add a case statement for Standard Vendor
Requests. If your device supports a USB Class Specification, then you
may also need to add cases for Class Device Request, Class Interface
Request and/or Class Endpoint Request. </P><PRE> case STANDARD_DEVICE_REQUEST:
printf("Standard Device Request ");
switch (SetupPacket.bRequest) {
case GET_STATUS:
/* Get Status Request to Device should return */
/* Remote Wakeup and Self Powered Status */
Buffer[0] = 0x01;
Buffer[1] = 0x00;
D11WriteEndpoint(D11_ENDPOINT_EP0_IN, Buffer, 2);
break;
case CLEAR_FEATURE:
case SET_FEATURE:
/* We don't support DEVICE_REMOTE_WAKEUP or TEST_MODE */
ErrorStallControlEndPoint();
break;
</PRE>
<P>The Get Status request is used to report the status of the device
in terms of if the device is bus or self powered and if it supports
remote wakeup. In our device we report it as self powered and as not
supporting remote wakeup. </P>
<P>Of the Device Feature requests, this device doesn't support
DEVICE_REMOTE_WAKEUP nor TEST_MODE and return a USB Request Error as a
result. </P><PRE> case SET_ADDRESS:
printf("Set Address\n\r");
DeviceAddress = SetupPacket.wValue | 0x80;
D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0);
CtlTransferInProgress = PROGRESS_ADDRESS;
break;
</PRE>
<P>The Set Address command is the only command that continues to be
processed after the status stage. All other commands must finish
processing before the status stage. The device address is read and
OR'ed with 0x80 and stored in a variable DeviceAddress. The OR'ing
with 0x80 is specific to the PDIUSBD11 with the most significant bit
indicating if the device is enabled or not. A zero length packet is
returned as status to the host indicating the command is complete.
However the host must send an IN Token, retrieve the zero length
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -