Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: usbdc.cpp
- Revision:
- 0:237d5ef643e9
diff -r 000000000000 -r 237d5ef643e9 usbdc.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usbdc.cpp Fri May 11 13:35:59 2012 +0000
@@ -0,0 +1,544 @@
+/* usbdc.cpp */
+/* USB device controller */
+/* Copyright (c) Phil Wright 2008 */
+
+#include "mbed.h"
+#include "usbdc.h"
+#include "cmsis.h"
+
+#ifdef TARGET_LPC2368
+#undef LPC_USB_BASE
+#define LPC_USB_BASE (0xFFE0C000) /* TODO */
+#endif
+
+/* Power Control for Peripherals register */
+#define PCUSB ((unsigned long)1<<31)
+
+/* USB Clock Control register */
+#define DEV_CLK_EN ((unsigned long)1<<1)
+#define AHB_CLK_EN ((unsigned long)1<<4)
+
+/* USB Clock Status register */
+#define DEV_CLK_ON ((unsigned long)1<<1)
+#define AHB_CLK_ON ((unsigned long)1<<4)
+
+/* USB Device Interupt registers */
+#define FRAME ((unsigned long)1<<0)
+#define EP_FAST ((unsigned long)1<<1)
+#define EP_SLOW ((unsigned long)1<<2)
+#define DEV_STAT ((unsigned long)1<<3)
+#define CCEMPTY ((unsigned long)1<<4)
+#define CDFULL ((unsigned long)1<<5)
+#define RxENDPKT ((unsigned long)1<<6)
+#define TxENDPKT ((unsigned long)1<<7)
+#define EP_RLZED ((unsigned long)1<<8)
+#define ERR_INT ((unsigned long)1<<9)
+
+/* Endpoint Interrupt Registers */
+#define EP(endpoint) (1<<endpoint)
+
+/* USB Control register */
+#define RD_EN (1<<0)
+#define WR_EN (1<<1)
+#define LOG_ENDPOINT(endpoint) ((endpoint>>1)<<2)
+
+/* USB Receive Packet Length register */
+#define DV ((unsigned long)1<<10)
+#define PKT_RDY ((unsigned long)1<<11)
+#define PKT_LNGTH_MASK (0x3ff)
+
+/* Serial Interface Engine (SIE) */
+#define SIE_WRITE (0x01)
+#define SIE_READ (0x02)
+#define SIE_COMMAND (0x05)
+#define SIE_CMD_CODE(phase, data) ((phase<<8)|(data<<16))
+
+/* SIE Command codes */
+#define SIE_CMD_SET_ADDRESS (0xD0)
+#define SIE_CMD_CONFIGURE_DEVICE (0xD8)
+#define SIE_CMD_SET_MODE (0xF3)
+#define SIE_CMD_READ_FRAME_NUMBER (0xF5)
+#define SIE_CMD_READ_TEST_REGISTER (0xFD)
+#define SIE_CMD_SET_DEVICE_STATUS (0xFE)
+#define SIE_CMD_GET_DEVICE_STATUS (0xFE)
+#define SIE_CMD_GET_ERROR_CODE (0xFF)
+#define SIE_CMD_READ_ERROR_STATUS (0xFB)
+
+#define SIE_CMD_SELECT_ENDPOINT(endpoint) (0x00+endpoint)
+#define SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint) (0x40+endpoint)
+#define SIE_CMD_SET_ENDPOINT_STATUS(endpoint) (0x40+endpoint)
+
+#define SIE_CMD_CLEAR_BUFFER (0xF2)
+#define SIE_CMD_VALIDATE_BUFFER (0xFA)
+
+/* SIE Device Status register */
+#define SIE_DS_CON (1<<0)
+#define SIE_DS_CON_CH (1<<1)
+#define SIE_DS_SUS (1<<2)
+#define SIE_DS_SUS_CH (1<<3)
+#define SIE_DS_RST (1<<4)
+
+/* SIE Device Set Address register */
+#define SIE_DSA_DEV_EN (1<<7)
+
+/* SIE Configue Device register */
+#define SIE_CONF_DEVICE (1<<0)
+
+/* Select Endpoint register */
+#define SIE_SE_FE (1<<0)
+#define SIE_SE_ST (1<<1)
+#define SIE_SE_STP (1<<2)
+#define SIE_SE_PO (1<<3)
+#define SIE_SE_EPN (1<<4)
+#define SIE_SE_B_1_FULL (1<<5)
+#define SIE_SE_B_2_FULL (1<<6)
+
+/* Set Endpoint Status command */
+#define SIE_SES_ST (1<<0)
+#define SIE_SES_DA (1<<5)
+#define SIE_SES_RF_MO (1<<6)
+#define SIE_SES_CND_ST (1<<7)
+
+usbdc *usbdc::instance;
+
+usbdc::usbdc()
+{
+#ifdef TARGET_LPC1768
+ LPC_SC->USBCLKCFG=5; /* TODO */
+#endif
+
+ /* Enable power to USB device controller */
+ LPC_SC->PCONP |= PCUSB;
+
+ /* Enable USB clocks */
+ LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN;
+ while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON));
+
+ /* Configure pins P0.29 and P0.30 to be USB D+ and USB D- */
+ LPC_PINCON->PINSEL1 &= 0xc3ffffff;
+ LPC_PINCON->PINSEL1 |= 0x14000000;
+
+#ifdef ENABLE_VBUS
+ /* Configure pin P1.30 to be VBUS */
+ LPC_PINCON->PINSEL3 &= 0xcfffffff;
+ LPC_PINCON->PINSEL3 |= 0x20000000;
+
+ /* Configure pin P1.30 to have pull-down */
+ LPC_PINCON->PINMODE3 |= 0x30000000;
+#endif
+
+ /* Configure pin P2.9 to be Connect */
+ LPC_PINCON->PINSEL4 &= 0xfffcffff;
+ LPC_PINCON->PINSEL4 |= 0x00040000;
+
+ /* Connect must be low for at least 2.5uS */
+ wait_ms(1);
+
+ /* Attach IRQ */
+ instance = this;
+ NVIC_SetVector(USB_IRQn, (uint32_t)&_usbisr);
+ NVIC_EnableIRQ(USB_IRQn);
+
+ /* Enable device interrupts */
+ enableEvents();
+}
+
+void usbdc::connect(void)
+{
+ /* Connect USB device */
+ unsigned char status;
+
+ status = getDeviceStatus();
+ setDeviceStatus(status | SIE_DS_CON);
+}
+
+void usbdc::disconnect(void)
+{
+ /* Disconnect USB device */
+ unsigned char status;
+
+ status = getDeviceStatus();
+ setDeviceStatus(status & ~SIE_DS_CON);
+}
+
+void usbdc::SIECommand(unsigned long command)
+{
+ /* The command phase of a SIE transaction */
+ LPC_USB->USBDevIntClr = CCEMPTY;
+ LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_COMMAND, command);
+ while (!(LPC_USB->USBDevIntSt & CCEMPTY));
+}
+
+void usbdc::SIEWriteData(unsigned char data)
+{
+ /* The data write phase of a SIE transaction */
+ LPC_USB->USBDevIntClr = CCEMPTY;
+ LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_WRITE, data);
+ while (!(LPC_USB->USBDevIntSt & CCEMPTY));
+}
+
+unsigned char usbdc::SIEReadData(unsigned long command)
+{
+ /* The data read phase of a SIE transaction */
+ LPC_USB->USBDevIntClr = CDFULL;
+ LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_READ, command);
+ while (!(LPC_USB->USBDevIntSt & CDFULL));
+ return (unsigned char)LPC_USB->USBCmdData;
+}
+
+void usbdc::setDeviceStatus(unsigned char status)
+{
+ /* Write SIE device status register */
+ SIECommand(SIE_CMD_SET_DEVICE_STATUS);
+ SIEWriteData(status);
+}
+
+unsigned char usbdc::getDeviceStatus(void)
+{
+ /* Read SIE device status register */
+ SIECommand(SIE_CMD_GET_DEVICE_STATUS);
+ return SIEReadData(SIE_CMD_GET_DEVICE_STATUS);
+}
+
+void usbdc::setAddress(unsigned char address)
+{
+ /* Write SIE device address register */
+ SIECommand(SIE_CMD_SET_ADDRESS);
+ SIEWriteData((address & 0x7f) | SIE_DSA_DEV_EN);
+}
+
+unsigned char usbdc::selectEndpoint(unsigned char endpoint)
+{
+ /* SIE select endpoint command */
+ SIECommand(SIE_CMD_SELECT_ENDPOINT(endpoint));
+ return SIEReadData(SIE_CMD_SELECT_ENDPOINT(endpoint));
+}
+
+#if 1
+unsigned char usbdc::selectEndpointClearInterrupt(unsigned char endpoint)
+{
+ /* SIE select endpoint and clear interrupt command */
+ /* Using the Select Endpoint / Clear Interrupt SIE command does not seem */
+ /* to clear the appropriate bit in EP_INT_STAT? - using EP_INT_CLR instead */
+ LPC_USB->USBEpIntClr = EP(endpoint);
+ while (!(LPC_USB->USBDevIntSt & CDFULL));
+ return (unsigned char)LPC_USB->USBCmdData;
+}
+#else
+unsigned char usbdc::selectEndpointClearInterrupt(unsigned char endpoint)
+{
+ /* SIE select endpoint and clear interrupt command */
+ SIECommand(SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint));
+ return SIEReadData(SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint));
+}
+#endif
+
+unsigned char usbdc::clearBuffer(void)
+{
+ /* SIE clear buffer command */
+ SIECommand(SIE_CMD_CLEAR_BUFFER);
+ return SIEReadData(SIE_CMD_CLEAR_BUFFER);
+}
+
+void usbdc::validateBuffer(void)
+{
+ /* SIE validate buffer command */
+ SIECommand(SIE_CMD_VALIDATE_BUFFER);
+}
+
+void usbdc::setEndpointStatus(unsigned char endpoint, unsigned char status)
+{
+ /* SIE set endpoint status command */
+ SIECommand(SIE_CMD_SET_ENDPOINT_STATUS(endpoint));
+ SIEWriteData(status);
+}
+
+void usbdc::realiseEndpoint(unsigned char endpoint, unsigned long maxPacket)
+{
+ /* Realise an endpoint */
+ LPC_USB->USBDevIntClr = EP_RLZED;
+ LPC_USB->USBReEp |= EP(endpoint);
+ LPC_USB->USBEpInd = endpoint;
+ LPC_USB->USBMaxPSize = maxPacket;
+
+ while (!(LPC_USB->USBDevIntSt & EP_RLZED));
+ LPC_USB->USBDevIntClr = EP_RLZED;
+
+ /* Clear stall state */
+ endpointStallState &= ~EP(endpoint);
+}
+
+void usbdc::enableEndpointEvent(unsigned char endpoint)
+{
+ /* Enable an endpoint interrupt */
+ LPC_USB->USBEpIntEn |= EP(endpoint);
+}
+
+void usbdc::disableEndpointEvent(unsigned char endpoint)
+{
+ /* Disable an endpoint interrupt */
+ LPC_USB->USBEpIntEn &= ~EP(endpoint);
+}
+
+void usbdc::stallEndpoint(unsigned char endpoint)
+{
+ /* Stall an endpoint */
+ if ( (endpoint==EP0IN) || (endpoint==EP0OUT) )
+ {
+ /* Conditionally stall both control endpoints */
+ setEndpointStatus(EP0OUT, SIE_SES_CND_ST);
+ }
+ else
+ {
+ setEndpointStatus(endpoint, SIE_SES_ST);
+
+ /* Update stall state */
+ endpointStallState |= EP(endpoint);
+ }
+}
+
+void usbdc::unstallEndpoint(unsigned char endpoint)
+{
+ /* Unstall an endpoint. The endpoint will also be reinitialised */
+ setEndpointStatus(endpoint, 0);
+
+ /* Update stall state */
+ endpointStallState &= ~EP(endpoint);
+}
+
+bool usbdc::getEndpointStallState(unsigned char endpoint)
+{
+ /* Returns true if endpoint stalled */
+ return endpointStallState & EP(endpoint);
+}
+
+void usbdc::configureDevice(void)
+{
+ /* SIE Configure device command */
+ SIECommand(SIE_CMD_CONFIGURE_DEVICE);
+ SIEWriteData(SIE_CONF_DEVICE);
+}
+
+void usbdc::unconfigureDevice(void)
+{
+ /* SIE Configure device command */
+ SIECommand(SIE_CMD_CONFIGURE_DEVICE);
+ SIEWriteData(0);
+}
+
+unsigned long usbdc::endpointRead(unsigned char endpoint, unsigned char *buffer)
+{
+ /* Read from an OUT endpoint */
+ unsigned long size;
+ unsigned long i;
+ unsigned long data;
+ unsigned char offset;
+
+ LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | RD_EN;
+ while (!(LPC_USB->USBRxPLen & PKT_RDY));
+
+ size = LPC_USB->USBRxPLen & PKT_LNGTH_MASK;
+
+ offset = 0;
+
+ for (i=0; i<size; i++)
+ {
+ if (offset==0)
+ {
+ /* Fetch up to four bytes of data as a word */
+ data = LPC_USB->USBRxData;
+ }
+
+ /* extract a byte */
+ *buffer++ = data>>offset;
+
+ /* move on to the next byte */
+ offset = (offset + 8) % 32;
+ }
+
+ /* Clear RD_EN to cover zero length packet case */
+ LPC_USB->USBCtrl=0;
+
+ selectEndpoint(endpoint);
+ clearBuffer();
+
+ return size;
+}
+
+void usbdc::endpointWrite(unsigned char endpoint, unsigned char *buffer, unsigned long size)
+{
+ /* Write to an IN endpoint */
+ unsigned long temp, data;
+ unsigned char offset;
+
+ LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | WR_EN;
+
+ LPC_USB->USBTxPLen = size;
+ offset = 0;
+ data = 0;
+
+ if (size>0)
+ {
+ do {
+ /* Fetch next data byte into a word-sized temporary variable */
+ temp = *buffer++;
+
+ /* Add to current data word */
+ temp = temp << offset;
+ data = data | temp;
+
+ /* move on to the next byte */
+ offset = (offset + 8) % 32;
+ size--;
+
+ if ((offset==0) || (size==0))
+ {
+ /* Write the word to the endpoint */
+ LPC_USB->USBTxData = data;
+ data = 0;
+ }
+ } while (size>0);
+ }
+
+ /* Clear WR_EN to cover zero length packet case */
+ LPC_USB->USBCtrl=0;
+
+ selectEndpoint(endpoint);
+ validateBuffer();
+}
+
+void usbdc::enableEvents(void)
+{
+ /* Enable interrupt sources */
+ LPC_USB->USBDevIntEn = EP_SLOW | DEV_STAT;
+}
+
+void usbdc::disableEvents(void)
+{
+ /* Disable interrupt sources */
+ LPC_USB->USBDevIntClr = EP_SLOW | DEV_STAT;
+}
+
+void usbdc::usbisr(void)
+{
+ unsigned char devStat;
+
+ if (LPC_USB->USBDevIntSt & FRAME)
+ {
+ /* Frame event */
+ deviceEventFrame();
+ /* Clear interrupt status flag */
+ LPC_USB->USBDevIntClr = FRAME;
+ }
+
+ if (LPC_USB->USBDevIntSt & DEV_STAT)
+ {
+ /* Device Status interrupt */
+ /* Must clear the interrupt status flag before reading the device status from the SIE */
+ LPC_USB->USBDevIntClr = DEV_STAT;
+
+ /* Read device status from SIE */
+ devStat = getDeviceStatus();
+
+ if (devStat & SIE_DS_RST)
+ {
+ /* Bus reset */
+ deviceEventReset();
+ }
+ }
+
+ if (LPC_USB->USBDevIntSt & EP_SLOW)
+ {
+ /* (Slow) Endpoint Interrupt */
+
+ /* Process each endpoint interrupt */
+ if (LPC_USB->USBEpIntSt & EP(EP0OUT))
+ {
+ if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP)
+ {
+ /* this is a setup packet */
+ endpointEventEP0Setup();
+ }
+ else
+ {
+ endpointEventEP0Out();
+ }
+ }
+
+ if (LPC_USB->USBEpIntSt & EP(EP0IN))
+ {
+ selectEndpointClearInterrupt(EP0IN);
+ endpointEventEP0In();
+ }
+
+ if (LPC_USB->USBEpIntSt & EP(EP1OUT))
+ {
+ selectEndpointClearInterrupt(EP1OUT);
+ endpointEventEP1Out();
+ }
+
+ if (LPC_USB->USBEpIntSt & EP(EP1IN))
+ {
+ selectEndpointClearInterrupt(EP1IN);
+ endpointEventEP1In();
+ }
+
+ if (LPC_USB->USBEpIntSt & EP(EP2OUT))
+ {
+ selectEndpointClearInterrupt(EP2OUT);
+ endpointEventEP2Out();
+ }
+
+ if (LPC_USB->USBEpIntSt & EP(EP2IN))
+ {
+ selectEndpointClearInterrupt(EP2IN);
+ endpointEventEP2In();
+ }
+
+ /* Clear interrupt status flag */
+ /* EP_SLOW and EP_FAST interrupt bits should be cleared after the corresponding endpoint interrupts are cleared. */
+ LPC_USB->USBDevIntClr = EP_SLOW;
+ }
+}
+
+
+void usbdc::_usbisr(void)
+{
+ instance->usbisr();
+}
+
+void usbdc::deviceEventReset(void)
+{
+}
+
+void usbdc::deviceEventFrame(void)
+{
+}
+
+void usbdc::endpointEventEP0Setup(void)
+{
+}
+
+void usbdc::endpointEventEP0In(void)
+{
+}
+
+void usbdc::endpointEventEP0Out(void)
+{
+}
+
+void usbdc::endpointEventEP1In(void)
+{
+}
+
+void usbdc::endpointEventEP1Out(void)
+{
+}
+
+void usbdc::endpointEventEP2In(void)
+{
+}
+
+void usbdc::endpointEventEP2Out(void)
+{
+}