/**
 * \file
 *
 * \brief AVR XMEGA Timer Counter (TC) driver
 *
 * Copyright (c) 2010-2013 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * 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.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * 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
 * 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.
 *
 * \asf_license_stop
 *
 */
#ifndef _TC_H_
#define _TC_H_

#include <compiler.h>
#include <parts.h>
#include "status_codes.h"
#include "pmic.h"
#include <sleepmgr.h>
#include <sysclk.h>


#ifdef __cplusplus
extern "C"
{
#endif



/**
 * \defgroup tc_group Timer Counter (TC)
 *
 * See \ref xmega_tc_quickstart
 *
 * This is a driver for the AVR XMEGA Timer Counter (TC). It provides functions
 * for enabling, disabling and configuring the TC modules.
 *
 * \section dependencies Dependencies
 * This driver depends on the following modules:
 * - \ref sysclk_group for peripheral clock control.
 * - \ref sleepmgr_group for setting allowed sleep mode.
 * - \ref interrupt_group for ISR definition and disabling interrupts during
 * critical code sections.
 * @{
 */



/**
 * \brief Interrupt event callback function type
 *
 * The interrupt handler can be configured to do a function callback,
 * the callback function must match the tc_callback_t type.
 *
 */
  typedef void (*tc_callback_t) (void);

//! Timer Counter Capture Compare Channel index
  enum tc_cc_channel_t
  {
    //! Channel A
    TC_CCA = 1,
    //! Channel B
    TC_CCB = 2,
    //! Channel C
    TC_CCC = 3,
    //! Channel D
    TC_CCD = 4,
  };

//! Timer Counter Capture Compare Channel index
  enum tc_cc_channel_mask_enable_t
  {
    //! Channel A Enable mask
    TC_CCAEN = TC0_CCAEN_bm,
    //! Channel B Enable mask
    TC_CCBEN = TC0_CCBEN_bm,
    //! Channel C Enable mask
    TC_CCCEN = TC0_CCCEN_bm,
    //! Channel D Enable mask
    TC_CCDEN = TC0_CCDEN_bm,
  };

//! Timer Counter Direction
  enum tc_dir_t
  {
    //! Counting up
    TC_UP = 0,
    //! Down Counting B
    TC_DOWN = 1
  };
//! Timer Counter Waveform Generator mode
  enum tc_wg_mode_t
  {
    //! TC in normal Mode
    TC_WG_NORMAL = TC_WGMODE_NORMAL_gc,
    //! TC in Frequency Generator mode
    TC_WG_FRQ = TC_WGMODE_FRQ_gc,
    //! TC in single slope PWM mode
    TC_WG_SS = TC_WGMODE_SS_gc,
    //! TC in dual slope Top PWM mode
    TC_WG_DS_T = TC_WGMODE_DS_T_gc,
    //! TC in dual slope Top Bottom PWM mode
    TC_WG_DS_TB = TC_WGMODE_DS_TB_gc,
    //! TC in dual slope Bottom PWM mode
    TC_WG_DS_B = TC_WGMODE_DS_B_gc
  };

//! TC interrupt levels
  enum TC_INT_LEVEL_t
  {
    TC_INT_LVL_OFF = 0x00,
    TC_INT_LVL_LO = 0x01,
    TC_INT_LVL_MED = 0x02,
    TC_INT_LVL_HI = 0x03,
  };

//! Macro to check if type of passed TC is TC1_t
#define tc_is_tc1(void) ((uint16_t)tc&0x40 ? true : false)
//! Macro to check if type of passed TC is TC0_t
#define tc_is_tc0(void) ((uint16_t)tc&0x40 ? false : true)

/**
 * \brief Enable TC
 *
 * Enables the TC.
 *
 * \param tc Pointer to TC module
 *
 * \note
 * unmask TC clock (sysclk), but does not configure the TC clock source.
 */
  void tc_enable (volatile void *tc);

/**
 * \brief Disable TC
 *
 * Disables the TC.
 *
 * \param tc Pointer to TC module
 *
 * \note
 * mask TC clock (sysclk).
 */
  void tc_disable (volatile void *tc);

/**
 * \ingroup tc_group
 * \defgroup tc_interrupt_group Timer Counter (TC) interrupt management
 * This group provides functions to configure TC module interrupts
 *
 *
 * @{
 */
/**
 * \brief Set TC overflow interrupt callback function
 *
 * This function allows the caller to set and change the interrupt callback
 * function. Without setting a callback function the interrupt handler in the
 * driver will only clear the interrupt flags.
 *
 * \param tc Pointer to the Timer Counter (TC) base address
 * \param callback Reference to a callback function
 */
  void tc_set_overflow_interrupt_callback (volatile void *tc,
					   tc_callback_t callback);

/**
 * \brief Set TC error interrupt callback function
 *
 * This function allows the caller to set and change the interrupt callback
 * function. Without setting a callback function the interrupt handler in the
 * driver will only clear the interrupt flags.
 *
 * \param tc Pointer to the Timer Counter (TC) base address
 * \param callback Reference to a callback function
 */
  void tc_set_error_interrupt_callback (volatile void *tc,
					tc_callback_t callback);

/**
 * \brief Set TC Capture Compare Channel A interrupt callback function
 *
 * This function allows the caller to set and change the interrupt callback
 * function. Without setting a callback function the interrupt handler in the
 * driver will only clear the interrupt flags.
 *
 * \param tc Pointer to the Timer Counter (TC) base address
 * \param callback Reference to a callback function
 */
  void tc_set_cca_interrupt_callback (volatile void *tc,
				      tc_callback_t callback);

/**
 * \brief Set TC Capture Compare Channel B interrupt callback function
 *
 * This function allows the caller to set and change the interrupt callback
 * function. Without setting a callback function the interrupt handler in the
 * driver will only clear the interrupt flags.
 *
 * \param tc Pointer to the Timer Counter (TC) base address
 * \param callback Reference to a callback function
 */
  void tc_set_ccb_interrupt_callback (volatile void *tc,
				      tc_callback_t callback);

/**
 * \brief Set TC Capture Compare Channel C interrupt callback function
 *
 * This function allows the caller to set and change the interrupt callback
 * function. Without setting a callback function the interrupt handler in the
 * driver will only clear the interrupt flags.
 *
 * \param tc Pointer to the Timer Counter (TC) base address
 * \param callback Reference to a callback function
 */
  void tc_set_ccc_interrupt_callback (volatile void *tc,
				      tc_callback_t callback);

/**
 * \brief Set TC Capture Compare Channel D interrupt callback function
 *
 * This function allows the caller to set and change the interrupt callback
 * function. Without setting a callback function the interrupt handler in the
 * driver will only clear the interrupt flags.
 *
 * \param tc Pointer to the Timer Counter (TC) base address
 * \param callback Reference to a callback function
 */
  void tc_set_ccd_interrupt_callback (volatile void *tc,
				      tc_callback_t callback);
/**
 * \brief Configures TC overflow Interrupt level
 *
 * \param tc Pointer to TC module.
 * \param level Overflow interrupt level
 * \note  Configures OVFINTLVL in INTCTRLA
 */
  static inline void tc_set_overflow_interrupt_level (volatile void *tc,
						      enum TC_INT_LEVEL_t
						      level)
  {
    ((TC0_t *) tc)->INTCTRLA = ((TC0_t *) tc)->INTCTRLA & ~TC0_OVFINTLVL_gm;
    ((TC0_t *) tc)->INTCTRLA =
      ((TC0_t *) tc)->INTCTRLA | (level << TC0_OVFINTLVL_gp);
  }

/**
 * \brief Configures TC error Interrupt level
 *
 * \param tc Pointer to TC module.
 * \param level Error interrupt level
 * \note  Configures ERRINTLVL in INTCTRLA
 */
  static inline void tc_set_error_interrupt_level (volatile void *tc,
						   enum TC_INT_LEVEL_t level)
  {
    ((TC0_t *) tc)->INTCTRLA = ((TC0_t *) tc)->INTCTRLA & ~TC0_ERRINTLVL_gm;
    ((TC0_t *) tc)->INTCTRLA =
      ((TC0_t *) tc)->INTCTRLA | (level << TC0_ERRINTLVL_gp);
  }

/**
 * \brief Configures TC Capture Compare A Interrupt level
 *
 * \param tc Pointer to TC module.
 * \param level CCA interrupt level
 * \note Configures CCAINTLVL in INTCTRLB
 */
  static inline void tc_set_cca_interrupt_level (volatile void *tc,
						 enum TC_INT_LEVEL_t level)
  {
    ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCAINTLVL_gm;
    ((TC0_t *) tc)->INTCTRLB =
      ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCAINTLVL_gp);
  }

/**
 * \brief Configures TC Capture Compare B Interrupt level
 *
 * \param tc Pointer to TC module.
 * \param level CCB interrupt level
 * \note Configures CCBINTLVL in INTCTRLB
 */
  static inline void tc_set_ccb_interrupt_level (volatile void *tc,
						 enum TC_INT_LEVEL_t level)
  {
    ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCBINTLVL_gm;
    ((TC0_t *) tc)->INTCTRLB =
      ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCBINTLVL_gp);
  }

/**
 * \brief Configures TC Capture Compare C Interrupt level
 *
 * \param tc Pointer to TC module.
 * \param level CCC interrupt level
 * \note Configures CCCINTLVL in INTCTRLB
 */
  static inline void tc_set_ccc_interrupt_level (volatile void *tc,
						 enum TC_INT_LEVEL_t level)
  {
    ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCCINTLVL_gm;
    ((TC0_t *) tc)->INTCTRLB =
      ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCCINTLVL_gp);
  }

  /**
 * \brief Configures TC Capture Compare D Interrupt level
 *
 * \param tc Pointer to TC module.
 * \param level CCD interrupt level
 * \note Configures CCDINTLVL in INTCTRLB
 */
  static inline void tc_set_ccd_interrupt_level (volatile void *tc,
						 enum TC_INT_LEVEL_t level)
  {
    ((TC0_t *) tc)->INTCTRLB = ((TC0_t *) tc)->INTCTRLB & ~TC0_CCDINTLVL_gm;
    ((TC0_t *) tc)->INTCTRLB =
      ((TC0_t *) tc)->INTCTRLB | (level << TC0_CCDINTLVL_gp);
  }

//@}

/**
 * \brief Configure Timer Clock Source
 *
 * \param tc Pointer to TC module.
 * \param TC_CLKSEL_enum Clock source selection
 * \note Configuring the clock also starts the timer
 */
  static inline void tc_write_clock_source (volatile void *tc,
					    TC_CLKSEL_t TC_CLKSEL_enum)
  {
    ((TC0_t *) tc)->CTRLA =
      (((TC0_t *) tc)->CTRLA & ~TC0_CLKSEL_gm) | TC_CLKSEL_enum;
  }

/**
 * \brief Read Timer Clock Source
 *
 * \param tc Pointer to TC module.
 * \return TC_CLKSEL_enum Clock source selection
 */
  static inline TC_CLKSEL_t tc_read_clock_source (volatile void *tc)
  {
    return (TC_CLKSEL_t) (((TC0_t *) tc)->CTRLA & TC0_CLKSEL_gm);
  }

/**
 * \brief Select clock for a specified TC and resolution.
 *
 * This function configures the clock selection, as prescaled CLKper, for a
 * specified TC that gives a resolution at least as high as the one specified.
 * The resolution of a TC is synonymous with its clock frequency.
 *
 * \note It is also possible to clock TCs with event channels. This is not
 * handled by this implementation.
 *
 * \param tc   ID of TC to get clock selection for.
 * \param resolution Desired resolution for the TC in Hz.
 */
  static inline void tc_set_resolution (volatile void *tc,
					uint32_t resolution)
  {
    uint32_t tc_clk_rate = sysclk_get_per_hz ();

    if (resolution <= (tc_clk_rate / 1024))
      {
	tc_write_clock_source (tc, TC_CLKSEL_DIV1024_gc);
      }
    else if (resolution <= (tc_clk_rate / 256))
      {
	tc_write_clock_source (tc, TC_CLKSEL_DIV256_gc);
      }
    else if (resolution <= (tc_clk_rate / 64))
      {
	tc_write_clock_source (tc, TC_CLKSEL_DIV64_gc);
      }
    else if (resolution <= (tc_clk_rate / 8))
      {
	tc_write_clock_source (tc, TC_CLKSEL_DIV8_gc);
      }
    else if (resolution <= (tc_clk_rate / 4))
      {
	tc_write_clock_source (tc, TC_CLKSEL_DIV4_gc);
      }
    else if (resolution <= (tc_clk_rate / 2))
      {
	tc_write_clock_source (tc, TC_CLKSEL_DIV2_gc);
      }
    else
      {
	tc_write_clock_source (tc, TC_CLKSEL_DIV1_gc);
      }
  }

