aboutsummaryrefslogtreecommitdiff
path: root/dumblogger/libraries/SDfat/examples/Arduino Examples/SdFatRawWrite/SdFatRawWrite.pde
blob: f2db00e2d936ce5ce8381d0838f582ede7f784cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
 * This sketch illustrates raw write functions in SdFat that
 * can be used for high speed data logging.  These functions
 * are used in the WaveRP library to record audio with the
 * Adafruit Wave Shield using the built-in Arduino ADC.
 *
 * The WaveRP library captures data from the ADC in an ISR 
 * that is driven driven by timer one.  Data is collected in
 * two 512 byte buffers and written to the SD card. 
 *
 * This sketch simulates logging from a source that produces
 * data at a constant rate of one block every MILLIS_PER_BLOCK.
 *
 * If a high quality SanDisk card is used with this sketch
 * no overruns occur and the maximum block write time is
 * 2 millis.
 *
 * Note: WaveRP creates a very large file then truncates it
 * to the length that is used for a recording. It only takes
 * a few seconds to erase a 500 MB file since the card only
 * marks the blocks as erased; no data transfer is required.
 */

#include <SdFat.h>
#include <SdFatUtil.h>

// number of blocks in the contiguous file
#define BLOCK_COUNT 10000UL

// time to produce a block of data
#define MILLIS_PER_BLOCK 10

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

uint32_t bgnBlock, endBlock;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

void error_P(const char* str) {
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  Serial.flush();
  PgmPrintln("Type any character to start");
  while (!Serial.available());

  // initialize the SD card
  uint32_t t = millis();
  
  // initialize the SD card at SPI_FULL_SPEED for best performance.
  // try SPI_HALF_SPEED if bus errors occur.
  if (!card.init(SPI_FULL_SPEED)) error("card.init failed");
  
  t = millis() - t;
  
  PgmPrint("Card init time: ");
  Serial.print(t);
  PgmPrintln(" millis");
  
  // initialize a FAT volume
  if (!volume.init(&card)) error("volume.init failed");
  
  // open the root directory
  if (!root.openRoot(&volume)) error("openRoot failed");
  
  // delete possible existing file
  SdFile::remove(&root, "RAW.TXT");
  
  // create a contiguous file
  if (!file.createContiguous(&root, "RAW.TXT", 512UL*BLOCK_COUNT)) {
    error("createContiguous failed");
  }
  // get the location of the file's blocks
  if (!file.contiguousRange(&bgnBlock, &endBlock)) {
    error("contiguousRange failed");
  }
  //*********************NOTE**************************************
  // NO SdFile calls are allowed while cache is used for raw writes
  //***************************************************************
  
  // clear the cache and use it as a 512 byte buffer
  uint8_t* pCache = volume.cacheClear();
  
  // fill cache with eight lines of 64 bytes each
  memset(pCache, ' ', 512);
  for (uint16_t i = 0; i < 512; i += 64) {
    // put line number at end of line then CR/LF
    pCache[i + 61] = '0' + (i/64);
    pCache[i + 62] = '\r';
    pCache[i + 63] = '\n';
  }
  
  PgmPrint("Start raw write of ");
  Serial.print(file.fileSize());
  PgmPrintln(" bytes at");
  
  Serial.print(512000UL/MILLIS_PER_BLOCK);
  PgmPrintln(" bytes per second");
  
  PgmPrint("Please wait ");
  Serial.print((BLOCK_COUNT*MILLIS_PER_BLOCK)/1000UL);
  PgmPrintln(" seconds");
  
  // tell card to setup for multiple block write with pre-erase
  if (!card.erase(bgnBlock, endBlock)) error("card.erase failed");
  if (!card.writeStart(bgnBlock, BLOCK_COUNT)) {
    error("writeStart failed");
  }
  // init stats
  uint16_t overruns = 0;
  uint16_t maxWriteTime = 0;
  t = millis();
  uint32_t tNext = t;

  // write data
  for (uint32_t b = 0; b < BLOCK_COUNT; b++) {
    // write must be done by this time
    tNext += MILLIS_PER_BLOCK;

    // put block number at start of first line in block
    uint32_t n = b;
    for (int8_t d = 5; d >= 0; d--){
      pCache[d] = n || d == 5 ? n % 10 + '0' : ' ';
      n /= 10;
    }
    // write a 512 byte block
    uint32_t tw = micros();
    if (!card.writeData(pCache)) error("writeData failed");
    tw = micros() - tw;
    
    // check for max write time
    if (tw > maxWriteTime) {
      maxWriteTime = tw;
    }
    // check for overrun
    if (millis() > tNext) {
      overruns++;
      // advance time to reflect overrun
      tNext = millis();
    }
    else {
      // wait for time to write next block
      while(millis() < tNext);
    }
  }
  // total write time
  t = millis() - t;
  
  // end multiple block write mode
  if (!card.writeStop()) error("writeStop failed");
  
  PgmPrintln("Done");
  
  PgmPrint("Overruns: ");
  Serial.println(overruns);
  
  PgmPrint("Elapsed time: ");
  Serial.print(t);
  PgmPrintln(" millis");
  
  PgmPrint("Max write time: ");
  Serial.print(maxWriteTime);
  PgmPrintln(" micros");
  
  // close files for next pass of loop
  root.close();
  file.close();
  Serial.println();
}