| @@ -0,0 +1,98 @@ | |||
| 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 | |||
| @@ -0,0 +1,21 @@ | |||
| Maximum Sample Rate Table | |||
| ADC clock kHz | |||
| 125 250 500 1000 | |||
| pins | |||
| 1 7692 14286 25000 40000 | |||
| 2 3810 6667 11111 16667 | |||
| 3 2572 4790 8421 13559 | |||
| 4 1942 3636 6452 10526 | |||
| 5 1559 2930 5229 8602 | |||
| 6 1303 2454 4396 7273 | |||
| 7 1119 2111 3791 6299 | |||
| 8 980 1852 3333 5556 | |||
| 9 872 1649 2974 4969 | |||
| 10 786 1487 2685 4494 | |||
| 11 715 1354 2446 4103 | |||
| 12 656 1242 2247 3774 | |||
| 13 606 1148 2078 3493 | |||
| 14 563 1067 1932 3252 | |||
| 15 525 996 1806 3042 | |||
| 16 493 935 1695 2857 | |||
| @@ -0,0 +1,39 @@ | |||
| #ifndef AnalogBinLogger_h | |||
| #define AnalogBinLogger_h | |||
| //------------------------------------------------------------------------------ | |||
| // First block of file. | |||
| struct metadata_t { | |||
| unsigned long adcFrequency; // ADC clock frequency | |||
| unsigned long cpuFrequency; // CPU clock frequency | |||
| unsigned long sampleInterval; // Sample interval in CPU cycles. | |||
| unsigned long recordEightBits; // Size of ADC values, nonzero for 8-bits. | |||
| unsigned long pinCount; // Number of analog pins in a sample. | |||
| unsigned long pinNumber[123]; // List of pin numbers in a sample. | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for 8-bit ADC mode. | |||
| const size_t DATA_DIM8 = 508; | |||
| struct block8_t { | |||
| unsigned short count; // count of data bytes | |||
| unsigned short overrun; // count of overruns since last block | |||
| unsigned char data[DATA_DIM8]; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for 10-bit ADC mode. | |||
| const size_t DATA_DIM16 = 254; | |||
| struct block16_t { | |||
| unsigned short count; // count of data bytes | |||
| unsigned short overrun; // count of overruns since last block | |||
| unsigned short data[DATA_DIM16]; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for PC use | |||
| struct adcdata_t { | |||
| unsigned short count; // count of data bytes | |||
| unsigned short overrun; // count of overruns since last block | |||
| union { | |||
| unsigned char u8[DATA_DIM8]; | |||
| unsigned short u16[DATA_DIM16]; | |||
| } data; | |||
| }; | |||
| #endif // AnalogBinLogger_h | |||
| @@ -0,0 +1,82 @@ | |||
| #include <stdio.h> | |||
| #include "AnalogBinLogger.h" | |||
| FILE *source; | |||
| FILE *destination; | |||
| int count = 0; | |||
| int main(int argc, char** argv) { | |||
| metadata_t meta; | |||
| adcdata_t adc; | |||
| // Make sure no padding/size problems. | |||
| if (sizeof(meta) != 512 || sizeof(adc) != 512) { | |||
| printf("block size error\n"); | |||
| return 0; | |||
| } | |||
| if (argc != 3) { | |||
| printf("missing arguments:\n"); | |||
| printf("%s binFile csvFile\n", argv[0]); | |||
| return 0; | |||
| } | |||
| source = fopen(argv[1], "rb"); | |||
| if (!source) { | |||
| printf("open failed for %s\n", argv[1]); | |||
| return 0; | |||
| } | |||
| if (fread(&meta, sizeof(meta), 1, source) != 1) { | |||
| printf("read meta data failed\n"); | |||
| return 0; | |||
| } | |||
| if ( meta.pinCount == 0 | |||
| || meta.pinCount > (sizeof(meta.pinNumber)/sizeof(meta.pinNumber[0])) | |||
| || meta.adcFrequency < 50000 || meta.adcFrequency > 4000000) { | |||
| printf("Invalid meta data\n"); | |||
| return 0; | |||
| } | |||
| destination = fopen(argv[2], "w"); | |||
| if (!destination) { | |||
| printf("open failed for %s\n", argv[2]); | |||
| return 0; | |||
| } | |||
| int pinCount = meta.pinCount; | |||
| printf("pinCount: %d\n", pinCount); | |||
| printf("Sample pins:"); | |||
| for (unsigned i = 0; i < meta.pinCount; i++) { | |||
| printf(" %d", meta.pinNumber[i]); | |||
| } | |||
| printf("\n"); | |||
| printf("ADC clock rate: %g kHz\n", 0.001*meta.adcFrequency); | |||
| float sampleInterval = (float)meta.sampleInterval/(float)meta.cpuFrequency; | |||
| printf("Sample rate: %g per sec\n", 1.0/sampleInterval); | |||
| printf("Sample interval: %.4f usec\n", 1.0e6*sampleInterval); | |||
| fprintf(destination, "Interval,%.4f,usec\n", 1.0e6*sampleInterval); | |||
| // Write header with pin numbers | |||
| for (int i = 0; i < ((int)meta.pinCount - 1); i++) { | |||
| fprintf(destination, "pin%d,", meta.pinNumber[i]); | |||
| } | |||
| fprintf(destination, "pin%d\n", meta.pinNumber[meta.pinCount - 1]); | |||
| unsigned maxCount = meta.recordEightBits ? DATA_DIM8 : DATA_DIM16; | |||
| while (!feof(source)) { | |||
| if (fread(&adc, sizeof(adc), 1, source) != 1) break; | |||
| if (adc.count > maxCount) { | |||
| printf("****Invalid data block****\n"); | |||
| return 0; | |||
| } | |||
| if (adc.overrun) { | |||
| fprintf(destination, "Overruns,%d\n", adc.overrun); | |||
| } | |||
| for (int i = 0; i < adc.count; i++) { | |||
| unsigned value = meta.recordEightBits ? adc.data.u8[i] : adc.data.u16[i]; | |||
| if ((i + 1)%pinCount) { | |||
| fprintf(destination, "%d,", value); | |||
| } else { | |||
| fprintf(destination, "%d\n", value); | |||
| } | |||
| } | |||
| count += adc.count; | |||
| } | |||
| printf("%d ADC values read\n", count); | |||
| fclose(source); | |||
| fclose(destination); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,96 @@ | |||
| AnalogBinLogger.ino logs analog data to a binary SD file at high rates. | |||
| Samples are logged at regular intervals by using timer1. Timer/Counter1 | |||
| Compare Match B is used to trigger the ADC for the first pin in a sample. | |||
| The ADC is triggered for remaining sample pins in the ADC conversion complete | |||
| interrupt routine. | |||
| Data is captured in the ADC interrupt routine and saved in 512 byte buffers. | |||
| Buffered data is written to the SD in a function called from loop(). The | |||
| entire data set is written to a large contiguous file as a single multi-block | |||
| write. This reduces write latency problems. | |||
| Many inexpensive SD cards work well at lower rates. I used a $6.00 | |||
| SanDisk 4 GB class 4 card for testing. | |||
| SanDisk class 4 cards work well at fairly high rates. I used the 4 GB SanDisk | |||
| card to log a single pin at 40,000 samples per second. | |||
| You may need to increase the time between samples if your card has higher | |||
| latency. Using a Mega Arduino can help since it has more buffering. | |||
| The bintocsv folder contains a PC program for converting binary files to | |||
| CSV files. I have included a executable for Windows. Linux and Mac users | |||
| can build from the included source files. bintocvs is a command line program. | |||
| bintocsv binFile csvFile | |||
| AnalogBinLogger requires a recent version of the SdFat library. The SdFat | |||
| folder contains a beta version I used for development. | |||
| The latest stable version is here: | |||
| http://code.google.com/p/sdfatlib/downloads/list | |||
| You also need to install the included BufferedWriter library. It provides | |||
| fast text formatting. | |||
| Example data for a 2 kHz sine wave logged at 40,000 samples per second is | |||
| shown in DATA.PNG and FFT.PNG shows a FFT of the data. See ExcelFFT.pdf | |||
| in the ADCdocs folder for details on calculating a FFT. | |||
| The accuracy of the ADC samples depends on the ADC clock rate. See the | |||
| ADC_ENOB.PNG file for a plot of accuracy vs ADC clock frequency. | |||
| See files in the ADCdocs folder for more information on ADC accuracy. | |||
| To modify this program you will need a good knowledge of the Arduino | |||
| ADC, timer1 and C++ programming. This is not for the newbie. | |||
| I have an LED and resistor connected to pin 3 to signal fatal errors and | |||
| data overruns. Fatal errors are indicated by a blinking led. Overrun errors | |||
| are indicated by a solid lit led. The count of samples dropped is written | |||
| to the SD and data logging continues. | |||
| You can disable the error led feature by setting the error pin number negative: | |||
| To use AnalogBinLogger, install these items. | |||
| Place the BufferWriter and SdFat folders in your sketchbook libraries folder. | |||
| Place the AnalogIsrLogger folder in your sketchbook folder. | |||
| You must edit the configuration constants at the beginning of the program | |||
| to set the sample pins, sample rate, and other configuration values. | |||
| Initially the program is setup to log the first five analog pins at 5000 | |||
| samples per second. Change these values to suit your needs. | |||
| See RateTable.txt for maximum allowed sample rates vs pin count and ADC clock | |||
| frequency. | |||
| The program has four commands: | |||
| c - convert file to CSV | |||
| d - dump data to Serial | |||
| e - overrun error details | |||
| r - record ADC data | |||
| All commands can be terminated by entering a character from the serial monitor. | |||
| The c command converts the current binary file to a text file. Entering a | |||
| character on the serial monitor terminates the command. | |||
| The d command converts the binary file to text and displays it on the serial | |||
| monitor. Entering a character on the serial monitor terminates the command. | |||
| The e command displays details about overruns in the current binary file. | |||
| Data overruns happen when data samples are lost due to long write latency | |||
| of the SD. | |||
| The r command will record ADC data to a binary file. It will terminate | |||
| when a character is entered on the serial monitor or the the maximum file | |||
| block count has been reached. | |||
| A number of program options can be set by changing constants at the beginning | |||
| of the program. | |||
| @@ -0,0 +1,62 @@ | |||
| Support has been added for the Arduino Due. | |||
| You must connect your SD socket to the 6-pin "ISP connector". You must have short | |||
| wires or a custom shield to run at full speed, 42 MHz. | |||
| If you have problems use a lower SPI speed. You can also check for SPI | |||
| errors by editing SdFatCobfig.h to enable CRC checking. | |||
| You should be be able to use any digital pin for SD chip select. The default | |||
| pin is SS which is pin 10 for Due. | |||
| The default SPI rate is 42 MHz. You can set SD chip select and the SPI rate | |||
| by calling: | |||
| bool SdFat::begin(uint8_t chipSelectPin, uint8_t spiRateID); | |||
| The second argument, spiRateID, sets the SCK rate and can be these symbols: | |||
| SPI_FULL_SPEED - 42 MHz | |||
| SPI_DIV3_SPEED - 28 MHz | |||
| SPI_HALF_SPEED - 21 MHz | |||
| SPI_DIV6_SPEED - 14 MHz | |||
| SPI_QUARTER_SPEED 10.5 MHz | |||
| SPI_EIGHTH_SPEED 5.25 MHz | |||
| Large reads and writes use fast multi-block SD read/write commands. For optimal | |||
| speed, use records that are a multiple of 512 bytes. | |||
| Run the bench.ino example to explore large read/write speed. | |||
| Replace this line: | |||
| #define BUF_SIZE 100 | |||
| With a large size like this: | |||
| #define BUF_SIZE 8192 | |||
| For best results the record size should be a power of two (512, 1024, 2048, | |||
| 4096, 8192). In this case records will be aligned with FAT cluster boundaries. | |||
| Since Due is fast, increase the test file size by editing this line: | |||
| #define FILE_SIZE_MB 5 | |||
| Run the PrintBenchmark.ino example to compare text formatting speed of Due | |||
| with AVR boards. | |||
| A number of options are available to configure SPI for the Due board. | |||
| You can use the standard SPI.h library by editing SdFatConfig.h and set | |||
| USE_ARDUINO_SPI_LIBRARY nonzero. You must include SPI.h in your sketch. | |||
| Several options can be set in Sd2Card.cpp in the USE_NATIVE_SAM3X_SPI | |||
| section. These include USE_SAM3X_DMAC to control use of DMA and | |||
| USE_SAM3X_BUS_MATRIX_FIX to change Bus Matrix operation. Most people | |||
| will not need to change these. | |||
| @@ -0,0 +1,13 @@ | |||
| SdFat has support for multiple SD cards. This requires multiple instances | |||
| of SdFat objects. | |||
| You must edit SdFatConfig.h to enable multiple instances of SdFat. Set | |||
| USE_MULTIPLE_CARDS nonzero like this: | |||
| #define USE_MULTIPLE_CARDS 1 | |||
| Look at TwoCards.pde in the SdFat/examples folder. This example demonstrates | |||
| use of two SD cards. | |||
| Read WorkingDirectory.txt for more information on volume working | |||
| directories and the current working directory. | |||
| @@ -0,0 +1,21 @@ | |||
| For those who don't like too much documentation. | |||
| To use this library place the SdFat folder into the libraries | |||
| subfolder in your main sketches folder. You may need to | |||
| create the libraries folder. Restart the Arduino IDE if | |||
| it was open. | |||
| Run the QuickStart.ino sketch from the | |||
| libraries/SdFat/examples/QuickStart folder. Click the | |||
| IDE up-arrow icon then -> libraries -> SdFat -> QuickStart. | |||
| You can also click File -> Examples -> SdFat -> QuickStart. | |||
| If problems occur try reading more documentation and use these | |||
| forums for help: | |||
| http://forums.adafruit.com/ | |||
| http://arduino.cc/forum/ | |||
| If QuickStart.ino runs successfully try more examples. | |||
| @@ -0,0 +1,23 @@ | |||
| To enable support for SPI transactions, edit SfFatCinfig.h and modify these | |||
| defines. | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature | |||
| * of the standard Arduino SPI library. You must include SPI.h in your | |||
| * sketches when ENABLE_SPI_TRANSACTION is nonzero. | |||
| */ | |||
| #define ENABLE_SPI_TRANSACTION 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during | |||
| * SD card busy waits. | |||
| * | |||
| * This will allow interrupt routines to access the SPI bus if | |||
| * ENABLE_SPI_TRANSACTION is nonzero. | |||
| * | |||
| * Setting ENABLE_SPI_YIELD will introduce some extra overhead and will | |||
| * slightly slow transfer rates. A few older SD cards may fail when | |||
| * ENABLE_SPI_YIELD is nonzero. | |||
| */ | |||
| #define ENABLE_SPI_YIELD 0 | |||
| @@ -0,0 +1,10 @@ | |||
| <html> | |||
| <head> | |||
| <title>A web page that points a browser to a different page</title> | |||
| <meta http-equiv="refresh" content="0; URL=html/index.html"> | |||
| <meta name="keywords" content="automatic redirection"> | |||
| </head> | |||
| <body> | |||
| Your browser didn't automatically redirect. Open html/index.html manually. | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,119 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef ArduinoStream_h | |||
| #define ArduinoStream_h | |||
| /** | |||
| * \file | |||
| * \brief ArduinoInStream and ArduinoOutStream classes | |||
| */ | |||
| #include <bufstream.h> | |||
| //============================================================================== | |||
| /** | |||
| * \class ArduinoInStream | |||
| * \brief Input stream for Arduino Stream objects | |||
| */ | |||
| class ArduinoInStream : public ibufstream { | |||
| public: | |||
| /** | |||
| * Constructor | |||
| * \param[in] hws hardware stream | |||
| * \param[in] buf buffer for input line | |||
| * \param[in] size size of input buffer | |||
| */ | |||
| ArduinoInStream(Stream &hws, char* buf, size_t size) { | |||
| m_hw = &hws; | |||
| m_line = buf; | |||
| m_size = size; | |||
| } | |||
| /** read a line. */ | |||
| void readline() { | |||
| size_t i = 0; | |||
| uint32_t t; | |||
| m_line[0] = '\0'; | |||
| while (!m_hw->available()) {} | |||
| while (1) { | |||
| t = millis(); | |||
| while (!m_hw->available()) { | |||
| if ((millis() - t) > 10) goto done; | |||
| } | |||
| if (i >= (m_size - 1)) { | |||
| setstate(failbit); | |||
| return; | |||
| } | |||
| m_line[i++] = m_hw->read(); | |||
| m_line[i] = '\0'; | |||
| } | |||
| done: | |||
| init(m_line); | |||
| } | |||
| protected: | |||
| /** Internal - do not use. | |||
| * \param[in] off | |||
| * \param[in] way | |||
| * \return true/false. | |||
| */ | |||
| bool seekoff(off_type off, seekdir way) {return false;} | |||
| /** Internal - do not use. | |||
| * \param[in] pos | |||
| * \return true/false. | |||
| */ | |||
| bool seekpos(pos_type pos) {return false;} | |||
| private: | |||
| char *m_line; | |||
| size_t m_size; | |||
| Stream* m_hw; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| * \class ArduinoOutStream | |||
| * \brief Output stream for Arduino Print objects | |||
| */ | |||
| class ArduinoOutStream : public ostream { | |||
| public: | |||
| /** constructor | |||
| * | |||
| * \param[in] pr Print object for this ArduinoOutStream. | |||
| */ | |||
| explicit ArduinoOutStream(Print& pr) : m_pr(&pr) {} | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** | |||
| * Internal do not use | |||
| * \param[in] c | |||
| */ | |||
| void putch(char c) { | |||
| if (c == '\n') m_pr->write('\r'); | |||
| m_pr->write(c); | |||
| } | |||
| void putstr(const char* str) {m_pr->write(str);} | |||
| bool seekoff(off_type off, seekdir way) {return false;} | |||
| bool seekpos(pos_type pos) {return false;} | |||
| bool sync() {return true;} | |||
| pos_type tellpos() {return 0;} | |||
| /// @endcond | |||
| private: | |||
| ArduinoOutStream() {} | |||
| Print* m_pr; | |||
| }; | |||
| #endif // ArduinoStream_h | |||
| @@ -0,0 +1,71 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <Arduino.h> | |||
| #if defined(UDR0) || defined(DOXYGEN) | |||
| #include <MinimumSerial.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set baud rate for serial port zero and enable in non interrupt mode. | |||
| * Do not call this function if you use another serial library. | |||
| * \param[in] baud rate | |||
| */ | |||
| void MinimumSerial::begin(uint32_t baud) { | |||
| uint16_t baud_setting; | |||
| // don't worry, the compiler will squeeze out F_CPU != 16000000UL | |||
| if (F_CPU != 16000000UL || baud != 57600) { | |||
| // Double the USART Transmission Speed | |||
| UCSR0A = 1 << U2X0; | |||
| baud_setting = (F_CPU / 4 / baud - 1) / 2; | |||
| } else { | |||
| // hardcoded exception for compatibility with the bootloader shipped | |||
| // with the Duemilanove and previous boards and the firmware on the 8U2 | |||
| // on the Uno and Mega 2560. | |||
| UCSR0A = 0; | |||
| baud_setting = (F_CPU / 8 / baud - 1) / 2; | |||
| } | |||
| // assign the baud_setting | |||
| UBRR0H = baud_setting >> 8; | |||
| UBRR0L = baud_setting; | |||
| // enable transmit and receive | |||
| UCSR0B |= (1 << TXEN0) | (1 << RXEN0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Unbuffered read | |||
| * \return -1 if no character is available or an available character. | |||
| */ | |||
| int MinimumSerial::read() { | |||
| if (UCSR0A & (1 << RXC0)) return UDR0; | |||
| return -1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Unbuffered write | |||
| * | |||
| * \param[in] b byte to write. | |||
| * \return 1 | |||
| */ | |||
| size_t MinimumSerial::write(uint8_t b) { | |||
| while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} | |||
| UDR0 = b; | |||
| return 1; | |||
| } | |||
| MinimumSerial MiniSerial; | |||
| #endif // defined(UDR0) || defined(DOXYGEN) | |||
| @@ -0,0 +1,36 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef MinimumSerial_h | |||
| #define MinimumSerial_h | |||
| /** | |||
| * \class MinimumSerial | |||
| * \brief mini serial class for the %SdFat library. | |||
| */ | |||
| class MinimumSerial : public Print { | |||
| public: | |||
| void begin(uint32_t baud); | |||
| int read(); | |||
| size_t write(uint8_t b); | |||
| using Print::write; | |||
| }; | |||
| #ifdef UDR0 | |||
| extern MinimumSerial MiniSerial; | |||
| #endif // UDR0 | |||
| #endif // MinimumSerial_h | |||
| @@ -0,0 +1,649 @@ | |||
| /* Arduino Sd2Card Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino Sd2Card Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino Sd2Card Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <Sd2Card.h> | |||
| #include <SdSpi.h> | |||
| #if !USE_SOFTWARE_SPI && ENABLE_SPI_TRANSACTION | |||
| #include <SPI.h> | |||
| #endif // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||
| // debug trace macro | |||
| #define SD_TRACE(m, b) | |||
| // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); | |||
| //------------------------------------------------------------------------------ | |||
| SdSpi Sd2Card::m_spi; | |||
| //============================================================================== | |||
| #if USE_SD_CRC | |||
| // CRC functions | |||
| //------------------------------------------------------------------------------ | |||
| static uint8_t CRC7(const uint8_t* data, uint8_t n) { | |||
| uint8_t crc = 0; | |||
| for (uint8_t i = 0; i < n; i++) { | |||
| uint8_t d = data[i]; | |||
| for (uint8_t j = 0; j < 8; j++) { | |||
| crc <<= 1; | |||
| if ((d & 0x80) ^ (crc & 0x80)) crc ^= 0x09; | |||
| d <<= 1; | |||
| } | |||
| } | |||
| return (crc << 1) | 1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| #if USE_SD_CRC == 1 | |||
| // slower CRC-CCITT | |||
| // uses the x^16,x^12,x^5,x^1 polynomial. | |||
| static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { | |||
| uint16_t crc = 0; | |||
| for (size_t i = 0; i < n; i++) { | |||
| crc = (uint8_t)(crc >> 8) | (crc << 8); | |||
| crc ^= data[i]; | |||
| crc ^= (uint8_t)(crc & 0xff) >> 4; | |||
| crc ^= crc << 12; | |||
| crc ^= (crc & 0xff) << 5; | |||
| } | |||
| return crc; | |||
| } | |||
| #elif USE_SD_CRC > 1 // CRC_CCITT | |||
| //------------------------------------------------------------------------------ | |||
| // faster CRC-CCITT | |||
| // uses the x^16,x^12,x^5,x^1 polynomial. | |||
| #ifdef __AVR__ | |||
| static const uint16_t crctab[] PROGMEM = { | |||
| #else // __AVR__ | |||
| static const uint16_t crctab[] = { | |||
| #endif // __AVR__ | |||
| 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, | |||
| 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, | |||
| 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, | |||
| 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, | |||
| 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, | |||
| 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, | |||
| 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, | |||
| 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, | |||
| 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, | |||
| 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, | |||
| 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, | |||
| 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, | |||
| 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, | |||
| 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, | |||
| 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, | |||
| 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, | |||
| 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, | |||
| 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, | |||
| 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, | |||
| 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, | |||
| 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, | |||
| 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, | |||
| 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, | |||
| 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, | |||
| 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, | |||
| 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, | |||
| 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, | |||
| 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, | |||
| 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, | |||
| 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, | |||
| 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, | |||
| 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 | |||
| }; | |||
| static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { | |||
| uint16_t crc = 0; | |||
| for (size_t i = 0; i < n; i++) { | |||
| #ifdef __AVR__ | |||
| crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0XFF]) ^ (crc << 8); | |||
| #else // __AVR__ | |||
| crc = crctab[(crc >> 8 ^ data[i]) & 0XFF] ^ (crc << 8); | |||
| #endif // __AVR__ | |||
| } | |||
| return crc; | |||
| } | |||
| #endif // CRC_CCITT | |||
| #endif // USE_SD_CRC | |||
| //============================================================================== | |||
| // Sd2Card member functions | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Initialize an SD flash memory card. | |||
| * | |||
| * \param[in] chipSelectPin SD chip select pin number. | |||
| * \param[in] sckDivisor SPI SCK clock rate divisor. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. The reason for failure | |||
| * can be determined by calling errorCode() and errorData(). | |||
| */ | |||
| bool Sd2Card::begin(uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| m_errorCode = m_type = 0; | |||
| m_chipSelectPin = chipSelectPin; | |||
| // 16-bit init start time allows over a minute | |||
| uint16_t t0 = (uint16_t)millis(); | |||
| uint32_t arg; | |||
| pinMode(m_chipSelectPin, OUTPUT); | |||
| digitalWrite(m_chipSelectPin, HIGH); | |||
| m_spi.begin(); | |||
| // set SCK rate for initialization commands | |||
| m_sckDivisor = SPI_SCK_INIT_DIVISOR; | |||
| m_spi.init(m_sckDivisor); | |||
| // must supply min of 74 clock cycles with CS high. | |||
| for (uint8_t i = 0; i < 10; i++) m_spi.send(0XFF); | |||
| // command to go idle in SPI mode | |||
| while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { | |||
| if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { | |||
| error(SD_CARD_ERROR_CMD0); | |||
| goto fail; | |||
| } | |||
| } | |||
| #if USE_SD_CRC | |||
| if (cardCommand(CMD59, 1) != R1_IDLE_STATE) { | |||
| error(SD_CARD_ERROR_CMD59); | |||
| goto fail; | |||
| } | |||
| #endif // USE_SD_CRC | |||
| // check SD version | |||
| while (1) { | |||
| if (cardCommand(CMD8, 0x1AA) == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE)) { | |||
| type(SD_CARD_TYPE_SD1); | |||
| break; | |||
| } | |||
| for (uint8_t i = 0; i < 4; i++) m_status = m_spi.receive(); | |||
| if (m_status == 0XAA) { | |||
| type(SD_CARD_TYPE_SD2); | |||
| break; | |||
| } | |||
| if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { | |||
| error(SD_CARD_ERROR_CMD8); | |||
| goto fail; | |||
| } | |||
| } | |||
| // initialize card and send host supports SDHC if SD2 | |||
| arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; | |||
| while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { | |||
| // check for timeout | |||
| if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { | |||
| error(SD_CARD_ERROR_ACMD41); | |||
| goto fail; | |||
| } | |||
| } | |||
| // if SD2 read OCR register to check for SDHC card | |||
| if (type() == SD_CARD_TYPE_SD2) { | |||
| if (cardCommand(CMD58, 0)) { | |||
| error(SD_CARD_ERROR_CMD58); | |||
| goto fail; | |||
| } | |||
| if ((m_spi.receive() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC); | |||
| // Discard rest of ocr - contains allowed voltage range. | |||
| for (uint8_t i = 0; i < 3; i++) m_spi.receive(); | |||
| } | |||
| chipSelectHigh(); | |||
| m_sckDivisor = sckDivisor; | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // send command and return error code. Return zero for OK | |||
| uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { | |||
| // select card | |||
| chipSelectLow(); | |||
| // wait if busy | |||
| waitNotBusy(SD_WRITE_TIMEOUT); | |||
| uint8_t *pa = reinterpret_cast<uint8_t *>(&arg); | |||
| #if USE_SD_CRC | |||
| // form message | |||
| uint8_t d[6] = {cmd | 0X40, pa[3], pa[2], pa[1], pa[0]}; | |||
| // add crc | |||
| d[5] = CRC7(d, 5); | |||
| // send message | |||
| for (uint8_t k = 0; k < 6; k++) m_spi.send(d[k]); | |||
| #else // USE_SD_CRC | |||
| // send command | |||
| m_spi.send(cmd | 0x40); | |||
| // send argument | |||
| for (int8_t i = 3; i >= 0; i--) m_spi.send(pa[i]); | |||
| // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA | |||
| m_spi.send(cmd == CMD0 ? 0X95 : 0X87); | |||
| #endif // USE_SD_CRC | |||
| // skip stuff byte for stop read | |||
| if (cmd == CMD12) m_spi.receive(); | |||
| // wait for response | |||
| for (uint8_t i = 0; ((m_status = m_spi.receive()) & 0X80) && i != 0XFF; i++) { | |||
| } | |||
| return m_status; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Determine the size of an SD flash memory card. | |||
| * | |||
| * \return The number of 512 byte data blocks in the card | |||
| * or zero if an error occurs. | |||
| */ | |||
| uint32_t Sd2Card::cardSize() { | |||
| csd_t csd; | |||
| if (!readCSD(&csd)) return 0; | |||
| if (csd.v1.csd_ver == 0) { | |||
| uint8_t read_bl_len = csd.v1.read_bl_len; | |||
| uint16_t c_size = (csd.v1.c_size_high << 10) | |||
| | (csd.v1.c_size_mid << 2) | csd.v1.c_size_low; | |||
| uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1) | |||
| | csd.v1.c_size_mult_low; | |||
| return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7); | |||
| } else if (csd.v2.csd_ver == 1) { | |||
| uint32_t c_size = 0X10000L * csd.v2.c_size_high + 0X100L | |||
| * (uint32_t)csd.v2.c_size_mid + csd.v2.c_size_low; | |||
| return (c_size + 1) << 10; | |||
| } else { | |||
| error(SD_CARD_ERROR_BAD_CSD); | |||
| return 0; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void Sd2Card::spiYield() { | |||
| #if ENABLE_SPI_YIELD && !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||
| chipSelectHigh(); | |||
| chipSelectLow(); | |||
| #endif // ENABLE_SPI_YIELD && !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void Sd2Card::chipSelectHigh() { | |||
| digitalWrite(m_chipSelectPin, HIGH); | |||
| // insure MISO goes high impedance | |||
| m_spi.send(0XFF); | |||
| #if !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||
| SPI.endTransaction(); | |||
| #endif // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void Sd2Card::chipSelectLow() { | |||
| #if !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||
| SPI.beginTransaction(SPISettings()); | |||
| #endif // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION) | |||
| m_spi.init(m_sckDivisor); | |||
| digitalWrite(m_chipSelectPin, LOW); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Erase a range of blocks. | |||
| * | |||
| * \param[in] firstBlock The address of the first block in the range. | |||
| * \param[in] lastBlock The address of the last block in the range. | |||
| * | |||
| * \note This function requests the SD card to do a flash erase for a | |||
| * range of blocks. The data on the card after an erase operation is | |||
| * either 0 or 1, depends on the card vendor. The card must support | |||
| * single block erase. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { | |||
| csd_t csd; | |||
| if (!readCSD(&csd)) goto fail; | |||
| // check for single block erase | |||
| if (!csd.v1.erase_blk_en) { | |||
| // erase size mask | |||
| uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; | |||
| if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { | |||
| // error card can't erase specified area | |||
| error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); | |||
| goto fail; | |||
| } | |||
| } | |||
| if (m_type != SD_CARD_TYPE_SDHC) { | |||
| firstBlock <<= 9; | |||
| lastBlock <<= 9; | |||
| } | |||
| if (cardCommand(CMD32, firstBlock) | |||
| || cardCommand(CMD33, lastBlock) | |||
| || cardCommand(CMD38, 0)) { | |||
| error(SD_CARD_ERROR_ERASE); | |||
| goto fail; | |||
| } | |||
| if (!waitNotBusy(SD_ERASE_TIMEOUT)) { | |||
| error(SD_CARD_ERROR_ERASE_TIMEOUT); | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Determine if card supports single block erase. | |||
| * | |||
| * \return The value one, true, is returned if single block erase is supported. | |||
| * The value zero, false, is returned if single block erase is not supported. | |||
| */ | |||
| bool Sd2Card::eraseSingleBlockEnable() { | |||
| csd_t csd; | |||
| return readCSD(&csd) ? csd.v1.erase_blk_en : false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Check for busy. MISO low indicates the card is busy. | |||
| * | |||
| * \return true if busy else false. | |||
| */ | |||
| bool Sd2Card::isBusy() { | |||
| bool rtn; | |||
| chipSelectLow(); | |||
| for (uint8_t i = 0; i < 8; i++) { | |||
| rtn = m_spi.receive() != 0XFF; | |||
| if (!rtn) break; | |||
| } | |||
| chipSelectHigh(); | |||
| return rtn; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Read a 512 byte block from an SD card. | |||
| * | |||
| * \param[in] blockNumber Logical block to be read. | |||
| * \param[out] dst Pointer to the location that will receive the data. | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) { | |||
| SD_TRACE("RB", blockNumber); | |||
| // use address if not SDHC card | |||
| if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; | |||
| if (cardCommand(CMD17, blockNumber)) { | |||
| error(SD_CARD_ERROR_CMD17); | |||
| goto fail; | |||
| } | |||
| return readData(dst, 512); | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Read one data block in a multiple block read sequence | |||
| * | |||
| * \param[in] dst Pointer to the location for the data to be read. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::readData(uint8_t *dst) { | |||
| chipSelectLow(); | |||
| return readData(dst, 512); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool Sd2Card::readData(uint8_t* dst, size_t count) { | |||
| #if USE_SD_CRC | |||
| uint16_t crc; | |||
| #endif // USE_SD_CRC | |||
| // wait for start block token | |||
| uint16_t t0 = millis(); | |||
| while ((m_status = m_spi.receive()) == 0XFF) { | |||
| if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) { | |||
| error(SD_CARD_ERROR_READ_TIMEOUT); | |||
| goto fail; | |||
| } | |||
| spiYield(); | |||
| } | |||
| if (m_status != DATA_START_BLOCK) { | |||
| error(SD_CARD_ERROR_READ); | |||
| goto fail; | |||
| } | |||
| // transfer data | |||
| if ((m_status = m_spi.receive(dst, count))) { | |||
| error(SD_CARD_ERROR_SPI_DMA); | |||
| goto fail; | |||
| } | |||
| #if USE_SD_CRC | |||
| // get crc | |||
| crc = (m_spi.receive() << 8) | m_spi.receive(); | |||
| if (crc != CRC_CCITT(dst, count)) { | |||
| error(SD_CARD_ERROR_READ_CRC); | |||
| goto fail; | |||
| } | |||
| #else | |||
| // discard crc | |||
| m_spi.receive(); | |||
| m_spi.receive(); | |||
| #endif // USE_SD_CRC | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** read CID or CSR register */ | |||
| bool Sd2Card::readRegister(uint8_t cmd, void* buf) { | |||
| uint8_t* dst = reinterpret_cast<uint8_t*>(buf); | |||
| if (cardCommand(cmd, 0)) { | |||
| error(SD_CARD_ERROR_READ_REG); | |||
| goto fail; | |||
| } | |||
| return readData(dst, 16); | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Start a read multiple blocks sequence. | |||
| * | |||
| * \param[in] blockNumber Address of first block in sequence. | |||
| * | |||
| * \note This function is used with readData() and readStop() for optimized | |||
| * multiple block reads. SPI chipSelect must be low for the entire sequence. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::readStart(uint32_t blockNumber) { | |||
| SD_TRACE("RS", blockNumber); | |||
| if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9; | |||
| if (cardCommand(CMD18, blockNumber)) { | |||
| error(SD_CARD_ERROR_CMD18); | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** End a read multiple blocks sequence. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::readStop() { | |||
| if (cardCommand(CMD12, 0)) { | |||
| error(SD_CARD_ERROR_CMD12); | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // wait for card to go not busy | |||
| bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) { | |||
| uint16_t t0 = millis(); | |||
| while (m_spi.receive() != 0XFF) { | |||
| if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail; | |||
| spiYield(); | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Writes a 512 byte block to an SD card. | |||
| * | |||
| * \param[in] blockNumber Logical block to be written. | |||
| * \param[in] src Pointer to the location of the data to be written. | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) { | |||
| SD_TRACE("WB", blockNumber); | |||
| // use address if not SDHC card | |||
| if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; | |||
| if (cardCommand(CMD24, blockNumber)) { | |||
| error(SD_CARD_ERROR_CMD24); | |||
| goto fail; | |||
| } | |||
| if (!writeData(DATA_START_BLOCK, src)) goto fail; | |||
| #define CHECK_PROGRAMMING 0 | |||
| #if CHECK_PROGRAMMING | |||
| // wait for flash programming to complete | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) { | |||
| error(SD_CARD_ERROR_WRITE_TIMEOUT); | |||
| goto fail; | |||
| } | |||
| // response is r2 so get and check two bytes for nonzero | |||
| if (cardCommand(CMD13, 0) || m_spi.receive()) { | |||
| error(SD_CARD_ERROR_WRITE_PROGRAMMING); | |||
| goto fail; | |||
| } | |||
| #endif // CHECK_PROGRAMMING | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Write one data block in a multiple block write sequence | |||
| * \param[in] src Pointer to the location of the data to be written. | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::writeData(const uint8_t* src) { | |||
| chipSelectLow(); | |||
| // wait for previous write to finish | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | |||
| if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto fail; | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| error(SD_CARD_ERROR_WRITE_MULTIPLE); | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // send one block of data for write block or write multiple blocks | |||
| bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { | |||
| #if USE_SD_CRC | |||
| uint16_t crc = CRC_CCITT(src, 512); | |||
| #else // USE_SD_CRC | |||
| uint16_t crc = 0XFFFF; | |||
| #endif // USE_SD_CRC | |||
| m_spi.send(token); | |||
| m_spi.send(src, 512); | |||
| m_spi.send(crc >> 8); | |||
| m_spi.send(crc & 0XFF); | |||
| m_status = m_spi.receive(); | |||
| if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) { | |||
| error(SD_CARD_ERROR_WRITE); | |||
| goto fail; | |||
| } | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Start a write multiple blocks sequence. | |||
| * | |||
| * \param[in] blockNumber Address of first block in sequence. | |||
| * \param[in] eraseCount The number of blocks to be pre-erased. | |||
| * | |||
| * \note This function is used with writeData() and writeStop() | |||
| * for optimized multiple block writes. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { | |||
| SD_TRACE("WS", blockNumber); | |||
| // send pre-erase count | |||
| if (cardAcmd(ACMD23, eraseCount)) { | |||
| error(SD_CARD_ERROR_ACMD23); | |||
| goto fail; | |||
| } | |||
| // use address if not SDHC card | |||
| if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; | |||
| if (cardCommand(CMD25, blockNumber)) { | |||
| error(SD_CARD_ERROR_CMD25); | |||
| goto fail; | |||
| } | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** End a write multiple blocks sequence. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool Sd2Card::writeStop() { | |||
| chipSelectLow(); | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | |||
| m_spi.send(STOP_TRAN_TOKEN); | |||
| if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail; | |||
| chipSelectHigh(); | |||
| return true; | |||
| fail: | |||
| error(SD_CARD_ERROR_STOP_TRAN); | |||
| chipSelectHigh(); | |||
| return false; | |||
| } | |||
| @@ -0,0 +1,201 @@ | |||
| /* Arduino Sd2Card Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino Sd2Card Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino Sd2Card Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef SpiCard_h | |||
| #define SpiCard_h | |||
| /** | |||
| * \file | |||
| * \brief Sd2Card class for V2 SD/SDHC cards | |||
| */ | |||
| #include <Arduino.h> | |||
| #include <SdFatConfig.h> | |||
| #include <SdInfo.h> | |||
| #include <SdSpi.h> | |||
| //------------------------------------------------------------------------------ | |||
| // SD card errors | |||
| /** timeout error for command CMD0 (initialize card in SPI mode) */ | |||
| uint8_t const SD_CARD_ERROR_CMD0 = 0X1; | |||
| /** CMD8 was not accepted - not a valid SD card*/ | |||
| uint8_t const SD_CARD_ERROR_CMD8 = 0X2; | |||
| /** card returned an error response for CMD12 (stop multiblock read) */ | |||
| uint8_t const SD_CARD_ERROR_CMD12 = 0X3; | |||
| /** card returned an error response for CMD17 (read block) */ | |||
| uint8_t const SD_CARD_ERROR_CMD17 = 0X4; | |||
| /** card returned an error response for CMD18 (read multiple block) */ | |||
| uint8_t const SD_CARD_ERROR_CMD18 = 0X5; | |||
| /** card returned an error response for CMD24 (write block) */ | |||
| uint8_t const SD_CARD_ERROR_CMD24 = 0X6; | |||
| /** WRITE_MULTIPLE_BLOCKS command failed */ | |||
| uint8_t const SD_CARD_ERROR_CMD25 = 0X7; | |||
| /** card returned an error response for CMD58 (read OCR) */ | |||
| uint8_t const SD_CARD_ERROR_CMD58 = 0X8; | |||
| /** SET_WR_BLK_ERASE_COUNT failed */ | |||
| uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; | |||
| /** ACMD41 initialization process timeout */ | |||
| uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; | |||
| /** card returned a bad CSR version field */ | |||
| uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; | |||
| /** erase block group command failed */ | |||
| uint8_t const SD_CARD_ERROR_ERASE = 0XC; | |||
| /** card not capable of single block erase */ | |||
| uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; | |||
| /** Erase sequence timed out */ | |||
| uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; | |||
| /** card returned an error token instead of read data */ | |||
| uint8_t const SD_CARD_ERROR_READ = 0XF; | |||
| /** read CID or CSD failed */ | |||
| uint8_t const SD_CARD_ERROR_READ_REG = 0X10; | |||
| /** timeout while waiting for start of read data */ | |||
| uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11; | |||
| /** card did not accept STOP_TRAN_TOKEN */ | |||
| uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12; | |||
| /** card returned an error token as a response to a write operation */ | |||
| uint8_t const SD_CARD_ERROR_WRITE = 0X13; | |||
| /** attempt to write protected block zero */ | |||
| uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14; // REMOVE - not used | |||
| /** card did not go ready for a multiple block write */ | |||
| uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15; | |||
| /** card returned an error to a CMD13 status check after a write */ | |||
| uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16; | |||
| /** timeout occurred during write programming */ | |||
| uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17; | |||
| /** incorrect rate selected */ | |||
| uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18; | |||
| /** init() not called */ | |||
| uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19; | |||
| /** card returned an error for CMD59 (CRC_ON_OFF) */ | |||
| uint8_t const SD_CARD_ERROR_CMD59 = 0X1A; | |||
| /** invalid read CRC */ | |||
| uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B; | |||
| /** SPI DMA error */ | |||
| uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C; | |||
| //------------------------------------------------------------------------------ | |||
| // card types | |||
| /** Standard capacity V1 SD card */ | |||
| uint8_t const SD_CARD_TYPE_SD1 = 1; | |||
| /** Standard capacity V2 SD card */ | |||
| uint8_t const SD_CARD_TYPE_SD2 = 2; | |||
| /** High Capacity SD card */ | |||
| uint8_t const SD_CARD_TYPE_SDHC = 3; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \class Sd2Card | |||
| * \brief Raw access to SD and SDHC flash memory cards. | |||
| */ | |||
| class Sd2Card { | |||
| public: | |||
| /** Construct an instance of Sd2Card. */ | |||
| Sd2Card() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} | |||
| bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN, | |||
| uint8_t sckDivisor = SPI_FULL_SPEED); | |||
| uint32_t cardSize(); | |||
| bool erase(uint32_t firstBlock, uint32_t lastBlock); | |||
| bool eraseSingleBlockEnable(); | |||
| /** | |||
| * Set SD error code. | |||
| * \param[in] code value for error code. | |||
| */ | |||
| void error(uint8_t code) {m_errorCode = code;} | |||
| /** | |||
| * \return error code for last error. See Sd2Card.h for a list of error codes. | |||
| */ | |||
| int errorCode() const {return m_errorCode;} | |||
| /** \return error data for last error. */ | |||
| int errorData() const {return m_status;} | |||
| /** | |||
| * Initialize an SD flash memory card. | |||
| * | |||
| * \param[in] chipSelectPin SD chip select pin number. | |||
| * \param[in] sckDivisor SPI SCK clock rate divisor. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. The reason for failure | |||
| * can be determined by calling errorCode() and errorData(). | |||
| */ | |||
| bool init(uint8_t sckDivisor = SPI_FULL_SPEED, | |||
| uint8_t chipSelectPin = SD_CHIP_SELECT_PIN) { | |||
| return begin(chipSelectPin, sckDivisor); | |||
| } | |||
| bool isBusy(); | |||
| bool readBlock(uint32_t block, uint8_t* dst); | |||
| /** | |||
| * Read a card's CID register. The CID contains card identification | |||
| * information such as Manufacturer ID, Product name, Product serial | |||
| * number and Manufacturing date. | |||
| * | |||
| * \param[out] cid pointer to area for returned data. | |||
| * | |||
| * \return true for success or false for failure. | |||
| */ | |||
| bool readCID(cid_t* cid) { | |||
| return readRegister(CMD10, cid); | |||
| } | |||
| /** | |||
| * Read a card's CSD register. The CSD contains Card-Specific Data that | |||
| * provides information regarding access to the card's contents. | |||
| * | |||
| * \param[out] csd pointer to area for returned data. | |||
| * | |||
| * \return true for success or false for failure. | |||
| */ | |||
| bool readCSD(csd_t* csd) { | |||
| return readRegister(CMD9, csd); | |||
| } | |||
| bool readData(uint8_t *dst); | |||
| bool readStart(uint32_t blockNumber); | |||
| bool readStop(); | |||
| /** Return SCK divisor. | |||
| * | |||
| * \return Requested SCK divisor. | |||
| */ | |||
| uint8_t sckDivisor() {return m_sckDivisor;} | |||
| /** Return the card type: SD V1, SD V2 or SDHC | |||
| * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. | |||
| */ | |||
| int type() const {return m_type;} | |||
| bool writeBlock(uint32_t blockNumber, const uint8_t* src); | |||
| bool writeData(const uint8_t* src); | |||
| bool writeStart(uint32_t blockNumber, uint32_t eraseCount); | |||
| bool writeStop(); | |||
| private: | |||
| //---------------------------------------------------------------------------- | |||
| // private functions | |||
| uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { | |||
| cardCommand(CMD55, 0); | |||
| return cardCommand(cmd, arg); | |||
| } | |||
| uint8_t cardCommand(uint8_t cmd, uint32_t arg); | |||
| bool readData(uint8_t* dst, size_t count); | |||
| bool readRegister(uint8_t cmd, void* buf); | |||
| void chipSelectHigh(); | |||
| void chipSelectLow(); | |||
| void spiYield(); | |||
| void type(uint8_t value) {m_type = value;} | |||
| bool waitNotBusy(uint16_t timeoutMillis); | |||
| bool writeData(uint8_t token, const uint8_t* src); | |||
| // private data | |||
| static SdSpi m_spi; | |||
| uint8_t m_chipSelectPin; | |||
| uint8_t m_errorCode; | |||
| uint8_t m_sckDivisor; | |||
| uint8_t m_status; | |||
| uint8_t m_type; | |||
| }; | |||
| #endif // SpiCard_h | |||
| @@ -0,0 +1,288 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef SdBaseFile_h | |||
| #define SdBaseFile_h | |||
| /** | |||
| * \file | |||
| * \brief SdBaseFile class | |||
| */ | |||
| #ifdef __AVR__ | |||
| #include <avr/pgmspace.h> | |||
| #else // __AVR__ | |||
| #ifndef PGM_P | |||
| /** pointer to flash for ARM */ | |||
| #define PGM_P const char* | |||
| #endif // PGM_P | |||
| #ifndef PSTR | |||
| /** store literal string in flash for ARM */ | |||
| #define PSTR(x) (x) | |||
| #endif // PSTR | |||
| #ifndef pgm_read_byte | |||
| /** read 8-bits from flash for ARM */ | |||
| #define pgm_read_byte(addr) (*(const unsigned char*)(addr)) | |||
| #endif // pgm_read_byte | |||
| #ifndef pgm_read_word | |||
| /** read 16-bits from flash for ARM */ | |||
| #define pgm_read_word(addr) (*(const uint16_t*)(addr)) | |||
| #endif // pgm_read_word | |||
| #ifndef PROGMEM | |||
| /** store in flash for ARM */ | |||
| #define PROGMEM const | |||
| #endif // PROGMEM | |||
| #endif // __AVR__ | |||
| #include <Arduino.h> | |||
| #include <SdFatConfig.h> | |||
| #include <SdVolume.h> | |||
| #include <utility/FatApiConstants.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \struct FatPos_t | |||
| * \brief internal type for istream | |||
| * do not use in user apps | |||
| */ | |||
| struct FatPos_t { | |||
| /** stream position */ | |||
| uint32_t position; | |||
| /** cluster for position */ | |||
| uint32_t cluster; | |||
| FatPos_t() : position(0), cluster(0) {} | |||
| }; | |||
| // values for m_type | |||
| /** This file has not been opened. */ | |||
| uint8_t const FAT_FILE_TYPE_CLOSED = 0; | |||
| /** A normal file */ | |||
| uint8_t const FAT_FILE_TYPE_NORMAL = 1; | |||
| /** A FAT12 or FAT16 root directory */ | |||
| uint8_t const FAT_FILE_TYPE_ROOT_FIXED = 2; | |||
| /** A FAT32 root directory */ | |||
| uint8_t const FAT_FILE_TYPE_ROOT32 = 3; | |||
| /** A subdirectory file*/ | |||
| uint8_t const FAT_FILE_TYPE_SUBDIR = 4; | |||
| /** Test value for directory type */ | |||
| uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \class SdBaseFile | |||
| * \brief Base class for SdFile with Print and C++ streams. | |||
| */ | |||
| class SdBaseFile { | |||
| public: | |||
| /** Create an instance. */ | |||
| SdBaseFile() : writeError(false), m_type(FAT_FILE_TYPE_CLOSED) {} | |||
| SdBaseFile(const char* path, uint8_t oflag); | |||
| #if DESTRUCTOR_CLOSES_FILE | |||
| ~SdBaseFile() {if(isOpen()) close();} | |||
| #endif // DESTRUCTOR_CLOSES_FILE | |||
| /** | |||
| * writeError is set to true if an error occurs during a write(). | |||
| * Set writeError to false before calling print() and/or write() and check | |||
| * for true after calls to print() and/or write(). | |||
| */ | |||
| bool writeError; | |||
| /** \return value of writeError */ | |||
| bool getWriteError() {return writeError;} | |||
| /** Set writeError to zero */ | |||
| void clearWriteError() {writeError = 0;} | |||
| //---------------------------------------------------------------------------- | |||
| // helpers for stream classes | |||
| /** get position for streams | |||
| * \param[out] pos struct to receive position | |||
| */ | |||
| void getpos(FatPos_t* pos); | |||
| /** set position for streams | |||
| * \param[out] pos struct with value for new position | |||
| */ | |||
| void setpos(FatPos_t* pos); | |||
| //---------------------------------------------------------------------------- | |||
| /** \return number of bytes available from yhe current position to EOF */ | |||
| uint32_t available() {return fileSize() - curPosition();} | |||
| bool close(); | |||
| bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); | |||
| bool createContiguous(SdBaseFile* dirFile, | |||
| const char* path, uint32_t size); | |||
| /** \return The current cluster number for a file or directory. */ | |||
| uint32_t curCluster() const {return m_curCluster;} | |||
| /** \return The current position for a file or directory. */ | |||
| uint32_t curPosition() const {return m_curPosition;} | |||
| /** \return Current working directory */ | |||
| static SdBaseFile* cwd() {return m_cwd;} | |||
| /** Set the date/time callback function | |||
| * | |||
| * \param[in] dateTime The user's call back function. The callback | |||
| * function is of the form: | |||
| * | |||
| * \code | |||
| * void dateTime(uint16_t* date, uint16_t* time) { | |||
| * uint16_t year; | |||
| * uint8_t month, day, hour, minute, second; | |||
| * | |||
| * // User gets date and time from GPS or real-time clock here | |||
| * | |||
| * // return date using FAT_DATE macro to format fields | |||
| * *date = FAT_DATE(year, month, day); | |||
| * | |||
| * // return time using FAT_TIME macro to format fields | |||
| * *time = FAT_TIME(hour, minute, second); | |||
| * } | |||
| * \endcode | |||
| * | |||
| * Sets the function that is called when a file is created or when | |||
| * a file's directory entry is modified by sync(). All timestamps, | |||
| * access, creation, and modify, are set when a file is created. | |||
| * sync() maintains the last access date and last modify date/time. | |||
| * | |||
| * See the timestamp() function. | |||
| */ | |||
| static void dateTimeCallback( | |||
| void (*dateTime)(uint16_t* date, uint16_t* time)) { | |||
| m_dateTime = dateTime; | |||
| } | |||
| /** Cancel the date/time callback function. */ | |||
| static void dateTimeCallbackCancel() {m_dateTime = 0;} | |||
| bool dirEntry(dir_t* dir); | |||
| static void dirName(const dir_t& dir, char* name); | |||
| bool exists(const char* name); | |||
| int16_t fgets(char* str, int16_t num, char* delim = 0); | |||
| /** \return The total number of bytes in a file or directory. */ | |||
| uint32_t fileSize() const {return m_fileSize;} | |||
| /** \return The first cluster number for a file or directory. */ | |||
| uint32_t firstCluster() const {return m_firstCluster;} | |||
| bool getFilename(char* name); | |||
| /** \return True if this is a directory else false. */ | |||
| bool isDir() const {return m_type >= FAT_FILE_TYPE_MIN_DIR;} | |||
| /** \return True if this is a normal file else false. */ | |||
| bool isFile() const {return m_type == FAT_FILE_TYPE_NORMAL;} | |||
| /** \return True if this is an open file/directory else false. */ | |||
| bool isOpen() const {return m_type != FAT_FILE_TYPE_CLOSED;} | |||
| /** \return True if this is a subdirectory else false. */ | |||
| bool isSubDir() const {return m_type == FAT_FILE_TYPE_SUBDIR;} | |||
| /** \return True if this is the root directory. */ | |||
| bool isRoot() const { | |||
| return m_type == FAT_FILE_TYPE_ROOT_FIXED || m_type == FAT_FILE_TYPE_ROOT32; | |||
| } | |||
| void ls(Print* pr, uint8_t flags = 0, uint8_t indent = 0); | |||
| void ls(uint8_t flags = 0); | |||
| bool mkdir(SdBaseFile* dir, const char* path, bool pFlag = true); | |||
| // alias for backward compactability | |||
| bool makeDir(SdBaseFile* dir, const char* path) { | |||
| return mkdir(dir, path, false); | |||
| } | |||
| bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); | |||
| bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); | |||
| bool open(const char* path, uint8_t oflag = O_READ); | |||
| bool openNext(SdBaseFile* dirFile, uint8_t oflag); | |||
| bool openRoot(SdVolume* vol); | |||
| int peek(); | |||
| bool printCreateDateTime(Print* pr); | |||
| static void printFatDate(uint16_t fatDate); | |||
| static void printFatDate(Print* pr, uint16_t fatDate); | |||
| static void printFatTime(uint16_t fatTime); | |||
| static void printFatTime(Print* pr, uint16_t fatTime); | |||
| int printField(int16_t value, char term); | |||
| int printField(uint16_t value, char term); | |||
| int printField(int32_t value, char term); | |||
| int printField(uint32_t value, char term); | |||
| bool printModifyDateTime(Print* pr); | |||
| size_t printName(); | |||
| size_t printName(Print* pr); | |||
| size_t printFileSize(Print* pr); | |||
| int16_t read(); | |||
| int read(void* buf, size_t nbyte); | |||
| int8_t readDir(dir_t* dir); | |||
| static bool remove(SdBaseFile* dirFile, const char* path); | |||
| bool remove(); | |||
| /** Set the file's current position to zero. */ | |||
| void rewind() {seekSet(0);} | |||
| bool rename(SdBaseFile* dirFile, const char* newPath); | |||
| bool rmdir(); | |||
| // for backward compatibility | |||
| bool rmDir() {return rmdir();} | |||
| bool rmRfStar(); | |||
| /** Set the files position to current position + \a pos. See seekSet(). | |||
| * \param[in] offset The new position in bytes from the current position. | |||
| * \return true for success or false for failure. | |||
| */ | |||
| bool seekCur(int32_t offset) { | |||
| return seekSet(m_curPosition + offset); | |||
| } | |||
| /** Set the files position to end-of-file + \a offset. See seekSet(). | |||
| * \param[in] offset The new position in bytes from end-of-file. | |||
| * \return true for success or false for failure. | |||
| */ | |||
| bool seekEnd(int32_t offset = 0) {return seekSet(m_fileSize + offset);} | |||
| bool seekSet(uint32_t pos); | |||
| bool sync(); | |||
| bool timestamp(SdBaseFile* file); | |||
| bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, | |||
| uint8_t hour, uint8_t minute, uint8_t second); | |||
| /** Type of file. You should use isFile() or isDir() instead of type() | |||
| * if possible. | |||
| * | |||
| * \return The file or directory type. | |||
| */ | |||
| uint8_t type() const {return m_type;} | |||
| bool truncate(uint32_t size); | |||
| /** \return SdVolume that contains this file. */ | |||
| SdVolume* volume() const {return m_vol;} | |||
| int write(const void* buf, size_t nbyte); | |||
| //------------------------------------------------------------------------------ | |||
| private: | |||
| // allow SdFat to set m_cwd | |||
| friend class SdFat; | |||
| /** experimental don't use */ | |||
| bool openParent(SdBaseFile* dir); | |||
| // private functions | |||
| bool addCluster(); | |||
| cache_t* addDirCluster(); | |||
| dir_t* cacheDirEntry(uint8_t action); | |||
| int8_t lsPrintNext(Print *pr, uint8_t flags, uint8_t indent); | |||
| static bool make83Name(const char* str, uint8_t* name, const char** ptr); | |||
| bool mkdir(SdBaseFile* parent, const uint8_t dname[11]); | |||
| bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag); | |||
| bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags); | |||
| dir_t* readDirCache(); | |||
| static void setCwd(SdBaseFile* cwd) {m_cwd = cwd;} | |||
| bool setDirSize(); | |||
| // bits defined in m_flags | |||
| // should be 0X0F | |||
| static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); | |||
| // sync of directory entry required | |||
| static uint8_t const F_FILE_DIR_DIRTY = 0X80; | |||
| // global pointer to cwd dir | |||
| static SdBaseFile* m_cwd; | |||
| // data time callback function | |||
| static void (*m_dateTime)(uint16_t* date, uint16_t* time); | |||
| // private data | |||
| uint8_t m_flags; // See above for definition of m_flags bits | |||
| uint8_t m_type; // type of file see above for values | |||
| uint8_t m_dirIndex; // index of directory entry in dirBlock | |||
| SdVolume* m_vol; // volume where file is located | |||
| uint32_t m_curCluster; // cluster for current file position | |||
| uint32_t m_curPosition; // current file position in bytes from beginning | |||
| uint32_t m_dirBlock; // block for this files directory entry | |||
| uint32_t m_fileSize; // file size in bytes | |||
| uint32_t m_firstCluster; // first cluster of file | |||
| }; | |||
| #endif // SdBaseFile_h | |||
| @@ -0,0 +1,322 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <utility/FmtNumber.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** List directory contents to stdOut. | |||
| * | |||
| * \param[in] flags The inclusive OR of | |||
| * | |||
| * LS_DATE - %Print file modification date | |||
| * | |||
| * LS_SIZE - %Print file size. | |||
| * | |||
| * LS_R - Recursive list of subdirectories. | |||
| */ | |||
| void SdBaseFile::ls(uint8_t flags) { | |||
| ls(SdFat::stdOut(), flags, 0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** List directory contents. | |||
| * | |||
| * \param[in] pr Print stream for list. | |||
| * | |||
| * \param[in] flags The inclusive OR of | |||
| * | |||
| * LS_DATE - %Print file modification date | |||
| * | |||
| * LS_SIZE - %Print file size. | |||
| * | |||
| * LS_R - Recursive list of subdirectories. | |||
| * | |||
| * \param[in] indent Amount of space before file name. Used for recursive | |||
| * list to indicate subdirectory level. | |||
| */ | |||
| //------------------------------------------------------------------------------ | |||
| void SdBaseFile::ls(Print* pr, uint8_t flags, uint8_t indent) { | |||
| if (!isDir()) { | |||
| pr->println(F("bad dir")); | |||
| return; | |||
| } | |||
| rewind(); | |||
| int8_t status; | |||
| while ((status = lsPrintNext(pr, flags, indent))) { | |||
| if (status > 1 && (flags & LS_R)) { | |||
| uint16_t index = curPosition()/32 - 1; | |||
| SdBaseFile s; | |||
| if (s.open(this, index, O_READ)) s.ls(pr, flags, indent + 2); | |||
| seekSet(32 * (index + 1)); | |||
| } | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // saves 32 bytes on stack for ls recursion | |||
| // return 0 - EOF, 1 - normal file, or 2 - directory | |||
| int8_t SdBaseFile::lsPrintNext(Print *pr, uint8_t flags, uint8_t indent) { | |||
| dir_t dir; | |||
| uint8_t w = 0; | |||
| while (1) { | |||
| if (read(&dir, sizeof(dir)) != sizeof(dir)) return 0; | |||
| if (dir.name[0] == DIR_NAME_FREE) return 0; | |||
| // skip deleted entry and entries for . and .. | |||
| if (dir.name[0] != DIR_NAME_DELETED && dir.name[0] != '.' | |||
| && DIR_IS_FILE_OR_SUBDIR(&dir)) break; | |||
| } | |||
| // indent for dir level | |||
| for (uint8_t i = 0; i < indent; i++) pr->write(' '); | |||
| // print name | |||
| for (uint8_t i = 0; i < 11; i++) { | |||
| if (dir.name[i] == ' ')continue; | |||
| if (i == 8) { | |||
| pr->write('.'); | |||
| w++; | |||
| } | |||
| pr->write(dir.name[i]); | |||
| w++; | |||
| } | |||
| if (DIR_IS_SUBDIR(&dir)) { | |||
| pr->write('/'); | |||
| w++; | |||
| } | |||
| if (flags & (LS_DATE | LS_SIZE)) { | |||
| while (w++ < 14) pr->write(' '); | |||
| } | |||
| // print modify date/time if requested | |||
| if (flags & LS_DATE) { | |||
| pr->write(' '); | |||
| printFatDate(pr, dir.lastWriteDate); | |||
| pr->write(' '); | |||
| printFatTime(pr, dir.lastWriteTime); | |||
| } | |||
| // print size if requested | |||
| if (!DIR_IS_SUBDIR(&dir) && (flags & LS_SIZE)) { | |||
| pr->write(' '); | |||
| pr->print(dir.fileSize); | |||
| } | |||
| pr->println(); | |||
| return DIR_IS_FILE(&dir) ? 1 : 2; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // print uint8_t with width 2 | |||
| static void print2u(Print* pr, uint8_t v) { | |||
| if (v < 10) pr->write('0'); | |||
| pr->print(v, DEC); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a file's creation date and time | |||
| * | |||
| * \param[in] pr Print stream for output. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdBaseFile::printCreateDateTime(Print* pr) { | |||
| dir_t dir; | |||
| if (!dirEntry(&dir)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| printFatDate(pr, dir.creationDate); | |||
| pr->write(' '); | |||
| printFatTime(pr, dir.creationTime); | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a directory date field to stdOut. | |||
| * | |||
| * Format is yyyy-mm-dd. | |||
| * | |||
| * \param[in] fatDate The date field from a directory entry. | |||
| */ | |||
| void SdBaseFile::printFatDate(uint16_t fatDate) { | |||
| printFatDate(SdFat::stdOut(), fatDate); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a directory date field. | |||
| * | |||
| * Format is yyyy-mm-dd. | |||
| * | |||
| * \param[in] pr Print stream for output. | |||
| * \param[in] fatDate The date field from a directory entry. | |||
| */ | |||
| void SdBaseFile::printFatDate(Print* pr, uint16_t fatDate) { | |||
| pr->print(FAT_YEAR(fatDate)); | |||
| pr->write('-'); | |||
| print2u(pr, FAT_MONTH(fatDate)); | |||
| pr->write('-'); | |||
| print2u(pr, FAT_DAY(fatDate)); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a directory time field to stdOut. | |||
| * | |||
| * Format is hh:mm:ss. | |||
| * | |||
| * \param[in] fatTime The time field from a directory entry. | |||
| */ | |||
| void SdBaseFile::printFatTime(uint16_t fatTime) { | |||
| printFatTime(SdFat::stdOut(), fatTime); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a directory time field. | |||
| * | |||
| * Format is hh:mm:ss. | |||
| * | |||
| * \param[in] pr Print stream for output. | |||
| * \param[in] fatTime The time field from a directory entry. | |||
| */ | |||
| void SdBaseFile::printFatTime(Print* pr, uint16_t fatTime) { | |||
| print2u(pr, FAT_HOUR(fatTime)); | |||
| pr->write(':'); | |||
| print2u(pr, FAT_MINUTE(fatTime)); | |||
| pr->write(':'); | |||
| print2u(pr, FAT_SECOND(fatTime)); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Template for SdBaseFile::printField() */ | |||
| template <typename Type> | |||
| static int printFieldT(SdBaseFile* file, char sign, Type value, char term) { | |||
| char buf[3*sizeof(Type) + 3]; | |||
| char* str = &buf[sizeof(buf)]; | |||
| if (term) { | |||
| *--str = term; | |||
| if (term == '\n') { | |||
| *--str = '\r'; | |||
| } | |||
| } | |||
| do { | |||
| Type m = value; | |||
| value /= 10; | |||
| *--str = '0' + m - 10*value; | |||
| } while (value); | |||
| if (sign) { | |||
| *--str = sign; | |||
| } | |||
| return file->write(str, &buf[sizeof(buf)] - str); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int SdBaseFile::printField(uint16_t value, char term) { | |||
| return printFieldT(this, 0, value, term); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int SdBaseFile::printField(int16_t value, char term) { | |||
| char sign = 0; | |||
| if (value < 0) { | |||
| sign = '-'; | |||
| value = -value; | |||
| } | |||
| return printFieldT(this, sign, (uint16_t)value, term); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int SdBaseFile::printField(uint32_t value, char term) { | |||
| return printFieldT(this, 0, value, term); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] term The field terminator. Use '\\n' for CR LF. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int SdBaseFile::printField(int32_t value, char term) { | |||
| char sign = 0; | |||
| if (value < 0) { | |||
| sign = '-'; | |||
| value = -value; | |||
| } | |||
| return printFieldT(this, sign, (uint32_t)value, term); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a file's modify date and time | |||
| * | |||
| * \param[in] pr Print stream for output. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdBaseFile::printModifyDateTime(Print* pr) { | |||
| dir_t dir; | |||
| if (!dirEntry(&dir)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| printFatDate(pr, dir.lastWriteDate); | |||
| pr->write(' '); | |||
| printFatTime(pr, dir.lastWriteTime); | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a file's name | |||
| * | |||
| * \param[in] pr Print stream for output. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| size_t SdBaseFile::printName(Print* pr) { | |||
| char name[13]; | |||
| if (!getFilename(name)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| return pr->print(name); | |||
| fail: | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a file's name to stdOut | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| size_t SdBaseFile::printName() { | |||
| return printName(SdFat::stdOut()); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| size_t SdBaseFile::printFileSize(Print* pr) { | |||
| char buf[10]; | |||
| char *ptr = fmtDec(fileSize(), buf + sizeof(buf)); | |||
| while (ptr > buf) *--ptr = ' '; | |||
| return pr->write(reinterpret_cast<uint8_t *>(buf), sizeof(buf)); | |||
| } | |||
| @@ -0,0 +1,247 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdFat.h> | |||
| //------------------------------------------------------------------------------ | |||
| #if USE_SERIAL_FOR_STD_OUT || !defined(UDR0) | |||
| Print* SdFat::m_stdOut = &Serial; | |||
| #else // USE_SERIAL_FOR_STD_OUT | |||
| #include <MinimumSerial.h> | |||
| Print* SdFat::m_stdOut = &MiniSerial; | |||
| #endif // USE_SERIAL_FOR_STD_OUT | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Initialize an SdFat object. | |||
| * | |||
| * Initializes the SD card, SD volume, and root directory. | |||
| * | |||
| * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init(). | |||
| * \param[in] sckDivisor value for SPI SCK divisor. See Sd2Card::init(). | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdFat::begin(uint8_t chipSelectPin, uint8_t sckDivisor) { | |||
| return m_card.begin(chipSelectPin, sckDivisor) | |||
| && m_vol.init(&m_card) && chdir(1); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Change a volume's working directory to root | |||
| * | |||
| * Changes the volume's working directory to the SD's root directory. | |||
| * Optionally set the current working directory to the volume's | |||
| * working directory. | |||
| * | |||
| * \param[in] set_cwd Set the current working directory to this volume's | |||
| * working directory if true. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdFat::chdir(bool set_cwd) { | |||
| if (set_cwd) SdBaseFile::setCwd(&m_vwd); | |||
| if (m_vwd.isOpen()) m_vwd.close(); | |||
| return m_vwd.openRoot(&m_vol); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Change a volume's working directory | |||
| * | |||
| * Changes the volume working directory to the \a path subdirectory. | |||
| * Optionally set the current working directory to the volume's | |||
| * working directory. | |||
| * | |||
| * Example: If the volume's working directory is "/DIR", chdir("SUB") | |||
| * will change the volume's working directory from "/DIR" to "/DIR/SUB". | |||
| * | |||
| * If path is "/", the volume's working directory will be changed to the | |||
| * root directory | |||
| * | |||
| * \param[in] path The name of the subdirectory. | |||
| * | |||
| * \param[in] set_cwd Set the current working directory to this volume's | |||
| * working directory if true. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdFat::chdir(const char *path, bool set_cwd) { | |||
| SdBaseFile dir; | |||
| dir.open(&m_vwd, path, O_READ); | |||
| // Check for correctly open directory. | |||
| if (!dir.isDir()) goto fail; | |||
| m_vwd = dir; | |||
| if (set_cwd) SdBaseFile::setCwd(&m_vwd); | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Set the current working directory to a volume's working directory. | |||
| * | |||
| * This is useful with multiple SD cards. | |||
| * | |||
| * The current working directory is changed to this volume's working directory. | |||
| * | |||
| * This is like the Windows/DOS \<drive letter>: command. | |||
| */ | |||
| void SdFat::chvol() { | |||
| SdBaseFile::setCwd(&m_vwd); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Test for the existence of a file. | |||
| * | |||
| * \param[in] name Name of the file to be tested for. | |||
| * | |||
| * \return true if the file exists else false. | |||
| */ | |||
| bool SdFat::exists(const char* name) { | |||
| return m_vwd.exists(name); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** List the directory contents of the volume working directory to stdOut. | |||
| * | |||
| * \param[in] flags The inclusive OR of | |||
| * | |||
| * LS_DATE - %Print file modification date | |||
| * | |||
| * LS_SIZE - %Print file size. | |||
| * | |||
| * LS_R - Recursive list of subdirectories. | |||
| */ | |||
| void SdFat::ls(uint8_t flags) { | |||
| m_vwd.ls(m_stdOut, flags); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** List the directory contents of the volume working directory to stdOut. | |||
| * | |||
| * \param[in] path directory to list. | |||
| * | |||
| * \param[in] flags The inclusive OR of | |||
| * | |||
| * LS_DATE - %Print file modification date | |||
| * | |||
| * LS_SIZE - %Print file size. | |||
| * | |||
| * LS_R - Recursive list of subdirectories. | |||
| */ | |||
| void SdFat::ls(const char* path, uint8_t flags) { | |||
| ls(m_stdOut, path, flags); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** List the directory contents of the volume working directory. | |||
| * | |||
| * \param[in] pr Print stream for list. | |||
| * | |||
| * \param[in] flags The inclusive OR of | |||
| * | |||
| * LS_DATE - %Print file modification date | |||
| * | |||
| * LS_SIZE - %Print file size. | |||
| * | |||
| * LS_R - Recursive list of subdirectories. | |||
| */ | |||
| void SdFat::ls(Print* pr, uint8_t flags) { | |||
| m_vwd.ls(pr, flags); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdFat::ls(Print* pr, const char* path, uint8_t flags) { | |||
| SdBaseFile dir(path, O_READ); | |||
| dir.ls(pr, flags); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Make a subdirectory in the volume working directory. | |||
| * | |||
| * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. | |||
| * | |||
| * \param[in] pFlag Create missing parent directories if true. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdFat::mkdir(const char* path, bool pFlag) { | |||
| SdBaseFile sub; | |||
| return sub.mkdir(&m_vwd, path, pFlag); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Remove a file from the volume working directory. | |||
| * | |||
| * \param[in] path A path with a valid 8.3 DOS name for the file. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdFat::remove(const char* path) { | |||
| return SdBaseFile::remove(&m_vwd, path); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Rename a file or subdirectory. | |||
| * | |||
| * \param[in] oldPath Path name to the file or subdirectory to be renamed. | |||
| * | |||
| * \param[in] newPath New path name of the file or subdirectory. | |||
| * | |||
| * The \a newPath object must not exist before the rename call. | |||
| * | |||
| * The file to be renamed must not be open. The directory entry may be | |||
| * moved and file system corruption could occur if the file is accessed by | |||
| * a file object that was opened before the rename() call. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdFat::rename(const char *oldPath, const char *newPath) { | |||
| SdBaseFile file; | |||
| if (!file.open(oldPath, O_READ)) return false; | |||
| return file.rename(&m_vwd, newPath); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Remove a subdirectory from the volume's working directory. | |||
| * | |||
| * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. | |||
| * | |||
| * The subdirectory file will be removed only if it is empty. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| */ | |||
| bool SdFat::rmdir(const char* path) { | |||
| SdBaseFile sub; | |||
| if (!sub.open(path, O_READ)) return false; | |||
| return sub.rmdir(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Truncate a file to a specified length. The current file position | |||
| * will be maintained if it is less than or equal to \a length otherwise | |||
| * it will be set to end of file. | |||
| * | |||
| * \param[in] path A path with a valid 8.3 DOS name for the file. | |||
| * \param[in] length The desired length for the file. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. | |||
| * Reasons for failure include file is read only, file is a directory, | |||
| * \a length is greater than the current file size or an I/O error occurs. | |||
| */ | |||
| bool SdFat::truncate(const char* path, uint32_t length) { | |||
| SdBaseFile file; | |||
| if (!file.open(path, O_WRITE)) return false; | |||
| return file.truncate(length); | |||
| } | |||
| @@ -0,0 +1,99 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef SdFat_h | |||
| #define SdFat_h | |||
| /** | |||
| * \file | |||
| * \brief SdFat class | |||
| */ | |||
| //------------------------------------------------------------------------------ | |||
| /** Macro for debug. */ | |||
| #define DBG_FAIL_MACRO // Serial.print(__FILE__);Serial.println(__LINE__) | |||
| //------------------------------------------------------------------------------ | |||
| /** SdFat version YYYYMMDD */ | |||
| #define SD_FAT_VERSION 20140824 | |||
| //------------------------------------------------------------------------------ | |||
| /** error if old IDE */ | |||
| #if !defined(ARDUINO) || ARDUINO < 100 | |||
| #error Arduino IDE must be 1.0 or greater | |||
| #endif // ARDUINO < 100 | |||
| //------------------------------------------------------------------------------ | |||
| #include <SdFile.h> | |||
| #include <SdStream.h> | |||
| #include <ArduinoStream.h> | |||
| #include <MinimumSerial.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \class SdFat | |||
| * \brief Integration class for the %SdFat library. | |||
| */ | |||
| class SdFat { | |||
| public: | |||
| SdFat() {} | |||
| /** \return a pointer to the Sd2Card object. */ | |||
| Sd2Card* card() {return &m_card;} | |||
| bool chdir(bool set_cwd = false); | |||
| bool chdir(const char* path, bool set_cwd = false); | |||
| void chvol(); | |||
| void errorHalt(); | |||
| void errorHalt(char const *msg); | |||
| void errorPrint(); | |||
| void errorPrint(char const *msg); | |||
| bool exists(const char* name); | |||
| bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN, | |||
| uint8_t sckDivisor = SPI_FULL_SPEED); | |||
| void initErrorHalt(); | |||
| void initErrorHalt(char const *msg); | |||
| void initErrorPrint(); | |||
| void initErrorPrint(char const *msg); | |||
| void ls(uint8_t flags = 0); | |||
| void ls(const char* path, uint8_t flags = 0); | |||
| void ls(Print* pr, uint8_t flags = 0); | |||
| void ls(Print* pr, const char* path, uint8_t flags = 0); | |||
| bool mkdir(const char* path, bool pFlag = true); | |||
| bool remove(const char* path); | |||
| bool rename(const char *oldPath, const char *newPath); | |||
| bool rmdir(const char* path); | |||
| bool truncate(const char* path, uint32_t length); | |||
| /** \return a pointer to the SdVolume object. */ | |||
| SdVolume* vol() {return &m_vol;} | |||
| /** \return a pointer to the volume working directory. */ | |||
| SdBaseFile* vwd() {return &m_vwd;} | |||
| //---------------------------------------------------------------------------- | |||
| void errorHalt_P(PGM_P msg); | |||
| void errorPrint_P(PGM_P msg); | |||
| void initErrorHalt_P(PGM_P msg); | |||
| void initErrorPrint_P(PGM_P msg); | |||
| //---------------------------------------------------------------------------- | |||
| /** | |||
| * Set stdOut Print stream for messages. | |||
| * \param[in] stream The new Print stream. | |||
| */ | |||
| static void setStdOut(Print* stream) {m_stdOut = stream;} | |||
| /** \return Print stream for messages. */ | |||
| static Print* stdOut() {return m_stdOut;} | |||
| private: | |||
| Sd2Card m_card; | |||
| SdVolume m_vol; | |||
| SdBaseFile m_vwd; | |||
| static Print* m_stdOut; | |||
| }; | |||
| #endif // SdFat_h | |||
| @@ -0,0 +1,196 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * \file | |||
| * \brief configuration definitions | |||
| */ | |||
| #ifndef SdFatConfig_h | |||
| #define SdFatConfig_h | |||
| #include <stdint.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache | |||
| * for FAT table entries. Improves performance for large writes that | |||
| * are not a multiple of 512 bytes. | |||
| */ | |||
| #ifdef __arm__ | |||
| #define USE_SEPARATE_FAT_CACHE 1 | |||
| #else // __arm__ | |||
| #define USE_SEPARATE_FAT_CACHE 0 | |||
| #endif // __arm__ | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set USE_MULTI_BLOCK_SD_IO nonzero to use multi-block SD read/write. | |||
| * | |||
| * Don't use mult-block read/write on small AVR boards. | |||
| */ | |||
| #if defined(RAMEND) && RAMEND < 3000 | |||
| #define USE_MULTI_BLOCK_SD_IO 0 | |||
| #else | |||
| #define USE_MULTI_BLOCK_SD_IO 1 | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * To enable SD card CRC checking set USE_SD_CRC nonzero. | |||
| * | |||
| * Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function. | |||
| * | |||
| * Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function. | |||
| */ | |||
| #define USE_SD_CRC 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * To use multiple SD cards set USE_MULTIPLE_CARDS nonzero. | |||
| * | |||
| * Using multiple cards costs about 200 bytes of flash. | |||
| * | |||
| * Each card requires about 550 bytes of SRAM so use of a Mega is recommended. | |||
| */ | |||
| #define USE_MULTIPLE_CARDS 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. | |||
| * | |||
| * Causes use of lots of heap in ARM. | |||
| */ | |||
| #define DESTRUCTOR_CLOSES_FILE 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * For AVR | |||
| * | |||
| * Set USE_SERIAL_FOR_STD_OUT nonzero to use Serial (the HardwareSerial class) | |||
| * for error messages and output from print functions like ls(). | |||
| * | |||
| * If USE_SERIAL_FOR_STD_OUT is zero, a small non-interrupt driven class | |||
| * is used to output messages to serial port zero. This allows an alternate | |||
| * Serial library like SerialPort to be used with SdFat. | |||
| * | |||
| * You can redirect stdOut with SdFat::setStdOut(Print* stream) and | |||
| * get the current stream with SdFat::stdOut(). | |||
| */ | |||
| #define USE_SERIAL_FOR_STD_OUT 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Call flush for endl if ENDL_CALLS_FLUSH is nonzero | |||
| * | |||
| * The standard for iostreams is to call flush. This is very costly for | |||
| * SdFat. Each call to flush causes 2048 bytes of I/O to the SD. | |||
| * | |||
| * SdFat has a single 512 byte buffer for SD I/O so it must write the current | |||
| * data block to the SD, read the directory block from the SD, update the | |||
| * directory entry, write the directory block to the SD and read the data | |||
| * block back into the buffer. | |||
| * | |||
| * The SD flash memory controller is not designed for this many rewrites | |||
| * so performance may be reduced by more than a factor of 100. | |||
| * | |||
| * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force | |||
| * all data to be written to the SD. | |||
| */ | |||
| #define ENDL_CALLS_FLUSH 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Allow FAT12 volumes if FAT12_SUPPORT is nonzero. | |||
| * FAT12 has not been well tested. | |||
| */ | |||
| #define FAT12_SUPPORT 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * SPI SCK divisor for SD initialization commands. | |||
| * or greater | |||
| */ | |||
| #ifdef __AVR__ | |||
| const uint8_t SPI_SCK_INIT_DIVISOR = 64; | |||
| #else | |||
| const uint8_t SPI_SCK_INIT_DIVISOR = 128; | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature | |||
| * of the standard Arduino SPI library. You must include SPI.h in your | |||
| * sketches when ENABLE_SPI_TRANSACTION is nonzero. | |||
| */ | |||
| #define ENABLE_SPI_TRANSACTION 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during | |||
| * SD card busy waits. | |||
| * | |||
| * This will allow interrupt routines to access the SPI bus if | |||
| * ENABLE_SPI_TRANSACTION is nonzero. | |||
| * | |||
| * Setting ENABLE_SPI_YIELD will introduce some extra overhead and will | |||
| * slightly slow transfer rates. A few older SD cards may fail when | |||
| * ENABLE_SPI_YIELD is nonzero. | |||
| */ | |||
| #define ENABLE_SPI_YIELD 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Force use of Arduino Standard SPI library if USE_ARDUINO_SPI_LIBRARY | |||
| * is nonzero. This will override native and software SPI for all boards. | |||
| */ | |||
| #define USE_ARDUINO_SPI_LIBRARY 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Define AVR_SOF_SPI nonzero to use software SPI on all AVR Arduinos. | |||
| */ | |||
| #define AVR_SOFT_SPI 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Define DUE_SOFT_SPI nonzero to use software SPI on Due Arduinos. | |||
| */ | |||
| #define DUE_SOFT_SPI 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Define LEONARDO_SOFT_SPI nonzero to use software SPI on Leonardo Arduinos. | |||
| * LEONARDO_SOFT_SPI allows an unmodified 328 Shield to be used | |||
| * on Leonardo Arduinos. | |||
| */ | |||
| #define LEONARDO_SOFT_SPI 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos. | |||
| * MEGA_SOFT_SPI allows an unmodified 328 Shield to be used | |||
| * on Mega Arduinos. | |||
| */ | |||
| #define MEGA_SOFT_SPI 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Set TEENSY3_SOFT_SPI nonzero to use software SPI on Teensy 3.x boards. | |||
| */ | |||
| #define TEENSY3_SOFT_SPI 0 | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Define software SPI pins. Default allows Uno shields to be used on other | |||
| * boards. | |||
| */ | |||
| // define software SPI pins | |||
| /** Default Software SPI chip select pin */ | |||
| uint8_t const SOFT_SPI_CS_PIN = 10; | |||
| /** Software SPI Master Out Slave In pin */ | |||
| uint8_t const SOFT_SPI_MOSI_PIN = 11; | |||
| /** Software SPI Master In Slave Out pin */ | |||
| uint8_t const SOFT_SPI_MISO_PIN = 12; | |||
| /** Software SPI Clock pin */ | |||
| uint8_t const SOFT_SPI_SCK_PIN = 13; | |||
| #endif // SdFatConfig_h | |||
| @@ -0,0 +1,145 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdFat.h> | |||
| #ifndef PSTR | |||
| #define PSTR(x) x | |||
| #define PGM_P const char* | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| static void pstrPrint(PGM_P str) { | |||
| for (uint8_t c; (c = pgm_read_byte(str)); str++) SdFat::stdOut()->write(c); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| static void pstrPrintln(PGM_P str) { | |||
| pstrPrint(str); | |||
| SdFat::stdOut()->println(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print any SD error code and halt. */ | |||
| void SdFat::errorHalt() { | |||
| errorPrint(); | |||
| while (1) {} | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print msg, any SD error code, and halt. | |||
| * | |||
| * \param[in] msg Message to print. | |||
| */ | |||
| void SdFat::errorHalt(char const* msg) { | |||
| errorPrint(msg); | |||
| while (1) {} | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print msg, any SD error code, and halt. | |||
| * | |||
| * \param[in] msg Message in program space (flash memory) to print. | |||
| */ | |||
| void SdFat::errorHalt_P(PGM_P msg) { | |||
| errorPrint_P(msg); | |||
| while (1) {} | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print any SD error code. */ | |||
| void SdFat::errorPrint() { | |||
| if (!m_card.errorCode()) return; | |||
| pstrPrint(PSTR("SD errorCode: 0X")); | |||
| m_stdOut->print(m_card.errorCode(), HEX); | |||
| pstrPrint(PSTR(",0X")); | |||
| m_stdOut->println(m_card.errorData(), HEX); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print msg, any SD error code. | |||
| * | |||
| * \param[in] msg Message to print. | |||
| */ | |||
| void SdFat::errorPrint(char const* msg) { | |||
| pstrPrint(PSTR("error: ")); | |||
| m_stdOut->println(msg); | |||
| errorPrint(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print msg, any SD error code. | |||
| * | |||
| * \param[in] msg Message in program space (flash memory) to print. | |||
| */ | |||
| void SdFat::errorPrint_P(PGM_P msg) { | |||
| pstrPrint(PSTR("error: ")); | |||
| pstrPrintln(msg); | |||
| errorPrint(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print error details and halt after SdFat::init() fails. */ | |||
| void SdFat::initErrorHalt() { | |||
| initErrorPrint(); | |||
| while (1) {} | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /**Print message, error details, and halt after SdFat::init() fails. | |||
| * | |||
| * \param[in] msg Message to print. | |||
| */ | |||
| void SdFat::initErrorHalt(char const *msg) { | |||
| m_stdOut->println(msg); | |||
| initErrorHalt(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /**Print message, error details, and halt after SdFat::init() fails. | |||
| * | |||
| * \param[in] msg Message in program space (flash memory) to print. | |||
| */ | |||
| void SdFat::initErrorHalt_P(PGM_P msg) { | |||
| pstrPrintln(msg); | |||
| initErrorHalt(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print error details after SdFat::init() fails. */ | |||
| void SdFat::initErrorPrint() { | |||
| if (m_card.errorCode()) { | |||
| pstrPrintln(PSTR("Can't access SD card. Do not reformat.")); | |||
| if (m_card.errorCode() == SD_CARD_ERROR_CMD0) { | |||
| pstrPrintln(PSTR("No card, wrong chip select pin, or SPI problem?")); | |||
| } | |||
| errorPrint(); | |||
| } else if (m_vol.fatType() == 0) { | |||
| pstrPrintln(PSTR("Invalid format, reformat SD.")); | |||
| } else if (!m_vwd.isOpen()) { | |||
| pstrPrintln(PSTR("Can't open root directory.")); | |||
| } else { | |||
| pstrPrintln(PSTR("No error found.")); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /**Print message and error details and halt after SdFat::init() fails. | |||
| * | |||
| * \param[in] msg Message to print. | |||
| */ | |||
| void SdFat::initErrorPrint(char const *msg) { | |||
| m_stdOut->println(msg); | |||
| initErrorPrint(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /**Print message and error details after SdFat::init() fails. | |||
| * | |||
| * \param[in] msg Message in program space (flash memory) to print. | |||
| */ | |||
| void SdFat::initErrorPrint_P(PGM_P msg) { | |||
| pstrPrintln(msg); | |||
| initErrorHalt(); | |||
| } | |||
| @@ -0,0 +1,76 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <stdlib.h> | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| #ifdef __arm__ | |||
| // should use uinstd.h to define sbrk but Due causes a conflict | |||
| extern "C" char* sbrk(int incr); | |||
| #else // __ARM__ | |||
| extern char *__brkval; | |||
| extern char __bss_end; | |||
| #endif // __arm__ | |||
| //------------------------------------------------------------------------------ | |||
| /** Amount of free RAM | |||
| * \return The number of free bytes. | |||
| */ | |||
| int SdFatUtil::FreeRam() { | |||
| char top; | |||
| #ifdef __arm__ | |||
| return &top - reinterpret_cast<char*>(sbrk(0)); | |||
| #else // __arm__ | |||
| return __brkval ? &top - __brkval : &top - &__bss_end; | |||
| #endif // __arm__ | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a string in flash memory. | |||
| * | |||
| * \param[in] pr Print object for output. | |||
| * \param[in] str Pointer to string stored in flash memory. | |||
| */ | |||
| void SdFatUtil::print_P(Print* pr, PGM_P str) { | |||
| for (uint8_t c; (c = pgm_read_byte(str)); str++) pr->write(c); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a string in flash memory followed by a CR/LF. | |||
| * | |||
| * \param[in] pr Print object for output. | |||
| * \param[in] str Pointer to string stored in flash memory. | |||
| */ | |||
| void SdFatUtil::println_P(Print* pr, PGM_P str) { | |||
| print_P(pr, str); | |||
| pr->println(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a string in flash memory to Serial. | |||
| * | |||
| * \param[in] str Pointer to string stored in flash memory. | |||
| */ | |||
| void SdFatUtil::SerialPrint_P(PGM_P str) { | |||
| print_P(SdFat::stdOut(), str); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** %Print a string in flash memory to Serial followed by a CR/LF. | |||
| * | |||
| * \param[in] str Pointer to string stored in flash memory. | |||
| */ | |||
| void SdFatUtil::SerialPrintln_P(PGM_P str) { | |||
| println_P(SdFat::stdOut(), str); | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef SdFatUtil_h | |||
| #define SdFatUtil_h | |||
| /** | |||
| * \file | |||
| * \brief Useful utility functions. | |||
| */ | |||
| #include <SdFat.h> | |||
| /** Store and print a string in flash memory.*/ | |||
| #define PgmPrint(x) SerialPrint_P(PSTR(x)) | |||
| /** Store and print a string in flash memory followed by a CR/LF.*/ | |||
| #define PgmPrintln(x) SerialPrintln_P(PSTR(x)) | |||
| namespace SdFatUtil { | |||
| int FreeRam(); | |||
| void print_P(Print* pr, PGM_P str); | |||
| void println_P(Print* pr, PGM_P str); | |||
| void SerialPrint_P(PGM_P str); | |||
| void SerialPrintln_P(PGM_P str); | |||
| } | |||
| using namespace SdFatUtil; // NOLINT | |||
| #endif // #define SdFatUtil_h | |||
| @@ -0,0 +1,219 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| \mainpage Arduino %SdFat Library | |||
| <CENTER>Copyright © 2012, 2013, 2014 by William Greiman | |||
| </CENTER> | |||
| \section Intro Introduction | |||
| The Arduino %SdFat Library is a minimal implementation of FAT16 and FAT32 | |||
| file systems on SD flash memory cards. Standard SD and high capacity SDHC | |||
| cards are supported. | |||
| Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT | |||
| nonzero in SdFatConfig.h. | |||
| The %SdFat library only supports short 8.3 names. | |||
| The main classes in %SdFat are SdFat, SdFile, StdioStream, \ref fstream, | |||
| \ref ifstream, and \ref ofstream. | |||
| The SdFat class maintains a volume working directories, a current working | |||
| directory, and simplifies initialization of other classes. | |||
| The SdFile class provides binary file access functions such as open(), read(), | |||
| remove(), write(), close() and sync(). This class supports access to the root | |||
| directory and subdirectories. | |||
| The StdioStream class implements functions similar to Linux/Unix standard | |||
| buffered input/output. | |||
| The \ref fstream class implements C++ iostreams for both reading and writing | |||
| text files. | |||
| The \ref ifstream class implements the C++ iostreams for reading text files. | |||
| The \ref ofstream class implements the C++ iostreams for writing text files. | |||
| The classes \ref ibufstream and \ref obufstream format and parse character | |||
| strings in memory buffers. | |||
| the classes ArduinoInStream and ArduinoOutStream provide iostream functions | |||
| for Serial, LiquidCrystal, and other devices. | |||
| The SdVolume class supports FAT16 and FAT32 partitions. Most applications | |||
| will not need to call SdVolume member function. | |||
| The Sd2Card class supports access to standard SD cards and SDHC cards. Most | |||
| applications will not need to call Sd2Card functions. The Sd2Card class can | |||
| be used for raw access to the SD card. | |||
| A number of example are provided in the %SdFat/examples folder. These were | |||
| developed to test %SdFat and illustrate its use. | |||
| %SdFat was developed for high speed data recording. %SdFat was used to | |||
| implement an audio record/play class, WaveRP, for the Adafruit Wave Shield. | |||
| This application uses special Sd2Card calls to write to contiguous files in | |||
| raw mode. These functions reduce write latency so that audio can be | |||
| recorded with the small amount of RAM in the Arduino. | |||
| \section SDcard SD\SDHC Cards | |||
| Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and | |||
| most consumer devices use the 4-bit parallel SD protocol. A card that | |||
| functions well on A PC or Mac may not work well on the Arduino. | |||
| Most cards have good SPI read performance but cards vary widely in SPI | |||
| write performance. Write performance is limited by how efficiently the | |||
| card manages internal erase/remapping operations. The Arduino cannot | |||
| optimize writes to reduce erase operations because of its limit RAM. | |||
| SanDisk cards generally have good write performance. They seem to have | |||
| more internal RAM buffering than other cards and therefore can limit | |||
| the number of flash erase operations that the Arduino forces due to its | |||
| limited RAM. | |||
| \section Hardware Hardware Configuration | |||
| %SdFat was developed using an | |||
| <A HREF = "http://www.adafruit.com/"> Adafruit Industries</A> | |||
| Data Logging Shield. | |||
| The hardware interface to the SD card should not use a resistor based level | |||
| shifter. %SdFat sets the SPI bus frequency to 8 MHz which results in signal | |||
| rise times that are too slow for the edge detectors in many newer SD card | |||
| controllers when resistor voltage dividers are used. | |||
| The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the | |||
| 74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield | |||
| uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the | |||
| 74LCX245. | |||
| If you are using a resistor based level shifter and are having problems try | |||
| setting the SPI bus frequency to 4 MHz. This can be done by using | |||
| card.init(SPI_HALF_SPEED) to initialize the SD card. | |||
| \section comment Bugs and Comments | |||
| If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. | |||
| \section SdFatClass SdFat Usage | |||
| %SdFat uses a slightly restricted form of short names. | |||
| Only printable ASCII characters are supported. No characters with code point | |||
| values greater than 127 are allowed. Space is not allowed even though space | |||
| was allowed in the API of early versions of DOS. | |||
| Short names are limited to 8 characters followed by an optional period (.) | |||
| and extension of up to 3 characters. The characters may be any combination | |||
| of letters and digits. The following special characters are also allowed: | |||
| $ % ' - _ @ ~ ` ! ( ) { } ^ # & | |||
| Short names are always converted to upper case and their original case | |||
| value is lost. | |||
| \par | |||
| An application which writes to a file using print(), println() or | |||
| \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink | |||
| at the appropriate time to force data and directory information to be written | |||
| to the SD Card. Data and directory information are also written to the SD card | |||
| when \link SdFile::close() close() \endlink is called. | |||
| \par | |||
| Applications must use care calling \link SdFile::sync() sync() \endlink | |||
| since 2048 bytes of I/O is required to update file and | |||
| directory information. This includes writing the current data block, reading | |||
| the block that contains the directory entry for update, writing the directory | |||
| block back and reading back the current data block. | |||
| It is possible to open a file with two or more instances of SdFile. A file may | |||
| be corrupted if data is written to the file by more than one instance of SdFile. | |||
| \section HowTo How to format SD Cards as FAT Volumes | |||
| You should use a freshly formatted SD card for best performance. FAT | |||
| file systems become slower if many files have been created and deleted. | |||
| This is because the directory entry for a deleted file is marked as deleted, | |||
| but is not deleted. When a new file is created, these entries must be scanned | |||
| before creating the file, a flaw in the FAT design. Also files can become | |||
| fragmented which causes reads and writes to be slower. | |||
| A formatter sketch, SdFormatter.pde, is included in the | |||
| %SdFat/examples/SdFormatter directory. This sketch attempts to | |||
| emulate SD Association's SDFormatter. | |||
| The best way to restore an SD card's format on a PC is to use SDFormatter | |||
| which can be downloaded from: | |||
| http://www.sdcard.org/consumers/formatter/ | |||
| SDFormatter aligns flash erase boundaries with file | |||
| system structures which reduces write latency and file system overhead. | |||
| SDFormatter does not have an option for FAT type so it may format | |||
| small cards as FAT12. | |||
| After the MBR is restored by SDFormatter you may need to reformat small | |||
| cards that have been formatted FAT12 to force the volume type to be FAT16. | |||
| If you reformat the SD card with an OS utility, choose a cluster size that | |||
| will result in: | |||
| 4084 < CountOfClusters && CountOfClusters < 65525 | |||
| The volume will then be FAT16. | |||
| If you are formatting an SD card on OS X or Linux, be sure to use the first | |||
| partition. Format this partition with a cluster count in above range for FAT16. | |||
| SDHC cards should be formatted FAT32 with a cluster size of 32 KB. | |||
| Microsoft operating systems support removable media formatted with a | |||
| Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector | |||
| in block zero. | |||
| Microsoft operating systems expect MBR formatted removable media | |||
| to have only one partition. The first partition should be used. | |||
| Microsoft operating systems do not support partitioning SD flash cards. | |||
| If you erase an SD card with a program like KillDisk, Most versions of | |||
| Windows will format the card as a super floppy. | |||
| \section References References | |||
| The Arduino site: | |||
| http://www.arduino.cc/ | |||
| For more information about FAT file systems see: | |||
| http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx | |||
| For information about using SD cards as SPI devices see: | |||
| http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf | |||
| The ATmega328 datasheet: | |||
| http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf | |||
| */ | |||
| @@ -0,0 +1,83 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdFile.h> | |||
| /** Create a file object and open it in the current working directory. | |||
| * | |||
| * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. | |||
| * | |||
| * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive | |||
| * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). | |||
| */ | |||
| SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Write data to an open file. | |||
| * | |||
| * \note Data is moved to the cache but may not be written to the | |||
| * storage device until sync() is called. | |||
| * | |||
| * \param[in] buf Pointer to the location of the data to be written. | |||
| * | |||
| * \param[in] nbyte Number of bytes to write. | |||
| * | |||
| * \return For success write() returns the number of bytes written, always | |||
| * \a nbyte. If an error occurs, write() returns -1. Possible errors | |||
| * include write() is called before a file has been opened, write is called | |||
| * for a read-only file, device is full, a corrupt file system or an I/O error. | |||
| * | |||
| */ | |||
| int SdFile::write(const void* buf, size_t nbyte) { | |||
| return SdBaseFile::write(buf, nbyte); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Write a byte to a file. Required by the Arduino Print class. | |||
| * \param[in] b the byte to be written. | |||
| * Use getWriteError to check for errors. | |||
| * \return 1 for success and 0 for failure. | |||
| */ | |||
| size_t SdFile::write(uint8_t b) { | |||
| return SdBaseFile::write(&b, 1) == 1 ? 1 : 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Write a string to a file. Used by the Arduino Print class. | |||
| * \param[in] str Pointer to the string. | |||
| * Use getWriteError to check for errors. | |||
| * \return count of characters written for success or -1 for failure. | |||
| */ | |||
| int SdFile::write(const char* str) { | |||
| return SdBaseFile::write(str, strlen(str)); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Write a PROGMEM string to a file. | |||
| * \param[in] str Pointer to the PROGMEM string. | |||
| * Use getWriteError to check for errors. | |||
| */ | |||
| void SdFile::write_P(PGM_P str) { | |||
| for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Write a PROGMEM string followed by CR/LF to a file. | |||
| * \param[in] str Pointer to the PROGMEM string. | |||
| * Use getWriteError to check for errors. | |||
| */ | |||
| void SdFile::writeln_P(PGM_P str) { | |||
| write_P(str); | |||
| write_P(PSTR("\r\n")); | |||
| } | |||
| @@ -0,0 +1,51 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * \file | |||
| * \brief SdFile class | |||
| */ | |||
| #include <SdBaseFile.h> | |||
| #ifndef SdFile_h | |||
| #define SdFile_h | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \class SdFile | |||
| * \brief SdBaseFile with Print. | |||
| */ | |||
| class SdFile : public SdBaseFile, public Print { | |||
| public: | |||
| SdFile() {} | |||
| SdFile(const char* name, uint8_t oflag); | |||
| #if DESTRUCTOR_CLOSES_FILE | |||
| ~SdFile() {} | |||
| #endif // DESTRUCTOR_CLOSES_FILE | |||
| /** \return value of writeError */ | |||
| bool getWriteError() {return SdBaseFile::getWriteError();} | |||
| /** Set writeError to zero */ | |||
| void clearWriteError() {SdBaseFile::clearWriteError();} | |||
| size_t write(uint8_t b); | |||
| int write(const char* str); | |||
| int write(const void* buf, size_t nbyte); | |||
| size_t write(const uint8_t *buf, size_t size) { | |||
| return SdBaseFile::write(buf, size);} | |||
| void write_P(PGM_P str); | |||
| void writeln_P(PGM_P str); | |||
| }; | |||
| #endif // SdFile_h | |||
| @@ -0,0 +1,303 @@ | |||
| /* Arduino Sd2Card Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino Sd2Card Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino Sd2Card Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef SdInfo_h | |||
| #define SdInfo_h | |||
| #include <stdint.h> | |||
| // Based on the document: | |||
| // | |||
| // SD Specifications | |||
| // Part 1 | |||
| // Physical Layer | |||
| // Simplified Specification | |||
| // Version 3.01 | |||
| // May 18, 2010 | |||
| // | |||
| // http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs | |||
| //------------------------------------------------------------------------------ | |||
| // SPI divisor constants | |||
| /** Set SCK to max rate of F_CPU/2. */ | |||
| uint8_t const SPI_FULL_SPEED = 2; | |||
| /** Set SCK rate to F_CPU/3 for Due */ | |||
| uint8_t const SPI_DIV3_SPEED = 3; | |||
| /** Set SCK rate to F_CPU/4. */ | |||
| uint8_t const SPI_HALF_SPEED = 4; | |||
| /** Set SCK rate to F_CPU/6 for Due */ | |||
| uint8_t const SPI_DIV6_SPEED = 6; | |||
| /** Set SCK rate to F_CPU/8. */ | |||
| uint8_t const SPI_QUARTER_SPEED = 8; | |||
| /** Set SCK rate to F_CPU/16. */ | |||
| uint8_t const SPI_EIGHTH_SPEED = 16; | |||
| /** Set SCK rate to F_CPU/32. */ | |||
| uint8_t const SPI_SIXTEENTH_SPEED = 32; | |||
| //------------------------------------------------------------------------------ | |||
| // SD operation timeouts | |||
| /** init timeout ms */ | |||
| uint16_t const SD_INIT_TIMEOUT = 2000; | |||
| /** erase timeout ms */ | |||
| uint16_t const SD_ERASE_TIMEOUT = 10000; | |||
| /** read timeout ms */ | |||
| uint16_t const SD_READ_TIMEOUT = 300; | |||
| /** write time out ms */ | |||
| uint16_t const SD_WRITE_TIMEOUT = 600; | |||
| //------------------------------------------------------------------------------ | |||
| // SD card commands | |||
| /** GO_IDLE_STATE - init card in spi mode if CS low */ | |||
| uint8_t const CMD0 = 0X00; | |||
| /** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ | |||
| uint8_t const CMD8 = 0X08; | |||
| /** SEND_CSD - read the Card Specific Data (CSD register) */ | |||
| uint8_t const CMD9 = 0X09; | |||
| /** SEND_CID - read the card identification information (CID register) */ | |||
| uint8_t const CMD10 = 0X0A; | |||
| /** STOP_TRANSMISSION - end multiple block read sequence */ | |||
| uint8_t const CMD12 = 0X0C; | |||
| /** SEND_STATUS - read the card status register */ | |||
| uint8_t const CMD13 = 0X0D; | |||
| /** READ_SINGLE_BLOCK - read a single data block from the card */ | |||
| uint8_t const CMD17 = 0X11; | |||
| /** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ | |||
| uint8_t const CMD18 = 0X12; | |||
| /** WRITE_BLOCK - write a single data block to the card */ | |||
| uint8_t const CMD24 = 0X18; | |||
| /** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ | |||
| uint8_t const CMD25 = 0X19; | |||
| /** ERASE_WR_BLK_START - sets the address of the first block to be erased */ | |||
| uint8_t const CMD32 = 0X20; | |||
| /** ERASE_WR_BLK_END - sets the address of the last block of the continuous | |||
| range to be erased*/ | |||
| uint8_t const CMD33 = 0X21; | |||
| /** ERASE - erase all previously selected blocks */ | |||
| uint8_t const CMD38 = 0X26; | |||
| /** APP_CMD - escape for application specific command */ | |||
| uint8_t const CMD55 = 0X37; | |||
| /** READ_OCR - read the OCR register of a card */ | |||
| uint8_t const CMD58 = 0X3A; | |||
| /** CRC_ON_OFF - enable or disable CRC checking */ | |||
| uint8_t const CMD59 = 0X3B; | |||
| /** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be | |||
| pre-erased before writing */ | |||
| uint8_t const ACMD23 = 0X17; | |||
| /** SD_SEND_OP_COMD - Sends host capacity support information and | |||
| activates the card's initialization process */ | |||
| uint8_t const ACMD41 = 0X29; | |||
| //------------------------------------------------------------------------------ | |||
| /** status for card in the ready state */ | |||
| uint8_t const R1_READY_STATE = 0X00; | |||
| /** status for card in the idle state */ | |||
| uint8_t const R1_IDLE_STATE = 0X01; | |||
| /** status bit for illegal command */ | |||
| uint8_t const R1_ILLEGAL_COMMAND = 0X04; | |||
| /** start data token for read or write single block*/ | |||
| uint8_t const DATA_START_BLOCK = 0XFE; | |||
| /** stop token for write multiple blocks*/ | |||
| uint8_t const STOP_TRAN_TOKEN = 0XFD; | |||
| /** start data token for write multiple blocks*/ | |||
| uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC; | |||
| /** mask for data response tokens after a write block operation */ | |||
| uint8_t const DATA_RES_MASK = 0X1F; | |||
| /** write data accepted token */ | |||
| uint8_t const DATA_RES_ACCEPTED = 0X05; | |||
| //------------------------------------------------------------------------------ | |||
| /** Card IDentification (CID) register */ | |||
| typedef struct CID { | |||
| // byte 0 | |||
| /** Manufacturer ID */ | |||
| unsigned char mid; | |||
| // byte 1-2 | |||
| /** OEM/Application ID */ | |||
| char oid[2]; | |||
| // byte 3-7 | |||
| /** Product name */ | |||
| char pnm[5]; | |||
| // byte 8 | |||
| /** Product revision least significant digit */ | |||
| unsigned char prv_m : 4; | |||
| /** Product revision most significant digit */ | |||
| unsigned char prv_n : 4; | |||
| // byte 9-12 | |||
| /** Product serial number */ | |||
| uint32_t psn; | |||
| // byte 13 | |||
| /** Manufacturing date year low digit */ | |||
| unsigned char mdt_year_high : 4; | |||
| /** not used */ | |||
| unsigned char reserved : 4; | |||
| // byte 14 | |||
| /** Manufacturing date month */ | |||
| unsigned char mdt_month : 4; | |||
| /** Manufacturing date year low digit */ | |||
| unsigned char mdt_year_low :4; | |||
| // byte 15 | |||
| /** not used always 1 */ | |||
| unsigned char always1 : 1; | |||
| /** CRC7 checksum */ | |||
| unsigned char crc : 7; | |||
| }__attribute__((packed)) cid_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** CSD for version 1.00 cards */ | |||
| typedef struct CSDV1 { | |||
| // byte 0 | |||
| unsigned char reserved1 : 6; | |||
| unsigned char csd_ver : 2; | |||
| // byte 1 | |||
| unsigned char taac; | |||
| // byte 2 | |||
| unsigned char nsac; | |||
| // byte 3 | |||
| unsigned char tran_speed; | |||
| // byte 4 | |||
| unsigned char ccc_high; | |||
| // byte 5 | |||
| unsigned char read_bl_len : 4; | |||
| unsigned char ccc_low : 4; | |||
| // byte 6 | |||
| unsigned char c_size_high : 2; | |||
| unsigned char reserved2 : 2; | |||
| unsigned char dsr_imp : 1; | |||
| unsigned char read_blk_misalign :1; | |||
| unsigned char write_blk_misalign : 1; | |||
| unsigned char read_bl_partial : 1; | |||
| // byte 7 | |||
| unsigned char c_size_mid; | |||
| // byte 8 | |||
| unsigned char vdd_r_curr_max : 3; | |||
| unsigned char vdd_r_curr_min : 3; | |||
| unsigned char c_size_low :2; | |||
| // byte 9 | |||
| unsigned char c_size_mult_high : 2; | |||
| unsigned char vdd_w_cur_max : 3; | |||
| unsigned char vdd_w_curr_min : 3; | |||
| // byte 10 | |||
| unsigned char sector_size_high : 6; | |||
| unsigned char erase_blk_en : 1; | |||
| unsigned char c_size_mult_low : 1; | |||
| // byte 11 | |||
| unsigned char wp_grp_size : 7; | |||
| unsigned char sector_size_low : 1; | |||
| // byte 12 | |||
| unsigned char write_bl_len_high : 2; | |||
| unsigned char r2w_factor : 3; | |||
| unsigned char reserved3 : 2; | |||
| unsigned char wp_grp_enable : 1; | |||
| // byte 13 | |||
| unsigned char reserved4 : 5; | |||
| unsigned char write_partial : 1; | |||
| unsigned char write_bl_len_low : 2; | |||
| // byte 14 | |||
| unsigned char reserved5: 2; | |||
| unsigned char file_format : 2; | |||
| unsigned char tmp_write_protect : 1; | |||
| unsigned char perm_write_protect : 1; | |||
| unsigned char copy : 1; | |||
| /** Indicates the file format on the card */ | |||
| unsigned char file_format_grp : 1; | |||
| // byte 15 | |||
| unsigned char always1 : 1; | |||
| unsigned char crc : 7; | |||
| }__attribute__((packed)) csd1_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** CSD for version 2.00 cards */ | |||
| typedef struct CSDV2 { | |||
| // byte 0 | |||
| unsigned char reserved1 : 6; | |||
| unsigned char csd_ver : 2; | |||
| // byte 1 | |||
| /** fixed to 0X0E */ | |||
| unsigned char taac; | |||
| // byte 2 | |||
| /** fixed to 0 */ | |||
| unsigned char nsac; | |||
| // byte 3 | |||
| unsigned char tran_speed; | |||
| // byte 4 | |||
| unsigned char ccc_high; | |||
| // byte 5 | |||
| /** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */ | |||
| unsigned char read_bl_len : 4; | |||
| unsigned char ccc_low : 4; | |||
| // byte 6 | |||
| /** not used */ | |||
| unsigned char reserved2 : 4; | |||
| unsigned char dsr_imp : 1; | |||
| /** fixed to 0 */ | |||
| unsigned char read_blk_misalign :1; | |||
| /** fixed to 0 */ | |||
| unsigned char write_blk_misalign : 1; | |||
| /** fixed to 0 - no partial read */ | |||
| unsigned char read_bl_partial : 1; | |||
| // byte 7 | |||
| /** high part of card size */ | |||
| unsigned char c_size_high : 6; | |||
| /** not used */ | |||
| unsigned char reserved3 : 2; | |||
| // byte 8 | |||
| /** middle part of card size */ | |||
| unsigned char c_size_mid; | |||
| // byte 9 | |||
| /** low part of card size */ | |||
| unsigned char c_size_low; | |||
| // byte 10 | |||
| /** sector size is fixed at 64 KB */ | |||
| unsigned char sector_size_high : 6; | |||
| /** fixed to 1 - erase single is supported */ | |||
| unsigned char erase_blk_en : 1; | |||
| /** not used */ | |||
| unsigned char reserved4 : 1; | |||
| // byte 11 | |||
| unsigned char wp_grp_size : 7; | |||
| /** sector size is fixed at 64 KB */ | |||
| unsigned char sector_size_low : 1; | |||
| // byte 12 | |||
| /** write_bl_len fixed for 512 byte blocks */ | |||
| unsigned char write_bl_len_high : 2; | |||
| /** fixed value of 2 */ | |||
| unsigned char r2w_factor : 3; | |||
| /** not used */ | |||
| unsigned char reserved5 : 2; | |||
| /** fixed value of 0 - no write protect groups */ | |||
| unsigned char wp_grp_enable : 1; | |||
| // byte 13 | |||
| unsigned char reserved6 : 5; | |||
| /** always zero - no partial block read*/ | |||
| unsigned char write_partial : 1; | |||
| /** write_bl_len fixed for 512 byte blocks */ | |||
| unsigned char write_bl_len_low : 2; | |||
| // byte 14 | |||
| unsigned char reserved7: 2; | |||
| /** Do not use always 0 */ | |||
| unsigned char file_format : 2; | |||
| unsigned char tmp_write_protect : 1; | |||
| unsigned char perm_write_protect : 1; | |||
| unsigned char copy : 1; | |||
| /** Do not use always 0 */ | |||
| unsigned char file_format_grp : 1; | |||
| // byte 15 | |||
| /** not used always 1 */ | |||
| unsigned char always1 : 1; | |||
| /** checksum */ | |||
| unsigned char crc : 7; | |||
| }__attribute__((packed)) csd2_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** union of old and new style CSD register */ | |||
| union csd_t { | |||
| csd1_t v1; | |||
| csd2_t v2; | |||
| }; | |||
| #endif // SdInfo_h | |||
| @@ -0,0 +1,173 @@ | |||
| /* Arduino SdSpi Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdSpi Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdSpi Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * \file | |||
| * \brief SdSpi class for V2 SD/SDHC cards | |||
| */ | |||
| #ifndef SdSpi_h | |||
| #define SdSpi_h | |||
| #include <Arduino.h> | |||
| #include <SdFatConfig.h> | |||
| #if !USE_ARDUINO_SPI_LIBRARY | |||
| // AVR Arduinos | |||
| #ifdef __AVR__ | |||
| #if AVR_SOFT_SPI | |||
| #define USE_SOFTWARE_SPI 1 | |||
| #elif LEONARDO_SOFT_SPI && defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY) | |||
| #define USE_SOFTWARE_SPI 1 | |||
| #elif MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)\ | |||
| ||defined(__AVR_ATmega2560__)) | |||
| #define USE_SOFTWARE_SPI 1 | |||
| #else // USE_SOFTWARE_SPI | |||
| #define USE_NATIVE_AVR_SPI 1 | |||
| #endif // USE_SOFTWARE_SPI | |||
| #endif // __AVR__ | |||
| // Due | |||
| #if defined(__arm__) && !defined(CORE_TEENSY) | |||
| #if DUE_SOFT_SPI | |||
| #define USE_SOFTWARE_SPI 1 | |||
| #else // DUE_SOFT_SPI | |||
| /** Nonzero - use native SAM3X SPI */ | |||
| #define USE_NATIVE_SAM3X_SPI 1 | |||
| #endif // DUE_SOFT_SPI | |||
| #endif // defined(__arm__) && !defined(CORE_TEENSY) | |||
| // Teensy 3.0 | |||
| #if defined(__arm__) && defined(CORE_TEENSY) | |||
| #if TEENSY3_SOFT_SPI | |||
| #define USE_SOFTWARE_SPI 1 | |||
| #else // TEENSY3_SOFT_SPI | |||
| /** Nonzero - use native MK20DX128 SPI */ | |||
| #define USE_NATIVE_TEENSY3_SPI 1 | |||
| #endif // TEENSY3_SOFT_SPI | |||
| #endif // defined(__arm__) && defined(CORE_TEENSY) | |||
| #endif // !USE_ARDUINO_SPI_LIBRARY | |||
| #ifndef USE_SOFTWARE_SPI | |||
| #define USE_SOFTWARE_SPI 0 | |||
| #endif // USE_SOFTWARE_SPI | |||
| #ifndef USE_NATIVE_AVR_SPI | |||
| #define USE_NATIVE_AVR_SPI 0 | |||
| #endif | |||
| #ifndef USE_NATIVE_SAM3X_SPI | |||
| #define USE_NATIVE_SAM3X_SPI 0 | |||
| #endif // USE_NATIVE_SAM3X_SPI | |||
| #ifndef USE_NATIVE_TEENSY3_SPI | |||
| #define USE_NATIVE_TEENSY3_SPI 0 | |||
| #endif // USE_NATIVE_TEENSY3_SPI | |||
| //------------------------------------------------------------------------------ | |||
| // define default chip select pin | |||
| // | |||
| #if !USE_SOFTWARE_SPI | |||
| /** The default chip select pin for the SD card is SS. */ | |||
| uint8_t const SD_CHIP_SELECT_PIN = SS; | |||
| #else // USE_AVR_SOFTWARE_SPI | |||
| /** SPI chip select pin for software SPI. */ | |||
| uint8_t const SD_CHIP_SELECT_PIN = SOFT_SPI_CS_PIN; | |||
| #endif // USE_AVR_SOFTWARE_SPI | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \class SdSpi | |||
| * \brief SPI class for access to SD and SDHC flash memory cards. | |||
| */ | |||
| class SdSpi { | |||
| public: | |||
| /** Initialize the SPI bus */ | |||
| void begin(); | |||
| /** Set SPI options for access to SD/SDHC cards. | |||
| * | |||
| * \param[in] spiDivisor SCK clock divider relative to the system clock. | |||
| */ | |||
| void init(uint8_t spiDivisor); | |||
| /** Receive a byte. | |||
| * | |||
| * \return The byte. | |||
| */ | |||
| uint8_t receive(); | |||
| /** Receive multiple bytes. | |||
| * | |||
| * \param[out] buf Buffer to receive the data. | |||
| * \param[in] n Number of bytes to receive. | |||
| * | |||
| * \return Zero for no error or nonzero error code. | |||
| */ | |||
| uint8_t receive(uint8_t* buf, size_t n); | |||
| /** Send a byte. | |||
| * | |||
| * \param[in] data Byte to send | |||
| */ | |||
| void send(uint8_t data); | |||
| /** Send multiple bytes. | |||
| * | |||
| * \param[in] buf Buffer for data to be sent. | |||
| * \param[in] n Number of bytes to send. | |||
| */ | |||
| void send(const uint8_t* buf, size_t n); | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Use of inline for AVR results in up to 10% better write performance. | |||
| // Inline also save a little flash memory. | |||
| /** inline avr native functions if nonzero. */ | |||
| #define USE_AVR_NATIVE_SPI_INLINE 1 | |||
| #if USE_NATIVE_AVR_SPI && USE_AVR_NATIVE_SPI_INLINE | |||
| inline uint8_t SdSpi::receive() { | |||
| SPDR = 0XFF; | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| return SPDR; | |||
| } | |||
| inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| if (n-- == 0) return 0; | |||
| SPDR = 0XFF; | |||
| for (size_t i = 0; i < n; i++) { | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| uint8_t b = SPDR; | |||
| SPDR = 0XFF; | |||
| buf[i] = b; | |||
| } | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| buf[n] = SPDR; | |||
| return 0; | |||
| } | |||
| inline void SdSpi::send(uint8_t data) { | |||
| SPDR = data; | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| } | |||
| inline void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| if (n == 0) return; | |||
| SPDR = buf[0]; | |||
| if (n > 1) { | |||
| uint8_t b = buf[1]; | |||
| size_t i = 2; | |||
| while (1) { | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| SPDR = b; | |||
| if (i == n) break; | |||
| b = buf[i++]; | |||
| } | |||
| } | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| } | |||
| #endif // USE_NATIVE_AVR_SPI && USE_AVR_NATIVE_SPI_INLINE | |||
| #endif // SdSpi_h | |||
| @@ -0,0 +1,86 @@ | |||
| /* Arduino SdSpi Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdSpi Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdSpi Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdSpi.h> | |||
| #if USE_NATIVE_AVR_SPI | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::begin() { | |||
| // set SS high - may be chip select for another SPI device | |||
| digitalWrite(SS, HIGH); | |||
| // SS must be in output mode even it is not chip select | |||
| pinMode(SS, OUTPUT); | |||
| pinMode(MISO, INPUT); | |||
| pinMode(MOSI, OUTPUT); | |||
| pinMode(SCK, OUTPUT); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::init(uint8_t sckDivisor) { | |||
| uint8_t r = 0; | |||
| for (uint8_t b = 2; sckDivisor > b && r < 6; b <<= 1, r++) {} | |||
| // See avr processor documentation | |||
| SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1); | |||
| SPSR = r & 1 || r == 6 ? 0 : 1 << SPI2X; | |||
| } | |||
| #if !USE_AVR_NATIVE_SPI_INLINE | |||
| //------------------------------------------------------------------------------ | |||
| uint8_t SdSpi::receive() { | |||
| SPDR = 0XFF; | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| return SPDR; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| if (n-- == 0) return 0; | |||
| SPDR = 0XFF; | |||
| for (size_t i = 0; i < n; i++) { | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| uint8_t b = SPDR; | |||
| SPDR = 0XFF; | |||
| buf[i] = b; | |||
| } | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| buf[n] = SPDR; | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::send(uint8_t data) { | |||
| SPDR = data; | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| if (n == 0) return; | |||
| SPDR = buf[0]; | |||
| if (n > 1) { | |||
| uint8_t b = buf[1]; | |||
| size_t i = 2; | |||
| while (1) { | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| SPDR = b; | |||
| if (i == n) break; | |||
| b = buf[i++]; | |||
| } | |||
| } | |||
| while (!(SPSR & (1 << SPIF))) {} | |||
| } | |||
| #endif // !USE_AVR_NATIVE_SPI_INLINE | |||
| #endif // USE_NATIVE_AVR_SPI | |||
| @@ -0,0 +1,70 @@ | |||
| /* Arduino SdSpi Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdSpi Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdSpi Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdSpi.h> | |||
| #if USE_ARDUINO_SPI_LIBRARY | |||
| #include <SPI.h> | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::begin() { | |||
| SPI.begin(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::init(uint8_t sckDivisor) { | |||
| SPI.setBitOrder(MSBFIRST); | |||
| SPI.setDataMode(SPI_MODE0); | |||
| #ifndef SPI_CLOCK_DIV128 | |||
| SPI.setClockDivider(sckDivisor); | |||
| #else // SPI_CLOCK_DIV128 | |||
| int v; | |||
| if (sckDivisor <= 2) v = SPI_CLOCK_DIV2; | |||
| else if (sckDivisor <= 4) v = SPI_CLOCK_DIV4; | |||
| else if (sckDivisor <= 8) v = SPI_CLOCK_DIV8; | |||
| else if (sckDivisor <= 16) v = SPI_CLOCK_DIV16; | |||
| else if (sckDivisor <= 32) v = SPI_CLOCK_DIV32; | |||
| else if (sckDivisor <= 64) v = SPI_CLOCK_DIV64; | |||
| else v = SPI_CLOCK_DIV128; | |||
| SPI.setClockDivider(v); | |||
| #endif // SPI_CLOCK_DIV128 | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI receive a byte */ | |||
| uint8_t SdSpi::receive() { | |||
| return SPI.transfer(0XFF); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI receive multiple bytes */ | |||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| for (size_t i = 0; i < n; i++) { | |||
| buf[i] = SPI.transfer(0XFF); | |||
| } | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI send a byte */ | |||
| void SdSpi::send(uint8_t b) { | |||
| SPI.transfer(b); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI send multiple bytes */ | |||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| for (size_t i = 0; i < n; i++) { | |||
| SPI.transfer(buf[i]); | |||
| } | |||
| } | |||
| #endif // USE_ARDUINO_SPI_LIBRARY | |||
| @@ -0,0 +1,216 @@ | |||
| /* Arduino SdSpi Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdSpi Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdSpi Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdSpi.h> | |||
| #if USE_NATIVE_SAM3X_SPI | |||
| /** Use SAM3X DMAC if nonzero */ | |||
| #define USE_SAM3X_DMAC 1 | |||
| /** Use extra Bus Matrix arbitration fix if nonzero */ | |||
| #define USE_SAM3X_BUS_MATRIX_FIX 0 | |||
| /** Time in ms for DMA receive timeout */ | |||
| #define SAM3X_DMA_TIMEOUT 100 | |||
| /** chip select register number */ | |||
| #define SPI_CHIP_SEL 3 | |||
| /** DMAC receive channel */ | |||
| #define SPI_DMAC_RX_CH 1 | |||
| /** DMAC transmit channel */ | |||
| #define SPI_DMAC_TX_CH 0 | |||
| /** DMAC Channel HW Interface Number for SPI TX. */ | |||
| #define SPI_TX_IDX 1 | |||
| /** DMAC Channel HW Interface Number for SPI RX. */ | |||
| #define SPI_RX_IDX 2 | |||
| //------------------------------------------------------------------------------ | |||
| /** Disable DMA Controller. */ | |||
| static void dmac_disable() { | |||
| DMAC->DMAC_EN &= (~DMAC_EN_ENABLE); | |||
| } | |||
| /** Enable DMA Controller. */ | |||
| static void dmac_enable() { | |||
| DMAC->DMAC_EN = DMAC_EN_ENABLE; | |||
| } | |||
| /** Disable DMA Channel. */ | |||
| static void dmac_channel_disable(uint32_t ul_num) { | |||
| DMAC->DMAC_CHDR = DMAC_CHDR_DIS0 << ul_num; | |||
| } | |||
| /** Enable DMA Channel. */ | |||
| static void dmac_channel_enable(uint32_t ul_num) { | |||
| DMAC->DMAC_CHER = DMAC_CHER_ENA0 << ul_num; | |||
| } | |||
| /** Poll for transfer complete. */ | |||
| static bool dmac_channel_transfer_done(uint32_t ul_num) { | |||
| return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::begin() { | |||
| PIO_Configure( | |||
| g_APinDescription[PIN_SPI_MOSI].pPort, | |||
| g_APinDescription[PIN_SPI_MOSI].ulPinType, | |||
| g_APinDescription[PIN_SPI_MOSI].ulPin, | |||
| g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); | |||
| PIO_Configure( | |||
| g_APinDescription[PIN_SPI_MISO].pPort, | |||
| g_APinDescription[PIN_SPI_MISO].ulPinType, | |||
| g_APinDescription[PIN_SPI_MISO].ulPin, | |||
| g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); | |||
| PIO_Configure( | |||
| g_APinDescription[PIN_SPI_SCK].pPort, | |||
| g_APinDescription[PIN_SPI_SCK].ulPinType, | |||
| g_APinDescription[PIN_SPI_SCK].ulPin, | |||
| g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); | |||
| pmc_enable_periph_clk(ID_SPI0); | |||
| #if USE_SAM3X_DMAC | |||
| pmc_enable_periph_clk(ID_DMAC); | |||
| dmac_disable(); | |||
| DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED; | |||
| dmac_enable(); | |||
| #if USE_SAM3X_BUS_MATRIX_FIX | |||
| MATRIX->MATRIX_WPMR = 0x4d415400; | |||
| MATRIX->MATRIX_MCFG[1] = 1; | |||
| MATRIX->MATRIX_MCFG[2] = 1; | |||
| MATRIX->MATRIX_SCFG[0] = 0x01000010; | |||
| MATRIX->MATRIX_SCFG[1] = 0x01000010; | |||
| MATRIX->MATRIX_SCFG[7] = 0x01000010; | |||
| #endif // USE_SAM3X_BUS_MATRIX_FIX | |||
| #endif // USE_SAM3X_DMAC | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // start RX DMA | |||
| static void spiDmaRX(uint8_t* dst, uint16_t count) { | |||
| dmac_channel_disable(SPI_DMAC_RX_CH); | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_SADDR = (uint32_t)&SPI0->SPI_RDR; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count | | |||
| DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | | |||
| DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC | | |||
| DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) | | |||
| DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG; | |||
| dmac_channel_enable(SPI_DMAC_RX_CH); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // start TX DMA | |||
| static void spiDmaTX(const uint8_t* src, uint16_t count) { | |||
| static uint8_t ff = 0XFF; | |||
| uint32_t src_incr = DMAC_CTRLB_SRC_INCR_INCREMENTING; | |||
| if (!src) { | |||
| src = &ff; | |||
| src_incr = DMAC_CTRLB_SRC_INCR_FIXED; | |||
| } | |||
| dmac_channel_disable(SPI_DMAC_TX_CH); | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)src; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count | | |||
| DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | | |||
| DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC | | |||
| src_incr | DMAC_CTRLB_DST_INCR_FIXED; | |||
| DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) | | |||
| DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG; | |||
| dmac_channel_enable(SPI_DMAC_TX_CH); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // initialize SPI controller | |||
| void SdSpi::init(uint8_t sckDivisor) { | |||
| uint8_t scbr = sckDivisor; | |||
| Spi* pSpi = SPI0; | |||
| // disable SPI | |||
| pSpi->SPI_CR = SPI_CR_SPIDIS; | |||
| // reset SPI | |||
| pSpi->SPI_CR = SPI_CR_SWRST; | |||
| // no mode fault detection, set master mode | |||
| pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; | |||
| // mode 0, 8-bit, | |||
| pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr) | SPI_CSR_NCPHA; | |||
| // enable SPI | |||
| pSpi->SPI_CR |= SPI_CR_SPIEN; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| static inline uint8_t spiTransfer(uint8_t b) { | |||
| Spi* pSpi = SPI0; | |||
| pSpi->SPI_TDR = b; | |||
| while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} | |||
| b = pSpi->SPI_RDR; | |||
| return b; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI receive a byte */ | |||
| uint8_t SdSpi::receive() { | |||
| return spiTransfer(0XFF); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI receive multiple bytes */ | |||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| Spi* pSpi = SPI0; | |||
| int rtn = 0; | |||
| #if USE_SAM3X_DMAC | |||
| // clear overrun error | |||
| uint32_t s = pSpi->SPI_SR; | |||
| spiDmaRX(buf, n); | |||
| spiDmaTX(0, n); | |||
| uint32_t m = millis(); | |||
| while (!dmac_channel_transfer_done(SPI_DMAC_RX_CH)) { | |||
| if ((millis() - m) > SAM3X_DMA_TIMEOUT) { | |||
| dmac_channel_disable(SPI_DMAC_RX_CH); | |||
| dmac_channel_disable(SPI_DMAC_TX_CH); | |||
| rtn = 2; | |||
| break; | |||
| } | |||
| } | |||
| if (pSpi->SPI_SR & SPI_SR_OVRES) rtn |= 1; | |||
| #else // USE_SAM3X_DMAC | |||
| for (size_t i = 0; i < n; i++) { | |||
| pSpi->SPI_TDR = 0XFF; | |||
| while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} | |||
| buf[i] = pSpi->SPI_RDR; | |||
| } | |||
| #endif // USE_SAM3X_DMAC | |||
| return rtn; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI send a byte */ | |||
| void SdSpi::send(uint8_t b) { | |||
| spiTransfer(b); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| Spi* pSpi = SPI0; | |||
| #if USE_SAM3X_DMAC | |||
| spiDmaTX(buf, n); | |||
| while (!dmac_channel_transfer_done(SPI_DMAC_TX_CH)) {} | |||
| #else // #if USE_SAM3X_DMAC | |||
| while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} | |||
| for (size_t i = 0; i < n; i++) { | |||
| pSpi->SPI_TDR = buf[i]; | |||
| while ((pSpi->SPI_SR & SPI_SR_TDRE) == 0) {} | |||
| } | |||
| #endif // #if USE_SAM3X_DMAC | |||
| while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} | |||
| // leave RDR empty | |||
| uint8_t b = pSpi->SPI_RDR; | |||
| } | |||
| #endif // USE_NATIVE_SAM3X_SPI | |||
| @@ -0,0 +1,61 @@ | |||
| /* Arduino SdSpi Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdSpi Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdSpi Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdSpi.h> | |||
| #if USE_SOFTWARE_SPI | |||
| #include <SoftSPI.h> | |||
| static | |||
| SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, 0> softSpiBus; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * initialize SPI pins | |||
| */ | |||
| void SdSpi::begin() { | |||
| softSpiBus.begin(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Initialize hardware SPI - dummy for soft SPI | |||
| */ | |||
| void SdSpi::init(uint8_t sckDivisor) {} | |||
| //------------------------------------------------------------------------------ | |||
| /** Soft SPI receive byte */ | |||
| uint8_t SdSpi::receive() { | |||
| return softSpiBus.receive(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Soft SPI read data */ | |||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| for (size_t i = 0; i < n; i++) { | |||
| buf[i] = receive(); | |||
| } | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Soft SPI send byte */ | |||
| void SdSpi::send(uint8_t data) { | |||
| softSpiBus.send(data); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| for (size_t i = 0; i < n; i++) { | |||
| send(buf[i]); | |||
| } | |||
| } | |||
| #endif // USE_SOFTWARE_SPI | |||
| @@ -0,0 +1,223 @@ | |||
| /* Arduino SdSpi Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdSpi Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdSpi Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdSpi.h> | |||
| #if USE_NATIVE_TEENSY3_SPI | |||
| // Teensy 3.0 functions | |||
| #include <mk20dx128.h> | |||
| // use 16-bit frame if SPI_USE_8BIT_FRAME is zero | |||
| #define SPI_USE_8BIT_FRAME 0 | |||
| // Limit initial fifo to three entries to avoid fifo overrun | |||
| #define SPI_INITIAL_FIFO_DEPTH 3 | |||
| // define some symbols that are not in mk20dx128.h | |||
| #ifndef SPI_SR_RXCTR | |||
| #define SPI_SR_RXCTR 0XF0 | |||
| #endif // SPI_SR_RXCTR | |||
| #ifndef SPI_PUSHR_CONT | |||
| #define SPI_PUSHR_CONT 0X80000000 | |||
| #endif // SPI_PUSHR_CONT | |||
| #ifndef SPI_PUSHR_CTAS | |||
| #define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) | |||
| #endif // SPI_PUSHR_CTAS | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * initialize SPI pins | |||
| */ | |||
| void SdSpi::begin() { | |||
| SIM_SCGC6 |= SIM_SCGC6_SPI0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Initialize hardware SPI | |||
| * | |||
| */ | |||
| void SdSpi::init(uint8_t sckDivisor) { | |||
| uint32_t ctar, ctar0, ctar1; | |||
| if (sckDivisor <= 2) { | |||
| // 1/2 speed | |||
| ctar = SPI_CTAR_DBR | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||
| } else if (sckDivisor <= 4) { | |||
| // 1/4 speed | |||
| ctar = SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); | |||
| } else if (sckDivisor <= 8) { | |||
| // 1/8 speed | |||
| ctar = SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); | |||
| } else if (sckDivisor <= 12) { | |||
| // 1/12 speed | |||
| ctar = SPI_CTAR_BR(2) | SPI_CTAR_CSSCK(2); | |||
| } else if (sckDivisor <= 16) { | |||
| // 1/16 speed | |||
| ctar = SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(3); | |||
| } else if (sckDivisor <= 32) { | |||
| // 1/32 speed | |||
| ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(4); | |||
| } else if (sckDivisor <= 64) { | |||
| // 1/64 speed | |||
| ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(5); | |||
| } else { | |||
| // 1/128 speed | |||
| ctar = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(6); | |||
| } | |||
| // CTAR0 - 8 bit transfer | |||
| ctar0 = ctar | SPI_CTAR_FMSZ(7); | |||
| // CTAR1 - 16 bit transfer | |||
| ctar1 = ctar | SPI_CTAR_FMSZ(15); | |||
| if (SPI0_CTAR0 != ctar0 || SPI0_CTAR1 != ctar1) { | |||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); | |||
| SPI0_CTAR0 = ctar0; | |||
| SPI0_CTAR1 = ctar1; | |||
| } | |||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); | |||
| CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||
| CORE_PIN12_CONFIG = PORT_PCR_MUX(2); | |||
| CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI receive a byte */ | |||
| uint8_t SdSpi::receive() { | |||
| SPI0_MCR |= SPI_MCR_CLR_RXF; | |||
| SPI0_SR = SPI_SR_TCF; | |||
| SPI0_PUSHR = 0xFF; | |||
| while (!(SPI0_SR & SPI_SR_TCF)) {} | |||
| return SPI0_POPR; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI receive multiple bytes */ | |||
| uint8_t SdSpi::receive(uint8_t* buf, size_t n) { | |||
| // clear any data in RX FIFO | |||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | |||
| #if SPI_USE_8BIT_FRAME | |||
| // initial number of bytes to push into TX FIFO | |||
| int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; | |||
| for (int i = 0; i < nf; i++) { | |||
| SPI0_PUSHR = 0XFF; | |||
| } | |||
| // limit for pushing dummy data into TX FIFO | |||
| uint8_t* limit = buf + n - nf; | |||
| while (buf < limit) { | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| SPI0_PUSHR = 0XFF; | |||
| *buf++ = SPI0_POPR; | |||
| } | |||
| // limit for rest of RX data | |||
| limit += nf; | |||
| while (buf < limit) { | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| *buf++ = SPI0_POPR; | |||
| } | |||
| #else // SPI_USE_8BIT_FRAME | |||
| // use 16 bit frame to avoid TD delay between frames | |||
| // get one byte if n is odd | |||
| if (n & 1) { | |||
| *buf++ = receive(); | |||
| n--; | |||
| } | |||
| // initial number of words to push into TX FIFO | |||
| int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; | |||
| for (int i = 0; i < nf; i++) { | |||
| SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; | |||
| } | |||
| uint8_t* limit = buf + n - 2*nf; | |||
| while (buf < limit) { | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; | |||
| uint16_t w = SPI0_POPR; | |||
| *buf++ = w >> 8; | |||
| *buf++ = w & 0XFF; | |||
| } | |||
| // limit for rest of RX data | |||
| limit += 2*nf; | |||
| while (buf < limit) { | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| uint16_t w = SPI0_POPR; | |||
| *buf++ = w >> 8; | |||
| *buf++ = w & 0XFF; | |||
| } | |||
| #endif // SPI_USE_8BIT_FRAME | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI send a byte */ | |||
| void SdSpi::send(uint8_t b) { | |||
| SPI0_MCR |= SPI_MCR_CLR_RXF; | |||
| SPI0_SR = SPI_SR_TCF; | |||
| SPI0_PUSHR = b; | |||
| while (!(SPI0_SR & SPI_SR_TCF)) {} | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** SPI send multiple bytes */ | |||
| void SdSpi::send(const uint8_t* buf , size_t n) { | |||
| // clear any data in RX FIFO | |||
| SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | |||
| #if SPI_USE_8BIT_FRAME | |||
| // initial number of bytes to push into TX FIFO | |||
| int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; | |||
| // limit for pushing data into TX fifo | |||
| const uint8_t* limit = buf + n; | |||
| for (int i = 0; i < nf; i++) { | |||
| SPI0_PUSHR = *buf++; | |||
| } | |||
| // write data to TX FIFO | |||
| while (buf < limit) { | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| SPI0_PUSHR = *buf++; | |||
| SPI0_POPR; | |||
| } | |||
| // wait for data to be sent | |||
| while (nf) { | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| SPI0_POPR; | |||
| nf--; | |||
| } | |||
| #else // SPI_USE_8BIT_FRAME | |||
| // use 16 bit frame to avoid TD delay between frames | |||
| // send one byte if n is odd | |||
| if (n & 1) { | |||
| send(*buf++); | |||
| n--; | |||
| } | |||
| // initial number of words to push into TX FIFO | |||
| int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; | |||
| // limit for pushing data into TX fifo | |||
| const uint8_t* limit = buf + n; | |||
| for (int i = 0; i < nf; i++) { | |||
| uint16_t w = (*buf++) << 8; | |||
| w |= *buf++; | |||
| SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; | |||
| } | |||
| // write data to TX FIFO | |||
| while (buf < limit) { | |||
| uint16_t w = *buf++ << 8; | |||
| w |= *buf++; | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; | |||
| SPI0_POPR; | |||
| } | |||
| // wait for data to be sent | |||
| while (nf) { | |||
| while (!(SPI0_SR & SPI_SR_RXCTR)) {} | |||
| SPI0_POPR; | |||
| nf--; | |||
| } | |||
| #endif // SPI_USE_8BIT_FRAME | |||
| } | |||
| #endif // USE_NATIVE_TEENSY3_SPI | |||
| @@ -0,0 +1,151 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdFat.h> | |||
| //============================================================================== | |||
| /// @cond SHOW_PROTECTED | |||
| int16_t SdStreamBase::getch() { | |||
| uint8_t c; | |||
| int8_t s = read(&c, 1); | |||
| if (s != 1) { | |||
| if (s < 0) { | |||
| setstate(badbit); | |||
| } else { | |||
| setstate(eofbit); | |||
| } | |||
| return -1; | |||
| } | |||
| if (c != '\r' || (getmode() & ios::binary)) return c; | |||
| s = read(&c, 1); | |||
| if (s == 1 && c == '\n') return c; | |||
| if (s == 1) seekCur(-1); | |||
| return '\r'; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdStreamBase::open(const char* path, ios::openmode mode) { | |||
| uint8_t flags; | |||
| switch (mode & (app | in | out | trunc)) { | |||
| case app | in: | |||
| case app | in | out: | |||
| flags = O_RDWR | O_APPEND | O_CREAT; | |||
| break; | |||
| case app: | |||
| case app | out: | |||
| flags = O_WRITE | O_APPEND | O_CREAT; | |||
| break; | |||
| case in: | |||
| flags = O_READ; | |||
| break; | |||
| case in | out: | |||
| flags = O_RDWR; | |||
| break; | |||
| case in | out | trunc: | |||
| flags = O_RDWR | O_TRUNC | O_CREAT; | |||
| break; | |||
| case out: | |||
| case out | trunc: | |||
| flags = O_WRITE | O_TRUNC | O_CREAT; | |||
| break; | |||
| default: | |||
| goto fail; | |||
| } | |||
| if (mode & ios::ate) flags |= O_AT_END; | |||
| if (!SdBaseFile::open(path, flags)) goto fail; | |||
| setmode(mode); | |||
| clear(); | |||
| return; | |||
| fail: | |||
| SdBaseFile::close(); | |||
| setstate(failbit); | |||
| return; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdStreamBase::putch(char c) { | |||
| if (c == '\n' && !(getmode() & ios::binary)) { | |||
| write('\r'); | |||
| } | |||
| write(c); | |||
| if (writeError) setstate(badbit); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdStreamBase::putstr(const char* str) { | |||
| size_t n = 0; | |||
| while (1) { | |||
| char c = str[n]; | |||
| if (c == '\0' || (c == '\n' && !(getmode() & ios::binary))) { | |||
| if (n > 0) write(str, n); | |||
| if (c == '\0') break; | |||
| write('\r'); | |||
| str += n; | |||
| n = 0; | |||
| } | |||
| n++; | |||
| } | |||
| if (writeError) setstate(badbit); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Internal do not use | |||
| * \param[in] off | |||
| * \param[in] way | |||
| */ | |||
| bool SdStreamBase::seekoff(off_type off, seekdir way) { | |||
| pos_type pos; | |||
| switch (way) { | |||
| case beg: | |||
| pos = off; | |||
| break; | |||
| case cur: | |||
| pos = curPosition() + off; | |||
| break; | |||
| case end: | |||
| pos = fileSize() + off; | |||
| break; | |||
| default: | |||
| return false; | |||
| } | |||
| return seekpos(pos); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Internal do not use | |||
| * \param[in] pos | |||
| */ | |||
| bool SdStreamBase::seekpos(pos_type pos) { | |||
| return seekSet(pos); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int SdStreamBase::write(const void* buf, size_t n) { | |||
| return SdBaseFile::write(buf, n); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void SdStreamBase::write(char c) { | |||
| write(&c, 1); | |||
| } | |||
| /// @endcond | |||
| @@ -0,0 +1,263 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef SdStream_h | |||
| #define SdStream_h | |||
| /** | |||
| * \file | |||
| * \brief \ref fstream, \ref ifstream, and \ref ofstream classes | |||
| */ | |||
| #include <SdBaseFile.h> | |||
| #include <iostream.h> | |||
| //============================================================================== | |||
| /** | |||
| * \class SdStreamBase | |||
| * \brief Base class for SD streams | |||
| */ | |||
| class SdStreamBase : protected SdBaseFile, virtual public ios { | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| int16_t getch(); | |||
| void putch(char c); | |||
| void putstr(const char *str); | |||
| void open(const char* path, ios::openmode mode); | |||
| /** Internal do not use | |||
| * \return mode | |||
| */ | |||
| ios::openmode getmode() {return m_mode;} | |||
| /** Internal do not use | |||
| * \param[in] mode | |||
| */ | |||
| void setmode(ios::openmode mode) {m_mode = mode;} | |||
| bool seekoff(off_type off, seekdir way); | |||
| bool seekpos(pos_type pos); | |||
| int write(const void* buf, size_t n); | |||
| void write(char c); | |||
| /// @endcond | |||
| private: | |||
| ios::openmode m_mode; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| * \class fstream | |||
| * \brief SD file input/output stream. | |||
| */ | |||
| class fstream : public iostream, SdStreamBase { | |||
| public: | |||
| using iostream::peek; | |||
| fstream() {} | |||
| /** Constructor with open | |||
| * | |||
| * \param[in] path path to open | |||
| * \param[in] mode open mode | |||
| */ | |||
| explicit fstream(const char* path, openmode mode = in | out) { | |||
| open(path, mode); | |||
| } | |||
| #if DESTRUCTOR_CLOSES_FILE | |||
| ~fstream() {} | |||
| #endif // DESTRUCTOR_CLOSES_FILE | |||
| /** Clear state and writeError | |||
| * \param[in] state new state for stream | |||
| */ | |||
| void clear(iostate state = goodbit) { | |||
| ios::clear(state); | |||
| SdBaseFile::writeError = false; | |||
| } | |||
| /** Close a file and force cached data and directory information | |||
| * to be written to the storage device. | |||
| */ | |||
| void close() {SdBaseFile::close();} | |||
| /** Open a fstream | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| * | |||
| * Valid open modes are (at end, ios::ate, and/or ios::binary may be added): | |||
| * | |||
| * ios::in - Open file for reading. | |||
| * | |||
| * ios::out or ios::out | ios::trunc - Truncate to 0 length, if existent, | |||
| * or create a file for writing only. | |||
| * | |||
| * ios::app or ios::out | ios::app - Append; open or create file for | |||
| * writing at end-of-file. | |||
| * | |||
| * ios::in | ios::out - Open file for update (reading and writing). | |||
| * | |||
| * ios::in | ios::out | ios::trunc - Truncate to zero length, if existent, | |||
| * or create file for update. | |||
| * | |||
| * ios::in | ios::app or ios::in | ios::out | ios::app - Append; open or | |||
| * create text file for update, writing at end of file. | |||
| */ | |||
| void open(const char* path, openmode mode = in | out) { | |||
| SdStreamBase::open(path, mode); | |||
| } | |||
| /** \return True if stream is open else false. */ | |||
| bool is_open () {return SdBaseFile::isOpen();} | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** Internal - do not use | |||
| * \return | |||
| */ | |||
| int16_t getch() {return SdStreamBase::getch();} | |||
| /** Internal - do not use | |||
| * \param[out] pos | |||
| */ | |||
| void getpos(FatPos_t* pos) {SdBaseFile::getpos(pos);} | |||
| /** Internal - do not use | |||
| * \param[in] c | |||
| */ | |||
| void putch(char c) {SdStreamBase::putch(c);} | |||
| /** Internal - do not use | |||
| * \param[in] str | |||
| */ | |||
| void putstr(const char *str) {SdStreamBase::putstr(str);} | |||
| /** Internal - do not use | |||
| * \param[in] pos | |||
| */ | |||
| bool seekoff(off_type off, seekdir way) { | |||
| return SdStreamBase::seekoff(off, way); | |||
| } | |||
| bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} | |||
| void setpos(FatPos_t* pos) {SdBaseFile::setpos(pos);} | |||
| bool sync() {return SdStreamBase::sync();} | |||
| pos_type tellpos() {return SdStreamBase::curPosition();} | |||
| /// @endcond | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| * \class ifstream | |||
| * \brief SD file input stream. | |||
| */ | |||
| class ifstream : public istream, SdStreamBase { | |||
| public: | |||
| using istream::peek; | |||
| ifstream() {} | |||
| /** Constructor with open | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| */ | |||
| explicit ifstream(const char* path, openmode mode = in) { | |||
| open(path, mode); | |||
| } | |||
| #if DESTRUCTOR_CLOSES_FILE | |||
| ~ifstream() {} | |||
| #endif // DESTRUCTOR_CLOSES_FILE | |||
| /** Close a file and force cached data and directory information | |||
| * to be written to the storage device. | |||
| */ | |||
| void close() {SdBaseFile::close();} | |||
| /** \return True if stream is open else false. */ | |||
| bool is_open() {return SdBaseFile::isOpen();} | |||
| /** Open an ifstream | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| * | |||
| * \a mode See fstream::open() for valid modes. | |||
| */ | |||
| void open(const char* path, openmode mode = in) { | |||
| SdStreamBase::open(path, mode | in); | |||
| } | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** Internal - do not use | |||
| * \return | |||
| */ | |||
| int16_t getch() {return SdStreamBase::getch();} | |||
| /** Internal - do not use | |||
| * \param[out] pos | |||
| */ | |||
| void getpos(FatPos_t* pos) {SdBaseFile::getpos(pos);} | |||
| /** Internal - do not use | |||
| * \param[in] pos | |||
| */ | |||
| bool seekoff(off_type off, seekdir way) { | |||
| return SdStreamBase::seekoff(off, way); | |||
| } | |||
| bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} | |||
| void setpos(FatPos_t* pos) {SdBaseFile::setpos(pos);} | |||
| pos_type tellpos() {return SdStreamBase::curPosition();} | |||
| /// @endcond | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| * \class ofstream | |||
| * \brief SD card output stream. | |||
| */ | |||
| class ofstream : public ostream, SdStreamBase { | |||
| public: | |||
| ofstream() {} | |||
| /** Constructor with open | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| */ | |||
| explicit ofstream(const char* path, ios::openmode mode = out) { | |||
| open(path, mode); | |||
| } | |||
| #if DESTRUCTOR_CLOSES_FILE | |||
| ~ofstream() {} | |||
| #endif // DESTRUCTOR_CLOSES_FILE | |||
| /** Clear state and writeError | |||
| * \param[in] state new state for stream | |||
| */ | |||
| void clear(iostate state = goodbit) { | |||
| ios::clear(state); | |||
| SdBaseFile::writeError = false; | |||
| } | |||
| /** Close a file and force cached data and directory information | |||
| * to be written to the storage device. | |||
| */ | |||
| void close() {SdBaseFile::close();} | |||
| /** Open an ofstream | |||
| * \param[in] path file to open | |||
| * \param[in] mode open mode | |||
| * | |||
| * \a mode See fstream::open() for valid modes. | |||
| */ | |||
| void open(const char* path, openmode mode = out) { | |||
| SdStreamBase::open(path, mode | out); | |||
| } | |||
| /** \return True if stream is open else false. */ | |||
| bool is_open() {return SdBaseFile::isOpen();} | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** | |||
| * Internal do not use | |||
| * \param[in] c | |||
| */ | |||
| void putch(char c) {SdStreamBase::putch(c);} | |||
| void putstr(const char* str) {SdStreamBase::putstr(str);} | |||
| bool seekoff(off_type off, seekdir way) { | |||
| return SdStreamBase::seekoff(off, way); | |||
| } | |||
| bool seekpos(pos_type pos) {return SdStreamBase::seekpos(pos);} | |||
| /** | |||
| * Internal do not use | |||
| * \param[in] b | |||
| */ | |||
| bool sync() {return SdStreamBase::sync();} | |||
| pos_type tellpos() {return SdStreamBase::curPosition();} | |||
| /// @endcond | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| #endif // SdStream_h | |||
| @@ -0,0 +1,595 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <SdFat.h> | |||
| //------------------------------------------------------------------------------ | |||
| #if !USE_MULTIPLE_CARDS | |||
| // raw block cache | |||
| uint8_t SdVolume::m_fatCount; // number of FATs on volume | |||
| uint32_t SdVolume::m_blocksPerFat; // FAT size in blocks | |||
| cache_t SdVolume::m_cacheBuffer; // 512 byte cache for Sd2Card | |||
| uint32_t SdVolume::m_cacheBlockNumber; // current block number | |||
| uint8_t SdVolume::m_cacheStatus; // status of cache block | |||
| #if USE_SEPARATE_FAT_CACHE | |||
| cache_t SdVolume::m_cacheFatBuffer; // 512 byte cache for FAT | |||
| uint32_t SdVolume::m_cacheFatBlockNumber; // current Fat block number | |||
| uint8_t SdVolume::m_cacheFatStatus; // status of cache Fatblock | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| Sd2Card* SdVolume::m_sdCard; // pointer to SD card object | |||
| #endif // USE_MULTIPLE_CARDS | |||
| //------------------------------------------------------------------------------ | |||
| // find a contiguous group of clusters | |||
| bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { | |||
| // start of group | |||
| uint32_t bgnCluster; | |||
| // end of group | |||
| uint32_t endCluster; | |||
| // last cluster of FAT | |||
| uint32_t fatEnd = m_clusterCount + 1; | |||
| // flag to save place to start next search | |||
| bool setStart; | |||
| // set search start cluster | |||
| if (*curCluster) { | |||
| // try to make file contiguous | |||
| bgnCluster = *curCluster + 1; | |||
| // don't save new start location | |||
| setStart = false; | |||
| } else { | |||
| // start at likely place for free cluster | |||
| bgnCluster = m_allocSearchStart; | |||
| // save next search start if no holes. | |||
| setStart = true; | |||
| } | |||
| // end of group | |||
| endCluster = bgnCluster; | |||
| // search the FAT for free clusters | |||
| for (uint32_t n = 0;; n++, endCluster++) { | |||
| // can't find space checked all clusters | |||
| if (n >= m_clusterCount) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| // past end - start from beginning of FAT | |||
| if (endCluster > fatEnd) { | |||
| bgnCluster = endCluster = 2; | |||
| } | |||
| uint32_t f; | |||
| if (!fatGet(endCluster, &f)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (f != 0) { | |||
| // don't update search start if unallocated clusters before endCluster. | |||
| if (bgnCluster != endCluster) setStart = false; | |||
| // cluster in use try next cluster as bgnCluster | |||
| bgnCluster = endCluster + 1; | |||
| } else if ((endCluster - bgnCluster + 1) == count) { | |||
| // done - found space | |||
| break; | |||
| } | |||
| } | |||
| // remember possible next free cluster | |||
| if (setStart) m_allocSearchStart = endCluster + 1; | |||
| // mark end of chain | |||
| if (!fatPutEOC(endCluster)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| // link clusters | |||
| while (endCluster > bgnCluster) { | |||
| if (!fatPut(endCluster - 1, endCluster)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| endCluster--; | |||
| } | |||
| if (*curCluster != 0) { | |||
| // connect chains | |||
| if (!fatPut(*curCluster, bgnCluster)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| // return first cluster number to caller | |||
| *curCluster = bgnCluster; | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| // cache functions | |||
| #if USE_SEPARATE_FAT_CACHE | |||
| //------------------------------------------------------------------------------ | |||
| cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) { | |||
| return cacheFetchData(blockNumber, options); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| cache_t* SdVolume::cacheFetchData(uint32_t blockNumber, uint8_t options) { | |||
| if (m_cacheBlockNumber != blockNumber) { | |||
| if (!cacheWriteData()) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (!(options & CACHE_OPTION_NO_READ)) { | |||
| if (!m_sdCard->readBlock(blockNumber, m_cacheBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| m_cacheStatus = 0; | |||
| m_cacheBlockNumber = blockNumber; | |||
| } | |||
| m_cacheStatus |= options & CACHE_STATUS_MASK; | |||
| return &m_cacheBuffer; | |||
| fail: | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||
| if (m_cacheFatBlockNumber != blockNumber) { | |||
| if (!cacheWriteFat()) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (!(options & CACHE_OPTION_NO_READ)) { | |||
| if (!m_sdCard->readBlock(blockNumber, m_cacheFatBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| m_cacheFatStatus = 0; | |||
| m_cacheFatBlockNumber = blockNumber; | |||
| } | |||
| m_cacheFatStatus |= options & CACHE_STATUS_MASK; | |||
| return &m_cacheFatBuffer; | |||
| fail: | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdVolume::cacheSync() { | |||
| return cacheWriteData() && cacheWriteFat(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdVolume::cacheWriteData() { | |||
| if (m_cacheStatus & CACHE_STATUS_DIRTY) { | |||
| if (!m_sdCard->writeBlock(m_cacheBlockNumber, m_cacheBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| m_cacheStatus &= ~CACHE_STATUS_DIRTY; | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdVolume::cacheWriteFat() { | |||
| if (m_cacheFatStatus & CACHE_STATUS_DIRTY) { | |||
| if (!m_sdCard->writeBlock(m_cacheFatBlockNumber, m_cacheFatBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| // mirror second FAT | |||
| if (m_fatCount > 1) { | |||
| uint32_t lbn = m_cacheFatBlockNumber + m_blocksPerFat; | |||
| if (!m_sdCard->writeBlock(lbn, m_cacheFatBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| m_cacheFatStatus &= ~CACHE_STATUS_DIRTY; | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| #else // USE_SEPARATE_FAT_CACHE | |||
| //------------------------------------------------------------------------------ | |||
| cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) { | |||
| if (m_cacheBlockNumber != blockNumber) { | |||
| if (!cacheSync()) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (!(options & CACHE_OPTION_NO_READ)) { | |||
| if (!m_sdCard->readBlock(blockNumber, m_cacheBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| m_cacheStatus = 0; | |||
| m_cacheBlockNumber = blockNumber; | |||
| } | |||
| m_cacheStatus |= options & CACHE_STATUS_MASK; | |||
| return &m_cacheBuffer; | |||
| fail: | |||
| return 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) { | |||
| return cacheFetch(blockNumber, options | CACHE_STATUS_FAT_BLOCK); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdVolume::cacheSync() { | |||
| if (m_cacheStatus & CACHE_STATUS_DIRTY) { | |||
| if (!m_sdCard->writeBlock(m_cacheBlockNumber, m_cacheBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| // mirror second FAT | |||
| if ((m_cacheStatus & CACHE_STATUS_FAT_BLOCK) && m_fatCount > 1) { | |||
| uint32_t lbn = m_cacheBlockNumber + m_blocksPerFat; | |||
| if (!m_sdCard->writeBlock(lbn, m_cacheBuffer.data)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| m_cacheStatus &= ~CACHE_STATUS_DIRTY; | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool SdVolume::cacheWriteData() { | |||
| return cacheSync(); | |||
| } | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| //------------------------------------------------------------------------------ | |||
| void SdVolume::cacheInvalidate() { | |||
| m_cacheBlockNumber = 0XFFFFFFFF; | |||
| m_cacheStatus = 0; | |||
| } | |||
| //============================================================================== | |||
| //------------------------------------------------------------------------------ | |||
| uint32_t SdVolume::clusterStartBlock(uint32_t cluster) const { | |||
| return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Fetch a FAT entry | |||
| bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { | |||
| uint32_t lba; | |||
| cache_t* pc; | |||
| // error if reserved cluster of beyond FAT | |||
| if (cluster < 2 || cluster > (m_clusterCount + 1)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| uint16_t index = cluster; | |||
| index += index >> 1; | |||
| lba = m_fatStartBlock + (index >> 9); | |||
| pc = cacheFetchFat(lba, CACHE_FOR_READ); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| index &= 0X1FF; | |||
| uint16_t tmp = pc->data[index]; | |||
| index++; | |||
| if (index == 512) { | |||
| pc = cacheFetchFat(lba + 1, CACHE_FOR_READ); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| index = 0; | |||
| } | |||
| tmp |= pc->data[index] << 8; | |||
| *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; | |||
| return true; | |||
| } | |||
| if (m_fatType == 16) { | |||
| lba = m_fatStartBlock + (cluster >> 8); | |||
| } else if (m_fatType == 32) { | |||
| lba = m_fatStartBlock + (cluster >> 7); | |||
| } else { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| pc = cacheFetchFat(lba, CACHE_FOR_READ); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (m_fatType == 16) { | |||
| *value = pc->fat16[cluster & 0XFF]; | |||
| } else { | |||
| *value = pc->fat32[cluster & 0X7F] & FAT32MASK; | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Store a FAT entry | |||
| bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { | |||
| uint32_t lba; | |||
| cache_t* pc; | |||
| // error if reserved cluster of beyond FAT | |||
| if (cluster < 2 || cluster > (m_clusterCount + 1)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| uint16_t index = cluster; | |||
| index += index >> 1; | |||
| lba = m_fatStartBlock + (index >> 9); | |||
| pc = cacheFetchFat(lba, CACHE_FOR_WRITE); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| index &= 0X1FF; | |||
| uint8_t tmp = value; | |||
| if (cluster & 1) { | |||
| tmp = (pc->data[index] & 0XF) | tmp << 4; | |||
| } | |||
| pc->data[index] = tmp; | |||
| index++; | |||
| if (index == 512) { | |||
| lba++; | |||
| index = 0; | |||
| pc = cacheFetchFat(lba, CACHE_FOR_WRITE); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| tmp = value >> 4; | |||
| if (!(cluster & 1)) { | |||
| tmp = ((pc->data[index] & 0XF0)) | tmp >> 4; | |||
| } | |||
| pc->data[index] = tmp; | |||
| return true; | |||
| } | |||
| if (m_fatType == 16) { | |||
| lba = m_fatStartBlock + (cluster >> 8); | |||
| } else if (m_fatType == 32) { | |||
| lba = m_fatStartBlock + (cluster >> 7); | |||
| } else { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| pc = cacheFetchFat(lba, CACHE_FOR_WRITE); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| // store entry | |||
| if (m_fatType == 16) { | |||
| pc->fat16[cluster & 0XFF] = value; | |||
| } else { | |||
| pc->fat32[cluster & 0X7F] = value; | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // free a cluster chain | |||
| bool SdVolume::freeChain(uint32_t cluster) { | |||
| uint32_t next; | |||
| do { | |||
| if (!fatGet(cluster, &next)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| // free cluster | |||
| if (!fatPut(cluster, 0)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (cluster < m_allocSearchStart) m_allocSearchStart = cluster; | |||
| cluster = next; | |||
| } while (!isEOC(cluster)); | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Volume free space in clusters. | |||
| * | |||
| * \return Count of free clusters for success or -1 if an error occurs. | |||
| */ | |||
| int32_t SdVolume::freeClusterCount() { | |||
| uint32_t free = 0; | |||
| uint32_t lba; | |||
| uint32_t todo = m_clusterCount + 2; | |||
| uint16_t n; | |||
| if (FAT12_SUPPORT && m_fatType == 12) { | |||
| for (unsigned i = 2; i < todo; i++) { | |||
| uint32_t c; | |||
| if (!fatGet(i, &c)) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| if (c == 0) free++; | |||
| } | |||
| } else if (m_fatType == 16 || m_fatType == 32) { | |||
| lba = m_fatStartBlock; | |||
| while (todo) { | |||
| cache_t* pc = cacheFetchFat(lba++, CACHE_FOR_READ); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| n = m_fatType == 16 ? 256 : 128; | |||
| if (todo < n) n = todo; | |||
| if (m_fatType == 16) { | |||
| for (uint16_t i = 0; i < n; i++) { | |||
| if (pc->fat16[i] == 0) free++; | |||
| } | |||
| } else { | |||
| for (uint16_t i = 0; i < n; i++) { | |||
| if (pc->fat32[i] == 0) free++; | |||
| } | |||
| } | |||
| todo -= n; | |||
| } | |||
| } else { | |||
| // invalid FAT type | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| return free; | |||
| fail: | |||
| return -1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Initialize a FAT volume. | |||
| * | |||
| * \param[in] dev The SD card where the volume is located. | |||
| * | |||
| * \param[in] part The partition to be used. Legal values for \a part are | |||
| * 1-4 to use the corresponding partition on a device formatted with | |||
| * a MBR, Master Boot Record, or zero if the device is formatted as | |||
| * a super floppy with the FAT boot sector in block zero. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. Reasons for | |||
| * failure include not finding a valid partition, not finding a valid | |||
| * FAT file system in the specified partition or an I/O error. | |||
| */ | |||
| bool SdVolume::init(Sd2Card* dev, uint8_t part) { | |||
| uint8_t tmp; | |||
| uint32_t totalBlocks; | |||
| uint32_t volumeStartBlock = 0; | |||
| fat32_boot_t* fbs; | |||
| cache_t* pc; | |||
| m_sdCard = dev; | |||
| m_fatType = 0; | |||
| m_allocSearchStart = 2; | |||
| m_cacheStatus = 0; // cacheSync() will write block if true | |||
| m_cacheBlockNumber = 0XFFFFFFFF; | |||
| #if USE_SEPARATE_FAT_CACHE | |||
| m_cacheFatStatus = 0; // cacheSync() will write block if true | |||
| m_cacheFatBlockNumber = 0XFFFFFFFF; | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| // if part == 0 assume super floppy with FAT boot sector in block zero | |||
| // if part > 0 assume mbr volume with partition table | |||
| if (part) { | |||
| if (part > 4) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| pc = cacheFetch(volumeStartBlock, CACHE_FOR_READ); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| part_t* p = &pc->mbr.part[part-1]; | |||
| if ((p->boot & 0X7F) != 0 || p->firstSector == 0) { | |||
| // not a valid partition | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| volumeStartBlock = p->firstSector; | |||
| } | |||
| pc = cacheFetch(volumeStartBlock, CACHE_FOR_READ); | |||
| if (!pc) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| fbs = &(pc->fbs32); | |||
| if (fbs->bytesPerSector != 512 || | |||
| fbs->fatCount == 0 || | |||
| fbs->reservedSectorCount == 0) { | |||
| // not valid FAT volume | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| m_fatCount = fbs->fatCount; | |||
| m_blocksPerCluster = fbs->sectorsPerCluster; | |||
| m_clusterBlockMask = m_blocksPerCluster - 1; | |||
| // determine shift that is same as multiply by m_blocksPerCluster | |||
| m_clusterSizeShift = 0; | |||
| for (tmp = 1; m_blocksPerCluster != tmp; m_clusterSizeShift++) { | |||
| tmp <<= 1; | |||
| if (tmp == 0) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } | |||
| m_blocksPerFat = fbs->sectorsPerFat16 ? | |||
| fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | |||
| m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount; | |||
| // count for FAT16 zero for FAT32 | |||
| m_rootDirEntryCount = fbs->rootDirEntryCount; | |||
| // directory start for FAT16 dataStart for FAT32 | |||
| m_rootDirStart = m_fatStartBlock + fbs->fatCount * m_blocksPerFat; | |||
| // data start for FAT16 and FAT32 | |||
| m_dataStartBlock = m_rootDirStart + ((32 * fbs->rootDirEntryCount + 511)/512); | |||
| // total blocks for FAT16 or FAT32 | |||
| totalBlocks = fbs->totalSectors16 ? | |||
| fbs->totalSectors16 : fbs->totalSectors32; | |||
| // total data blocks | |||
| m_clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock); | |||
| // divide by cluster size to get cluster count | |||
| m_clusterCount >>= m_clusterSizeShift; | |||
| // FAT type is determined by cluster count | |||
| if (m_clusterCount < 4085) { | |||
| m_fatType = 12; | |||
| if (!FAT12_SUPPORT) { | |||
| DBG_FAIL_MACRO; | |||
| goto fail; | |||
| } | |||
| } else if (m_clusterCount < 65525) { | |||
| m_fatType = 16; | |||
| } else { | |||
| m_rootDirStart = fbs->fat32RootCluster; | |||
| m_fatType = 32; | |||
| } | |||
| return true; | |||
| fail: | |||
| return false; | |||
| } | |||
| @@ -0,0 +1,216 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef SdVolume_h | |||
| #define SdVolume_h | |||
| /** | |||
| * \file | |||
| * \brief SdVolume class | |||
| */ | |||
| #include <SdFatConfig.h> | |||
| #include <Sd2Card.h> | |||
| #include <utility/FatStructs.h> | |||
| //============================================================================== | |||
| // SdVolume class | |||
| /** | |||
| * \brief Cache for an SD data block | |||
| */ | |||
| union cache_t { | |||
| /** Used to access cached file data blocks. */ | |||
| uint8_t data[512]; | |||
| /** Used to access cached FAT16 entries. */ | |||
| uint16_t fat16[256]; | |||
| /** Used to access cached FAT32 entries. */ | |||
| uint32_t fat32[128]; | |||
| /** Used to access cached directory entries. */ | |||
| dir_t dir[16]; | |||
| /** Used to access a cached Master Boot Record. */ | |||
| mbr_t mbr; | |||
| /** Used to access to a cached FAT boot sector. */ | |||
| fat_boot_t fbs; | |||
| /** Used to access to a cached FAT32 boot sector. */ | |||
| fat32_boot_t fbs32; | |||
| /** Used to access to a cached FAT32 FSINFO sector. */ | |||
| fat32_fsinfo_t fsinfo; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \class SdVolume | |||
| * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. | |||
| */ | |||
| class SdVolume { | |||
| public: | |||
| /** Create an instance of SdVolume */ | |||
| SdVolume() : m_fatType(0) {} | |||
| /** Clear the cache and returns a pointer to the cache. Used by the WaveRP | |||
| * recorder to do raw write to the SD card. Not for normal apps. | |||
| * \return A pointer to the cache buffer or zero if an error occurs. | |||
| */ | |||
| cache_t* cacheClear() { | |||
| if (!cacheSync()) return 0; | |||
| m_cacheBlockNumber = 0XFFFFFFFF; | |||
| return &m_cacheBuffer; | |||
| } | |||
| /** Initialize a FAT volume. Try partition one first then try super | |||
| * floppy format. | |||
| * | |||
| * \param[in] dev The Sd2Card where the volume is located. | |||
| * | |||
| * \return The value one, true, is returned for success and | |||
| * the value zero, false, is returned for failure. Reasons for | |||
| * failure include not finding a valid partition, not finding a valid | |||
| * FAT file system or an I/O error. | |||
| */ | |||
| bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);} | |||
| bool init(Sd2Card* dev, uint8_t part); | |||
| // inline functions that return volume info | |||
| /** \return The volume's cluster size in blocks. */ | |||
| uint8_t blocksPerCluster() const {return m_blocksPerCluster;} | |||
| /** \return The number of blocks in one FAT. */ | |||
| uint32_t blocksPerFat() const {return m_blocksPerFat;} | |||
| /** \return The total number of clusters in the volume. */ | |||
| uint32_t clusterCount() const {return m_clusterCount;} | |||
| /** \return The shift count required to multiply by blocksPerCluster. */ | |||
| uint8_t clusterSizeShift() const {return m_clusterSizeShift;} | |||
| /** \return The logical block number for the start of file data. */ | |||
| uint32_t dataStartBlock() const {return clusterStartBlock(2);} | |||
| /** \return The number of FAT structures on the volume. */ | |||
| uint8_t fatCount() const {return m_fatCount;} | |||
| /** \return The logical block number for the start of the first FAT. */ | |||
| uint32_t fatStartBlock() const {return m_fatStartBlock;} | |||
| /** \return The FAT type of the volume. Values are 12, 16 or 32. */ | |||
| uint8_t fatType() const {return m_fatType;} | |||
| int32_t freeClusterCount(); | |||
| /** \return The number of entries in the root directory for FAT16 volumes. */ | |||
| uint32_t rootDirEntryCount() const {return m_rootDirEntryCount;} | |||
| /** \return The logical block number for the start of the root directory | |||
| on FAT16 volumes or the first cluster number on FAT32 volumes. */ | |||
| uint32_t rootDirStart() const {return m_rootDirStart;} | |||
| /** Sd2Card object for this volume | |||
| * \return pointer to Sd2Card object. | |||
| */ | |||
| Sd2Card* sdCard() {return m_sdCard;} | |||
| /** Debug access to FAT table | |||
| * | |||
| * \param[in] n cluster number. | |||
| * \param[out] v value of entry | |||
| * \return true for success or false for failure | |||
| */ | |||
| bool dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);} | |||
| //------------------------------------------------------------------------------ | |||
| private: | |||
| // Allow SdBaseFile access to SdVolume private data. | |||
| friend class SdBaseFile; | |||
| //------------------------------------------------------------------------------ | |||
| uint32_t m_allocSearchStart; // Start cluster for alloc search. | |||
| uint8_t m_blocksPerCluster; // Cluster size in blocks. | |||
| uint8_t m_clusterBlockMask; // Mask to extract block of cluster. | |||
| uint32_t m_clusterCount; // Clusters in one FAT. | |||
| uint8_t m_clusterSizeShift; // Cluster count to block count shift. | |||
| uint32_t m_dataStartBlock; // First data block number. | |||
| uint32_t m_fatStartBlock; // Start block for first FAT. | |||
| uint8_t m_fatType; // Volume type (12, 16, OR 32). | |||
| uint16_t m_rootDirEntryCount; // Number of entries in FAT16 root dir. | |||
| uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. | |||
| //------------------------------------------------------------------------------ | |||
| // block caches | |||
| // use of static functions save a bit of flash - maybe not worth complexity | |||
| // | |||
| static const uint8_t CACHE_STATUS_DIRTY = 1; | |||
| static const uint8_t CACHE_STATUS_FAT_BLOCK = 2; | |||
| static const uint8_t CACHE_STATUS_MASK | |||
| = CACHE_STATUS_DIRTY | CACHE_STATUS_FAT_BLOCK; | |||
| static const uint8_t CACHE_OPTION_NO_READ = 4; | |||
| // value for option argument in cacheFetch to indicate read from cache | |||
| static uint8_t const CACHE_FOR_READ = 0; | |||
| // value for option argument in cacheFetch to indicate write to cache | |||
| static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; | |||
| // reserve cache block with no read | |||
| static uint8_t const CACHE_RESERVE_FOR_WRITE | |||
| = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; | |||
| #if USE_MULTIPLE_CARDS | |||
| uint8_t m_fatCount; // number of FATs on volume | |||
| uint32_t m_blocksPerFat; // FAT size in blocks | |||
| cache_t m_cacheBuffer; // 512 byte cache for device blocks | |||
| uint32_t m_cacheBlockNumber; // Logical number of block in the cache | |||
| Sd2Card* m_sdCard; // Sd2Card object for cache | |||
| uint8_t m_cacheStatus; // status of cache block | |||
| #if USE_SEPARATE_FAT_CACHE | |||
| cache_t m_cacheFatBuffer; // 512 byte cache for FAT | |||
| uint32_t m_cacheFatBlockNumber; // current Fat block number | |||
| uint8_t m_cacheFatStatus; // status of cache Fatblock | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| #else // USE_MULTIPLE_CARDS | |||
| static uint8_t m_fatCount; // number of FATs on volume | |||
| static uint32_t m_blocksPerFat; // FAT size in blocks | |||
| static cache_t m_cacheBuffer; // 512 byte cache for device blocks | |||
| static uint32_t m_cacheBlockNumber; // Logical number of block in the cache | |||
| static uint8_t m_cacheStatus; // status of cache block | |||
| #if USE_SEPARATE_FAT_CACHE | |||
| static cache_t m_cacheFatBuffer; // 512 byte cache for FAT | |||
| static uint32_t m_cacheFatBlockNumber; // current Fat block number | |||
| static uint8_t m_cacheFatStatus; // status of cache Fatblock | |||
| #endif // USE_SEPARATE_FAT_CACHE | |||
| static Sd2Card* m_sdCard; // Sd2Card object for cache | |||
| #endif // USE_MULTIPLE_CARDS | |||
| cache_t *cacheAddress() {return &m_cacheBuffer;} | |||
| uint32_t cacheBlockNumber() {return m_cacheBlockNumber;} | |||
| #if USE_MULTIPLE_CARDS | |||
| cache_t* cacheFetch(uint32_t blockNumber, uint8_t options); | |||
| cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options); | |||
| cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options); | |||
| void cacheInvalidate(); | |||
| bool cacheSync(); | |||
| bool cacheWriteData(); | |||
| bool cacheWriteFat(); | |||
| #else // USE_MULTIPLE_CARDS | |||
| static cache_t* cacheFetch(uint32_t blockNumber, uint8_t options); | |||
| static cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options); | |||
| static cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options); | |||
| static void cacheInvalidate(); | |||
| static bool cacheSync(); | |||
| static bool cacheWriteData(); | |||
| static bool cacheWriteFat(); | |||
| #endif // USE_MULTIPLE_CARDS | |||
| //------------------------------------------------------------------------------ | |||
| bool allocContiguous(uint32_t count, uint32_t* curCluster); | |||
| uint8_t blockOfCluster(uint32_t position) const { | |||
| return (position >> 9) & m_clusterBlockMask;} | |||
| uint32_t clusterStartBlock(uint32_t cluster) const; | |||
| bool fatGet(uint32_t cluster, uint32_t* value); | |||
| bool fatPut(uint32_t cluster, uint32_t value); | |||
| bool fatPutEOC(uint32_t cluster) { | |||
| return fatPut(cluster, 0x0FFFFFFF); | |||
| } | |||
| bool freeChain(uint32_t cluster); | |||
| bool isEOC(uint32_t cluster) const { | |||
| if (FAT12_SUPPORT && m_fatType == 12) return cluster >= FAT12EOC_MIN; | |||
| if (m_fatType == 16) return cluster >= FAT16EOC_MIN; | |||
| return cluster >= FAT32EOC_MIN; | |||
| } | |||
| bool readBlock(uint32_t block, uint8_t* dst) { | |||
| return m_sdCard->readBlock(block, dst);} | |||
| bool writeBlock(uint32_t block, const uint8_t* dst) { | |||
| return m_sdCard->writeBlock(block, dst); | |||
| } | |||
| }; | |||
| #endif // SdVolume | |||
| @@ -0,0 +1,535 @@ | |||
| /* Arduino RamDisk Library | |||
| * Copyright (C) 2014 by William Greiman | |||
| * | |||
| * This file is part of the Arduino RamDisk Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino RamDisk Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * \file | |||
| * StdioStream implementation | |||
| */ | |||
| #include <Arduino.h> | |||
| #include <StdioStream.h> | |||
| #include <utility/FmtNumber.h> | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::fclose() { | |||
| int rtn = 0; | |||
| if (!m_flags) { | |||
| return EOF; | |||
| } | |||
| if (m_flags & F_SWR) { | |||
| if (!flushBuf()) rtn = EOF; | |||
| } | |||
| if (!SdBaseFile::close()) rtn = EOF; | |||
| m_r = 0; | |||
| m_w = 0; | |||
| m_flags = 0; | |||
| return rtn; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::fflush() { | |||
| if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) { | |||
| if (flushBuf() && SdBaseFile::sync()) return 0; | |||
| } | |||
| return EOF; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| char* StdioStream::fgets(char* str, int num, size_t* len) { | |||
| char* s = str; | |||
| size_t n; | |||
| if (num-- <= 0) return 0; | |||
| while (num) { | |||
| if ((n = m_r) == 0) { | |||
| if (!fillBuf()) { | |||
| if (s == str) return 0; | |||
| break; | |||
| } | |||
| n = m_r; | |||
| } | |||
| if (n > num) n = num; | |||
| uint8_t* end = reinterpret_cast<uint8_t*>(memchr(m_p, '\n', n)); | |||
| if (end != 0) { | |||
| n = ++end - m_p; | |||
| memcpy(s, m_p, n); | |||
| m_r -= n; | |||
| m_p = end; | |||
| s += n; | |||
| break; | |||
| } | |||
| memcpy(s, m_p, n); | |||
| m_r -= n; | |||
| m_p += n; | |||
| s += n; | |||
| num -= n; | |||
| } | |||
| *s = 0; | |||
| if (len) *len = s - str; | |||
| return str; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool StdioStream::fopen(const char* filename, const char* mode) { | |||
| uint8_t oflags; | |||
| switch (*mode++) { | |||
| case 'a': | |||
| m_flags = F_SWR; | |||
| oflags = O_WRITE | O_CREAT | O_APPEND | O_AT_END; | |||
| break; | |||
| case 'r': | |||
| m_flags = F_SRD; | |||
| oflags = O_READ; | |||
| break; | |||
| case 'w': | |||
| m_flags = F_SWR; | |||
| oflags = O_WRITE | O_CREAT | O_TRUNC; | |||
| break; | |||
| default: | |||
| goto fail; | |||
| } | |||
| while (*mode) { | |||
| switch (*mode++) { | |||
| case '+': | |||
| m_flags |= F_SRW; | |||
| oflags |= O_RDWR; | |||
| break; | |||
| case 'b': | |||
| break; | |||
| case 'x': | |||
| oflags |= O_EXCL; | |||
| break; | |||
| default: | |||
| goto fail; | |||
| } | |||
| } | |||
| if ((oflags & O_EXCL) && !(oflags & O_WRITE)) goto fail; | |||
| if (!SdBaseFile::open(filename, oflags)) goto fail; | |||
| m_r = 0; | |||
| m_w = 0; | |||
| m_p = m_buf; | |||
| return true; | |||
| fail: | |||
| m_flags = 0; | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::fputs(const char* str) { | |||
| size_t len = strlen(str); | |||
| return fwrite(str, 1, len) == len ? len : EOF; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::fputs_P(PGM_P str) { | |||
| PGM_P bgn = str; | |||
| for (char c; (c = pgm_read_byte(str)); str++) { | |||
| if (putc(c) < 0) return EOF; | |||
| } | |||
| return str - bgn; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| size_t StdioStream::fread(void* ptr, size_t size, size_t count) { | |||
| uint8_t* dst = reinterpret_cast<uint8_t*>(ptr); | |||
| size_t total = size*count; | |||
| if (total == 0) return 0; | |||
| size_t need = total; | |||
| while (need > m_r) { | |||
| memcpy(dst, m_p, m_r); | |||
| dst += m_r; | |||
| m_p += m_r; | |||
| need -= m_r; | |||
| if (!fillBuf()) { | |||
| return (total - need)/size; | |||
| } | |||
| } | |||
| memcpy(dst, m_p, need); | |||
| m_r -= need; | |||
| m_p += need; | |||
| return count; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::fseek(int32_t offset, int origin) { | |||
| int32_t pos; | |||
| if (m_flags & F_SWR) { | |||
| if (!flushBuf()) { | |||
| goto fail; | |||
| } | |||
| } | |||
| switch (origin) { | |||
| case SEEK_CUR: | |||
| pos = ftell(); | |||
| if (pos < 0) { | |||
| goto fail; | |||
| } | |||
| pos += offset; | |||
| if (!SdBaseFile::seekCur(pos)) { | |||
| goto fail; | |||
| } | |||
| break; | |||
| case SEEK_SET: | |||
| if (!SdBaseFile::seekSet(offset)) { | |||
| goto fail; | |||
| } | |||
| break; | |||
| case SEEK_END: | |||
| if (!SdBaseFile::seekEnd(offset)) { | |||
| goto fail; | |||
| } | |||
| break; | |||
| default: | |||
| goto fail; | |||
| } | |||
| m_r = 0; | |||
| m_p = m_buf; | |||
| return 0; | |||
| fail: | |||
| return EOF; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int32_t StdioStream::ftell() { | |||
| uint32_t pos = SdBaseFile::curPosition(); | |||
| if (m_flags & F_SRD) { | |||
| if (m_r > pos) return -1L; | |||
| pos -= m_r; | |||
| } else if (m_flags & F_SWR) { | |||
| pos += m_p - m_buf; | |||
| } | |||
| return pos; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) { | |||
| return write(ptr, count*size) < 0 ? EOF : count; | |||
| #if 0 //////////////////////////////////////////////////////////////////////////////////// | |||
| const uint8_t* src = static_cast<const uint8_t*>(ptr); | |||
| size_t total = count*size; | |||
| if (total == 0) return 0; | |||
| size_t todo = total; | |||
| while (todo > m_w) { | |||
| memcpy(m_p, src, m_w); | |||
| m_p += m_w; | |||
| src += m_w; | |||
| todo -= m_w; | |||
| if (!flushBuf()) { | |||
| return (total - todo)/size; | |||
| } | |||
| } | |||
| memcpy(m_p, src, todo); | |||
| m_p += todo; | |||
| m_w -= todo; | |||
| return count; | |||
| #endif ////////////////////////////////////////////////////////////////////////////////// | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::write(const void* buf, size_t count) { | |||
| const uint8_t* src = static_cast<const uint8_t*>(buf); | |||
| size_t todo = count; | |||
| while (todo > m_w) { | |||
| memcpy(m_p, src, m_w); | |||
| m_p += m_w; | |||
| src += m_w; | |||
| todo -= m_w; | |||
| if (!flushBuf()) return EOF; | |||
| } | |||
| memcpy(m_p, src, todo); | |||
| m_p += todo; | |||
| m_w -= todo; | |||
| return count; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| size_t StdioStream::print(const __FlashStringHelper *str) { | |||
| const char PROGMEM *p = (const char PROGMEM *)str; | |||
| uint8_t c; | |||
| while (c = pgm_read_byte(p)) { | |||
| if (putc(c) < 0) return 0; | |||
| p++; | |||
| } | |||
| return p - (const char PROGMEM *)str; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printDec(float value, uint8_t prec) { | |||
| #define FLOAT_NEW_WAY | |||
| #ifdef FLOAT_NEW_WAY | |||
| char buf[24]; | |||
| char *ptr = fmtFloat(value, buf + sizeof(buf), prec); | |||
| // return fputs(ptr); | |||
| // uint8_t len = buf + sizeof(buf) - ptr; | |||
| return write(ptr, buf + sizeof(buf) - ptr); | |||
| #else | |||
| char* ptr; | |||
| uint8_t rtn = 0; | |||
| uint8_t sign = 0; | |||
| if (value < 0) { | |||
| value = -value; | |||
| sign = '-'; | |||
| } | |||
| // check for NaN INF OVF | |||
| if (isnan(value)) { | |||
| if (fputs_P(PSTR("nan")) < 0) return -1; | |||
| rtn += 3; | |||
| } else if (isinf(value)) { | |||
| if (fputs_P(PSTR("inf")) < 0) return -1; | |||
| rtn += 3; | |||
| } else if (value > 4294967040.0) { | |||
| if (fputs_P(PSTR("ovf")) < 0) return -1;; | |||
| rtn += 3; | |||
| } else { | |||
| if (sign) { | |||
| if (putc(sign) < 0) return -1; | |||
| rtn++; | |||
| } | |||
| if (prec > 9) prec = 9; | |||
| /* | |||
| uint32_t s = 1; | |||
| for (uint8_t i = 0; i < prec; i++) { | |||
| // s *= 10; | |||
| s = ((s << 2) + s) << 1; | |||
| } | |||
| // round value | |||
| value += 0.5/s; | |||
| */ | |||
| value += scale10(0.5, -prec); | |||
| uint32_t whole = value; | |||
| int np; | |||
| if ((np = printDec(whole)) < 0) return -1; | |||
| rtn += np; | |||
| if (prec) { | |||
| if (putc('.') < 0) return -1; | |||
| char* str = fmtSpace(prec); | |||
| if (!str) return -1; | |||
| char* tmp = str - prec; | |||
| // uint32_t fraction = s*(value - whole); | |||
| uint32_t fraction = scale10(value - whole, prec); | |||
| ptr = fmtDec(fraction, str); | |||
| while (ptr > tmp) *--ptr = '0'; | |||
| rtn += prec + 1; | |||
| } | |||
| } | |||
| return rtn; | |||
| #endif | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printDec(signed char n) { | |||
| uint8_t s = 0; | |||
| if (n < 0) { | |||
| if (fputc('-') < 0) return -1; | |||
| n = -n; | |||
| s = 1; | |||
| } | |||
| printDec((unsigned char)n); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printDec(int16_t n) { | |||
| int s; | |||
| uint8_t rtn = 0; | |||
| if (n < 0) { | |||
| if (fputc('-') < 0) return -1; | |||
| n = -n; | |||
| rtn++; | |||
| } | |||
| if ((s = printDec((uint16_t)n)) < 0) return s; | |||
| return rtn; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printDec(uint16_t n) { | |||
| #define NEW_WAY | |||
| #ifdef NEW_WAY | |||
| char buf[5]; | |||
| char *ptr = fmtDec(n, buf + sizeof(buf)); | |||
| uint8_t len = buf + sizeof(buf) - ptr; | |||
| return write(ptr, len); | |||
| #else | |||
| uint8_t len; | |||
| if (n < 100) { | |||
| len = n < 10 ? 1 : 2; | |||
| } else { | |||
| len = n < 1000 ? 3 : n < 10000 ? 4 : 5; | |||
| } | |||
| char* str = fmtSpace(len); | |||
| if (!str) return -1; | |||
| fmtDec(n, str); | |||
| return len; | |||
| #endif | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printDec(int32_t n) { | |||
| uint8_t s = 0; | |||
| if (n < 0) { | |||
| if (fputc('-') < 0) return -1; | |||
| n = -n; | |||
| s = 1; | |||
| } | |||
| int rtn = printDec((uint32_t)n); | |||
| return rtn > 0 ? rtn + s : -1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printDec(uint32_t n) { | |||
| #ifdef NEW_WAY | |||
| char buf[10]; | |||
| char *ptr = fmtDec(n, buf + sizeof(buf)); | |||
| uint8_t len = buf + sizeof(buf) - ptr; | |||
| return write(ptr, len); | |||
| #else | |||
| uint8_t len; | |||
| if (n < 0X10000) { | |||
| return printDec((uint16_t)n); | |||
| } | |||
| if (n < 10000000) { | |||
| len = n < 100000 ? 5 : n < 1000000 ? 6 : 7; | |||
| } else { | |||
| len = n < 100000000 ? 8 : n < 1000000000 ? 9 : 10; | |||
| } | |||
| char* str = fmtSpace(len); | |||
| if (!str) return -1; | |||
| fmtDec(n, str); | |||
| return len; | |||
| #endif | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::printHex(uint32_t n) { | |||
| #ifdef NEW_WAY | |||
| char buf[8]; | |||
| char *ptr = fmtHex(n, buf + sizeof(buf)); | |||
| uint8_t len = buf + sizeof(buf) - ptr; | |||
| return write(ptr, len); | |||
| #else | |||
| size_t len; | |||
| if (n < 0X10000) { | |||
| len = n < 0X10 ? 1 : n < 0X100 ? 2 : n < 0X1000 ? 3 : 4; | |||
| } else { | |||
| len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8; | |||
| } | |||
| char* str = fmtSpace(len); | |||
| if (!str) return -1; | |||
| do { | |||
| uint8_t h = n & 0XF; | |||
| *str-- = h + (h < 10 ? '0' : 'A' - 10); | |||
| n >>= 4; | |||
| } while (n); | |||
| return len; | |||
| #endif | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool StdioStream::rewind() { | |||
| if (m_flags & F_SWR) { | |||
| if (!flushBuf()) return false; | |||
| } | |||
| SdBaseFile::seekSet(0); | |||
| m_r = 0; | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::ungetc(int c) { | |||
| // error if EOF. | |||
| if (c == EOF) return EOF; | |||
| // error if not reading. | |||
| if ((m_flags & F_SRD) == 0) return EOF; | |||
| // error if no space. | |||
| if (m_p == m_buf) return EOF; | |||
| m_r++; | |||
| m_flags &= ~F_EOF; | |||
| return *--m_p = (uint8_t)c; | |||
| } | |||
| //============================================================================== | |||
| // private | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::fillGet() { | |||
| if (!fillBuf()) { | |||
| return EOF; | |||
| } | |||
| m_r--; | |||
| return *m_p++; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // private | |||
| bool StdioStream::fillBuf() { | |||
| if (!(m_flags & F_SRD)) { /////////////check for F_ERR and F_EOF ??///////////////// | |||
| if (!(m_flags & F_SRW)) { | |||
| m_flags |= F_ERR; | |||
| return false; | |||
| } | |||
| if (m_flags & F_SWR) { | |||
| if (!flushBuf()) { | |||
| return false; | |||
| } | |||
| m_flags &= ~F_SWR; | |||
| m_flags |= F_SRD; | |||
| m_w = 0; | |||
| } | |||
| } | |||
| m_p = m_buf + UNGETC_BUF_SIZE; | |||
| int nr = SdBaseFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE); | |||
| if (nr <= 0) { | |||
| m_flags |= nr < 0 ? F_ERR : F_EOF; | |||
| m_r = 0; | |||
| return false; | |||
| } | |||
| m_r = nr; | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // private | |||
| bool StdioStream::flushBuf() { | |||
| if (!(m_flags & F_SWR)) { /////////////////check for F_ERR ??//////////////////////// | |||
| if (!(m_flags & F_SRW)) { | |||
| m_flags |= F_ERR; | |||
| return false; | |||
| } | |||
| m_flags &= ~F_SRD; | |||
| m_flags |= F_SWR; | |||
| m_r = 0; | |||
| m_w = sizeof(m_buf); | |||
| m_p = m_buf; | |||
| return true; | |||
| } | |||
| uint8_t n = m_p - m_buf; | |||
| m_p = m_buf; | |||
| m_w = sizeof(m_buf); | |||
| if (SdBaseFile::write(m_buf, n) == n) return true; | |||
| m_flags |= F_ERR; | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int StdioStream::flushPut(uint8_t c) { | |||
| if (!flushBuf()) return EOF; | |||
| m_w--; | |||
| return *m_p++ = c; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| char* StdioStream::fmtSpace(uint8_t len) { | |||
| if (m_w < len) { | |||
| if (!flushBuf() || m_w < len) { | |||
| return 0; | |||
| } | |||
| } | |||
| if (len > m_w) return 0; | |||
| m_p += len; | |||
| m_w -= len; | |||
| return reinterpret_cast<char*>(m_p); | |||
| } | |||
| @@ -0,0 +1,656 @@ | |||
| /* Arduino RamDisk Library | |||
| * Copyright (C) 2014 by William Greiman | |||
| * | |||
| * This file is part of the Arduino RamDisk Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino RamDisk Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef StdioStream_h | |||
| #define StdioStream_h | |||
| /** | |||
| * \file | |||
| * StdioStream class | |||
| */ | |||
| #include <limits.h> | |||
| #include <Arduino.h> | |||
| #include <SdFat.h> | |||
| #include <SdBaseFile.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** Total size of stream buffer. The entire buffer is used for output. | |||
| * During input UNGETC_BUF_SIZE of this space is reserved for ungetc. | |||
| */ | |||
| const uint8_t STREAM_BUF_SIZE = 64; | |||
| /** Amount of buffer allocated for ungetc during input. */ | |||
| const uint8_t UNGETC_BUF_SIZE = 2; | |||
| //------------------------------------------------------------------------------ | |||
| // Get rid of any macros defined in <stdio.h>. | |||
| #include <stdio.h> | |||
| #undef clearerr | |||
| #undef fclose | |||
| #undef feof | |||
| #undef ferror | |||
| #undef fflush | |||
| #undef fgetc | |||
| #undef fgetpos | |||
| #undef fgets | |||
| #undef fopen | |||
| #undef fprintf | |||
| #undef fputc | |||
| #undef fputs | |||
| #undef fread | |||
| #undef freopen | |||
| #undef fscanf | |||
| #undef fseek | |||
| #undef fsetpos | |||
| #undef ftell | |||
| #undef fwrite | |||
| #undef getc | |||
| #undef getchar | |||
| #undef gets | |||
| #undef perror | |||
| #undef printf | |||
| #undef putc | |||
| #undef putchar | |||
| #undef puts | |||
| #undef remove | |||
| #undef rename | |||
| #undef rewind | |||
| #undef scanf | |||
| #undef setbuf | |||
| #undef setvbuf | |||
| #undef sprintf // NOLINT | |||
| #undef sscanf | |||
| #undef tmpfile | |||
| #undef tmpnam | |||
| #undef ungetc | |||
| #undef vfprintf | |||
| #undef vprintf | |||
| #undef vsprintf | |||
| // make sure needed macros are defined | |||
| #ifndef EOF | |||
| /** End-of-file return value. */ | |||
| #define EOF (-1) | |||
| #endif // EOF | |||
| #ifndef NULL | |||
| /** Null pointer */ | |||
| #define NULL 0 | |||
| #endif // NULL | |||
| #ifndef SEEK_CUR | |||
| /** Seek relative to current position. */ | |||
| #define SEEK_CUR 1 | |||
| #endif // SEEK_CUR | |||
| #ifndef SEEK_END | |||
| /** Seek relative to end-of-file. */ | |||
| #define SEEK_END 2 | |||
| #endif // SEEK_END | |||
| #ifndef SEEK_SET | |||
| /** Seek relative to start-of-file. */ | |||
| #define SEEK_SET 0 | |||
| #endif // SEEK_SET | |||
| //------------------------------------------------------------------------------ | |||
| /** \class StdioStream | |||
| * \brief StdioStream implements a minimal stdio stream. | |||
| * | |||
| * StdioStream does not support subdirectories or long file names. | |||
| */ | |||
| class StdioStream : private SdBaseFile { | |||
| public: | |||
| /** Constructor | |||
| * | |||
| */ | |||
| StdioStream() { | |||
| m_w = m_r = 0; | |||
| m_p = m_buf; | |||
| m_flags = 0; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Clear the stream's end-of-file and error indicators. */ | |||
| void clearerr() {m_flags &= ~(F_ERR | F_EOF);} | |||
| //---------------------------------------------------------------------------- | |||
| /** Close a stream. | |||
| * | |||
| * A successful call to the fclose function causes the stream to be | |||
| * flushed and the associated file to be closed. Any unwritten buffered | |||
| * data is written to the file; any unread buffered data is discarded. | |||
| * Whether or not the call succeeds, the stream is disassociated from | |||
| * the file. | |||
| * | |||
| * \return zero if the stream was successfully closed, or EOF if any any | |||
| * errors are detected. | |||
| */ | |||
| int fclose(); | |||
| //---------------------------------------------------------------------------- | |||
| /** Test the stream's end-of-file indicator. | |||
| * \return non-zero if and only if the end-of-file indicator is set. | |||
| */ | |||
| int feof() {return (m_flags & F_EOF) != 0;} | |||
| //---------------------------------------------------------------------------- | |||
| /** Test the stream's error indicator. | |||
| * \return return non-zero if and only if the error indicator is set. | |||
| */ | |||
| int ferror() {return (m_flags & F_ERR) != 0;} | |||
| //---------------------------------------------------------------------------- | |||
| /** Flush the stream. | |||
| * | |||
| * If stream is an output stream or an update stream in which the most | |||
| * recent operation was not input, any unwritten data is written to the | |||
| * file; otherwise the call is an error since any buffered input data | |||
| * would be lost. | |||
| * | |||
| * \return sets the error indicator for the stream and returns EOF if an | |||
| * error occurs, otherwise it returns zero. | |||
| */ | |||
| int fflush(); | |||
| //---------------------------------------------------------------------------- | |||
| /** Get a byte from the stream. | |||
| * | |||
| * \return If the end-of-file indicator for the stream is set, or if the | |||
| * stream is at end-of-file, the end-of-file indicator for the stream is | |||
| * set and the fgetc function returns EOF. Otherwise, the fgetc function | |||
| * returns the next character from the input stream. | |||
| */ | |||
| int fgetc() {return m_r-- == 0 ? fillGet() : *m_p++;} | |||
| //---------------------------------------------------------------------------- | |||
| /** Get a string from a stream. | |||
| * | |||
| * The fgets function reads at most one less than the number of | |||
| * characters specified by num from the stream into the array pointed | |||
| * to by str. No additional characters are read after a new-line | |||
| * character (which is retained) or after end-of-file. A null character | |||
| * is written immediately after the last character read into the array. | |||
| * | |||
| * \param[out] str Pointer to an array of where the string is copied. | |||
| * | |||
| * \param[in] num Maximum number of characters including the null | |||
| * character. | |||
| * | |||
| * \param[out] len If len is not null and fgets is successful, the | |||
| * length of the string is returned. | |||
| * | |||
| * \return str if successful. If end-of-file is encountered and no | |||
| * characters have been read into the array, the contents of the array | |||
| * remain unchanged and a null pointer is returned. If a read error | |||
| * occurs during the operation, the array contents are indeterminate | |||
| * and a null pointer is returned. | |||
| */ | |||
| char* fgets(char* str, int num, size_t* len = 0); | |||
| //---------------------------------------------------------------------------- | |||
| /** Open a stream. | |||
| * | |||
| * Open a file and associates the stream with it. | |||
| * | |||
| * \param[in] filename name of the file to be opened. | |||
| * | |||
| * \param[in] mode a string that indicates the open mode. | |||
| * | |||
| * <table> | |||
| * <tr> | |||
| * <td>"r" or "rb"</td> | |||
| * <td>Open a file for reading. The file must exist.</td> | |||
| * </tr> | |||
| * <tr> | |||
| * <td>"w" or "wb"</td> | |||
| * <td>Truncate an existing to zero length or create an empty file | |||
| * for writing.</td> | |||
| * </tr> | |||
| * <tr> | |||
| * <td>"wx" or "wbx"</td> | |||
| * <td>Create a file for writing. Fails if the file already exists.</td> | |||
| * </tr> | |||
| * <tr> | |||
| * <td>"a" or "ab"</td> | |||
| * <td>Append; open or create file for writing at end-of-file.</td> | |||
| * </tr> | |||
| * <tr> | |||
| * <td>"r+" or "rb+" or "r+b"</td> | |||
| * <td>Open a file for update (reading and writing).</td> | |||
| * </tr> | |||
| * <tr> | |||
| * <td>"w+" or "w+b" or "wb+"</td> | |||
| * <td>Truncate an existing to zero length or create a file for update.</td> | |||
| * </tr> | |||
| * <tr> | |||
| * <td>"w+x" or "w+bx" or "wb+x"</td> | |||
| * <td>Create a file for update. Fails if the file already exists.</td> | |||
| * </tr> | |||
| * <tr> | |||
| * <td>"a+" or "a+b" or "ab+"</td> | |||
| * <td>Append; open or create a file for update, writing at end-of-file.</td> | |||
| * </tr> | |||
| * </table> | |||
| * The character 'b' shall have no effect, but is allowed for ISO C | |||
| * standard conformance. | |||
| * | |||
| * Opening a file with append mode causes all subsequent writes to the | |||
| * file to be forced to the then current end-of-file, regardless of | |||
| * intervening calls to the fseek function. | |||
| * | |||
| * When a file is opened with update mode, both input and output may be | |||
| * performed on the associated stream. However, output shall not be | |||
| * directly followed by input without an intervening call to the fflush | |||
| * function or to a file positioning function (fseek, or rewind), and | |||
| * input shall not be directly followed by output without an intervening | |||
| * call to a file positioning function, unless the input operation | |||
| * encounters endof-file. | |||
| * | |||
| * \return true for success or false for failure. | |||
| */ | |||
| bool fopen(const char* filename, const char * mode); | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a byte to a stream. | |||
| * | |||
| * \param[in] c the byte to be written (converted to an unsigned char). | |||
| * | |||
| * \return Upon successful completion, fputc() returns the value it | |||
| * has written. Otherwise, it returns EOF and sets the error indicator for | |||
| * the stream. | |||
| */ | |||
| int fputc(int c) {return m_w-- == 0 ? flushPut(c) : *m_p++ = c;} | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a string to a stream. | |||
| * | |||
| * \param[in] str a pointer to the string to be written. | |||
| * | |||
| * \return for success, fputs() returns a non-negative | |||
| * number. Otherwise, it returns EOF and sets the error indicator for | |||
| * the stream. | |||
| */ | |||
| int fputs(const char* str); | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a string stored in flash. | |||
| * | |||
| * \param[in] str string to be written. | |||
| * | |||
| * \return for success, fputs() returns a non-negative | |||
| * number. Otherwise, it returns EOF and sets the error indicator for | |||
| * the stream. | |||
| */ | |||
| int fputs_P(PGM_P str); | |||
| //---------------------------------------------------------------------------- | |||
| /** Binary input. | |||
| * | |||
| * Reads an array of up to count elements, each one with a size of size | |||
| * bytes. | |||
| * \param[out] ptr pointer to area of at least (size*count) bytes where | |||
| * the data will be stored. | |||
| * | |||
| * \param[in] size the size, in bytes, of each element to be read. | |||
| * | |||
| * \param[in] count the number of elements to be read. | |||
| * | |||
| * \return number of elements successfully read, which may be less than | |||
| * count if a read error or end-of-file is encountered. If size or count | |||
| * is zero, fread returns zero and the contents of the array and the | |||
| * state of the stream remain unchanged. | |||
| */ | |||
| size_t fread(void* ptr, size_t size, size_t count); | |||
| //---------------------------------------------------------------------------- | |||
| /** Set the file position for the stream. | |||
| * | |||
| * \param[in] offset number of offset from the origin. | |||
| * | |||
| * \param[in] origin position used as reference for the offset. It is | |||
| * specified by one of the following constants. | |||
| * | |||
| * SEEK_SET - Beginning of file. | |||
| * | |||
| * SEEK_CUR - Current position of the file pointer. | |||
| * | |||
| * SEEK_END - End of file. | |||
| * | |||
| * \return zero for success. Otherwise, it returns non-zero and sets the | |||
| * error indicator for the stream. | |||
| */ | |||
| int fseek(int32_t offset, int origin); | |||
| //---------------------------------------------------------------------------- | |||
| /** Get the current position in a stream. | |||
| * | |||
| * \return If successful, ftell return the current value of the position | |||
| * indicator. On failure, ftell returns −1L. | |||
| */ | |||
| int32_t ftell(); | |||
| //---------------------------------------------------------------------------- | |||
| /** Binary output. | |||
| * | |||
| * Writes an array of up to count elements, each one with a size of size | |||
| * bytes. | |||
| * \param[in] ptr pointer to (size*count) bytes of data to be written. | |||
| * | |||
| * \param[in] size the size, in bytes, of each element to be written. | |||
| * | |||
| * \param[in] count the number of elements to be written. | |||
| * | |||
| * \return number of elements successfully written. if this number is | |||
| * less than count, an error has occured. If size or count is zero, | |||
| * fwrite returns zero. | |||
| */ | |||
| size_t fwrite(const void * ptr, size_t size, size_t count); | |||
| //---------------------------------------------------------------------------- | |||
| /** Get a byte from the stream. | |||
| * | |||
| * getc and fgetc are equivalent but getc is inline so it is faster but | |||
| * require more flash memory. | |||
| * | |||
| * \return If the end-of-file indicator for the stream is set, or if the | |||
| * stream is at end-of-file, the end-of-file indicator for the stream is | |||
| * set and the fgetc function returns EOF. Otherwise, the fgetc function | |||
| * returns the next character from the input stream. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| int getc() {return m_r-- == 0 ? fillGet() : *m_p++;} | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a byte to a stream. | |||
| * | |||
| * putc and fputc are equivalent but putc is inline so it is faster but | |||
| * require more flash memory. | |||
| * | |||
| * \param[in] c the byte to be written (converted to an unsigned char). | |||
| * | |||
| * \return Upon successful completion, fputc() returns the value it | |||
| * has written. Otherwise, it returns EOF and sets the error indicator for | |||
| * the stream. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| int putc(int c) {return m_w-- == 0 ? flushPut(c) : *m_p++ = c;} | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a CR/LF. | |||
| * | |||
| * \return two, the number of bytes written, for success or -1 for failure. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| int putCRLF() { | |||
| if (m_w < 2) { | |||
| if (!flushBuf()) return -1; | |||
| } | |||
| *m_p++ = '\r'; | |||
| *m_p++ = '\n'; | |||
| m_w -= 2; | |||
| return 2; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a character. | |||
| * \param[in] c the character to write. | |||
| * \return the number of bytes written. | |||
| */ | |||
| size_t print(char c) { | |||
| return putc(c) < 0 ? 0 : 1; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a string. | |||
| * | |||
| * \param[in] str the string to be written. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| size_t print(const char* str) { | |||
| int n = fputs(str); | |||
| return n < 0 ? 0 : n; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a string stored in flash memory. | |||
| * | |||
| * \param[in] str the string to print. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| size_t print(const __FlashStringHelper *str); | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a floating point number. | |||
| * | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * | |||
| * \param[in] val the number to be printed. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| size_t print(double val, uint8_t prec = 2) { | |||
| return print(static_cast<float>(val), prec); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a floating point number. | |||
| * | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * | |||
| * \param[in] val the number to be printed. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| size_t print(float val, uint8_t prec = 2) { | |||
| int n = printDec(val, prec); | |||
| return n > 0 ? n : 0; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a number. | |||
| * | |||
| * \param[in] val the number to be printed. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| template <typename T> | |||
| size_t print(T val) { | |||
| int n = printDec(val); | |||
| return n > 0 ? n : 0; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Write a CR/LF. | |||
| * | |||
| * \return two, the number of bytes written, for success or zero for failure. | |||
| */ | |||
| size_t println() { | |||
| return putCRLF() > 0 ? 2 : 0; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a floating point number followed by CR/LF. | |||
| * | |||
| * \param[in] val the number to be printed. | |||
| * | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| size_t println(double val, uint8_t prec = 2) { | |||
| return println(static_cast<float>(val), prec); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a floating point number followed by CR/LF. | |||
| * | |||
| * \param[in] val the number to be printed. | |||
| * | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| size_t println(float val, uint8_t prec = 2) { | |||
| int n = printDec(val, prec); | |||
| return n > 0 && putCRLF() > 0 ? n + 2 : 0; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print an item followed by CR/LF | |||
| * | |||
| * \param[in] val the item to be printed. | |||
| * | |||
| * \return the number of bytes written. | |||
| */ | |||
| template <typename T> | |||
| size_t println(T val) { | |||
| int n = print(val); | |||
| return putCRLF() > 0 ? n + 2 : 0; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a char as a number. | |||
| * \param[in] n number to be printed. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(char n) { | |||
| if (CHAR_MIN == 0) { | |||
| return printDec((unsigned char)n); | |||
| } else { | |||
| return printDec((signed char)n); | |||
| } | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** print a signed 8-bit integer | |||
| * \param[in] n number to be printed. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(signed char n); | |||
| //---------------------------------------------------------------------------- | |||
| /** Print an unsigned 8-bit number. | |||
| * \param[in] n number to be print. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(unsigned char n) { | |||
| return printDec((uint16_t)n); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a int16_t | |||
| * \param[in] n number to be printed. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(int16_t n); | |||
| //---------------------------------------------------------------------------- | |||
| /** print a uint16_t. | |||
| * \param[in] n number to be printed. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(uint16_t n); | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a signed 32-bit integer. | |||
| * \param[in] n number to be printed. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(int32_t n); | |||
| //---------------------------------------------------------------------------- | |||
| /** Write an unsigned 32-bit number. | |||
| * \param[in] n number to be printed. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(uint32_t n); | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a double. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(double value, uint8_t prec) { | |||
| return printDec(static_cast<float>(value), prec); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a float. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printDec(float value, uint8_t prec); | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] term The field terminator. | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printField(double value, char term, uint8_t prec = 2) { | |||
| return printField(static_cast<float>(value), term, prec) > 0; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] term The field terminator. | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printField(float value, char term, uint8_t prec = 2) { | |||
| int rtn = printDec(value, prec); | |||
| return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] term The field terminator. | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| template <typename T> | |||
| int printField(T value, char term) { | |||
| int rtn = printDec(value); | |||
| return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Print HEX | |||
| * \param[in] n number to be printed as HEX. | |||
| * | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printHex(uint32_t n); | |||
| //---------------------------------------------------------------------------- | |||
| /** Print HEX with CRLF | |||
| * \param[in] n number to be printed as HEX. | |||
| * | |||
| * \return The number of bytes written or -1 if an error occurs. | |||
| */ | |||
| int printHexln(uint32_t n) { | |||
| int rtn = printHex(n); | |||
| return rtn < 0 || putCRLF() != 2 ? -1 : rtn + 2; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Set position of a stream to the beginning. | |||
| * | |||
| * The rewind function sets the file position to the beginning of the | |||
| * file. It is equivalent to fseek(0L, SEEK_SET) except that the error | |||
| * indicator for the stream is also cleared. | |||
| * | |||
| * \return true for success or false for failure. | |||
| */ | |||
| bool rewind(); | |||
| //---------------------------------------------------------------------------- | |||
| /** Push a byte back into an input stream. | |||
| * | |||
| * \param[in] c the byte (converted to an unsigned char) to be pushed back. | |||
| * | |||
| * One character of pushback is guaranteed. If the ungetc function is | |||
| * called too many times without an intervening read or file positioning | |||
| * operation on that stream, the operation may fail. | |||
| * | |||
| * A successful intervening call to a file positioning function (fseek, | |||
| * fsetpos, or rewind) discards any pushed-back characters for the stream. | |||
| * | |||
| * \return Upon successful completion, ungetc() returns the byte pushed | |||
| * back after conversion. Otherwise it returns EOF. | |||
| */ | |||
| int ungetc(int c); | |||
| //============================================================================ | |||
| private: | |||
| bool fillBuf(); | |||
| int fillGet(); | |||
| bool flushBuf(); | |||
| int flushPut(uint8_t c); | |||
| char* fmtSpace(uint8_t len); | |||
| int write(const void* buf, size_t count); | |||
| //---------------------------------------------------------------------------- | |||
| // F_SRD and F_WR are never simultaneously asserted | |||
| static const uint8_t F_SRD = 0x01; // OK to read | |||
| static const uint8_t F_SWR = 0x02; // OK to write | |||
| static const uint8_t F_SRW = 0x04; // open for reading & writing | |||
| static const uint8_t F_EOF = 0x10; // found EOF | |||
| static const uint8_t F_ERR = 0x20; // found error | |||
| //---------------------------------------------------------------------------- | |||
| uint8_t m_flags; | |||
| uint8_t* m_p; | |||
| uint8_t m_r; | |||
| uint8_t m_w; | |||
| uint8_t m_buf[STREAM_BUF_SIZE]; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| #endif // StdioStream_h | |||
| @@ -0,0 +1,146 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef bufstream_h | |||
| #define bufstream_h | |||
| /** | |||
| * \file | |||
| * \brief \ref ibufstream and \ref obufstream classes | |||
| */ | |||
| #include <iostream.h> | |||
| //============================================================================== | |||
| /** | |||
| * \class ibufstream | |||
| * \brief parse a char string | |||
| */ | |||
| class ibufstream : public istream { | |||
| public: | |||
| /** Constructor */ | |||
| ibufstream() : m_buf(0), m_len(0) {} | |||
| /** Constructor | |||
| * \param[in] str pointer to string to be parsed | |||
| * Warning: The string will not be copied so must stay in scope. | |||
| */ | |||
| explicit ibufstream(const char* str) { | |||
| init(str); | |||
| } | |||
| /** Initialize an ibufstream | |||
| * \param[in] str pointer to string to be parsed | |||
| * Warning: The string will not be copied so must stay in scope. | |||
| */ | |||
| void init(const char* str) { | |||
| m_buf = str; | |||
| m_len = strlen(m_buf); | |||
| m_pos = 0; | |||
| clear(); | |||
| } | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| int16_t getch() { | |||
| if (m_pos < m_len) return m_buf[m_pos++]; | |||
| setstate(eofbit); | |||
| return -1; | |||
| } | |||
| void getpos(FatPos_t *pos) { | |||
| pos->position = m_pos; | |||
| } | |||
| bool seekoff(off_type off, seekdir way) {return false;} | |||
| bool seekpos(pos_type pos) { | |||
| if (pos < m_len) { | |||
| m_pos = pos; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void setpos(FatPos_t *pos) { | |||
| m_pos = pos->position; | |||
| } | |||
| pos_type tellpos() { | |||
| return m_pos; | |||
| } | |||
| /// @endcond | |||
| private: | |||
| const char* m_buf; | |||
| size_t m_len; | |||
| size_t m_pos; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| * \class obufstream | |||
| * \brief format a char string | |||
| */ | |||
| class obufstream : public ostream { | |||
| public: | |||
| /** constructor */ | |||
| obufstream() : m_in(0) {} | |||
| /** Constructor | |||
| * \param[in] buf buffer for formatted string | |||
| * \param[in] size buffer size | |||
| */ | |||
| obufstream(char *buf, size_t size) { | |||
| init(buf, size); | |||
| } | |||
| /** Initialize an obufstream | |||
| * \param[in] buf buffer for formatted string | |||
| * \param[in] size buffer size | |||
| */ | |||
| void init(char *buf, size_t size) { | |||
| m_buf = buf; | |||
| buf[0] = '\0'; | |||
| m_size = size; | |||
| m_in = 0; | |||
| } | |||
| /** \return a pointer to the buffer */ | |||
| char* buf() {return m_buf;} | |||
| /** \return the length of the formatted string */ | |||
| size_t length() {return m_in;} | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| void putch(char c) { | |||
| if (m_in >= (m_size - 1)) { | |||
| setstate(badbit); | |||
| return; | |||
| } | |||
| m_buf[m_in++] = c; | |||
| m_buf[m_in]= '\0'; | |||
| } | |||
| void putstr(const char *str) { | |||
| while (*str) putch(*str++); | |||
| } | |||
| bool seekoff(off_type off, seekdir way) {return false;} | |||
| bool seekpos(pos_type pos) { | |||
| if (pos > m_in) return false; | |||
| m_in = pos; | |||
| m_buf[m_in] = '\0'; | |||
| return true; | |||
| } | |||
| bool sync() {return true;} | |||
| pos_type tellpos() { | |||
| return m_in; | |||
| } | |||
| /// @endcond | |||
| private: | |||
| char *m_buf; | |||
| size_t m_size; | |||
| size_t m_in; | |||
| }; | |||
| #endif // bufstream_h | |||
| @@ -0,0 +1,172 @@ | |||
| // A simple data logger for the Arduino analog pins with optional DS1307 | |||
| // uses RTClib from https://github.com/adafruit/RTClib | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> // define FreeRam() | |||
| #define SD_CHIP_SELECT SS // SD chip select pin | |||
| #define USE_DS1307 0 // set nonzero to use DS1307 RTC | |||
| #define LOG_INTERVAL 1000 // mills between entries | |||
| #define SENSOR_COUNT 3 // number of analog pins to log | |||
| #define ECHO_TO_SERIAL 1 // echo data to serial port if nonzero | |||
| #define WAIT_TO_START 1 // Wait for serial input in setup() | |||
| #define ADC_DELAY 10 // ADC delay for high impedence sensors | |||
| // file system object | |||
| SdFat sd; | |||
| // text file for logging | |||
| ofstream logfile; | |||
| // Serial print stream | |||
| ArduinoOutStream cout(Serial); | |||
| // buffer to format data - makes it eaiser to echo to Serial | |||
| char buf[80]; | |||
| //------------------------------------------------------------------------------ | |||
| #if SENSOR_COUNT > 6 | |||
| #error SENSOR_COUNT too large | |||
| #endif // SENSOR_COUNT | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| #if USE_DS1307 | |||
| // use RTClib from Adafruit | |||
| // https://github.com/adafruit/RTClib | |||
| // The Arduino IDE has a bug that causes Wire and RTClib to be loaded even | |||
| // if USE_DS1307 is false. | |||
| #error remove this line and uncomment the next two lines. | |||
| //#include <Wire.h> | |||
| //#include <RTClib.h> | |||
| RTC_DS1307 RTC; // define the Real Time Clock object | |||
| //------------------------------------------------------------------------------ | |||
| // call back for file timestamps | |||
| void dateTime(uint16_t* date, uint16_t* time) { | |||
| DateTime now = RTC.now(); | |||
| // return date using FAT_DATE macro to format fields | |||
| *date = FAT_DATE(now.year(), now.month(), now.day()); | |||
| // return time using FAT_TIME macro to format fields | |||
| *time = FAT_TIME(now.hour(), now.minute(), now.second()); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // format date/time | |||
| ostream& operator << (ostream& os, DateTime& dt) { | |||
| os << dt.year() << '/' << int(dt.month()) << '/' << int(dt.day()) << ','; | |||
| os << int(dt.hour()) << ':' << setfill('0') << setw(2) << int(dt.minute()); | |||
| os << ':' << setw(2) << int(dt.second()) << setfill(' '); | |||
| return os; | |||
| } | |||
| #endif // USE_DS1307 | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial){} // wait for Leonardo | |||
| // pstr stores strings in flash to save RAM | |||
| cout << endl << pstr("FreeRam: ") << FreeRam() << endl; | |||
| #if WAIT_TO_START | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| #endif // WAIT_TO_START | |||
| #if USE_DS1307 | |||
| // connect to RTC | |||
| Wire.begin(); | |||
| if (!RTC.begin()) error("RTC failed"); | |||
| // set date time callback function | |||
| SdFile::dateTimeCallback(dateTime); | |||
| DateTime now = RTC.now(); | |||
| cout << now << endl; | |||
| #endif // USE_DS1307 | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // create a new file in root, the current working directory | |||
| char name[] = "LOGGER00.CSV"; | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| name[6] = i/10 + '0'; | |||
| name[7] = i%10 + '0'; | |||
| if (sd.exists(name)) continue; | |||
| logfile.open(name); | |||
| break; | |||
| } | |||
| if (!logfile.is_open()) error("file.open"); | |||
| cout << pstr("Logging to: ") << name << endl; | |||
| cout << pstr("Type any character to stop\n\n"); | |||
| // format header in buffer | |||
| obufstream bout(buf, sizeof(buf)); | |||
| bout << pstr("millis"); | |||
| #if USE_DS1307 | |||
| bout << pstr(",date,time"); | |||
| #endif // USE_DS1307 | |||
| for (uint8_t i = 0; i < SENSOR_COUNT; i++) { | |||
| bout << pstr(",sens") << int(i); | |||
| } | |||
| logfile << buf << endl; | |||
| #if ECHO_TO_SERIAL | |||
| cout << buf << endl; | |||
| #endif // ECHO_TO_SERIAL | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| uint32_t m; | |||
| // wait for time to be a multiple of interval | |||
| do { | |||
| m = millis(); | |||
| } while (m % LOG_INTERVAL); | |||
| // use buffer stream to format line | |||
| obufstream bout(buf, sizeof(buf)); | |||
| // start with time in millis | |||
| bout << m; | |||
| #if USE_DS1307 | |||
| DateTime now = RTC.now(); | |||
| bout << ',' << now; | |||
| #endif // USE_DS1307 | |||
| // read analog pins and format data | |||
| for (uint8_t ia = 0; ia < SENSOR_COUNT; ia++) { | |||
| #if ADC_DELAY | |||
| analogRead(ia); | |||
| delay(ADC_DELAY); | |||
| #endif // ADC_DELAY | |||
| bout << ',' << analogRead(ia); | |||
| } | |||
| bout << endl; | |||
| // log data and flush to SD | |||
| logfile << buf << flush; | |||
| // check for error | |||
| if (!logfile) error("write data failed"); | |||
| #if ECHO_TO_SERIAL | |||
| cout << buf; | |||
| #endif // ECHO_TO_SERIAL | |||
| // don't log two points in the same millis | |||
| if (m == millis()) delay(1); | |||
| if (!Serial.available()) return; | |||
| logfile.close(); | |||
| cout << pstr("Done!"); | |||
| while (1); | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| #include <SdFat.h> | |||
| // create a serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(2000); | |||
| cout << "Hello, World!\n"; | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,18 @@ | |||
| // This example illustrates use of SdFat's | |||
| // minimal unbuffered AVR Serial support. | |||
| // | |||
| // This is useful for debug and saves RAM | |||
| // Will not work on Due, Leonardo, or Teensy | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| #ifndef UDR0 | |||
| #error no AVR serial port0 | |||
| #endif | |||
| void setup() { | |||
| MiniSerial.begin(9600); | |||
| MiniSerial.println(FreeRam()); | |||
| MiniSerial.println(F("Type any Character")); | |||
| while(MiniSerial.read() < 0) {} | |||
| MiniSerial.println(F("Done")); | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,113 @@ | |||
| /* | |||
| * This sketch is a simple Print benchmark. | |||
| */ | |||
| #include <SD.h> | |||
| #include <SPI.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // number of lines to print | |||
| const uint16_t N_PRINT = 20000; | |||
| // test file | |||
| File file; | |||
| //------------------------------------------------------------------------------ | |||
| void error(char* s) { | |||
| Serial.println(s); | |||
| while(1); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) { | |||
| // wait for Leonardo | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| uint32_t maxLatency; | |||
| uint32_t minLatency; | |||
| uint32_t totalLatency; | |||
| while (Serial.read() >= 0) { | |||
| } | |||
| // pstr stores strings in flash to save RAM | |||
| Serial.println(F("Type any character to start")); | |||
| while (Serial.read() <= 0) { | |||
| } | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card | |||
| if (!SD.begin(chipSelect)) error("begin"); | |||
| Serial.println(F("Starting print test. Please wait.\n")); | |||
| // do write test | |||
| for (int test = 0; test < 2; test++) { | |||
| file = SD.open("BENCH.TXT", FILE_WRITE); | |||
| if (!file) error("open failed"); | |||
| switch(test) { | |||
| case 0: | |||
| Serial.println(F("Test of println(uint16_t)")); | |||
| break; | |||
| case 1: | |||
| Serial.println(F("Test of println(double)")); | |||
| break; | |||
| } | |||
| maxLatency = 0; | |||
| minLatency = 999999; | |||
| totalLatency = 0; | |||
| uint32_t t = millis(); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| uint32_t m = micros(); | |||
| switch(test) { | |||
| case 0: | |||
| file.println(i); | |||
| break; | |||
| case 1: | |||
| file.println((double)0.01*i); | |||
| break; | |||
| } | |||
| // if (file.writeError) { | |||
| // error("write failed"); | |||
| // } | |||
| m = micros() - m; | |||
| if (maxLatency < m) maxLatency = m; | |||
| if (minLatency > m) minLatency = m; | |||
| totalLatency += m; | |||
| } | |||
| file.flush(); | |||
| t = millis() - t; | |||
| double s = file.size(); | |||
| Serial.print(F("Time ")); | |||
| Serial.print(0.001*t); | |||
| Serial.println(F(" sec")); | |||
| Serial.print(F("File size ")); | |||
| Serial.print(0.001*s); | |||
| Serial.print(F(" KB\n")); | |||
| Serial.print(F("Write ")); | |||
| Serial.print(s/t); | |||
| Serial.print(F(" KB/sec\n")); | |||
| Serial.print(F("Maximum latency: ")); | |||
| Serial.print(maxLatency); | |||
| Serial.print(F(" usec, Minimum Latency: ")); | |||
| Serial.print(minLatency); | |||
| Serial.print(F(" usec, Avg Latency: ")); | |||
| Serial.print(totalLatency/N_PRINT); | |||
| Serial.println(F(" usec\n")); | |||
| SD.remove("BENCH.TXT"); | |||
| } | |||
| file.close(); | |||
| Serial.println(F("Done!\n")); | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| /* | |||
| * Sketch to compare size of Arduino SD library with SdFat V2. | |||
| * See SdFatSize.pde for SdFat sketch. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SD.h> | |||
| File file; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| if (!SD.begin()) { | |||
| Serial.println("begin failed"); | |||
| return; | |||
| } | |||
| file = SD.open("TEST_SD.TXT", FILE_WRITE); | |||
| file.println("Hello"); | |||
| file.close(); | |||
| Serial.println("Done"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,28 @@ | |||
| /* | |||
| * Sketch to compare size of SdFat V2 with Arduino SD library. | |||
| * See SD_Size.pde for Arduino SD sketch. | |||
| * | |||
| */ | |||
| #include <SdFat.h> | |||
| SdFat sd; | |||
| SdFile file; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| if (!sd.begin()) { | |||
| Serial.println("begin failed"); | |||
| return; | |||
| } | |||
| file.open("SIZE_TST.TXT", O_RDWR | O_CREAT | O_AT_END); | |||
| file.println("Hello"); | |||
| file.close(); | |||
| Serial.println("Done"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,135 @@ | |||
| /* | |||
| * This sketch is a test of subdirectory and file creation. | |||
| * It also tests allocation of clusters to directories. | |||
| * | |||
| * It will create two subdirectories and create enough files | |||
| * to force the allocation of a cluster to each directory. | |||
| * | |||
| * More than 3000 files may be created on a FAT32 volume. | |||
| * | |||
| * Note: Some cards may 'stutter' others just get slow due | |||
| * to the number of flash erases this program causes. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| SdFat sd; | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| /* | |||
| * create enough files to force a cluster to be allocated to dir. | |||
| */ | |||
| void dirAllocTest(SdBaseFile* dir) { | |||
| char buf[13], name[13]; | |||
| SdFile file; | |||
| uint16_t n; | |||
| uint32_t size = dir->fileSize(); | |||
| // create files and write name to file | |||
| for (n = 0; ; n++){ | |||
| // make file name | |||
| sprintf(name, "%u.TXT", n); | |||
| // open start time | |||
| uint32_t t0 = millis(); | |||
| if (!file.open(dir, name, O_WRITE | O_CREAT | O_EXCL)) { | |||
| error("open for write failed"); | |||
| } | |||
| // open end time and write start time | |||
| uint32_t t1 = millis(); | |||
| // write file name to file | |||
| file.print(name); | |||
| if (!file.close()) error("close write"); | |||
| // write end time | |||
| uint32_t t2 = millis(); | |||
| PgmPrint("WR "); | |||
| Serial.print(n); | |||
| Serial.write(' '); | |||
| // print time to create file | |||
| Serial.print(t1 - t0); | |||
| Serial.write(' '); | |||
| // print time to write file | |||
| Serial.println(t2 - t1); | |||
| // directory size will change when a cluster is added | |||
| if (dir->fileSize() != size) break; | |||
| } | |||
| // read files and check content | |||
| for (uint16_t i = 0; i <= n; i++) { | |||
| sprintf(name, "%u.TXT", i); | |||
| // open start time | |||
| uint32_t t0 = millis(); | |||
| if (!file.open(dir, name, O_READ)) { | |||
| error("open for read failed"); | |||
| } | |||
| // open end time and read start time | |||
| uint32_t t1 = millis(); | |||
| int16_t nr = file.read(buf, 13); | |||
| if (nr < 5) error("file.read failed"); | |||
| // read end time | |||
| uint32_t t2 = millis(); | |||
| // check file content | |||
| if (strlen(name) != nr || strncmp(name, buf, nr)) { | |||
| error("content compare failed"); | |||
| } | |||
| if (!file.close()) error("close read failed"); | |||
| PgmPrint("RD "); | |||
| Serial.print(i); | |||
| Serial.write(' '); | |||
| // print open time | |||
| Serial.print(t1 - t0); | |||
| Serial.write(' '); | |||
| // print read time | |||
| Serial.println(t2 - t1); | |||
| } | |||
| } | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| PgmPrintln("Type any character to start"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(200); // Catch Due reset problem | |||
| // 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(); | |||
| // write files to root if FAT32 | |||
| if (sd.vol()->fatType() == 32) { | |||
| PgmPrintln("Writing files to root"); | |||
| dirAllocTest(sd.vwd()); | |||
| } | |||
| // create sub1 and write files | |||
| SdFile sub1; | |||
| if (!sub1.makeDir(sd.vwd(), "SUB1")) error("makdeDir SUB1 failed"); | |||
| PgmPrintln("Writing files to SUB1"); | |||
| dirAllocTest(&sub1); | |||
| // create sub2 and write files | |||
| SdFile sub2; | |||
| if (!sub2.makeDir(&sub1, "SUB2")) error("makeDir SUB2 failed"); | |||
| PgmPrintln("Writing files to SUB2"); | |||
| dirAllocTest(&sub2); | |||
| PgmPrintln("Done"); | |||
| } | |||
| void loop() { } | |||
| @@ -0,0 +1,97 @@ | |||
| /* | |||
| * This sketch will remove the files and directories | |||
| * created by the SdFatMakeDir.pde sketch. | |||
| * | |||
| * Performance is erratic due to the large number | |||
| * of flash erase operations caused by many random | |||
| * writes to file structures. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| SdFat sd; | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| /* | |||
| * remove all files in dir. | |||
| */ | |||
| void deleteFiles(SdBaseFile* dir) { | |||
| char name[13]; | |||
| SdFile file; | |||
| // open and delete files | |||
| for (uint16_t n = 0; ; n++){ | |||
| sprintf(name, "%u.TXT", n); | |||
| // open start time | |||
| uint32_t t0 = millis(); | |||
| // assume done if open fails | |||
| if (!file.open(dir, name, O_WRITE)) return; | |||
| // open end time and remove start time | |||
| uint32_t t1 = millis(); | |||
| if (!file.remove()) error("file.remove failed"); | |||
| // remove end time | |||
| uint32_t t2 = millis(); | |||
| PgmPrint("RM "); | |||
| Serial.print(n); | |||
| Serial.write(' '); | |||
| // open time | |||
| Serial.print(t1 - t0); | |||
| Serial.write(' '); | |||
| // remove time | |||
| Serial.println(t2 - t1); | |||
| } | |||
| } | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| PgmPrintln("Type any character to start"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(200); // Catch Due reset problem | |||
| // 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) { | |||
| PgmPrintln("Remove files in root"); | |||
| deleteFiles(sd.vwd()); | |||
| } | |||
| // open SUB1 and delete files | |||
| SdFile sub1; | |||
| if (!sub1.open("SUB1", O_READ)) error("open SUB1 failed"); | |||
| PgmPrintln("Remove files in SUB1"); | |||
| deleteFiles(&sub1); | |||
| // open SUB2 and delete files | |||
| SdFile sub2; | |||
| if (!sub2.open(&sub1, "SUB2", O_READ)) error("open SUB2 failed"); | |||
| PgmPrintln("Remove files in SUB2"); | |||
| deleteFiles(&sub2); | |||
| // remove SUB2 | |||
| if (!sub2.rmDir()) error("sub2.rmDir failed"); | |||
| PgmPrintln("SUB2 removed"); | |||
| // remove SUB1 | |||
| if (!sub1.rmDir()) error("sub1.rmDir failed"); | |||
| PgmPrintln("SUB1 removed"); | |||
| PgmPrintln("Done"); | |||
| } | |||
| void loop() { } | |||
| @@ -0,0 +1,63 @@ | |||
| /* | |||
| * Append Example | |||
| * | |||
| * This sketch shows how to use open for append. | |||
| * The sketch will append 100 line each time it opens the file. | |||
| * The sketch will open and close the file 100 times. | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system object | |||
| SdFat sd; | |||
| // create Serial stream | |||
| ArduinoOutStream cout(Serial); | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| // filename for this example | |||
| char name[] = "APPEND.TXT"; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| // pstr() stores strings in flash to save RAM | |||
| cout << endl << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // Catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| cout << pstr("Appending to: ") << name; | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| // open stream for append | |||
| ofstream sdout(name, ios::out | ios::app); | |||
| if (!sdout) error("open failed"); | |||
| // append 100 lines to the file | |||
| for (uint8_t j = 0; j < 100; j++) { | |||
| // use int() so byte will print as decimal number | |||
| sdout << "line " << int(j) << " of pass " << int(i); | |||
| sdout << " millis = " << millis() << endl; | |||
| } | |||
| // close the stream | |||
| sdout.close(); | |||
| if (!sdout) error("append data failed"); | |||
| // output progress indicator | |||
| if (i % 25 == 0) cout << endl; | |||
| cout << '.'; | |||
| } | |||
| cout << endl << "Done" << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,70 @@ | |||
| /* | |||
| * Calculate the sum and average of a list of floating point numbers | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // object for the SD file system | |||
| SdFat sd; | |||
| // define a serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| void writeTestFile() { | |||
| // open the output file | |||
| ofstream sdout("AVG_TEST.TXT"); | |||
| // write a series of float numbers | |||
| for (int16_t i = -1001; i < 2000; i += 13) { | |||
| sdout << 0.1 * i << endl; | |||
| } | |||
| if (!sdout) sd.errorHalt("sdout failed"); | |||
| sdout.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void calcAverage() { | |||
| uint16_t n = 0; // count of input numbers | |||
| double num; // current input number | |||
| double sum = 0; // sum of input numbers | |||
| // open the input file | |||
| ifstream sdin("AVG_TEST.TXT"); | |||
| // check for an open failure | |||
| if (!sdin) sd.errorHalt("sdin failed"); | |||
| // read and sum the numbers | |||
| while (sdin >> num) { | |||
| n++; | |||
| sum += num; | |||
| } | |||
| // print the results | |||
| cout << "sum of " << n << " numbers = " << sum << endl; | |||
| cout << "average = " << sum/n << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // Catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // write the test file | |||
| writeTestFile(); | |||
| // read the test file and calculate the average | |||
| calcAverage(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,133 @@ | |||
| /* | |||
| * This sketch is a simple binary write/read benchmark | |||
| * for the standard Arduino SD.h library. | |||
| */ | |||
| #include <SPI.h> | |||
| #include <SD.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| #define FILE_SIZE_MB 5 | |||
| #define FILE_SIZE (1000000UL*FILE_SIZE_MB) | |||
| #define BUF_SIZE 100 | |||
| uint8_t buf[BUF_SIZE]; | |||
| // test file | |||
| File file; | |||
| //------------------------------------------------------------------------------ | |||
| void error(char* s) { | |||
| Serial.println(s); | |||
| while(1); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial){} // wait for Leonardo | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| uint32_t maxLatency; | |||
| uint32_t minLatency; | |||
| uint32_t totalLatency; | |||
| // discard any input | |||
| while (Serial.read() >= 0) {} | |||
| // pstr stores strings in flash to save RAM | |||
| Serial.println(F("Type any character to start")); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| if (!SD.begin(chipSelect)) error("begin"); | |||
| // open or create file - truncate existing file. | |||
| file = SD.open("BENCH.DAT", FILE_WRITE | O_TRUNC); | |||
| // file = SD.open("BENCH.DAT", O_CREAT | O_TRUNC | O_CREAT); | |||
| if (!file) { | |||
| error("open failed"); | |||
| } | |||
| // fill buf with known data | |||
| 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'; | |||
| Serial.print(F("File size ")); | |||
| Serial.print(FILE_SIZE_MB); | |||
| Serial.println(F("MB")); | |||
| Serial.print(F("Buffer size ")); | |||
| Serial.print(BUF_SIZE); | |||
| Serial.println(F(" bytes")); | |||
| Serial.println(F("Starting write test. Please wait up to a minute")); | |||
| // do write test | |||
| uint32_t n = FILE_SIZE/sizeof(buf); | |||
| maxLatency = 0; | |||
| minLatency = 999999; | |||
| totalLatency = 0; | |||
| uint32_t t = millis(); | |||
| for (uint32_t i = 0; i < n; i++) { | |||
| uint32_t m = micros(); | |||
| if (file.write(buf, sizeof(buf)) != sizeof(buf)) { | |||
| error("write failed"); | |||
| } | |||
| m = micros() - m; | |||
| if (maxLatency < m) maxLatency = m; | |||
| if (minLatency > m) minLatency = m; | |||
| totalLatency += m; | |||
| } | |||
| file.flush(); | |||
| t = millis() - t; | |||
| double s = file.size(); | |||
| Serial.print(F("Write ")); | |||
| Serial.print(s/t); | |||
| Serial.print(F(" KB/sec\n")); | |||
| Serial.print(F("Maximum latency: ")); | |||
| Serial.print(maxLatency); | |||
| Serial.print(F(" usec, Minimum Latency: ")); | |||
| Serial.print(minLatency); | |||
| Serial.print(F(" usec, Avg Latency: ")); | |||
| Serial.print(totalLatency/n); | |||
| Serial.print(F(" usec\n\n")); | |||
| Serial.println(F("Starting read test. Please wait up to a minute")); | |||
| // do read test | |||
| file.seek(0); | |||
| maxLatency = 0; | |||
| minLatency = 99999; | |||
| totalLatency = 0; | |||
| t = millis(); | |||
| for (uint32_t i = 0; i < n; i++) { | |||
| buf[BUF_SIZE-1] = 0; | |||
| uint32_t m = micros(); | |||
| if (file.read(buf, sizeof(buf)) != sizeof(buf)) { | |||
| 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"); | |||
| } | |||
| } | |||
| t = millis() - t; | |||
| Serial.print(F("Read ")); | |||
| Serial.print(s/t); | |||
| Serial.print(F(" KB/sec\n")); | |||
| Serial.print(F("Maximum latency: ")); | |||
| Serial.print(maxLatency); | |||
| Serial.print(F(" usec, Minimum Latency: ")); | |||
| Serial.print(minLatency); | |||
| Serial.print(F(" usec, Avg Latency: ")); | |||
| Serial.print(totalLatency/n); | |||
| Serial.print(F(" usec\n\n")); | |||
| Serial.print(F("Done\n\n")); | |||
| file.close(); | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| /* | |||
| * Use of ibufsteam to parse a line and obufstream to format a line | |||
| */ | |||
| #include <SdFat.h> | |||
| // create a serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| char buf[20]; // buffer for formatted line | |||
| int i, j, k; // values from parsed line | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(2000); | |||
| // initialize input string | |||
| ibufstream bin("123 456 789"); | |||
| // parse the string "123 456 789" | |||
| bin >> i >> j >> k; | |||
| // initialize output buffer | |||
| obufstream bout(buf, sizeof(buf)); | |||
| // format the output string | |||
| bout << k << ',' << j << ',' << i << endl; | |||
| // write the string to serial | |||
| cout << buf; | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,53 @@ | |||
| /* | |||
| * Append a line to a file - demo of pathnames and streams | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system object | |||
| SdFat sd; | |||
| // define a serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| /* | |||
| * Append a line to LOGFILE.TXT | |||
| */ | |||
| void logEvent(const char *msg) { | |||
| // create dir if needed | |||
| sd.mkdir("LOGS/2011/JAN"); | |||
| // create or open a file for append | |||
| ofstream sdlog("LOGS/2011/JAN/LOGFILE.TXT", ios::out | ios::app); | |||
| // append a line to the file | |||
| sdlog << msg << endl; | |||
| // check for errors | |||
| if (!sdlog) sd.errorHalt("append failed"); | |||
| sdlog.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // append a line to the logfile | |||
| logEvent("Another line for the logfile"); | |||
| cout << "Done - check /LOGS/2011/JAN/LOGFILE.TXT on the SD" << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,91 @@ | |||
| // Demo of rewriting a line read by fgets | |||
| #include <SdFat.h> | |||
| // SD card chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system | |||
| SdFat sd; | |||
| // print stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash memory | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void demoFgets() { | |||
| char line[25]; | |||
| int c; | |||
| uint32_t pos; | |||
| // open test file | |||
| SdFile rdfile("FGETS.TXT", O_RDWR); | |||
| // check for open error | |||
| if (!rdfile.isOpen()) error("demoFgets"); | |||
| // list file | |||
| cout << pstr("-----Before Rewrite\r\n"); | |||
| while ((c = rdfile.read()) >= 0) Serial.write(c); | |||
| rdfile.rewind(); | |||
| // read lines from the file to get position | |||
| while (1) { | |||
| pos = rdfile.curPosition(); | |||
| if (rdfile.fgets(line, sizeof(line)) < 0) { | |||
| error("Line not found"); | |||
| } | |||
| // find line that contains "Line C" | |||
| if (strstr(line, "Line C"))break; | |||
| } | |||
| // rewrite line with 'C' | |||
| if (!rdfile.seekSet(pos))error("seekSet"); | |||
| rdfile.println("Line R"); | |||
| rdfile.rewind(); | |||
| // list file | |||
| cout << pstr("\r\n-----After Rewrite\r\n"); | |||
| while ((c = rdfile.read()) >= 0) Serial.write(c); | |||
| // close so rewrite is not lost | |||
| rdfile.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void makeTestFile() { | |||
| // create or open test file | |||
| SdFile wrfile("FGETS.TXT", O_WRITE | O_CREAT | O_TRUNC); | |||
| // check for open error | |||
| if (!wrfile.isOpen()) error("MakeTestFile"); | |||
| // write test file | |||
| wrfile.write_P(PSTR( | |||
| "Line A\r\n" | |||
| "Line B\r\n" | |||
| "Line C\r\n" | |||
| "Line D\r\n" | |||
| "Line E\r\n" | |||
| )); | |||
| wrfile.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial){} // wait for Leonardo | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| makeTestFile(); | |||
| demoFgets(); | |||
| cout << pstr("\nDone\n"); | |||
| } | |||
| void loop(void) {} | |||
| @@ -0,0 +1,40 @@ | |||
| /* | |||
| * Read the logfile created by the eventlog.pde example. | |||
| * Demo of pathnames and working directories | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system object | |||
| SdFat sd; | |||
| // define a serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| int c; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // set current working directory | |||
| if (!sd.chdir("LOGS/2011/JAN/")) { | |||
| sd.errorHalt("chdir failed. Did you run eventlog.pde?"); | |||
| } | |||
| // open file in current working directory | |||
| ifstream file("LOGFILE.TXT"); | |||
| if (!file.is_open()) sd.errorHalt("open failed"); | |||
| // copy the file to Serial | |||
| while ((c = file.get()) >= 0) cout << (char)c; | |||
| cout << "Done" << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,30 @@ | |||
| Old and debug examples. | |||
| AnalogLogger - A simple data logger for one or more analog pins. | |||
| append - This sketch creates a large file by successive | |||
| open/write/close operations. | |||
| average - A demonstration of parsing floating point numbers. | |||
| benchSD - A read/write benchmark for the standard Arduino SD.h library. | |||
| bufstream - ibufsteam to parse a line and obufstream to format a line. | |||
| eventlog - Append a line to a file - demo of pathnames and streams. | |||
| fgetsRewrite - Demo of rewriting a line read by fgets. | |||
| HelloWorld - Create a serial output stream. | |||
| MiniSerial - SdFat minimal serial support for debug. | |||
| PrintBenchmarkSD - Bench mark SD.h print. | |||
| readlog - Read file. Demo of pathnames and current working directory. | |||
| SD_Size - Determine flash used by SD.h example. | |||
| SdFatSize - Determine flash used by SdFat. | |||
| TestMkdirRmdir - Test mkdir, rmdir, and directory management. | |||
| @@ -0,0 +1,39 @@ | |||
| #ifndef AnalogBinLogger_h | |||
| #define AnalogBinLogger_h | |||
| //------------------------------------------------------------------------------ | |||
| // First block of file. | |||
| struct metadata_t { | |||
| unsigned long adcFrequency; // ADC clock frequency | |||
| unsigned long cpuFrequency; // CPU clock frequency | |||
| unsigned long sampleInterval; // Sample interval in CPU cycles. | |||
| unsigned long recordEightBits; // Size of ADC values, nonzero for 8-bits. | |||
| unsigned long pinCount; // Number of analog pins in a sample. | |||
| unsigned long pinNumber[123]; // List of pin numbers in a sample. | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for 8-bit ADC mode. | |||
| const size_t DATA_DIM8 = 508; | |||
| struct block8_t { | |||
| unsigned short count; // count of data bytes | |||
| unsigned short overrun; // count of overruns since last block | |||
| unsigned char data[DATA_DIM8]; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for 10-bit ADC mode. | |||
| const size_t DATA_DIM16 = 254; | |||
| struct block16_t { | |||
| unsigned short count; // count of data bytes | |||
| unsigned short overrun; // count of overruns since last block | |||
| unsigned short data[DATA_DIM16]; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // Data block for PC use | |||
| struct adcdata_t { | |||
| unsigned short count; // count of data bytes | |||
| unsigned short overrun; // count of overruns since last block | |||
| union { | |||
| unsigned char u8[DATA_DIM8]; | |||
| unsigned short u16[DATA_DIM16]; | |||
| } data; | |||
| }; | |||
| #endif // AnalogBinLogger_h | |||
| @@ -0,0 +1,787 @@ | |||
| /** | |||
| * 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 13 512 byte buffers will be used. | |||
| * | |||
| * Each 512 byte data block in the file has a four byte header followed by up | |||
| * to 508 bytes of data. (508 values in 8-bit mode or 254 values in 10-bit mode) | |||
| * Each block contains an integral number of samples with unused space at the | |||
| * end of the block. | |||
| * | |||
| * Data is written to the file using a SD multiple block write command. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| #include <StdioStream.h> | |||
| #include "AnalogBinLogger.h" | |||
| //------------------------------------------------------------------------------ | |||
| // 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 | |||
| //------------------------------------------------------------------------------ | |||
| // 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) | |||
| //------------------------------------------------------------------------------ | |||
| // 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 blocks. | |||
| // The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks. | |||
| // This file is flash erased using special SD commands. The file will be | |||
| // truncated if logging is stopped early. | |||
| const uint32_t FILE_BLOCK_COUNT = 256000; | |||
| // log file base name. Must be six characters or less. | |||
| #define FILE_BASE_NAME "ANALOG" | |||
| // Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC. | |||
| #define RECORD_EIGHT_BITS 0 | |||
| //------------------------------------------------------------------------------ | |||
| // 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 = 3; | |||
| // SD chip select pin. | |||
| const uint8_t SD_CS_PIN = SS; | |||
| //------------------------------------------------------------------------------ | |||
| // Buffer definitions. | |||
| // | |||
| // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||
| // buffers. QUEUE_DIM must be a power of two larger than | |||
| //(BUFFER_BLOCK_COUNT + 1). | |||
| // | |||
| #if RAMEND < 0X8FF | |||
| #error Too little SRAM | |||
| // | |||
| #elif RAMEND < 0X10FF | |||
| // Use total of two 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 1; | |||
| // Dimension for queues of 512 byte SD blocks. | |||
| const uint8_t QUEUE_DIM = 4; // Must be a power of two! | |||
| // | |||
| #elif RAMEND < 0X20FF | |||
| // Use total of five 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 4; | |||
| // Dimension for queues of 512 byte SD blocks. | |||
| const uint8_t QUEUE_DIM = 8; // Must be a power of two! | |||
| // | |||
| #elif RAMEND < 0X40FF | |||
| // Use total of 13 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
| // Dimension for queues of 512 byte SD blocks. | |||
| const uint8_t QUEUE_DIM = 16; // Must be a power of two! | |||
| // | |||
| #else // RAMEND | |||
| // Use total of 29 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 28; | |||
| // Dimension for queues of 512 byte SD blocks. | |||
| const uint8_t QUEUE_DIM = 32; // Must be a power of two! | |||
| #endif // RAMEND | |||
| //============================================================================== | |||
| // End of configuration constants. | |||
| //============================================================================== | |||
| // Temporary log file. Will be deleted if a reset or power failure occurs. | |||
| #define TMP_FILE_NAME "TMP_LOG.BIN" | |||
| // Size of file base name. Must not be larger than six. | |||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
| // 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 = 100; | |||
| // Maximum cycles for timer0 system interrupt, millis, micros. | |||
| const uint16_t ISR_TIMER0 = 160; | |||
| //============================================================================== | |||
| SdFat sd; | |||
| SdBaseFile binFile; | |||
| char binName[13] = FILE_BASE_NAME "00.BIN"; | |||
| #if RECORD_EIGHT_BITS | |||
| const size_t SAMPLES_PER_BLOCK = DATA_DIM8/PIN_COUNT; | |||
| typedef block8_t block_t; | |||
| #else // RECORD_EIGHT_BITS | |||
| const size_t SAMPLES_PER_BLOCK = DATA_DIM16/PIN_COUNT; | |||
| typedef block16_t block_t; | |||
| #endif // RECORD_EIGHT_BITS | |||
| block_t* emptyQueue[QUEUE_DIM]; | |||
| uint8_t emptyHead; | |||
| uint8_t emptyTail; | |||
| block_t* fullQueue[QUEUE_DIM]; | |||
| volatile uint8_t fullHead; // volatile insures non-interrupt code sees changes. | |||
| uint8_t fullTail; | |||
| // queueNext assumes QUEUE_DIM is a power of two | |||
| inline uint8_t queueNext(uint8_t ht) {return (ht + 1) & (QUEUE_DIM -1);} | |||
| //============================================================================== | |||
| // Interrupt Service Routines | |||
| // Pointer to current buffer. | |||
| block_t* isrBuf; | |||
| // Need new buffer if true. | |||
| bool isrBufNeeded = true; | |||
| // 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 (isrBufNeeded && emptyHead == emptyTail) { | |||
| // no buffers - count overrun | |||
| if (isrOver < 0XFFFF) isrOver++; | |||
| // Avoid missed timer error. | |||
| timerFlag = false; | |||
| return; | |||
| } | |||
| // Start ADC | |||
| 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; | |||
| } | |||
| // Check for buffer needed. | |||
| if (isrBufNeeded) { | |||
| // Remove buffer from empty queue. | |||
| isrBuf = emptyQueue[emptyTail]; | |||
| emptyTail = queueNext(emptyTail); | |||
| isrBuf->count = 0; | |||
| isrBuf->overrun = isrOver; | |||
| isrBufNeeded = false; | |||
| } | |||
| // Store ADC data. | |||
| isrBuf->data[isrBuf->count++] = d; | |||
| // Check for buffer full. | |||
| if (isrBuf->count >= PIN_COUNT*SAMPLES_PER_BLOCK) { | |||
| // Put buffer isrIn full queue. | |||
| uint8_t tmp = fullHead; // Avoid extra fetch of volatile fullHead. | |||
| fullQueue[tmp] = (block_t*)isrBuf; | |||
| fullHead = queueNext(tmp); | |||
| // Set buffer needed and clear overruns. | |||
| isrBufNeeded = true; | |||
| 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) error_P(PSTR(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void error_P(const char* msg) { | |||
| sd.errorPrint_P(msg); | |||
| fatalBlink(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // | |||
| void fatalBlink() { | |||
| while (true) { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| delay(200); | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| delay(200); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| #if ADPS0 != 0 || ADPS1 != 1 || ADPS2 != 2 | |||
| #error unexpected ADC prescaler bits | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| // 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; | |||
| - (PIN_COUNT > 1 ? ISR_SETUP_ADC : 0); | |||
| 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 > sizeof(meta->pinNumber)/sizeof(meta->pinNumber[0])) { | |||
| 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; | |||
| 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 (int 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, 4); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // enable ADC and timer1 interrupts | |||
| void adcStart() { | |||
| // initialize ISR | |||
| isrBufNeeded = true; | |||
| isrOver = 0; | |||
| adcindex = 1; | |||
| // 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; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void adcStop() { | |||
| TIMSK1 = 0; | |||
| ADCSRA = 0; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Convert binary file to CSV file. | |||
| void binaryToCsv() { | |||
| uint8_t lastPct = 0; | |||
| block_t buf; | |||
| metadata_t* pm; | |||
| uint32_t t0 = millis(); | |||
| char csvName[13]; | |||
| StdioStream csvStream; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| binFile.rewind(); | |||
| if (!binFile.read(&buf , 512) == 512) error("Read metadata failed"); | |||
| // Create a new CSV file. | |||
| strcpy(csvName, binName); | |||
| strcpy_P(&csvName[BASE_NAME_SIZE + 3], PSTR("CSV")); | |||
| if (!csvStream.fopen(csvName, "w")) { | |||
| error("open csvStream failed"); | |||
| } | |||
| Serial.println(); | |||
| Serial.print(F("Writing: ")); | |||
| Serial.print(csvName); | |||
| Serial.println(F(" - type any character to stop")); | |||
| pm = (metadata_t*)&buf; | |||
| csvStream.print(F("Interval,")); | |||
| float intervalMicros = 1.0e6*pm->sampleInterval/(float)pm->cpuFrequency; | |||
| csvStream.print(intervalMicros, 4); | |||
| csvStream.println(F(",usec")); | |||
| for (uint8_t i = 0; i < pm->pinCount; i++) { | |||
| if (i) csvStream.putc(','); | |||
| csvStream.print(F("pin")); | |||
| csvStream.print(pm->pinNumber[i]); | |||
| } | |||
| csvStream.println(); | |||
| uint32_t tPct = millis(); | |||
| while (!Serial.available() && binFile.read(&buf, 512) == 512) { | |||
| uint16_t i; | |||
| if (buf.count == 0) break; | |||
| if (buf.overrun) { | |||
| csvStream.print(F("OVERRUN,")); | |||
| csvStream.println(buf.overrun); | |||
| } | |||
| for (uint16_t j = 0; j < buf.count; j += PIN_COUNT) { | |||
| for (uint16_t i = 0; i < PIN_COUNT; i++) { | |||
| if (i) csvStream.putc(','); | |||
| csvStream.print(buf.data[i + j]); | |||
| } | |||
| csvStream.println(); | |||
| } | |||
| 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 (Serial.available()) break; | |||
| } | |||
| csvStream.fclose(); | |||
| Serial.print(F("Done: ")); | |||
| Serial.print(0.001*(millis() - t0)); | |||
| Serial.println(F(" Seconds")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // read data file and check for overruns | |||
| void checkOverrun() { | |||
| bool headerPrinted = false; | |||
| block_t buf; | |||
| uint32_t bgnBlock, endBlock; | |||
| uint32_t bn = 0; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
| error("contiguousRange failed"); | |||
| } | |||
| binFile.rewind(); | |||
| Serial.println(); | |||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||
| if (!binFile.read(&buf , 512) == 512) { | |||
| error("Read metadata failed"); | |||
| } | |||
| bn++; | |||
| while (binFile.read(&buf, 512) == 512) { | |||
| if (buf.count == 0) break; | |||
| if (buf.overrun) { | |||
| if (!headerPrinted) { | |||
| Serial.println(); | |||
| Serial.println(F("Overruns:")); | |||
| Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||
| headerPrinted = true; | |||
| } | |||
| Serial.print(bn); | |||
| Serial.print(','); | |||
| Serial.print(bgnBlock + bn); | |||
| Serial.print(','); | |||
| Serial.println(buf.overrun); | |||
| } | |||
| bn++; | |||
| } | |||
| if (!headerPrinted) { | |||
| Serial.println(F("No errors found")); | |||
| } else { | |||
| Serial.println(F("Done")); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // dump data file to Serial | |||
| void dumpData() { | |||
| block_t buf; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| binFile.rewind(); | |||
| if (binFile.read(&buf , 512) != 512) { | |||
| error("Read metadata failed"); | |||
| } | |||
| Serial.println(); | |||
| Serial.println(F("Type any character to stop")); | |||
| delay(1000); | |||
| while (!Serial.available() && binFile.read(&buf , 512) == 512) { | |||
| if (buf.count == 0) break; | |||
| if (buf.overrun) { | |||
| Serial.print(F("OVERRUN,")); | |||
| Serial.println(buf.overrun); | |||
| } | |||
| for (uint16_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")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // log data | |||
| // max number of blocks to erase per erase call | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void logData() { | |||
| uint32_t bgnBlock, endBlock; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT]; | |||
| Serial.println(); | |||
| // Initialize ADC and timer1. | |||
| adcInit((metadata_t*) &block[0]); | |||
| // Find unused file name. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| } | |||
| while (sd.exists(binName)) { | |||
| if (binName[BASE_NAME_SIZE + 1] != '9') { | |||
| binName[BASE_NAME_SIZE + 1]++; | |||
| } else { | |||
| binName[BASE_NAME_SIZE + 1] = '0'; | |||
| if (binName[BASE_NAME_SIZE] == '9') { | |||
| error("Can't create file name"); | |||
| } | |||
| binName[BASE_NAME_SIZE]++; | |||
| } | |||
| } | |||
| // Delete old tmp file. | |||
| if (sd.exists(TMP_FILE_NAME)) { | |||
| Serial.println(F("Deleting tmp file")); | |||
| if (!sd.remove(TMP_FILE_NAME)) { | |||
| error("Can't remove tmp file"); | |||
| } | |||
| } | |||
| // Create new file. | |||
| Serial.println(F("Creating new file")); | |||
| binFile.close(); | |||
| if (!binFile.createContiguous(sd.vwd(), | |||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
| error("createContiguous failed"); | |||
| } | |||
| // Get the address of the file on the SD. | |||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
| error("contiguousRange failed"); | |||
| } | |||
| // Use SdFat's internal buffer. | |||
| uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||
| if (cache == 0) error("cacheClear failed"); | |||
| // Flash erase all data in the file. | |||
| Serial.println(F("Erasing all data")); | |||
| uint32_t bgnErase = bgnBlock; | |||
| uint32_t endErase; | |||
| while (bgnErase < endBlock) { | |||
| endErase = bgnErase + ERASE_SIZE; | |||
| if (endErase > endBlock) endErase = endBlock; | |||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||
| error("erase failed"); | |||
| } | |||
| bgnErase = endErase + 1; | |||
| } | |||
| // Start a multiple block write. | |||
| if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||
| error("writeBegin failed"); | |||
| } | |||
| // Write metadata. | |||
| if (!sd.card()->writeData((uint8_t*)&block[0])) { | |||
| error("Write metadata failed"); | |||
| } | |||
| // Initialize queues. | |||
| emptyHead = emptyTail = 0; | |||
| fullHead = fullTail = 0; | |||
| // Use SdFat buffer for one block. | |||
| emptyQueue[emptyHead] = (block_t*)cache; | |||
| emptyHead = queueNext(emptyHead); | |||
| // Put rest of buffers in the empty queue. | |||
| for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||
| emptyQueue[emptyHead] = &block[i]; | |||
| emptyHead = queueNext(emptyHead); | |||
| } | |||
| // Give SD time to prepare for big write. | |||
| delay(1000); | |||
| Serial.println(F("Logging - type any character to stop")); | |||
| // Wait for Serial Idle. | |||
| Serial.flush(); | |||
| delay(10); | |||
| uint32_t bn = 1; | |||
| uint32_t t0 = millis(); | |||
| uint32_t t1 = t0; | |||
| uint32_t overruns = 0; | |||
| uint32_t count = 0; | |||
| uint32_t maxLatency = 0; | |||
| // Start logging interrupts. | |||
| adcStart(); | |||
| while (1) { | |||
| if (fullHead != fullTail) { | |||
| // Get address of block to write. | |||
| block_t* pBlock = fullQueue[fullTail]; | |||
| // Write block to SD. | |||
| uint32_t usec = micros(); | |||
| if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||
| error("write data failed"); | |||
| } | |||
| usec = micros() - usec; | |||
| t1 = millis(); | |||
| if (usec > maxLatency) maxLatency = usec; | |||
| 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); | |||
| } | |||
| } | |||
| // Move block to empty queue. | |||
| emptyQueue[emptyHead] = pBlock; | |||
| emptyHead = queueNext(emptyHead); | |||
| fullTail = queueNext(fullTail); | |||
| bn++; | |||
| if (bn == FILE_BLOCK_COUNT) { | |||
| // File full so stop ISR calls. | |||
| adcStop(); | |||
| break; | |||
| } | |||
| } | |||
| if (timerError) { | |||
| error("Missed timer event - rate too high"); | |||
| } | |||
| if (Serial.available()) { | |||
| // Stop ISR calls. | |||
| adcStop(); | |||
| if (isrBuf != 0 && isrBuf->count >= PIN_COUNT) { | |||
| // Truncate to last complete sample. | |||
| isrBuf->count = PIN_COUNT*(isrBuf->count/PIN_COUNT); | |||
| // Put buffer in full queue. | |||
| fullQueue[fullHead] = isrBuf; | |||
| fullHead = queueNext(fullHead); | |||
| isrBuf = 0; | |||
| } | |||
| if (fullHead == fullTail) break; | |||
| } | |||
| } | |||
| if (!sd.card()->writeStop()) { | |||
| error("writeStop failed"); | |||
| } | |||
| // Truncate file if recording stopped early. | |||
| if (bn != FILE_BLOCK_COUNT) { | |||
| Serial.println(F("Truncating file")); | |||
| if (!binFile.truncate(512L * bn)) { | |||
| error("Can't truncate file"); | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| Serial.println(binName); | |||
| Serial.print(F("Max block write usec: ")); | |||
| Serial.println(maxLatency); | |||
| 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("Samples/sec: ")); | |||
| Serial.println((1000.0/PIN_COUNT)*count/(t1-t0)); | |||
| Serial.print(F("Overruns: ")); | |||
| Serial.println(overruns); | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| } | |||
| Serial.begin(9600); | |||
| // Read the first sample pin to init the ADC. | |||
| analogRead(PIN_LIST[0]); | |||
| Serial.print(F("FreeRam: ")); | |||
| Serial.println(FreeRam()); | |||
| // initialize file system. | |||
| if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||
| sd.initErrorPrint(); | |||
| fatalBlink(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop(void) { | |||
| // discard any input | |||
| while (Serial.read() >= 0) {} | |||
| Serial.println(); | |||
| Serial.println(F("type:")); | |||
| 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("r - record ADC data")); | |||
| while(!Serial.available()) {} | |||
| char c = tolower(Serial.read()); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| } | |||
| // Read any extra Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| if (c == 'c') { | |||
| binaryToCsv(); | |||
| } else if (c == 'd') { | |||
| dumpData(); | |||
| } else if (c == 'e') { | |||
| checkOverrun(); | |||
| } else if (c == 'r') { | |||
| logData(); | |||
| } else { | |||
| Serial.println(F("Invalid entry")); | |||
| } | |||
| } | |||
| @@ -0,0 +1,529 @@ | |||
| /** | |||
| * This program logs data to a binary file. Functions are included | |||
| * to convert the binary file to a CSV text file. | |||
| * | |||
| * Samples are logged at regular intervals. The maximum logging rate | |||
| * depends on the quality of your SD card and the time required to | |||
| * read sensor data. This example has been tested at 500 Hz with | |||
| * good SD card on an Uno. 4000 HZ is possible on a Due. | |||
| * | |||
| * 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 13 512 byte buffers will be used. | |||
| * | |||
| * Data is written to the file using a SD multiple block write command. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| //------------------------------------------------------------------------------ | |||
| // User data functions. Modify these functions for your data items. | |||
| #include "UserDataType.h" // Edit this include file to change data_t. | |||
| // Acquire a data record. | |||
| void acquireData(data_t* data) { | |||
| data->time = micros(); | |||
| for (int i = 0; i < ADC_DIM; i++) { | |||
| data->adc[i] = analogRead(i); | |||
| } | |||
| } | |||
| // Print a data record. | |||
| void printData(Print* pr, data_t* data) { | |||
| pr->print(data->time); | |||
| for (int i = 0; i < ADC_DIM; i++) { | |||
| pr->write(','); | |||
| pr->print(data->adc[i]); | |||
| } | |||
| pr->println(); | |||
| } | |||
| // Print data header. | |||
| void printHeader(Print* pr) { | |||
| pr->print(F("time")); | |||
| for (int i = 0; i < ADC_DIM; i++) { | |||
| pr->print(F(",adc")); | |||
| pr->print(i); | |||
| } | |||
| pr->println(); | |||
| } | |||
| //============================================================================== | |||
| // Start of configuration constants. | |||
| //============================================================================== | |||
| //Interval between data records in microseconds. | |||
| const uint32_t LOG_INTERVAL_USEC = 2000; | |||
| //------------------------------------------------------------------------------ | |||
| // Pin definitions. | |||
| // | |||
| // SD chip select pin. | |||
| const uint8_t SD_CS_PIN = SS; | |||
| // | |||
| // 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 = 3; | |||
| //------------------------------------------------------------------------------ | |||
| // File definitions. | |||
| // | |||
| // Maximum file size in blocks. | |||
| // The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks. | |||
| // This file is flash erased using special SD commands. The file will be | |||
| // truncated if logging is stopped early. | |||
| const uint32_t FILE_BLOCK_COUNT = 256000; | |||
| // log file base name. Must be six characters or less. | |||
| #define FILE_BASE_NAME "DATA" | |||
| //------------------------------------------------------------------------------ | |||
| // Buffer definitions. | |||
| // | |||
| // The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional | |||
| // buffers. | |||
| // | |||
| #ifndef RAMEND | |||
| // Assume ARM. Use total of nine 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 8; | |||
| // | |||
| #elif RAMEND < 0X8FF | |||
| #error Too little SRAM | |||
| // | |||
| #elif RAMEND < 0X10FF | |||
| // Use total of two 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 1; | |||
| // | |||
| #elif RAMEND < 0X20FF | |||
| // Use total of five 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 4; | |||
| // | |||
| #else // RAMEND | |||
| // Use total of 13 512 byte buffers. | |||
| const uint8_t BUFFER_BLOCK_COUNT = 12; | |||
| #endif // RAMEND | |||
| //============================================================================== | |||
| // End of configuration constants. | |||
| //============================================================================== | |||
| // Temporary log file. Will be deleted if a reset or power failure occurs. | |||
| #define TMP_FILE_NAME "TMP_LOG.BIN" | |||
| // Size of file base name. Must not be larger than six. | |||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
| SdFat sd; | |||
| SdBaseFile binFile; | |||
| char binName[13] = FILE_BASE_NAME "00.BIN"; | |||
| // Number of data records in a block. | |||
| const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t); | |||
| //Compute fill so block size is 512 bytes. FILL_DIM may be zero. | |||
| const uint16_t FILL_DIM = 512 - 4 - DATA_DIM*sizeof(data_t); | |||
| struct block_t { | |||
| uint16_t count; | |||
| uint16_t overrun; | |||
| data_t data[DATA_DIM]; | |||
| uint8_t fill[FILL_DIM]; | |||
| }; | |||
| const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 2; | |||
| block_t* emptyQueue[QUEUE_DIM]; | |||
| uint8_t emptyHead; | |||
| uint8_t emptyTail; | |||
| block_t* fullQueue[QUEUE_DIM]; | |||
| uint8_t fullHead; | |||
| uint8_t fullTail; | |||
| // Advance queue index. | |||
| inline uint8_t queueNext(uint8_t ht) {return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;} | |||
| //============================================================================== | |||
| // Error messages stored in flash. | |||
| #define error(msg) error_P(PSTR(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void error_P(const char* msg) { | |||
| sd.errorPrint_P(msg); | |||
| fatalBlink(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // | |||
| void fatalBlink() { | |||
| while (true) { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| delay(200); | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| delay(200); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| // Convert binary file to CSV file. | |||
| void binaryToCsv() { | |||
| uint8_t lastPct = 0; | |||
| block_t block; | |||
| uint32_t t0 = millis(); | |||
| uint32_t syncCluster = 0; | |||
| SdFile csvFile; | |||
| char csvName[13]; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(); | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| binFile.rewind(); | |||
| // Create a new csvFile. | |||
| strcpy(csvName, binName); | |||
| strcpy_P(&csvName[BASE_NAME_SIZE + 3], PSTR("CSV")); | |||
| if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) { | |||
| error("open csvFile failed"); | |||
| } | |||
| Serial.println(); | |||
| Serial.print(F("Writing: ")); | |||
| Serial.print(csvName); | |||
| Serial.println(F(" - type any character to stop")); | |||
| printHeader(&csvFile); | |||
| uint32_t tPct = millis(); | |||
| while (!Serial.available() && binFile.read(&block, 512) == 512) { | |||
| uint16_t i; | |||
| if (block.count == 0) break; | |||
| if (block.overrun) { | |||
| csvFile.print(F("OVERRUN,")); | |||
| csvFile.println(block.overrun); | |||
| } | |||
| for (i = 0; i < block.count; i++) { | |||
| printData(&csvFile, &block.data[i]); | |||
| } | |||
| if (csvFile.curCluster() != syncCluster) { | |||
| csvFile.sync(); | |||
| syncCluster = csvFile.curCluster(); | |||
| } | |||
| 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 (Serial.available()) break; | |||
| } | |||
| csvFile.close(); | |||
| Serial.print(F("Done: ")); | |||
| Serial.print(0.001*(millis() - t0)); | |||
| Serial.println(F(" Seconds")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // read data file and check for overruns | |||
| void checkOverrun() { | |||
| bool headerPrinted = false; | |||
| block_t block; | |||
| uint32_t bgnBlock, endBlock; | |||
| uint32_t bn = 0; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(); | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
| error("contiguousRange failed"); | |||
| } | |||
| binFile.rewind(); | |||
| Serial.println(); | |||
| Serial.println(F("Checking overrun errors - type any character to stop")); | |||
| while (binFile.read(&block, 512) == 512) { | |||
| if (block.count == 0) break; | |||
| if (block.overrun) { | |||
| if (!headerPrinted) { | |||
| Serial.println(); | |||
| Serial.println(F("Overruns:")); | |||
| Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount")); | |||
| headerPrinted = true; | |||
| } | |||
| Serial.print(bn); | |||
| Serial.print(','); | |||
| Serial.print(bgnBlock + bn); | |||
| Serial.print(','); | |||
| Serial.println(block.overrun); | |||
| } | |||
| bn++; | |||
| } | |||
| if (!headerPrinted) { | |||
| Serial.println(F("No errors found")); | |||
| } else { | |||
| Serial.println(F("Done")); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // dump data file to Serial | |||
| void dumpData() { | |||
| block_t block; | |||
| if (!binFile.isOpen()) { | |||
| Serial.println(); | |||
| Serial.println(F("No current binary file")); | |||
| return; | |||
| } | |||
| binFile.rewind(); | |||
| Serial.println(); | |||
| Serial.println(F("Type any character to stop")); | |||
| delay(1000); | |||
| printHeader(&Serial); | |||
| while (!Serial.available() && binFile.read(&block , 512) == 512) { | |||
| if (block.count == 0) break; | |||
| if (block.overrun) { | |||
| Serial.print(F("OVERRUN,")); | |||
| Serial.println(block.overrun); | |||
| } | |||
| for (uint16_t i = 0; i < block.count; i++) { | |||
| printData(&Serial, &block.data[i]); | |||
| } | |||
| } | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // log data | |||
| // max number of blocks to erase per erase call | |||
| uint32_t const ERASE_SIZE = 262144L; | |||
| void logData() { | |||
| uint32_t bgnBlock, endBlock; | |||
| // Allocate extra buffer space. | |||
| block_t block[BUFFER_BLOCK_COUNT]; | |||
| block_t* curBlock = 0; | |||
| Serial.println(); | |||
| // Find unused file name. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| } | |||
| while (sd.exists(binName)) { | |||
| if (binName[BASE_NAME_SIZE + 1] != '9') { | |||
| binName[BASE_NAME_SIZE + 1]++; | |||
| } else { | |||
| binName[BASE_NAME_SIZE + 1] = '0'; | |||
| if (binName[BASE_NAME_SIZE] == '9') { | |||
| error("Can't create file name"); | |||
| } | |||
| binName[BASE_NAME_SIZE]++; | |||
| } | |||
| } | |||
| // Delete old tmp file. | |||
| if (sd.exists(TMP_FILE_NAME)) { | |||
| Serial.println(F("Deleting tmp file")); | |||
| if (!sd.remove(TMP_FILE_NAME)) { | |||
| error("Can't remove tmp file"); | |||
| } | |||
| } | |||
| // Create new file. | |||
| Serial.println(F("Creating new file")); | |||
| binFile.close(); | |||
| if (!binFile.createContiguous(sd.vwd(), | |||
| TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) { | |||
| error("createContiguous failed"); | |||
| } | |||
| // Get the address of the file on the SD. | |||
| if (!binFile.contiguousRange(&bgnBlock, &endBlock)) { | |||
| error("contiguousRange failed"); | |||
| } | |||
| // Use SdFat's internal buffer. | |||
| uint8_t* cache = (uint8_t*)sd.vol()->cacheClear(); | |||
| if (cache == 0) error("cacheClear failed"); | |||
| // Flash erase all data in the file. | |||
| Serial.println(F("Erasing all data")); | |||
| uint32_t bgnErase = bgnBlock; | |||
| uint32_t endErase; | |||
| while (bgnErase < endBlock) { | |||
| endErase = bgnErase + ERASE_SIZE; | |||
| if (endErase > endBlock) endErase = endBlock; | |||
| if (!sd.card()->erase(bgnErase, endErase)) { | |||
| error("erase failed"); | |||
| } | |||
| bgnErase = endErase + 1; | |||
| } | |||
| // Start a multiple block write. | |||
| if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) { | |||
| error("writeBegin failed"); | |||
| } | |||
| // Initialize queues. | |||
| emptyHead = emptyTail = 0; | |||
| fullHead = fullTail = 0; | |||
| // Use SdFat buffer for one block. | |||
| emptyQueue[emptyHead] = (block_t*)cache; | |||
| emptyHead = queueNext(emptyHead); | |||
| // Put rest of buffers in the empty queue. | |||
| for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) { | |||
| emptyQueue[emptyHead] = &block[i]; | |||
| emptyHead = queueNext(emptyHead); | |||
| } | |||
| Serial.println(F("Logging - type any character to stop")); | |||
| // Wait for Serial Idle. | |||
| Serial.flush(); | |||
| delay(10); | |||
| uint32_t bn = 0; | |||
| uint32_t t0 = millis(); | |||
| uint32_t t1 = t0; | |||
| uint32_t overrun = 0; | |||
| uint32_t overrunTotal = 0; | |||
| uint32_t count = 0; | |||
| uint32_t maxLatency = 0; | |||
| int32_t diff; | |||
| // Start at a multiple of interval. | |||
| uint32_t logTime = micros()/LOG_INTERVAL_USEC + 1; | |||
| logTime *= LOG_INTERVAL_USEC; | |||
| bool closeFile = false; | |||
| while (1) { | |||
| // Time for next data record. | |||
| logTime += LOG_INTERVAL_USEC; | |||
| if (Serial.available()) closeFile = true; | |||
| if (closeFile) { | |||
| if (curBlock != 0 && curBlock->count >= 0) { | |||
| // Put buffer in full queue. | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| curBlock = 0; | |||
| } | |||
| } else { | |||
| if (curBlock == 0 && emptyTail != emptyHead) { | |||
| curBlock = emptyQueue[emptyTail]; | |||
| emptyTail = queueNext(emptyTail); | |||
| curBlock->count = 0; | |||
| curBlock->overrun = overrun; | |||
| overrun = 0; | |||
| } | |||
| do { | |||
| diff = logTime - micros(); | |||
| } while(diff > 0); | |||
| if (diff < -10) error("LOG_INTERVAL_USEC too small"); | |||
| if (curBlock == 0) { | |||
| overrun++; | |||
| } else { | |||
| acquireData(&curBlock->data[curBlock->count++]); | |||
| if (curBlock->count == DATA_DIM) { | |||
| fullQueue[fullHead] = curBlock; | |||
| fullHead = queueNext(fullHead); | |||
| curBlock = 0; | |||
| } | |||
| } | |||
| } | |||
| if (fullHead == fullTail) { | |||
| // Exit loop if done. | |||
| if (closeFile) break; | |||
| } else if (!sd.card()->isBusy()) { | |||
| // Get address of block to write. | |||
| block_t* pBlock = fullQueue[fullTail]; | |||
| fullTail = queueNext(fullTail); | |||
| // Write block to SD. | |||
| uint32_t usec = micros(); | |||
| if (!sd.card()->writeData((uint8_t*)pBlock)) { | |||
| error("write data failed"); | |||
| } | |||
| usec = micros() - usec; | |||
| t1 = millis(); | |||
| if (usec > maxLatency) maxLatency = usec; | |||
| count += pBlock->count; | |||
| // Add overruns and possibly light LED. | |||
| if (pBlock->overrun) { | |||
| overrunTotal += pBlock->overrun; | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, HIGH); | |||
| } | |||
| } | |||
| // Move block to empty queue. | |||
| emptyQueue[emptyHead] = pBlock; | |||
| emptyHead = queueNext(emptyHead); | |||
| bn++; | |||
| if (bn == FILE_BLOCK_COUNT) { | |||
| // File full so stop | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| if (!sd.card()->writeStop()) { | |||
| error("writeStop failed"); | |||
| } | |||
| // Truncate file if recording stopped early. | |||
| if (bn != FILE_BLOCK_COUNT) { | |||
| Serial.println(F("Truncating file")); | |||
| if (!binFile.truncate(512L * bn)) { | |||
| error("Can't truncate file"); | |||
| } | |||
| } | |||
| if (!binFile.rename(sd.vwd(), binName)) { | |||
| error("Can't rename file"); | |||
| } | |||
| Serial.print(F("File renamed: ")); | |||
| Serial.println(binName); | |||
| Serial.print(F("Max block write usec: ")); | |||
| Serial.println(maxLatency); | |||
| Serial.print(F("Record time sec: ")); | |||
| Serial.println(0.001*(t1 - t0), 3); | |||
| Serial.print(F("Sample count: ")); | |||
| Serial.println(count); | |||
| Serial.print(F("Samples/sec: ")); | |||
| Serial.println((1000.0)*count/(t1-t0)); | |||
| Serial.print(F("Overruns: ")); | |||
| Serial.println(overrunTotal); | |||
| Serial.println(F("Done")); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| if (ERROR_LED_PIN >= 0) { | |||
| pinMode(ERROR_LED_PIN, OUTPUT); | |||
| } | |||
| Serial.begin(9600); | |||
| Serial.print(F("FreeRam: ")); | |||
| Serial.println(FreeRam()); | |||
| Serial.print(F("Records/block: ")); | |||
| Serial.println(DATA_DIM); | |||
| if (sizeof(block_t) != 512) error("Invalid block size"); | |||
| // initialize file system. | |||
| if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) { | |||
| sd.initErrorPrint(); | |||
| fatalBlink(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop(void) { | |||
| // discard any input | |||
| while (Serial.read() >= 0) {} | |||
| Serial.println(); | |||
| Serial.println(F("type:")); | |||
| 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("r - record data")); | |||
| while(!Serial.available()) {} | |||
| char c = tolower(Serial.read()); | |||
| // Discard extra Serial data. | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| if (ERROR_LED_PIN >= 0) { | |||
| digitalWrite(ERROR_LED_PIN, LOW); | |||
| } | |||
| if (c == 'c') { | |||
| binaryToCsv(); | |||
| } else if (c == 'd') { | |||
| dumpData(); | |||
| } else if (c == 'e') { | |||
| checkOverrun(); | |||
| } else if (c == 'r') { | |||
| logData(); | |||
| } else { | |||
| Serial.println(F("Invalid entry")); | |||
| } | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| #ifndef UserDataType_h | |||
| #define UserDataType_h | |||
| const uint8_t ADC_DIM = 4; | |||
| struct data_t { | |||
| unsigned long time; | |||
| unsigned short adc[ADC_DIM]; | |||
| }; | |||
| #endif // UserDataType_h | |||
| @@ -0,0 +1,40 @@ | |||
| /* | |||
| * Print size, modify date/time, and name for all files in root. | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system object | |||
| SdFat sd; | |||
| SdFile file; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(1000); | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // open next file in root. The volume working directory, vwd, is root | |||
| while (file.openNext(sd.vwd(), O_READ)) { | |||
| 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(); | |||
| } | |||
| Serial.println("Done!"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,119 @@ | |||
| /* | |||
| * This sketch is a simple Print benchmark. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // number of lines to print | |||
| const uint16_t N_PRINT = 20000; | |||
| // file system | |||
| SdFat sd; | |||
| // test file | |||
| SdFile file; | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) { | |||
| // wait for Leonardo | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| uint32_t maxLatency; | |||
| uint32_t minLatency; | |||
| uint32_t totalLatency; | |||
| while (Serial.read() >= 0) { | |||
| } | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) { | |||
| } | |||
| delay(400); // catch Due reset problem | |||
| cout << pstr("Free RAM: ") << FreeRam() << endl; | |||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||
| // try SPI_HALF_SPEED if bus errors occur. | |||
| if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt(); | |||
| cout << pstr("Type is FAT") << int(sd.vol()->fatType()) << endl; | |||
| // open or create file - truncate existing file. | |||
| if (!file.open("BENCH.TXT", O_CREAT | O_TRUNC | O_RDWR)) { | |||
| error("open failed"); | |||
| } | |||
| cout << pstr("Starting print test. Please wait.\n\n"); | |||
| // do write test | |||
| for (int test = 0; test < 3; test++) { | |||
| switch(test) { | |||
| case 0: | |||
| cout << pstr("Test of println(uint16_t)\n"); | |||
| break; | |||
| case 1: | |||
| cout << pstr("Test of printField(uint16_t, char)\n"); | |||
| break; | |||
| case 2: | |||
| cout << pstr("Test of println(double)\n"); | |||
| break; | |||
| } | |||
| file.truncate(0); | |||
| maxLatency = 0; | |||
| minLatency = 999999; | |||
| totalLatency = 0; | |||
| uint32_t t = millis(); | |||
| for (uint16_t i = 0; i < N_PRINT; i++) { | |||
| uint32_t m = micros(); | |||
| switch(test) { | |||
| case 0: | |||
| file.println(i); | |||
| break; | |||
| case 1: | |||
| file.printField(i, '\n'); | |||
| break; | |||
| case 2: | |||
| file.println((double)0.01*i); | |||
| break; | |||
| } | |||
| if (file.writeError) { | |||
| error("write failed"); | |||
| } | |||
| m = micros() - m; | |||
| if (maxLatency < m) maxLatency = m; | |||
| if (minLatency > m) minLatency = m; | |||
| totalLatency += m; | |||
| } | |||
| file.sync(); | |||
| t = millis() - t; | |||
| double s = file.fileSize(); | |||
| cout << pstr("Time ") << 0.001*t << pstr(" sec\n"); | |||
| cout << pstr("File size ") << 0.001*s << pstr(" KB\n"); | |||
| cout << pstr("Write ") << s/t << pstr(" KB/sec\n"); | |||
| cout << pstr("Maximum latency: ") << maxLatency; | |||
| cout << pstr(" usec, Minimum Latency: ") << minLatency; | |||
| cout << pstr(" usec, Avg Latency: "); | |||
| cout << totalLatency/N_PRINT << pstr(" usec\n\n"); | |||
| } | |||
| file.close(); | |||
| cout << pstr("Done!\n\n"); | |||
| } | |||
| @@ -0,0 +1,156 @@ | |||
| // Quick hardware test. | |||
| // | |||
| #include <SdFat.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. | |||
| // Change spiSpeed to SPI_FULL_SPEED for better performance | |||
| // Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||
| const uint8_t spiSpeed = SPI_HALF_SPEED; | |||
| //------------------------------------------------------------------------------ | |||
| // Normally the SdFat class is used in applications in place | |||
| // of Sd2Card, SdVolume, and SdFile for root. | |||
| // These internal classes are used here to diagnose problems. | |||
| Sd2Card card; | |||
| SdVolume volume; | |||
| SdFile root; | |||
| // 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 << pstr("Try another SD card or reduce the SPI bus speed.\n"); | |||
| cout << pstr("Edit spiSpeed in this sketch to change it.\n"); | |||
| } | |||
| void reformatMsg() { | |||
| cout << pstr("Try reformatting the card. For best results use\n"); | |||
| cout << pstr("the SdFormatter sketch in SdFat/examples or download\n"); | |||
| cout << pstr("and use SDFormatter from www.sdcard.org/consumer.\n"); | |||
| } | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // Wait for Leonardo. | |||
| delay(1000); // Delay for Due. | |||
| cout << pstr("\nSPI pins:\n"); | |||
| cout << pstr("MOSI: ") << int(MOSI) << endl; | |||
| cout << pstr("MISO: ") << int(MISO) << endl; | |||
| cout << pstr("SCK: ") << int(SCK) << endl; | |||
| if (DISABLE_CHIP_SELECT < 0) { | |||
| cout << pstr( | |||
| "\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 << pstr( | |||
| "\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 | |||
| while (Serial.read() >= 0) {} | |||
| if (!firstTry) cout << pstr("\nRestarting\n"); | |||
| firstTry = false; | |||
| cout << pstr("\nEnter the chip select pin number: "); | |||
| cin.readline(); | |||
| if (cin >> chipSelect) { | |||
| cout << chipSelect << endl; | |||
| } else { | |||
| cout << pstr("\nInvalid pin number\n"); | |||
| return; | |||
| } | |||
| if (DISABLE_CHIP_SELECT < 0) { | |||
| cout << pstr( | |||
| "\nAssuming the SD is the only SPI device.\n" | |||
| "Edit DISABLE_CHIP_SELECT to disable another device.\n"); | |||
| } else { | |||
| cout << pstr("\nDisabling SPI device on pin "); | |||
| cout << int(DISABLE_CHIP_SELECT) << endl; | |||
| pinMode(DISABLE_CHIP_SELECT, OUTPUT); | |||
| digitalWrite(DISABLE_CHIP_SELECT, HIGH); | |||
| } | |||
| if (!card.init(spiSpeed, chipSelect)) { | |||
| cout << pstr( | |||
| "\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 << pstr("errorCode: ") << hex << showbase << int(card.errorCode()); | |||
| cout << pstr(", errorData: ") << int(card.errorData()); | |||
| cout << dec << noshowbase << endl; | |||
| return; | |||
| } | |||
| cout << pstr("\nCard successfully initialized.\n"); | |||
| cout << endl; | |||
| uint32_t size = card.cardSize(); | |||
| if (size == 0) { | |||
| cout << pstr("Can't determine the card size.\n"); | |||
| cardOrSpeed(); | |||
| return; | |||
| } | |||
| uint32_t sizeMB = 0.000512 * size + 0.5; | |||
| cout << pstr("Card size: ") << sizeMB; | |||
| cout << pstr(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << endl; | |||
| if (!volume.init(&card)) { | |||
| if (card.errorCode()) { | |||
| cout << pstr("Can't read the card.\n"); | |||
| cardOrSpeed(); | |||
| } else { | |||
| cout << pstr("Can't find a valid FAT16/FAT32 partition.\n"); | |||
| reformatMsg(); | |||
| } | |||
| return; | |||
| } | |||
| cout << pstr("Volume is FAT") << int(volume.fatType()); | |||
| cout << pstr(", Cluster size (bytes): ") << 512L * volume.blocksPerCluster(); | |||
| cout << endl << endl; | |||
| root.close(); | |||
| if (!root.openRoot(&volume)) { | |||
| cout << pstr("Can't open root directory.\n"); | |||
| reformatMsg(); | |||
| return; | |||
| } | |||
| cout << pstr("Files found (name date time size):\n"); | |||
| root.ls(LS_R | LS_DATE | LS_SIZE); | |||
| if ((sizeMB > 1100 && volume.blocksPerCluster() < 64) | |||
| || (sizeMB < 2200 && volume.fatType() == 32)) { | |||
| cout << pstr("\nThis card should be reformatted for best performance.\n"); | |||
| cout << pstr("Use a cluster size of 32 KB for cards larger than 1 GB.\n"); | |||
| cout << pstr("Only cards larger than 2 GB should be formatted FAT32.\n"); | |||
| reformatMsg(); | |||
| return; | |||
| } | |||
| // read any existing Serial data | |||
| while (Serial.read() >= 0) {} | |||
| cout << pstr("\nSuccess! Type any character to restart.\n"); | |||
| while (Serial.read() < 0) {} | |||
| } | |||
| @@ -0,0 +1,174 @@ | |||
| /* | |||
| * This sketch illustrates raw write functions in SdFat that | |||
| * can be used for high speed data logging. These functions | |||
| * are used in the WaveRP library to record audio with the | |||
| * Adafruit Wave Shield using the built-in Arduino ADC. | |||
| * | |||
| * The WaveRP library captures data from the ADC in an ISR | |||
| * that is driven driven by timer one. Data is collected in | |||
| * two 512 byte buffers and written to the SD card. | |||
| * | |||
| * This sketch simulates logging from a source that produces | |||
| * data at a constant rate of one block every MICROS_PER_BLOCK. | |||
| * | |||
| * If a high quality SanDisk card is used with this sketch | |||
| * no overruns occur and the maximum block write time is | |||
| * under 2000 micros. | |||
| * | |||
| * Note: WaveRP creates a very large file then truncates it | |||
| * to the length that is used for a recording. It only takes | |||
| * a few seconds to erase a 500 MB file since the card only | |||
| * marks the blocks as erased; no data transfer is required. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // number of blocks in the contiguous file | |||
| const uint32_t BLOCK_COUNT = 10000UL; | |||
| // time to produce a block of data | |||
| const uint32_t MICROS_PER_BLOCK = 10000; | |||
| // file system | |||
| SdFat sd; | |||
| // test file | |||
| SdFile file; | |||
| // file extent | |||
| uint32_t bgnBlock, endBlock; | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| // log of first overruns | |||
| #define OVER_DIM 20 | |||
| struct { | |||
| uint32_t block; | |||
| uint32_t micros; | |||
| } over[OVER_DIM]; | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop(void) { | |||
| while (Serial.read() >= 0) {} | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| cout << pstr("Free RAM: ") << FreeRam() << endl; | |||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||
| // try SPI_HALF_SPEED if bus errors occur. | |||
| if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt(); | |||
| // delete possible existing file | |||
| sd.remove("RAW.TXT"); | |||
| // create a contiguous file | |||
| if (!file.createContiguous(sd.vwd(), "RAW.TXT", 512UL*BLOCK_COUNT)) { | |||
| error("createContiguous failed"); | |||
| } | |||
| // get the location of the file's blocks | |||
| if (!file.contiguousRange(&bgnBlock, &endBlock)) { | |||
| error("contiguousRange failed"); | |||
| } | |||
| //*********************NOTE************************************** | |||
| // NO SdFile calls are allowed while cache is used for raw writes | |||
| //*************************************************************** | |||
| // clear the cache and use it as a 512 byte buffer | |||
| uint8_t* pCache = (uint8_t*)sd.vol()->cacheClear(); | |||
| // fill cache with eight lines of 64 bytes each | |||
| memset(pCache, ' ', 512); | |||
| for (uint16_t i = 0; i < 512; i += 64) { | |||
| // put line number at end of line then CR/LF | |||
| pCache[i + 61] = '0' + (i/64); | |||
| pCache[i + 62] = '\r'; | |||
| pCache[i + 63] = '\n'; | |||
| } | |||
| cout << pstr("Start raw write of ") << file.fileSize() << pstr(" bytes at\n"); | |||
| cout << 512000000UL/MICROS_PER_BLOCK << pstr(" bytes per second\n"); | |||
| cout << pstr("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL; | |||
| cout << pstr(" seconds\n"); | |||
| // tell card to setup for multiple block write with pre-erase | |||
| if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) { | |||
| error("writeStart failed"); | |||
| } | |||
| // init stats | |||
| uint16_t overruns = 0; | |||
| uint32_t maxWriteTime = 0; | |||
| uint32_t t = micros(); | |||
| uint32_t tNext = t; | |||
| // write data | |||
| for (uint32_t b = 0; b < BLOCK_COUNT; b++) { | |||
| // write must be done by this time | |||
| tNext += MICROS_PER_BLOCK; | |||
| // put block number at start of first line in block | |||
| uint32_t n = b; | |||
| for (int8_t d = 5; d >= 0; d--){ | |||
| pCache[d] = n || d == 5 ? n % 10 + '0' : ' '; | |||
| n /= 10; | |||
| } | |||
| // write a 512 byte block | |||
| uint32_t tw = micros(); | |||
| if (!sd.card()->writeData(pCache)) error("writeData failed"); | |||
| tw = micros() - tw; | |||
| // check for max write time | |||
| if (tw > maxWriteTime) { | |||
| maxWriteTime = tw; | |||
| } | |||
| // check for overrun | |||
| if (micros() > tNext) { | |||
| if (overruns < OVER_DIM) { | |||
| over[overruns].block = b; | |||
| over[overruns].micros = tw; | |||
| } | |||
| overruns++; | |||
| // advance time to reflect overrun | |||
| tNext = micros(); | |||
| } | |||
| else { | |||
| // wait for time to write next block | |||
| while(micros() < tNext); | |||
| } | |||
| } | |||
| // total write time | |||
| t = micros() - t; | |||
| // end multiple block write mode | |||
| if (!sd.card()->writeStop()) error("writeStop failed"); | |||
| cout << pstr("Done\n"); | |||
| cout << pstr("Elapsed time: ") << setprecision(3)<< 1.e-6*t; | |||
| cout << pstr(" seconds\n"); | |||
| cout << pstr("Max write time: ") << maxWriteTime << pstr(" micros\n"); | |||
| cout << pstr("Overruns: ") << overruns << endl; | |||
| if (overruns) { | |||
| uint8_t n = overruns > OVER_DIM ? OVER_DIM : overruns; | |||
| cout << pstr("fileBlock,micros") << endl; | |||
| for (uint8_t i = 0; i < n; i++) { | |||
| cout << over[i].block << ',' << over[i].micros << endl; | |||
| } | |||
| } | |||
| // close file for next pass of loop | |||
| file.close(); | |||
| Serial.println(); | |||
| } | |||
| @@ -0,0 +1,68 @@ | |||
| // Ported to SdFat from the native Arduino SD library example by Bill Greiman | |||
| // On the Ethernet Shield, CS is pin 4. SdFat handles setting SS | |||
| const int chipSelect = 4; | |||
| /* | |||
| SD card read/write | |||
| This example shows how to read and write data to and from an SD card file | |||
| The circuit: | |||
| * SD card attached to SPI bus as follows: | |||
| ** MOSI - pin 11 | |||
| ** MISO - pin 12 | |||
| ** CLK - pin 13 | |||
| ** CS - pin 4 | |||
| created Nov 2010 | |||
| by David A. Mellis | |||
| updated 2 Dec 2010 | |||
| by Tom Igoe | |||
| modified by Bill Greiman 11 Apr 2011 | |||
| This example code is in the public domain. | |||
| */ | |||
| #include <SdFat.h> | |||
| SdFat sd; | |||
| SdFile myFile; | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| Serial.println("Type any character to start"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // Initialize SdFat or print a detailed error message and halt | |||
| // Use half speed like the native library. | |||
| // change to SPI_FULL_SPEED for more performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // open the file for write at end like the Native SD library | |||
| if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) { | |||
| sd.errorHalt("opening test.txt for write failed"); | |||
| } | |||
| // if the file opened okay, write to it: | |||
| Serial.print("Writing to test.txt..."); | |||
| myFile.println("testing 1, 2, 3."); | |||
| // close the file: | |||
| myFile.close(); | |||
| Serial.println("done."); | |||
| // re-open the file for reading: | |||
| if (!myFile.open("test.txt", O_READ)) { | |||
| sd.errorHalt("opening test.txt for read failed"); | |||
| } | |||
| Serial.println("test.txt:"); | |||
| // read from the file until there's nothing else in it: | |||
| int data; | |||
| while ((data = myFile.read()) >= 0) Serial.write(data); | |||
| // close the file: | |||
| myFile.close(); | |||
| } | |||
| void loop() { | |||
| // nothing happens after setup | |||
| } | |||
| @@ -0,0 +1,486 @@ | |||
| /* | |||
| * This sketch will format an SD or SDHC card. | |||
| * Warning all data will be deleted! | |||
| * | |||
| * For SD/SDHC cards larger than 64 MB this | |||
| * sketch attempts to match the format | |||
| * generated by SDFormatter available here: | |||
| * | |||
| * http://www.sdcard.org/consumers/formatter/ | |||
| * | |||
| * For smaller cards this sketch uses FAT16 | |||
| * and SDFormatter uses FAT12. | |||
| */ | |||
| // Print extra info for debug if DEBUG_PRINT is nonzero | |||
| #define DEBUG_PRINT 0 | |||
| #include <SdFat.h> | |||
| #if DEBUG_PRINT | |||
| #include <SdFatUtil.h> | |||
| #endif // DEBUG_PRINT | |||
| // | |||
| // 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; | |||
| // Change spiSpeed to SPI_FULL_SPEED for better performance | |||
| // Use SPI_QUARTER_SPEED for even slower SPI bus speed | |||
| const uint8_t spiSpeed = SPI_HALF_SPEED; | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| Sd2Card card; | |||
| uint32_t cardSizeBlocks; | |||
| uint16_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) sdError_P(PSTR(msg)) | |||
| void sdError_P(const char* str) { | |||
| cout << pstr("error: "); | |||
| cout << pgm(str) << endl; | |||
| if (card.errorCode()) { | |||
| cout << pstr("SD error: ") << hex << int(card.errorCode()); | |||
| cout << ',' << int(card.errorData()) << dec << endl; | |||
| } | |||
| while (1); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| #if DEBUG_PRINT | |||
| void debugPrint() { | |||
| cout << pstr("FreeRam: ") << FreeRam() << endl; | |||
| cout << pstr("partStart: ") << relSector << endl; | |||
| cout << pstr("partSize: ") << partSize << endl; | |||
| cout << pstr("reserved: ") << reservedSectors << endl; | |||
| cout << pstr("fatStart: ") << fatStart << endl; | |||
| cout << pstr("fatSize: ") << fatSize << endl; | |||
| cout << pstr("dataStart: ") << dataStart << endl; | |||
| cout << pstr("clusterCount: "); | |||
| cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl; | |||
| cout << endl; | |||
| cout << pstr("Heads: ") << int(numberOfHeads) << endl; | |||
| cout << pstr("Sectors: ") << int(sectorsPerTrack) << endl; | |||
| cout << pstr("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 << pstr("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 (!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"); | |||
| } | |||
| 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 << pstr("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 << pstr("All data set to ") << setw(4) << int(cache.data[0]) << endl; | |||
| cout << dec << noshowbase << setfill(' ') << right; | |||
| cout << pstr("Erase done\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void formatCard() { | |||
| cout << endl; | |||
| cout << pstr("Formatting\n"); | |||
| initSizes(); | |||
| if (card.type() != SD_CARD_TYPE_SDHC) { | |||
| cout << pstr("FAT16\n"); | |||
| makeFat16(); | |||
| } else { | |||
| cout << pstr("FAT32\n"); | |||
| makeFat32(); | |||
| } | |||
| #if DEBUG_PRINT | |||
| debugPrint(); | |||
| #endif // DEBUG_PRINT | |||
| cout << pstr("Format done\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| char c; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| cout << pstr( | |||
| "\n" | |||
| "This sketch 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()) {} | |||
| delay(400); // catch Due restart problem | |||
| c = Serial.read(); | |||
| cout << c << endl; | |||
| if (c != 'Y') { | |||
| cout << pstr("Quiting, you did not enter 'Y'.\n"); | |||
| return; | |||
| } | |||
| // read any existing Serial data | |||
| while (Serial.read() >= 0) {} | |||
| cout << pstr( | |||
| "\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()) {} | |||
| c = Serial.read(); | |||
| cout << c << endl; | |||
| if (!strchr("EFQ", c)) { | |||
| cout << pstr("Quiting, invalid option entered.") << endl; | |||
| return; | |||
| } | |||
| if (!card.init(spiSpeed, chipSelect)) { | |||
| cout << pstr( | |||
| "\nSD initialization failure!\n" | |||
| "Is the SD card inserted correctly?\n" | |||
| "Is chip select correct at the top of this sketch?\n"); | |||
| sdError("card.init failed"); | |||
| } | |||
| cardSizeBlocks = card.cardSize(); | |||
| if (cardSizeBlocks == 0) sdError("cardSize"); | |||
| cardCapacityMB = (cardSizeBlocks + 2047)/2048; | |||
| cout << pstr("Card Size: ") << cardCapacityMB; | |||
| cout << pstr(" MB, (MB = 1,048,576 bytes)") << endl; | |||
| if (c == 'E' || c == 'F') { | |||
| eraseCard(); | |||
| } | |||
| if (c == 'F' || c == 'Q') { | |||
| formatCard(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,197 @@ | |||
| /* | |||
| * This sketch attempts to initialize an SD card and analyze its structure. | |||
| */ | |||
| #include <SdFat.h> | |||
| /* | |||
| * 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 SdChipSelect = SS; | |||
| Sd2Card card; | |||
| SdVolume vol; | |||
| // 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) sdErrorMsg_P(PSTR(msg)); | |||
| void sdErrorMsg_P(const char* str) { | |||
| cout << pgm(str) << endl; | |||
| if (card.errorCode()) { | |||
| cout << pstr("SD errorCode: "); | |||
| cout << hex << int(card.errorCode()) << endl; | |||
| cout << pstr("SD errorData: "); | |||
| cout << int(card.errorData()) << dec << endl; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| uint8_t cidDmp() { | |||
| cid_t cid; | |||
| if (!card.readCID(&cid)) { | |||
| sdErrorMsg("readCID failed"); | |||
| return false; | |||
| } | |||
| cout << pstr("\nManufacturer ID: "); | |||
| cout << hex << int(cid.mid) << dec << endl; | |||
| cout << pstr("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl; | |||
| cout << pstr("Product: "); | |||
| for (uint8_t i = 0; i < 5; i++) { | |||
| cout << cid.pnm[i]; | |||
| } | |||
| cout << pstr("\nVersion: "); | |||
| cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl; | |||
| cout << pstr("Serial number: ") << hex << cid.psn << dec << endl; | |||
| cout << pstr("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 (!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 << pstr("csd version error\n"); | |||
| return false; | |||
| } | |||
| eraseSize++; | |||
| cout << pstr("cardSize: ") << 0.000512*cardSize; | |||
| cout << pstr(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << pstr("flashEraseSize: ") << int(eraseSize) << pstr(" blocks\n"); | |||
| cout << pstr("eraseSingleBlock: "); | |||
| if (eraseSingleBlock) { | |||
| cout << pstr("true\n"); | |||
| } else { | |||
| cout << pstr("false\n"); | |||
| } | |||
| return true; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // print partition table | |||
| uint8_t partDmp() { | |||
| cache_t *p = vol.cacheClear(); | |||
| if (!p) { | |||
| sdErrorMsg("cacheClear failed"); | |||
| return false; | |||
| } | |||
| if (!card.readBlock(0, p->data)) { | |||
| sdErrorMsg("read MBR failed"); | |||
| return false; | |||
| } | |||
| cout << pstr("\nSD Partition Table\n"); | |||
| cout << pstr("part,boot,type,start,length\n"); | |||
| for (uint8_t ip = 1; ip < 5; ip++) { | |||
| part_t *pt = &p->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 << pstr("\nVolume is FAT") << int(vol.fatType()) << endl; | |||
| cout << pstr("blocksPerCluster: ") << int(vol.blocksPerCluster()) << endl; | |||
| cout << pstr("clusterCount: ") << vol.clusterCount() << endl; | |||
| uint32_t volFree = vol.freeClusterCount(); | |||
| cout << pstr("freeClusters: ") << volFree << endl; | |||
| float fs = 0.000512*volFree*vol.blocksPerCluster(); | |||
| cout << pstr("freeSpace: ") << fs << pstr(" MB (MB = 1,000,000 bytes)\n"); | |||
| cout << pstr("fatStartBlock: ") << vol.fatStartBlock() << endl; | |||
| cout << pstr("fatCount: ") << int(vol.fatCount()) << endl; | |||
| cout << pstr("blocksPerFat: ") << vol.blocksPerFat() << endl; | |||
| cout << pstr("rootDirStart: ") << vol.rootDirStart() << endl; | |||
| cout << pstr("dataStartBlock: ") << vol.dataStartBlock() << endl; | |||
| if (vol.dataStartBlock() % eraseSize) { | |||
| cout << pstr("Data area is not aligned on flash erase boundaries!\n"); | |||
| cout << pstr("Download and use formatter from www.sdcard.org/consumer!\n"); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while(!Serial) {} // wait for Leonardo | |||
| // use uppercase in hex and use 0X base prefix | |||
| cout << uppercase << showbase << endl; | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("SdFat version: ") << SD_FAT_VERSION << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| // read any existing Serial data | |||
| while (Serial.read() >= 0) {} | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("\ntype any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| uint32_t t = millis(); | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!card.init(SPI_HALF_SPEED, SdChipSelect)) { | |||
| sdErrorMsg("\ncard.init failed"); | |||
| return; | |||
| } | |||
| t = millis() - t; | |||
| cardSize = card.cardSize(); | |||
| if (cardSize == 0) { | |||
| sdErrorMsg("cardSize failed"); | |||
| return; | |||
| } | |||
| cout << pstr("\ninit time: ") << t << " ms" << endl; | |||
| cout << pstr("\nCard type: "); | |||
| switch (card.type()) { | |||
| case SD_CARD_TYPE_SD1: | |||
| cout << pstr("SD1\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SD2: | |||
| cout << pstr("SD2\n"); | |||
| break; | |||
| case SD_CARD_TYPE_SDHC: | |||
| if (cardSize < 70000000) { | |||
| cout << pstr("SDHC\n"); | |||
| } else { | |||
| cout << pstr("SDXC\n"); | |||
| } | |||
| break; | |||
| default: | |||
| cout << pstr("Unknown\n"); | |||
| } | |||
| if (!cidDmp()) return; | |||
| if (!csdDmp()) return; | |||
| if (!partDmp()) return; | |||
| if (!vol.init(&card)) { | |||
| sdErrorMsg("\nvol.init failed"); | |||
| return; | |||
| } | |||
| volDmp(); | |||
| } | |||
| @@ -0,0 +1,201 @@ | |||
| #include <SdFat.h> | |||
| #include <StdioStream.h> | |||
| // Define PRINT_FIELD nonzero to use printField. | |||
| #define PRINT_FIELD 0 | |||
| // Number of lines to list on Serial. | |||
| #define STDIO_LIST_COUNT 0 | |||
| #define VERIFY_CONTENT 0 | |||
| const uint8_t SD_CS_PIN = SS; | |||
| SdFat sd; | |||
| SdFile printFile; | |||
| StdioStream stdioFile; | |||
| float f[100]; | |||
| char buf[20]; | |||
| char* label[] = | |||
| {"uint8_t 0 to 255, 100 times ", "uint16_t 0 to 20000", | |||
| "uint32_t 0 to 20000", "uint32_t 1000000000 to 1000010000", | |||
| "float nnn.ffff, 10000 times"}; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| uint32_t m; | |||
| uint32_t printSize; | |||
| uint32_t stdioSize; | |||
| uint32_t printTime; | |||
| uint32_t stdioTime; | |||
| Serial.begin(9600); | |||
| Serial.println(F("Type any character to start")); | |||
| while (!Serial.available()); | |||
| Serial.println(F("Starting test")); | |||
| if (!sd.begin(SD_CS_PIN)) sd.errorHalt(); | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| f[i] = 123.0 + 0.12345*i; | |||
| } | |||
| for (uint8_t dataType = 0; dataType < 5; dataType++) { | |||
| for (uint8_t fileType = 0; fileType < 2; fileType++) { | |||
| if (!fileType) { | |||
| if (!printFile.open("PRRINT.TXT", O_CREAT | O_RDWR | O_TRUNC)) { | |||
| Serial.println("open fail"); | |||
| return; | |||
| } | |||
| printTime = millis(); | |||
| switch (dataType) { | |||
| case 0: | |||
| for (uint16_t i =0; i < 100; i++) { | |||
| for (uint8_t j = 0; j < 255; j++) { | |||
| printFile.println(j); | |||
| } | |||
| } | |||
| break; | |||
| case 1: | |||
| for (uint16_t i = 0; i < 20000; i++) { | |||
| printFile.println(i); | |||
| } | |||
| break; | |||
| case 2: | |||
| for (uint32_t i = 0; i < 20000; i++) { | |||
| printFile.println(i); | |||
| } | |||
| break; | |||
| case 3: | |||
| for (uint16_t i = 0; i < 10000; i++) { | |||
| printFile.println(i + 1000000000UL); | |||
| } | |||
| break; | |||
| case 4: | |||
| for (int j = 0; j < 100; j++) { | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| printFile.println(f[i], 4); | |||
| } | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| printFile.sync(); | |||
| printTime = millis() - printTime; | |||
| printFile.rewind(); | |||
| printSize = printFile.fileSize(); | |||
| } else { | |||
| if (!stdioFile.fopen("STREAM.TXT", "w+")) { | |||
| Serial.println("fopen fail"); | |||
| return; | |||
| } | |||
| stdioTime = millis(); | |||
| switch (dataType) { | |||
| case 0: | |||
| for (uint16_t i =0; i < 100; i++) { | |||
| for (uint8_t j = 0; j < 255; j++) { | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(j, '\n'); | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(j); | |||
| #endif // PRINT_FIELD | |||
| } | |||
| } | |||
| break; | |||
| case 1: | |||
| for (uint16_t i = 0; i < 20000; i++) { | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(i, '\n'); | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(i); | |||
| #endif // PRINT_FIELD | |||
| } | |||
| break; | |||
| case 2: | |||
| for (uint32_t i = 0; i < 20000; i++) { | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(i, '\n'); | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(i); | |||
| #endif // PRINT_FIELD | |||
| } | |||
| break; | |||
| case 3: | |||
| for (uint16_t i = 0; i < 10000; i++) { | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(i + 1000000000UL, '\n'); | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(i + 1000000000UL); | |||
| #endif // PRINT_FIELD | |||
| } | |||
| break; | |||
| case 4: | |||
| for (int j = 0; j < 100; j++) { | |||
| for (uint8_t i = 0; i < 100; i++) { | |||
| #if PRINT_FIELD | |||
| stdioFile.printField(f[i], '\n', 4); | |||
| #else // PRINT_FIELD | |||
| stdioFile.println(f[i], 4); | |||
| #endif // PRINT_FIELD | |||
| } | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| stdioFile.fflush(); | |||
| stdioTime = millis() - stdioTime; | |||
| stdioSize = stdioFile.ftell(); | |||
| if (STDIO_LIST_COUNT) { | |||
| size_t len; | |||
| stdioFile.rewind(); | |||
| for (int i = 0; i < STDIO_LIST_COUNT; i++) { | |||
| stdioFile.fgets(buf, sizeof(buf), &len); | |||
| Serial.print(len);Serial.print(','); | |||
| Serial.print(buf); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| Serial.println(label[dataType]); | |||
| if (VERIFY_CONTENT && printSize == stdioSize) { | |||
| printFile.rewind(); | |||
| stdioFile.rewind(); | |||
| for (uint32_t i = 0; i < stdioSize; i++) { | |||
| if (printFile.read() != stdioFile.getc()) { | |||
| Serial.print(F("Files differ at pos: ")); | |||
| Serial.println(i); | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| Serial.print("fileSize: "); | |||
| if (printSize != stdioSize) { | |||
| Serial.print(printSize); | |||
| Serial.print(" != "); | |||
| } | |||
| Serial.println(stdioSize); | |||
| Serial.print("print millis: "); | |||
| Serial.println(printTime); | |||
| Serial.print("stdio millis: "); | |||
| Serial.println(stdioTime); | |||
| Serial.print("ratio: "); | |||
| Serial.println((float)printTime/(float)stdioTime); | |||
| Serial.println(); | |||
| printFile.close(); | |||
| stdioFile.fclose(); | |||
| } | |||
| Serial.println("Done"); | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,75 @@ | |||
| // This stress test will create and write files until the SD is full. | |||
| #include <SdFat.h> | |||
| // SD chip select pin. | |||
| const uint8_t SD_CS_PIN = SS; | |||
| // Set write buffer size. | |||
| #ifdef __arm__ | |||
| #ifndef CORE_TEENSY | |||
| // Due | |||
| const size_t BUF_SIZE = 32768; | |||
| #else // CORE_TEENSY | |||
| // Teensy 3.0 | |||
| const size_t BUF_SIZE = 8192; | |||
| #endif // CORE_TEENSY | |||
| #elif defined(RAMEND) && RAMEND > 5000 | |||
| // AVR with more than 4 KB RAM | |||
| const size_t BUF_SIZE = 4096; | |||
| #else // __arm__ | |||
| // other | |||
| const size_t BUF_SIZE = 512; | |||
| #endif // __arm__ | |||
| const size_t FILE_SIZE_KB = 10240; | |||
| const uint16_t BUFS_PER_FILE = (1024L*FILE_SIZE_KB/BUF_SIZE); | |||
| SdFat sd; | |||
| SdFile file; | |||
| uint8_t buf[BUF_SIZE]; | |||
| char name[13]; | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| Serial.print("BUF_SIZE "); | |||
| Serial.println(BUF_SIZE); | |||
| Serial.println("Type any character to start"); | |||
| while (Serial.read() < 0) {} | |||
| if (!sd.begin(SD_CS_PIN))sd.errorHalt("sd.begin"); | |||
| // Fill buf with known value. | |||
| for (size_t i = 0; i < BUF_SIZE; i++) buf[i] = i; | |||
| // Wait to begin. | |||
| do {delay(10);} while (Serial.read() >= 0); | |||
| Serial.println("Type any character to stop after next file"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| // Free KB on SD. | |||
| uint32_t freeKB = sd.vol()->freeClusterCount()*sd.vol()->blocksPerCluster()/2; | |||
| Serial.print("Free KB: "); | |||
| Serial.println(freeKB); | |||
| if (freeKB < 2*FILE_SIZE_KB) { | |||
| Serial.println(" Done!"); | |||
| while(1); | |||
| } | |||
| sprintf(name, "%lu.DAT", freeKB); | |||
| if (!file.open(name, O_WRITE | O_CREAT | O_TRUNC)) { | |||
| sd.errorHalt("Open error!"); | |||
| } | |||
| for (uint16_t i = 0; i < BUFS_PER_FILE; i++) { | |||
| if (file.write(buf, BUF_SIZE) != BUF_SIZE) { | |||
| sd.errorHalt("Write error!"); | |||
| } | |||
| } | |||
| file.close(); | |||
| if (Serial.available()) { | |||
| Serial.println("Stopped!"); | |||
| while(1); | |||
| } | |||
| } | |||
| @@ -0,0 +1,168 @@ | |||
| /* | |||
| * This sketch tests the dateTimeCallback() function | |||
| * and the timestamp() function. | |||
| */ | |||
| #include <SdFat.h> | |||
| SdFat sd; | |||
| SdFile file; | |||
| // Default SD chip select is SS pin | |||
| const uint8_t chipSelect = SS; | |||
| // create Serial stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| /* | |||
| * date/time values for debug | |||
| * normally supplied by a real-time clock or GPS | |||
| */ | |||
| // date 1-Oct-09 | |||
| uint16_t year = 2009; | |||
| uint8_t month = 10; | |||
| uint8_t day = 1; | |||
| // time 20:30:40 | |||
| uint8_t hour = 20; | |||
| uint8_t minute = 30; | |||
| uint8_t second = 40; | |||
| //------------------------------------------------------------------------------ | |||
| /* | |||
| * User provided date time callback function. | |||
| * See SdFile::dateTimeCallback() for usage. | |||
| */ | |||
| void dateTime(uint16_t* date, uint16_t* time) { | |||
| // User gets date and time from GPS or real-time | |||
| // clock in real callback function | |||
| // return date using FAT_DATE macro to format fields | |||
| *date = FAT_DATE(year, month, day); | |||
| // return time using FAT_TIME macro to format fields | |||
| *time = FAT_TIME(hour, minute, second); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /* | |||
| * Function to print all timestamps. | |||
| */ | |||
| void printTimestamps(SdFile& f) { | |||
| dir_t d; | |||
| if (!f.dirEntry(&d)) error("f.dirEntry failed"); | |||
| cout << pstr("Creation: "); | |||
| f.printFatDate(d.creationDate); | |||
| cout << ' '; | |||
| f.printFatTime(d.creationTime); | |||
| cout << endl; | |||
| cout << pstr("Modify: "); | |||
| f.printFatDate(d.lastWriteDate); | |||
| cout <<' '; | |||
| f.printFatTime(d.lastWriteTime); | |||
| cout << endl; | |||
| cout << pstr("Access: "); | |||
| f.printFatDate(d.lastAccessDate); | |||
| cout << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| cout << pstr("Type any character to start\n"); | |||
| while (!Serial.available()); | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // remove files if they exist | |||
| sd.remove("CALLBACK.TXT"); | |||
| sd.remove("DEFAULT.TXT"); | |||
| sd.remove("STAMP.TXT"); | |||
| // create a new file with default timestamps | |||
| if (!file.open("DEFAULT.TXT", O_CREAT | O_WRITE)) { | |||
| error("open DEFAULT.TXT failed"); | |||
| } | |||
| cout << pstr("\nOpen with default times\n"); | |||
| printTimestamps(file); | |||
| // close file | |||
| file.close(); | |||
| /* | |||
| * Test the date time callback function. | |||
| * | |||
| * dateTimeCallback() sets the function | |||
| * that is called when a file is created | |||
| * or when a file's directory entry is | |||
| * modified by sync(). | |||
| * | |||
| * The callback can be disabled by the call | |||
| * SdFile::dateTimeCallbackCancel() | |||
| */ | |||
| // set date time callback function | |||
| SdFile::dateTimeCallback(dateTime); | |||
| // create a new file with callback timestamps | |||
| if (!file.open("CALLBACK.TXT", O_CREAT | O_WRITE)) { | |||
| error("open CALLBACK.TXT failed"); | |||
| } | |||
| cout << ("\nOpen with callback times\n"); | |||
| printTimestamps(file); | |||
| // change call back date | |||
| day += 1; | |||
| // must add two to see change since FAT second field is 5-bits | |||
| second += 2; | |||
| // modify file by writing a byte | |||
| file.write('t'); | |||
| // force dir update | |||
| file.sync(); | |||
| cout << pstr("\nTimes after write\n"); | |||
| printTimestamps(file); | |||
| // close file | |||
| file.close(); | |||
| /* | |||
| * Test timestamp() function | |||
| * | |||
| * Cancel callback so sync will not | |||
| * change access/modify timestamp | |||
| */ | |||
| SdFile::dateTimeCallbackCancel(); | |||
| // create a new file with default timestamps | |||
| if (!file.open("STAMP.TXT", O_CREAT | O_WRITE)) { | |||
| error("open STAMP.TXT failed"); | |||
| } | |||
| // set creation date time | |||
| if (!file.timestamp(T_CREATE, 2009, 11, 10, 1, 2, 3)) { | |||
| error("set create time failed"); | |||
| } | |||
| // set write/modification date time | |||
| if (!file.timestamp(T_WRITE, 2009, 11, 11, 4, 5, 6)) { | |||
| error("set write time failed"); | |||
| } | |||
| // set access date | |||
| if (!file.timestamp(T_ACCESS, 2009, 11, 12, 7, 8, 9)) { | |||
| error("set access time failed"); | |||
| } | |||
| cout << pstr("\nTimes after timestamp() calls\n"); | |||
| printTimestamps(file); | |||
| file.close(); | |||
| cout << pstr("\nDone\n"); | |||
| } | |||
| void loop(void){} | |||
| @@ -0,0 +1,140 @@ | |||
| /* | |||
| * Example use of two SD cards. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| #if !USE_MULTIPLE_CARDS | |||
| #error You must set USE_MULTIPLE_CARDS nonzero in SdFatConfig.h | |||
| #endif | |||
| SdFat sd1; | |||
| const uint8_t SD1_CS = 10; // chip select for sd1 | |||
| SdFat sd2; | |||
| const uint8_t SD2_CS = 9; // 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_P(PSTR(msg)) | |||
| #define initError(msg) initErrorHalt_P(PSTR(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| PgmPrint("FreeRam: "); | |||
| Serial.println(FreeRam()); | |||
| // fill buffer with known data | |||
| for (int i = 0; i < sizeof(buf); i++) buf[i] = i; | |||
| PgmPrintln("type any character to start"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // disable sd2 while initializing sd1 | |||
| pinMode(SD2_CS, OUTPUT); | |||
| digitalWrite(SD2_CS, HIGH); | |||
| // initialize the first card | |||
| if (!sd1.begin(SD1_CS)) { | |||
| 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)) { | |||
| 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 | |||
| PgmPrintln("------sd1 root-------"); | |||
| sd1.ls(); | |||
| PgmPrintln("------sd2 root-------"); | |||
| sd2.ls(); | |||
| // make /DIR1 the default directory for sd1 | |||
| if (!sd1.chdir("/DIR1")) sd1.errorExit("sd1.chdir"); | |||
| // make /DIR2 the default directory for sd2 | |||
| if (!sd2.chdir("/DIR2")) sd2.errorExit("sd2.chdir"); | |||
| // list current directory on both cards | |||
| PgmPrintln("------sd1 DIR1-------"); | |||
| sd1.ls(); | |||
| PgmPrintln("------sd2 DIR2-------"); | |||
| sd2.ls(); | |||
| PgmPrintln("---------------------"); | |||
| // remove RENAME.BIN from /DIR2 directory of sd2 | |||
| if (sd2.exists("RENAME.BIN")) { | |||
| if (!sd2.remove("RENAME.BIN")) { | |||
| sd2.errorExit("remove RENAME.BIN"); | |||
| } | |||
| } | |||
| // 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"); | |||
| } | |||
| PgmPrintln("Writing TEST.BIN to sd1"); | |||
| // write data to /DIR1/TEST.BIN on sd1 | |||
| for (int 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_WRITE | O_CREAT | O_TRUNC)) { | |||
| sd2.errorExit("file2"); | |||
| } | |||
| PgmPrintln("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 (file2.write(buf, n) != n) sd2.errorExit("write2"); | |||
| } | |||
| t = millis() - t; | |||
| PgmPrint("File size: "); | |||
| Serial.println(file2.fileSize()); | |||
| PgmPrint("Copy time: "); | |||
| Serial.print(t); | |||
| PgmPrintln(" millis"); | |||
| // close TEST.BIN | |||
| file1.close(); | |||
| // rename the copy | |||
| file2.close(); | |||
| if (!sd2.rename("COPY.BIN", "RENAME.BIN")) { | |||
| sd2.errorExit("sd2.rename"); | |||
| } | |||
| PgmPrintln("Done"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() {} | |||
| @@ -0,0 +1,173 @@ | |||
| /* | |||
| * This sketch is a simple binary write/read benchmark. | |||
| */ | |||
| #include <SdFat.h> | |||
| #include <SdFatUtil.h> | |||
| // 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 = 10; | |||
| // Read pass count. | |||
| const uint8_t READ_COUNT = 5; | |||
| //============================================================================== | |||
| // End of configuration constants. | |||
| //------------------------------------------------------------------------------ | |||
| // File size in bytes. | |||
| const uint32_t FILE_SIZE = 1000000UL*FILE_SIZE_MB; | |||
| uint8_t buf[BUF_SIZE]; | |||
| // file system | |||
| SdFat sd; | |||
| // test file | |||
| SdFile file; | |||
| // Serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void cidDmp() { | |||
| cid_t cid; | |||
| if (!sd.card()->readCID(&cid)) { | |||
| error("readCID failed"); | |||
| } | |||
| cout << pstr("\nManufacturer ID: "); | |||
| cout << hex << int(cid.mid) << dec << endl; | |||
| cout << pstr("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl; | |||
| cout << pstr("Product: "); | |||
| for (uint8_t i = 0; i < 5; i++) { | |||
| cout << cid.pnm[i]; | |||
| } | |||
| cout << pstr("\nVersion: "); | |||
| cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl; | |||
| cout << pstr("Serial number: ") << hex << cid.psn << dec << endl; | |||
| cout << pstr("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); | |||
| while (!Serial){} // wait for Leonardo | |||
| cout << pstr("\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 | |||
| while (Serial.read() >= 0) {} | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| cout << pstr("Free RAM: ") << FreeRam() << endl; | |||
| // initialize the SD card at SPI_FULL_SPEED for best performance. | |||
| // try SPI_HALF_SPEED if bus errors occur. | |||
| if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt(); | |||
| cout << pstr("Type is FAT") << int(sd.vol()->fatType()) << endl; | |||
| cout << pstr("Card size: ") << sd.card()->cardSize()*512E-9; | |||
| cout << pstr(" GB (GB = 1E9 bytes)") << endl; | |||
| cidDmp(); | |||
| // open or create file - truncate existing file. | |||
| if (!file.open("BENCH.DAT", O_CREAT | O_TRUNC | O_RDWR)) { | |||
| error("open failed"); | |||
| } | |||
| // fill buf with known data | |||
| 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 << pstr("File size ") << FILE_SIZE_MB << pstr(" MB\n"); | |||
| cout << pstr("Buffer size ") << BUF_SIZE << pstr(" bytes\n"); | |||
| cout << pstr("Starting write test, please wait.") << endl << endl; | |||
| // do write test | |||
| uint32_t n = FILE_SIZE/sizeof(buf); | |||
| cout <<pstr("write speed and latency") << endl; | |||
| cout << pstr("speed,max,min,avg") << endl; | |||
| cout << pstr("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)) { | |||
| error("write failed"); | |||
| } | |||
| 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 << pstr("Starting read test, please wait.") << endl; | |||
| cout << endl <<pstr("read speed and latency") << endl; | |||
| cout << pstr("speed,max,min,avg") << endl; | |||
| cout << pstr("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(); | |||
| if (file.read(buf, sizeof(buf)) != sizeof(buf)) { | |||
| 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"); | |||
| } | |||
| } | |||
| t = millis() - t; | |||
| cout << s/t <<',' << maxLatency << ',' << minLatency; | |||
| cout << ',' << totalLatency/n << endl; | |||
| } | |||
| cout << endl << pstr("Done") << endl; | |||
| file.close(); | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| /* | |||
| * Demo of ArduinoInStream and ArduinoOutStream | |||
| */ | |||
| #include <SdFat.h> | |||
| // create serial output stream | |||
| ArduinoOutStream cout(Serial); | |||
| // input buffer for line | |||
| char cinBuf[40]; | |||
| // create serial input stream | |||
| ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf)); | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| int32_t n; | |||
| cout << "\nenter an integer\n"; | |||
| cin.readline(); | |||
| if (cin >> n) { | |||
| cout << "The number is: " << n; | |||
| } else { | |||
| // will fail if no digits or not in range [-2147483648, 2147483647] | |||
| cout << "Invalid input: " << cinBuf; | |||
| } | |||
| cout << endl; | |||
| } | |||
| @@ -0,0 +1,138 @@ | |||
| /* | |||
| * Simple data logger. | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin. Be sure to disable any other SPI devices such as Enet. | |||
| const uint8_t chipSelect = SS; | |||
| // Interval between data records in milliseconds. | |||
| // The interval must be greater than the maximum SD write latency plus the | |||
| // time to acquire and write data to the SD to avoid overrun errors. | |||
| // Run the bench example to check the quality of your SD card. | |||
| const uint32_t SAMPLE_INTERVAL_MS = 200; | |||
| // Log file base name. Must be six characters or less. | |||
| #define FILE_BASE_NAME "DATA" | |||
| //------------------------------------------------------------------------------ | |||
| // File system object. | |||
| SdFat sd; | |||
| // Log file. | |||
| SdFile file; | |||
| // Time in micros for next data record. | |||
| uint32_t logTime; | |||
| //============================================================================== | |||
| // User functions. Edit writeHeader() and logData() for your requirements. | |||
| const uint8_t ANALOG_COUNT = 4; | |||
| //------------------------------------------------------------------------------ | |||
| // Write data header. | |||
| void writeHeader() { | |||
| file.print(F("micros")); | |||
| for (uint8_t i = 0; i < ANALOG_COUNT; i++) { | |||
| file.print(F(",adc")); | |||
| file.print(i, DEC); | |||
| } | |||
| file.println(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Log a data record. | |||
| void logData() { | |||
| uint16_t data[ANALOG_COUNT]; | |||
| // Read all channels to avoid SD write latency between readings. | |||
| for (uint8_t i = 0; i < ANALOG_COUNT; i++) { | |||
| data[i] = analogRead(i); | |||
| } | |||
| // Write data to file. Start with log time in micros. | |||
| file.print(logTime); | |||
| // Write ADC data to CSV record. | |||
| for (uint8_t i = 0; i < ANALOG_COUNT; i++) { | |||
| file.write(','); | |||
| file.print(data[i]); | |||
| } | |||
| file.println(); | |||
| } | |||
| //============================================================================== | |||
| // Error messages stored in flash. | |||
| #define error(msg) error_P(PSTR(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void error_P(const char* msg) { | |||
| sd.errorHalt_P(msg); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1; | |||
| char fileName[13] = FILE_BASE_NAME "00.CSV"; | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(1000); | |||
| Serial.println(F("Type any character to start")); | |||
| while (!Serial.available()) {} | |||
| // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // Find an unused file name. | |||
| if (BASE_NAME_SIZE > 6) { | |||
| error("FILE_BASE_NAME too long"); | |||
| } | |||
| while (sd.exists(fileName)) { | |||
| if (fileName[BASE_NAME_SIZE + 1] != '9') { | |||
| fileName[BASE_NAME_SIZE + 1]++; | |||
| } else if (fileName[BASE_NAME_SIZE] != '9') { | |||
| fileName[BASE_NAME_SIZE + 1] = '0'; | |||
| fileName[BASE_NAME_SIZE]++; | |||
| } else { | |||
| error("Can't create file name"); | |||
| } | |||
| } | |||
| if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) error("file.open"); | |||
| do { | |||
| delay(10); | |||
| } while (Serial.read() >= 0); | |||
| Serial.print(F("Logging to: ")); | |||
| Serial.println(fileName); | |||
| Serial.println(F("Type any character to stop")); | |||
| // Write data header. | |||
| writeHeader(); | |||
| // Start on a multiple of the sample interval. | |||
| logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1; | |||
| logTime *= 1000UL*SAMPLE_INTERVAL_MS; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop() { | |||
| // Time for next record. | |||
| logTime += 1000UL*SAMPLE_INTERVAL_MS; | |||
| // Wait for log time. | |||
| int32_t diff; | |||
| do { | |||
| diff = micros() - logTime; | |||
| } while (diff < 0); | |||
| // Check for data rate too high. | |||
| if (diff > 10) error("Missed data record"); | |||
| logData(); | |||
| // Force data to SD and update the directory entry to avoid data loss. | |||
| if (!file.sync() || file.getWriteError()) error("write error"); | |||
| if (Serial.available()) { | |||
| // Close file and stop. | |||
| file.close(); | |||
| Serial.println(F("Done")); | |||
| while(1) {} | |||
| } | |||
| } | |||
| @@ -0,0 +1,108 @@ | |||
| /* | |||
| * Example use of chdir(), ls(), mkdir(), and rmdir(). | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD card chip select pin. | |||
| const uint8_t SD_CHIP_SELECT = SS; | |||
| //------------------------------------------------------------------------------ | |||
| // Permit SD to be wiped if ALLOW_WIPE is true. | |||
| const bool ALLOW_WIPE = false; | |||
| // File system object. | |||
| SdFat sd; | |||
| // 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) error_P(PSTR(msg)) | |||
| //------------------------------------------------------------------------------ | |||
| void error_P(const char* msg) { | |||
| sd.errorHalt_P(msg); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(1000); | |||
| cout << pstr("Type any character to start\n"); | |||
| // Wait for input line and discard. | |||
| cin.readline(); | |||
| // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // Check for empty SD. | |||
| if (file.openNext(sd.vwd(), O_READ)) { | |||
| cout << pstr("Found files/folders in the root directory.\n"); | |||
| if (!ALLOW_WIPE) { | |||
| error("SD not empty, use a blank SD or set ALLOW_WIPE true."); | |||
| } else { | |||
| cout << pstr("Type: 'WIPE' to delete all SD files.\n"); | |||
| char buf[10]; | |||
| cin.readline(); | |||
| cin.get(buf, sizeof(buf)); | |||
| if (cin.fail() || strncmp(buf, "WIPE", 4) || buf[4] >= ' ') { | |||
| error("Invalid WIPE input"); | |||
| } | |||
| file.close(); | |||
| sd.vwd()->rmRfStar(); | |||
| cout << pstr("***SD wiped clean.***\n\n"); | |||
| } | |||
| } | |||
| // Create a new folder. | |||
| if (!sd.mkdir("FOLDER1")) error("Create FOLDER1 failed"); | |||
| cout << pstr("Created FOLDER1\n"); | |||
| // Create a file in FOLDER1 using a path. | |||
| if (!file.open("FOLDER1/FILE1.TXT", O_CREAT | O_WRITE)) { | |||
| error("create FOLDER1/FILE1.TXT failed"); | |||
| } | |||
| file.close(); | |||
| cout << pstr("Created FOLDER1/FILE1.TXT\n"); | |||
| // Change volume working directory to FOLDER1. | |||
| if (!sd.chdir("FOLDER1")) error("chdir failed for FOLDER1.\n"); | |||
| cout << pstr("chdir to FOLDER1\n"); | |||
| // Create FILE2.TXT in current directory. | |||
| if (!file.open("FILE2.TXT", O_CREAT | O_WRITE)) { | |||
| error("create FILE2.TXT failed"); | |||
| } | |||
| file.close(); | |||
| cout << pstr("Created FILE2.TXT in current directory\n"); | |||
| cout << pstr("List 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 << pstr("\nFILE1.TXT and FILE2.TXT removed.\n"); | |||
| // Change current directory to root. | |||
| if (!sd.chdir()) error("chdir to root failed.\n"); | |||
| cout << pstr("List of files on the SD.\n"); | |||
| sd.ls(LS_R); | |||
| // Remove FOLDER1. | |||
| if (!sd.rmdir("FOLDER1")) error("rmdir for FOLDER1 failed\n"); | |||
| cout << pstr("\nFOLDER1 removed, SD empty.\n"); | |||
| cout << pstr("Done!\n"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Nothing happens in loop. | |||
| void loop() {} | |||
| @@ -0,0 +1,74 @@ | |||
| // Demo of fgets function to read lines from a file. | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| SdFat sd; | |||
| // print stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash memory | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void demoFgets() { | |||
| char line[25]; | |||
| int n; | |||
| // open test file | |||
| SdFile rdfile("FGETS.TXT", O_READ); | |||
| // check for open error | |||
| if (!rdfile.isOpen()) error("demoFgets"); | |||
| cout << endl << pstr( | |||
| "Lines with '>' end with a '\\n' character\n" | |||
| "Lines with '#' do not end with a '\\n' character\n" | |||
| "\n"); | |||
| // read lines from the file | |||
| while ((n = rdfile.fgets(line, sizeof(line))) > 0) { | |||
| if (line[n - 1] == '\n') { | |||
| cout << '>' << line; | |||
| } else { | |||
| cout << '#' << line << endl; | |||
| } | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void makeTestFile() { | |||
| // create or open test file | |||
| SdFile wrfile("FGETS.TXT", O_WRITE | O_CREAT | O_TRUNC); | |||
| // check for open error | |||
| if (!wrfile.isOpen()) error("MakeTestFile"); | |||
| // write test file | |||
| wrfile.write_P(PSTR( | |||
| "Line with CRLF\r\n" | |||
| "Line with only LF\n" | |||
| "Long line that will require an extra read\n" | |||
| "\n" // empty line | |||
| "Line at EOF without NL" | |||
| )); | |||
| wrfile.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // Wait for Leonardo | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| makeTestFile(); | |||
| demoFgets(); | |||
| cout << pstr("\nDone\n"); | |||
| } | |||
| void loop(void) {} | |||
| @@ -0,0 +1,66 @@ | |||
| /* | |||
| * Print a table with various formatting options | |||
| * Format dates | |||
| */ | |||
| #include <SdFat.h> | |||
| // create Serial stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| // print a table to demonstrate format manipulators | |||
| void example(void) { | |||
| const int max = 10; | |||
| const int width = 4; | |||
| for (int row = 1; row <= max; row++) { | |||
| for (int col = 1; col <= max; col++) { | |||
| cout << setw(width) << row * col << (col == max ? '\n' : ' '); | |||
| } | |||
| } | |||
| cout << endl; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // print a date as mm/dd/yyyy with zero fill in mm and dd | |||
| // shows how to set and restore the fill character | |||
| void showDate(int m, int d, int y) { | |||
| // convert two digit year | |||
| if (y < 100) y += 2000; | |||
| // set new fill to '0' save old fill character | |||
| char old = cout.fill('0'); | |||
| // print date | |||
| cout << setw(2) << m << '/' << setw(2) << d << '/' << y << endl; | |||
| // restore old fill character | |||
| cout.fill(old); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| delay(2000); | |||
| cout << endl << "default formatting" << endl; | |||
| example(); | |||
| cout << showpos << "showpos" << endl; | |||
| example(); | |||
| cout << hex << left << showbase << "hex left showbase" << endl; | |||
| example(); | |||
| cout << internal << setfill('0') << uppercase; | |||
| cout << "uppercase hex internal showbase fill('0')" <<endl; | |||
| example(); | |||
| // restore default format flags and fill character | |||
| cout.flags(ios::dec | ios::right | ios::skipws); | |||
| cout.fill(' '); | |||
| cout << "showDate example" <<endl; | |||
| showDate(7, 4, 11); | |||
| showDate(12, 25, 11); | |||
| } | |||
| void loop(void) {} | |||
| @@ -0,0 +1,75 @@ | |||
| /* | |||
| * Example of getline from section 27.7.1.3 of the C++ standard | |||
| * Demonstrates the behavior of getline for various exceptions. | |||
| * See http://www.cplusplus.com/reference/iostream/istream/getline/ | |||
| * | |||
| * Note: This example is meant to demonstrate subtleties the standard and | |||
| * may not the best way to read a file. | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system object | |||
| SdFat sd; | |||
| // create a serial stream | |||
| ArduinoOutStream cout(Serial); | |||
| //------------------------------------------------------------------------------ | |||
| void makeTestFile() { | |||
| ofstream sdout("GETLINE.TXT"); | |||
| // use flash for text to save RAM | |||
| sdout << pstr( | |||
| "short line\n" | |||
| "\n" | |||
| "17 character line\n" | |||
| "too long for buffer\n" | |||
| "line with no nl"); | |||
| sdout.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void testGetline() { | |||
| const int line_buffer_size = 18; | |||
| char buffer[line_buffer_size]; | |||
| ifstream sdin("GETLINE.TXT"); | |||
| int line_number = 0; | |||
| while (sdin.getline(buffer, line_buffer_size, '\n') || sdin.gcount()) { | |||
| int count = sdin.gcount(); | |||
| if (sdin.fail()) { | |||
| cout << "Partial long line"; | |||
| sdin.clear(sdin.rdstate() & ~ios_base::failbit); | |||
| } else if (sdin.eof()) { | |||
| cout << "Partial final line"; // sdin.fail() is false | |||
| } else { | |||
| count--; // Don’t include newline in count | |||
| cout << "Line " << ++line_number; | |||
| } | |||
| cout << " (" << count << " chars): " << buffer << endl; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup(void) { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| // pstr stores strings in flash to save RAM | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // make the test file | |||
| makeTestFile(); | |||
| // run the example | |||
| testGetline(); | |||
| cout << "\nDone!\n"; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void loop(void) {} | |||
| @@ -0,0 +1,100 @@ | |||
| /* | |||
| * This example reads a simple CSV, comma-separated values, file. | |||
| * Each line of the file has three values, a long and two floats. | |||
| */ | |||
| #include <SdFat.h> | |||
| // SD chip select pin | |||
| const uint8_t chipSelect = SS; | |||
| // file system object | |||
| SdFat sd; | |||
| // create Serial stream | |||
| ArduinoOutStream cout(Serial); | |||
| char fileName[] = "TESTFILE.CSV"; | |||
| //------------------------------------------------------------------------------ | |||
| // store error strings in flash to save RAM | |||
| #define error(s) sd.errorHalt_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| // read and print CSV test file | |||
| void readFile() { | |||
| long lg; | |||
| float f1, f2; | |||
| char text[10]; | |||
| char c1, c2, c3; // space for commas. | |||
| // open input file | |||
| ifstream sdin(fileName); | |||
| // check for open error | |||
| if (!sdin.is_open()) error("open"); | |||
| // read until input fails | |||
| while (1) { | |||
| // Get text field. | |||
| sdin.get(text, sizeof(text), ','); | |||
| // Assume EOF if fail. | |||
| if (sdin.fail()) break; | |||
| // Get commas and numbers. | |||
| sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2; | |||
| // Skip CR/LF. | |||
| sdin.skipWhite(); | |||
| if (sdin.fail()) error("bad input"); | |||
| // error in line if not commas | |||
| if (c1 != ',' || c2 != ',' || c3 != ',') error("comma"); | |||
| // print in six character wide columns | |||
| cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl; | |||
| } | |||
| // Error in an input line if file is not at EOF. | |||
| if (!sdin.eof()) error("readFile"); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // write test file | |||
| void writeFile() { | |||
| // create or open and truncate output file | |||
| ofstream sdout(fileName); | |||
| // write file from string stored in flash | |||
| sdout << pstr( | |||
| "Line 1,1,2.3,4.5\n" | |||
| "Line 2,6,7.8,9.0\n" | |||
| "Line 3,9,8.7,6.5\n" | |||
| "Line 4,-4,-3.2,-1\n") << flush; | |||
| // check for any errors | |||
| if (!sdout) error("writeFile"); | |||
| sdout.close(); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| cout << pstr("Type any character to start\n"); | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // create test file | |||
| writeFile(); | |||
| cout << endl; | |||
| // read and print test | |||
| readFile(); | |||
| cout << "\nDone!" << endl; | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,77 @@ | |||
| /* | |||
| * This sketch demonstrates use of SdFile::rename() | |||
| * and SdFat::rename(). | |||
| */ | |||
| #include <SdFat.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_P(PSTR(s)) | |||
| //------------------------------------------------------------------------------ | |||
| void setup() { | |||
| Serial.begin(9600); | |||
| while (!Serial) {} // wait for Leonardo | |||
| cout << pstr("Insert an empty SD. Type any character to start.") << endl; | |||
| while (Serial.read() <= 0) {} | |||
| delay(400); // catch Due reset problem | |||
| // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with | |||
| // breadboards. use SPI_FULL_SPEED for better performance. | |||
| if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt(); | |||
| // create a file and write one line to the file | |||
| SdFile file("NAME1.TXT", O_WRITE | O_CREAT); | |||
| if (!file.isOpen()) error("NAME1"); | |||
| 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")) error("NAME2"); | |||
| file.println("A test line for NAME2.TXT"); | |||
| // list files | |||
| cout << pstr("------") << 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(sd.vwd(), "DIR1/NAME3.TXT")) error("NAME3"); | |||
| file.println("A line for DIR1/NAME3.TXT"); | |||
| // list files | |||
| cout << pstr("------") << 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_WRITE | O_APPEND)) { | |||
| error("DIR2/DIR3/NAME3.TXT"); | |||
| } | |||
| file.println("A line for DIR2/DIR3/NAME3.TXT"); | |||
| // list files | |||
| cout << pstr("------") << endl; | |||
| sd.ls(LS_R); | |||
| cout << pstr("Done") << endl; | |||
| } | |||
| void loop() {} | |||
| @@ -0,0 +1,394 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef ios_h | |||
| #define ios_h | |||
| #include <SdBaseFile.h> | |||
| /** | |||
| * \file | |||
| * \brief \ref ios_base and \ref ios classes | |||
| */ | |||
| //============================================================================== | |||
| /** | |||
| * \class ios_base | |||
| * \brief Base class for all streams | |||
| */ | |||
| class ios_base { | |||
| public: | |||
| /** typedef for iostate bitmask */ | |||
| typedef unsigned char iostate; | |||
| // State flags. | |||
| /** iostate for no flags */ | |||
| static const iostate goodbit = 0x00; | |||
| /** iostate bad bit for a nonrecoverable error. */ | |||
| static const iostate badbit = 0X01; | |||
| /** iostate bit for end of file reached */ | |||
| static const iostate eofbit = 0x02; | |||
| /** iostate fail bit for nonfatal error */ | |||
| static const iostate failbit = 0X04; | |||
| /** | |||
| * unsigned size that can represent maximum file size. | |||
| * (violates spec - should be signed) | |||
| */ | |||
| typedef uint32_t streamsize; | |||
| /** type for absolute seek position */ | |||
| typedef uint32_t pos_type; | |||
| /** type for relative seek offset */ | |||
| typedef int32_t off_type; | |||
| /** enumerated type for the direction of relative seeks */ | |||
| enum seekdir { | |||
| /** seek relative to the beginning of the stream */ | |||
| beg, | |||
| /** seek relative to the current stream position */ | |||
| cur, | |||
| /** seek relative to the end of the stream */ | |||
| end | |||
| }; | |||
| /** type for format flags */ | |||
| typedef unsigned int fmtflags; | |||
| /** left adjust fields */ | |||
| static const fmtflags left = 0x0001; | |||
| /** right adjust fields */ | |||
| static const fmtflags right = 0x0002; | |||
| /** fill between sign/base prefix and number */ | |||
| static const fmtflags internal = 0x0004; | |||
| /** base 10 flag*/ | |||
| static const fmtflags dec = 0x0008; | |||
| /** base 16 flag */ | |||
| static const fmtflags hex = 0x0010; | |||
| /** base 8 flag */ | |||
| static const fmtflags oct = 0x0020; | |||
| // static const fmtflags fixed = 0x0040; | |||
| // static const fmtflags scientific = 0x0080; | |||
| /** use strings true/false for bool */ | |||
| static const fmtflags boolalpha = 0x0100; | |||
| /** use prefix 0X for hex and 0 for oct */ | |||
| static const fmtflags showbase = 0x0200; | |||
| /** always show '.' for floating numbers */ | |||
| static const fmtflags showpoint = 0x0400; | |||
| /** show + sign for nonnegative numbers */ | |||
| static const fmtflags showpos = 0x0800; | |||
| /** skip initial white space */ | |||
| static const fmtflags skipws = 0x1000; | |||
| // static const fmtflags unitbuf = 0x2000; | |||
| /** use uppercase letters in number representations */ | |||
| static const fmtflags uppercase = 0x4000; | |||
| /** mask for adjustfield */ | |||
| static const fmtflags adjustfield = left | right | internal; | |||
| /** mask for basefield */ | |||
| static const fmtflags basefield = dec | hex | oct; | |||
| // static const fmtflags floatfield = scientific | fixed; | |||
| //---------------------------------------------------------------------------- | |||
| /** typedef for iostream open mode */ | |||
| typedef uint8_t openmode; | |||
| // Openmode flags. | |||
| /** seek to end before each write */ | |||
| static const openmode app = 0X4; | |||
| /** open and seek to end immediately after opening */ | |||
| static const openmode ate = 0X8; | |||
| /** perform input and output in binary mode (as opposed to text mode) */ | |||
| static const openmode binary = 0X10; | |||
| /** open for input */ | |||
| static const openmode in = 0X20; | |||
| /** open for output */ | |||
| static const openmode out = 0X40; | |||
| /** truncate an existing stream when opening */ | |||
| static const openmode trunc = 0X80; | |||
| //---------------------------------------------------------------------------- | |||
| ios_base() : m_fill(' '), m_fmtflags(dec | right | skipws) | |||
| , m_precision(2), m_width(0) {} | |||
| /** \return fill character */ | |||
| char fill() {return m_fill;} | |||
| /** Set fill character | |||
| * \param[in] c new fill character | |||
| * \return old fill character | |||
| */ | |||
| char fill(char c) { | |||
| char r = m_fill; | |||
| m_fill = c; | |||
| return r; | |||
| } | |||
| /** \return format flags */ | |||
| fmtflags flags() const {return m_fmtflags;} | |||
| /** set format flags | |||
| * \param[in] fl new flag | |||
| * \return old flags | |||
| */ | |||
| fmtflags flags(fmtflags fl) { | |||
| fmtflags tmp = m_fmtflags; | |||
| m_fmtflags = fl; | |||
| return tmp; | |||
| } | |||
| /** \return precision */ | |||
| int precision() const {return m_precision;} | |||
| /** set precision | |||
| * \param[in] n new precision | |||
| * \return old precision | |||
| */ | |||
| int precision(unsigned int n) { | |||
| int r = m_precision; | |||
| m_precision = n; | |||
| return r; | |||
| } | |||
| /** set format flags | |||
| * \param[in] fl new flags to be or'ed in | |||
| * \return old flags | |||
| */ | |||
| fmtflags setf(fmtflags fl) { | |||
| fmtflags r = m_fmtflags; | |||
| m_fmtflags |= fl; | |||
| return r; | |||
| } | |||
| /** modify format flags | |||
| * \param[in] mask flags to be removed | |||
| * \param[in] fl flags to be set after mask bits have been cleared | |||
| * \return old flags | |||
| */ | |||
| fmtflags setf(fmtflags fl, fmtflags mask) { | |||
| fmtflags r = m_fmtflags; | |||
| m_fmtflags &= ~mask; | |||
| m_fmtflags |= fl; | |||
| return r; | |||
| } | |||
| /** clear format flags | |||
| * \param[in] fl flags to be cleared | |||
| * \return old flags | |||
| */ | |||
| void unsetf(fmtflags fl) { | |||
| m_fmtflags &= ~fl; | |||
| } | |||
| /** \return width */ | |||
| unsigned width() {return m_width;} | |||
| /** set width | |||
| * \param[in] n new width | |||
| * \return old width | |||
| */ | |||
| unsigned width(unsigned n) { | |||
| unsigned r = m_width; | |||
| m_width = n; | |||
| return r; | |||
| } | |||
| protected: | |||
| /** \return current number base */ | |||
| uint8_t flagsToBase() { | |||
| uint8_t f = flags() & basefield; | |||
| return f == oct ? 8 : f != hex ? 10 : 16; | |||
| } | |||
| private: | |||
| char m_fill; | |||
| fmtflags m_fmtflags; | |||
| unsigned char m_precision; | |||
| unsigned int m_width; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| /** function for boolalpha manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& boolalpha(ios_base& str) { | |||
| str.setf(ios_base::boolalpha); | |||
| return str; | |||
| } | |||
| /** function for dec manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& dec(ios_base& str) { | |||
| str.setf(ios_base::dec, ios_base::basefield); | |||
| return str; | |||
| } | |||
| /** function for hex manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& hex(ios_base& str) { | |||
| str.setf(ios_base::hex, ios_base::basefield); | |||
| return str; | |||
| } | |||
| /** function for internal manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& internal(ios_base& str) { | |||
| str.setf(ios_base::internal, ios_base::adjustfield); | |||
| return str; | |||
| } | |||
| /** function for left manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& left(ios_base& str) { | |||
| str.setf(ios_base::left, ios_base::adjustfield); | |||
| return str; | |||
| } | |||
| /** function for noboolalpha manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& noboolalpha(ios_base& str) { | |||
| str.unsetf(ios_base::boolalpha); | |||
| return str; | |||
| } | |||
| /** function for noshowbase manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& noshowbase(ios_base& str) { | |||
| str.unsetf(ios_base::showbase); | |||
| return str; | |||
| } | |||
| /** function for noshowpoint manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& noshowpoint(ios_base& str) { | |||
| str.unsetf(ios_base::showpoint); | |||
| return str; | |||
| } | |||
| /** function for noshowpos manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& noshowpos(ios_base& str) { | |||
| str.unsetf(ios_base::showpos); | |||
| return str; | |||
| } | |||
| /** function for noskipws manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& noskipws(ios_base& str) { | |||
| str.unsetf(ios_base::skipws); | |||
| return str; | |||
| } | |||
| /** function for nouppercase manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& nouppercase(ios_base& str) { | |||
| str.unsetf(ios_base::uppercase); | |||
| return str; | |||
| } | |||
| /** function for oct manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& oct(ios_base& str) { | |||
| str.setf(ios_base::oct, ios_base::basefield); | |||
| return str; | |||
| } | |||
| /** function for right manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& right(ios_base& str) { | |||
| str.setf(ios_base::right, ios_base::adjustfield); | |||
| return str; | |||
| } | |||
| /** function for showbase manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& showbase(ios_base& str) { | |||
| str.setf(ios_base::showbase); | |||
| return str; | |||
| } | |||
| /** function for showpos manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& showpos(ios_base& str) { | |||
| str.setf(ios_base::showpos); | |||
| return str; | |||
| } | |||
| /** function for showpoint manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& showpoint(ios_base& str) { | |||
| str.setf(ios_base::showpoint); | |||
| return str; | |||
| } | |||
| /** function for skipws manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& skipws(ios_base& str) { | |||
| str.setf(ios_base::skipws); | |||
| return str; | |||
| } | |||
| /** function for uppercase manipulator | |||
| * \param[in] str The stream | |||
| * \return The stream | |||
| */ | |||
| inline ios_base& uppercase(ios_base& str) { | |||
| str.setf(ios_base::uppercase); | |||
| return str; | |||
| } | |||
| //============================================================================== | |||
| /** | |||
| * \class ios | |||
| * \brief Error and state information for all streams | |||
| */ | |||
| class ios : public ios_base { | |||
| public: | |||
| /** Create ios with no error flags set */ | |||
| ios() : m_iostate(0) {} | |||
| /** \return null pointer if fail() is true. */ | |||
| operator const void*() const { | |||
| return !fail() ? reinterpret_cast<const void*>(this) : 0; | |||
| } | |||
| /** \return true if fail() else false. */ | |||
| bool operator!() const {return fail();} | |||
| /** \return The iostate flags for this file. */ | |||
| iostate rdstate() const {return m_iostate;} | |||
| /** \return True if no iostate flags are set else false. */ | |||
| bool good() const {return m_iostate == goodbit;} | |||
| /** \return true if end of file has been reached else false. | |||
| * | |||
| * Warning: An empty file returns false before the first read. | |||
| * | |||
| * Moral: eof() is only useful in combination with fail(), to find out | |||
| * whether EOF was the cause for failure | |||
| */ | |||
| bool eof() const {return m_iostate & eofbit;} | |||
| /** \return true if any iostate bit other than eof are set else false. */ | |||
| bool fail() const {return m_iostate & (failbit | badbit);} | |||
| /** \return true if bad bit is set else false. */ | |||
| bool bad() const {return m_iostate & badbit;} | |||
| /** Clear iostate bits. | |||
| * | |||
| * \param[in] state The flags you want to set after clearing all flags. | |||
| **/ | |||
| void clear(iostate state = goodbit) {m_iostate = state;} | |||
| /** Set iostate bits. | |||
| * | |||
| * \param[in] state Bitts to set. | |||
| **/ | |||
| void setstate(iostate state) {m_iostate |= state;} | |||
| private: | |||
| iostate m_iostate; | |||
| }; | |||
| #endif // ios_h | |||
| @@ -0,0 +1,153 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef iostream_h | |||
| #define iostream_h | |||
| /** | |||
| * \file | |||
| * \brief \ref iostream class | |||
| */ | |||
| #include <istream.h> | |||
| #include <ostream.h> | |||
| /** Skip white space | |||
| * \param[in] is the Stream | |||
| * \return The stream | |||
| */ | |||
| inline istream& ws(istream& is) { | |||
| is.skipWhite(); | |||
| return is; | |||
| } | |||
| /** insert endline | |||
| * \param[in] os The Stream | |||
| * \return The stream | |||
| */ | |||
| inline ostream& endl(ostream& os) { | |||
| os.put('\n'); | |||
| #if ENDL_CALLS_FLUSH | |||
| os.flush(); | |||
| #endif // ENDL_CALLS_FLUSH | |||
| return os; | |||
| } | |||
| /** flush manipulator | |||
| * \param[in] os The stream | |||
| * \return The stream | |||
| */ | |||
| inline ostream& flush(ostream& os) { | |||
| os.flush(); | |||
| return os; | |||
| } | |||
| /** | |||
| * \struct setfill | |||
| * \brief type for setfill manipulator | |||
| */ | |||
| struct setfill { | |||
| /** fill character */ | |||
| char c; | |||
| /** constructor | |||
| * | |||
| * \param[in] arg new fill character | |||
| */ | |||
| explicit setfill(char arg) : c(arg) {} | |||
| }; | |||
| /** setfill manipulator | |||
| * \param[in] os the stream | |||
| * \param[in] arg set setfill object | |||
| * \return the stream | |||
| */ | |||
| inline ostream &operator<< (ostream &os, const setfill &arg) { | |||
| os.fill(arg.c); | |||
| return os; | |||
| } | |||
| /** setfill manipulator | |||
| * \param[in] obj the stream | |||
| * \param[in] arg set setfill object | |||
| * \return the stream | |||
| */ | |||
| inline istream &operator>>(istream &obj, const setfill &arg) { | |||
| obj.fill(arg.c); | |||
| return obj; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** \struct setprecision | |||
| * \brief type for setprecision manipulator | |||
| */ | |||
| struct setprecision { | |||
| /** precision */ | |||
| unsigned int p; | |||
| /** constructor | |||
| * \param[in] arg new precision | |||
| */ | |||
| explicit setprecision(unsigned int arg) : p(arg) {} | |||
| }; | |||
| /** setprecision manipulator | |||
| * \param[in] os the stream | |||
| * \param[in] arg set setprecision object | |||
| * \return the stream | |||
| */ | |||
| inline ostream &operator<< (ostream &os, const setprecision &arg) { | |||
| os.precision(arg.p); | |||
| return os; | |||
| } | |||
| /** setprecision manipulator | |||
| * \param[in] is the stream | |||
| * \param[in] arg set setprecision object | |||
| * \return the stream | |||
| */ | |||
| inline istream &operator>>(istream &is, const setprecision &arg) { | |||
| is.precision(arg.p); | |||
| return is; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** \struct setw | |||
| * \brief type for setw manipulator | |||
| */ | |||
| struct setw { | |||
| /** width */ | |||
| unsigned w; | |||
| /** constructor | |||
| * \param[in] arg new width | |||
| */ | |||
| explicit setw(unsigned arg) : w(arg) {} | |||
| }; | |||
| /** setw manipulator | |||
| * \param[in] os the stream | |||
| * \param[in] arg set setw object | |||
| * \return the stream | |||
| */ | |||
| inline ostream &operator<< (ostream &os, const setw &arg) { | |||
| os.width(arg.w); | |||
| return os; | |||
| } | |||
| /** setw manipulator | |||
| * \param[in] is the stream | |||
| * \param[in] arg set setw object | |||
| * \return the stream | |||
| */ | |||
| inline istream &operator>>(istream &is, const setw &arg) { | |||
| is.width(arg.w); | |||
| return is; | |||
| } | |||
| //============================================================================== | |||
| /** | |||
| * \class iostream | |||
| * \brief Input/Output stream | |||
| */ | |||
| class iostream : public istream, public ostream { | |||
| }; | |||
| #endif // iostream_h | |||
| @@ -0,0 +1,411 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <float.h> | |||
| #include <istream.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Extract a character if one is available. | |||
| * | |||
| * \return The character or -1 if a failure occurs. A failure is indicated | |||
| * by the stream state. | |||
| */ | |||
| int istream::get() { | |||
| int c; | |||
| m_gcount = 0; | |||
| c = getch(); | |||
| if (c < 0) { | |||
| setstate(failbit); | |||
| } else { | |||
| m_gcount = 1; | |||
| } | |||
| return c; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Extract a character if one is available. | |||
| * | |||
| * \param[out] c location to receive the extracted character. | |||
| * | |||
| * \return always returns *this. A failure is indicated by the stream state. | |||
| */ | |||
| istream& istream::get(char& c) { | |||
| int tmp = get(); | |||
| if (tmp >= 0) c = tmp; | |||
| return *this; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Extract characters. | |||
| * | |||
| * \param[out] str Location to receive extracted characters. | |||
| * \param[in] n Size of str. | |||
| * \param[in] delim Delimiter | |||
| * | |||
| * Characters are extracted until extraction fails, n is less than 1, | |||
| * n-1 characters are extracted, or the next character equals | |||
| * \a delim (delim is not extracted). If no characters are extracted | |||
| * failbit is set. If end-of-file occurs the eofbit is set. | |||
| * | |||
| * \return always returns *this. A failure is indicated by the stream state. | |||
| */ | |||
| istream& istream::get(char *str, streamsize n, char delim) { | |||
| int c; | |||
| FatPos_t pos; | |||
| m_gcount = 0; | |||
| while ((m_gcount + 1) < n) { | |||
| c = getch(&pos); | |||
| if (c < 0) { | |||
| break; | |||
| } | |||
| if (c == delim) { | |||
| setpos(&pos); | |||
| break; | |||
| } | |||
| str[m_gcount++] = c; | |||
| } | |||
| if (n > 0) str[m_gcount] = '\0'; | |||
| if (m_gcount == 0) setstate(failbit); | |||
| return *this; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void istream::getBool(bool *b) { | |||
| if ((flags() & boolalpha) == 0) { | |||
| getNumber(b); | |||
| return; | |||
| } | |||
| PGM_P truePtr = PSTR("true"); | |||
| PGM_P falsePtr = PSTR("false"); | |||
| const uint8_t true_len = 4; | |||
| const uint8_t false_len = 5; | |||
| bool trueOk = true; | |||
| bool falseOk = true; | |||
| uint8_t i = 0; | |||
| int c = readSkip(); | |||
| while (1) { | |||
| falseOk = falseOk && c == pgm_read_byte(falsePtr + i); | |||
| trueOk = trueOk && c == pgm_read_byte(truePtr + i); | |||
| if (trueOk == false && falseOk == false) break; | |||
| i++; | |||
| if (trueOk && i == true_len) { | |||
| *b = true; | |||
| return; | |||
| } | |||
| if (falseOk && i == false_len) { | |||
| *b = false; | |||
| return; | |||
| } | |||
| c = getch(); | |||
| } | |||
| setstate(failbit); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void istream::getChar(char* ch) { | |||
| int16_t c = readSkip(); | |||
| if (c < 0) { | |||
| setstate(failbit); | |||
| } else { | |||
| *ch = c; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // | |||
| // http://www.exploringbinary.com/category/numbers-in-computers/ | |||
| // | |||
| int16_t const EXP_LIMIT = 100; | |||
| static const uint32_t uint32_max = (uint32_t)-1; | |||
| bool istream::getDouble(double* value) { | |||
| bool got_digit = false; | |||
| bool got_dot = false; | |||
| bool neg; | |||
| int16_t c; | |||
| bool expNeg = false; | |||
| int16_t exp = 0; | |||
| int16_t fracExp = 0; | |||
| uint32_t frac = 0; | |||
| FatPos_t endPos; | |||
| double pow10; | |||
| double v; | |||
| getpos(&endPos); | |||
| c = readSkip(); | |||
| neg = c == '-'; | |||
| if (c == '-' || c == '+') { | |||
| c = getch(); | |||
| } | |||
| while (1) { | |||
| if (isdigit(c)) { | |||
| got_digit = true; | |||
| if (frac < uint32_max/10) { | |||
| frac = frac * 10 + (c - '0'); | |||
| if (got_dot) fracExp--; | |||
| } else { | |||
| if (!got_dot) fracExp++; | |||
| } | |||
| } else if (!got_dot && c == '.') { | |||
| got_dot = true; | |||
| } else { | |||
| break; | |||
| } | |||
| if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) goto fail; | |||
| c = getch(&endPos); | |||
| } | |||
| if (!got_digit) goto fail; | |||
| if (c == 'e' || c == 'E') { | |||
| c = getch(); | |||
| expNeg = c == '-'; | |||
| if (c == '-' || c == '+') { | |||
| c = getch(); | |||
| } | |||
| while (isdigit(c)) { | |||
| if (exp > EXP_LIMIT) goto fail; | |||
| exp = exp * 10 + (c - '0'); | |||
| c = getch(&endPos); | |||
| } | |||
| } | |||
| v = static_cast<double>(frac); | |||
| exp = expNeg ? fracExp - exp : fracExp + exp; | |||
| expNeg = exp < 0; | |||
| if (expNeg) exp = -exp; | |||
| pow10 = 10.0; | |||
| while (exp) { | |||
| if (exp & 1) { | |||
| if (expNeg) { | |||
| // check for underflow | |||
| if (v < FLT_MIN * pow10 && frac != 0) goto fail; | |||
| v /= pow10; | |||
| } else { | |||
| // check for overflow | |||
| if (v > FLT_MAX / pow10) goto fail; | |||
| v *= pow10; | |||
| } | |||
| } | |||
| pow10 *= pow10; | |||
| exp >>= 1; | |||
| } | |||
| setpos(&endPos); | |||
| *value = neg ? -v : v; | |||
| return true; | |||
| fail: | |||
| // error restore position to last good place | |||
| setpos(&endPos); | |||
| setstate(failbit); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Extract characters | |||
| * | |||
| * \param[out] str Location to receive extracted characters. | |||
| * \param[in] n Size of str. | |||
| * \param[in] delim Delimiter | |||
| * | |||
| * Characters are extracted until extraction fails, | |||
| * the next character equals \a delim (delim is extracted), or n-1 | |||
| * characters are extracted. | |||
| * | |||
| * The failbit is set if no characters are extracted or n-1 characters | |||
| * are extracted. If end-of-file occurs the eofbit is set. | |||
| * | |||
| * \return always returns *this. A failure is indicated by the stream state. | |||
| */ | |||
| istream& istream::getline(char *str, streamsize n, char delim) { | |||
| FatPos_t pos; | |||
| int c; | |||
| m_gcount = 0; | |||
| if (n > 0) str[0] = '\0'; | |||
| while (1) { | |||
| c = getch(&pos); | |||
| if (c < 0) { | |||
| break; | |||
| } | |||
| if (c == delim) { | |||
| m_gcount++; | |||
| break; | |||
| } | |||
| if ((m_gcount + 1) >= n) { | |||
| setpos(&pos); | |||
| setstate(failbit); | |||
| break; | |||
| } | |||
| str[m_gcount++] = c; | |||
| str[m_gcount] = '\0'; | |||
| } | |||
| if (m_gcount == 0) setstate(failbit); | |||
| return *this; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| bool istream::getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num) { | |||
| int16_t c; | |||
| int8_t any = 0; | |||
| int8_t have_zero = 0; | |||
| uint8_t neg; | |||
| uint32_t val = 0; | |||
| uint32_t cutoff; | |||
| uint8_t cutlim; | |||
| FatPos_t endPos; | |||
| uint8_t f = flags() & basefield; | |||
| uint8_t base = f == oct ? 8 : f != hex ? 10 : 16; | |||
| getpos(&endPos); | |||
| c = readSkip(); | |||
| neg = c == '-' ? 1 : 0; | |||
| if (c == '-' || c == '+') { | |||
| c = getch(); | |||
| } | |||
| if (base == 16 && c == '0') { // TESTSUITE | |||
| c = getch(&endPos); | |||
| if (c == 'X' || c == 'x') { | |||
| c = getch(); | |||
| // remember zero in case no hex digits follow x/X | |||
| have_zero = 1; | |||
| } else { | |||
| any = 1; | |||
| } | |||
| } | |||
| // set values for overflow test | |||
| cutoff = neg ? negMax : posMax; | |||
| cutlim = cutoff % base; | |||
| cutoff /= base; | |||
| while (1) { | |||
| if (isdigit(c)) { | |||
| c -= '0'; | |||
| } else if (isalpha(c)) { | |||
| c -= isupper(c) ? 'A' - 10 : 'a' - 10; | |||
| } else { | |||
| break; | |||
| } | |||
| if (c >= base) { | |||
| break; | |||
| } | |||
| if (val > cutoff || (val == cutoff && c > cutlim)) { | |||
| // indicate overflow error | |||
| any = -1; | |||
| break; | |||
| } | |||
| val = val * base + c; | |||
| c = getch(&endPos); | |||
| any = 1; | |||
| } | |||
| setpos(&endPos); | |||
| if (any > 0 || (have_zero && any >= 0)) { | |||
| *num = neg ? -val : val; | |||
| return true; | |||
| } | |||
| setstate(failbit); | |||
| return false; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * | |||
| */ | |||
| void istream::getStr(char *str) { | |||
| FatPos_t pos; | |||
| uint16_t i = 0; | |||
| uint16_t m = width() ? width() - 1 : 0XFFFE; | |||
| if (m != 0) { | |||
| getpos(&pos); | |||
| int c = readSkip(); | |||
| while (i < m) { | |||
| if (c < 0) { | |||
| break; | |||
| } | |||
| if (isspace(c)) { | |||
| setpos(&pos); | |||
| break; | |||
| } | |||
| str[i++] = c; | |||
| c = getch(&pos); | |||
| } | |||
| } | |||
| str[i] = '\0'; | |||
| if (i == 0) setstate(failbit); | |||
| width(0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Extract characters and discard them. | |||
| * | |||
| * \param[in] n maximum number of characters to ignore. | |||
| * \param[in] delim Delimiter. | |||
| * | |||
| * Characters are extracted until extraction fails, \a n characters | |||
| * are extracted, or the next input character equals \a delim | |||
| * (the delimiter is extracted). If end-of-file occurs the eofbit is set. | |||
| * | |||
| * Failures are indicated by the state of the stream. | |||
| * | |||
| * \return *this | |||
| * | |||
| */ | |||
| istream& istream::ignore(streamsize n, int delim) { | |||
| int c; | |||
| m_gcount = 0; | |||
| while (m_gcount < n) { | |||
| c = getch(); | |||
| if (c < 0) { | |||
| break; | |||
| } | |||
| m_gcount++; | |||
| if (c == delim) break; | |||
| } | |||
| return *this; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * Return the next available character without consuming it. | |||
| * | |||
| * \return The character if the stream state is good else -1; | |||
| * | |||
| */ | |||
| int istream::peek() { | |||
| int16_t c; | |||
| FatPos_t pos; | |||
| m_gcount = 0; | |||
| getpos(&pos); | |||
| c = getch(); | |||
| if (c < 0) { | |||
| if (!bad()) setstate(eofbit); | |||
| } else { | |||
| setpos(&pos); | |||
| } | |||
| return c; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| int16_t istream::readSkip() { | |||
| int16_t c; | |||
| do { | |||
| c = getch(); | |||
| } while (isspace(c) && (flags() & skipws)); | |||
| return c; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** used to implement ws() */ | |||
| void istream::skipWhite() { | |||
| int c; | |||
| FatPos_t pos; | |||
| do { | |||
| c = getch(&pos); | |||
| } while (isspace(c)); | |||
| setpos(&pos); | |||
| } | |||
| @@ -0,0 +1,307 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef istream_h | |||
| #define istream_h | |||
| /** | |||
| * \file | |||
| * \brief \ref istream class | |||
| */ | |||
| #include <ios.h> | |||
| /** | |||
| * \class istream | |||
| * \brief Input Stream | |||
| */ | |||
| class istream : public virtual ios { | |||
| public: | |||
| istream() {} | |||
| /** call manipulator | |||
| * \param[in] pf function to call | |||
| * \return the stream | |||
| */ | |||
| istream& operator>>(istream& (*pf)(istream& str)) { | |||
| return pf(*this); | |||
| } | |||
| /** call manipulator | |||
| * \param[in] pf function to call | |||
| * \return the stream | |||
| */ | |||
| istream& operator>>(ios_base& (*pf)(ios_base& str)) { | |||
| pf(*this); | |||
| return *this; | |||
| } | |||
| /** call manipulator | |||
| * \param[in] pf function to call | |||
| * \return the stream | |||
| */ | |||
| istream& operator>>(ios& (*pf)(ios& str)) { | |||
| pf(*this); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a character string | |||
| * \param[out] str location to store the string. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>>(char *str) { | |||
| getStr(str); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a character | |||
| * \param[out] ch location to store the character. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>>(char& ch) { | |||
| getChar(&ch); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a character string | |||
| * \param[out] str location to store the string. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>>(signed char *str) { | |||
| getStr(reinterpret_cast<char*>(str)); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a character | |||
| * \param[out] ch location to store the character. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>>(signed char& ch) { | |||
| getChar(reinterpret_cast<char*>(&ch)); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a character string | |||
| * \param[out] str location to store the string. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>>(unsigned char *str) { | |||
| getStr(reinterpret_cast<char*>(str)); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a character | |||
| * \param[out] ch location to store the character. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>>(unsigned char& ch) { | |||
| getChar(reinterpret_cast<char*>(&ch)); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type bool. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>>(bool& arg) { | |||
| getBool(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type short. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>>(short& arg) { // NOLINT | |||
| getNumber(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type unsigned short. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>>(unsigned short& arg) { // NOLINT | |||
| getNumber(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type int. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>>(int& arg) { | |||
| getNumber(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type unsigned int. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>>(unsigned int& arg) { | |||
| getNumber(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type long. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>>(long& arg) { // NOLINT | |||
| getNumber(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type unsigned long. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>>(unsigned long& arg) { // NOLINT | |||
| getNumber(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type double. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>> (double& arg) { | |||
| getDouble(&arg); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type float. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream &operator>> (float& arg) { | |||
| double v; | |||
| getDouble(&v); | |||
| arg = v; | |||
| return *this; | |||
| } | |||
| /** | |||
| * Extract a value of type void*. | |||
| * \param[out] arg location to store the value. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& operator>> (void*& arg) { | |||
| uint32_t val; | |||
| getNumber(&val); | |||
| arg = reinterpret_cast<void*>(val); | |||
| return *this; | |||
| } | |||
| /** | |||
| * \return The number of characters extracted by the last unformatted | |||
| * input function. | |||
| */ | |||
| streamsize gcount() const {return m_gcount;} | |||
| int get(); | |||
| istream& get(char& ch); | |||
| istream& get(char *str, streamsize n, char delim = '\n'); | |||
| istream& getline(char *str, streamsize count, char delim = '\n'); | |||
| istream& ignore(streamsize n = 1, int delim= -1); | |||
| int peek(); | |||
| // istream& read(char *str, streamsize count); | |||
| // streamsize readsome(char *str, streamsize count); | |||
| /** | |||
| * \return the stream position | |||
| */ | |||
| pos_type tellg() {return tellpos();} | |||
| /** | |||
| * Set the stream position | |||
| * \param[in] pos The absolute position in which to move the read pointer. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& seekg(pos_type pos) { | |||
| if (!seekpos(pos)) setstate(failbit); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Set the stream position. | |||
| * | |||
| * \param[in] off An offset to move the read pointer relative to way. | |||
| * \a off is a signed 32-bit int so the offset is limited to +- 2GB. | |||
| * \param[in] way One of ios::beg, ios::cur, or ios::end. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| istream& seekg(off_type off, seekdir way) { | |||
| if (!seekoff(off, way)) setstate(failbit); | |||
| return *this; | |||
| } | |||
| void skipWhite(); | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** | |||
| * Internal - do not use | |||
| * \return | |||
| */ | |||
| virtual int16_t getch() = 0; | |||
| /** | |||
| * Internal - do not use | |||
| * \param[out] pos | |||
| * \return | |||
| */ | |||
| int16_t getch(FatPos_t* pos) { | |||
| getpos(pos); | |||
| return getch(); | |||
| } | |||
| /** | |||
| * Internal - do not use | |||
| * \param[out] pos | |||
| */ | |||
| virtual void getpos(FatPos_t* pos) = 0; | |||
| /** | |||
| * Internal - do not use | |||
| * \param[in] pos | |||
| */ | |||
| virtual bool seekoff(off_type off, seekdir way) = 0; | |||
| virtual bool seekpos(pos_type pos) = 0; | |||
| virtual void setpos(FatPos_t* pos) = 0; | |||
| virtual pos_type tellpos() = 0; | |||
| /// @endcond | |||
| private: | |||
| void getBool(bool *b); | |||
| void getChar(char* ch); | |||
| bool getDouble(double* value); | |||
| template <typename T> void getNumber(T* value); | |||
| bool getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num); | |||
| void getStr(char *str); | |||
| int16_t readSkip(); | |||
| size_t m_gcount; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| template <typename T> | |||
| void istream::getNumber(T* value) { | |||
| uint32_t tmp; | |||
| if ((T)-1 < 0) { | |||
| // number is signed, max positive value | |||
| uint32_t const m = ((uint32_t)-1) >> (33 - sizeof(T) * 8); | |||
| // max absolute value of negative number is m + 1. | |||
| if (getNumber(m, m + 1, &tmp)) { | |||
| *value = (T)tmp; | |||
| } | |||
| } else { | |||
| // max unsigned value for T | |||
| uint32_t const m = (T)-1; | |||
| if (getNumber(m, m, &tmp)) { | |||
| *value = (T)tmp; | |||
| } | |||
| } | |||
| } | |||
| #endif // istream_h | |||
| @@ -0,0 +1,176 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <ostream.h> | |||
| #ifndef PSTR | |||
| #define PSTR(x) x | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::do_fill(unsigned len) { | |||
| for (; len < width(); len++) putch(fill()); | |||
| width(0); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::fill_not_left(unsigned len) { | |||
| if ((flags() & adjustfield) != left) { | |||
| do_fill(len); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| char* ostream::fmtNum(uint32_t n, char *ptr, uint8_t base) { | |||
| char a = flags() & uppercase ? 'A' - 10 : 'a' - 10; | |||
| do { | |||
| uint32_t m = n; | |||
| n /= base; | |||
| char c = m - base * n; | |||
| *--ptr = c < 10 ? c + '0' : c + a; | |||
| } while (n); | |||
| return ptr; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putBool(bool b) { | |||
| if (flags() & boolalpha) { | |||
| if (b) { | |||
| putPgm(PSTR("true")); | |||
| } else { | |||
| putPgm(PSTR("false")); | |||
| } | |||
| } else { | |||
| putChar(b ? '1' : '0'); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putChar(char c) { | |||
| fill_not_left(1); | |||
| putch(c); | |||
| do_fill(1); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putDouble(double n) { | |||
| uint8_t nd = precision(); | |||
| double round = 0.5; | |||
| char sign; | |||
| char buf[13]; // room for sign, 10 digits, '.', and zero byte | |||
| char *end = buf + sizeof(buf) - 1; | |||
| char *str = end; | |||
| // terminate string | |||
| *end = '\0'; | |||
| // get sign and make nonnegative | |||
| if (n < 0.0) { | |||
| sign = '-'; | |||
| n = -n; | |||
| } else { | |||
| sign = flags() & showpos ? '+' : '\0'; | |||
| } | |||
| // check for larger than uint32_t | |||
| if (n > 4.0E9) { | |||
| putPgm(PSTR("BIG FLT")); | |||
| return; | |||
| } | |||
| // round up and separate in and fraction parts | |||
| for (uint8_t i = 0; i < nd; ++i) round *= 0.1; | |||
| n += round; | |||
| uint32_t intPart = n; | |||
| double fractionPart = n - intPart; | |||
| // format intPart and decimal point | |||
| if (nd || (flags() & showpoint)) *--str = '.'; | |||
| str = fmtNum(intPart, str, 10); | |||
| // calculate length for fill | |||
| uint8_t len = sign ? 1 : 0; | |||
| len += nd + end - str; | |||
| // extract adjust field | |||
| fmtflags adj = flags() & adjustfield; | |||
| if (adj == internal) { | |||
| if (sign) putch(sign); | |||
| do_fill(len); | |||
| } else { | |||
| // do fill for internal or right | |||
| fill_not_left(len); | |||
| if (sign) *--str = sign; | |||
| } | |||
| putstr(str); | |||
| // output fraction | |||
| while (nd-- > 0) { | |||
| fractionPart *= 10.0; | |||
| int digit = static_cast<int>(fractionPart); | |||
| putch(digit + '0'); | |||
| fractionPart -= digit; | |||
| } | |||
| // do fill if not done above | |||
| do_fill(len); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putNum(int32_t n) { | |||
| bool neg = n < 0 && flagsToBase() == 10; | |||
| if (neg) n = -n; | |||
| putNum(n, neg); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putNum(uint32_t n, bool neg) { | |||
| char buf[13]; | |||
| char* end = buf + sizeof(buf) - 1; | |||
| char* num; | |||
| char* str; | |||
| uint8_t base = flagsToBase(); | |||
| *end = '\0'; | |||
| str = num = fmtNum(n, end, base); | |||
| if (base == 10) { | |||
| if (neg) { | |||
| *--str = '-'; | |||
| } else if (flags() & showpos) { | |||
| *--str = '+'; | |||
| } | |||
| } else if (flags() & showbase) { | |||
| if (flags() & hex) { | |||
| *--str = flags() & uppercase ? 'X' : 'x'; | |||
| } | |||
| *--str = '0'; | |||
| } | |||
| uint8_t len = end - str; | |||
| fmtflags adj = flags() & adjustfield; | |||
| if (adj == internal) { | |||
| while (str < num) putch(*str++); | |||
| } | |||
| if (adj != left) { | |||
| do_fill(len); | |||
| } | |||
| putstr(str); | |||
| do_fill(len); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putPgm(const char* str) { | |||
| int n; | |||
| for (n = 0; pgm_read_byte(&str[n]); n++) {} | |||
| fill_not_left(n); | |||
| for (uint8_t c; (c = pgm_read_byte(str)); str++) { | |||
| putch(c); | |||
| } | |||
| do_fill(n); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| void ostream::putStr(const char *str) { | |||
| unsigned n = strlen(str); | |||
| fill_not_left(n); | |||
| putstr(str); | |||
| do_fill(n); | |||
| } | |||
| @@ -0,0 +1,287 @@ | |||
| /* Arduino SdFat Library | |||
| * Copyright (C) 2012 by William Greiman | |||
| * | |||
| * This file is part of the Arduino SdFat Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino SdFat Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef ostream_h | |||
| #define ostream_h | |||
| /** | |||
| * \file | |||
| * \brief \ref ostream class | |||
| */ | |||
| #include <ios.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** macro for flash inserter */ | |||
| #define pstr(str) pgm(PSTR(str)) | |||
| /** \struct pgm | |||
| * \brief type for string in flash | |||
| */ | |||
| struct pgm { | |||
| /** Pointer to flash string */ | |||
| char *ptr; | |||
| /** constructor | |||
| * \param[in] str initializer for pointer. | |||
| */ | |||
| explicit pgm(char* str) : ptr(str) {} | |||
| /** constructor | |||
| * \param[in] str initializer for pointer. | |||
| */ | |||
| explicit pgm(const char *str) : ptr(const_cast<char*>(str)) {} | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| * \class ostream | |||
| * \brief Output Stream | |||
| */ | |||
| class ostream : public virtual ios { | |||
| public: | |||
| ostream() {} | |||
| /** call manipulator | |||
| * \param[in] pf function to call | |||
| * \return the stream | |||
| */ | |||
| ostream& operator<< (ostream& (*pf)(ostream& str)) { | |||
| return pf(*this); | |||
| } | |||
| /** call manipulator | |||
| * \param[in] pf function to call | |||
| * \return the stream | |||
| */ | |||
| ostream& operator<< (ios_base& (*pf)(ios_base& str)) { | |||
| pf(*this); | |||
| return *this; | |||
| } | |||
| /** Output bool | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (bool arg) { | |||
| putBool(arg); | |||
| return *this; | |||
| } | |||
| /** Output string | |||
| * \param[in] arg string to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (const char *arg) { | |||
| putStr(arg); | |||
| return *this; | |||
| } | |||
| /** Output string | |||
| * \param[in] arg string to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (const signed char *arg) { | |||
| putStr((const char*)arg); | |||
| return *this; | |||
| } | |||
| /** Output string | |||
| * \param[in] arg string to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (const unsigned char *arg) { | |||
| putStr((const char*)arg); | |||
| return *this; | |||
| } | |||
| /** Output character | |||
| * \param[in] arg character to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (char arg) { | |||
| putChar(arg); | |||
| return *this; | |||
| } | |||
| /** Output character | |||
| * \param[in] arg character to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (signed char arg) { | |||
| putChar(static_cast<char>(arg)); | |||
| return *this; | |||
| } | |||
| /** Output character | |||
| * \param[in] arg character to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (unsigned char arg) { | |||
| putChar(static_cast<char>(arg)); | |||
| return *this; | |||
| } | |||
| /** Output double | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (double arg) { | |||
| putDouble(arg); | |||
| return *this; | |||
| } | |||
| /** Output float | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (float arg) { | |||
| putDouble(arg); | |||
| return *this; | |||
| } | |||
| /** Output signed short | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (short arg) { // NOLINT | |||
| putNum((int32_t)arg); | |||
| return *this; | |||
| } | |||
| /** Output unsigned short | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (unsigned short arg) { // NOLINT | |||
| putNum((uint32_t)arg); | |||
| return *this; | |||
| } | |||
| /** Output signed int | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (int arg) { | |||
| putNum((int32_t)arg); | |||
| return *this; | |||
| } | |||
| /** Output unsigned int | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (unsigned int arg) { | |||
| putNum((uint32_t)arg); | |||
| return *this; | |||
| } | |||
| /** Output signed long | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (long arg) { // NOLINT | |||
| putNum(arg); | |||
| return *this; | |||
| } | |||
| /** Output unsigned long | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (unsigned long arg) { // NOLINT | |||
| putNum(arg); | |||
| return *this; | |||
| } | |||
| /** Output pointer | |||
| * \param[in] arg value to output | |||
| * \return the stream | |||
| */ | |||
| ostream& operator<< (const void* arg) { | |||
| putNum(reinterpret_cast<uint32_t>(arg)); | |||
| return *this; | |||
| } | |||
| /** Output a string from flash using the pstr() macro | |||
| * \param[in] arg pgm struct pointing to string | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (pgm arg) { | |||
| putPgm(arg.ptr); | |||
| return *this; | |||
| } | |||
| /** Output a string from flash using the Arduino F() macro. | |||
| * \param[in] arg pointing to flash string | |||
| * \return the stream | |||
| */ | |||
| ostream &operator<< (const __FlashStringHelper *arg) { | |||
| putPgm(reinterpret_cast<const char*>(arg)); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Puts a character in a stream. | |||
| * | |||
| * The unformatted output function inserts the element \a ch. | |||
| * It returns *this. | |||
| * | |||
| * \param[in] ch The character | |||
| * \return A reference to the ostream object. | |||
| */ | |||
| ostream& put(char ch) { | |||
| putch(ch); | |||
| return *this; | |||
| } | |||
| // ostream& write(char *str, streamsize count); | |||
| /** | |||
| * Flushes the buffer associated with this stream. The flush function | |||
| * calls the sync function of the associated file. | |||
| * \return A reference to the ostream object. | |||
| */ | |||
| ostream& flush() { | |||
| if (!sync()) setstate(badbit); | |||
| return *this; | |||
| } | |||
| /** | |||
| * \return the stream position | |||
| */ | |||
| pos_type tellp() {return tellpos();} | |||
| /** | |||
| * Set the stream position | |||
| * \param[in] pos The absolute position in which to move the write pointer. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| ostream& seekp(pos_type pos) { | |||
| if (!seekpos(pos)) setstate(failbit); | |||
| return *this; | |||
| } | |||
| /** | |||
| * Set the stream position. | |||
| * | |||
| * \param[in] off An offset to move the write pointer relative to way. | |||
| * \a off is a signed 32-bit int so the offset is limited to +- 2GB. | |||
| * \param[in] way One of ios::beg, ios::cur, or ios::end. | |||
| * \return Is always *this. Failure is indicated by the state of *this. | |||
| */ | |||
| ostream& seekp(off_type off, seekdir way) { | |||
| if (!seekoff(off, way)) setstate(failbit); | |||
| return *this; | |||
| } | |||
| protected: | |||
| /// @cond SHOW_PROTECTED | |||
| /** Put character with binary/text conversion | |||
| * \param[in] ch character to write | |||
| */ | |||
| virtual void putch(char ch) = 0; | |||
| virtual void putstr(const char *str) = 0; | |||
| virtual bool seekoff(off_type pos, seekdir way) = 0; | |||
| virtual bool seekpos(pos_type pos) = 0; | |||
| virtual bool sync() = 0; | |||
| virtual pos_type tellpos() = 0; | |||
| /// @endcond | |||
| private: | |||
| void do_fill(unsigned len); | |||
| void fill_not_left(unsigned len); | |||
| char* fmtNum(uint32_t n, char *ptr, uint8_t base); | |||
| void putBool(bool b); | |||
| void putChar(char c); | |||
| void putDouble(double n); | |||
| void putNum(uint32_t n, bool neg = false); | |||
| void putNum(int32_t n); | |||
| void putPgm(const char* str); | |||
| void putStr(const char* str); | |||
| }; | |||
| #endif // ostream_h | |||
| @@ -0,0 +1,654 @@ | |||
| /* Arduino DigitalIO Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino DigitalIO Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino DigitalIO Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * @file | |||
| * @brief Fast Digital Pin functions | |||
| * | |||
| * @defgroup digitalPin Fast Pin I/O | |||
| * @details Fast Digital I/O functions and template class. | |||
| * @{ | |||
| */ | |||
| #ifndef DigitalPin_h | |||
| #define DigitalPin_h | |||
| #include <Arduino.h> | |||
| #ifdef __arm__ | |||
| #ifdef CORE_TEENSY | |||
| //------------------------------------------------------------------------------ | |||
| /** read pin value | |||
| * @param[in] pin Arduino pin number | |||
| * @return value read | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| bool fastDigitalRead(uint8_t pin) { | |||
| return *portInputRegister(pin); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Set pin value | |||
| * @param[in] pin Arduino pin number | |||
| * @param[in] level value to write | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void fastDigitalWrite(uint8_t pin, bool value) { | |||
| if (value) { | |||
| *portSetRegister(pin) = 1; | |||
| } else { | |||
| *portClearRegister(pin) = 1; | |||
| } | |||
| } | |||
| #else // CORE_TEENSY | |||
| //------------------------------------------------------------------------------ | |||
| /** read pin value | |||
| * @param[in] pin Arduino pin number | |||
| * @return value read | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| bool fastDigitalRead(uint8_t pin){ | |||
| return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Set pin value | |||
| * @param[in] pin Arduino pin number | |||
| * @param[in] level value to write | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void fastDigitalWrite(uint8_t pin, bool value){ | |||
| if(value) { | |||
| g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin; | |||
| } else { | |||
| g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin; | |||
| } | |||
| } | |||
| #endif // CORE_TEENSY | |||
| //------------------------------------------------------------------------------ | |||
| inline void fastDigitalToggle(uint8_t pin) { | |||
| fastDigitalWrite(pin, !fastDigitalRead(pin)); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| inline void fastPinMode(uint8_t pin, bool mode) {pinMode(pin, mode);} | |||
| #else // __arm__ | |||
| #include <avr/io.h> | |||
| #include <util/atomic.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * @class pin_map_t | |||
| * @brief struct for mapping digital pins | |||
| */ | |||
| struct pin_map_t { | |||
| volatile uint8_t* ddr; /**< address of DDR for this pin */ | |||
| volatile uint8_t* pin; /**< address of PIN for this pin */ | |||
| volatile uint8_t* port; /**< address of PORT for this pin */ | |||
| uint8_t bit; /**< bit number for this pin */ | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| #if defined(__AVR_ATmega168__)\ | |||
| ||defined(__AVR_ATmega168P__)\ | |||
| ||defined(__AVR_ATmega328P__) | |||
| // 168 and 328 Arduinos | |||
| const static pin_map_t pinMap[] = { | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 0 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 1 | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 2 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 3 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 4 | |||
| {&DDRD, &PIND, &PORTD, 5}, // D5 5 | |||
| {&DDRD, &PIND, &PORTD, 6}, // D6 6 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 7 | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 8 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 9 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 10 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 11 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 12 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 13 | |||
| {&DDRC, &PINC, &PORTC, 0}, // C0 14 | |||
| {&DDRC, &PINC, &PORTC, 1}, // C1 15 | |||
| {&DDRC, &PINC, &PORTC, 2}, // C2 16 | |||
| {&DDRC, &PINC, &PORTC, 3}, // C3 17 | |||
| {&DDRC, &PINC, &PORTC, 4}, // C4 18 | |||
| {&DDRC, &PINC, &PORTC, 5} // C5 19 | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| #elif defined(__AVR_ATmega1280__)\ | |||
| || defined(__AVR_ATmega2560__) | |||
| // Mega | |||
| static const pin_map_t pinMap[] = { | |||
| {&DDRE, &PINE, &PORTE, 0}, // E0 0 | |||
| {&DDRE, &PINE, &PORTE, 1}, // E1 1 | |||
| {&DDRE, &PINE, &PORTE, 4}, // E4 2 | |||
| {&DDRE, &PINE, &PORTE, 5}, // E5 3 | |||
| {&DDRG, &PING, &PORTG, 5}, // G5 4 | |||
| {&DDRE, &PINE, &PORTE, 3}, // E3 5 | |||
| {&DDRH, &PINH, &PORTH, 3}, // H3 6 | |||
| {&DDRH, &PINH, &PORTH, 4}, // H4 7 | |||
| {&DDRH, &PINH, &PORTH, 5}, // H5 8 | |||
| {&DDRH, &PINH, &PORTH, 6}, // H6 9 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 10 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 11 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 12 | |||
| {&DDRB, &PINB, &PORTB, 7}, // B7 13 | |||
| {&DDRJ, &PINJ, &PORTJ, 1}, // J1 14 | |||
| {&DDRJ, &PINJ, &PORTJ, 0}, // J0 15 | |||
| {&DDRH, &PINH, &PORTH, 1}, // H1 16 | |||
| {&DDRH, &PINH, &PORTH, 0}, // H0 17 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 18 | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 19 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 20 | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 21 | |||
| {&DDRA, &PINA, &PORTA, 0}, // A0 22 | |||
| {&DDRA, &PINA, &PORTA, 1}, // A1 23 | |||
| {&DDRA, &PINA, &PORTA, 2}, // A2 24 | |||
| {&DDRA, &PINA, &PORTA, 3}, // A3 25 | |||
| {&DDRA, &PINA, &PORTA, 4}, // A4 26 | |||
| {&DDRA, &PINA, &PORTA, 5}, // A5 27 | |||
| {&DDRA, &PINA, &PORTA, 6}, // A6 28 | |||
| {&DDRA, &PINA, &PORTA, 7}, // A7 29 | |||
| {&DDRC, &PINC, &PORTC, 7}, // C7 30 | |||
| {&DDRC, &PINC, &PORTC, 6}, // C6 31 | |||
| {&DDRC, &PINC, &PORTC, 5}, // C5 32 | |||
| {&DDRC, &PINC, &PORTC, 4}, // C4 33 | |||
| {&DDRC, &PINC, &PORTC, 3}, // C3 34 | |||
| {&DDRC, &PINC, &PORTC, 2}, // C2 35 | |||
| {&DDRC, &PINC, &PORTC, 1}, // C1 36 | |||
| {&DDRC, &PINC, &PORTC, 0}, // C0 37 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 38 | |||
| {&DDRG, &PING, &PORTG, 2}, // G2 39 | |||
| {&DDRG, &PING, &PORTG, 1}, // G1 40 | |||
| {&DDRG, &PING, &PORTG, 0}, // G0 41 | |||
| {&DDRL, &PINL, &PORTL, 7}, // L7 42 | |||
| {&DDRL, &PINL, &PORTL, 6}, // L6 43 | |||
| {&DDRL, &PINL, &PORTL, 5}, // L5 44 | |||
| {&DDRL, &PINL, &PORTL, 4}, // L4 45 | |||
| {&DDRL, &PINL, &PORTL, 3}, // L3 46 | |||
| {&DDRL, &PINL, &PORTL, 2}, // L2 47 | |||
| {&DDRL, &PINL, &PORTL, 1}, // L1 48 | |||
| {&DDRL, &PINL, &PORTL, 0}, // L0 49 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 50 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 51 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 52 | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 53 | |||
| {&DDRF, &PINF, &PORTF, 0}, // F0 54 | |||
| {&DDRF, &PINF, &PORTF, 1}, // F1 55 | |||
| {&DDRF, &PINF, &PORTF, 2}, // F2 56 | |||
| {&DDRF, &PINF, &PORTF, 3}, // F3 57 | |||
| {&DDRF, &PINF, &PORTF, 4}, // F4 58 | |||
| {&DDRF, &PINF, &PORTF, 5}, // F5 59 | |||
| {&DDRF, &PINF, &PORTF, 6}, // F6 60 | |||
| {&DDRF, &PINF, &PORTF, 7}, // F7 61 | |||
| {&DDRK, &PINK, &PORTK, 0}, // K0 62 | |||
| {&DDRK, &PINK, &PORTK, 1}, // K1 63 | |||
| {&DDRK, &PINK, &PORTK, 2}, // K2 64 | |||
| {&DDRK, &PINK, &PORTK, 3}, // K3 65 | |||
| {&DDRK, &PINK, &PORTK, 4}, // K4 66 | |||
| {&DDRK, &PINK, &PORTK, 5}, // K5 67 | |||
| {&DDRK, &PINK, &PORTK, 6}, // K6 68 | |||
| {&DDRK, &PINK, &PORTK, 7} // K7 69 | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| #elif defined(__AVR_ATmega1284P__)\ | |||
| || defined(__AVR_ATmega1284__)\ | |||
| || defined(__AVR_ATmega644P__)\ | |||
| || defined(__AVR_ATmega644__)\ | |||
| || defined(__AVR_ATmega64__)\ | |||
| || defined(__AVR_ATmega32__)\ | |||
| || defined(__AVR_ATmega324__)\ | |||
| || defined(__AVR_ATmega16__) | |||
| #ifdef defined(VARIANT_MIGHTY) | |||
| // Mighty Layout | |||
| static const pin_map_t pinMap[] = { | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 0 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 1 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 2 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 3 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 4 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 5 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 6 | |||
| {&DDRB, &PINB, &PORTB, 7}, // B7 7 | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 8 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 9 | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 10 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 11 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 12 | |||
| {&DDRD, &PIND, &PORTD, 5}, // D5 13 | |||
| {&DDRD, &PIND, &PORTD, 6}, // D6 14 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 15 | |||
| {&DDRC, &PINC, &PORTC, 0}, // C0 16 | |||
| {&DDRC, &PINC, &PORTC, 1}, // C1 17 | |||
| {&DDRC, &PINC, &PORTC, 2}, // C2 18 | |||
| {&DDRC, &PINC, &PORTC, 3}, // C3 19 | |||
| {&DDRC, &PINC, &PORTC, 4}, // C4 20 | |||
| {&DDRC, &PINC, &PORTC, 5}, // C5 21 | |||
| {&DDRC, &PINC, &PORTC, 6}, // C6 22 | |||
| {&DDRC, &PINC, &PORTC, 7}, // C7 23 | |||
| {&DDRA, &PINA, &PORTA, 0}, // A0 24 | |||
| {&DDRA, &PINA, &PORTA, 1}, // A1 25 | |||
| {&DDRA, &PINA, &PORTA, 2}, // A2 26 | |||
| {&DDRA, &PINA, &PORTA, 3}, // A3 27 | |||
| {&DDRA, &PINA, &PORTA, 4}, // A4 28 | |||
| {&DDRA, &PINA, &PORTA, 5}, // A5 29 | |||
| {&DDRA, &PINA, &PORTA, 6}, // A6 30 | |||
| {&DDRA, &PINA, &PORTA, 7} // A7 31 | |||
| }; | |||
| #elif defined(VARIANT_BOBUINO) | |||
| // Bobuino Layout | |||
| static const pin_map_t pinMap[] = { | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 0 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 1 | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 2 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 3 | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 4 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 5 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 6 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 7 | |||
| {&DDRD, &PIND, &PORTD, 5}, // D5 8 | |||
| {&DDRD, &PIND, &PORTD, 6}, // D6 9 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 10 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 11 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 12 | |||
| {&DDRB, &PINB, &PORTB, 7}, // B7 13 | |||
| {&DDRA, &PINA, &PORTA, 7}, // A7 14 | |||
| {&DDRA, &PINA, &PORTA, 6}, // A6 15 | |||
| {&DDRA, &PINA, &PORTA, 5}, // A5 16 | |||
| {&DDRA, &PINA, &PORTA, 4}, // A4 17 | |||
| {&DDRA, &PINA, &PORTA, 3}, // A3 18 | |||
| {&DDRA, &PINA, &PORTA, 2}, // A2 19 | |||
| {&DDRA, &PINA, &PORTA, 1}, // A1 20 | |||
| {&DDRA, &PINA, &PORTA, 0}, // A0 21 | |||
| {&DDRC, &PINC, &PORTC, 0}, // C0 22 | |||
| {&DDRC, &PINC, &PORTC, 1}, // C1 23 | |||
| {&DDRC, &PINC, &PORTC, 2}, // C2 24 | |||
| {&DDRC, &PINC, &PORTC, 3}, // C3 25 | |||
| {&DDRC, &PINC, &PORTC, 4}, // C4 26 | |||
| {&DDRC, &PINC, &PORTC, 5}, // C5 27 | |||
| {&DDRC, &PINC, &PORTC, 6}, // C6 28 | |||
| {&DDRC, &PINC, &PORTC, 7}, // C7 29 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 30 | |||
| {&DDRD, &PIND, &PORTD, 7} // D7 31 | |||
| }; | |||
| #elif defined(VARIANT_STANDARD) | |||
| // Standard Layout | |||
| static const pin_map_t pinMap[] = { | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 0 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 1 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 2 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 3 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 4 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 5 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 6 | |||
| {&DDRB, &PINB, &PORTB, 7}, // B7 7 | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 8 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 9 | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 10 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 11 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 12 | |||
| {&DDRD, &PIND, &PORTD, 5}, // D5 13 | |||
| {&DDRD, &PIND, &PORTD, 6}, // D6 14 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 15 | |||
| {&DDRC, &PINC, &PORTC, 0}, // C0 16 | |||
| {&DDRC, &PINC, &PORTC, 1}, // C1 17 | |||
| {&DDRC, &PINC, &PORTC, 2}, // C2 18 | |||
| {&DDRC, &PINC, &PORTC, 3}, // C3 19 | |||
| {&DDRC, &PINC, &PORTC, 4}, // C4 20 | |||
| {&DDRC, &PINC, &PORTC, 5}, // C5 21 | |||
| {&DDRC, &PINC, &PORTC, 6}, // C6 22 | |||
| {&DDRC, &PINC, &PORTC, 7}, // C7 23 | |||
| {&DDRA, &PINA, &PORTA, 7}, // A7 24 | |||
| {&DDRA, &PINA, &PORTA, 6}, // A6 25 | |||
| {&DDRA, &PINA, &PORTA, 5}, // A5 26 | |||
| {&DDRA, &PINA, &PORTA, 4}, // A4 27 | |||
| {&DDRA, &PINA, &PORTA, 3}, // A3 28 | |||
| {&DDRA, &PINA, &PORTA, 2}, // A2 29 | |||
| {&DDRA, &PINA, &PORTA, 1}, // A1 30 | |||
| {&DDRA, &PINA, &PORTA, 0} // A0 31 | |||
| }; | |||
| #else // VARIANT_MIGHTY | |||
| #error Undefined variant 1284, 644, 324, 64, 32 | |||
| #endif // VARIANT_MIGHTY | |||
| //------------------------------------------------------------------------------ | |||
| #elif defined(__AVR_ATmega32U4__) | |||
| #ifdef CORE_TEENSY | |||
| // Teensy 2.0 | |||
| static const pin_map_t pinMap[] = { | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 0 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 1 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 2 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 3 | |||
| {&DDRB, &PINB, &PORTB, 7}, // B7 4 | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 5 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 6 | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 7 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 8 | |||
| {&DDRC, &PINC, &PORTC, 6}, // C6 9 | |||
| {&DDRC, &PINC, &PORTC, 7}, // C7 10 | |||
| {&DDRD, &PIND, &PORTD, 6}, // D6 11 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 12 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 13 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 14 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 15 | |||
| {&DDRF, &PINF, &PORTF, 7}, // F7 16 | |||
| {&DDRF, &PINF, &PORTF, 6}, // F6 17 | |||
| {&DDRF, &PINF, &PORTF, 5}, // F5 18 | |||
| {&DDRF, &PINF, &PORTF, 4}, // F4 19 | |||
| {&DDRF, &PINF, &PORTF, 1}, // F1 20 | |||
| {&DDRF, &PINF, &PORTF, 0}, // F0 21 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 22 | |||
| {&DDRD, &PIND, &PORTD, 5}, // D5 23 | |||
| {&DDRE, &PINE, &PORTE, 6} // E6 24 | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| #else // CORE_TEENSY | |||
| // Leonardo | |||
| static const pin_map_t pinMap[] = { | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 0 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 1 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 2 | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 3 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 4 | |||
| {&DDRC, &PINC, &PORTC, 6}, // C6 5 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 6 | |||
| {&DDRE, &PINE, &PORTE, 6}, // E6 7 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 8 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 9 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 10 | |||
| {&DDRB, &PINB, &PORTB, 7}, // B7 11 | |||
| {&DDRD, &PIND, &PORTD, 6}, // D6 12 | |||
| {&DDRC, &PINC, &PORTC, 7}, // C7 13 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 14 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 15 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 16 | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 17 | |||
| {&DDRF, &PINF, &PORTF, 7}, // F7 18 | |||
| {&DDRF, &PINF, &PORTF, 6}, // F6 19 | |||
| {&DDRF, &PINF, &PORTF, 5}, // F5 20 | |||
| {&DDRF, &PINF, &PORTF, 4}, // F4 21 | |||
| {&DDRF, &PINF, &PORTF, 1}, // F1 22 | |||
| {&DDRF, &PINF, &PORTF, 0}, // F0 23 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 24 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 25 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 26 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 27 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 28 | |||
| {&DDRD, &PIND, &PORTD, 6} // D6 29 | |||
| }; | |||
| #endif // CORE_TEENSY | |||
| //------------------------------------------------------------------------------ | |||
| #elif defined(__AVR_AT90USB646__)\ | |||
| || defined(__AVR_AT90USB1286__) | |||
| // Teensy++ 1.0 & 2.0 | |||
| static const pin_map_t pinMap[] = { | |||
| {&DDRD, &PIND, &PORTD, 0}, // D0 0 | |||
| {&DDRD, &PIND, &PORTD, 1}, // D1 1 | |||
| {&DDRD, &PIND, &PORTD, 2}, // D2 2 | |||
| {&DDRD, &PIND, &PORTD, 3}, // D3 3 | |||
| {&DDRD, &PIND, &PORTD, 4}, // D4 4 | |||
| {&DDRD, &PIND, &PORTD, 5}, // D5 5 | |||
| {&DDRD, &PIND, &PORTD, 6}, // D6 6 | |||
| {&DDRD, &PIND, &PORTD, 7}, // D7 7 | |||
| {&DDRE, &PINE, &PORTE, 0}, // E0 8 | |||
| {&DDRE, &PINE, &PORTE, 1}, // E1 9 | |||
| {&DDRC, &PINC, &PORTC, 0}, // C0 10 | |||
| {&DDRC, &PINC, &PORTC, 1}, // C1 11 | |||
| {&DDRC, &PINC, &PORTC, 2}, // C2 12 | |||
| {&DDRC, &PINC, &PORTC, 3}, // C3 13 | |||
| {&DDRC, &PINC, &PORTC, 4}, // C4 14 | |||
| {&DDRC, &PINC, &PORTC, 5}, // C5 15 | |||
| {&DDRC, &PINC, &PORTC, 6}, // C6 16 | |||
| {&DDRC, &PINC, &PORTC, 7}, // C7 17 | |||
| {&DDRE, &PINE, &PORTE, 6}, // E6 18 | |||
| {&DDRE, &PINE, &PORTE, 7}, // E7 19 | |||
| {&DDRB, &PINB, &PORTB, 0}, // B0 20 | |||
| {&DDRB, &PINB, &PORTB, 1}, // B1 21 | |||
| {&DDRB, &PINB, &PORTB, 2}, // B2 22 | |||
| {&DDRB, &PINB, &PORTB, 3}, // B3 23 | |||
| {&DDRB, &PINB, &PORTB, 4}, // B4 24 | |||
| {&DDRB, &PINB, &PORTB, 5}, // B5 25 | |||
| {&DDRB, &PINB, &PORTB, 6}, // B6 26 | |||
| {&DDRB, &PINB, &PORTB, 7}, // B7 27 | |||
| {&DDRA, &PINA, &PORTA, 0}, // A0 28 | |||
| {&DDRA, &PINA, &PORTA, 1}, // A1 29 | |||
| {&DDRA, &PINA, &PORTA, 2}, // A2 30 | |||
| {&DDRA, &PINA, &PORTA, 3}, // A3 31 | |||
| {&DDRA, &PINA, &PORTA, 4}, // A4 32 | |||
| {&DDRA, &PINA, &PORTA, 5}, // A5 33 | |||
| {&DDRA, &PINA, &PORTA, 6}, // A6 34 | |||
| {&DDRA, &PINA, &PORTA, 7}, // A7 35 | |||
| {&DDRE, &PINE, &PORTE, 4}, // E4 36 | |||
| {&DDRE, &PINE, &PORTE, 5}, // E5 37 | |||
| {&DDRF, &PINF, &PORTF, 0}, // F0 38 | |||
| {&DDRF, &PINF, &PORTF, 1}, // F1 39 | |||
| {&DDRF, &PINF, &PORTF, 2}, // F2 40 | |||
| {&DDRF, &PINF, &PORTF, 3}, // F3 41 | |||
| {&DDRF, &PINF, &PORTF, 4}, // F4 42 | |||
| {&DDRF, &PINF, &PORTF, 5}, // F5 43 | |||
| {&DDRF, &PINF, &PORTF, 6}, // F6 44 | |||
| {&DDRF, &PINF, &PORTF, 7} // F7 45 | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| #else // CPU type | |||
| #error unknown CPU type | |||
| #endif // CPU type | |||
| //------------------------------------------------------------------------------ | |||
| /** count of pins */ | |||
| static const uint8_t digitalPinCount = sizeof(pinMap)/sizeof(pin_map_t); | |||
| //============================================================================== | |||
| /** generate bad pin number error */ | |||
| void badPinNumber(void) | |||
| __attribute__((error("Pin number is too large or not a constant"))); | |||
| //------------------------------------------------------------------------------ | |||
| /** Check for valid pin number | |||
| * @param[in] pin Number of pin to be checked. | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void badPinCheck(uint8_t pin) { | |||
| if (!__builtin_constant_p(pin) || pin >= digitalPinCount) { | |||
| badPinNumber(); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** fast write helper | |||
| * @param[in] address I/O register address | |||
| * @param[in] bit bit number to write | |||
| * @param[in] level value for bit | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) { | |||
| uint8_t oldSREG; | |||
| if (address > (uint8_t*)0X5F) { | |||
| oldSREG = SREG; | |||
| cli(); | |||
| } | |||
| if (level) { | |||
| *address |= 1 << bit; | |||
| } else { | |||
| *address &= ~(1 << bit); | |||
| } | |||
| if (address > (uint8_t*)0X5F) { | |||
| SREG = oldSREG; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** read pin value | |||
| * @param[in] pin Arduino pin number | |||
| * @return value read | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| bool fastDigitalRead(uint8_t pin) { | |||
| badPinCheck(pin); | |||
| return (*pinMap[pin].pin >> pinMap[pin].bit) & 1; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** toggle a pin | |||
| * @param[in] pin Arduino pin number | |||
| * | |||
| * If the pin is in output mode toggle the pin level. | |||
| * If the pin is in input mode toggle the state of the 20K pullup. | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void fastDigitalToggle(uint8_t pin) { | |||
| badPinCheck(pin); | |||
| if (pinMap[pin].pin > (uint8_t*)0X5F) { | |||
| // must write bit to high address port | |||
| *pinMap[pin].pin = 1 << pinMap[pin].bit; | |||
| } else { | |||
| // will compile to sbi and PIN register will not be read. | |||
| *pinMap[pin].pin |= 1 << pinMap[pin].bit; | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Set pin value | |||
| * @param[in] pin Arduino pin number | |||
| * @param[in] level value to write | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void fastDigitalWrite(uint8_t pin, bool level) { | |||
| badPinCheck(pin); | |||
| fastBitWriteSafe(pinMap[pin].port, pinMap[pin].bit, level); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** set pin mode | |||
| * @param[in] pin Arduino pin number | |||
| * @param[in] mode if true set output mode else input mode | |||
| * | |||
| * fastPinMode does not enable or disable the 20K pullup for input mode. | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void fastPinMode(uint8_t pin, bool mode) { | |||
| badPinCheck(pin); | |||
| fastBitWriteSafe(pinMap[pin].ddr, pinMap[pin].bit, mode); | |||
| } | |||
| #endif // __arm__ | |||
| //------------------------------------------------------------------------------ | |||
| /** set pin configuration | |||
| * @param[in] pin Arduino pin number | |||
| * @param[in] mode If true set output mode else input mode | |||
| * @param[in] level If mode is output, set level high/low. | |||
| * If mode is input, enable or disable the pin's 20K pullup. | |||
| */ | |||
| static inline __attribute__((always_inline)) | |||
| void fastPinConfig(uint8_t pin, bool mode, bool level) { | |||
| fastPinMode(pin, mode); | |||
| fastDigitalWrite(pin, level); | |||
| } | |||
| //============================================================================== | |||
| /** | |||
| * @class DigitalPin | |||
| * @brief Fast digital port I/O | |||
| */ | |||
| template<uint8_t PinNumber> | |||
| class DigitalPin { | |||
| public: | |||
| //---------------------------------------------------------------------------- | |||
| /** Constructor */ | |||
| DigitalPin() {} | |||
| //---------------------------------------------------------------------------- | |||
| /** Constructor | |||
| * @param[in] pinMode if true set output mode else input mode. | |||
| */ | |||
| explicit DigitalPin(bool pinMode) { | |||
| mode(pinMode); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Constructor | |||
| * @param[in] mode If true set output mode else input mode | |||
| * @param[in] level If mode is output, set level high/low. | |||
| * If mode is input, enable or disable the pin's 20K pullup. | |||
| */ | |||
| DigitalPin(bool mode, bool level) { | |||
| config(mode, level); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Asignment operator | |||
| * @param[in] value If true set the pin's level high else set the | |||
| * pin's level low. | |||
| * | |||
| * @return This DigitalPin instance. | |||
| */ | |||
| inline DigitalPin & operator = (bool value) __attribute__((always_inline)) { | |||
| write(value); | |||
| return *this; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Parenthesis operator | |||
| * @return Pin's level | |||
| */ | |||
| inline operator bool () const __attribute__((always_inline)) { | |||
| return read(); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** set pin configuration | |||
| * @param[in] mode If true set output mode else input mode | |||
| * @param[in] level If mode is output, set level high/low. | |||
| * If mode is input, enable or disable the pin's 20K pullup. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void config(bool mode, bool level) { | |||
| fastPinConfig(PinNumber, mode, level); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** | |||
| * Set pin level high if output mode or enable 20K pullup if input mode. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void high() {write(true);} | |||
| //---------------------------------------------------------------------------- | |||
| /** | |||
| * Set pin level low if output mode or disable 20K pullup if input mode. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void low() {write(false);} | |||
| //---------------------------------------------------------------------------- | |||
| /** | |||
| * Set pin mode | |||
| * @param[in] pinMode if true set output mode else input mode. | |||
| * | |||
| * mode() does not enable or disable the 20K pullup for input mode. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void mode(bool pinMode) { | |||
| fastPinMode(PinNumber, pinMode); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** @return Pin's level */ | |||
| inline __attribute__((always_inline)) | |||
| bool read() const { | |||
| return fastDigitalRead(PinNumber); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** toggle a pin | |||
| * | |||
| * If the pin is in output mode toggle the pin's level. | |||
| * If the pin is in input mode toggle the state of the 20K pullup. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void toggle() { | |||
| fastDigitalToggle(PinNumber); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Write the pin's level. | |||
| * @param[in] value If true set the pin's level high else set the | |||
| * pin's level low. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void write(bool value) { | |||
| fastDigitalWrite(PinNumber, value); | |||
| } | |||
| }; | |||
| #endif // DigitalPin_h | |||
| /** @} */ | |||
| @@ -0,0 +1,65 @@ | |||
| /* FatLib Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the FatLib Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the FatLib Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef FatApiConstants_h | |||
| #define FatApiConstants_h | |||
| //------------------------------------------------------------------------------ | |||
| // use the gnu style oflag in open() | |||
| /** open() oflag for reading */ | |||
| uint8_t const O_READ = 0X01; | |||
| /** open() oflag - same as O_IN */ | |||
| uint8_t const O_RDONLY = O_READ; | |||
| /** open() oflag for write */ | |||
| uint8_t const O_WRITE = 0X02; | |||
| /** open() oflag - same as O_WRITE */ | |||
| uint8_t const O_WRONLY = O_WRITE; | |||
| /** open() oflag for reading and writing */ | |||
| uint8_t const O_RDWR = (O_READ | O_WRITE); | |||
| /** open() oflag mask for access modes */ | |||
| uint8_t const O_ACCMODE = (O_READ | O_WRITE); | |||
| /** The file offset shall be set to the end of the file prior to each write. */ | |||
| uint8_t const O_APPEND = 0X04; | |||
| /** synchronous writes - call sync() after each write */ | |||
| uint8_t const O_SYNC = 0X08; | |||
| /** truncate the file to zero length */ | |||
| uint8_t const O_TRUNC = 0X10; | |||
| /** set the initial position at the end of the file */ | |||
| uint8_t const O_AT_END = 0X20; | |||
| /** create the file if nonexistent */ | |||
| uint8_t const O_CREAT = 0X40; | |||
| /** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ | |||
| uint8_t const O_EXCL = 0X80; | |||
| // SdBaseFile class static and const definitions | |||
| // flags for ls() | |||
| /** ls() flag to print modify date */ | |||
| uint8_t const LS_DATE = 1; | |||
| /** ls() flag to print file size */ | |||
| uint8_t const LS_SIZE = 2; | |||
| /** ls() flag for recursive list of subdirectories */ | |||
| uint8_t const LS_R = 4; | |||
| // flags for timestamp | |||
| /** set the file's last access date */ | |||
| uint8_t const T_ACCESS = 1; | |||
| /** set the file's creation date and time */ | |||
| uint8_t const T_CREATE = 2; | |||
| /** Set the file's write date and time */ | |||
| uint8_t const T_WRITE = 4; | |||
| #endif // FatApiConstants_h | |||
| @@ -0,0 +1,678 @@ | |||
| /* FatLib Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the FatLib Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the FatLib Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef FatStructs_h | |||
| #define FatStructs_h | |||
| /** | |||
| * \file | |||
| * \brief FAT file structures | |||
| */ | |||
| /* | |||
| * mostly from Microsoft document fatgen103.doc | |||
| * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx | |||
| */ | |||
| //------------------------------------------------------------------------------ | |||
| /** Value for byte 510 of boot block or MBR */ | |||
| uint8_t const BOOTSIG0 = 0X55; | |||
| /** Value for byte 511 of boot block or MBR */ | |||
| uint8_t const BOOTSIG1 = 0XAA; | |||
| /** Value for bootSignature field int FAT/FAT32 boot sector */ | |||
| uint8_t const EXTENDED_BOOT_SIG = 0X29; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \struct partitionTable | |||
| * \brief MBR partition table entry | |||
| * | |||
| * A partition table entry for a MBR formatted storage device. | |||
| * The MBR partition table has four entries. | |||
| */ | |||
| struct partitionTable { | |||
| /** | |||
| * Boot Indicator . Indicates whether the volume is the active | |||
| * partition. Legal values include: 0X00. Do not use for booting. | |||
| * 0X80 Active partition. | |||
| */ | |||
| uint8_t boot; | |||
| /** | |||
| * Head part of Cylinder-head-sector address of the first block in | |||
| * the partition. Legal values are 0-255. Only used in old PC BIOS. | |||
| */ | |||
| uint8_t beginHead; | |||
| /** | |||
| * Sector part of Cylinder-head-sector address of the first block in | |||
| * the partition. Legal values are 1-63. Only used in old PC BIOS. | |||
| */ | |||
| unsigned beginSector : 6; | |||
| /** High bits cylinder for first block in partition. */ | |||
| unsigned beginCylinderHigh : 2; | |||
| /** | |||
| * Combine beginCylinderLow with beginCylinderHigh. Legal values | |||
| * are 0-1023. Only used in old PC BIOS. | |||
| */ | |||
| uint8_t beginCylinderLow; | |||
| /** | |||
| * Partition type. See defines that begin with PART_TYPE_ for | |||
| * some Microsoft partition types. | |||
| */ | |||
| uint8_t type; | |||
| /** | |||
| * head part of cylinder-head-sector address of the last sector in the | |||
| * partition. Legal values are 0-255. Only used in old PC BIOS. | |||
| */ | |||
| uint8_t endHead; | |||
| /** | |||
| * Sector part of cylinder-head-sector address of the last sector in | |||
| * the partition. Legal values are 1-63. Only used in old PC BIOS. | |||
| */ | |||
| unsigned endSector : 6; | |||
| /** High bits of end cylinder */ | |||
| unsigned endCylinderHigh : 2; | |||
| /** | |||
| * Combine endCylinderLow with endCylinderHigh. Legal values | |||
| * are 0-1023. Only used in old PC BIOS. | |||
| */ | |||
| uint8_t endCylinderLow; | |||
| /** Logical block address of the first block in the partition. */ | |||
| uint32_t firstSector; | |||
| /** Length of the partition, in blocks. */ | |||
| uint32_t totalSectors; | |||
| }__attribute__((packed)); | |||
| /** Type name for partitionTable */ | |||
| typedef struct partitionTable part_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \struct masterBootRecord | |||
| * | |||
| * \brief Master Boot Record | |||
| * | |||
| * The first block of a storage device that is formatted with a MBR. | |||
| */ | |||
| struct masterBootRecord { | |||
| /** Code Area for master boot program. */ | |||
| uint8_t codeArea[440]; | |||
| /** Optional Windows NT disk signature. May contain boot code. */ | |||
| uint32_t diskSignature; | |||
| /** Usually zero but may be more boot code. */ | |||
| uint16_t usuallyZero; | |||
| /** Partition tables. */ | |||
| part_t part[4]; | |||
| /** First MBR signature byte. Must be 0X55 */ | |||
| uint8_t mbrSig0; | |||
| /** Second MBR signature byte. Must be 0XAA */ | |||
| uint8_t mbrSig1; | |||
| }__attribute__((packed)); | |||
| /** Type name for masterBootRecord */ | |||
| typedef struct masterBootRecord mbr_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \struct fat_boot | |||
| * | |||
| * \brief Boot sector for a FAT12/FAT16 volume. | |||
| * | |||
| */ | |||
| struct fat_boot { | |||
| /** | |||
| * The first three bytes of the boot sector must be valid, | |||
| * executable x 86-based CPU instructions. This includes a | |||
| * jump instruction that skips the next non-executable bytes. | |||
| */ | |||
| uint8_t jump[3]; | |||
| /** | |||
| * This is typically a string of characters that identifies | |||
| * the operating system that formatted the volume. | |||
| */ | |||
| char oemId[8]; | |||
| /** | |||
| * The size of a hardware sector. Valid decimal values for this | |||
| * field are 512, 1024, 2048, and 4096. For most disks used in | |||
| * the United States, the value of this field is 512. | |||
| */ | |||
| uint16_t bytesPerSector; | |||
| /** | |||
| * Number of sectors per allocation unit. This value must be a | |||
| * power of 2 that is greater than 0. The legal values are | |||
| * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. | |||
| */ | |||
| uint8_t sectorsPerCluster; | |||
| /** | |||
| * The number of sectors preceding the start of the first FAT, | |||
| * including the boot sector. The value of this field is always 1. | |||
| */ | |||
| uint16_t reservedSectorCount; | |||
| /** | |||
| * The number of copies of the FAT on the volume. | |||
| * The value of this field is always 2. | |||
| */ | |||
| uint8_t fatCount; | |||
| /** | |||
| * For FAT12 and FAT16 volumes, this field contains the count of | |||
| * 32-byte directory entries in the root directory. For FAT32 volumes, | |||
| * this field must be set to 0. For FAT12 and FAT16 volumes, this | |||
| * value should always specify a count that when multiplied by 32 | |||
| * results in a multiple of bytesPerSector. FAT16 volumes should | |||
| * use the value 512. | |||
| */ | |||
| uint16_t rootDirEntryCount; | |||
| /** | |||
| * This field is the old 16-bit total count of sectors on the volume. | |||
| * This count includes the count of all sectors in all four regions | |||
| * of the volume. This field can be 0; if it is 0, then totalSectors32 | |||
| * must be non-zero. For FAT32 volumes, this field must be 0. For | |||
| * FAT12 and FAT16 volumes, this field contains the sector count, and | |||
| * totalSectors32 is 0 if the total sector count fits | |||
| * (is less than 0x10000). | |||
| */ | |||
| uint16_t totalSectors16; | |||
| /** | |||
| * This dates back to the old MS-DOS 1.x media determination and is | |||
| * no longer usually used for anything. 0xF8 is the standard value | |||
| * for fixed (nonremovable) media. For removable media, 0xF0 is | |||
| * frequently used. Legal values are 0xF0 or 0xF8-0xFF. | |||
| */ | |||
| uint8_t mediaType; | |||
| /** | |||
| * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. | |||
| * On FAT32 volumes this field must be 0, and sectorsPerFat32 | |||
| * contains the FAT size count. | |||
| */ | |||
| uint16_t sectorsPerFat16; | |||
| /** Sectors per track for interrupt 0x13. Not used otherwise. */ | |||
| uint16_t sectorsPerTrack; | |||
| /** Number of heads for interrupt 0x13. Not used otherwise. */ | |||
| uint16_t headCount; | |||
| /** | |||
| * Count of hidden sectors preceding the partition that contains this | |||
| * FAT volume. This field is generally only relevant for media | |||
| * visible on interrupt 0x13. | |||
| */ | |||
| uint32_t hidddenSectors; | |||
| /** | |||
| * This field is the new 32-bit total count of sectors on the volume. | |||
| * This count includes the count of all sectors in all four regions | |||
| * of the volume. This field can be 0; if it is 0, then | |||
| * totalSectors16 must be non-zero. | |||
| */ | |||
| uint32_t totalSectors32; | |||
| /** | |||
| * Related to the BIOS physical drive number. Floppy drives are | |||
| * identified as 0x00 and physical hard disks are identified as | |||
| * 0x80, regardless of the number of physical disk drives. | |||
| * Typically, this value is set prior to issuing an INT 13h BIOS | |||
| * call to specify the device to access. The value is only | |||
| * relevant if the device is a boot device. | |||
| */ | |||
| uint8_t driveNumber; | |||
| /** used by Windows NT - should be zero for FAT */ | |||
| uint8_t reserved1; | |||
| /** 0X29 if next three fields are valid */ | |||
| uint8_t bootSignature; | |||
| /** | |||
| * A random serial number created when formatting a disk, | |||
| * which helps to distinguish between disks. | |||
| * Usually generated by combining date and time. | |||
| */ | |||
| uint32_t volumeSerialNumber; | |||
| /** | |||
| * A field once used to store the volume label. The volume label | |||
| * is now stored as a special file in the root directory. | |||
| */ | |||
| char volumeLabel[11]; | |||
| /** | |||
| * A field with a value of either FAT, FAT12 or FAT16, | |||
| * depending on the disk format. | |||
| */ | |||
| char fileSystemType[8]; | |||
| /** X86 boot code */ | |||
| uint8_t bootCode[448]; | |||
| /** must be 0X55 */ | |||
| uint8_t bootSectorSig0; | |||
| /** must be 0XAA */ | |||
| uint8_t bootSectorSig1; | |||
| }__attribute__((packed)); | |||
| /** Type name for FAT Boot Sector */ | |||
| typedef struct fat_boot fat_boot_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \struct fat32_boot | |||
| * | |||
| * \brief Boot sector for a FAT32 volume. | |||
| * | |||
| */ | |||
| struct fat32_boot { | |||
| /** | |||
| * The first three bytes of the boot sector must be valid, | |||
| * executable x 86-based CPU instructions. This includes a | |||
| * jump instruction that skips the next non-executable bytes. | |||
| */ | |||
| uint8_t jump[3]; | |||
| /** | |||
| * This is typically a string of characters that identifies | |||
| * the operating system that formatted the volume. | |||
| */ | |||
| char oemId[8]; | |||
| /** | |||
| * The size of a hardware sector. Valid decimal values for this | |||
| * field are 512, 1024, 2048, and 4096. For most disks used in | |||
| * the United States, the value of this field is 512. | |||
| */ | |||
| uint16_t bytesPerSector; | |||
| /** | |||
| * Number of sectors per allocation unit. This value must be a | |||
| * power of 2 that is greater than 0. The legal values are | |||
| * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. | |||
| */ | |||
| uint8_t sectorsPerCluster; | |||
| /** | |||
| * The number of sectors preceding the start of the first FAT, | |||
| * including the boot sector. Must not be zero | |||
| */ | |||
| uint16_t reservedSectorCount; | |||
| /** | |||
| * The number of copies of the FAT on the volume. | |||
| * The value of this field is always 2. | |||
| */ | |||
| uint8_t fatCount; | |||
| /** | |||
| * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0. | |||
| */ | |||
| uint16_t rootDirEntryCount; | |||
| /** | |||
| * For FAT32 volumes, this field must be 0. | |||
| */ | |||
| uint16_t totalSectors16; | |||
| /** | |||
| * This dates back to the old MS-DOS 1.x media determination and is | |||
| * no longer usually used for anything. 0xF8 is the standard value | |||
| * for fixed (non-removable) media. For removable media, 0xF0 is | |||
| * frequently used. Legal values are 0xF0 or 0xF8-0xFF. | |||
| */ | |||
| uint8_t mediaType; | |||
| /** | |||
| * On FAT32 volumes this field must be 0, and sectorsPerFat32 | |||
| * contains the FAT size count. | |||
| */ | |||
| uint16_t sectorsPerFat16; | |||
| /** Sectors per track for interrupt 0x13. Not used otherwise. */ | |||
| uint16_t sectorsPerTrack; | |||
| /** Number of heads for interrupt 0x13. Not used otherwise. */ | |||
| uint16_t headCount; | |||
| /** | |||
| * Count of hidden sectors preceding the partition that contains this | |||
| * FAT volume. This field is generally only relevant for media | |||
| * visible on interrupt 0x13. | |||
| */ | |||
| uint32_t hidddenSectors; | |||
| /** | |||
| * Contains the total number of sectors in the FAT32 volume. | |||
| */ | |||
| uint32_t totalSectors32; | |||
| /** | |||
| * Count of sectors occupied by one FAT on FAT32 volumes. | |||
| */ | |||
| uint32_t sectorsPerFat32; | |||
| /** | |||
| * This field is only defined for FAT32 media and does not exist on | |||
| * FAT12 and FAT16 media. | |||
| * Bits 0-3 -- Zero-based number of active FAT. | |||
| * Only valid if mirroring is disabled. | |||
| * Bits 4-6 -- Reserved. | |||
| * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. | |||
| * -- 1 means only one FAT is active; it is the one referenced | |||
| * in bits 0-3. | |||
| * Bits 8-15 -- Reserved. | |||
| */ | |||
| uint16_t fat32Flags; | |||
| /** | |||
| * FAT32 version. High byte is major revision number. | |||
| * Low byte is minor revision number. Only 0.0 define. | |||
| */ | |||
| uint16_t fat32Version; | |||
| /** | |||
| * Cluster number of the first cluster of the root directory for FAT32. | |||
| * This usually 2 but not required to be 2. | |||
| */ | |||
| uint32_t fat32RootCluster; | |||
| /** | |||
| * Sector number of FSINFO structure in the reserved area of the | |||
| * FAT32 volume. Usually 1. | |||
| */ | |||
| uint16_t fat32FSInfo; | |||
| /** | |||
| * If non-zero, indicates the sector number in the reserved area | |||
| * of the volume of a copy of the boot record. Usually 6. | |||
| * No value other than 6 is recommended. | |||
| */ | |||
| uint16_t fat32BackBootBlock; | |||
| /** | |||
| * Reserved for future expansion. Code that formats FAT32 volumes | |||
| * should always set all of the bytes of this field to 0. | |||
| */ | |||
| uint8_t fat32Reserved[12]; | |||
| /** | |||
| * Related to the BIOS physical drive number. Floppy drives are | |||
| * identified as 0x00 and physical hard disks are identified as | |||
| * 0x80, regardless of the number of physical disk drives. | |||
| * Typically, this value is set prior to issuing an INT 13h BIOS | |||
| * call to specify the device to access. The value is only | |||
| * relevant if the device is a boot device. | |||
| */ | |||
| uint8_t driveNumber; | |||
| /** used by Windows NT - should be zero for FAT */ | |||
| uint8_t reserved1; | |||
| /** 0X29 if next three fields are valid */ | |||
| uint8_t bootSignature; | |||
| /** | |||
| * A random serial number created when formatting a disk, | |||
| * which helps to distinguish between disks. | |||
| * Usually generated by combining date and time. | |||
| */ | |||
| uint32_t volumeSerialNumber; | |||
| /** | |||
| * A field once used to store the volume label. The volume label | |||
| * is now stored as a special file in the root directory. | |||
| */ | |||
| char volumeLabel[11]; | |||
| /** | |||
| * A text field with a value of FAT32. | |||
| */ | |||
| char fileSystemType[8]; | |||
| /** X86 boot code */ | |||
| uint8_t bootCode[420]; | |||
| /** must be 0X55 */ | |||
| uint8_t bootSectorSig0; | |||
| /** must be 0XAA */ | |||
| uint8_t bootSectorSig1; | |||
| }__attribute__((packed)); | |||
| /** Type name for FAT32 Boot Sector */ | |||
| typedef struct fat32_boot fat32_boot_t; | |||
| //------------------------------------------------------------------------------ | |||
| /** Lead signature for a FSINFO sector */ | |||
| uint32_t const FSINFO_LEAD_SIG = 0x41615252; | |||
| /** Struct signature for a FSINFO sector */ | |||
| uint32_t const FSINFO_STRUCT_SIG = 0x61417272; | |||
| /** | |||
| * \struct fat32_fsinfo | |||
| * | |||
| * \brief FSINFO sector for a FAT32 volume. | |||
| * | |||
| */ | |||
| struct fat32_fsinfo { | |||
| /** must be 0X52, 0X52, 0X61, 0X41 */ | |||
| uint32_t leadSignature; | |||
| /** must be zero */ | |||
| uint8_t reserved1[480]; | |||
| /** must be 0X72, 0X72, 0X41, 0X61 */ | |||
| uint32_t structSignature; | |||
| /** | |||
| * Contains the last known free cluster count on the volume. | |||
| * If the value is 0xFFFFFFFF, then the free count is unknown | |||
| * and must be computed. Any other value can be used, but is | |||
| * not necessarily correct. It should be range checked at least | |||
| * to make sure it is <= volume cluster count. | |||
| */ | |||
| uint32_t freeCount; | |||
| /** | |||
| * This is a hint for the FAT driver. It indicates the cluster | |||
| * number at which the driver should start looking for free clusters. | |||
| * If the value is 0xFFFFFFFF, then there is no hint and the driver | |||
| * should start looking at cluster 2. | |||
| */ | |||
| uint32_t nextFree; | |||
| /** must be zero */ | |||
| uint8_t reserved2[12]; | |||
| /** must be 0X00, 0X00, 0X55, 0XAA */ | |||
| uint8_t tailSignature[4]; | |||
| }__attribute__((packed)); | |||
| /** Type name for FAT32 FSINFO Sector */ | |||
| typedef struct fat32_fsinfo fat32_fsinfo_t; | |||
| //------------------------------------------------------------------------------ | |||
| // End Of Chain values for FAT entries | |||
| /** FAT12 end of chain value used by Microsoft. */ | |||
| uint16_t const FAT12EOC = 0XFFF; | |||
| /** Minimum value for FAT12 EOC. Use to test for EOC. */ | |||
| uint16_t const FAT12EOC_MIN = 0XFF8; | |||
| /** FAT16 end of chain value used by Microsoft. */ | |||
| uint16_t const FAT16EOC = 0XFFFF; | |||
| /** Minimum value for FAT16 EOC. Use to test for EOC. */ | |||
| uint16_t const FAT16EOC_MIN = 0XFFF8; | |||
| /** FAT32 end of chain value used by Microsoft. */ | |||
| uint32_t const FAT32EOC = 0X0FFFFFFF; | |||
| /** Minimum value for FAT32 EOC. Use to test for EOC. */ | |||
| uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; | |||
| /** Mask a for FAT32 entry. Entries are 28 bits. */ | |||
| uint32_t const FAT32MASK = 0X0FFFFFFF; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * \struct directoryEntry | |||
| * \brief FAT short directory entry | |||
| * | |||
| * Short means short 8.3 name, not the entry size. | |||
| * | |||
| * Date Format. A FAT directory entry date stamp is a 16-bit field that is | |||
| * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the | |||
| * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the | |||
| * 16-bit word): | |||
| * | |||
| * Bits 9-15: Count of years from 1980, valid value range 0-127 | |||
| * inclusive (1980-2107). | |||
| * | |||
| * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. | |||
| * | |||
| * Bits 0-4: Day of month, valid value range 1-31 inclusive. | |||
| * | |||
| * Time Format. A FAT directory entry time stamp is a 16-bit field that has | |||
| * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the | |||
| * 16-bit word, bit 15 is the MSB of the 16-bit word). | |||
| * | |||
| * Bits 11-15: Hours, valid value range 0-23 inclusive. | |||
| * | |||
| * Bits 5-10: Minutes, valid value range 0-59 inclusive. | |||
| * | |||
| * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). | |||
| * | |||
| * The valid time range is from Midnight 00:00:00 to 23:59:58. | |||
| */ | |||
| struct directoryEntry { | |||
| /** Short 8.3 name. | |||
| * | |||
| * The first eight bytes contain the file name with blank fill. | |||
| * The last three bytes contain the file extension with blank fill. | |||
| */ | |||
| uint8_t name[11]; | |||
| /** Entry attributes. | |||
| * | |||
| * The upper two bits of the attribute byte are reserved and should | |||
| * always be set to 0 when a file is created and never modified or | |||
| * looked at after that. See defines that begin with DIR_ATT_. | |||
| */ | |||
| uint8_t attributes; | |||
| /** | |||
| * Reserved for use by Windows NT. Set value to 0 when a file is | |||
| * created and never modify or look at it after that. | |||
| */ | |||
| uint8_t reservedNT; | |||
| /** | |||
| * The granularity of the seconds part of creationTime is 2 seconds | |||
| * so this field is a count of tenths of a second and its valid | |||
| * value range is 0-199 inclusive. (WHG note - seems to be hundredths) | |||
| */ | |||
| uint8_t creationTimeTenths; | |||
| /** Time file was created. */ | |||
| uint16_t creationTime; | |||
| /** Date file was created. */ | |||
| uint16_t creationDate; | |||
| /** | |||
| * Last access date. Note that there is no last access time, only | |||
| * a date. This is the date of last read or write. In the case of | |||
| * a write, this should be set to the same date as lastWriteDate. | |||
| */ | |||
| uint16_t lastAccessDate; | |||
| /** | |||
| * High word of this entry's first cluster number (always 0 for a | |||
| * FAT12 or FAT16 volume). | |||
| */ | |||
| uint16_t firstClusterHigh; | |||
| /** Time of last write. File creation is considered a write. */ | |||
| uint16_t lastWriteTime; | |||
| /** Date of last write. File creation is considered a write. */ | |||
| uint16_t lastWriteDate; | |||
| /** Low word of this entry's first cluster number. */ | |||
| uint16_t firstClusterLow; | |||
| /** 32-bit unsigned holding this file's size in bytes. */ | |||
| uint32_t fileSize; | |||
| }__attribute__((packed)); | |||
| //------------------------------------------------------------------------------ | |||
| // Definitions for directory entries | |||
| // | |||
| /** Type name for directoryEntry */ | |||
| typedef struct directoryEntry dir_t; | |||
| /** escape for name[0] = 0XE5 */ | |||
| uint8_t const DIR_NAME_0XE5 = 0X05; | |||
| /** name[0] value for entry that is free after being "deleted" */ | |||
| uint8_t const DIR_NAME_DELETED = 0XE5; | |||
| /** name[0] value for entry that is free and no allocated entries follow */ | |||
| uint8_t const DIR_NAME_FREE = 0X00; | |||
| /** file is read-only */ | |||
| uint8_t const DIR_ATT_READ_ONLY = 0X01; | |||
| /** File should hidden in directory listings */ | |||
| uint8_t const DIR_ATT_HIDDEN = 0X02; | |||
| /** Entry is for a system file */ | |||
| uint8_t const DIR_ATT_SYSTEM = 0X04; | |||
| /** Directory entry contains the volume label */ | |||
| uint8_t const DIR_ATT_VOLUME_ID = 0X08; | |||
| /** Entry is for a directory */ | |||
| uint8_t const DIR_ATT_DIRECTORY = 0X10; | |||
| /** Old DOS archive bit for backup support */ | |||
| uint8_t const DIR_ATT_ARCHIVE = 0X20; | |||
| /** Test value for long name entry. Test is | |||
| (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ | |||
| uint8_t const DIR_ATT_LONG_NAME = 0X0F; | |||
| /** Test mask for long name entry */ | |||
| uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; | |||
| /** defined attribute bits */ | |||
| uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; | |||
| /** Directory entry is part of a long name | |||
| * \param[in] dir Pointer to a directory entry. | |||
| * | |||
| * \return true if the entry is for part of a long name else false. | |||
| */ | |||
| static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { | |||
| return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; | |||
| } | |||
| /** Mask for file/subdirectory tests */ | |||
| uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); | |||
| /** Directory entry is for a file | |||
| * \param[in] dir Pointer to a directory entry. | |||
| * | |||
| * \return true if the entry is for a normal file else false. | |||
| */ | |||
| static inline uint8_t DIR_IS_FILE(const dir_t* dir) { | |||
| return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; | |||
| } | |||
| /** Directory entry is for a subdirectory | |||
| * \param[in] dir Pointer to a directory entry. | |||
| * | |||
| * \return true if the entry is for a subdirectory else false. | |||
| */ | |||
| static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { | |||
| return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; | |||
| } | |||
| /** Directory entry is for a file or subdirectory | |||
| * \param[in] dir Pointer to a directory entry. | |||
| * | |||
| * \return true if the entry is for a normal file or subdirectory else false. | |||
| */ | |||
| static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { | |||
| return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; | |||
| } | |||
| /** date field for FAT directory entry | |||
| * \param[in] year [1980,2107] | |||
| * \param[in] month [1,12] | |||
| * \param[in] day [1,31] | |||
| * | |||
| * \return Packed date for dir_t entry. | |||
| */ | |||
| static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { | |||
| return (year - 1980) << 9 | month << 5 | day; | |||
| } | |||
| /** year part of FAT directory date field | |||
| * \param[in] fatDate Date in packed dir format. | |||
| * | |||
| * \return Extracted year [1980,2107] | |||
| */ | |||
| static inline uint16_t FAT_YEAR(uint16_t fatDate) { | |||
| return 1980 + (fatDate >> 9); | |||
| } | |||
| /** month part of FAT directory date field | |||
| * \param[in] fatDate Date in packed dir format. | |||
| * | |||
| * \return Extracted month [1,12] | |||
| */ | |||
| static inline uint8_t FAT_MONTH(uint16_t fatDate) { | |||
| return (fatDate >> 5) & 0XF; | |||
| } | |||
| /** day part of FAT directory date field | |||
| * \param[in] fatDate Date in packed dir format. | |||
| * | |||
| * \return Extracted day [1,31] | |||
| */ | |||
| static inline uint8_t FAT_DAY(uint16_t fatDate) { | |||
| return fatDate & 0X1F; | |||
| } | |||
| /** time field for FAT directory entry | |||
| * \param[in] hour [0,23] | |||
| * \param[in] minute [0,59] | |||
| * \param[in] second [0,59] | |||
| * | |||
| * \return Packed time for dir_t entry. | |||
| */ | |||
| static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { | |||
| return hour << 11 | minute << 5 | second >> 1; | |||
| } | |||
| /** hour part of FAT directory time field | |||
| * \param[in] fatTime Time in packed dir format. | |||
| * | |||
| * \return Extracted hour [0,23] | |||
| */ | |||
| static inline uint8_t FAT_HOUR(uint16_t fatTime) { | |||
| return fatTime >> 11; | |||
| } | |||
| /** minute part of FAT directory time field | |||
| * \param[in] fatTime Time in packed dir format. | |||
| * | |||
| * \return Extracted minute [0,59] | |||
| */ | |||
| static inline uint8_t FAT_MINUTE(uint16_t fatTime) { | |||
| return(fatTime >> 5) & 0X3F; | |||
| } | |||
| /** second part of FAT directory time field | |||
| * Note second/2 is stored in packed time. | |||
| * | |||
| * \param[in] fatTime Time in packed dir format. | |||
| * | |||
| * \return Extracted second [0,58] | |||
| */ | |||
| static inline uint8_t FAT_SECOND(uint16_t fatTime) { | |||
| return 2*(fatTime & 0X1F); | |||
| } | |||
| /** Default date for file timestamps is 1 Jan 2000 */ | |||
| uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; | |||
| /** Default time for file timestamp is 1 am */ | |||
| uint16_t const FAT_DEFAULT_TIME = (1 << 11); | |||
| #endif // FatStructs_h | |||
| @@ -0,0 +1,405 @@ | |||
| /* FatLib Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the FatLib Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the FatLib Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #include <avr/pgmspace.h> | |||
| #include <FmtNumber.h> | |||
| // Use Stimmer div/mod 10 on avr | |||
| #ifdef __AVR__ | |||
| #define USE_STIMMER | |||
| #endif // __AVR__ | |||
| //------------------------------------------------------------------------------ | |||
| // Stimmer div/mod 10 for AVR | |||
| // this code fragment works out i/10 and i%10 by calculating | |||
| // i*(51/256)*(256/255)/2 == i*51/510 == i/10 | |||
| // by "j.k" I mean 32.8 fixed point, j is integer part, k is fractional part | |||
| // j.k = ((j+1.0)*51.0)/256.0 | |||
| // (we add 1 because we will be using the floor of the result later) | |||
| // divmod10_asm16 and divmod10_asm32 are public domain code by Stimmer. | |||
| // http://forum.arduino.cc/index.php?topic=167414.msg1293679#msg1293679 | |||
| #define divmod10_asm16(in32, mod8, tmp8) \ | |||
| asm volatile( \ | |||
| " ldi %2,51 \n\t" \ | |||
| " mul %A0,%2 \n\t" \ | |||
| " clr %A0 \n\t" \ | |||
| " add r0,%2 \n\t" \ | |||
| " adc %A0,r1 \n\t" \ | |||
| " mov %1,r0 \n\t" \ | |||
| " mul %B0,%2 \n\t" \ | |||
| " clr %B0 \n\t" \ | |||
| " add %A0,r0 \n\t" \ | |||
| " adc %B0,r1 \n\t" \ | |||
| " clr r1 \n\t" \ | |||
| " add %1,%A0 \n\t" \ | |||
| " adc %A0,%B0 \n\t" \ | |||
| " adc %B0,r1 \n\t" \ | |||
| " add %1,%B0 \n\t" \ | |||
| " adc %A0,r1 \n\t" \ | |||
| " adc %B0,r1 \n\t" \ | |||
| " lsr %B0 \n\t" \ | |||
| " ror %A0 \n\t" \ | |||
| " ror %1 \n\t" \ | |||
| " ldi %2,10 \n\t" \ | |||
| " mul %1,%2 \n\t" \ | |||
| " mov %1,r1 \n\t" \ | |||
| " clr r1 \n\t" \ | |||
| :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0") | |||
| #define divmod10_asm32(in32, mod8, tmp8) \ | |||
| asm volatile( \ | |||
| " ldi %2,51 \n\t" \ | |||
| " mul %A0,%2 \n\t" \ | |||
| " clr %A0 \n\t" \ | |||
| " add r0,%2 \n\t" \ | |||
| " adc %A0,r1 \n\t" \ | |||
| " mov %1,r0 \n\t" \ | |||
| " mul %B0,%2 \n\t" \ | |||
| " clr %B0 \n\t" \ | |||
| " add %A0,r0 \n\t" \ | |||
| " adc %B0,r1 \n\t" \ | |||
| " mul %C0,%2 \n\t" \ | |||
| " clr %C0 \n\t" \ | |||
| " add %B0,r0 \n\t" \ | |||
| " adc %C0,r1 \n\t" \ | |||
| " mul %D0,%2 \n\t" \ | |||
| " clr %D0 \n\t" \ | |||
| " add %C0,r0 \n\t" \ | |||
| " adc %D0,r1 \n\t" \ | |||
| " clr r1 \n\t" \ | |||
| " add %1,%A0 \n\t" \ | |||
| " adc %A0,%B0 \n\t" \ | |||
| " adc %B0,%C0 \n\t" \ | |||
| " adc %C0,%D0 \n\t" \ | |||
| " adc %D0,r1 \n\t" \ | |||
| " add %1,%B0 \n\t" \ | |||
| " adc %A0,%C0 \n\t" \ | |||
| " adc %B0,%D0 \n\t" \ | |||
| " adc %C0,r1 \n\t" \ | |||
| " adc %D0,r1 \n\t" \ | |||
| " add %1,%D0 \n\t" \ | |||
| " adc %A0,r1 \n\t" \ | |||
| " adc %B0,r1 \n\t" \ | |||
| " adc %C0,r1 \n\t" \ | |||
| " adc %D0,r1 \n\t" \ | |||
| " lsr %D0 \n\t" \ | |||
| " ror %C0 \n\t" \ | |||
| " ror %B0 \n\t" \ | |||
| " ror %A0 \n\t" \ | |||
| " ror %1 \n\t" \ | |||
| " ldi %2,10 \n\t" \ | |||
| " mul %1,%2 \n\t" \ | |||
| " mov %1,r1 \n\t" \ | |||
| " clr r1 \n\t" \ | |||
| :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0") | |||
| //------------------------------------------------------------------------------ | |||
| /* | |||
| // C++ code is based on this version of divmod10 by robtillaart. | |||
| // http://forum.arduino.cc/index.php?topic=167414.msg1246851#msg1246851 | |||
| // from robtillaart post: | |||
| // The code is based upon the divu10() code from the book Hackers Delight1. | |||
| // My insight was that the error formula in divu10() was in fact modulo 10 | |||
| // but not always. Sometimes it was 10 more. | |||
| void divmod10(uint32_t in, uint32_t &div, uint32_t &mod) | |||
| { | |||
| // q = in * 0.8; | |||
| uint32_t q = (in >> 1) + (in >> 2); | |||
| q = q + (q >> 4); | |||
| q = q + (q >> 8); | |||
| q = q + (q >> 16); // not needed for 16 bit version | |||
| // q = q / 8; ==> q = in *0.1; | |||
| q = q >> 3; | |||
| // determine error | |||
| uint32_t r = in - ((q << 3) + (q << 1)); // r = in - q*10; | |||
| div = q + (r > 9); | |||
| if (r > 9) mod = r - 10; | |||
| else mod = r; | |||
| } | |||
| // Hackers delight function is here: | |||
| // http://www.hackersdelight.org/hdcodetxt/divuc.c.txt | |||
| // Code below uses 8/10 = 0.1100 1100 1100 1100 1100 1100 1100 1100. | |||
| // 15 ops including the multiply, or 17 elementary ops. | |||
| unsigned divu10(unsigned n) { | |||
| unsigned q, r; | |||
| q = (n >> 1) + (n >> 2); | |||
| q = q + (q >> 4); | |||
| q = q + (q >> 8); | |||
| q = q + (q >> 16); | |||
| q = q >> 3; | |||
| r = n - q*10; | |||
| return q + ((r + 6) >> 4); | |||
| // return q + (r > 9); | |||
| } | |||
| */ | |||
| //------------------------------------------------------------------------------ | |||
| static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; | |||
| static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; | |||
| // scale float v by power of ten. return v*10^n | |||
| float scale10(float v, int8_t n) { | |||
| const float *s; | |||
| if (n < 0) { | |||
| n = -n; | |||
| s = m; | |||
| } else { | |||
| s = p; | |||
| } | |||
| n &= 63; | |||
| for (uint8_t i = 0; n; n >>= 1, i++) { | |||
| if (n & 1) v *= pgm_read_float(&s[i]); | |||
| } | |||
| return v; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // Format 16-bit unsigned | |||
| char* fmtDec(uint16_t n, char* p) { | |||
| while (n > 9) { | |||
| #ifdef USE_STIMMER | |||
| uint8_t tmp8, r; | |||
| divmod10_asm16(n, r, tmp8); | |||
| #else // USE_STIMMER | |||
| uint16_t t = n; | |||
| n = (n >> 1) + (n >> 2); | |||
| n = n + (n >> 4); | |||
| n = n + (n >> 8); | |||
| // n = n + (n >> 16); // no code for 16-bit n | |||
| n = n >> 3; | |||
| uint8_t r = t - (((n << 2) + n) << 1); | |||
| if (r > 9) { | |||
| n++; | |||
| r -= 10; | |||
| } | |||
| #endif // USE_STIMMER | |||
| *--p = r + '0'; | |||
| } | |||
| *--p = n + '0'; | |||
| return p; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| // format 32-bit unsigned | |||
| char* fmtDec(uint32_t n, char* p) { | |||
| while (n >> 16) { | |||
| #ifdef USE_STIMMER | |||
| uint8_t tmp8, r; | |||
| divmod10_asm32(n, r, tmp8); | |||
| #else // USE_STIMMER | |||
| uint32_t t = n; | |||
| n = (n >> 1) + (n >> 2); | |||
| n = n + (n >> 4); | |||
| n = n + (n >> 8); | |||
| n = n + (n >> 16); | |||
| n = n >> 3; | |||
| uint8_t r = t - (((n << 2) + n) << 1); | |||
| if (r > 9) { | |||
| n++; | |||
| r -= 10; | |||
| } | |||
| #endif // USE_STIMMER | |||
| *--p = r + '0'; | |||
| } | |||
| return fmtDec((uint16_t)n, p); | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| char* fmtFloat(float value, char* p, uint8_t prec) { | |||
| char sign = value < 0 ? '-' : 0; | |||
| if (sign) value = -value; | |||
| if (isnan(value)) { | |||
| *--p = 'n'; | |||
| *--p = 'a'; | |||
| *--p = 'n'; | |||
| return p; | |||
| } | |||
| if (isinf(value)) { | |||
| *--p = 'f'; | |||
| *--p = 'n'; | |||
| *--p = 'i'; | |||
| return p; | |||
| } | |||
| if (value > 4294967040.0) { | |||
| *--p = 'f'; | |||
| *--p = 'v'; | |||
| *--p = 'o'; | |||
| return p; | |||
| } | |||
| if (prec > 9) prec = 9; | |||
| value += scale10(0.5, -prec); | |||
| uint32_t whole = value; | |||
| if (prec) { | |||
| char* tmp = p - prec; | |||
| uint32_t fraction = scale10(value - whole, prec); | |||
| p = fmtDec(fraction, p); | |||
| while (p > tmp) *--p = '0'; | |||
| *--p = '.'; | |||
| } | |||
| p = fmtDec(whole, p); | |||
| if (sign) *--p = sign; | |||
| return p; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| /** Print a number followed by a field terminator. | |||
| * \param[in] value The number to be printed. | |||
| * \param[in] ptr Pointer to last char in buffer. | |||
| * \param[in] prec Number of digits after decimal point. | |||
| * \param[in] expChar Use exp format if non zero. | |||
| * \return Pointer to first character of result. | |||
| */ | |||
| char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { | |||
| bool neg = value < 0; | |||
| if (neg) value = -value; | |||
| // check for nan inf ovf | |||
| if (isnan(value)) { | |||
| *--ptr = 'n'; | |||
| *--ptr = 'a'; | |||
| *--ptr = 'n'; | |||
| return ptr; | |||
| } | |||
| if (isinf(value)) { | |||
| *--ptr = 'f'; | |||
| *--ptr = 'n'; | |||
| *--ptr = 'i'; | |||
| return ptr; | |||
| } | |||
| if (!expChar && value > 4294967040.0) { | |||
| *--ptr = 'f'; | |||
| *--ptr = 'v'; | |||
| *--ptr = 'o'; | |||
| return ptr; | |||
| } | |||
| if (prec > 9) prec = 9; | |||
| float round = scale10(0.5, -prec); | |||
| if (expChar) { | |||
| int8_t exp = 0; | |||
| bool expNeg = false; | |||
| if (value) { | |||
| while (value > 10.0) { | |||
| value *= 0.1; | |||
| exp++; | |||
| } | |||
| while (value < 1.0) { | |||
| value *= 10.0; | |||
| exp--; | |||
| } | |||
| value += round; | |||
| if (value > 10.0) { | |||
| value *= 0.1; | |||
| exp++; | |||
| } | |||
| expNeg = exp < 0; | |||
| if (expNeg) exp = -exp; | |||
| } | |||
| ptr = fmtDec((uint16_t)exp, ptr); | |||
| if (exp < 10) *--ptr = '0'; | |||
| *--ptr = expNeg ? '-' : '+'; | |||
| *--ptr = expChar; | |||
| } else { | |||
| // round value | |||
| value += round; | |||
| } | |||
| uint32_t whole = value; | |||
| if (prec) { | |||
| char* tmp = ptr - prec; | |||
| uint32_t fraction = scale10(value - whole, prec); | |||
| ptr = fmtDec(fraction, ptr); | |||
| while (ptr > tmp) *--ptr = '0'; | |||
| *--ptr = '.'; | |||
| } | |||
| ptr = fmtDec(whole, ptr); | |||
| if (neg) *--ptr = '-'; | |||
| return ptr; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| char* fmtHex(uint32_t n, char* p) { | |||
| do { | |||
| uint8_t h = n & 0XF; | |||
| *--p = h + (h < 10 ? '0' : 'A' - 10); | |||
| n >>= 4; | |||
| } while (n); | |||
| return p; | |||
| } | |||
| //------------------------------------------------------------------------------ | |||
| float scanFloat(const char* str, char** ptr) { | |||
| int16_t const EXP_LIMIT = 100; | |||
| bool digit = false; | |||
| bool dot = false; | |||
| uint32_t fract = 0; | |||
| int fracExp = 0; | |||
| uint8_t nd = 0; | |||
| bool neg; | |||
| int c; | |||
| float v; | |||
| const char* successPtr; | |||
| if (ptr) *ptr = const_cast<char*>(str); | |||
| while (isspace((c = *str++))) {} | |||
| neg = c == '-'; | |||
| if (c == '-' || c == '+') c = *str++; | |||
| // Skip leading zeros | |||
| while (c == '0') { | |||
| c = *str++; | |||
| digit = true; | |||
| } | |||
| for (;;) { | |||
| if (isdigit(c)) { | |||
| digit = true; | |||
| if (nd < 9) { | |||
| fract = 10*fract + c - '0'; | |||
| nd++; | |||
| if (dot) fracExp--; | |||
| } else { | |||
| if (!dot) fracExp++; | |||
| } | |||
| } else if (c == '.') { | |||
| if (dot) goto fail; | |||
| dot = true; | |||
| } else { | |||
| if (!digit) goto fail; | |||
| break; | |||
| } | |||
| successPtr = str; | |||
| c = *str++; | |||
| } | |||
| if (c == 'e' || c == 'E') { | |||
| int exp = 0; | |||
| c = *str++; | |||
| bool expNeg = c == '-'; | |||
| if (c == '-' || c == '+') { | |||
| c = *str++; | |||
| } | |||
| while (isdigit(c)) { | |||
| if (exp > EXP_LIMIT) goto fail; | |||
| exp = 10*exp + c - '0'; | |||
| successPtr = str; | |||
| c = *str++; | |||
| } | |||
| fracExp += expNeg ? -exp : exp; | |||
| } | |||
| if (ptr) *ptr = const_cast<char*>(successPtr); | |||
| v = scale10(static_cast<float>(fract), fracExp); | |||
| return neg ? -v: v; | |||
| fail: | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| /* FatLib Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the FatLib Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the FatLib Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| #ifndef FmtNumber_h | |||
| #define FmtNumber_h | |||
| #include <Arduino.h> | |||
| char* fmtDec(uint16_t n, char* p); | |||
| char* fmtDec(uint32_t n, char* p); | |||
| char* fmtFloat(float value, char* p, uint8_t prec); | |||
| char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar); | |||
| char* fmtHex(uint32_t n, char* p); | |||
| float scale10(float v, int8_t n); | |||
| float scanFloat(const char* str, char** ptr); | |||
| #endif // FmtNumber_h | |||
| @@ -0,0 +1,162 @@ | |||
| /* Arduino DigitalIO Library | |||
| * Copyright (C) 2013 by William Greiman | |||
| * | |||
| * This file is part of the Arduino DigitalIO Library | |||
| * | |||
| * This Library is free software: you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation, either version 3 of the License, or | |||
| * (at your option) any later version. | |||
| * | |||
| * This Library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU General Public License | |||
| * along with the Arduino DigitalIO Library. If not, see | |||
| * <http://www.gnu.org/licenses/>. | |||
| */ | |||
| /** | |||
| * @file | |||
| * @brief Software SPI. | |||
| * | |||
| * @defgroup softSPI Software SPI | |||
| * @details Software SPI Template Class. | |||
| * @{ | |||
| */ | |||
| #ifndef SoftSPI_h | |||
| #define SoftSPI_h | |||
| #include <DigitalPin.h> | |||
| //------------------------------------------------------------------------------ | |||
| /** Nop for timing. */ | |||
| #define nop asm volatile ("nop\n\t") | |||
| //------------------------------------------------------------------------------ | |||
| /** Pin Mode for MISO is input.*/ | |||
| const bool MISO_MODE = false; | |||
| /** Pullups disabled for MISO are disabled. */ | |||
| const bool MISO_LEVEL = false; | |||
| /** Pin Mode for MOSI is output.*/ | |||
| const bool MOSI_MODE = true; | |||
| /** Pin Mode for SCK is output. */ | |||
| const bool SCK_MODE = true; | |||
| //------------------------------------------------------------------------------ | |||
| /** | |||
| * @class SoftSPI | |||
| * @brief Fast software SPI. | |||
| */ | |||
| template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin, uint8_t Mode = 0> | |||
| class SoftSPI { | |||
| public: | |||
| //---------------------------------------------------------------------------- | |||
| /** Initialize SoftSPI pins. */ | |||
| void begin() { | |||
| fastPinConfig(MisoPin, MISO_MODE, MISO_LEVEL); | |||
| fastPinConfig(MosiPin, MOSI_MODE, !MODE_CPHA(Mode)); | |||
| fastPinConfig(SckPin, SCK_MODE, MODE_CPOL(Mode)); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Soft SPI receive byte. | |||
| * @return Data byte received. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| uint8_t receive() { | |||
| uint8_t data = 0; | |||
| receiveBit(7, &data); | |||
| receiveBit(6, &data); | |||
| receiveBit(5, &data); | |||
| receiveBit(4, &data); | |||
| receiveBit(3, &data); | |||
| receiveBit(2, &data); | |||
| receiveBit(1, &data); | |||
| receiveBit(0, &data); | |||
| return data; | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Soft SPI send byte. | |||
| * @param[in] data Data byte to send. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| void send(uint8_t data) { | |||
| sendBit(7, data); | |||
| sendBit(6, data); | |||
| sendBit(5, data); | |||
| sendBit(4, data); | |||
| sendBit(3, data); | |||
| sendBit(2, data); | |||
| sendBit(1, data); | |||
| sendBit(0, data); | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| /** Soft SPI transfer byte. | |||
| * @param[in] txData Data byte to send. | |||
| * @return Data byte received. | |||
| */ | |||
| inline __attribute__((always_inline)) | |||
| uint8_t transfer(uint8_t txData) { | |||
| uint8_t rxData = 0; | |||
| transferBit(7, &rxData, txData); | |||
| transferBit(6, &rxData, txData); | |||
| transferBit(5, &rxData, txData); | |||
| transferBit(4, &rxData, txData); | |||
| transferBit(3, &rxData, txData); | |||
| transferBit(2, &rxData, txData); | |||
| transferBit(1, &rxData, txData); | |||
| transferBit(0, &rxData, txData); | |||
| return rxData; | |||
| } | |||
| private: | |||
| //---------------------------------------------------------------------------- | |||
| inline __attribute__((always_inline)) | |||
| bool MODE_CPHA(uint8_t mode) {return (mode & 1) != 0;} | |||
| inline __attribute__((always_inline)) | |||
| bool MODE_CPOL(uint8_t mode) {return (mode & 2) != 0;} | |||
| inline __attribute__((always_inline)) | |||
| void receiveBit(uint8_t bit, uint8_t* data) { | |||
| if (MODE_CPHA(Mode)) { | |||
| fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); | |||
| } | |||
| nop; | |||
| nop; | |||
| fastDigitalWrite(SckPin, | |||
| MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); | |||
| if (fastDigitalRead(MisoPin)) *data |= 1 << bit; | |||
| if (!MODE_CPHA(Mode)) { | |||
| fastDigitalWrite(SckPin, MODE_CPOL(Mode)); | |||
| } | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| inline __attribute__((always_inline)) | |||
| void sendBit(uint8_t bit, uint8_t data) { | |||
| if (MODE_CPHA(Mode)) { | |||
| fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); | |||
| } | |||
| fastDigitalWrite(MosiPin, data & (1 << bit)); | |||
| fastDigitalWrite(SckPin, | |||
| MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); | |||
| nop; | |||
| nop; | |||
| if (!MODE_CPHA(Mode)) { | |||
| fastDigitalWrite(SckPin, MODE_CPOL(Mode)); | |||
| } | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| inline __attribute__((always_inline)) | |||
| void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData) { | |||
| if (MODE_CPHA(Mode)) { | |||
| fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); | |||
| } | |||
| fastDigitalWrite(MosiPin, txData & (1 << bit)); | |||
| fastDigitalWrite(SckPin, | |||
| MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); | |||
| if (fastDigitalRead(MisoPin)) *rxData |= 1 << bit; | |||
| if (!MODE_CPHA(Mode)) { | |||
| fastDigitalWrite(SckPin, MODE_CPOL(Mode)); | |||
| } | |||
| } | |||
| //---------------------------------------------------------------------------- | |||
| }; | |||
| #endif // SoftSPI_h | |||
| /** @} */ | |||