Skip to content

Commit 3941c41

Browse files
committed
teensy40: Add GPIO external interrupt support
1 parent c1def48 commit 3941c41

File tree

1 file changed

+139
-15
lines changed

1 file changed

+139
-15
lines changed

src/machine/machine_mimxrt1062.go

+139-15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package machine
44

55
import (
66
"device/nxp"
7+
"math/bits"
8+
"runtime/interrupt"
79
"runtime/volatile"
810
)
911

@@ -17,29 +19,56 @@ type PinMode uint8
1719

1820
const (
1921
// GPIO
20-
PinInput PinMode = iota
21-
PinInputPullUp
22-
PinInputPullDown
23-
PinOutput
24-
PinOutputOpenDrain
25-
PinDisable
22+
PinInput PinMode = 0
23+
PinInputPullUp PinMode = 1
24+
PinInputPullDown PinMode = 2
25+
PinOutput PinMode = 3
26+
PinOutputOpenDrain PinMode = 4
27+
PinDisable PinMode = 5
2628

2729
// ADC
28-
PinInputAnalog
30+
PinInputAnalog PinMode = 6
2931

3032
// UART
31-
PinModeUARTTX
32-
PinModeUARTRX
33+
PinModeUARTTX PinMode = 7
34+
PinModeUARTRX PinMode = 8
3335

3436
// SPI
35-
PinModeSPISDI
36-
PinModeSPISDO
37-
PinModeSPICLK
38-
PinModeSPICS
37+
PinModeSPISDI PinMode = 9
38+
PinModeSPISDO PinMode = 10
39+
PinModeSPICLK PinMode = 11
40+
PinModeSPICS PinMode = 12
3941

4042
// I2C
41-
PinModeI2CSDA
42-
PinModeI2CSCL
43+
PinModeI2CSDA PinMode = 13
44+
PinModeI2CSCL PinMode = 14
45+
)
46+
47+
type PinChange uint8
48+
49+
const (
50+
PinLow PinChange = 0
51+
PinHigh PinChange = 1
52+
PinRising PinChange = 2
53+
PinFalling PinChange = 3
54+
PinToggle PinChange = 4
55+
)
56+
57+
// pinJumpTable represents a function lookup table for all 128 GPIO pins.
58+
//
59+
// There are 4 GPIO ports (A-D) and 32 pins (0-31) on each port. The uint8 value
60+
// of a Pin is used as table index. The number of pins with a defined (non-nil)
61+
// function is recorded in the uint8 field numDefined.
62+
type pinJumpTable struct {
63+
lut [4 * 32]func(Pin)
64+
numDefined uint8
65+
}
66+
67+
// pinISR stores the interrupt callbacks for GPIO pins, and pinInterrupt holds
68+
// an interrupt service routine that dispatches the interrupt callbacks.
69+
var (
70+
pinISR pinJumpTable
71+
pinInterrupt *interrupt.Interrupt
4372
)
4473

4574
// From the i.MXRT1062 Processor Reference Manual (Chapter 12 - GPIO):
@@ -301,6 +330,101 @@ func (p Pin) Toggle() {
301330
gpio.DR_TOGGLE.Set(p.getMask())
302331
}
303332

333+
// dispatchInterrupt invokes the user-provided callback functions for external
334+
// interrupts generated on the high-speed GPIO pins.
335+
//
336+
// Unfortunately, all four high-speed GPIO ports (A-D) are connected to just a
337+
// single interrupt control line. Therefore, the interrupt status register (ISR)
338+
// must be checked in all four GPIO ports on every interrupt.
339+
func (jt *pinJumpTable) dispatchInterrupt(interrupt.Interrupt) {
340+
handle := func(gpio *nxp.GPIO_Type, port Pin) {
341+
if status := gpio.ISR.Get() & gpio.IMR.Get(); status != 0 {
342+
gpio.ISR.Set(status) // clear interrupt
343+
for status != 0 {
344+
p := Pin(bits.TrailingZeros32(status))
345+
i := Pin(port + p)
346+
jt.lut[i](i)
347+
status &^= 1 << p
348+
}
349+
}
350+
}
351+
if jt.numDefined > 0 {
352+
handle(nxp.GPIO6, portA)
353+
handle(nxp.GPIO7, portB)
354+
handle(nxp.GPIO8, portC)
355+
handle(nxp.GPIO9, portD)
356+
}
357+
}
358+
359+
// set associates a function with a given Pin in the receiver lookup table. If
360+
// the function is nil, the given Pin's associated function is removed.
361+
func (jt *pinJumpTable) set(pin Pin, fn func(Pin)) {
362+
if int(pin) < len(jt.lut) {
363+
if nil != fn {
364+
if nil == jt.lut[pin] {
365+
jt.numDefined++
366+
}
367+
jt.lut[pin] = fn
368+
} else {
369+
if nil != jt.lut[pin] {
370+
jt.numDefined--
371+
}
372+
jt.lut[pin] = nil
373+
}
374+
}
375+
}
376+
377+
// SetInterrupt sets an interrupt to be executed when a particular pin changes
378+
// state. The pin should already be configured as an input, including a pull up
379+
// or down if no external pull is provided.
380+
//
381+
// This call will replace a previously set callback on this pin. You can pass a
382+
// nil func to unset the pin change interrupt. If you do so, the change
383+
// parameter is ignored and can be set to any value (such as 0).
384+
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
385+
_, gpio := p.getGPIO() // use fast GPIO for all pins
386+
mask := p.getMask()
387+
if nil != callback {
388+
switch change {
389+
case PinLow, PinHigh, PinRising, PinFalling:
390+
gpio.EDGE_SEL.ClearBits(mask)
391+
var reg *volatile.Register32
392+
var pos uint8
393+
if pos = p.getPos(); pos < 16 {
394+
reg = &gpio.ICR1 // ICR1 = pins 0-15
395+
} else {
396+
reg = &gpio.ICR2 // ICR2 = pins 16-31
397+
pos -= 16
398+
}
399+
reg.ReplaceBits(uint32(change), 0x3, pos*2)
400+
case PinToggle:
401+
gpio.EDGE_SEL.SetBits(mask)
402+
}
403+
pinISR.set(p, callback) // associate the callback with the pin
404+
gpio.ISR.Set(mask) // clear any pending interrupt (W1C)
405+
gpio.IMR.SetBits(mask) // enable external interrupt
406+
} else {
407+
pinISR.set(p, nil) // remove any associated callback from the pin
408+
gpio.ISR.Set(mask) // clear any pending interrupt (W1C)
409+
gpio.IMR.ClearBits(mask) // disable external interrupt
410+
}
411+
// enable or disable the interrupt based on number of defined callbacks
412+
if pinISR.numDefined > 0 {
413+
if nil == pinInterrupt {
414+
// create the Interrupt if it is not yet defined
415+
irq := interrupt.New(nxp.IRQ_GPIO6_7_8_9, pinISR.dispatchInterrupt)
416+
pinInterrupt = &irq
417+
pinInterrupt.Enable()
418+
}
419+
} else {
420+
if nil != pinInterrupt {
421+
// disable the interrupt if it is defined
422+
pinInterrupt.Disable()
423+
}
424+
}
425+
return nil
426+
}
427+
304428
// getGPIO returns both the normal (IPG_CLK_ROOT) and high-speed (AHB_CLK_ROOT)
305429
// GPIO peripherals to which a given Pin is connected.
306430
//

0 commit comments

Comments
 (0)