/* * Copyright (c) 2015-2018, Texas Instruments Incorporated * 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 following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS 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. */ /* * By default disable both asserts and log for this module. * This must be done before DebugP.h is included. */ #ifndef DebugP_ASSERT_ENABLED #define DebugP_ASSERT_ENABLED 0 #endif #ifndef DebugP_LOG_ENABLED #define DebugP_LOG_ENABLED 0 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PAD_CONFIG_BASE (OCP_SHARED_BASE + OCP_SHARED_O_GPIO_PAD_CONFIG_0) #define PAD_RESET_STATE 0xC61 /*! * @brief If the PWM period is lower than this value, setDutyAndPeriod * will briefly disable the PWM channel to set the new values. * * This is to prevent the case where the period, but not the duty, is * applied before the timeout and the next cycle is in an undetermined state. */ #define PWM_PERIOD_FOR_GLITCH_PROTECTION 0xF void PWMTimerCC32XX_close(PWM_Handle handle); int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd, void *arg); void PWMTimerCC32XX_init(PWM_Handle handle); PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params); int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue); int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue); int_fast16_t PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle, uint32_t dutyValue, uint32_t periodValue); void PWMTimerCC32XX_start(PWM_Handle handle); void PWMTimerCC32XX_stop(PWM_Handle handle); /* PWM function table for PWMTimerCC32XX implementation */ const PWM_FxnTable PWMTimerCC32XX_fxnTable = { PWMTimerCC32XX_close, PWMTimerCC32XX_control, PWMTimerCC32XX_init, PWMTimerCC32XX_open, PWMTimerCC32XX_setDuty, PWMTimerCC32XX_setPeriod, PWMTimerCC32XX_setDutyAndPeriod, PWMTimerCC32XX_start, PWMTimerCC32XX_stop }; /* * Internal value to notify an error has occurred while calculating a duty * or period. */ static const uint32_t PWM_INVALID_VALUE = (~0); /* * GPT peripheral load & match registers are 16 bits wide. Max value which * can be set is 65535. */ static const uint16_t PWM_MAX_MATCH_REG_VALUE = (~0); /* * GPT peripherals have 24 bit resolution. The max period value which be * set is 16777215. */ static const uint32_t PWM_MAX_PERIOD_COUNT = (0xFFFFFF); /* * The following fields are used by CC32XX driverlib APIs and therefore * must be populated by driverlib macro definitions. For CC32XX driverlib * these definitions are found in: * - inc/hw_memmap.h * - driverlib/gpio.h * - driverlib/pin.h * - driverlib/timer.h */ static const uint32_t timerBaseAddresses[4] = { TIMERA0_BASE, TIMERA1_BASE, TIMERA2_BASE, TIMERA3_BASE, }; static const uint32_t timerHalves[2] = { TIMER_A, TIMER_B, }; #define NUMGPIOPORTS 4 static const uint32_t gpioBaseAddresses[NUMGPIOPORTS] = { GPIOA0_BASE, GPIOA1_BASE, GPIOA2_BASE, GPIOA3_BASE, }; #define NUMGPIOPINS 8 static const uint32_t gpioPinIndexes[NUMGPIOPINS] = { GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7, }; #define PinConfigTimerPort(config) (((config) >> 28) & 0xF) #define PinConfigTimerHalf(config) (((config) >> 24) & 0xF) #define PinConfigGPIOPort(config) (((config) >> 20) & 0xF) #define PinConfigGPIOPinIndex(config) (((config) >> 16) & 0xF) #define PinConfigPinMode(config) (((config) >> 8) & 0xF) #define PinConfigPin(config) (((config) >> 0) & 0x3F) /* * ======== getDutyCounts ======== */ static uint32_t getDutyCounts(PWM_Duty_Units dutyUnits, uint32_t dutyValue, uint32_t periodCounts) { uint32_t duty = 0; ClockP_FreqHz freq; ClockP_getCpuFreq(&freq); switch (dutyUnits) { case PWM_DUTY_COUNTS: duty = dutyValue; break; case PWM_DUTY_FRACTION: duty = (((uint64_t) dutyValue) * ((uint64_t) periodCounts)) / PWM_DUTY_FRACTION_MAX; break; case PWM_DUTY_US: duty = (dutyValue != 0) ? (dutyValue * (freq.lo/1000000)) - 1 : 0; break; default: /* Unsupported duty units return an invalid duty */ duty = PWM_INVALID_VALUE; } return (duty); } /* * ======== getPeriodCounts ======== */ static uint32_t getPeriodCounts(PWM_Period_Units periodUnits, uint32_t periodValue) { uint32_t period = 0; ClockP_FreqHz freq; ClockP_getCpuFreq(&freq); switch (periodUnits) { case PWM_PERIOD_COUNTS: period = periodValue; break; case PWM_PERIOD_HZ: if (periodValue && periodValue <= freq.lo) { period = (freq.lo / periodValue) - 1; } break; case PWM_PERIOD_US: period = (periodValue * (freq.lo/1000000)) - 1; break; default: /* Unsupported period units return an invalid period */ period = PWM_INVALID_VALUE; } return (period); } /* * ======== getPowerMgrId ======== */ static uint_fast16_t getPowerMgrId(uint32_t baseAddr) { switch (baseAddr) { case GPIOA0_BASE: return (PowerCC32XX_PERIPH_GPIOA0); case GPIOA1_BASE: return (PowerCC32XX_PERIPH_GPIOA1); case GPIOA2_BASE: return (PowerCC32XX_PERIPH_GPIOA2); case GPIOA3_BASE: return (PowerCC32XX_PERIPH_GPIOA3); case GPIOA4_BASE: return (PowerCC32XX_PERIPH_GPIOA4); default: /* Should never get here */ return ((unsigned int) -1); } } /* * ======== initHw ======== */ static int initHw(PWM_Handle handle, uint32_t period, uint32_t duty) { uintptr_t key; int32_t result; uint32_t timerConfigVal; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; uint32_t timerBaseAddr; uint16_t halfTimer; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)]; key = HwiP_disable(); MAP_TimerDisable(timerBaseAddr, halfTimer); /* * The CC32XX SDK TimerConfigure API halts both timers when it is * used to configure a single half timer. The code below performs * the register operations necessary to configure each half timer * individually. */ /* Enable CCP to IO path */ HWREG(APPS_CONFIG_BASE + APPS_CONFIG_O_GPT_TRIG_SEL) = 0xFF; /* Split the timer and configure it as a PWM */ timerConfigVal = ((halfTimer & (TIMER_CFG_A_PWM | TIMER_CFG_B_PWM)) | TIMER_CFG_SPLIT_PAIR); HWREG(timerBaseAddr + TIMER_O_CFG) |= (timerConfigVal >> 24); if (halfTimer & TIMER_A) { HWREG(timerBaseAddr + TIMER_O_TAMR) = timerConfigVal & 255; } else { HWREG(timerBaseAddr + TIMER_O_TBMR) = (timerConfigVal >> 8) & 255; } /* Set the peripheral output to active-high */ MAP_TimerControlLevel(timerBaseAddr, halfTimer, true); HwiP_restore(key); result = PWMTimerCC32XX_setPeriod(handle, period); if (result != PWM_STATUS_SUCCESS) { return (result); } result = PWMTimerCC32XX_setDuty(handle, duty); if (result != PWM_STATUS_SUCCESS) { return (result); } return (PWM_STATUS_SUCCESS); } /* * ======== postNotifyFxn ======== * Called by Power module when waking up from LPDS. */ static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg) { PWM_Handle handle = (PWM_Handle) clientArg; PWMTimerCC32XX_Object *object = handle->object; initHw(handle, object->period, object->duty); return (Power_NOTIFYDONE); } /* * ======== PWMTimerCC32XX_close ======== * @pre Function assumes that the handle is not NULL */ void PWMTimerCC32XX_close(PWM_Handle handle) { PWMTimerCC32XX_Object *object = handle->object; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; TimerCC32XX_SubTimer subTimer; uint32_t timerBaseAddr; uint32_t gpioBaseAddr; uint32_t padRegister; uintptr_t key; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A + PinConfigTimerHalf(hwAttrs->pwmPin)); /* * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr * is set to 0 & the GPIO power dependencies are not released. */ gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ? 0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)]; PWMTimerCC32XX_stop(handle); key = HwiP_disable(); TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer); /* Remove GPIO power dependency if pin is GPIO capable */ if (gpioBaseAddr) { Power_releaseDependency(getPowerMgrId(gpioBaseAddr)); } Power_unregisterNotify(&object->postNotify); padRegister = (PinToPadGet((hwAttrs->pwmPin) & 0x3f)<<2) + PAD_CONFIG_BASE; HWREG(padRegister) = PAD_RESET_STATE; object->isOpen = false; HwiP_restore(key); DebugP_log1("PWM:(%p) is closed", (uintptr_t) handle); } /* * ======== PWMTimerCC32XX_control ======== * @pre Function assumes that the handle is not NULL */ int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd, void *arg) { /* No implementation yet */ return (PWM_STATUS_UNDEFINEDCMD); } /* * ======== PWMTimerCC32XX_init ======== * @pre Function assumes that the handle is not NULL */ void PWMTimerCC32XX_init(PWM_Handle handle) { } /* * ======== PWMTimerCC32XX_open ======== * @pre Function assumes that the handle is not NULL */ PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params) { uintptr_t key; PWMTimerCC32XX_Object *object = handle->object; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; TimerCC32XX_SubTimer subTimer; uint32_t timerBaseAddr; uint32_t gpioBaseAddr; uint16_t pin; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; pin = PinConfigPin(hwAttrs->pwmPin); subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A + PinConfigTimerHalf(hwAttrs->pwmPin)); key = HwiP_disable(); if (object->isOpen) { HwiP_restore(key); DebugP_log1("PWM:(%p) already opened.", (uintptr_t) handle); return (NULL); } if (!TimerCC32XX_allocateTimerResource(timerBaseAddr, subTimer)) { HwiP_restore(key); DebugP_log1("Timer: 0x%X unavailable.", timerBaseAddr); return (NULL); } object->isOpen = true; HwiP_restore(key); /* * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr * is set to 0 & the GPIO power dependencies are not set. */ gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ? 0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)]; /* Set GPIO power dependency if pin is GPIO capable */ if (gpioBaseAddr) { /* Check GPIO power resource Id */ if (getPowerMgrId(gpioBaseAddr) == ((unsigned int) -1)) { TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer); object->isOpen = false; DebugP_log1("PWM:(%p) Failed to determine GPIO power resource ID.", (uintptr_t) handle); return (NULL); } /* Register power dependency for GPIO port */ Power_setDependency(getPowerMgrId(gpioBaseAddr)); } Power_registerNotify(&object->postNotify, PowerCC32XX_AWAKE_LPDS, postNotifyFxn, (uintptr_t) handle); /* * Set PWM duty to initial value (not 0) - required when inverting * output polarity to generate a duty equal to 0 or period. See comments in * PWMTimerCC32XX_setDuty for more information. */ object->duty = 0; object->period = 0; object->dutyUnits = params->dutyUnits; object->idleLevel = params->idleLevel; object->periodUnits = params->periodUnits; object->pwmStarted = 0; /* Initialize the peripheral & set the period & duty */ if (initHw(handle, params->periodValue, params->dutyValue) != PWM_STATUS_SUCCESS) { PWMTimerCC32XX_close(handle); DebugP_log1("PWM:(%p) Failed set initial PWM configuration.", (uintptr_t) handle); return (NULL); } /* Configure the Power_pinParkState based on idleLevel param */ PowerCC32XX_setParkState((PowerCC32XX_Pin) pin, (object->idleLevel == PWM_IDLE_HIGH)); /* Called to set the initial idleLevel */ PWMTimerCC32XX_stop(handle); DebugP_log3("PWM:(%p) opened; period set to: %d; duty set to: %d", (uintptr_t) handle, params->periodValue, params->dutyValue); return (handle); } /* * ======== PWMTimerCC32XX_setDuty ======== * @pre Function assumes that handle is not NULL */ int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue) { uintptr_t key; uint32_t duty; uint32_t period; PWMTimerCC32XX_Object *object = handle->object; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; uint32_t timerBaseAddr; uint16_t halfTimer; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)]; key = HwiP_disable(); period = object->period; duty = getDutyCounts(object->dutyUnits, dutyValue, period); if (duty == PWM_INVALID_VALUE) { HwiP_restore(key); DebugP_log1("PWM:(%p) duty units could not be determined.", (uintptr_t) handle); return (PWM_STATUS_ERROR); } if (duty > period) { HwiP_restore(key); DebugP_log1("PWM:(%p) duty is out of range.", (uintptr_t) handle); return (PWM_STATUS_INVALID_DUTY); } /* * The timer peripheral cannot generate a duty equal to the period when * the timer is counting down. In these cases the PWM duty is set to the * period value (output remains low) and output polarity is inverted. * Additionally, if the output is changed from the period the PWM output * polarity must be inverted again. * * The code below uses the previous duty (object->duty) and the new duty to * determine if the polarity should be inverted. * For more details refer to the device specific datasheet and the following * E2E post: * http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/354826.aspx */ if (((duty == period) && (object->duty != period)) || ((duty != period) && (object->duty == period))) { HWREG(timerBaseAddr + TIMER_O_CTL) ^= (halfTimer & (TIMER_CTL_TAPWML | TIMER_CTL_TBPWML)); } /* * Set & store the new duty. IMPORTANT: this must be saved after output * inversion is determined and before the duty = 0 corner case. */ object->duty = duty; /* * Special corner case, if duty is 0 we set it to the period without * inverting output */ if (duty == 0) { duty = period; } MAP_TimerPrescaleMatchSet(timerBaseAddr, halfTimer, duty / PWM_MAX_MATCH_REG_VALUE); MAP_TimerMatchSet(timerBaseAddr, halfTimer, duty % PWM_MAX_MATCH_REG_VALUE); HwiP_restore(key); DebugP_log2("PWM:(%p) duty set to: %d", (uintptr_t) handle, dutyValue); return (PWM_STATUS_SUCCESS); } /* * ======== PWMTimerCC32XX_setPeriod ======== * @pre Function assumes that handle is not NULL */ int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue) { uintptr_t key; uint32_t duty; uint32_t period; PWMTimerCC32XX_Object *object = handle->object; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; uint32_t timerBaseAddr; uint16_t halfTimer; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)]; key = HwiP_disable(); duty = object->duty; period = getPeriodCounts(object->periodUnits, periodValue); if (period == PWM_INVALID_VALUE) { HwiP_restore(key); DebugP_log1("PWM:(%p) period units could not be determined.", (uintptr_t) handle); return (PWM_STATUS_ERROR); } if ((period == 0) || (period <= duty) || (period > PWM_MAX_PERIOD_COUNT)) { HwiP_restore(key); DebugP_log1("PWM:(%p) period is out of range.", (uintptr_t) handle); return (PWM_STATUS_INVALID_PERIOD); } /* Set the new period */ object->period = period; MAP_TimerPrescaleSet(timerBaseAddr, halfTimer, period / PWM_MAX_MATCH_REG_VALUE); MAP_TimerLoadSet(timerBaseAddr, halfTimer, period % PWM_MAX_MATCH_REG_VALUE); HwiP_restore(key); DebugP_log2("PWM:(%p) period set to: %d", (uintptr_t) handle, periodValue); return (PWM_STATUS_SUCCESS); } /* * ======== PWMTimerCC32XX_setDutyAndPeriod ======== * @pre Function assumes that handle is not NULL */ int_fast16_t PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle, uint32_t dutyValue, uint32_t periodValue) { uintptr_t key; uint32_t duty; uint32_t period; bool stopped = false; PWMTimerCC32XX_Object *object = handle->object; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; uint32_t oldPeriod; uint32_t timerBaseAddr; uint16_t halfTimer; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)]; key = HwiP_disable(); oldPeriod = getPeriodCounts(object->periodUnits, object->period); period = getPeriodCounts(object->periodUnits, periodValue); duty = getDutyCounts(object->dutyUnits, dutyValue, period); if (period == PWM_INVALID_VALUE) { HwiP_restore(key); DebugP_log1("PWM:(%p) period units could not be determined.", (uintptr_t) handle); return (PWM_STATUS_ERROR); } if ((period == 0) || (period < duty) || (period > PWM_MAX_PERIOD_COUNT)) { HwiP_restore(key); DebugP_log1("PWM:(%p) period is out of range.", (uintptr_t) handle); return (PWM_STATUS_INVALID_PERIOD); } /* Set the new period */ object->period = period; /* * The timer peripheral cannot generate a duty equal to the period when * the timer is counting down. In these cases the PWM duty is set to the * period value (output remains low) and output polarity is inverted. * Additionally, if the output is changed from the period the PWM output * polarity must be inverted again. * * The code below uses the previous duty (object->duty) and the new duty to * determine if the polarity should be inverted. * For more details refer to the device specific datasheet and the following * E2E post: * http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/354826.aspx */ if (((duty == period) && (object->duty != period)) || ((duty != period) && (object->duty == period))) { HWREG(timerBaseAddr + TIMER_O_CTL) ^= (halfTimer & (TIMER_CTL_TAPWML | TIMER_CTL_TBPWML)); } /* * Set & store the new duty. IMPORTANT: this must be saved after output * inversion is determined and before the duty = 0 corner case. */ object->duty = duty; /* * Special corner case, if duty is 0 we set it to the period without * inverting output */ if (duty == 0) { duty = period; } if (object->pwmStarted && oldPeriod <= PWM_PERIOD_FOR_GLITCH_PROTECTION) { stopped = true; MAP_TimerDisable(timerBaseAddr, halfTimer); } MAP_TimerPrescaleSet(timerBaseAddr, halfTimer, period / PWM_MAX_MATCH_REG_VALUE); MAP_TimerPrescaleMatchSet(timerBaseAddr, halfTimer, duty / PWM_MAX_MATCH_REG_VALUE); MAP_TimerLoadSet(timerBaseAddr, halfTimer, period % PWM_MAX_MATCH_REG_VALUE); MAP_TimerMatchSet(timerBaseAddr, halfTimer, duty % PWM_MAX_MATCH_REG_VALUE); if (stopped) { MAP_TimerEnable(timerBaseAddr, halfTimer); } HwiP_restore(key); return (PWM_STATUS_SUCCESS); } /* * ======== PWMTimerCC32XX_start ======== * @pre Function assumes that handle is not NULL */ void PWMTimerCC32XX_start(PWM_Handle handle) { uintptr_t key; PWMTimerCC32XX_Object *object = handle->object; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; uint32_t timerBaseAddr; uint16_t halfTimer; uint16_t pin; uint16_t mode; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)]; pin = PinConfigPin(hwAttrs->pwmPin); mode = PinConfigPinMode(hwAttrs->pwmPin); key = HwiP_disable(); /* * GP timer ticks only in Active mode. Cannot be used in HIB or LPDS. * Set constraint to disallow LPDS. */ if (!(object->pwmStarted)) { Power_setConstraint(PowerCC32XX_DISALLOW_LPDS); object->pwmStarted = true; } /* Start the timer & set pinmux to PWM mode */ MAP_TimerEnable(timerBaseAddr, halfTimer); MAP_PinTypeTimer((unsigned long)pin, (unsigned long)mode); HwiP_restore(key); } /* * ======== PWMTimerCC32XX_stop ======== * @pre Function assumes that handle is not NULL */ void PWMTimerCC32XX_stop(PWM_Handle handle) { uintptr_t key; uint8_t output; PWMTimerCC32XX_Object *object = handle->object; PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs; uint32_t timerBaseAddr; uint16_t halfTimer; uint32_t gpioBaseAddr; uint8_t gpioPinIndex; uint16_t pin; timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)]; halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)]; pin = PinConfigPin(hwAttrs->pwmPin); /* * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr * is set to 0 & the GPIO power dependencies are not set. */ gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ? 0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)]; gpioPinIndex = (PinConfigGPIOPinIndex(hwAttrs->pwmPin) >= NUMGPIOPINS) ? 0 : gpioPinIndexes[PinConfigGPIOPinIndex(hwAttrs->pwmPin)]; key = HwiP_disable(); /* Remove the dependency to allow LPDS */ if (object->pwmStarted) { Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS); object->pwmStarted = false; } /* Set pin as GPIO with IdleLevel value & stop the timer */ output = (object->idleLevel) ? gpioPinIndex : 0; MAP_PinTypeGPIO((unsigned long)pin, PIN_MODE_0, false); /* Only configure the pin as GPIO if the pin is GPIO capable */ if (gpioBaseAddr) { MAP_GPIODirModeSet(gpioBaseAddr, gpioPinIndex, GPIO_DIR_MODE_OUT); MAP_GPIOPinWrite(gpioBaseAddr, gpioPinIndex, output); } /* Stop the Timer */ MAP_TimerDisable(timerBaseAddr, halfTimer); HwiP_restore(key); }