stmhal: Add pulse_width_ratio to timer channel object.
This allows to set the pulse width (for PWM mode) as a ratio relative to the period of the timer. Eg, 0.5 is a 50% duty cycle. You can set the ratio in the channel init, or using channel.pulse_width_ratio; the latter can also read the pulse width as a ratio.
This commit is contained in:
parent
becbc87fd7
commit
0e58c5810d
@ -175,6 +175,7 @@ Q(BOTH)
|
||||
// for TimerChannel class
|
||||
Q(TimerChannel)
|
||||
Q(pulse_width)
|
||||
Q(pulse_width_ratio)
|
||||
Q(compare)
|
||||
Q(capture)
|
||||
Q(polarity)
|
||||
|
111
stmhal/timer.c
111
stmhal/timer.c
@ -104,7 +104,7 @@ typedef enum {
|
||||
STATIC const struct {
|
||||
qstr name;
|
||||
uint32_t oc_mode;
|
||||
} gChannelMode[] = {
|
||||
} channel_mode_info[] = {
|
||||
{ MP_QSTR_PWM, TIM_OCMODE_PWM1 },
|
||||
{ MP_QSTR_PWM_INVERTED, TIM_OCMODE_PWM2 },
|
||||
{ MP_QSTR_OC_TIMING, TIM_OCMODE_TIMING },
|
||||
@ -147,7 +147,7 @@ TIM_HandleTypeDef TIM5_Handle;
|
||||
TIM_HandleTypeDef TIM6_Handle;
|
||||
|
||||
// Used to divide down TIM3 and periodically call the flash storage IRQ
|
||||
static uint32_t tim3_counter = 0;
|
||||
STATIC uint32_t tim3_counter = 0;
|
||||
|
||||
// Used to do callbacks to Python code on interrupt
|
||||
STATIC pyb_timer_obj_t *pyb_timer_obj_all[14];
|
||||
@ -266,6 +266,8 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
|
||||
/******************************************************************************/
|
||||
/* Micro Python bindings */
|
||||
|
||||
STATIC const mp_obj_type_t pyb_timer_channel_type;
|
||||
|
||||
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;
|
||||
|
||||
@ -507,8 +509,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit);
|
||||
|
||||
/// \method channel(channel, mode, ...)
|
||||
///
|
||||
/// If only a channel nunber is passed, then a previously initialized channel
|
||||
/// object is returned.
|
||||
/// If only a channel number is passed, then a previously initialized channel
|
||||
/// object is returned (or `None` if there is no previous channel).
|
||||
///
|
||||
/// Othwerwise, a TimerChannel object is initialized and returned.
|
||||
///
|
||||
@ -540,7 +542,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit);
|
||||
///
|
||||
/// Keyword arguments for Timer.PWM modes:
|
||||
///
|
||||
/// - 'pulse_width' - determines the initial pulse width to use.
|
||||
/// - `pulse_width` - determines the initial pulse width value to use.
|
||||
/// - `pulse_width_ratio` - determines the initial pulse width ratio to use.
|
||||
///
|
||||
/// Keyword arguments for Timer.OC modes:
|
||||
///
|
||||
@ -563,17 +566,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit);
|
||||
/// ch2 = timer.channel(2, pyb.Timer.PWM, pin=pyb.Pin.board.X2, pulse_width=210000)
|
||||
/// ch3 = timer.channel(3, pyb.Timer.PWM, pin=pyb.Pin.board.X3, pulse_width=420000)
|
||||
STATIC const mp_arg_t pyb_timer_channel_args[] = {
|
||||
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_pulse_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_compare, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
|
||||
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_pulse_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
|
||||
{ MP_QSTR_pulse_width_ratio, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_compare, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
|
||||
};
|
||||
#define PYB_TIMER_CHANNEL_NUM_ARGS MP_ARRAY_SIZE(pyb_timer_channel_args)
|
||||
|
||||
STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
mp_arg_check_num(n_args, n_args - 3, 3, MP_OBJ_FUN_ARGS_MAX, true);
|
||||
|
||||
pyb_timer_obj_t *self = args[0];
|
||||
mp_int_t channel = mp_obj_get_int(args[1]);
|
||||
|
||||
@ -591,8 +593,10 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map
|
||||
prev_chan = chan;
|
||||
chan = chan->next;
|
||||
}
|
||||
if (kw_args->used == 0) {
|
||||
// Return the previously allocated channel
|
||||
|
||||
// If only the channel number is given return the previously allocated
|
||||
// channel (or None if no previous channel).
|
||||
if (n_args == 2) {
|
||||
if (chan) {
|
||||
return chan;
|
||||
}
|
||||
@ -658,8 +662,24 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map
|
||||
case CHANNEL_MODE_PWM_NORMAL:
|
||||
case CHANNEL_MODE_PWM_INVERTED: {
|
||||
TIM_OC_InitTypeDef oc_config;
|
||||
oc_config.OCMode = gChannelMode[chan->mode].oc_mode;
|
||||
oc_config.Pulse = vals[2].u_int;
|
||||
oc_config.OCMode = channel_mode_info[chan->mode].oc_mode;
|
||||
if (vals[2].u_int != 0xffffffff) {
|
||||
// absolute pulse width value given
|
||||
oc_config.Pulse = vals[2].u_int;
|
||||
} else if (vals[3].u_obj != mp_const_none) {
|
||||
// pulse width ratio given
|
||||
uint32_t period = (__HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self)) + 1;
|
||||
uint32_t cmp = mp_obj_get_float(vals[3].u_obj) * period;
|
||||
if (cmp < 0) {
|
||||
cmp = 0;
|
||||
} else if (cmp > period) {
|
||||
cmp = period;
|
||||
}
|
||||
oc_config.Pulse = cmp;
|
||||
} else {
|
||||
// nothing given, default to pulse width of 0
|
||||
oc_config.Pulse = 0;
|
||||
}
|
||||
oc_config.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
oc_config.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
oc_config.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
@ -682,9 +702,9 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map
|
||||
case CHANNEL_MODE_OC_FORCED_ACTIVE:
|
||||
case CHANNEL_MODE_OC_FORCED_INACTIVE: {
|
||||
TIM_OC_InitTypeDef oc_config;
|
||||
oc_config.OCMode = gChannelMode[chan->mode].oc_mode;
|
||||
oc_config.Pulse = vals[3].u_int;
|
||||
oc_config.OCPolarity = vals[4].u_int;
|
||||
oc_config.OCMode = channel_mode_info[chan->mode].oc_mode;
|
||||
oc_config.Pulse = vals[4].u_int;
|
||||
oc_config.OCPolarity = vals[5].u_int;
|
||||
if (oc_config.OCPolarity == 0xffffffff) {
|
||||
oc_config.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
}
|
||||
@ -708,7 +728,7 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map
|
||||
case CHANNEL_MODE_IC: {
|
||||
TIM_IC_InitTypeDef ic_config;
|
||||
|
||||
ic_config.ICPolarity = vals[4].u_int;
|
||||
ic_config.ICPolarity = vals[5].u_int;
|
||||
if (ic_config.ICPolarity == 0xffffffff) {
|
||||
ic_config.ICPolarity = TIM_ICPOLARITY_RISING;
|
||||
}
|
||||
@ -734,11 +754,11 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map
|
||||
|
||||
return chan;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_channel_obj, 3, pyb_timer_channel);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_channel_obj, 2, pyb_timer_channel);
|
||||
|
||||
/// \method counter([value])
|
||||
/// Get or set the timer counter.
|
||||
mp_obj_t pyb_timer_counter(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
STATIC mp_obj_t pyb_timer_counter(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_obj_t *self = args[0];
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
@ -753,7 +773,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_counter_obj, 1, 2, pyb_time
|
||||
|
||||
/// \method prescaler([value])
|
||||
/// Get or set the prescaler for the timer.
|
||||
mp_obj_t pyb_timer_prescaler(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
STATIC mp_obj_t pyb_timer_prescaler(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_obj_t *self = args[0];
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
@ -768,7 +788,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_prescaler_obj, 1, 2, pyb_ti
|
||||
|
||||
/// \method period([value])
|
||||
/// Get or set the period of the timer.
|
||||
mp_obj_t pyb_timer_period(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
STATIC mp_obj_t pyb_timer_period(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_obj_t *self = args[0];
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
@ -852,7 +872,7 @@ STATIC void pyb_timer_channel_print(void (*print)(void *env, const char *fmt, ..
|
||||
print(env, "TimerChannel(timer=%u, channel=%u, mode=%s)",
|
||||
self->timer->tim_id,
|
||||
self->channel,
|
||||
qstr_str(gChannelMode[self->mode].name));
|
||||
qstr_str(channel_mode_info[self->mode].name));
|
||||
}
|
||||
|
||||
/// \method capture([value])
|
||||
@ -869,7 +889,7 @@ 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.
|
||||
STATIC mp_obj_t pyb_timer_channel_capture_compare(uint 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];
|
||||
if (self->channel == 0) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d no channel specified", self->timer->tim_id));
|
||||
@ -885,6 +905,35 @@ STATIC mp_obj_t pyb_timer_channel_capture_compare(uint n_args, const mp_obj_t *a
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_capture_compare_obj, 1, 2, pyb_timer_channel_capture_compare);
|
||||
|
||||
/// \method pulse_width_ratio([value])
|
||||
/// Get or set the pulse width ratio associated with a channel. The value is
|
||||
/// a floating-point number between 0.0 and 1.0, and is relative to the period
|
||||
/// of the timer associated with this channel. For example, a ratio of 0.5
|
||||
/// would be a 50% duty cycle.
|
||||
STATIC mp_obj_t pyb_timer_channel_pulse_width_ratio(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_channel_obj_t *self = args[0];
|
||||
if (self->channel == 0) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d no channel specified", self->timer->tim_id));
|
||||
}
|
||||
uint32_t period = (__HAL_TIM_GetAutoreload(&self->timer->tim) & TIMER_CNT_MASK(self->timer)) + 1;
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
uint32_t cmp = __HAL_TIM_GetCompare(&self->timer->tim, TIMER_CHANNEL(self)) & TIMER_CNT_MASK(self->timer);
|
||||
return mp_obj_new_float((float)cmp / (float)period);
|
||||
} else {
|
||||
// set
|
||||
uint32_t cmp = mp_obj_get_float(args[1]) * period;
|
||||
if (cmp < 0) {
|
||||
cmp = 0;
|
||||
} else if (cmp > period) {
|
||||
cmp = period;
|
||||
}
|
||||
__HAL_TIM_SetCompare(&self->timer->tim, TIMER_CHANNEL(self), cmp & TIMER_CNT_MASK(self->timer));
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_pulse_width_ratio_obj, 1, 2, pyb_timer_channel_pulse_width_ratio);
|
||||
|
||||
/// \method callback(fun)
|
||||
/// Set the function to be called when the timer channel triggers.
|
||||
/// `fun` is passed 1 argument, the timer object.
|
||||
@ -927,19 +976,20 @@ STATIC const mp_map_elem_t pyb_timer_channel_locals_dict_table[] = {
|
||||
// instance methods
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_channel_callback_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_pulse_width), (mp_obj_t)&pyb_timer_channel_capture_compare_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_pulse_width_ratio), (mp_obj_t)&pyb_timer_channel_pulse_width_ratio_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_capture), (mp_obj_t)&pyb_timer_channel_capture_compare_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_compare), (mp_obj_t)&pyb_timer_channel_capture_compare_obj },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(pyb_timer_channel_locals_dict, pyb_timer_channel_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t pyb_timer_channel_type = {
|
||||
STATIC const mp_obj_type_t pyb_timer_channel_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_TimerChannel,
|
||||
.print = pyb_timer_channel_print,
|
||||
.locals_dict = (mp_obj_t)&pyb_timer_channel_locals_dict,
|
||||
};
|
||||
|
||||
void timer_handle_irq_channel(pyb_timer_obj_t *tim, uint8_t channel, mp_obj_t callback) {
|
||||
STATIC void timer_handle_irq_channel(pyb_timer_obj_t *tim, uint8_t channel, mp_obj_t callback) {
|
||||
uint32_t irq_mask = TIMER_IRQ_MASK(channel);
|
||||
|
||||
if (__HAL_TIM_GET_FLAG(&tim->tim, irq_mask) != RESET) {
|
||||
@ -981,8 +1031,9 @@ void timer_irq_handler(uint tim_id) {
|
||||
pyb_timer_obj_t *tim = pyb_timer_obj_all[tim_id - 1];
|
||||
|
||||
if (tim == NULL) {
|
||||
// timer object has not been set, so we can't do anything
|
||||
printf("No timer object for id=%d\n", tim_id);
|
||||
// Timer object has not been set, so we can't do anything.
|
||||
// This can happen under normal circumstances for timers like
|
||||
// 1 & 10 which use the same IRQ.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@ extern TIM_HandleTypeDef TIM5_Handle;
|
||||
extern TIM_HandleTypeDef TIM6_Handle;
|
||||
|
||||
extern const mp_obj_type_t pyb_timer_type;
|
||||
extern const mp_obj_type_t pyb_timer_channel_type;
|
||||
|
||||
void timer_init0(void);
|
||||
void timer_tim3_init(void);
|
||||
|
Loading…
Reference in New Issue
Block a user