Files
bacnet_stack/ports/xplained/ASF/xmega/drivers/tc/tc.h
T
2019-10-08 23:47:53 -05:00

1685 lines
45 KiB
C

/**
* \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_ */