SPI help - SD card and Shift register

Hi all, I seem to have reached an impasse code wise.

My desire is to load code onto an micro SD card and then have that code run a multitude of different devices. I have been able to get the SD card to work and a shift register to work separately, and have even been able to run several servos based off of code read off the SD card when the servos are connected to the arduino directly and not the shift register.

When i include the shift register i come into problems. My guess is that my attempts to deselect the sd card by pulling its CS high is not working. Would you wise programming sages, be willing to take a look at a young fools code and perhaps make some suggestions?

#include <SD.h>

File myFile;

char code;
int h = 2;

char buffer[5];
int b = 0;

long data[5];
int d = 0;

int pos = 0;

long duration(long start)
{
  long now = millis();
  long interval = now - start;
  return interval;
}

void setup()
{
  //Serial.begin(9600);
  pinMode(11,OUTPUT);
  pinMode(13,OUTPUT);
  pinMode(2,OUTPUT);
  pinMode(10,OUTPUT);
  digitalWrite(2,HIGH);

  while(!SD.begin(10))
  {
  }

  myFile = SD.open("test2.txt");

  while(myFile.available())
  {
    while(d < h)
    {
      code = myFile.read();

      if(code != '\r')
      {
        buffer[b] = code;
        b++;
      }
      else
      {
        buffer[b] = '\0';
        data[d] = atol(buffer);
        b = 0;
        d++;
      }
    }
    long servo = data[0];
    long angle = data[1];
    
    //Serial.println(servo);
    //Serial.println(angle);
   
    pos = myFile.position();
    myFile.close();
    digitalWrite(10,HIGH);

    for(int i = 0; i < 20; i++)
    {

      digitalWrite(2,LOW);
      shiftOut(11,13,MSBFIRST,servo);
      digitalWrite(2,HIGH);

      long starttime = millis();  
      while(duration(starttime) < angle)
      {
      }

      digitalWrite(2,LOW);
      shiftOut(11,13,MSBFIRST,0);
      digitalWrite(2,HIGH);

      starttime = millis();  
      while(duration(starttime) < 25 - angle)
      {
      }
    }
    digitalWrite(10,LOW);
    myFile = SD.open("test2.txt");
    myFile.seek(pos);
    d = 0;
  }
}

void loop()
{
}

the Serial communication data is just for testing whether the SD card part is working right, and is "//" out for the real run.

as additional info - the SD transmits code fine, and when the SD read part is "//" out and the two variables "servo" and "angle" are replaced with numbers like 128 and .5, the servo runs just fine. This is why i believe my attempts deselect the SD are faulty.

Any help would be greatly appreciated.

As a side note - for some reason, the atol does weird things with the numbers read off the card such as rounding (turning 0.5 to 0 and 3.5 to 3, along with some other odd stuff). This isnt a big deal, and doesn't impede the process, and is easy enough to get around, but is there a way to fix this without changing the data type to float??

Any advice would be deeply appreciated!!!

As a side note - for some reason, the atol does weird things with the numbers read off the card such as rounding (turning 0.5 to 0 and 3.5 to 3, along with some other odd stuff).

Why do you think that atol() (ASCII to long) is doing weird things when it truncates your float values to long (int)?

the SD transmits code fine

No, it doesn't An SD card holds data. It doesn't transmit it anywhere - that requires a radio. It doesn't hold code. Code vs. data is a matter of interpretation of the data.

      long starttime = millis();  
      while(duration(starttime) < angle)
      {
      }

Why are you re-implementing delay()? What is the relationship between time and angle?

  1. for the atol, i can understand the rounding and why float would be needed ( i guess for the sake of preserving chip space, i was just optimistic that there was a different solution from atof)

most of the strings being converted act as you'd predict, but what i don't understand is 2 strings being converted
16
0.5
are both being converted to "-1" (they're the last 2 lines of data on the SD if that has an affect).
I suppose i'll just make a compromise on this

  1. i apologize for the improper use of semantics. I'm not all too proficient at articulating myself (if you haven't already noticed) - what I meant, was the code which the arduino uses to read lines of data from the SD card and save the resulting numbers to the two variables, servo, and angle, works fine. (i was calling the "data" "code", because the numbers stored on the SD card are saved to the SD card via a 3rd software and effectively allow for the arduino to undertake different task (activate different servos at different times)

  2. i chose that more circuitous route of invoking a delay, because it is my understanding that delay() halts the processor, which I don't want.

  3. as the angle of the servo is specified by the length of the pulse, the value assigned to the variable angle defines how short or long the pulse will be. I find that .5ms and 3.5 ms are the two 180 degree poles of the servo band of motion. (sorry if im wasting your time with trivial facts you already know).

Thank you very much Paul for your speedy reply! Any and all criticism is appreciated

i was just optimistic that there was a different solution from atof

What's the problem with atof()? If the values are indeed floats, then atof() is the appropriate function to use.

but what i don't understand is 2 strings being converted
16
0.5
are both being converted to "-1"

You'd need to print the value in the string before the call to atol() to see EXACTLY what the atol() function is seeing. Most likely, it is not what you think.

  1. i chose that more circuitous route of invoking a delay, because it is my understanding that delay() halts the processor, which I don't want.

The delay() function does no more, and no less, than your re-implementation of the delay function. Your code blocks until the required time has passed, just like delay() does.

okay. thank you for the assistance. i've changed my code to use normal delay, and atof.

As for the bulk of the question, do you know why the servo part of the code isn't working?

it seems like when i break up code into different segments, the SD read part and the servo part, they both work, and when they are conjoined, the SD read works find but the servo doesn't.

Is my assumption that the SD card is hogging the SPI bus correct? How do i go about deactivating the SD card CS?
it seems that pulling the CS line on the SD card LOW like the data sheet would imply (apparently it is "negative/HIGH activated"?) doesn't seem to be working.

The SD library takes care of the slave select on its after the SD.begin(10) call. If you have an Uno, you can't use D11 to D13 for your shiftOut pins. Those are SPI data pins.

do you know why the servo part of the code isn't working?

I can't see any reason for them not to work. Of course, you haven't said anything about how the servos are connected and powered.

i've changed my code to use normal delay, and atof.

It's generally a good idea to re-post it, then.

Is my assumption that the SD card is hogging the SPI bus correct?

That might be the case. But, since servos are not SPI devices, that should not matter.

A circuit diagram is in order.

sorry for the poor excuse for the "schematic" that was my first time trying to draw one up.

The circuit is pretty generic. 12 v wall DC outlet to 5 V step down where the servos draw their power, then a 3 V step down where the shift register and sd reader draw power. all of the SPI lines are connected, and the 5 v ground goes to an arduino ground pin.

This is all done on a breadboard btw.

#include <SD.h>

File myFile;

char code;
int h = 2;

char buffer[5];
int b = 0;

float data[5];
int d = 0;

int pos = 0;


void setup()
{
  //Serial.begin(9600);
  pinMode(11,OUTPUT);
  pinMode(13,OUTPUT);
  pinMode(2,OUTPUT);
  pinMode(10,OUTPUT);
  digitalWrite(2,HIGH);

  while(!SD.begin(10))
  {
  }

  myFile = SD.open("test2.txt");

  while(myFile.available())
  {
    while(d < h)
    {
      code = myFile.read();

      if(code != '\r')
      {
        buffer[b] = code;
        b++;
      }
      else
      {
        buffer[b] = '\0';
        data[d] = atof(buffer);
        b = 0;
        d++;
      }
    }
    float servo = data[0];
    float angle = data[1];
    
   /* Serial.println(servo);
    Serial.println(angle);*/
   
    pos = myFile.position();
    myFile.close();
    digitalWrite(10,LOW);

    for(int i = 0; i < 20; i++)
    {

      digitalWrite(2,LOW);
      shiftOut(11,13,MSBFIRST,servo);
      digitalWrite(2,HIGH);

      delay(angle);

      digitalWrite(2,LOW);
      shiftOut(11,13,MSBFIRST,0);
      digitalWrite(2,HIGH);

      delay(25 - angle);
    }
    digitalWrite(10,HIGH);
    myFile = SD.open("test2.txt");
    myFile.seek(pos);
    d = 0;
  }
}

void loop()
{
}

You can't use D11 to D13 for the shift register. Those are the SPI data lines. Select another pair of pins.

You can use the SPI pins for the shift register - just need to do SPI.transfer( ) to send data to it.
Select a pin to use for the latch signal. Then:
SCK connects to SRCLK
MOSI connects to Ser Data in
latchPin connects to RCLK.
OE/ connects to Gnd
SRCLR (MCLR?) connects to VCC

digitalWrite (latchPin, LOW);
SPI.transfer(shiftRegisterData);
digitalWrite (latchPin, HIGH);

Transfer works great!!

I cant thank you enough - i don't know if this taints the whole forum concept of good will, but do you have a paypal account? even though i live in Aus, i still think someone should buy you a beer for your help.

Just from a place of academia, do you happen to know how SPI.transfer() works different from shiftOut()? I assume that in shift out you specifiy MOSI, CLK, MSB just like you do when preping the SPI library for use, so i figured shiftOut is just a more concise way of utilizing SPI. Is this incorrect? Does it deselect all of the chips who's CS pins that aren't pulled LOW (or HIGH in the case of the SD card)?

I ask because as they say, if you can't take it apart, you don't really own it.

Thanks again!

SPI uses dedicated internal hardware to shift the data out very quickly. Write a byte to a register, hardware shifts it out.

shiftOut() fakes it by having the software put a bit out, bring the clock up, lower the clock, put the next bit out, raise the clock, lower the clock, etc 6 more times.
With SPI divisor set to 2, 8 MHz transfers can occur. Default speed is 4 MHz.
CS is software controlled in both cases, your sketch has to ensure only 1 slave is selected at a time. Not all slaves have active low CS, but most do.

I ask because as they say, if you can't take it apart, you don't really own it.

You have our permission to "take it apart". You have the source code for shiftOut() and SPI.transfer(). Feel free to look at the source code and "take it apart".