/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2011, 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.
 * ----------------------------------------------------------------------------
 */


/**
 *  \page getting_started Getting Started with SAM3S8/SD8 Microcontrollers
 *
 *  \section Purpose
 *
 *  The Getting Started example will help new users get familiar with Atmel's
 *  SAM3S8/SD8 family of microcontrollers. This basic application shows the startup
 *  sequence of a chip and how to use its core peripherals.
 *
 *  \section Requirements
 *
 *  This package can be used with SAM3S evaluation kits 2.
 *
 *  \section Description
 *
 *  The demonstration program makes two LEDs on the board blink at a fixed rate.
 *  This rate is generated by using Time tick timer. The blinking can be stopped
 *  using two buttons (one for each LED).
 *
 *  \section 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/6421B.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="http://www.iar.com/website1/1.0.1.0/78/1/">
 *     IAR EWARM User and reference guides</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.
 *  -# Two LEDs should start blinking on the board. In the terminal window, the
 *     following text should appear (values depend on the board and chip used):
 *     \code
 *      -- Getting Started Example xxx --
 *      -- xxxxxx-xx
 *      -- Compiled: xxx xx xxxx xx:xx:xx --
 *     \endcode
 *  -# Pressing and release button 1 should make the first LED stop & restart
 *     blinking; pressing button 2 should make the other LED stop & restart
 *     blinking.
 *
 *  \section References
 *  - getting-started/main.c
 *  - pio.h
 *  - pio_it.h
 *  - led.h
 *  - trace.h
 */

/** \file
 *
 *  This file contains all the specific code for the getting-started example.
 *
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "board.h"

#include <stdbool.h>
#include <stdio.h>

/*----------------------------------------------------------------------------
 *        Local definitions
 *----------------------------------------------------------------------------*/

/** IRQ priority for PIO (The lower the value, the greater the priority) */
#define IRQ_PRIOR_PIO    0

/** LED0 blink time, LED1 blink half this time, in ms */
#define BLINK_PERIOD        1000

/*----------------------------------------------------------------------------
 *        Local variables
 *----------------------------------------------------------------------------*/

/** Pushbutton \#1 pin instance. */
const Pin pinPB1 = PIN_PUSHBUTTON_1 ;

/** Pushbutton \#1 pin instance. */
const Pin pinPB2 = PIN_PUSHBUTTON_2 ;

/** LED0 blinking control. */
volatile bool bLed0Active = true ;

/** LED1 blinking control. */
volatile bool bLed1Active = true ;

/** Global timestamp in milliseconds since start of application */
volatile uint32_t dwTimeStamp = 0;

/*----------------------------------------------------------------------------
 *        Local functions
 *----------------------------------------------------------------------------*/

/**
 *  \brief Process Buttons Events
 *
 *  Change active states of LEDs when corresponding button events happened.
 */
static void ProcessButtonEvt( uint8_t ucButton )
{
    if ( ucButton == 0 )
    {
        bLed0Active = !bLed0Active ;
        if ( !bLed0Active )
        {
            LED_Clear( 0 );
        }
    }
    else
    {
        bLed1Active = !bLed1Active ;

        /* Enable LED#2 and TC if they were disabled */
        if ( bLed1Active )
        {
            LED_Set( 1 );
            TC_Start( TC0, 0 );
        }
        /* Disable LED#2 and TC if they were enabled */
        else
        {
            LED_Clear( 1 );
            TC_Stop( TC0, 0 );
        }
    }
}

/**
 *  \brief Handler for Sytem Tick interrupt.
 *
 *  Process System Tick Event
 *  Increments the timestamp counter.
 */
void SysTick_Handler( void )
{
    dwTimeStamp ++;
}

/**
 *  \brief Handler for Button 1 rising edge interrupt.
 *
 *  Handle process led1 status change.
 */
static void _Button1_Handler( const Pin* pPin )
{
    if ( pPin == &pinPB1 )
    {
        ProcessButtonEvt( 0 ) ;
    }
}

/**
 *  \brief Handler for Button 2 falling edge interrupt.
 *
 *  Handle process led2 status change.
 */
static void _Button2_Handler( const Pin* pPin )
{
    if ( pPin == &pinPB2 )
    {
        ProcessButtonEvt( 1 ) ;
    }
}

/**
 *  \brief Configure the Pushbuttons
 *
 *  Configure the PIO as inputs and generate corresponding interrupt when
 *  pressed or released.
 */
static void _ConfigureButtons( void )
{
    /* Configure pios as inputs. */
    PIO_Configure( &pinPB1, 1 ) ;
    PIO_Configure( &pinPB2, 1 ) ;

    /* Adjust pio debounce filter patameters, uses 10 Hz filter. */
    PIO_SetDebounceFilter( &pinPB1, 10 ) ;
    PIO_SetDebounceFilter( &pinPB2, 10 ) ;

    /* Initialize pios interrupt handlers, see PIO definition in board.h. */
    PIO_ConfigureIt( &pinPB1, _Button1_Handler ) ; /* Interrupt on rising edge  */
    PIO_ConfigureIt( &pinPB2, _Button2_Handler ) ; /* Interrupt on rising edge */

    /* Enable PIO controller IRQs. */
    NVIC_EnableIRQ( (IRQn_Type)pinPB1.id ) ;
    NVIC_EnableIRQ( (IRQn_Type)pinPB2.id ) ;

    /* Enable PIO line interrupts. */
    PIO_EnableIt( &pinPB1 ) ;
    PIO_EnableIt( &pinPB2 ) ;
}

/**
 *  \brief Configure LEDs
 *
 *  Configures LEDs \#1 and \#2 (cleared by default).
 */
static void _ConfigureLeds( void )
{
    LED_Configure( 0 ) ;
    LED_Configure( 1 ) ;
}

/**
 *  Interrupt handler for TC0 interrupt. Toggles the state of LED\#2.
 */
void TC0_IrqHandler(void)
{
    volatile uint32_t dummy;
    /* Clear status bit to acknowledge interrupt */
    dummy = TC0->TC_CHANNEL[ 0 ].TC_SR;

    /** Toggle LED state. */
    LED_Toggle( 1 );
    printf( "2 " );
}

/**
 *  Configure Timer Counter 0 to generate an interrupt every 250ms.
 */
static void _ConfigureTc(void)
{
    uint32_t div;
    uint32_t tcclks;

    /** Enable peripheral clock. */
    PMC->PMC_PCER0 = 1 << ID_TC0;

    /** Configure TC for a 4Hz frequency and trigger on RC compare. */
    TC_FindMckDivisor( 4, BOARD_MCK, &div, &tcclks, BOARD_MCK );
    TC_Configure( TC0, 0, tcclks | TC_CMR_CPCTRG );
    TC0->TC_CHANNEL[ 0 ].TC_RC = ( BOARD_MCK / div ) / 4;

    /* Configure and enable interrupt on RC compare */
    NVIC_EnableIRQ( (IRQn_Type)ID_TC0 );
    TC0->TC_CHANNEL[ 0 ].TC_IER = TC_IER_CPCS;

    /** Start the counter if LED1 is enabled. */
    if ( bLed1Active ) {

        TC_Start( TC0, 0 );
    }
}

/**
 *  Waits for the given number of milliseconds (using the dwTimeStamp generated
 *  by the SAM3's microcontrollers's system tick).
 *  \param delay  Delay to wait for, in milliseconds.
 */
static void _Wait(unsigned long delay)
{
    volatile uint32_t start = dwTimeStamp;
    uint32_t elapsed;
    do {
        elapsed = dwTimeStamp;
        elapsed -= start;
    }
    while (elapsed < delay);
}

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

/**
 *  \brief getting-started Application entry point.
 *
 *  \return Unused (ANSI-C compatibility).
 */
extern int main( void )
{
    /* Disable watchdog */
    WDT_Disable( WDT ) ;

    /* Output example information */
    printf( "-- Getting Started Example %s --\n\r", SOFTPACK_VERSION ) ;
    printf( "-- %s\n\r", BOARD_NAME ) ;
    printf( "-- Compiled: %s %s --\n\r", __DATE__, __TIME__ ) ;

    /* Configure systick for 1 ms. */
    printf( "Configure system tick to get 1ms tick period.\n\r" ) ;
    if ( SysTick_Config( BOARD_MCK / 1000 ) )
    {
        printf("-F- Systick configuration error\n\r" ) ;
    }

    /* PIO configuration for LEDs and Buttons. */
    PIO_InitializeInterrupts( IRQ_PRIOR_PIO ) ;

    printf( "Configure TC.\n\r" );
    _ConfigureTc() ;

    printf( "Configure LED PIOs.\n\r" ) ;
    _ConfigureLeds() ;

    printf( "Configure buttons with debouncing.\n\r" ) ;
    _ConfigureButtons() ;

    printf( "Press USRBP1 to Start/Stop the blue LED D2 blinking.\n\r" ) ;
    printf( "Press USRBP2 to Start/Stop the green LED D3 blinking.\n\r" ) ;

    while ( 1 )
    {
        /* Wait for LED to be active */
        while( !bLed0Active );

        /* Toggle LED state if active */
        if ( bLed0Active )
        {
            LED_Toggle( 0 );
            printf( "1 " );
        }

        /* Wait for 500ms */
        _Wait(500);
    }
}

