uSD Card Tests:
Before modifying the V2 code to attempt to improve uSD performance, I decided to first see just how fast my 2GB Patriot uSD card can transfer data on my Linux system.
To ensure the card itself is being tested (and not the adapter or hub), I put my uSD card in an SD adapter that was then placed in an SDHC-compatible SD card reader on a USB2 port. This setup uses the full 4-bit SD interface, while the V2 uses the SPI interface, which is at least 4x slower (probably worse).
My card shows up as /dev/sdb, and the first (and only) partition, /dev/sdb1, mounts as /media/728D-CF57
Code: Select all
$ sudo hdparmn -t /dev/sdb
Timing buffered disk reads: 40 MB in 3.01 seconds = 13.28 MB/sec
Not too shabby!
But we really care about write performance:
Code: Select all
$ dd count=1k bs=1M if=/dev/zero of=/media/728D-CF57/test.img
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 355.876 s, 3.0 MB/s
Not bad! This test was performed on a "dirty" card that had been completely filled up, all files deleted, and NOT reformatted prior to the test. So it includes all erase-before-write acions the card performs behind the scenes.
The SD card specification says the SPI port on a uSD card should be capable of operating at 50 MHz, which yields a transfer rate of 6.2 MB/s. This will limit the maximum read speed, but should not in any way hinder the write speed.
But how fast can the LPC2148 transfer data out SPI0? First, let's look at the SPI0 clock: The V2 uses a 12 MHz crystal that the PLL multiplies by 5 to yield a 60 MHz processor clock. This clock is divided by 4 to yield a peripheral clock of 15 MHz. The lowest allowed SPI0 clock divider value is 8, yielding a maximum SPI0 clock rate of 1.875 MHz. It takes SPI 8 clocks to transfer each byte, yielding a maximum possible SPI0 transfer rate of 234.375 KB/s.
However, the SPI0 transfer is not done via DMA, and requires the processor to poll the SPI0 status and loop to transfer each byte, so there are some instruction delays that occur between when one SPI0 byte transfer ends and the next begins. I haven't counted the instructions, but it should be no more than 20 or so, for a polling rate of about 3 MHz, which yields only a tiny reduction in overall the SPI0 data rate. So let's just round down VERY generously, to include tons of time to send the write command, to yield a maximum expected uSD transfer rate of 200 KB/s.
I expect to log a minimum of 8 bytes per sample, and the limit of the ADXL345 is 3200 samples/s, yielding a logging rate of 25.6 KB/s. Even given all the limitations of the V2 processor, this should be easily achievable. But the current code is limited to just under 13 KB/s.
If we put this in terms of 512 byte sectors, thats an expected ideal transfer rate of 400 sectors/s, a desired logging rate of 51.2 sectors/s, and a current V2 performance of about 25 sectors/s.
Another way to look at it is as the time needed to write a sector: 2.5 ms/sector max, 19.5 ms/sector desired, and 40 ms/sector currently.
So why is the current code so slow? That was partially discussed in a prior post in this thread. Basically, whenever a new cluster is needed, the FAT code must read and update at least one FAT sector, sometimes more. At best, this temporarily cuts the throughput by a factor of 2 (probably slightly worse when you add in the time needed to compute the update). So the 40 ms/sector may be the worst-case, due to allocating a new cluster, meaning the write rate between cluster allocations should be less than 20 ms/sector.
In other words, if we can eliminate the FAT updates for cluster allocation, we should be able to attain at least 20 ms/sector. That's enticingly close to our desired rate of 19.5 ms/sector!
However, I may have just found a bug in the V2 code: In the Initialize() function in main.c, the "S0SPCR" register is being set to 8, when it should be the "S0SPCCR" register! A single-character typo that yields another valid register name instead of creating an error that would be easy to fix. The previous value written to the S0SPCCR register appears to be 60. That error would degrade the transfer rate by a factor of 7.5, to 18.75 ms/sector MAX! That seems way too close to 20 ms to be a coincidence.
If this is indeed a bug, then we may not need to go through the bother of trying to avoid the FAT updates: We may have more than enough uSD bandwidth.
I'll code it up this weekend and see if I missed something, or if my analysis is wrong.