00001 /* ---------------------------------------------------------------------------- */ 00002 /* Atmel Microcontroller Software Support */ 00003 /* SAM Software Package License */ 00004 /* ---------------------------------------------------------------------------- */ 00005 /* Copyright (c) 2015, Atmel Corporation */ 00006 /* */ 00007 /* All rights reserved. */ 00008 /* */ 00009 /* Redistribution and use in source and binary forms, with or without */ 00010 /* modification, are permitted provided that the following condition is met: */ 00011 /* */ 00012 /* - Redistributions of source code must retain the above copyright notice, */ 00013 /* this list of conditions and the disclaimer below. */ 00014 /* */ 00015 /* Atmel's name may not be used to endorse or promote products derived from */ 00016 /* this software without specific prior written permission. */ 00017 /* */ 00018 /* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR */ 00019 /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */ 00020 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE */ 00021 /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, */ 00022 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ 00023 /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */ 00024 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF */ 00025 /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */ 00026 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */ 00027 /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 00028 /* ---------------------------------------------------------------------------- */ 00029 00030 /** \addtogroup tc_module 00031 * The TC driver provides the Interface to configure the Timer Counter (TC). 00032 * 00033 * \section Usage 00034 * <ul> 00035 * <li> Optionally, use TC_FindMckDivisor() to let the program find the best 00036 * TCCLKS field value automatically.</li> 00037 * <li> Configure a Timer Counter in the desired mode using TC_Configure().</li> 00038 * <li> Start or stop the timer clock using TC_Start() and TC_Stop().</li> 00039 * 00040 * </ul> 00041 * For more accurate information, please look at the TC section of the Datasheet. 00042 * 00043 * Related files :\n 00044 * \ref tc.c\n 00045 * \ref tc.h.\n 00046 */ 00047 00048 /** 00049 * \file 00050 * 00051 * \section Purpose 00052 * 00053 * Interface for configuring and using Timer Counter (TC) peripherals. 00054 * 00055 * \section Usage 00056 * -# Optionally, use TC_FindMckDivisor() to let the program find the best 00057 * TCCLKS field value automatically. 00058 * -# Configure a Timer Counter in the desired mode using TC_Configure(). 00059 * -# Start or stop the timer clock using TC_Start() and TC_Stop(). 00060 */ 00061 00062 /** 00063 * \file 00064 * 00065 * Implementation of Timer Counter (TC). 00066 * 00067 */ 00068 00069 /*------------------------------------------------------------------------------ 00070 * Headers 00071 *-----------------------------------------------------------------------------*/ 00072 00073 #include "board.h" 00074 00075 #include <assert.h> 00076 00077 /*------------------------------------------------------------------------------ 00078 * Global functions 00079 *----------------------------------------------------------------------------*/ 00080 00081 /** 00082 * \brief Configures a Timer Counter Channel 00083 * 00084 * Configures a Timer Counter to operate in the given mode. Timer is stopped 00085 * after configuration and must be restarted with TC_Start(). All the 00086 * interrupts of the timer are also disabled. 00087 * 00088 * \param pTc Pointer to a Tc instance. 00089 * \param channel Channel number. 00090 * \param mode Operating mode (TC_CMR value). 00091 */ 00092 extern void TC_Configure(Tc *pTc, uint32_t dwChannel, uint32_t dwMode) 00093 { 00094 TcChannel *pTcCh; 00095 00096 assert(dwChannel < (sizeof(pTc->TC_CHANNEL) / sizeof( 00097 pTc->TC_CHANNEL[0]))); 00098 pTcCh = pTc->TC_CHANNEL + dwChannel; 00099 00100 /* Disable TC clock */ 00101 pTcCh->TC_CCR = TC_CCR_CLKDIS; 00102 00103 /* Disable interrupts */ 00104 pTcCh->TC_IDR = 0xFFFFFFFF; 00105 00106 /* Clear status register */ 00107 pTcCh->TC_SR; 00108 00109 /* Set mode */ 00110 pTcCh->TC_CMR = dwMode; 00111 } 00112 00113 /** 00114 * \brief Reset and Start the TC Channel 00115 * 00116 * Enables the timer clock and performs a software reset to start the counting. 00117 * 00118 * \param pTc Pointer to a Tc instance. 00119 * \param dwChannel Channel number. 00120 */ 00121 extern void TC_Start(Tc *pTc, uint32_t dwChannel) 00122 { 00123 TcChannel *pTcCh; 00124 00125 assert(dwChannel < (sizeof(pTc->TC_CHANNEL) / sizeof( 00126 pTc->TC_CHANNEL[0]))); 00127 00128 pTcCh = pTc->TC_CHANNEL + dwChannel; 00129 pTcCh->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; 00130 } 00131 00132 /** 00133 * \brief Stop TC Channel 00134 * 00135 * Disables the timer clock, stopping the counting. 00136 * 00137 * \param pTc Pointer to a Tc instance. 00138 * \param dwChannel Channel number. 00139 */ 00140 extern void TC_Stop(Tc *pTc, uint32_t dwChannel) 00141 { 00142 TcChannel *pTcCh; 00143 00144 assert(dwChannel < (sizeof(pTc->TC_CHANNEL) / sizeof( 00145 pTc->TC_CHANNEL[0]))); 00146 00147 pTcCh = pTc->TC_CHANNEL + dwChannel; 00148 pTcCh->TC_CCR = TC_CCR_CLKDIS; 00149 } 00150 00151 /** 00152 * \brief Find best MCK divisor 00153 * 00154 * Finds the best MCK divisor given the timer frequency and MCK. The result 00155 * is guaranteed to satisfy the following equation: 00156 * \code 00157 * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV) 00158 * \endcode 00159 * with DIV being the highest possible value. 00160 * 00161 * \param dwFreq Desired timer frequency. 00162 * \param dwMCk Master clock frequency. 00163 * \param dwDiv Divisor value. 00164 * \param dwTcClks TCCLKS field value for divisor. 00165 * \param dwBoardMCK Board clock frequency. 00166 * 00167 * \return 1 if a proper divisor has been found, otherwise 0. 00168 */ 00169 extern uint32_t TC_FindMckDivisor(uint32_t dwFreq, uint32_t dwMCk, 00170 uint32_t *dwDiv, uint32_t *dwTcClks, uint32_t dwBoardMCK) 00171 { 00172 /* 00173 TCCLKS: 00174 0 - PCK6: default source - Slow Clock; update according to actual case 00175 4 - SLCK: (slow clock) is either "Embedded 32kHz RC Oscillator" or 00176 "32768Hz Crystal Oscillator" 00177 */ 00178 const uint32_t adwDivisors[5] = { BOARD_MCK / 32768, 8, 32, 128, BOARD_MCK / 32768 }; 00179 00180 uint32_t dwIndex = 0; 00181 dwBoardMCK = dwBoardMCK; 00182 00183 /* Satisfy lower bound */ 00184 while (dwFreq < ((dwMCk / adwDivisors[dwIndex]) / 65536)) { 00185 dwIndex++; 00186 00187 /* If no divisor can be found, return 0 */ 00188 if (dwIndex == (sizeof(adwDivisors) / sizeof(adwDivisors[0]))) 00189 return 0; 00190 } 00191 00192 /* Try to maximize DIV while satisfying upper bound */ 00193 while (dwIndex < 4) { 00194 if (dwFreq > (dwMCk / adwDivisors[dwIndex + 1])) 00195 break; 00196 00197 dwIndex++; 00198 } 00199 00200 /* Store results */ 00201 if (dwDiv) 00202 *dwDiv = adwDivisors[dwIndex]; 00203 00204 if (dwTcClks) 00205 *dwTcClks = dwIndex; 00206 00207 return 1; 00208 } 00209