From 09dc9d31eae8cba06ee66facc34198a5f90d1ef4 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Sat, 16 Jul 2022 18:05:07 +0200 Subject: [PATCH] Add RP2040 PWM driver All individual RP2040 PWM channels are mapped onto distinct ChibiOS PWM drivers, as this fits driver model which wants an independent timer per driver. --- os/hal/ports/RP/LLD/PWMv1/driver.mk | 9 + os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.c | 464 ++++++++++++++++++ os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.h | 324 ++++++++++++ os/hal/ports/RP/RP2040/platform.mk | 1 + .../RP2040/RT-RP2040-PICO-ADC/cfg/mcuconf.h | 13 + .../RP2040/RT-RP2040-PICO-HID/cfg/mcuconf.h | 13 + .../RT-RP2040-PICO-I2C-24AA01/cfg/mcuconf.h | 13 + .../RT-RP2040-PICO-SERIAL/cfg/mcuconf.h | 13 + 8 files changed, 850 insertions(+) create mode 100644 os/hal/ports/RP/LLD/PWMv1/driver.mk create mode 100644 os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.c create mode 100644 os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.h diff --git a/os/hal/ports/RP/LLD/PWMv1/driver.mk b/os/hal/ports/RP/LLD/PWMv1/driver.mk new file mode 100644 index 00000000..0df8ba74 --- /dev/null +++ b/os/hal/ports/RP/LLD/PWMv1/driver.mk @@ -0,0 +1,9 @@ +ifeq ($(USE_SMART_BUILD),yes) +ifneq ($(findstring HAL_USE_PWM TRUE,$(HALCONF)),) +PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.c +endif +else +PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.c +endif + +PLATFORMINC += $(CHIBIOS_CONTRIB)/os/hal/ports/RP/LLD/PWMv1 diff --git a/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.c b/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.c new file mode 100644 index 00000000..c3785de0 --- /dev/null +++ b/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.c @@ -0,0 +1,464 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_pwm_lld.c + * @brief PLATFORM PWM subsystem low level driver source. + * + * @addtogroup PWM + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_PWM == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief PWMD0 driver identifier. + * @note The driver PWMD0 allocates the PWM channel 0 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM0 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD0; +#endif + +/** + * @brief PWMD1 driver identifier. + * @note The driver PWMD1 allocates the PWM channel 1 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM1 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD1; +#endif + +/** + * @brief PWMD2 driver identifier. + * @note The driver PWMD2 allocates the PWM channel 2 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM2 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD2; +#endif + +/** + * @brief PWMD3 driver identifier. + * @note The driver PWMD0 allocates the PWM channel 3 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM3 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD3; +#endif + +/** + * @brief PWMD4 driver identifier. + * @note The driver PWMD4 allocates the PWM channel 4 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM4 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD4; +#endif + +/** + * @brief PWMD5 driver identifier. + * @note The driver PWMD5 allocates the PWM channel 5 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM5 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD5; +#endif + +/** + * @brief PWMD6 driver identifier. + * @note The driver PWMD6 allocates the PWM channel 6 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM6 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD6; +#endif + +/** + * @brief PWMD7 driver identifier. + * @note The driver PWMD7 allocates the PWM channel 7 peripheral when enabled. + */ +#if (RP_PWM_USE_PWM7 == TRUE) || defined(__DOXYGEN__) +PWMDriver PWMD7; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if (RP_PWM_USE_PWM0 == TRUE) || (RP_PWM_USE_PWM1 == TRUE) || \ + (RP_PWM_USE_PWM2 == TRUE) || (RP_PWM_USE_PWM3 == TRUE) || \ + (RP_PWM_USE_PWM4 == TRUE) || (RP_PWM_USE_PWM5 == TRUE) || \ + (RP_PWM_USE_PWM6 == TRUE) || (RP_PWM_USE_PWM7 == TRUE) || \ + defined(__DOXYGEN__) + +OSAL_IRQ_HANDLER(RP_PWM_IRQ_WRAP_HANDLER) { + OSAL_IRQ_PROLOGUE(); + + uint32_t ints = PWM->INTS; + PWM->INTR = ints; + +#if RP_PWM_USE_PWM0 == TRUE + if (((ints & PWM_INTS_CH0) != 0) && (PWMD0.config->callback != NULL)) { + PWMD0.config->callback(&PWMD0); + } +#endif + +#if RP_PWM_USE_PWM1 == TRUE + if (((ints & PWM_INTS_CH1) != 0) && (PWMD1.config->callback != NULL)) { + PWMD1.config->callback(&PWMD1); + } +#endif + +#if RP_PWM_USE_PWM2 == TRUE + if (((ints & PWM_INTS_CH2) != 0) && (PWMD2.config->callback != NULL)) { + PWMD2.config->callback(&PWMD2); + } +#endif + +#if RP_PWM_USE_PWM3 == TRUE + if (((ints & PWM_INTS_CH3) != 0) && (PWMD3.config->callback != NULL)) { + PWMD3.config->callback(&PWMD3); + } +#endif + +#if RP_PWM_USE_PWM4 == TRUE + if (((ints & PWM_INTS_CH4) != 0) && (PWMD4.config->callback != NULL)) { + PWMD4.config->callback(&PWMD4); + } +#endif + +#if RP_PWM_USE_PWM5 == TRUE + if (((ints & PWM_INTS_CH5) != 0) && (PWMD5.config->callback != NULL)) { + PWMD5.config->callback(&PWMD5); + } +#endif + +#if RP_PWM_USE_PWM6 == TRUE + if (((ints & PWM_INTS_CH6) != 0) && (PWMD6.config->callback != NULL)) { + PWMD6.config->callback(&PWMD6); + } +#endif + +#if RP_PWM_USE_PWM7 == TRUE + if (((ints & PWM_INTS_CH7) != 0) && (PWMD7.config->callback != NULL)) { + PWMD7.config->callback(&PWMD7); + } +#endif + + OSAL_IRQ_EPILOGUE(); +} + +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level PWM driver initialization. + * + * @notapi + */ +void pwm_lld_init(void) { + +#if RP_PWM_USE_PWM0 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD0); + PWMD0.pwm = PWM; + PWMD0.timer_id = 0; + PWMD0.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +#if RP_PWM_USE_PWM1 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD1); + PWMD1.pwm = PWM; + PWMD1.timer_id = 1; + PWMD1.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +#if RP_PWM_USE_PWM2 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD2); + PWMD2.pwm = PWM; + PWMD2.timer_id = 2; + PWMD2.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +#if RP_PWM_USE_PWM3 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD3); + PWMD3.pwm = PWM; + PWMD3.timer_id = 3; + PWMD3.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +#if RP_PWM_USE_PWM4 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD4); + PWMD4.pwm = PWM; + PWMD4.timer_id = 4; + PWMD4.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +#if RP_PWM_USE_PWM5 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD5); + PWMD5.pwm = PWM; + PWMD5.timer_id = 5; + PWMD5.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +#if RP_PWM_USE_PWM6 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD6); + PWMD6.pwm = PWM; + PWMD6.timer_id = 6; + PWMD6.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +#if RP_PWM_USE_PWM7 == TRUE + /* Driver initialization.*/ + pwmObjectInit(&PWMD7); + PWMD7.pwm = PWM; + PWMD7.timer_id = 7; + PWMD7.channels = PWM_CHANNELS; + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); +#endif + +} + +/** + * @brief Configures and activates the PWM peripheral. + * @note Starting a driver that is already in the @p PWM_READY state + * disables all the active channels. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_start(PWMDriver *pwmp) { + PWM_TypeDef *p = pwmp->pwm; + + if (pwmp->state == PWM_STOP) { + /* Clock activation and timer reset.*/ + hal_lld_peripheral_unreset(RESETS_ALLREG_PWM); + nvicEnableVector(RP_PWM_IRQ_WRAP_NUMBER, RP_PWM_IRQ_WRAP_NUMBER_PRIORITY); + } else { + /* Driver re-configuration scenario, it must be stopped first.*/ + p->CH[pwmp->timer_id].CSR = 0; + p->CH[pwmp->timer_id].CTR = 0; + p->CH[pwmp->timer_id].CC = 0; + } + + /* Counter clock divider */ + halfreq_t sys_clk = halClockGetPointX(clk_sys); + halfreq_t pwm_freq_min = sys_clk / 256; + + osalDbgAssert(pwmp->config->frequency >= pwm_freq_min, "RP2040 pwm counter frequency has a minimal value of the system clock divided by 256"); + + /* Integer part must not be zero */ + halfreq_t integer = sys_clk / pwmp->config->frequency; + integer = integer == 0 ? 1 : integer; + + halfreq_t fraction = (sys_clk << 4) / pwmp->config->frequency; + p->CH[pwmp->timer_id].DIV = (integer << 4 | (fraction & 0xF)); + p->CH[pwmp->timer_id].TOP = pwmp->period; + + uint32_t csr = PWM_CSR_EN | PWM_CSR_DIVMODE_FREE; + csr &= ~PWM_CSR_PH_CORRECT; + + switch (pwmp->config->channels[0].mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW: + csr |= PWM_CSR_A_INV; + break; + case PWM_OUTPUT_ACTIVE_HIGH: + csr &= ~PWM_CSR_A_INV; + break; + default: + ; + } + + switch (pwmp->config->channels[1].mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW: + csr |= PWM_CSR_B_INV; + break; + case PWM_OUTPUT_ACTIVE_HIGH: + csr &= ~PWM_CSR_B_INV; + break; + default: + ; + } + + p->CH[pwmp->timer_id].CSR = csr; +} + +/** + * @brief Deactivates the PWM peripheral. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_stop(PWMDriver *pwmp) { + PWM_TypeDef *p = pwmp->pwm; + + /* If in ready state then disables the PWM clock.*/ + if (pwmp->state == PWM_READY) { + p->CH[pwmp->timer_id].CSR = 0U; + p->CH[pwmp->timer_id].CTR = 0U; + p->CH[pwmp->timer_id].CC = 0U; + p->CH[pwmp->timer_id].DIV = 1U; + p->CH[pwmp->timer_id].TOP = 0xFFFF; + } + + /* If all timers are disabled, disable the interrupt and reset pwm peripheral */ + if (!p->EN) { + nvicDisableVector(RP_PWM_IRQ_WRAP_NUMBER); + hal_lld_peripheral_reset(RESETS_ALLREG_PWM); + } +} + +/** + * @brief Enables a PWM channel. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The channel is active using the specified configuration. + * @note The function has effect at the next cycle start. + * @note Channel notification is not enabled. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * @param[in] width PWM pulse width as clock pulses number + * + * @notapi + */ +void pwm_lld_enable_channel(PWMDriver *pwmp, + pwmchannel_t channel, + pwmcnt_t width) { + uint32_t current_cc = pwmp->pwm->CH[pwmp->timer_id].CC; + + if (channel == 0) { + pwmp->pwm->CH[pwmp->timer_id].CC = (width & PWM_CC_A) | (current_cc & PWM_CC_B); + } else { + pwmp->pwm->CH[pwmp->timer_id].CC = (current_cc & PWM_CC_A) | ((width << PWM_CC_B_Pos) & PWM_CC_B); + } +} + +/** + * @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...channels-1) + * + * @notapi + */ +void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) { + if (channel == 0) { + pwmp->pwm->CH[pwmp->timer_id].CC &= ~PWM_CC_A | PWM_CC_B; + } else { + pwmp->pwm->CH[pwmp->timer_id].CC &= PWM_CC_A | ~PWM_CC_B; + } +} + +/** + * @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) { + pwmp->pwm->INTE |= PWM_INTE_CH(pwmp->timer_id); +} + +/** + * @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->pwm->INTE &= ~PWM_INTE_CH(pwmp->timer_id); +} + +/** + * @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) { + (void)pwmp; + (void)channel; + osalDbgAssert(false, "Individual channel notifications are not available on RP2040."); +} + +/** + * @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) { + (void)pwmp; + (void)channel; + osalDbgAssert(false, "Individual channel notifications are not available on RP2040."); +} + + +#endif /* HAL_USE_PWM == TRUE */ + +/** @} */ diff --git a/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.h b/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.h new file mode 100644 index 00000000..4418de2e --- /dev/null +++ b/os/hal/ports/RP/LLD/PWMv1/hal_pwm_lld.h @@ -0,0 +1,324 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_pwm_lld.h + * @brief PLATFORM PWM subsystem low level driver header. + * + * @addtogroup PWM + * @{ + */ + +#ifndef HAL_PWM_LLD_H +#define HAL_PWM_LLD_H + +#if (HAL_USE_PWM == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Number of PWM channels per PWM driver. + */ +#define PWM_CHANNELS 2 + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name PLATFORM configuration options + * @{ + */ +/** + * @brief PWMD0 driver enable switch. + * @details If set to @p TRUE the support for PWM0 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM0) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM0 FALSE +#endif + +/** + * @brief PWMD1 driver enable switch. + * @details If set to @p TRUE the support for PWM1 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM1) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM1 FALSE +#endif + +/** + * @brief PWMD2 driver enable switch. + * @details If set to @p TRUE the support for PWM2 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM2) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM2 FALSE +#endif + +/** + * @brief PWMD3 driver enable switch. + * @details If set to @p TRUE the support for PWM3 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM3) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM3 FALSE +#endif + +/** + * @brief PWMD4 driver enable switch. + * @details If set to @p TRUE the support for PWM4 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM4) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM4 FALSE +#endif + +/** + * @brief PWMD5 driver enable switch. + * @details If set to @p TRUE the support for PWM5 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM5) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM5 FALSE +#endif + +/** + * @brief PWMD6 driver enable switch. + * @details If set to @p TRUE the support for PWM6 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM6) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM6 FALSE +#endif + +/** + * @brief PWMD7 driver enable switch. + * @details If set to @p TRUE the support for PWM7 is included. + * @note The default is @p FALSE. + */ +#if !defined(RP_PWM_USE_PWM7) || defined(__DOXYGEN__) +#define RP_PWM_USE_PWM7 FALSE +#endif + +/** + * @brief PWMD0..7 interrupt priority level setting. + */ +#if !defined(RP_PWM_IRQ_WRAP_NUMBER_PRIORITY) || defined(__DOXYGEN__) +#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY 3 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Configuration checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a PWM mode. + */ +typedef uint32_t pwmmode_t; + +/** + * @brief Type of a PWM channel. + */ +typedef uint8_t pwmchannel_t; + +/** + * @brief Type of a channels mask. + */ +typedef uint32_t pwmchnmsk_t; + +/** + * @brief Type of a PWM counter. + */ +typedef uint16_t pwmcnt_t; + +/** + * @brief Type of a PWM driver channel configuration structure. + */ +typedef struct { + /** + * @brief Channel active logic level. + */ + pwmmode_t mode; + /** + * @brief Channel callback pointer. + * @note This callback is invoked on the channel compare event. If set to + * @p NULL then the callback is disabled. + */ + pwmcallback_t callback; + /* End of the mandatory fields.*/ +} PWMChannelConfig; + +/** + * @brief Type of a PWM driver configuration structure. + */ +typedef struct { + /** + * @brief Timer clock in Hz. + * @note The low level can use assertions in order to catch invalid + * frequency specifications. + */ + uint32_t frequency; + /** + * @brief PWM period in ticks. + * @note The low level can use assertions in order to catch invalid + * period specifications. + */ + pwmcnt_t period; + /** + * @brief Periodic callback pointer. + * @note This callback is invoked on PWM counter reset. If set to + * @p NULL then the callback is disabled. + */ + pwmcallback_t callback; + /** + * @brief Channels configurations. + */ + PWMChannelConfig channels[PWM_CHANNELS]; + /* End of the mandatory fields.*/ +} PWMConfig; + +/** + * @brief Structure representing a PWM driver. + */ +struct PWMDriver { + /** + * @brief Driver state. + */ + pwmstate_t state; + /** + * @brief Current driver configuration data. + */ + const PWMConfig *config; + /** + * @brief Current PWM period in ticks. + */ + pwmcnt_t period; + /** + * @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 + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the PWM registers block. + */ + PWM_TypeDef *pwm; + + /** + * @brief Index of the associated PWM timer. + */ + pwmchannel_t timer_id; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Changes the period the PWM peripheral. + * @details This function changes the period of a PWM unit that has already + * been activated using @p pwmStart(). + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The PWM unit period is changed to the new value. + * @note The function has effect at the next cycle start. + * @note If a period is specified that is shorter than the pulse width + * programmed in one of the channels then the behavior is not + * guaranteed. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] period new cycle time in ticks + * + * @notapi + */ +#define pwm_lld_change_period(pwmp, period) \ + ((pwmp)->pwm->CH[(pwmp)->timer_id].TOP = (period)) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if (RP_PWM_USE_PWM0 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD0; +#endif + +#if (RP_PWM_USE_PWM1 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD1; +#endif + +#if (RP_PWM_USE_PWM2 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD2; +#endif + +#if (RP_PWM_USE_PWM3 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD3; +#endif + +#if (RP_PWM_USE_PWM4 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD4; +#endif + +#if (RP_PWM_USE_PWM5 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD5; +#endif + +#if (RP_PWM_USE_PWM6 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD6; +#endif + +#if (RP_PWM_USE_PWM7 == TRUE) && !defined(__DOXYGEN__) +extern PWMDriver PWMD7; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void pwm_lld_init(void); + void pwm_lld_start(PWMDriver *pwmp); + void pwm_lld_stop(PWMDriver *pwmp); + void pwm_lld_enable_channel(PWMDriver *pwmp, + pwmchannel_t channel, + pwmcnt_t width); + void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel); + void pwm_lld_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 + +#endif /* HAL_USE_PWM == TRUE */ + +#endif /* HAL_PWM_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/RP/RP2040/platform.mk b/os/hal/ports/RP/RP2040/platform.mk index 974a7fbf..f71e16da 100644 --- a/os/hal/ports/RP/RP2040/platform.mk +++ b/os/hal/ports/RP/RP2040/platform.mk @@ -13,6 +13,7 @@ else endif include ${CHIBIOS_CONTRIB}/os/hal/ports/RP/LLD/I2Cv1/driver.mk +include ${CHIBIOS_CONTRIB}/os/hal/ports/RP/LLD/PWMv1/driver.mk include ${CHIBIOS_CONTRIB}/os/hal/ports/RP/LLD/ADCv1/driver.mk include ${CHIBIOS_CONTRIB}/os/hal/ports/RP/LLD/USBDv1/driver.mk diff --git a/testhal/RP/RP2040/RT-RP2040-PICO-ADC/cfg/mcuconf.h b/testhal/RP/RP2040/RT-RP2040-PICO-ADC/cfg/mcuconf.h index 45d880cf..4cfec7ad 100644 --- a/testhal/RP/RP2040/RT-RP2040-PICO-ADC/cfg/mcuconf.h +++ b/testhal/RP/RP2040/RT-RP2040-PICO-ADC/cfg/mcuconf.h @@ -71,6 +71,19 @@ #define RP_SPI_SPI1_DMA_PRIORITY 1 #define RP_SPI_DMA_ERROR_HOOK(spip) +/* + * PWM driver system settings. + */ +#define RP_PWM_USE_PWM0 FALSE +#define RP_PWM_USE_PWM1 FALSE +#define RP_PWM_USE_PWM2 FALSE +#define RP_PWM_USE_PWM3 FALSE +#define RP_PWM_USE_PWM4 FALSE +#define RP_PWM_USE_PWM5 FALSE +#define RP_PWM_USE_PWM6 FALSE +#define RP_PWM_USE_PWM7 FALSE +#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY 3 + /* * USB driver system settings. */ diff --git a/testhal/RP/RP2040/RT-RP2040-PICO-HID/cfg/mcuconf.h b/testhal/RP/RP2040/RT-RP2040-PICO-HID/cfg/mcuconf.h index cc76e866..e45ac559 100644 --- a/testhal/RP/RP2040/RT-RP2040-PICO-HID/cfg/mcuconf.h +++ b/testhal/RP/RP2040/RT-RP2040-PICO-HID/cfg/mcuconf.h @@ -71,6 +71,19 @@ #define RP_SPI_SPI1_DMA_PRIORITY 1 #define RP_SPI_DMA_ERROR_HOOK(spip) +/* + * PWM driver system settings. + */ +#define RP_PWM_USE_PWM0 FALSE +#define RP_PWM_USE_PWM1 FALSE +#define RP_PWM_USE_PWM2 FALSE +#define RP_PWM_USE_PWM3 FALSE +#define RP_PWM_USE_PWM4 FALSE +#define RP_PWM_USE_PWM5 FALSE +#define RP_PWM_USE_PWM6 FALSE +#define RP_PWM_USE_PWM7 FALSE +#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY 3 + /* * USB driver system settings. */ diff --git a/testhal/RP/RP2040/RT-RP2040-PICO-I2C-24AA01/cfg/mcuconf.h b/testhal/RP/RP2040/RT-RP2040-PICO-I2C-24AA01/cfg/mcuconf.h index de2e1b16..928ebc81 100644 --- a/testhal/RP/RP2040/RT-RP2040-PICO-I2C-24AA01/cfg/mcuconf.h +++ b/testhal/RP/RP2040/RT-RP2040-PICO-I2C-24AA01/cfg/mcuconf.h @@ -73,6 +73,19 @@ #define RP_SPI_SPI1_DMA_PRIORITY 1 #define RP_SPI_DMA_ERROR_HOOK(spip) +/* + * PWM driver system settings. + */ +#define RP_PWM_USE_PWM0 FALSE +#define RP_PWM_USE_PWM1 FALSE +#define RP_PWM_USE_PWM2 FALSE +#define RP_PWM_USE_PWM3 FALSE +#define RP_PWM_USE_PWM4 FALSE +#define RP_PWM_USE_PWM5 FALSE +#define RP_PWM_USE_PWM6 FALSE +#define RP_PWM_USE_PWM7 FALSE +#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY 3 + /* * USB driver system settings. */ diff --git a/testhal/RP/RP2040/RT-RP2040-PICO-SERIAL/cfg/mcuconf.h b/testhal/RP/RP2040/RT-RP2040-PICO-SERIAL/cfg/mcuconf.h index 31a61fcd..62f546c6 100644 --- a/testhal/RP/RP2040/RT-RP2040-PICO-SERIAL/cfg/mcuconf.h +++ b/testhal/RP/RP2040/RT-RP2040-PICO-SERIAL/cfg/mcuconf.h @@ -71,6 +71,19 @@ #define RP_SPI_SPI1_DMA_PRIORITY 1 #define RP_SPI_DMA_ERROR_HOOK(spip) +/* + * PWM driver system settings. + */ +#define RP_PWM_USE_PWM0 FALSE +#define RP_PWM_USE_PWM1 FALSE +#define RP_PWM_USE_PWM2 FALSE +#define RP_PWM_USE_PWM3 FALSE +#define RP_PWM_USE_PWM4 FALSE +#define RP_PWM_USE_PWM5 FALSE +#define RP_PWM_USE_PWM6 FALSE +#define RP_PWM_USE_PWM7 FALSE +#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY 3 + /* * USB driver system settings. */