stm32/timer: Add tick_hz arg to Timer constructor and init method.

The period of the timer can now be specified using the "period" and
"tick_hz" args.  The period in seconds will be: period/tick_hz.  tick_hz
defaults to 1000, so if period is specified on its own then it will be in
units of milliseconds.
This commit is contained in:
Damien George 2018-07-04 11:56:36 +10:00
parent 821b59d439
commit 46091b8a95
1 changed files with 40 additions and 2 deletions

View File

@ -314,6 +314,40 @@ STATIC uint32_t compute_prescaler_period_from_freq(pyb_timer_obj_t *self, mp_obj
return (prescaler - 1) & 0xffff; return (prescaler - 1) & 0xffff;
} }
// computes prescaler and period so TIM triggers with a period of t_num/t_den seconds
STATIC uint32_t compute_prescaler_period_from_t(pyb_timer_obj_t *self, int32_t t_num, int32_t t_den, uint32_t *period_out) {
uint32_t source_freq = timer_get_source_freq(self->tim_id);
if (t_num <= 0 || t_den <= 0) {
mp_raise_ValueError("must have positive freq");
}
uint64_t period = (uint64_t)source_freq * (uint64_t)t_num / (uint64_t)t_den;
uint32_t prescaler = 1;
while (period > TIMER_CNT_MASK(self)) {
// if we can divide exactly, and without prescaler overflow, do that first
if (prescaler <= 13107 && period % 5 == 0) {
prescaler *= 5;
period /= 5;
} else if (prescaler <= 21845 && period % 3 == 0) {
prescaler *= 3;
period /= 3;
} else {
// may not divide exactly, but loses minimal precision
uint32_t period_lsb = period & 1;
prescaler <<= 1;
period >>= 1;
if (period < prescaler) {
// round division up
prescaler |= period_lsb;
}
if (prescaler > 0x10000) {
mp_raise_ValueError("period too large");
}
}
}
*period_out = (period - 1) & TIMER_CNT_MASK(self);
return (prescaler - 1) & 0xffff;
}
// Helper function for determining the period used for calculating percent // Helper function for determining the period used for calculating percent
STATIC uint32_t compute_period(pyb_timer_obj_t *self) { STATIC uint32_t compute_period(pyb_timer_obj_t *self) {
// In center mode, compare == period corresponds to 100% // In center mode, compare == period corresponds to 100%
@ -529,11 +563,12 @@ STATIC void pyb_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_
/// ///
/// You must either specify freq or both of period and prescaler. /// You must either specify freq or both of period and prescaler.
STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_freq, ARG_prescaler, ARG_period, ARG_mode, ARG_div, ARG_callback, ARG_deadtime }; enum { ARG_freq, ARG_prescaler, ARG_period, ARG_tick_hz, ARG_mode, ARG_div, ARG_callback, ARG_deadtime };
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
{ MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
{ MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIM_COUNTERMODE_UP} }, { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIM_COUNTERMODE_UP} },
{ MP_QSTR_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
@ -554,8 +589,11 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, size_t n_args, cons
// set prescaler and period directly // set prescaler and period directly
init->Prescaler = args[ARG_prescaler].u_int; init->Prescaler = args[ARG_prescaler].u_int;
init->Period = args[ARG_period].u_int; init->Period = args[ARG_period].u_int;
} else if (args[ARG_period].u_int != 0xffffffff) {
// set prescaler and period from desired period and tick_hz scale
init->Prescaler = compute_prescaler_period_from_t(self, args[ARG_period].u_int, args[ARG_tick_hz].u_int, &init->Period);
} else { } else {
mp_raise_TypeError("must specify either freq, or prescaler and period"); mp_raise_TypeError("must specify either freq, period, or prescaler and period");
} }
init->CounterMode = args[ARG_mode].u_int; init->CounterMode = args[ARG_mode].u_int;