1 /* ----------------------------------------------------------------------------
\r
2 * SAM Software Package License
\r
3 * ----------------------------------------------------------------------------
\r
4 * Copyright (c) 2015, Atmel Corporation
\r
6 * All rights reserved.
\r
8 * Redistribution and use in source and binary forms, with or without
\r
9 * modification, are permitted provided that the following conditions are met:
\r
11 * - Redistributions of source code must retain the above copyright notice,
\r
12 * this list of conditions and the disclaimer below.
\r
14 * Atmel's name may not be used to endorse or promote products derived from
\r
15 * this software without specific prior written permission.
\r
17 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
\r
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
\r
20 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
\r
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
\r
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
\r
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
\r
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
\r
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
27 * ----------------------------------------------------------------------------
\r
30 /** \addtogroup tc_module
\r
32 * The TC driver provides the Interface to configure the Timer Counter (TC).
\r
36 * <li> Optionally, use tc_find_mck_divisor() to let the program find the best
\r
37 * TCCLKS field value automatically.</li>
\r
38 * <li> Configure a Timer Counter in the desired mode using tc_configure().</li>
\r
39 * <li> Start or stop the timer clock using tc_start() and tc_stop().</li>
\r
42 * For more accurate information, please look at the TC section of the Datasheet.
\r
54 * Interface for configuring and using Timer Counter (TC) peripherals.
\r
57 * -# Optionally, use tc_find_mck_divisor() to let the program find the best
\r
58 * TCCLKS field value automatically.
\r
59 * -# Configure a Timer Counter in the desired mode using tc_configure().
\r
60 * -# Start or stop the timer clock using tc_start() and tc_stop().
\r
66 * Implementation of Timer Counter (TC).
\r
70 /*------------------------------------------------------------------------------
\r
72 *------------------------------------------------------------------------------*/
\r
75 #include "peripherals/tc.h"
\r
76 #include "peripherals/pmc.h"
\r
80 /*------------------------------------------------------------------------------
\r
82 *------------------------------------------------------------------------------*/
\r
85 * \brief Configures a Timer Counter Channel
\r
87 * Configures a Timer Counter to operate in the given mode. Timer is stopped
\r
88 * after configuration and must be restarted with tc_start(). All the
\r
89 * interrupts of the timer are also disabled.
\r
91 * \param pTc Pointer to a Tc instance.
\r
92 * \param channel Channel number.
\r
93 * \param mode Operating mode (TC_CMR value).
\r
95 void tc_configure(Tc * pTc, uint32_t channel, uint32_t mode)
\r
97 volatile TcChannel *pTcCh;
\r
100 (sizeof (pTc->TC_CHANNEL) / sizeof (pTc->TC_CHANNEL[0])));
\r
101 pTcCh = pTc->TC_CHANNEL + channel;
\r
103 /* Disable TC clock */
\r
104 pTcCh->TC_CCR = TC_CCR_CLKDIS;
\r
106 /* Disable interrupts */
\r
107 pTcCh->TC_IDR = 0xFFFFFFFF;
\r
109 /* Clear status register */
\r
113 pTcCh->TC_CMR = mode;
\r
117 * \brief Reset and Start the TC Channel
\r
119 * Enables the timer clock and performs a software reset to start the counting.
\r
121 * \param pTc Pointer to a Tc instance.
\r
122 * \param channel Channel number.
\r
124 void tc_start(Tc * pTc, uint32_t channel)
\r
126 volatile TcChannel *pTcCh;
\r
129 (sizeof (pTc->TC_CHANNEL) / sizeof (pTc->TC_CHANNEL[0])));
\r
131 pTcCh = pTc->TC_CHANNEL + channel;
\r
132 pTcCh->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
\r
133 pTcCh->TC_IER = TC_IER_COVFS;
\r
137 * \brief Stop TC Channel
\r
139 * Disables the timer clock, stopping the counting.
\r
141 * \param pTc Pointer to a Tc instance.
\r
142 * \param channel Channel number.
\r
144 void tc_stop(Tc * pTc, uint32_t channel)
\r
146 volatile TcChannel *pTcCh;
\r
149 (sizeof (pTc->TC_CHANNEL) / sizeof (pTc->TC_CHANNEL[0])));
\r
151 pTcCh = pTc->TC_CHANNEL + channel;
\r
152 pTcCh->TC_CCR = TC_CCR_CLKDIS;
\r
153 pTcCh->TC_IDR = TC_IER_COVFS;
\r
157 * \brief Enables TC channel interrupts
\r
159 * \param tc Pointer to Tc instance
\r
160 * \param channel Channel number
\r
161 * \param mask mask of interrupts to enable
\r
163 void tc_enable_it(Tc* tc, uint32_t channel, uint32_t mask)
\r
165 assert(channel < (sizeof (tc->TC_CHANNEL) / sizeof (tc->TC_CHANNEL[0])));
\r
166 tc->TC_CHANNEL[channel].TC_IER = mask;
\r
170 * \brief Find best MCK divisor
\r
172 * Finds the best MCK divisor given the timer frequency and MCK. The result
\r
173 * is guaranteed to satisfy the following equation:
\r
175 * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)
\r
177 * with DIV being the highest possible value.
\r
179 * \param freq Desired timer freq.
\r
180 * \param div Divisor value.
\r
181 * \param tc_clks TCCLKS field value for divisor.
\r
183 * \return 1 if a proper divisor has been found, otherwise 0.
\r
185 uint32_t tc_find_mck_divisor (uint32_t freq, uint32_t * div,
\r
186 uint32_t * tc_clks)
\r
188 const uint32_t periph_clock = pmc_get_peripheral_clock(ID_TC0);
\r
189 const uint32_t available_freqs[5] = {periph_clock >> 1, periph_clock >> 3, periph_clock >> 5, periph_clock >> 7, 32768};
\r
192 for (i = 0; i < 5; ++i)
\r
194 uint32_t tmp = freq << 1;
\r
195 if (tmp > available_freqs[i])
\r
199 i = (i == 5 ? i-1 : i);
\r
201 /* Store results */
\r
203 *div = periph_clock / available_freqs[i];
\r
212 uint32_t tc_get_status(Tc* tc, uint32_t channel_num)
\r
214 return tc->TC_CHANNEL[channel_num].TC_SR;
\r
218 void tc_trigger_on_freq(Tc* tc, uint32_t channel_num, uint32_t freq)
\r
221 uint32_t tcclks = 0;
\r
222 uint32_t tc_id = get_tc_id_from_addr(tc);
\r
223 TcChannel* channel = &tc->TC_CHANNEL[channel_num];
\r
225 tc_find_mck_divisor(freq, &div, &tcclks);
\r
226 tc_configure(tc, channel_num, tcclks | TC_CMR_CPCTRG);
\r
227 channel->TC_RC = (pmc_get_peripheral_clock(tc_id) / div) / freq;
\r