mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-20 04:19:04 +00:00
machine/stm32: add STM32U5 SPI support
Add SPIv2 driver for STM32U5 using TXDR/RXDR and CFG1/CFG2 registers, which differ from the classic SPI peripheral on older STM32 families. Implements Configure() and single-byte Transfer(). Add SPI1 peripheral and pin definitions to arduino-uno-q board. Exclude stm32u5 from the shared classic SPI build tag.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
//go:build stm32u5
|
||||
|
||||
package machine
|
||||
|
||||
// SPI on STM32U5 uses the new SPIv2 peripheral with separate TXDR/RXDR registers.
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK Pin
|
||||
SDO Pin
|
||||
SDI Pin
|
||||
LSBFirst bool
|
||||
Mode uint8
|
||||
}
|
||||
|
||||
// Configure is intended to setup the STM32U5 SPI peripheral.
|
||||
func (spi *SPI) Configure(config SPIConfig) error {
|
||||
// Disable SPI interface before any configuration changes
|
||||
spi.Bus.CR1.ClearBits(1) // Clear SPE (bit 0)
|
||||
|
||||
// Enable clock for SPI
|
||||
enableAltFuncClock(unsafe.Pointer(spi.Bus))
|
||||
|
||||
// Init pins - use defaults if not specified
|
||||
if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
|
||||
config.SCK = SPI0_SCK_PIN
|
||||
config.SDO = SPI0_SDO_PIN
|
||||
config.SDI = SPI0_SDI_PIN
|
||||
}
|
||||
spi.configurePins(config)
|
||||
|
||||
// Configure CFG1: baud rate and data size
|
||||
var cfg1 uint32
|
||||
|
||||
// Set baud rate (MBR bits [30:28])
|
||||
cfg1 |= spi.getBaudRate(config)
|
||||
|
||||
// Set data size to 8 bits (DSIZE[4:0] = 7 = 8-1)
|
||||
cfg1 |= 7
|
||||
|
||||
spi.Bus.CFG1.Set(cfg1)
|
||||
|
||||
// Configure CFG2: master mode, SS output, polarity, phase
|
||||
var cfg2 uint32
|
||||
const (
|
||||
cfg2_MASTER = 1 << 22 // MASTER bit
|
||||
cfg2_SSM = 1 << 26 // Software SS management
|
||||
cfg2_SSOE = 1 << 29 // SS output enable
|
||||
cfg2_AFCNTR = 1 << 31 // Alternate function GPIO control always active
|
||||
cfg2_CPOL = 1 << 25
|
||||
cfg2_CPHA = 1 << 24
|
||||
cfg2_LSBFRST = 1 << 23
|
||||
)
|
||||
|
||||
cfg2 |= cfg2_MASTER | cfg2_SSM | cfg2_AFCNTR
|
||||
|
||||
// Set polarity and phase
|
||||
switch config.Mode {
|
||||
case Mode1:
|
||||
cfg2 |= cfg2_CPHA
|
||||
case Mode2:
|
||||
cfg2 |= cfg2_CPOL
|
||||
case Mode3:
|
||||
cfg2 |= cfg2_CPOL | cfg2_CPHA
|
||||
}
|
||||
|
||||
// Set bit transfer order
|
||||
if config.LSBFirst {
|
||||
cfg2 |= cfg2_LSBFRST
|
||||
}
|
||||
|
||||
spi.Bus.CFG2.Set(cfg2)
|
||||
|
||||
// Enable SPI
|
||||
spi.Bus.CR1.SetBits(1) // Set SPE (bit 0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
func (spi *SPI) Transfer(w byte) (byte, error) {
|
||||
// Set transfer size to 1 frame
|
||||
spi.Bus.CR2.Set(1)
|
||||
|
||||
// Start the transfer
|
||||
spi.Bus.CR1.SetBits(1 << 9) // CSTART bit
|
||||
|
||||
// Wait until TX FIFO is ready (TXP bit in SR)
|
||||
const sr_TXP = 1 << 1
|
||||
const sr_RXP = 1 << 0
|
||||
const sr_EOT = 1 << 3
|
||||
|
||||
for !spi.Bus.SR.HasBits(sr_TXP) {
|
||||
}
|
||||
|
||||
// Write byte to TXDR
|
||||
spi.Bus.TXDR.Set(uint32(w))
|
||||
|
||||
// Wait for RX data available (RXP bit in SR)
|
||||
for !spi.Bus.SR.HasBits(sr_RXP) {
|
||||
}
|
||||
|
||||
// Read received byte
|
||||
data := byte(spi.Bus.RXDR.Get())
|
||||
|
||||
// Wait for end of transfer
|
||||
for !spi.Bus.SR.HasBits(sr_EOT) {
|
||||
}
|
||||
|
||||
// Clear EOT flag
|
||||
spi.Bus.IFCR.Set(sr_EOT)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
Reference in New Issue
Block a user