1 /* ----------------------------------------------------------------------------
\r
2 * SAM Software Package License
\r
3 * ----------------------------------------------------------------------------
\r
4 * Copyright (c) 2011, 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 /** \addtogroup pwm_module Working with PWM
\r
31 * \ingroup peripherals_module
\r
32 * The PWM driver provides the interface to configure and use the PWM
\r
35 * The PWM macrocell controls square output waveforms of 4 channels.
\r
36 * Characteristics of output waveforms such as period, duty-cycle,
\r
37 * dead-time can be configured.\n
\r
38 * Some of PWM channels can be linked together as synchronous channel and
\r
39 * duty-cycle of synchronous channels can be updated by PDC automaticly.
\r
41 * Before enabling the channels, they must have been configured first.
\r
42 * The main settings include:
\r
44 * <li>Configuration of the clock generator.</li>
\r
45 * <li>Selection of the clock for each channel.</li>
\r
46 * <li>Configuration of output waveform characteristics, such as period, duty-cycle etc.</li>
\r
47 * <li>Configuration for synchronous channels if needed.</li>
\r
48 * - Selection of the synchronous channels.
\r
49 * - Selection of the moment when the WRDY flag and the corresponding PDC
\r
50 * transfer request are set (PTRM and PTRCS in the PWM_SCM register).
\r
51 * - Configuration of the update mode (UPDM in the PWM_SCM register).
\r
52 * - Configuration of the update period (UPR in the PWM_SCUP register).
\r
55 * After the channels is enabled, the user must use respective update registers
\r
56 * to change the wave characteristics to prevent unexpected output waveform.
\r
57 * i.e. PWM_CDTYUPDx register should be used if user want to change duty-cycle
\r
58 * when the channel is enabled.
\r
60 * For more accurate information, please look at the PWM section of the
\r
73 * Implementation of the Pulse Width Modulation Controller (PWM) peripheral.
\r
77 /*----------------------------------------------------------------------------
\r
79 *----------------------------------------------------------------------------*/
\r
86 /*----------------------------------------------------------------------------
\r
88 *----------------------------------------------------------------------------*/
\r
91 * \brief Finds a prescaler/divisor couple to generate the desired frequency
\r
94 * Returns the value to enter in PWM_CLK or 0 if the configuration cannot be
\r
97 * \param frequency Desired frequency in Hz.
\r
98 * \param mck Master clock frequency in Hz.
\r
100 static uint16_t FindClockConfiguration(
\r
101 uint32_t frequency,
\r
104 uint32_t divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
\r
105 uint8_t divisor = 0;
\r
106 uint32_t prescaler;
\r
108 assert(frequency < mck);
\r
110 /* Find prescaler and divisor values */
\r
111 prescaler = (mck / divisors[divisor]) / frequency;
\r
112 while ((prescaler > 255) && (divisor < 11)) {
\r
115 prescaler = (mck / divisors[divisor]) / frequency;
\r
118 /* Return result */
\r
119 if ( divisor < 11 )
\r
121 TRACE_DEBUG( "Found divisor=%u and prescaler=%u for freq=%uHz\n\r", divisors[divisor], prescaler, frequency ) ;
\r
123 return prescaler | (divisor << 8) ;
\r
131 /*----------------------------------------------------------------------------
\r
132 * Exported functions
\r
133 *----------------------------------------------------------------------------*/
\r
136 * \brief Configures PWM a channel with the given parameters, basic configure function.
\r
138 * The PWM controller must have been clocked in the PMC prior to calling this
\r
140 * Beware: this function disables the channel. It waits until disable is effective.
\r
142 * \param channel Channel number.
\r
143 * \param prescaler Channel prescaler.
\r
144 * \param alignment Channel alignment.
\r
145 * \param polarity Channel polarity.
\r
147 void PWMC_ConfigureChannel(
\r
150 uint32_t prescaler,
\r
151 uint32_t alignment,
\r
154 pPwm->PWM_CH_NUM[0].PWM_CMR = 1;
\r
156 // assert(prescaler < PWM_CMR0_CPRE_MCKB);
\r
157 assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
\r
158 assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
\r
160 /* Disable channel (effective at the end of the current period) */
\r
161 if ((pPwm->PWM_SR & (1 << channel)) != 0) {
\r
162 pPwm->PWM_DIS = 1 << channel;
\r
163 while ((pPwm->PWM_SR & (1 << channel)) != 0);
\r
166 /* Configure channel */
\r
167 pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity;
\r
171 * \brief Configures PWM a channel with the given parameters, extend configure function.
\r
173 * The PWM controller must have been clocked in the PMC prior to calling this
\r
175 * Beware: this function disables the channel. It waits until disable is effective.
\r
177 * \param channel Channel number.
\r
178 * \param prescaler Channel prescaler.
\r
179 * \param alignment Channel alignment.
\r
180 * \param polarity Channel polarity.
\r
181 * \param countEventSelect Channel counter event selection.
\r
182 * \param DTEnable Channel dead time generator enable.
\r
183 * \param DTHInverte Channel Dead-Time PWMHx output Inverted.
\r
184 * \param DTLInverte Channel Dead-Time PWMHx output Inverted.
\r
186 void PWMC_ConfigureChannelExt(
\r
189 uint32_t prescaler,
\r
190 uint32_t alignment,
\r
192 uint32_t countEventSelect,
\r
194 uint32_t DTHInverte,
\r
195 uint32_t DTLInverte)
\r
197 // assert(prescaler < PWM_CMR0_CPRE_MCKB);
\r
198 assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
\r
199 assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
\r
200 assert((countEventSelect & (uint32_t)~PWM_CMR_CES) == 0);
\r
201 assert((DTEnable & (uint32_t)~PWM_CMR_DTE) == 0);
\r
202 assert((DTHInverte & (uint32_t)~PWM_CMR_DTHI) == 0);
\r
203 assert((DTLInverte & (uint32_t)~PWM_CMR_DTLI) == 0);
\r
205 /* Disable channel (effective at the end of the current period) */
\r
206 if ((pPwm->PWM_SR & (1 << channel)) != 0) {
\r
207 pPwm->PWM_DIS = 1 << channel;
\r
208 while ((pPwm->PWM_SR & (1 << channel)) != 0);
\r
211 /* Configure channel */
\r
212 pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity |
\r
213 countEventSelect | DTEnable | DTHInverte | DTLInverte;
\r
217 * \brief Configures PWM clocks A & B to run at the given frequencies.
\r
219 * This function finds the best MCK divisor and prescaler values automatically.
\r
221 * \param clka Desired clock A frequency (0 if not used).
\r
222 * \param clkb Desired clock B frequency (0 if not used).
\r
223 * \param mck Master clock frequency.
\r
225 void PWMC_ConfigureClocks(Pwm* pPwm, uint32_t clka, uint32_t clkb, uint32_t mck)
\r
233 result = FindClockConfiguration(clka, mck);
\r
234 assert( result != 0 ) ;
\r
241 result = FindClockConfiguration(clkb, mck);
\r
242 assert( result != 0 ) ;
\r
243 mode |= (result << 16);
\r
246 /* Configure clocks */
\r
247 TRACE_DEBUG( "Setting PWM_CLK = 0x%08X\n\r", mode ) ;
\r
248 pPwm->PWM_CLK = mode;
\r
252 * \brief Sets the period value used by a PWM channel.
\r
254 * This function writes directly to the CPRD register if the channel is disabled;
\r
255 * otherwise, it uses the update register CPRDUPD.
\r
257 * \param channel Channel number.
\r
258 * \param period Period value.
\r
260 void PWMC_SetPeriod( Pwm* pPwm, uint8_t channel, uint16_t period)
\r
262 /* If channel is disabled, write to CPRD */
\r
263 if ((pPwm->PWM_SR & (1 << channel)) == 0) {
\r
265 pPwm->PWM_CH_NUM[channel].PWM_CPRD = period;
\r
267 /* Otherwise use update register */
\r
270 pPwm->PWM_CH_NUM[channel].PWM_CPRDUPD = period;
\r
275 * \brief Sets the duty cycle used by a PWM channel.
\r
276 * This function writes directly to the CDTY register if the channel is disabled;
\r
277 * otherwise it uses the update register CDTYUPD.
\r
278 * Note that the duty cycle must always be inferior or equal to the channel
\r
281 * \param channel Channel number.
\r
282 * \param duty Duty cycle value.
\r
284 void PWMC_SetDutyCycle( Pwm* pPwm, uint8_t channel, uint16_t duty)
\r
286 assert(duty <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
\r
288 /* If channel is disabled, write to CDTY */
\r
289 if ((pPwm->PWM_SR & (1 << channel)) == 0) {
\r
291 pPwm->PWM_CH_NUM[channel].PWM_CDTY = duty;
\r
293 /* Otherwise use update register */
\r
296 pPwm->PWM_CH_NUM[channel].PWM_CDTYUPD = duty;
\r
301 * \brief Sets the dead time used by a PWM channel.
\r
302 * This function writes directly to the DT register if the channel is disabled;
\r
303 * otherwise it uses the update register DTUPD.
\r
304 * Note that the dead time must always be inferior or equal to the channel
\r
307 * \param channel Channel number.
\r
308 * \param timeH Dead time value for PWMHx output.
\r
309 * \param timeL Dead time value for PWMLx output.
\r
311 void PWMC_SetDeadTime( Pwm* pPwm, uint8_t channel, uint16_t timeH, uint16_t timeL)
\r
313 assert(timeH <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
\r
314 assert(timeL <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
\r
316 /* If channel is disabled, write to DT */
\r
317 if ((pPwm->PWM_SR & (1 << channel)) == 0) {
\r
319 pPwm->PWM_CH_NUM[channel].PWM_DT = timeH | (timeL << 16);
\r
321 /* Otherwise use update register */
\r
323 pPwm->PWM_CH_NUM[channel].PWM_DTUPD = timeH | (timeL << 16);
\r
328 * \brief Configures Syncronous channel with the given parameters.
\r
329 * Beware: At this time, the channels should be disabled.
\r
331 * \param channels Bitwise OR of Syncronous channels.
\r
332 * \param updateMode Syncronous channel update mode.
\r
333 * \param requestMode PDC transfer request mode.
\r
334 * \param requestComparisonSelect PDC transfer request comparison selection.
\r
336 void PWMC_ConfigureSyncChannel( Pwm* pPwm,
\r
338 uint32_t updateMode,
\r
339 uint32_t requestMode,
\r
340 uint32_t requestComparisonSelect)
\r
342 pPwm->PWM_SCM = channels | updateMode | requestMode | requestComparisonSelect;
\r
346 * \brief Sets the update period of the synchronous channels.
\r
347 * This function writes directly to the SCUP register if the channel #0 is disabled;
\r
348 * otherwise it uses the update register SCUPUPD.
\r
350 * \param period update period.
\r
352 void PWMC_SetSyncChannelUpdatePeriod( Pwm* pPwm, uint8_t period)
\r
354 /* If channel is disabled, write to SCUP */
\r
355 if ((pPwm->PWM_SR & (1 << 0)) == 0) {
\r
357 pPwm->PWM_SCUP = period;
\r
359 /* Otherwise use update register */
\r
362 pPwm->PWM_SCUPUPD = period;
\r
367 * \brief Sets synchronous channels update unlock.
\r
369 * Note: If the UPDM field is set to 0, writing the UPDULOCK bit to 1
\r
370 * triggers the update of the period value, the duty-cycle and
\r
371 * the dead-time values of synchronous channels at the beginning
\r
372 * of the next PWM period. If the field UPDM is set to 1 or 2,
\r
373 * writing the UPDULOCK bit to 1 triggers only the update of
\r
374 * the period value and of the dead-time values of synchronous channels.
\r
375 * This bit is automatically reset when the update is done.
\r
377 void PWMC_SetSyncChannelUpdateUnlock( Pwm* pPwm )
\r
379 pPwm->PWM_SCUC = PWM_SCUC_UPDULOCK;
\r
383 * \brief Enables the given PWM channel.
\r
385 * This does NOT enable the corresponding pin;this must be done in the user code.
\r
387 * \param channel Channel number.
\r
389 void PWMC_EnableChannel( Pwm* pPwm, uint8_t channel)
\r
391 pPwm->PWM_ENA = 1 << channel;
\r
395 * \brief Disables the given PWM channel.
\r
397 * Beware, channel will be effectively disabled at the end of the current period.
\r
398 * Application can check channel is disabled using the following wait loop:
\r
399 * while ((PWM->PWM_SR & (1 << channel)) != 0);
\r
401 * \param channel Channel number.
\r
403 void PWMC_DisableChannel( Pwm* pPwm, uint8_t channel)
\r
405 pPwm->PWM_DIS = 1 << channel;
\r
409 * \brief Enables the period interrupt for the given PWM channel.
\r
411 * \param channel Channel number.
\r
413 void PWMC_EnableChannelIt( Pwm* pPwm, uint8_t channel)
\r
415 pPwm->PWM_IER1 = 1 << channel;
\r
419 * \brief Return PWM Interrupt Status2 Register
\r
422 uint32_t PWMC_GetStatus2( Pwm* pPwm)
\r
424 return pPwm->PWM_ISR2;
\r
428 * \brief Disables the period interrupt for the given PWM channel.
\r
430 * \param channel Channel number.
\r
432 void PWMC_DisableChannelIt( Pwm* pPwm, uint8_t channel)
\r
434 pPwm->PWM_IDR1 = 1 << channel;
\r
438 * \brief Enables the selected interrupts sources on a PWMC peripheral.
\r
440 * \param sources1 Bitwise OR of selected interrupt sources of PWM_IER1.
\r
441 * \param sources2 Bitwise OR of selected interrupt sources of PWM_IER2.
\r
443 void PWMC_EnableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
\r
445 pPwm->PWM_IER1 = sources1;
\r
446 pPwm->PWM_IER2 = sources2;
\r
450 * \brief Disables the selected interrupts sources on a PWMC peripheral.
\r
452 * \param sources1 Bitwise OR of selected interrupt sources of PWM_IDR1.
\r
453 * \param sources2 Bitwise OR of selected interrupt sources of PWM_IDR2.
\r
455 void PWMC_DisableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
\r
457 pPwm->PWM_IDR1 = sources1;
\r
458 pPwm->PWM_IDR2 = sources2;
\r
462 * \brief Set PWM output override value.
\r
464 * \param value Bitwise OR of output override value.
\r
466 void PWMC_SetOverrideValue( Pwm* pPwm, uint32_t value)
\r
468 pPwm->PWM_OOV = value;
\r
472 * \brief Enalbe override output.
\r
474 * \param value Bitwise OR of output selection.
\r
475 * \param sync 0: enable the output asyncronously, 1: enable it syncronously
\r
477 void PWMC_EnableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
\r
481 pPwm->PWM_OSSUPD = value;
\r
484 pPwm->PWM_OSS = value;
\r
489 * \brief Output Selection for override PWM output.
\r
491 * \param value Bitwise OR of output override value.
\r
493 void PWMC_OutputOverrideSelection( Pwm* pPwm, uint32_t value )
\r
495 pPwm->PWM_OS = value;
\r
500 * \brief Disalbe override output.
\r
502 * \param value Bitwise OR of output selection.
\r
503 * \param sync 0: enable the output asyncronously, 1: enable it syncronously
\r
505 void PWMC_DisableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
\r
509 pPwm->PWM_OSCUPD = value;
\r
512 pPwm->PWM_OSC = value;
\r
517 * \brief Set PWM fault mode.
\r
519 * \param mode Bitwise OR of fault mode.
\r
521 void PWMC_SetFaultMode( Pwm* pPwm, uint32_t mode)
\r
523 pPwm->PWM_FMR = mode;
\r
527 * \brief PWM fault clear.
\r
529 * \param fault Bitwise OR of fault to clear.
\r
531 void PWMC_FaultClear( Pwm* pPwm, uint32_t fault)
\r
533 pPwm->PWM_FCR = fault;
\r
537 * \brief Set PWM fault protection value.
\r
539 * \param value Bitwise OR of fault protection value.
\r
541 void PWMC_SetFaultProtectionValue( Pwm* pPwm, uint32_t value)
\r
543 pPwm->PWM_FPV1 = value;
\r
547 * \brief Enable PWM fault protection.
\r
549 * \param value Bitwise OR of FPEx[y].
\r
551 void PWMC_EnableFaultProtection( Pwm* pPwm, uint32_t value)
\r
553 pPwm->PWM_FPE = value;
\r
557 * \brief Configure comparison unit.
\r
559 * \param x comparison x index
\r
560 * \param value comparison x value.
\r
561 * \param mode comparison x mode
\r
563 void PWMC_ConfigureComparisonUnit( Pwm* pPwm, uint32_t x, uint32_t value, uint32_t mode)
\r
567 /* If channel is disabled, write to CMPxM & CMPxV */
\r
568 if ((pPwm->PWM_SR & (1 << 0)) == 0) {
\r
569 pPwm->PWM_CMP[x].PWM_CMPM = mode;
\r
570 pPwm->PWM_CMP[x].PWM_CMPV = value;
\r
572 /* Otherwise use update register */
\r
574 pPwm->PWM_CMP[x].PWM_CMPMUPD = mode;
\r
575 pPwm->PWM_CMP[x].PWM_CMPVUPD = value;
\r
580 * \brief Configure event line mode.
\r
583 * \param mode Bitwise OR of line mode selection
\r
585 void PWMC_ConfigureEventLineMode( Pwm* pPwm, uint32_t x, uint32_t mode)
\r
590 pPwm->PWM_ELMR[0] = mode;
\r
591 } else if (x == 1) {
\r
592 pPwm->PWM_ELMR[1] = mode;
\r