初始化USB外设:
// created by perry.peng 2012/04/21
void
main (void)
{
LEDCON = 0xff; // 10 mA current source.
CKCON0 |= 7; // Set the system and the T0 working in X2 mode.
TMOD = 0x01; //
M10=0;
M00=1; 16bit-timer
TH0 = 0; // init values
TL0 = 0;
TF0 = 0; // reset interrupt flag (already done by hardware)
TR0 = 1; // timer0
run ms_multiple_drive = 0;
USBCON |= MSK_USBE; ///< Suspend USB Clock
PLLDIV = PLL_12MHz; ///< PLL clock source is 12MHz
PLLCON |= MSK_PLLEN; ///< enable PLL
USBCON &= ~MSK_SUSPCLK;
USBINT &= ~MSK_SPINT;
USBCON |= MSK_DETACH; /* Attach Command, Vref pin at high level. */
while (!TF0);
TF0 = 0;
TR0 = 0; /* timer0 stop */
P3 = 0;
USBCON &= ~MSK_DETACH; /* Attach Command, Vref pin at high level. */
USBIEN = MSK_ESPINT | MSK_EWUPCPU | MSK_EEORINT | MSK_ESOFINT;
IEN1 |= MSK_EUSB;
sei ();
Usb_ep_setup (EP_CTRL, EP_ENABLE | EP_TYPE_CONTROL | EP_DIR_OUT);
USBCON &= ~MSK_DETACH; /* Attach Command, Vref pin at high level. */
UsbConnected = TRUE;处理USB协议中的Setup包:
void Usb_setup_packet_handler (void)
{
#define D2H(x) ((x) << 8 | 0x80) ///< 设备到主机包
#define D2HX(x, n) (((x) << 8) | (n)) ///< 设备到主机包,子功能。
#define H2D(x) ((x) << 8) ///< 主机到设备包
#define H2DX(x, n) (((x) << 8) | (n)) ///< 主机到设备包,子功能
#define GDT(x) ((x) << 8)
#define GDTS(x, n) (((x) << 8) | (n))
UEPNUM = EP_CTRL; // select control endport.
if (!(UEPSTAX & MSK_RXSETUP)) /* Check if a Setup packet is received. */
return;
cfgPacket.bytes[1] = UEPDATX; //!< bmRequestType
cfgPacket.bytes[0] = UEPDATX; //!< bRequest
/* SETUP packet:
bmRequestType bRequest wValue wIndex wLength
GET_DESCRIPTOR: 0x80 0x06 DType & DIndex Lang ID DLength *D=Descriptor
GET_DESCRIPTOR: 0x80 0x06
*/
switch (cfgPacket.reqId) {
case D2H (GET_DESCRIPTOR):
cfgPacket.bytes[3] = UEPDATX; /* read LSB of wValue */
cfgPacket.bytes[2] = UEPDATX; /* read MSB of wValue */
cfgPacket.bytes[5] = UEPDATX; /* read LSB of wValue */
cfgPacket.bytes[4] = UEPDATX; /* read MSB of wValue */
cfgPacket.bytes[7] = UEPDATX; /* read LSB of wLength */
cfgPacket.bytes[6] = UEPDATX; /* read MSB of wLength */
switch (cfgPacket.R.wValue) {
case GDT(DEVICE_DESCRIPTOR):
TransferDataSize = DeviceDescriptor[0]; //!< sizeof (DeviceDescriptor);
TransferDataPtr = &DeviceDescriptor[0];
break;
case GDT(CONFIGURATION_DESCRIPTOR):
TransferDataSize = ConfigurationDescriptor[2]; //!< sizeof (ConfigurationDescriptor);
TransferDataPtr = &ConfigurationDescriptor[0];
break;
case GDTS(STRING_DESCRIPTOR, USB_STRING_LANG):
TransferDataSize = StringDescriptorLanguageID[0]; //!< sizeof (StringDescriptorLanguageID);
TransferDataPtr = &StringDescriptorLanguageID[0];
break;
case GDTS(STRING_DESCRIPTOR, USB_STRING_MANU):
TransferDataSize = StringDescriptorManuf[0]; //!< sizeof (StringDescriptorManuf);
TransferDataPtr = &StringDescriptorManuf[0];
break;
case GDTS(STRING_DESCRIPTOR, USB_STRING_PROD):
TransferDataSize = StringDescriptorProduct[0]; //!< sizeof (StringDescriptorProduct);
TransferDataPtr = &StringDescriptorProduct[0];
break;
case GDTS(STRING_DESCRIPTOR, USB_STRING_SN):
TransferDataSize = StringDescriptorSerialNumber[0]; //!< sizeof (StringDescriptorSerialNumber);
TransferDataPtr = &StringDescriptorSerialNumber[0];
break;
default:
UEPSTAX |= MSK_STALLRQ;
UEPSTAX &= ~MSK_RXSETUP;
return;
}
NeedZeroPacket = FALSE; /* no zero length packet */
UEPSTAX &= ~MSK_RXSETUP; //!< clear the receive setup flag
UEPSTAX |= MSK_DIR; /* set out on EP0 */
if (cfgPacket.R.wLength > TransferDataSize) {
if ((TransferDataSize % EP_CONTROL_LENGTH) == 0)
NeedZeroPacket = TRUE;
else
NeedZeroPacket = FALSE; //!< no need of zero length packet
} else {
TransferDataSize = (uint8_t) cfgPacket.R.wLength; //!< send only requested number of data
}
while ((TransferDataSize != 0) && (!(UEPSTAX & MSK_RXOUTB0B1))) {
uint8_t c; // New
if (TransferDataSize > EP_CONTROL_LENGTH)
c = EP_CONTROL_LENGTH;
else
c = TransferDataSize;
TransferDataSize -= c;
while (c != 0) {
UEPDATX = *TransferDataPtr;
TransferDataPtr++;
c--;
}
UEPSTAX |= MSK_TXRDY;
while ((!(UEPSTAX & MSK_TXCMPL)) && (!(UEPSTAX & MSK_RXOUTB0B1)));
UEPSTAX &= ~MSK_TXCMPL;
}
if (UEPSTAX & MSK_RXOUTB0B1) { //!< abort from Host
UEPSTAX &= ~MSK_RXOUT;
return;
}
if (NeedZeroPacket == TRUE) {
UEPSTAX |= MSK_TXRDY;
while ((!(UEPSTAX & MSK_TXCMPL)) && (!(UEPSTAX & MSK_RXOUTB0B1)));
UEPSTAX &= ~MSK_TXCMPL;
}
while (!(UEPSTAX & MSK_RXOUTB0B1));
UEPSTAX &= ~MSK_RXOUT;
break;
case D2H (GET_CONFIGURATION):
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_DIR; /* set out on EP0 */
UEPDATX = UsbCurrentConfig;
UEPSTAX |= MSK_TXRDY;
while (!UEPSTAX & MSK_TXCMPL);
UEPSTAX &= ~MSK_TXCMPL;
while (!UEPSTAX & MSK_RXOUTB0B1);
UEPSTAX &= ~MSK_RXOUT;
break;
case H2D (SET_ADDRESS):
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_TXRDY; //!< send a ZLP for STATUS phase
while (!(UEPSTAX & MSK_TXCMPL)); //!< waits for status phase done
//!< before using the new address
UEPSTAX &= ~MSK_TXCMPL; // for Tiger compatibility
USBADDR &= ~MSK_FEN;
USBADDR = cfgPacket.bytes[2];
USBADDR |= MSK_FEN;
break;
case H2D (SET_CONFIGURATION):
cfgPacket.bytes[3] = UEPDATX;
cfgPacket.bytes[2] = UEPDATX;
if (cfgPacket.bytes[3] > NB_CONFIGURATIONS) {
UEPSTAX |= MSK_STALLRQ;
UEPSTAX &= ~MSK_RXSETUP;
break;
}
UEPSTAX &= ~MSK_RXSETUP;
UsbCurrentConfig = cfgPacket.bytes[3];
UEPSTAX |= MSK_TXRDY; //!< send a ZLP for STATUS phase
while (!UEPSTAX & MSK_TXCMPL);
UEPSTAX &= ~MSK_TXCMPL;
Usb_ep_setup (EP_MS_IN, EP_ENABLE | EP_TYPE_BULK | EP_DIR_IN);
Usb_ep_setup (EP_MS_OUT, EP_ENABLE | EP_TYPE_BULK | EP_DIR_OUT);
break;
case H2DX (SET_FEATURE, ENDPOINT_TYPE):
cfgPacket.bytes[3] = UEPDATX;
cfgPacket.bytes[2] = UEPDATX;
cfgPacket.bytes[5] = UEPDATX;
cfgPacket.bytes[4] = UEPDATX;
if (cfgPacket.R.wValue == FEATURE_ENDPOINT_HALT) {
uint8_t wIndex;
wIndex = cfgPacket.R.wIndex & EP_DIR;
UEPNUM = wIndex;
if (wIndex != EP_CTRL && UEPCONX & MSK_EPEN) {
UEPSTAX |= MSK_STALLRQ;
UEPNUM = EP_CTRL;
endpoint_status[wIndex] = 0x01;
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_TXRDY;
break;
}
}
UEPSTAX |= MSK_STALLRQ;
UEPSTAX &= ~MSK_RXSETUP;
break;
case D2HX (GET_STATUS, REQUEST_DEVICE_STATUS):
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_DIR;
UEPDATX = DEVICE_STATUS;
UEPDATX = 0x00;
UEPSTAX |= MSK_TXRDY;
while (!UEPSTAX & MSK_TXCMPL);
UEPSTAX &= ~MSK_TXCMPL;
while (!UEPSTAX & MSK_RXOUTB0B1);
UEPSTAX &= ~MSK_RXOUT;
break;
case D2HX (GET_STATUS, REQUEST_INTERFACE_STATUS):
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_DIR;
UEPDATX = INTERFACE_STATUS;
UEPDATX = 0x00;
UEPSTAX |= MSK_TXRDY;
while (!UEPSTAX & MSK_TXCMPL);
UEPSTAX &= ~MSK_TXCMPL;
while (!UEPSTAX & MSK_RXOUTB0B1);
UEPSTAX &= ~MSK_RXOUT;
break;
case D2HX (GET_STATUS, REQUEST_ENDPOINT_STATUS):
cfgPacket.bytes[3] = UEPDATX;
cfgPacket.bytes[2] = UEPDATX;
cfgPacket.bytes[5] = UEPDATX;
cfgPacket.bytes[4] = UEPDATX;
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_DIR;
UEPDATX = endpoint_status[cfgPacket.R.wIndex & EP_DIR];
UEPDATX = 0x00;
UEPSTAX |= MSK_TXRDY;
while (!UEPSTAX & MSK_TXCMPL);
UEPSTAX &= ~MSK_TXCMPL;
while (!UEPSTAX & MSK_RXOUTB0B1);
UEPSTAX &= ~MSK_RXOUT;
break;
case H2DX (CLEAR_FEATURE, ENDPOINT_TYPE):
cfgPacket.bytes[3] = UEPDATX;
cfgPacket.bytes[2] = UEPDATX;
cfgPacket.bytes[5] = UEPDATX;
cfgPacket.bytes[4] = UEPDATX;
if (cfgPacket.R.wValue == FEATURE_ENDPOINT_HALT) {
cfgPacket.R.wIndex &= EP_DIR;
UEPNUM = cfgPacket.R.wIndex;
if (UEPCONX & MSK_EPEN) {
if (cfgPacket.R.wIndex != EP_CTRL) {
UEPSTAX &= ~MSK_STALLRQ;
UEPRST = 1 << (uint8_t) cfgPacket.R.wIndex;
UEPRST = 0;
}
UEPNUM = EP_CTRL;
endpoint_status[cfgPacket.R.wIndex] = 0x00;
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_TXRDY;
break;
}
}
UEPSTAX |= MSK_STALLRQ;
UEPSTAX &= ~MSK_RXSETUP;
break;
//case D2H (MASS_STORAGE_RESET):
case H2D (MASS_STORAGE_RESET):
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_TXRDY;
break;
//case D2H (GET_MAX_LUN):
case H2D (GET_MAX_LUN):
UEPSTAX &= ~MSK_RXSETUP;
UEPSTAX |= MSK_DIR; /* set out on EP0 */
UEPDATX = MAX_LUN - 1;
UEPSTAX |= MSK_TXRDY;
while (!UEPSTAX & MSK_TXCMPL);
UEPSTAX &= ~MSK_TXCMPL;
ms_multiple_drive = 1;
break;
case H2D (SET_DESCRIPTOR):
case H2D (SET_INTERFACE):
case H2DX (SET_FEATURE, ZERO_TYPE):
case H2DX (SET_FEATURE, INTERFACE_TYPE):
case H2DX (CLEAR_FEATURE, ZERO_TYPE):
case H2DX (CLEAR_FEATURE, INTERFACE_TYPE):
case D2H (SYNCH_FRAME):
case D2H (GET_INTERFACE):
default:
UEPSTAX |= MSK_STALLRQ;
UEPSTAX &= ~MSK_RXSETUP;
break;
}
}
定义一堆USB协议中定义的描述符:
//! Usb Device Descriptor
static const uint8_t const __code DeviceDescriptor[] = {
/* bLength = */ 0x12, //!< Size of this descriptor in bytes
/* bDescriptorType = */ DEVICE_DESCRIPTOR, //!< DEVICE descriptor type
/* bscUSB = */ 0x02, //!< Binay Coded Decimal Spec. release
0x00,
/* bDeviceClass = */ 0x00, //!< Class code assigned by the USB
/* bDeviceSubClass = */ 0x00, //!< Sub-class code assigned by the USB
/* bDeviceProtocol = */ 0x00, //!< Protocol code assigned by the USB
/* bMaxPacketSize0 = */ EP_CONTROL_LENGTH, //!< Max packet size for EP0
/* idVender = */ 0x03, //!< ??ID?,ATMEL?ID?0xeb03,??????,??????*/
0xeb,
/* idProduct = */ 0x13, //!< ??ID?,HID??0x2013, ??????,??????*/
0x20,
/* bcdDevice = */ 0x00, //!< ????USB??????,???1.0??,?0x0100?*/
0x01,
/* iManufacturer = */ USB_STRING_MANU, //!< ?????????,?????????,???????1????*/
/* iProduct = */ USB_STRING_PROD, //!< ??????????????1,????2???????????????????*/
/* iSerialNumber = */ USB_STRING_SN, //!< ????????????????3?????*/
/* bNumConfigurations=*/ 0x01 //!< ???????????????????????,???????1?*/
};
//! Usb Device Descriptor
__code uint8_t DeviceQualifierDescriptor[] = {
/* bLength = */ 0x0a, //!< Size of this descriptor in bytes
/* bDescriptorType = */ DEVICE_QUALIFIER_DESCRIPTOR, //!< DEVICEQUALIFIER descriptor type
/* bscUSB = */ 0x02, //!< Binay Coded Decimal Spec. release
0x00,
/* bDeviceClass = */ 0x00, //!< Class code assigned by the USB
/* bDeviceSubClass = */ 0x00, //!< Sub-class code assigned by the USB
/* bDeviceProtocol = */ 0x00, //!< Protocol code assigned by the USB
/* bMaxPacketSize0 = */ EP_CONTROL_LENGTH, //!< Max packet size for EP0
/* bNumConfigurations = */0x01, //!< Number of possible configurations
/* bReserved = */ 0x00 //!< Reserved for future use, must be zero
};
//! Configuration Descriptor
__code uint8_t ConfigurationDescriptor[] = {
/* bLength = */ 0x09, //!< size of this descriptor in bytes
/* bDescriptorType = */ CONFIGURATION_DESCRIPTOR, //!< CONFIGURATION descriptor type
/* wTotalLength = */ (0x09+0x09+0x07+0x07), //!< total length of data returned
0x00,
/* bNumInterfaces = */ 0x01, //!< number of interfaces for this conf.
/* bConfigurationValue = */0x01, //!< value for SetConfiguration resquest
/* iConfiguration = */ 0x00, //!< index of string descriptor
/* bmAttibutes = */ USB_CONFIG_BUSPOWERED, //!< Configuration characteristics
/* MaxPower = */ 50, //!< 50 = 100mA, maximum power consumption
/* bLength = */ 0x09, //!< size of this descriptor in bytes
/* bDescriptorType = */ INTERFACE_DESCRIPTOR, //!< INTERFACE descriptor type
/* bInterfaceNumber = */ 0x00, //!< Number of interface
/* bAlternateSetting = */ 0x00, //!< value to select alternate setting
/* bNumEndpoints = */ 0x02, //!< Number of EP except EP 0
/* bInterfaceClass = */ 0x08, //!< 0x08 = Mass Storage, Class code assigned by the USB
/* bInterfaceSubClass = */0x06, //!< 0x06 = SCSI transparent Cammand Set, Sub-class code assigned by the USB
/* bInterfaceProtocol = */0x50, //!< 0x50 = Bulk-Only Transport, Protocol code assigned by the USB
/* iInterface = */ 0x00, //!< Index of string descriptor
/* bLength = */ 0x07, //!< size of this descriptor in bytes
/* bDescriptorType = */ ENDPOINT_DESCRIPTOR, //!< ENDPOINT descriptor type
/* bEndpointAddress = */ (EP_MS_IN | 0x80), //!< Address of the endpoint
/* bmAttributes = */ EP_TYPE_BULK, //!< Endpoint's attributes
/* wMaxPacketSize = */ 64, //!< Maximum packet size for this EP
0x00,
/* bInterval = */ 0x00, //!< Interval for polling EP in ms
/* bLength = */ 0x07, //!< size of this descriptor in bytes
/* bDescriptorType = */ ENDPOINT_DESCRIPTOR, //!< ENDPOINT descriptor type
/* bEndpointAddress = */ EP_MS_OUT, //!< Address of the endpoint
/* bmAttributes = */ EP_TYPE_BULK, //!< Endpoint's attributes
/* wMaxPacketSize = */ 64, //!< Maximum packet size for this EP
0x00,
/* bInterval = */ 0x00 //!< Interval for polling EP in ms
};
// StringDescriptor LanguageID
__code uint8_t StringDescriptorLanguageID[] = {
/* bLength = */ 0x04, //!< size of this descriptor in bytes
/* bDescriptorType = */ STRING_DESCRIPTOR, //!< STRING_DESCRIPTOR descriptor type
/* wLanguageID = */ 0x09, //!< wLanguageID = 0x0409
0x04
};
// StringDescriptor Manufacture
__code uint8_t StringDescriptorManuf[] = {
/* bLength = */ 24, //!< size of this descriptor in bytes
/* bDescriptorType = */ STRING_DESCRIPTOR, //!< STRING_DESCRIPTOR descriptor type
WCHAR('F'),
WCHAR('l'),
WCHAR('e'),
WCHAR('x'),
WCHAR('t'),
WCHAR('r'),
WCHAR('o'),
WCHAR('n'),
WCHAR('i'),
WCHAR('c'),
WCHAR('s') };
// StringDescriptor Product
__code uint8_t StringDescriptorProduct[] = {
/* bLength = */ 14, //!< size of this descriptor in bytes
/* bDescriptorType = */ STRING_DESCRIPTOR, //!< STRING_DESCRIPTOR descriptor type
WCHAR('U'),
WCHAR('S'),
WCHAR('B'),
WCHAR('D'),
WCHAR('i'),
WCHAR('s'),
WCHAR('k') };
// StringDescriptor SerialNumber, Serial Number should be at least 12 characters long
__code uint8_t StringDescriptorSerialNumber[] = {
/* bLength = */ 16, //!< size of this descriptor in bytes
/* bDescriptorType = */ STRING_DESCRIPTOR, //!< STRING_DESCRIPTOR descriptor type
WCHAR('2'),
WCHAR('A'),
WCHAR('S'),
WCHAR('4'),
WCHAR('2'),
WCHAR('5'),
WCHAR('6') };
处理CBW和CSW:
void usb_mass_storage_cbw (void)
{
__bit cbw_error;
uint8_t c;
cbw_error = FALSE;
UEPNUM = EP_MS_OUT; //! check if dCBWSignature is correct
if (0x55 != UEPDATX) cbw_error = TRUE; //! 'U'
if (0x53 != UEPDATX) cbw_error = TRUE; //! 'S'
if (0x42 != UEPDATX) cbw_error = TRUE; //! 'B'
if (0x43 != UEPDATX) cbw_error = TRUE; //! 'C'
dCBWTag[0] = UEPDATX; //! Store CBW Tag to be repeated in CSW
dCBWTag[1] = UEPDATX;
dCBWTag[2] = UEPDATX;
dCBWTag[3] = UEPDATX;
((uint8_t *) & g_scsi_data_remaining)[3] = UEPDATX;
((uint8_t *) & g_scsi_data_remaining)[2] = UEPDATX;
((uint8_t *) & g_scsi_data_remaining)[1] = UEPDATX;
((uint8_t *) & g_scsi_data_remaining)[0] = UEPDATX;
if (UEPDATX != 0x00) {
ms_data_direction = 1;
if (cbw_error) {
Usb_ack_receive_out_ms ();
UEPNUM = EP_MS_IN;
UEPSTAX |= MSK_STALLRQ;
return;
}
} else {
ms_data_direction = 0;
if (cbw_error) {
UEPSTAX |= MSK_STALLRQ;
Usb_ack_receive_out_ms ();
return;
}
}
usb_LUN = UEPDATX;
if (!ms_multiple_drive) {
usb_LUN = 0;
}
ACC = UEPDATX; //! dummy CBWCBLength read
for (c = 0; c < 16; c++) // store scsi_command
{
g_scsi_command[c] = UEPDATX;
}
Usb_ack_receive_out_ms ();
if (ms_data_direction == 1) {
UEPNUM = EP_MS_IN;
}
if (TRUE != scsi_decode_command ()) {
if (g_scsi_data_remaining != 0) {
UEPSTAX |= MSK_STALLRQ;
}
}
}
void usb_mass_storage_csw (void)
{
UEPNUM = EP_MS_IN;
while (UEPSTAX & MSK_STALLRQ) {
Usb_setup_packet_handler ();
UEPNUM = EP_MS_IN;
}
UEPNUM = EP_MS_OUT;
while (UEPSTAX & MSK_STALLRQ) {
Usb_setup_packet_handler ();
UEPNUM = EP_MS_OUT;
}
UEPNUM = EP_MS_IN;
//! write CSW Signature
Usb_write_byte (0x55); //! 'U'
Usb_write_byte (0x53); //! 'S'
Usb_write_byte (0x42); //! 'B'
Usb_write_byte (0x53); //! 'S'
//! write stored CBW Tag
Usb_write_byte (dCBWTag[0]);
Usb_write_byte (dCBWTag[1]);
Usb_write_byte (dCBWTag[2]);
Usb_write_byte (dCBWTag[3]);
//! write data residue value
Usb_write_byte (((uint8_t *) & g_scsi_data_remaining)[3]);
Usb_write_byte (((uint8_t *) & g_scsi_data_remaining)[2]);
Usb_write_byte (((uint8_t *) & g_scsi_data_remaining)[1]);
Usb_write_byte (((uint8_t *) & g_scsi_data_remaining)[0]);
//! write command status
Usb_write_byte (g_scsi_status); //! 0 -> PASS, 1 -> FAIL
Usb_send_in ();
}
LEDCON = 0xff; // 10 mA current source.
CKCON0 |= 7; // Set the system and the T0 working in X2 mode.