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


zephyr_i2s_dev.h
#ifndef ZEPHYR_I2S_DEV_H_
#define ZEPHYR_I2S_DEV_H_

#ifdef MY_CODE
#define SAMPLE_FREQUENCY    48000
#define NUMBER_OF_CHANNELS  2
#else
#define SAMPLE_FREQUENCY    16000
#define NUMBER_OF_CHANNELS  1
#endif
#define SAMPLE_BIT_WIDTH    16
#define BYTES_PER_SAMPLE    sizeof(int16_t)
/* Such block length provides an echo with the delay of 100 ms. */
#define SAMPLES_PER_BLOCK   ((SAMPLE_FREQUENCY / 10) * NUMBER_OF_CHANNELS)
#define ZEPHYR_AUDIO_BLOCK_SIZE  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
#define ZEPHYR_AUDIO_INITIAL_BLOCKS 2
//#define ZEPHYR_AUDIO_BLOCK_COUNT ZEPHYR_AUDIO_INITIAL_BLOCKS + 4
#define ZEPHYR_AUDIO_BLOCK_COUNT ZEPHYR_AUDIO_INITIAL_BLOCKS + 32

typedef void I2S_Stream;

int zephyr_i2s_audio_init(void);
int zephyr_i2s_get_rate();
int zephyr_i2s_open_stream(I2S_Stream **stream, uint8_t *buf, uint8_t audio_in);
int zephyr_i2s_close_stream(I2S_Stream *stream);
int zephyr_i2s_start(uint8_t audio_in);
int zephyr_i2s_stop(uint8_t audio_in);

#endif /* ZEPHYR_I2S_DEV_H_ */


zephyr_i2s_dev.c
/***/
#include <stdio.h>
#include <string.h>
#include <portaudio.h>
#include <audio_dev.h>
#include <pthread.h>
#include <zephyr_i2s_dev.h>
#include <zephyr_audio_dev.h>

#include <zephyr/kernel.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/audio/codec.h>

#if DT_NODE_EXISTS(DT_NODELABEL(i2s_rxtx))
#define I2S_RX_NODE  DT_NODELABEL(i2s_rxtx)
#define I2S_TX_NODE  I2S_RX_NODE
#else
#define I2S_RX_NODE  DT_NODELABEL(i2s_rx)
#define I2S_TX_NODE  DT_NODELABEL(i2s_tx)
#endif

#define TIMEOUT             1000

K_MEM_SLAB_DEFINE_IN_SECT_STATIC(mem_slab, __dtcm_noinit_section, ZEPHYR_AUDIO_BLOCK_SIZE, ZEPHYR_AUDIO_BLOCK_COUNT, 4);

static const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_NODE);
static const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);
static uint8_t start_stream = 0;

enum zephyr_i2s_state {
    I2S_STREAM_RUNNING,
    I2S_STREAM_CLOSED
};

struct i2s_strm {
    uint8_t alloc;
    uint8_t devid;
    uint8_t *i2s_buf;
    pthread_t i2s_thread;
    pthread_attr_t  threadAttr;
    enum zephyr_i2s_state state;
};

#define MODOPS_I2S_STREAM_COUNT 2
static struct i2s_strm i2s_strm_pool[MODOPS_I2S_STREAM_COUNT] = {0};


/////////////////////////////I2S Stream///////////////////
static struct i2s_strm *i2s_str_alloc()
{
    struct i2s_strm *i2s_strm_ = NULL;
    for(uint8_t i=0; i < MODOPS_I2S_STREAM_COUNT; i++)
    {
        if(i2s_strm_pool[i].alloc == 0)
        {
            i2s_strm_ = &i2s_strm_pool[i];
            i2s_strm_pool[i].alloc = 1;
            break;
        }
    }
    return i2s_strm_;
}

static void i2s_str_free(struct i2s_strm *i2s_strm_)
{
    if(!i2s_strm_)
        return;
    for(uint8_t i=0; i < MODOPS_I2S_STREAM_COUNT; i++)
    {
        if(i2s_strm_pool[i].devid == i2s_strm_->devid)
        {
            i2s_strm_pool[i].alloc = 0;
            break;
        }
    }
}

static void i2s_thread_wakeup(struct i2s_strm *strm, enum i2s_state state)
{
    if(!strm)
        return;
    strm->state = state;
}

