Skip to content

Commit 2680779

Browse files
committed
add double buffer for I2S DMA writes
1 parent 50e1872 commit 2680779

File tree

5 files changed

+189
-18
lines changed

5 files changed

+189
-18
lines changed

libraries/I2S/src/I2S.cpp

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u
3636
_fsPin(fsPin),
3737

3838
_dmaChannel(-1),
39+
_dmaTransferInProgress(false),
3940

4041
_onTransmit(NULL)
4142
{
4243
}
4344

44-
int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, int driveClock)
45+
int I2SClass::begin(int mode, long sampleRate, int bitsPerSample)
4546
{
4647
switch (mode) {
4748
case I2S_PHILIPS_MODE:
@@ -101,10 +102,11 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, int driveClock
101102

102103
DMA.incSrc(_dmaChannel);
103104
DMA.onTransferComplete(_dmaChannel, I2SClass::onDmaTransferComplete);
104-
DMA.onTransferError(_dmaChannel, I2SClass::onDmaTransferError);
105105
DMA.setTriggerSource(_dmaChannel, i2sd.dmaTriggerSource(_deviceIndex));
106106
DMA.setTransferWidth(_dmaChannel, bitsPerSample);
107107

108+
_doubleBuffer.reset();
109+
108110
return 0;
109111
}
110112

@@ -165,7 +167,20 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size)
165167

166168
size_t I2SClass::availableForWrite()
167169
{
168-
return 2;
170+
uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0);
171+
size_t space;
172+
173+
// disable interrupts,
174+
__disable_irq();
175+
176+
space = _doubleBuffer.availableForWrite();
177+
178+
if (enableInterrupts) {
179+
// re-enable the interrupts
180+
__enable_irq();
181+
}
182+
183+
return space;
169184
}
170185

171186
size_t I2SClass::write(int sample)
@@ -186,7 +201,28 @@ size_t I2SClass::write(int32_t sample)
186201

187202
size_t I2SClass::write(const void *buffer, size_t size)
188203
{
189-
return 0;
204+
uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0);
205+
size_t written;
206+
207+
// disable interrupts,
208+
__disable_irq();
209+
210+
written = _doubleBuffer.write(buffer, size);
211+
212+
if (_dmaTransferInProgress == false) {
213+
_dmaTransferInProgress = true;
214+
215+
DMA.transfer(_dmaChannel, _doubleBuffer.data(), i2sd.data(_deviceIndex), _doubleBuffer.available());
216+
217+
_doubleBuffer.swap();
218+
}
219+
220+
if (enableInterrupts) {
221+
// re-enable the interrupts
222+
__enable_irq();
223+
}
224+
225+
return written;
190226
}
191227

192228
void I2SClass::onTransmit(void(*function)(void))
@@ -235,19 +271,19 @@ void I2SClass::onDmaTransferComplete(int channel)
235271
}
236272
}
237273

238-
void I2SClass::onDmaTransferError(int channel)
239-
{
240-
if (I2S._dmaChannel == channel) {
241-
I2S.onTransferError();
242-
}
243-
}
244-
245274
void I2SClass::onTransferComplete(void)
246275
{
247-
}
276+
if (_doubleBuffer.available()) {
277+
DMA.transfer(_dmaChannel, _doubleBuffer.data(), i2sd.data(_deviceIndex), _doubleBuffer.available());
248278

249-
void I2SClass::onTransferError(void)
250-
{
279+
_doubleBuffer.swap();
280+
} else {
281+
_dmaTransferInProgress = false;
282+
}
283+
284+
if (_onTransmit) {
285+
_onTransmit();
286+
}
251287
}
252288

