Is SSC busted? Details of I2S struggles

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys, 

 

I'm using the SSC and PDCA in an AT32UC3A3256 to implement two-way I2S. For now I'm focusing on the TX side where both SCLK and LRCK are generated by the MCU. I keep reading rumors of SSC silicon bugs. Have you had any success with reliably using the SSC for I2S signas? 

 

The code I'm working on is open source. I'd like to share any workarounds for SSC-I2S.

 

In my code there is non-zero probability that the left and right channels are swapped, i.e. polarity inversion on LRCK. I'm able to reduce the probabiltiy by timing calls to pdca_enable(), pdca_init_channel() and pdca_enable_interrupt_reload_counter_zero() relative to LRCK edges. But I'm not able to reduce the probablility of channel swap to zero. Strangely, the probability of inversion seems higher on the 2nd reset after DFU. I can try to sqeeze the inversion issue out of my code, but when I change completely unrelated code it comes right back in.

 

There is a report of a race condition in ssc_pdc_start() at the bottom of this page: http://www.ultimaserial.com/avr_lwip_tips4.html

 

In the picture you see how LRCK is strange for the first period. I set PIN_PX18 just before I call ssc_i2s_init(). Is this full period of LRCK beign 1 to be expected? MCLK is provided externally, and SCLK is correctly generated by the MCU. 

 

 

Any input appreciated!

 

 

Thanks,

Børge

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The ssc_i2s_init code which is called is below. 

 

Are you aware of 

ssc->cr = txen_mask | rxen_mask;

having to be called after the varios pdca inits?

 

sample_frequency = 48000;
data_bit_res = 24; // Same poor functionality with 32
frame_bit_res = 32;

  else if (mode == SSC_I2S_MODE_STEREO_OUT_STEREO_IN)
  {
	  ssc->cmr = AVR32_SSC_CMR_DIV_NOT_ACTIVE << AVR32_SSC_CMR_DIV_OFFSET;
      /* Set transmit clock mode:
       *   CKS - use TK pin.  Signal from GCLK1
       *   CKO - no output on TK.  Input only.
       *   CKI - shift data on falling clock
       *   CKG - transmit continuous clock on TK
       *   START - on any TF(WS) edge
       *   STTDLY - TF toggles before last bit of last word, not before
       *            first bit on next word. Therefore: delay one cycle.
       *   PERIOD - generate framesync for each sample (FS is generated
       *            every (PERIOD + 1) * 2 clock)
       */
      ssc->tcmr = AVR32_SSC_TCMR_CKS_TK_PIN	                << AVR32_SSC_TCMR_CKS_OFFSET    |
                  AVR32_SSC_TCMR_CKO_INPUT_ONLY				<< AVR32_SSC_TCMR_CKO_OFFSET    |
                  0                                         << AVR32_SSC_TCMR_CKI_OFFSET    |
                  AVR32_SSC_TCMR_CKG_NONE                   << AVR32_SSC_TCMR_CKG_OFFSET    |
//                AVR32_SSC_TCMR_START_DETECT_FALLING_TF	   << AVR32_SSC_TCMR_START_OFFSET  |	// Only transmit on falling BSB 20170605 see ssc_320.h
                  AVR32_SSC_TCMR_START_DETECT_ANY_EDGE_TF   << AVR32_SSC_TCMR_START_OFFSET  |		// Transmit on rising and falling
//                AVR32_SSC_TCMR_START_DETECT_LEVEL_CHANGE_TF   << AVR32_SSC_TCMR_START_OFFSET  |
                  1                                         << AVR32_SSC_TCMR_STTDLY_OFFSET |
                  (frame_bit_res - 1)                       << AVR32_SSC_TCMR_PERIOD_OFFSET;

    /* Set transmit frame mode:
     *  DATLEN - one sample for one channel
     *  DATDEF - Default to zero,
     *  MSBF - transmit msb first,
     *  DATNB - Transfer two words (left+right),
     *  FSLEN - Frame sync is entire left channel
     *  FSOS - transmit negative pulse on WS (start sync on left channel)
     *  FSDEN - Do not use transmit frame sync data
     *  FSEDGE - detect frame sync positive edge
     */

    ssc->tfmr = (data_bit_res - 1)                                 << AVR32_SSC_TFMR_DATLEN_OFFSET                              |
                0                                                  << AVR32_SSC_TFMR_DATDEF_OFFSET                              |
                1                                                  << AVR32_SSC_TFMR_MSBF_OFFSET                                |
                (1 - 1)                                            << AVR32_SSC_TFMR_DATNB_OFFSET                               |
                (((frame_bit_res - 1)                              << AVR32_SSC_TFMR_FSLEN_OFFSET) & AVR32_SSC_TFMR_FSLEN_MASK) |
                AVR32_SSC_TFMR_FSOS_NEG_PULSE                      << AVR32_SSC_TFMR_FSOS_OFFSET                                |
                0                                                  << AVR32_SSC_TFMR_FSDEN_OFFSET                               |
                1                                                  << AVR32_SSC_TFMR_FSEDGE_OFFSET                              |
                ((frame_bit_res - 1) >> AVR32_SSC_TFMR_FSLEN_SIZE) << AVR32_SSC_TFMR_FSLENHI_OFFSET;

    txen_mask = AVR32_SSC_CR_TXEN_MASK;

	     /* Set receive clock mode:
	       *  CKS - use RK pin
	       *  CKO - No clock output,
	       *  CKI - shift data on rising edge,
	       *  CKG - No clock output,
	       *  START -  v76 On rising edge of the FRAME_SYNC input which is connected to FSYNC
	       *  START -  v77 On level change of LRCK if SSC_RX_FS is connected to AD_LRCK
	       *  STTDLY - v77 i2s data starts one SCLK after LRCK change
	       *  STTDLY - v76 i2s data starts zero SCLK, ie immediately on FSYNC
	       *  PERIOD - No FS generation
	       */

	  ssc->rcmr = (AVR32_SSC_RCMR_CKS_RK_PIN << AVR32_SSC_RCMR_CKS_OFFSET) |
	                (1                             << AVR32_SSC_RCMR_CKI_OFFSET)|
	                (AVR32_SSC_RCMR_CKO_INPUT_ONLY << AVR32_SSC_RCMR_CKO_OFFSET) |
//  I2S specs says data starts one SCLK after LRCK edge.  However testing shows that
//  AK data starts showing up immediately
//	                (1                         << AVR32_SSC_RCMR_STTDLY_OFFSET ) |
	    	        (0                        << AVR32_SSC_RCMR_STTDLY_OFFSET ) |
//	                (AVR32_SSC_RCMR_START_DETECT_FALLING_RF << AVR32_SSC_RCMR_START_OFFSET);
					(AVR32_SSC_DETECT_LEVEL_CHANGE_RF << AVR32_SSC_RCMR_START_OFFSET);
//	                (AVR32_SSC_RCMR_START_DETECT_RISING_RF << AVR32_SSC_RCMR_START_OFFSET);

      ssc->rfmr = (data_bit_res - 1)                               << AVR32_SSC_RFMR_DATLEN_OFFSET                              |
                   1                                               << AVR32_SSC_RFMR_MSBF_OFFSET                                |
                   (1 - 1)                                         << AVR32_SSC_RFMR_DATNB_OFFSET                               |
                   (((frame_bit_res - 1)                           << AVR32_SSC_RFMR_FSLEN_OFFSET) & AVR32_SSC_RFMR_FSLEN_MASK) |
                   AVR32_SSC_RFMR_FSOS_INPUT_ONLY                  << AVR32_SSC_RFMR_FSOS_OFFSET                                |
                   1                                               << AVR32_SSC_RFMR_FSEDGE_OFFSET                              |
                   ((frame_bit_res - 1) >> AVR32_SSC_RFMR_FSLEN_SIZE) << AVR32_SSC_RFMR_FSLENHI_OFFSET;

        rxen_mask = AVR32_SSC_CR_RXEN_MASK;

        /* Enable transceiver and/or receiver */
        ssc->cr = txen_mask | rxen_mask;
  }

 

