From 0cabd4de69ccb0f1468e8e502ee4a01b84276837 Mon Sep 17 00:00:00 2001 From: sago35 Date: Thu, 11 Mar 2021 21:07:01 +0900 Subject: [PATCH] atsamd5x: improve SPI --- src/machine/machine_atsamd51.go | 91 +++++++++++++++++++++++++++++++++ src/machine/spi.go | 2 +- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go index 736826bb..a7ee6b56 100644 --- a/src/machine/machine_atsamd51.go +++ b/src/machine/machine_atsamd51.go @@ -10,6 +10,7 @@ package machine import ( "device/arm" "device/sam" + "errors" "runtime/interrupt" "runtime/volatile" "unsafe" @@ -1469,6 +1470,96 @@ func (spi SPI) Transfer(w byte) (byte, error) { return byte(spi.Bus.DATA.Get()), nil } +var ( + ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size") +) + +// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read +// interface, there must always be the same number of bytes written as bytes read. +// The Tx method knows about this, and offers a few different ways of calling it. +// +// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer. +// Note that the tx and rx buffers must be the same size: +// +// spi.Tx(tx, rx) +// +// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros +// until all the bytes in the command packet have been received: +// +// spi.Tx(tx, nil) +// +// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet": +// +// spi.Tx(nil, rx) +// +func (spi SPI) Tx(w, r []byte) error { + switch { + case w == nil: + // read only, so write zero and read a result. + spi.rx(r) + case r == nil: + // write only + spi.tx(w) + + default: + // write/read + if len(w) != len(r) { + return ErrTxInvalidSliceSize + } + + spi.txrx(w, r) + } + + return nil +} + +func (spi SPI) tx(tx []byte) { + for i := 0; i < len(tx); i++ { + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) { + } + spi.Bus.DATA.Set(uint32(tx[i])) + } + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_TXC) { + } + + // read to clear RXC register + for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) { + spi.Bus.DATA.Get() + } +} + +func (spi SPI) rx(rx []byte) { + spi.Bus.DATA.Set(0) + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) { + } + + for i := 1; i < len(rx); i++ { + spi.Bus.DATA.Set(0) + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) { + } + rx[i-1] = byte(spi.Bus.DATA.Get()) + } + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) { + } + rx[len(rx)-1] = byte(spi.Bus.DATA.Get()) +} + +func (spi SPI) txrx(tx, rx []byte) { + spi.Bus.DATA.Set(uint32(tx[0])) + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) { + } + + for i := 1; i < len(rx); i++ { + spi.Bus.DATA.Set(uint32(tx[i])) + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) { + } + rx[i-1] = byte(spi.Bus.DATA.Get()) + } + for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) { + } + rx[len(rx)-1] = byte(spi.Bus.DATA.Get()) +} + // The QSPI peripheral on ATSAMD51 is only available on the following pins const ( QSPI_SCK = PB10 diff --git a/src/machine/spi.go b/src/machine/spi.go index 238a5d0b..35b7ccd0 100644 --- a/src/machine/spi.go +++ b/src/machine/spi.go @@ -1,4 +1,4 @@ -// +build !baremetal sam stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega +// +build !baremetal atsamd21 stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega package machine