mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 20:43:40 +00:00
cfd74c2954
* Add STM32G0B1 target support Introduce support for STM32G0B1 microcontrollers, including target-specific JSON files, linker scripts, and runtime initialization. This update adds hardware support for GPIO, UART, SPI, I2C, timers, and additional board-specific configurations like Nucleo-G0B1RE. * Update STM32G0 clock initialization to 64MHz and adjust related configurations Reconfigure STM32G0 to use a 64MHz system clock via PLL with HSI16 as the source. Update flash latency, prescaler settings, and I2C timing values to reflect the new frequency. * Cleanup * Cleanup * Add STM32G0-specific UART implementation Introduce a new UART implementation for the STM32G0 series with chip-specific setup and configuration methods. Update the generic STM32 UART code to exclude STM32G0. * Refactor STM32G0 runtime and machine code to utilize chip-specific register access functions Simplify and standardize register operations with dedicated setter methods in the STM32G0 runtime and machine code and cleanup redundant syntax. * Remove redundant commented-out APBENR1 register operations in STM32G0 machine code * Introduce FDCAN support for STM32G0B1 series Add FDCAN peripheral implementation targeting STM32G0B1, including support for standard, extended identifiers, and bit rate configuration. Update board files to include FDCAN pins, instances, and clock configuration for Nucleo-G0B1RE and Amken Trio boards.
148 lines
4.9 KiB
Go
148 lines
4.9 KiB
Go
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0
|
||
|
||
package machine
|
||
|
||
// Peripheral abstraction layer for SPI on the stm32 family
|
||
|
||
import (
|
||
"device/stm32"
|
||
"runtime/volatile"
|
||
"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 STM32 SPI1 interface.
|
||
func (spi *SPI) Configure(config SPIConfig) error {
|
||
|
||
// -- CONFIGURING THE SPI IN MASTER MODE --
|
||
//
|
||
// 1. Select the BR[2:0] bits to define the serial clock baud rate (see
|
||
// SPI_CR1 register).
|
||
// 2. Select the CPOL and CPHA bits to define one of the four relationships
|
||
// between the data transfer and the serial clock (see Figure 248). This
|
||
// step is not required when the TI mode is selected.
|
||
// 3. Set the DFF bit to define 8- or 16-bit data frame format
|
||
// 4. Configure the LSBFIRST bit in the SPI_CR1 register to define the frame
|
||
// format. This step is not required when the TI mode is selected.
|
||
// 5. If the NSS pin is required in input mode, in hardware mode, connect the
|
||
// NSS pin to a high-level signal during the complete byte transmit
|
||
// sequence. In NSS software mode, set the SSM and SSI bits in the SPI_CR1
|
||
// register. If the NSS pin is required in output mode, the SSOE bit only
|
||
// should be set. This step is not required when the TI mode is selected.
|
||
// 6. Set the FRF bit in SPI_CR2 to select the TI protocol for serial
|
||
// communications.
|
||
// 7. The MSTR and SPE bits must be set (they remain set only if the NSS pin
|
||
// is connected to a high-level signal).
|
||
|
||
// disable SPI interface before any configuration changes
|
||
spi.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE)
|
||
|
||
// enable clock for SPI
|
||
enableAltFuncClock(unsafe.Pointer(spi.Bus))
|
||
|
||
// init pins
|
||
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)
|
||
|
||
// Get SPI baud rate based on the bus speed it's attached to
|
||
var conf uint32 = spi.getBaudRate(config)
|
||
|
||
// set bit transfer order
|
||
if config.LSBFirst {
|
||
conf |= stm32.SPI_CR1_LSBFIRST
|
||
}
|
||
|
||
// set polarity and phase on the SPI interface
|
||
switch config.Mode {
|
||
case Mode1:
|
||
conf |= stm32.SPI_CR1_CPHA
|
||
case Mode2:
|
||
conf |= stm32.SPI_CR1_CPOL
|
||
case Mode3:
|
||
conf |= stm32.SPI_CR1_CPOL
|
||
conf |= stm32.SPI_CR1_CPHA
|
||
}
|
||
|
||
// configure as SPI master
|
||
conf |= stm32.SPI_CR1_MSTR | stm32.SPI_CR1_SSI
|
||
|
||
// enable the SPI interface
|
||
conf |= stm32.SPI_CR1_SPE
|
||
|
||
// use software CS (GPIO) by default
|
||
conf |= stm32.SPI_CR1_SSM
|
||
|
||
// now set the configuration
|
||
spi.Bus.CR1.Set(conf)
|
||
|
||
// Series-specific configuration to set 8-bit transfer mode
|
||
spi.config8Bits()
|
||
|
||
// enable SPI
|
||
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||
|
||
return nil
|
||
}
|
||
|
||
// Transfer writes/reads a single byte using the SPI interface.
|
||
func (spi *SPI) Transfer(w byte) (byte, error) {
|
||
|
||
// 1. Enable the SPI by setting the SPE bit to 1.
|
||
// 2. Write the first data item to be transmitted into the SPI_DR register
|
||
// (this clears the TXE flag).
|
||
// 3. Wait until TXE=1 and write the second data item to be transmitted. Then
|
||
// wait until RXNE=1 and read the SPI_DR to get the first received data
|
||
// item (this clears the RXNE bit). Repeat this operation for each data
|
||
// item to be transmitted/received until the n–1 received data.
|
||
// 4. Wait until RXNE=1 and read the last received data.
|
||
// 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. 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.
|
||
for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXNE) {
|
||
}
|
||
|
||
// copy input word (8-bit) in data register (DR), which was shifted in on MISO
|
||
// and parallel-loaded into register.
|
||
data := byte(spi.Bus.DR.Get())
|
||
|
||
// wait for SPI bus transmit buffer empty bit (TXE) to be set.
|
||
// warning: blocks forever until this condition is met.
|
||
for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXE) {
|
||
}
|
||
|
||
// wait for SPI bus busy bit (BSY) to be clear to indicate synchronous
|
||
// transfer complete. this will effectively prevent this Transfer() function
|
||
// from being capable of maintaining high-bandwidth communication throughput,
|
||
// but it will help guarantee stability on the bus.
|
||
for spi.Bus.SR.HasBits(stm32.SPI_SR_BSY) {
|
||
}
|
||
|
||
// clear the overrun flag (only in full-duplex mode)
|
||
if !spi.Bus.CR1.HasBits(stm32.SPI_CR1_RXONLY | stm32.SPI_CR1_BIDIMODE | stm32.SPI_CR1_BIDIOE) {
|
||
spi.Bus.SR.Get()
|
||
}
|
||
|
||
// Return received data from SPI data register
|
||
return data, nil
|
||
}
|