Børge

 

Last Edited: Sat. Jun 10, 2017 - 10:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Børge,

 

Did you figure this out yet? I'm encountering the same issues with my SSC channel swap on an Atmel ARM chip. However, we have the same configuration; external crystal clocking the codec, LRCLK and BCLK provided by mcu. It's very interesting... maybe this has to do with the ARM chipset vs ATUC3 but if I start a bypass through the microcontroller, my channels (stereo input - > stereo output) will be in sync forever. However, if I connect USB, this will sometimes get it out of sync. If I unplug USB and restart the SSC/DMA/interrupts, sometimes the channels will swap again. However, they stay in "sync" for the rest of the time.

 

As for output, I have been working with a add/removal method Atmel has in one of their documentations to "sync" my USB/DAC and SSC transmits. Obviously I'd love to share but I haven't gotten anything yet either. I've emailed Atmel several times on this but they don't know why this is happening either.

 

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did not even know what SSC means, so I put it in duckduck.

https://duckduckgo.com/html?q=i2...

 

First hit is  AVR32788: AVR 32 How to use the SSC in I2S mode

http://www.atmel.com/Images/doc3...

It almost seems silly to me to post such something so obvious but you might have missed it and this AN claims to:

 

This application note describes how the

I²S protocol is handled on AVR32 devices

and gives important information about how to

get the best configuration for different

sample rates.

 

Or just ignore silly me :)

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Paul, I'm sure the OP has read all the documentation supplied by Atmel (I have referenced that article many times as well). The issue is not getting the SSC to work as I2S / transferring data between codec and mcu; the issue is that the SSC will swap the left and right channels. I've found that configurations I've tried will still lead to channel swapping. From what I can gather, the SSC doesn't really handle the buffer data / synchronize the audio data; it just sends it to its destination once it receives its data. 

 

It is interesting to notice that the OP's LRCLK's first period is strange. I wouldn't know why that's happening. I'm also looking for this non-zero probability of channel swapping as well hence my previous post. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rfoo wrote:
Hi Paul, I'm sure the OP
  I'd give it a probability of 0.98 (Not knowing him personally).

 

rfoo wrote:
notice that the OP's LRCLK's first period is strange
Yes I (and him) have probably also noticed it.

But it is not only "strange", but when looking at the timing the broad pulse seems to have exactly twice as wide as all the others, which suggest the channels are swapped right there.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com