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 }