We have FAT

Debug C:UAVmain.c:23 - Initialising block buffers...
Debug C:UAVmain.c:27 - Initialising magnetometre...
Debug C:UAVmain.c:32 - Initialising physical media...
Debug C:UAVDiskPhysicalMedia.c:83 - Ensuring SD card is initialised...
Debug C:UAVSDMemoryCard.c:336 - Initialising SPI...
Debug C:UAVSDMemoryCard.c:341 - Initialising SD card...
Debug C:UAVSDMemoryCard.c:349 - Starting card up...................
Debug C:UAVSDMemoryCard.c:361 - Waiting for card to boot....
Debug C:UAVSDMemoryCard.c:380 - Telling card to initialise itself.....
Debug C:UAVSDMemoryCard.c:393 - SD card initialised.
Debug C:UAVDiskPhysicalMedia.c:88 - Enabling CRCs on command packets...
Debug C:UAVDiskPhysicalMedia.c:93 - Setting initial block length to 512...
Debug C:UAVmain.c:37 - Initialising volume interface...
Debug C:UAVmain.c:42 - Initialising standard I/O...
Debug C:UAVmain.c:49 - Opening file...
ERROR C:UAVSDMemoryCard.c:683 - CRC of read data failed (residual was 0xd183).
ERROR C:UAVSDMemoryCard.c:811 - Unable to retrieve card's CSD, error #3 (EIO).
ERROR C:UAVDiskPhysicalMedia.c:145 - Unable to determine capacity of MMC/SD card, error #3 (EIO).
Debug C:UAVDiskVolumeInterface.c:139 - Successfully opened volume starting at logical address 0 (0x0), length 0 (0x0), suggestedFormat 0 (actual format 3).
Debug C:UAVmain.c:58 - Reading from file...
#include <eZ8.h>
#include <stdio.h>
#include <defines.h>

#ifndef PRJ_IO_H
#define PRJ_IO_H

#define CLOCK_FREQUENCY 18432000UL

void initIO(void);

unsigned char getButtons(unsigned char *raw);
unsigned char getAudioIn(void);

void putAudioOut(unsigned char value);
void setInputVolume(unsigned char volume);
void setOutputVolume(unsigned char volume);
void setBaudRate(unsigned long bitsPerSecond);
void setTransmit(BOOL on);
void setReceive(BOOL on);
void setBacklight(unsigned char brightness);

Debug C:UAVmain.c:65 - Closing file...
Debug C:UAVmain.c:88 - End.

Let’s look at what it’s taken to get to this point. I’ve been working on this for most of today and yesterday afternoon… probably nearly 20 hours in total, if you count the hours wasted trying to build a 25- to 9-pin serial converter (photos here, and no, it never did work). I did take some time off for meals and a little bit of sleep, and a few episodes of Andromeda, and even a brief game of Starcraft which was made all the briefer by it unceremoniously crashing when I started my main attack. It does that. It’s very frustrating.

Anyway, good thing I have my trusty CVS repository handy to tell me exactly what I had to fix to get everything working. From the CVS logs (edited for readability):

  • Replaced “const” with “CONST” in various places where we mean “I won’t change this string”, as opposed to “this is in Flash”, which on the AVR disappears so that we don’t inadvertantly start using Flash instead of SRAM. This was breaking the CRC functions, among other things.
  • Fixed CRC16 polynomial – was erroneously 0x11011, should be 0x11021. This was causing the CRC16 on data reads to fail, even though the data was clearly valid.
  • Adjusted PERR() (our printf-style logging function) for compatibility with the ICC AVR7 compiler bug where 8-bit arguments are placed on the stack as 16-bit, so we need to pop them off as 16-bit and just ignore the high byte.
  • PERR(): Negative ints are now printed correctly, rather than being off by one (was negating, not changing the sign).
  • PERR(): %c, %% and %<unknown> (and %s/%S, when the parameter is NULL) now return to PMBase mode, so they actually work now.
  • PERR(): Increased logical-AND constant from 16-bit to 32-bit for hex output, so 32-bit values are now supported (previously it just printed the bottom 16-bits).
  • PERR(): Hardware flow control now reads CTS from the PIND register instead of PORTD. Apparently this is supposed to work better (the original approach is apparently not meant to work at all, despite the substantial evidence to the contrary).
  • Fixed logging under PC_SIMULATION; forgot that __func__ transforms into a variable reference, not a static string.
  • Got the magnetometre code compiling and it’s init function working, just to be sure the magnetometre CS is inactive and thus not interfering with the MMC.
  • Removed ()’s from around macro definitions, because the ICC AVR7 compiler #%@! itself when it sees them.
  • Fixed MMC CS bit – it’s on PC1, which is the *second* bit, so we should be using 0x02, not 0x01, when dealing with it on port C.
  • Now correctly construct MMC packet… no idea what I was thinking before. (elaborate, I was setting the wrong bits to the wrong things… it was just spastic)
  • Rejigged loop in SDInitialise() sending SD_Command_GoIdle to handle write timeouts properly, and not consider the response unless 0 == err (otherwise we end up exiting prematurely, because response is garbage if 0 != err).
  • Now call initIntervalCounter() before trying to use it.
  • Okay… so, SPI SS is active low for a start, and in any case logical-anythinging 0x00 to a value is useless… whoops. (elaborate: I had written “PORTB |= 0x00;”… brilliant)
  • Now set the SPI SS line as an output and assert it always low, to prevent write collisions and subsequent very nasty behaviour (livelock). (elaborate: and I screwed it up anyway; see the previous point)
  • SPI: Added in various junk (commented out at present) that may or may not be helpful or necessary in future; particular macros to disable code compression temporarily. Although in testing they don’t work anyway.
  • Ported my CRC test driver to work on the AVR, which largely meant moving stuff around to satisfy the stupid ICC AVR7 compiler.
  • In _allocateBlock() we now scrub the newly allocated block if the data read fails, thus preventing it inadvertantly being used nonetheless if a subsequent call is made (which, prior to this fix, was picking up the previous, failed block because it still had it’s logicalAddress set). This was a problem for both USE_MALLOC and !USE_MALLOC versions; both are fixed.
  • Now skip the first pass of the driver interrogation loop if the suggested format is unknown (which is what happens anyway, but now we’re more efficient).
  • Fixed a subtle bug in the driver interrogation loop whereby it was skipping all entries which actually had entry points. This worked with the current setup because we only have three entries, all for the FAT driver, so even if the first was skipped, the second would pick it up.

Those are just the direct bugs… I spent a fair bit of time tweaking other bits and pieces too. I’m not noting them here to be pompous; indeed, careful scrutiny will reveal that most are my own fault (although closely followed in frequency by ICC AVR7 bugs).

I’m surprised that it took this much effort to get things working… I thought it would take a solid few hours, and then I’d get on to more scrutinising tests of the FAT driver itself. I had no idea it would take me the better part of a full day just to get the basic MMC/SD interface working.

But now that it is, things are going well. Since I’ve thoroughly tested the FAT driver in what I call “PC simulation” (i.e. on my Mac), I’m confident it works – indeed, once I got MMC data reading working, everything else followed perfectly the very first time. If I’d tried to develop the FAT driver only the AVR, it’d take me a month to get it working, at least.

And of course, it’s worth mentioning at this juncture that Sparkfun have gone and been the right bloody gits they usually are, and released a dirt cheap “DOSonCHIP” IC, that does most if not all what my FAT and MMC code does, for a mere $11.95. Bastards.

But then, there’s also existing software implementations of FAT16/32 support for AVRs. Lots of them, in fact. But then that’d defeat the point, really; I should be doing a business course if I want to just buy my way out of doing real work.

Still, it’s clear that my implementation isn’t particularly… good. It works, and well… but too well. I followed the spec a little too closely and literally, which means my driver will happily handle a whole galaxy of possible configurations that would in fact never actually occur. Hmm. There are other implementations done in maybe a hundred lines (for basic reading), versus my thousands. I do have good logging, though, in my defense. :D

It’s a common problem with me, actually… writing code that’s overly robust and flexible. It certainly doesn’t suit this style of development, with a very resource-constrained device. Although I will crow that my implementation is the only one I know of that will work in less than 512 bytes of SRAM, since it doesn’t have to use the same block size as the physical device, or the sector size of the FAT file system. But that’s exactly it; who cares? In hindsight it’s easy to see that you’re just not going to encounter a FAT volume that doesn’t use a 512 byte sector, and we’ve got 8k of SRAM to play with anyway.

Well, I should be happy that this is working, but now I’m quite depressed. This isn’t the way self reflection is supposed to work. :)

Leave a Comment