stm32: add minimal stm32wlex5 / lorae5 target

credit to ofauchon
Этот коммит содержится в:
Kenneth Bell 2021-11-11 10:56:05 +00:00 коммит произвёл Ron Evans
родитель 339301b709
коммит 62d4a6a77f
17 изменённых файлов: 582 добавлений и 6 удалений

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

@ -430,6 +430,8 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=lorae5 examples/blinky1
@$(MD5SUM) test.hex
endif
ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial

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

@ -107,6 +107,7 @@ The following 72 microcontroller boards are currently supported:
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)

@ -1 +1 @@
Subproject commit 3a0b0829b70bf930c74fb69d887bee28005e7d99
Subproject commit 5f417214efe79598eadd38f68b4432384ca57456

54
src/machine/board_lorae5.go Обычный файл
Просмотреть файл

@ -0,0 +1,54 @@
//go:build lorae5
// +build lorae5
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
LED1 = PB5
LED = LED1 // Default LED
)
// SubGhz (SPI3)
const (
SPI0_NSS_PIN = PA4
SPI0_SCK_PIN = PA5
SPI0_SDO_PIN = PA6
SPI0_SDI_PIN = PA7
)
// UARTS
const (
//MCU USART1
UART1_TX_PIN = PB6
UART1_RX_PIN = PB7
//MCU USART2
UART2_TX_PIN = PA2
UART2_RX_PIN = PA3
// DEFAULT USART
UART_TX_PIN = UART1_TX_PIN
UART_RX_PIN = UART1_RX_PIN
)
var (
// Console UART
UART0 = &_UART0
_UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
TxAltFuncSelector: AF7_USART1_2,
RxAltFuncSelector: AF7_USART1_2,
}
DefaultUART = UART0
)
func init() {
// Enable UARTs Interrupts
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART0.handleInterrupt)
}

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

@ -1,4 +1,5 @@
// +build atmega nrf sam stm32 fe310 k210 rp2040
//go:build atmega || nrf || sam || (stm32 && !stm32wle5) || fe310 || k210 || rp2040
// +build atmega nrf sam stm32,!stm32wle5 fe310 k210 rp2040
package machine

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

@ -1,4 +1,5 @@
// +build stm32,!stm32f1,!stm32l5
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wle5
// +build stm32,!stm32f1,!stm32l5,!stm32wle5
package machine

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

@ -0,0 +1,27 @@
//go:build stm32wle5
// +build stm32wle5
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() {
// No registers to enable
}

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

@ -1,4 +1,5 @@
// +build stm32,!stm32l4,!stm32l5
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wle5
// +build stm32,!stm32l4,!stm32l5,!stm32wle5
package machine

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

@ -1,3 +1,4 @@
//go:build stm32l4 || stm32l5
// +build stm32l4 stm32l5
package machine

87
src/machine/machine_stm32_gpio_revb_mp.go Обычный файл
Просмотреть файл

@ -0,0 +1,87 @@
//go:build stm32wle5
// +build stm32wle5
package machine
import (
"device/stm32"
)
//
// This variant of the GPIO input interrupt logic is for
// multi-core chips with a larger number of interrupt
// channels (more than fits in a single register).
//
// This logic is currently used by the single-core stm32wle5
// due to a patch in stm32-rs project that has renamed the
// registers to match the dual-core names. This renaming is
// being discussed and may change in future.
//
//
// 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.C1IMR1.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.C1IMR1.SetBits(1 << pin)
intr := p.registerInterrupt()
intr.SetPriority(0)
intr.Enable()
return nil
}

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

@ -1,4 +1,5 @@
// +build stm32,!stm32f7x2,!stm32l5x2
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32wle5
// +build stm32,!stm32f7x2,!stm32l5x2,!stm32wle5
package machine

343
src/machine/machine_stm32wle5.go Обычный файл
Просмотреть файл

