mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 07:23:39 +00:00
samd51: add support for async SPI using DMA
This commit is contained in:
@@ -683,6 +683,56 @@ func (p Pin) getPinGrouping() (uint8, uint8) {
|
|||||||
return group, pin_in_group
|
return group, pin_in_group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Static DMA channel allocation.
|
||||||
|
// If there are a lot of DMA using peripherals, we might need to switch to
|
||||||
|
// dynamic allocation instead.
|
||||||
|
const (
|
||||||
|
dmaChannelSERCOM0 = iota
|
||||||
|
dmaChannelSERCOM1
|
||||||
|
dmaChannelSERCOM2
|
||||||
|
dmaChannelSERCOM3
|
||||||
|
dmaChannelSERCOM4
|
||||||
|
dmaChannelSERCOM5
|
||||||
|
dmaChannelSERCOM6
|
||||||
|
dmaChannelSERCOM7
|
||||||
|
dmaNumChannels
|
||||||
|
)
|
||||||
|
|
||||||
|
// DMA descriptor structure. This structure is defined by the hardware, and is
|
||||||
|
// described by "22.9 Register Summary - SRAM" in the datasheet.
|
||||||
|
type dmaDescriptor struct {
|
||||||
|
btctrl uint16
|
||||||
|
btcnt uint16
|
||||||
|
srcaddr unsafe.Pointer
|
||||||
|
dstaddr unsafe.Pointer
|
||||||
|
descaddr unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:align 16
|
||||||
|
var dmaDescriptorSection [dmaNumChannels]dmaDescriptor
|
||||||
|
|
||||||
|
//go:align 16
|
||||||
|
var dmaDescriptorWritebackSection [dmaNumChannels]dmaDescriptor
|
||||||
|
|
||||||
|
// Enable and configure the DMAC peripheral if it hasn't been enabled already.
|
||||||
|
func enableDMAC() {
|
||||||
|
if !sam.DMAC.CTRL.HasBits(sam.DMAC_CTRL_DMAENABLE) {
|
||||||
|
// Init DMAC.
|
||||||
|
// First configure the clocks, then configure the DMA descriptors. Those
|
||||||
|
// descriptors must live in SRAM and must be aligned on a 16-byte
|
||||||
|
// boundary.
|
||||||
|
// Some examples:
|
||||||
|
// http://www.lucadavidian.com/2018/03/08/wifi-controlled-neo-pixels-strips/
|
||||||
|
// https://svn.larosterna.com/oss/trunk/arduino/zerotimer/zerodma.cpp
|
||||||
|
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_DMAC_)
|
||||||
|
sam.DMAC.BASEADDR.Set(uint32(uintptr(unsafe.Pointer(&dmaDescriptorSection))))
|
||||||
|
sam.DMAC.WRBADDR.Set(uint32(uintptr(unsafe.Pointer(&dmaDescriptorWritebackSection))))
|
||||||
|
|
||||||
|
// Enable peripheral with all priorities.
|
||||||
|
sam.DMAC.CTRL.SetBits(sam.DMAC_CTRL_DMAENABLE | sam.DMAC_CTRL_LVLEN0 | sam.DMAC_CTRL_LVLEN1 | sam.DMAC_CTRL_LVLEN2 | sam.DMAC_CTRL_LVLEN3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// InitADC initializes the ADC.
|
// InitADC initializes the ADC.
|
||||||
func InitADC() {
|
func InitADC() {
|
||||||
// ADC Bias Calibration
|
// ADC Bias Calibration
|
||||||
@@ -1652,6 +1702,101 @@ func (spi SPI) txrx(tx, rx []byte) {
|
|||||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Channel to be used for SPI transfers.
|
||||||
|
// These channels are currently statically allocated.
|
||||||
|
func (spi SPI) dmaTxChannel() uint8 {
|
||||||
|
return dmaChannelSERCOM0 + spi.SERCOM
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAsync returns whether the SPI supports async operation (usually DMA).
|
||||||
|
//
|
||||||
|
// It returns true on the SAM D5x chips.
|
||||||
|
func (spi SPI) IsAsync() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a transfer in the background.
|
||||||
|
//
|
||||||
|
// After this, another StartTx() or Wait() must be called. The provided byte
|
||||||
|
// slices (tx and rx) may only be accessed again after Wait() was called.
|
||||||
|
func (s SPI) StartTx(tx, rx []byte) error {
|
||||||
|
// Check whether we support doing this transfer using DMA.
|
||||||
|
if len(rx) != 0 {
|
||||||
|
return s.Tx(tx, rx)
|
||||||
|
}
|
||||||
|
if len(tx) == 0 {
|
||||||
|
return nil // nothing to send/receive
|
||||||
|
}
|
||||||
|
if len(tx) != int(uint16(len(tx))) {
|
||||||
|
// The transfer size is a 16-bit field.
|
||||||
|
// TODO: chain multiple transfers in some way when encountering these
|
||||||
|
// large buffer sizes. They're not currently implemented because
|
||||||
|
// transferring 64kB of data is less commonly done.
|
||||||
|
return s.Tx(tx, rx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable DMAC (if not already enabled).
|
||||||
|
enableDMAC()
|
||||||
|
|
||||||
|
// Wait until a possible previous transfer has been completed.
|
||||||
|
s.Wait()
|
||||||
|
|
||||||
|
// Configure the DMA channel, if it hasn't been configured already.
|
||||||
|
dstaddr := unsafe.Pointer(&s.Bus.DATA.Reg)
|
||||||
|
if dmaDescriptorSection[s.dmaTxChannel()].dstaddr != dstaddr {
|
||||||
|
// Configure channel descriptor.
|
||||||
|
dmaDescriptorSection[s.dmaTxChannel()] = dmaDescriptor{
|
||||||
|
btctrl: (1 << 0) | // VALID: Descriptor Valid
|
||||||
|
(0 << 3) | // BLOCKACT=NOACT: Block Action
|
||||||
|
(1 << 10) | // SRCINC: Source Address Increment Enable
|
||||||
|
(0 << 11) | // DSTINC: Destination Address Increment Enable
|
||||||
|
(1 << 12) | // STEPSEL=SRC: Step Selection
|
||||||
|
(0 << 13), // STEPSIZE=X1: Address Increment Step Size
|
||||||
|
dstaddr: dstaddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset channel.
|
||||||
|
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.ClearBits(sam.DMAC_CHANNEL_CHCTRLA_ENABLE)
|
||||||
|
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.SetBits(sam.DMAC_CHANNEL_CHCTRLA_SWRST)
|
||||||
|
|
||||||
|
// Configure channel.
|
||||||
|
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHPRILVL.Set(0)
|
||||||
|
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.Set((sam.DMAC_CHANNEL_CHCTRLA_TRIGACT_BURST << sam.DMAC_CHANNEL_CHCTRLA_TRIGACT_Pos) |
|
||||||
|
(s.triggerSource() << sam.DMAC_CHANNEL_CHCTRLA_TRIGSRC_Pos) |
|
||||||
|
(sam.DMAC_CHANNEL_CHCTRLA_BURSTLEN_SINGLE << sam.DMAC_CHANNEL_CHCTRLA_BURSTLEN_Pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
// For some reason, you have to provide the address just past the end of the
|
||||||
|
// array instead of the address of the array.
|
||||||
|
descriptor := &dmaDescriptorSection[s.dmaTxChannel()]
|
||||||
|
descriptor.srcaddr = unsafe.Pointer(uintptr(unsafe.Pointer(&tx[0])) + uintptr(len(tx)))
|
||||||
|
descriptor.btcnt = uint16(len(tx)) // beat count
|
||||||
|
|
||||||
|
// Start the transfer.
|
||||||
|
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.SetBits(sam.DMAC_CHANNEL_CHCTRLA_ENABLE)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until all active transactions (started by StartTx) have finished. The
|
||||||
|
// buffers provided in StartTx will be available after this method returns.
|
||||||
|
func (spi SPI) Wait() error {
|
||||||
|
// Wait until the previous SPI transfer completed.
|
||||||
|
// This is basically the same thing as in SPI.tx.
|
||||||
|
|
||||||
|
// TODO: maybe block (and sleep) until the transfer has completed?
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// The QSPI peripheral on ATSAMD51 is only available on the following pins
|
// The QSPI peripheral on ATSAMD51 is only available on the following pins
|
||||||
const (
|
const (
|
||||||
QSPI_SCK = PB10
|
QSPI_SCK = PB10
|
||||||
|
|||||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA trigger source
|
||||||
|
func (spi SPI) triggerSource() (tx uint32) {
|
||||||
|
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||||
|
switch spi.Bus {
|
||||||
|
case sam.SERCOM0_SPIM:
|
||||||
|
return 0x05
|
||||||
|
case sam.SERCOM1_SPIM:
|
||||||
|
return 0x07
|
||||||
|
case sam.SERCOM2_SPIM:
|
||||||
|
return 0x09
|
||||||
|
case sam.SERCOM3_SPIM:
|
||||||
|
return 0x0A
|
||||||
|
case sam.SERCOM4_SPIM:
|
||||||
|
return 0x0C
|
||||||
|
case sam.SERCOM5_SPIM:
|
||||||
|
return 0x0E
|
||||||
|
default:
|
||||||
|
return 0 // should be unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This chip has three TCC peripherals, which have PWM as one feature.
|
// This chip has three TCC peripherals, which have PWM as one feature.
|
||||||
var (
|
var (
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
|||||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA trigger source
|
||||||
|
func (spi SPI) triggerSource() (tx uint32) {
|
||||||
|
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||||
|
switch spi.Bus {
|
||||||
|
case sam.SERCOM0_SPIM:
|
||||||
|
return 0x05
|
||||||
|
case sam.SERCOM1_SPIM:
|
||||||
|
return 0x07
|
||||||
|
case sam.SERCOM2_SPIM:
|
||||||
|
return 0x09
|
||||||
|
case sam.SERCOM3_SPIM:
|
||||||
|
return 0x0B
|
||||||
|
case sam.SERCOM4_SPIM:
|
||||||
|
return 0x0D
|
||||||
|
case sam.SERCOM5_SPIM:
|
||||||
|
return 0x0F
|
||||||
|
default:
|
||||||
|
return 0 // should be unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
var (
|
var (
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
|||||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA trigger source
|
||||||
|
func (spi SPI) triggerSource() (tx uint32) {
|
||||||
|
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||||
|
switch spi.Bus {
|
||||||
|
case sam.SERCOM0_SPIM:
|
||||||
|
return 0x05
|
||||||
|
case sam.SERCOM1_SPIM:
|
||||||
|
return 0x07
|
||||||
|
case sam.SERCOM2_SPIM:
|
||||||
|
return 0x09
|
||||||
|
case sam.SERCOM3_SPIM:
|
||||||
|
return 0x0A
|
||||||
|
case sam.SERCOM4_SPIM:
|
||||||
|
return 0x0C
|
||||||
|
case sam.SERCOM5_SPIM:
|
||||||
|
return 0x0E
|
||||||
|
default:
|
||||||
|
return 0 // should be unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
var (
|
var (
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
|||||||
@@ -76,6 +76,31 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA trigger source
|
||||||
|
func (spi SPI) triggerSource() (tx uint32) {
|
||||||
|
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||||
|
switch spi.Bus {
|
||||||
|
case sam.SERCOM0_SPIM:
|
||||||
|
return 0x05
|
||||||
|
case sam.SERCOM1_SPIM:
|
||||||
|
return 0x07
|
||||||
|
case sam.SERCOM2_SPIM:
|
||||||
|
return 0x09
|
||||||
|
case sam.SERCOM3_SPIM:
|
||||||
|
return 0x0A
|
||||||
|
case sam.SERCOM4_SPIM:
|
||||||
|
return 0x0C
|
||||||
|
case sam.SERCOM5_SPIM:
|
||||||
|
return 0x0E
|
||||||
|
case sam.SERCOM6_SPIM:
|
||||||
|
return 0x10
|
||||||
|
case sam.SERCOM7_SPIM:
|
||||||
|
return 0x12
|
||||||
|
default:
|
||||||
|
return 0 // should be unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
var (
|
var (
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
|||||||
@@ -76,6 +76,31 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA trigger source
|
||||||
|
func (spi SPI) triggerSource() (tx uint32) {
|
||||||
|
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||||
|
switch spi.Bus {
|
||||||
|
case sam.SERCOM0_SPIM:
|
||||||
|
return 0x05
|
||||||
|
case sam.SERCOM1_SPIM:
|
||||||
|
return 0x07
|
||||||
|
case sam.SERCOM2_SPIM:
|
||||||
|
return 0x09
|
||||||
|
case sam.SERCOM3_SPIM:
|
||||||
|
return 0x0A
|
||||||
|
case sam.SERCOM4_SPIM:
|
||||||
|
return 0x0C
|
||||||
|
case sam.SERCOM5_SPIM:
|
||||||
|
return 0x0E
|
||||||
|
case sam.SERCOM6_SPIM:
|
||||||
|
return 0x10
|
||||||
|
case sam.SERCOM7_SPIM:
|
||||||
|
return 0x12
|
||||||
|
default:
|
||||||
|
return 0 // should be unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
var (
|
var (
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
|||||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA trigger source
|
||||||
|
func (spi SPI) triggerSource() (tx uint32) {
|
||||||
|
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||||
|
switch spi.Bus {
|
||||||
|
case sam.SERCOM0_SPIM:
|
||||||
|
return 0x05
|
||||||
|
case sam.SERCOM1_SPIM:
|
||||||
|
return 0x07
|
||||||
|
case sam.SERCOM2_SPIM:
|
||||||
|
return 0x09
|
||||||
|
case sam.SERCOM3_SPIM:
|
||||||
|
return 0x0A
|
||||||
|
case sam.SERCOM4_SPIM:
|
||||||
|
return 0x0C
|
||||||
|
case sam.SERCOM5_SPIM:
|
||||||
|
return 0x0E
|
||||||
|
default:
|
||||||
|
return 0 // should be unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
var (
|
var (
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
|||||||
@@ -76,6 +76,31 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA trigger source
|
||||||
|
func (spi SPI) triggerSource() (tx uint32) {
|
||||||
|
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||||
|
switch spi.Bus {
|
||||||
|
case sam.SERCOM0_SPIM:
|
||||||
|
return 0x05
|
||||||
|
case sam.SERCOM1_SPIM:
|
||||||
|
return 0x07
|
||||||
|
case sam.SERCOM2_SPIM:
|
||||||
|
return 0x09
|
||||||
|
case sam.SERCOM3_SPIM:
|
||||||
|
return 0x0A
|
||||||
|
case sam.SERCOM4_SPIM:
|
||||||
|
return 0x0C
|
||||||
|
case sam.SERCOM5_SPIM:
|
||||||
|
return 0x0E
|
||||||
|
case sam.SERCOM6_SPIM:
|
||||||
|
return 0x10
|
||||||
|
case sam.SERCOM7_SPIM:
|
||||||
|
return 0x12
|
||||||
|
default:
|
||||||
|
return 0 // should be unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||||
var (
|
var (
|
||||||
TCC0 = (*TCC)(sam.TCC0)
|
TCC0 = (*TCC)(sam.TCC0)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !baremetal || atmega || esp32 || fe310 || k210 || nrf || (nxp && !mk66f18) || sam || (stm32 && !stm32f7x2 && !stm32l5x2)
|
//go:build !baremetal || atmega || esp32 || fe310 || k210 || nrf || (nxp && !mk66f18) || atsamd21 || (stm32 && !stm32f7x2 && !stm32l5x2)
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user