diff options
author | Dave Hylands <dhylands@gmail.com> | 2014-08-20 13:21:11 -0700 |
---|---|---|
committer | Dave Hylands <dhylands@gmail.com> | 2014-09-19 09:26:13 -0700 |
commit | becbc87fd73c98664b472b4a06c2b54558dd5669 (patch) | |
tree | 44b1e1daaab750fa2c10899bc9c3489ab4ec738f /stmhal/timer.c | |
parent | 2842945e760c98793cb0c62a66345ab8850afc1a (diff) | |
download | micropython-becbc87fd73c98664b472b4a06c2b54558dd5669.tar.gz micropython-becbc87fd73c98664b472b4a06c2b54558dd5669.zip |
Add Timer support (PWM, OC, IC) for stmhal and teensy
Diffstat (limited to 'stmhal/timer.c')
-rw-r--r-- | stmhal/timer.c | 594 |
1 files changed, 539 insertions, 55 deletions
diff --git a/stmhal/timer.c b/stmhal/timer.c index 6bb425f12f..c0ec08bcc6 100644 --- a/stmhal/timer.c +++ b/stmhal/timer.c @@ -41,6 +41,7 @@ #include "runtime.h" #include "timer.h" #include "servo.h" +#include "pin.h" /// \moduleref pyb /// \class Timer - periodically call a function @@ -63,10 +64,10 @@ /// Further examples: /// /// tim = pyb.Timer(4, freq=100) # freq in Hz -/// tim = pyb.Timer(4, prescaler=1, period=100) +/// tim = pyb.Timer(4, prescaler=0, period=99) /// tim.counter() # get counter (can also set) /// tim.prescaler(2) # set prescaler (can also get) -/// tim.period(200) # set period (can also get) +/// tim.period(199) # set period (can also get) /// tim.callback(lambda t: ...) # set callback for update interrupt (t=tim instance) /// tim.callback(None) # clear callback /// @@ -88,14 +89,59 @@ // TIM6: // - ADC, DAC for read_timed and write_timed +typedef enum { + CHANNEL_MODE_PWM_NORMAL, + CHANNEL_MODE_PWM_INVERTED, + CHANNEL_MODE_OC_TIMING, + CHANNEL_MODE_OC_ACTIVE, + CHANNEL_MODE_OC_INACTIVE, + CHANNEL_MODE_OC_TOGGLE, + CHANNEL_MODE_OC_FORCED_ACTIVE, + CHANNEL_MODE_OC_FORCED_INACTIVE, + CHANNEL_MODE_IC, +} pyb_channel_mode; + +STATIC const struct { + qstr name; + uint32_t oc_mode; +} gChannelMode[] = { + { MP_QSTR_PWM, TIM_OCMODE_PWM1 }, + { MP_QSTR_PWM_INVERTED, TIM_OCMODE_PWM2 }, + { MP_QSTR_OC_TIMING, TIM_OCMODE_TIMING }, + { MP_QSTR_OC_ACTIVE, TIM_OCMODE_ACTIVE }, + { MP_QSTR_OC_INACTIVE, TIM_OCMODE_INACTIVE }, + { MP_QSTR_OC_TOGGLE, TIM_OCMODE_TOGGLE }, + { MP_QSTR_OC_FORCED_ACTIVE, TIM_OCMODE_FORCED_ACTIVE }, + { MP_QSTR_OC_FORCED_INACTIVE, TIM_OCMODE_FORCED_INACTIVE }, + { MP_QSTR_IC, 0 }, +}; + +typedef struct _pyb_timer_channel_obj_t { + mp_obj_base_t base; + struct _pyb_timer_obj_t *timer; + uint8_t channel; + uint8_t mode; + mp_obj_t callback; + struct _pyb_timer_channel_obj_t *next; +} pyb_timer_channel_obj_t; + typedef struct _pyb_timer_obj_t { mp_obj_base_t base; - mp_uint_t tim_id; + uint8_t tim_id; + uint8_t is_32bit; mp_obj_t callback; TIM_HandleTypeDef tim; IRQn_Type irqn; + pyb_timer_channel_obj_t *channel; + } pyb_timer_obj_t; +// The following yields TIM_IT_UPDATE when channel is zero and +// TIM_IT_CC1..TIM_IT_CC4 when channel is 1..4 +#define TIMER_IRQ_MASK(channel) (1 << (channel)) +#define TIMER_CNT_MASK(self) ((self)->is_32bit ? 0x3fffffff : 0xffff) +#define TIMER_CHANNEL(self) ((((self)->channel) - 1) << 2) + TIM_HandleTypeDef TIM3_Handle; TIM_HandleTypeDef TIM5_Handle; TIM_HandleTypeDef TIM6_Handle; @@ -109,6 +155,7 @@ STATIC pyb_timer_obj_t *pyb_timer_obj_all[14]; STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in); STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback); +STATIC mp_obj_t pyb_timer_channel_callback(mp_obj_t self_in, mp_obj_t callback); void timer_init0(void) { tim3_counter = 0; @@ -135,7 +182,7 @@ void timer_tim3_init(void) { TIM3_Handle.Instance = TIM3; TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1; // TIM3 fires every USBD_CDC_POLLING_INTERVAL ms TIM3_Handle.Init.Prescaler = 84-1; // for System clock at 168MHz, TIM3 runs at 1MHz - TIM3_Handle.Init.ClockDivision = 0; + TIM3_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; HAL_TIM_Base_Init(&TIM3_Handle); @@ -167,10 +214,11 @@ void timer_tim5_init(void) { // PWM clock configuration TIM5_Handle.Instance = TIM5; - TIM5_Handle.Init.Period = 2000; // timer cycles at 50Hz + TIM5_Handle.Init.Period = 2000 - 1; // timer cycles at 50Hz TIM5_Handle.Init.Prescaler = ((SystemCoreClock / 2) / 100000) - 1; // timer runs at 100kHz - TIM5_Handle.Init.ClockDivision = 0; + TIM5_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; TIM5_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; + HAL_TIM_PWM_Init(&TIM5_Handle); } @@ -194,7 +242,7 @@ void timer_tim6_init(uint freq) { TIM6_Handle.Instance = TIM6; TIM6_Handle.Init.Period = period - 1; TIM6_Handle.Init.Prescaler = prescaler - 1; - TIM6_Handle.Init.ClockDivision = 0; // unused for TIM6 + TIM6_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // unused for TIM6 TIM6_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; // unused for TIM6 HAL_TIM_Base_Init(&TIM6_Handle); } @@ -224,13 +272,14 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void if (self->tim.State == HAL_TIM_STATE_RESET) { print(env, "Timer(%u)", self->tim_id); } else { - print(env, "Timer(%u, prescaler=%u, period=%u, mode=%u, div=%u)", + print(env, "Timer(%u, prescaler=%u, period=%u, mode=%s, div=%u)", self->tim_id, self->tim.Init.Prescaler, self->tim.Init.Period, - self->tim.Init.CounterMode, - self->tim.Init.ClockDivision - ); + self->tim.Init.CounterMode == TIM_COUNTERMODE_UP ? "UP" : + self->tim.Init.CounterMode == TIM_COUNTERMODE_DOWN ? "DOWN" : "CENTER", + self->tim.Init.ClockDivision == TIM_CLOCKDIVISION_DIV4 ? 4 : + self->tim.Init.ClockDivision == TIM_CLOCKDIVISION_DIV2 ? 2 : 1); } } @@ -239,13 +288,45 @@ STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void /// or by prescaler and period: /// /// tim.init(freq=100) # set the timer to trigger at 100Hz -/// tim.init(prescaler=100, period=300) # set the prescaler and period directly -STATIC const mp_arg_t pyb_timer_init_args[] = { - { MP_QSTR_freq, 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_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 = TIM_CLOCKDIVISION_DIV1} }, +/// tim.init(prescaler=83, period=999) # set the prescaler and period directly +/// +/// Keyword arguments: +/// +/// - `freq` - specifies the periodic frequency of the timer. You migh also +/// view this as the frequency with which the timer goes through +/// one complete cycle. +/// +/// - `prescaler` [0-0xffff] - specifies the value to be loaded into the +/// timer's Prescaler Register (PSC). The timer clock source is divided by +/// (`prescaler + 1`) to arrive at the timer clock. Timers 2-7 and 12-14 +/// have a clock source of 84 MHz (pyb.freq()[2] * 2), and Timers 1, and 8-11 +/// have a clock source of 168 MHz (pyb.freq()[3] * 2). +/// +/// - `period` [0-0xffff] for timers 1, 3, 4, and 6-15. [0-0x3fffffff] for timers 2 & 5. +/// Specifies the value to be loaded into the timer's AutoReload +/// Register (ARR). 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. +/// +/// - `mode` can be one of: +/// - `Timer.UP` - configures the timer to count from 0 to ARR (default) +/// - `Timer.DOWN` - configures the timer to count from ARR down to 0. +/// - `Timer.CENTER` - confgures the timer to count from 0 to ARR and +/// then back down to 0. +/// +/// - `div` can be one of 1, 2, or 4. Divides the timer clock to determine +/// the sampling clock used by the digital filters. +/// +/// - `callback` - as per Timer.callback() +/// +/// You must either specify freq or both of period and prescaler. + STATIC const mp_arg_t pyb_timer_init_args[] = { + { MP_QSTR_freq, 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_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_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; #define PYB_TIMER_INIT_NUM_ARGS MP_ARRAY_SIZE(pyb_timer_init_args) @@ -281,7 +362,7 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, mp_uint_t n_args, c // respective APB clock. See DM00031020 Rev 4, page 115. uint32_t period = MAX(1, 2 * tim_clock / vals[0].u_int); uint32_t prescaler = 1; - while (period > 0xffff) { + while (period > TIMER_CNT_MASK(self)) { period >>= 1; prescaler <<= 1; } @@ -296,9 +377,16 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, mp_uint_t n_args, c } init->CounterMode = vals[3].u_int; - init->ClockDivision = vals[4].u_int; + + init->ClockDivision = vals[4].u_int == 2 ? TIM_CLOCKDIVISION_DIV2 : + vals[4].u_int == 4 ? TIM_CLOCKDIVISION_DIV4 : + TIM_CLOCKDIVISION_DIV1; init->RepetitionCounter = 0; + if (!IS_TIM_COUNTER_MODE(init->CounterMode)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid counter_mode (%d)", init->CounterMode)); + } + // init the TIM peripheral switch (self->tim_id) { case 1: __TIM1_CLK_ENABLE(); break; @@ -316,14 +404,18 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, mp_uint_t n_args, c case 13: __TIM13_CLK_ENABLE(); break; case 14: __TIM14_CLK_ENABLE(); break; } - HAL_TIM_Base_Init(&self->tim); - HAL_TIM_Base_Start(&self->tim); - // set the priority (if not a special timer) if (self->tim_id != 3 && self->tim_id != 5) { HAL_NVIC_SetPriority(self->irqn, 0xe, 0xe); // next-to lowest priority } + HAL_TIM_Base_Init(&self->tim); + if (vals[5].u_obj == mp_const_none) { + HAL_TIM_Base_Start(&self->tim); + } else { + pyb_timer_callback(self, vals[5].u_obj); + } + return mp_const_none; } @@ -337,19 +429,22 @@ STATIC mp_obj_t pyb_timer_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t // create new Timer object pyb_timer_obj_t *tim = m_new_obj(pyb_timer_obj_t); + memset(tim, 0, sizeof(*tim)); + tim->base.type = &pyb_timer_type; tim->callback = mp_const_none; - memset(&tim->tim, 0, sizeof(tim->tim)); + tim->channel = NULL; // get TIM number tim->tim_id = mp_obj_get_int(args[0]); + tim->is_32bit = false; switch (tim->tim_id) { case 1: tim->tim.Instance = TIM1; tim->irqn = TIM1_UP_TIM10_IRQn; break; - case 2: tim->tim.Instance = TIM2; tim->irqn = TIM2_IRQn; break; + case 2: tim->tim.Instance = TIM2; tim->irqn = TIM2_IRQn; tim->is_32bit = true; break; case 3: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Timer 3 is for internal use only")); // TIM3 used for low-level stuff; go via regs if necessary case 4: tim->tim.Instance = TIM4; tim->irqn = TIM4_IRQn; break; - case 5: tim->tim.Instance = TIM5; tim->irqn = TIM5_IRQn; break; + case 5: tim->tim.Instance = TIM5; tim->irqn = TIM5_IRQn; tim->is_32bit = true; break; case 6: tim->tim.Instance = TIM6; tim->irqn = TIM6_DAC_IRQn; break; case 7: tim->tim.Instance = TIM7; tim->irqn = TIM7_IRQn; break; case 8: tim->tim.Instance = TIM8; tim->irqn = TIM8_UP_TIM13_IRQn; break; @@ -386,18 +481,261 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_init_obj, 1, pyb_timer_init); /// Deinitialises the timer. /// /// Disables the callback (and the associated irq). +/// Disables any channel callbacks (and the associated irq). /// Stops the timer, and disables the timer peripheral. STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in) { pyb_timer_obj_t *self = self_in; - // Disable the interrupt + // Disable the base interrupt pyb_timer_callback(self_in, mp_const_none); + pyb_timer_channel_obj_t *chan = self->channel; + self->channel = NULL; + + // Disable the channel interrupts + while (chan != NULL) { + pyb_timer_channel_callback(chan, mp_const_none); + pyb_timer_channel_obj_t *prev_chan = chan; + chan = chan->next; + prev_chan->next = NULL; + } + HAL_TIM_Base_DeInit(&self->tim); return mp_const_none; } 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. +/// +/// Othwerwise, a TimerChannel object is initialized and returned. +/// +/// Each channel can be configured to perform pwm, output compare, or +/// input capture. All channels share the same underlying timer, which means +/// that they share the same timer clock. +/// +/// Keyword arguments: +/// +/// - `mode` can be one of: +/// - `Timer.PWM` - configure the timer in PWM mode (active high). +/// - `Timer.PWM_INVERTED` - configure the timer in PWM mode (active low). +/// - `Timer.OC_TIMING` - indicates that no pin is driven. +/// - `Timer.OC_ACTIVE` - the pin will be made active when a compare +/// match occurs (active is determined by polarity) +/// - `Timer.OC_INACTIVE` - the pin will be made inactive when a compare +/// match occurs. +/// - `Timer.OC_TOGGLE` - the pin will be toggled when an compare match occurs. +/// - `Timer.OC_FORCED_ACTIVE` - the pin is forced active (compare match is ignored). +/// - `Timer.OC_FORCED_INACTIVE` - the pin is forced inactive (compare match is ignored). +/// - `Timer.IC` - configure the timer in Input Capture mode. +/// +/// - `callback` - as per TimerChannel.callback() +/// +/// - `pin` None (the default) or a Pin object. If specified (and not None) +/// this will cause the alternate function of the the indicated pin +/// to be configured for this timer channel. An error will be raised if +/// the pin doesn't support any alternate functions for this timer channel. +/// +/// Keyword arguments for Timer.PWM modes: +/// +/// - 'pulse_width' - determines the initial pulse width to use. +/// +/// Keyword arguments for Timer.OC modes: +/// +/// - `compare` - determines the initial value of the compare register. +/// +/// - `polarity` can be one of: +/// - `Timer.HIGH` - output is active high +/// - `Timer.LOW` - output is acive low +/// +/// Optional keyword arguments for Timer.IC modes: +/// +/// - `polarity` can be one of: +/// - `Timer.RISING` - captures on rising edge. +/// - `Timer.FALLING` - captures on falling edge. +/// - `Timer.BOTH` - captures on both edges. +/// +/// PWM Example: +/// +/// timer = pyb.Timer(2, freq=1000) +/// 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} }, +}; +#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]); + + if (channel < 1 || channel > 4) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid channel (%d)", channel)); + } + + pyb_timer_channel_obj_t *chan = self->channel; + pyb_timer_channel_obj_t *prev_chan = NULL; + + while (chan != NULL) { + if (chan->channel == channel) { + break; + } + prev_chan = chan; + chan = chan->next; + } + if (kw_args->used == 0) { + // Return the previously allocated channel + if (chan) { + return chan; + } + return mp_const_none; + } + + // If there was already a channel, then remove it from the list. Note that + // the order we do things here is important so as to appear atomic to + // the IRQ handler. + if (chan) { + // Turn off any IRQ associated with the channel. + pyb_timer_channel_callback(chan, mp_const_none); + + // Unlink the channel from the list. + if (prev_chan) { + prev_chan->next = chan->next; + } + self->channel = chan->next; + chan->next = NULL; + } + + // Allocate and initialize a new channel + mp_arg_val_t vals[PYB_TIMER_CHANNEL_NUM_ARGS]; + mp_arg_parse_all(n_args - 3, args + 3, kw_args, PYB_TIMER_CHANNEL_NUM_ARGS, pyb_timer_channel_args, vals); + + chan = m_new_obj(pyb_timer_channel_obj_t); + memset(chan, 0, sizeof(*chan)); + chan->base.type = &pyb_timer_channel_type; + chan->timer = self; + chan->channel = channel; + chan->mode = mp_obj_get_int(args[2]); + chan->callback = vals[0].u_obj; + + mp_obj_t pin_obj = vals[1].u_obj; + if (pin_obj != mp_const_none) { + if (!MP_OBJ_IS_TYPE(pin_obj, &pin_type)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "pin argument needs to be be a Pin type")); + } + const pin_obj_t *pin = pin_obj; + const pin_af_obj_t *af = pin_find_af(pin, AF_FN_TIM, self->tim_id); + if (af == NULL) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin %s doesn't have an af for TIM%d", qstr_str(pin->name), self->tim_id)); + } + // pin.init(mode=AF_PP, af=idx) + const mp_obj_t args[6] = { + (mp_obj_t)&pin_init_obj, + pin_obj, + MP_OBJ_NEW_QSTR(MP_QSTR_mode), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_AF_PP), + MP_OBJ_NEW_QSTR(MP_QSTR_af), MP_OBJ_NEW_SMALL_INT(af->idx) + }; + mp_call_method_n_kw(0, 2, args); + } + + // Link the channel to the timer before we turn the channel on. + // Note that this needs to appear atomic to the IRQ handler (the write + // to self->channel is atomic, so we're good, but I thought I'd mention + // in case this was ever changed in the future). + chan->next = self->channel; + self->channel = chan; + + switch (chan->mode) { + + 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.OCPolarity = TIM_OCPOLARITY_HIGH; + oc_config.OCNPolarity = TIM_OCNPOLARITY_HIGH; + oc_config.OCFastMode = TIM_OCFAST_DISABLE; + oc_config.OCIdleState = TIM_OCIDLESTATE_SET; + oc_config.OCNIdleState = TIM_OCNIDLESTATE_SET; + + HAL_TIM_PWM_ConfigChannel(&self->tim, &oc_config, TIMER_CHANNEL(chan)); + if (chan->callback == mp_const_none) { + HAL_TIM_PWM_Start(&self->tim, TIMER_CHANNEL(chan)); + } else { + HAL_TIM_PWM_Start_IT(&self->tim, TIMER_CHANNEL(chan)); + } + break; + } + + case CHANNEL_MODE_OC_TIMING: + case CHANNEL_MODE_OC_ACTIVE: + case CHANNEL_MODE_OC_INACTIVE: + case CHANNEL_MODE_OC_TOGGLE: + 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; + if (oc_config.OCPolarity == 0xffffffff) { + oc_config.OCPolarity = TIM_OCPOLARITY_HIGH; + } + oc_config.OCNPolarity = TIM_OCNPOLARITY_HIGH; + oc_config.OCFastMode = TIM_OCFAST_DISABLE; + oc_config.OCIdleState = TIM_OCIDLESTATE_SET; + oc_config.OCNIdleState = TIM_OCNIDLESTATE_SET; + + if (!IS_TIM_OC_POLARITY(oc_config.OCPolarity)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid polarity (%d)", oc_config.OCPolarity)); + } + HAL_TIM_OC_ConfigChannel(&self->tim, &oc_config, TIMER_CHANNEL(chan)); + if (chan->callback == mp_const_none) { + HAL_TIM_OC_Start(&self->tim, TIMER_CHANNEL(chan)); + } else { + HAL_TIM_OC_Start_IT(&self->tim, TIMER_CHANNEL(chan)); + } + break; + } + + case CHANNEL_MODE_IC: { + TIM_IC_InitTypeDef ic_config; + + ic_config.ICPolarity = vals[4].u_int; + if (ic_config.ICPolarity == 0xffffffff) { + ic_config.ICPolarity = TIM_ICPOLARITY_RISING; + } + ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI; + ic_config.ICPrescaler = TIM_ICPSC_DIV1; + ic_config.ICFilter = 0; + + if (!IS_TIM_IC_POLARITY(ic_config.ICPolarity)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid polarity (%d)", ic_config.ICPolarity)); + } + HAL_TIM_IC_ConfigChannel(&self->tim, &ic_config, TIMER_CHANNEL(chan)); + if (chan->callback == mp_const_none) { + HAL_TIM_IC_Start(&self->tim, TIMER_CHANNEL(chan)); + } else { + HAL_TIM_IC_Start_IT(&self->tim, TIMER_CHANNEL(chan)); + } + break; + } + + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid mode (%d)", chan->mode)); + } + + return chan; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_channel_obj, 3, 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) { @@ -434,10 +772,10 @@ 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 - return mp_obj_new_int(self->tim.Instance->ARR & 0xffff); + return mp_obj_new_int(__HAL_TIM_GetAutoreload(&self->tim) & TIMER_CNT_MASK(self)); } else { // set - __HAL_TIM_SetAutoreload(&self->tim, mp_obj_get_int(args[1]) & 0xffff); + __HAL_TIM_SetAutoreload(&self->tim, mp_obj_get_int(args[1]) & TIMER_CNT_MASK(self)); return mp_const_none; } } @@ -469,12 +807,29 @@ STATIC const mp_map_elem_t pyb_timer_locals_dict_table[] = { // instance methods { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_timer_init_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_timer_deinit_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_channel), (mp_obj_t)&pyb_timer_channel_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_counter), (mp_obj_t)&pyb_timer_counter_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_prescaler), (mp_obj_t)&pyb_timer_prescaler_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_period_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_callback_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_UP), MP_OBJ_NEW_SMALL_INT(TIM_COUNTERMODE_UP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_DOWN), MP_OBJ_NEW_SMALL_INT(TIM_COUNTERMODE_DOWN) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CENTER), MP_OBJ_NEW_SMALL_INT(TIM_COUNTERMODE_CENTERALIGNED1) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PWM), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_PWM_NORMAL) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PWM_INVERTED), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_PWM_INVERTED) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_TIMING), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_TIMING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_ACTIVE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_ACTIVE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_INACTIVE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_INACTIVE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_TOGGLE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_TOGGLE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_FORCED_ACTIVE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_FORCED_ACTIVE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_FORCED_INACTIVE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_FORCED_INACTIVE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IC), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_IC) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_HIGH), MP_OBJ_NEW_SMALL_INT(TIM_OCPOLARITY_HIGH) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LOW), MP_OBJ_NEW_SMALL_INT(TIM_OCPOLARITY_LOW) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RISING), MP_OBJ_NEW_SMALL_INT(TIM_ICPOLARITY_RISING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_FALLING), MP_OBJ_NEW_SMALL_INT(TIM_ICPOLARITY_FALLING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BOTH), MP_OBJ_NEW_SMALL_INT(TIM_ICPOLARITY_BOTHEDGE) }, }; - STATIC MP_DEFINE_CONST_DICT(pyb_timer_locals_dict, pyb_timer_locals_dict_table); const mp_obj_type_t pyb_timer_type = { @@ -485,6 +840,141 @@ const mp_obj_type_t pyb_timer_type = { .locals_dict = (mp_obj_t)&pyb_timer_locals_dict, }; +/// \moduleref pyb +/// \class TimerChannel - setup a channel for a timer. +/// +/// Timer channels are used to generate/capture a signal using a timer. +/// +/// TimerChannel objects are created using the Timer.channel() method. +STATIC void pyb_timer_channel_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + pyb_timer_channel_obj_t *self = self_in; + + print(env, "TimerChannel(timer=%u, channel=%u, mode=%s)", + self->timer->tim_id, + self->channel, + qstr_str(gChannelMode[self->mode].name)); +} + +/// \method capture([value]) +/// Get or set the capture value associated with a channel. +/// capture, compare, and pulse_width are all aliases for the same function. +/// capture is the logical name to use when the channel is in input capture mode. + +/// \method compare([value]) +/// Get or set the compare value associated with a channel. +/// capture, compare, and pulse_width are all aliases for the same function. +/// compare is the logical name to use when the channel is in output compare mode. + +/// \method pulse_width([value]) +/// 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) { + 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)); + } + if (n_args == 1) { + // get + return mp_obj_new_int(__HAL_TIM_GetCompare(&self->timer->tim, TIMER_CHANNEL(self)) & TIMER_CNT_MASK(self->timer)); + } else { + // set + __HAL_TIM_SetCompare(&self->timer->tim, TIMER_CHANNEL(self), mp_obj_get_int(args[1]) & TIMER_CNT_MASK(self->timer)); + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_capture_compare_obj, 1, 2, pyb_timer_channel_capture_compare); + +/// \method callback(fun) +/// Set the function to be called when the timer channel triggers. +/// `fun` is passed 1 argument, the timer object. +/// If `fun` is `None` then the callback will be disabled. +STATIC mp_obj_t pyb_timer_channel_callback(mp_obj_t self_in, mp_obj_t callback) { + pyb_timer_channel_obj_t *self = self_in; + if (callback == mp_const_none) { + // stop interrupt (but not timer) + __HAL_TIM_DISABLE_IT(&self->timer->tim, TIMER_IRQ_MASK(self->channel)); + self->callback = mp_const_none; + } else if (mp_obj_is_callable(callback)) { + self->callback = callback; + HAL_NVIC_EnableIRQ(self->timer->irqn); + // start timer, so that it interrupts on overflow + switch (self->mode) { + case CHANNEL_MODE_PWM_NORMAL: + case CHANNEL_MODE_PWM_INVERTED: + HAL_TIM_PWM_Start_IT(&self->timer->tim, TIMER_CHANNEL(self)); + break; + case CHANNEL_MODE_OC_TIMING: + case CHANNEL_MODE_OC_ACTIVE: + case CHANNEL_MODE_OC_INACTIVE: + case CHANNEL_MODE_OC_TOGGLE: + case CHANNEL_MODE_OC_FORCED_ACTIVE: + case CHANNEL_MODE_OC_FORCED_INACTIVE: + HAL_TIM_OC_Start_IT(&self->timer->tim, TIMER_CHANNEL(self)); + break; + case CHANNEL_MODE_IC: + HAL_TIM_IC_Start_IT(&self->timer->tim, TIMER_CHANNEL(self)); + break; + } + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "callback must be None or a callable object")); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_timer_channel_callback_obj, pyb_timer_channel_callback); + +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_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 = { + { &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) { + uint32_t irq_mask = TIMER_IRQ_MASK(channel); + + if (__HAL_TIM_GET_FLAG(&tim->tim, irq_mask) != RESET) { + if (__HAL_TIM_GET_ITSTATUS(&tim->tim, irq_mask) != RESET) { + // clear the interrupt + __HAL_TIM_CLEAR_IT(&tim->tim, irq_mask); + + // execute callback if it's set + if (callback != mp_const_none) { + // When executing code within a handler we must lock the GC to prevent + // any memory allocations. We must also catch any exceptions. + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_1(callback, tim); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + tim->callback = mp_const_none; + __HAL_TIM_DISABLE_IT(&tim->tim, irq_mask); + if (channel == 0) { + printf("Uncaught exception in Timer(" UINT_FMT + ") interrupt handler\n", tim->tim_id); + } else { + printf("Uncaught exception in Timer(" UINT_FMT ") channel " + UINT_FMT " interrupt handler\n", tim->tim_id, channel); + } + mp_obj_print_exception((mp_obj_t)nlr.ret_val); + } + gc_unlock(); + } + } + } +} + void timer_irq_handler(uint tim_id) { if (tim_id - 1 < PYB_TIMER_OBJ_ALL_NUM) { // get the timer object @@ -492,34 +982,28 @@ void timer_irq_handler(uint tim_id) { 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); return; } - // see if it was a TIM update event (the only event we currently interrupt on) - if (__HAL_TIM_GET_FLAG(&tim->tim, TIM_FLAG_UPDATE) != RESET) { - if (__HAL_TIM_GET_ITSTATUS(&tim->tim, TIM_IT_UPDATE) != RESET) { - // clear the interrupt - __HAL_TIM_CLEAR_IT(&tim->tim, TIM_IT_UPDATE); - - // execute callback if it's set - if (tim->callback != mp_const_none) { - // When executing code within a handler we must lock the GC to prevent - // any memory allocations. We must also catch any exceptions. - gc_lock(); - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_call_function_1(tim->callback, tim); - nlr_pop(); - } else { - // Uncaught exception; disable the callback so it doesn't run again. - tim->callback = mp_const_none; - __HAL_TIM_DISABLE_IT(&tim->tim, TIM_IT_UPDATE); - printf("Uncaught exception in Timer(" UINT_FMT ") interrupt handler\n", tim->tim_id); - mp_obj_print_exception((mp_obj_t)nlr.ret_val); - } - gc_unlock(); - } - } + // Check for timer (versus timer channel) interrupt. + timer_handle_irq_channel(tim, 0, tim->callback); + uint32_t handled = TIMER_IRQ_MASK(0); + + // Check to see if a timer channel interrupt was pending + pyb_timer_channel_obj_t *chan = tim->channel; + while (chan != NULL) { + timer_handle_irq_channel(tim, chan->channel, chan->callback); + handled |= TIMER_IRQ_MASK(chan->channel); + chan = chan->next; + } + + // Finally, clear any remaining interrupt sources. Otherwise we'll + // just get called continuously. + uint32_t unhandled = __HAL_TIM_GET_ITSTATUS(&tim->tim, 0xff & ~handled); + if (unhandled != 0) { + __HAL_TIM_CLEAR_IT(&tim->tim, unhandled); + printf("Unhandled interrupt SR=0x%02lx (now disabled)\n", unhandled); } } } |