aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2014-08-30 13:54:04 +0000
committergdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2014-08-30 13:54:04 +0000
commite40ae9763814661fa99e2b546e4f2562fb48650b (patch)
treef3157c29964739ef5e7fcc46688861d63c5b9313
parenta2ee0679d1525a80cdaccc020413bfbb61d9b070 (diff)
downloadChibiOS-e40ae9763814661fa99e2b546e4f2562fb48650b.tar.gz
ChibiOS-e40ae9763814661fa99e2b546e4f2562fb48650b.tar.bz2
ChibiOS-e40ae9763814661fa99e2b546e4f2562fb48650b.zip
Enhanced PWM driver
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7208 35acf78f-673a-0410-8e92-d51de3d6d3f4
-rw-r--r--os/hal/include/pwm.h26
-rw-r--r--os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c188
-rw-r--r--os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h49
-rw-r--r--os/hal/ports/STM32/LLD/TIMv1/stm32_tim.h3
-rw-r--r--os/hal/ports/STM32/STM32F0xx/stm32_registry.h6
-rw-r--r--os/hal/ports/STM32/STM32F4xx/stm32_registry.h2
-rw-r--r--os/hal/src/pwm.c127
-rw-r--r--testhal/STM32/STM32F0xx/PWM-ICU/main.c2
-rw-r--r--testhal/STM32/STM32F1xx/PWM-ICU/main.c2
-rw-r--r--testhal/STM32/STM32F30x/PWM-ICU/debug/STM32F30x-PWM_ICU (OpenOCD, Flash and Run).launch52
-rw-r--r--testhal/STM32/STM32F30x/PWM-ICU/main.c2
-rw-r--r--testhal/STM32/STM32F37x/PWM-ICU/main.c2
-rw-r--r--testhal/STM32/STM32F4xx/PWM-ICU/main.c2
-rw-r--r--testhal/STM32/STM32L1xx/PWM-ICU/.cproject1
-rw-r--r--testhal/STM32/STM32L1xx/PWM-ICU/main.c2
15 files changed, 395 insertions, 71 deletions
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,9 +36,13 @@
/**
* @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");
+
+ 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");
- pwm_lld_disable_channel(pwmp, channel);
+ 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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.cdt.debug.gdbjtag.launchConfigurationType">
+<stringAttribute key="bad_container_name" value="\STM32F30x-PWM-ICU\debug"/>
+<intAttribute key="org.eclipse.cdt.debug.gdbjtag.core.delay" value="1"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.doHalt" value="true"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.doReset" value="true"/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.imageFileName" value=""/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.imageOffset" value=""/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.initCommands" value="set remotetimeout 20&#13;&#10;monitor reset init&#13;&#10;monitor sleep 50&#13;&#10;"/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.ipAddress" value="localhost"/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.jtagDevice" value="Generic TCP/IP"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.loadImage" value="true"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.loadSymbols" value="true"/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.pcRegister" value=""/>
+<intAttribute key="org.eclipse.cdt.debug.gdbjtag.core.portNumber" value="3333"/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.runCommands" value=""/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.setPcRegister" value="false"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.setResume" value="true"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.setStopAt" value="true"/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.stopAt" value="main"/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.symbolsFileName" value=""/>
+<stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.symbolsOffset" value=""/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.useFileForImage" value="false"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.useFileForSymbols" value="false"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.useProjBinaryForImage" value="true"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.useProjBinaryForSymbols" value="true"/>
+<booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.useRemoteTarget" value="true"/>
+<stringAttribute key="org.eclipse.cdt.debug.mi.core.DEBUG_NAME" value="arm-none-eabi-gdb"/>
+<stringAttribute key="org.eclipse.cdt.debug.mi.core.commandFactory" value="Standard"/>
+<stringAttribute key="org.eclipse.cdt.debug.mi.core.protocol" value="mi"/>
+<booleanAttribute key="org.eclipse.cdt.debug.mi.core.verboseMode" value="false"/>
+<stringAttribute key="org.eclipse.cdt.dsf.gdb.DEBUG_NAME" value="arm-none-eabi-gdb"/>
+<intAttribute key="org.eclipse.cdt.launch.ATTR_BUILD_BEFORE_LAUNCH_ATTR" value="2"/>
+<stringAttribute key="org.eclipse.cdt.launch.COREFILE_PATH" value=""/>
+<stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_REGISTER_GROUPS" value=""/>
+<stringAttribute key="org.eclipse.cdt.launch.FORMAT" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&lt;contentList/&gt;"/>
+<stringAttribute key="org.eclipse.cdt.launch.GLOBAL_VARIABLES" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;globalVariableList/&gt;&#13;&#10;"/>
+<stringAttribute key="org.eclipse.cdt.launch.MEMORY_BLOCKS" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;memoryBlockExpressionList/&gt;&#13;&#10;"/>
+<stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="./build/ch.elf"/>
+<stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="STM32F30x-PWM-ICU"/>
+<booleanAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_AUTO_ATTR" value="true"/>
+<stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value="0.1093754934"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/STM32F30x-PWM-ICU"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+</listAttribute>
+</launchConfiguration>
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 @@
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+ <storageModule moduleId="refreshScope"/>
</cproject>
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);
/*