Merge pull request #877 from dhylands/timer-overflow

Fix timer overflow code.
This commit is contained in:
Damien George 2014-09-29 12:18:48 +01:00
commit bf683e6b32
6 changed files with 145 additions and 68 deletions

View File

@ -309,10 +309,12 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
case UINT64: case UINT64:
case INT64: case INT64:
return mp_obj_new_int_from_ll(((int64_t*)p)[index]); return mp_obj_new_int_from_ll(((int64_t*)p)[index]);
#if MICROPY_PY_BUILTINS_FLOAT
case FLOAT32: case FLOAT32:
return mp_obj_new_float(((float*)p)[index]); return mp_obj_new_float(((float*)p)[index]);
case FLOAT64: case FLOAT64:
return mp_obj_new_float(((double*)p)[index]); return mp_obj_new_float(((double*)p)[index]);
#endif
default: default:
assert(0); assert(0);
return MP_OBJ_NULL; return MP_OBJ_NULL;

View File

@ -349,6 +349,7 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) {
return ((raw_value - CORE_TEMP_V25) / CORE_TEMP_AVG_SLOPE) + 25; return ((raw_value - CORE_TEMP_V25) / CORE_TEMP_AVG_SLOPE) + 25;
} }
#if MICROPY_PY_BUILTINS_FLOAT
float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) { float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) {
uint32_t raw_value = adc_config_and_read_channel(adcHandle, ADC_CHANNEL_VBAT); 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; return raw_value * VBAT_DIV / 4096.0f * 3.3f;
} }
#endif
/******************************************************************************/ /******************************************************************************/
/* Micro Python bindings : adc_all object */ /* 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); 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) { STATIC mp_obj_t adc_all_read_core_vbat(mp_obj_t self_in) {
pyb_adc_all_obj_t *self = self_in; pyb_adc_all_obj_t *self = self_in;
float data = adc_read_core_vbat(&self->handle); 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); return mp_obj_new_float(data);
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_vref_obj, adc_all_read_core_vref); 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[] = { 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_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}, { 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_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}, { 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); STATIC MP_DEFINE_CONST_DICT(adc_all_locals_dict, adc_all_locals_dict_table);

View File

@ -124,10 +124,14 @@ STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) {
mp_uint_t timeout = -1; mp_uint_t timeout = -1;
if (n_args == 4) { if (n_args == 4) {
if (args[3] != mp_const_none) { if (args[3] != mp_const_none) {
#if MICROPY_PY_BUILTINS_FLOAT
float timeout_f = mp_obj_get_float(args[3]); float timeout_f = mp_obj_get_float(args[3]);
if (timeout_f >= 0) { if (timeout_f >= 0) {
timeout = (mp_uint_t)(timeout_f * 1000); timeout = (mp_uint_t)(timeout_f * 1000);
} }
#else
timeout = mp_obj_get_int(args[3]) * 1000;
#endif
} }
} }

View File

@ -268,37 +268,84 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
STATIC const mp_obj_type_t pyb_timer_channel_type; 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. // 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 // 'percent_in' can be an int or a float between 0 and 100 (out of range
// clamped). // 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; uint32_t cmp;
if (0) { if (0) {
#if MICROPY_PY_BUILTINS_FLOAT #if MICROPY_PY_BUILTINS_FLOAT
} else if (MP_OBJ_IS_TYPE(val, &mp_type_float)) { } else if (MP_OBJ_IS_TYPE(percent_in, &mp_type_float)) {
cmp = mp_obj_get_float(val) / 100.0 * period; 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 #endif
} else { } else {
// For integer arithmetic, if period is large and 100*period will // For integer arithmetic, if period is large and 100*period will
// overflow, then divide period before multiplying by cmp. Otherwise // overflow, then divide period before multiplying by cmp. Otherwise
// do it the other way round to retain precision. // do it the other way round to retain precision.
// TODO we really need an mp_obj_get_uint_clamped function here so mp_int_t percent = mp_obj_get_int(percent_in);
// that we can get long-int values as large as 0xffffffff. if (percent <= 0) {
cmp = mp_obj_get_int(val); cmp = 0;
if (period > (1 << 31) / 100) { } else if (percent >= 100) {
cmp = cmp * (period / 100); cmp = period;
} else if (period > MAX_PERIOD_DIV_100) {
cmp = (uint32_t)percent * (period / 100);
} else { } else {
cmp = (cmp * period) / 100; cmp = ((uint32_t)percent * period) / 100;
} }
} }
if (cmp < 0) {
cmp = 0;
} else if (cmp > period) {
cmp = period;
}
return cmp; 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) { 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; 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; oc_config.OCMode = channel_mode_info[chan->mode].oc_mode;
if (vals[3].u_obj != mp_const_none) { if (vals[3].u_obj != mp_const_none) {
// pulse width percent given // pulse width percent given
uint32_t period = (__HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self)) + 1; uint32_t period = compute_period(self);
// 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;
}
oc_config.Pulse = compute_pwm_value_from_percent(period, vals[3].u_obj); oc_config.Pulse = compute_pwm_value_from_percent(period, vals[3].u_obj);
} else { } else {
// use absolute pulse width value (defaults to 0 if nothing given) // 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. /// Get or set the pulse width value associated with a channel.
/// capture, compare, and pulse_width are all aliases for the same function. /// 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. /// 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) { 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]; pyb_timer_channel_obj_t *self = args[0];
if (n_args == 1) { 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%. /// 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) { 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]; pyb_timer_channel_obj_t *self = args[0];
uint32_t period = (__HAL_TIM_GetAutoreload(&self->timer->tim) & TIMER_CNT_MASK(self->timer)) + 1; uint32_t period = compute_period(self->timer);
// 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;
}
if (n_args == 1) { if (n_args == 1) {
// get // get
uint32_t cmp = __HAL_TIM_GetCompare(&self->timer->tim, TIMER_CHANNEL(self)) & TIMER_CNT_MASK(self->timer); uint32_t cmp = __HAL_TIM_GetCompare(&self->timer->tim, TIMER_CHANNEL(self)) & TIMER_CNT_MASK(self->timer);
#if MICROPY_PY_BUILTINS_FLOAT return compute_percent_from_pwm_value(period, cmp);
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
} else { } else {
// set // set
uint32_t cmp = compute_pwm_value_from_percent(period, args[1]); uint32_t cmp = compute_pwm_value_from_percent(period, args[1]);

View File

@ -8,7 +8,6 @@
#define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_GC (1)
#define MICROPY_ENABLE_FINALISER (1) #define MICROPY_ENABLE_FINALISER (1)
#define MICROPY_HELPER_REPL (1) #define MICROPY_HELPER_REPL (1)
#define MICROPY_PY_BUILTINS_FLOAT (1)
#define MICROPY_ENABLE_SOURCE_LINE (1) #define MICROPY_ENABLE_SOURCE_LINE (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)

View File

@ -128,37 +128,70 @@ mp_uint_t get_prescaler_shift(mp_int_t prescaler) {
STATIC const mp_obj_type_t pyb_timer_channel_type; 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. // 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 // 'val' can be an int or a float between 0 and 100 (out of range values are
// clamped). // 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; uint32_t cmp;
if (0) { if (0) {
#if MICROPY_PY_BUILTINS_FLOAT #if MICROPY_PY_BUILTINS_FLOAT
} else if (MP_OBJ_IS_TYPE(val, &mp_type_float)) { } else if (MP_OBJ_IS_TYPE(percent_in, &mp_type_float)) {
cmp = mp_obj_get_float(val) / 100.0 * period; 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 #endif
} else { } else {
// For integer arithmetic, if period is large and 100*period will mp_int_t percent = mp_obj_get_int(percent_in);
// overflow, then divide period before multiplying by cmp. Otherwise if (percent <= 0) {
// do it the other way round to retain precision. cmp = 0;
// TODO we really need an mp_obj_get_uint_clamped function here so } else if (percent >= 100) {
// that we can get long-int values as large as 0xffffffff. cmp = period;
cmp = mp_obj_get_int(val);
if (period > (1 << 31) / 100) {
cmp = cmp * (period / 100);
} else { } else {
cmp = (cmp * period) / 100; cmp = ((uint32_t)percent * period) / 100;
} }
} }
if (cmp < 0) {
cmp = 0;
} else if (cmp > period) {
cmp = period;
}
return cmp; 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) { 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; 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, self->tim_id,
1 << (self->ftm.Instance->SC & 7), 1 << (self->ftm.Instance->SC & 7),
self->ftm.Instance->MOD & 0xffff, 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 /// - `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. /// Modulo Register (MOD). This determines the period of the timer (i.e.
/// when the counter cycles). The timer counter will roll-over after /// 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: /// - `mode` can be one of:
/// - `Timer.UP` - configures the timer to count from 0 to MOD (default) /// - `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 period = MAX(1, F_BUS / vals[0].u_int);
uint32_t prescaler_shift = 0; uint32_t prescaler_shift = 0;
while (period > 0x10000 && prescaler_shift < 7) { while (period > 0xffff && prescaler_shift < 7) {
period >>= 1; period >>= 1;
prescaler_shift++; prescaler_shift++;
} }
if (period > 0x10000) { if (period > 0xffff) {
period = 0x10000; period = 0xffff;
} }
init->PrescalerShift = prescaler_shift; init->PrescalerShift = prescaler_shift;
init->Period = period - 1; init->Period = period;
} else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) { } else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) {
// set prescaler and period directly // set prescaler and period directly
init->PrescalerShift = get_prescaler_shift(vals[1].u_int); 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; oc_config.OCMode = channel_mode_info[chan->mode].oc_mode;
if (vals[3].u_obj != mp_const_none) { if (vals[3].u_obj != mp_const_none) {
// pulse width ratio given // 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); oc_config.Pulse = compute_pwm_value_from_percent(period, vals[3].u_obj);
} else { } else {
// use absolute pulse width value (defaults to 0 if nothing given) // use absolute pulse width value (defaults to 0 if nothing given)
oc_config.Pulse = vals[2].u_int; 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); HAL_FTM_PWM_ConfigChannel(&self->ftm, &oc_config, channel);
if (chan->callback == mp_const_none) { 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. /// Get or set the pulse width value associated with a channel.
/// capture, compare, and pulse_width are all aliases for the same function. /// 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. /// 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) { 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]; pyb_timer_channel_obj_t *self = args[0];
FTM_TypeDef *FTMx = self->timer->ftm.Instance; 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) { 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]; pyb_timer_channel_obj_t *self = args[0];
FTM_TypeDef *FTMx = self->timer->ftm.Instance; 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) { if (n_args == 1) {
// get // get
uint32_t cmp = FTMx->channel[self->channel].CV & 0xffff; uint32_t cmp = FTMx->channel[self->channel].CV & 0xffff;
#if MICROPY_PY_BUILTINS_FLOAT return compute_percent_from_pwm_value(period, cmp);
return mp_obj_new_float((float)cmp / (float)period * 100.0);
#else
return mp_obj_new_int(cmp * 100 / period);
#endif
} else { } else {
// set // set
uint32_t cmp = compute_pwm_value_from_percent(period, args[1]); uint32_t cmp = compute_pwm_value_from_percent(period, args[1]);