ESP-IDF Firmware
Firmware architecture and call graph
Loading...
Searching...
No Matches
wave_gen.c
Go to the documentation of this file.
1#include <math.h>
2#include <string.h>
3#include "sdkconfig.h"
4#include "freertos/FreeRTOS.h"
5#include "freertos/task.h"
6#include "driver/i2s_std.h"
7#include "esp_log.h"
8#include "esp_check.h"
9#include "wave_gen.h"
10
11#if CONFIG_ENABLE_SDM_WAVE_GEN
12#if defined(__has_include)
13#if __has_include("driver/sdm.h") && __has_include("driver/gptimer.h")
14#define WAVE_GEN_HAS_SDM 1
15#include "driver/sdm.h"
16#include "driver/gptimer.h"
17#include "esp_attr.h"
18#else
19#define WAVE_GEN_HAS_SDM 0
20#endif
21#else
22#define WAVE_GEN_HAS_SDM 1
23#include "driver/sdm.h"
24#include "driver/gptimer.h"
25#include "esp_attr.h"
26#endif
27#else
28#define WAVE_GEN_HAS_SDM 0
29#endif
30
31static const char *TAG = "wave_gen";
32
33#define I2S_BUFFER_SIZE 1024
34#define DEFAULT_SAMPLE_RATE 44100
35#define I2S_BUFFER_SIZE 1024
36#define DEFAULT_SAMPLE_RATE 44100
37#define PI 3.14159265358979323846f
38
39static i2s_chan_handle_t tx_conn_handle = NULL;
40static TaskHandle_t s_wave_task_handle = NULL;
41static bool s_running = false;
42
43// Generator State
44static float s_frequency = 440.0f;
45static float s_volume = 0.5f;
48static float s_phase = 0.0f;
49
50#if WAVE_GEN_HAS_SDM
51#define SDM_LUT_SIZE 256
52#define SDM_CARRIER_HZ (1 * 1000 * 1000)
53#define SDM_UPDATE_HZ (20 * 1000)
54#define SDM_DENSITY_MAX 90
55
56static sdm_channel_handle_t s_sdm_chan = NULL;
57static gptimer_handle_t s_sdm_timer = NULL;
58static volatile uint32_t s_sdm_phase_acc = 0;
59static uint32_t s_sdm_phase_step = 0;
60static int8_t s_sdm_lut[SDM_LUT_SIZE];
61static bool s_sdm_running = false;
62
63static void sdm_build_lut(float amplitude) {
64 if (amplitude < 0.0f) amplitude = 0.0f;
65 if (amplitude > 1.0f) amplitude = 1.0f;
66 for (int i = 0; i < SDM_LUT_SIZE; i++) {
67 float phase = (float)i / (float)SDM_LUT_SIZE;
68 float s = sinf(phase * 2.0f * PI);
69 int32_t density = (int32_t)lrintf(s * amplitude * (float)SDM_DENSITY_MAX);
70 if (density > SDM_DENSITY_MAX) density = SDM_DENSITY_MAX;
71 if (density < -SDM_DENSITY_MAX) density = -SDM_DENSITY_MAX;
72 s_sdm_lut[i] = (int8_t)density;
73 }
74}
75
76static bool IRAM_ATTR sdm_timer_cb(gptimer_handle_t timer,
77 const gptimer_alarm_event_data_t *edata,
78 void *user_ctx) {
79 (void)timer;
80 (void)edata;
81 (void)user_ctx;
82 uint32_t phase = s_sdm_phase_acc + s_sdm_phase_step;
83 s_sdm_phase_acc = phase;
84 uint8_t idx = (uint8_t)(phase >> 24);
85 sdm_channel_set_pulse_density(s_sdm_chan, s_sdm_lut[idx]);
86 return false;
87}
88
89static esp_err_t sdm_init(uint32_t freq_hz) {
90 if (s_sdm_chan) {
91 return ESP_OK;
92 }
93
94 sdm_config_t config = {
95 .clk_src = SDM_CLK_SRC_DEFAULT,
96 .sample_rate_hz = SDM_CARRIER_HZ,
97 .gpio_num = CONFIG_SDM_GPIO,
98 };
99 ESP_RETURN_ON_ERROR(sdm_new_channel(&config, &s_sdm_chan), TAG, "Failed to create SDM channel");
100 ESP_RETURN_ON_ERROR(sdm_channel_enable(s_sdm_chan), TAG, "Failed to enable SDM channel");
101
102 sdm_build_lut(s_volume);
103 if (freq_hz < 1) freq_hz = 1;
104 s_sdm_phase_step = (uint32_t)(((uint64_t)freq_hz << 32) / SDM_UPDATE_HZ);
105 s_sdm_phase_acc = 0;
106
107 gptimer_config_t tcfg = {
108 .clk_src = GPTIMER_CLK_SRC_DEFAULT,
109 .direction = GPTIMER_COUNT_UP,
110 .resolution_hz = SDM_UPDATE_HZ,
111 };
112 ESP_RETURN_ON_ERROR(gptimer_new_timer(&tcfg, &s_sdm_timer), TAG, "Failed to create SDM timer");
113
114 gptimer_event_callbacks_t cbs = {
115 .on_alarm = sdm_timer_cb,
116 };
117 ESP_RETURN_ON_ERROR(gptimer_register_event_callbacks(s_sdm_timer, &cbs, NULL), TAG, "Failed to register SDM timer callback");
118
119 gptimer_alarm_config_t alarm_cfg = {
120 .reload_count = 0,
121 .alarm_count = 1,
122 .flags.auto_reload_on_alarm = true,
123 };
124 ESP_RETURN_ON_ERROR(gptimer_set_alarm_action(s_sdm_timer, &alarm_cfg), TAG, "Failed to set SDM timer alarm");
125 ESP_RETURN_ON_ERROR(gptimer_enable(s_sdm_timer), TAG, "Failed to enable SDM timer");
126 ESP_RETURN_ON_ERROR(gptimer_start(s_sdm_timer), TAG, "Failed to start SDM timer");
127
128 return ESP_OK;
129}
130
131static void sdm_deinit(void) {
132 if (s_sdm_timer) {
133 gptimer_stop(s_sdm_timer);
134 gptimer_disable(s_sdm_timer);
135 gptimer_del_timer(s_sdm_timer);
136 s_sdm_timer = NULL;
137 }
138 if (s_sdm_chan) {
139 sdm_channel_disable(s_sdm_chan);
140 sdm_del_channel(s_sdm_chan);
141 s_sdm_chan = NULL;
142 }
143}
144#endif
145
146static void wave_gen_task(void *args)
147{
148 size_t w_bytes = 0;
149 // Stereo buffer (Left + Right interleaving)
150 // Using 32-bit samples for high resolution
151 int32_t *samples = (int32_t *)malloc(I2S_BUFFER_SIZE * 2 * sizeof(int32_t));
152 if (!samples) {
153 ESP_LOGE(TAG, "Failed to allocate audio buffer");
154 vTaskDelete(NULL);
155 return;
156 }
157
158 float phase_inc = 0.0f;
159
160 while (s_running) {
161 // Recalculate phase increment per block to allow dynamic frequency changes
162 // phase goes from 0.0f to 1.0f
163 if (s_sample_rate > 0) {
164 phase_inc = s_frequency / (float)s_sample_rate;
165 }
166
167 // Fill buffer
168 for (int i = 0; i < I2S_BUFFER_SIZE; i++) {
169 float sample_val = 0.0f;
170
171 switch (s_type) {
172 case WAVE_TYPE_SINE:
173 sample_val = sinf(s_phase * 2.0f * PI);
174 break;
175 case WAVE_TYPE_SQUARE:
176 sample_val = (s_phase < 0.5f) ? 1.0f : -1.0f;
177 break;
179 sample_val = (s_phase < 0.5f) ? (4.0f * s_phase - 1.0f) : (3.0f - 4.0f * s_phase);
180 break;
182 sample_val = 2.0f * s_phase - 1.0f;
183 break;
184 }
185
186 // Apply volume and scale to 32-bit signed integer range
187 // Max positive: 2147483647
188 int32_t pcm_val = (int32_t)(sample_val * s_volume * 2147483647.0f);
189
190 // Interleaved Stereo (Left = Right)
191 samples[i * 2] = pcm_val;
192 samples[i * 2 + 1] = pcm_val;
193
194 // Advance phase
195 s_phase += phase_inc;
196 if (s_phase >= 1.0f) {
197 s_phase -= 1.0f;
198 }
199 }
200
201 // Write to I2S
202 /* Because we are writing to a standard I2S channel, we should ensure the format matches the config */
203 if (i2s_channel_write(tx_conn_handle, samples, I2S_BUFFER_SIZE * 2 * sizeof(int32_t), &w_bytes, portMAX_DELAY) != ESP_OK) {
204 ESP_LOGW(TAG, "I2S Write Failed");
205 }
206 }
207
208 free(samples);
209 vTaskDelete(NULL);
210 s_wave_task_handle = NULL;
211}
212
214{
215 if (tx_conn_handle) {
216 ESP_LOGW(TAG, "Already initialized");
217 return ESP_OK;
218 }
219
220 ESP_LOGI(TAG, "Initializing I2S Wave Gen (32-bit) on BCK:%d WS:%d DO:%d MCK:%d",
221 config->bck_io_num, config->ws_io_num, config->data_out_num, config->mck_io_num);
222
223 i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
224 ESP_RETURN_ON_ERROR(i2s_new_channel(&chan_cfg, &tx_conn_handle, NULL), TAG, "Failed to create I2S channel");
225
226 i2s_std_config_t std_cfg = {
227 .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(config->sample_rate),
228 .slot_cfg = I2S_STD_PCM_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
229 .gpio_cfg = {
230 .mclk = config->mck_io_num, // Can be I2S_GPIO_UNUSED (-1)
231 .bclk = config->bck_io_num,
232 .ws = config->ws_io_num,
233 .dout = config->data_out_num,
234 .din = I2S_GPIO_UNUSED,
235 .invert_flags = {
236 .mclk_inv = false,
237 .bclk_inv = false,
238 .ws_inv = false,
239 },
240 },
241 };
242
243 // Some PCM5102 modules benefit from MCLK being enabled if wired,
244 // but often they generate it internally from BCK if MCLK is floating/grounded.
245 // The driver will handle pin configuration.
246
247 ESP_RETURN_ON_ERROR(i2s_channel_init_std_mode(tx_conn_handle, &std_cfg), TAG, "Failed to init I2S std mode");
248
249 s_sample_rate = config->sample_rate;
250 return ESP_OK;
251}
252
254{
255 if (!tx_conn_handle) return ESP_FAIL;
256 if (s_running) return ESP_OK;
257
258 ESP_RETURN_ON_ERROR(i2s_channel_enable(tx_conn_handle), TAG, "Failed to enable channel");
259
260 s_running = true;
261 xTaskCreate(wave_gen_task, "wave_gen_task", 4096, NULL, 5, &s_wave_task_handle);
262 ESP_LOGI(TAG, "Wave Gen Started");
263 return ESP_OK;
264}
265
267{
268 if (!s_running) return ESP_OK;
269 s_running = false;
270 // Wait for task or just let it spin down?
271 // For simplicity, we assume task sees s_running false and exits.
272 // Ideally we'd join or wait.
273
274 // Allow task to exit loop
275 vTaskDelay(pdMS_TO_TICKS(100));
276
277 if (tx_conn_handle) {
278 i2s_channel_disable(tx_conn_handle);
279 }
280 ESP_LOGI(TAG, "Wave Gen Stopped");
281 return ESP_OK;
282}
283
284void wave_gen_set_freq(float freq_hz)
285{
286 if (freq_hz < 1.0f) freq_hz = 1.0f;
287 if (freq_hz > (float)s_sample_rate / 2.0f) freq_hz = (float)s_sample_rate / 2.0f;
288 s_frequency = freq_hz;
289}
290
292{
293 s_type = type;
294}
295
296void wave_gen_set_volume(float volume)
297{
298 if (volume < 0.0f) volume = 0.0f;
299 if (volume > 1.0f) volume = 1.0f;
300 s_volume = volume;
301}
302
304{
305#if WAVE_GEN_HAS_SDM
306 if (s_sdm_running) {
307 return ESP_OK;
308 }
309 ESP_LOGI(TAG, "Starting SDM sine on GPIO %d at %d Hz", CONFIG_SDM_GPIO, CONFIG_SDM_FREQ_HZ);
310 ESP_RETURN_ON_ERROR(sdm_init((uint32_t)CONFIG_SDM_FREQ_HZ), TAG, "SDM init failed");
311 s_sdm_running = true;
312 return ESP_OK;
313#else
314 return ESP_ERR_NOT_SUPPORTED;
315#endif
316}
317
319{
320#if WAVE_GEN_HAS_SDM
321 if (!s_sdm_running) {
322 return ESP_OK;
323 }
324 s_sdm_running = false;
325 sdm_deinit();
326 return ESP_OK;
327#else
328 return ESP_ERR_NOT_SUPPORTED;
329#endif
330}
#define ESP_RETURN_ON_ERROR(x, log_tag, format,...)
int esp_err_t
Definition esp_err.h:21
#define ESP_OK
Definition esp_err.h:23
static const char * TAG
Definition main/main.c:31
Configuration structure for Wave Generator.
Definition wave_gen.h:22
uint32_t sample_rate
Definition wave_gen.h:27
#define I2S_BUFFER_SIZE
Definition wave_gen.c:33
void wave_gen_set_type(wave_type_t type)
Set the waveform type.
Definition wave_gen.c:291
void wave_gen_set_freq(float freq_hz)
Set the frequency of the generated wave.
Definition wave_gen.c:284
esp_err_t wave_gen_stop(void)
Stop the wave generation task.
Definition wave_gen.c:266
esp_err_t wave_gen_init(const wave_gen_config_t *config)
Initialize the Wave Generator component.
Definition wave_gen.c:213
static float s_frequency
Definition wave_gen.c:44
esp_err_t wave_gen_sdm_stop(void)
Stop the SDM sine generator.
Definition wave_gen.c:318
static wave_type_t s_type
Definition wave_gen.c:46
#define PI
Definition wave_gen.c:37
static void wave_gen_task(void *args)
Definition wave_gen.c:146
static TaskHandle_t s_wave_task_handle
Definition wave_gen.c:40
static uint32_t s_sample_rate
Definition wave_gen.c:47
static float s_phase
Definition wave_gen.c:48
static float s_volume
Definition wave_gen.c:45
esp_err_t wave_gen_start(void)
Start the wave generation task.
Definition wave_gen.c:253
#define DEFAULT_SAMPLE_RATE
Definition wave_gen.c:34
static bool s_running
Definition wave_gen.c:41
esp_err_t wave_gen_sdm_start(void)
Start the SDM sine generator (uses CONFIG_SDM_* settings).
Definition wave_gen.c:303
static i2s_chan_handle_t tx_conn_handle
Definition wave_gen.c:39
void wave_gen_set_volume(float volume)
Set the output volume/amplitude.
Definition wave_gen.c:296
wave_type_t
Waveform types for generation.
Definition wave_gen.h:12
@ WAVE_TYPE_SAWTOOTH
Definition wave_gen.h:16
@ WAVE_TYPE_TRIANGLE
Definition wave_gen.h:15
@ WAVE_TYPE_SINE
Definition wave_gen.h:13
@ WAVE_TYPE_SQUARE
Definition wave_gen.h:14