From 85e960e612d7ca16f2a8b2ecd1e536d2184ffa57 Mon Sep 17 00:00:00 2001 From: Thomas Klaehn Date: Sat, 18 Apr 2020 10:20:12 +0200 Subject: [PATCH] Add virtual timer --- .vscode/tasks.json | 3 +- Makefile | 1 + interfaces/HwTimerInterface.h | 19 +++++ interfaces/VirtualTimerInterface.h | 19 +++++ src/application/blinky/main.cc | 25 ++++-- src/application/spi/main.cc | 5 -- src/application/sys_tick/main.cc | 49 ------------ src/delay.cc | 27 +++++++ src/delay.h | 25 ++++++ src/platform/cm4/SystemTick.cc | 9 ++- src/platform/cm4/SystemTick.h | 27 +++++-- src/virtual_timer/VirtualTimer.cc | 38 ++++++++++ src/virtual_timer/VirtualTimer.h | 30 ++++++++ src/virtual_timer/VirtualTimerDistributor.cc | 80 ++++++++++++++++++++ src/virtual_timer/VirtualTimerDistributor.h | 41 ++++++++++ 15 files changed, 325 insertions(+), 73 deletions(-) create mode 100644 interfaces/HwTimerInterface.h create mode 100644 interfaces/VirtualTimerInterface.h delete mode 100644 src/application/sys_tick/main.cc create mode 100644 src/delay.cc create mode 100644 src/delay.h create mode 100644 src/virtual_timer/VirtualTimer.cc create mode 100644 src/virtual_timer/VirtualTimer.h create mode 100644 src/virtual_timer/VirtualTimerDistributor.cc create mode 100644 src/virtual_timer/VirtualTimerDistributor.h diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0f9bc0e..b645cd0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,10 +2,9 @@ "version": "2.0.0", "options": { "env": { - // "APPLICATION": "blinky", + "APPLICATION": "blinky", // "APPLICATION": "spi", // "APPLICATION": "st7789_lcd", - "APPLICATION": "sys_tick", }, }, "presentation": { diff --git a/Makefile b/Makefile index 9bf9e2c..de07461 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ CC_SRCS = $(wildcard $(SRC_DIR)/*.cc) CC_SRCS += $(wildcard $(SRC_DIR)/platform/$(CORE)/*.cc) CC_SRCS += $(wildcard $(SRC_DIR)/platform/$(PLATFORM)/*.cc) CC_SRCS += $(wildcard $(SRC_DIR)/application/$(APPLICATION)/*.cc) +CC_SRCS += $(wildcard $(SRC_DIR)/virtual_timer/*.cc) CC_OBJS = $(patsubst $(SRC_DIR)%,$(OBJ_DIR)%,$(patsubst %.cc,%.cc.o,$(CC_SRCS))) OBJS = $(A_OBJS) $(C_OBJS) $(CC_OBJS) diff --git a/interfaces/HwTimerInterface.h b/interfaces/HwTimerInterface.h new file mode 100644 index 0000000..e22d84a --- /dev/null +++ b/interfaces/HwTimerInterface.h @@ -0,0 +1,19 @@ +#ifndef __INTERFACES_HWTIMERINTERFACE_H__ +#define __INTERFACES_HWTIMERINTERFACE_H__ + +#include + +namespace pinetime::interfaces +{ + +class HwTimerInterface +{ +public: + virtual void enable_timer() = 0; + virtual void disable_timer() = 0; + virtual uint64_t tick() = 0; +}; + +} + +#endif diff --git a/interfaces/VirtualTimerInterface.h b/interfaces/VirtualTimerInterface.h new file mode 100644 index 0000000..ff1e7fd --- /dev/null +++ b/interfaces/VirtualTimerInterface.h @@ -0,0 +1,19 @@ +#ifndef __INTERFACES_VIRTUALTIMERINTERFACE_H__ +#define __INTERFACES_VIRTUALTIMERINTERFACE_H__ + +#include + +namespace pinetime::interfaces +{ + +class VirtualTimerInterface +{ +public: + virtual void timer_notification(uint64_t) = 0; + virtual void timer_enable() = 0; + virtual void timer_disable() = 0; +}; + +} + +#endif diff --git a/src/application/blinky/main.cc b/src/application/blinky/main.cc index 47fec2e..0a8ad6f 100644 --- a/src/application/blinky/main.cc +++ b/src/application/blinky/main.cc @@ -1,15 +1,19 @@ #include -#include "delay.h" +#include "virtual_timer/VirtualTimerDistributor.h" #include "platform/cm4/InterruptHandler.h" #include "platform/cm4/InterruptGuardian.h" +#include "platform/cm4/SystemTick.h" #include "platform/nrf52/gpio.h" #include "platform/nrf52/InterruptHandler.h" #include "platform/nrf52/InterruptGuardian.h" +#include "delay.h" + using namespace pinetime::platform; +using namespace pinetime::virtual_timer; enum { PIN_NUMBER_LED_1 = 17, @@ -18,23 +22,30 @@ enum { PIN_NUMBER_LED_4 = 20 }; +// LEDs nrf52::Gpio led_1(PIN_NUMBER_LED_1); nrf52::Gpio led_2(PIN_NUMBER_LED_2); nrf52::Gpio led_3(PIN_NUMBER_LED_3); nrf52::Gpio led_4(PIN_NUMBER_LED_4); - std::array leds = {&led_1, &led_2, &led_3, &led_4}; -cm4::InterruptGuardian cm4::InterruptGuardian::instance; +// IRQs nrf52::InterruptGuardian nrf52::InterruptGuardian::instance; +cm4::InterruptGuardian cm4::InterruptGuardian::instance; + +// Timer +cm4::SystemTick system_tick; +VirtualTimerDistributor virtual_timer_distributor(system_tick.instance()); +pinetime::Delay delay; int main(void) { + cm4::InterruptGuardian::enable_interrupts(); while(true) { - for(auto it = std::begin(leds); it != std::end(leds); ++it) { - nrf52::Gpio * tmp = *it; - tmp->toggle(); - delay_ms(500); + for(auto it = leds.begin(); it != leds.end(); ++it) { + nrf52::Gpio * led = *it; + led->toggle(); + delay.ms(200); } } return 0; diff --git a/src/application/spi/main.cc b/src/application/spi/main.cc index 3f742f9..1476946 100644 --- a/src/application/spi/main.cc +++ b/src/application/spi/main.cc @@ -2,8 +2,6 @@ #include "platform/cm4/InterruptHandler.h" #include "platform/cm4/InterruptGuardian.h" -#include "platform/cm4/SystemTick.h" - #include "platform/nrf52/gpio.h" #include "platform/nrf52/spi.h" #include "platform/nrf52/InterruptHandler.h" @@ -21,9 +19,6 @@ nrf52::InterruptGuardian nrf52::InterruptGuardian::instance; int main(void) { - cm4::SystemTick ticker; - ticker.enable(); - cm4::InterruptGuardian::enable_interrupts(); nrf52::Spi spi_0(0, 2, 3, 4, lcd_chip_select); while(1) { diff --git a/src/application/sys_tick/main.cc b/src/application/sys_tick/main.cc deleted file mode 100644 index 2c8e756..0000000 --- a/src/application/sys_tick/main.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include - -#include "platform/cm4/InterruptHandler.h" -#include "platform/cm4/InterruptGuardian.h" -#include "platform/cm4/SystemTick.h" - -#include "platform/nrf52/gpio.h" -#include "platform/nrf52/InterruptHandler.h" -#include "platform/nrf52/InterruptGuardian.h" - -using namespace pinetime::platform; - -enum { - PIN_NUMBER_LED_1 = 17, - PIN_NUMBER_LED_2 = 18, - PIN_NUMBER_LED_3 = 19, - PIN_NUMBER_LED_4 = 20 -}; - -nrf52::Gpio led_1(PIN_NUMBER_LED_1); -nrf52::Gpio led_2(PIN_NUMBER_LED_2); -nrf52::Gpio led_3(PIN_NUMBER_LED_3); -nrf52::Gpio led_4(PIN_NUMBER_LED_4); - -std::array leds = {&led_1, &led_2, &led_3, &led_4}; - -nrf52::InterruptGuardian nrf52::InterruptGuardian::instance; -cm4::InterruptGuardian cm4::InterruptGuardian::instance; - - -int main(void) -{ - cm4::SystemTick ticker; - ticker.enable(); - uint32_t last_tick = 0; - - cm4::InterruptGuardian::enable_interrupts(); - while(true) { - for(auto it = std::begin(leds); it != std::end(leds); ++it) { - volatile uint32_t tick = ticker.tick() / 1000; - if(tick != last_tick) { - nrf52::Gpio * tmp = *it; - tmp->toggle(); - last_tick = tick; - } - } - } - return 0; -} diff --git a/src/delay.cc b/src/delay.cc new file mode 100644 index 0000000..76d3dd3 --- /dev/null +++ b/src/delay.cc @@ -0,0 +1,27 @@ +#include "delay.h" + +using namespace pinetime; +using namespace pinetime::virtual_timer; + +Delay::Delay() + : VirtualTimer(0) + , pause(false) +{ +} + +void Delay::ms(uint64_t time_ms) +{ + this->pause = true; + this->timer_set_period_ms(time_ms); + this->timer_enable(); + while(this->pause) { + // FIXME: Low power + asm volatile("nop"); + } + this->timer_disable(); +} + +void Delay::notification() +{ + this->pause = false; +} diff --git a/src/delay.h b/src/delay.h new file mode 100644 index 0000000..d032c6a --- /dev/null +++ b/src/delay.h @@ -0,0 +1,25 @@ +#ifndef __DELAY_H__ +#define __DELAY_H__ + +#include "virtual_timer/VirtualTimer.h" + +namespace pinetime +{ + +class Delay + : public pinetime::virtual_timer::VirtualTimer +{ + +public: + Delay(); + void ms(uint64_t); + + void notification(); + +private: + bool pause; +}; + +} + +#endif diff --git a/src/platform/cm4/SystemTick.cc b/src/platform/cm4/SystemTick.cc index c95adfa..85047ff 100644 --- a/src/platform/cm4/SystemTick.cc +++ b/src/platform/cm4/SystemTick.cc @@ -2,11 +2,13 @@ extern "C" { #include "nrf52.h" } +#include "virtual_timer/VirtualTimerDistributor.h" #include "platform/cm4/InterruptHandler.h" #include "platform/cm4/InterruptGuardian.h" #include "platform/cm4/SystemTick.h" using namespace pinetime::platform::cm4; +using namespace pinetime::virtual_timer; SystemTick::SystemTick() : InterruptHandler(InterruptGuardian::Cm4IrqN::SYS_TICK_IRQ) @@ -17,19 +19,20 @@ SystemTick::SystemTick() void SystemTick::handle() { this->sys_tick++; + VirtualTimerDistributor::instance().notify(this->sys_tick); } -void SystemTick::enable() +void SystemTick::enable_timer() { SysTick_Config(SystemCoreClock / 1000); } -void SystemTick::disable() +void SystemTick::disable_timer() { SysTick->CTRL = 0; } -uint32_t SystemTick::tick() +uint64_t SystemTick::tick() { return this->sys_tick; } diff --git a/src/platform/cm4/SystemTick.h b/src/platform/cm4/SystemTick.h index 360383b..75c4063 100644 --- a/src/platform/cm4/SystemTick.h +++ b/src/platform/cm4/SystemTick.h @@ -1,21 +1,34 @@ #ifndef __PLATFORM_CM4_SYSTEMTICK_H__ #define __PLATFORM_CM4_SYSTEMTICK_H__ +#include "HwTimerInterface.h" #include "platform/cm4/InterruptHandler.h" -namespace pinetime::platform::cm4 { +namespace pinetime::platform::cm4 +{ +class SystemTick; +} +extern pinetime::platform::cm4::SystemTick system_tick; -class SystemTick : public pinetime::platform::cm4::InterruptHandler +namespace pinetime::platform::cm4 +{ +class SystemTick + : public pinetime::platform::cm4::InterruptHandler + , public pinetime::interfaces::HwTimerInterface { public: SystemTick(); - void handle(); - void enable(); - void disable(); - uint32_t tick(); + + static inline SystemTick & instance() { return system_tick; } + + void enable_timer() override; + void disable_timer() override; + uint64_t tick() override; + + void handle() override; private: - uint32_t sys_tick; + uint64_t sys_tick; }; } diff --git a/src/virtual_timer/VirtualTimer.cc b/src/virtual_timer/VirtualTimer.cc new file mode 100644 index 0000000..98e7a06 --- /dev/null +++ b/src/virtual_timer/VirtualTimer.cc @@ -0,0 +1,38 @@ +#include "virtual_timer/VirtualTimerDistributor.h" +#include "virtual_timer/VirtualTimer.h" + +using namespace pinetime::virtual_timer; + +VirtualTimer::VirtualTimer(uint64_t period_ms) + : period(period_ms) +{ +} + +void VirtualTimer::timer_enable() +{ + VirtualTimerDistributor & distri = VirtualTimerDistributor::instance(); + this->start_time = distri.tick(); + distri.register_timer(this); +} + +void VirtualTimer::timer_disable() +{ + VirtualTimerDistributor::instance().unregister_timer(this); +} + +void VirtualTimer::timer_notification(uint64_t tick_ms) +{ + if((tick_ms - this->start_time) >= (this->period)) { + this->notification(); + } +} + +void VirtualTimer::timer_set_period_ms(uint64_t period_ms) +{ + this->period = period_ms; +} + +// void VirtualTimer::notify() +// { +// asm volatile("nop"); +// } \ No newline at end of file diff --git a/src/virtual_timer/VirtualTimer.h b/src/virtual_timer/VirtualTimer.h new file mode 100644 index 0000000..62bc03f --- /dev/null +++ b/src/virtual_timer/VirtualTimer.h @@ -0,0 +1,30 @@ +#ifndef __VIRTUAL_TIMER_VIRTUALTIMER_H__ +#define __VIRTUAL_TIMER_VIRTUALTIMER_H__ + +#include "VirtualTimerInterface.h" + +namespace pinetime::virtual_timer +{ + +class VirtualTimer + : public pinetime::interfaces::VirtualTimerInterface +{ +public: + VirtualTimer(uint64_t); + + void timer_enable() override; + void timer_disable() override; + void timer_notification(uint64_t) override; + + void timer_set_period_ms(uint64_t); + + virtual void notification() = 0; + +private: + uint64_t period; + uint64_t start_time; +}; + +} + +#endif diff --git a/src/virtual_timer/VirtualTimerDistributor.cc b/src/virtual_timer/VirtualTimerDistributor.cc new file mode 100644 index 0000000..2af8be0 --- /dev/null +++ b/src/virtual_timer/VirtualTimerDistributor.cc @@ -0,0 +1,80 @@ +#include "virtual_timer/VirtualTimer.h" +#include "virtual_timer/VirtualTimerDistributor.h" + +using namespace pinetime::virtual_timer; +using namespace pinetime::interfaces; + +VirtualTimerDistributor::VirtualTimerDistributor(pinetime::interfaces::HwTimerInterface & timer) + : hw_timer(timer) + , num_registered_timers(0) +{ + for(auto it = this->virtual_timer_list.begin(); it != this->virtual_timer_list.end(); ++it) { + *it = nullptr; + } +} + +void VirtualTimerDistributor::register_timer(VirtualTimerInterface *timer) +{ + volatile bool enable = false; + if(num_registered_timers == MAX_TIMERS) { + // FIXME: Error notification + return; + } + if(num_registered_timers == 0) { + // Timer list is empty - so - hw timer isn't running + enable = true; + } else { + // Check if timer already registered + for(auto it = this->virtual_timer_list.begin(); it != this->virtual_timer_list.end(); ++it) { + if(*it == timer) { + return; + } + } + } + for(auto it = this->virtual_timer_list.begin(); it != this->virtual_timer_list.end(); ++it) { + if(*it == nullptr) { + // FIXME: We might need an interrupt lock here? + *it = timer; + num_registered_timers++; + break; + } + } + if(enable) { + hw_timer.enable_timer(); + } +} + +void VirtualTimerDistributor::unregister_timer(VirtualTimerInterface *timer) +{ + if(num_registered_timers == 0) { + // FIXME: Error notification + return; + } + for(auto it = this->virtual_timer_list.begin(); it != this->virtual_timer_list.end(); ++it) { + if(*it == timer) { + // FIXME: We might need an interrupt lock here? + *it = nullptr; + num_registered_timers--; + break; + } + } + if(num_registered_timers == 0) { + hw_timer.disable_timer(); + } +} + +void VirtualTimerDistributor::notify(uint64_t time_ms) +{ + // FIXME: We're still in interrupt context of hw timer interrupt. + for(auto it = this->virtual_timer_list.begin(); it != this->virtual_timer_list.end(); ++it) { + if(*it != nullptr) { + VirtualTimerInterface *vt = *it; + vt->timer_notification(time_ms); + } + } +} + +uint64_t VirtualTimerDistributor::tick() +{ + return hw_timer.tick(); +} diff --git a/src/virtual_timer/VirtualTimerDistributor.h b/src/virtual_timer/VirtualTimerDistributor.h new file mode 100644 index 0000000..fc98645 --- /dev/null +++ b/src/virtual_timer/VirtualTimerDistributor.h @@ -0,0 +1,41 @@ +#ifndef __VIRTUAL_TIMER_VIRTUALTIMERDISTRIBUTOR_H__ +#define __VIRTUAL_TIMER_VIRTUALTIMERDISTRIBUTOR_H__ + +#include + +#include "HwTimerInterface.h" +#include "VirtualTimerInterface.h" + +namespace pinetime::virtual_timer +{ +class VirtualTimerDistributor; +} +extern pinetime::virtual_timer::VirtualTimerDistributor virtual_timer_distributor; + +namespace pinetime::virtual_timer +{ + +class VirtualTimerDistributor +{ + public: + VirtualTimerDistributor(pinetime::interfaces::HwTimerInterface &); + + static inline VirtualTimerDistributor& instance() { return virtual_timer_distributor; } + + void register_timer(pinetime::interfaces::VirtualTimerInterface *); + void unregister_timer(pinetime::interfaces::VirtualTimerInterface *); + + void notify(uint64_t); + uint64_t tick(); + + private: + static const uint32_t MAX_TIMERS = 8; + + std::array virtual_timer_list; + pinetime::interfaces::HwTimerInterface & hw_timer; + uint32_t num_registered_timers; +}; + +} + +#endif