From 297b3b0209b1f6cc0f35ec0f95380d4747b18292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pressl=2C=20=C5=A0t=C4=9Bp=C3=A1n?= Date: Fri, 26 Apr 2024 13:10:16 +0200 Subject: [PATCH] arch/arm/src/samv7/sam_pwm.c: option to make channels synchronous Make channels synchronous (i.e. share the same timebase) with the help of SAMV7_PWMx_CHy_SYNC defines. All the channels share the same timebase of channel 0, so this channel must be defined too. Signed-off-by: Stepan Pressl --- arch/arm/src/samv7/Kconfig | 54 +++++++++++++++ arch/arm/src/samv7/sam_pwm.c | 126 +++++++++++++++++++++++++++++++++-- 2 files changed, 175 insertions(+), 5 deletions(-) diff --git a/arch/arm/src/samv7/Kconfig b/arch/arm/src/samv7/Kconfig index d3602c5f44..562685e8c3 100644 --- a/arch/arm/src/samv7/Kconfig +++ b/arch/arm/src/samv7/Kconfig @@ -669,6 +669,18 @@ menuconfig SAMV7_PWM0 if SAMV7_PWM0 +config SAMV7_PWM0_SYNC + bool "PWM0 synchronous channels" + depends on PWM_MULTICHAN + default n + ---help--- + This option makes the synchronization between channels possible. + This means every synchronized channel has the same clock, the + same period and the same alignment. If any channel is defined + as synchronous, channel 0 is defined as synchronous too because + channel 0's counter is used by other synchronous channels. + This option automatically defines channel 0 as synchronous. + config SAMV7_PWM0_CH0 bool "PWM0 Channel 0" default n @@ -701,6 +713,11 @@ config SAMV7_PWM0_CH1_COMP depends on !SAMV7_PWM0_CH1_LONLY default n +config SAMV7_PWM0_CH1_SYNC + bool "Synchronous channel" + depends on SAMV7_PWM0_SYNC + default n + endif config SAMV7_PWM0_CH2 @@ -718,6 +735,11 @@ config SAMV7_PWM0_CH2_COMP depends on !SAMV7_PWM0_CH2_LONLY default n +config SAMV7_PWM0_CH2_SYNC + bool "Synchronous channel" + depends on SAMV7_PWM0_SYNC + default n + endif config SAMV7_PWM0_CH3 @@ -735,6 +757,11 @@ config SAMV7_PWM0_CH3_COMP depends on !SAMV7_PWM0_CH3_LONLY default n +config SAMV7_PWM0_CH3_SYNC + bool "Synchronous channel" + depends on SAMV7_PWM0_SYNC + default n + endif menu "PWM Comparison units" @@ -905,6 +932,18 @@ menuconfig SAMV7_PWM1 if SAMV7_PWM1 +config SAMV7_PWM1_SYNC + bool "PWM1 synchronous channels" + depends on PWM_MULTICHAN + default n + ---help--- + This option makes the synchronization between channels possible. + This means every synchronized channel has the same clock, the + same period and the same alignment. If any channel is defined + as synchronous, channel 0 is defined as synchronous too because + channel 0's counter is used by other synchronous channels. + This option automatically defines channel 0 as synchronous. + config SAMV7_PWM1_CH0 bool "PWM1 Channel 0" default n @@ -937,6 +976,11 @@ config SAMV7_PWM1_CH1_COMP depends on !SAMV7_PWM1_CH1_LONLY default n +config SAMV7_PWM1_CH1_SYNC + bool "Synchronous channel" + depends on SAMV7_PWM1_SYNC + default n + endif config SAMV7_PWM1_CH2 @@ -954,6 +998,11 @@ config SAMV7_PWM1_CH2_COMP depends on !SAMV7_PWM1_CH2_LONLY default n +config SAMV7_PWM1_CH2_SYNC + bool "Synchronous channel" + depends on SAMV7_PWM1_SYNC + default n + endif config SAMV7_PWM1_CH3 @@ -971,6 +1020,11 @@ config SAMV7_PWM1_CH3_COMP depends on !SAMV7_PWM1_CH3_LONLY default n +config SAMV7_PWM1_CH3_SYNC + bool "Synchronous channel" + depends on SAMV7_PWM1_SYNC + default n + endif menu "PWM Comparison units" diff --git a/arch/arm/src/samv7/sam_pwm.c b/arch/arm/src/samv7/sam_pwm.c index 58ce8ce16b..e743a83e2a 100644 --- a/arch/arm/src/samv7/sam_pwm.c +++ b/arch/arm/src/samv7/sam_pwm.c @@ -65,6 +65,13 @@ #define PWM_RES 65535 #define COMP_UNITS_NUM 8 +/* Sync offset flags defines */ + +#define CH0_SYNC_FLAG (1 << 0) +#define CH1_SYNC_FLAG (1 << 1) +#define CH2_SYNC_FLAG (1 << 2) +#define CH3_SYNC_FLAG (1 << 3) + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -104,6 +111,7 @@ struct sam_pwm_s const struct sam_pwm_fault_s *fault; uint8_t channels_num; /* Number of channels */ uintptr_t base; /* Base address of peripheral register */ + uint8_t sync; /* Flags of synchronized channels */ }; /* PWM driver methods */ @@ -229,6 +237,38 @@ static struct sam_pwm_fault_s g_pwm0_fault = .gpio_2 = GPIO_PWMC0_FI2, }; +/* Define sync flags */ + +#ifdef CONFIG_SAMV7_PWM0_SYNC + #define PWM0_CH0_SYNC_FLAG CH0_SYNC_FLAG + + #ifdef CONFIG_SAMV7_PWM0_CH1_SYNC + #define PWM0_CH1_SYNC_FLAG CH1_SYNC_FLAG + #else + #define PWM0_CH1_SYNC_FLAG 0 + #endif + + #ifdef CONFIG_SAMV7_PWM0_CH2_SYNC + #define PWM0_CH2_SYNC_FLAG CH2_SYNC_FLAG + #else + #define PWM0_CH2_SYNC_FLAG 0 + #endif + + #ifdef CONFIG_SAMV7_PWM0_CH3_SYNC + #define PWM0_CH3_SYNC_FLAG CH3_SYNC_FLAG + #else + #define PWM0_CH3_SYNC_FLAG 0 + #endif +#else + #define PWM0_CH0_SYNC_FLAG 0 + #define PWM0_CH1_SYNC_FLAG 0 + #define PWM0_CH2_SYNC_FLAG 0 + #define PWM0_CH3_SYNC_FLAG 0 +#endif + +#define PWM0_SYNC_FLAGS (PWM0_CH0_SYNC_FLAG | PWM0_CH1_SYNC_FLAG | \ + PWM0_CH2_SYNC_FLAG | PWM0_CH3_SYNC_FLAG) + static struct sam_pwm_s g_pwm0 = { .ops = &g_pwmops, @@ -237,6 +277,7 @@ static struct sam_pwm_s g_pwm0 = .fault = &g_pwm0_fault, .channels_num = PWM0_NCHANNELS, .base = SAM_PWM0_BASE, + .sync = PWM0_SYNC_FLAGS, }; #endif /* CONFIG_SAMV7_PWM0 */ @@ -340,6 +381,38 @@ static struct sam_pwm_fault_s g_pwm1_fault = .gpio_2 = GPIO_PWMC1_FI2, }; +/* Define sync flags */ + +#ifdef CONFIG_SAMV7_PWM1_SYNC + #define PWM1_CH0_SYNC_FLAG CH0_SYNC_FLAG + + #ifdef CONFIG_SAMV7_PWM1_CH1_SYNC + #define PWM1_CH1_SYNC_FLAG CH1_SYNC_FLAG + #else + #define PWM1_CH1_SYNC_FLAG 0 + #endif + + #ifdef CONFIG_SAMV7_PWM1_CH2_SYNC + #define PWM1_CH2_SYNC_FLAG CH2_SYNC_FLAG + #else + #define PWM1_CH2_SYNC_FLAG 0 + #endif + + #ifdef CONFIG_SAMV7_PWM1_CH3_SYNC + #define PWM1_CH3_SYNC_FLAG CH3_SYNC_FLAG + #else + #define PWM1_CH3_SYNC_FLAG 0 + #endif +#else + #define PWM1_CH0_SYNC_FLAG 0 + #define PWM1_CH1_SYNC_FLAG 0 + #define PWM1_CH2_SYNC_FLAG 0 + #define PWM1_CH3_SYNC_FLAG 0 +#endif + +#define PWM1_SYNC_FLAGS (PWM1_CH0_SYNC_FLAG | PWM1_CH1_SYNC_FLAG | \ + PWM1_CH2_SYNC_FLAG | PWM1_CH3_SYNC_FLAG) + static struct sam_pwm_s g_pwm1 = { .ops = &g_pwmops, @@ -348,6 +421,7 @@ static struct sam_pwm_s g_pwm1 = .fault = &g_pwm1_fault, .channels_num = PWM1_NCHANNELS, .base = SAM_PWM1_BASE, + .sync = PWM1_SYNC_FLAGS, }; #endif @@ -496,10 +570,14 @@ static void pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, width); } - /* Enable the channel */ - regval = CHID_SEL(1 << channel); - pwm_putreg(priv, SAMV7_PWM_ENA, regval); + + /* The enabling of a channel should be only done on unsynced channels */ + + if (!(priv->sync & regval)) + { + pwm_putreg(priv, SAMV7_PWM_ENA, regval); + } } /**************************************************************************** @@ -684,6 +762,13 @@ static void pwm_set_polarity(struct pwm_lowerhalf_s *dev, uint8_t channel, struct sam_pwm_s *priv = (struct sam_pwm_s *)dev; uint16_t regval; + /* Can't change polarity, if the channel is enabled! */ + + if (pwm_getreg(priv, SAMV7_PWM_SR) & CHID_SEL(1 << channel)) + { + return; + } + regval = pwm_getreg(priv, SAMV7_PWM_CMRX + (channel * CHANNEL_OFFSET)); regval &= ~CMR_CPOL; regval &= ~CMR_DPOLI; @@ -810,6 +895,14 @@ static int pwm_setup(struct pwm_lowerhalf_s *dev) pwm_putreg(priv, SAMV7_PWM_FPV1, 0); pwm_putreg(priv, SAMV7_PWM_FPV2, 0); + /* Enable synchronous channels. The flags in priv->sync + * correspond to the lowest bits in PWM_SCM. + * UPDM[1:0] is set to zero (manual update of deadtime, duty). + */ + + regval = (uint32_t)priv->sync; + pwm_putreg(priv, SAMV7_PWM_SCM, regval); + return OK; } @@ -864,9 +957,7 @@ static int pwm_start(struct pwm_lowerhalf_s *dev, const struct pwm_info_s *info) { struct sam_pwm_s *priv = (struct sam_pwm_s *)dev; -#ifdef CONFIG_PWM_OVERWRITE uint32_t regval; -#endif #ifdef CONFIG_PWM_MULTICHAN for (int i = 0; i < PWM_NCHANNELS; i++) @@ -920,6 +1011,25 @@ static int pwm_start(struct pwm_lowerhalf_s *dev, #endif } } + + /* Perform the update of synchronized PWM channels */ + + if (priv->sync) + { + regval = SCUC_UPDULOCK; + + /* Enable the Channel 0 if synchronous channels are used. + * Channel 0's counter is used by all synchronous channels and + * enabling CH0 results in enabling all synchronous channels. + * + * Enable the CH0 here after all setting all channel parameters, + * because setting polarity configurations requires disabled + * channels. + */ + + pwm_putreg(priv, SAMV7_PWM_ENA, CHID_SEL(1)); + pwm_putreg(priv, SAMV7_PWM_SCUC, regval); + } #else /* Set the frequency and enable PWM output just for first channel */ @@ -969,6 +1079,12 @@ static int pwm_stop(struct pwm_lowerhalf_s *dev) pwm_putreg(priv, SAMV7_PWM_DIS, regval); } + /* Just to be sure, disable all sync channels too */ + + regval = pwm_getreg(priv, SAMV7_PWM_SCM); + regval &= ~(CHID_SEL(1 << 0) | CHID_SEL(1 << 1) | + CHID_SEL(1 << 2) | CHID_SEL(1 << 3)); + pwm_putreg(priv, SAMV7_PWM_SCM, regval); #else regval = CHID_SEL(1 << priv->channels[0].channel); pwm_putreg(priv, SAMV7_PWM_DIS, regval);