SAMV71 Xplained Ultra Software Package 1.4

pwmc.c

Go to the documentation of this file.
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 pwm_module Working with PWM
00031  *  \ingroup peripherals_module
00032  * The PWM driver provides the interface to configure and use the PWM
00033  * peripheral.
00034  *
00035  * The PWM macrocell controls square output waveforms of 4 channels.
00036  * Characteristics of output waveforms such as period, duty-cycle,
00037  * dead-time can be configured.\n
00038  * Some of PWM channels can be linked together as synchronous channel and
00039  * duty-cycle of synchronous channels can be updated by PDC automatically.
00040  *
00041  * Before enabling the channels, they must have been configured first.
00042  * The main settings include:
00043  * <ul>
00044  * <li>Configuration of the clock generator.</li>
00045  * <li>Selection of the clock for each channel.</li>
00046  * <li>Configuration of output waveform characteristics, such as period, 
00047  * duty-cycle etc.</li>
00048  * <li>Configuration for synchronous channels if needed.</li>
00049  *    - Selection of the synchronous channels.
00050  *    - Selection of the moment when the WRDY flag and the corresponding PDC
00051  *      transfer request are set (PTRM and PTRCS in the PWM_SCM register).
00052  *    - Configuration of the update mode (UPDM in the PWM_SCM register).
00053  *    - Configuration of the update period (UPR in the PWM_SCUP register).
00054  * </ul>
00055  *
00056  * After the channels is enabled, the user must use respective update registers
00057  * to change the wave characteristics to prevent unexpected output waveform.
00058  * i.e. PWM_CDTYUPDx register should be used if user want to change duty-cycle
00059  * when the channel is enabled.
00060  *
00061  * For more accurate information, please look at the PWM section of the
00062  * Datasheet.
00063  *
00064  * Related files :\n
00065  * \ref pwmc.c\n
00066  * \ref pwmc.h.\n
00067  */
00068 /*@{*/
00069 /*@}*/
00070 
00071 /**
00072  * \file
00073  *
00074  * Implementation of the Pulse Width Modulation Controller (PWM) peripheral.
00075  *
00076  */
00077 
00078 /*----------------------------------------------------------------------------
00079  *        Headers
00080  *----------------------------------------------------------------------------*/
00081 
00082 #include "chip.h"
00083 
00084 #include <stdint.h>
00085 #include <assert.h>
00086 
00087 /*----------------------------------------------------------------------------
00088  *         Local functions
00089  *----------------------------------------------------------------------------*/
00090 
00091 /**
00092  * \brief Finds a prescaler/divisor couple to generate the desired frequency
00093  * from MCK.
00094  *
00095  * Returns the value to enter in PWM_CLK or 0 if the configuration cannot be
00096  * met.
00097  *
00098  * \param frequency  Desired frequency in Hz.
00099  * \param mck  Master clock frequency in Hz.
00100  */
00101 static uint16_t FindClockConfiguration(
00102         uint32_t frequency,
00103         uint32_t mck)
00104 {
00105     uint32_t divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
00106     uint8_t divisor = 0;
00107     uint32_t prescaler;
00108 
00109     assert(frequency < mck);
00110 
00111     /* Find prescaler and divisor values */
00112     prescaler = (mck / divisors[divisor]) / frequency;
00113     while ((prescaler > 255) && (divisor < 11)) {
00114         divisor++;
00115         prescaler = (mck / divisors[divisor]) / frequency;
00116     }
00117 
00118     /* Return result */
00119     if ( divisor < 11 ) {
00120         TRACE_DEBUG( "Found divisor=%u and prescaler=%u for freq=%uHz\n\r",
00121                 divisors[divisor], prescaler, frequency ) ;
00122 
00123         return prescaler | (divisor << 8) ;
00124     } else {
00125         return 0 ;
00126     }
00127 }
00128 
00129 /*----------------------------------------------------------------------------
00130  *        Exported functions
00131  *----------------------------------------------------------------------------*/
00132 
00133 /**
00134  * \brief Configures PWM a channel with the given parameters, basic configure
00135  * function.
00136  * 
00137  * The PWM controller must have been clocked in the PMC prior to calling this
00138  * function.
00139  * Beware: this function disables the channel. It waits until disable is effective.
00140  *
00141  * \param channel  Channel number.
00142  * \param prescaler  Channel prescaler.
00143  * \param alignment  Channel alignment.
00144  * \param polarity  Channel polarity.
00145  */
00146 void PWMC_ConfigureChannel(
00147         Pwm* pPwm,
00148         uint8_t channel,
00149         uint32_t prescaler,
00150         uint32_t alignment,
00151         uint32_t polarity)
00152 {
00153     pPwm->PWM_CH_NUM[0].PWM_CMR = 1;
00154 
00155     //    assert(prescaler < PWM_CMR0_CPRE_MCKB);
00156     assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
00157     assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
00158 
00159     /* Disable channel (effective at the end of the current period) */
00160     if ((pPwm->PWM_SR & (1 << channel)) != 0) {
00161         pPwm->PWM_DIS = 1 << channel;
00162         while ((pPwm->PWM_SR & (1 << channel)) != 0);
00163     }
00164 
00165     /* Configure channel */
00166     pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity;
00167 }
00168 
00169 /**
00170  * \brief Configures PWM a channel with the given parameters, extend configure 
00171  * function.
00172  * The PWM controller must have been clocked in the PMC prior to calling this
00173  * function.
00174  * Beware: this function disables the channel. It waits until disable is effective.
00175  *
00176  * \param channel            Channel number.
00177  * \param prescaler          Channel prescaler.
00178  * \param alignment          Channel alignment.
00179  * \param polarity           Channel polarity.
00180  * \param countEventSelect   Channel counter event selection.
00181  * \param DTEnable           Channel dead time generator enable.
00182  * \param DTHInverte         Channel Dead-Time PWMHx output Inverted.
00183  * \param DTLInverte         Channel Dead-Time PWMHx output Inverted.
00184  */
00185 void PWMC_ConfigureChannelExt(
00186         Pwm* pPwm,
00187         uint8_t channel,
00188         uint32_t prescaler,
00189         uint32_t alignment,
00190         uint32_t polarity,
00191         uint32_t countEventSelect,
00192         uint32_t DTEnable,
00193         uint32_t DTHInverte,
00194         uint32_t DTLInverte)
00195 {
00196     //    assert(prescaler < PWM_CMR0_CPRE_MCKB);
00197     assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
00198     assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
00199     assert((countEventSelect & (uint32_t)~PWM_CMR_CES) == 0);
00200     assert((DTEnable & (uint32_t)~PWM_CMR_DTE) == 0);
00201     assert((DTHInverte & (uint32_t)~PWM_CMR_DTHI) == 0);
00202     assert((DTLInverte & (uint32_t)~PWM_CMR_DTLI) == 0);
00203 
00204     /* Disable channel (effective at the end of the current period) */
00205     if ((pPwm->PWM_SR & (1 << channel)) != 0) {
00206         pPwm->PWM_DIS = 1 << channel;
00207         while ((pPwm->PWM_SR & (1 << channel)) != 0);
00208     }
00209 
00210     /* Configure channel */
00211     pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity |
00212         countEventSelect | DTEnable | DTHInverte | DTLInverte;
00213 }
00214 
00215 /**
00216  * \brief Configures PWM clocks A & B to run at the given frequencies.
00217  *
00218  * This function finds the best MCK divisor and prescaler values automatically.
00219  *
00220  * \param clka  Desired clock A frequency (0 if not used).
00221  * \param clkb  Desired clock B frequency (0 if not used).
00222  * \param mck  Master clock frequency.
00223  */
00224 void PWMC_ConfigureClocks(Pwm* pPwm, uint32_t clka, uint32_t clkb, uint32_t mck)
00225 {
00226     uint32_t mode = 0;
00227     uint32_t result;
00228 
00229     /* Clock A */
00230     if (clka != 0) {
00231         result = FindClockConfiguration(clka, mck);
00232         assert( result != 0 ) ;
00233         mode |= result;
00234     }
00235 
00236     /* Clock B */
00237     if (clkb != 0) {
00238         result = FindClockConfiguration(clkb, mck);
00239         assert( result != 0 ) ;
00240         mode |= (result << 16);
00241     }
00242 
00243     /* Configure clocks */
00244     TRACE_DEBUG( "Setting PWM_CLK = 0x%08X\n\r", mode ) ;
00245     pPwm->PWM_CLK = mode;
00246 }
00247 
00248 /**
00249  * \brief Sets the period value used by a PWM channel.
00250  *
00251  * This function writes directly to the CPRD register if the channel is disabled;
00252  * otherwise, it uses the update register CPRDUPD.
00253  *
00254  * \param channel Channel number.
00255  * \param period  Period value.
00256  */
00257 void PWMC_SetPeriod( Pwm* pPwm, uint8_t channel, uint16_t period)
00258 {
00259     /* If channel is disabled, write to CPRD */
00260     if ((pPwm->PWM_SR & (1 << channel)) == 0) {
00261 
00262         pPwm->PWM_CH_NUM[channel].PWM_CPRD = period;
00263     } else {
00264     /* Otherwise use update register */
00265         pPwm->PWM_CH_NUM[channel].PWM_CPRDUPD = period;
00266     }
00267 }
00268 
00269 /**
00270  * \brief Sets the duty cycle used by a PWM channel.
00271  * This function writes directly to the CDTY register if the channel is disabled;
00272  * otherwise it uses the update register CDTYUPD.
00273  * Note that the duty cycle must always be inferior or equal to the channel
00274  * period.
00275  *
00276  * \param channel  Channel number.
00277  * \param duty     Duty cycle value.
00278  */
00279 void PWMC_SetDutyCycle( Pwm* pPwm, uint8_t channel, uint16_t duty)
00280 {
00281     assert(duty <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
00282 
00283     /* If channel is disabled, write to CDTY */
00284     if ((pPwm->PWM_SR & (1 << channel)) == 0) {
00285         pPwm->PWM_CH_NUM[channel].PWM_CDTY = duty;
00286     } else {
00287         /* Otherwise use update register */
00288         pPwm->PWM_CH_NUM[channel].PWM_CDTYUPD = duty;
00289     }
00290 }
00291 
00292 /**
00293  * \brief Sets the dead time used by a PWM channel.
00294  * This function writes directly to the DT register if the channel is disabled;
00295  * otherwise it uses the update register DTUPD.
00296  * Note that the dead time must always be inferior or equal to the channel
00297  * period.
00298  *
00299  * \param channel  Channel number.
00300  * \param timeH    Dead time value for PWMHx output.
00301  * \param timeL    Dead time value for PWMLx output.
00302  */
00303 void PWMC_SetDeadTime( Pwm* pPwm, uint8_t channel, uint16_t timeH, uint16_t timeL)
00304 {
00305     assert(timeH <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
00306     assert(timeL <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
00307 
00308     /* If channel is disabled, write to DT */
00309     if ((pPwm->PWM_SR & (1 << channel)) == 0) {
00310         pPwm->PWM_CH_NUM[channel].PWM_DT = timeH | (timeL << 16);
00311     } else {
00312     /* Otherwise use update register */
00313         pPwm->PWM_CH_NUM[channel].PWM_DTUPD = timeH | (timeL << 16);
00314     }
00315 }
00316 
00317 /**
00318  * \brief Configures Synchronous channel with the given parameters.
00319  * Beware: At this time, the channels should be disabled.
00320  *
00321  * \param channels                 Bitwise OR of Synchronous channels.
00322  * \param updateMode               Synchronous channel update mode.
00323  * \param requestMode              PDC transfer request mode.
00324  * \param requestComparisonSelect  PDC transfer request comparison selection.
00325  */
00326 void PWMC_ConfigureSyncChannel( Pwm* pPwm,
00327         uint32_t channels,
00328         uint32_t updateMode,
00329         uint32_t requestMode,
00330         uint32_t requestComparisonSelect)
00331 {
00332     pPwm->PWM_SCM = channels | updateMode | requestMode | requestComparisonSelect;
00333 }
00334 
00335 /**
00336  * \brief Sets the update period of the synchronous channels.
00337  * This function writes directly to the SCUP register if the channel #0 is disabled;
00338  * otherwise it uses the update register SCUPUPD.
00339  *
00340  * \param period   update period.
00341  */
00342 void PWMC_SetSyncChannelUpdatePeriod( Pwm* pPwm, uint8_t period)
00343 {
00344     /* If channel is disabled, write to SCUP */
00345     if ((pPwm->PWM_SR & (1 << 0)) == 0) {
00346         pPwm->PWM_SCUP = period;
00347     } else {
00348     /* Otherwise use update register */
00349         pPwm->PWM_SCUPUPD = period;
00350     }
00351 }
00352 
00353 /**
00354  * \brief Sets synchronous channels update unlock.
00355  *
00356  * Note: If the UPDM field is set to 0, writing the UPDULOCK bit to 1
00357  * triggers the update of the period value, the duty-cycle and
00358  * the dead-time values of synchronous channels at the beginning
00359  * of the next PWM period. If the field UPDM is set to 1 or 2,
00360  * writing the UPDULOCK bit to 1 triggers only the update of
00361  * the period value and of the dead-time values of synchronous channels.
00362  * This bit is automatically reset when the update is done.
00363  */
00364 void PWMC_SetSyncChannelUpdateUnlock( Pwm* pPwm )
00365 {
00366     pPwm->PWM_SCUC = PWM_SCUC_UPDULOCK;
00367 }
00368 
00369 /**
00370  * \brief Enables the given PWM channel.
00371  *
00372  * This does NOT enable the corresponding pin;this must be done in the user code.
00373  *
00374  * \param channel  Channel number.
00375  */
00376 void PWMC_EnableChannel( Pwm* pPwm, uint8_t channel)
00377 {
00378     pPwm->PWM_ENA = 1 << channel;
00379 }
00380 
00381 /**
00382  * \brief Disables the given PWM channel.
00383  *
00384  * Beware, channel will be effectively disabled at the end of the current period.
00385  * Application can check channel is disabled using the following wait loop:
00386  * while ((PWM->PWM_SR & (1 << channel)) != 0);
00387  *
00388  * \param channel  Channel number.
00389  */
00390 void PWMC_DisableChannel( Pwm* pPwm, uint8_t channel)
00391 {
00392     pPwm->PWM_DIS = 1 << channel;
00393 }
00394 
00395 /**
00396  * \brief Enables the period interrupt for the given PWM channel.
00397  *
00398  * \param channel  Channel number.
00399  */
00400 void PWMC_EnableChannelIt( Pwm* pPwm, uint8_t channel)
00401 {
00402     pPwm->PWM_IER1 = 1 << channel;
00403 }
00404 
00405 /**
00406  * \brief Return PWM Interrupt Status2 Register
00407  *
00408  */
00409 uint32_t PWMC_GetStatus2( Pwm* pPwm)
00410 {
00411     return pPwm->PWM_ISR2;
00412 }
00413 
00414 /**
00415  * \brief Disables the period interrupt for the given PWM channel.
00416  *
00417  * \param channel  Channel number.
00418  */
00419 void PWMC_DisableChannelIt( Pwm* pPwm, uint8_t channel)
00420 {
00421     pPwm->PWM_IDR1 = 1 << channel;
00422 }
00423 
00424 /**
00425  * \brief Enables the selected interrupts sources on a PWMC peripheral.
00426  *
00427  * \param sources1  Bitwise OR of selected interrupt sources of PWM_IER1.
00428  * \param sources2  Bitwise OR of selected interrupt sources of PWM_IER2.
00429  */
00430 void PWMC_EnableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
00431 {
00432     pPwm->PWM_IER1 = sources1;
00433     pPwm->PWM_IER2 = sources2;
00434 }
00435 
00436 /**
00437  * \brief Disables the selected interrupts sources on a PWMC peripheral.
00438  *
00439  * \param sources1  Bitwise OR of selected interrupt sources of PWM_IDR1.
00440  * \param sources2  Bitwise OR of selected interrupt sources of PWM_IDR2.
00441  */
00442 void PWMC_DisableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
00443 {
00444     pPwm->PWM_IDR1 = sources1;
00445     pPwm->PWM_IDR2 = sources2;
00446 }
00447 
00448 /**
00449  * \brief Set PWM output override value.
00450  *
00451  * \param value  Bitwise OR of output override value.
00452  */
00453 void PWMC_SetOverrideValue( Pwm* pPwm, uint32_t value)
00454 {
00455     pPwm->PWM_OOV = value;
00456 }
00457 
00458 /**
00459  * \brief Enable override output.
00460  *
00461  * \param value  Bitwise OR of output selection.
00462  * \param sync   0: enable the output asynchronously, 1: enable it synchronously
00463  */
00464 void PWMC_EnableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
00465 {
00466     if (sync) {
00467         pPwm->PWM_OSSUPD = value;
00468     } else {
00469         pPwm->PWM_OSS = value;
00470     }
00471 }
00472 
00473 /**
00474  * \brief Output Selection for override PWM output.
00475  *
00476  * \param value  Bitwise OR of output override value.
00477  */
00478 void PWMC_OutputOverrideSelection( Pwm* pPwm, uint32_t value )
00479 {
00480     pPwm->PWM_OS = value;
00481 }
00482 
00483 
00484 /**
00485  * \brief Disable override output.
00486  *
00487  * \param value  Bitwise OR of output selection.
00488  * \param sync   0: enable the output asynchronously, 1: enable it synchronously
00489  */
00490 void PWMC_DisableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
00491 {
00492     if (sync) {
00493 
00494         pPwm->PWM_OSCUPD = value;
00495     } else {
00496 
00497         pPwm->PWM_OSC = value;
00498     }
00499 }
00500 
00501 /**
00502  * \brief Set PWM fault mode.
00503  *
00504  * \param mode  Bitwise OR of fault mode.
00505  */
00506 void PWMC_SetFaultMode( Pwm* pPwm, uint32_t mode)
00507 {
00508     pPwm->PWM_FMR = mode;
00509 }
00510 
00511 /**
00512  * \brief PWM fault clear.
00513  *
00514  * \param fault  Bitwise OR of fault to clear.
00515  */
00516 void PWMC_FaultClear( Pwm* pPwm, uint32_t fault)
00517 {
00518     pPwm->PWM_FCR = fault;
00519 }
00520 
00521 /**
00522  * \brief Set PWM fault protection value.
00523  *
00524  * \param value  Bitwise OR of fault protection value.
00525  */
00526 void PWMC_SetFaultProtectionValue( Pwm* pPwm, uint32_t value)
00527 {
00528     pPwm->PWM_FPV1 = value;
00529 }
00530 
00531 /**
00532  * \brief Enable PWM fault protection.
00533  *
00534  * \param value  Bitwise OR of FPEx[y].
00535  */
00536 void PWMC_EnableFaultProtection( Pwm* pPwm, uint32_t value)
00537 {
00538     pPwm->PWM_FPE = value;
00539 }
00540 
00541 /**
00542  * \brief Configure comparison unit.
00543  *
00544  * \param x     comparison x index
00545  * \param value comparison x value.
00546  * \param mode  comparison x mode
00547  */
00548 void PWMC_ConfigureComparisonUnit( Pwm* pPwm, uint32_t x, uint32_t value, uint32_t mode)
00549 {
00550     assert(x < 8);
00551 
00552     /* If channel is disabled, write to CMPxM & CMPxV */
00553     if ((pPwm->PWM_SR & (1 << 0)) == 0) {
00554         pPwm->PWM_CMP[x].PWM_CMPM = mode;
00555         pPwm->PWM_CMP[x].PWM_CMPV = value;
00556     } else {
00557         /* Otherwise use update register */
00558         pPwm->PWM_CMP[x].PWM_CMPMUPD = mode;
00559         pPwm->PWM_CMP[x].PWM_CMPVUPD = value;
00560     }
00561 }
00562 
00563 /**
00564  * \brief Configure event line mode.
00565  *
00566  * \param x    Line x
00567  * \param mode Bitwise OR of line mode selection
00568  */
00569 void PWMC_ConfigureEventLineMode( Pwm* pPwm, uint32_t x, uint32_t mode)
00570 {
00571     assert(x < 2);
00572 
00573     if (x == 0) {
00574         pPwm->PWM_ELMR[0] = mode;
00575     } else if (x == 1) {
00576         pPwm->PWM_ELMR[1] = mode;
00577     }
00578 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines