diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 73a8db7cc0..3e35ed682a 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -309,10 +309,12 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) { case UINT64: case INT64: return mp_obj_new_int_from_ll(((int64_t*)p)[index]); + #if MICROPY_PY_BUILTINS_FLOAT case FLOAT32: return mp_obj_new_float(((float*)p)[index]); case FLOAT64: return mp_obj_new_float(((double*)p)[index]); + #endif default: assert(0); return MP_OBJ_NULL; diff --git a/stmhal/adc.c b/stmhal/adc.c index 3a93c20abe..6044931ec9 100644 --- a/stmhal/adc.c +++ b/stmhal/adc.c @@ -349,6 +349,7 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) { return ((raw_value - CORE_TEMP_V25) / CORE_TEMP_AVG_SLOPE) + 25; } +#if MICROPY_PY_BUILTINS_FLOAT float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) { uint32_t raw_value = adc_config_and_read_channel(adcHandle, ADC_CHANNEL_VBAT); @@ -368,6 +369,7 @@ float adc_read_core_vref(ADC_HandleTypeDef *adcHandle) { return raw_value * VBAT_DIV / 4096.0f * 3.3f; } +#endif /******************************************************************************/ /* Micro Python bindings : adc_all object */ @@ -399,6 +401,7 @@ STATIC mp_obj_t adc_all_read_core_temp(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_temp_obj, adc_all_read_core_temp); +#if MICROPY_PY_BUILTINS_FLOAT STATIC mp_obj_t adc_all_read_core_vbat(mp_obj_t self_in) { pyb_adc_all_obj_t *self = self_in; float data = adc_read_core_vbat(&self->handle); @@ -412,12 +415,15 @@ STATIC mp_obj_t adc_all_read_core_vref(mp_obj_t self_in) { return mp_obj_new_float(data); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_vref_obj, adc_all_read_core_vref); +#endif STATIC const mp_map_elem_t adc_all_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_read_channel), (mp_obj_t)&adc_all_read_channel_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_read_core_temp), (mp_obj_t)&adc_all_read_core_temp_obj}, +#if MICROPY_PY_BUILTINS_FLOAT { MP_OBJ_NEW_QSTR(MP_QSTR_read_core_vbat), (mp_obj_t)&adc_all_read_core_vbat_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_read_core_vref), (mp_obj_t)&adc_all_read_core_vref_obj}, +#endif }; STATIC MP_DEFINE_CONST_DICT(adc_all_locals_dict, adc_all_locals_dict_table); diff --git a/stmhal/modselect.c b/stmhal/modselect.c index bad535f210..a84c94554c 100644 --- a/stmhal/modselect.c +++ b/stmhal/modselect.c @@ -124,10 +124,14 @@ STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) { mp_uint_t timeout = -1; if (n_args == 4) { if (args[3] != mp_const_none) { + #if MICROPY_PY_BUILTINS_FLOAT float timeout_f = mp_obj_get_float(args[3]); if (timeout_f >= 0) { timeout = (mp_uint_t)(timeout_f * 1000); } + #else + timeout = mp_obj_get_int(args[3]) * 1000; + #endif } } diff --git a/stmhal/timer.c b/stmhal/timer.c index 93f66c6314..026d11c815 100644 --- a/stmhal/timer.c +++ b/stmhal/timer.c @@ -268,37 +268,84 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { STATIC const mp_obj_type_t pyb_timer_channel_type; +// This is the largest value that we can multiply by 100 and have the result +// fit in a uint32_t. +#define MAX_PERIOD_DIV_100 42949672 + +// Helper function for determining the period used for calculating percent +STATIC uint32_t compute_period(pyb_timer_obj_t *self) { + // In center mode, compare == period corresponds to 100% + // In edge mode, compare == (period + 1) corresponds to 100% + uint32_t period = (__HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self)); + if (period != 0xffffffff) { + if (self->tim.Init.CounterMode == TIM_COUNTERMODE_UP || + self->tim.Init.CounterMode == TIM_COUNTERMODE_DOWN) { + // Edge mode + period++; + } + } + return period; +} + // Helper function to compute PWM value from timer period and percent value. -// 'val' can be an int or a float between 0 and 100 (out of range values are -// clamped). -STATIC uint32_t compute_pwm_value_from_percent(uint32_t period, mp_obj_t val) { +// 'percent_in' can be an int or a float between 0 and 100 (out of range +// values are clamped). +STATIC uint32_t compute_pwm_value_from_percent(uint32_t period, mp_obj_t percent_in) { uint32_t cmp; if (0) { #if MICROPY_PY_BUILTINS_FLOAT - } else if (MP_OBJ_IS_TYPE(val, &mp_type_float)) { - cmp = mp_obj_get_float(val) / 100.0 * period; + } else if (MP_OBJ_IS_TYPE(percent_in, &mp_type_float)) { + float percent = mp_obj_get_float(percent_in); + if (percent <= 0.0) { + cmp = 0; + } else if (percent >= 100.0) { + cmp = period; + } else { + cmp = percent / 100.0 * ((float)period); + } #endif } else { // For integer arithmetic, if period is large and 100*period will // overflow, then divide period before multiplying by cmp. Otherwise // do it the other way round to retain precision. - // TODO we really need an mp_obj_get_uint_clamped function here so - // that we can get long-int values as large as 0xffffffff. - cmp = mp_obj_get_int(val); - if (period > (1 << 31) / 100) { - cmp = cmp * (period / 100); + mp_int_t percent = mp_obj_get_int(percent_in); + if (percent <= 0) { + cmp = 0; + } else if (percent >= 100) { + cmp = period; + } else if (period > MAX_PERIOD_DIV_100) { + cmp = (uint32_t)percent * (period / 100); } else { - cmp = (cmp * period) / 100; + cmp = ((uint32_t)percent * period) / 100; } } - if (cmp < 0) { - cmp = 0; - } else if (cmp > period) { - cmp = period; - } return cmp; } +// Helper function to compute percentage from timer perion and PWM value. +STATIC mp_obj_t compute_percent_from_pwm_value(uint32_t period, uint32_t cmp) { + #if MICROPY_PY_BUILTINS_FLOAT + float percent; + if (cmp > period) { + percent = 100.0; + } else { + percent = (float)cmp * 100.0 / ((float)period); + } + return mp_obj_new_float(percent); + #else + mp_int_t percent; + if (cmp > period) { + percent = 100; + } else if (period > MAX_PERIOD_DIV_100) { + // We divide the top and bottom by 128, and then do the math. + percent = (cmp / 128) * 100 / (period / 128); + } else { + percent = cmp * 100 / period; + } + return mp_obj_new_int(percent); + #endif +} + STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { pyb_timer_obj_t *self = self_in; @@ -696,13 +743,7 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map oc_config.OCMode = channel_mode_info[chan->mode].oc_mode; if (vals[3].u_obj != mp_const_none) { // pulse width percent given - uint32_t period = (__HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self)) + 1; - // For 32-bit timer, maximum period + 1 will overflow. In that - // case we set the period back to 0xffffffff which will give very - // close to the correct result for the percentage calculation. - if (period == 0) { - period = 0xffffffff; - } + uint32_t period = compute_period(self); oc_config.Pulse = compute_pwm_value_from_percent(period, vals[3].u_obj); } else { // use absolute pulse width value (defaults to 0 if nothing given) @@ -917,6 +958,9 @@ STATIC void pyb_timer_channel_print(void (*print)(void *env, const char *fmt, .. /// Get or set the pulse width value associated with a channel. /// capture, compare, and pulse_width are all aliases for the same function. /// pulse_width is the logical name to use when the channel is in PWM mode. +/// +/// In edge aligned mode, a pulse_width of `period + 1` corresponds to a duty cycle of 100% +/// In center aligned mode, a pulse width of `period` corresponds to a duty cycle of 100% STATIC mp_obj_t pyb_timer_channel_capture_compare(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *self = args[0]; if (n_args == 1) { @@ -938,22 +982,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_capture_compare_obj /// a duty cycle of 25%. STATIC mp_obj_t pyb_timer_channel_pulse_width_percent(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *self = args[0]; - uint32_t period = (__HAL_TIM_GetAutoreload(&self->timer->tim) & TIMER_CNT_MASK(self->timer)) + 1; - // For 32-bit timer, maximum period + 1 will overflow. In that case we set - // the period back to 0xffffffff which will give very close to the correct - // result for the percentage calculation. - if (period == 0) { - period = 0xffffffff; - } + uint32_t period = compute_period(self->timer); if (n_args == 1) { // get uint32_t cmp = __HAL_TIM_GetCompare(&self->timer->tim, TIMER_CHANNEL(self)) & TIMER_CNT_MASK(self->timer); - #if MICROPY_PY_BUILTINS_FLOAT - return mp_obj_new_float((float)cmp / (float)period * 100.0); - #else - // TODO handle overflow of multiplication for 32-bit timer - return mp_obj_new_int(cmp * 100 / period); - #endif + return compute_percent_from_pwm_value(period, cmp); } else { // set uint32_t cmp = compute_pwm_value_from_percent(period, args[1]); diff --git a/teensy/mpconfigport.h b/teensy/mpconfigport.h index 3528101b37..8e2ba4afd9 100644 --- a/teensy/mpconfigport.h +++ b/teensy/mpconfigport.h @@ -8,7 +8,6 @@ #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_FINALISER (1) #define MICROPY_HELPER_REPL (1) -#define MICROPY_PY_BUILTINS_FLOAT (1) #define MICROPY_ENABLE_SOURCE_LINE (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) diff --git a/teensy/timer.c b/teensy/timer.c index 9af1c5151e..d7892039a5 100644 --- a/teensy/timer.c +++ b/teensy/timer.c @@ -128,37 +128,70 @@ mp_uint_t get_prescaler_shift(mp_int_t prescaler) { STATIC const mp_obj_type_t pyb_timer_channel_type; +// Helper function for determining the period used for calculating percent +STATIC uint32_t compute_period(pyb_timer_obj_t *self) { + // In center mode, compare == period corresponds to 100% + // In edge mode, compare == (period + 1) corresponds to 100% + FTM_TypeDef *FTMx = self->ftm.Instance; + uint32_t period = (FTMx->MOD & 0xffff); + if ((FTMx->SC & FTM_SC_CPWMS) == 0) { + // Edge mode + period++; + } + return period; +} + // Helper function to compute PWM value from timer period and percent value. // 'val' can be an int or a float between 0 and 100 (out of range values are // clamped). -STATIC uint32_t compute_pwm_value_from_percent(uint32_t period, mp_obj_t val) { +STATIC uint32_t compute_pwm_value_from_percent(uint32_t period, mp_obj_t percent_in) { uint32_t cmp; if (0) { #if MICROPY_PY_BUILTINS_FLOAT - } else if (MP_OBJ_IS_TYPE(val, &mp_type_float)) { - cmp = mp_obj_get_float(val) / 100.0 * period; + } else if (MP_OBJ_IS_TYPE(percent_in, &mp_type_float)) { + float percent = mp_obj_get_float(percent_in); + if (percent <= 0.0) { + cmp = 0; + } else if (percent >= 100.0) { + cmp = period; + } else { + cmp = percent / 100.0 * ((float)period); + } #endif } else { - // For integer arithmetic, if period is large and 100*period will - // overflow, then divide period before multiplying by cmp. Otherwise - // do it the other way round to retain precision. - // TODO we really need an mp_obj_get_uint_clamped function here so - // that we can get long-int values as large as 0xffffffff. - cmp = mp_obj_get_int(val); - if (period > (1 << 31) / 100) { - cmp = cmp * (period / 100); + mp_int_t percent = mp_obj_get_int(percent_in); + if (percent <= 0) { + cmp = 0; + } else if (percent >= 100) { + cmp = period; } else { - cmp = (cmp * period) / 100; + cmp = ((uint32_t)percent * period) / 100; } } - if (cmp < 0) { - cmp = 0; - } else if (cmp > period) { - cmp = period; - } return cmp; } +// Helper function to compute percentage from timer perion and PWM value. +STATIC mp_obj_t compute_percent_from_pwm_value(uint32_t period, uint32_t cmp) { + #if MICROPY_PY_BUILTINS_FLOAT + float percent = (float)cmp * 100.0 / (float)period; + if (cmp > period) { + percent = 100.0; + } else { + percent = (float)cmp * 100.0 / (float)period; + } + return mp_obj_new_float(percent); + #else + mp_int_t percent; + if (cmp > period) { + percent = 100; + } else { + percent = cmp * 100 / period; + } + return mp_obj_new_int(percent); + #endif +} + STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { pyb_timer_obj_t *self = self_in; @@ -169,7 +202,7 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void self->tim_id, 1 << (self->ftm.Instance->SC & 7), self->ftm.Instance->MOD & 0xffff, - self->ftm.Init.CounterMode == FTM_COUNTERMODE_UP ? "tUP" : "CENTER"); + self->ftm.Init.CounterMode == FTM_COUNTERMODE_UP ? "UP" : "CENTER"); } } @@ -193,7 +226,8 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void /// - `period` [0-0xffff] - Specifies the value to be loaded into the timer's /// Modulo Register (MOD). This determines the period of the timer (i.e. /// when the counter cycles). The timer counter will roll-over after -/// `period + 1` timer clock cycles. +/// `period` timer clock cycles. In center mode, a compare register > 0x7fff +/// doesn't seem to work properly, so keep this in mind. /// /// - `mode` can be one of: /// - `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 uint32_t period = MAX(1, F_BUS / vals[0].u_int); uint32_t prescaler_shift = 0; - while (period > 0x10000 && prescaler_shift < 7) { + while (period > 0xffff && prescaler_shift < 7) { period >>= 1; prescaler_shift++; } - if (period > 0x10000) { - period = 0x10000; + if (period > 0xffff) { + period = 0xffff; } init->PrescalerShift = prescaler_shift; - init->Period = period - 1; + init->Period = period; } else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) { // set prescaler and period directly 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 oc_config.OCMode = channel_mode_info[chan->mode].oc_mode; if (vals[3].u_obj != mp_const_none) { // pulse width ratio given - uint32_t period = (self->ftm.Instance->MOD & 0xffff) + 1; + uint32_t period = compute_period(self); oc_config.Pulse = compute_pwm_value_from_percent(period, vals[3].u_obj); } else { // use absolute pulse width value (defaults to 0 if nothing given) oc_config.Pulse = vals[2].u_int; } - oc_config.OCPolarity = FTM_OCPOLARITY_HIGH; + oc_config.OCPolarity = FTM_OCPOLARITY_HIGH; HAL_FTM_PWM_ConfigChannel(&self->ftm, &oc_config, channel); if (chan->callback == mp_const_none) { @@ -745,6 +779,9 @@ STATIC void pyb_timer_channel_print(void (*print)(void *env, const char *fmt, .. /// Get or set the pulse width value associated with a channel. /// capture, compare, and pulse_width are all aliases for the same function. /// pulse_width is the logical name to use when the channel is in PWM mode. +/// +/// In edge aligned mode, a pulse_width of `period + 1` corresponds to a duty cycle of 100% +/// In center aligned mode, a pulse width of `period` corresponds to a duty cycle of 100% STATIC mp_obj_t pyb_timer_channel_capture_compare(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *self = args[0]; 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 STATIC mp_obj_t pyb_timer_channel_pulse_width_percent(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *self = args[0]; FTM_TypeDef *FTMx = self->timer->ftm.Instance; - uint32_t period = (FTMx->MOD & 0xffff) + 1; + uint32_t period = compute_period(self->timer); if (n_args == 1) { // get uint32_t cmp = FTMx->channel[self->channel].CV & 0xffff; - #if MICROPY_PY_BUILTINS_FLOAT - return mp_obj_new_float((float)cmp / (float)period * 100.0); - #else - return mp_obj_new_int(cmp * 100 / period); - #endif + return compute_percent_from_pwm_value(period, cmp); } else { // set uint32_t cmp = compute_pwm_value_from_percent(period, args[1]);