родитель
8fb93fbac4
коммит
c5de68622e
3 изменённых файлов: 319 добавлений и 2 удалений
|
@ -237,6 +237,8 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// #==================================================================#
|
||||||
|
// | SPI |
|
||||||
// #===========#==========#===============#===========================#
|
// #===========#==========#===============#===========================#
|
||||||
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
|
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
|
||||||
// #===========#==========#===============#=================-=========#
|
// #===========#==========#===============#=================-=========#
|
||||||
|
@ -261,6 +263,70 @@ const (
|
||||||
SPI3_CS_PIN = D36
|
SPI3_CS_PIN = D36
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SPI0 = SPI1 // SPI0 is an alias of SPI1 (LPSPI4)
|
||||||
|
SPI1 = &_SPI1
|
||||||
|
_SPI1 = SPI{
|
||||||
|
Bus: nxp.LPSPI4,
|
||||||
|
muxSDI: muxSelect{ // D12 (PB1 [B0_01])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_SDI_SELECT_INPUT_DAISY_GPIO_B0_01_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_SDI_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSDO: muxSelect{ // D11 (PB2 [B0_02])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_SDO_SELECT_INPUT_DAISY_GPIO_B0_02_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_SDO_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSCK: muxSelect{ // D13 (PB3 [B0_03])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_SCK_SELECT_INPUT_DAISY_GPIO_B0_03_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_SCK_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxCS: muxSelect{ // D10 (PB0 [B0_00])
|
||||||
|
mux: nxp.IOMUXC_LPSPI4_PCS0_SELECT_INPUT_DAISY_GPIO_B0_00_ALT3,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI4_PCS0_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
SPI2 = &_SPI2
|
||||||
|
_SPI2 = SPI{
|
||||||
|
Bus: nxp.LPSPI3,
|
||||||
|
muxSDI: muxSelect{ // D1 (PA2 [AD_B0_02])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_SDI_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT7,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_SDI_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSDO: muxSelect{ // D26 (PA30 [AD_B1_14])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_SDO_SELECT_INPUT_DAISY_GPIO_AD_B1_14_ALT2,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_SDO_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSCK: muxSelect{ // D27 (PA31 [AD_B1_15])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_SCK_SELECT_INPUT_DAISY_GPIO_AD_B1_15,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_SCK_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxCS: muxSelect{ // D0 (PA3 [AD_B0_03])
|
||||||
|
mux: nxp.IOMUXC_LPSPI3_PCS0_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT7,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI3_PCS0_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
SPI3 = &_SPI3
|
||||||
|
_SPI3 = SPI{
|
||||||
|
Bus: nxp.LPSPI1,
|
||||||
|
muxSDI: muxSelect{ // D34 (PC15 [SD_B0_03])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_SDI_SELECT_INPUT_DAISY_GPIO_SD_B0_03_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_SDI_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSDO: muxSelect{ // D35 (PC14 [SD_B0_02])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_SDO_SELECT_INPUT_DAISY_GPIO_SD_B0_02_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_SDO_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxSCK: muxSelect{ // D37 (PC12 [SD_B0_00])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_SCK_SELECT_INPUT_DAISY_GPIO_SD_B0_00_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_SCK_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
muxCS: muxSelect{ // D36 (PC13 [SD_B0_01])
|
||||||
|
mux: nxp.IOMUXC_LPSPI1_PCS0_SELECT_INPUT_DAISY_GPIO_SD_B0_01_ALT4,
|
||||||
|
sel: &nxp.IOMUXC.LPSPI1_PCS0_SELECT_INPUT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// #====================================================#
|
// #====================================================#
|
||||||
// | I2C |
|
// | I2C |
|
||||||
// #===========#==========#=============#===============#
|
// #===========#==========#=============#===============#
|
||||||
|
|
251
src/machine/machine_mimxrt1062_spi.go
Обычный файл
251
src/machine/machine_mimxrt1062_spi.go
Обычный файл
|
@ -0,0 +1,251 @@
|
||||||
|
// +build mimxrt1062
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// SPI peripheral abstraction layer for the MIMXRT1062
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/nxp"
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPIConfig is used to store config info for SPI.
|
||||||
|
type SPIConfig struct {
|
||||||
|
Frequency uint32
|
||||||
|
SDI Pin
|
||||||
|
SDO Pin
|
||||||
|
SCK Pin
|
||||||
|
CS Pin
|
||||||
|
LSBFirst bool
|
||||||
|
Mode uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SPIConfig) getPins() (di, do, ck, cs Pin) {
|
||||||
|
if 0 == c.SDI && 0 == c.SDO && 0 == c.SCK && 0 == c.CS {
|
||||||
|
// default pins if none specified
|
||||||
|
return SPI_SDI_PIN, SPI_SDO_PIN, SPI_SCK_PIN, SPI_CS_PIN
|
||||||
|
}
|
||||||
|
return c.SDI, c.SDO, c.SCK, c.CS
|
||||||
|
}
|
||||||
|
|
||||||
|
type SPI struct {
|
||||||
|
Bus *nxp.LPSPI_Type
|
||||||
|
|
||||||
|
// these hold the input selector ("daisy chain") values that select which pins
|
||||||
|
// are connected to the LPSPI device, and should be defined where the SPI
|
||||||
|
// instance is declared (e.g., in the board definition). see the godoc
|
||||||
|
// comments on type muxSelect for more details.
|
||||||
|
muxSDI, muxSDO, muxSCK, muxCS muxSelect
|
||||||
|
|
||||||
|
// these are copied from SPIConfig, during (*SPI).Configure(SPIConfig), and
|
||||||
|
// should be considered read-only for internal reference (i.e., modifying them
|
||||||
|
// will have no desirable effect).
|
||||||
|
sdi, sdo, sck, cs Pin
|
||||||
|
frequency uint32
|
||||||
|
|
||||||
|
// auxiliary state data used internally
|
||||||
|
configured bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
statusTxDataRequest = nxp.LPSPI_SR_TDF // Transmit data flag
|
||||||
|
statusRxDataReady = nxp.LPSPI_SR_RDF // Receive data flag
|
||||||
|
statusWordComplete = nxp.LPSPI_SR_WCF // Word Complete flag
|
||||||
|
statusFrameComplete = nxp.LPSPI_SR_FCF // Frame Complete flag
|
||||||
|
statusTransferComplete = nxp.LPSPI_SR_TCF // Transfer Complete flag
|
||||||
|
statusTransmitError = nxp.LPSPI_SR_TEF // Transmit Error flag (FIFO underrun)
|
||||||
|
statusReceiveError = nxp.LPSPI_SR_REF // Receive Error flag (FIFO overrun)
|
||||||
|
statusDataMatch = nxp.LPSPI_SR_DMF // Data Match flag
|
||||||
|
statusModuleBusy = nxp.LPSPI_SR_MBF // Module Busy flag
|
||||||
|
statusAll = nxp.LPSPI_SR_TDF | nxp.LPSPI_SR_RDF |
|
||||||
|
nxp.LPSPI_SR_WCF | nxp.LPSPI_SR_FCF | nxp.LPSPI_SR_TCF | nxp.LPSPI_SR_TEF |
|
||||||
|
nxp.LPSPI_SR_REF | nxp.LPSPI_SR_DMF | nxp.LPSPI_SR_MBF
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errSPINotConfigured = errors.New("SPI interface is not yet configured")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure is intended to setup an SPI interface for transmit/receive.
|
||||||
|
func (spi *SPI) Configure(config SPIConfig) {
|
||||||
|
|
||||||
|
const defaultSpiFreq = 4000000 // 4 MHz
|
||||||
|
|
||||||
|
// init pins
|
||||||
|
spi.sdi, spi.sdo, spi.sck, spi.cs = config.getPins()
|
||||||
|
|
||||||
|
// configure the mux and pad control registers
|
||||||
|
spi.sdi.Configure(PinConfig{Mode: PinModeSPISDI})
|
||||||
|
spi.sdo.Configure(PinConfig{Mode: PinModeSPISDO})
|
||||||
|
spi.sck.Configure(PinConfig{Mode: PinModeSPICLK})
|
||||||
|
spi.cs.Configure(PinConfig{Mode: PinModeSPICS})
|
||||||
|
|
||||||
|
// configure the mux input selector
|
||||||
|
spi.muxSDI.connect()
|
||||||
|
spi.muxSDO.connect()
|
||||||
|
spi.muxSCK.connect()
|
||||||
|
spi.muxCS.connect()
|
||||||
|
|
||||||
|
// software reset of LPSPI state registers
|
||||||
|
spi.Bus.CR.SetBits(nxp.LPSPI_CR_RST)
|
||||||
|
// also reset FIFOs (not performed by software reset above)
|
||||||
|
spi.Bus.CR.SetBits(nxp.LPSPI_CR_RRF | nxp.LPSPI_CR_RTF)
|
||||||
|
spi.Bus.CR.Set(0)
|
||||||
|
|
||||||
|
// set controller mode, and input data is sampled on delayed SCK edge
|
||||||
|
spi.Bus.CFGR1.Set(nxp.LPSPI_CFGR1_MASTER | nxp.LPSPI_CFGR1_SAMPLE)
|
||||||
|
|
||||||
|
spi.frequency = config.Frequency
|
||||||
|
if 0 == spi.frequency {
|
||||||
|
spi.frequency = defaultSpiFreq
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure LPSPI clock divisor and CS assertion delays
|
||||||
|
div := spi.getClockDivisor(config.Frequency)
|
||||||
|
ccr := (div << nxp.LPSPI_CCR_SCKDIV_Pos) & nxp.LPSPI_CCR_SCKDIV_Msk
|
||||||
|
ccr |= ((div / 2) << nxp.LPSPI_CCR_DBT_Pos) & nxp.LPSPI_CCR_DBT_Msk
|
||||||
|
ccr |= ((div / 2) << nxp.LPSPI_CCR_PCSSCK_Pos) & nxp.LPSPI_CCR_PCSSCK_Msk
|
||||||
|
spi.Bus.CCR.Set(ccr)
|
||||||
|
|
||||||
|
// 8-bit frame size (words)
|
||||||
|
tcr := uint32(7)
|
||||||
|
if config.LSBFirst {
|
||||||
|
tcr |= nxp.LPSPI_TCR_LSBF
|
||||||
|
}
|
||||||
|
// set polarity and phase
|
||||||
|
switch config.Mode {
|
||||||
|
case Mode1:
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPHA
|
||||||
|
case Mode2:
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPOL
|
||||||
|
case Mode3:
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPOL
|
||||||
|
tcr |= nxp.LPSPI_TCR_CPHA
|
||||||
|
}
|
||||||
|
spi.Bus.TCR.Set(tcr)
|
||||||
|
|
||||||
|
// clear FIFO water marks
|
||||||
|
spi.setWatermark(0, 0)
|
||||||
|
|
||||||
|
// enable LPSPI module
|
||||||
|
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
|
||||||
|
|
||||||
|
spi.configured = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer writes/reads a single byte using the SPI interface.
|
||||||
|
func (spi *SPI) Transfer(w byte) (byte, error) {
|
||||||
|
if !spi.configured {
|
||||||
|
return 0, errSPINotConfigured
|
||||||
|
}
|
||||||
|
|
||||||
|
const readTryMax = 10000
|
||||||
|
|
||||||
|
for spi.Bus.SR.HasBits(statusModuleBusy) {
|
||||||
|
} // wait for SPI busy bit to clear
|
||||||
|
|
||||||
|
_, txFIFOSize := spi.getFIFOSize()
|
||||||
|
|
||||||
|
spi.flushFIFO(true, true)
|
||||||
|
spi.Bus.SR.Set(statusAll) // clear all status flags (W1C)
|
||||||
|
|
||||||
|
// enable LPSPI module
|
||||||
|
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
|
||||||
|
|
||||||
|
// TODO: unnecessary since we just flushed the FIFO?
|
||||||
|
for { // wait for TX FIFO to not be full
|
||||||
|
if _, txFIFO := spi.getFIFOCount(); txFIFO < txFIFOSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out byte to TX FIFO
|
||||||
|
spi.Bus.TDR.Set(uint32(w))
|
||||||
|
|
||||||
|
// try to read from RX FIFO if anything exists
|
||||||
|
didRead := false
|
||||||
|
data := byte(0)
|
||||||
|
for i := 0; !didRead && (i < readTryMax); i++ {
|
||||||
|
rxFIFO, _ := spi.getFIFOCount()
|
||||||
|
didRead = rxFIFO > 0
|
||||||
|
if didRead {
|
||||||
|
data = byte(spi.Bus.RDR.Get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if nothing was read, then wait for transfer complete flag to decide when
|
||||||
|
// we are finished
|
||||||
|
if !didRead {
|
||||||
|
for !spi.Bus.SR.HasBits(nxp.LPSPI_SR_TCF) {
|
||||||
|
} // wait for all transfers complete flag to set
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) isHardwareCSPin(pin Pin) bool {
|
||||||
|
switch unsafe.Pointer(spi.Bus) {
|
||||||
|
case unsafe.Pointer(nxp.LPSPI1):
|
||||||
|
return SPI1_CS_PIN == pin
|
||||||
|
case unsafe.Pointer(nxp.LPSPI2):
|
||||||
|
return SPI2_CS_PIN == pin
|
||||||
|
case unsafe.Pointer(nxp.LPSPI3):
|
||||||
|
return SPI3_CS_PIN == pin
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) hasHardwareCSPin() bool {
|
||||||
|
return spi.isHardwareCSPin(spi.cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClockDivisor finds the SPI prescalar that minimizes the error between
|
||||||
|
// requested frequency and possible frequencies available with the LPSPI clock.
|
||||||
|
// this routine is based on Teensyduino (libraries/SPI/SPI.cpp):
|
||||||
|
// `void SPIClass::setClockDivider_noInline(uint32_t clk)`
|
||||||
|
func (spi *SPI) getClockDivisor(freq uint32) uint32 {
|
||||||
|
const clock = 132000000 // LPSPI root clock frequency (PLL2)
|
||||||
|
d := uint32(clock)
|
||||||
|
if freq > 0 {
|
||||||
|
d /= freq
|
||||||
|
}
|
||||||
|
if d > 0 && clock/d > freq {
|
||||||
|
d++
|
||||||
|
}
|
||||||
|
if d > 257 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
if d > 2 {
|
||||||
|
return d - 2
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) getFIFOSize() (rx, tx uint32) {
|
||||||
|
param := spi.Bus.PARAM.Get()
|
||||||
|
return uint32(1) << ((param & nxp.LPSPI_PARAM_RXFIFO_Msk) >> nxp.LPSPI_PARAM_RXFIFO_Pos),
|
||||||
|
uint32(1) << ((param & nxp.LPSPI_PARAM_TXFIFO_Msk) >> nxp.LPSPI_PARAM_TXFIFO_Pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) getFIFOCount() (rx, tx uint32) {
|
||||||
|
fsr := spi.Bus.FSR.Get()
|
||||||
|
return (fsr & nxp.LPSPI_FSR_RXCOUNT_Msk) >> nxp.LPSPI_FSR_RXCOUNT_Pos,
|
||||||
|
(fsr & nxp.LPSPI_FSR_TXCOUNT_Msk) >> nxp.LPSPI_FSR_TXCOUNT_Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) flushFIFO(rx, tx bool) {
|
||||||
|
var flush uint32
|
||||||
|
if rx {
|
||||||
|
flush |= nxp.LPSPI_CR_RRF
|
||||||
|
}
|
||||||
|
if tx {
|
||||||
|
flush |= nxp.LPSPI_CR_RTF
|
||||||
|
}
|
||||||
|
spi.Bus.CR.SetBits(flush)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi *SPI) setWatermark(rx, tx uint32) {
|
||||||
|
spi.Bus.FCR.Set(((rx << nxp.LPSPI_FCR_RXWATER_Pos) & nxp.LPSPI_FCR_RXWATER_Msk) |
|
||||||
|
((tx << nxp.LPSPI_FCR_TXWATER_Pos) & nxp.LPSPI_FCR_TXWATER_Msk))
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build !baremetal || (stm32 && !stm32f7x2 && !stm32l5x2) || fe310 || k210 || atmega
|
//go:build !baremetal || (stm32 && !stm32f7x2 && !stm32l5x2) || fe310 || k210 || (nxp && !mk66f18) || atmega
|
||||||
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega
|
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2 fe310 k210 nxp,!mk66f18 atmega
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче