/MakefileBasedBuild/Atmel/sam3x/sam3x-ek/libraries/libchip_sam3x/source/pwm.c
C | 614 lines | 238 code | 68 blank | 308 comment | 50 complexity | e95d04ca42a2d451db37036c58646de5 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
- /* ----------------------------------------------------------------------------
- * ATMEL Microcontroller Software Support
- * ----------------------------------------------------------------------------
- * Copyright (c) 2009, Atmel Corporation
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the disclaimer below.
- *
- * Atmel's name may not be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
- * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * ----------------------------------------------------------------------------
- */
-
- /** \addtogroup pwm_module Working with PWM
- * The PWM driver provides the interface to configure and use the PWM
- * peripheral.
- *
- * The PWM macrocell controls square output waveforms of 4 channels.
- * Characteristics of output waveforms such as period, duty-cycle,
- * dead-time can be configured.\n
- * Some of PWM channels can be linked together as synchronous channel and
- * duty-cycle of synchronous channels can be updated by PDC automaticly.
- *
- * Before enabling the channels, they must have been configured first.
- * The main settings include:
- * <ul>
- * <li>Configuration of the clock generator.</li>
- * <li>Selection of the clock for each channel.</li>
- * <li>Configuration of output waveform characteristics, such as period, duty-cycle etc.</li>
- * <li>Configuration for synchronous channels if needed.</li>
- * - Selection of the synchronous channels.
- * - Selection of the moment when the WRDY flag and the corresponding PDC
- * transfer request are set (PTRM and PTRCS in the PWM_SCM register).
- * - Configuration of the update mode (UPDM in the PWM_SCM register).
- * - Configuration of the update period (UPR in the PWM_SCUP register).
- * </ul>
- *
- * After the channels is enabled, the user must use respective update registers
- * to change the wave characteristics to prevent unexpected output waveform.
- * i.e. PWM_CDTYUPDx register should be used if user want to change duty-cycle
- * when the channel is enabled.
- *
- * For more accurate information, please look at the PWM section of the
- * Datasheet.
- *
- * Related files :\n
- * \ref pwmc.c\n
- * \ref pwmc.h.\n
- */
- /*@{*/
- /*@}*/
-
- /**
- * \file
- *
- * Implementation of the Pulse Width Modulation Controller (PWM) peripheral.
- *
- */
-
- /*----------------------------------------------------------------------------
- * Headers
- *----------------------------------------------------------------------------*/
-
- #include "chip.h"
-
- #include <stdint.h>
- #include <assert.h>
-
- /*----------------------------------------------------------------------------
- * Local functions
- *----------------------------------------------------------------------------*/
-
- /**
- * \brief Finds a prescaler/divisor couple to generate the desired frequency
- * from MCK.
- *
- * Returns the value to enter in PWM_CLK or 0 if the configuration cannot be
- * met.
- *
- * \param frequency Desired frequency in Hz.
- * \param mck Master clock frequency in Hz.
- */
- static uint16_t FindClockConfiguration(
- uint32_t frequency,
- uint32_t mck)
- {
- uint32_t divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
- uint8_t divisor = 0;
- uint32_t prescaler;
-
- assert(frequency < mck);
-
- /* Find prescaler and divisor values */
- prescaler = (mck / divisors[divisor]) / frequency;
- while ((prescaler > 255) && (divisor < 11)) {
-
- divisor++;
- prescaler = (mck / divisors[divisor]) / frequency;
- }
-
- /* Return result */
- if ( divisor < 11 )
- {
- TRACE_DEBUG( "Found divisor=%u and prescaler=%u for freq=%uHz\n\r", divisors[divisor], prescaler, frequency ) ;
-
- return prescaler | (divisor << 8) ;
- }
- else
- {
- return 0 ;
- }
- }
-
- /*----------------------------------------------------------------------------
- * Exported functions
- *----------------------------------------------------------------------------*/
-
- /**
- * \brief Configures PWM a channel with the given parameters, basic configure function.
- *
- * The PWM controller must have been clocked in the PMC prior to calling this
- * function.
- * Beware: this function disables the channel. It waits until disable is effective.
- *
- * \param channel Channel number.
- * \param prescaler Channel prescaler.
- * \param alignment Channel alignment.
- * \param polarity Channel polarity.
- */
- void PWMC_ConfigureChannel(
- Pwm* pPwm,
- uint8_t channel,
- uint32_t prescaler,
- uint32_t alignment,
- uint32_t polarity)
- {
- pPwm->PWM_CH_NUM[0].PWM_CMR = 1;
-
- // assert(prescaler < PWM_CMR0_CPRE_MCKB);
- assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
- assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
-
- /* Disable channel (effective at the end of the current period) */
- if ((pPwm->PWM_SR & (1 << channel)) != 0) {
- pPwm->PWM_DIS = 1 << channel;
- while ((pPwm->PWM_SR & (1 << channel)) != 0);
- }
-
- /* Configure channel */
- pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity;
- }
-
- /**
- * \brief Configures PWM a channel with the given parameters, extend configure function.
- *
- * The PWM controller must have been clocked in the PMC prior to calling this
- * function.
- * Beware: this function disables the channel. It waits until disable is effective.
- *
- * \param channel Channel number.
- * \param prescaler Channel prescaler.
- * \param alignment Channel alignment.
- * \param polarity Channel polarity.
- * \param countEventSelect Channel counter event selection.
- * \param DTEnable Channel dead time generator enable.
- * \param DTHInverte Channel Dead-Time PWMHx output Inverted.
- * \param DTLInverte Channel Dead-Time PWMHx output Inverted.
- */
- void PWMC_ConfigureChannelExt(
- Pwm* pPwm,
- uint8_t channel,
- uint32_t prescaler,
- uint32_t alignment,
- uint32_t polarity,
- uint32_t countEventSelect,
- uint32_t DTEnable,
- uint32_t DTHInverte,
- uint32_t DTLInverte)
- {
- // assert(prescaler < PWM_CMR0_CPRE_MCKB);
- assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
- assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
- assert((countEventSelect & (uint32_t)~PWM_CMR_CES) == 0);
- assert((DTEnable & (uint32_t)~PWM_CMR_DTE) == 0);
- assert((DTHInverte & (uint32_t)~PWM_CMR_DTHI) == 0);
- assert((DTLInverte & (uint32_t)~PWM_CMR_DTLI) == 0);
-
- /* Disable channel (effective at the end of the current period) */
- if ((pPwm->PWM_SR & (1 << channel)) != 0) {
- pPwm->PWM_DIS = 1 << channel;
- while ((pPwm->PWM_SR & (1 << channel)) != 0);
- }
-
- /* Configure channel */
- pPwm->PWM_CH_NUM[channel].PWM_CMR = prescaler | alignment | polarity |
- countEventSelect | DTEnable | DTHInverte | DTLInverte;
- }
-
- /**
- * \brief Configures PWM clocks A & B to run at the given frequencies.
- *
- * This function finds the best MCK divisor and prescaler values automatically.
- *
- * \param clka Desired clock A frequency (0 if not used).
- * \param clkb Desired clock B frequency (0 if not used).
- * \param mck Master clock frequency.
- */
- void PWMC_ConfigureClocks(uint32_t clka, uint32_t clkb, uint32_t mck)
- {
- uint32_t mode = 0;
- uint32_t result;
-
- /* Clock A */
- if (clka != 0) {
-
- result = FindClockConfiguration(clka, mck);
- assert( result != 0 ) ;
- mode |= result;
- }
-
- /* Clock B */
- if (clkb != 0) {
-
- result = FindClockConfiguration(clkb, mck);
- assert( result != 0 ) ;
- mode |= (result << 16);
- }
-
- /* Configure clocks */
- TRACE_DEBUG( "Setting PWM_CLK = 0x%08X\n\r", mode ) ;
- PWM->PWM_CLK = mode;
- }
-
- /**
- * \brief Sets the period value used by a PWM channel.
- *
- * This function writes directly to the CPRD register if the channel is disabled;
- * otherwise, it uses the update register CPRDUPD.
- *
- * \param channel Channel number.
- * \param period Period value.
- */
- void PWMC_SetPeriod( Pwm* pPwm, uint8_t channel, uint16_t period)
- {
- /* If channel is disabled, write to CPRD */
- if ((pPwm->PWM_SR & (1 << channel)) == 0) {
-
- pPwm->PWM_CH_NUM[channel].PWM_CPRD = period;
- }
- /* Otherwise use update register */
- else {
-
- pPwm->PWM_CH_NUM[channel].PWM_CPRDUPD = period;
- }
- }
-
- /**
- * \brief Sets the duty cycle used by a PWM channel.
- * This function writes directly to the CDTY register if the channel is disabled;
- * otherwise it uses the update register CDTYUPD.
- * Note that the duty cycle must always be inferior or equal to the channel
- * period.
- *
- * \param channel Channel number.
- * \param duty Duty cycle value.
- */
- void PWMC_SetDutyCycle( Pwm* pPwm, uint8_t channel, uint16_t duty)
- {
- assert(duty <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
-
- /* If channel is disabled, write to CDTY */
- if ((pPwm->PWM_SR & (1 << channel)) == 0) {
-
- pPwm->PWM_CH_NUM[channel].PWM_CDTY = duty;
- }
- /* Otherwise use update register */
- else {
-
- pPwm->PWM_CH_NUM[channel].PWM_CDTYUPD = duty;
- }
- }
-
- /**
- * \brief Sets the dead time used by a PWM channel.
- * This function writes directly to the DT register if the channel is disabled;
- * otherwise it uses the update register DTUPD.
- * Note that the dead time must always be inferior or equal to the channel
- * period.
- *
- * \param channel Channel number.
- * \param timeH Dead time value for PWMHx output.
- * \param timeL Dead time value for PWMLx output.
- */
- void PWMC_SetDeadTime( Pwm* pPwm, uint8_t channel, uint16_t timeH, uint16_t timeL)
- {
- assert(timeH <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
- assert(timeL <= pPwm->PWM_CH_NUM[channel].PWM_CPRD);
-
- /* If channel is disabled, write to DT */
- if ((pPwm->PWM_SR & (1 << channel)) == 0) {
-
- pPwm->PWM_CH_NUM[channel].PWM_DT = timeH | (timeL << 16);
- }
- /* Otherwise use update register */
- else {
- pPwm->PWM_CH_NUM[channel].PWM_DTUPD = timeH | (timeL << 16);
- }
- }
-
- /**
- * \brief Configures Syncronous channel with the given parameters.
- * Beware: At this time, the channels should be disabled.
- *
- * \param channels Bitwise OR of Syncronous channels.
- * \param updateMode Syncronous channel update mode.
- * \param requestMode PDC transfer request mode.
- * \param requestComparisonSelect PDC transfer request comparison selection.
- */
- void PWMC_ConfigureSyncChannel( Pwm* pPwm,
- uint32_t channels,
- uint32_t updateMode,
- uint32_t requestMode,
- uint32_t requestComparisonSelect)
- {
- pPwm->PWM_SCM = channels | updateMode | requestMode | requestComparisonSelect;
- }
-
- /**
- * \brief Sets the update period of the synchronous channels.
- * This function writes directly to the SCUP register if the channel #0 is disabled;
- * otherwise it uses the update register SCUPUPD.
- *
- * \param period update period.
- */
- void PWMC_SetSyncChannelUpdatePeriod( Pwm* pPwm, uint8_t period)
- {
- /* If channel is disabled, write to SCUP */
- if ((pPwm->PWM_SR & (1 << 0)) == 0) {
-
- pPwm->PWM_SCUP = period;
- }
- /* Otherwise use update register */
- else {
-
- pPwm->PWM_SCUPUPD = period;
- }
- }
-
- /**
- * \brief Sets synchronous channels update unlock.
- *
- * Note: If the UPDM field is set to 0, writing the UPDULOCK bit to 1
- * triggers the update of the period value, the duty-cycle and
- * the dead-time values of synchronous channels at the beginning
- * of the next PWM period. If the field UPDM is set to 1 or 2,
- * writing the UPDULOCK bit to 1 triggers only the update of
- * the period value and of the dead-time values of synchronous channels.
- * This bit is automatically reset when the update is done.
- */
- void PWMC_SetSyncChannelUpdateUnlock( Pwm* pPwm )
- {
- pPwm->PWM_SCUC = PWM_SCUC_UPDULOCK;
- }
-
- /**
- * \brief Enables the given PWM channel.
- *
- * This does NOT enable the corresponding pin;this must be done in the user code.
- *
- * \param channel Channel number.
- */
- void PWMC_EnableChannel( Pwm* pPwm, uint8_t channel)
- {
- pPwm->PWM_ENA = 1 << channel;
- }
-
- /**
- * \brief Disables the given PWM channel.
- *
- * Beware, channel will be effectively disabled at the end of the current period.
- * Application can check channel is disabled using the following wait loop:
- * while ((PWM->PWM_SR & (1 << channel)) != 0);
- *
- * \param channel Channel number.
- */
- void PWMC_DisableChannel( Pwm* pPwm, uint8_t channel)
- {
- pPwm->PWM_DIS = 1 << channel;
- }
-
- /**
- * \brief Enables the period interrupt for the given PWM channel.
- *
- * \param channel Channel number.
- */
- void PWMC_EnableChannelIt( Pwm* pPwm, uint8_t channel)
- {
- pPwm->PWM_IER1 = 1 << channel;
- }
-
- /**
- * \brief Disables the period interrupt for the given PWM channel.
- *
- * \param channel Channel number.
- */
- void PWMC_DisableChannelIt( Pwm* pPwm, uint8_t channel)
- {
- pPwm->PWM_IDR1 = 1 << channel;
- }
-
- /**
- * \brief Enables the selected interrupts sources on a PWMC peripheral.
- *
- * \param sources1 Bitwise OR of selected interrupt sources of PWM_IER1.
- * \param sources2 Bitwise OR of selected interrupt sources of PWM_IER2.
- */
- void PWMC_EnableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
- {
- pPwm->PWM_IER1 = sources1;
- pPwm->PWM_IER2 = sources2;
- }
-
- /**
- * \brief Disables the selected interrupts sources on a PWMC peripheral.
- *
- * \param sources1 Bitwise OR of selected interrupt sources of PWM_IDR1.
- * \param sources2 Bitwise OR of selected interrupt sources of PWM_IDR2.
- */
- void PWMC_DisableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
- {
- pPwm->PWM_IDR1 = sources1;
- pPwm->PWM_IDR2 = sources2;
- }
-
- /**
- * \brief Sends the contents of buffer through a PWMC peripheral, using the PDC to
- * take care of the transfer.
- *
- * Note: Duty cycle of syncronous channels can update by PDC
- * when the field UPDM (Update Mode) in the PWM_SCM register is set to 2.
- *
- * \param pwmc Pointer to an Pwm instance.
- * \param buffer Data buffer to send.
- * \param length Length of the data buffer.
- */
- uint8_t PWMC_WriteBuffer(Pwm *pwmc,
- void *buffer,
- uint32_t length)
- {
- /* Check if first bank is free */
- if (pwmc->PWM_TCR == 0) {
-
- pwmc->PWM_TPR = (uint32_t) buffer;
- pwmc->PWM_TCR = length;
- pwmc->PWM_PTCR = PERIPH_PTCR_TXTEN;
- return 1;
- }
- /* Check if second bank is free */
- else if (pwmc->PWM_TNCR == 0) {
-
- pwmc->PWM_TNPR = (uint32_t) buffer;
- pwmc->PWM_TNCR = length;
- return 1;
- }
-
- /* No free banks */
- return 0;
- }
-
- /**
- * \brief Set PWM output override value.
- *
- * \param value Bitwise OR of output override value.
- */
- void PWMC_SetOverrideValue( Pwm* pPwm, uint32_t value)
- {
- pPwm->PWM_OOV = value;
- }
-
- /**
- * \brief Enalbe override output.
- *
- * \param value Bitwise OR of output selection.
- * \param sync 0: enable the output asyncronously, 1: enable it syncronously
- */
- void PWMC_EnableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
- {
- if (sync) {
-
- pPwm->PWM_OSSUPD = value;
- } else {
-
- pPwm->PWM_OSS = value;
- }
- }
-
- /**
- * \brief Disalbe override output.
- *
- * \param value Bitwise OR of output selection.
- * \param sync 0: enable the output asyncronously, 1: enable it syncronously
- */
- void PWMC_DisableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
- {
- if (sync) {
-
- pPwm->PWM_OSCUPD = value;
- } else {
-
- pPwm->PWM_OSC = value;
- }
- }
-
- /**
- * \brief Set PWM fault mode.
- *
- * \param mode Bitwise OR of fault mode.
- */
- void PWMC_SetFaultMode( Pwm* pPwm, uint32_t mode)
- {
- pPwm->PWM_FMR = mode;
- }
-
- /**
- * \brief PWM fault clear.
- *
- * \param fault Bitwise OR of fault to clear.
- */
- void PWMC_FaultClear( Pwm* pPwm, uint32_t fault)
- {
- pPwm->PWM_FCR = fault;
- }
-
- /**
- * \brief Set PWM fault protection value.
- *
- * \param value Bitwise OR of fault protection value.
- */
- void PWMC_SetFaultProtectionValue( Pwm* pPwm, uint32_t value)
- {
- pPwm->PWM_FPV = value;
- }
-
- /**
- * \brief Enable PWM fault protection.
- *
- * \param value Bitwise OR of FPEx[y].
- */
- extern void PWMC_EnableFaultProtection( Pwm* pPwm, uint32_t dwChannel, uint32_t dwValue )
- {
- assert( dwChannel < 8 ) ;
-
- if ( dwChannel < 4 )
- {
- pPwm->PWM_FPE1 = (((0xffu << (dwChannel<<4)) & ((dwValue) << (dwChannel<<4)))) ;
- }
- else
- {
- pPwm->PWM_FPE2 = (((0xffu << ((dwChannel-4)<<4)) & ((dwValue) << ((dwChannel-4)<<4)))) ;
- }
- }
-
- /**
- * \brief Configure comparison unit.
- *
- * \param x comparison x index
- * \param value comparison x value.
- * \param mode comparison x mode
- */
- void PWMC_ConfigureComparisonUnit( Pwm* pPwm, uint32_t x, uint32_t value, uint32_t mode)
- {
- assert(x < 8);
-
- /* If channel is disabled, write to CMPxM & CMPxV */
- if ((pPwm->PWM_SR & (1 << 0)) == 0) {
- pPwm->PWM_CMP[x].PWM_CMPM = mode;
- pPwm->PWM_CMP[x].PWM_CMPV = value;
- }
- /* Otherwise use update register */
- else {
- pPwm->PWM_CMP[x].PWM_CMPMUPD = mode;
- pPwm->PWM_CMP[x].PWM_CMPVUPD = value;
- }
- }
-
- /**
- * \brief Configure event line mode.
- *
- * \param x Line x
- * \param mode Bitwise OR of line mode selection
- */
- void PWMC_ConfigureEventLineMode( Pwm* pPwm, uint32_t x, uint32_t mode)
- {
- assert(x < 2);
-
- pPwm->PWM_ELMR[x] = mode;
- }
-