1 /* ----------------------------------------------------------------------------
\r
2 * ATMEL Microcontroller Software Support
\r
3 * ----------------------------------------------------------------------------
\r
4 * Copyright (c) 2008, 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 //------------------------------------------------------------------------------
\r
32 //------------------------------------------------------------------------------
\r
36 #include <utility/assert.h>
\r
37 #include <utility/trace.h>
\r
39 //------------------------------------------------------------------------------
\r
41 //------------------------------------------------------------------------------
\r
43 //------------------------------------------------------------------------------
\r
44 /// Finds a prescaler/divisor couple to generate the desired frequency from
\r
46 /// Returns the value to enter in PWMC_MR or 0 if the configuration cannot be
\r
48 /// \param frequency Desired frequency in Hz.
\r
49 /// \param mck Master clock frequency in Hz.
\r
50 //------------------------------------------------------------------------------
\r
51 static unsigned short FindClockConfiguration(
\r
52 unsigned int frequency,
\r
55 unsigned int divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
\r
56 unsigned char divisor = 0;
\r
57 unsigned int prescaler;
\r
59 SANITY_CHECK(frequency < mck);
\r
61 // Find prescaler and divisor values
\r
62 prescaler = (mck / divisors[divisor]) / frequency;
\r
63 while ((prescaler > 255) && (divisor < 11)) {
\r
66 prescaler = (mck / divisors[divisor]) / frequency;
\r
72 trace_LOG(trace_DEBUG, "-D- Found divisor=%u and prescaler=%u for freq=%uHz\n\r",
\r
73 divisors[divisor], prescaler, frequency);
\r
74 return prescaler | (divisor << 8);
\r
82 //------------------------------------------------------------------------------
\r
84 //------------------------------------------------------------------------------
\r
86 //------------------------------------------------------------------------------
\r
87 /// Configures PWM a channel with the given parameters.
\r
88 /// The PWM controller must have been clocked in the PMC prior to calling this
\r
90 /// \param channel Channel number.
\r
91 /// \param prescaler Channel prescaler.
\r
92 /// \param alignment Channel alignment.
\r
93 /// \param polarity Channel polarity.
\r
94 //------------------------------------------------------------------------------
\r
95 void PWMC_ConfigureChannel(
\r
96 unsigned char channel,
\r
97 unsigned int prescaler,
\r
98 unsigned int alignment,
\r
99 unsigned int polarity)
\r
101 SANITY_CHECK(prescaler < AT91C_PWMC_CPRE_MCKB);
\r
102 SANITY_CHECK((alignment & ~AT91C_PWMC_CALG) == 0);
\r
103 SANITY_CHECK((polarity & ~AT91C_PWMC_CPOL) == 0);
\r
106 AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;
\r
108 // Configure channel
\r
109 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR = prescaler | alignment | polarity;
\r
112 //------------------------------------------------------------------------------
\r
113 /// Configures PWM clocks A & B to run at the given frequencies. This function
\r
114 /// finds the best MCK divisor and prescaler values automatically.
\r
115 /// \param clka Desired clock A frequency (0 if not used).
\r
116 /// \param clkb Desired clock B frequency (0 if not used).
\r
117 /// \param mck Master clock frequency.
\r
118 //------------------------------------------------------------------------------
\r
119 void PWMC_ConfigureClocks(unsigned int clka, unsigned int clkb, unsigned int mck)
\r
121 unsigned int mode = 0;
\r
122 unsigned int result;
\r
127 result = FindClockConfiguration(clka, mck);
\r
128 ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clka);
\r
135 result = FindClockConfiguration(clkb, mck);
\r
136 ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clkb);
\r
137 mode |= (result << 16);
\r
140 // Configure clocks
\r
141 trace_LOG(trace_DEBUG, "-D- Setting PWMC_MR = 0x%08X\n\r", mode);
\r
142 AT91C_BASE_PWMC->PWMC_MR = mode;
\r
145 //------------------------------------------------------------------------------
\r
146 /// Sets the period value used by a PWM channel. This function writes directly
\r
147 /// to the CPRD register if the channel is disabled; otherwise, it uses the
\r
148 /// update register CUPD.
\r
149 /// \param channel Channel number.
\r
150 /// \param period Period value.
\r
151 //------------------------------------------------------------------------------
\r
152 void PWMC_SetPeriod(unsigned char channel, unsigned short period)
\r
154 // If channel is disabled, write to CPRD
\r
155 if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {
\r
157 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR = period;
\r
159 // Otherwise use update register
\r
162 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR |= AT91C_PWMC_CPD;
\r
163 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = period;
\r
167 //------------------------------------------------------------------------------
\r
168 /// Sets the duty cycle used by a PWM channel. This function writes directly to
\r
169 /// the CDTY register if the channel is disabled; otherwise it uses the
\r
170 /// update register CUPD.
\r
171 /// Note that the duty cycle must always be inferior or equal to the channel
\r
173 /// \param channel Channel number.
\r
174 /// \param duty Duty cycle value.
\r
175 //------------------------------------------------------------------------------
\r
176 void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty)
\r
178 SANITY_CHECK(duty <= AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR);
\r
181 #if defined(at91sam7s16) || defined(at91sam7s161) || defined(at91sam7s32) \
\r
182 || defined(at91sam7s321) || defined(at91sam7s64) || defined(at91sam7s128) \
\r
183 || defined(at91sam7s256) || defined(at91sam7s512)
\r
184 ASSERT(duty > 0, "-F- Duty cycle value 0 is not permitted on SAM7S chips.\n\r");
\r
185 ASSERT((duty > 1) || (AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR & AT91C_PWMC_CALG),
\r
186 "-F- Duty cycle value 1 is not permitted in left-aligned mode on SAM7S chips.\n\r");
\r
189 // If channel is disabled, write to CDTY
\r
190 if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {
\r
192 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CDTYR = duty;
\r
194 // Otherwise use update register
\r
197 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR &= ~AT91C_PWMC_CPD;
\r
198 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = duty;
\r
202 //------------------------------------------------------------------------------
\r
203 /// Enables the given PWM channel. This does NOT enable the corresponding pin;
\r
204 /// this must be done in the user code.
\r
205 /// \param channel Channel number.
\r
206 //------------------------------------------------------------------------------
\r
207 void PWMC_EnableChannel(unsigned char channel)
\r
209 AT91C_BASE_PWMC->PWMC_ENA = 1 << channel;
\r
212 //------------------------------------------------------------------------------
\r
213 /// Disables the given PWM channel.
\r
214 /// \param channel Channel number.
\r
215 //------------------------------------------------------------------------------
\r
216 void PWMC_DisableChannel(unsigned char channel)
\r
218 AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;
\r
221 //------------------------------------------------------------------------------
\r
222 /// Enables the period interrupt for the given PWM channel.
\r
223 /// \param channel Channel number.
\r
224 //------------------------------------------------------------------------------
\r
225 void PWMC_EnableChannelIt(unsigned char channel)
\r
227 AT91C_BASE_PWMC->PWMC_IER = 1 << channel;
\r
230 //------------------------------------------------------------------------------
\r
231 /// Disables the period interrupt for the given PWM channel.
\r
232 /// \param channel Channel number.
\r
233 //------------------------------------------------------------------------------
\r
234 void PWMC_DisableChannelIt(unsigned char channel)
\r
236 AT91C_BASE_PWMC->PWMC_IDR = 1 << channel;
\r