Problem with I2S+DMA on SAMD51 - Page 1
Forum section: electronics / general

author: PeterZ
author_rank: Regular Contributor
timestamp: June 21, 2019, 03:29:01 pm
content: Hello all!i am working on a audio related project using the SAMD51J20 MCU. I have been trying to make the I2S work with DMA driven dual buffers, but i'm experiencing a weird problem with configuring the I2S serializers. The code does not use any external drivers, my goal is to make it work by talking directly to registers.Background:According to the datasheet and various code examples i could find, there a two serializer blocks in the I2S of SAMD/E5x MCU. One is used as transmitter (idx = 0) and the second as receiver (idx=1).My project is using a Master mode, where the MCU generatels all the required I2S clocks for the codec.So farI have configured all the clocks, pin muxing and the I2S parameters (Fs, bit depth, slots etc) This is all tested and (almost) working.DMA channels are configured using linked descriptors to transfer packets of data using double buffers.Codec chip is cofigured correctly and receives/transmits data (tested with logic analyzer).I2S TX side works, tested by populating the TX buffers with some data. I can see the analog signal on the codec's DAC outputs.The problem is with the RX side.I'm not sure if i understood the datasheet correctly about the two serializers being used as TX (0) and RX (1). The datasheet sugests it is the case:In the pinMux table, although there are separate MCLK, BCLK and FSCLK outputs for both serializers, there is only one pair of data pins: I2S_SDO and I2S_SDI.One could assume the SDO will be tied to the serializer 0 (Tx) and the SDI to the serializer 1 (Rx) if the pin muxes are configured to bank J.However, i'm constantly getting the RXRDY0 and RXOR0 (serializer 0) triggered instead of RXRDY1 and RXOR1:There is one interesting thing more, the TXCTRL register and SERMODE bit field.The datasheet says the default state is 0, which presumably makes the serializer work as receiver. To make it work as a TX, bit 0 should be set to 1, but it seems writing to that place has no effect. It will be reverted to 0. Ontop of that the SERMODE bitfield is not implemented in Atmel SAMD51_DFP included in the Atmel Studio 7. These two bits are marked as reserved.My initial thoughts on that was, well, the reg is called TXCTRL, so maybe it's function is fixed to TX and trying to change it has no effect. On the other hand, what is the point of adding a TXRDY1 (transmit ready) interrupt flag to serializer 1, which can't be set to transmit data (RXCTRL reg, SERMODE[1:0] has only two "receive" settings)?I have tried to clock the serializer 1 from both sources: CLK0 and CLK1 (i2s internal clocks, CLKSEL in RXCTRL reg), the results are the same.Thinking there might be some bug in the pin muxing, i have tried another pin available for the SDI - PB10 (i'm using PA22 in my circuit), again the results are the same.Here is my i2s init code:Code:void i2s_init(void){populateTXbuf();// write some data to the buffers for testingi2s_config_dma_for_rx();i2s_config_dma_for_tx();HAL_GPIO_LED_TX_out();HAL_GPIO_LED_RX_out();// configure pins// set MCLK output = PB17PORT->Group.WRCONFIG.reg =PORT_WRCONFIG_PMUX(MUX_PB17J_I2S_MCK0)| PORT_WRCONFIG_WRPMUX| PORT_WRCONFIG_PMUXEN| PORT_WRCONFIG_WRPINCFG| PORT_WRCONFIG_HWSEL| ((1 << (I2S_MCLK_PIN - 16) & 0xffff));// set BCLK output = PB16PORT->Group.WRCONFIG.reg =PORT_WRCONFIG_PMUX(MUX_PB16J_I2S_SCK0)| PORT_WRCONFIG_WRPMUX| PORT_WRCONFIG_PMUXEN| PORT_WRCONFIG_WRPINCFG| PORT_WRCONFIG_HWSEL| ((1 << (I2S_BCLK_PIN - 16) & 0xffff));// set LRCLK output = PA20PORT->Group.WRCONFIG.reg =PORT_WRCONFIG_PMUX(MUX_PA20J_I2S_FS0)| PORT_WRCONFIG_WRPMUX| PORT_WRCONFIG_PMUXEN| PORT_WRCONFIG_WRPINCFG| PORT_WRCONFIG_HWSEL| ((1 << (I2S_LRCLK_PIN - 16) & 0xffff));// set DOUT output = PA21PORT->Group.WRCONFIG.reg =PORT_WRCONFIG_PMUX(MUX_PA21J_I2S_SDO)| PORT_WRCONFIG_WRPMUX| PORT_WRCONFIG_PMUXEN| PORT_WRCONFIG_WRPINCFG| PORT_WRCONFIG_HWSEL| ((1 << (I2S_DOUT_PIN - 16) & 0xffff));// set DIN input = PA22PORT->Group.WRCONFIG.reg =PORT_WRCONFIG_PMUX(MUX_PA22J_I2S_SDI)| PORT_WRCONFIG_WRPMUX| PORT_WRCONFIG_PMUXEN| PORT_WRCONFIG_WRPINCFG| PORT_WRCONFIG_HWSEL| ((1 << (I2S_DIN_PIN - 16) & 0xffff));// I2S interruptsNVIC_ClearPendingIRQ(I2S_IRQn);// Clear any Nested Vector Interrupt Controller (NVIC) pending interruptsNVIC_SetPriority(I2S_IRQn, 2);// Set the Nested Vector Interrupt Controller (NVIC) (0 = highest)NVIC_EnableIRQ(I2S_IRQn);// Connect selected I2S to the Nested Vector Interrupt Controller (NVIC)MCLK->APBDMASK.reg |= MCLK_APBDMASK_I2S;// enable the I2S interface// set up clock// source GCLK5 = 17.1429MHzGCLK->PCHCTRL.reg = GCLK_PCHCTRL_GEN(5) | GCLK_PCHCTRL_CHEN;// TXwhile (0 == (GCLK->PCHCTRL.reg & GCLK_PCHCTRL_CHEN));GCLK->PCHCTRL.reg = GCLK_PCHCTRL_GEN(5) | GCLK_PCHCTRL_CHEN;// RXwhile (0 == (GCLK->PCHCTRL.reg & GCLK_PCHCTRL_CHEN));I2S->CTRLA.bit.SWRST = 1;// soft resetwhile(I2S->SYNCBUSY.bit.SWRST);// transmitterI2S->CLKCTRL.reg =I2S_CLKCTRL_MCKOUTDIV(0)|/* Mclk = GCLK5 */I2S_CLKCTRL_MCKEN|/* Enable Mclk output */I2S_CLKCTRL_SLOTSIZE(I2S_CLKCTRL_SLOTSIZE_16_Val)|/* 16 bit per sample */I2S_CLKCTRL_NBSLOTS(1)|/* 2 slots for each frame */I2S_CLKCTRL_MCKDIV(I2S_MCKDIV)|/* Mclk divider->BCLK */I2S_CLKCTRL_BITDELAY|I2S_CLKCTRL_FSWIDTH_SLOT;// receiverI2S->CLKCTRL.reg =I2S_CLKCTRL_MCKOUTDIV(0)|/* Mclk = GCLK5 */I2S_CLKCTRL_SLOTSIZE(I2S_CLKCTRL_SLOTSIZE_16_Val)|/* 16 bit per sample */I2S_CLKCTRL_NBSLOTS(1)|/* 2 slots for each frame */I2S_CLKCTRL_MCKDIV(I2S_MCKDIV)|/* Mclk divider->BCLK */I2S_CLKCTRL_BITDELAY|I2S_CLKCTRL_FSWIDTH_SLOT;// (receiver mode is default)I2S->TXCTRL.reg =I2S_TXCTRL_DATASIZE_16 + 0x01;// trying to write 1 to bit0 (SERMODE) to set the mode to TX// but the bit is always reverted to 0// Serializer0 is configuredI2S->RXCTRL.reg = I2S_RXCTRL_SERMODE_RX | I2S_RXCTRL_DATASIZE_16 | I2S_RXCTRL_CLKSEL_CLK0;I2S->INTENSET.reg = I2S_INTENSET_TXUR0 | I2S_INTENSET_RXOR1 | I2S_INTENSET_RXRDY1;// enable TX underrun and RX overrun interruptsDMAC->Channel.CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;// enable TX dma channelDMAC->Channel.CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;// enable RX dma channelI2S->CTRLA.reg |= I2S_CTRLA_ENABLE;// enable i2swhile(I2S->SYNCBUSY.bit.ENABLE);I2S->CTRLA.bit.CKEN0 = 1;// enable ck0while (I2S->SYNCBUSY.bit.CKEN0);I2S->CTRLA.bit.CKEN1 = 1;// enable ck1while (I2S->SYNCBUSY.bit.CKEN1);I2S->CTRLA.reg |= I2S_CTRLA_RXEN;// enable receiverwhile (I2S->SYNCBUSY.bit.RXEN);I2S->CTRLA.reg |= I2S_CTRLA_TXEN;// enable transmitterwhile (I2S->SYNCBUSY.bit.TXEN);}Am i missing something obvious? After two days working on the i2s only it is possible...The core of the problem comes down to:why is the serializer 0 receiving data and triggering the RX interrupts, while succesfully transmitting data using DMA memory -> I2S->TXDATA transfers.Thanks in advance for any thoughs or advices you might have.And special thanks to ataradov for his starter code for the atmel SAMDs!

author: ataradov
author_rank: Super Contributor
timestamp: June 21, 2019, 05:35:00 pm
content: Here is example code that works for me for both TX and RX.SERMODE is a mistake, and it was already noted for fixing.I serially have no idea about the logic behind RXRDY0/1 and TXRDY0/1.In this case I had to use I2S_DMAC_ID_RX_0 and I2S_DMAC_ID_TX_0 as DMA channels. Again, no idea why.

author: PeterZ
author_rank: Regular Contributor
timestamp: June 23, 2019, 12:09:45 pm
content: Thanks a lot!I got it working by hooking up the RX DMA to I2S_DMAC_ID_TX_0 instead of I2S_DMAC_ID_TX_1.I have the impression that at some point the silicon design of the D/E5x was optimized/changed (serializer funtcions fixed to TX and RX, not the case with CM0+ D2x family i.e.), but the register map hasn't been updated. Not to mention the datasheet.Perhaps the INTFLAG and the corresponding INTENSET/INTENCLR registers should be mapped like this:Since that's how the hardware seems to work. Somewhere in the design process the serializers were fixed to TX and RX mode and the interrupt lines from the ser1 (RX) were routed to the bits used by the RX part of the ser0, at this point no longer used/needed.Now the next step - get the QSPI to work with external RAM.

author: ataradov
author_rank: Super Contributor
timestamp: June 23, 2019, 05:37:08 pm
content: D21 is supposed to use the same IP (it is marked as the same version). But I guess different configurations are possible. I still see no reason for bits to change meaning. I will investigate this.