sam: add support for pin change interrupts (samd5x)
Этот коммит содержится в:
родитель
fed433c046
коммит
5c8d4e54d6
1 изменённых файлов: 160 добавлений и 0 удалений
|
@ -44,6 +44,26 @@ const (
|
||||||
PinInputPulldown PinMode = 18
|
PinInputPulldown PinMode = 18
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PinChange uint8
|
||||||
|
|
||||||
|
// Pin change interrupt constants for SetInterrupt.
|
||||||
|
const (
|
||||||
|
PinRising PinChange = sam.EIC_CONFIG_SENSE0_RISE
|
||||||
|
PinFalling PinChange = sam.EIC_CONFIG_SENSE0_FALL
|
||||||
|
PinToggle PinChange = sam.EIC_CONFIG_SENSE0_BOTH
|
||||||
|
)
|
||||||
|
|
||||||
|
// Callbacks to be called for pins configured with SetInterrupt. Unfortunately,
|
||||||
|
// we also need to keep track of which interrupt channel is used by which pin,
|
||||||
|
// as the only alternative would be iterating through all pins.
|
||||||
|
//
|
||||||
|
// We're using the magic constant 16 here because the SAM D21 has 16 interrupt
|
||||||
|
// channels configurable for pins.
|
||||||
|
var (
|
||||||
|
interruptPins [16]Pin // warning: the value is invalid when pinCallbacks[i] is not set!
|
||||||
|
pinCallbacks [16]func(Pin)
|
||||||
|
)
|
||||||
|
|
||||||
// Hardware pins
|
// Hardware pins
|
||||||
const (
|
const (
|
||||||
PA00 Pin = 0
|
PA00 Pin = 0
|
||||||
|
@ -268,6 +288,146 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetInterrupt sets an interrupt to be executed when a particular pin changes
|
||||||
|
// state.
|
||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
// Most pins follow a common pattern where the EXTINT value is the pin
|
||||||
|
// number modulo 16. However, there are a few exceptions, as you can see
|
||||||
|
// below.
|
||||||
|
extint := uint8(0)
|
||||||
|
|
||||||
|
switch p {
|
||||||
|
case PA08:
|
||||||
|
// Connected to NMI. This is not currently supported.
|
||||||
|
return ErrInvalidInputPin
|
||||||
|
case PB26:
|
||||||
|
extint = 12
|
||||||
|
case PB27:
|
||||||
|
extint = 13
|
||||||
|
case PB28:
|
||||||
|
extint = 14
|
||||||
|
case PB29:
|
||||||
|
extint = 15
|
||||||
|
default:
|
||||||
|
// All other pins follow a normal pattern.
|
||||||
|
extint = uint8(p) % 16
|
||||||
|
}
|
||||||
|
|
||||||
|
if callback == nil {
|
||||||
|
// Disable this pin interrupt (if it was enabled).
|
||||||
|
sam.EIC.INTENCLR.Set(1 << extint)
|
||||||
|
if pinCallbacks[extint] != nil {
|
||||||
|
pinCallbacks[extint] = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pinCallbacks[extint] != nil {
|
||||||
|
// The pin was already configured.
|
||||||
|
// To properly re-configure a pin, unset it first and set a new
|
||||||
|
// configuration.
|
||||||
|
return ErrNoPinChangeChannel
|
||||||
|
}
|
||||||
|
pinCallbacks[extint] = callback
|
||||||
|
interruptPins[extint] = p
|
||||||
|
|
||||||
|
if (sam.EIC.CTRLA.Get() & 0x02) == 0 {
|
||||||
|
// EIC peripheral has not yet been initialized. Initialize it now.
|
||||||
|
|
||||||
|
// The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is
|
||||||
|
// enabled by default, so doesn't have to be re-enabled. The other is
|
||||||
|
// required for detecting edges and must be enabled manually.
|
||||||
|
sam.GCLK.PCHCTRL[4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
|
// should not be necessary (CLKCTRL is not synchronized)
|
||||||
|
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0 << sam.GCLK_SYNCBUSY_GENCTRL_Pos) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONFIG register is enable-protected, so disable EIC.
|
||||||
|
sam.EIC.CTRLA.Set(0)
|
||||||
|
|
||||||
|
// Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the
|
||||||
|
// sense value (filter bit set to 0, sense bits set to the change value).
|
||||||
|
addr := &sam.EIC.CONFIG[0]
|
||||||
|
if extint >= 8 {
|
||||||
|
addr = &sam.EIC.CONFIG[1]
|
||||||
|
}
|
||||||
|
pos := (extint % 8) * 4 // bit position in register
|
||||||
|
addr.Set((addr.Get() &^ (0xf << pos)) | uint32(change)<<pos)
|
||||||
|
|
||||||
|
// Enable external interrupt for this pin.
|
||||||
|
sam.EIC.INTENSET.Set(1 << extint)
|
||||||
|
|
||||||
|
sam.EIC.CTRLA.Set(sam.EIC_CTRLA_ENABLE)
|
||||||
|
for sam.EIC.SYNCBUSY.HasBits(sam.EIC_SYNCBUSY_ENABLE) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they
|
||||||
|
// were set before). This avoids clearing the pin pull mode while
|
||||||
|
// configuring the pin interrupt.
|
||||||
|
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | (p.getPinCfg() & (sam.PORT_GROUP_PINCFG_INEN | sam.PORT_GROUP_PINCFG_PULLEN)))
|
||||||
|
if p&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||||
|
p.setPMux(val | (0 << sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||||
|
p.setPMux(val | (0 << sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEICInterrupt := func(interrupt.Interrupt) {
|
||||||
|
flags := sam.EIC.INTFLAG.Get()
|
||||||
|
sam.EIC.INTFLAG.Set(flags) // clear interrupt
|
||||||
|
for i := uint(0); i < 16; i++ { // there are 16 channels
|
||||||
|
if flags&(1<<i) != 0 {
|
||||||
|
pinCallbacks[i](interruptPins[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch extint {
|
||||||
|
case 0:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_0, handleEICInterrupt).Enable()
|
||||||
|
case 1:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_1, handleEICInterrupt).Enable()
|
||||||
|
case 2:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_2, handleEICInterrupt).Enable()
|
||||||
|
case 3:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_3, handleEICInterrupt).Enable()
|
||||||
|
case 4:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_4, handleEICInterrupt).Enable()
|
||||||
|
case 5:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_5, handleEICInterrupt).Enable()
|
||||||
|
case 6:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_6, handleEICInterrupt).Enable()
|
||||||
|
case 7:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_7, handleEICInterrupt).Enable()
|
||||||
|
case 8:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_8, handleEICInterrupt).Enable()
|
||||||
|
case 9:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_9, handleEICInterrupt).Enable()
|
||||||
|
case 10:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_10, handleEICInterrupt).Enable()
|
||||||
|
case 11:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_11, handleEICInterrupt).Enable()
|
||||||
|
case 12:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_12, handleEICInterrupt).Enable()
|
||||||
|
case 13:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_13, handleEICInterrupt).Enable()
|
||||||
|
case 14:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_14, handleEICInterrupt).Enable()
|
||||||
|
case 15:
|
||||||
|
interrupt.New(sam.IRQ_EIC_EXTINT_15, handleEICInterrupt).Enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||||
// implement bit-banged drivers.
|
// implement bit-banged drivers.
|
||||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче