/* * This file is part of the Micro Python project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2015 Daniel Campora * * 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 "py/mpconfig.h" #include MICROPY_HAL_H #include "py/obj.h" #include "py/nlr.h" #include "py/runtime.h" #include "py/gc.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "inc/hw_timer.h" #include "rom_map.h" #include "interrupt.h" #include "prcm.h" #include "timer.h" #include "pybtimer.h" #include "pybsleep.h" #include "mpcallback.h" #include "mpexception.h" /// \moduleref pyb /// \class Timer - generate periodic events, count events, and create PWM signals. /// /// Each timer consists of a counter that counts up at a certain rate. The rate /// at which it counts is the peripheral clock frequency (in Hz) divided by the /// timer prescaler. When the counter reaches the timer period it triggers an /// event, and the counter resets back to zero. By using the callback method, /// the timer event can call a Python function. /// /// Example usage to toggle an LED at a fixed frequency: /// /// tim = pyb.Timer(4) # create a timer object using timer 4 /// tim.init(mode=Timer.PERIODIC) # initialize it in periodic mode /// tim_ch = tim.channel(Timer.A, freq=2) # configure channel A at a frequency of 2Hz /// tim_ch.callback(handler=lambda t:led.toggle()) # toggle a LED on every cycle of the timer /// /// Further examples: /// /// tim1 = pyb.Timer(2, mode=Timer.EVENT_COUNT) # initialize it capture mode /// tim2 = pyb.Timer(1, mode=Timer.PWM) # initialize it in PWM mode /// tim_ch = tim1.channel(Timer.A, freq=1, polarity=Timer.POSITIVE) # start the event counter with a frequency of 1Hz and triggered by positive edges /// tim_ch = tim2.channel(Timer.B, freq=10000, duty_cycle=50) # start the PWM on channel B with a 50% duty cycle /// tim_ch.time() # get the current time in usec (can also be set) /// tim_ch.freq(20) # set the frequency (can also get) /// tim_ch.duty_cycle(30) # set the duty cycle to 30% (can also get) /// tim_ch.duty_cycle(30, Timer.NEGATIVE) # set the duty cycle to 30% and change the polarity to negative /// tim_ch.event_count() # get the number of captured events /// tim_ch.event_time() # get the the time of the last captured event /// tim_ch.period(2000000) # change the period to 2 seconds /// /****************************************************************************** DECLARE PRIVATE CONSTANTS ******************************************************************************/ #define PYBTIMER_NUM_TIMERS (4) #define PYBTIMER_POLARITY_POS (0x01) #define PYBTIMER_POLARITY_NEG (0x02) #define PYBTIMER_SRC_FREQ_HZ HAL_FCPU_HZ /****************************************************************************** DEFINE PRIVATE TYPES ******************************************************************************/ typedef struct _pyb_timer_obj_t { mp_obj_base_t base; uint32_t timer; uint32_t config; uint16_t intflags; uint8_t peripheral; uint8_t id; } pyb_timer_obj_t; typedef struct _pyb_timer_channel_obj_t { mp_obj_base_t base; struct _pyb_timer_obj_t *timer; uint32_t frequency; uint32_t period; uint16_t channel; uint8_t polarity; uint8_t duty_cycle; } pyb_timer_channel_obj_t; /****************************************************************************** DEFINE PRIVATE DATA ******************************************************************************/ STATIC const mp_cb_methods_t pyb_timer_channel_cb_methods; STATIC pyb_timer_obj_t pyb_timer_obj[PYBTIMER_NUM_TIMERS] = {{.timer = TIMERA0_BASE, .peripheral = PRCM_TIMERA0}, {.timer = TIMERA1_BASE, .peripheral = PRCM_TIMERA1}, {.timer = TIMERA2_BASE, .peripheral = PRCM_TIMERA2}, {.timer = TIMERA3_BASE, .peripheral = PRCM_TIMERA3}}; STATIC const mp_obj_type_t pyb_timer_channel_type; /****************************************************************************** DECLARE PRIVATE FUNCTIONS ******************************************************************************/ STATIC mp_obj_t pyb_timer_channel_callback (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); STATIC void timer_disable (pyb_timer_obj_t *tim); STATIC void TIMER0AIntHandler(void); STATIC void TIMER0BIntHandler(void); STATIC void TIMER1AIntHandler(void); STATIC void TIMER1BIntHandler(void); STATIC void TIMER2AIntHandler(void); STATIC void TIMER2BIntHandler(void); STATIC void TIMER3AIntHandler(void); STATIC void TIMER3BIntHandler(void); /****************************************************************************** DEFINE PUBLIC FUNCTIONS ******************************************************************************/ void timer_init0 (void) { mp_obj_list_init(&MP_STATE_PORT(pyb_timer_channel_obj_list), 0); } void pyb_timer_channel_callback_enable (mp_obj_t self_in) { pyb_timer_channel_obj_t *self = self_in; MAP_TimerIntClear(self->timer->timer, self->timer->intflags & self->channel); MAP_TimerIntEnable(self->timer->timer, self->timer->intflags & self->channel); } void pyb_timer_channel_callback_disable (mp_obj_t self_in) { pyb_timer_channel_obj_t *self = self_in; MAP_TimerIntDisable(self->timer->timer, self->timer->intflags & self->channel); } pyb_timer_channel_obj_t *pyb_timer_channel_find (uint32_t timer, uint16_t channel_n) { for (mp_uint_t i = 0; i < MP_STATE_PORT(pyb_timer_channel_obj_list).len; i++) { pyb_timer_channel_obj_t *ch = ((pyb_timer_channel_obj_t *)(MP_STATE_PORT(pyb_timer_channel_obj_list).items[i])); // any 32-bit timer must be matched by any of its 16-bit versions if (ch->timer->timer == timer && ((ch->channel & TIMER_A) == channel_n || (ch->channel & TIMER_B) == channel_n)) { return ch; } } return MP_OBJ_NULL; } void pyb_timer_channel_remove (pyb_timer_channel_obj_t *ch) { pyb_timer_channel_obj_t *channel; if ((channel = pyb_timer_channel_find(ch->timer->timer, ch->channel))) { mp_obj_list_remove(&MP_STATE_PORT(pyb_timer_channel_obj_list), channel); } } void pyb_timer_channel_add (pyb_timer_channel_obj_t *ch) { // remove it in case it already exists pyb_timer_channel_remove(ch); mp_obj_list_append(&MP_STATE_PORT(pyb_timer_channel_obj_list), ch); } STATIC void timer_disable (pyb_timer_obj_t *tim) { // disable all timers and it's interrupts MAP_TimerDisable(tim->timer, TIMER_A | TIMER_B); MAP_TimerIntDisable(tim->timer, tim->intflags); MAP_TimerIntClear(tim->timer, tim->intflags); MAP_PRCMPeripheralClkDisable(tim->peripheral, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); memset(&pyb_timer_obj[tim->id], 0, sizeof(pyb_timer_obj_t)); } // computes prescaler period and match value so timer triggers at freq-Hz STATIC uint32_t compute_prescaler_period_and_match_value(pyb_timer_channel_obj_t *ch, uint32_t *period_out, uint32_t *match_out) { uint32_t maxcount = (ch->channel == (TIMER_A | TIMER_B)) ? 0xFFFFFFFF : 0xFFFF; uint32_t prescaler; uint32_t period_c = (ch->frequency > 0) ? PYBTIMER_SRC_FREQ_HZ / ch->frequency : ((PYBTIMER_SRC_FREQ_HZ / 1000000) * ch->period); period_c = MAX(1, period_c) - 1; if (period_c == 0) { goto error; } prescaler = period_c >> 16; *period_out = period_c; if (prescaler > 0xFF && maxcount == 0xFFFF) { goto error; } // check limit values for the duty cycle if (ch->duty_cycle == 0) { *match_out = period_c - 1; } else { *match_out = period_c - ((period_c * ch->duty_cycle) / 100); } if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM && (*match_out > 0xFFFF)) { goto error; } return prescaler; error: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); } STATIC void timer_init (pyb_timer_obj_t *tim) { MAP_PRCMPeripheralClkEnable(tim->peripheral, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); MAP_PRCMPeripheralReset(tim->peripheral); MAP_TimerConfigure(tim->timer, tim->config); } STATIC void timer_channel_init (pyb_timer_channel_obj_t *ch) { // calculate the period, the prescaler and the match value uint32_t period_c; uint32_t match; uint32_t prescaler = compute_prescaler_period_and_match_value(ch, &period_c, &match); // set the prescaler MAP_TimerPrescaleSet(ch->timer->timer, ch->channel, (prescaler < 0xFF) ? prescaler : 0); // set the load value MAP_TimerLoadSet(ch->timer->timer, ch->channel, period_c); // configure the pwm if we are in such mode if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM) { // invert the timer output if required MAP_TimerControlLevel(ch->timer->timer, ch->channel, (ch->polarity == PYBTIMER_POLARITY_NEG) ? true : false); // set the match value (which is simply the duty cycle translated to ticks) MAP_TimerMatchSet(ch->timer->timer, ch->channel, match); } // configure the event edge type if we are in such mode else if ((ch->timer->config & 0x0F) == TIMER_CFG_A_CAP_COUNT || (ch->timer->config & 0x0F) == TIMER_CFG_A_CAP_TIME) { uint32_t polarity = TIMER_EVENT_BOTH_EDGES; if (ch->polarity == PYBTIMER_POLARITY_POS) { polarity = TIMER_EVENT_POS_EDGE; } else if (ch->polarity == PYBTIMER_POLARITY_NEG) { polarity = TIMER_EVENT_NEG_EDGE; } MAP_TimerControlEvent(ch->timer->timer, ch->channel, polarity); } #ifdef DEBUG // stall the timer when the processor is halted while debugging MAP_TimerControlStall(ch->timer->timer, ch->channel, true); #endif // now enable the timer channel MAP_TimerEnable(ch->timer->timer, ch->channel); } /******************************************************************************/ /* Micro Python bindings */ STATIC void pyb_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_timer_obj_t *tim = self_in; uint32_t mode = tim->config & 0xFF; // timer mode qstr mode_qst = MP_QSTR_PWM; switch(mode) { case TIMER_CFG_A_ONE_SHOT: mode_qst = MP_QSTR_ONE_SHOT; break; case TIMER_CFG_A_PERIODIC: mode_qst = MP_QSTR_PERIODIC; break; case TIMER_CFG_A_CAP_COUNT: mode_qst = MP_QSTR_EDGE_COUNT; break; case TIMER_CFG_A_CAP_TIME: mode_qst = MP_QSTR_EDGE_TIME; break; default: break; } mp_printf(print, "", (tim->id + 1), mode_qst); } /// \method init(mode, *, width) /// Initialise the timer. Initialisation must give the desired mode /// and an optional timer width /// /// tim.init(mode=Timer.ONE_SHOT, width=32) # one shot mode /// tim.init(mode=Timer.PERIODIC) # configure in free running periodic mode /// split into two 16-bit independent timers /// /// Keyword arguments: /// /// - `width` - specifies the width of the timer. Default is 32 bit mode. When in 16 bit mode /// the timer is splitted into 2 independent channels. /// STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *tim, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, }, { MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} }, }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // check the mode uint32_t _mode = args[0].u_int; if (_mode != TIMER_CFG_A_ONE_SHOT && _mode != TIMER_CFG_A_PERIODIC && _mode != TIMER_CFG_A_CAP_COUNT && _mode != TIMER_CFG_A_CAP_TIME && _mode != TIMER_CFG_A_PWM) { goto error; } // check the width if (args[1].u_int != 16 && args[1].u_int != 32) { goto error; } bool is16bit = (args[1].u_int == 16); if (!is16bit && (_mode != TIMER_CFG_A_ONE_SHOT && _mode != TIMER_CFG_A_PERIODIC)) { // 32-bit mode is only available when in free running modes goto error; } tim->config = is16bit ? ((_mode | (_mode << 8)) | TIMER_CFG_SPLIT_PAIR) : _mode; timer_init(tim); // register it with the sleep module pybsleep_add ((const mp_obj_t)tim, (WakeUpCB_t)timer_init); return mp_const_none; error: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); } /// \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 4 STATIC mp_obj_t pyb_timer_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t 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 a new Timer object int32_t timer_idx = mp_obj_get_int(args[0]) - 1; if (timer_idx < 0 || timer_idx > (PYBTIMER_NUM_TIMERS - 1)) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_resource_not_avaliable)); } pyb_timer_obj_t *tim = &pyb_timer_obj[timer_idx]; tim->base.type = &pyb_timer_type; tim->id = timer_idx; 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); } return (mp_obj_t)tim; } // \method init() /// initializes the timer STATIC mp_obj_t pyb_timer_init(mp_uint_t 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() /// disables the timer STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in) { pyb_timer_obj_t *self = self_in; timer_disable(self); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit); /// \method channel(channel, *, freq, period, polarity, duty_cycle) /// Initialise the timer channel. Initialization requires at least a frequency param. With no /// extra params given besides the channel id, the channel is returned with the previous configuration /// os 'None', if it hasn't been initialized before. /// /// tim1.channel(Timer.A, freq=1000) # set channel A frequency to 1KHz /// tim2.channel(Timer.AB, freq=10) # both channels (because it's a 32 bit timer) combined to create a 10Hz timer /// /// when initialiazing the channel of a 32-bit timer, channel ID MUST be = Timer.AB /// /// Keyword arguments: /// /// - `freq` - specifies the frequency in Hz. /// - `period` - specifies the period in microseconds. /// - `polarity` - in PWM specifies the polarity of the pulse. In capture mode specifies the edge to capture. /// in order to capture on both negative and positive edges, make it = Timer.POSITIVE | Timer.NEGATIVE. /// - `duty_cycle` - sets the duty cycle value /// STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PYBTIMER_POLARITY_POS} }, { MP_QSTR_duty_cycle, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; pyb_timer_obj_t *tim = pos_args[0]; mp_int_t channel_n = mp_obj_get_int(pos_args[1]); // verify that the timer has been already initialized if (!tim->config) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_request_not_possible)); } if (channel_n != TIMER_A && channel_n != TIMER_B && channel_n != (TIMER_A | TIMER_B)) { // invalid channel goto error; } if (channel_n == (TIMER_A | TIMER_B) && (tim->config & TIMER_CFG_SPLIT_PAIR)) { // 32-bit channel selected when the timer is in 16-bit mode goto error; } // if only the channel number is given return the previously // allocated channel (or None if no previous channel) if (n_args == 2 && kw_args->used == 0) { pyb_timer_channel_obj_t *ch; if ((ch = pyb_timer_channel_find(tim->timer, channel_n))) { return ch; } return mp_const_none; } // parse the arguments mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // throw an exception if both frequency and period are given if (args[0].u_int != 0 && args[1].u_int != 0) { goto error; } // check that at least one of them has a valid value if (args[0].u_int <= 0 && args[1].u_int <= 0) { goto error; } // check that the polarity is not 'both' in pwm mode if ((tim->config & TIMER_A) == TIMER_CFG_A_PWM && args[2].u_int == (PYBTIMER_POLARITY_POS | PYBTIMER_POLARITY_NEG)) { goto error; } // allocate a new timer channel pyb_timer_channel_obj_t *ch = m_new_obj(pyb_timer_channel_obj_t); ch->base.type = &pyb_timer_channel_type; ch->timer = tim; ch->channel = channel_n; // get the frequency the polarity and the duty cycle ch->frequency = args[0].u_int; ch->period = args[1].u_int; ch->polarity = args[2].u_int; ch->duty_cycle = MIN(100, MAX(0, args[3].u_int)); timer_channel_init(ch); // register it with the sleep module pybsleep_add ((const mp_obj_t)ch, (WakeUpCB_t)timer_channel_init); // add the timer to the list pyb_timer_channel_add(ch); return ch; error: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_channel_obj, 2, pyb_timer_channel); 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 }, // class constants { MP_OBJ_NEW_QSTR(MP_QSTR_A), MP_OBJ_NEW_SMALL_INT(TIMER_A) }, { MP_OBJ_NEW_QSTR(MP_QSTR_B), MP_OBJ_NEW_SMALL_INT(TIMER_B) }, { MP_OBJ_NEW_QSTR(MP_QSTR_ONE_SHOT), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_ONE_SHOT) }, { MP_OBJ_NEW_QSTR(MP_QSTR_PERIODIC), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_PERIODIC) }, { MP_OBJ_NEW_QSTR(MP_QSTR_EDGE_COUNT), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_CAP_COUNT) }, { MP_OBJ_NEW_QSTR(MP_QSTR_EDGE_TIME), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_CAP_TIME) }, { MP_OBJ_NEW_QSTR(MP_QSTR_PWM), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_PWM) }, { MP_OBJ_NEW_QSTR(MP_QSTR_POSITIVE), MP_OBJ_NEW_SMALL_INT(PYBTIMER_POLARITY_POS) }, { MP_OBJ_NEW_QSTR(MP_QSTR_NEGATIVE), MP_OBJ_NEW_SMALL_INT(PYBTIMER_POLARITY_NEG) }, }; 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, }; STATIC const mp_cb_methods_t pyb_timer_channel_cb_methods = { .init = pyb_timer_channel_callback, .enable = pyb_timer_channel_callback_enable, .disable = pyb_timer_channel_callback_disable, }; STATIC void TIMERGenericIntHandler(uint32_t timer, uint16_t channel) { pyb_timer_channel_obj_t *self; uint32_t status; if ((self = pyb_timer_channel_find(timer, channel))) { status = MAP_TimerIntStatus(self->timer->timer, true) & self->channel; MAP_TimerIntClear(self->timer->timer, status); mp_obj_t _callback = mpcallback_find(self); mpcallback_handler(_callback); } } STATIC void TIMER0AIntHandler(void) { TIMERGenericIntHandler(TIMERA0_BASE, TIMER_A); } STATIC void TIMER0BIntHandler(void) { TIMERGenericIntHandler(TIMERA0_BASE, TIMER_B); } STATIC void TIMER1AIntHandler(void) { TIMERGenericIntHandler(TIMERA1_BASE, TIMER_A); } STATIC void TIMER1BIntHandler(void) { TIMERGenericIntHandler(TIMERA1_BASE, TIMER_B); } STATIC void TIMER2AIntHandler(void) { TIMERGenericIntHandler(TIMERA2_BASE, TIMER_A); } STATIC void TIMER2BIntHandler(void) { TIMERGenericIntHandler(TIMERA2_BASE, TIMER_B); } STATIC void TIMER3AIntHandler(void) { TIMERGenericIntHandler(TIMERA3_BASE, TIMER_A); } STATIC void TIMER3BIntHandler(void) { TIMERGenericIntHandler(TIMERA3_BASE, TIMER_B); } STATIC void pyb_timer_channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_timer_channel_obj_t *ch = self_in; char *ch_id = "AB"; // timer channel if (ch->channel == TIMER_A) { ch_id = "A"; } else if (ch->channel == TIMER_B) { ch_id = "B"; } mp_printf(print, "<%q %s, timer=%u, %q=%u", MP_QSTR_TimerChannel, ch_id, (ch->timer->id + 1), MP_QSTR_freq, ch->frequency); uint32_t mode = ch->timer->config & 0xFF; if (mode == TIMER_CFG_A_CAP_COUNT || mode == TIMER_CFG_A_CAP_TIME || mode == TIMER_CFG_A_PWM) { mp_printf(print, ", %q=Timer.", MP_QSTR_polarity); switch (ch->polarity) { case PYBTIMER_POLARITY_POS: mp_printf(print, "POSITIVE"); break; case PYBTIMER_POLARITY_NEG: mp_printf(print, "NEGATIVE"); break; default: mp_printf(print, "BOTH"); break; } if (mode == TIMER_CFG_A_PWM) { mp_printf(print, ", %q=%u", MP_QSTR_duty_cycle, ch->duty_cycle); } } mp_printf(print, ">"); } /// \method freq([value]) /// get or set the frequency of the timer channel STATIC mp_obj_t pyb_timer_channel_freq(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *ch = args[0]; if (n_args == 1) { // get return mp_obj_new_int(ch->frequency); } else { // set int32_t _frequency = mp_obj_get_int(args[1]); if (_frequency <= 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); } ch->frequency = _frequency; ch->period = 1000000 / _frequency; timer_channel_init(ch); return mp_const_none; } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_freq_obj, 1, 2, pyb_timer_channel_freq); /// \method period([value]) /// get or set the period of the timer channel in microseconds STATIC mp_obj_t pyb_timer_channel_period(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *ch = args[0]; if (n_args == 1) { // get return mp_obj_new_int(ch->period); } else { // set int32_t _period = mp_obj_get_int(args[1]); if (_period <= 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); } ch->period = _period; ch->frequency = 1000000 / _period; timer_channel_init(ch); return mp_const_none; } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_period_obj, 1, 2, pyb_timer_channel_period); /// \method time([value]) /// get or set the value of the timer channel in microseconds STATIC mp_obj_t pyb_timer_channel_time(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *ch = args[0]; uint32_t value; // calculate the period, the prescaler and the match value uint32_t period_c; uint32_t match; (void)compute_prescaler_period_and_match_value(ch, &period_c, &match); if (n_args == 1) { // get value = (ch->channel == TIMER_B) ? HWREG(ch->timer->timer + TIMER_O_TBV) : HWREG(ch->timer->timer + TIMER_O_TAV); // return the current timer value in microseconds // substract value to period since we are always operating in count-down mode uint32_t time_t = (1000 * (period_c - value)) / period_c; return mp_obj_new_int((time_t * 1000) / ch->frequency); } else { // set value = (mp_obj_get_int(args[1]) * ((ch->frequency * period_c) / 1000)) / 1000; if ((value > 0xFFFF) && (ch->timer->config & TIMER_CFG_SPLIT_PAIR)) { // this exceeds the maximum value of a 16-bit timer nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); } // write period minus value since we are always operating in count-down mode TimerValueSet (ch->timer->timer, ch->channel, (period_c - value)); return mp_const_none; } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_time_obj, 1, 2, pyb_timer_channel_time); /// \method event_count() /// get the number of events triggered by the configured edge STATIC mp_obj_t pyb_timer_channel_event_count(mp_obj_t self_in) { pyb_timer_channel_obj_t *ch = self_in; return mp_obj_new_int(MAP_TimerValueGet(ch->timer->timer, ch->channel == (TIMER_A | TIMER_B) ? TIMER_A : ch->channel)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_channel_event_count_obj, pyb_timer_channel_event_count); /// \method event_time() /// get the time at which the last event was triggered STATIC mp_obj_t pyb_timer_channel_event_time(mp_obj_t self_in) { pyb_timer_channel_obj_t *ch = self_in; // calculate the period, the prescaler and the match value uint32_t period_c; uint32_t match; (void)compute_prescaler_period_and_match_value(ch, &period_c, &match); uint32_t value = MAP_TimerValueGet(ch->timer->timer, ch->channel == (TIMER_A | TIMER_B) ? TIMER_A : ch->channel); // substract value to period since we are always operating in count-down mode uint32_t time_t = (1000 * (period_c - value)) / period_c; return mp_obj_new_int((time_t * 1000) / ch->frequency); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_channel_event_time_obj, pyb_timer_channel_event_time); /// \method duty_cycle() /// get or set the duty cycle when in PWM mode STATIC mp_obj_t pyb_timer_channel_duty_cycle(mp_uint_t n_args, const mp_obj_t *args) { pyb_timer_channel_obj_t *ch = args[0]; if (n_args == 1) { // get return mp_obj_new_int(ch->duty_cycle); } else { // duty cycle must be converted from percentage to ticks // calculate the period, the prescaler and the match value uint32_t period_c; uint32_t match; ch->duty_cycle = MIN(100, MAX(0, mp_obj_get_int(args[1]))); compute_prescaler_period_and_match_value(ch, &period_c, &match); if (n_args == 3) { // set the new polarity if requested ch->polarity = mp_obj_get_int(args[2]); MAP_TimerControlLevel(ch->timer->timer, ch->channel, (ch->polarity == PYBTIMER_POLARITY_NEG) ? true : false); } MAP_TimerMatchSet(ch->timer->timer, ch->channel, match); return mp_const_none; } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_duty_cycle_obj, 1, 3, pyb_timer_channel_duty_cycle); /// \method callback(handler, priority, value) /// create a callback object associated with the timer channel STATIC mp_obj_t pyb_timer_channel_callback (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { mp_arg_val_t args[mpcallback_INIT_NUM_ARGS]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, mpcallback_INIT_NUM_ARGS, mpcallback_init_args, args); pyb_timer_channel_obj_t *ch = pos_args[0]; mp_obj_t _callback = mpcallback_find(ch); if (kw_args->used > 0) { // convert the priority to the correct value uint priority = mpcallback_translate_priority (args[2].u_int); // validate the power mode uint pwrmode = args[4].u_int; if (pwrmode != PYB_PWR_MODE_ACTIVE) { goto invalid_args; } uint32_t _config = (ch->channel == TIMER_B) ? ((ch->timer->config & TIMER_B) >> 8) : (ch->timer->config & TIMER_A); uint32_t c_value = mp_obj_get_int(args[3].u_obj); // validate and set the value if we are in edge count mode if (_config == TIMER_CFG_A_CAP_COUNT) { if (!c_value || c_value > 0xFFFF) { // zero or exceeds the maximum value of a 16-bit timer goto invalid_args; } MAP_TimerMatchSet(ch->timer->timer, ch->channel, c_value); } // disable the callback first pyb_timer_channel_callback_disable(ch); uint8_t shift = (ch->channel == TIMER_B) ? 8 : 0; switch (_config) { case TIMER_CFG_A_ONE_SHOT: case TIMER_CFG_A_PERIODIC: ch->timer->intflags |= TIMER_TIMA_TIMEOUT << shift; break; case TIMER_CFG_A_CAP_COUNT: ch->timer->intflags |= TIMER_CAPA_MATCH << shift; // set the match value and make 1 the minimum MAP_TimerMatchSet(ch->timer->timer, ch->channel, MAX(1, c_value)); break; case TIMER_CFG_A_CAP_TIME: ch->timer->intflags |= TIMER_CAPA_EVENT << shift; break; case TIMER_CFG_A_PWM: // special case for the PWM match interrupt ch->timer->intflags |= ((ch->channel & TIMER_A) == TIMER_A) ? TIMER_TIMA_MATCH : TIMER_TIMB_MATCH; break; default: break; } // special case for a 32-bit timer if (ch->channel == (TIMER_A | TIMER_B)) { ch->timer->intflags |= (ch->timer->intflags << 8); } void (*pfnHandler)(void); uint32_t intregister; switch (ch->timer->timer) { case TIMERA0_BASE: if (ch->channel == TIMER_B) { pfnHandler = &TIMER0BIntHandler; intregister = INT_TIMERA0B; } else { pfnHandler = &TIMER0AIntHandler; intregister = INT_TIMERA0A; } break; case TIMERA1_BASE: if (ch->channel == TIMER_B) { pfnHandler = &TIMER1BIntHandler; intregister = INT_TIMERA1B; } else { pfnHandler = &TIMER1AIntHandler; intregister = INT_TIMERA1A; } break; case TIMERA2_BASE: if (ch->channel == TIMER_B) { pfnHandler = &TIMER2BIntHandler; intregister = INT_TIMERA2B; } else { pfnHandler = &TIMER2AIntHandler; intregister = INT_TIMERA2A; } break; default: if (ch->channel == TIMER_B) { pfnHandler = &TIMER3BIntHandler; intregister = INT_TIMERA3B; } else { pfnHandler = &TIMER3AIntHandler; intregister = INT_TIMERA3A; } break; } // register the interrupt and configure the priority MAP_IntPrioritySet(intregister, priority); MAP_TimerIntRegister(ch->timer->timer, ch->channel, pfnHandler); // create the callback _callback = mpcallback_new (ch, args[1].u_obj, &pyb_timer_channel_cb_methods, true); // reload the timer uint32_t period_c; uint32_t match; compute_prescaler_period_and_match_value(ch, &period_c, &match); MAP_TimerLoadSet(ch->timer->timer, ch->channel, period_c); // enable the callback before returning pyb_timer_channel_callback_enable(ch); } else if (!_callback) { _callback = mpcallback_new (ch, mp_const_none, &pyb_timer_channel_cb_methods, false); } return _callback; invalid_args: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_channel_callback_obj, 1, pyb_timer_channel_callback); STATIC const mp_map_elem_t pyb_timer_channel_locals_dict_table[] = { // instance methods { MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_timer_channel_freq_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_channel_period_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&pyb_timer_channel_time_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_event_count), (mp_obj_t)&pyb_timer_channel_event_count_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_event_time), (mp_obj_t)&pyb_timer_channel_event_time_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_duty_cycle), (mp_obj_t)&pyb_timer_channel_duty_cycle_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_channel_callback_obj }, }; STATIC MP_DEFINE_CONST_DICT(pyb_timer_channel_locals_dict, pyb_timer_channel_locals_dict_table); STATIC const mp_obj_type_t pyb_timer_channel_type = { { &mp_type_type }, .name = MP_QSTR_TimerChannel, .print = pyb_timer_channel_print, .locals_dict = (mp_obj_t)&pyb_timer_channel_locals_dict, };