YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
gpio_rp2040.cpp
1// ---------------------------------------------
2// This file is part of
3// _ _ __ _ _ __ __
4// ( \/ ) /__\ ( )_( ) /__\ ( )
5// \ / /(__)\ ) _ ( /(__)\ )(__
6// (__)(__)(__)(_) (_)(__)(__)(____)
7//
8// Yet Another HW Abstraction Library
9// Copyright (C) Andreas Terstegge
10// BSD Licensed (see file LICENSE)
11//
12// ---------------------------------------------
13//
14// GPIO driver for RP2040. Supports open-source and
15// open-drain modes as well as interrupts.
16//
17#include "gpio_rp2040.h"
18#include "RP2040.h"
19#include <cassert>
20
21using namespace _SIO_;
22using namespace _IO_BANK0_;
23using namespace _PADS_BANK0_;
24
25function<void()> gpio_rp2040::_intHandler[30] {nullptr};
26uint8_t gpio_rp2040::_irqConfig [30] {0};
27
28gpio_rp2040::gpio_rp2040(gpio_pin_t gpio)
29 : _gpio(gpio) {
30 _mask = 1 << gpio;
31}
32
33void gpio_rp2040::setGpio(gpio_pin_t gpio) {
34 assert(gpio < 30);
35 _gpio = gpio;
36 _mask = 1 << (gpio % 32);
37}
38
39gpio_pin_t gpio_rp2040::getGpio() const {
40 return _gpio;
41}
42
43void
44gpio_rp2040::gpioMode (uint16_t mode) {
45 assert(_gpio < 30);
46 setSEL (GPIO_CTRL_FUNCSEL__sio);
47 setMode (mode);
48}
49
50bool
51gpio_rp2040::gpioRead () const {
52 assert(_gpio < 30);
53 return SIO.GPIO_IN & _mask;
54}
55
56void
57gpio_rp2040::gpioWrite (bool value) {
58 assert(_gpio < 30);
59 if (_open_drain) {
60 if (value) SIO.GPIO_OE_CLR = _mask;
61 else SIO.GPIO_OE_SET = _mask;
62 }
63 else if (_open_source) {
64 if (value) SIO.GPIO_OE_SET = _mask;
65 else SIO.GPIO_OE_CLR = _mask;
66 }
67 else {
68 // No open drain/source
69 if (value) SIO.GPIO_OUT_SET = _mask;
70 else SIO.GPIO_OUT_CLR = _mask;
71 }
72}
73
74void
75gpio_rp2040::gpioToggle () {
76 assert(_gpio < 30);
77 if (_open_drain || _open_source)
78 SIO.GPIO_OE_XOR = _mask;
79 else SIO.GPIO_OUT_XOR = _mask;
80}
81
82void
83gpio_rp2040::gpioAttachIrq (gpio_mode_t mode,
84 function<void()> handler) {
85 assert(_gpio < 30);
86 _irqConfig[_gpio] = 0;
87 // configure irqs according to mode
88 if (mode & GPIO::LEVEL_LOW) {
89 _irqConfig[_gpio] |= 0x1;
90 }
91 if (mode & GPIO::LEVEL_HIGH) {
92 _irqConfig[_gpio] |= 0x2;
93 }
94 if (mode & GPIO::FALLING) {
95 _irqConfig[_gpio] |= 0x4;
96 }
97 if (mode & GPIO::RISING) {
98 _irqConfig[_gpio] |= 0x8;
99 }
100 // store handler addr
101 _intHandler[_gpio] = handler;
102 // Finally enable irqs
103 gpioEnableIrq();
104}
105
106void
107gpio_rp2040::gpioDetachIrq () {
108 assert(_gpio < 30);
109 // Disable irqs
110 gpioDisableIrq();
111 // unregister handler
112 _intHandler[_gpio] = 0;
113}
114
115void
116gpio_rp2040::gpioEnableIrq () {
117 assert(_gpio < 30);
118 PROC0_INTE0_t *INTE_SET = &IO_BANK0_SET.PROC0_INTE0 + (_gpio >> 3) + (SIO.CPUID*12);
119 INTR0_t *INTR = &IO_BANK0.INTR0 + (_gpio >> 3) + (SIO.CPUID*12);
120 int mask_shift = (_gpio & 0x7) * 4;
121 // Switch off NVIC irq so we get no
122 // handler calls during configuration
123 NVIC_DisableIRQ(IO_IRQ_BANK0_IRQn);
124 // Enable irqs
125 *INTE_SET = _irqConfig[_gpio] << mask_shift;
126 // Clear pending interrupts
127 *INTR = (0xf << mask_shift);
128 // Enable NVIC irq
129 NVIC_ClearPendingIRQ(IO_IRQ_BANK0_IRQn);
130 NVIC_EnableIRQ (IO_IRQ_BANK0_IRQn);
131}
132
133void
134gpio_rp2040::gpioDisableIrq () {
135 assert(_gpio < 30);
136 PROC0_INTE0_t *INTE_CLR = &IO_BANK0_CLR.PROC0_INTE0 + (_gpio >> 3) + (SIO.CPUID*12);
137 INTR0_t *INTR = &IO_BANK0.INTR0 + (_gpio >> 3);
138 int mask_shift = (_gpio & 0x7) * 4;
139 // Clear pending interrupts
140 *INTR = (0xf << mask_shift);
141 // Disable irqs
142 *INTE_CLR = (0xf << mask_shift);
143}
144
145void gpio_rp2040::setSEL (uint8_t sel) {
146 assert(_gpio < 30);
147 GPIO_CTRL_t *io_ctrl = &IO_BANK0.GPIO0_CTRL + (_gpio << 1);
148 // Set the pad function and reset all other bits
149 io_ctrl->FUNCSEL = sel;
150 io_ctrl->OUTOVER = 0;
151 io_ctrl->OEOVER = 0;
152 io_ctrl->INOVER = 0;
153 io_ctrl->IRQOVER = 0;
154}
155
156void gpio_rp2040::setMode (uint16_t mode) {
157 assert(_gpio < 30);
158 GPIO_t *pad_ctrl = &PADS_BANK0.GPIO[_gpio];
159
160 // Reset open drain/source
161 _open_drain = false;
162 _open_source = false;
163 // Reset SIO configuration
164 SIO.GPIO_OUT_CLR = _mask;
165 SIO.GPIO_OE_CLR = _mask;
166 // Reset pad configuration
167 pad_ctrl->OD = 0;
168 pad_ctrl->IE = 1;
169 pad_ctrl->DRIVE = GPIO_DRIVE__2mA;
170 pad_ctrl->PUE = 0;
171 pad_ctrl->PDE = 0;
172 pad_ctrl->SCHMITT = 1;
173 pad_ctrl->SLEWFAST = 0;
174
175 if (mode & GPIO::INPUT) {
176 // Default is okay
177 }
178 if (mode & GPIO::INPUT_INVERT) {
179 // Default is NEARLY okay :)
180 GPIO_CTRL_t *io_ctrl = &IO_BANK0.GPIO0_CTRL + (_gpio << 1);
181 io_ctrl->INOVER = GPIO_CTRL_INOVER__INVERT;
182 }
183 if (mode & GPIO::OUTPUT) {
184 SIO.GPIO_OE_SET = _mask;
185 }
186 if (mode & GPIO::OUTPUT_OPEN_DRAIN) {
187 _open_drain = true;
188 SIO.GPIO_OE_SET = _mask;
189 }
190 if (mode & GPIO::OUTPUT_OPEN_SOURCE) {
191 _open_source = true;
192 SIO.GPIO_OUT_SET = _mask;
193 }
194 if (mode & GPIO::PULLUP) {
195 pad_ctrl->PUE = 1;
196 }
197 if (mode & GPIO::PULLDOWN) {
198 pad_ctrl->PDE = 1;
199 }
200 if (mode & GPIO::FAST) {
201 pad_ctrl->SLEWFAST = 1;
202 }
203 if ((mode & GPIO::DRIVE_4mA) || (mode & GPIO::DRIVE_8mA)) {
204 pad_ctrl->DRIVE = (mode >> 10) & 0x3;
205 }
206 if (mode & GPIO::INIT_HIGH) {
207 gpioWrite (HIGH);
208 }
209 if (mode & GPIO::INIT_LOW) {
210 gpioWrite (LOW);
211 }
212}
213
214// Interrupt handler
216extern "C" {
217
218void IO_IRQ_BANK0_Handler(void) {
219 PROC0_INTS0_t *INTS = &IO_BANK0.PROC0_INTS0 + (SIO.CPUID*12);
220 INTR0_t *INTR = &IO_BANK0.INTR0;
221 for (int i=0; i < 4; ++i, INTS++, INTR++) {
222 while (uint8_t pos = __builtin_ffs(*INTS)) {
223 // Clear irq
224 *INTR = (1 << --pos);
225 // Call irq handler
226 uint16_t gpio = (pos >> 2) + (i << 3);
227 gpio_rp2040::_intHandler[gpio]();
228 }
229 }
230}
231
232} // extern "C"
CMSIS-Core(M) Device Peripheral Access Layer Header File for Device RP2040.