machine/rp2040: add interrupt API
Этот коммит содержится в:
родитель
6c02b4956c
коммит
b534dd67e0
3 изменённых файлов: 189 добавлений и 0 удалений
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build rp2040
|
||||||
// +build rp2040
|
// +build rp2040
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
@ -114,3 +115,11 @@ func init() {
|
||||||
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
||||||
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
|
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentCore returns the core number the call was made from.
|
||||||
|
func CurrentCore() int {
|
||||||
|
return int(rp.SIO.CPUID.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumCores returns number of cores available on the device.
|
||||||
|
func NumCores() int { return 2 }
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
//go:build rp2040
|
||||||
// +build rp2040
|
// +build rp2040
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/rp"
|
"device/rp"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -208,3 +210,108 @@ func (p Pin) Set(value bool) {
|
||||||
func (p Pin) Get() bool {
|
func (p Pin) Get() bool {
|
||||||
return p.get()
|
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]func(Pin)
|
||||||
|
setInt [2]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] = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pinCallbacks[core] != nil {
|
||||||
|
// Callback already configured. Should disable callback by passing a nil callback first.
|
||||||
|
return ErrNoPinChangeChannel
|
||||||
|
}
|
||||||
|
p.setInterrupt(change, true)
|
||||||
|
pinCallbacks[core] = callback
|
||||||
|
|
||||||
|
if setInt[core] {
|
||||||
|
// interrupt has already been set. Exit.
|
||||||
|
println("core set")
|
||||||
|
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) {
|
||||||
|
// panic("END") // if program is not ended here rp2040 will call interrupt again when finished, a vicious spin cycle.
|
||||||
|
core := CurrentCore()
|
||||||
|
callback := pinCallbacks[core]
|
||||||
|
if callback != nil {
|
||||||
|
// TODO fix gpio acquisition (see below)
|
||||||
|
// For now all callbacks get pin 255 (nonexistent).
|
||||||
|
callback(0xff)
|
||||||
|
}
|
||||||
|
var gpio Pin
|
||||||
|
for gpio = 0; gpio < _NUMBANK0_GPIOS; gpio++ {
|
||||||
|
// Acknowledge all GPIO interrupts for now
|
||||||
|
// since we are yet unable to acquire interrupt status
|
||||||
|
gpio.acknowledgeInterrupt(0xff) // TODO fix status get. For now we acknowledge all pending interrupts.
|
||||||
|
// Commented code below from C SDK not working.
|
||||||
|
// statreg := base.intS[gpio>>3]
|
||||||
|
// change := getIntChange(gpio, statreg.Get())
|
||||||
|
// if change != 0 {
|
||||||
|
// gpio.acknowledgeInterrupt(change)
|
||||||
|
// if callback != nil {
|
||||||
|
// callback(gpio)
|
||||||
|
// return
|
||||||
|
// } else {
|
||||||
|
// panic("unset callback in handler")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
73
src/machine/machine_rp2040_sync.go
Обычный файл
73
src/machine/machine_rp2040_sync.go
Обычный файл
|
@ -0,0 +1,73 @@
|
||||||
|
//go:build rp2040
|
||||||
|
// +build rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/rp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// machine_rp2040_sync.go contains interrupt and
|
||||||
|
// lock primitives similar to those found in Pico SDK's
|
||||||
|
// irq.c
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Number of spin locks available
|
||||||
|
_NUMSPINLOCKS = 32
|
||||||
|
// Number of interrupt handlers available
|
||||||
|
_NUMIRQ = 32
|
||||||
|
_PICO_SPINLOCK_ID_IRQ = 9
|
||||||
|
_NUMBANK0_GPIOS = 30
|
||||||
|
)
|
||||||
|
|
||||||
|
// Clears interrupt flag on a pin
|
||||||
|
func (p Pin) acknowledgeInterrupt(change PinChange) {
|
||||||
|
ioBank0.intR[p>>3].Set(p.ioIntBit(change))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic interrupt setting via ioBANK0 for GPIO interrupts.
|
||||||
|
func (p Pin) setInterrupt(change PinChange, enabled bool) {
|
||||||
|
// Separate mask/force/status per-core, so check which core called, and
|
||||||
|
// set the relevant IRQ controls.
|
||||||
|
switch CurrentCore() {
|
||||||
|
case 0:
|
||||||
|
p.ctrlSetInterrupt(change, enabled, &ioBank0.proc0IRQctrl)
|
||||||
|
case 1:
|
||||||
|
p.ctrlSetInterrupt(change, enabled, &ioBank0.proc1IRQctrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctrlSetInterrupt acknowledges any pending interrupt and enables or disables
|
||||||
|
// the interrupt for a given IRQ control bank (IOBANK, DormantIRQ, QSPI).
|
||||||
|
//
|
||||||
|
// pico-sdk calls this the _gpio_set_irq_enabled, not to be confused with
|
||||||
|
// gpio_set_irq_enabled (no leading underscore).
|
||||||
|
func (p Pin) ctrlSetInterrupt(change PinChange, enabled bool, base *irqCtrl) {
|
||||||
|
p.acknowledgeInterrupt(change)
|
||||||
|
enReg := &base.intE[p>>3]
|
||||||
|
if enabled {
|
||||||
|
enReg.SetBits(p.ioIntBit(change))
|
||||||
|
} else {
|
||||||
|
enReg.ClearBits(p.ioIntBit(change))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable or disable a specific interrupt on the executing core.
|
||||||
|
// num is the interrupt number which must be in [0,31].
|
||||||
|
func irqSet(num uint32, enabled bool) {
|
||||||
|
if num >= _NUMIRQ {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
irqSetMask(1<<num, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func irqSetMask(mask uint32, enabled bool) {
|
||||||
|
if enabled {
|
||||||
|
// Clear pending before enable
|
||||||
|
// (if IRQ is actually asserted, it will immediately re-pend)
|
||||||
|
rp.PPB.NVIC_ICPR.Set(mask)
|
||||||
|
rp.PPB.NVIC_ISER.Set(mask)
|
||||||
|
} else {
|
||||||
|
rp.PPB.NVIC_ICER.Set(mask)
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче