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 "py/obj.h"
|
||||
#include "systick.h"
|
||||
#include "dma.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 {
|
||||
dma_id_not_defined=-1,
|
||||
dma_id_0,
|
||||
@ -52,6 +59,11 @@ typedef enum {
|
||||
dma_id_15,
|
||||
} 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 {
|
||||
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
||||
DMA_Stream_TypeDef *instance;
|
||||
@ -530,9 +542,12 @@ void DMA2_Channel7_IRQHandler(void) { IRQ_ENTER(DMA2_Channel7_IRQn); if (dma_han
|
||||
|
||||
#endif
|
||||
|
||||
static void dma_idle_handler(uint32_t tick);
|
||||
|
||||
// Resets the idle counter for the DMA controller associated with dma_id.
|
||||
static void dma_tickle(dma_id_t dma_id) {
|
||||
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) {
|
||||
@ -681,12 +696,16 @@ void dma_invalidate_channel(const dma_descr_t *dma_descr) {
|
||||
|
||||
// Called from the SysTick handler
|
||||
// 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[] = {
|
||||
DMA1_ENABLE_MASK, DMA2_ENABLE_MASK
|
||||
};
|
||||
{
|
||||
int controller = tick & 1;
|
||||
int controller = (tick >> DMA_SYSTICK_LOG2) & 1;
|
||||
if (dma_idle.counter[controller] == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -81,23 +81,9 @@ extern const dma_descr_t dma_SDIO_0;
|
||||
|
||||
#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_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_invalidate_channel(const dma_descr_t *dma_descr);
|
||||
void dma_idle_handler(int controller);
|
||||
|
||||
#endif // MICROPY_INCLUDED_STM32_DMA_H
|
||||
|
@ -299,54 +299,6 @@ void PendSV_Handler(void) {
|
||||
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 */
|
||||
/* 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.
|
||||
* @param None
|
||||
|
@ -30,12 +30,16 @@
|
||||
#include "py/runtime.h"
|
||||
#include "extmod/vfs_fat.h"
|
||||
|
||||
#include "systick.h"
|
||||
#include "led.h"
|
||||
#include "storage.h"
|
||||
#include "irq.h"
|
||||
|
||||
#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)
|
||||
|
||||
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
||||
@ -44,10 +48,14 @@
|
||||
|
||||
static bool storage_is_initialised = false;
|
||||
|
||||
static void storage_systick_callback(uint32_t ticks_ms);
|
||||
|
||||
void storage_init(void) {
|
||||
if (!storage_is_initialised) {
|
||||
storage_is_initialised = true;
|
||||
|
||||
systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback);
|
||||
|
||||
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
|
||||
|
||||
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
||||
@ -73,11 +81,20 @@ uint32_t storage_get_block_count(void) {
|
||||
#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);
|
||||
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
||||
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0);
|
||||
#endif
|
||||
IRQ_EXIT(FLASH_IRQn);
|
||||
}
|
||||
|
||||
void storage_flush(void) {
|
||||
|
@ -30,9 +30,6 @@
|
||||
|
||||
#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
|
||||
enum {
|
||||
BDEV_IOCTL_INIT = 1,
|
||||
@ -44,7 +41,6 @@ enum {
|
||||
void storage_init(void);
|
||||
uint32_t storage_get_block_size(void);
|
||||
uint32_t storage_get_block_count(void);
|
||||
void storage_irq_handler(void);
|
||||
void storage_flush(void);
|
||||
bool storage_read_block(uint8_t *dest, uint32_t block);
|
||||
bool storage_write_block(const uint8_t *src, uint32_t block);
|
||||
|
@ -32,6 +32,39 @@
|
||||
|
||||
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,
|
||||
// and works when interrupts are disabled. This function is intended to be
|
||||
// used only by the ST HAL functions.
|
||||
|
@ -26,6 +26,22 @@
|
||||
#ifndef 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);
|
||||
bool systick_has_passed(uint32_t stc, uint32_t delay_ms);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user