YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
gpio_rp2350.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 RP2350. Supports open-source and
15// open-drain modes as well as interrupts.
16//
17#include "gpio_rp2350.h"
18#include "RP2350.h"
19#include <cassert>
20
21// Macro to increment the address of a register if the
22// GPIO number is larger than 31. The register for the
23// 'higher' GPIOs (32 to 47) is usually at the following
24// address location.
25#define OFF32(x) (*(&x + _high_gpio))
26
27using namespace _SIO_;
28using namespace _IO_BANK0_;
29using namespace _PADS_BANK0_;
30
31function<void()> gpio_rp2350::_intHandler[48] {nullptr};
32uint8_t gpio_rp2350::_irqConfig [48] {0};
33
34gpio_rp2350::gpio_rp2350(gpio_pin_t gpio) : _gpio(gpio) {
35 // Set GPIO mask and 'high' boolean
36 // The value gpio is not checked, because
37 // it might be 'not connected' (0xffff)
38 _mask = 1 << (gpio % 32);
39 _high_gpio = (gpio > 31);
40}
41
42void gpio_rp2350::setGpio(gpio_pin_t gpio) {
43 assert(gpio < 48);
44 _gpio = gpio;
45 _mask = 1 << (gpio % 32);
46 _high_gpio = (gpio > 31);
47}
48
49gpio_pin_t gpio_rp2350::getGpio() const {
50 return _gpio;
51}
52
53void gpio_rp2350::gpioMode (uint16_t mode) {
54 assert(_gpio < 48);
55 setSEL (GPIO_CTRL_FUNCSEL__sio);
56 setMode(mode);
57}
58
59bool gpio_rp2350::gpioRead () const {
60 assert(_gpio < 48);
61// GPIO_t *pad_ctrl = &PADS_BANK0.GPIO[_gpio]; // Bug E9
62// pad_ctrl->IE = 1; //1; Bug E9
63 bool res = OFF32( SIO.GPIO_IN ) & _mask;
64// pad_ctrl->IE = 0; //1; Bug E9
65 return res;
66}
67
68void gpio_rp2350::gpioWrite (bool value) {
69 assert(_gpio < 48);
70 if (_open_drain) {
71 if (value) {
72 OFF32( SIO.GPIO_OE_CLR ) = _mask;
73 } else {
74 OFF32( SIO.GPIO_OE_SET ) = _mask;
75 }
76 }
77 else if (_open_source) {
78 if (value) {
79 OFF32( SIO.GPIO_OE_SET ) = _mask;
80 } else {
81 OFF32( SIO.GPIO_OE_CLR ) = _mask;
82 }
83 }
84 else {
85 // No open drain/source
86 if (value) {
87 OFF32( SIO.GPIO_OUT_SET ) = _mask;
88 } else {
89 OFF32( SIO.GPIO_OUT_CLR ) = _mask;
90 }
91 }
92}
93
94void gpio_rp2350::gpioToggle() {
95 assert(_gpio < 48);
96 if (_open_drain || _open_source) {
97 OFF32( SIO.GPIO_OE_XOR ) = _mask;
98 } else {
99 OFF32( SIO.GPIO_OUT_XOR ) = _mask;
100 }
101}
102
103void gpio_rp2350::gpioAttachIrq (gpio_mode_t mode,
104 function<void()> handler) {
105 _irqConfig[_gpio] = 0;
106 // configure irqs according to mode
107 if (mode & GPIO::LEVEL_LOW) {
108 _irqConfig[_gpio] |= 0x1;
109 }
110 if (mode & GPIO::LEVEL_HIGH) {
111 _irqConfig[_gpio] |= 0x2;
112 }
113 if (mode & GPIO::FALLING) {
114 _irqConfig[_gpio] |= 0x4;
115 }
116 if (mode & GPIO::RISING) {
117 _irqConfig[_gpio] |= 0x8;
118 }
119 // store handler addr
120 _intHandler[_gpio] = handler;
121 // Finally enable interrupts
122 gpioEnableIrq();
123}
124
125void gpio_rp2350::gpioDetachIrq () {
126 // Disable irqs
127 gpioDisableIrq();
128 // unregister handler
129 _intHandler[_gpio] = nullptr;
130}
131
132void gpio_rp2350::gpioEnableIrq() {
133 PROC0_INTE0_t *INTE_SET = &IO_BANK0_SET.PROC0_INTE0 + (_gpio >> 3) + (SIO.CPUID*18);
134 PROC0_INTE0_t *INTE_CLR = &IO_BANK0_CLR.PROC0_INTE0 + (_gpio >> 3) + (SIO.CPUID*18);
135 INTR0_t *INTR = &IO_BANK0.INTR0 + (_gpio >> 3);
136 int mask_shift = (_gpio & 0x7) * 4;
137 // Switch off NVIC irq, so we get no
138 // handler calls during configuration
139 NVIC_DisableIRQ(IO_IRQ_BANK0_IRQn);
140 // Enable irqs (reset values and set related bits)
141 *INTE_CLR = (0xf << mask_shift);
142 *INTE_SET = _irqConfig[_gpio] << mask_shift;
143 // Clear pending interrupts
144 *INTR = (0xf << mask_shift);
145 // Enable NVIC irq
146 NVIC_ClearPendingIRQ(IO_IRQ_BANK0_IRQn);
147 NVIC_EnableIRQ (IO_IRQ_BANK0_IRQn);
148}
149
150void gpio_rp2350::gpioDisableIrq() {
151 PROC0_INTE0_t *INTE_CLR = &IO_BANK0_CLR.PROC0_INTE0 + (_gpio >> 3) + (SIO.CPUID*18);
152 INTR0_t *INTR = &IO_BANK0.INTR0 + (_gpio >> 3);
153 int mask_shift = (_gpio & 0x7) * 4;
154 // Clear pending interrupts
155 *INTR = (0xf << mask_shift);
156 // Disable irqs
157 *INTE_CLR = (0xf << mask_shift);
158}
159
160void gpio_rp2350::setSEL (uint8_t sel) const {
161 assert(_gpio < 48);
162 GPIO_CTRL_t *IO_CTRL = &IO_BANK0.GPIO0_CTRL + (_gpio << 1);
163 // Set the pad function and reset all other bits
164 IO_CTRL->FUNCSEL = sel;
165 IO_CTRL->OUTOVER = 0;
166 IO_CTRL->OEOVER = 0;
167 IO_CTRL->INOVER = 0;
168 IO_CTRL->IRQOVER = 0;
169 // Obviously we want to use this pin.
170 // So deactivate the PAD isolation and
171 // enable gpio input
172 GPIO_t *PAD_CTRL = &PADS_BANK0.GPIO[_gpio];
173 PAD_CTRL->ISO = 0;
174 PAD_CTRL->IE = 1;
175}
176
177void gpio_rp2350::setMode (uint16_t mode) {
178 // Reset open drain/source
179 _open_drain = false;
180 _open_source = false;
181 // Reset SIO configuration
182 OFF32( SIO.GPIO_OUT_CLR ) = _mask;
183 OFF32( SIO.GPIO_OE_CLR ) = _mask;
184 // Reset pad configuration
185 GPIO_t *PAD_CTRL = &PADS_BANK0.GPIO[_gpio];
186 PAD_CTRL->ISO = 0;
187 PAD_CTRL->OD = 0;
188 PAD_CTRL->IE = 0;
189 PAD_CTRL->DRIVE = GPIO_DRIVE__2mA;
190 PAD_CTRL->PUE = 0;
191 PAD_CTRL->PDE = 0;
192 PAD_CTRL->SCHMITT = 1;
193 PAD_CTRL->SLEWFAST = 0;
194
195 if (mode & GPIO::INPUT) {
196 // Enable input
197 PAD_CTRL->IE = 1;
198 }
199 if (mode & GPIO::INPUT_INVERT) {
200 // Enable input
201 PAD_CTRL->IE = 1;
202 GPIO_CTRL_t *io_ctrl = &IO_BANK0.GPIO0_CTRL + (_gpio << 1);
203 io_ctrl->INOVER = GPIO_CTRL_INOVER__INVERT;
204 }
205 if (mode & GPIO::OUTPUT) {
206 OFF32( SIO.GPIO_OE_SET ) = _mask;
207 }
208 if (mode & GPIO::OUTPUT_OPEN_DRAIN) {
209 _open_drain |= _mask;
210 OFF32( SIO.GPIO_OE_SET ) = _mask;
211 }
212 if (mode & GPIO::OUTPUT_OPEN_SOURCE) {
213 _open_source |= _mask;
214 OFF32( SIO.GPIO_OUT_SET ) = _mask;
215 }
216 if (mode & GPIO::PULLUP) {
217 PAD_CTRL->PUE = 1;
218 }
219 if (mode & GPIO::PULLDOWN) {
220 PAD_CTRL->PDE = 1;
221 }
222 if (mode & GPIO::FAST) {
223 PAD_CTRL->SLEWFAST = 1;
224 }
225 if ((mode & GPIO::DRIVE_4mA) || (mode & GPIO::DRIVE_8mA)) {
226 PAD_CTRL->DRIVE = (mode >> 10) & 0x3;
227 }
228 if (mode & GPIO::INIT_HIGH) {
229 gpioWrite(HIGH);
230 }
231 if (mode & GPIO::INIT_LOW) {
232 gpioWrite(LOW);
233 }
234}
235
236// Interrupt handler
238extern "C" {
239
240void IO_IRQ_BANK0_Handler(void) {
241 PROC0_INTS0_t *INTS = &IO_BANK0.PROC0_INTS0 + (SIO.CPUID*18);
242 INTR0_t *INTR = &IO_BANK0.INTR0;
243 for (int i = 0; i < 6; ++i, INTS++, INTR++) {
244 while (uint8_t pos = __builtin_ffs(*INTS)) {
245 // Clear irq
246 *INTR = (1 << --pos);
247 // Call irq handler
248 uint16_t gpio = (pos >> 2) + (i << 3);
249 if (gpio_rp2350::_intHandler[gpio]) {
250 gpio_rp2350::_intHandler[gpio]();
251 }
252 }
253 }
254}
255
256} // extern "C"