The file SDspi.fth contains a generic driver for SD and MMC cards in SPI mode. The low level SPI interface should provide the following words:
|
SPIRD and SPIWR are expected to be fast. Their semantics were chosen to minimize threading overhead for target Forths that use threaded code.
Please note the following restrictions.
For further details of SD and MMC cards see
http://www.sdcard.org/about/memory_card/pls/
http://wolverine.caltech.edu/referenc/SDMMCv52.pdf
The standard SD/MMC card pinout is as follows.
|
where the pins are numbered on the connector as below:
|
1 equ MciSem? \ -- x
Set this non-zero to use a an exclusive access semaphore in
the sector read/write routines SecRead and
SecWrite. MciSem? can be defined before this file
and will override this copy. This semaphore will be needed if
the disc can be accessed by other sources such as USB.
The SD error/throw codes are defined in terms of the err_FATread and err_FATwrite codes from XC6harn.fth.
err_FATread equ sd_read_fail \ SD card failure codes err_FATread 1+ equ sd_cmd_fail \ command hung err_FATwrite equ sd_write_fail \ write error err_FATwrite 1+ equ sd_reset_fail \ failure to reset
1000 equ sd_timeout \ -- ms
Timeout in milliseconds. Slower targets may want a smaller
SD_TIMEOUT value, especially those whose SPI interface
takes more than a millisecond per transfer. This usually only
comes into play when using a bit-banged interface.
: SD_idle \ cnt --
Send idle (0xFF) a specified number of times.
: SD_sendArg \ arg32 --
Send arg32 in big-endian format.
: SD_prep \ --
Prepare to send an SD command.
: SD_NB \ -- res8|-1
Wait until the card is not busy or times out, returning -1
for no response or the 8-bit response. In SPI mode, all
commands generate at least one byte of response.
: SD_send \ arg32 cmd6 -- result8 ; -1=timeout, other = result
Sends a command and tests for time-out, returning -1 for no
response or the 8-bit response. In SPI mode, all commands
generate at least one byte of response. The line is left with
CS active.
: +spi@ \ x -- x<<8+b
Shift x 8 bits left, read the next SPI byte in a response and
merge it.
: SD_ReadResp32 \ -- x32
Read four bytes in big-endian format.
: SD_sync \ --
Get the card into a known state.
0 value SD2card? \ -- flag
Returns true when the card follows SD v2 specification.
0 value HCcard?
Returns true when the card is a High Capacity (SDHC) card.
: tryCMD0 \ -- ior
Try 10 times at 100 ms intervals to reset the card, returning
0 for success.
: CMD8 \ -- b
Send command 8 for range 2.7-3.6 volts, and return 0 for a good
response, a non-zero response byte for a bad command, or -1 for
bad response data.
: CheckInit \ sd2? -- ior
Initialise the card. The parameter sd2 is -1 for an SD2
card or 0 for a SD1 card. Ior is returned 0 for success.
N.B. This operation may take several seconds.
: SD_reset \ --
Reset the card, and perform the initialisation sequence.
On failure, an sd_reset_fail throw occurs.
: SD_cmd \ arg32 cmd --
A more robust version of SD_send.
: SD_readstatus \ -- status
Read the SD card status register.
: SD_wait \ xt -- timeout?
Wait for condition specified by xt, which has the stack
effect ( -- flag) where flag is returned non-zero to
finish the operation before timeout.
; equ SD_writedone? \ -- xt
Gives the xt of a :NONAME word used with SD_wait
while writing a data block.
; equ SD_readready? \ -- xt
Gives the xt of a :NONAME word used with SD_wait
before reading a data block.
: SD_waitread \ --
Wait until ready to read.
: SD_readcsd \ addr --
Read the CSD - 16 bytes.
The basic sector read and write routines use the simple API above. If your SPI unit supports DMA operation, you can use it for the 512 byte block transfers by providing the words
sdWriteBlk \ asrc --
sdReadBlk \ adest --
These routines write and read the 512 bytes of data to and from the SPI bus.
: SD_addr \ blockno -- sdaddr
Convert a sector number to an SD card address.
: SD_writeblock \ asrc blockno --
Write a 512-byte block from address asrc to block blockno.
: SD_writeblock \ asrc blockno --
Write a 512-byte block from address asrc to block blockno.
: SD_readblock \ adest blockno --
Read a 512-byte block to address adest from block blockno.
: SD_readblock \ adest blockno --
Read a 512-byte block to address adest from block blockno.
The card CSD is read during initialisation into CSDbuff. You can assume that the card supports a minimum block size of 512 bytes. The size information is contained in the following byte offsets in CSDbuff.
|
The number of 512-byte blocks in the SD card is s * 2^(m+2+r-9). Formatting involves writing a new boot record and clearing the FAT table. Searching for bad sectors is recommended but a brute force search will take a long time. Other data in the CSD includes write-protect status and access times. Card formatting is not yet supported as it is assumed that cards will be formatted on a PC.
: GetBit \ caddr u -- 0/1
Get bit number u from a bit array. Bit 0 is the top bit
of the first byte. Bit 15 is the bottom bit of the second
byte.
: GetBits \ caddr start len -- x
Get len bits starting at bit start from the bitmap
starting at caddr.
0 value #Sectors
Number of sectors on the card.
#16 buffer: CSDbuff \ -- addr
Buffer to read CSD.
: CheckCSD \ --
Extract the CSD information, particularly the card capacity in
sectors.
The file system needs these words to access the mass storage:
SecRead |
( addr sector dev -- ) Reads a sector from the specified device. THROWs on error. |
SecWrite |
( addr sector dev -- ) Writes a sector to the specified device. THROWs on error. |
MassInit |
( -- ) Initializes mass storage access. |
MassTerm |
( -- ) Terminates mass storage access. |
To simplify the code, the raw read/write interface treats all read/write errors as fatal, and THROWs on error. Retries should be accommodated within the sector read/write code.
Semaphore SecSem \ -- addr
Exclusive access semaphore for sector read/write routines.
: SecRead \ addr sector dev --
Reads a sector from the specified device. THROWs on error.
: SecWrite \ addr sector dev --
Writes a sector to the specified device. THROWs on error.
: MassInit \ --
Initialize the mass storage manager. This is done when
a new card is detected.
: MassTerm \ --
Shut down the mass storage manager. This is done when a
card is removed.
: .sector \ u --
Read and dump a sector.
0 [if]
To compile the test code, change the conditional compilation
in the file from 0 [if] to 1 [if].
: ParseCSD \ *csd --
Parse the CSD structure and display the contents.
: .CSD \ --
Read and display the CSD contents.