This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

how to send i2s data by timer in NCS1.8.0?

Hello,

NCS1.8.0   Windows10  X64;

i2s test,see the source code ,

it runing ok;

but,when i change to timer send,got error.

i want to send data by timer,

source code
#include <zephyr.h>
#include <ztest.h>
#include <drivers/i2s.h>

//--------------------------------------------------------------------------------------
#define I2S_DEV_NAME_RX "I2S_0"
#define BLOCK_SIZE 160
static const struct device *dev_i2s_rxtx;
static bool dir_both_supported;
static int tx_data_idx=0;
#define TIMEOUT          2000
#define FRAME_CLK_FREQ   8000

K_MEM_SLAB_DEFINE(tx_32_mem_slab, BLOCK_SIZE, 20, 32);//loop memory
K_MEM_SLAB_DEFINE(rx_32_mem_slab, BLOCK_SIZE, 20, 32);//loop memory
void to_display_format(char* title,const uint8_t *src, size_t size)
{
	uint8_t buffer_print[BLOCK_SIZE * 5 + 1];
	for (int i = 0; i < size; i++) {
		sprintf(buffer_print + 5 * i, "0x%02x,", src[i]);
	}
	//LOG_ERR(title,dst);
	printk(title,buffer_print);
}
static int configure_stream(const struct device *dev_i2s, enum i2s_dir dir)
{
	int ret;
	struct i2s_config i2s_cfg;

	i2s_cfg.word_size = 16U;
	i2s_cfg.channels = 2U;
	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 = TIMEOUT;

	if (dir == I2S_DIR_TX) {
		/* Configure the Transmit port as Master */
		i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER
				| I2S_OPT_BIT_CLK_MASTER;
	} else if (dir == I2S_DIR_RX) {
		/* Configure the Receive port as Slave */
		i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE
				| I2S_OPT_BIT_CLK_SLAVE;
	} else { /* dir == I2S_DIR_BOTH */
		i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER
				| I2S_OPT_BIT_CLK_MASTER;
	}

	if (!IS_ENABLED(CONFIG_I2S_TEST_USE_GPIO_LOOPBACK)) {
		i2s_cfg.options |= I2S_OPT_LOOPBACK;
	}

	if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) {
		i2s_cfg.mem_slab = &tx_32_mem_slab;
		ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg);
		if (ret < 0) {
			TC_PRINT("Failed to configure I2S TX stream (%d)\n",
				 ret);
			return -TC_FAIL;
		}
	}

	if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) {
		i2s_cfg.mem_slab = &rx_32_mem_slab;
		ret = i2s_configure(dev_i2s, I2S_DIR_RX, &i2s_cfg);
		if (ret < 0) {
			TC_PRINT("Failed to configure I2S RX stream (%d)\n",
				 ret);
			return -TC_FAIL;
		}
	}

	return TC_PASS;
}

void i2s_dir_both_transfer_configure(void)
{
	dev_i2s_rxtx = device_get_binding(I2S_DEV_NAME_RX);
	zassert_not_null(dev_i2s_rxtx, "device " I2S_DEV_NAME_RX " not found");

	int ret = configure_stream(dev_i2s_rxtx, I2S_DIR_BOTH);
	zassert_equal(ret, TC_PASS, NULL);

	/* Check if the tested driver supports the I2S_DIR_BOTH value.
	 * Use the DROP trigger for this, as in the current state of the driver
	 * (READY, both TX and RX queues empty) it is actually a no-op.
	 */
	ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_DROP);
	dir_both_supported = (ret == 0);

	if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) {
		zassert_true(dir_both_supported, "I2S_DIR_BOTH value is supposed to be supported.");
	}
}

void i2s_transfer_send(void)
{
	if (!dir_both_supported) {
		TC_PRINT("I2S_DIR_BOTH value is not supported.\n");
		ztest_test_skip();
		return;
	}

	uint8_t *rx_block=NULL,*tx_block=NULL;
	size_t rx_size=0,num_verified=0;

	// Prepare TX data blocks 
	int ret = k_mem_slab_alloc(&tx_32_mem_slab, &tx_block,K_FOREVER);
	zassert_equal(ret, 0, NULL);
	//fill_buf((uint16_t *)tx_block, 0);
	for(int i=0;i<160;i++)
	{
		tx_block[i]=0xc0;
	}

	ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_START);
	zassert_equal(ret, 0, "RX/TX START trigger failed\n");

	/* Prefill TX queue */
	ret = i2s_write(dev_i2s_rxtx, tx_block, BLOCK_SIZE);
	zassert_equal(ret, 0, NULL);

	/* All data written, drain TX queue and stop both streams. */
	ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN);
	zassert_equal(ret, 0, "RX/TX DRAIN trigger failed");

	ret = i2s_read(dev_i2s_rxtx, &rx_block, &rx_size);
	zassert_equal(ret, 0, NULL);
	zassert_equal(rx_size, BLOCK_SIZE, NULL);

	TC_PRINT(" TX blocks sent: %d  %d\n", BLOCK_SIZE,tx_data_idx);
	TC_PRINT(" RX blocks received: %d\n\n", rx_size);
	// to_display_format("tx_block: %s\n",tx_block, BLOCK_SIZE);
	// to_display_format("rx_block: %s\n",rx_block, BLOCK_SIZE);

	// // Verify received data (loopback data not real time,need offset>=2)
	// ret = verify_buf((uint16_t *)rx_block, 0);
	// if (ret != 0) {
	// 	TC_PRINT("%d RX block invalid\n", 0);
	// }
	k_mem_slab_free(&rx_32_mem_slab, &rx_block);
	//zassert_equal(num_verified, 1, "Invalid RX blocks received");
}

//--------------------------------------------------------------------------------------
void timer_ms_handler(struct k_timer *dummy)
{
    //timer_lms++;
	i2s_transfer_send();
    //printk(" timer_ms_handler! \n");
}
K_TIMER_DEFINE(my_timer, timer_ms_handler, NULL);

void test_main(void)
{
	i2s_dir_both_transfer_configure();
	// start periodic timer that expires once every second 
    k_timer_start(&my_timer, K_MSEC(120), K_MSEC(120));	
	// for(int i=0;i<10;i++)
	// 	i2s_transfer_send();
	while(1){};
	k_timer_stop(&my_timer);
}
logs
*** Booting Zephyr OS build v2.7.0-ncs1  ***
I: I2S MCK frequency: 1391304, actual PCM rate: 43478
I: I2S MCK frequency: 1391304, actual PCM rate: 43478
ASSERTION FAIL [!arch_is_in_isr() || ((timeout).ticks == (((k_timeout_t) {})).ticks)] @ WEST_TOPDIR/zephyr/kernel/msg_q.c:118
	
E: r0/a1:  0x00000004  r1/a2:  0x00000076  r2/a3:  0x00000001
E: r3/a4:  0x00002ded r12/ip:  0x00000000 r14/lr:  0x00005ab3
E:  xpsr:  0x41000021
E: Faulting instruction address (r15/pc): 0x0000842c
E: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
E: Fault during interrupt handling

E: Current thread: 0x200003b8 (main)
E: Halting system
   

thanks

Best Regards

  • Hello,

    This API cannot be used from an interrupt context (timer callback in this case). That is why the assert is raised:

    ASSERTION FAIL [!arch_is_in_isr() || ((timeout).ticks == (((k_timeout_t) {})).ticks)] @ WEST_TOPDIR/zephyr/kernel/msg_q.c:118

    A solution is to use a semaphore like this:

    K_SEM_DEFINE(i2s_transfer, 0, 1);
    
    void timer_ms_handler(struct k_timer *dummy)
    {
    	k_sem_give(&i2s_transfer);
    }
    
    void test_main() 
    {
    	...
    	while(1) {
    		k_sem_take(&i2s_transfer, K_FOREVER);
    		i2c_transfer_send();
    	}
    }

    Best regards,

    Vidar

Related