Select one of the symbols to view example projects that use it.
 
Outline
#include <stdint.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_tdm.h"
#include "driver/gpio.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "i2s_example_pins.h"
#define EXAMPLE_I2S_DUPLEX_MODE
#define EXAMPLE_TDM_BCLK_IO1
#define EXAMPLE_TDM_WS_IO1
#define EXAMPLE_TDM_DOUT_IO1
#define EXAMPLE_TDM_DIN_IO1
#define EXAMPLE_TDM_BCLK_IO2
#define EXAMPLE_TDM_WS_IO2
#define EXAMPLE_TDM_DOUT_IO2
#define EXAMPLE_TDM_DIN_IO2
#define EXAMPLE_BUFF_SIZE
tx_chan
rx_chan
i2s_example_read_task(void *)
i2s_example_write_task(void *)
i2s_example_init_tdm_duplex()
app_main()
Files
loading (1/3)...
SourceVuESP-IDF Framework and Examplesi2s_tdm samplemain/i2s_tdm_example_main.c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/* * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 *//* ... */ #include <stdint.h> #include <stdlib.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/i2s_tdm.h" #include "driver/gpio.h" #include "esp_check.h" #include "sdkconfig.h" #include "i2s_example_pins.h"9 includes /* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal * Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated *//* ... */ #define EXAMPLE_I2S_DUPLEX_MODE 1 #define EXAMPLE_TDM_BCLK_IO1 EXAMPLE_I2S_BCLK_IO1 // I2S bit clock io number #define EXAMPLE_TDM_WS_IO1 EXAMPLE_I2S_WS_IO1 // I2S word select io number #define EXAMPLE_TDM_DOUT_IO1 EXAMPLE_I2S_DOUT_IO1 // I2S data out io number #define EXAMPLE_TDM_DIN_IO1 EXAMPLE_I2S_DIN_IO1 // I2S data in io number5 defines #if !EXAMPLE_I2S_DUPLEX_MODE #define EXAMPLE_TDM_BCLK_IO2 EXAMPLE_I2S_BCLK_IO2 // I2S bit clock io number #define EXAMPLE_TDM_WS_IO2 EXAMPLE_I2S_WS_IO2 // I2S word select io number #define EXAMPLE_TDM_DOUT_IO2 EXAMPLE_I2S_DOUT_IO2 // I2S data out io number #define EXAMPLE_TDM_DIN_IO2 EXAMPLE_I2S_DIN_IO2 // I2S data in io number/* ... */ #endif // !EXAMPLE_I2S_DUPLEX_MODE #define EXAMPLE_BUFF_SIZE 2048 static i2s_chan_handle_t tx_chan; // I2S tx channel handler static i2s_chan_handle_t rx_chan; // I2S rx channel handler static void i2s_example_read_task(void *args) { uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE); assert(r_buf); // Check if r_buf allocation success size_t r_bytes = 0; /* Enable the RX channel */ ESP_ERROR_CHECK(i2s_channel_enable(rx_chan)); /* ATTENTION: The print and delay in the read task only for monitoring the data by human, * Normally there shouldn't be any delays to ensure a short polling time, * Otherwise the dma buffer will overflow and lead to the data lost *//* ... */ while (1) { /* Read i2s data */ if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) { printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes); printf("[0] %x [1] %x [2] %x [3] %x\n[4] %x [5] %x [6] %x [7] %x\n\n", r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]); }{...} else { printf("Read Task: i2s read failed\n"); }{...} vTaskDelay(pdMS_TO_TICKS(200)); }{...} free(r_buf); vTaskDelete(NULL); }{ ... } static void i2s_example_write_task(void *args) { uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE); assert(w_buf); // Check if w_buf allocation success /* Assign w_buf */ for (int i = 0; i < EXAMPLE_BUFF_SIZE; i += 8) { w_buf[i] = 0x12; w_buf[i + 1] = 0x34; w_buf[i + 2] = 0x56; w_buf[i + 3] = 0x78; w_buf[i + 4] = 0x9A; w_buf[i + 5] = 0xBC; w_buf[i + 6] = 0xDE; w_buf[i + 7] = 0xF0; }{...} size_t w_bytes = EXAMPLE_BUFF_SIZE; /* (Optional) Preload the data before enabling the TX channel, so that the valid data can be transmitted immediately */ while (w_bytes == EXAMPLE_BUFF_SIZE) { /* Here we load the target buffer repeatedly, until all the DMA buffers are preloaded */ ESP_ERROR_CHECK(i2s_channel_preload_data(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes)); }{...} /* Enable the TX channel */ ESP_ERROR_CHECK(i2s_channel_enable(tx_chan)); while (1) { /* Write i2s data */ if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) { printf("Write Task: i2s write %d bytes\n", w_bytes); }{...} else { printf("Write Task: i2s write failed\n"); }{...} vTaskDelay(pdMS_TO_TICKS(200)); }{...} free(w_buf); vTaskDelete(NULL); }{ ... } #if EXAMPLE_I2S_DUPLEX_MODE static void i2s_example_init_tdm_duplex(void) { /* Setp 1: Determine the I2S channel configuration and allocate both channels * The default configuration can be generated by the helper macro, * it only requires the I2S controller id and I2S role *//* ... */ i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan)); /* Step 2: Setting the configurations of TDM mode, and initialize rx & tx channels * The slot configuration and clock configuration can be generated by the macros * These two helper macros is defined in 'i2s_tdm.h' which can only be used in TDM mode. * They can help to specify the slot and clock configurations for initialization or re-configuring *//* ... */ i2s_tdm_config_t tdm_cfg = { .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000), /* For the target that not support full data bit-width in multiple slots (e.g. ESP32C3, ESP32S3, ESP32C6) * The maximum bits in one frame is limited by the hardware, the number of bit clock can't exceed 128 in one frame, * which is to say, TDM mode can only support 32 bit-width data upto 4 slots, * 16 bit-width data upto 8 slots and 8 bit-width data upto 16 slots * But for the target that support full data bit-width in multiple slots (e.g. ESP32H2) * There is no such limitation, it can support up to 32 bit-width with 16 slots *//* ... */ .slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3), .gpio_cfg = { .mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it .bclk = EXAMPLE_TDM_BCLK_IO1, .ws = EXAMPLE_TDM_WS_IO1, .dout = EXAMPLE_TDM_DOUT_IO1, .din = EXAMPLE_TDM_DOUT_IO1, // In duplex mode, bind output and input to a same gpio can loopback internally .invert_flags = { .mclk_inv = false, .bclk_inv = false, .ws_inv = false, }{...}, }{...}, }{...}; /* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too small * The driver will increasing the multiple automatically to ensure the bclk_div is greater than 2. * Modify the mclk_multiple to 512 directly here to avoid the warning *//* ... */ tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512; /* Initialize the channels */ ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tdm_cfg)); ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &tdm_cfg)); }{ ... } /* ... */#else static void i2s_example_init_tdm_simplex(void) { /* Setp 1: Determine the I2S channel configuration and allocate two channels one by one * The default configuration can be generated by the helper macro, * it only requires the I2S controller id and I2S role *//* ... */ i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL)); i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan)); /* Step 2: Setting the configurations of TDM mode and initialize each channels one by one * The slot configuration and clock configuration can be generated by the macros * These two helper macros is defined in 'i2s_tdm.h' which can only be used in TDM mode. * They can help to specify the slot and clock configurations for initialization or re-configuring *//* ... */ i2s_tdm_config_t tx_tdm_cfg = { .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000), .slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3), .gpio_cfg = { .mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it .bclk = EXAMPLE_TDM_BCLK_IO1, .ws = EXAMPLE_TDM_WS_IO1, .dout = EXAMPLE_TDM_DOUT_IO1, .din = EXAMPLE_TDM_DIN_IO1, .invert_flags = { .mclk_inv = false, .bclk_inv = false, .ws_inv = false, }{...}, }{...}, }{...}; tx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512; ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(tx_chan, &tx_tdm_cfg)); i2s_tdm_config_t rx_tdm_cfg = { .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(44100), .slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO, I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3 | I2S_TDM_SLOT4 | I2S_TDM_SLOT5 | I2S_TDM_SLOT6 | I2S_TDM_SLOT7), .gpio_cfg = { .mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it .bclk = EXAMPLE_TDM_BCLK_IO2, .ws = EXAMPLE_TDM_WS_IO2, .dout = EXAMPLE_TDM_DOUT_IO2, .din = EXAMPLE_TDM_DIN_IO2, .invert_flags = { .mclk_inv = false, .bclk_inv = false, .ws_inv = false, }{...}, }{...}, }{...}; /* While there are more than two slots activated, receiving data might be wrong if the mclk multiple is too small * The driver will increasing the multiple automatically to ensure the bclk_div is greater than 2. * Modify the mclk_multiple to 512 directly here to avoid the warning *//* ... */ rx_tdm_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_512; ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_chan, &rx_tdm_cfg)); }{...} /* ... */#endif void app_main(void) { #if EXAMPLE_I2S_DUPLEX_MODE i2s_example_init_tdm_duplex(); #else i2s_example_init_tdm_simplex(); #endif /* Step 3: Create writing and reading task, enable and start the channels */ xTaskCreate(i2s_example_read_task, "i2s_example_read_task", 4096, NULL, 5, NULL); xTaskCreate(i2s_example_write_task, "i2s_example_write_task", 4096, NULL, 5, NULL); }{ ... }
Details