github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_stm32_spi.go (about)

     1  //go:build stm32 && !stm32f7x2 && !stm32l5x2
     2  
     3  package machine
     4  
     5  // Peripheral abstraction layer for SPI on the stm32 family
     6  
     7  import (
     8  	"device/stm32"
     9  	"runtime/volatile"
    10  	"unsafe"
    11  )
    12  
    13  // SPIConfig is used to store config info for SPI.
    14  type SPIConfig struct {
    15  	Frequency uint32
    16  	SCK       Pin
    17  	SDO       Pin
    18  	SDI       Pin
    19  	LSBFirst  bool
    20  	Mode      uint8
    21  }
    22  
    23  // Configure is intended to setup the STM32 SPI1 interface.
    24  func (spi SPI) Configure(config SPIConfig) error {
    25  
    26  	// -- CONFIGURING THE SPI IN MASTER MODE --
    27  	//
    28  	// 1. Select the BR[2:0] bits to define the serial clock baud rate (see
    29  	// 	  SPI_CR1 register).
    30  	// 2. Select the CPOL and CPHA bits to define one of the four relationships
    31  	//    between the data transfer and the serial clock (see Figure 248). This
    32  	//    step is not required when the TI mode is selected.
    33  	// 3. Set the DFF bit to define 8- or 16-bit data frame format
    34  	// 4. Configure the LSBFIRST bit in the SPI_CR1 register to define the frame
    35  	//    format. This step is not required when the TI mode is selected.
    36  	// 5. If the NSS pin is required in input mode, in hardware mode, connect the
    37  	//    NSS pin to a high-level signal during the complete byte transmit
    38  	//    sequence. In NSS software mode, set the SSM and SSI bits in the SPI_CR1
    39  	//    register. If the NSS pin is required in output mode, the SSOE bit only
    40  	//    should be set. This step is not required when the TI mode is selected.
    41  	// 6. Set the FRF bit in SPI_CR2 to select the TI protocol for serial
    42  	//    communications.
    43  	// 7. The MSTR and SPE bits must be set (they remain set only if the NSS pin
    44  	//    is connected to a high-level signal).
    45  
    46  	// disable SPI interface before any configuration changes
    47  	spi.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE)
    48  
    49  	// enable clock for SPI
    50  	enableAltFuncClock(unsafe.Pointer(spi.Bus))
    51  
    52  	// init pins
    53  	if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
    54  		config.SCK = SPI0_SCK_PIN
    55  		config.SDO = SPI0_SDO_PIN
    56  		config.SDI = SPI0_SDI_PIN
    57  	}
    58  	spi.configurePins(config)
    59  
    60  	// Get SPI baud rate based on the bus speed it's attached to
    61  	var conf uint32 = spi.getBaudRate(config)
    62  
    63  	// set bit transfer order
    64  	if config.LSBFirst {
    65  		conf |= stm32.SPI_CR1_LSBFIRST
    66  	}
    67  
    68  	// set polarity and phase on the SPI interface
    69  	switch config.Mode {
    70  	case Mode1:
    71  		conf |= stm32.SPI_CR1_CPHA
    72  	case Mode2:
    73  		conf |= stm32.SPI_CR1_CPOL
    74  	case Mode3:
    75  		conf |= stm32.SPI_CR1_CPOL
    76  		conf |= stm32.SPI_CR1_CPHA
    77  	}
    78  
    79  	// configure as SPI master
    80  	conf |= stm32.SPI_CR1_MSTR | stm32.SPI_CR1_SSI
    81  
    82  	// enable the SPI interface
    83  	conf |= stm32.SPI_CR1_SPE
    84  
    85  	// use software CS (GPIO) by default
    86  	conf |= stm32.SPI_CR1_SSM
    87  
    88  	// now set the configuration
    89  	spi.Bus.CR1.Set(conf)
    90  
    91  	// Series-specific configuration to set 8-bit transfer mode
    92  	spi.config8Bits()
    93  
    94  	// enable SPI
    95  	spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
    96  
    97  	return nil
    98  }
    99  
   100  // Transfer writes/reads a single byte using the SPI interface.
   101  func (spi SPI) Transfer(w byte) (byte, error) {
   102  
   103  	// 1. Enable the SPI by setting the SPE bit to 1.
   104  	// 2. Write the first data item to be transmitted into the SPI_DR register
   105  	//    (this clears the TXE flag).
   106  	// 3. Wait until TXE=1 and write the second data item to be transmitted. Then
   107  	//    wait until RXNE=1 and read the SPI_DR to get the first received data
   108  	//    item (this clears the RXNE bit). Repeat this operation for each data
   109  	//    item to be transmitted/received until the n–1 received data.
   110  	// 4. Wait until RXNE=1 and read the last received data.
   111  	// 5. Wait until TXE=1 and then wait until BSY=0 before disabling the SPI.
   112  
   113  	// put output word (8-bit) in data register (DR), which is parallel-loaded
   114  	// into shift register, and shifted out on MOSI.  Some series have 16-bit
   115  	// register but writes must be strictly 8-bit to output a byte.  Writing
   116  	// 16-bits indicates a packed transfer (2 bytes).
   117  	(*volatile.Register8)(unsafe.Pointer(&spi.Bus.DR.Reg)).Set(w)
   118  
   119  	// wait for SPI bus receive buffer not empty bit (RXNE) to be set.
   120  	// warning: blocks forever until this condition is met.
   121  	for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXNE) {
   122  	}
   123  
   124  	// copy input word (8-bit) in data register (DR), which was shifted in on MISO
   125  	// and parallel-loaded into register.
   126  	data := byte(spi.Bus.DR.Get())
   127  
   128  	// wait for SPI bus transmit buffer empty bit (TXE) to be set.
   129  	// warning: blocks forever until this condition is met.
   130  	for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXE) {
   131  	}
   132  
   133  	// wait for SPI bus busy bit (BSY) to be clear to indicate synchronous
   134  	// transfer complete. this will effectively prevent this Transfer() function
   135  	// from being capable of maintaining high-bandwidth communication throughput,
   136  	// but it will help guarantee stability on the bus.
   137  	for spi.Bus.SR.HasBits(stm32.SPI_SR_BSY) {
   138  	}
   139  
   140  	// clear the overrun flag (only in full-duplex mode)
   141  	if !spi.Bus.CR1.HasBits(stm32.SPI_CR1_RXONLY | stm32.SPI_CR1_BIDIMODE | stm32.SPI_CR1_BIDIOE) {
   142  		spi.Bus.SR.Get()
   143  	}
   144  
   145  	// Return received data from SPI data register
   146  	return data, nil
   147  }