/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/gpio.h>
#include <stdio.h>
#include "codec_header.h"
#define SAMPLE_NO 64
#define CHANNELS 2
#define NUM_BLOCKS 4
/* BLOCK_SIZE in bytes */
#define BLOCK_SIZE (CHANNELS * SAMPLE_NO * sizeof(int16_t))
const struct device *dev_i2s = DEVICE_DT_GET(DT_NODELABEL(i2s20));
/* Stereo interleaved buffers */
static int16_t tx_block[NUM_BLOCKS][CHANNELS * SAMPLE_NO];
/* Sine wave table */
static const int16_t sine[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
};
static void fill_buf(int16_t *buf, int shift)
{
for (int i = 0; i < SAMPLE_NO; i++) {
buf[2 * i] = sine[i] >> shift; /* Left */
buf[2 * i + 1] = sine[(i + SAMPLE_NO / 4) % SAMPLE_NO] >> shift; /* Right */
}
}
int main(void)
{
struct i2s_config i2s_cfg;
int ret;
printk("=== System start ===\n");
// POWER-UP DELAY
k_msleep(300);
// provide mclk clock
//mclk_timer();
// INIT I2C
ret = i2c_init_codec();
if (ret)
{
printk("Codec I2C init failed (%d). STOP.\n", ret);
while (1)
{
k_sleep(K_FOREVER);
}
}
printk("Codec I2C init OK\n");
// CHECK I2S DEVICE
if (!device_is_ready(dev_i2s)) {
printk("I2S device not ready\n");
return -ENODEV;
}
// CONFIGURE I2S
memset(&i2s_cfg, 0, sizeof(i2s_cfg));
i2s_cfg.word_size = 16;
i2s_cfg.channels = CHANNELS;
i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
i2s_cfg.frame_clk_freq = 8000;
i2s_cfg.block_size = BLOCK_SIZE;
i2s_cfg.timeout =1000;
i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;
ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg);
if (ret)
{
printk("I2S configure failed (%d)\n", ret);
return ret;
}
printk("I2S configured.........\n");
// PREPARE AUDIO BUFFERS
for (int i = 0; i < NUM_BLOCKS; i++)
{
fill_buf(tx_block[i], i % 3);
}
// QUEUE BUFFERS BEFORE START
ret = i2s_write(dev_i2s, tx_block[0], BLOCK_SIZE);
ret |= i2s_write(dev_i2s, tx_block[1], BLOCK_SIZE);
if (ret)
{
printk("I2S write failed (%d)\n", ret);
return ret;
}
printk("Initial TX buffers queued\n");
// NOW START I2S -lrck
ret = i2s_trigger(dev_i2s, I2S_DIR_TX, I2S_TRIGGER_START);
if (ret)
{
printk("I2S start failed (%d)\n", ret);
return ret;
}
printk("I2S started successfully\n");
// remaining buffer
int idx = 2;
while (1)
{
ret = i2s_write(dev_i2s, tx_block[idx % NUM_BLOCKS], BLOCK_SIZE);
// ret = i2s_write(dev_i2s, tx_block[idx % NUM_BLOCKS], BLOCK_SIZE);
if (ret == 0)
{
idx++;
k_msleep(1);
}
else if (ret == -EAGAIN)
{
i2s_trigger(dev_i2s, I2S_DIR_TX, I2S_TRIGGER_DRAIN);
i2s_trigger(dev_i2s, I2S_DIR_TX, I2S_TRIGGER_START);
k_msleep(2);
}
else
{
printk("I2S write error (%d)\n", ret);
break;
}
}
printk("i2s_write.............");
return 0;
}
ovelay file
&pinctrl {
i2c21_default: i2c22_default {
group1 {
psels =
<NRF_PSEL(TWIM_SCL, 1, 12)>,
<NRF_PSEL(TWIM_SDA, 1, 13)>;
bias-pull-up;
};
};
i2c21_sleep: i2c22_sleep {
group1 {
psels = <NRF_PSEL(TWIM_SCL, 1,12)>,
<NRF_PSEL(TWIM_SDA, 1, 13)>;
low-power-enable;
};
};
i2s20_default: i2s20_default {
group1 {
psels = <NRF_PSEL(I2S_SDIN, 1, 6)>, <NRF_PSEL(I2S_SCK_S, 1, 3)>;
};
};
i2s20_sleep: i2s20_sleep {
group1 {
psels = <NRF_PSEL(I2S_SDIN, 1, 6)>,
<NRF_PSEL(I2S_SDOUT, 1, 5)>,
<NRF_PSEL(I2S_SCK_S, 1, 3)>,
<NRF_PSEL(I2S_MCK, 1, 4)>,
<NRF_PSEL(I2S_LRCK_M, 1, 14)>;
};
};
};
&i2c21 {
status = "disabled";
pinctrl-0 = <&i2c22_default>;
pinctrl-1 = <&i2c22_sleep>;
pinctrl-names = "default", "sleep";
audiocodec: audiocodec@18 {
compatible = "i2c-device";
status = "okay";
reg = < 0x18 >;
};
};
&i2s20 {
status = "okay";
pinctrl-0 = <&i2s20_default>;
pinctrl-1 = <&i2s20_sleep>;
pinctrl-names = "default", "sleep";
};
/ {
custom_pins{
compatible = "gpio-keys";
codec_reset: codec-reset-node {
gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
label = "Codec Reset Pin";
};
};
};
&i2c21 {
status = "okay";
};
/ {
button0: button_0 {
compatible = "gpio-keys";
button0_key: button0_key {
gpios = <&gpio1 15 0>;
label = "MCUBoot Button";
};
};
leds {
compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>;
label = "User LED 0";
};
};
};
&uart20 {
status = "okay";
};
&uart20_default {
group1 {
psels = <NRF_PSEL(UART_TX, 1, 8)>, <NRF_PSEL(UART_RX, 1, 7)>;
};
};
/delete-node/ &{/pin-controller/i2c22_default/group2/};
&i2c22 {
status = "disabled";
};
/delete-node/ &button0;
/delete-node/ &{/pin-controller/i2s20_default/group5/};
/delete-node/ &{/pin-controller/i2s20_default/group2/};
/delete-node/ &{/pin-controller/i2s20_default/group4/};
/delete-node/ &{/pin-controller/i2s20_default/group3/};
should i implement mclk clock seprate in code ?? OK
[00:06:49.411,024] <inf> i2s_nrfx: I2S MCK frequency: 256000, actual PCM rate: 8000
[00:06:49.411,024] <inf> i2s_nrfx: I2S MCK frequency: 256000, actual PCM rate: 8000