/**
 * \brief Get real resolution for a specified TC.
 *
 * This function returns the resolution which the specified clock selection
 * of TC will result in. The resolution of a TC is synonymous with its clock
 * frequency.
 *
 * \note This function does not handle event channel clock selections.
 *
 * \param tc Pointer of TC module to get resolution for.
 *
 * \return The resolution of \a tc.
 */
  static inline uint32_t tc_get_resolution (volatile void *tc)
  {
    uint32_t tc_clk_rate = sysclk_get_per_hz ();
    switch (tc_read_clock_source (tc))
      {
      case TC_CLKSEL_OFF_gc:
	tc_clk_rate = 0;
	break;

      case TC_CLKSEL_DIV1024_gc:
	tc_clk_rate /= 1024;
	break;

      case TC_CLKSEL_DIV256_gc:
	tc_clk_rate /= 256;
	break;

      case TC_CLKSEL_DIV64_gc:
	tc_clk_rate /= 64;
	break;

      case TC_CLKSEL_DIV8_gc:
	tc_clk_rate /= 8;
	break;

      case TC_CLKSEL_DIV4_gc:
	tc_clk_rate /= 4;
	break;

      case TC_CLKSEL_DIV2_gc:
	tc_clk_rate /= 2;
	break;

      case TC_CLKSEL_DIV1_gc:
	break;

      default:
	tc_clk_rate = 0;
	break;
      }
    return (tc_clk_rate);
  }

/**
 * \brief Configure Timer Direction
 *
 * \param tc Pointer to TC module.
 * \param dir Timer direction :
 */
  static inline void tc_set_direction (volatile void *tc, enum tc_dir_t dir)
  {
    if (dir == TC_UP)
      {
	((TC0_t *) tc)->CTRLFCLR |= ~TC0_DIR_bm;
      }
    else
      {
	((TC0_t *) tc)->CTRLFSET |= TC0_DIR_bm;
      }
  }

/**
 * \brief Write the Counter value of the Timer
 *
 * \param tc Pointer to TC module.
 * \param cnt_value Counter value :
 */
  static inline void tc_write_count (volatile void *tc, uint16_t cnt_value)
  {
    ((TC0_t *) tc)->CNT = cnt_value;
  }

/**
 * \brief Reads the Counter value of the Timer
 *
 * \param tc Pointer to TC module.
 * \note Output the Counter value CNT
 */
  static inline uint16_t tc_read_count (volatile void *tc)
  {
    return (((TC0_t *) tc)->CNT);
  }

/**
 * \brief Writes the Period value of the Timer
 *
 * \param tc Pointer to TC module.
 * \param per_value Period value : PER
 */
  static inline void tc_write_period (volatile void *tc, uint16_t per_value)
  {
    ((TC0_t *) tc)->PER = per_value;
  }

/**
 * \brief Reads the Period value of the Timer
 *
 * \param tc Pointer to TC module.
 * \return Period value : PER
 */
  static inline uint16_t tc_read_period (volatile void *tc)
  {
    return (((TC0_t *) tc)->PER);
  }

/**
 * \brief Writes the Period Buffer value of the Timer
 *
 * \param tc Pointer to TC module.
 * \param per_buf Period Buffer value : PERH/PERL
 */
  static inline void tc_write_period_buffer (volatile void *tc,
					     uint16_t per_buf)
  {
    ((TC0_t *) tc)->PERBUF = per_buf;
  }

/**
 * \brief Reads the Period Buffer value of the Timer
 *
 * \param tc Pointer to TC module.
 * \return Period Buffer value : PERH/PERL
 */
  static inline uint16_t tc_read_period_buffer (volatile void *tc)
  {
    return (((TC0_t *) tc)->PERBUF);
  }

/**
 * \brief Tests if the Period Buffer is valid
 *
 * \param tc Pointer to TC module.
 * \return  period Buffer is valid or not:PERBV
 */
  static inline bool tc_period_buffer_is_valid (volatile void *tc)
  {
    return (((TC0_t *) tc)->CTRLGCLR & TC0_PERBV_bm);
  }

/**
 * \brief Enables delay (used for 32bit timer mode)
 *
 * \param tc Pointer to TC module.
 * \note  enables Delay mode
 */
  static inline void tc_enable_delay (volatile void *tc)
  {
    ((TC0_t *) tc)->CTRLD = (((TC0_t *) tc)->CTRLD &
			     ~TC0_EVDLY_bm) | (1 << TC0_EVDLY_bp);
  }

/**
 * \brief Disables delay
 *
 * \param tc Pointer to TC module.
 * \note  disables Delay mode
 */
  static inline void tc_disable_delay (volatile void *tc)
  {
    ((TC0_t *) tc)->CTRLD = ((TC0_t *) tc)->CTRLD & ~TC0_EVDLY_bm;
  }

/**
 * \brief Tests if the Overflow flag is set
 *
 * \param tc Pointer to TC module.
 * \return  overflow has occurred or not : OVFIF
 */
  static inline bool tc_is_overflow (volatile void *tc)
  {
    return (((TC0_t *) tc)->INTFLAGS & TC0_OVFIF_bm);
  }

/**
 * \brief Clears the Overflow flag
 *
 * \param tc Pointer to TC module.
 * \note  OVFIF is cleared
 */
  static inline void tc_clear_overflow (volatile void *tc)
  {
    ((TC0_t *) tc)->INTFLAGS |= TC0_OVFIF_bm;
  }

/**
 * \brief Tests if the Error flag is set
 *
 * \param tc Pointer to TC module.
 * \return  Error has occurred or not : ERRIF
 */
  static inline bool tc_read_error (volatile void *tc)
  {
    return (((TC0_t *) tc)->INTFLAGS & TC0_ERRIF_bm);
  }

