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

Go To Last Post
354 posts / 0 new

Pages

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

Surely the first step is to run your AVR at 3.3V.
Connect all the relevant pins of your card reader.
Connect LEDs for Card-detect and Write-protect.

Use the basic disk commands to read sectors. Observe any error returns.

If necessary, put stubs in for lying about card-detect etc.

You can examine the sector contents and just see that they do not contain garbage.

If the disk-commands "di", "dd", "ds" etc work ok, then the FAT commands "fi", "fs", "fl" ... etc should work too. And unlike a lot of other SDCard code, FatFs works fine with FAT16 and FAT32.

David.

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

I adapted it for a MEGA168, using a 10ms timer too, and it works pretty well!
I started from the original AVR sample and I kept the socket insertion and write protection functionnalities etc. I remapped pins to my pins, I commented out everything about RTC. I took the UART souce from the OP.

For timer2 I use this at 16MHz, it's not exact but for a test, it's ok:

	OCR2A = 156-1;		// Timer2: 100Hz interval (OC2)
	TCCR2A = _BV(WGM21);                // CTC mode (clear timer on compare match)
	TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);     // clock/1024

	TIMSK2 = _BV(OCIE2A);	// Enable TC2.oc, interrupt

For SPI speed I used:

// 16MHz / 64 = 250K, in the slow clock range
#define   FCLK_SLOW()      (SPCR |=  _BV(SPR1))      /* Set slow clock (100k-400k) */
#define   FCLK_FAST()      (SPCR &= ~_BV(SPR1))      /* Set fast clock (depends on the CSD) */

My 168 is running at 5V, the SD at 3.3V, and to communicate with the card (a lexar 2GB) I use voltage divider on SCK, MOSI, SS. I can read/write SDHC/FAT32. (I changed the main to do some open/write/close then some open/read/close etc and output some debug on the serial port, because the "monitor" is too big for my 168)

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

Thank you for the great feedback.

What I can see is that the disk_initialize() routine returns error (1). Does this mean that something is wrong with the hardware (pin mapping, clock settings etc.) or it can also be a problem with the way the card is formatted on a PC (FAT or FAT32 FS, cluster size etc.)?

I have a code example that works without using FATFS, and I am using the same pin mapping, so I am not sure if I have a pure hardware problem...

Regards,
P

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

You are using a CAN128, so you should be very similar to the mega64/mega128 sample code.

Do you have an LED on the CD output. So you can verify the Card detect input when you insert/remove card.
Basically, if you have the SPI lines + CS + CD lines connected correctly then disk_initialise() should be ok.

I would suggest just making a fresh start with unzipping the project. If you can match the wiring exactly then the only change is 'mcu=at90can128' in the Makefile.

David.

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

david.prentice wrote:
If you can match the wiring exactly then the only change is 'mcu=at90can128' in the Makefile.
Not correct in this case. Read the migration document:

AVR096: Migrating from ATmega128 to AT90CAN128
http://www.atmel.com/dyn/resourc...

migration document wrote:
The AT90CAN128 is functionally pin compatible with ATmega128. Certain pins have been upgraded with regard to their associated alternate functions, the change of the timers/counters index and the voluntary removal ATmega103 compatibility.

There are some important changes like the synchronous and aynchronous timer0 and timer2 register names are swapped. The pins are in the same place, its just program code gets messed up when switching between these chips.

The ATmega128 SFIOR register bits were split up and moved into AT90CAN128 GTCCR, ADCSRB and MCUCR registers. MCUCR and MCUCSR are changed and so on. Some bit locations have moved, so program code that used numeric constants to manipulate bits in these registers, rather than using the individual bit names is another problem. For example: the mega128 SM bit in MCUCR=0x20 would become 90CAN SMCR=0x01, while (1 << SM) would still be OK when changed to the correct register. At least cases where both the register changed and the bit position changed are rare.

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

From memory, there is one 100Hz timer IRQ and one SPI function.
Also from memory, the code uses the relevant bit names. So the code will compile correctly regardless of where the sfr's live in the AVR. MOSI, MISO, SCK use the same physical pins. The SS, Card-detect, LED-indicators are all just regular GPIO.

I do not have a CAN128. But I am sure that ratep2001 has already checked the availability of these SPI and GPIO pins on his particular board.

David.

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

I have just solved the problem with the disk_initialize() error - it was indeed a hardware problem.

Since my CS pin is on the PORTG, blindly replacing the code from the tutorial and believing the power_on() routine was platform independet (with CS on PORTC) gave me the problem in the power_on() routine. Maybe a good idea is to define new DDR_CS in the pin mapping section and use it in all other routines, like power_on, to avoid any platform dependent definitions further on.

Now I get error 9 - INVALID OBJECT as FRESULT from the function f_readdir. What can be wrong?

Since I place all my files in the root, I define *ptr=' ' in the f_opendir function. Maybe this gives me a wrong dir object? Can I completely skip f_openddir if I am working in the root? If Yes, what do I replace &dir from f_readdir() with?

Regards,
P

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

You should get good results for "di 0", "ds 0", "dd 0 123".
Try "fi 0", "fs", "fl".
Then "fg directory", "fl"

The default "ff.h" and "ff.c" should work out of the box. You have probably only altered "rtc.c" and "mmc.c"

Go through all the commands in "main.c" to exercise the filesystems. Try reading and writing files, making directories etc.

You should make no alterations to "ff.c" at all. If you want to see how to use f_opendir() then look at how it is exercised in "main.c".

David.

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

I am not using the uart console, so I cannot send the commands di, fi and fl. In the main.c, I put just a very short version of the original code:

// Initialize disk #0
di_res = disk_initialize((BYTE)0);

// File-system initialize - mount the drive #0
fi_res = f_mount((BYTE)0, &Fatfs[0]);

// Directory listing

*ptr = ' '; // No directories used - all files in root
/* MMC - SD card Directory listing */
// while (*ptr == ' ') ptr++;
res = f_opendir(&dir, ptr);

Why do I get error nr. 5 (FR_NO_PATH) as a result of f_opendir? How do I refer to the root in the f_opendir command to create the directory object?

I tried also to list the directory called "direct", by defining *ptr = "direct", and I got the same error.

Can it be that now the problem is with the SD card itself (wrong FAT FS when formatted on PC, bad card, wrong size / brand issues etc.)?

Regards,
P

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

Since your principal problem is getting FatFS up and running on your hardware, it is best to keep the test program as near as possible to the one in the distribution. e.g. it is not difficult to wire up a UART or character LCD.

you have:

Quote:

*ptr = ' '; // No directories used - all files in root 

Since when have you come across a directory with a leading space character and more importantly pointing to nowhere in particular?

If you want to pass an empty string:

ptr = "";    // note you are pointing to a NUL character.

You only have to think of common operations. 'ls' or 'ls directory'. The former is effectively specifying an empty string.

David.

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

Quote:

Can it be that now the problem is with the SD card itself (wrong FAT FS when formatted on PC, bad card, wrong size / brand issues etc.)?

Download and install WinHex on your PC it is invaluable for checking FAT structured storage media.

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

Great write up. Very helpful indeed. I was able to get the whole thing up and running on an atmega128 in half a day.

Mike

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

Good for you, Mike. I guess you are running the original application, as in the tutorial.

I have still troubles with my own program, where I just use f_opendir and f_readdir. Both the disk initialization and file initialization go smoothly, their result is 0.

However, I get error nr.5 on f_opendir and error nr.9 on f_readdir. I have both tried to access the root with *ptr = "" and a directory with *ptr = 'xxx', in both cases I get the same errors.

I installed the WinHex and checked the SD card with it. I must admit I don't know whether there is something specific I need to look for, but I can see the same folder and file structure as in Windows Explorer.

Should I dig into f_opendir and debug line after line while checking whether the variables get the same values as in WinHex?

Regards,
P.

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

Well the errors you are getting are here:

typedef enum {
	FR_OK = 0,			/* 0 */
	FR_DISK_ERR,		/* 1 */
	FR_INT_ERR,			/* 2 */
	FR_NOT_READY,		/* 3 */
	FR_NO_FILE,			/* 4 */
	FR_NO_PATH,			/* 5 */
	FR_INVALID_NAME,	/* 6 */
	FR_DENIED,			/* 7 */
	FR_EXIST,			/* 8 */
	FR_INVALID_OBJECT,	/* 9 */
	FR_WRITE_PROTECTED,	/* 10 */
	FR_INVALID_DRIVE,	/* 11 */
	FR_NOT_ENABLED,		/* 12 */
	FR_NO_FILESYSTEM,	/* 13 */
	FR_MKFS_ABORTED,	/* 14 */
	FR_TIMEOUT			/* 15 */
} FRESULT;

So the opendir() is returning FR_NO_PATH and the readdir() is FR_INVALID_OBJECT (it doesn't make sense to read a dir if it cannot be opened so I guess this is expected).

Sounds like it may be the way you specify the path to opendir() that is invalid. Is it an LFN and do you have LFN support enabled?

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

Quote:

However, I get error nr.5 on f_opendir and error nr.9 on f_readdir. I have both tried to access the root with *ptr = "" and a directory with *ptr = 'xxx', in both cases I get the same errors.

You cannot be serious. You should use:

ptr = "";
ptr = "valid_directory_name";

It is a simple matter of assignment of a pointer. The Compiler should definitely barf at both of your syntax examples.

And of course you should have compiled with support for directories.

David.

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

(I'm on the road and responding from memory)

I noticed that I got "FR_NO_PATH" when the file I was trying to open was in the top level directory, i.e. no directory structure. It didn't seem to matter in my case.

I got "FR_INVALID_OBJECT" a few times. Forgive me if these are bogus leads, but they are easy to check.
* Did you specify a sensible parameter (like 0) during the disk init process? In my test program initially I erroneously defaulted it and presumably submitted junk which it quietly accepted.
* Ditto for the fs init process.
* One of the above processes sometimes seemed to take several seconds to finish, and I may not have waited before moving on.
* Make sure you have not left a file open.
* Make sure that you can read/write the card under windows. I wrote a few files onto the card so I had something to see in the test program.

Apropos nothing.... I now have the code working on a mega128 and a mega644 and about to repeat for a 2561. The major challenge was figuring out all the timer parameters and traps. The interrupt-driven usart code worked like a charm once I had it pointed at the right usart :-)

Also I'm using a micro-SD card socket which doesn't have a write-permit switch, so I had to compensate for that in the code.

Finally, I seem to recall that the status byte format is at least partially dependent on the bit positions of the write-permit, and card-detect signals in the associated hardware register. If (as in my case) those signals are wired up to different registers there is an opportunity for conflict.

Sorry I can't be of more help. Happy to help more when I get back home.

Mike

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

Thank you for all your great ideas, but the problem was, like David pointed out, in the wrong definition of the pointer ptr. Compiler didn't complain though, if it is one way or another...

Now I am ready to crunch those numbers within the files. I hope there will be no more reasons to stumble...

Regards,
P

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

If the Compiler did not complain then you should look very carefully at the compiler flags.

Do a 'make -n' or copy-paste the Studio build output. Then compare with a virgin build from a virgin project.

I can see that

*ptr = '\0';    // legal but not what you want
*ptr = 'K';     // ditto
*ptr = "";      // ILLEGAL
*ptr = 'xxxx';  // ILLEGAL
ptr = "";       // point to an empty string
ptr = "valid_directory_name";

David.

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

Hi, I am trying to set up the demo but do not have the ability to use the USART on my controller at the moment but do have a working LCD for display.

Looking at the demo code, I see there are several steps to follow before you can do an 'ls' type command but I cannot understand what the arguments are. I have a single SD card, 1 partition and only files in root atm. What are the functions and arguments I need to get FatFS up and running?

So far I am guessing:

disk_initialize with NULL for argument?
f_mount with no clue on the arguments

then I should be able to call:

f_opendir again no idea on arguments

The xitoa functions are really throwing me for a loop. I understand what each does but not what they do together in the case statement set of main.c

In the end I just want to be able to see:

Quote:
disk init: OK
f mount: OK
showing root dir...
file1.txt
file2.txt
dir1
dir2
...

on my LCD screen without having to spoon feed it commands over USART (which I do not have available atm)

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

It is always easiest to make as few changes as possible when porting / adapting some code.

Just adapt the USART calls to the equivalent LCD calls. e.g. uart_init() -> lcd_init(), uart_putc()-> lcd_putc() ...

Remember to transform a '\n' linefeed on a UART to equivalent LCD control sequences.

I use a similar lcd2uart.c set of functions to run someone else's LCD code on a UART.

But for your eventual application, you need all the disk_xxx() functions and as many ff_xxxx() functions as needed.

David.

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

david.prentice wrote:
It is always easiest to make as few changes as possible when porting / adapting some code.

Just adapt the USART calls to the equivalent LCD calls. e.g. uart_init() -> lcd_init(), uart_putc()-> lcd_putc() ...

Remember to transform a '\n' linefeed on a UART to equivalent LCD control sequences.

I use a similar lcd2uart.c set of functions to run someone else's LCD code on a UART.

But for your eventual application, you need all the disk_xxx() functions and as many ff_xxxx() functions as needed.

David.

Ok but I am trying to understand the arguments passed to these functions. For instance looking at the init function for the fat (using cmd 'di') the code is:

if(!xatoi(&ptr, &p1)) break;
disk_initialize((BYTE)p1); 

What is p1 in this case? I assume 0 for most cases as p1 is the disk number I think.

Again with f_mount:

if (!xatoi(&ptr, &p1)) break;
put_rc(f_mount((BYTE)p1, &Fatfs[p1])); 

Same thing with p1 here.

The file list command is even more confusing but that's OK for now I guess. At this point though I should be able to call the f_open command to blindly open a file and read from it.

I realize this tutorial is designed to show HOW to use the FatFS example code and I am happy to use it as such but I would really like to know what to do to get to the point where I can say

f_open(my_file_ptr, "data.txt", FA_READ);

and read from data.txt.

I know I need to run the above commands but am unsure of the arguments right now.

EDIT: Had parameters on code fragment incorrect.

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

Hello

I got my MP3-Player working with a TinyFS-project from a friend. So now, i wanted to integrate the latest FatFS-Version (which has Tiny-Mode being enabled per config-header).
So i tried this, it compiled, but when i programmed my Mega32, i got this error:

Getting isp parameter.. SD=0x01 .. OKOK
Reading FLASH input file.. OK
Entering programming mode.. OK!
Erasing device.. OK!
Programming FLASH .. OK!
Reading FLASH .. OK!
WARNING: FLASH byte address 0x0D5A is 0x00 (should be 0x80).. FAILED!
Leaving programming mode.. OK!

And the address value is changes with every programming.

If i use the old .hex-file, there's no problem.

Very strange...

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

Has the code burst 32K?

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

you mean, that it got above 32K?
No i don't think so, because AVR-Studio tells me:

Device: atmega32

Program: 10270 bytes (31.3% Full)
(.text + .data + .bootloader)

Data: 1836 bytes (89.6% Full)
(.data + .bss + .noinit)

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

Ha! I could solve the problem. I forgot to copy the Makefile_mmc in my new project.
Now, everything works fine.

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

No Shit!
I still had the old working project opened.

So with the new FatFs and the Makefile added it still doesn't work. Damn!

Any Ideas?

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

My compiler gives a warning "../main.c:64: warning: implicit declaration of function 'disk_timerproc'
" when I am trying to call this function in my timer interrupt. What is this?

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

My main.c has:

void disk_timerproc (void); // in mmc.c

and

/*---------------------------------------------------------*/
/* 100Hz timer interrupt generated by OC2                  */
/*---------------------------------------------------------*/


ISR(TIMER2_COMP_vect)
{
	Timer++;			/* Performance counter for this module */
	disk_timerproc();	/* Drive timer procedure of low level disk I/O module */
}

then mmc.c has:

/*-----------------------------------------------------------------------*/
/* Device Timer Interrupt Procedure  (Platform dependent)                */
/*-----------------------------------------------------------------------*/
/* This function must be called in period of 10ms                        */

void disk_timerproc (void)
{
	static BYTE pv;
	BYTE n, s;


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

Does your code not have these?

(at the very least it sounds like your main.c doesn't have the declaration)

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

MasterBate wrote:
No Shit!
I still had the old working project opened.

So with the new FatFs and the Makefile added it still doesn't work. Damn!

Any Ideas?

Try disconnecting your SD card while programming. I was getting the same error using an STK500 with the card connected.

On a different note, I've been troubleshooting this code on an ATMega32 and so far no luck. It seems like disk init and mounting are successful - both 'di 0' and 'fi 0' return rc=0. 'ds' returns

Sector size: 512
Card type: 2
OCR:
00000000 01 FE 00 03 ....

However, when I try other commands, e.g. fs or fl, I get rc=1 FR_DISK_ERR. I managed to track this down to disk_read() in mmc.c. When the CMD17 command is sent to read a single block of data, the card seems to acknowledge it correctly. But when rcvr_datablock is invoked to actually read the data, it returns an error due to an incorrect token sent by the card - correct token is 0xFE, but I see 0x01. Thus, I cannot receive any data from the card (which is a 2gb kingston). Anyone had similar problems? Help greatly appreciated.

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

Tried a different card - Cannon 16Meg SD - same problem...

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

Zora53 wrote:
MasterBate wrote:
No Shit!
I still had the old working project opened.

So with the new FatFs and the Makefile added it still doesn't work. Damn!

Any Ideas?

Try disconnecting your SD card while programming. I was getting the same error using an STK500 with the card connected.

On a different note, I've been troubleshooting this code on an ATMega32 and so far no luck. It seems like disk init and mounting are successful - both 'di 0' and 'fi 0' return rc=0. 'ds' returns

Sector size: 512
Card type: 2
OCR:
00000000 01 FE 00 03 ....

However, when I try other commands, e.g. fs or fl, I get rc=1 FR_DISK_ERR. I managed to track this down to disk_read() in mmc.c. When the CMD17 command is sent to read a single block of data, the card seems to acknowledge it correctly. But when rcvr_datablock is invoked to actually read the data, it returns an error due to an incorrect token sent by the card - correct token is 0xFE, but I see 0x01. Thus, I cannot receive any data from the card (which is a 2gb kingston). Anyone had similar problems? Help greatly appreciated.

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

I solved my problem, although I still can't explain why things weren't working in the first place. All I did was remove my SD card from the breadboard and connect it directly to the port header on the STK500, so now I just have 6 wires going from the STK to the card's breakout board. Everything else is exactly the same. Can anyone explain how the breadboard could have messed up SPI comms with the SD card?

Also I forgot to mention earlier that one of the symptoms I had when the card was malfunctioning was that instead of the 0xFE byte at the beginning of a data block in the card's response, I would always get 0xFC.

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

Maybe you were picking up noise on the longer lines? I don't know how the current Chen handles speed. It could be that the initial conversation is at a lower speed and then it tries to transfer data at a higher speed. At the higher speed, the noise from longer lines might have been enough to disrupt the communication.

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

kubark42 wrote:
Maybe you were picking up noise on the longer lines? I don't know how the current Chen handles speed. It could be that the initial conversation is at a lower speed and then it tries to transfer data at a higher speed. At the higher speed, the noise from longer lines might have been enough to disrupt the communication.

I tried different SPI clock speeds, all the way down to 25kHz and it didn't make a difference. But yes, it seems like there had to be noise somewhere.

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

I have implemented the console program from Chen and modified it for my test setup. I have benefited greatly from the many others who preceded me, and heeded the cautions & caveats. Things seem to be basically working- I can mount & initialize the SD card, and read files pre-written elsewhere. The question is, has anyone done a 'magic decoder ring' for the console program to make it easier to use? I can't find a user's manual, or function description manual anywhere. I'm looking for things like file mode arguments for the f_open() and f_write() functions. For instance, things seem to work if I use 0x01 (read only) as the mode argument for fo (file open), but I'm not sure if other flags & descriptors need to be or'ed in. This will be even more important for writing files. I'm willing to wade through the ff.c and ff.h files if I have to but of course I'd prefer the easy way.

I would like to get a solid grasp of the demo program, next step is to go right to the functions to open, write, append etc as required by the application. Sorry if this is in the wrong place- it's not really a reply to the previous post but it's not a new topic either. Feel free to advise as to proper etiquette- it's my first time (be gentle...).

Thanks,
Harry

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

Quote:

This will be even more important for writing files. I'm willing to wade through the ff.c and ff.h files

You can't expect to use a lib without reading at least the .h file - that's what (should!) document the API. If you are lucky there'll maybe also be separate documentation (though often this is simply Doxygen extracted from the .h in human readable form)

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

clawson wrote:
Quote:

This will be even more important for writing files. I'm willing to wade through the ff.c and ff.h files

You can't expect to use a lib without reading at least the .h file - that's what (should!) document the API. If you are lucky there'll maybe also be separate documentation (though often this is simply Doxygen extracted from the .h in human readable form)

I disagree with that. While that might be standard practice, it is in fact more of a sign of laziness than good design. When I bake a cake, I don't mix in the ingredient list with the ingredients.

Doxygen is a bastard solution that doesn't really work except when you already are very clear in what you're doing. It messes up the code royally by adding lots of garbage to a file that should otherwise be clear.

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

hsieber wrote:
I have implemented the console program from Chen and modified it for my test setup. I have benefited greatly from the many others who preceded me, and heeded the cautions & caveats. Things seem to be basically working- I can mount & initialize the SD card, and read files pre-written elsewhere. The question is, has anyone done a 'magic decoder ring' for the console program to make it easier to use?

No, it's not something I ever did. What I always did was take the snippets of code that I saw in Chen and modified them to suit my needs. This is really easy with a debugger, since you can follow the code through step by step and see what functions really do the work.

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

Quote:

While that might be standard practice, it is in fact more of a sign of laziness than good design.

But it IS standard practice, so those of us who use libs have to get jiggy with it. You or I may not like it and prefer a proper .doc to describe the API but this is the real world.

I'd far sooner the programmer at least stop to put as much documentation as possible in the .h at least rather than leaving things unsaid!

The AVR-LibC is a good example (I think) that shows that the technique can work well. Almost the entire library reference at:

http://www.nongnu.org/avr-libc/u...

is Doxygen extracted from the .h's and it reads very well. I'm more than happy with that HTML documenation.

The upshot of Doxygen annotation is that it's FAR more likely that the manual remains in step with the actual functionality offered. It's far to easy for .h/.c to be changed and the programmer to forget to update a separate .doc otherwise.

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

clawson wrote:
Quote:

The upshot of Doxygen annotation is that it's FAR more likely that the manual remains in step with the actual functionality offered. It's far to easy for .h/.c to be changed and the programmer to forget to update a separate .doc otherwise.

Yup, yup. Wasn't it Churchill who quipped "It has been said that democracy is the worst form of government except all the others that have been tried."?

Still, I'd rather pretty much anything than doxygen. But I do have to admit that the AVR-LibC manual looks pretty good, in fact spectacular, compared to all other doxygen outputs I've seen. I wonder what they do differently?

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

Thanks for the responses. I didn't mean to imply that I wouldn't take the time to go through the code if necessary; in fact I had to delve through f_write() to create_chain() to get_fat() to move_window() to disk_write() to find the source of a pesky FR_DISK_ERR during writes, only to find that it was all because the SCLK was too fast (looked more like a sine wave than a square wave). And yes, I did read through all 10 pages of this thread and I did see where others had SPI clock speed issues. Thought I had implemented the FCLK_SLOW & _FAST macros but I hadn't. Oddly, this problem didn't seem to affect reads, just writes. Moral- start with the basics & work up, not vice versa.

Also I found Mr. Chan's very nice 'FAT File System Module' description online- everything one needs to know to use the functions.

Thanks again,
Harry

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

kubark42 wrote:
Still, I'd rather pretty much anything than doxygen. But I do have to admit that the AVR-LibC manual looks pretty good, in fact spectacular, compared to all other doxygen outputs I've seen. I wonder what they do differently?
Isn't that obvious? People knowing how to use doxygen, and time invested in writing the doxygen documentation.

It is the old, simple thing: No tool in the world can convert sh*it into gold, garbage in, garbage out. So if you want non-garbage out, you have to put non-garbage in.

Stealing Proteus doesn't make you an engineer.

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

First of all, I would like to thank everybody (and especially kubark42) for all their help. I have finally gotten this working on Atmega644P - the comments were especially invaluable.

A few discoveries:
1. I did require an internal pullup. I've experimented with taking out the pullup code, however, the disk I/O initialisation then consistently fails.
2. I was able to use the SS as chip select.

Hence, SPI code resembled as follows:
mmc.c (or diskio.c) initialisation

#define DD_SS	DDB4
#define DD_MOSI	DDB5
#define DD_MISO	DDB6
#define DD_SCK	DDB7
	
#define SELECT()	PORTB &= ~(1<<DD_SS)
#define	DESELECT()	PORTB |= (1<<DD_SS)

mmc.c (or diskio.c) power on

DDRB = (1<<DD_SS)|(1<<DD_MOSI)|(1<<DD_SCK);
PORTB = (1<<DD_SS)|(1<<DD_MISO);
SPCR = (1<<SPE)|(1<<MSTR);

I have one question regarding your tutorial, and it's specifically regarding how you set up the timer. You did:

OCR2A = 90-1;
TCCR2B = 0b00000101;

Now, if you're using the 8Mhz internal oscillator, then the values in TCCR2B result in the clock divided by 128 - resulting in essentially a 65.5Khz timer. You then count to 89 which further divides the 65.5Khz timer by 89 resulting in a 702Hz signal. But the requirement is for a 100Hz timer. Am I missing something?
My timer initilisation was as follows:

TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20);
OCR2A = 78;

I was thus dividing by the clock by 1024 and then further dividing by 78. 8Mhz/(1024*78) = ~100Hz.
I'm an AVR newbie, so any explanation for the above lines will be very helpful.

Thanks once again!

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

If you are running of the internal 8MHz oscillator.( also in anyother case)
what did you have set your clok/8 fuse to? if it is set then you have to divide by an additional factor of 8 giving you roughly 100Hz)

regards

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

Ah, that might explain it. My CLKDIV8 fuse is not set. Thanks for clearing that up meslomp.

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

Quote:

You then count to 89 which further divides the 65.5Khz timer by 89 resulting in a 702Hz signal.

Just to note that when a timer is run in CTC mode the OCR is always set to "desired count - 1" because the counting sequence includes 0. So a setting of 90-1 (or 89) is actually counting 90 ticks, not 89. So the calculation would be 694.4Hz, not 702Hz.

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

Maybe a helpful hint for those porting this to xmega...

In mmc..c you'll probably notice on your own that you need to change rcvr_spi() to check SPIC.STATUS bit 7 instead of SPIC>DATA bit 7.

At least for me, it was too easy to miss the equivalent change in the marcro, rcvr_spi_m.

Many, many thanks to kbark42, to ChaN, and to all those who have provided helpful input to this tutorial.

Steve

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

has anyone tried this link?

http://www.dharmanitech.com/2009...

Does it work?

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

Quote:

has anyone tried this link?

It might well work but what confidence would you have in software that took until V2.0 to correct something as fundamental as using 8+3 rather than 7+3. Or how about that only in the latest (2.3) code was the following fault fixed:
Quote:
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

The subject of this thread is FatFs and the reason the majority of people choose to use it is because it's well written, highly developed, extensively tested code and, because it's used by 1,000's of people, there is a LOT of support for it (such as this very thread). If you used the software in your link where could you hope to find support for it?

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

Hello, I'm also trying to integrate this into my Xmega project and I'm running into following problem when I try to compile I get this :

../FF/rtc.h:12: error: expected ')' before '*' token
../FF/rtc.h:12: error: expected ')' before numeric constant
../FF/rtc.h:18: error: expected declaration specifiers or '...' before '(' token
../FF/rtc.h:19: error: expected ')' before numeric constant

and I don't know why. Has anyone else seen this before or can explain when and why this kind of errors occur ?

the rtc.h looks like this :

#include "integer.h"

typedef struct {
	WORD	year;	// 2000..2099
	BYTE	month;	// 1..12 
	BYTE	mday;	// 1.. 31
	BYTE	wday;	// 1..7 
	BYTE	hour;	// 0..23 
	BYTE	min;	// 0..59 
	BYTE	sec;	// 0..59 
line 12 }RTC;

//int iic_write (BYTE, UINT, UINT, const void*);	// Write to IIC device 
//int iic_read (BYTE, UINT, UINT, void*);		// Read from IIC device 

//int rtc_init (void);						// Initialize RTC
line 18 int rtc_gettime (RTC*);					// Get time 
line 19 int rtc_settime (const RTC*);				// Set time 

and rtc.c like this :

#include 
#include 
#include "rtc.h"


int rtc_gettime (RTC *rtc)
{
/*
	BYTE buf[8];


	if (!iic_read(0xD0, 0, 7, buf)) return 0;

	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->wday = (buf[2] & 0x07);
	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;
*/
    rtc->sec = 1;
	rtc->min = 2;
	rtc->hour = 3;
	rtc->wday = 4;
	rtc->mday = 5;
	rtc->month = 6;
	rtc->year = 2007;
	return 1;
}




int 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] = rtc->wday & 7;
	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;

//	return iic_write(0xD0, 0, 7, buf);
	return TRUE;
}

Thank you very much.
Mat

Last Edited: Tue. Oct 5, 2010 - 07:00 AM

Pages