diff --git a/arch/arm/src/imxrt/imxrt_flexpwm.c b/arch/arm/src/imxrt/imxrt_flexpwm.c index 9e1d064541..6032c18a92 100644 --- a/arch/arm/src/imxrt/imxrt_flexpwm.c +++ b/arch/arm/src/imxrt/imxrt_flexpwm.c @@ -569,13 +569,13 @@ static int pwm_change_freq(struct pwm_lowerhalf_s *dev, uint16_t regval; uint16_t olddiv = getreg16(priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET + MODULE_OFFSET * shift); - uint16_t newdiv = (uint32_t)((float)CLK_FREQ / info->frequency + 0.5f); + uint32_t newdiv = (CLK_FREQ + (info->frequency / 2)) / info->frequency - 1; uint16_t prescale = 0; while (newdiv > PWM_RES && prescale < 7) { newdiv = newdiv >> 1; - prescale = prescale + 1; + prescale++; } if (newdiv > PWM_RES) @@ -595,8 +595,8 @@ static int pwm_change_freq(struct pwm_lowerhalf_s *dev, putreg16(regval, priv->base + IMXRT_FLEXPWM_SM0CTRL_OFFSET + MODULE_OFFSET * shift); - putreg16(newdiv - 1, priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET - + MODULE_OFFSET * shift); + putreg16(newdiv, priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET + + MODULE_OFFSET * shift); /* Update VAL0, VAL3 and VAL5 registers */ @@ -644,7 +644,6 @@ static int pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, uint16_t period; uint16_t width; uint16_t regval; - double duty_pct; uint8_t shift = channel - 1; /* Shift submodle offset addresses */ /* Get the period value */ @@ -654,8 +653,7 @@ static int pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, /* Compute PWM width (count value to set PWM low) */ - duty_pct = (duty / 65536.0) * 100; - width = (uint16_t)(((uint16_t)duty_pct * period) / 100); + width = b16toi(duty * period + b16HALF); /* Clear corresponding MCTRL[LDOK] bit */ diff --git a/arch/arm/src/samv7/hardware/sam_pwm.h b/arch/arm/src/samv7/hardware/sam_pwm.h index d903037b77..078c2885a1 100644 --- a/arch/arm/src/samv7/hardware/sam_pwm.h +++ b/arch/arm/src/samv7/hardware/sam_pwm.h @@ -119,7 +119,7 @@ # define CLK_DIVB_CLKB_POFF (0 << CLK_DIVB_SHIFT) /* CLKB clock is turned off */ # define CLK_DIVB_PREB (1 << CLK_DIVB_SHIFT) /* CLKB clock is selected by PREB */ -#define CLK_PREB_SHIFT (24) /* Bits: 24-27 CLKA Source Clock Selection */ +#define CLK_PREB_SHIFT (24) /* Bits: 24-27 CLKB Source Clock Selection */ #define CLK_PREB_MASK (0x7 << CLK_PREB_SHIFT) # define CLK_PREB_CLK (0 << CLK_PREB_SHIFT) /* Peripheral Clock */ # define CLK_PREB_CLK_DIV2 (1 << CLK_PREB_SHIFT) /* Peripheral Clock/2 */ @@ -172,7 +172,7 @@ /* DMA Register */ #define DMAR_DMADUTY_SHIFT (0) /* Bits: 0-23 Duty Cycle Holding Register */ -#define DMAR_DMADUTY_MAS (0xfffff << DMAR_DMADUTY_SHIFT) +#define DMAR_DMADUTY_MASK (0xffff << DMAR_DMADUTY_SHIFT) # define DMAR_DMADUTY_SEL(n) ((uint32_t)(n) << DMAR_DMADUTY_SHIFT) /* Sync Channels Update Control Register */ diff --git a/arch/arm/src/samv7/sam_pwm.c b/arch/arm/src/samv7/sam_pwm.c index 6fd40119c7..e446c60248 100644 --- a/arch/arm/src/samv7/sam_pwm.c +++ b/arch/arm/src/samv7/sam_pwm.c @@ -53,14 +53,14 @@ #ifdef CONFIG_SAMV7_PWM #ifdef CONFIG_PWM_NCHANNELS -# define PWM_NCHANNELS CONFIG_PWM_NCHANNELS +# define PWM_NCHANNELS CONFIG_PWM_NCHANNELS #else -# define PWM_NCHANNELS 1 +# define PWM_NCHANNELS 1 #endif -#define CHANNEL_OFFSET 0x20 -#define CLK_FREQ BOARD_MCK_FREQUENCY -#define PWM_RES 65535 +#define CHANNEL_OFFSET 0x20 +#define CLK_FREQ BOARD_MCK_FREQUENCY +#define PWM_RES 65535 /**************************************************************************** * Pre-processor Definitions @@ -73,8 +73,7 @@ struct sam_pwm_channel_s { uint8_t channel; /* Number of PWM module */ - bool used; /* True if the module is used */ - uint32_t pin; /* PWM output pin */ + gpio_pinset_t pin; /* PWM output pin */ }; struct sam_pwm_s @@ -82,8 +81,7 @@ struct sam_pwm_s const struct pwm_ops_s *ops; /* PWM operations */ const struct sam_pwm_channel_s *channels; uint8_t channels_num; /* Number of channels */ - uint32_t frequency; /* PWM frequency */ - uint32_t base; /* Base address of peripheral register */ + uintptr_t base; /* Base address of peripheral register */ }; /* PWM driver methods */ @@ -116,28 +114,24 @@ static struct sam_pwm_channel_s g_pwm0_channels[] = #ifdef CONFIG_SAMV7_PWM0_CH0 { .channel = 0, - .used = true, .pin = GPIO_PWMC0_H0, }, #endif #ifdef CONFIG_SAMV7_PWM0_CH1 { .channel = 1, - .used = true, .pin = GPIO_PWMC0_H1, }, #endif #ifdef CONFIG_SAMV7_PWM0_CH2 { .channel = 2, - .used = true, .pin = GPIO_PWMC0_H2, }, #endif #ifdef CONFIG_SAMV7_PWM0_CH3 { .channel = 3, - .used = true, .pin = GPIO_PWMC0_H3, }, #endif @@ -148,7 +142,6 @@ static struct sam_pwm_s g_pwm0 = .ops = &g_pwmops, .channels = g_pwm0_channels, .channels_num = PWM0_NCHANNELS, - .frequency = 0, .base = SAM_PWM0_BASE, }; #endif /* CONFIG_SAMV7_PWM0 */ @@ -160,28 +153,24 @@ static struct sam_pwm_channel_s g_pwm1_channels[] = #ifdef CONFIG_SAMV7_PWM1_CH0 { .channel = 0, - .used = true, .pin = GPIO_PWMC1_H0 }, #endif #ifdef CONFIG_SAMV7_PWM1_CH1 { .channel = 1, - .used = true, .pin = GPIO_PWMC1_H1 }, #endif #ifdef CONFIG_SAMV7_PWM1_CH2 { .channel = 2, - .used = true, .pin = GPIO_PWMC1_H2 }, #endif #ifdef CONFIG_SAMV7_PWM1_CH3 { .channel = 3, - .used = true, .pin = GPIO_PWMC1_H3 }, #endif @@ -192,7 +181,6 @@ static struct sam_pwm_s g_pwm1 = .ops = &g_pwmops, .channels = g_pwm1_channels, .channels_num = PWM1_NCHANNELS, - .frequency = 0, .base = SAM_PWM1_BASE, }; @@ -208,10 +196,10 @@ static uint32_t pwm_getreg(struct sam_pwm_s *priv, uint32_t offset); /* Helper functions */ -static int pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, - ub16_t duty); -static int pwm_change_freq(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info, uint8_t channel); +static void pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, + ub16_t duty); +static void pwm_set_freq(struct pwm_lowerhalf_s *dev, uint8_t channel, + uint32_t frequency); /**************************************************************************** * Private Functions @@ -229,40 +217,34 @@ static uint32_t pwm_getreg(struct sam_pwm_s *priv, uint32_t offset) } /**************************************************************************** - * Name: pwm_change_freq + * Name: pwm_set_freq * * Description: * Set timer frequency and change registers value to respect that * frequency. * * Input Parameters: - * dev - A reference to the lower half PWM driver state structure - * info - A reference to the characteristics of the pulsed output + * dev - A reference to the lower half PWM driver state structure + * channel - Channel to by updated + * frequency - New frequency * * Returned Value: - * Zero on success; a negated errno value on failure + * None * ****************************************************************************/ -static int pwm_change_freq(struct pwm_lowerhalf_s *dev, - const struct pwm_info_s *info, uint8_t channel) +static void pwm_set_freq(struct pwm_lowerhalf_s *dev, uint8_t channel, + uint32_t frequency) { struct sam_pwm_s *priv = (struct sam_pwm_s *)dev; -#ifdef CONFIG_PWM_MULTICHAN - uint8_t shift = info->channels[channel].channel - 1; -#else - uint8_t shift = priv->channels[0].channel; -#endif uint32_t regval; - uint32_t olddiv = pwm_getreg(priv, SAMV7_PWM_CPRDX - + (shift * CHANNEL_OFFSET)); - uint32_t newdiv = (uint32_t)((float)CLK_FREQ / info->frequency + 0.5f); + uint32_t newdiv = (CLK_FREQ + (frequency / 2)) / frequency - 1; uint32_t prescale = 0; while (newdiv > PWM_RES && prescale < 11) { newdiv = newdiv >> 1; - prescale = prescale + 1; + prescale++; } if (newdiv > PWM_RES) @@ -274,19 +256,26 @@ static int pwm_change_freq(struct pwm_lowerhalf_s *dev, newdiv = 2; } - regval = pwm_getreg(priv, SAMV7_PWM_CMRX + (shift * CHANNEL_OFFSET)); + regval = pwm_getreg(priv, SAMV7_PWM_CMRX + (channel * CHANNEL_OFFSET)); + regval &= ~CMR_CPRE_MASK; regval |= CMR_CPRE_SEL(prescale); - pwm_putreg(priv, SAMV7_PWM_CMRX + (shift * CHANNEL_OFFSET), regval); - pwm_putreg(priv, SAMV7_PWM_CPRDUPDX + (shift * CHANNEL_OFFSET), - newdiv - 1); + if (pwm_getreg(priv, SAMV7_PWM_SR) & CHID_SEL(1 << channel)) + { + pwm_putreg(priv, SAMV7_PWM_CMUPDX + (channel * CHANNEL_OFFSET), + regval); - regval = pwm_getreg(priv, SAMV7_PWM_CDTYX + (shift * CHANNEL_OFFSET)); - regval = regval * newdiv / olddiv; - pwm_putreg(priv, SAMV7_PWM_CDTYUPDX + (shift * CHANNEL_OFFSET), - regval); + pwm_putreg(priv, SAMV7_PWM_CPRDUPDX + (channel * CHANNEL_OFFSET), + CPRD_CPRD_SEL(newdiv)); + } + else + { + pwm_putreg(priv, SAMV7_PWM_CMRX + (channel * CHANNEL_OFFSET), + regval); - return OK; + pwm_putreg(priv, SAMV7_PWM_CPRDX + (channel * CHANNEL_OFFSET), + CPRD_CPRD_SEL(newdiv)); + } } /**************************************************************************** @@ -301,37 +290,43 @@ static int pwm_change_freq(struct pwm_lowerhalf_s *dev, * duty - New duty * * Returned Value: - * Zero on success; a negated errno value on failure + * None * ****************************************************************************/ -static int pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, - ub16_t duty) +static void pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, + ub16_t duty) { struct sam_pwm_s *priv = (struct sam_pwm_s *)dev; uint16_t period; uint16_t width; uint16_t regval; - double duty_pct; - uint8_t shift = channel; /* Shift submodle offset addresses */ /* Get the period value */ - period = pwm_getreg(priv, SAMV7_PWM_CPRDX + (shift * CHANNEL_OFFSET)); + period = pwm_getreg(priv, SAMV7_PWM_CPRDX + (channel * CHANNEL_OFFSET)); /* Compute PWM width (count value to set PWM low) */ - duty_pct = (duty / 65536.0) * 100; - width = (uint16_t)(((uint16_t)duty_pct * period) / 100); + width = b16toi(duty * period + b16HALF); /* Update duty cycle */ - pwm_putreg(priv, SAMV7_PWM_CDTYUPDX + (shift * CHANNEL_OFFSET), width); + if (pwm_getreg(priv, SAMV7_PWM_SR) & CHID_SEL(1 << channel)) + { + pwm_putreg(priv, SAMV7_PWM_CDTYUPDX + (channel * CHANNEL_OFFSET), + width); + } + else + { + pwm_putreg(priv, SAMV7_PWM_CDTYX + (channel * CHANNEL_OFFSET), + width); + } - regval = CHID_SEL(1 << shift); + /* Enable the channel */ + + regval = CHID_SEL(1 << channel); pwm_putreg(priv, SAMV7_PWM_ENA, regval); - - return OK; } /**************************************************************************** @@ -353,7 +348,8 @@ static int pwm_set_output(struct pwm_lowerhalf_s *dev, uint8_t channel, static int pwm_setup(struct pwm_lowerhalf_s *dev) { struct sam_pwm_s *priv = (struct sam_pwm_s *)dev; - uint32_t pin = 0; + gpio_pinset_t pin = 0; + uint8_t channel; uint32_t regval; /* Unlock User Interface */ @@ -368,13 +364,6 @@ static int pwm_setup(struct pwm_lowerhalf_s *dev) for (int i = 0; i < priv->channels_num; i++) { - /* Configure the channel only if is set to be used */ - - if (priv->channels[i].used != 1) - { - continue; - } - pin = priv->channels[i].pin; if (pin != 0) @@ -382,39 +371,28 @@ static int pwm_setup(struct pwm_lowerhalf_s *dev) sam_configgpio(pin); } - sam_configgpio(pin); + channel = priv->channels[i].channel; regval = CMR_CPOL | CMR_DPOLI; - pwm_putreg(priv, SAMV7_PWM_CMRX + (i * CHANNEL_OFFSET), regval); + pwm_putreg(priv, SAMV7_PWM_CMRX + (channel * CHANNEL_OFFSET), regval); - /* Set duty cycle register */ + /* Reset duty cycle register */ - pwm_putreg(priv, SAMV7_PWM_CDTYX + (i * CHANNEL_OFFSET), 0); + pwm_putreg(priv, SAMV7_PWM_CDTYX + (channel * CHANNEL_OFFSET), 0); - /* Set period register with default period */ + /* Reset period register */ - regval = 0x82b8; - pwm_putreg(priv, SAMV7_PWM_CPRDX + (i * CHANNEL_OFFSET), regval); + pwm_putreg(priv, SAMV7_PWM_CPRDX + (channel * CHANNEL_OFFSET), 0); /* Reset Dead Time Register */ - pwm_putreg(priv, SAMV7_PWM_DTX + (i * CHANNEL_OFFSET), 0); + pwm_putreg(priv, SAMV7_PWM_DTX + (channel * CHANNEL_OFFSET), 0); /* Fault protection registers */ pwm_putreg(priv, SAMV7_PWM_FPV1, 0); pwm_putreg(priv, SAMV7_PWM_FPV2, 0); pwm_putreg(priv, SAMV7_PWM_FPE, 0); - - /* Enable the channel */ - - regval = CHID_SEL(1 << i); - pwm_putreg(priv, SAMV7_PWM_ENA, regval); - - /* Set synchronous output */ - - regval = SCM_SYNC_SEL(1 << i); - pwm_putreg(priv, SAMV7_PWM_SCM, regval); } return OK; @@ -449,28 +427,6 @@ static int pwm_shutdown(struct pwm_lowerhalf_s *dev) regval = IR1_CHID_SEL(CHID_MASK); pwm_putreg(priv, SAMV7_PWM_IDR1, regval); - for (int i = 0; i < priv->channels_num; i++) - { - /* Skip modules that are not used */ - - if (priv->channels[i].used != 1) - { - continue; - } - - /* Reset period register */ - - pwm_putreg(priv, SAMV7_PWM_CPRDX + (i * CHANNEL_OFFSET), 0); - - /* Reset duty cycle register */ - - pwm_putreg(priv, SAMV7_PWM_CDTYX + (i * CHANNEL_OFFSET), 0); - - /* Reset Dead Time Register */ - - pwm_putreg(priv, SAMV7_PWM_DTX + (i * CHANNEL_OFFSET), 0); - } - return OK; } @@ -493,71 +449,39 @@ 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; - int ret = OK; - /* Change frequency only if it is needed */ - - if (info->frequency != priv->frequency) - { +#ifdef CONFIG_PWM_MULTICHAN for (int i = 0; i < PWM_NCHANNELS; i++) { -#ifdef CONFIG_PWM_MULTICHAN + int8_t index = info->channels[i].channel; + /* Break the loop if all following channels are not configured */ - if (info->channels[i].channel == -1) + if (index == -1) { break; } /* Configure the module freq only if is set to be used */ - if (info->channels[i].channel != 0) + if (index > 0 && (index - 1) < priv->channels_num) { - ret = pwm_change_freq(dev, info, i); + /* Set the frequency and enable PWM output for each channel */ + + pwm_set_freq(dev, priv->channels[index - 1].channel, + info->frequency); + pwm_set_output(dev, priv->channels[index - 1].channel, + info->channels[i].duty); } + } #else - ret = pwm_change_freq(dev, info, i); + /* Set the frequency and enable PWM output just for first channel */ + + pwm_set_freq(dev, priv->channels[0].channel, info->frequency); + pwm_set_output(dev, priv->channels[0].channel, info->duty); #endif - } - /* Save current frequency */ - - if (ret == OK) - { - priv->frequency = info->frequency; - } - } - -#ifdef CONFIG_PWM_MULTICHAN - for (int i = 0; ret == OK && i < PWM_NCHANNELS; i++) - { - /* Break the loop if all following channels are not configured */ - - if (info->channels[i].channel == -1) - { - break; - } - - /* Enable PWM output for each channel */ - - if (info->channels[i].channel != 0) - { - ret = pwm_set_output(dev, info->channels[i].channel - 1, - info->channels[i].duty); - } - } -#else - /* Enable PWM output just for first channel */ - - ret = pwm_set_output(dev, priv->channels[0].channel, info->duty); - -#endif /* CONFIG_PWM_MULTICHAN */ - - /* Set sychnronous outputs */ - - pwm_putreg(priv, SAMV7_PWM_SCUC, SCUC_UPDULOCK); - - return ret; + return OK; } /**************************************************************************** @@ -582,31 +506,18 @@ static int pwm_start(struct pwm_lowerhalf_s *dev, static int pwm_stop(struct pwm_lowerhalf_s *dev) { struct sam_pwm_s *priv = (struct sam_pwm_s *)dev; - uint8_t shift; uint32_t regval; #ifdef CONFIG_PWM_MULTICHAN for (int i = 0; i < priv->channels_num; i++) { - /* Skip settings if channel is not configured */ - - if (!priv->channels[i].used) - { - continue; - } - - shift = priv->channels[i].channel - 1; - - regval = CHID_SEL(1 << shift); + regval = CHID_SEL(1 << priv->channels[i].channel); pwm_putreg(priv, SAMV7_PWM_DIS, regval); } #else - shift = priv->channels[0].channel; - - regval = CHID_SEL(1 << shift); - pwm_putreg(priv, SAMV7_PWM_DIS, regval); - + regval = CHID_SEL(1 << priv->channels[0].channel); + pwm_putreg(priv, SAMV7_PWM_DIS, regval); #endif /* CONFIG_PWM_MULTICHAN */ return OK; diff --git a/arch/arm/src/samv7/sam_pwm.h b/arch/arm/src/samv7/sam_pwm.h index d8fb1e1ebd..b21bd6c3e8 100644 --- a/arch/arm/src/samv7/sam_pwm.h +++ b/arch/arm/src/samv7/sam_pwm.h @@ -60,8 +60,7 @@ #define PWM0_CH3 0 #endif -#define PWM0_NCHANNELS (PWM0_CH0 + PWM0_CH1 + \ - PWM0_CH2 + PWM0_CH3) +#define PWM0_NCHANNELS (PWM0_CH0 + PWM0_CH1 + PWM0_CH2 + PWM0_CH3) #ifdef CONFIG_SAMV7_PWM1_CH0 #define PWM1_CH0 1 @@ -87,8 +86,7 @@ #define PWM1_CH3 0 #endif -#define PWM1_NCHANNELS (PWM1_CH0 + PWM1_CH1 + \ - PWM1_CH2 + PWM1_CH3) +#define PWM1_NCHANNELS (PWM1_CH0 + PWM1_CH1 + PWM1_CH2 + PWM1_CH3) /**************************************************************************** * Public Function Prototypes diff --git a/include/nuttx/timers/pwm.h b/include/nuttx/timers/pwm.h index 6207720e7c..dccbb991c4 100644 --- a/include/nuttx/timers/pwm.h +++ b/include/nuttx/timers/pwm.h @@ -119,7 +119,7 @@ #ifdef CONFIG_PWM_MULTICHAN struct pwm_chan_s { - ub16_t duty; + ub16_t duty; int8_t channel; }; #endif