[TUT] [C] Getting SD/MMC card working painlessly with FatFS

Last post
341 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NOTE: This thread is an evolving work. E L Chan continues to develop FatFs and this first post is based on his version back in 2008. It has changed a lot over the years. So yes, read just this first post and understand some of the things you need to do to adapt FatFs for yourown use but you MUST red all pages (currently 15 as I write this) to understand later changes that have happened in the FatFs code. After reading to the end of the thread you are then ready to start work. You will probably end up using one of the worked examples in the later pages of this thread - not necessarily just this first post. Moderator, August 2013

FatFS is a great library for getting FAT12/FAT16/FAT32 running on pretty much any C platform http://elm-chan.org/fsw/ff/00index_e.html. The developer even took the time to provide example code for pretty much any application you could want. The problem that I ran into, however, is that the code isn't very easy to adapt, unless you spend a lot of timing pouring over it and troubleshooting.

This tutorial is primarily aimed at people who have little idea about how to program AVRs and want to spend more time developing their ideas and less time trouble-shooting the back-end. I hope that this tutorial helps you cut to the chase and have a working MMC/SD card interface in just a few minutes.

First off, let me point out that this code is running on an ATmega644p, developed on an STK500. I'm using a Kingston microSD card that I can plug into a miniSD adapter (that I soldered to wires so that I could plug them directly into the SPI pins) or into a normal adapter (that I can plug into a computer SD card reader).

My pinout looks like this:

DAT1-------NC
DO---------MISO (PB6)
Vss2-------GND
CLK--------SCK (PB7)
Vcc--------3.3V (PC0)
Vss1-------GND
DI---------MOSI (PB5)
CS---------SS (PC1)
DAT2-------NC

with no extra components. (In retrospect, it's probably not a bad idea to put a capacitor at the 3.3V input to the SD card, in order to make sure voltage doesn't sag.)

For this tutorial, you will need a uC with at least 32K of flash memory and 4K of internal SRAM. (Of course, by disabling many of the functions and using Tiny-FatFS, it can be made far smaller, but that is outside the scope of this tutorial.)

The code is very comprehensive, but there are a few things that are overengineered, and were causing serious problems with my ATmega644p.

For instance, the UART code is very dense, and makes use of interrupts, which somewhat complicates things (the code is no longer linear, as the interrupts can, well, interrupt) and makes it harder to understand for beginners. It also uses a FIFO buffer, which is again overkill for a program that patiently waits until the user inputs a two letter combination.

Anyhoo... Don't get me wrong, it's a GREAT library! I just think that for a first time user it's a little harder than it should be.

CAVEAT!!!:

Quote:
You MUST use 3.5V or less on both the SD card, and the SPI lines. If your chip is already running at <3.5V, great, otherwise you NEED level converters. Don't be mad at me if you blow up your SD card because you gave it too much juice. (They like 3.3V, but will tolerate 3.5V.)

Now on to the good stuff. Start by downloading the ffsample.zip package from http://elm-chan.org/fsw/ff/00index_e.html. Unzip the avr directory-- it's the only one you will need.

This next step is optional; it's only to help clean the directory. Delete/move the following files (They're unnecessary for SD card support, although they don't hurt anything if you leave them there.):

    ata.c cfc.c
    cfmm.c
    Makefile_ata
    Makefile_ata2
    Makefile_cfc
    Makefile_cfc2
    Makefile_cfmm
makefile

Next, modify "MCU_TARGET = atmega64" (line 3 or so) in both Makefile_mmc and Makefile_mmc2 to match your uC. I'm using a ATmega644p so I replaced "atmega64" with "atmega644p".

Next, make a copy of Makefile_mmc and rename it Makefile. (Makefile_mmc2 is for Tiny-FatFS, which we'll get to later. The modifications to one apply to the other, with the exception of a couple bugs that needed fixing.)

Right now, if you were to compile it, there's a good chance it wouldn't even compile because of different register names between uC. This is certainly the case between the atmega64 and the atmega644p. So let's fix that.

main.c

If you read the warnings, you'll probably see that most of the errors are in main.c where the ATmega64 has a LOT more ports than your standard avr chip. So fire up your favorite text editor (I use Smultron), and take a look at the IoInit() function.

First, find the function ISR(TIMER2_COMP_vect). You need to check that this is the correct vector name. It wasn't for me. Looking on the Interrupt Vector table in the ATmega644p's documentation, I saw I had to change it to:

TIMER2_COMPA_vect

(Remember, "_vect" must be put at the end of the vector name in the table)

Comment ALL the lines out from PORTA = ... to PORTG=...
(See further down for how this looks). You'll eventually need to feed the SD card 3.3V, so later on you might reactivate ONE (1) pin, but you certainly don't need all of them.

Next, a little bit further down in the same function (line 196 or so) you might need to change the Timer2 variables. Be careful here, as it's not as simple as just renaming the variables. The registers might have changed, too. By looking on the ATmega64 datasheet, you can see on page 160 that OCR2 is the Output Compare Register for Timer2, an 8-bit timer. Cross-referencing to the ATmega644p datasheet, you see that there are TWO (2) Output Compare Registers. I chose to replace OCR2 with OCR2A, but I expect it would have worked just fine with OCR2B, also.

You'll also probably need to change TCRR2 and TIMSK, but be careful, as you might need to change the value written to the register, too. In my case, again comparing datasheets, on page 157 I found that originally, TCCR2 bits WGM21, CS22, and CS20 are written high. On the ATmega644p, page 153-156, I found that the TCCR2 register has been split into two parts, and so in order to enable all the original bits, I had to write to both TCCR2A and TCCR2B. Likewise for TIMSK, where on page 160 you see that only OCIE2 is high. On the ATmega644p, I had to set TIMSK2 (I don't know why there is a two here and not on the ATmega64) for OCIE2A.

