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  }