================================================================================================================================================

Saturday, January 31, 2009

SD/SDHC Card Interfacing with ATmega8 /32 (FAT32 implementation)


Hi friends,
Here is my project on interfacing of SD Card (microSD). microSD cards are available very cheap nowadays, a great option for having a huge memory in any embedded system project. It is compatible with SPI bus, so the interfacing is easy. SD card adapters are also easily available in market, one can easily make a bread-board adapter by soldering few pins on it. Following figures show the SD card pin-out & the bread-board adapter design by soldering 7-pins of a breakout header on the microSD adapter (Click on images for larger view).












I had started this project with 1GB microSD card from SanDisk (later on tested with transcend cards also). The microcontroller is AVR ATmega8 or ATmega32 running at 8Mhz internal clock. MAX232 is used to interface the circuit with PC for monitoring the data. A 3.3v supply is used for powering the mega8, microSD and max232 (though the specified supply for max232 is 5v, it works comfortably at 3.3v).7 pins of the microSD are used here, shown in the figure of pin-out.

Schematic for ATmega8 is shown here (updated on 10 May 2010, SD series resistors are removed, as they were limiting the speed of SPI bus. 51k pullups are added on CMD/DAT lines. This gives better stability with different cards. Also, two 3.6v zeners are added to protect SD in case when the ISP programmer voltage levels are of 5v. these diodes are not required if your programmer has settings for 3.3v output)
(Note: VCC & GND pins of MAX232 are not shown in the schematic, but they must be connected in the actual hardware)
Following is the schematic for ATmega32, without RTC (updated on 10 May 2010):


Following is the schematic for ATmega32, with RTC (added on
17 May 2010; CS pin correction, PB4 instead of PB1, done in Mar 2014). Here two supply voltages are used, 3.3v for SD & 5v for remaining ICs.



The aim of this project was to learn interfacing of SD card and to understand the data transfer in raw format as well as in FAT32 format. I started with raw data transfer, sending some data to any block of the microSD, reading a block of it, reading and writing multiple blocks, erasing multiple blocks. All this in raw format. I used RS232 for viewing the data read by microcontroller from SD card. The uc sends the data to HyperTerminal. Similarly, to write data to card, the data was fed thru HyperTerminal, by typing some text.

Once raw data transfer achieved, I formatted the card with windowsXP (FAT32) and loaded it with some text files, directories and other files (all stored in root directory of the card). After that I wrote the FAT32 routines to read files, get file list (using HyperTerminal again), finding the total/free memory of card. All this data is sent to HyperTerminal by the uc.

Following is the HyperTerminal window showing different options:
Options 0 to 4 are low level functions dealing with raw data. If you use option 0, 1 or 3, you may have to reformat the card before using the FAT32 routines.
0: Erases selected number of blocks strating from selected block
1: Writes data to specified SD block address. Data to be entered in HyperTerminal using PC keyboard
2: Readss data of specified SD block address. Data is displayed on HyperTerminal window
3. Writes selected number of blocks strating from selected block
4. Reads selected number of blocks strating from selected block

Here, the multiple-block functions related to options 3 & 4 are disabled due to memory constraint as that time mega8 was used for testing and these functions are not required for FAT32 testing. While testing with mega32, options 3 & 4 can be enabled by removing a macro (#define FAT_TESTING_ONLY) defined in SD_routines.h.

Options 5 to 9 are related to FAT32 . Only short file names are supported right now, 8byte name+3bytes extension. If you store a long name file in SD, it will be displayed by these routines in short name format only.
For testing these options, format the card with FAT32 file system and store some directories and text files (because text files can be read & checked thru HyperTerminal).

5: Displays list of available directories and files with size (in the root directory of the card)
6: Reads a specified file and displays the file contents on HyperTerminal
7: Create/Append file with specified name, enter text from HyperTerminal
8: Deletes any existing file with specified name
9: Displays total & free memory of the card (using FSinfo sector of the SD card)

Following figures show the HyperTerminal window when options 5 & 9 are selected:
(These figures show menu from Ver2.3 or earlier. Menu style is changed from Ver_2.4 onwards, which is shown in the update history)

Note: HyperTerminal is used here at 19200 baudrate, No parity, Flow Control 'none'.

This project needs very few components and can be done easily at home. Try it out!











Download the source code files from here:Download here the zipped source code files modified for mega32, written in winAVR-AVRStudio.

Version 2.4.1 (RTC added for Date/Time entries) 17 May 2010/24 Apr 2011
Version 2.3 (SDHC support added) 09 May 2010
Version 2.2 (No SDHC support) 13 Sep 2009

Download EAGLE schematic file of Ver 2.4

Download/view source code files V2.1 (for ATmega8):
Following files are compiled using winAVR inside AVRStudio. This Version does not support SDHC cards. Also, append file feature is not available.
1. SD_main.c
2. SD_routines.c & SD_routines.h
3. FAT32.c & FAT32.h (Ver 2.1, last updated-13 Sep 09)
4. SPI_routines.c & SPI_routines.h
5. UART_routines.c & UART_routines.h
6. Makefile
7. HEX file (Ver 2.1, last updated-13 Sep 09)

Please put up a comment or mail me if you find a bug in the code. The updates are because of valuable suggestions & comments from the users of this code. Thanks a lot!!

This library has been updated by ExploreEmbedded to support lpc2148, lpc1114, lpc1768 controllers (in addition to ATmega devices). Please visit this page for more details:
https://www.exploreembedded.com/wiki/LPC1768:_SD_Card_Interface

Buy from Farnell (Element14): ATmega32 (India), ATmega32 (USA)


Related posts:

  • You can visit my post of microSD ATmega32 Data-Logger, with LCD and temperature sensor, which uses modified FAT32 (or FatFs) library for automatically creating files without using hyper terminal.
  • If you want to test FAT32 functions or learn more without making the microcontroller hardware, you can visit my post here: microSD-FAT32 using Visual C++)


Update History-----------------------------------------------------------------------------
Version 2.4:

- Real Time Clock circuit support is added for time & date entries in the files. Now the current date of file creation and file update will be entered in the FAT table (can be viewed by checking file 'properties' using a PC)
(The RTC will also be useful in data-logging with time-stamp)
- Three more options added in the Hyper Terminal menu for displaying or updating RTC date & time. New menu is shown in the above figure.

Ver 2.4.1:
- Same as 2.4, with a bug fix for RTC: 'twi_init' function added to define I2C clock freq. 100K@ 16MHz (50k@8MHz). This was taking default values earlier, which was as high as 500K@8MHz, not desirable

(Note: Version 2.2, 2.3 & 2.4 are tested on ATmega32, but they can be adopted to any controller having RAM >= 1KB and Flash >= 16KB)
Current memory usage (Ver_2.4): Flash: 12908 Bytes; RAM: 700 Bytes (appx.);

Version 2.3:
- Support for SDHC cards added (tested with SanDisk & Transcend microSD and microSDHC cards). The initialization sequence and command formats are modified.
- A bug which was causing the program flow to go into infinite loop if the character number 512 in a sector was a CR (Carriage Return, '\r'), in the writeFile function. Thanks to David & Piotr M. who pointed it out in the comments.
- Code is also tested successfully at 16MHz clock (8MHz SPI clock) with for SD/SDHC cards.

Follwing are the Hyper Terminal windows showing card detection (One window shows baudrate as 38400, that was while testing for higher clock speeds, current code still uses 19200 baud and 8MHz internal clock of Mega32).








Version 2.2:
- Append file feature added. 'createFile' function replaced with writeFile, which looks for the filename first, if the given file name doesn't exist then it creates new file and writes data, but if the file already exists, then it opens it and appends the entered data.
- A bug removed which was giving error related to use of 'LONG'
- The FAT32.c & .h files are updated on 13 Sep 09 to remove a bug which was limiting file size to 32MB (Thanks to Kun-Szabo Marton who pointed it out)

Data transfer rate: 1 raw data block (512 bytes) takes 4.15ms for reading or writing (123.37 KBytes/s) at current 4 MHz SPI clock rate. If you have flash more than 8k, you can declare the SPI_receive() and SPI_transmit() functions as 'inline' functions. This will increase the transfer rate to 140 KBytes/s. These transfer rates can be further increased by using a 16MHz crystal (8 MHz SPI clock). FAT32 file reading is done at 78 to 91 KBytes/sec.

Version 2.1:
- A bug removed which stopped creating new files after 32*8 files in the root directory
- The root directory was unnecessarily getting expanded by one cluster whenever a file was created. Fixed in the new version
- Also, the fixed cluster size of 8 sectors is removed, this version will support other cluster sizes as well

Version 2.0:
- Support added for SD cards having first sector as MBR rather than the boot sector
- createFile and deleteFile functions added
- A bug fixed in reading files stord at far locations in memory also correction made to accept 8+3 char file name (by mistake, it was taking 7+3 earlier)
- FSinfo sector used for storing total free cluster count &
next free cluster number for faster file access
- Instant freeMemory display (earlier it was taking more than 30secs) using FSinfo sector. FSinfo sector is updated now whenever a file is created or deleted
- File memory size display in decimal, like windows (earlier it was in hex)
- Raw SD functions multiple block read and write, which are not required for FAT32, are disabled using FAT32_TESTING_ONLY macro for getting extra space required by createFile & deleteFile changes (you can activate it if you have more than 8k flash) Right now flash is 99.9% full
- Clock speed raised from 1Mhz to 8 MHz, new SPI speed (after SD initialization): 4MHz instead of 500K & baudrate: 19200 (for HyperTerminal) instead of 4800
-------------------------------------------------------------------------------

References/ Further Reading:
1. Microsoft's FAT32 specification document
2. SD-Simplified Physical Layer Specifications
4. F. Foust's Application Note on SD
5. FAT32 Structure info, includes MBR details
6. Simple FAT32 Structure explanation
 
7. Chan's FAT library
8. SD Association's Website for further info

Regards,

CC Dharmani
ccd@dharmanitech.com

723 comments:

1 – 200 of 723   Newer›   Newest»
Michael said...

Fantastic!

Perfect timing also as I'm adding FAT to my SD project today since I'm sick and can't go out :-)

Anonymous said...

This is just absolutely great, I am too doing a project which includes a MMC card and this will surely be a helpfull resource, great work man

Anonymous said...

suuuuuuuper - just in time 4 me :)

Thanks a lot !!!!!!!!!!!!

Datalogger, code / data storage, update facility, ...

that's all easier now - and cheap

Anonymous said...

Wow ...wonderful coding and read how u fixed the errors in count of free clusters.Superb.Will be of great help to understand FAT32 concepts

Anonymous said...

Pretty good and compact project! Thanks a lot! I will try it with ISIS Proteus...

Anonymous said...

thank you very much! thats great sharing....yakuzaa when you try it in ISIS can you share simulation files with us.tnx

Anonymous said...

great job man.....
it's very... very...Nice
thanks alot

U-2 Man said...

very nice project...
I will try for dot matrix display project...The SD card will use to save bitmap file to display

Anonymous said...

good project &very useful

Anonymous said...

This project not work for me in ISIS and on real MCU....
Now I'm using ported to CVAVR Elm Chan's FatFs library - it works perfectly on ISIS and real MCU with SDHC cards up to 16GB!

CC Dharmani said...

Sorry to know that. I've tested this code for AVR only and upto 2GB card. Yet to test for SDHC. i think there won't be many changes in it.

BTW, what's the 'real MCU'?

Anonymous said...

The main differences that I found between normal (V1, <=2Gbyte) and high-capacity (V2, >2Gbyte) were ;
(a) initialising the card and
(b) decoding the CSD.

Anonymous said...

dude.. really great.. and that too with 8-bit uC..Really wonderful.. Will get back to u in case of doubts..

CC Dharmani said...

Thanks, man!! sure, u can contact me for any queries!

subhajit_dbb said...

extremely grateful to you for this project .....i was hunting like anything for something like this....just tell me one thing ...would it be wrong to use max232 at 5v.Max232 gives a 5v TTL output does it not affect ATmega8L when you are operating the microcontroller at 3.3v.PLEASE DONT MIND IF MY QUESTION IS TOO SILLY.I AM ONLY A BEGINNER.

CC Dharmani said...

as per the datasheet, you should not apply voltages >VCC+0.5 to any i/o pin.
If you are using 5v for max232, connect a 3.3v zener between RXD line of uc & GND, this will cut the 5v to 3.3v

subhajit_dbb said...

Could you please tell name me any such diode......i need it badly.Would you mind giving me your mobile number at subhajitroy86@gmail.com.My purpose is totally academic.I am a B-Tech 3rd yr student from west Bengal.THANKS FOR YOUR REPLY

CC Dharmani said...

you can go to your local electronic shop and just ask for 3.3v zener and you'll get it! look for 3v3 marking on the diode.
I'll mail you my contact number.

Anonymous said...

great job.. thanks a lot.. i am going to do exactly as u have done for one of my projects.. Between I2c and SPI what do u think is better, i was looking for a comparison between them. If you have any projects on I2c please let me know.. i also need to learn I2C for another project

your circuit diagrams looks so good a well made like in books - what software do u use to draw them.. i can draw them in Multisim or Altuim designer which i use for simulation or pcb designing but they dont look as cool as yours, i need well drawn professionals looking circuit diagrams for writing papers/presentations..

you cd email me on subrat.nayak[AT]gmail.com if u need to

CC Dharmani said...

Hi Subrat,
thanks for the comment.
Selection between i2c & SPI depends on your application. If you need a simpler interface with speeds below 400k, go for i2c, it consumes only 2 pins of microcontroller and you can have many devices connected on the same 2-wire bus.
SPI will be the choice if you are looking for high speed bus. Devices with 50MHz SPI are available. It takes 3 pins of controller plus a chip select pin for each device connected on the bus.
For i2c code, you can check out my project:
http://www.dharmanitech.com/2008/08/interfacing-rtc-serial-eeprom-using-i2c.html

For drawing schematics, I use EAGLE, it's pretty good and simple to use.

Regards.

Unknown said...

Hey dharmani,
I really appreciate your help and prompt repsonse..

subrat

Nibble said...

Very good!

I am wondering if I can use it in a project. But the 3.3V is a problem to me. I need to use 5V. What are de changes need to operate with 5V? The MicroSD card will accept 5V?

Thanks!

CC Dharmani said...

Hi Claudio,
the microSD has to be operated at 3.3v only. For connecting microSD to a 5v controller, you can put voltage divider resistors on 3 pins: MOSI, SCK & Chip Select, to reduce the voltage applied to SD card pins from 5v to 3.3v. Also, it's better to use a separate 3.3v regulator for the Vcc of microSD.

Regards.

Anonymous said...

Thank you very much!!

I got many information.

Nibble said...

Thanks again!

subrat said...

Apart from feeding the vcc of the sd card with 3.3v, do all the rest 4 lines coming from the atmega8 and going to the sd card need to be sending pulses max of 3.3v?? can they be 5v?? voltage dividers as u suggested are not accurate , so what the max and min range of the voltage that needs to be send to the sd card.. i hope it doesn't have to be exact 3.3 v..

Moreover, as u said the manufacture r suggests pull up resistors on the pins of the SD card? do they say its needed for all the 4 lines on it.. i hope these need to be pulled up to 3.3v.. u havent used them but things change when the circuit is moved from bread board to real PCB.. i have experienced that quite many times , on boards i dint need pull up/down resistors but on pcb they dont work and i had to put them in.. so i was curious did to try this on a pcb?? if not i wd prefer to be safe and put those pull ups..

thanks,
subrat

subrat said...

the link that u gave me for i2c, doesnt work.. u gave it to me long ago but i had not tried it yeta nd when i checke dit out today, it doesnt work.. please check..

thanks,
subrat

subrat said...

i also have another query.. since the same lines are being used for the isp as well as for the sd card, when i program the micro controller with an isp , do i need to remove the sd card? or they can both co-exist at same time.. after programming and the sd card is in there, do i need to remove the connections to the isp programmer for safe working??

i have noticed that once the programming is done, the micro controller starts working even if the isp programmer remains hooked up in there..but in this case the sd card was in there on the same lines so i thought better check with u..

subrat said...

i recently lost my expensive AVR jtag programmer now i dont want to buy another so i was wondering can i make my own usb to isp or may be serial to isp programmer..i found some circuit diagrams from the internet for serial to isp AVR programmer but wanted to check with u before i try any of them bcose some have mx232 and some dont, i thought max232 is compulsory for this job.. but i really want to make a usb to isp bcose i dont have serial port on my laptop and have to use a usb to serial adaptor which has a max232 then i have to put another max 232 on my serial to isp programmer, so i was thinking of making a usb to isp.. i know there are ftdi chips that can convert usb to serial at 5v and then if i cd implement a commonly used serial to isp programmer circuit, it shud do my job.. Please comment...

the links of programmers that i cd find
http://lea.hamradio.si/~s56wix/avrprog/
http://elm-chan.org/works/avrx/avrx_com.png

CC Dharmani said...

Hi Subrat,
I've not made PCB of my design, but two of the visitors of this blog have made it and tested (they sent me the pics), it works perfect even without any pull-ups on the SD lines on the PCB.

The 3.3v level on the three comm lines is not so strict, as per my experience, you can feed 3v to 3.7v without any problem (note: MY EXPERIENCE!!!) so, you don't need very accurate voltage dividers

If your AVR programmer voltage is 5v, then it would be better to use 3.3v zener diodes on MISO & SCK lines to protect the card from 5v applied to it. (again, my experience says you can keep the card in position while programming the micro, it doesn't get affected!!)

The link on I2C which I've provided earlier works very well, I just tested! (if any problem, become member of avrfreaks.net, it's not harmful, OR mail me asking to send the code files to your mail ID)

Regards.

CC Dharmani said...

Hi Subrat, Here is an easy to make USB programmer, good for the those using laptops:

http://www.fischl.de/usbasp/

Ziyauddin said...

thanks a lot !!!!!!!!!!
But please give information of about retry function as "time out"

CC Dharmani said...

Hi Ziyauddin,
the retry variable is declared to count how many tries you have to do before the card responds.
if retry limit is 0xfe, then the controller will resend a message to card maximum of 255 times. If working properly, card will respond much before the number 255 is reached. If card does not respond within 255 tries, the controller will display an error message.

Anonymous said...

hey...can u please tell me which zener diode i should use to connect atmega32 to Micro SD card. Is there any power constraint for tht? please reply ASAP..
if u can then gimme ur mail id on:
kgzone2001@yahoo.co.in

CC Dharmani said...

3.3v zener diodes. The marking on it will contain 3V3. For example, C3V3PH or BZX79C3V3. Power rating can be 0.25w to 0.5w

amol said...

I am getting various parameter values from sensors and i want to store date and time with the result in the SD card. Please send me C program for AtMega16.

tarun said...

I looked up for ISP, but the info i got doesnt seem to content me, so will you please tell me more about how it is to be used, and which one is to be used in this project.

CC Dharmani said...

for different AVR ISP programmers, refer to my post:

http://www.dharmanitech.com/2008/09/diy-avr-programmers.html

There you'll find some of the options for making a programmer yourself. You can use any programmer for the SD card project, only the ISP connector may change depending on the ISP unit.
If the ISP programmer's voltage level is 5v, care should be taken that the voltage level on the SPI bus is around 3.3v while programming the controller.

Anonymous said...

Man, really seems to work.. how did you stuff all this into the mega8? most others use >1k ram for this.. and at least 512K for buffering. (i did not look into your code yet, just curious..)

Rene

khushal :) said...

what if i use AtMega32L at 3.3 v power supply. Do i still need zener diodes on the SPI lines???

khushal :) said...

also notice that Power Consumption at 1 MHz, 3V, 25°C for ATmega32L
– Active: 1.1 mA
will memory card work or not??

CC Dharmani said...

Don't worry about the power consumption of mega32, it can very well drive the SPI bus, it's designed for it!!

I had suggested the use of zener diodes in case when your AVR programmer doesn't sense the voltage level of the circuit and always outputs signals of the level of 5v. This can be damaging to the controller & the SD card both.

MorgothCreator said...

What is real transfer rate for writing and reading a 1MB file for example, using the FAT32 and haw many megahertz has the controller while doing this test. I want you to do a test to determine artificial transfer rate without internal serial unit intervention.
I made a library of FAT32 in asm and the details are here:
http://digitalelectronicsandprograming.blogspot.com/2009/02/fat32-complete-library-in-asm-linguage.html

khushal :) said...

yeah...bt thats why i asked about atmega32L, not atmega32. I can operate that @ 3.3 V so do i still need the diodes??? please clearify my doubt!

CC Dharmani said...

Hi Morgoth,
the data transfer rate which I've mentioned in my post is measured during raw data transfer. As you know, the actual transfer rate while transferring files will differ as it involves going thru the cluster chain stored in the FAT table.
Here in my code, I'm using a single 512 byte buffer for accessing data as well as reading the FAT table, hence, the buffer allocation is done back and forth, which will result into lower transfer rates compared to the systems having an extra buffer (more RAM).
Anyways, good suggestion, I'll measure the transfer rate for 1MB file, after blocking the UART routines, to get the actual file transfer rate.

CC Dharmani said...

To khushal:
Hi man, the zeners are required actually for mega32L, not for mega32, since you'll operate the mega32 at 5v anyways.
The problem comes when you are operating the controller at 3.3v AND if your AVR programmer signals are of 5v level. You better use zeners there.
(If you operate controller at 5v, then anyways you'll be using the voltage divider resistors with SD card, which will make sure that 5v coming from controller or the programmer gets converted to 3.3v before it goes to SD, so no need of zeners there).

khushal :) said...

thanks for the information :)
but actually i would like to ask you something more..can u please give me your contact no. or mail id..Its a bit urgent..
Khushal

CC Dharmani said...

My email id is scattered across my blog, at the end of almost every post and also at the top of the right hand side bar! :)
Anyways, it's: ccd@dharmanitech.com

Anonymous said...

hi CC
thanx...
actually i was using ur makefile with fedora but it was giving some erros like:
*** seperator needed
please help yaar..
i request to give your cell no...please..@itskhushal@gmail.com

Ziyauddin said...

dear sir,
how to write the block of data in MMC card,i define block size is 32bytes and i make a function that pass 1 byte only .i want to write my 32 bytes data which is stored in char array.so how to write this data in MMC card.

Ziyauddin said...

Dear sir,
what is the work of "start block" in write function.

CC Dharmani said...

Hi Ziyauddin,
the initialization sequence for MMC card is little different then SD cards. I've not tested my code for MMC.

In case of the readSingleBlock & writeSingleBlock functions, the 'start block' specifies the block-number of a data block which you want to read or write.
For readMultipleBlocks or writeMultipleBlocks or eraseBlocks, the 'start block' specifies the block-number from which the multiple-block read/write or erase will be started.

Ziyauddin said...

Dear sir,
please give me complete information for single block write as shifting of start block.
To be-send complete description of this program.
my email is:
er_ziyauddin@yahoo.com

Daniel said...

Wow. Using an SD card adapter is such a good idea. SD breakout boards are quite expensive whereas adapters are a dime a dozen.

I just saw a 128mb micro sd card with adapter for $2 on ebay with free shipping. 128mb may be small, but for data logging and debugging it is plenty.

Anonymous said...

Pefect work, thank you. But I don't be able to run the project without a litle changes. This is about SS line. It is PB2 for ATmega8 MC. Is this mistake? So makro definition SD_CS_DEASSERT (SD_CS_ASSERT) will be another:
PORTB |= 0x04.
P.S. Sorry for broken english

CC Dharmani said...

No problem, you can use SS line for SD_CS_ASSERT. Just change those two macros, as you mentioned (I've tested it that way also):
#define SD_CS_ASSERT PORTB &= ~0x04
#define SD_CS_DEASSERT PORTB |= 0x04

Ethan Russell said...

Cool! I'm using this in a project of mine as well!

I had some problems with the Makefile, and I think it's because I'm using Cygwin in WINDOZE, but...

I changed the makefile to replace the four spaces in front of some of the lines to tabs. Apparently, tab is some sort of instruction as a prefix in the makefile. Also, I changed ../meh.c to meh.c on all of the source files. Everything is in the same directory for me.

If anyone else is having trouble compiling, try that.

http://etharooni.polorix.net/SDMakefile

Kyle James said...

Just curios, can you use the Max232n instead of the Max232?

Or are they not cross-compatible?

CC Dharmani said...

Max232n is nothing but he PDIP version of the MAX232 chip, it's the same! So, can be used without any problem.

Kyle James said...

Hoo. Yo soy muchas n00b. I have another question.

When I try to compile in AVR Studio 4, it gives me an error:

makefile:50: *** missing separator. Stop.

The comments ask the same question, but there is no answer.

I think I have everything set up right, but apparently not. Please post if you have any suggestions or e-mail me at 'kylejames@live.ca'

CC Dharmani said...

Hi,
I've sent you the makefile generated by AVRStudio as it is in your mail. Use it, it may solve the problem.

Regards.

babek said...

hi CC Dharmani
I also riceve this error
makefile:50: *** missing separator. Stop.
can you send me a makefile for avrstudio.
babek_nor@inbox.az

tarun said...

Hi CC Dharmani..
will this code for Atmega8 also run for Atmega16??

Saran said...

Hi Dharmani,
Hats off to you man. After seeing your blog I started to do some embedded project as a hobby. I am new to this AVR core. I am using Atmega32 with winavr GCC compiler. My problem is, controller not detecting the external 8MHZ crystal, instead it is running with internal 1 MHZ oscillator. what is the command for the fuse setting in winavr Or else is some other way for Fuse setting? If so please let me know.

Anonymous said...

Sir,
I download the source code. I am new to Win Avr.
After unzipping the rar u gave, there are C files another default folder with .elf files , Make file and def folder.
I cpied all this files to the main folder.
I didn change anything in Make file. But it is showing error
"make.exe: *** No rule to make target `../FAT32.c', needed by `FAT32.o'. Stop. "

when i tried to compile SD_main.C
Please help.

prem.rust@gmail.com
shankarprem@hotmail.com

CC Dharmani said...

To Babek:
I've sent you the makefile, check it out!

To Tarun:
Yes, it'll run on atmega16 also. Just keep the connections same and change the controller in your project configuration settings (if you are using AVRstudio) orin the makefile (if you are using winAVR separately).

CC Dharmani said...

To Saran:
you have to change the fuse low byte. For using external crystal, you can make the CKSEL3 to CKSEL0 bits 1111. For using internal 8 MHz oscillator, you can set those bits to 0100. How to write fuse to the controller depends on which programmer you are using. Check out your programming software for setting fuse bytes. Also, refer to the AVR datasheet. 'memory programming' section where the details of the fuse bits are given

to 'anonymous':
For using the folder which you've downloaded, you've to install the AVRStudio. The winAVR will run inside AVRStudio. In the AVRStudio, click on 'open project' and select the project from the unzipped folder, named 'SD_Card.aps'.
Once you open the project, all the files will automatically appear in the project file list. Then you can compile it without any error.

Saran said...

Hi Dharmani,
I am using winavr GCC compiler, it is having AVRdude version5.5 & gcc version 3.3.3
I don't know what command i have to give in the command prompt for the Fuse bit setting. Can u help me reg.., this.

CC Dharmani said...

For setting the fuse low byte for internal 8 MHz clock, following command can be used:
-U lfuse:w:0xe4:m you can replace the 0xe4 with oxef, if you want to use external 8 MHz crystal

Tarun said...

I made the circuit as shown in your schematic. Only changes were that i used Atmeg 16 and that i didnt connect an ISP connector(I couldnt find one).

But as soon as i give power, the interrupt LED glows and the hyper terminal shows nothing(except for some abstract text). Please Help ASAP!!! I am making the project for my final semester and the date is near...

CC Dharmani said...

Check again the controller-max232-pc connections. Make sure Hyper Terminal is running at 19200 baud, with flow control 'none'. Also make sure the controller is running at 8MHz.

Rajiv said...

Hello Dharmani,

I am Tarun's friend and working on the same project with him.
Cant there be any other reason to the above problem stated by Tarun. Because we have checked the circuit, and the connections are correct. Hyper Terminal is running at 19200 baud.Also the controller is running at 8 Mhz.

Thanks.Help would be appreciated.

CC Dharmani said...

Hi Rajiv,
as tarun said that the LED glows, that means the program has started. And also he mentioned some garbage coming on Hyper Terminal, that can happen when there is mismatch of the bauds. Check again that in the hyper terminal settings, the flow control is selected as 'none'.

Try this also, set the hyper terminal at 4800 to check whether the controller is running at 1 MHz. (keep the 'flow control' always 'none')

If still problem, mail me the project folder, I'll have a look!
ccd@dharmanitech.com

Rajiv said...

I have mailed you,please have a look

Rajiv said...

Hello once agian,
i would also want to clarify from you that in AVRStudio we have chosen the debug platform as "AVR Simulator", device "ATmega16".Is it correct or we need to choose other debug platform?

MILIND said...

I am preety impressed with work done by you.
I want to interface SD card to P89v51RD2.how complex it is acheive this interface??later i wont to store some images on it ,and it might be required to display those stored images on the colour LCD.so can i carry on my work with the RD2, or i have to go with another controllers like arm7 ?
Please help me ............

CC Dharmani said...

To Rajiv:That's correct, No problem for that.

To Milind:Hi, the controller you've selected is not having the SPI bus hardware inbuilt. So, if you want to use it for this project, you'll need have a software SPI library, may be using the UART. There are some examples on net on how to implement the SPI using UART. Try google, you'll get it.
Other alternative is to use SD card in non-SPI mode (standard SD mode). Got thru the SD manual for more info (I've put up a link for downloading the manual in my post)

Or, a better solution would be to use a microcontroller with SPI bus hardware inbuilt, like the one I've used, AVR controller.

Anonymous said...

hi, I'm HachmadinaJAD, do U Want to work for MY???

I PAY VERY good...

Unknown said...

Hi, i tried using your code. Works well for file sizes less than 4KB. but after that it repeats what it has read in the first 4KB. Doesn't seems to read beyond that. It keeps repeating until it exceeds the file size.

Saran said...

Hi Dharmani,
Thanks for the Fuse setting command. It is working well. I am having 2GB SD card. Is it advisable to have this card to work out ??

And I have one more query, assume that I am having one mp3 files in the SD card. Is it possible to run those files. I am not stressing by means of ATmega. Suggest me proper controller for this kind of application. Does any RTOS is necessary for this ??
Need H/w & S/w support

MILIND said...

sorry sir i am not gettin? According to datasheet P89v51rd2 has SPI(miso,mosi & clk)?? please explain??
again adding library might consume some more amount of memory.

my other concern is memory usage.because i am going to interface some more peripherals later.
how much ram and flash usage your code requires??

CC Dharmani said...

To Ash:That means the controller is reading only the first cluster of the file (assuming usual 512 bytes/sector & 8 sectors/cluster). Have you made any changes in the code, in the FAT32.c?

To Saran:There are lot of examples on net of designing an mp3 player using a microcontroller. Search in the project section of www.avrfreaks.net, you'll find such designs using avr controllers.

to Miling:Sorry man, my mistake, the controller you specified does have the SPI bus. You can use the code given here with very few modifications.
The code takes 8k FLASH, including FAT32. The size can be reduced if you remove the UART message strings. And if you just want to dump data into SD without FAT format, the code can be made less than 2k. RAM usage is around 600-700 bytes.

Rajiv said...

Hello,
we have used 4.7 uF capacitors instead of 1uF capacitors with the MAX232.Could it be the reason for our circuit not working?
(please refer above for our problem stated by tarun and rajiv).

MILIND said...

Finally i managed to compile the code without any error in keil compiler for P89v51RD2.
I have made some changes in the code.i have made buffer[512] as xdata.will it matter??????
right now my Program Size is: data=23.2 xdata=768 code=14189
also right now i am tryin to build the circuit for the same.
i have a CKT made with controller running at 11.0592MHZ at 5V and max232 for serial comm.
how do i set the speed of spi to 4mhz??i have made it as
#define SPI_HIGH_SPEED SPCR = 0x50; SPSR = 0x00;
is it ok???
do i require a 3.3V zener for all the 4 pins of spi??

CC Dharmani said...

Hi Milind, you are using a different controller, and the macro that I've used is for AVR controller only. I've not used the one you've mentioned. Refer to datasheet of the microcontroller to check out how to set the SPI clock speed. Also your compiler documentation for details on xdata definition.

If you ISP programmer and SD card are connected on the same lines, you may need zener to limit the 5v of programmer to 3.3v (refer to some of the earlier comments on this post)

MILIND said...

i am facing problem initialising SD card.i am using 2GB SD card (sandisk).
i got following responses
command-----------response
GO_IDLE_STATE-----0x01
CMD58-------------0x00
CRC_ON_OFF--------0x00
SET_BLOCK_LEN-----0x00
SEND_OP_COND------0x55

0x55 means what??
from above response does it mean that my SPI is working ???
i have use spi speed as(11.0592MHZ/16)
initial voltages at controller pin:
ss = 2.5v
miso = 2.5v
mosi = 4.45v
clk = 0v

and i have connected 100ohms resistor and 3.3v zener at 3 of the pins of spi is it ok???

what is wrong?? how can i check?

MILIND said...

hey please help me inialising the SD!
for CMD0 i got 0x01
for CMD1 i am gettin 0x55 or 0x05
what does it mean???

and after that i received
FAT32 not found!

?????????????????

CC Dharmani said...

Hi Milind,
try as per the code I've posted. After CMD0 (GO_IDLE_STATE), send CMD1 (SEND_OP_COND). then only send the remaining commands. CMD0 returns 0x01 (you're getting correct response there) but the CMD1 has to return 0x00.

MILIND said...

ya you are correct.i tried with same code.the response for CMD0 is 0x00.
but for cmd1 i m gettin 0x05 or 0x55.
which i feel is illegal command.i haven't changed the code logic i am just tryin to view the response at the hyperterminal.
quite frustrating but for so long i am looking for 0x00 but gettin 0x05 .
(i tried with both transend sd 1gb and sandisk SD 2gb)
please help??????

MILIND said...

now i made a little change and it worked!
while sending CMD1 i replaced the CRC 0x95 with 0xff.so i am gettin 0x00 every time i initialise.
after initialisation i am gettin

FAT32 not found!
(means not able to read boot sector)
how to resolve it???

also i am tryin to read a single block by pressing '2'
it just shows - Read succesfully!
but it has never shown the read data????

To write a single block it always shows write failed!

??????

CC Dharmani said...

Hi milind, I had missed the SPI clock speed in your previous comment. It's 11.592M/16 as you mentioned, which makes it go beyond 400K. Usually, SD cards need SPI clock below 400K during initialization. Reduce the clock speed and try the code again.
The CRC is disabled by default in SPI mode. IT's required to be 0x95 only during the first command, CMD0, which makes the card enter into SPI mode. So, CMD1 won't haveany eefect of what CRC value is.
One more thing, check out the phase & polarity settings of the SPI clock. The phase should be Low & the clock signal should remain high when idle.

MILIND said...

SPI settings i have used:master mode,msb first,CPOL=CPHA=0.
these settings are same as you have done.
card doesnt initalise if i make cpol = 1.
p89v51rd2 datasheet says
if CPOL is 1 = SCK is high when idle (active LOW),
if CPOL is 0 = SCK islow when idle (active HIGH).

making CPOL = 0.i checked the clk line which i found to be 0 volts when no data communication.
for CMD0 response is 0x01
for CMD1 response is 0x00
seems initialisation is done.

but after that i received
FAT32 not found!
(means not able to read boot sector)
how to resolve it???

writing single block it shows
Write successful!
but after that when i read a single block by pressing '2'
it just prints 512 spaces & then prints - Read succesfully!
but it has never shown the read data????


in my controller i have 4 settings for spi clk: FOSC/4,FOSC/16,FOSC/64,FOSC/128.
during SD init i used FOSC/16 and i found initilisation successful.
after SD init i used FOSC/4.

does this spi clock rate will matter for reading boot sector???
i will try using 16MHZ crystal.
which will give
FOSC/4 = 4MHZ(after SD INIT)
FOSC/64 = 250Khz(during SD INIT)

ANY SUGGESATIONS ?????

amol said...

I am getting problem with serial communication i have used atmega16 8 Mhz crystal and boud rate 9600
hyper terminal shows only '@@@@@@'
plz solve it

CC Dharmani said...

To Milind:
The messages show that the card is initialized properly!
Are you sure of not reading a blank sector? Did you try reading block 0? OR try to write a block with some characters and read the same block. The same characters should appear.
If problem continues, try to display hex value of what you're receiving.
Also, check the readSingleBlock function, whether anything is changed where the characters are being printed on the hyperTerminal?

CC Dharmani said...

to amol:
which project are you referring to, as you've posted the same comment on two different posts?
Also, pls tell me the baud defined in controller & Hyper terminal both.

amol said...

sir, i am referring to both RTC & SD card interfacing with atmega16. i am using same boud rate to controller & PC 9600. there is no error in program though I am getting nothing on hyper terminal

MILIND said...

HI,

The transmitHex was not working so i introduced a delay of 1ms in the function transmitString,so now its working.now am tryin to send the data to hyperterm in hex format.
as seen that when i try to read the 0000 block.
in the boot code area it showed all 00,
in the first partition area it shows some hex data
0x00 0x04 0x04 0x00 0x0B 0x06 0xE6 0xCD 0xFF 0x00 0x00 0x00 0x01 0xEF 0x1D 0x00
and it shows the signature as 0x55 0xAA
Read successful!
then i tried writing to block 5000
it displayed Write successful!
then i tried reading the block 5000
it displayed all 0xFF
Read successful!


what is wrong?????????????????
If you want to go through the code ,i can mail it to you.
anyway i have mailed you the captured text.

CC Dharmani said...

Hi Milind,
send me the complete code. I'll check it out. mail to: ccd@dharmanitech.com

MILIND said...

ya i have mailed you my complete code. i am using 1GB Transcend SD.
since am not able to receive proper response to write command with 2GB sandisk SD.

iam still receiving the response
FAT32 not found!
with both the cards.
that means problem reading the boot sector.
in my captured text u will find all zeros in the boot code area.why??????

AAA 小喇叭 said...

hello, it sounds promising to the purpose of homebrew.

is it possible to get a zipped package of the Mega8 version too, thanks for result of your hard work anyway.

CC Dharmani said...

Sure,
drop your mail id here or mail me at ccd@dharmanitech.com and you'll get the zipped code for mega8, too!

Kyle said...

i downloaded the zipped code modified for mega32 and program the hex file in the folder into my mega32 without any change(is this the right way to run the test program??), but it didn't show up anything on the hyperterminal.

then i checked the sd_main.c and find a problem with the port_init function, why should the PORTB be set 0xBF and DDRB 0xEF ?? also DDRD? i changed the port_init with PORTB--0xEF,DDRB--0xBF and DDRD--0xFE (the way which i think is right)and compile it with winavr, this time the hyperterminal show up the menu, but the options didn't work well showing "fat32 options disabled".

i'm new to avr, could you give me a hand?

thanks anyway for all the information!

CC Dharmani said...

Hi Kyle.
thanks for pointing it out. You are right about the port init. But since it never gave any problem to me so i never changed it. Just now I compiled and tested the code in my circuit again the earlier code and with the corrections you mentioned. Both are working perfect for me!
Anyways, I've changed the zip file for mega32 to include this change.

Regarding the FAT32 options, the message 'FAT32 options disabled' comes when the controller is not able to detect the FAT32 file system in the card. You can retry after formatting the card with FAT32 file system using your PC.

Kyle said...

thanks
but i got confused
why could both codes work well with the different i/o setting??

MILIND said...

sir did u received my mail??
i have sent u the zipped code.
i searched over the net for my problem and i found that MBR is located in different places for different card.i am using 1GB transcend card.(and the other card which i have is 2gb sandisk is china made ,so might be the problem with this card).but right now i must stick to transend one bcoz i can view the the MBR of it.

problem is getting the cluster.

i am bit confused about finding the appropriate cluster?????

bcoz i end up getting
Error getting cluster!
can i get some docs explaining fat structure for sd card???
equations for accessing sector,clusters....etc..??????????

MILIND said...

sir for raw read/write did we require the information related to clusters,sectors.....etc????

i.e.,will the boot sector location will matter for raw read write????

again i dint find 0xEB or 0xE9 any where????

CC Dharmani said...

Hi Milind,
the first sector in the card is either boot sector or the MBR (Master Boot Record). If the first byte of the sector is 0xEB or 0xE9, then it's boot sector. IF it's not, then it should be MBR with end signature of 0xaa55.
You are getting the first sector end signature correctly (the 0xaa55 gets stored with 0x55 first and 0xaa last).
In your case, the first sector is not the boot sector, but it contains the valid signature of the MBR. The problem is, MBR should contain the partition info from bytes 446 to 509 (total 64 bytes)from where the address of the boot sector can be obtained, which is essential for the controller to get information on importatnt parameters lile cluster size, fat volume, root cluster etc.

Surprisingly, your card MBR contains no partition info, so the controller is declaring that no FAT32 found.

For raw data transfer, you need not worry about the cluster, sector etc. It only needs that the card is initialized, no problem if the fat32 is not found.

MILIND said...

sir i am not able to write to SD card.
for CMD24 response is 0x00.
CS low.
start block token 0xfe is sent.
512 bytes data is sent.
dummy CRC (16-bit) is sent.
and now am expecting 0x05 to be received.but am continously getting 0x55 ??????

Stan said...

Regarding "No rule to make target" errors. Try removing the spaces in any directory names. I renamed "Source Files" and "Header Files" to "SourceFiles" and "HeaderFiles"

That eliminated THAT error.

Stan said...

Actually, let me clarify that. I reduced the compile errors significantly by putting all the .c and .h files in the default directory (created by AVR Studio when you start a new project).

I'm an assembler guy, so I'm making some silly errors trying to get this thing to work.

Anonymous said...

how to convert the little endian to big endian, i'm always getting "fat32 not found" .

CC Dharmani said...

There can be other reasons for 'FAT32 not found'. If you are using AVR controller, you won't need to do little endian - big endian conversion, as they follow little endian convention which is followed by the FAT32 file system.

Which microcontroller are you using?

Marc said...

Hey CC,

I'm using the ATMEGA8. I have everything connected, and have a 2GB SD Card connected formatted with FAT32 (does it matter what the allocation unit size is?) When I load in your hex file, it says "SD init fail..FAT32 not found!" When I press any key, I can write to the file but not read from it.

Marc said...

Sorry about that, I meant I can write to the SD Card (raw data), but not read raw data from it.

Jean-Louis said...

Hello,

Do you (or another who read this) know if the toshiba microSD card 1Gb support the SPI mode ? I know that SPI mode is officialy an "option" for the microSD, and for the moment I'm not able to get any response of this card ...

thx.

CC Dharmani said...

To Mark:
Hi, the main reason for SD init fail is improper connections. Check out all the SD connections again to match with the schematic I've provided in this post. Also, check out the SD adapter properly, if you've soldered it, whether pins inside are bent.
Since you are getting the hyper terminal messages, i don't doubt the controller frequency (8 MHz).

To Jean-Louis:
Toshiba card supports SPI mode.

Marc said...

CC,

Thank you for your quick reply. It turned out that one of the pins (one used for chip select) on my atmega8 was broken off!

Anyways, I was looking through your SPI code, and I was wondering what the point of the following line is for:

SPDR = 0xff;

It's in your SPI_receive() function. Why are you filling up the data register with 0xFF?

CC Dharmani said...

Hi Marc,
during the SPI communication, the clock is provided by the master. Here the AVR is master, and AVR sends out the clock only when you load the SPDR register with something.
So, when we write SPDR = 0xff, the AVR actually transmits the 0xff along with the clock. Since the slave (SD) is waiting for the clock, to put it's data on the bus, as soon as the clock is activated, it starts transmitting the data. In this process, the slave receives the 0xff. But, since it is not expecting any data at that point, it'll simply ignore that byte.
Actually, you can use a single function for receive and transmit both on SPI, as during any of these two processes, the AVR will perform receive and transmit both. I'm using separate function only to avoid confusion.

Marc said...

Hi CC,

Ahh that makes sense. For some reason I thought the SCK is sent out the moment SS is asserted.

Lastly, I am now able to initilaize and list files in my SD card, but when I try to "Read File" on an MP3 file in my 2GB SD card, all I see is an endless stream of junk that gets sent to my hyperterminal.

It kind of looks like that it's repeating the data over and over again. I saw in one of your earlier posts you mentioned that the controller is only reading the first cluster. I have not changed anything with the code, I'm using your hex file you posted.

CC Dharmani said...

Ofcourse marc, you can't display an mp3 file on hyper terminal, it can display only text files properly. mp3 file will look there junk only. Try to read a text file and check if the hyper terminal displays it correctly, if it is, the code is working perfect there.
you have to treat mp3 files separately. For example, you can read it and send it directly to an mp3 decoder chip which can decode the file and produce audio signal. This code just demonstrates how to access files from the SD card with FAT32 file system. you can put it into various uses by adding your own code.

Marc said...

CC,

I did indeed expect garbage to come out, but it was an infinite loop of garbage, is that expected? I was expecting some sort of end to the junk-data and returning to the menu. I apologize if that is an uneducated question, I'm more of an EE than a Computer Science guy.

Jean-Louis said...

CC > ok thanks :)

CC Dharmani said...

Hi marc, today I converted a 3.2MB mp3 file into text. It resulted into a text file with total of 3356455 characters. If a non-stop text stream (with 8-bit ascii characters)with this length is transmitted to hyper terminal with 19200 baud-rate, without any other delay , it'll take (3356455*10/19200=) 1748 seconds, or 29 minutes, for displaying complete file on hyper terminal. Now add into this, the delay taken by the microcontroller to go thru the FAT32 structure to find next cluster in the cluster-chain, by searching thru the FAT entries and other over-head delays which occur due to calling the functions.

I'm sure you wouldn't have waited so long for the file to get displayed completely!! :)

Well, I did it, just out of curiosity. I waited till 27 minutes (ofcourse, while doing some other stuff) for a 2.3MB file to get displayed completely, & finally the menu appeared!! ;)

Marc said...

CC,

Thank you very much for your reply, I truly appreciate it and your contributions. I have learned a lot from this project. And you are correct, I did not wait more than even 15 minutes for the file =)

Saran said...

Hi Dharmani,

Actually i am need to display the status (presence)of Memory card in my application. Is it possible to specify the connected/removal of SD card? If so let me know the procedure.

CC Dharmani said...

Hi Saran,
you can use a microSD card PCB connector which has a separate pin for card detection purpose. You can simply connect that to one of your controller input port-pin to detect presence/absence of the card.
otherwise, in the code, the line where "SD init fail.." is written, you can replace it with "SD card not detected.."

MorgothCreator said...

I see that the code is not optimized at all.
For spi functions that are in the front end, of instead:
spi_init void (void)
unsigned char SPI_transmit (unsigned char data)
unsigned char SPI_receive (void)
can make:
static inline void spi_init (void)
static inline unsigned char SPI_transmit (unsigned char data)
static inline unsigned char SPI_receive (void)

in uart library:
unsigned char receiveByte( void )
void transmitByte( unsigned char data )
void transmitString_F(char* string)
void transmitString(unsigned char* string)
can make this:
static inline unsigned char receiveByte( void )
static inline void transmitByte( unsigned char data )
static inline void transmitString_F(char* string)
static inline void transmitString(unsigned char* string)

With this modification can economize the space on flash memory, and cam make more functions on remaining space.

I propose this changes because your library is not dinamic library :)

CC Dharmani said...

Hi Morgoth,
thanks for the suggestions.
The code was originally written for ATmega8, and there it occupies 99.9% of flash. Declaring functions 'inline' would increase the speed, but it'll need more flash, which i didn't have (as i understand, inline function code is put up by compiler directly at all places wherever the function is called, rather than putting the call instruction to the function. This can be used to reduce the timing overhead occured by calling the function, but it certainly needs more code memory)

I've mentioned this fact in the post also where I've written about the data transfer rates.

But certainly, for controllers having more than 8K, those functions can be declared 'inline' to increase the speed of data transfer.

Unknown said...

Hi, I'm also working with MCU SD card interface and I would like to ask for some help if you can. How do I get high data transfer to the SD card? What's the data rate that you acan achieve for writing to the SD card?Any help or advice you can give me is most welcomed.
thanks

CC Dharmani said...

Hi felipe,
as per the SD card association, "the SD Speed Class Ratings specify the following minimum write speeds based on 'the best fragmented state where no memory unit is occupied'

* Class 2: 2 MB/s
* Class 4: 4 MB/s
* Class 6: 6 MB/s

One critical difference between the Speed Class and the maximum speed ratings is the ability of the host device to query the SD card for the speed class and determine the best location to store data that meets the performance required"
Some of the high speed cards even claim to have achieved 10 MB/s & 20 MB/s speeds also.

So, if you have a powerful microcontroller/processor with lots of RAM and high clock speed, you can achieve the specified speeds or even higher.

When interfacing with microcontrollers, main limitation is the controller's own clock speed, which is usually in few Mega Hertz. This limits the SPI clock speed which results in lower transfer rates.

For achieving maximum possible speed with microcontrollers, run your controller at maximum clock allowed, set the SPI clock speed with minimum prescaler.
Also, reduce the timing overhead by declaring the frequently called functions as 'inline' (like the SPI transmit & receive functions). If you have more RAM, have separate buffers for data & the FAT table access. That will reduce the time consumed by buffer allocation.

Bharath said...

Sir,

Thanks a lot.Your project has been very useful to me. I am also doing a project in which I am interfacing a microSD card with ATMEGA8 to store the on field data.
But instead of using a Hyperterminal to enter data I am trying to give the input from the microcontroller itself.In my project, the ATMEGA8 calculates the speed and I need to store this speed value onto the microSD card.In order to achieve this,can you please tell me what are the necessary changes to be made.
I shall be very grateful if you help me.Thank you once again.

Unknown said...

Hi again.
Thanks for the fast response. I'm already running my SPI peripheral at 20MHz, but I still only get 200KB/s.
I was thinking of implementing multi read/write, do you now how to do it?
I've been trying to send multi block command(CMD25) and multi block start token(0xFC) to make it work but when I try to write/read to it but it only reads/writes 512bytes (1 sector).Another thing that isn't clear to me is the data transfer in this mode.After I send CMD25+0xFC, can I send any size of block, ie.2048bytes, or can I only send 512bytes (configured in BLOCKLEN)? Also, do I have to update the sector address every time or does the SD card handle this?Sorry for bothering you again and thanks very much for your earlier reply.

Unknown said...

CC Dharmani, PLEASE disconsider my last post. I saw your code and most of my questions were answered so now I'm going to try again.
thanks.

Jacky said...

I used codes and found that file could not be opened by PC. What might be wrong?

Unknown said...

Hi i used your code to act my Microsd card to support FAT32 file system.But in that i got FAT32 file sytem not found error

Ankit said...

Dharmani sir,
Thank u for sharing ur code.
It worked for me though with some effort.

thank u very much.

Ankit said...

My experience says the cause of the error
"SD init fail.. FAT32 not found!"
is wrong connections.
Check all the connections.

I was working on the atmega32 and the port initialization and chip select pin troubled me.

Unknown said...

hi Dharamani sir ....how can i give file name for create file function in FAT32 code....I gave
unsigned char fileName[13] = { 'q', 'n', 'i', 'm', 'i', 't', 's', '.', 't', 'x', 't'} ;
then i call creatfile function.But my system going to hang...May i know how i want to give filename for the createfile function.......

Loc Phuoc Nguyen said...

That's great!
Thank you about your source code. I'm beginer with Atmega, and your code so helpful for me.
By the way, I want to write draw data from computer to SD card. Have you ever written SD card from computer using C++. If you had, could you share for everyone! or show me which library to write draw data to SD card
Thanks Dharmani!

CC Dharmani said...

To Jacky:
Hi, did you change anything in the code, if yes, ou can send me the modified code, I'll chekc it out. I had verified the createFile function. If you generate .txt file using it, it'll open using the PC.

To Muthu:
As you didn't mention about "Sd init fail.." message, I assume that the card is properly initialized. If you've used the raw data options (0, 1 or 3) to write anything in the SD, the FAT32 file structure might have got disturbed. Try again after re-formatting the card with FAT32 using your PC.

Regarding the file name problem, store the unused bytes in the fileName[] array with 0x00.

To Ankit
Thanks for the comments. man!

To Loc Phuoc Nguyen:
I haven't written code for C++. But if you want to write the card using C++ with a PC, and if you use this AVR circuit with the code loaded, the job is pretty easy. As you see, the hyper terminal is used here to send command to the microcontroller from PC for reading/writing the SD.
All you need to do is to implement C++ code to access the serial port and send the command directly to the controller via that port just the way hyper terminal sends it. Using that code, you can read/write the card with C++, rather that the hyper terminal

Unknown said...

Hi Sir,
Thanks for the fast response.My sd card init process pass and i can able to read and write in the RAW formet.When i use in FAT32 formet ,it says FAT32 formet Found.Still Ok.But when i create file using createFile function i got File does not exist error.I gave fileName as two method

unsigned char fileName[13] = { 'q', '.', 't', 'x', 't', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

unsigned char fileName[13];
fileName[0] = 'q';
fileName[1] = '.';
fileName[2] = 't';
fileName[3] = 'x';
fileName[4] = 't';
fileName[5] = 0x00;
fileName[6] = 0x00;
fileName[7] = 0x00;
fileName[8] = 0x00;
fileName[9] = 0x00;
fileName[10] = 0x00;
fileName[11] = 0x00;
fileName[12] = 0x00;

both the input i got same error.May i know whty this error happen and how can i rectify?.

Ankit said...

Dharmani sir,
i a have a peculiar problem. i formatted transcend 1GB microSD card on machine running windowsXpSP2. the card gets initialized properly but sometimes it does not detect the fat32 filesystem sometimes it does.
I would think it is a connection problem but then if the card is getting initialized that means the connections are ok. this happens very often.
Can it be any other problem except loose connections?

PS: i did nt change the code except SD_CS_ASSERT & SD_CS_DEASSERT for using PB4 on ATMega32.

Unknown said...

Hi sir,
In createFile function i didn't not enter the file data through keyboard.If i want to give directly means how can i change this following code for my application


while(1)
{
getSetNextCluster (nextFree, SET, EOF); //last cluster of the file, marked EOF
startBlock = getFirstSector (nextFree);

TX_NEWLINE;
transmitString_F(PSTR(" Enter text (end with ~):"));

i=0;
j=0;
do
{
data = receiveByte();
if(data == 0x08) //'Back Space' key pressed
{
if(i != 0)
{
transmitByte(data);
transmitByte(' ');
transmitByte(data);
i--;
size--;
}
continue;
}
transmitByte(data);
buffer[i++] = data;
size++;
if(data == 0x0d) //'Carriege Return (CR)' character
{
transmitByte(0x0a);
buffer[i++] = 0x0a; //appending 'Line Feed (LF)' character
size++;
}

if(i == 512)
{
i=0;
error = SD_writeSingleBlock (startBlock);
// TX_NEWLINE;
// TX_NEWLINE;
// if(error) {transmitString_F(PSTR("Write failed..")); return;}
j++;
if(j==8) {j=0; break;}
startBlock++;
}
}while (data != '~');

if(data == '~')
{
size--; //to remove the last entered '~' character
i--;
for(;i<512;i++)
buffer[i]= 0x00;
error = SD_writeSingleBlock (startBlock);
// TX_NEWLINE;
// TX_NEWLINE;
// if(error) {transmitString_F(PSTR("Write failed..")); return;}
break;
}

prevCluster = nextFree;

nextFree = searchNextFreeCluster(prevCluster);
if(nextFree == 0)
{
TX_NEWLINE;
transmitString_F(PSTR(" No free cluster!"));
return;
}

getSetNextCluster(prevCluster, SET, nextFree);
}

Bharath said...

Sir,
Even I have the same doubt as Muthu. Instead of giving the data from the hyperterminal to create a file, how should the code be changed so that I myself can give a name to create a file rather than accepting from the keyboard.
And sir , one more thing. After creating a text file, how can we write text into it. I need to store some on field speed values into that text file.
I shall be very grateful if you help me with this.Thank you.

CC Dharmani said...

To Muthu:
In your first problem, it sounds like you are calling readFile() function by mistake instead of createFile(), check it out, if not solved yet!

To Ankit:
In the main function, call the SD_init() function twice, instead of once, and check again.

To Muthu & Bharath:
While writing these libraries, I had thought of it as just a learning aid for SD card & FAT32 for newcomers, that's why it uses hyper terminal for reading, writing as it's much easier to verify that way.
But now, I'm modifying these routines to make them more generic, which can be used like the way you guys want.
I'll post it here shortly (within a week) after testing it. Till then, try it your own way :)

Bharath said...

Sir,
Thanks for the reply. I am eagerly awaiting for that code.Mean while I will try experimenting.

vishnu said...

Hi sir
will the sd card interface is available with isis...

Thnx

CC Dharmani said...

Hi Vishnu,
I don't have any idea about isis.. so, can't give a specific comment. But, yes, the FAT & SD library can be used for any microcontroller.

Adarsh Agrawal said...

hi sir adarsh here from iit mumbai....
sir i have to design a device which read the contents and ppl near him will able to hear..

the basic idea is i have to take the data from the sd card....stored in wave/hex/text format and using that pwm is generated which is input to the amplifier and finally to the speaker..
so what ever u was discussing is able to read a wave/hex file or not.also i want to know is data taken fron sd card bit by bit or in a cluster...
basically i have to play a speech stored in sd card in wave format....
so suggest me in what format data is to b stored and how it is taken using atmega 32..

hope for ur reply soon.....

nischay said...

Hello dharma,

Nice info about SD card interface.i am trying to use mega8 to play raw wav data stored on a SD card (raw image is stored on the SDcard).but want to do it in AVR assembly, can you help me with SD card read,write and init routines in asm

thanks,

nischay

Anonymous said...

Hey,

I was able to successfully interface your awesome library to my atmega328 and sd card. I am trying to play an mp3 file through a VS1002 mp3 decoder, but the song is playing in slow motion. I have spent the past 4 days trying to figure out what could be wrong. Just to rule your code out, do you know of any delays or functions or such that could possibily be hindering my transmit to SPI?

CC Dharmani said...

To Adarsh:
How r u going to store the data in the SD card? If you are using your PC to store the wave files then you need to use FAT structure, not the raw SD functions. There you can decode the wave file to get the data chunks & send it byte-by-byte to the PWM.
Other way would be to use an audio codec IC, like VS1002 on SPI bus(see the last comment), that would decode the WAVE file and give directly audio output. Also, that would be able to play mp3 files, if in future you decide to store data in mp3 format.

To Nischay:
if you check the previous comments, Morgoth has written the SD routines in assembly, you can check out the link he has given:
http://digitalelectronicsandprograming.blogspot.com/2009/02/fat32-complete-library-in-asm-linguage.html

To Anonymous:
Did you try using higher frequency crystal like 16MHz? It will make the SPI bus speed double. Check out if that makes the songs play faster. Also, you can declare the SPI transmit & receive functions as "inline", that'll save some more time.

Anonymous said...

Yes, I am using a 16Mhz crystal for my atmega328. Declaring the functions inline did not help. So you don't think anything in your readFile code would be hindering my transmit?

Anonymous said...

Hi,

I can read/write SD card but I can't access the FAT because is a
little endian to big endian issue.

I use Keil compiler for 8051.

Do you have any suggestion ?

Adarsh Agrawal said...

thanks for ur quick reply.....
yes sir i want to store the wav files using pc only...so tell me how to store data in sd card using pc.....
i have basically no idea about fat system plese give me a detail idea of storing the wav files in sd card using pc...

thanks
adarsh

CC Dharmani said...

To anonymous:
Ya, I don't think the FAT32 code is the reason for delayed audio. May be you can check out the VS1002 driver code & settings.

To anonymous:
I haven't tried so far to modify the code for big-endian structure.
You have to write code to get the values of the variables bigger then 1 byte by converting them from little-endian to big-endian structure.

To Adarsh:
You can use an SD card reader to write files into SD card using PC. After storing the files, when u interface the card with the controller, u will also need to modify the FAT32 reading functions so that the controller doesn't send the data to hyper terminal but send it to PWM or the audio codec chip.

Adarsh Agrawal said...

can i directly write wav files to sd card........is there some modification should be done....formatting should be done in which format.............is wave file is binary files.......in what format it should be written....so that data is taken byte by byte..........

M. said...

Nice project, but i have a problem. When I try to write a file larger than 1kB it's unredable under Windows (file corrupt message). File is always readable via HT (atmega). After running ScanDisk under Windows corrupt files are healed.
I'm working with Your code for M32 (low voltage). Any idea why it's like that? Do You have any problems with files >1kB?

felipe said...

Dear CC Dharmani,
If it is not too much trouble I would like to ask you for help/suggestion(or to anyone in this forum that reads this msg).I'm trying to achieve maximum data write but I'm only able to achieve around 250KB/s. According to any SD card specification, they are able to transfer at least 1MB/s. I'm already running SPI at 20MHz and using multi block write and I can only write at 250KB/s. Although I'm 5Mhz short of the maximum frequency for the SPI I think that I should be writing at 700KB/s at least. So, if anyone has any ideia as how to do this I will be very thankful.
Regards

CC Dharmani said...

To Adarsh:
you can format the card with FAT32 and store the WAV files directly, using the SD card reader. WAV file contains the uncompressed audio data which can be accessed byte by byte. For this, you'll need to understand WAV file structure. Just go to google and search for 'wave file format', you'll get the format document. Go thru it for better understanding.

To M.
Did you change anything in the createFile function? That error comes when the root directory is not extended to new limits after creating a file, even though the file is created properly. When you run the windows scan, it automatically corrects the root directory boundery, so the files appear properly.
Check out the code, if you've chnaged anything!

To Felipe:
If you have RAM more than 1KB, you can try to use two separate buffers for data access and the FAT access. This will reduce the time consumed for finding the files in clusters and will result in some increase in the speed.

Unknown said...

CC Dharmani,

Sorry, I forgot to mention that I'm writing the data in raw mode to SD card, i.e. I fill a buffer with 512Bytes and I tell the MCU to write this same buffer from sector 500 to sector 4500 using multi block write.
I have 6KB of RAM to use but even with this memory I can't achieve high speed data transfer. I have also noted that the maximum sectors that I can write to the SD card, using multi write, is somewhere between 4000 and 5000 sectors (I guess this is about the size of the buffer inside my SD card). If I try to write more than this amount of sectors the data will not be written to the card BUT if I call a new multi block write it works just fine but even this way I'm not achieving the data speeds that the card should work.If you have anymore sugestions I'll be grateful. And thanks again for spending your time to answer me.
Regards

Adarsh Agrawal said...

hi sir still waiting for your reply........
can i replace sd card with mmc card....

please give ur mobile number...
really i am in trouble as i have to complete my project within 5 days...

pls post ur phone no on

adarsh1203.07@bitmesra.ac.in

Adarsh Agrawal said...

sir i use mmc card and it shows an error sdinit failed....will it be solved by using sd card....is there any other way of init mmc card...
i use atmega16 with 16mhz clock....

M. said...

CC Dharmani said...
"Did you change anything in the createFile function?"

No, it's untouched. I've even tried with fresh copy of FAT32.c from both projects (M32 & M8). Verification showned that they are perfect match, as expected.
My only change is location of CS pin.
Could it be something witch SD card? (I'm using 128MB micro SD with regular size adapter made by SanDisk).

Thanks 4 reply :)

CC Dharmani said...

To Felipe:
I think the other possibilities to increase the speed is to review your functions of filling the 512 byte buffer, how fast you are writing it and the internal writing speed of the SD card. Card to card writing speed varies. Have you tried another SD card?

To Adarsh:
The MMC card has a small difference in the initialization command sequence, in my code I've not taken care of that, but the SD_init() function can be changed little to try with MMC card, though I've not tested it. If you have an SD/microSd card, try the code with that one or I'll send you the function of MMC initialization to your mail ID.

To M.:
One of the reader of this blog has tested the code on 128MB card successfully. But anyways, you can try with another card. If possible you can send me the sector-0 data (512 bytes) in HEX. I'll just check the FAT structure if any difference is there.

Adarsh Agrawal said...

actually sir i tried with mmc/sd card it doesnt work and when i insert the card in card reader even card now is not detected..........

i just have to read wave files data from sd card using atmega 16l..nothing else.

please suggest me the full code.......and pls send ur phone number.....i m trouble...i have to complete my project soon..........

adarsh1203.07@bitmesra.ac.in

Adarsh Agrawal said...

it would be better if u send the mmc card init code can i use 16mhz crystal..............

i got msg on hyperterminal as sd card not init...fat32 not found.....

CC Dharmani said...

If the SD card also displays the same error message, I think better have a close look again at your connections, that's the most probable reason!

Adarsh Agrawal said...

so i have to use only thee 100 ohm resistor and 1 capacitor.........nothing else.......

Adarsh Agrawal said...

pls give ur contact no.i have lots of doubts..........will 2 gb sd card will work.......i have 2 gb kingston sd card.......

M. said...

Hello again,

CC Dharmani said...
"If possible you can send me the sector-0 data (512 bytes) in HEX. I'll just check the FAT structure if any difference is there."

I've found my problem. Using WinHex I've analyzed sector zero of my SD Card. My problem was different cluster size. In Your code cluster size is set to 4kB.
On my 128MB SD card cluster size was set to 1kB by windows during Card formatting process.

So, fo further compatibility with all kind (capacities) of SD cards it is good to consider implementation of cluster size autocheck.

I'll try to do that by myself. If I succed the code will be sent to You :)

Thanks 4 good clue ;)