/**
 * \brief Clears the Error flag
 *
 * \param tc Pointer to TC module.
 * \note  ERRIF is cleared
 */
  static inline void tc_clear_error (volatile void *tc)
  {
    ((TC0_t *) tc)->INTFLAGS |= TC0_ERRIF_bm;
  }

/**
 * \brief Restart the Timer
 *
 * \param tc Pointer to TC module.
 * \note  CMD[3] in CTRLFSET is set to 1 and CMD[2] in CTRLFCLR is set
 */
  static inline void tc_restart (volatile void *tc)
  {
    ((TC0_t *) tc)->CTRLFSET = TC_CMD_RESTART_gc;
  }

/**
 * \brief Reset the Timer
 *
 * \param tc Pointer to TC module.
 * \note  CMD[3:2] in CTRLFSET are set to 1
 */
  static inline void tc_reset (volatile void *tc)
  {
    ((TC0_t *) tc)->CTRLFSET = TC_CMD_RESET_gc;
  }

/**
 * \brief Update the Timer
 *
 * \param tc Pointer to TC module.
 * \note  CMD[2] in CTRLFSET is set to 1 and CMD[3] in CTRLFCLR is set
 */
  static inline void tc_update (volatile void *tc)
  {
    ((TC0_t *) tc)->CTRLFSET = TC_CMD_UPDATE_gc;
  }

/**
 * \brief Configures the Timer in Byte mode
 *
 * \param tc Pointer to TC module.
 * \note Configures  BYTEM in CTRLE
 */
  static inline void tc_set_8bits_mode (volatile void *tc)
  {
#ifdef TC0_BYTEM0_bm
    ((TC0_t *) tc)->CTRLE |= TC0_BYTEM0_bm;
#else
    ((TC0_t *) tc)->CTRLE |= TC0_BYTEM_bm;
#endif
  }

/**
 * \brief Locks the Update of the Buffered registers
 *
 * \param tc Pointer to TC module.
 *
 *  */
  static inline void tc_lock_update_buffers (volatile void *tc)
  {
    ((TC0_t *) tc)->CTRLFSET |= TC0_LUPD_bm;
  }

/**
 * \brief Unlocks the Update of the Buffered registers
 *
 * \param tc Pointer to TC module.
 * \note  Configures LUPD in CTRLFCLR
 */
  static inline void tc_unlock_update_buffers (volatile void *tc)
  {
    ((TC0_t *) tc)->CTRLFCLR |= TC0_LUPD_bm;
  }

/**
 * \brief Enables Compare/Capture channel
 *
 * \param tc Pointer to TC module.
 * \param enablemask CC channel
 */
  static inline void tc_enable_cc_channels (volatile void *tc,
					    enum tc_cc_channel_mask_enable_t
					    enablemask)
  {
    if (tc_is_tc0 (void *tc))
      {
	((TC0_t *) tc)->CTRLB |= enablemask;
      }
    else if (tc_is_tc1 (void *tc))
      {
	((TC1_t *) tc)->CTRLB |= enablemask & (TC1_CCAEN_bm | TC1_CCBEN_bm);
      }
  }

/**
 * \brief Disables Compare/Capture channel
 *
 * \param tc Pointer to TC module.
 * \param disablemask CC channel
 */
  static inline void tc_disable_cc_channels (volatile void *tc,
					     enum tc_cc_channel_mask_enable_t
					     disablemask)
  {
    if (tc_is_tc0 (void *tc))
      {
	((TC0_t *) tc)->CTRLB &= ~disablemask;
      }
    else if (tc_is_tc1 (void *tc))
      {
	((TC1_t *) tc)->CTRLB &= ~(disablemask & TC0_CCAEN_bm & TC0_CCBEN_bm);
      }
  }

/**
 * \brief Enables Input capture mode
 *
 * \param tc Pointer to TC module.
 * \param eventsource Source for the capture
 * \param eventaction Event action capture type
 */
  static inline void tc_set_input_capture (volatile void *tc,
					   TC_EVSEL_t eventsource,
					   TC_EVACT_t eventaction)
  {
    ((TC0_t *) tc)->CTRLD &= ~(TC0_EVSEL_gm | TC0_EVACT_gm);
    ((TC0_t *) tc)->CTRLD |= ((uint8_t) eventsource | (uint8_t) eventaction);
  }

/**
 * \brief Reads the Capture value
 *
 * \param tc Pointer to TC module.
 * \param channel_index Channel x
 * \return  Read value of CCx
 */
  static inline uint16_t tc_read_cc (volatile void *tc,
				     enum tc_cc_channel_t channel_index)
  {
    if (tc_is_tc0 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return (((TC0_t *) tc)->CCA);
	  case TC_CCB:
	    return (((TC0_t *) tc)->CCB);
	  case TC_CCC:
	    return (((TC0_t *) tc)->CCC);
	  case TC_CCD:
	    return (((TC0_t *) tc)->CCD);
	  }
      }
    else if (tc_is_tc1 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return (((TC1_t *) tc)->CCA);
	  case TC_CCB:
	    return (((TC1_t *) tc)->CCB);
	  default:
	    return (0);
	  }
      }
    return (0);
  }

/**
 * \brief Writes the CC value
 *
 * \param tc Pointer to TC module.
 * \param channel_index CC Channel
 * \param value Counter value
 */
  static inline void tc_write_cc (volatile void *tc,
				  enum tc_cc_channel_t channel_index,
				  uint16_t value)
  {
    if (tc_is_tc0 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    ((TC0_t *) tc)->CCA = value;
	    break;
	  case TC_CCB:
	    ((TC0_t *) tc)->CCB = value;
	    break;
	  case TC_CCC:
	    ((TC0_t *) tc)->CCC = value;
	    break;
	  case TC_CCD:
	    ((TC0_t *) tc)->CCD = value;
	    break;
	  }
      }
    else if (tc_is_tc1 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    ((TC1_t *) tc)->CCA = value;
	    break;
	  case TC_CCB:
	    ((TC1_t *) tc)->CCB = value;
	    break;
	  default:
	    return;
	  }
      }
  }

