Damien George | 04b9147 | 2014-05-03 23:27:38 +0100 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the Micro Python project, http://micropython.org/ |
| 3 | * |
| 4 | * The MIT License (MIT) |
| 5 | * |
| 6 | * Copyright (c) 2013, 2014 Damien P. George |
| 7 | * |
| 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 9 | * of this software and associated documentation files (the "Software"), to deal |
| 10 | * in the Software without restriction, including without limitation the rights |
| 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 12 | * copies of the Software, and to permit persons to whom the Software is |
| 13 | * furnished to do so, subject to the following conditions: |
| 14 | * |
| 15 | * The above copyright notice and this permission notice shall be included in |
| 16 | * all copies or substantial portions of the Software. |
| 17 | * |
| 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 24 | * THE SOFTWARE. |
| 25 | */ |
| 26 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 27 | #include <stdint.h> |
| 28 | #include <stdio.h> |
| 29 | #include <string.h> |
| 30 | |
| 31 | #include <stm32f4xx_hal.h> |
| 32 | #include "usbd_cdc_msc_hid.h" |
| 33 | #include "usbd_cdc_interface.h" |
| 34 | |
| 35 | #include "nlr.h" |
| 36 | #include "misc.h" |
| 37 | #include "mpconfig.h" |
| 38 | #include "qstr.h" |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 39 | #include "gc.h" |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 40 | #include "obj.h" |
| 41 | #include "runtime.h" |
| 42 | #include "timer.h" |
| 43 | #include "servo.h" |
| 44 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 45 | /// \moduleref pyb |
| 46 | /// \class Timer - periodically call a function |
| 47 | /// |
| 48 | /// Timers can be used for a great variety of tasks. At the moment, only |
| 49 | /// the simplest case is implemented: that of calling a function periodically. |
| 50 | /// |
| 51 | /// Each timer consists of a counter that counts up at a certain rate. The rate |
| 52 | /// at which it counts is the peripheral clock frequency (in Hz) divided by the |
| 53 | /// timer prescaler. When the counter reaches the timer period it triggers an |
| 54 | /// event, and the counter resets back to zero. By using the callback method, |
| 55 | /// the timer event can call a Python function. |
| 56 | /// |
| 57 | /// Example usage to toggle an LED at a fixed frequency: |
| 58 | /// |
| 59 | /// tim = pyb.Timer(4) # create a timer object using timer 4 |
| 60 | /// tim.init(freq=2) # trigger at 2Hz |
| 61 | /// tim.callback(lambda t:pyb.LED(1).toggle()) |
| 62 | /// |
| 63 | /// Further examples: |
| 64 | /// |
| 65 | /// tim = pyb.Timer(4, freq=100) # freq in Hz |
| 66 | /// tim = pyb.Timer(4, prescaler=1, period=100) |
| 67 | /// tim.counter() # get counter (can also set) |
| 68 | /// tim.prescaler(2) # set prescaler (can also get) |
| 69 | /// tim.period(200) # set period (can also get) |
| 70 | /// tim.callback(lambda t: ...) # set callback for update interrupt (t=tim instance) |
| 71 | /// tim.callback(None) # clear callback |
| 72 | /// |
| 73 | /// *Note:* Timer 3 is reserved for internal use. Timer 5 controls |
| 74 | /// the servo driver, and Timer 6 is used for timed ADC/DAC reading/writing. |
| 75 | /// It is recommended to use the other timers in your programs. |
| 76 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 77 | // The timers can be used by multiple drivers, and need a common point for |
| 78 | // the interrupts to be dispatched, so they are all collected here. |
| 79 | // |
| 80 | // TIM3: |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 81 | // - flash storage controller, to flush the cache |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 82 | // - USB CDC interface, interval, to check for new data |
| 83 | // - LED 4, PWM to set the LED intensity |
| 84 | // |
| 85 | // TIM5: |
| 86 | // - servo controller, PWM |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 87 | // |
| 88 | // TIM6: |
| 89 | // - ADC, DAC for read_timed and write_timed |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 90 | |
| 91 | typedef struct _pyb_timer_obj_t { |
| 92 | mp_obj_base_t base; |
| 93 | machine_uint_t tim_id; |
| 94 | mp_obj_t callback; |
| 95 | TIM_HandleTypeDef tim; |
| 96 | IRQn_Type irqn; |
| 97 | } pyb_timer_obj_t; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 98 | |
| 99 | TIM_HandleTypeDef TIM3_Handle; |
| 100 | TIM_HandleTypeDef TIM5_Handle; |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 101 | TIM_HandleTypeDef TIM6_Handle; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 102 | |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 103 | // Used to divide down TIM3 and periodically call the flash storage IRQ |
| 104 | static uint32_t tim3_counter = 0; |
| 105 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 106 | // Used to do callbacks to Python code on interrupt |
| 107 | STATIC pyb_timer_obj_t *pyb_timer_obj_all[14]; |
Emmanuel Blot | f6932d6 | 2014-06-19 18:54:34 +0200 | [diff] [blame] | 108 | #define PYB_TIMER_OBJ_ALL_NUM MP_ARRAY_SIZE(pyb_timer_obj_all) |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 109 | |
| 110 | void timer_init0(void) { |
| 111 | tim3_counter = 0; |
| 112 | for (uint i = 0; i < PYB_TIMER_OBJ_ALL_NUM; i++) { |
| 113 | pyb_timer_obj_all[i] = NULL; |
| 114 | } |
| 115 | } |
| 116 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 117 | // TIM3 is set-up for the USB CDC interface |
| 118 | void timer_tim3_init(void) { |
| 119 | // set up the timer for USBD CDC |
| 120 | __TIM3_CLK_ENABLE(); |
| 121 | |
| 122 | TIM3_Handle.Instance = TIM3; |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 123 | TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1; // TIM3 fires every USBD_CDC_POLLING_INTERVAL ms |
| 124 | TIM3_Handle.Init.Prescaler = 84-1; // for System clock at 168MHz, TIM3 runs at 1MHz |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 125 | TIM3_Handle.Init.ClockDivision = 0; |
| 126 | TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; |
| 127 | HAL_TIM_Base_Init(&TIM3_Handle); |
| 128 | |
| 129 | HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0); |
| 130 | HAL_NVIC_EnableIRQ(TIM3_IRQn); |
| 131 | |
| 132 | if (HAL_TIM_Base_Start(&TIM3_Handle) != HAL_OK) { |
| 133 | /* Starting Error */ |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | /* unused |
| 138 | void timer_tim3_deinit(void) { |
| 139 | // reset TIM3 timer |
| 140 | __TIM3_FORCE_RESET(); |
| 141 | __TIM3_RELEASE_RESET(); |
| 142 | } |
| 143 | */ |
| 144 | |
| 145 | // TIM5 is set-up for the servo controller |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 146 | // This function inits but does not start the timer |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 147 | void timer_tim5_init(void) { |
| 148 | // TIM5 clock enable |
| 149 | __TIM5_CLK_ENABLE(); |
| 150 | |
| 151 | // set up and enable interrupt |
| 152 | HAL_NVIC_SetPriority(TIM5_IRQn, 6, 0); |
| 153 | HAL_NVIC_EnableIRQ(TIM5_IRQn); |
| 154 | |
| 155 | // PWM clock configuration |
| 156 | TIM5_Handle.Instance = TIM5; |
| 157 | TIM5_Handle.Init.Period = 2000; // timer cycles at 50Hz |
| 158 | TIM5_Handle.Init.Prescaler = ((SystemCoreClock / 2) / 100000) - 1; // timer runs at 100kHz |
| 159 | TIM5_Handle.Init.ClockDivision = 0; |
| 160 | TIM5_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; |
| 161 | HAL_TIM_PWM_Init(&TIM5_Handle); |
| 162 | } |
| 163 | |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 164 | // Init TIM6 with a counter-overflow at the given frequency (given in Hz) |
| 165 | // TIM6 is used by the DAC and ADC for auto sampling at a given frequency |
| 166 | // This function inits but does not start the timer |
| 167 | void timer_tim6_init(uint freq) { |
| 168 | // TIM6 clock enable |
| 169 | __TIM6_CLK_ENABLE(); |
| 170 | |
| 171 | // Timer runs at SystemCoreClock / 2 |
| 172 | // Compute the prescaler value so TIM6 triggers at freq-Hz |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 173 | uint32_t period = MAX(1, (SystemCoreClock / 2) / freq); |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 174 | uint32_t prescaler = 1; |
| 175 | while (period > 0xffff) { |
| 176 | period >>= 1; |
| 177 | prescaler <<= 1; |
| 178 | } |
| 179 | |
| 180 | // Time base clock configuration |
| 181 | TIM6_Handle.Instance = TIM6; |
| 182 | TIM6_Handle.Init.Period = period - 1; |
| 183 | TIM6_Handle.Init.Prescaler = prescaler - 1; |
| 184 | TIM6_Handle.Init.ClockDivision = 0; // unused for TIM6 |
| 185 | TIM6_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; // unused for TIM6 |
| 186 | HAL_TIM_Base_Init(&TIM6_Handle); |
| 187 | } |
| 188 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 189 | // Interrupt dispatch |
| 190 | void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { |
| 191 | if (htim == &TIM3_Handle) { |
| 192 | USBD_CDC_HAL_TIM_PeriodElapsedCallback(); |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 193 | |
| 194 | // Periodically raise a flash IRQ for the flash storage controller |
| 195 | if (tim3_counter++ >= 500 / USBD_CDC_POLLING_INTERVAL) { |
| 196 | tim3_counter = 0; |
| 197 | NVIC->STIR = FLASH_IRQn; |
| 198 | } |
| 199 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 200 | } else if (htim == &TIM5_Handle) { |
| 201 | servo_timer_irq_callback(); |
| 202 | } |
| 203 | } |
| 204 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 205 | /******************************************************************************/ |
| 206 | /* Micro Python bindings */ |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 207 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 208 | STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { |
| 209 | pyb_timer_obj_t *self = self_in; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 210 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 211 | if (self->tim.State == HAL_TIM_STATE_RESET) { |
| 212 | print(env, "Timer(%u)", self->tim_id); |
| 213 | } else { |
| 214 | print(env, "Timer(%u, prescaler=%u, period=%u, mode=%u, div=%u)", |
| 215 | self->tim_id, |
| 216 | self->tim.Init.Prescaler, |
| 217 | self->tim.Init.Period, |
| 218 | self->tim.Init.CounterMode, |
| 219 | self->tim.Init.ClockDivision |
| 220 | ); |
| 221 | } |
| 222 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 223 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 224 | /// \method init(*, freq, prescaler, period) |
| 225 | /// Initialise the timer. Initialisation must be either by frequency (in Hz) |
| 226 | /// or by prescaler and period: |
| 227 | /// |
| 228 | /// tim.init(freq=100) # set the timer to trigger at 100Hz |
| 229 | /// tim.init(prescaler=100, period=300) # set the prescaler and period directly |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 230 | STATIC const mp_arg_t pyb_timer_init_args[] = { |
| 231 | { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, |
| 232 | { MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, |
| 233 | { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, |
| 234 | { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIM_COUNTERMODE_UP} }, |
| 235 | { MP_QSTR_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIM_CLOCKDIVISION_DIV1} }, |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 236 | }; |
Emmanuel Blot | f6932d6 | 2014-06-19 18:54:34 +0200 | [diff] [blame] | 237 | #define PYB_TIMER_INIT_NUM_ARGS MP_ARRAY_SIZE(pyb_timer_init_args) |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 238 | |
| 239 | 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) { |
| 240 | // parse args |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 241 | mp_arg_val_t vals[PYB_TIMER_INIT_NUM_ARGS]; |
| 242 | mp_arg_parse_all(n_args, args, kw_args, PYB_TIMER_INIT_NUM_ARGS, pyb_timer_init_args, vals); |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 243 | |
| 244 | // set the TIM configuration values |
| 245 | TIM_Base_InitTypeDef *init = &self->tim.Init; |
| 246 | |
| 247 | if (vals[0].u_int != 0xffffffff) { |
| 248 | // set prescaler and period from frequency |
| 249 | |
| 250 | if (vals[0].u_int == 0) { |
| 251 | nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't have 0 frequency")); |
| 252 | } |
| 253 | |
| 254 | // work out TIM's clock source |
| 255 | uint tim_clock; |
| 256 | if (self->tim_id == 1 || (8 <= self->tim_id && self->tim_id <= 11)) { |
| 257 | // TIM{1,8,9,10,11} are on APB2 |
| 258 | tim_clock = HAL_RCC_GetPCLK2Freq(); |
| 259 | } else { |
| 260 | // TIM{2,3,4,5,6,7,12,13,14} are on APB1 |
| 261 | tim_clock = HAL_RCC_GetPCLK1Freq(); |
| 262 | } |
| 263 | |
| 264 | // compute the prescaler value so TIM triggers at freq-Hz |
| 265 | // dpgeorge: I don't understand why we need to multiply tim_clock by 2 |
| 266 | uint32_t period = MAX(1, 2 * tim_clock / vals[0].u_int); |
| 267 | uint32_t prescaler = 1; |
| 268 | while (period > 0xffff) { |
| 269 | period >>= 1; |
| 270 | prescaler <<= 1; |
| 271 | } |
| 272 | init->Prescaler = prescaler - 1; |
| 273 | init->Period = period - 1; |
| 274 | } else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) { |
| 275 | // set prescaler and period directly |
| 276 | init->Prescaler = vals[1].u_int; |
| 277 | init->Period = vals[2].u_int; |
| 278 | } else { |
| 279 | nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "must specify either freq, or prescaler and period")); |
| 280 | } |
| 281 | |
| 282 | init->CounterMode = vals[3].u_int; |
| 283 | init->ClockDivision = vals[4].u_int; |
| 284 | init->RepetitionCounter = 0; |
| 285 | |
| 286 | // init the TIM peripheral |
| 287 | switch (self->tim_id) { |
| 288 | case 1: __TIM1_CLK_ENABLE(); break; |
| 289 | case 2: __TIM2_CLK_ENABLE(); break; |
| 290 | case 3: __TIM3_CLK_ENABLE(); break; |
| 291 | case 4: __TIM4_CLK_ENABLE(); break; |
| 292 | case 5: __TIM5_CLK_ENABLE(); break; |
| 293 | case 6: __TIM6_CLK_ENABLE(); break; |
| 294 | case 7: __TIM7_CLK_ENABLE(); break; |
| 295 | case 8: __TIM8_CLK_ENABLE(); break; |
| 296 | case 9: __TIM9_CLK_ENABLE(); break; |
| 297 | case 10: __TIM10_CLK_ENABLE(); break; |
| 298 | case 11: __TIM11_CLK_ENABLE(); break; |
| 299 | case 12: __TIM12_CLK_ENABLE(); break; |
| 300 | case 13: __TIM13_CLK_ENABLE(); break; |
| 301 | case 14: __TIM14_CLK_ENABLE(); break; |
| 302 | } |
| 303 | HAL_TIM_Base_Init(&self->tim); |
| 304 | HAL_TIM_Base_Start(&self->tim); |
| 305 | |
| 306 | // set the priority (if not a special timer) |
| 307 | if (self->tim_id != 3 && self->tim_id != 5) { |
| 308 | HAL_NVIC_SetPriority(self->irqn, 0xe, 0xe); // next-to lowest priority |
| 309 | } |
| 310 | |
| 311 | return mp_const_none; |
| 312 | } |
| 313 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 314 | /// \classmethod \constructor(id, ...) |
| 315 | /// Construct a new timer object of the given id. If additional |
| 316 | /// arguments are given, then the timer is initialised by `init(...)`. |
| 317 | /// `id` can be 1 to 14, excluding 3. |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 318 | STATIC mp_obj_t pyb_timer_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { |
| 319 | // check arguments |
| 320 | mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); |
| 321 | |
| 322 | // create new Timer object |
| 323 | pyb_timer_obj_t *tim = m_new_obj(pyb_timer_obj_t); |
| 324 | tim->base.type = &pyb_timer_type; |
| 325 | tim->callback = mp_const_none; |
| 326 | memset(&tim->tim, 0, sizeof(tim->tim)); |
| 327 | |
| 328 | // get TIM number |
| 329 | tim->tim_id = mp_obj_get_int(args[0]); |
| 330 | |
| 331 | switch (tim->tim_id) { |
| 332 | case 1: tim->tim.Instance = TIM1; tim->irqn = TIM1_UP_TIM10_IRQn; break; |
| 333 | case 2: tim->tim.Instance = TIM2; tim->irqn = TIM2_IRQn; break; |
| 334 | 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 |
| 335 | case 4: tim->tim.Instance = TIM4; tim->irqn = TIM4_IRQn; break; |
| 336 | case 5: tim->tim.Instance = TIM5; tim->irqn = TIM5_IRQn; break; |
| 337 | case 6: tim->tim.Instance = TIM6; tim->irqn = TIM6_DAC_IRQn; break; |
| 338 | case 7: tim->tim.Instance = TIM7; tim->irqn = TIM7_IRQn; break; |
| 339 | case 8: tim->tim.Instance = TIM8; tim->irqn = TIM8_UP_TIM13_IRQn; break; |
| 340 | case 9: tim->tim.Instance = TIM9; tim->irqn = TIM1_BRK_TIM9_IRQn; break; |
| 341 | case 10: tim->tim.Instance = TIM10; tim->irqn = TIM1_UP_TIM10_IRQn; break; |
| 342 | case 11: tim->tim.Instance = TIM11; tim->irqn = TIM1_TRG_COM_TIM11_IRQn; break; |
| 343 | case 12: tim->tim.Instance = TIM12; tim->irqn = TIM8_BRK_TIM12_IRQn; break; |
| 344 | case 13: tim->tim.Instance = TIM13; tim->irqn = TIM8_UP_TIM13_IRQn; break; |
| 345 | case 14: tim->tim.Instance = TIM14; tim->irqn = TIM8_TRG_COM_TIM14_IRQn; break; |
| 346 | default: nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d does not exist", tim->tim_id)); |
| 347 | } |
| 348 | |
| 349 | if (n_args > 1 || n_kw > 0) { |
| 350 | // start the peripheral |
| 351 | mp_map_t kw_args; |
| 352 | mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); |
| 353 | pyb_timer_init_helper(tim, n_args - 1, args + 1, &kw_args); |
| 354 | } |
| 355 | |
| 356 | // set the global variable for interrupt callbacks |
| 357 | if (tim->tim_id - 1 < PYB_TIMER_OBJ_ALL_NUM) { |
| 358 | pyb_timer_obj_all[tim->tim_id - 1] = tim; |
| 359 | } |
| 360 | |
| 361 | return (mp_obj_t)tim; |
| 362 | } |
| 363 | |
| 364 | STATIC mp_obj_t pyb_timer_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { |
| 365 | return pyb_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); |
| 366 | } |
| 367 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_init_obj, 1, pyb_timer_init); |
| 368 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 369 | /// \method deinit() |
| 370 | /// Deinitialises the timer. |
| 371 | /// |
| 372 | /// *This function is not yet implemented.* |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 373 | STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in) { |
| 374 | //pyb_timer_obj_t *self = self_in; |
| 375 | // TODO implement me |
| 376 | return mp_const_none; |
| 377 | } |
| 378 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit); |
| 379 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 380 | /// \method counter([value]) |
| 381 | /// Get or set the timer counter. |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 382 | mp_obj_t pyb_timer_counter(uint n_args, const mp_obj_t *args) { |
| 383 | pyb_timer_obj_t *self = args[0]; |
| 384 | if (n_args == 1) { |
| 385 | // get |
| 386 | return mp_obj_new_int(self->tim.Instance->CNT); |
| 387 | } else { |
| 388 | // set |
| 389 | __HAL_TIM_SetCounter(&self->tim, mp_obj_get_int(args[1])); |
| 390 | return mp_const_none; |
| 391 | } |
| 392 | } |
| 393 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_counter_obj, 1, 2, pyb_timer_counter); |
| 394 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 395 | /// \method prescaler([value]) |
| 396 | /// Get or set the prescaler for the timer. |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 397 | mp_obj_t pyb_timer_prescaler(uint n_args, const mp_obj_t *args) { |
| 398 | pyb_timer_obj_t *self = args[0]; |
| 399 | if (n_args == 1) { |
| 400 | // get |
| 401 | return mp_obj_new_int(self->tim.Instance->PSC & 0xffff); |
| 402 | } else { |
| 403 | // set |
| 404 | self->tim.Init.Prescaler = self->tim.Instance->PSC = mp_obj_get_int(args[1]) & 0xffff; |
| 405 | return mp_const_none; |
| 406 | } |
| 407 | } |
| 408 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_prescaler_obj, 1, 2, pyb_timer_prescaler); |
| 409 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 410 | /// \method period([value]) |
| 411 | /// Get or set the period of the timer. |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 412 | mp_obj_t pyb_timer_period(uint n_args, const mp_obj_t *args) { |
| 413 | pyb_timer_obj_t *self = args[0]; |
| 414 | if (n_args == 1) { |
| 415 | // get |
| 416 | return mp_obj_new_int(self->tim.Instance->ARR & 0xffff); |
| 417 | } else { |
| 418 | // set |
| 419 | __HAL_TIM_SetAutoreload(&self->tim, mp_obj_get_int(args[1]) & 0xffff); |
| 420 | return mp_const_none; |
| 421 | } |
| 422 | } |
| 423 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_period_obj, 1, 2, pyb_timer_period); |
| 424 | |
Damien George | 3eb8163 | 2014-05-02 16:58:15 +0100 | [diff] [blame] | 425 | /// \method callback(fun) |
| 426 | /// Set the function to be called when the timer triggers. |
| 427 | /// `fun` is passed 1 argument, the timer object. |
| 428 | /// If `fun` is `None` then the callback will be disabled. |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 429 | STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback) { |
| 430 | pyb_timer_obj_t *self = self_in; |
| 431 | if (callback == mp_const_none) { |
| 432 | // stop interrupt (but not timer) |
| 433 | __HAL_TIM_DISABLE_IT(&self->tim, TIM_IT_UPDATE); |
| 434 | self->callback = mp_const_none; |
| 435 | } else if (mp_obj_is_callable(callback)) { |
| 436 | self->callback = callback; |
| 437 | HAL_NVIC_EnableIRQ(self->irqn); |
| 438 | // start timer, so that it interrupts on overflow |
| 439 | HAL_TIM_Base_Start_IT(&self->tim); |
| 440 | } else { |
| 441 | nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "callback must be None or a callable object")); |
| 442 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 443 | return mp_const_none; |
| 444 | } |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 445 | STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_timer_callback_obj, pyb_timer_callback); |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 446 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 447 | STATIC const mp_map_elem_t pyb_timer_locals_dict_table[] = { |
| 448 | // instance methods |
| 449 | { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_timer_init_obj }, |
| 450 | { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_timer_deinit_obj }, |
| 451 | { MP_OBJ_NEW_QSTR(MP_QSTR_counter), (mp_obj_t)&pyb_timer_counter_obj }, |
| 452 | { MP_OBJ_NEW_QSTR(MP_QSTR_prescaler), (mp_obj_t)&pyb_timer_prescaler_obj }, |
| 453 | { MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_period_obj }, |
| 454 | { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_callback_obj }, |
| 455 | }; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 456 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 457 | STATIC MP_DEFINE_CONST_DICT(pyb_timer_locals_dict, pyb_timer_locals_dict_table); |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 458 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 459 | const mp_obj_type_t pyb_timer_type = { |
| 460 | { &mp_type_type }, |
| 461 | .name = MP_QSTR_Timer, |
| 462 | .print = pyb_timer_print, |
| 463 | .make_new = pyb_timer_make_new, |
| 464 | .locals_dict = (mp_obj_t)&pyb_timer_locals_dict, |
| 465 | }; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 466 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 467 | void timer_irq_handler(uint tim_id) { |
| 468 | if (tim_id - 1 < PYB_TIMER_OBJ_ALL_NUM) { |
| 469 | // get the timer object |
| 470 | pyb_timer_obj_t *tim = pyb_timer_obj_all[tim_id - 1]; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 471 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 472 | if (tim == NULL) { |
| 473 | // timer object has not been set, so we can't do anything |
| 474 | return; |
| 475 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 476 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 477 | // see if it was a TIM update event (the only event we currently interrupt on) |
| 478 | if (__HAL_TIM_GET_FLAG(&tim->tim, TIM_FLAG_UPDATE) != RESET) { |
| 479 | if (__HAL_TIM_GET_ITSTATUS(&tim->tim, TIM_IT_UPDATE) != RESET) { |
| 480 | // clear the interrupt |
| 481 | __HAL_TIM_CLEAR_IT(&tim->tim, TIM_IT_UPDATE); |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 482 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 483 | // execute callback if it's set |
| 484 | if (tim->callback != mp_const_none) { |
| 485 | // When executing code within a handler we must lock the GC to prevent |
| 486 | // any memory allocations. We must also catch any exceptions. |
| 487 | gc_lock(); |
| 488 | nlr_buf_t nlr; |
| 489 | if (nlr_push(&nlr) == 0) { |
| 490 | mp_call_function_1(tim->callback, tim); |
| 491 | nlr_pop(); |
| 492 | } else { |
| 493 | // Uncaught exception; disable the callback so it doesn't run again. |
| 494 | tim->callback = mp_const_none; |
| 495 | __HAL_TIM_DISABLE_IT(&tim->tim, TIM_IT_UPDATE); |
Damien George | 5874c1c | 2014-05-03 13:24:21 +0100 | [diff] [blame] | 496 | printf("Uncaught exception in Timer(" UINT_FMT ") interrupt handler\n", tim->tim_id); |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame] | 497 | mp_obj_print_exception((mp_obj_t)nlr.ret_val); |
| 498 | } |
| 499 | gc_unlock(); |
| 500 | } |
| 501 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 502 | } |
| 503 | } |
| 504 | } |