/* This source file is part of the ATMEL AVR32-SoftwareFramework-AT32AP7000-1.0.0 Release */

/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief COUNT & COMPARE usage example.
 *
 * Example of COUNT & COMPARE registers usage, using the USART software driver
 * (for printing ASCII msgs), the GPIO software driver (to map the USART on I/O pins),
 * the INTC software driver (for interrupt management).
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices.
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 *****************************************************************************/

/*! \page License
 * Copyright (C) 2006-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:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of ATMEL may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
 * SPECIFICALLY 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.
 */

/*! \mainpage
 * \section intro Introduction
 *
 * This documents gives an example of the usage of the CPU Cycle counter. The cycle counter is a COUNT register,
 * that increments once every clock. The count register can be used together with
 * the COMPARE register to create a timer with interrupt functionality.
 * The COMPARE register holds a value that the COUNT register is compared against.
 * When the COMPARE and COUNT registers match, a compare interrupt request is
 * generated. For all devices except the AT32AP700x COUNT is reset to 0 on COMPARE match.
 *
 * \section example-description Example's Operating Mode
 * This example shows how to use the COUNT register together with the COMPARE register
 * to generate an interrupt periodically. Here is the operating mode of the example:
 * - At the beginning of the code, we check that initial default values of the COUNT
 * and COMPARE registers are correct.
 * - Then, the COUNT & COMPARE interrupt mechanism is tested with a short delay. Messages
 * are displayed on USART1. This delay is equal to (1/fCPU)*NB_CLOCK_CYCLE_DELAY_SHORT
 * in case NB_CLOCK_CYCLE_DELAY_SHORT value is reloaded (83.3ms) or (1/fCPU)*NB_CLOCK_CYCLE_DELAY_LONG
 * in case NB_CLOCK_CYCLE_DELAY_LONG value is reloaded (1.67s)
 * - Then the program infinitly loops, using the COUNT & COMPARE interrupt mechanism
 * with a longer delay. Messages are displayed on USART1 and one of Led 1 through Led4
 * is on upon each COUNT & COMPARE match (Led1 -> Led2 -> Led3 -> Led4 -> Led1 ...and so on).
 *
  \section files Main Files
 * - cycle_counter_example.c : cycle counter example
 * - cycle_counter.h: cycle counter driver interface
 *
 * \section compinfo Compilation Info
 * This software was written for the GNU GCC for AVR32 and IAR Systems compiler
 * for AVR32. Other compilers may or may not work.
 *
 * \section deviceinfo Device Info
 * All AVR32 devices. This example has been tested with the following setup:<BR>
 * <ul>
 *  <li>STK1000 development board
 *  </ul>
 *
 * \section setupinfo Setup Information
 * CPU speed: <i>20 Mhz. </i>
 *
 * \section configinfo Configuration Information
 * This example has been tested with the following configuration:
 * - USART1 connected to a PC serial port via a standard RS232 DB9 cable;
 * - PC terminal settings:
 *   - 115200 bps,
 *   - 8 data bits,
 *   - no parity bit,
 *   - 1 stop bit,
 *   - no flow control.
 *
 * \section contactinfo Contact Information
 * For further information, visit
 * <A href="http://www.atmel.com/products/AVR32/">Atmel AVR32</A>.\n
 * Support and FAQ: http://support.atmel.no/
 */


#if __GNUC__
#  include "intc.h"
#endif

#define _ASSERT_ENABLE_
#include "compiler.h"
#include "print_funcs.h"
#include "board.h"
#include "cycle_counter.h"
#include "pm_at32ap7000.h"


#define NB_CLOCK_CYCLE_DELAY_SHORT    1000000   // 100 ms if fCPU==20MHz
#define NB_CLOCK_CYCLE_DELAY_LONG    20000000   // 1 s if fCPU==20MHz


// Counter of COUNT/COMPARE matches.
static volatile unsigned int u32NbCompareIrqTrigger = 0;

// COUNT/COMPARE match interrupt handler and main function synchronizer.
static volatile unsigned char u8DisplayMsg = 0;

