at91work/usb-device-composite-cdchid.../main.c

1012 lines
36 KiB
C

/* ----------------------------------------------------------------------------
* 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
/// <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6224.pdf">
/// SAM-BA User Guide</a>, the
/// <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6310.pdf">
/// GNU-Based Software Development</a> application note or to the
/// <a href="ftp://ftp.iar.se/WWWfiles/arm/Guides/EWARM_UserGuide.ENU.pdf">
/// IAR EWARM User Guide</a>, 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 <board.h>
#include <pio/pio.h>
#include <pio/pio_it.h>
#include <aic/aic.h>
#include <usart/usart.h>
#include <utility/trace.h>
#include <pit/pit.h>
#include <usb/common/core/USBConfigurationDescriptor.h>
#include <usb/common/hid/HIDKeypad.h>
#include <utility/led.h>
#include <pmc/pmc.h>
#include <usb/device/composite/COMPOSITEDDriver.h>
#include <string.h>
//-----------------------------------------------------------------------------
// 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;
}
}
}