1 /*----------------------------------------------------------------------------
3 *----------------------------------------------------------------------------
5 * Purpose: USB Hardware Layer Module for Philips LPC17xx
7 *----------------------------------------------------------------------------
8 * This software is supplied "AS IS" without any warranties, express,
9 * implied or statutory, including but not limited to the implied
10 * warranties of fitness for purpose, satisfactory quality and
11 * noninfringement. Keil extends you a royalty-free right to reproduce
12 * and distribute executable files created using this software for use
13 * on NXP Semiconductors LPC microcontroller devices only. Nothing else
14 * gives you the right to use this software.
16 * Copyright (c) 2009 Keil - An ARM Company. All rights reserved.
17 *----------------------------------------------------------------------------
19 * V1.20 Added USB_ClearEPBuf
20 * V1.00 Initial Version
21 *----------------------------------------------------------------------------*/
22 #include "projectconfig.h"
32 * USB and IO Clock configuration only.
33 * The same as call PeriClkIOInit(IOCON_USB);
34 * The purpose is to reduce the code space for
35 * overall USB project and reserve code space for
40 void USBIOClkConfig( void )
42 /* Enable AHB clock to the GPIO domain. */
43 SCB_SYSAHBCLKCTRL
|= SCB_SYSAHBCLKCTRL_GPIO
;
45 /* Enable Timer32_1, IOCON, and USB blocks */
46 SCB_SYSAHBCLKCTRL
|= (SCB_SYSAHBCLKCTRL_CT32B1
| SCB_SYSAHBCLKCTRL_IOCON
| SCB_SYSAHBCLKCTRL_USB_REG
);
49 SCB_PDRUNCFG
&= ~(SCB_PDSLEEPCFG_USBPAD_PD
); // Power-up USB PHY
50 SCB_PDRUNCFG
&= ~(SCB_PDSLEEPCFG_USBPLL_PD
); // Power-up USB PLL
52 SCB_USBPLLCLKSEL
= SCB_USBPLLCLKSEL_SOURCE_MAINOSC
; // Select PLL Input
53 SCB_USBPLLCLKUEN
= SCB_USBPLLCLKUEN_UPDATE
; // Update Clock Source
54 SCB_USBPLLCLKUEN
= SCB_USBPLLCLKUEN_DISABLE
; // Toggle Update Register
55 SCB_USBPLLCLKUEN
= SCB_USBPLLCLKUEN_UPDATE
;
57 // Wait until the USB clock is updated
58 while (!(SCB_USBPLLCLKUEN
& SCB_USBPLLCLKUEN_UPDATE
));
60 // Set USB clock to 48MHz (12MHz x 4)
61 SCB_USBPLLCTRL
= (SCB_USBPLLCTRL_MULT_4
);
62 while (!(SCB_USBPLLSTAT
& SCB_USBPLLSTAT_LOCK
)); // Wait Until PLL Locked
63 SCB_USBCLKSEL
= SCB_USBCLKSEL_SOURCE_USBPLLOUT
;
65 // Set USB pin functions
66 IOCON_PIO0_1
&= ~IOCON_PIO0_1_FUNC_MASK
;
67 IOCON_PIO0_1
|= IOCON_PIO0_1_FUNC_CLKOUT
; // CLK OUT
68 IOCON_PIO0_3
&= ~IOCON_PIO0_3_FUNC_MASK
;
69 IOCON_PIO0_3
|= IOCON_PIO0_3_FUNC_USB_VBUS
; // VBus
70 IOCON_PIO0_6
&= ~IOCON_PIO0_6_FUNC_MASK
;
71 IOCON_PIO0_6
|= IOCON_PIO0_6_FUNC_USB_CONNECT
; // Soft Connect
77 * Delay number of clock cycles
78 * Parameters: Delay length
82 void delay (uint32_t length
) {
85 for ( i
= 0; i
< length
; i
++ )
87 __asm
volatile("nop");
93 * Get Endpoint Physical Address
94 * Parameters: EPNum: Endpoint Number
97 * Return Value: Endpoint Physical Address
100 uint32_t EPAdr (uint32_t EPNum
) {
103 val
= (EPNum
& 0x0F) << 1;
113 * Parameters: cmd: Command
117 void WrCmd (uint32_t cmd
) {
119 USB_DEVINTCLR
= CCEMTY_INT
;
121 while ((USB_DEVINTST
& (CCEMTY_INT
| DEV_STAT_INT
)) == 0);
127 * Parameters: cmd: Command
132 void WrCmdDat (uint32_t cmd
, uint32_t val
) {
140 * Write Command to Endpoint
141 * Parameters: cmd: Command
146 void WrCmdEP (uint32_t EPNum
, uint32_t cmd
){
148 WrCmd(CMD_SEL_EP(EPAdr(EPNum
)));
155 * Parameters: cmd: Command
156 * Return Value: Data Value
159 uint32_t RdCmdDat (uint32_t cmd
) {
161 USB_DEVINTCLR
= CCEMTY_INT
| CDFULL_INT
;
163 while ((USB_DEVINTST
& (CDFULL_INT
| DEV_STAT_INT
)) == 0);
164 return (USB_CMDDATA
);
169 * USB Initialize Function
170 * Called by the User to initialize USB
174 void USB_Init (void) {
176 // Setup USB clock and pins
180 /* It's important that only BULK and FRAME(ISO) can be routed
182 USB_DEVFIQSEL
= 0x01; /* SOF Use FIQ */
184 /* Enable the USB Interrupt */
185 NVIC_EnableIRQ(USB_FIQn
);
188 /* Enable the USB Interrupt */
189 NVIC_EnableIRQ(USB_IRQn
);
198 * USB Connect Function
199 * Called by the User to Connect/Disconnect USB
200 * Parameters: con: Connect/Disconnect
204 void USB_Connect (uint32_t con
) {
205 WrCmdDat(CMD_SET_DEV_STAT
, DAT_WR_BYTE(con
? DEV_CON
: 0));
211 * Called automatically on USB Reset
215 void USB_Reset (void) {
217 USB_DEVINTCLR
= 0x000FFFFF;
218 /* Enable all eight(8) EPs, note: EP won't be ready until it's
219 configured/enabled when device sending SetEPStatus command
220 to the command engine. */
221 USB_DEVINTEN
= DEV_STAT_INT
| (0xFF<<1) |
222 (USB_SOF_EVENT
? FRAME_INT
: 0);
228 * USB Suspend Function
229 * Called automatically on USB Suspend
233 void USB_Suspend (void) {
234 /* Performed by Hardware */
239 * USB Resume Function
240 * Called automatically on USB Resume
244 void USB_Resume (void) {
245 /* Performed by Hardware */
250 * USB Remote Wakeup Function
251 * Called automatically on USB Remote Wakeup
255 void USB_WakeUp (void) {
257 if (USB_DeviceStatus
& USB_GETSTATUS_REMOTE_WAKEUP
) {
258 WrCmdDat(CMD_SET_DEV_STAT
, DAT_WR_BYTE(DEV_CON
));
264 * USB Remote Wakeup Configuration Function
265 * Parameters: cfg: Enable/Disable
269 void USB_WakeUpCfg (uint32_t cfg
) {
270 cfg
= cfg
; /* Not needed */
275 * USB Set Address Function
276 * Parameters: adr: USB Address
280 void USB_SetAddress (uint32_t adr
) {
281 WrCmdDat(CMD_SET_ADDR
, DAT_WR_BYTE(DEV_EN
| adr
)); /* Don't wait for next */
282 WrCmdDat(CMD_SET_ADDR
, DAT_WR_BYTE(DEV_EN
| adr
)); /* Setup Status Phase */
287 * USB Configure Function
288 * Parameters: cfg: Configure/Deconfigure
292 void USB_Configure (uint32_t cfg
) {
294 WrCmdDat(CMD_CFG_DEV
, DAT_WR_BYTE(cfg
? CONF_DVICE
: 0));
300 * Configure USB Endpoint according to Descriptor
301 * Parameters: pEPD: Pointer to Endpoint Descriptor
305 void USB_ConfigEP (USB_ENDPOINT_DESCRIPTOR
*pEPD
) {
311 * Set Direction for USB Control Endpoint
312 * Parameters: dir: Out (dir == 0), In (dir <> 0)
316 void USB_DirCtrlEP (uint32_t dir
) {
317 dir
= dir
; /* Not needed */
322 * Enable USB Endpoint
323 * Parameters: EPNum: Endpoint Number
324 * EPNum.0..3: Address
329 void USB_EnableEP (uint32_t EPNum
) {
330 WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum
)), DAT_WR_BYTE(0));
335 * Disable USB Endpoint
336 * Parameters: EPNum: Endpoint Number
337 * EPNum.0..3: Address
342 void USB_DisableEP (uint32_t EPNum
) {
343 WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum
)), DAT_WR_BYTE(EP_STAT_DA
));
349 * Parameters: EPNum: Endpoint Number
350 * EPNum.0..3: Address
355 void USB_ResetEP (uint32_t EPNum
) {
356 WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum
)), DAT_WR_BYTE(0));
361 * Set Stall for USB Endpoint
362 * Parameters: EPNum: Endpoint Number
363 * EPNum.0..3: Address
368 void USB_SetStallEP (uint32_t EPNum
) {
369 WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum
)), DAT_WR_BYTE(EP_STAT_ST
));
374 * Clear Stall for USB Endpoint
375 * Parameters: EPNum: Endpoint Number
376 * EPNum.0..3: Address
381 void USB_ClrStallEP (uint32_t EPNum
) {
382 WrCmdDat(CMD_SET_EP_STAT(EPAdr(EPNum
)), DAT_WR_BYTE(0));
387 * Clear USB Endpoint Buffer
388 * Parameters: EPNum: Endpoint Number
389 * EPNum.0..3: Address
394 void USB_ClearEPBuf (uint32_t EPNum
) {
395 WrCmdEP(EPNum
, CMD_CLR_BUF
);
400 * Read USB Endpoint Data
401 * Parameters: EPNum: Endpoint Number
402 * EPNum.0..3: Address
404 * pData: Pointer to Data Buffer
405 * Return Value: Number of bytes read
408 uint32_t USB_ReadEP (uint32_t EPNum
, uint8_t *pData
) {
411 USB_CTRL
= ((EPNum
& 0x0F) << 2) | CTRL_RD_EN
;
412 /* 3 clock cycles to fetch the packet length from RAM. */
417 } while ((cnt
& PKT_DV
) == 0);
418 cnt
&= PKT_LNGTH_MASK
;
420 for (n
= 0; n
< (cnt
+ 3) / 4; n
++) {
421 *((uint32_t __attribute__((packed
)) *)pData
) = USB_RXDATA
;
427 if ((EPNum
& 0x80) != 0x04) { /* Non-Isochronous Endpoint */
428 WrCmdEP(EPNum
, CMD_CLR_BUF
);
436 * Write USB Endpoint Data
437 * Parameters: EPNum: Endpoint Number
438 * EPNum.0..3: Address
440 * pData: Pointer to Data Buffer
441 * cnt: Number of bytes to write
442 * Return Value: Number of bytes written
445 uint32_t USB_WriteEP (uint32_t EPNum
, uint8_t *pData
, uint32_t cnt
) {
448 USB_CTRL
= ((EPNum
& 0x0F) << 2) | CTRL_WR_EN
;
449 /* 3 clock cycles to fetch the packet length from RAM. */
453 for (n
= 0; n
< (cnt
+ 3) / 4; n
++) {
454 USB_TXDATA
= *((uint32_t __attribute__((packed
)) *)pData
);
460 WrCmdEP(EPNum
, CMD_VALID_BUF
);
466 * Get USB Last Frame Number
468 * Return Value: Frame Number
471 uint32_t USB_GetFrame (void) {
475 val
= RdCmdDat(DAT_RD_FRAME
);
476 val
= val
| (RdCmdDat(DAT_RD_FRAME
) << 8);
483 * USB Interrupt Service Routine
487 void USB_IRQHandler (void)
489 uint32_t disr
, val
, n
, m
;
491 disr
= USB_DEVINTST
; /* Device Interrupt Status */
492 USB_DEVINTCLR
= disr
;
494 /* Device Status Interrupt (Reset, Connect change, Suspend/Resume) */
495 if (disr
& DEV_STAT_INT
) {
496 WrCmd(CMD_GET_DEV_STAT
);
497 val
= RdCmdDat(DAT_GET_DEV_STAT
); /* Device Status */
498 if (val
& DEV_RST
) { /* Reset */
504 if (val
& DEV_CON_CH
) { /* Connect change */
506 USB_Power_Event(val
& DEV_CON
);
509 if (val
& DEV_SUS_CH
) { /* Suspend/Resume */
510 if (val
& DEV_SUS
) { /* Suspend */
512 #if USB_SUSPEND_EVENT
515 } else { /* Resume */
526 /* Start of Frame Interrupt */
527 if (disr
& FRAME_INT
) {
528 USB_DEVINTCLR
= FRAME_INT
;
535 /* NO error interrupt anymore, below code can be used
536 as example to get error status from command engine. */
537 /* Error Interrupt */
538 if (disr
& ERR_INT
) {
539 WrCmd(CMD_RD_ERR_STAT
);
540 val
= RdCmdDat(DAT_RD_ERR_STAT
);
541 USB_Error_Event(val
);
545 /* Endpoint's Interrupt */
546 if (disr
& (0xFF<<1)) {
547 /* if any of the EP0 through EP7 is set, or bit 1 through 9 on disr */
548 for (n
= 0; n
< USB_EP_NUM
; n
++) { /* Check All Endpoints */
549 /* skip frame interrupt at bit 0 in disr */
550 // if (disr & ((1 << n)<<1)) {
551 if ((disr
>>1) & (1 << n
)) {
553 /* clear EP interrupt by sending cmd to the command engine. */
554 WrCmd(CMD_SEL_EP_CLRI(n
));
555 val
= RdCmdDat(DAT_SEL_EP_CLRI(n
));
556 if ((n
& 1) == 0) { /* OUT Endpoint */
557 if (n
== 0) { /* Control OUT Endpoint */
558 if (val
& EP_SEL_STP
) { /* Setup Packet */
560 USB_P_EP
[0](USB_EVT_SETUP
);
566 USB_P_EP
[m
](USB_EVT_OUT
);
568 } else { /* IN Endpoint */
570 USB_P_EP
[m
](USB_EVT_IN
);