Skip to content

Commit 97ef94d

Browse files
committed
stmhal, timer: Set freq from float; get timer source freq.
Timers now have the following new features: - can init freq using floating point; eg tim.init(freq=0.1) - tim.source_freq() added to get freq of timer clock source - tim.freq() added to get/set freq - print(tim) now prints freq
1 parent c3ab90d commit 97ef94d

2 files changed

Lines changed: 135 additions & 64 deletions

File tree

stmhal/qstrdefsport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ Q(init)
166166
Q(deinit)
167167
Q(channel)
168168
Q(counter)
169+
Q(source_freq)
169170
Q(prescaler)
170171
Q(period)
171172
Q(callback)

stmhal/timer.c

Lines changed: 134 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ typedef struct _pyb_timer_obj_t {
133133
TIM_HandleTypeDef tim;
134134
IRQn_Type irqn;
135135
pyb_timer_channel_obj_t *channel;
136-
137136
} pyb_timer_obj_t;
138137

139138
// The following yields TIM_IT_UPDATE when channel is zero and
@@ -153,6 +152,7 @@ STATIC uint32_t tim3_counter = 0;
153152
STATIC pyb_timer_obj_t *pyb_timer_obj_all[14];
154153
#define PYB_TIMER_OBJ_ALL_NUM MP_ARRAY_SIZE(pyb_timer_obj_all)
155154