M. said...

So... the big change is:

in FAT32.c

line 451 is: if(j==8) {j=0; break;}

should be: if(j==sectorPerCluster) {j=0; break;}

enjoy :)

Bharath said...

Sir,
I am using a data logger in one of my projects. If I write RAW data onto the SD card, how can I view that data on my computer? It would be much more helpful if you tell me how can I import that RAW data into Microsoft Excel.Because when a card formatted as RAW is opened in a computer ,it says "Card not Formatted. Do you want to format the card?" But I want to view my RAW data on the computer.
Can you help me?

MorgothCreator said...

To view raw data from SD card and from other storage devices you can use WinHex.

Bharath said...

Thanks Morgoth for your quick reply. I will try it out.

CC Dharmani said...

Thanks M., for suggesting that small but very effective change for compatibility with different size cards! :)
I'm modifying my library for making it more general, there I'm using variables rather than fixed numbers, just like you suggested.

Vikrant Srivastava said...

Sir, I am trying to read a file in FAT32 format from the SD Card. The file was directly loaded into the SD Card using PC but the problem is that it is unable to detect the file.
Can u please point out what exactly could be the problem. Thanks.

P.S. - I have made quite a few modifications in the code but am pretty sure that the code is fine.

Vikrant Srivastava said...

Sir, with reference to my previous post I want to add that I am only concerned with reading a FAT32 file from SD Card which was directly loaded into it by a PC.
For this purpose I have made quite a few modifications in FAT32.c file. I have kept only the below listed functions and removed all other functions:

- getBootSectorData
- getFirstSector
- getSetNextCluster
- getSetFreeCluster
- findFiles
- readFile

Please tell me if I am making any mistake by removing the other functions. Basically I am not able to read boot sector. I am using Kingston 256 MB card.

Vikrant Srivastava said...

Sir, I was going through previous posts and I think I got my problem. The uC I am working with is big endian.Can u plz suggest wat could be done for this.

Bharath said...

Dharmani Sir,
If I wanted to use the first options i.e. reading and writing raw data , in which form should I initially format the card. I have a 2GB microSD card formatted in FAT32 form. Now if I want to use first two options ,into which other form should I format the card? And is there no other way of viewing the raw data other than the hyperterminal. Is there any software to accomplish this?
Thank you.

CC Dharmani said...

To Vikrant:
Hi, I've never tested anything big-endian as I'm using the AVRs, but I think now I need to look into it as this question is coming frequently. Which controller are you using?
Can you just send me the sector-0 data? I just want to check it out for suggesting a solution.

To Bharath:
For using the raw function, no need to worry about the format of the card. It doesn't make any difference whether you format the card or not!
For viewing raw data, you can use the winHex, as suggested by Morgoth. You can download it from here:
http://www.x-ways.net/winhex/index-m.html
You can also use Bray's Terminal:
http://www.smileymicros.com/download/term20040714.zip?&MMN_position=42:42

Royce said...

Hello Mr Dharmani,
I'm new to SD card..Your project is very interesting. Can you kindly confirm these points:
1.
One cannot write less than 512 bytes to the SD card at one go, correct ? Does that mean I accumulate all my data (say in a data logger app) in an external (RAM) buffer till it reaches 512 bytes? What if power is removed before this point ? Also, if I just want to overwrite a few bytes, do I read the entire block into RAM, modify it & then write the whole thing back to the SD card ? Is there *any* way to just write a few bytes to the card ?
2.
Does this also mean that the uC needs to have at least 512b spare RAM for SD card operations?

Thanks & Regards,
--Royce Pereira

Royce said...

Hello,
I also want to add, that I will use the SD card with AVRs.
Also I do not need to view my data on a PC, so I need only raw data access.

Thanks & Regards,
--Royce

Vikrant Srivastava said...

Sir, I am working on a Freescale uC of Coldfire v1 series. I am trying to convert the code for big endian myself. I hope it will work. Sir can u plz tell me dat for reading a FAT32 file from SD Card are the functions listed below are sufficient or do I need to include anymore functions???

- getBootSectorData
- getFirstSector
- getSetNextCluster
- getSetFreeCluster
- findFiles
- readFile.

I am able to read and write raw data from SD Card but not any FAT file. Does for reading and writing raw data this big or little endian comes into picture???

CC Dharmani said...

To Royce:
Hi, the SD will always read/write a block the moment you send the block address. But, since the SD is operating on SPI bus, theoritically you can hold the communication after each byte by stopping the SPI clock, but practically I've not tested this one, one need to check out if the SD is having any time-out limit internally..

You can manage with RAM lower than 512 bytes, but compromising with the speed!

To Vikrant:
For reading a file, you'll also need the convertFileName() function.

While reading /writing raw data, the little-endian big-endian doesn't make any difference as the data is accessed byte-by-byte.

Royce said...

Thanks for the reply.
I am not clear about 'needing less than 512B buffer, while sacrificing speed'.

Could you kindly elaborate this technique ?

Thanks !!

CC Dharmani said...

Hi Royce,
for reading the raw data, if you don't have a big buffer then you can read that block into smaller chunks, for example, if you have 50 byte buffer, you can get first 50 bytes over the SPI bus and do whatever process you want to do on them, after that read the next 50 bytes of the block using the same buffer, you can repeat the process till 500 bytes are over after that get the last 12 bytes.

So, here you will add some extra loops in the readSingleBlock() or writeSingleBlock() functions, which will reduce the overall reading/writing speed of the SD card.
Well, whether some reduction of speed has any effect that depends on your application.

Royce said...

Hi,
The challenge is actually with writing the data. If I have a logger where I read 5 integers( temperatures for example), I need to save the 10 bytes & exit. In the meantime, the user may ask to view the to-date log (or some other interruption), so there is no way the uC can hang around for 512B to be written (esp.if the logging is done once per 5 minutes), as it may want to switch to reading the card, before 512B are up.

So I may have to save in RAM & wait for 512 bytes to accumulate, which may take 2 days (at slow log intervals). This is not good 'coz if the power is disconnected, what happens to my RAM values waiting to be written to the card?

Am I missing something here ? Is there some way that these concerns have already been addressed?

All these issues are holding me back from implementing SD card in an upcoming logger project.

CC Dharmani said...

That can be achieved easily with a 512 byte buffer and an integer to keep track of where in that 512 bytes you have to add data.
Read the block, modify those 10 bytes in the buffer and write the buffer back to the same block, as you had mentioned earlier. Then the controller is free to do other tasks till the next 10 bytes are ready to be written.

Unknown said...

Dear CC Dharmani,
I've tryied doing what you suggested so I bought a new card (8GB SDHC 4) to see if I can get higher writing speeds. According to the SD standards this card should be able to write 4MB/s continuously without any problem but all I got was 330KB/s. Any more ideas? ANYONE? I'm really running out of ideas and I need to achieve a high writing data speed.thanks again for all of your prompt replies.

CC Dharmani said...

Hi Felipe,
if possible, you can interface the card with the controller using SD Bus protocol (4-line parallel) instead of SPI bus, there you can achieve speed almost four times as compared to SPI mode speed.

Nischay,India said...

Hello Dharma,

thanks for providing link to Morgoth's ASM library, iam able to write to sectors (No FAT- only raw data transfers) but unable to read from the sector/s ( starting from sector 0) - can you suggest any solution ?

CC Dharmani said...

Hi Nischay, I'm not so comfortable with the assembly coding. Better person to ask that question would be Morgoth himself.

Hashmi said...

Dear sir CC Dharmani ,
your project about SD-Card interfacing to ATMega AVR is much interesting and helpful,
can you kindly help me about mt difficulties?

how i will write data in new created FAT-32 file in SD-Card?
thanks.

Anonymous said...

I'm trying to adapt your code to an Atmega168. It's turning out to be harder than expected. The led lights up but nothing is displayed in the terminal. I need help with the port values and registers I think

CC Dharmani said...

Yup, you need to change some of the registers for mega168. Send me the code you modified on ccd@dharmanitech.com, I'll check it out.

Anonymous said...

When getting the "FAT32 not found" error, try putting a 50K pull-up resistor to the DO pin (pin 7). That did the trick in my installation.

Anonymous said...

Hi,

can you tell me why the µC keeps resetting all the time when no SD card is inserted? How can I disable that? I want to disable the µC reset when a card is inserted, too. How do I do that?

CC Dharmani said...

Do you have your AVR programmer connected to the board in off condition? that can make the uC reset continuously. Either keep it unplugged or keep it ON.

aniket patil said...

gr8 job man!
i just want 2 ask u one question

how i will be able to interface two SPI pheripherals(like SD Card and 3310 Nokia LCD display) at the same time to atmega32.
i know that atmega32 has only one SPI bus.

hope u will answer my question.

thank u for such gr8 contribution to our knowledge.

«Oldest ‹Older   1 – 200 of 723   Newer› Newest»