diff --git a/os/hal/include/pwm.h b/os/hal/include/pwm.h
index c8095d2b7..2861cbcea 100644
--- a/os/hal/include/pwm.h
+++ b/os/hal/include/pwm.h
@@ -57,34 +57,50 @@
* @brief Driver state machine possible states.
*/
typedef enum {
- PWM_UNINIT = 0, /**< @brief Not initialized. */
- PWM_STOP = 1, /**< @brief Stopped. */
- PWM_READY = 2, /**< @brief Ready. */
+ PWM_UNINIT = 0, /**< Not initialized. */
+ PWM_STOP = 1, /**< Stopped. */
+ PWM_READY = 2, /**< Ready. */
} pwmstate_t;
/**
* @brief PWM logic mode.
*/
typedef enum {
- PWM_OUTPUT_DISABLED = 0, /**< @brief Output not driven, callback
- only. */
- PWM_OUTPUT_ACTIVE_HIGH = 1, /**< @brief Idle is logic level 0. */
- PWM_OUTPUT_ACTIVE_LOW = 2 /**< @brief Idle is logic level 1. */
+ PWM_OUTPUT_DISABLED = 0, /**< Output not driven, callback only. */
+ PWM_OUTPUT_ACTIVE_HIGH = 1, /**< Idle is logic level 0. */
+ PWM_OUTPUT_ACTIVE_LOW = 2 /**< Idle is logic level 1. */
} pwmmode_t;
-/**
- * @brief PWM notification callback type.
- *
- * @param[in] active current channel output state
- */
-typedef void (*pwmcallback_t)(void);
-
#include "pwm_lld.h"
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
+/**
+ * @brief Enables a PWM channel.
+ * @details Programs (or reprograms) a PWM channel.
+ * @note This function has to be invoked from a lock zone.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] channel PWM channel identifier
+ * @param[in] width PWM pulse width as clock pulses number
+ */
+#define pwmEnableChannelI(pwmp, channel, width) \
+ pwm_lld_enable_channel(pwmp, channel, width)
+
+/**
+ * @brief Disables a PWM channel.
+ * @details The channel is disabled and its output line returned to the
+ * idle state.
+ * @note This function has to be invoked from a lock zone.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] channel PWM channel identifier
+ */
+#define pwmDisableChannelI(pwmp, channel) \
+ pwm_lld_disable_channel(pwmp, channel)
+
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
diff --git a/os/hal/platforms/STM32/pwm_lld.c b/os/hal/platforms/STM32/pwm_lld.c
index 472b1c15e..fbe986336 100644
--- a/os/hal/platforms/STM32/pwm_lld.c
+++ b/os/hal/platforms/STM32/pwm_lld.c
@@ -25,8 +25,9 @@
*/
/**
- * @file STM32/pwm_lld.c
- * @brief STM32 PWM subsystem low level driver header.
+ * @file STM32/pwm_lld.c
+ * @brief STM32 PWM subsystem low level driver header.
+ *
* @addtogroup STM32_PWM
* @{
*/
@@ -41,32 +42,32 @@
/*===========================================================================*/
/**
- * @brief PWM1 driver identifier.
- * @note The driver PWM1 allocates the complex timer TIM1 when enabled.
+ * @brief PWM1 driver identifier.
+ * @note The driver PWM1 allocates the complex timer TIM1 when enabled.
*/
#if defined(USE_STM32_PWM1) || defined(__DOXYGEN__)
PWMDriver PWMD1;
#endif
/**
- * @brief PWM2 driver identifier.
- * @note The driver PWM2 allocates the timer TIM2 when enabled.
+ * @brief PWM2 driver identifier.
+ * @note The driver PWM2 allocates the timer TIM2 when enabled.
*/
#if defined(USE_STM32_PWM2) || defined(__DOXYGEN__)
PWMDriver PWMD2;
#endif
/**
- * @brief PWM3 driver identifier.
- * @note The driver PWM3 allocates the timer TIM3 when enabled.
+ * @brief PWM3 driver identifier.
+ * @note The driver PWM3 allocates the timer TIM3 when enabled.
*/
#if defined(USE_STM32_PWM3) || defined(__DOXYGEN__)
PWMDriver PWMD3;
#endif
/**
- * @brief PWM4 driver identifier.
- * @note The driver PWM4 allocates the timer TIM4 when enabled.
+ * @brief PWM4 driver identifier.
+ * @note The driver PWM4 allocates the timer TIM4 when enabled.
*/
#if defined(USE_STM32_PWM4) || defined(__DOXYGEN__)
PWMDriver PWMD4;
@@ -80,29 +81,12 @@ PWMDriver PWMD4;
/* Driver local functions. */
/*===========================================================================*/
-/**
- * @brief Stops all channels.
- *
- * @param[in] pwmp pointer to a @p PWMDriver object
- */
-static void stop_channels(PWMDriver *pwmp) {
-
- pwmp->pd_enabled_channels = 0; /* All channels disabled. */
- pwmp->pd_tim->CCER = 0; /* Outputs disabled. */
- pwmp->pd_tim->CCR1 = 0; /* Comparator 1 disabled. */
- pwmp->pd_tim->CCR2 = 0; /* Comparator 2 disabled. */
- pwmp->pd_tim->CCR3 = 0; /* Comparator 3 disabled. */
- pwmp->pd_tim->CCR4 = 0; /* Comparator 4 disabled. */
- pwmp->pd_tim->CCMR1 = 0; /* Channels 1 and 2 frozen. */
- pwmp->pd_tim->CCMR2 = 0; /* Channels 3 and 4 frozen. */
-}
-
#if USE_STM32_PWM2 || USE_STM32_PWM3 || USE_STM32_PWM4 || defined(__DOXYGEN__)
/**
- * @brief Common TIM2...TIM4 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.
+ * @brief Common TIM2...TIM4 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.
*/
static void serve_interrupt(PWMDriver *pwmp) {
uint16_t sr;
@@ -111,15 +95,15 @@ static void serve_interrupt(PWMDriver *pwmp) {
pwmp->pd_tim->SR = ~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF |
TIM_SR_CC4IF | TIM_SR_UIF);
if ((sr & TIM_SR_CC1IF) != 0)
- pwmp->pd_config->pc_channels[0].pcc_callback();
+ pwmp->pd_config->pc_channels[0].pcc_callback(pwmp);
if ((sr & TIM_SR_CC2IF) != 0)
- pwmp->pd_config->pc_channels[1].pcc_callback();
+ pwmp->pd_config->pc_channels[1].pcc_callback(pwmp);
if ((sr & TIM_SR_CC3IF) != 0)
- pwmp->pd_config->pc_channels[2].pcc_callback();
+ pwmp->pd_config->pc_channels[2].pcc_callback(pwmp);
if ((sr & TIM_SR_CC4IF) != 0)
- pwmp->pd_config->pc_channels[3].pcc_callback();
+ pwmp->pd_config->pc_channels[3].pcc_callback(pwmp);
if ((sr & TIM_SR_UIF) != 0)
- pwmp->pd_config->pc_callback();
+ pwmp->pd_config->pc_callback(pwmp);
}
#endif /* USE_STM32_PWM2 || USE_STM32_PWM3 || USE_STM32_PWM4 */
@@ -129,26 +113,26 @@ static void serve_interrupt(PWMDriver *pwmp) {
#if USE_STM32_PWM1
/**
- * @brief TIM1 update interrupt handler.
- * @note It is assumed that this interrupt is only activated if the callback
- * pointer is not equal to @p NULL in order to not perform an extra
- * check in a potentially critical interrupt handler.
+ * @brief TIM1 update interrupt handler.
+ * @note It is assumed that this interrupt is only activated if the callback
+ * pointer is not equal to @p NULL in order to not perform an extra
+ * check in a potentially critical interrupt handler.
*/
CH_IRQ_HANDLER(VectorA4) {
CH_IRQ_PROLOGUE();
TIM1->SR = ~TIM_SR_UIF;
- PWMD1.pd_config->pc_callback();
+ PWMD1.pd_config->pc_callback(&PWMD1);
CH_IRQ_EPILOGUE();
}
/**
- * @brief TIM1 compare interrupt 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.
+ * @brief TIM1 compare interrupt 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.
*/
CH_IRQ_HANDLER(VectorAC) {
uint16_t sr;
@@ -158,13 +142,13 @@ CH_IRQ_HANDLER(VectorAC) {
sr = TIM1->SR & TIM1->DIER;
TIM1->SR = ~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF);
if ((sr & TIM_SR_CC1IF) != 0)
- PWMD1.pd_config->pc_channels[0].pcc_callback();
+ PWMD1.pd_config->pc_channels[0].pcc_callback(&PWMD1);
if ((sr & TIM_SR_CC2IF) != 0)
- PWMD1.pd_config->pc_channels[1].pcc_callback();
+ PWMD1.pd_config->pc_channels[1].pcc_callback(&PWMD1);
if ((sr & TIM_SR_CC3IF) != 0)
- PWMD1.pd_config->pc_channels[2].pcc_callback();
+ PWMD1.pd_config->pc_channels[2].pcc_callback(&PWMD1);
if ((sr & TIM_SR_CC4IF) != 0)
- PWMD1.pd_config->pc_channels[3].pcc_callback();
+ PWMD1.pd_config->pc_channels[3].pcc_callback(&PWMD1);
CH_IRQ_EPILOGUE();
}
@@ -172,7 +156,7 @@ CH_IRQ_HANDLER(VectorAC) {
#if USE_STM32_PWM2
/**
- * @brief TIM2 interrupt handler.
+ * @brief TIM2 interrupt handler.
*/
CH_IRQ_HANDLER(VectorB0) {
@@ -186,7 +170,7 @@ CH_IRQ_HANDLER(VectorB0) {
#if USE_STM32_PWM3
/**
- * @brief TIM3 interrupt handler.
+ * @brief TIM3 interrupt handler.
*/
CH_IRQ_HANDLER(VectorB4) {
@@ -200,7 +184,7 @@ CH_IRQ_HANDLER(VectorB4) {
#if USE_STM32_PWM4
/**
- * @brief TIM4 interrupt handler.
+ * @brief TIM4 interrupt handler.
*/
CH_IRQ_HANDLER(VectorB8) {
@@ -217,7 +201,7 @@ CH_IRQ_HANDLER(VectorB8) {
/*===========================================================================*/
/**
- * @brief Low level PWM driver initialization.
+ * @brief Low level PWM driver initialization.
*/
void pwm_lld_init(void) {
@@ -267,55 +251,73 @@ void pwm_lld_init(void) {
}
/**
- * @brief Configures and activates the PWM peripheral.
+ * @brief Configures and activates the PWM peripheral.
*
- * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] pwmp pointer to a @p PWMDriver object
*/
void pwm_lld_start(PWMDriver *pwmp) {
uint16_t ccer;
+ /* Reset channels.*/
+ pwmp->pd_enabled_channels = 0; /* All channels disabled. */
+
if (pwmp->pd_state == PWM_STOP) {
- /* Clock activation.*/
+ /* Clock activation and timer reset.*/
#if USE_STM32_PWM1
if (&PWMD1 == pwmp) {
- NVICEnableVector(TIM1_UP_IRQn,
- CORTEX_PRIORITY_MASK(STM32_PWM1_IRQ_PRIORITY));
- NVICEnableVector(TIM1_CC_IRQn,
- CORTEX_PRIORITY_MASK(STM32_PWM1_IRQ_PRIORITY));
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
+ RCC->APB2RSTR = RCC_APB2RSTR_TIM1RST;
+ RCC->APB2RSTR = 0;
+ NVICEnableVector(TIM1_UP_IRQn,
+ CORTEX_PRIORITY_MASK(STM32_PWM_TIM1_IRQ_PRIORITY));
+ NVICEnableVector(TIM1_CC_IRQn,
+ CORTEX_PRIORITY_MASK(STM32_PWM_TIM1_IRQ_PRIORITY));
}
#endif
#if USE_STM32_PWM2
if (&PWMD2 == pwmp) {
- NVICEnableVector(TIM2_IRQn,
- CORTEX_PRIORITY_MASK(STM32_PWM2_IRQ_PRIORITY));
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
+ RCC->APB1RSTR = RCC_APB1RSTR_TIM2RST;
+ RCC->APB1RSTR = 0;
+ NVICEnableVector(TIM2_IRQn,
+ CORTEX_PRIORITY_MASK(STM32_PWM_TIM2_IRQ_PRIORITY));
}
#endif
#if USE_STM32_PWM3
if (&PWMD3 == pwmp) {
- NVICEnableVector(TIM3_IRQn,
- CORTEX_PRIORITY_MASK(STM32_PWM3_IRQ_PRIORITY));
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
+ RCC->APB1RSTR = RCC_APB1RSTR_TIM3RST;
+ RCC->APB1RSTR = 0;
+ NVICEnableVector(TIM3_IRQn,
+ CORTEX_PRIORITY_MASK(STM32_PWM_TIM3_IRQ_PRIORITY));
}
#endif
#if USE_STM32_PWM4
if (&PWMD4 == pwmp) {
- NVICEnableVector(TIM4_IRQn,
- CORTEX_PRIORITY_MASK(STM32_PWM4_IRQ_PRIORITY));
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
+ RCC->APB1RSTR = RCC_APB1RSTR_TIM4RST;
+ RCC->APB1RSTR = 0;
+ NVICEnableVector(TIM4_IRQn,
+ CORTEX_PRIORITY_MASK(STM32_PWM_TIM4_IRQ_PRIORITY));
}
#endif
}
- /* Reset channels.*/
- stop_channels(pwmp);
+ else {
+ /* Driver re-configuration scenario, it must be stopped first.*/
+ /* Really required ?????????? */
+ pwmp->pd_tim->CR1 = 0; /* Timer stopped. */
+ pwmp->pd_tim->CR2 = 0; /* Timer stopped. */
+ pwmp->pd_tim->SMCR = 0; /* Slave mode disabled. */
+ pwmp->pd_tim->CCR1 = 0; /* Comparator 1 disabled. */
+ pwmp->pd_tim->CCR2 = 0; /* Comparator 2 disabled. */
+ pwmp->pd_tim->CCR3 = 0; /* Comparator 3 disabled. */
+ pwmp->pd_tim->CCR4 = 0; /* Comparator 4 disabled. */
+ pwmp->pd_tim->CNT = 0;
+ }
- /* Configuration or reconfiguration.*/
- pwmp->pd_tim->CR1 = 0; /* Timer stopped. */
- pwmp->pd_tim->SMCR = 0; /* Slave mode disabled. */
+ /* Timer configuration.*/
pwmp->pd_tim->CR2 = pwmp->pd_config->pc_cr2;
pwmp->pd_tim->PSC = pwmp->pd_config->pc_psc;
- pwmp->pd_tim->CNT = 0;
pwmp->pd_tim->ARR = pwmp->pd_config->pc_arr;
/* Output enables and polarities setup.*/
ccer = 0;
@@ -356,22 +358,30 @@ void pwm_lld_start(PWMDriver *pwmp) {
pwmp->pd_tim->SR = 0; /* Clear pending IRQs. */
pwmp->pd_tim->DIER = pwmp->pd_config->pc_callback == NULL ? 0 : TIM_DIER_UIE;
pwmp->pd_tim->BDTR = TIM_BDTR_MOE;
- pwmp->pd_tim->CR1 = TIM_CR1_ARPE | TIM_CR1_URS |
- TIM_CR1_CEN; /* Timer configured and started.*/
+ /* Timer configured and started.*/
+ pwmp->pd_tim->CR1 = TIM_CR1_ARPE | TIM_CR1_URS | TIM_CR1_CEN;
}
/**
- * @brief Deactivates the PWM peripheral.
+ * @brief Deactivates the PWM peripheral.
*
- * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] pwmp pointer to a @p PWMDriver object
*/
void pwm_lld_stop(PWMDriver *pwmp) {
/* If in ready state then disables the PWM clock.*/
if (pwmp->pd_state == PWM_READY) {
- stop_channels(pwmp);
- pwmp->pd_tim->CR1 = 0;
- pwmp->pd_tim->BDTR = 0;
- pwmp->pd_tim->DIER = 0;
+ pwmp->pd_enabled_channels = 0; /* All channels disabled. */
+ pwmp->pd_tim->CR1 = 0;
+ pwmp->pd_tim->CR2 = 0;
+ pwmp->pd_tim->CCER = 0; /* Outputs disabled. */
+ pwmp->pd_tim->CCR1 = 0; /* Comparator 1 disabled. */
+ pwmp->pd_tim->CCR2 = 0; /* Comparator 2 disabled. */
+ pwmp->pd_tim->CCR3 = 0; /* Comparator 3 disabled. */
+ pwmp->pd_tim->CCR4 = 0; /* Comparator 4 disabled. */
+ pwmp->pd_tim->BDTR = 0;
+ pwmp->pd_tim->DIER = 0;
+ pwmp->pd_tim->SR = 0;
+ pwmp->pd_tim->EGR = TIM_EGR_UG; /* Update event. */
#if USE_STM32_PWM1
if (&PWMD1 == pwmp) {
@@ -402,112 +412,42 @@ void pwm_lld_stop(PWMDriver *pwmp) {
}
/**
- * @brief Enables a PWM channel.
+ * @brief Enables a PWM channel.
*
* @param[in] pwmp pointer to a @p PWMDriver object
- * @param[in] channel PWM channel identifier
+ * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
* @param[in] width PWM pulse width as clock pulses number
*/
void pwm_lld_enable_channel(PWMDriver *pwmp,
pwmchannel_t channel,
pwmcnt_t width) {
- /*
- * Changes the pulse width.
- */
- switch (channel) {
- case 0:
- pwmp->pd_tim->CCR1 = width;
- break;
- case 1:
- pwmp->pd_tim->CCR2 = width;
- break;
- case 2:
- pwmp->pd_tim->CCR3 = width;
- break;
- case 3:
- pwmp->pd_tim->CCR4 = width;
- break;
- }
+ *(&pwmp->pd_tim->CCR1 + (channel * 2)) = width; /* New duty cycle. */
if ((pwmp->pd_enabled_channels & (1 << channel)) == 0) {
- /*
- * The channel is not enabled yet.
- */
+ /* The channel is not enabled yet.*/
pwmp->pd_enabled_channels |= (1 << channel);
- /*
- * Setup the comparator, the channel is configured as PWM mode 1 with
- * preload enabled.
- */
- switch (channel) {
- case 0:
- pwmp->pd_tim->CCMR1 = (pwmp->pd_tim->CCMR1 & 0xFF00) |
- TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 |
- TIM_CCMR1_OC1PE;
- pwmp->pd_tim->SR = ~TIM_SR_CC1IF;
- pwmp->pd_tim->DIER |= pwmp->pd_config->pc_channels[0].pcc_callback == NULL
- ? 0 : TIM_DIER_CC1IE;
- break;
- case 1:
- pwmp->pd_tim->CCMR1 = (pwmp->pd_tim->CCMR1 & 0x00FF) |
- TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 |
- TIM_CCMR1_OC2PE;
- pwmp->pd_tim->SR = ~TIM_SR_CC2IF;
- pwmp->pd_tim->DIER |= pwmp->pd_config->pc_channels[1].pcc_callback == NULL
- ? 0 : TIM_DIER_CC2IE;
- break;
- case 2:
- pwmp->pd_tim->CCMR2 = (pwmp->pd_tim->CCMR2 & 0xFF00) |
- TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 |
- TIM_CCMR2_OC3PE;
- pwmp->pd_tim->SR = ~TIM_SR_CC3IF;
- pwmp->pd_tim->DIER |= pwmp->pd_config->pc_channels[2].pcc_callback == NULL
- ? 0 : TIM_DIER_CC3IE;
- break;
- case 3:
- pwmp->pd_tim->CCMR2 = (pwmp->pd_tim->CCMR2 & 0x00FF) |
- TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 |
- TIM_CCMR2_OC4PE;
- pwmp->pd_tim->SR = ~TIM_SR_CC4IF;
- pwmp->pd_tim->DIER |= pwmp->pd_config->pc_channels[3].pcc_callback == NULL
- ? 0 : TIM_DIER_CC4IE;
- break;
+ /* If there is a callback associated to the channel then the proper
+ interrupt is cleared and enabled.*/
+ if (pwmp->pd_config->pc_channels[channel].pcc_callback) {
+ pwmp->pd_tim->SR = ~(2 << channel);
+ pwmp->pd_tim->DIER |= (2 << channel);
}
}
}
/**
- * @brief Disables a PWM channel.
+ * @brief Disables a PWM channel.
* @details The channel is disabled and its output line returned to the
* idle state.
*
* @param[in] pwmp pointer to a @p PWMDriver object
- * @param[in] channel PWM channel identifier
+ * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
*/
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) {
+ *(&pwmp->pd_tim->CCR1 + (channel * 2)) = 0;
+ pwmp->pd_tim->DIER = ~(2 << channel);
pwmp->pd_enabled_channels &= ~(1 << channel);
- switch (channel) {
- case 0:
- pwmp->pd_tim->CCR1 = 0;
- pwmp->pd_tim->CCMR1 = pwmp->pd_tim->CCMR1 & 0xFF00;
- pwmp->pd_tim->DIER &= ~TIM_DIER_CC1IE;
- break;
- case 1:
- pwmp->pd_tim->CCR2 = 0;
- pwmp->pd_tim->CCMR1 = pwmp->pd_tim->CCMR1 & 0x00FF;
- pwmp->pd_tim->DIER &= ~TIM_DIER_CC2IE;
- break;
- case 2:
- pwmp->pd_tim->CCR3 = 0;
- pwmp->pd_tim->CCMR2 = pwmp->pd_tim->CCMR2 & 0xFF00;
- pwmp->pd_tim->DIER &= ~TIM_DIER_CC3IE;
- break;
- case 3:
- pwmp->pd_tim->CCR4 = 0;
- pwmp->pd_tim->CCMR2 = pwmp->pd_tim->CCMR2 & 0x00FF;
- pwmp->pd_tim->DIER &= ~TIM_DIER_CC4IE;
- break;
- }
}
#endif /* CH_HAL_USE_PWM */
diff --git a/os/hal/platforms/STM32/pwm_lld.h b/os/hal/platforms/STM32/pwm_lld.h
index dfbb92204..b716d77c2 100644
--- a/os/hal/platforms/STM32/pwm_lld.h
+++ b/os/hal/platforms/STM32/pwm_lld.h
@@ -10,115 +10,147 @@
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
- ---
-
- A special exception to the GPL can be applied should you wish to distribute
- a combined work that includes ChibiOS/RT, without being obliged to provide
- the source code for any proprietary components. See the file exception.txt
- for full details of how and when the exception can be applied.
+ along with this program. If not, see .
*/
/**
- * @file STM32/pwm_lld.h
- * @brief STM32 PWM subsystem low level driver header.
- * @addtogroup STM32_PWM
+ * @file STM32/pwm_lld.h
+ * @brief STM32 PWM subsystem low level driver header.
+ *
+ * @addtogroup PWM
* @{
*/
#ifndef _PWM_LLD_H_
#define _PWM_LLD_H_
-#if CH_HAL_USE_PWM || defined(__DOXYGEN__)
+#if HAL_USE_PWM || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
- * @brief Number of PWM channels per PWM driver.
+ * @brief Number of PWM channels per PWM driver.
*/
-#define PWM_CHANNELS 4
+#define PWM_CHANNELS 4
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
- * @brief PWM1 driver enable switch.
+ * @brief PWM1 driver enable switch.
* @details If set to @p TRUE the support for PWM1 is included.
- * @note The default is @p TRUE.
+ * @note The default is @p TRUE.
*/
-#if !defined(USE_STM32_PWM1) || defined(__DOXYGEN__)
-#define USE_STM32_PWM1 TRUE
+#if !defined(STM32_PWM_USE_TIM1) || defined(__DOXYGEN__)
+#define STM32_PWM_USE_TIM1 TRUE
#endif
/**
- * @brief PWM2 driver enable switch.
+ * @brief PWM2 driver enable switch.
* @details If set to @p TRUE the support for PWM2 is included.
- * @note The default is @p TRUE.
+ * @note The default is @p TRUE.
*/
-#if !defined(USE_STM32_PWM2) || defined(__DOXYGEN__)
-#define USE_STM32_PWM2 TRUE
+#if !defined(STM32_PWM_USE_TIM2) || defined(__DOXYGEN__)
+#define STM32_PWM_USE_TIM2 TRUE
#endif
/**
- * @brief PWM3 driver enable switch.
+ * @brief PWM3 driver enable switch.
* @details If set to @p TRUE the support for PWM3 is included.
- * @note The default is @p TRUE.
+ * @note The default is @p TRUE.
*/
-#if !defined(USE_STM32_PWM3) || defined(__DOXYGEN__)
-#define USE_STM32_PWM3 TRUE
+#if !defined(STM32_PWM_USE_TIM3) || defined(__DOXYGEN__)
+#define STM32_PWM_USE_TIM3 TRUE
#endif
/**
- * @brief PWM4 driver enable switch.
+ * @brief PWM4 driver enable switch.
* @details If set to @p TRUE the support for PWM4 is included.
- * @note The default is @p TRUE.
+ * @note The default is @p TRUE.
*/
-#if !defined(USE_STM32_PWM4) || defined(__DOXYGEN__)
-#define USE_STM32_PWM4 TRUE
+#if !defined(STM32_PWM_USE_TIM4) || defined(__DOXYGEN__)
+#define STM32_PWM_USE_TIM4 TRUE
#endif
/**
- * @brief PWM1 interrupt priority level setting.
+ * @brief PWM5 driver enable switch.
+ * @details If set to @p TRUE the support for PWM5 is included.
+ * @note The default is @p TRUE.
*/
-#if !defined(STM32_PWM1_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_PWM1_IRQ_PRIORITY 7
+#if !defined(STM32_PWM_USE_TIM5) || defined(__DOXYGEN__)
+#define STM32_PWM_USE_TIM5 TRUE
#endif
/**
- * @brief PWM2 interrupt priority level setting.
+ * @brief PWM1 interrupt priority level setting.
*/
-#if !defined(STM32_PWM2_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_PWM2_IRQ_PRIORITY 7
+#if !defined(STM32_PWM_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_PWM_TIM1_IRQ_PRIORITY 7
#endif
/**
- * @brief PWM3 interrupt priority level setting.
+ * @brief PWM2 interrupt priority level setting.
*/
-#if !defined(STM32_PWM3_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_PWM3_IRQ_PRIORITY 7
+#if !defined(STM32_PWM_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_PWM_TIM2_IRQ_PRIORITY 7
#endif
/**
- * @brief PWM4 interrupt priority level setting.
+ * @brief PWM3 interrupt priority level setting.
*/
-#if !defined(STM32_PWM4_IRQ_PRIORITY) || defined(__DOXYGEN__)
-#define STM32_PWM4_IRQ_PRIORITY 7
+#if !defined(STM32_PWM_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_PWM_TIM3_IRQ_PRIORITY 7
+#endif
+
+/**
+ * @brief PWM4 interrupt priority level setting.
+ */
+#if !defined(STM32_PWM_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_PWM_TIM4_IRQ_PRIORITY 7
+#endif
+
+/**
+ * @brief PWM5 interrupt priority level setting.
+ */
+#if !defined(STM32_PWM_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define STM32_PWM_TIM5_IRQ_PRIORITY 7
#endif
/*===========================================================================*/
/* Configuration checks. */
/*===========================================================================*/
-#if USE_STM32_PWM4 && defined(STM32F10X_LD)
-#error "TIM4 not present in low density STM32 devices"
+#if STM32_PWM_USE_TIM1 && !STM32_HAS_TIM1
+#error "TIM1 not present in the selected device"
+#endif
+
+#if STM32_PWM_USE_TIM2 && !STM32_HAS_TIM2
+#error "TIM2 not present in the selected device"
+#endif
+
+#if STM32_PWM_USE_TIM3 && !STM32_HAS_TIM3
+#error "TIM3 not present in the selected device"
+#endif
+
+#if STM32_PWM_USE_TIM4 && !STM32_HAS_TIM4
+#error "TIM4 not present in the selected device"
+#endif
+
+#if STM32_PWM_USE_TIM5 && !STM32_HAS_TIM5
+#error "TIM5 not present in the selected device"
+#endif
+
+#if !STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM2 && \
+ !STM32_PWM_USE_TIM3 && !STM32_PWM_USE_TIM4 && \
+ !STM32_PWM_USE_TIM5
+#error "PWM driver activated but no TIM peripheral assigned"
#endif
/*===========================================================================*/
@@ -126,18 +158,29 @@
/*===========================================================================*/
/**
- * @brief PWM channel type.
+ * @brief PWM channel type.
*/
typedef uint8_t pwmchannel_t;
/**
- * @brief PWM counter type.
+ * @brief PWM counter type.
*/
typedef uint16_t pwmcnt_t;
/**
- * @brief PWM driver channel configuration structure.
- * @note It could be empty on some architectures.
+ * @brief Type of a structure representing an PWM driver.
+ */
+typedef struct PWMDriver PWMDriver;
+
+/**
+ * @brief PWM notification callback type.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ */
+typedef void (*pwmcallback_t)(PWMDriver *pwmp);
+
+/**
+ * @brief PWM driver channel configuration structure.
*/
typedef struct {
/**
@@ -146,22 +189,21 @@ typedef struct {
pwmmode_t pcc_mode;
/**
* @brief Channel callback pointer.
- * @details This callback is invoked on the channel compare event. If set to
- * @p NULL then the callback is disabled.
+ * @note This callback is invoked on the channel compare event. If set to
+ * @p NULL then the callback is disabled.
*/
pwmcallback_t pcc_callback;
/* End of the mandatory fields.*/
} PWMChannelConfig;
/**
- * @brief PWM driver configuration structure.
- * @note It could be empty on some architectures.
+ * @brief PWM driver configuration structure.
*/
typedef struct {
/**
* @brief Periodic callback pointer.
- * @details This callback is invoked on PWM counter reset. If set to
- * @p NULL then the callback is disabled.
+ * @note This callback is invoked on PWM counter reset. If set to
+ * @p NULL then the callback is disabled.
*/
pwmcallback_t pc_callback;
/**
@@ -179,15 +221,15 @@ typedef struct {
uint16_t pc_arr;
/**
* @brief TIM CR2 register initialization data.
- * @note The value of this field should normally be equal to zero.
+ * @note The value of this field should normally be equal to zero.
*/
uint16_t pc_cr2;
} PWMConfig;
/**
- * @brief Structure representing a PWM driver.
+ * @brief Structure representing a PWM driver.
*/
-typedef struct {
+struct PWMDriver {
/**
* @brief Driver state.
*/
@@ -196,6 +238,9 @@ typedef struct {
* @brief Current driver configuration data.
*/
const PWMConfig *pd_config;
+#if defined(PWM_DRIVER_EXT_FIELDS)
+ PWM_DRIVER_EXT_FIELDS
+#endif
/* End of the mandatory fields.*/
/**
* @brief Bit mask of the enabled channels.
@@ -205,32 +250,119 @@ typedef struct {
* @brief Pointer to the TIMx registers block.
*/
TIM_TypeDef *pd_tim;
-} PWMDriver;
+};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
+/**
+ * @brief PWM clock prescaler initialization utility.
+ * @note The real clock value is rounded to the lower valid value, please
+ * make sure that the source clock frequency is a multiple of the
+ * requested PWM clock frequency.
+ * @note The calculated value must fit into an unsigned 16 bits integer.
+ *
+ * @param[in] clksrc clock source frequency, depending on the target timer
+ * cell it can be one of:
+ * - STM32_TIMCLK1
+ * - STM32_TIMCLK2
+ * .
+ * Please refer to the STM32 HAL driver documentation
+ * and/or the STM32 Reference Manual for the right clock
+ * source.
+ * @param[in] pwmclk PWM clock frequency in cycles
+ * @return The value to be stored in the @p pc_psc field of the
+ * @p PWMConfig structure.
+ */
+#define PWM_COMPUTE_PSC(clksrc, pwmclk) \
+ ((uint16_t)(((clksrc) / (pwmclk)) - 1))
+
+/**
+ * @brief PWM cycle period initialization utility.
+ * @note The calculated value must fit into an unsigned 16 bits integer.
+ *
+ * @param[in] pwmclk PWM clock frequency in cycles
+ * @param[in] pwmperiod PWM cycle period in nanoseconds
+ * @return The value to be stored in the @p pc_arr field of the
+ * @p PWMConfig structure.
+ */
+#define PWM_COMPUTE_ARR(pwmclk, pwmperiod) \
+ ((uint16_t)(((pwmclk) / (1000000000 / (pwmperiod))) - 1))
+
+/**
+ * @brief Converts from fraction to pulse width.
+ * @note Be careful with rounding errors, this is integer math not magic.
+ * You can specify tenths of thousandth but make sure you have the
+ * proper hardware resolution by carefully choosing the clock source
+ * and prescaler settings, see @p PWM_COMPUTE_PSC.
+ *
+ * @param[in] numerator numerator of the fraction
+ * @param[in] denominator percentage as an integer between 0 and numerator
+ * @return The pulse width to be passed to @p pwmEnableChannel().
+ *
+ * @api
+ */
+#define PWM_FRACTION_TO_WIDTH(pwmp, numerator, denominator) \
+ ((uint16_t)((((uint32_t)(pwmp)->pd_config->pc_arr + 1UL) * \
+ (uint32_t)(denominator)) / (uint32_t)(numerator)))
+
+/**
+ * @brief Converts from degrees to pulse width.
+ * @note Be careful with rounding errors, this is integer math not magic.
+ * You can specify hundredths of degrees but make sure you have the
+ * proper hardware resolution by carefully choosing the clock source
+ * and prescaler settings, see @p PWM_COMPUTE_PSC.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] degrees degrees as an integer between 0 and 36000
+ * @return The pulse width to be passed to @p pwmEnableChannel().
+ *
+ * @api
+ */
+#define PWM_DEGREES_TO_WIDTH(pwmp, degrees) \
+ PWM_FRACTION_TO_WIDTH(pwmp, 36000, degrees)
+
+/**
+ * @brief Converts from percentage to pulse width.
+ * @note Be careful with rounding errors, this is integer math not magic.
+ * You can specify tenths of thousandth but make sure you have the
+ * proper hardware resolution by carefully choosing the clock source
+ * and prescaler settings, see @p PWM_COMPUTE_PSC.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] percentage percentage as an integer between 0 and 10000
+ * @return The pulse width to be passed to @p pwmEnableChannel().
+ *
+ * @api
+ */
+#define PWM_PERCENTAGE_TO_WIDTH(pwmp, percentage) \
+ PWM_FRACTION_TO_WIDTH(pwmp, 10000, percentage)
+
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
-#if defined(USE_STM32_PWM1) && !defined(__DOXYGEN__)
+#if defined(STM32_PWM_USE_TIM1) && !defined(__DOXYGEN__)
extern PWMDriver PWMD1;
#endif
-#if defined(USE_STM32_PWM2) && !defined(__DOXYGEN__)
+#if defined(STM32_PWM_USE_TIM2) && !defined(__DOXYGEN__)
extern PWMDriver PWMD2;
#endif
-#if defined(USE_STM32_PWM3) && !defined(__DOXYGEN__)
+#if defined(STM32_PWM_USE_TIM3) && !defined(__DOXYGEN__)
extern PWMDriver PWMD3;
#endif
-#if defined(USE_STM32_PWM4) && !defined(__DOXYGEN__)
+#if defined(STM32_PWM_USE_TIM4) && !defined(__DOXYGEN__)
extern PWMDriver PWMD4;
#endif
+#if defined(STM32_PWM_USE_TIM5) && !defined(__DOXYGEN__)
+extern PWMDriver PWMD5;
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -245,7 +377,7 @@ extern "C" {
}
#endif
-#endif /* CH_HAL_USE_PWM */
+#endif /* HAL_USE_PWM */
#endif /* _PWM_LLD_H_ */
diff --git a/os/hal/src/pwm.c b/os/hal/src/pwm.c
index 9211091fb..8973537d9 100644
--- a/os/hal/src/pwm.c
+++ b/os/hal/src/pwm.c
@@ -27,6 +27,7 @@
/**
* @file pwm.c
* @brief PWM Driver code.
+ *
* @addtogroup PWM
* @{
*/
@@ -34,7 +35,7 @@
#include "ch.h"
#include "hal.h"
-#if CH_HAL_USE_PWM || defined(__DOXYGEN__)
+#if HAL_USE_PWM || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver exported variables. */
@@ -69,6 +70,9 @@ void pwmObjectInit(PWMDriver *pwmp) {
pwmp->pd_state = PWM_STOP;
pwmp->pd_config = NULL;
+#if defined(PWM_DRIVER_EXT_INIT_HOOK)
+ PWM_DRIVER_EXT_INIT_HOOK(pwmp);
+#endif
}
/**
@@ -83,8 +87,7 @@ void pwmStart(PWMDriver *pwmp, const PWMConfig *config) {
chSysLock();
chDbgAssert((pwmp->pd_state == PWM_STOP) || (pwmp->pd_state == PWM_READY),
- "pwmStart(), #1",
- "invalid state");
+ "pwmStart(), #1", "invalid state");
pwmp->pd_config = config;
pwm_lld_start(pwmp);
pwmp->pd_state = PWM_READY;
@@ -102,8 +105,7 @@ void pwmStop(PWMDriver *pwmp) {
chSysLock();
chDbgAssert((pwmp->pd_state == PWM_STOP) || (pwmp->pd_state == PWM_READY),
- "pwmStop(), #1",
- "invalid state");
+ "pwmStop(), #1", "invalid state");
pwm_lld_stop(pwmp);
pwmp->pd_state = PWM_STOP;
chSysUnlock();
@@ -111,9 +113,10 @@ void pwmStop(PWMDriver *pwmp) {
/**
* @brief Enables a PWM channel.
+ * @details Programs (or reprograms) a PWM channel.
*
* @param[in] pwmp pointer to a @p PWMDriver object
- * @param[in] channel PWM channel identifier
+ * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
* @param[in] width PWM pulse width as clock pulses number
*/
void pwmEnableChannel(PWMDriver *pwmp,
@@ -125,18 +128,18 @@ void pwmEnableChannel(PWMDriver *pwmp,
chSysLock();
chDbgAssert(pwmp->pd_state == PWM_READY,
- "pwmEnableChannel(), #1", "invalid state");
+ "pwmEnableChannel(), #1", "not ready");
pwm_lld_enable_channel(pwmp, channel, width);
chSysUnlock();
}
/**
- * @brief Disables a PWM channel.
+ * @brief Disables a PWM channel.
* @details The channel is disabled and its output line returned to the
* idle state.
*
* @param[in] pwmp pointer to a @p PWMDriver object
- * @param[in] channel PWM channel identifier
+ * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
*/
void pwmDisableChannel(PWMDriver *pwmp, pwmchannel_t channel) {
@@ -145,7 +148,7 @@ void pwmDisableChannel(PWMDriver *pwmp, pwmchannel_t channel) {
chSysLock();
chDbgAssert(pwmp->pd_state == PWM_READY,
- "pwmDisableChannel(), #1", "invalid state");
+ "pwmDisableChannel(), #1", "not ready");
pwm_lld_disable_channel(pwmp, channel);
chSysUnlock();
}
diff --git a/os/hal/templates/pwm_lld.c b/os/hal/templates/pwm_lld.c
index 18f4110d0..a1c7b50c3 100644
--- a/os/hal/templates/pwm_lld.c
+++ b/os/hal/templates/pwm_lld.c
@@ -35,6 +35,8 @@
#include "ch.h"
#include "hal.h"
+#if CH_HAL_USE_PWM || defined(__DOXYGEN__)
+
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
@@ -98,25 +100,11 @@ bool_t pwm_lld_is_enabled(PWMDriver *pwmp, pwmchannel_t channel) {
return FALSE;
}
-/**
- * @brief Enables a callback mode for the specified PWM channel.
- * @details The callback mode must be set before starting a PWM channel.
- *
- * @param[in] pwmp pointer to the @p PWMDriver object
- * @param[in] channel PWM channel identifier
- * @param[in] edge output edge mode
- * @param[in] callback callback function
- */
-void pwm_lld_set_callback(PWMDriver *pwmp, pwmchannel_t channel,
- pwmedge_t edge, pwmcallback_t callback) {
-
-}
-
/**
* @brief Enables a PWM channel.
*
- * @param[in] pwmp pointer to the @p PWMDriver object
- * @param[in] channel PWM channel identifier
+ * @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
*/
void pwm_lld_enable_channel(PWMDriver *pwmp,
@@ -130,11 +118,13 @@ void pwm_lld_enable_channel(PWMDriver *pwmp,
* @details The channel is disabled and its output line returned to the
* idle state.
*
- * @param[in] pwmp pointer to the @p PWMDriver object
- * @param[in] channel PWM channel identifier
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
*/
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) {
}
+#endif /* CH_HAL_USE_PWM */
+
/** @} */
diff --git a/os/hal/templates/pwm_lld.h b/os/hal/templates/pwm_lld.h
index 8db3681fb..8605c3d8c 100644
--- a/os/hal/templates/pwm_lld.h
+++ b/os/hal/templates/pwm_lld.h
@@ -35,6 +35,8 @@
#ifndef _PWM_LLD_H_
#define _PWM_LLD_H_
+#if CH_HAL_USE_PWM || defined(__DOXYGEN__)
+
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
@@ -69,17 +71,61 @@ typedef uint8_t pwmchannel_t;
typedef uint16_t pwmcnt_t;
/**
- * @brief Driver configuration structure.
- * @note It could be empty on some architectures.
+ * @brief Type of a structure representing an PWM driver.
+ */
+typedef struct PWMDriver PWMDriver;
+
+/**
+ * @brief PWM notification callback type.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ */
+typedef void (*pwmcallback_t)(PWMDriver *pwmp);
+
+/**
+ * @brief PWM driver channel configuration structure.
+ * @note Some architectures may not be able to support the channel mode
+ * or the callback, in this case the fields are ignored.
*/
typedef struct {
+ /**
+ * @brief Channel active logic level.
+ */
+ pwmmode_t pcc_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 pcc_callback;
+ /* End of the mandatory fields.*/
+} PWMChannelConfig;
+/**
+ * @brief Driver configuration structure.
+ * @note Implementations may extend this structure to contain more,
+ * architecture dependent, fields.
+ */
+typedef struct {
+ /**
+ * @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 pc_callback;
+ /**
+ * @brief Channels configurations.
+ */
+ PWMChannelConfig pc_channels[PWM_CHANNELS];
+ /* End of the mandatory fields.*/
} PWMConfig;
/**
* @brief Structure representing an PWM driver.
+ * @note Implementations may extend this structure to contain more,
+ * architecture dependent, fields.
*/
-typedef struct {
+struct PWMDriver {
/**
* @brief Driver state.
*/
@@ -88,13 +134,63 @@ typedef struct {
* @brief Current configuration data.
*/
const PWMConfig *pd_config;
+#if defined(PWM_DRIVER_EXT_FIELDS)
+ PWM_DRIVER_EXT_FIELDS
+#endif
/* End of the mandatory fields.*/
-} PWMDriver;
+};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
+/**
+ * @brief Converts from fraction to pulse width.
+ * @note Be careful with rounding errors, this is integer math not magic.
+ * You can specify tenths of thousandth but make sure you have the
+ * proper hardware resolution by carefully choosing the clock source
+ * and prescaler settings, see @p PWM_COMPUTE_PSC.
+ *
+ * @param[in] numerator numerator of the fraction
+ * @param[in] denominator percentage as an integer between 0 and numerator
+ * @return The pulse width to be passed to @p pwmEnableChannel().
+ *
+ * @api
+ */
+#define PWM_FRACTION_TO_WIDTH(pwmp, numerator, denominator) 0
+
+/**
+ * @brief Converts from degrees to pulse width.
+ * @note Be careful with rounding errors, this is integer math not magic.
+ * You can specify hundredths of degrees but make sure you have the
+ * proper hardware resolution by carefully choosing the clock source
+ * and prescaler settings, see @p PWM_COMPUTE_PSC.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] degrees degrees as an integer between 0 and 36000
+ * @return The pulse width to be passed to @p pwmEnableChannel().
+ *
+ * @api
+ */
+#define PWM_DEGREES_TO_WIDTH(pwmp, degrees) \
+ PWM_FRACTION_TO_WIDTH(pwmp, 36000, degrees)
+
+/**
+ * @brief Converts from percentage to pulse width.
+ * @note Be careful with rounding errors, this is integer math not magic.
+ * You can specify tenths of thousandth but make sure you have the
+ * proper hardware resolution by carefully choosing the clock source
+ * and prescaler settings, see @p PWM_COMPUTE_PSC.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] percentage percentage as an integer between 0 and 10000
+ * @return The pulse width to be passed to @p pwmEnableChannel().
+ *
+ * @api
+ */
+#define PWM_PERCENTAGE_TO_WIDTH(pwmp, percentage) \
+ PWM_FRACTION_TO_WIDTH(pwmp, 10000, percentage)
+
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
@@ -106,8 +202,6 @@ extern "C" {
void pwm_lld_start(PWMDriver *pwmp);
void pwm_lld_stop(PWMDriver *pwmp);
bool_t pwm_lld_is_enabled(PWMDriver *pwmp, pwmchannel_t channel);
- void pwm_lld_set_callback(PWMDriver *pwmp, pwmchannel_t channel,
- pwmedge_t edge, pwmcallback_t callback);
void pwm_lld_enable_channel(PWMDriver *pwmp,
pwmchannel_t channel,
pwmcnt_t width);
@@ -116,6 +210,8 @@ extern "C" {
}
#endif
+#endif /* CH_HAL_USE_PWM */
+
#endif /* _PWM_LLD_H_ */
/** @} */
diff --git a/readme.txt b/readme.txt
index cd87e80c5..c5dbd8eca 100644
--- a/readme.txt
+++ b/readme.txt
@@ -61,6 +61,12 @@
*** 2.0.8 ***
- FIX: Fixed failed memory recovery by registry scan, improved the related
test case (bug 3116888).
+- FIX: Fixed PWM channels going to ACTIVE state when the pulse width is
+ set to zero in the STM32 PWM driver (bug 3114481).
+- FIX: Fixed PWM channels return to IDLE state in STM32 PWM driver (bug
+ 3114467).
+- CHANGE: Bugs 3114467 and 3114481 have been fixed by backporting the 2.1.x
+ PWM driver, there is a difference in the PWM callback parameters.
*** 2.0.7 ***
- FIX: Fixed typo in board name (bug 3113574).