diff --git a/os/hal/include/pwm.h b/os/hal/include/pwm.h index d7176660e..6ecd93717 100644 --- a/os/hal/include/pwm.h +++ b/os/hal/include/pwm.h @@ -87,7 +87,7 @@ typedef enum { typedef struct PWMDriver PWMDriver; /** - * @brief PWM notification callback type. + * @brief Type of a PWM notification callback. * * @param[in] pwmp pointer to a @p PWMDriver object */ @@ -187,13 +187,15 @@ typedef void (*pwmcallback_t)(PWMDriver *pwmp); * or immediately (fallback implementation). * * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * @param[in] channel PWM channel identifier (0...channels-1) * @param[in] width PWM pulse width as clock pulses number * * @iclass */ -#define pwmEnableChannelI(pwmp, channel, width) \ - pwm_lld_enable_channel(pwmp, channel, width) +#define pwmEnableChannelI(pwmp, channel, width) do { \ + (pwmp)->enabled |= 1 << (channel); \ + pwm_lld_enable_channel(pwmp, channel, width); \ +} while (0) /** * @brief Disables a PWM channel. @@ -205,24 +207,26 @@ typedef void (*pwmcallback_t)(PWMDriver *pwmp); * or immediately (fallback implementation). * * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * @param[in] channel PWM channel identifier (0...channels-1) * * @iclass */ -#define pwmDisableChannelI(pwmp, channel) \ - pwm_lld_disable_channel(pwmp, channel) +#define pwmDisableChannelI(pwmp, channel) do { \ + (pwmp)->enabled &= ~(1 << (channel)); \ + pwm_lld_disable_channel(pwmp, channel); \ +} while (0) /** * @brief Returns a PWM channel status. * @pre The PWM unit must have been activated using @p pwmStart(). * * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * @param[in] channel PWM channel identifier (0...channels-1) * * @iclass */ #define pwmIsChannelEnabledI(pwmp, channel) \ - pwm_lld_is_channel_enabled(pwmp, channel) + ((bool)((pwmp)->enabled & (1 << (channel)))) /** @} */ /*===========================================================================*/ @@ -241,6 +245,10 @@ extern "C" { pwmchannel_t channel, pwmcnt_t width); void pwmDisableChannel(PWMDriver *pwmp, pwmchannel_t channel); + void pwmEnablePeriodicNotification(PWMDriver *pwmp); + void pwmDisablePeriodicNotification(PWMDriver *pwmp); + void pwmEnableChannelNotification(PWMDriver *pwmp, pwmchannel_t channel); + void pwmDisableChannelNotification(PWMDriver *pwmp, pwmchannel_t channel); #ifdef __cplusplus } #endif diff --git a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c b/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c index 07055fba4..2b633c2e2 100644 --- a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c +++ b/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c @@ -114,15 +114,19 @@ static void pwm_lld_serve_interrupt(PWMDriver *pwmp) { sr = pwmp->tim->SR; sr &= pwmp->tim->DIER & STM32_TIM_DIER_IRQ_MASK; pwmp->tim->SR = ~sr; - if ((sr & STM32_TIM_SR_CC1IF) != 0) + if (((sr & STM32_TIM_SR_CC1IF) != 0) && + (pwmp->config->channels[0].callback != NULL)) pwmp->config->channels[0].callback(pwmp); - if ((sr & STM32_TIM_SR_CC2IF) != 0) + if (((sr & STM32_TIM_SR_CC2IF) != 0) && + (pwmp->config->channels[1].callback != NULL)) pwmp->config->channels[1].callback(pwmp); - if ((sr & STM32_TIM_SR_CC3IF) != 0) + if (((sr & STM32_TIM_SR_CC3IF) != 0) && + (pwmp->config->channels[2].callback != NULL)) pwmp->config->channels[2].callback(pwmp); - if ((sr & STM32_TIM_SR_CC4IF) != 0) + if (((sr & STM32_TIM_SR_CC4IF) != 0) && + (pwmp->config->channels[3].callback != NULL)) pwmp->config->channels[3].callback(pwmp); - if ((sr & STM32_TIM_SR_UIF) != 0) + if (((sr & STM32_TIM_SR_UIF) != 0) && (pwmp->config->callback != NULL)) pwmp->config->callback(pwmp); } #endif /* STM32_PWM_USE_TIM2 || ... || STM32_PWM_USE_TIM5 */ @@ -148,7 +152,8 @@ OSAL_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) { OSAL_IRQ_PROLOGUE(); STM32_TIM1->SR = ~STM32_TIM_SR_UIF; - PWMD1.config->callback(&PWMD1); + if (PWMD1.config->callback != NULL) + PWMD1.config->callback(&PWMD1); OSAL_IRQ_EPILOGUE(); } @@ -169,15 +174,22 @@ OSAL_IRQ_HANDLER(STM32_TIM1_CC_HANDLER) { OSAL_IRQ_PROLOGUE(); - sr = STM32_TIM1->SR & STM32_TIM1->DIER & STM32_TIM_DIER_IRQ_MASK; + sr = STM32_TIM1->SR & STM32_TIM1->DIER & (STM32_TIM_DIER_CC1IE | + STM32_TIM_DIER_CC2IE | + STM32_TIM_DIER_CC3IE | + STM32_TIM_DIER_CC4IE); STM32_TIM1->SR = ~sr; - if ((sr & STM32_TIM_SR_CC1IF) != 0) + if (((sr & STM32_TIM_SR_CC1IF) != 0) && + (PWMD1.config->channels[0].callback != NULL)) PWMD1.config->channels[0].callback(&PWMD1); - if ((sr & STM32_TIM_SR_CC2IF) != 0) + if (((sr & STM32_TIM_SR_CC2IF) != 0) && + (PWMD1.config->channels[1].callback != NULL)) PWMD1.config->channels[1].callback(&PWMD1); - if ((sr & STM32_TIM_SR_CC3IF) != 0) + if (((sr & STM32_TIM_SR_CC3IF) != 0) && + (PWMD1.config->channels[2].callback != NULL)) PWMD1.config->channels[2].callback(&PWMD1); - if ((sr & STM32_TIM_SR_CC4IF) != 0) + if (((sr & STM32_TIM_SR_CC4IF) != 0) && + (PWMD1.config->channels[3].callback != NULL)) PWMD1.config->channels[3].callback(&PWMD1); OSAL_IRQ_EPILOGUE(); @@ -277,7 +289,8 @@ OSAL_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) { OSAL_IRQ_PROLOGUE(); STM32_TIM8->SR = ~TIM_SR_UIF; - PWMD8.config->callback(&PWMD8); + if (PWMD8.config->callback != NULL) + PWMD8.config->callback(&PWMD8); OSAL_IRQ_EPILOGUE(); } @@ -298,15 +311,22 @@ OSAL_IRQ_HANDLER(STM32_TIM8_CC_HANDLER) { OSAL_IRQ_PROLOGUE(); - sr = STM32_TIM8->SR & STM32_TIM8->DIER & STM32_TIM_DIER_IRQ_MASK; + sr = STM32_TIM8->SR & STM32_TIM8->DIER & (STM32_TIM_DIER_CC1IE | + STM32_TIM_DIER_CC2IE | + STM32_TIM_DIER_CC3IE | + STM32_TIM_DIER_CC4IE); STM32_TIM8->SR = ~sr; - if ((sr & STM32_TIM_SR_CC1IF) != 0) + if (((sr & STM32_TIM_SR_CC1IF) != 0) && + (PWMD8.config->channels[0].callback != NULL)) PWMD8.config->channels[0].callback(&PWMD8); - if ((sr & STM32_TIM_SR_CC2IF) != 0) + if (((sr & STM32_TIM_SR_CC2IF) != 0) && + (PWMD8.config->channels[1].callback != NULL)) PWMD8.config->channels[1].callback(&PWMD8); - if ((sr & STM32_TIM_SR_CC3IF) != 0) + if (((sr & STM32_TIM_SR_CC3IF) != 0) && + (PWMD8.config->channels[2].callback != NULL)) PWMD8.config->channels[2].callback(&PWMD8); - if ((sr & STM32_TIM_SR_CC4IF) != 0) + if (((sr & STM32_TIM_SR_CC4IF) != 0) && + (PWMD8.config->channels[3].callback != NULL)) PWMD8.config->channels[3].callback(&PWMD8); OSAL_IRQ_EPILOGUE(); @@ -346,42 +366,49 @@ void pwm_lld_init(void) { #if STM32_PWM_USE_TIM1 /* Driver initialization.*/ pwmObjectInit(&PWMD1); + PWMD1.channels = STM32_TIM1_CHANNELS; PWMD1.tim = STM32_TIM1; #endif #if STM32_PWM_USE_TIM2 /* Driver initialization.*/ pwmObjectInit(&PWMD2); + PWMD2.channels = STM32_TIM2_CHANNELS; PWMD2.tim = STM32_TIM2; #endif #if STM32_PWM_USE_TIM3 /* Driver initialization.*/ pwmObjectInit(&PWMD3); + PWMD3.channels = STM32_TIM3_CHANNELS; PWMD3.tim = STM32_TIM3; #endif #if STM32_PWM_USE_TIM4 /* Driver initialization.*/ pwmObjectInit(&PWMD4); + PWMD4.channels = STM32_TIM4_CHANNELS; PWMD4.tim = STM32_TIM4; #endif #if STM32_PWM_USE_TIM5 /* Driver initialization.*/ pwmObjectInit(&PWMD5); + PWMD5.channels = STM32_TIM5_CHANNELS; PWMD5.tim = STM32_TIM5; #endif #if STM32_PWM_USE_TIM8 /* Driver initialization.*/ pwmObjectInit(&PWMD8); + PWMD8.channels = STM32_TIM8_CHANNELS; PWMD8.tim = STM32_TIM8; #endif #if STM32_PWM_USE_TIM9 /* Driver initialization.*/ pwmObjectInit(&PWMD9); + PWMD9.channels = STM32_TIM9_CHANNELS; PWMD9.tim = STM32_TIM9; #endif } @@ -475,6 +502,10 @@ void pwm_lld_start(PWMDriver *pwmp) { STM32_TIM_CCMR1_OC2M(6) | STM32_TIM_CCMR1_OC2PE; pwmp->tim->CCMR2 = STM32_TIM_CCMR2_OC3M(6) | STM32_TIM_CCMR2_OC3PE | STM32_TIM_CCMR2_OC4M(6) | STM32_TIM_CCMR2_OC4PE; +#if STM32_TIM_MAX_CHANNELS > 4 + pwmp->tim->CCMR3 = STM32_TIM_CCMR3_OC5M(6) | STM32_TIM_CCMR3_OC5PE | + STM32_TIM_CCMR3_OC6M(6) | STM32_TIM_CCMR3_OC6PE; +#endif } else { /* Driver re-configuration scenario, it must be stopped first.*/ @@ -486,6 +517,12 @@ void pwm_lld_start(PWMDriver *pwmp) { pwmp->tim->CCR[1] = 0; /* Comparator 2 disabled. */ pwmp->tim->CCR[2] = 0; /* Comparator 3 disabled. */ pwmp->tim->CCR[3] = 0; /* Comparator 4 disabled. */ +#if STM32_TIM_MAX_CHANNELS > 4 + if (pwmp->channels > 4) { + pwmp->tim->CCXR[0] = 0; /* Comparator 5 disabled. */ + pwmp->tim->CCXR[1] = 0; /* Comparator 6 disabled. */ + } +#endif pwmp->tim->CNT = 0; /* Counter reset to zero. */ } @@ -571,7 +608,6 @@ void pwm_lld_start(PWMDriver *pwmp) { pwmp->tim->CCER = ccer; pwmp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */ - pwmp->tim->DIER |= pwmp->config->callback == NULL ? 0 : STM32_TIM_DIER_UIE; pwmp->tim->SR = 0; /* Clear pending IRQs. */ #if STM32_PWM_USE_TIM1 || STM32_PWM_USE_TIM8 #if STM32_PWM_USE_ADVANCED @@ -655,9 +691,10 @@ void pwm_lld_stop(PWMDriver *pwmp) { * @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. + * @note Channel notification is not enabled. * * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * @param[in] channel PWM channel identifier (0...channels-1) * @param[in] width PWM pulse width as clock pulses number * * @notapi @@ -666,36 +703,121 @@ void pwm_lld_enable_channel(PWMDriver *pwmp, pwmchannel_t channel, pwmcnt_t width) { - pwmp->tim->CCR[channel] = width; /* New duty cycle. */ - /* If there is a callback defined for the channel then the associated - interrupt must be enabled.*/ - if (pwmp->config->channels[channel].callback != NULL) { - uint32_t dier = pwmp->tim->DIER; - /* If the IRQ is not already enabled care must be taken to clear it, - it is probably already pending because the timer is running.*/ - if ((dier & (2 << channel)) == 0) { - pwmp->tim->DIER = dier | (2 << channel); - pwmp->tim->SR = ~(2 << channel); - } - } + /* Changing channel duty cycle on the fly.*/ +#if STM32_TIM_MAX_CHANNELS <= 4 + pwmp->tim->CCR[channel] = width; +#else + if (channel <= 4) + pwmp->tim->CCR[channel] = width; + else + pwmp->tim->CCXR[channel - 4] = width; +#endif } /** - * @brief Disables a PWM channel. + * @brief Disables a PWM channel and its notification. * @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) + * @param[in] channel PWM channel identifier (0...channels-1) * * @notapi */ void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) { +#if STM32_TIM_MAX_CHANNELS <= 4 pwmp->tim->CCR[channel] = 0; pwmp->tim->DIER &= ~(2 << channel); +#else + if (channel <= 4) { + pwmp->tim->CCR[channel] = 0; + pwmp->tim->DIER &= ~(2 << channel); + } + else + pwmp->tim->CCXR[channel - 4] = 0; +#endif +} + +/** + * @brief Enables the periodic activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @note If the notification is already enabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_enable_periodic_notification(PWMDriver *pwmp) { + uint32_t dier = pwmp->tim->DIER; + + /* If the IRQ is not already enabled care must be taken to clear it, + it is probably already pending because the timer is running.*/ + if ((dier & STM32_TIM_DIER_UIE) == 0) { + pwmp->tim->DIER = dier | STM32_TIM_DIER_UIE; + pwmp->tim->SR &= STM32_TIM_SR_UIF; + } +} + +/** + * @brief Disables the periodic activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @note If the notification is already disabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_disable_periodic_notification(PWMDriver *pwmp) { + + pwmp->tim->DIER &= ~STM32_TIM_DIER_UIE; +} + +/** + * @brief Enables a channel de-activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @pre The channel must have been activated using @p pwmEnableChannel(). + * @note If the notification is already enabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * + * @notapi + */ +void pwm_lld_enable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel) { + uint32_t dier = pwmp->tim->DIER; + +#if STM32_TIM_MAX_CHANNELS > 4 + /* Channels 4 and 5 do not support callbacks.*/ + osalDbgAssert(channel < 4, "callback not supported"); +#endif + + /* If the IRQ is not already enabled care must be taken to clear it, + it is probably already pending because the timer is running.*/ + if ((dier & (2 << channel)) == 0) { + pwmp->tim->DIER = dier | (2 << channel); + pwmp->tim->SR = ~(2 << channel); + } +} + +/** + * @brief Disables a channel de-activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @pre The channel must have been activated using @p pwmEnableChannel(). + * @note If the notification is already disabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * + * @notapi + */ +void pwm_lld_disable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel) { + + pwmp->tim->DIER &= ~(2 << channel); } #endif /* HAL_USE_PWM */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h b/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h index ca517651e..5b54847fb 100644 --- a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h +++ b/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h @@ -36,8 +36,12 @@ /** * @brief Number of PWM channels per PWM driver. */ -#define PWM_CHANNELS 4 +#define PWM_CHANNELS STM32_TIM_MAX_CHANNELS +/** + * @name STM32-specific PWM complementary output mode macros + * @{ + */ /** * @brief Complementary output modes mask. * @note This is an STM32-specific setting. @@ -67,6 +71,7 @@ * timers TIM1 and TIM8. */ #define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW 0x20 +/** @} */ /*===========================================================================*/ /* Driver pre-compile time settings. */ @@ -283,22 +288,27 @@ /*===========================================================================*/ /** - * @brief PWM mode type. + * @brief Type of a PWM mode. */ typedef uint32_t pwmmode_t; /** - * @brief PWM channel type. + * @brief Type of a PWM channel. */ typedef uint8_t pwmchannel_t; /** - * @brief PWM counter type. + * @brief Type of a channels mask. + */ +typedef uint32_t pwmchnmsk_t; + +/** + * @brief Type of a PWM counter. */ typedef uint16_t pwmcnt_t; /** - * @brief PWM driver channel configuration structure. + * @brief Type of a PWM driver channel configuration structure. */ typedef struct { /** @@ -315,7 +325,7 @@ typedef struct { } PWMChannelConfig; /** - * @brief PWM driver configuration structure. + * @brief Type of a PWM driver configuration structure. */ typedef struct { /** @@ -377,6 +387,14 @@ struct PWMDriver { * @brief Current PWM period in ticks. */ pwmcnt_t period; + /** + * @brief Mask of the enabled channels. + */ + pwmchnmsk_t enabled; + /** + * @brief Number of channels in this instance. + */ + pwmchannel_t channels; #if defined(PWM_DRIVER_EXT_FIELDS) PWM_DRIVER_EXT_FIELDS #endif @@ -414,19 +432,6 @@ struct PWMDriver { #define pwm_lld_change_period(pwmp, period) \ ((pwmp)->tim->ARR = (uint16_t)((period) - 1)) -/** - * @brief Returns a PWM channel status. - * @pre The PWM unit must have been activated using @p pwmStart(). - * - * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) - * - * @notapi - */ -#define pwm_lld_is_channel_enabled(pwmp, channel) \ - (((pwmp)->tim->CCR[channel] != 0) || \ - (((pwmp)->tim->DIER & (2 << channel)) != 0)) - /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ @@ -469,6 +474,12 @@ extern "C" { pwmchannel_t channel, pwmcnt_t width); void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel); + void pwm_lld_enable_periodic_notification(PWMDriver *pwmp); + void pwm_lld_disable_periodic_notification(PWMDriver *pwmp); + void pwm_lld_enable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel); + void pwm_lld_disable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel); #ifdef __cplusplus } #endif diff --git a/os/hal/ports/STM32/LLD/TIMv1/stm32_tim.h b/os/hal/ports/STM32/LLD/TIMv1/stm32_tim.h index 1e0c9ecbb..9770846c6 100644 --- a/os/hal/ports/STM32/LLD/TIMv1/stm32_tim.h +++ b/os/hal/ports/STM32/LLD/TIMv1/stm32_tim.h @@ -431,8 +431,7 @@ typedef struct { volatile uint32_t DMAR; volatile uint32_t OR; volatile uint32_t CCMR3; - volatile uint32_t CCR5; - volatile uint32_t CCR6; + volatile uint32_t CCXR[2]; } stm32_tim_t; /*===========================================================================*/ diff --git a/os/hal/ports/STM32/STM32F0xx/stm32_registry.h b/os/hal/ports/STM32/STM32F0xx/stm32_registry.h index f6d183a8d..a00ff3960 100644 --- a/os/hal/ports/STM32/STM32F0xx/stm32_registry.h +++ b/os/hal/ports/STM32/STM32F0xx/stm32_registry.h @@ -106,6 +106,8 @@ #define STM32_HAS_SPI6 FALSE /* TIM attributes.*/ +#define STM32_TIM_MAX_CHANNELS 4 + #define STM32_HAS_TIM1 TRUE #define STM32_TIM1_IS_32BITS FALSE #define STM32_TIM1_CHANNELS 4 @@ -236,6 +238,8 @@ #define STM32_HAS_SPI6 FALSE /* TIM attributes.*/ +#define STM32_TIM_MAX_CHANNELS 4 + #define STM32_HAS_TIM1 TRUE #define STM32_TIM1_IS_32BITS FALSE #define STM32_TIM1_CHANNELS 4 @@ -363,6 +367,8 @@ #define STM32_HAS_SPI6 FALSE /* TIM attributes.*/ +#define STM32_TIM_MAX_CHANNELS 4 + #define STM32_HAS_TIM1 TRUE #define STM32_TIM1_IS_32BITS FALSE #define STM32_TIM1_CHANNELS 4 diff --git a/os/hal/ports/STM32/STM32F4xx/stm32_registry.h b/os/hal/ports/STM32/STM32F4xx/stm32_registry.h index 8f2d9c442..50a082b33 100644 --- a/os/hal/ports/STM32/STM32F4xx/stm32_registry.h +++ b/os/hal/ports/STM32/STM32F4xx/stm32_registry.h @@ -193,6 +193,8 @@ #endif /* !(defined(STM32F427_437xx) || defined(STM32F429_439xx)) */ /* TIM attributes.*/ +#define STM32_TIM_MAX_CHANNELS 4 + #define STM32_HAS_TIM1 TRUE #define STM32_TIM1_IS_32BITS FALSE #define STM32_TIM1_CHANNELS 4 diff --git a/os/hal/src/pwm.c b/os/hal/src/pwm.c index c10fda3ce..eb35e434e 100644 --- a/os/hal/src/pwm.c +++ b/os/hal/src/pwm.c @@ -73,6 +73,8 @@ void pwmObjectInit(PWMDriver *pwmp) { pwmp->state = PWM_STOP; pwmp->config = NULL; + pwmp->enabled = 0; + pwmp->channels = 0; #if defined(PWM_DRIVER_EXT_INIT_HOOK) PWM_DRIVER_EXT_INIT_HOOK(pwmp); #endif @@ -117,7 +119,8 @@ void pwmStop(PWMDriver *pwmp) { osalDbgAssert((pwmp->state == PWM_STOP) || (pwmp->state == PWM_READY), "invalid state"); pwm_lld_stop(pwmp); - pwmp->state = PWM_STOP; + pwmp->enabled = 0; + pwmp->state = PWM_STOP; osalSysUnlock(); } @@ -155,7 +158,7 @@ void pwmChangePeriod(PWMDriver *pwmp, pwmcnt_t period) { * or immediately (fallback implementation). * * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * @param[in] channel PWM channel identifier (0...channels-1) * @param[in] width PWM pulse width as clock pulses number * * @api @@ -164,16 +167,19 @@ void pwmEnableChannel(PWMDriver *pwmp, pwmchannel_t channel, pwmcnt_t width) { - osalDbgCheck((pwmp != NULL) && (channel < PWM_CHANNELS)); + osalDbgCheck((pwmp != NULL) && (channel < pwmp->channels)); osalSysLock(); + osalDbgAssert(pwmp->state == PWM_READY, "not ready"); - pwm_lld_enable_channel(pwmp, channel, width); + + pwmEnableChannelI(pwmp, channel, width); + osalSysUnlock(); } /** - * @brief Disables a PWM channel. + * @brief Disables a PWM channel and its notification. * @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. @@ -182,17 +188,122 @@ void pwmEnableChannel(PWMDriver *pwmp, * or immediately (fallback implementation). * * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * @param[in] channel PWM channel identifier (0...channels-1) * * @api */ void pwmDisableChannel(PWMDriver *pwmp, pwmchannel_t channel) { - osalDbgCheck((pwmp != NULL) && (channel < PWM_CHANNELS)); + osalDbgCheck((pwmp != NULL) && (channel < pwmp->channels)); osalSysLock(); + osalDbgAssert(pwmp->state == PWM_READY, "not ready"); - pwm_lld_disable_channel(pwmp, channel); + + pwmDisableChannelI(pwmp, channel); + + osalSysUnlock(); +} + +/** + * @brief Enables the periodic activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @note If the notification is already enabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @api + */ +void pwmEnablePeriodicNotification(PWMDriver *pwmp) { + + osalDbgCheck(pwmp != NULL); + + osalSysLock(); + + osalDbgAssert(pwmp->state == PWM_READY, "not ready"); + osalDbgAssert(pwmp->config->callback != NULL, "undefined periodic callback"); + + pwm_lld_enable_periodic_notification(pwmp); + + osalSysUnlock(); +} + +/** + * @brief Disables the periodic activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @note If the notification is already disabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @api + */ +void pwmDisablePeriodicNotification(PWMDriver *pwmp) { + + osalDbgCheck(pwmp != NULL); + + osalSysLock(); + + osalDbgAssert(pwmp->state == PWM_READY, "not ready"); + osalDbgAssert(pwmp->config->callback != NULL, "undefined periodic callback"); + + pwm_lld_disable_periodic_notification(pwmp); + + osalSysUnlock(); +} + +/** + * @brief Enables a channel de-activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @pre The channel must have been activated using @p pwmEnableChannel(). + * @note If the notification is already enabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * + * @api + */ +void pwmEnableChannelNotification(PWMDriver *pwmp, pwmchannel_t channel) { + + osalDbgCheck((pwmp != NULL) && (channel < pwmp->channels)); + + osalSysLock(); + + osalDbgAssert(pwmp->state == PWM_READY, "not ready"); + osalDbgAssert((pwmp->enabled & (1 << channel)) != 0, + "channel not enabled"); + osalDbgAssert(pwmp->config->channels[channel].callback != NULL, + "undefined channel callback"); + + pwm_lld_enable_channel_notification(pwmp, channel); + + osalSysUnlock(); +} + +/** + * @brief Disables a channel de-activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @pre The channel must have been activated using @p pwmEnableChannel(). + * @note If the notification is already disabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * + * @api + */ +void pwmDisableChannelNotification(PWMDriver *pwmp, pwmchannel_t channel) { + + osalDbgCheck((pwmp != NULL) && (channel < pwmp->channels)); + + osalSysLock(); + + osalDbgAssert(pwmp->state == PWM_READY, "not ready"); + osalDbgAssert((pwmp->enabled & (1 << channel)) != 0, + "channel not enabled"); + osalDbgAssert(pwmp->config->channels[channel].callback != NULL, + "undefined channel callback"); + + pwm_lld_disable_channel_notification(pwmp, channel); + osalSysUnlock(); } diff --git a/testhal/STM32/STM32F0xx/PWM-ICU/main.c b/testhal/STM32/STM32F0xx/PWM-ICU/main.c index fa4c4de8c..661635c95 100644 --- a/testhal/STM32/STM32F0xx/PWM-ICU/main.c +++ b/testhal/STM32/STM32F0xx/PWM-ICU/main.c @@ -94,6 +94,7 @@ int main(void) { * The two pins have to be externally connected together. */ pwmStart(&PWMD1, &pwmcfg); + pwmEnablePeriodicNotification(&PWMD1); palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(2)); icuStart(&ICUD3, &icucfg); palSetPadMode(GPIOA, 6, PAL_MODE_ALTERNATE(1)); @@ -104,6 +105,7 @@ int main(void) { * Starts the PWM channel 0 using 75% duty cycle. */ pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 7500)); + pwmEnableChannelNotification(&PWMD1, 0); chThdSleepMilliseconds(5000); /* diff --git a/testhal/STM32/STM32F1xx/PWM-ICU/main.c b/testhal/STM32/STM32F1xx/PWM-ICU/main.c index eecd5b2ad..92805fec4 100644 --- a/testhal/STM32/STM32F1xx/PWM-ICU/main.c +++ b/testhal/STM32/STM32F1xx/PWM-ICU/main.c @@ -92,6 +92,7 @@ int main(void) { * Initializes the PWM driver 1 and ICU driver 4. */ pwmStart(&PWMD1, &pwmcfg); + pwmEnablePeriodicNotification(&PWMD1); palSetPadMode(IOPORT1, 8, PAL_MODE_STM32_ALTERNATE_PUSHPULL); icuStart(&ICUD4, &icucfg); icuEnable(&ICUD4); @@ -101,6 +102,7 @@ int main(void) { * Starts the PWM channel 0 using 75% duty cycle. */ pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 7500)); + pwmEnableChannelNotification(&PWMD1, 0); chThdSleepMilliseconds(5000); /* diff --git a/testhal/STM32/STM32F30x/PWM-ICU/debug/STM32F30x-PWM_ICU (OpenOCD, Flash and Run).launch b/testhal/STM32/STM32F30x/PWM-ICU/debug/STM32F30x-PWM_ICU (OpenOCD, Flash and Run).launch new file mode 100644 index 000000000..3b55accd1 --- /dev/null +++ b/testhal/STM32/STM32F30x/PWM-ICU/debug/STM32F30x-PWM_ICU (OpenOCD, Flash and Run).launch @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testhal/STM32/STM32F30x/PWM-ICU/main.c b/testhal/STM32/STM32F30x/PWM-ICU/main.c index 96444baef..f0af2504a 100644 --- a/testhal/STM32/STM32F30x/PWM-ICU/main.c +++ b/testhal/STM32/STM32F30x/PWM-ICU/main.c @@ -89,6 +89,7 @@ int main(void) { * The two pins have to be externally connected together. */ pwmStart(&PWMD4, &pwmcfg); + pwmEnablePeriodicNotification(&PWMD4); palSetPadMode(GPIOD, 12, PAL_MODE_ALTERNATE(2)); icuStart(&ICUD3, &icucfg); palSetPadMode(GPIOC, 6, PAL_MODE_ALTERNATE(2)); @@ -99,6 +100,7 @@ int main(void) { * Starts the PWM channel 0 using 75% duty cycle. */ pwmEnableChannel(&PWMD4, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD4, 7500)); + pwmEnableChannelNotification(&PWMD4, 0); chThdSleepMilliseconds(5000); /* diff --git a/testhal/STM32/STM32F37x/PWM-ICU/main.c b/testhal/STM32/STM32F37x/PWM-ICU/main.c index ccc877f3f..0178af14a 100644 --- a/testhal/STM32/STM32F37x/PWM-ICU/main.c +++ b/testhal/STM32/STM32F37x/PWM-ICU/main.c @@ -89,6 +89,7 @@ int main(void) { * The two pins have to be externally connected together. */ pwmStart(&PWMD5, &pwmcfg); + pwmEnablePeriodicNotification(&PWMD5); palSetPadMode(GPIOC, 0, PAL_MODE_ALTERNATE(2)); icuStart(&ICUD3, &icucfg); palSetPadMode(GPIOC, 6, PAL_MODE_ALTERNATE(2)); @@ -99,6 +100,7 @@ int main(void) { * Starts the PWM channel 0 using 75% duty cycle. */ pwmEnableChannel(&PWMD5, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD5, 7500)); + pwmEnableChannelNotification(&PWMD5, 0); chThdSleepMilliseconds(5000); /* diff --git a/testhal/STM32/STM32F4xx/PWM-ICU/main.c b/testhal/STM32/STM32F4xx/PWM-ICU/main.c index a964d221e..64da60b1d 100644 --- a/testhal/STM32/STM32F4xx/PWM-ICU/main.c +++ b/testhal/STM32/STM32F4xx/PWM-ICU/main.c @@ -89,6 +89,7 @@ int main(void) { * The two pins have to be externally connected together. */ pwmStart(&PWMD1, &pwmcfg); + pwmEnablePeriodicNotification(&PWMD1); palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(1)); icuStart(&ICUD3, &icucfg); palSetPadMode(GPIOC, 6, PAL_MODE_ALTERNATE(2)); @@ -99,6 +100,7 @@ int main(void) { * Starts the PWM channel 0 using 75% duty cycle. */ pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 7500)); + pwmEnableChannelNotification(&PWMD1, 0); chThdSleepMilliseconds(5000); /* diff --git a/testhal/STM32/STM32L1xx/PWM-ICU/.cproject b/testhal/STM32/STM32L1xx/PWM-ICU/.cproject index b96ec18ee..077ea250c 100644 --- a/testhal/STM32/STM32L1xx/PWM-ICU/.cproject +++ b/testhal/STM32/STM32L1xx/PWM-ICU/.cproject @@ -48,4 +48,5 @@ + diff --git a/testhal/STM32/STM32L1xx/PWM-ICU/main.c b/testhal/STM32/STM32L1xx/PWM-ICU/main.c index 9e5955e4a..654f23379 100644 --- a/testhal/STM32/STM32L1xx/PWM-ICU/main.c +++ b/testhal/STM32/STM32L1xx/PWM-ICU/main.c @@ -89,6 +89,7 @@ int main(void) { * The two pins have to be externally connected together. */ pwmStart(&PWMD2, &pwmcfg); + pwmEnablePeriodicNotification(&PWMD2); palSetPadMode(GPIOA, 15, PAL_MODE_ALTERNATE(1)); icuStart(&ICUD3, &icucfg); palSetPadMode(GPIOC, 6, PAL_MODE_ALTERNATE(2)); @@ -99,6 +100,7 @@ int main(void) { * Starts the PWM channel 0 using 75% duty cycle. */ pwmEnableChannel(&PWMD2, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD2, 7500)); + pwmEnableChannelNotification(&PWMD2, 0); chThdSleepMilliseconds(5000); /*