DM: in progress i2s / dma support

This commit is contained in:
dean 2017-09-22 17:35:41 -04:00
parent 8a32178667
commit dc5464d328
5 changed files with 402 additions and 10 deletions

View file

@ -20,10 +20,21 @@
#include <wiring_private.h>
#include "utility/DMA.h"
#if defined(__SAMD51__)
#include "utility/SAMD51_I2SDevice.h"
static I2SDevice_SAMD51 i2sd(*I2S);
#else
#include "utility/SAMD21_I2SDevice.h"
static I2SDevice_SAMD21G18x i2sd(*I2S);
#endif
#include "I2S.h"
int I2SClass::_beginCount = 0;
@ -98,7 +109,11 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc
if (_beginCount == 0) {
// enable the I2S interface
#if defined(__SAMD51__)
MCLK->APBDMASK.reg |= MCLK_APBDMASK_I2S;
#else
PM->APBCMASK.reg |= PM_APBCMASK_I2S;
#endif
// reset the device
i2sd.reset();
@ -176,7 +191,11 @@ void I2SClass::end()
i2sd.disable();
// disable the I2S interface
#if defined(__SAMD51__)
MCLK->APBDMASK.reg &= ~(MCLK_APBDMASK_I2S);
#else
PM->APBCMASK.reg &= ~PM_APBCMASK_I2S;
#endif
}
}

View file

@ -39,9 +39,14 @@ DMAClass::~DMAClass()
void DMAClass::begin()
{
if (_beginCount == 0) {
#if defined(__SAMD51__)
MCLK->AHBMASK.bit.DMAC_ = 1;
#else
// enable the DMA interface
PM->AHBMASK.bit.DMAC_ = 1;
PM->APBBMASK.bit.DMAC_ = 1;
#endif
// perform a reset
DMAC->CTRL.bit.SWRST = 1;
@ -57,9 +62,35 @@ void DMAClass::begin()
DMAC->CTRL.bit.LVLEN3 = 1;
DMAC->CTRL.bit.DMAENABLE = 1;
// enable the interrupt at lowest priority
#if defined(__SAMD51__)
NVIC_DisableIRQ(DMAC_0_IRQn);
NVIC_ClearPendingIRQ(DMAC_0_IRQn);
NVIC_EnableIRQ(DMAC_0_IRQn);
NVIC_SetPriority(DMAC_0_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
NVIC_DisableIRQ(DMAC_1_IRQn);
NVIC_ClearPendingIRQ(DMAC_1_IRQn);
NVIC_EnableIRQ(DMAC_1_IRQn);
NVIC_SetPriority(DMAC_1_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
NVIC_DisableIRQ(DMAC_2_IRQn);
NVIC_ClearPendingIRQ(DMAC_2_IRQn);
NVIC_EnableIRQ(DMAC_2_IRQn);
NVIC_SetPriority(DMAC_2_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
NVIC_DisableIRQ(DMAC_3_IRQn);
NVIC_ClearPendingIRQ(DMAC_3_IRQn);
NVIC_EnableIRQ(DMAC_3_IRQn);
NVIC_SetPriority(DMAC_3_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
NVIC_DisableIRQ(DMAC_4_IRQn);
NVIC_ClearPendingIRQ(DMAC_4_IRQn);
NVIC_EnableIRQ(DMAC_4_IRQn);
NVIC_SetPriority(DMAC_4_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
#else
NVIC_EnableIRQ(DMAC_IRQn);
NVIC_SetPriority(DMAC_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
#endif
}
_beginCount++;
@ -71,14 +102,27 @@ void DMAClass::end()
if (_beginCount == 0) {
// disable the interrupt
#if defined(__SAMD51__)
NVIC_DisableIRQ(DMAC_0_IRQn);
NVIC_DisableIRQ(DMAC_1_IRQn);
NVIC_DisableIRQ(DMAC_2_IRQn);
NVIC_DisableIRQ(DMAC_3_IRQn);
NVIC_DisableIRQ(DMAC_4_IRQn);
#else
NVIC_DisableIRQ(DMAC_IRQn);
#endif
// disable
DMAC->CTRL.bit.DMAENABLE = 0;
// disable the DMA interface
PM->APBBMASK.bit.DMAC_ = 0;
#if defined(__SAMD51__)
MCLK->AHBMASK.bit.DMAC_ = 0;
#else
// enable the DMA interface
PM->AHBMASK.bit.DMAC_ = 0;
PM->APBBMASK.bit.DMAC_ = 0;
#endif
}
}
@ -96,9 +140,14 @@ int DMAClass::allocateChannel()
memset((void*)&_descriptors[i], 0x00, sizeof(_descriptors[i]));
// select the channel and reset it
#if defined(__SAMD51__)
DMAC->Channel[i].CHCTRLA.bit.ENABLE = 0;
DMAC->Channel[i].CHCTRLA.bit.SWRST = 1;
#else
DMAC->CHID.bit.ID = i;
DMAC->CHCTRLA.bit.ENABLE = 0;
DMAC->CHCTRLA.bit.SWRST = 1;
#endif
channel = i;
break;
@ -111,8 +160,12 @@ int DMAClass::allocateChannel()
void DMAClass::freeChannel(int channel)
{
// select the channel and disable it
DMAC->CHID.bit.ID = channel;
DMAC->CHCTRLA.bit.ENABLE = 0;
#if defined(__SAMD51__)
DMAC->Channel[channel].CHCTRLA.bit.ENABLE = 0;
#else
DMAC->CHID.bit.ID = channel;
DMAC->CHCTRLA.bit.ENABLE = 0;
#endif
_channelMask &= ~(1 << channel);
}
@ -120,12 +173,21 @@ void DMAClass::freeChannel(int channel)
void DMAClass::setPriorityLevel(int channel, int level)
{
// select the channel and set priority level
DMAC->CHID.bit.ID = channel;
DMAC->CHCTRLB.bit.LVL = level;
#if defined(__SAMD51__)
DMAC->Channel[channel].CHPRILVL.reg = level;
#else
DMAC->CHID.bit.ID = channel;
DMAC->CHCTRLB.bit.LVL = level;
#endif
}
void DMAClass::setTriggerSource(int channel, int source)
{
#if defined(__SAMD51__)
DMAC->Channel[channel].CHCTRLA.bit.TRIGSRC = source;
DMAC->Channel[channel].CHCTRLA.bit.TRIGACT = DMAC_CHCTRLA_TRIGACT_BLOCK_Val;
#else
// select the channel and set a trigger source
DMAC->CHID.bit.ID = channel;
DMAC->CHCTRLB.bit.TRIGSRC = source;
@ -136,6 +198,7 @@ void DMAClass::setTriggerSource(int channel, int source)
} else {
DMAC->CHCTRLB.bit.TRIGACT = DMAC_CHCTRLB_TRIGACT_BLOCK_Val;
}
#endif
}
void DMAClass::setTransferWidth(int channel, int transferWidth)
@ -178,8 +241,11 @@ int DMAClass::transfer(int channel, void* src, void* dst, uint16_t size)
return 1;
}
#if !defined(__SAMD51__)
// select the channel
DMAC->CHID.bit.ID = channel;
#endif
// disable event output generation and block actions
_descriptors[channel].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val;
@ -223,16 +289,26 @@ int DMAClass::transfer(int channel, void* src, void* dst, uint16_t size)
// validate the descriptor
_descriptors[channel].BTCTRL.bit.VALID = 1;
#if defined(__SAMD51__)
DMAC->Channel[channel].CHINTENSET.bit.TERR = 1;
DMAC->Channel[channel].CHINTENSET.bit.TCMPL = 1;
DMAC->Channel[channel].CHCTRLA.bit.ENABLE = 1;
if (DMAC->Channel[channel].CHCTRLA.bit.TRIGSRC == 0) {
// uses software trigger, so trigger it
DMAC->SWTRIGCTRL.reg |= (1 << channel);
}
#else
// enable channel and transfer error + complete interrupts
DMAC->CHINTENSET.bit.TERR = 1;
DMAC->CHINTENSET.bit.TCMPL = 1;
DMAC->CHCTRLA.bit.ENABLE = 1;
if (DMAC->CHCTRLB.bit.TRIGSRC == 0) {
// uses software trigger, so trigger it
DMAC->SWTRIGCTRL.reg |= (1 << channel);
}
#endif
return 0;
}
@ -251,11 +327,32 @@ void DMAClass::onService()
{
// get the channel and select it
int channel = DMAC->INTPEND.bit.ID;
#if !defined(__SAMD51__)
DMAC->CHID.bit.ID = channel;
#endif
// invalidate the channel
_descriptors[channel].BTCTRL.bit.VALID = 0;
#if defined(__SAMD51__)
if (DMAC->Channel[channel].CHINTFLAG.bit.TERR) {
// clear the error interrupt and call the error callback if there is one
DMAC->Channel[channel].CHINTFLAG.bit.TERR = 1;
if (_transferErrorCallbacks[channel]) {
_transferErrorCallbacks[channel](channel);
}
}
if (DMAC->Channel[channel].CHINTFLAG.bit.TCMPL) {
// clear the complete interrupt and call the callback if there is one
DMAC->Channel[channel].CHINTFLAG.bit.TCMPL = 1;
if (_transferCompleteCallbacks[channel]) {
_transferCompleteCallbacks[channel](channel);
}
}
#else
if (DMAC->CHINTFLAG.bit.TERR) {
// clear the error interrupt and call the error callback if there is one
DMAC->CHINTFLAG.bit.TERR = 1;
@ -273,12 +370,55 @@ void DMAClass::onService()
_transferCompleteCallbacks[channel](channel);
}
}
#endif
}
extern "C" {
#if defined(__SAMD51__)
static void _dmac_handler(void)
{
DMA.onService();
}
/**
* \brief DMAC interrupt handler
*/
void DMAC_0_Handler(void)
{
_dmac_handler();
}
/**
* \brief DMAC interrupt handler
*/
void DMAC_1_Handler(void)
{
_dmac_handler();
}
/**
* \brief DMAC interrupt handler
*/
void DMAC_2_Handler(void)
{
_dmac_handler();
}
/**
* \brief DMAC interrupt handler
*/
void DMAC_3_Handler(void)
{
_dmac_handler();
}
/**
* \brief DMAC interrupt handler
*/
void DMAC_4_Handler(void)
{
_dmac_handler();
}
#else
void DMAC_Handler() {
DMA.onService();
}
#endif
}
DMAClass DMA;

View file

@ -17,7 +17,11 @@
*/
#pragma once
#if defined(__SAMD51__)
#define NUM_DMA_CHANNELS 4
#else
#define NUM_DMA_CHANNELS 1
#endif
/*
WARNING: The API for this class may change and it's not intended for public use!

View file

@ -0,0 +1,229 @@
/*
Copyright (c) 2016 Arduino LLC. 2017 Adafruit All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <Arduino.h>
class I2SDevice_SAMD51 {
public:
I2SDevice_SAMD51(I2s& _i2s) :
i2s(_i2s)
{
// Empty
}
inline void reset() {
while(i2s.SYNCBUSY.bit.SWRST);
i2s.CTRLA.bit.SWRST = 1;
}
inline void disable() {
while(i2s.SYNCBUSY.bit.ENABLE);
i2s.CTRLA.bit.ENABLE = 0;
}
inline void enable() {
while(i2s.SYNCBUSY.bit.ENABLE);
i2s.CTRLA.bit.ENABLE = 1;
}
inline int glckId(int index) {
return (index == 0) ? I2S_GCLK_ID_0 : I2S_GCLK_ID_1;
}
inline void setSerialClockSelectMasterClockDiv(int index) {
i2s.CLKCTRL[index].bit.SCKSEL = I2S_CLKCTRL_SCKSEL_MCKDIV_Val;
}
inline void setSerialClockSelectPin(int index) {
i2s.CLKCTRL[index].bit.SCKSEL = I2S_CLKCTRL_SCKSEL_SCKPIN_Val;
}
inline void setFrameSyncSelectSerialClockDiv(int index) {
i2s.CLKCTRL[index].bit.FSSEL = I2S_CLKCTRL_FSSEL_SCKDIV_Val;
}
inline void setFrameSyncSelectPin(int index) {
i2s.CLKCTRL[index].bit.FSSEL = I2S_CLKCTRL_FSSEL_FSPIN_Val;
}
inline void set0BitDelay(int index) {
i2s.CLKCTRL[index].bit.BITDELAY = I2S_CLKCTRL_BITDELAY_LJ_Val;
}
inline void set1BitDelay(int index) {
i2s.CLKCTRL[index].bit.BITDELAY = I2S_CLKCTRL_BITDELAY_I2S_Val;
}
inline void setNumberOfSlots(int index, int nbslots) {
i2s.CLKCTRL[index].bit.NBSLOTS = nbslots;
}
inline void setSlotSize(int index, int size) {
switch (size) {
case 32:
i2s.CLKCTRL[index].bit.SLOTSIZE = I2S_CLKCTRL_SLOTSIZE_32_Val;
break;
case 24:
i2s.CLKCTRL[index].bit.SLOTSIZE = I2S_CLKCTRL_SLOTSIZE_24_Val;
break;
case 16:
i2s.CLKCTRL[index].bit.SLOTSIZE = I2S_CLKCTRL_SLOTSIZE_16_Val;
break;
case 8:
i2s.CLKCTRL[index].bit.SLOTSIZE = I2S_CLKCTRL_SLOTSIZE_8_Val;
break;
}
}
inline void setDataSize(int index, int size) {
switch (size) {
case 32:
i2s.RXCTRL.bit.DATASIZE = I2S_RXCTRL_DATASIZE_32_Val;
i2s.TXCTRL.bit.DATASIZE = I2S_TXCTRL_DATASIZE_32_Val;
break;
case 24:
i2s.RXCTRL.bit.DATASIZE = I2S_RXCTRL_DATASIZE_24_Val;
i2s.TXCTRL.bit.DATASIZE = I2S_TXCTRL_DATASIZE_24_Val;
break;
case 16:
i2s.RXCTRL.bit.DATASIZE = I2S_RXCTRL_DATASIZE_16_Val;
i2s.TXCTRL.bit.DATASIZE = I2S_TXCTRL_DATASIZE_16_Val;
break;
case 8:
i2s.RXCTRL.bit.DATASIZE = I2S_RXCTRL_DATASIZE_8_Val;
i2s.TXCTRL.bit.DATASIZE = I2S_TXCTRL_DATASIZE_8_Val;
break;
}
}
inline void setSlotAdjustedRight(int index) {
i2s.RXCTRL.bit.SLOTADJ = I2S_RXCTRL_SLOTADJ_RIGHT_Val;
i2s.TXCTRL.bit.SLOTADJ = I2S_TXCTRL_SLOTADJ_RIGHT_Val;
}
inline void setSlotAdjustedLeft(int index) {
i2s.RXCTRL.bit.SLOTADJ = I2S_RXCTRL_SLOTADJ_LEFT_Val;
i2s.TXCTRL.bit.SLOTADJ = I2S_TXCTRL_SLOTADJ_LEFT_Val;
}
inline void setClockUnit(int index) {
i2s.RXCTRL.bit.CLKSEL = (index == 0) ? I2S_RXCTRL_CLKSEL_CLK0_Val : I2S_RXCTRL_CLKSEL_CLK1_Val;
}
inline void setTxMode(int index) {
i2s.RXCTRL.bit.SERMODE = 0x01;
i2s.TXCTRL.reg &= ~(0x03); //TODO: why is this not in CMSIS...
i2s.TXCTRL.reg |= 0x01;
}
inline void setRxMode(int index) {
i2s.RXCTRL.bit.SERMODE = 0x00;
i2s.TXCTRL.reg &= ~(0x03); //TODO: why is this not in CMSIS...
i2s.TXCTRL.reg |= 0x00;
}
inline void enableClockUnit(int index) {
if (index == 0) {
while(i2s.SYNCBUSY.bit.CKEN0);
i2s.CTRLA.bit.CKEN0 = 1;
} else {
while(i2s.SYNCBUSY.bit.CKEN1);
i2s.CTRLA.bit.CKEN1 = 1;
}
}
inline void disableClockUnit(int index) {
if (index == 0) {
while(i2s.SYNCBUSY.bit.CKEN0);
i2s.CTRLA.bit.CKEN0 = 0;
} else {
while(i2s.SYNCBUSY.bit.CKEN1);
i2s.CTRLA.bit.CKEN1 = 0;
}
}
inline void enableSerializer(int index) {
i2s.CTRLA.bit.RXEN = 1;
i2s.CTRLA.bit.TXEN = 1;
}
inline void disableSerializer(int index) {
i2s.CTRLA.bit.RXEN = 0;
i2s.CTRLA.bit.TXEN = 0;
}
inline int dmaTriggerSource(int index) {
/*
if (i2s.SERCTRL[index].bit.SERMODE == I2S_SERCTRL_SERMODE_TX_Val) {
return (index == 0) ? I2S_DMAC_ID_TX_0 : I2S_DMAC_ID_TX_1;
} else {
return (index == 0) ? I2S_DMAC_ID_RX_0 : I2S_DMAC_ID_RX_1;
}
*/
}
inline int txReady(int index) {
return (index == 0) ? i2s.INTFLAG.bit.TXRDY0 :i2s.INTFLAG.bit.TXRDY1;
}
inline void writeData(int index, int32_t value) {
while (i2s.SYNCBUSY.bit.TXDATA);
i2s.TXDATA.bit.DATA = value;
}
inline void clearTxReady(int index) {
if (index == 0) {
i2s.INTFLAG.bit.TXRDY0 = 1;
} else {
i2s.INTFLAG.bit.TXRDY1 = 1;
}
}
inline int rxReady(int index) {
return (index == 0) ? i2s.INTFLAG.bit.RXRDY0 :i2s.INTFLAG.bit.RXRDY1;
}
inline int32_t readData(int index) {
while(i2s.SYNCBUSY.bit.RXDATA)
return i2s.RXDATA.bit.DATA;
}
inline void clearRxReady(int index) {
if (index == 0) {
i2s.INTFLAG.bit.RXRDY0 = 1;
} else {
i2s.INTFLAG.bit.RXRDY1 = 1;
}
}
inline void* data(int index) {
return (void*)&i2s.RXDATA.reg;
}
private:
volatile I2s &i2s;
};

View file

@ -191,9 +191,9 @@ static const uint8_t SCL = PIN_WIRE_SCL;
#define I2S_CLOCK_GENERATOR 3
//TODO: these
#define PIN_I2S_SD (9u)
#define PIN_I2S_SCK (1u)
#define PIN_I2S_FS (0u)
#define PIN_I2S_SD (13u)
#define PIN_I2S_SCK (3u)
#define PIN_I2S_FS (12u)
#ifdef __cplusplus
}