From 25f3adb47ec715234599bb0d892ad79277400a5b Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Sun, 11 Apr 2021 08:28:04 -0700 Subject: stm32: support SPI on L4 series --- lib/stm32-svd | 2 +- src/machine/board_nucleol432kc.go | 50 ++++++++++++++++++++++++++---- src/machine/machine_stm32_spi.go | 16 +++++++--- src/machine/machine_stm32f103.go | 4 +++ src/machine/machine_stm32f405.go | 4 +++ src/machine/machine_stm32f407.go | 4 +++ src/machine/machine_stm32l0.go | 4 +++ src/machine/machine_stm32l4.go | 64 +++++++++++++++++++++++++++++++++++++++ src/machine/spi.go | 2 +- 9 files changed, 138 insertions(+), 12 deletions(-) diff --git a/lib/stm32-svd b/lib/stm32-svd index c6b5be976..3a0b0829b 160000 --- a/lib/stm32-svd +++ b/lib/stm32-svd @@ -1 +1 @@ -Subproject commit c6b5be976f440196dcd819a8d9a52c6786f67990 +Subproject commit 3a0b0829b70bf930c74fb69d887bee28005e7d99 diff --git a/src/machine/board_nucleol432kc.go b/src/machine/board_nucleol432kc.go index a8cc31ac1..cb92a9613 100644 --- a/src/machine/board_nucleol432kc.go +++ b/src/machine/board_nucleol432kc.go @@ -13,20 +13,53 @@ const ( LED_GREEN = PB3 ) -// UART pins const ( + // Arduino Pins + A0 = PA0 + A1 = PA1 + A2 = PA3 + A3 = PA4 + A4 = PA5 + A5 = PA6 + A6 = PA7 + A7 = PA2 + + D0 = PA10 + D1 = PA9 + D2 = PA12 + D3 = PB0 + D4 = PB7 + D5 = PB6 + D6 = PB1 + D7 = PC14 + D8 = PC15 + D9 = PA8 + D10 = PA11 + D11 = PB5 + D12 = PB4 + D13 = PB3 +) + +const ( + // UART pins // PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP) UART_TX_PIN = PA2 UART_RX_PIN = PA15 -) -// I2C pins -const ( + // I2C pins // With default solder bridge settings: // PB6 / Arduino D5 / CN3 Pin 8 is SCL // PB7 / Arduino D4 / CN3 Pin 7 is SDA I2C0_SCL_PIN = PB6 I2C0_SDA_PIN = PB7 + + // SPI pins + SPI1_SCK_PIN = PB3 + SPI1_SDI_PIN = PB5 + SPI1_SDO_PIN = PB4 + SPI0_SCK_PIN = SPI1_SCK_PIN + SPI0_SDI_PIN = SPI1_SDI_PIN + SPI0_SDO_PIN = SPI1_SDO_PIN ) var ( @@ -40,15 +73,20 @@ var ( RxAltFuncSelector: 3, } UART1 = &UART0 -) -var ( // I2C1 is documented, alias to I2C0 as well I2C1 = &I2C{ Bus: stm32.I2C1, AltFuncSelector: 4, } I2C0 = I2C1 + + // SPI1 is documented, alias to SPI0 as well + SPI1 = &SPI{ + Bus: stm32.SPI1, + AltFuncSelector: 5, + } + SPI0 = SPI1 ) func init() { diff --git a/src/machine/machine_stm32_spi.go b/src/machine/machine_stm32_spi.go index f46113e67..626234920 100644 --- a/src/machine/machine_stm32_spi.go +++ b/src/machine/machine_stm32_spi.go @@ -1,4 +1,4 @@ -// +build stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2 +// +build stm32,!stm32f7x2,!stm32l5x2 package machine @@ -6,6 +6,7 @@ package machine import ( "device/stm32" + "runtime/volatile" "unsafe" ) @@ -86,7 +87,12 @@ func (spi SPI) Configure(config SPIConfig) { // now set the configuration spi.Bus.CR1.Set(conf) - spi.Bus.CR2.SetBits((conf & stm32.SPI_CR1_SSM_Msk) >> 16) + + // Series-specific configuration to set 8-bit transfer mode + spi.config8Bits() + + // enable SPI + spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE) } // Transfer writes/reads a single byte using the SPI interface. @@ -103,8 +109,10 @@ func (spi SPI) Transfer(w byte) (byte, error) { // 5. Wait until TXE=1 and then wait until BSY=0 before disabling the SPI. // put output word (8-bit) in data register (DR), which is parallel-loaded - // into shift register, and shifted out on MOSI. - spi.Bus.DR.Set(uint32(w)) + // into shift register, and shifted out on MOSI. Some series have 16-bit + // register but writes must be strictly 8-bit to output a byte. Writing + // 16-bits indicates a packed transfer (2 bytes). + (*volatile.Register8)(unsafe.Pointer(&spi.Bus.DR.Reg)).Set(w) // wait for SPI bus receive buffer not empty bit (RXNE) to be set. // warning: blocks forever until this condition is met. diff --git a/src/machine/machine_stm32f103.go b/src/machine/machine_stm32f103.go index e1c91ca03..556785789 100644 --- a/src/machine/machine_stm32f103.go +++ b/src/machine/machine_stm32f103.go @@ -161,6 +161,10 @@ var ( SPI0 = SPI1 ) +func (spi SPI) config8Bits() { + // no-op on this series +} + // Set baud rate for SPI func (spi SPI) getBaudRate(config SPIConfig) uint32 { var conf uint32 diff --git a/src/machine/machine_stm32f405.go b/src/machine/machine_stm32f405.go index d70b6a712..92ad79f65 100644 --- a/src/machine/machine_stm32f405.go +++ b/src/machine/machine_stm32f405.go @@ -67,6 +67,10 @@ type SPI struct { AltFuncSelector uint8 } +func (spi SPI) config8Bits() { + // no-op on this series +} + func (spi SPI) configurePins(config SPIConfig) { config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector) config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector) diff --git a/src/machine/machine_stm32f407.go b/src/machine/machine_stm32f407.go index 72c157ccb..6ffafcd3b 100644 --- a/src/machine/machine_stm32f407.go +++ b/src/machine/machine_stm32f407.go @@ -70,6 +70,10 @@ type SPI struct { AltFuncSelector uint8 } +func (spi SPI) config8Bits() { + // no-op on this series +} + // Set baud rate for SPI func (spi SPI) getBaudRate(config SPIConfig) uint32 { var conf uint32 diff --git a/src/machine/machine_stm32l0.go b/src/machine/machine_stm32l0.go index 886c3985a..f4df17aba 100644 --- a/src/machine/machine_stm32l0.go +++ b/src/machine/machine_stm32l0.go @@ -184,6 +184,10 @@ type SPI struct { AltFuncSelector uint8 } +func (spi SPI) config8Bits() { + // no-op on this series +} + // Set baud rate for SPI func (spi SPI) getBaudRate(config SPIConfig) uint32 { var conf uint32 diff --git a/src/machine/machine_stm32l4.go b/src/machine/machine_stm32l4.go index 95e1eb0fe..7b52f68a5 100644 --- a/src/machine/machine_stm32l4.go +++ b/src/machine/machine_stm32l4.go @@ -182,3 +182,67 @@ func enableAltFuncClock(bus unsafe.Pointer) { stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN) } } + +//---------- SPI related types and code + +// SPI on the STM32Fxxx using MODER / alternate function pins +type SPI struct { + Bus *stm32.SPI_Type + AltFuncSelector uint8 +} + +func (spi SPI) config8Bits() { + // Set rx threshold to 8-bits, so RXNE flag is set for 1 byte + // (common STM32 SPI implementation does 8-bit transfers only) + spi.Bus.CR2.SetBits(stm32.SPI_CR2_FRXTH) +} + +// Set baud rate for SPI +func (spi SPI) getBaudRate(config SPIConfig) uint32 { + var conf uint32 + + // Default + if config.Frequency == 0 { + config.Frequency = 4e6 + } + + localFrequency := config.Frequency + + // set frequency dependent on PCLK prescaler. Since these are rather weird + // speeds due to the CPU freqency, pick a range up to that frquency for + // clients to use more human-understandable numbers, e.g. nearest 100KHz + + // These are based on 80MHz peripheral clock frquency + switch { + case localFrequency < 312500: + conf = stm32.SPI_CR1_BR_Div256 + case localFrequency < 625000: + conf = stm32.SPI_CR1_BR_Div128 + case localFrequency < 1250000: + conf = stm32.SPI_CR1_BR_Div64 + case localFrequency < 2500000: + conf = stm32.SPI_CR1_BR_Div32 + case localFrequency < 5000000: + conf = stm32.SPI_CR1_BR_Div16 + case localFrequency < 10000000: + conf = stm32.SPI_CR1_BR_Div8 + // NOTE: many SPI components won't operate reliably (or at all) above 10MHz + // Check the datasheet of the part + case localFrequency < 20000000: + conf = stm32.SPI_CR1_BR_Div4 + case localFrequency < 40000000: + conf = stm32.SPI_CR1_BR_Div2 + default: + // None of the specific baudrates were selected; choose the lowest speed + conf = stm32.SPI_CR1_BR_Div256 + } + + return conf << stm32.SPI_CR1_BR_Pos +} + +// Configure SPI pins for input output and clock +func (spi SPI) configurePins(config SPIConfig) { + config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector) + config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector) + config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector) +} diff --git a/src/machine/spi.go b/src/machine/spi.go index 6bea0141d..80f7c133d 100644 --- a/src/machine/spi.go +++ b/src/machine/spi.go @@ -1,4 +1,4 @@ -// +build !baremetal stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2 fe310 k210 atmega +// +build !baremetal stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega package machine -- cgit v1.2.3