diff --git a/components/driver/i2s.c b/components/driver/i2s.c index 6194d34d52..ab73aa8160 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -100,8 +100,9 @@ typedef struct { #ifdef CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; #endif - i2s_hal_context_t hal; /*!< I2S hal context*/ - i2s_hal_config_t hal_cfg; /*!< I2S hal configurations*/ + i2s_hal_context_t hal; /*!< I2S hal context*/ + i2s_channel_fmt_t init_chan_fmt; /*!< The initial channel format while installing, used for keep left or right mono when switch between mono and stereo */ + i2s_hal_config_t hal_cfg; /*!< I2S hal configurations*/ } i2s_obj_t; static i2s_obj_t *p_i2s[SOC_I2S_NUM]; @@ -1577,11 +1578,18 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_ cfg->chan_bits = (bits_cfg >> 16) > cfg->sample_bits ? (bits_cfg >> 16) : cfg->sample_bits; #if SOC_I2S_SUPPORTS_TDM if (ch & I2S_CHANNEL_MONO) { - cfg->chan_fmt = I2S_CHANNEL_FMT_ONLY_RIGHT; - cfg->chan_mask = I2S_TDM_ACTIVE_CH0; // Only activate one channel in mono if (ch >> 16) { + cfg->chan_fmt = I2S_CHANNEL_FMT_MULTIPLE; + cfg->chan_mask = 1 << __builtin_ctz(ch & 0xFFFF0000); // mono the minimun actived slot cfg->total_chan = i2s_get_max_channel_num(ch); } else { + if (p_i2s[i2s_num]->init_chan_fmt == I2S_CHANNEL_FMT_ONLY_LEFT) { + cfg->chan_mask = I2S_TDM_ACTIVE_CH1; // left slot mono + cfg->chan_fmt = I2S_CHANNEL_FMT_ONLY_LEFT; + } else { + cfg->chan_mask = I2S_TDM_ACTIVE_CH0; // right slot mono + cfg->chan_fmt = I2S_CHANNEL_FMT_ONLY_RIGHT; + } cfg->total_chan = 2; } } else { @@ -1598,7 +1606,15 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_ } #else /* Default */ - cfg->chan_fmt = ch == I2S_CHANNEL_MONO ? I2S_CHANNEL_FMT_ONLY_RIGHT : cfg->chan_fmt; + if (ch & I2S_CHANNEL_MONO) { + if (p_i2s[i2s_num]->init_chan_fmt == I2S_CHANNEL_FMT_ONLY_LEFT) { + cfg->chan_fmt = I2S_CHANNEL_FMT_ONLY_LEFT; + } else { + cfg->chan_fmt = I2S_CHANNEL_FMT_ONLY_RIGHT; + } + } else { + cfg->chan_fmt = I2S_CHANNEL_FMT_RIGHT_LEFT; + } cfg->active_chan = i2s_get_active_channel_num(cfg); cfg->total_chan = 2; #endif @@ -1763,6 +1779,7 @@ static esp_err_t i2s_driver_init(i2s_port_t i2s_num, const i2s_config_t *i2s_con p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; p_i2s[i2s_num]->mclk_multiple = i2s_config->mclk_multiple; p_i2s[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; + p_i2s[i2s_num]->init_chan_fmt = i2s_config->channel_format; /* I2S HAL configuration assignment */ p_i2s[i2s_num]->hal_cfg.mode = i2s_config->mode; @@ -1788,7 +1805,10 @@ static esp_err_t i2s_driver_init(i2s_port_t i2s_num, const i2s_config_t *i2s_con p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; p_i2s[i2s_num]->hal_cfg.total_chan = 2; break; - case I2S_CHANNEL_FMT_ONLY_RIGHT: // fall through + case I2S_CHANNEL_FMT_ONLY_RIGHT: + p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH1; + p_i2s[i2s_num]->hal_cfg.total_chan = 2; + break; case I2S_CHANNEL_FMT_ONLY_LEFT: p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0; p_i2s[i2s_num]->hal_cfg.total_chan = 2; diff --git a/components/driver/test/test_i2s.c b/components/driver/test/test_i2s.c index 6e4140ad68..f6f5a667c0 100644 --- a/components/driver/test/test_i2s.c +++ b/components/driver/test/test_i2s.c @@ -17,6 +17,7 @@ #include "freertos/task.h" #include "freertos/queue.h" #include "driver/i2s.h" +#include "hal/i2s_hal.h" #include "driver/gpio.h" #include "hal/gpio_hal.h" #include "unity.h" @@ -181,15 +182,24 @@ TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s]") TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, i2s_driver_uninstall(I2S_NUM_0)); } -TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]") +/** + * @brief Test mono and stereo mode of I2S by loopback + * @note Only rx channel distinguish left mono and right mono, tx channel does not + * @note 1. Check switch mono/stereo by 'i2s_set_clk' + * 2. Check rx right mono and left mono (requiring tx works in stereo mode) + * 3. Check tx mono (requiring rx works in stereo mode) + */ +TEST_CASE("I2S_mono_stereo_loopback_test", "[i2s]") { +#define WRITE_BUF_LEN 2000 +#define READ_BUF_LEN 4000 // master driver installed and send data i2s_config_t master_i2s_config = { .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX, .sample_rate = SAMPLE_RATE, .bits_per_sample = SAMPLE_BITS, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, + .communication_format = I2S_COMM_FORMAT_STAND_MSB, .dma_buf_count = 6, .dma_buf_len = 100, .use_apll = 0, @@ -210,47 +220,148 @@ TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]") .data_out_num = DATA_OUT_IO, .data_in_num = DATA_IN_IO }; + /* Install I2S in duplex mode */ TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL)); + TEST_ESP_OK(i2s_stop(I2S_NUM_0)); + /* Config TX as stereo channel directly, because legacy driver can't support config tx&rx separately */ +#if !SOC_I2S_SUPPORTS_TDM + i2s_ll_tx_set_chan_mod(&I2S0, 0); +#else + i2s_ll_tx_set_active_chan_mask(&I2S0, 0x03); +#endif + i2s_ll_tx_enable_mono_mode(&I2S0, false); + TEST_ESP_OK(i2s_set_pin(I2S_NUM_0, &master_pin_config)); i2s_test_io_config(I2S_TEST_MODE_LOOPBACK); - printf("\r\nheap size: %d\n", esp_get_free_heap_size()); - uint8_t *data_wr = (uint8_t *)malloc(sizeof(uint8_t) * 400); - size_t i2s_bytes_write = 0; - size_t bytes_read = 0; - int length = 0; - uint8_t *i2s_read_buff = (uint8_t *)malloc(sizeof(uint8_t) * 10000); + TEST_ESP_OK(i2s_start(I2S_NUM_0)); - for (int i = 0; i < 100; i++) { - data_wr[i] = i + 1; + uint16_t *w_buf = calloc(1, WRITE_BUF_LEN); + uint16_t *r_buf = calloc(1, READ_BUF_LEN); + size_t w_bytes = 0; + size_t r_bytes = 0; + for (int n = 0; n < WRITE_BUF_LEN / 2; n++) { + w_buf[n] = n%100; } - int flag = 0; // break loop flag - int end_position = 0; - // write data to slave - i2s_write(I2S_NUM_0, data_wr, sizeof(uint8_t) * 400, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS); - while (!flag) { - if (length >= 10000 - 500) { + /* rx right mono test + * tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... + * rx receive: 0x01[R] 0x03[R] ... */ + TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY)); + TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY)); +#if CONFIG_IDF_TARGET_ESP32 + /* The data of tx/rx channels are flipped on ESP32 */ + for (int n = 0; n < READ_BUF_LEN / 2; n += 2) { + int16_t temp = r_buf[n]; + r_buf[n] = r_buf[n+1]; + r_buf[n+1] = temp; + } +#endif + int i = 0; + for (i = 0; (i < READ_BUF_LEN / 2); i++) { + if (r_buf[i] == 1) { + printf("%d %d %d %d\n%d %d %d %d\n", + r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3], + r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]); break; } - i2s_read(I2S_NUM_0, i2s_read_buff + length, sizeof(uint8_t) * 500, &bytes_read, 1000 / portMAX_DELAY); - if (bytes_read > 0) { - for (int i = length; i < length + bytes_read; i++) { - if (i2s_read_buff[i] == 100) { - flag = 1; - end_position = i; - break; - } - } + } + printf("Data start index: %d\n", i); + TEST_ASSERT(i < READ_BUF_LEN / 2 - 50); + for (int16_t j = 1; j < 100; j += 2) { + TEST_ASSERT_EQUAL_INT16(r_buf[i++], j); + } + printf("rx right mono test passed\n"); + + /* tx/rx stereo test + * tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... + * rx receive: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... */ + TEST_ESP_OK(i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, SAMPLE_BITS, I2S_CHANNEL_STEREO)); + TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY)); + TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY)); + + for (i = 0; (i < READ_BUF_LEN / 2); i++) { + if (r_buf[i] == 1) { + printf("%d %d %d %d\n%d %d %d %d\n", + r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3], + r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]); + break; } - length = length + bytes_read; } - // test the read data right or not - for (int i = end_position - 99; i <= end_position; i++) { - TEST_ASSERT_EQUAL_UINT8((i - end_position + 100), *(i2s_read_buff + i)); + printf("Data start index: %d\n", i); + TEST_ASSERT(i < READ_BUF_LEN / 2 - 100); + for (int16_t j = 1; j < 100; j ++) { + TEST_ASSERT_EQUAL_INT16(r_buf[i++], j); // receive all number } - free(data_wr); - free(i2s_read_buff); - i2s_driver_uninstall(I2S_NUM_0); + printf("tx/rx stereo test passed\n"); + + /* tx mono rx right mono test + * tx format: 0x01[L] 0x01[R] 0x02[L] 0x02[R] ... + * rx receive: 0x01[R] 0x02[R] ... */ + TEST_ESP_OK(i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_MONO)); + TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY)); + TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY)); + + for (i = 0; i < READ_BUF_LEN / 2; i++) { + if (r_buf[i] == 1) { + printf("%d %d %d %d\n%d %d %d %d\n", + r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3], + r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]); + break; + } + } + printf("Data start index: %d\n", i); + TEST_ASSERT(i < READ_BUF_LEN / 2 - 100); + for (int16_t j = 1; j < 100; j ++) { + TEST_ASSERT_EQUAL_INT16(r_buf[i++], j); + } + printf("tx/rx mono test passed\n"); + + /* Reinstalling I2S to test rx left mono */ + TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); + master_i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; + TEST_ESP_OK(i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL)); + TEST_ESP_OK(i2s_stop(I2S_NUM_0)); +#if !SOC_I2S_SUPPORTS_TDM + i2s_ll_tx_set_chan_mod(&I2S0, 0); +#else + i2s_ll_tx_set_active_chan_mask(&I2S0, 0x03); +#endif + i2s_ll_tx_enable_mono_mode(&I2S0, false); + i2s_ll_tx_enable_mono_mode(&I2S0, false); + + TEST_ESP_OK(i2s_start(I2S_NUM_0)); + + /* rx left mono test + * tx format: 0x00[L] 0x01[R] 0x02[L] 0x03[R] ... + * rx receive: 0x00[R] 0x02[R] ... */ + TEST_ESP_OK(i2s_write(I2S_NUM_0, w_buf, WRITE_BUF_LEN, &w_bytes, portMAX_DELAY)); + TEST_ESP_OK(i2s_read(I2S_NUM_0, r_buf, READ_BUF_LEN, &r_bytes, portMAX_DELAY)); +#if CONFIG_IDF_TARGET_ESP32 + /* The data of tx/rx channels are flipped on ESP32 */ + for (int n = 0; n < READ_BUF_LEN / 2; n += 2) { + int16_t temp = r_buf[n]; + r_buf[n] = r_buf[n+1]; + r_buf[n+1] = temp; + } +#endif + for (i = 0; (i < READ_BUF_LEN / 2); i++) { + if (r_buf[i] == 2) { + printf("%d %d %d %d\n%d %d %d %d\n", + r_buf[i], r_buf[i+1], r_buf[i+2], r_buf[i+3], + r_buf[i+4], r_buf[i+5], r_buf[i+6], r_buf[i+7]); + break; + } + } + printf("Data start index: %d\n", i); + TEST_ASSERT(i < READ_BUF_LEN / 2 - 50); + for (int16_t j = 2; j < 100; j += 2) { + TEST_ASSERT_EQUAL_INT16(r_buf[i++], j); + } + printf("rx left mono test passed\n"); + + free(w_buf); + free(r_buf); + TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); } #if SOC_I2S_SUPPORTS_TDM diff --git a/components/hal/esp32/include/hal/i2s_ll.h b/components/hal/esp32/include/hal/i2s_ll.h index fd20c406cb..144ae402c3 100644 --- a/components/hal/esp32/include/hal/i2s_ll.h +++ b/components/hal/esp32/include/hal/i2s_ll.h @@ -695,6 +695,17 @@ static inline void i2s_ll_tx_set_chan_mod(i2s_dev_t *hw, uint32_t val) hw->conf_chan.tx_chan_mod = val; } +/** + * @brief Set I2S rx chan mode + * + * @param hw Peripheral I2S hardware instance address. + * @param val value to set rx chan mode + */ +static inline void i2s_ll_rx_set_chan_mod(i2s_dev_t *hw, uint32_t val) +{ + hw->conf_chan.rx_chan_mod = val; +} + /** * @brief Enable TX mono mode * @@ -705,7 +716,6 @@ static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { int data_bit = hw->sample_rate_conf.tx_bits_mod; hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; - hw->conf_chan.tx_chan_mod = mono_ena; } /** @@ -718,7 +728,6 @@ static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) { int data_bit = hw->sample_rate_conf.rx_bits_mod; hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; - hw->conf_chan.rx_chan_mod = mono_ena; } /** diff --git a/components/hal/esp32s2/include/hal/i2s_ll.h b/components/hal/esp32s2/include/hal/i2s_ll.h index 6f453221ee..521c5e70dc 100644 --- a/components/hal/esp32s2/include/hal/i2s_ll.h +++ b/components/hal/esp32s2/include/hal/i2s_ll.h @@ -771,6 +771,17 @@ static inline void i2s_ll_tx_set_chan_mod(i2s_dev_t *hw, uint32_t val) hw->conf_chan.tx_chan_mod = val; } +/** + * @brief Set I2S rx chan mode + * + * @param hw Peripheral I2S hardware instance address. + * @param val value to set rx chan mode + */ +static inline void i2s_ll_rx_set_chan_mod(i2s_dev_t *hw, uint32_t val) +{ + hw->conf_chan.rx_chan_mod = val; +} + /** * @brief Set I2S tx bits mod * @@ -804,7 +815,6 @@ static inline void i2s_ll_tx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) int data_bit = hw->sample_rate_conf.tx_bits_mod; hw->fifo_conf.tx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; hw->conf.tx_dma_equal = mono_ena; - hw->conf_chan.tx_chan_mod = mono_ena; } /** @@ -818,7 +828,6 @@ static inline void i2s_ll_rx_enable_mono_mode(i2s_dev_t *hw, bool mono_ena) int data_bit = hw->sample_rate_conf.rx_bits_mod; hw->fifo_conf.rx_fifo_mod = data_bit <= I2S_BITS_PER_SAMPLE_16BIT ? mono_ena : 2 + mono_ena; hw->conf.rx_dma_equal = mono_ena; - hw->conf_chan.rx_chan_mod = mono_ena; } /** diff --git a/components/hal/i2s_hal.c b/components/hal/i2s_hal.c index bd585af53b..142971993d 100644 --- a/components/hal/i2s_hal.c +++ b/components/hal/i2s_hal.c @@ -238,6 +238,8 @@ void i2s_hal_tx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t chan_num = hal_cfg->total_chan; i2s_ll_tx_set_active_chan_mask(hal->dev, hal_cfg->chan_mask >> 16); i2s_ll_tx_set_chan_num(hal->dev, chan_num); +#else + i2s_ll_tx_set_chan_mod(hal->dev, hal_cfg->chan_fmt < I2S_CHANNEL_FMT_ONLY_RIGHT ? hal_cfg->chan_fmt : (hal_cfg->chan_fmt >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left #endif i2s_ll_tx_set_sample_bit(hal->dev, chan_bits, data_bits); i2s_ll_tx_enable_mono_mode(hal->dev, is_mono); @@ -264,6 +266,8 @@ void i2s_hal_rx_set_channel_style(i2s_hal_context_t *hal, const i2s_hal_config_t chan_num = hal_cfg->total_chan; i2s_ll_rx_set_active_chan_mask(hal->dev, hal_cfg->chan_mask >> 16); i2s_ll_rx_set_chan_num(hal->dev, chan_num); +#else + i2s_ll_rx_set_chan_mod(hal->dev, hal_cfg->chan_fmt < I2S_CHANNEL_FMT_ONLY_RIGHT ? hal_cfg->chan_fmt : (hal_cfg->chan_fmt >> 1)); // 0-two channel;1-right;2-left;3-righ;4-left #endif i2s_ll_rx_set_sample_bit(hal->dev, chan_bits, data_bits); i2s_ll_rx_enable_mono_mode(hal->dev, is_mono);