7#include "esp_adc/adc_cali.h"
8#include "esp_adc/adc_cali_scheme.h"
9#include "esp_adc/adc_continuous.h"
10#include "esp_adc/adc_filter.h"
15#include "esp_heap_caps.h"
18#include "freertos/queue.h"
19#include "freertos/semphr.h"
20#include "freertos/task.h"
26#if CONFIG_ENABLE_I2S_WAVE_GEN || CONFIG_ENABLE_SDM_WAVE_GEN
31static const char *
TAG =
"adc_fft";
33#if defined(__GNUC__) && !defined(__clang__)
34#define OPTIMIZE_O2 __attribute__((optimize("O2")))
39#define FFT_SIZE CONFIG_ADC_FFT_SIZE
40#define ADC_SPS CONFIG_ADC_FS
43#if CONFIG_FFT_BUILD_TRANSPORT
44#if CONFIG_ADC_ENABLE_WINDOW || CONFIG_OUTPUT_TOPK_ENABLE || \
45 (CONFIG_OUTPUT_CUTOFF_HZ > 0) || CONFIG_ADC_IIR_FILTER_ENABLE
46#error "Transport build forbids windowing/top-K/cutoff/IIR filtering"
51#define SAMPLE_MAX 4096
53#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
55#define SPIKE_DB_THRESHOLD 20.0f
56#define SPIKE_RATIO_THRESHOLD 8.0f
57#define SPIKE_MIN_AVG_LINEAR 1e-7f
59static float *
ch0 = NULL;
60static float *
ch1 = NULL;
61static float *
win = NULL;
73#define FRAME_MAGIC_0 0xF0
74#define FRAME_MAGIC_1 0x0D
75#define FRAME_MAGIC_2 0xF0
76#define FRAME_MAGIC_3 0x0D
77#define FRAME_VERSION 0x02
78#define FRAME_HEADER_SIZE 9u
79#define FRAME_PAYLOAD_SIZE (sizeof(frame_payload_t))
80#define FRAME_TOTAL_SIZE \
81 (FRAME_HEADER_SIZE + FRAME_PAYLOAD_SIZE + 4u)
83 "payload length must fit in uint16");
85 "payload must stay 32-bit aligned");
97#if CONFIG_STATUS_NEOPIXEL_ENABLE
99static SemaphoreHandle_t g_led_lock;
104#define CDC_TX_TASK_STACK_SIZE 4096
107#define ADC_TASK_STACK_SIZE 8192
110 dst[0] = (uint8_t)(value & 0xFFu);
111 dst[1] = (uint8_t)((value >> 8) & 0xFFu);
115 dst[0] = (uint8_t)(value & 0xFFu);
116 dst[1] = (uint8_t)((value >> 8) & 0xFFu);
117 dst[2] = (uint8_t)((value >> 16) & 0xFFu);
118 dst[3] = (uint8_t)((value >> 24) & 0xFFu);
123 write_u32_le(dst + 4, (uint32_t)((value >> 32) & 0xFFFFFFFFu));
127 return esp_crc32_le(0,
data, len);
131 const char label[4], uint64_t t_ms, uint32_t sps,
133 uint8_t *dst = out->
bytes;
144 off +=
sizeof(uint64_t);
146 off +=
sizeof(uint32_t);
148 off +=
sizeof(uint32_t);
149 memcpy(dst + off, label, 4);
158#if CONFIG_DIAG_CDC_ENABLE
160 uint64_t total_bytes_sent;
161 uint32_t window_bytes;
162 uint64_t window_start_ms;
168static diag_cdc_t s_diag_cdc;
170static void diag_cdc_reset_window(uint64_t now_ms)
172 s_diag_cdc.window_bytes = 0;
173 s_diag_cdc.window_start_ms = now_ms;
174 s_diag_cdc.wa_min = UINT32_MAX;
175 s_diag_cdc.wa_max = 0;
176 s_diag_cdc.wa_sum = 0;
177 s_diag_cdc.wa_count = 0;
180static void diag_cdc_sample_wa(uint32_t v)
182 if (v < s_diag_cdc.wa_min) s_diag_cdc.wa_min = v;
183 if (v > s_diag_cdc.wa_max) s_diag_cdc.wa_max = v;
184 s_diag_cdc.wa_sum += v;
185 s_diag_cdc.wa_count++;
188static void diag_cdc_on_queued(
size_t q)
190 s_diag_cdc.total_bytes_sent += q;
191 s_diag_cdc.window_bytes += (uint32_t)q;
194static void diag_cdc_maybe_log(uint64_t now_ms)
196 const uint64_t interval_ms = (uint64_t)CONFIG_DIAG_CDC_LOG_INTERVAL_S * 1000ULL;
197 if (now_ms < s_diag_cdc.window_start_ms + interval_ms)
return;
199 uint64_t elapsed = now_ms - s_diag_cdc.window_start_ms;
200 float bps = elapsed ? (s_diag_cdc.window_bytes * 1000.0f) / (
float)elapsed : 0.0f;
201 uint32_t wa_avg = s_diag_cdc.wa_count ? (uint32_t)(s_diag_cdc.wa_sum / s_diag_cdc.wa_count) : 0;
203 ESP_LOGI(
TAG,
"CDC diag: bytes_sec=%.1f B/s window=%llums total=%llu bytes, wa[min/avg/max]=%u/%u/%u count=%u",
204 bps, (
unsigned long long)elapsed,
205 (
unsigned long long)s_diag_cdc.total_bytes_sent,
206 (
unsigned)s_diag_cdc.wa_min == UINT32_MAX ? 0 : (
unsigned)s_diag_cdc.wa_min,
208 (
unsigned)s_diag_cdc.wa_max,
209 (
unsigned)s_diag_cdc.wa_count);
212 diag_cdc_reset_window(now_ms);
223 if (!tud_cdc_n_connected((uint8_t)port)) {
227 const int base_delay_ms = 100;
229 static uint64_t last_flush_log_ms = 0;
231 for (
int attempt = 0; attempt < max_retries; ++attempt) {
239 if (!tud_cdc_n_connected((uint8_t)port)) {
242 vTaskDelay(pdMS_TO_TICKS(base_delay_ms << attempt));
246 now_ms = esp_timer_get_time() / 1000ULL;
247 if (now_ms > last_flush_log_ms + 1000ULL) {
248 ESP_LOGW(
TAG,
"CDC flush failed after %d retries", max_retries);
249 last_flush_log_ms = now_ms;
258 const size_t max_chunk = 512;
259 const uint32_t wait_step_ms = 2;
264 const uint32_t wait_budget_ms = 10000;
267#if CONFIG_DIAG_CDC_ENABLE
269 diag_cdc_reset_window(esp_timer_get_time() / 1000ULL);
270 uint64_t last_stack_log_ms = esp_timer_get_time() / 1000ULL;
273 if (xQueueReceive(
g_frameq, &frm, portMAX_DELAY) == pdTRUE) {
278 vTaskDelay(pdMS_TO_TICKS(100));
284 uint32_t waited_ms = 0;
291 if (chunk > max_chunk) {
295 uint32_t wa = tud_cdc_n_write_available((uint8_t)
g_data_port);
296#if CONFIG_DIAG_CDC_ENABLE && CONFIG_DIAG_CDC_SAMPLE_WRITE_AVAILABLE
297 diag_cdc_sample_wa(wa);
300 if (waited_ms >= wait_budget_ms) {
301 ESP_LOGW(
TAG,
"CDC TX backpressure (%u ms), dropping frame",
302 (
unsigned)waited_ms);
305 vTaskDelay(pdMS_TO_TICKS(wait_step_ms));
306 waited_ms += wait_step_ms;
313 if (waited_ms >= wait_budget_ms) {
314 ESP_LOGW(
TAG,
"CDC TX stalled (%u ms), dropping frame",
315 (
unsigned)waited_ms);
318 vTaskDelay(pdMS_TO_TICKS(wait_step_ms));
319 waited_ms += wait_step_ms;
322#if CONFIG_DIAG_CDC_ENABLE
323 diag_cdc_on_queued(queued);
329 ESP_LOGW(
TAG,
"CDC send dropped %u bytes",
331#if CONFIG_DIAG_CDC_ENABLE
333 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
334 diag_cdc_maybe_log(now_ms);
342#if CONFIG_STATUS_NEOPIXEL_ENABLE
345 xSemaphoreTake(g_led_lock, pdMS_TO_TICKS(10)) == pdTRUE) {
346 uint8_t b = (uint8_t)CONFIG_STATUS_NEOPIXEL_BRIGHTNESS;
350 xSemaphoreGive(g_led_lock);
355#if CONFIG_DIAG_CDC_ENABLE
357 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
358 diag_cdc_maybe_log(now_ms);
360 if (now_ms > last_stack_log_ms + 5000ULL) {
363 ESP_LOGI(
TAG,
"Stack HWM: cdc_tx=%lu words, adc_fft=%lu words",
364 (
unsigned long)cdc_hwm, (
unsigned long)adc_hwm);
365 last_stack_log_ms = now_ms;
373#if CONFIG_STATUS_NEOPIXEL_ENABLE
374static void status_led_init(
void) {
375 g_led_lock = xSemaphoreCreateMutex();
377 .strip_gpio_num = CONFIG_STATUS_NEOPIXEL_GPIO,
381 .flags.invert_out =
false,
384 .clk_src = RMT_CLK_SRC_DEFAULT,
386 .flags.with_dma =
false,
392static void status_led_set(uint8_t r, uint8_t g, uint8_t b) {
396 if (g_led_lock && xSemaphoreTake(g_led_lock, pdMS_TO_TICKS(10)) != pdTRUE) {
399 uint8_t scale = (uint8_t)CONFIG_STATUS_NEOPIXEL_BRIGHTNESS;
400 uint8_t r_s = (uint8_t)((r * scale) / 255u);
401 uint8_t g_s = (uint8_t)((g * scale) / 255u);
402 uint8_t b_s = (uint8_t)((b * scale) / 255u);
406 xSemaphoreGive(g_led_lock);
412 adc_cali_curve_fitting_config_t cali_config = {
415 .bitwidth = ADC_BITWIDTH_12,
417 if (adc_cali_create_scheme_curve_fitting(&cali_config, &
cali_handle) ==
419 ESP_LOGI(
TAG,
"ADC calibration ready (curve fitting)");
422 ESP_LOGW(
TAG,
"ADC calibration not available; raw counts will be used");
430 return (
float)mv / 1000.0f;
441 return ((
float)(mv - midpoint_mv)) / 1000.0f;
444 return (
float)raw - 2048.0f;
448 for (
int i = 0; i <
FFT_SIZE; i++) {
460#if CONFIG_OUTPUT_CUTOFF_HZ > 0
461 uint32_t cutoff_hz = (uint32_t)CONFIG_OUTPUT_CUTOFF_HZ;
462 uint32_t max_bin = (cutoff_hz * (uint64_t)
FFT_SIZE) / sps;
469 for (uint32_t
k = max_bin + 1;
k <
FFT_SIZE / 2;
k++) {
471 buf[2 *
k + 1] = 0.0f;
477#if CONFIG_ADC_ATTEN_DB_0
478 return ADC_ATTEN_DB_0;
479#elif CONFIG_ADC_ATTEN_DB_2_5
480 return ADC_ATTEN_DB_2_5;
481#elif CONFIG_ADC_ATTEN_DB_6
482 return ADC_ATTEN_DB_6;
484 return ADC_ATTEN_DB_12;
493 (void)adc_cali_raw_to_voltage(
cali_handle, 2048, &midpoint_mv);
501 uint64_t last_adc_diag_ms = esp_timer_get_time() / 1000ULL;
502 uint32_t diag_reads = 0;
503 uint32_t diag_out_bytes = 0;
504 uint32_t diag_accept_ch0 = 0;
505 uint32_t diag_accept_ch1 = 0;
506 uint32_t diag_skip_unit = 0;
507 uint32_t diag_other_chan = 0;
509 adc_continuous_handle_t adc_handle = NULL;
510 adc_continuous_handle_cfg_t handle_cfg = {
511 .max_store_buf_size =
513 .conv_frame_size = 512,
515 ESP_ERROR_CHECK(adc_continuous_new_handle(&handle_cfg, &adc_handle));
517 uint32_t sample_rate =
ADC_SPS;
518#if defined(CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH)
519 if (sample_rate > CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH) {
520 ESP_LOGW(
TAG,
"ADC_SPS=%u above SOC max %u; clamping", sample_rate,
521 (
unsigned)CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH);
522 sample_rate = CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH;
525#if defined(CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW)
526 if (sample_rate < CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW) {
527 ESP_LOGW(
TAG,
"ADC_SPS=%u below SOC min %u; clamping", sample_rate,
528 (
unsigned)CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW);
529 sample_rate = CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW;
535 const uint32_t per_channel_sps = sample_rate /
CHANNELS;
537 adc_digi_pattern_config_t pattern[
CHANNELS] = {0};
538 pattern[0].atten = atten;
539 pattern[0].channel = CONFIG_ADC_CH0;
540 pattern[0].unit = ADC_UNIT_1;
541 pattern[0].bit_width = ADC_BITWIDTH_12;
542 pattern[1].atten = atten;
543 pattern[1].channel = CONFIG_ADC_CH1;
544 pattern[1].unit = ADC_UNIT_1;
545 pattern[1].bit_width = ADC_BITWIDTH_12;
547 adc_continuous_config_t dig_cfg = {
548 .sample_freq_hz = sample_rate,
549 .conv_mode = ADC_CONV_SINGLE_UNIT_1,
551 .format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,
553 .adc_pattern = pattern,
555 ESP_ERROR_CHECK(adc_continuous_config(adc_handle, &dig_cfg));
557#if CONFIG_ADC_IIR_FILTER_ENABLE
558 adc_digi_iir_filter_t coeff = ADC_DIGI_IIR_FILTER_COEFF_2;
559#if CONFIG_ADC_IIR_FILTER_COEFF_4
560 coeff = ADC_DIGI_IIR_FILTER_COEFF_4;
561#elif CONFIG_ADC_IIR_FILTER_COEFF_8
562 coeff = ADC_DIGI_IIR_FILTER_COEFF_8;
563#elif CONFIG_ADC_IIR_FILTER_COEFF_16
564 coeff = ADC_DIGI_IIR_FILTER_COEFF_16;
565#elif CONFIG_ADC_IIR_FILTER_COEFF_64
566 coeff = ADC_DIGI_IIR_FILTER_COEFF_64;
568 adc_continuous_iir_filter_config_t filter_cfg = {
572 adc_iir_filter_handle_t iir_handle = NULL;
574 adc_new_continuous_iir_filter(adc_handle, &filter_cfg, &iir_handle));
575 ESP_ERROR_CHECK(adc_continuous_iir_filter_enable(iir_handle));
576 ESP_LOGI(
TAG,
"ADC IIR Filter enabled");
579 ESP_ERROR_CHECK(adc_continuous_start(adc_handle));
582 "Sampling ADC1 CH%d (GPIO%d) and CH%d (GPIO%d) at %u sps/ch, FFT %d",
583 CONFIG_ADC_CH0, CONFIG_ADC_CH0 + 1, CONFIG_ADC_CH1,
584 CONFIG_ADC_CH1 + 1, (
unsigned)per_channel_sps,
FFT_SIZE);
587 const size_t read_len = 2048 *
sizeof(adc_digi_output_data_t);
588 uint8_t *raw = heap_caps_aligned_alloc(16, read_len,
589 MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
591 raw = heap_caps_aligned_alloc(16, read_len, MALLOC_CAP_DMA);
594 ESP_LOGE(
TAG,
"Failed to reserve DMA buffer (%zu bytes)", read_len);
601#if CONFIG_STATUS_NEOPIXEL_ENABLE
602 status_led_set(0, 0, 255);
609 uint32_t out_len = 0;
610 esp_err_t ret = adc_continuous_read(adc_handle, raw, read_len, &out_len,
612 if (ret == ESP_ERR_TIMEOUT) {
616 ESP_LOGW(
TAG,
"adc_continuous_read returned %s", esp_err_to_name(ret));
617 vTaskDelay(pdMS_TO_TICKS(1));
623 diag_out_bytes += out_len;
625 for (uint32_t i = 0; i +
sizeof(adc_digi_output_data_t) <= out_len;
626 i +=
sizeof(adc_digi_output_data_t)) {
627 adc_digi_output_data_t *p = (adc_digi_output_data_t *)&raw[i];
628 if (p->type2.unit != ADC_UNIT_1) {
632 if (p->type2.channel == CONFIG_ADC_CH0 && count0 <
FFT_SIZE) {
637 }
else if (p->type2.channel == CONFIG_ADC_CH1 && count1 <
FFT_SIZE) {
648 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
649 if (now_ms > last_adc_diag_ms + 2000ULL) {
651 "ADC diag (2s): reads=%u out=%uB accept[ch0/ch1]=%u/%u skip_unit=%u other_chan=%u",
652 (
unsigned)diag_reads, (
unsigned)diag_out_bytes,
653 (
unsigned)diag_accept_ch0, (
unsigned)diag_accept_ch1,
654 (
unsigned)diag_skip_unit, (
unsigned)diag_other_chan);
661 last_adc_diag_ms = now_ms;
666 float mean0 = (float)(sum0 /
FFT_SIZE);
667 float mean1 = (float)(sum1 /
FFT_SIZE);
668 for (
int i = 0; i <
FFT_SIZE; i++) {
673#if CONFIG_ADC_ENABLE_WINDOW
683 uint64_t now_ms = esp_timer_get_time() / 1000ULL;
686 if (xQueueSend(
g_frameq, &frame, portMAX_DELAY) != pdTRUE) {
687 ESP_LOGW(
TAG,
"Frame queue send failed (ch0)");
691 if (xQueueSend(
g_frameq, &frame, portMAX_DELAY) != pdTRUE) {
692 ESP_LOGW(
TAG,
"Frame queue send failed (ch1)");
697#if CONFIG_ENABLE_I2S_WAVE_GEN
698static void sweep_task(
void *arg) {
699 const float f_start = 33.0f;
700 const float f_end = 3333.0f;
701 const float duration_sec = 9.0f;
702 const int update_ms = 50;
705 const float step = (f_end - f_start) / (duration_sec * (1000.0f / update_ms));
707 float freq = f_start;
710 vTaskDelay(pdMS_TO_TICKS(update_ms));
721#if CONFIG_STATUS_NEOPIXEL_ENABLE
723 status_led_set(255, 0, 0);
726 .device_descriptor = NULL,
727 .string_descriptor = NULL,
728 .external_phy =
false,
732#if CONFIG_TINYUSB_CDC_COUNT > 1
741 .rx_unread_buf_sz = CONFIG_TINYUSB_CDC_RX_BUFSIZE,
743 .callback_rx_wanted_char = NULL,
744 .callback_line_state_changed = NULL,
745 .callback_line_coding_changed = NULL,
750#if CONFIG_ENABLE_I2S_WAVE_GEN
758 .sample_rate = 44100,
767#if CONFIG_ENABLE_SDM_WAVE_GEN
771 size_t fft_mem_sz =
FFT_SIZE *
sizeof(float);
772 ch0 = heap_caps_aligned_alloc(16, fft_mem_sz, MALLOC_CAP_SPIRAM);
773 ch1 = heap_caps_aligned_alloc(16, fft_mem_sz, MALLOC_CAP_SPIRAM);
774 win = heap_caps_aligned_alloc(16, fft_mem_sz, MALLOC_CAP_SPIRAM);
777 ESP_LOGE(
TAG,
"Failed to allocate FFT buffers");
783#if CONFIG_ADC_ENABLE_WINDOW
784#if CONFIG_ADC_WINDOW_HANN
786#elif CONFIG_ADC_WINDOW_BLACKMAN
788#elif CONFIG_ADC_WINDOW_BLACKMAN_HARRIS
790#elif CONFIG_ADC_WINDOW_BLACKMAN_NUTTALL
792#elif CONFIG_ADC_WINDOW_NUTTALL
794#elif CONFIG_ADC_WINDOW_FLAT_TOP
797 for (
int i = 0; i <
FFT_SIZE; i++) {
804#if CONFIG_ENABLE_I2S_WAVE_GEN
805 xTaskCreate(sweep_task,
"sweep", 2048, NULL, 5, NULL);
813 const BaseType_t tusb_core = 0;
814#if CONFIG_FREERTOS_UNICORE
815 const BaseType_t adc_core = 0;
817 const BaseType_t adc_core = 1;
820 xTaskCreatePinnedToCore(
832 xTaskCreatePinnedToCore(
841 xTaskCreatePinnedToCore(
#define dsps_bit_rev4r_fc32
#define dsps_cplx2real_fc32
esp_err_t dsps_fft4r_init_fc32(float *fft_table_buff, int max_fft_size)
init fft tables
void dsps_wind_blackman_f32(float *window, int len)
Blackman window.
void dsps_wind_blackman_harris_f32(float *window, int len)
Blackman-Harris window.
void dsps_wind_blackman_nuttall_f32(float *window, int len)
Blackman-Nuttall window.
void dsps_wind_flat_top_f32(float *window, int len)
Flat-Top window.
void dsps_wind_hann_f32(float *window, int len)
Hann window.
void dsps_wind_nuttall_f32(float *window, int len)
Nuttall window.
esp_err_t led_strip_refresh(led_strip_handle_t strip)
Refresh memory colors to LEDs.
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
Set RGB for a specific pixel.
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
Create LED strip based on RMT TX channel.
struct led_strip_t * led_strip_handle_t
LED strip handle.
static void apply_complex_cutoff(float *buf, uint32_t sps)
static QueueHandle_t g_frameq
static void write_u32_le(uint8_t *dst, uint32_t value)
#define ADC_TASK_STACK_SIZE
#define FRAME_PAYLOAD_SIZE
static float sample_to_centered(uint16_t raw, bool calibrated, int midpoint_mv)
#define CDC_TX_TASK_STACK_SIZE
#define FRAME_HEADER_SIZE
static void write_u16_le(uint8_t *dst, uint16_t value)
static void build_frame(frame_wire_t *out, const float *data, const char label[4], uint64_t t_ms, uint32_t sps, uint16_t seq)
static TaskHandle_t g_cdc_task_handle
static void apply_window(float *buf)
static adc_cali_handle_t cali_handle
static void adc_fft_task(void *arg)
bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten)
static tinyusb_cdcacm_itf_t g_data_port
static void perform_fft(float *buf)
struct __attribute__((packed))
static void cdc_tx_task(void *arg)
static bool safe_cdcacm_write_flush(tinyusb_cdcacm_itf_t port, int max_retries)
static adc_atten_t cfg_atten(void)
static float sample_to_volts(uint16_t raw, bool calibrated)
static uint32_t frame_crc32(const uint8_t *data, size_t len)
#define LED_STRIP_RMT_RES_HZ
static TaskHandle_t g_adc_task_handle
static void write_u64_le(uint8_t *dst, uint64_t value)
uint8_t bytes[(9u+(sizeof(frame_payload_t))+4u)]
LED Strip RMT specific configuration.
Configuration structure for CDC-ACM.
Configuration structure of the TinyUSB core.
Configuration structure for Wave Generator.
static float data[128 *2]
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
This is an all-in-one helper function, including:
size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, const uint8_t *in_buf, size_t in_size)
Write data to write buffer from a byte array.
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf)
Check if the ACM initialized.
esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg)
Initialize CDC ACM. Initialization will be finished with the tud_cdc_line_state_cb callback.
tinyusb_cdcacm_itf_t
CDC ports available to setup.
esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks)
Send all data from a write buffer. Use tinyusb_cdcacm_write_queue to add data to the buffer.
void wave_gen_set_type(wave_type_t type)
Set the waveform type.
void wave_gen_set_freq(float freq_hz)
Set the frequency of the generated wave.
esp_err_t wave_gen_init(const wave_gen_config_t *config)
Initialize the Wave Generator component.
esp_err_t wave_gen_start(void)
Start the wave generation task.
esp_err_t wave_gen_sdm_start(void)
Start the SDM sine generator (uses CONFIG_SDM_* settings).
void wave_gen_set_volume(float volume)
Set the output volume/amplitude.