From becbc87fd73c98664b472b4a06c2b54558dd5669 Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Wed, 20 Aug 2014 13:21:11 -0700 Subject: [PATCH] Add Timer support (PWM, OC, IC) for stmhal and teensy --- stmhal/pin.c | 2 +- stmhal/pin.h | 4 +- stmhal/pin_named_pins.c | 6 +- stmhal/qstrdefsport.h | 27 ++ stmhal/timer.c | 594 +++++++++++++++++++++++--- stmhal/timer.h | 1 + teensy/Makefile | 8 +- teensy/hal_ftm.c | 201 +++++++++ teensy/hal_ftm.h | 184 ++++++++ teensy/hal_gpio.c | 9 +- teensy/make-pins.py | 28 +- teensy/mk20dx256_af.csv | 2 +- teensy/modpyb.c | 4 +- teensy/mpconfigport.h | 6 + teensy/pin_defs_teensy.c | 23 +- teensy/pin_defs_teensy.h | 4 + teensy/qstrdefsport.h | 36 ++ teensy/reg.c | 52 +++ teensy/reg.h | 8 + teensy/teensy_hal.h | 13 +- teensy/teensy_pins.csv | 1 + teensy/timer.c | 897 +++++++++++++++++++++++++++++++++++++++ teensy/timer.h | 31 ++ 23 files changed, 2055 insertions(+), 86 deletions(-) create mode 100644 teensy/hal_ftm.c create mode 100644 teensy/hal_ftm.h create mode 100644 teensy/reg.c create mode 100644 teensy/reg.h create mode 100644 teensy/timer.c create mode 100644 teensy/timer.h diff --git a/stmhal/pin.c b/stmhal/pin.c index c96da794e6..286e0992c0 100644 --- a/stmhal/pin.c +++ b/stmhal/pin.c @@ -413,7 +413,7 @@ STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *self, mp_uint_t n_args, con STATIC mp_obj_t pin_obj_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { return pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pin_init_obj, 1, pin_obj_init); +MP_DEFINE_CONST_FUN_OBJ_KW(pin_init_obj, 1, pin_obj_init); /// \method value([value]) /// Get or set the digital logic level of the pin: diff --git a/stmhal/pin.h b/stmhal/pin.h index 6ac73aabd2..492803871f 100644 --- a/stmhal/pin.h +++ b/stmhal/pin.h @@ -82,12 +82,14 @@ extern const mp_obj_type_t pin_cpu_pins_obj_type; extern const mp_obj_dict_t pin_cpu_pins_locals_dict; extern const mp_obj_dict_t pin_board_pins_locals_dict; +MP_DECLARE_CONST_FUN_OBJ(pin_init_obj); + void pin_init0(void); uint32_t pin_get_mode(const pin_obj_t *pin); uint32_t pin_get_pull(const pin_obj_t *pin); uint32_t pin_get_af(const pin_obj_t *pin); const pin_obj_t *pin_find(mp_obj_t user_obj); const pin_obj_t *pin_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t name); -const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit, uint8_t pin_type); +const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit); const pin_af_obj_t *pin_find_af_by_index(const pin_obj_t *pin, mp_uint_t af_idx); const pin_af_obj_t *pin_find_af_by_name(const pin_obj_t *pin, const char *name); diff --git a/stmhal/pin_named_pins.c b/stmhal/pin_named_pins.c index 36442a0b0b..24b6f79b6c 100644 --- a/stmhal/pin_named_pins.c +++ b/stmhal/pin_named_pins.c @@ -64,17 +64,15 @@ const pin_obj_t *pin_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t na return NULL; } -/* unused -const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit, uint8_t type) { +const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit) { const pin_af_obj_t *af = pin->af; for (mp_uint_t i = 0; i < pin->num_af; i++, af++) { - if (af->fn == fn && af->unit == unit && af->type == type) { + if (af->fn == fn && af->unit == unit) { return af; } } return NULL; } -*/ const pin_af_obj_t *pin_find_af_by_index(const pin_obj_t *pin, mp_uint_t af_idx) { const pin_af_obj_t *af = pin->af; diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index 7f19d1f09b..3c1bf16d2f 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -144,6 +144,9 @@ Q(recv) // for Timer class Q(Timer) +Q(init) +Q(deinit) +Q(channel) Q(counter) Q(prescaler) Q(period) @@ -151,6 +154,30 @@ Q(callback) Q(freq) Q(mode) Q(div) +Q(UP) +Q(DOWN) +Q(CENTER) +Q(IC) +Q(PWM) +Q(PWM_INVERTED) +Q(OC_TIMING) +Q(OC_ACTIVE) +Q(OC_INACTIVE) +Q(OC_TOGGLE) +Q(OC_FORCED_ACTIVE) +Q(OC_FORCED_INACTIVE) +Q(HIGH) +Q(LOW) +Q(RISING) +Q(FALLING) +Q(BOTH) + +// for TimerChannel class +Q(TimerChannel) +Q(pulse_width) +Q(compare) +Q(capture) +Q(polarity) // for ExtInt class Q(ExtInt) 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); } } } diff --git a/stmhal/timer.h b/stmhal/timer.h index 2a892db285..c737ee7635 100644 --- a/stmhal/timer.h +++ b/stmhal/timer.h @@ -34,6 +34,7 @@ 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); diff --git a/teensy/Makefile b/teensy/Makefile index 09bee1dbcd..478389e30f 100644 --- a/teensy/Makefile +++ b/teensy/Makefile @@ -42,8 +42,11 @@ CFLAGS += -Og -ggdb else CFLAGS += -Os #-DNDEBUG endif +CFLAGS += -fdata-sections -ffunction-sections +LDFLAGS += -Wl,--gc-sections SRC_C = \ + hal_ftm.c \ hal_gpio.c \ help.c \ import.c \ @@ -54,7 +57,9 @@ SRC_C = \ memzip.c \ modpyb.c \ pin_defs_teensy.c \ + reg.c \ teensy_hal.c \ + timer.c \ uart.c \ usb.c \ @@ -141,6 +146,7 @@ GEN_PINS_SRC = $(BUILD)/pins_gen.c GEN_PINS_HDR = $(HEADER_BUILD)/pins.h GEN_PINS_QSTR = $(BUILD)/pins_qstr.h GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins_af_const.h +GEN_PINS_AF_PY = $(BUILD)/pins_af.py # Making OBJ use an order-only depenedency on the generated pins.h file # has the side effect of making the pins.h file before we actually compile @@ -153,7 +159,7 @@ $(OBJ): | $(HEADER_BUILD)/pins.h # both pins_$(BOARD).c and pins.h $(BUILD)/%_gen.c $(HEADER_BUILD)/%.h $(HEADER_BUILD)/%_af_const.h $(BUILD)/%_qstr.h: teensy_%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD) $(ECHO) "Create $@" - $(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) --qstr $(GEN_PINS_QSTR) --af-const $(GEN_PINS_AF_CONST) > $(GEN_PINS_SRC) + $(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) --qstr $(GEN_PINS_QSTR) --af-const $(GEN_PINS_AF_CONST) --af-py $(GEN_PINS_AF_PY) > $(GEN_PINS_SRC) $(BUILD)/pins_gen.o: $(BUILD)/pins_gen.c $(call compile_c) diff --git a/teensy/hal_ftm.c b/teensy/hal_ftm.c new file mode 100644 index 0000000000..28992881be --- /dev/null +++ b/teensy/hal_ftm.c @@ -0,0 +1,201 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include "teensy_hal.h" + +void HAL_FTM_Base_Init(FTM_HandleTypeDef *hftm) { + /* Check the parameters */ + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_PRESCALERSHIFT(hftm->Init.PrescalerShift)); + assert_param(IS_FTM_COUNTERMODE(hftm->Init.CounterMode)); + assert_param(IS_FTM_PERIOD(hftm->Init.Period)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->MODE = FTM_MODE_WPDIS; + FTMx->SC = 0; + FTMx->MOD = hftm->Init.Period; + uint32_t sc = FTM_SC_PS(hftm->Init.PrescalerShift); + if (hftm->Init.CounterMode == FTM_COUNTERMODE_CENTER) { + sc |= FTM_SC_CPWMS; + } + FTMx->SC = sc; + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_Base_Start(FTM_HandleTypeDef *hftm) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->CNT = 0; + FTMx->SC &= ~FTM_SC_CLKS(3); + FTMx->SC |= FTM_SC_CLKS(1); + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_Base_Start_IT(FTM_HandleTypeDef *hftm) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->CNT = 0; + FTMx->SC |= FTM_SC_CLKS(1) | FTM_SC_TOIE; + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_Base_DeInit(FTM_HandleTypeDef *hftm) { + assert_param(IS_FTM_INSTANCE(hftm->Instance)); + + hftm->State = HAL_FTM_STATE_BUSY; + + __HAL_FTM_DISABLE_TOF_IT(hftm); + + hftm->State = HAL_FTM_STATE_RESET; +} + +void HAL_FTM_OC_Init(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_Init(hftm); +} + +void HAL_FTM_OC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_CHANNEL(channel)); + assert_param(IS_FTM_OC_MODE(sConfig->OCMode)); + assert_param(IS_FTM_OC_PULSE(sConfig->Pulse)); + assert_param(IS_FTM_OC_POLARITY(sConfig->OCPolarity)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->channel[channel].CSC = sConfig->OCMode; + FTMx->channel[channel].CV = sConfig->Pulse; + if (sConfig->OCPolarity & 1) { + FTMx->POL |= (1 << channel); + } else { + FTMx->POL &= ~(1 << channel); + } + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_OC_Start(FTM_HandleTypeDef *hftm, uint32_t channel) { + // Nothing else to do +} + +void HAL_FTM_OC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + FTMx->channel[channel].CSC |= FTM_CSC_CHIE; +} + +void HAL_FTM_OC_DeInit(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_DeInit(hftm); +} + +void HAL_FTM_PWM_Init(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_Init(hftm); +} + +void HAL_FTM_PWM_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_CHANNEL(channel)); + assert_param(IS_FTM_PWM_MODE(sConfig->OCMode)); + assert_param(IS_FTM_OC_PULSE(sConfig->Pulse)); + assert_param(IS_FTM_OC_POLARITY(sConfig->OCPolarity)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->channel[channel].CSC = sConfig->OCMode; + FTMx->channel[channel].CV = sConfig->Pulse; + if (sConfig->OCPolarity & 1) { + FTMx->POL |= (1 << channel); + } else { + FTMx->POL &= ~(1 << channel); + } + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_PWM_Start(FTM_HandleTypeDef *hftm, uint32_t channel) { + // Nothing else to do +} + +void HAL_FTM_PWM_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + FTMx->channel[channel].CSC |= FTM_CSC_CHIE; +} + +void HAL_FTM_PWM_DeInit(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_DeInit(hftm); +} + +void HAL_FTM_IC_Init(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_Init(hftm); +} + +void HAL_FTM_IC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_IC_InitTypeDef* sConfig, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_CHANNEL(channel)); + assert_param(IS_FTM_IC_POLARITY(sConfig->ICPolarity)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->channel[channel].CSC = sConfig->ICPolarity; + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_IC_Start(FTM_HandleTypeDef *hftm, uint32_t channel) { + //FTM_TypeDef *FTMx = hftm->Instance; + //assert_param(IS_FTM_INSTANCE(FTMx)); + + // Nothing else to do +} + +void HAL_FTM_IC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + FTMx->channel[channel].CSC |= FTM_CSC_CHIE; +} + +void HAL_FTM_IC_DeInit(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_DeInit(hftm); +} diff --git a/teensy/hal_ftm.h b/teensy/hal_ftm.h new file mode 100644 index 0000000000..3dc15300d7 --- /dev/null +++ b/teensy/hal_ftm.h @@ -0,0 +1,184 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define FTM0 ((FTM_TypeDef *)&FTM0_SC) +#define FTM1 ((FTM_TypeDef *)&FTM1_SC) +#define FTM2 ((FTM_TypeDef *)&FTM2_SC) + +typedef struct { + volatile uint32_t CSC; // Channel x Status And Control + volatile uint32_t CV; // Channel x Value +} FTM_ChannelTypeDef; + +typedef struct { + volatile uint32_t SC; // Status And Control + volatile uint32_t CNT; // Counter + volatile uint32_t MOD; // Modulo + FTM_ChannelTypeDef channel[8]; + volatile uint32_t CNTIN; // Counter Initial Value + volatile uint32_t STATUS; // Capture And Compare Status + volatile uint32_t MODE; // Features Mode Selection + volatile uint32_t SYNC; // Synchronization + volatile uint32_t OUTINIT; // Initial State For Channels Output + volatile uint32_t OUTMASK; // Output Mask + volatile uint32_t COMBINE; // Function For Linked Channels + volatile uint32_t DEADTIME; // Deadtime Insertion Control + volatile uint32_t EXTTRIG; // FTM External Trigger + volatile uint32_t POL; // Channels Polarity + volatile uint32_t FMS; // Fault Mode Status + volatile uint32_t FILTER; // Input Capture Filter Control + volatile uint32_t FLTCTRL; // Fault Control + volatile uint32_t QDCTRL; // Quadrature Decoder Control And Status + volatile uint32_t CONF; // Configuration + volatile uint32_t FLTPOL; // FTM Fault Input Polarity + volatile uint32_t SYNCONF; // Synchronization Configuration + volatile uint32_t INVCTRL; // FTM Inverting Control + volatile uint32_t SWOCTRL; // FTM Software Output Control + volatile uint32_t PWMLOAD; // FTM PWM Load +} FTM_TypeDef; + +typedef struct { + uint32_t PrescalerShift; // Sets the prescaler to 1 << PrescalerShift + uint32_t CounterMode; // One of FTM_COUNTERMODE_xxx + uint32_t Period; // Specifies the Period for determining timer overflow +} FTM_Base_InitTypeDef; + +typedef struct { + uint32_t OCMode; // One of FTM_OCMODE_xxx + uint32_t Pulse; // Specifies initial pulse width (0-0xffff) + uint32_t OCPolarity; // One of FTM_OCPOLRITY_xxx +} FTM_OC_InitTypeDef; + +typedef struct { + uint32_t ICPolarity; // Specifies Rising/Falling/Both +} FTM_IC_InitTypeDef; + +#define IS_FTM_INSTANCE(INSTANCE) (((INSTANCE) == FTM0) || \ + ((INSTANCE) == FTM1) || \ + ((INSTANCE) == FTM2)) + +#define IS_FTM_PRESCALERSHIFT(PRESCALERSHIFT) (((PRESCALERSHIFT) & ~7) == 0) + +#define FTM_COUNTERMODE_UP (0) +#define FTM_COUNTERMODE_CENTER (FTM_SC_CPWMS) + +#define IS_FTM_COUNTERMODE(MODE) (((MODE) == FTM_COUNTERMODE_UP) ||\ + ((MODE) == FTM_COUNTERMODE_CENTER)) + +#define IS_FTM_PERIOD(PERIOD) (((PERIOD) & 0xFFFF0000) == 0) + +#define FTM_CSC_CHF 0x80 +#define FTM_CSC_CHIE 0x40 +#define FTM_CSC_MSB 0x20 +#define FTM_CSC_MSA 0x10 +#define FTM_CSC_ELSB 0x08 +#define FTM_CSC_ELSA 0x04 +#define FTM_CSC_DMA 0x01 + +#define FTM_OCMODE_TIMING (0) +#define FTM_OCMODE_ACTIVE (FTM_CSC_MSA | FTM_CSC_ELSB | FTM_CSC_ELSA) +#define FTM_OCMODE_INACTIVE (FTM_CSC_MSA | FTM_CSC_ELSB) +#define FTM_OCMODE_TOGGLE (FTM_CSC_MSA | FTM_CSC_ELSA) +#define FTM_OCMODE_PWM1 (FTM_CSC_MSB | FTM_CSC_ELSB) +#define FTM_OCMODE_PWM2 (FTM_CSC_MSB | FTM_CSC_ELSA) + +#define IS_FTM_OC_MODE(mode) ((mode) == FTM_OCMODE_TIMING || \ + (mode) == FTM_OCMODE_ACTIVE || \ + (mode) == FTM_OCMODE_INACTIVE || \ + (mode) == FTM_OCMODE_TOGGLE ) + +#define IS_FTM_PWM_MODE(mode) ((mode) == FTM_OCMODE_PWM1 || \ + (mode) == FTM_OCMODE_PWM2) + +#define IS_FTM_CHANNEL(channel) (((channel) & ~7) == 0) + +#define IS_FTM_PULSE(pulse) (((pulse) & ~0xffff) == 0) + +#define FTM_OCPOLARITY_HIGH (0) +#define FTM_OCPOLARITY_LOW (1) + +#define IS_FTM_OC_POLARITY(polarity) ((polarity) == FTM_OCPOLARITY_HIGH || \ + (polarity) == FTM_OCPOLARITY_LOW) + +#define FTM_ICPOLARITY_RISING (FTM_CSC_ELSA) +#define FTM_ICPOLARITY_FALLING (FTM_CSC_ELSB) +#define FTM_ICPOLARITY_BOTH (FTM_CSC_ELSA | FTM_CSC_ELSB) + +#define IS_FTM_IC_POLARITY(polarity) ((polarity) == FTM_ICPOLARITY_RISING || \ + (polarity) == FTM_ICPOLARITY_FALLING || \ + (polarity) == FTM_ICPOLARITY_BOTH) + +typedef enum { + HAL_FTM_STATE_RESET = 0x00, + HAL_FTM_STATE_READY = 0x01, + HAL_FTM_STATE_BUSY = 0x02, +} HAL_FTM_State; + +typedef struct { + FTM_TypeDef *Instance; + FTM_Base_InitTypeDef Init; + HAL_FTM_State State; + +} FTM_HandleTypeDef; + +#define __HAL_FTM_GET_TOF_FLAG(HANDLE) (((HANDLE)->Instance->SC & FTM_SC_TOF) != 0) +#define __HAL_FTM_CLEAR_TOF_FLAG(HANDLE) ((HANDLE)->Instance->SC &= ~FTM_SC_TOF) + +#define __HAL_FTM_GET_TOF_IT(HANDLE) (((HANDLE)->Instance->SC & FTM_SC_TOIE) != 0) +#define __HAL_FTM_ENABLE_TOF_IT(HANDLE) ((HANDLE)->Instance->SC |= FTM_SC_TOIE) +#define __HAL_FTM_DISABLE_TOF_IT(HANDLE) ((HANDLE)->Instance->SC &= ~FTM_SC_TOIE) + +#define __HAL_FTM_GET_CH_FLAG(HANDLE, CH) (((HANDLE)->Instance->channel[CH].CSC & FTM_CSC_CHF) != 0) +#define __HAL_FTM_CLEAR_CH_FLAG(HANDLE, CH) ((HANDLE)->Instance->channel[CH].CSC &= ~FTM_CSC_CHF) + +#define __HAL_FTM_GET_CH_IT(HANDLE, CH) (((HANDLE)->Instance->channel[CH].CSC & FTM_CSC_CHIE) != 0) +#define __HAL_FTM_ENABLE_CH_IT(HANDLE, CH) ((HANDLE)->Instance->channel[CH].CSC |= FTM_CSC_CHIE) +#define __HAL_FTM_DISABLE_CH_IT(HANDLE, CH) ((HANDLE)->Instance->channel[CH].CSC &= ~FTM_CSC_CHIE) + +void HAL_FTM_Base_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_Base_Start(FTM_HandleTypeDef *hftm); +void HAL_FTM_Base_Start_IT(FTM_HandleTypeDef *hftm); +void HAL_FTM_Base_DeInit(FTM_HandleTypeDef *hftm); + +void HAL_FTM_OC_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_OC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel); +void HAL_FTM_OC_Start(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_OC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_OC_DeInit(FTM_HandleTypeDef *hftm); + +void HAL_FTM_PWM_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_PWM_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel); +void HAL_FTM_PWM_Start(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_PWM_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_PWM_DeInit(FTM_HandleTypeDef *hftm); + +void HAL_FTM_IC_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_IC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_IC_InitTypeDef* sConfig, uint32_t channel); +void HAL_FTM_IC_Start(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_IC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_IC_DeInit(FTM_HandleTypeDef *hftm); + + diff --git a/teensy/hal_gpio.c b/teensy/hal_gpio.c index 218560e29b..e65d03410e 100644 --- a/teensy/hal_gpio.c +++ b/teensy/hal_gpio.c @@ -17,7 +17,6 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) if ((GPIO_Init->Pin & bitmask) == 0) { continue; } - volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(GPIOx, position); /*--------------------- GPIO Mode Configuration ------------------------*/ @@ -50,6 +49,8 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) /* Check the Speed parameter */ assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); + *port_pcr |= PORT_PCR_DSE; + /* Configure the IO Speed */ if (GPIO_Init->Speed > GPIO_SPEED_MEDIUM) { *port_pcr &= ~PORT_PCR_SRE; @@ -59,10 +60,12 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) /* Configure the IO Output Type */ if (GPIO_Init->Mode & GPIO_OUTPUT_TYPE) { - *port_pcr |= PORT_PCR_ODE; + *port_pcr |= PORT_PCR_ODE; // OD } else { - *port_pcr &= ~PORT_PCR_ODE; + *port_pcr &= ~PORT_PCR_ODE; // PP } + } else { + *port_pcr &= ~PORT_PCR_DSE; } /* Activate the Pull-up or Pull down resistor for the current IO */ diff --git a/teensy/make-pins.py b/teensy/make-pins.py index 6df1e4162b..f7ba7a04a5 100755 --- a/teensy/make-pins.py +++ b/teensy/make-pins.py @@ -8,7 +8,7 @@ import sys import csv SUPPORTED_FN = { - 'FTM' : ['CH0', 'CH1', 'CH2', 'CH3', + 'FTM' : ['CH0', 'CH1', 'CH2', 'CH3', 'CH4', 'CH5', 'CH6', 'CH7', 'QD_PHA', 'QD_PHB'], 'I2C' : ['SDA', 'SCL'], 'UART' : ['RX', 'TX', 'CTS', 'RTS'], @@ -313,6 +313,17 @@ class Pins(object): print(' { %-*s %s },' % (mux_name_width + 26, key, val), file=af_const_file) + def print_af_py(self, af_py_filename): + with open(af_py_filename, 'wt') as af_py_file: + print('PINS_AF = (', file=af_py_file); + for named_pin in self.board_pins: + print(" ('%s', " % named_pin.name(), end='', file=af_py_file) + for af in named_pin.pin().alt_fn: + if af.is_supported(): + print("(%d, '%s'), " % (af.idx, af.af_str), end='', file=af_py_file) + print('),', file=af_py_file) + print(')', file=af_py_file) + def main(): parser = argparse.ArgumentParser( @@ -324,13 +335,19 @@ def main(): "-a", "--af", dest="af_filename", help="Specifies the alternate function file for the chip", - default="stm32f4xx-af.csv" + default="mk20dx256_af.csv" ) parser.add_argument( "--af-const", dest="af_const_filename", help="Specifies header file for alternate function constants.", - default="build/pins-af-const.h" + default="build/pins_af_const.h" + ) + parser.add_argument( + "--af-py", + dest="af_py_filename", + help="Specifies the filename for the python alternate function mappings.", + default="build/pins_af.py" ) parser.add_argument( "-b", "--board", @@ -341,13 +358,13 @@ def main(): "-p", "--prefix", dest="prefix_filename", help="Specifies beginning portion of generated pins file", - default="stm32f4xx-prefix.c" + default="mk20dx256_prefix.c" ) parser.add_argument( "-q", "--qstr", dest="qstr_filename", help="Specifies name of generated qstr header file", - default="build/pins-qstr.h" + default="build/pins_qstr.h" ) parser.add_argument( "-r", "--hdr", @@ -381,6 +398,7 @@ def main(): pins.print_header(args.hdr_filename) pins.print_qstr(args.qstr_filename) pins.print_af_hdr(args.af_const_filename) + pins.print_af_py(args.af_py_filename) if __name__ == "__main__": diff --git a/teensy/mk20dx256_af.csv b/teensy/mk20dx256_af.csv index 3015c6c7a1..571587de6b 100644 --- a/teensy/mk20dx256_af.csv +++ b/teensy/mk20dx256_af.csv @@ -61,5 +61,5 @@ Pin,Name,Default,ALT0,ALT1,ALT2,ALT3,ALT4,ALT5,ALT6,ALT7,EzPort 60,PTD3,DISABLED,,PTD3,SPI0_SIN,UART2_TX,,FB_AD3,,, 61,PTD4/LLWU_P14,DISABLED,,PTD4/LLWU_P14,SPI0_PCS1,UART0_RTS_b,FTM0_CH4,FB_AD2,EWM_IN,, 62,PTD5,ADC0_SE6b,ADC0_SE6b,PTD5,SPI0_PCS2,UART0_CTS_b/UART0_COL_b,FTM0_CH5,FB_AD1,EWM_OUT_b,, -63,PTD6/LLWU_P15,ADC0_SE7b,ADC0_SE7b,PTD6/LLWU_P15,SPI0_PCS3,UART0_RX,FTM0_CH6,FB_AD0,FTM0_FLT0,, +63,PTD6/LLWU_P15,ADC0_SE7b,ADC0_SE7b,PTD6/LLWU_P15,SPI0_PCS3,UART0_RX,FTM0_CH6,FB_AD0,FTM0_FLT0f,, 64,PTD7,DISABLED,,PTD7,CMT_IRO,UART0_TX,FTM0_CH7,,FTM0_FLT1,, diff --git a/teensy/modpyb.c b/teensy/modpyb.c index 518b93be8f..696554ee4b 100644 --- a/teensy/modpyb.c +++ b/teensy/modpyb.c @@ -43,7 +43,7 @@ #include "pyexec.h" #include "led.h" #include "pin.h" -//#include "timer.h" +#include "timer.h" #include "extint.h" #include "usrsw.h" #include "rng.h" @@ -252,7 +252,7 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_udelay), (mp_obj_t)&pyb_udelay_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&pyb_sync_obj }, -// { MP_OBJ_NEW_QSTR(MP_QSTR_Timer), (mp_obj_t)&pyb_timer_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_Timer), (mp_obj_t)&pyb_timer_type }, //#if MICROPY_HW_ENABLE_RNG // { MP_OBJ_NEW_QSTR(MP_QSTR_rng), (mp_obj_t)&pyb_rng_get_obj }, diff --git a/teensy/mpconfigport.h b/teensy/mpconfigport.h index 5e4b5129be..3528101b37 100644 --- a/teensy/mpconfigport.h +++ b/teensy/mpconfigport.h @@ -20,6 +20,12 @@ #define MICROPY_PY_SYS_STDFILES (1) #define MICROPY_PY_CMATH (1) +#define MICROPY_TIMER_REG (0) +#define MICROPY_REG (MICROPY_TIMER_REG) + +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0) + // extra built in names to add to the global namespace extern const struct _mp_obj_fun_builtin_t mp_builtin_help_obj; extern const struct _mp_obj_fun_builtin_t mp_builtin_input_obj; diff --git a/teensy/pin_defs_teensy.c b/teensy/pin_defs_teensy.c index dd2f0dc21f..35be669970 100644 --- a/teensy/pin_defs_teensy.c +++ b/teensy/pin_defs_teensy.c @@ -14,10 +14,13 @@ // GPIO_MODE_AF_PP, GPIO_MODE_AF_OD, or GPIO_MODE_ANALOG. uint32_t pin_get_mode(const pin_obj_t *pin) { + if (pin->gpio == NULL) { + // Analog only pin + return GPIO_MODE_ANALOG; + } volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); uint32_t pcr = *port_pcr; - uint32_t af = (*port_pcr & PORT_PCR_MUX_MASK) >> 8;; - + uint32_t af = (pcr & PORT_PCR_MUX_MASK) >> 8; if (af == 0) { return GPIO_MODE_ANALOG; } @@ -41,10 +44,18 @@ uint32_t pin_get_mode(const pin_obj_t *pin) { // be one of GPIO_NOPULL, GPIO_PULLUP, or GPIO_PULLDOWN. uint32_t pin_get_pull(const pin_obj_t *pin) { - volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); + if (pin->gpio == NULL) { + // Analog only pin + return GPIO_NOPULL; + } + volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); uint32_t pcr = *port_pcr; - if (pcr & PORT_PCR_PE) { + uint32_t af = (pcr & PORT_PCR_MUX_MASK) >> 8; + + // pull is only valid for digital modes (hence the af > 0 test) + + if (af > 0 && (pcr & PORT_PCR_PE) != 0) { if (pcr & PORT_PCR_PS) { return GPIO_PULLUP; } @@ -56,6 +67,10 @@ uint32_t pin_get_pull(const pin_obj_t *pin) { // Returns the af (alternate function) index currently set for a pin. uint32_t pin_get_af(const pin_obj_t *pin) { + if (pin->gpio == NULL) { + // Analog only pin + return 0; + } volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); return (*port_pcr & PORT_PCR_MUX_MASK) >> 8; } diff --git a/teensy/pin_defs_teensy.h b/teensy/pin_defs_teensy.h index 70fd47d8fd..54a6055f1a 100644 --- a/teensy/pin_defs_teensy.h +++ b/teensy/pin_defs_teensy.h @@ -19,6 +19,10 @@ enum { AF_PIN_TYPE_FTM_CH1, AF_PIN_TYPE_FTM_CH2, AF_PIN_TYPE_FTM_CH3, + AF_PIN_TYPE_FTM_CH4, + AF_PIN_TYPE_FTM_CH5, + AF_PIN_TYPE_FTM_CH6, + AF_PIN_TYPE_FTM_CH7, AF_PIN_TYPE_FTM_QD_PHA, AF_PIN_TYPE_FTM_QD_PHB, diff --git a/teensy/qstrdefsport.h b/teensy/qstrdefsport.h index bdafd14286..44f5d4309d 100644 --- a/teensy/qstrdefsport.h +++ b/teensy/qstrdefsport.h @@ -87,6 +87,42 @@ Q(PULL_NONE) Q(PULL_UP) Q(PULL_DOWN) +// for Timer class +Q(Timer) +Q(init) +Q(deinit) +Q(channel) +Q(counter) +Q(prescaler) +Q(period) +Q(callback) +Q(freq) +Q(mode) +Q(reg) +Q(UP) +Q(CENTER) +Q(IC) +Q(PWM) +Q(PWM_INVERTED) +Q(OC_TIMING) +Q(OC_ACTIVE) +Q(OC_INACTIVE) +Q(OC_TOGGLE) +Q(OC_FORCED_ACTIVE) +Q(OC_FORCED_INACTIVE) +Q(HIGH) +Q(LOW) +Q(RISING) +Q(FALLING) +Q(BOTH) + +// for TimerChannel class +Q(TimerChannel) +Q(pulse_width) +Q(compare) +Q(capture) +Q(polarity) +t // for UART class Q(UART) Q(baudrate) diff --git a/teensy/reg.c b/teensy/reg.c new file mode 100644 index 0000000000..8783a8351f --- /dev/null +++ b/teensy/reg.c @@ -0,0 +1,52 @@ +#include +#include +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" +#include "reg.h" + +#if MICROPY_REG + +mp_obj_t reg_cmd(void *base, reg_t *reg, mp_uint_t num_regs, uint n_args, const mp_obj_t *args) { + if (n_args == 0) { + // dump all regs + + for (mp_uint_t reg_idx = 0; reg_idx < num_regs; reg_idx++, reg++) { + printf(" %-8s @0x%08x = 0x%08lx\n", + reg->name, (mp_uint_t)base + reg->offset, *(uint32_t *)((uint8_t *)base + reg->offset)); + } + return mp_const_none; + } + + mp_uint_t addr = 0; + + if (MP_OBJ_IS_STR(args[0])) { + const char *name = mp_obj_str_get_str(args[0]); + mp_uint_t reg_idx; + for (reg_idx = 0; reg_idx < num_regs; reg_idx++, reg++) { + if (strcmp(name, reg->name) == 0) { + break; + } + } + if (reg_idx >= num_regs) { + printf("Unknown register: '%s'\n", name); + return mp_const_none; + } + addr = (mp_uint_t)base + reg->offset; + } else { + addr = (mp_uint_t)base + mp_obj_get_int(args[0]); + } + + if (n_args < 2) { + // get + printf("0x%08lx\n", *(uint32_t *)addr); + } else { + *(uint32_t *)addr = mp_obj_get_int(args[1]); + } + return mp_const_none; +} + +#endif diff --git a/teensy/reg.h b/teensy/reg.h new file mode 100644 index 0000000000..5d1d27443b --- /dev/null +++ b/teensy/reg.h @@ -0,0 +1,8 @@ +typedef struct { + const char *name; + mp_uint_t offset; +} reg_t; + +#define REG_ENTRY(st, name) { #name, offsetof(st, name) } + +mp_obj_t reg_cmd(void *base, reg_t *reg, mp_uint_t num_reg, uint n_args, const mp_obj_t *args); diff --git a/teensy/teensy_hal.h b/teensy/teensy_hal.h index 39d272f778..d27116d415 100644 --- a/teensy/teensy_hal.h +++ b/teensy/teensy_hal.h @@ -1,4 +1,5 @@ #include +#include "hal_ftm.h" #ifdef USE_FULL_ASSERT #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) @@ -7,9 +8,7 @@ #define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */ -#define FTM0 ((FTM_TypeDef *)&FTM0_SC) -#define FTM1 ((FTM_TypeDef *)&FTM1_SC) -#define FTM2 ((FTM_TypeDef *)&FTM2_SC) +#define HAL_NVIC_EnableIRQ(irq) NVIC_ENABLE_IRQ(irq) #define GPIOA ((GPIO_TypeDef *)&GPIOA_PDOR) #define GPIOB ((GPIO_TypeDef *)&GPIOB_PDOR) @@ -29,10 +28,6 @@ #define UART1 ((UART_TypeDef *)&UART1_BDH) #define UART2 ((UART_TypeDef *)&UART2_BDH) -typedef struct { - uint32_t dummy; -} FTM_TypeDef; - typedef struct { uint32_t dummy; } I2C_TypeDef; @@ -93,10 +88,10 @@ typedef struct { } GPIO_InitTypeDef; #define GPIO_PORT_TO_PORT_NUM(GPIOx) \ - ((GPIOx->PDOR - GPIOA_PDOR) / (GPIOB_PDOR - GPIOA_PDOR)) + ((&GPIOx->PDOR - &GPIOA_PDOR) / (&GPIOB_PDOR - &GPIOA_PDOR)) #define GPIO_PIN_TO_PORT_PCR(GPIOx, pin) \ - (&PORTA_PCR0 + GPIO_PORT_TO_PORT_NUM(GPIOx) * 32 + (pin)) + (&PORTA_PCR0 + (GPIO_PORT_TO_PORT_NUM(GPIOx) * 0x400) + (pin)) #define GPIO_AF2_I2C0 2 #define GPIO_AF2_I2C1 2 diff --git a/teensy/teensy_pins.csv b/teensy/teensy_pins.csv index acaef63aad..10887e2120 100644 --- a/teensy/teensy_pins.csv +++ b/teensy/teensy_pins.csv @@ -53,3 +53,4 @@ A17,PTC8 A18,PTC10 A19,PTC11 A20,PTE0 +LED,PTC5 diff --git a/teensy/timer.c b/teensy/timer.c new file mode 100644 index 0000000000..96678b86cc --- /dev/null +++ b/teensy/timer.c @@ -0,0 +1,897 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" +#include MICROPY_HAL_H +#include "gc.h" +#include "pin.h" +#include "reg.h" + +#include "timer.h" + + +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, FTM_OCMODE_PWM1 }, + { MP_QSTR_PWM_INVERTED, FTM_OCMODE_PWM2 }, + { MP_QSTR_OC_TIMING, FTM_OCMODE_TIMING }, + { MP_QSTR_OC_ACTIVE, FTM_OCMODE_ACTIVE }, + { MP_QSTR_OC_INACTIVE, FTM_OCMODE_INACTIVE }, + { MP_QSTR_OC_TOGGLE, FTM_OCMODE_TOGGLE }, +// { MP_QSTR_OC_FORCED_ACTIVE, FTM_OCMODE_FORCED_ACTIVE }, +// { MP_QSTR_OC_FORCED_INACTIVE, FTM_OCMODE_FORCED_INACTIVE }, + { MP_QSTR_IC, 0 }, +}; + +struct _pyb_timer_obj_t; + +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; + uint8_t tim_id; + uint8_t irqn; + mp_obj_t callback; + FTM_HandleTypeDef ftm; + pyb_timer_channel_obj_t *channel; +} pyb_timer_obj_t; + +// Used to do callbacks to Python code on interrupt +STATIC pyb_timer_obj_t *pyb_timer_obj_all[3]; +#define PYB_TIMER_OBJ_ALL_NUM MP_ARRAY_SIZE(pyb_timer_obj_all) + +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) { + for (uint i = 0; i < PYB_TIMER_OBJ_ALL_NUM; i++) { + pyb_timer_obj_all[i] = NULL; + } +} + +// unregister all interrupt sources +void timer_deinit(void) { + for (uint i = 0; i < PYB_TIMER_OBJ_ALL_NUM; i++) { + pyb_timer_obj_t *tim = pyb_timer_obj_all[i]; + if (tim != NULL) { + pyb_timer_deinit(tim); + } + } +} + +mp_uint_t get_prescaler_shift(mp_int_t prescaler) { + mp_uint_t prescaler_shift; + for (prescaler_shift = 0; prescaler_shift < 8; prescaler_shift++) { + if (prescaler == (1 << prescaler_shift)) { + return prescaler_shift; + } + } + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "prescaler must be a power of 2 between 1 and 128, not %d", prescaler)); +} + +/******************************************************************************/ +/* Micro Python bindings */ + +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; + + if (self->ftm.State == HAL_FTM_STATE_RESET) { + print(env, "Timer(%u)", self->tim_id); + } else { + print(env, "Timer(%u, prescaler=%u, period=%u, mode=%s)", + self->tim_id, + 1 << self->ftm.Init.PrescalerShift, + self->ftm.Init.Period, + self->ftm.Init.CounterMode == FTM_COUNTERMODE_UP ? "tUP" : "CENTER"); + } +} + +/// \method init(*, freq, prescaler, period) +/// Initialise the timer. Initialisation must be either by frequency (in Hz) +/// or by prescaler and period: +/// +/// tim.init(freq=100) # set the timer to trigger at 100Hz +/// 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` 1, 2, 4, 8 16 32, 64 or 128 - specifies the value to be loaded into the +/// timer's prescaler. The timer clock source is divided by +/// (`prescaler`) to arrive at the timer clock. +/// +/// - `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. +/// 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 MOD (default) +/// - `Timer.CENTER` - confgures the timer to count from 0 to MOD and +/// then back down to 0. +/// +/// - `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 = FTM_COUNTERMODE_UP} }, + { 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) + +STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { + // parse args + mp_arg_val_t vals[PYB_TIMER_INIT_NUM_ARGS]; + mp_arg_parse_all(n_args, args, kw_args, PYB_TIMER_INIT_NUM_ARGS, pyb_timer_init_args, vals); + + FTM_HandleTypeDef *ftm = &self->ftm; + + // set the TIM configuration values + FTM_Base_InitTypeDef *init = &ftm->Init; + + if (vals[0].u_int != 0xffffffff) { + // set prescaler and period from frequency + + if (vals[0].u_int == 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't have 0 frequency")); + } + + uint32_t period = MAX(1, F_BUS / vals[0].u_int); + uint32_t prescaler_shift = 0; + while (period > 0x10000 && prescaler_shift < 7) { + period >>= 1; + prescaler_shift++; + } + if (period > 0x10000) { + period = 0x10000; + } + init->PrescalerShift = prescaler_shift; + init->Period = period - 1; + } else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) { + // set prescaler and period directly + init->PrescalerShift = get_prescaler_shift(vals[1].u_int); + init->Period = vals[2].u_int; + if (!IS_FTM_PERIOD(init->Period)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "period must be between 0 and 65535, not %d", init->Period)); + } + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "must specify either freq, or prescaler and period")); + } + + init->CounterMode = vals[3].u_int; + if (!IS_FTM_COUNTERMODE(init->CounterMode)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "invalid counter mode: %d", init->CounterMode)); + } + + // Currently core/mk20dx128.c sets SIM_SCGC6_FTM0, SIM_SCGC6_FTM1, SIM_SCGC3_FTM2 + // so we don't need to do it here. + + NVIC_SET_PRIORITY(self->irqn, 0xe); // next-to lowest priority + + HAL_FTM_Base_Init(ftm); + if (vals[4].u_obj == mp_const_none) { + HAL_FTM_Base_Start(ftm); + } else { + pyb_timer_callback(self, vals[4].u_obj); + } + + return mp_const_none; +} + +/// \classmethod \constructor(id, ...) +/// Construct a new timer object of the given id. If additional +/// arguments are given, then the timer is initialised by `init(...)`. +/// `id` can be 1 to 14, excluding 3. +STATIC mp_obj_t pyb_timer_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // 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; + tim->channel = NULL; + + // get FTM number + tim->tim_id = mp_obj_get_int(args[0]); + + switch (tim->tim_id) { + case 0: tim->ftm.Instance = FTM0; tim->irqn = IRQ_FTM0; break; + case 1: tim->ftm.Instance = FTM1; tim->irqn = IRQ_FTM1; break; + case 2: tim->ftm.Instance = FTM2; tim->irqn = IRQ_FTM2; break; + default: nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d does not exist", tim->tim_id)); + } + + if (n_args > 1 || n_kw > 0) { + // start the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pyb_timer_init_helper(tim, n_args - 1, args + 1, &kw_args); + } + + // set the global variable for interrupt callbacks + if (tim->tim_id < PYB_TIMER_OBJ_ALL_NUM) { + pyb_timer_obj_all[tim->tim_id] = tim; + } + + return (mp_obj_t)tim; +} + +STATIC mp_obj_t pyb_timer_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return pyb_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_init_obj, 1, pyb_timer_init); + +/// \method deinit() +/// Deinitialises the timer. +/// +/// Disables the callback (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 + 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_FTM_Base_DeInit(&self->ftm); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit); + +/// \method channel(channel, ...) +/// +/// 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.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(0, prescaler=128, period=37500, counter_mode=pyb.Timer.COUNTER_MODE_CENTER) +/// ch0 = t0.channel(0, pyb.Timer.PWM, pin=pyb.Pin.board.D22, pulse_width=(t0.period() + 1) // 4) +/// ch1 = t0.channel(1, pyb.Timer.PWM, pin=pyb.Pin.board.D23, pulse_width=(t0.period() + 1) // 2) +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 - 2, 2, 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 < 0 || channel > 7) { + 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_FTM, 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: { + FTM_OC_InitTypeDef oc_config; + oc_config.OCMode = gChannelMode[chan->mode].oc_mode; + oc_config.Pulse = vals[2].u_int; + oc_config.OCPolarity = FTM_OCPOLARITY_HIGH; + + HAL_FTM_PWM_ConfigChannel(&self->ftm, &oc_config, channel); + if (chan->callback == mp_const_none) { + HAL_FTM_PWM_Start(&self->ftm, channel); + } else { + HAL_FTM_PWM_Start_IT(&self->ftm, channel); + } + break; + } + + case CHANNEL_MODE_OC_TIMING: + case CHANNEL_MODE_OC_ACTIVE: + case CHANNEL_MODE_OC_INACTIVE: + case CHANNEL_MODE_OC_TOGGLE: { + FTM_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 = FTM_OCPOLARITY_HIGH; + } + + if (!IS_FTM_OC_POLARITY(oc_config.OCPolarity)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid polarity (%d)", oc_config.OCPolarity)); + } + HAL_FTM_OC_ConfigChannel(&self->ftm, &oc_config, channel); + if (chan->callback == mp_const_none) { + HAL_FTM_OC_Start(&self->ftm, channel); + } else { + HAL_FTM_OC_Start_IT(&self->ftm, channel); + } + break; + } + + case CHANNEL_MODE_IC: { + FTM_IC_InitTypeDef ic_config; + + ic_config.ICPolarity = vals[4].u_int; + if (ic_config.ICPolarity == 0xffffffff) { + ic_config.ICPolarity = FTM_ICPOLARITY_RISING; + } + + if (!IS_FTM_IC_POLARITY(ic_config.ICPolarity)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid polarity (%d)", ic_config.ICPolarity)); + } + HAL_FTM_IC_ConfigChannel(&self->ftm, &ic_config, chan->channel); + if (chan->callback == mp_const_none) { + HAL_FTM_IC_Start(&self->ftm, channel); + } else { + HAL_FTM_IC_Start_IT(&self->ftm, channel); + } + 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(uint 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->ftm.Instance->CNT); + } + // set - In order to write to CNT we need to set CNTIN + self->ftm.Instance->CNTIN = mp_obj_get_int(args[1]); + self->ftm.Instance->CNT = 0; // write any value to load CNTIN into CNT + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_counter_obj, 1, 2, pyb_timer_counter); + +/// \method prescaler([value]) +/// Get or set the prescaler for the timer. +mp_obj_t pyb_timer_prescaler(uint n_args, const mp_obj_t *args) { + pyb_timer_obj_t *self = args[0]; + if (n_args == 1) { + // get + return mp_obj_new_int(1 << (self->ftm.Instance->SC & 7)); + } + + // set + mp_uint_t prescaler_shift = get_prescaler_shift(mp_obj_get_int(args[1])); + + mp_uint_t sc = self->ftm.Instance->SC; + sc &= ~7; + sc |= FTM_SC_PS(prescaler_shift); + self->ftm.Instance->SC = sc; + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_prescaler_obj, 1, 2, pyb_timer_prescaler); + +/// \method period([value]) +/// Get or set the period of the timer. +mp_obj_t pyb_timer_period(uint 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->ftm.Instance->MOD & 0xffff); + } + + // set + mp_int_t period = mp_obj_get_int(args[1]) & 0xffff; + self->ftm.Instance->CNT = 0; + self->ftm.Instance->MOD = period; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_period_obj, 1, 2, pyb_timer_period); + +/// \method callback(fun) +/// Set the function to be called when the timer 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_callback(mp_obj_t self_in, mp_obj_t callback) { + pyb_timer_obj_t *self = self_in; + if (callback == mp_const_none) { + // stop interrupt (but not timer) + __HAL_FTM_DISABLE_TOF_IT(&self->ftm); + self->callback = mp_const_none; + } else if (mp_obj_is_callable(callback)) { + self->callback = callback; + HAL_NVIC_EnableIRQ(self->irqn); + // start timer, so that it interrupts on overflow + HAL_FTM_Base_Start_IT(&self->ftm); + } 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_callback_obj, pyb_timer_callback); + +#if MICROPY_TIMER_REG +reg_t timer_reg[] = { + REG_ENTRY(FTM_TypeDef, SC), + REG_ENTRY(FTM_TypeDef, CNT), + REG_ENTRY(FTM_TypeDef, MOD), + REG_ENTRY(FTM_TypeDef, CNTIN), + REG_ENTRY(FTM_TypeDef, STATUS), + REG_ENTRY(FTM_TypeDef, MODE), + REG_ENTRY(FTM_TypeDef, SYNC), + REG_ENTRY(FTM_TypeDef, OUTINIT), + REG_ENTRY(FTM_TypeDef, OUTMASK), + REG_ENTRY(FTM_TypeDef, COMBINE), + REG_ENTRY(FTM_TypeDef, DEADTIME), + REG_ENTRY(FTM_TypeDef, EXTTRIG), + REG_ENTRY(FTM_TypeDef, POL), + REG_ENTRY(FTM_TypeDef, FMS), + REG_ENTRY(FTM_TypeDef, FILTER), + REG_ENTRY(FTM_TypeDef, FLTCTRL), + REG_ENTRY(FTM_TypeDef, QDCTRL), + REG_ENTRY(FTM_TypeDef, CONF), + REG_ENTRY(FTM_TypeDef, FLTPOL), + REG_ENTRY(FTM_TypeDef, SYNCONF), + REG_ENTRY(FTM_TypeDef, INVCTRL), + REG_ENTRY(FTM_TypeDef, SWOCTRL), + REG_ENTRY(FTM_TypeDef, PWMLOAD), +}; + +mp_obj_t pyb_timer_reg(uint n_args, const mp_obj_t *args) { + pyb_timer_obj_t *self = args[0]; + return reg_cmd(self->ftm.Instance, timer_reg, MP_ARRAY_SIZE(timer_reg), n_args - 1, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_reg_obj, 1, 3, pyb_timer_reg); +#endif // MICROPY_TIMER_REG + +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 }, +#if MICROPY_TIMER_REG + { MP_OBJ_NEW_QSTR(MP_QSTR_reg), (mp_obj_t)&pyb_timer_reg_obj }, +#endif + { MP_OBJ_NEW_QSTR(MP_QSTR_UP), MP_OBJ_NEW_SMALL_INT(FTM_COUNTERMODE_UP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CENTER), MP_OBJ_NEW_SMALL_INT(FTM_COUNTERMODE_CENTER) }, + { 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_IC), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_IC) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_HIGH), MP_OBJ_NEW_SMALL_INT(FTM_OCPOLARITY_HIGH) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LOW), MP_OBJ_NEW_SMALL_INT(FTM_OCPOLARITY_LOW) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RISING), MP_OBJ_NEW_SMALL_INT(FTM_ICPOLARITY_RISING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_FALLING), MP_OBJ_NEW_SMALL_INT(FTM_ICPOLARITY_FALLING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BOTH), MP_OBJ_NEW_SMALL_INT(FTM_ICPOLARITY_BOTH) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pyb_timer_locals_dict, pyb_timer_locals_dict_table); + +const mp_obj_type_t pyb_timer_type = { + { &mp_type_type }, + .name = MP_QSTR_Timer, + .print = pyb_timer_print, + .make_new = pyb_timer_make_new, + .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 == 0xffffffff) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d no channel specified", self->timer->tim_id)); + } + FTM_TypeDef *FTMx = self->timer->ftm.Instance; + if (n_args == 1) { + // get + return mp_obj_new_int(FTMx->channel[self->channel].CV); + } + + mp_int_t pw = mp_obj_get_int(args[1]); + + // set + FTMx->channel[self->channel].CV = pw; + 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_FTM_DISABLE_CH_IT(&self->timer->ftm, 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_FTM_PWM_Start_IT(&self->timer->ftm, self->channel); + break; + case CHANNEL_MODE_OC_TIMING: + case CHANNEL_MODE_OC_ACTIVE: + case CHANNEL_MODE_OC_INACTIVE: + case CHANNEL_MODE_OC_TOGGLE: + HAL_FTM_OC_Start_IT(&self->timer->ftm, self->channel); + break; + case CHANNEL_MODE_IC: + HAL_FTM_IC_Start_IT(&self->timer->ftm, self->channel); + 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); + +#if MICROPY_TIMER_REG +reg_t timer_channel_reg[] = { + REG_ENTRY(FTM_ChannelTypeDef, CSC), + REG_ENTRY(FTM_ChannelTypeDef, CV), +}; + +mp_obj_t pyb_timer_channel_reg(uint n_args, const mp_obj_t *args) { + pyb_timer_channel_obj_t *self = args[0]; + return reg_cmd(&self->timer->ftm.Instance->channel[self->channel], + timer_channel_reg, MP_ARRAY_SIZE(timer_channel_reg), + n_args - 1, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_reg_obj, 1, 3, pyb_timer_channel_reg); +#endif + +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 }, +#if MICROPY_TIMER_REG + { MP_OBJ_NEW_QSTR(MP_QSTR_reg), (mp_obj_t)&pyb_timer_channel_reg_obj }, +#endif +}; +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, +}; + +STATIC bool ftm_handle_irq_callback(pyb_timer_obj_t *self, mp_uint_t channel, mp_obj_t callback) { + // execute callback if it's set + if (callback == mp_const_none) { + return false; + } + bool handled = false; + + // 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, self); + nlr_pop(); + handled = true; + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->callback = mp_const_none; + if (channel == 0xffffffff) { + printf("Uncaught exception in Timer(" UINT_FMT + ") interrupt handler\n", self->tim_id); + } else { + printf("Uncaught exception in Timer(" UINT_FMT ") channel " + UINT_FMT " interrupt handler\n", self->tim_id, channel); + } + mp_obj_print_exception((mp_obj_t)nlr.ret_val); + } + gc_unlock(); + return handled; +} + +STATIC void ftm_irq_handler(uint tim_id) { + if (tim_id >= PYB_TIMER_OBJ_ALL_NUM) { + return; + } + // get the timer object + pyb_timer_obj_t *self = pyb_timer_obj_all[tim_id]; + if (self == NULL) { + // timer object has not been set, so we can't do anything + printf("No timer object for id=%d\n", tim_id); + return; + } + FTM_HandleTypeDef *hftm = &self->ftm; + + bool handled = false; + + // Check for timer (versus timer channel) interrupt. + if (__HAL_FTM_GET_TOF_IT(hftm) && __HAL_FTM_GET_TOF_FLAG(hftm)) { + __HAL_FTM_CLEAR_TOF_FLAG(hftm); + if (ftm_handle_irq_callback(self, 0xffffffff, self->callback)) { + handled = true; + } else { + __HAL_FTM_DISABLE_TOF_IT(&self->ftm); + printf("No callback for Timer %d TOF (now disabled)\n", tim_id); + } + } + + uint32_t processed = 0; + + // Check to see if a timer channel interrupt is pending + pyb_timer_channel_obj_t *chan = self->channel; + while (chan != NULL) { + processed |= (1 << chan->channel); + if (__HAL_FTM_GET_CH_IT(&self->ftm, chan->channel) && __HAL_FTM_GET_CH_FLAG(&self->ftm, chan->channel)) { + __HAL_FTM_CLEAR_CH_FLAG(&self->ftm, chan->channel); + if (ftm_handle_irq_callback(self, chan->channel, chan->callback)) { + handled = true; + } else { + __HAL_FTM_DISABLE_CH_IT(&self->ftm, chan->channel); + printf("No callback for Timer %d channel %u (now disabled)\n", + self->tim_id, chan->channel); + } + } + chan = chan->next; + } + + if (!handled) { + // An interrupt occurred for a channel we didn't process. Find it and + // turn it off. + for (mp_uint_t channel = 0; channel < 8; channel++) { + if ((processed & (1 << channel)) == 0) { + if (__HAL_FTM_GET_CH_FLAG(&self->ftm, channel) != 0) { + __HAL_FTM_CLEAR_CH_FLAG(&self->ftm, channel); + __HAL_FTM_DISABLE_CH_IT(&self->ftm, channel); + printf("Unhandled interrupt Timer %d channel %u (now disabled)\n", + tim_id, channel); + } + } + } + } +} + +void ftm0_isr(void) { + ftm_irq_handler(0); +} + +void ftm1_isr(void) { + ftm_irq_handler(1); +} + +void ftm2_isr(void) { + ftm_irq_handler(2); +} diff --git a/teensy/timer.h b/teensy/timer.h new file mode 100644 index 0000000000..a7b2978564 --- /dev/null +++ b/teensy/timer.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +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_deinit(void);