253289
/*

libraries/I2S/src/I2S.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,19 @@
2121

2222
#include <Arduino.h>
2323

24+
#include "utility/I2SDoubleBuffer.h"
25+
2426
typedef enum {
2527
I2S_PHILIPS_MODE
2628
} i2s_mode_t;
2729

28-
#define I2S_BUFFER_SIZE 512
2930

3031
class I2SClass : public Stream
3132
{
3233
public:
3334
I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin);
3435

35-
int begin(int mode, long sampleRate, int bitsPerSample, int driveClock = 1);
36+
int begin(int mode, long sampleRate, int bitsPerSample);
3637
void end();
3738

3839
// from Stream
@@ -58,10 +59,8 @@ class I2SClass : public Stream
5859
void disableClock();
5960

6061
static void onDmaTransferComplete(int);
61-
static void onDmaTransferError(int);
6262

6363
void onTransferComplete(void);
64-
void onTransferError(void);
6564

6665
private:
6766
static int _beginCount;
@@ -74,6 +73,9 @@ class I2SClass : public Stream
7473

7574
int _dmaChannel;
7675

76+
bool _dmaTransferInProgress;
77+
I2SDoubleBuffer _doubleBuffer;
78+
7779
void (*_onTransmit)(void);
7880
};
7981

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright (c) 2016 Arduino LLC. All right reserved.
3+
4+
This library is free software; you can redistribute it and/or
5+
modify it under the terms of the GNU Lesser General Public
6+
License as published by the Free Software Foundation; either
7+
version 2.1 of the License, or (at your option) any later version.
8+
9+
This library is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
See the GNU Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public
15+
License along with this library; if not, write to the Free Software
16+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
#include <string.h>
20+
21+
#include "I2SDoubleBuffer.h"
22+
23+
I2SDoubleBuffer::I2SDoubleBuffer()
24+
{
25+
reset();
26+
}
27+
28+
I2SDoubleBuffer::~I2SDoubleBuffer()
29+
{
30+
}
31+
32+
void I2SDoubleBuffer::reset()
33+
{
34+
_index = 0;
35+
_length[0] = 0;
36+
_length[1] = 0;
37+
}
38+
39+
size_t I2SDoubleBuffer::availableForWrite()
40+
{
41+
return (I2S_BUFFER_SIZE - _length[_index]);
42+
}
43+
44+
size_t I2SDoubleBuffer::write(const void *buffer, size_t size)
45+
{
46+
size_t space = availableForWrite();
47+
48+
if (size > space) {
49+
size = space;
50+
}
51+
52+
if (size == 0) {
53+
return 0;
54+
}
55+
56+
memcpy(&_buffer[_index][_length[_index]], buffer, size);
57+
58+
_length[_index] += size;
59+
60+
return size;
61+
}
62+
63+
void* I2SDoubleBuffer::data()
64+
{
65+
return (void*)_buffer[_index];
66+
}
67+
68+
size_t I2SDoubleBuffer::available()
69+
{
70+
return _length[_index];
71+
}
72+
73+
void I2SDoubleBuffer::swap()
74+
{
75+
if (_index == 0) {
76+
_index = 1;
77+
} else {
78+
_index = 0;
79+
}
80+
81+
_length[_index] = 0;
82+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright (c) 2016 Arduino LLC. All right reserved.
3+
4+
This library is free software; you can redistribute it and/or
5+
modify it under the terms of the GNU Lesser General Public
6+
License as published by the Free Software Foundation; either
7+
version 2.1 of the License, or (at your option) any later version.
8+
9+
This library is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
See the GNU Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public
15+
License along with this library; if not, write to the Free Software
16+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
#ifndef _I2S_DOUBLE_BUFFER_H_INCLUDED
20+
#define _I2S_DOUBLE_BUFFER_H_INCLUDED
21+
22+
#include <stddef.h>
23+
#include <stdint.h>
24+
25+
#define I2S_BUFFER_SIZE 512
26+
27+
class I2SDoubleBuffer
28+
{
29+
public:
30+
I2SDoubleBuffer();
31+
virtual ~I2SDoubleBuffer();
32+
33+
void reset();
34+
35+
size_t availableForWrite();
36+
size_t write(const void *buffer, size_t size);
37+
void* data();
38+
size_t available();
39+
void swap();
40+
41+
private:
42+
uint8_t _buffer[2][I2S_BUFFER_SIZE];
43+
volatile int _length[2];
44+
volatile int _index;
45+
};
46+
47+
#endif

libraries/I2S/src/utility/SAMD21_I2SDevice.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ class I2SDevice_SAMD21G18x {
157157
}
158158
}
159159

160+
inline void* data(int index) {
161+
return (void*)&i2s.DATA[index].reg;
162+
}
163+
160164
private:
161165
volatile I2s &i2s;
162166
};

0 commit comments

Comments
 (0)