Add gpio event driver

This commit is contained in:
Thomas Klaehn 2020-04-25 10:09:38 +02:00
parent 85e960e612
commit 2e3d0c6f80
9 changed files with 290 additions and 36 deletions

3
.vscode/tasks.json vendored
View File

@ -2,7 +2,8 @@
"version": "2.0.0", "version": "2.0.0",
"options": { "options": {
"env": { "env": {
"APPLICATION": "blinky", // "APPLICATION": "blinky",
"APPLICATION": "button",
// "APPLICATION": "spi", // "APPLICATION": "spi",
// "APPLICATION": "st7789_lcd", // "APPLICATION": "st7789_lcd",
}, },

View File

@ -10,11 +10,14 @@ class GpioInterface
public: public:
enum class direction {IN, OUT}; enum class direction {IN, OUT};
virtual void set_direction(direction) = 0; virtual void set_direction(direction, bool) = 0;
virtual uint32_t get() = 0; virtual uint32_t get() = 0;
virtual void set() = 0; virtual void set() = 0;
virtual void clear() = 0; virtual void clear() = 0;
virtual void toggle() = 0; virtual void toggle() = 0;
virtual void handle() = 0;
virtual uint32_t pin_number() = 0;
}; };
} }

View File

@ -7,6 +7,7 @@
#include "platform/cm4/SystemTick.h" #include "platform/cm4/SystemTick.h"
#include "platform/nrf52/gpio.h" #include "platform/nrf52/gpio.h"
#include "platform/nrf52/gpiote.h"
#include "platform/nrf52/InterruptHandler.h" #include "platform/nrf52/InterruptHandler.h"
#include "platform/nrf52/InterruptGuardian.h" #include "platform/nrf52/InterruptGuardian.h"
@ -15,6 +16,18 @@
using namespace pinetime::platform; using namespace pinetime::platform;
using namespace pinetime::virtual_timer; 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 { enum {
PIN_NUMBER_LED_1 = 17, PIN_NUMBER_LED_1 = 17,
PIN_NUMBER_LED_2 = 18, 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); nrf52::Gpio led_4(PIN_NUMBER_LED_4);
std::array<nrf52::Gpio *, 4> leds = {&led_1, &led_2, &led_3, &led_4}; std::array<nrf52::Gpio *, 4> 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) int main(void)
{ {
cm4::InterruptGuardian::enable_interrupts(); cm4::InterruptGuardian::enable_interrupts();

View File

@ -0,0 +1,72 @@
#include <array>
#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<nrf52::Gpio *, 4> 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();
}
// delay.ms(200);
}
}
return 0;
}

View File

@ -1,22 +1,33 @@
#include "platform/nrf52/gpio.h"
extern "C" { extern "C" {
#include "nrf52.h" #include "nrf52.h"
#include "nrf52_bitfields.h" #include "nrf52_bitfields.h"
NRF_GPIO_Type *const GPIO_REGS = reinterpret_cast<NRF_GPIO_Type *>(NRF_P0_BASE); NRF_GPIO_Type *const GPIO_REGS = reinterpret_cast<NRF_GPIO_Type *>(NRF_P0_BASE);
NRF_GPIOTE_Type *const GPIOTE_REGS = reinterpret_cast<NRF_GPIOTE_Type *>(NRF_GPIOTE_BASE);
} }
#include "platform/nrf52/gpiote.h"
#include "platform/nrf52/gpio.h"
using namespace pinetime::platform::nrf52; using namespace pinetime::platform::nrf52;
Gpio::Gpio()
: pin(0)
, blocking(false)
, wait_for_event(false)
{
}
Gpio::Gpio(uint32_t pin) Gpio::Gpio(uint32_t pin)
: pin_number(pin) : pin(pin)
, blocking(false)
, wait_for_event(false)
{ {
this->set_direction(direction::OUT); this->set_direction(direction::OUT);
this->clear(); 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 direct = GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos;
uint32_t input = GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_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. // GPIO_PIN_CNF_PULL_Pullup, ///< Pin pull-up resistor enabled.
pull = GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos; pull = GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos;
drive = GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_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 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; return res;
} }
void Gpio::set() void Gpio::set()
{ {
GPIO_REGS->OUTSET = 1UL << (this->pin_number); GPIO_REGS->OUTSET = 1UL << (this->pin);
} }
void Gpio::clear() void Gpio::clear()
{ {
GPIO_REGS->OUTCLR = 1UL << (this->pin_number); GPIO_REGS->OUTCLR = 1UL << (this->pin);
} }
void Gpio::toggle() void Gpio::toggle()
{ {
uint32_t state = GPIO_REGS->OUT; uint32_t state = GPIO_REGS->OUT;
GPIO_REGS->OUTSET = (~state & (1UL << (this->pin_number))); GPIO_REGS->OUTSET = (~state & (1UL << (this->pin)));
GPIO_REGS->OUTCLR = (state & (1UL << (this->pin_number))); GPIO_REGS->OUTCLR = (state & (1UL << (this->pin)));
}
void Gpio::handle()
{
this->wait_for_event = false;
} }

View File

@ -3,22 +3,30 @@
#include "gpio_interface.h" #include "gpio_interface.h"
#include "platform/nrf52/InterruptHandler.h"
namespace pinetime::platform::nrf52 { namespace pinetime::platform::nrf52 {
class Gpio : public pinetime::interfaces::GpioInterface class Gpio : public pinetime::interfaces::GpioInterface
{ {
public: public:
inline Gpio() {} Gpio();
Gpio(uint32_t); Gpio(uint32_t);
void set_pin_number(uint32_t); void pin_number(uint32_t);
void set_direction(direction) override; uint32_t pin_number() override;
void set_direction(direction, bool blocking_read=false) override;
uint32_t get() override; uint32_t get() override;
void set() override; void set() override;
void clear() override; void clear() override;
void toggle() override; void toggle() override;
void handle() override;
private: private:
uint32_t pin_number; uint32_t pin;
bool blocking;
volatile bool wait_for_event;
}; };
} }

View File

@ -0,0 +1,77 @@
#include <cassert>
#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_Type *>(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();
}
}
}

View File

@ -0,0 +1,44 @@
#ifndef __PLATFORM_NRF52_GPIOTE_H__
#define __PLATFORM_NRF52_GPIOTE_H__
#include <array>
#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<pinetime::interfaces::GpioInterface *, GPIOTE_CHANNELS> gpiote_channels;
uint32_t num_registered;
};
}
#endif

View File

@ -54,3 +54,13 @@ void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
h->handle(); 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();
}