00001 /* ---------------------------------------------------------------------------- 00002 * SAM Software Package License 00003 * ---------------------------------------------------------------------------- 00004 * Copyright (c) 2011, Atmel Corporation 00005 * 00006 * All rights reserved. 00007 * 00008 * Redistribution and use in source and binary forms, with or without 00009 * modification, are permitted provided that the following conditions are met: 00010 * 00011 * - Redistributions of source code must retain the above copyright notice, 00012 * this list of conditions and the disclaimer below. 00013 * 00014 * Atmel's name may not be used to endorse or promote products derived from 00015 * this software without specific prior written permission. 00016 * 00017 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR 00018 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00019 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE 00020 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, 00021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00022 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 00023 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00024 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00025 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 00026 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00027 * ---------------------------------------------------------------------------- 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( pTc->TC_CHANNEL[0] )) ) ; 00097 pTcCh = pTc->TC_CHANNEL+dwChannel ; 00098 00099 /* Disable TC clock */ 00100 pTcCh->TC_CCR = TC_CCR_CLKDIS ; 00101 00102 /* Disable interrupts */ 00103 pTcCh->TC_IDR = 0xFFFFFFFF ; 00104 00105 /* Clear status register */ 00106 pTcCh->TC_SR ; 00107 00108 /* Set mode */ 00109 pTcCh->TC_CMR = dwMode ; 00110 } 00111 00112 /** 00113 * \brief Reset and Start the TC Channel 00114 * 00115 * Enables the timer clock and performs a software reset to start the counting. 00116 * 00117 * \param pTc Pointer to a Tc instance. 00118 * \param dwChannel Channel number. 00119 */ 00120 extern void TC_Start( Tc *pTc, uint32_t dwChannel ) 00121 { 00122 TcChannel* pTcCh ; 00123 00124 assert( dwChannel < (sizeof( pTc->TC_CHANNEL )/sizeof( pTc->TC_CHANNEL[0] )) ) ; 00125 00126 pTcCh = pTc->TC_CHANNEL+dwChannel ; 00127 pTcCh->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ; 00128 } 00129 00130 /** 00131 * \brief Stop TC Channel 00132 * 00133 * Disables the timer clock, stopping the counting. 00134 * 00135 * \param pTc Pointer to a Tc instance. 00136 * \param dwChannel Channel number. 00137 */ 00138 extern void TC_Stop(Tc *pTc, uint32_t dwChannel ) 00139 { 00140 TcChannel* pTcCh ; 00141 00142 assert( dwChannel < (sizeof( pTc->TC_CHANNEL )/sizeof( pTc->TC_CHANNEL[0] )) ) ; 00143 00144 pTcCh = pTc->TC_CHANNEL+dwChannel ; 00145 pTcCh->TC_CCR = TC_CCR_CLKDIS ; 00146 } 00147 00148 /** 00149 * \brief Find best MCK divisor 00150 * 00151 * Finds the best MCK divisor given the timer frequency and MCK. The result 00152 * is guaranteed to satisfy the following equation: 00153 * \code 00154 * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV) 00155 * \endcode 00156 * with DIV being the highest possible value. 00157 * 00158 * \param dwFreq Desired timer frequency. 00159 * \param dwMCk Master clock frequency. 00160 * \param dwDiv Divisor value. 00161 * \param dwTcClks TCCLKS field value for divisor. 00162 * \param dwBoardMCK Board clock frequency. 00163 * 00164 * \return 1 if a proper divisor has been found, otherwise 0. 00165 */ 00166 extern uint32_t TC_FindMckDivisor( uint32_t dwFreq, uint32_t dwMCk, 00167 uint32_t *dwDiv, uint32_t *dwTcClks, uint32_t dwBoardMCK ) 00168 { 00169 const uint32_t adwDivisors[5] = { 2, 8, 32, 128, BOARD_MCK / 32768 } ; 00170 00171 uint32_t dwIndex = 0 ; 00172 dwBoardMCK = dwBoardMCK; 00173 /* Satisfy lower bound */ 00174 while ( dwFreq < ((dwMCk / adwDivisors[dwIndex]) / 65536) ) { 00175 dwIndex++ ; 00176 00177 /* If no divisor can be found, return 0 */ 00178 if ( dwIndex == (sizeof( adwDivisors )/sizeof( adwDivisors[0] )) ) { 00179 return 0 ; 00180 } 00181 } 00182 00183 /* Try to maximize DIV while satisfying upper bound */ 00184 while ( dwIndex < 4 ) { 00185 if ( dwFreq > (dwMCk / adwDivisors[dwIndex + 1]) ) { 00186 break ; 00187 } 00188 dwIndex++ ; 00189 } 00190 00191 /* Store results */ 00192 if ( dwDiv ) { 00193 *dwDiv = adwDivisors[dwIndex] ; 00194 } 00195 if ( dwTcClks ) { 00196 *dwTcClks = dwIndex ; 00197 } 00198 return 1 ; 00199 } 00200