155+
STATIC uint32_t timer_get_source_freq(uint32_t tim_id);
156156
STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in);
157157
STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback);
158158
STATIC mp_obj_t pyb_timer_channel_callback(mp_obj_t self_in, mp_obj_t callback);
@@ -181,7 +181,7 @@ void timer_tim3_init(void) {
181181

182182
TIM3_Handle.Instance = TIM3;
183183
TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1; // TIM3 fires every USBD_CDC_POLLING_INTERVAL ms
184-
TIM3_Handle.Init.Prescaler = 2 * HAL_RCC_GetPCLK1Freq() / 1000000 - 1; // TIM3 runs at 1MHz
184+
TIM3_Handle.Init.Prescaler = timer_get_source_freq(3) / 1000000 - 1; // TIM3 runs at 1MHz
185185
TIM3_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
186186
TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
187187
HAL_TIM_Base_Init(&TIM3_Handle);
@@ -215,7 +215,7 @@ void timer_tim5_init(void) {
215215
// PWM clock configuration
216216
TIM5_Handle.Instance = TIM5;
217217
TIM5_Handle.Init.Period = 2000 - 1; // timer cycles at 50Hz
218-
TIM5_Handle.Init.Prescaler = ((SystemCoreClock / 2) / 100000) - 1; // timer runs at 100kHz
218+
TIM5_Handle.Init.Prescaler = (timer_get_source_freq(5) / 100000) - 1; // timer runs at 100kHz
219219
TIM5_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
220220
TIM5_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
221221

@@ -231,7 +231,7 @@ void timer_tim6_init(uint freq) {
231231

232232
// Timer runs at SystemCoreClock / 2
233233
// Compute the prescaler value so TIM6 triggers at freq-Hz
234-
uint32_t period = MAX(1, (SystemCoreClock / 2) / freq);
234+
uint32_t period = MAX(1, timer_get_source_freq(6) / freq);
235235
uint32_t prescaler = 1;
236236
while (period > 0xffff) {
237237
period >>= 1;
@@ -263,6 +263,29 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
263263
}
264264
}
265265

266+
// Get the frequency (in Hz) of the source clock for the given timer.
267+
// On STM32F405/407/415/417 there are 2 cases for how the clock freq is set.
268+
// If the APB prescaler is 1, then the timer clock is equal to its respective
269+
// APB clock. Otherwise (APB prescaler > 1) the timer clock is twice its
270+
// respective APB clock. See DM00031020 Rev 4, page 115.
271+
STATIC uint32_t timer_get_source_freq(uint32_t tim_id) {
272+
uint32_t source;
273+
if (tim_id == 1 || (8 <= tim_id && tim_id <= 11)) {
274+
// TIM{1,8,9,10,11} are on APB2
275+
source = HAL_RCC_GetPCLK2Freq();
276+
if ((uint32_t)((RCC->CFGR & RCC_CFGR_PPRE2) >> 3) != RCC_HCLK_DIV1) {
277+
source *= 2;
278+
}
279+
} else {
280+
// TIM{2,3,4,5,6,7,12,13,14} are on APB1
281+
source = HAL_RCC_GetPCLK1Freq();
282+
if ((uint32_t)(RCC->CFGR & RCC_CFGR_PPRE1) != RCC_HCLK_DIV1) {
283+
source *= 2;
284+
}
285+
}
286+
return source;
287+
}
288+
266289
/******************************************************************************/
267290
/* Micro Python bindings */
268291

@@ -272,6 +295,37 @@ STATIC const mp_obj_type_t pyb_timer_channel_type;
272295
// fit in a uint32_t.
273296
#define MAX_PERIOD_DIV_100 42949672
274297

298+
// computes prescaler and period so TIM triggers at freq-Hz
299+
STATIC uint32_t compute_prescaler_period_from_freq(pyb_timer_obj_t *self, mp_obj_t freq_in, uint32_t *period_out) {
300+
uint32_t source_freq = timer_get_source_freq(self->tim_id);
301+
uint32_t prescaler = 1;
302+
uint32_t period;
303+
if (0) {
304+
#if MICROPY_PY_BUILTINS_FLOAT
305+
} else if (MP_OBJ_IS_TYPE(freq_in, &mp_type_float)) {
306+
float freq = mp_obj_get_float(freq_in);
307+
if (freq <= 0) {
308+
goto bad_freq;
309+
}
310+
period = MAX(1, source_freq / freq);
311+
#endif
312+
} else {
313+
mp_int_t freq = mp_obj_get_int(freq_in);
314+
if (freq <= 0) {
315+
goto bad_freq;
316+
bad_freq:
317+
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "must have positive freq"));
318+
}
319+
period = MAX(1, source_freq / freq);
320+
}
321+
while (period > TIMER_CNT_MASK(self)) {
322+
prescaler <<= 1;
323+
period >>= 1;
324+
}
325+
*period_out = (period - 1) & TIMER_CNT_MASK(self);
326+
return (prescaler - 1) & 0xffff;
327+
}
328+
275329
// Helper function for determining the period used for calculating percent
276330
STATIC uint32_t compute_period(pyb_timer_obj_t *self) {
277331
// In center mode, compare == period corresponds to 100%
@@ -351,10 +405,15 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void
351405
if (self->tim.State == HAL_TIM_STATE_RESET) {
352406
print(env, "Timer(%u)", self->tim_id);
353407
} else {
354-
print(env, "Timer(%u, prescaler=%u, period=%u, mode=%s, div=%u)",
408+
uint32_t prescaler = self->tim.Instance->PSC & 0xffff;
409+
uint32_t period = __HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self);
410+
// for efficiency, we compute and print freq as an int (not a float)
411+
uint32_t freq = timer_get_source_freq(self->tim_id) / ((prescaler + 1) * (period + 1));
412+
print(env, "Timer(%u, freq=%u, prescaler=%u, period=%u, mode=%s, div=%u)",
355413
self->tim_id,
356-
self->tim.Instance->PSC & 0xffff,
357-
__HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self),
414+
freq,
415+
prescaler,
416+
period,
358417
self->tim.Init.CounterMode == TIM_COUNTERMODE_UP ? "UP" :
359418
self->tim.Init.CounterMode == TIM_COUNTERMODE_DOWN ? "DOWN" : "CENTER",
360419
self->tim.Init.ClockDivision == TIM_CLOCKDIVISION_DIV4 ? 4 :
@@ -399,74 +458,46 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void
399458
/// - `callback` - as per Timer.callback()
400459
///
401460
/// You must either specify freq or both of period and prescaler.
402-
STATIC const mp_arg_t pyb_timer_init_args[] = {
403-
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
404-
{ MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
405-
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
406-
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIM_COUNTERMODE_UP} },
407-
{ MP_QSTR_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
408-
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
409-
};
410-
#define PYB_TIMER_INIT_NUM_ARGS MP_ARRAY_SIZE(pyb_timer_init_args)
461+
STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
462+
static const mp_arg_t allowed_args[] = {
463+
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
464+
{ MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
465+
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
466+
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIM_COUNTERMODE_UP} },
467+
{ MP_QSTR_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
468+
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
469+
};
411470

412-
STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
413471
// parse args
414-
mp_arg_val_t vals[PYB_TIMER_INIT_NUM_ARGS];
415-
mp_arg_parse_all(n_args, args, kw_args, PYB_TIMER_INIT_NUM_ARGS, pyb_timer_init_args, vals);
472+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
473+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
416474

417475
// set the TIM configuration values
418476
TIM_Base_InitTypeDef *init = &self->tim.Init;
419477

420-
if (vals[0].u_int != 0xffffffff) {
421-
// set prescaler and period from frequency
422-
423-
if (vals[0].u_int == 0) {
424-
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't have 0 frequency"));
425-
}
426-
427-
// work out TIM's clock source
428-
uint tim_clock;
429-
if (self->tim_id == 1 || (8 <= self->tim_id && self->tim_id <= 11)) {
430-
// TIM{1,8,9,10,11} are on APB2
431-
tim_clock = HAL_RCC_GetPCLK2Freq();
432-
} else {
433-
// TIM{2,3,4,5,6,7,12,13,14} are on APB1
434-
tim_clock = HAL_RCC_GetPCLK1Freq();
435-
}
436-
437-
// Compute the prescaler value so TIM triggers at freq-Hz
438-
// On STM32F405/407/415/417 there are 2 cases for how the clock freq is set.
439-
// If the APB prescaler is 1, then the timer clock is equal to its respective
440-
// APB clock. Otherwise (APB prescaler > 1) the timer clock is twice its
441-
// respective APB clock. See DM00031020 Rev 4, page 115.
442-
uint32_t period = MAX(1, 2 * tim_clock / vals[0].u_int);
443-
uint32_t prescaler = 1;
444-
while (period > TIMER_CNT_MASK(self)) {
445-
period >>= 1;
446-
prescaler <<= 1;
447-
}
448-
init->Prescaler = prescaler - 1;
449-
init->Period = period - 1;
450-
} else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) {
478+
if (args[0].u_obj != mp_const_none) {
479+
// set prescaler and period from desired frequency
480+
init->Prescaler = compute_prescaler_period_from_freq(self, args[0].u_obj, &init->Period);
481+
} else if (args[1].u_int != 0xffffffff && args[2].u_int != 0xffffffff) {
451482
// set prescaler and period directly
452-
init->Prescaler = vals[1].u_int;
453-
init->Period = vals[2].u_int;
483+
init->Prescaler = args[1].u_int;
484+
init->Period = args[2].u_int;
454485
} else {
455486
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "must specify either freq, or prescaler and period"));
456487
}
457488

458-
init->CounterMode = vals[3].u_int;
489+
init->CounterMode = args[3].u_int;
490+
if (!IS_TIM_COUNTER_MODE(init->CounterMode)) {
491+
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid mode (%d)", init->CounterMode));
492+
}
459493

460-
init->ClockDivision = vals[4].u_int == 2 ? TIM_CLOCKDIVISION_DIV2 :
461-
vals[4].u_int == 4 ? TIM_CLOCKDIVISION_DIV4 :
494+
init->ClockDivision = args[4].u_int == 2 ? TIM_CLOCKDIVISION_DIV2 :
495+
args[4].u_int == 4 ? TIM_CLOCKDIVISION_DIV4 :
462496
TIM_CLOCKDIVISION_DIV1;
463-
init->RepetitionCounter = 0;
464497

465-
if (!IS_TIM_COUNTER_MODE(init->CounterMode)) {
466-
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid counter_mode (%d)", init->CounterMode));
467-
}
498+
init->RepetitionCounter = 0;
468499

469-
// init the TIM peripheral
500+
// enable TIM clock
470501
switch (self->tim_id) {
471502
case 1: __TIM1_CLK_ENABLE(); break;
472503
case 2: __TIM2_CLK_ENABLE(); break;
@@ -483,16 +514,18 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, mp_uint_t n_args, c
483514
case 13: __TIM13_CLK_ENABLE(); break;
484515
case 14: __TIM14_CLK_ENABLE(); break;
485516
}
486-
// set the priority (if not a special timer)
517+
518+
// set IRQ priority (if not a special timer)
487519
if (self->tim_id != 3 && self->tim_id != 5) {
488520
HAL_NVIC_SetPriority(self->irqn, 0xe, 0xe); // next-to lowest priority
489521
}
490522