/**
 * \brief Writes the Capture/Compare Buffer value
 *
 * \param tc Pointer to TC module.
 * \param channel_index CC Channel
 * \param buffer_value Counter Buffer value
 */
  static inline void tc_write_cc_buffer (volatile void *tc,
					 enum tc_cc_channel_t channel_index,
					 uint16_t buffer_value)
  {
    if (tc_is_tc0 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    ((TC0_t *) tc)->CCABUF = buffer_value;
	    break;
	  case TC_CCB:
	    ((TC0_t *) tc)->CCBBUF = buffer_value;
	    break;
	  case TC_CCC:
	    ((TC0_t *) tc)->CCCBUF = buffer_value;
	    break;
	  case TC_CCD:
	    ((TC0_t *) tc)->CCDBUF = buffer_value;
	    break;
	  }
      }
    else if (tc_is_tc1 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    ((TC1_t *) tc)->CCABUF = buffer_value;
	    break;
	  case TC_CCB:
	    ((TC1_t *) tc)->CCBBUF = buffer_value;
	    break;
	  default:
	    return;
	  }
      }
  }

/**
 * \brief Reads the Capture/Compare Buffer value
 *
 * \param tc Pointer to TC module.
 * \param channel_index CC Channel
 * \return  CCx Buffer value
 */
  static inline uint16_t tc_read_cc_buffer (volatile void *tc,
					    enum tc_cc_channel_t
					    channel_index)
  {
    if (tc_is_tc0 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return (((TC0_t *) tc)->CCABUF);
	  case TC_CCB:
	    return (((TC0_t *) tc)->CCBBUF);
	  case TC_CCC:
	    return (((TC0_t *) tc)->CCCBUF);
	  case TC_CCD:
	    return (((TC0_t *) tc)->CCDBUF);
	  }
      }
    else if (tc_is_tc1 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return (((TC1_t *) tc)->CCABUF);
	  case TC_CCB:
	    return (((TC1_t *) tc)->CCBBUF);
	  default:
	    return (0);
	  }
      }
    return (0);
  }

/**
 * \brief Reports is Capture/Compare Buffer is valid
 *
 * \param tc Pointer to TC module.
 * \param channel_index CC Channel
 * \return  CCx Buffer is valid or not
 */
  static inline bool tc_cc_buffer_is_valid (volatile void *tc,
					    enum tc_cc_channel_t
					    channel_index)
  {
    if (tc_is_tc0 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return ((TC0_t *) tc)->CTRLGCLR & TC0_CCABV_bm;
	  case TC_CCB:
	    return ((TC0_t *) tc)->CTRLGCLR & TC0_CCBBV_bm;
	  case TC_CCC:
	    return ((TC0_t *) tc)->CTRLGCLR & TC0_CCCBV_bm;
	  case TC_CCD:
	    return ((TC0_t *) tc)->CTRLGCLR & TC0_CCDBV_bm;
	  }
      }
    else if (tc_is_tc1 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return (((TC1_t *) tc)->CTRLGCLR & TC1_CCABV_bm);
	  case TC_CCB:
	    return (((TC1_t *) tc)->CTRLGCLR & TC1_CCBBV_bm);
	  default:
	    return (0);
	  }
      }
    return (0);
  }

/**
 * \brief Reports if Capture/Compare interrupt has occurred
 *
 * \param tc Pointer to TC module.
 * \param channel_index CC Channel
 * \return  CCx Interrupt or not
 */
  static inline bool tc_is_cc_interrupt (volatile void *tc,
					 enum tc_cc_channel_t channel_index)
  {
    if (tc_is_tc0 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return (((TC0_t *) tc)->INTFLAGS & TC0_CCAIF_bm);
	  case TC_CCB:
	    return (((TC0_t *) tc)->INTFLAGS & TC0_CCBIF_bm);
	  case TC_CCC:
	    return (((TC0_t *) tc)->INTFLAGS & TC0_CCCIF_bm);
	  case TC_CCD:
	    return (((TC0_t *) tc)->INTFLAGS & TC0_CCDIF_bm);
	  }
      }
    else if (tc_is_tc1 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    return (((TC1_t *) tc)->INTFLAGS & TC1_CCAIF_bm);
	  case TC_CCB:
	    return (((TC1_t *) tc)->INTFLAGS & TC1_CCBIF_bm);
	  default:
	    return (0);
	  }
      }
    return (0);
  }

/**
 * \brief Clears Capture/Compare interrupt
 *
 * \param tc Pointer to TC module.
 * \param channel_index CC Channel
 */
  static inline void tc_clear_cc_interrupt (volatile void *tc,
					    enum tc_cc_channel_t
					    channel_index)
  {
    if (tc_is_tc0 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    ((TC0_t *) tc)->INTFLAGS = TC0_CCAIF_bm;
	    break;
	  case TC_CCB:
	    ((TC0_t *) tc)->INTFLAGS = TC0_CCBIF_bm;
	    break;
	  case TC_CCC:
	    ((TC0_t *) tc)->INTFLAGS = TC0_CCCIF_bm;
	    break;
	  case TC_CCD:
	    ((TC0_t *) tc)->INTFLAGS = TC0_CCDIF_bm;
	    break;
	  }
      }
    else if (tc_is_tc1 (void *tc))
      {
	switch (channel_index)
	  {
	  case TC_CCA:
	    ((TC1_t *) tc)->INTFLAGS = TC1_CCAIF_bm;
	    break;
	  case TC_CCB:
	    ((TC1_t *) tc)->INTFLAGS = TC1_CCBIF_bm;
	    break;
	  default:
	    return;
	  }
      }
  }

/**
 * \brief Configures TC in the specified Waveform generator mode
 *
 * \param tc Pointer to TC module.
 * \param wgm : waveform generator
 */
  static inline void tc_set_wgm (volatile void *tc, enum tc_wg_mode_t wgm)
  {
    ((TC0_t *) tc)->CTRLB = (((TC0_t *) tc)->CTRLB & ~TC0_WGMODE_gm) | wgm;
  }

/**
 * \ingroup tc_group
 * \defgroup tc_awex_group AWeX extension driver
 * This group provides low level drivers to configure AWeX extension
 * @{
 */

/**
 * \brief AWeX extension enable
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_enable_cwcm (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL |= AWEX_CWCM_bm;
  }

/**
 * \brief AWeX extension disable Common waveform mode
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_disable_cwcm (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL &= ~AWEX_CWCM_bm;
  }

/**
 * \brief AWeX extension enable pattern generator mode
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_enable_pgm (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL |= AWEX_PGM_bm;
  }

/**
 * \brief AWeX extension disable pattern generator mode
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_disable_pgm (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL &= ~AWEX_PGM_bm;
  }

/**
 * \brief AWeX extension : enable Deadtime insertion on ccA
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_enable_cca_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL |= AWEX_DTICCAEN_bm;
  }

/**
 * \brief AWeX extension : disable Deadtime insertion on ccA
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_disable_cca_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCAEN_bm;
  }

/**
 * \brief AWeX extension : enable Deadtime insertion on ccB
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_enable_ccb_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL |= AWEX_DTICCBEN_bm;
  }

/**
 * \brief AWeX extension : disable Deadtime insertion on ccB
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_disable_ccb_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCBEN_bm;
  }

/**
 * \brief AWeX extension : enable Deadtime insertion on ccC
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_enable_ccc_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL |= AWEX_DTICCCEN_bm;
  }

/**
 * \brief AWeX extension : disable Deadtime insertion on ccD
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_disable_ccc_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCCEN_bm;
  }

/**
 * \brief AWeX extension : enable Deadtime insertion on ccD
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_enable_ccd_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL |= AWEX_DTICCDEN_bm;
  }

/**
 * \brief AWeX extension : disable Deadtime insertion on ccD
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_disable_ccd_deadtime (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->CTRL &= ~AWEX_DTICCDEN_bm;
  }
/**
 * \brief AWeX extension : configures high side deadtime
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \param value : deadtime value
 */
  static inline void tc_awex_set_dti_high (AWEX_t * awex, int16_t value)
  {
    ((AWEX_t *) awex)->DTHS = value;
  }
/**
 * \brief AWeX extension : configures low side deadtime
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \param value : deadtime value
 */
  static inline void tc_awex_set_dti_low (AWEX_t * awex, int16_t value)
  {
    ((AWEX_t *) awex)->DTLS = value;
  }
/**
 * \brief AWeX extension : configures symmetrical deadtime
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \param value : deadtime value
 */
  static inline void tc_awex_set_dti_both (AWEX_t * awex, int16_t value)
  {
    ((AWEX_t *) awex)->DTBOTH = value;
  }

/**
 * \brief AWeX extension : configures symmetrical deadtime buffer
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \param value : deadtime buffer value
 */
  static inline void tc_awex_set_dti_both_buffer (AWEX_t * awex,
						  int16_t value)
  {
    ((AWEX_t *) awex)->DTBOTHBUF = value;
  }

/**
 * \brief AWeX extension : returns the deadtime buffer high nibble
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \return Dead Time High value
 */
  static inline int8_t tc_awex_get_dti_high_buffer (AWEX_t * awex)
  {
    return (((AWEX_t *) awex)->DTHSBUF);
  }

/**
 * \brief AWeX extension : returns the deadtime buffer low nibble
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \return Dead Time High value
 */
  static inline int8_t tc_awex_get_dti_low_buffer (AWEX_t * awex)
  {
    return (((AWEX_t *) awex)->DTLSBUF);
  }

/**
 * \brief AWeX extension : returns if DTI high buffer is valid
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \return  Dead Time High Buffer valid or not
 */
  static inline bool tc_awex_is_dti_high_buffer_valid (AWEX_t * awex)
  {
    return (((AWEX_t *) awex)->STATUS & AWEX_DTHSBUFV_bm);
  }

/**
 * \brief AWeX extension : returns if DTI low buffer is valid
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \return Dead Time Low Buffer is valid or not
 */
  static inline bool tc_awex_is_dti_low_buffer_valid (AWEX_t * awex)
  {
    return (((AWEX_t *) awex)->STATUS & AWEX_DTLSBUFV_bm);
  }

/**
 * \brief AWeX extension : configures the Fault restart in latched mode
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_fdmode_restart_latched (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->FDCTRL &= ~AWEX_FDMODE_bm;
  }

/**
 * \brief AWeX extension : configures the Fault restart in cycle to cycle mode
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_fdmode_restart_cycle (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->FDCTRL |= AWEX_FDMODE_bm;
  }

/**
 * \brief AWeX extension : returns if fault is detected
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline bool tc_awex_fault_is_detected (AWEX_t * awex)
  {
    return (((AWEX_t *) awex)->STATUS & AWEX_FDF_bm);
  }

/**
 * \brief AWeX extension : clears the Fault detection
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_clear_fault (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->STATUS = AWEX_FDF_bm;
  }

/**
 * \brief AWeX extension : configures fault action
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \param fd_act Fault action
 */
  static inline void tc_awex_set_fault_detection_action (AWEX_t *
							 awex,
							 AWEX_FDACT_t fd_act)
  {
    ((AWEX_t *) awex)->FDCTRL = (((AWEX_t *) awex)->FDCTRL & ~AWEX_FDACT_gm) |
      (fd_act & AWEX_FDACT_gm);

  }

/**
 * \brief AWeX extension : configures fault detection event
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \param eventmask Fault detection event
 */
  static inline void tc_awex_set_fault_detection_event (AWEX_t * awex,
							int8_t eventmask)
  {
    ((AWEX_t *) awex)->FDEMASK = eventmask;
  }

/**
 * \brief AWeX extension : configures the port overdrive
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 * \param value Output override configuration
 */
  static inline void tc_awex_set_output_override (AWEX_t * awex, int8_t value)
  {
    ((AWEX_t *) awex)->OUTOVEN = value;
  }

/**
 * \brief AWeX extension : enable fault detection on debug break detection
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_enable_fault_debug_break (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->FDCTRL &= ~AWEX_FDDBD_bm;
  }

/**
 * \brief AWeX extension : disable fault detection on debug break detection
 *
 * \param awex Pointer to AWeX module (AWEXC or AWEXE)
 */
  static inline void tc_awex_disable_fault_debug_break (AWEX_t * awex)
  {
    ((AWEX_t *) awex)->FDCTRL |= AWEX_FDDBD_bm;
  }
//@}
/**
 * \ingroup tc_group
 * \defgroup tc_hires_group Hi-Res extension driver
 * This group provides low level drivers to configure Hi-Res extension
 * @{
 */

