Atmel website | ARM Community | AVR freaks | Technical Support
Banner
 FAQ •  Search •  Register •  Login 

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: Using SSC in "slave mode" on AT91SAM7S
PostPosted: Fri Sep 18, 2009 2:43 pm 
Offline

Joined: Fri Jun 12, 2009 1:54 pm
Posts: 17
Dear AT91SAM7 friends,

I integrated a voice codec using the SSC on the AT91SAM7S256. I ran into a lot of problems, so I am writing this post to make it easier for the next guy to figure this out.
The Atmel software package has no example code for SSC, and I have seen many posts saying "did anybody actually ever get the SSC to work in passive mode?" without getting responses, so that was discouraging.

When I first read the SSC section of the datasheet, I was very excited about the flexibility of the peripheral and all the new possibilities, since I have only worked with smaller MCUs before. Little did I know that the described features do not work in all combinations, which not only limits that flexibility, but makes development extremely frustrating since the limitations are not documented. There is an errata section in the datasheet that lists certain conditions that fail or work incorrectly, but it is in no way complete.

OK, so my codec is providing me a bit clock (BCLK) frame sync (FSYNC) and data (SOUT/SIN). By default it is spitting out data on the SOUT, which is great to get started. It expects me to send data using the same clock and FSYNC.

First I needed to program the PIO so it uses the pins for SSC. Since the atmel provided header file doesn't have definitions for the SSC pins, I created them manually and initialized them.

Atmel's peripheral library has convenience functions to set up and operate the ssc.
Add the #include <ssc/ssc.h> into your main file, and you can just use SSC_Configure to enable, reset, and program the clock.

Then I programmed the receiver registers, and voila, I got data. I then changed it to use DMA, no issues there. Note that in for DMA transfers, you use the "number of transfers" as a length indication, not number of bytes. In my case because I use 16 bit samples, I had to divide my buffer size by 2.

Sending was a censored. At first no matter what I did I couldn't get a signal on the TD pin to transmit. I even tried in "master mode", e.g. creating a DIV clock signal on TK, disconnecting the codec, not using the receiver. I never got data on TD. The telling sign was that I had something like 2V sitting on the pin, no matter how I programmed it through the PIO. Double checking the AT91SAM7S-EK development board's schematics I found that the board supports the D/A peripherals which one of them happens to be located on the same pin as the TD of the SSC. There are jumpers you can and must open, specifically JP24/25 to disconnect EXT_AD0 and EXT_AD1. After doing this, the data magically appeared on TD and I thought that I am golden.
This is when the trouble started. Since the codec required me to use its clock and send my data during its frame sync, naturally I decided to use RK/RF/RD on the input, and then program the transmitter to use TD, use the RK clock, and start sending when receive starts. Negative, I either get nothing or my data is all over the place or at least 3 bits delayed. After trying a thousand other things and reading everybody elses frustrated comments, I decide to go the reverse way: receiving the clock and fsync on the transmit side's pins TK/TF. To do this I had to program those pin on the PIO as inputs. My data now looks all screwed up. A look at the TK pin shows me a triangular wave where my clock should be, wtf?! Disconnecting the pin reveals that TK pin is riding high even though I had programmed it as an input. Checking the AT91SAM7S-EK schematics again, I learn that this pin is used as a pullup for USB USB_PUP. Opening jumper JP-1 solves this, and my clock now looks normal, and I get my data. It is still one clock cycle delayed, but setting the transmitter configuration correctly fixes this (see code below).

One thing you should know when you use the configuration macros: They don't differenciate between the receiver and transmitter, there is only one set of macros for both, so the terminology is confusing and in some cases flat out wrong as I will show you next.

In my code example I configure the transmitter to be AT91C_SSC_START_FALL_RF, but it really means falling TF. There is no specific macro for TF signals, since the values are the same. No big deal.
Be careful however with the macros AT91C_SSC_CKS_RK and AT91C_SSC_CKS_TK. THEY MUST BE REVERSED FOR THE TRANSMITTER. Check the numeric values: for the receiver its 0x01 to use TK and 0x02 to use RK. Its reversed for the transmitter, therefore the use of the macro must be reveresed as well.

