Post Go back to editing

I2S passthrough on custom board

Thread Summary

The user encountered issues with I2S data signals on a custom board using ADSP-21565, PCM1808 ADC, and PCM5102 DAC. The problem was resolved by adding SPORT data and clock configuration APIs in the application code. The SRU configuration in system.svc is not mandatory if SRU and SRU2 functions are used in the application, and the SPORT driver in system.svc is not required for I2S audio passthrough if the necessary configurations are done via APIs.
AI Generated Content
Category: Hardware

Hello,

I am bringing up a custom board with ADSP-21565, and I2S ADC/DAC (PCM1808 and PCM5102).

I went through a lot of examples and explanations, but I am still struggling.

Currently I generate I2S clocks from an external clock coming on pin (DAI0 PIN08) routed to PCGA.

My ADC is on DAI0 and the DAC is on DAI1, so I used cross connections to share the clocks. PCG is configured as follow:

static void PCG_init(void)
{
	/* PCG A for I2S - ADC and DAC */
	ADI_PCG_CLK_INFO gClkInfoI2S =
	{
		ADI_PCG_CLK_EXT,	             /* Clock Source */
		8u,          					 /* Clock Divisor for 3.072 MHz = (24.576 MHz / 8) */
		false                            /* External Trigger */
	};

	ADI_PCG_FS_INFO gFsInfoI2S =		 /* 48k */
	{
		ADI_PCG_FS_EXT,		             /* Clock Source */
		512u,     						 /* Frame Sync Divisor for 48k = (24.576 MHz / 512) */
		256u,                            /* Pulse Width */
		1u,                              /* Phase */
		false,                           /* External Trigger */
		ADI_PCG_FSBYPASS_MODE_NORMAL     /* Bypass Mode */
	};

	adi_pcg_Init(ADI_PCG_DEV_A, &gClkInfoI2S, &gFsInfoI2S);
}

SRU is configured as follow:

void SRU_init(void)
{
	/* 20 bits to activate DAI0 and DAI1 inputs */
	*pREG_PADS0_DAI0_IE=0x000FFFFF;
	*pREG_PADS0_DAI1_IE=0x000FFFFF;

	/* MCLK input from DAI0 PIN8 to PCGA */
    SRU(DAI0_PB08_O, PCG0_EXTCLKA_I);
    SRU(LOW, DAI0_PBEN08_I);

    /* I2S ADC */
    /* SCLK output from PCGA to DAI0 PIN10 */
    SRU(PCG0_CLKA_O, DAI0_PB10_I);
    SRU(HIGH, DAI0_PBEN10_I);
    /* SCLK output from PCGA to SPORT 0A */
    SRU(DAI0_PB10_O, SPT0_ACLK_I);
    /* LRCK output from PCGA to DAI0 PIN9 */
    SRU(PCG0_FSA_O, DAI0_PB09_I);
    SRU(HIGH, DAI0_PBEN09_I);
    /* LRCK output from PCGA to SPORT 0A */
    SRU(DAI0_PB09_O, SPT0_AFS_I);
    /* ADC IN 0 to SPORT 1A */
    SRU(DAI0_PB19_O, SPT0_AD0_I);
    SRU(LOW, DAI0_PBEN19_I);

    /* I2S DAC */
    /* SCLK output from PCGA to DAI1 PIN8 and SPORT4A */
    SRU2(PCG0_CRS_CLKA_O, DAI1_PB08_I);
    SRU2(DAI1_PB08_O, SPT4_ACLK_I);
    SRU2(HIGH, DAI1_PBEN08_I);

    /* LRCK output from PCGA to DAI1 PIN7 and SPORT4A */
    SRU2(PCG0_CRS_FSA_O, DAI1_PB07_I);
    SRU2(DAI1_PB07_O, SPT4_AFS_I);
    SRU2(HIGH, DAI1_PBEN07_I);

    /* SPORT4A data to DAC - DAI1 PIN09 */
    SRU2(SPT4_AD0_O, DAI1_PB09_I);
    SRU2(HIGH, DAI1_PBEN09_I);
}

All clocks seem correct at the ADC and DAC pins, but not data signals. I suspect an issue with my SPORT configurations.

Here is data output of the DSP sent to the DAC, with test values. Frame signal below, data signal up:

Some bits correspond to my test values, but the 32 bits seem truncated, and 2 values out of 3 are full logic highs (which I never send).

My SPORTs are configured as follow:

static int SPORT_Init(void)
{
    /* SPORT return code */
    ADI_SPORT_RESULT result;

	/* Open the SPORT Device 0A */
    result = adi_sport_Open(SPORT_DEVICE_0, ADI_HALF_SPORT_A, ADI_SPORT_DIR_RX, ADI_SPORT_I2S_MODE, SPORT_0A_Memory, ADI_SPORT_MEMORY_SIZE, &h_SPORT_0A_Rx);
	if(result!=ADI_SPORT_SUCCESS)
	{
		printf("SPORT 0A open Failed\n");
		return FAILED;
	}

	/* Open the SPORT Device 4A */
	result = adi_sport_Open(SPORT_DEVICE_4, ADI_HALF_SPORT_A, ADI_SPORT_DIR_TX, ADI_SPORT_I2S_MODE, SPORT_4A_Memory, ADI_SPORT_MEMORY_SIZE, &h_SPORT_4A_Tx);
	if(result!=ADI_SPORT_SUCCESS)
	{
		printf("SPORT 4A open Failed\n");
		return FAILED;
	}

	/* Register SPORT Callback function */
	result = adi_sport_RegisterCallback(h_SPORT_0A_Rx, SPORT_Callback, NULL);
	check_result(result);
	result = adi_sport_RegisterCallback(h_SPORT_4A_Tx, SPORT_Callback, NULL);
	check_result(result);

	/* Prepare descriptors */
	SPORT_PrepareDescriptors();
	SPORT_PrepareDataBuffers();

	/* Submit the first buffer for Rx.  */
	result = adi_sport_DMATransfer(h_SPORT_0A_Rx, &SPORT_0A_descriptor_1_ADC, (DMA_NUM_DESC), ADI_PDMA_DESCRIPTOR_LIST, ADI_SPORT_CHANNEL_PRIM);
	check_result(result);

	result = adi_sport_DMATransfer(h_SPORT_4A_Tx, &SPORT_4A_descriptor_1_DAC, (DMA_NUM_DESC), ADI_PDMA_DESCRIPTOR_LIST, ADI_SPORT_CHANNEL_PRIM);
	check_result(result);

	/*Enable Sport Devices */
	result = adi_sport_Enable(h_SPORT_0A_Rx, true);
	check_result(result);
	result = adi_sport_Enable(h_SPORT_4A_Tx, true);
	check_result(result);

	return SUCCESS;
}

And DMA as follow:

/* ADC */
ADI_PDMA_DESC_LIST SPORT_0A_descriptor_1_ADC;
ADI_PDMA_DESC_LIST SPORT_0A_descriptor_2_ADC;
int32_t ADC_Buffer_1[BLOCK_SIZE * ADC_NB_CH];
int32_t ADC_Buffer_2[BLOCK_SIZE * ADC_NB_CH];
static uint8_t SPORT_0A_Memory[ADI_SPORT_MEMORY_SIZE];
static ADI_SPORT_HANDLE h_SPORT_0A_Rx;

/* DAC */
ADI_PDMA_DESC_LIST SPORT_4A_descriptor_1_DAC;
ADI_PDMA_DESC_LIST SPORT_4A_descriptor_2_DAC;
int32_t DAC_Buffer_1[BLOCK_SIZE * DAC_NB_CH];
int32_t DAC_Buffer_2[BLOCK_SIZE * DAC_NB_CH];
static uint8_t SPORT_4A_Memory[ADI_SPORT_MEMORY_SIZE];
static ADI_SPORT_HANDLE h_SPORT_4A_Tx;

volatile uint8_t SPORTCallbackCount = 0;
volatile uint32_t data_available=0;

void SPORT_PrepareDescriptors(void){
	/* ADC - Populate descriptor for SPORT 1 DMA transfer */
	SPORT_0A_descriptor_1_ADC.pStartAddr	= (int32_t*)ADC_Buffer_1;
	SPORT_0A_descriptor_1_ADC.Config		= ENUM_DMA_CFG_XCNT_INT;
	SPORT_0A_descriptor_1_ADC.XCount		= BLOCK_SIZE * ADC_NB_CH;
	SPORT_0A_descriptor_1_ADC.XModify		= 4;
	SPORT_0A_descriptor_1_ADC.YCount		= 0;
	SPORT_0A_descriptor_1_ADC.YModify		= 0;
	SPORT_0A_descriptor_1_ADC.pNxtDscp		= &SPORT_0A_descriptor_2_ADC;

	SPORT_0A_descriptor_2_ADC.pStartAddr	= (int32_t*)ADC_Buffer_2;
	SPORT_0A_descriptor_2_ADC.Config		= ENUM_DMA_CFG_XCNT_INT;
	SPORT_0A_descriptor_2_ADC.XCount		= BLOCK_SIZE * ADC_NB_CH;
	SPORT_0A_descriptor_2_ADC.XModify		= 4;
	SPORT_0A_descriptor_2_ADC.YCount		= 0;
	SPORT_0A_descriptor_2_ADC.YModify		= 0;
	SPORT_0A_descriptor_2_ADC.pNxtDscp		= &SPORT_0A_descriptor_1_ADC;

	/* DAC - Populate descriptor for SPORT 4 DMA transfer */
	SPORT_4A_descriptor_1_DAC.pStartAddr	= (int32_t*)DAC_Buffer_1;
	SPORT_4A_descriptor_1_DAC.Config		= ENUM_DMA_CFG_XCNT_INT ;
	SPORT_4A_descriptor_1_DAC.XCount		= BLOCK_SIZE * DAC_NB_CH;
	SPORT_4A_descriptor_1_DAC.XModify		= 4;
	SPORT_4A_descriptor_1_DAC.YCount		= 0;
	SPORT_4A_descriptor_1_DAC.YModify		= 0;
	SPORT_4A_descriptor_1_DAC.pNxtDscp		= &SPORT_4A_descriptor_2_DAC;

	SPORT_4A_descriptor_2_DAC.pStartAddr	= (int32_t*)DAC_Buffer_2;
	SPORT_4A_descriptor_2_DAC.Config		= ENUM_DMA_CFG_XCNT_INT ;
	SPORT_4A_descriptor_2_DAC.XCount		= BLOCK_SIZE * DAC_NB_CH;
	SPORT_4A_descriptor_2_DAC.XModify		= 4;
	SPORT_4A_descriptor_2_DAC.YCount		= 0;
	SPORT_4A_descriptor_2_DAC.YModify		= 0;
	SPORT_4A_descriptor_2_DAC.pNxtDscp		= &SPORT_4A_descriptor_1_DAC;
}

RX and TX callback work properly.

1-) can you please confirm my SRU config is correct?

2-) what is the default configuration of a SPORT? Does it always work with 32 bits?

3-) Can you help me understand the behavior I witness? 2 values out of 3 seem wrong

The DSP is master, ADC and DAC are configured as slave and I2S format.

Thank you,

Fabien

Thread Notes

Parents
  • Hi Fabien,

    Thank you for your inquiry.

    In your SPORT configuration function, it appears that the SPORT data and clock configuration APIs were not called. Could you please confirm that whether these configurations were done in static driver file under the driver folder. Please note that in the board support package SPORT example code these configurations are done in the static file(adi_sport_config_2156x.h) which is located at: project\system\drivers\sport.

    Also if possible, please share your project with us. This would help us isolate the issue and assist you further.

    Waiting for your reply.

    Best Regards,
    Santhakumari.V

  • Hi Santhakumari.

    Adding data clock and frame APIs made it work. Thank you!

    I have some difficulties understanding the relationship between system.svc settings and the application code.

    1-) Could you confirm that visually setting up SRU in system.svc will change system/sru/sru_config.c, which is the initial configuration of SRU. And it will be overwritten by SRU_init() in my application code? Can we say that setting up SRU visually in system.svc is never mandatory because we can use SRU and SRU2 functions instead?

    2-) Wether I add or remove the SPORT driver in system.svc > overview > installed add-ins, my I2S audio passthrough still works. Why is that, and what is the use of adding the driver?

    3-) 'Please note that in the board support package SPORT example code these configurations are done in the static file' I understand that this file provides an initial configuration of all SPORTs, and that it was modified directly to make SPORT examples of the BSP. In my case I now use APIs, which will override those values. Is that right? Once again, it is never mandatory to directly modify driver code if I use APIs?

    Best Regards,
    Fabien

  • Hello Fabien,

    Glad to know that your project is working fine. Please find the details for your queries here.
    1-) Could you confirm that visually setting up SRU in system.svc will change system/sru/sru_config.c, which is the initial configuration of SRU. And it will be overwritten by SRU_init() in my application code? Can we say that setting up SRU visually in system.svc is never mandatory because we can use SRU and SRU2 functions instead?
    >>> Yes, your understanding is correct. When configuring the SRU via the GUI (system.svc), the corresponding code will automatically generated in the system/sru/sru_config.c file. The GUI SRU function (adi_SRU_Init()) is called within the main() function, specifically inside the adi_initComponents() function. So, any SRU functions called in the application after adi_initComponents() will overwrite the SRU configurations defined in sru_config.c.

    2. Whether I add or remove the SPORT driver in system.svc > overview > installed add-ins, my I2S audio passthrough still works. Why is that, and what is the use of adding the driver?
    >>> Yes. The driver source files has been already linked via libdrv.dlb which was included in the default ldf file. While adding it via system.svc, helped to include static files as well as debug driver source codes. If you add the SPORT driver in system.svc, the static file "adi_sport_config_2156x.h" will be created in the following path:Project/ASRC_Audio_Passthrough/system/drivers/sport.

    3-) 'Please note that in the board support package SPORT example code these configurations are done in the static file' I understand that this file provides an initial configuration of all SPORTs, and that it was modified directly to make SPORT examples of the BSP. In my case I now use APIs, which will override those values. Is that right? Once again, it is never mandatory to directly modify driver code if I use APIs?
    >>> This static file allows you to configure all SPORT data and clock settings. These configurations are applied through the SPORT open API. However, if you call the SPORT data and clock configuration APIs in your application after initialization, it will overwrite the settings defined in the static file. So, it's enough to configure either via the static file or through the API, using both is unnecessary.

    Also, please note that the generated static file is specific to the particular project.

    Hope this helps.

    Best Regards,
    Santhakumari.V

  • Hello Santhakumari,

    Thank you for the precise answers. It is now clear on my side.

    I am now integrating SigmaStudio+ in this same project and I made another post about my issues:

     SigmaStudio+ hardware configuration versus CCES Target firmware 

    Best Regards,
    Fabien

Reply Children
No Data