fsrw demo to read and display file contents
Below is my attempt to add some lines to a created file, then open the file to be read, and the contents displayed to the VGA screen. The part that is not working is the displaying of contents to the screen. What am I missing here?
Thanks
Ray
Thanks
Ray
''Open_sd_030.spin
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
'' Using the DEMO board with an SD card reader
'' attched to P0 ... P4
DO = 0
CLK = 1
DI = 2
CS = 3
VAR
BYTE dirbuff[12]
BYTE filebuff[256]
BYTE filesize
OBJ
sdfat : "fsrw"
vga : "MikronautsVGA64"
PUB demo | mount
vga.start(16) '' Start the VGA driver
vga.cls '' Clear the screen
'' Mount the SD card
mount:= \sdfat.mount_explicit(DO, CLK, DI, CS)
if mount < 0
vga.str(string("Failed to MOUNT",$0D))
abort
vga.str(string("SD card was found and MOUNTED."))
vga.out($0D)
'' Add some txt to an exiting file
sdfat.popen(string("test3.txt"),"a")
sdfat.pputs(string("This is the xxxx line.",$0D))
sdfat.pputs(string("This is the yyyy line.",$0D))
sdfat.pclose
vga.str(string("File has been wrtten to.",$0D))
'' Show files on the SD card
sdfat.opendir
sdfat.nextfile(@dirbuff)
vga.str(@dirbuff)
vga.out($0D)
sdfat.nextfile(@dirbuff)
vga.str(@dirbuff)
'' Open the file for read and display contents
sdfat.popen(string("text3.txt"),"r")
filesize:=sdfat.get_filesize
sdfat.pread(@filebuff,filesize)
vga.str(@filebuff))
'' Unmount the SD card
sdfat.unmount
vga.out($0D)
vga.str(string("SD card has been unmounted",$0D))

