i2s on ATSAME51 odd interrupt behavior - Page 1
Forum section: electronics / general

author: divermartin
author_rank: Newbie
timestamp: January 22, 2021, 05:41:38 am
content: Hello!I have been able to find preciously little on examples of i2s examples using the ATSAME51 series MCU's. The most useful post I've found is here from last yearhttps://www.eevblog.com/forum/microcontrollers/problem-with-i2sdma-on-samd51/, so I figured I'd post here for my odd problem, see if I get any takers.Okay, I've got a PCM1803A on the i2s lines, configured for a sample rate of 62.5kHz (24Mhz clock, easy math, yadda). I can receive data with it, 24bit, left justified, etc. I'm not doing DMA or anything funky - This implementation basically lives to process the incoming data and throw it out the other end, so right now I'm polling (you can yell at me later) I2S->INTFLAG.bit.RXRDY0, and reading I2S->RXDATA. I put in a quick line to bring an IO high/low to show a pulse each time an interrupt is generated, so I know that is occuring at 125kHz, lines up with the frame sync toggling.Now, here's the problem. My understanding is that when I read RXDATA, I should get slot (left channel), and then reading it again, I should get slot (right channel). However, I am getting an interrupt for every slot, not every frame (2 data words). When I read RXDATA after interrupt, I get the most recent data. When I read RXDATA again, I get the previous interrupts data. This is consistent from interrrupt to interrupt, like it's forgetting the FS0 state and just going "Well, I got data so... it goes here." and basically acting like a FIFO queue. This wouldn't be the end of the world, except now I have no way to know if the data is left or right to synchronize! Arrh!Anyone have a suggestion? As standard, I checked the datasheet errata, nothing there... I tried using INTFLAG.bit.RXRDY1, but that doesn't generate interrupts. I'm not using the Transmit syncronizer, so I don't have it configured... here's the configuration as I'm using it... I have tried other variations, like changing the FMT0/1 pins on the PCM1803A to do 24bit left justified (0 bit delay), FSWIDTH_HALF, FSWIDTH_SLOT (both yield the same result on my scope). My base configuration uses the Atmel start, but I've gone back and forth between bare metal configuration and the start config, nothing seems to change.There is only one thing I can think of, but I can't seem to prove it in any way - I think the RX Syncronizer might by default try somehow to use FS1 instead of FS0? I am using FS0 going to the PCM, and because RX is configured to use clock, I would think this would be okay, and I wouldn't need to loop FS0 -> FS1 or something silly. I've tried both with and without FS1 pin mux control...I2S->CTRLA.reg |= I2S_CTRLA_SWRST; //Reset i2swhile (I2S->SYNCBUSY.reg);while (I2S->CTRLA.bit.SWRST);I2S->CLKCTRL.reg = I2S_CLKCTRL_SLOTSIZE_24 | //24 bit slot size (0x2)I2S_CLKCTRL_NBSLOTS(1) | //Number of slots = 2 (n+1), have tried 1 slot, that breaks alltogether...I2S_CLKCTRL_FSWIDTH_HALF | // Have used FSWIDTH_SLOT = both produce a 62.5kHz FS signal, so one frame = one slot.I2S_CLKCTRL_BITDELAY_I2S | //I2S 1 bit delay)I2S_CLKCTRL_FSSEL_SCKDIV | //Divided Serial Clock n is used as Frame Sync n source (0x0)I2S_CLKCTRL_SCKSEL_MCKDIV | //Divided Master Clock n is used as Serial Clock n source (0x0)I2S_CLKCTRL_MCKSEL_GCLK | //Select GCK (0x0)I2S_CLKCTRL_MCKEN | //Enable MCK (0x1)I2S_CLKCTRL_MCKDIV(0x07) | //Set division to 8 (0x07 = 7+1), SDCLK = 3MHz, / 24 = 125kHz, 2 slots = 62.5kHz fsI2S_CLKCTRL_MCKOUTDIV(0x00);I2S->RXCTRL.reg = I2S_RXCTRL_SLOTDIS7 | //Don't need slots 2-7, just 0 & 1I2S_RXCTRL_SLOTDIS6 | //Have tried variations on not disabling slots, no change in behaviorI2S_RXCTRL_SLOTDIS5 |I2S_RXCTRL_SLOTDIS4 |I2S_RXCTRL_SLOTDIS3 |I2S_RXCTRL_SLOTDIS2 |I2S_RXCTRL_BITREV_MSBIT | //MSB First (i2s default)I2S_RXCTRL_WORDADJ_LEFT | //I2S Default Left AdjustI2S_RXCTRL_SERMODE_RX | //default (0x00)I2S_RXCTRL_CLKSEL_CLK0 | // Use clock module 0 for both RX & TXI2S_RXCTRL_SLOTADJ_LEFT | // 0x8 left adjustedI2S_RXCTRL_DATASIZE_24; //0x01 24 bit data sizeI2S->CTRLA.reg = I2S_CTRLA_ENABLE | I2S_CTRLA_CKEN0 | I2S_CTRLA_RXEN;<other setup code and stuff>while(1){if(I2S->INTFLAG.bit.RXRDY0){REG_PORT_OUTSET0 = PORT_PA15; //Pulse occurs @ 125kHzREG_PORT_OUTCLR0 = PORT_PA15;I2S->INTENCLR.bit.RXRDY0 = 1;rx_data=I2S->RXDATA.reg;rx_data=I2S->RXDATA.reg;<do something with rx_data[0/1]>}}Disclaimer: I am a Microchip employee, but from a waaaay different division that isn't even MCU related, this is a personal project of mine, so I don't have any good support contacts.

