PDA

View Full Version : Reading from an SD Card Gadget Gangster Tutorial: How does this code work?



varnon
03-24-2012, 04:06 AM
The tutorial in reference is here:
http://www.gadgetgangster.com/tutorials/414



'
'SD Read Example 2 by Jeff Ledger
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000


DO = 0 'Set these pins to match your SD connections.
CLK = 1
DI = 2
CS = 3

OBJ
sdfat : "fsrw"
pst : "Parallax Serial Terminal"
num : "Numbers"


VAR
BYTE DATA

PUB demo | mount, r, counter, number


'Start the required code to communicate with Parallax Serial Terminal.
'Wait four seconds for the user to launch PST and press the Enable button.
pst.Start(115_200)
waitcnt(clkfreq*4 + cnt) 'Increase the 4 if more time is required.

'Attempt to mount the SD card.
'Report a failure to mount if the card is not found.
'Halt if the card fails to mount.
mount := \sdfat.mount_explicit(DO, CLK, DI, CS)
if mount < 0
pst.str( string( 13, "Failed to mount", 13 ) )
abort


'Report a message that the card was found and mounted.
pst.str(string(13,"SD was card found and mounted fine.",13,13))


sdfat.popen(string("data.csv"),"r")


counter:=0 'Setup a counter starting at zero.

repeat 'Start a repeat loop.
r := sdfat.pgetc 'Get a character from data.csv

if r < 0 'If the character is "less than zero"
pst.newline ' send a newline to the terminal
quit ' and quit.

if r == 44 or r == 13 'If character is a comma or carriage return

pst.str(string("String: ")) ' Display line of text.
pst.str(@data) ' Display data.
number:=num.FromStr(@data, num#dec) ' Convert data string to number.
pst.str(string(" Number: ")) ' Display line of text.
pst.dec(number) ' Display the number.


pst.str(string(" +")) 'A little test to make sure
pst.dec(number) ' we are dealing with
pst.str(string("=")) ' actual number data.
pst.dec(number+number) 'Display number + number

pst.newline 'Send a carriage return to PST
bytefill(@data,0,counter) 'Fill data with zeros again.
counter:=0 'Reset counter to negative 0.


if r > 44 'If the character "greater than 44"
data[counter]:=data[counter]+r ' send character to data string.
counter++ ' increment the counter.

pst.str(string("Closing file and unmounting SD card.",13))
sdfat.pclose

'Unmount the card and end program.
sdfat.unmount






There are a few things that I don't understand.


First, I am a little confused to how "data" is used.
In the VAR section, it is declared as a byte...
But the way it is referenced in the program (data[counter]) suggests that "data" is an array of bytes.
Is this correct? By declaring "byte data" is a byte sized and byte aligned array being created?
Or is it that no array is created, but that to "data[1]" refers to a non-named byte that is 1 byte away from "data"?


Second, I do not understand the line "data[counter]:=data[counter]+r"
Why is "r" added to the existing value? The existing value should be 0, correct? For me, the program has worked identically with "data[counter]:=r"


Finally, why do you need to refer to the address of data in "pst.str(@data)"? Not referring to the address obviously does not work, but I am not sure why. The other time the address is referred to is in the "bytefill" command. This makes a little more sense to me. The bytes are set to 0 from the starting address (@data or data[0] should be equivalent I think) to whatever the last address that was written to is, determined by the counter.


For my purposes, I encountered errors during the file reading phase of my program. I found that this occurred when there were around 30 comma separated values in my text file. I found that declaring the variable data as an array such as "byte data[100]" eliminated all problems. But really, I have no idea why that worked.


Can anyone clarify this questions a little? I do have working code, but I really want to understand how and why things work so that I can correct any bugs that come up.


Thanks

Ariba
03-25-2012, 07:28 AM
First, I am a little confused to how "data" is used.
In the VAR section, it is declared as a byte...
But the way it is referenced in the program (data[counter]) suggests that "data" is an array of bytes.
Is this correct? By declaring "byte data" is a byte sized and byte aligned array being created?
Or is it that no array is created, but that to "data[1]" refers to a non-named byte that is 1 byte away from "data"?

"byte data" declares only a byte variable. You're correct that here an array should be declared: byte data[80] or so.
But Spin does not test array boundaries, so you can access single variables also as array:
temp := data
temp := data[0] 'is the same
temp := data[1] 'accesses the following byte variable in memory



Second, I do not understand the line "data[counter]:=data[counter]+r"
Why is "r" added to the existing value? The existing value should be 0, correct? For me, the program has worked identically with "data[counter]:=r"

Yes, makes no sense. "data[counter]:=r" is fine.



Finally, why do you need to refer to the address of data in "pst.str(@data)"? Not referring to the address obviously does not work, but I am not sure why. The other time the address is referred to is in the "bytefill" command. This makes a little more sense to me. The bytes are set to 0 from the starting address (@data or data[0] should be equivalent I think) to whatever the last address that was written to is, determined by the counter.

Strings in Spin are arrays of characters (=bytes) with and ending zero byte. To pass a string to a methode normally a pointer to the begin of the string is passed. The methode processes then the string beginning from that address until a zero char is reached.
Here you fill the data-array with characters and then pass the address of the data array to display it with pst.str()


Andy

Oldbitcollector (Jeff)
03-25-2012, 04:00 PM
@varnon,

First of all, I'm the guy who wrote that confusion.. :) I apologize if it threw you..
Before Spin, my coding background was mostly in variations of BASIC, and sometimes it shows in my code.

The DATA byte variable issue will be corrected in the next revision of that document, you are spot-on correct that it's size should be defined. As for the "data[counter]:=data[counter]+r" vs "data[counter]:=r", The statement is redundant and will be adjusted.

Spin tends to be a little "too flexible" at times allowing many variations where other languages would have simply refused to compile. This is both a blessing an a curse of the language.

Thanks for the constructive input.
OBC

varnon
03-25-2012, 06:34 PM
Thanks for the responses! On reflection, I hope the original post did not come off as a criticism, I really needed that tutorial. I just wanted to understand a few parts.

One more question on the data array.
If we don't explicitly declare an array with byte data[100]. We can still reference data[#] the same way. The difference is that the byte at that address may or may not have been used or changed by another function. If we declare the array explicitly, this should make sure that nothing else modifies those bytes, where if we don't declare the array explicitly, other code could modify those bytes. Is this correct?

We also should only have to declare the size of the data array to include the maximum number of characters that will be read, plus 1 for the null string terminator. For example, in my program, I am saving and reading a time value, in milliseconds. The maximum value of my clock is about 2,100,000,000 milliseconds. Considering this, the size of my byte array should be 11. 10 bytes for the maximum number of characters, and an 11th byte for the 000 null termination. Am I correct?

I really appreciate the tutorial and responses. This is becoming much clearer to me. (Or I think it is anyway.)

Oldbitcollector (Jeff)
03-25-2012, 07:32 PM
I'm not entirely sure I understand where you are going here, but if I follow you correctly, the defined data array will be all zeros until you place data in the array, meaning that if you place 10 characters, the 11th will still be a zero.

...and I appreciate constructive criticism! Not only does it keep me on my toes, but it helps refine the document for others who come along.

OBC

varnon
03-25-2012, 08:02 PM
Hmm, let me try to rephrase.


I mean to say that if you know the maximum number of characters you will be reading at once, you can define the array as byte[maximum +1]. This should "reserve" space for the maximum number of characters and a null. For my example of a maximum of 10 characters, and data[11], if you read less than the maximum number of characters the null will be earlier in the array. If you read the maximum number of characters, the characters will take up the first 10 slots, and the null the 11th. Of course on initiation and on clearing the array will be filled with zeros.


I think if this space is "reserved" by explicitly declaring an array, then other lines of code will not modify it unless explicitly instructed. Otherwise, if you just declared "byte data" then declare another byte variable somewhere in the program, it could be one space away from data. This would cause data[1] to be referencing another variable, and some problems could occur. (I think.)


I'm honestly not sure if this is correct, that is why I am posting. I had some problem with my code until I explicitly named byte as an array. What I have described is the only explanation I can think of that seems to make sense to me.
Right now I have "byte data[100]" because I have plenty of space. I don't expect to run out any time soon. But since I am making a set of objects to be referred to by other programs, I would like to be as efficient with space as possible.


Maybe this was more clear?

Ariba
03-26-2012, 04:28 AM
Spin is a simple language, which not does any dynamic allocation of strings, and has no garbage collection or so.
This means in Spin you need to reserve the space for strings and arrays by declaring it in the VAR section (as you say). The reserved size must never be exceeded in the whole code, otherwise you overwrite other variables, or the stack or a part of the next object in memory.
If you don't know the max. size of a line in the file "data.csv", then you should check the counter variable and stop writing to the data array if it exceeds the declared size -1:

if counter < MAXSIZE-1
data[counter++] := r


Andy