nrf: add support for pin change interrupts
Этот коммит содержится в:
родитель
c248418dbe
коммит
19c7965fc5
5 изменённых файлов: 140 добавлений и 4 удалений
2
Makefile
2
Makefile
|
@ -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
Обычный файл
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
Обычный файл
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
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче