USB Mouse (relative) example for mbed NXP LPC11U24 beta
Diff: USB_HID_STACK/USBBusInterface_LPC17_LPC23.c
- Revision:
- 0:163560051396
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USB_HID_STACK/USBBusInterface_LPC17_LPC23.c Mon Oct 24 10:26:27 2011 +0000 @@ -0,0 +1,679 @@ +/* USBBusInterface_LPC17_LPC23.c */ +/* USB Bus Interface for NXP LPC1768 and LPC2368 */ +/* Copyright (c) 2011 ARM Limited. All rights reserved. */ + +#ifdef TARGET_LPC1768 + +#include "USBBusInterface.h" +#include "USBEvents.h" + + +/* Get endpoint direction */ +#define IN_EP(endpoint) ((endpoint) & 1U ? true : false) +#define OUT_EP(endpoint) ((endpoint) & 1U ? false : true) + +/* Convert physical endpoint number to register bit */ +#define EP(endpoint) (1UL<<endpoint) + +/* Power Control for Peripherals register */ +#define PCUSB (1UL<<31) + +/* USB Clock Control register */ +#define DEV_CLK_EN (1UL<<1) +#define AHB_CLK_EN (1UL<<4) + +/* USB Clock Status register */ +#define DEV_CLK_ON (1UL<<1) +#define AHB_CLK_ON (1UL<<4) + +/* USB Device Interupt registers */ +#define FRAME (1UL<<0) +#define EP_FAST (1UL<<1) +#define EP_SLOW (1UL<<2) +#define DEV_STAT (1UL<<3) +#define CCEMPTY (1UL<<4) +#define CDFULL (1UL<<5) +#define RxENDPKT (1UL<<6) +#define TxENDPKT (1UL<<7) +#define EP_RLZED (1UL<<8) +#define ERR_INT (1UL<<9) + +/* 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 (1UL<<10) +#define PKT_RDY (1UL<<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) + +static uint32_t endpointStallState; + +static int epComplete = 0; +/* + * Serial Interface Engine commands + */ + +static void SIECommand(uint32_t 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)); +} + +static void SIEWriteData(uint8_t 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)); +} + +static uint8_t SIEReadData(uint32_t 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 (uint8_t)LPC_USB->USBCmdData; +} + +static void SIEsetDeviceStatus(uint8_t status) +{ + /* Write SIE device status register */ + SIECommand(SIE_CMD_SET_DEVICE_STATUS); + SIEWriteData(status); +} + +static uint8_t SIEgetDeviceStatus(void) +{ + /* Read SIE device status register */ + SIECommand(SIE_CMD_GET_DEVICE_STATUS); + return SIEReadData(SIE_CMD_GET_DEVICE_STATUS); +} + +void SIEsetAddress(uint8_t address) +{ + /* Write SIE device address register */ + SIECommand(SIE_CMD_SET_ADDRESS); + SIEWriteData((address & 0x7f) | SIE_DSA_DEV_EN); +} + +static uint8_t SIEselectEndpoint(uint8_t endpoint) +{ + /* SIE select endpoint command */ + SIECommand(SIE_CMD_SELECT_ENDPOINT(endpoint)); + return SIEReadData(SIE_CMD_SELECT_ENDPOINT(endpoint)); +} + +static uint8_t SIEclearBuffer(void) +{ + /* SIE clear buffer command */ + SIECommand(SIE_CMD_CLEAR_BUFFER); + return SIEReadData(SIE_CMD_CLEAR_BUFFER); +} + +static void SIEvalidateBuffer(void) +{ + /* SIE validate buffer command */ + SIECommand(SIE_CMD_VALIDATE_BUFFER); +} + +static void SIEsetEndpointStatus(uint8_t endpoint, uint8_t status) +{ + /* SIE set endpoint status command */ + SIECommand(SIE_CMD_SET_ENDPOINT_STATUS(endpoint)); + SIEWriteData(status); +} + +static uint16_t SIEgetFrameNumber(void) __attribute__ ((unused)); +static uint16_t SIEgetFrameNumber(void) +{ + /* Read current frame number */ + uint16_t lowByte; + uint16_t highByte; + + SIECommand(SIE_CMD_READ_FRAME_NUMBER); + lowByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER); + highByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER); + + return (highByte << 8) | lowByte; +} + +static void SIEconfigureDevice(void) +{ + /* SIE Configure device command */ + SIECommand(SIE_CMD_CONFIGURE_DEVICE); + SIEWriteData(SIE_CONF_DEVICE); +} + +static void SIEunconfigureDevice(void) +{ + /* SIE Configure device command */ + SIECommand(SIE_CMD_CONFIGURE_DEVICE); + SIEWriteData(0); +} + +static void SIEconnect(void) +{ + /* Connect USB device */ + uint8_t status; + + status = SIEgetDeviceStatus(); + SIEsetDeviceStatus(status | SIE_DS_CON); +} + + +static void SIEdisconnect(void) +{ + /* Disconnect USB device */ + uint8_t status; + + status = SIEgetDeviceStatus(); + SIEsetDeviceStatus(status & ~SIE_DS_CON); +} + +/* + * Endpoint commands + */ + +static uint8_t selectEndpointClearInterrupt(uint8_t endpoint) +{ + /* Implemented using using EP_INT_CLR. */ + LPC_USB->USBEpIntClr = EP(endpoint); + while (!(LPC_USB->USBDevIntSt & CDFULL)); + return (uint8_t)LPC_USB->USBCmdData; +} + +static void stallEndpoint(uint8_t endpoint) +{ + /* Stall an endpoint */ + if ( (endpoint==EP0IN) || (endpoint==EP0OUT) ) + { + /* Conditionally stall both control endpoints */ + SIEsetEndpointStatus(EP0OUT, SIE_SES_CND_ST); + } + else + { + SIEsetEndpointStatus(endpoint, SIE_SES_ST); + + /* Update stall state */ + endpointStallState |= EP(endpoint); + } +} + +static void unstallEndpoint(uint8_t endpoint) +{ + /* Unstall an endpoint. The endpoint will also be reinitialised */ + SIEsetEndpointStatus(endpoint, 0); + + /* Update stall state */ + endpointStallState &= ~EP(endpoint); +} + +static bool getEndpointStallState(uint8_t endpoint) +{ + /* Returns true if endpoint stalled */ + return endpointStallState & EP(endpoint); +} + +static void realiseEndpoint(uint8_t endpoint, uint32_t 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); +} + +static void enableEndpointEvent(uint8_t endpoint) +{ + /* Enable an endpoint interrupt */ + LPC_USB->USBEpIntEn |= EP(endpoint); +} + +static void disableEndpointEvent(uint8_t endpoint) __attribute__ ((unused)); +static void disableEndpointEvent(uint8_t endpoint) +{ + /* Disable an endpoint interrupt */ + LPC_USB->USBEpIntEn &= ~EP(endpoint); +} + +static volatile uint32_t __attribute__((used)) dummyRead; + +static uint32_t endpointRead(uint8_t endpoint, uint8_t *buffer) +{ + /* Read from an OUT endpoint */ + uint32_t size; + uint32_t i; + uint32_t data; + uint8_t offset; + + LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | RD_EN; + while (!(LPC_USB->USBRxPLen & PKT_RDY)); + + size = LPC_USB->USBRxPLen & PKT_LNGTH_MASK; + + offset = 0; + + if (size > 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; + } + } + else + { + dummyRead = LPC_USB->USBRxData; + } + + SIEselectEndpoint(endpoint); + SIEclearBuffer(); + return size; +} + +static void endpointWrite(uint8_t endpoint, uint8_t *buffer, uint32_t size) +{ + /* Write to an IN endpoint */ + uint32_t temp, data; + uint8_t 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); + } + else + { + LPC_USB->USBTxData = 0; + } + + /* Clear WR_EN to cover zero length packet case */ + LPC_USB->USBCtrl=0; + + SIEselectEndpoint(endpoint); + SIEvalidateBuffer(); +} + +/* + * USBBusInterface API + */ + +bool USBBusInterface_init(void) +{ + /* Disable IRQ */ + NVIC_DisableIRQ(USB_IRQn); + + /* 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; + + /* Disconnect USB device */ + SIEdisconnect(); + + /* 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(0.3); + + /* Set the maximum packet size for the control endpoints */ + realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0); + realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0); + + /* Attach IRQ */ + NVIC_EnableIRQ(USB_IRQn); + + /* Enable interrupts for device events and EP0 */ + LPC_USB->USBDevIntEn = EP_SLOW | DEV_STAT; + enableEndpointEvent(EP0IN); + enableEndpointEvent(EP0OUT); + return true; +} + +void USBBusInterface_uninit(void) +{ + /* Ensure device disconnected */ + SIEdisconnect(); + + /* Disable USB interrupts */ + NVIC_DisableIRQ(USB_IRQn); +} + +void USBBusInterface_connect(void) +{ + /* Connect USB device */ + SIEconnect(); +} + +void USBBusInterface_disconnect(void) +{ + /* Disconnect USB device */ + SIEdisconnect(); +} + +void USBBusInterface_configureDevice(void) +{ + SIEconfigureDevice(); +} + +void USBBusInterface_unconfigureDevice(void) +{ + SIEunconfigureDevice(); +} + +void USBBusInterface_setAddress(uint8_t address) +{ + SIEsetAddress(address); +} + +void USBBusInterface_EP0setup(uint8_t *buffer) +{ + endpointRead(EP0OUT, buffer); +} + +void USBBusInterface_EP0read(void) +{ + /* Not required */ +} + +uint32_t USBBusInterface_EP0getReadResult(uint8_t *buffer) +{ + return endpointRead(EP0OUT, buffer); +} + +void USBBusInterface_EP0write(uint8_t *buffer, uint32_t size) +{ + endpointWrite(EP0IN, buffer, size); +} + +void USBBusInterface_EP0getWriteResult(void) +{ + /* Not required */ +} + +void USBBusInterface_EP0stall(void) +{ + /* This will stall both control endpoints */ + stallEndpoint(EP0OUT); +} + +EP_STATUS USBBusInterface_endpointRead(uint8_t endpoint, uint8_t *data, uint32_t maximumSize) +{ + if (getEndpointStallState(endpoint)) + { + return EP_STALLED; + } + + return EP_PENDING; +} + +EP_STATUS USBBusInterface_endpointReadResult(uint8_t endpoint, uint32_t *bytesRead) +{ + return EP_PENDING; +} + +EP_STATUS USBBusInterface_endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) +{ + if (getEndpointStallState(endpoint)) + { + return EP_STALLED; + } + + epComplete &= EP(endpoint); + + endpointWrite(endpoint, data, size); + return EP_PENDING; +} + +EP_STATUS USBBusInterface_endpointWriteResult(uint8_t endpoint) +{ + if (epComplete & EP(endpoint)) + { + epComplete &= ~EP(endpoint); + return EP_COMPLETED; + } + + return EP_PENDING; +} + +bool USBBusInterface_realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags) +{ + realiseEndpoint(endpoint, maxPacket); + enableEndpointEvent(endpoint); + return true; +} + +void USBBusInterface_stallEndpoint(uint8_t endpoint) +{ + stallEndpoint(endpoint); +} + +void USBBusInterface_unstallEndpoint(uint8_t endpoint) +{ + unstallEndpoint(endpoint); +} + +bool USBBusInterface_getEndpointStallState(uint8_t endpoint) +{ + return getEndpointStallState(endpoint); +} + +void USBBusInterface_remoteWakeup(void) +{ + /* Remote wakeup */ + uint8_t status; + + /* Enable USB clocks */ + LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN; + while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON)); + + status = SIEgetDeviceStatus(); + SIEsetDeviceStatus(status & ~SIE_DS_SUS); +} + +/* + * USB interrupt handler + */ + +extern "C" +{ +void USB_IRQHandler(void) +{ + uint8_t devStat; + + if (LPC_USB->USBDevIntSt & FRAME) + { + /* Start of frame event */ + USBDevice_SOF(SIEgetFrameNumber()); + /* 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 = SIEgetDeviceStatus(); + + if (devStat & SIE_DS_RST) + { + /* Bus reset */ + USBDevice_busReset(); + } + } + + 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 */ + USBDevice_EP0setup(); + } + else + { + USBDevice_EP0out(); + } + } + + if (LPC_USB->USBEpIntSt & EP(EP0IN)) + { + selectEndpointClearInterrupt(EP0IN); + USBDevice_EP0in(); + } + +/* TODO: This should cover all endpoints, not just EP1,2,3:*/ + if (LPC_USB->USBEpIntSt & EP(EP1IN)) + { + selectEndpointClearInterrupt(EP1IN); + epComplete |= EP(EP1IN); + } + + if (LPC_USB->USBEpIntSt & EP(EP1OUT)) + { + selectEndpointClearInterrupt(EP1OUT); + epComplete |= EP(EP1OUT); + } + + if (LPC_USB->USBEpIntSt & EP(EP2IN)) + { + selectEndpointClearInterrupt(EP2IN); + epComplete |= EP(EP2IN); + } + + if (LPC_USB->USBEpIntSt & EP(EP2OUT)) + { + selectEndpointClearInterrupt(EP2OUT); + epComplete |= EP(EP2OUT); + } + + if (LPC_USB->USBEpIntSt & EP(EP3IN)) + { + selectEndpointClearInterrupt(EP3IN); + epComplete |= EP(EP3IN); + } + + if (LPC_USB->USBEpIntSt & EP(EP3OUT)) + { + selectEndpointClearInterrupt(EP3OUT); + epComplete |= EP(EP3OUT); + } + + /* 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; + } +} +} + +#endif