523+
// init TIM
491524
HAL_TIM_Base_Init(&self->tim);
492-
if (vals[5].u_obj == mp_const_none) {
525+
if (args[5].u_obj == mp_const_none) {
493526
HAL_TIM_Base_Start(&self->tim);
494527
} else {
495-
pyb_timer_callback(self, vals[5].u_obj);
528+
pyb_timer_callback(self, args[5].u_obj);
496529
}
497530

498531
return mp_const_none;
@@ -839,6 +872,41 @@ STATIC mp_obj_t pyb_timer_counter(mp_uint_t n_args, const mp_obj_t *args) {
839872
}
840873
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_counter_obj, 1, 2, pyb_timer_counter);
841874

875+
/// \method source_freq()
876+
/// Get the frequency of the source of the timer.
877+
STATIC mp_obj_t pyb_timer_source_freq(mp_obj_t self_in) {
878+
pyb_timer_obj_t *self = self_in;
879+
uint32_t source_freq = timer_get_source_freq(self->tim_id);
880+
return mp_obj_new_int(source_freq);
881+
}
882+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_source_freq_obj, pyb_timer_source_freq);
883+
884+
/// \method freq([value])
885+
/// Get or set the frequency for the timer (changes prescaler and period if set).
886+
STATIC mp_obj_t pyb_timer_freq(mp_uint_t n_args, const mp_obj_t *args) {
887+
pyb_timer_obj_t *self = args[0];
888+
if (n_args == 1) {
889+
// get
890+
uint32_t prescaler = self->tim.Instance->PSC & 0xffff;
891+
uint32_t period = __HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self);
892+
uint32_t source_freq = timer_get_source_freq(self->tim_id);
893+
uint32_t divide = ((prescaler + 1) * (period + 1));
894+
if (source_freq % divide == 0) {
895+
return mp_obj_new_int(source_freq / divide);
896+
} else {
897+
return mp_obj_new_float((float)source_freq / (float)divide);
898+
}
899+
} else {
900+
// set
901+
uint32_t period;
902+
uint32_t prescaler = compute_prescaler_period_from_freq(self, args[1], &period);
903+
self->tim.Instance->PSC = prescaler;
904+
__HAL_TIM_SetAutoreload(&self->tim, period);
905+
return mp_const_none;
906+
}
907+
}
908+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_freq_obj, 1, 2, pyb_timer_freq);
909+
842910
/// \method prescaler([value])
843911
/// Get or set the prescaler for the timer.
844912
STATIC mp_obj_t pyb_timer_prescaler(mp_uint_t n_args, const mp_obj_t *args) {
@@ -848,7 +916,7 @@ STATIC mp_obj_t pyb_timer_prescaler(mp_uint_t n_args, const mp_obj_t *args) {
848916
return mp_obj_new_int(self->tim.Instance->PSC & 0xffff);
849917
} else {
850918
// set
851-
self->tim.Init.Prescaler = self->tim.Instance->PSC = mp_obj_get_int(args[1]) & 0xffff;
919+
self->tim.Instance->PSC = mp_obj_get_int(args[1]) & 0xffff;
852920
return mp_const_none;
853921
}
854922
}
@@ -897,6 +965,8 @@ STATIC const mp_map_elem_t pyb_timer_locals_dict_table[] = {
897965
{ MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_timer_deinit_obj },
898966
{ MP_OBJ_NEW_QSTR(MP_QSTR_channel), (mp_obj_t)&pyb_timer_channel_obj },
899967
{ MP_OBJ_NEW_QSTR(MP_QSTR_counter), (mp_obj_t)&pyb_timer_counter_obj },
968+
{ MP_OBJ_NEW_QSTR(MP_QSTR_source_freq), (mp_obj_t)&pyb_timer_source_freq_obj },
969+
{ MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_timer_freq_obj },
900970
{ MP_OBJ_NEW_QSTR(MP_QSTR_prescaler), (mp_obj_t)&pyb_timer_prescaler_obj },
901971
{ MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_period_obj },
902972
{ MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_callback_obj },

0 commit comments

Comments
 (0)