From 6c3f236feb85d826d9cf432524f4426e5963f715 Mon Sep 17 00:00:00 2001 From: Karl Osterseher Date: Mon, 19 Sep 2022 22:46:38 +0200 Subject: [PATCH] - increase buffer size written to DMA buffer on initial sync o configurable through CHNK_CTRL_CNT - move time sync messaging to timer callback so it will keep going to send and receive them as long as we are connected to snapserver - change task priorities and core IDs of tasks Signed-off-by: Karl Osterseher --- components/custom_driver/i2s.c | 1959 +++++++++------------ components/custom_driver/include/i2s.h | 642 +++---- components/lightsnapcast/include/player.h | 4 +- components/lightsnapcast/player.c | 102 +- main/main.c | 1209 ++----------- 5 files changed, 1467 insertions(+), 2449 deletions(-) diff --git a/components/custom_driver/i2s.c b/components/custom_driver/i2s.c index 30a1b2a..faa036e 100644 --- a/components/custom_driver/i2s.c +++ b/components/custom_driver/i2s.c @@ -42,21 +42,19 @@ static const char *I2S_TAG = "c_I2S"; -#define I2S_CHECK(a, str, ret) \ - if (!(a)) \ - { \ - ESP_LOGE (I2S_TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \ - return (ret); \ - } +#define I2S_CHECK(a, str, ret) \ + if (!(a)) { \ + ESP_LOGE(I2S_TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret); \ + } -#define I2S_ENTER_CRITICAL_ISR() \ - portENTER_CRITICAL_ISR (&i2s_spinlock[i2s_num]) -#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR (&i2s_spinlock[i2s_num]) -#define I2S_ENTER_CRITICAL() portENTER_CRITICAL (&i2s_spinlock[i2s_num]) -#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL (&i2s_spinlock[i2s_num]) -#define I2S_FULL_DUPLEX_SLAVE_MODE_MASK \ +#define I2S_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) +#define I2S_FULL_DUPLEX_SLAVE_MODE_MASK \ (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_SLAVE) -#define I2S_FULL_DUPLEX_MASTER_MODE_MASK \ +#define I2S_FULL_DUPLEX_MASTER_MODE_MASK \ (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER) // TODO: Refactor to put this logic into LL @@ -68,8 +66,7 @@ static const char *I2S_TAG = "c_I2S"; * @brief DMA buffer object * */ -typedef struct -{ +typedef struct { char **buf; int buf_size; int rw_pos; @@ -83,8 +80,7 @@ typedef struct * @brief I2S object instance * */ -typedef struct -{ +typedef struct { i2s_port_t i2s_num; /*!< I2S port number*/ int queue_size; /*!< I2S event queue size*/ QueueHandle_t i2s_queue; /*!< I2S queue handler*/ @@ -108,120 +104,98 @@ typedef struct i2s_hal_context_t hal; /*!< I2S hal context*/ } i2s_obj_t; -static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = { 0 }; +static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0}; static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX]; -static i2s_dma_t *i2s_create_dma_queue (i2s_port_t i2s_num, int dma_buf_count, - int dma_buf_len); -static esp_err_t i2s_destroy_dma_queue (i2s_port_t i2s_num, i2s_dma_t *dma); +static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, + int dma_buf_len); +static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma); -static inline void -gpio_matrix_out_check (int gpio, uint32_t signal_idx, bool out_inv, - bool oen_inv) -{ +static inline void gpio_matrix_out_check(int gpio, uint32_t signal_idx, + bool out_inv, bool oen_inv) { // if pin = -1, do not need to configure - if (gpio != -1) - { - gpio_hal_iomux_func_sel (GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); - gpio_set_direction (gpio, GPIO_MODE_OUTPUT); - esp_rom_gpio_connect_out_signal (gpio, signal_idx, out_inv, oen_inv); - } + if (gpio != -1) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); + gpio_set_direction(gpio, GPIO_MODE_OUTPUT); + esp_rom_gpio_connect_out_signal(gpio, signal_idx, out_inv, oen_inv); + } } -static inline void -gpio_matrix_in_check (int gpio, uint32_t signal_idx, bool inv) -{ - if (gpio != -1) - { - gpio_hal_iomux_func_sel (GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); - // Set direction, for some GPIOs, the input function are not enabled as - // default. - gpio_set_direction (gpio, GPIO_MODE_INPUT); - esp_rom_gpio_connect_in_signal (gpio, signal_idx, inv); - } +static inline void gpio_matrix_in_check(int gpio, uint32_t signal_idx, + bool inv) { + if (gpio != -1) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); + // Set direction, for some GPIOs, the input function are not enabled as + // default. + gpio_set_direction(gpio, GPIO_MODE_INPUT); + esp_rom_gpio_connect_in_signal(gpio, signal_idx, inv); + } } -esp_err_t -i2s_custom_clear_intr_status (i2s_port_t i2s_num, uint32_t clr_mask) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - i2s_hal_clear_intr_status (&(p_i2s_obj[i2s_num]->hal), clr_mask); +esp_err_t i2s_custom_clear_intr_status(i2s_port_t i2s_num, uint32_t clr_mask) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), clr_mask); return ESP_OK; } -esp_err_t -i2s_custom_enable_rx_intr (i2s_port_t i2s_num) -{ - - I2S_ENTER_CRITICAL (); - i2s_hal_enable_rx_intr (&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL (); +esp_err_t i2s_custom_enable_rx_intr(i2s_port_t i2s_num) { + I2S_ENTER_CRITICAL(); + i2s_hal_enable_rx_intr(&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL(); return ESP_OK; } -esp_err_t -i2s_custom_disable_rx_intr (i2s_port_t i2s_num) -{ - I2S_ENTER_CRITICAL (); - i2s_hal_disable_rx_intr (&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL (); +esp_err_t i2s_custom_disable_rx_intr(i2s_port_t i2s_num) { + I2S_ENTER_CRITICAL(); + i2s_hal_disable_rx_intr(&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL(); return ESP_OK; } -esp_err_t -i2s_custom_disable_tx_intr (i2s_port_t i2s_num) -{ - I2S_ENTER_CRITICAL (); - i2s_hal_disable_tx_intr (&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL (); +esp_err_t i2s_custom_disable_tx_intr(i2s_port_t i2s_num) { + I2S_ENTER_CRITICAL(); + i2s_hal_disable_tx_intr(&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL(); return ESP_OK; } -esp_err_t -i2s_custom_enable_tx_intr (i2s_port_t i2s_num) -{ - I2S_ENTER_CRITICAL (); - i2s_hal_enable_tx_intr (&(p_i2s_obj[i2s_num]->hal)); - I2S_EXIT_CRITICAL (); +esp_err_t i2s_custom_enable_tx_intr(i2s_port_t i2s_num) { + I2S_ENTER_CRITICAL(); + i2s_hal_enable_tx_intr(&(p_i2s_obj[i2s_num]->hal)); + I2S_EXIT_CRITICAL(); return ESP_OK; } -float -i2s_custom_get_clk (i2s_port_t i2s_num) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); +float i2s_custom_get_clk(i2s_port_t i2s_num) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); return p_i2s_obj[i2s_num]->real_rate; } -static esp_err_t -i2s_isr_register (i2s_port_t i2s_num, int intr_alloc_flags, - void (*fn) (void *), void *arg, i2s_isr_handle_t *handle) -{ - return esp_intr_alloc (i2s_periph_signal[i2s_num].irq, intr_alloc_flags, fn, - arg, handle); +static esp_err_t i2s_isr_register(i2s_port_t i2s_num, int intr_alloc_flags, + void (*fn)(void *), void *arg, + i2s_isr_handle_t *handle) { + return esp_intr_alloc(i2s_periph_signal[i2s_num].irq, intr_alloc_flags, fn, + arg, handle); } -static float -i2s_apll_get_fi2s (int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) -{ - int f_xtal = (int)rtc_clk_xtal_freq_get () * 1000000; +static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, + int sdm2, int odir) { + int f_xtal = (int)rtc_clk_xtal_freq_get() * 1000000; #if CONFIG_IDF_TARGET_ESP32 /* ESP32 rev0 silicon issue for APLL range/accuracy, please see ESP32 ECO * document for more information on this */ - if (esp_efuse_get_chip_ver () == 0) - { - sdm0 = 0; - sdm1 = 0; - } + if (esp_efuse_get_chip_ver() == 0) { + sdm0 = 0; + sdm1 = 0; + } #endif float fout = f_xtal * (sdm2 + sdm1 / 256.0f + sdm0 / 65536.0f + 4); - if (fout < SOC_I2S_APLL_MIN_FREQ || fout > SOC_I2S_APLL_MAX_FREQ) - { - return SOC_I2S_APLL_MAX_FREQ; - } - float fpll = fout / (2 * (odir + 2)); //== fi2s (N=1, b=0, a=1) + if (fout < SOC_I2S_APLL_MIN_FREQ || fout > SOC_I2S_APLL_MAX_FREQ) { + return SOC_I2S_APLL_MAX_FREQ; + } + float fpll = fout / (2 * (odir + 2)); //== fi2s (N=1, b=0, a=1) return fpll / 2; } @@ -262,17 +236,14 @@ i2s_apll_get_fi2s (int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) * @return ESP_ERR_INVALID_ARG or ESP_OK */ -esp_err_t -i2s_apll_calculate_fi2s (int rate, int bits_per_sample, int *sdm0, int *sdm1, - int *sdm2, int *odir) -{ +esp_err_t i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm0, + int *sdm1, int *sdm2, int *odir) { int _odir, _sdm0, _sdm1, _sdm2; float avg; float min_rate, max_rate, min_diff; - if (rate / bits_per_sample / 2 / 8 < SOC_I2S_APLL_MIN_RATE) - { - return ESP_ERR_INVALID_ARG; - } + if (rate / bits_per_sample / 2 / 8 < SOC_I2S_APLL_MIN_RATE) { + return ESP_ERR_INVALID_ARG; + } *sdm0 = 0; *sdm1 = 0; @@ -280,367 +251,325 @@ i2s_apll_calculate_fi2s (int rate, int bits_per_sample, int *sdm0, int *sdm1, *odir = 0; min_diff = SOC_I2S_APLL_MAX_FREQ; - for (_sdm2 = 4; _sdm2 < 9; _sdm2++) - { - max_rate = i2s_apll_get_fi2s (bits_per_sample, 255, 255, _sdm2, 0); - min_rate = i2s_apll_get_fi2s (bits_per_sample, 0, 0, _sdm2, 31); - avg = (max_rate + min_rate) / 2; - if (abs (avg - rate) < min_diff) - { - min_diff = abs (avg - rate); - *sdm2 = _sdm2; - } + for (_sdm2 = 4; _sdm2 < 9; _sdm2++) { + max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, 0); + min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, 31); + avg = (max_rate + min_rate) / 2; + if (abs(avg - rate) < min_diff) { + min_diff = abs(avg - rate); + *sdm2 = _sdm2; } + } min_diff = SOC_I2S_APLL_MAX_FREQ; - for (_odir = 0; _odir < 32; _odir++) - { - max_rate = i2s_apll_get_fi2s (bits_per_sample, 255, 255, *sdm2, _odir); - min_rate = i2s_apll_get_fi2s (bits_per_sample, 0, 0, *sdm2, _odir); - avg = (max_rate + min_rate) / 2; - if (abs (avg - rate) < min_diff) - { - min_diff = abs (avg - rate); - *odir = _odir; - } + for (_odir = 0; _odir < 32; _odir++) { + max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, *sdm2, _odir); + min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, *sdm2, _odir); + avg = (max_rate + min_rate) / 2; + if (abs(avg - rate) < min_diff) { + min_diff = abs(avg - rate); + *odir = _odir; } + } min_diff = SOC_I2S_APLL_MAX_FREQ; - for (_sdm2 = 4; _sdm2 < 9; _sdm2++) - { - max_rate = i2s_apll_get_fi2s (bits_per_sample, 255, 255, _sdm2, *odir); - min_rate = i2s_apll_get_fi2s (bits_per_sample, 0, 0, _sdm2, *odir); - avg = (max_rate + min_rate) / 2; - if (abs (avg - rate) < min_diff) - { - min_diff = abs (avg - rate); - *sdm2 = _sdm2; - } + for (_sdm2 = 4; _sdm2 < 9; _sdm2++) { + max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, 255, _sdm2, *odir); + min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, 0, _sdm2, *odir); + avg = (max_rate + min_rate) / 2; + if (abs(avg - rate) < min_diff) { + min_diff = abs(avg - rate); + *sdm2 = _sdm2; } + } min_diff = SOC_I2S_APLL_MAX_FREQ; - for (_sdm1 = 0; _sdm1 < 256; _sdm1++) - { - max_rate = i2s_apll_get_fi2s (bits_per_sample, 255, _sdm1, *sdm2, *odir); - min_rate = i2s_apll_get_fi2s (bits_per_sample, 0, _sdm1, *sdm2, *odir); - avg = (max_rate + min_rate) / 2; - if (abs (avg - rate) < min_diff) - { - min_diff = abs (avg - rate); - *sdm1 = _sdm1; - } + for (_sdm1 = 0; _sdm1 < 256; _sdm1++) { + max_rate = i2s_apll_get_fi2s(bits_per_sample, 255, _sdm1, *sdm2, *odir); + min_rate = i2s_apll_get_fi2s(bits_per_sample, 0, _sdm1, *sdm2, *odir); + avg = (max_rate + min_rate) / 2; + if (abs(avg - rate) < min_diff) { + min_diff = abs(avg - rate); + *sdm1 = _sdm1; } + } min_diff = SOC_I2S_APLL_MAX_FREQ; - for (_sdm0 = 0; _sdm0 < 256; _sdm0++) - { - avg = i2s_apll_get_fi2s (bits_per_sample, _sdm0, *sdm1, *sdm2, *odir); - if (abs (avg - rate) < min_diff) - { - min_diff = abs (avg - rate); - *sdm0 = _sdm0; - } + for (_sdm0 = 0; _sdm0 < 256; _sdm0++) { + avg = i2s_apll_get_fi2s(bits_per_sample, _sdm0, *sdm1, *sdm2, *odir); + if (abs(avg - rate) < min_diff) { + min_diff = abs(avg - rate); + *sdm0 = _sdm0; } + } return ESP_OK; } -esp_err_t -i2s_custom_init_dma_tx_queues (i2s_port_t i2s_num, uint8_t *data, size_t size, - size_t *written) -{ +esp_err_t i2s_custom_init_dma_tx_queues(i2s_port_t i2s_num, uint8_t *data, + size_t size, size_t *written, + uint32_t *currentDescriptor, + uint32_t *currentDescriptorOffset) { size_t tmpSize = size; - int i; + uint32_t i; - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK ((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); - i2s_custom_stop (i2s_num); + i2s_custom_stop(i2s_num); - xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); - i2s_hal_set_out_link_addr (&(p_i2s_obj[i2s_num]->hal), - (uint32_t)p_i2s_obj[i2s_num]->tx->desc[0]); + i2s_hal_set_out_link_addr(&(p_i2s_obj[i2s_num]->hal), + (uint32_t)p_i2s_obj[i2s_num]->tx->desc[0]); p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; p_i2s_obj[i2s_num]->tx->rw_pos = 0; // fill DMA buffers - if ((data != NULL) && (written != NULL) && (size != 0)) - { - size_t offset = 0; - size_t maxDmaBufBytes = p_i2s_obj[i2s_num]->dma_buf_len - * p_i2s_obj[i2s_num]->bytes_per_sample - * p_i2s_obj[i2s_num]->channel_num; + if ((data != NULL) && (written != NULL) && (size != 0)) { + size_t offset = *currentDescriptorOffset; + size_t maxDmaBufBytes = p_i2s_obj[i2s_num]->dma_buf_len * + p_i2s_obj[i2s_num]->bytes_per_sample * + p_i2s_obj[i2s_num]->channel_num; - for (i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) - { - char *buf = (char *)p_i2s_obj[i2s_num]->tx->desc[i]->buf; + for (i = *currentDescriptor; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { + char *buf = (char *)p_i2s_obj[i2s_num]->tx->desc[i]->buf; - if (tmpSize > maxDmaBufBytes) - { + if (tmpSize > maxDmaBufBytes) { + memcpy(buf, &data[offset], maxDmaBufBytes); + offset += maxDmaBufBytes; - memcpy (buf, &data[offset], maxDmaBufBytes); - offset += maxDmaBufBytes; + // ESP_LOGW(I2S_TAG, "wrote %d", + // maxDmaBufBytes); - // ESP_LOGW(I2S_TAG, "wrote %d", - // maxDmaBufBytes); + tmpSize -= maxDmaBufBytes; + } else { + memcpy(buf, &data[offset], tmpSize); + offset += tmpSize; - tmpSize -= maxDmaBufBytes; - } - else - { - memcpy (buf, &data[offset], tmpSize); - offset += tmpSize; + // ESP_LOGW(I2S_TAG, "wrote %d", + // tmpSize); - // ESP_LOGW(I2S_TAG, "wrote %d", - // tmpSize); + tmpSize = 0; + } - tmpSize = 0; - } - - if (tmpSize == 0) - { - break; - } + if (tmpSize == 0) { + if (currentDescriptor) { + *currentDescriptor = i; } - *written = offset; + if (currentDescriptorOffset) { + if (offset == size) { + *currentDescriptorOffset = 0; + } else { + *currentDescriptorOffset = offset; + } + } + + break; + } } - // empty queue - xQueueReset (p_i2s_obj[i2s_num]->tx->queue); + *written = offset; + } - xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); + // empty queue + xQueueReset(p_i2s_obj[i2s_num]->tx->queue); + + xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); return ESP_OK; } -esp_err_t -i2s_custom_set_clk (i2s_port_t i2s_num, uint32_t rate, - i2s_bits_per_sample_t bits, i2s_channel_t ch) -{ - int factor - = (256 % bits) ? 384 : 256; // According to hardware codec - // requirement(supported 256fs or 384fs) +esp_err_t i2s_custom_set_clk(i2s_port_t i2s_num, uint32_t rate, + i2s_bits_per_sample_t bits, i2s_channel_t ch) { + int factor = + (256 % bits) ? 384 : 256; // According to hardware codec + // requirement(supported 256fs or 384fs) int clkmInteger, clkmDecimals, bck = 0; double denom = (double)1 / 64; int channel = 2; i2s_dma_t *save_tx = NULL, *save_rx = NULL; - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (bits % 8 != 0 || bits > I2S_BITS_PER_SAMPLE_32BIT - || bits < I2S_BITS_PER_SAMPLE_16BIT) - { - ESP_LOGE (I2S_TAG, "Invalid bits per sample"); - return ESP_ERR_INVALID_ARG; - } + if (bits % 8 != 0 || bits > I2S_BITS_PER_SAMPLE_32BIT || + bits < I2S_BITS_PER_SAMPLE_16BIT) { + ESP_LOGE(I2S_TAG, "Invalid bits per sample"); + return ESP_ERR_INVALID_ARG; + } - if (p_i2s_obj[i2s_num] == NULL) - { - ESP_LOGE (I2S_TAG, "Not initialized yet"); - return ESP_ERR_INVALID_ARG; - } + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Not initialized yet"); + return ESP_ERR_INVALID_ARG; + } p_i2s_obj[i2s_num]->sample_rate = rate; double clkmdiv = (double)I2S_BASE_CLK / (rate * factor); - if (clkmdiv > 256) - { - ESP_LOGE (I2S_TAG, "clkmdiv is too large\r\n"); - return ESP_ERR_INVALID_ARG; - } + if (clkmdiv > 256) { + ESP_LOGE(I2S_TAG, "clkmdiv is too large\r\n"); + return ESP_ERR_INVALID_ARG; + } // wait all on-going writing finish - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) - { - xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, - (portTickType)portMAX_DELAY); + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) { + xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + } + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) { + xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + } + + i2s_custom_stop(i2s_num); + i2s_hal_set_rx_mode(&(p_i2s_obj[i2s_num]->hal), ch, bits); + i2s_hal_set_tx_mode(&(p_i2s_obj[i2s_num]->hal), ch, bits); + + if (p_i2s_obj[i2s_num]->channel_num != (int)ch) { + p_i2s_obj[i2s_num]->channel_num = (ch == 2) ? 2 : 1; + } + + if ((int)bits != p_i2s_obj[i2s_num]->bits_per_sample) { + p_i2s_obj[i2s_num]->bits_per_sample = bits; + + // Round bytes_per_sample up to next multiple of 16 bits + int halfwords_per_sample = (bits + 15) / 16; + p_i2s_obj[i2s_num]->bytes_per_sample = halfwords_per_sample * 2; + + // Because limited of DMA buffer is 4092 bytes + if (p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * + p_i2s_obj[i2s_num]->channel_num > + 4092) { + p_i2s_obj[i2s_num]->dma_buf_len = 4092 / + p_i2s_obj[i2s_num]->bytes_per_sample / + p_i2s_obj[i2s_num]->channel_num; } - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) - { - xSemaphoreTake (p_i2s_obj[i2s_num]->rx->mux, - (portTickType)portMAX_DELAY); + // Re-create TX DMA buffer + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + save_tx = p_i2s_obj[i2s_num]->tx; + + p_i2s_obj[i2s_num]->tx = + i2s_create_dma_queue(i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, + p_i2s_obj[i2s_num]->dma_buf_len); + if (p_i2s_obj[i2s_num]->tx == NULL) { + ESP_LOGE(I2S_TAG, "Failed to create tx dma buffer"); + i2s_custom_driver_uninstall(i2s_num); + return ESP_ERR_NO_MEM; + } + i2s_hal_set_out_link_addr(&(p_i2s_obj[i2s_num]->hal), + (uint32_t)p_i2s_obj[i2s_num]->tx->desc[0]); + + // destroy old tx dma if exist + if (save_tx) { + i2s_destroy_dma_queue(i2s_num, save_tx); + } } + // Re-create RX DMA buffer + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + save_rx = p_i2s_obj[i2s_num]->rx; - i2s_custom_stop (i2s_num); - i2s_hal_set_rx_mode (&(p_i2s_obj[i2s_num]->hal), ch, bits); - i2s_hal_set_tx_mode (&(p_i2s_obj[i2s_num]->hal), ch, bits); - - if (p_i2s_obj[i2s_num]->channel_num != (int)ch) - { - p_i2s_obj[i2s_num]->channel_num = (ch == 2) ? 2 : 1; - } - - if ((int)bits != p_i2s_obj[i2s_num]->bits_per_sample) - { - p_i2s_obj[i2s_num]->bits_per_sample = bits; - - // Round bytes_per_sample up to next multiple of 16 bits - int halfwords_per_sample = (bits + 15) / 16; - p_i2s_obj[i2s_num]->bytes_per_sample = halfwords_per_sample * 2; - - // Because limited of DMA buffer is 4092 bytes - if (p_i2s_obj[i2s_num]->dma_buf_len - * p_i2s_obj[i2s_num]->bytes_per_sample - * p_i2s_obj[i2s_num]->channel_num - > 4092) - { - p_i2s_obj[i2s_num]->dma_buf_len - = 4092 / p_i2s_obj[i2s_num]->bytes_per_sample - / p_i2s_obj[i2s_num]->channel_num; - } - // Re-create TX DMA buffer - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) - { - - save_tx = p_i2s_obj[i2s_num]->tx; - - p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue ( - i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, - p_i2s_obj[i2s_num]->dma_buf_len); - if (p_i2s_obj[i2s_num]->tx == NULL) - { - ESP_LOGE (I2S_TAG, "Failed to create tx dma buffer"); - i2s_custom_driver_uninstall (i2s_num); - return ESP_ERR_NO_MEM; - } - i2s_hal_set_out_link_addr ( - &(p_i2s_obj[i2s_num]->hal), - (uint32_t)p_i2s_obj[i2s_num]->tx->desc[0]); - - // destroy old tx dma if exist - if (save_tx) - { - i2s_destroy_dma_queue (i2s_num, save_tx); - } - } - // Re-create RX DMA buffer - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) - { - - save_rx = p_i2s_obj[i2s_num]->rx; - - p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue ( - i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, - p_i2s_obj[i2s_num]->dma_buf_len); - if (p_i2s_obj[i2s_num]->rx == NULL) - { - ESP_LOGE (I2S_TAG, "Failed to create rx dma buffer"); - i2s_custom_driver_uninstall (i2s_num); - return ESP_ERR_NO_MEM; - } - i2s_hal_set_in_link (&(p_i2s_obj[i2s_num]->hal), - p_i2s_obj[i2s_num]->dma_buf_len - * p_i2s_obj[i2s_num]->channel_num - * p_i2s_obj[i2s_num]->bytes_per_sample, - (uint32_t)p_i2s_obj[i2s_num]->rx->desc[0]); - // destroy old rx dma if exist - if (save_rx) - { - i2s_destroy_dma_queue (i2s_num, save_rx); - } - } + p_i2s_obj[i2s_num]->rx = + i2s_create_dma_queue(i2s_num, p_i2s_obj[i2s_num]->dma_buf_count, + p_i2s_obj[i2s_num]->dma_buf_len); + if (p_i2s_obj[i2s_num]->rx == NULL) { + ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer"); + i2s_custom_driver_uninstall(i2s_num); + return ESP_ERR_NO_MEM; + } + i2s_hal_set_in_link(&(p_i2s_obj[i2s_num]->hal), + p_i2s_obj[i2s_num]->dma_buf_len * + p_i2s_obj[i2s_num]->channel_num * + p_i2s_obj[i2s_num]->bytes_per_sample, + (uint32_t)p_i2s_obj[i2s_num]->rx->desc[0]); + // destroy old rx dma if exist + if (save_rx) { + i2s_destroy_dma_queue(i2s_num, save_rx); + } } + } double mclk; int sdm0, sdm1, sdm2, odir, m_scale = 8; int fi2s_clk = rate * channel * bits * m_scale; #if SOC_I2S_SUPPORTS_PDM - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_PDM) - { - uint32_t b_clk = 0; - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) - { - uint32_t fp, fs; - i2s_hal_get_tx_pdm (&(p_i2s_obj[i2s_num]->hal), &fp, &fs); - // Recommended set `fp = 960, fs = sample_rate / 100` - fs = rate / 100; - i2s_hal_tx_pdm_cfg (&(p_i2s_obj[i2s_num]->hal), fp, fs); - b_clk = rate * I2S_PDM_BCK_FACTOR * fp / fs; - } - else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) - { - uint32_t dsr; - i2s_hal_get_rx_pdm (&(p_i2s_obj[i2s_num]->hal), &dsr); - b_clk = rate * I2S_PDM_BCK_FACTOR * (dsr ? 2 : 1); - } - fi2s_clk = b_clk * m_scale; - int factor2 = 5; - mclk = b_clk * factor2; - clkmdiv = ((double)I2S_BASE_CLK) / mclk; - clkmInteger = clkmdiv; - clkmDecimals = (clkmdiv - clkmInteger) / denom; - bck = mclk / b_clk; + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_PDM) { + uint32_t b_clk = 0; + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + uint32_t fp, fs; + i2s_hal_get_tx_pdm(&(p_i2s_obj[i2s_num]->hal), &fp, &fs); + // Recommended set `fp = 960, fs = sample_rate / 100` + fs = rate / 100; + i2s_hal_tx_pdm_cfg(&(p_i2s_obj[i2s_num]->hal), fp, fs); + b_clk = rate * I2S_PDM_BCK_FACTOR * fp / fs; + } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + uint32_t dsr; + i2s_hal_get_rx_pdm(&(p_i2s_obj[i2s_num]->hal), &dsr); + b_clk = rate * I2S_PDM_BCK_FACTOR * (dsr ? 2 : 1); } - else + fi2s_clk = b_clk * m_scale; + int factor2 = 5; + mclk = b_clk * factor2; + clkmdiv = ((double)I2S_BASE_CLK) / mclk; + clkmInteger = clkmdiv; + clkmDecimals = (clkmdiv - clkmInteger) / denom; + bck = mclk / b_clk; + } else #endif - { - clkmInteger = clkmdiv; - clkmDecimals = (clkmdiv - clkmInteger) / denom; - mclk = clkmInteger + denom * clkmDecimals; - bck = factor / (bits * channel); - } + { + clkmInteger = clkmdiv; + clkmDecimals = (clkmdiv - clkmInteger) / denom; + mclk = clkmInteger + denom * clkmDecimals; + bck = factor / (bits * channel); + } - if (p_i2s_obj[i2s_num]->use_apll && p_i2s_obj[i2s_num]->fixed_mclk) - { - fi2s_clk = p_i2s_obj[i2s_num]->fixed_mclk; - m_scale = fi2s_clk / bits / rate / channel; - } - if (p_i2s_obj[i2s_num]->use_apll - && i2s_apll_calculate_fi2s (fi2s_clk, bits, &sdm0, &sdm1, &sdm2, &odir) - == ESP_OK) - { - ESP_LOGD (I2S_TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, - sdm2, odir); - rtc_clk_apll_enable (1, sdm0, sdm1, sdm2, odir); - i2s_hal_set_clk_div (&(p_i2s_obj[i2s_num]->hal), 1, 1, 0, m_scale, - m_scale); - i2s_hal_set_clock_sel (&(p_i2s_obj[i2s_num]->hal), I2S_CLK_APLL); - double fi2s_rate = i2s_apll_get_fi2s (bits, sdm0, sdm1, sdm2, odir); - p_i2s_obj[i2s_num]->real_rate = fi2s_rate / bits / channel / m_scale; - ESP_LOGI (I2S_TAG, - "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, " - "BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", - rate, fi2s_rate / bits / channel / m_scale, bits, 1, m_scale, - fi2s_rate, fi2s_rate / 8, 1, 0); - } - else - { - i2s_hal_set_clock_sel (&(p_i2s_obj[i2s_num]->hal), I2S_CLK_D2CLK); - i2s_hal_set_clk_div (&(p_i2s_obj[i2s_num]->hal), clkmInteger, 63, - clkmDecimals, bck, bck); - double real_rate - = (double)(I2S_BASE_CLK / (bck * bits * clkmInteger) / 2); - p_i2s_obj[i2s_num]->real_rate = real_rate; - ESP_LOGI (I2S_TAG, - "PLL_D2: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, " - "BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", - rate, real_rate, bits, clkmInteger, bck, - (double)I2S_BASE_CLK / mclk, real_rate * bits * channel, 64, - clkmDecimals); - } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) - { - p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; - p_i2s_obj[i2s_num]->tx->rw_pos = 0; - } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) - { - p_i2s_obj[i2s_num]->rx->curr_ptr = NULL; - p_i2s_obj[i2s_num]->rx->rw_pos = 0; - } + if (p_i2s_obj[i2s_num]->use_apll && p_i2s_obj[i2s_num]->fixed_mclk) { + fi2s_clk = p_i2s_obj[i2s_num]->fixed_mclk; + m_scale = fi2s_clk / bits / rate / channel; + } + if (p_i2s_obj[i2s_num]->use_apll && + i2s_apll_calculate_fi2s(fi2s_clk, bits, &sdm0, &sdm1, &sdm2, &odir) == + ESP_OK) { + ESP_LOGD(I2S_TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, + odir); + rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, odir); + i2s_hal_set_clk_div(&(p_i2s_obj[i2s_num]->hal), 1, 1, 0, m_scale, m_scale); + i2s_hal_set_clock_sel(&(p_i2s_obj[i2s_num]->hal), I2S_CLK_APLL); + double fi2s_rate = i2s_apll_get_fi2s(bits, sdm0, sdm1, sdm2, odir); + p_i2s_obj[i2s_num]->real_rate = fi2s_rate / bits / channel / m_scale; + ESP_LOGI(I2S_TAG, + "APLL: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, " + "BCK_M: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, fi2s_rate / bits / channel / m_scale, bits, 1, m_scale, + fi2s_rate, fi2s_rate / 8, 1, 0); + } else { + i2s_hal_set_clock_sel(&(p_i2s_obj[i2s_num]->hal), I2S_CLK_D2CLK); + i2s_hal_set_clk_div(&(p_i2s_obj[i2s_num]->hal), clkmInteger, 63, + clkmDecimals, bck, bck); + double real_rate = (double)(I2S_BASE_CLK / (bck * bits * clkmInteger) / 2); + p_i2s_obj[i2s_num]->real_rate = real_rate; + ESP_LOGI(I2S_TAG, + "PLL_D2: Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, " + "BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, real_rate, bits, clkmInteger, bck, + (double)I2S_BASE_CLK / mclk, real_rate * bits * channel, 64, + clkmDecimals); + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + p_i2s_obj[i2s_num]->tx->curr_ptr = NULL; + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + p_i2s_obj[i2s_num]->rx->curr_ptr = NULL; + p_i2s_obj[i2s_num]->rx->rw_pos = 0; + } - i2s_hal_set_tx_bits_mod (&(p_i2s_obj[i2s_num]->hal), bits); - i2s_hal_set_rx_bits_mod (&(p_i2s_obj[i2s_num]->hal), bits); + i2s_hal_set_tx_bits_mod(&(p_i2s_obj[i2s_num]->hal), bits); + i2s_hal_set_rx_bits_mod(&(p_i2s_obj[i2s_num]->hal), bits); // wait all writing on-going finish - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) - { - xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); - } - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) - { - xSemaphoreGive (p_i2s_obj[i2s_num]->rx->mux); - } + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) && p_i2s_obj[i2s_num]->tx) { + xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + } + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && p_i2s_obj[i2s_num]->rx) { + xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); + } // i2s_custom_start(i2s_num); // don't start just yet, we want to // fill dma buffer first return ESP_OK; @@ -648,20 +577,17 @@ i2s_custom_set_clk (i2s_port_t i2s_num, uint32_t rate, // ensure all DMA buffers are available right after initialization // ENSURE i2s_custom_start() isn't called before i2s_write has filled at // least one buffer - return i2s_custom_init_dma_tx_queues (i2s_num, NULL, 0, NULL); + return i2s_custom_init_dma_tx_queues(i2s_num, NULL, 0, NULL, NULL, NULL); } -static void IRAM_ATTR -i2s_intr_handler_default (void *arg) -{ +static void IRAM_ATTR i2s_intr_handler_default(void *arg) { i2s_obj_t *p_i2s = (i2s_obj_t *)arg; uint32_t status; - i2s_hal_get_intr_status (&(p_i2s->hal), &status); - if (status == 0) - { - // Avoid spurious interrupt - return; - } + i2s_hal_get_intr_status(&(p_i2s->hal), &status); + if (status == 0) { + // Avoid spurious interrupt + return; + } i2s_event_t i2s_event; int dummy; @@ -670,416 +596,345 @@ i2s_intr_handler_default (void *arg) lldesc_t *finish_desc = NULL; - if ((status & I2S_INTR_OUT_DSCR_ERR) || (status & I2S_INTR_IN_DSCR_ERR)) - { - ESP_EARLY_LOGE (I2S_TAG, "dma error, interrupt status: 0x%08x", status); - if (p_i2s->i2s_queue) - { - i2s_event.type = I2S_EVENT_DMA_ERROR; - if (xQueueIsQueueFullFromISR (p_i2s->i2s_queue)) - { - xQueueReceiveFromISR (p_i2s->i2s_queue, &dummy, - &high_priority_task_awoken); - } - xQueueSendFromISR (p_i2s->i2s_queue, (void *)&i2s_event, + if ((status & I2S_INTR_OUT_DSCR_ERR) || (status & I2S_INTR_IN_DSCR_ERR)) { + ESP_EARLY_LOGE(I2S_TAG, "dma error, interrupt status: 0x%08x", status); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_DMA_ERROR; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); - } + } + xQueueSendFromISR(p_i2s->i2s_queue, (void *)&i2s_event, + &high_priority_task_awoken); } + } - if ((status & I2S_INTR_OUT_EOF) && p_i2s->tx) - { - i2s_hal_get_out_eof_des_addr (&(p_i2s->hal), (uint32_t *)&finish_desc); - // All buffers are empty. This means we have an underflow on our hands. - if (xQueueIsQueueFullFromISR (p_i2s->tx->queue)) - { - xQueueReceiveFromISR (p_i2s->tx->queue, &dummy, - &high_priority_task_awoken); - // See if tx descriptor needs to be auto cleared: - // This will avoid any kind of noise that may get introduced due to - // transmission of previous data from tx descriptor on I2S line. - if (p_i2s->tx_desc_auto_clear == true) - { - memset ((void *)dummy, 0, p_i2s->tx->buf_size); - } - } - xQueueSendFromISR (p_i2s->tx->queue, (void *)(&finish_desc->buf), - &high_priority_task_awoken); - if (p_i2s->i2s_queue) - { - i2s_event.type = I2S_EVENT_TX_DONE; - if (xQueueIsQueueFullFromISR (p_i2s->i2s_queue)) - { - xQueueReceiveFromISR (p_i2s->i2s_queue, &dummy, - &high_priority_task_awoken); - } - xQueueSendFromISR (p_i2s->i2s_queue, (void *)&i2s_event, + if ((status & I2S_INTR_OUT_EOF) && p_i2s->tx) { + i2s_hal_get_out_eof_des_addr(&(p_i2s->hal), (uint32_t *)&finish_desc); + // All buffers are empty. This means we have an underflow on our hands. + if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { + xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, + &high_priority_task_awoken); + // See if tx descriptor needs to be auto cleared: + // This will avoid any kind of noise that may get introduced due to + // transmission of previous data from tx descriptor on I2S line. + if (p_i2s->tx_desc_auto_clear == true) { + memset((void *)dummy, 0, p_i2s->tx->buf_size); + } + } + xQueueSendFromISR(p_i2s->tx->queue, (void *)(&finish_desc->buf), + &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_TX_DONE; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); - } + } + xQueueSendFromISR(p_i2s->i2s_queue, (void *)&i2s_event, + &high_priority_task_awoken); } + } - if ((status & I2S_INTR_IN_SUC_EOF) && p_i2s->rx) - { - // All buffers are full. This means we have an overflow. - i2s_hal_get_in_eof_des_addr (&(p_i2s->hal), (uint32_t *)&finish_desc); - if (xQueueIsQueueFullFromISR (p_i2s->rx->queue)) - { - xQueueReceiveFromISR (p_i2s->rx->queue, &dummy, - &high_priority_task_awoken); - } - xQueueSendFromISR (p_i2s->rx->queue, (void *)(&finish_desc->buf), - &high_priority_task_awoken); - if (p_i2s->i2s_queue) - { - i2s_event.type = I2S_EVENT_RX_DONE; - if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR (p_i2s->i2s_queue)) - { - xQueueReceiveFromISR (p_i2s->i2s_queue, &dummy, - &high_priority_task_awoken); - } - xQueueSendFromISR (p_i2s->i2s_queue, (void *)&i2s_event, + if ((status & I2S_INTR_IN_SUC_EOF) && p_i2s->rx) { + // All buffers are full. This means we have an overflow. + i2s_hal_get_in_eof_des_addr(&(p_i2s->hal), (uint32_t *)&finish_desc); + if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { + xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, + &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->rx->queue, (void *)(&finish_desc->buf), + &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_RX_DONE; + if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); - } + } + xQueueSendFromISR(p_i2s->i2s_queue, (void *)&i2s_event, + &high_priority_task_awoken); } - i2s_hal_clear_intr_status (&(p_i2s->hal), status); + } + i2s_hal_clear_intr_status(&(p_i2s->hal), status); - if (high_priority_task_awoken == pdTRUE) - { - portYIELD_FROM_ISR (); - } + if (high_priority_task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } } -static esp_err_t -i2s_destroy_dma_queue (i2s_port_t i2s_num, i2s_dma_t *dma) -{ +static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) { int bux_idx; - if (p_i2s_obj[i2s_num] == NULL) - { - ESP_LOGE (I2S_TAG, "Not initialized yet"); - return ESP_ERR_INVALID_ARG; + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Not initialized yet"); + return ESP_ERR_INVALID_ARG; + } + if (dma == NULL) { + ESP_LOGE(I2S_TAG, "dma is NULL"); + return ESP_ERR_INVALID_ARG; + } + for (bux_idx = 0; bux_idx < p_i2s_obj[i2s_num]->dma_buf_count; bux_idx++) { + if (dma->desc && dma->desc[bux_idx]) { + free(dma->desc[bux_idx]); } - if (dma == NULL) - { - ESP_LOGE (I2S_TAG, "dma is NULL"); - return ESP_ERR_INVALID_ARG; + if (dma->buf && dma->buf[bux_idx]) { + free(dma->buf[bux_idx]); } - for (bux_idx = 0; bux_idx < p_i2s_obj[i2s_num]->dma_buf_count; bux_idx++) - { - if (dma->desc && dma->desc[bux_idx]) - { - free (dma->desc[bux_idx]); - } - if (dma->buf && dma->buf[bux_idx]) - { - free (dma->buf[bux_idx]); - } - } - if (dma->buf) - { - free (dma->buf); - } - if (dma->desc) - { - free (dma->desc); - } - vQueueDelete (dma->queue); - vSemaphoreDelete (dma->mux); - free (dma); + } + if (dma->buf) { + free(dma->buf); + } + if (dma->desc) { + free(dma->desc); + } + vQueueDelete(dma->queue); + vSemaphoreDelete(dma->mux); + free(dma); return ESP_OK; } -static i2s_dma_t * -i2s_create_dma_queue (i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) -{ +static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, + int dma_buf_len) { int bux_idx; - int sample_size - = p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; - i2s_dma_t *dma = (i2s_dma_t *)malloc (sizeof (i2s_dma_t)); - if (dma == NULL) - { - ESP_LOGE (I2S_TAG, "Error malloc i2s_dma_t"); + int sample_size = + p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + i2s_dma_t *dma = (i2s_dma_t *)malloc(sizeof(i2s_dma_t)); + if (dma == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc i2s_dma_t"); + return NULL; + } + memset(dma, 0, sizeof(i2s_dma_t)); + + dma->buf = (char **)malloc(sizeof(char *) * dma_buf_count); + if (dma->buf == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer pointer"); + free(dma); + return NULL; + } + memset(dma->buf, 0, sizeof(char *) * dma_buf_count); + + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->buf[bux_idx] = + (char *)heap_caps_calloc(1, dma_buf_len * sample_size, MALLOC_CAP_DMA); + if (dma->buf[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer"); + i2s_destroy_dma_queue(i2s_num, dma); return NULL; } - memset (dma, 0, sizeof (i2s_dma_t)); + ESP_LOGD(I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); + } - dma->buf = (char **)malloc (sizeof (char *) * dma_buf_count); - if (dma->buf == NULL) - { - ESP_LOGE (I2S_TAG, "Error malloc dma buffer pointer"); - free (dma); + dma->desc = (lldesc_t **)malloc(sizeof(lldesc_t *) * dma_buf_count); + if (dma->desc == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx] = + (lldesc_t *)heap_caps_malloc(sizeof(lldesc_t), MALLOC_CAP_DMA); + if (dma->desc[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description entry"); + i2s_destroy_dma_queue(i2s_num, dma); return NULL; } - memset (dma->buf, 0, sizeof (char *) * dma_buf_count); + } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) - { - dma->buf[bux_idx] = (char *)heap_caps_calloc ( - 1, dma_buf_len * sample_size, MALLOC_CAP_DMA); - if (dma->buf[bux_idx] == NULL) - { - ESP_LOGE (I2S_TAG, "Error malloc dma buffer"); - i2s_destroy_dma_queue (i2s_num, dma); - return NULL; - } - ESP_LOGD (I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); - } + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx]->owner = 1; + dma->desc[bux_idx]->eof = 1; + dma->desc[bux_idx]->sosf = 0; + dma->desc[bux_idx]->length = dma_buf_len * sample_size; + dma->desc[bux_idx]->size = dma_buf_len * sample_size; + dma->desc[bux_idx]->buf = (uint8_t *)dma->buf[bux_idx]; + dma->desc[bux_idx]->offset = 0; + dma->desc[bux_idx]->empty = + (uint32_t)((bux_idx < (dma_buf_count - 1)) ? (dma->desc[bux_idx + 1]) + : dma->desc[0]); + } - dma->desc = (lldesc_t **)malloc (sizeof (lldesc_t *) * dma_buf_count); - if (dma->desc == NULL) - { - ESP_LOGE (I2S_TAG, "Error malloc dma description"); - i2s_destroy_dma_queue (i2s_num, dma); - return NULL; - } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) - { - dma->desc[bux_idx] - = (lldesc_t *)heap_caps_malloc (sizeof (lldesc_t), MALLOC_CAP_DMA); - if (dma->desc[bux_idx] == NULL) - { - ESP_LOGE (I2S_TAG, "Error malloc dma description entry"); - i2s_destroy_dma_queue (i2s_num, dma); - return NULL; - } - } - - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) - { - dma->desc[bux_idx]->owner = 1; - dma->desc[bux_idx]->eof = 1; - dma->desc[bux_idx]->sosf = 0; - dma->desc[bux_idx]->length = dma_buf_len * sample_size; - dma->desc[bux_idx]->size = dma_buf_len * sample_size; - dma->desc[bux_idx]->buf = (uint8_t *)dma->buf[bux_idx]; - dma->desc[bux_idx]->offset = 0; - dma->desc[bux_idx]->empty = (uint32_t) ((bux_idx < (dma_buf_count - 1)) - ? (dma->desc[bux_idx + 1]) - : dma->desc[0]); - } - - dma->queue = xQueueCreate (dma_buf_count - 1, sizeof (char *)); - dma->mux = xSemaphoreCreateMutex (); + dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char *)); + dma->mux = xSemaphoreCreateMutex(); dma->buf_size = dma_buf_len * sample_size; - ESP_LOGI (I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", - dma_buf_len * sample_size, dma_buf_count); + ESP_LOGI(I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", + dma_buf_len * sample_size, dma_buf_count); return dma; } -esp_err_t -i2s_custom_start (i2s_port_t i2s_num) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); +esp_err_t i2s_custom_start(i2s_port_t i2s_num) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); // start DMA link - I2S_ENTER_CRITICAL (); - i2s_hal_reset (&(p_i2s_obj[i2s_num]->hal)); + I2S_ENTER_CRITICAL(); + i2s_hal_reset(&(p_i2s_obj[i2s_num]->hal)); - esp_intr_disable (p_i2s_obj[i2s_num]->i2s_isr_handle); - i2s_hal_clear_intr_status (&(p_i2s_obj[i2s_num]->hal), I2S_INTR_MAX); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) - { - i2s_custom_enable_tx_intr (i2s_num); - i2s_hal_start_tx (&(p_i2s_obj[i2s_num]->hal)); - } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) - { - i2s_custom_enable_rx_intr (i2s_num); - i2s_hal_start_rx (&(p_i2s_obj[i2s_num]->hal)); - } - esp_intr_enable (p_i2s_obj[i2s_num]->i2s_isr_handle); - I2S_EXIT_CRITICAL (); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), I2S_INTR_MAX); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + i2s_custom_enable_tx_intr(i2s_num); + i2s_hal_start_tx(&(p_i2s_obj[i2s_num]->hal)); + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + i2s_custom_enable_rx_intr(i2s_num); + i2s_hal_start_rx(&(p_i2s_obj[i2s_num]->hal)); + } + esp_intr_enable(p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S_EXIT_CRITICAL(); return ESP_OK; } -esp_err_t -i2s_custom_stop (i2s_port_t i2s_num) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_ENTER_CRITICAL (); - esp_intr_disable (p_i2s_obj[i2s_num]->i2s_isr_handle); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) - { - i2s_hal_stop_tx (&(p_i2s_obj[i2s_num]->hal)); - i2s_custom_disable_tx_intr (i2s_num); - } - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) - { - i2s_hal_stop_rx (&(p_i2s_obj[i2s_num]->hal)); - i2s_custom_disable_rx_intr (i2s_num); - } +esp_err_t i2s_custom_stop(i2s_port_t i2s_num) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_ENTER_CRITICAL(); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + i2s_hal_stop_tx(&(p_i2s_obj[i2s_num]->hal)); + i2s_custom_disable_tx_intr(i2s_num); + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + i2s_hal_stop_rx(&(p_i2s_obj[i2s_num]->hal)); + i2s_custom_disable_rx_intr(i2s_num); + } uint32_t mask; - i2s_hal_get_intr_status (&(p_i2s_obj[i2s_num]->hal), &mask); - i2s_hal_clear_intr_status (&(p_i2s_obj[i2s_num]->hal), mask); - I2S_EXIT_CRITICAL (); + i2s_hal_get_intr_status(&(p_i2s_obj[i2s_num]->hal), &mask); + i2s_hal_clear_intr_status(&(p_i2s_obj[i2s_num]->hal), mask); + I2S_EXIT_CRITICAL(); return ESP_OK; } -esp_err_t -i2s_custom_set_pin (i2s_port_t i2s_num, const i2s_pin_config_t *pin) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (pin == NULL) - { - return ESP_ERR_INVALID_ARG; - } +esp_err_t i2s_custom_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + if (pin == NULL) { + return ESP_ERR_INVALID_ARG; + } - if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO (pin->bck_io_num)) - { - ESP_LOGE (I2S_TAG, "bck_io_num error"); - return ESP_FAIL; - } - if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO (pin->ws_io_num)) - { - ESP_LOGE (I2S_TAG, "ws_io_num error"); - return ESP_FAIL; - } - if (pin->data_out_num != -1 - && !GPIO_IS_VALID_OUTPUT_GPIO (pin->data_out_num)) - { - ESP_LOGE (I2S_TAG, "data_out_num error"); - return ESP_FAIL; - } - if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO (pin->data_in_num)) - { - ESP_LOGE (I2S_TAG, "data_in_num error"); - return ESP_FAIL; - } + if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->bck_io_num)) { + ESP_LOGE(I2S_TAG, "bck_io_num error"); + return ESP_FAIL; + } + if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->ws_io_num)) { + ESP_LOGE(I2S_TAG, "ws_io_num error"); + return ESP_FAIL; + } + if (pin->data_out_num != -1 && + !GPIO_IS_VALID_OUTPUT_GPIO(pin->data_out_num)) { + ESP_LOGE(I2S_TAG, "data_out_num error"); + return ESP_FAIL; + } + if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_in_num)) { + ESP_LOGE(I2S_TAG, "data_in_num error"); + return ESP_FAIL; + } int bck_sig = -1, ws_sig = -1, data_out_sig = -1, data_in_sig = -1; // Each IIS hw module has a RX and TX unit. // For TX unit, the output signal index should be I2SnO_xxx_OUT_IDX // For TX unit, the input signal index should be I2SnO_xxx_IN_IDX - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) - { - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) - { - bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; - data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; - } - else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) - { - bck_sig = i2s_periph_signal[i2s_num].o_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_in_sig; - data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; - } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { + bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; + ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; + data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; + } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { + bck_sig = i2s_periph_signal[i2s_num].o_bck_in_sig; + ws_sig = i2s_periph_signal[i2s_num].o_ws_in_sig; + data_out_sig = i2s_periph_signal[i2s_num].o_data_out_sig; } + } // For RX unit, the output signal index should be I2SnI_xxx_OUT_IDX // For RX unit, the input signal index should be I2SnI_xxx_IN_IDX - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) - { - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) - { - bck_sig = i2s_periph_signal[i2s_num].i_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_out_sig; - data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; - } - else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) - { - bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; - data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; - } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { + bck_sig = i2s_periph_signal[i2s_num].i_bck_out_sig; + ws_sig = i2s_periph_signal[i2s_num].i_ws_out_sig; + data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; + } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { + bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; + ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; + data_in_sig = i2s_periph_signal[i2s_num].i_data_in_sig; } + } // For "full-duplex + slave" mode, we should select RX signal index for ws // and bck. For "full-duplex + master" mode, we should select TX signal index // for ws and bck. - if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_SLAVE_MODE_MASK) - == I2S_FULL_DUPLEX_SLAVE_MODE_MASK) - { - bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; - ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; - } - else if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_MASTER_MODE_MASK) - == I2S_FULL_DUPLEX_MASTER_MODE_MASK) - { - bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; - ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; - } - gpio_matrix_out_check (pin->data_out_num, data_out_sig, 0, 0); - gpio_matrix_in_check (pin->data_in_num, data_in_sig, 0); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) - { - gpio_matrix_out_check (pin->ws_io_num, ws_sig, 0, 0); - gpio_matrix_out_check (pin->bck_io_num, bck_sig, 0, 0); - } - else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) - { - gpio_matrix_in_check (pin->ws_io_num, ws_sig, 0); - gpio_matrix_in_check (pin->bck_io_num, bck_sig, 0); - } - ESP_LOGD (I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, - data_in_sig, ws_sig, bck_sig); + if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_SLAVE_MODE_MASK) == + I2S_FULL_DUPLEX_SLAVE_MODE_MASK) { + bck_sig = i2s_periph_signal[i2s_num].i_bck_in_sig; + ws_sig = i2s_periph_signal[i2s_num].i_ws_in_sig; + } else if ((p_i2s_obj[i2s_num]->mode & I2S_FULL_DUPLEX_MASTER_MODE_MASK) == + I2S_FULL_DUPLEX_MASTER_MODE_MASK) { + bck_sig = i2s_periph_signal[i2s_num].o_bck_out_sig; + ws_sig = i2s_periph_signal[i2s_num].o_ws_out_sig; + } + gpio_matrix_out_check(pin->data_out_num, data_out_sig, 0, 0); + gpio_matrix_in_check(pin->data_in_num, data_in_sig, 0); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { + gpio_matrix_out_check(pin->ws_io_num, ws_sig, 0, 0); + gpio_matrix_out_check(pin->bck_io_num, bck_sig, 0, 0); + } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { + gpio_matrix_in_check(pin->ws_io_num, ws_sig, 0); + gpio_matrix_in_check(pin->bck_io_num, bck_sig, 0); + } + ESP_LOGD(I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, + data_in_sig, ws_sig, bck_sig); return ESP_OK; } -esp_err_t -i2s_custom_set_sample_rates (i2s_port_t i2s_num, uint32_t rate) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK ((p_i2s_obj[i2s_num]->bytes_per_sample > 0), - "bits_per_sample not set", ESP_ERR_INVALID_ARG); - return i2s_custom_set_clk (i2s_num, rate, - p_i2s_obj[i2s_num]->bits_per_sample, - p_i2s_obj[i2s_num]->channel_num); +esp_err_t i2s_custom_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((p_i2s_obj[i2s_num]->bytes_per_sample > 0), + "bits_per_sample not set", ESP_ERR_INVALID_ARG); + return i2s_custom_set_clk(i2s_num, rate, p_i2s_obj[i2s_num]->bits_per_sample, + p_i2s_obj[i2s_num]->channel_num); } #if SOC_I2S_SUPPORTS_PDM -esp_err_t -i2s_custom_set_pdm_rx_down_sample (i2s_port_t i2s_num, i2s_pdm_dsr_t dsr) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - i2s_hal_rx_pdm_cfg (&(p_i2s_obj[i2s_num]->hal), dsr); - return i2s_custom_set_clk (i2s_num, p_i2s_obj[i2s_num]->sample_rate, - p_i2s_obj[i2s_num]->bits_per_sample, - p_i2s_obj[i2s_num]->channel_num); +esp_err_t i2s_custom_set_pdm_rx_down_sample(i2s_port_t i2s_num, + i2s_pdm_dsr_t dsr) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + i2s_hal_rx_pdm_cfg(&(p_i2s_obj[i2s_num]->hal), dsr); + return i2s_custom_set_clk(i2s_num, p_i2s_obj[i2s_num]->sample_rate, + p_i2s_obj[i2s_num]->bits_per_sample, + p_i2s_obj[i2s_num]->channel_num); } #endif -static esp_err_t -i2s_custom_check_cfg_static (i2s_port_t i2s_num, const i2s_config_t *cfg) -{ +static esp_err_t i2s_custom_check_cfg_static(i2s_port_t i2s_num, + const i2s_config_t *cfg) { #if SOC_I2S_SUPPORTS_PDM // We only check if the I2S number is invalid when set to PDM mode. - I2S_CHECK (!((cfg->mode & I2S_MODE_PDM) && (i2s_num != I2S_NUM_0)), - "I2S DAC PDM only support on I2S0", ESP_ERR_INVALID_ARG); + I2S_CHECK(!((cfg->mode & I2S_MODE_PDM) && (i2s_num != I2S_NUM_0)), + "I2S DAC PDM only support on I2S0", ESP_ERR_INVALID_ARG); return ESP_OK; #endif - I2S_CHECK (cfg->communication_format - && (cfg->communication_format < I2S_COMM_FORMAT_STAND_MAX), - "invalid communication formats", ESP_ERR_INVALID_ARG); - I2S_CHECK ( - !((cfg->communication_format & I2S_COMM_FORMAT_STAND_MSB) - && (cfg->communication_format & I2S_COMM_FORMAT_STAND_PCM_LONG)), - "multiple communication formats specified", ESP_ERR_INVALID_ARG); + I2S_CHECK(cfg->communication_format && + (cfg->communication_format < I2S_COMM_FORMAT_STAND_MAX), + "invalid communication formats", ESP_ERR_INVALID_ARG); + I2S_CHECK(!((cfg->communication_format & I2S_COMM_FORMAT_STAND_MSB) && + (cfg->communication_format & I2S_COMM_FORMAT_STAND_PCM_LONG)), + "multiple communication formats specified", ESP_ERR_INVALID_ARG); return ESP_OK; } -static esp_err_t -i2s_param_config (i2s_port_t i2s_num, const i2s_config_t *i2s_config) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK ((i2s_config), "param null", ESP_ERR_INVALID_ARG); - I2S_CHECK ((i2s_custom_check_cfg_static (i2s_num, i2s_config) == ESP_OK), - "param check error", ESP_ERR_INVALID_ARG); +static esp_err_t i2s_param_config(i2s_port_t i2s_num, + const i2s_config_t *i2s_config) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((i2s_config), "param null", ESP_ERR_INVALID_ARG); + I2S_CHECK((i2s_custom_check_cfg_static(i2s_num, i2s_config) == ESP_OK), + "param check error", ESP_ERR_INVALID_ARG); - periph_module_enable (i2s_periph_signal[i2s_num].module); + periph_module_enable(i2s_periph_signal[i2s_num].module); // configure I2S data port interface. - i2s_hal_config_param (&(p_i2s_obj[i2s_num]->hal), i2s_config); - if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) - && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) - { - i2s_hal_enable_sig_loopback (&(p_i2s_obj[i2s_num]->hal)); - if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) - { - i2s_hal_enable_master_mode (&(p_i2s_obj[i2s_num]->hal)); - } - else - { - i2s_hal_enable_slave_mode (&(p_i2s_obj[i2s_num]->hal)); - } + i2s_hal_config_param(&(p_i2s_obj[i2s_num]->hal), i2s_config); + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && + (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) { + i2s_hal_enable_sig_loopback(&(p_i2s_obj[i2s_num]->hal)); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { + i2s_hal_enable_master_mode(&(p_i2s_obj[i2s_num]->hal)); + } else { + i2s_hal_enable_slave_mode(&(p_i2s_obj[i2s_num]->hal)); } + } p_i2s_obj[i2s_num]->use_apll = i2s_config->use_apll; p_i2s_obj[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; @@ -1087,405 +942,351 @@ i2s_param_config (i2s_port_t i2s_num, const i2s_config_t *i2s_config) return ESP_OK; } -esp_err_t -i2s_custom_zero_dma_buffer (i2s_port_t i2s_num) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num]->rx && p_i2s_obj[i2s_num]->rx->buf != NULL - && p_i2s_obj[i2s_num]->rx->buf_size != 0) - { - for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) - { - memset (p_i2s_obj[i2s_num]->rx->buf[i], 0, - p_i2s_obj[i2s_num]->rx->buf_size); - } +esp_err_t i2s_custom_zero_dma_buffer(i2s_port_t i2s_num) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + if (p_i2s_obj[i2s_num]->rx && p_i2s_obj[i2s_num]->rx->buf != NULL && + p_i2s_obj[i2s_num]->rx->buf_size != 0) { + for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { + memset(p_i2s_obj[i2s_num]->rx->buf[i], 0, + p_i2s_obj[i2s_num]->rx->buf_size); } - if (p_i2s_obj[i2s_num]->tx && p_i2s_obj[i2s_num]->tx->buf != NULL - && p_i2s_obj[i2s_num]->tx->buf_size != 0) - { - int bytes_left = 0; - bytes_left - = (p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos) - % 4; - if (bytes_left) - { - size_t zero_bytes = 0, bytes_written; - i2s_custom_write (i2s_num, (void *)&zero_bytes, bytes_left, - &bytes_written, portMAX_DELAY); - } - for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) - { - memset (p_i2s_obj[i2s_num]->tx->buf[i], 0, - p_i2s_obj[i2s_num]->tx->buf_size); - } + } + if (p_i2s_obj[i2s_num]->tx && p_i2s_obj[i2s_num]->tx->buf != NULL && + p_i2s_obj[i2s_num]->tx->buf_size != 0) { + int bytes_left = 0; + bytes_left = + (p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos) % 4; + if (bytes_left) { + size_t zero_bytes = 0, bytes_written; + i2s_custom_write(i2s_num, (void *)&zero_bytes, bytes_left, &bytes_written, + portMAX_DELAY); } + for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { + memset(p_i2s_obj[i2s_num]->tx->buf[i], 0, + p_i2s_obj[i2s_num]->tx->buf_size); + } + } return ESP_OK; } -esp_err_t -i2s_custom_driver_install (i2s_port_t i2s_num, const i2s_config_t *i2s_config, - int queue_size, void *i2s_queue) -{ +esp_err_t i2s_custom_driver_install(i2s_port_t i2s_num, + const i2s_config_t *i2s_config, + int queue_size, void *i2s_queue) { esp_err_t err; - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK ((i2s_config != NULL), "I2S configuration must not NULL", - ESP_ERR_INVALID_ARG); - I2S_CHECK ( + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((i2s_config != NULL), "I2S configuration must not NULL", + ESP_ERR_INVALID_ARG); + I2S_CHECK( (i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), "I2S buffer count less than 128 and more than 2", ESP_ERR_INVALID_ARG); - I2S_CHECK ((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), - "I2S buffer length at most 1024 and more than 8", - ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num] == NULL) - { - p_i2s_obj[i2s_num] = (i2s_obj_t *)malloc (sizeof (i2s_obj_t)); - if (p_i2s_obj[i2s_num] == NULL) - { - ESP_LOGE (I2S_TAG, "Malloc I2S driver error"); - return ESP_ERR_NO_MEM; - } - memset (p_i2s_obj[i2s_num], 0, sizeof (i2s_obj_t)); + I2S_CHECK((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), + "I2S buffer length at most 1024 and more than 8", + ESP_ERR_INVALID_ARG); + if (p_i2s_obj[i2s_num] == NULL) { + p_i2s_obj[i2s_num] = (i2s_obj_t *)malloc(sizeof(i2s_obj_t)); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Malloc I2S driver error"); + return ESP_ERR_NO_MEM; + } + memset(p_i2s_obj[i2s_num], 0, sizeof(i2s_obj_t)); - portMUX_TYPE i2s_spinlock_unlocked[1] = { portMUX_INITIALIZER_UNLOCKED }; - for (int x = 0; x < I2S_NUM_MAX; x++) - { - i2s_spinlock[x] = i2s_spinlock_unlocked[0]; - } - // To make sure hardware is enabled before any hardware register - // operations. - periph_module_enable (i2s_periph_signal[i2s_num].module); - i2s_hal_init (&(p_i2s_obj[i2s_num]->hal), i2s_num); + portMUX_TYPE i2s_spinlock_unlocked[1] = {portMUX_INITIALIZER_UNLOCKED}; + for (int x = 0; x < I2S_NUM_MAX; x++) { + i2s_spinlock[x] = i2s_spinlock_unlocked[0]; + } + // To make sure hardware is enabled before any hardware register + // operations. + periph_module_enable(i2s_periph_signal[i2s_num].module); + i2s_hal_init(&(p_i2s_obj[i2s_num]->hal), i2s_num); - p_i2s_obj[i2s_num]->i2s_num = i2s_num; - p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; - p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; - p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; - p_i2s_obj[i2s_num]->mode = i2s_config->mode; + p_i2s_obj[i2s_num]->i2s_num = i2s_num; + p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; + p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; + p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; + p_i2s_obj[i2s_num]->mode = i2s_config->mode; - p_i2s_obj[i2s_num]->bits_per_sample = 0; - p_i2s_obj[i2s_num]->bytes_per_sample = 0; // Not initialized yet - p_i2s_obj[i2s_num]->channel_num - = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; + p_i2s_obj[i2s_num]->bits_per_sample = 0; + p_i2s_obj[i2s_num]->bytes_per_sample = 0; // Not initialized yet + p_i2s_obj[i2s_num]->channel_num = + i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; #ifdef CONFIG_PM_ENABLE - if (i2s_config->use_apll) - { - err = esp_pm_lock_create (ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", - &p_i2s_obj[i2s_num]->pm_lock); - } - else - { - err = esp_pm_lock_create (ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", - &p_i2s_obj[i2s_num]->pm_lock); - } - if (err != ESP_OK) - { - free (p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - ESP_LOGE (I2S_TAG, "I2S pm lock error"); - return err; - } -#endif // CONFIG_PM_ENABLE + if (i2s_config->use_apll) { + err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", + &p_i2s_obj[i2s_num]->pm_lock); + } else { + err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", + &p_i2s_obj[i2s_num]->pm_lock); + } + if (err != ESP_OK) { + free(p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + ESP_LOGE(I2S_TAG, "I2S pm lock error"); + return err; + } +#endif // CONFIG_PM_ENABLE - // initial interrupt - err = i2s_isr_register (i2s_num, i2s_config->intr_alloc_flags, - i2s_intr_handler_default, p_i2s_obj[i2s_num], - &p_i2s_obj[i2s_num]->i2s_isr_handle); - if (err != ESP_OK) - { + // initial interrupt + err = i2s_isr_register(i2s_num, i2s_config->intr_alloc_flags, + i2s_intr_handler_default, p_i2s_obj[i2s_num], + &p_i2s_obj[i2s_num]->i2s_isr_handle); + if (err != ESP_OK) { #ifdef CONFIG_PM_ENABLE - if (p_i2s_obj[i2s_num]->pm_lock) - { - esp_pm_lock_delete (p_i2s_obj[i2s_num]->pm_lock); - } + if (p_i2s_obj[i2s_num]->pm_lock) { + esp_pm_lock_delete(p_i2s_obj[i2s_num]->pm_lock); + } #endif - free (p_i2s_obj[i2s_num]); - p_i2s_obj[i2s_num] = NULL; - ESP_LOGE (I2S_TAG, "Register I2S Interrupt error"); - return err; - } - i2s_custom_stop (i2s_num); - err = i2s_param_config (i2s_num, i2s_config); - if (err != ESP_OK) - { - i2s_custom_driver_uninstall (i2s_num); - ESP_LOGE (I2S_TAG, "I2S param configure error"); - return err; - } - - if (i2s_queue) - { - p_i2s_obj[i2s_num]->i2s_queue - = xQueueCreate (queue_size, sizeof (i2s_event_t)); - *((QueueHandle_t *)i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; - ESP_LOGI (I2S_TAG, "queue free spaces: %d", - uxQueueSpacesAvailable (p_i2s_obj[i2s_num]->i2s_queue)); - } - else - { - p_i2s_obj[i2s_num]->i2s_queue = NULL; - } - // set clock and start - return i2s_custom_set_clk (i2s_num, i2s_config->sample_rate, - i2s_config->bits_per_sample, - p_i2s_obj[i2s_num]->channel_num); + free(p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + ESP_LOGE(I2S_TAG, "Register I2S Interrupt error"); + return err; + } + i2s_custom_stop(i2s_num); + err = i2s_param_config(i2s_num, i2s_config); + if (err != ESP_OK) { + i2s_custom_driver_uninstall(i2s_num); + ESP_LOGE(I2S_TAG, "I2S param configure error"); + return err; } - ESP_LOGW (I2S_TAG, "I2S driver already installed"); - return ESP_OK; -} - -esp_err_t -i2s_custom_driver_uninstall (i2s_port_t i2s_num) -{ - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - if (p_i2s_obj[i2s_num] == NULL) - { - ESP_LOGI (I2S_TAG, "already uninstalled"); - return ESP_OK; - } - i2s_custom_stop (i2s_num); - esp_intr_free (p_i2s_obj[i2s_num]->i2s_isr_handle); - - if (p_i2s_obj[i2s_num]->tx != NULL - && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) - { - i2s_destroy_dma_queue (i2s_num, p_i2s_obj[i2s_num]->tx); - p_i2s_obj[i2s_num]->tx = NULL; - } - if (p_i2s_obj[i2s_num]->rx != NULL - && (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX)) - { - i2s_destroy_dma_queue (i2s_num, p_i2s_obj[i2s_num]->rx); - p_i2s_obj[i2s_num]->rx = NULL; - } - - if (p_i2s_obj[i2s_num]->i2s_queue) - { - vQueueDelete (p_i2s_obj[i2s_num]->i2s_queue); + if (i2s_queue) { + p_i2s_obj[i2s_num]->i2s_queue = + xQueueCreate(queue_size, sizeof(i2s_event_t)); + *((QueueHandle_t *)i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; + ESP_LOGI(I2S_TAG, "queue free spaces: %d", + uxQueueSpacesAvailable(p_i2s_obj[i2s_num]->i2s_queue)); + } else { p_i2s_obj[i2s_num]->i2s_queue = NULL; } + // set clock and start + return i2s_custom_set_clk(i2s_num, i2s_config->sample_rate, + i2s_config->bits_per_sample, + p_i2s_obj[i2s_num]->channel_num); + } - if (p_i2s_obj[i2s_num]->use_apll) - { - rtc_clk_apll_enable (0, 0, 0, 0, 0); - } + ESP_LOGW(I2S_TAG, "I2S driver already installed"); + return ESP_OK; +} + +esp_err_t i2s_custom_driver_uninstall(i2s_port_t i2s_num) { + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGI(I2S_TAG, "already uninstalled"); + return ESP_OK; + } + i2s_custom_stop(i2s_num); + esp_intr_free(p_i2s_obj[i2s_num]->i2s_isr_handle); + + if (p_i2s_obj[i2s_num]->tx != NULL && + (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->tx); + p_i2s_obj[i2s_num]->tx = NULL; + } + if (p_i2s_obj[i2s_num]->rx != NULL && + (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX)) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->rx); + p_i2s_obj[i2s_num]->rx = NULL; + } + + if (p_i2s_obj[i2s_num]->i2s_queue) { + vQueueDelete(p_i2s_obj[i2s_num]->i2s_queue); + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + + if (p_i2s_obj[i2s_num]->use_apll) { + rtc_clk_apll_enable(0, 0, 0, 0, 0); + } #ifdef CONFIG_PM_ENABLE - if (p_i2s_obj[i2s_num]->pm_lock) - { - esp_pm_lock_delete (p_i2s_obj[i2s_num]->pm_lock); - } + if (p_i2s_obj[i2s_num]->pm_lock) { + esp_pm_lock_delete(p_i2s_obj[i2s_num]->pm_lock); + } #endif - free (p_i2s_obj[i2s_num]); + free(p_i2s_obj[i2s_num]); p_i2s_obj[i2s_num] = NULL; - periph_module_disable (i2s_periph_signal[i2s_num].module); + periph_module_disable(i2s_periph_signal[i2s_num].module); return ESP_OK; } -esp_err_t -i2s_custom_write (i2s_port_t i2s_num, const void *src, size_t size, - size_t *bytes_written, TickType_t ticks_to_wait) -{ +esp_err_t i2s_custom_write(i2s_port_t i2s_num, const void *src, size_t size, + size_t *bytes_written, TickType_t ticks_to_wait) { char *data_ptr, *src_byte; size_t bytes_can_write; *bytes_written = 0; - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK ((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", - ESP_ERR_INVALID_ARG); - I2S_CHECK ((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); - xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", + ESP_ERR_INVALID_ARG); + I2S_CHECK((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); + xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE - esp_pm_lock_acquire (p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_acquire(p_i2s_obj[i2s_num]->pm_lock); #endif src_byte = (char *)src; - while (size > 0) - { - // for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { - // ESP_LOGI(I2S_TAG,"%d: EOF %d", i, - // p_i2s_obj[i2s_num]->tx->desc[i]->eof); - // } + while (size > 0) { + // for (int i = 0; i < p_i2s_obj[i2s_num]->dma_buf_count; i++) { + // ESP_LOGI(I2S_TAG,"%d: EOF %d", i, + // p_i2s_obj[i2s_num]->tx->desc[i]->eof); + // } - if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size - || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) - { - if (xQueueReceive (p_i2s_obj[i2s_num]->tx->queue, - &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) - == pdFALSE) - { - break; - } - p_i2s_obj[i2s_num]->tx->rw_pos = 0; - } - ESP_LOGD (I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", - size, p_i2s_obj[i2s_num]->tx->rw_pos, - p_i2s_obj[i2s_num]->tx->buf_size, - (int)p_i2s_obj[i2s_num]->tx->curr_ptr); - data_ptr = (char *)p_i2s_obj[i2s_num]->tx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; - bytes_can_write - = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; - if (bytes_can_write > size) - { - bytes_can_write = size; - } - memcpy (data_ptr, src_byte, bytes_can_write); - size -= bytes_can_write; - src_byte += bytes_can_write; - p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; - (*bytes_written) += bytes_can_write; + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || + p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, + &p_i2s_obj[i2s_num]->tx->curr_ptr, + ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->tx->rw_pos = 0; } + ESP_LOGD(I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, + p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, + (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + data_ptr = (char *)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + bytes_can_write = + p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + if (bytes_can_write > size) { + bytes_can_write = size; + } + memcpy(data_ptr, src_byte, bytes_can_write); + size -= bytes_can_write; + src_byte += bytes_can_write; + p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + (*bytes_written) += bytes_can_write; + } #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release (p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_release(p_i2s_obj[i2s_num]->pm_lock); #endif - xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); + xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); return ESP_OK; } -esp_err_t -i2s_custom_write_expand (i2s_port_t i2s_num, const void *src, size_t size, - size_t src_bits, size_t aim_bits, - size_t *bytes_written, TickType_t ticks_to_wait) -{ +esp_err_t i2s_custom_write_expand(i2s_port_t i2s_num, const void *src, + size_t size, size_t src_bits, size_t aim_bits, + size_t *bytes_written, + TickType_t ticks_to_wait) { char *data_ptr; int bytes_can_write, tail; int src_bytes, aim_bytes, zero_bytes; *bytes_written = 0; - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK ((size > 0), "size must greater than zero", ESP_ERR_INVALID_ARG); - I2S_CHECK ((aim_bits * size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", - ESP_ERR_INVALID_ARG); - I2S_CHECK ((aim_bits >= src_bits), "aim_bits mustn't be less than src_bits", - ESP_ERR_INVALID_ARG); - I2S_CHECK ((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); - if (src_bits < I2S_BITS_PER_SAMPLE_8BIT - || aim_bits < I2S_BITS_PER_SAMPLE_8BIT) - { - ESP_LOGE (I2S_TAG, - "bits mustn't be less than 8, src_bits %d aim_bits %d", - src_bits, aim_bits); - return ESP_ERR_INVALID_ARG; - } - if (src_bits > I2S_BITS_PER_SAMPLE_32BIT - || aim_bits > I2S_BITS_PER_SAMPLE_32BIT) - { - ESP_LOGE (I2S_TAG, - "bits mustn't be greater than 32, src_bits %d aim_bits %d", - src_bits, aim_bits); - return ESP_ERR_INVALID_ARG; - } - if ((src_bits == I2S_BITS_PER_SAMPLE_16BIT - || src_bits == I2S_BITS_PER_SAMPLE_32BIT) - && (size % 2 != 0)) - { - ESP_LOGE (I2S_TAG, - "size must be a even number while src_bits is even, src_bits " - "%d size %d", - src_bits, size); - return ESP_ERR_INVALID_ARG; - } - if (src_bits == I2S_BITS_PER_SAMPLE_24BIT && (size % 3 != 0)) - { - ESP_LOGE (I2S_TAG, - "size must be a multiple of 3 while src_bits is 24, size %d", - size); - return ESP_ERR_INVALID_ARG; - } + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((size > 0), "size must greater than zero", ESP_ERR_INVALID_ARG); + I2S_CHECK((aim_bits * size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", + ESP_ERR_INVALID_ARG); + I2S_CHECK((aim_bits >= src_bits), "aim_bits mustn't be less than src_bits", + ESP_ERR_INVALID_ARG); + I2S_CHECK((p_i2s_obj[i2s_num]->tx), "tx NULL", ESP_ERR_INVALID_ARG); + if (src_bits < I2S_BITS_PER_SAMPLE_8BIT || + aim_bits < I2S_BITS_PER_SAMPLE_8BIT) { + ESP_LOGE(I2S_TAG, "bits mustn't be less than 8, src_bits %d aim_bits %d", + src_bits, aim_bits); + return ESP_ERR_INVALID_ARG; + } + if (src_bits > I2S_BITS_PER_SAMPLE_32BIT || + aim_bits > I2S_BITS_PER_SAMPLE_32BIT) { + ESP_LOGE(I2S_TAG, + "bits mustn't be greater than 32, src_bits %d aim_bits %d", + src_bits, aim_bits); + return ESP_ERR_INVALID_ARG; + } + if ((src_bits == I2S_BITS_PER_SAMPLE_16BIT || + src_bits == I2S_BITS_PER_SAMPLE_32BIT) && + (size % 2 != 0)) { + ESP_LOGE(I2S_TAG, + "size must be a even number while src_bits is even, src_bits " + "%d size %d", + src_bits, size); + return ESP_ERR_INVALID_ARG; + } + if (src_bits == I2S_BITS_PER_SAMPLE_24BIT && (size % 3 != 0)) { + ESP_LOGE(I2S_TAG, + "size must be a multiple of 3 while src_bits is 24, size %d", + size); + return ESP_ERR_INVALID_ARG; + } src_bytes = src_bits / 8; aim_bytes = aim_bits / 8; zero_bytes = aim_bytes - src_bytes; - xSemaphoreTake (p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); size = size * aim_bytes / src_bytes; - ESP_LOGD (I2S_TAG, "aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, - size); - while (size > 0) - { - if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size - || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) - { - if (xQueueReceive (p_i2s_obj[i2s_num]->tx->queue, - &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) - == pdFALSE) - { - break; - } - p_i2s_obj[i2s_num]->tx->rw_pos = 0; - } - data_ptr = (char *)p_i2s_obj[i2s_num]->tx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; - bytes_can_write - = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; - if (bytes_can_write > (int)size) - { - bytes_can_write = size; - } - tail = bytes_can_write % aim_bytes; - bytes_can_write = bytes_can_write - tail; - - memset (data_ptr, 0, bytes_can_write); - for (int j = 0; j < bytes_can_write; j += (aim_bytes - zero_bytes)) - { - j += zero_bytes; - memcpy (&data_ptr[j], (const char *)(src + *bytes_written), - aim_bytes - zero_bytes); - (*bytes_written) += (aim_bytes - zero_bytes); - } - size -= bytes_can_write; - p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + ESP_LOGD(I2S_TAG, "aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, + size); + while (size > 0) { + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || + p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, + &p_i2s_obj[i2s_num]->tx->curr_ptr, + ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->tx->rw_pos = 0; } - xSemaphoreGive (p_i2s_obj[i2s_num]->tx->mux); + data_ptr = (char *)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + bytes_can_write = + p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + if (bytes_can_write > (int)size) { + bytes_can_write = size; + } + tail = bytes_can_write % aim_bytes; + bytes_can_write = bytes_can_write - tail; + + memset(data_ptr, 0, bytes_can_write); + for (int j = 0; j < bytes_can_write; j += (aim_bytes - zero_bytes)) { + j += zero_bytes; + memcpy(&data_ptr[j], (const char *)(src + *bytes_written), + aim_bytes - zero_bytes); + (*bytes_written) += (aim_bytes - zero_bytes); + } + size -= bytes_can_write; + p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + } + xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); return ESP_OK; } -esp_err_t -i2s_custom_read (i2s_port_t i2s_num, void *dest, size_t size, - size_t *bytes_read, TickType_t ticks_to_wait) -{ +esp_err_t i2s_custom_read(i2s_port_t i2s_num, void *dest, size_t size, + size_t *bytes_read, TickType_t ticks_to_wait) { char *data_ptr, *dest_byte; int bytes_can_read; *bytes_read = 0; dest_byte = (char *)dest; - I2S_CHECK ((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); - I2S_CHECK ((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", - ESP_ERR_INVALID_ARG); - I2S_CHECK ((p_i2s_obj[i2s_num]->rx), "rx NULL", ESP_ERR_INVALID_ARG); - xSemaphoreTake (p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG); + I2S_CHECK((size < SOC_I2S_MAX_BUFFER_SIZE), "size is too large", + ESP_ERR_INVALID_ARG); + I2S_CHECK((p_i2s_obj[i2s_num]->rx), "rx NULL", ESP_ERR_INVALID_ARG); + xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE - esp_pm_lock_acquire (p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_acquire(p_i2s_obj[i2s_num]->pm_lock); #endif - while (size > 0) - { - if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size - || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) - { - if (xQueueReceive (p_i2s_obj[i2s_num]->rx->queue, - &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) - == pdFALSE) - { - break; - } - p_i2s_obj[i2s_num]->rx->rw_pos = 0; - } - data_ptr = (char *)p_i2s_obj[i2s_num]->rx->curr_ptr; - data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; - bytes_can_read - = p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; - if (bytes_can_read > (int)size) - { - bytes_can_read = size; - } - memcpy (dest_byte, data_ptr, bytes_can_read); - size -= bytes_can_read; - dest_byte += bytes_can_read; - p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; - (*bytes_read) += bytes_can_read; + while (size > 0) { + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || + p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, + &p_i2s_obj[i2s_num]->rx->curr_ptr, + ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->rx->rw_pos = 0; } + data_ptr = (char *)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + bytes_can_read = + p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; + if (bytes_can_read > (int)size) { + bytes_can_read = size; + } + memcpy(dest_byte, data_ptr, bytes_can_read); + size -= bytes_can_read; + dest_byte += bytes_can_read; + p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; + (*bytes_read) += bytes_can_read; + } #ifdef CONFIG_PM_ENABLE - esp_pm_lock_release (p_i2s_obj[i2s_num]->pm_lock); + esp_pm_lock_release(p_i2s_obj[i2s_num]->pm_lock); #endif - xSemaphoreGive (p_i2s_obj[i2s_num]->rx->mux); + xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); return ESP_OK; } diff --git a/components/custom_driver/include/i2s.h b/components/custom_driver/include/i2s.h index e127bf2..c5628bc 100644 --- a/components/custom_driver/include/i2s.h +++ b/components/custom_driver/include/i2s.h @@ -29,350 +29,350 @@ #include "soc/soc_caps.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif -#define I2S_PIN_NO_CHANGE \ +#define I2S_PIN_NO_CHANGE \ (-1) /*!< Use in i2s_pin_config_t for pins which should not be changed */ - typedef intr_handle_t i2s_isr_handle_t; +typedef intr_handle_t i2s_isr_handle_t; - /** - * @brief Set I2S pin number - * - * @note - * The I2S peripheral output signals can be connected to multiple GPIO pads. - * However, the I2S peripheral input signal can only be connected to one GPIO - * pad. - * - * @param i2s_num I2S_NUM_0 or I2S_NUM_1 - * - * @param pin I2S Pin structure, or NULL to set 2-channel 8-bit - * internal DAC pin configuration (GPIO25 & GPIO26) - * - * Inside the pin configuration structure, set I2S_PIN_NO_CHANGE for any pin - * where the current configuration should not be changed. - * - * @note if *pin is set as NULL, this function will initialize both of the - * built-in DAC channels by default. if you don't want this to happen and you - * want to initialize only one of the DAC channels, you can call - * i2s_set_dac_mode instead. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_FAIL IO error - */ - esp_err_t i2s_custom_set_pin (i2s_port_t i2s_num, - const i2s_pin_config_t *pin); +/** + * @brief Set I2S pin number + * + * @note + * The I2S peripheral output signals can be connected to multiple GPIO pads. + * However, the I2S peripheral input signal can only be connected to one GPIO + * pad. + * + * @param i2s_num I2S_NUM_0 or I2S_NUM_1 + * + * @param pin I2S Pin structure, or NULL to set 2-channel 8-bit + * internal DAC pin configuration (GPIO25 & GPIO26) + * + * Inside the pin configuration structure, set I2S_PIN_NO_CHANGE for any pin + * where the current configuration should not be changed. + * + * @note if *pin is set as NULL, this function will initialize both of the + * built-in DAC channels by default. if you don't want this to happen and you + * want to initialize only one of the DAC channels, you can call + * i2s_set_dac_mode instead. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL IO error + */ +esp_err_t i2s_custom_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin); #if SOC_I2S_SUPPORTS_PDM - /** - * @brief Set PDM mode down-sample rate - * In PDM RX mode, there would be 2 rounds of downsample process in - * hardware. In the first downsample process, the sampling number can be 16 - * or 8. In the second downsample process, the sampling number is fixed as 8. - * So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm - * * 128) accordingly. - * @param i2s_num I2S_NUM_0, I2S_NUM_1 - * @param dsr i2s RX down sample rate for PDM mode. - * - * @note After calling this function, it would call i2s_set_clk inside to - * update the clock frequency. Please call this function after I2S driver has - * been initialized. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_ERR_NO_MEM Out of memory - */ - esp_err_t i2s_custom_set_pdm_rx_down_sample (i2s_port_t i2s_num, - i2s_pdm_dsr_t dsr); +/** + * @brief Set PDM mode down-sample rate + * In PDM RX mode, there would be 2 rounds of downsample process in + * hardware. In the first downsample process, the sampling number can be 16 + * or 8. In the second downsample process, the sampling number is fixed as 8. + * So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm + * * 128) accordingly. + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param dsr i2s RX down sample rate for PDM mode. + * + * @note After calling this function, it would call i2s_set_clk inside to + * update the clock frequency. Please call this function after I2S driver has + * been initialized. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ +esp_err_t i2s_custom_set_pdm_rx_down_sample(i2s_port_t i2s_num, + i2s_pdm_dsr_t dsr); #endif - /** - * @brief Set I2S dac mode, I2S built-in DAC is disabled by default - * - * @param dac_mode DAC mode configurations - see i2s_dac_mode_t - * - * @note Built-in DAC functions are only supported on I2S0 for current ESP32 - * chip. If either of the built-in DAC channel are enabled, the other one can - * not be used as RTC DAC function at the same time. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ - esp_err_t i2s_custom_set_dac_mode (i2s_dac_mode_t dac_mode); +/** + * @brief Set I2S dac mode, I2S built-in DAC is disabled by default + * + * @param dac_mode DAC mode configurations - see i2s_dac_mode_t + * + * @note Built-in DAC functions are only supported on I2S0 for current ESP32 + * chip. If either of the built-in DAC channel are enabled, the other one can + * not be used as RTC DAC function at the same time. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_custom_set_dac_mode(i2s_dac_mode_t dac_mode); - /** - * @brief Install and start I2S driver. - * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 - * - * @param i2s_config I2S configurations - see i2s_config_t struct - * - * @param queue_size I2S event queue size/depth. - * - * @param i2s_queue I2S event queue handle, if set NULL, driver will - * not use an event queue. - * - * This function must be called before any I2S driver read/write operations. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - * - ESP_ERR_NO_MEM Out of memory - */ - esp_err_t i2s_custom_driver_install (i2s_port_t i2s_num, - const i2s_config_t *i2s_config, - int queue_size, void *i2s_queue); +/** + * @brief Install and start I2S driver. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param i2s_config I2S configurations - see i2s_config_t struct + * + * @param queue_size I2S event queue size/depth. + * + * @param i2s_queue I2S event queue handle, if set NULL, driver will + * not use an event queue. + * + * This function must be called before any I2S driver read/write operations. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ +esp_err_t i2s_custom_driver_install(i2s_port_t i2s_num, + const i2s_config_t *i2s_config, + int queue_size, void *i2s_queue); - /** - * @brief Uninstall I2S driver. - * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ - esp_err_t i2s_custom_driver_uninstall (i2s_port_t i2s_num); +/** + * @brief Uninstall I2S driver. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_custom_driver_uninstall(i2s_port_t i2s_num); - /** - * @brief Write data to I2S DMA transmit buffer. - * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 - * - * @param src Source address to write from - * - * @param size Size of data in bytes - * - * @param[out] bytes_written Number of bytes written, if timeout, the result - * will be less than the size passed in. - * - * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this - * many ticks pass without space becoming available in the DMA - * transmit buffer, then the function will return (note that if the - * data is written to the DMA buffer in pieces, the overall operation - * may still take longer than this timeout.) Pass portMAX_DELAY for no - * timeout. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ - esp_err_t i2s_custom_write (i2s_port_t i2s_num, const void *src, size_t size, - size_t *bytes_written, TickType_t ticks_to_wait); +/** + * @brief Write data to I2S DMA transmit buffer. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param src Source address to write from + * + * @param size Size of data in bytes + * + * @param[out] bytes_written Number of bytes written, if timeout, the result + * will be less than the size passed in. + * + * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this + * many ticks pass without space becoming available in the DMA + * transmit buffer, then the function will return (note that if the + * data is written to the DMA buffer in pieces, the overall operation + * may still take longer than this timeout.) Pass portMAX_DELAY for no + * timeout. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_custom_write(i2s_port_t i2s_num, const void *src, size_t size, + size_t *bytes_written, TickType_t ticks_to_wait); - /** - * @brief Write data to I2S DMA transmit buffer while expanding the number of - * bits per sample. For example, expanding 16-bit PCM to 32-bit PCM. - * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 - * - * @param src Source address to write from - * - * @param size Size of data in bytes - * - * @param src_bits Source audio bit - * - * @param aim_bits Bit wanted, no more than 32, and must be - * greater than src_bits - * - * @param[out] bytes_written Number of bytes written, if timeout, the result - * will be less than the size passed in. - * - * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this - * many ticks pass without space becoming available in the DMA - * transmit buffer, then the function will return (note that if the - * data is written to the DMA buffer in pieces, the overall operation - * may still take longer than this timeout.) Pass portMAX_DELAY for no - * timeout. - * - * Format of the data in source buffer is determined by the I2S - * configuration (see i2s_config_t). - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ - esp_err_t i2s_custom_write_expand (i2s_port_t i2s_num, const void *src, - size_t size, size_t src_bits, - size_t aim_bits, size_t *bytes_written, - TickType_t ticks_to_wait); +/** + * @brief Write data to I2S DMA transmit buffer while expanding the number of + * bits per sample. For example, expanding 16-bit PCM to 32-bit PCM. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param src Source address to write from + * + * @param size Size of data in bytes + * + * @param src_bits Source audio bit + * + * @param aim_bits Bit wanted, no more than 32, and must be + * greater than src_bits + * + * @param[out] bytes_written Number of bytes written, if timeout, the result + * will be less than the size passed in. + * + * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this + * many ticks pass without space becoming available in the DMA + * transmit buffer, then the function will return (note that if the + * data is written to the DMA buffer in pieces, the overall operation + * may still take longer than this timeout.) Pass portMAX_DELAY for no + * timeout. + * + * Format of the data in source buffer is determined by the I2S + * configuration (see i2s_config_t). + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_custom_write_expand(i2s_port_t i2s_num, const void *src, + size_t size, size_t src_bits, size_t aim_bits, + size_t *bytes_written, + TickType_t ticks_to_wait); - /** - * @brief Read data from I2S DMA receive buffer - * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 - * - * @param dest Destination address to read into - * - * @param size Size of data in bytes - * - * @param[out] bytes_read Number of bytes read, if timeout, bytes read will - * be less than the size passed in. - * - * @param ticks_to_wait RX buffer wait timeout in RTOS ticks. If this many - * ticks pass without bytes becoming available in the DMA receive buffer, - * then the function will return (note that if data is read from the DMA - * buffer in pieces, the overall operation may still take longer than this - * timeout.) Pass portMAX_DELAY for no timeout. - * - * @note If the built-in ADC mode is enabled, we should call i2s_adc_enable - * and i2s_adc_disable around the whole reading process, to prevent the data - * getting corrupted. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Parameter error - */ - esp_err_t i2s_custom_read (i2s_port_t i2s_num, void *dest, size_t size, - size_t *bytes_read, TickType_t ticks_to_wait); +/** + * @brief Read data from I2S DMA receive buffer + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param dest Destination address to read into + * + * @param size Size of data in bytes + * + * @param[out] bytes_read Number of bytes read, if timeout, bytes read will + * be less than the size passed in. + * + * @param ticks_to_wait RX buffer wait timeout in RTOS ticks. If this many + * ticks pass without bytes becoming available in the DMA receive buffer, + * then the function will return (note that if data is read from the DMA + * buffer in pieces, the overall operation may still take longer than this + * timeout.) Pass portMAX_DELAY for no timeout. + * + * @note If the built-in ADC mode is enabled, we should call i2s_adc_enable + * and i2s_adc_disable around the whole reading process, to prevent the data + * getting corrupted. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_custom_read(i2s_port_t i2s_num, void *dest, size_t size, + size_t *bytes_read, TickType_t ticks_to_wait); - /** - * @brief APLL calculate function, was described by following: - * APLL Output frequency is given by the formula: - * - * apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + - * sdm0/65536)/((o_div + 2) * 2) apll_freq = fout / ((o_div + 2) * 2) - * - * The dividend in this expression should be in the range of 240 - - * 600 MHz. In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0. - * * sdm0 frequency adjustment parameter, 0..255 - * * sdm1 frequency adjustment parameter, 0..255 - * * sdm2 frequency adjustment parameter, 0..63 - * * o_div frequency divider, 0..31 - * - * The most accurate way to find the sdm0..2 and odir parameters - * is to loop through them all, then apply the above formula, finding the - * closest frequency to the desired one. But 256*256*64*32 = 134.217.728 - * loops are too slow with ESP32 - * 1. We will choose the parameters with the highest level of - * change, With 350MHz + +#define SYNC_TASK_PRIORITY 10 //(configMAX_PRIORITIES - 1) +#define SYNC_TASK_CORE_ID tskNO_AFFINITY // 1 // tskNO_AFFINITY static const char *TAG = "PLAYER"; @@ -2250,32 +2252,38 @@ static void player_task(void *pvParameters) { adjust_apll(0); // reset to normal playback speed - fragment = chnk->fragment; - p_payload = fragment->payload; - size = fragment->size; - - i2s_custom_init_dma_tx_queues(I2S_NUM_0, (uint8_t *)p_payload, size, - &written); - size -= written; - p_payload += written; - - // ESP_LOGE(TAG, "wrote %d", written); - - if (size == 0) { - if (fragment->nextFragment != NULL) { - fragment = fragment->nextFragment; - p_payload = fragment->payload; - size = fragment->size; - } else { - free_pcm_chunk(chnk); - chnk = NULL; + uint32_t currentDescriptor = 0, currentDescriptorOffset = 0; + uint32_t tmpCnt = CHNK_CTRL_CNT; + while (tmpCnt) { + if (chnk == NULL) { + if (pcmChkQHdl != NULL) { + ret = xQueueReceive(pcmChkQHdl, &chnk, portMAX_DELAY); + } } - } - // TCP_STATS_DISPLAY(); - // IP_STATS_DISPLAY(); - // MEM_STATS_DISPLAY(); - // LINK_STATS_DISPLAY(); + fragment = chnk->fragment; + p_payload = fragment->payload; + size = fragment->size; + + i2s_custom_init_dma_tx_queues(I2S_NUM_0, (uint8_t *)p_payload, size, + &written, ¤tDescriptor, + ¤tDescriptorOffset); + size -= written; + p_payload += written; + + if (size == 0) { + if (fragment->nextFragment != NULL) { + fragment = fragment->nextFragment; + p_payload = fragment->payload; + size = fragment->size; + } else { + free_pcm_chunk(chnk); + chnk = NULL; + } + } + + tmpCnt--; + } // Wait to be notified of a timer interrupt. xTaskNotifyWait(pdFALSE, // Don't clear bits on entry. @@ -2340,6 +2348,19 @@ static void player_task(void *pvParameters) { chnk = NULL; } + // get count of chunks we are late for + uint32_t c = ceil((float)age / (float)chkDur_us); // round up + // now clear all those chunks which are probably late too + while (c--) { + ret = xQueueReceive(pcmChkQHdl, &chnk, pdMS_TO_TICKS(1)); + if (ret == pdPASS) { + free_pcm_chunk(chnk); + chnk = NULL; + } else { + break; + } + } + int64_t t; get_diff_to_server(&t); @@ -2383,10 +2404,12 @@ static void player_task(void *pvParameters) { // if ((avg < -hardResyncThreshold) // || (avg > hardResyncThreshold) || // (initialSync == 0)) - if ((initialSync == 0) || (uxQueueMessagesWaiting(pcmChkQHdl) == - 0)) // only resync if we are getting late. - // hopefully being early will get ok - // through apll speed control + + // only resync if we are getting late. + // hopefully being early will get ok + // through apll speed control + if ((initialSync == 0) || (uxQueueMessagesWaiting(pcmChkQHdl) == 0) || + (avg > hardResyncThreshold)) // if ((avg > hardResyncThreshold) || (initialSync == 0)) // // only resync if we are getting late. hopefully being // early will get ok through apll speed control @@ -2396,6 +2419,19 @@ static void player_task(void *pvParameters) { chnk = NULL; } + // get count of chunks we are late for + uint32_t c = ceil((float)age / (float)chkDur_us); // round up + // now clear all those chunks which are probably late too + while (c--) { + ret = xQueueReceive(pcmChkQHdl, &chnk, pdMS_TO_TICKS(1)); + if (ret == pdPASS) { + free_pcm_chunk(chnk); + chnk = NULL; + } else { + break; + } + } + int64_t t; get_diff_to_server(&t); @@ -2428,8 +2464,8 @@ static void player_task(void *pvParameters) { } // clang-format off -// int64_t t; -// get_diff_to_server (&t); + int64_t t; + get_diff_to_server (&t); // // struct timeval now; // // get current time @@ -2469,7 +2505,7 @@ static void player_task(void *pvParameters) { // heap_caps_get_largest_free_block // (MALLOC_CAP_32BIT | MALLOC_CAP_EXEC)); - // ESP_LOGI (TAG, "%d, %lldus, %lldus %lldus", dir, age, avg, t); +// ESP_LOGI (TAG, "%d, %lldus, %lldus %lldus", dir, age, avg, t); // ESP_LOGI (TAG, "%d %lldus, %d", dir, avg, uxQueueMessagesWaiting(pcmChkQHdl)); diff --git a/main/main.c b/main/main.c index f52a8de..b0cd397 100644 --- a/main/main.c +++ b/main/main.c @@ -75,13 +75,13 @@ SemaphoreHandle_t decoderWriteSemaphore = NULL; const char *VERSION_STRING = "0.0.2"; #define HTTP_TASK_PRIORITY 6 -#define HTTP_TASK_CORE_ID 1 // tskNO_AFFINITY +#define HTTP_TASK_CORE_ID tskNO_AFFINITY // 1 // tskNO_AFFINITY #define OTA_TASK_PRIORITY 6 -#define OTA_TASK_CORE_ID 1 // tskNO_AFFINITY +#define OTA_TASK_CORE_ID tskNO_AFFINITY // 1 // tskNO_AFFINITY -#define FLAC_TASK_PRIORITY 6 -#define FLAC_TASK_CORE_ID 1 // tskNO_AFFINITY +#define FLAC_TASK_PRIORITY 8 +#define FLAC_TASK_CORE_ID tskNO_AFFINITY // 1 // tskNO_AFFINITY xTaskHandle t_ota_task = NULL; xTaskHandle t_http_get_task = NULL; @@ -117,20 +117,88 @@ typedef struct flacData_s { uint32_t bytes; } flacData_t; +void time_sync_msg_cb(void *args); + +static char base_message_serialized[BASE_MESSAGE_SIZE]; +static char time_message_serialized[TIME_MESSAGE_SIZE]; +static const esp_timer_create_args_t tSyncArgs = {.callback = &time_sync_msg_cb, + .name = "tSyncMsg"}; + +struct netconn *lwipNetconn; + +static int id_counter = 0; /** * */ void time_sync_msg_cb(void *args) { - BaseType_t xHigherPriorityTaskWoken; + base_message_t base_message_tx; + struct timeval now; + int result; + time_message_t time_message_tx = {{0, 0}}; + int rc1 = ERR_OK; // causes kernel panic, which shouldn't happen though? // Isn't it called from timer task instead of ISR? // xSemaphoreGive(timeSyncSemaphoreHandle); - xSemaphoreGiveFromISR(timeSyncSemaphoreHandle, &xHigherPriorityTaskWoken); - if (xHigherPriorityTaskWoken) { - portYIELD_FROM_ISR(); + result = gettimeofday(&now, NULL); + // ESP_LOGI(TAG, "time of day: %ld %ld", now.tv_sec, + // now.tv_usec); + if (result) { + ESP_LOGI(TAG, "Failed to gettimeofday"); + + return; } + + base_message_tx.type = SNAPCAST_MESSAGE_TIME; + base_message_tx.id = id_counter++; + base_message_tx.refersTo = 0; + base_message_tx.received.sec = 0; + base_message_tx.received.usec = 0; + base_message_tx.sent.sec = now.tv_sec; + base_message_tx.sent.usec = now.tv_usec; + base_message_tx.size = TIME_MESSAGE_SIZE; + + result = base_message_serialize(&base_message_tx, base_message_serialized, + BASE_MESSAGE_SIZE); + if (result) { + ESP_LOGE(TAG, "Failed to serialize base message for time"); + + return; + } + + memset(&time_message_tx, 0, sizeof(time_message_tx)); + + result = time_message_serialize(&time_message_tx, time_message_serialized, + TIME_MESSAGE_SIZE); + if (result) { + ESP_LOGI(TAG, "Failed to serialize time message"); + + return; + } + + rc1 = netconn_write(lwipNetconn, base_message_serialized, BASE_MESSAGE_SIZE, + NETCONN_NOCOPY); + if (rc1 != ERR_OK) { + ESP_LOGW(TAG, "error writing timesync base msg"); + + return; + } + + rc1 = netconn_write(lwipNetconn, time_message_serialized, TIME_MESSAGE_SIZE, + NETCONN_NOCOPY); + if (rc1 != ERR_OK) { + ESP_LOGW(TAG, "error writing timesync msg"); + + return; + } + + // ESP_LOGI(TAG, "%s: sent time sync message", __func__); + + // xSemaphoreGiveFromISR(timeSyncSemaphoreHandle, &xHigherPriorityTaskWoken); + // if (xHigherPriorityTaskWoken) { + // portYIELD_FROM_ISR(); + // } } static FLAC__StreamDecoderReadStatus read_callback( @@ -354,13 +422,6 @@ static void flac_decoder_task(void *pvParameters) { } } -static char base_message_serialized[BASE_MESSAGE_SIZE]; -static char time_message_serialized[TIME_MESSAGE_SIZE]; -static const esp_timer_create_args_t tSyncArgs = {.callback = &time_sync_msg_cb, - .name = "tSyncMsg"}; - -struct netconn *lwipNetconn; - /** * */ @@ -373,7 +434,7 @@ static void http_get_task(void *pvParameters) { hello_message_t hello_message; wire_chunk_message_t wire_chnk = {{0, 0}, 0, NULL}; char *hello_message_serialized = NULL; - int result, size, id_counter; + int result, size; //, id_counter; struct timeval now, trx, tdif, ttx; time_message_t time_message_rx = {{0, 0}}; time_message_t time_message_tx = {{0, 0}}; @@ -405,13 +466,14 @@ static void http_get_task(void *pvParameters) { struct netbuf *firstNetBuf = NULL; struct netbuf *newNetBuf = NULL; uint16_t len; + uint64_t timeout = 100000; // create a timer to send time sync messages every x µs esp_timer_create(&tSyncArgs, &timeSyncMessageTimer); - timeSyncSemaphoreHandle = xSemaphoreCreateMutex(); - xSemaphoreGive(timeSyncSemaphoreHandle); + // timeSyncSemaphoreHandle = xSemaphoreCreateMutex(); + // xSemaphoreGive(timeSyncSemaphoreHandle); - id_counter = 0; + // id_counter = 0; #if CONFIG_SNAPCLIENT_USE_MDNS ESP_LOGI(TAG, "Enable mdns"); @@ -423,11 +485,28 @@ static void http_get_task(void *pvParameters) { ESP_LOGE(TAG, "reset_diff_buffer: couldn't reset median filter long. STOP"); + timeout = 100000; + + esp_timer_stop(timeSyncMessageTimer); + if (received_header == true) { + if (!esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_start_periodic(timeSyncMessageTimer, timeout); + } + + if ((latency_buffer_full() > 0) && (timeout < 1000000)) { + if (esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_stop(timeSyncMessageTimer); + } + + esp_timer_start_periodic(timeSyncMessageTimer, timeout); + } + } + return; } esp_timer_stop(timeSyncMessageTimer); - xSemaphoreGive(timeSyncSemaphoreHandle); + // xSemaphoreGive(timeSyncSemaphoreHandle); if (opusDecoder != NULL) { opus_decoder_destroy(opusDecoder); @@ -1634,6 +1713,21 @@ static void http_get_task(void *pvParameters) { internalState = 0; received_header = true; + esp_timer_stop(timeSyncMessageTimer); + if (!esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_start_periodic(timeSyncMessageTimer, + timeout); + } + + if ((latency_buffer_full() > 0) && + (timeout < 1000000)) { + if (esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_stop(timeSyncMessageTimer); + } + + esp_timer_start_periodic(timeSyncMessageTimer, + timeout); + } } break; @@ -2096,6 +2190,26 @@ static void http_get_task(void *pvParameters) { "Clearing time buffer"); reset_latency_buffer(); + + timeout = 100000; + + esp_timer_stop(timeSyncMessageTimer); + if (received_header == true) { + if (!esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_start_periodic(timeSyncMessageTimer, + timeout); + } + + if ((latency_buffer_full() > 0) && + (timeout < 1000000)) { + if (esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_stop(timeSyncMessageTimer); + } + + esp_timer_start_periodic(timeSyncMessageTimer, + timeout); + } + } } newValue = @@ -2103,22 +2217,24 @@ static void http_get_task(void *pvParameters) { (int64_t)tmpDiffToServer.tv_usec); player_latency_insert(newValue); - // ESP_LOGE(TAG, - // "latency %lld", - // newValue); + // ESP_LOGE(TAG, "latency %lld", newValue); // store current time lastTimeSync.tv_sec = now.tv_sec; lastTimeSync.tv_usec = now.tv_usec; - if (xSemaphoreTake(timeSyncSemaphoreHandle, 0) == - pdTRUE) { - ESP_LOGW(TAG, - "couldn't take " - "timeSyncSemaphoreHandle"); - } + // if + // (xSemaphoreTake(timeSyncSemaphoreHandle, + // 0) == + // pdTRUE) { + // ESP_LOGW(TAG, + // "couldn't take + // " + // "timeSyncSemaphoreHandle"); + // } - uint64_t timeout; + /* + //uint64_t timeout; if (latency_buffer_full() > 0) { // we give // timeSyncSemaphoreHandle after @@ -2131,15 +2247,43 @@ static void http_get_task(void *pvParameters) { // reset_latency_buffer() was // called timeout = 1000000; - } else { + + ESP_LOGE(TAG, "full %lld", timeout); + } + else { // Do a initial time sync with // the server at boot we need to // fill diffBuff fast so we get a // good estimate of latency timeout = 100000; + + ESP_LOGE(TAG, "empty %lld", timeout); + } + */ + + if (received_header == true) { + if (!esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_start_periodic(timeSyncMessageTimer, + timeout); + } + + if ((latency_buffer_full() > 0) && + (timeout < 1000000)) { + timeout = 1000000; + + ESP_LOGI(TAG, "latency buffer full"); + + if (esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_stop(timeSyncMessageTimer); + } + + esp_timer_start_periodic(timeSyncMessageTimer, + timeout); + } } - esp_timer_start_once(timeSyncMessageTimer, timeout); + // esp_timer_start_once(timeSyncMessageTimer, + // timeout); } } else { ESP_LOGE(TAG, @@ -2212,6 +2356,23 @@ static void http_get_task(void *pvParameters) { break; } + /* + if (received_header == true) { + if (!esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_start_periodic(timeSyncMessageTimer, timeout); + } + + if ((latency_buffer_full() > 0) && (timeout < 1000000)) { + if (esp_timer_is_active(timeSyncMessageTimer)) { + esp_timer_stop(timeSyncMessageTimer); + } + + esp_timer_start_periodic(timeSyncMessageTimer, timeout); + } + } + */ + + /* if (received_header == true) { if (xSemaphoreTake(timeSyncSemaphoreHandle, 0) == pdTRUE) { result = gettimeofday(&now, NULL); @@ -2264,6 +2425,7 @@ static void http_get_task(void *pvParameters) { } } } + */ } } } @@ -2271,989 +2433,6 @@ static void http_get_task(void *pvParameters) { /** * */ -static void http_get_task_backup(void *pvParameters) { - struct sockaddr_in servaddr; - char *start; - int sock = -1; - base_message_t base_message; - hello_message_t hello_message; - char *hello_message_serialized = NULL; - int result, size, id_counter; - struct timeval now, trx, tdif, ttx; - time_message_t time_message; - struct timeval tmpDiffToServer; - struct timeval lastTimeSync = {0, 0}; - esp_timer_handle_t timeSyncMessageTimer = NULL; - int16_t frameSize = 960; // 960*2: 20ms, 960*1: 10ms - int16_t *audio = NULL; - int16_t pcm_size = 120; - uint16_t channels; - esp_err_t err = 0; - codec_header_message_t codec_header_message; - server_settings_message_t server_settings_message; - bool received_header = false; - mdns_result_t *r; - OpusDecoder *opusDecoder = NULL; - codec_type_t codec = NONE; - snapcastSetting_t scSet; - flacData_t flacData = {NULL, NULL, 0}; - flacData_t *pFlacData; - char *typedMsg = NULL; - uint32_t lastTypedMsgSize = 0; - - // create a timer to send time sync messages every x µs - esp_timer_create(&tSyncArgs, &timeSyncMessageTimer); - timeSyncSemaphoreHandle = xSemaphoreCreateMutex(); - xSemaphoreGive(timeSyncSemaphoreHandle); - - id_counter = 0; - - while (1) { - if (reset_latency_buffer() < 0) { - ESP_LOGE(TAG, - "reset_diff_buffer: couldn't reset median filter long. STOP"); - - return; - } - - esp_timer_stop(timeSyncMessageTimer); - xSemaphoreGive(timeSyncSemaphoreHandle); - - if (opusDecoder != NULL) { - opus_decoder_destroy(opusDecoder); - opusDecoder = NULL; - } - - if (t_flac_decoder_task != NULL) { - vTaskDelete(t_flac_decoder_task); - t_flac_decoder_task = NULL; - } - - if (flacDecoder != NULL) { - FLAC__stream_decoder_finish(flacDecoder); - FLAC__stream_decoder_delete(flacDecoder); - flacDecoder = NULL; - } - - if (decoderWriteQHdl != NULL) { - vQueueDelete(decoderWriteQHdl); - decoderWriteQHdl = NULL; - } - - if (decoderReadQHdl != NULL) { - vQueueDelete(decoderReadQHdl); - decoderReadQHdl = NULL; - } - -#if SNAPCAST_SERVER_USE_MDNS - // Find snapcast server - // Connect to first snapcast server found - r = NULL; - err = 0; - while (!r || err) { - ESP_LOGI(TAG, "Lookup snapcast service on network"); - esp_err_t err = mdns_query_ptr("_snapcast", "_tcp", 3000, 20, &r); - if (err) { - ESP_LOGE(TAG, "Query Failed"); - } - - if (!r) { - ESP_LOGW(TAG, "No results found!"); - } - - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - - char serverAddr[] = "255.255.255.255"; - ESP_LOGI(TAG, "Found %s:%d", - inet_ntop(AF_INET, &(r->addr->addr.u_addr.ip4.addr), serverAddr, - sizeof(serverAddr)), - r->port); - - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = r->addr->addr.u_addr.ip4.addr; - servaddr.sin_port = htons(r->port); - mdns_query_results_free(r); -#else - // configure a failsafe snapserver according to CONFIG values - servaddr.sin_family = AF_INET; - inet_pton(AF_INET, SNAPCAST_SERVER_HOST, &(servaddr.sin_addr.s_addr)); - servaddr.sin_port = htons(SNAPCAST_SERVER_PORT); -#endif - ESP_LOGI(TAG, "allocate socket"); - sock = socket(AF_INET, SOCK_STREAM, 0); - - if (sock < 0) { - ESP_LOGE(TAG, "... Failed to allocate socket."); - vTaskDelay(1000 / portTICK_PERIOD_MS); - continue; - } - ESP_LOGI(TAG, "... allocated socket %d", sock); - - ESP_LOGI(TAG, "connect to socket"); - err = - connect(sock, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)); - if (err < 0) { - ESP_LOGE(TAG, "%s, %d", strerror(errno), errno); - - shutdown(sock, 2); - closesocket(sock); - - vTaskDelay(4000 / portTICK_PERIOD_MS); - - continue; - } - - ESP_LOGI(TAG, "... connected"); - - result = gettimeofday(&now, NULL); - if (result) { - ESP_LOGI(TAG, "Failed to gettimeofday\r\n"); - return; - } - - received_header = false; - - // init base message - base_message.type = SNAPCAST_MESSAGE_HELLO; - base_message.id = 0x0000; - base_message.refersTo = 0x0000; - base_message.sent.sec = now.tv_sec; - base_message.sent.usec = now.tv_usec; - base_message.received.sec = 0; - base_message.received.usec = 0; - base_message.size = 0x00000000; - - // init hello message - hello_message.mac = mac_address; - hello_message.hostname = "ESP32-Caster"; - hello_message.version = (char *)VERSION_STRING; - hello_message.client_name = "libsnapcast"; - hello_message.os = "esp32"; - hello_message.arch = "xtensa"; - hello_message.instance = 1; - hello_message.id = mac_address; - hello_message.protocol_version = 2; - - if (hello_message_serialized == NULL) { - hello_message_serialized = hello_message_serialize( - &hello_message, (size_t *)&(base_message.size)); - if (!hello_message_serialized) { - ESP_LOGE(TAG, "Failed to serialize hello message\r\b"); - return; - } - } - - result = base_message_serialize(&base_message, base_message_serialized, - BASE_MESSAGE_SIZE); - if (result) { - ESP_LOGE(TAG, "Failed to serialize base message\r\n"); - return; - } - - result = send(sock, base_message_serialized, BASE_MESSAGE_SIZE, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing base msg to socket: %s", strerror(errno)); - - free(hello_message_serialized); - hello_message_serialized = NULL; - - shutdown(sock, 2); - closesocket(sock); - - continue; - } - - result = send(sock, hello_message_serialized, base_message.size, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing hello msg to socket: %s", strerror(errno)); - - free(hello_message_serialized); - hello_message_serialized = NULL; - - shutdown(sock, 2); - closesocket(sock); - - continue; - } - - free(hello_message_serialized); - hello_message_serialized = NULL; - - // init default setting - scSet.buf_ms = 0; - scSet.codec = NONE; - scSet.bits = 0; - scSet.ch = 0; - scSet.sr = 0; - scSet.chkDur_ms = 0; - scSet.volume = 0; - scSet.muted = true; - - lastTypedMsgSize = 0; - if (typedMsg) { - free(typedMsg); - typedMsg = NULL; - } - - uint64_t startTime, endTime; - - for (;;) { - // ESP_LOGW (TAG, "stack free: %d", - // uxTaskGetStackHighWaterMark(NULL)); - - size = 0; - result = 0; - while (size < BASE_MESSAGE_SIZE) { - result = recv(sock, &(base_message_serialized[size]), - BASE_MESSAGE_SIZE - size, 0); - if (result < 0) { - break; - } - size += result; - } - - if (result < 0) { - if (errno != 0) { - ESP_LOGW(TAG, "1: %s, %d", strerror(errno), (int)errno); - } - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - if (result > 0) { - result = gettimeofday(&now, NULL); - // ESP_LOGI(TAG, "time of day: %ld %ld", now.tv_sec, - // now.tv_usec); - if (result) { - ESP_LOGW(TAG, "Failed to gettimeofday"); - continue; - } - - result = base_message_deserialize(&base_message, - base_message_serialized, size); - if (result) { - ESP_LOGW(TAG, "Failed to read base message: %d", result); - continue; - } - - base_message.received.usec = now.tv_usec; - // ESP_LOGI(TAG,"%d %d : %d %d : %d - // %d",base_message.size, - // base_message.refersTo, - // base_message.sent.sec, - // base_message.sent.usec, - // base_message.received.sec, - // base_message.received.usec - // ); - - // TODO: ensure this buffer is freed before task gets deleted - size = 0; - if (lastTypedMsgSize < base_message.size) { - // typedMsg = (char *)heap_caps_realloc - // (typedMsg, base_message.size, MALLOC_CAP_8BIT); - // if (typedMsg == NULL) - { - // ESP_LOGI (TAG, "get memory - // for typed message %d, %d, %d", - // base_message.size, - // heap_caps_get_free_size(MALLOC_CAP_8BIT), - // heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); - typedMsg = (char *)heap_caps_realloc(typedMsg, base_message.size, - MALLOC_CAP_8BIT); - - // typedMsg = (char - // *)heap_caps_malloc (base_message.size, - // MALLOC_CAP_8BIT); - if (typedMsg == NULL) { - ESP_LOGE(TAG, "Couldn't get memory for typed message %d, %d, %d", - base_message.size, - heap_caps_get_free_size(MALLOC_CAP_8BIT), - heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); - - // dummy read next data to a char variable without - // incrementing and drop it (base_message.size) - char dummy; - start = &dummy; - while (size < base_message.size) { - result = recv(sock, start, 1, 0); - if (result < 0) { - ESP_LOGW(TAG, "Failed to read from server: %d", result); - - break; - } - - size++; - } - - continue; - } else { - lastTypedMsgSize = base_message.size; - } - } - } - start = typedMsg; - - while (size < base_message.size) { - result = recv(sock, &(start[size]), base_message.size - size, 0); - if (result < 0) { - ESP_LOGW(TAG, "Failed to read from server: %d", result); - - break; - } - - size += result; - } - - if (result < 0) { - if (errno != 0) { - ESP_LOGI(TAG, "2: %s, %d", strerror(errno), (int)errno); - } - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - switch (base_message.type) { - case SNAPCAST_MESSAGE_CODEC_HEADER: - result = codec_header_message_deserialize(&codec_header_message, - start, size); - if (result) { - ESP_LOGI(TAG, "Failed to read codec header: %d", result); - return; - } - - size = codec_header_message.size; - start = codec_header_message.payload; - - if (opusDecoder != NULL) { - opus_decoder_destroy(opusDecoder); - opusDecoder = NULL; - } - - if (t_flac_decoder_task != NULL) { - vTaskDelete(t_flac_decoder_task); - t_flac_decoder_task = NULL; - } - - if (flacDecoder != NULL) { - FLAC__stream_decoder_finish(flacDecoder); - FLAC__stream_decoder_delete(flacDecoder); - flacDecoder = NULL; - } - - if (decoderWriteQHdl != NULL) { - vQueueDelete(decoderWriteQHdl); - decoderWriteQHdl = NULL; - } - - if (decoderReadQHdl != NULL) { - vQueueDelete(decoderReadQHdl); - decoderReadQHdl = NULL; - } - - // ESP_LOGI(TAG, "Received codec header message with size - // %d", codec_header_message.size); - - if (strcmp(codec_header_message.codec, "opus") == 0) { - uint32_t rate; - memcpy(&rate, start + 4, sizeof(rate)); - uint16_t bits; - memcpy(&bits, start + 8, sizeof(bits)); - memcpy(&channels, start + 10, sizeof(channels)); - ESP_LOGI(TAG, "%s sampleformat: %d:%d:%d", - codec_header_message.codec, rate, bits, channels); - - if (audio != NULL) { - free(audio); - audio = NULL; - } - - if (flacData.outData != NULL) { - free(flacData.outData); - flacData.outData = NULL; - } - - int error = 0; - opusDecoder = opus_decoder_create(rate, channels, &error); - if (error != 0) { - ESP_LOGE(TAG, "Failed to init %s decoder", - codec_header_message.codec); - return; - } else { - ESP_LOGI(TAG, "Initialized %s decoder", - codec_header_message.codec); - - codec = OPUS; - - scSet.codec = codec; - scSet.bits = bits; - scSet.ch = channels; - scSet.sr = rate; - } - } else if (strcmp(codec_header_message.codec, "flac") == 0) { - codec = FLAC; - - if (t_flac_decoder_task == NULL) { - xTaskCreatePinnedToCore(&flac_decoder_task, "flac_decoder_task", - 4 * 4096, &scSet, FLAC_TASK_PRIORITY, - &t_flac_decoder_task, - FLAC_TASK_CORE_ID); - } - - // check if audio buffer was previously allocated by some - // other codec this would happen if codec is changed - // while client was running - if (audio != NULL) { - free(audio); - audio = NULL; - } - - if (flacData.outData != NULL) { - free(flacData.outData); - flacData.outData = NULL; - } - - flacData.bytes = codec_header_message.size; - flacData.inData = codec_header_message.payload; - pFlacData = &flacData; - - // wait for task creation done - while (decoderReadQHdl == NULL) { - vTaskDelay(10); - } - - // send data to flac decoder - xQueueSend(decoderReadQHdl, &pFlacData, portMAX_DELAY); - // and wait until it is done - xQueueReceive(decoderWriteQHdl, &pFlacData, portMAX_DELAY); - - ESP_LOGI(TAG, "%s sampleformat: %d:%d:%d", - codec_header_message.codec, scSet.sr, scSet.bits, - scSet.ch); - } else if (strcmp(codec_header_message.codec, "pcm") == 0) { - codec = PCM; - - if (audio != NULL) { - free(audio); - audio = NULL; - } - - if (flacData.outData != NULL) { - free(flacData.outData); - flacData.outData = NULL; - } - - memcpy(&channels, start + 22, sizeof(channels)); - uint32_t rate; - memcpy(&rate, start + 24, sizeof(rate)); - uint16_t bits; - memcpy(&bits, start + 34, sizeof(bits)); - - ESP_LOGI(TAG, "%s sampleformat: %d:%d:%d", - codec_header_message.codec, rate, bits, channels); - - scSet.codec = codec; - scSet.bits = bits; - scSet.ch = channels; - scSet.sr = rate; - } else { - codec = NONE; - - ESP_LOGI(TAG, "Codec : %s not supported", - codec_header_message.codec); - ESP_LOGI(TAG, - "Change encoder codec to opus / flac / pcm in " - "/etc/snapserver.conf " - "on server"); - return; - } - - trx.tv_sec = base_message.sent.sec; - trx.tv_usec = base_message.sent.usec; - // we do this, so uint32_t timvals won't overflow - // if e.g. raspberry server is off to far - settimeofday(&trx, NULL); - ESP_LOGI(TAG, "syncing clock to server %ld.%06ld", trx.tv_sec, - trx.tv_usec); - - codec_header_message_free(&codec_header_message); - - received_header = true; - - break; - - case SNAPCAST_MESSAGE_WIRE_CHUNK: { - if (!received_header) { - // if (typedMsg != NULL) - // { - // free (typedMsg); - // typedMsg = NULL; - // } - - continue; - } - - wire_chunk_message_t wire_chunk_message; - - result = wire_chunk_message_deserialize(&wire_chunk_message, start, - size); - if (result) { - ESP_LOGI(TAG, "Failed to read wire chunk: %d\r\n", result); - - wire_chunk_message_free(&wire_chunk_message); - break; - } - - // ESP_LOGI(TAG, "wire - // chnk with size:" - // "%d, timestamp %d.%d", - // wire_chunk_message.size, - // wire_chunk_message.timestamp.sec, - // wire_chunk_message.timestamp.usec); - - // store chunk's timestamp, decoder callback - // will need it later - tv_t timestamp; - timestamp = wire_chunk_message.timestamp; - - switch (codec) { - case OPUS: { - int frame_size = 0; - - if (audio == NULL) { -#if CONFIG_USE_PSRAM - audio = (int16_t *)heap_caps_malloc( - frameSize * scSet.ch * (scSet.bits / 8), - MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 960*2: 20ms, - // 960*1: 10ms -#else - audio = (int16_t *)malloc( - frameSize * scSet.ch * - (scSet.bits / 8)); // 960*2: 20ms, 960*1: 10ms -#endif - } - - if (audio == NULL) { - ESP_LOGE(TAG, - "Failed to allocate memory for " - "opus audio decoder"); - } else { - size = wire_chunk_message.size; - start = wire_chunk_message.payload; - - while ((frame_size = opus_decode( - opusDecoder, (unsigned char *)start, size, - (opus_int16 *)audio, pcm_size / channels, 0)) == - OPUS_BUFFER_TOO_SMALL) { - pcm_size = pcm_size * 2; - - // 960*2: 20ms, 960*1: 10ms -#if CONFIG_USE_PSRAM - audio = (int16_t *)heap_caps_realloc( - audio, pcm_size * scSet.ch * (scSet.bits / 8), - MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 2 channels + - // 2 Byte per - // sample == - // int32_t -#else - audio = (int16_t *)realloc( - audio, pcm_size * scSet.ch * (scSet.bits / 8)); - // audio = (int16_t, - // *)heap_caps_realloc( - // (int32_t - // *)audio, frameSize * - // CHANNELS * - // (BITS_PER_SAMPLE / 8), - // MALLOC_CAP_32BIT); -#endif - - ESP_LOGI(TAG, - "OPUS encoding buffer too small, " - "resizing to %d " - "samples per channel", - pcm_size / channels); - } - - if (frame_size < 0) { - ESP_LOGE(TAG, "Decode error : %d, %d, %s, %s, %d\n", - frame_size, size, start, (char *)audio, - pcm_size / channels); - - free(audio); - audio = NULL; - } else { - wire_chunk_message_t pcm_chunk_message; - - pcm_chunk_message.size = - frame_size * scSet.ch * (scSet.bits / 8); - pcm_chunk_message.payload = (char *)audio; - pcm_chunk_message.timestamp = timestamp; - - scSet.chkDur_ms = (1000UL * pcm_chunk_message.size) / - (uint32_t)(scSet.ch * (scSet.bits / 8)) / - scSet.sr; - if (player_send_snapcast_setting(&scSet) != pdPASS) { - ESP_LOGE(TAG, - "Failed to notify sync task about " - "codec. Did you init player?"); - - return; - } - -#if CONFIG_USE_DSP_PROCESSOR - dsp_setup_flow(500, scSet.sr, scSet.chkDur_ms); - dsp_processor(pcm_chunk_message.payload, - pcm_chunk_message.size, dspFlow); -#endif - - insert_pcm_chunk(&pcm_chunk_message); - } - } - - break; - } - - case FLAC: { - flacData.bytes = wire_chunk_message.size; - flacData.inData = wire_chunk_message.payload; - pFlacData = &flacData; - - // startTime = - // esp_timer_get_time (); - // send data to flac decoder - xQueueSend(decoderReadQHdl, &pFlacData, portMAX_DELAY); - // and wait until it is done - xQueueReceive(decoderWriteQHdl, &pFlacData, portMAX_DELAY); - // endTime = - // esp_timer_get_time (); - // ESP_LOGW(TAG, - //"%lld", endTime - startTime); - - wire_chunk_message_t pcm_chunk_message; - - pcm_chunk_message.size = flacData.bytes; - pcm_chunk_message.payload = flacData.outData; - pcm_chunk_message.timestamp = timestamp; - - scSet.chkDur_ms = (1000UL * pcm_chunk_message.size) / - (uint32_t)(scSet.ch * (scSet.bits / 8)) / - scSet.sr; - if (player_send_snapcast_setting(&scSet) != pdPASS) { - ESP_LOGE(TAG, - "Failed to notify sync task about " - "codec. Did you init player?"); - - return; - } - -#if CONFIG_USE_DSP_PROCESSOR - dsp_setup_flow(500, scSet.sr, scSet.chkDur_ms); - dsp_processor(pcm_chunk_message.payload, pcm_chunk_message.size, - dspFlow); -#endif - - // int ret; - // do { - // ret = - insert_pcm_chunk(&pcm_chunk_message); - // if (ret < 0) { - // vTaskDelay(10); - // } - // } while(ret != 0); - - break; - } - - case PCM: { - wire_chunk_message_t pcm_chunk_message; - - size = wire_chunk_message.size; - start = wire_chunk_message.payload; - - pcm_chunk_message.size = size; - pcm_chunk_message.timestamp = timestamp; - pcm_chunk_message.payload = wire_chunk_message.payload; - - scSet.chkDur_ms = (1000UL * pcm_chunk_message.size) / - (uint32_t)(scSet.ch * (scSet.bits / 8)) / - scSet.sr; - if (player_send_snapcast_setting(&scSet) != pdPASS) { - ESP_LOGE(TAG, - "Failed to notify sync task about " - "codec. Did you init player?"); - - return; - } - -#if CONFIG_USE_DSP_PROCESSOR - dsp_setup_flow(500, scSet.sr, scSet.chkDur_ms); - dsp_processor(pcm_chunk_message.payload, pcm_chunk_message.size, - dspFlow); -#endif - - insert_pcm_chunk(&pcm_chunk_message); - - break; - } - - default: { - ESP_LOGE(TAG, "Decoder not supported"); - - return; - - break; - } - } - - wire_chunk_message_free(&wire_chunk_message); - - break; - } - - case SNAPCAST_MESSAGE_SERVER_SETTINGS: - // The first 4 bytes in the buffer are the size of the - // string. We don't need this, so we'll shift the entire - // buffer over 4 bytes and use the extra room to add a null - // character so cJSON can pares it. - memmove(start, start + 4, size - 4); - start[size - 3] = '\0'; - result = server_settings_message_deserialize( - &server_settings_message, start); - if (result) { - ESP_LOGI(TAG, "Failed to read server settings: %d", result); - return; - } - // log mute state, buffer, latency - ESP_LOGI(TAG, "Buffer length: %d", - server_settings_message.buffer_ms); - ESP_LOGI(TAG, "Latency: %d", - server_settings_message.latency); - ESP_LOGI(TAG, "Mute: %d", server_settings_message.muted); - ESP_LOGI(TAG, "Setting volume: %d", server_settings_message.volume); - - // Volume setting using ADF HAL abstraction - if (scSet.muted != server_settings_message.muted) { - audio_hal_set_mute(board_handle->audio_hal, - server_settings_message.muted); - } - if (scSet.volume != server_settings_message.volume) { - audio_hal_set_volume(board_handle->audio_hal, - server_settings_message.volume); - } - - scSet.cDacLat_ms = server_settings_message.latency; - scSet.buf_ms = server_settings_message.buffer_ms; - scSet.muted = server_settings_message.muted; - scSet.volume = server_settings_message.volume; - - if (player_send_snapcast_setting(&scSet) != pdPASS) { - ESP_LOGE(TAG, "Failed to notify sync task. Did you init player?"); - - return; - } - - break; - - case SNAPCAST_MESSAGE_TIME: - result = time_message_deserialize(&time_message, start, size); - if (result) { - ESP_LOGI(TAG, "Failed to deserialize time message"); - - return; - } - - // ESP_LOGI(TAG, "BaseTX : %d %d ", - // base_message.sent.sec , - // base_message.sent.usec); ESP_LOGI(TAG, - //"BaseRX : %d %d ", - // base_message.received.sec , - // base_message.received.usec); ESP_LOGI(TAG, - // "baseTX->RX : %d s ", - // (base_message.received.sec - // - - // base_message.sent.sec)); ESP_LOGI(TAG, - // "baseTX->RX : %d ms ", - // (base_message.received.usec - - // base_message.sent.usec)/1000); - // ESP_LOGI(TAG, "Latency : %d.%d ", - // time_message.latency.sec, - // time_message.latency.usec/1000); - - // tv == server to client latency (s2c) - // time_message.latency == client to server latency(c2s) - // TODO the fact that I have to do this simple conversion - // means I should probably use the timeval struct instead of - // my own - trx.tv_sec = base_message.received.sec; - trx.tv_usec = base_message.received.usec; - ttx.tv_sec = base_message.sent.sec; - ttx.tv_usec = base_message.sent.usec; - timersub(&trx, &ttx, &tdif); - - trx.tv_sec = time_message.latency.sec; - trx.tv_usec = time_message.latency.usec; - - // trx == c2s: client to server - // tdif == s2c: server to client - // ESP_LOGI(TAG, "c2s: %ld %ld", - // trx.tv_sec, trx.tv_usec); ESP_LOGI(TAG, - // "s2c: %ld %ld", tdif.tv_sec, - // tdif.tv_usec); - - timersub(&trx, &tdif, &tmpDiffToServer); - if ((tmpDiffToServer.tv_sec / 2) == 0) { - tmpDiffToServer.tv_sec = 0; - tmpDiffToServer.tv_usec = - (suseconds_t)((int64_t)tmpDiffToServer.tv_sec * 1000000LL / - 2) + - (int64_t)tmpDiffToServer.tv_usec / 2; - } else { - tmpDiffToServer.tv_sec /= 2; - tmpDiffToServer.tv_usec /= 2; - } - - // ESP_LOGI(TAG, - // "Current latency: %ld.%06ld", - // tmpDiffToServer.tv_sec, - // tmpDiffToServer.tv_usec); - - // TODO: Move the time message sending to an own thread maybe - // following code is storing / initializing / resetting diff - // to server algorithm we collect a number of latencies and - // apply a median filter. Based on these we can get server - // now - { - struct timeval diff; - int64_t newValue; - - // clear diffBuffer if last update is older than a minute - timersub(&now, &lastTimeSync, &diff); - - if (diff.tv_sec > 60) { - ESP_LOGW(TAG, - "Last time sync older than a minute. " - "Clearing time buffer"); - - reset_latency_buffer(); - } - - newValue = ((int64_t)tmpDiffToServer.tv_sec * 1000000LL + - (int64_t)tmpDiffToServer.tv_usec); - player_latency_insert(newValue); - - // ESP_LOGE(TAG, "latency %lld", - // newValue); - - // store current time - lastTimeSync.tv_sec = now.tv_sec; - lastTimeSync.tv_usec = now.tv_usec; - - if (xSemaphoreTake(timeSyncSemaphoreHandle, 0) == pdTRUE) { - ESP_LOGW(TAG, "couldn't take timeSyncSemaphoreHandle"); - } - - uint64_t timeout; - if (latency_buffer_full() > 0) { - // we give timeSyncSemaphoreHandle after x µs through - // timer - // TODO: maybe start a periodic timer here, but we need - // to remember if it is already running then. also we - // need to stop it if reset_latency_buffer() was called - timeout = 1000000; - } else { - // Do a initial time sync with the server at boot - // we need to fill diffBuff fast so we get a good - // estimate of latency - timeout = 100000; - } - - esp_timer_start_once(timeSyncMessageTimer, timeout); - } - - break; - } - - // if (typedMsg != NULL) - // { - // free (typedMsg); - // typedMsg = NULL; - // } - } - - if (received_header == true) { - if (xSemaphoreTake(timeSyncSemaphoreHandle, 0) == pdTRUE) { - result = gettimeofday(&now, NULL); - // ESP_LOGI(TAG, "time of day: %ld %ld", now.tv_sec, - // now.tv_usec); - if (result) { - ESP_LOGI(TAG, "Failed to gettimeofday"); - continue; - } - - base_message.type = SNAPCAST_MESSAGE_TIME; - base_message.id = id_counter++; - base_message.refersTo = 0; - base_message.received.sec = 0; - base_message.received.usec = 0; - base_message.sent.sec = now.tv_sec; - base_message.sent.usec = now.tv_usec; - base_message.size = TIME_MESSAGE_SIZE; - - result = base_message_serialize( - &base_message, base_message_serialized, BASE_MESSAGE_SIZE); - if (result) { - ESP_LOGE(TAG, "Failed to serialize base message for time\r\n"); - continue; - } - - memset(&time_message, 0, sizeof(time_message)); - - result = time_message_serialize( - &time_message, time_message_serialized, TIME_MESSAGE_SIZE); - if (result) { - ESP_LOGI(TAG, "Failed to serialize time message\r\b"); - continue; - } - - result = send(sock, base_message_serialized, BASE_MESSAGE_SIZE, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing timesync base msg to socket: %s", - strerror(errno)); - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - result = send(sock, time_message_serialized, TIME_MESSAGE_SIZE, 0); - if (result < 0) { - ESP_LOGW(TAG, "error writing timesync msg to socket: %s", - strerror(errno)); - - shutdown(sock, 2); - closesocket(sock); - - break; // stop for(;;) will try to reconnect then - } - - // ESP_LOGI(TAG, "sent time sync message - // %ld.%06ld", now.tv_sec, now.tv_usec); - } - } - - // endTime = esp_timer_get_time (); - // ESP_LOGW(TAG, "%lld", endTime - startTime); - } - } -} - void app_main(void) { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||