Загрузка данных


#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/audio/codec.h>
#include <string.h>

/* RX and TX different on sai */
#define I2S_RX_NODE  DT_NODELABEL(i2s_rx)
#define I2S_TX_NODE  DT_NODELABEL(i2s_tx)

#define AUDIO_MCLK_FREQ 12288000

/* RX config (SGTL5000) */
#define SAMPLE_FREQ_RX       16000  /* 16 kHz */
/* TX config (TFA9882) */
#define SAMPLE_FREQ_TX       32000  /* 32kHz */
#define SAMPLE_BIT_WIDTH    16
#define BYTES_PER_SAMPLE    sizeof(int16_t)
#define NUMBER_OF_CHANNELS  2

/* Buffers for RX (16 kHz) and TX (32 kHz) will have different sizes! */
#define SAMPLES_PER_BLOCK_RX   ((SAMPLE_FREQ_RX / 10) * NUMBER_OF_CHANNELS)
#define SAMPLES_PER_BLOCK_TX   ((SAMPLE_FREQ_TX / 10) * NUMBER_OF_CHANNELS)

#define INITIAL_BLOCKS      2
#define BLOCK_COUNT (INITIAL_BLOCKS + 4)
#define TIMEOUT             1000

#define BLOCK_SIZE_RX  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK_RX)
#define BLOCK_SIZE_TX  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK_TX)

/* We create two different memory slabs for different block sizes. */
K_MEM_SLAB_DEFINE_STATIC(mem_slab_rx, BLOCK_SIZE_RX, 6, 4);
K_MEM_SLAB_DEFINE_STATIC(mem_slab_tx, BLOCK_SIZE_TX, 6, 4);

static int16_t echo_block[SAMPLES_PER_BLOCK_RX];
static volatile bool echo_enabled = false;
static K_SEM_DEFINE(toggle_transfer, 1, 1);

#define SW0_NODE        DT_ALIAS(sw0)
#ifdef CONFIG_TOGGLE_ECHO_EFFECT_SW0
static struct gpio_dt_spec sw0_spec = GPIO_DT_SPEC_GET(SW0_NODE, gpios);
static void sw0_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
	bool enable = !echo_enabled;
	echo_enabled = enable;
	printk("Echo %sabled\n", (enable ? "en" : "dis"));
}
#endif

#define SW1_NODE        DT_ALIAS(sw1)
#ifdef CONFIG_STOP_START_STREAMS_SW1
static struct gpio_dt_spec sw1_spec = GPIO_DT_SPEC_GET(SW1_NODE, gpios);
static void sw1_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
	k_sem_give(&toggle_transfer);
}
#endif

static bool init_buttons(void)
{
#ifdef CONFIG_TOGGLE_ECHO_EFFECT_SW0
	static struct gpio_callback sw0_cb_data;
	if (!gpio_is_ready_dt(&sw0_spec)) return false;
	gpio_pin_configure_dt(&sw0_spec, GPIO_INPUT);
	gpio_pin_interrupt_configure_dt(&sw0_spec, GPIO_INT_EDGE_TO_ACTIVE);
	gpio_init_callback(&sw0_cb_data, sw0_handler, BIT(sw0_spec.pin));
	gpio_add_callback(sw0_spec.port, &sw0_cb_data);
#endif
#ifdef CONFIG_STOP_START_STREAMS_SW1
	static struct gpio_callback sw1_cb_data;
	if (!gpio_is_ready_dt(&sw1_spec)) return false;
	gpio_pin_configure_dt(&sw1_spec, GPIO_INPUT);
	gpio_pin_interrupt_configure_dt(&sw1_spec, GPIO_INT_EDGE_TO_ACTIVE);
	gpio_init_callback(&sw1_cb_data, sw1_handler, BIT(sw1_spec.pin));
	gpio_add_callback(sw1_spec.port, &sw1_cb_data);
#endif
	return true;
}

static void process_block_data(void *mem_block, uint32_t number_of_samples)
{
	static bool clear_echo_block = false;
	
	/* Защита от выхода за границы буфера echo_block */
	if (number_of_samples > SAMPLES_PER_BLOCK_RX) {
		number_of_samples = SAMPLES_PER_BLOCK_RX;
	}

	if (echo_enabled) {
		for (uint32_t i = 0; i < number_of_samples; ++i) {
			int16_t *sample = &((int16_t *)mem_block)[i];
			*sample += echo_block[i];
			echo_block[i] = (*sample) / 2;
		}
		clear_echo_block = true;
	} else if (clear_echo_block) {
		clear_echo_block = false;
		memset(echo_block, 0, sizeof(echo_block));
	}
}

static bool prepare_transfer(const struct device *i2s_dev_rx,
			     const struct device *i2s_dev_tx)
{
	int ret;
	for (int i = 0; i < INITIAL_BLOCKS; ++i) {
		void *mem_block;
		ret = k_mem_slab_alloc(&mem_slab_tx, &mem_block, K_NO_WAIT);
		if (ret < 0) return false;
		memset(mem_block, 0, BLOCK_SIZE_TX);
		ret = i2s_write(i2s_dev_tx, mem_block, BLOCK_SIZE_TX);
		if (ret < 0) {
			k_mem_slab_free(&mem_slab_tx, mem_block);
			return false;
		}
	}
	return true;
}

static bool trigger_command(const struct device *i2s_dev_rx,
			    const struct device *i2s_dev_tx,
			    enum i2s_trigger_cmd cmd)
{
	int ret;

	ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, cmd);
	if (ret < 0) {
		printk("Failed to trigger command %d on RX: %d\n", cmd, ret);
		return false;
	}

	ret = i2s_trigger(i2s_dev_tx, I2S_DIR_TX, cmd);
	if (ret < 0) {
		printk("Failed to trigger command %d on TX: %d\n", cmd, ret);
		return false;
	}

	return true;
}

void audio_upsample_to_32k(const int16_t *src, int16_t *dst, uint8_t src_channels, uint32_t src_framerate)
{
	const uint32_t target_framerate = 32000;
	uint32_t factor = target_framerate / src_framerate;
	uint32_t src_frames = (src_framerate / 10); 
	uint32_t dst_idx = 0;

	for (uint32_t f = 0; f < src_frames; f++) {
		for (uint32_t rep = 0; rep < factor; rep++) {
			if (src_channels == 2) {
				dst[dst_idx]     = src[f * 2];     // Left
				dst[dst_idx + 1] = src[f * 2 + 1]; // Right
				dst_idx += 2;
			} else {
				dst[dst_idx]     = src[f]; // Left
				dst[dst_idx + 1] = src[f]; // Right
				dst_idx += 2;
			}
		}
	}
}

int main(void)
{
	const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_NODE);
	const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);
	struct i2s_config config_rx;
	struct i2s_config config_tx;
	bool streaming = false;

	printk("I2S asynchronous Audio: SGTL5000 (RX) & TFA9882 (TX)\n");

	const struct device *const rx_dev = DEVICE_DT_GET(DT_NODELABEL(rx_audio_codec));
	const struct device *const tx_dev = DEVICE_DT_GET(DT_NODELABEL(tx_audio_codec));
	struct audio_codec_cfg audio_cfg;

	if (!device_is_ready(rx_dev) || !device_is_ready(tx_dev)) {
		printk("Audio codecs are not ready\n");
		return 0;
	}

	/* 1. RX Capture Config (Исправлены макросы) */
	audio_cfg.dai_route = AUDIO_ROUTE_CAPTURE;
	audio_cfg.dai_type = AUDIO_DAI_TYPE_I2S;
	audio_cfg.dai_cfg.i2s.word_size = SAMPLE_BIT_WIDTH;
	audio_cfg.dai_cfg.i2s.channels = NUMBER_OF_CHANNELS;
	audio_cfg.dai_cfg.i2s.format = I2S_FMT_DATA_FORMAT_I2S;
	audio_cfg.dai_cfg.i2s.options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE;
	audio_cfg.dai_cfg.i2s.frame_clk_freq = SAMPLE_FREQ_RX; 
	audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab_rx;
	audio_cfg.dai_cfg.i2s.block_size = BLOCK_SIZE_RX;
	audio_cfg.mclk_freq = AUDIO_MCLK_FREQ;
	audio_codec_configure(rx_dev, &audio_cfg);

	/* 2. TX Playback Config */
	audio_cfg.dai_route = AUDIO_ROUTE_PLAYBACK;
	audio_cfg.dai_cfg.i2s.frame_clk_freq = SAMPLE_FREQ_TX;
	audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab_tx;
	audio_cfg.dai_cfg.i2s.block_size = BLOCK_SIZE_TX;
	audio_codec_configure(tx_dev, &audio_cfg);
	k_msleep(500);

	if (!init_buttons()) return 0;

	if (!device_is_ready(i2s_dev_rx) || !device_is_ready(i2s_dev_tx)) {
		printk("SAI2 Devices are not ready\n");
		return 0;
	}

	/* 3. Настройка параметров шины I2S/SAI */
	config_rx.word_size = SAMPLE_BIT_WIDTH;
	config_rx.channels = NUMBER_OF_CHANNELS;
	config_rx.format = I2S_FMT_DATA_FORMAT_I2S;
	config_rx.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
	config_rx.timeout = TIMEOUT;

	/* RX I2S config */
	config_rx.frame_clk_freq = SAMPLE_FREQ_RX; 
	config_rx.mem_slab = &mem_slab_rx;
	config_rx.block_size = BLOCK_SIZE_RX;
	i2s_configure(i2s_dev_rx, I2S_DIR_RX, &config_rx);

	/* TX I2S config (Завершение инициализации) */
	config_tx = config_rx; 
	config_tx.frame_clk_freq = SAMPLE_FREQ_TX; 
	config_tx.mem_slab = &mem_slab_tx;
	config_tx.block_size = BLOCK_SIZE_TX;
	i2s_configure(i2s_dev_tx, I2S_DIR_TX, &config_tx);

	/* 4. Главный цикл стриминга */
	while (1) {
		k_sem_take(&toggle_transfer, K_FOREVER);

		if (!streaming) {
			if (!prepare_transfer(i2s_dev_rx, i2s_dev_tx)) {
				printk("Failed to prepare TX buffers\n");
				continue;
			}
			if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_START)) {
				continue;
			}
			streaming = true;
			printk("Stream started\n");
		} else {
			if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_DROP)) {
				continue;
			}
			streaming = false;
			printk("Stream stopped\n");
		}

		/* Обработка данных в реальном времени */
		while (streaming && k_sem_take(&toggle_transfer, K_NO_WAIT) != 0) {
			void *rx_block = NULL;
			void *tx_block = NULL;
			size_t rx_size;
			int ret;

			/* Чтение из RX (16 кГц) */
			ret = i2s_read(i2s_dev_rx, &rx_block, &rx_size);
			if (ret < 0) {
				printk("i2s_read failed: %d\n", ret);
				break;
			}

			/* Обработка эффектов */
			process_block_data(rx_block, SAMPLES_PER_BLOCK_RX);

			/* Аллокация блока под TX (32 кГц) */
			ret = k_mem_slab_alloc(&mem_slab_tx, &tx_block, K_NO_WAIT);
			if (ret < 0) {
				printk("Failed to allocate TX block: %d\n", ret);
				k_mem_slab_free(&mem_slab_rx, rx_block);
				break;
			}

			/* Апсемплинг */
			audio_upsample_to_32k((const int16_t *)rx_block, (int16_t *)tx_block, 
			                      NUMBER_OF_CHANNELS, SAMPLE_FREQ_RX);

			/* Освобождаем RX блок */
			k_mem_slab_free(&mem_slab_rx, rx_block);

			/* Отправка в TX (32 кГц) */
			ret = i2s_write(i2s_dev_tx, tx_block, BLOCK_SIZE_TX);
			if (ret < 0) {
				printk("i2s_write failed: %d\n", ret);
				k_mem_slab_free(&mem_slab_tx, tx_block);
				break;
			}
		}
	}
	return 0;
}