summaryrefslogtreecommitdiff
path: root/FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c
diff options
context:
space:
mode:
authorRichard Barry <ribarry@amazon.com>2014-09-02 16:06:57 +0000
committerRichard Barry <ribarry@amazon.com>2014-09-02 16:06:57 +0000
commit33cc3a292bfc7ccb1f20dcf00ad854863ea4cae3 (patch)
treebc5061a848705f78601f597152eee397d73745bb /FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c
parent99229b597b57d6ddff289355469f367d2319cefe (diff)
downloadfreertos-git-33cc3a292bfc7ccb1f20dcf00ad854863ea4cae3.tar.gz
Demo code only:
Add the IntQ standard test to the SAM4S project.
Diffstat (limited to 'FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c')
-rw-r--r--FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c580
1 files changed, 580 insertions, 0 deletions
diff --git a/FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c b/FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c
new file mode 100644
index 000000000..d2bf3aeed
--- /dev/null
+++ b/FreeRTOS/Demo/CORTEX_M4_ATSAM4S_Atmel_Studio/src/asf/sam/drivers/tc/tc.c
@@ -0,0 +1,580 @@
+/**
+ * \file
+ *
+ * \brief Timer Counter (TC) driver for SAM.
+ *
+ * Copyright (c) 2011-2012 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
+ *
+ */
+
+#include <assert.h>
+#include "tc.h"
+
+/// @cond 0
+/**INDENT-OFF**/
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**INDENT-ON**/
+/// @endcond
+
+#define TC_WPMR_WPKEY_VALUE TC_WPMR_WPKEY((uint32_t)0x54494D)
+
+/**
+ * \defgroup sam_drivers_tc_group Timer Counter (TC)
+ *
+ * The Timer Counter (TC) includes three identical 32-bit Timer Counter
+ * channels. Each channel can be independently programmed to perform a wide
+ * range of functions including frequency measurement, event counting,
+ * interval measurement, pulse generation, delay timing and pulse width
+ * modulation.
+ *
+ * @{
+ */
+
+/**
+ * \brief Configure TC for timer, waveform generation or capture.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ * \param ul_mode Control mode register value to set.
+ *
+ * \attention If the TC is configured for waveform generation, the external
+ * event selection (EEVT) should only be set to \c TC_CMR_EEVT_TIOB or the
+ * equivalent value \c 0 if it really is the intention to use TIOB as an
+ * external event trigger.\n
+ * This is because the setting forces TIOB to be an input even if the
+ * external event trigger has not been enabled with \c TC_CMR_ENETRG, and
+ * thus prevents normal operation of TIOB.
+ */
+void tc_init(Tc *p_tc, uint32_t ul_channel, uint32_t ul_mode)
+{
+ TcChannel *tc_channel;
+
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+ tc_channel = p_tc->TC_CHANNEL + ul_channel;
+
+ /* Disable TC clock. */
+ tc_channel->TC_CCR = TC_CCR_CLKDIS;
+
+ /* Disable interrupts. */
+ tc_channel->TC_IDR = 0xFFFFFFFF;
+
+ /* Clear status register. */
+ tc_channel->TC_SR;
+
+ /* Set mode. */
+ tc_channel->TC_CMR = ul_mode;
+}
+
+/**
+ * \brief Asserts a SYNC signal to generate a software trigger to
+ * all channels.
+ *
+ * \param p_tc Pointer to a TC instance.
+ *
+ */
+void tc_sync_trigger(Tc *p_tc)
+{
+ p_tc->TC_BCR = TC_BCR_SYNC;
+}
+
+/**
+ * \brief Configure TC Block mode.
+ * \note tc_init() must be called first.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_blockmode Block mode register value to set.
+ *
+ */
+void tc_set_block_mode(Tc *p_tc, uint32_t ul_blockmode)
+{
+ p_tc->TC_BMR = ul_blockmode;
+}
+
+#if (!SAM3U)
+
+/**
+ * \brief Configure TC for 2-bit Gray Counter for Stepper Motor.
+ * \note tc_init() must be called first.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ * \param ul_steppermode Stepper motor mode register value to set.
+ *
+ * \return 0 for OK.
+ */
+uint32_t tc_init_2bit_gray(Tc *p_tc, uint32_t ul_channel,
+ uint32_t ul_steppermode)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ p_tc->TC_CHANNEL[ul_channel].TC_SMMR = ul_steppermode;
+ return 0;
+}
+
+#endif
+
+/**
+ * \brief Start TC clock counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ */
+void tc_start(Tc *p_tc, uint32_t ul_channel)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ p_tc->TC_CHANNEL[ul_channel].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
+}
+
+/**
+ * \brief Stop TC clock counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ */
+void tc_stop(Tc *p_tc, uint32_t ul_channel)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ p_tc->TC_CHANNEL[ul_channel].TC_CCR = TC_CCR_CLKDIS;
+}
+
+/**
+ * \brief Read RA TC counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ *
+ * \return RA value.
+ */
+int tc_read_ra(Tc *p_tc, uint32_t ul_channel)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ return p_tc->TC_CHANNEL[ul_channel].TC_RA;
+}
+
+/**
+ * \brief Read RB TC counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ *
+ * \return RB value.
+ */
+int tc_read_rb(Tc *p_tc, uint32_t ul_channel)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ return p_tc->TC_CHANNEL[ul_channel].TC_RB;
+}
+
+/**
+ * \brief Read RC TC counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ *
+ * \return RC value.
+ */
+int tc_read_rc(Tc *p_tc, uint32_t ul_channel)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ return p_tc->TC_CHANNEL[ul_channel].TC_RC;
+}
+
+/**
+ * \brief Write RA TC counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ * \param ul_value Value to set in register.
+ */
+void tc_write_ra(Tc *p_tc, uint32_t ul_channel,
+ uint32_t ul_value)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ p_tc->TC_CHANNEL[ul_channel].TC_RA = ul_value;
+}
+
+/**
+ * \brief Write RB TC counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ * \param ul_value Value to set in register.
+ */
+void tc_write_rb(Tc *p_tc, uint32_t ul_channel,
+ uint32_t ul_value)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ p_tc->TC_CHANNEL[ul_channel].TC_RB = ul_value;
+}
+
+/**
+ * \brief Write RC TC counter on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ * \param ul_value Value to set in register.
+ */
+void tc_write_rc(Tc *p_tc, uint32_t ul_channel,
+ uint32_t ul_value)
+{
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+
+ p_tc->TC_CHANNEL[ul_channel].TC_RC = ul_value;
+}
+
+/**
+ * \brief Enable TC interrupts on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ * \param ul_sources Interrupt sources bit map.
+ */
+void tc_enable_interrupt(Tc *p_tc, uint32_t ul_channel,
+ uint32_t ul_sources)
+{
+ TcChannel *tc_channel;
+
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+ tc_channel = p_tc->TC_CHANNEL + ul_channel;
+ tc_channel->TC_IER = ul_sources;
+}
+
+/**
+ * \brief Disable TC interrupts on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ * \param ul_sources Interrupt sources bit map.
+ */
+void tc_disable_interrupt(Tc *p_tc, uint32_t ul_channel,
+ uint32_t ul_sources)
+{
+ TcChannel *tc_channel;
+
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+ tc_channel = p_tc->TC_CHANNEL + ul_channel;
+ tc_channel->TC_IDR = ul_sources;
+}
+
+/**
+ * \brief Read TC interrupt mask on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ *
+ * \return The interrupt mask value.
+ */
+uint32_t tc_get_interrupt_mask(Tc *p_tc, uint32_t ul_channel)
+{
+ TcChannel *tc_channel;
+
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+ tc_channel = p_tc->TC_CHANNEL + ul_channel;
+ return tc_channel->TC_IMR;
+}
+
+/**
+ * \brief Get current status on the selected channel.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_channel Channel to configure.
+ *
+ * \return The current TC status.
+ */
+uint32_t tc_get_status(Tc *p_tc, uint32_t ul_channel)
+{
+ TcChannel *tc_channel;
+
+ Assert(ul_channel <
+ (sizeof(p_tc->TC_CHANNEL) / sizeof(p_tc->TC_CHANNEL[0])));
+ tc_channel = p_tc->TC_CHANNEL + ul_channel;
+ return tc_channel->TC_SR;
+}
+
+/* TC divisor used to find the lowest acceptable timer frequency */
+#define TC_DIV_FACTOR 65536
+
+#if (!SAM4L)
+
+#ifndef FREQ_SLOW_CLOCK_EXT
+#define FREQ_SLOW_CLOCK_EXT 32768 /* External slow clock frequency (hz) */
+#endif
+
+/**
+ * \brief Find the best MCK divisor.
+ *
+ * Finds the best MCK divisor given the timer frequency and MCK. The result
+ * is guaranteed to satisfy the following equation:
+ * \code
+ * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)
+ * \endcode
+ * with DIV being the lowest possible value,
+ * to maximize timing adjust resolution.
+ *
+ * \param ul_freq Desired timer frequency.
+ * \param ul_mck Master clock frequency.
+ * \param p_uldiv Divisor value.
+ * \param p_ultcclks TCCLKS field value for divisor.
+ * \param ul_boardmck Board clock frequency.
+ *
+ * \return 1 if a proper divisor has been found, otherwise 0.
+ */
+uint32_t tc_find_mck_divisor(uint32_t ul_freq, uint32_t ul_mck,
+ uint32_t *p_uldiv, uint32_t *p_ultcclks, uint32_t ul_boardmck)
+{
+ const uint32_t divisors[5] = { 2, 8, 32, 128,
+ ul_boardmck / FREQ_SLOW_CLOCK_EXT };
+ uint32_t ul_index;
+ uint32_t ul_high, ul_low;
+
+ /* Satisfy frequency bound. */
+ for (ul_index = 0;
+ ul_index < (sizeof(divisors) / sizeof(divisors[0]));
+ ul_index++) {
+ ul_high = ul_mck / divisors[ul_index];
+ ul_low = ul_high / TC_DIV_FACTOR;
+ if (ul_freq > ul_high) {
+ return 0;
+ } else if (ul_freq >= ul_low) {
+ break;
+ }
+ }
+ if (ul_index >= (sizeof(divisors) / sizeof(divisors[0]))) {
+ return 0;
+ }
+
+ /* Store results. */
+ if (p_uldiv) {
+ *p_uldiv = divisors[ul_index];
+ }
+
+ if (p_ultcclks) {
+ *p_ultcclks = ul_index;
+ }
+
+ return 1;
+}
+
+#endif
+
+#if (SAM4L)
+/**
+ * \brief Find the best PBA clock divisor.
+ *
+ * Finds the best divisor given the timer frequency and PBA clock. The result
+ * is guaranteed to satisfy the following equation:
+ * \code
+ * (ul_pbaclk / (2* DIV * 65536)) <= freq <= (ul_pbaclk / (2* DIV))
+ * \endcode
+ * with DIV being the lowest possible value,
+ * to maximize timing adjust resolution.
+ *
+ * \param ul_freq Desired timer frequency.
+ * \param ul_mck PBA clock frequency.
+ * \param p_uldiv Divisor value.
+ * \param p_ultcclks TCCLKS field value for divisor.
+ * \param ul_boardmck useless here.
+ *
+ * \return 1 if a proper divisor has been found, otherwise 0.
+ */
+uint32_t tc_find_mck_divisor(uint32_t ul_freq, uint32_t ul_mck,
+ uint32_t *p_uldiv, uint32_t *p_ultcclks, uint32_t ul_boardmck)
+{
+ const uint32_t divisors[5] = { 0, 2, 8, 32, 128};
+ uint32_t ul_index;
+ uint32_t ul_high, ul_low;
+
+ UNUSED(ul_boardmck);
+
+ /* Satisfy frequency bound. */
+ for (ul_index = 1;
+ ul_index < (sizeof(divisors) / sizeof(divisors[0]));
+ ul_index++) {
+ ul_high = ul_mck / divisors[ul_index];
+ ul_low = ul_high / TC_DIV_FACTOR;
+ if (ul_freq > ul_high) {
+ return 0;
+ } else if (ul_freq >= ul_low) {
+ break;
+ }
+ }
+ if (ul_index >= (sizeof(divisors) / sizeof(divisors[0]))) {
+ return 0;
+ }
+
+ /* Store results. */
+ if (p_uldiv) {
+ *p_uldiv = divisors[ul_index];
+ }
+
+ if (p_ultcclks) {
+ *p_ultcclks = ul_index;
+ }
+
+ return 1;
+}
+
+#endif
+
+#if (!SAM4L)
+
+/**
+ * \brief Enable TC QDEC interrupts.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_sources Interrupts to be enabled.
+ */
+void tc_enable_qdec_interrupt(Tc *p_tc, uint32_t ul_sources)
+{
+ p_tc->TC_QIER = ul_sources;
+}
+
+/**
+ * \brief Disable TC QDEC interrupts.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_sources Interrupts to be disabled.
+ */
+void tc_disable_qdec_interrupt(Tc *p_tc, uint32_t ul_sources)
+{
+ p_tc->TC_QIDR = ul_sources;
+}
+
+/**
+ * \brief Read TC QDEC interrupt mask.
+ *
+ * \param p_tc Pointer to a TC instance.
+ *
+ * \return The interrupt mask value.
+ */
+uint32_t tc_get_qdec_interrupt_mask(Tc *p_tc)
+{
+ return p_tc->TC_QIMR;
+}
+
+/**
+ * \brief Get current QDEC status.
+ *
+ * \param p_tc Pointer to a TC instance.
+ *
+ * \return The current TC status.
+ */
+uint32_t tc_get_qdec_interrupt_status(Tc *p_tc)
+{
+ return p_tc->TC_QISR;
+}
+
+#endif
+
+#if (!SAM3U)
+
+/**
+ * \brief Enable or disable write protection of TC registers.
+ *
+ * \param p_tc Pointer to a TC instance.
+ * \param ul_enable 1 to enable, 0 to disable.
+ */
+void tc_set_writeprotect(Tc *p_tc, uint32_t ul_enable)
+{
+ if (ul_enable) {
+ p_tc->TC_WPMR = TC_WPMR_WPKEY_VALUE | TC_WPMR_WPEN;
+ } else {
+ p_tc->TC_WPMR = TC_WPMR_WPKEY_VALUE;
+ }
+}
+
+#endif
+
+#if SAM4L
+
+/**
+ * \brief Indicate features.
+ *
+ * \param p_tc Pointer to a TC instance.
+ *
+ * \return TC_FEATURES value.
+ */
+uint32_t tc_get_feature(Tc *p_tc)
+{
+ return p_tc->TC_FEATURES;
+}
+
+/**
+ * \brief Indicate version.
+ *
+ * \param p_tc Pointer to a TC instance.
+ *
+ * \return TC_VERSION value.
+ */
+uint32_t tc_get_version(Tc *p_tc)
+{
+ return p_tc->TC_VERSION;
+}
+
+#endif
+
+//@}
+
+/// @cond 0
+/**INDENT-OFF**/
+#ifdef __cplusplus
+}
+#endif
+/**INDENT-ON**/
+/// @endcond