Need help reading 2 bytes at a time from SD card

Hello all. I am working on a digital sampler using the Due. The samples are 12 bits. The design of the sampler will be such that I need to save and retrieve a word at a time to and from the SD card. I am having trouble with retrieving the words correctly.

I am testing it with just a 1kHz sine wave to the ADC. The data gets saved to the SD card ok but when I retrieve it, I get something like a distorted sine wave with every other word being way too large.

Does anyone know of a simpler way or can tell me what I'm doing wrong?

I am using the sdFat library. I have looked at the documentation online and the example sketches but I'm still a little lost. Is there a more in-depth tutorial online somewhere?

Thanks. - Rob

SD_test_1-23-15_RevB.ino (1.75 KB)

This:

for (int i=0; i<100; i++)  {
    sample = ADC->ADC_CDR[7];
    myFile.println(sample);
    Serial.println(sample);
  }

is going to yield really terrible audio quality, as there will be huge amounts of jitter in the sample rate. You'll need to figure out if the ADC can be configured to be triggered by a timer, so the sample rate is constant. Then you'll need to make sure your code reads, and stores, the data in a timely manner, ideally using conversion complete interrupts. Figuring all that out, if the chip even supports it, will require detailed study of the SAM data sheet, and register-level programming.

I've never used SDFat, but this:

  //read each byte into a buffer
  for (int i=0; i<100; i++)  {
    for (int j=0; j<1; j++)  {
      sample2[j] = myFile.read();
    }

appears wrong to me. SDFat read() appears to me to read a byte from the file, and return it as an int. You need to read two bytes, and re-assemble them into a single int, taking into account the "Endian-ness" of the file data.

Regards,
Ray L.

Hi Ray. Thanks for the help. Since my original post, I have continued to work on the code and actually had some success.

What I've been working on is mainly a proof of concept to see if I can even get the samples into and out of the SD card in time. (Goal is under 24 uS)

I revamped my read() and write() loops a little and, using an excel sheet, was able to prove that the data going to the card is exactly the same as that coming from the card.

Like you suggested, I had to break it up into two bytes like this...

for (int i=0; i<100; i++)  {
    buf[i] = ADC->ADC_CDR[7];
    sampFile.write(highByte(buf[i]));
    sampFile.write(lowByte(buf[i]));
    }

and this...

for (int i=0; i<100; i++)  { 
    if ((dACC_ISR & TXRDY_mSK) > 0x00000000) {
      dACC_CDR = word(sampFile.read(), sampFile.read());
      //dACC_CDR = sample;   //play the sample from the SD card to DAC0
    }
   }

I still wonder if there is a way to send a word at a time to cut down the delay even more.

I've got the read and write times down to about 8 micros to write a word, and as you can probably tell, I've been messing around with programming directly to the registers to put the ADC and DAC into freerun mode, because there's just no time for Arduino functions.

Next I need to tackle the automatic interrupts like you said. I know there was something in the datasheet about that but I'm a little new to this stuff and still on the wrong end of the learning curve.

If anyone has any info or links to any tutorials about using the SAM3X interrupt timer I would greatly appreciate any insight.

Thanks.

Well just a quick update on my progress in case anyone is listening. :grinning:

I was able to find a piece of code which sets up an interrupt using the SAM's TC (counter)

// Black magic
void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency) {
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk((uint32_t)irq);
  TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1);
  uint32_t rc = VARIANT_MCK/2/frequency; //2 because we selected TIMER_CLOCK1 above
  TC_SetRA(tc, channel, rc/2); //50% high, 50% low
  TC_SetRC(tc, channel, rc);
  TC_Start(tc, channel);
  tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
  tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
  NVIC_EnableIRQ(irq);
}

void setup(){
  pinMode(13,OUTPUT);

  // Start timer. Parameters are:

  // TC1 : timer counter. Can be TC0, TC1 or TC2
  // 0   : channel. Can be 0, 1 or 2
  // TC3_IRQn: irq number. See table.
  // 40000  : frequency (in Hz)
  // The interrupt service routine is TC3_Handler. See table.

  startTimer(TC1, 0, TC3_IRQn, 40000);  //the last number will be the freq. of interrupt in Hz

  // Paramters table:
  // TC0, 0, TC0_IRQn  =>  TC0_Handler()
  // TC0, 1, TC1_IRQn  =>  TC1_Handler()
  // TC0, 2, TC2_IRQn  =>  TC2_Handler()
  // TC1, 0, TC3_IRQn  =>  TC3_Handler()
  // TC1, 1, TC4_IRQn  =>  TC4_Handler()
  // TC1, 2, TC5_IRQn  =>  TC5_Handler()
  // TC2, 0, TC6_IRQn  =>  TC6_Handler()
  // TC2, 1, TC7_IRQn  =>  TC7_Handler()
  // TC2, 2, TC8_IRQn  =>  TC8_Handler()
}

void loop(){
}

volatile boolean l;

// This function is called every 25 uS.
void TC3_Handler()
{
  // You must do TC_GetStatus to "accept" interrupt
  // As parameters use the first two parameters used in startTimer (TC1, 0 in this case)
  TC_GetStatus(TC1, 0);

  digitalWrite(13, l = !l); //make a square wave of frequency 20 kHz
}

Big props to Sebastian Vik and @cmaglie for posting this very helpful code.

This is a really powerful tool because you can have a bunch of independent interrupts/ timers.

Since I am sampling an audio signal, I set the interrupt at 40kHz which should allow time between samples to save and recall 2 bytes from the SD card, but also will allow sampling up to 20kHz.

I put the code into practice and it worked nice. I was initially concerned because at about 10kHz, you get a sort of bit-crushed stair step effect on the wave coming from the DAC. Using the cursor; however, I can see there is approx. 25 uS between each stair step so I guess this is to be expected (T = 25uS.)

If anyone has any pointers or is interested in talking about this project in more detail, I'm all ears. Thanks. Will update on progress as I go, (unless I get kicked off here :smiley: )

Peace