Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions src/machine/machine_mimxrt1062.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package machine

import (
"device/nxp"
"math/bits"
"runtime/interrupt"
"runtime/volatile"
)

Expand Down Expand Up @@ -42,6 +44,33 @@ const (
PinModeI2CSCL
)

type PinChange uint8

const (
PinLow PinChange = iota
PinHigh
PinRising
PinFalling
PinToggle
)

// pinJumpTable represents a function lookup table for all 128 GPIO pins.
//
// There are 4 GPIO ports (A-D) and 32 pins (0-31) on each port. The uint8 value
// of a Pin is used as table index. The number of pins with a defined (non-nil)
// function is recorded in the uint8 field numDefined.
type pinJumpTable struct {
lut [4 * 32]func(Pin)
numDefined uint8
}

// pinISR stores the interrupt callbacks for GPIO pins, and pinInterrupt holds
// an interrupt service routine that dispatches the interrupt callbacks.
var (
pinISR pinJumpTable
pinInterrupt *interrupt.Interrupt
)

// From the i.MXRT1062 Processor Reference Manual (Chapter 12 - GPIO):
//
// | High-speed GPIOs exist in this device:
Expand Down Expand Up @@ -301,6 +330,101 @@ func (p Pin) Toggle() {
gpio.DR_TOGGLE.Set(p.getMask())
}

// dispatchInterrupt invokes the user-provided callback functions for external
// interrupts generated on the high-speed GPIO pins.
//
// Unfortunately, all four high-speed GPIO ports (A-D) are connected to just a
// single interrupt control line. Therefore, the interrupt status register (ISR)
// must be checked in all four GPIO ports on every interrupt.
func (jt *pinJumpTable) dispatchInterrupt(interrupt.Interrupt) {
handle := func(gpio *nxp.GPIO_Type, port Pin) {
if status := gpio.ISR.Get() & gpio.IMR.Get(); status != 0 {
gpio.ISR.Set(status) // clear interrupt
for status != 0 {
p := Pin(bits.TrailingZeros32(status))
i := Pin(port + p)
jt.lut[i](i)
status &^= 1 << p
}
}
}
if jt.numDefined > 0 {
handle(nxp.GPIO6, portA)
handle(nxp.GPIO7, portB)
handle(nxp.GPIO8, portC)
handle(nxp.GPIO9, portD)
}
}

// set associates a function with a given Pin in the receiver lookup table. If
// the function is nil, the given Pin's associated function is removed.
func (jt *pinJumpTable) set(pin Pin, fn func(Pin)) {
if int(pin) < len(jt.lut) {
if nil != fn {
if nil == jt.lut[pin] {
jt.numDefined++
}
jt.lut[pin] = fn
} else {
if nil != jt.lut[pin] {
jt.numDefined--
}
jt.lut[pin] = nil
}
}
}

// 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 {
_, gpio := p.getGPIO() // use fast GPIO for all pins
mask := p.getMask()
if nil != callback {
switch change {
case PinLow, PinHigh, PinRising, PinFalling:
gpio.EDGE_SEL.ClearBits(mask)
var reg *volatile.Register32
var pos uint8
if pos = p.getPos(); pos < 16 {
reg = &gpio.ICR1 // ICR1 = pins 0-15
} else {
reg = &gpio.ICR2 // ICR2 = pins 16-31
pos -= 16
}
reg.ReplaceBits(uint32(change), 0x3, pos*2)
case PinToggle:
gpio.EDGE_SEL.SetBits(mask)
}
pinISR.set(p, callback) // associate the callback with the pin
gpio.ISR.Set(mask) // clear any pending interrupt (W1C)
gpio.IMR.SetBits(mask) // enable external interrupt
} else {
pinISR.set(p, nil) // remove any associated callback from the pin
gpio.ISR.Set(mask) // clear any pending interrupt (W1C)
gpio.IMR.ClearBits(mask) // disable external interrupt
}
// enable or disable the interrupt based on number of defined callbacks
if pinISR.numDefined > 0 {
if nil == pinInterrupt {
// create the Interrupt if it is not yet defined
irq := interrupt.New(nxp.IRQ_GPIO6_7_8_9, pinISR.dispatchInterrupt)
pinInterrupt = &irq
pinInterrupt.Enable()
}
} else {
if nil != pinInterrupt {
// disable the interrupt if it is defined
pinInterrupt.Disable()
}
}
return nil
}

// getGPIO returns both the normal (IPG_CLK_ROOT) and high-speed (AHB_CLK_ROOT)
// GPIO peripherals to which a given Pin is connected.
//
Expand Down