1 | /*
|
2 | * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
3 | *
|
4 | * SPDX-License-Identifier: Unlicense OR CC0-1.0
|
5 | */
|
6 |
|
7 | #include <stdint.h>
|
8 | #include <stdlib.h>
|
9 | #include "freertos/FreeRTOS.h"
|
10 | #include "freertos/task.h"
|
11 | #include "driver/i2s_std.h"
|
12 | #include "driver/gpio.h"
|
13 | #include "esp_check.h"
|
14 | #include "sdkconfig.h"
|
15 | #include "i2s_example_pins.h"
|
16 | #include <math.h>
|
17 |
|
18 |
|
19 | /* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal
|
20 | * Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated,
|
21 | * Specifically, due to the hardware limitation, the simplex rx & tx channels can't be registered on the same controllers on ESP32 and ESP32-S2,
|
22 | * and ESP32-S2 has only one I2S controller, so it can't allocate two simplex channels */
|
23 | #define EXAMPLE_I2S_DUPLEX_MODE CONFIG_USE_DUPLEX
|
24 |
|
25 | #define EXAMPLE_STD_BCLK_IO1 EXAMPLE_I2S_BCLK_IO1 // I2S bit clock io number
|
26 | #define EXAMPLE_STD_WS_IO1 EXAMPLE_I2S_WS_IO1 // I2S word select io number
|
27 | #define EXAMPLE_STD_DOUT_IO1 EXAMPLE_I2S_DOUT_IO1 // I2S data out io number
|
28 | #define EXAMPLE_STD_DIN_IO1 EXAMPLE_I2S_DIN_IO1 // I2S data in io number
|
29 | #if !EXAMPLE_I2S_DUPLEX_MODE
|
30 | #define EXAMPLE_STD_BCLK_IO2 EXAMPLE_I2S_BCLK_IO2 // I2S bit clock io number
|
31 | #define EXAMPLE_STD_WS_IO2 EXAMPLE_I2S_WS_IO2 // I2S word select io number
|
32 | #define EXAMPLE_STD_DOUT_IO2 EXAMPLE_I2S_DOUT_IO2 // I2S data out io number
|
33 | #define EXAMPLE_STD_DIN_IO2 EXAMPLE_I2S_DIN_IO2 // I2S data in io number
|
34 | #endif
|
35 |
|
36 | #define EXAMPLE_BUFF_SIZE 2048
|
37 | #define SAMPLE_RATE 22050
|
38 |
|
39 | static i2s_chan_handle_t tx_chan; // I2S tx channel handler
|
40 | static i2s_chan_handle_t rx_chan; // I2S rx channel handler
|
41 |
|
42 | static void i2s_example_read_task(void *args)
|
43 | {
|
44 | uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
45 | assert(r_buf); // Check if r_buf allocation success
|
46 | size_t r_bytes = 0;
|
47 |
|
48 | /* Enable the RX channel */
|
49 | ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
|
50 |
|
51 | /* ATTENTION: The print and delay in the read task only for monitoring the data by human,
|
52 | * Normally there shouldn't be any delays to ensure a short polling time,
|
53 | * Otherwise the dma buffer will overflow and lead to the data lost */
|
54 | while (1) {
|
55 | /* Read i2s data */
|
56 | if (i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 1000) == ESP_OK) {
|
57 | printf("Read Task: i2s read %d bytes\n-----------------------------------\n", r_bytes);
|
58 | printf("[0] %x [1] %x [2] %x [3] %x\n[4] %x [5] %x [6] %x [7] %x\n\n",
|
59 | r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
|
60 | } else {
|
61 | printf("Read Task: i2s read failed\n");
|
62 | }
|
63 | vTaskDelay(pdMS_TO_TICKS(200));
|
64 | }
|
65 | free(r_buf);
|
66 | vTaskDelete(NULL);
|
67 | }
|
68 |
|
69 | static void i2s_example_write_task(void *args)
|
70 | {
|
71 | uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
|
72 | assert(w_buf); // Check if w_buf allocation success
|
73 |
|
74 | for (int i = 0; i < EXAMPLE_BUFF_SIZE / 2; i++) {
|
75 | float phase = (2.0f * M_PI * i) / (SAMPLE_RATE / 220);
|
76 | int16_t sample = (int16_t)(127 * sinf(phase));
|
77 | w_buf[i] = sample; // Kein MSB nötig bei 8 Bit
|
78 | w_buf[2 * i + 1] = (sample >> 8) & 0xFF; // MSB
|
79 | }
|
80 |
|
81 |
|
82 | size_t w_bytes = EXAMPLE_BUFF_SIZE;
|
83 |
|
84 | /* (Optional) Preload the data before enabling the TX channel, so that the valid data can be transmitted immediately */
|
85 | while (w_bytes == EXAMPLE_BUFF_SIZE) {
|
86 | /* Here we load the target buffer repeatedly, until all the DMA buffers are preloaded */
|
87 | ESP_ERROR_CHECK(i2s_channel_preload_data(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes));
|
88 | }
|
89 |
|
90 | /* Enable the TX channel */
|
91 | ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
|
92 | while (1) {
|
93 | /* Write i2s data */
|
94 | if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) {
|
95 | printf("Write Task: i2s write %d bytes\n", w_bytes);
|
96 | } else {
|
97 | printf("Write Task: i2s write failed\n");
|
98 | }
|
99 | vTaskDelay(pdMS_TO_TICKS(200));
|
100 | }
|
101 | free(w_buf);
|
102 | vTaskDelete(NULL);
|
103 | }
|
104 |
|
105 | static void i2s_example_init_std_simplex(void)
|
106 | {
|
107 | /* Setp 1: Determine the I2S channel configuration and allocate two channels one by one
|
108 | * The default configuration can be generated by the helper macro,
|
109 | * it only requires the I2S controller id and I2S role
|
110 | * The tx and rx channels here are registered on different I2S controller,
|
111 | * Except ESP32 and ESP32-S2, others allow to register two separate tx & rx channels on a same controller */
|
112 | i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
113 | ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
114 | i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
115 | ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_chan));
|
116 |
|
117 | /* Step 2: Setting the configurations of standard mode and initialize each channels one by one
|
118 | * The slot configuration and clock configuration can be generated by the macros
|
119 | * These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode.
|
120 | * They can help to specify the slot and clock configurations for initialization or re-configuring */
|
121 |
|
122 |
|
123 |
|
124 |
|
125 | i2s_std_config_t std_cfg = {
|
126 | .clk_cfg = {
|
127 | .sample_rate_hz = SAMPLE_RATE,
|
128 | .clk_src = I2S_CLK_SRC_DEFAULT,
|
129 | .mclk_multiple = I2S_MCLK_MULTIPLE_256
|
130 | },
|
131 | .slot_cfg = {
|
132 | .data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
|
133 | .slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT,
|
134 | .slot_mode = I2S_SLOT_MODE_STEREO,
|
135 | .slot_mask = I2S_STD_SLOT_BOTH,
|
136 | .ws_width = I2S_DATA_BIT_WIDTH_16BIT,
|
137 | .ws_pol = false,
|
138 | .bit_shift = true,
|
139 | .msb_right = false
|
140 | },
|
141 | .gpio_cfg = {
|
142 | .mclk = I2S_GPIO_UNUSED,
|
143 | .bclk = GPIO_NUM_5,
|
144 | .ws = GPIO_NUM_25,
|
145 | .dout = GPIO_NUM_26,
|
146 | .din = I2S_GPIO_UNUSED,
|
147 | .invert_flags = {
|
148 | .mclk_inv = false,
|
149 | .bclk_inv = false,
|
150 | .ws_inv = false
|
151 | }
|
152 | }
|
153 | };
|
154 |
|
155 | ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
|
156 |
|
157 |
|
158 | i2s_std_config_t rx_std_cfg = {
|
159 | .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(22050),
|
160 | .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
|
161 | .gpio_cfg = {
|
162 | .mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
|
163 | .bclk = EXAMPLE_STD_BCLK_IO2,
|
164 | .ws = EXAMPLE_STD_WS_IO2,
|
165 | .dout = EXAMPLE_STD_DOUT_IO2,
|
166 | .din = EXAMPLE_STD_DIN_IO2,
|
167 | .invert_flags = {
|
168 | .mclk_inv = false,
|
169 | .bclk_inv = false,
|
170 | .ws_inv = false,
|
171 | },
|
172 | },
|
173 | };
|
174 | /* Default is only receiving left slot in mono mode,
|
175 | * update to right here to show how to change the default configuration */
|
176 | rx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT;
|
177 | ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &rx_std_cfg));
|
178 | }
|
179 |
|
180 |
|
181 | extern "C" void app_main(void)
|
182 | {
|
183 | #if EXAMPLE_I2S_DUPLEX_MODE
|
184 | i2s_example_init_std_duplex();
|
185 | #else
|
186 | i2s_example_init_std_simplex();
|
187 | #endif
|
188 |
|
189 | /* Step 3: Create writing and reading task, enable and start the channels */
|
190 | xTaskCreate(i2s_example_read_task, "i2s_example_read_task", 4096, NULL, 5, NULL);
|
191 | xTaskCreate(i2s_example_write_task, "i2s_example_write_task", 4096, NULL, 5,
|