Lastly, two lines later comment out sei();. The final function now looks like this:

//	PORTA = 0b11111111;	// Port A

//	PORTB = 0b10110000; // Port B
//	DDRB  = 0b11000000;

//	PORTC = 0b11111111;	// Port C

//	PORTD = 0b11111111; // Port D

//	PORTE = 0b11110010; // Port E
//	DDRE  = 0b10000010;

//	PORTF = 0b11111111;	// Port F

//	PORTG = 0b11111; 	// Port G
	OCR2A = 90-1;		// Timer2: 100Hz interval (OC2)
	TCCR2A = 0b00000010;
	TCCR2B = 0b00000101;

	TIMSK2 = 0b00000010;	// Enable TC2.oc interrupt

	rtc_init();			// Initialize RTC

//	sei();

mmc.c

Now it still won't compile for most of us, but we're almost there. We need to fix some things in mmc.c. We will make a lot of modifications, so you might want to back it up, too.

Again, on my chip I don't have a PORTE, so of course this fails. Moreover, the pins for the SPI port on the ATmega64 and ATmega644p are different.

Start by adding the following lines right after the #include section.

/*SPI configuration*/
#define DD_MOSI	DDB5
#define DD_SCK	DDB7
#define DDR_SPI	DDRB
#define DD_SS	4

/* Defines for SD card SPI access */
#define SD_CS_PIN	1
#define SD_CS_PORT	PORTC
#define SD_PWR_PIN	0
#define SD_PWR_PORT	PORTC

These defines will all have to be modified to your particular ATmega and setup. The DD_* #defines can be found in the I/O Ports-->Alternate Port Functions section.

The SD_* #defines are up to you. I chose to conenct the SD card's power supply to PIN0 (you did make sure you're not outputting more than 3.5V, right???) chip select to PIN1 of PORTC, but you could chose another if it were more convenient. Just avoid using one of the four pins associated with the SPI port. You can even comment out the SD_PWR_* if you have an alternate power supply (in this case, you have a 3.3V level converter on the SPI I/O pins, right???)

Continuing on, modify the #define SELECT() and #define DESELECT() as so:

#define SELECT()		SD_CS_PORT &= ~(1<<SD_CS_PIN)		/* MMC CS = L */
#define DESELECT()	SD_CS_PORT |=  (1<<SD_CS_PIN)		/* MMC CS = H */

Then delete these three lines:

#define SOCKPORT	PINB			/* Socket contact port */
#define SOCKWP		0x20			/* Write protect switch (PB5) */
#define SOCKINS		0x10			/* Card detect switch (PB4) */

Next, go to the power_on(void) function and replace it with

static
void power_on (void)
{
#if (defined SD_PWR_PIN | defined SD_PWR_PORT)
	DDRC|=(1<<SD_PWR_PIN); 			// Turns on PWR pin as output
	SD_PWR_PORT|=(1<<SD_PWR_PIN);	// Drives PWR pin high
#endif

	DDRC|=(1<<SD_CS_PIN); 			// Turns on CS pin as output
	DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)| (1<<DD_SS);
	SPCR = (1<<SPE)|(1<<MSTR); /* Initialize SPI port (Mode 0) */
}

Change power_off(void) to:

static
void power_off (void)
{
	SELECT();            /* Wait for card ready */
	wait_ready();
	release_spi();
	Stat |= STA_NOINIT;		/* Set STA_NOINIT */
}

and change chk_power(void) to

static
int chk_power(void)		/* Socket power state: 0=off, 1=on */
{
	return 1;
}

Finally, at the very end, replace the function disk_timerproc(void) with:

void disk_timerproc (void)
{
	BYTE n;

	n=Timer1;						/* 100Hz decrement timer */
	if(n)
		Timer1 = --n;
	n=Timer2;
	if(n)
		Timer2 = --n;
}

The code will probably now compile, but it almost certainly won't work. There are still some more modifications to make.

uart.c

First, let's get rid of the original uarts code and replace it with something a little easier to understand. Simply rename the existing uart.c, for example to uart.c.old, and save this code as a new uarts.c:

/*------------------------------------------------*/
/* UART functions                                 */

#include 
#include 
#include 
#include "uart.h"

#define	BAUD		9600

void USART_Transmit( unsigned char txData ) 
{
	/* Wait for empty transmit buffer */ 
	while ( !( UCSR0A & (1<<UDRE0)) );
	/* Put data into buffer, sends the data */ 
	UDR0 = txData; 
}

void USART_set_baud_rate(double baudrate)
{
	// calculate division factor for requested baud rate, and set it
	int bauddiv = ((F_CPU+(baudrate*8L))/(baudrate*16L)-1);
	UBRR0L= bauddiv;
#ifdef UBRR0H
	UBRR0H= (bauddiv>>8);
#endif
}

/* Initialize UART */

void uart_init()
{
	UCSR0B = (1<<RXEN0)|(1<<TXEN0);		// Turn on U(S)ART port
	UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);	// Set frame format: 8 data bits, 1 stop bit, no parity
	USART_set_baud_rate(BAUD); //Set baud rate
}


/* Get a received character */
uint8_t uart_get ()
{
	unsigned char d;
	while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been recieved and is ready to be read from UDR 
	d=UDR0;
	return d;
}

/* Transmit a character */
void uart_put(uint8_t d)
{
	
	USART_Transmit( d );
}

/* Transmit a string */
void uart_puts(const char *s)
{
	while (*s) 
		USART_Transmit( *s++ );
}

uart.h

You'll also need to backup uart.h (uart.h.old, perhaps), and replace it with:

void uart_init(void);			/* Initialize UART */
uint8_t uart_get (void);		/* Get a byte from UART Rx */
uint8_t uart_test(void);		/* Check number of data in UART Rx FIFO */
void uart_put(unsigned char);	/* Transmit a byte*/
void uart_puts(const char *s);	/* Transmit a string of bytes*/

If you have trouble understand what this does, check out abcminiuser's excellent tutorial on USART.

Next, let's look at the rtc clock. While what was done in the example was very comprehensive, this is far too much for a simple data logging operation. Let's just trim this down to the bare minimum. I'll leave the function structure there because it could come in handy later on, especially if you want to run a clock off the internal oscillator-- even if it's inaccurate, it will still be accurate enough to let you know approximately when a file was made.

Again, backup rtc.c-- to for instance rtc.c.old-- and save this code to a new rtc.c

/*--------------------------------------------------------------------------*/
/*  RTC controls                                                            */

#include 
#include 
#include "rtc.h"

BOOL rtc_gettime (RTC *rtc)
{

//	BYTE buf[8];

//	rtc_read() //This is where you would read the clock.
	
//	rtc->sec = (buf[0] & 0x0F) + ((buf[0] >> 4) & 7) * 10;
//	rtc->min = (buf[1] & 0x0F) + (buf[1] >> 4) * 10;
//	rtc->hour = (buf[2] & 0x0F) + ((buf[2] >> 4) & 3) * 10;
//	rtc->mday = (buf[4] & 0x0F) + ((buf[4] >> 4) & 3) * 10;
//	rtc->month = (buf[5] & 0x0F) + ((buf[5] >> 4) & 1) * 10;
//	rtc->year = 2000 + (buf[6] & 0x0F) + (buf[6] >> 4) * 10;


	//This code is just to provide some kind of a valid response.
	rtc->sec = 1;
	rtc->min = 2;
	rtc->hour = 3;
	rtc->mday = 4;
	rtc->month = 5;
	rtc->year = 2006;


	return TRUE;
}

BOOL rtc_settime (const RTC *rtc)
{
	BYTE buf[8];

	buf[0] = rtc->sec / 10 * 16 + rtc->sec % 10;
	buf[1] = rtc->min / 10 * 16 + rtc->min % 10;
	buf[2] = rtc->hour / 10 * 16 + rtc->hour % 10;
	buf[3] = 0;
	buf[4] = rtc->mday / 10 * 16 + rtc->mday % 10;
	buf[5] = rtc->month / 10 * 16 + rtc->month % 10;
	buf[6] = (rtc->year - 2000) / 10 * 16 + (rtc->year - 2000) % 10;

/*This is where you would set the new time to the clock*/

	return TRUE;
}

BOOL rtc_init (void)
{
	BYTE buf[8];	/* RTC R/W buffer */
//	UINT n;

	/* Read RTC */
//	rtc_read() //This is where you would read the clock the first time.

	if (/*SOMETHING_IS_WRONG*/0) {	/* When RTC data has been broken, set default time */
		/* Reset time to Jan 1, '08 */
		memset(buf, 0, 8);
		buf[4] = 1; buf[5] = 1; buf[6] = 8;
//		rtc_write(buf);
		/* Clear data memory */
		memset(buf, 0, 8);
//		for (n = 8; n < 64; n += 8)
//			rtc_write(buf);
		return FALSE;
	}
	return TRUE;
}

There you go, all done! Now just type make in the directory root and you should have a hex file ready to be uploaded to your uC.

CAVEAT!!!:

Quote:
On many uC, the SPI port is the same as the ISP port, so you may have problems uploading the program if the uC is connected to the memory card. I do, so I have to remove it every time I want to upload, otherwise I get an error.

Once you upload the hex file, you need to interface with the program through a serial terminal. It's a very simple interface, although it's not well documented. Commands are given in two-letter combinations, followed by a parameter, if one is needed. If the command was correctly formatted, there is ALWAYS a response of the form:

rc=

To get started, you need to initialize the disk. Type:

di 0

This is a Disk Initialize for disk #0. Afterward, assuming the sd card is already formatted, type:

fi 0

which means File-system Initialize for disk #0. Now you can access the disk structure by typing

fl

which means File-system List.

If you get this far and it seems to respond, then everything is just fine. (If not, post here and hopefully someone will help you.) There are many more commands, but you can discover them for yourself by looking in the for(;;) loop in main.c

Enjoy!

--Kenn

P.S. If you want to try Tiny-FatFS, just apply the same modifications to main2.c as above, copy Makefile_mmc2 to Makefile, and recompile with

make clean
make

Edit: Fixed typos, added suggestion for capacitor on 3.3V input.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow - thanks for this - I just bought one of these:

http://www.futurlec.com/Mini_SC.shtml

and was about to start "playing" !

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, thanks a lot for writing this up. Currently, I am looking at interfacing a SD card to my ATmega32 and this TUT will be a great help.

davef

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thought I'd just try compiling it for the ATmega64, before making changes and the error file doesn't look too good. BTW, it generates the hex file.

What compiler are you using? With WinAVR 20071221 I get the following:
(I think I have seen this "signedness" issue moving from older versions of WinAVR)

Quote:

> "make" all
avr-gcc -gdwarf-2 -Wall -Os -mcall-prologues -mmcu=atmega64 -c -o main.o main.c
main.c: In function 'main':
main.c:235: warning: pointer targets in passing argument 1 of 'get_line' differ in signedness
main.c:236: warning: pointer targets in assignment differ in signedness
main.c:243: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:244: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:249: warning: pointer targets in assignment differ in signedness
main.c:250: warning: pointer targets in passing argument 1 of 'put_dump' differ in signedness
main.c:254: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:259: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:289: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:290: warning: pointer targets in assignment differ in signedness
main.c:291: warning: pointer targets in passing argument 1 of 'put_dump' differ in signedness
main.c:295: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:296: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:299: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:304: warning: pointer targets in passing argument 1 of 'get_line' differ in signedness
main.c:305: warning: pointer targets in assignment differ in signedness
main.c:308: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:316: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:317: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:318: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:323: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:324: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:325: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:330: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:341: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:393: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:402: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:410: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:426: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:440: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:440: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:478: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:478: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:483: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:483: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:483: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:485: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:485: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:485: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:524: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:524: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:524: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:534: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:536: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:537: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:538: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:539: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
main.c:540: warning: pointer targets in passing argument 2 of 'xatoi' differ in signedness
C:\DOCUME~1\Sander\LOCALS~1\Temp/ccJ6Gurb.s: Assembler messages:
C:\DOCUME~1\Sander\LOCALS~1\Temp/ccJ6Gurb.s:1503: Warning: expression dangerous with linker stubs
C:\DOCUME~1\Sander\LOCALS~1\Temp/ccJ6Gurb.s:1504: Warning: expression dangerous with linker stubs
avr-gcc -gdwarf-2 -Wall -Os -mcall-prologues -mmcu=atmega64 -c -o uart.o uart.c
avr-gcc -c -mmcu=atmega64 -I. -x assembler-with-cpp -Wa,-adhlns=xitoa.lst,-gstabs xitoa.S -o xitoa.o
avr-gcc -gdwarf-2 -Wall -Os -mcall-prologues -mmcu=atmega64 -c -o ff.o ff.c
avr-gcc -gdwarf-2 -Wall -Os -mcall-prologues -mmcu=atmega64 -c -o mmc.o mmc.c
C:\DOCUME~1\Sander\LOCALS~1\Temp/cc7GywOR.s: Assembler messages:
C:\DOCUME~1\Sander\LOCALS~1\Temp/cc7GywOR.s:1291: Warning: expression dangerous with linker stubs
C:\DOCUME~1\Sander\LOCALS~1\Temp/cc7GywOR.s:1292: Warning: expression dangerous with linker stubs
avr-gcc -gdwarf-2 -Wall -Os -mcall-prologues -mmcu=atmega64 -c -o rtc.o rtc.c
avr-gcc -gdwarf-2 -Wall -Os -mcall-prologues -mmcu=atmega64 -Wl,-Map,avr_mmc.map -o avr_mmc.elf main.o uart.o xitoa.o ff.o mmc.o rtc.o
avr-objdump -h -S avr_mmc.elf > avr_mmc.lst
avr-objcopy -j .text -j .data -O ihex avr_mmc.elf avr_mmc.hex
avr-size -C --mcu=atmega64 avr_mmc.elf
AVR Memory Usage
----------------
Device: atmega64

Program: 23910 bytes (36.5% Full)
(.text + .data + .bootloader)

Data: 2494 bytes (60.9% Full)
(.data + .bss + .noinit)

> Process Exit Code: 0
> Time Taken: 00:11

A minor typo in the last line (of the tutorial), I think you mean Makefile_mmc2. BTW, excellent job of writing it up.

Any suggestions to clean up the errors?

Thanks,
davef

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm... strange. I didn't get that kind of error for xitoa.o, but if you're using an older WinAVR, why not upgrade? I just learned that a new version (20080610) came out last month, so maybe this will work right out of the box. In any case, once I get to work I'll check and see what WinAVR I'm using. It's possible there's a mistake in the [TUT], but I don't know what that is because I didn't touch xitoa.S.

P.S. Fixed the typo. Thanks!

P.P.S. It seems you fixed the error, as the output now compiles. What was it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Initially, when I deleted the files that weren't needed I must have deleted xitoa.S
When I put it in the project the .hex file was generated, but with all the above warnings.

