/* ---------------------------------------------------------------------------- * ATMEL Microcontroller Software Support * ---------------------------------------------------------------------------- * Copyright (c) 2008, Atmel Corporation * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ---------------------------------------------------------------------------- */ //----------------------------------------------------------------------------- /// \dir "USB COMPOSITE CDC+HID project" /// /// !!!Purpose /// /// The USB COMPOSITE Project will help you to get familiar with the /// USB Device Port(UDP)interface and also some of the other interfaces in /// AT91SAM microcontrollers. Also it can help you to be familiar with the USB /// Framework that is used for rapid development of USB-compliant class /// drivers such as USB Communication Device class (CDC), and how to combine /// two USB functions to a single composite device (such as CDC + MSD). /// /// You can find following information depends on your needs: /// - Sample usage of USB Device Framework. /// - USB COMPOSITE device and functions driver development based on the AT91 /// USB Device Framework and other re-usable class driver code. /// - USB enumerate sequence, the standard and class-specific descriptors and /// requests handling. /// - The initialize sequence and usage of UDP interface. /// /// !See /// - pio: Pin configurations and peripheral configure. /// - memories: Storage Media interface for MSD /// - ssc: SSC interface driver /// - "dac-at73c213": I2S codec at73c213 driver /// - usb: USB Device Framework, USB CDC driver and UDP interface driver /// - "AT91 USB device framework" /// - "USBD API" /// - "composite" /// - "USB COMPOSITE Device" /// - "hid-keyboard" /// - "USB HID Keyboard" /// - "cdc-serial" /// - "USB CDC Serial Device" /// - projects: /// - "usb-device-hid-keyboard-project" /// - "usb-device-cdc-serial-project" /// /// !!!Requirements /// /// This package can be used with some of Atmel evaluation kits that have UDP /// interface, depending on the functions included. /// /// The current supported board list: /// - at91sam7s-ek (exclude at91sam7s32) /// - at91sam7x-ek /// - at91sam7xc-ek /// - at91sam7a3-ek /// - at91sam7se-ek /// - at91sam9260-ek /// - at91sam9263-ek /// /// !!!Windows Driver Update /// /// The composite device is generally supported by Microsoft windows, but some /// patches are needed for muti-interface functions such as CDC & Audio. The /// example composite devices are tested under windows XP (SP3). For CDC /// serial port, additional windows driver file (CompositeCDCSerial.inf) can /// be found at at91lib\usb\device\composite\drv. /// /// The following is alternate update to fix the composite device support /// on windows XP: /// /// !!Install Windows Service Pack 3 (SP3) /// /// All the fixes for USB generic driver are included in window XP service pack /// 3. It can be found at /// http://technet.microsoft.com/zh-cn/windows/bb794714(en-us).aspx . /// /// !!Install Windows Hot Fixes /// /// Two hot fixes are necessary for window to recognize the composite device /// correctly: /// /// -# http://support.microsoft.com/kb/814560 /// -# http://support.microsoft.com/kb/918365 /// /// !!!Description /// /// When an EK running this program connected to a host (PC for example), with /// USB cable, host will notice the attachment of a USB %device. No %device /// driver offered for the %device now. /// /// !!!Usage /// /// -# Build the program and download it inside the evaluation board. Please /// refer to the /// /// SAM-BA User Guide, the /// /// GNU-Based Software Development application note or to the /// /// IAR EWARM User Guide, depending on your chosen solution. /// -# On the computer, open and configure a terminal application /// (e.g. HyperTerminal on Microsoft Windows) with these settings: /// - 115200 bauds /// - 8 bits of data /// - No parity /// - 1 stop bit /// - No flow control /// -# Start the application. /// -# In the terminal window, the following text should appear: /// \code /// -- USB Composite Device Project xxx -- /// -- AT91xxxxxx-xx /// -- Compiled: xxx xx xxxx xx:xx:xx -- /// \endcode /// -# When connecting USB cable to windows, the LED blinks, and the host /// reports a new USB %device attachment. /// -# For the windows driver installation and the test functions, please /// refer to "USB CDC serial converter" & /// "USB HID Keyboard Project". /// -# You can use the inf file /// at91lib\\usb\\device\\composite\\drv\\CompositeCDCSerial.inf /// to install the CDC serial port. /// //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// \unit /// /// !Purpose /// /// This file contains all the specific code for the /// usb-device-composite-cdchid-project /// /// !Contents /// /// The code can be roughly broken down as follows: /// - Configuration functions /// - VBus_Configure /// - PIO configurations in start of main /// - Interrupt handlers /// - ISR_Vbus /// - Callback functions /// - USBDCallbacks_RequestReceived /// - The main function, which implements the program behavior /// /// Please refer to the list of functions in the #Overview# tab of this unit /// for more detailed information. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Headers //------------------------------------------------------------------------------ #include #include #include #include #include #include #include #include #include #include #include #include #include //----------------------------------------------------------------------------- // Definitions //----------------------------------------------------------------------------- #ifndef AT91C_ID_TC0 #if defined(AT91C_ID_TC012) #define AT91C_ID_TC0 AT91C_ID_TC012 #elif defined(AT91C_ID_TC) #define AT91C_ID_TC0 AT91C_ID_TC #else #error Pb define ID_TC #endif #endif /// Master clock frequency in Hz #define MCK BOARD_MCK /// Number of keys used in the example. #define NUM_KEYS 4 /// Number of non-modifiers keys. #define NUM_NORMAL_KEYS 3 /// Number of modifier keys. #define NUM_MODIFIER_KEYS (NUM_KEYS - NUM_NORMAL_KEYS) /// Num lock LED index. #define LED_NUMLOCK USBD_LEDOTHER /// Delay for pushbutton debouncing (ms) #define DEBOUNCE_TIME 10 /// PIT period value (useconds) #define PIT_PERIOD 1000 /// Size in bytes of the buffer used for reading data from the USB & USART #define DATABUFFERSIZE BOARD_USB_ENDPOINTS_MAXPACKETSIZE(2) /// Use for power management #define STATE_IDLE 0 /// The USB device is in suspend state #define STATE_SUSPEND 4 /// The USB device is in resume state #define STATE_RESUME 5 //----------------------------------------------------------------------------- // Internal variables //----------------------------------------------------------------------------- /// State of USB, for suspend and resume unsigned char USBState = STATE_IDLE; //- CDC /// List of pins that must be configured for use by the application. static const Pin pinsUsart[] = {PIN_USART0_TXD, PIN_USART0_RXD}; /// Double-buffer for storing incoming USART data. static unsigned char usartBuffers[2][DATABUFFERSIZE]; /// Current USART buffer index. static unsigned char usartCurrentBuffer = 0; /// Buffer for storing incoming USB data. static unsigned char usbSerialBuffer0[DATABUFFERSIZE]; //static unsigned char usbSerialBuffer1[DATABUFFERSIZE]; //- HID /// List of pinsPushButtons to configure for the applicatino. static Pin pinsPushButtons[] = {PINS_PUSHBUTTONS}; /// Array of key codes produced by each button. static unsigned char keyCodes[NUM_KEYS] = { HIDKeypad_A, HIDKeypad_NUMLOCK, HIDKeypad_9, HIDKeypad_RIGHTSHIFT }; /// Current status (pressed or not) for each key. static unsigned char keyStatus[NUM_KEYS]; //------------------------------------------------------------------------------ // Remote wake-up support (optional) //------------------------------------------------------------------------------ #if (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_BUSPOWERED_RWAKEUP) \ || (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_SELFPOWERED_RWAKEUP) #define WAKEUP_CONFIGURE() ConfigureWakeUp() /// Button for Wake-UP the USB device. static const Pin pinWakeUp = PIN_PUSHBUTTON_1; //------------------------------------------------------------------------------ /// Interrupt service routine for the PIT. Debounces the wake-up pin input. //------------------------------------------------------------------------------ static void ISR_Pit(void) { static unsigned long debounceCounter = DEBOUNCE_TIME; unsigned long pisr = 0; // Read the PISR pisr = PIT_GetStatus() & AT91C_PITC_PITS; if (pisr != 0) { // Read the PIVR. It acknowledges the IT PIT_GetPIVR(); } // Button released if (PIO_Get(&pinWakeUp)) { debounceCounter = DEBOUNCE_TIME; } // Button still pressed else { debounceCounter--; } // End of debounce time if (debounceCounter == 0) { debounceCounter = DEBOUNCE_TIME; PIT_DisableIT(); AT91C_BASE_PITC->PITC_PIMR &= ~AT91C_PITC_PITEN; COMPOSITEDDriver_RemoteWakeUp(); } } //------------------------------------------------------------------------------ /// Configures the PIT to generate 1ms ticks. //------------------------------------------------------------------------------ static void ConfigurePit(void) { // Initialize and enable the PIT PIT_Init(PIT_PERIOD, BOARD_MCK / 1000000); // Disable the interrupt on the interrupt controller AIC_DisableIT(AT91C_ID_SYS); // Configure the AIC for PIT interrupts AIC_ConfigureIT(AT91C_ID_SYS, 0, ISR_Pit); // Enable the interrupt on the interrupt controller AIC_EnableIT(AT91C_ID_SYS); // Enable the interrupt on the pit PIT_EnableIT(); // Enable the pit PIT_Enable(); } //------------------------------------------------------------------------------ /// Interrupt service routine for the remote wake-up pin. Starts the debouncing /// sequence. //------------------------------------------------------------------------------ static void WakeUpHandler(const Pin *pin) { TRACE_DEBUG("Wake-up handler\n\r"); // Check current level on the remote wake-up pin if (!PIO_Get(&pinWakeUp)) { ConfigurePit(); } } //------------------------------------------------------------------------------ /// Configures the wake-up pin to generate interrupts. //------------------------------------------------------------------------------ static void ConfigureWakeUp(void) { TRACE_INFO("Wake-up configuration\n\r"); // Configure PIO PIO_Configure(&pinWakeUp, 1); PIO_ConfigureIt(&pinWakeUp, WakeUpHandler); PIO_EnableIt(&pinWakeUp); } #else #define WAKEUP_CONFIGURE() #endif //----------------------------------------------------------------------------- // VBus monitoring (optional) //----------------------------------------------------------------------------- #if defined(PIN_USB_VBUS) #define VBUS_CONFIGURE() VBus_Configure() /// VBus pin instance. static const Pin pinVbus = PIN_USB_VBUS; //----------------------------------------------------------------------------- /// Handles interrupts coming from PIO controllers. //----------------------------------------------------------------------------- static void ISR_Vbus(const Pin *pPin) { TRACE_INFO("VBUS "); // Check current level on VBus if (PIO_Get(&pinVbus)) { TRACE_INFO("conn\n\r"); USBD_Connect(); } else { TRACE_INFO("discon\n\r"); USBD_Disconnect(); } } //----------------------------------------------------------------------------- /// Configures the VBus pin to trigger an interrupt when the level on that pin /// changes. //----------------------------------------------------------------------------- static void VBus_Configure( void ) { TRACE_INFO("VBus configuration\n\r"); // Configure PIO PIO_Configure(&pinVbus, 1); PIO_ConfigureIt(&pinVbus, ISR_Vbus); PIO_EnableIt(&pinVbus); // Check current level on VBus if (PIO_Get(&pinVbus)) { // if VBUS present, force the connect TRACE_INFO("conn\n\r"); USBD_Connect(); } else { USBD_Disconnect(); } } #else #define VBUS_CONFIGURE() USBD_Connect() #endif //#if defined(PIN_USB_VBUS) #if defined (CP15_PRESENT) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { PMC_CPUInIdleMode(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { } #elif defined(at91sam7a3) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #elif defined (at91sam7se) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; // Voltage regulator in standby mode : Enable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // Voltage regulator in normal mode : Disable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY; // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #elif defined (at91sam7s) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; // Voltage regulator in standby mode : Enable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // Voltage regulator in normal mode : Disable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY; // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #elif defined (at91sam7x) || defined (at91sam7xc) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; // Voltage regulator in standby mode : Enable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // Voltage regulator in normal mode : Disable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY; // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #endif //------------------------------------------------------------------------------ // Callbacks re-implementation //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Invoked when the USB device leaves the Suspended state. By default, /// configures the LEDs. //------------------------------------------------------------------------------ void USBDCallbacks_Resumed(void) { // Initialize LEDs LED_Configure(USBD_LEDPOWER); LED_Set(USBD_LEDPOWER); LED_Configure(USBD_LEDUSB); LED_Clear(USBD_LEDUSB); USBState = STATE_RESUME; } //------------------------------------------------------------------------------ /// Invoked when the USB device gets suspended. By default, turns off all LEDs. //------------------------------------------------------------------------------ void USBDCallbacks_Suspended(void) { // Turn off LEDs LED_Clear(USBD_LEDPOWER); LED_Clear(USBD_LEDUSB); USBState = STATE_SUSPEND; } //----------------------------------------------------------------------------- // Internal functions //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// Handles interrupts coming from Timer #0. //----------------------------------------------------------------------------- static void ISR_Timer0() { unsigned char size; unsigned int status = AT91C_BASE_TC0->TC_SR; if ((status & AT91C_TC_CPCS) != 0) { // Flush PDC buffer size = DATABUFFERSIZE - AT91C_BASE_US0->US_RCR; if (size == 0) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; return; } AT91C_BASE_US0->US_RCR = 0; // Send current buffer through the USB while (CDCDSerialDriver_Write(0, usartBuffers[usartCurrentBuffer], size, 0, 0) != USBD_STATUS_SUCCESS); // Restart read on buffer USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[usartCurrentBuffer], DATABUFFERSIZE); usartCurrentBuffer = 1 - usartCurrentBuffer; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; } } //----------------------------------------------------------------------------- /// Callback invoked when data has been received on the USB. //----------------------------------------------------------------------------- static void UsbDataReceived0(unsigned int unused, unsigned char status, unsigned int received, unsigned int remaining) { // Check that data has been received successfully if (status == USBD_STATUS_SUCCESS) { // Send data through USART while (!USART_WriteBuffer(AT91C_BASE_US0, usbSerialBuffer0, received)); AT91C_BASE_US0->US_IER = AT91C_US_TXBUFE; // Check if bytes have been discarded if ((received == DATABUFFERSIZE) && (remaining > 0)) { TRACE_WARNING( "UsbDataReceived: %u bytes discarded\n\r", remaining); } } else { TRACE_WARNING("UsbDataReceived: Transfer error\n\r"); } } //----------------------------------------------------------------------------- /// Handles interrupts coming from USART #0. //----------------------------------------------------------------------------- static void ISR_Usart0() { unsigned int status = AT91C_BASE_US0->US_CSR; unsigned short serialState; // If USB device is not configured, do nothing if (USBD_GetState() != USBD_STATE_CONFIGURED) { AT91C_BASE_US0->US_IDR = 0xFFFFFFFF; return; } // Buffer has been read successfully if ((status & AT91C_US_ENDRX) != 0) { // Disable timer AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // Send buffer through the USBSerial0 while (CDCDSerialDriver_Write(0, usartBuffers[usartCurrentBuffer], DATABUFFERSIZE, 0, 0) != USBD_STATUS_SUCCESS); // Restart read on buffer USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[usartCurrentBuffer], DATABUFFERSIZE); usartCurrentBuffer = 1 - usartCurrentBuffer; // Restart timer AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; } // Buffer has been sent if ((status & AT91C_US_TXBUFE) != 0) { // Restart USB read CDCDSerialDriver_Read(0, usbSerialBuffer0, DATABUFFERSIZE, (TransferCallback) UsbDataReceived0, 0); AT91C_BASE_US0->US_IDR = AT91C_US_TXBUFE; } // Errors serialState = CDCDSerialDriver_GetSerialState(0); // Overrun if ((status & AT91C_US_OVER) != 0) { TRACE_WARNING("ISR_Usart0: Overrun\n\r"); serialState |= CDCD_STATE_OVERRUN; } // Framing error if ((status & AT91C_US_FRAME) != 0) { TRACE_WARNING("ISR_Usart0: Framing error\n\r"); serialState |= CDCD_STATE_FRAMING; } CDCDSerialDriver_SetSerialState(0, serialState); } //----------------------------------------------------------------------------- /// Invoked when the status of the keyboard LEDs changes. Turns the num. lock /// LED on or off. /// \param numLockStatus Indicates the current status of the num. lock key. /// \param capsLockStatus Indicates the current status of the caps lock key. /// \param scrollLockStatus Indicates the current status of the scroll lock key //----------------------------------------------------------------------------- void HIDDKeyboardCallbacks_LedsChanged( unsigned char numLockStatus, unsigned char capsLockStatus, unsigned char scrollLockStatus) { // Num. lock if (numLockStatus) { LED_Set(LED_NUMLOCK); } else { LED_Clear(LED_NUMLOCK); } } //----------------------------------------------------------------------------- // Internal functions //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// Monitor keyboard buttons & Update key status in HID driver //----------------------------------------------------------------------------- static void HIDDKeyboardProcessKeys(void) { unsigned int i; unsigned char pressedKeys[NUM_KEYS]; unsigned char pressedKeysSize = 0; unsigned char releasedKeys[NUM_KEYS]; unsigned char releasedKeysSize = 0; // Monitor buttons for (i=0; i < PIO_LISTSIZE(pinsPushButtons); i++) { // Check if button state has changed unsigned char isButtonPressed = PIO_Get(&(pinsPushButtons[i])); if (isButtonPressed != keyStatus[i]) { // Update button state if (!isButtonPressed) { // Key has been pressed TRACE_INFO("-I- Key %u has been pressed\n\r", i); keyStatus[i] = 0; pressedKeys[pressedKeysSize] = keyCodes[i]; pressedKeysSize++; HIDDKeyboardDriver_RemoteWakeUp(); } else { // Key has been released TRACE_INFO("-I- Key %u has been released\n\r", i); keyStatus[i] = 1; releasedKeys[releasedKeysSize] = keyCodes[i]; releasedKeysSize++; } } } // Update key status in the HID driver if necessary if ((pressedKeysSize != 0) || (releasedKeysSize != 0)) { unsigned char status; do { status = HIDDKeyboardDriver_ChangeKeys(pressedKeys, pressedKeysSize, releasedKeys, releasedKeysSize); } while (status != USBD_STATUS_SUCCESS); } } //----------------------------------------------------------------------------- // Exported function //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// Initializes drivers and start the USB composite device. //----------------------------------------------------------------------------- int main() { TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK); printf("-- USB Composite Device Project %s --\n\r", SOFTPACK_VERSION); printf("-- %s\n\r", BOARD_NAME); printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__); // If they are present, configure Vbus & Wake-up pins PIO_InitializeInterrupts(0); // ----- HID Function Initialize // Initialize key statuses and configure push buttons PIO_Configure(pinsPushButtons, PIO_LISTSIZE(pinsPushButtons)); memset(keyStatus, 1, NUM_KEYS); // Configure LEDs LED_Configure(LED_NUMLOCK); // ----- CDC Function Initialize // Configure USART PIO_Configure(pinsUsart, PIO_LISTSIZE(pinsUsart)); AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_US0; AT91C_BASE_US0->US_IDR = 0xFFFFFFFF; USART_Configure(AT91C_BASE_US0, USART_MODE_ASYNCHRONOUS, 115200, MCK); USART_SetTransmitterEnabled(AT91C_BASE_US0, 1); USART_SetReceiverEnabled(AT91C_BASE_US0, 1); AIC_ConfigureIT(AT91C_ID_US0, 0, ISR_Usart0); AIC_EnableIT(AT91C_ID_US0); // Configure timer 0 AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_IDR = 0xFFFFFFFF; AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK | AT91C_TC_CPCSTOP | AT91C_TC_CPCDIS | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_WAVE; AT91C_BASE_TC0->TC_RC = 0x00FF; AT91C_BASE_TC0->TC_IER = AT91C_TC_CPCS; AIC_ConfigureIT(AT91C_ID_TC0, 0, ISR_Timer0); AIC_EnableIT(AT91C_ID_TC0); // USB COMPOSITE driver initialization COMPOSITEDDriver_Initialize(); WAKEUP_CONFIGURE(); // connect if needed VBUS_CONFIGURE(); // Driver loop while (1) { // Device is not configured if (USBD_GetState() < USBD_STATE_CONFIGURED) { // Connect pull-up, wait for configuration USBD_Connect(); while (USBD_GetState() < USBD_STATE_CONFIGURED); // Start receiving data on the USART usartCurrentBuffer = 0; USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[0], DATABUFFERSIZE); USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[1], DATABUFFERSIZE); AT91C_BASE_US0->US_IER = AT91C_US_ENDRX | AT91C_US_FRAME | AT91C_US_OVER; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Start receiving data on the USB CDCDSerialDriver_Read(0, usbSerialBuffer0, DATABUFFERSIZE, (TransferCallback) UsbDataReceived0, 0); } else { HIDDKeyboardProcessKeys(); } if( USBState == STATE_SUSPEND ) { TRACE_DEBUG("suspend !\n\r"); LowPowerMode(); USBState = STATE_IDLE; } if( USBState == STATE_RESUME ) { // Return in normal MODE TRACE_DEBUG("resume !\n\r"); NormalPowerMode(); USBState = STATE_IDLE; } } }