static void *i2s_thread_in(void *arg)
{
    struct i2s_strm *i2s_stream = (struct i2s_strm *) arg;
    uint32_t block_size;
    uint8_t *buf = NULL;
    uint8_t half_transfer = 1;
    int ret;

    buf = i2s_stream->i2s_buf;
    while (1)
    {
        if(start_stream)
        {
            ret = i2s_buf_read(i2s_dev_rx, buf, &block_size);
            //printf("I2S_READ: %p size %u ret=%d\n",buf,block_size,ret);
            if (ret < 0)
            {
                printf("I2S_IN: Failed to read data: %d\n", ret);
                //break;
            }
            else
            {
                //printf("I2S_IN: Read data size=%u\n",block_size);
                if(half_transfer)
                {
                    BSP_AUDIO_IN_HalfTransfer_CallBack();
                    buf += ZEPHYR_AUDIO_BLOCK_SIZE;
                    half_transfer = 0;
                }
                else
                {
                    BSP_AUDIO_IN_TransferComplete_CallBack();
                    buf = i2s_stream->i2s_buf;
                    half_transfer = 1;
                }
            }
        }
        if (i2s_stream->state != I2S_STREAM_RUNNING)
        {
            printf("Exit i2s_in_thread\n");
            break;
        }
    }
    return NULL;
}

static void *i2s_thread_out(void *arg)
{
    struct i2s_strm *i2s_stream = (struct i2s_strm *) arg;
    uint8_t *buf = NULL;
    uint8_t half_transfer = 1;
    int ret;

    buf = i2s_stream->i2s_buf;
    while (1)
    {
        if(start_stream)
        {
            ret = i2s_buf_write(i2s_dev_tx, buf, ZEPHYR_AUDIO_BLOCK_SIZE);
            //printf("I2S_WRITE: %p size %u ret=%d\n",buf,ZEPHYR_AUDIO_BLOCK_SIZE,ret);
            if (ret < 0)
            {
                printf("I2S_OUT: Failed to write data: %d\n", ret);
                //break;
            }
            else
            {
                //printf("I2S_OUT: Write data size=%u\n",ZEPHYR_AUDIO_BLOCK_SIZE);
                if(half_transfer)
                {
                    BSP_AUDIO_OUT_HalfTransfer_CallBack();
                    buf += ZEPHYR_AUDIO_BLOCK_SIZE;
                    half_transfer = 0;
                }
                else
                {
                    BSP_AUDIO_OUT_TransferComplete_CallBack();
                    buf = i2s_stream->i2s_buf;
                    half_transfer = 1;
                }
            }
        }
        if (i2s_stream->state != I2S_STREAM_RUNNING)
        {
            printf("Exit i2s_out_thread\n");
            break;
        }
    }
    return NULL;
}
///////////////////////////////////////////////////////////

static bool configure_streams(const struct device *i2s_dev_rx,
          const struct device *i2s_dev_tx,
          const struct i2s_config *config)
{
    int ret;

    if (i2s_dev_rx == i2s_dev_tx)
    {
        ret = i2s_configure(i2s_dev_rx, I2S_DIR_BOTH, config);
        printf("%s(%d) configure BOTH rx tx ret=%d\n",__FUNCTION__,__LINE__,ret);
        if (ret == 0)
        {
            return true;
        }
        /* -ENOSYS means that the RX and TX streams need to be
        * configured separately.
        */
        if (ret != -ENOSYS)
        {
            printk("Failed to configure streams: %d\n", ret);
            return false;
        }
    }

    ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, config);
    if (ret < 0)
    {
        printk("Failed to configure RX stream: %d\n", ret);
        return false;
    }

    ret = i2s_configure(i2s_dev_tx, I2S_DIR_TX, config);
    if (ret < 0)
    {
        printk("Failed to configure TX stream: %d\n", ret);
        return false;
    }

    return true;
}

static bool prepare_transfer(const struct device *i2s_dev_rx, const struct device *i2s_dev_tx)
{
    int ret;

    for (int i = 0; i < ZEPHYR_AUDIO_INITIAL_BLOCKS; ++i)
    {
        void *mem_block;

        ret = k_mem_slab_alloc(&mem_slab, &mem_block, K_NO_WAIT);
        if (ret < 0)
        {
            printk("Failed to allocate TX block %d: %d\n", i, ret);
            return false;
        }

        memset(mem_block, 0, ZEPHYR_AUDIO_BLOCK_SIZE);

        ret = i2s_write(i2s_dev_tx, mem_block, ZEPHYR_AUDIO_BLOCK_SIZE);
        if (ret < 0)
        {
            printk("Failed to write block %d: %d\n", i, ret);
            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;

    if (i2s_dev_rx == i2s_dev_tx)
    {
        ret = i2s_trigger(i2s_dev_rx, I2S_DIR_BOTH, cmd);
        if (ret == 0)
        {
            return true;
        }
        /* -ENOSYS means that commands for the RX and TX streams need
        * to be triggered separately.
        */
        if (ret != -ENOSYS)
        {
            printk("Failed to trigger command %d: %d\n", cmd, ret);
            return false;
        }
    }

    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;
}

int zephyr_i2s_start(uint8_t audio_in)
{
printf("%s(%d) audio_in=%d\n",__FUNCTION__,__LINE__,audio_in);
    if((i2s_dev_rx == i2s_dev_tx) && audio_in)
        return 0;
    if(!audio_in)
    {
        if (!prepare_transfer(i2s_dev_rx, i2s_dev_tx))
        {
            return -1;
        }
    }

    if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_START))
    {
        return -1;
    }
    start_stream = 1;
    return 0;
}

int zephyr_i2s_stop(uint8_t audio_in)
{
printf("%s(%d) start_stream=%d\n",__FUNCTION__,__LINE__,start_stream);
    if(start_stream)
    {
        start_stream = 0;
        if (!trigger_command(i2s_dev_rx, i2s_dev_tx, I2S_TRIGGER_DROP))
        {
            return -1;
        }
    }
    return 0;
}

int zephyr_i2s_open_stream(I2S_Stream **stream, uint8_t *buf, uint8_t audio_in)
{
    struct i2s_strm *i2s_stream;
    int ret = 0;

    i2s_stream = i2s_str_alloc();
    if (!i2s_stream) {
        printf("Cannot allocate i2s_strm\n");
        return -1;
    }
    i2s_stream->devid = (audio_in) ? 1 : 2;
    i2s_stream->i2s_buf = buf;
    i2s_stream->state = I2S_STREAM_RUNNING;
    *stream = i2s_stream;
    pthread_attr_init(&i2s_stream->threadAttr);
    pthread_attr_setstacksize(&i2s_stream->threadAttr, 1024);
    ret = pthread_create(&i2s_stream->i2s_thread, &i2s_stream->threadAttr, audio_in ? i2s_thread_in : i2s_thread_out, i2s_stream);
    if (ret != 0) {
        printf("i2s pthread_create failed ret=%d\n",ret);
        ret = -1;
    }
    else
        ret = 0;
    if(audio_in)
        pthread_setname_np(i2s_stream->i2s_thread, "i2s_thread_in");
    else
        pthread_setname_np(i2s_stream->i2s_thread, "i2s_thread_out");
    return ret;
}

int zephyr_i2s_close_stream(I2S_Stream *stream)
{
    struct i2s_strm *i2s_stream = stream;

    i2s_thread_wakeup(i2s_stream, I2S_STREAM_CLOSED);
    /* Wait until thread finished and only after that free the stream */
    pthread_join(i2s_stream->i2s_thread, NULL);
    pthread_attr_destroy(&i2s_stream->threadAttr);
    i2s_str_free(i2s_stream);

    return paNoError;
}

int zephyr_i2s_audio_init(void)
{
    struct i2s_config config;

#if DT_NODE_HAS_STATUS(DT_NODELABEL(audio_codec), okay)
    const struct device *const codec_dev = DEVICE_DT_GET(DT_NODELABEL(audio_codec));
    struct audio_codec_cfg audio_cfg;

    audio_cfg.dai_route = AUDIO_ROUTE_PLAYBACK_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_FRAME_CLK_MASTER;
    audio_cfg.dai_cfg.i2s.frame_clk_freq = SAMPLE_FREQUENCY;
    audio_cfg.dai_cfg.i2s.mem_slab = &mem_slab;
    audio_cfg.dai_cfg.i2s.block_size = ZEPHYR_AUDIO_BLOCK_SIZE;

    audio_codec_configure(codec_dev, &audio_cfg);
    k_msleep(1000);
#else
    printf("No found audio_codec int DTS!!! Error\n");
    return -1;
#endif

    if (!device_is_ready(i2s_dev_rx))
    {
        printk("%s is not ready\n", i2s_dev_rx->name);
        return -1;
    }

    if (i2s_dev_rx != i2s_dev_tx && !device_is_ready(i2s_dev_tx))
    {
        printk("%s is not ready\n", i2s_dev_tx->name);
        return -1;
    }
    config.word_size = SAMPLE_BIT_WIDTH;
    config.channels = NUMBER_OF_CHANNELS;
    config.format = I2S_FMT_DATA_FORMAT_I2S;
    config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
    config.frame_clk_freq = SAMPLE_FREQUENCY;
    config.mem_slab = &mem_slab;
    config.block_size = ZEPHYR_AUDIO_BLOCK_SIZE;
    config.timeout = TIMEOUT;
    if (!configure_streams(i2s_dev_rx, i2s_dev_tx, &config))
    {
        return -1;
    }
    return 0;
}

int zephyr_i2s_get_rate(void)
{
    return SAMPLE_FREQUENCY;
}