stm32: Add soft timer implementation, using SysTick at 1ms resolution.
This commit adds an implementation of a "software timer" with a 1ms resolution, using SysTick. It allows unlimited number of concurrent timers (limited only by memory needed for each timer entry). They can be one-shot or periodic, and associated with a Python callback. There is a very small overhead added to the SysTick IRQ, which could be further optimised in the future, eg by patching SysTick_Handler code dynamically.
This commit is contained in:
parent
43f53a2bbd
commit
a5d97f1db9
|
@ -249,6 +249,7 @@ SRC_C = \
|
|||
irq.c \
|
||||
pendsv.c \
|
||||
systick.c \
|
||||
softtimer.c \
|
||||
powerctrl.c \
|
||||
powerctrlboot.c \
|
||||
pybthread.c \
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "gccollect.h"
|
||||
#include "factoryreset.h"
|
||||
#include "modmachine.h"
|
||||
#include "softtimer.h"
|
||||
#include "i2c.h"
|
||||
#include "spi.h"
|
||||
#include "uart.h"
|
||||
|
@ -743,6 +744,7 @@ soft_reset_exit:
|
|||
#if MICROPY_PY_NETWORK
|
||||
mod_network_deinit();
|
||||
#endif
|
||||
soft_timer_deinit();
|
||||
timer_deinit();
|
||||
uart_deinit_all();
|
||||
#if MICROPY_HW_ENABLE_CAN
|
||||
|
|
|
@ -282,6 +282,8 @@ struct _mp_bluetooth_nimble_root_pointers_t;
|
|||
\
|
||||
mp_obj_t pyb_extint_callback[PYB_EXTI_NUM_VECTORS]; \
|
||||
\
|
||||
struct _soft_timer_entry_t *soft_timer_head; \
|
||||
\
|
||||
/* pointers to all Timer objects (if they have been created) */ \
|
||||
struct _pyb_timer_obj_t *pyb_timer_obj_all[MICROPY_HW_MAX_TIMER]; \
|
||||
\
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#define MICROPY_INCLUDED_STM32_PENDSV_H
|
||||
|
||||
enum {
|
||||
PENDSV_DISPATCH_SOFT_TIMER,
|
||||
#if MICROPY_PY_NETWORK && MICROPY_PY_LWIP
|
||||
PENDSV_DISPATCH_LWIP,
|
||||
#if MICROPY_PY_NETWORK_CYW43
|
||||
|
@ -39,9 +40,7 @@ enum {
|
|||
PENDSV_DISPATCH_MAX
|
||||
};
|
||||
|
||||
#if (MICROPY_PY_NETWORK && MICROPY_PY_LWIP) || (MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE)
|
||||
#define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX
|
||||
#endif
|
||||
|
||||
typedef void (*pendsv_dispatch_t)(void);
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "py/runtime.h"
|
||||
#include "irq.h"
|
||||
#include "softtimer.h"
|
||||
|
||||
#define TICKS_PERIOD 0x80000000
|
||||
#define TICKS_DIFF(t1, t0) ((int32_t)(((t1 - t0 + TICKS_PERIOD / 2) & (TICKS_PERIOD - 1)) - TICKS_PERIOD / 2))
|
||||
|
||||
extern __IO uint32_t uwTick;
|
||||
|
||||
volatile uint32_t soft_timer_next;
|
||||
|
||||
void soft_timer_deinit(void) {
|
||||
MP_STATE_PORT(soft_timer_head) = NULL;
|
||||
}
|
||||
|
||||
STATIC void soft_timer_schedule_systick(uint32_t ticks_ms) {
|
||||
uint32_t irq_state = disable_irq();
|
||||
uint32_t uw_tick = uwTick;
|
||||
if (TICKS_DIFF(ticks_ms, uw_tick) <= 0) {
|
||||
soft_timer_next = uw_tick + 1;
|
||||
} else {
|
||||
soft_timer_next = ticks_ms;
|
||||
}
|
||||
enable_irq(irq_state);
|
||||
}
|
||||
|
||||
// Must be executed at IRQ_PRI_PENDSV
|
||||
void soft_timer_handler(void) {
|
||||
uint32_t ticks_ms = uwTick;
|
||||
soft_timer_entry_t *head = MP_STATE_PORT(soft_timer_head);
|
||||
while (head != NULL && TICKS_DIFF(head->expiry_ms, ticks_ms) <= 0) {
|
||||
mp_sched_schedule(head->callback, MP_OBJ_FROM_PTR(head));
|
||||
if (head->mode == SOFT_TIMER_MODE_PERIODIC) {
|
||||
head->expiry_ms += head->delta_ms;
|
||||
// Shift this node along to its new position
|
||||
soft_timer_entry_t *cur = head;
|
||||
while (cur->next != NULL && TICKS_DIFF(head->expiry_ms, cur->next->expiry_ms) >= 0) {
|
||||
cur = cur->next;
|
||||
}
|
||||
if (cur != head) {
|
||||
soft_timer_entry_t *next = head->next;
|
||||
head->next = cur->next;
|
||||
cur->next = head;
|
||||
head = next;
|
||||
}
|
||||
} else {
|
||||
head = head->next;
|
||||
}
|
||||
}
|
||||
MP_STATE_PORT(soft_timer_head) = head;
|
||||
if (head == NULL) {
|
||||
// No more timers left, set largest delay possible
|
||||
soft_timer_next = uwTick;
|
||||
} else {
|
||||
// Set soft_timer_next so SysTick calls us back at the correct time
|
||||
soft_timer_schedule_systick(head->expiry_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void soft_timer_insert(soft_timer_entry_t *entry) {
|
||||
uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV);
|
||||
soft_timer_entry_t **head_ptr = &MP_STATE_PORT(soft_timer_head);
|
||||
while (*head_ptr != NULL && TICKS_DIFF(entry->expiry_ms, (*head_ptr)->expiry_ms) >= 0) {
|
||||
head_ptr = &(*head_ptr)->next;
|
||||
}
|
||||
entry->next = *head_ptr;
|
||||
*head_ptr = entry;
|
||||
if (head_ptr == &MP_STATE_PORT(soft_timer_head)) {
|
||||
// This new timer became the earliest one so set soft_timer_next
|
||||
soft_timer_schedule_systick((*head_ptr)->expiry_ms);
|
||||
}
|
||||
restore_irq_pri(irq_state);
|
||||
}
|
||||
|
||||
void soft_timer_remove(soft_timer_entry_t *entry) {
|
||||
uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV);
|
||||
soft_timer_entry_t **cur = &MP_STATE_PORT(soft_timer_head);
|
||||
while (*cur != NULL) {
|
||||
if (*cur == entry) {
|
||||
*cur = entry->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
restore_irq_pri(irq_state);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_STM32_SOFTTIMER_H
|
||||
#define MICROPY_INCLUDED_STM32_SOFTTIMER_H
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
#define SOFT_TIMER_MODE_ONE_SHOT (1)
|
||||
#define SOFT_TIMER_MODE_PERIODIC (2)
|
||||
|
||||
typedef struct _soft_timer_entry_t {
|
||||
mp_obj_base_t base; // so struct can be used as an object and still be traced by GC
|
||||
struct _soft_timer_entry_t *next;
|
||||
uint32_t mode;
|
||||
uint32_t expiry_ms;
|
||||
uint32_t delta_ms; // for periodic mode
|
||||
mp_obj_t callback;
|
||||
} soft_timer_entry_t;
|
||||
|
||||
extern volatile uint32_t soft_timer_next;
|
||||
|
||||
void soft_timer_deinit(void);
|
||||
void soft_timer_handler(void);
|
||||
void soft_timer_insert(soft_timer_entry_t *entry);
|
||||
void soft_timer_remove(soft_timer_entry_t *entry);
|
||||
|
||||
#endif // MICROPY_INCLUDED_STM32_SOFTTIMER_H
|
|
@ -27,7 +27,9 @@
|
|||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "irq.h"
|
||||
#include "pendsv.h"
|
||||
#include "systick.h"
|
||||
#include "softtimer.h"
|
||||
#include "pybthread.h"
|
||||
|
||||
extern __IO uint32_t uwTick;
|
||||
|
@ -52,6 +54,10 @@ void SysTick_Handler(void) {
|
|||
f(uw_tick);
|
||||
}
|
||||
|
||||
if (soft_timer_next == uw_tick) {
|
||||
pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler);
|
||||
}
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
if (pyb_thread_enabled) {
|
||||
if (pyb_thread_cur->timeslice == 0) {
|
||||
|
|
Loading…
Reference in New Issue