mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Add i2s driver
This commit is contained in:
parent
8bcd341fca
commit
5462d98e5a
807
components/driver/i2s.c
Normal file
807
components/driver/i2s.c
Normal file
@ -0,0 +1,807 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <string.h>
|
||||
#include <esp_types.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "rom/lldesc.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
#include "esp_intr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char* I2S_TAG = "I2S";
|
||||
#define I2S_CHECK(a, str, ret) if (!(a)) { \
|
||||
ESP_LOGE(I2S_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
#define I2S_BASE_CLK (2*APB_CLK_FREQ)
|
||||
#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 gpio_matrix_out_check(a, b, c, d) if(a != -1) gpio_matrix_out(a, b, c, d) //if pin = -1, do not need to configure
|
||||
#define gpio_matrix_in_check(a, b, c) if(a != -1) gpio_matrix_in(a, b, c)
|
||||
|
||||
|
||||
/**
|
||||
* @brief DMA buffer object
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
char **buf;
|
||||
int buf_size;
|
||||
int rw_pos;
|
||||
void *curr_ptr;
|
||||
SemaphoreHandle_t mux;
|
||||
xQueueHandle queue;
|
||||
lldesc_t **desc;
|
||||
} i2s_dma_t;
|
||||
|
||||
/**
|
||||
* @brief I2S object instance
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_port_t i2s_num; /*!< I2S port number*/
|
||||
int queue_size; /*!< I2S event queue size*/
|
||||
QueueHandle_t i2s_queue; /*!< I2S queue handler*/
|
||||
int dma_buf_count; /*!< DMA buffer count, number of buffer*/
|
||||
int dma_buf_len; /*!< DMA buffer length, length of each buffer*/
|
||||
i2s_dma_t *rx; /*!< DMA Tx buffer*/
|
||||
i2s_dma_t *tx; /*!< DMA Rx buffer*/
|
||||
i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/
|
||||
int channel_num; /*!< Number of channels*/
|
||||
int bytes_per_sample; /*!< Bytes per sample*/
|
||||
i2s_mode_t mode; /*!< I2S Working mode*/
|
||||
} i2s_obj_t;
|
||||
|
||||
static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0};
|
||||
static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1};
|
||||
static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED};
|
||||
static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num)
|
||||
{
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
I2S_ENTER_CRITICAL();
|
||||
I2S[i2s_num]->conf.rx_fifo_reset = 1;
|
||||
I2S[i2s_num]->conf.rx_fifo_reset = 0;
|
||||
I2S[i2s_num]->conf.tx_fifo_reset = 1;
|
||||
I2S[i2s_num]->conf.tx_fifo_reset = 0;
|
||||
I2S_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_clear_intr_status(i2s_port_t i2s_num, uint32_t clr_mask)
|
||||
{
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
I2S[i2s_num]->int_clr.val = clr_mask;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_enable_rx_intr(i2s_port_t i2s_num)
|
||||
{
|
||||
|
||||
I2S_ENTER_CRITICAL();
|
||||
I2S[i2s_num]->int_ena.in_suc_eof = 1;
|
||||
I2S[i2s_num]->int_ena.in_dscr_err = 1;
|
||||
I2S_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_disable_rx_intr(i2s_port_t i2s_num)
|
||||
{
|
||||
I2S_ENTER_CRITICAL();
|
||||
I2S[i2s_num]->int_ena.in_suc_eof = 0;
|
||||
I2S[i2s_num]->int_ena.in_dscr_err = 0;
|
||||
I2S_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_disable_tx_intr(i2s_port_t i2s_num)
|
||||
{
|
||||
I2S_ENTER_CRITICAL();
|
||||
I2S[i2s_num]->int_ena.out_eof = 0;
|
||||
I2S[i2s_num]->int_ena.out_dscr_err = 0;
|
||||
I2S_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num)
|
||||
{
|
||||
I2S_ENTER_CRITICAL();
|
||||
I2S[i2s_num]->int_ena.out_eof = 1;
|
||||
I2S[i2s_num]->int_ena.out_dscr_err = 1;
|
||||
I2S_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2s_isr_register(i2s_port_t i2s_num, uint8_t intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle)
|
||||
{
|
||||
return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle);
|
||||
}
|
||||
|
||||
static esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint8_t bits, bool fuzzy)
|
||||
{
|
||||
int factor = (256%bits)? 384 : 256; // According to hardware codec requirement(supported 256fs or 384fs)
|
||||
int clkmInteger, clkmDecimals, bck = 0;
|
||||
float denom = (float)1 / 64;
|
||||
int channel = 2;
|
||||
|
||||
float clkmdiv = (float)I2S_BASE_CLK / (rate * factor);
|
||||
if (clkmdiv > 256) {
|
||||
ESP_LOGE(I2S_TAG, "clkmdiv is too large\r\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
clkmInteger = clkmdiv;
|
||||
clkmDecimals = (clkmdiv - clkmInteger) / denom;
|
||||
float mclk = clkmInteger + denom * clkmDecimals;
|
||||
bck = factor/(bits * channel);
|
||||
|
||||
I2S[i2s_num]->clkm_conf.clka_en = 0;
|
||||
I2S[i2s_num]->clkm_conf.clkm_div_a = 63;
|
||||
I2S[i2s_num]->clkm_conf.clkm_div_b = clkmDecimals;
|
||||
I2S[i2s_num]->clkm_conf.clkm_div_num = clkmInteger;
|
||||
I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck;
|
||||
I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck;
|
||||
I2S[i2s_num]->sample_rate_conf.tx_bits_mod = bits;
|
||||
I2S[i2s_num]->sample_rate_conf.rx_bits_mod = bits;
|
||||
float real_rate = (float)(I2S_BASE_CLK / (bck * bits * clkmInteger)/2);
|
||||
ESP_LOGI(I2S_TAG, "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, (float)I2S_BASE_CLK / mclk, real_rate *16*2, 64, clkmDecimals);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR i2s_intr_handler_default(void *arg)
|
||||
{
|
||||
i2s_obj_t *p_i2s = (i2s_obj_t*) arg;
|
||||
uint8_t i2s_num = p_i2s->i2s_num;
|
||||
i2s_dev_t* i2s_reg = I2S[i2s_num];
|
||||
i2s_event_t i2s_event;
|
||||
int dummy;
|
||||
|
||||
portBASE_TYPE high_priority_task_awoken = 0;
|
||||
|
||||
lldesc_t *finish_desc;
|
||||
|
||||
if (i2s_reg->int_st.out_dscr_err || i2s_reg->int_st.in_dscr_err) {
|
||||
ESP_LOGE(I2S_TAG, "out_dscr_err: %d or in_dscr_err:%d", i2s_reg->int_st.out_dscr_err == 1, i2s_reg->int_st.in_dscr_err == 1);
|
||||
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 (i2s_reg->int_st.out_eof && p_i2s->tx) {
|
||||
finish_desc = (lldesc_t*) i2s_reg->out_eof_des_addr;
|
||||
// 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);
|
||||
}
|
||||
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 (i2s_reg->int_st.in_suc_eof && p_i2s->rx) {
|
||||
// All buffers are full. This means we have an overflow.
|
||||
finish_desc = (lldesc_t*) i2s_reg->in_eof_des_addr;
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (high_priority_task_awoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
i2s_reg->int_clr.val = I2S[i2s_num]->int_st.val;
|
||||
}
|
||||
|
||||
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_FAIL;
|
||||
}
|
||||
if (dma == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
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);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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");
|
||||
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");
|
||||
|
||||
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*) malloc(dma_buf_len * sample_size);
|
||||
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]);
|
||||
memset(dma->buf[bux_idx], 0, dma_buf_len * sample_size);
|
||||
}
|
||||
|
||||
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*) malloc(sizeof(lldesc_t));
|
||||
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->rw_pos = 0;
|
||||
dma->buf_size = dma_buf_len * sample_size;
|
||||
dma->curr_ptr = NULL;
|
||||
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_start(i2s_port_t i2s_num)
|
||||
{
|
||||
//start DMA link
|
||||
I2S_ENTER_CRITICAL();
|
||||
esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle);
|
||||
I2S[i2s_num]->int_clr.val = 0xFFFFFFFF;
|
||||
if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) {
|
||||
ESP_LOGD(I2S_TAG, "I2S_MODE_TX");
|
||||
i2s_enable_tx_intr(i2s_num);
|
||||
I2S[i2s_num]->out_link.start = 1;
|
||||
I2S[i2s_num]->conf.tx_start = 1;
|
||||
}
|
||||
if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) {
|
||||
ESP_LOGD(I2S_TAG, "I2S_MODE_RX");
|
||||
i2s_enable_rx_intr(i2s_num);
|
||||
I2S[i2s_num]->in_link.start = 1;
|
||||
I2S[i2s_num]->conf.rx_start = 1;
|
||||
}
|
||||
esp_intr_enable(p_i2s_obj[i2s_num]->i2s_isr_handle);
|
||||
I2S_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_stop(i2s_port_t i2s_num)
|
||||
{
|
||||
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[i2s_num]->out_link.stop = 1;
|
||||
I2S[i2s_num]->conf.tx_start = 0;
|
||||
i2s_disable_tx_intr(i2s_num);
|
||||
}
|
||||
if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) {
|
||||
I2S[i2s_num]->in_link.stop = 1;
|
||||
I2S[i2s_num]->conf.rx_start = 0;
|
||||
i2s_disable_rx_intr(i2s_num);
|
||||
}
|
||||
I2S_EXIT_CRITICAL();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static esp_err_t configure_dac_pin(void)
|
||||
{
|
||||
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_DIG_FORCE_M);
|
||||
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_CLK_INV_M);
|
||||
|
||||
SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC_XPD_FORCE_M);
|
||||
SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC_M);
|
||||
|
||||
CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RUE_M);
|
||||
CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RDE_M);
|
||||
|
||||
SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC_XPD_FORCE_M);
|
||||
SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC_M);
|
||||
|
||||
CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RUE_M);
|
||||
CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RDE_M);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin)
|
||||
{
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
if (pin == NULL) {
|
||||
return configure_dac_pin();
|
||||
}
|
||||
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_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;
|
||||
//TX & RX
|
||||
if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) {
|
||||
bck_sig = I2S0I_BCK_OUT_IDX;
|
||||
ws_sig = I2S0I_WS_OUT_IDX;
|
||||
data_in_sig = I2S0I_DATA_IN15_IDX;
|
||||
if (i2s_num == I2S_NUM_1) {
|
||||
bck_sig = I2S1I_BCK_OUT_IDX;
|
||||
ws_sig = I2S1I_WS_OUT_IDX;
|
||||
data_in_sig = I2S1I_DATA_IN15_IDX;
|
||||
}
|
||||
}
|
||||
if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) {
|
||||
bck_sig = I2S0O_BCK_OUT_IDX;
|
||||
ws_sig = I2S0O_WS_OUT_IDX;
|
||||
data_out_sig = I2S0O_DATA_OUT23_IDX;
|
||||
if (i2s_num == I2S_NUM_1) {
|
||||
bck_sig = I2S1O_BCK_OUT_IDX;
|
||||
ws_sig = I2S1O_WS_OUT_IDX;
|
||||
data_out_sig = I2S1O_DATA_OUT23_IDX;
|
||||
}
|
||||
}
|
||||
|
||||
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_LOGE(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_set_sample_rates(i2s_port_t i2s_num, uint32_t rate)
|
||||
{
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
I2S_CHECK((p_i2s_obj[i2s_num]->bytes_per_sample > 0), "bits_per_sample not set", ESP_FAIL);
|
||||
return i2s_set_clk(i2s_num, rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0);
|
||||
}
|
||||
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_FAIL);
|
||||
I2S_CHECK((i2s_config), "param null", ESP_FAIL);
|
||||
if (i2s_num == I2S_NUM_1) {
|
||||
periph_module_enable(PERIPH_I2S1_MODULE);
|
||||
} else {
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
}
|
||||
// configure I2S data port interface.
|
||||
i2s_reset_fifo(i2s_num);
|
||||
|
||||
//reset i2s
|
||||
I2S[i2s_num]->conf.tx_reset = 1;
|
||||
I2S[i2s_num]->conf.tx_reset = 0;
|
||||
I2S[i2s_num]->conf.rx_reset = 1;
|
||||
I2S[i2s_num]->conf.rx_reset = 0;
|
||||
|
||||
|
||||
//reset dma
|
||||
I2S[i2s_num]->lc_conf.in_rst = 1;
|
||||
I2S[i2s_num]->lc_conf.in_rst = 0;
|
||||
I2S[i2s_num]->lc_conf.out_rst = 1;
|
||||
I2S[i2s_num]->lc_conf.out_rst = 0;
|
||||
|
||||
|
||||
//Enable and configure DMA
|
||||
I2S[i2s_num]->lc_conf.check_owner = 0;
|
||||
I2S[i2s_num]->lc_conf.out_loop_test = 0;
|
||||
I2S[i2s_num]->lc_conf.out_auto_wrback = 0;
|
||||
I2S[i2s_num]->lc_conf.out_data_burst_en = 0;
|
||||
I2S[i2s_num]->lc_conf.outdscr_burst_en = 0;
|
||||
I2S[i2s_num]->lc_conf.out_no_restart_clr = 0;
|
||||
I2S[i2s_num]->lc_conf.indscr_burst_en = 0;
|
||||
I2S[i2s_num]->lc_conf.out_eof_mode = 1;
|
||||
|
||||
|
||||
I2S[i2s_num]->conf2.lcd_en = 0;
|
||||
I2S[i2s_num]->conf2.camera_en = 0;
|
||||
I2S[i2s_num]->pdm_conf.pcm2pdm_conv_en = 0;
|
||||
I2S[i2s_num]->pdm_conf.pdm2pcm_conv_en = 0;
|
||||
|
||||
I2S[i2s_num]->fifo_conf.dscr_en = 0;
|
||||
p_i2s_obj[i2s_num]->channel_num = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1;
|
||||
|
||||
I2S[i2s_num]->conf_chan.tx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left
|
||||
I2S[i2s_num]->fifo_conf.tx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel
|
||||
I2S[i2s_num]->conf.tx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel
|
||||
|
||||
I2S[i2s_num]->conf_chan.rx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left
|
||||
I2S[i2s_num]->fifo_conf.rx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel
|
||||
I2S[i2s_num]->conf.rx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel
|
||||
I2S[i2s_num]->fifo_conf.dscr_en = 1;//connect dma to fifo
|
||||
|
||||
I2S[i2s_num]->conf.tx_start = 0;
|
||||
I2S[i2s_num]->conf.rx_start = 0;
|
||||
|
||||
if (i2s_config->mode & I2S_MODE_TX) {
|
||||
I2S[i2s_num]->conf.tx_msb_right = 0;
|
||||
I2S[i2s_num]->conf.tx_right_first = 0;
|
||||
|
||||
I2S[i2s_num]->conf.tx_slave_mod = 0; // Master
|
||||
I2S[i2s_num]->fifo_conf.tx_fifo_mod_force_en = 1;//?
|
||||
|
||||
if (i2s_config->mode & I2S_MODE_SLAVE) {
|
||||
I2S[i2s_num]->conf.tx_slave_mod = 1;//TX Slave
|
||||
}
|
||||
}
|
||||
|
||||
if (i2s_config->mode & I2S_MODE_RX) {
|
||||
I2S[i2s_num]->conf.rx_msb_right = 0;
|
||||
I2S[i2s_num]->conf.rx_right_first = 0;
|
||||
I2S[i2s_num]->conf.rx_slave_mod = 0; // Master
|
||||
I2S[i2s_num]->fifo_conf.rx_fifo_mod_force_en = 1;//?
|
||||
I2S[i2s_num]->rx_eof_num = (i2s_config->dma_buf_len);
|
||||
if (i2s_config->mode & I2S_MODE_SLAVE) {
|
||||
I2S[i2s_num]->conf.rx_slave_mod = 1;//RX Slave
|
||||
}
|
||||
}
|
||||
|
||||
if (i2s_config->mode & I2S_MODE_DAC_BUILT_IN) {
|
||||
I2S[i2s_num]->conf2.lcd_en = 1;
|
||||
I2S[i2s_num]->conf.tx_right_first = 1;
|
||||
I2S[i2s_num]->fifo_conf.tx_fifo_mod = 3;
|
||||
}
|
||||
|
||||
if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S) {
|
||||
I2S[i2s_num]->conf.tx_short_sync = 0;
|
||||
I2S[i2s_num]->conf.rx_short_sync = 0;
|
||||
I2S[i2s_num]->conf.tx_msb_shift = 1;
|
||||
I2S[i2s_num]->conf.rx_msb_shift = 1;
|
||||
if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S_LSB) {
|
||||
if (i2s_config->mode & I2S_MODE_TX) {
|
||||
I2S[i2s_num]->conf.tx_msb_shift = 0;
|
||||
}
|
||||
if (i2s_config->mode & I2S_MODE_RX) {
|
||||
I2S[i2s_num]->conf.rx_msb_shift = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM) {
|
||||
I2S[i2s_num]->conf.tx_msb_shift = 0;
|
||||
I2S[i2s_num]->conf.rx_msb_shift = 0;
|
||||
I2S[i2s_num]->conf.tx_short_sync = 0;
|
||||
I2S[i2s_num]->conf.rx_short_sync = 0;
|
||||
if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM_SHORT) {
|
||||
if (i2s_config->mode & I2S_MODE_TX) {
|
||||
I2S[i2s_num]->conf.tx_short_sync = 1;
|
||||
}
|
||||
if (i2s_config->mode & I2S_MODE_RX) {
|
||||
I2S[i2s_num]->conf.rx_short_sync = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) {
|
||||
I2S[i2s_num]->conf.sig_loopback = 1;
|
||||
}
|
||||
i2s_set_clk(i2s_num, i2s_config->sample_rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num)
|
||||
{
|
||||
int total_buffer_in_bytes = p_i2s_obj[i2s_num]->dma_buf_count * p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num;
|
||||
const uint32_t zero_sample[2] = { 0 };
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
while (total_buffer_in_bytes > 0) {
|
||||
i2s_push_sample(i2s_num, (const char*) zero_sample, 10);
|
||||
total_buffer_in_bytes -= p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue)
|
||||
{
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
I2S_CHECK((i2s_config != NULL), "I2S configuration must not NULL", ESP_FAIL);
|
||||
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_FAIL);
|
||||
I2S_CHECK((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 2048), "I2S buffer length at most 2048 and more than 8", ESP_FAIL);
|
||||
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_FAIL;
|
||||
}
|
||||
|
||||
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]->bytes_per_sample = i2s_config->bits_per_sample/8;
|
||||
|
||||
//initial dma
|
||||
if (ESP_FAIL == 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)) {
|
||||
free(p_i2s_obj[i2s_num]);
|
||||
ESP_LOGE(I2S_TAG, "Register I2S Interrupt error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
i2s_stop(i2s_num);
|
||||
i2s_param_config(i2s_num, i2s_config);
|
||||
|
||||
if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) {
|
||||
p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len);
|
||||
if (p_i2s_obj[i2s_num]->tx == NULL) {
|
||||
ESP_LOGE(I2S_TAG, "Failed to create tx dma buffer");
|
||||
i2s_driver_uninstall(i2s_num);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
I2S[i2s_num]->out_link.addr = (uint32_t) p_i2s_obj[i2s_num]->tx->desc[0];
|
||||
}
|
||||
|
||||
if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) {
|
||||
p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len);
|
||||
if (p_i2s_obj[i2s_num]->rx == NULL){
|
||||
ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer");
|
||||
i2s_driver_uninstall(i2s_num);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
I2S[i2s_num]->in_link.addr = (uint32_t) p_i2s_obj[i2s_num]->rx->desc[0];
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
i2s_start(i2s_num);
|
||||
} else {
|
||||
ESP_LOGE(I2S_TAG, "I2S driver already installed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num)
|
||||
{
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
if (p_i2s_obj[i2s_num] == NULL) {
|
||||
ESP_LOGI(I2S_TAG, "ALREADY NULL");
|
||||
return ESP_OK;
|
||||
}
|
||||
i2s_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;
|
||||
}
|
||||
|
||||
free(p_i2s_obj[i2s_num]);
|
||||
p_i2s_obj[i2s_num] = NULL;
|
||||
|
||||
if (i2s_num == I2S_NUM_0) {
|
||||
periph_module_disable(PERIPH_I2S0_MODULE);
|
||||
} else if (i2s_num == I2S_NUM_1) {
|
||||
periph_module_disable(PERIPH_I2S1_MODULE);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait)
|
||||
{
|
||||
char *data_ptr;
|
||||
int bytes_can_write, bytes_writen = 0;
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
if (p_i2s_obj[i2s_num]->tx == NULL) {
|
||||
return 0;
|
||||
}
|
||||
xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY);
|
||||
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;
|
||||
}
|
||||
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, bytes_can_write);
|
||||
size -= bytes_can_write;
|
||||
src += bytes_can_write;
|
||||
p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write;
|
||||
bytes_writen += bytes_can_write;
|
||||
}
|
||||
xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux);
|
||||
return bytes_writen;
|
||||
}
|
||||
|
||||
int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait)
|
||||
{
|
||||
char *data_ptr;
|
||||
int bytes_can_read, byte_read = 0;
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
if (p_i2s_obj[i2s_num]->rx == NULL) {
|
||||
return 0;
|
||||
}
|
||||
xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY);
|
||||
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 > size) {
|
||||
bytes_can_read = size;
|
||||
}
|
||||
memcpy(dest, data_ptr, bytes_can_read);
|
||||
size -= bytes_can_read;
|
||||
dest += bytes_can_read;
|
||||
p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read;
|
||||
byte_read += bytes_can_read;
|
||||
}
|
||||
xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux);
|
||||
return byte_read;
|
||||
}
|
||||
int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait)
|
||||
{
|
||||
int i, bytes_to_push = 0;
|
||||
char *data_ptr;
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
ESP_LOGD(I2S_TAG, "rw_pos: %d, buf_size: %d, curr_ptr: %d", 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);
|
||||
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;
|
||||
for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) {
|
||||
*data_ptr++ = *sample++;
|
||||
bytes_to_push ++;
|
||||
}
|
||||
if (p_i2s_obj[i2s_num]->channel_num == 2) {
|
||||
for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) {
|
||||
*data_ptr++ = *sample++;
|
||||
bytes_to_push ++;
|
||||
}
|
||||
}
|
||||
|
||||
p_i2s_obj[i2s_num]->tx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num;
|
||||
return bytes_to_push;
|
||||
}
|
||||
|
||||
int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait)
|
||||
{
|
||||
int i, bytes_to_pop = 0;
|
||||
char *data_ptr;
|
||||
I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL);
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) {
|
||||
*sample++ = *data_ptr++;
|
||||
bytes_to_pop++;
|
||||
}
|
||||
if (p_i2s_obj[i2s_num]->channel_num == 2) {
|
||||
for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) {
|
||||
*sample++ = *data_ptr++;
|
||||
bytes_to_pop++;
|
||||
}
|
||||
}
|
||||
|
||||
p_i2s_obj[i2s_num]->rx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num;
|
||||
return bytes_to_pop;
|
||||
}
|
380
components/driver/include/driver/i2s.h
Normal file
380
components/driver/include/driver/i2s.h
Normal file
@ -0,0 +1,380 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _DRIVER_I2S_H_
|
||||
#define _DRIVER_I2S_H_
|
||||
#include "esp_err.h"
|
||||
#include <esp_types.h>
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "soc/i2s_reg.h"
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "rom/gpio.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define I2S_PIN_NO_CHANGE (-1)
|
||||
|
||||
|
||||
/**
|
||||
* @brief I2S bit width per sample.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_BITS_PER_SAMPLE_8BIT = 8, /*!< I2S bits per sample: 8-bits*/
|
||||
I2S_BITS_PER_SAMPLE_16BIT = 16, /*!< I2S bits per sample: 16-bits*/
|
||||
I2S_BITS_PER_SAMPLE_24BIT = 24, /*!< I2S bits per sample: 24-bits*/
|
||||
I2S_BITS_PER_SAMPLE_32BIT = 32, /*!< I2S bits per sample: 32-bits*/
|
||||
} i2s_bits_per_sample_t;
|
||||
|
||||
/**
|
||||
* @brief I2S communication standard format
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_COMM_FORMAT_I2S = 0x01, /*!< I2S communication format I2S*/
|
||||
I2S_COMM_FORMAT_I2S_MSB = 0x02, /*!< I2S format MSB*/
|
||||
I2S_COMM_FORMAT_I2S_LSB = 0x04, /*!< I2S format LSB*/
|
||||
I2S_COMM_FORMAT_PCM = 0x08, /*!< I2S communication format PCM*/
|
||||
I2S_COMM_FORMAT_PCM_SHORT = 0x10, /*!< PCM Short*/
|
||||
I2S_COMM_FORMAT_PCM_LONG = 0x20, /*!< PCM Long*/
|
||||
} i2s_comm_format_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief I2S channel format type
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00,
|
||||
I2S_CHANNEL_FMT_ALL_RIGHT,
|
||||
I2S_CHANNEL_FMT_ALL_LEFT,
|
||||
I2S_CHANNEL_FMT_ONLY_RIGHT,
|
||||
I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
} i2s_channel_fmt_t;
|
||||
|
||||
/**
|
||||
* @brief PDM sample rate ratio, measured in Hz.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
PDM_SAMPLE_RATE_RATIO_64,
|
||||
PDM_SAMPLE_RATE_RATIO_128,
|
||||
} pdm_sample_rate_ratio_t;
|
||||
|
||||
/**
|
||||
* @brief PDM PCM convter enable/disable.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
PDM_PCM_CONV_ENABLE,
|
||||
PDM_PCM_CONV_DISABLE,
|
||||
} pdm_pcm_conv_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief I2S Peripheral, 0 & 1.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_NUM_0 = 0x0, /*!< I2S 0*/
|
||||
I2S_NUM_1 = 0x1, /*!< I2S 1*/
|
||||
I2S_NUM_MAX,
|
||||
} i2s_port_t;
|
||||
|
||||
/**
|
||||
* @brief I2S Mode, defaut is I2S_MODE_MASTER | I2S_MODE_TX
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_MODE_MASTER = 1,
|
||||
I2S_MODE_SLAVE = 2,
|
||||
I2S_MODE_TX = 4,
|
||||
I2S_MODE_RX = 8,
|
||||
I2S_MODE_DAC_BUILT_IN = 16
|
||||
} i2s_mode_t;
|
||||
|
||||
/**
|
||||
* @brief I2S configuration parameters for i2s_param_config function
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_mode_t mode; /*!< I2S work mode*/
|
||||
int sample_rate; /*!< I2S sample rate*/
|
||||
i2s_bits_per_sample_t bits_per_sample; /*!< I2S bits per sample*/
|
||||
i2s_channel_fmt_t channel_format; /*!< I2S channel format */
|
||||
i2s_comm_format_t communication_format; /*!< I2S communication format */
|
||||
int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */
|
||||
int dma_buf_count; /*!< I2S DMA Buffer Count */
|
||||
int dma_buf_len; /*!< I2S DMA Buffer Length */
|
||||
} i2s_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S event types
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_EVENT_DMA_ERROR,
|
||||
I2S_EVENT_TX_DONE, /*!< I2S DMA finish sent 1 buffer*/
|
||||
I2S_EVENT_RX_DONE, /*!< I2S DMA finish received 1 buffer*/
|
||||
I2S_EVENT_MAX, /*!< I2S event max index*/
|
||||
} i2s_event_type_t;
|
||||
|
||||
/**
|
||||
* @brief Event structure used in I2S event queue
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_event_type_t type; /*!< I2S event type */
|
||||
size_t size; /*!< I2S data size for I2S_DATA event*/
|
||||
} i2s_event_t;
|
||||
|
||||
/**
|
||||
* @brief I2S pin number for i2s_set_pin
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int bck_io_num; /*!< BCK in out pin*/
|
||||
int ws_io_num; /*!< WS in out pin*/
|
||||
int data_out_num; /*!< DATA out pin*/
|
||||
int data_in_num; /*!< DATA in pin*/
|
||||
} i2s_pin_config_t;
|
||||
|
||||
typedef intr_handle_t i2s_isr_handle_t;
|
||||
/**
|
||||
* @brief Set I2S pin number
|
||||
*
|
||||
* @note
|
||||
* Internal signal can be output to multiple GPIO pads
|
||||
* Only one GPIO pad can connect with input signal
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0 or I2S_NUM_1
|
||||
*
|
||||
* @param pin I2S Pin struct, or NULL for 2-channels, 8-bits DAC pin configuration (GPIO25 & GPIO26)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin);
|
||||
|
||||
/**
|
||||
* @brief i2s install and start 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.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t i2s_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_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num);
|
||||
|
||||
/**
|
||||
* @brief i2s read data buffer to i2s dma buffer
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @param src source address to write
|
||||
*
|
||||
* @param size size of data (size in bytes)
|
||||
*
|
||||
* @param ticks_to_wait Write timeout
|
||||
*
|
||||
* @return number of written bytes
|
||||
*/
|
||||
int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait);
|
||||
|
||||
/**
|
||||
* @brief i2s write data buffer to i2s dma buffer
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @param dest destination address to read
|
||||
*
|
||||
* @param size size of data (size in bytes)
|
||||
*
|
||||
* @param ticks_to_wait Read timeout
|
||||
*
|
||||
* @return number of read bytes
|
||||
*/
|
||||
int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait);
|
||||
|
||||
/**
|
||||
* @brief i2s push 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8.
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8)
|
||||
*
|
||||
* @param ticks_to_wait Push timeout
|
||||
*
|
||||
* @return number of push bytes
|
||||
*/
|
||||
int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait);
|
||||
|
||||
/**
|
||||
* @brief Pop 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8.
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8)
|
||||
*
|
||||
* @param ticks_to_wait Pop timeout
|
||||
*
|
||||
* @return number of pop bytes
|
||||
*/
|
||||
int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set clock rate used for I2S RX and TX
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @param rate I2S clock (ex: 8000, 44100...)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate);
|
||||
|
||||
/**
|
||||
* @brief Start driver
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t i2s_start(i2s_port_t i2s_num);
|
||||
|
||||
/**
|
||||
* @brief Stop driver
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t i2s_stop(i2s_port_t i2s_num);
|
||||
|
||||
/**
|
||||
* @brief Set the TX DMA buffer contents to all zeroes
|
||||
*
|
||||
* @param i2s_num I2S_NUM_0, I2S_NUM_1
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Parameter error
|
||||
*/
|
||||
esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num);
|
||||
|
||||
/***************************EXAMPLE**********************************
|
||||
*
|
||||
*
|
||||
* ----------------EXAMPLE OF I2S SETTING ---------------------
|
||||
* @code{c}
|
||||
*
|
||||
* #include "freertos/queue.h"
|
||||
* #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h
|
||||
* int i2s_num = 0; //i2s port number
|
||||
* i2s_config_t i2s_config = {
|
||||
* .mode = I2S_MODE_MASTER | I2S_MODE_TX,
|
||||
* .sample_rate = 44100,
|
||||
* .bits_per_sample = 16, //16, 32
|
||||
* .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //format LEFT_RIGHT
|
||||
* .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
|
||||
* .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
* .dma_buf_count = 8,
|
||||
* .dma_buf_len = 64
|
||||
* };
|
||||
*
|
||||
* i2s_pin_config_t pin_config = {
|
||||
* .bck_io_num = 26,
|
||||
* .ws_io_num = 25,
|
||||
* .data_out_num = 22,
|
||||
* .data_in_num = I2S_PIN_NO_CHANGE
|
||||
* };
|
||||
*
|
||||
* i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver
|
||||
*
|
||||
* i2s_set_pin(i2s_num, &pin_config);
|
||||
*
|
||||
* i2s_set_sample_rates(i2s_num, 22050); //set sample rates
|
||||
*
|
||||
*
|
||||
* i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver
|
||||
*@endcode
|
||||
*
|
||||
* ----------------EXAMPLE USING I2S WITH DAC ---------------------
|
||||
* @code{c}
|
||||
*
|
||||
* #include "freertos/queue.h"
|
||||
* #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h
|
||||
* int i2s_num = 0; //i2s port number
|
||||
* i2s_config_t i2s_config = {
|
||||
* .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
|
||||
* .sample_rate = 44100,
|
||||
* .bits_per_sample = 8, // Only 8-bit DAC support
|
||||
* .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //
|
||||
* .communication_format = I2S_COMM_FORMAT_I2S_MSB,
|
||||
* .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
* .dma_buf_count = 8,
|
||||
* .dma_buf_len = 64
|
||||
* };
|
||||
*
|
||||
*
|
||||
* i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver
|
||||
*
|
||||
* i2s_set_pin(i2s_num, NULL); //for internal DAC
|
||||
*
|
||||
* i2s_set_sample_rates(i2s_num, 22050); //set sample rates
|
||||
*
|
||||
* i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver
|
||||
*@endcode
|
||||
*-----------------------------------------------------------------------------*
|
||||
***************************END OF EXAMPLE**********************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _DRIVER_I2S_H_ */
|
8
examples/22_i2s/Makefile
Normal file
8
examples/22_i2s/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
VERBOSE = 1
|
||||
PROJECT_NAME := esp32-i2s-driver-example
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
72
examples/22_i2s/main/app_main.c
Normal file
72
examples/22_i2s/main/app_main.c
Normal file
@ -0,0 +1,72 @@
|
||||
/* I2S Example
|
||||
|
||||
This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/i2s.h"
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#define SAMPLE_RATE (36000)
|
||||
#define I2S_NUM (0)
|
||||
#define WAVE_FREQ_HZ (100)
|
||||
#define PI 3.14159265
|
||||
|
||||
#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ)
|
||||
|
||||
void app_main()
|
||||
{
|
||||
unsigned int i, sample_val;
|
||||
float sin_float, triangle_float, triangle_step = 65536.0 / SAMPLE_PER_CYCLE;
|
||||
//for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes each sample)
|
||||
//using 6 buffers, we need 60-samples per buffer
|
||||
//2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
|
||||
.sample_rate = SAMPLE_RATE,
|
||||
.bits_per_sample = 16, //16-bit per channel
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
|
||||
.dma_buf_count = 6,
|
||||
.dma_buf_len = 60, //
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1
|
||||
};
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = 26,
|
||||
.ws_io_num = 25,
|
||||
.data_out_num = 22,
|
||||
.data_in_num = -1 //Not used
|
||||
};
|
||||
|
||||
nvs_flash_init();
|
||||
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
|
||||
i2s_set_pin(I2S_NUM, &pin_config);
|
||||
|
||||
triangle_float = -32767;
|
||||
|
||||
for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
|
||||
sin_float = sin(i * PI / 180.0);
|
||||
if(sin_float >= 0)
|
||||
triangle_float += triangle_step;
|
||||
else
|
||||
triangle_float -= triangle_step;
|
||||
sin_float *= 32767;
|
||||
|
||||
sample_val = 0;
|
||||
sample_val += (short)triangle_float;
|
||||
sample_val = sample_val << 16;
|
||||
sample_val += (short) sin_float;
|
||||
|
||||
i2s_push_sample(I2S_NUM, (char *)&sample_val, portMAX_DELAY);
|
||||
}
|
||||
}
|
10
examples/22_i2s/main/component.mk
Normal file
10
examples/22_i2s/main/component.mk
Normal file
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default,
|
||||
# this will take the sources in the src/ directory, compile them and link them into
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
Loading…
Reference in New Issue
Block a user