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