stm32/systick: Make periodic systick callbacks use a cyclic func table.
Instead of checking each callback (currently storage and dma) explicitly for each SysTick IRQ, use a simple circular function table indexed by the lower bits of the millisecond tick counter. This allows callbacks to be easily enabled/disabled at runtime, and scales well to a large number of callbacks.
This commit is contained in:
parent
5fbda53d3c
commit
1bcf4afb10
|
@ -29,9 +29,16 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "py/obj.h"
|
#include "py/obj.h"
|
||||||
|
#include "systick.h"
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
|
|
||||||
|
#define DMA_IDLE_ENABLED() (dma_idle.enabled != 0)
|
||||||
|
#define DMA_SYSTICK_LOG2 (3)
|
||||||
|
#define DMA_SYSTICK_MASK ((1 << DMA_SYSTICK_LOG2) - 1)
|
||||||
|
#define DMA_IDLE_TICK_MAX (8) // 8*8 = 64 msec
|
||||||
|
#define DMA_IDLE_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & DMA_SYSTICK_MASK) == 0)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
dma_id_not_defined=-1,
|
dma_id_not_defined=-1,
|
||||||
dma_id_0,
|
dma_id_0,
|
||||||
|
@ -52,6 +59,11 @@ typedef enum {
|
||||||
dma_id_15,
|
dma_id_15,
|
||||||
} dma_id_t;
|
} dma_id_t;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
uint16_t enabled; // Used to test if both counters are == 0
|
||||||
|
uint8_t counter[2];
|
||||||
|
} dma_idle_count_t;
|
||||||
|
|
||||||
struct _dma_descr_t {
|
struct _dma_descr_t {
|
||||||
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
||||||
DMA_Stream_TypeDef *instance;
|
DMA_Stream_TypeDef *instance;
|
||||||
|
@ -530,9 +542,12 @@ void DMA2_Channel7_IRQHandler(void) { IRQ_ENTER(DMA2_Channel7_IRQn); if (dma_han
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void dma_idle_handler(uint32_t tick);
|
||||||
|
|
||||||
// Resets the idle counter for the DMA controller associated with dma_id.
|
// Resets the idle counter for the DMA controller associated with dma_id.
|
||||||
static void dma_tickle(dma_id_t dma_id) {
|
static void dma_tickle(dma_id_t dma_id) {
|
||||||
dma_idle.counter[(dma_id < NSTREAMS_PER_CONTROLLER) ? 0 : 1] = 1;
|
dma_idle.counter[(dma_id < NSTREAMS_PER_CONTROLLER) ? 0 : 1] = 1;
|
||||||
|
systick_enable_dispatch(SYSTICK_DISPATCH_DMA, dma_idle_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dma_enable_clock(dma_id_t dma_id) {
|
static void dma_enable_clock(dma_id_t dma_id) {
|
||||||
|
@ -681,12 +696,16 @@ void dma_invalidate_channel(const dma_descr_t *dma_descr) {
|
||||||
|
|
||||||
// Called from the SysTick handler
|
// Called from the SysTick handler
|
||||||
// We use LSB of tick to select which controller to process
|
// We use LSB of tick to select which controller to process
|
||||||
void dma_idle_handler(int tick) {
|
static void dma_idle_handler(uint32_t tick) {
|
||||||
|
if (!DMA_IDLE_ENABLED() || !DMA_IDLE_TICK(tick)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static const uint32_t controller_mask[] = {
|
static const uint32_t controller_mask[] = {
|
||||||
DMA1_ENABLE_MASK, DMA2_ENABLE_MASK
|
DMA1_ENABLE_MASK, DMA2_ENABLE_MASK
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
int controller = tick & 1;
|
int controller = (tick >> DMA_SYSTICK_LOG2) & 1;
|
||||||
if (dma_idle.counter[controller] == 0) {
|
if (dma_idle.counter[controller] == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,23 +81,9 @@ extern const dma_descr_t dma_SDIO_0;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef union {
|
|
||||||
uint16_t enabled; // Used to test if both counters are == 0
|
|
||||||
uint8_t counter[2];
|
|
||||||
} dma_idle_count_t;
|
|
||||||
extern volatile dma_idle_count_t dma_idle;
|
|
||||||
#define DMA_IDLE_ENABLED() (dma_idle.enabled != 0)
|
|
||||||
|
|
||||||
#define DMA_SYSTICK_MASK 0x0e
|
|
||||||
#define DMA_MSECS_PER_SYSTICK (DMA_SYSTICK_MASK + 1)
|
|
||||||
#define DMA_IDLE_TICK_MAX (8) // 128 msec
|
|
||||||
#define DMA_IDLE_TICK(tick) (((tick) & DMA_SYSTICK_MASK) == 0)
|
|
||||||
|
|
||||||
|
|
||||||
void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir, void *data);
|
void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir, void *data);
|
||||||
void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir, void *data);
|
void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir, void *data);
|
||||||
void dma_deinit(const dma_descr_t *dma_descr);
|
void dma_deinit(const dma_descr_t *dma_descr);
|
||||||
void dma_invalidate_channel(const dma_descr_t *dma_descr);
|
void dma_invalidate_channel(const dma_descr_t *dma_descr);
|
||||||
void dma_idle_handler(int controller);
|
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_STM32_DMA_H
|
#endif // MICROPY_INCLUDED_STM32_DMA_H
|
||||||
|
|
|
@ -299,54 +299,6 @@ void PendSV_Handler(void) {
|
||||||
pendsv_isr_handler();
|
pendsv_isr_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles SysTick Handler.
|
|
||||||
* @param None
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void SysTick_Handler(void) {
|
|
||||||
// Instead of calling HAL_IncTick we do the increment here of the counter.
|
|
||||||
// This is purely for efficiency, since SysTick is called 1000 times per
|
|
||||||
// second at the highest interrupt priority.
|
|
||||||
// Note: we don't need uwTick to be declared volatile here because this is
|
|
||||||
// the only place where it can be modified, and the code is more efficient
|
|
||||||
// without the volatile specifier.
|
|
||||||
extern uint32_t uwTick;
|
|
||||||
uwTick += 1;
|
|
||||||
|
|
||||||
// Read the systick control regster. This has the side effect of clearing
|
|
||||||
// the COUNTFLAG bit, which makes the logic in mp_hal_ticks_us
|
|
||||||
// work properly.
|
|
||||||
SysTick->CTRL;
|
|
||||||
|
|
||||||
// Right now we have the storage and DMA controllers to process during
|
|
||||||
// this interrupt and we use custom dispatch handlers. If this needs to
|
|
||||||
// be generalised in the future then a dispatch table can be used as
|
|
||||||
// follows: ((void(*)(void))(systick_dispatch[uwTick & 0xf]))();
|
|
||||||
|
|
||||||
#if MICROPY_HW_ENABLE_STORAGE
|
|
||||||
if (STORAGE_IDLE_TICK(uwTick)) {
|
|
||||||
NVIC->STIR = FLASH_IRQn;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (DMA_IDLE_ENABLED() && DMA_IDLE_TICK(uwTick)) {
|
|
||||||
dma_idle_handler(uwTick);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MICROPY_PY_THREAD
|
|
||||||
if (pyb_thread_enabled) {
|
|
||||||
if (pyb_thread_cur->timeslice == 0) {
|
|
||||||
if (pyb_thread_cur->run_next != pyb_thread_cur) {
|
|
||||||
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
--pyb_thread_cur->timeslice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* STM32F4xx Peripherals Interrupt Handlers */
|
/* STM32F4xx Peripherals Interrupt Handlers */
|
||||||
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */
|
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */
|
||||||
|
@ -465,23 +417,6 @@ void OTG_HS_WKUP_IRQHandler(void) {
|
||||||
{
|
{
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// Handle a flash (erase/program) interrupt.
|
|
||||||
void FLASH_IRQHandler(void) {
|
|
||||||
IRQ_ENTER(FLASH_IRQn);
|
|
||||||
// This calls the real flash IRQ handler, if needed
|
|
||||||
/*
|
|
||||||
uint32_t flash_cr = FLASH->CR;
|
|
||||||
if ((flash_cr & FLASH_IT_EOP) || (flash_cr & FLASH_IT_ERR)) {
|
|
||||||
HAL_FLASH_IRQHandler();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#if MICROPY_HW_ENABLE_STORAGE
|
|
||||||
// This call the storage IRQ handler, to check if the flash cache needs flushing
|
|
||||||
storage_irq_handler();
|
|
||||||
#endif
|
|
||||||
IRQ_EXIT(FLASH_IRQn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief These functions handle the EXTI interrupt requests.
|
* @brief These functions handle the EXTI interrupt requests.
|
||||||
* @param None
|
* @param None
|
||||||
|
|
|
@ -30,12 +30,16 @@
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "extmod/vfs_fat.h"
|
#include "extmod/vfs_fat.h"
|
||||||
|
|
||||||
|
#include "systick.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
|
|
||||||
#if MICROPY_HW_ENABLE_STORAGE
|
#if MICROPY_HW_ENABLE_STORAGE
|
||||||
|
|
||||||
|
#define STORAGE_SYSTICK_MASK (0x1ff) // 512ms
|
||||||
|
#define STORAGE_IDLE_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & STORAGE_SYSTICK_MASK) == 0)
|
||||||
|
|
||||||
#define FLASH_PART1_START_BLOCK (0x100)
|
#define FLASH_PART1_START_BLOCK (0x100)
|
||||||
|
|
||||||
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
||||||
|
@ -44,10 +48,14 @@
|
||||||
|
|
||||||
static bool storage_is_initialised = false;
|
static bool storage_is_initialised = false;
|
||||||
|
|
||||||
|
static void storage_systick_callback(uint32_t ticks_ms);
|
||||||
|
|
||||||
void storage_init(void) {
|
void storage_init(void) {
|
||||||
if (!storage_is_initialised) {
|
if (!storage_is_initialised) {
|
||||||
storage_is_initialised = true;
|
storage_is_initialised = true;
|
||||||
|
|
||||||
|
systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback);
|
||||||
|
|
||||||
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
|
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
|
||||||
|
|
||||||
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
||||||
|
@ -73,11 +81,20 @@ uint32_t storage_get_block_count(void) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void storage_irq_handler(void) {
|
static void storage_systick_callback(uint32_t ticks_ms) {
|
||||||
|
if (STORAGE_IDLE_TICK(ticks_ms)) {
|
||||||
|
// Trigger a FLASH IRQ to execute at a lower priority
|
||||||
|
NVIC->STIR = FLASH_IRQn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FLASH_IRQHandler(void) {
|
||||||
|
IRQ_ENTER(FLASH_IRQn);
|
||||||
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0);
|
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0);
|
||||||
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
||||||
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0);
|
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0);
|
||||||
#endif
|
#endif
|
||||||
|
IRQ_EXIT(FLASH_IRQn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void storage_flush(void) {
|
void storage_flush(void) {
|
||||||
|
|
|
@ -30,9 +30,6 @@
|
||||||
|
|
||||||
#define FLASH_BLOCK_SIZE (512)
|
#define FLASH_BLOCK_SIZE (512)
|
||||||
|
|
||||||
#define STORAGE_SYSTICK_MASK (0x1ff) // 512ms
|
|
||||||
#define STORAGE_IDLE_TICK(tick) (((tick) & STORAGE_SYSTICK_MASK) == 2)
|
|
||||||
|
|
||||||
// Try to match Python-level VFS block protocol where possible for these constants
|
// Try to match Python-level VFS block protocol where possible for these constants
|
||||||
enum {
|
enum {
|
||||||
BDEV_IOCTL_INIT = 1,
|
BDEV_IOCTL_INIT = 1,
|
||||||
|
@ -44,7 +41,6 @@ enum {
|
||||||
void storage_init(void);
|
void storage_init(void);
|
||||||
uint32_t storage_get_block_size(void);
|
uint32_t storage_get_block_size(void);
|
||||||
uint32_t storage_get_block_count(void);
|
uint32_t storage_get_block_count(void);
|
||||||
void storage_irq_handler(void);
|
|
||||||
void storage_flush(void);
|
void storage_flush(void);
|
||||||
bool storage_read_block(uint8_t *dest, uint32_t block);
|
bool storage_read_block(uint8_t *dest, uint32_t block);
|
||||||
bool storage_write_block(const uint8_t *src, uint32_t block);
|
bool storage_write_block(const uint8_t *src, uint32_t block);
|
||||||
|
|
|
@ -32,6 +32,39 @@
|
||||||
|
|
||||||
extern __IO uint32_t uwTick;
|
extern __IO uint32_t uwTick;
|
||||||
|
|
||||||
|
systick_dispatch_t systick_dispatch_table[SYSTICK_DISPATCH_NUM_SLOTS];
|
||||||
|
|
||||||
|
void SysTick_Handler(void) {
|
||||||
|
// Instead of calling HAL_IncTick we do the increment here of the counter.
|
||||||
|
// This is purely for efficiency, since SysTick is called 1000 times per
|
||||||
|
// second at the highest interrupt priority.
|
||||||
|
uint32_t uw_tick = uwTick + 1;
|
||||||
|
uwTick = uw_tick;
|
||||||
|
|
||||||
|
// Read the systick control regster. This has the side effect of clearing
|
||||||
|
// the COUNTFLAG bit, which makes the logic in mp_hal_ticks_us
|
||||||
|
// work properly.
|
||||||
|
SysTick->CTRL;
|
||||||
|
|
||||||
|
// Dispatch to any registered handlers in a cycle
|
||||||
|
systick_dispatch_t f = systick_dispatch_table[uw_tick & (SYSTICK_DISPATCH_NUM_SLOTS - 1)];
|
||||||
|
if (f != NULL) {
|
||||||
|
f(uw_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MICROPY_PY_THREAD
|
||||||
|
if (pyb_thread_enabled) {
|
||||||
|
if (pyb_thread_cur->timeslice == 0) {
|
||||||
|
if (pyb_thread_cur->run_next != pyb_thread_cur) {
|
||||||
|
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
--pyb_thread_cur->timeslice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// We provide our own version of HAL_Delay that calls __WFI while waiting,
|
// We provide our own version of HAL_Delay that calls __WFI while waiting,
|
||||||
// and works when interrupts are disabled. This function is intended to be
|
// and works when interrupts are disabled. This function is intended to be
|
||||||
// used only by the ST HAL functions.
|
// used only by the ST HAL functions.
|
||||||
|
|
|
@ -26,6 +26,22 @@
|
||||||
#ifndef MICROPY_INCLUDED_STM32_SYSTICK_H
|
#ifndef MICROPY_INCLUDED_STM32_SYSTICK_H
|
||||||
#define MICROPY_INCLUDED_STM32_SYSTICK_H
|
#define MICROPY_INCLUDED_STM32_SYSTICK_H
|
||||||
|
|
||||||
|
#define SYSTICK_DISPATCH_NUM_SLOTS (2)
|
||||||
|
#define SYSTICK_DISPATCH_DMA (0)
|
||||||
|
#define SYSTICK_DISPATCH_STORAGE (1)
|
||||||
|
|
||||||
|
typedef void (*systick_dispatch_t)(uint32_t);
|
||||||
|
|
||||||
|
extern systick_dispatch_t systick_dispatch_table[SYSTICK_DISPATCH_NUM_SLOTS];
|
||||||
|
|
||||||
|
static inline void systick_enable_dispatch(size_t slot, systick_dispatch_t f) {
|
||||||
|
systick_dispatch_table[slot] = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void systick_disable_dispatch(size_t slot) {
|
||||||
|
systick_dispatch_table[slot] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void systick_wait_at_least(uint32_t stc, uint32_t delay_ms);
|
void systick_wait_at_least(uint32_t stc, uint32_t delay_ms);
|
||||||
bool systick_has_passed(uint32_t stc, uint32_t delay_ms);
|
bool systick_has_passed(uint32_t stc, uint32_t delay_ms);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue