The Sparkfun Log-O-Matic is an interesting device but the included firmware leaves a little something to be desired. Its performance is very limited so that if you want to record analog data at more than a few hundred Samples per Second (SPS) you are out of luck. Note that this is for the version 1 hardware as the updated V2 hardware appeared just after I purchased one.
I have made some minor and major improvements to the firmware. In the minor category I cleaned up the source file main.c, added 16K of buffer space, and changed the SPI code so it increased the clock rate after initialization like it is supposed to. I also turned on compiler optimization in all but main.c. That tends to cause the IRQ routines in it to break. These minor changes do produce significant improvements in speed. For reference purposes I will call this version 3.0.
V3 firmware source and executable
In the major category, I built on the changes in main.c but replaced all of the lower level code. The FAT16 file system code was replaced with something a lot simpler. This will be called the version 4.0 firmware.
Both versions were built using an evaluation version of the Crossworks tools. I started out using the GNU tools but was unable to traverse the bewildering array of variations and options for the ARM.
There are a lot of changes in V4 to discuss so I will start at the bottom and work up.
The lowest level code that deals with the SPI interface is confined to the file "spi.c". This has just two routines. The first is the general purpose routine to send and receive a single byte of data. I added an additional routine just for transmitting a block of data since that operation was performed frequently and could benefit from the optimization. "spi.h" also contains preprocessor macros for dealing with the chip select output pin and setting the SPI port clock speed.
Next up from the SPI code is "sd.c" which handles all of the details of the protocol required by the SD card. The initialization routine has been updated from the antiquated MMC version to something that meets the current SD card standard (2.0). SDHC cards are not supported because that would require FAT32 support. Support for multi-sector write commands is also included.
Next is the new FAT16 code in "fat.c". This provides a limited but higher speed interface to a FAT16 file system. When a file is opened for writing the FAT table is scanned from the end towards the beginning. The assumption being that the largest continuous block of free clusters will be here. Writing is limited to this free space. When opened the directory entry is created and a single cluster is assigned to it. Only when the file is closed will the cluster table be updated along with the file size in the directory entry. This eliminates all of the thrashing of the FAT table at the cost of the file state being up in the air until it is closed.
This code has no local buffers for reading and writing of sectors. If a routine needs a buffer in order to read a sector of data, a pointer to a suitable buffer must be provided by the calling program. Because the data buffers are only in use during the writing of data, they can and are used for this purpose.
"buffer.c" contains the code for the data buffers. Included is code to insert data into a buffer. This routine checks to see if there is space available. If the buffers are full the data is dropped and the buffers are not modified. This is a change in behaviour from the original code which would replace old data in the buffers with new. The number and size of buffers is set in "buffer.h". A minimum of two buffers are needed and they must be a multiple of the sector size. Maximum size is limited by available memory. 16K of buffer space seems reasonable as this allows plenty of space for things like stacks, variables, the heap, etc.
All of the interrupt routines are collected into "irq.c". This is because compiler optimization breaks the interrupt code. All the rest of the code is compiled with optimization on in order to decrease size and improve execution speed.
"adc.c" contains the code to perform ADC conversions. There are two versions. The first handles the mode 2 operation and the second are interrupt service routines. I would prefer to use the faster FIQ interrupt but have not yet figured out how to make them work. The 10 bit conversion results are stored in two bytes. In mode 2 the upper 6 bits are unused but in mode 3 the upper 4 bits contain the ADC and channel number that produced the result. The interrupt code is still not optimal as I have noticed that the ARM instruction set includes bit field operators which would be very useful here.
At the top level I have added a new mode of operation. Mode 3 uses interrupts instead of polling to operate the ADC. This decreases the time wasted waiting for a conversion to complete. Maximum recording speed is significantly increased as a result and I have tested this at 160,000 SPS. I use a simple program to demuliplex the data for analysis.
Speaking of testing, this is of course limited to the memory cards I have on hand. In the process I discovered that the 128MB Kingston micro-SD cards that I have do not comply with the SD card specification. They do work but they do not comply. I have also tested using 2GB Patriot micro-SD and Sandisk Ultra II SD cards. At this time my old 64MB Sandisk MMC card will not initialize and I suspect that it is confused by the new SD initialization sequence. This doesn't seem too important so I might not ever try and fix it.
By adding a line with "#define TESTSD" to the "main.c" file the mode 2 code is replaced with code to test the raw write speed. At every interrupt the code will fill one buffer with as many 32 bit integers holding the current count of interrupts as will fit. If the code discovers that there is no empty buffer available, it sets a flag and quits. When the next interrupt occurs this flag is checked and if their is an empty buffer, it is filled with a special pattern to mark the buffer over-run condition.