Skip to content

Commit d01966d

Browse files
iabdalkaderdelta-G
authored andcommitted
libraries: Add I2S library.
Signed-off-by: iabdalkader <[email protected]>
1 parent d6ddd89 commit d01966d

File tree

4 files changed

+337
-0
lines changed

4 files changed

+337
-0
lines changed

libraries/I2S/I2S.cpp

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
This file is part of the Arduino_AdvancedAnalog library.
3+
Copyright (c) 2024 Arduino SA. All rights reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
#include "I2S.h"
21+
22+
I2SClass I2S;
23+
24+
extern "C" i2s_cfg_t g_i2s0_cfg;
25+
extern "C" ssi_instance_ctrl_t g_i2s0_ctrl;
26+
extern "C" void i2s_callback(i2s_callback_args_t *p_args);
27+
28+
int I2SClass::start_transfer() {
29+
if (!rx_buf && (i2s_mode & I2S_MODE_IN)) {
30+
rx_buf = rx_pool->alloc(DMA_BUFFER_WRITE);
31+
}
32+
33+
if (!tx_buf && (i2s_mode & I2S_MODE_OUT)) {
34+
tx_buf = tx_pool->alloc(DMA_BUFFER_READ);
35+
}
36+
37+
// Start I2S DMA.
38+
if (i2s_mode == I2S_MODE_IN) {
39+
if (R_SSI_Read(&g_i2s0_ctrl, (void*) rx_buf->data(), rx_buf->bytes()) != FSP_SUCCESS) {
40+
return 0;
41+
}
42+
} else if (i2s_mode == I2S_MODE_OUT) {
43+
if (R_SSI_Write(&g_i2s0_ctrl, (void*) tx_buf->data(), tx_buf->bytes()) != FSP_SUCCESS) {
44+
return 0;
45+
}
46+
} else {
47+
if (R_SSI_WriteRead(&g_i2s0_ctrl, (void*) tx_buf->data(),
48+
(void*) rx_buf->data(), tx_buf->bytes()) != FSP_SUCCESS) {
49+
return 0;
50+
}
51+
}
52+
return 1;
53+
}
54+
55+
int I2SClass::begin(uint32_t i2s_mode, uint32_t sample_rate, size_t n_samples, size_t n_buffers) {
56+
this->i2s_mode = i2s_mode;
57+
58+
IRQManager::getInstance().addPeripheral(IRQ_I2S, &g_i2s0_cfg);
59+
if (R_SSI_Open(&g_i2s0_ctrl, &g_i2s0_cfg) != FSP_SUCCESS) {
60+
return false;
61+
}
62+
63+
if (R_SSI_CallbackSet(&g_i2s0_ctrl, i2s_callback, this, NULL) != FSP_SUCCESS) {
64+
return false;
65+
}
66+
67+
// Internal AUDIO_CLK from GPT channel.
68+
// Need to find the timer connected to GPT_TIMER 2A for internal clock
69+
auto pwm = new PwmOut(64);
70+
pwm->begin(50, 25, true);
71+
72+
// Configure I/Os.
73+
pinPeripheral(BSP_IO_PORT_01_PIN_12, (uint32_t) (IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_SSI));
74+
pinPeripheral(BSP_IO_PORT_01_PIN_13, (uint32_t) (IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_SSI));
75+
pinPeripheral(BSP_IO_PORT_01_PIN_14, (uint32_t) (IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_SSI));
76+
pinPeripheral(BSP_IO_PORT_01_PIN_15, (uint32_t) (IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_SSI));
77+
78+
if (i2s_mode & I2S_MODE_IN) {
79+
// Allocate RX buffer pool.
80+
if (!(rx_pool = new DMAPool<Sample>(n_samples, 2, n_buffers))) {
81+
return 0;
82+
}
83+
}
84+
85+
if (i2s_mode & I2S_MODE_OUT) {
86+
// Allocate TX buffer pool.
87+
if (!(tx_pool = new DMAPool<Sample>(n_samples, 2, n_buffers))) {
88+
return 0;
89+
}
90+
}
91+
92+
if (i2s_mode == I2S_MODE_IN) {
93+
return start_transfer();
94+
}
95+
96+
if (i2s_mode == I2S_MODE_INOUT) {
97+
// The transmit pool has to be primed with a few buffers first,
98+
// before the transfer can be started in full-duplex mode.
99+
for (int i=0; i<3; i++) {
100+
SampleBuffer outbuf = dequeue();
101+
memset(outbuf.data(), 0, outbuf.bytes());
102+
write(outbuf);
103+
}
104+
}
105+
106+
return 1;
107+
}
108+
109+
bool I2SClass::available() {
110+
if (rx_pool && i2s_mode == I2S_MODE_IN) {
111+
return rx_pool->readable();
112+
} else if (tx_pool && i2s_mode == I2S_MODE_OUT) {
113+
return tx_pool->writable();
114+
} else if (tx_pool && rx_pool) {
115+
return rx_pool->readable() && tx_pool->writable();
116+
}
117+
return false;
118+
}
119+
120+
SampleBuffer I2SClass::read() {
121+
while (!rx_pool->readable()) {
122+
__WFI();
123+
}
124+
return *rx_pool->alloc(DMA_BUFFER_READ);
125+
}
126+
127+
SampleBuffer I2SClass::dequeue() {
128+
while (!tx_pool->writable()) {
129+
__WFI();
130+
}
131+
return *tx_pool->alloc(DMA_BUFFER_WRITE);
132+
}
133+
134+
void I2SClass::write(SampleBuffer buf) {
135+
static uint32_t buf_count = 0;
136+
if (tx_pool == nullptr) {
137+
return;
138+
}
139+
140+
buf.flush();
141+
buf.release();
142+
143+
if (tx_buf == nullptr && (++buf_count % 3) == 0) {
144+
start_transfer();
145+
}
146+
}
147+
148+
extern "C" void i2s_callback(i2s_callback_args_t *p_args) {
149+
if (p_args == NULL) {
150+
return;
151+
}
152+
153+
i2s_event_t i2s_event = p_args->event;
154+
I2SClass *i2s = (I2SClass *) p_args->p_context;
155+
156+
if (i2s_event == I2S_EVENT_TX_EMPTY) {
157+
// Release the current buffer and get the next one.
158+
if (i2s->tx_pool->readable()) {
159+
i2s->tx_buf->release();
160+
i2s->tx_buf = i2s->tx_pool->alloc(DMA_BUFFER_READ);
161+
} else {
162+
// TODO stop/restart
163+
}
164+
R_SSI_Write(&g_i2s0_ctrl, (void*) i2s->tx_buf->data(), i2s->tx_buf->bytes());
165+
}
166+
167+
if (i2s_event == I2S_EVENT_RX_FULL) {
168+
// Update the buffer's timestamp.
169+
//i2s->rx_buf->timestamp(us_ticker_read());
170+
if (i2s->rx_pool->writable()) {
171+
// Move current DMA buffer to ready queue.
172+
i2s->rx_buf->release();
173+
// Allocate a new free buffer.
174+
i2s->rx_buf = i2s->rx_pool->alloc(DMA_BUFFER_WRITE);
175+
// Currently, all multi-channel buffers are interleaved.
176+
if (i2s->rx_buf->channels() > 1) {
177+
i2s->rx_buf->set_flags(DMA_BUFFER_INTRLVD);
178+
}
179+
} else {
180+
i2s->rx_buf->set_flags(DMA_BUFFER_DISCONT);
181+
}
182+
R_SSI_Read(&g_i2s0_ctrl, (void*) i2s->rx_buf->data(), i2s->rx_buf->bytes());
183+
}
184+
}

