Chapter 9 Official Guide, changing the base pin on the SD card
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!!
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 0You 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.