diff --git a/src/machine/machine.go b/src/machine/machine.go index 06274220da..18be2f2bd2 100644 --- a/src/machine/machine.go +++ b/src/machine/machine.go @@ -3,12 +3,13 @@ package machine import "errors" var ( - ErrTimeoutRNG = errors.New("machine: RNG Timeout") - ErrInvalidInputPin = errors.New("machine: invalid input pin") - ErrInvalidOutputPin = errors.New("machine: invalid output pin") - ErrInvalidClockPin = errors.New("machine: invalid clock pin") - ErrInvalidDataPin = errors.New("machine: invalid data pin") - ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt") + ErrTimeoutRNG = errors.New("machine: RNG Timeout") + ErrInvalidInputPin = errors.New("machine: invalid input pin") + ErrInvalidOutputPin = errors.New("machine: invalid output pin") + ErrInvalidClockPin = errors.New("machine: invalid clock pin") + ErrInvalidDataPin = errors.New("machine: invalid data pin") + ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt") + ErrFeatureNotImplemented = errors.New("machine: this feature is not yet implemented") ) // Device is the running program's chip name, such as "ATSAMD51J19A" or diff --git a/src/machine/machine_atmega328p.go b/src/machine/machine_atmega328p.go index a7d5d0c20f..5005636450 100644 --- a/src/machine/machine_atmega328p.go +++ b/src/machine/machine_atmega328p.go @@ -5,10 +5,15 @@ package machine import ( "device/avr" + "errors" "runtime/interrupt" "runtime/volatile" ) +var ( + ErrCallbackAlreadyDefined = errors.New("machine: callback already defined") +) + const irq_USART0_RX = avr.IRQ_USART_RX // getPortMask returns the PORTx register and mask for the pin. @@ -467,3 +472,71 @@ var SPI0 = SPI{ sdo: PB3, sdi: PB4, cs: PB2} + +// Pin Change Interrupts +var ( + pinCallbacks [3][8]func(Pin) +) + +func handlePCINTInterrupts(intr uint8, port *volatile.Register8) { + current := port.Get() + for i := uint8(0); i < 8; i++ { + if (current>>i)&0x01 == 0x01 && pinCallbacks[intr][i] != nil { + pinCallbacks[intr][i](Pin(i * (intr + 1))) + } + } +} + +func handlePCINT0Interrupts(intr interrupt.Interrupt) { + handlePCINTInterrupts(avr.IRQ_PCINT0-avr.IRQ_PCINT0, avr.PINB) +} + +func handlePCINT1Interrupts(intr interrupt.Interrupt) { + handlePCINTInterrupts(avr.IRQ_PCINT1-avr.IRQ_PCINT0, avr.PINC) +} + +func handlePCINT2Interrupts(intr interrupt.Interrupt) { + handlePCINTInterrupts(avr.IRQ_PCINT2-avr.IRQ_PCINT0, avr.PIND) +} + +type PinChange uint8 + +// Pin change interrupt constants for SetInterrupt. +const ( + PinToggle PinChange = 1 +) + +func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) { + if callback == nil { + // Disable this pin interrupt + return ErrFeatureNotImplemented + } + + switch { + case pin >= PB0 && pin <= PB7: + // PCMSK0 - PCINT0-7 + pinIndex := pin - PD0 + pinCallbacks[0][pinIndex] = callback + avr.PCMSK0.SetBits(1 << pinIndex) + avr.PCICR.SetBits(avr.PCICR_PCIE0) + interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts) + case pin >= PC0 && pin <= PC7: + // PCMSK1 - PCINT8-14 + pinIndex := pin - PC0 + pinCallbacks[1][pinIndex] = callback + avr.PCMSK1.SetBits(1 << pinIndex) + avr.PCICR.SetBits(avr.PCICR_PCIE1) + interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts) + case pin >= PD0 && pin <= PD7: + // PCMSK2 - PCINT16-23 + pinIndex := pin - PD0 + pinCallbacks[2][pinIndex] = callback + avr.PCMSK2.SetBits(1 << pinIndex) + avr.PCICR.SetBits(avr.PCICR_PCIE2) + interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts) + default: + return ErrInvalidInputPin + } + + return nil +}