I am using WinAvr-20071221. The signedness issues I mentioned happened when I upgraded from a 2006 or 2005 version. I have WinAVR-20080610, I'll give that a try in the next few days.

I found that Tiny-FatFS, as in the source, is not really much smaller. As suggested I would need to eliminate functions to try and get it into an ATmega32. A friend is just in the process of developing a board with the ATmega128 on it. Perhaps good timing!

davef

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I found that Tiny-FatFS, as in the source, is not really much smaller.

I thought it was "tiny" in the sense of reduced RAM usage, not necessarily reduced code space usage.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
AVR Memory Usage
----------------
Device: atmega64

Program: 20470 bytes (31.2% Full)
(.text + .data + .bootloader)

Data: 2914 bytes (71.1% Full)
(.data + .bss + .noinit)

I thought Data was RAM :oops: How can you tell how much RAM it is going to use?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Data in that context IS RAM

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:
I found that Tiny-FatFS, as in the source, is not really much smaller.

I thought it was "tiny" in the sense of reduced RAM usage, not necessarily reduced code space usage.

I think that you have to configure it for read only, minimize, etc... (all in the header file (t)ff.h) in order to really reduce the code base. I'll have to do a couple tests once I get everything going to see what kind of difference it makes. Maybe not enough to shoehorn it into a 16K one, but perhaps enough to run some reasonably big code alongside FatFS on a 32K. Since I'm using a 64K module for some simple data logging, it doesn't really make any difference to me.

==========================================
UPDATE:
==========================================

I just ran a couple experiments with my current code. I don't use have any of the functions that are removed, so this is just the difference in program size for the exact same program. ~4K is quite a big difference, especially if it's the difference between a 32K chip and a 64K one!

_FS_MINIMIZE=0

AVR Memory Usage
----------------
Device: atmega644p

Program: 23726 bytes (36.2% Full)
(.text + .data + .bootloader)

Data: 1687 bytes (41.2% Full)
(.data + .bss + .noinit)

_FS_MINIMIZE=1

AVR Memory Usage
----------------
Device: atmega644p

Program: 21116 bytes (32.2% Full)
(.text + .data + .bootloader)

Data: 1687 bytes (41.2% Full)
(.data + .bss + .noinit)

_FS_MINIMIZE=2

AVR Memory Usage
----------------
Device: atmega644p

Program: 20414 bytes (31.1% Full)
(.text + .data + .bootloader)

Data: 1687 bytes (41.2% Full)
(.data + .bss + .noinit)

_FS_MINIMIZE=3

AVR Memory Usage
----------------
Device: atmega644p

Program: 19682 bytes (30.0% Full)
(.text + .data + .bootloader)

Data: 1687 bytes (41.2% Full)
(.data + .bss + .noinit)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi from where can i get he code to build this amazing project..
this link i not functional http://elm-chan.org/fsw/ff/ffsample.zip

please forward me the file
divyansh@ymail.com
thanx in advance..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi from where can i get he code to build this amazing project..
this link i not functional http://elm-chan.org/fsw/ff/ffsample.zip

please forward me the file
divyansh@ymail.com
thanx in advance..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Pointer to where I can learn about the xitoa function?
I can't find any definition about xitoa on the net. Seems odd, but this SD/MMC FATFS seems to be about the only piece of code that uses xitoa. It's not in the WINAVR library.
Especially this piece of code: !xatoi(&ptr, &p1) what is this doing?

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As near as I can figure, xitoa seems to be Chen's implementation of an eXtended print-to-console API. I quite like it, and I now use it in a lot of other programs. For the most part, you would use it as you would use the normal functions in a C program running in the terminal. There are a couple ones which change functionality somewhat, notably xitoa(), which allows you to print directly to the serial port, instead of first printing to a string buffer and then writing the buffer to the port.

Quote:
Especially this piece of code: !xatoi(&ptr, &p1) what is this doing?

I have no idea what that does. There's some funky pointer-fu that I don't really understand in Chen's work. Of course, maybe with a bit more context it might be easier to grok.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Especially this piece of code: !xatoi(&ptr, &p1) what is this doing?

Well ptr is going to be a pointer to a string of digits such as "0x1F2C" or "0b101010" or whatever and p1 is a pointer to a 'long' variable that will take the result. The code then does a pretty standard atoi() style conversion but if it finds an "illegal" character (such as "01F2C" being passed without the 'x') it returns 0 rather than 1. So all the uses tend to be "if (!xatoi(&ptr, &p1)) break;" so that it doesn't use an illegally converted result.

You can see all this in the asm code in xitoa.S

Cliff

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Folks,
I just started playing around with the FAT FS code.
Seems to compile, but I'm also getting that signedness warning (about 50 times).

Using winavr 20080610

Quote:
main.c:235: warning: pointer targets in passing argument 1 of 'get_line' differ in signedness

Pretty much a noob with c. I sort of get the issue of unsigned versus signed char pointers, but with elm chans extended print routines being in assembler I'm lost. Was there an easy fix found for this error (other then going back to an earlier version of winavr)?

BTW, the -funsigned-char flag has no effect. But I don't know if that would affect pointers.

Thanks,

-carl

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a benign warning - ignore it.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson,
Thanks, as long as it's benign I guess I can ignore it.