author: ataradov
author_rank: Super Contributor
timestamp: January 22, 2021, 06:42:58 am
content: There is no FIFO, you get one interrupt per received data slot. You can only read RXDATA once per interrupt.There is no way to synchronize LEFT/RIGHT channel, you just need to keep track and don't miss interrupts. It will always start consistently, but if you miss once interrupt, you will lose synchronization and channels will flip.

author: divermartin
author_rank: Newbie
timestamp: January 22, 2021, 06:44:58 am
content: Looks like... I'm a dummy. I've been beating my head against the wall for days, and I think this is the issue:if(I2S->INTFLAG.bit.RXOR0){I2S->INTFLAG.bit.RXOR0 = 1;//Clear flag//Read data , do stuff}Error #1: Using INTFLAG.bit.RXRDY0 instead of INTFLAG.bit.RXOR0Error #2: Using INTENCLR.bit.RXOR0 when trying to use RXOR0 - this never cleared the interrupt - failure to read datasheet, which says:Bits 4, 5 – RXORx Receive Overrun x [x=1..0]This flag is cleared by writing a '1' to it.This flag is set when a Receive Overrun condition occurs in Sequencer x, and will generate an interrupt request ifINTENCLR/SET.RXORx is set to '1'.Writing a '0' to this bit has no effect.Writing a '1' to this bit will clear the Receive Overrun x interrupt flag.Contrast to:Bits 0, 1 – RXRDYx Receive Ready x [x=1..0]This flag is cleared byreading from DATAx register orwriting a '1' to it.This flag is set when a Sequencer x has received a new data word, and will generate an interrupt request ifINTENCLR/SET.RXRDYx is set to '1'.Writing a '0' to this bit has no effect.Writing a '1' to this bit will clear the Receive Ready x interrupt flag.So now my interrupt runs at exactly 62.5kHz, and triggers after both channels have been read... woo!

author: ataradov
author_rank: Super Contributor
timestamp: January 22, 2021, 06:47:10 am
content: "I2S->INTFLAG.bit.RXOR0 = 1;//Clear flag" is the wrong pattern to clear flags. This will clear all the flags as it will do RMW operation. The correct way is "I2S->INTFLAG.reg = I2S_INTFLAG_RXOR0;"

author: divermartin
author_rank: Newbie
timestamp: January 22, 2021, 06:59:24 am
content: Ah, good point - I probably wouldn't have noticed the difference as I'm automatically clearing the other RX interrupt flag when I do the data read anyway... but hey, fewer cycles better I guess