UltimaSerial Using AVR's Synchronous Serial Controller (SSC) and Peripheral Direct Memory Access Controller (PDCA)

Data logger
UltimaSerial

 

Windaq add-ons
Windaq Add-ons

 

Spectrogram
UltimaWaterfall

 

Ultimaserial XChart
XChart

 

FFT1024
FFT1024

 

Ultimaserial Classroom
Lessons

Frame-Sync synchronous serial communication is the perfect protocol for transmitting data of multiple channels.

Here is how a typical TDM (time-division multiplexed) Frame-Sync synchronous serial data communication should work:

  • Frame-Sync is always in-sync with the data from the first channel
  • There should be no Frame-sync before and after the data transfer
  • No matter when you inspect the data stream you know exactly how to align the data from different channels

With these features, this protocol will never result in channel rotation even when there are glitches on the lines

Hardware

To demonstrate this problem, we need two EVK1100s and designate one as transmitter and the other as receiver. 

Make the following wiring connections:

  1. Transmitter's PA16 to Receiver's PA17: Serial Data Out/In

  2. Transmitter's PA15 to Receiver's PA18: Serial Clock

  3. Transmitter's PA14 to Receiver's PA19: Serial Data Frame Sync

Software implementation for the transmitter  


ssc->tcmr = 0 << AVR32_SSC_TCMR_CKS_OFFSET |

1 << AVR32_SSC_TCMR_CKO_OFFSET |

1 << AVR32_SSC_TCMR_CKI_OFFSET |

0 << AVR32_SSC_TCMR_CKG_OFFSET |

5 << AVR32_SSC_TCMR_START_OFFSET |

1 << AVR32_SSC_TCMR_STTDLY_OFFSET |

255 << AVR32_SSC_TCMR_PERIOD_OFFSET;

 

ssc->tfmr = (24 - 1) << AVR32_SSC_TFMR_DATLEN_OFFSET |

1 << AVR32_SSC_TFMR_DATDEF_OFFSET |

1 << AVR32_SSC_TFMR_MSBF_OFFSET |

3 << AVR32_SSC_TFMR_DATNB_OFFSET |

(((0) << AVR32_SSC_TFMR_FSLEN_OFFSET) & AVR32_SSC_TFMR_FSLEN_MASK) |

2 << AVR32_SSC_TFMR_FSOS_OFFSET |

1 << AVR32_SSC_TFMR_FSDEN_OFFSET |

0 << AVR32_SSC_TFMR_FSEDGE_OFFSET |

((0) >> AVR32_SSC_TFMR_FSLEN_SIZE) << AVR32_SSC_TFMR_FSLENHI_OFFSET;

For detail settings/timing, please refer to the following screen captures from the logic analyzer

void out_overrun_callback(void)
{
      ssc_pdc_output(buffer_out,(SAMPLE_COUNT));
}

...

int main(void)
{
...
for (i=0; i<SAMPLE_COUNT; ){
      buffer_out[i++]=0x11111111; 
      buffer_out[i++]=0xFFFFFFFF;
      buffer_out[i++]=0xFFFFFFFF;
      buffer_out[i++]=0xFFFFFFFF;
}

.....

// Start SSC
ssc_pdc_start(DEFAULT_SAMPLE_RATE_HZ,
4,
24,
SAMPLE_COUNT,
FALSE,
&out_overrun_callback,
&in_underrun_callback,
FOSC0*4); //48Mhz


ssc_pdc_output(buffer_out,(SAMPLE_COUNT));
while (1);

}

As long as you don't write your codes to run into the problem in AVR's SSC output unit, you will have a good data stream. 

 

Software Implementation in the Receiver (For detail settings, please refer to the screen captures of the logic analyzer)

ssc->rcmr = 2 << AVR32_SSC_RCMR_CKS_OFFSET |

0 << AVR32_SSC_RCMR_CKO_OFFSET |

1 << AVR32_SSC_RCMR_CKI_OFFSET |

0 << AVR32_SSC_RCMR_CKG_OFFSET |

4 << AVR32_SSC_RCMR_START_OFFSET |

0 << AVR32_SSC_RCMR_STTDLY_OFFSET |

0 << AVR32_SSC_RCMR_PERIOD_OFFSET; 

 

ssc->rfmr = (data_bit_res - 1) << AVR32_SSC_RFMR_DATLEN_OFFSET |

1 << AVR32_SSC_RFMR_MSBF_OFFSET |

3 << AVR32_SSC_RFMR_DATNB_OFFSET |

(((0) << AVR32_SSC_RFMR_FSLEN_OFFSET) & AVR32_SSC_RFMR_FSLEN_MASK) |

0 << AVR32_SSC_RFMR_FSOS_OFFSET |

1 << AVR32_SSC_RFMR_FSEDGE_OFFSET |

0 << AVR32_SSC_RFMR_FSLENHI_OFFSET;

 

void in_underrun_callback(void)
{
     rdone=1;
}

int main(void)
{
....
ssc_pdc_start(DEFAULT_SAMPLE_RATE_HZ,
4,
24,
SAMPLE_COUNT,
FALSE,
&out_overrun_callback,
&in_underrun_callback,
FOSC0*4); //48Mhz

myrepeat:

ssc_pdc_input(buffer_in,(SAMPLE_COUNT));

while (rdone==0){
print_dbg(".");
}

print_dbg("\n.............\n");
for (i=0; i<80; ){
print_dbg_hex(buffer_in[i++]);
print_dbg(" ");
print_dbg_hex(buffer_in[i++]);
print_dbg(" ");
print_dbg_hex(buffer_in[i++]);
print_dbg(" ");
print_dbg_hex(buffer_in[i++]);
print_dbg("\n");
}

goto myrepeat;

This will receive a batch of data, print them out and reinitialize SSC/DMA for the next round

Result:

We run into the channel rotation business. Instead of getting 00111111 as the first channel, it can be anywhere depending sessions

.............
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF

.............
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF
00FFFFFF 00FFFFFF 00111111 00FFFFFF

.............
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF
00FFFFFF 00111111 00FFFFFF 00FFFFFF

Fix it

Thanks to the help from Heihopp of AVR Freak forum!

The problem is ssc_pdc_start and ssc_pdc_input from the Atmel example programs have a race condition. Tthe fix is to move the start of SSC to after the start of PDAC by commenting out by commenting out ssc->cr = AVR32_SSC_CR_RXEN_MASK in ssc_pdc_start and adding ssc->cr = AVR32_SSC_CR_RXEN_MASK to the end of ssc_pdc_input