Skip to content

Commit 23a4eb8

Browse files
authored
Merge pull request #306 from arduino/add_i2s_support
Add I2S support.
2 parents 3c7003a + 867ec21 commit 23a4eb8

File tree

7 files changed

+385
-1
lines changed

7 files changed

+385
-1
lines changed

Diff for: cores/arduino/IRQManager.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#define ADC_PRIORITY 12
2424
#define CAN_PRIORITY 12
2525
#define CANFD_PRIORITY 12
26+
#define I2S_PRIORITY 12
2627
#define FIRST_INT_SLOT_FREE 0
2728

2829
IRQManager::IRQManager() : last_interrupt_index{0} {
@@ -1006,6 +1007,41 @@ bool IRQManager::addPeripheral(Peripheral_t p, void *cfg) {
10061007
}
10071008
}
10081009
#endif
1010+
1011+
#if I2S_HOWMANY > 0
1012+
/* **********************************************************************
1013+
I2S
1014+
********************************************************************** */
1015+
else if(p == IRQ_I2S && cfg != NULL) {
1016+
i2s_cfg_t *i2s_cfg = (i2s_cfg_t *)cfg;
1017+
1018+
if(i2s_cfg->txi_irq == FSP_INVALID_VECTOR) {
1019+
i2s_cfg->txi_irq = (IRQn_Type)last_interrupt_index;
1020+
i2s_cfg->txi_ipl = I2S_PRIORITY;
1021+
*(irq_ptr + last_interrupt_index) = (uint32_t)ssi_txi_isr;
1022+
R_ICU->IELSR[last_interrupt_index] = BSP_PRV_IELS_ENUM(EVENT_SSI0_TXI);
1023+
R_FSP_IsrContextSet(i2s_cfg->txi_irq, (void*)i2s_cfg->p_context);
1024+
last_interrupt_index++;
1025+
}
1026+
if(i2s_cfg->rxi_irq == FSP_INVALID_VECTOR) {
1027+
i2s_cfg->rxi_irq = (IRQn_Type)last_interrupt_index;
1028+
i2s_cfg->rxi_ipl = I2S_PRIORITY;
1029+
*(irq_ptr + last_interrupt_index) = (uint32_t)ssi_rxi_isr;
1030+
R_ICU->IELSR[last_interrupt_index] = BSP_PRV_IELS_ENUM(EVENT_SSI0_RXI);
1031+
R_FSP_IsrContextSet(i2s_cfg->rxi_irq, (void*)i2s_cfg->p_context);
1032+
last_interrupt_index++;
1033+
}
1034+
if(i2s_cfg->int_irq == FSP_INVALID_VECTOR) {
1035+
i2s_cfg->int_irq = (IRQn_Type)last_interrupt_index;
1036+
i2s_cfg->idle_err_ipl = I2S_PRIORITY;
1037+
*(irq_ptr + last_interrupt_index) = (uint32_t)ssi_int_isr;
1038+
R_ICU->IELSR[last_interrupt_index] = BSP_PRV_IELS_ENUM(EVENT_SSI0_INT);
1039+
R_FSP_IsrContextSet(i2s_cfg->int_irq, (void*)i2s_cfg->p_context);
1040+
last_interrupt_index++;
1041+
}
1042+
}
1043+
#endif
1044+
10091045
else {
10101046
rv = false;
10111047
}

Diff for: cores/arduino/IRQManager.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919
#include "r_external_irq_api.h"
2020
#endif
2121

22+
#if I2S_HOWMANY > 0
23+
#include "r_ssi.h"
24+
extern "C" {
25+
void ssi_txi_isr(void);
26+
void ssi_rxi_isr(void);
27+
void ssi_int_isr(void);
28+
}
29+
#endif
30+
2231
#include "r_timer_api.h"
2332

2433
#ifdef ELC_EVENT_DMAC0_INT
@@ -44,7 +53,8 @@ typedef enum {
4453
IRQ_CAN,
4554
IRQ_ETHERNET,
4655
IRQ_CANFD,
47-
IRQ_SDCARD
56+
IRQ_SDCARD,
57+
IRQ_I2S
4858
} Peripheral_t;
4959

5060
#if SDCARD_HOWMANY > 0

Diff for: 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+
}

Diff for: 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__

Diff for: 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+
};

0 commit comments

Comments
 (0)