Sd Card Read Speed Problem

In order to operate 3 stepper motor simultaneously, I have to sent serial data to the firmware of the stepper motor drivers via RS-485. I use Arduino Mega to send that stream of strings after reading from an SD card. The bottleneck is the speed at which the firmware needs that strings to drive the motors.

I have to send the commands to 3 different drivers at every milliseconds intervals. So, I use an interrupt in the purpose. The format of the strings are such that <11 character here>, thus, i can identify these characters enclosed with the start & end markers.

Since I’m beginner in the field, I try to use a combination of example codes attached as follows. When I use this method to drive the motors via SD card (comprising all the sequential commands in a txt file), I realize that recieving & sending the 11 characters for just the first driver takes 1 milllisecond.

I wonder the possibility of reading at faster speeds from SD card. Any suggestions or any source that I can begin with?
Previously, I read related forum posts. But, unfortunetely, I could not find a method that I can understand and implement.

#include <SdFat.h>
SdFat sd;
SdFile myFile;

const int chipSelect = 53;
const byte numChars = 11;
char receivedChars[numChars];   // an array to store the received data
boolean newData = false;

void setup() {

  Serial1.begin(115200);
  Serial.begin(115200);

  Serial1.setTimeout(1);
  Serial.setTimeout(1);

  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // change to SPI_FULL_SPEED for more performance.
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();

  if (!myFile.open("test.txt", O_READ)) {
    sd.errorHalt("opening test.txt for read failed");
  }

  //set timer1 interrupt at 1kHz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 250hz increments
  OCR1A = 250 - 1; // = 0.001*(16*10^6) / (1*64) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS10 for x1 prescaler
  TCCR1B |= ((1 << CS10) | ( 1 << CS11)); //64 prescale
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  //myFile.close();
}

ISR(TIMER1_COMPA_vect)
{ //timer1 interrupt 1

  recvWithStartEndMarkers();   // command for the first stepper
  showNewData();               // send commands from serial for the first stepper
  recvWithStartEndMarkers();   // command for the second stepper
  showNewData();               // send commands from serial for the second stepper
  recvWithStartEndMarkers();   // command for the third stepper
  showNewData();               // send commands from serial for the third stepper
 
}


void loop() {

}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';

  while ((myFile.available() >= 0) && newData == false) {
    char rc = myFile.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void showNewData() {
  if (newData == true) {

    Serial1.print(String(receivedChars));
    Serial1.print("\r\n");
    Serial.print(String(receivedChars));
    Serial.print("\r\n");
    newData = false;

  }
}

Well, you can read all 512 bytes into a buffer and deblock the lines yourself. And you can double buffer the reads, so one buffer can be filled while you are processing the other one.

Paul

Rather than reading one character at a time, have you tried reading multiple at one time:

int read (void *buf, size_t nbyte)

If the problem is just the first read, then once you have opened the file, just try a peek to ensure the buffer is loaded. ie. myfile.peek()

What makes you so sure the problem is the reading of the SD card?
What sort of SD cards have you tried?

Paul_KD7HB:
Well, you can read all 512 bytes into a buffer and deblock the lines yourself. And you can double buffer the reads, so one buffer can be filled while you are processing the other one.

Thanks for the replies. I will try to read the file as blocks of characters instead of reading one byte at a time. I think you mean such a structure:

uint8_t buf[512]
myFile.read(buf, sizeof(buf))

I will also create two different buffer like this and process them as mentioned.

countrypaul:
Rather than reading one character at a time, have you tried reading multiple at one time:

int read (void *buf, size_t nbyte)

If the problem is just the first read, then once you have opened the file, just try a peek to ensure the buffer is loaded. ie. myfile.peek()

What makes you so sure the problem is the reading of the SD card?
What sort of SD cards have you tried?

I think it is not just about the first read. I read from a regular Sandisk 16 GB microSD card (U1 speed class) attached to an adapter like that: Micro-SD-Card-Storage-Adapter

If I use constant strings written on the arduino sketch (inside the interrupt), the drivers take the commands and very smooth motion is achieved. That's why I think the main limitation is the reading from the SD card.

Just re-read your orginal message then looked at your code again and realised that you are using an ISR and have all the I/O performed within that ISR. That is normally frowned upon as being within an ISR blocks other interupts from activating and can lead to inconsistent performance.

An ISR should normally be very small and just set a variable or something similar, then the code within the normal loop () function should process that variable and perform any I/O that is needed or anything else that requires an interupt.

I will freely admit that I have not used timer interupts in the way you are using them so am a little out of my depth, but any work I have done with interupts has always been done to ensure no other interupts are needed whilst in the ISR. This normally means no writing to serial output for example.

Hopefully someone else with more experience of timer interupts will comment on this.