teensy40: Add GPIO external interrupt support
Этот коммит содержится в:
родитель
ce57a034c3
коммит
7cc687d416
1 изменённых файлов: 139 добавлений и 15 удалений
|
@ -4,6 +4,8 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/nxp"
|
"device/nxp"
|
||||||
|
"math/bits"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,29 +19,56 @@ type PinMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// GPIO
|
// GPIO
|
||||||
PinInput PinMode = iota
|
PinInput PinMode = 0
|
||||||
PinInputPullUp
|
PinInputPullUp PinMode = 1
|
||||||
PinInputPullDown
|
PinInputPullDown PinMode = 2
|
||||||
PinOutput
|
PinOutput PinMode = 3
|
||||||
PinOutputOpenDrain
|
PinOutputOpenDrain PinMode = 4
|
||||||
PinDisable
|
PinDisable PinMode = 5
|
||||||
|
|
||||||
// ADC
|
// ADC
|
||||||
PinInputAnalog
|
PinInputAnalog PinMode = 6
|
||||||
|
|
||||||
// UART
|
// UART
|
||||||
PinModeUARTTX
|
PinModeUARTTX PinMode = 7
|
||||||
PinModeUARTRX
|
PinModeUARTRX PinMode = 8
|
||||||
|
|
||||||
// SPI
|
// SPI
|
||||||
PinModeSPISDI
|
PinModeSPISDI PinMode = 9
|
||||||
PinModeSPISDO
|
PinModeSPISDO PinMode = 10
|
||||||
PinModeSPICLK
|
PinModeSPICLK PinMode = 11
|
||||||
PinModeSPICS
|
PinModeSPICS PinMode = 12
|
||||||
|
|
||||||
// I2C
|
// I2C
|
||||||
PinModeI2CSDA
|
PinModeI2CSDA PinMode = 13
|
||||||
PinModeI2CSCL
|
PinModeI2CSCL PinMode = 14
|
||||||
|
)
|
||||||
|
|
||||||
|
type PinChange uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
PinLow PinChange = 0
|
||||||
|
PinHigh PinChange = 1
|
||||||
|
PinRising PinChange = 2
|
||||||
|
PinFalling PinChange = 3
|
||||||
|
PinToggle PinChange = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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):
|
// From the i.MXRT1062 Processor Reference Manual (Chapter 12 - GPIO):
|
||||||
|
@ -301,6 +330,101 @@ func (p Pin) Toggle() {
|
||||||
gpio.DR_TOGGLE.Set(p.getMask())
|
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)
|
// getGPIO returns both the normal (IPG_CLK_ROOT) and high-speed (AHB_CLK_ROOT)
|
||||||
// GPIO peripherals to which a given Pin is connected.
|
// GPIO peripherals to which a given Pin is connected.
|
||||||
//
|
//
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче