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:
Damien George 2019-10-23 16:56:14 +11:00
parent 43f53a2bbd
commit a5d97f1db9
7 changed files with 174 additions and 2 deletions

View File

@ -249,6 +249,7 @@ SRC_C = \
irq.c \
pendsv.c \
systick.c \
softtimer.c \
powerctrl.c \
powerctrlboot.c \
pybthread.c \

View File

@ -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

View File

@ -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]; \
\

View File

@ -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);

112
ports/stm32/softtimer.c Normal file
View File

@ -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);
}

50
ports/stm32/softtimer.h Normal file
View File

@ -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

View File

@ -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) {