machine/rp2040: add interrupt API
Этот коммит содержится в:
родитель
6c02b4956c
коммит
b534dd67e0
3 изменённых файлов: 189 добавлений и 0 удалений
|
@ -1,3 +1,4 @@
|
|||
//go:build rp2040
|
||||
// +build rp2040
|
||||
|
||||
package machine
|
||||
|
@ -114,3 +115,11 @@ func init() {
|
|||
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.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
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/rp"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
@ -208,3 +210,108 @@ func (p Pin) Set(value bool) {
|
|||
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]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)
|
||||
}
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче