github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_spi.go (about) 1 //go:build rp2040 2 3 package machine 4 5 import ( 6 "device/rp" 7 "errors" 8 "unsafe" 9 ) 10 11 // SPI on the RP2040 12 var ( 13 SPI0 = &_SPI0 14 _SPI0 = SPI{ 15 Bus: rp.SPI0, 16 } 17 SPI1 = &_SPI1 18 _SPI1 = SPI{ 19 Bus: rp.SPI1, 20 } 21 ) 22 23 // SPIConfig is used to store config info for SPI. 24 type SPIConfig struct { 25 Frequency uint32 26 // LSB not supported on rp2040. 27 LSBFirst bool 28 // Mode's two most LSB are CPOL and CPHA. i.e. Mode==2 (0b10) is CPOL=1, CPHA=0 29 Mode uint8 30 // Serial clock pin 31 SCK Pin 32 // TX or Serial Data Out (MOSI if rp2040 is master) 33 SDO Pin 34 // RX or Serial Data In (MISO if rp2040 is master) 35 SDI Pin 36 } 37 38 var ( 39 ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022") 40 ErrSPITimeout = errors.New("SPI timeout") 41 ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz") 42 errSPIInvalidSDI = errors.New("invalid SPI SDI pin") 43 errSPIInvalidSDO = errors.New("invalid SPI SDO pin") 44 errSPIInvalidSCK = errors.New("invalid SPI SCK pin") 45 ) 46 47 type SPI struct { 48 Bus *rp.SPI0_Type 49 } 50 51 // Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read 52 // interface, there must always be the same number of bytes written as bytes read. 53 // The Tx method knows about this, and offers a few different ways of calling it. 54 // 55 // This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer. 56 // Note that the tx and rx buffers must be the same size: 57 // 58 // spi.Tx(tx, rx) 59 // 60 // This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros 61 // until all the bytes in the command packet have been received: 62 // 63 // spi.Tx(tx, nil) 64 // 65 // This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet": 66 // 67 // spi.Tx(nil, rx) 68 // 69 // Remark: This implementation (RP2040) allows reading into buffer with a custom repeated 70 // value on tx. 71 // 72 // spi.Tx([]byte{0xff}, rx) // may cause unwanted heap allocations. 73 // 74 // This form sends 0xff and puts the result into rx buffer. Useful for reading from SD cards 75 // which require 0xff input on SI. 76 func (spi SPI) Tx(w, r []byte) (err error) { 77 switch { 78 case w == nil: 79 // read only, so write zero and read a result. 80 err = spi.rx(r, 0) 81 case r == nil: 82 // write only 83 err = spi.tx(w) 84 case len(w) == 1 && len(r) > 1: 85 // Read with custom repeated value. 86 err = spi.rx(r, w[0]) 87 default: 88 // write/read 89 err = spi.txrx(w, r) 90 } 91 return err 92 } 93 94 // Write a single byte and read a single byte from TX/RX FIFO. 95 func (spi SPI) Transfer(w byte) (byte, error) { 96 for !spi.isWritable() { 97 } 98 99 spi.Bus.SSPDR.Set(uint32(w)) 100 101 for !spi.isReadable() { 102 } 103 return uint8(spi.Bus.SSPDR.Get()), nil 104 } 105 106 func (spi SPI) SetBaudRate(br uint32) error { 107 const freqin uint32 = 125 * MHz 108 const maxBaud uint32 = 66.5 * MHz // max output frequency is 66.5MHz on rp2040. see Note page 527. 109 // Find smallest prescale value which puts output frequency in range of 110 // post-divide. Prescale is an even number from 2 to 254 inclusive. 111 var prescale, postdiv uint32 112 for prescale = 2; prescale < 255; prescale += 2 { 113 if freqin < (prescale+2)*256*br { 114 break 115 } 116 } 117 if prescale > 254 || br > maxBaud { 118 return ErrSPIBaud 119 } 120 // Find largest post-divide which makes output <= baudrate. Post-divide is 121 // an integer in the range 1 to 256 inclusive. 122 for postdiv = 256; postdiv > 1; postdiv-- { 123 if freqin/(prescale*(postdiv-1)) > br { 124 break 125 } 126 } 127 spi.Bus.SSPCPSR.Set(prescale) 128 spi.Bus.SSPCR0.ReplaceBits((postdiv-1)<<rp.SPI0_SSPCR0_SCR_Pos, rp.SPI0_SSPCR0_SCR_Msk, 0) 129 return nil 130 } 131 132 func (spi SPI) GetBaudRate() uint32 { 133 const freqin uint32 = 125 * MHz 134 prescale := spi.Bus.SSPCPSR.Get() 135 postdiv := ((spi.Bus.SSPCR0.Get() & rp.SPI0_SSPCR0_SCR_Msk) >> rp.SPI0_SSPCR0_SCR_Pos) + 1 136 return freqin / (prescale * postdiv) 137 } 138 139 // Configure is intended to setup/initialize the SPI interface. 140 // Default baudrate of 4MHz is used if Frequency == 0. Default 141 // word length (data bits) is 8. 142 // Below is a list of GPIO pins corresponding to SPI0 bus on the rp2040: 143 // 144 // SI : 0, 4, 17 a.k.a RX and MISO (if rp2040 is master) 145 // SO : 3, 7, 19 a.k.a TX and MOSI (if rp2040 is master) 146 // SCK: 2, 6, 18 147 // 148 // SPI1 bus GPIO pins: 149 // 150 // SI : 8, 12 151 // SO : 11, 15 152 // SCK: 10, 14 153 // 154 // No pin configuration is needed of SCK, SDO and SDI needed after calling Configure. 155 func (spi SPI) Configure(config SPIConfig) error { 156 const defaultBaud uint32 = 4 * MHz 157 if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 { 158 // set default pins if config zero valued or invalid clock pin supplied. 159 switch spi.Bus { 160 case rp.SPI0: 161 config.SCK = SPI0_SCK_PIN 162 config.SDO = SPI0_SDO_PIN 163 config.SDI = SPI0_SDI_PIN 164 case rp.SPI1: 165 config.SCK = SPI1_SCK_PIN 166 config.SDO = SPI1_SDO_PIN 167 config.SDI = SPI1_SDI_PIN 168 } 169 } 170 var okSDI, okSDO, okSCK bool 171 switch spi.Bus { 172 case rp.SPI0: 173 okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20 174 okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23 175 okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22 176 case rp.SPI1: 177 okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28 178 okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27 179 okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26 180 } 181 182 switch { 183 case !okSDI: 184 return errSPIInvalidSDI 185 case !okSDO: 186 return errSPIInvalidSDO 187 case !okSCK: 188 return errSPIInvalidSCK 189 } 190 191 if config.Frequency == 0 { 192 config.Frequency = defaultBaud 193 } 194 // SPI pin configuration 195 config.SCK.setFunc(fnSPI) 196 config.SDO.setFunc(fnSPI) 197 config.SDI.setFunc(fnSPI) 198 199 return spi.initSPI(config) 200 } 201 202 func (spi SPI) initSPI(config SPIConfig) (err error) { 203 spi.reset() 204 // LSB-first not supported on PL022: 205 if config.LSBFirst { 206 return ErrLSBNotSupported 207 } 208 err = spi.SetBaudRate(config.Frequency) 209 // Set SPI Format (CPHA and CPOL) and frame format (default is Motorola) 210 spi.setFormat(config.Mode, rp.XIP_SSI_CTRLR0_SPI_FRF_STD) 211 212 // Always enable DREQ signals -- harmless if DMA is not listening 213 spi.Bus.SSPDMACR.SetBits(rp.SPI0_SSPDMACR_TXDMAE | rp.SPI0_SSPDMACR_RXDMAE) 214 // Finally enable the SPI 215 spi.Bus.SSPCR1.SetBits(rp.SPI0_SSPCR1_SSE) 216 return err 217 } 218 219 //go:inline 220 func (spi SPI) setFormat(mode uint8, frameFormat uint32) { 221 cpha := uint32(mode) & 1 222 cpol := uint32(mode>>1) & 1 223 spi.Bus.SSPCR0.ReplaceBits( 224 (cpha<<rp.SPI0_SSPCR0_SPH_Pos)| 225 (cpol<<rp.SPI0_SSPCR0_SPO_Pos)| 226 (uint32(7)<<rp.SPI0_SSPCR0_DSS_Pos)| // Set databits (SPI word length) to 8 bits. 227 (frameFormat&0b11)<<rp.SPI0_SSPCR0_FRF_Pos, // Frame format bits 4:5 228 rp.SPI0_SSPCR0_SPH_Msk|rp.SPI0_SSPCR0_SPO_Msk|rp.SPI0_SSPCR0_DSS_Msk|rp.SPI0_SSPCR0_FRF_Msk, 0) 229 } 230 231 // reset resets SPI and waits until reset is done. 232 // 233 //go:inline 234 func (spi SPI) reset() { 235 resetVal := spi.deinit() 236 rp.RESETS.RESET.ClearBits(resetVal) 237 // Wait until reset is done. 238 for !rp.RESETS.RESET_DONE.HasBits(resetVal) { 239 } 240 } 241 242 //go:inline 243 func (spi SPI) deinit() (resetVal uint32) { 244 switch spi.Bus { 245 case rp.SPI0: 246 resetVal = rp.RESETS_RESET_SPI0 247 case rp.SPI1: 248 resetVal = rp.RESETS_RESET_SPI1 249 } 250 // Perform SPI reset. 251 rp.RESETS.RESET.SetBits(resetVal) 252 return resetVal 253 } 254 255 // isWritable returns false if no space is available to write. True if a write is possible 256 // 257 //go:inline 258 func (spi SPI) isWritable() bool { 259 return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_TNF) 260 } 261 262 // isReadable returns true if a read is possible i.e. data is present 263 // 264 //go:inline 265 func (spi SPI) isReadable() bool { 266 return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_RNE) 267 } 268 269 // PrintRegs prints SPI's peripheral common registries current values 270 func (spi SPI) PrintRegs() { 271 cr0 := spi.Bus.SSPCR0.Get() 272 cr1 := spi.Bus.SSPCR1.Get() 273 dmacr := spi.Bus.SSPDMACR.Get() 274 cpsr := spi.Bus.SSPCPSR.Get() 275 dr := spi.Bus.SSPDR.Get() 276 ris := spi.Bus.SSPRIS.Get() 277 println("CR0:", cr0) 278 println("CR1:", cr1) 279 println("DMACR:", dmacr) 280 println("CPSR:", cpsr) 281 println("DR:", dr) 282 println("RIS:", ris) 283 } 284 285 //go:inline 286 func (spi SPI) isBusy() bool { 287 return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_BSY) 288 } 289 290 // tx writes buffer to SPI ignoring Rx. 291 func (spi SPI) tx(tx []byte) error { 292 if len(tx) == 0 { 293 // We don't have to do anything. 294 // This avoids a panic in &tx[0] when len(tx) == 0. 295 return nil 296 } 297 298 // Pick the DMA channel reserved for this SPI peripheral. 299 var ch *dmaChannel 300 var dreq uint32 301 if spi.Bus == rp.SPI0 { 302 ch = &dmaChannels[spi0DMAChannel] 303 dreq = 16 // DREQ_SPI0_TX 304 } else { // SPI1 305 ch = &dmaChannels[spi1DMAChannel] 306 dreq = 18 // DREQ_SPI1_TX 307 } 308 309 // Configure the DMA peripheral as follows: 310 // - set read address, write address, and number of transfer units (bytes) 311 // - increment read address (in memory), don't increment write address (SSPDR) 312 // - set data size to single bytes 313 // - set the DREQ so that the DMA will fill the SPI FIFO as needed 314 // - start the transfer 315 ch.READ_ADDR.Set(uint32(uintptr(unsafe.Pointer(&tx[0])))) 316 ch.WRITE_ADDR.Set(uint32(uintptr(unsafe.Pointer(&spi.Bus.SSPDR)))) 317 ch.TRANS_COUNT.Set(uint32(len(tx))) 318 ch.CTRL_TRIG.Set(rp.DMA_CH0_CTRL_TRIG_INCR_READ | 319 rp.DMA_CH0_CTRL_TRIG_DATA_SIZE_SIZE_BYTE<<rp.DMA_CH0_CTRL_TRIG_DATA_SIZE_Pos | 320 dreq<<rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_Pos | 321 rp.DMA_CH0_CTRL_TRIG_EN) 322 323 // Wait until the transfer is complete. 324 // TODO: do this more efficiently: 325 // - Add a new API to start the transfer, without waiting for it to 326 // complete. This way, the CPU can do something useful while the 327 // transfer is in progress. 328 // - If we have to wait, do so by waiting for an interrupt and blocking 329 // this goroutine until finished (so that other goroutines can run or 330 // the CPU can go to sleep). 331 for ch.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0 { 332 } 333 334 // We didn't read any result values, which means the RX FIFO has likely 335 // overflown. We have to clean up this mess now. 336 337 // Drain RX FIFO, then wait for shifting to finish (which may be *after* 338 // TX FIFO drains), then drain RX FIFO again 339 for spi.isReadable() { 340 spi.Bus.SSPDR.Get() 341 } 342 for spi.isBusy() { 343 } 344 for spi.isReadable() { 345 spi.Bus.SSPDR.Get() 346 } 347 // Don't leave overrun flag set 348 spi.Bus.SSPICR.Set(rp.SPI0_SSPICR_RORIC) 349 return nil 350 } 351 352 // rx reads buffer to SPI ignoring x. 353 // txrepeat is output repeatedly on SO as data is read in from SI. 354 // Generally this can be 0, but some devices require a specific value here, 355 // e.g. SD cards expect 0xff 356 func (spi SPI) rx(rx []byte, txrepeat byte) error { 357 plen := len(rx) 358 const fifoDepth = 8 // see txrx 359 var rxleft, txleft = plen, plen 360 for txleft != 0 || rxleft != 0 { 361 if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth { 362 spi.Bus.SSPDR.Set(uint32(txrepeat)) 363 txleft-- 364 } 365 if rxleft != 0 && spi.isReadable() { 366 rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get()) 367 rxleft-- 368 continue 369 } 370 } 371 for spi.isBusy() { 372 gosched() 373 } 374 return nil 375 } 376 377 // Write len bytes from src to SPI. Simultaneously read len bytes from SPI to dst. 378 // Note this function is guaranteed to exit in a known amount of time (bits sent * time per bit) 379 func (spi SPI) txrx(tx, rx []byte) error { 380 plen := len(tx) 381 if plen != len(rx) { 382 return ErrTxInvalidSliceSize 383 } 384 // Never have more transfers in flight than will fit into the RX FIFO, 385 // else FIFO will overflow if this code is heavily interrupted. 386 const fifoDepth = 8 387 var rxleft, txleft = plen, plen 388 for txleft != 0 || rxleft != 0 { 389 if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth { 390 spi.Bus.SSPDR.Set(uint32(tx[plen-txleft])) 391 txleft-- 392 } 393 if rxleft != 0 && spi.isReadable() { 394 rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get()) 395 rxleft-- 396 } 397 } 398 399 if txleft != 0 || rxleft != 0 { 400 // Transaction ended early due to timeout 401 return ErrSPITimeout 402 } 403 for spi.isBusy() { 404 gosched() 405 } 406 return nil 407 }