Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 1 | #include <stdint.h> |
| 2 | #include <stdio.h> |
| 3 | #include <string.h> |
| 4 | |
| 5 | #include <stm32f4xx_hal.h> |
| 6 | #include "usbd_cdc_msc_hid.h" |
| 7 | #include "usbd_cdc_interface.h" |
| 8 | |
| 9 | #include "nlr.h" |
| 10 | #include "misc.h" |
| 11 | #include "mpconfig.h" |
| 12 | #include "qstr.h" |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 13 | #include "gc.h" |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 14 | #include "obj.h" |
| 15 | #include "runtime.h" |
| 16 | #include "timer.h" |
| 17 | #include "servo.h" |
| 18 | |
| 19 | // The timers can be used by multiple drivers, and need a common point for |
| 20 | // the interrupts to be dispatched, so they are all collected here. |
| 21 | // |
| 22 | // TIM3: |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 23 | // - flash storage controller, to flush the cache |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 24 | // - USB CDC interface, interval, to check for new data |
| 25 | // - LED 4, PWM to set the LED intensity |
| 26 | // |
| 27 | // TIM5: |
| 28 | // - servo controller, PWM |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 29 | // |
| 30 | // TIM6: |
| 31 | // - ADC, DAC for read_timed and write_timed |
| 32 | // |
| 33 | // Python usage model: |
| 34 | // |
| 35 | // tim = pyb.Timer(4, freq=100) # freq in Hz |
| 36 | // tim = pyb.Timer(4, prescaler=1, period=100) |
| 37 | // tim.counter() # get counter (can also set) |
| 38 | // tim.prescaler(2) # set prescaler (can also get) |
| 39 | // tim.period(200) # set period (can also get) |
| 40 | // tim.callback(lambda t: ...) # set callback for update interrupt (t=tim instance) |
| 41 | // tim.callback(None) # clear callback |
| 42 | |
| 43 | typedef struct _pyb_timer_obj_t { |
| 44 | mp_obj_base_t base; |
| 45 | machine_uint_t tim_id; |
| 46 | mp_obj_t callback; |
| 47 | TIM_HandleTypeDef tim; |
| 48 | IRQn_Type irqn; |
| 49 | } pyb_timer_obj_t; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 50 | |
| 51 | TIM_HandleTypeDef TIM3_Handle; |
| 52 | TIM_HandleTypeDef TIM5_Handle; |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 53 | TIM_HandleTypeDef TIM6_Handle; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 54 | |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 55 | // Used to divide down TIM3 and periodically call the flash storage IRQ |
| 56 | static uint32_t tim3_counter = 0; |
| 57 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 58 | // Used to do callbacks to Python code on interrupt |
| 59 | STATIC pyb_timer_obj_t *pyb_timer_obj_all[14]; |
| 60 | #define PYB_TIMER_OBJ_ALL_NUM (sizeof(pyb_timer_obj_all) / sizeof(pyb_timer_obj_all[0])) |
| 61 | |
| 62 | void timer_init0(void) { |
| 63 | tim3_counter = 0; |
| 64 | for (uint i = 0; i < PYB_TIMER_OBJ_ALL_NUM; i++) { |
| 65 | pyb_timer_obj_all[i] = NULL; |
| 66 | } |
| 67 | } |
| 68 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 69 | // TIM3 is set-up for the USB CDC interface |
| 70 | void timer_tim3_init(void) { |
| 71 | // set up the timer for USBD CDC |
| 72 | __TIM3_CLK_ENABLE(); |
| 73 | |
| 74 | TIM3_Handle.Instance = TIM3; |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 75 | TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1; // TIM3 fires every USBD_CDC_POLLING_INTERVAL ms |
| 76 | 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] | 77 | TIM3_Handle.Init.ClockDivision = 0; |
| 78 | TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; |
| 79 | HAL_TIM_Base_Init(&TIM3_Handle); |
| 80 | |
| 81 | HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0); |
| 82 | HAL_NVIC_EnableIRQ(TIM3_IRQn); |
| 83 | |
| 84 | if (HAL_TIM_Base_Start(&TIM3_Handle) != HAL_OK) { |
| 85 | /* Starting Error */ |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /* unused |
| 90 | void timer_tim3_deinit(void) { |
| 91 | // reset TIM3 timer |
| 92 | __TIM3_FORCE_RESET(); |
| 93 | __TIM3_RELEASE_RESET(); |
| 94 | } |
| 95 | */ |
| 96 | |
| 97 | // TIM5 is set-up for the servo controller |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 98 | // This function inits but does not start the timer |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 99 | void timer_tim5_init(void) { |
| 100 | // TIM5 clock enable |
| 101 | __TIM5_CLK_ENABLE(); |
| 102 | |
| 103 | // set up and enable interrupt |
| 104 | HAL_NVIC_SetPriority(TIM5_IRQn, 6, 0); |
| 105 | HAL_NVIC_EnableIRQ(TIM5_IRQn); |
| 106 | |
| 107 | // PWM clock configuration |
| 108 | TIM5_Handle.Instance = TIM5; |
| 109 | TIM5_Handle.Init.Period = 2000; // timer cycles at 50Hz |
| 110 | TIM5_Handle.Init.Prescaler = ((SystemCoreClock / 2) / 100000) - 1; // timer runs at 100kHz |
| 111 | TIM5_Handle.Init.ClockDivision = 0; |
| 112 | TIM5_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; |
| 113 | HAL_TIM_PWM_Init(&TIM5_Handle); |
| 114 | } |
| 115 | |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 116 | // Init TIM6 with a counter-overflow at the given frequency (given in Hz) |
| 117 | // TIM6 is used by the DAC and ADC for auto sampling at a given frequency |
| 118 | // This function inits but does not start the timer |
| 119 | void timer_tim6_init(uint freq) { |
| 120 | // TIM6 clock enable |
| 121 | __TIM6_CLK_ENABLE(); |
| 122 | |
| 123 | // Timer runs at SystemCoreClock / 2 |
| 124 | // Compute the prescaler value so TIM6 triggers at freq-Hz |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 125 | uint32_t period = MAX(1, (SystemCoreClock / 2) / freq); |
Damien George | 4d7f4eb | 2014-04-15 19:52:56 +0100 | [diff] [blame] | 126 | uint32_t prescaler = 1; |
| 127 | while (period > 0xffff) { |
| 128 | period >>= 1; |
| 129 | prescaler <<= 1; |
| 130 | } |
| 131 | |
| 132 | // Time base clock configuration |
| 133 | TIM6_Handle.Instance = TIM6; |
| 134 | TIM6_Handle.Init.Period = period - 1; |
| 135 | TIM6_Handle.Init.Prescaler = prescaler - 1; |
| 136 | TIM6_Handle.Init.ClockDivision = 0; // unused for TIM6 |
| 137 | TIM6_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; // unused for TIM6 |
| 138 | HAL_TIM_Base_Init(&TIM6_Handle); |
| 139 | } |
| 140 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 141 | // Interrupt dispatch |
| 142 | void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { |
| 143 | if (htim == &TIM3_Handle) { |
| 144 | USBD_CDC_HAL_TIM_PeriodElapsedCallback(); |
Damien George | 6d98353 | 2014-04-16 23:08:36 +0100 | [diff] [blame] | 145 | |
| 146 | // Periodically raise a flash IRQ for the flash storage controller |
| 147 | if (tim3_counter++ >= 500 / USBD_CDC_POLLING_INTERVAL) { |
| 148 | tim3_counter = 0; |
| 149 | NVIC->STIR = FLASH_IRQn; |
| 150 | } |
| 151 | |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 152 | } else if (htim == &TIM5_Handle) { |
| 153 | servo_timer_irq_callback(); |
| 154 | } |
| 155 | } |
| 156 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 157 | /******************************************************************************/ |
| 158 | /* Micro Python bindings */ |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 159 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 160 | STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { |
| 161 | pyb_timer_obj_t *self = self_in; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 162 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 163 | if (self->tim.State == HAL_TIM_STATE_RESET) { |
| 164 | print(env, "Timer(%u)", self->tim_id); |
| 165 | } else { |
| 166 | print(env, "Timer(%u, prescaler=%u, period=%u, mode=%u, div=%u)", |
| 167 | self->tim_id, |
| 168 | self->tim.Init.Prescaler, |
| 169 | self->tim.Init.Period, |
| 170 | self->tim.Init.CounterMode, |
| 171 | self->tim.Init.ClockDivision |
| 172 | ); |
| 173 | } |
| 174 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 175 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 176 | STATIC const mp_arg_parse_t pyb_timer_init_accepted_args[] = { |
| 177 | { MP_QSTR_freq, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = 0xffffffff} }, |
| 178 | { MP_QSTR_prescaler, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = 0xffffffff} }, |
| 179 | { MP_QSTR_period, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = 0xffffffff} }, |
| 180 | { MP_QSTR_mode, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = TIM_COUNTERMODE_UP} }, |
| 181 | { MP_QSTR_div, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = TIM_CLOCKDIVISION_DIV1} }, |
| 182 | }; |
| 183 | #define PYB_TIMER_INIT_NUM_ARGS (sizeof(pyb_timer_init_accepted_args) / sizeof(pyb_timer_init_accepted_args[0])) |
| 184 | |
| 185 | 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) { |
| 186 | // parse args |
| 187 | mp_arg_parse_val_t vals[PYB_TIMER_INIT_NUM_ARGS]; |
| 188 | mp_arg_parse_all(n_args, args, kw_args, PYB_TIMER_INIT_NUM_ARGS, pyb_timer_init_accepted_args, vals); |
| 189 | |
| 190 | // set the TIM configuration values |
| 191 | TIM_Base_InitTypeDef *init = &self->tim.Init; |
| 192 | |
| 193 | if (vals[0].u_int != 0xffffffff) { |
| 194 | // set prescaler and period from frequency |
| 195 | |
| 196 | if (vals[0].u_int == 0) { |
| 197 | nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't have 0 frequency")); |
| 198 | } |
| 199 | |
| 200 | // work out TIM's clock source |
| 201 | uint tim_clock; |
| 202 | if (self->tim_id == 1 || (8 <= self->tim_id && self->tim_id <= 11)) { |
| 203 | // TIM{1,8,9,10,11} are on APB2 |
| 204 | tim_clock = HAL_RCC_GetPCLK2Freq(); |
| 205 | } else { |
| 206 | // TIM{2,3,4,5,6,7,12,13,14} are on APB1 |
| 207 | tim_clock = HAL_RCC_GetPCLK1Freq(); |
| 208 | } |
| 209 | |
| 210 | // compute the prescaler value so TIM triggers at freq-Hz |
| 211 | // dpgeorge: I don't understand why we need to multiply tim_clock by 2 |
| 212 | uint32_t period = MAX(1, 2 * tim_clock / vals[0].u_int); |
| 213 | uint32_t prescaler = 1; |
| 214 | while (period > 0xffff) { |
| 215 | period >>= 1; |
| 216 | prescaler <<= 1; |
| 217 | } |
| 218 | init->Prescaler = prescaler - 1; |
| 219 | init->Period = period - 1; |
| 220 | } else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) { |
| 221 | // set prescaler and period directly |
| 222 | init->Prescaler = vals[1].u_int; |
| 223 | init->Period = vals[2].u_int; |
| 224 | } else { |
| 225 | nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "must specify either freq, or prescaler and period")); |
| 226 | } |
| 227 | |
| 228 | init->CounterMode = vals[3].u_int; |
| 229 | init->ClockDivision = vals[4].u_int; |
| 230 | init->RepetitionCounter = 0; |
| 231 | |
| 232 | // init the TIM peripheral |
| 233 | switch (self->tim_id) { |
| 234 | case 1: __TIM1_CLK_ENABLE(); break; |
| 235 | case 2: __TIM2_CLK_ENABLE(); break; |
| 236 | case 3: __TIM3_CLK_ENABLE(); break; |
| 237 | case 4: __TIM4_CLK_ENABLE(); break; |
| 238 | case 5: __TIM5_CLK_ENABLE(); break; |
| 239 | case 6: __TIM6_CLK_ENABLE(); break; |
| 240 | case 7: __TIM7_CLK_ENABLE(); break; |
| 241 | case 8: __TIM8_CLK_ENABLE(); break; |
| 242 | case 9: __TIM9_CLK_ENABLE(); break; |
| 243 | case 10: __TIM10_CLK_ENABLE(); break; |
| 244 | case 11: __TIM11_CLK_ENABLE(); break; |
| 245 | case 12: __TIM12_CLK_ENABLE(); break; |
| 246 | case 13: __TIM13_CLK_ENABLE(); break; |
| 247 | case 14: __TIM14_CLK_ENABLE(); break; |
| 248 | } |
| 249 | HAL_TIM_Base_Init(&self->tim); |
| 250 | HAL_TIM_Base_Start(&self->tim); |
| 251 | |
| 252 | // set the priority (if not a special timer) |
| 253 | if (self->tim_id != 3 && self->tim_id != 5) { |
| 254 | HAL_NVIC_SetPriority(self->irqn, 0xe, 0xe); // next-to lowest priority |
| 255 | } |
| 256 | |
| 257 | return mp_const_none; |
| 258 | } |
| 259 | |
| 260 | STATIC mp_obj_t pyb_timer_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { |
| 261 | // check arguments |
| 262 | mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); |
| 263 | |
| 264 | // create new Timer object |
| 265 | pyb_timer_obj_t *tim = m_new_obj(pyb_timer_obj_t); |
| 266 | tim->base.type = &pyb_timer_type; |
| 267 | tim->callback = mp_const_none; |
| 268 | memset(&tim->tim, 0, sizeof(tim->tim)); |
| 269 | |
| 270 | // get TIM number |
| 271 | tim->tim_id = mp_obj_get_int(args[0]); |
| 272 | |
| 273 | switch (tim->tim_id) { |
| 274 | case 1: tim->tim.Instance = TIM1; tim->irqn = TIM1_UP_TIM10_IRQn; break; |
| 275 | case 2: tim->tim.Instance = TIM2; tim->irqn = TIM2_IRQn; break; |
| 276 | 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 |
| 277 | case 4: tim->tim.Instance = TIM4; tim->irqn = TIM4_IRQn; break; |
| 278 | case 5: tim->tim.Instance = TIM5; tim->irqn = TIM5_IRQn; break; |
| 279 | case 6: tim->tim.Instance = TIM6; tim->irqn = TIM6_DAC_IRQn; break; |
| 280 | case 7: tim->tim.Instance = TIM7; tim->irqn = TIM7_IRQn; break; |
| 281 | case 8: tim->tim.Instance = TIM8; tim->irqn = TIM8_UP_TIM13_IRQn; break; |
| 282 | case 9: tim->tim.Instance = TIM9; tim->irqn = TIM1_BRK_TIM9_IRQn; break; |
| 283 | case 10: tim->tim.Instance = TIM10; tim->irqn = TIM1_UP_TIM10_IRQn; break; |
| 284 | case 11: tim->tim.Instance = TIM11; tim->irqn = TIM1_TRG_COM_TIM11_IRQn; break; |
| 285 | case 12: tim->tim.Instance = TIM12; tim->irqn = TIM8_BRK_TIM12_IRQn; break; |
| 286 | case 13: tim->tim.Instance = TIM13; tim->irqn = TIM8_UP_TIM13_IRQn; break; |
| 287 | case 14: tim->tim.Instance = TIM14; tim->irqn = TIM8_TRG_COM_TIM14_IRQn; break; |
| 288 | default: nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d does not exist", tim->tim_id)); |
| 289 | } |
| 290 | |
| 291 | if (n_args > 1 || n_kw > 0) { |
| 292 | // start the peripheral |
| 293 | mp_map_t kw_args; |
| 294 | mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); |
| 295 | pyb_timer_init_helper(tim, n_args - 1, args + 1, &kw_args); |
| 296 | } |
| 297 | |
| 298 | // set the global variable for interrupt callbacks |
| 299 | if (tim->tim_id - 1 < PYB_TIMER_OBJ_ALL_NUM) { |
| 300 | pyb_timer_obj_all[tim->tim_id - 1] = tim; |
| 301 | } |
| 302 | |
| 303 | return (mp_obj_t)tim; |
| 304 | } |
| 305 | |
| 306 | STATIC mp_obj_t pyb_timer_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { |
| 307 | return pyb_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); |
| 308 | } |
| 309 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_init_obj, 1, pyb_timer_init); |
| 310 | |
| 311 | STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in) { |
| 312 | //pyb_timer_obj_t *self = self_in; |
| 313 | // TODO implement me |
| 314 | return mp_const_none; |
| 315 | } |
| 316 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit); |
| 317 | |
| 318 | mp_obj_t pyb_timer_counter(uint n_args, const mp_obj_t *args) { |
| 319 | pyb_timer_obj_t *self = args[0]; |
| 320 | if (n_args == 1) { |
| 321 | // get |
| 322 | return mp_obj_new_int(self->tim.Instance->CNT); |
| 323 | } else { |
| 324 | // set |
| 325 | __HAL_TIM_SetCounter(&self->tim, mp_obj_get_int(args[1])); |
| 326 | return mp_const_none; |
| 327 | } |
| 328 | } |
| 329 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_counter_obj, 1, 2, pyb_timer_counter); |
| 330 | |
| 331 | mp_obj_t pyb_timer_prescaler(uint n_args, const mp_obj_t *args) { |
| 332 | pyb_timer_obj_t *self = args[0]; |
| 333 | if (n_args == 1) { |
| 334 | // get |
| 335 | return mp_obj_new_int(self->tim.Instance->PSC & 0xffff); |
| 336 | } else { |
| 337 | // set |
| 338 | self->tim.Init.Prescaler = self->tim.Instance->PSC = mp_obj_get_int(args[1]) & 0xffff; |
| 339 | return mp_const_none; |
| 340 | } |
| 341 | } |
| 342 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_prescaler_obj, 1, 2, pyb_timer_prescaler); |
| 343 | |
| 344 | mp_obj_t pyb_timer_period(uint n_args, const mp_obj_t *args) { |
| 345 | pyb_timer_obj_t *self = args[0]; |
| 346 | if (n_args == 1) { |
| 347 | // get |
| 348 | return mp_obj_new_int(self->tim.Instance->ARR & 0xffff); |
| 349 | } else { |
| 350 | // set |
| 351 | __HAL_TIM_SetAutoreload(&self->tim, mp_obj_get_int(args[1]) & 0xffff); |
| 352 | return mp_const_none; |
| 353 | } |
| 354 | } |
| 355 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_period_obj, 1, 2, pyb_timer_period); |
| 356 | |
| 357 | // TIM6 is used as an internal interrupt to schedule something at a specific rate |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 358 | mp_obj_t timer_py_callback; |
| 359 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 360 | STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback) { |
| 361 | pyb_timer_obj_t *self = self_in; |
| 362 | if (callback == mp_const_none) { |
| 363 | // stop interrupt (but not timer) |
| 364 | __HAL_TIM_DISABLE_IT(&self->tim, TIM_IT_UPDATE); |
| 365 | self->callback = mp_const_none; |
| 366 | } else if (mp_obj_is_callable(callback)) { |
| 367 | self->callback = callback; |
| 368 | HAL_NVIC_EnableIRQ(self->irqn); |
| 369 | // start timer, so that it interrupts on overflow |
| 370 | HAL_TIM_Base_Start_IT(&self->tim); |
| 371 | } else { |
| 372 | nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "callback must be None or a callable object")); |
| 373 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 374 | return mp_const_none; |
| 375 | } |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 376 | 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] | 377 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 378 | STATIC const mp_map_elem_t pyb_timer_locals_dict_table[] = { |
| 379 | // instance methods |
| 380 | { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_timer_init_obj }, |
| 381 | { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_timer_deinit_obj }, |
| 382 | { MP_OBJ_NEW_QSTR(MP_QSTR_counter), (mp_obj_t)&pyb_timer_counter_obj }, |
| 383 | { MP_OBJ_NEW_QSTR(MP_QSTR_prescaler), (mp_obj_t)&pyb_timer_prescaler_obj }, |
| 384 | { MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_period_obj }, |
| 385 | { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_callback_obj }, |
| 386 | }; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 387 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 388 | 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] | 389 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 390 | const mp_obj_type_t pyb_timer_type = { |
| 391 | { &mp_type_type }, |
| 392 | .name = MP_QSTR_Timer, |
| 393 | .print = pyb_timer_print, |
| 394 | .make_new = pyb_timer_make_new, |
| 395 | .locals_dict = (mp_obj_t)&pyb_timer_locals_dict, |
| 396 | }; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 397 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 398 | void timer_irq_handler(uint tim_id) { |
| 399 | if (tim_id - 1 < PYB_TIMER_OBJ_ALL_NUM) { |
| 400 | // get the timer object |
| 401 | pyb_timer_obj_t *tim = pyb_timer_obj_all[tim_id - 1]; |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 402 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 403 | if (tim == NULL) { |
| 404 | // timer object has not been set, so we can't do anything |
| 405 | return; |
| 406 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 407 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 408 | // see if it was a TIM update event (the only event we currently interrupt on) |
| 409 | if (__HAL_TIM_GET_FLAG(&tim->tim, TIM_FLAG_UPDATE) != RESET) { |
| 410 | if (__HAL_TIM_GET_ITSTATUS(&tim->tim, TIM_IT_UPDATE) != RESET) { |
| 411 | // clear the interrupt |
| 412 | __HAL_TIM_CLEAR_IT(&tim->tim, TIM_IT_UPDATE); |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 413 | |
Damien George | 7fdfa93 | 2014-04-21 16:48:16 +0100 | [diff] [blame^] | 414 | // execute callback if it's set |
| 415 | if (tim->callback != mp_const_none) { |
| 416 | // When executing code within a handler we must lock the GC to prevent |
| 417 | // any memory allocations. We must also catch any exceptions. |
| 418 | gc_lock(); |
| 419 | nlr_buf_t nlr; |
| 420 | if (nlr_push(&nlr) == 0) { |
| 421 | mp_call_function_1(tim->callback, tim); |
| 422 | nlr_pop(); |
| 423 | } else { |
| 424 | // Uncaught exception; disable the callback so it doesn't run again. |
| 425 | tim->callback = mp_const_none; |
| 426 | __HAL_TIM_DISABLE_IT(&tim->tim, TIM_IT_UPDATE); |
| 427 | printf("Uncaught exception in Timer(%lu) interrupt handler\n", tim->tim_id); |
| 428 | mp_obj_print_exception((mp_obj_t)nlr.ret_val); |
| 429 | } |
| 430 | gc_unlock(); |
| 431 | } |
| 432 | } |
Damien George | a12be91 | 2014-04-02 15:09:36 +0100 | [diff] [blame] | 433 | } |
| 434 | } |
| 435 | } |