libraries/I2S/I2S.h

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
This file is part of the Arduino_AdvancedAnalog library.
3+
Copyright (c) 2024 Arduino SA. All rights reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
#ifndef __I2S_H__
20+
#define __I2S_H__
21+
22+
#include "r_i2s_api.h"
23+
#include "r_ssi.h"
24+
#include "pwm.h"
25+
#include "api/DMAPool.h"
26+
27+
typedef uint16_t Sample;
28+
typedef DMABuffer<Sample> &SampleBuffer;
29+
30+
enum {
31+
I2S_MODE_IN = (1U << 0U),
32+
I2S_MODE_OUT = (1U << 1U),
33+
I2S_MODE_INOUT = (I2S_MODE_IN | I2S_MODE_OUT),
34+
};
35+
36+
class I2SClass {
37+
private:
38+
uint32_t i2s_mode;
39+
int start_transfer();
40+
41+
public:
42+
DMABuffer<Sample> *tx_buf;
43+
DMABuffer<Sample> *rx_buf;
44+
DMAPool<Sample> *tx_pool;
45+
DMAPool<Sample> *rx_pool;
46+
47+
I2SClass(): tx_buf(nullptr), rx_buf(nullptr), tx_pool(nullptr), rx_pool(nullptr) {
48+
}
49+
bool available();
50+
SampleBuffer read();
51+
SampleBuffer dequeue();
52+
void write(SampleBuffer buf);
53+
int begin(uint32_t i2s_mode, uint32_t sample_rate, size_t n_samples, size_t n_buffers);
54+
int stop();
55+
};
56+
57+
extern I2SClass I2S;
58+
#endif // __I2S_H__