Comments
You open it for appending. So, if you already run the program several times it's bigger than 256 bytes and thus reading the whole file will destroy your RAM (propably including the program).
This is called a buffer overflow and is used to hack systems here and there. So, the best way to read any stream is by using a limit and rather read it in portions.
In your case you can use a loop to read/print the file counting down the variable filesize. If I remember right pread returns how many bytes actually have been read.
Also think about increasing the length of filebuffer by one byte and always store a 0 (zero-byte) after the last read character. Otherwise vga.str might print more than you want.
Ray
''Open_sd_030.spin CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 '' Using the DEMO board with an SD card reader '' attched to P0 ... P4 DO = 0 CLK = 1 DI = 2 CS = 3 VAR byte tbuf[20] OBJ sdfat : "fsrw" vga : "MikronautsVGA64" PUB demo | mount,r vga.start(16) '' Start the VGA driver vga.cls '' Clear the screen '' Mount the SD card. mount:= \sdfat.mount_explicit(DO, CLK, DI, CS) if mount < 0 vga.str(string("Failed to MOUNT",$0D)) abort vga.str(string("SD card was found and MOUNTED.")) vga.out($0D) '' Newline vga.out($0D) '' Over write some txt to an exiting file. sdfat.popen(string("test3.txt"),"w") sdfat.pputs(string("This is the xxxx line.",13)) '' Just the 13 works, no 10 sdfat.pputs(string("This is the yyyy line.",13)) sdfat.pclose vga.str(string("File has been wrtten to.",$0D)) vga.out($0D) ''Newline '' Show files on the SD card. vga.str(string("SD card Directory",$0D)) sdfat.opendir repeat while 0 == sdfat.nextfile(@tbuf) vga.str(@tbuf) vga.out($0D) vga.out($0D) '' Open the file for read and display contents. vga.str(string("Opening the file...",$0D)) r:=sdfat.popen(string("test3.txt"),"r") vga.str(string("File is Open!")) ''vga.dec(r) repeat 2 vga.out($0D) repeat r:=sdfat.pgetc '' Reads the open file one byte at a time. if r < 0 quit vga.out(r) '' One char at a time. sdfat.pclose '' Close the open file. sdfat.unmount '' Unmount the card. vga.out($0D) vga.str(string("SD card has been unmounted",$0D))pub readln(pntr, n) | c, len '' reads line of text from open file '' -- terminated by LF or EOF '' -- pntr is address of string buffer '' -- n is maximum number of characters to read from line len := 0 ' length of returned string repeat n c := \sdcard.pgetc ' get a character if ((c == LF) or (c < 0)) ' LF or EOF? quit else byte[pntr++] := c ' move c to buffer len++ ' update character count byte[pntr] := 0 ' terminate end of line return lenThe attached demo will probably be helpful, and contains a bunch of string methods that may be handy, too.
pub preadln(pntr, n) : len | c '' reads line of text from open file '' -> pntr is address of string buffer '' -> n is maximum number of characters to read from line\ '' <- len is the length of returned string '' -- terminated by LF or EOF repeat n c := pgetc ' get a character if ((c == 10) or (c =< 0)) ' LF or EOF? Oops, I had CR(13) instead of LF(10) quit ' all done byte[pntr][len++] := c ' move c to buffer byte[pntr][len] := 0 ' terminate end of linethanks,Jonathan
As far as I remember it reads 512 bytes. With the function getLine it simply searches for the lineend and replaces it with zero, so the buffer can simply be used as string.
With the next call you get back the address of the next line. If there is no lineend in the remaining buffer, the remaining part is copied to the beginning and the next bunch is loaded. That's the reason why the buffer has more than 512 bytes. It needs max. linelength more bytes.
The config reader is doing even more. It not only reads a file, it parses it's content. If you change the separator from " " to "," you can easily read CSV files with it and find the values in an array of longs.
You should use "c == 10" in your comparison instead of "c == 13" (the LF character is 10). Windows text files also contain a CR, but it is always before the LF. You may want to include the CR and LF characters in the string, some programs may want to know how the string was terminated, or you could have another version of the routine that strips them out. You could make it faster if you duplicate some of the code from pread in preadln.
Dave
Dave, good point about the 10 vs 13, it's my "yes, I'm using Windows" tell...sorry about that! And yes, there's plenty of room for speed-ups, again just not sure if the code-space/speed tradeoff is justified, especially for people using the Propeller IDE instead of BST (where the unused functions would get stripped). My personal leaning is to just add in the simple version of the function (maybe with a terminating character as a function parameter).
Thanks for the feedback, everybody!
Jonathan
Ray
''demo2_sd_030.spin CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 PS2_DATA = 26 PS2_CLK = 27 DO = 0 CLK = 1 DI = 2 CS = 3 ccnt = 32 lccnt = 12 VAR byte tbuf[20] byte buffer[ccnt] long index byte lbuffer[lccnt] long lindex OBJ vga : "MikronautsVGA64" sdfat : "fsrw" kbd : "keyboard_010" PUB Start | mount vga.start(16) vga.cls kbd.start(PS2_DATA, PS2_CLK) kbd.clearkeys mount:= \sdfat.mount_explicit(DO, CLK, DI, CS) if mount < 0 vga.str(string("SD Failed to MOUNT!",$0D)) abort vga.str(string("SD card was found and MOUNTED!",$0D,$0D)) Main PUB Main vga.str(string("DEMO2 SD use and control!",$0D)) vga.out(">") repeat get_keystring vga.blink(100) PUB get_keystring | c if (kbd.gotkey == TRUE) c:=kbd.key vga.out(c) if index < constant(ccnt-1) buffer[index++]:=c if c == $0D buffer[index-1]:=0 if (@buffer <> @dir) if (@buffer <> @type) vga.str(string("No such command!",$0D)) if strcomp(@dir,@buffer) dir_display if strcomp(@type,@buffer) type_file index~ bytefill(@buffer, 0, ccnt) vga.out(">") PUB type_file | r,c vga.cls vga.gotoxy(0,0) vga.str(string("File Name: ")) '' Type in the file name 8.3 repeat c:=kbd.key if lindex < constant(lccnt-1) lbuffer[lindex++]:=c if c == $0D lbuffer[lindex-1]:=0 lindex~ bytefill(@lbuffer, 0, lccnt) vga.str(string("Opening a file...",$0D)) ''r:= sdfat.popen(string("test3.txt"),"r") r:= sdfat.popen(string(@lbuffer),"r") '' @lbuffer does not work repeat 2 vga.out($0D) repeat r:=sdfat.pgetc if r < 0 quit vga.out(r) sdfat.pclose vga.out($0D) PUB dir_display vga.cls vga.gotoxy(0,0) vga.str(string("SD Directory: ",$0D,$0D)) sdfat.opendir repeat while 0 == sdfat.nextfile(@tbuf) vga.str(@tbuf) vga.out($0D) DAT dir byte "dir",0 type byte "type",0remove the bold part.
if (@buffer <> @dir) if (@buffer <> @type) vga.str(string("No such command!",$0D))should beif not strcomp(@buffer, @dir) and not strcomp(@buffer, @type) vga.str(string("No such command!",$0D))or you could do something likeif strcomp(@dir,@buffer) dir_display elseif strcomp(@type,@buffer) type_file else vga.str(string("No such command!",$0D))Also,
if (kbd.gotkey == TRUE) c:=kbd.key vga.out(c) if index < constant(ccnt-1) buffer[index++]:=cshould be something likerepeat repeat while not kbd.gotkey c := kbd.key vga.out(c) if c == 13 quit buffer[index++] := c if index => constant(ccnt-1) quit buffer[index] := 0Ray
''demo2_sd_030.spin CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 PS2_DATA = 26 PS2_CLK = 27 DO = 0 CLK = 1 DI = 2 CS = 3 ccnt = 32 lccnt = 12 VAR byte tbuf[20] byte buffer[ccnt] long index byte lbuffer[lccnt] long lindex OBJ vga : "MikronautsVGA64" sdfat : "fsrw" kbd : "keyboard_010" PUB Start | mount vga.start(16) vga.cls kbd.start(PS2_DATA, PS2_CLK) kbd.clearkeys mount:= \sdfat.mount_explicit(DO, CLK, DI, CS) if mount < 0 vga.str(string("SD Failed to MOUNT!",$0D)) abort vga.str(string("SD card was found and MOUNTED!",$0D,$0D)) Main PUB Main vga.str(string("DEMO2 SD use and control!",$0D)) vga.out(">") repeat get_keystring vga.blink(100) PUB get_keystring | c if (kbd.gotkey == TRUE) c:=kbd.key vga.out(c) if index < constant(ccnt-1) buffer[index++]:=c if c == $0D buffer[index-1]:=0 if (@buffer <> @dir) if (@buffer <> @type) vga.str(string("No such command!",$0D)) if strcomp(@dir,@buffer) dir_display if strcomp(@type,@buffer) type_file index~ bytefill(@buffer, 0, ccnt) vga.out(">") PUB type_file | r,c,f vga.cls vga.gotoxy(0,0) vga.str(string("File Name: ")) '' Type in the file name 8.3 repeat if (kbd.gotkey == TRUE) c:=kbd.key vga.out(c) if lindex < constant(lccnt-1) lbuffer[lindex++]:=c vga.blink(100) until c == $0D '' if c == $0D lbuffer[lindex-1]:=0 vga.str(string("Opening a file...",$0D)) '' ' 'r:= sdfat.popen(string("test3.txt"),"r") r:= sdfat.popen(@lbuffer,"r") '' @lbuffer does not work repeat 2 vga.out($0D) repeat r:=sdfat.pgetc if r < 0 '' '' quit return vga.out(r) lindex~ bytefill(@lbuffer, 0, lccnt) sdfat.pclose vga.out($0D) PUB dir_display vga.cls vga.gotoxy(0,0) vga.str(string("SD Directory: ",$0D,$0D)) sdfat.opendir repeat while 0 == sdfat.nextfile(@tbuf) vga.str(@tbuf) vga.out($0D) DAT dir byte "dir",0 type byte "type",0Jonathan
(going to edit that right now)
pub readln(pntr, n) | c, len '' reads line of text from open file '' -- terminated by LF or EOF '' -- pntr is address of string buffer '' -- n is maximum number of characters to read from line len := 0 ' length of returned string repeat n c := \sdcard.pgetc ' get a character if ((c == LF) or (c =< 0)) ' LF or EOF? quit byte[pntr][len++] := c ' move c to buffer byte[pntr][len] := 0 ' terminate end of line return lenAny chance the udpate is going to include multi-file support? I don't care about folders for my projects, but I'd love to be pulling audio and animatronic control data from the same SD card.
Jonathan
EDIT:
if you make len your return variable, it is initialized to 0 for you, and is returned at the end of the routine, saving 2 lines of code.
So you can also read a string to the next comma or space or any other character:
pub readStr(pntr, tchar, n) : len | c '' reads text from open file '' -- terminated by tchar or EOF '' -- pntr is address of string buffer '' -- tchar is the termination character (i.e. 0 10 13 " " "," ";") '' -- n is maximum number of characters to read from line '' <- returns lenght of string repeat n c := \sdcard.pgetc ' get a character if c == tchar or c =< 0 ' termination char or EOF? quit byte[pntr][len++] := c ' move c to buffer byte[pntr][len] := 0 ' terminate end of lineAndy
So, basically you should type in 'help' to list the command list, please let me know if there are any major problems. Of course this works with the VGA monitor, in the 64x24 mode, and is using the standard 5MHz crystal.
Ray
Ray, there is an example similar to this which I think is in the FSRW 2.6 distribution zip, but I'm including it here as an attachment. It uses the serial terminal instead of VGA and keyboard.
Jonathan
Since everything is geared for TV, or serial terminal, I decided that I wanted to work in VGA. Now, everybody has some choices, and you are not funneled into TV, or serial terminal mode. Long live VGA LOL
Ray
You should change the logic at the beginning of get_keystring so that it returns "if kbd.gotkey <> true". Currently, if kbd.gotkey is not true the "if c == $0D" statement is executed using an uninitialized value for c. c is a stack variable, and it's initial value is unknown. Your code is working OK because vga.blink is probably changing the stack location that c uses to something other than $0D.
You should also include the keyboard_010.spin and MikronautsVGA64.spin objects that you are using in the ZIP file. keyboard_10.spin is not in the ZIP file, and the copy of MikronautsVGA64.spin that is in the ZIP file doesn't have the blink and gotoxy methods.
Ray