Here’s the code used for the accelerometer reader and data writer to SD card for reference purposes:
// Train accelerometer measurement and data logging system // // (C) 2014 zapmaker with credit to others inline // Original code from 2012 // Apache 2.0 license except as noted by others' code // #include <WProgram.h> #include <avr/pgmspace.h> #include <microfat2.h> #include <mmc.h> #include <DevicePrint.h> #define srDataPin 2 #define srClockPin 3 #define demuxAPin 4 #define demuxBPin 5 #define demuxCPin 6 #define demuxEnablePin 7 #define tachPin 8 // Accelerometer that outputs three axes as a voltage #define accelXPin A0 #define accelYPin A1 #define accelZPin A2 // Trim potentiometers #define trimXPin A3 #define trimYPin A4 #define scalePin A5 DevicePrint dp; byte sectorBuffer[512]; char sprint_buffer[40]; // filtering array to smooth out accelerometer data #define arrSize 20 word arrX[arrSize]; word arrY[arrSize]; byte flipLookup[8]; word arrXPos = 0; word arrYPos = 0; /* Timer2 reload value, globally available */ unsigned int tcnt2; unsigned int timeVal = 0; byte writeC = 0; void setup() { // 74LS164 shift register clock/data pinMode(srDataPin, OUTPUT); pinMode(srClockPin, OUTPUT); // 74LS138 demux data/control pinMode(demuxAPin, OUTPUT); pinMode(demuxBPin, OUTPUT); pinMode(demuxCPin, OUTPUT); pinMode(demuxEnablePin, OUTPUT); // photointerrupter input to measure wheel distance of train engine pinMode(tachPin, INPUT); digitalWrite(srDataPin, LOW); digitalWrite(srClockPin, LOW); digitalWrite(demuxAPin, LOW); digitalWrite(demuxBPin, LOW); digitalWrite(demuxCPin, LOW); digitalWrite(demuxEnablePin, LOW); Serial.begin(115200); if (mmc::initialize() != RES_OK) error(PSTR("mmc init failed.\n")); // Pass in the sector-sized buffer we'll be using ourselves later. // uFat doesn't own it, it just needs to use it temporarily. // We also pass in the address of a function that is used to read // sectors from our device. if (!microfat2::initialize(sectorBuffer, &mmc::readSectors)) error(PSTR("uFat init failed.\n")); showDirectory(); testWrite(); unsigned long sector, fileSize; // the file name we are writing to the SD card if(!microfat2::locateFileStart(PSTR("DATA CSV"), sector, fileSize)) { error(PSTR("Couldn't find data.csv on the card")); } // The dp library won't write past the end of the allowed space. // Indeed, it will only write up to the nearest sector boundary at // the end of the file on the card. If your target file is 1000 bytes, // only 512 will be written over. The remaining will be left as they // were when written to the card. dp.initialize(sector, fileSize / 512, sectorBuffer, proxyWriter); memset(sectorBuffer, '.', 512); // fills the file on the card for (byte i = 0; i < arrSize; i++) { arrX[i] = 0; arrY[i] = 0; } // clear s/r for (byte i = 0; i < 8; i++) { digitalWrite(srClockPin, HIGH); digitalWrite(srClockPin, LOW); } byte initVal = 7; for (byte i = 0; i < 8; i++, initVal--) { flipLookup[i] = initVal; } setupTimerInterrupt(); } void loop() { int x, y, z=0; int xp, yp, sc; x = analogRead(accelXPin); arrX[arrXPos++] = x; if (arrXPos >= arrSize) arrXPos = 0; // filter x-data int xr = 0; for (byte i = 0; i < arrSize; i++) xr += arrX[i]; xr /= arrSize; y = analogRead(accelYPin); arrY[arrYPos++] = y; if (arrYPos >= arrSize) arrYPos = 0; // filter y-data int yr = 0; for (byte i = 0; i < arrSize; i++) yr += arrY[i]; yr /= arrSize; int trimsc = 16; word inXTr, inYTr, inSc; // read in the current trim values inXTr= analogRead(trimXPin); inYTr = analogRead(trimYPin); inSc = analogRead(scalePin); // make the trim adjustments xp = inXTr / trimsc; yp = inYTr / trimsc; sc = (inSc / 32) + 1; // compute a value 0-7 for the 8x8 LED matrix int xf = ((xr - ((366 + (xp - (trimsc / 2))) - (4 * sc))) / sc); int yf = ((yr - ((369 + (yp - (trimsc / 2))) - (4 * sc))) / sc); //z = analogRead(accelZPin); // read the wheel position int tachState = digitalRead(tachPin); writeLedMatrix(xf, yf); if (writeC == arrSize) { // write data to the SD card writeData(xr, yr, tachState); writeC = 0; } writeC++; } void writeLedMatrix(int dc, int sv) { if (dc < 0) { dc = 0; } else if (dc > 7) { dc = 7; } else if (sv < 0) { sv = 0; } else if (sv > 7) { sv = 7; } // this is to save on pins used by the Arduino - let two external chips drive the matrix digitalWrite(demuxEnablePin, LOW); writeDemux(dc); writeShifter(sv); digitalWrite(demuxEnablePin, HIGH); } void writeLedMatrixWithOverflow(int dc, int sv) { word d = 2; byte c = 3; boolean hit = false; if (dc < 0) { dc = 0; hit = true; for (byte j = 0; j < c; j++) for (byte i = 0; i < 8; i++) { digitalWrite(demuxEnablePin, LOW); writeDemux(dc); writeShifter(i); digitalWrite(demuxEnablePin, HIGH); delay(d); } } else if (dc > 7) { dc = 7; hit = true; for (byte j = 0; j < c; j++) for (byte i = 0; i < 8; i++) { digitalWrite(demuxEnablePin, LOW); writeDemux(dc); writeShifter(i); digitalWrite(demuxEnablePin, HIGH); delay(d); } } else if (sv < 0) { sv = 0; hit = true; for (byte j = 0; j < c; j++) for (byte i = 0; i < 8; i++) { digitalWrite(demuxEnablePin, LOW); writeDemux(i); writeShifter(sv); digitalWrite(demuxEnablePin, HIGH); delay(d); } } else if (sv > 7) { sv = 7; hit = true; for (byte j = 0; j < c; j++) for (byte i = 0; i < 8; i++) { digitalWrite(demuxEnablePin, LOW); writeDemux(i); writeShifter(sv); digitalWrite(demuxEnablePin, HIGH); delay(d); } } else { digitalWrite(demuxEnablePin, LOW); writeDemux(dc); writeShifter(sv); digitalWrite(demuxEnablePin, HIGH); } } void writeDemux(word value) { word newValue = value < 0 ? 0 : value; newValue = value > 7 ? 7 : newValue; newValue = flipLookup[newValue]; newValue <<= 4; PORTD = (0x8F & PORTD) | (newValue); } void writeShifter(word value) { word newValue = value < 0 ? 0 : value; newValue = value > 7 ? 7 : newValue; for (byte i = 0; i < 8; i++) { if (i == newValue) digitalWrite(srDataPin, HIGH); else digitalWrite(srDataPin, LOW); digitalWrite(srClockPin, HIGH); digitalWrite(srClockPin, LOW); } } // BEWARE - don't print strings longer than 39 characters! // If you can't help it, adjust buffer size above. // void pprint(const char* s) { strcpy_P(sprint_buffer, (PGM_P)s); Serial.print(sprint_buffer); } void error(const char* s) { pprint(PSTR("Error: ")); pprint(s); pprint(PSTR("<press reset>")); for( /* ever */ ; ; ) { digitalWrite(13, (millis() / 250) & 1); } } //http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235125412/0 bool showDirectory_walkerfn(directory_entry_t* directory_entry_data, unsigned index, void* user_data) { int* count = (int*)user_data; Serial.print(index, DEC); Serial.print(' '); // Terminate the filename string. // This is deliberately corrupting the buffer data, but that's ok. directory_entry_data->filespec[11] = 0; Serial.println(directory_entry_data->filespec); // Increase 'seen file' count *count = (*count)+1; // don't stop return false; } void showDirectory(void) { int count = 0; pprint(PSTR("Directory of files on card:\n\n")); microfat2::walkDirectory(showDirectory_walkerfn, &count); pprint(PSTR("\n")); Serial.print(count, DEC); pprint(PSTR(" files found.\n\n")); } void testWrite() { unsigned long sector; unsigned long byteSize; if (microfat2::locateFileStart(PSTR("DATA CSV"), sector, byteSize)) { if (byteSize >= 512) { if (RES_OK == mmc::readSectors(sectorBuffer, sector, 1)) { for (int i = 0; i < 512; ++i) { sectorBuffer[i] = sectorBuffer[i] + 1; } if (RES_OK == mmc::writeSectors(sectorBuffer, sector, 1)) { pprint(PSTR("Written to data.csv OK!")); } else { error(PSTR("Failed to write updated data.")); } } else { error(PSTR("Failed to read data.csv.")); } } else { error(PSTR("Found data.csv, but it's too small.")); } } else { error(PSTR("data.csv not present on card.")); } } void writeData(int xp, int yp, int tachState) { dp.print(timeVal); dp.print(","); dp.print(xp); dp.print(","); dp.print(yp); dp.print(","); dp.print(tachState); dp.println(); } // https://docs.google.com/Doc?id=dqhc6fg_0gmk96kdd&pli=1 uint8_t proxyWriter(const uint8_t* buffer, unsigned long sector, uint8_t count) { // I could have just used this function to pass as the sector write... // uint8_t val = mmc::writeSectors(buffer, sector, count); // ... but I want to process the buffer after each write to the card! // if (dp.m_bufferIndex == 512) { // we've written a full buffer so clear it out ready for the next // writes - the device print variables will be updated after we return // from this function. // memset(sectorBuffer, '.', 512); } return val; } // http://popdevelop.com/2010/04/mastering-timer-interrupts-on-the-arduino/ void setupTimerInterrupt() { /* First disable the timer overflow interrupt while we're configuring */ TIMSK2 &= ~(1<<TOIE2); /* Configure timer2 in normal mode (pure counting, no PWM etc.) */ TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); TCCR2B &= ~(1<<WGM22); /* Select clock source: internal I/O clock */ ASSR &= ~(1<<AS2); /* Disable Compare Match A interrupt enable (only want overflow) */ TIMSK2 &= ~(1<<OCIE2A); /* Now configure the prescaler to CPU clock divided by 128 */ TCCR2B |= (1<<CS22) | (1<<CS20); // Set bits TCCR2B &= ~(1<<CS21); // Clear bit /* We need to calculate a proper value to load the timer counter. * The following loads the value 131 into the Timer 2 counter register * The math behind this is: * (CPU frequency) / (prescaler value) = 125000 Hz = 8us. * (desired period) / 8us = 125. * MAX(uint8) + 1 - 125 = 131; */ /* Save value globally for later reload in ISR */ tcnt2 = 131; /* Finally load end enable the timer */ TCNT2 = tcnt2; TIMSK2 |= (1<<TOIE2); } /* * Install the Interrupt Service Routine (ISR) for Timer2 overflow. * This is normally done by writing the address of the ISR in the * interrupt vector table but conveniently done by using ISR() */ ISR(TIMER2_OVF_vect) { /* Reload the timer */ TCNT2 = tcnt2; timeVal++; }