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_DEBUG("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 /// Beware: this function disables the channel. It waits until disable is effective.
\r
91 /// \param channel Channel number.
\r
92 /// \param prescaler Channel prescaler.
\r
93 /// \param alignment Channel alignment.
\r
94 /// \param polarity Channel polarity.
\r
95 //------------------------------------------------------------------------------
\r
96 void PWMC_ConfigureChannel(
\r
97 unsigned char channel,
\r
98 unsigned int prescaler,
\r
99 unsigned int alignment,
\r
100 unsigned int polarity)
\r
102 SANITY_CHECK(prescaler < AT91C_PWMC_CPRE_MCKB);
\r
103 SANITY_CHECK((alignment & ~AT91C_PWMC_CALG) == 0);
\r
104 SANITY_CHECK((polarity & ~AT91C_PWMC_CPOL) == 0);
\r
106 // Disable channel (effective at the end of the current period)
\r
107 if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) != 0) {
\r
108 AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;
\r
109 while ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) != 0);
\r
112 // Configure channel
\r
113 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR = prescaler | alignment | polarity;
\r
116 //------------------------------------------------------------------------------
\r
117 /// Configures PWM clocks A & B to run at the given frequencies. This function
\r
118 /// finds the best MCK divisor and prescaler values automatically.
\r
119 /// \param clka Desired clock A frequency (0 if not used).
\r
120 /// \param clkb Desired clock B frequency (0 if not used).
\r
121 /// \param mck Master clock frequency.
\r
122 //------------------------------------------------------------------------------
\r
123 void PWMC_ConfigureClocks(unsigned int clka, unsigned int clkb, unsigned int mck)
\r
125 unsigned int mode = 0;
\r
126 unsigned int result;
\r
131 result = FindClockConfiguration(clka, mck);
\r
132 ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clka);
\r
139 result = FindClockConfiguration(clkb, mck);
\r
140 ASSERT(result != 0, "-F- Could not generate the desired PWM frequency (%uHz)\n\r", clkb);
\r
141 mode |= (result << 16);
\r
144 // Configure clocks
\r
145 TRACE_DEBUG("Setting PWMC_MR = 0x%08X\n\r", mode);
\r
146 AT91C_BASE_PWMC->PWMC_MR = mode;
\r
149 //------------------------------------------------------------------------------
\r
150 /// Sets the period value used by a PWM channel. This function writes directly
\r
151 /// to the CPRD register if the channel is disabled; otherwise, it uses the
\r
152 /// update register CUPD.
\r
153 /// \param channel Channel number.
\r
154 /// \param period Period value.
\r
155 //------------------------------------------------------------------------------
\r
156 void PWMC_SetPeriod(unsigned char channel, unsigned short period)
\r
158 // If channel is disabled, write to CPRD
\r
159 if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {
\r
161 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR = period;
\r
163 // Otherwise use update register
\r
166 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR |= AT91C_PWMC_CPD;
\r
167 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = period;
\r
171 //------------------------------------------------------------------------------
\r
172 /// Sets the duty cycle used by a PWM channel. This function writes directly to
\r
173 /// the CDTY register if the channel is disabled; otherwise it uses the
\r
174 /// update register CUPD.
\r
175 /// Note that the duty cycle must always be inferior or equal to the channel
\r
177 /// \param channel Channel number.
\r
178 /// \param duty Duty cycle value.
\r
179 //------------------------------------------------------------------------------
\r
180 void PWMC_SetDutyCycle(unsigned char channel, unsigned short duty)
\r
182 SANITY_CHECK(duty <= AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CPRDR);
\r
185 #if defined(at91sam7s16) || defined(at91sam7s161) || defined(at91sam7s32) \
\r
186 || defined(at91sam7s321) || defined(at91sam7s64) || defined(at91sam7s128) \
\r
187 || defined(at91sam7s256) || defined(at91sam7s512)
\r
188 ASSERT(duty > 0, "-F- Duty cycle value 0 is not permitted on SAM7S chips.\n\r");
\r
189 ASSERT((duty > 1) || (AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR & AT91C_PWMC_CALG),
\r
190 "-F- Duty cycle value 1 is not permitted in left-aligned mode on SAM7S chips.\n\r");
\r
193 // If channel is disabled, write to CDTY
\r
194 if ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) == 0) {
\r
196 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CDTYR = duty;
\r
198 // Otherwise use update register
\r
201 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CMR &= ~AT91C_PWMC_CPD;
\r
202 AT91C_BASE_PWMC->PWMC_CH[channel].PWMC_CUPDR = duty;
\r
206 //------------------------------------------------------------------------------
\r
207 /// Enables the given PWM channel. This does NOT enable the corresponding pin;
\r
208 /// this must be done in the user code.
\r
209 /// \param channel Channel number.
\r
210 //------------------------------------------------------------------------------
\r
211 void PWMC_EnableChannel(unsigned char channel)
\r
213 AT91C_BASE_PWMC->PWMC_ENA = 1 << channel;
\r
216 //------------------------------------------------------------------------------
\r
217 /// Disables the given PWM channel.
\r
218 /// Beware, channel will be effectively disabled at the end of the current period.
\r
219 /// Application can check channel is disabled using the following wait loop:
\r
220 /// while ((AT91C_BASE_PWMC->PWMC_SR & (1 << channel)) != 0);
\r
221 /// \param channel Channel number.
\r
222 //------------------------------------------------------------------------------
\r
223 void PWMC_DisableChannel(unsigned char channel)
\r
225 AT91C_BASE_PWMC->PWMC_DIS = 1 << channel;
\r
228 //------------------------------------------------------------------------------
\r
229 /// Enables the period interrupt for the given PWM channel.
\r
230 /// \param channel Channel number.
\r
231 //------------------------------------------------------------------------------
\r
232 void PWMC_EnableChannelIt(unsigned char channel)
\r
234 AT91C_BASE_PWMC->PWMC_IER = 1 << channel;
\r
237 //------------------------------------------------------------------------------
\r
238 /// Disables the period interrupt for the given PWM channel.
\r
239 /// \param channel Channel number.
\r
240 //------------------------------------------------------------------------------
\r
241 void PWMC_DisableChannelIt(unsigned char channel)
\r
243 AT91C_BASE_PWMC->PWMC_IDR = 1 << channel;
\r