add support for GPIO interrupts on esp32c3
Update interrupt_esp32c3.go: make callHandler inline save and restore MSTATUS along with MEPC save and restore actual threshold value and call fence print additional data during exception
Этот коммит содержится в:
родитель
0243a5b8be
коммит
b176494e44
2 изменённых файлов: 225 добавлений и 14 удалений
|
@ -5,11 +5,15 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/esp"
|
"device/esp"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const deviceName = esp.Device
|
const deviceName = esp.Device
|
||||||
|
const maxPin = 22
|
||||||
|
const cpuInterruptFromPin = 6
|
||||||
|
|
||||||
// CPUFrequency returns the current CPU frequency of the chip.
|
// CPUFrequency returns the current CPU frequency of the chip.
|
||||||
// Currently it is a fixed frequency but it may allow changing in the future.
|
// Currently it is a fixed frequency but it may allow changing in the future.
|
||||||
|
@ -24,6 +28,18 @@ const (
|
||||||
PinInputPulldown
|
PinInputPulldown
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PinChange uint8
|
||||||
|
|
||||||
|
// Pin change interrupt constants for SetInterrupt.
|
||||||
|
const (
|
||||||
|
PinNoInterrupt PinChange = iota
|
||||||
|
PinRising
|
||||||
|
PinFalling
|
||||||
|
PinToggle
|
||||||
|
PinLowLevel
|
||||||
|
PinHighLevel
|
||||||
|
)
|
||||||
|
|
||||||
// Configure this pin with the given configuration.
|
// Configure this pin with the given configuration.
|
||||||
func (p Pin) Configure(config PinConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
if p == NoPin {
|
if p == NoPin {
|
||||||
|
@ -84,6 +100,11 @@ func (p Pin) mux() *volatile.Register32 {
|
||||||
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.IO_MUX.GPIO0)) + uintptr(p)*4)))
|
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.IO_MUX.GPIO0)) + uintptr(p)*4)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pin returns the PIN register corresponding to the given GPIO pin.
|
||||||
|
func (p Pin) pin() *volatile.Register32 {
|
||||||
|
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.PIN0)) + uintptr(p)*4)))
|
||||||
|
}
|
||||||
|
|
||||||
// Set the pin to high or low.
|
// Set the pin to high or low.
|
||||||
// Warning: only use this on an output pin!
|
// Warning: only use this on an output pin!
|
||||||
func (p Pin) Set(value bool) {
|
func (p Pin) Set(value bool) {
|
||||||
|
@ -129,6 +150,70 @@ func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
|
||||||
return &esp.GPIO.OUT_W1TC, 1 << p
|
return &esp.GPIO.OUT_W1TC, 1 << p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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).
|
||||||
|
// If the pin is already configured with a callback, you must first unset
|
||||||
|
// this pins interrupt before you can set a new callback.
|
||||||
|
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
|
||||||
|
if p >= maxPin {
|
||||||
|
return ErrInvalidInputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
if callback == nil || change == PinNoInterrupt {
|
||||||
|
// Disable this pin interrupt
|
||||||
|
p.pin().ClearBits(esp.GPIO_PIN_PIN_INT_TYPE_Msk | esp.GPIO_PIN_PIN_INT_ENA_Msk)
|
||||||
|
|
||||||
|
if pinCallbacks[p] != nil {
|
||||||
|
pinCallbacks[p] = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pinCallbacks[p] != nil {
|
||||||
|
// The pin was already configured.
|
||||||
|
// To properly re-configure a pin, unset it first and set a new
|
||||||
|
// configuration.
|
||||||
|
return ErrNoPinChangeChannel
|
||||||
|
}
|
||||||
|
pinCallbacks[p] = callback
|
||||||
|
|
||||||
|
onceSetupPinInterrupt.Do(func() {
|
||||||
|
err = setupPinInterrupt()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.pin().Set(
|
||||||
|
(p.pin().Get() & ^uint32(esp.GPIO_PIN_PIN_INT_TYPE_Msk|esp.GPIO_PIN_PIN_INT_ENA_Msk)) |
|
||||||
|
uint32(change)<<esp.GPIO_PIN_PIN_INT_TYPE_Pos | uint32(1)<<esp.GPIO_PIN_PIN_INT_ENA_Pos)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
pinCallbacks [maxPin]func(Pin)
|
||||||
|
onceSetupPinInterrupt sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupPinInterrupt() error {
|
||||||
|
esp.INTERRUPT_CORE0.GPIO_INTERRUPT_PRO_MAP.Set(cpuInterruptFromPin)
|
||||||
|
return interrupt.New(cpuInterruptFromPin, func(interrupt.Interrupt) {
|
||||||
|
status := esp.GPIO.STATUS.Get()
|
||||||
|
for i, mask := 0, uint32(1); i < maxPin; i, mask = i+1, mask<<1 {
|
||||||
|
if (status&mask) != 0 && pinCallbacks[i] != nil {
|
||||||
|
pinCallbacks[i](Pin(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// clear interrupt bit
|
||||||
|
esp.GPIO.STATUS_W1TC.SetBits(status)
|
||||||
|
}).Enable()
|
||||||
|
}
|
||||||
|
|
||||||
var DefaultUART = UART0
|
var DefaultUART = UART0
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -34,9 +34,9 @@ func (i Interrupt) Enable() error {
|
||||||
// Set pulse interrupt type (rising edge detection)
|
// Set pulse interrupt type (rising edge detection)
|
||||||
esp.INTERRUPT_CORE0.CPU_INT_TYPE.SetBits(1 << i.num)
|
esp.INTERRUPT_CORE0.CPU_INT_TYPE.SetBits(1 << i.num)
|
||||||
|
|
||||||
// Set default threshold to 5
|
// Set default threshold to defaultThreshold
|
||||||
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(i.num)*4)))
|
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(i.num)*4)))
|
||||||
reg.Set(5)
|
reg.Set(defaultThreshold)
|
||||||
|
|
||||||
// Reset interrupt before reenabling
|
// Reset interrupt before reenabling
|
||||||
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(1 << i.num)
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(1 << i.num)
|
||||||
|
@ -47,6 +47,118 @@ func (i Interrupt) Enable() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adding pseudo function calls that is replaced by the compiler with the actual
|
||||||
|
// functions registered through interrupt.New.
|
||||||
|
//go:linkname callHandlers runtime/interrupt.callHandlers
|
||||||
|
func callHandlers(num int)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IRQNUM_1 = 1 + iota
|
||||||
|
IRQNUM_2
|
||||||
|
IRQNUM_3
|
||||||
|
IRQNUM_4
|
||||||
|
IRQNUM_5
|
||||||
|
IRQNUM_6
|
||||||
|
IRQNUM_7
|
||||||
|
IRQNUM_8
|
||||||
|
IRQNUM_9
|
||||||
|
IRQNUM_10
|
||||||
|
IRQNUM_11
|
||||||
|
IRQNUM_12
|
||||||
|
IRQNUM_13
|
||||||
|
IRQNUM_14
|
||||||
|
IRQNUM_15
|
||||||
|
IRQNUM_16
|
||||||
|
IRQNUM_17
|
||||||
|
IRQNUM_18
|
||||||
|
IRQNUM_19
|
||||||
|
IRQNUM_20
|
||||||
|
IRQNUM_21
|
||||||
|
IRQNUM_22
|
||||||
|
IRQNUM_23
|
||||||
|
IRQNUM_24
|
||||||
|
IRQNUM_25
|
||||||
|
IRQNUM_26
|
||||||
|
IRQNUM_27
|
||||||
|
IRQNUM_28
|
||||||
|
IRQNUM_29
|
||||||
|
IRQNUM_30
|
||||||
|
IRQNUM_31
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultThreshold = 5
|
||||||
|
disableThreshold = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func callHandler(n int) {
|
||||||
|
switch n {
|
||||||
|
case IRQNUM_1:
|
||||||
|
callHandlers(IRQNUM_1)
|
||||||
|
case IRQNUM_2:
|
||||||
|
callHandlers(IRQNUM_2)
|
||||||
|
case IRQNUM_3:
|
||||||
|
callHandlers(IRQNUM_3)
|
||||||
|
case IRQNUM_4:
|
||||||
|
callHandlers(IRQNUM_4)
|
||||||
|
case IRQNUM_5:
|
||||||
|
callHandlers(IRQNUM_5)
|
||||||
|
case IRQNUM_6:
|
||||||
|
callHandlers(IRQNUM_6)
|
||||||
|
case IRQNUM_7:
|
||||||
|
callHandlers(IRQNUM_7)
|
||||||
|
case IRQNUM_8:
|
||||||
|
callHandlers(IRQNUM_8)
|
||||||
|
case IRQNUM_9:
|
||||||
|
callHandlers(IRQNUM_9)
|
||||||
|
case IRQNUM_10:
|
||||||
|
callHandlers(IRQNUM_10)
|
||||||
|
case IRQNUM_11:
|
||||||
|
callHandlers(IRQNUM_11)
|
||||||
|
case IRQNUM_12:
|
||||||
|
callHandlers(IRQNUM_12)
|
||||||
|
case IRQNUM_13:
|
||||||
|
callHandlers(IRQNUM_13)
|
||||||
|
case IRQNUM_14:
|
||||||
|
callHandlers(IRQNUM_14)
|
||||||
|
case IRQNUM_15:
|
||||||
|
callHandlers(IRQNUM_15)
|
||||||
|
case IRQNUM_16:
|
||||||
|
callHandlers(IRQNUM_16)
|
||||||
|
case IRQNUM_17:
|
||||||
|
callHandlers(IRQNUM_17)
|
||||||
|
case IRQNUM_18:
|
||||||
|
callHandlers(IRQNUM_18)
|
||||||
|
case IRQNUM_19:
|
||||||
|
callHandlers(IRQNUM_19)
|
||||||
|
case IRQNUM_20:
|
||||||
|
callHandlers(IRQNUM_20)
|
||||||
|
case IRQNUM_21:
|
||||||
|
callHandlers(IRQNUM_21)
|
||||||
|
case IRQNUM_22:
|
||||||
|
callHandlers(IRQNUM_22)
|
||||||
|
case IRQNUM_23:
|
||||||
|
callHandlers(IRQNUM_23)
|
||||||
|
case IRQNUM_24:
|
||||||
|
callHandlers(IRQNUM_24)
|
||||||
|
case IRQNUM_25:
|
||||||
|
callHandlers(IRQNUM_25)
|
||||||
|
case IRQNUM_26:
|
||||||
|
callHandlers(IRQNUM_26)
|
||||||
|
case IRQNUM_27:
|
||||||
|
callHandlers(IRQNUM_27)
|
||||||
|
case IRQNUM_28:
|
||||||
|
callHandlers(IRQNUM_28)
|
||||||
|
case IRQNUM_29:
|
||||||
|
callHandlers(IRQNUM_29)
|
||||||
|
case IRQNUM_30:
|
||||||
|
callHandlers(IRQNUM_30)
|
||||||
|
case IRQNUM_31:
|
||||||
|
callHandlers(IRQNUM_31)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//export handleInterrupt
|
//export handleInterrupt
|
||||||
func handleInterrupt() {
|
func handleInterrupt() {
|
||||||
mcause := riscv.MCAUSE.Get()
|
mcause := riscv.MCAUSE.Get()
|
||||||
|
@ -54,12 +166,17 @@ func handleInterrupt() {
|
||||||
interruptNumber := uint32(mcause & 0x1f)
|
interruptNumber := uint32(mcause & 0x1f)
|
||||||
|
|
||||||
if !exception && interruptNumber > 0 {
|
if !exception && interruptNumber > 0 {
|
||||||
// save mepc, which could be overwritten by another CPU interrupt
|
// save MSTATUS & MEPC, which could be overwritten by another CPU interrupt
|
||||||
|
mstatus := riscv.MSTATUS.Get()
|
||||||
mepc := riscv.MEPC.Get()
|
mepc := riscv.MEPC.Get()
|
||||||
|
// Useing threshold to temporary disable this interrupts.
|
||||||
|
// FYI: using CPU interrupt enable bit make runtime to loose interrupts.
|
||||||
|
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(interruptNumber)*4)))
|
||||||
|
thresholdSave := reg.Get()
|
||||||
|
reg.Set(disableThreshold)
|
||||||
|
riscv.Asm("fence")
|
||||||
|
|
||||||
// disable interrupt
|
|
||||||
interruptBit := uint32(1 << interruptNumber)
|
interruptBit := uint32(1 << interruptNumber)
|
||||||
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.ClearBits(interruptBit)
|
|
||||||
|
|
||||||
// reset pending status interrupt
|
// reset pending status interrupt
|
||||||
if esp.INTERRUPT_CORE0.CPU_INT_TYPE.Get()&interruptBit != 0 {
|
if esp.INTERRUPT_CORE0.CPU_INT_TYPE.Get()&interruptBit != 0 {
|
||||||
|
@ -72,23 +189,22 @@ func handleInterrupt() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable CPU interrupts
|
// enable CPU interrupts
|
||||||
riscv.MSTATUS.SetBits(0x8)
|
riscv.MSTATUS.SetBits(1 << 3)
|
||||||
|
|
||||||
// Call registered interrupt handler(s)
|
// Call registered interrupt handler(s)
|
||||||
esp.HandleInterrupt(int(interruptNumber))
|
callHandler(int(interruptNumber))
|
||||||
|
|
||||||
// disable CPU interrupts
|
// disable CPU interrupts
|
||||||
riscv.MSTATUS.ClearBits(0x8)
|
riscv.MSTATUS.ClearBits(1 << 3)
|
||||||
|
|
||||||
// mpie must be set to 1 to resume interrupts after 'MRET'
|
// restore interrupt threshold to enable interrupt again
|
||||||
riscv.MSTATUS.SetBits(0x80)
|
reg.Set(thresholdSave)
|
||||||
|
riscv.Asm("fence")
|
||||||
|
|
||||||
// restore MEPC
|
// restore MSTATUS & MEPC
|
||||||
|
riscv.MSTATUS.Set(mstatus)
|
||||||
riscv.MEPC.Set(mepc)
|
riscv.MEPC.Set(mepc)
|
||||||
|
|
||||||
// enable this interrupt
|
|
||||||
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.SetBits(interruptBit)
|
|
||||||
|
|
||||||
// do not enable CPU interrupts now
|
// do not enable CPU interrupts now
|
||||||
// the 'MRET' in src/device/riscv/handleinterrupt.S will copies the state of MPIE back into MIE, and subsequently clears MPIE.
|
// the 'MRET' in src/device/riscv/handleinterrupt.S will copies the state of MPIE back into MIE, and subsequently clears MPIE.
|
||||||
// riscv.MSTATUS.SetBits(0x8)
|
// riscv.MSTATUS.SetBits(0x8)
|
||||||
|
@ -104,6 +220,16 @@ func handleException(mcause uintptr) {
|
||||||
println("*** Exception: pc:", riscv.MEPC.Get())
|
println("*** Exception: pc:", riscv.MEPC.Get())
|
||||||
println("*** Exception: code:", uint32(mcause&0x1f))
|
println("*** Exception: code:", uint32(mcause&0x1f))
|
||||||
println("*** Exception: mcause:", mcause)
|
println("*** Exception: mcause:", mcause)
|
||||||
|
switch uint32(mcause & 0x1f) {
|
||||||
|
case 1:
|
||||||
|
println("*** virtual addess:", riscv.MTVAL.Get())
|
||||||
|
case 2:
|
||||||
|
println("*** opcode:", riscv.MTVAL.Get())
|
||||||
|
case 5:
|
||||||
|
println("*** read address:", riscv.MTVAL.Get())
|
||||||
|
case 7:
|
||||||
|
println("*** write address:", riscv.MTVAL.Get())
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
riscv.Asm("wfi")
|
riscv.Asm("wfi")
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче