Read a micro SD card using MMC / SPI

Download pre-release library modules and new examples to use with Astrobe for Cortex-M. Forum members can also upload their own source code examples.
Post Reply
cfbsoftware
Site Admin
Posts: 493
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

Read a micro SD card using MMC / SPI

Post by cfbsoftware » Sat Jun 09, 2012 1:25 am

Main module: Readcard.mod

Code: Select all

MODULE ReadCard;
(* =========================================================================  
   Example ARM Oberon Program  
 
   Description:
     Display the letters and numbers found in the first four blocks of an 
     SD Card that appear to contain text information.
      
   Target: 
     LPC17xx systems with an SD Card connected to the SPI1 bus

   Tested on:
     Olimex LPC1766-STK Development Prototype Board
     
   Refs: 
     Oberon for Cortex-M3 Microcontrollers
   
   (c) 2009-2012 CFB Software   
   http://www.astrobe.com  
   
   ========================================================================= *)

IMPORT SPI, Main, MCU, MMC, Out, SYSTEM;

CONST
  MaxBlocks = 4;
  MaxBlockNo = 4096;

PROCEDURE ShowProgress(s: ARRAY OF CHAR);
BEGIN
  Out.String(s); Out.Ln
END ShowProgress;
 
 
PROCEDURE IsLetter(ch: CHAR): BOOLEAN;
BEGIN
  RETURN ((ch >= "a") & (ch <= "z")) OR ((ch >= "A") & (ch <= "Z"))
END IsLetter;
  

PROCEDURE IsDigit(ch: CHAR): BOOLEAN;
BEGIN
  RETURN (ch >= "0") & (ch <= "9")
END IsDigit;

  
PROCEDURE DisplayChar(ch: CHAR);
BEGIN
  Out.Char(" ");
  Out.Char(ch);
END DisplayChar;
 
PROCEDURE DisplayHex(ch: CHAR);
BEGIN
  Out.Hex(ORD(ch), 4);
END DisplayHex;
 

PROCEDURE DisplayBlock(blockNo: INTEGER; data: MMC.DataBlock);
VAR
  i: INTEGER; 
  ch: CHAR;
BEGIN
  Out.String("BlockNumber: ");
  Out.Int(blockNo, 0);
  Out.Ln;
  FOR i := 0 TO LEN(data) - 1 DO
    ch := data[i];
    IF IsLetter(ch) OR IsDigit(ch) THEN DisplayChar(ch) ELSE DisplayChar(".") END;
    IF (i + 1) MOD 32 = 0 THEN Out.Ln() END
  END;
  Out.Ln()
END DisplayBlock;


PROCEDURE IsTextBlock(block: ARRAY OF CHAR): BOOLEAN;
(* If the first four characters are letters it is assumed to be
   a text block
*)
VAR
  i: INTEGER;
  text: BOOLEAN;
BEGIN
  text := TRUE;
  i := 0;
  WHILE (i < 4) & text DO 
    text := IsLetter(block[i]);
    INC(i) 
  END; 
  RETURN text
END IsTextBlock;


PROCEDURE SDPowerOn(on: BOOLEAN);
VAR
  s: SET;
BEGIN
  (* P0.21 *)
  SYSTEM.GET(MCU.PINSEL1, s);
  SYSTEM.PUT(MCU.PINSEL1, s - {10, 11});
  SYSTEM.GET(MCU.FIO0DIR, s);
  SYSTEM.PUT(MCU.FIO0DIR, s + {21});
  IF on THEN 
    SYSTEM.PUT(MCU.FIO0CLR, {21})
  ELSE 
    SYSTEM.PUT(MCU.FIO0SET, {21})
  END 
END SDPowerOn;


PROCEDURE Run();  
CONST
  SPIBus = 1;
  nBits = 8;
  useSSEL = TRUE;
VAR
  blockNo, blockCount, status: INTEGER; 
  rdData: MMC.DataBlock;
BEGIN   
  ShowProgress("Initialise SPI");
  SPI.Init(SPIBus, nBits, useSSEL);  
  SDPowerOn(TRUE);  
  ShowProgress("Initialise MMC");
  status := MMC.Init(); 
  ASSERT(status = MMC.NoError, 101);
 
  ShowProgress("Reading ...");
  blockCount := 0; 
  blockNo := 0;
  WHILE (blockCount < MaxBlocks) & (blockNo < MaxBlockNo) DO
    status := MMC.ReadBlock(blockNo, rdData);
    ASSERT(status = MMC.NoError, 102); 
    IF IsTextBlock(rdData) THEN 
      DisplayBlock(blockNo, rdData);
      INC(blockCount)
    END;
    INC(blockNo)
  END;
  SDPowerOn(FALSE);  
  ShowProgress("Finished") 
END Run;

BEGIN
  Run()
END ReadCard.
MMC.def

Code: Select all

DEFINITION MODULE MMC;
(* =========================================================================  
   Astrobe Library Functions for MultiMedia / SD Cards using SPI
   
   REF: NXP Application Note AN10406. 
   "Accessing SD/MMC Card using SPI on LPC2000, V3 (Jan 3, 2007)" 
   
   Oberon Version (c) 2009-2012 CFB Software   
   http://www.astrobe.com  
   
   ========================================================================= *)

IMPORT SPI;
 
CONST  

  NoError*            = 0;
  IdleStateTimeout*   = 1; 
  OpCondTimeout*      = 2; 
  SetBlockLenTimeout* = 3; 
  WriteBlockTimeout*  = 4; 
  WriteBlockFail*     = 5;
  ReadBlockTimeout*   = 6; 
  ReadBlockDataTokenMissing* = 7; 
  DataTokenTimeout*   = 8; 
  SelectCardTimeout*  = 9; 
  SetRelativeAddrTimeout* = 10; 
 
PROCEDURE Init*(): INTEGER;

PROCEDURE WriteBlock*(blockNumber: INTEGER; data: DataBlock): INTEGER;

PROCEDURE ReadBlock*(blockNumber: INTEGER; VAR data: DataBlock): INTEGER; 

END MMC.
Attachments
ReadCard.zip
ReadCard.mod, MMC.def, MMC.mod
(3.85 KiB) Downloaded 1497 times

bscholte
Posts: 19
Joined: Sat Jan 24, 2015 6:15 pm
Location: Austria
Contact:

Re: Read a micro SD card using MMC / SPI

Post by bscholte » Wed Jan 28, 2015 4:17 pm

Trying to get this to run on an LPCXpresso base board with LPC1769 target. The code does not compile. Issue;

In ReadCard.Run(line121): SPI.Init(SPIBus, nBits, useSSEL); where useSSEL is a boolean constant.

But the proc header is PROCEDURE Init*(bus, nBits: INTEGER; ConfigurePins:ConfigurePinsProc);

What ConfigurePins Procedure should be provided. Using MCU.Configure() leads to a crash in ReadCard.Run(Line 125)

Hope someone can help out here. Getting this to work is probably much easier than porting the MMC/FAT demo that comes with LPCXpresso.

Thanks in Advance!
Bart

cfbsoftware
Site Admin
Posts: 493
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

Re: Read a micro SD card using MMC / SPI

Post by cfbsoftware » Thu Jan 29, 2015 2:34 am

You will find corresponding source code files in your Examples\LPC1769\Olimex folder that was installed with Astrobe v5.2. Those files are compatible with the latest library modules. Use them instead of the ones posted here.

bscholte
Posts: 19
Joined: Sat Jan 24, 2015 6:15 pm
Location: Austria
Contact:

Re: Read a micro SD card using MMC / SPI

Post by bscholte » Fri Jan 30, 2015 4:10 pm

The card enable and card inserted signals are on slightly different signals than on the Olimex board. In order to read the card on the LPCXpresso base board, the SDPowerOn should be replaced with almost the identical procedure used to enable the 7-segment;

Code: Select all

CONST chipSelect = {2};

PROCEDURE SDPowerOn(on: BOOLEAN);
VAR
  s: SET;
BEGIN
  (* SPI1 *)
  (* Pin 2.2=GPIO_30 is used for chip select *)
  SYSTEM.GET(MCU.PINSEL4, s);
  SYSTEM.PUT(MCU.PINSEL4, s - {4, 5});

  (* Pin 2.2 is an output pin *)
  SYSTEM.GET(MCU.FIO2DIR, s);
  SYSTEM.PUT(MCU.FIO2DIR, s + chipSelect);
  
  (*Set pin 2.11=GPIO_38 as input*)
  SYSTEM.GET(MCU.FIO2MASK, s);
  SYSTEM.PUT(MCU.FIO2MASK, s - {11});
  
  IF on THEN 
    SYSTEM.PUT(MCU.FIO2CLR, {2})
  ELSE 
    SYSTEM.PUT(MCU.FIO2SET, {2})
  END 
 
END SDPowerOn;
To direct the chip select to the MMC/SD reader, remove the "A" jumper from J55.

The direction of pin GPIO_38 is set to input to make sure the card insertion detection works. Card presence can be detected by checking GPIO_38. This is done with;

Code: Select all

PROCEDURE DiskIn():BOOLEAN;
(*check pin GPIO_38 for Disk. 0=disk, 1=empty*)
VAR disk:BOOLEAN;
BEGIN
  IF SYSTEM.BIT(MCU.FIO2PIN1, 3)
   THEN disk:=FALSE
   ELSE disk:=TRUE
   END;
  RETURN disk
END DiskIn;
Important note: The card detection signal is faster than the actual card insertion. If an attempt is made to read immediately after insertion detection, the read will fail. Allow for some delay between detecting the card insertion and the first read attempt.

The rest of the original code remains unchanged and with these small changes, the card access works perfect.

The challenge now becomes to make a FAT like file system for the cards. Has anyone done this already?

Bart

cfbsoftware
Site Admin
Posts: 493
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

Re: Read a micro SD card using MMC / SPI

Post by cfbsoftware » Sat Jan 31, 2015 4:43 am

Thank you for your contribution :)
The challenge now becomes to make a FAT like file system for the cards. Has anyone done this already?
Rather than using a FAT-based system I have been checking the feasibility of using Niklaus Wirth's filesystem designed for use with SDHC cards in the FPGA-based Project Oberon 2013 system. It is attractive to me because the design appears elegant, the implementation details are thoroughly documented and the complete Oberon source code is available. The documents and code can be downloaded from:

http://www.projectoberon.com

I first wanted to make sure that if I was to use the filesystem to read/write files to an SD card on an Astrobe development board that I would then be able to transfer the files to/from the SD card when mounted on a Windows PC. I have now succeeded in doing this as reported recently on the ETH Oberon mailing list:

https://lists.inf.ethz.ch/pipermail/obe ... 07966.html

Chris.

bscholte
Posts: 19
Joined: Sat Jan 24, 2015 6:15 pm
Location: Austria
Contact:

Re: Read a micro SD card using MMC / SPI

Post by bscholte » Sat Feb 14, 2015 10:57 am

Dear all,

For compatibility reasons, I decided to implement the FAT16 file system. Completely based on the modules that were published in the first article of this post. It is working quite well and also very old (32MB) card with very fragmented files can be red. I have not attached the code yet because there are still a few caveats;
- It only reads files. For my application, in the coming months, I will only need read capabilities. Writing will be implemented later.
- The file system currently pre-loads the FAT chain in an array. The files size is limited to blocksize*chainlenght. For smaller SD cards with many small blocks, this quickly becomes a problem. Unless you use a lot of memory for the FAT array. I would like to improve on this first before publishing.

Still, if anyone would already like to get the current code, just post the request on this forum....................

I hope to upload a working, read-only version soon.

Bart

PS: If you are not bound by compatibility, I fully support the suggestion Chris made to use the Oberon FS. It is a much smoother file system.

Post Reply