@@ -135,6 +135,7 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
135135 bool variable_frequency ) {
136136 self -> pin = pin ;
137137 self -> variable_frequency = variable_frequency ;
138+ self -> duty_cycle = duty ;
138139
139140 if (pin -> timer [0 ].index >= TC_INST_NUM &&
140141 pin -> timer [1 ].index >= TCC_INST_NUM
@@ -322,6 +323,13 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) {
322323}
323324
324325extern void common_hal_pulseio_pwmout_set_duty_cycle (pulseio_pwmout_obj_t * self , uint16_t duty ) {
326+ // Store the unadjusted duty cycle. It turns out the the process of adjusting and calculating
327+ // the duty cycle here and reading it back is lossy - the value will decay over time.
328+ // Track it here so that if frequency is changed we can use this value to recalculate the
329+ // proper duty cycle.
330+ // See https://github.com/adafruit/circuitpython/issues/2086 for more details
331+ self -> duty_cycle = duty ;
332+
325333 const pin_timer_t * t = self -> timer ;
326334 if (t -> is_tc ) {
327335 uint16_t adjusted_duty = tc_periods [t -> index ] * duty / 0xffff ;
@@ -415,7 +423,6 @@ void common_hal_pulseio_pwmout_set_frequency(pulseio_pwmout_obj_t* self,
415423 break ;
416424 }
417425 }
418- uint16_t old_duty = common_hal_pulseio_pwmout_get_duty_cycle (self );
419426 if (t -> is_tc ) {
420427 Tc * tc = tc_insts [t -> index ];
421428 uint8_t old_divisor = tc -> COUNT16 .CTRLA .bit .PRESCALER ;
@@ -450,7 +457,7 @@ void common_hal_pulseio_pwmout_set_frequency(pulseio_pwmout_obj_t* self,
450457 #endif
451458 }
452459
453- common_hal_pulseio_pwmout_set_duty_cycle (self , old_duty );
460+ common_hal_pulseio_pwmout_set_duty_cycle (self , self -> duty_cycle );
454461}
455462
456463uint32_t common_hal_pulseio_pwmout_get_frequency (pulseio_pwmout_obj_t * self ) {
0 commit comments