@@ -139,7 +139,7 @@ typedef struct _pyb_timer_obj_t {
139139// The following yields TIM_IT_UPDATE when channel is zero and
140140// TIM_IT_CC1..TIM_IT_CC4 when channel is 1..4
141141#define TIMER_IRQ_MASK (channel ) (1 << (channel))
142- #define TIMER_CNT_MASK (self ) ((self)->is_32bit ? 0x3fffffff : 0xffff)
142+ #define TIMER_CNT_MASK (self ) ((self)->is_32bit ? 0xffffffff : 0xffff)
143143#define TIMER_CHANNEL (self ) ((((self)->channel) - 1) << 2)
144144
145145TIM_HandleTypeDef TIM3_Handle ;
@@ -268,6 +268,37 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
268268
269269STATIC const mp_obj_type_t pyb_timer_channel_type ;
270270
271+ // Helper function to compute PWM value from timer period and percent value.
272+ // 'val' can be an int or a float between 0 and 100 (out of range values are
273+ // clamped).
274+ STATIC uint32_t compute_pwm_value_from_percent (uint32_t period , mp_obj_t val ) {
275+ uint32_t cmp ;
276+ if (0 ) {
277+ #if MICROPY_PY_BUILTINS_FLOAT
278+ } else if (MP_OBJ_IS_TYPE (val , & mp_type_float )) {
279+ cmp = mp_obj_get_float (val ) / 100.0 * period ;
280+ #endif
281+ } else {
282+ // For integer arithmetic, if period is large and 100*period will
283+ // overflow, then divide period before multiplying by cmp. Otherwise
284+ // do it the other way round to retain precision.
285+ // TODO we really need an mp_obj_get_uint_clamped function here so
286+ // that we can get long-int values as large as 0xffffffff.
287+ cmp = mp_obj_get_int (val );
288+ if (period > (1 << 31 ) / 100 ) {
289+ cmp = cmp * (period / 100 );
290+ } else {
291+ cmp = (cmp * period ) / 100 ;
292+ }
293+ }
294+ if (cmp < 0 ) {
295+ cmp = 0 ;
296+ } else if (cmp > period ) {
297+ cmp = period ;
298+ }
299+ return cmp ;
300+ }
301+
271302STATIC void pyb_timer_print (void (* print )(void * env , const char * fmt , ...), void * env , mp_obj_t self_in , mp_print_kind_t kind ) {
272303 pyb_timer_obj_t * self = self_in ;
273304
@@ -568,7 +599,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit);
568599STATIC const mp_arg_t pyb_timer_channel_args [] = {
569600 { MP_QSTR_callback , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = mp_const_none } },
570601 { MP_QSTR_pin , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = mp_const_none } },
571- { MP_QSTR_pulse_width , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0xffffffff } },
602+ { MP_QSTR_pulse_width , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
572603 { MP_QSTR_pulse_width_percent , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = mp_const_none } },
573604 { MP_QSTR_compare , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
574605 { MP_QSTR_polarity , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0xffffffff } },
@@ -663,30 +694,19 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map
663694 case CHANNEL_MODE_PWM_INVERTED : {
664695 TIM_OC_InitTypeDef oc_config ;
665696 oc_config .OCMode = channel_mode_info [chan -> mode ].oc_mode ;
666- if (vals [2 ].u_int != 0xffffffff ) {
667- // absolute pulse width value given
668- oc_config .Pulse = vals [2 ].u_int ;
669- } else if (vals [3 ].u_obj != mp_const_none ) {
697+ if (vals [3 ].u_obj != mp_const_none ) {
670698 // pulse width percent given
671699 uint32_t period = (__HAL_TIM_GetAutoreload (& self -> tim ) & TIMER_CNT_MASK (self )) + 1 ;
672- uint32_t cmp ;
673- #if MICROPY_PY_BUILTINS_FLOAT
674- if (MP_OBJ_IS_TYPE (vals [3 ].u_obj , & mp_type_float )) {
675- cmp = mp_obj_get_float (vals [3 ].u_obj ) * period / 100.0 ;
676- } else
677- #endif
678- {
679- cmp = mp_obj_get_int (vals [3 ].u_obj ) * period / 100 ;
680- }
681- if (cmp < 0 ) {
682- cmp = 0 ;
683- } else if (cmp > period ) {
684- cmp = period ;
700+ // For 32-bit timer, maximum period + 1 will overflow. In that
701+ // case we set the period back to 0xffffffff which will give very
702+ // close to the correct result for the percentage calculation.
703+ if (period == 0 ) {
704+ period = 0xffffffff ;
685705 }
686- oc_config .Pulse = cmp ;
706+ oc_config .Pulse = compute_pwm_value_from_percent ( period , vals [ 3 ]. u_obj ) ;
687707 } else {
688- // nothing given, default to pulse width of 0
689- oc_config .Pulse = 0 ;
708+ // use absolute pulse width value (defaults to 0 if nothing given)
709+ oc_config .Pulse = vals [ 2 ]. u_int ;
690710 }
691711 oc_config .OCPolarity = TIM_OCPOLARITY_HIGH ;
692712 oc_config .OCNPolarity = TIM_OCNPOLARITY_HIGH ;
@@ -910,38 +930,33 @@ STATIC mp_obj_t pyb_timer_channel_capture_compare(mp_uint_t n_args, const mp_obj
910930}
911931STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (pyb_timer_channel_capture_compare_obj , 1 , 2 , pyb_timer_channel_capture_compare );
912932
913- /// \method pulse_width_ratio([value])
914- /// Get or set the pulse width ratio associated with a channel. The value is
915- /// a floating-point number between 0.0 and 1.0, and is relative to the period
916- /// of the timer associated with this channel. For example, a ratio of 0.5
917- /// would be a 50% duty cycle.
933+ /// \method pulse_width_percent([value])
934+ /// Get or set the pulse width percentage associated with a channel. The value
935+ /// is a number between 0 and 100 and sets the percentage of the timer period
936+ /// for which the pulse is active. The value can be an integer or
937+ /// floating-point number for more accuracy. For example, a value of 25 gives
938+ /// a duty cycle of 25%.
918939STATIC mp_obj_t pyb_timer_channel_pulse_width_percent (mp_uint_t n_args , const mp_obj_t * args ) {
919940 pyb_timer_channel_obj_t * self = args [0 ];
920941 uint32_t period = (__HAL_TIM_GetAutoreload (& self -> timer -> tim ) & TIMER_CNT_MASK (self -> timer )) + 1 ;
942+ // For 32-bit timer, maximum period + 1 will overflow. In that case we set
943+ // the period back to 0xffffffff which will give very close to the correct
944+ // result for the percentage calculation.
945+ if (period == 0 ) {
946+ period = 0xffffffff ;
947+ }
921948 if (n_args == 1 ) {
922949 // get
923950 uint32_t cmp = __HAL_TIM_GetCompare (& self -> timer -> tim , TIMER_CHANNEL (self )) & TIMER_CNT_MASK (self -> timer );
924- #if MICROPY_PY_BUILTINS_FLOAT
925- return mp_obj_new_float ((float )cmp * 100.0 / (float )period );
926- #else
951+ #if MICROPY_PY_BUILTINS_FLOAT
952+ return mp_obj_new_float ((float )cmp / (float )period * 100.0 );
953+ #else
954+ // TODO handle overflow of multiplication for 32-bit timer
927955 return mp_obj_new_int (cmp * 100 / period );
928- #endif
956+ #endif
929957 } else {
930958 // set
931- uint32_t cmp ;
932- #if MICROPY_PY_BUILTINS_FLOAT
933- if (MP_OBJ_IS_TYPE (args [1 ], & mp_type_float )) {
934- cmp = mp_obj_get_float (args [1 ]) * period / 100.0 ;
935- } else
936- #endif
937- {
938- cmp = mp_obj_get_int (args [1 ]) * period / 100 ;
939- }
940- if (cmp < 0 ) {
941- cmp = 0 ;
942- } else if (cmp > period ) {
943- cmp = period ;
944- }
959+ uint32_t cmp = compute_pwm_value_from_percent (period , args [1 ]);
945960 __HAL_TIM_SetCompare (& self -> timer -> tim , TIMER_CHANNEL (self ), cmp & TIMER_CNT_MASK (self -> timer ));
946961 return mp_const_none ;
947962 }
0 commit comments