Fixed bugs 3114467 and 3114481.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/stable_2.0.x@2442 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
gdisirio 2010-11-28 09:17:10 +00:00
parent fc2a392477
commit cdada0e185
7 changed files with 458 additions and 275 deletions

View File

@ -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. */
/*===========================================================================*/

View File

@ -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 */

View File

@ -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 <http://www.gnu.org/licenses/>.
---
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 <http://www.gnu.org/licenses/>.
*/
/**
* @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_ */

View File

@ -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();
}

View File

@ -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 */
/** @} */

View File

@ -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_ */
/** @} */

View File

@ -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).