mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-10 06:29:32 +00:00
Puya PY32F MCU support (#5106)
* pinout yamls removed * machine: implement default UART pin configuration for Embedfire boards * flash command moved to chip level so VS Code plugin can select bare chip * machine: add support for alternate pin mode configuration * add build targets for embedfire on py32 * add support for embedfire target in GNUmakefile for py32 * lib/py32-svd: remove duplicate DBGMCU.IDCODE CODE field Update submodule to fix duplicate SetIDCODE/GetIDCODE method declarations in the generated py32f002bxx.go device file. * update subproject commit reference in py32-svd * refactor: update CPU frequency handling in machine and runtime packages * refactor: replace ConfigureUARTPin function with direct pin configuration in UART setup * SetAltFunc documentation for GPIO pin alternate functions * refactor: improve UART write and flush error handling with timeout * machine/py32: generalize UART driver to any USART Add a per-instance setup func to the UART type so the driver is no longer hardwired to USART1. DefaultUART stays USART1; add UART2 (USART2) in a separately build-tagged file since py32f002x parts lack USART2. RX IRQ handlers reference runtime-assigned vars to break the init cycle, and setup is assigned in the var initializer so it runs before InitSerial. Add the py32f003_32k_4k target (32K flash / 4K RAM). * Add canonical PY32F002, F003, and F030 density targets Use CMSIS/pyocd device names for target files and build tags. Add all F003 and F030 densities plus F002A/B, correct F002B to 24K flash and its own startup file, fix the F030x8 pyocd target, and update Embedfire inheritance. * Add targets for all PY32 CMSIS devices * Support all PY32 register layout variants * machine/py32: use generated register definitions * machine/py32: fix alternate-function documentation * machine/py32: report the configured CPU frequency * machine/py32: use unsafe.Add for GPIO ports * machine/py32: bound and yield UART polling * machine/py32: configure UART pins from UARTConfig * build: use the current PY32 SVD repository URL * test: cover PY32 UART register layouts * machine/py32: restore dynamic CPU frequency tracking * machine/py32: share the USART TX-ready bit * targets/py32: scale system stacks with RAM * machine/py32: keep clock state internal * targets/py32: normalize generated metadata * machine/py32: normalize GPIO configuration * machine/py32: fix T020 UART setup * machine/py32: reduce clock variant files * machine/py32: clean up UART variants * machine/py32: decouple UART clock capability * lib/py32-svd: use organization repository * runtime/py32: name 24MHz HSI encodings * machine/py32: derive startup clock from capability * machine/py32: use generated USART clock mask * machine/py32: name T020 8-bit UART mode * ci: include PY32 in sharded smoke tests * runtime: remove PY32 HSI frequency workaround
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
//go:build embedfire_py32f002b
|
||||
|
||||
// Pin mappings for the Embedfire PY32F002B board.
|
||||
|
||||
package machine
|
||||
|
||||
// LEDs
|
||||
const (
|
||||
LED1 = PA1
|
||||
LED2 = PA5
|
||||
LED3 = PA4
|
||||
LED = LED2
|
||||
)
|
||||
|
||||
// Buttons
|
||||
const (
|
||||
KEY1 = PA3
|
||||
KEY2 = PA0
|
||||
)
|
||||
|
||||
const (
|
||||
UART_TX_PIN = PA6
|
||||
UART_RX_PIN = PA7
|
||||
)
|
||||
|
||||
func defaultUARTPins() (Pin, Pin) {
|
||||
return UART_TX_PIN, UART_RX_PIN
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//go:build embedfire_py32f030
|
||||
|
||||
// Pin mappings for the Embedfire PY32F030 board.
|
||||
|
||||
package machine
|
||||
|
||||
// LEDs
|
||||
const (
|
||||
LED1 = PA2
|
||||
LED2 = PA3
|
||||
LED3 = PA4
|
||||
LED = LED2
|
||||
)
|
||||
|
||||
// Buttons
|
||||
const (
|
||||
KEY1 = PA5
|
||||
KEY2 = PA6
|
||||
)
|
||||
|
||||
const (
|
||||
UART_TX_PIN = PA7
|
||||
UART_RX_PIN = PA8
|
||||
)
|
||||
|
||||
func defaultUARTPins() (Pin, Pin) {
|
||||
return UART_TX_PIN, UART_RX_PIN
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:build py32
|
||||
|
||||
package machine
|
||||
|
||||
var cpuFrequencyHz uint32 = defaultCPUFrequencyHz
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
return cpuFrequencyHz
|
||||
}
|
||||
|
||||
func setCPUFrequency(frequency uint32) {
|
||||
cpuFrequencyHz = frequency
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build py32 && !py32_no_hsi_fs
|
||||
|
||||
package machine
|
||||
|
||||
const defaultCPUFrequencyHz = 24_000_000
|
||||
@@ -0,0 +1,5 @@
|
||||
//go:build py32 && py32_no_hsi_fs
|
||||
|
||||
package machine
|
||||
|
||||
const defaultCPUFrequencyHz = 8_000_000
|
||||
@@ -0,0 +1,226 @@
|
||||
//go:build py32
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/py32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const deviceName = py32.Device
|
||||
|
||||
// Peripheral port offsets.
|
||||
// Keep the same spacing used on other MCUs so helpers like Pin.getPortNumber
|
||||
// can keep using simple division by 16.
|
||||
const (
|
||||
portA Pin = iota * 16
|
||||
portB
|
||||
portC
|
||||
portD
|
||||
portE
|
||||
portF
|
||||
)
|
||||
|
||||
const (
|
||||
PA0 Pin = portA + iota
|
||||
PA1
|
||||
PA2
|
||||
PA3
|
||||
PA4
|
||||
PA5
|
||||
PA6
|
||||
PA7
|
||||
PA8
|
||||
PA9
|
||||
PA10
|
||||
PA11
|
||||
PA12
|
||||
PA13
|
||||
PA14
|
||||
PA15
|
||||
)
|
||||
|
||||
const (
|
||||
PB0 Pin = portB + iota
|
||||
PB1
|
||||
PB2
|
||||
PB3
|
||||
PB4
|
||||
PB5
|
||||
PB6
|
||||
PB7
|
||||
PB8
|
||||
PB9
|
||||
PB10
|
||||
PB11
|
||||
PB12
|
||||
PB13
|
||||
PB14
|
||||
PB15
|
||||
)
|
||||
|
||||
const (
|
||||
PC0 Pin = portC + iota
|
||||
PC1
|
||||
PC2
|
||||
PC3
|
||||
PC4
|
||||
PC5
|
||||
PC6
|
||||
PC7
|
||||
PC8
|
||||
PC9
|
||||
PC10
|
||||
PC11
|
||||
PC12
|
||||
PC13
|
||||
PC14
|
||||
PC15
|
||||
)
|
||||
|
||||
const (
|
||||
PD0 Pin = portD + iota
|
||||
PD1
|
||||
PD2
|
||||
PD3
|
||||
PD4
|
||||
PD5
|
||||
PD6
|
||||
PD7
|
||||
PD8
|
||||
PD9
|
||||
PD10
|
||||
PD11
|
||||
PD12
|
||||
PD13
|
||||
PD14
|
||||
PD15
|
||||
)
|
||||
|
||||
const (
|
||||
PE0 Pin = portE + iota
|
||||
PE1
|
||||
PE2
|
||||
PE3
|
||||
PE4
|
||||
PE5
|
||||
PE6
|
||||
PE7
|
||||
PE8
|
||||
PE9
|
||||
PE10
|
||||
PE11
|
||||
PE12
|
||||
PE13
|
||||
PE14
|
||||
PE15
|
||||
)
|
||||
|
||||
const (
|
||||
PF0 Pin = portF + iota
|
||||
PF1
|
||||
PF2
|
||||
PF3
|
||||
PF4
|
||||
PF5
|
||||
PF6
|
||||
PF7
|
||||
PF8
|
||||
PF9
|
||||
PF10
|
||||
PF11
|
||||
PF12
|
||||
PF13
|
||||
PF14
|
||||
PF15
|
||||
)
|
||||
|
||||
const (
|
||||
PinOutput PinMode = iota
|
||||
PinInput
|
||||
PinInputPulldown
|
||||
PinInputPullup
|
||||
PinInputAnalog
|
||||
PinAlternate
|
||||
)
|
||||
|
||||
// These unshifted encodings are shared by the supported PY32 parts. Register
|
||||
// masks come from the generated device definitions.
|
||||
const (
|
||||
gpioModeInput = 0
|
||||
gpioModeOutput = 1
|
||||
gpioModeAlternate = 2
|
||||
gpioModeAnalog = 3
|
||||
|
||||
gpioPullFloating = 0
|
||||
gpioPullUp = 1
|
||||
gpioPullDown = 2
|
||||
gpioPullMask = py32.GPIO_PUPDR_PUPD0_Msk
|
||||
|
||||
gpioOutputSpeedLow = 0
|
||||
gpioOutputSpeedMedium = 1
|
||||
gpioOutputSpeedHigh = 2
|
||||
gpioOutputSpeedVeryHigh = 3
|
||||
|
||||
gpioOutputTypePushPull = 0
|
||||
gpioOutputTypeMask = py32.GPIO_OTYPER_OT0_Msk
|
||||
)
|
||||
|
||||
func (p Pin) getPortNumber() uint8 {
|
||||
return uint8(p) >> 4
|
||||
}
|
||||
|
||||
func (p Pin) getPinNumber() uint8 {
|
||||
return uint8(p) & 0x0F
|
||||
}
|
||||
|
||||
func (p Pin) getPort() (*py32.GPIO_Type, uint8) {
|
||||
offset := uintptr(p.getPortNumber()) * (uintptr(unsafe.Pointer(py32.GPIOB)) - uintptr(unsafe.Pointer(py32.GPIOA)))
|
||||
return (*py32.GPIO_Type)(unsafe.Add(unsafe.Pointer(py32.GPIOA), offset)), p.getPinNumber()
|
||||
}
|
||||
|
||||
func (p Pin) Set(high bool) {
|
||||
port, pin := p.getPort()
|
||||
if high {
|
||||
port.BSRR.Set(py32.GPIO_BSRR_BS0 << pin)
|
||||
} else {
|
||||
port.BSRR.Set(py32.GPIO_BSRR_BR0 << pin)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Pin) Get() bool {
|
||||
port, pin := p.getPort()
|
||||
val := port.IDR.Get() & (py32.GPIO_IDR_ID0 << pin)
|
||||
return val > 0
|
||||
}
|
||||
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
p.enableClock()
|
||||
port, pin := p.getPort()
|
||||
pos := pin * 2
|
||||
|
||||
switch config.Mode {
|
||||
|
||||
case PinInput:
|
||||
port.MODER.ReplaceBits(gpioModeInput, gpioModeMask, pos)
|
||||
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
|
||||
case PinInputPulldown:
|
||||
port.MODER.ReplaceBits(gpioModeInput, gpioModeMask, pos)
|
||||
port.PUPDR.ReplaceBits(gpioPullDown, gpioPullMask, pos)
|
||||
case PinInputPullup:
|
||||
port.MODER.ReplaceBits(gpioModeInput, gpioModeMask, pos)
|
||||
port.PUPDR.ReplaceBits(gpioPullUp, gpioPullMask, pos)
|
||||
case PinOutput:
|
||||
port.MODER.ReplaceBits(gpioModeOutput, gpioModeMask, pos)
|
||||
port.OTYPER.ReplaceBits(gpioOutputTypePushPull, gpioOutputTypeMask, pos>>1)
|
||||
setPinOutputSpeed(port, gpioOutputSpeedHigh, pos)
|
||||
case PinInputAnalog:
|
||||
port.MODER.ReplaceBits(gpioModeAnalog, gpioModeMask, pos)
|
||||
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
|
||||
case PinAlternate:
|
||||
port.MODER.ReplaceBits(gpioModeAlternate, gpioModeMask, pos)
|
||||
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
|
||||
port.OTYPER.ReplaceBits(gpioOutputTypePushPull, gpioOutputTypeMask, pos>>1)
|
||||
setPinOutputSpeed(port, gpioOutputSpeedHigh, pos)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build py32 && !py32_no_gpio_afrh
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
// SetAltFunc selects a pin-specific alternate function.
|
||||
// Configure the pin as PinAlternate first; AF values are listed in the device
|
||||
// datasheet and vary by pin and device.
|
||||
func (p Pin) SetAltFunc(af uint8) {
|
||||
port, pin := p.getPort()
|
||||
if pin >= 8 {
|
||||
port.AFRH.ReplaceBits(uint32(af), py32.GPIO_AFRL_AFSEL0_Msk, (pin%8)*4)
|
||||
} else {
|
||||
port.AFRL.ReplaceBits(uint32(af), py32.GPIO_AFRL_AFSEL0_Msk, (pin%8)*4)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && !py32_gpio_clock_ahb2 && !py32_gpio_clock_ahb
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func (p Pin) enableClock() {
|
||||
py32.RCC.IOPENR.SetBits(py32.RCC_IOPENR_GPIOAEN << p.getPortNumber())
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && py32_gpio_clock_ahb
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func (p Pin) enableClock() {
|
||||
py32.RCC.AHBENR.SetBits(py32.RCC_AHBENR_IOPAEN << p.getPortNumber())
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && py32_gpio_clock_ahb2
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func (p Pin) enableClock() {
|
||||
py32.RCC.AHB2ENR.SetBits(py32.RCC_AHB2ENR_IOPAEN << p.getPortNumber())
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:build py32 && py32_no_gpio_afrh
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
// SetAltFunc selects a pin-specific alternate function.
|
||||
// Configure the pin as PinAlternate first; AF values are listed in the device
|
||||
// datasheet and vary by pin and device.
|
||||
func (p Pin) SetAltFunc(af uint8) {
|
||||
port, pin := p.getPort()
|
||||
port.AFRL.ReplaceBits(uint32(af), py32.GPIO_AFRL_AFSEL0_Msk, (pin%8)*4)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//go:build py32 && !py32_gpio_ospdder
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
const (
|
||||
gpioModeMask = py32.GPIO_MODER_MODE0_Msk
|
||||
gpioOutputSpeedMask = py32.GPIO_OSPEEDR_OSPEED0_Msk
|
||||
)
|
||||
|
||||
func setPinOutputSpeed(port *py32.GPIO_Type, speed uint32, pos uint8) {
|
||||
port.OSPEEDR.ReplaceBits(speed, gpioOutputSpeedMask, pos)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//go:build py32 && py32_gpio_ospdder
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
const (
|
||||
gpioModeMask = py32.GPIO_MODER_MODER0_Msk
|
||||
gpioOutputSpeedMask = py32.GPIO_OSPDDER_OSPEED0_Msk
|
||||
)
|
||||
|
||||
func setPinOutputSpeed(port *py32.GPIO_Type, speed uint32, pos uint8) {
|
||||
port.OSPDDER.ReplaceBits(speed, gpioOutputSpeedMask, pos)
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//go:build py32 && !py32_uart_type
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/py32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART implements a minimal USART driver for PY32 parts. It works with any of
|
||||
// the on-chip USART peripherals: the peripheral-specific wiring (clock gate and
|
||||
// RX interrupt) is provided by the setup function stored on the instance, which
|
||||
// is set when the instance is created (see setupUSART1 / setupUSART2).
|
||||
type UART struct {
|
||||
Bus *py32.USART_Type
|
||||
Buffer *RingBuffer
|
||||
irq interrupt.Interrupt
|
||||
setup func(*UART)
|
||||
txRetries uint32
|
||||
num uint8
|
||||
}
|
||||
|
||||
// DefaultUART is the first USART (USART1) and backs machine.Serial.
|
||||
var DefaultUART = &UART{Bus: defaultUSART(), Buffer: NewRingBuffer(), setup: setupUSART1, num: 1}
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) error {
|
||||
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
uart.txRetries = uartTXRetryBudget(config.BaudRate)
|
||||
if err := configureUARTPins(uart.num, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable the peripheral clock and hook its RX interrupt.
|
||||
uart.setup(uart)
|
||||
|
||||
// Reset control registers to a known state.
|
||||
uart.Bus.CR1.Set(0)
|
||||
uart.Bus.CR2.Set(0)
|
||||
uart.Bus.CR3.Set(0)
|
||||
|
||||
clockHz := CPUFrequency()
|
||||
|
||||
// Oversampling by 16: BRR expects fck/baud.
|
||||
divider := (clockHz + (config.BaudRate / 2)) / config.BaudRate
|
||||
uart.Bus.BRR.Set(divider)
|
||||
|
||||
// Enable transmitter, receiver, RX interrupt, and the peripheral.
|
||||
uart.Bus.CR1.Set(py32.USART_CR1_TE | py32.USART_CR1_RE | py32.USART_CR1_RXNEIE | py32.USART_CR1_UE)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupUSART1 enables the USART1 peripheral clock and installs its RX interrupt
|
||||
// handler. It records the instance in usart1RX so the handler can reach it
|
||||
// without the var initializer forming an initialization cycle.
|
||||
func setupUSART1(uart *UART) {
|
||||
usart1RX = uart
|
||||
enableUSART1Clock()
|
||||
configureUSART1Interrupt(uart)
|
||||
}
|
||||
|
||||
func handleUSART1Interrupt(interrupt.Interrupt) {
|
||||
usart1RX.Receive(uint8(readUSARTData(usart1RX.Bus)))
|
||||
}
|
||||
|
||||
// usart1RX is the UART whose RX interrupt is serviced by handleUSART1Interrupt.
|
||||
// It is set by setupUSART1 at Configure time; keeping it as a plain package var
|
||||
// (rather than referencing DefaultUART from the handler) avoids an
|
||||
// initialization cycle so DefaultUART.setup can be set in its var initializer.
|
||||
var usart1RX *UART
|
||||
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
retries := uart.txRetries
|
||||
for retries > 0 && !usartTXReady(uart.Bus) {
|
||||
uartYield()
|
||||
retries--
|
||||
}
|
||||
if retries <= 0 {
|
||||
return errUARTWriteTimeout
|
||||
}
|
||||
writeUSARTData(uart.Bus, uint32(c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {
|
||||
retries := uart.txRetries
|
||||
for retries > 0 && !usartTXComplete(uart.Bus) {
|
||||
uartYield()
|
||||
retries--
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//go:build py32 && (py32f003xx || py32f030xx || py32f040xx)
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/py32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART2 is the second USART (USART2), available on PY32 parts that provide it.
|
||||
// It shares the generic UART driver; its clock gate and interrupt are wired up
|
||||
// by setupUSART2.
|
||||
var UART2 = &UART{Bus: py32.USART2, Buffer: NewRingBuffer(), setup: setupUSART2, num: 2}
|
||||
|
||||
// setupUSART2 enables the USART2 peripheral clock and installs its RX interrupt
|
||||
// handler.
|
||||
func setupUSART2(uart *UART) {
|
||||
usart2RX = uart
|
||||
py32.RCC.APBENR1.SetBits(py32.RCC_APBENR1_USART2EN)
|
||||
uart.irq = interrupt.New(py32.IRQ_USART2, handleUSART2Interrupt)
|
||||
uart.irq.SetPriority(0xc0)
|
||||
uart.irq.Enable()
|
||||
}
|
||||
|
||||
func handleUSART2Interrupt(interrupt.Interrupt) {
|
||||
usart2RX.Receive(uint8(readUSARTData(usart2RX.Bus)))
|
||||
}
|
||||
|
||||
// usart2RX is the UART serviced by handleUSART2Interrupt, set by setupUSART2.
|
||||
var usart2RX *UART
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && !py32_uart_type && !py32_uart_clock_apb2 && !py32_usart1_clock_literal
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func enableUSART1Clock() {
|
||||
py32.RCC.APBENR2.SetBits(py32.RCC_APBENR2_USART1EN)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && !py32_uart_type && py32_uart_clock_apb2
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func enableUSART1Clock() {
|
||||
py32.RCC.APB2ENR.SetBits(py32.RCC_APB2ENR_USART1EN)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
//go:build py32 && !py32_uart_type && !py32_uart_clock_apb2 && py32_usart1_clock_literal
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func enableUSART1Clock() {
|
||||
// The F001 SVDs omit USART1EN; the vendor register layout places it at bit 14.
|
||||
py32.RCC.APBENR2.SetBits(1 << 14)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//go:build py32
|
||||
|
||||
package machine
|
||||
|
||||
import "runtime/interrupt"
|
||||
|
||||
type uartError string
|
||||
|
||||
func (e uartError) Error() string { return string(e) }
|
||||
|
||||
// errUARTWriteTimeout is returned when the transmit register stays busy.
|
||||
const errUARTWriteTimeout uartError = "UART: write timeout"
|
||||
|
||||
const (
|
||||
errUARTInvalidTXPin uartError = "UART: invalid TX pin"
|
||||
errUARTInvalidRXPin uartError = "UART: invalid RX pin"
|
||||
errUARTPinsEqual uartError = "UART: TX and RX pins must differ"
|
||||
)
|
||||
|
||||
func configureUARTPins(uartNum uint8, config UARTConfig) error {
|
||||
if config.TX == 0 && config.RX == 0 {
|
||||
config.TX, config.RX = defaultUARTPins()
|
||||
}
|
||||
if config.TX == config.RX && config.TX != NoPin {
|
||||
return errUARTPinsEqual
|
||||
}
|
||||
if config.TX != NoPin {
|
||||
af, ok := uartPinAF(uartNum, config.TX, true)
|
||||
if !ok {
|
||||
return errUARTInvalidTXPin
|
||||
}
|
||||
config.TX.Configure(PinConfig{Mode: PinAlternate})
|
||||
config.TX.SetAltFunc(af)
|
||||
}
|
||||
if config.RX != NoPin {
|
||||
af, ok := uartPinAF(uartNum, config.RX, false)
|
||||
if !ok {
|
||||
return errUARTInvalidRXPin
|
||||
}
|
||||
config.RX.Configure(PinConfig{Mode: PinAlternate})
|
||||
config.RX.SetAltFunc(af)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func uartTXRetryBudget(baudRate uint32) uint32 {
|
||||
retries := CPUFrequency() / baudRate * 10
|
||||
if retries < 10 {
|
||||
return 10
|
||||
}
|
||||
return retries
|
||||
}
|
||||
|
||||
func uartYield() {
|
||||
if !interrupt.In() {
|
||||
gosched()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//go:build py32 && !py32_uart_type && !py32_usart_split_data
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func readUSARTData(bus *py32.USART_Type) uint32 {
|
||||
return bus.DR.Get()
|
||||
}
|
||||
|
||||
func writeUSARTData(bus *py32.USART_Type, value uint32) {
|
||||
bus.DR.Set(value)
|
||||
}
|
||||
|
||||
func usartTXReady(bus *py32.USART_Type) bool {
|
||||
const txEmpty = 1 << 7 // TXE or TXE_TXFNF, depending on the family.
|
||||
return bus.SR.Get()&txEmpty != 0
|
||||
}
|
||||
|
||||
func usartTXComplete(bus *py32.USART_Type) bool {
|
||||
return bus.SR.Get()&py32.USART_SR_TC != 0
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//go:build py32 && py32_usart_split_data
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func readUSARTData(bus *py32.USART_Type) uint32 {
|
||||
return bus.RDR.Get()
|
||||
}
|
||||
|
||||
func writeUSARTData(bus *py32.USART_Type, value uint32) {
|
||||
bus.TDR.Set(value)
|
||||
}
|
||||
|
||||
func usartTXReady(bus *py32.USART_Type) bool {
|
||||
return bus.SR.Get()&py32.USART_SR_TXE_TXFNF != 0
|
||||
}
|
||||
|
||||
func usartTXComplete(bus *py32.USART_Type) bool {
|
||||
return bus.SR.Get()&py32.USART_SR_TC != 0
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//go:build py32 && !py32_uart_type && !py32_uart_no_interrupt
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/py32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
func configureUSART1Interrupt(uart *UART) {
|
||||
uart.irq = interrupt.New(py32.IRQ_USART1, handleUSART1Interrupt)
|
||||
uart.irq.SetPriority(0xc0)
|
||||
uart.irq.Enable()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//go:build py32 && !py32_default_uart_pins
|
||||
|
||||
package machine
|
||||
|
||||
func defaultUARTPins() (Pin, Pin) {
|
||||
return NoPin, NoPin
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
//go:build py32 && !py32_uart_type && py32_uart_no_interrupt
|
||||
|
||||
package machine
|
||||
|
||||
// The F410 SVD does not provide USART interrupt metadata. Keep transmit and
|
||||
// configuration support without inventing an IRQ number.
|
||||
func configureUSART1Interrupt(uart *UART) {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//go:build py32 && py32f002bxx
|
||||
|
||||
package machine
|
||||
|
||||
// PY32F002B datasheet tables 3-4 and 3-5.
|
||||
func uartPinAF(uartNum uint8, pin Pin, tx bool) (uint8, bool) {
|
||||
if uartNum != 1 {
|
||||
return 0, false
|
||||
}
|
||||
if tx {
|
||||
switch pin {
|
||||
case PA3, PA6, PA7, PB4, PB6:
|
||||
return 1, true
|
||||
}
|
||||
} else {
|
||||
switch pin {
|
||||
case PA2, PA4, PB5:
|
||||
return 1, true
|
||||
case PA7:
|
||||
return 3, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//go:build py32 && (py32f003xx || py32f030xx)
|
||||
|
||||
package machine
|
||||
|
||||
// PY32F003 datasheet tables 3-6 through 3-8 and PY32F030 datasheet
|
||||
// tables 3-1 through 3-3.
|
||||
func uartPinAF(uartNum uint8, pin Pin, tx bool) (uint8, bool) {
|
||||
switch uartNum {
|
||||
case 1:
|
||||
if tx {
|
||||
switch pin {
|
||||
case PB6, PF3:
|
||||
return 0, true
|
||||
case PA2, PA9, PA14:
|
||||
return 1, true
|
||||
case PA7, PA10, PB8, PF1:
|
||||
return 8, true
|
||||
}
|
||||
} else {
|
||||
switch pin {
|
||||
case PB2, PB7:
|
||||
return 0, true
|
||||
case PA3, PA10, PA15:
|
||||
return 1, true
|
||||
case PA8, PA9, PA13, PF0:
|
||||
return 8, true
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
if tx {
|
||||
switch pin {
|
||||
case PA2, PA9, PA14, PB6, PB8, PF1, PF3:
|
||||
return 4, true
|
||||
case PA0, PA4, PA7, PF0:
|
||||
return 9, true
|
||||
}
|
||||
} else {
|
||||
switch pin {
|
||||
case PB2:
|
||||
return 3, true
|
||||
case PA3, PA10, PA15, PB7, PF0, PF2:
|
||||
return 4, true
|
||||
case PA1, PA5, PA8, PF1:
|
||||
return 9, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//go:build py32 && !py32f002bxx && !py32f003xx && !py32f030xx
|
||||
|
||||
package machine
|
||||
|
||||
func uartPinAF(uartNum uint8, pin Pin, tx bool) (uint8, bool) {
|
||||
return 0, false
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//go:build py32 && py32_uart_type
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/py32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
type UART struct {
|
||||
Bus *py32.UART_Type
|
||||
Buffer *RingBuffer
|
||||
irq interrupt.Interrupt
|
||||
txRetries uint32
|
||||
num uint8
|
||||
}
|
||||
|
||||
var DefaultUART = &UART{Bus: py32.UART1, Buffer: NewRingBuffer(), num: 1}
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) error {
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
uart.txRetries = uartTXRetryBudget(config.BaudRate)
|
||||
if err := configureUARTPins(uart.num, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
py32.RCC.APBENR1.SetBits(py32.RCC_APBENR1_UART1EN)
|
||||
uart.Bus.CR1.Set(py32.UART_CR1_M_Char8Bits)
|
||||
uart.Bus.CR2.Set(py32.UART_CR2_RXNEIE)
|
||||
uart.Bus.CR3.Set(0)
|
||||
divider := (CPUFrequency() + config.BaudRate*8) / (config.BaudRate * 16)
|
||||
uart.Bus.BRR.Set(divider)
|
||||
|
||||
uart.irq = interrupt.New(py32.IRQ_UART1, handleUART1Interrupt)
|
||||
uart.irq.SetPriority(0xc0)
|
||||
uart.irq.Enable()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleUART1Interrupt(interrupt.Interrupt) {
|
||||
DefaultUART.Receive(uint8(DefaultUART.Bus.DR.Get()))
|
||||
}
|
||||
|
||||
func (uart *UART) writeByte(c byte) error {
|
||||
retries := uart.txRetries
|
||||
for retries > 0 && uart.Bus.SR.Get()&py32.UART_SR_TDRE == 0 {
|
||||
uartYield()
|
||||
retries--
|
||||
}
|
||||
if retries <= 0 {
|
||||
return errUARTWriteTimeout
|
||||
}
|
||||
uart.Bus.DR.Set(uint32(c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {
|
||||
retries := uart.txRetries
|
||||
for retries > 0 && uart.Bus.SR.Get()&py32.UART_SR_TXE == 0 {
|
||||
uartYield()
|
||||
retries--
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && !py32_uart_type && !py32_usart_unnumbered
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func defaultUSART() *py32.USART_Type {
|
||||
return py32.USART1
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && py32_usart_unnumbered
|
||||
|
||||
package machine
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func defaultUSART() *py32.USART_Type {
|
||||
return py32.USART
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build atmega || esp || nrf || sam || sifive || stm32 || k210 || nxp || rp2040 || rp2350
|
||||
//go:build atmega || esp || nrf || sam || sifive || stm32 || k210 || nxp || rp2040 || rp2350 || py32
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build atsamd21 || nrf51
|
||||
//go:build atsamd21 || nrf51 || py32
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build cortexm && !atsamd21 && !nrf51
|
||||
//go:build cortexm && !atsamd21 && !nrf51 && !py32
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
//go:build py32
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"machine"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
var tickCounter volatile.Register64
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
preinit()
|
||||
|
||||
configureHSI()
|
||||
|
||||
configureSystemTimer()
|
||||
machine.InitSerial()
|
||||
|
||||
run()
|
||||
exit(0)
|
||||
}
|
||||
|
||||
// configureSystemTimer configures SysTick for 1ms ticks.
|
||||
func configureSystemTimer() {
|
||||
arm.SetupSystemTimer(machine.CPUFrequency() / 1000)
|
||||
}
|
||||
|
||||
func ticksToNanoseconds(value timeUnit) int64 {
|
||||
return int64(value * 1000_000)
|
||||
}
|
||||
|
||||
func nanosecondsToTicks(ns int64) timeUnit {
|
||||
return timeUnit(ns / 1000_000)
|
||||
}
|
||||
|
||||
//go:linkname ticks runtime.ticks
|
||||
func ticks() timeUnit {
|
||||
return timeUnit(tickCounter.Get())
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
if d <= 0 {
|
||||
return
|
||||
}
|
||||
start := ticks()
|
||||
stop := start + d
|
||||
for ticks() < stop {
|
||||
waitForEvents()
|
||||
}
|
||||
}
|
||||
|
||||
func waitForEvents() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
//export SysTick_Handler
|
||||
func handleSysTick() {
|
||||
tickCounter.Set(tickCounter.Get() + 1)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build py32 && !py32_hsi_fs_op && !py32_no_hsi_fs
|
||||
|
||||
package runtime
|
||||
|
||||
import "device/py32"
|
||||
|
||||
func configureHSI() {
|
||||
py32.RCC.SetICSCR_HSI_FS(py32.RCC_ICSCR_HSI_FS_Freq24MHz)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
//go:build py32 && py32_no_hsi_fs
|
||||
|
||||
package runtime
|
||||
|
||||
// The M4 SVDs do not expose the M0+ ICSCR.HSI_FS selector. Preserve their
|
||||
// vendor-defined 8 MHz reset clock instead of writing an unverified encoding.
|
||||
func configureHSI() {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build py32 && py32_hsi_fs_op
|
||||
|
||||
package runtime
|
||||
|
||||
import "device/py32"
|
||||
|
||||
// The official L090/T09x headers provide HSI_FS_OP position and mask helpers,
|
||||
// but no value definitions. Encoding 4 selects the runtime's 24 MHz clock.
|
||||
func configureHSI() {
|
||||
py32.RCC.SetICSCR_HSI_FS_OP(4)
|
||||
}
|
||||
Reference in New Issue
Block a user