USB Mouse (relative) example for mbed NXP LPC11U24 beta
USB_HID_STACK/USBBusInterface_LPC17_LPC23.c
- Committer:
- chris
- Date:
- 2011-11-09
- Revision:
- 1:e089be2a6aa1
- Parent:
- 0:163560051396
File content as of revision 1:e089be2a6aa1:
/* 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