libraries/I2S/config.c

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include "r_i2s_api.h"
2+
#include "r_ssi.h"
3+
#include "r_dtc.h"
4+
dtc_instance_ctrl_t g_transfer0_ctrl;
5+
6+
transfer_info_t g_transfer0_info = {
7+
.transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED,
8+
.transfer_settings_word_b.repeat_area = TRANSFER_REPEAT_AREA_SOURCE,
9+
.transfer_settings_word_b.irq = TRANSFER_IRQ_END,
10+
.transfer_settings_word_b.chain_mode = TRANSFER_CHAIN_MODE_DISABLED,
11+
.transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED,
12+
.transfer_settings_word_b.size = TRANSFER_SIZE_4_BYTE,
13+
.transfer_settings_word_b.mode = TRANSFER_MODE_BLOCK,
14+
.p_dest = (void*) NULL,
15+
.p_src = (void const*) NULL,
16+
.num_blocks = 0,
17+
.length = 0,
18+
};
19+
20+
const dtc_extended_cfg_t g_transfer0_cfg_extend ={
21+
.activation_source = VECTOR_NUMBER_SSI0_TXI,
22+
};
23+
24+
const transfer_cfg_t g_transfer0_cfg = {
25+
.p_info = &g_transfer0_info,
26+
.p_extend = &g_transfer0_cfg_extend
27+
};
28+
29+
/* Instance structure to use this module. */
30+
const transfer_instance_t g_transfer0 = {
31+
.p_ctrl = &g_transfer0_ctrl,
32+
.p_cfg = &g_transfer0_cfg,
33+
.p_api = &g_transfer_on_dtc
34+
};
35+
ssi_instance_ctrl_t g_i2s0_ctrl;
36+
37+
/** SSI instance configuration */
38+
const ssi_extended_cfg_t g_i2s0_cfg_extend = {
39+
.audio_clock = SSI_AUDIO_CLOCK_INTERNAL,
40+
.bit_clock_div = SSI_CLOCK_DIV_1
41+
};
42+
43+
/** I2S interface configuration */
44+
const i2s_cfg_t g_i2s0_cfg = {
45+
.channel = 0,
46+
.pcm_width = I2S_PCM_WIDTH_16_BITS,
47+
//.pcm_width = I2S_PCM_WIDTH_32_BITS,
48+
.operating_mode = I2S_MODE_MASTER,
49+
.word_length = I2S_WORD_LENGTH_32_BITS,
50+
.ws_continue = I2S_WS_CONTINUE_ON,
51+
.p_callback = NULL,
52+
.p_context = NULL,
53+
.p_extend = &g_i2s0_cfg_extend,
54+
.txi_irq = FSP_INVALID_VECTOR,
55+
.rxi_irq = FSP_INVALID_VECTOR,
56+
.int_irq = FSP_INVALID_VECTOR,
57+
.txi_ipl = (2),
58+
.rxi_ipl = (2),
59+
.idle_err_ipl = (2),
60+
.p_transfer_tx = NULL, // MAY be null
61+
.p_transfer_rx = NULL,
62+
};
63+
64+
/* Instance structure to use this module. */
65+
const i2s_instance_t g_i2s0 = {
66+
.p_ctrl = &g_i2s0_ctrl,
67+
.p_cfg = &g_i2s0_cfg,
68+
.p_api = &g_i2s_on_ssi
69+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <I2S.h>
2+
3+
void setup() {
4+
Serial.begin(9600);
5+
while (!Serial) {
6+
7+
}
8+
9+
// Resolution, sample rate, number of samples per channel, queue depth.
10+
if (!I2S.begin(I2S_MODE_INOUT, 32000, 256, 8)) {
11+
Serial.println("Failed to start I2S");
12+
while (1);
13+
}
14+
}
15+
16+
void loop() {
17+
if (I2S.available()) {
18+
SampleBuffer rxbuf = I2S.read();
19+
SampleBuffer txbuf = I2S.dequeue();
20+
for (size_t i=0; i<rxbuf.size(); i++) {
21+
txbuf[i] = rxbuf[i];
22+
}
23+
rxbuf.release();
24+
I2S.write(txbuf);
25+
}
26+
}

0 commit comments

Comments
 (0)