aboutsummaryrefslogtreecommitdiffstats
path: root/os
diff options
context:
space:
mode:
Diffstat (limited to 'os')
-rw-r--r--os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.c1108
-rw-r--r--os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.h349
2 files changed, 1457 insertions, 0 deletions
diff --git a/os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.c b/os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.c
new file mode 100644
index 000000000..6d7416a32
--- /dev/null
+++ b/os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.c
@@ -0,0 +1,1108 @@
+/*
+ * 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 SPC560Pxx/pwm_lld.c
+ * @brief SPC560Pxx low level FlexPWM 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) {
+
+ (void)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 */
+
+/** @} */
diff --git a/os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.h b/os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.h
new file mode 100644
index 000000000..e7b9cf6af
--- /dev/null
+++ b/os/hal/platforms/SPC5xx/FlexPWM_v1/pwm_lld.h
@@ -0,0 +1,349 @@
+/*
+ * 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 SPC560Pxx/pwm_lld.h
+ * @brief SPC560Pxx low level FlexPWM driver header.
+ *
+ * @addtogroup PWM
+ * @{
+ */
+
+#ifndef _PWM_LLD_H_
+#define _PWM_LLD_H_
+
+#if HAL_USE_PWM || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @name LINIER register bits definitions
+ * @{
+ */
+#define SPC5_STS_CMPF0 (1U << 0)
+#define SPC5_STS_CMPF1 (1U << 1)
+#define SPC5_STS_CMPF2 (1U << 2)
+#define SPC5_STS_CMPF3 (1U << 3)
+#define SPC5_STS_CMPF4 (1U << 4)
+#define SPC5_STS_CMPF5 (1U << 5)
+#define SPC5_STS_CFX0 (1U << 6)
+#define SPC5_STS_CFX1 (1U << 7)
+#define SPC5_STS_RF (1U << 12)
+#define SPC5_STS_REF (1U << 13)
+#define SPC5_STS_RUF (1U << 14)
+/** @} */
+
+/**
+ * @brief Number of PWM channels per PWM driver.
+ */
+#define PWM_CHANNELS 2
+
+/**
+ * @brief Complementary output modes mask.
+ * @note This is an SPC5-specific setting.
+ */
+#define PWM_COMPLEMENTARY_OUTPUT_MASK 0xF0
+
+/**
+ * @brief Complementary output not driven.
+ * @note This is an SPC5-specific setting.
+ */
+#define PWM_COMPLEMENTARY_OUTPUT_DISABLED 0x00
+
+/**
+ * @brief Complementary output, active is logic level one.
+ * @note This is an SPC5-specific setting.
+ */
+#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH 0x10
+
+/**
+ * @brief Complementary output, active is logic level zero.
+ * @note This is an SPC5-specific setting.
+ */
+#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW 0x20
+
+/**
+ * @brief Edge-Aligned PWM functional mode.
+ * @note This is an SPC5-specific setting.
+ */
+#define EDGE_ALIGNED_PWM 0x01
+
+/**
+ * @brief Center-Aligned PWM functional mode.
+ * @note This is an SPC5-specific setting.
+ */
+#define CENTER_ALIGNED_PWM 0x02
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+
+/**
+ * @brief PWMD1 driver enable switch.
+ * @details If set to @p TRUE the support for PWMD1 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(SPC5_PWM_USE_SMOD0) || defined(__DOXYGEN__)
+#define SPC5_PWM_USE_SMOD0 TRUE
+#endif
+
+/**
+ * @brief PWMD2 driver enable switch.
+ * @details If set to @p TRUE the support for PWMD2 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(SPC5_PWM_USE_SMOD1) || defined(__DOXYGEN__)
+#define SPC5_PWM_USE_SMOD1 TRUE
+#endif
+
+/**
+ * @brief PWMD3 driver enable switch.
+ * @details If set to @p TRUE the support for PWMD3 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(SPC5_PWM_USE_SMOD2) || defined(__DOXYGEN__)
+#define SPC5_PWM_USE_SMOD2 TRUE
+#endif
+
+/**
+ * @brief PWMD4 driver enable switch.
+ * @details If set to @p TRUE the support for PWMD4 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(SPC5_PWM_USE_SMOD3) || defined(__DOXYGEN__)
+#define SPC5_PWM_USE_SMOD3 TRUE
+#endif
+
+/**
+ * @brief PWMD1 interrupt priority level setting.
+ */
+#if !defined(SPC5_PWM_SMOD0_PRIORITY) || defined(__DOXYGEN__)
+#define SPC5_PWM_SMOD0_PRIORITY 7
+#endif
+
+/**
+ * @brief PWMD2 interrupt priority level setting.
+ */
+#if !defined(SPC5_PWM_SMOD1_PRIORITY) || defined(__DOXYGEN__)
+#define SPC5_PWM_SMOD1_PRIORITY 7
+#endif
+
+/**
+ * @brief PWMD3 interrupt priority level setting.
+ */
+#if !defined(SPC5_PWM_SMOD2_PRIORITY) || defined(__DOXYGEN__)
+#define SPC5_PWM_SMOD2_PRIORITY 7
+#endif
+
+/**
+ * @brief PWMD4 interrupt priority level setting.
+ */
+#if !defined(SPC5_PWM_SMOD3_PRIORITY) || defined(__DOXYGEN__)
+#define SPC5_PWM_SMOD3_PRIORITY 7
+#endif
+
+/**
+ * @brief FlexPWM-0 peripheral configuration when started.
+ * @note The default configuration is 1 (always run) in run mode and
+ * 2 (only halt) in low power mode. The defaults of the run modes
+ * are defined in @p hal_lld.h.
+ */
+#if !defined(SPC5_PWM_FLEXPWM0_START_PCTL) || defined(__DOXYGEN__)
+#define SPC5_PWM_FLEXPWM0_START_PCTL (SPC5_ME_PCTL_RUN(1) | \
+ SPC5_ME_PCTL_LP(2))
+#endif
+
+/**
+ * @brief FlexPWM-0 peripheral configuration when stopped.
+ * @note The default configuration is 0 (never run) in run mode and
+ * 0 (never run) in low power mode. The defaults of the run modes
+ * are defined in @p hal_lld.h.
+ */
+#if !defined(SPC5_PWM_FLEXPWM0_STOP_PCTL) || defined(__DOXYGEN__)
+#define SPC5_PWM_FLEXPWM0_STOP_PCTL (SPC5_ME_PCTL_RUN(0) | \
+ SPC5_ME_PCTL_LP(0))
+#endif
+
+/*===========================================================================*/
+/* Configuration checks. */
+/*===========================================================================*/
+
+#if SPC5_PWM_USE_SMOD0 && !SPC5_HAS_FLEXPWM0
+#error "SMOD0 not present in the selected device"
+#endif
+
+#if SPC5_PWM_USE_SMOD1 && !SPC5_HAS_FLEXPWM0
+#error "SMOD1 not present in the selected device"
+#endif
+
+#if SPC5_PWM_USE_SMDO2 && !SPC5_HAS_FLEXPWM0
+#error "SMOD2 not present in the selected device"
+#endif
+
+#if SPC5_PWM_USE_SMOD3 && !SPC5_HAS_FLEXPWM0
+#error "SMOD3 not present in the selected device"
+#endif
+
+#if (!SPC5_PWM_USE_SMOD0 && !SPC5_PWM_USE_SMOD1 && \
+ !SPC5_PWM_USE_SMOD2 && !SPC5_PWM_USE_SMOD3) && SPC5_PWM_USE_FLEXPWM0
+#error "PWM driver activated but no SubModule assigned"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief PWM mode type.
+ */
+typedef uint32_t pwmmode_t;
+
+/**
+ * @brief PWM channel type.
+ */
+typedef uint8_t pwmchannel_t;
+
+/**
+ * @brief PWM counter type.
+ */
+typedef uint16_t pwmcnt_t;
+
+/**
+ * @brief PWM driver channel configuration structure.
+ */
+typedef struct {
+ /**
+ * @brief Channel active logic level.
+ */
+ pwmmode_t mode;
+ /**
+ * @brief Channel callback pointer.
+ * @note This callback is invoked on the channel compare event. If set to
+ * @p NULL then the callback is disabled.
+ */
+ pwmcallback_t callback;
+ /* End of the mandatory fields.*/
+} PWMChannelConfig;
+
+/**
+ * @brief PWM driver configuration structure.
+ */
+typedef struct {
+ /**
+ * @brief Timer clock in Hz.
+ * @note The low level can use assertions in order to catch invalid
+ * frequency specifications.
+ */
+ uint32_t frequency;
+ /**
+ * @brief PWM period in ticks.
+ * @note The low level can use assertions in order to catch invalid
+ * period specifications.
+ */
+ pwmcnt_t period;
+ /**
+ * @brief Periodic callback pointer.
+ * @note This callback is invoked on PWM counter reset. If set to
+ * @p NULL then the callback is disabled.
+ */
+ pwmcallback_t callback;
+ /**
+ * @brief Channels configurations.
+ */
+ PWMChannelConfig channels[PWM_CHANNELS];
+ /* End of the mandatory fields.*/
+ /**
+ * @brief PWM functional mode.
+ */
+ pwmmode_t mode;
+} PWMConfig;
+
+/**
+ * @brief Structure representing a PWM driver.
+ */
+struct PWMDriver {
+ /**
+ * @brief Driver state.
+ */
+ pwmstate_t state;
+ /**
+ * @brief Current driver configuration data.
+ */
+ const PWMConfig *config;
+ /**
+ * @brief Current PWM period in ticks.
+ */
+ pwmcnt_t period;
+#if defined(PWM_DRIVER_EXT_FIELDS)
+ PWM_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @Pointer to the volatile FlexPWM registers block.
+ */
+ volatile struct FLEXPWM_tag *flexpwmp;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if SPC5_PWM_USE_SMOD0 && !defined(__DOXYGEN__)
+extern PWMDriver PWMD1;
+#endif
+
+#if SPC5_PWM_USE_SMOD1 && !defined(__DOXYGEN__)
+extern PWMDriver PWMD2;
+#endif
+
+#if SPC5_PWM_USE_SMOD2 && !defined(__DOXYGEN__)
+extern PWMDriver PWMD3;
+#endif
+
+#if SPC5_PWM_USE_SMOD3 && !defined(__DOXYGEN__)
+extern PWMDriver PWMD4;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void pwm_lld_init(void);
+ void pwm_lld_start(PWMDriver *pwmp);
+ void pwm_lld_stop(PWMDriver *pwmp);
+ void pwm_lld_enable_channel(PWMDriver *pwmp,
+ pwmchannel_t channel,
+ pwmcnt_t width);
+ void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel);
+ void pwm_lld_change_period(PWMDriver *pwmp, pwmcnt_t period);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_PWM */
+
+#endif /* _PWM_LLD_H_ */
+
+/** @} */