SD Card Speed



  • @Tritonium - yes, it's the Makeblock XY plotter kit, but I was driving it with stand-alone motor controllers and a Propeller running the SD test code from this thread.
  • Jason...One other item. My program calculates the motorXdir and motorXstep in a different location than where the motorYdir and motorYstep are computed. I need to put motorXdir and motorXstep into char byteToOutput then after some code is run combine motorYdir and motorYstep into char byteToOutput just before fputc( byteToOutput, fp ) is executed.

    How would you do that?

  • Jason...I pasted together a Code Checker program since I was unable to get output from pins 0,1,2,3.

    The .c program is attached. I expected all output pins to go HIGH but there is no activity.

  • Most likely fails because you don't close the file when you're done writing it. That means the subsequent call to open it will fail.

    For the combining of the bits, you can easily just OR ( | ) two values together to combine them, and use << to put them into the right bit positions. The code in your test with xdir / xstep / ydir / ystep is doing just what you need. Without seeing the rest of the code it's hard to suggest much beyond that.

    Make the functions that compute the step / dir return a char with step in the low bit, and dir in the second bit, then just OR the two results together, like this:

    CharToOutput = stepDirX | (stepDirY << 2);
  • Jason...Adding the fclose(fp) instruction after the "for loop" did not work. It appears that no bits are getting into the SD Memory for your program to read.

  • That should work - try adding an fflush, like this:
      FILE * fp = fopen("plasma.bin", "wb" );
      for(i=0; i<1024; i++)
    	  char byteToOutput = (Xdir << 0) | (Xstep << 1) | (Ydir << 2) | (Ystep << 3);
    	  fputc( byteToOutput, fp );

    That shouldn't be necessary, but it might be. I don't see anything wrong with the code otherwise.
  • Jason...The fflush(fp) instruction did not work. The only difference between your SDTest program that works and the Code Checker program that does not is the method of entering data into the SD Memory. You use the Buffer method in SDTest as opposed to the fputc(byteToOutput, fp ) method in Code Checker.

    Would it be possible for you to actually run the Code Checker on your Propeller Activity Board for verification?

  • I could try it tonight or this weekend, yes. It would be easy to change the code to use the buffered call:

    Use this instead of fputc():
    fwrite( &outputByte, 1, 1, fp );
  • Jason...That instruction did not compile.

    I will leave shortly and not return until sometime Sunday.

  • JasonDorieJasonDorie Posts: 1,930
    edited April 2017 Vote Up0Vote Down
    Not in the exact form I wrote it, no. Not everything I post will have been tested, so you may occasionally be called on to apply critical thinking skills. From the previous code snippet you posted, you called it "byteToOutput", so substitute my "outputByte" for that, like this:
      FILE * fp = fopen("plasma.bin", "wb" );
      for(i=0; i<1024; i++)
    	  char byteToOutput = (Xdir << 0) | (Xstep << 1) | (Ydir << 2) | (Ystep << 3);
    	  fwrite( &byteToOutput, 1, 1, fp );
  • @Jason - looks like the message you wrote has come back on you, and in the same manner it was written, through tiny tiny steps :)
    Tachyon Forth - compact, fast, forthwright and interactive
    Tachyon Forth News Blog
    Brisbane, Australia
  • Jason...Attempts to write nibbles into the SD memory and write the nibbles out to propeller pins 0,1,2,3 produced the following results:

    1. char byteToOutput = (xDir<<0 | xStep<<1 | yDir<<2 | yStep<<3);
    fwrite( &byteToOutput,1,1,fp);
    Compiles but fails to generate output on pins 0,1,2,3. 0E

    2. char byteToOutput = (xDir<<0 | xStep<<1 | yDir<<2 | yStep<<3);
    fputc( byteToOutput,1,1,fp);
    Fails to compile.

    3. char byteToOutput = (xDir<<0 | xStep<<1 | yDir<<2 | yStep<<3);
    fputc( &byteToOutput,1,1,fp);
    Fails to compile.

    4. char byteToOutput = (xDir<<0 | xStep<<1 | yDir<<2 | yStep<<3);
    fputc( byteToOutput,fp);
    Fails to compile.

    5. char outputByte = (xDir<<0 | xStep<<1 | yDir<<2 | yStep<<3);
    fputc( &outputByte,1,1,fp);
    Compiles but no output on pins 0,1,2,3. 0E

    A SimpleIDE Clocking Rate test code using a repeating HIGH(0); LOW(0); pattern produces a clock rate of 39.0623 kHz. This frequency spins the ClearPath motors at an acceptable speed.

    Your original SDTest program produced an output on propeller pin 0 that spins the ClearPath motors at an acceptable speed.

    I modified the parallax\learn\SD memory tutorial program to write numbers commensurate with a nibble but in .txt and those numbers were read out of the SD Memory and displayed on the screen perfectly. However, when the numbers were decoded and used to drive the ClearPath motors...the speed was way too slow. One comment was made that reading one word out of SD memory at a time is not the way.

    We know that:

    1. The SD memory can handle text numbers.

    2. The SD memory can handle blocks of 512bits.

    3. To date, the SD memory together with your read SDTest program cannot handle four-bit nibbles. Either the four-bit nibbles are not being stored in SD memory in the right manner (or at all) or the SDTest program cannot recognize the manner in which the nibbles are stored.

  • Dave HeinDave Hein Posts: 5,373
    edited April 2017 Vote Up0Vote Down
    Discovery, you should be able to figure this out on your own. At some point the student needs to do his own homework. The parents can't keep doing it for him, or the student will never learn.

    Look at the error messages that SimpleIDE is generating. You will see that you're using too many parameters for fputc. Google "fputc" to see how to use fputc. That should solve your fputc problem. BTW, your test #4 should work OK for fputc. It must be failing to compile for some other reason. Or maybe you have items #4 and #5 reversed.

    SD cards handle data in chunks of 512 bytes. C's standard I/O library allows you to write data in multiples of bytes, and its drivers handle the buffering to break it down into 512-byte chunks. The standard I/O library doesn't handle 4-bt nibbles, so you need to do that yourself. 2 4-bit nibbles fit into a single 8-bit byte. Packing nibbles into bytes is pretty straightforward. Assuming that nibble1 and nibble2 contain 4-bit values, then they can be packet into a byte by doing "byteval = (nibble1<<4) | nibble2". Unpacking a byte would be "nibble2 = byteval & 255" and "nibble1 = (byteval >> 8 ) & 255".

    The fact that you don't get output on pins 0, 1, 2 and 3 could be caused by many things. Maybe your SD card file doesn't contain anything. This can be easily verified by looking at the file on a computer. Have you done that? Maybe your output cog isn't running. You can verify that by turning an LED on and off every 100 or 1000 loops. Maybe you don't have DIRA setup correctly. Make sure that your main cog isn't stomping on pins 0, 1, 2 and 3 by setting those bits in DIRA. Only your output cog should set those bits in its DIRA.
  • Dave...I don't need a lecture. I would like code that works. But, thanks for the information.

  • Dave...By the way, I paid Jason to produce working code as a product.

  • JasonDorieJasonDorie Posts: 1,930
    edited April 2017 Vote Up0Vote Down
    Important distinction: you paid for work already completed - the title of the email you sent was "payment for work accomplished', which I feel is pretty clear.
  • JasonDorieJasonDorie Posts: 1,930
    edited April 2017 Vote Up0Vote Down
    Discovery - in your version, you've removed the code that mounts the SD card device. None of the file operations after that will do anything if the SD driver isn't running:
      // Set up the SD card reader
      sd_mount(DO, CLK, DI, CS);                  // Mount SD card
      FILE * fp = fopen("plasma.bin", "wb" );
    You keep saying "the delivered code doesn't work", but what's happening is that the delivered code is fine until you break it, which is also an important distinction.

    In each of your above tests, the ones that "compiled fine but produce no output", probably do actually work if you mount the SD card.

    Be careful as you change the code - Everything that's in there is there for a reason, and the comments were put there to help you understand how it works and what everything does. By removing all the comments, you've actually made it harder on yourself to maintain and modify the code because you may not realize the importance of what you're changing.

    In order to figure out what broke, I simply referred to my existing working version and looked at the differences in the code. It was immediately obvious that you'd removed the line to mount the SD card.
  • Jason...Nice catch. I completely missed copying sd_mount.

    Only the following version worked.

    char byteToOutput = (xDir<<0 | xStep<<1 | yDir<<2 | yStep<<3);
    fwrite( &byteToOutput,1,1,fp);

    This solves my problem.

    Thank you very much.

  • I locally tested the fputc( byteToOutput, fp ) version and that worked too.
  • Jason...Okay good. There are two other items that showed up during testing. I am writing a 1111 and 0000 pair of nibbles into the SD memory and watching the output on the scope for pins 0,1,2,3. The output looks good but whether I write 2 nibbles or 2,000,000 nibbles...I get X0E at the end of the print to screen. Sometimes I get X2E but according to your notes the X indicates a read error. Maybe you know why.

    Also, if I write more than 2,000,000 nibbles to the SD memory, the program locks up. The memory lists its volume as 2GB. Do you get the same results?

  • X means the block ended "early". The SD card is read in blocks of 512 bytes, so if I get less than that I display the X. It doesn't necessarily mean an error occurred. If your file is not a multiple of 512 bytes you'll see that displayed. E is always displayed when it has read the last data from the file.

    As for locking up after more than 2M bytes, that one I haven't seen, but I haven't tried writing more than that. It doesn't seem like that should be any sort of hard limit though. Do you have any other data on your SD card, or is it empty other than the test file?

    It takes a significant amount of time to write the data in "un-buffered" mode, IE writing a single byte at a time - like, 10 seconds to write 16kb of data, which seems abysmal. It's entirely possible that the underlying code is doing a write of the whole 512 byte block every time you call fputc(), which would be horrible.

    Try doing something like this instead:
      //Write data to SD memory      
      fp = fopen("test.txt", "wb"); // Open a file for writing
        char byteToOutput = i&15;  // This is just making the bits toggle - do something meaningful here
        Buffer[0][ i & 511] = byteToOutput;  // Use the first 512-byte buffer to hold data until it's full....
        if( (i & 511) == 511 ) {  // when we get to the end of the 512 bytes (0 to 511)
          fwrite( Buffer[0], 1, 512, fp );  // ....write it to the SD card all at once
        if( (i&16383) == 16383 ) {  // display some output to the screen so we know it's working...
          printi( "writing...  %d\r", i );

    That code only writes multiples of 512 bytes to the file, and I tested it up to 4,000,000 with no issues.
  • Jason...Understood on the meaning of "X", not and error. The SD memory was cleared using the fflush instruction.

    I am using fwrite you want me to change to fputc?

    I will try your suggestion above.

  • fwrite of a single byte at a time won't be any better than fputc, I suspect.

    fwrite takes arguments to specify how much data to write - Your original use of it was only writing one byte at a time.

    Also, fflush doesn't clear the SD card - it just writes any buffered data out. To clear the SD card you'd need to mount it on a PC and format it. There are likely ways do do it from the Prop, but I've never done it.
  • Jason...That is interesting. I expected the SD memory to clear when fflush was executed. My application requires a clear SD memory each time a program is down loaded to the propeller. Could you find out how to do it from the propeller?

    Also, the code you provided did not compile. I had to change the fp = to FILE * fp= and the code ran perfectly. The data looks great.

  • If you are sure that you'll only ever have that one file on the Prop, since you're re-writing it every time there should be no need to format the card. Calling fopen() on the only file on it will erase it before it starts writing again.
  • Jason...That is the design of the system. The only file on the SD memory will be plasma.txt and I expect to re-write it over and over again.

    Sounds as if there is no problem.

  • Jason...The Buffer method you used for writing to the SD memory is really fast. It takes about 3 minutes and 30 seconds to write 4 mega nibbles to the sd memory and about 2 minutes and 50 seconds to dump the contents to the ClearPath motors. That is really good.

    How would you change the code to write the plasma nibbles: xDir xStep yDir yStep that are currently set as binary digits into the Buffer and retain the same speed? If need be, I can set the nibbles as text: such as 15 and all the other numbers needed to drive the motors.

  • JasonDorieJasonDorie Posts: 1,930
    edited April 2017 Vote Up0Vote Down
    Presumably you already have code to compute the step and direction pin values, yes? Wherever that code lives, you'd need to update it so that:

    - it has access to the file pointer you've opened
    - it has access to an output buffer of chars
    - it has access to an index / counter for where you are in the buffer

    - when you compute the next xstep/xdir and ystep/ydir pairs, simply OR them together like I'm doing to build the "byteToOutput" value
    - put the computed value into the buffer at the index of the counter
    - increment the counter
    - when the counter reaches the size of the buffer, write the buffer to the SD card and reset the counter to zero
    - keep doing this until the entire path is computed

    - if the current counter isn't zero, it means you have some stuff in the buffer that hasn't been written
    - if that happens, fill the remainder of the buffer with zeros, and write it to disk normally
    - close the file

    If you set things "as text", assuming I understand you, you'll lose all the speed you have. As it stands, each motor step/dir pair is written as a single byte to the file, leaving 4 bits unused. If you store 4 sequential text digits to the file instead, you'll cut the speed by a factor of 4.
  • Jason...Yes, I developed the code for setting the xDir, xStep, yDir, and yStep states.

    I will see what I can do to update the code in accordance with the above directions.

  • Jason...Before I begin following your procedure above, I wonder if you would address an issue that came up during code checker testing.

    Whether I load the SD memory with 1,000 nibbles or 4 mega nibbles using the for( ) instruction, in your code above, the program outputs the same amount of data to the ClearPath motors...2 minutes and 45 seconds worth.

    When I load 1,000 nibbles it is a fast load...just a few seconds.

    When I load 4 mega nibbles it takes about 4 minutes. Makes sense.

    However, the about of data output is the same. How can that be?

Sign In or Register to comment.