// from: http://pe.ece.olin.edu/ece/projects.html
// Included here for documentation purpose.
//
// Author: Bradley A. Minch
// Organization: Franklin W. Olin College of Engineering
// Revision History:
// 01/19/2006 - Added wait for initial SE0 condition to clear at the end
// of InitUSB().
// 01/13/2006 - Fixed problem with DATA OUT transfers (only worked properly
// with class requests) in ProcessSetupToken. Added code to
// disable all EPs except EP0 on a valid SET_CONFIGURATION
// request. Changed code to use BSTALL instead of EPSTALL for
// Request Error on EP0. Changed CLEAR_FEATURE, SET_FEATURE,
// and GET_STATUS requests to use BSTALL instead of EPSTALL.
// Eliminated initial for loop in main().
// 11/14/2005 - Initial port to C for the Microchip C18 compiler complete
// 06/22/2005 - Added code to disable all endpoints on USRTIF and to mask
// bits 0, 1, and 7 of USTAT on TRNIF in ServiceUSB.
// 04/21/2005 - Initial public release.
//
// ============================================================================
//
// Peripheral Description:
//
// This peripheral enumerates as a vendor-specific device. The main event loop
// blinks an LED connected to RA1 on and off at about 2 Hz and the peripheral
// responds to a pair of vendor-specific requests that turn on or off an LED
// connected to RA0. The firmware is configured to use an external 4-MHz
// crystal, to operate as a low-speed USB device, and to use the internal
// pull-up resistor.
//
// ============================================================================
//
// Software Licence Agreement:
//
// THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO WARRANTIES, WHETHER
// EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY
// TO THIS SOFTWARE. THE AUTHOR SHALL NOT, UNDER ANY CIRCUMSTANCES, BE LIABLE
// FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
#include
#include "usb_defs.h"
#pragma config PLLDIV = 1
#pragma config CPUDIV = OSC3_PLL4
#pragma config USBDIV = 2
#pragma config FOSC = XTPLL_XT
#pragma config FCMEM = OFF
#pragma config IESO = OFF
#pragma config PWRT = OFF
#pragma config BOR = ON
#pragma config BORV = 21
#pragma config VREGEN = ON
#pragma config WDT = OFF
#pragma config WDTPS = 32768
#pragma config MCLRE = ON
#pragma config LPT1OSC = OFF
#pragma config PBADEN = OFF
#pragma config CCP2MX = ON
#pragma config STVREN = ON
#pragma config LVP = OFF
#pragma config ICPRT = OFF
#pragma config XINST = OFF
#pragma config DEBUG = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CP2 = OFF
#pragma config CP3 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRT2 = OFF
#pragma config WRT3 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTR2 = OFF
#pragma config EBTR3 = OFF
#pragma config EBTRB = OFF
#define SHOW_ENUM_STATUS
#define SET_RA0 0x01 // vendor-specific request to set RA0 to high
#define CLR_RA0 0x02 // vendor-specific request to set RA0 to low
#pragma udata
BUFDESC USB_buffer_desc;
unsigned char USB_buffer_data[8];
unsigned char USB_error_flags;
unsigned char USB_curr_config;
unsigned char USB_device_status;
unsigned char USB_dev_req;
unsigned char USB_address_pending;
unsigned char rom *USB_desc_ptr;
unsigned char USB_bytes_left;
unsigned char USB_packet_length;
unsigned char USB_USTAT;
unsigned char USB_USWSTAT;
unsigned char COUNTER_L;
unsigned char COUNTER_H;
#pragma romdata
rom const unsigned char Device[] = {
0x12, // bLength
DEVICE, // bDescriptorType
0x10, // bcdUSB (low byte)
0x01, // bcdUSB (high byte)
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
MAX_PACKET_SIZE, // bMaxPacketSize
0xD8, // idVendor (low byte)
0x04, // idVendor (high byte)
0x01, // idProduct (low byte)
0x00, // idProduct (high byte)
0x00, // bcdDevice (low byte)
0x00, // bcdDevice (high byte)
0x01, // iManufacturer
0x02, // iProduct
0x00, // iSerialNumber (none)
NUM_CONFIGURATIONS // bNumConfigurations
};
rom const unsigned char Configuration1[] = {
0x09, // bLength
CONFIGURATION, // bDescriptorType
0x12, // wTotalLength (low byte)
0x00, // wTotalLength (high byte)
NUM_INTERFACES, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration (none)
0xA0, // bmAttributes
0x32, // bMaxPower (100 mA)
0x09, // bLength (Interface1 descriptor starts here)
INTERFACE, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x00, // bNumEndpoints (excluding EP0)
0xFF, // bInterfaceClass (vendor specific class code)
0x00, // bInterfaceSubClass
0xFF, // bInterfaceProtocol (vendor specific protocol used)
0x00 // iInterface (none)
};
rom const unsigned char String0[] = {
0x04, // bLength
STRING, // bDescriptorType
0x09, // wLANGID[0] (low byte)
0x04 // wLANGID[0] (high byte)
};
rom const unsigned char String1[] = {
0x36, // bLength
STRING, // bDescriptorType
'M', 0x00, 'i', 0x00, 'c', 0x00, 'r', 0x00, 'o', 0x00, 'c', 0x00, 'h', 0x00, 'i', 0x00, 'p', 0x00, ' ', 0x00,
'T', 0x00, 'e', 0x00, 'c', 0x00, 'h', 0x00, 'n', 0x00, 'o', 0x00, 'l', 0x00, 'o', 0x00, 'g', 0x00, 'y', 0x00, ',', 0x00, ' ', 0x00,
'I', 0x00, 'n', 0x00, 'c', 0x00, '.', 0x00
};
rom const unsigned char String2[] = {
0x44, // bLength
STRING, // bDescriptorType
'E', 0x00, 'N', 0x00, 'G', 0x00, 'R', 0x00, ' ', 0x00, '2', 0x00, '2', 0x00, '1', 0x00, '0', 0x00, ' ', 0x00,
'P', 0x00, 'I', 0x00, 'C', 0x00, '1', 0x00, '8', 0x00, 'F', 0x00, '2', 0x00, '4', 0x00, '5', 0x00, '5', 0x00, ' ', 0x00,
'U', 0x00, 'S', 0x00, 'B', 0x00, ' ', 0x00,
'F', 0x00, 'i', 0x00, 'r', 0x00, 'm', 0x00, 'w', 0x00, 'a', 0x00, 'r', 0x00, 'e', 0x00
};
#pragma code
void InitUSB(void) {
UIE = 0x00; // mask all USB interrupts
UIR = 0x00; // clear all USB interrupt flags
UCFG = 0x10; // configure USB for low-speed transfers and to use the on-chip transciever and pull-up resistor
UCON = 0x08; // enable the USB module and its supporting circuitry
USB_curr_config = 0x00;
USB_USWSTAT = 0x00; // default to powered state
USB_device_status = 0x01;
USB_dev_req = NO_REQUEST; // No device requests in process
#ifdef SHOW_ENUM_STATUS
TRISB = 0x00; // set all bits of PORTB as outputs
PORTB = 0x01; // set bit zero to indicate Powered status
#endif
while (UCONbits.SE0); // wait for the first SE0 to end
}
void ServiceUSB(void) {
BUFDESC *buf_desc_ptr;
if (UIRbits.UERRIF) {
UEIR = 0x00;
} else if (UIRbits.SOFIF) {
UIRbits.SOFIF = 0;
} else if (UIRbits.IDLEIF) {
UIRbits.IDLEIF = 0;
UCONbits.SUSPND = 1;
#ifdef SHOW_ENUM_STATUS
PORTB &= 0xE0;
PORTBbits.RB4 = 1;
#endif
} else if (UIRbits.ACTVIF) {
UIRbits.ACTVIF = 0;
UCONbits.SUSPND = 0;
#ifdef SHOW_ENUM_STATUS
PORTB &= 0xE0;
PORTB |= 0x01<status;
USB_buffer_desc.bytecount = buf_desc_ptr->bytecount;
USB_buffer_desc.address = buf_desc_ptr->address;
USB_USTAT = USTAT; // save the USB status register
UIRbits.TRNIF = 0; // clear TRNIF interrupt flag
#ifdef SHOW_ENUM_STATUS
switch (USB_USTAT&0x18) { // toggle bit 5, 6, or 7 of PORTB to reflect EP0, EP1, or EP2 activity
case EP0:
PORTB ^= 0x20;
break;
case EP1:
PORTB ^= 0x40;
break;
case EP2:
PORTB ^= 0x80;
}
#endif
USB_error_flags = 0x00; // clear USB error flags
switch (USB_buffer_desc.status&0x3C) { // extract PID bits
case TOKEN_SETUP:
ProcessSetupToken();
break;
case TOKEN_IN:
ProcessInToken();
break;
case TOKEN_OUT:
ProcessOutToken();
}
if (USB_error_flags&0x01) { // if there was a Request Error...
BD0O.bytecount = MAX_PACKET_SIZE; // ...get ready to receive the next Setup token...
BD0I.status = 0x84;
BD0O.status = 0x84; // ...and issue a protocol stall on EP0
}
}
}
void ProcessSetupToken(void) {
unsigned char n;
for (n = 0; n<8; n++) {
USB_buffer_data[n] = USB_buffer_desc.address[n];
}
BD0O.bytecount = MAX_PACKET_SIZE; // reset the EP0 OUT byte count
BD0I.status = 0x08; // return the EP0 IN buffer to us (dequeue any pending requests)
BD0O.status = (!(USB_buffer_data[bmRequestType]&0x80) && (USB_buffer_data[wLength] || USB_buffer_data[wLengthHigh])) ? 0xC8:0x88; // set EP0 OUT UOWN back to USB and DATA0/DATA1 packet according to the request type
UCONbits.PKTDIS = 0; // assuming there is nothing to dequeue, clear the packet disable bit
USB_dev_req = NO_REQUEST; // clear the device request in process
switch (USB_buffer_data[bmRequestType]&0x60) { // extract request type bits
case STANDARD:
StandardRequests();
break;
case CLASS:
ClassRequests();
break;
case VENDOR:
VendorRequests();
break;
default:
USB_error_flags |= 0x01; // set Request Error Flag
}
}
void StandardRequests(void) {
unsigned char *UEP;
unsigned char n;
BUFDESC *buf_desc_ptr;
switch (USB_buffer_data[bRequest]) {
case GET_STATUS:
switch (USB_buffer_data[bmRequestType]&0x1F) { // extract request recipient bits
case RECIPIENT_DEVICE:
BD0I.address[0] = USB_device_status;
BD0I.address[1] = 0x00;
BD0I.bytecount = 0x02;
BD0I.status = 0xC8; // send packet as DATA1, set UOWN bit
break;
case RECIPIENT_INTERFACE:
switch (USB_USWSTAT) {
case ADDRESS_STATE:
USB_error_flags |= 0x01; // set Request Error Flag
break;
case CONFIG_STATE:
if (USB_buffer_data[wIndex]>2; // return the BSTALL bit of EP0 IN or OUT, whichever was requested
BD0I.address[1] = 0x00;
BD0I.bytecount = 0x02;
BD0I.status = 0xC8; // send packet as DATA1, set UOWN bit
} else {
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
case CONFIG_STATE:
UEP = (unsigned char *)&UEP0;
n = USB_buffer_data[wIndex]&0x0F; // get EP and strip off direction bit for offset from UEP0
buf_desc_ptr = &BD0O+((n<<1)|((USB_buffer_data[wIndex]&0x80) ? 0x01:0x00)); // compute pointer to the buffer descriptor for the specified EP
if (UEP[n]&((USB_buffer_data[wIndex]&0x80) ? 0x02:0x04)) { // if the specified EP is enabled for transfers in the specified direction...
BD0I.address[0] = ((buf_desc_ptr->status)&0x04)>>2; // ...return the BSTALL bit of the specified EP
BD0I.address[1] = 0x00;
BD0I.bytecount = 0x02;
BD0I.status = 0xC8; // send packet as DATA1, set UOWN bit
} else {
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
default:
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
default:
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
case CLEAR_FEATURE:
case SET_FEATURE:
switch (USB_buffer_data[bmRequestType]&0x1F) { // extract request recipient bits
case RECIPIENT_DEVICE:
switch (USB_buffer_data[wValue]) {
case DEVICE_REMOTE_WAKEUP:
if (USB_buffer_data[bRequest]==CLEAR_FEATURE)
USB_device_status &= 0xFE;
else
USB_device_status |= 0x01;
BD0I.bytecount = 0x00; // set EP0 IN byte count to 0
BD0I.status = 0xC8; // send packet as DATA1, set UOWN bit
break;
default:
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
case RECIPIENT_ENDPOINT:
switch (USB_USWSTAT) {
case ADDRESS_STATE:
if (!(USB_buffer_data[wIndex]&0x0F)) { // get EP, strip off direction bit, and see if its EP0
BD0I.bytecount = 0x00; // set EP0 IN byte count to 0
BD0I.status = 0xC8; // send packet as DATA1, set UOWN bit
} else {
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
case CONFIG_STATE:
UEP = (unsigned char *)&UEP0;
if (n = USB_buffer_data[wIndex]&0x0F) { // get EP and strip off direction bit for offset from UEP0, if not EP0...
buf_desc_ptr = &BD0O+((n<<1)|((USB_buffer_data[wIndex]&0x80) ? 0x01:0x00)); // compute pointer to the buffer descriptor for the specified EP
if (USB_buffer_data[wIndex]&0x80) { // if the specified EP direction is IN...
if (UEP[n]&0x02) { // if EPn is enabled for IN transfers...
buf_desc_ptr->status = (USB_buffer_data[bRequest]==CLEAR_FEATURE) ? 0x00:0x84;
} else {
USB_error_flags |= 0x01; // set Request Error Flag
}
} else { // ...otherwise the specified EP direction is OUT, so...
if (UEP[n]&0x04) { // if EPn is enabled for OUT transfers...
buf_desc_ptr->status = (USB_buffer_data[bRequest]==CLEAR_FEATURE) ? 0x88:0x84;
} else {
USB_error_flags |= 0x01; // set Request Error Flag
}
}
}
if (!(USB_error_flags&0x01)) { // if there was no Request Error...
BD0I.bytecount = 0x00;
BD0I.status = 0xC8; // ...send packet as DATA1, set UOWN bit
}
break;
default:
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
default:
USB_error_flags |= 0x01; // set Request Error Flag
}
break;
case SET_ADDRESS:
if (USB_buffer_data[wValue]>0x7F) { // if new device address is illegal, send Request Error
USB_error_flags |= 0x01; // set Request Error Flag
} else {
USB_dev_req = SET_ADDRESS; // processing a SET_ADDRESS request
USB_address_pending = USB_buffer_data[wValue]; // save new address
BD0I.bytecount = 0x00; // set EP0 IN byte count to 0
BD0I.status = 0xC8; // send packet as DATA1, set UOWN bit
}
break;
case GET_DESCRIPTOR:
USB_dev_req = GET_DESCRIPTOR; // processing a GET_DESCRIPTOR request
switch (USB_buffer_data[wValueHigh]) {
case DEVICE:
USB_desc_ptr = Device;
USB_bytes_left = USB_desc_ptr[0];
if ((USB_buffer_data[wLengthHigh]==0x00) && (USB_buffer_data[wLength]