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 }