/* * Licensed under ST Liberty SW License Agreement V2, (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.st.com/software_license_agreement_liberty_v2 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file FlexPWM_v1/pwm_lld.c * @brief SPC5xx low level PWM driver code. * * @addtogroup PWM * @{ */ #include "ch.h" #include "hal.h" #if HAL_USE_PWM || defined(__DOXYGEN__) /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ /** * @brief PWMD1 driver identifier. * @note The driver PWMD1 allocates the complex timer TIM1 when enabled. */ #if SPC5_PWM_USE_SMOD0 || defined(__DOXYGEN__) PWMDriver PWMD1; #endif /** * @brief PWMD2 driver identifier. * @note The driver PWMD2 allocates the timer TIM2 when enabled. */ #if SPC5_PWM_USE_SMOD1 || defined(__DOXYGEN__) PWMDriver PWMD2; #endif /** * @brief PWMD3 driver identifier. * @note The driver PWMD3 allocates the timer TIM3 when enabled. */ #if SPC5_PWM_USE_SMOD2 || defined(__DOXYGEN__) PWMDriver PWMD3; #endif /** * @brief PWMD4 driver identifier. * @note The driver PWMD4 allocates the timer TIM4 when enabled. */ #if SPC5_PWM_USE_SMOD3 || defined(__DOXYGEN__) PWMDriver PWMD4; #endif /*===========================================================================*/ /* Driver local variables. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ /** * @brief Configures and activates the PWM peripheral submodule. * * @param[in] pwmp pointer to a @p PWMDriver object * @param[in] sid PWM submodule identifier * * @notapi */ void pwm_lld_start_submodule(PWMDriver *pwmp, uint8_t sid) { pwmcnt_t pwmperiod; uint32_t psc; /* Clears Status Register.*/ pwmp->flexpwmp->SUB[sid].STS.R = 0xFFFF; /* Clears LDOK and initializes the registers.*/ pwmp->flexpwmp->MCTRL.B.CLDOK |= (0b0000 | (1U << sid)); pwmp->flexpwmp->SUB[sid].OCTRL.R = 0x0000; pwmp->flexpwmp->SUB[sid].INTEN.R = 0x0000; /* Setting PWM clock frequency and submodule prescaler.*/ psc = SPC5_FLEXPWM0_CLK / pwmp->config->frequency; chDbgAssert((psc <= 0xFFFF) && (((psc) * pwmp->config->frequency) == SPC5_FLEXPWM0_CLK) && ((psc == 1) || (psc == 2) || (psc == 4) || (psc == 8) || (psc == 16) || (psc == 32) || (psc == 64) || (psc == 128)), "icu_lld_start(), #1", "invalid frequency"); switch (psc) { case 1: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b000; break; case 2: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b001; break; case 4: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b010; break; case 8: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b011; break; case 16: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b100; break; case 32: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b101; break; case 64: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b110; break; case 128: pwmp->flexpwmp->SUB[sid].CTRL.B.PRSC = 0b111; break; } /* Disables PWM FAULT function. */ pwmp->flexpwmp->SUB[sid].DISMAP.R = 0x0000; pwmp->flexpwmp->SUB[sid].CTRL2.R = 0x0000; pwmp->flexpwmp->SUB[sid].CTRL2.B.INDEP = 1; /* Sets PWM period.*/ pwmperiod = pwmp->period; pwmp->flexpwmp->SUB[sid].INIT.R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[sid].VAL[0].R = 0x0000; pwmp->flexpwmp->SUB[sid].VAL[1].R = pwmperiod / 2; /* Sets the submodule channels.*/ switch (pwmp->config->mode & PWM_OUTPUT_MASK) { case EDGE_ALIGNED_PWM: /* Setting reloads.*/ pwmp->flexpwmp->SUB[sid].CTRL.B.HALF = 0; pwmp->flexpwmp->SUB[sid].CTRL.B.FULL = 1; /* Setting active front of PWM channels.*/ pwmp->flexpwmp->SUB[sid].VAL[2].R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[sid].VAL[4].R = ~(pwmperiod / 2) + 1U; break; case CENTER_ALIGNED_PWM: /* Setting reloads.*/ pwmp->flexpwmp->SUB[sid].CTRL.B.HALF = 1; pwmp->flexpwmp->SUB[sid].CTRL.B.FULL = 0; break; default: ; } /* Polarities setup.*/ switch (pwmp->config->channels[0].mode & PWM_OUTPUT_MASK) { case PWM_OUTPUT_ACTIVE_LOW: pwmp->flexpwmp->SUB[sid].OCTRL.B.POLA = 1; /* Enables CHA mask.*/ pwmp->flexpwmp->MASK.B.MASKA |= (0b0000 | (1U << sid)); /* Enables CHA.*/ pwmp->flexpwmp->OUTEN.B.PWMA_EN |= (0b0000 | (1U << sid)); break; case PWM_OUTPUT_ACTIVE_HIGH: pwmp->flexpwmp->SUB[sid].OCTRL.B.POLA = 0; /* Enables CHA mask.*/ pwmp->flexpwmp->MASK.B.MASKA |= (0b0000 | (1U << sid)); /* Enables CHA.*/ pwmp->flexpwmp->OUTEN.B.PWMA_EN |= (0b0000 | (1U << sid)); break; case PWM_OUTPUT_DISABLED: /* Enables CHA mask.*/ pwmp->flexpwmp->MASK.B.MASKA |= (0b0000 | (1U << sid)); break; default: ; } switch (pwmp->config->channels[1].mode & PWM_OUTPUT_MASK) { case PWM_OUTPUT_ACTIVE_LOW: pwmp->flexpwmp->SUB[sid].OCTRL.B.POLB = 1; /* Enables CHB mask.*/ pwmp->flexpwmp->MASK.B.MASKB |= (0b0000 | (1U << sid)); /* Enables CHB.*/ pwmp->flexpwmp->OUTEN.B.PWMB_EN |= (0b0000 | (1U << sid)); break; case PWM_OUTPUT_ACTIVE_HIGH: pwmp->flexpwmp->SUB[sid].OCTRL.B.POLB = 0; /* Enables CHB mask.*/ pwmp->flexpwmp->MASK.B.MASKB |= (0b0000 | (1U << sid)); /* Enables CHB.*/ pwmp->flexpwmp->OUTEN.B.PWMB_EN |= (0b0000 | (1U << sid)); break; case PWM_OUTPUT_DISABLED: /* Enables CHB mask.*/ pwmp->flexpwmp->MASK.B.MASKB |= (0b0000 | (1U << sid)); break; default: ; } /* Complementary output setup.*/ /* switch (pwmp->config->channels[0].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: chDbgAssert(pwmp->config->channels[1].mode == PWM_OUTPUT_ACTIVE_LOW, "pwm_lld_start(), #1", "the PWM chB must be set in PWM_OUTPUT_ACTIVE_LOW"); //pwmp->flexpwmp->SUB[sid].OCTRL.B.POLA = 1; pwmp->flexpwmp->SUB[sid].CTRL2.B.INDEP = 0; pwmp->flexpwmp->MCTRL.B.IPOL |= (0b0000 | (1U << sid)); pwmp->flexpwmp->MASK.B.MASKA |= (0b0000 | (1U << sid)); pwmp->flexpwmp->OUTEN.B.PWMA_EN |= (0b0000 | (1U << sid)); //pwmp->flexpwmp->SUB[0].OCTRL.B.POLB = 0; break; case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: chDbgAssert(pwmp->config->channels[1].mode == PWM_OUTPUT_ACTIVE_HIGH, "pwm_lld_start(), #2", "the PWM chB must be set in PWM_OUTPUT_ACTIVE_HIGH"); pwmp->flexpwmp->SUB[sid].CTRL2.B.INDEP = 0; pwmp->flexpwmp->MCTRL.B.IPOL |= (0b0000 | (0U << sid)); pwmp->flexpwmp->MASK.B.MASKA |= (0b0000 | (1U << sid)); pwmp->flexpwmp->OUTEN.B.PWMA_EN |= (0b0000 | (1U << sid)); // pwmp->flexpwmp->SUB[0].OCTRL.B.POLA = 0; //pwmp->flexpwmp->SUB[0].OCTRL.B.POLB = 1; break; default: ; } switch (pwmp->config->channels[1].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: chDbgAssert(pwmp->config->channels[0].mode == PWM_OUTPUT_ACTIVE_LOW, "pwm_lld_start(), #3", "the PWM chA must be set in PWM_OUTPUT_ACTIVE_LOW"); pwmp->flexpwmp->SUB[sid].CTRL2.B.INDEP = 0; pwmp->flexpwmp->MCTRL.B.IPOL &= ~ (0b0000 | (1U << sid)); // pwmp->flexpwmp->SUB[0].OCTRL.B.POLA = 0; pwmp->flexpwmp->SUB[sid].OCTRL.B.POLB = 1; pwmp->flexpwmp->MASK.B.MASKB |= (0b0000 | (1U << sid)); pwmp->flexpwmp->OUTEN.B.PWMB_EN |= (0b0000 | (1U << sid)); break; case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: chDbgAssert(pwmp->config->channels[0].mode == PWM_OUTPUT_ACTIVE_HIGH, "pwm_lld_start(), #4", "the PWM chA must be set in PWM_OUTPUT_ACTIVE_HIGH"); pwmp->flexpwmp->SUB[sid].CTRL2.B.INDEP = 0; pwmp->flexpwmp->MCTRL.B.IPOL &= ~ (0b0000 | (1U << sid)); pwmp->flexpwmp->MASK.B.MASKB |= (0b0000 | (1U << sid)); pwmp->flexpwmp->OUTEN.B.PWMB_EN |= (0b0000 | (1U << sid)); // pwmp->flexpwmp->SUB[0].OCTRL.B.POLA = 1; // pwmp->flexpwmp->SUB[0].OCTRL.B.POLB = 0; break; default: ; } */ /* Sets the INIT and MASK registers.*/ pwmp->flexpwmp->SUB[sid].CTRL2.B.FRCEN = 1U; pwmp->flexpwmp->SUB[sid].CTRL2.B.FORCE_SEL = 0b000; pwmp->flexpwmp->SUB[sid].CTRL2.B.FORCE = 1U; /* Updates SMOD registers and starts SMOD.*/ pwmp->flexpwmp->MCTRL.B.LDOK |= (0b0000 | (1U << sid)); pwmp->flexpwmp->MCTRL.B.RUN |= (0b0000 | (1U << sid)); } /** * @brief Enables a PWM channel of a submodule. * * @param[in] pwmp pointer to a @p PWMDriver object * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) * @param[in] width PWM pulse width as clock pulses number * @param[in] sid PWM submodule id * * @notapi */ void pwm_lld_enable_submodule_channel(PWMDriver *pwmp, pwmchannel_t channel, pwmcnt_t width, uint8_t sid) { pwmcnt_t pwmperiod; int16_t nwidth; pwmperiod = pwmp->period; nwidth = width - (pwmperiod / 2); /* Clears LDOK.*/ pwmp->flexpwmp->MCTRL.B.CLDOK |= (0b0000 | (1U << sid)); /* Active the width interrupt.*/ if (channel == 0) { if (pwmp->config->channels[0].callback != NULL) { if ((pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE & 0b001000) == 0) { pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE |= 0b001000; } } /* Sets the channel width.*/ switch (pwmp->config->mode & PWM_OUTPUT_MASK) { case EDGE_ALIGNED_PWM: if (nwidth >= 0) pwmp->flexpwmp->SUB[sid].VAL[3].R = nwidth; else pwmp->flexpwmp->SUB[sid].VAL[3].R = ~((pwmperiod / 2) - width) + 1U; break; case CENTER_ALIGNED_PWM: pwmp->flexpwmp->SUB[sid].VAL[3].R = width / 2; pwmp->flexpwmp->SUB[sid].VAL[2].R = ~(width / 2) + 1U; break; default: ; } /* Removes the channel mask if it is necessary.*/ if ((pwmp->flexpwmp->MASK.B.MASKA & (0b0000 | (1U << sid))) == 1) pwmp->flexpwmp->MASK.B.MASKA &= ~ (0b0000 | (1U << sid)); } /* Active the width interrupt.*/ else if (channel == 1) { if (pwmp->config->channels[1].callback != NULL) { if ((pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE & 0b100000) == 0) { pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE |= 0b100000; } } /* Sets the channel width.*/ switch (pwmp->config->mode & PWM_OUTPUT_MASK) { case EDGE_ALIGNED_PWM: if (nwidth >= 0) pwmp->flexpwmp->SUB[sid].VAL[5].R = nwidth; else pwmp->flexpwmp->SUB[sid].VAL[5].R = ~((pwmperiod / 2) - width) + 1U; break; case CENTER_ALIGNED_PWM: pwmp->flexpwmp->SUB[sid].VAL[5].R = width / 2; pwmp->flexpwmp->SUB[sid].VAL[4].R = ~(width / 2) + 1U; break; default: ; } /* Removes the channel mask if it is necessary.*/ if ((pwmp->flexpwmp->MASK.B.MASKB & (0b0000 | (1U << sid))) == 1) pwmp->flexpwmp->MASK.B.MASKB &= ~ (0b0000 | (1U << sid)); } /* Active the periodic interrupt.*/ if (pwmp->flexpwmp->SUB[sid].INTEN.B.RIE != 1U) { if (pwmp->config->callback != NULL) { pwmp->flexpwmp->SUB[sid].INTEN.B.RIE = 1; } } /* Sets the MASK registers.*/ pwmp->flexpwmp->SUB[sid].CTRL2.B.FRCEN = 1U; pwmp->flexpwmp->SUB[sid].CTRL2.B.FORCE_SEL = 0b000; pwmp->flexpwmp->SUB[sid].CTRL2.B.FORCE = 1U; /* Forces reload of the VALUE registers.*/ pwmp->flexpwmp->MCTRL.B.LDOK |= (0b0000 | (1U << sid)); } /** * @brief Disables a PWM channel of a submodule. * * @param[in] pwmp pointer to a @p PWMDriver object * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) * @param[in] sid PWM submodule id * * @notapi */ void pwm_lld_disable_submodule_channel(PWMDriver *pwmp, pwmchannel_t channel, uint8_t sid) { pwmp->flexpwmp->MCTRL.B.CLDOK |= (0b0000 | (1U << sid)); /* Disable the width interrupt.*/ if (channel == 0) { if (pwmp->config->channels[0].callback != NULL) { if ((pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE & 0b001000) == 1) { pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE &= 0b110111; } } /* Active the channel mask.*/ pwmp->flexpwmp->MASK.B.MASKA |= (0b0000 | (1U << sid)); } /* Disable the width interrupt.*/ else if (channel == 1) { if (pwmp->config->channels[1].callback != NULL) { if ((pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE & 0b100000) == 1) { pwmp->flexpwmp->SUB[sid].INTEN.B.CMPIE &= 0b011111; } } /* Active the channel mask.*/ pwmp->flexpwmp->MASK.B.MASKB |= (0b0000 | (1U << sid)); } /* Sets the MASK registers.*/ pwmp->flexpwmp->SUB[sid].CTRL2.B.FRCEN = 1U; pwmp->flexpwmp->SUB[sid].CTRL2.B.FORCE_SEL = 0b000; pwmp->flexpwmp->SUB[sid].CTRL2.B.FORCE = 1U; /* Disable RIE interrupt to prevent reload interrupt.*/ if ((pwmp->flexpwmp->MASK.B.MASKA & (0b0000 | (1U << sid))) && (pwmp->flexpwmp->MASK.B.MASKB & (0b0000 | (1U << sid))) == 1) { pwmp->flexpwmp->SUB[sid].INTEN.B.RIE = 0; /* Clear the reload flag.*/ pwmp->flexpwmp->SUB[sid].STS.B.RF = 1U; } pwmp->flexpwmp->MCTRL.B.LDOK |= (0b0000 | (1U << sid)); } #if SPC5_PWM_USE_SMOD0 || SPC5_PWM_USE_SMOD1 || SPC5_PWM_USE_SMOD2 || \ SPC5_PWM_USE_SMOD3 || defined(__DOXYGEN__) /** * @brief Common SMOD0...SMOD3 IRQ handler. * @note It is assumed that the various sources are only activated if the * associated callback pointer is not equal to @p NULL in order to not * perform an extra check in a potentially critical interrupt handler. * * @param[in] pwmp pointer to a @p PWMDriver object */ static void pwm_lld_serve_interrupt(PWMDriver *pwmp) { uint16_t sr; #if SPC5_PWM_USE_SMOD0 if (&PWMD1 == pwmp) { sr = pwmp->flexpwmp->SUB[0].STS.R & pwmp->flexpwmp->SUB[0].INTEN.R; if ((sr & SPC5_STS_CMPF3) != 0) { pwmp->flexpwmp->SUB[0].STS.B.CMPF |= 0b001000; pwmp->config->channels[0].callback(pwmp); } if ((sr & SPC5_STS_CMPF5) != 0) { pwmp->flexpwmp->SUB[0].STS.B.CMPF |= 0b100000; pwmp->config->channels[1].callback(pwmp); } if ((sr & SPC5_STS_RF) != 0) { pwmp->flexpwmp->SUB[0].STS.B.RF = 1U; pwmp->config->callback(pwmp); } } #endif #if SPC5_PWM_USE_SMOD1 if (&PWMD2 == pwmp) { sr = pwmp->flexpwmp->SUB[1].STS.R & pwmp->flexpwmp->SUB[1].INTEN.R; if ((sr & SPC5_STS_CMPF3) != 0) { pwmp->flexpwmp->SUB[1].STS.B.CMPF |= 0b001000; pwmp->config->channels[0].callback(pwmp); } if ((sr & SPC5_STS_CMPF5) != 0) { pwmp->flexpwmp->SUB[1].STS.B.CMPF |= 0b100000; pwmp->config->channels[1].callback(pwmp); } if ((sr & SPC5_STS_RF) != 0) { pwmp->flexpwmp->SUB[1].STS.B.RF = 1U; pwmp->config->callback(pwmp); } } #endif #if SPC5_PWM_USE_SMOD2 if (&PWMD3 == pwmp) { sr = pwmp->flexpwmp->SUB[2].STS.R & pwmp->flexpwmp->SUB[2].INTEN.R; if ((sr & SPC5_STS_CMPF3) != 0) { pwmp->flexpwmp->SUB[2].STS.B.CMPF |= 0b001000; pwmp->config->channels[0].callback(pwmp); } if ((sr & SPC5_STS_CMPF5) != 0) { pwmp->flexpwmp->SUB[2].STS.B.CMPF |= 0b100000; pwmp->config->channels[1].callback(pwmp); } if ((sr & SPC5_STS_RF) != 0) { pwmp->flexpwmp->SUB[2].STS.B.RF = 1U; pwmp->config->callback(pwmp); } } #endif #if SPC5_PWM_USE_SMOD3 if (&PWMD4 == pwmp) { sr = pwmp->flexpwmp->SUB[3].STS.R & pwmp->flexpwmp->SUB[3].INTEN.R; if ((sr & SPC5_STS_CMPF3) != 0) { pwmp->flexpwmp->SUB[3].STS.B.CMPF |= 0b001000; pwmp->config->channels[0].callback(pwmp); } if ((sr & SPC5_STS_CMPF5) != 0) { pwmp->flexpwmp->SUB[3].STS.B.CMPF |= 0b100000; pwmp->config->channels[1].callback(pwmp); } if ((sr & SPC5_STS_RF) != 0) { pwmp->flexpwmp->SUB[3].STS.B.RF = 1U; pwmp->config->callback(pwmp); } } #endif } #endif /* SPC5_PWM_USE_SMOD0 || ... || SPC5_PWM_USE_SMOD3 */ /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ #if SPC5_PWM_USE_SMOD0 || defined(__DOXYGEN__) #if !defined(SPC5_FLEXPWM0_RF0_HANDLER) #error "SPC5_FLEXPWM0_RF0_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD0 RF0 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_RF0_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD1); CH_IRQ_EPILOGUE(); } #if !defined(SPC5_FLEXPWM0_COF0_HANDLER) #error "SPC5_FLEXPWM0_COF0_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD0 COF0 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_COF0_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD1); CH_IRQ_EPILOGUE(); } #endif #if SPC5_PWM_USE_SMOD1 || defined(__DOXYGEN__) #if !defined(SPC5_FLEXPWM0_RF1_HANDLER) #error "SPC5_FLEXPWM0_RF1_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD1 RF1 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_RF1_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD2); CH_IRQ_EPILOGUE(); } #if !defined(SPC5_FLEXPWM0_COF1_HANDLER) #error "SPC5_FLEXPWM0_COF1_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD1 COF1 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_COF1_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD2); CH_IRQ_EPILOGUE(); } #endif #if SPC5_PWM_USE_SMOD2 || defined(__DOXYGEN__) #if !defined(SPC5_FLEXPWM0_RF2_HANDLER) #error "SPC5_FLEXPWM0_RF2_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD2 RF2 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_RF2_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD3); CH_IRQ_EPILOGUE(); } #if !defined(SPC5_FLEXPWM0_COF2_HANDLER) #error "SPC5_FLEXPWM0_COF2_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD2 COF2 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_COF2_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD3); CH_IRQ_EPILOGUE(); } #endif #if SPC5_PWM_USE_SMOD3 || defined(__DOXYGEN__) #if !defined(SPC5_FLEXPWM0_RF3_HANDLER) #error "SPC5_FLEXPWM0_RF3_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD1 RF3 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_RF3_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD4); CH_IRQ_EPILOGUE(); } #if !defined(SPC5_FLEXPWM0_COF3_HANDLER) #error "SPC5_FLEXPWM0_COF3_HANDLER not defined" #endif /** * @brief FlexPWM0-SMOD1 COF3 interrupt handler. * * @isr */ CH_IRQ_HANDLER(SPC5_FLEXPWM0_COF3_HANDLER) { CH_IRQ_PROLOGUE(); pwm_lld_serve_interrupt(&PWMD4); CH_IRQ_EPILOGUE(); } #endif /*===========================================================================*/ /* Driver exported functions. */ /*===========================================================================*/ /** * @brief Low level PWM driver initialization. * * @notapi */ void pwm_lld_init(void) { #if (SPC5_PWM_USE_SMOD0) /* Driver initialization.*/ pwmObjectInit(&PWMD1); PWMD1.flexpwmp = &FLEXPWM_0; INTC.PSR[SPC5_FLEXPWM0_RF0_NUMBER].R = SPC5_PWM_SMOD0_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_COF0_NUMBER].R = SPC5_PWM_SMOD0_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_CAF0_NUMBER].R = SPC5_PWM_SMOD0_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_FFLAG_NUMBER].R = SPC5_PWM_SMOD0_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_REF_NUMBER].R = SPC5_PWM_SMOD0_PRIORITY; #endif #if (SPC5_PWM_USE_SMOD1) /* Driver initialization.*/ pwmObjectInit(&PWMD2); PWMD2.flexpwmp = &FLEXPWM_0; INTC.PSR[SPC5_FLEXPWM0_RF1_NUMBER].R = SPC5_PWM_SMOD1_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_COF1_NUMBER].R = SPC5_PWM_SMOD1_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_CAF1_NUMBER].R = SPC5_PWM_SMOD1_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_FFLAG_NUMBER].R = SPC5_PWM_SMOD1_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_REF_NUMBER].R = SPC5_PWM_SMOD1_PRIORITY; #endif #if (SPC5_PWM_USE_SMOD2) /* Driver initialization.*/ pwmObjectInit(&PWMD3); PWMD3.flexpwmp = &FLEXPWM_0; INTC.PSR[SPC5_FLEXPWM0_RF2_NUMBER].R = SPC5_PWM_SMOD2_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_COF2_NUMBER].R = SPC5_PWM_SMOD2_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_CAF2_NUMBER].R = SPC5_PWM_SMOD2_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_FFLAG_NUMBER].R = SPC5_PWM_SMOD2_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_REF_NUMBER].R = SPC5_PWM_SMOD2_PRIORITY; #endif #if (SPC5_PWM_USE_SMOD3) /* Driver initialization.*/ pwmObjectInit(&PWMD4); PWMD4.flexpwmp = &FLEXPWM_0; INTC.PSR[SPC5_FLEXPWM0_RF3_NUMBER].R = SPC5_PWM_SMOD3_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_COF3_NUMBER].R = SPC5_PWM_SMOD3_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_CAF3_NUMBER].R = SPC5_PWM_SMOD3_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_FFLAG_NUMBER].R = SPC5_PWM_SMOD3_PRIORITY; INTC.PSR[SPC5_FLEXPWM0_REF_NUMBER].R = SPC5_PWM_SMOD3_PRIORITY; #endif } /** * @brief Configures and activates the PWM peripheral. * @note Starting a driver that is already in the @p PWM_READY state * disables all the active channels. * * @param[in] pwmp pointer to a @p PWMDriver object * * @notapi */ void pwm_lld_start(PWMDriver *pwmp) { if (pwmp->state == PWM_STOP) { uint8_t SMOD0 = 0; uint8_t SMOD1 = 0; uint8_t SMOD2 = 0; uint8_t SMOD3 = 0; #if SPC5_PWM_USE_SMOD0 if (PWMD1.state == PWM_READY) SMOD0 = 1U; #endif #if SPC5_PWM_USE_SMOD1 if (PWMD2.state == PWM_READY) SMOD1 = 1U; #endif #if SPC5_PWM_USE_SMOD2 if (PWMD3.state == PWM_READY) SMOD2 = 1U; #endif #if SPC5_PWM_USE_SMOD3 if (PWMD4.state == PWM_READY) SMOD3 = 1U; #endif /* Set Peripheral Clock.*/ if (!(SMOD0 || SMOD1 || SMOD2 || SMOD3)) { halSPCSetPeripheralClockMode(SPC5_FLEXPWM0_PCTL, SPC5_PWM_FLEXPWM0_START_PCTL); } #if SPC5_PWM_USE_SMOD0 if (&PWMD1 == pwmp) { pwm_lld_start_submodule(pwmp, 0); } #endif #if SPC5_PWM_USE_SMOD1 if (&PWMD2 == pwmp) { pwm_lld_start_submodule(pwmp, 1); } #endif #if SPC5_PWM_USE_SMOD2 if (&PWMD3 == pwmp) { pwm_lld_start_submodule(pwmp, 2); } #endif #if SPC5_PWM_USE_SMOD3 if (&PWMD4 == pwmp) { pwm_lld_start_submodule(pwmp, 3); } #endif } else { /* Driver re-configuration scenario, it must be stopped first.*/ #if SPC5_PWM_USE_SMOD0 if (&PWMD1 == pwmp) { /* Disable the interrupts.*/ pwmp->flexpwmp->SUB[0].INTEN.R = 0x0000; /* Disable the submodule.*/ pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b1110; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b1110; /* Active the submodule masks.*/ pwmp->flexpwmp->MASK.B.MASKA &= 0b1110; pwmp->flexpwmp->MASK.B.MASKB &= 0b1110; /* Sets the MASK registers.*/ pwmp->flexpwmp->SUB[0].CTRL2.B.FRCEN = 1U; pwmp->flexpwmp->SUB[0].CTRL2.B.FORCE_SEL = 0b000; pwmp->flexpwmp->SUB[0].CTRL2.B.FORCE = 1U; } #endif #if SPC5_PWM_USE_SMOD1 if (&PWMD2 == pwmp) { /* Disable the interrupts.*/ pwmp->flexpwmp->SUB[1].INTEN.R = 0x0000; /* Disable the submodule.*/ pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b1101; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b1101; /* Active the submodule masks.*/ pwmp->flexpwmp->MASK.B.MASKA &= 0b1101; pwmp->flexpwmp->MASK.B.MASKB &= 0b1101; /* Sets the MASK registers.*/ pwmp->flexpwmp->SUB[1].CTRL2.B.FRCEN = 1U; pwmp->flexpwmp->SUB[1].CTRL2.B.FORCE_SEL = 0b000; pwmp->flexpwmp->SUB[1].CTRL2.B.FORCE = 1U; } #endif #if SPC5_PWM_USE_SMOD2 if (&PWMD3 == pwmp) { /* Disable the interrupts.*/ pwmp->flexpwmp->SUB[2].INTEN.R = 0x0000; /* Disable the submodule.*/ pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b1011; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b1011; /* Active the submodule masks.*/ pwmp->flexpwmp->MASK.B.MASKA &= 0b1011; pwmp->flexpwmp->MASK.B.MASKB &= 0b1011; /* Sets the MASK registers.*/ pwmp->flexpwmp->SUB[2].CTRL2.B.FRCEN = 1U; pwmp->flexpwmp->SUB[2].CTRL2.B.FORCE_SEL = 0b000; pwmp->flexpwmp->SUB[2].CTRL2.B.FORCE = 1U; } #endif #if SPC5_PWM_USE_SMOD3 if (&PWMD4 == pwmp) { /* Disable the interrupts.*/ pwmp->flexpwmp->SUB[3].INTEN.R = 0x0000; /* Disable the submodule.*/ pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b0111; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b0111; /* Active the submodule masks.*/ pwmp->flexpwmp->MASK.B.MASKA &= 0b0111; pwmp->flexpwmp->MASK.B.MASKB &= 0b0111; /* Sets the MASK registers.*/ pwmp->flexpwmp->SUB[3].CTRL2.B.FRCEN = 1U; pwmp->flexpwmp->SUB[3].CTRL2.B.FORCE_SEL = 0b000; pwmp->flexpwmp->SUB[3].CTRL2.B.FORCE = 1U; } #endif } } /** * @brief Deactivates the PWM peripheral. * * @param[in] pwmp pointer to a @p PWMDriver object * * @notapi */ void pwm_lld_stop(PWMDriver *pwmp) { /* If in ready state then disables the PWM clock.*/ if (pwmp->state == PWM_READY) { #if SPC5_PWM_USE_SMOD0 if (&PWMD1 == pwmp) { /* SMOD stop.*/ pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b0001; pwmp->flexpwmp->SUB[0].INTEN.R = 0x0000; pwmp->flexpwmp->SUB[0].STS.R = 0xFFFF; pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b1110; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b1110; pwmp->flexpwmp->MCTRL.B.RUN &= 0b1110; } #endif #if SPC5_PWM_USE_SMOD1 if (&PWMD2 == pwmp) { /* SMOD stop.*/ pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b0010; pwmp->flexpwmp->SUB[1].INTEN.R = 0x0000; pwmp->flexpwmp->SUB[1].STS.R = 0xFFFF; pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b1101; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b1101; pwmp->flexpwmp->MCTRL.B.RUN &= 0b1101; } #endif #if SPC5_PWM_USE_SMOD2 if (&PWMD3 == pwmp) { /* SMOD stop.*/ pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b0100; pwmp->flexpwmp->SUB[2].INTEN.R = 0x0000; pwmp->flexpwmp->SUB[2].STS.R = 0xFFFF; pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b1011; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b1011; pwmp->flexpwmp->MCTRL.B.RUN &= 0b1011; } #endif #if SPC5_PWM_USE_SMOD3 if (&PWMD4 == pwmp) { /* SMOD stop.*/ pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b1000; pwmp->flexpwmp->SUB[3].INTEN.R = 0x0000; pwmp->flexpwmp->SUB[3].STS.R = 0xFFFF; pwmp->flexpwmp->OUTEN.B.PWMA_EN &= 0b0111; pwmp->flexpwmp->OUTEN.B.PWMB_EN &= 0b0111; pwmp->flexpwmp->MCTRL.B.RUN &= 0b0111; } #endif /* Disable peripheral clock if there is not an activated module.*/ if ((pwmp->flexpwmp->MCTRL.B.RUN & 0b0001) || (pwmp->flexpwmp->MCTRL.B.RUN & 0b0010) || (pwmp->flexpwmp->MCTRL.B.RUN & 0b0100) || (pwmp->flexpwmp->MCTRL.B.RUN & 0b1000) == 0) { halSPCSetPeripheralClockMode(SPC5_FLEXPWM0_PCTL, SPC5_PWM_FLEXPWM0_STOP_PCTL); } } } /** * @brief Enables a PWM channel. * @pre The PWM unit must have been activated using @p pwmStart(). * @post The channel is active using the specified configuration. * @note The function has effect at the next cycle start. * * @param[in] pwmp pointer to a @p PWMDriver object * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) * @param[in] width PWM pulse width as clock pulses number * * @notapi */ void pwm_lld_enable_channel(PWMDriver *pwmp, pwmchannel_t channel, pwmcnt_t width) { #if SPC5_PWM_USE_SMOD0 if (&PWMD1 == pwmp) { pwm_lld_enable_submodule_channel(pwmp, channel, width, 0); } #endif #if SPC5_PWM_USE_SMOD1 if (&PWMD2 == pwmp) { pwm_lld_enable_submodule_channel(pwmp, channel, width, 1); } #endif #if SPC5_PWM_USE_SMOD2 if (&PWMD3 == pwmp) { pwm_lld_enable_submodule_channel(pwmp, channel, width, 2); } #endif #if SPC5_PWM_USE_SMOD3 if (&PWMD4 == pwmp) { pwm_lld_enable_submodule_channel(pwmp, channel, width, 3); } #endif } /** * @brief Disables a PWM channel. * @pre The PWM unit must have been activated using @p pwmStart(). * @post The channel is disabled and its output line returned to the * idle state. * @note The function has effect at the next cycle start. * * @param[in] pwmp pointer to a @p PWMDriver object * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) * * @notapi */ void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) { #if SPC5_PWM_USE_SMOD0 if (&PWMD1 == pwmp) { pwm_lld_disable_submodule_channel(pwmp, channel, 0); } #endif #if SPC5_PWM_USE_SMOD1 if (&PWMD2 == pwmp) { pwm_lld_disable_submodule_channel(pwmp, channel, 1); } #endif #if SPC5_PWM_USE_SMOD2 if (&PWMD3 == pwmp) { pwm_lld_disable_submodule_channel(pwmp, channel, 2); } #endif #if SPC5_PWM_USE_SMOD3 if (&PWMD4 == pwmp) { pwm_lld_disable_submodule_channel(pwmp, channel, 3); } #endif } /** * @brief Changes the period the PWM peripheral. * @details This function changes the period of a PWM unit that has already * been activated using @p pwmStart(). * @pre The PWM unit must have been activated using @p pwmStart(). * @post The PWM unit period is changed to the new value. * @note The function has effect at the next cycle start. * @note If a period is specified that is shorter than the pulse width * programmed in one of the channels then the behavior is not * guaranteed. * * @param[in] pwmp pointer to a @p PWMDriver object * @param[in] period new cycle time in ticks * * @notapi */ void pwm_lld_change_period(PWMDriver *pwmp, pwmcnt_t period) { pwmcnt_t pwmperiod; pwmperiod = period; #if SPC5_PWM_USE_SMOD0 if (&PWMD1 == pwmp) { pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b0001; /* Setting PWM period.*/ pwmp->flexpwmp->SUB[0].INIT.R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[0].VAL[0].R = 0x0000; pwmp->flexpwmp->SUB[0].VAL[1].R = pwmperiod / 2; switch (pwmp->config->mode & PWM_OUTPUT_MASK) { case EDGE_ALIGNED_PWM: /* Setting active front of PWM channels.*/ pwmp->flexpwmp->SUB[0].VAL[2].R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[0].VAL[4].R = ~(pwmperiod / 2) + 1U; break; default: ; } pwmp->flexpwmp->MCTRL.B.LDOK |= 0b0001; } #endif #if SPC5_PWM_USE_SMOD1 if (&PWMD2 == pwmp) { pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b0010; /* Setting PWM period.*/ pwmp->flexpwmp->SUB[1].INIT.R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[1].VAL[0].R = 0x0000; pwmp->flexpwmp->SUB[1].VAL[1].R = pwmperiod / 2; switch (pwmp->config->mode & PWM_OUTPUT_MASK) { case EDGE_ALIGNED_PWM: /* Setting active front of PWM channels.*/ pwmp->flexpwmp->SUB[1].VAL[2].R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[1].VAL[4].R = ~(pwmperiod / 2) + 1U; break; default: ; } pwmp->flexpwmp->MCTRL.B.LDOK |= 0b0010; } #endif #if SPC5_PWM_USE_SMOD2 if (&PWMD3 == pwmp) { pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b0100; /* Setting PWM period.*/ pwmp->flexpwmp->SUB[2].INIT.R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[2].VAL[0].R = 0x0000; pwmp->flexpwmp->SUB[2].VAL[1].R = pwmperiod / 2; switch (pwmp->config->mode & PWM_OUTPUT_MASK) { case EDGE_ALIGNED_PWM: /* Setting active front of PWM channels.*/ pwmp->flexpwmp->SUB[2].VAL[2].R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[2].VAL[4].R = ~(pwmperiod / 2) + 1U; break; default: ; } pwmp->flexpwmp->MCTRL.B.LDOK |= 0b0100; } #endif #if SPC5_PWM_USE_SMOD3 if (&PWMD4 == pwmp) { pwmp->flexpwmp->MCTRL.B.CLDOK |= 0b1000; /* Setting PWM period.*/ pwmp->flexpwmp->SUB[3].INIT.R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[3].VAL[0].R = 0x0000; pwmp->flexpwmp->SUB[3].VAL[1].R = pwmperiod / 2; switch (pwmp->config->mode & PWM_OUTPUT_MASK) { case EDGE_ALIGNED_PWM: /* Setting active front of PWM channels.*/ pwmp->flexpwmp->SUB[3].VAL[2].R = ~(pwmperiod / 2) + 1U; pwmp->flexpwmp->SUB[3].VAL[4].R = ~(pwmperiod / 2) + 1U; break; default: ; } pwmp->flexpwmp->MCTRL.B.LDOK |= 0b1000; } #endif } #endif /* HAL_USE_PWM */ /** @} */