How to config the tdm port on nrf54lm20

I want to try the tdm function on the nrf54lm20 dk.

But i have encountered problem in the first step.

I do not know how to add the tdm port and assign the pin.

Whin i enable the tdm in the device tree, there is nothin in this page.

only this code generated in the overlay files

I try to assign the pin:

But seems invalid...

Can you help me to pass the first step?

Thank you very much!

Parents
  • Hi,

     

    The TDM can be sourced via two clocks, as shown here:

    https://docs.nordicsemi.com/bundle/ps_nrf54LM20A/page/tdm.html#d1594e1438

     

    44100 Hz is not exactly possible with these two clock sources, so you will have a bit of a miss, but 210 Hz off is a bit much.

    I see that the example that I sent you is missing a crucial component, and that is starting the HFXO.

     

    Could you try adding this function (and calling it) on boot?

    https://github.com/nrfconnect/sdk-nrf/blob/main/samples/peripheral/radio_test/src/main.c#L28-L70

     

    Similar to this (headers and all added for you):

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/sys/iterable_sections.h>
    #include <zephyr/drivers/clock_control.h>
    #include <zephyr/drivers/clock_control/nrf_clock_control.h>
    #if NRF54L_ERRATA_20_PRESENT
    #include <hal/nrf_power.h>
    #endif /* NRF54L_ERRATA_20_PRESENT */
    
    #include <zephyr/logging/log.h>
    
    #define LOG_MODULE_NAME i2s_test
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    
    #define SAMPLE_NO 64
    #define NUM_BLOCKS 4
    #define BLOCK_SIZE (SAMPLE_NO * sizeof(int16_t))
    
    K_MEM_SLAB_DEFINE(tx_mem_slab, BLOCK_SIZE, NUM_BLOCKS, 4);
    
    const struct device *const i2s_dev = DEVICE_DT_GET(DT_ALIAS(i2s_node0));
    
    /* Audio samples*/
    static int16_t data[SAMPLE_NO];
    
    static void clock_init(void)
    {
    	int err;
    	int res;
    	struct onoff_manager *clk_mgr;
    	struct onoff_client clk_cli;
    
    	clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    	if (!clk_mgr) {
    		printk("Unable to get the Clock manager\n");
    		return;
    	}
    
    	sys_notify_init_spinwait(&clk_cli.notify);
    
    	err = onoff_request(clk_mgr, &clk_cli);
    	if (err < 0) {
    		printk("Clock request failed: %d\n", err);
    		return;
    	}
    
    	do {
    		err = sys_notify_fetch_result(&clk_cli.notify, &res);
    		if (!err && res) {
    			printk("Clock could not be started: %d\n", res);
    			return;
    		}
    	} while (err);
    
    #if NRF54L_ERRATA_20_PRESENT
    	if (nrf54l_errata_20()) {
    		nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_CONSTLAT);
    	}
    #endif /* NRF54L_ERRATA_20_PRESENT */
    
    #if defined(NRF54LM20A_ENGA_XXAA)
    	/* MLTPAN-39 */
    	nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART);
    #endif /* defined(NRF54LM20A_ENGA_XXAA) */
    
    	printk("Clock has started\n");
    }
    
    static int configure_stream(const struct device *dev_i2s, uint32_t frame_clk_freq)
    {
    	int err;
    	struct i2s_config i2s_cfg;
    
    	i2s_cfg.word_size = 16U;
    	i2s_cfg.channels = 1U;
    	i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S;
    	i2s_cfg.frame_clk_freq = frame_clk_freq;
    	i2s_cfg.block_size = BLOCK_SIZE;
    	i2s_cfg.timeout = 1000;
    	i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE;
    
    	i2s_cfg.mem_slab = &tx_mem_slab;
    	err = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg);
    	if (err < 0) {
    		LOG_ERR("Failed to configure I2S TX stream (%d)\n",
    			 err);
    		return err;
    	}
    
    	return 0;
    }
    
    static void fill_buf(int16_t *tx_block)
    {
    	memcpy(tx_block, data, sizeof(data));
    }
    
    int main(void)
    {
    	int err;
    	int tx_idx;
    	void *tx_block[NUM_BLOCKS];
    	clock_init();
    	LOG_INF("I2S Sample started");
    	/* Fill with a fixed reckognizable pattern to make
    	   any gaps apparent */
    	memset(data, 0xAA, sizeof(data));
    
        if (!device_is_ready(i2s_dev)) {
    		LOG_ERR("Device %s is not ready.", i2s_dev->name);
    		return -EIO;
    	}
    
    	err = configure_stream(i2s_dev, 44100);
    	if(err) {
    		LOG_ERR("configure_stream() failed. (err %d)", err);
    	}
    
    	/* Prepare TX data blocks */
    	for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) {
    		err = k_mem_slab_alloc(&tx_mem_slab, &tx_block[tx_idx],
    				       K_FOREVER);
    		if(err) {
    			LOG_ERR("k_mem_slab_alloc() failed. (err %d)", err);
    		}
    		fill_buf(tx_block[tx_idx]);
    	}
    
    	tx_idx = 0;
    
    	/* Prefill TX queue */
    	err = i2s_write(i2s_dev, tx_block[tx_idx++], BLOCK_SIZE);
    	if(err) {
    		LOG_ERR("i2s write() failed. (err %d)", err);
    	}
    
    	err = i2s_write(i2s_dev, tx_block[tx_idx++], BLOCK_SIZE);
    	if(err) {
    		LOG_ERR("i2s write() failed. (err %d)", err);
    	}
    
    	i2s_trigger(i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START);
    
    	/* Keep alternating between the two buffers */
    	for(;;) {
    		
    		tx_idx = tx_idx & (NUM_BLOCKS-1);	
    		err = i2s_write(i2s_dev, tx_block[tx_idx], BLOCK_SIZE);
    		if(err) {
    			LOG_ERR("i2s write() failed. (err %d)", err);
    			return 1;
    		}
    
    		err = k_mem_slab_alloc(&tx_mem_slab, &tx_block[tx_idx],
    				       K_FOREVER);
    		if(err) {
    			LOG_ERR("k_mem_slab_alloc() failed. (err %d)", err);
    		}
    		fill_buf(tx_block[tx_idx]);
    		tx_idx++;
    	}
    
    	return 0;
    }

     

    Kind regards,

    Håkon

  • I noticed that sample in zip archive configures I2S/TDM with

    i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;

    While code snippet (that You pasted) with starting HFXO sets

    i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE;

    Is this intentional?

    I tried i2s_samplerate test with and without HFXO started.
    Without HFXO started I see low clock accuracy and strong temperature dependency.
    Whereas, with HFXO started I'm able to get

    Target values are:
     -  8000 Sps: 8000 +/- 10
     - 16000 Sps: 15873 +/- 10
     - 32000 Sps: 32258 +/- 10
     - 44100 Sps: 43478 +/- 10
     - 48000 Sps: 47619 +/- 10
     - 88200 Sps: 90909 +/- 15
     - 96000 Sps: 100000 +/- 15


    I don't know how to correctly switch TDM peripheral to ACLK.

Reply
  • I noticed that sample in zip archive configures I2S/TDM with

    i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER;

    While code snippet (that You pasted) with starting HFXO sets

    i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE;

    Is this intentional?

    I tried i2s_samplerate test with and without HFXO started.
    Without HFXO started I see low clock accuracy and strong temperature dependency.
    Whereas, with HFXO started I'm able to get

    Target values are:
     -  8000 Sps: 8000 +/- 10
     - 16000 Sps: 15873 +/- 10
     - 32000 Sps: 32258 +/- 10
     - 44100 Sps: 43478 +/- 10
     - 48000 Sps: 47619 +/- 10
     - 88200 Sps: 90909 +/- 15
     - 96000 Sps: 100000 +/- 15


    I don't know how to correctly switch TDM peripheral to ACLK.

Children
  • Oh, that was not my intention! It should be _CLK_MASTER, not _CLK_SLAVE. I will try see if I can edit the post.

    I tried i2s_samplerate test with and without HFXO started.
    Without HFXO started I see low clock accuracy and strong temperature dependency.
    Whereas, with HFXO started I'm able to get

    If you check my reply further up:

    Wanted CLK signal is 2*16bit*44.1kHz = 1411200 Hz.

    Since 32M/1.4112 MHz is not equal to an integer, it the nearest is 32M / 23 = 1.3913 MHz.

    You can see that this matches almost exactly with the "wanted 44.1k" measurement that you have:

    43478*32 = 1391296 Hz, ie. 1.39128 MHz.

    I don't know how to correctly switch TDM peripheral to ACLK.

    This is currently not supported by NCS, but logged internally as a feature request.

     

    Kind regards,

    Håkon

Related