SAMV71 Xplained Ultra Software Package 1.5

pwmc.c

Go to the documentation of this file.
00001 /* ---------------------------------------------------------------------------- */
00002 /*                  Atmel Microcontroller Software Support                      */
00003 /*                       SAM Software Package License                           */
00004 /* ---------------------------------------------------------------------------- */
00005 /* Copyright (c) 2015, Atmel Corporation                                        */
00006 /*                                                                              */
00007 /* All rights reserved.                                                         */
00008 /*                                                                              */
00009 /* Redistribution and use in source and binary forms, with or without           */
00010 /* modification, are permitted provided that the following condition is met:    */
00011 /*                                                                              */
00012 /* - Redistributions of source code must retain the above copyright notice,     */
00013 /* this list of conditions and the disclaimer below.                            */
00014 /*                                                                              */
00015 /* Atmel's name may not be used to endorse or promote products derived from     */
00016 /* this software without specific prior written permission.                     */
00017 /*                                                                              */
00018 /* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
00019 /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
00020 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
00021 /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
00022 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
00023 /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
00024 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
00025 /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
00026 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
00027 /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
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 
00114     while ((prescaler > 255) && (divisor < 11)) {
00115         divisor++;
00116         prescaler = (mck / divisors[divisor]) / frequency;
00117     }
00118 
00119     /* Return result */
00120     if (divisor < 11) {
00121         TRACE_DEBUG("Found divisor=%u and prescaler=%u for freq=%uHz\n\r",
00122                      divisors[divisor], prescaler, frequency);
00123 
00124         return prescaler | (divisor << 8);
00125     } else
00126         return 0;
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 
00163         while ((pPwm->PWM_SR & (1 << channel)) != 0);
00164     }
00165 
00166     /* Configure channel */
00167     pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity;
00168 }
00169 
00170 /**
00171  * \brief Configures PWM a channel with the given parameters, extend configure
00172  * function.
00173  * The PWM controller must have been clocked in the PMC prior to calling this
00174  * function.
00175  * Beware: this function disables the channel. It waits until disable is effective.
00176  *
00177  * \param channel            Channel number.
00178  * \param prescaler          Channel prescaler.
00179  * \param alignment          Channel alignment.
00180  * \param polarity           Channel polarity.
00181  * \param countEventSelect   Channel counter event selection.
00182  * \param DTEnable           Channel dead time generator enable.
00183  * \param DTHInverte         Channel Dead-Time PWMHx output Inverted.
00184  * \param DTLInverte         Channel Dead-Time PWMHx output Inverted.
00185  */
00186 void PWMC_ConfigureChannelExt(
00187     Pwm *pPwm,
00188     uint8_t channel,
00189     uint32_t prescaler,
00190     uint32_t alignment,
00191     uint32_t polarity,
00192     uint32_t countEventSelect,
00193     uint32_t DTEnable,
00194     uint32_t DTHInverte,
00195     uint32_t DTLInverte)
00196 {
00197     //    assert(prescaler < PWM_CMR0_CPRE_MCKB);
00198     assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
00199     assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
00200     assert((countEventSelect & (uint32_t)~PWM_CMR_CES) == 0);
00201     assert((DTEnable & (uint32_t)~PWM_CMR_DTE) == 0);
00202     assert((DTHInverte & (uint32_t)~PWM_CMR_DTHI) == 0);
00203     assert((DTLInverte & (uint32_t)~PWM_CMR_DTLI) == 0);
00204 
00205     /* Disable channel (effective at the end of the current period) */
00206     if ((pPwm->PWM_SR & (1 << channel)) != 0) {
00207         pPwm->PWM_DIS = 1 << channel;
00208 
00209         while ((pPwm->PWM_SR & (1 << channel)) != 0);
00210     }
00211 
00212     /* Configure channel */
00213     pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity |
00214                                         countEventSelect | DTEnable | DTHInverte | DTLInverte;
00215 }
00216 
00217 /**
00218  * \brief Configures PWM clocks A & B to run at the given frequencies.
00219  *
00220  * This function finds the best MCK divisor and prescaler values automatically.
00221  *
00222  * \param clka  Desired clock A frequency (0 if not used).
00223  * \param clkb  Desired clock B frequency (0 if not used).
00224  * \param mck  Master clock frequency.
00225  */
00226 void PWMC_ConfigureClocks(Pwm *pPwm, uint32_t clka, uint32_t clkb, uint32_t mck)
00227 {
00228     uint32_t mode = 0;
00229     uint32_t result;
00230 
00231     /* Clock A */
00232     if (clka != 0) {
00233         result = FindClockConfiguration(clka, mck);
00234         assert(result != 0);
00235         mode |= result;
00236     }
00237 
00238     /* Clock B */
00239     if (clkb != 0) {
00240         result = FindClockConfiguration(clkb, mck);
00241         assert(result != 0);
00242         mode |= (result << 16);
00243     }
00244 
00245     /* Configure clocks */
00246     TRACE_DEBUG("Setting PWM_CLK = 0x%08X\n\r", mode);
00247     pPwm->PWM_CLK = mode;
00248 }
00249 
00250 /**
00251  * \brief Sets the period value used by a PWM channel.
00252  *
00253  * This function writes directly to the CPRD register if the channel is disabled;
00254  * otherwise, it uses the update register CPRDUPD.
00255  *
00256  * \param channel Channel number.
00257  * \param period  Period value.
00258  */
00259 void PWMC_SetPeriod(Pwm *pPwm, uint8_t channel, uint16_t period)
00260 {
00261     /* If channel is disabled, write to CPRD */
00262     if ((pPwm->PWM_SR & (1 << channel)) == 0)
00263 
00264         pPwm->PWM_CH_NUM[channel].PWM_CPRD = period;
00265     else {
00266         /* Otherwise use update register */
00267         pPwm->PWM_CH_NUM[channel].PWM_CPRDUPD = period;
00268     }
00269 }
00270 
00271 /**
00272  * \brief Sets the duty cycle used by a PWM channel.
00273  * This function writes directly to the CDTY register if the channel is disabled;
00274  * otherwise it uses the update register CDTYUPD.
00275  * Note that the duty cycle must always be inferior or equal to the channel
00276  * period.
00277  *
00278  * \param channel  Channel number.
00279  * \param duty     Duty cycle value.
00280  */
00281 void PWMC_SetDutyCycle(Pwm *pPwm, uint8_t channel, uint16_t duty)
00282 {
00283     assert(duty <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
00284 
00285     /* If channel is disabled, write to CDTY */
00286     if ((pPwm->PWM_SR & (1 << channel)) == 0)
00287         pPwm->PWM_CH_NUM[channel].PWM_CDTY = duty;
00288     else {
00289         /* Otherwise use update register */
00290         pPwm->PWM_CH_NUM[channel].PWM_CDTYUPD = duty;
00291     }
00292 }
00293 
00294 /**
00295  * \brief Sets the dead time used by a PWM channel.
00296  * This function writes directly to the DT register if the channel is disabled;
00297  * otherwise it uses the update register DTUPD.
00298  * Note that the dead time must always be inferior or equal to the channel
00299  * period.
00300  *
00301  * \param channel  Channel number.
00302  * \param timeH    Dead time value for PWMHx output.
00303  * \param timeL    Dead time value for PWMLx output.
00304  */
00305 void PWMC_SetDeadTime(Pwm *pPwm, uint8_t channel, uint16_t timeH,
00306                        uint16_t timeL)
00307 {
00308     assert(timeH <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
00309     assert(timeL <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
00310 
00311     /* If channel is disabled, write to DT */
00312     if ((pPwm->PWM_SR & (1 << channel)) == 0)
00313         pPwm->PWM_CH_NUM[channel].PWM_DT = timeH | (timeL << 16);
00314     else {
00315         /* Otherwise use update register */
00316         pPwm->PWM_CH_NUM[channel].PWM_DTUPD = timeH | (timeL << 16);
00317     }
00318 }
00319 
00320 /**
00321  * \brief Configures Synchronous channel with the given parameters.
00322  * Beware: At this time, the channels should be disabled.
00323  *
00324  * \param channels                 Bitwise OR of Synchronous channels.
00325  * \param updateMode               Synchronous channel update mode.
00326  * \param requestMode              PDC transfer request mode.
00327  * \param requestComparisonSelect  PDC transfer request comparison selection.
00328  */
00329 void PWMC_ConfigureSyncChannel(Pwm *pPwm,
00330                                 uint32_t channels,
00331                                 uint32_t updateMode,
00332                                 uint32_t requestMode,
00333                                 uint32_t requestComparisonSelect)
00334 {
00335     pPwm->PWM_SCM = channels | updateMode | requestMode | requestComparisonSelect;
00336 }
00337 
00338 /**
00339  * \brief Sets the update period of the synchronous channels.
00340  * This function writes directly to the SCUP register if the channel #0 is disabled;
00341  * otherwise it uses the update register SCUPUPD.
00342  *
00343  * \param period   update period.
00344  */
00345 void PWMC_SetSyncChannelUpdatePeriod(Pwm *pPwm, uint8_t period)
00346 {
00347     /* If channel is disabled, write to SCUP */
00348     if ((pPwm->PWM_SR & (1 << 0)) == 0)
00349         pPwm->PWM_SCUP = period;
00350     else {
00351         /* Otherwise use update register */
00352         pPwm->PWM_SCUPUPD = period;
00353     }
00354 }
00355 
00356 /**
00357  * \brief Sets synchronous channels update unlock.
00358  *
00359  * Note: If the UPDM field is set to 0, writing the UPDULOCK bit to 1
00360  * triggers the update of the period value, the duty-cycle and
00361  * the dead-time values of synchronous channels at the beginning
00362  * of the next PWM period. If the field UPDM is set to 1 or 2,
00363  * writing the UPDULOCK bit to 1 triggers only the update of
00364  * the period value and of the dead-time values of synchronous channels.
00365  * This bit is automatically reset when the update is done.
00366  */
00367 void PWMC_SetSyncChannelUpdateUnlock(Pwm *pPwm)
00368 {
00369     pPwm->PWM_SCUC = PWM_SCUC_UPDULOCK;
00370 }
00371 
00372 /**
00373  * \brief Enables the given PWM channel.
00374  *
00375  * This does NOT enable the corresponding pin;this must be done in the user code.
00376  *
00377  * \param channel  Channel number.
00378  */
00379 void PWMC_EnableChannel(Pwm *pPwm, uint8_t channel)
00380 {
00381     pPwm->PWM_ENA = 1 << channel;
00382 }
00383 
00384 /**
00385  * \brief Disables the given PWM channel.
00386  *
00387  * Beware, channel will be effectively disabled at the end of the current period.
00388  * Application can check channel is disabled using the following wait loop:
00389  * while ((PWM->PWM_SR & (1 << channel)) != 0);
00390  *
00391  * \param channel  Channel number.
00392  */
00393 void PWMC_DisableChannel(Pwm *pPwm, uint8_t channel)
00394 {
00395     pPwm->PWM_DIS = 1 << channel;
00396 }
00397 
00398 /**
00399  * \brief Enables the period interrupt for the given PWM channel.
00400  *
00401  * \param channel  Channel number.
00402  */
00403 void PWMC_EnableChannelIt(Pwm *pPwm, uint8_t channel)
00404 {
00405     pPwm->PWM_IER1 = 1 << channel;
00406 }
00407 
00408 /**
00409  * \brief Return PWM Interrupt Status2 Register
00410  *
00411  */
00412 uint32_t PWMC_GetStatus2(Pwm *pPwm)
00413 {
00414     return pPwm->PWM_ISR2;
00415 }
00416 
00417 /**
00418  * \brief Disables the period interrupt for the given PWM channel.
00419  *
00420  * \param channel  Channel number.
00421  */
00422 void PWMC_DisableChannelIt(Pwm *pPwm, uint8_t channel)
00423 {
00424     pPwm->PWM_IDR1 = 1 << channel;
00425 }
00426 
00427 /**
00428  * \brief Enables the selected interrupts sources on a PWMC peripheral.
00429  *
00430  * \param sources1  Bitwise OR of selected interrupt sources of PWM_IER1.
00431  * \param sources2  Bitwise OR of selected interrupt sources of PWM_IER2.
00432  */
00433 void PWMC_EnableIt(Pwm *pPwm, uint32_t sources1, uint32_t sources2)
00434 {
00435     pPwm->PWM_IER1 = sources1;
00436     pPwm->PWM_IER2 = sources2;
00437 }
00438 
00439 /**
00440  * \brief Disables the selected interrupts sources on a PWMC peripheral.
00441  *
00442  * \param sources1  Bitwise OR of selected interrupt sources of PWM_IDR1.
00443  * \param sources2  Bitwise OR of selected interrupt sources of PWM_IDR2.
00444  */
00445 void PWMC_DisableIt(Pwm *pPwm, uint32_t sources1, uint32_t sources2)
00446 {
00447     pPwm->PWM_IDR1 = sources1;
00448     pPwm->PWM_IDR2 = sources2;
00449 }
00450 
00451 /**
00452  * \brief Set PWM output override value.
00453  *
00454  * \param value  Bitwise OR of output override value.
00455  */
00456 void PWMC_SetOverrideValue(Pwm *pPwm, uint32_t value)
00457 {
00458     pPwm->PWM_OOV = value;
00459 }
00460 
00461 /**
00462  * \brief Enable override output.
00463  *
00464  * \param value  Bitwise OR of output selection.
00465  * \param sync   0: enable the output asynchronously, 1: enable it synchronously
00466  */
00467 void PWMC_EnableOverrideOutput(Pwm *pPwm, uint32_t value, uint32_t sync)
00468 {
00469     if (sync)
00470         pPwm->PWM_OSSUPD = value;
00471     else
00472         pPwm->PWM_OSS = value;
00473 }
00474 
00475 /**
00476  * \brief Output Selection for override PWM output.
00477  *
00478  * \param value  Bitwise OR of output override value.
00479  */
00480 void PWMC_OutputOverrideSelection(Pwm *pPwm, uint32_t value)
00481 {
00482     pPwm->PWM_OS = value;
00483 }
00484 
00485 
00486 /**
00487  * \brief Disable override output.
00488  *
00489  * \param value  Bitwise OR of output selection.
00490  * \param sync   0: enable the output asynchronously, 1: enable it synchronously
00491  */
00492 void PWMC_DisableOverrideOutput(Pwm *pPwm, uint32_t value, uint32_t sync)
00493 {
00494     if (sync)
00495 
00496         pPwm->PWM_OSCUPD = value;
00497     else
00498 
00499         pPwm->PWM_OSC = value;
00500 }
00501 
00502 /**
00503  * \brief Set PWM fault mode.
00504  *
00505  * \param mode  Bitwise OR of fault mode.
00506  */
00507 void PWMC_SetFaultMode(Pwm *pPwm, uint32_t mode)
00508 {
00509     pPwm->PWM_FMR = mode;
00510 }
00511 
00512 /**
00513  * \brief PWM fault clear.
00514  *
00515  * \param fault  Bitwise OR of fault to clear.
00516  */
00517 void PWMC_FaultClear(Pwm *pPwm, uint32_t fault)
00518 {
00519     pPwm->PWM_FCR = fault;
00520 }
00521 
00522 /**
00523  * \brief Set PWM fault protection value.
00524  *
00525  * \param value  Bitwise OR of fault protection value.
00526  */
00527 void PWMC_SetFaultProtectionValue(Pwm *pPwm, uint32_t value)
00528 {
00529     pPwm->PWM_FPV1 = value;
00530 }
00531 
00532 /**
00533  * \brief Enable PWM fault protection.
00534  *
00535  * \param value  Bitwise OR of FPEx[y].
00536  */
00537 void PWMC_EnableFaultProtection(Pwm *pPwm, uint32_t value)
00538 {
00539     pPwm->PWM_FPE = value;
00540 }
00541 
00542 /**
00543  * \brief Configure comparison unit.
00544  *
00545  * \param x     comparison x index
00546  * \param value comparison x value.
00547  * \param mode  comparison x mode
00548  */
00549 void PWMC_ConfigureComparisonUnit(Pwm *pPwm, uint32_t x, uint32_t value,
00550                                    uint32_t mode)
00551 {
00552     assert(x < 8);
00553 
00554     /* If channel is disabled, write to CMPxM & CMPxV */
00555     if ((pPwm->PWM_SR & (1 << 0)) == 0) {
00556         pPwm->PWM_CMP[x].PWM_CMPM = mode;
00557         pPwm->PWM_CMP[x].PWM_CMPV = value;
00558     } else {
00559         /* Otherwise use update register */
00560         pPwm->PWM_CMP[x].PWM_CMPMUPD = mode;
00561         pPwm->PWM_CMP[x].PWM_CMPVUPD = value;
00562     }
00563 }
00564 
00565 /**
00566  * \brief Configure event line mode.
00567  *
00568  * \param x    Line x
00569  * \param mode Bitwise OR of line mode selection
00570  */
00571 void PWMC_ConfigureEventLineMode(Pwm *pPwm, uint32_t x, uint32_t mode)
00572 {
00573     assert(x < 2);
00574 
00575     if (x == 0)
00576         pPwm->PWM_ELMR[0] = mode;
00577     else if (x == 1)
00578         pPwm->PWM_ELMR[1] = mode;
00579 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines