Chapter 9 Official Guide, changing the base pin on the SD card
SteveWoodrough
Posts: 190
I'm going through the exercise in Chapter 9 of the Official Guide, and I'm having trouble changing the SD card pin outs to work with my setup. If I use the base pin of 2 everything works just fine. But if I try to change the base pin to something other than 2 the SD card will not mount and log data. I think the reason is the given pin sequece is somehow backwards from the logic. Here are the actual pin numbers that work:
Pin 2 CS
Pin 3 DI
Pin 4 SCLK
Pin 5 DO
This sequence works just fine and creates a data table log on the SD card when the base pin is specified to be 2. What is strange is that per the documentation in the code, DO should be the base pin followed by SCLK, DI and CS. So for some reason the code works as published but when I try to change the pin selections, it does not work.
Below is the main code and the two other referenced objects.
Thank You
Steve
Pin 2 CS
Pin 3 DI
Pin 4 SCLK
Pin 5 DO
This sequence works just fine and creates a data table log on the SD card when the base pin is specified to be 2. What is strange is that per the documentation in the code, DO should be the base pin followed by SCLK, DI and CS. So for some reason the code works as published but when I try to change the pin selections, it does not work.
Below is the main code and the two other referenced objects.
Thank You
Steve
''*************************************** ''* Main Sensor Logger code * ''* Author: Joshua Hintze * ''* Copyright (c) 2009 * ''*************************************** CON 'Set operating speed to 80 MHz per core thus we operate at ~20 MIPS 1/4 of clock rate _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 START_LOGGING_SWITCH = 16 STOP_LOGGING_SWITCH = 17 LED_MOUNT_SD = 18 LED_GPS_SATS = 19 LED_HEART_BEAT = 20 LED_PRESS_GOOD = 21 LED_RECORDING = 22 OBJ text : "TV_Text" ''TV Terminal is used for debug gps : "GPS_IO_mini" ''Simple GPS COG serial parser. Reads in NMEA sentences sdfat : "fsrw" ''Fat16 SD card object alt : "abs_pressure_01" ''Absolute pressure sensor conv : "numbers" ''Conversion for string to number and vice versa var byte tbuf[20] long stringptr long stringlen long sdmounted long sdrecording pub main | gmt, r 'Start the required cogs gps.start(1, 9600) 'Initialize on pin 0 at 4800 baud text.start(12) 'TV terminal begins at pin 12 alt.start 'Initialize and run the alt sensor (ABS pressure) conv.init 'Initialize the conversion object sdrecording := 0 'Start off not recording until a button is pressed { The order of the pins goes as follows: FirstPin = DataOut from SD card (MISO on propeller) NextPin = Clk for the SD card (SCLK) NextPin = DataIn to the SD card (MOSI on propeller) NextPin = ChipSelect (Controls SD card CS) } dira[18..23]~~ 'Flag LEDs to show we have are succsessfull outa[18..17]~~ 'LEDs on mean OK if \sdfat.mount(2) <> 0 'Mount SD card starting on pin 2 outa[LED_MOUNT_SD] := 0 text.str(string(13,"Failed to mount SD")) sdmounted := 0 else sdmounted := 1 text.str(string("Mounted.", 13)) text.str(string("Dir: ", 13)) sdfat.opendir repeat while 0 == sdfat.nextfile(@tbuf) text.str(@tbuf) text.out(13) text.str(string("That's the dir", 13)) text.out(13) 'Pause for one second waitcnt(1*clkfreq + cnt) repeat r from 0 to 16 text.out(13) ' Start drawing to the TV repeat ' Set the terminal X and Y position back to 0,0 text.out($0A) ' Pass in the column flag text.out(0) ' Now the X position = 0 text.out($0B) ' Pass in the row flag text.out(0) ' Now the Y position = 0 text.str((string("Latitude "))) text.str(gps.N_S) text.out($20) text.str(gps.latitude_degpart) text.out($20) text.str(gps.latitude_minpart) text.out(13) text.str((string("Longitude "))) text.str(gps.e_w) text.out($20) text.str(gps.longitude_degpart) text.out($20) text.str(gps.longitude_minpart) text.out(13) text.str((string("GPS Altitude "))) text.str(gps.GPSaltitude) text.out(13) text.str((string("Speed "))) text.str(gps.speed) text.out(13) text.str((string("Satellites "))) text.str(gps.satellites) text.out(13) text.str((string("Time GMT "))) text.str(gps.time) text.out(13) text.str((string("Date "))) text.str(gps.date) text.out(13) text.str((string("Heading "))) text.str(gps.heading) text.out(13) text.str(string("temp ")) text.dec(alt.get_temperature) text.out(13) text.str(string("pressure ")) text.dec(alt.get_pressure) text.out(13) { text.dec(alt.get_c1) text.out($20) text.dec(alt.get_c2) text.out($20) text.dec(alt.get_c3) text.out($20) text.dec(alt.get_c4) text.out($20) text.dec(alt.get_c5) text.out($20) text.dec(alt.get_c6) text.out(13) text.dec(alt.get_d1) text.out($20) text.dec(alt.get_d2) text.out($20) text.dec(alt.get_calib_temp) text.out($20) text.dec(alt.get_dt) text.out($20) text.out(13) } 'now output to the file the values we have if sdrecording == 1 \sdfat.sdstr(gps.latitude_degpart) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.latitude_minpart) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.N_S) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.longitude_degpart) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.longitude_minpart) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.e_w) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.GPSaltitude) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.speed) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.satellites) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.time) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.date) \sdfat.sdstr(string(", ")) \sdfat.sdstr(gps.heading) \sdfat.sdstr(string(", ")) \sdfat.sddec(alt.get_temperature) \sdfat.sdstr(string(", ")) \sdfat.sddec(alt.get_pressure) \sdfat.sdstr(string(13,10)) \sdfat.pflush if conv.FromStr(gps.satellites,conv#DEC) < 3 outa[LED_GPS_SATS] := 0 else outa[LED_GPS_SATS] := 1 if (alt.get_temperature > 200) and (alt.get_temperature < 300) outa[LED_PRESS_GOOD] := 1 else outa[LED_PRESS_GOOD] := 0 'Check for switch press if (sdrecording == 0) and (ina[START_LOGGING_SWITCH] == 0) OpenNewFile if(sdrecording == 1) outa[LED_RECORDING] := 1 if (sdrecording == 1) and ina[STOP_LOGGING_SWITCH] == 0 sdrecording := 0 outa[LED_RECORDING] := 0 'toggle heartbeat led !outa[LED_HEART_BEAT] 'now wait some time before doing it again waitcnt(clkfreq / 4 + cnt) PUB OpenNewFile | index, fptr 'Now check to see if the SD card was mounted successfully. If it was we need to find a file 'to open. In this case we will attempt to open a file in read mode named sensorXX.log where XX 'ranges from 00 to 99. If the read fails it means there isn't a file that exists and therefore 'we can use that file name if sdmounted == 1 repeat index from 0 to 99 bytemove(@tbuf[0],string("sensor"),6) stringptr := conv.ToStr(index,conv#DEC) + 1 'for some reason it puts a space on the front (+/- sign maybe?) stringlen := strsize(stringptr) bytemove(@tbuf[6],stringptr,stringlen) bytemove(@tbuf[6+stringlen],string(".log"),4) tbuf[10+stringlen] := 0 if(\sdfat.popen(@tbuf, "r")) == -1 quit fptr := sdfat.popen(@tbuf, "w") sdfat.sdstr(string("Lat Deg, Lat Min, Lat (N/S), Long Deg, Long Min, Long (W/E), GPS Alt, Speed, Sats, Time GMT, Date, Heading, Temp, Pressure",13,10)) text.str(string("Openned file named: ")) text.str(@tbuf[0]) sdrecording := 1 DAT {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
{{ ' fsrw.spin 1.6 Copyright 2008 Radical Eye Software ' ' See end of file for terms of use. ' ' This object provides FAT16 file read/write access on a block device. ' Only one file open at a time. Open modes are 'r' (read), 'a' (append), ' 'w' (write), and 'd' (delete). Only the root directory is supported. ' No long filenames are supported. We also support traversing the ' root directory. ' ' In general, negative return values are errors; positive return ' values are success. Other than -1 on popen when the file does not ' exist, all negative return values will be "aborted" rather than ' returned. ' ' Changes: ' v1.1 28 December 2006 Fixed offset for ctime ' v1.2 29 December 2006 Made default block driver be fast one ' v1.3 6 January 2007 Added some docs, and a faster asm ' v1.4 4 February 2007 Rearranged vars to save memory; ' eliminated need for adjacent pins; ' reduced idle current consumption; added ' sample code with abort code data ' v1.5 7 April 2007 Fixed problem when directory is larger ' than a cluster. ' v1.6 23 September 2008 Fixed a bug found when mixing pputc ' with pwrite. Also made the assembly ' routines a bit more cautious. ' ' Third Party Changes: ' v1.7 20 December 2008 Changes by John Twomey AKA: QuattroRS4 ' ------------------------------------------------ ' Added 'SDstr' to Simplify Writing of strings ' to SD Card. Use as follows - ' sdfat.sdstr(string("Test String")) ' sdfat.sdstr(string("Test String",13,10)) ' the latter example adds CR & LF. ' ' Added 'SDdec' to simplify writing of decimal ' values to SD Card. Use as follows - ' sdfat.sddec(25) ' The above example writes 25 to SDCard. ' ' Added 'SDhex' to Simplify Writing of Hexadecimal ' values to SD Card'. Use as follows - ' sdfat.sdhex(25) ' The above example writes 19 to SD card. ' ' Added 'SDbin' to Simplify Writing of Binary ' to SD Card'. Use as follows - ' sdfat.sdbin(254,8) ' The above example writes 11111110 to SD card. ' ' Comments added to Code Additions also. ' ------------------------------------------------ ' ' }} ' ' Constants describing FAT volumes. ' con SECTORSIZE = 512 SECTORSHIFT = 9 DIRSIZE = 32 DIRSHIFT = 5 ' ' The object that provides the block-level access. ' obj sdspi: "sdspi" var ' ' ' Variables concerning the open file. ' long fclust ' the current cluster number long filesize ' the total current size of the file long floc ' the seek position of the file long frem ' how many bytes remain in this cluster from this file long bufat ' where in the buffer our current character is long bufend ' the last valid character (read) or free position (write) long direntry ' the byte address of the directory entry (if open for write) long writelink ' the byte offset of the disk location to store a new cluster long fatptr ' the byte address of the most recently written fat entry ' ' Variables used when mounting to describe the FAT layout of the card. ' long rootdir ' the byte address of the start of the root directory long rootdirend ' the byte immediately following the root directory. long dataregion ' the start of the data region, offset by two sectors long clustershift ' log base 2 of blocks per cluster long fat1 ' the block address of the fat1 space long totclusters ' how many clusters in the volume long sectorsperfat ' how many sectors per fat ' ' Variables controlling the caching. ' long lastread ' the block address of the buf2 contents long dirty ' nonzero if buf2 is dirty ' ' Buffering: two sector buffers. These two buffers must be longword ' aligned! To ensure this, make sure they are the first byte variables ' defined in this object. ' byte buf[SECTORSIZE] ' main data buffer byte buf2[SECTORSIZE] ' main metadata buffer byte padname[11] ' filename buffer pri writeblock2(n, b) ' ' On metadata writes, if we are updating the FAT region, also update ' the second FAT region. ' sdspi.writeblock(n, b) if (n => fat1 and n < fat1 + sectorsperfat) sdspi.writeblock(n+sectorsperfat, b) pri flushifdirty ' ' If the metadata block is dirty, write it out. ' if (dirty) writeblock2(lastread, @buf2) dirty := 0 pri readblockc(n) ' ' Read a block into the metadata buffer, if that block is not already ' there. ' if (n <> lastread) flushifdirty sdspi.readblock(n, @buf2) lastread := n pri brword(b) ' ' Read a byte-reversed word from a (possibly odd) address. ' return (byte[b]) + ((byte[b][1]) << 8) pri brlong(b) ' ' Read a byte-reversed long from a (possibly odd) address. ' return brword(b) + (brword(b+2) << 16) pri brwword(w, v) ' ' Write a byte-reversed word to a (possibly odd) address, and ' mark the metadata buffer as dirty. ' byte[w++] := v byte[w] := v >> 8 dirty := 1 pri brwlong(w, v) ' ' Write a byte-reversed long to a (possibly odd) address, and ' mark the metadata buffer as dirty. ' brwword(w, v) brwword(w+2, v >> 16) pub mount(basepin) | start, sectorspercluster, reserved, rootentries, sectors {{ ' Mount a volume. The address passed in is passed along to the block ' layer; see the currently used block layer for documentation. If the ' volume mounts, a 0 is returned, else abort is called. }} sdspi.start(basepin) lastread := -1 dirty := 0 sdspi.readblock(0, @buf) if (brlong(@buf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24))) start := 0 else start := brlong(@buf+$1c6) sdspi.readblock(start, @buf) if (brlong(@buf+$36) <> constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) or buf[$3a] <> "6") abort(-20) ' not a fat16 volume if (brword(@buf+$0b) <> SECTORSIZE) abort(-21) ' bad bytes per sector sectorspercluster := buf[$0d] if (sectorspercluster & (sectorspercluster - 1)) abort(-22) ' bad sectors per cluster clustershift := 0 repeat while (sectorspercluster > 1) clustershift++ sectorspercluster >>= 1 sectorspercluster := 1 << clustershift reserved := brword(@buf+$0e) if (buf[$10] <> 2) abort(-23) ' not two FATs rootentries := brword(@buf+$11) sectors := brword(@buf+$13) if (sectors == 0) sectors := brlong(@buf+$20) sectorsperfat := brword(@buf+$16) if (brword(@buf+$1fe) <> $aa55) abort(-24) ' bad FAT signature fat1 := start + reserved rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT rootdirend := rootdir + (rootentries << DIRSHIFT) dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster totclusters := ((sectors - dataregion + start) >> clustershift) if (totclusters > $fff0) abort(-25) ' too many clusters return 0 pri readbytec(byteloc) ' ' Read a byte address from the disk through the metadata buffer and ' return a pointer to that location. ' readblockc(byteloc >> SECTORSHIFT) return @buf2 + (byteloc & constant(SECTORSIZE - 1)) pri readfat(clust) ' ' Read a fat location and return a pointer to the location of that ' entry. ' fatptr := (fat1 << SECTORSHIFT) + (clust << 1) return readbytec(fatptr) pri followchain | clust ' ' Follow the fat chain and update the writelink. ' clust := brword(readfat(fclust)) writelink := fatptr return clust pri nextcluster | clust ' ' Read the next cluster and return it. Set up writelink to ' point to the cluster we just read, for later updating. If the ' cluster number is bad, return a negative number. ' clust := followchain if (clust < 2 or clust => totclusters) abort(-9) ' bad cluster value return clust pri freeclusters(clust) | bp ' ' Free an entire cluster chain. Used by remove and by overwrite. ' Assumes the pointer has already been cleared/set to $ffff. ' repeat while (clust < $fff0) if (clust < 2) abort(-26) ' bad cluster number") bp := readfat(clust) clust := brword(bp) brwword(bp, 0) flushifdirty pri datablock ' ' Calculate the block address of the current data location. ' return (fclust << clustershift) + dataregion + ((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) pri uc(c) ' ' Compute the upper case version of a character. ' if ("a" =< c and c =< "z") return c - 32 return c pri pflushbuf(r, metadata) | cluststart, newcluster, count, i ' ' Flush the current buffer, if we are open for write. This may ' allocate a new cluster if needed. If metadata is true, the ' metadata is written through to disk including any FAT cluster ' allocations and also the file size in the directory entry. ' if (direntry == 0) abort(-27) ' not open for writing if (r > 0) ' must *not* allocate cluster if flushing an empty buffer if (frem < SECTORSIZE) ' find a new clustercould be anywhere! If possible, stay on the ' same page used for the last cluster. newcluster := -1 cluststart := fclust & constant(!((SECTORSIZE >> 1) - 1)) count := 2 repeat readfat(cluststart) repeat i from 0 to constant(SECTORSIZE - 2) step 2 if (buf2[i]==0 and buf2[i+1]==0) newcluster := cluststart + (i >> 1) if (newcluster => totclusters) newcluster := -1 quit if (newcluster > 1) brwword(@buf2+i, -1) brwword(readbytec(writelink), newcluster) writelink := fatptr + i fclust := newcluster frem := SECTORSIZE << clustershift quit else cluststart += constant(SECTORSIZE >> 1) if (cluststart => totclusters) cluststart := 0 count-- if (count < 0) r := -5 ' No space left on device quit if (frem => SECTORSIZE) sdspi.writeblock(datablock, @buf) if (r == SECTORSIZE) ' full buffer, clear it floc += r frem -= r bufat := 0 bufend := r else ' not a full blockleave pointers alone if (r < 0 or metadata) ' update metadata even if error readblockc(direntry >> SECTORSHIFT) ' flushes unwritten FAT too brwlong(@buf2+(direntry & constant(SECTORSIZE-1))+28, floc+bufat) flushifdirty if (r < 0) abort(r) return r pub pflush {{ ' Call flush with the current data buffer location, and the flush ' metadata flag set. }} return pflushbuf(bufat, 1) pri pfillbuf | r ' ' Get some data into an empty buffer. If no more data is available, ' return -1. Otherwise return the number of bytes read into the ' buffer. ' if (floc => filesize) return -1 if (frem == 0) fclust := nextcluster frem := SECTORSIZE << clustershift if (frem + floc > filesize) frem := filesize - floc sdspi.readblock(datablock, @buf) r := SECTORSIZE if (floc + r => filesize) r := filesize - floc floc += r frem -= r bufat := 0 bufend := r return r pub pclose | r {{ ' Flush and close the currently open file if any. Also reset the ' pointers to valid values. If there is no error, 0 will be returned. }} r := 0 if (direntry) r := pflush bufat := 0 bufend := 0 filesize := 0 floc := 0 frem := 0 writelink := 0 direntry := 0 fclust := 0 return r pri pdate {{ ' Get the current date and time, as a long, in the format required ' by FAT16. Right now it"s hardwired to return the date this ' software was created on (April 7, 2007). You can change this ' to return a valid date/time if you have access to this data in ' your setup. }} return constant(((2007-1980) << 25) + (1 << 21) + (7 << 16) + (4 << 11)) pub popen(s, mode) | i, sentinel, dirptr, freeentry {{ ' Close any currently open file, and open a new one with the given ' file name and mode. Mode can be "r" "w" "a" or "d" (delete). ' If the file is opened successfully, 0 will be returned. If the ' file did not exist, and the mode was not "w" or "a", -1 will be ' returned. Otherwise abort will be called with a negative error ' code. }} pclose i := 0 repeat while (i<8 and byte[s] and byte[s] <> ".") padname[i++] := uc(byte[s++]) repeat while (i<8) padname[i++] := " " repeat while (byte[s] and byte[s] <> ".") s++ if (byte[s] == ".") s++ repeat while (i<11 and byte[s]) padname[i++] := uc(byte[s++]) repeat while (i < 11) padname[i++] := " " sentinel := 0 freeentry := 0 repeat dirptr from rootdir to rootdirend - DIRSIZE step DIRSIZE s := readbytec(dirptr) if (freeentry == 0 and (byte[s] == 0 or byte[s] == $e5)) freeentry := dirptr if (byte[s] == 0) sentinel := dirptr quit repeat i from 0 to 10 if (padname[i] <> byte[s][i]) quit if (i == 11 and 0 == (byte[s][$0b] & $18)) ' this always returns fclust := brword(s+$1a) filesize := brlong(s+$1c) if (mode == "r") frem := SECTORSIZE << clustershift if (frem > filesize) frem := filesize return 0 if (byte[s][11] & $d9) abort(-6) ' no permission to write if (mode == "d") brwword(s, $e5) freeclusters(fclust) flushifdirty return 0 if (mode == "w") brwword(s+26, -1) brwlong(s+28, 0) writelink := dirptr + 26 direntry := dirptr freeclusters(fclust) bufend := SECTORSIZE fclust := 0 filesize := 0 frem := 0 return 0 elseif (mode == "a") ' this code will eventually be moved to seek frem := filesize freeentry := SECTORSIZE << clustershift if (fclust => $fff0) fclust := 0 repeat while (frem > freeentry) if (fclust < 2) abort(-7) ' eof repeat while following chain fclust := nextcluster frem -= freeentry floc := filesize & constant(!(SECTORSIZE - 1)) bufend := SECTORSIZE bufat := frem & constant(SECTORSIZE - 1) writelink := dirptr + 26 direntry := dirptr if (bufat) sdspi.readblock(datablock, @buf) frem := freeentry - (floc & (freeentry - 1)) else if (fclust < 2 or frem == freeentry) frem := 0 else frem := freeentry - (floc & (freeentry - 1)) if (fclust => 2) followchain return 0 else abort(-3) ' bad argument if (mode <> "w" and mode <> "a") return -1 ' not found direntry := freeentry if (direntry == 0) abort(-2) ' no empty directory entry ' write (or new append): create valid directory entry s := readbytec(direntry) bytefill(s, 0, DIRSIZE) bytemove(s, @padname, 11) brwword(s+26, -1) i := pdate brwlong(s+$e, i) ' write create time and date brwlong(s+$16, i) ' write last modified date and time if (direntry == sentinel and direntry + DIRSIZE < rootdirend) brwword(readbytec(direntry+DIRSIZE), 0) flushifdirty writelink := direntry + 26 fclust := 0 bufend := SECTORSIZE return 0 pub pread(ubuf, count) | r, t {{ ' Read count bytes into the buffer ubuf. Returns the number of bytes ' successfully read, or a negative number if there is an error. ' The buffer may be as large as you want. }} r := 0 repeat while (count > 0) if (bufat => bufend) t := pfillbuf if (t =< 0) if (r > 0) return r return t t := bufend - bufat if (t > count) t := count bytemove(ubuf, @buf+bufat, t) bufat += t r += t ubuf += t count -= t return r pub pgetc | t {{ ' Read and return a single character. If the end of file is ' reached, -1 will be returned. If an error occurs, a negative ' number will be returned. }} if (bufat => bufend) t := pfillbuf if (t =< 0) return -1 return (buf[bufat++]) pub pwrite(ubuf, count) | r, t {{ ' Write count bytes from the buffer ubuf. Returns the number of bytes ' successfully written, or a negative number if there is an error. ' The buffer may be as large as you want. }} t := 0 repeat while (count > 0) if (bufat => bufend) t := pflushbuf(bufat, 0) t := bufend - bufat if (t > count) t := count bytemove(@buf+bufat, ubuf, t) r += t bufat += t ubuf += t count -= t return t pub pputc(c) {{ ' Write a single character into the file open for write. Returns ' 0 if successful, or a negative number if some error occurred. }} if (bufat == SECTORSIZE) pflushbuf(SECTORSIZE, 0) buf[bufat++] := c return 0 Pub SDStr(ptr) {{ ' Addition by John Twomey AKA: QuattroRS4 ' Routine added to write a string ' Use as follows in Program - ' sdfat.sdstr(string("this string will be written to SDcard")) ' ' can also do - ' sdfat.sdstr(string("this string will be written to SDcard",13,10)) ' the above adds CR & LF (Carriage Return and Line Feed) }} repeat while byte[ptr] pputc(byte[ptr++]) PUB SDdec(value) | i {{ ' Addition by John Twomey AKA: QuattroRS4 ' Routine added to write decimal values to SD Card ' Use as follows in Program - ' sdfat.sddec(25) ' writes 25 to SDCard }} if value < 0 -value pputc("-") i := 1_000_000_000 repeat 10 if value => i pputc(value / i + "0") value //= i result~~ elseif result or i == 1 pputc("0") i /= 10 PUB SDhex(value, digits) {{ ' Addition by John Twomey AKA: QuattroRS4 ' Routine added to write hexadecimal values to SD Card ' Use as follows in Program - ' sdfat.sdhex(25) 'this example writes 19 to SD card }} value <<= (8 - digits) << 2 repeat digits pputc(lookupz((value <-= 4) & $F : "0".."9", "A".."F")) PUB SDbin(value, digits) {{ ' Addition by John Twomey AKA: QuattroRS4 ' Routine added to write Binary number to SD Card ' Use as follows in Program - ' sdfat.sdbin(254,8) 'this example writes 11111110 to SD card }} value <<= 32 - digits repeat digits pputc((value <-= 1) & 1 + "0") pub opendir | off {{ ' Close the currently open file, and set up the read buffer for ' calls to nextfile. }} pclose off := rootdir - (dataregion << SECTORSHIFT) fclust := off >> (clustershift + SECTORSHIFT) floc := off - (fclust << (clustershift + SECTORSHIFT)) frem := rootdirend - rootdir filesize := floc + frem return 0 pub nextfile(fbuf) | i, t, at, lns {{ ' Find the next file in the root directory and extract its ' (8.3) name into fbuf. Fbuf must be sized to hold at least ' 13 characters (8 + 1 + 3 + 1). If there is no next file, ' -1 will be returned. If there is, 0 will be returned. }} repeat if (bufat => bufend) t := pfillbuf if (t < 0) return t if (((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) == 0) fclust++ at := @buf + bufat if (byte[at] == 0) return -1 bufat += DIRSIZE if (byte[at] <> $e5 and (byte[at][$0b] & $18) == 0) lns := fbuf repeat i from 0 to 10 byte[fbuf] := byte[at][i] fbuf++ if (byte[at][i] <> " ") lns := fbuf if (i == 7 or i == 10) fbuf := lns if (i == 7) byte[fbuf] := "." fbuf++ byte[fbuf] := 0 return 0 {{ ' Permission is hereby granted, free of charge, to any person obtaining ' a copy of this software and associated documentation files ' (the "Software"), to deal in the Software without restriction, ' including without limitation the rights to use, copy, modify, merge, ' publish, distribute, sublicense, and/or sell copies of the Software, ' and to permit persons to whom the Software is furnished to do so, ' subject to the following conditions: ' ' The above copyright notice and this permission notice shall be included ' in all copies or substantial portions of the Software. ' ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. }}
' sdspi: SPI interface to a Secure Digital card. ' ' Copyright 2008 Radical Eye Software ' ' See end of file for terms of use. ' ' This version is in Spin so it is very slow (3Kb/sec). ' A version in assembly is about 100x faster. ' ' You probably never want to call this; you want to use fswr ' instead (which calls this); this is only the lowest layer. ' ' Assumes SD card is interfaced using four consecutive Propeller ' pins, as follows (assuming the base pin is pin 0): ' 3.3v '       '       20k ' p0 ──────┻─┼─┼─┼─┼─┼────── do ' p1 ────────┻─┼─┼─┼─┼────── clk ' p2 ──────────┻─┼─┼─┼────── di ' p3 ────────────┻─┼─┼────── cs (dat3) ' 150 └─┼────── irq (dat1) ' └────── p9 (dat2) ' ' The 150 ohm resistors are current limiters and are only ' needed if you don't trust your code (and don't want an SD ' driven signal to conflict with a Propeller driven signal). ' A value of 150 should be okay, unless you've got some ' unusually high capacitance on the line. The 20k resistors ' are pullups, and should be there on all six lines (even ' the ones we don't drive). ' ' This code is not general-purpose SPI code; it's very specific ' to reading SD cards, although it can be used as an example. ' ' The code does not use CRC at the moment (this is the default). ' With some additional effort we can probe the card to see if it ' supports CRC, and if so, turn it on. ' ' All operations are guarded by a watchdog timer, just in case ' no card is plugged in or something else is wrong. If an ' operation does not complete in one second it is aborted. ' con sectorsize = 512 sectorshift = 9 var long di, do, clk, cs, starttime pri send(outv) ' ' Send eight bits, then raise di. ' outv ><= 8 repeat 8 outa[clk] := 0 outa[di] := outv outv >>= 1 outa[clk] := 1 outa[di] := 1 pri checktime ' ' Did we go over our time limit yet? ' if cnt - starttime > clkfreq abort -41 ' Timeout during read pri read | r ' ' Read eight bits from the card. ' r := 0 repeat 8 outa[clk] := 0 outa[clk] := 1 r += r + ina[do] return r pri readresp | r ' ' Read eight bits, and loop until we ' get something other than $ff. ' repeat if (r := read) <> $ff return r checktime pri busy | r ' ' Wait until card stops returning busy ' repeat if (r := read) return r checktime pri cmd(op, parm) ' ' Send a full command sequence, and get and ' return the response. We make sure cs is low, ' send the required eight clocks, then the ' command and parameter, and then the CRC for ' the only command that needs one (the first one). ' Finally we spin until we get a result. ' outa[cs]~ read send($40+op) send(parm >> 15) send(parm >> 7) send(parm << 1) send(0) send($95) return readresp pri endcmd ' ' Deselect the card to terminate a command. ' outa[cs] := 1 return 0 pub start(basepin) ' ' Initialize the card! Send a whole bunch of ' clocks (in case the previous program crashed ' in the middle of a read command or something), ' then a reset command, and then wait until the ' card goes idle. If you want to change this ' method to make the pins not be adjacent, all you ' need to do is change these first four lines. ' cs := basepin++ di := basepin++ clk := basepin++ do := basepin ' outa[clk] := outa[di] := outa[cs] := 1 dira[clk] := dira[di] := dira[cs] := 1 starttime := cnt repeat 600 read cmd(0, 0) endcmd repeat cmd(55, 0) basepin := cmd(41, 0) endcmd if basepin <> 1 quit if basepin abort -40 ' could not initialize card return 0 pub readblock(n, b) ' ' Read a single block. The "n" passed in is the ' block number (blocks are 512 bytes); the b passed ' in is the address of 512 blocks to fill with the ' data. ' starttime := cnt cmd(17, n) readresp repeat sectorsize byte[b++] := read read read return endcmd { pub getCSD(b) ' ' Read the CSD register. Passed in is a 16-byte ' buffer. ' starttime := cnt cmd(9, 0) readresp repeat 16 byte[b++] := read read read return endcmd } pub writeblock(n, b) ' ' Write a single block. Mirrors the read above. ' starttime := cnt cmd(24, n) send($fe) repeat sectorsize send(byte[b++]) read read if ((readresp & $1f) <> 5) abort -42 busy return endcmd {{ ' Permission is hereby granted, free of charge, to any person obtaining ' a copy of this software and associated documentation files ' (the "Software"), to deal in the Software without restriction, ' including without limitation the rights to use, copy, modify, merge, ' publish, distribute, sublicense, and/or sell copies of the Software, ' and to permit persons to whom the Software is furnished to do so, ' subject to the following conditions: ' ' The above copyright notice and this permission notice shall be included ' in all copies or substantial portions of the Software. ' ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. }}
Comments
Did you add the 20K pullup resisters?
I have connected my SD card to all kinds of different pins.
Duane J
This is how I made a bunch of these SD card adapters:
http://forums.parallax.com/showthread.php?130083-SD-Card-Pins
http://www.rayslogic.com/propeller/Programming/SD_Card/SD_Card.htm
I think the problem is in the code excerpt below. If I force the pins to be specific values:
cs := 2 di := 3 clk := 4 do := 5
cs := 3 di := 4 clk := 5 do := 6
the SD cards mounts and records GPS data just fine.
if I use
cs := 4 di := 5 clk := 6 do := 7
then the SD card will not mount.
Any ideas are welcome...
Thanks!!
You can individually specify the pins by using the startxxxx(x,x,x,xO method instead of the start method. I cannot recall the exact name of the startxxxx method so you will need to look at the driver.
Although what I have said here does not make sense with the results you seem to be getting, perhaps this will help.
BTW the order of the cs,di,clk,do lines in your quoted code does not make sense. Also I presume your example of setting cs, di etc on the one line is just a representation because you cannot set these on the one line in spin.
One last comment. I presume you dont have anything else on the lines 0-7 of the prop that could interfere.
With regard to the paramerters that work listed as:
cs := 2 di := 3 clk := 4 do := 5
cs := 3 di := 4 clk := 5 do := 6
I wrote all on one line just for clarity. Within the code each is on a seprate line.
My pin setup is taken directly from P.323 of the Official guide which lists:
Pin 2 CS
Pin 3 DI
Pin 4 SCLK
Pin 5 DO
I agree that is not the pin setup implied by the code, DO should be the lowest Pin number, but regardless it works.
Just for grins I did attempt to run DO to Pin 2, SCLK to PIN 3, DI to PIN 4, and CS to PIN 5 but it would not work.
You mention a Startxxx method that allowed individual pin selection. I cannot locate in the code I downloaded. I'm wondering if the code I downloaded has been replaced or updated. The code I got was downloaded from an FTP site listed in the book not from the OBEX. I'm not married to this code set, and if there is a better GPS data logger I'd be more than happy to try it out.
Best Regards,
Steve
I think if you are using the entire file from the Official Guide, and try to move the pins up to P04 or P05, you are stepping on the pin parameters that the Barometer has set.
If all you want is data logging with GPS, may I point you to this thread:
http://forums.parallax.com/showthread.php?104049-Google-Earth-GPS-SD-Card-Logger-COMPLETE!
It works great, and I have been using it for over two years on a 2GB uSD card.
I believe you could change the pins in this program.
Jim
EDIT: Sometimes people put the Base Pin to equal the high pin and count down from there. (IE Base pin P04 then count down to P0)
If your code called sdfat.mount(2) and it worked OK, then you must have DO on P2, SCLK on P3, DI on P4 and CS on P5. There is no way that it would work with the pinout that you described. Therefore, you must be confused about the labelling of the pins, and have it in the reversed order. When you moved to pins P3, P4, P5 and P6 did you change the call to mount to sdfat.mount(3)? This should have worked if you maintained the same pin order that you used before.
Dave
Thank You, I'll check that out. Right now everything is a learning experience. In writing my response below I just caught what you are saying. With my changes I'm trying to make PIN 7 do two things at once. Let me whack the pressure sensor object or free up references to PIN 7 and see if that fixes it. Like I said...Everything is a learing experience.
Dave,
Thank you for making me quintuple check my wiring and making me really think why it does work! I've attached photos of the SD card and now everybody can see just how bad my soldering skills are! As you can see the DO pin is near the card center and CS towards one edge. I've marked the backside of the card since that is the side nearest me. I also include a picture of the wiring on the Demo Board. This wiring sequence is exactly what is written on page 323 of the Official Guide. The software is the download from the FTP site, and when setup EXACTLY as prescribed....it works!!! I do not know enough to do this wrong, all I do is follow the instructions! When I follow the instructions, IT WORKS!! ;-)
The difficulty comes in when I try to use alternate pin outs.
I do not know very much except that in the bowels of the SDSPI object provided from the FTP site it is written:
cs := basepin++
di := basepin++
clk := basepin++
do := basepin
That seems a strange way to assign pins but if I understand this correctly, with basepin starting at 2, this would make CS = 2, post increment so that DI = 3, post increment so that CLK = 4, post increment so that DO = 5
Reflecting on this more deeply I think Jim is correct. I'm having PIN 7 try to do two things at once. Not a good strategy....
I am confused why your version of sdspi.spin has the pins in the reverse order. Where did you get that file?
BTW, there is a more recent version of FSRW that has a mount_explicit method that allows you to specify each of the pins independently. It's version 2.6. I suppose that with version 1.6 the sdspi.spin file had to be modified to accomodate pin assignments that didn't increment by 1 starting from DO.
Hi Dave,
The code came from here:
ftp://ftp.propeller-chip.com/PCMProp/Chapter_09/Source/
I would say Joshua did code it a little differently than normal, (reversing the base pin to be the higher number).
Jim
Thank You Dave and others who put a bit of time into this.