| @@ -1,20 +1,101 @@ | |||
| ### Warning: This beta is no longer up to date with the release SdFat. | |||
| ### Warning: This is an early beta version of SdFat Version 2. | |||
| ### Please use the current version of SdFat. | |||
| This library is in early development and features may change. | |||
| It will clearly have bugs. I am posting this version to get comments and | |||
| help finding bugs/compatibility problems. | |||
| Changes Version 1.0.10: | |||
| You can help by posting issues for problems you find. I am doing a great deal | |||
| of testing but actual applications make the best test cases. | |||
| Initial test version for Particle Gen3 mesh. | |||
| SdFat Version 2 supports FAT16/FAT32 and exFAT SD cards. It is mostly | |||
| backward compatible with SdFat Version 1 for FAT16/FAT32 cards. | |||
| Changes Version 1.0.9: | |||
| exFAT supports files larger than 4GB so files sizes and positions are | |||
| type uint64_t for classes that support exFAT. | |||
| This version of SdFat has been modified to use standard POSIX/Linux | |||
| definitions of open flags from fcntl.h. | |||
| exFAT has many features not available in FAT16/FAT32. exFAT has excellent | |||
| support for contiguous files on flash devices and supports preallocation. | |||
| Open flags are access modes, O_RDONLY, O_RDWR, O_WRONLY, and modifiers | |||
| O_APPEND, O_CREAT, O_EXCL, O_SYNC, O_TRUNC. | |||
| If the SD card is the only SPI device, use dedicated SPI mode. This can | |||
| greatly improve performance. See the bench example. | |||
| The mods required changing the type for open flags from uint8_t to int so | |||
| bugs are likely if any uint8_t local variables were missed. | |||
| Here is write performance for an old, 2011, card on a Due board. | |||
| ``` | |||
| Shared SPI: | |||
| write speed and latency | |||
| speed,max,min,avg | |||
| KB/Sec,usec,usec,usec | |||
| 294.45,24944,1398,1737 | |||
| Dedicated SPI: | |||
| write speed and latency | |||
| speed,max,min,avg | |||
| KB/Sec,usec,usec,usec | |||
| 3965.11,16733,110,127 | |||
| ``` | |||
| The default version of SdFatConfig.h enables support for dedicated SPI and | |||
| optimized access to contiguous files. This make SdFat Version 2 slightly | |||
| larger than Version 1. If these features are disabled, Version 2 is smaller | |||
| than Version 1. | |||
| The types for the classes SdFat and File are defined in SdFatConfig.h. | |||
| The default version of SdFatConfig.h defines SdFat to only support FAT16/FAT32. | |||
| SdFat and File are defined in terms of more basic classes by typedefs. You | |||
| can use these basic classes in applications. | |||
| Support for exFAT requires a substantial amount of flash. Here are sizes on | |||
| an UNO for a simple program that opens a file, prints one line, and closes | |||
| the file. | |||
| ``` | |||
| FAT16/FAT32 only: 9780 bytes flash, 875 bytes SRAM. | |||
| exFAT only: 13830 bytes flash, 938 bytes SRAM. | |||
| FAT16/FAT32/exFAT: 19326 bytes flash, 928 bytes SRAM. | |||
| ``` | |||
| The section below of SdFatConfig.h has been edited to uses FAT16/FAT32 for | |||
| small AVR boards and FAT16/FAT32/exFAT for all other boards. | |||
| ``` | |||
| /** | |||
| * File types for SdFat, File, SdFile, SdBaseFile, fstream, | |||
| * ifstream, and ofstream. | |||
| * | |||
| * Set SDFAT_FILE_TYPE to: | |||
| * | |||
| * 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| */ | |||
| #if defined(__AVR__) && FLASHEND < 0X8000 | |||
| // FAT16/FAT32 for 32K AVR boards. | |||
| #define SDFAT_FILE_TYPE 1 | |||
| #else // defined(__AVR__) && FLASHEND < 0X8000 | |||
| // FAT16/FAT32 and exFAT for all other boards. | |||
| #define SDFAT_FILE_TYPE 3 | |||
| #endif // defined(__AVR__) && FLASHEND < 0X8000 | |||
| ``` | |||
| The SdBaseFile class has no Arduino Stream or Print support. | |||
| The File class is derived from Stream and SdBaseFile. | |||
| The SdFile class is derived from SdBaseFile and Print. | |||
| Please try the examples. Start with SdInfo, bench, and ExFatLogger. | |||
| To use SdFat Version 2, unzip the download file, rename the library folder | |||
| SdFat and place the SdFat folder into the libraries sub-folder in your main | |||
| sketch folder. | |||
| For more information see the Manual installation section of this guide: | |||
| http://arduino.cc/en/Guide/Libraries | |||
| A number of configuration options can be set by editing SdFatConfig.h | |||
| define macros. See the html documentation File tab for details. | |||
| Please read the html documentation for this library in SdFat/extras/SdFat.html. | |||
| Start with the Main Page. Next go to the Classes tab and read the | |||
| documentation for the classes SdFat32, SdExFat, SdFs, File32, ExFile, FsFile. | |||
| The SdFat and File classes are defined in terms of the above classes by | |||
| typedefs. Edit SdFatConfig.h to select class options. | |||
| Please continue by reading the html documentation in the SdFat/extras folder. | |||
| @@ -0,0 +1,31 @@ | |||
| #ifndef AnalogBinLogger_h | |||
| #define AnalogBinLogger_h | |||
| const size_t BLOCK_SIZE = 64; | |||
| //------------------------------------------------------------------------------ | |||
| // First block of file. | |||
| const size_t PIN_NUM_DIM = BLOCK_SIZE - 3*sizeof(uint32_t) - 2*sizeof(uint8_t); | |||
| struct metadata_t { | |||
| uint32_t adcFrequency; // ADC clock frequency | |||
| uint32_t cpuFrequency; // CPU clock frequency | |||
| uint32_t sampleInterval; // Sample interval in CPU cycles. | |||
| uint8_t recordEightBits; // Size of ADC values, nonzero for 8-bits. | |||
| uint8_t pinCount; // Number of analog pins in a sample. | |||
| uint8_t pinNumber[PIN_NUM_DIM]; // List of pin numbers in a sample. | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for 8-bit ADC mode. | |||
| const size_t DATA_DIM8 = (BLOCK_SIZE - 2*sizeof(uint16_t))/sizeof(uint8_t); | |||
| struct block8_t { | |||
| uint16_t count; // count of data values | |||
| uint16_t overrun; // count of overruns since last block | |||
| uint8_t data[DATA_DIM8]; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for 10-bit ADC mode. | |||
| const size_t DATA_DIM16 = (BLOCK_SIZE - 2*sizeof(uint16_t))/sizeof(uint16_t); | |||
| struct block16_t { | |||
| unsigned short count; // count of data values | |||
| unsigned short overrun; // count of overruns since last block | |||
| unsigned short data[DATA_DIM16]; | |||
| }; | |||
| #endif // AnalogBinLogger_h | |||
| @@ -0,0 +1,887 @@ | |||
| /** | |||
| * This program logs data from the Arduino ADC to a binary file. | |||
| * | |||
| * Samples are logged at regular intervals. Each Sample consists of the ADC | |||
| * values for the analog pins defined in the PIN_LIST array. The pins numbers | |||
| * may be in any order. | |||
| * | |||
| * Edit the configuration constants below to set the sample pins, sample rate, | |||
| * and other configuration values. | |||
| * | |||
| * If your SD card has a long write latency, it may be necessary to use | |||
| * slower sample rates. Using a Mega Arduino helps overcome latency | |||
| * problems since more 64 byte buffer blocks will be used. | |||
| * | |||
| * Each 64 byte data block in the file has a four byte header followed by up | |||
| * to 60 bytes of data. (60 values in 8-bit mode or 30 values in 10-bit mode) | |||
| * Each block contains an integral number of samples with unused space at the | |||
| * end of the block. | |||
| * | |||
| */ | |||
| #ifdef __AVR__ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "BufferedPrint.h" | |||
| #include "FreeStack.h" | |||
| #include "AvrAdcLogger.h" | |||
| // Save SRAM if 328. | |||
| #ifdef __AVR_ATmega328P__ | |||
| #include "MinimumSerial.h" | |||
| MinimumSerial MinSerial; | |||
| #define Serial MinSerial | |||
| #endif // __AVR_ATmega328P__ | |||
| //------------------------------------------------------------------------------ | |||
| // This example was designed for exFAT but will support FAT16/FAT32. | |||
| // | |||
| // If an exFAT SD is required, the ExFatFormatter example will format | |||
| // smaller cards with an exFAT file system. | |||
| // | |||
| // Note: Uno will not support SD_FAT_TYPE = 3. | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 2 | |||
| //------------------------------------------------------------------------------ | |||
| // Set USE_RTC nonzero for file timestamps. | |||
| // RAM use will be marginal on Uno with RTClib. | |||
| #define USE_RTC 0 | |||
| #if USE_RTC | |||
| #include "RTClib.h" | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| // Pin definitions. | |||
| // | |||
| // Digital pin to indicate an error, set to -1 if not used. | |||
| // The led blinks for fatal errors. The led goes on solid for SD write | |||
| // overrun errors and logging continues. | |||
| const int8_t ERROR_LED_PIN = -1; | |||
| // SD chip select pin. | |||
| const uint8_t SD_CS_PIN = SS; | |||
| //------------------------------------------------------------------------------ | |||
| // Analog pin number list for a sample. Pins may be in any order and pin | |||
| // numbers may be repeated. | |||
| const uint8_t PIN_LIST[] = {0, 1, 2, 3, 4}; | |||
| //------------------------------------------------------------------------------ | |||
| // Sample rate in samples per second. | |||
| const float SAMPLE_RATE = 5000; // Must be 0.25 or greater. | |||
| // The interval between samples in seconds, SAMPLE_INTERVAL, may be set to a | |||
| // constant instead of being calculated from SAMPLE_RATE. SAMPLE_RATE is not | |||
| // used in the code below. For example, setting SAMPLE_INTERVAL = 2.0e-4 | |||
| // will result in a 200 microsecond sample interval. | |||
| const float SAMPLE_INTERVAL = 1.0/SAMPLE_RATE; | |||
| // Setting ROUND_SAMPLE_INTERVAL non-zero will cause the sample interval to | |||
| // be rounded to a a multiple of the ADC clock period and will reduce sample | |||
| // time jitter. | |||
| #define ROUND_SAMPLE_INTERVAL 1 | |||
| //------------------------------------------------------------------------------ | |||
| // Reference voltage. See the processor data-sheet for reference details. | |||
| // uint8_t const ADC_REF = 0; // External Reference AREF pin. | |||
| uint8_t const ADC_REF = (1 << REFS0); // Vcc Reference. | |||
| // uint8_t const ADC_REF = (1 << REFS1); // Internal 1.1 (only 644 1284P Mega) | |||
| // uint8_t const ADC_REF = (1 << REFS1) | (1 << REFS0); // Internal 1.1 or 2.56 | |||
| //------------------------------------------------------------------------------ | |||
| // File definitions. | |||
| // | |||
| // Maximum file size in bytes. | |||
| // The program creates a contiguous file with MAX_FILE_SIZE_MiB bytes. | |||
| // The file will be truncated if logging is stopped early. | |||
| const uint32_t MAX_FILE_SIZE_MiB = 100; // 100 MiB file. | |||
| // log file name. Integer field before dot will be incremented. | |||
| #define LOG_FILE_NAME "AvrAdc00.bin" | |||
| // Maximum length name including zero byte. | |||
| const size_t NAME_DIM = 40; | |||
| // Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC. | |||
| #define RECORD_EIGHT_BITS 0 | |||
| //------------------------------------------------------------------------------ | |||
| // FIFO size definition. Use a multiple of 512 bytes for best performance. | |||
| // | |||
| #if RAMEND < 0X8FF | |||
| #error SRAM too small | |||
| #elif RAMEND < 0X10FF | |||
| const size_t FIFO_SIZE_BYTES = 512; | |||
| #elif RAMEND < 0X20FF | |||
| const size_t FIFO_SIZE_BYTES = 4*512; | |||
| #elif RAMEND < 0X40FF | |||
| const size_t FIFO_SIZE_BYTES = 12*512; | |||
| #else // RAMEND | |||
| const size_t FIFO_SIZE_BYTES = 16*512; | |||
| #endif // RAMEND | |||
| //------------------------------------------------------------------------------ | |||
| // ADC clock rate. | |||
| // The ADC clock rate is normally calculated from the pin count and sample | |||
| // interval. The calculation attempts to use the lowest possible ADC clock | |||
| // rate. | |||
| // | |||
| // You can select an ADC clock rate by defining the symbol ADC_PRESCALER to | |||
| // one of these values. You must choose an appropriate ADC clock rate for | |||
| // your sample interval. | |||
| // #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno | |||
| // #define ADC_PRESCALER 6 // F_CPU/64 250 kHz on an Uno | |||
| // #define ADC_PRESCALER 5 // F_CPU/32 500 kHz on an Uno | |||
| // #define ADC_PRESCALER 4 // F_CPU/16 1000 kHz on an Uno | |||
| // #define ADC_PRESCALER 3 // F_CPU/8 2000 kHz on an Uno (8-bit mode only) | |||
| //============================================================================== | |||
| // End of configuration constants. | |||
| //============================================================================== | |||
| // Temporary log file. Will be deleted if a reset or power failure occurs. | |||
| #define TMP_FILE_NAME "tmp_adc.bin" | |||
| // Number of analog pins to log. | |||
| const uint8_t PIN_COUNT = sizeof(PIN_LIST)/sizeof(PIN_LIST[0]); | |||
| // Minimum ADC clock cycles per sample interval | |||
| const uint16_t MIN_ADC_CYCLES = 15; | |||
| // Extra cpu cycles to setup ADC with more than one pin per sample. | |||
| const uint16_t ISR_SETUP_ADC = PIN_COUNT > 1 ? 100 : 0; | |||
| // Maximum cycles for timer0 system interrupt, millis, micros. | |||
| const uint16_t ISR_TIMER0 = 160; | |||
| //============================================================================== | |||
| const uint32_t MAX_FILE_SIZE = MAX_FILE_SIZE_MiB << 20; | |||
| // Select fastest interface. | |||
| #if ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // ENABLE_DEDICATED_SPI | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| typedef File file_t; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| typedef File32 file_t; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| typedef ExFile file_t; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| typedef FsFile file_t; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| file_t binFile; | |||
| file_t csvFile; | |||
| char binName[] = LOG_FILE_NAME; | |||
| #if RECORD_EIGHT_BITS | |||
| const size_t BLOCK_MAX_COUNT = PIN_COUNT*(DATA_DIM8/PIN_COUNT); | |||
| typedef block8_t block_t; | |||
| #else // RECORD_EIGHT_BITS | |||
| const size_t BLOCK_MAX_COUNT = PIN_COUNT*(DATA_DIM16/PIN_COUNT); | |||
| typedef block16_t block_t; | |||
| #endif // RECORD_EIGHT_BITS | |||
| // Size of FIFO in blocks. | |||
| size_t const FIFO_DIM = FIFO_SIZE_BYTES/sizeof(block_t); | |||
| block_t* fifoData; | |||
| volatile size_t fifoCount = 0; // volatile - shared, ISR and background. | |||
| size_t fifoHead = 0; // Only accessed by ISR during logging. | |||
| size_t fifoTail = 0; // Only accessed by writer during logging. | |||
| //============================================================================== | |||
| // Interrupt Service Routines | |||
| // Disable ADC interrupt if true. | |||
| volatile bool isrStop = false; | |||
| // Pointer to current buffer. | |||
| block_t* isrBuf = nullptr; | |||
| // overrun count | |||
| uint16_t isrOver = 0; | |||
| // ADC configuration for each pin. | |||
| uint8_t adcmux[PIN_COUNT]; | |||
| uint8_t adcsra[PIN_COUNT]; | |||
| uint8_t adcsrb[PIN_COUNT]; | |||
| uint8_t adcindex = 1; | |||
| // Insure no timer events are missed. | |||
| volatile bool timerError = false; | |||
| volatile bool timerFlag = false; | |||
| //------------------------------------------------------------------------------ | |||
| // ADC done interrupt. | |||
| ISR(ADC_vect) { | |||
| // Read ADC data. | |||
| #if RECORD_EIGHT_BITS | |||
| uint8_t d = ADCH; | |||
| #else // RECORD_EIGHT_BITS | |||
| // This will access ADCL first. | |||
| uint16_t d = ADC; | |||
| #endif // RECORD_EIGHT_BITS | |||
| if (!isrBuf) { | |||
| if (fifoCount < FIFO_DIM) { | |||
| isrBuf = fifoData + fifoHead; | |||
| } else { | |||
| // no buffers - count overrun | |||
| if (isrOver < 0XFFFF) { | |||
| isrOver++; | |||
| } | |||
| // Avoid missed timer error. | |||
| timerFlag = false; | |||
| return; | |||
| } | |||
| } | |||
| // Start ADC for next pin | |||
| if (PIN_COUNT > 1) { | |||
| ADMUX = adcmux[adcindex]; | |||
| ADCSRB = adcsrb[adcindex]; | |||
| ADCSRA = adcsra[adcindex]; | |||
| if (adcindex == 0) { | |||
| timerFlag = false; | |||
| } | |||
| adcindex = adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0; | |||
| } else { | |||
| timerFlag = false; | |||
| } | |||
| // Store ADC data. | |||
| isrBuf->data[isrBuf->count++] = d; | |||
| // Check for buffer full. | |||
| if (isrBuf->count >= BLOCK_MAX_COUNT) { | |||
| fifoHead = fifoHead < (FIFO_DIM - 1) ? fifoHead + 1 : 0; | |||
| fifoCount++; | |||
| // Check for end logging. | |||
| if (isrStop) { | |||
| adcStop(); | |||
| return; | |||
| } | |||
| // Set buffer needed and clear overruns. | |||
| isrBuf = nullptr; | |||
| isrOver = 0; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // timer1 interrupt to clear OCF1B | |||
| ISR(TIMER1_COMPB_vect) { | |||
| // Make sure ADC ISR responded to timer event. | |||
| if (timerFlag) { | |||
| timerError = true; | |||
| } | |||
| timerFlag = true; | |||
| } | |||
| //============================================================================== | |||
| // Error messages stored in flash. | |||
| #define error(msg) (Serial.println(F(msg)),errorHalt()) | |||
| #define assert(e) ((e) ? (void)0 : error("assert: " #e)) | |||
| //------------------------------------------------------------------------------ | |||
| // | |||
| void fatalBlink() { | |||
| while (true) { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| delay(200); | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| delay(200); | |||
| } | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void errorHalt() { | |||
| // Print minimal error data. | |||
| // sd.errorPrint(&Serial); | |||
| // Print extended error info - uses extra bytes of flash. | |||
| sd.printSdError(&Serial); | |||
| // Try to save data. | |||
| binFile.close(); | |||
| fatalBlink(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printUnusedStack() { | |||
| Serial.print(F("\nUnused stack: ")); | |||
| Serial.println(UnusedStack()); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| #if USE_RTC | |||
| RTC_DS1307 rtc; | |||
| // Call back for file timestamps. Only called for file create and sync(). | |||
| void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) { | |||
| DateTime now = rtc.now(); | |||
| // Return date using FS_DATE macro to format fields. | |||
| *date = FS_DATE(now.year(), now.month(), now.day()); | |||
| // Return time using FS_TIME macro to format fields. | |||
| *time = FS_TIME(now.hour(), now.minute(), now.second()); | |||
| // Return low time bits in units of 10 ms. | |||
| *ms10 = now.second() & 1 ? 100 : 0; | |||
| } | |||
| #endif // USE_RTC | |||
| //============================================================================== | |||
| #if ADPS0 != 0 || ADPS1 != 1 || ADPS2 != 2 | |||
| #error unexpected ADC prescaler bits | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| inline bool adcActive() {return (1 << ADIE) & ADCSRA;} | |||
| //------------------------------------------------------------------------------ | |||
| // initialize ADC and timer1 | |||
| void adcInit(metadata_t* meta) { | |||
| uint8_t adps; // prescaler bits for ADCSRA | |||
| uint32_t ticks = F_CPU*SAMPLE_INTERVAL + 0.5; // Sample interval cpu cycles. | |||
| if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) { | |||
| error("Invalid ADC reference"); | |||
| } | |||
| #ifdef ADC_PRESCALER | |||
| if (ADC_PRESCALER > 7 || ADC_PRESCALER < 2) { | |||
| error("Invalid ADC prescaler"); | |||
| } | |||
| adps = ADC_PRESCALER; | |||
| #else // ADC_PRESCALER | |||
| // Allow extra cpu cycles to change ADC settings if more than one pin. | |||
| int32_t adcCycles = (ticks - ISR_TIMER0)/PIN_COUNT - ISR_SETUP_ADC; | |||
| for (adps = 7; adps > 0; adps--) { | |||
| if (adcCycles >= (MIN_ADC_CYCLES << adps)) { | |||
| break; | |||
| } | |||
| } | |||
| #endif // ADC_PRESCALER | |||
| meta->adcFrequency = F_CPU >> adps; | |||
| if (meta->adcFrequency > (RECORD_EIGHT_BITS ? 2000000 : 1000000)) { | |||
| error("Sample Rate Too High"); | |||
| } | |||
| #if ROUND_SAMPLE_INTERVAL | |||
| // Round so interval is multiple of ADC clock. | |||
| ticks += 1 << (adps - 1); | |||
| ticks >>= adps; | |||
| ticks <<= adps; | |||
| #endif // ROUND_SAMPLE_INTERVAL | |||
| if (PIN_COUNT > BLOCK_MAX_COUNT || PIN_COUNT > PIN_NUM_DIM) { | |||
| error("Too many pins"); | |||
| } | |||
| meta->pinCount = PIN_COUNT; | |||
| meta->recordEightBits = RECORD_EIGHT_BITS; | |||
| for (int i = 0; i < PIN_COUNT; i++) { | |||
| uint8_t pin = PIN_LIST[i]; | |||
| if (pin >= NUM_ANALOG_INPUTS) { | |||
| error("Invalid Analog pin number"); | |||
| } | |||
| meta->pinNumber[i] = pin; | |||
| // Set ADC reference and low three bits of analog pin number. | |||
| adcmux[i] = (pin & 7) | ADC_REF; | |||
| if (RECORD_EIGHT_BITS) { | |||
| adcmux[i] |= 1 << ADLAR; | |||
| } | |||
| // If this is the first pin, trigger on timer/counter 1 compare match B. | |||
| adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0; | |||
| #ifdef MUX5 | |||
| if (pin > 7) { | |||
| adcsrb[i] |= (1 << MUX5); | |||
| } | |||
| #endif // MUX5 | |||
| adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps; | |||
| // First pin triggers on timer 1 compare match B rest are free running. | |||
| adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC; | |||
| } | |||
| // Setup timer1 | |||
| TCCR1A = 0; | |||
| uint8_t tshift; | |||
| if (ticks < 0X10000) { | |||
| // no prescale, CTC mode | |||
| TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); | |||
| tshift = 0; | |||
| } else if (ticks < 0X10000*8) { | |||
| // prescale 8, CTC mode | |||
| TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); | |||
| tshift = 3; | |||
| } else if (ticks < 0X10000*64) { | |||
| // prescale 64, CTC mode | |||
| TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << CS10); | |||
| tshift = 6; | |||
| } else if (ticks < 0X10000*256) { | |||
| // prescale 256, CTC mode | |||
| TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12); | |||
| tshift = 8; | |||
| } else if (ticks < 0X10000*1024) { | |||
| // prescale 1024, CTC mode | |||
| TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12) | (1 << CS10); | |||
| tshift = 10; | |||
| } else { | |||
| error("Sample Rate Too Slow"); | |||
| } | |||
| // divide by prescaler | |||
| ticks >>= tshift; | |||
| // set TOP for timer reset | |||
| ICR1 = ticks - 1; | |||
| // compare for ADC start | |||
| OCR1B = 0; | |||
| // multiply by prescaler | |||
| ticks <<= tshift; | |||
| // Sample interval in CPU clock ticks. | |||
| meta->sampleInterval = ticks; | |||
| meta->cpuFrequency = F_CPU; | |||
| float sampleRate = (float)meta->cpuFrequency/meta->sampleInterval; | |||
| Serial.print(F("Sample pins:")); | |||
| for (uint8_t i = 0; i < meta->pinCount; i++) { | |||
| Serial.print(' '); | |||
| Serial.print(meta->pinNumber[i], DEC); | |||
| } | |||
| Serial.println(); | |||
| Serial.print(F("ADC bits: ")); | |||
| Serial.println(meta->recordEightBits ? 8 : 10); | |||
| Serial.print(F("ADC clock kHz: ")); | |||
| Serial.println(meta->adcFrequency/1000); | |||
| Serial.print(F("Sample Rate: ")); | |||
| Serial.println(sampleRate); | |||
| Serial.print(F("Sample interval usec: ")); | |||
| Serial.println(1000000.0/sampleRate); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // enable ADC and timer1 interrupts | |||
| void adcStart() { | |||
| // initialize ISR | |||
| adcindex = 1; | |||
| isrBuf = nullptr; | |||
| isrOver = 0; | |||
| isrStop = false; | |||
| // Clear any pending interrupt. | |||
| ADCSRA |= 1 << ADIF; | |||
| // Setup for first pin. | |||
| ADMUX = adcmux[0]; | |||
| ADCSRB = adcsrb[0]; | |||
| ADCSRA = adcsra[0]; | |||
| // Enable timer1 interrupts. | |||
| timerError = false; | |||
| timerFlag = false; | |||
| TCNT1 = 0; | |||
| TIFR1 = 1 << OCF1B; | |||
| TIMSK1 = 1 << OCIE1B; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| inline void adcStop() { | |||
| TIMSK1 = 0; | |||
| ADCSRA = 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Convert binary file to csv file. | |||
| void binaryToCsv() { | |||
| uint8_t lastPct = 0; | |||
| block_t* pd; | |||
| metadata_t* pm; | |||
| uint32_t t0 = millis(); | |||
| // Use fast buffered print class. | |||
| BufferedPrint<file_t, 64> bp(&csvFile); | |||
| block_t binBuffer[FIFO_DIM]; | |||
| assert(sizeof(block_t) == sizeof(metadata_t)); | |||
| binFile.rewind(); | |||
| uint32_t tPct = millis(); | |||
| bool doMeta = true; | |||
| while (!Serial.available()) { | |||
| pd = binBuffer; | |||
| int nb = binFile.read(binBuffer, sizeof(binBuffer)); | |||
| if (nb < 0) { | |||
| error("read binFile failed"); | |||
| } | |||
| size_t nd = nb/sizeof(block_t); | |||
| if (nd < 1) { | |||
| break; | |||
| } | |||
| if (doMeta) { | |||
| doMeta = false; | |||
| pm = (metadata_t*)pd++; | |||
| if (PIN_COUNT != pm->pinCount) { | |||
| error("Invalid pinCount"); | |||
| } | |||
| bp.print(F("Interval,")); | |||
| float intervalMicros = 1.0e6*pm->sampleInterval/(float)pm->cpuFrequency; | |||
| bp.print(intervalMicros, 4); | |||
| bp.println(F(",usec")); | |||
| for (uint8_t i = 0; i < PIN_COUNT; i++) { | |||
| if (i) { | |||
| bp.print(','); | |||
| } | |||
| bp.print(F("pin")); | |||
| bp.print(pm->pinNumber[i]); | |||
| } | |||
| bp.println(); | |||
| if (nd-- == 1) { | |||
| break; | |||
| } | |||
| } | |||
| for (size_t i = 0; i < nd; i++, pd++) { | |||
| if (pd->overrun) { | |||
| bp.print(F("OVERRUN,")); | |||
| bp.println(pd->overrun); | |||
| } | |||
| for (size_t j = 0; j < pd->count; j += PIN_COUNT) { | |||
| for (size_t i = 0; i < PIN_COUNT; i++) { | |||
| if (!bp.printField(pd->data[i + j], i == (PIN_COUNT-1) ? '\n' : ',')) { | |||
| error("printField failed"); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if ((millis() - tPct) > 1000) { | |||
| uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100); | |||
| if (pct != lastPct) { | |||
| tPct = millis(); | |||
| lastPct = pct; | |||
| Serial.print(pct, DEC); | |||
| Serial.println('%'); | |||
| } | |||
| } | |||
| } | |||
| if (!bp.sync() || !csvFile.close()) { | |||
| error("close csvFile failed"); | |||
| } | |||
| Serial.print(F("Done: ")); | |||
| Serial.print(0.001*(millis() - t0)); | |||
| Serial.println(F(" Seconds")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void createBinFile() { | |||
| binFile.close(); | |||
| while (sd.exists(binName)) { | |||
| char* p = strchr(binName, '.'); | |||
| if (!p) { | |||
| error("no dot in filename"); | |||
| } | |||
| while (true) { | |||
| p--; | |||
| if (p < binName || *p < '0' || *p > '9') { | |||
| error("Can't create file name"); | |||
| } | |||
| if (p[0] != '9') { | |||
| p[0]++; | |||
| break; | |||
| } | |||
| p[0] = '0'; | |||
| } | |||
| } | |||
| Serial.print(F("Opening: ")); | |||
| Serial.println(binName); | |||
| if (!binFile.open(binName, O_RDWR | O_CREAT)) { | |||
| error("open binName failed"); | |||
| } | |||
| Serial.print(F("Allocating: ")); | |||
| Serial.print(MAX_FILE_SIZE_MiB); | |||
| Serial.println(F(" MiB")); | |||
| if (!binFile.preAllocate(MAX_FILE_SIZE)) { | |||
| error("preAllocate failed"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool createCsvFile() { | |||
| char csvName[NAME_DIM]; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return false; | |||
| } | |||
| binFile.getName(csvName, sizeof(csvName)); | |||
| char* dot = strchr(csvName, '.'); | |||
| if (!dot) { | |||
| error("no dot in binName"); | |||
| } | |||
| strcpy(dot + 1, "csv"); | |||
| if (!csvFile.open(csvName, O_WRONLY|O_CREAT|O_TRUNC)) { | |||
| error("open csvFile failed"); | |||
| } | |||
| Serial.print(F("Writing: ")); | |||
| Serial.print(csvName); | |||
| Serial.println(F(" - type any character to stop")); | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // log data | |||
| void logData() { | |||
| uint32_t t0; | |||
| uint32_t t1; | |||
| uint32_t overruns =0; | |||
| uint32_t count = 0; | |||
| uint32_t maxLatencyUsec = 0; | |||
| size_t maxFifoUse = 0; | |||
| block_t fifoBuffer[FIFO_DIM]; | |||
| adcInit((metadata_t*)fifoBuffer); | |||
| // Write metadata. | |||
| if (sizeof(metadata_t) != binFile.write(fifoBuffer, sizeof(metadata_t))) { | |||
| error("Write metadata failed"); | |||
| } | |||
| fifoCount = 0; | |||
| fifoHead = 0; | |||
| fifoTail = 0; | |||
| fifoData = fifoBuffer; | |||
| // Initialize all blocks to save ISR overhead. | |||
| memset(fifoBuffer, 0, sizeof(fifoBuffer)); | |||
| Serial.println(F("Logging - type any character to stop")); | |||
| // Wait for Serial Idle. | |||
| Serial.flush(); | |||
| delay(10); | |||
| t0 = millis(); | |||
| t1 = t0; | |||
| // Start logging interrupts. | |||
| adcStart(); | |||
| while (1) { | |||
| uint32_t m; | |||
| noInterrupts(); | |||
| size_t tmpFifoCount = fifoCount; | |||
| interrupts(); | |||
| if (tmpFifoCount) { | |||
| block_t* pBlock = fifoData + fifoTail; | |||
| // Write block to SD. | |||
| m = micros(); | |||
| if (sizeof(block_t) != binFile.write(pBlock, sizeof(block_t))) { | |||
| error("write data failed"); | |||
| } | |||
| m = micros() - m; | |||
| t1 = millis(); | |||
| if (m > maxLatencyUsec) { | |||
| maxLatencyUsec = m; | |||
| } | |||
| if (tmpFifoCount >maxFifoUse) { | |||
| maxFifoUse = tmpFifoCount; | |||
| } | |||
| count += pBlock->count; | |||
| // Add overruns and possibly light LED. | |||
| if (pBlock->overrun) { | |||
| overruns += pBlock->overrun; | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| } | |||
| } | |||
| // Initialize empty block to save ISR overhead. | |||
| pBlock->count = 0; | |||
| pBlock->overrun = 0; | |||
| fifoTail = fifoTail < (FIFO_DIM - 1) ? fifoTail + 1 : 0; | |||
| noInterrupts(); | |||
| fifoCount--; | |||
| interrupts(); | |||
| if (binFile.curPosition() >= MAX_FILE_SIZE) { | |||
| // File full so stop ISR calls. | |||
| adcStop(); | |||
| break; | |||
| } | |||
| } | |||
| if (timerError) { | |||
| error("Missed timer event - rate too high"); | |||
| } | |||
| if (Serial.available()) { | |||
| // Stop ISR interrupts. | |||
| isrStop = true; | |||
| } | |||
| if (fifoCount == 0 && !adcActive()) { | |||
| break; | |||
| } | |||
| } | |||
| Serial.println(); | |||
| // Truncate file if recording stopped early. | |||
| if (binFile.curPosition() < MAX_FILE_SIZE) { | |||
| Serial.println(F("Truncating file")); | |||
| Serial.flush(); | |||
| if (!binFile.truncate()) { | |||
| error("Can't truncate file"); | |||
| } | |||
| } | |||
| Serial.print(F("Max write latency usec: ")); | |||
| Serial.println(maxLatencyUsec); | |||
| Serial.print(F("Record time sec: ")); | |||
| Serial.println(0.001*(t1 - t0), 3); | |||
| Serial.print(F("Sample count: ")); | |||
| Serial.println(count/PIN_COUNT); | |||
| Serial.print(F("Overruns: ")); | |||
| Serial.println(overruns); | |||
| Serial.print(F("FIFO_DIM: ")); | |||
| Serial.println(FIFO_DIM); | |||
| Serial.print(F("maxFifoUse: ")); | |||
| Serial.println(maxFifoUse + 1); // include ISR use. | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void openBinFile() { | |||
| char name[NAME_DIM]; | |||
| serialClearInput(); | |||
| Serial.println(F("Enter file name")); | |||
| if (!serialReadLine(name, sizeof(name))) { | |||
| return; | |||
| } | |||
| if (!sd.exists(name)) { | |||
| Serial.println(name); | |||
| Serial.println(F("File does not exist")); | |||
| return; | |||
| } | |||
| binFile.close(); | |||
| if (!binFile.open(name, O_RDWR)) { | |||
| Serial.println(name); | |||
| Serial.println(F("open failed")); | |||
| return; | |||
| } | |||
| Serial.println(F("File opened")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Print data file to Serial | |||
| void printData() { | |||
| block_t buf; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| binFile.rewind(); | |||
| if (binFile.read(&buf , sizeof(buf)) != sizeof(buf)) { | |||
| error("Read metadata failed"); | |||
| } | |||
| Serial.println(F("Type any character to stop")); | |||
| delay(1000); | |||
| while (!Serial.available() && | |||
| binFile.read(&buf , sizeof(buf)) == sizeof(buf)) { | |||
| if (buf.count == 0) { | |||
| break; | |||
| } | |||
| if (buf.overrun) { | |||
| Serial.print(F("OVERRUN,")); | |||
| Serial.println(buf.overrun); | |||
| } | |||
| for (size_t i = 0; i < buf.count; i++) { | |||
| Serial.print(buf.data[i], DEC); | |||
| if ((i+1)%PIN_COUNT) { | |||
| Serial.print(','); | |||
| } else { | |||
| Serial.println(); | |||
| } | |||
| } | |||
| } | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void serialClearInput() { | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool serialReadLine(char* str, size_t size) { | |||
| size_t n = 0; | |||
| while(!Serial.available()) { | |||
| } | |||
| while (true) { | |||
| int c = Serial.read(); | |||
| if (c < ' ') break; | |||
| str[n++] = c; | |||
| if (n >= size) { | |||
| Serial.println(F("input too long")); | |||
| return false; | |||
| } | |||
| uint32_t m = millis(); | |||
| while (!Serial.available() && (millis() - m) < 100){} | |||
| if (!Serial.available()) break; | |||
| } | |||
| str[n] = 0; | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| } | |||
| Serial.begin(9600); | |||
| while(!Serial) {} | |||
| Serial.println(F("Type any character to begin.")); | |||
| while(!Serial.available()) {} | |||
| FillStack(); | |||
| // Read the first sample pin to init the ADC. | |||
| analogRead(PIN_LIST[0]); | |||
| #if !ENABLE_DEDICATED_SPI | |||
| Serial.println(F( | |||
| "\nFor best performance edit SdFatConfig.h\n" | |||
| "and set ENABLE_DEDICATED_SPI nonzero")); | |||
| #endif // !ENABLE_DEDICATED_SPI | |||
| // Initialize SD. | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| error("sd.begin failed"); | |||
| } | |||
| #if USE_RTC | |||
| if (!rtc.begin()) { | |||
| error("rtc.begin failed"); | |||
| } | |||
| if (!rtc.isrunning()) { | |||
| // Set RTC to sketch compile date & time. | |||
| // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); | |||
| error("RTC is NOT running!"); | |||
| } else { | |||
| Serial.println(F("RTC is running")); | |||
| } | |||
| // Set callback | |||
| FsDateTime::setCallback(dateTime); | |||
| #endif // USE_RTC | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop(void) { | |||
| printUnusedStack(); | |||
| // Read any Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| Serial.println(); | |||
| Serial.println(F("type:")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("c - convert file to csv")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("p - print data to Serial")); | |||
| Serial.println(F("r - record ADC data")); | |||
| while(!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| char c = tolower(Serial.read()); | |||
| Serial.println(); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| } | |||
| // Read any Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| if (c == 'b') { | |||
| openBinFile(); | |||
| } else if (c == 'c') { | |||
| if (createCsvFile()) { | |||
| binaryToCsv(); | |||
| } | |||
| } else if (c == 'l') { | |||
| Serial.println(F("ls:")); | |||
| sd.ls(&Serial, LS_DATE | LS_SIZE); | |||
| } else if (c == 'p') { | |||
| printData(); | |||
| } else if (c == 'r') { | |||
| createBinFile(); | |||
| logData(); | |||
| } else { | |||
| Serial.println(F("Invalid entry")); | |||
| } | |||
| } | |||
| #else // __AVR__ | |||
| #error This program is only for AVR. | |||
| #endif // __AVR__ | |||
| @@ -0,0 +1,78 @@ | |||
| // A simple read/write example for SD.h. | |||
| // Mostly from the SD.h ReadWrite example. | |||
| // | |||
| // Your SD must be formatted FAT16/FAT32. | |||
| // | |||
| // Set USE_SD_H nonzero to use SD.h. | |||
| // Set USE_SD_H zero to use SdFat.h. | |||
| // | |||
| #define USE_SD_H 0 | |||
| // | |||
| #if USE_SD_H | |||
| #include <SD.h> | |||
| #else // USE_SD_H | |||
| #include "SdFat.h" | |||
| SdFat SD; | |||
| #endif // USE_SD_H | |||
| // Modify SD_CS_PIN for your board. | |||
| // For Teensy 3.6 and SdFat.h use BUILTIN_SDCARD. | |||
| #define SD_CS_PIN SS | |||
| File myFile; | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} | |||
| #if USE_SD_H | |||
| Serial.println(F("Using SD.h. Set USE_SD_H zero to use SdFat.h.")); | |||
| #else // USE_SD_H | |||
| Serial.println(F("Using SdFat.h. Set USE_SD_H nonzero to use SD.h.")); | |||
| #endif // USE_SD_H | |||
| Serial.println(F("\nType any character to begin.")); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| Serial.print("Initializing SD card..."); | |||
| if (!SD.begin(SD_CS_PIN)) { | |||
| Serial.println("initialization failed!"); | |||
| return; | |||
| } | |||
| Serial.println("initialization done."); | |||
| // open the file. | |||
| myFile = SD.open("test.txt", FILE_WRITE); | |||
| // if the file opened okay, write to it: | |||
| if (myFile) { | |||
| Serial.print("Writing to test.txt..."); | |||
| myFile.println("testing 1, 2, 3."); | |||
| // close the file: | |||
| myFile.close(); | |||
| Serial.println("done."); | |||
| } else { | |||
| // if the file didn't open, print an error: | |||
| Serial.println("error opening test.txt"); | |||
| } | |||
| // re-open the file for reading: | |||
| myFile = SD.open("test.txt"); | |||
| if (myFile) { | |||
| Serial.println("test.txt:"); | |||
| // read from the file until there's nothing else in it: | |||
| while (myFile.available()) { | |||
| Serial.write(myFile.read()); | |||
| } | |||
| // close the file: | |||
| myFile.close(); | |||
| } else { | |||
| // if the file didn't open, print an error: | |||
| Serial.println("error opening test.txt"); | |||
| } | |||
| } | |||
| void loop() { | |||
| // nothing happens after setup | |||
| } | |||
| @@ -0,0 +1,232 @@ | |||
| // Test and benchmark of the fast bufferedPrint class. | |||
| // | |||
| // Mainly for AVR but may improve print performance with other CPUs. | |||
| #include "SdFat.h" | |||
| #include "BufferedPrint.h" | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| typedef File file_t; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| typedef File32 file_t; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| typedef ExFile file_t; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| typedef FsFile file_t; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| // number of lines to print | |||
| const uint16_t N_PRINT = 20000; | |||
| //------------------------------------------------------------------------------ | |||
| void benchmark() { | |||
| file_t file; | |||
| BufferedPrint<file_t, 64> bp; | |||
| // do write test | |||
| Serial.println(); | |||
| for (int test = 0; test < 6; test++) { | |||
| char fileName[13] = "bench0.txt"; | |||
| fileName[5] = '0' + test; | |||
| // open or create file - truncate existing file. | |||
| if (!file.open(fileName, O_RDWR | O_CREAT | O_TRUNC)) { | |||
| sd.errorHalt(&Serial, F("open failed")); | |||
| } | |||
| if (test & 1) { | |||
| bp.begin(&file); | |||
| } | |||
| uint32_t t = millis(); | |||
| switch(test) { | |||
| case 0: | |||
| Serial.println(F("Test of println(uint16_t)")); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| file.println(i); | |||
| } | |||
| break; | |||
| case 1: | |||
| Serial.println(F("Test of printField(uint16_t, char)")); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| bp.printField(i, '\n'); | |||
| } | |||
| break; | |||
| case 2: | |||
| Serial.println(F("Test of println(uint32_t)")); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| file.println(12345678UL + i); | |||
| } | |||
| break; | |||
| case 3: | |||
| Serial.println(F("Test of printField(uint32_t, char)")); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| bp.printField(12345678UL + i, '\n'); | |||
| } | |||
| break; | |||
| case 4: | |||
| Serial.println(F("Test of println(double)")); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| file.println((double)0.01*i); | |||
| } | |||
| break; | |||
| case 5: | |||
| Serial.println(F("Test of printField(double, char)")); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| bp.printField((double)0.01*i, '\n'); | |||
| } | |||
| break; | |||
| } | |||
| if (test & 1) { | |||
| bp.sync(); | |||
| } | |||
| if (file.getWriteError()) { | |||
| sd.errorHalt(&Serial, F("write failed")); | |||
| } | |||
| double s = file.fileSize(); | |||
| file.close(); | |||
| t = millis() - t; | |||
| Serial.print(F("Time ")); | |||
| Serial.print(0.001*t, 3); | |||
| Serial.println(F(" sec")); | |||
| Serial.print(F("File size ")); | |||
| Serial.print(0.001*s); | |||
| Serial.println(F(" KB")); | |||
| Serial.print(F("Write ")); | |||
| Serial.print(s/t); | |||
| Serial.println(F(" KB/sec")); | |||
| Serial.println(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void testMemberFunctions() { | |||
| BufferedPrint<Print, 32> bp(&Serial); | |||
| char c = 'c'; // char | |||
| //#define BASIC_TYPES | |||
| #ifdef BASIC_TYPES | |||
| signed char sc = -1; // signed 8-bit | |||
| unsigned char uc = 1; // unsiged 8-bit | |||
| signed short ss = -2; // signed 16-bit | |||
| unsigned short us = 2; // unsigned 16-bit | |||
| signed long sl = -4; // signed 32-bit | |||
| unsigned long ul = 4; // unsigned 32-bit | |||
| #else // BASIC_TYPES | |||
| int8_t sc = -1; // signed 8-bit | |||
| uint8_t uc = 1; // unsiged 8-bit | |||
| int16_t ss = -2; // signed 16-bit | |||
| uint16_t us = 2; // unsigned 16-bit | |||
| int32_t sl = -4; // signed 32-bit | |||
| uint32_t ul = 4; // unsigned 32-bit | |||
| #endif // BASIC_TYPES | |||
| float f = -1.234; | |||
| double d = -5.678; | |||
| bp.println(); | |||
| bp.println("Test print()"); | |||
| bp.print(c); | |||
| bp.println(); | |||
| bp.print("string"); | |||
| bp.println(); | |||
| bp.print(F("flash")); | |||
| bp.println(); | |||
| bp.print(sc); | |||
| bp.println(); | |||
| bp.print(uc); | |||
| bp.println(); | |||
| bp.print(ss); | |||
| bp.println(); | |||
| bp.print(us); | |||
| bp.println(); | |||
| bp.print(sl); | |||
| bp.println(); | |||
| bp.print(ul); | |||
| bp.println(); | |||
| bp.print(f); | |||
| bp.println(); | |||
| bp.print(d); | |||
| bp.println(); | |||
| bp.println(); | |||
| bp.println("Test println()"); | |||
| bp.println(c); | |||
| bp.println("string"); | |||
| bp.println(F("flash")); | |||
| bp.println(sc); | |||
| bp.println(uc); | |||
| bp.println(ss); | |||
| bp.println(us); | |||
| bp.println(sl); | |||
| bp.println(ul); | |||
| bp.println(f); | |||
| bp.println(d); | |||
| bp.println(); | |||
| bp.println("Test printField()"); | |||
| bp.printField(c, ','); | |||
| bp.printField("string", ','); | |||
| bp.printField(F("flash"), ','); | |||
| bp.printField(sc, ','); | |||
| bp.printField(uc, ','); | |||
| bp.printField(ss, ','); | |||
| bp.printField(us, ','); | |||
| bp.printField(sl, ','); | |||
| bp.printField(ul, ','); | |||
| bp.printField(f, ','); | |||
| bp.printField(d, '\n'); | |||
| bp.sync(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} | |||
| Serial.println("Type any character to begin."); | |||
| while(!Serial.available()) {} | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| Serial.println(); | |||
| Serial.println(F("Test member funcions:")); | |||
| testMemberFunctions(); | |||
| Serial.println(); | |||
| Serial.println(F("Benchmark performance for uint16_t, uint32_t, and double:")); | |||
| benchmark(); | |||
| Serial.println("Done"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| } | |||
| @@ -1,59 +1,92 @@ | |||
| /* | |||
| * Example use of chdir(), ls(), mkdir(), and rmdir(). | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // SD card chip select pin. | |||
| const uint8_t chipSelect = SS; | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| //------------------------------------------------------------------------------ | |||
| // File system object. | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| // Use for file creation in folders. | |||
| SdFile file; | |||
| File file; | |||
| File root; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| File32 root; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| ExFile root; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| FsFile root; | |||
| #endif // SD_FAT_TYPE | |||
| // Create a Serial output stream. | |||
| ArduinoOutStream cout(Serial); | |||
| // Buffer for Serial input. | |||
| char cinBuf[40]; | |||
| // Create a serial input stream. | |||
| ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf)); | |||
| //============================================================================== | |||
| // Error messages stored in flash. | |||
| #define error(msg) sd.errorHalt(F(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| // Store error strings in flash to save RAM. | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| delay(1000); | |||
| cout << F("Type any character to start\n"); | |||
| // Wait for input line and discard. | |||
| cin.readline(); | |||
| cout << endl; | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| // Initialize the SD card. | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| if (sd.exists("Folder1") | |||
| if (sd.exists("Folder1") | |||
| || sd.exists("Folder1/file1.txt") | |||
| || sd.exists("Folder1/File2.txt")) { | |||
| error("Please remove existing Folder1, file1.txt, and File2.txt"); | |||
| } | |||
| int rootFileCount = 0; | |||
| sd.vwd()->rewind(); | |||
| while (file.openNext(sd.vwd(), O_RDONLY)) { | |||
| if (!root.open("/")) { | |||
| error("open root"); | |||
| } | |||
| while (file.openNext(&root, O_RDONLY)) { | |||
| if (!file.isHidden()) { | |||
| rootFileCount++; | |||
| } | |||
| @@ -113,7 +146,6 @@ void setup() { | |||
| if (!sd.rmdir("Folder1")) { | |||
| error("rmdir for Folder1 failed\n"); | |||
| } | |||
| cout << F("\nFolder1 removed.\n"); | |||
| cout << F("\nList of files on the SD.\n"); | |||
| sd.ls(LS_R); | |||
| @@ -0,0 +1,79 @@ | |||
| // Force exFAT formatting for all SD cards larger than 512MB. | |||
| #include "SdFat.h" | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Select fastest interface. | |||
| #if HAS_SDIO_CLASS | |||
| // SD config for Teensy 3.6 SDIO. | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| //#define SD_CONFIG SdioConfig(DMA_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| SdExFat sd; | |||
| //------------------------------------------------------------------------------ | |||
| void errorHalt() { | |||
| sd.printSdError(&Serial); | |||
| SysCall::halt(); | |||
| } | |||
| #define error(s) (Serial.println(F(s)),errorHalt()) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} | |||
| Serial.println(F("Type any character to begin")); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| do { | |||
| delay(10); | |||
| } while(Serial.read() >= 0); | |||
| Serial.println(); | |||
| Serial.println(F( | |||
| "Your SD will be formated exFAT.\r\n" | |||
| "All data on the SD will be lost.\r\n" | |||
| "Type 'Y' to continue.\r\n")); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| if (Serial.read() != 'Y') { | |||
| Serial.println(F("Exiting, 'Y' not typed.")); | |||
| return; | |||
| } | |||
| if (!sd.cardBegin(SD_CONFIG)) { | |||
| error("cardBegin failed"); | |||
| } | |||
| if(!sd.format(&Serial)) { | |||
| error("format failed"); | |||
| } | |||
| if (!sd.volumeBegin()) { | |||
| error("volumeBegin failed"); | |||
| } | |||
| Serial.print(F("Bytes per cluster: ")); | |||
| Serial.println(sd.bytesPerCluster()); | |||
| Serial.println(F("Done")); | |||
| } | |||
| void loop() { | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| // Avoid IDE problems by defining struct in septate .h file. | |||
| // Pad record so size is a power of two for best write performance. | |||
| #ifndef ExFatLogger_h | |||
| #define ExFatLogger_h | |||
| const size_t ADC_COUNT = 4; | |||
| struct data_t { | |||
| uint16_t adc[ADC_COUNT]; | |||
| }; | |||
| #endif // ExFatLogger_h | |||
| @@ -0,0 +1,579 @@ | |||
| // Example to demonstrate write latency for preallocated exFAT files. | |||
| // I suggest you write a PC program to convert very large bin files. | |||
| // | |||
| // If an exFAT SD is required, the ExFatFormatter example will format | |||
| // smaller cards with an exFAT file system. | |||
| // | |||
| // The maximum data rate will depend on the quality of your SD, | |||
| // the size of the FIFO, and using dedicated SPI. | |||
| #include "SdFat.h" | |||
| #include "FreeStack.h" | |||
| #include "ExFatLogger.h" | |||
| //------------------------------------------------------------------------------ | |||
| // This example was designed for exFAT but will support FAT16/FAT32. | |||
| // Note: Uno will not support SD_FAT_TYPE = 3. | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 2 | |||
| //------------------------------------------------------------------------------ | |||
| // Interval between data records in microseconds. | |||
| // Try 250 with Teensy 3.6, Due, or STM32. | |||
| // Try 2000 with AVR boards. | |||
| // Try 4000 with SAMD Zero boards. | |||
| const uint32_t LOG_INTERVAL_USEC = 2000; | |||
| // Set USE_RTC nonzero for file timestamps. | |||
| // RAM use will be marginal on Uno with RTClib. | |||
| #define USE_RTC 0 | |||
| #if USE_RTC | |||
| #include "RTClib.h" | |||
| #endif // USE_RTC | |||
| // LED to light if overruns occur. | |||
| #define ERROR_LED_PIN -1 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // FIFO SIZE - 512 byte sectors. Modify for your board. | |||
| #ifdef __AVR_ATmega328P__ | |||
| // Use 512 bytes for 328 boards. | |||
| #define FIFO_SIZE_SECTORS 1 | |||
| #elif defined(__AVR__) | |||
| // Use 2 KiB for other AVR boards. | |||
| #define FIFO_SIZE_SECTORS 4 | |||
| #else // __AVR_ATmega328P__ | |||
| // Use 8 KiB for non-AVR boards. | |||
| #define FIFO_SIZE_SECTORS 16 | |||
| #endif // __AVR_ATmega328P__ | |||
| // Preallocate 1GiB file. | |||
| const uint32_t PREALLOCATE_SIZE_MiB = 1024UL; | |||
| // Select the fastest interface. Assumes no other SPI devices. | |||
| #if ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // ENABLE_DEDICATED_SPI | |||
| // Save SRAM if 328. | |||
| #ifdef __AVR_ATmega328P__ | |||
| #include "MinimumSerial.h" | |||
| MinimumSerial MinSerial; | |||
| #define Serial MinSerial | |||
| #endif // __AVR_ATmega328P__ | |||
| //============================================================================== | |||
| // Replace logRecord(), printRecord(), and ExFatLogger.h for your sensors. | |||
| void logRecord(data_t* data, uint16_t overrun) { | |||
| if (overrun) { | |||
| // Add one since this record has no adc data. Could add overrun field. | |||
| overrun++; | |||
| data->adc[0] = 0X8000 | overrun; | |||
| } else { | |||
| for (size_t i = 0; i < ADC_COUNT; i++) { | |||
| data->adc[i] = analogRead(i); | |||
| } | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printRecord(Print* pr, data_t* data) { | |||
| static uint32_t nr = 0; | |||
| if (!data) { | |||
| pr->print(F("LOG_INTERVAL_USEC,")); | |||
| pr->println(LOG_INTERVAL_USEC); | |||
| pr->print(F("rec#")); | |||
| for (size_t i = 0; i < ADC_COUNT; i++) { | |||
| pr->print(F(",adc")); | |||
| pr->print(i); | |||
| } | |||
| pr->println(); | |||
| nr = 0; | |||
| return; | |||
| } | |||
| if (data->adc[0] & 0X8000) { | |||
| uint16_t n = data->adc[0] & 0X7FFF; | |||
| nr += n; | |||
| pr->print(F("-1,")); | |||
| pr->print(n); | |||
| pr->println(F(",overuns")); | |||
| } else { | |||
| pr->print(nr++); | |||
| for (size_t i = 0; i < ADC_COUNT; i++) { | |||
| pr->write(','); | |||
| pr->print(data->adc[i]); | |||
| } | |||
| pr->println(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const uint64_t PREALLOCATE_SIZE = (uint64_t)PREALLOCATE_SIZE_MiB << 20; | |||
| // Max length of file name including zero byte. | |||
| #define FILE_NAME_DIM 40 | |||
| // Max number of records to buffer while SD is busy. | |||
| const size_t FIFO_DIM = 512*FIFO_SIZE_SECTORS/sizeof(data_t); | |||
| #if SD_FAT_TYPE == 0 | |||
| typedef SdFat sd_t; | |||
| typedef File file_t; | |||
| #elif SD_FAT_TYPE == 1 | |||
| typedef SdFat32 sd_t; | |||
| typedef File32 file_t; | |||
| #elif SD_FAT_TYPE == 2 | |||
| typedef SdExFat sd_t; | |||
| typedef ExFile file_t; | |||
| #elif SD_FAT_TYPE == 3 | |||
| typedef SdFs sd_t; | |||
| typedef FsFile file_t; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| sd_t sd; | |||
| file_t binFile; | |||
| file_t csvFile; | |||
| // You may modify the filename. Digits before the dot are file versions. | |||
| char binName[] = "ExFatLogger00.bin"; | |||
| //------------------------------------------------------------------------------ | |||
| #if USE_RTC | |||
| RTC_DS1307 rtc; | |||
| // Call back for file timestamps. Only called for file create and sync(). | |||
| void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) { | |||
| DateTime now = rtc.now(); | |||
| // Return date using FS_DATE macro to format fields. | |||
| *date = FS_DATE(now.year(), now.month(), now.day()); | |||
| // Return time using FS_TIME macro to format fields. | |||
| *time = FS_TIME(now.hour(), now.minute(), now.second()); | |||
| // Return low time bits in units of 10 ms. | |||
| *ms10 = now.second() & 1 ? 100 : 0; | |||
| } | |||
| #endif // USE_RTC | |||
| //------------------------------------------------------------------------------ | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| #define dbgAssert(e) ((e) ? (void)0 : error("assert " #e)) | |||
| //----------------------------------------------------------------------------- | |||
| // Convert binary file to csv file. | |||
| void binaryToCsv() { | |||
| uint8_t lastPct = 0; | |||
| uint32_t t0 = millis(); | |||
| data_t binData[FIFO_DIM]; | |||
| if (!binFile.seekSet(512)) { | |||
| error("binFile.seek faile"); | |||
| } | |||
| uint32_t tPct = millis(); | |||
| printRecord(&csvFile, nullptr); | |||
| while (!Serial.available() && binFile.available()) { | |||
| int nb = binFile.read(binData, sizeof(binData)); | |||
| if (nb <= 0 ) { | |||
| error("read binFile failed"); | |||
| } | |||
| size_t nr = nb/sizeof(data_t); | |||
| for (size_t i = 0; i < nr; i++) { | |||
| printRecord(&csvFile, &binData[i]); | |||
| } | |||
| if ((millis() - tPct) > 1000) { | |||
| uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100); | |||
| if (pct != lastPct) { | |||
| tPct = millis(); | |||
| lastPct = pct; | |||
| Serial.print(pct, DEC); | |||
| Serial.println('%'); | |||
| csvFile.sync(); | |||
| } | |||
| } | |||
| if (Serial.available()) { | |||
| break; | |||
| } | |||
| } | |||
| csvFile.close(); | |||
| Serial.print(F("Done: ")); | |||
| Serial.print(0.001*(millis() - t0)); | |||
| Serial.println(F(" Seconds")); | |||
| } | |||
| //------------------------------------------------------------------------------- | |||
| void createBinFile() { | |||
| binFile.close(); | |||
| while (sd.exists(binName)) { | |||
| char* p = strchr(binName, '.'); | |||
| if (!p) { | |||
| error("no dot in filename"); | |||
| } | |||
| while (true) { | |||
| p--; | |||
| if (p < binName || *p < '0' || *p > '9') { | |||
| error("Can't create file name"); | |||
| } | |||
| if (p[0] != '9') { | |||
| p[0]++; | |||
| break; | |||
| } | |||
| p[0] = '0'; | |||
| } | |||
| } | |||
| if (!binFile.open(binName, O_RDWR | O_CREAT)) { | |||
| error("open binName failed"); | |||
| } | |||
| Serial.println(binName); | |||
| if (!binFile.preAllocate(PREALLOCATE_SIZE)) { | |||
| error("preAllocate failed"); | |||
| } | |||
| Serial.print(F("preAllocated: ")); | |||
| Serial.print(PREALLOCATE_SIZE_MiB); | |||
| Serial.println(F(" MiB")); | |||
| } | |||
| //------------------------------------------------------------------------------- | |||
| bool createCsvFile() { | |||
| char csvName[FILE_NAME_DIM]; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return false; | |||
| } | |||
| // Create a new csvFile. | |||
| binFile.getName(csvName, sizeof(csvName)); | |||
| char* dot = strchr(csvName, '.'); | |||
| if (!dot) { | |||
| error("no dot in filename"); | |||
| } | |||
| strcpy(dot + 1, "csv"); | |||
| if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) { | |||
| error("open csvFile failed"); | |||
| } | |||
| serialClearInput(); | |||
| Serial.print(F("Writing: ")); | |||
| Serial.print(csvName); | |||
| Serial.println(F(" - type any character to stop")); | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------- | |||
| void logData() { | |||
| int32_t delta; // Jitter in log time. | |||
| int32_t maxDelta = 0; | |||
| uint32_t maxLogMicros = 0; | |||
| uint32_t maxWriteMicros = 0; | |||
| size_t maxFifoUse = 0; | |||
| size_t fifoCount = 0; | |||
| size_t fifoHead = 0; | |||
| size_t fifoTail = 0; | |||
| uint16_t overrun = 0; | |||
| uint16_t maxOverrun = 0; | |||
| uint32_t totalOverrun = 0; | |||
| uint32_t fifoBuf[128*FIFO_SIZE_SECTORS]; | |||
| data_t* fifoData = (data_t*)fifoBuf; | |||
| // Write dummy sector to start multi-block write. | |||
| dbgAssert(sizeof(fifoBuf) >= 512); | |||
| memset(fifoBuf, 0, sizeof(fifoBuf)); | |||
| if (binFile.write(fifoBuf, 512) != 512) { | |||
| error("write first sector failed"); | |||
| } | |||
| serialClearInput(); | |||
| Serial.println(F("Type any character to stop")); | |||
| // Wait until SD is not busy. | |||
| while (sd.card()->isBusy()) {} | |||
| // Start time for log file. | |||
| uint32_t m = millis(); | |||
| // Time to log next record. | |||
| uint32_t logTime = micros(); | |||
| while (true) { | |||
| // Time for next data record. | |||
| logTime += LOG_INTERVAL_USEC; | |||
| // Wait until time to log data. | |||
| delta = micros() - logTime; | |||
| if (delta > 0) { | |||
| Serial.print(F("delta: ")); | |||
| Serial.println(delta); | |||
| error("Rate too fast"); | |||
| } | |||
| while (delta < 0) { | |||
| delta = micros() - logTime; | |||
| } | |||
| if (fifoCount < FIFO_DIM) { | |||
| uint32_t m = micros(); | |||
| logRecord(fifoData + fifoHead, overrun); | |||
| m = micros() - m; | |||
| if (m > maxLogMicros) { | |||
| maxLogMicros = m; | |||
| } | |||
| fifoHead = fifoHead < (FIFO_DIM - 1) ? fifoHead + 1 : 0; | |||
| fifoCount++; | |||
| if (overrun) { | |||
| if (overrun > maxOverrun) { | |||
| maxOverrun = overrun; | |||
| } | |||
| overrun = 0; | |||
| } | |||
| } else { | |||
| totalOverrun++; | |||
| overrun++; | |||
| if (overrun > 0XFFF) { | |||
| error("too many overruns"); | |||
| } | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| } | |||
| } | |||
| // Save max jitter. | |||
| if (delta > maxDelta) { | |||
| maxDelta = delta; | |||
| } | |||
| // Write data if SD is not busy. | |||
| if (!sd.card()->isBusy()) { | |||
| size_t nw = fifoHead > fifoTail ? fifoCount : FIFO_DIM - fifoTail; | |||
| // Limit write time by not writing more than 512 bytes. | |||
| const size_t MAX_WRITE = 512/sizeof(data_t); | |||
| if (nw > MAX_WRITE) nw = MAX_WRITE; | |||
| size_t nb = nw*sizeof(data_t); | |||
| uint32_t usec = micros(); | |||
| if (nb != binFile.write(fifoData + fifoTail, nb)) { | |||
| error("write binFile failed"); | |||
| } | |||
| usec = micros() - usec; | |||
| if (usec > maxWriteMicros) { | |||
| maxWriteMicros = usec; | |||
| } | |||
| fifoTail = (fifoTail + nw) < FIFO_DIM ? fifoTail + nw : 0; | |||
| if (fifoCount > maxFifoUse) { | |||
| maxFifoUse = fifoCount; | |||
| } | |||
| fifoCount -= nw; | |||
| if (Serial.available()) { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| Serial.print(F("\nLog time: ")); | |||
| Serial.print(0.001*(millis() - m)); | |||
| Serial.println(F(" Seconds")); | |||
| binFile.truncate(); | |||
| binFile.sync(); | |||
| Serial.print(("File size: ")); | |||
| // Warning cast used for print since fileSize is uint64_t. | |||
| Serial.print((uint32_t)binFile.fileSize()); | |||
| Serial.println(F(" bytes")); | |||
| Serial.print(F("totalOverrun: ")); | |||
| Serial.println(totalOverrun); | |||
| Serial.print(F("FIFO_DIM: ")); | |||
| Serial.println(FIFO_DIM); | |||
| Serial.print(F("maxFifoUse: ")); | |||
| Serial.println(maxFifoUse); | |||
| Serial.print(F("maxLogMicros: ")); | |||
| Serial.println(maxLogMicros); | |||
| Serial.print(F("maxWriteMicros: ")); | |||
| Serial.println(maxWriteMicros); | |||
| Serial.print(F("Log interval: ")); | |||
| Serial.print(LOG_INTERVAL_USEC); | |||
| Serial.print(F(" micros\nmaxDelta: ")); | |||
| Serial.print(maxDelta); | |||
| Serial.println(F(" micros")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void openBinFile() { | |||
| char name[FILE_NAME_DIM]; | |||
| serialClearInput(); | |||
| Serial.println(F("Enter file name")); | |||
| if (!serialReadLine(name, sizeof(name))) { | |||
| return; | |||
| } | |||
| if (!sd.exists(name)) { | |||
| Serial.println(name); | |||
| Serial.println(F("File does not exist")); | |||
| return; | |||
| } | |||
| binFile.close(); | |||
| if (!binFile.open(name, O_RDONLY)) { | |||
| Serial.println(name); | |||
| Serial.println(F("open failed")); | |||
| return; | |||
| } | |||
| Serial.println(F("File opened")); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void printData() { | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| // Skip first dummy sector. | |||
| if (!binFile.seekSet(512)) { | |||
| error("seek failed"); | |||
| } | |||
| serialClearInput(); | |||
| Serial.println(F("type any character to stop\n")); | |||
| delay(1000); | |||
| printRecord(&Serial, nullptr); | |||
| while (binFile.available() && !Serial.available()) { | |||
| data_t record; | |||
| if (binFile.read(&record, sizeof(data_t)) != sizeof(data_t)) { | |||
| error("read binFile failed"); | |||
| } | |||
| printRecord(&Serial, &record); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printUnusedStack() { | |||
| #if HAS_UNUSED_STACK | |||
| Serial.print(F("\nUnused stack: ")); | |||
| Serial.println(UnusedStack()); | |||
| #endif // HAS_UNUSED_STACK | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void serialClearInput() { | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool serialReadLine(char* str, size_t size) { | |||
| size_t n = 0; | |||
| while(!Serial.available()) { | |||
| yield(); | |||
| } | |||
| while (true) { | |||
| int c = Serial.read(); | |||
| if (c < ' ') break; | |||
| str[n++] = c; | |||
| if (n >= size) { | |||
| Serial.println(F("input too long")); | |||
| return false; | |||
| } | |||
| uint32_t m = millis(); | |||
| while (!Serial.available() && (millis() - m) < 100){} | |||
| if (!Serial.available()) break; | |||
| } | |||
| str[n] = 0; | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void testSensor() { | |||
| const uint32_t interval = 200000; | |||
| int32_t diff; | |||
| data_t data; | |||
| serialClearInput(); | |||
| Serial.println(F("\nTesting - type any character to stop\n")); | |||
| delay(1000); | |||
| printRecord(&Serial, nullptr); | |||
| uint32_t m = micros(); | |||
| while (!Serial.available()) { | |||
| m += interval; | |||
| do { | |||
| diff = m - micros(); | |||
| } while (diff > 0); | |||
| logRecord(&data, 0); | |||
| printRecord(&Serial, &data); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| } | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| delay(1000); | |||
| Serial.println(F("Type any character to begin")); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| FillStack(); | |||
| #if !ENABLE_DEDICATED_SPI | |||
| Serial.println(F( | |||
| "\nFor best performance edit SdFatConfig.h\n" | |||
| "and set ENABLE_DEDICATED_SPI nonzero")); | |||
| #endif // !ENABLE_DEDICATED_SPI | |||
| Serial.print(FIFO_DIM); | |||
| Serial.println(F(" FIFO entries will be used.")); | |||
| // Initialize SD. | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| #if USE_RTC | |||
| if (!rtc.begin()) { | |||
| error("rtc.begin failed"); | |||
| } | |||
| if (!rtc.isrunning()) { | |||
| // Set RTC to sketch compile date & time. | |||
| // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); | |||
| error("RTC is NOT running!"); | |||
| } | |||
| // Set callback | |||
| FsDateTime::setCallback(dateTime); | |||
| #endif // USE_RTC | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| printUnusedStack(); | |||
| // Read any Serial data. | |||
| serialClearInput(); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| } | |||
| Serial.println(); | |||
| Serial.println(F("type: ")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("c - convert file to csv")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("p - print data to Serial")); | |||
| Serial.println(F("r - record data")); | |||
| Serial.println(F("t - test without logging")); | |||
| while(!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| char c = tolower(Serial.read()); | |||
| Serial.println(); | |||
| if (c == 'b') { | |||
| openBinFile(); | |||
| } else if (c == 'c') { | |||
| if (createCsvFile()) { | |||
| binaryToCsv(); | |||
| } | |||
| } else if (c == 'l') { | |||
| Serial.println(F("ls:")); | |||
| sd.ls(&Serial, LS_DATE | LS_SIZE); | |||
| } else if (c == 'p') { | |||
| printData(); | |||
| } else if (c == 'r') { | |||
| createBinFile(); | |||
| logData(); | |||
| } else if (c == 't') { | |||
| testSensor(); | |||
| } else { | |||
| Serial.println(F("Invalid entry")); | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| // Simple test of Unicode file name. | |||
| // Note: Unicode is only supported by the SdExFat class. | |||
| // No exFAT functions will be defined for char* paths. | |||
| // The SdFs class cannot be used. | |||
| #include "SdFat.h" | |||
| #if USE_UNICODE_NAMES | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Use SPI, SD_CS_PIN, SHARED_SPI, FULL_SPEED. | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN) | |||
| SdExFat sd; | |||
| ExFile file; | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) { | |||
| yield(); | |||
| } | |||
| Serial.println("Type any character to begin"); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| if (!file.open(u"Euros \u20AC test.txt", FILE_WRITE)) { | |||
| Serial.println("file.open failed"); | |||
| return; | |||
| } | |||
| file.println("This is not Unicode"); | |||
| file.close(); | |||
| Serial.println("Done!"); | |||
| } | |||
| void loop() { | |||
| } | |||
| #else // USE_UNICODE_NAMES | |||
| #error USE_UNICODE_NAMES must be nonzero in SdFat/src/ExFatLib/ExFatCongfig.h | |||
| #endif // USE_UNICODE_NAMES | |||
| @@ -1,16 +1,59 @@ | |||
| /* | |||
| * Print size, modify date/time, and name for all files in root. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| // SD default chip select pin. | |||
| const uint8_t chipSelect = SS; | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // file system object | |||
| SdFat sd; | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| SdFile file; | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File dir; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 dir; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile dir; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile dir; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| //------------------------------------------------------------------------------ | |||
| // Store error strings in flash to save RAM. | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| @@ -25,17 +68,18 @@ void setup() { | |||
| SysCall::yield(); | |||
| } | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| // Initialize the SD. | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| // Open next file in root. The volume working directory, vwd, is root. | |||
| // Warning, openNext starts at the current position of sd.vwd() so a | |||
| // rewind may be neccessary in your application. | |||
| sd.vwd()->rewind(); | |||
| while (file.openNext(sd.vwd(), O_RDONLY)) { | |||
| // Open root directory | |||
| if (!dir.open("/")){ | |||
| error("dir.open failed"); | |||
| } | |||
| // Open next file in root. | |||
| // Warning, openNext starts at the current position of dir so a | |||
| // rewind may be necessary in your application. | |||
| while (file.openNext(&dir, O_RDONLY)) { | |||
| file.printFileSize(&Serial); | |||
| Serial.write(' '); | |||
| file.printModifyDateTime(&Serial); | |||
| @@ -48,7 +92,11 @@ void setup() { | |||
| Serial.println(); | |||
| file.close(); | |||
| } | |||
| Serial.println("Done!"); | |||
| if (dir.getError()) { | |||
| Serial.println("openNext failed"); | |||
| } else { | |||
| Serial.println("Done!"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| void loop() {} | |||
| @@ -3,20 +3,36 @@ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 3 | |||
| // | |||
| // Set DISABLE_CHIP_SELECT to disable a second SPI device. | |||
| // For example, with the Ethernet shield, set DISABLE_CHIP_SELECT | |||
| // to 10 to disable the Ethernet controller. | |||
| const int8_t DISABLE_CHIP_SELECT = -1; | |||
| // | |||
| // Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select | |||
| // Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select | |||
| // the highest speed supported by the board that is not over 4 MHz. | |||
| // Change SPI_SPEED to SD_SCK_MHZ(50) for best performance. | |||
| #define SPI_SPEED SD_SCK_MHZ(4) | |||
| //------------------------------------------------------------------------------ | |||
| // File system object. | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| // Serial streams | |||
| ArduinoOutStream cout(Serial); | |||
| @@ -40,8 +56,8 @@ void reformatMsg() { | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| @@ -120,18 +136,13 @@ void loop() { | |||
| reformatMsg(); | |||
| return; | |||
| } | |||
| if (!sd.vwd()->isOpen()) { | |||
| cout << F("Can't open root directory.\n"); | |||
| reformatMsg(); | |||
| return; | |||
| } | |||
| cout << F("Can't determine error type\n"); | |||
| return; | |||
| } | |||
| cout << F("\nCard successfully initialized.\n"); | |||
| cout << endl; | |||
| uint32_t size = sd.card()->cardSize(); | |||
| uint32_t size = sd.card()->sectorCount(); | |||
| if (size == 0) { | |||
| cout << F("Can't determine the card size.\n"); | |||
| cardOrSpeed(); | |||
| @@ -142,13 +153,13 @@ void loop() { | |||
| cout << F(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << endl; | |||
| cout << F("Volume is FAT") << int(sd.vol()->fatType()); | |||
| cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster(); | |||
| cout << F(", Cluster size (bytes): ") << sd.vol()->bytesPerCluster(); | |||
| cout << endl << endl; | |||
| cout << F("Files found (date time size name):\n"); | |||
| sd.ls(LS_R | LS_DATE | LS_SIZE); | |||
| if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64) | |||
| if ((sizeMB > 1100 && sd.vol()->sectorsPerCluster() < 64) | |||
| || (sizeMB < 2200 && sd.vol()->fatType() == 32)) { | |||
| cout << F("\nThis card should be reformatted for best performance.\n"); | |||
| cout << F("Use a cluster size of 32 KB for cards larger than 1 GB.\n"); | |||
| @@ -0,0 +1,153 @@ | |||
| #include "SdFat.h" | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| char line[40]; | |||
| //------------------------------------------------------------------------------ | |||
| // Store error strings in flash to save RAM. | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| // Check for extra characters in field or find minus sign. | |||
| char* skipSpace(char* str) { | |||
| while (isspace(*str)) str++; | |||
| return str; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool parseLine(char* str) { | |||
| char* ptr; | |||
| // Set strtok start of line. | |||
| str = strtok(str, ","); | |||
| if (!str) return false; | |||
| // Print text field. | |||
| Serial.println(str); | |||
| // Subsequent calls to strtok expects a null pointer. | |||
| str = strtok(nullptr, ","); | |||
| if (!str) return false; | |||
| // Convert string to long integer. | |||
| int32_t i32 = strtol(str, &ptr, 0); | |||
| if (str == ptr || *skipSpace(ptr)) return false; | |||
| Serial.println(i32); | |||
| str = strtok(nullptr, ","); | |||
| if (!str) return false; | |||
| // strtoul accepts a leading minus with unexpected results. | |||
| if (*skipSpace(str) == '-') return false; | |||
| // Convert string to unsigned long integer. | |||
| uint32_t u32 = strtoul(str, &ptr, 0); | |||
| if (str == ptr || *skipSpace(ptr)) return false; | |||
| Serial.println(u32); | |||
| str = strtok(nullptr, ","); | |||
| if (!str) return false; | |||
| // Convert string to double. | |||
| double d = strtod(str, &ptr); | |||
| if (str == ptr || *skipSpace(ptr)) return false; | |||
| Serial.println(d); | |||
| // Check for extra fields. | |||
| return strtok(nullptr, ",") == nullptr; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| yield(); | |||
| } | |||
| Serial.println("Type any character to start"); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| // Initialize the SD. | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| return; | |||
| } | |||
| // Remove any existing file. | |||
| if (sd.exists("ReadCsvDemo.csv")) { | |||
| sd.remove("ReadCsvDemo.csv"); | |||
| } | |||
| // Create the file. | |||
| if (!file.open("ReadCsvDemo.csv", FILE_WRITE)) { | |||
| error("open failed"); | |||
| } | |||
| // Write test data. | |||
| file.print(F( | |||
| "abc,123,456,7.89\r\n" | |||
| "def,-321,654,-9.87\r\n" | |||
| "ghi,333,0xff,5.55")); | |||
| // Rewind file for read. | |||
| file.rewind(); | |||
| while (file.available()) { | |||
| int n = file.fgets(line, sizeof(line)); | |||
| if (n <= 0) { | |||
| error("fgets failed"); | |||
| } | |||
| if (line[n-1] != '\n' && n == (sizeof(line) - 1)) { | |||
| error("line too long"); | |||
| } | |||
| if (!parseLine(line)) { | |||
| error("parseLine failed"); | |||
| } | |||
| Serial.println(); | |||
| } | |||
| file.close(); | |||
| Serial.println(F("Done")); | |||
| } | |||
| void loop() { | |||
| } | |||
| @@ -0,0 +1,141 @@ | |||
| // Test of time-stamp callback. | |||
| // Set the callback with this statement. | |||
| // FsDateTime::setCallback(dateTime); | |||
| #include "SdFat.h" | |||
| // https://github.com/adafruit/RTClib | |||
| #include "RTClib.h" | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| RTC_DS1307 rtc; | |||
| //------------------------------------------------------------------------------ | |||
| // Call back for file timestamps. Only called for file create and sync(). | |||
| void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) { | |||
| DateTime now = rtc.now(); | |||
| // Return date using FS_DATE macro to format fields. | |||
| *date = FS_DATE(now.year(), now.month(), now.day()); | |||
| // Return time using FS_TIME macro to format fields. | |||
| *time = FS_TIME(now.hour(), now.minute(), now.second()); | |||
| // Return low time bits in units of 10 ms, 0 <= ms10 <= 199. | |||
| *ms10 = now.second() & 1 ? 100 : 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printField(Print* pr, char sep, uint8_t v) { | |||
| if (sep) { | |||
| pr->write(sep); | |||
| } | |||
| if (v < 10) { | |||
| pr->write('0'); | |||
| } | |||
| pr->print(v); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printNow(Print* pr) { | |||
| DateTime now = rtc.now(); | |||
| pr->print(now.year()); | |||
| printField(pr, '-',now.month()); | |||
| printField(pr, '-',now.day()); | |||
| printField(pr, ' ',now.hour()); | |||
| printField(pr, ':',now.minute()); | |||
| printField(pr, ':',now.second()); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) { | |||
| yield(); | |||
| } | |||
| Serial.println(F("Type any character to begin")); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| if (!rtc.begin()) { | |||
| Serial.println(F("rtc.begin failed")); | |||
| return; | |||
| } | |||
| if (!rtc.isrunning()) { | |||
| Serial.println("RTC is NOT running!"); | |||
| return; | |||
| // following line sets the RTC to the date & time this sketch was compiled | |||
| // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); | |||
| // This line sets the RTC with an explicit date & time, for example to set | |||
| // January 21, 2014 at 3am you would call: | |||
| // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); | |||
| } | |||
| Serial.print(F("DateTime::now ")); | |||
| printNow(&Serial); | |||
| Serial.println(); | |||
| // Set callback | |||
| FsDateTime::setCallback(dateTime); | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| // Remove old version to set create time. | |||
| if (sd.exists("RtcTest.txt")) { | |||
| sd.remove("RtcTest.txt"); | |||
| } | |||
| if (!file.open("RtcTest.txt", FILE_WRITE)) { | |||
| Serial.println(F("file.open failed")); | |||
| return; | |||
| } | |||
| // Print current date time to file. | |||
| file.print(F("Test file at: ")); | |||
| printNow(&file); | |||
| file.println(); | |||
| file.close(); | |||
| // List files in SD root. | |||
| sd.ls(LS_DATE | LS_SIZE); | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| } | |||
| @@ -6,19 +6,17 @@ | |||
| #include "SdFat.h" | |||
| #include "FreeStack.h" | |||
| // set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes | |||
| // Use first SPI port | |||
| SdFat sd1; | |||
| // SdFatEX sd1; | |||
| const uint8_t SD1_CS = PA4; // chip select for sd1 | |||
| // Use second SPI port | |||
| SPIClass SPI_2(2); | |||
| SdFat sd2(&SPI_2); | |||
| // SdFatEX sd2(&SPI_2); | |||
| const uint8_t SD2_CS = PB12; // chip select for sd2 | |||
| // Chip select PA4, shared SPI, 18 MHz, port 1. | |||
| #define SD1_CONFIG SdSpiConfig(PA4, SHARED_SPI, SD_SCK_MHZ(18), &SPI) | |||
| SdFs sd1; | |||
| FsFile file1; | |||
| // Use mySPI2 since SPI2 is used in SPI.h as a different type. | |||
| static SPIClass mySPI2(2); | |||
| // Chip select PB21, dedicated SPI, 18 MHz, port 2. | |||
| #define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2) | |||
| SdFs sd2; | |||
| FsFile file2; | |||
| const uint8_t BUF_DIM = 100; | |||
| uint8_t buf[BUF_DIM]; | |||
| @@ -28,14 +26,22 @@ const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||
| //------------------------------------------------------------------------------ | |||
| // print error msg, any SD error codes, and halt. | |||
| // store messages in flash | |||
| #define errorExit(msg) errorHalt(F(msg)) | |||
| #define initError(msg) initErrorHalt(F(msg)) | |||
| #define error(msg) {Serial.println(msg); errorHalt();} | |||
| void errorHalt() { | |||
| if (sd1.sdErrorCode()) { | |||
| sd1.errorHalt(); | |||
| } | |||
| sd2.errorHalt(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // fill buffer with known data | |||
| for (size_t i = 0; i < sizeof(buf); i++) { | |||
| @@ -44,86 +50,72 @@ void setup() { | |||
| Serial.println(F("type any character to start")); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // initialize the first card | |||
| if (!sd1.begin(SD1_CS, SD_SCK_MHZ(18))) { | |||
| sd1.initError("sd1:"); | |||
| if (!sd1.begin(SD1_CONFIG)) { | |||
| error("sd1.begin"); | |||
| } | |||
| // create Dir1 on sd1 if it does not exist | |||
| if (!sd1.exists("/Dir1")) { | |||
| if (!sd1.mkdir("/Dir1")) { | |||
| sd1.errorExit("sd1.mkdir"); | |||
| error("sd1.mkdir"); | |||
| } | |||
| } | |||
| // Make Dir1 the working directory on sd1. | |||
| if (!sd1.chdir("Dir1")) { | |||
| error("dsd1.chdir"); | |||
| } | |||
| // initialize the second card | |||
| if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) { | |||
| sd2.initError("sd2:"); | |||
| if (!sd2.begin(SD2_CONFIG)) { | |||
| error("sd2.begin"); | |||
| } | |||
| // create Dir2 on sd2 if it does not exist | |||
| if (!sd2.exists("/Dir2")) { | |||
| if (!sd2.mkdir("/Dir2")) { | |||
| sd2.errorExit("sd2.mkdir"); | |||
| error("sd2.mkdir"); | |||
| } | |||
| } | |||
| // list root directory on both cards | |||
| Serial.println(F("------sd1 root-------")); | |||
| sd1.ls(); | |||
| Serial.println(F("------sd2 root-------")); | |||
| sd2.ls(); | |||
| // make /Dir1 the default directory for sd1 | |||
| if (!sd1.chdir("/Dir1")) { | |||
| sd1.errorExit("sd1.chdir"); | |||
| } | |||
| // Make Dir2 the working directory on sd2. | |||
| if (!sd2.chdir("Dir2")) { | |||
| error("sd2.chdir"); | |||
| } | |||
| // remove test.bin from /Dir1 directory of sd1 | |||
| if (sd1.exists("test.bin")) { | |||
| if (!sd1.remove("test.bin")) { | |||
| sd2.errorExit("remove test.bin"); | |||
| error("remove test.bin"); | |||
| } | |||
| } | |||
| // make /Dir2 the default directory for sd2 | |||
| if (!sd2.chdir("/Dir2")) { | |||
| sd2.errorExit("sd2.chdir"); | |||
| } | |||
| // remove rename.bin from /Dir2 directory of sd2 | |||
| if (sd2.exists("rename.bin")) { | |||
| if (!sd2.remove("rename.bin")) { | |||
| sd2.errorExit("remove rename.bin"); | |||
| error("remove rename.bin"); | |||
| } | |||
| } | |||
| // list current directory on both cards | |||
| // list directories. | |||
| Serial.println(F("------sd1 Dir1-------")); | |||
| sd1.ls(); | |||
| sd1.ls("/", LS_R | LS_SIZE); | |||
| Serial.println(F("------sd2 Dir2-------")); | |||
| sd2.ls(); | |||
| sd2.ls("/", LS_R | LS_SIZE); | |||
| Serial.println(F("---------------------")); | |||
| // set the current working directory for open() to sd1 | |||
| sd1.chvol(); | |||
| // create or open /Dir1/test.bin and truncate it to zero length | |||
| SdFile file1; | |||
| if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||
| sd1.errorExit("file1"); | |||
| if (!file1.open(&sd1, "test.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||
| error("file1.open"); | |||
| } | |||
| Serial.println(F("Writing test.bin to sd1")); | |||
| // write data to /Dir1/test.bin on sd1 | |||
| for (uint16_t i = 0; i < NWRITE; i++) { | |||
| if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| sd1.errorExit("sd1.write"); | |||
| error("file1.write"); | |||
| } | |||
| } | |||
| // set the current working directory for open() to sd2 | |||
| sd2.chvol(); | |||
| // create or open /Dir2/copy.bin and truncate it to zero length | |||
| SdFile file2; | |||
| if (!file2.open("copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) { | |||
| sd2.errorExit("file2"); | |||
| if (!file2.open(&sd2, "copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) { | |||
| error("file2.open"); | |||
| } | |||
| Serial.println(F("Copying test.bin to copy.bin")); | |||
| @@ -134,13 +126,13 @@ void setup() { | |||
| while (1) { | |||
| int n = file1.read(buf, sizeof(buf)); | |||
| if (n < 0) { | |||
| sd1.errorExit("read1"); | |||
| error("file1.read"); | |||
| } | |||
| if (n == 0) { | |||
| break; | |||
| } | |||
| if ((int)file2.write(buf, n) != n) { | |||
| sd2.errorExit("write2"); | |||
| error("file2.write"); | |||
| } | |||
| } | |||
| t = millis() - t; | |||
| @@ -151,23 +143,25 @@ void setup() { | |||
| Serial.println(F(" millis")); | |||
| // close test.bin | |||
| file1.close(); | |||
| // sync copy.bin so ls works. | |||
| file2.close(); | |||
| // list current directory on both cards | |||
| // list directories. | |||
| Serial.println(F("------sd1 -------")); | |||
| sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| sd1.ls("/", LS_R | LS_SIZE); | |||
| Serial.println(F("------sd2 -------")); | |||
| sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| sd2.ls("/", LS_R | LS_SIZE); | |||
| Serial.println(F("---------------------")); | |||
| Serial.println(F("Renaming copy.bin")); | |||
| // rename the copy | |||
| // Rename copy.bin. The renamed file will be in Dir2. | |||
| if (!sd2.rename("copy.bin", "rename.bin")) { | |||
| sd2.errorExit("sd2.rename"); | |||
| error("rename copy.bin"); | |||
| } | |||
| // list current directory on both cards | |||
| file2.close(); | |||
| // list directories. | |||
| Serial.println(F("------sd1 -------")); | |||
| sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| sd1.ls("/", LS_R | LS_SIZE); | |||
| Serial.println(F("------sd2 -------")); | |||
| sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| sd2.ls("/", LS_R | LS_SIZE); | |||
| Serial.println(F("---------------------")); | |||
| Serial.println(F("Done")); | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| // Print a list of error codes, symbols, and comments. | |||
| #include "SdFat.h" | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} | |||
| delay(1000); | |||
| Serial.println(); | |||
| Serial.println(F("Code,Symbol - failed operation")); | |||
| for (uint8_t code = 0; code <= SD_CARD_ERROR_UNKNOWN; code++) { | |||
| Serial.print(code < 16 ? "0X0" : "0X"); | |||
| Serial.print(code, HEX); | |||
| Serial.print(","); | |||
| printSdErrorSymbol(&Serial, code); | |||
| Serial.print(" - "); | |||
| printSdErrorText(&Serial, code); | |||
| Serial.println(); | |||
| } | |||
| } | |||
| void loop() {} | |||
| @@ -1,411 +1,76 @@ | |||
| /* | |||
| * This program will format an SD or SDHC card. | |||
| * This program will format SD/SDHC/SDXC cards. | |||
| * Warning all data will be deleted! | |||
| * | |||
| * For SD/SDHC cards larger than 64 MB this | |||
| * program attempts to match the format | |||
| * This program attempts to match the format | |||
| * generated by SDFormatter available here: | |||
| * | |||
| * http://www.sdcard.org/consumers/formatter/ | |||
| * | |||
| * For smaller cards this program uses FAT16 | |||
| * and SDFormatter uses FAT12. | |||
| * For very small cards this program uses FAT16 | |||
| * and the above SDFormatter uses FAT12. | |||
| */ | |||
| // Set USE_SDIO to zero for SPI card access. | |||
| #define USE_SDIO 0 | |||
| // | |||
| // Change the value of chipSelect if your hardware does | |||
| // not use the default value, SS. Common values are: | |||
| // Arduino Ethernet shield: pin 4 | |||
| // Sparkfun SD shield: pin 8 | |||
| // Adafruit SD shields and modules: pin 10 | |||
| const uint8_t chipSelect = SS; | |||
| // Initialize at highest supported speed not over 50 MHz. | |||
| // Reduce max speed if errors occur. | |||
| #define SPI_SPEED SD_SCK_MHZ(50) | |||
| // Print extra info for debug if DEBUG_PRINT is nonzero | |||
| #define DEBUG_PRINT 0 | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| #if DEBUG_PRINT | |||
| #include "FreeStack.h" | |||
| #endif // DEBUG_PRINT | |||
| /* | |||
| Set DISABLE_CS_PIN to disable a second SPI device. | |||
| For example, with the Ethernet shield, set DISABLE_CS_PIN | |||
| to 10 to disable the Ethernet controller. | |||
| */ | |||
| const int8_t DISABLE_CS_PIN = -1; | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI | |||
| and your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| //============================================================================== | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| #if USE_SDIO | |||
| // Use faster SdioCardEX | |||
| SdioCardEX card; | |||
| // SdioCard card; | |||
| #else // USE_SDIO | |||
| Sd2Card card; | |||
| #endif // USE_SDIO | |||
| uint32_t cardSizeBlocks; | |||
| uint32_t cardCapacityMB; | |||
| // cache for SD block | |||
| cache_t cache; | |||
| // MBR information | |||
| uint8_t partType; | |||
| uint32_t relSector; | |||
| uint32_t partSize; | |||
| // Fake disk geometry | |||
| uint8_t numberOfHeads; | |||
| uint8_t sectorsPerTrack; | |||
| // FAT parameters | |||
| uint16_t reservedSectors; | |||
| uint8_t sectorsPerCluster; | |||
| uint32_t fatStart; | |||
| uint32_t fatSize; | |||
| uint32_t dataStart; | |||
| // constants for file system structure | |||
| uint16_t const BU16 = 128; | |||
| uint16_t const BU32 = 8192; | |||
| // strings needed in file system structures | |||
| char noName[] = "NO NAME "; | |||
| char fat16str[] = "FAT16 "; | |||
| char fat32str[] = "FAT32 "; | |||
| //------------------------------------------------------------------------------ | |||
| #define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();} | |||
| //------------------------------------------------------------------------------ | |||
| void sdErrorHalt() { | |||
| if (card.errorCode()) { | |||
| cout << F("SD error: ") << hex << int(card.errorCode()); | |||
| cout << ',' << int(card.errorData()) << dec << endl; | |||
| } | |||
| SysCall::halt(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| #if DEBUG_PRINT | |||
| void debugPrint() { | |||
| cout << F("FreeStack: ") << FreeStack() << endl; | |||
| cout << F("partStart: ") << relSector << endl; | |||
| cout << F("partSize: ") << partSize << endl; | |||
| cout << F("reserved: ") << reservedSectors << endl; | |||
| cout << F("fatStart: ") << fatStart << endl; | |||
| cout << F("fatSize: ") << fatSize << endl; | |||
| cout << F("dataStart: ") << dataStart << endl; | |||
| cout << F("clusterCount: "); | |||
| cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl; | |||
| cout << endl; | |||
| cout << F("Heads: ") << int(numberOfHeads) << endl; | |||
| cout << F("Sectors: ") << int(sectorsPerTrack) << endl; | |||
| cout << F("Cylinders: "); | |||
| cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl; | |||
| } | |||
| #endif // DEBUG_PRINT | |||
| //------------------------------------------------------------------------------ | |||
| // write cached block to the card | |||
| uint8_t writeCache(uint32_t lbn) { | |||
| return card.writeBlock(lbn, cache.data); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // initialize appropriate sizes for SD capacity | |||
| void initSizes() { | |||
| if (cardCapacityMB <= 6) { | |||
| sdError("Card is too small."); | |||
| } else if (cardCapacityMB <= 16) { | |||
| sectorsPerCluster = 2; | |||
| } else if (cardCapacityMB <= 32) { | |||
| sectorsPerCluster = 4; | |||
| } else if (cardCapacityMB <= 64) { | |||
| sectorsPerCluster = 8; | |||
| } else if (cardCapacityMB <= 128) { | |||
| sectorsPerCluster = 16; | |||
| } else if (cardCapacityMB <= 1024) { | |||
| sectorsPerCluster = 32; | |||
| } else if (cardCapacityMB <= 32768) { | |||
| sectorsPerCluster = 64; | |||
| } else { | |||
| // SDXC cards | |||
| sectorsPerCluster = 128; | |||
| } | |||
| cout << F("Blocks/Cluster: ") << int(sectorsPerCluster) << endl; | |||
| // set fake disk geometry | |||
| sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63; | |||
| if (cardCapacityMB <= 16) { | |||
| numberOfHeads = 2; | |||
| } else if (cardCapacityMB <= 32) { | |||
| numberOfHeads = 4; | |||
| } else if (cardCapacityMB <= 128) { | |||
| numberOfHeads = 8; | |||
| } else if (cardCapacityMB <= 504) { | |||
| numberOfHeads = 16; | |||
| } else if (cardCapacityMB <= 1008) { | |||
| numberOfHeads = 32; | |||
| } else if (cardCapacityMB <= 2016) { | |||
| numberOfHeads = 64; | |||
| } else if (cardCapacityMB <= 4032) { | |||
| numberOfHeads = 128; | |||
| } else { | |||
| numberOfHeads = 255; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // zero cache and optionally set the sector signature | |||
| void clearCache(uint8_t addSig) { | |||
| memset(&cache, 0, sizeof(cache)); | |||
| if (addSig) { | |||
| cache.mbr.mbrSig0 = BOOTSIG0; | |||
| cache.mbr.mbrSig1 = BOOTSIG1; | |||
| } | |||
| } | |||
| uint32_t cardSectorCount = 0; | |||
| uint8_t sectorBuffer[512]; | |||
| //------------------------------------------------------------------------------ | |||
| // zero FAT and root dir area on SD | |||
| void clearFatDir(uint32_t bgn, uint32_t count) { | |||
| clearCache(false); | |||
| #if USE_SDIO | |||
| for (uint32_t i = 0; i < count; i++) { | |||
| if (!card.writeBlock(bgn + i, cache.data)) { | |||
| sdError("Clear FAT/DIR writeBlock failed"); | |||
| } | |||
| if ((i & 0XFF) == 0) { | |||
| cout << '.'; | |||
| } | |||
| } | |||
| #else // USE_SDIO | |||
| if (!card.writeStart(bgn, count)) { | |||
| sdError("Clear FAT/DIR writeStart failed"); | |||
| } | |||
| for (uint32_t i = 0; i < count; i++) { | |||
| if ((i & 0XFF) == 0) { | |||
| cout << '.'; | |||
| } | |||
| if (!card.writeData(cache.data)) { | |||
| sdError("Clear FAT/DIR writeData failed"); | |||
| } | |||
| } | |||
| if (!card.writeStop()) { | |||
| sdError("Clear FAT/DIR writeStop failed"); | |||
| } | |||
| #endif // USE_SDIO | |||
| cout << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // return cylinder number for a logical block number | |||
| uint16_t lbnToCylinder(uint32_t lbn) { | |||
| return lbn / (numberOfHeads * sectorsPerTrack); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // return head number for a logical block number | |||
| uint8_t lbnToHead(uint32_t lbn) { | |||
| return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // return sector number for a logical block number | |||
| uint8_t lbnToSector(uint32_t lbn) { | |||
| return (lbn % sectorsPerTrack) + 1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // format and write the Master Boot Record | |||
| void writeMbr() { | |||
| clearCache(true); | |||
| part_t* p = cache.mbr.part; | |||
| p->boot = 0; | |||
| uint16_t c = lbnToCylinder(relSector); | |||
| if (c > 1023) { | |||
| sdError("MBR CHS"); | |||
| } | |||
| p->beginCylinderHigh = c >> 8; | |||
| p->beginCylinderLow = c & 0XFF; | |||
| p->beginHead = lbnToHead(relSector); | |||
| p->beginSector = lbnToSector(relSector); | |||
| p->type = partType; | |||
| uint32_t endLbn = relSector + partSize - 1; | |||
| c = lbnToCylinder(endLbn); | |||
| if (c <= 1023) { | |||
| p->endCylinderHigh = c >> 8; | |||
| p->endCylinderLow = c & 0XFF; | |||
| p->endHead = lbnToHead(endLbn); | |||
| p->endSector = lbnToSector(endLbn); | |||
| } else { | |||
| // Too big flag, c = 1023, h = 254, s = 63 | |||
| p->endCylinderHigh = 3; | |||
| p->endCylinderLow = 255; | |||
| p->endHead = 254; | |||
| p->endSector = 63; | |||
| } | |||
| p->firstSector = relSector; | |||
| p->totalSectors = partSize; | |||
| if (!writeCache(0)) { | |||
| sdError("write MBR"); | |||
| } | |||
| } | |||
| // SdCardFactory constructs and initializes the appropriate card. | |||
| SdCardFactory cardFactory; | |||
| // Pointer to generic SD card. | |||
| SdCard* m_card = nullptr; | |||
| //------------------------------------------------------------------------------ | |||
| // generate serial number from card size and micros since boot | |||
| uint32_t volSerialNumber() { | |||
| return (cardSizeBlocks << 8) + micros(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // format the SD as FAT16 | |||
| void makeFat16() { | |||
| uint32_t nc; | |||
| for (dataStart = 2 * BU16;; dataStart += BU16) { | |||
| nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; | |||
| fatSize = (nc + 2 + 255)/256; | |||
| uint32_t r = BU16 + 1 + 2 * fatSize + 32; | |||
| if (dataStart < r) { | |||
| continue; | |||
| } | |||
| relSector = dataStart - r + BU16; | |||
| break; | |||
| } | |||
| // check valid cluster count for FAT16 volume | |||
| if (nc < 4085 || nc >= 65525) { | |||
| sdError("Bad cluster count"); | |||
| } | |||
| reservedSectors = 1; | |||
| fatStart = relSector + reservedSectors; | |||
| partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32; | |||
| if (partSize < 32680) { | |||
| partType = 0X01; | |||
| } else if (partSize < 65536) { | |||
| partType = 0X04; | |||
| } else { | |||
| partType = 0X06; | |||
| } | |||
| // write MBR | |||
| writeMbr(); | |||
| clearCache(true); | |||
| fat_boot_t* pb = &cache.fbs; | |||
| pb->jump[0] = 0XEB; | |||
| pb->jump[1] = 0X00; | |||
| pb->jump[2] = 0X90; | |||
| for (uint8_t i = 0; i < sizeof(pb->oemId); i++) { | |||
| pb->oemId[i] = ' '; | |||
| } | |||
| pb->bytesPerSector = 512; | |||
| pb->sectorsPerCluster = sectorsPerCluster; | |||
| pb->reservedSectorCount = reservedSectors; | |||
| pb->fatCount = 2; | |||
| pb->rootDirEntryCount = 512; | |||
| pb->mediaType = 0XF8; | |||
| pb->sectorsPerFat16 = fatSize; | |||
| pb->sectorsPerTrack = sectorsPerTrack; | |||
| pb->headCount = numberOfHeads; | |||
| pb->hidddenSectors = relSector; | |||
| pb->totalSectors32 = partSize; | |||
| pb->driveNumber = 0X80; | |||
| pb->bootSignature = EXTENDED_BOOT_SIG; | |||
| pb->volumeSerialNumber = volSerialNumber(); | |||
| memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel)); | |||
| memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType)); | |||
| // write partition boot sector | |||
| if (!writeCache(relSector)) { | |||
| sdError("FAT16 write PBS failed"); | |||
| } | |||
| // clear FAT and root directory | |||
| clearFatDir(fatStart, dataStart - fatStart); | |||
| clearCache(false); | |||
| cache.fat16[0] = 0XFFF8; | |||
| cache.fat16[1] = 0XFFFF; | |||
| // write first block of FAT and backup for reserved clusters | |||
| if (!writeCache(fatStart) | |||
| || !writeCache(fatStart + fatSize)) { | |||
| sdError("FAT16 reserve failed"); | |||
| } | |||
| } | |||
| #define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();} | |||
| //------------------------------------------------------------------------------ | |||
| // format the SD as FAT32 | |||
| void makeFat32() { | |||
| uint32_t nc; | |||
| relSector = BU32; | |||
| for (dataStart = 2 * BU32;; dataStart += BU32) { | |||
| nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; | |||
| fatSize = (nc + 2 + 127)/128; | |||
| uint32_t r = relSector + 9 + 2 * fatSize; | |||
| if (dataStart >= r) { | |||
| break; | |||
| void sdErrorHalt() { | |||
| if (!m_card) { | |||
| cout << F("Invalid SD_CONFIG") << endl; | |||
| } else if (m_card->errorCode()) { | |||
| if (m_card->errorCode() == SD_CARD_ERROR_CMD0) { | |||
| cout << F("No card, wrong chip select pin, or wiring error?") << endl; | |||
| } | |||
| cout << F("SD errorCode: ") << hex << showbase; | |||
| printSdErrorSymbol(&Serial, m_card->errorCode()); | |||
| cout << F(" = ") << int(m_card->errorCode()) << endl; | |||
| cout << F("SD errorData = ") << int(m_card->errorData()) << endl; | |||
| } | |||
| // error if too few clusters in FAT32 volume | |||
| if (nc < 65525) { | |||
| sdError("Bad cluster count"); | |||
| } | |||
| reservedSectors = dataStart - relSector - 2 * fatSize; | |||
| fatStart = relSector + reservedSectors; | |||
| partSize = nc * sectorsPerCluster + dataStart - relSector; | |||
| // type depends on address of end sector | |||
| // max CHS has lbn = 16450560 = 1024*255*63 | |||
| if ((relSector + partSize) <= 16450560) { | |||
| // FAT32 | |||
| partType = 0X0B; | |||
| } else { | |||
| // FAT32 with INT 13 | |||
| partType = 0X0C; | |||
| } | |||
| writeMbr(); | |||
| clearCache(true); | |||
| fat32_boot_t* pb = &cache.fbs32; | |||
| pb->jump[0] = 0XEB; | |||
| pb->jump[1] = 0X00; | |||
| pb->jump[2] = 0X90; | |||
| for (uint8_t i = 0; i < sizeof(pb->oemId); i++) { | |||
| pb->oemId[i] = ' '; | |||
| } | |||
| pb->bytesPerSector = 512; | |||
| pb->sectorsPerCluster = sectorsPerCluster; | |||
| pb->reservedSectorCount = reservedSectors; | |||
| pb->fatCount = 2; | |||
| pb->mediaType = 0XF8; | |||
| pb->sectorsPerTrack = sectorsPerTrack; | |||
| pb->headCount = numberOfHeads; | |||
| pb->hidddenSectors = relSector; | |||
| pb->totalSectors32 = partSize; | |||
| pb->sectorsPerFat32 = fatSize; | |||
| pb->fat32RootCluster = 2; | |||
| pb->fat32FSInfo = 1; | |||
| pb->fat32BackBootBlock = 6; | |||
| pb->driveNumber = 0X80; | |||
| pb->bootSignature = EXTENDED_BOOT_SIG; | |||
| pb->volumeSerialNumber = volSerialNumber(); | |||
| memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel)); | |||
| memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType)); | |||
| // write partition boot sector and backup | |||
| if (!writeCache(relSector) | |||
| || !writeCache(relSector + 6)) { | |||
| sdError("FAT32 write PBS failed"); | |||
| } | |||
| clearCache(true); | |||
| // write extra boot area and backup | |||
| if (!writeCache(relSector + 2) | |||
| || !writeCache(relSector + 8)) { | |||
| sdError("FAT32 PBS ext failed"); | |||
| } | |||
| fat32_fsinfo_t* pf = &cache.fsinfo; | |||
| pf->leadSignature = FSINFO_LEAD_SIG; | |||
| pf->structSignature = FSINFO_STRUCT_SIG; | |||
| pf->freeCount = 0XFFFFFFFF; | |||
| pf->nextFree = 0XFFFFFFFF; | |||
| // write FSINFO sector and backup | |||
| if (!writeCache(relSector + 1) | |||
| || !writeCache(relSector + 7)) { | |||
| sdError("FAT32 FSINFO failed"); | |||
| } | |||
| clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster); | |||
| clearCache(false); | |||
| cache.fat32[0] = 0x0FFFFFF8; | |||
| cache.fat32[1] = 0x0FFFFFFF; | |||
| cache.fat32[2] = 0x0FFFFFFF; | |||
| // write first block of FAT and backup for reserved clusters | |||
| if (!writeCache(fatStart) | |||
| || !writeCache(fatStart + fatSize)) { | |||
| sdError("FAT32 reserve failed"); | |||
| } | |||
| SysCall::halt(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // flash erase all data | |||
| @@ -418,54 +83,73 @@ void eraseCard() { | |||
| do { | |||
| lastBlock = firstBlock + ERASE_SIZE - 1; | |||
| if (lastBlock >= cardSizeBlocks) { | |||
| lastBlock = cardSizeBlocks - 1; | |||
| if (lastBlock >= cardSectorCount) { | |||
| lastBlock = cardSectorCount - 1; | |||
| } | |||
| if (!card.erase(firstBlock, lastBlock)) { | |||
| if (!m_card->erase(firstBlock, lastBlock)) { | |||
| sdError("erase failed"); | |||
| } | |||
| cout << '.'; | |||
| if ((n++)%32 == 31) { | |||
| if ((n++)%64 == 63) { | |||
| cout << endl; | |||
| } | |||
| firstBlock += ERASE_SIZE; | |||
| } while (firstBlock < cardSizeBlocks); | |||
| } while (firstBlock < cardSectorCount); | |||
| cout << endl; | |||
| if (!card.readBlock(0, cache.data)) { | |||
| if (!m_card->readSector(0, sectorBuffer)) { | |||
| sdError("readBlock"); | |||
| } | |||
| cout << hex << showbase << setfill('0') << internal; | |||
| cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl; | |||
| cout << F("All data set to ") << setw(4) << int(sectorBuffer[0]) << endl; | |||
| cout << dec << noshowbase << setfill(' ') << right; | |||
| cout << F("Erase done\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void formatCard() { | |||
| cout << endl; | |||
| cout << F("Formatting\n"); | |||
| initSizes(); | |||
| if (card.type() != SD_CARD_TYPE_SDHC) { | |||
| cout << F("FAT16\n"); | |||
| makeFat16(); | |||
| ExFatFormatter exFatFormatter; | |||
| FatFormatter fatFormatter; | |||
| // Format exFAT if larger than 32GB. | |||
| bool rtn = cardSectorCount > 67108864 ? | |||
| exFatFormatter.format(m_card, sectorBuffer, &Serial) : | |||
| fatFormatter.format(m_card, sectorBuffer, &Serial); | |||
| if (!rtn) { | |||
| sdErrorHalt(); | |||
| } | |||
| cout << F("Run the SdInfo example for format details.") << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printConfig(SdSpiConfig config) { | |||
| if (DISABLE_CS_PIN < 0) { | |||
| cout << F( | |||
| "\nAssuming the SD is the only SPI device.\n" | |||
| "Edit DISABLE_CS_PIN to disable an SPI device.\n"); | |||
| } else { | |||
| cout << F("FAT32\n"); | |||
| makeFat32(); | |||
| cout << F("\nDisabling SPI device on pin "); | |||
| cout << int(DISABLE_CS_PIN) << endl; | |||
| pinMode(DISABLE_CS_PIN, OUTPUT); | |||
| digitalWrite(DISABLE_CS_PIN, HIGH); | |||
| } | |||
| #if DEBUG_PRINT | |||
| debugPrint(); | |||
| #endif // DEBUG_PRINT | |||
| cout << F("Format done\n"); | |||
| cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin); | |||
| cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printConfig(SdioConfig config) { | |||
| (void)config; | |||
| cout << F("Assuming an SDIO interface.\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| char c; | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| cout << F("Type any character to start\n"); | |||
| printConfig(SD_CONFIG); | |||
| cout << F("\nType any character to start\n"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| @@ -475,21 +159,21 @@ void setup() { | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| cout << F( | |||
| "\n" | |||
| "This program can erase and/or format SD/SDHC cards.\n" | |||
| "This program can erase and/or format SD/SDHC/SDXC cards.\n" | |||
| "\n" | |||
| "Erase uses the card's fast flash erase command.\n" | |||
| "Flash erase sets all data to 0X00 for most cards\n" | |||
| "and 0XFF for a few vendor's cards.\n" | |||
| "\n" | |||
| "Cards larger than 2 GB will be formatted FAT32 and\n" | |||
| "smaller cards will be formatted FAT16.\n" | |||
| "Cards up to 2 GiB (GiB = 2^30 bytes) will be formated FAT16.\n" | |||
| "Cards larger than 2 GiB and up to 32 GiB will be formatted\n" | |||
| "FAT32. Cards larger than 32 GiB will be formatted exFAT.\n" | |||
| "\n" | |||
| "Warning, all data on the card will be erased.\n" | |||
| "Enter 'Y' to continue: "); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| c = Serial.read(); | |||
| cout << c << endl; | |||
| if (c != 'Y') { | |||
| @@ -501,6 +185,32 @@ void setup() { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| // Select and initialize proper card driver. | |||
| m_card = cardFactory.newCard(SD_CONFIG); | |||
| if (!m_card || m_card->errorCode()) { | |||
| sdError("card init failed."); | |||
| return; | |||
| } | |||
| cardSectorCount = m_card->sectorCount(); | |||
| if (!cardSectorCount) { | |||
| sdError("Get sector count failed."); | |||
| return; | |||
| } | |||
| cout << F("\nCard size: ") << cardSectorCount*5.12e-7; | |||
| cout << F(" GB (GB = 1E9 bytes)\n"); | |||
| cout << F("Card size: ") << cardSectorCount/2097152.0; | |||
| cout << F(" GiB (GiB = 2^30 bytes)\n"); | |||
| cout << F("Card will be formated "); | |||
| if (cardSectorCount > 67108864) { | |||
| cout << F("exFAT\n"); | |||
| } else if (cardSectorCount > 4194304) { | |||
| cout << F("FAT32\n"); | |||
| } else { | |||
| cout << F("FAT16\n"); | |||
| } | |||
| cout << F( | |||
| "\n" | |||
| "Options are:\n" | |||
| @@ -519,28 +229,6 @@ void setup() { | |||
| cout << F("Quiting, invalid option entered.") << endl; | |||
| return; | |||
| } | |||
| #if USE_SDIO | |||
| if (!card.begin()) { | |||
| sdError("card.begin failed"); | |||
| } | |||
| #else // USE_SDIO | |||
| if (!card.begin(chipSelect, SPI_SPEED)) { | |||
| cout << F( | |||
| "\nSD initialization failure!\n" | |||
| "Is the SD card inserted correctly?\n" | |||
| "Is chip select correct at the top of this program?\n"); | |||
| sdError("card.begin failed"); | |||
| } | |||
| #endif | |||
| cardSizeBlocks = card.cardSize(); | |||
| if (cardSizeBlocks == 0) { | |||
| sdError("cardSize"); | |||
| } | |||
| cardCapacityMB = (cardSizeBlocks + 2047)/2048; | |||
| cout << F("Card Size: ") << setprecision(0) << 1.048576*cardCapacityMB; | |||
| cout << F(" MB, (MB = 1,000,000 bytes)") << endl; | |||
| if (c == 'E' || c == 'F') { | |||
| eraseCard(); | |||
| } | |||
| @@ -548,5 +236,5 @@ void setup() { | |||
| formatCard(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| void loop() { | |||
| } | |||
| @@ -1,93 +1,81 @@ | |||
| /* | |||
| * This program attempts to initialize an SD card and analyze its structure. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // Set USE_SDIO to zero for SPI card access. | |||
| #define USE_SDIO 0 | |||
| /* | |||
| * SD chip select pin. Common values are: | |||
| * | |||
| * Arduino Ethernet shield, pin 4. | |||
| * SparkFun SD shield, pin 8. | |||
| * Adafruit SD shields and modules, pin 10. | |||
| * Default SD chip select is the SPI SS pin. | |||
| */ | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| Set DISABLE_CS_PIN to disable a second SPI device. | |||
| For example, with the Ethernet shield, set DISABLE_CS_PIN | |||
| to 10 to disable the Ethernet controller. | |||
| */ | |||
| const int8_t DISABLE_CS_PIN = -1; | |||
| /* | |||
| * Set DISABLE_CHIP_SELECT to disable a second SPI device. | |||
| * For example, with the Ethernet shield, set DISABLE_CHIP_SELECT | |||
| * to 10 to disable the Ethernet controller. | |||
| */ | |||
| const int8_t DISABLE_CHIP_SELECT = -1; | |||
| #if USE_SDIO | |||
| // Use faster SdioCardEX | |||
| SdFatSdioEX sd; | |||
| // SdFatSdio sd; | |||
| #else // USE_SDIO | |||
| SdFat sd; | |||
| #endif // USE_SDIO | |||
| Change the value of SD_CS_PIN if you are using SPI | |||
| and your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // serial output steam | |||
| ArduinoOutStream cout(Serial); | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| // global for card size | |||
| uint32_t cardSize; | |||
| // global for card erase size | |||
| uint32_t eraseSize; | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash | |||
| #define sdErrorMsg(msg) sd.errorPrint(F(msg)); | |||
| SdFs sd; | |||
| cid_t m_cid; | |||
| csd_t m_csd; | |||
| uint32_t m_eraseSize; | |||
| uint32_t m_ocr; | |||
| static ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| uint8_t cidDmp() { | |||
| cid_t cid; | |||
| if (!sd.card()->readCID(&cid)) { | |||
| sdErrorMsg("readCID failed"); | |||
| return false; | |||
| } | |||
| bool cidDmp() { | |||
| cout << F("\nManufacturer ID: "); | |||
| cout << hex << int(cid.mid) << dec << endl; | |||
| cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl; | |||
| cout << uppercase << showbase << hex << int(m_cid.mid) << dec << endl; | |||
| cout << F("OEM ID: ") << m_cid.oid[0] << m_cid.oid[1] << endl; | |||
| cout << F("Product: "); | |||
| for (uint8_t i = 0; i < 5; i++) { | |||
| cout << cid.pnm[i]; | |||
| cout << m_cid.pnm[i]; | |||
| } | |||
| cout << F("\nVersion: "); | |||
| cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl; | |||
| cout << F("Serial number: ") << hex << cid.psn << dec << endl; | |||
| cout << int(m_cid.prv_n) << '.' << int(m_cid.prv_m) << endl; | |||
| cout << F("Serial number: ") << hex << m_cid.psn << dec << endl; | |||
| cout << F("Manufacturing date: "); | |||
| cout << int(cid.mdt_month) << '/'; | |||
| cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl; | |||
| cout << int(m_cid.mdt_month) << '/'; | |||
| cout << (2000 + m_cid.mdt_year_low + 10 * m_cid.mdt_year_high) << endl; | |||
| cout << endl; | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| uint8_t csdDmp() { | |||
| csd_t csd; | |||
| uint8_t eraseSingleBlock; | |||
| if (!sd.card()->readCSD(&csd)) { | |||
| sdErrorMsg("readCSD failed"); | |||
| return false; | |||
| } | |||
| if (csd.v1.csd_ver == 0) { | |||
| eraseSingleBlock = csd.v1.erase_blk_en; | |||
| eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; | |||
| } else if (csd.v2.csd_ver == 1) { | |||
| eraseSingleBlock = csd.v2.erase_blk_en; | |||
| eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low; | |||
| bool csdDmp() { | |||
| bool eraseSingleBlock; | |||
| if (m_csd.v1.csd_ver == 0) { | |||
| eraseSingleBlock = m_csd.v1.erase_blk_en; | |||
| m_eraseSize = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low; | |||
| } else if (m_csd.v2.csd_ver == 1) { | |||
| eraseSingleBlock = m_csd.v2.erase_blk_en; | |||
| m_eraseSize = (m_csd.v2.sector_size_high << 1) | m_csd.v2.sector_size_low; | |||
| } else { | |||
| cout << F("csd version error\n"); | |||
| cout << F("m_csd version error\n"); | |||
| return false; | |||
| } | |||
| eraseSize++; | |||
| cout << F("cardSize: ") << 0.000512*cardSize; | |||
| m_eraseSize++; | |||
| cout << F("cardSize: ") << 0.000512 * sdCardCapacity(&m_csd); | |||
| cout << F(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n"); | |||
| cout << F("flashEraseSize: ") << int(m_eraseSize) << F(" blocks\n"); | |||
| cout << F("eraseSingleBlock: "); | |||
| if (eraseSingleBlock) { | |||
| cout << F("true\n"); | |||
| @@ -97,77 +85,123 @@ uint8_t csdDmp() { | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // print partition table | |||
| uint8_t partDmp() { | |||
| mbr_t mbr; | |||
| if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) { | |||
| sdErrorMsg("read MBR failed"); | |||
| void errorPrint() { | |||
| if (sd.sdErrorCode()) { | |||
| cout << F("SD errorCode: ") << hex << showbase; | |||
| printSdErrorSymbol(&Serial, sd.sdErrorCode()); | |||
| cout << F(" = ") << int(sd.sdErrorCode()) << endl; | |||
| cout << F("SD errorData = ") << int(sd.sdErrorData()) << endl; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool mbrDmp() { | |||
| MbrSector_t mbr; | |||
| bool valid = true; | |||
| if (!sd.card()->readSector(0, (uint8_t*)&mbr)) { | |||
| cout << F("\nread MBR failed.\n"); | |||
| errorPrint(); | |||
| return false; | |||
| } | |||
| cout << F("\nSD Partition Table\n"); | |||
| cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n"); | |||
| for (uint8_t ip = 1; ip < 5; ip++) { | |||
| part_t *pt = &mbr.part[ip - 1]; | |||
| if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { | |||
| cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | |||
| return true; | |||
| MbrPart_t *pt = &mbr.part[ip - 1]; | |||
| if ((pt->boot != 0 && pt->boot != 0X80) || | |||
| getLe32(pt->relativeSectors) > sdCardCapacity(&m_csd)) { | |||
| valid = false; | |||
| } | |||
| cout << int(ip) << ',' << uppercase << showbase << hex; | |||
| cout << int(pt->boot) << ','; | |||
| for (int i = 0; i < 3; i++ ) { | |||
| cout << int(pt->beginCHS[i]) << ','; | |||
| } | |||
| cout << int(pt->type) << ','; | |||
| for (int i = 0; i < 3; i++ ) { | |||
| cout << int(pt->endCHS[i]) << ','; | |||
| } | |||
| cout << dec << getLe32(pt->relativeSectors) << ','; | |||
| cout << getLe32(pt->totalSectors) << endl; | |||
| } | |||
| cout << F("\nSD Partition Table\n"); | |||
| cout << F("part,boot,type,start,length\n"); | |||
| for (uint8_t ip = 1; ip < 5; ip++) { | |||
| part_t *pt = &mbr.part[ip - 1]; | |||
| cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | |||
| cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | |||
| if (!valid) { | |||
| cout << F("\nMBR not valid, assuming Super Floppy format.\n"); | |||
| } | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void volDmp() { | |||
| cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl; | |||
| cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl; | |||
| cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl; | |||
| cout << F("freeClusters: "); | |||
| uint32_t volFree = sd.vol()->freeClusterCount(); | |||
| cout << volFree << endl; | |||
| float fs = 0.000512*volFree*sd.vol()->blocksPerCluster(); | |||
| cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl; | |||
| cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl; | |||
| cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl; | |||
| cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl; | |||
| cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl; | |||
| if (sd.vol()->dataStartBlock() % eraseSize) { | |||
| cout << F("Data area is not aligned on flash erase boundaries!\n"); | |||
| void dmpVol() { | |||
| cout << F("\nScanning FAT, please wait.\n"); | |||
| uint32_t freeClusterCount = sd.freeClusterCount(); | |||
| if (sd.fatType() <= 32) { | |||
| cout << F("\nVolume is FAT") << int(sd.fatType()) << endl; | |||
| } else { | |||
| cout << F("\nVolume is exFAT\n"); | |||
| } | |||
| cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl; | |||
| cout << F("clusterCount: ") << sd.clusterCount() << endl; | |||
| cout << F("freeClusterCount: ") << freeClusterCount << endl; | |||
| cout << F("fatStartSector: ") << sd.fatStartSector() << endl; | |||
| cout << F("dataStartSector: ") << sd.dataStartSector() << endl; | |||
| if (sd.dataStartSector() % m_eraseSize) { | |||
| cout << F("Data area is not aligned on flash erase boundary!\n"); | |||
| cout << F("Download and use formatter from www.sdcard.org!\n"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| void printCardType() { | |||
| // use uppercase in hex and use 0X base prefix | |||
| cout << uppercase << showbase << endl; | |||
| cout << F("\nCard type: "); | |||
| // F stores strings in flash to save RAM | |||
| cout << F("SdFat version: ") << SD_FAT_VERSION << endl; | |||
| #if !USE_SDIO | |||
| if (DISABLE_CHIP_SELECT < 0) { | |||
| switch (sd.card()->type()) { | |||
| case SD_CARD_TYPE_SD1: | |||
| cout << F("SD1\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SD2: | |||
| cout << F("SD2\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SDHC: | |||
| if (sdCardCapacity(&m_csd) < 70000000) { | |||
| cout << F("SDHC\n"); | |||
| } else { | |||
| cout << F("SDXC\n"); | |||
| } | |||
| break; | |||
| default: | |||
| cout << F("Unknown\n"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printConfig(SdSpiConfig config) { | |||
| if (DISABLE_CS_PIN < 0) { | |||
| cout << F( | |||
| "\nAssuming the SD is the only SPI device.\n" | |||
| "Edit DISABLE_CHIP_SELECT to disable another device.\n"); | |||
| "Edit DISABLE_CS_PIN to disable an SPI device.\n"); | |||
| } else { | |||
| cout << F("\nDisabling SPI device on pin "); | |||
| cout << int(DISABLE_CHIP_SELECT) << endl; | |||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | |||
| cout << int(DISABLE_CS_PIN) << endl; | |||
| pinMode(DISABLE_CS_PIN, OUTPUT); | |||
| digitalWrite(DISABLE_CS_PIN, HIGH); | |||
| } | |||
| cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin); | |||
| cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printConfig(SdioConfig config) { | |||
| (void)config; | |||
| cout << F("Assuming an SDIO interface.\n"); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| cout << F("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT); | |||
| cout << F("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n"); | |||
| #endif // !USE_SDIO | |||
| cout << F("SdFat version: ") << SD_FAT_VERSION << endl; | |||
| printConfig(SD_CONFIG); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| @@ -181,68 +215,44 @@ void loop() { | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| uint32_t t = millis(); | |||
| #if USE_SDIO | |||
| if (!sd.cardBegin()) { | |||
| sdErrorMsg("\ncardBegin failed"); | |||
| return; | |||
| } | |||
| #else // USE_SDIO | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||
| sdErrorMsg("cardBegin failed"); | |||
| if (!sd.cardBegin(SD_CONFIG)) { | |||
| cout << F( | |||
| "\nSD initialization failed.\n" | |||
| "Do not reformat the card!\n" | |||
| "Is the card correctly inserted?\n" | |||
| "Is there a wiring/soldering problem?\n"); | |||
| if (isSpi(SD_CONFIG)) { | |||
| cout << F( | |||
| "Is SD_CS_PIN set to the correct value?\n" | |||
| "Does another SPI device need to be disabled?\n" | |||
| ); | |||
| } | |||
| errorPrint(); | |||
| return; | |||
| } | |||
| #endif // USE_SDIO | |||
| t = millis() - t; | |||
| cout << F("init time: ") << t << " ms" << endl; | |||
| cardSize = sd.card()->cardSize(); | |||
| if (cardSize == 0) { | |||
| sdErrorMsg("cardSize failed"); | |||
| if (!sd.card()->readCID(&m_cid) || | |||
| !sd.card()->readCSD(&m_csd) || | |||
| !sd.card()->readOCR(&m_ocr)) { | |||
| cout << F("readInfo failed\n"); | |||
| errorPrint(); | |||
| return; | |||
| } | |||
| cout << F("\ninit time: ") << t << " ms" << endl; | |||
| cout << F("\nCard type: "); | |||
| switch (sd.card()->type()) { | |||
| case SD_CARD_TYPE_SD1: | |||
| cout << F("SD1\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SD2: | |||
| cout << F("SD2\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SDHC: | |||
| if (cardSize < 70000000) { | |||
| cout << F("SDHC\n"); | |||
| } else { | |||
| cout << F("SDXC\n"); | |||
| } | |||
| break; | |||
| default: | |||
| cout << F("Unknown\n"); | |||
| } | |||
| if (!cidDmp()) { | |||
| printCardType(); | |||
| cidDmp(); | |||
| csdDmp(); | |||
| cout << F("\nOCR: ") << uppercase << showbase; | |||
| cout << hex << m_ocr << dec << endl; | |||
| if (!mbrDmp()) { | |||
| return; | |||
| } | |||
| if (!csdDmp()) { | |||
| if (!sd.volumeBegin()) { | |||
| cout << F("\nvolumeBegin failed. Is the card formatted?\n"); | |||
| errorPrint(); | |||
| return; | |||
| } | |||
| uint32_t ocr; | |||
| if (!sd.card()->readOCR(&ocr)) { | |||
| sdErrorMsg("\nreadOCR failed"); | |||
| return; | |||
| } | |||
| cout << F("OCR: ") << hex << ocr << dec << endl; | |||
| if (!partDmp()) { | |||
| return; | |||
| } | |||
| if (!sd.fsBegin()) { | |||
| sdErrorMsg("\nFile System initialization failed.\n"); | |||
| return; | |||
| } | |||
| volDmp(); | |||
| } | |||
| dmpVol(); | |||
| } | |||
| @@ -1,25 +1,44 @@ | |||
| // An example of the SdFatSoftSpi template class. | |||
| // This example is for an Adafruit Data Logging Shield on a Mega. | |||
| // An example of the SoftSpiDriver template class. | |||
| // This example is for an old Adafruit Data Logging Shield on a Mega. | |||
| // Software SPI is required on Mega since this shield connects to pins 10-13. | |||
| // This example will also run on an Uno and other boards using software SPI. | |||
| // | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h | |||
| #if SPI_DRIVER_SELECT == 2 // Must be set in SdFat/SdFatConfig.h | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| // | |||
| // Chip select may be constant or RAM variable. | |||
| const uint8_t SD_CS_PIN = 10; | |||
| // | |||
| // Pin numbers in templates must be constants. | |||
| const uint8_t SOFT_MISO_PIN = 12; | |||
| const uint8_t SOFT_MOSI_PIN = 11; | |||
| const uint8_t SOFT_SCK_PIN = 13; | |||
| // | |||
| // Chip select may be constant or RAM variable. | |||
| const uint8_t SD_CHIP_SELECT_PIN = 10; | |||
| // SdFat software SPI template | |||
| SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd; | |||
| SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi; | |||
| // Speed argument is ignored for software SPI. | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi) | |||
| // Test file. | |||
| SdFile file; | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| @@ -32,11 +51,11 @@ void setup() { | |||
| SysCall::yield(); | |||
| } | |||
| if (!sd.begin(SD_CHIP_SELECT_PIN)) { | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| if (!file.open("SoftSPI.txt", O_RDWR O _CREAT)) { | |||
| if (!file.open("SoftSPI.txt", O_RDWR | O_CREAT)) { | |||
| sd.errorHalt(F("open failed")); | |||
| } | |||
| file.println(F("This line was printed using software SPI.")); | |||
| @@ -53,6 +72,6 @@ void setup() { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| #else // ENABLE_SOFTWARE_SPI_CLASS | |||
| #error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h | |||
| #endif //ENABLE_SOFTWARE_SPI_CLASS | |||
| #else // SPI_DRIVER_SELECT | |||
| #error SPI_DRIVER_SELECT must be two in SdFat/SdFatConfig.h | |||
| #endif //SPI_DRIVER_SELECT | |||
| @@ -0,0 +1,136 @@ | |||
| // Test of time-stamp callback with Teensy3. | |||
| // The upload time will be used to set the RTC. | |||
| // You must arrange for syncing the RTC. | |||
| #include "SdFat.h" | |||
| #include <TimeLib.h> | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 3 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| //------------------------------------------------------------------------------ | |||
| // Call back for file timestamps. Only called for file create and sync(). | |||
| void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) { | |||
| // Return date using FS_DATE macro to format fields. | |||
| *date = FS_DATE(year(), month(), day()); | |||
| // Return time using FS_TIME macro to format fields. | |||
| *time = FS_TIME(hour(), minute(), second()); | |||
| // Return low time bits in units of 10 ms. | |||
| *ms10 = second() & 1 ? 100 : 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| time_t getTeensy3Time() | |||
| { | |||
| return Teensy3Clock.get(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printField(Print* pr, char sep, uint8_t v) { | |||
| if (sep) { | |||
| pr->write(sep); | |||
| } | |||
| if (v < 10) { | |||
| pr->write('0'); | |||
| } | |||
| pr->print(v); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void printNow(Print* pr) { | |||
| pr->print(year()); | |||
| printField(pr, '-', month()); | |||
| printField(pr, '-', day()); | |||
| printField(pr, ' ', hour()); | |||
| printField(pr, ':', minute()); | |||
| printField(pr, ':', second()); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| // set the Time library to use Teensy 3.0's RTC to keep time | |||
| setSyncProvider(getTeensy3Time); | |||
| Serial.begin(9600); | |||
| while (!Serial) { | |||
| yield(); | |||
| } | |||
| Serial.println(F("Type any character to begin")); | |||
| while (!Serial.available()) { | |||
| yield(); | |||
| } | |||
| if (timeStatus()!= timeSet) { | |||
| Serial.println("Unable to sync with the RTC"); | |||
| return; | |||
| } | |||
| Serial.print(F("DateTime::now ")); | |||
| printNow(&Serial); | |||
| Serial.println(); | |||
| // Set callback | |||
| FsDateTime::setCallback(dateTime); | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| // Remove old version to set create time. | |||
| if (sd.exists("RtcTest.txt")) { | |||
| sd.remove("RtcTest.txt"); | |||
| } | |||
| if (!file.open("RtcTest.txt", FILE_WRITE)) { | |||
| Serial.println(F("file.open failed")); | |||
| return; | |||
| } | |||
| // Print current date time to file. | |||
| file.print(F("Test file at: ")); | |||
| printNow(&file); | |||
| file.println(); | |||
| file.close(); | |||
| // List files in SD root. | |||
| sd.ls(LS_DATE | LS_SIZE); | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| } | |||
| @@ -1,23 +1,33 @@ | |||
| // Simple performance test for Teensy 3.5/3.6 SDHC. | |||
| // Demonstrates yield() efficiency. | |||
| // Warning SdFatSdio and SdFatSdioEX normally should | |||
| // not both be used in a program. | |||
| // Each has its own cache and member variables. | |||
| // Demonstrates yield() efficiency for SDIO modes. | |||
| // Uses built-in SD for SPI modes. | |||
| #include "SdFat.h" | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 3 | |||
| // 32 KiB buffer. | |||
| const size_t BUF_DIM = 32768; | |||
| // 8 MiB file. | |||
| const uint32_t FILE_SIZE = 256UL*BUF_DIM; | |||
| SdFatSdio sd; | |||
| SdFatSdioEX sdEx; | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| uint8_t buf[BUF_DIM]; | |||
| @@ -32,24 +42,25 @@ uint32_t yieldMicros = 0; | |||
| uint32_t yieldCalls = 0; | |||
| // Max busy time for single yield call. | |||
| uint32_t yieldMaxUsec = 0; | |||
| // Control access to the two versions of SdFat. | |||
| bool useEx = false; | |||
| //----------------------------------------------------------------------------- | |||
| bool sdBusy() { | |||
| return useEx ? sdEx.card()->isBusy() : sd.card()->isBusy(); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void errorHalt(const char* msg) { | |||
| if (useEx) { | |||
| sdEx.errorHalt(msg); | |||
| } else { | |||
| sd.errorHalt(msg); | |||
| Serial.print("Error: "); | |||
| Serial.println(msg); | |||
| if (sd.sdErrorCode()) { | |||
| if (sd.sdErrorCode() == SD_CARD_ERROR_ACMD41) { | |||
| Serial.println("Try power cycling the SD card."); | |||
| } | |||
| printSdErrorSymbol(&Serial, sd.sdErrorCode()); | |||
| Serial.print(", ErrorData: 0X"); | |||
| Serial.println(sd.sdErrorData(), HEX); | |||
| } | |||
| while (true) {} | |||
| } | |||
| bool ready = false; | |||
| //----------------------------------------------------------------------------- | |||
| bool sdBusy() { | |||
| return ready ? sd.card()->isBusy() : false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| uint32_t kHzSdClk() { | |||
| return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Replace "weak" system yield() function. | |||
| void yield() { | |||
| @@ -81,8 +92,11 @@ void runTest() { | |||
| Serial.println("\nsize,write,read"); | |||
| Serial.println("bytes,KB/sec,KB/sec"); | |||
| for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) { | |||
| file.truncate(0); | |||
| uint32_t nRdWr = FILE_SIZE/nb; | |||
| uint32_t nRdWr = FILE_SIZE/nb; | |||
| if (!file.truncate(0)) { | |||
| errorHalt("truncate failed"); | |||
| } | |||
| Serial.print(nb); | |||
| Serial.print(','); | |||
| uint32_t t = micros(); | |||
| @@ -123,8 +137,8 @@ void runTest() { | |||
| Serial.println(yieldCalls); | |||
| Serial.print("yieldMaxUsec "); | |||
| Serial.println(yieldMaxUsec); | |||
| Serial.print("kHzSdClk "); | |||
| Serial.println(kHzSdClk()); | |||
| // Serial.print("kHzSdClk "); | |||
| // Serial.println(kHzSdClk()); | |||
| Serial.println("Done"); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| @@ -132,38 +146,55 @@ void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) { | |||
| } | |||
| Serial.println("SdFatSdioEX uses extended multi-block transfers without DMA."); | |||
| Serial.println("SdFatSdio uses a traditional DMA SDIO implementation."); | |||
| Serial.println("Note the difference is speed and busy yield time.\n"); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void loop() { | |||
| static bool warn = true; | |||
| if (warn) { | |||
| warn = false; | |||
| Serial.println( | |||
| "SD cards must be power cycled to leave\n" | |||
| "SPI mode so do SDIO tests first.\n" | |||
| "\nCycle power on the card if an error occurs."); | |||
| } | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read()); | |||
| Serial.println("Type '1' for SdFatSdioEX or '2' for SdFatSdio"); | |||
| Serial.println( | |||
| "\nType '1' for FIFO SDIO" | |||
| "\n '2' for DMA SDIO" | |||
| "\n '3' for Dedicated SPI" | |||
| "\n '4' for Shared SPI"); | |||
| while (!Serial.available()) { | |||
| } | |||
| char c = Serial.read(); | |||
| if (c != '1' && c != '2') { | |||
| Serial.println("Invalid input"); | |||
| return; | |||
| } | |||
| if (c =='1') { | |||
| useEx = true; | |||
| if (!sdEx.begin()) { | |||
| sd.initErrorHalt("SdFatSdioEX begin() failed"); | |||
| if (!sd.begin(SdioConfig(FIFO_SDIO))) { | |||
| errorHalt("begin failed"); | |||
| } | |||
| // make sdEx the current volume. | |||
| sdEx.chvol(); | |||
| } else { | |||
| useEx = false; | |||
| if (!sd.begin()) { | |||
| sd.initErrorHalt("SdFatSdio begin() failed"); | |||
| Serial.println("\nFIFO SDIO mode."); | |||
| } else if (c == '2') { | |||
| if (!sd.begin(SdioConfig(DMA_SDIO))) { | |||
| errorHalt("begin failed"); | |||
| } | |||
| Serial.println("\nDMA SDIO mode - slow for small transfers."); | |||
| } else if (c == '3') { | |||
| if (!sd.begin(SdSpiConfig(SDCARD_SS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) { | |||
| errorHalt("begin failed"); | |||
| } | |||
| Serial.println("\nDedicated SPI mode."); | |||
| } else if (c == '4') { | |||
| if (!sd.begin(SdSpiConfig(SDCARD_SS_PIN, SHARED_SPI, SD_SCK_MHZ(50)))) { | |||
| errorHalt("begin failed"); | |||
| } | |||
| // make sd the current volume. | |||
| sd.chvol(); | |||
| Serial.println("\nShared SPI mode - slow for small transfers."); | |||
| } else { | |||
| Serial.println("Invalid input"); | |||
| return; | |||
| } | |||
| ready = true; | |||
| runTest(); | |||
| ready = false; | |||
| } | |||
| @@ -1,16 +1,44 @@ | |||
| /* | |||
| * This program is a simple binary write/read benchmark. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| #include "FreeStack.h" | |||
| // Set USE_SDIO to zero for SPI card access. | |||
| #define USE_SDIO 0 | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| // Set PRE_ALLOCATE true to pre-allocate file clusters. | |||
| const bool PRE_ALLOCATE = true; | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // Set SKIP_FIRST_LATENCY true if the first read/write to the SD can | |||
| // be avoid by writing a file header or reading the first record. | |||
| const bool SKIP_FIRST_LATENCY = true; | |||
| // Size of read/write. | |||
| const size_t BUF_SIZE = 512; | |||
| @@ -29,38 +57,36 @@ const uint8_t READ_COUNT = 2; | |||
| // File size in bytes. | |||
| const uint32_t FILE_SIZE = 1000000UL*FILE_SIZE_MB; | |||
| uint8_t buf[BUF_SIZE]; | |||
| // Insure 4-byte alignment. | |||
| uint32_t buf32[(BUF_SIZE + 3)/4]; | |||
| uint8_t* buf = (uint8_t*)buf32; | |||
| // file system | |||
| #if USE_SDIO | |||
| // Traditional DMA version. | |||
| // SdFatSdio sd; | |||
| // Faster version. | |||
| SdFatSdioEX sd; | |||
| #else // USE_SDIO | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| #endif // USE_SDIO | |||
| // Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O. | |||
| // Requires dedicated use of the SPI bus. | |||
| // SdFatEX sd; | |||
| // Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI. | |||
| // Args are misoPin, mosiPin, sckPin. | |||
| // SdFatSoftSpi<6, 7, 5> sd; | |||
| // test file | |||
| SdFile file; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // Store error strings in flash to save RAM. | |||
| #define error(s) sd.errorHalt(F(s)) | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void cidDmp() { | |||
| cid_t cid; | |||
| if (!sd.card()->readCID(&cid)) { | |||
| error("readCID failed"); | |||
| } | |||
| cout << F("\nManufacturer ID: "); | |||
| @@ -81,13 +107,18 @@ void cidDmp() { | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| delay(1000); | |||
| cout << F("\nUse a freshly formatted SD for best performance.\n"); | |||
| if (!ENABLE_DEDICATED_SPI) { | |||
| cout << F( | |||
| "\nSet ENABLE_DEDICATED_SPI nonzero in\n" | |||
| "SdFatConfig.h for best SPI performance.\n"); | |||
| } | |||
| // use uppercase in hex and use 0X base prefix | |||
| cout << uppercase << showbase << endl; | |||
| @@ -99,33 +130,31 @@ void loop() { | |||
| uint32_t maxLatency; | |||
| uint32_t minLatency; | |||
| uint32_t totalLatency; | |||
| bool skipLatency; | |||
| // Discard any input. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| // F( stores strings in flash to save RAM | |||
| // F() stores strings in flash to save RAM | |||
| cout << F("Type any character to start\n"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| cout << F("chipSelect: ") << int(chipSelect) << endl; | |||
| cout << F("FreeStack: ") << FreeStack() << endl; | |||
| #if USE_SDIO | |||
| if (!sd.begin()) { | |||
| sd.initErrorHalt(); | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| #else // USE_SDIO | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| if (sd.fatType() == FAT_TYPE_EXFAT) { | |||
| cout << F("Type is exFAT") << endl; | |||
| } else { | |||
| cout << F("Type is FAT") << int(sd.fatType()) << endl; | |||
| } | |||
| #endif // USE_SDIO | |||
| cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl; | |||
| cout << F("Card size: ") << sd.card()->cardSize()*512E-9; | |||
| cout << F("Card size: ") << sd.card()->sectorCount()*512E-9; | |||
| cout << F(" GB (GB = 1E9 bytes)") << endl; | |||
| cidDmp(); | |||
| @@ -136,42 +165,51 @@ void loop() { | |||
| } | |||
| // fill buf with known data | |||
| for (size_t i = 0; i < (BUF_SIZE-2); i++) { | |||
| for (uint16_t i = 0; i < (BUF_SIZE-2); i++) { | |||
| buf[i] = 'A' + (i % 26); | |||
| } | |||
| buf[BUF_SIZE-2] = '\r'; | |||
| buf[BUF_SIZE-1] = '\n'; | |||
| cout << F("File size ") << FILE_SIZE_MB << F(" MB\n"); | |||
| cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n"); | |||
| cout << F("FILE_SIZE_MB = ") << FILE_SIZE_MB << endl; | |||
| cout << F("BUF_SIZE = ") << BUF_SIZE << F(" bytes\n"); | |||
| cout << F("Starting write test, please wait.") << endl << endl; | |||
| // do write test | |||
| uint32_t n = FILE_SIZE/sizeof(buf); | |||
| uint32_t n = FILE_SIZE/BUF_SIZE; | |||
| cout <<F("write speed and latency") << endl; | |||
| cout << F("speed,max,min,avg") << endl; | |||
| cout << F("KB/Sec,usec,usec,usec") << endl; | |||
| for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) { | |||
| file.truncate(0); | |||
| if (PRE_ALLOCATE) { | |||
| if (!file.preAllocate(FILE_SIZE)) { | |||
| error("preAllocate failed"); | |||
| } | |||
| } | |||
| maxLatency = 0; | |||
| minLatency = 9999999; | |||
| totalLatency = 0; | |||
| skipLatency = SKIP_FIRST_LATENCY; | |||
| t = millis(); | |||
| for (uint32_t i = 0; i < n; i++) { | |||
| uint32_t m = micros(); | |||
| if (file.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| sd.errorPrint("write failed"); | |||
| file.close(); | |||
| return; | |||
| if (file.write(buf, BUF_SIZE) != BUF_SIZE) { | |||
| error("write failed"); | |||
| } | |||
| m = micros() - m; | |||
| if (maxLatency < m) { | |||
| maxLatency = m; | |||
| } | |||
| if (minLatency > m) { | |||
| minLatency = m; | |||
| } | |||
| totalLatency += m; | |||
| if (skipLatency) { | |||
| // Wait until first write to SD, not just a copy to the cache. | |||
| skipLatency = file.curPosition() < 512; | |||
| } else { | |||
| if (maxLatency < m) { | |||
| maxLatency = m; | |||
| } | |||
| if (minLatency > m) { | |||
| minLatency = m; | |||
| } | |||
| } | |||
| } | |||
| file.sync(); | |||
| t = millis() - t; | |||
| @@ -190,26 +228,29 @@ void loop() { | |||
| maxLatency = 0; | |||
| minLatency = 9999999; | |||
| totalLatency = 0; | |||
| skipLatency = SKIP_FIRST_LATENCY; | |||
| t = millis(); | |||
| for (uint32_t i = 0; i < n; i++) { | |||
| buf[BUF_SIZE-1] = 0; | |||
| uint32_t m = micros(); | |||
| int32_t nr = file.read(buf, sizeof(buf)); | |||
| if (nr != sizeof(buf)) { | |||
| sd.errorPrint("read failed"); | |||
| file.close(); | |||
| return; | |||
| int32_t nr = file.read(buf, BUF_SIZE); | |||
| if (nr != BUF_SIZE) { | |||
| error("read failed"); | |||
| } | |||
| m = micros() - m; | |||
| if (maxLatency < m) { | |||
| maxLatency = m; | |||
| } | |||
| if (minLatency > m) { | |||
| minLatency = m; | |||
| } | |||
| totalLatency += m; | |||
| if (buf[BUF_SIZE-1] != '\n') { | |||
| error("data check"); | |||
| error("data check error"); | |||
| } | |||
| if (skipLatency) { | |||
| skipLatency = false; | |||
| } else { | |||
| if (maxLatency < m) { | |||
| maxLatency = m; | |||
| } | |||
| if (minLatency > m) { | |||
| minLatency = m; | |||
| } | |||
| } | |||
| } | |||
| s = file.fileSize(); | |||
| @@ -0,0 +1,87 @@ | |||
| #include "SdFat.h" | |||
| #ifdef __AVR__ | |||
| const uint32_t FILE_SIZE_MiB = 10UL; | |||
| #else // __AVR__ | |||
| const uint32_t FILE_SIZE_MiB = 100UL; | |||
| #endif | |||
| bool waitBusy = true; | |||
| #define SD_CONFIG SdSpiConfig(SS, DEDICATED_SPI) | |||
| // Config for Teensy 3.5/3.6 buit-in SD. | |||
| //#define SD_CONFIG SdSpiConfig(SDCARD_SS_PIN, DEDICATED_SPI) | |||
| //#define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| //------------------------------------------------------------------------------ | |||
| const uint64_t FILE_SIZE = (uint64_t)FILE_SIZE_MiB << 20; | |||
| SdExFat sd; | |||
| ExFile file; | |||
| uint8_t buf[512]; | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| delay(1000); | |||
| Serial.println(F("Type any character to start\n")); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| // Initialize the SD card. | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| if (!file.open("SdBusyTest.bin", O_RDWR | O_CREAT |O_TRUNC)) { | |||
| error("file open failed"); | |||
| } | |||
| if (!file.preAllocate(FILE_SIZE)) { | |||
| error("preallocate failed"); | |||
| } | |||
| Serial.print(F("Starting write of ")); | |||
| Serial.print(FILE_SIZE_MiB); | |||
| Serial.println(F(" MiB.")); | |||
| uint32_t maxMicros = 0; | |||
| uint32_t minMicros = 99999999; | |||
| uint32_t ms = millis(); | |||
| // Write a dummy sector to start a multi-sector write. | |||
| if(file.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| error("write failed for first sector"); | |||
| } | |||
| while (file.position() < FILE_SIZE) { | |||
| if (waitBusy) { | |||
| while (sd.card()->isBusy()) {} | |||
| } | |||
| uint32_t m = micros(); | |||
| if (file.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| error("write failed"); | |||
| } | |||
| m = micros() - m; | |||
| if (m < minMicros) { | |||
| minMicros = m; | |||
| } | |||
| if (m > maxMicros) { | |||
| maxMicros = m; | |||
| } | |||
| } | |||
| ms = millis() - ms; | |||
| Serial.print(F("minMicros: ")); | |||
| Serial.println(minMicros); | |||
| Serial.print(F("maxMicros: ")); | |||
| Serial.println(maxMicros); | |||
| Serial.print(1e-3*ms); | |||
| Serial.println(F(" Seconds")); | |||
| Serial.print(1.0*FILE_SIZE/ms); | |||
| Serial.println(F(" KB/sec")); | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,47 @@ | |||
| #include "SdFat.h" | |||
| #define DUMP_RAW 0 | |||
| #define DUMP_UPCASE 0 | |||
| const uint8_t CS_PIN = SS; | |||
| //#define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #define SD_CONFIG SdSpiConfig(CS_PIN) | |||
| SdExFat sd; | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {yield();} | |||
| Serial.println(F("Type any character to begin")); | |||
| while (!Serial.available()) {yield();} | |||
| if (!sd.begin(SD_CONFIG)){ | |||
| error("begin failed"); | |||
| } | |||
| #if DUMP_RAW | |||
| sd.dmpSector(&Serial, 0); | |||
| for (uint8_t i = 0; i < 24; i++) { | |||
| sd.dmpSector(&Serial, 0X8000 + i); | |||
| Serial.println(); | |||
| } | |||
| return; | |||
| #endif // DUMP_RAW | |||
| ExFatFile root; | |||
| if (!root.openRoot(&sd)) { | |||
| error("openRoot failed"); | |||
| } | |||
| sd.printDir(&Serial, &root); | |||
| // startSector = 0, sectorCount = 1. | |||
| sd.dmpFat(&Serial, 0, 1); | |||
| sd.dmpBitmap(&Serial); | |||
| sd.printVolInfo(&Serial); | |||
| sd.checkUpcase(&Serial); | |||
| #if DUMP_UPCASE | |||
| sd.printUpcase(&Serial); | |||
| #endif // DUMP_UPCASE | |||
| // sd.dmpCluster(&Serial, 8, 0, 4); | |||
| Serial.println("Done"); | |||
| } | |||
| void loop() { | |||
| // put your main code here, to run repeatedly: | |||
| } | |||
| @@ -10,23 +10,22 @@ | |||
| * Note: Some cards may 'stutter' others just get slow due | |||
| * to the number of flash erases this program causes. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| SdFat sd; | |||
| typedef File file_t; | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt(F(s)) | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| /* | |||
| * create enough files to force a cluster to be allocated to dir. | |||
| */ | |||
| void dirAllocTest(FatFile* dir) { | |||
| void dirAllocTest(file_t* dir) { | |||
| char buf[32], name[32]; | |||
| SdFile file; | |||
| file_t file; | |||
| uint16_t n; | |||
| uint32_t size = dir->dirSize(); | |||
| @@ -37,7 +36,7 @@ void dirAllocTest(FatFile* dir) { | |||
| // open start time | |||
| uint32_t t0 = millis(); | |||
| if (!file.open(dir, name, O_WRITE | O_CREAT | O_EXCL)) { | |||
| if (!file.open(dir, name, O_WRONLY | O_CREAT | O_EXCL)) { | |||
| error("open for write failed"); | |||
| } | |||
| @@ -70,7 +69,7 @@ void dirAllocTest(FatFile* dir) { | |||
| // open start time | |||
| uint32_t t0 = millis(); | |||
| if (!file.open(dir, name, O_READ)) { | |||
| if (!file.open(dir, name, O_RDONLY)) { | |||
| error("open for read failed"); | |||
| } | |||
| @@ -102,6 +101,7 @@ void dirAllocTest(FatFile* dir) { | |||
| } | |||
| void setup() { | |||
| file_t root; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| Serial.println(F("Type any character to start")); | |||
| @@ -110,23 +110,25 @@ void setup() { | |||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||
| // try SPI_HALF_SPEED if bus errors occur. | |||
| if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt(); | |||
| if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| root.openRoot(&sd); | |||
| uint32_t m = millis(); | |||
| // write files to root if FAT32 | |||
| if (sd.vol()->fatType() == 32) { | |||
| // write files to root if not FAT16 | |||
| if (sd.fatType() != 16) { | |||
| Serial.println(F("Writing files to root")); | |||
| dirAllocTest(sd.vwd()); | |||
| dirAllocTest(&root); | |||
| } | |||
| // create sub1 and write files | |||
| SdFile sub1; | |||
| if (!sub1.mkdir(sd.vwd(), "SUB1")) error("makdeDir SUB1 failed"); | |||
| file_t sub1; | |||
| if (!sub1.mkdir(&root, "SUB1")) error("makdeDir SUB1 failed"); | |||
| Serial.println(F("Writing files to SUB1")); | |||
| dirAllocTest(&sub1); | |||
| // create sub2 and write files | |||
| SdFile sub2; | |||
| file_t sub2; | |||
| if (!sub2.mkdir(&sub1, "SUB2")) error("mkdir SUB2 failed"); | |||
| Serial.println(F("Writing files to SUB2")); | |||
| dirAllocTest(&sub2); | |||
| @@ -6,23 +6,22 @@ | |||
| * of flash erase operations caused by many random | |||
| * writes to file structures. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| SdFat sd; | |||
| typedef File file_t; | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt(F(s)) | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| /* | |||
| * remove all files in dir. | |||
| */ | |||
| void deleteFiles(FatFile* dir) { | |||
| char name[32]; | |||
| SdFile file; | |||
| file_t file; | |||
| // open and delete files | |||
| for (uint16_t n = 0; ; n++){ | |||
| @@ -32,7 +31,7 @@ void deleteFiles(FatFile* dir) { | |||
| uint32_t t0 = millis(); | |||
| // assume done if open fails | |||
| if (!file.open(dir, name, O_WRITE)) return; | |||
| if (!file.open(dir, name, O_WRONLY)) return; | |||
| // open end time and remove start time | |||
| uint32_t t1 = millis(); | |||
| @@ -55,6 +54,7 @@ void deleteFiles(FatFile* dir) { | |||
| } | |||
| void setup() { | |||
| file_t root; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| Serial.println(F("Type any character to start")); | |||
| @@ -63,24 +63,25 @@ void setup() { | |||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||
| // try SPI_HALF_SPEED if bus errors occur. | |||
| if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt(); | |||
| // delete files in root if FAT32 | |||
| if (sd.vol()->fatType() == 32) { | |||
| if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| root.openRoot(&sd); | |||
| // delete files in root if not FAT16. | |||
| if (sd.fatType() != 16) { | |||
| Serial.println(F("Remove files in root")); | |||
| deleteFiles(sd.vwd()); | |||
| deleteFiles(&root); | |||
| } | |||
| // open SUB1 and delete files | |||
| SdFile sub1; | |||
| if (!sub1.open("SUB1", O_READ)) error("open SUB1 failed"); | |||
| file_t sub1; | |||
| if (!sub1.open("SUB1", O_RDONLY)) error("open SUB1 failed"); | |||
| Serial.println(F("Remove files in SUB1")); | |||
| deleteFiles(&sub1); | |||
| // open SUB2 and delete files | |||
| SdFile sub2; | |||
| if (!sub2.open(&sub1, "SUB2", O_READ)) error("open SUB2 failed"); | |||
| file_t sub2; | |||
| if (!sub2.open(&sub1, "SUB2", O_RDONLY)) error("open SUB2 failed"); | |||
| Serial.println(F("Remove files in SUB2")); | |||
| deleteFiles(&sub2); | |||
| @@ -95,4 +96,4 @@ void setup() { | |||
| Serial.println(F("Done")); | |||
| } | |||
| void loop() { } | |||
| void loop() { } | |||
| @@ -747,7 +747,7 @@ void logData() { | |||
| error("Can't truncate file"); | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| if (!binFile.rename(binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| @@ -0,0 +1,129 @@ | |||
| /* | |||
| * Example use of chdir(), ls(), mkdir(), and rmdir(). | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // SD card chip select pin. | |||
| const uint8_t chipSelect = SS; | |||
| //------------------------------------------------------------------------------ | |||
| // File system object. | |||
| SdFat sd; | |||
| // Directory file. | |||
| SdFile root; | |||
| // Use for file creation in folders. | |||
| SdFile file; | |||
| // Create a Serial output stream. | |||
| ArduinoOutStream cout(Serial); | |||
| // Buffer for Serial input. | |||
| char cinBuf[40]; | |||
| // Create a serial input stream. | |||
| ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf)); | |||
| //============================================================================== | |||
| // Error messages stored in flash. | |||
| #define error(msg) sd.errorHalt(F(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| delay(1000); | |||
| cout << F("Type any character to start\n"); | |||
| // Wait for input line and discard. | |||
| cin.readline(); | |||
| cout << endl; | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| if (sd.exists("Folder1") | |||
| || sd.exists("Folder1/file1.txt") | |||
| || sd.exists("Folder1/File2.txt")) { | |||
| error("Please remove existing Folder1, file1.txt, and File2.txt"); | |||
| } | |||
| int rootFileCount = 0; | |||
| if (!root.open("/")) { | |||
| error("open root failed"); | |||
| } | |||
| while (file.openNext(&root, O_RDONLY)) { | |||
| if (!file.isHidden()) { | |||
| rootFileCount++; | |||
| } | |||
| file.close(); | |||
| if (rootFileCount > 10) { | |||
| error("Too many files in root. Please use an empty SD."); | |||
| } | |||
| } | |||
| if (rootFileCount) { | |||
| cout << F("\nPlease use an empty SD for best results.\n\n"); | |||
| delay(1000); | |||
| } | |||
| // Create a new folder. | |||
| if (!sd.mkdir("Folder1")) { | |||
| error("Create Folder1 failed"); | |||
| } | |||
| cout << F("Created Folder1\n"); | |||
| // Create a file in Folder1 using a path. | |||
| if (!file.open("Folder1/file1.txt", O_WRONLY | O_CREAT)) { | |||
| error("create Folder1/file1.txt failed"); | |||
| } | |||
| file.close(); | |||
| cout << F("Created Folder1/file1.txt\n"); | |||
| // Change volume working directory to Folder1. | |||
| if (!sd.chdir("Folder1")) { | |||
| error("chdir failed for Folder1.\n"); | |||
| } | |||
| cout << F("chdir to Folder1\n"); | |||
| // Create File2.txt in current directory. | |||
| if (!file.open("File2.txt", O_WRONLY | O_CREAT)) { | |||
| error("create File2.txt failed"); | |||
| } | |||
| file.close(); | |||
| cout << F("Created File2.txt in current directory\n"); | |||
| cout << F("\nList of files on the SD.\n"); | |||
| sd.ls("/", LS_R); | |||
| // Remove files from current directory. | |||
| if (!sd.remove("file1.txt") || !sd.remove("File2.txt")) { | |||
| error("remove failed"); | |||
| } | |||
| cout << F("\nfile1.txt and File2.txt removed.\n"); | |||
| // Change current directory to root. | |||
| if (!sd.chdir()) { | |||
| error("chdir to root failed.\n"); | |||
| } | |||
| cout << F("\nList of files on the SD.\n"); | |||
| sd.ls(LS_R); | |||
| // Remove Folder1. | |||
| if (!sd.rmdir("Folder1")) { | |||
| error("rmdir for Folder1 failed\n"); | |||
| } | |||
| cout << F("\nFolder1 removed.\n"); | |||
| cout << F("\nList of files on the SD.\n"); | |||
| sd.ls(LS_R); | |||
| cout << F("Done!\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Nothing happens in loop. | |||
| void loop() {} | |||
| @@ -192,7 +192,7 @@ void binaryToCsv() { | |||
| Serial.println(); | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // Create a new csvFile. | |||
| strcpy(csvName, binName); | |||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
| @@ -245,7 +245,7 @@ void createBinFile() { | |||
| // max number of blocks to erase per erase call | |||
| const uint32_t ERASE_SIZE = 262144L; | |||
| uint32_t bgnBlock, endBlock; | |||
| // Delete old tmp file. | |||
| if (sd.exists(TMP_FILE_NAME)) { | |||
| Serial.println(F("Deleting tmp file " TMP_FILE_NAME)); | |||
| @@ -349,19 +349,19 @@ void recordBinFile() { | |||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||
| // Index of last queue location. | |||
| const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT - 1]; | |||
| block_t* curBlock = 0; | |||
| block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||
| uint8_t emptyTop; | |||
| uint8_t minTop; | |||
| block_t* fullQueue[QUEUE_DIM]; | |||
| uint8_t fullHead = 0; | |||
| uint8_t fullTail = 0; | |||
| uint8_t fullTail = 0; | |||
| // Use SdFat's internal buffer. | |||
| emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||
| @@ -374,7 +374,7 @@ void recordBinFile() { | |||
| } | |||
| emptyTop = BUFFER_BLOCK_COUNT; | |||
| minTop = BUFFER_BLOCK_COUNT; | |||
| // Start a multiple block write. | |||
| if (!sd.card()->writeStart(binFile.firstBlock())) { | |||
| error("writeStart failed"); | |||
| @@ -383,7 +383,7 @@ void recordBinFile() { | |||
| Serial.println(FreeStack()); | |||
| Serial.println(F("Logging - type any character to stop")); | |||
| bool closeFile = false; | |||
| uint32_t bn = 0; | |||
| uint32_t bn = 0; | |||
| uint32_t maxLatency = 0; | |||
| uint32_t overrun = 0; | |||
| uint32_t overrunTotal = 0; | |||
| @@ -393,7 +393,7 @@ void recordBinFile() { | |||
| logTime += LOG_INTERVAL_USEC; | |||
| if (Serial.available()) { | |||
| closeFile = true; | |||
| } | |||
| } | |||
| if (closeFile) { | |||
| if (curBlock != 0) { | |||
| // Put buffer in full queue. | |||
| @@ -412,7 +412,7 @@ void recordBinFile() { | |||
| overrun = 0; | |||
| } | |||
| if ((int32_t)(logTime - micros()) < 0) { | |||
| error("Rate too fast"); | |||
| error("Rate too fast"); | |||
| } | |||
| int32_t delta; | |||
| do { | |||
| @@ -423,24 +423,24 @@ void recordBinFile() { | |||
| overrunTotal++; | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| } | |||
| } | |||
| #if ABORT_ON_OVERRUN | |||
| Serial.println(F("Overrun abort")); | |||
| break; | |||
| #endif // ABORT_ON_OVERRUN | |||
| #endif // ABORT_ON_OVERRUN | |||
| } else { | |||
| #if USE_SHARED_SPI | |||
| sd.card()->spiStop(); | |||
| #endif // USE_SHARED_SPI | |||
| #endif // USE_SHARED_SPI | |||
| acquireData(&curBlock->data[curBlock->count++]); | |||
| #if USE_SHARED_SPI | |||
| sd.card()->spiStart(); | |||
| #endif // USE_SHARED_SPI | |||
| #endif // USE_SHARED_SPI | |||
| if (curBlock->count == DATA_DIM) { | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
| curBlock = 0; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if (fullHead == fullTail) { | |||
| @@ -506,7 +506,7 @@ void recoverTmpFile() { | |||
| if (binFile.read(&count, 2) != 2) error("read"); | |||
| if (count == 0 || count > DATA_DIM) { | |||
| endBlock = midBlock - 1; | |||
| } else { | |||
| } else { | |||
| bgnBlock = midBlock; | |||
| } | |||
| } | |||
| @@ -529,7 +529,7 @@ void renameBinFile() { | |||
| binName[BASE_NAME_SIZE]++; | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| if (!binFile.rename(binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| @@ -563,8 +563,8 @@ void setup(void) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| } | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| @@ -578,10 +578,10 @@ void setup(void) { | |||
| // Allow userSetup access to SPI bus. | |||
| pinMode(SD_CS_PIN, OUTPUT); | |||
| digitalWrite(SD_CS_PIN, HIGH); | |||
| // Setup sensors. | |||
| userSetup(); | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
| @@ -609,11 +609,11 @@ void loop(void) { | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| Serial.println(); | |||
| Serial.println(F("type:")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("c - convert file to csv")); | |||
| Serial.println(F("d - dump data to Serial")); | |||
| Serial.println(F("e - overrun error details")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("r - record data")); | |||
| Serial.println(F("t - test without logging")); | |||
| while(!Serial.available()) { | |||
| @@ -623,7 +623,7 @@ void loop(void) { | |||
| Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||
| SysCall::halt(); | |||
| #endif | |||
| char c = tolower(Serial.read()); | |||
| // Discard extra Serial data. | |||
| @@ -643,12 +643,12 @@ void loop(void) { | |||
| } else if (c == 'e') { | |||
| checkOverrun(); | |||
| } else if (c == 'l') { | |||
| Serial.println(F("\nls:")); | |||
| sd.ls(&Serial, LS_SIZE); | |||
| Serial.println(F("\nls:")); | |||
| sd.ls(&Serial, LS_SIZE); | |||
| } else if (c == 'r') { | |||
| logData(); | |||
| } else if (c == 't') { | |||
| testSensor(); | |||
| testSensor(); | |||
| } else { | |||
| Serial.println(F("Invalid entry")); | |||
| } | |||
| @@ -192,7 +192,7 @@ void binaryToCsv() { | |||
| Serial.println(); | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // Create a new csvFile. | |||
| strcpy(csvName, binName); | |||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
| @@ -245,7 +245,7 @@ void createBinFile() { | |||
| // max number of blocks to erase per erase call | |||
| const uint32_t ERASE_SIZE = 262144L; | |||
| uint32_t bgnBlock, endBlock; | |||
| // Delete old tmp file. | |||
| if (sd.exists(TMP_FILE_NAME)) { | |||
| Serial.println(F("Deleting tmp file " TMP_FILE_NAME)); | |||
| @@ -349,19 +349,19 @@ void recordBinFile() { | |||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||
| // Index of last queue location. | |||
| const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT - 1]; | |||
| block_t* curBlock = 0; | |||
| block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||
| uint8_t emptyTop; | |||
| uint8_t minTop; | |||
| block_t* fullQueue[QUEUE_DIM]; | |||
| uint8_t fullHead = 0; | |||
| uint8_t fullTail = 0; | |||
| uint8_t fullTail = 0; | |||
| // Use SdFat's internal buffer. | |||
| emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||
| @@ -374,7 +374,7 @@ void recordBinFile() { | |||
| } | |||
| emptyTop = BUFFER_BLOCK_COUNT; | |||
| minTop = BUFFER_BLOCK_COUNT; | |||
| // Start a multiple block write. | |||
| if (!sd.card()->writeStart(binFile.firstBlock())) { | |||
| error("writeStart failed"); | |||
| @@ -383,7 +383,7 @@ void recordBinFile() { | |||
| Serial.println(FreeStack()); | |||
| Serial.println(F("Logging - type any character to stop")); | |||
| bool closeFile = false; | |||
| uint32_t bn = 0; | |||
| uint32_t bn = 0; | |||
| uint32_t maxLatency = 0; | |||
| uint32_t overrun = 0; | |||
| uint32_t overrunTotal = 0; | |||
| @@ -393,7 +393,7 @@ void recordBinFile() { | |||
| logTime += LOG_INTERVAL_USEC; | |||
| if (Serial.available()) { | |||
| closeFile = true; | |||
| } | |||
| } | |||
| if (closeFile) { | |||
| if (curBlock != 0) { | |||
| // Put buffer in full queue. | |||
| @@ -412,7 +412,7 @@ void recordBinFile() { | |||
| overrun = 0; | |||
| } | |||
| if ((int32_t)(logTime - micros()) < 0) { | |||
| error("Rate too fast"); | |||
| error("Rate too fast"); | |||
| } | |||
| int32_t delta; | |||
| do { | |||
| @@ -423,24 +423,24 @@ void recordBinFile() { | |||
| overrunTotal++; | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| } | |||
| } | |||
| #if ABORT_ON_OVERRUN | |||
| Serial.println(F("Overrun abort")); | |||
| break; | |||
| #endif // ABORT_ON_OVERRUN | |||
| #endif // ABORT_ON_OVERRUN | |||
| } else { | |||
| #if USE_SHARED_SPI | |||
| sd.card()->spiStop(); | |||
| #endif // USE_SHARED_SPI | |||
| #endif // USE_SHARED_SPI | |||
| acquireData(&curBlock->data[curBlock->count++]); | |||
| #if USE_SHARED_SPI | |||
| sd.card()->spiStart(); | |||
| #endif // USE_SHARED_SPI | |||
| #endif // USE_SHARED_SPI | |||
| if (curBlock->count == DATA_DIM) { | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
| curBlock = 0; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if (fullHead == fullTail) { | |||
| @@ -506,7 +506,7 @@ void recoverTmpFile() { | |||
| if (binFile.read(&count, 2) != 2) error("read"); | |||
| if (count == 0 || count > DATA_DIM) { | |||
| endBlock = midBlock - 1; | |||
| } else { | |||
| } else { | |||
| bgnBlock = midBlock; | |||
| } | |||
| } | |||
| @@ -529,7 +529,7 @@ void renameBinFile() { | |||
| binName[BASE_NAME_SIZE]++; | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| if (!binFile.rename(binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| @@ -563,8 +563,8 @@ void setup(void) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| } | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| @@ -578,10 +578,10 @@ void setup(void) { | |||
| // Allow userSetup access to SPI bus. | |||
| pinMode(SD_CS_PIN, OUTPUT); | |||
| digitalWrite(SD_CS_PIN, HIGH); | |||
| // Setup sensors. | |||
| userSetup(); | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
| @@ -609,11 +609,11 @@ void loop(void) { | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| Serial.println(); | |||
| Serial.println(F("type:")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("c - convert file to csv")); | |||
| Serial.println(F("d - dump data to Serial")); | |||
| Serial.println(F("e - overrun error details")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("r - record data")); | |||
| Serial.println(F("t - test without logging")); | |||
| while(!Serial.available()) { | |||
| @@ -623,7 +623,7 @@ void loop(void) { | |||
| Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||
| SysCall::halt(); | |||
| #endif | |||
| char c = tolower(Serial.read()); | |||
| // Discard extra Serial data. | |||
| @@ -643,12 +643,12 @@ void loop(void) { | |||
| } else if (c == 'e') { | |||
| checkOverrun(); | |||
| } else if (c == 'l') { | |||
| Serial.println(F("\nls:")); | |||
| sd.ls(&Serial, LS_SIZE); | |||
| Serial.println(F("\nls:")); | |||
| sd.ls(&Serial, LS_SIZE); | |||
| } else if (c == 'r') { | |||
| logData(); | |||
| } else if (c == 't') { | |||
| testSensor(); | |||
| testSensor(); | |||
| } else { | |||
| Serial.println(F("Invalid entry")); | |||
| } | |||
| @@ -192,7 +192,7 @@ void binaryToCsv() { | |||
| Serial.println(); | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // Create a new csvFile. | |||
| strcpy(csvName, binName); | |||
| strcpy(&csvName[BASE_NAME_SIZE + 3], "csv"); | |||
| @@ -245,7 +245,7 @@ void createBinFile() { | |||
| // max number of blocks to erase per erase call | |||
| const uint32_t ERASE_SIZE = 262144L; | |||
| uint32_t bgnBlock, endBlock; | |||
| // Delete old tmp file. | |||
| if (sd.exists(TMP_FILE_NAME)) { | |||
| Serial.println(F("Deleting tmp file " TMP_FILE_NAME)); | |||
| @@ -349,19 +349,19 @@ void recordBinFile() { | |||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1; | |||
| // Index of last queue location. | |||
| const uint8_t QUEUE_LAST = QUEUE_DIM - 1; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT - 1]; | |||
| block_t* curBlock = 0; | |||
| block_t* emptyStack[BUFFER_BLOCK_COUNT]; | |||
| uint8_t emptyTop; | |||
| uint8_t minTop; | |||
| block_t* fullQueue[QUEUE_DIM]; | |||
| uint8_t fullHead = 0; | |||
| uint8_t fullTail = 0; | |||
| uint8_t fullTail = 0; | |||
| // Use SdFat's internal buffer. | |||
| emptyStack[0] = (block_t*)sd.vol()->cacheClear(); | |||
| @@ -374,7 +374,7 @@ void recordBinFile() { | |||
| } | |||
| emptyTop = BUFFER_BLOCK_COUNT; | |||
| minTop = BUFFER_BLOCK_COUNT; | |||
| // Start a multiple block write. | |||
| if (!sd.card()->writeStart(binFile.firstBlock())) { | |||
| error("writeStart failed"); | |||
| @@ -383,7 +383,7 @@ void recordBinFile() { | |||
| Serial.println(FreeStack()); | |||
| Serial.println(F("Logging - type any character to stop")); | |||
| bool closeFile = false; | |||
| uint32_t bn = 0; | |||
| uint32_t bn = 0; | |||
| uint32_t maxLatency = 0; | |||
| uint32_t overrun = 0; | |||
| uint32_t overrunTotal = 0; | |||
| @@ -393,7 +393,7 @@ void recordBinFile() { | |||
| logTime += LOG_INTERVAL_USEC; | |||
| if (Serial.available()) { | |||
| closeFile = true; | |||
| } | |||
| } | |||
| if (closeFile) { | |||
| if (curBlock != 0) { | |||
| // Put buffer in full queue. | |||
| @@ -412,7 +412,7 @@ void recordBinFile() { | |||
| overrun = 0; | |||
| } | |||
| if ((int32_t)(logTime - micros()) < 0) { | |||
| error("Rate too fast"); | |||
| error("Rate too fast"); | |||
| } | |||
| int32_t delta; | |||
| do { | |||
| @@ -423,24 +423,24 @@ void recordBinFile() { | |||
| overrunTotal++; | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| } | |||
| } | |||
| #if ABORT_ON_OVERRUN | |||
| Serial.println(F("Overrun abort")); | |||
| break; | |||
| #endif // ABORT_ON_OVERRUN | |||
| #endif // ABORT_ON_OVERRUN | |||
| } else { | |||
| #if USE_SHARED_SPI | |||
| sd.card()->spiStop(); | |||
| #endif // USE_SHARED_SPI | |||
| #endif // USE_SHARED_SPI | |||
| acquireData(&curBlock->data[curBlock->count++]); | |||
| #if USE_SHARED_SPI | |||
| sd.card()->spiStart(); | |||
| #endif // USE_SHARED_SPI | |||
| #endif // USE_SHARED_SPI | |||
| if (curBlock->count == DATA_DIM) { | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0; | |||
| curBlock = 0; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if (fullHead == fullTail) { | |||
| @@ -506,7 +506,7 @@ void recoverTmpFile() { | |||
| if (binFile.read(&count, 2) != 2) error("read"); | |||
| if (count == 0 || count > DATA_DIM) { | |||
| endBlock = midBlock - 1; | |||
| } else { | |||
| } else { | |||
| bgnBlock = midBlock; | |||
| } | |||
| } | |||
| @@ -529,7 +529,7 @@ void renameBinFile() { | |||
| binName[BASE_NAME_SIZE]++; | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| if (!binFile.rename(binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| @@ -563,8 +563,8 @@ void setup(void) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| } | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| @@ -578,10 +578,10 @@ void setup(void) { | |||
| // Allow userSetup access to SPI bus. | |||
| pinMode(SD_CS_PIN, OUTPUT); | |||
| digitalWrite(SD_CS_PIN, HIGH); | |||
| // Setup sensors. | |||
| userSetup(); | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) { | |||
| @@ -609,11 +609,11 @@ void loop(void) { | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| Serial.println(); | |||
| Serial.println(F("type:")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("b - open existing bin file")); | |||
| Serial.println(F("c - convert file to csv")); | |||
| Serial.println(F("d - dump data to Serial")); | |||
| Serial.println(F("e - overrun error details")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("l - list files")); | |||
| Serial.println(F("r - record data")); | |||
| Serial.println(F("t - test without logging")); | |||
| while(!Serial.available()) { | |||
| @@ -623,7 +623,7 @@ void loop(void) { | |||
| Serial.println(F("LowLatencyLogger can not run with watchdog timer")); | |||
| SysCall::halt(); | |||
| #endif | |||
| char c = tolower(Serial.read()); | |||
| // Discard extra Serial data. | |||
| @@ -643,12 +643,12 @@ void loop(void) { | |||
| } else if (c == 'e') { | |||
| checkOverrun(); | |||
| } else if (c == 'l') { | |||
| Serial.println(F("\nls:")); | |||
| sd.ls(&Serial, LS_SIZE); | |||
| Serial.println(F("\nls:")); | |||
| sd.ls(&Serial, LS_SIZE); | |||
| } else if (c == 'r') { | |||
| logData(); | |||
| } else if (c == 't') { | |||
| testSensor(); | |||
| testSensor(); | |||
| } else { | |||
| Serial.println(F("Invalid entry")); | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| /* | |||
| * Print size, modify date/time, and name for all files in root. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| // SD default chip select pin. | |||
| const uint8_t chipSelect = SS; | |||
| // file system object | |||
| SdFat sd; | |||
| SdFile root; | |||
| SdFile file; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| Serial.println("Type any character to start"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| if (!root.open("/")) { | |||
| sd.errorHalt("open root failed"); | |||
| } | |||
| // Open next file in root. | |||
| // Warning, openNext starts at the current directory position | |||
| // so a rewind of the directory may be required. | |||
| while (file.openNext(&root, O_RDONLY)) { | |||
| file.printFileSize(&Serial); | |||
| Serial.write(' '); | |||
| file.printModifyDateTime(&Serial); | |||
| Serial.write(' '); | |||
| file.printName(&Serial); | |||
| if (file.isDir()) { | |||
| // Indicate a directory. | |||
| Serial.write('/'); | |||
| } | |||
| Serial.println(); | |||
| file.close(); | |||
| } | |||
| if (root.getError()) { | |||
| Serial.println("openNext failed"); | |||
| } else { | |||
| Serial.println("Done!"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,161 @@ | |||
| // Quick hardware test for SPI card access. | |||
| // | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // | |||
| // Set DISABLE_CHIP_SELECT to disable a second SPI device. | |||
| // For example, with the Ethernet shield, set DISABLE_CHIP_SELECT | |||
| // to 10 to disable the Ethernet controller. | |||
| const int8_t DISABLE_CHIP_SELECT = -1; | |||
| // | |||
| // Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select | |||
| // the highest speed supported by the board that is not over 4 MHz. | |||
| // Change SPI_SPEED to SD_SCK_MHZ(50) for best performance. | |||
| #define SPI_SPEED SD_SCK_MHZ(4) | |||
| //------------------------------------------------------------------------------ | |||
| // File system object. | |||
| SdFat sd; | |||
| // Serial streams | |||
| ArduinoOutStream cout(Serial); | |||
| // input buffer for line | |||
| char cinBuf[40]; | |||
| ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf)); | |||
| // SD card chip select | |||
| int chipSelect; | |||
| void cardOrSpeed() { | |||
| cout << F("Try another SD card or reduce the SPI bus speed.\n"); | |||
| cout << F("Edit SPI_SPEED in this program to change it.\n"); | |||
| } | |||
| void reformatMsg() { | |||
| cout << F("Try reformatting the card. For best results use\n"); | |||
| cout << F("the SdFormatter program in SdFat/examples or download\n"); | |||
| cout << F("and use SDFormatter from www.sdcard.org/downloads.\n"); | |||
| } | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| cout << F("\nSPI pins:\n"); | |||
| cout << F("MISO: ") << int(MISO) << endl; | |||
| cout << F("MOSI: ") << int(MOSI) << endl; | |||
| cout << F("SCK: ") << int(SCK) << endl; | |||
| cout << F("SS: ") << int(SS) << endl; | |||
| if (DISABLE_CHIP_SELECT < 0) { | |||
| cout << F( | |||
| "\nBe sure to edit DISABLE_CHIP_SELECT if you have\n" | |||
| "a second SPI device. For example, with the Ethernet\n" | |||
| "shield, DISABLE_CHIP_SELECT should be set to 10\n" | |||
| "to disable the Ethernet controller.\n"); | |||
| } | |||
| cout << F( | |||
| "\nSD chip select is the key hardware option.\n" | |||
| "Common values are:\n" | |||
| "Arduino Ethernet shield, pin 4\n" | |||
| "Sparkfun SD shield, pin 8\n" | |||
| "Adafruit SD shields and modules, pin 10\n"); | |||
| } | |||
| bool firstTry = true; | |||
| void loop() { | |||
| // Read any existing Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| if (!firstTry) { | |||
| cout << F("\nRestarting\n"); | |||
| } | |||
| firstTry = false; | |||
| cout << F("\nEnter the chip select pin number: "); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| cin.readline(); | |||
| if (cin >> chipSelect) { | |||
| cout << chipSelect << endl; | |||
| } else { | |||
| cout << F("\nInvalid pin number\n"); | |||
| return; | |||
| } | |||
| if (DISABLE_CHIP_SELECT < 0) { | |||
| cout << F( | |||
| "\nAssuming the SD is the only SPI device.\n" | |||
| "Edit DISABLE_CHIP_SELECT to disable another device.\n"); | |||
| } else { | |||
| cout << F("\nDisabling SPI device on pin "); | |||
| cout << int(DISABLE_CHIP_SELECT) << endl; | |||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | |||
| } | |||
| if (!sd.begin(chipSelect, SPI_SPEED)) { | |||
| if (sd.card()->errorCode()) { | |||
| cout << F( | |||
| "\nSD initialization failed.\n" | |||
| "Do not reformat the card!\n" | |||
| "Is the card correctly inserted?\n" | |||
| "Is chipSelect set to the correct value?\n" | |||
| "Does another SPI device need to be disabled?\n" | |||
| "Is there a wiring/soldering problem?\n"); | |||
| cout << F("\nerrorCode: ") << hex << showbase; | |||
| cout << int(sd.card()->errorCode()); | |||
| cout << F(", errorData: ") << int(sd.card()->errorData()); | |||
| cout << dec << noshowbase << endl; | |||
| return; | |||
| } | |||
| if (sd.vol()->fatType() == 0) { | |||
| cout << F("Can't find a valid FAT16/FAT32 partition.\n"); | |||
| reformatMsg(); | |||
| return; | |||
| } | |||
| cout << F("begin failed, can't determine error type\n"); | |||
| return; | |||
| } | |||
| cout << F("\nCard successfully initialized.\n"); | |||
| cout << endl; | |||
| uint32_t size = sd.card()->cardSize(); | |||
| if (size == 0) { | |||
| cout << F("Can't determine the card size.\n"); | |||
| cardOrSpeed(); | |||
| return; | |||
| } | |||
| uint32_t sizeMB = 0.000512 * size + 0.5; | |||
| cout << F("Card size: ") << sizeMB; | |||
| cout << F(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << endl; | |||
| cout << F("Volume is FAT") << int(sd.vol()->fatType()); | |||
| cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster(); | |||
| cout << endl << endl; | |||
| cout << F("Files found (date time size name):\n"); | |||
| sd.ls(LS_R | LS_DATE | LS_SIZE); | |||
| if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64) | |||
| || (sizeMB < 2200 && sd.vol()->fatType() == 32)) { | |||
| cout << F("\nThis card should be reformatted for best performance.\n"); | |||
| cout << F("Use a cluster size of 32 KB for cards larger than 1 GB.\n"); | |||
| cout << F("Only cards larger than 2 GB should be formatted FAT32.\n"); | |||
| reformatMsg(); | |||
| return; | |||
| } | |||
| // Read any extra Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| cout << F("\nSuccess! Type any character to restart.\n"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,175 @@ | |||
| /* | |||
| * Example use of two SPI ports on an STM32 board. | |||
| * Note SPI speed is limited to 18 MHz. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "FreeStack.h" | |||
| #error See new Version 2 STM32 example | |||
| // set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes | |||
| // Use first SPI port | |||
| SdFat sd1; | |||
| // SdFatEX sd1; | |||
| const uint8_t SD1_CS = PA4; // chip select for sd1 | |||
| // Use second SPI port | |||
| SPIClass SPI_2(2); | |||
| SdFat sd2(&SPI_2); | |||
| // SdFatEX sd2(&SPI_2); | |||
| const uint8_t SD2_CS = PB12; // chip select for sd2 | |||
| const uint8_t BUF_DIM = 100; | |||
| uint8_t buf[BUF_DIM]; | |||
| const uint32_t FILE_SIZE = 1000000; | |||
| const uint16_t NWRITE = FILE_SIZE/BUF_DIM; | |||
| //------------------------------------------------------------------------------ | |||
| // print error msg, any SD error codes, and halt. | |||
| // store messages in flash | |||
| #define errorExit(msg) errorHalt(F(msg)) | |||
| #define initError(msg) initErrorHalt(F(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| } | |||
| // fill buffer with known data | |||
| for (size_t i = 0; i < sizeof(buf); i++) { | |||
| buf[i] = i; | |||
| } | |||
| Serial.println(F("type any character to start")); | |||
| while (!Serial.available()) { | |||
| } | |||
| Serial.print(F("FreeStack: ")); | |||
| Serial.println(FreeStack()); | |||
| // initialize the first card | |||
| if (!sd1.begin(SD1_CS, SD_SCK_MHZ(18))) { | |||
| sd1.initError("sd1:"); | |||
| } | |||
| // create Dir1 on sd1 if it does not exist | |||
| if (!sd1.exists("/Dir1")) { | |||
| if (!sd1.mkdir("/Dir1")) { | |||
| sd1.errorExit("sd1.mkdir"); | |||
| } | |||
| } | |||
| // initialize the second card | |||
| if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) { | |||
| sd2.initError("sd2:"); | |||
| } | |||
| // create Dir2 on sd2 if it does not exist | |||
| if (!sd2.exists("/Dir2")) { | |||
| if (!sd2.mkdir("/Dir2")) { | |||
| sd2.errorExit("sd2.mkdir"); | |||
| } | |||
| } | |||
| // list root directory on both cards | |||
| Serial.println(F("------sd1 root-------")); | |||
| sd1.ls(); | |||
| Serial.println(F("------sd2 root-------")); | |||
| sd2.ls(); | |||
| // make /Dir1 the default directory for sd1 | |||
| if (!sd1.chdir("/Dir1")) { | |||
| sd1.errorExit("sd1.chdir"); | |||
| } | |||
| // remove test.bin from /Dir1 directory of sd1 | |||
| if (sd1.exists("test.bin")) { | |||
| if (!sd1.remove("test.bin")) { | |||
| sd2.errorExit("remove test.bin"); | |||
| } | |||
| } | |||
| // make /Dir2 the default directory for sd2 | |||
| if (!sd2.chdir("/Dir2")) { | |||
| sd2.errorExit("sd2.chdir"); | |||
| } | |||
| // remove rename.bin from /Dir2 directory of sd2 | |||
| if (sd2.exists("rename.bin")) { | |||
| if (!sd2.remove("rename.bin")) { | |||
| sd2.errorExit("remove rename.bin"); | |||
| } | |||
| } | |||
| // list current directory on both cards | |||
| Serial.println(F("------sd1 Dir1-------")); | |||
| sd1.ls(); | |||
| Serial.println(F("------sd2 Dir2-------")); | |||
| sd2.ls(); | |||
| Serial.println(F("---------------------")); | |||
| // set the current working directory for open() to sd1 | |||
| sd1.chvol(); | |||
| // create or open /Dir1/test.bin and truncate it to zero length | |||
| SdFile file1; | |||
| if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) { | |||
| sd1.errorExit("file1"); | |||
| } | |||
| Serial.println(F("Writing test.bin to sd1")); | |||
| // write data to /Dir1/test.bin on sd1 | |||
| for (uint16_t i = 0; i < NWRITE; i++) { | |||
| if (file1.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| sd1.errorExit("sd1.write"); | |||
| } | |||
| } | |||
| // set the current working directory for open() to sd2 | |||
| sd2.chvol(); | |||
| // create or open /Dir2/copy.bin and truncate it to zero length | |||
| SdFile file2; | |||
| if (!file2.open("copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) { | |||
| sd2.errorExit("file2"); | |||
| } | |||
| Serial.println(F("Copying test.bin to copy.bin")); | |||
| // copy file1 to file2 | |||
| file1.rewind(); | |||
| uint32_t t = millis(); | |||
| while (1) { | |||
| int n = file1.read(buf, sizeof(buf)); | |||
| if (n < 0) { | |||
| sd1.errorExit("read1"); | |||
| } | |||
| if (n == 0) { | |||
| break; | |||
| } | |||
| if ((int)file2.write(buf, n) != n) { | |||
| sd2.errorExit("write2"); | |||
| } | |||
| } | |||
| t = millis() - t; | |||
| Serial.print(F("File size: ")); | |||
| Serial.println(file2.fileSize()); | |||
| Serial.print(F("Copy time: ")); | |||
| Serial.print(t); | |||
| Serial.println(F(" millis")); | |||
| // close test.bin | |||
| file1.close(); | |||
| file2.close(); | |||
| // list current directory on both cards | |||
| Serial.println(F("------sd1 -------")); | |||
| sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| Serial.println(F("------sd2 -------")); | |||
| sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| Serial.println(F("---------------------")); | |||
| Serial.println(F("Renaming copy.bin")); | |||
| // rename the copy | |||
| if (!sd2.rename("copy.bin", "rename.bin")) { | |||
| sd2.errorExit("sd2.rename"); | |||
| } | |||
| // list current directory on both cards | |||
| Serial.println(F("------sd1 -------")); | |||
| sd1.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| Serial.println(F("------sd2 -------")); | |||
| sd2.ls("/", LS_R | LS_DATE | LS_SIZE); | |||
| Serial.println(F("---------------------")); | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,552 @@ | |||
| /* | |||
| * This program will format an SD or SDHC card. | |||
| * Warning all data will be deleted! | |||
| * | |||
| * For SD/SDHC cards larger than 64 MB this | |||
| * program attempts to match the format | |||
| * generated by SDFormatter available here: | |||
| * | |||
| * http://www.sdcard.org/consumers/formatter/ | |||
| * | |||
| * For smaller cards this program uses FAT16 | |||
| * and SDFormatter uses FAT12. | |||
| */ | |||
| #error use new Version 2 SdFormatter | |||
| // Set USE_SDIO to zero for SPI card access. | |||
| #define USE_SDIO 0 | |||
| // | |||
| // Change the value of chipSelect if your hardware does | |||
| // not use the default value, SS. Common values are: | |||
| // Arduino Ethernet shield: pin 4 | |||
| // Sparkfun SD shield: pin 8 | |||
| // Adafruit SD shields and modules: pin 10 | |||
| const uint8_t chipSelect = SS; | |||
| // Initialize at highest supported speed not over 50 MHz. | |||
| // Reduce max speed if errors occur. | |||
| #define SPI_SPEED SD_SCK_MHZ(50) | |||
| // Print extra info for debug if DEBUG_PRINT is nonzero | |||
| #define DEBUG_PRINT 0 | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| #if DEBUG_PRINT | |||
| #include "FreeStack.h" | |||
| #endif // DEBUG_PRINT | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| #if USE_SDIO | |||
| // Use faster SdioCardEX | |||
| SdioCardEX card; | |||
| // SdioCard card; | |||
| #else // USE_SDIO | |||
| Sd2Card card; | |||
| #endif // USE_SDIO | |||
| uint32_t cardSizeBlocks; | |||
| uint32_t cardCapacityMB; | |||
| // cache for SD block | |||
| cache_t cache; | |||
| // MBR information | |||
| uint8_t partType; | |||
| uint32_t relSector; | |||
| uint32_t partSize; | |||
| // Fake disk geometry | |||
| uint8_t numberOfHeads; | |||
| uint8_t sectorsPerTrack; | |||
| // FAT parameters | |||
| uint16_t reservedSectors; | |||
| uint8_t sectorsPerCluster; | |||
| uint32_t fatStart; | |||
| uint32_t fatSize; | |||
| uint32_t dataStart; | |||
| // constants for file system structure | |||
| uint16_t const BU16 = 128; | |||
| uint16_t const BU32 = 8192; | |||
| // strings needed in file system structures | |||
| char noName[] = "NO NAME "; | |||
| char fat16str[] = "FAT16 "; | |||
| char fat32str[] = "FAT32 "; | |||
| //------------------------------------------------------------------------------ | |||
| #define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();} | |||
| //------------------------------------------------------------------------------ | |||
| void sdErrorHalt() { | |||
| if (card.errorCode()) { | |||
| cout << F("SD error: ") << hex << int(card.errorCode()); | |||
| cout << ',' << int(card.errorData()) << dec << endl; | |||
| } | |||
| SysCall::halt(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| #if DEBUG_PRINT | |||
| void debugPrint() { | |||
| cout << F("FreeStack: ") << FreeStack() << endl; | |||
| cout << F("partStart: ") << relSector << endl; | |||
| cout << F("partSize: ") << partSize << endl; | |||
| cout << F("reserved: ") << reservedSectors << endl; | |||
| cout << F("fatStart: ") << fatStart << endl; | |||
| cout << F("fatSize: ") << fatSize << endl; | |||
| cout << F("dataStart: ") << dataStart << endl; | |||
| cout << F("clusterCount: "); | |||
| cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl; | |||
| cout << endl; | |||
| cout << F("Heads: ") << int(numberOfHeads) << endl; | |||
| cout << F("Sectors: ") << int(sectorsPerTrack) << endl; | |||
| cout << F("Cylinders: "); | |||
| cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl; | |||
| } | |||
| #endif // DEBUG_PRINT | |||
| //------------------------------------------------------------------------------ | |||
| // write cached block to the card | |||
| uint8_t writeCache(uint32_t lbn) { | |||
| return card.writeBlock(lbn, cache.data); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // initialize appropriate sizes for SD capacity | |||
| void initSizes() { | |||
| if (cardCapacityMB <= 6) { | |||
| sdError("Card is too small."); | |||
| } else if (cardCapacityMB <= 16) { | |||
| sectorsPerCluster = 2; | |||
| } else if (cardCapacityMB <= 32) { | |||
| sectorsPerCluster = 4; | |||
| } else if (cardCapacityMB <= 64) { | |||
| sectorsPerCluster = 8; | |||
| } else if (cardCapacityMB <= 128) { | |||
| sectorsPerCluster = 16; | |||
| } else if (cardCapacityMB <= 1024) { | |||
| sectorsPerCluster = 32; | |||
| } else if (cardCapacityMB <= 32768) { | |||
| sectorsPerCluster = 64; | |||
| } else { | |||
| // SDXC cards | |||
| sectorsPerCluster = 128; | |||
| } | |||
| cout << F("Blocks/Cluster: ") << int(sectorsPerCluster) << endl; | |||
| // set fake disk geometry | |||
| sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63; | |||
| if (cardCapacityMB <= 16) { | |||
| numberOfHeads = 2; | |||
| } else if (cardCapacityMB <= 32) { | |||
| numberOfHeads = 4; | |||
| } else if (cardCapacityMB <= 128) { | |||
| numberOfHeads = 8; | |||
| } else if (cardCapacityMB <= 504) { | |||
| numberOfHeads = 16; | |||
| } else if (cardCapacityMB <= 1008) { | |||
| numberOfHeads = 32; | |||
| } else if (cardCapacityMB <= 2016) { | |||
| numberOfHeads = 64; | |||
| } else if (cardCapacityMB <= 4032) { | |||
| numberOfHeads = 128; | |||
| } else { | |||
| numberOfHeads = 255; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // zero cache and optionally set the sector signature | |||
| void clearCache(uint8_t addSig) { | |||
| memset(&cache, 0, sizeof(cache)); | |||
| if (addSig) { | |||
| cache.mbr.mbrSig0 = BOOTSIG0; | |||
| cache.mbr.mbrSig1 = BOOTSIG1; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // zero FAT and root dir area on SD | |||
| void clearFatDir(uint32_t bgn, uint32_t count) { | |||
| clearCache(false); | |||
| #if USE_SDIO | |||
| for (uint32_t i = 0; i < count; i++) { | |||
| if (!card.writeBlock(bgn + i, cache.data)) { | |||
| sdError("Clear FAT/DIR writeBlock failed"); | |||
| } | |||
| if ((i & 0XFF) == 0) { | |||
| cout << '.'; | |||
| } | |||
| } | |||
| #else // USE_SDIO | |||
| if (!card.writeStart(bgn, count)) { | |||
| sdError("Clear FAT/DIR writeStart failed"); | |||
| } | |||
| for (uint32_t i = 0; i < count; i++) { | |||
| if ((i & 0XFF) == 0) { | |||
| cout << '.'; | |||
| } | |||
| if (!card.writeData(cache.data)) { | |||
| sdError("Clear FAT/DIR writeData failed"); | |||
| } | |||
| } | |||
| if (!card.writeStop()) { | |||
| sdError("Clear FAT/DIR writeStop failed"); | |||
| } | |||
| #endif // USE_SDIO | |||
| cout << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // return cylinder number for a logical block number | |||
| uint16_t lbnToCylinder(uint32_t lbn) { | |||
| return lbn / (numberOfHeads * sectorsPerTrack); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // return head number for a logical block number | |||
| uint8_t lbnToHead(uint32_t lbn) { | |||
| return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // return sector number for a logical block number | |||
| uint8_t lbnToSector(uint32_t lbn) { | |||
| return (lbn % sectorsPerTrack) + 1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // format and write the Master Boot Record | |||
| void writeMbr() { | |||
| clearCache(true); | |||
| part_t* p = cache.mbr.part; | |||
| p->boot = 0; | |||
| uint16_t c = lbnToCylinder(relSector); | |||
| if (c > 1023) { | |||
| sdError("MBR CHS"); | |||
| } | |||
| p->beginCylinderHigh = c >> 8; | |||
| p->beginCylinderLow = c & 0XFF; | |||
| p->beginHead = lbnToHead(relSector); | |||
| p->beginSector = lbnToSector(relSector); | |||
| p->type = partType; | |||
| uint32_t endLbn = relSector + partSize - 1; | |||
| c = lbnToCylinder(endLbn); | |||
| if (c <= 1023) { | |||
| p->endCylinderHigh = c >> 8; | |||
| p->endCylinderLow = c & 0XFF; | |||
| p->endHead = lbnToHead(endLbn); | |||
| p->endSector = lbnToSector(endLbn); | |||
| } else { | |||
| // Too big flag, c = 1023, h = 254, s = 63 | |||
| p->endCylinderHigh = 3; | |||
| p->endCylinderLow = 255; | |||
| p->endHead = 254; | |||
| p->endSector = 63; | |||
| } | |||
| p->firstSector = relSector; | |||
| p->totalSectors = partSize; | |||
| if (!writeCache(0)) { | |||
| sdError("write MBR"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // generate serial number from card size and micros since boot | |||
| uint32_t volSerialNumber() { | |||
| return (cardSizeBlocks << 8) + micros(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // format the SD as FAT16 | |||
| void makeFat16() { | |||
| uint32_t nc; | |||
| for (dataStart = 2 * BU16;; dataStart += BU16) { | |||
| nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; | |||
| fatSize = (nc + 2 + 255)/256; | |||
| uint32_t r = BU16 + 1 + 2 * fatSize + 32; | |||
| if (dataStart < r) { | |||
| continue; | |||
| } | |||
| relSector = dataStart - r + BU16; | |||
| break; | |||
| } | |||
| // check valid cluster count for FAT16 volume | |||
| if (nc < 4085 || nc >= 65525) { | |||
| sdError("Bad cluster count"); | |||
| } | |||
| reservedSectors = 1; | |||
| fatStart = relSector + reservedSectors; | |||
| partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32; | |||
| if (partSize < 32680) { | |||
| partType = 0X01; | |||
| } else if (partSize < 65536) { | |||
| partType = 0X04; | |||
| } else { | |||
| partType = 0X06; | |||
| } | |||
| // write MBR | |||
| writeMbr(); | |||
| clearCache(true); | |||
| fat_boot_t* pb = &cache.fbs; | |||
| pb->jump[0] = 0XEB; | |||
| pb->jump[1] = 0X00; | |||
| pb->jump[2] = 0X90; | |||
| for (uint8_t i = 0; i < sizeof(pb->oemId); i++) { | |||
| pb->oemId[i] = ' '; | |||
| } | |||
| pb->bytesPerSector = 512; | |||
| pb->sectorsPerCluster = sectorsPerCluster; | |||
| pb->reservedSectorCount = reservedSectors; | |||
| pb->fatCount = 2; | |||
| pb->rootDirEntryCount = 512; | |||
| pb->mediaType = 0XF8; | |||
| pb->sectorsPerFat16 = fatSize; | |||
| pb->sectorsPerTrack = sectorsPerTrack; | |||
| pb->headCount = numberOfHeads; | |||
| pb->hidddenSectors = relSector; | |||
| pb->totalSectors32 = partSize; | |||
| pb->driveNumber = 0X80; | |||
| pb->bootSignature = EXTENDED_BOOT_SIG; | |||
| pb->volumeSerialNumber = volSerialNumber(); | |||
| memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel)); | |||
| memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType)); | |||
| // write partition boot sector | |||
| if (!writeCache(relSector)) { | |||
| sdError("FAT16 write PBS failed"); | |||
| } | |||
| // clear FAT and root directory | |||
| clearFatDir(fatStart, dataStart - fatStart); | |||
| clearCache(false); | |||
| cache.fat16[0] = 0XFFF8; | |||
| cache.fat16[1] = 0XFFFF; | |||
| // write first block of FAT and backup for reserved clusters | |||
| if (!writeCache(fatStart) | |||
| || !writeCache(fatStart + fatSize)) { | |||
| sdError("FAT16 reserve failed"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // format the SD as FAT32 | |||
| void makeFat32() { | |||
| uint32_t nc; | |||
| relSector = BU32; | |||
| for (dataStart = 2 * BU32;; dataStart += BU32) { | |||
| nc = (cardSizeBlocks - dataStart)/sectorsPerCluster; | |||
| fatSize = (nc + 2 + 127)/128; | |||
| uint32_t r = relSector + 9 + 2 * fatSize; | |||
| if (dataStart >= r) { | |||
| break; | |||
| } | |||
| } | |||
| // error if too few clusters in FAT32 volume | |||
| if (nc < 65525) { | |||
| sdError("Bad cluster count"); | |||
| } | |||
| reservedSectors = dataStart - relSector - 2 * fatSize; | |||
| fatStart = relSector + reservedSectors; | |||
| partSize = nc * sectorsPerCluster + dataStart - relSector; | |||
| // type depends on address of end sector | |||
| // max CHS has lbn = 16450560 = 1024*255*63 | |||
| if ((relSector + partSize) <= 16450560) { | |||
| // FAT32 | |||
| partType = 0X0B; | |||
| } else { | |||
| // FAT32 with INT 13 | |||
| partType = 0X0C; | |||
| } | |||
| writeMbr(); | |||
| clearCache(true); | |||
| fat32_boot_t* pb = &cache.fbs32; | |||
| pb->jump[0] = 0XEB; | |||
| pb->jump[1] = 0X00; | |||
| pb->jump[2] = 0X90; | |||
| for (uint8_t i = 0; i < sizeof(pb->oemId); i++) { | |||
| pb->oemId[i] = ' '; | |||
| } | |||
| pb->bytesPerSector = 512; | |||
| pb->sectorsPerCluster = sectorsPerCluster; | |||
| pb->reservedSectorCount = reservedSectors; | |||
| pb->fatCount = 2; | |||
| pb->mediaType = 0XF8; | |||
| pb->sectorsPerTrack = sectorsPerTrack; | |||
| pb->headCount = numberOfHeads; | |||
| pb->hidddenSectors = relSector; | |||
| pb->totalSectors32 = partSize; | |||
| pb->sectorsPerFat32 = fatSize; | |||
| pb->fat32RootCluster = 2; | |||
| pb->fat32FSInfo = 1; | |||
| pb->fat32BackBootBlock = 6; | |||
| pb->driveNumber = 0X80; | |||
| pb->bootSignature = EXTENDED_BOOT_SIG; | |||
| pb->volumeSerialNumber = volSerialNumber(); | |||
| memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel)); | |||
| memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType)); | |||
| // write partition boot sector and backup | |||
| if (!writeCache(relSector) | |||
| || !writeCache(relSector + 6)) { | |||
| sdError("FAT32 write PBS failed"); | |||
| } | |||
| clearCache(true); | |||
| // write extra boot area and backup | |||
| if (!writeCache(relSector + 2) | |||
| || !writeCache(relSector + 8)) { | |||
| sdError("FAT32 PBS ext failed"); | |||
| } | |||
| fat32_fsinfo_t* pf = &cache.fsinfo; | |||
| pf->leadSignature = FSINFO_LEAD_SIG; | |||
| pf->structSignature = FSINFO_STRUCT_SIG; | |||
| pf->freeCount = 0XFFFFFFFF; | |||
| pf->nextFree = 0XFFFFFFFF; | |||
| // write FSINFO sector and backup | |||
| if (!writeCache(relSector + 1) | |||
| || !writeCache(relSector + 7)) { | |||
| sdError("FAT32 FSINFO failed"); | |||
| } | |||
| clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster); | |||
| clearCache(false); | |||
| cache.fat32[0] = 0x0FFFFFF8; | |||
| cache.fat32[1] = 0x0FFFFFFF; | |||
| cache.fat32[2] = 0x0FFFFFFF; | |||
| // write first block of FAT and backup for reserved clusters | |||
| if (!writeCache(fatStart) | |||
| || !writeCache(fatStart + fatSize)) { | |||
| sdError("FAT32 reserve failed"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // flash erase all data | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void eraseCard() { | |||
| cout << endl << F("Erasing\n"); | |||
| uint32_t firstBlock = 0; | |||
| uint32_t lastBlock; | |||
| uint16_t n = 0; | |||
| do { | |||
| lastBlock = firstBlock + ERASE_SIZE - 1; | |||
| if (lastBlock >= cardSizeBlocks) { | |||
| lastBlock = cardSizeBlocks - 1; | |||
| } | |||
| if (!card.erase(firstBlock, lastBlock)) { | |||
| sdError("erase failed"); | |||
| } | |||
| cout << '.'; | |||
| if ((n++)%32 == 31) { | |||
| cout << endl; | |||
| } | |||
| firstBlock += ERASE_SIZE; | |||
| } while (firstBlock < cardSizeBlocks); | |||
| cout << endl; | |||
| if (!card.readBlock(0, cache.data)) { | |||
| sdError("readBlock"); | |||
| } | |||
| cout << hex << showbase << setfill('0') << internal; | |||
| cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl; | |||
| cout << dec << noshowbase << setfill(' ') << right; | |||
| cout << F("Erase done\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void formatCard() { | |||
| cout << endl; | |||
| cout << F("Formatting\n"); | |||
| initSizes(); | |||
| if (card.type() != SD_CARD_TYPE_SDHC) { | |||
| cout << F("FAT16\n"); | |||
| makeFat16(); | |||
| } else { | |||
| cout << F("FAT32\n"); | |||
| makeFat32(); | |||
| } | |||
| #if DEBUG_PRINT | |||
| debugPrint(); | |||
| #endif // DEBUG_PRINT | |||
| cout << F("Format done\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| char c; | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| cout << F("Type any character to start\n"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| // Discard any extra characters. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| cout << F( | |||
| "\n" | |||
| "This program can erase and/or format SD/SDHC cards.\n" | |||
| "\n" | |||
| "Erase uses the card's fast flash erase command.\n" | |||
| "Flash erase sets all data to 0X00 for most cards\n" | |||
| "and 0XFF for a few vendor's cards.\n" | |||
| "\n" | |||
| "Cards larger than 2 GB will be formatted FAT32 and\n" | |||
| "smaller cards will be formatted FAT16.\n" | |||
| "\n" | |||
| "Warning, all data on the card will be erased.\n" | |||
| "Enter 'Y' to continue: "); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| c = Serial.read(); | |||
| cout << c << endl; | |||
| if (c != 'Y') { | |||
| cout << F("Quiting, you did not enter 'Y'.\n"); | |||
| return; | |||
| } | |||
| // Read any existing Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| cout << F( | |||
| "\n" | |||
| "Options are:\n" | |||
| "E - erase the card and skip formatting.\n" | |||
| "F - erase and then format the card. (recommended)\n" | |||
| "Q - quick format the card without erase.\n" | |||
| "\n" | |||
| "Enter option: "); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| c = Serial.read(); | |||
| cout << c << endl; | |||
| if (!strchr("EFQ", c)) { | |||
| cout << F("Quiting, invalid option entered.") << endl; | |||
| return; | |||
| } | |||
| #if USE_SDIO | |||
| if (!card.begin()) { | |||
| sdError("card.begin failed"); | |||
| } | |||
| #else // USE_SDIO | |||
| if (!card.begin(chipSelect, SPI_SPEED)) { | |||
| cout << F( | |||
| "\nSD initialization failure!\n" | |||
| "Is the SD card inserted correctly?\n" | |||
| "Is chip select correct at the top of this program?\n"); | |||
| sdError("card.begin failed"); | |||
| } | |||
| #endif | |||
| cardSizeBlocks = card.cardSize(); | |||
| if (cardSizeBlocks == 0) { | |||
| sdError("cardSize"); | |||
| } | |||
| cardCapacityMB = (cardSizeBlocks + 2047)/2048; | |||
| cout << F("Card Size: ") << setprecision(0) << 1.048576*cardCapacityMB; | |||
| cout << F(" MB, (MB = 1,000,000 bytes)") << endl; | |||
| if (c == 'E' || c == 'F') { | |||
| eraseCard(); | |||
| } | |||
| if (c == 'F' || c == 'Q') { | |||
| formatCard(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,248 @@ | |||
| /* | |||
| * This program attempts to initialize an SD card and analyze its structure. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| #error Use new Version 2 SdInfo | |||
| // Set USE_SDIO to zero for SPI card access. | |||
| #define USE_SDIO 0 | |||
| /* | |||
| * SD chip select pin. Common values are: | |||
| * | |||
| * Arduino Ethernet shield, pin 4. | |||
| * SparkFun SD shield, pin 8. | |||
| * Adafruit SD shields and modules, pin 10. | |||
| * Default SD chip select is the SPI SS pin. | |||
| */ | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| /* | |||
| * Set DISABLE_CHIP_SELECT to disable a second SPI device. | |||
| * For example, with the Ethernet shield, set DISABLE_CHIP_SELECT | |||
| * to 10 to disable the Ethernet controller. | |||
| */ | |||
| const int8_t DISABLE_CHIP_SELECT = -1; | |||
| #if USE_SDIO | |||
| // Use faster SdioCardEX | |||
| SdFatSdioEX sd; | |||
| // SdFatSdio sd; | |||
| #else // USE_SDIO | |||
| SdFat sd; | |||
| #endif // USE_SDIO | |||
| // serial output steam | |||
| ArduinoOutStream cout(Serial); | |||
| // global for card size | |||
| uint32_t cardSize; | |||
| // global for card erase size | |||
| uint32_t eraseSize; | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash | |||
| #define sdErrorMsg(msg) sd.errorPrint(F(msg)); | |||
| //------------------------------------------------------------------------------ | |||
| uint8_t cidDmp() { | |||
| cid_t cid; | |||
| if (!sd.card()->readCID(&cid)) { | |||
| sdErrorMsg("readCID failed"); | |||
| return false; | |||
| } | |||
| cout << F("\nManufacturer ID: "); | |||
| cout << hex << int(cid.mid) << dec << endl; | |||
| cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl; | |||
| cout << F("Product: "); | |||
| for (uint8_t i = 0; i < 5; i++) { | |||
| cout << cid.pnm[i]; | |||
| } | |||
| cout << F("\nVersion: "); | |||
| cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl; | |||
| cout << F("Serial number: ") << hex << cid.psn << dec << endl; | |||
| cout << F("Manufacturing date: "); | |||
| cout << int(cid.mdt_month) << '/'; | |||
| cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl; | |||
| cout << endl; | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| uint8_t csdDmp() { | |||
| csd_t csd; | |||
| uint8_t eraseSingleBlock; | |||
| if (!sd.card()->readCSD(&csd)) { | |||
| sdErrorMsg("readCSD failed"); | |||
| return false; | |||
| } | |||
| if (csd.v1.csd_ver == 0) { | |||
| eraseSingleBlock = csd.v1.erase_blk_en; | |||
| eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; | |||
| } else if (csd.v2.csd_ver == 1) { | |||
| eraseSingleBlock = csd.v2.erase_blk_en; | |||
| eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low; | |||
| } else { | |||
| cout << F("csd version error\n"); | |||
| return false; | |||
| } | |||
| eraseSize++; | |||
| cout << F("cardSize: ") << 0.000512*cardSize; | |||
| cout << F(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n"); | |||
| cout << F("eraseSingleBlock: "); | |||
| if (eraseSingleBlock) { | |||
| cout << F("true\n"); | |||
| } else { | |||
| cout << F("false\n"); | |||
| } | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // print partition table | |||
| uint8_t partDmp() { | |||
| mbr_t mbr; | |||
| if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) { | |||
| sdErrorMsg("read MBR failed"); | |||
| return false; | |||
| } | |||
| for (uint8_t ip = 1; ip < 5; ip++) { | |||
| part_t *pt = &mbr.part[ip - 1]; | |||
| if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) { | |||
| cout << F("\nNo MBR. Assuming Super Floppy format.\n"); | |||
| return true; | |||
| } | |||
| } | |||
| cout << F("\nSD Partition Table\n"); | |||
| cout << F("part,boot,type,start,length\n"); | |||
| for (uint8_t ip = 1; ip < 5; ip++) { | |||
| part_t *pt = &mbr.part[ip - 1]; | |||
| cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type); | |||
| cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl; | |||
| } | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void volDmp() { | |||
| cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl; | |||
| cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl; | |||
| cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl; | |||
| cout << F("freeClusters: "); | |||
| uint32_t volFree = sd.vol()->freeClusterCount(); | |||
| cout << volFree << endl; | |||
| float fs = 0.000512*volFree*sd.vol()->blocksPerCluster(); | |||
| cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl; | |||
| cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl; | |||
| cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl; | |||
| cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl; | |||
| cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl; | |||
| if (sd.vol()->dataStartBlock() % eraseSize) { | |||
| cout << F("Data area is not aligned on flash erase boundaries!\n"); | |||
| cout << F("Download and use formatter from www.sdcard.org!\n"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| // use uppercase in hex and use 0X base prefix | |||
| cout << uppercase << showbase << endl; | |||
| // F stores strings in flash to save RAM | |||
| cout << F("SdFat version: ") << SD_FAT_VERSION << endl; | |||
| #if !USE_SDIO | |||
| if (DISABLE_CHIP_SELECT < 0) { | |||
| cout << F( | |||
| "\nAssuming the SD is the only SPI device.\n" | |||
| "Edit DISABLE_CHIP_SELECT to disable another device.\n"); | |||
| } else { | |||
| cout << F("\nDisabling SPI device on pin "); | |||
| cout << int(DISABLE_CHIP_SELECT) << endl; | |||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | |||
| } | |||
| cout << F("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT); | |||
| cout << F("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n"); | |||
| #endif // !USE_SDIO | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| // Read any existing Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| // F stores strings in flash to save RAM | |||
| cout << F("\ntype any character to start\n"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| uint32_t t = millis(); | |||
| #if USE_SDIO | |||
| if (!sd.cardBegin()) { | |||
| sdErrorMsg("\ncardBegin failed"); | |||
| return; | |||
| } | |||
| #else // USE_SDIO | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) { | |||
| sdErrorMsg("cardBegin failed"); | |||
| return; | |||
| } | |||
| #endif // USE_SDIO | |||
| t = millis() - t; | |||
| cardSize = sd.card()->cardSize(); | |||
| if (cardSize == 0) { | |||
| sdErrorMsg("cardSize failed"); | |||
| return; | |||
| } | |||
| cout << F("\ninit time: ") << t << " ms" << endl; | |||
| cout << F("\nCard type: "); | |||
| switch (sd.card()->type()) { | |||
| case SD_CARD_TYPE_SD1: | |||
| cout << F("SD1\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SD2: | |||
| cout << F("SD2\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SDHC: | |||
| if (cardSize < 70000000) { | |||
| cout << F("SDHC\n"); | |||
| } else { | |||
| cout << F("SDXC\n"); | |||
| } | |||
| break; | |||
| default: | |||
| cout << F("Unknown\n"); | |||
| } | |||
| if (!cidDmp()) { | |||
| return; | |||
| } | |||
| if (!csdDmp()) { | |||
| return; | |||
| } | |||
| uint32_t ocr; | |||
| if (!sd.card()->readOCR(&ocr)) { | |||
| sdErrorMsg("\nreadOCR failed"); | |||
| return; | |||
| } | |||
| cout << F("OCR: ") << hex << ocr << dec << endl; | |||
| if (!partDmp()) { | |||
| return; | |||
| } | |||
| if (!sd.fsBegin()) { | |||
| sdErrorMsg("\nFile System initialization failed.\n"); | |||
| return; | |||
| } | |||
| volDmp(); | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| // An example of the SdFatSoftSpi template class. | |||
| // This example is for an Adafruit Data Logging Shield on a Mega. | |||
| // Software SPI is required on Mega since this shield connects to pins 10-13. | |||
| // This example will also run on an Uno and other boards using software SPI. | |||
| // | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #error See Version 2 software SPI example | |||
| #if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h | |||
| // | |||
| // Pin numbers in templates must be constants. | |||
| const uint8_t SOFT_MISO_PIN = 12; | |||
| const uint8_t SOFT_MOSI_PIN = 11; | |||
| const uint8_t SOFT_SCK_PIN = 13; | |||
| // | |||
| // Chip select may be constant or RAM variable. | |||
| const uint8_t SD_CHIP_SELECT_PIN = 10; | |||
| // SdFat software SPI template | |||
| SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd; | |||
| // Test file. | |||
| SdFile file; | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| Serial.println("Type any character to start"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| if (!sd.begin(SD_CHIP_SELECT_PIN)) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| if (!file.open("SoftSPI.txt", O_RDWR | O_CREAT)) { | |||
| sd.errorHalt(F("open failed")); | |||
| } | |||
| file.println(F("This line was printed using software SPI.")); | |||
| file.rewind(); | |||
| while (file.available()) { | |||
| Serial.write(file.read()); | |||
| } | |||
| file.close(); | |||
| Serial.println(F("Done.")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| #else // ENABLE_SOFTWARE_SPI_CLASS | |||
| #error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h | |||
| #endif //ENABLE_SOFTWARE_SPI_CLASS | |||
| @@ -1,6 +1,7 @@ | |||
| // Benchmark comparing SdFile and StdioStream. | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // Define PRINT_FIELD nonzero to use printField. | |||
| #define PRINT_FIELD 0 | |||
| @@ -24,9 +25,9 @@ const char* label[] = | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| uint32_t printSize; | |||
| uint32_t printSize = 0; | |||
| uint32_t stdioSize = 0; | |||
| uint32_t printTime; | |||
| uint32_t printTime = 0; | |||
| uint32_t stdioTime = 0; | |||
| Serial.begin(9600); | |||
| @@ -0,0 +1,169 @@ | |||
| // Simple performance test for Teensy 3.5/3.6 SDHC. | |||
| // Demonstrates yield() efficiency. | |||
| // Warning SdFatSdio and SdFatSdioEX normally should | |||
| // not both be used in a program. | |||
| // Each has its own cache and member variables. | |||
| #include "SdFat.h" | |||
| #error See Version 2 SDIO example | |||
| // 32 KiB buffer. | |||
| const size_t BUF_DIM = 32768; | |||
| // 8 MiB file. | |||
| const uint32_t FILE_SIZE = 256UL*BUF_DIM; | |||
| SdFatSdio sd; | |||
| SdFatSdioEX sdEx; | |||
| File file; | |||
| uint8_t buf[BUF_DIM]; | |||
| // buffer as uint32_t | |||
| uint32_t* buf32 = (uint32_t*)buf; | |||
| // Total usec in read/write calls. | |||
| uint32_t totalMicros = 0; | |||
| // Time in yield() function. | |||
| uint32_t yieldMicros = 0; | |||
| // Number of yield calls. | |||
| uint32_t yieldCalls = 0; | |||
| // Max busy time for single yield call. | |||
| uint32_t yieldMaxUsec = 0; | |||
| // Control access to the two versions of SdFat. | |||
| bool useEx = false; | |||
| //----------------------------------------------------------------------------- | |||
| bool sdBusy() { | |||
| return useEx ? sdEx.card()->isBusy() : sd.card()->isBusy(); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void errorHalt(const char* msg) { | |||
| if (useEx) { | |||
| sdEx.errorHalt(msg); | |||
| } else { | |||
| sd.errorHalt(msg); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| uint32_t kHzSdClk() { | |||
| return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Replace "weak" system yield() function. | |||
| void yield() { | |||
| // Only count cardBusy time. | |||
| if (!sdBusy()) { | |||
| return; | |||
| } | |||
| uint32_t m = micros(); | |||
| yieldCalls++; | |||
| while (sdBusy()) { | |||
| // Do something here. | |||
| } | |||
| m = micros() - m; | |||
| if (m > yieldMaxUsec) { | |||
| yieldMaxUsec = m; | |||
| } | |||
| yieldMicros += m; | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void runTest() { | |||
| // Zero Stats | |||
| totalMicros = 0; | |||
| yieldMicros = 0; | |||
| yieldCalls = 0; | |||
| yieldMaxUsec = 0; | |||
| if (!file.open("TeensyDemo.bin", O_RDWR | O_CREAT)) { | |||
| errorHalt("open failed"); | |||
| } | |||
| Serial.println("\nsize,write,read"); | |||
| Serial.println("bytes,KB/sec,KB/sec"); | |||
| for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) { | |||
| file.truncate(0); | |||
| uint32_t nRdWr = FILE_SIZE/nb; | |||
| Serial.print(nb); | |||
| Serial.print(','); | |||
| uint32_t t = micros(); | |||
| for (uint32_t n = 0; n < nRdWr; n++) { | |||
| // Set start and end of buffer. | |||
| buf32[0] = n; | |||
| buf32[nb/4 - 1] = n; | |||
| if (nb != file.write(buf, nb)) { | |||
| errorHalt("write failed"); | |||
| } | |||
| } | |||
| t = micros() - t; | |||
| totalMicros += t; | |||
| Serial.print(1000.0*FILE_SIZE/t); | |||
| Serial.print(','); | |||
| file.rewind(); | |||
| t = micros(); | |||
| for (uint32_t n = 0; n < nRdWr; n++) { | |||
| if ((int)nb != file.read(buf, nb)) { | |||
| errorHalt("read failed"); | |||
| } | |||
| // crude check of data. | |||
| if (buf32[0] != n || buf32[nb/4 - 1] != n) { | |||
| errorHalt("data check"); | |||
| } | |||
| } | |||
| t = micros() - t; | |||
| totalMicros += t; | |||
| Serial.println(1000.0*FILE_SIZE/t); | |||
| } | |||
| file.close(); | |||
| Serial.print("\ntotalMicros "); | |||
| Serial.println(totalMicros); | |||
| Serial.print("yieldMicros "); | |||
| Serial.println(yieldMicros); | |||
| Serial.print("yieldCalls "); | |||
| Serial.println(yieldCalls); | |||
| Serial.print("yieldMaxUsec "); | |||
| Serial.println(yieldMaxUsec); | |||
| Serial.print("kHzSdClk "); | |||
| Serial.println(kHzSdClk()); | |||
| Serial.println("Done"); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) { | |||
| } | |||
| Serial.println("SdFatSdioEX uses extended multi-block transfers without DMA."); | |||
| Serial.println("SdFatSdio uses a traditional DMA SDIO implementation."); | |||
| Serial.println("Note the difference is speed and busy yield time.\n"); | |||
| } | |||
| //----------------------------------------------------------------------------- | |||
| void loop() { | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read()); | |||
| Serial.println("Type '1' for SdFatSdioEX or '2' for SdFatSdio"); | |||
| while (!Serial.available()) { | |||
| } | |||
| char c = Serial.read(); | |||
| if (c != '1' && c != '2') { | |||
| Serial.println("Invalid input"); | |||
| return; | |||
| } | |||
| if (c =='1') { | |||
| useEx = true; | |||
| if (!sdEx.begin()) { | |||
| sd.initErrorHalt("SdFatSdioEX begin() failed"); | |||
| } | |||
| // make sdEx the current volume. | |||
| sdEx.chvol(); | |||
| } else { | |||
| useEx = false; | |||
| if (!sd.begin()) { | |||
| sd.initErrorHalt("SdFatSdio begin() failed"); | |||
| } | |||
| // make sd the current volume. | |||
| sd.chvol(); | |||
| } | |||
| runTest(); | |||
| } | |||
| @@ -5,7 +5,6 @@ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| SdFat sd; | |||
| SdFile file; | |||
| @@ -52,25 +51,12 @@ void dateTime(uint16_t* date, uint16_t* time) { | |||
| * Function to print all timestamps. | |||
| */ | |||
| void printTimestamps(SdFile& f) { | |||
| dir_t d; | |||
| if (!f.dirEntry(&d)) { | |||
| error("f.dirEntry failed"); | |||
| } | |||
| cout << F("Creation: "); | |||
| f.printFatDate(d.creationDate); | |||
| cout << ' '; | |||
| f.printFatTime(d.creationTime); | |||
| cout << endl; | |||
| cout << F("Modify: "); | |||
| f.printFatDate(d.lastWriteDate); | |||
| cout <<' '; | |||
| f.printFatTime(d.lastWriteTime); | |||
| cout << endl; | |||
| cout << F("Access: "); | |||
| f.printFatDate(d.lastAccessDate); | |||
| f.printCreateDateTime(&Serial); | |||
| cout << endl << F("Modify: "); | |||
| f.printModifyDateTime(&Serial); | |||
| cout << endl << F("Access: "); | |||
| f.printAccessDateTime(&Serial); | |||
| cout << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| @@ -36,7 +36,7 @@ void printFreeSpace() { | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| @@ -55,22 +55,22 @@ void setup() { | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| // Insure no TEST_FILE. | |||
| // Insure no TEST_FILE. | |||
| sd.remove(TEST_FILE); | |||
| cout << F("\nFirst call to freeClusterCount scans the FAT.\n\n"); | |||
| printFreeSpace(); | |||
| cout << F("Create and write to ") << TEST_FILE << endl; | |||
| if (!file.open(TEST_FILE, O_WRONLY | O_CREAT)) { | |||
| sd.errorHalt(F("Create failed")); | |||
| } | |||
| file.print(F("Cause a cluster to be allocated")); | |||
| file.close(); | |||
| cout << F("\nSecond freeClusterCount call is faster if\n"); | |||
| cout << F("MAINTAIN_FREE_CLUSTER_COUNT is nonzero.\n\n"); | |||
| printFreeSpace(); | |||
| cout << F("Remove ") << TEST_FILE << endl << endl; | |||
| @@ -0,0 +1,222 @@ | |||
| /* | |||
| * This program is a simple binary write/read benchmark. | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| #include "FreeStack.h" | |||
| // Set USE_SDIO to zero for SPI card access. | |||
| #define USE_SDIO 0 | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // Size of read/write. | |||
| const size_t BUF_SIZE = 512; | |||
| // File size in MB where MB = 1,000,000 bytes. | |||
| const uint32_t FILE_SIZE_MB = 5; | |||
| // Write pass count. | |||
| const uint8_t WRITE_COUNT = 2; | |||
| // Read pass count. | |||
| const uint8_t READ_COUNT = 2; | |||
| //============================================================================== | |||
| // End of configuration constants. | |||
| //------------------------------------------------------------------------------ | |||
| // File size in bytes. | |||
| const uint32_t FILE_SIZE = 1000000UL*FILE_SIZE_MB; | |||
| uint8_t buf[BUF_SIZE]; | |||
| // file system | |||
| #if USE_SDIO | |||
| // Traditional DMA version. | |||
| // SdFatSdio sd; | |||
| // Faster version. | |||
| SdFatSdioEX sd; | |||
| #else // USE_SDIO | |||
| SdFat sd; | |||
| #endif // USE_SDIO | |||
| // Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O. | |||
| // Requires dedicated use of the SPI bus. | |||
| // SdFatEX sd; | |||
| // Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI. | |||
| // Args are misoPin, mosiPin, sckPin. | |||
| // SdFatSoftSpi<6, 7, 5> sd; | |||
| // test file | |||
| SdFile file; | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // Store error strings in flash to save RAM. | |||
| #define error(s) sd.errorHalt(F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void cidDmp() { | |||
| cid_t cid; | |||
| if (!sd.card()->readCID(&cid)) { | |||
| error("readCID failed"); | |||
| } | |||
| cout << F("\nManufacturer ID: "); | |||
| cout << hex << int(cid.mid) << dec << endl; | |||
| cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl; | |||
| cout << F("Product: "); | |||
| for (uint8_t i = 0; i < 5; i++) { | |||
| cout << cid.pnm[i]; | |||
| } | |||
| cout << F("\nVersion: "); | |||
| cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl; | |||
| cout << F("Serial number: ") << hex << cid.psn << dec << endl; | |||
| cout << F("Manufacturing date: "); | |||
| cout << int(cid.mdt_month) << '/'; | |||
| cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl; | |||
| cout << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| delay(1000); | |||
| cout << F("\nUse a freshly formatted SD for best performance.\n"); | |||
| // use uppercase in hex and use 0X base prefix | |||
| cout << uppercase << showbase << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| float s; | |||
| uint32_t t; | |||
| uint32_t maxLatency; | |||
| uint32_t minLatency; | |||
| uint32_t totalLatency; | |||
| // Discard any input. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.available() && Serial.read() >= 0); | |||
| // F( stores strings in flash to save RAM | |||
| cout << F("Type any character to start\n"); | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| cout << F("chipSelect: ") << int(chipSelect) << endl; | |||
| cout << F("FreeStack: ") << FreeStack() << endl; | |||
| #if USE_SDIO | |||
| if (!sd.begin()) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| #else // USE_SDIO | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| #endif // USE_SDIO | |||
| cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl; | |||
| cout << F("Card size: ") << sd.card()->cardSize()*512E-9; | |||
| cout << F(" GB (GB = 1E9 bytes)") << endl; | |||
| cidDmp(); | |||
| // open or create file - truncate existing file. | |||
| if (!file.open("bench.dat", O_RDWR | O_CREAT | O_TRUNC)) { | |||
| error("open failed"); | |||
| } | |||
| // fill buf with known data | |||
| for (size_t i = 0; i < (BUF_SIZE-2); i++) { | |||
| buf[i] = 'A' + (i % 26); | |||
| } | |||
| buf[BUF_SIZE-2] = '\r'; | |||
| buf[BUF_SIZE-1] = '\n'; | |||
| cout << F("File size ") << FILE_SIZE_MB << F(" MB\n"); | |||
| cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n"); | |||
| cout << F("Starting write test, please wait.") << endl << endl; | |||
| // do write test | |||
| uint32_t n = FILE_SIZE/sizeof(buf); | |||
| cout <<F("write speed and latency") << endl; | |||
| cout << F("speed,max,min,avg") << endl; | |||
| cout << F("KB/Sec,usec,usec,usec") << endl; | |||
| for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) { | |||
| file.truncate(0); | |||
| maxLatency = 0; | |||
| minLatency = 9999999; | |||
| totalLatency = 0; | |||
| t = millis(); | |||
| for (uint32_t i = 0; i < n; i++) { | |||
| uint32_t m = micros(); | |||
| if (file.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| sd.errorPrint("write failed"); | |||
| file.close(); | |||
| return; | |||
| } | |||
| m = micros() - m; | |||
| if (maxLatency < m) { | |||
| maxLatency = m; | |||
| } | |||
| if (minLatency > m) { | |||
| minLatency = m; | |||
| } | |||
| totalLatency += m; | |||
| } | |||
| file.sync(); | |||
| t = millis() - t; | |||
| s = file.fileSize(); | |||
| cout << s/t <<',' << maxLatency << ',' << minLatency; | |||
| cout << ',' << totalLatency/n << endl; | |||
| } | |||
| cout << endl << F("Starting read test, please wait.") << endl; | |||
| cout << endl <<F("read speed and latency") << endl; | |||
| cout << F("speed,max,min,avg") << endl; | |||
| cout << F("KB/Sec,usec,usec,usec") << endl; | |||
| // do read test | |||
| for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { | |||
| file.rewind(); | |||
| maxLatency = 0; | |||
| minLatency = 9999999; | |||
| totalLatency = 0; | |||
| t = millis(); | |||
| for (uint32_t i = 0; i < n; i++) { | |||
| buf[BUF_SIZE-1] = 0; | |||
| uint32_t m = micros(); | |||
| int32_t nr = file.read(buf, sizeof(buf)); | |||
| if (nr != sizeof(buf)) { | |||
| sd.errorPrint("read failed"); | |||
| file.close(); | |||
| return; | |||
| } | |||
| m = micros() - m; | |||
| if (maxLatency < m) { | |||
| maxLatency = m; | |||
| } | |||
| if (minLatency > m) { | |||
| minLatency = m; | |||
| } | |||
| totalLatency += m; | |||
| if (buf[BUF_SIZE-1] != '\n') { | |||
| error("data check"); | |||
| } | |||
| } | |||
| s = file.fileSize(); | |||
| t = millis() - t; | |||
| cout << s/t <<',' << maxLatency << ',' << minLatency; | |||
| cout << ',' << totalLatency/n << endl; | |||
| } | |||
| cout << endl << F("Done") << endl; | |||
| file.close(); | |||
| } | |||
| @@ -0,0 +1,106 @@ | |||
| /* | |||
| * This program demonstrates use of SdFile::rename() | |||
| * and SdFat::rename(). | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system | |||
| SdFat sd; | |||
| // Serial print stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt(F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| cout << F("Insert an empty SD. Type any character to start.") << endl; | |||
| while (!Serial.available()) { | |||
| SysCall::yield(); | |||
| } | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| } | |||
| // Remove file/dirs from previous run. | |||
| if (sd.exists("dir2/DIR3/NAME3.txt")) { | |||
| cout << F("Removing /dir2/DIR3/NAME3.txt") << endl; | |||
| if (!sd.remove("dir2/DIR3/NAME3.txt") || | |||
| !sd.rmdir("dir2/DIR3/") || | |||
| !sd.rmdir("dir2/")) { | |||
| error("remove/rmdir failed"); | |||
| } | |||
| } | |||
| // create a file and write one line to the file | |||
| SdFile file("Name1.txt", O_WRONLY | O_CREAT); | |||
| if (!file.isOpen()) { | |||
| error("Name1.txt"); | |||
| } | |||
| file.println("A test line for Name1.txt"); | |||
| // rename the file name2.txt and add a line. | |||
| if (!file.rename("name2.txt")) { | |||
| error("name2.txt"); | |||
| } | |||
| file.println("A test line for name2.txt"); | |||
| // list files | |||
| cout << F("------") << endl; | |||
| sd.ls(LS_R); | |||
| // make a new directory - "Dir1" | |||
| if (!sd.mkdir("Dir1")) { | |||
| error("Dir1"); | |||
| } | |||
| // move file into Dir1, rename it NAME3.txt and add a line | |||
| if (!file.rename("Dir1/NAME3.txt")) { | |||
| error("NAME3.txt"); | |||
| } | |||
| file.println("A line for Dir1/NAME3.txt"); | |||
| // list files | |||
| cout << F("------") << endl; | |||
| sd.ls(LS_R); | |||
| // make directory "dir2" | |||
| if (!sd.mkdir("dir2")) { | |||
| error("dir2"); | |||
| } | |||
| // close file before rename(oldPath, newPath) | |||
| file.close(); | |||
| // move Dir1 into dir2 and rename it DIR3 | |||
| if (!sd.rename("Dir1", "dir2/DIR3")) { | |||
| error("dir2/DIR3"); | |||
| } | |||
| // open file for append in new location and add a line | |||
| if (!file.open("dir2/DIR3/NAME3.txt", O_WRONLY | O_APPEND)) { | |||
| error("dir2/DIR3/NAME3.txt"); | |||
| } | |||
| file.println("A line for dir2/DIR3/NAME3.txt"); | |||
| file.close(); | |||
| // list files | |||
| cout << F("------") << endl; | |||
| sd.ls(LS_R); | |||
| cout << F("Done") << endl; | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,66 @@ | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/STM32Test/STM32Test.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/STM32Test/STM32Test.ino" | |||
| 8c8 | |||
| < | |||
| --- | |||
| > #error See new Version 2 STM32 example | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/SdFormatter/SdFormatter.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/SdFormatter/SdFormatter.ino" | |||
| 14c14 | |||
| < | |||
| --- | |||
| > #error use new Version 2 SdFormatter | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/SdInfo/SdInfo.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/SdInfo/SdInfo.ino" | |||
| 7c7 | |||
| < | |||
| --- | |||
| > #error Use new Version 2 SdInfo | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/SoftwareSpi/SoftwareSpi.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/SoftwareSpi/SoftwareSpi.ino" | |||
| 7a8 | |||
| > #error See Version 2 software SPI example | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/StdioBench/StdioBench.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/StdioBench/StdioBench.ino" | |||
| 3a4 | |||
| > #include "sdios.h" | |||
| 27c28 | |||
| < uint32_t printSize; | |||
| --- | |||
| > uint32_t printSize = 0; | |||
| 29c30 | |||
| < uint32_t printTime; | |||
| --- | |||
| > uint32_t printTime = 0; | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/TeensySdioDemo/TeensySdioDemo.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/TeensySdioDemo/TeensySdioDemo.ino" | |||
| 9c9 | |||
| < | |||
| --- | |||
| > #error See Version 2 SDIO example | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/Timestamp/Timestamp.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/Timestamp/Timestamp.ino" | |||
| 8d7 | |||
| < | |||
| 55,59d53 | |||
| < dir_t d; | |||
| < if (!f.dirEntry(&d)) { | |||
| < error("f.dirEntry failed"); | |||
| < } | |||
| < | |||
| 61,73c55,59 | |||
| < f.printFatDate(d.creationDate); | |||
| < cout << ' '; | |||
| < f.printFatTime(d.creationTime); | |||
| < cout << endl; | |||
| < | |||
| < cout << F("Modify: "); | |||
| < f.printFatDate(d.lastWriteDate); | |||
| < cout <<' '; | |||
| < f.printFatTime(d.lastWriteTime); | |||
| < cout << endl; | |||
| < | |||
| < cout << F("Access: "); | |||
| < f.printFatDate(d.lastAccessDate); | |||
| --- | |||
| > f.printCreateDateTime(&Serial); | |||
| > cout << endl << F("Modify: "); | |||
| > f.printModifyDateTime(&Serial); | |||
| > cout << endl << F("Access: "); | |||
| > f.printAccessDateTime(&Serial); | |||
| diff -rb "C:\\Users\\bill\\Documents\\Arduino\\libraries\\SdFat\\examples/wipe/wipe.ino" "C:\\Users\\bill\\Documents\\ArduinoSdFat\\libraries\\SdFat\\examples\\examplesV1/wipe/wipe.ino" | |||
| 1a2 | |||
| > #error wipe is not supported in SdFat V2. Use bool format(print_t* pr = nullptr). | |||
| @@ -1,4 +1,5 @@ | |||
| // Example to wipe all data from an already formatted SD. | |||
| #error wipe is not supported in SdFat V2. Use bool format(print_t* pr = nullptr). | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| const int chipSelect = SS; | |||
| @@ -39,4 +40,4 @@ void setup() { | |||
| } | |||
| void loop() { | |||
| } | |||
| } | |||
| @@ -1,27 +1,66 @@ | |||
| /* | |||
| * This program demonstrates use of SdFile::rename() | |||
| * and SdFat::rename(). | |||
| * This program demonstrates use of rename(). | |||
| */ | |||
| #include <SPI.h> | |||
| #include "SdFat.h" | |||
| #include "sdios.h" | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, | |||
| // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. | |||
| #define SD_FAT_TYPE 0 | |||
| // file system | |||
| /* | |||
| Change the value of SD_CS_PIN if you are using SPI and | |||
| your hardware does not use the default value, SS. | |||
| Common values are: | |||
| Arduino Ethernet shield: pin 4 | |||
| Sparkfun SD shield: pin 8 | |||
| Adafruit SD shields and modules: pin 10 | |||
| */ | |||
| // SDCARD_SS_PIN is defined for the built-in SD on some boards. | |||
| #ifndef SDCARD_SS_PIN | |||
| const uint8_t SD_CS_PIN = SS; | |||
| #else // SDCARD_SS_PIN | |||
| // Assume built-in SD is used. | |||
| const uint8_t SD_CS_PIN = SDCARD_SS_PIN; | |||
| #endif // SDCARD_SS_PIN | |||
| // Try to select the best SD card configuration. | |||
| #if HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdioConfig(FIFO_SDIO) | |||
| #elif ENABLE_DEDICATED_SPI | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI) | |||
| #else // HAS_SDIO_CLASS | |||
| #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI) | |||
| #endif // HAS_SDIO_CLASS | |||
| #if SD_FAT_TYPE == 0 | |||
| SdFat sd; | |||
| File file; | |||
| #elif SD_FAT_TYPE == 1 | |||
| SdFat32 sd; | |||
| File32 file; | |||
| #elif SD_FAT_TYPE == 2 | |||
| SdExFat sd; | |||
| ExFile file; | |||
| #elif SD_FAT_TYPE == 3 | |||
| SdFs sd; | |||
| FsFile file; | |||
| #else // SD_FAT_TYPE | |||
| #error Invalid SD_FAT_TYPE | |||
| #endif // SD_FAT_TYPE | |||
| // Serial print stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt(F(s)) | |||
| #define error(s) sd.errorHalt(&Serial, F(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| // Wait for USB Serial | |||
| // Wait for USB Serial | |||
| while (!Serial) { | |||
| SysCall::yield(); | |||
| } | |||
| @@ -32,8 +71,8 @@ void setup() { | |||
| // Initialize at the highest speed supported by the board that is | |||
| // not over 50 MHz. Try a lower speed if SPI errors occur. | |||
| if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) { | |||
| sd.initErrorHalt(); | |||
| if (!sd.begin(SD_CONFIG)) { | |||
| sd.initErrorHalt(&Serial); | |||
| } | |||
| // Remove file/dirs from previous run. | |||
| @@ -46,15 +85,13 @@ void setup() { | |||
| } | |||
| } | |||
| // create a file and write one line to the file | |||
| SdFile file("Name1.txt", O_WRONLY | O_CREAT); | |||
| if (!file.isOpen()) { | |||
| if (!file.open("Name1.txt", O_WRONLY | O_CREAT)) { | |||
| error("Name1.txt"); | |||
| } | |||
| file.println("A test line for Name1.txt"); | |||
| // rename the file name2.txt and add a line. | |||
| // sd.vwd() is the volume working directory, root. | |||
| if (!file.rename(sd.vwd(), "name2.txt")) { | |||
| if (!file.rename("name2.txt")) { | |||
| error("name2.txt"); | |||
| } | |||
| file.println("A test line for name2.txt"); | |||
| @@ -69,7 +106,7 @@ void setup() { | |||
| } | |||
| // move file into Dir1, rename it NAME3.txt and add a line | |||
| if (!file.rename(sd.vwd(), "Dir1/NAME3.txt")) { | |||
| if (!file.rename("Dir1/NAME3.txt")) { | |||
| error("NAME3.txt"); | |||
| } | |||
| file.println("A line for Dir1/NAME3.txt"); | |||
| @@ -104,4 +141,4 @@ void setup() { | |||
| cout << F("Done") << endl; | |||
| } | |||
| void loop() {} | |||
| void loop() {} | |||
| @@ -1,98 +0,0 @@ | |||
| Static Tests of the Arduino Internal ADC. | |||
| Several people have asked about the DC accuracy of the Arduino ADC when used in my data logging applications at slow sample rates. | |||
| Here are my results of some "hobby level" measurements of the Arduino ADC. | |||
| One question is how important is the ADC clock rate. I did measurents for an ADC clock rate of 125 kHz to 2MHz. | |||
| Another question is how much does Noise Reduction Mode help. I did a series of measurements using this mode. | |||
| Noise Reduction Mode only reduced the mean absolute error slightly. | |||
| I do calibration to remove Offset Error and Gain Error. Calibration is very important for good accuracy. | |||
| These tests depend on the Arduino voltage regulator providing a stable voltage during the tests. The Arduino ADC reference voltage is Vcc for these tests. This may not be realistic for practical applications | |||
| Integral Non-linearity (INL) is the main remaining source of error. | |||
| Here are my results for static (DC) tests of the internal ADC for three UNOs. | |||
| The Arduinos are powered by a high quality nine volt power supply. | |||
| These tests measure a DC level so do not include problems due to time jitter, S/H time, and other dynamic errors. | |||
| There are several studies of the dynamic behavior of the Arduino ADC that determine ENOB (Effective Number Of Bits). | |||
| I used a shield with a 12-bit MCP4921 DAC to generate voltage levels. This ADC has an output buffer so it provides a very low impedance source. | |||
| I measured the voltage of the DAC with a calibrated 18-bit MCP3422 ADC on the shield. | |||
| I used DAC levels from 20 to 4075 to avoid zero offset errors at low voltages and DAC buffer problems at high voltages. | |||
| Each series of measurements has 4056 data points. | |||
| This is a voltage range of about 0.023 to 4.972 volts. | |||
| I calibrated the Arduino ADC for each series of measurements with a linear fit of the form. | |||
| v = a + b*adcValue | |||
| Errors are the difference between the value measured with the 18-bit ADC and the calibrated value measured with the AVR ADC. | |||
| I also show the results for no calibration, the NoCal column, using the datasheet formula. | |||
| Vin = Vref*adcValue/1024 | |||
| The rows in the tables tables are. | |||
| Min - minimum error in millivolts | |||
| Max - maximum error in millivolts | |||
| MAE - mean absolute error in millivolts | |||
| The columns in the tables are: | |||
| Ideal - results for a perfect 10-bit ADC for comparison. | |||
| NoCal - datasheet formula (5/1024)*adcValue with Noise Reduction Mode. | |||
| NR128 - Noise Reduction mode with Prescaler of 128 (ADC clock of 125 kHz). | |||
| PS128 - analogRead with Prescaler of 128 (ADC clock of 125 kHz). | |||
| PS64 - analogRead with Prescaler of 64 (ADC clock of 250 kHz). | |||
| PS32 - analogRead with Prescaler of 32 (ADC clock of 500 kHz). | |||
| PS16 - analogRead with Prescaler of 16 (ADC clock of 1 MHz). | |||
| PS8 - analogRead with Prescaler of 8 (ADC clock of 2 MHz). | |||
| Results for three UNO Arduinos | |||
| First Arduino - Error Millivolts | |||
| Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8 | |||
| Min -2.44 -2.43 -3.72 -4.01 -3.88 -4.53 -6.57 -27.18 | |||
| Max 2.44 11.69 3.74 4.24 4.15 5.17 8.69 23.21 | |||
| MAE 1.22 5.02 1.33 1.38 1.37 1.44 1.96 4.11 | |||
| Second Arduino - Error Millivolts | |||
| Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8 | |||
| Min -2.44 -9.24 -4.87 -4.86 -5.05 -5.34 -6.52 -24.04 | |||
| Max 2.44 11.62 3.95 4.64 4.69 5.71 8.41 21.29 | |||
| MAE 1.22 5.33 1.41 1.43 1.44 1.53 2.02 4.05 | |||
| Third Arduino - Error Millivolts | |||
| Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8 | |||
| Min -2.44 -7.88 -4.12 -4.40 -4.32 -4.41 -6.97 -26.93 | |||
| Max 2.44 12.53 3.80 4.04 4.18 5.27 8.84 24.59 | |||
| MAE 1.22 4.85 1.29 1.33 1.34 1.42 1.91 4.10 | |||