@@ -128,37 +128,70 @@ mp_uint_t get_prescaler_shift(mp_int_t prescaler) {
128128
129129STATIC const mp_obj_type_t pyb_timer_channel_type ;
130130
131+ // Helper function for determining the period used for calculating percent
132+ STATIC uint32_t compute_period (pyb_timer_obj_t * self ) {
133+ // In center mode, compare == period corresponds to 100%
134+ // In edge mode, compare == (period + 1) corresponds to 100%
135+ FTM_TypeDef * FTMx = self -> ftm .Instance ;
136+ uint32_t period = (FTMx -> MOD & 0xffff );
137+ if ((FTMx -> SC & FTM_SC_CPWMS ) == 0 ) {
138+ // Edge mode
139+ period ++ ;
140+ }
141+ return period ;
142+ }
143+
131144// Helper function to compute PWM value from timer period and percent value.
132145// 'val' can be an int or a float between 0 and 100 (out of range values are
133146// clamped).
134- STATIC uint32_t compute_pwm_value_from_percent (uint32_t period , mp_obj_t val ) {
147+ STATIC uint32_t compute_pwm_value_from_percent (uint32_t period , mp_obj_t percent_in ) {
135148 uint32_t cmp ;
136149 if (0 ) {
137150 #if MICROPY_PY_BUILTINS_FLOAT
138- } else if (MP_OBJ_IS_TYPE (val , & mp_type_float )) {
139- cmp = mp_obj_get_float (val ) / 100.0 * period ;
151+ } else if (MP_OBJ_IS_TYPE (percent_in , & mp_type_float )) {
152+ float percent = mp_obj_get_float (percent_in );
153+ if (percent <= 0.0 ) {
154+ cmp = 0 ;
155+ } else if (percent >= 100.0 ) {
156+ cmp = period ;
157+ } else {
158+ cmp = percent / 100.0 * ((float )period );
159+ }
140160 #endif
141161 } else {
142- // For integer arithmetic, if period is large and 100*period will
143- // overflow, then divide period before multiplying by cmp. Otherwise
144- // do it the other way round to retain precision.
145- // TODO we really need an mp_obj_get_uint_clamped function here so
146- // that we can get long-int values as large as 0xffffffff.
147- cmp = mp_obj_get_int (val );
148- if (period > (1 << 31 ) / 100 ) {
149- cmp = cmp * (period / 100 );
162+ mp_int_t percent = mp_obj_get_int (percent_in );
163+ if (percent <= 0 ) {
164+ cmp = 0 ;
165+ } else if (percent >= 100 ) {
166+ cmp = period ;
150167 } else {
151- cmp = (cmp * period ) / 100 ;
168+ cmp = (( uint32_t ) percent * period ) / 100 ;
152169 }
153170 }
154- if (cmp < 0 ) {
155- cmp = 0 ;
156- } else if (cmp > period ) {
157- cmp = period ;
158- }
159171 return cmp ;
160172}
161173
174+ // Helper function to compute percentage from timer perion and PWM value.
175+ STATIC mp_obj_t compute_percent_from_pwm_value (uint32_t period , uint32_t cmp ) {
176+ #if MICROPY_PY_BUILTINS_FLOAT
177+ float percent = (float )cmp * 100.0 / (float )period ;
178+ if (cmp > period ) {
179+ percent = 100.0 ;
180+ } else {
181+ percent = (float )cmp * 100.0 / (float )period ;
182+ }
183+ return mp_obj_new_float (percent );
184+ #else
185+ mp_int_t percent ;
186+ if (cmp > period ) {
187+ percent = 100 ;
188+ } else {
189+ percent = cmp * 100 / period ;
190+ }
191+ return mp_obj_new_int (percent );
192+ #endif
193+ }
194+
162195STATIC void pyb_timer_print (void (* print )(void * env , const char * fmt , ...), void * env , mp_obj_t self_in , mp_print_kind_t kind ) {
163196 pyb_timer_obj_t * self = self_in ;
164197
@@ -169,7 +202,7 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void
169202 self -> tim_id ,
170203 1 << (self -> ftm .Instance -> SC & 7 ),
171204 self -> ftm .Instance -> MOD & 0xffff ,
172- self -> ftm .Init .CounterMode == FTM_COUNTERMODE_UP ? "tUP " : "CENTER" );
205+ self -> ftm .Init .CounterMode == FTM_COUNTERMODE_UP ? "UP " : "CENTER" );
173206 }
174207}
175208
@@ -193,7 +226,8 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void
193226/// - `period` [0-0xffff] - Specifies the value to be loaded into the timer's
194227/// Modulo Register (MOD). This determines the period of the timer (i.e.
195228/// when the counter cycles). The timer counter will roll-over after
196- /// `period + 1` timer clock cycles.
229+ /// `period` timer clock cycles. In center mode, a compare register > 0x7fff
230+ /// doesn't seem to work properly, so keep this in mind.
197231///
198232/// - `mode` can be one of:
199233/// - `Timer.UP` - configures the timer to count from 0 to MOD (default)
@@ -231,15 +265,15 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, uint n_args, const
231265
232266 uint32_t period = MAX (1 , F_BUS / vals [0 ].u_int );
233267 uint32_t prescaler_shift = 0 ;
234- while (period > 0x10000 && prescaler_shift < 7 ) {
268+ while (period > 0xffff && prescaler_shift < 7 ) {
235269 period >>= 1 ;
236270 prescaler_shift ++ ;
237271 }
238- if (period > 0x10000 ) {
239- period = 0x10000 ;
272+ if (period > 0xffff ) {
273+ period = 0xffff ;
240274 }
241275 init -> PrescalerShift = prescaler_shift ;
242- init -> Period = period - 1 ;
276+ init -> Period = period ;
243277 } else if (vals [1 ].u_int != 0xffffffff && vals [2 ].u_int != 0xffffffff ) {
244278 // set prescaler and period directly
245279 init -> PrescalerShift = get_prescaler_shift (vals [1 ].u_int );
@@ -501,13 +535,13 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map
501535 oc_config .OCMode = channel_mode_info [chan -> mode ].oc_mode ;
502536 if (vals [3 ].u_obj != mp_const_none ) {
503537 // pulse width ratio given
504- uint32_t period = (self -> ftm . Instance -> MOD & 0xffff ) + 1 ;
538+ uint32_t period = compute_period (self ) ;
505539 oc_config .Pulse = compute_pwm_value_from_percent (period , vals [3 ].u_obj );
506540 } else {
507541 // use absolute pulse width value (defaults to 0 if nothing given)
508542 oc_config .Pulse = vals [2 ].u_int ;
509543 }
510- oc_config .OCPolarity = FTM_OCPOLARITY_HIGH ;
544+ oc_config .OCPolarity = FTM_OCPOLARITY_HIGH ;
511545
512546 HAL_FTM_PWM_ConfigChannel (& self -> ftm , & oc_config , channel );
513547 if (chan -> callback == mp_const_none ) {
@@ -745,6 +779,9 @@ STATIC void pyb_timer_channel_print(void (*print)(void *env, const char *fmt, ..
745779/// Get or set the pulse width value associated with a channel.
746780/// capture, compare, and pulse_width are all aliases for the same function.
747781/// pulse_width is the logical name to use when the channel is in PWM mode.
782+ ///
783+ /// In edge aligned mode, a pulse_width of `period + 1` corresponds to a duty cycle of 100%
784+ /// In center aligned mode, a pulse width of `period` corresponds to a duty cycle of 100%
748785STATIC mp_obj_t pyb_timer_channel_capture_compare (mp_uint_t n_args , const mp_obj_t * args ) {
749786 pyb_timer_channel_obj_t * self = args [0 ];
750787 FTM_TypeDef * FTMx = self -> timer -> ftm .Instance ;
@@ -770,15 +807,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_capture_compare_obj
770807STATIC mp_obj_t pyb_timer_channel_pulse_width_percent (mp_uint_t n_args , const mp_obj_t * args ) {
771808 pyb_timer_channel_obj_t * self = args [0 ];
772809 FTM_TypeDef * FTMx = self -> timer -> ftm .Instance ;
773- uint32_t period = ( FTMx -> MOD & 0xffff ) + 1 ;
810+ uint32_t period = compute_period ( self -> timer ) ;
774811 if (n_args == 1 ) {
775812 // get
776813 uint32_t cmp = FTMx -> channel [self -> channel ].CV & 0xffff ;
777- #if MICROPY_PY_BUILTINS_FLOAT
778- return mp_obj_new_float ((float )cmp / (float )period * 100.0 );
779- #else
780- return mp_obj_new_int (cmp * 100 / period );
781- #endif
814+ return compute_percent_from_pwm_value (period , cmp );
782815 } else {
783816 // set
784817 uint32_t cmp = compute_pwm_value_from_percent (period , args [1 ]);
0 commit comments