@@ -149,6 +149,21 @@ void pwmout_reset(void) {
149149
150150#define PWM_SRC_CLK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk)
151151
152+ static int calculate_pulse_count (uint32_t frequency , uint8_t * prescaler ) {
153+ if (frequency > PWM_SRC_CLK_FREQ /2 ) {
154+ return 0 ;
155+ }
156+ for (int shift = 0 ; shift < 8 ; shift ++ ) {
157+ int pulse_count = PWM_SRC_CLK_FREQ /(1 <<shift )/frequency ;
158+ if (pulse_count >= 65535 ) {
159+ continue ;
160+ }
161+ * prescaler = shift ;
162+ return pulse_count ;
163+ }
164+ return 0 ;
165+ }
166+
152167pwmout_result_t common_hal_pwmio_pwmout_construct (pwmio_pwmout_obj_t * self ,
153168 const mcu_pin_obj_t * pin ,
154169 uint16_t duty ,
@@ -196,16 +211,16 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
196211 // pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle;
197212 pwmConfig .enableDebugMode = true;
198213
199- if (PWM_Init (self -> pwm -> pwm , self -> pwm -> submodule , & pwmConfig ) == kStatus_Fail ) {
200- return PWMOUT_INVALID_PIN ;
201- }
214+ self -> pulse_count = calculate_pulse_count (frequency , & self -> prescaler );
202215
203- if (frequency == 0 || frequency > PWM_SRC_CLK_FREQ / 2 ) {
216+ if (self -> pulse_count == 0 ) {
204217 return PWMOUT_INVALID_FREQUENCY ;
205218 }
206219
207- if (PWM_SRC_CLK_FREQ / frequency >= 65536 ) {
208- return PWMOUT_INVALID_FREQUENCY ;
220+ pwmConfig .prescale = self -> prescaler ;
221+
222+ if (PWM_Init (self -> pwm -> pwm , self -> pwm -> submodule , & pwmConfig ) == kStatus_Fail ) {
223+ return PWMOUT_INVALID_PIN ;
209224 }
210225
211226 pwm_signal_param_t pwmSignal = {
@@ -228,7 +243,6 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
228243
229244 PWM_StartTimer (self -> pwm -> pwm , 1 << self -> pwm -> submodule );
230245
231- self -> pulse_count = PWM_SRC_CLK_FREQ /frequency ;
232246
233247 common_hal_pwmio_pwmout_set_duty_cycle (self , duty );
234248
@@ -253,7 +267,11 @@ void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t *self, uint16_t d
253267 // * it works in integer percents
254268 // * it can't set the "X" duty cycle
255269 self -> duty_cycle = duty ;
256- self -> duty_scaled = ((uint32_t )duty * self -> pulse_count + self -> pulse_count /2 ) / 65535 ;
270+ if (duty == 65535 ) {
271+ self -> duty_scaled = self -> pulse_count + 1 ;
272+ } else {
273+ self -> duty_scaled = ((uint32_t )duty * self -> pulse_count + self -> pulse_count /2 ) / 65535 ;
274+ }
257275 switch (self -> pwm -> channel ) {
258276 case kPWM_PwmX :
259277 self -> pwm -> pwm -> SM [self -> pwm -> submodule ].VAL0 = 0 ;
@@ -271,27 +289,37 @@ void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t *self, uint16_t d
271289}
272290
273291uint16_t common_hal_pwmio_pwmout_get_duty_cycle (pwmio_pwmout_obj_t * self ) {
292+ if (self -> duty_cycle == 65535 ) {
293+ return 65535 ;
294+ }
274295 return ((uint32_t )self -> duty_scaled * 65535 + 65535 /2 ) / self -> pulse_count ;
275296}
276297
277298void common_hal_pwmio_pwmout_set_frequency (pwmio_pwmout_obj_t * self ,
278299 uint32_t frequency ) {
279300
280- if (frequency > PWM_SRC_CLK_FREQ /2 ) {
301+ int pulse_count = calculate_pulse_count (frequency , & self -> prescaler );
302+ if (pulse_count == 0 ) {
281303 mp_raise_ValueError (translate ("Invalid PWM frequency" ));
282304 }
283305
284- if (PWM_SRC_CLK_FREQ / frequency >= 65536 ) {
285- mp_raise_ValueError (translate ("Invalid PWM frequency" ));
286- }
306+ self -> pulse_count = pulse_count ;
287307
288- self -> pulse_count = PWM_SRC_CLK_FREQ /frequency ;
308+ // a small glitch can occur when adjusting the prescaler, from the setting
309+ // of CTRL just below to the setting of the Ldok register in
310+ // set_duty_cycle.
311+ uint32_t reg = self -> pwm -> pwm -> SM [self -> pwm -> submodule ].CTRL ;
312+ reg &= ~(PWM_CTRL_PRSC_MASK );
313+ reg |= PWM_CTRL_PRSC (self -> prescaler );
314+ self -> pwm -> pwm -> SM [self -> pwm -> submodule ].CTRL = reg ;
289315 self -> pwm -> pwm -> SM [self -> pwm -> submodule ].VAL1 = self -> pulse_count ;
316+
317+ // we need to recalculate the duty cycle. As a side effect of this
290318 common_hal_pwmio_pwmout_set_duty_cycle (self , self -> duty_cycle );
291319}
292320
293321uint32_t common_hal_pwmio_pwmout_get_frequency (pwmio_pwmout_obj_t * self ) {
294- return PWM_SRC_CLK_FREQ /self -> pulse_count ;
322+ return PWM_SRC_CLK_FREQ /self -> pulse_count /( 1 << self -> prescaler ) ;
295323}
296324
297325bool common_hal_pwmio_pwmout_get_variable_frequency (pwmio_pwmout_obj_t * self ) {
0 commit comments