родитель
8fb93fbac4
коммит
c5de68622e
3 изменённых файлов: 319 добавлений и 2 удалений
|
@ -237,6 +237,8 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// #==================================================================#
|
||||
// | SPI |
|
||||
// #===========#==========#===============#===========================#
|
||||
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
|
||||
// #===========#==========#===============#=================-=========#
|
||||
|
@ -261,6 +263,70 @@ const (
|
|||
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 |
|
||||
// #===========#==========#=============#===============#
|
||||
|
|
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
|
||||
// +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 nxp,!mk66f18 atmega
|
||||
|
||||
package machine
|
||||
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче