samples: drivers: i2s: add output sample
Add simple I2S output sample. This sample is verified with the RT1060 EVKB, but can be ported to any board with I2S support. It simply demonstrates how to write I2S output data using the I2S API. The output can be verified using a signal analyzer, if the user desires. Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
98d95851c6
commit
8596ee9337
8
samples/drivers/i2s/output/CMakeLists.txt
Normal file
8
samples/drivers/i2s/output/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(i2s_output)
|
||||
|
||||
target_sources(app PRIVATE src/main.c)
|
36
samples/drivers/i2s/output/README.rst
Normal file
36
samples/drivers/i2s/output/README.rst
Normal file
|
@ -0,0 +1,36 @@
|
|||
.. zephyr:code-sample:: i2s-output
|
||||
:name: I2S output
|
||||
:relevant-api: i2s_interface
|
||||
|
||||
Send I2S output stream
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This sample demonstrates how to use an I2S driver to send an output stream of
|
||||
audio data. Currently, no codec is used with this sample. The I2S output can
|
||||
be verified with a signal analyzer.
|
||||
|
||||
The sample will send a short burst of audio data, consisting of a sine wave.
|
||||
The I2S TX queue will then be drained, and audio output will stop.
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
The I2S device to be used by the sample is specified by defining
|
||||
a devicetree alias named ``i2s_tx``
|
||||
|
||||
This sample has been tested on :ref:`mimxrt1060_evk` (mimxrt1060_evkb)
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
The code can be found in :zephyr_file:`samples/drivers/i2s/output`.
|
||||
|
||||
To build and flash the application:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/drivers/i2s/output
|
||||
:board: mimxrt1060_evkb
|
||||
:goals: build flash
|
||||
:compact:
|
2
samples/drivers/i2s/output/boards/mimxrt1060_evkb.conf
Normal file
2
samples/drivers/i2s/output/boards/mimxrt1060_evkb.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Raise DMA TCD Queue size, as this is required by the I2S SAI driver
|
||||
CONFIG_DMA_TCD_QUEUE_SIZE=4
|
11
samples/drivers/i2s/output/boards/mimxrt1060_evkb.overlay
Normal file
11
samples/drivers/i2s/output/boards/mimxrt1060_evkb.overlay
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
i2s-tx = &sai1;
|
||||
};
|
||||
};
|
1
samples/drivers/i2s/output/prj.conf
Normal file
1
samples/drivers/i2s/output/prj.conf
Normal file
|
@ -0,0 +1 @@
|
|||
CONFIG_I2S=y
|
9
samples/drivers/i2s/output/sample.yaml
Normal file
9
samples/drivers/i2s/output/sample.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
sample:
|
||||
description: I2S Output Sample
|
||||
name: i2s_output
|
||||
common:
|
||||
tags: drivers
|
||||
depends_on: i2s
|
||||
tests:
|
||||
sample.drivers.i2s.output:
|
||||
filter: dt_alias_exists("i2s-tx")
|
128
samples/drivers/i2s/output/src/main.c
Normal file
128
samples/drivers/i2s/output/src/main.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/i2s.h>
|
||||
#include <zephyr/sys/iterable_sections.h>
|
||||
|
||||
#define SAMPLE_NO 64
|
||||
|
||||
/* The data represent a sine wave */
|
||||
static int16_t data[SAMPLE_NO] = {
|
||||
3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169,
|
||||
25329, 27244, 28897, 30272, 31356, 32137, 32609, 32767,
|
||||
32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169,
|
||||
20787, 18204, 15446, 12539, 9511, 6392, 3211, 0,
|
||||
-3212, -6393, -9512, -12540, -15447, -18205, -20788, -23170,
|
||||
-25330, -27245, -28898, -30273, -31357, -32138, -32610, -32767,
|
||||
-32610, -32138, -31357, -30273, -28898, -27245, -25330, -23170,
|
||||
-20788, -18205, -15447, -12540, -9512, -6393, -3212, -1,
|
||||
};
|
||||
|
||||
/* Fill buffer with sine wave on left channel, and sine wave shifted by
|
||||
* 90 degrees on right channel. "att" represents a power of two to attenuate
|
||||
* the samples by
|
||||
*/
|
||||
static void fill_buf(int16_t *tx_block, int att)
|
||||
{
|
||||
int r_idx;
|
||||
|
||||
for (int i = 0; i < SAMPLE_NO; i++) {
|
||||
/* Left channel is sine wave */
|
||||
tx_block[2 * i] = data[i] / (1 << att);
|
||||
/* Right channel is same sine wave, shifted by 90 degrees */
|
||||
r_idx = (i + (ARRAY_SIZE(data) / 4)) % ARRAY_SIZE(data);
|
||||
tx_block[2 * i + 1] = data[r_idx] / (1 << att);
|
||||
}
|
||||
}
|
||||
|
||||
#define NUM_BLOCKS 20
|
||||
#define BLOCK_SIZE (2 * sizeof(data))
|
||||
|
||||
#ifdef CONFIG_NOCACHE_MEMORY
|
||||
#define MEM_SLAB_CACHE_ATTR __nocache
|
||||
#else
|
||||
#define MEM_SLAB_CACHE_ATTR
|
||||
#endif /* CONFIG_NOCACHE_MEMORY */
|
||||
|
||||
static char MEM_SLAB_CACHE_ATTR __aligned(WB_UP(32))
|
||||
_k_mem_slab_buf_tx_0_mem_slab[(NUM_BLOCKS) * WB_UP(BLOCK_SIZE)];
|
||||
|
||||
static STRUCT_SECTION_ITERABLE(k_mem_slab, tx_0_mem_slab) =
|
||||
Z_MEM_SLAB_INITIALIZER(tx_0_mem_slab, _k_mem_slab_buf_tx_0_mem_slab,
|
||||
WB_UP(BLOCK_SIZE), NUM_BLOCKS);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
void *tx_block[NUM_BLOCKS];
|
||||
struct i2s_config i2s_cfg;
|
||||
int ret;
|
||||
uint32_t tx_idx;
|
||||
const struct device *dev_i2s = DEVICE_DT_GET(DT_ALIAS(i2s_tx));
|
||||
|
||||
if (!device_is_ready(dev_i2s)) {
|
||||
printf("I2S device not ready\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Configure I2S stream */
|
||||
i2s_cfg.word_size = 16U;
|
||||
i2s_cfg.channels = 2U;
|
||||
i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
|
||||
i2s_cfg.frame_clk_freq = 44100;
|
||||
i2s_cfg.block_size = BLOCK_SIZE;
|
||||
i2s_cfg.timeout = 2000;
|
||||
/* Configure the Transmit port as Master */
|
||||
i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER
|
||||
| I2S_OPT_BIT_CLK_MASTER;
|
||||
i2s_cfg.mem_slab = &tx_0_mem_slab;
|
||||
ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg);
|
||||
if (ret < 0) {
|
||||
printf("Failed to configure I2S stream\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Prepare all TX blocks */
|
||||
for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) {
|
||||
ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block[tx_idx],
|
||||
K_FOREVER);
|
||||
if (ret < 0) {
|
||||
printf("Failed to allocate TX block\n");
|
||||
return ret;
|
||||
}
|
||||
fill_buf((uint16_t *)tx_block[tx_idx], tx_idx % 3);
|
||||
}
|
||||
|
||||
tx_idx = 0;
|
||||
/* Send first block */
|
||||
ret = i2s_write(dev_i2s, tx_block[tx_idx++], BLOCK_SIZE);
|
||||
if (ret < 0) {
|
||||
printf("Could not write TX buffer %d\n", tx_idx);
|
||||
return ret;
|
||||
}
|
||||
/* Trigger the I2S transmission */
|
||||
ret = i2s_trigger(dev_i2s, I2S_DIR_TX, I2S_TRIGGER_START);
|
||||
if (ret < 0) {
|
||||
printf("Could not trigger I2S tx\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (; tx_idx < NUM_BLOCKS; ) {
|
||||
ret = i2s_write(dev_i2s, tx_block[tx_idx++], BLOCK_SIZE);
|
||||
if (ret < 0) {
|
||||
printf("Could not write TX buffer %d\n", tx_idx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* Drain TX queue */
|
||||
ret = i2s_trigger(dev_i2s, I2S_DIR_TX, I2S_TRIGGER_DRAIN);
|
||||
if (ret < 0) {
|
||||
printf("Could not trigger I2S tx\n");
|
||||
return ret;
|
||||
}
|
||||
printf("All I2S blocks written\n");
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue