Skip to content

Commit

Permalink
stm32: support pin input interrupts
Browse files Browse the repository at this point in the history
  • Loading branch information
kenbell authored and deadprogram committed Jun 11, 2021
1 parent c017ed2 commit 5f9e339
Show file tree
Hide file tree
Showing 16 changed files with 527 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/examples/pininterrupt/stm32.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// +build stm32

package main

import "machine"

const (
buttonMode = machine.PinInputPulldown
buttonPinChange = machine.PinRising | machine.PinFalling
)
6 changes: 6 additions & 0 deletions src/machine/board_bluepill.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ const (
LED = PC13
)

const (
// This board does not have a user button, so
// use first GPIO pin by default
BUTTON = PA0
)

var Serial = UART1

// UART pins
Expand Down
6 changes: 6 additions & 0 deletions src/machine/board_nucleol031k6.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const (
LED_GREEN = PB3
)

const (
// This board does not have a user button, so
// use first GPIO pin by default
BUTTON = PA0
)

const (
// Arduino Pins
A0 = PA0 // ADC_IN0
Expand Down
6 changes: 6 additions & 0 deletions src/machine/board_nucleol432kc.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const (
LED_GREEN = PB3
)

const (
// This board does not have a user button, so
// use first GPIO pin by default
BUTTON = PA0
)

const (
// Arduino Pins
A0 = PA0
Expand Down
4 changes: 4 additions & 0 deletions src/machine/board_stm32f4disco.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const (
LED_BLUE = PD15
)

const (
BUTTON = PA0
)

// UART pins
const (
UART_TX_PIN = PA2
Expand Down
7 changes: 7 additions & 0 deletions src/machine/machine_stm32.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ const (
// Also, the stm32f1xx series handles things differently from the stm32f0/2/3/4

// ---------- General pin operations ----------
type PinChange uint8

const (
PinRising PinChange = 1 << iota
PinFalling
PinToggle = PinRising | PinFalling
)

// Set the pin to high or low.
// Warning: only use this on an output pin!
Expand Down
27 changes: 27 additions & 0 deletions src/machine/machine_stm32_exti_afio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// +build stm32f1

package machine

import (
"device/stm32"
"runtime/volatile"
)

func getEXTIConfigRegister(pin uint8) *volatile.Register32 {
switch (pin & 0xf) / 4 {
case 0:
return &stm32.AFIO.EXTICR1
case 1:
return &stm32.AFIO.EXTICR2
case 2:
return &stm32.AFIO.EXTICR3
case 3:
return &stm32.AFIO.EXTICR4
}
return nil
}

func enableEXTIConfigRegisters() {
// Enable AFIO
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN)
}
26 changes: 26 additions & 0 deletions src/machine/machine_stm32_exti_exti.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// +build stm32l5

package machine

import (
"device/stm32"
"runtime/volatile"
)

func getEXTIConfigRegister(pin uint8) *volatile.Register32 {
switch (pin & 0xf) / 4 {
case 0:
return &stm32.EXTI.EXTICR1
case 1:
return &stm32.EXTI.EXTICR2
case 2:
return &stm32.EXTI.EXTICR3
case 3:
return &stm32.EXTI.EXTICR4
}
return nil
}

func enableEXTIConfigRegisters() {
// No-op
}
27 changes: 27 additions & 0 deletions src/machine/machine_stm32_exti_syscfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// +build stm32,!stm32f1,!stm32l5

package machine

import (
"device/stm32"
"runtime/volatile"
)

func getEXTIConfigRegister(pin uint8) *volatile.Register32 {
switch (pin & 0xf) / 4 {
case 0:
return &stm32.SYSCFG.EXTICR1
case 1:
return &stm32.SYSCFG.EXTICR2
case 2:
return &stm32.SYSCFG.EXTICR3
case 3:
return &stm32.SYSCFG.EXTICR4
}
return nil
}

func enableEXTIConfigRegisters() {
// Enable SYSCFG
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
}
92 changes: 92 additions & 0 deletions src/machine/machine_stm32_gpio_reva.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// +build stm32,!stm32l4,!stm32l5

package machine

import (
"device/stm32"
)

// This variant of the GPIO input interrupt logic is for
// chips with a smaller number of interrupt channels
// (that fits in a single register).

//
// STM32 allows one interrupt source per pin number, with
// the same pin number in different ports sharing a single
// interrupt source (so PA0, PB0, PC0 all share). Only a
// single physical pin can be connected to each interrupt
// line.
//
// To call interrupt callbacks, we record here for each
// pin number the callback and the actual associated pin.
//

// Callbacks for pin interrupt events
var pinCallbacks [16]func(Pin)

// The pin currently associated with interrupt callback
// for a given slot.
var interruptPins [16]Pin

// 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 {
port := uint32(uint8(p) / 16)
pin := uint8(p) % 16

enableEXTIConfigRegisters()

if callback == nil {
stm32.EXTI.IMR.ClearBits(1 << pin)
pinCallbacks[pin] = nil
return nil
}

if pinCallbacks[pin] != nil {
// The pin was already configured.
// To properly re-configure a pin, unset it first and set a new
// configuration.
return ErrNoPinChangeChannel
}

// Set the callback now (before the interrupt is enabled) to avoid
// possible race condition
pinCallbacks[pin] = callback
interruptPins[pin] = p

crReg := getEXTIConfigRegister(pin)
shift := (pin & 0x3) * 4
crReg.ReplaceBits(port, 0xf, shift)

if (change & PinRising) != 0 {
stm32.EXTI.RTSR.SetBits(1 << pin)
}
if (change & PinFalling) != 0 {
stm32.EXTI.FTSR.SetBits(1 << pin)
}
stm32.EXTI.IMR.SetBits(1 << pin)

intr := p.registerInterrupt()
intr.SetPriority(0)
intr.Enable()

return nil
}

func handlePinInterrupt(pin uint8) {
if stm32.EXTI.PR.HasBits(1 << pin) {
// Writing 1 to the pending register clears the
// pending flag for that bit
stm32.EXTI.PR.Set(1 << pin)

callback := pinCallbacks[pin]
if callback != nil {
callback(interruptPins[pin])
}
}
}
79 changes: 79 additions & 0 deletions src/machine/machine_stm32_gpio_revb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// +build stm32l4 stm32l5

package machine

import (
"device/stm32"
)

// This variant of the GPIO input interrupt logic is for
// chips with a larger number of interrupt channels (more
// than fits in a single register).

//
// STM32 allows one interrupt source per pin number, with
// the same pin number in different ports sharing a single
// interrupt source (so PA0, PB0, PC0 all share). Only a
// single physical pin can be connected to each interrupt
// line.
//
// To call interrupt callbacks, we record here for each
// pin number the callback and the actual associated pin.
//

// Callbacks for pin interrupt events
var pinCallbacks [16]func(Pin)

// The pin currently associated with interrupt callback
// for a given slot.
var interruptPins [16]Pin

// 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 {
port := uint32(uint8(p) / 16)
pin := uint8(p) % 16

enableEXTIConfigRegisters()

if callback == nil {
stm32.EXTI.IMR1.ClearBits(1 << pin)
pinCallbacks[pin] = nil
return nil
}

if pinCallbacks[pin] != nil {
// The pin was already configured.
// To properly re-configure a pin, unset it first and set a new
// configuration.
return ErrNoPinChangeChannel
}

// Set the callback now (before the interrupt is enabled) to avoid
// possible race condition
pinCallbacks[pin] = callback
interruptPins[pin] = p

crReg := getEXTIConfigRegister(pin)
shift := (pin & 0x3) * 4
crReg.ReplaceBits(port, 0xf, shift)

if (change & PinRising) != 0 {
stm32.EXTI.RTSR1.SetBits(1 << pin)
}
if (change & PinFalling) != 0 {
stm32.EXTI.FTSR1.SetBits(1 << pin)
}
stm32.EXTI.IMR1.SetBits(1 << pin)

intr := p.registerInterrupt()
intr.SetPriority(0)
intr.Enable()

return nil
}
41 changes: 41 additions & 0 deletions src/machine/machine_stm32f4.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,47 @@ func (p Pin) enableClock() {
}
}

func (p Pin) registerInterrupt() interrupt.Interrupt {
pin := uint8(p) % 16

switch pin {
case 0:
return interrupt.New(stm32.IRQ_EXTI0, func(interrupt.Interrupt) { handlePinInterrupt(0) })
case 1:
return interrupt.New(stm32.IRQ_EXTI1, func(interrupt.Interrupt) { handlePinInterrupt(1) })
case 2:
return interrupt.New(stm32.IRQ_EXTI2, func(interrupt.Interrupt) { handlePinInterrupt(2) })
case 3:
return interrupt.New(stm32.IRQ_EXTI3, func(interrupt.Interrupt) { handlePinInterrupt(3) })
case 4:
return interrupt.New(stm32.IRQ_EXTI4, func(interrupt.Interrupt) { handlePinInterrupt(4) })
case 5:
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(5) })
case 6:
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(6) })
case 7:
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(7) })
case 8:
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(8) })
case 9:
return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(9) })
case 10:
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(10) })
case 11:
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(11) })
case 12:
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(12) })
case 13:
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(13) })
case 14:
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(14) })
case 15:
return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(15) })
}

return interrupt.Interrupt{}
}

// Enable peripheral clock
func enableAltFuncClock(bus unsafe.Pointer) {
switch bus {
Expand Down
Loading

0 comments on commit 5f9e339

Please sign in to comment.