Hi
I've recently switched to NCS v3.0.0 because it now has TDM peripheral support for nRF54H20, however since there is no specific examples to use TDM, I've used I2S example that comes with Zephyr (zephyr/samples/drivers/i2s/output).
I've set up my app.overlay file like so:
/* * Copyright (c) 2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ / { aliases { i2s-tx = &tdm131; }; }; &pinctrl { tdm131_default: tdm131_default { group1 { psels = <NRF_PSEL(TDM_SDOUT, 1, 9)>, <NRF_PSEL(TDM_SDIN, 1, 10)>, <NRF_PSEL(TDM_FSYNC_M, 1, 11)>, <NRF_PSEL(TDM_SCK_M, 2, 3)>; }; }; }; &tdm131 { status = "okay"; pinctrl-0 = <&tdm131_default>; pinctrl-names = "default"; // without this, there will be assert fault at i2s_nrfx_tdm.c:288 memory-regions = < &cpuapp_dma_region >; }; &audiopll { status = "okay"; frequency = <NRFS_AUDIOPLL_FREQ_AUDIO_44K1>; }; &gpio1 { status = "okay"; }; &gpio2 { status = "okay"; };
prj.conf has the following content
CONFIG_MAIN_STACK_SIZE=8192 CONFIG_LOG=y CONFIG_LOG_PROCESS_THREAD_SLEEP_MS=50 CONFIG_GPIO=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_TIMER_RANDOM_INITIAL_STATE=123456789 CONFIG_TIMER_RANDOM_GENERATOR=y CONFIG_SERIAL=y # because it at least do something when in interrupt driven mode CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_PM_DEVICE=y CONFIG_ASSERT=y CONFIG_PM_DEVICE_RUNTIME=y CONFIG_I2S=y CONFIG_I2S_LOG_LEVEL_DBG=y CONFIG_CBPRINTF_PACKAGE_LOG_LEVEL_OFF=y
And main.c repeats example code from zephyr/samples/drivers/i2s/output:
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/random/random.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/i2s.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
#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 10
#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 */
// __attribute__((section("DMA_RAM3x_APP")))
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);
const struct device *dev_i2s = DEVICE_DT_GET(DT_ALIAS(i2s_tx));
int main(void)
{
LOG_INF("starting");
// volatile uint32_t i = 0;
// while (i == 0) {
// k_msleep(100);
// }
void *tx_block[NUM_BLOCKS];
struct i2s_config i2s_cfg;
int ret;
uint32_t tx_idx;
if (!device_is_ready(dev_i2s)) {
LOG_INF("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) {
LOG_INF("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) {
LOG_INF("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) {
LOG_INF("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) {
LOG_INF("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) {
LOG_INF("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) {
LOG_INF("Could not trigger I2S tx\n");
return ret;
}
LOG_INF("All I2S blocks written\n");
return 0;
while (true) {
k_msleep(1000);
}
return 0;
}
Project builds fine, runs fine, i get expected logs and see that I2S has done working:
But I see no output on actual pins P1.09, P1.10, P1.11 and P2.03, they are always in low state.
I'm running this project on nRF54H20DK EngC (0.9.1), i've followed bring-up guide and transitioned it's lifecycle to ROT, updated SoC binaries to 0.9.6 and updated BICR beforehand.
Is there something else that is missing in this setup?