Skip to content

Commit b176494

Browse files
zdimadeadprogram
authored andcommitted
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
1 parent 0243a5b commit b176494

File tree

2 files changed

+225
-14
lines changed

2 files changed

+225
-14
lines changed

src/machine/machine_esp32c3.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ package machine
55

66
import (
77
"device/esp"
8+
"runtime/interrupt"
89
"runtime/volatile"
10+
"sync"
911
"unsafe"
1012
)
1113

1214
const deviceName = esp.Device
15+
const maxPin = 22
16+
const cpuInterruptFromPin = 6
1317

1418
// CPUFrequency returns the current CPU frequency of the chip.
1519
// Currently it is a fixed frequency but it may allow changing in the future.
@@ -24,6 +28,18 @@ const (
2428
PinInputPulldown
2529
)
2630

31+
type PinChange uint8
32+
33+
// Pin change interrupt constants for SetInterrupt.
34+
const (
35+
PinNoInterrupt PinChange = iota
36+
PinRising
37+
PinFalling
38+
PinToggle
39+
PinLowLevel
40+
PinHighLevel
41+
)
42+
2743
// Configure this pin with the given configuration.
2844
func (p Pin) Configure(config PinConfig) {
2945
if p == NoPin {
@@ -84,6 +100,11 @@ func (p Pin) mux() *volatile.Register32 {
84100
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.IO_MUX.GPIO0)) + uintptr(p)*4)))
85101
}
86102

103+
// pin returns the PIN register corresponding to the given GPIO pin.
104+
func (p Pin) pin() *volatile.Register32 {
105+
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.PIN0)) + uintptr(p)*4)))
106+
}
107+
87108
// Set the pin to high or low.
88109
// Warning: only use this on an output pin!
89110
func (p Pin) Set(value bool) {
@@ -129,6 +150,70 @@ func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
129150
return &esp.GPIO.OUT_W1TC, 1 << p
130151
}
131152

153+
// SetInterrupt sets an interrupt to be executed when a particular pin changes
154+
// state. The pin should already be configured as an input, including a pull up
155+
// or down if no external pull is provided.
156+
//
157+
// You can pass a nil func to unset the pin change interrupt. If you do so,
158+
// the change parameter is ignored and can be set to any value (such as 0).
159+
// If the pin is already configured with a callback, you must first unset
160+
// this pins interrupt before you can set a new callback.
161+
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) (err error) {
162+
if p >= maxPin {
163+
return ErrInvalidInputPin
164+
}
165+
166+
if callback == nil || change == PinNoInterrupt {
167+
// Disable this pin interrupt
168+
p.pin().ClearBits(esp.GPIO_PIN_PIN_INT_TYPE_Msk | esp.GPIO_PIN_PIN_INT_ENA_Msk)
169+
170+
if pinCallbacks[p] != nil {
171+
pinCallbacks[p] = nil
172+
}
173+
return nil
174+
}
175+
176+
if pinCallbacks[p] != nil {
177+
// The pin was already configured.
178+
// To properly re-configure a pin, unset it first and set a new
179+
// configuration.
180+
return ErrNoPinChangeChannel
181+
}
182+
pinCallbacks[p] = callback
183+
184+
onceSetupPinInterrupt.Do(func() {
185+
err = setupPinInterrupt()
186+
})
187+
if err != nil {
188+
return err
189+
}
190+
191+
p.pin().Set(
192+
(p.pin().Get() & ^uint32(esp.GPIO_PIN_PIN_INT_TYPE_Msk|esp.GPIO_PIN_PIN_INT_ENA_Msk)) |
193+
uint32(change)<<esp.GPIO_PIN_PIN_INT_TYPE_Pos | uint32(1)<<esp.GPIO_PIN_PIN_INT_ENA_Pos)
194+
195+
return nil
196+
}
197+
198+
var (
199+
pinCallbacks [maxPin]func(Pin)
200+
onceSetupPinInterrupt sync.Once
201+
)
202+
203+
func setupPinInterrupt() error {
204+
esp.INTERRUPT_CORE0.GPIO_INTERRUPT_PRO_MAP.Set(cpuInterruptFromPin)
205+
return interrupt.New(cpuInterruptFromPin, func(interrupt.Interrupt) {
206+
status := esp.GPIO.STATUS.Get()
207+
for i, mask := 0, uint32(1); i < maxPin; i, mask = i+1, mask<<1 {
208+
if (status&mask) != 0 && pinCallbacks[i] != nil {
209+
pinCallbacks[i](Pin(i))
210+
}
211+
}
212+
// clear interrupt bit
213+
esp.GPIO.STATUS_W1TC.SetBits(status)
214+
}).Enable()
215+
}
216+
132217
var DefaultUART = UART0
133218

134219
var (

src/runtime/interrupt/interrupt_esp32c3.go

Lines changed: 140 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ func (i Interrupt) Enable() error {
3434
// Set pulse interrupt type (rising edge detection)
3535
esp.INTERRUPT_CORE0.CPU_INT_TYPE.SetBits(1 << i.num)
3636

37-
// Set default threshold to 5
37+
// Set default threshold to defaultThreshold
3838
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(i.num)*4)))
39-
reg.Set(5)
39+
reg.Set(defaultThreshold)
4040

4141
// Reset interrupt before reenabling
4242
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(1 << i.num)
@@ -47,19 +47,136 @@ func (i Interrupt) Enable() error {
4747
return nil
4848
}
4949

50+
// Adding pseudo function calls that is replaced by the compiler with the actual
51+
// functions registered through interrupt.New.
52+
//go:linkname callHandlers runtime/interrupt.callHandlers
53+
func callHandlers(num int)
54+
55+
const (
56+
IRQNUM_1 = 1 + iota
57+
IRQNUM_2
58+
IRQNUM_3
59+
IRQNUM_4
60+
IRQNUM_5
61+
IRQNUM_6
62+
IRQNUM_7
63+
IRQNUM_8
64+
IRQNUM_9
65+
IRQNUM_10
66+
IRQNUM_11
67+
IRQNUM_12
68+
IRQNUM_13
69+
IRQNUM_14
70+
IRQNUM_15
71+
IRQNUM_16
72+
IRQNUM_17
73+
IRQNUM_18
74+
IRQNUM_19
75+
IRQNUM_20
76+
IRQNUM_21
77+
IRQNUM_22
78+
IRQNUM_23
79+
IRQNUM_24
80+
IRQNUM_25
81+
IRQNUM_26
82+
IRQNUM_27
83+
IRQNUM_28
84+
IRQNUM_29
85+
IRQNUM_30
86+
IRQNUM_31
87+
)
88+
89+
const (
90+
defaultThreshold = 5
91+
disableThreshold = 10
92+
)
93+
94+
//go:inline
95+
func callHandler(n int) {
96+
switch n {
97+
case IRQNUM_1:
98+
callHandlers(IRQNUM_1)
99+
case IRQNUM_2:
100+
callHandlers(IRQNUM_2)
101+
case IRQNUM_3:
102+
callHandlers(IRQNUM_3)
103+
case IRQNUM_4:
104+
callHandlers(IRQNUM_4)
105+
case IRQNUM_5:
106+
callHandlers(IRQNUM_5)
107+
case IRQNUM_6:
108+
callHandlers(IRQNUM_6)
109+
case IRQNUM_7:
110+
callHandlers(IRQNUM_7)
111+
case IRQNUM_8:
112+
callHandlers(IRQNUM_8)
113+
case IRQNUM_9:
114+
callHandlers(IRQNUM_9)
115+
case IRQNUM_10:
116+
callHandlers(IRQNUM_10)
117+
case IRQNUM_11:
118+
callHandlers(IRQNUM_11)
119+
case IRQNUM_12:
120+
callHandlers(IRQNUM_12)
121+
case IRQNUM_13:
122+
callHandlers(IRQNUM_13)
123+
case IRQNUM_14:
124+
callHandlers(IRQNUM_14)
125+
case IRQNUM_15:
126+
callHandlers(IRQNUM_15)
127+
case IRQNUM_16:
128+
callHandlers(IRQNUM_16)
129+
case IRQNUM_17:
130+
callHandlers(IRQNUM_17)
131+
case IRQNUM_18:
132+
callHandlers(IRQNUM_18)
133+
case IRQNUM_19:
134+
callHandlers(IRQNUM_19)
135+
case IRQNUM_20:
136+
callHandlers(IRQNUM_20)
137+
case IRQNUM_21:
138+
callHandlers(IRQNUM_21)
139+
case IRQNUM_22:
140+
callHandlers(IRQNUM_22)
141+
case IRQNUM_23:
142+
callHandlers(IRQNUM_23)
143+
case IRQNUM_24:
144+
callHandlers(IRQNUM_24)
145+
case IRQNUM_25:
146+
callHandlers(IRQNUM_25)
147+
case IRQNUM_26:
148+
callHandlers(IRQNUM_26)
149+
case IRQNUM_27:
150+
callHandlers(IRQNUM_27)
151+
case IRQNUM_28:
152+
callHandlers(IRQNUM_28)
153+
case IRQNUM_29:
154+
callHandlers(IRQNUM_29)
155+
case IRQNUM_30:
156+
callHandlers(IRQNUM_30)
157+
case IRQNUM_31:
158+
callHandlers(IRQNUM_31)
159+
}
160+
}
161+
50162
//export handleInterrupt
51163
func handleInterrupt() {
52164
mcause := riscv.MCAUSE.Get()
53165
exception := mcause&(1<<31) == 0
54166
interruptNumber := uint32(mcause & 0x1f)
55167

56168
if !exception && interruptNumber > 0 {
57-
// save mepc, which could be overwritten by another CPU interrupt
169+
// save MSTATUS & MEPC, which could be overwritten by another CPU interrupt
170+
mstatus := riscv.MSTATUS.Get()
58171
mepc := riscv.MEPC.Get()
172+
// Useing threshold to temporary disable this interrupts.
173+
// FYI: using CPU interrupt enable bit make runtime to loose interrupts.
174+
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(interruptNumber)*4)))
175+
thresholdSave := reg.Get()
176+
reg.Set(disableThreshold)
177+
riscv.Asm("fence")
59178

60-
// disable interrupt
61179
interruptBit := uint32(1 << interruptNumber)
62-
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.ClearBits(interruptBit)
63180

64181
// reset pending status interrupt
65182
if esp.INTERRUPT_CORE0.CPU_INT_TYPE.Get()&interruptBit != 0 {
@@ -72,23 +189,22 @@ func handleInterrupt() {
72189
}
73190

74191
// enable CPU interrupts
75-
riscv.MSTATUS.SetBits(0x8)
192+
riscv.MSTATUS.SetBits(1 << 3)
76193

77194
// Call registered interrupt handler(s)
78-
esp.HandleInterrupt(int(interruptNumber))
195+
callHandler(int(interruptNumber))
79196

80197
// disable CPU interrupts
81-
riscv.MSTATUS.ClearBits(0x8)
198+
riscv.MSTATUS.ClearBits(1 << 3)
82199

83-
// mpie must be set to 1 to resume interrupts after 'MRET'
84-
riscv.MSTATUS.SetBits(0x80)
200+
// restore interrupt threshold to enable interrupt again
201+
reg.Set(thresholdSave)
202+
riscv.Asm("fence")
85203

86-
// restore MEPC
204+
// restore MSTATUS & MEPC
205+
riscv.MSTATUS.Set(mstatus)
87206
riscv.MEPC.Set(mepc)
88207

89-
// enable this interrupt
90-
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.SetBits(interruptBit)
91-
92208
// do not enable CPU interrupts now
93209
// the 'MRET' in src/device/riscv/handleinterrupt.S will copies the state of MPIE back into MIE, and subsequently clears MPIE.
94210
// riscv.MSTATUS.SetBits(0x8)
@@ -104,6 +220,16 @@ func handleException(mcause uintptr) {
104220
println("*** Exception: pc:", riscv.MEPC.Get())
105221
println("*** Exception: code:", uint32(mcause&0x1f))
106222
println("*** Exception: mcause:", mcause)
223+
switch uint32(mcause & 0x1f) {
224+
case 1:
225+
println("*** virtual addess:", riscv.MTVAL.Get())
226+
case 2:
227+
println("*** opcode:", riscv.MTVAL.Get())
228+
case 5:
229+
println("*** read address:", riscv.MTVAL.Get())
230+
case 7:
231+
println("*** write address:", riscv.MTVAL.Get())
232+
}
107233
for {
108234
riscv.Asm("wfi")
109235
}

0 commit comments

Comments
 (0)