|
| 1 | +//go:build rp2040 |
1 | 2 | // +build rp2040
|
2 | 3 |
|
3 | 4 | package machine
|
4 | 5 |
|
5 | 6 | import (
|
6 | 7 | "device/rp"
|
| 8 | + "runtime/interrupt" |
7 | 9 | "runtime/volatile"
|
8 | 10 | "unsafe"
|
9 | 11 | )
|
@@ -208,3 +210,108 @@ func (p Pin) Set(value bool) {
|
208 | 210 | func (p Pin) Get() bool {
|
209 | 211 | return p.get()
|
210 | 212 | }
|
| 213 | + |
| 214 | +// PinChange represents one or more trigger events that can happen on a given GPIO pin |
| 215 | +// on the RP2040. ORed PinChanges are valid input to most IRQ functions. |
| 216 | +type PinChange uint8 |
| 217 | + |
| 218 | +// Pin change interrupt constants for SetInterrupt. |
| 219 | +const ( |
| 220 | + // PinLevelLow triggers whenever pin is at a low (around 0V) logic level. |
| 221 | + PinLevelLow PinChange = 1 << iota |
| 222 | + // PinLevelLow triggers whenever pin is at a high (around 3V) logic level. |
| 223 | + PinLevelHigh |
| 224 | + // Edge falling |
| 225 | + PinFalling |
| 226 | + // Edge rising |
| 227 | + PinRising |
| 228 | +) |
| 229 | + |
| 230 | +// Callbacks to be called for pins configured with SetInterrupt. |
| 231 | +var ( |
| 232 | + pinCallbacks [2]func(Pin) |
| 233 | + setInt [2]bool |
| 234 | +) |
| 235 | + |
| 236 | +// SetInterrupt sets an interrupt to be executed when a particular pin changes |
| 237 | +// state. The pin should already be configured as an input, including a pull up |
| 238 | +// or down if no external pull is provided. |
| 239 | +// |
| 240 | +// This call will replace a previously set callback on this pin. You can pass a |
| 241 | +// nil func to unset the pin change interrupt. If you do so, the change |
| 242 | +// parameter is ignored and can be set to any value (such as 0). |
| 243 | +func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error { |
| 244 | + if p > 31 || p < 0 { |
| 245 | + return ErrInvalidInputPin |
| 246 | + } |
| 247 | + core := CurrentCore() |
| 248 | + if callback == nil { |
| 249 | + // disable current interrupt |
| 250 | + p.setInterrupt(change, false) |
| 251 | + pinCallbacks[core] = nil |
| 252 | + return nil |
| 253 | + } |
| 254 | + |
| 255 | + if pinCallbacks[core] != nil { |
| 256 | + // Callback already configured. Should disable callback by passing a nil callback first. |
| 257 | + return ErrNoPinChangeChannel |
| 258 | + } |
| 259 | + p.setInterrupt(change, true) |
| 260 | + pinCallbacks[core] = callback |
| 261 | + |
| 262 | + if setInt[core] { |
| 263 | + // interrupt has already been set. Exit. |
| 264 | + println("core set") |
| 265 | + return nil |
| 266 | + } |
| 267 | + interrupt.New(rp.IRQ_IO_IRQ_BANK0, gpioHandleInterrupt).Enable() |
| 268 | + irqSet(rp.IRQ_IO_IRQ_BANK0, true) |
| 269 | + return nil |
| 270 | +} |
| 271 | + |
| 272 | +// gpioHandleInterrupt finds the corresponding pin for the interrupt. |
| 273 | +// C SDK equivalent of gpio_irq_handler |
| 274 | +func gpioHandleInterrupt(intr interrupt.Interrupt) { |
| 275 | + // panic("END") // if program is not ended here rp2040 will call interrupt again when finished, a vicious spin cycle. |
| 276 | + core := CurrentCore() |
| 277 | + callback := pinCallbacks[core] |
| 278 | + if callback != nil { |
| 279 | + // TODO fix gpio acquisition (see below) |
| 280 | + // For now all callbacks get pin 255 (nonexistent). |
| 281 | + callback(0xff) |
| 282 | + } |
| 283 | + var gpio Pin |
| 284 | + for gpio = 0; gpio < _NUMBANK0_GPIOS; gpio++ { |
| 285 | + // Acknowledge all GPIO interrupts for now |
| 286 | + // since we are yet unable to acquire interrupt status |
| 287 | + gpio.acknowledgeInterrupt(0xff) // TODO fix status get. For now we acknowledge all pending interrupts. |
| 288 | + // Commented code below from C SDK not working. |
| 289 | + // statreg := base.intS[gpio>>3] |
| 290 | + // change := getIntChange(gpio, statreg.Get()) |
| 291 | + // if change != 0 { |
| 292 | + // gpio.acknowledgeInterrupt(change) |
| 293 | + // if callback != nil { |
| 294 | + // callback(gpio) |
| 295 | + // return |
| 296 | + // } else { |
| 297 | + // panic("unset callback in handler") |
| 298 | + // } |
| 299 | + // } |
| 300 | + } |
| 301 | +} |
| 302 | + |
| 303 | +// events returns the bit representation of the pin change for the rp2040. |
| 304 | +func (change PinChange) events() uint32 { |
| 305 | + return uint32(change) |
| 306 | +} |
| 307 | + |
| 308 | +// intBit is the bit storage form of a PinChange for a given Pin |
| 309 | +// in the IO_BANK0 interrupt registers (page 269 RP2040 Datasheet). |
| 310 | +func (p Pin) ioIntBit(change PinChange) uint32 { |
| 311 | + return change.events() << (4 * (p % 8)) |
| 312 | +} |
| 313 | + |
| 314 | +// Acquire interrupt data from a INT status register. |
| 315 | +func getIntChange(p Pin, status uint32) PinChange { |
| 316 | + return PinChange(status>>(4*(p%8))) & 0xf |
| 317 | +} |
0 commit comments