// COUNT/COMPARE match interrupt handler
#if __GNUC__
// GCC-specific syntax to declare an interrupt handler. The interrupt handler
// registration is done in the main function using the INTC software driver module.
__attribute__((__interrupt__))
#elif __ICCAVR32__
// IAR-specific syntax to declare and register an interrupt handler.
// Register to the interrupt group 0(cf Section "Interrupt Request Signal Map"
// in the datasheet) with interrupt level 0.
#pragma handler = AVR32_CORE_IRQ_GROUP, 0
__interrupt
#endif
static void compare_irq_handler(void)
{
  // Count the number of times this IRQ handler is called.
  u32NbCompareIrqTrigger++;
  u8DisplayMsg = 1; // Inform the main program that it may display a msg saying
                    // that the COUNT&COMPARE interrupt occurred.
  // Clear the pending interrupt(writing a value to the COMPARE register clears
  // any pending compare interrupt requests). Schedule the COUNT&COMPARE match
  // interrupt to happen every NB_CLOCK_CYCLE_DELAY_LONG cycles.
#if __AVR32_AP7000__ || __AT32AP7000__
  U32 next_compare;

  // AP7000 don't reset COUNT on compare match. We need to offset next COMPARE.
  next_compare = Get_sys_compare();
  next_compare += NB_CLOCK_CYCLE_DELAY_LONG;
  if (next_compare == 0) // Avoid disabling compare
	next_compare++;
  Set_sys_compare(next_compare);
#else
  Set_sys_compare(NB_CLOCK_CYCLE_DELAY_LONG);
#endif
}

/* Main function */
int main(void)
{
   U32 u32CompareVal;
   U32 u32CompareValVerif;
   U32 u32CountVal;
   U32 u32CountNextVal;
   U8  u8LedMap = 0x01;

   // Reset PM. Makes sure we get the expected clocking after a soft reset (e.g.: JTAG reset)
   pm_reset();

   // Switch the main clock to OSC0
   //  pm_switch_to_osc0(pm, FOSC0, OSC0_STARTUP);
   // Init DEBUG module
   init_dbg_rs232(FOSC0);

   print_dbg("---------------------------------------------\n");

   // Read COMPARE register.
   // NOTE: it should be equal to 0 (default value upon reset) => The compare
   // and exception generation feature is thus currently disabled.
   u32CompareVal = Get_sys_compare();
   Assert(!u32CompareVal);

   // Read COUNT register.
   // NOTE: the COUNT register increments since reset => it should be != 0.
   u32CountVal = Get_sys_count();
   Assert(u32CountVal);

#if __GNUC__
   // Disable all interrupts.
   Disable_global_interrupt();

   INTC_init_interrupts();

   // Register the compare interrupt handler to the interrupt controller.
   // compare_irq_handler is the interrupt handler to register.
   // AVR32_CORE_COMPARE_IRQ is the IRQ of the interrupt handler to register.
   // AVR32_INTC_INT0 is the interrupt priority level to assign to the group of this IRQ.
   // void INTC_register_interrupt(__int_handler handler, unsigned int irq, unsigned int int_lev);
   INTC_register_interrupt(&compare_irq_handler, AVR32_CORE_COMPARE_IRQ, AVR32_INTC_INT0);
#endif
   // Enable all interrupts.
   Enable_global_interrupt();

   // Schedule the COUNT&COMPARE match interrupt in NB_CLOCK_CYCLE_DELAY_SHORT 
   // clock cycles from now.
   u32CountVal = Get_sys_count();

   u32CompareVal = u32CountVal + NB_CLOCK_CYCLE_DELAY_SHORT; // WARNING: MUST FIT IN 32bits.
   // If u32CompareVal ends up to be 0, make it 1 so that the COMPARE and exception
   // generation feature does not get disabled.
   if(0 == u32CompareVal)
   {
      u32CompareVal++;
   }

   Set_sys_compare(u32CompareVal); // GO

   // Check if the previous write in the COMPARE register succeeded.
   u32CompareValVerif = Get_sys_compare();
   Assert( u32CompareVal==u32CompareValVerif );

   //  The previous COMPARE write succeeded.
   // Loop until the COUNT&COMPARE match triggers.
   while (!u32NbCompareIrqTrigger)
   {
      u32CountNextVal = Get_sys_count();

      if (u32CountNextVal < u32CompareVal)
         print_dbg("COUNT HAS NOT REACHED COMPARE YET (INFO)\n");
      else if (u32CountNextVal > u32CompareVal)
         // This should never happen if COMPARE is not zero.
         print_dbg("COUNT IS GREATER THAN COMPARE (INFO)\n");
      else
         print_dbg("COUNT IS EQUAL TO COMPARE (INFO)\n");
      // NOTE: since the COUNT register is reset to zero upon COUNT/COMPARE match,
      // the printed messages here are not "accurate".
   }

   while (TRUE)
   {
      if (u8DisplayMsg)
      {
         u8DisplayMsg = 0; // Reset

         // Turn the current LED on only and move to next LED.
         LED_Display_Field(LED_MONO0_GREEN |
                           LED_MONO1_GREEN |
                           LED_MONO2_GREEN |
                           LED_MONO3_GREEN,
                           u8LedMap);
         u8LedMap = max((U8)(u8LedMap << 1) & 0x0F, 0x01);

         // Print some info on the debug port.
         print_dbg("\nCOMPARE INTERRUPT TRIGGERED (OK): #");
         print_dbg_ulong(u32NbCompareIrqTrigger);
      }
   }
}