/**
 * \brief Hi-Res Extension : configures the Hi-Res
 *
 * \param hires Pointer to AWeX module (AWEXC or AWEXE)
 * \param hi_res_mode HIRES configuration
 */
  static inline void tc_hires_set_mode (HIRES_t * hires,
					HIRES_HREN_t hi_res_mode)
  {
    ((HIRES_t *) hires)->CTRLA = hi_res_mode;
  }
//@}

/** @} */


#ifdef __cplusplus
}
#endif

/**
 * \page xmega_tc_quickstart Quick Start Guide for the XMEGA TC Driver
 *
 * This is the quick start guide for the \ref tc_group , with step-by-step 
 * instructions on how to configure and use the driver for a specific use case. 
 * The code examples can be copied into e.g the main application loop or any
 * other function that will need to control the timer/counters.
 *
 *
 * \section xmega_tc_qs_use_cases Use cases
 * - \ref xmega_tc_qs_ovf
 * - \ref xmega_tc_qs_cc
 * - \ref xmega_tc_qs_pwm
 *
 *
 * \section xmega_tc_qs_ovf Timer/counter overflow (interrupt based)
 *
 * This use case will prepare a timer to trigger an interrupt when the timer
 * overflows. The interrupt is handled by a cutomisable callback function.
 *
 * We will setup the timer in this mode:
 * - Normal WGM mode (incrementing timer)
 * - Use the system clock as clock source
 * - No prescaling (clock divider set to 1)
 * - Overflow interrupt after 1000 counts. This will be done by setting the top
 *   value to 1000.
 *
 *
 * \section xmega_tc_qs_ovf_setup Setup steps
 *
 * \subsection xmega_tc_qs_ovf_usage_prereq Prequisites
 *
 * For the setup code of this use case to work, the following must
 * be added to the project:
 * - \ref interrupt_group "Global Interrupt Management"
 * - \ref clk_group "Clock Management"
 *
 * \subsection xmega_tc_qs_ovf_setup_code Example code
 *
 * Add a callback function that will be executed when the overflow interrupt
 * trigger.
 * \code
 * static void my_callback(void)
 * {
 *     // User code to execute when the overflow occurs here
 * }
 * \endcode
 * Add to, e.g., the main loop in the application C-file:
 * \code
 * pmic_init();
 * sysclk_init();
 * tc_enable(&TCC0);
 * tc_set_overflow_interrupt_callback(&TCC0, my_callback);
 * tc_set_wgm(&TCC0, TC_WG_NORMAL);
 * tc_write_period(&TCC0, 1000);
 * tc_set_overflow_interrupt_level(&TCC0, TC_INT_LVL_LO);
 * cpu_irq_enable();
 * tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc);
 * \endcode
 *
 * \subsection xmega_tc_qs_ovf_setup_code_workflow Workflow
 *
 * -# Enable the interrupt controller:
 *  - \code pmic_init(); \endcode
 * -# Enable the clock system:
 *  - \code sysclk_init(); \endcode
 * -#  Enable timer/counter TCC0
 *  - \code tc_enable(&TCC0); \endcode
 *    \note This will enable the clock system for the module
 * -# Set the callback function for overflow interrupt
 *  - \code tc_set_overflow_interrupt_callback(&TCC0, my_callback); \endcode
 *    \warning This function requires that the my_callback function is defined
 * -# Set the desired waveform mode
 *  - \code tc_set_wgm(&TCC0, TC_WG_NORMAL); \endcode
 *    \note In this case, we use normal mode where the timer increments it
            count value until the TOP value is reached. The timer then reset
            its count value to 0.
 * -# Set the period 
 *  - \code tc_write_period(&TCC0, 1000); \endcode
 *    \note This will specify the TOP value of the counter. The timer will
 *          overflow and reset when this value is reached.
 * -# Set the overflow interrupt level 
 *   - \code tc_set_overflow_interrupt_level(&TCC0, TC_INT_LVL_LO); \endcode
 * -# Enable interrupts:
 *  - \code cpu_irq_enable(); \endcode
 * -# Set the clock source
 *  - \code tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc); \endcode
 *    \warning When the clock source is set, the timer will start counting
 *
 * \section xmega_tc_qs_ovf_usage Usage steps
 *
 * - None. The timer will run in the background, and the code written in the 
 *   call back function will execute each time the timer overflows. 
 *
 *
 * \section xmega_tc_qs_cc Timer/counter compare match (interrupt based)
 *
 * This use case will prepare a timer to trigger two independent interrupts
 * when it reaches two different compare values. The period of the timer
 * is customizable and the two compare matches will be handled by two separate
 * interrupts implemented in call back functions.
 *
 * We will setup the timer in this mode:
 * - Normal WGM mode - incrementing timer
 * - Use the system clock as clock source
 * - No prescaling (divider set to 1)
 * - Period of timer 10000 counts
 * - Compare match A interrupt trigger after 100 counts
 * - Compare match B interrupt trigger after 1000 counts
 * - If compare A and compare B match occurs simultaneously, compare B
 *   should have higher priority
 *
 *
 * \section xmega_tc_qs_cc_setup Setup steps
 *
 * \subsection xmega_tc_qs_cc_usage_prereq Prequisites
 * For the setup code of this use case to work, the following must
 * be added to the project:
 * - \ref interrupt_group "Global Interrupt Management"
 * - \ref clk_group "Clock Management"
 *
 * \subsection xmega_tc_qs_cc_setup_code Example code
 *
 * Add two callback functions that will be executed when compare match A and 
 * compare match B occurs
 * \code
 * static void my_cca_callback(void)
 * {
 *    // User code here to execute when a channel A compare match occurs
 * }
 * static void my_ccb_callback(void)
 * {
 *    // User code here to execute when a channel B compare match occurs
 * }
 * \endcode
 * Add to, e.g., the main loop in the application C-file:
 * \code
 * pmic_init();
 * sysclk_init();
 * cpu_irq_enable();
 * tc_enable(&TCC0);
 * tc_set_cca_interrupt_callback(&TCC0, my_cca_callback);
 * tc_set_ccb_interrupt_callback(&TCC0, my_ccb_callback);
 * tc_set_wgm(&TCC0, TC_WG_NORMAL);
 * tc_write_period(&TCC0, 10000);
 * tc_write_cc(&TCC0, TC_CCA, 100);
 * tc_write_cc(&TCC0, TC_CCB, 1000);
 * tc_enable_cc_channels(&TCC0,(TC_CCAEN | TC_CCBEN));
 * tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_LO);
 * tc_set_ccb_interrupt_level(&TCC0, TC_INT_LVL_MED);
 * tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc);
 * \endcode
 *
 * \subsection xmega_tc_qs_cc_setup_code_workflow Workflow
 *
 * -# Enable the interrupt controller:
 *  - \code pmic_init(); \endcode
 * -# Enable the clock system:
 *  - \code sysclk_init(); \endcode
 * -# Enable interrupts:
 *  - \code cpu_irq_enable(); \endcode
 * -#  Enable timer/counter TCC0
 *  - \code tc_enable(&TCC0); \endcode
 *    \note This will enable the clock system for the module
 * -# Set call back function for CCA interrupt
 *  - \code tc_set_cca_interrupt_callback(&TCC0, my_cca_callback); \endcode
 *    \warning This function requires that the call back function is defined
 * -# Set call back function for CCB interrupt
 *  - \code tc_set_ccb_interrupt_callback(&TCC0, my_ccb_callback); \endcode
 *    \warning This function requires that the call back function is defined
 * -# Set the desired waveform mode
 *  - \code tc_set_wgm(&TCC0, TC_WG_NORMAL); \endcode
 *    \note In this case, we use normal mode where the timer increments it
            count value until the TOP value is reached. The timer then reset
            its count value to 0.
 * -# Set the period 
 *  - \code tc_write_period(&TCC0, 10000); \endcode
 *    \note This will specify the TOP value of the counter. The timer will
 *          overflow and reset when this value is reached.
 * -# Set compare match value on CCA
 *   - \code tc_write_cc(&TCC0, TC_CCA, 100); \endcode
 * -# Set compare match value on CCB
 *   - \code tc_write_cc(&TCC0, TC_CCB, 1000); \endcode
 * -# Enable compare channel A and compare channel B
 *  -\code tc_enable_cc_channels(&TCC0, (TC_CCAEN | TC_CCBEN)); \endcode
 * -# Set interrupt level on channel A (low priority, see \ref TC_INT_LEVEL_t)
 *   - \code tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_LO); \endcode
 * -# Set interrupt level on channel B (medium priority \ref TC_INT_LEVEL_t)
 *   - \code tc_set_ccb_interrupt_level(&TCC0, TC_INT_LVL_MED); \endcode
 * -# Set the clock source
 *  - \code tc_write_clock_source(&TCC0, TC_CLKSEL_DIV1_gc); \endcode
 *    \warning When the clock source is set, the timer will start counting
 *
 * \section xmega_tc_qs_cc_usage Usage steps
 *
 * - None. The timer will run in the background, and the code written in the 
 *   call back functions will execute each time a compare match occur.
 *
 *
 * \section xmega_tc_qs_pwm Timer/counter PWM
 * 
 * This use case will setup a timer in PWM mode. For more details you can 
 * also look at the XMEGA PWM service.
 *
 * We will setup the timer in this mode:
 * - Normal WGM mode - incrementing timer
 * - Use the 2MHz oscillator as clock source (default)
 * - 1Hz PWM frequency (2MHz clock, 1024x prescale, TOP value 1950) 
 * - 10% duty cycle (1:10 ratio between PER and CC register)
 * - Output the PWM signal to a I/O port
 *
 * \section xmega_tc_qs_pwm_setup Setup steps
 *
 * \subsection xmega_tc_qs_pwm_usage_prereq Prequisites
 * For the setup code of this use case to work, the following must
 * be added to the project:
 * - \ref clk_group "Clock Management"
 *
 * \subsection xmega_tc_qs_pwm_setup_code Example code
 *
 * Add to, e.g., the main loop in the application C-file:
 * \code
 * board_init();
 * sysclk_init();
 * tc_enable(&TCE0);
 * tc_set_wgm(&TCE0, TC_WG_SS);
 * tc_write_period(&TCE0, 1950);
 * tc_write_cc(&TCE0, TC_CCA, 195);
 * tc_enable_cc_channels(&TCE0,TC_CCAEN);
 * tc_write_clock_source(&TCE0, TC_CLKSEL_DIV1024_gc);
 * \endcode
 *
 * \subsection xmega_tc_qs_pwm_setup_code_workflow Workflow
 *
 * -# Ensure that PWM I/O pin is configured as output
 *  - \code board_init(); \endcode
 *    \note The board_init(); function configures the I/O pins. If this function
 *          is not executed, the I/O pin must be configured as output manually
 * -# Enable the clock system:
 *  - \code sysclk_init(); \endcode
 * -#  Enable timer/counter TCE0
 *  - \code tc_enable(&TCE0); \endcode
 *    \note This will enable the clock system for the module
 * -# Set the desired waveform mode
 *  - \code tc_set_wgm(&TCE0, TC_WG_NORMAL); \endcode
 *    \note In this case, we use normal mode where the timer increments it
 *          count value until the TOP value is reached. The timer then reset
  *         its count value to 0.
 * -# Set the period 
 *  - \code tc_write_period(&TCE0, 1950); \endcode
 *    \note This will specify the TOP value of the counter. The timer will
 *          overflow and reset when this value is reached.
 * -# Set compare match value on CCA
 *   - \code tc_write_cc(&TCC0, TC_CCA, 195); \endcode
 *     \note The PWM duty cycle will be the ratio between PER and CCA, which
 *           is set by the tc_write_period() and tc_write_cc() functions. Use
 *           tc_write_cc() to change duty cycle run time (e.g to dim a LED).
 *           When CCA = 0, the duty cycle will be 0%. When CCA = PER (top value)
 *           the duty cycle will be 100%.
 * -# Enable compare channel A 
 *  -\code tc_enable_cc_channels(&TCE0,TC_CCAEN); \endcode
 * -# Set the clock source
 *  - \code tc_write_clock_source(&TCE0, TC_CLKSEL_DIV1024_gc); \endcode
 *    \warning When the clock source is set, the timer will start counting
 *
 * \section xmega_tc_qs_pwm_usage Usage steps
 *  - Use tc_write_cc() to change the duty cycle of the PWM signal
 *  - Use tc_write_period() to change the PWM frequency
 */

#endif /* _TC_H_ */
