Add I2S support
This commit is contained in:
parent
72b1dc1a9b
commit
5a033fed7d
14 changed files with 1536 additions and 0 deletions
|
|
@ -65,6 +65,7 @@ void init( void )
|
|||
// Capture error
|
||||
while ( 1 ) ;
|
||||
}
|
||||
NVIC_SetPriority (SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 2); /* set Priority for Systick Interrupt (2nd lowest) */
|
||||
|
||||
// Clock PORT for Digital I/O
|
||||
// PM->APBBMASK.reg |= PM_APBBMASK_PORT ;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
This example reads audio data from an Invensense's ICS43432 I2S microphone
|
||||
breakout board, and prints out the samples to the Serial console. The
|
||||
Serial Plotter built into the Arduino IDE can be used to plot the audio
|
||||
data (Tools -> Serial Plotter)
|
||||
|
||||
Circuit:
|
||||
* Arduino/Genuino Zero or MKR1000 board
|
||||
* ICS43432:
|
||||
* GND connected GND
|
||||
* 3.3V connected 3.3V
|
||||
* WS connected to pin 0 (Zero) or pin 3 (MKR1000)
|
||||
* CLK connected to pin 1 (Zero) or pin 2 (MKR1000)
|
||||
* SD connected to pin 9 (Zero) or pin A6 (MKR1000)
|
||||
|
||||
created 17 November 2016
|
||||
by Sandeep Mistry
|
||||
*/
|
||||
|
||||
#include <I2S.h>
|
||||
|
||||
void setup() {
|
||||
// Open serial communications and wait for port to open:
|
||||
// A baud rate of 115200 is used instead of 9600 for a faster data rate
|
||||
// on non-native USB ports
|
||||
Serial.begin(115200);
|
||||
while (!Serial) {
|
||||
; // wait for serial port to connect. Needed for native USB port only
|
||||
}
|
||||
|
||||
// start I2S at 8 kHz with 32-bits per sample
|
||||
if (I2S.begin(I2S_PHILIPS_MODE, 8000, 32)) {
|
||||
Serial.println("Failed to initialize I2S!");
|
||||
while (1); // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// read a sample
|
||||
int sample = I2S.read();
|
||||
|
||||
if (sample) {
|
||||
// if it's non-zero print value to serial
|
||||
Serial.println(sample);
|
||||
}
|
||||
}
|
||||
53
libraries/I2S/examples/SimpleTone/SimpleTone.ino
Normal file
53
libraries/I2S/examples/SimpleTone/SimpleTone.ino
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
This example generates a square wave based tone at a specified frequency
|
||||
and sample rate. Then outputs the data using the I2S interface to a
|
||||
MAX08357 I2S Amp Breakout board.
|
||||
|
||||
Circuit:
|
||||
* Arduino/Genuino Zero or MKR1000 board
|
||||
* MAX08357:
|
||||
* GND connected GND
|
||||
* VIN connected 5V
|
||||
* LRC connected to pin 0 (Zero) or pin 3 (MKR1000)
|
||||
* BCLK connected to pin 1 (Zero) or pin 2 (MKR1000)
|
||||
* DIN connected to pin 9 (Zero) or pin A6 (MKR1000)
|
||||
|
||||
created 17 November 2016
|
||||
by Sandeep Mistry
|
||||
*/
|
||||
|
||||
#include <I2S.h>
|
||||
|
||||
const int frequency = 440; // frequency of square wave in Hz
|
||||
const int amplitude = 500; // amplitude of square wave
|
||||
const int sampleRate = 8000; // sample rate in Hz
|
||||
|
||||
const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave
|
||||
|
||||
short sample = amplitude; // current sample value
|
||||
int count = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("I2S simple tone");
|
||||
|
||||
// start I2S at the sample rate with 16-bits per sample
|
||||
if (I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) {
|
||||
Serial.println("Failed to initialize I2S!");
|
||||
while (1); // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (count % halfWavelength == 0) {
|
||||
// invert the sample every half wavelength count multiple to generate square wave
|
||||
sample = -1 * sample;
|
||||
}
|
||||
|
||||
// write the same sample twice, once for left and once for the right channel
|
||||
I2S.write(sample);
|
||||
I2S.write(sample);
|
||||
|
||||
// increment the counter for the next sample
|
||||
count++;
|
||||
}
|
||||
25
libraries/I2S/keywords.txt
Normal file
25
libraries/I2S/keywords.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map I2S
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
I2S KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
|
||||
onReceive KEYWORD2
|
||||
onTransmit KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
I2S_PHILIPS_MODE LITERAL1
|
||||
I2S_RIGHT_JUSTIFIED_MODE LITERAL1
|
||||
I2S_LEFT_JUSTIFIED_MODE LITERAL1
|
||||
9
libraries/I2S/library.properties
Normal file
9
libraries/I2S/library.properties
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
name=I2S
|
||||
version=1.0
|
||||
author=Arduino
|
||||
maintainer=Arduino <info@arduino.cc>
|
||||
sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for Arduino Zero.
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=http://www.arduino.cc/en/Reference/I2S
|
||||
architectures=samd
|
||||
519
libraries/I2S/src/I2S.cpp
Normal file
519
libraries/I2S/src/I2S.cpp
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino LLC. 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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <wiring_private.h>
|
||||
|
||||
#include "utility/DMA.h"
|
||||
#include "utility/SAMD21_I2SDevice.h"
|
||||
|
||||
static I2SDevice_SAMD21G18x i2sd(*I2S);
|
||||
|
||||
#include "I2S.h"
|
||||
|
||||
int I2SClass::_beginCount = 0;
|
||||
|
||||
I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) :
|
||||
_deviceIndex(deviceIndex),
|
||||
_clockGenerator(clockGenerator),
|
||||
_sdPin(sdPin),
|
||||
_sckPin(sckPin),
|
||||
_fsPin(fsPin),
|
||||
|
||||
_state(I2S_STATE_IDLE),
|
||||
_dmaChannel(-1),
|
||||
_bitsPerSample(0),
|
||||
_dmaTransferInProgress(false),
|
||||
|
||||
_onTransmit(NULL),
|
||||
_onReceive(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
int I2SClass::begin(int mode, long sampleRate, int bitsPerSample)
|
||||
{
|
||||
// master mode (driving clock and frame select pins - output)
|
||||
return begin(mode, sampleRate, bitsPerSample, true);
|
||||
}
|
||||
|
||||
int I2SClass::begin(int mode, int bitsPerSample)
|
||||
{
|
||||
// slave mode (not driving clock and frame select pin - input)
|
||||
return begin(mode, 0, bitsPerSample, false);
|
||||
}
|
||||
|
||||
int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock)
|
||||
{
|
||||
if (_state != I2S_STATE_IDLE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case I2S_PHILIPS_MODE:
|
||||
case I2S_RIGHT_JUSTIFIED_MODE:
|
||||
case I2S_LEFT_JUSTIFIED_MODE:
|
||||
break;
|
||||
|
||||
default:
|
||||
// invalid mode
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (bitsPerSample) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
_bitsPerSample = bitsPerSample;
|
||||
break;
|
||||
|
||||
default:
|
||||
// invalid bits per sample
|
||||
return 1;
|
||||
}
|
||||
|
||||
// try to allocate a DMA channel
|
||||
DMA.begin();
|
||||
|
||||
_dmaChannel = DMA.allocateChannel();
|
||||
|
||||
if (_dmaChannel < 0) {
|
||||
// no DMA channel available
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (_beginCount == 0) {
|
||||
// enable the I2S interface
|
||||
PM->APBCMASK.reg |= PM_APBCMASK_I2S;
|
||||
|
||||
// reset the device
|
||||
i2sd.reset();
|
||||
}
|
||||
|
||||
_beginCount++;
|
||||
|
||||
if (driveClock) {
|
||||
// set up clock
|
||||
enableClock(sampleRate * 2 * bitsPerSample);
|
||||
|
||||
i2sd.setSerialClockSelectMasterClockDiv(_deviceIndex);
|
||||
i2sd.setFrameSyncSelectSerialClockDiv(_deviceIndex);
|
||||
} else {
|
||||
// use input signal from SCK and FS pins
|
||||
i2sd.setSerialClockSelectPin(_deviceIndex);
|
||||
i2sd.setFrameSyncSelectPin(_deviceIndex);
|
||||
}
|
||||
|
||||
// disable device before continuing
|
||||
i2sd.disable();
|
||||
|
||||
if (mode == I2S_PHILIPS_MODE) {
|
||||
i2sd.set1BitDelay(_deviceIndex);
|
||||
} else {
|
||||
i2sd.set0BitDelay(_deviceIndex);
|
||||
}
|
||||
i2sd.setNumberOfSlots(_deviceIndex, 1);
|
||||
i2sd.setSlotSize(_deviceIndex, bitsPerSample);
|
||||
i2sd.setDataSize(_deviceIndex, bitsPerSample);
|
||||
|
||||
pinPeripheral(_sckPin, PIO_COM);
|
||||
pinPeripheral(_fsPin, PIO_COM);
|
||||
|
||||
if (mode == I2S_RIGHT_JUSTIFIED_MODE) {
|
||||
i2sd.setSlotAdjustedRight(_deviceIndex);
|
||||
} else {
|
||||
i2sd.setSlotAdjustedLeft(_deviceIndex);
|
||||
}
|
||||
|
||||
i2sd.setClockUnit(_deviceIndex);
|
||||
|
||||
pinPeripheral(_sdPin, PIO_COM);
|
||||
|
||||
// done configure enable
|
||||
i2sd.enable();
|
||||
|
||||
_doubleBuffer.reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void I2SClass::end()
|
||||
{
|
||||
if (_dmaChannel > -1) {
|
||||
DMA.freeChannel(_dmaChannel);
|
||||
}
|
||||
|
||||
_state = I2S_STATE_IDLE;
|
||||
_dmaTransferInProgress = false;
|
||||
|
||||
i2sd.disableSerializer(_deviceIndex);
|
||||
i2sd.disableClockUnit(_deviceIndex);
|
||||
|
||||
// set the pins back to input mode
|
||||
pinMode(_sdPin, INPUT);
|
||||
pinMode(_fsPin, INPUT);
|
||||
pinMode(_sckPin, INPUT);
|
||||
|
||||
disableClock();
|
||||
|
||||
_beginCount--;
|
||||
|
||||
if (_beginCount == 0) {
|
||||
i2sd.disable();
|
||||
|
||||
// disable the I2S interface
|
||||
PM->APBCMASK.reg &= ~PM_APBCMASK_I2S;
|
||||
}
|
||||
}
|
||||
|
||||
int I2SClass::available()
|
||||
{
|
||||
if (_state != I2S_STATE_RECEIVER) {
|
||||
enableReceiver();
|
||||
}
|
||||
|
||||
uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0);
|
||||
size_t avail;
|
||||
|
||||
// disable interrupts,
|
||||
__disable_irq();
|
||||
|
||||
avail = _doubleBuffer.available();
|
||||
|
||||
if (_dmaTransferInProgress == false && _doubleBuffer.available() == 0) {
|
||||
// no DMA transfer in progress, start a receive process
|
||||
_dmaTransferInProgress = true;
|
||||
|
||||
DMA.transfer(_dmaChannel, i2sd.data(_deviceIndex), _doubleBuffer.data(), _doubleBuffer.availableForWrite());
|
||||
|
||||
// switch to the next buffer for user output (will be empty)
|
||||
_doubleBuffer.swap();
|
||||
}
|
||||
|
||||
if (enableInterrupts) {
|
||||
// re-enable the interrupts
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
union i2s_sample_t {
|
||||
uint8_t b8;
|
||||
int16_t b16;
|
||||
int32_t b32;
|
||||
};
|
||||
|
||||
int I2SClass::read()
|
||||
{
|
||||
i2s_sample_t sample;
|
||||
|
||||
sample.b32 = 0;
|
||||
|
||||
read(&sample, _bitsPerSample / 8);
|
||||
|
||||
if (_bitsPerSample == 32) {
|
||||
return sample.b32;
|
||||
} else if (_bitsPerSample == 16) {
|
||||
return sample.b16;
|
||||
} else if (_bitsPerSample == 8) {
|
||||
return sample.b8;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int I2SClass::peek()
|
||||
{
|
||||
uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0);
|
||||
i2s_sample_t sample;
|
||||
|
||||
sample.b32 = 0;
|
||||
|
||||
// disable interrupts,
|
||||
__disable_irq();
|
||||
|
||||
_doubleBuffer.peek(&sample, _bitsPerSample / 8);
|
||||
|
||||
if (enableInterrupts) {
|
||||
// re-enable the interrupts
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
if (_bitsPerSample == 32) {
|
||||
return sample.b32;
|
||||
} else if (_bitsPerSample == 16) {
|
||||
return sample.b16;
|
||||
} else if (_bitsPerSample == 8) {
|
||||
return sample.b8;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void I2SClass::flush()
|
||||
{
|
||||
// do nothing, writes are DMA triggered
|
||||
}
|
||||
|
||||
size_t I2SClass::write(uint8_t data)
|
||||
{
|
||||
return write((int32_t)data);
|
||||
}
|
||||
|
||||
size_t I2SClass::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
return write((const void*)buffer, size);
|
||||
}
|
||||
|
||||
size_t I2SClass::availableForWrite()
|
||||
{
|
||||
if (_state != I2S_STATE_TRANSMITTER) {
|
||||
enableTransmitter();
|
||||
}
|
||||
|
||||
uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0);
|
||||
size_t space;
|
||||
|
||||
// disable interrupts,
|
||||
__disable_irq();
|
||||
|
||||
space = _doubleBuffer.availableForWrite();
|
||||
|
||||
if (enableInterrupts) {
|
||||
// re-enable the interrupts
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
int I2SClass::read(void* buffer, size_t size)
|
||||
{
|
||||
if (_state != I2S_STATE_RECEIVER) {
|
||||
enableReceiver();
|
||||
}
|
||||
|
||||
uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0);
|
||||
|
||||
// disable interrupts,
|
||||
__disable_irq();
|
||||
|
||||
int read = _doubleBuffer.read(buffer, size);
|
||||
|
||||
if (_dmaTransferInProgress == false && _doubleBuffer.available() == 0) {
|
||||
// no DMA transfer in progress, start a receive process
|
||||
_dmaTransferInProgress = true;
|
||||
|
||||
DMA.transfer(_dmaChannel, i2sd.data(_deviceIndex), _doubleBuffer.data(), _doubleBuffer.availableForWrite());
|
||||
|
||||
// switch to the next buffer for user output (will be empty)
|
||||
_doubleBuffer.swap();
|
||||
}
|
||||
|
||||
if (enableInterrupts) {
|
||||
// re-enable the interrupts
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
size_t I2SClass::write(int sample)
|
||||
{
|
||||
return write((int32_t)sample);
|
||||
}
|
||||
|
||||
size_t I2SClass::write(int32_t sample)
|
||||
{
|
||||
if (_state != I2S_STATE_TRANSMITTER) {
|
||||
enableTransmitter();
|
||||
}
|
||||
|
||||
// this is a blocking write
|
||||
while(!i2sd.txReady(_deviceIndex));
|
||||
|
||||
i2sd.writeData(_deviceIndex, sample);
|
||||
|
||||
i2sd.clearTxReady(_deviceIndex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t I2SClass::write(const void *buffer, size_t size)
|
||||
{
|
||||
if (_state != I2S_STATE_TRANSMITTER) {
|
||||
enableTransmitter();
|
||||
}
|
||||
|
||||
uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0);
|
||||
size_t written;
|
||||
|
||||
// disable interrupts,
|
||||
__disable_irq();
|
||||
|
||||
written = _doubleBuffer.write(buffer, size);
|
||||
|
||||
if (_dmaTransferInProgress == false && _doubleBuffer.available()) {
|
||||
// no DMA transfer in progress, start a transmit process
|
||||
_dmaTransferInProgress = true;
|
||||
|
||||
DMA.transfer(_dmaChannel, _doubleBuffer.data(), i2sd.data(_deviceIndex), _doubleBuffer.available());
|
||||
|
||||
// switch to the next buffer for input
|
||||
_doubleBuffer.swap();
|
||||
}
|
||||
|
||||
if (enableInterrupts) {
|
||||
// re-enable the interrupts
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
void I2SClass::onTransmit(void(*function)(void))
|
||||
{
|
||||
_onTransmit = function;
|
||||
}
|
||||
|
||||
void I2SClass::onReceive(void(*function)(void))
|
||||
{
|
||||
_onReceive = function;
|
||||
}
|
||||
|
||||
void I2SClass::enableClock(int divider)
|
||||
{
|
||||
// configure the clock divider
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
GCLK->GENDIV.bit.ID = _clockGenerator;
|
||||
GCLK->GENDIV.bit.DIV = SystemCoreClock / divider;
|
||||
|
||||
// use the DFLL as the source
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
GCLK->GENCTRL.bit.ID = _clockGenerator;
|
||||
GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val;
|
||||
GCLK->GENCTRL.bit.IDC = 1;
|
||||
GCLK->GENCTRL.bit.GENEN = 1;
|
||||
|
||||
// enable
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
GCLK->CLKCTRL.bit.ID = i2sd.glckId(_deviceIndex);
|
||||
GCLK->CLKCTRL.bit.GEN = _clockGenerator;
|
||||
GCLK->CLKCTRL.bit.CLKEN = 1;
|
||||
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
}
|
||||
|
||||
void I2SClass::disableClock()
|
||||
{
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
GCLK->GENCTRL.bit.ID = _clockGenerator;
|
||||
GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_DFLL48M_Val;
|
||||
GCLK->GENCTRL.bit.IDC = 1;
|
||||
GCLK->GENCTRL.bit.GENEN = 0;
|
||||
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
GCLK->CLKCTRL.bit.ID = i2sd.glckId(_deviceIndex);
|
||||
GCLK->CLKCTRL.bit.GEN = _clockGenerator;
|
||||
GCLK->CLKCTRL.bit.CLKEN = 0;
|
||||
|
||||
while (GCLK->STATUS.bit.SYNCBUSY);
|
||||
}
|
||||
|
||||
void I2SClass::enableTransmitter()
|
||||
{
|
||||
i2sd.setTxMode(_deviceIndex);
|
||||
i2sd.enableClockUnit(_deviceIndex);
|
||||
i2sd.enableSerializer(_deviceIndex);
|
||||
|
||||
DMA.incSrc(_dmaChannel);
|
||||
DMA.onTransferComplete(_dmaChannel, I2SClass::onDmaTransferComplete);
|
||||
DMA.setTriggerSource(_dmaChannel, i2sd.dmaTriggerSource(_deviceIndex));
|
||||
DMA.setTransferWidth(_dmaChannel, _bitsPerSample);
|
||||
|
||||
_state = I2S_STATE_TRANSMITTER;
|
||||
}
|
||||
|
||||
void I2SClass::enableReceiver()
|
||||
{
|
||||
i2sd.setRxMode(_deviceIndex);
|
||||
i2sd.enableClockUnit(_deviceIndex);
|
||||
i2sd.enableSerializer(_deviceIndex);
|
||||
|
||||
DMA.incDst(_dmaChannel);
|
||||
DMA.onTransferComplete(_dmaChannel, I2SClass::onDmaTransferComplete);
|
||||
DMA.setTriggerSource(_dmaChannel, i2sd.dmaTriggerSource(_deviceIndex));
|
||||
DMA.setTransferWidth(_dmaChannel, _bitsPerSample);
|
||||
|
||||
_state = I2S_STATE_RECEIVER;
|
||||
}
|
||||
|
||||
void I2SClass::onTransferComplete(void)
|
||||
{
|
||||
if (_state == I2S_STATE_TRANSMITTER) {
|
||||
// transmit complete
|
||||
|
||||
if (_doubleBuffer.available()) {
|
||||
// output is available to transfer, start the DMA process for the current buffer
|
||||
|
||||
DMA.transfer(_dmaChannel, _doubleBuffer.data(), i2sd.data(_deviceIndex), _doubleBuffer.available());
|
||||
|
||||
// swap to the next user buffer for input
|
||||
_doubleBuffer.swap();
|
||||
} else {
|
||||
// no user data buffered to send
|
||||
_dmaTransferInProgress = false;
|
||||
}
|
||||
|
||||
// call the users transmit callback if provided
|
||||
if (_onTransmit) {
|
||||
_onTransmit();
|
||||
}
|
||||
} else {
|
||||
// receive complete
|
||||
|
||||
if (_doubleBuffer.available() == 0) {
|
||||
// the user has read all the current input, start the DMA process to fill it again
|
||||
DMA.transfer(_dmaChannel, i2sd.data(_deviceIndex), _doubleBuffer.data(), _doubleBuffer.availableForWrite());
|
||||
|
||||
// swap to the next buffer that has previously been filled, so that the user can read it
|
||||
_doubleBuffer.swap(_doubleBuffer.availableForWrite());
|
||||
} else {
|
||||
// user has not read current data, no free buffer to transfer into
|
||||
_dmaTransferInProgress = false;
|
||||
}
|
||||
|
||||
// call the users receveive callback if provided
|
||||
if (_onReceive) {
|
||||
_onReceive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2SClass::onDmaTransferComplete(int channel)
|
||||
{
|
||||
#if I2S_INTERFACES_COUNT > 0
|
||||
if (I2S._dmaChannel == channel) {
|
||||
I2S.onTransferComplete();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if I2S_INTERFACES_COUNT > 0
|
||||
I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS);
|
||||
#endif
|
||||
112
libraries/I2S/src/I2S.h
Normal file
112
libraries/I2S/src/I2S.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino LLC. 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
|
||||
*/
|
||||
|
||||
#ifndef _I2S_H_INCLUDED
|
||||
#define _I2S_H_INCLUDED
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "utility/I2SDoubleBuffer.h"
|
||||
|
||||
typedef enum {
|
||||
I2S_PHILIPS_MODE,
|
||||
I2S_RIGHT_JUSTIFIED_MODE,
|
||||
I2S_LEFT_JUSTIFIED_MODE
|
||||
} i2s_mode_t;
|
||||
|
||||
class I2SClass : public Stream
|
||||
{
|
||||
public:
|
||||
// the device index and pins must map to the "COM" pads in Table 6-1 of the datasheet
|
||||
I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin);
|
||||
|
||||
// the SCK and FS pins are driven as outputs using the sample rate
|
||||
int begin(int mode, long sampleRate, int bitsPerSample);
|
||||
// the SCK and FS pins are inputs, other side controls sample rate
|
||||
int begin(int mode, int bitsPerSample);
|
||||
void end();
|
||||
|
||||
// from Stream
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
|
||||
// from Print
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
virtual size_t availableForWrite();
|
||||
|
||||
int read(void* buffer, size_t size);
|
||||
|
||||
size_t write(int);
|
||||
size_t write(int32_t);
|
||||
size_t write(const void *buffer, size_t size);
|
||||
|
||||
void onTransmit(void(*)(void));
|
||||
void onReceive(void(*)(void));
|
||||
|
||||
private:
|
||||
int begin(int mode, long sampleRate, int bitsPerSample, bool driveClock);
|
||||
|
||||
void enableClock(int divider);
|
||||
void disableClock();
|
||||
|
||||
void enableTransmitter();
|
||||
void enableReceiver();
|
||||
|
||||
void onTransferComplete(void);
|
||||
|
||||
static void onDmaTransferComplete(int);
|
||||
|
||||
private:
|
||||
typedef enum {
|
||||
I2S_STATE_IDLE,
|
||||
I2S_STATE_TRANSMITTER,
|
||||
I2S_STATE_RECEIVER
|
||||
} i2s_state_t;
|
||||
|
||||
static int _beginCount;
|
||||
|
||||
uint8_t _deviceIndex;
|
||||
uint8_t _clockGenerator;
|
||||
uint8_t _sdPin;
|
||||
uint8_t _sckPin;
|
||||
uint8_t _fsPin;
|
||||
|
||||
i2s_state_t _state;
|
||||
int _dmaChannel;
|
||||
int _bitsPerSample;
|
||||
|
||||
volatile bool _dmaTransferInProgress;
|
||||
I2SDoubleBuffer _doubleBuffer;
|
||||
|
||||
void (*_onTransmit)(void);
|
||||
void (*_onReceive)(void);
|
||||
};
|
||||
|
||||
// "I2S" is already defined by the CMSIS device, undefine it so the I2SClass
|
||||
// instance can be called I2S
|
||||
#undef I2S
|
||||
|
||||
#if I2S_INTERFACES_COUNT > 0
|
||||
extern I2SClass I2S;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
284
libraries/I2S/src/utility/DMA.cpp
Normal file
284
libraries/I2S/src/utility/DMA.cpp
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino LLC. 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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "DMA.h"
|
||||
|
||||
int DMAClass::_beginCount = 0;
|
||||
|
||||
DMAClass::DMAClass() :
|
||||
_channelMask(0)
|
||||
{
|
||||
memset(_transferCompleteCallbacks, 0x00, sizeof(_transferCompleteCallbacks));
|
||||
memset(_transferErrorCallbacks, 0x00, sizeof(_transferErrorCallbacks));
|
||||
|
||||
memset(_descriptors, 0x00, sizeof(_descriptors));
|
||||
memset(_descriptorsWriteBack, 0x00, sizeof(_descriptorsWriteBack));
|
||||
}
|
||||
|
||||
DMAClass::~DMAClass()
|
||||
{
|
||||
}
|
||||
|
||||
void DMAClass::begin()
|
||||
{
|
||||
if (_beginCount == 0) {
|
||||
// enable the DMA interface
|
||||
PM->AHBMASK.bit.DMAC_ = 1;
|
||||
PM->APBBMASK.bit.DMAC_ = 1;
|
||||
|
||||
// perform a reset
|
||||
DMAC->CTRL.bit.SWRST = 1;
|
||||
|
||||
// configure the descriptor addresses
|
||||
DMAC->BASEADDR.bit.BASEADDR = (uint32_t)_descriptors;
|
||||
DMAC->WRBADDR.bit.WRBADDR = (uint32_t)_descriptorsWriteBack;
|
||||
|
||||
// enable with all levels
|
||||
DMAC->CTRL.bit.LVLEN0 = 1;
|
||||
DMAC->CTRL.bit.LVLEN1 = 1;
|
||||
DMAC->CTRL.bit.LVLEN2 = 1;
|
||||
DMAC->CTRL.bit.LVLEN3 = 1;
|
||||
DMAC->CTRL.bit.DMAENABLE = 1;
|
||||
|
||||
// enable the interrupt at lowest priority
|
||||
NVIC_EnableIRQ(DMAC_IRQn);
|
||||
NVIC_SetPriority(DMAC_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
|
||||
}
|
||||
|
||||
_beginCount++;
|
||||
}
|
||||
|
||||
void DMAClass::end()
|
||||
{
|
||||
_beginCount--;
|
||||
|
||||
if (_beginCount == 0) {
|
||||
// disable the interrupt
|
||||
NVIC_DisableIRQ(DMAC_IRQn);
|
||||
|
||||
// disable
|
||||
DMAC->CTRL.bit.DMAENABLE = 0;
|
||||
|
||||
// disable the DMA interface
|
||||
PM->APBBMASK.bit.DMAC_ = 0;
|
||||
PM->AHBMASK.bit.DMAC_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int DMAClass::allocateChannel()
|
||||
{
|
||||
int channel = -1;
|
||||
|
||||
// try to find a free DMA channel
|
||||
for (int i = 0; i < NUM_DMA_CHANNELS; i++) {
|
||||
if ((_channelMask & (1 << i)) == 0) {
|
||||
// found one, set the mask bit to indicate it is allocated
|
||||
_channelMask |= (1 << i);
|
||||
|
||||
// clear the descriptor for the channel
|
||||
memset((void*)&_descriptors[i], 0x00, sizeof(_descriptors[i]));
|
||||
|
||||
// select the channel and reset it
|
||||
DMAC->CHID.bit.ID = i;
|
||||
DMAC->CHCTRLA.bit.ENABLE = 0;
|
||||
DMAC->CHCTRLA.bit.SWRST = 1;
|
||||
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
void DMAClass::freeChannel(int channel)
|
||||
{
|
||||
// select the channel and disable it
|
||||
DMAC->CHID.bit.ID = channel;
|
||||
DMAC->CHCTRLA.bit.ENABLE = 0;
|
||||
|
||||
_channelMask &= ~(1 << 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;
|
||||
}
|
||||
|
||||
void DMAClass::setTriggerSource(int channel, int source)
|
||||
{
|
||||
// select the channel and set a trigger source
|
||||
DMAC->CHID.bit.ID = channel;
|
||||
DMAC->CHCTRLB.bit.TRIGSRC = source;
|
||||
|
||||
if (DMAC->CHCTRLB.bit.TRIGSRC) {
|
||||
// if it's not a software source (0), set trigger action a a beat
|
||||
DMAC->CHCTRLB.bit.TRIGACT = DMAC_CHCTRLB_TRIGACT_BEAT_Val;
|
||||
} else {
|
||||
DMAC->CHCTRLB.bit.TRIGACT = DMAC_CHCTRLB_TRIGACT_BLOCK_Val;
|
||||
}
|
||||
}
|
||||
|
||||
void DMAClass::setTransferWidth(int channel, int transferWidth)
|
||||
{
|
||||
// select the channel and set transfer width
|
||||
switch (transferWidth) {
|
||||
case 8:
|
||||
default:
|
||||
_descriptors[channel].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
_descriptors[channel].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_HWORD_Val;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
_descriptors[channel].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_WORD_Val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DMAClass::incSrc(int channel)
|
||||
{
|
||||
// select the channel and enable source increment
|
||||
_descriptors[channel].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_SRC_Val;
|
||||
_descriptors[channel].BTCTRL.bit.SRCINC = 1;
|
||||
}
|
||||
|
||||
void DMAClass::incDst(int channel)
|
||||
{
|
||||
// select the channel and enable destination increment
|
||||
_descriptors[channel].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_DST_Val;
|
||||
_descriptors[channel].BTCTRL.bit.DSTINC = 1;
|
||||
}
|
||||
|
||||
int DMAClass::transfer(int channel, void* src, void* dst, uint16_t size)
|
||||
{
|
||||
if (_descriptors[channel].BTCTRL.bit.VALID) {
|
||||
// transfer in progress, fail
|
||||
return 1;
|
||||
}
|
||||
|
||||
// select the channel
|
||||
DMAC->CHID.bit.ID = channel;
|
||||
|
||||
// disable event output generation and block actions
|
||||
_descriptors[channel].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val;
|
||||
_descriptors[channel].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_NOACT_Val;
|
||||
|
||||
// map beat size to transfer width in bytes
|
||||
int transferWidth;
|
||||
|
||||
switch (_descriptors[channel].BTCTRL.bit.BEATSIZE) {
|
||||
case DMAC_BTCTRL_BEATSIZE_BYTE_Val:
|
||||
default:
|
||||
transferWidth = 1;
|
||||
break;
|
||||
|
||||
case DMAC_BTCTRL_BEATSIZE_HWORD_Val:
|
||||
transferWidth = 2;
|
||||
break;
|
||||
|
||||
case DMAC_BTCTRL_BEATSIZE_WORD_Val:
|
||||
transferWidth = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
// set step size to 1, source + destination addresses, no next descriptor block count
|
||||
_descriptors[channel].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val;
|
||||
_descriptors[channel].SRCADDR.bit.SRCADDR = (uint32_t)src;
|
||||
_descriptors[channel].DSTADDR.bit.DSTADDR = (uint32_t)dst;
|
||||
_descriptors[channel].DESCADDR.bit.DESCADDR = 0;
|
||||
_descriptors[channel].BTCNT.bit.BTCNT = size / transferWidth;
|
||||
|
||||
if (_descriptors[channel].BTCTRL.bit.SRCINC) {
|
||||
// if increment source is set, the source address must be the end address
|
||||
_descriptors[channel].SRCADDR.bit.SRCADDR += size;
|
||||
}
|
||||
|
||||
if (_descriptors[channel].BTCTRL.bit.DSTINC) {
|
||||
// if increment destination is set, the destination address must be the end address
|
||||
_descriptors[channel].DSTADDR.bit.DSTADDR += size;
|
||||
}
|
||||
|
||||
// validate the descriptor
|
||||
_descriptors[channel].BTCTRL.bit.VALID = 1;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DMAClass::onTransferComplete(int channel, void(*function)(int))
|
||||
{
|
||||
_transferCompleteCallbacks[channel] = function;
|
||||
}
|
||||
|
||||
void DMAClass::onTransferError(int channel, void(*function)(int))
|
||||
{
|
||||
_transferErrorCallbacks[channel] = function;
|
||||
}
|
||||
|
||||
void DMAClass::onService()
|
||||
{
|
||||
// get the channel and select it
|
||||
int channel = DMAC->INTPEND.bit.ID;
|
||||
DMAC->CHID.bit.ID = channel;
|
||||
|
||||
// invalidate the channel
|
||||
_descriptors[channel].BTCTRL.bit.VALID = 0;
|
||||
|
||||
if (DMAC->CHINTFLAG.bit.TERR) {
|
||||
// clear the error interrupt and call the error callback if there is one
|
||||
DMAC->CHINTFLAG.bit.TERR = 1;
|
||||
|
||||
if (_transferErrorCallbacks[channel]) {
|
||||
_transferErrorCallbacks[channel](channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (DMAC->CHINTFLAG.bit.TCMPL) {
|
||||
// clear the complete interrupt and call the callback if there is one
|
||||
DMAC->CHINTFLAG.bit.TCMPL = 1;
|
||||
|
||||
if (_transferCompleteCallbacks[channel]) {
|
||||
_transferCompleteCallbacks[channel](channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void DMAC_Handler() {
|
||||
DMA.onService();
|
||||
}
|
||||
}
|
||||
|
||||
DMAClass DMA;
|
||||
60
libraries/I2S/src/utility/DMA.h
Normal file
60
libraries/I2S/src/utility/DMA.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino LLC. 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
|
||||
|
||||
#define NUM_DMA_CHANNELS 1
|
||||
|
||||
/*
|
||||
WARNING: The API for this class may change and it's not intended for public use!
|
||||
*/
|
||||
class DMAClass
|
||||
{
|
||||
public:
|
||||
DMAClass();
|
||||
virtual ~DMAClass();
|
||||
|
||||
void begin();
|
||||
void end();
|
||||
|
||||
int allocateChannel();
|
||||
void freeChannel(int channel);
|
||||
|
||||
void setPriorityLevel(int channel, int level);
|
||||
void setTriggerSource(int channel, int source);
|
||||
void setTransferWidth(int channel, int transferWidth);
|
||||
void incSrc(int channel);
|
||||
void incDst(int channel);
|
||||
int transfer(int channel, void* src, void* dst, uint16_t size);
|
||||
|
||||
void onTransferComplete(int channel, void(*function)(int));
|
||||
void onTransferError(int channel, void(*function)(int));
|
||||
|
||||
void onService();
|
||||
|
||||
private:
|
||||
static int _beginCount;
|
||||
uint32_t _channelMask;
|
||||
|
||||
void (*_transferCompleteCallbacks[NUM_DMA_CHANNELS])(int);
|
||||
void (*_transferErrorCallbacks[NUM_DMA_CHANNELS])(int);
|
||||
|
||||
DmacDescriptor _descriptors[NUM_DMA_CHANNELS] __attribute__ ((aligned (16)));
|
||||
DmacDescriptor _descriptorsWriteBack[NUM_DMA_CHANNELS] __attribute__ ((aligned (16)));
|
||||
};
|
||||
|
||||
extern DMAClass DMA;
|
||||
120
libraries/I2S/src/utility/I2SDoubleBuffer.cpp
Normal file
120
libraries/I2S/src/utility/I2SDoubleBuffer.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino LLC. 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
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "I2SDoubleBuffer.h"
|
||||
|
||||
I2SDoubleBuffer::I2SDoubleBuffer()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
I2SDoubleBuffer::~I2SDoubleBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void I2SDoubleBuffer::reset()
|
||||
{
|
||||
_index = 0;
|
||||
_length[0] = 0;
|
||||
_length[1] = 0;
|
||||
_readOffset[0] = 0;
|
||||
_readOffset[1] = 0;
|
||||
}
|
||||
|
||||
size_t I2SDoubleBuffer::availableForWrite()
|
||||
{
|
||||
return (I2S_BUFFER_SIZE - (_length[_index] - _readOffset[_index]));
|
||||
}
|
||||
|
||||
size_t I2SDoubleBuffer::write(const void *buffer, size_t size)
|
||||
{
|
||||
size_t space = availableForWrite();
|
||||
|
||||
if (size > space) {
|
||||
size = space;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&_buffer[_index][_length[_index]], buffer, size);
|
||||
|
||||
_length[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t I2SDoubleBuffer::read(void *buffer, size_t size)
|
||||
{
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
_readOffset[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t I2SDoubleBuffer::peek(void *buffer, size_t size)
|
||||
{
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void* I2SDoubleBuffer::data()
|
||||
{
|
||||
return (void*)_buffer[_index];
|
||||
}
|
||||
|
||||
size_t I2SDoubleBuffer::available()
|
||||
{
|
||||
return _length[_index] - _readOffset[_index];
|
||||
}
|
||||
|
||||
void I2SDoubleBuffer::swap(int length)
|
||||
{
|
||||
if (_index == 0) {
|
||||
_index = 1;
|
||||
} else {
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
_length[_index] = length;
|
||||
_readOffset[_index] = 0;
|
||||
}
|
||||
50
libraries/I2S/src/utility/I2SDoubleBuffer.h
Normal file
50
libraries/I2S/src/utility/I2SDoubleBuffer.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino LLC. 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
|
||||
*/
|
||||
|
||||
#ifndef _I2S_DOUBLE_BUFFER_H_INCLUDED
|
||||
#define _I2S_DOUBLE_BUFFER_H_INCLUDED
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define I2S_BUFFER_SIZE 512
|
||||
|
||||
class I2SDoubleBuffer
|
||||
{
|
||||
public:
|
||||
I2SDoubleBuffer();
|
||||
virtual ~I2SDoubleBuffer();
|
||||
|
||||
void reset();
|
||||
|
||||
size_t availableForWrite();
|
||||
size_t write(const void *buffer, size_t size);
|
||||
size_t read(void *buffer, size_t size);
|
||||
size_t peek(void *buffer, size_t size);
|
||||
void* data();
|
||||
size_t available();
|
||||
void swap(int length = 0);
|
||||
|
||||
private:
|
||||
uint8_t _buffer[2][I2S_BUFFER_SIZE];
|
||||
volatile int _length[2];
|
||||
volatile int _readOffset[2];
|
||||
volatile int _index;
|
||||
};
|
||||
|
||||
#endif
|
||||
236
libraries/I2S/src/utility/SAMD21_I2SDevice.h
Normal file
236
libraries/I2S/src/utility/SAMD21_I2SDevice.h
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
Copyright (c) 2016 Arduino LLC. 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_SAMD21G18x {
|
||||
public:
|
||||
I2SDevice_SAMD21G18x(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.SERCTRL[index].bit.DATASIZE = I2S_SERCTRL_DATASIZE_32_Val;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
i2s.SERCTRL[index].bit.DATASIZE = I2S_SERCTRL_DATASIZE_24_Val;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
i2s.SERCTRL[index].bit.DATASIZE = I2S_SERCTRL_DATASIZE_16_Val;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
i2s.SERCTRL[index].bit.DATASIZE = I2S_SERCTRL_DATASIZE_8_Val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void setSlotAdjustedRight(int index) {
|
||||
i2s.SERCTRL[index].bit.SLOTADJ = I2S_SERCTRL_SLOTADJ_RIGHT_Val;
|
||||
}
|
||||
|
||||
inline void setSlotAdjustedLeft(int index) {
|
||||
i2s.SERCTRL[index].bit.SLOTADJ = I2S_SERCTRL_SLOTADJ_LEFT_Val;
|
||||
}
|
||||
|
||||
inline void setClockUnit(int index) {
|
||||
i2s.SERCTRL[index].bit.CLKSEL = (index == 0) ? I2S_SERCTRL_CLKSEL_CLK0_Val : I2S_SERCTRL_CLKSEL_CLK1_Val;
|
||||
}
|
||||
|
||||
inline void setTxMode(int index) {
|
||||
i2s.SERCTRL[index].bit.SERMODE = I2S_SERCTRL_SERMODE_TX_Val;
|
||||
}
|
||||
|
||||
inline void setRxMode(int index) {
|
||||
i2s.SERCTRL[index].bit.SERMODE = I2S_SERCTRL_SERMODE_RX_Val;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (index == 0) {
|
||||
while(i2s.SYNCBUSY.bit.SEREN0);
|
||||
i2s.CTRLA.bit.SEREN0 = 1;
|
||||
} else {
|
||||
while(i2s.SYNCBUSY.bit.SEREN1);
|
||||
i2s.CTRLA.bit.SEREN1 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline void disableSerializer(int index) {
|
||||
if (index == 0) {
|
||||
while(i2s.SYNCBUSY.bit.SEREN0);
|
||||
i2s.CTRLA.bit.SEREN0 = 0;
|
||||
} else {
|
||||
while(i2s.SYNCBUSY.bit.SEREN1);
|
||||
i2s.CTRLA.bit.SEREN1 = 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) {
|
||||
if (index == 0) {
|
||||
while (i2s.SYNCBUSY.bit.DATA0);
|
||||
} else {
|
||||
while (i2s.SYNCBUSY.bit.DATA1);
|
||||
}
|
||||
|
||||
i2s.DATA[index].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) {
|
||||
if (index == 0) {
|
||||
while (i2s.SYNCBUSY.bit.DATA0);
|
||||
} else {
|
||||
while (i2s.SYNCBUSY.bit.DATA1);
|
||||
}
|
||||
|
||||
return i2s.DATA[index].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.DATA[index].reg;
|
||||
}
|
||||
|
||||
private:
|
||||
volatile I2s &i2s;
|
||||
};
|
||||
|
|
@ -161,6 +161,17 @@ static const uint8_t SCL = PIN_WIRE_SCL;
|
|||
#define PIN_USB_DM (28ul)
|
||||
#define PIN_USB_DP (29ul)
|
||||
|
||||
/*
|
||||
* I2S Interfaces
|
||||
*/
|
||||
#define I2S_INTERFACES_COUNT 1
|
||||
|
||||
#define I2S_DEVICE 0
|
||||
#define I2S_CLOCK_GENERATOR 3
|
||||
#define PIN_I2S_SD (9u)
|
||||
#define PIN_I2S_SCK (1u)
|
||||
#define PIN_I2S_FS (0u)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -135,6 +135,16 @@ static const uint8_t SCL = PIN_WIRE_SCL;
|
|||
#define PIN_USB_DP (23ul)
|
||||
#define PIN_USB_HOST_ENABLE (24ul)
|
||||
|
||||
// I2S Interfaces
|
||||
// --------------
|
||||
#define I2S_INTERFACES_COUNT 1
|
||||
|
||||
#define I2S_DEVICE 0
|
||||
#define I2S_CLOCK_GENERATOR 3
|
||||
#define PIN_I2S_SD (PIN_A6)
|
||||
#define PIN_I2S_SCK (2u)
|
||||
#define PIN_I2S_FS (3u)
|
||||
|
||||
// Needed for WINC1501B (WiFi101) library
|
||||
// --------------------------------------
|
||||
#define WINC1501_RESET_PIN (30u)
|
||||
|
|
|
|||
Loading…
Reference in a new issue