Загрузка данных
#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)
{
int ret;
#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
(void)ret;
return true;
}
static void process_block_data(void *mem_block, uint32_t number_of_samples)
{
static bool clear_echo_block;
if (echo_enabled) {
for (int 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) return false;
}
return true;
}
/* Trigger for RX and TX */
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;
}
/**
* @brief Universal upsampling of the audio block to a target frequency of 32 kHz.
*
* @param src Pointer to the source (input) data buffer.
* @param dst Pointer to the destination (output) data buffer (must be pre-allocated).
* @param src_channels Number of recording channels (1 - mono, 2 - stereo).
* @param src_framerate The recording sampling frequency in Hz (e.g. 8000 or 16000).
*/
void audio_upsample_to_32k(const int16_t *src, int16_t *dst, uint8_t src_channels, uint32_t src_framerate)
{
/* The amplifier's target frequency is always 32000 Hz */
const uint32_t target_framerate = 32000;
/* We calculate how many times the frequency needs to be increased (for 8 kHz the factor = 4, for 16 kHz = 2) */
uint32_t factor = target_framerate / src_framerate;
/* Calculate how many frames (time samples) are in the original block */
/* A frame is one full measurement (for mono, this is one sample; for stereo, it is a pair of L+R samples) */
uint32_t src_frames = (src_framerate / 10);
uint32_t dst_idx = 0;
for (uint32_t f = 0; f < src_frames; f++) {
/* For each source time frame, make 'factor' copies to the output buffer */
for (uint32_t rep = 0; rep < factor; rep++) {
if (src_channels == 2) {
/* STEREO mode: Copy the Left + Right channel pair */
dst[dst_idx] = src[f * 2]; // Left
dst[dst_idx + 1] = src[f * 2 + 1]; // Right
dst_idx += 2;
} else {
/* MONO mode: TFA9882 waits for data on two I2S bus channels. */
/* Duplicate the mono signal into the left and right channels of the output frame */
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;
printk("I2S asynchronous Audio: SGTL5000 (RX) & TFA9882 (TX)\n");
/* We get links to both independent codecs */
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)) {
printk("RX codec is not ready\n");
return 0;
}
/* 1. RX Capture */
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_FREQUENCY;
audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab_rx;
audio_cfg.dai_cfg.i2s.block_size = BLOCK_SIZE;
audio_cfg.mclk_freq = AUDIO_MCLK_FREQ;
audio_codec_configure(rx_dev, &audio_cfg);
/* 2. TX Playback*/
audio_cfg.dai_route = AUDIO_ROUTE_PLAYBACK;
audio_cfg.dai_cfg.i2s.options = I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE;
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. Setting up I2S/SAI2 bus parameters (Processor - Master for both sections) */
/* General parameters */
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 config */
config_rx.frame_clk_freq = SAMPLE_FREQ_RX; // 16000 Hz
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 config */
config_tx = config_rx; // Copying general settings
config_tx.frame_clk_freq = SAMPLE_FREQ_TX; // 32000 Hz (TFA9882)
config_tx.mem_slab = &mem_slab_tx;
config_tx.block_size = BLOCK_SIZE_TX;
i2s_configure(i2s_dev_tx, I2S_DIR_TX, &config_tx);
/* Infinite loop of audio stream processing (Echo effect) */
for (;;) {
k_sem_take(&toggle_transfer, K_FOREVER);
if (!prepare_transfer(i2s_dev_rx, i2s_dev_tx)) {
break;
}
if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_START)) {
break;
}
printk("Streams started\n");
while (k_sem_take(&toggle_transfer, K_NO_WAIT) == -EBUSY) {
void *mem_block_rx;
void *mem_block_tx;
size_t block_size_rx;
int ret;
/* 1. Read 16 kHz */
ret = i2s_read(i2s_dev_rx, &mem_block_rx, &block_size_rx);
if (ret < 0) break;
/* Processing echo at a frequency of 16 kHz */
process_block_data(mem_block_rx, block_size_rx / BYTES_PER_SAMPLE);
/* 2. We allocate a new clean block for sending at 32 kHz */
ret = k_mem_slab_alloc(&mem_slab_tx, &mem_block_tx, K_NO_WAIT);
if (ret < 0) {
k_mem_slab_free(&mem_slab_rx, mem_block_rx);
break;
}
/* 3. Simple Upsampling x2 (Repeat Stereo Samples) */
audio_upsample_to_32k((int16_t *)mem_block_rx, (int16_t *)mem_block_tx, NUMBER_OF_CHANNELS, SAMPLE_FREQ_RX)
/* We release the input RX block, it is no longer needed */
k_mem_slab_free(&mem_slab_rx, mem_block_rx);
/* 4. We are sending the finished upsampled 32 kHz block to the TFA9882 */
ret = i2s_write(i2s_dev_tx, mem_block_tx, BLOCK_SIZE_TX);
if (ret < 0) {
k_mem_slab_free(&mem_slab_tx, mem_block_tx);
break;
}
}
if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_DROP)) {
break;
}
printk("Streams stopped\n");
}
return 0;
}