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

     1  //go:build mimxrt1062
     2  
     3  package machine
     4  
     5  // SPI peripheral abstraction layer for the MIMXRT1062
     6  
     7  import (
     8  	"device/nxp"
     9  	"errors"
    10  	"unsafe"
    11  )
    12  
    13  // SPIConfig is used to store config info for SPI.
    14  type SPIConfig struct {
    15  	Frequency uint32
    16  	SDI       Pin
    17  	SDO       Pin
    18  	SCK       Pin
    19  	CS        Pin
    20  	LSBFirst  bool
    21  	Mode      uint8
    22  }
    23  
    24  func (c SPIConfig) getPins() (di, do, ck, cs Pin) {
    25  	if 0 == c.SDI && 0 == c.SDO && 0 == c.SCK && 0 == c.CS {
    26  		// default pins if none specified
    27  		return SPI_SDI_PIN, SPI_SDO_PIN, SPI_SCK_PIN, SPI_CS_PIN
    28  	}
    29  	return c.SDI, c.SDO, c.SCK, c.CS
    30  }
    31  
    32  type SPI struct {
    33  	Bus *nxp.LPSPI_Type
    34  
    35  	// these hold the input selector ("daisy chain") values that select which pins
    36  	// are connected to the LPSPI device, and should be defined where the SPI
    37  	// instance is declared (e.g., in the board definition). see the godoc
    38  	// comments on type muxSelect for more details.
    39  	muxSDI, muxSDO, muxSCK, muxCS muxSelect
    40  
    41  	// these are copied from SPIConfig, during (*SPI).Configure(SPIConfig), and
    42  	// should be considered read-only for internal reference (i.e., modifying them
    43  	// will have no desirable effect).
    44  	sdi, sdo, sck, cs Pin
    45  	frequency         uint32
    46  
    47  	// auxiliary state data used internally
    48  	configured bool
    49  }
    50  
    51  const (
    52  	statusTxDataRequest    = nxp.LPSPI_SR_TDF // Transmit data flag
    53  	statusRxDataReady      = nxp.LPSPI_SR_RDF // Receive data flag
    54  	statusWordComplete     = nxp.LPSPI_SR_WCF // Word Complete flag
    55  	statusFrameComplete    = nxp.LPSPI_SR_FCF // Frame Complete flag
    56  	statusTransferComplete = nxp.LPSPI_SR_TCF // Transfer Complete flag
    57  	statusTransmitError    = nxp.LPSPI_SR_TEF // Transmit Error flag (FIFO underrun)
    58  	statusReceiveError     = nxp.LPSPI_SR_REF // Receive Error flag (FIFO overrun)
    59  	statusDataMatch        = nxp.LPSPI_SR_DMF // Data Match flag
    60  	statusModuleBusy       = nxp.LPSPI_SR_MBF // Module Busy flag
    61  	statusAll              = nxp.LPSPI_SR_TDF | nxp.LPSPI_SR_RDF |
    62  		nxp.LPSPI_SR_WCF | nxp.LPSPI_SR_FCF | nxp.LPSPI_SR_TCF | nxp.LPSPI_SR_TEF |
    63  		nxp.LPSPI_SR_REF | nxp.LPSPI_SR_DMF | nxp.LPSPI_SR_MBF
    64  )
    65  
    66  var (
    67  	errSPINotConfigured = errors.New("SPI interface is not yet configured")
    68  )
    69  
    70  // Configure is intended to setup an SPI interface for transmit/receive.
    71  func (spi *SPI) Configure(config SPIConfig) error {
    72  
    73  	const defaultSpiFreq = 4000000 // 4 MHz
    74  
    75  	// init pins
    76  	spi.sdi, spi.sdo, spi.sck, spi.cs = config.getPins()
    77  
    78  	// configure the mux and pad control registers
    79  	spi.sdi.Configure(PinConfig{Mode: PinModeSPISDI})
    80  	spi.sdo.Configure(PinConfig{Mode: PinModeSPISDO})
    81  	spi.sck.Configure(PinConfig{Mode: PinModeSPICLK})
    82  	spi.cs.Configure(PinConfig{Mode: PinModeSPICS})
    83  
    84  	// configure the mux input selector
    85  	spi.muxSDI.connect()
    86  	spi.muxSDO.connect()
    87  	spi.muxSCK.connect()
    88  	spi.muxCS.connect()
    89  
    90  	// software reset of LPSPI state registers
    91  	spi.Bus.CR.SetBits(nxp.LPSPI_CR_RST)
    92  	// also reset FIFOs (not performed by software reset above)
    93  	spi.Bus.CR.SetBits(nxp.LPSPI_CR_RRF | nxp.LPSPI_CR_RTF)
    94  	spi.Bus.CR.Set(0)
    95  
    96  	// set controller mode, and input data is sampled on delayed SCK edge
    97  	spi.Bus.CFGR1.Set(nxp.LPSPI_CFGR1_MASTER | nxp.LPSPI_CFGR1_SAMPLE)
    98  
    99  	spi.frequency = config.Frequency
   100  	if 0 == spi.frequency {
   101  		spi.frequency = defaultSpiFreq
   102  	}
   103  
   104  	// configure LPSPI clock divisor and CS assertion delays
   105  	div := spi.getClockDivisor(config.Frequency)
   106  	ccr := (div << nxp.LPSPI_CCR_SCKDIV_Pos) & nxp.LPSPI_CCR_SCKDIV_Msk
   107  	ccr |= ((div / 2) << nxp.LPSPI_CCR_DBT_Pos) & nxp.LPSPI_CCR_DBT_Msk
   108  	ccr |= ((div / 2) << nxp.LPSPI_CCR_PCSSCK_Pos) & nxp.LPSPI_CCR_PCSSCK_Msk
   109  	spi.Bus.CCR.Set(ccr)
   110  
   111  	// 8-bit frame size (words)
   112  	tcr := uint32(7)
   113  	if config.LSBFirst {
   114  		tcr |= nxp.LPSPI_TCR_LSBF
   115  	}
   116  	// set polarity and phase
   117  	switch config.Mode {
   118  	case Mode1:
   119  		tcr |= nxp.LPSPI_TCR_CPHA
   120  	case Mode2:
   121  		tcr |= nxp.LPSPI_TCR_CPOL
   122  	case Mode3:
   123  		tcr |= nxp.LPSPI_TCR_CPOL
   124  		tcr |= nxp.LPSPI_TCR_CPHA
   125  	}
   126  	spi.Bus.TCR.Set(tcr)
   127  
   128  	// clear FIFO water marks
   129  	spi.setWatermark(0, 0)
   130  
   131  	// enable LPSPI module
   132  	spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
   133  
   134  	spi.configured = true
   135  
   136  	return nil
   137  }
   138  
   139  // Transfer writes/reads a single byte using the SPI interface.
   140  func (spi *SPI) Transfer(w byte) (byte, error) {
   141  	if !spi.configured {
   142  		return 0, errSPINotConfigured
   143  	}
   144  
   145  	const readTryMax = 10000
   146  
   147  	for spi.Bus.SR.HasBits(statusModuleBusy) {
   148  	} // wait for SPI busy bit to clear
   149  
   150  	_, txFIFOSize := spi.getFIFOSize()
   151  
   152  	spi.flushFIFO(true, true)
   153  	spi.Bus.SR.Set(statusAll) // clear all status flags (W1C)
   154  
   155  	// enable LPSPI module
   156  	spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
   157  
   158  	// TODO: unnecessary since we just flushed the FIFO?
   159  	for { // wait for TX FIFO to not be full
   160  		if _, txFIFO := spi.getFIFOCount(); txFIFO < txFIFOSize {
   161  			break
   162  		}
   163  	}
   164  
   165  	// write out byte to TX FIFO
   166  	spi.Bus.TDR.Set(uint32(w))
   167  
   168  	// try to read from RX FIFO if anything exists
   169  	didRead := false
   170  	data := byte(0)
   171  	for i := 0; !didRead && (i < readTryMax); i++ {
   172  		rxFIFO, _ := spi.getFIFOCount()
   173  		didRead = rxFIFO > 0
   174  		if didRead {
   175  			data = byte(spi.Bus.RDR.Get())
   176  		}
   177  	}
   178  
   179  	// if nothing was read, then wait for transfer complete flag to decide when
   180  	// we are finished
   181  	if !didRead {
   182  		for !spi.Bus.SR.HasBits(nxp.LPSPI_SR_TCF) {
   183  		} // wait for all transfers complete flag to set
   184  	}
   185  
   186  	return data, nil
   187  }
   188  
   189  func (spi *SPI) isHardwareCSPin(pin Pin) bool {
   190  	switch unsafe.Pointer(spi.Bus) {
   191  	case unsafe.Pointer(nxp.LPSPI1):
   192  		return SPI1_CS_PIN == pin
   193  	case unsafe.Pointer(nxp.LPSPI2):
   194  		return SPI2_CS_PIN == pin
   195  	case unsafe.Pointer(nxp.LPSPI3):
   196  		return SPI3_CS_PIN == pin
   197  	}
   198  	return false
   199  }
   200  
   201  func (spi *SPI) hasHardwareCSPin() bool {
   202  	return spi.isHardwareCSPin(spi.cs)
   203  }
   204  
   205  // getClockDivisor finds the SPI prescalar that minimizes the error between
   206  // requested frequency and possible frequencies available with the LPSPI clock.
   207  // this routine is based on Teensyduino (libraries/SPI/SPI.cpp):
   208  //
   209  //	void SPIClass::setClockDivider_noInline(uint32_t clk)
   210  func (spi *SPI) getClockDivisor(freq uint32) uint32 {
   211  	const clock = 132000000 // LPSPI root clock frequency (PLL2)
   212  	d := uint32(clock)
   213  	if freq > 0 {
   214  		d /= freq
   215  	}
   216  	if d > 0 && clock/d > freq {
   217  		d++
   218  	}
   219  	if d > 257 {
   220  		return 255
   221  	}
   222  	if d > 2 {
   223  		return d - 2
   224  	}
   225  	return 0
   226  }
   227  
   228  func (spi *SPI) getFIFOSize() (rx, tx uint32) {
   229  	param := spi.Bus.PARAM.Get()
   230  	return uint32(1) << ((param & nxp.LPSPI_PARAM_RXFIFO_Msk) >> nxp.LPSPI_PARAM_RXFIFO_Pos),
   231  		uint32(1) << ((param & nxp.LPSPI_PARAM_TXFIFO_Msk) >> nxp.LPSPI_PARAM_TXFIFO_Pos)
   232  }
   233  
   234  func (spi *SPI) getFIFOCount() (rx, tx uint32) {
   235  	fsr := spi.Bus.FSR.Get()
   236  	return (fsr & nxp.LPSPI_FSR_RXCOUNT_Msk) >> nxp.LPSPI_FSR_RXCOUNT_Pos,
   237  		(fsr & nxp.LPSPI_FSR_TXCOUNT_Msk) >> nxp.LPSPI_FSR_TXCOUNT_Pos
   238  }
   239  
   240  func (spi *SPI) flushFIFO(rx, tx bool) {
   241  	var flush uint32
   242  	if rx {
   243  		flush |= nxp.LPSPI_CR_RRF
   244  	}
   245  	if tx {
   246  		flush |= nxp.LPSPI_CR_RTF
   247  	}
   248  	spi.Bus.CR.SetBits(flush)
   249  }
   250  
   251  func (spi *SPI) setWatermark(rx, tx uint32) {
   252  	spi.Bus.FCR.Set(((rx << nxp.LPSPI_FCR_RXWATER_Pos) & nxp.LPSPI_FCR_RXWATER_Msk) |
   253  		((tx << nxp.LPSPI_FCR_TXWATER_Pos) & nxp.LPSPI_FCR_TXWATER_Msk))
   254  }