In general, I like to get rid of these warnings, especially when there are 40-50 of them. When an additional non-benign warning pops up (as I learn and develop new code) it tends to get lost in the field of all these other ignorable warnings.

I'll check to see if I can suppress this particular warning msg. Additionally, if this is becoming a more general gcc question, I'll take it over to the avr gcc forum.

-carl

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It was one of the recent GCC releases where that over-aggressive warning started to appear. As it doesn't help much and causes a lot of people concern I think moves are afoot to revert the behaviour in future.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

successfully compiled elmchan code..
I am able to read the MBR but not able find files..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

divgup wrote:
successfully compiled elmchan code..
I am able to read the MBR but not able find files..

That could be a million things. Would it have hurt you to have given a bit more information? You are looking for help, after all.

However, if you're able to read the MBR, at least your SD settings work properly, so there's still hope.

What size is the card? Over 2GB and it could have problems. I've heard about some of these new SD-HD cards not following the specs.

Check the filesystem, too. If it's not working with FAT16, try FAT32 and vice-versa.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am sorry but i dont know what is the problem..
I am using 256 MB card of kingston.

I have written two codes in the first one which i learnt after reading this post.Following problem is coming.
If i make two files in the SD-card.Both of them are read upto the end.When i give command to read the file which is located first in the MBR(which i get to know by seeing the SD viewer).

In the second code i was able to
create file.
See a list of files present.
But the code hangs in between.I am not able to find the problem.
I can mail you the .zip file.
I know i am asking for too much but can you please help me in this.
I assure you that there will only be a slight problem in the code.
thanx and regards
divyansh@ymail.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, I'm not really clear on what your problem is. Maybe someone else can read between the lines?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

divgup,

