nrf: add support for pin change interrupts

Этот коммит содержится в:
Ayke van Laethem 2020-01-13 16:21:16 +01:00 коммит произвёл Ron Evans
родитель c248418dbe
коммит 19c7965fc5
5 изменённых файлов: 140 добавлений и 4 удалений

Просмотреть файл

@ -203,6 +203,8 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink $(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial $(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick $(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick

10
src/examples/pininterrupt/pca10040.go Обычный файл
Просмотреть файл

@ -0,0 +1,10 @@
// +build pca10040
package main
import "machine"
const (
buttonMode = machine.PinInputPullup
buttonPinChange = machine.PinRising
)

52
src/examples/pininterrupt/pininterrupt.go Обычный файл
Просмотреть файл

@ -0,0 +1,52 @@
package main
// This example demonstrates how to use pin change interrupts.
//
// This is only an example and should not be copied directly in any serious
// circuit, because it lacks an important feature: debouncing.
// See: https://en.wikipedia.org/wiki/Switch#Contact_bounce
import (
"machine"
"runtime/volatile"
"time"
)
const (
button = machine.BUTTON
led = machine.LED
)
func main() {
var lightLed volatile.Register8
lightLed.Set(0)
// Configure the LED, defaulting to on (usually setting the pin to low will
// turn the LED on).
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
led.Low()
// Make sure the pin is configured as a pullup to avoid floating inputs.
// Pullup works for most buttons, as most buttons short to ground when
// pressed.
button.Configure(machine.PinConfig{Mode: buttonMode})
// Set an interrupt on this pin.
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
if lightLed.Get() != 0 {
lightLed.Set(0)
led.Low()
} else {
lightLed.Set(1)
led.High()
}
})
if err != nil {
println("could not configure pin interrupt:", err.Error())
}
// Make sure the program won't exit.
for {
time.Sleep(time.Hour)
}
}

Просмотреть файл

@ -7,6 +7,7 @@ var (
ErrInvalidOutputPin = errors.New("machine: invalid output pin") ErrInvalidOutputPin = errors.New("machine: invalid output pin")
ErrInvalidClockPin = errors.New("machine: invalid clock pin") ErrInvalidClockPin = errors.New("machine: invalid clock pin")
ErrInvalidDataPin = errors.New("machine: invalid data pin") ErrInvalidDataPin = errors.New("machine: invalid data pin")
ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt")
) )
type PinConfig struct { type PinConfig struct {

Просмотреть файл

@ -21,6 +21,18 @@ const (
PinOutput PinMode = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos) PinOutput PinMode = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
) )
type PinChange uint8
// Pin change interrupt constants for SetInterrupt.
const (
PinRising PinChange = nrf.GPIOTE_CONFIG_POLARITY_LoToHi
PinFalling PinChange = nrf.GPIOTE_CONFIG_POLARITY_HiToLo
PinToggle PinChange = nrf.GPIOTE_CONFIG_POLARITY_Toggle
)
// Callbacks to be called for pins configured with SetInterrupt.
var pinCallbacks [len(nrf.GPIOTE.CONFIG)]func(Pin)
// Configure this pin with the given configuration. // Configure this pin with the given configuration.
func (p Pin) Configure(config PinConfig) { func (p Pin) Configure(config PinConfig) {
cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled
@ -59,6 +71,65 @@ func (p Pin) Get() bool {
return (port.IN.Get()>>pin)&1 != 0 return (port.IN.Get()>>pin)&1 != 0
} }
// 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 {
// Some variables to easily check whether a channel was already configured
// as an event channel for the given pin.
// This is not just an optimization, this is requred: the datasheet says
// that configuring more than one channel for a given pin results in
// unpredictable behavior.
expectedConfigMask := uint32(nrf.GPIOTE_CONFIG_MODE_Msk | nrf.GPIOTE_CONFIG_PSEL_Msk)
expectedConfig := nrf.GPIOTE_CONFIG_MODE_Event<<nrf.GPIOTE_CONFIG_MODE_Pos | uint32(p)<<nrf.GPIOTE_CONFIG_PSEL_Pos
foundChannel := false
for i := range nrf.GPIOTE.CONFIG {
config := nrf.GPIOTE.CONFIG[i].Get()
if config == 0 || config&expectedConfigMask == expectedConfig {
// Found an empty GPIOTE channel or one that was already configured
// for this pin.
if callback == nil {
// Disable this channel.
nrf.GPIOTE.INTENCLR.Set(uint32(1 << uint(i)))
pinCallbacks[i] = nil
return nil
}
// Enable this channel with the given callback.
nrf.GPIOTE.INTENCLR.Set(uint32(1 << uint(i)))
nrf.GPIOTE.CONFIG[i].Set(nrf.GPIOTE_CONFIG_MODE_Event<<nrf.GPIOTE_CONFIG_MODE_Pos |
uint32(p)<<nrf.GPIOTE_CONFIG_PSEL_Pos |
uint32(change)<<nrf.GPIOTE_CONFIG_POLARITY_Pos)
pinCallbacks[i] = callback
nrf.GPIOTE.INTENSET.Set(uint32(1 << uint(i)))
foundChannel = true
break
}
}
if !foundChannel {
return ErrNoPinChangeChannel
}
// Set and enable the GPIOTE interrupt. It's not a problem if this happens
// more than once.
interrupt.New(nrf.IRQ_GPIOTE, func(interrupt.Interrupt) {
for i := range nrf.GPIOTE.EVENTS_IN {
if nrf.GPIOTE.EVENTS_IN[i].Get() != 0 {
nrf.GPIOTE.EVENTS_IN[i].Set(0)
pin := Pin((nrf.GPIOTE.CONFIG[i].Get() & nrf.GPIOTE_CONFIG_PSEL_Msk) >> nrf.GPIOTE_CONFIG_PSEL_Pos)
pinCallbacks[i](pin)
}
}
}).Enable()
// Everything was configured correctly.
return nil
}
// UART on the NRF. // UART on the NRF.
type UART struct { type UART struct {
Buffer *RingBuffer Buffer *RingBuffer