I often see requests for information about accessing an MMC
(MultiMediaCard) disk with the LPC2106.
So, going off half cocked with the flush of initial success, I thought
I'd post the details of how I am doing it from Forth. I include the
actual code below as it is fairly short.
I am accessing an MMC disk with my Riscy Pygness Forth, using an Olimex
board to which I wired an MMC socket. Pictures (click on the thumbnails
to see larger versions) are at
http://pygmy.utoh.org/riscy/
(I'll be releasing Riscy Pygness as a completely free Forth (in the
MIT/BSD-license sense) for the Olimex and tiniARM boards. A *very*
preliminary version is already present on the above site.)
If you don't read Forth (yet), the following hints might make it
understandable enough for you to translate to assembler or C.
Definitions (code entry points) begin with a colon and end
(i.e. exit) with a semicolon. This is in the ColorForth style in
that a word can have multiple entry and exit points.
Comments begin with a left parenthesis followed by a space, and
extend until the following right parenthesis.
All characters are allowed in a definition's name. So
(GET-MMC-RESPONSE is not part of a comment but is a secondary entry
point as part of the definition for GET-MMC-RESPONSE. How can you
tell the left parenthesis did not start a comment? Because the left
parenthesis was not followed immediately by a space.
Stack comments are used at the beginning of a definition and at
various points within the definition, to show what is on the data
stack, etc. For example, the comment ( c - c) for SPI!@
(pronounced "ess pee eye store fetch" indicates that SPI!@ expects a
byte on the stack (which it will transmit via the SPI interface) and
will return a byte on the stack (the incoming byte from the SPI
interface).
Hexadecimal numbers begin with a leading dollar sign (e.g. $80).
Mini-glossary:
! ( u a -) "store" stores the 32-bit number u at the address a
@ ( a - u) "fetch" fetches the 32-bit number u from the address a
C@ ( a - c) "C fetch" fetches the byte c from address a
C! ( c a -) "C store" stores the byte c into address a
ROR24 ( u - u') rotates the 32-bit number u right by 24 bits, thus
moving the most significant byte into the least
significant byte, i.e. in assembler (where TOS
stands for "top of stack" and is an alias for
R0):
mov TOS, TOS, ror #24
To access the card, first initialize the SPI interface on the LPC2106
with the word SPI-CONFIGURE. Then, initialize the disk itself with
the word MMC-INIT. If all went well, MMC-INIT returns zero. After
that, blocks of data can be read or written with READ-MMC-BLOCK or
WRITE-MMC-BLOCK. The data for the writes come from a buffer address
that you specify. The data from the reads go into a 512-byte buffer
named MMC-BUFFER. Generally, the MMC-accessing words return a zero if
all went well and a non-zero error code otherwise.
I'll be glad to attempt to answer questions about it or to receive
comments or corrections.
Further plans include at least a minimal FAT16 layer.
--
Frank
( constants )
: PINSEL0 $E002C000 ;
: IOPIN $E0028000 ;
: IOSET $E0028004 ;
: IODIR $E0028008 ;
: IOCLR $E002800C ;
: SPCR $E0020000 ;
: SPSR $E0020004 ;
: SPDR $E0020008 ;
: SPCCR $E002000C ;
: SPINT $E002001C ;
( SPI interface to MMC disk )
: BITS-ON ( mask -) IOSET ! ;
: BITS-OFF ( mask -) IOCLR ! ;
: PIN13 ( - mask) $00002000 ;
: DISABLE-MMC ( -) PIN13 BITS-ON ;
: ENABLE-MMC ( -) PIN13 BITS-OFF ;
: SPI!@ ( c - c) ( send a byte then collect the returned byte)
SPDR ! BEGIN SPSR @ $80 AND UNTIL SPDR @ ;
: SPI! ( c -) ( send a byte but throw away returned byte)
SPI!@ DROP ;
: SPI@ ( - c) ( send a dummy byte and collect returned byte)
$FF SPI!@ ;
: DUMMY-SPI-BYTES ( # -) FOR $FF SPI! NEXT ;
: GET-MMC-RESPONSE ( - f)
( Try up to 256 times to get a response. A zero response )
( means no error. A valid response is a byte whose MSBit is zero.)
( Return -1 in case of a time-out. )
256 ( remaining tries) ( fall through to next word)
: (GET-MMC-RESPONSE ( remaining# - f) ( PAUSE )
SPI@ DUP $80 AND NOT IF NIP ( cardResponse) ; THEN DROP
( remaining#) 1- DUP 0= IF DROP -1 ; THEN
(GET-MMC-RESPONSE ;
: GET-DATA-TOKEN ( - error)
( Try up to 256 times to get a non $FF response. $FE is the value )
( we hope for. Return -1 in case of a time-out. )
256 ( remaining tries) ( fall through to next word)
: (GET-DATA-TOKEN ( remaining# - f) ( PAUSE )
SPI@ DUP $FF - IF NIP ( cardResponse) ; THEN DROP
( remaining#) 1- DUP 0= IF DROP -1 ; THEN
(GET-DATA-TOKEN ;
: FLUSH-MMC ( -) ( send it 8 clocks to finish whatever it needs to do)
DISABLE-MMC $FF SPI! ;
: SEND-MMC-COMMAND ( data cmd - error)
FLUSH-MMC ( Perhaps should be done at end of transaction rather )
( than at the start of the new transaction? )
ENABLE-MMC
$40 OR SPI!
( data) 4 FOR ROR24 DUP SPI! NEXT DROP
$95 ( fakeCRC) SPI!
GET-MMC-RESPONSE ( 0 means no error) ( error) ;
: CMD0 ( - f) 0 0 SEND-MMC-COMMAND ; ( go into idle state)
: CMD1 ( - f) 0 1 SEND-MMC-COMMAND ; ( is card initialized yet?)
: CMD9 ( - f) 0 9 SEND-MMC-COMMAND ; ( read CSD)
: CMD10 ( - f) 0 10 SEND-MMC-COMMAND ; ( read CID)
: CMD13 ( - f) 0 13 SEND-MMC-COMMAND ; ( get status)
: CMD16 ( blkLength - f) 16 SEND-MMC-COMMAND ; ( set block length)
: CMD17 ( a - f) 17 SEND-MMC-COMMAND ; ( start block read)
: CMD24 ( a - f) 24 SEND-MMC-COMMAND ; ( start block write)
: CMD58 ( - f) 0 58 SEND-MMC-COMMAND ; ( read OCR)
: SPI-CONFIGURE ( -)
( P.13 27:26 = 00 for gpio for MMC CS* )
( P.07 15:14 = 01 for SPI SSEL* )
( P.06 13:12 = 01 for SPI MOSI )
( P.05 11:10 = 01 for SPI MISO )
( P.04 9:8 = 01 for SPI SCK )
( 0000 0000 0000 0000 0101 0101 0000 0000 = $00005500 )
PINSEL0 @ $5500 OR ( leave serial Tx and Rx undisturbed) PINSEL0 !
IODIR @ PIN13 OR IODIR ! ( make pin 13 an output)
DISABLE-MMC
8 SPCCR ! ( for debugging, use 254 to slow it down)
( for fastest speed, use 8, which is the smallest allowed)
( cclk is about 15MHz so pclk is about 3.75MHz)
( We must set SPCCR to 8 or greater. )
( At 8, spi clock would be about .47 MHz.)
( Divisor must be even.)
( divisor spi clock )
( 8 .47 MHz )
( 128 29297 Hz )
( 254 14763 Hz )
$20 SPCR ! ( make it be master with CPOL=0, CPHA=0) ;
: MMC-INIT ( - errorFlag)
DISABLE-MMC 20 DUMMY-SPI-BYTES
CMD0 ( result)
( Result should be 1 if card successfully went into idle mode.)
DUP 1- IF ( utoh, not a 1, so return result as an error) ; THEN
DROP
( Ok so far, so send CMD1 up to 256 times until card exits from idle mode.)
256 BEGIN
CMD1 0= IF DROP 0 ; THEN
1- DUP 0=
UNTIL DROP -1 ( timed out) ;
CREATE MMC-BUFFER LISP (eallot 512)
: SET-BLOCK-LEN ( # - error) CMD16 ;
: READ-MMC-BYTES ( # - error)
( Call this only after receiving a good data token. Read # bytes into )
( MMC-BUFFER then read and throw away the 2-byte checksum)
( #) MMC-BUFFER SWAP ( a #)
FOR ( a) SPI@ OVER C! 1+ NEXT DROP
SPI@ DROP SPI@ DROP ( ) 0 ( ok) ;
: BLK@ ( blk# - error) 512 * 512 ( fall through)
: READ-MMC-BLOCK ( cardAddress # - error)
SWAP ( # cardAddress) CMD17 DUP IF NIP ( error) ; THEN DROP
( #) GET-DATA-TOKEN DUP $FE - IF ( # token) NIP ( token) ; THEN DROP
( #) READ-MMC-BYTES ( error) ;
: WRITE-MMC-BYTES ( fromAddr # -)
( Send a data token. Then send the block of data. Then send )
( a dummy 2-byte checksum. Caller is responsible for getting)
( the data response.)
( fromAddr #) $FE SPI! ( i.e. send start of block data token)
( fromAddr #) FOR ( a) DUP C@ SPI! 1+ NEXT DROP ( )
$FF SPI! $FF SPI! ( i.e. send dummy 2-byte checksum) ;
: BLK! ( a blk# - error) 512 * 512 ( fromAddr cardAddr #)
( fall through)
: WRITE-MMC-BLOCK ( fromAddr cardAddress # - error)
SWAP ( fromAddr # cardAddress) CMD24 ( fromAddr # error)
DUP IF NIP NIP ( error) ; THEN DROP
( fromAddr #) WRITE-MMC-BYTES ( )
GET-MMC-RESPONSE ( error) ;Message
reading and writing MMC disk
2004-08-01 by Frank Sergeant
Attachments
- No local attachments were found for this message.