From e70de68d991a4c3211e93d4f7e24cca7c156b533 Mon Sep 17 00:00:00 2001 From: Thomas Klaehn Date: Sat, 25 Apr 2020 10:09:38 +0200 Subject: [PATCH] Add gpio event driver --- .vscode/tasks.json | 3 +- interfaces/gpio_interface.h | 5 +- src/application/blinky/main.cc | 22 ++++--- src/application/button/main.cc | 71 +++++++++++++++++++++ src/platform/nrf52/gpio.cc | 63 ++++++++++++++----- src/platform/nrf52/gpio.h | 30 +++++---- src/platform/nrf52/gpiote.cc | 77 +++++++++++++++++++++++ src/platform/nrf52/gpiote.h | 44 +++++++++++++ src/platform/nrf52/low_level_interrupt.cc | 10 +++ 9 files changed, 289 insertions(+), 36 deletions(-) create mode 100644 src/application/button/main.cc create mode 100644 src/platform/nrf52/gpiote.cc create mode 100644 src/platform/nrf52/gpiote.h diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b645cd0..960861f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,7 +2,8 @@ "version": "2.0.0", "options": { "env": { - "APPLICATION": "blinky", + // "APPLICATION": "blinky", + "APPLICATION": "button", // "APPLICATION": "spi", // "APPLICATION": "st7789_lcd", }, diff --git a/interfaces/gpio_interface.h b/interfaces/gpio_interface.h index e3df6cd..36a3e31 100644 --- a/interfaces/gpio_interface.h +++ b/interfaces/gpio_interface.h @@ -10,11 +10,14 @@ class GpioInterface public: enum class direction {IN, OUT}; - virtual void set_direction(direction) = 0; + virtual void set_direction(direction, bool) = 0; virtual uint32_t get() = 0; virtual void set() = 0; virtual void clear() = 0; virtual void toggle() = 0; + + virtual void handle() = 0; + virtual uint32_t pin_number() = 0; }; } diff --git a/src/application/blinky/main.cc b/src/application/blinky/main.cc index 0a8ad6f..b7b3a6a 100644 --- a/src/application/blinky/main.cc +++ b/src/application/blinky/main.cc @@ -7,6 +7,7 @@ #include "platform/cm4/SystemTick.h" #include "platform/nrf52/gpio.h" +#include "platform/nrf52/gpiote.h" #include "platform/nrf52/InterruptHandler.h" #include "platform/nrf52/InterruptGuardian.h" @@ -15,6 +16,18 @@ using namespace pinetime::platform; using namespace pinetime::virtual_timer; +// IRQs +nrf52::InterruptGuardian nrf52::InterruptGuardian::instance; +cm4::InterruptGuardian cm4::InterruptGuardian::instance; + +// GPIO events +nrf52::Gpiote gpiote; + +// Timer +cm4::SystemTick system_tick; +VirtualTimerDistributor virtual_timer_distributor(system_tick.instance()); +pinetime::Delay delay; + enum { PIN_NUMBER_LED_1 = 17, PIN_NUMBER_LED_2 = 18, @@ -29,15 +42,6 @@ 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}; -// 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(); diff --git a/src/application/button/main.cc b/src/application/button/main.cc new file mode 100644 index 0000000..c275b06 --- /dev/null +++ b/src/application/button/main.cc @@ -0,0 +1,71 @@ +#include + +#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/gpiote.h" +#include "platform/nrf52/InterruptHandler.h" +#include "platform/nrf52/InterruptGuardian.h" + +#include "gpio_interface.h" +#include "delay.h" + +using namespace pinetime::platform; +using namespace pinetime::virtual_timer; + +// IRQs +nrf52::InterruptGuardian nrf52::InterruptGuardian::instance; +cm4::InterruptGuardian cm4::InterruptGuardian::instance; + +// GPIO events +nrf52::Gpiote gpiote; + +// Timer +cm4::SystemTick system_tick; +VirtualTimerDistributor virtual_timer_distributor(system_tick.instance()); +pinetime::Delay delay; + + +enum { + PIN_NUMBER_BUTTON_1 = 13, + PIN_NUMBER_BUTTON_2 = 14, + PIN_NUMBER_BUTTON_3 = 15, + PIN_NUMBER_BUTTON_4 = 16, + PIN_NUMBER_LED_1 = 17, + PIN_NUMBER_LED_2 = 18, + PIN_NUMBER_LED_3 = 19, + 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}; + +// Buttons +nrf52::Gpio button_1(PIN_NUMBER_BUTTON_1); +nrf52::Gpio button_2(PIN_NUMBER_BUTTON_2); +nrf52::Gpio button_3(PIN_NUMBER_BUTTON_3); +nrf52::Gpio button_4(PIN_NUMBER_BUTTON_4); + +int main(void) +{ + button_1.set_direction(pinetime::interfaces::GpioInterface::direction::IN, true); + cm4::InterruptGuardian::enable_interrupts(); + while(true) { + for(auto it = leds.begin(); it != leds.end(); ++it) { + uint32_t b1 = button_1.get(); + if(b1) { + nrf52::Gpio * led = *it; + led->toggle(); + } + } + } + return 0; +} diff --git a/src/platform/nrf52/gpio.cc b/src/platform/nrf52/gpio.cc index 20e8a34..b3c04fe 100644 --- a/src/platform/nrf52/gpio.cc +++ b/src/platform/nrf52/gpio.cc @@ -1,22 +1,33 @@ -#include "platform/nrf52/gpio.h" - extern "C" { #include "nrf52.h" #include "nrf52_bitfields.h" NRF_GPIO_Type *const GPIO_REGS = reinterpret_cast(NRF_P0_BASE); + NRF_GPIOTE_Type *const GPIOTE_REGS = reinterpret_cast(NRF_GPIOTE_BASE); } +#include "platform/nrf52/gpiote.h" +#include "platform/nrf52/gpio.h" + using namespace pinetime::platform::nrf52; +Gpio::Gpio() + : pin(0) + , blocking(false) + , wait_for_event(false) +{ +} + Gpio::Gpio(uint32_t pin) - : pin_number(pin) + : pin(pin) + , blocking(false) + , wait_for_event(false) { this->set_direction(direction::OUT); this->clear(); } -void Gpio::set_direction(direction dir) +void Gpio::set_direction(direction dir, bool blocking_read) { uint32_t direct = GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos; uint32_t input = GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos; @@ -33,36 +44,60 @@ void Gpio::set_direction(direction dir) // GPIO_PIN_CNF_PULL_Pullup, ///< Pin pull-up resistor enabled. pull = GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos; drive = GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos; - sense = GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos; + sense = GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos; + + gpiote.instance().register_handler(this); + + this->blocking = blocking_read; + + gpiote.instance().enable(); } - GPIO_REGS->PIN_CNF[pin_number] = direct | input | pull | drive | sense; + GPIO_REGS->PIN_CNF[pin] = direct | input | pull | drive | sense; } -void Gpio::set_pin_number(uint32_t pin) +void Gpio::pin_number(uint32_t pin) { - this->pin_number = pin; + this->pin = pin; +} + +uint32_t Gpio::pin_number() +{ + return this->pin; } uint32_t Gpio::get() { - uint32_t res = (GPIO_REGS->IN >> pin_number) & 1UL; + if(this->blocking) { + this->wait_for_event = true; + while(this->wait_for_event == true) { + // FIXME: Low Power + asm volatile("nop"); + } + } + uint32_t res = (GPIO_REGS->IN >> pin) & 1UL; + return res; } void Gpio::set() { - GPIO_REGS->OUTSET = 1UL << (this->pin_number); + GPIO_REGS->OUTSET = 1UL << (this->pin); } void Gpio::clear() { - GPIO_REGS->OUTCLR = 1UL << (this->pin_number); + GPIO_REGS->OUTCLR = 1UL << (this->pin); } void Gpio::toggle() { uint32_t state = GPIO_REGS->OUT; - GPIO_REGS->OUTSET = (~state & (1UL << (this->pin_number))); - GPIO_REGS->OUTCLR = (state & (1UL << (this->pin_number))); -} \ No newline at end of file + GPIO_REGS->OUTSET = (~state & (1UL << (this->pin))); + GPIO_REGS->OUTCLR = (state & (1UL << (this->pin))); +} + +void Gpio::handle() +{ + this->wait_for_event = false; +} diff --git a/src/platform/nrf52/gpio.h b/src/platform/nrf52/gpio.h index e16e536..0164571 100644 --- a/src/platform/nrf52/gpio.h +++ b/src/platform/nrf52/gpio.h @@ -3,22 +3,30 @@ #include "gpio_interface.h" +#include "platform/nrf52/InterruptHandler.h" + namespace pinetime::platform::nrf52 { class Gpio : public pinetime::interfaces::GpioInterface { - public: - inline Gpio() {} - Gpio(uint32_t); - void set_pin_number(uint32_t); - void set_direction(direction) override; - uint32_t get() override; - void set() override; - void clear() override; - void toggle() override; +public: + Gpio(); + Gpio(uint32_t); + void pin_number(uint32_t); + uint32_t pin_number() override; - private: - uint32_t pin_number; + void set_direction(direction, bool blocking_read=false) override; + uint32_t get() override; + void set() override; + void clear() override; + void toggle() override; + + void handle() override; + +private: + uint32_t pin; + bool blocking; + volatile bool wait_for_event; }; } diff --git a/src/platform/nrf52/gpiote.cc b/src/platform/nrf52/gpiote.cc new file mode 100644 index 0000000..dcc729f --- /dev/null +++ b/src/platform/nrf52/gpiote.cc @@ -0,0 +1,77 @@ +#include + +#include "platform/nrf52/InterruptHandler.h" +#include "platform/nrf52/InterruptGuardian.h" + +#include "platform/nrf52/gpiote.h" + +extern "C" { + #include "nrf52.h" + #include "nrf52_bitfields.h" + + NRF_GPIOTE_Type *const GPIOTE_REGS = reinterpret_cast(NRF_GPIOTE_BASE); +} + +using namespace pinetime::platform::nrf52; + +Gpiote::Gpiote() + : InterruptHandler(InterruptGuardian::Nrf52IrqN::GPIOTE_IRQ) + , num_registered(0) +{ + for(auto it = this->gpiote_channels.begin(); it != this->gpiote_channels.end(); ++it) { + *it = nullptr; + } +} + +void Gpiote::enable() +{ + NVIC_SetPriority(GPIOTE_IRQn, 7); + NVIC_EnableIRQ(GPIOTE_IRQn); +} + +void Gpiote::disable() +{ + NVIC_DisableIRQ(GPIOTE_IRQn); +} + +void Gpiote::register_handler(pinetime::interfaces::GpioInterface *gpio) +{ + if(num_registered == GPIOTE_CHANNELS) { + //FIXME: Error notification + return; + } + + if(num_registered == 0) { + this->enable(); + } else { + //check if gpio already registered + for(auto it = this->gpiote_channels.begin(); it != this->gpiote_channels.end(); ++it) { + if(*it == gpio) { + return; + } + } + } + uint32_t i = 0; + for(auto it = this->gpiote_channels.begin(); it != this->gpiote_channels.end(); ++it) { + if(*it == nullptr) { + *it = gpio; + GPIOTE_REGS->INTENSET = GPIOTE_INTENSET_IN0_Msk; + GPIOTE_REGS->CONFIG[i] = (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) + | ((gpio->pin_number()) << GPIOTE_CONFIG_PSEL_Pos) + | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos); + } + i++; + } +} + +void Gpiote::handle() +{ + for(uint32_t i = 0; i < GPIOTE_CHANNELS; ++i) { + if(GPIOTE_REGS->EVENTS_IN[i] != 0) { + GPIOTE_REGS->EVENTS_IN[i] = 0; + pinetime::interfaces::GpioInterface *h = this->gpiote_channels[i]; + assert(h != nullptr); + h->handle(); + } + } +} diff --git a/src/platform/nrf52/gpiote.h b/src/platform/nrf52/gpiote.h new file mode 100644 index 0000000..6b7408c --- /dev/null +++ b/src/platform/nrf52/gpiote.h @@ -0,0 +1,44 @@ +#ifndef __PLATFORM_NRF52_GPIOTE_H__ +#define __PLATFORM_NRF52_GPIOTE_H__ + +#include + +#include "gpio_interface.h" +#include "platform/nrf52/InterruptHandler.h" + +namespace pinetime::platform::nrf52 +{ +class Gpiote; +} +extern pinetime::platform::nrf52::Gpiote gpiote; + +namespace pinetime::platform::nrf52 +{ + +class Gpiote + : public pinetime::platform::nrf52::InterruptHandler +{ +public: + Gpiote(); + + static inline Gpiote & instance() { return gpiote; } + + void enable() override; + void disable() override; + + void register_handler(pinetime::interfaces::GpioInterface *); + +private: + void handle() override; + + enum { + GPIOTE_CHANNELS = 8, + }; + + std::array gpiote_channels; + uint32_t num_registered; +}; + +} + +#endif diff --git a/src/platform/nrf52/low_level_interrupt.cc b/src/platform/nrf52/low_level_interrupt.cc index 35807ab..c185c90 100644 --- a/src/platform/nrf52/low_level_interrupt.cc +++ b/src/platform/nrf52/low_level_interrupt.cc @@ -54,3 +54,13 @@ void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) h->handle(); } + +void GPIOTE_IRQHandler(void) +{ + uint32_t irq_nr = InterruptGuardian::Nrf52IrqN::GPIOTE_IRQ; + InterruptHandler *h = InterruptGuardian::instance.nrf52_vector[irq_nr]; + + assert(h != nullptr); + + h->handle(); +}