Can you use a utility such as WinHex (http://www.x-ways.net/winhex/) to capture the MBR? Then look at the four byte field at offset 0x1C6 (which is LBA of the start of the first partition) and also take a dump of that sector.

Both these sectors end 55,AA - if they don't you are looking in the wrong place.

Once we can see a copy of the boot/BPB sector it'll be possible to determine where the FATs and root directory are and check those.

Cliff

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there any code that can be used with ATmega8? 32K is too much

I just need to write and read from sd card

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There was a thread recently (last 1-2 months) describing a method of writing a (dummy?) file to the card then writing and reading from it without using a file system.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hammer111 wrote:
Is there any code that can be used with ATmega8? 32K is too much

I just need to write and read from sd card

If you can read german, the following site is something for you.
http://www.holger-klabunde.de/avr/avrboard.htm
Download the file: FATSingleOpt WinAVR 4.1.1
This can run in an ATMega8.
I successfully used it to build a GPS logger

Success,

Paul

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey...

I've been banging my head off the wall trying to get this working...
But no luck.... Using Tiny FS on an ATmega8 @ 8Mhz...

I'm a little confused about this part....

Quote:
Code:
/*SPI configuration*/
#define DD_MOSI DDB5
#define DD_SCK DDB7
#define DDR_SPI DDRB
#define DD_SS 4

/* Defines for SD card SPI access */
#define SD_CS_PIN 1
#define SD_CS_PORT PORTC
#define SD_PWR_PIN 0
#define SD_PWR_PORT PORTC

These defines will all have to be modified to your particular ATmega and setup. The DD_* #defines can be found in the I/O Ports-->Alternate Port Functions section.

The SD_* #defines are up to you. I chose to conenct the SD card's power supply to PIN0 (you did make sure you're not outputting more than 3.5V, right???) chip select to PIN1 of PORTC, but you could chose another if it were more convenient. Just avoid using one of the four pins associated with the SPI port. You can even comment out the SD_PWR_* if you have an alternate power supply (in this case, you have a 3.3V level converter on the SPI I/O pins, right???)

Where and what is chip select... ?
On the card holder, there is a CS pin... I've got it wired to SS on the mega32...
But where does this go?

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

krazatchu wrote:

Where and what is chip select... ?
On the card holder, there is a CS pin... I've got it wired to SS on the mega32...
But where does this go?

Ready for a magic trick? CS is... Chip Select! Ta-da. Thank you, thank you, I'll be here all week.

Explained better, "slave select" can be another name for "chip select". Since all devices on the SPI bus share the same communication lines, there has to be a way to differentiate between them when the master is talking. The master does this by activating the chip/slave select pin on the device it wants to talk to. Any devices who are not being addressed simply ignore the data they see on the bus until such time as their chip select pin is pulled appropriately.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lol...

I should be more specific...
It was this part in particular:

Quote:
chip select to PIN1 of PORTC, but you could chose another if it were more convenient. Just avoid using one of the four pins associated with the SPI port.

I think you mean 3 pins, right? SCK, MOSI, MISO...
There would be no need to avoid using SS on the AVR as CS on the MMC...

I was having much trouble on the weekend getting it to work ...
Part of the problem seemed to be one of the SD cards, A SanDisk Ultra II 2GB...
They seem to have a slightly different initialization sequence...

I did manage to get it working with a SanDisk 256mb card...
But then I think I damaged the MBR, so it will still initialize but it won't access or reformat...

Still trying to get the 2GB card working if anyone has suggestions...
I found this thread and experimented a bit, but no luck yet...

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50889&postdays=0&postorder=asc

Thanks and great tutorial...
Michael

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

krazatchu wrote:
lol...

I should be more specific...
It was this part in particular:

Quote:
chip select to PIN1 of PORTC, but you could chose another if it were more convenient. Just avoid using one of the four pins associated with the SPI port.

I think you mean 3 pins, right? SCK, MOSI, MISO...
There would be no need to avoid using SS on the AVR as CS on the MMC...

You are absolutely correct, but when using SPI I tend to stay away from the SS pin, as if you configure it incorrectly, and then use it, you'll get some really annoying headache bugs that won't make sense. (Basically, it can knock the uC out of master mode and into slave mode, causing communications to fail for no apparent reason.) So it's safer for those of us to lazy or befuddled to make bug-free code on the first try just to leave it alone.

Quote:

I was having much trouble on the weekend getting it to work ...
Part of the problem seemed to be one of the SD cards, A SanDisk Ultra II 2GB...
They seem to have a slightly different initialization sequence...

I did manage to get it working with a SanDisk 256mb card...
But then I think I damaged the MBR, so it will still initialize but it won't access or reformat...

Still trying to get the 2GB card working if anyone has suggestions...
I found this thread and experimented a bit, but no luck yet...

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50889&postdays=0&postorder=asc

Thanks and great tutorial...
Michael

Yeah, some cards can be a little odd, I understand. Even if you damage the MBR, that shouldn't change a thing for you reformatting. That being said, I now have two SD cards that simply don't work anymore with the AVR, but can and do work with a normal card reader. Don't know what could be happening, but obviously it's something in the way the card is being handled. Maybe the voltage supply isn't as clean as I'd like...

Glad you found the tutorial useful. If you look around, you'll find another one I just wrote on using the SD card for a bootloader. That goes pretty well with this one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kubark42 wrote:
but when using SPI I tend to stay away from the SS pin, as if you configure it incorrectly, and then use it, you'll get some really annoying headache bugs that won't make sense. (Basically, it can knock the uC out of master mode and into slave mode, causing communications to fail for no apparent reason.) So it's safer for those of us to lazy or befuddled to make bug-free code on the first try just to leave it alone.

That's not a great idea. The whole point of SS (apart from ensuring that only one slave is enabled at a time) is as a resynchronisation mechanism. SPI works by having 8 pulses on the SCK and data lines but if a spurious clock pulse enters the system or one is not detected then the master and slave can get out of syncronisation. The way to get them back is for the master to de-assert SS (take it high) to the active slave andthen assert it (take it low) before a sequence of byte transfers. At this moment the slave can resynchronise its counter back to the start of an 8 bit frame.

The only "gotcha" to do with SPI on AVRs is that if an AVR is configured as a master and the designated SS pin (which would only be used if it were a slave) happens to get pulled low then the AVR will switch to slave mode operation unexpectedly. The usual solution is either to leave that pin as an input but enable it's pull-up or actually make use of it as the driving line for controlling the distant slave anyway.

I'd recommend anyone using SPI *do* make use of _SS unless they simply cannot afford the pins or tracking space. Avoiding the unexpected Master->Slave transition is easy once you are aware that it can occur and what may cause it.

Cliff

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

The only "gotcha" to do with SPI on AVRs is that if an AVR is configured as a master and the designated SS pin (which would only be used if it were a slave) happens to get pulled low then the AVR will switch to slave mode operation unexpectedly. The usual solution is either to leave that pin as an input but enable it's pull-up or actually make use of it as the driving line for controlling the distant slave anyway.

I'd recommend anyone using SPI *do* make use of _SS unless they simply cannot afford the pins or tracking space. Avoiding the unexpected Master->Slave transition is easy once you are aware that it can occur and what may cause it.

Cliff

Alright, sounds like good advice. In reality, I use the SS pin for controlling the slave (as you say, it's there so might as well use it) but I've seen others advise against it so I thought I'd follow their lead.

Quote:

That's not a great idea. The whole point of SS (apart from ensuring that only one slave is enabled at a time) is as a resynchronisation mechanism. SPI works by having 8 pulses on the SCK and data lines but if a spurious clock pulse enters the system or one is not detected then the master and slave can get out of syncronisation. The way to get them back is for the master to de-assert SS (take it high) to the active slave andthen assert it (take it low) before a sequence of byte transfers. At this moment the slave can resynchronise its counter back to the start of an 8 bit frame.

I didn't know about that. Is this something that happens asynchronously from the uC, or do you have to program that yourself?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's asynchronous within the operation of the SPI logic I believe. When it sees a high->low transition on _SS it resets its internal bit counter.

EDIT: yup, just found this in the mega16 datasheet where it's describing its own use as a slave:

Quote:
The SS pin is useful for packet/byte synchronization to keep the Slave Bit Counter synchronous with the Master Clock generator. When the SS pin is driven high, the SPI Slave will immediately reset the send and receive logic, and drop any partially received data in the Shift Register.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While searching for a fix for my 256mb SanDisk, I found a couple of utilities that are supposed to reformat the SD card according to the SD Spec...

I read that a windows format won't return the drive to the proper SD Specs...

For SD/MMC cards...
http://panasonic.jp/support/global/cs/sd/download/sd_formatter.html

For USB disks...
http://files.extremeoverclocking.com/file.php?f=197

Neither of them worked for me as my 256mb card won't even detect by windows...

I'm going to try the MMC Unlocker project here: http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_id=798&item_type=project But if that doesn't work, I guess this card is dead...

Ciao!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you kubark42 for this comprehensive tutorial. I've now successfully implemented FatFS on an Atmega128. I'm able to communicate with my 2 gb Sandisk SD card. I've one question though. How do I read a file? According to the code in main.c I type

fr 

. What is len then? I guess it's not the name of the file, since that doesn't work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

`len` is the number of bytes you want to read. Usually, you'll want to read/write all a sector at once, so it'll be 512.

Glad to hear it's worked for you! Hope it was clear enough and there weren't too many gotchas. Let me know if you think there's anything that could be improved.

Kenn

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the quick reply. Your tutorial seems to me totally flawless, it's very hard for me to find anything that could be improved. Keep up the good work:).

Marcin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another question ...

I'm trying to create a single file which I append every time I want to add data...

I believe I should be using f_stat to get the file size...
Then f_lseek to set the write position to the end of the file...

However, I'm a bit confused on the implementation...
Here is my broken code ...

FILINFO FileInfo;

........

// Register a work area for logical drive 0
error_code ( f_mount(0, &fs)) ;	
	
// create destination file
error_code ( f_open (&fdst, file_name_id , FA_CREATE_ALWAYS | FA_WRITE) );

f_sync(&fdst); // flushes cached information

// get file info including freespace...
error_code ( f_stat ( file_name_id , FileInfo.fsize   ) ); 

// goto end of file, append 
error_code ( f_lseek (file_name_id,  FileInfo.fsize) ); 

//write file, 32 is buffer size 	
res_write = ( f_write(&fdst, write_file_buffer, 32, &bw) );

I'm sure I'm handling the union structure wrong, and perhaps the declaration...
Please don't laugh at me :< ... it's late....

Thanks!!
Michael

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No need for the f_stat the following is working for me.


FIL   log_file;             // system logfile stdout gets piped here.

...

            res = f_open(&log_file, "log.txt", FA_OPEN_ALWAYS | FA_WRITE);
            if (!res) 
            {
                res = f_lseek(&log_file, log_file.fsize);
                if (res)
                {
                    D(1) fprintf_P(stderr, PSTR("sd_putchar f_lseek failed res 0x%x\n"),res);
                }
            }
            else
            {
                D(1) fprintf_P(stderr, PSTR("sd_putchar f_open failed res 0x%x\n"),res);
            }
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh silly me...

I thought it was looking for the file name on disk... lol

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kingFisher wrote:

If you can read german, the following site is something for you.
http://www.holger-klabunde.de/avr/avrboard.htm
Download the file: FATSingleOpt WinAVR 4.1.1
This can run in an ATMega8.
I successfully used it to build a GPS logger

Success,

Paul

Paul, I downloaded this library and compiled it, but after compilation the hex file is still some 21 kb. I disabled most of the functions but still can't get it smaller than that. Is there any chance you could share your code, I'm trying to get it running on an atmega168.

Thanks in advance

Marcin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've managed to overcome this problem. Been doing a silly mistake. Admin could remove this and my last post.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi bonzoq. i just successfully implemented the tutorial thank to kubark42. bonzoq may i ask what parameter you change when using 128? cause i have 128, and not a 644p.

and hi kubark42, may i ask further tutorial? i a m newbie(VERY,MUCH) on AVR, after compiling it completely...how can i generate the hex file? i did generate it, but by some trails, and now i cant generate it again. I base it on the modified time.
I use: winavr,GCC,programmers notepad
im really new, thanks alot.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To generate a hex file? Programmers Notepad can do it for you in one of the menus. I believe it's "Make all" or something to that effect. However, you should be able to open a command window, go to the local directory, and type "make".

FYI, "make clean" erases all the compiled files so you can recompile with a blank slate. This is sometimes necessary for various reasons. If things don't seem to work, but should, you might try a "make clean" first.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

leoren_tm wrote:
hi bonzoq. i just successfully implemented the tutorial thank to kubark42. bonzoq may i ask what parameter you change when using 128? cause i have 128, and not a 644p.

Hi leoren_tm. you just need to follow kubark's tutorial and change the names of the registers he mentions according to the atmega128's documentation. Don't forget to set the correct MCU_TARGET in the makefile too. I could send you my code, but no sooner than on Thursday CET. Just send my a private message with your e-mail address if you want to.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ohhh NICE..i have the code running. thanks a lot. anyway do i have to set those fuse setting, bootloader(just read those)..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As the Atmega128 and Atmega64 are identical in pins and feature set, do I have to change anything in the example?

I'm having little trouble getting this to work. I bet its my proto board giving me the hard time, but we'll see as I get a new SD-socket today..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Careful. I programmed this for the ATMega644p, NOT the ATMega64. As far as I'm aware, they're not the same processor at all.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kubark42 wrote:
Careful. I programmed this for the ATMega644p, NOT the ATMega64. As far as I'm aware, they're not the same processor at all.

Nononono.. :D

You got me all wrong. The original ELM example is written for the ATMega64 and I was referring to that.

But.. I guess I'll just have to find out for myself.

Pages