@ -0,0 +1,343 @@
//go:build stm32wle5
// +build stm32wle5
package machine
// Peripheral abstraction layer for the stm32wle5
import (
"device/stm32"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
const (
AF0_SYSTEM = 0
AF1_TIM1_2_LPTIM1 = 1
AF2_TIM1_2 = 2
AF3_SPIS2_TIM1_LPTIM3 = 3
AF4_I2C1_2_3 = 4
AF5_SPI1_SPI2S2 = 5
AF6_RF = 6
AF7_USART1_2 = 7
AF8_LPUART1 = 8
AF12_COMP1_2_TIM1 = 12
AF13_DEBUG = 13
AF14_TIM2_16_17_LPTIM2 = 14
AF15_EVENTOUT = 15
)
func CPUFrequency() uint32 {
return 4e6
}
const APB1_TIM_FREQ = 4e6
const APB2_TIM_FREQ = 4e6
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PA8 = portA + 8
PA9 = portA + 9
PA10 = portA + 10
PA11 = portA + 11
PA12 = portA + 12
PA13 = portA + 13
PA14 = portA + 14
PA15 = portA + 15
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PB8 = portB + 8
PB9 = portB + 9
PB10 = portB + 10
PB11 = portB + 11
PB12 = portB + 12
PB13 = portB + 13
PB14 = portB + 14
PB15 = portB + 15
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PC8 = portC + 8
PC9 = portC + 9
PC10 = portC + 10
PC11 = portC + 11
PC12 = portC + 12
PC13 = portC + 13
PC14 = portC + 14
PC15 = portC + 15
PH3 = portH + 3
)
func (p Pin) getPort() *stm32.GPIO_Type {
switch p / 16 {
case 0:
return stm32.GPIOA
case 1:
return stm32.GPIOB
case 2:
return stm32.GPIOC
case 7:
return stm32.GPIOH
default:
panic("machine: unknown port")
}
}
// enableClock enables the clock for this desired GPIO port.
func (p Pin) enableClock() {
switch p / 16 {
case 0:
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOAEN)
case 1:
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOBEN)
case 2:
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOCEN)
case 7:
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOHEN)
default:
panic("machine: unknown port")
}
}
// Enable peripheral clock
func enableAltFuncClock(bus unsafe.Pointer) {
switch bus {
// APB1ENR1
case unsafe.Pointer(stm32.LPTIM1): // LPTIM1 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_LPTIM1EN)
case unsafe.Pointer(stm32.DAC): // DAC clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_DAC1EN)
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C3EN)
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C2EN)
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C1EN)
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_USART2EN)
case unsafe.Pointer(stm32.SPI2): // SPI2S2 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_SPI2S2EN)
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_WWDGEN)
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_TIM2EN)
// APB1ENR2
case unsafe.Pointer(stm32.LPTIM3): // LPTIM3 clock enable
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPTIM3EN)
case unsafe.Pointer(stm32.LPTIM2): // LPTIM2 clock enable
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPTIM2EN)
case unsafe.Pointer(stm32.LPUART): // LPUART clock enable
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPUART1EN)
//APB2ENR
case unsafe.Pointer(stm32.TIM17): // TIM17 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM17EN)
case unsafe.Pointer(stm32.TIM16): // TIM16 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM16EN)
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
case unsafe.Pointer(stm32.ADC): // ADC clock enable
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADCEN)
}
}
func handlePinInterrupt(pin uint8) {
if stm32.EXTI.PR1.HasBits(1 << pin) {
// Writing 1 to the pending register clears the
// pending flag for that bit
stm32.EXTI.PR1.Set(1 << pin)
callback := pinCallbacks[pin]
if callback != nil {
callback(interruptPins[pin])
}
}
}
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{}
}
//---------- UART related code
// Configure the UART.
func (uart UART) configurePins(config UARTConfig) {
// enable the alternate functions on the TX and RX pins
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
}
// UART baudrate calc based on the bus and clockspeed
// NOTE: keep this in sync with the runtime/runtime_stm32wle5.go clock init code
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
var br uint32
uartClock := CPUFrequency() // No Prescaler configuration
br = uint32((uartClock + baudRate/2) / baudRate)
return (br)
}
// Register names vary by ST processor, these are for STM L5
func (uart *UART) setRegisters() {
uart.rxReg = &uart.Bus.RDR
uart.txReg = &uart.Bus.TDR
uart.statusReg = &uart.Bus.ISR
uart.txEmptyFlag = stm32.USART_ISR_TXFNF //(TXFNF == TXE == bit 7, but depends alternate RM0461/1094)
}
//---------- Timer related code
var (
TIM1 = TIM{
EnableRegister: &stm32.RCC.APB2ENR,
EnableFlag: stm32.RCC_APB2ENR_TIM1EN,
Device: stm32.TIM1,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PA8, AF1_TIM1_2_LPTIM1}}},
TimerChannel{Pins: []PinFunction{{PA9, AF1_TIM1_2_LPTIM1}}},
TimerChannel{Pins: []PinFunction{{PA10, AF1_TIM1_2_LPTIM1}}},
TimerChannel{Pins: []PinFunction{{PA11, AF1_TIM1_2_LPTIM1}}},
},
busFreq: APB2_TIM_FREQ,
}
TIM2 = TIM{
EnableRegister: &stm32.RCC.APB1ENR1,
EnableFlag: stm32.RCC_APB1ENR1_TIM2EN,
Device: stm32.TIM2,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PA0, AF1_TIM1_2_LPTIM1}, {PA5, AF1_TIM1_2_LPTIM1}, {PA15, AF1_TIM1_2_LPTIM1}}},
TimerChannel{Pins: []PinFunction{{PA1, AF1_TIM1_2_LPTIM1}, {PB3, AF1_TIM1_2_LPTIM1}}},
TimerChannel{Pins: []PinFunction{{PA2, AF1_TIM1_2_LPTIM1}, {PB10, AF1_TIM1_2_LPTIM1}}},
TimerChannel{Pins: []PinFunction{{PA3, AF1_TIM1_2_LPTIM1}, {PB11, AF1_TIM1_2_LPTIM1}}},
},
busFreq: APB1_TIM_FREQ,
}
TIM16 = TIM{
EnableRegister: &stm32.RCC.APB2ENR,
EnableFlag: stm32.RCC_APB2ENR_TIM16EN,
Device: stm32.TIM16,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PA6, AF14_TIM2_16_17_LPTIM2}}},
TimerChannel{Pins: []PinFunction{}},
TimerChannel{Pins: []PinFunction{}},
TimerChannel{Pins: []PinFunction{}},
},
busFreq: APB2_TIM_FREQ,
}
TIM17 = TIM{
EnableRegister: &stm32.RCC.APB2ENR,
EnableFlag: stm32.RCC_APB2ENR_TIM17EN,
Device: stm32.TIM17,
Channels: [4]TimerChannel{
TimerChannel{Pins: []PinFunction{{PA7, AF1_TIM1_2_LPTIM1}, {PB9, AF1_TIM1_2_LPTIM1}}},
TimerChannel{Pins: []PinFunction{}},
TimerChannel{Pins: []PinFunction{}},
TimerChannel{Pins: []PinFunction{}},
},
busFreq: APB2_TIM_FREQ,
}
)
func (t *TIM) registerUPInterrupt() interrupt.Interrupt {
switch t {
case &TIM1:
return interrupt.New(stm32.IRQ_TIM1_UP, TIM1.handleUPInterrupt)
case &TIM2:
return interrupt.New(stm32.IRQ_TIM2, TIM2.handleUPInterrupt)
case &TIM16:
return interrupt.New(stm32.IRQ_TIM16, TIM16.handleUPInterrupt)
case &TIM17:
return interrupt.New(stm32.IRQ_TIM17, TIM17.handleUPInterrupt)
}
return interrupt.Interrupt{}
}
func (t *TIM) registerOCInterrupt() interrupt.Interrupt {
switch t {
case &TIM1:
return interrupt.New(stm32.IRQ_TIM1_CC, TIM1.handleOCInterrupt)
case &TIM2:
return interrupt.New(stm32.IRQ_TIM2, TIM2.handleOCInterrupt)
case &TIM16:
return interrupt.New(stm32.IRQ_TIM16, TIM16.handleOCInterrupt)
case &TIM17:
return interrupt.New(stm32.IRQ_TIM17, TIM17.handleOCInterrupt)
}
return interrupt.Interrupt{}
}
func (t *TIM) enableMainOutput() {
t.Device.BDTR.SetBits(stm32.TIM_BDTR_MOE)
}
type arrtype = uint32
type arrRegType = volatile.Register32
const (
ARR_MAX = 0x10000
PSC_MAX = 0x10000
)

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

@ -1,4 +1,5 @@
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega
//go:build !baremetal || (stm32 && !stm32f7x2 && !stm32l5x2 && !stm32wle5) || fe310 || k210 || atmega
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2,!stm32wle5 fe310 k210 atmega
package machine

26
src/runtime/runtime_stm32wle5.go Обычный файл
Просмотреть файл

@ -0,0 +1,26 @@
//go:build stm32wle5
// +build stm32wle5
package runtime
import (
"machine"
)
type arrtype = uint32
func init() {
// Currently the clock is not configured, which means
// the MCU runs at default reset clock speed (4Mhz).
// Code to initialize RCC and PLL can go here.
// UART init
machine.Serial.Configure(machine.UARTConfig{})
// Timers init
initTickTimer(&machine.TIM1)
}
func putchar(c byte) {
machine.Serial.WriteByte(c)
}

12
targets/lorae5.json Обычный файл
Просмотреть файл

@ -0,0 +1,12 @@
{
"inherits": [
"stm32wle5"
],
"build-tags": [
"lorae5"
],
"serial": "uart",
"flash-method": "openocd",
"openocd-interface": "stlink-v2",
"openocd-target": "stm32wlx"
}

8
targets/stm32wle5.json Обычный файл
Просмотреть файл

@ -0,0 +1,8 @@
{
"inherits": ["cortex-m4"],
"build-tags": ["stm32wle5", "stm32"],
"extra-files": [
"src/device/stm32/stm32wle5.s"
],
"linkerscript": "targets/stm32wle5.ld"
}

10
targets/stm32wle5.ld Обычный файл
Просмотреть файл

@ -0,0 +1,10 @@
MEMORY
{
FLASH_TEXT (rw) : ORIGIN = 0x08000000, LENGTH = 256K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}
_stack_size = 4K;
INCLUDE "targets/arm.ld"