1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
|
//go:build rp2040
// +build rp2040
package machine
import (
"device/rp"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
type io struct {
status volatile.Register32
ctrl volatile.Register32
}
type irqCtrl struct {
intE [4]volatile.Register32
intF [4]volatile.Register32
intS [4]volatile.Register32
}
type ioBank0Type struct {
io [30]io
intR [4]volatile.Register32
proc0IRQctrl irqCtrl
proc1IRQctrl irqCtrl
dormantWakeIRQctrl irqCtrl
}
var ioBank0 = (*ioBank0Type)(unsafe.Pointer(rp.IO_BANK0))
type padsBank0Type struct {
voltageSelect volatile.Register32
io [30]volatile.Register32
}
var padsBank0 = (*padsBank0Type)(unsafe.Pointer(rp.PADS_BANK0))
// pinFunc represents a GPIO function.
//
// Each GPIO can have one function selected at a time.
// Likewise, each peripheral input (e.g. UART0 RX) should only be selected
// on one GPIO at a time. If the same peripheral input is connected to multiple GPIOs,
// the peripheral sees the logical OR of these GPIO inputs.
type pinFunc uint8
// GPIO function selectors
const (
fnJTAG pinFunc = 0
fnSPI pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO
fnUART pinFunc = 2
fnI2C pinFunc = 3
// Connect a PWM slice to GPIO. There are eight PWM slices,
// each with two outputchannels (A/B). The B pin can also be used as an input,
// for frequency and duty cyclemeasurement
fnPWM pinFunc = 4
// Software control of GPIO, from the single-cycle IO (SIO) block.
// The SIO function (F5)must be selected for the processors to drive a GPIO,
// but the input is always connected,so software can check the state of GPIOs at any time.
fnSIO pinFunc = 5
// Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces,
// and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs.
// The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected,
// so the PIOs canalways see the state of all pins.
fnPIO0, fnPIO1 pinFunc = 6, 7
// General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040,
// e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter.
// e.g. Output: optional integer divide
fnGPCK pinFunc = 8
// USB power control signals to/from the internal USB controller
fnUSB pinFunc = 9
fnNULL pinFunc = 0x1f
fnXIP pinFunc = 0
)
const (
PinOutput PinMode = iota
PinInput
PinInputPulldown
PinInputPullup
PinAnalog
PinUART
PinPWM
PinI2C
PinSPI
)
func (p Pin) PortMaskSet() (*uint32, uint32) {
return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_SET)), 1 << p
}
// set drives the pin high
func (p Pin) set() {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_SET.Set(mask)
}
func (p Pin) PortMaskClear() (*uint32, uint32) {
return (*uint32)(unsafe.Pointer(&rp.SIO.GPIO_OUT_CLR)), 1 << p
}
// clr drives the pin low
func (p Pin) clr() {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_CLR.Set(mask)
}
// xor toggles the pin
func (p Pin) xor() {
mask := uint32(1) << p
rp.SIO.GPIO_OUT_XOR.Set(mask)
}
// get returns the pin value
func (p Pin) get() bool {
return rp.SIO.GPIO_IN.HasBits(1 << p)
}
func (p Pin) ioCtrl() *volatile.Register32 {
return &ioBank0.io[p].ctrl
}
func (p Pin) padCtrl() *volatile.Register32 {
return &padsBank0.io[p]
}
func (p Pin) pullup() {
p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PUE)
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE)
}
func (p Pin) pulldown() {
p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PDE)
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
}
func (p Pin) pulloff() {
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE)
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
}
// setSlew sets pad slew rate control.
// true sets to fast. false sets to slow.
func (p Pin) setSlew(sr bool) {
p.padCtrl().ReplaceBits(boolToBit(sr)<<rp.PADS_BANK0_GPIO0_SLEWFAST_Pos, rp.PADS_BANK0_GPIO0_SLEWFAST_Msk, 0)
}
// setSchmitt enables or disables Schmitt trigger.
func (p Pin) setSchmitt(trigger bool) {
p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0)
}
// setFunc will set pin function to fn.
func (p Pin) setFunc(fn pinFunc) {
// Set input enable, Clear output disable
p.padCtrl().ReplaceBits(rp.PADS_BANK0_GPIO0_IE,
rp.PADS_BANK0_GPIO0_IE_Msk|rp.PADS_BANK0_GPIO0_OD_Msk, 0)
// Zero all fields apart from fsel; we want this IO to do what the peripheral tells it.
// This doesn't affect e.g. pullup/pulldown, as these are in pad controls.
p.ioCtrl().Set(uint32(fn) << rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Pos)
}
// init initializes the gpio pin
func (p Pin) init() {
mask := uint32(1) << p
rp.SIO.GPIO_OE_CLR.Set(mask)
p.clr()
}
// Configure configures the gpio pin as per mode.
func (p Pin) Configure(config PinConfig) {
p.init()
mask := uint32(1) << p
switch config.Mode {
case PinOutput:
p.setFunc(fnSIO)
rp.SIO.GPIO_OE_SET.Set(mask)
case PinInput:
p.setFunc(fnSIO)
p.pulloff()
case PinInputPulldown:
p.setFunc(fnSIO)
p.pulldown()
case PinInputPullup:
p.setFunc(fnSIO)
p.pullup()
case PinAnalog:
p.setFunc(fnNULL)
p.pulloff()
case PinUART:
p.setFunc(fnUART)
case PinPWM:
p.setFunc(fnPWM)
case PinI2C:
// IO config according to 4.3.1.3 of rp2040 datasheet.
p.setFunc(fnI2C)
p.pullup()
p.setSchmitt(true)
p.setSlew(false)
case PinSPI:
p.setFunc(fnSPI)
}
}
// Set drives the pin high if value is true else drives it low.
func (p Pin) Set(value bool) {
if value {
p.set()
} else {
p.clr()
}
}
// Get reads the pin value.
func (p Pin) Get() bool {
return p.get()
}
// PinChange represents one or more trigger events that can happen on a given GPIO pin
// on the RP2040. ORed PinChanges are valid input to most IRQ functions.
type PinChange uint8
// Pin change interrupt constants for SetInterrupt.
const (
// PinLevelLow triggers whenever pin is at a low (around 0V) logic level.
PinLevelLow PinChange = 1 << iota
// PinLevelLow triggers whenever pin is at a high (around 3V) logic level.
PinLevelHigh
// Edge falling
PinFalling
// Edge rising
PinRising
)
// Callbacks to be called for pins configured with SetInterrupt.
var (
pinCallbacks [2][_NUMBANK0_GPIOS]func(Pin)
setInt [2][_NUMBANK0_GPIOS]bool
)
// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state. The pin should already be configured as an input, including a pull up
// or down if no external pull is provided.
//
// This call will replace a previously set callback on this pin. You can pass a
// nil func to unset the pin change interrupt. If you do so, the change
// parameter is ignored and can be set to any value (such as 0).
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
if p > 31 || p < 0 {
return ErrInvalidInputPin
}
core := CurrentCore()
if callback == nil {
// disable current interrupt
p.setInterrupt(change, false)
pinCallbacks[core][p] = nil
return nil
}
if pinCallbacks[core][p] != nil {
// Callback already configured. Should disable callback by passing a nil callback first.
return ErrNoPinChangeChannel
}
p.setInterrupt(change, true)
pinCallbacks[core][p] = callback
if setInt[core][p] {
// interrupt has already been set. Exit.
return nil
}
interrupt.New(rp.IRQ_IO_IRQ_BANK0, gpioHandleInterrupt).Enable()
irqSet(rp.IRQ_IO_IRQ_BANK0, true)
return nil
}
// gpioHandleInterrupt finds the corresponding pin for the interrupt.
// C SDK equivalent of gpio_irq_handler
func gpioHandleInterrupt(intr interrupt.Interrupt) {
core := CurrentCore()
var gpio Pin
for gpio = 0; gpio < _NUMBANK0_GPIOS; gpio++ {
var base *irqCtrl
switch core {
case 0:
base = &ioBank0.proc0IRQctrl
case 1:
base = &ioBank0.proc1IRQctrl
}
statreg := base.intS[gpio>>3]
change := getIntChange(gpio, statreg.Get())
if change != 0 {
gpio.acknowledgeInterrupt(change)
callback := pinCallbacks[core][gpio]
if callback != nil {
callback(gpio)
}
}
}
}
// events returns the bit representation of the pin change for the rp2040.
func (change PinChange) events() uint32 {
return uint32(change)
}
// intBit is the bit storage form of a PinChange for a given Pin
// in the IO_BANK0 interrupt registers (page 269 RP2040 Datasheet).
func (p Pin) ioIntBit(change PinChange) uint32 {
return change.events() << (4 * (p % 8))
}
// Acquire interrupt data from a INT status register.
func getIntChange(p Pin, status uint32) PinChange {
return PinChange(status>>(4*(p%8))) & 0xf
}
|