- I hope this post will save some poor soul some time and trouble one day




#define PIN_SSC_TF {1 << 15, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_DEFAULT}
#define PIN_SSC_TK {1 << 16, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_DEFAULT}
#define PIN_SSC_TD {1 << 17, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
#define PIN_SSC_RD {1 << 18, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
#define PIN_SSC_RK {1 << 19, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
#define PIN_SSC_RF {1 << 20, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_PERIPH_A, PIO_DEFAULT}
static const Pin sscPins[] = {PIN_SSC_TD, PIN_SSC_TF, PIN_SSC_TK, PIN_SSC_RD, PIN_SSC_RK, PIN_SSC_RF};

unsigned byte readBuffer1[1024];
unsigned byte readBuffer2[1024];

void initialize()
{
// configure pins
PIO_Configure(sscPins, PIO_LISTSIZE(sscPins));

// this will enable and reset the ssc and NOT create a clock (because my goal is to use the codec's clock)
SSC_Configure(AT91C_BASE_SSC, AT91C_ID_SSC, 0, 0);

// disable ssc interrupts
AT91C_BASE_SSC->SSC_IDR = 0x0FFF;

// configure ssc receiver
AT91C_BASE_SSC->SSC_RCMR = AT91C_SSC_CKS_TK | AT91C_SSC_START_TX;
AT91C_BASE_SSC->SSC_RFMR = SSC_DATLEN(16) | AT91C_SSC_MSBF | SSC_DATNB(1) | AT91C_SSC_FSOS_NONE;

// configure ssc transmitter
AT91C_BASE_SSC->SSC_TCMR = AT91C_SSC_CKS_RK | AT91C_SSC_START_FALL_RF | AT91C_SSC_CKI;
AT91C_BASE_SSC->SSC_TFMR = SSC_DATLEN(16) | AT91C_SSC_MSBF | SSC_DATNB(1) | AT91C_SSC_FSOS_NONE;

// start reading using DMA the size must be supplied in "transfers", each transfer is 16bit
SSC_ReadBuffer(AT91C_BASE_SSC, readBuffer, sizeof(readBuffer1) / 2);
SSC_ReadBuffer(AT91C_BASE_SSC, readBuffer, sizeof(readBuffer2) / 2);

// configure interrupts
AIC_ConfigureIT(AT91C_ID_SSC, 0, ISR_SSC);
SSC_EnableInterrupts(AT91C_BASE_SSC, AT91C_SSC_ENDRX);
AIC_EnableIT(AT91C_ID_SSC);

// enable ssc action
SSC_EnableReceiver(AT91C_BASE_SSC);
SSC_EnableTransmitter(AT91C_BASE_SSC);
}

// only thing missing now is the interrupt handler:

void ISR_SSC(void)
{
volatile unsigned int status;
status = ssc->SSC_SR;
// [...] now the buffer is filled and you can do what you want with the data. To read a continuous stream
// you should call ReadBuffer again, once each time this function is called, alternating the two buffers.
}

///to send data, just call the SSC_WriteBuffer function at the same time you call the ReadBuffer functions.
///You dont need extra interrupts for that as long as you read/write buffer sizes are the same and
///everything is in sync anyway.


Top
 Profile  
 
 Post subject: Re: Using SSC in "slave mode" on AT91SAM7S
PostPosted: Mon Jan 11, 2010 9:52 am 
Offline

Joined: Sat Jul 19, 2008 6:30 pm
Posts: 2
Hi micha1999,

thank you very much for your detailed explanation.
It helped a lot in getting up and running in my current little project.

My problem still is, that my transmitter doesn't care when to start sending the first word of my stereo samples. I tried to configure the edge detection in START(Transmit Start Selection), but I only get it to work in the mode 0x6 (Detection of any level change on TF signal). This produces the result that my left and right channels switch on most transfer starts.

My self-made board includes a MAX9853 codec, which is configured to be SSC-master.

Maybe you have an idea on how to fix this?

BR and TIA


Top
 Profile  
 
 Post subject: Re: Using SSC in "slave mode" on AT91SAM7S
PostPosted: Wed Nov 24, 2010 7:39 pm 
Offline

Joined: Mon Sep 13, 2010 3:06 pm
Posts: 5
Hello There,

I'm using the at91sam9260, and tried to read some datas of a hardware that I have..

The problem is: I need a bigger samples than RCR can tolerate..
In fact, I need 131072 samples for 8 channels, ie, 131072*8*4(bytes) = 4194304 bytes and RCR only tolerate 65535.

How can I proceed?

I try to type ReadBuffer in ISR_SSC but just go to interrupt 1 time.

Look my code:
extern void acquire(void)
{
Pin pins[] = { PINS_SSC_RX };
PIO_Configure(pins, PIO_LISTSIZE(pins));

double mult = 1.1; //
unsigned int masterClock = MCK;
unsigned int Fs = AqAmostragem; // 20000
unsigned int bitrate = (int)(mult * Fs * LEN);
unsigned int period = (int)(mult * LEN);

printf("[Start Acquiring]\n\r");
printf("-- master clock: %u Hz --\n\r", masterClock);
printf("-- sampling frequency: %u Hz --\n\r", Fs);
printf("-- bit rate: %u bps --\n\r", bitrate);
printf("-- SSC Period=%u --\n\r", period);


AqBufferLen = AQCHANNELS * AqNumAmostras * 4; // 4 bytes per sample - ADC 24 bits, but ssc register is 32 bits

remainingSamples = (AQCHANNELS*4) * AqNumAmostras;
receivedSamples = 0;

// Configure the SSC
SSC_Configure(AT91C_BASE_SSC0,
AT91C_ID_SSC0,
bitrate,
masterClock);

// disable ssc interrupts
AT91C_BASE_SSC0->SSC_IDR = 0x0FFF;

unsigned int RCMR = (CKS) | (CKO) | (CKI) | (CKG) | (START) | (STOP) | (STT_DLY) | (SSC_PERIOD(period));
unsigned int RFMR = (SSC_DATLEN(SAMPLE_SIZE)) | (LOOP_OFF) | (MSBF) | (SSC_DATNB(AQCHANNELS)) | (SSC_FSLEN(1)) | (FSOS) | (FSEDGE);

SSC_ConfigureReceiver(AT91C_BASE_SSC0, RCMR, RFMR);

memset(AqBuffer, 0, remainingSamples);
// start reading using DMA the size must be supplied in "transfers", each transfer is 16bit

SSC_ReadBuffer(AT91C_BASE_SSC0, AqBuffer, 1024/2); // Just did this because u did
SSC_ReadBuffer(AT91C_BASE_SSC0, AqBuffer, 1024/2);

// configure interrupts
AIC_ConfigureIT(AT91C_ID_SSC0, 0, ISR_Ssc);
SSC_EnableInterrupts(AT91C_BASE_SSC0, AT91C_SSC_ENDRX);
AIC_EnableIT(AT91C_ID_SSC0);

// enable ssc action
SSC_EnableReceiver(AT91C_BASE_SSC0);
}


And ISR_SSC code:

static void ISR_Ssc(void)
{
unsigned int status = AT91C_BASE_SSC0->SSC_SR;
unsigned int size;

// Last buffer received
if ((status & AT91C_SSC_RXBUFF) != 0) {
SSC_DisableInterrupts(AT91C_BASE_SSC0, AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);
AT91C_BASE_SSC0->SSC_PTCR = AT91C_PDC_RXTDIS;
}

// One buffer received & more buffers to receive
else if (remainingSamples > 0) {
size = min(remainingSamples, 65535);
SSC_ReadBuffer(AT91C_BASE_SSC0, AqBuffer + receivedSamples, size);
remainingSamples -= size;
receivedSamples += size;
}
// One buffer sent, no more buffers
else {
SSC_DisableInterrupts(AT91C_BASE_SSC0, AT91C_SSC_ENDRX);
}
}


Thanks for help!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: Google [Bot] and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: