First Commit
This commit is contained in:
21
libraries/SdFat/LICENSE.md
Normal file
21
libraries/SdFat/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2011..2020 Bill Greiman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
110
libraries/SdFat/README.md
Normal file
110
libraries/SdFat/README.md
Normal file
@@ -0,0 +1,110 @@
|
||||
File copy constructors and file assignment operators have been made private by
|
||||
default in 2.2.3 to prevent call by value and multiple copies of file instances.
|
||||
|
||||
SdFatConfig.h has options to make file constructors and assignment operators
|
||||
public.
|
||||
|
||||
UTF-8 encoded filenames are supported in v2.1.0 or later.
|
||||
|
||||
Try the UnicodeFilenames example. Here is output from ls:
|
||||
<pre>
|
||||
Type any character to begin
|
||||
ls:
|
||||
0 😀/
|
||||
20 россиянин
|
||||
17 très élégant
|
||||
9 狗.txt
|
||||
</pre>
|
||||
|
||||
SdFat Version 2 supports FAT16/FAT32 and exFAT SD cards. It is mostly
|
||||
backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
|
||||
|
||||
exFAT supports files larger than 4GB so files sizes and positions are
|
||||
type uint64_t for classes that support exFAT.
|
||||
|
||||
exFAT has many features not available in FAT16/FAT32. exFAT has excellent
|
||||
support for contiguous files on flash devices and supports preallocation.
|
||||
|
||||
If the SD card is the only SPI device, use dedicated SPI mode. This can
|
||||
greatly improve performance. See the bench example.
|
||||
|
||||
Here is write performance for an old, 2011, card on a Due board.
|
||||
```
|
||||
Shared SPI:
|
||||
write speed and latency
|
||||
speed,max,min,avg
|
||||
KB/Sec,usec,usec,usec
|
||||
294.45,24944,1398,1737
|
||||
|
||||
Dedicated SPI:
|
||||
write speed and latency
|
||||
speed,max,min,avg
|
||||
KB/Sec,usec,usec,usec
|
||||
3965.11,16733,110,127
|
||||
```
|
||||
The default version of SdFatConfig.h enables support for dedicated SPI and
|
||||
optimized access to contiguous files. This makes SdFat Version 2 slightly
|
||||
larger than Version 1. If these features are disabled, Version 2 is smaller
|
||||
than Version 1.
|
||||
|
||||
The types for the classes SdFat and File are defined in SdFatConfig.h.
|
||||
The default version of SdFatConfig.h defines SdFat to only support FAT16/FAT32.
|
||||
SdFat and File are defined in terms of more basic classes by typedefs. You
|
||||
can use these basic classes in applications.
|
||||
|
||||
Support for exFAT requires a substantial amount of flash. Here are sizes on
|
||||
an UNO for a simple program that opens a file, prints one line, and closes
|
||||
the file.
|
||||
```
|
||||
FAT16/FAT32 only: 9780 bytes flash, 875 bytes SRAM.
|
||||
|
||||
exFAT only: 13830 bytes flash, 938 bytes SRAM.
|
||||
|
||||
FAT16/FAT32/exFAT: 19326 bytes flash, 928 bytes SRAM.
|
||||
```
|
||||
The section below of SdFatConfig.h has been edited to uses FAT16/FAT32 for
|
||||
small AVR boards and FAT16/FAT32/exFAT for all other boards.
|
||||
```
|
||||
/**
|
||||
* File types for SdFat, File, SdFile, SdBaseFile, fstream,
|
||||
* ifstream, and ofstream.
|
||||
*
|
||||
* Set SDFAT_FILE_TYPE to:
|
||||
*
|
||||
* 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
*/
|
||||
#if defined(__AVR__) && FLASHEND < 0X8000
|
||||
// FAT16/FAT32 for 32K AVR boards.
|
||||
#define SDFAT_FILE_TYPE 1
|
||||
#else // defined(__AVR__) && FLASHEND < 0X8000
|
||||
// FAT16/FAT32 and exFAT for all other boards.
|
||||
#define SDFAT_FILE_TYPE 3
|
||||
#endif // defined(__AVR__) && FLASHEND < 0X8000
|
||||
```
|
||||
The SdBaseFile class has no Arduino Stream or Print support.
|
||||
|
||||
The File class is derived from Stream and SdBaseFile.
|
||||
|
||||
The SdFile class is derived from SdBaseFile and Print.
|
||||
|
||||
Please try the examples. Start with SdInfo, bench, and ExFatLogger.
|
||||
|
||||
To use SdFat Version 2, unzip the download file, rename the library folder
|
||||
SdFat and place the SdFat folder into the libraries sub-folder in your main
|
||||
sketch folder.
|
||||
|
||||
For more information see the Manual installation section of this guide:
|
||||
|
||||
http://arduino.cc/en/Guide/Libraries
|
||||
|
||||
A number of configuration options can be set by editing SdFatConfig.h
|
||||
define macros. See the html documentation File tab for details.
|
||||
|
||||
Please read the html documentation for this library in SdFat/doc/SdFat.html.
|
||||
Start with the Main Page. Next go to the Classes tab and read the
|
||||
documentation for the classes SdFat32, SdExFat, SdFs, File32, ExFile, FsFile.
|
||||
|
||||
The SdFat and File classes are defined in terms of the above classes by
|
||||
typedefs. Edit SdFatConfig.h to select class options.
|
||||
|
||||
Please continue by reading the html documentation in the SdFat/doc folder.
|
||||
2783
libraries/SdFat/doc/Doxyfile
Normal file
2783
libraries/SdFat/doc/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
51
libraries/SdFat/doc/SdErrorCodes.txt
Normal file
51
libraries/SdFat/doc/SdErrorCodes.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
2022-07-01
|
||||
|
||||
Run the SdErrorCode example to produce an updated list.
|
||||
|
||||
Code,Symbol - failed operation
|
||||
0X00,SD_CARD_ERROR_NONE - No error
|
||||
0X01,SD_CARD_ERROR_CMD0 - Card reset failed
|
||||
0X02,SD_CARD_ERROR_CMD2 - SDIO read CID
|
||||
0X03,SD_CARD_ERROR_CMD3 - SDIO publish RCA
|
||||
0X04,SD_CARD_ERROR_CMD6 - Switch card function
|
||||
0X05,SD_CARD_ERROR_CMD7 - SDIO card select
|
||||
0X06,SD_CARD_ERROR_CMD8 - Send and check interface settings
|
||||
0X07,SD_CARD_ERROR_CMD9 - Read CSD data
|
||||
0X08,SD_CARD_ERROR_CMD10 - Read CID data
|
||||
0X09,SD_CARD_ERROR_CMD12 - Stop multiple block read
|
||||
0X0A,SD_CARD_ERROR_CMD13 - Read card status
|
||||
0X0B,SD_CARD_ERROR_CMD17 - Read single block
|
||||
0X0C,SD_CARD_ERROR_CMD18 - Read multiple blocks
|
||||
0X0D,SD_CARD_ERROR_CMD24 - Write single block
|
||||
0X0E,SD_CARD_ERROR_CMD25 - Write multiple blocks
|
||||
0X0F,SD_CARD_ERROR_CMD32 - Set first erase block
|
||||
0X10,SD_CARD_ERROR_CMD33 - Set last erase block
|
||||
0X11,SD_CARD_ERROR_CMD38 - Erase selected blocks
|
||||
0X12,SD_CARD_ERROR_CMD58 - Read OCR register
|
||||
0X13,SD_CARD_ERROR_CMD59 - Set CRC mode
|
||||
0X14,SD_CARD_ERROR_ACMD6 - Set SDIO bus width
|
||||
0X15,SD_CARD_ERROR_ACMD13 - Read extended status
|
||||
0X16,SD_CARD_ERROR_ACMD23 - Set pre-erased count
|
||||
0X17,SD_CARD_ERROR_ACMD41 - Activate card initialization
|
||||
0X18,SD_CARD_ERROR_ACMD51 - Read SCR data
|
||||
0X19,SD_CARD_ERROR_READ_TOKEN - Bad read data token
|
||||
0X1A,SD_CARD_ERROR_READ_CRC - Read CRC error
|
||||
0X1B,SD_CARD_ERROR_READ_FIFO - SDIO fifo read timeout
|
||||
0X1C,SD_CARD_ERROR_READ_REG - Read CID or CSD failed.
|
||||
0X1D,SD_CARD_ERROR_READ_START - Bad readStart argument
|
||||
0X1E,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
|
||||
0X1F,SD_CARD_ERROR_STOP_TRAN - Multiple block stop failed
|
||||
0X20,SD_CARD_ERROR_TRANSFER_COMPLETE - SDIO transfer complete
|
||||
0X21,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
|
||||
0X22,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
|
||||
0X23,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
|
||||
0X24,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
|
||||
0X25,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
|
||||
0X26,SD_CARD_ERROR_DMA - DMA transfer failed
|
||||
0X27,SD_CARD_ERROR_ERASE - Card did not accept erase commands
|
||||
0X28,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
|
||||
0X29,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
|
||||
0X2A,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
|
||||
0X2B,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
|
||||
0X2C,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
|
||||
0X2D,SD_CARD_ERROR_UNKNOWN - Unknown error
|
||||
10
libraries/SdFat/doc/SdFat.html
Normal file
10
libraries/SdFat/doc/SdFat.html
Normal file
@@ -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>
|
||||
4
libraries/SdFat/doc/ZipMsg/index.html
Normal file
4
libraries/SdFat/doc/ZipMsg/index.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<h3>Replace the content of the html folder by unzipping html.zip.</h3>
|
||||
<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
|
||||
<h3>This makes viewing changes on GitHub difficult.</h3>
|
||||
<p> </p>
|
||||
3
libraries/SdFat/doc/clean_html.bat
Normal file
3
libraries/SdFat/doc/clean_html.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
del html\*.md5
|
||||
del html\*.map
|
||||
pause
|
||||
3
libraries/SdFat/doc/del_html.bat
Normal file
3
libraries/SdFat/doc/del_html.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
rm html/*.*
|
||||
rm html/search/*.*
|
||||
pause
|
||||
BIN
libraries/SdFat/doc/html.zip
Normal file
BIN
libraries/SdFat/doc/html.zip
Normal file
Binary file not shown.
4
libraries/SdFat/doc/html/index.html
Normal file
4
libraries/SdFat/doc/html/index.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<h3>Replace the content of the html folder by unzipping html.zip.</h3>
|
||||
<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
|
||||
<h3>This makes viewing changes on GitHub difficult.</h3>
|
||||
<p> </p>
|
||||
289
libraries/SdFat/doc/mainpage.h
Normal file
289
libraries/SdFat/doc/mainpage.h
Normal file
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* Copyright (c) 2011-2024 Bill Greiman
|
||||
* This file is part of the SdFat library for SD memory cards.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/**
|
||||
\mainpage Arduino %SdFat Library
|
||||
|
||||
\section Warn Warnings for SdFat V2
|
||||
|
||||
This is a major new version of SdFat. It is mostly
|
||||
backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
|
||||
|
||||
You should edit SdFatConfig.h to select features. The default version of
|
||||
SdFatConfig.h is suitable for UNO and other small AVR boards.
|
||||
|
||||
\section Intro Introduction
|
||||
|
||||
The Arduino %SdFat library supports FAT16, FAT32, and exFAT file systems
|
||||
on Standard SD, SDHC, and SDXC cards.
|
||||
|
||||
In %SdFat version 1, SdFat and File are the main classes.
|
||||
|
||||
In %SdFat version 2, SdFat and File are defined by typedefs in terms of the
|
||||
following classes.
|
||||
|
||||
The file system classes in the %SdFat library are SdFat32, SdExFat, and SdFs.
|
||||
SdFat32 supports FAT16 and FAT32. SdExFat supports exFAT, SdFs supports
|
||||
FAT16, FAT32, and exFAT.
|
||||
|
||||
The corresponding file classes are File32, ExFile, and FsFile.
|
||||
|
||||
The types for SdFat and File are defined in SdFatConfig.h. This version
|
||||
uses FAT16/FAT32 for small AVR boards and FAT16/FAT32/exFAT for all other
|
||||
boards.
|
||||
|
||||
\code{.cpp}
|
||||
// File types for SdFat, File, SdFile, SdBaseFile, fstream,
|
||||
// ifstream, and ofstream.
|
||||
//
|
||||
// Set SDFAT_FILE_TYPE to:
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
//
|
||||
#if defined(__AVR__) && FLASHEND < 0X8000
|
||||
// FAT16/FAT32 for 32K AVR boards.
|
||||
#define SDFAT_FILE_TYPE 1
|
||||
#else // defined(__AVR__) && FLASHEND < 0X8000
|
||||
// FAT16/FAT32 and exFAT for all other boards.
|
||||
#define SDFAT_FILE_TYPE 3
|
||||
#endif // defined(__AVR__) && FLASHEND < 0X8000
|
||||
\endcode
|
||||
|
||||
It is possible to use option three, support or FAT16/FAT32 and exFat
|
||||
on an Uno or other AVR board with 32KB flash and 2KB SRAM but memory
|
||||
will be very limited.
|
||||
|
||||
Uno memory use for a simple data logger is:
|
||||
|
||||
> option 1, FAT16/FAT32, 11902 bytes of flash and 855 bytes of SRAM.
|
||||
>
|
||||
> option 2, exFAT, 14942 bytes of flash and 895 bytes of SRAM.
|
||||
>
|
||||
> option 3, FAT16/FAT32 and exFAT, 21834 bytes of flash and 908 bytes of SRAM.
|
||||
|
||||
Please read documentation under the above classes tab for more information.
|
||||
|
||||
A number of example are provided in the %SdFat/examples folder. These were
|
||||
developed to test %SdFat and illustrate its use.
|
||||
|
||||
\section exFAT exFAT Features
|
||||
|
||||
exFAT has many features not available in FAT16/FAT32.
|
||||
|
||||
Files larger than 4GiB, 64-bit file size and file position.
|
||||
|
||||
Free space allocation performance improved by using a free space bitmap.
|
||||
|
||||
Removal of the physical "." and ".." directory entries that appear in
|
||||
FAT16/FAT32 subdirectories.
|
||||
|
||||
Better support for large flash pages with boundary alignment offsets
|
||||
for the FAT table and data region.
|
||||
|
||||
exFAT files have two separate 64-bit length fields. The DataLength
|
||||
field indicate how much space is allocate to the file. The ValidDataLength
|
||||
field indicates how much actual data has been written to the file.
|
||||
|
||||
An exFAT file can be contiguous with pre-allocate clusters and bypass the
|
||||
use of the FAT table. In this case the contiguous flag is set in the
|
||||
directory entry. This allows an entire file to be written as one large
|
||||
multi-block write.
|
||||
|
||||
\section SDPath Paths and Working Directories
|
||||
|
||||
Relative paths in %SdFat are resolved in a manner similar to Windows.
|
||||
|
||||
Each instance of SdFat32, SdExFat, and SdFs has a current directory.
|
||||
This directory is called the volume working directory, vwd.
|
||||
Initially this directory is the root directory for the volume.
|
||||
|
||||
The volume working directory is changed by calling the chdir(path).
|
||||
|
||||
The call sd.chdir("/2014") will change the volume working directory
|
||||
for sd to "/2014", assuming "/2014" exists.
|
||||
|
||||
Relative paths for member functions are resolved by starting at
|
||||
the volume working directory.
|
||||
|
||||
For example, the call sd.mkdir("April") will create the directory
|
||||
"/2014/April" assuming the volume working directory is "/2014".
|
||||
|
||||
There is current working directory, cwd, that is used to resolve paths
|
||||
for file.open() calls.
|
||||
|
||||
For a single SD card, the current working directory is always the volume
|
||||
working directory for that card.
|
||||
|
||||
For multiple SD cards the current working directory is set to the volume
|
||||
working directory of a card by calling the chvol() member function.
|
||||
The chvol() call is like the Windows \<drive letter>: command.
|
||||
|
||||
The call sd2.chvol() will set the current working directory to the volume
|
||||
working directory for sd2.
|
||||
|
||||
If the volume working directory for sd2 is "/music" the call
|
||||
|
||||
file.open("BigBand.wav", O_READ);
|
||||
|
||||
will open "/music/BigBand.wav" on sd2.
|
||||
|
||||
\section Install Installation
|
||||
|
||||
You must manually install %SdFat by renaming the download folder %SdFat
|
||||
and copy the %SdFat folder to the Arduino libraries folder in your
|
||||
sketchbook folder.
|
||||
|
||||
It will be necessary to unzip and rename the folder if you download a zip
|
||||
file from GitHub.
|
||||
|
||||
See the Manual installation section of this guide.
|
||||
|
||||
http://arduino.cc/en/Guide/Libraries
|
||||
|
||||
\section SDconfig SdFat Configuration
|
||||
|
||||
Several configuration options may be changed by editing the SdFatConfig.h
|
||||
file in the %SdFat/src folder.
|
||||
|
||||
Here are a few of the key options.
|
||||
|
||||
If the symbol ENABLE_DEDICATED_SPI is nonzero, multi-block SD I/O may
|
||||
be used for better performance. The SPI bus may not be shared with
|
||||
other devices in this mode.
|
||||
|
||||
The symbol SPI_DRIVER_SELECT is used to select the SPI driver.
|
||||
|
||||
> If the symbol SPI_DRIVER_SELECT is:
|
||||
>
|
||||
> 0 - An optimized custom SPI driver is used if it exists
|
||||
> else the standard library driver is used.
|
||||
>
|
||||
> 1 - The standard library driver is always used.
|
||||
>
|
||||
> 2 - The software SPI driver is always used.
|
||||
>
|
||||
> 3 - An external SPI driver derived from SdSpiBaseClass is always used.
|
||||
|
||||
To enable SD card CRC checking in SPI mode set USE_SD_CRC nonzero.
|
||||
|
||||
See SdFatConfig.h for other options.
|
||||
|
||||
\section Hardware Hardware Configuration
|
||||
|
||||
The hardware interface to the SD card should not use a resistor based level
|
||||
shifter. Resistor based level shifters results in signal rise times that are
|
||||
too slow for many newer SD cards.
|
||||
|
||||
|
||||
\section HowTo How to format SD Cards
|
||||
|
||||
The best way to restore an SD card's format on a PC or Mac is to use
|
||||
SDFormatter which can be downloaded from:
|
||||
|
||||
http://www.sdcard.org/downloads
|
||||
|
||||
A formatter program, SdFormatter.ino, is included in the
|
||||
%SdFat/examples/SdFormatter directory. This program attempts to
|
||||
emulate SD Association's SDFormatter.
|
||||
|
||||
SDFormatter aligns flash erase boundaries with file
|
||||
system structures which reduces write latency and file system overhead.
|
||||
|
||||
The PC/Mac SDFormatter does not have an option for FAT type so it may format
|
||||
very small cards as FAT12. Use the %SdFormatter example to force FAT16
|
||||
formatting of small cards.
|
||||
|
||||
Do not format the SD card with an OS utility, OS utilities do not format SD
|
||||
cards in conformance with the SD standard.
|
||||
|
||||
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. Also files can become
|
||||
fragmented which causes reads and writes to be slower.
|
||||
|
||||
\section ExampleFiles Examples
|
||||
|
||||
A number of examples are provided in the SdFat/examples folder.
|
||||
|
||||
To access these examples from the Arduino development environment
|
||||
go to: %File -> Examples -> %SdFat -> \<program Name\>
|
||||
|
||||
Compile, upload to your Arduino and click on Serial Monitor to run
|
||||
the example.
|
||||
|
||||
Here is a list:
|
||||
|
||||
AvrAdcLogger - Fast AVR ADC logger using Timer/ADC interrupts.
|
||||
|
||||
BackwardCompatibility - Demonstrate SD.h compatibility with %SdFat.h.
|
||||
|
||||
bench - A read/write benchmark.
|
||||
|
||||
%BufferedPrint - Demo a buffered print class for AVR loggers.
|
||||
|
||||
debug folder - Some of my debug programs - will be remove in the future.
|
||||
|
||||
DirectoryFunctions - Use of chdir(), ls(), mkdir(), and rmdir().
|
||||
|
||||
examplesV1 folder - Examples from SdFat V1 for compatibility tests.
|
||||
|
||||
ExFatLogger - A data-logger optimized for exFAT features.
|
||||
|
||||
MinimumSizeSdReader - Example of small file reader for FAT16/FAT32.
|
||||
|
||||
OpenNext - Open all files in the root dir and print their filename.
|
||||
|
||||
QuickStart - Quick hardware test for SPI card access.
|
||||
|
||||
ReadCsvFile - Function to read a CSV text file one field at a time.
|
||||
|
||||
rename - demonstrates use of rename().
|
||||
|
||||
RtcTimestampTest - Demonstration of timestamps with RTClib.
|
||||
|
||||
SdErrorCodes - Produce a list of error codes.
|
||||
|
||||
SdFormatter - This program will format an SD, SDHC, or SDXC card.
|
||||
|
||||
SdInfo - Initialize an SD card and analyze its structure for trouble shooting.
|
||||
|
||||
SoftwareSpi - Demo of limited Software SPI support in SdFat V2.
|
||||
|
||||
STM32Test - Example use of two SPI ports on an STM32 board.
|
||||
|
||||
TeensyDmaAdcLogger - Fast logger using DMA ADC.
|
||||
|
||||
TeensyRtcTimestamp - %File timestamps for Teensy3.
|
||||
|
||||
TeensySdioDemo - Demo of SDIO and SPI modes for the Teensy 3.5/3.6 built-in SD.
|
||||
|
||||
TeensySdioLogger - Fast logger using a ring buffer.
|
||||
|
||||
UnicodeFilenames - Test program for Unicode file names.
|
||||
|
||||
UserChipSelectFunction - Useful for port expanders or replacement of the standard GPIO functions.
|
||||
|
||||
UserSPIDriver - An example of an external SPI driver.
|
||||
*/
|
||||
33
libraries/SdFat/examples/AvrAdcLogger/AvrAdcLogger.h
Normal file
33
libraries/SdFat/examples/AvrAdcLogger/AvrAdcLogger.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef AnalogBinLogger_h
|
||||
#define AnalogBinLogger_h
|
||||
const size_t BLOCK_SIZE = 64;
|
||||
//------------------------------------------------------------------------------
|
||||
// First block of file.
|
||||
const size_t PIN_NUM_DIM =
|
||||
BLOCK_SIZE - 3 * sizeof(uint32_t) - 2 * sizeof(uint8_t);
|
||||
struct metadata_t {
|
||||
uint32_t adcFrequency; // ADC clock frequency
|
||||
uint32_t cpuFrequency; // CPU clock frequency
|
||||
uint32_t sampleInterval; // Sample interval in CPU cycles.
|
||||
uint8_t recordEightBits; // Size of ADC values, nonzero for 8-bits.
|
||||
uint8_t pinCount; // Number of analog pins in a sample.
|
||||
uint8_t pinNumber[PIN_NUM_DIM]; // List of pin numbers in a sample.
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for 8-bit ADC mode.
|
||||
const size_t DATA_DIM8 = (BLOCK_SIZE - 2 * sizeof(uint16_t)) / sizeof(uint8_t);
|
||||
struct block8_t {
|
||||
uint16_t count; // count of data values
|
||||
uint16_t overrun; // count of overruns since last block
|
||||
uint8_t data[DATA_DIM8];
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for 10-bit ADC mode.
|
||||
const size_t DATA_DIM16 =
|
||||
(BLOCK_SIZE - 2 * sizeof(uint16_t)) / sizeof(uint16_t);
|
||||
struct block16_t {
|
||||
unsigned short count; // count of data values
|
||||
unsigned short overrun; // count of overruns since last block
|
||||
unsigned short data[DATA_DIM16];
|
||||
};
|
||||
#endif // AnalogBinLogger_h
|
||||
906
libraries/SdFat/examples/AvrAdcLogger/AvrAdcLogger.ino
Normal file
906
libraries/SdFat/examples/AvrAdcLogger/AvrAdcLogger.ino
Normal file
@@ -0,0 +1,906 @@
|
||||
/**
|
||||
* This program logs data from the Arduino ADC to a binary file.
|
||||
*
|
||||
* Samples are logged at regular intervals. Each Sample consists of the ADC
|
||||
* values for the analog pins defined in the PIN_LIST array. The pins numbers
|
||||
* may be in any order.
|
||||
*
|
||||
* Edit the configuration constants below to set the sample pins, sample rate,
|
||||
* and other configuration values.
|
||||
*
|
||||
* If your SD card has a long write latency, it may be necessary to use
|
||||
* slower sample rates. Using a Mega Arduino helps overcome latency
|
||||
* problems since more 64 byte buffer blocks will be used.
|
||||
*
|
||||
* Each 64 byte data block in the file has a four byte header followed by up
|
||||
* to 60 bytes of data. (60 values in 8-bit mode or 30 values in 10-bit mode)
|
||||
* Each block contains an integral number of samples with unused space at the
|
||||
* end of the block.
|
||||
*
|
||||
*/
|
||||
#ifdef __AVR__
|
||||
#include <SPI.h>
|
||||
|
||||
#include "AvrAdcLogger.h"
|
||||
#include "BufferedPrint.h"
|
||||
#include "FreeStack.h"
|
||||
#include "SdFat.h"
|
||||
|
||||
// Save SRAM if 328.
|
||||
#ifdef __AVR_ATmega328P__
|
||||
#include "MinimumSerial.h"
|
||||
MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
//------------------------------------------------------------------------------
|
||||
// This example was designed for exFAT but will support FAT16/FAT32.
|
||||
//
|
||||
// Note: Uno will not support SD_FAT_TYPE = 3.
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 2
|
||||
//------------------------------------------------------------------------------
|
||||
// Set USE_RTC nonzero for file timestamps.
|
||||
// RAM use will be marginal on Uno with RTClib.
|
||||
// Set USE_RTC nonzero for file timestamps.
|
||||
// RAM use will be marginal on Uno with RTClib.
|
||||
// 0 - RTC not used
|
||||
// 1 - DS1307
|
||||
// 2 - DS3231
|
||||
// 3 - PCF8523
|
||||
#define USE_RTC 0
|
||||
#if USE_RTC
|
||||
#include "RTClib.h"
|
||||
#endif // USE_RTC
|
||||
//------------------------------------------------------------------------------
|
||||
// Pin definitions.
|
||||
//
|
||||
// Digital pin to indicate an error, set to -1 if not used.
|
||||
// The led blinks for fatal errors. The led goes on solid for SD write
|
||||
// overrun errors and logging continues.
|
||||
const int8_t ERROR_LED_PIN = -1;
|
||||
|
||||
// SD chip select pin.
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
//------------------------------------------------------------------------------
|
||||
// Analog pin number list for a sample. Pins may be in any order and pin
|
||||
// numbers may be repeated.
|
||||
const uint8_t PIN_LIST[] = {0, 1, 2, 3, 4};
|
||||
//------------------------------------------------------------------------------
|
||||
// Sample rate in samples per second.
|
||||
const float SAMPLE_RATE = 5000; // Must be 0.25 or greater.
|
||||
|
||||
// The interval between samples in seconds, SAMPLE_INTERVAL, may be set to a
|
||||
// constant instead of being calculated from SAMPLE_RATE. SAMPLE_RATE is not
|
||||
// used in the code below. For example, setting SAMPLE_INTERVAL = 2.0e-4
|
||||
// will result in a 200 microsecond sample interval.
|
||||
const float SAMPLE_INTERVAL = 1.0 / SAMPLE_RATE;
|
||||
|
||||
// Setting ROUND_SAMPLE_INTERVAL non-zero will cause the sample interval to
|
||||
// be rounded to a a multiple of the ADC clock period and will reduce sample
|
||||
// time jitter.
|
||||
#define ROUND_SAMPLE_INTERVAL 1
|
||||
//------------------------------------------------------------------------------
|
||||
// Reference voltage. See the processor data-sheet for reference details.
|
||||
// uint8_t const ADC_REF = 0; // External Reference AREF pin.
|
||||
uint8_t const ADC_REF = (1 << REFS0); // Vcc Reference.
|
||||
// uint8_t const ADC_REF = (1 << REFS1); // Internal 1.1 (only 644 1284P Mega)
|
||||
// uint8_t const ADC_REF = (1 << REFS1) | (1 << REFS0); // Internal 1.1 or 2.56
|
||||
//------------------------------------------------------------------------------
|
||||
// File definitions.
|
||||
//
|
||||
// Maximum file size in bytes.
|
||||
// The program creates a contiguous file with MAX_FILE_SIZE_MiB bytes.
|
||||
// The file will be truncated if logging is stopped early.
|
||||
const uint32_t MAX_FILE_SIZE_MiB = 100; // 100 MiB file.
|
||||
|
||||
// log file name. Integer field before dot will be incremented.
|
||||
#define LOG_FILE_NAME "AvrAdc00.bin"
|
||||
|
||||
// Maximum length name including zero byte.
|
||||
const size_t NAME_DIM = 40;
|
||||
|
||||
// Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC.
|
||||
#define RECORD_EIGHT_BITS 0
|
||||
//------------------------------------------------------------------------------
|
||||
// FIFO size definition. Use a multiple of 512 bytes for best performance.
|
||||
//
|
||||
#if RAMEND < 0X8FF
|
||||
#error SRAM too small
|
||||
#elif RAMEND < 0X10FF
|
||||
const size_t FIFO_SIZE_BYTES = 512;
|
||||
#elif RAMEND < 0X20FF
|
||||
const size_t FIFO_SIZE_BYTES = 4 * 512;
|
||||
#elif RAMEND < 0X40FF
|
||||
const size_t FIFO_SIZE_BYTES = 12 * 512;
|
||||
#else // RAMEND
|
||||
const size_t FIFO_SIZE_BYTES = 16 * 512;
|
||||
#endif // RAMEND
|
||||
//------------------------------------------------------------------------------
|
||||
// ADC clock rate.
|
||||
// The ADC clock rate is normally calculated from the pin count and sample
|
||||
// interval. The calculation attempts to use the lowest possible ADC clock
|
||||
// rate.
|
||||
//
|
||||
// You can select an ADC clock rate by defining the symbol ADC_PRESCALER to
|
||||
// one of these values. You must choose an appropriate ADC clock rate for
|
||||
// your sample interval.
|
||||
// #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno
|
||||
// #define ADC_PRESCALER 6 // F_CPU/64 250 kHz on an Uno
|
||||
// #define ADC_PRESCALER 5 // F_CPU/32 500 kHz on an Uno
|
||||
// #define ADC_PRESCALER 4 // F_CPU/16 1000 kHz on an Uno
|
||||
// #define ADC_PRESCALER 3 // F_CPU/8 2000 kHz on an Uno (8-bit mode only)
|
||||
//==============================================================================
|
||||
// End of configuration constants.
|
||||
//==============================================================================
|
||||
// Temporary log file. Will be deleted if a reset or power failure occurs.
|
||||
#define TMP_FILE_NAME "tmp_adc.bin"
|
||||
|
||||
// Number of analog pins to log.
|
||||
const uint8_t PIN_COUNT = sizeof(PIN_LIST) / sizeof(PIN_LIST[0]);
|
||||
|
||||
// Minimum ADC clock cycles per sample interval
|
||||
const uint16_t MIN_ADC_CYCLES = 15;
|
||||
|
||||
// Extra cpu cycles to setup ADC with more than one pin per sample.
|
||||
const uint16_t ISR_SETUP_ADC = PIN_COUNT > 1 ? 100 : 0;
|
||||
|
||||
// Maximum cycles for timer0 system interrupt.
|
||||
const uint16_t ISR_TIMER0 = 160;
|
||||
//==============================================================================
|
||||
const uint32_t MAX_FILE_SIZE = MAX_FILE_SIZE_MiB << 20;
|
||||
|
||||
// Max SPI rate for AVR is 10 MHz for F_CPU 20 MHz, 8 MHz for F_CPU 16 MHz.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(10)
|
||||
|
||||
// Select fastest interface.
|
||||
#if ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // ENABLE_DEDICATED_SPI
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
typedef File file_t;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
typedef File32 file_t;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
typedef ExFile file_t;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
typedef FsFile file_t;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
file_t binFile;
|
||||
file_t csvFile;
|
||||
|
||||
char binName[] = LOG_FILE_NAME;
|
||||
|
||||
#if RECORD_EIGHT_BITS
|
||||
const size_t BLOCK_MAX_COUNT = PIN_COUNT * (DATA_DIM8 / PIN_COUNT);
|
||||
typedef block8_t block_t;
|
||||
#else // RECORD_EIGHT_BITS
|
||||
const size_t BLOCK_MAX_COUNT = PIN_COUNT * (DATA_DIM16 / PIN_COUNT);
|
||||
typedef block16_t block_t;
|
||||
#endif // RECORD_EIGHT_BITS
|
||||
|
||||
// Size of FIFO in blocks.
|
||||
size_t const FIFO_DIM = FIFO_SIZE_BYTES / sizeof(block_t);
|
||||
block_t* fifoData;
|
||||
volatile size_t fifoCount = 0; // volatile - shared, ISR and background.
|
||||
size_t fifoHead = 0; // Only accessed by ISR during logging.
|
||||
size_t fifoTail = 0; // Only accessed by writer during logging.
|
||||
//==============================================================================
|
||||
// Interrupt Service Routines
|
||||
|
||||
// Disable ADC interrupt if true.
|
||||
volatile bool isrStop = false;
|
||||
|
||||
// Pointer to current buffer.
|
||||
block_t* isrBuf = nullptr;
|
||||
// overrun count
|
||||
uint16_t isrOver = 0;
|
||||
|
||||
// ADC configuration for each pin.
|
||||
uint8_t adcmux[PIN_COUNT];
|
||||
uint8_t adcsra[PIN_COUNT];
|
||||
uint8_t adcsrb[PIN_COUNT];
|
||||
uint8_t adcindex = 1;
|
||||
|
||||
// Insure no timer events are missed.
|
||||
volatile bool timerError = false;
|
||||
volatile bool timerFlag = false;
|
||||
//------------------------------------------------------------------------------
|
||||
// ADC done interrupt.
|
||||
ISR(ADC_vect) {
|
||||
// Read ADC data.
|
||||
#if RECORD_EIGHT_BITS
|
||||
uint8_t d = ADCH;
|
||||
#else // RECORD_EIGHT_BITS
|
||||
// This will access ADCL first.
|
||||
uint16_t d = ADC;
|
||||
#endif // RECORD_EIGHT_BITS
|
||||
|
||||
if (!isrBuf) {
|
||||
if (fifoCount < FIFO_DIM) {
|
||||
isrBuf = fifoData + fifoHead;
|
||||
} else {
|
||||
// no buffers - count overrun
|
||||
if (isrOver < 0XFFFF) {
|
||||
isrOver++;
|
||||
}
|
||||
// Avoid missed timer error.
|
||||
timerFlag = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Start ADC for next pin
|
||||
if (PIN_COUNT > 1) {
|
||||
ADMUX = adcmux[adcindex];
|
||||
ADCSRB = adcsrb[adcindex];
|
||||
ADCSRA = adcsra[adcindex];
|
||||
if (adcindex == 0) {
|
||||
timerFlag = false;
|
||||
}
|
||||
adcindex = adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0;
|
||||
} else {
|
||||
timerFlag = false;
|
||||
}
|
||||
// Store ADC data.
|
||||
isrBuf->data[isrBuf->count++] = d;
|
||||
|
||||
// Check for buffer full.
|
||||
if (isrBuf->count >= BLOCK_MAX_COUNT) {
|
||||
fifoHead = fifoHead < (FIFO_DIM - 1) ? fifoHead + 1 : 0;
|
||||
fifoCount++;
|
||||
// Check for end logging.
|
||||
if (isrStop) {
|
||||
adcStop();
|
||||
return;
|
||||
}
|
||||
// Set buffer needed and clear overruns.
|
||||
isrBuf = nullptr;
|
||||
isrOver = 0;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// timer1 interrupt to clear OCF1B
|
||||
ISR(TIMER1_COMPB_vect) {
|
||||
// Make sure ADC ISR responded to timer event.
|
||||
if (timerFlag) {
|
||||
timerError = true;
|
||||
}
|
||||
timerFlag = true;
|
||||
}
|
||||
//==============================================================================
|
||||
// Error messages stored in flash.
|
||||
#define error(msg) (Serial.println(F(msg)), errorHalt())
|
||||
#define assert(e) ((e) ? (void)0 : error("assert: " #e))
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
void fatalBlink() {
|
||||
while (true) {
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
delay(200);
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
delay(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void errorHalt() {
|
||||
// Print minimal error data.
|
||||
// sd.errorPrint(&Serial);
|
||||
// Print extended error info - uses extra bytes of flash.
|
||||
sd.printSdError(&Serial);
|
||||
// Try to save data.
|
||||
binFile.close();
|
||||
fatalBlink();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printUnusedStack() {
|
||||
Serial.print(F("\nUnused stack: "));
|
||||
Serial.println(UnusedStack());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
#if USE_RTC
|
||||
#if USE_RTC == 1
|
||||
RTC_DS1307 rtc;
|
||||
#elif USE_RTC == 2
|
||||
RTC_DS3231 rtc;
|
||||
#elif USE_RTC == 3
|
||||
RTC_PCF8523 rtc;
|
||||
#else // USE_RTC == type
|
||||
#error USE_RTC type not implemented.
|
||||
#endif // USE_RTC == type
|
||||
// Call back for file timestamps. Only called for file create and sync().
|
||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
||||
DateTime now = rtc.now();
|
||||
|
||||
// Return date using FS_DATE macro to format fields.
|
||||
*date = FS_DATE(now.year(), now.month(), now.day());
|
||||
|
||||
// Return time using FS_TIME macro to format fields.
|
||||
*time = FS_TIME(now.hour(), now.minute(), now.second());
|
||||
|
||||
// Return low time bits in units of 10 ms.
|
||||
*ms10 = now.second() & 1 ? 100 : 0;
|
||||
}
|
||||
#endif // USE_RTC
|
||||
//==============================================================================
|
||||
#if ADPS0 != 0 || ADPS1 != 1 || ADPS2 != 2
|
||||
#error unexpected ADC prescaler bits
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
inline bool adcActive() { return (1 << ADIE) & ADCSRA; }
|
||||
//------------------------------------------------------------------------------
|
||||
// initialize ADC and timer1
|
||||
void adcInit(metadata_t* meta) {
|
||||
uint8_t adps; // prescaler bits for ADCSRA
|
||||
uint32_t ticks =
|
||||
F_CPU * SAMPLE_INTERVAL + 0.5; // Sample interval cpu cycles.
|
||||
|
||||
if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) {
|
||||
error("Invalid ADC reference");
|
||||
}
|
||||
#ifdef ADC_PRESCALER
|
||||
if (ADC_PRESCALER > 7 || ADC_PRESCALER < 2) {
|
||||
error("Invalid ADC prescaler");
|
||||
}
|
||||
adps = ADC_PRESCALER;
|
||||
#else // ADC_PRESCALER
|
||||
// Allow extra cpu cycles to change ADC settings if more than one pin.
|
||||
int32_t adcCycles = (ticks - ISR_TIMER0) / PIN_COUNT - ISR_SETUP_ADC;
|
||||
|
||||
for (adps = 7; adps > 0; adps--) {
|
||||
if (adcCycles >= (MIN_ADC_CYCLES << adps)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // ADC_PRESCALER
|
||||
meta->adcFrequency = F_CPU >> adps;
|
||||
if (meta->adcFrequency > (RECORD_EIGHT_BITS ? 2000000 : 1000000)) {
|
||||
error("Sample Rate Too High");
|
||||
}
|
||||
#if ROUND_SAMPLE_INTERVAL
|
||||
// Round so interval is multiple of ADC clock.
|
||||
ticks += 1 << (adps - 1);
|
||||
ticks >>= adps;
|
||||
ticks <<= adps;
|
||||
#endif // ROUND_SAMPLE_INTERVAL
|
||||
|
||||
if (PIN_COUNT > BLOCK_MAX_COUNT || PIN_COUNT > PIN_NUM_DIM) {
|
||||
error("Too many pins");
|
||||
}
|
||||
meta->pinCount = PIN_COUNT;
|
||||
meta->recordEightBits = RECORD_EIGHT_BITS;
|
||||
|
||||
for (int i = 0; i < PIN_COUNT; i++) {
|
||||
uint8_t pin = PIN_LIST[i];
|
||||
if (pin >= NUM_ANALOG_INPUTS) {
|
||||
error("Invalid Analog pin number");
|
||||
}
|
||||
meta->pinNumber[i] = pin;
|
||||
|
||||
// Set ADC reference and low three bits of analog pin number.
|
||||
adcmux[i] = (pin & 7) | ADC_REF;
|
||||
if (RECORD_EIGHT_BITS) {
|
||||
adcmux[i] |= 1 << ADLAR;
|
||||
}
|
||||
|
||||
// If this is the first pin, trigger on timer/counter 1 compare match B.
|
||||
adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0;
|
||||
#ifdef MUX5
|
||||
if (pin > 7) {
|
||||
adcsrb[i] |= (1 << MUX5);
|
||||
}
|
||||
#endif // MUX5
|
||||
adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps;
|
||||
// First pin triggers on timer 1 compare match B rest are free running.
|
||||
adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC;
|
||||
}
|
||||
|
||||
// Setup timer1
|
||||
TCCR1A = 0;
|
||||
uint8_t tshift;
|
||||
if (ticks < 0X10000) {
|
||||
// no prescale, CTC mode
|
||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
|
||||
tshift = 0;
|
||||
} else if (ticks < 0X10000 * 8) {
|
||||
// prescale 8, CTC mode
|
||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
|
||||
tshift = 3;
|
||||
} else if (ticks < 0X10000 * 64) {
|
||||
// prescale 64, CTC mode
|
||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << CS10);
|
||||
tshift = 6;
|
||||
} else if (ticks < 0X10000 * 256) {
|
||||
// prescale 256, CTC mode
|
||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12);
|
||||
tshift = 8;
|
||||
} else if (ticks < 0X10000 * 1024) {
|
||||
// prescale 1024, CTC mode
|
||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12) | (1 << CS10);
|
||||
tshift = 10;
|
||||
} else {
|
||||
error("Sample Rate Too Slow");
|
||||
}
|
||||
// divide by prescaler
|
||||
ticks >>= tshift;
|
||||
// set TOP for timer reset
|
||||
ICR1 = ticks - 1;
|
||||
// compare for ADC start
|
||||
OCR1B = 0;
|
||||
|
||||
// multiply by prescaler
|
||||
ticks <<= tshift;
|
||||
|
||||
// Sample interval in CPU clock ticks.
|
||||
meta->sampleInterval = ticks;
|
||||
meta->cpuFrequency = F_CPU;
|
||||
float sampleRate = (float)meta->cpuFrequency / meta->sampleInterval;
|
||||
Serial.print(F("Sample pins:"));
|
||||
for (uint8_t i = 0; i < meta->pinCount; i++) {
|
||||
Serial.print(' ');
|
||||
Serial.print(meta->pinNumber[i], DEC);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.print(F("ADC bits: "));
|
||||
Serial.println(meta->recordEightBits ? 8 : 10);
|
||||
Serial.print(F("ADC clock kHz: "));
|
||||
Serial.println(meta->adcFrequency / 1000);
|
||||
Serial.print(F("Sample Rate: "));
|
||||
Serial.println(sampleRate);
|
||||
Serial.print(F("Sample interval usec: "));
|
||||
Serial.println(1000000.0 / sampleRate);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// enable ADC and timer1 interrupts
|
||||
void adcStart() {
|
||||
// initialize ISR
|
||||
adcindex = 1;
|
||||
isrBuf = nullptr;
|
||||
isrOver = 0;
|
||||
isrStop = false;
|
||||
|
||||
// Clear any pending interrupt.
|
||||
ADCSRA |= 1 << ADIF;
|
||||
|
||||
// Setup for first pin.
|
||||
ADMUX = adcmux[0];
|
||||
ADCSRB = adcsrb[0];
|
||||
ADCSRA = adcsra[0];
|
||||
|
||||
// Enable timer1 interrupts.
|
||||
timerError = false;
|
||||
timerFlag = false;
|
||||
TCNT1 = 0;
|
||||
TIFR1 = 1 << OCF1B;
|
||||
TIMSK1 = 1 << OCIE1B;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
inline void adcStop() {
|
||||
TIMSK1 = 0;
|
||||
ADCSRA = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Convert binary file to csv file.
|
||||
void binaryToCsv() {
|
||||
uint8_t lastPct = 0;
|
||||
block_t* pd;
|
||||
metadata_t* pm;
|
||||
uint32_t t0 = millis();
|
||||
// Use fast buffered print class.
|
||||
BufferedPrint<file_t, 64> bp(&csvFile);
|
||||
block_t binBuffer[FIFO_DIM];
|
||||
|
||||
assert(sizeof(block_t) == sizeof(metadata_t));
|
||||
binFile.rewind();
|
||||
uint32_t tPct = millis();
|
||||
bool doMeta = true;
|
||||
while (!Serial.available()) {
|
||||
pd = binBuffer;
|
||||
int nb = binFile.read(binBuffer, sizeof(binBuffer));
|
||||
if (nb < 0) {
|
||||
error("read binFile failed");
|
||||
}
|
||||
size_t nd = nb / sizeof(block_t);
|
||||
if (nd < 1) {
|
||||
break;
|
||||
}
|
||||
if (doMeta) {
|
||||
doMeta = false;
|
||||
pm = (metadata_t*)pd++;
|
||||
if (PIN_COUNT != pm->pinCount) {
|
||||
error("Invalid pinCount");
|
||||
}
|
||||
bp.print(F("Interval,"));
|
||||
float intervalMicros =
|
||||
1.0e6 * pm->sampleInterval / (float)pm->cpuFrequency;
|
||||
bp.print(intervalMicros, 4);
|
||||
bp.println(F(",usec"));
|
||||
for (uint8_t i = 0; i < PIN_COUNT; i++) {
|
||||
if (i) {
|
||||
bp.print(',');
|
||||
}
|
||||
bp.print(F("pin"));
|
||||
bp.print(pm->pinNumber[i]);
|
||||
}
|
||||
bp.println();
|
||||
if (nd-- == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < nd; i++, pd++) {
|
||||
if (pd->overrun) {
|
||||
bp.print(F("OVERRUN,"));
|
||||
bp.println(pd->overrun);
|
||||
}
|
||||
for (size_t j = 0; j < pd->count; j += PIN_COUNT) {
|
||||
for (size_t i = 0; i < PIN_COUNT; i++) {
|
||||
if (!bp.printField(pd->data[i + j],
|
||||
i == (PIN_COUNT - 1) ? '\n' : ',')) {
|
||||
error("printField failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((millis() - tPct) > 1000) {
|
||||
uint8_t pct = binFile.curPosition() / (binFile.fileSize() / 100);
|
||||
if (pct != lastPct) {
|
||||
tPct = millis();
|
||||
lastPct = pct;
|
||||
Serial.print(pct, DEC);
|
||||
Serial.println('%');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bp.sync() || !csvFile.close()) {
|
||||
error("close csvFile failed");
|
||||
}
|
||||
Serial.print(F("Done: "));
|
||||
Serial.print(0.001 * (millis() - t0));
|
||||
Serial.println(F(" Seconds"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void createBinFile() {
|
||||
binFile.close();
|
||||
while (sd.exists(binName)) {
|
||||
char* p = strchr(binName, '.');
|
||||
if (!p) {
|
||||
error("no dot in filename");
|
||||
}
|
||||
while (true) {
|
||||
p--;
|
||||
if (p < binName || *p < '0' || *p > '9') {
|
||||
error("Can't create file name");
|
||||
}
|
||||
if (p[0] != '9') {
|
||||
p[0]++;
|
||||
break;
|
||||
}
|
||||
p[0] = '0';
|
||||
}
|
||||
}
|
||||
Serial.print(F("Opening: "));
|
||||
Serial.println(binName);
|
||||
if (!binFile.open(binName, O_RDWR | O_CREAT)) {
|
||||
error("open binName failed");
|
||||
}
|
||||
Serial.print(F("Allocating: "));
|
||||
Serial.print(MAX_FILE_SIZE_MiB);
|
||||
Serial.println(F(" MiB"));
|
||||
if (!binFile.preAllocate(MAX_FILE_SIZE)) {
|
||||
error("preAllocate failed");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
bool createCsvFile() {
|
||||
char csvName[NAME_DIM];
|
||||
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println(F("No current binary file"));
|
||||
return false;
|
||||
}
|
||||
binFile.getName(csvName, sizeof(csvName));
|
||||
char* dot = strchr(csvName, '.');
|
||||
if (!dot) {
|
||||
error("no dot in binName");
|
||||
}
|
||||
strcpy(dot + 1, "csv");
|
||||
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
error("open csvFile failed");
|
||||
}
|
||||
Serial.print(F("Writing: "));
|
||||
Serial.print(csvName);
|
||||
Serial.println(F(" - type any character to stop"));
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// log data
|
||||
void logData() {
|
||||
uint32_t t0;
|
||||
uint32_t t1;
|
||||
uint32_t overruns = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t maxLatencyUsec = 0;
|
||||
size_t maxFifoUse = 0;
|
||||
block_t fifoBuffer[FIFO_DIM];
|
||||
|
||||
adcInit((metadata_t*)fifoBuffer);
|
||||
// Write metadata.
|
||||
if (sizeof(metadata_t) != binFile.write(fifoBuffer, sizeof(metadata_t))) {
|
||||
error("Write metadata failed");
|
||||
}
|
||||
fifoCount = 0;
|
||||
fifoHead = 0;
|
||||
fifoTail = 0;
|
||||
fifoData = fifoBuffer;
|
||||
// Initialize all blocks to save ISR overhead.
|
||||
memset(fifoBuffer, 0, sizeof(fifoBuffer));
|
||||
|
||||
Serial.println(F("Logging - type any character to stop"));
|
||||
// Wait for Serial Idle.
|
||||
Serial.flush();
|
||||
delay(10);
|
||||
|
||||
t0 = millis();
|
||||
t1 = t0;
|
||||
// Start logging interrupts.
|
||||
adcStart();
|
||||
while (1) {
|
||||
uint32_t m;
|
||||
noInterrupts();
|
||||
size_t tmpFifoCount = fifoCount;
|
||||
interrupts();
|
||||
if (tmpFifoCount) {
|
||||
block_t* pBlock = fifoData + fifoTail;
|
||||
// Write block to SD.
|
||||
m = micros();
|
||||
if (sizeof(block_t) != binFile.write(pBlock, sizeof(block_t))) {
|
||||
error("write data failed");
|
||||
}
|
||||
m = micros() - m;
|
||||
t1 = millis();
|
||||
if (m > maxLatencyUsec) {
|
||||
maxLatencyUsec = m;
|
||||
}
|
||||
if (tmpFifoCount > maxFifoUse) {
|
||||
maxFifoUse = tmpFifoCount;
|
||||
}
|
||||
count += pBlock->count;
|
||||
|
||||
// Add overruns and possibly light LED.
|
||||
if (pBlock->overrun) {
|
||||
overruns += pBlock->overrun;
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
}
|
||||
}
|
||||
// Initialize empty block to save ISR overhead.
|
||||
pBlock->count = 0;
|
||||
pBlock->overrun = 0;
|
||||
fifoTail = fifoTail < (FIFO_DIM - 1) ? fifoTail + 1 : 0;
|
||||
|
||||
noInterrupts();
|
||||
fifoCount--;
|
||||
interrupts();
|
||||
|
||||
if (binFile.curPosition() >= MAX_FILE_SIZE) {
|
||||
// File full so stop ISR calls.
|
||||
adcStop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (timerError) {
|
||||
error("Missed timer event - rate too high");
|
||||
}
|
||||
if (Serial.available()) {
|
||||
// Stop ISR interrupts.
|
||||
isrStop = true;
|
||||
}
|
||||
if (fifoCount == 0 && !adcActive()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
// Truncate file if recording stopped early.
|
||||
if (binFile.curPosition() < MAX_FILE_SIZE) {
|
||||
Serial.println(F("Truncating file"));
|
||||
Serial.flush();
|
||||
if (!binFile.truncate()) {
|
||||
error("Can't truncate file");
|
||||
}
|
||||
}
|
||||
Serial.print(F("Max write latency usec: "));
|
||||
Serial.println(maxLatencyUsec);
|
||||
Serial.print(F("Record time sec: "));
|
||||
Serial.println(0.001 * (t1 - t0), 3);
|
||||
Serial.print(F("Sample count: "));
|
||||
Serial.println(count / PIN_COUNT);
|
||||
Serial.print(F("Overruns: "));
|
||||
Serial.println(overruns);
|
||||
Serial.print(F("FIFO_DIM: "));
|
||||
Serial.println(FIFO_DIM);
|
||||
Serial.print(F("maxFifoUse: "));
|
||||
Serial.println(maxFifoUse + 1); // include ISR use.
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void openBinFile() {
|
||||
char name[NAME_DIM];
|
||||
clearSerialInput();
|
||||
Serial.println(F("Enter file name"));
|
||||
if (!serialReadLine(name, sizeof(name))) {
|
||||
return;
|
||||
}
|
||||
if (!sd.exists(name)) {
|
||||
Serial.println(name);
|
||||
Serial.println(F("File does not exist"));
|
||||
return;
|
||||
}
|
||||
binFile.close();
|
||||
if (!binFile.open(name, O_RDWR)) {
|
||||
Serial.println(name);
|
||||
Serial.println(F("open failed"));
|
||||
return;
|
||||
}
|
||||
Serial.println(F("File opened"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Print data file to Serial
|
||||
void printData() {
|
||||
block_t buf;
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
binFile.rewind();
|
||||
if (binFile.read(&buf, sizeof(buf)) != sizeof(buf)) {
|
||||
error("Read metadata failed");
|
||||
}
|
||||
Serial.println(F("Type any character to stop"));
|
||||
delay(1000);
|
||||
while (!Serial.available() &&
|
||||
binFile.read(&buf, sizeof(buf)) == sizeof(buf)) {
|
||||
if (buf.count == 0) {
|
||||
break;
|
||||
}
|
||||
if (buf.overrun) {
|
||||
Serial.print(F("OVERRUN,"));
|
||||
Serial.println(buf.overrun);
|
||||
}
|
||||
for (size_t i = 0; i < buf.count; i++) {
|
||||
Serial.print(buf.data[i], DEC);
|
||||
if ((i + 1) % PIN_COUNT) {
|
||||
Serial.print(',');
|
||||
} else {
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
bool serialReadLine(char* str, size_t size) {
|
||||
size_t n = 0;
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
while (true) {
|
||||
int c = Serial.read();
|
||||
if (c < ' ') break;
|
||||
str[n++] = c;
|
||||
if (n >= size) {
|
||||
Serial.println(F("input too long"));
|
||||
return false;
|
||||
}
|
||||
uint32_t m = millis();
|
||||
while (!Serial.available() && (millis() - m) < 100) {
|
||||
}
|
||||
if (!Serial.available()) break;
|
||||
}
|
||||
str[n] = 0;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
pinMode(ERROR_LED_PIN, OUTPUT);
|
||||
}
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
Serial.println(F("Type any character to begin."));
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
|
||||
FillStack();
|
||||
|
||||
// Read the first sample pin to init the ADC.
|
||||
analogRead(PIN_LIST[0]);
|
||||
|
||||
#if !ENABLE_DEDICATED_SPI
|
||||
Serial.println(
|
||||
F("\nFor best performance edit SdFatConfig.h\n"
|
||||
"and set ENABLE_DEDICATED_SPI nonzero"));
|
||||
#endif // !ENABLE_DEDICATED_SPI
|
||||
// Initialize SD.
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
error("sd.begin failed");
|
||||
}
|
||||
#if USE_RTC
|
||||
if (!rtc.begin()) {
|
||||
error("rtc.begin failed");
|
||||
}
|
||||
if (!rtc.isrunning()) {
|
||||
// Set RTC to sketch compile date & time.
|
||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
error("RTC is NOT running!");
|
||||
} else {
|
||||
Serial.println(F("RTC is running"));
|
||||
}
|
||||
// Set callback
|
||||
FsDateTime::setCallback(dateTime);
|
||||
#endif // USE_RTC
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {
|
||||
printUnusedStack();
|
||||
// Read any Serial data.
|
||||
clearSerialInput();
|
||||
Serial.println();
|
||||
Serial.println(F("type:"));
|
||||
Serial.println(F("b - open existing bin file"));
|
||||
Serial.println(F("c - convert file to csv"));
|
||||
Serial.println(F("l - list files"));
|
||||
Serial.println(F("p - print data to Serial"));
|
||||
Serial.println(F("r - record ADC data"));
|
||||
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
char c = tolower(Serial.read());
|
||||
Serial.println();
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
}
|
||||
// Read any Serial data.
|
||||
clearSerialInput();
|
||||
|
||||
if (c == 'b') {
|
||||
openBinFile();
|
||||
} else if (c == 'c') {
|
||||
if (createCsvFile()) {
|
||||
binaryToCsv();
|
||||
}
|
||||
} else if (c == 'l') {
|
||||
Serial.println(F("ls:"));
|
||||
sd.ls(&Serial, LS_DATE | LS_SIZE);
|
||||
} else if (c == 'p') {
|
||||
printData();
|
||||
} else if (c == 'r') {
|
||||
createBinFile();
|
||||
logData();
|
||||
} else {
|
||||
Serial.println(F("Invalid entry"));
|
||||
}
|
||||
}
|
||||
#else // __AVR__
|
||||
#error This program is only for AVR.
|
||||
#endif // __AVR__
|
||||
@@ -0,0 +1,84 @@
|
||||
// A simple read/write example for SD.h.
|
||||
// Mostly from the SD.h ReadWrite example.
|
||||
//
|
||||
// Your SD must be formatted FAT16/FAT32.
|
||||
//
|
||||
// SD.h does not support some default SdFat features.
|
||||
// To compare flash size, set USE_FAT_FILE_FLAG_CONTIGUOUS,
|
||||
// ENABLE_DEDICATED_SPI, and USE_LONG_FILE_NAMES to zero also
|
||||
// set SDFAT_FILE_TYPE to one in SdFat/src/SdFatCongfig.h
|
||||
//
|
||||
// Set USE_SD_H nonzero to use SD.h.
|
||||
// Set USE_SD_H zero to use SdFat.h.
|
||||
//
|
||||
#define USE_SD_H 0
|
||||
//
|
||||
#if USE_SD_H
|
||||
#include <SD.h>
|
||||
#else // USE_SD_H
|
||||
#include "SdFat.h"
|
||||
SdFat SD;
|
||||
#endif // USE_SD_H
|
||||
|
||||
// Modify SD_CS_PIN for your board.
|
||||
// For Teensy 3.6 and SdFat.h use BUILTIN_SDCARD.
|
||||
#define SD_CS_PIN SS
|
||||
|
||||
File myFile;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
|
||||
#if USE_SD_H
|
||||
Serial.println(F("Using SD.h. Set USE_SD_H zero to use SdFat.h."));
|
||||
#else // USE_SD_H
|
||||
Serial.println(F("Using SdFat.h. Set USE_SD_H nonzero to use SD.h."));
|
||||
#endif // USE_SD_H
|
||||
Serial.println(F("\nType any character to begin."));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
Serial.print("Initializing SD card...");
|
||||
|
||||
if (!SD.begin(SD_CS_PIN)) {
|
||||
Serial.println("initialization failed!");
|
||||
return;
|
||||
}
|
||||
Serial.println("initialization done.");
|
||||
|
||||
// open the file.
|
||||
myFile = SD.open("test.txt", FILE_WRITE);
|
||||
|
||||
// if the file opened okay, write to it:
|
||||
if (myFile) {
|
||||
Serial.print("Writing to test.txt...");
|
||||
myFile.println("testing 1, 2, 3.");
|
||||
// close the file:
|
||||
myFile.close();
|
||||
Serial.println("done.");
|
||||
} else {
|
||||
// if the file didn't open, print an error:
|
||||
Serial.println("error opening test.txt");
|
||||
}
|
||||
|
||||
// re-open the file for reading:
|
||||
myFile = SD.open("test.txt");
|
||||
if (myFile) {
|
||||
Serial.println("test.txt:");
|
||||
|
||||
// read from the file until there's nothing else in it:
|
||||
while (myFile.available()) {
|
||||
Serial.write(myFile.read());
|
||||
}
|
||||
// close the file:
|
||||
myFile.close();
|
||||
} else {
|
||||
// if the file didn't open, print an error:
|
||||
Serial.println("error opening test.txt");
|
||||
}
|
||||
}
|
||||
void loop() {
|
||||
// nothing happens after setup
|
||||
}
|
||||
236
libraries/SdFat/examples/BufferedPrint/BufferedPrint.ino
Normal file
236
libraries/SdFat/examples/BufferedPrint/BufferedPrint.ino
Normal file
@@ -0,0 +1,236 @@
|
||||
// Test and benchmark of the fast bufferedPrint class.
|
||||
//
|
||||
// Mainly for AVR but may improve print performance with other CPUs.
|
||||
#include "BufferedPrint.h"
|
||||
#include "SdFat.h"
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
typedef File file_t;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
typedef File32 file_t;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
typedef ExFile file_t;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
typedef FsFile file_t;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
// number of lines to print
|
||||
const uint16_t N_PRINT = 20000;
|
||||
//------------------------------------------------------------------------------
|
||||
void benchmark() {
|
||||
file_t file;
|
||||
BufferedPrint<file_t, 64> bp;
|
||||
// do write test
|
||||
Serial.println();
|
||||
for (int test = 0; test < 6; test++) {
|
||||
char fileName[13] = "bench0.txt";
|
||||
fileName[5] = '0' + test;
|
||||
// open or create file - truncate existing file.
|
||||
if (!file.open(fileName, O_RDWR | O_CREAT | O_TRUNC)) {
|
||||
sd.errorHalt(&Serial, F("open failed"));
|
||||
}
|
||||
if (test & 1) {
|
||||
bp.begin(&file);
|
||||
}
|
||||
uint32_t t = millis();
|
||||
switch (test) {
|
||||
case 0:
|
||||
Serial.println(F("Test of println(uint16_t)"));
|
||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
||||
file.println(i);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Serial.println(F("Test of printField(uint16_t, char)"));
|
||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
||||
bp.printField(i, '\n');
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Serial.println(F("Test of println(uint32_t)"));
|
||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
||||
file.println(12345678UL + i);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Serial.println(F("Test of printField(uint32_t, char)"));
|
||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
||||
bp.printField(12345678UL + i, '\n');
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
Serial.println(F("Test of println(double)"));
|
||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
||||
file.println((double)0.01 * i);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
Serial.println(F("Test of printField(double, char)"));
|
||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
||||
bp.printField((double)0.01 * i, '\n');
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (test & 1) {
|
||||
bp.sync();
|
||||
}
|
||||
if (file.getWriteError()) {
|
||||
sd.errorHalt(&Serial, F("write failed"));
|
||||
}
|
||||
double s = file.fileSize();
|
||||
file.close();
|
||||
t = millis() - t;
|
||||
Serial.print(F("Time "));
|
||||
Serial.print(0.001 * t, 3);
|
||||
Serial.println(F(" sec"));
|
||||
Serial.print(F("File size "));
|
||||
Serial.print(0.001 * s);
|
||||
Serial.println(F(" KB"));
|
||||
Serial.print(F("Write "));
|
||||
Serial.print(s / t);
|
||||
Serial.println(F(" KB/sec"));
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testMemberFunctions() {
|
||||
BufferedPrint<Print, 32> bp(&Serial);
|
||||
char c = 'c'; // char
|
||||
//#define BASIC_TYPES
|
||||
#ifdef BASIC_TYPES
|
||||
signed char sc = -1; // signed 8-bit
|
||||
unsigned char uc = 1; // unsiged 8-bit
|
||||
signed short ss = -2; // signed 16-bit
|
||||
unsigned short us = 2; // unsigned 16-bit
|
||||
signed long sl = -4; // signed 32-bit
|
||||
unsigned long ul = 4; // unsigned 32-bit
|
||||
#else // BASIC_TYPES
|
||||
int8_t sc = -1; // signed 8-bit
|
||||
uint8_t uc = 1; // unsiged 8-bit
|
||||
int16_t ss = -2; // signed 16-bit
|
||||
uint16_t us = 2; // unsigned 16-bit
|
||||
int32_t sl = -4; // signed 32-bit
|
||||
uint32_t ul = 4; // unsigned 32-bit
|
||||
#endif // BASIC_TYPES
|
||||
float f = -1.234;
|
||||
double d = -5.678;
|
||||
bp.println();
|
||||
bp.println("Test print()");
|
||||
bp.print(c);
|
||||
bp.println();
|
||||
bp.print("string");
|
||||
bp.println();
|
||||
bp.print(F("flash"));
|
||||
bp.println();
|
||||
bp.print(sc);
|
||||
bp.println();
|
||||
bp.print(uc);
|
||||
bp.println();
|
||||
bp.print(ss);
|
||||
bp.println();
|
||||
bp.print(us);
|
||||
bp.println();
|
||||
bp.print(sl);
|
||||
bp.println();
|
||||
bp.print(ul);
|
||||
bp.println();
|
||||
bp.print(f);
|
||||
bp.println();
|
||||
bp.print(d);
|
||||
bp.println();
|
||||
bp.println();
|
||||
|
||||
bp.println("Test println()");
|
||||
bp.println(c);
|
||||
bp.println("string");
|
||||
bp.println(F("flash"));
|
||||
bp.println(sc);
|
||||
bp.println(uc);
|
||||
bp.println(ss);
|
||||
bp.println(us);
|
||||
bp.println(sl);
|
||||
bp.println(ul);
|
||||
bp.println(f);
|
||||
bp.println(d);
|
||||
bp.println();
|
||||
|
||||
bp.println("Test printField()");
|
||||
bp.printField(c, ',');
|
||||
bp.printField("string", ',');
|
||||
bp.printField(F("flash"), ',');
|
||||
bp.printField(sc, ',');
|
||||
bp.printField(uc, ',');
|
||||
bp.printField(ss, ',');
|
||||
bp.printField(us, ',');
|
||||
bp.printField(sl, ',');
|
||||
bp.printField(ul, ',');
|
||||
bp.printField(f, ',');
|
||||
bp.printField(d, '\n');
|
||||
|
||||
bp.sync();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
Serial.println("Type any character to begin.");
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println(F("Test member funcions:"));
|
||||
testMemberFunctions();
|
||||
Serial.println();
|
||||
Serial.println(
|
||||
F("Benchmark performance for uint16_t, uint32_t, and double:"));
|
||||
benchmark();
|
||||
Serial.println("Done");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Example use of chdir(), ls(), mkdir(), and rmdir().
|
||||
*/
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
File root;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
File32 root;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
ExFile root;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
FsFile root;
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
// Create a Serial output stream.
|
||||
ArduinoOutStream cout(Serial);
|
||||
//------------------------------------------------------------------------------
|
||||
// Store error strings in flash to save RAM.
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
delay(1000);
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize the SD card.
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
if (sd.exists("Folder1") || sd.exists("Folder1/file1.txt") ||
|
||||
sd.exists("Folder1/File2.txt")) {
|
||||
error("Please remove existing Folder1, file1.txt, and File2.txt");
|
||||
}
|
||||
|
||||
int rootFileCount = 0;
|
||||
if (!root.open("/")) {
|
||||
error("open root");
|
||||
}
|
||||
while (file.openNext(&root, O_RDONLY)) {
|
||||
if (!file.isHidden()) {
|
||||
rootFileCount++;
|
||||
}
|
||||
file.close();
|
||||
if (rootFileCount > 10) {
|
||||
error("Too many files in root. Please use an empty SD.");
|
||||
}
|
||||
}
|
||||
if (rootFileCount) {
|
||||
cout << F("\nPlease use an empty SD for best results.\n\n");
|
||||
delay(1000);
|
||||
}
|
||||
// Create a new folder.
|
||||
if (!sd.mkdir("Folder1")) {
|
||||
error("Create Folder1 failed");
|
||||
}
|
||||
cout << F("Created Folder1\n");
|
||||
|
||||
// Create a file in Folder1 using a path.
|
||||
if (!file.open("Folder1/file1.txt", O_WRONLY | O_CREAT)) {
|
||||
error("create Folder1/file1.txt failed");
|
||||
}
|
||||
file.close();
|
||||
cout << F("Created Folder1/file1.txt\n");
|
||||
|
||||
// Change volume working directory to Folder1.
|
||||
if (!sd.chdir("Folder1")) {
|
||||
error("chdir failed for Folder1.\n");
|
||||
}
|
||||
cout << F("chdir to Folder1\n");
|
||||
|
||||
// Create File2.txt in current directory.
|
||||
if (!file.open("File2.txt", O_WRONLY | O_CREAT)) {
|
||||
error("create File2.txt failed");
|
||||
}
|
||||
file.close();
|
||||
cout << F("Created File2.txt in current directory\n");
|
||||
|
||||
cout << F("\nList of files on the SD.\n");
|
||||
sd.ls("/", LS_R);
|
||||
|
||||
// Remove files from current directory.
|
||||
if (!sd.remove("file1.txt") || !sd.remove("File2.txt")) {
|
||||
error("remove failed");
|
||||
}
|
||||
cout << F("\nfile1.txt and File2.txt removed.\n");
|
||||
|
||||
// Change current directory to root.
|
||||
if (!sd.chdir()) {
|
||||
error("chdir to root failed.\n");
|
||||
}
|
||||
|
||||
cout << F("\nList of files on the SD.\n");
|
||||
sd.ls(LS_R);
|
||||
|
||||
// Remove Folder1.
|
||||
if (!sd.rmdir("Folder1")) {
|
||||
error("rmdir for Folder1 failed\n");
|
||||
}
|
||||
cout << F("\nFolder1 removed.\n");
|
||||
cout << F("\nList of files on the SD.\n");
|
||||
sd.ls(LS_R);
|
||||
cout << F("Done!\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Nothing happens in loop.
|
||||
void loop() {}
|
||||
9
libraries/SdFat/examples/ExFatLogger/ExFatLogger.h
Normal file
9
libraries/SdFat/examples/ExFatLogger/ExFatLogger.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Avoid IDE problems by defining struct in septate .h file.
|
||||
// Pad record so size is a power of two for best write performance.
|
||||
#ifndef ExFatLogger_h
|
||||
#define ExFatLogger_h
|
||||
const size_t ADC_COUNT = 4;
|
||||
struct data_t {
|
||||
uint16_t adc[ADC_COUNT];
|
||||
};
|
||||
#endif // ExFatLogger_h
|
||||
597
libraries/SdFat/examples/ExFatLogger/ExFatLogger.ino
Normal file
597
libraries/SdFat/examples/ExFatLogger/ExFatLogger.ino
Normal file
@@ -0,0 +1,597 @@
|
||||
// Example to demonstrate write latency for preallocated exFAT files.
|
||||
// I suggest you write a PC program to convert very large bin files.
|
||||
//
|
||||
// The maximum data rate will depend on the quality of your SD,
|
||||
// the size of the FIFO, and using dedicated SPI.
|
||||
#include "ExFatLogger.h"
|
||||
#include "FreeStack.h"
|
||||
#include "SdFat.h"
|
||||
//------------------------------------------------------------------------------
|
||||
// This example was designed for exFAT but will support FAT16/FAT32.
|
||||
// Note: Uno will not support SD_FAT_TYPE = 3.
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 2
|
||||
//------------------------------------------------------------------------------
|
||||
// Interval between data records in microseconds.
|
||||
// Try 250 with Teensy 3.6, Due, or STM32.
|
||||
// Try 2000 with AVR boards.
|
||||
// Try 4000 with SAMD Zero boards.
|
||||
const uint32_t LOG_INTERVAL_USEC = 2000;
|
||||
|
||||
// Set USE_RTC nonzero for file timestamps.
|
||||
// RAM use will be marginal on Uno with RTClib.
|
||||
// 0 - RTC not used
|
||||
// 1 - DS1307
|
||||
// 2 - DS3231
|
||||
// 3 - PCF8523
|
||||
#define USE_RTC 0
|
||||
#if USE_RTC
|
||||
#include "RTClib.h"
|
||||
#endif // USE_RTC
|
||||
|
||||
// LED to light if overruns occur.
|
||||
#define ERROR_LED_PIN -1
|
||||
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// FIFO SIZE - 512 byte sectors. Modify for your board.
|
||||
#ifdef __AVR_ATmega328P__
|
||||
// Use 512 bytes for 328 boards.
|
||||
#define FIFO_SIZE_SECTORS 1
|
||||
#elif defined(__AVR__)
|
||||
// Use 2 KiB for other AVR boards.
|
||||
#define FIFO_SIZE_SECTORS 4
|
||||
#else // __AVR_ATmega328P__
|
||||
// Use 8 KiB for non-AVR boards.
|
||||
#define FIFO_SIZE_SECTORS 16
|
||||
#endif // __AVR_ATmega328P__
|
||||
|
||||
// Preallocate 1GiB file.
|
||||
const uint32_t PREALLOCATE_SIZE_MiB = 1024UL;
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
// Save SRAM if 328.
|
||||
#ifdef __AVR_ATmega328P__
|
||||
#include "MinimumSerial.h"
|
||||
MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
//==============================================================================
|
||||
// Replace logRecord(), printRecord(), and ExFatLogger.h for your sensors.
|
||||
void logRecord(data_t* data, uint16_t overrun) {
|
||||
if (overrun) {
|
||||
// Add one since this record has no adc data. Could add overrun field.
|
||||
overrun++;
|
||||
data->adc[0] = 0X8000 | overrun;
|
||||
} else {
|
||||
for (size_t i = 0; i < ADC_COUNT; i++) {
|
||||
data->adc[i] = analogRead(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printRecord(Print* pr, data_t* data) {
|
||||
static uint32_t nr = 0;
|
||||
if (!data) {
|
||||
pr->print(F("LOG_INTERVAL_USEC,"));
|
||||
pr->println(LOG_INTERVAL_USEC);
|
||||
pr->print(F("rec#"));
|
||||
for (size_t i = 0; i < ADC_COUNT; i++) {
|
||||
pr->print(F(",adc"));
|
||||
pr->print(i);
|
||||
}
|
||||
pr->println();
|
||||
nr = 0;
|
||||
return;
|
||||
}
|
||||
if (data->adc[0] & 0X8000) {
|
||||
uint16_t n = data->adc[0] & 0X7FFF;
|
||||
nr += n;
|
||||
pr->print(F("-1,"));
|
||||
pr->print(n);
|
||||
pr->println(F(",overuns"));
|
||||
} else {
|
||||
pr->print(nr++);
|
||||
for (size_t i = 0; i < ADC_COUNT; i++) {
|
||||
pr->write(',');
|
||||
pr->print(data->adc[i]);
|
||||
}
|
||||
pr->println();
|
||||
}
|
||||
}
|
||||
//==============================================================================
|
||||
const uint64_t PREALLOCATE_SIZE = (uint64_t)PREALLOCATE_SIZE_MiB << 20;
|
||||
// Max length of file name including zero byte.
|
||||
#define FILE_NAME_DIM 40
|
||||
// Max number of records to buffer while SD is busy.
|
||||
const size_t FIFO_DIM = 512 * FIFO_SIZE_SECTORS / sizeof(data_t);
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
typedef SdFat sd_t;
|
||||
typedef File file_t;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
typedef SdFat32 sd_t;
|
||||
typedef File32 file_t;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
typedef SdExFat sd_t;
|
||||
typedef ExFile file_t;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
typedef SdFs sd_t;
|
||||
typedef FsFile file_t;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
sd_t sd;
|
||||
|
||||
file_t binFile;
|
||||
file_t csvFile;
|
||||
// You may modify the filename. Digits before the dot are file versions.
|
||||
char binName[] = "ExFatLogger00.bin";
|
||||
//------------------------------------------------------------------------------
|
||||
#if USE_RTC
|
||||
#if USE_RTC == 1
|
||||
RTC_DS1307 rtc;
|
||||
#elif USE_RTC == 2
|
||||
RTC_DS3231 rtc;
|
||||
#elif USE_RTC == 3
|
||||
RTC_PCF8523 rtc;
|
||||
#else // USE_RTC == type
|
||||
#error USE_RTC type not implemented.
|
||||
#endif // USE_RTC == type
|
||||
// Call back for file timestamps. Only called for file create and sync().
|
||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
||||
DateTime now = rtc.now();
|
||||
|
||||
// Return date using FS_DATE macro to format fields.
|
||||
*date = FS_DATE(now.year(), now.month(), now.day());
|
||||
|
||||
// Return time using FS_TIME macro to format fields.
|
||||
*time = FS_TIME(now.hour(), now.minute(), now.second());
|
||||
|
||||
// Return low time bits in units of 10 ms.
|
||||
*ms10 = now.second() & 1 ? 100 : 0;
|
||||
}
|
||||
#endif // USE_RTC
|
||||
//------------------------------------------------------------------------------
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
#define dbgAssert(e) ((e) ? (void)0 : error("assert " #e))
|
||||
//-----------------------------------------------------------------------------
|
||||
// Convert binary file to csv file.
|
||||
void binaryToCsv() {
|
||||
uint8_t lastPct = 0;
|
||||
uint32_t t0 = millis();
|
||||
data_t binData[FIFO_DIM];
|
||||
|
||||
if (!binFile.seekSet(512)) {
|
||||
error("binFile.seek failed");
|
||||
}
|
||||
uint32_t tPct = millis();
|
||||
printRecord(&csvFile, nullptr);
|
||||
while (!Serial.available() && binFile.available()) {
|
||||
int nb = binFile.read(binData, sizeof(binData));
|
||||
if (nb <= 0) {
|
||||
error("read binFile failed");
|
||||
}
|
||||
size_t nr = nb / sizeof(data_t);
|
||||
for (size_t i = 0; i < nr; i++) {
|
||||
printRecord(&csvFile, &binData[i]);
|
||||
}
|
||||
|
||||
if ((millis() - tPct) > 1000) {
|
||||
uint8_t pct = binFile.curPosition() / (binFile.fileSize() / 100);
|
||||
if (pct != lastPct) {
|
||||
tPct = millis();
|
||||
lastPct = pct;
|
||||
Serial.print(pct, DEC);
|
||||
Serial.println('%');
|
||||
csvFile.sync();
|
||||
}
|
||||
}
|
||||
if (Serial.available()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
csvFile.close();
|
||||
Serial.print(F("Done: "));
|
||||
Serial.print(0.001 * (millis() - t0));
|
||||
Serial.println(F(" Seconds"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
void createBinFile() {
|
||||
binFile.close();
|
||||
while (sd.exists(binName)) {
|
||||
char* p = strchr(binName, '.');
|
||||
if (!p) {
|
||||
error("no dot in filename");
|
||||
}
|
||||
while (true) {
|
||||
p--;
|
||||
if (p < binName || *p < '0' || *p > '9') {
|
||||
error("Can't create file name");
|
||||
}
|
||||
if (p[0] != '9') {
|
||||
p[0]++;
|
||||
break;
|
||||
}
|
||||
p[0] = '0';
|
||||
}
|
||||
}
|
||||
if (!binFile.open(binName, O_RDWR | O_CREAT)) {
|
||||
error("open binName failed");
|
||||
}
|
||||
Serial.println(binName);
|
||||
if (!binFile.preAllocate(PREALLOCATE_SIZE)) {
|
||||
error("preAllocate failed");
|
||||
}
|
||||
|
||||
Serial.print(F("preAllocated: "));
|
||||
Serial.print(PREALLOCATE_SIZE_MiB);
|
||||
Serial.println(F(" MiB"));
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
bool createCsvFile() {
|
||||
char csvName[FILE_NAME_DIM];
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println(F("No current binary file"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new csvFile.
|
||||
binFile.getName(csvName, sizeof(csvName));
|
||||
char* dot = strchr(csvName, '.');
|
||||
if (!dot) {
|
||||
error("no dot in filename");
|
||||
}
|
||||
strcpy(dot + 1, "csv");
|
||||
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
error("open csvFile failed");
|
||||
}
|
||||
clearSerialInput();
|
||||
Serial.print(F("Writing: "));
|
||||
Serial.print(csvName);
|
||||
Serial.println(F(" - type any character to stop"));
|
||||
return true;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
void logData() {
|
||||
int32_t delta; // Jitter in log time.
|
||||
int32_t maxDelta = 0;
|
||||
uint32_t maxLogMicros = 0;
|
||||
uint32_t maxWriteMicros = 0;
|
||||
size_t maxFifoUse = 0;
|
||||
size_t fifoCount = 0;
|
||||
size_t fifoHead = 0;
|
||||
size_t fifoTail = 0;
|
||||
uint16_t overrun = 0;
|
||||
uint16_t maxOverrun = 0;
|
||||
uint32_t totalOverrun = 0;
|
||||
uint32_t fifoBuf[128 * FIFO_SIZE_SECTORS];
|
||||
data_t* fifoData = (data_t*)fifoBuf;
|
||||
|
||||
// Write dummy sector to start multi-block write.
|
||||
dbgAssert(sizeof(fifoBuf) >= 512);
|
||||
memset(fifoBuf, 0, sizeof(fifoBuf));
|
||||
if (binFile.write(fifoBuf, 512) != 512) {
|
||||
error("write first sector failed");
|
||||
}
|
||||
clearSerialInput();
|
||||
Serial.println(F("Type any character to stop"));
|
||||
|
||||
// Wait until SD is not busy.
|
||||
while (sd.card()->isBusy()) {
|
||||
}
|
||||
|
||||
// Start time for log file.
|
||||
uint32_t m = millis();
|
||||
|
||||
// Time to log next record.
|
||||
uint32_t logTime = micros();
|
||||
while (true) {
|
||||
// Time for next data record.
|
||||
logTime += LOG_INTERVAL_USEC;
|
||||
|
||||
// Wait until time to log data.
|
||||
delta = micros() - logTime;
|
||||
if (delta > 0) {
|
||||
Serial.print(F("delta: "));
|
||||
Serial.println(delta);
|
||||
error("Rate too fast");
|
||||
}
|
||||
while (delta < 0) {
|
||||
delta = micros() - logTime;
|
||||
}
|
||||
|
||||
if (fifoCount < FIFO_DIM) {
|
||||
uint32_t m = micros();
|
||||
logRecord(fifoData + fifoHead, overrun);
|
||||
m = micros() - m;
|
||||
if (m > maxLogMicros) {
|
||||
maxLogMicros = m;
|
||||
}
|
||||
fifoHead = fifoHead < (FIFO_DIM - 1) ? fifoHead + 1 : 0;
|
||||
fifoCount++;
|
||||
if (overrun) {
|
||||
if (overrun > maxOverrun) {
|
||||
maxOverrun = overrun;
|
||||
}
|
||||
overrun = 0;
|
||||
}
|
||||
} else {
|
||||
totalOverrun++;
|
||||
overrun++;
|
||||
if (overrun > 0XFFF) {
|
||||
error("too many overruns");
|
||||
}
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
}
|
||||
}
|
||||
// Save max jitter.
|
||||
if (delta > maxDelta) {
|
||||
maxDelta = delta;
|
||||
}
|
||||
// Write data if SD is not busy.
|
||||
if (!sd.card()->isBusy()) {
|
||||
size_t nw = fifoHead > fifoTail ? fifoCount : FIFO_DIM - fifoTail;
|
||||
// Limit write time by not writing more than 512 bytes.
|
||||
const size_t MAX_WRITE = 512 / sizeof(data_t);
|
||||
if (nw > MAX_WRITE) nw = MAX_WRITE;
|
||||
size_t nb = nw * sizeof(data_t);
|
||||
uint32_t usec = micros();
|
||||
if (nb != binFile.write(fifoData + fifoTail, nb)) {
|
||||
error("write binFile failed");
|
||||
}
|
||||
usec = micros() - usec;
|
||||
if (usec > maxWriteMicros) {
|
||||
maxWriteMicros = usec;
|
||||
}
|
||||
fifoTail = (fifoTail + nw) < FIFO_DIM ? fifoTail + nw : 0;
|
||||
if (fifoCount > maxFifoUse) {
|
||||
maxFifoUse = fifoCount;
|
||||
}
|
||||
fifoCount -= nw;
|
||||
if (Serial.available()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Serial.print(F("\nLog time: "));
|
||||
Serial.print(0.001 * (millis() - m));
|
||||
Serial.println(F(" Seconds"));
|
||||
binFile.truncate();
|
||||
binFile.sync();
|
||||
Serial.print(("File size: "));
|
||||
// Warning cast used for print since fileSize is uint64_t.
|
||||
Serial.print((uint32_t)binFile.fileSize());
|
||||
Serial.println(F(" bytes"));
|
||||
Serial.print(F("totalOverrun: "));
|
||||
Serial.println(totalOverrun);
|
||||
Serial.print(F("FIFO_DIM: "));
|
||||
Serial.println(FIFO_DIM);
|
||||
Serial.print(F("maxFifoUse: "));
|
||||
Serial.println(maxFifoUse);
|
||||
Serial.print(F("maxLogMicros: "));
|
||||
Serial.println(maxLogMicros);
|
||||
Serial.print(F("maxWriteMicros: "));
|
||||
Serial.println(maxWriteMicros);
|
||||
Serial.print(F("Log interval: "));
|
||||
Serial.print(LOG_INTERVAL_USEC);
|
||||
Serial.print(F(" micros\nmaxDelta: "));
|
||||
Serial.print(maxDelta);
|
||||
Serial.println(F(" micros"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void openBinFile() {
|
||||
char name[FILE_NAME_DIM];
|
||||
clearSerialInput();
|
||||
Serial.println(F("Enter file name"));
|
||||
if (!serialReadLine(name, sizeof(name))) {
|
||||
return;
|
||||
}
|
||||
if (!sd.exists(name)) {
|
||||
Serial.println(name);
|
||||
Serial.println(F("File does not exist"));
|
||||
return;
|
||||
}
|
||||
binFile.close();
|
||||
if (!binFile.open(name, O_RDONLY)) {
|
||||
Serial.println(name);
|
||||
Serial.println(F("open failed"));
|
||||
return;
|
||||
}
|
||||
Serial.println(F("File opened"));
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void printData() {
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
// Skip first dummy sector.
|
||||
if (!binFile.seekSet(512)) {
|
||||
error("seek failed");
|
||||
}
|
||||
clearSerialInput();
|
||||
Serial.println(F("type any character to stop\n"));
|
||||
delay(1000);
|
||||
printRecord(&Serial, nullptr);
|
||||
while (binFile.available() && !Serial.available()) {
|
||||
data_t record;
|
||||
if (binFile.read(&record, sizeof(data_t)) != sizeof(data_t)) {
|
||||
error("read binFile failed");
|
||||
}
|
||||
printRecord(&Serial, &record);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printUnusedStack() {
|
||||
#if HAS_UNUSED_STACK
|
||||
Serial.print(F("\nUnused stack: "));
|
||||
Serial.println(UnusedStack());
|
||||
#endif // HAS_UNUSED_STACK
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
bool serialReadLine(char* str, size_t size) {
|
||||
size_t n = 0;
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
while (true) {
|
||||
int c = Serial.read();
|
||||
if (c < ' ') break;
|
||||
str[n++] = c;
|
||||
if (n >= size) {
|
||||
Serial.println(F("input too long"));
|
||||
return false;
|
||||
}
|
||||
uint32_t m = millis();
|
||||
while (!Serial.available() && (millis() - m) < 100) {
|
||||
}
|
||||
if (!Serial.available()) break;
|
||||
}
|
||||
str[n] = 0;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testSensor() {
|
||||
const uint32_t interval = 200000;
|
||||
int32_t diff;
|
||||
data_t data;
|
||||
clearSerialInput();
|
||||
Serial.println(F("\nTesting - type any character to stop\n"));
|
||||
delay(1000);
|
||||
printRecord(&Serial, nullptr);
|
||||
uint32_t m = micros();
|
||||
while (!Serial.available()) {
|
||||
m += interval;
|
||||
do {
|
||||
diff = m - micros();
|
||||
} while (diff > 0);
|
||||
logRecord(&data, 0);
|
||||
printRecord(&Serial, &data);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
pinMode(ERROR_LED_PIN, OUTPUT);
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
}
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
delay(1000);
|
||||
Serial.println(F("Type any character to begin"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
FillStack();
|
||||
#if !ENABLE_DEDICATED_SPI
|
||||
Serial.println(
|
||||
F("\nFor best performance edit SdFatConfig.h\n"
|
||||
"and set ENABLE_DEDICATED_SPI nonzero"));
|
||||
#endif // !ENABLE_DEDICATED_SPI
|
||||
|
||||
Serial.print(FIFO_DIM);
|
||||
Serial.println(F(" FIFO entries will be used."));
|
||||
|
||||
// Initialize SD.
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
#if USE_RTC
|
||||
if (!rtc.begin()) {
|
||||
error("rtc.begin failed");
|
||||
}
|
||||
if (!rtc.isrunning()) {
|
||||
// Set RTC to sketch compile date & time.
|
||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
error("RTC is NOT running!");
|
||||
}
|
||||
// Set callback
|
||||
FsDateTime::setCallback(dateTime);
|
||||
#endif // USE_RTC
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
printUnusedStack();
|
||||
// Read any Serial data.
|
||||
clearSerialInput();
|
||||
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println(F("type: "));
|
||||
Serial.println(F("b - open existing bin file"));
|
||||
Serial.println(F("c - convert file to csv"));
|
||||
Serial.println(F("l - list files"));
|
||||
Serial.println(F("p - print data to Serial"));
|
||||
Serial.println(F("r - record data"));
|
||||
Serial.println(F("t - test without logging"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
char c = tolower(Serial.read());
|
||||
Serial.println();
|
||||
|
||||
if (c == 'b') {
|
||||
openBinFile();
|
||||
} else if (c == 'c') {
|
||||
if (createCsvFile()) {
|
||||
binaryToCsv();
|
||||
}
|
||||
} else if (c == 'l') {
|
||||
Serial.println(F("ls:"));
|
||||
sd.ls(&Serial, LS_DATE | LS_SIZE);
|
||||
} else if (c == 'p') {
|
||||
printData();
|
||||
} else if (c == 'r') {
|
||||
createBinFile();
|
||||
logData();
|
||||
} else if (c == 't') {
|
||||
testSensor();
|
||||
} else {
|
||||
Serial.println(F("Invalid entry"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Create a text file on the SD with this path using short 8.3 names.
|
||||
#define SFN_PATH "/DIR/TEST.TXT"
|
||||
|
||||
// Modify CS_PIN for your chip select pin.
|
||||
#define CS_PIN SS
|
||||
|
||||
// Set USE_SD_H to one for SD.h or zero for SdFat.
|
||||
#define USE_SD_H 0
|
||||
|
||||
#if USE_SD_H
|
||||
#include "SD.h"
|
||||
File file;
|
||||
#else
|
||||
#include "SdFat.h"
|
||||
// Setting ENABLE_DEDICATED_SPI to zero saves over 200 more bytes.
|
||||
#if ENABLE_DEDICATED_SPI
|
||||
#warning \
|
||||
"Set ENABLE_DEDICATED_SPI zero in SdFat/src/SdFatConfig.h for minimum size"
|
||||
#endif // ENABLE_DEDICATED_SPI
|
||||
// Insure FAT16/FAT32 only.
|
||||
SdFat32 SD;
|
||||
// FatFile does not support Stream functions, just simple read/write.
|
||||
FatFile file;
|
||||
#endif
|
||||
|
||||
void error(const char* msg) {
|
||||
Serial.println(msg);
|
||||
while (true) {
|
||||
}
|
||||
}
|
||||
void setup() {
|
||||
int n;
|
||||
char buf[4];
|
||||
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
Serial.println("Type any character to begin");
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
|
||||
if (!SD.begin(CS_PIN)) error("SD.begin");
|
||||
|
||||
#if USE_SD_H
|
||||
file = SD.open(SFN_PATH);
|
||||
if (!file) error("open");
|
||||
#else
|
||||
// Open existing file with a path of 8.3 names.
|
||||
// Directories will be opened O_RDONLY files O_RDWR.
|
||||
if (!file.openExistingSFN(SFN_PATH)) error("open");
|
||||
#endif
|
||||
while ((n = file.read(buf, sizeof(buf)))) {
|
||||
Serial.write(buf, n);
|
||||
}
|
||||
// close() is only needed if you write to the file. For example, read
|
||||
// config data, modify the data, rewind the file and write the data.
|
||||
// file.close();
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
105
libraries/SdFat/examples/OpenNext/OpenNext.ino
Normal file
105
libraries/SdFat/examples/OpenNext/OpenNext.ino
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Print size, modify date/time, and name for all files in root.
|
||||
*/
|
||||
#include "SdFat.h"
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File dir;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 dir;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile dir;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile dir;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
//------------------------------------------------------------------------------
|
||||
// Store error strings in flash to save RAM.
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize the SD.
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
// Open root directory
|
||||
if (!dir.open("/")) {
|
||||
error("dir.open failed");
|
||||
}
|
||||
// Open next file in root.
|
||||
// Warning, openNext starts at the current position of dir so a
|
||||
// rewind may be necessary in your application.
|
||||
while (file.openNext(&dir, O_RDONLY)) {
|
||||
file.printFileSize(&Serial);
|
||||
Serial.write(' ');
|
||||
file.printModifyDateTime(&Serial);
|
||||
Serial.write(' ');
|
||||
file.printName(&Serial);
|
||||
if (file.isDir()) {
|
||||
// Indicate a directory.
|
||||
Serial.write('/');
|
||||
}
|
||||
Serial.println();
|
||||
file.close();
|
||||
}
|
||||
if (dir.getError()) {
|
||||
Serial.println("openNext failed");
|
||||
} else {
|
||||
Serial.println("Done!");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
192
libraries/SdFat/examples/QuickStart/QuickStart.ino
Normal file
192
libraries/SdFat/examples/QuickStart/QuickStart.ino
Normal file
@@ -0,0 +1,192 @@
|
||||
// Quick hardware test for SPI card access.
|
||||
//
|
||||
#include <SPI.h>
|
||||
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
//
|
||||
// Set DISABLE_CHIP_SELECT to disable a second SPI device.
|
||||
// For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
|
||||
// to 10 to disable the Ethernet controller.
|
||||
const int8_t DISABLE_CHIP_SELECT = -1;
|
||||
//
|
||||
// Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select
|
||||
// the highest speed supported by the board that is not over 4 MHz.
|
||||
// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance.
|
||||
#define SPI_SPEED SD_SCK_MHZ(4)
|
||||
//------------------------------------------------------------------------------
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
// Serial streams
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
// input buffer for line
|
||||
char cinBuf[40];
|
||||
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
|
||||
|
||||
// SD card chip select
|
||||
int chipSelect;
|
||||
|
||||
void cardOrSpeed() {
|
||||
cout << F("Try another SD card or reduce the SPI bus speed.\n");
|
||||
cout << F("Edit SPI_SPEED in this program to change it.\n");
|
||||
}
|
||||
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
|
||||
void reformatMsg() {
|
||||
cout << F("Try reformatting the card. For best results use\n");
|
||||
cout << F("the SdFormatter program in SdFat/examples or download\n");
|
||||
cout << F("and use SDFormatter from www.sdcard.org/downloads.\n");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
cout << F("\nSPI pins:\n");
|
||||
cout << F("MISO: ") << int(MISO) << endl;
|
||||
cout << F("MOSI: ") << int(MOSI) << endl;
|
||||
cout << F("SCK: ") << int(SCK) << endl;
|
||||
cout << F("SS: ") << int(SS) << endl;
|
||||
#ifdef SDCARD_SS_PIN
|
||||
cout << F("SDCARD_SS_PIN: ") << int(SDCARD_SS_PIN) << endl;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
if (DISABLE_CHIP_SELECT < 0) {
|
||||
cout << F(
|
||||
"\nBe sure to edit DISABLE_CHIP_SELECT if you have\n"
|
||||
"a second SPI device. For example, with the Ethernet\n"
|
||||
"shield, DISABLE_CHIP_SELECT should be set to 10\n"
|
||||
"to disable the Ethernet controller.\n");
|
||||
}
|
||||
cout << F(
|
||||
"\nSD chip select is the key hardware option.\n"
|
||||
"Common values are:\n"
|
||||
"Arduino Ethernet shield, pin 4\n"
|
||||
"Sparkfun SD shield, pin 8\n"
|
||||
"Adafruit SD shields and modules, pin 10\n");
|
||||
}
|
||||
|
||||
bool firstTry = true;
|
||||
void loop() {
|
||||
// Read any existing Serial data.
|
||||
clearSerialInput();
|
||||
|
||||
if (!firstTry) {
|
||||
cout << F("\nRestarting\n");
|
||||
}
|
||||
firstTry = false;
|
||||
|
||||
cout << F("\nEnter the chip select pin number: ");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
cin.readline();
|
||||
if (cin >> chipSelect) {
|
||||
cout << chipSelect << endl;
|
||||
} else {
|
||||
cout << F("\nInvalid pin number\n");
|
||||
return;
|
||||
}
|
||||
if (DISABLE_CHIP_SELECT < 0) {
|
||||
cout << F(
|
||||
"\nAssuming the SD is the only SPI device.\n"
|
||||
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
|
||||
} else {
|
||||
cout << F("\nDisabling SPI device on pin ");
|
||||
cout << int(DISABLE_CHIP_SELECT) << endl;
|
||||
pinMode(DISABLE_CHIP_SELECT, OUTPUT);
|
||||
digitalWrite(DISABLE_CHIP_SELECT, HIGH);
|
||||
}
|
||||
if (!sd.begin(chipSelect, SPI_SPEED)) {
|
||||
if (sd.card()->errorCode()) {
|
||||
cout << F(
|
||||
"\nSD initialization failed.\n"
|
||||
"Do not reformat the card!\n"
|
||||
"Is the card correctly inserted?\n"
|
||||
"Is chipSelect set to the correct value?\n"
|
||||
"Does another SPI device need to be disabled?\n"
|
||||
"Is there a wiring/soldering problem?\n");
|
||||
cout << F("\nerrorCode: ") << hex << showbase;
|
||||
cout << int(sd.card()->errorCode());
|
||||
cout << F(", errorData: ") << int(sd.card()->errorData());
|
||||
cout << dec << noshowbase << endl;
|
||||
return;
|
||||
}
|
||||
cout << F("\nCard successfully initialized.\n");
|
||||
if (sd.vol()->fatType() == 0) {
|
||||
cout << F("Can't find a valid FAT16/FAT32/exFAT partition.\n");
|
||||
reformatMsg();
|
||||
return;
|
||||
}
|
||||
cout << F("Can't determine error type\n");
|
||||
return;
|
||||
}
|
||||
cout << F("\nCard successfully initialized.\n");
|
||||
cout << endl;
|
||||
|
||||
uint32_t size = sd.card()->sectorCount();
|
||||
if (size == 0) {
|
||||
cout << F("Can't determine the card size.\n");
|
||||
cardOrSpeed();
|
||||
return;
|
||||
}
|
||||
uint32_t sizeMB = 0.000512 * size + 0.5;
|
||||
cout << F("Card size: ") << sizeMB;
|
||||
cout << F(" MB (MB = 1,000,000 bytes)\n");
|
||||
cout << endl;
|
||||
if (sd.fatType() <= 32) {
|
||||
cout << F("\nVolume is FAT") << int(sd.fatType());
|
||||
} else {
|
||||
cout << F("\nVolume is exFAT");
|
||||
}
|
||||
cout << F(", Cluster size (bytes): ") << sd.vol()->bytesPerCluster();
|
||||
cout << endl << endl;
|
||||
|
||||
cout << F("Files found (date time size name):\n");
|
||||
sd.ls(LS_R | LS_DATE | LS_SIZE);
|
||||
|
||||
if ((sizeMB > 1100 && sd.vol()->sectorsPerCluster() < 64) ||
|
||||
(sizeMB < 2200 && sd.vol()->fatType() == 32)) {
|
||||
cout << F("\nThis card should be reformatted for best performance.\n");
|
||||
cout << F("Use a cluster size of 32 KB for cards larger than 1 GB.\n");
|
||||
cout << F("Only cards larger than 2 GB should be formatted FAT32.\n");
|
||||
reformatMsg();
|
||||
return;
|
||||
}
|
||||
// Read any extra Serial data.
|
||||
clearSerialInput();
|
||||
|
||||
cout << F("\nSuccess! Type any character to restart.\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
155
libraries/SdFat/examples/ReadCsvFile/ReadCsvFile.ino
Normal file
155
libraries/SdFat/examples/ReadCsvFile/ReadCsvFile.ino
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "SdFat.h"
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
char line[40];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Store error strings in flash to save RAM.
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
// Check for extra characters in field or find minus sign.
|
||||
char* skipSpace(char* str) {
|
||||
while (isspace(*str)) str++;
|
||||
return str;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
bool parseLine(char* str) {
|
||||
char* ptr;
|
||||
|
||||
// Set strtok start of line.
|
||||
str = strtok(str, ",");
|
||||
if (!str) return false;
|
||||
|
||||
// Print text field.
|
||||
Serial.println(str);
|
||||
|
||||
// Subsequent calls to strtok expects a null pointer.
|
||||
str = strtok(nullptr, ",");
|
||||
if (!str) return false;
|
||||
|
||||
// Convert string to long integer.
|
||||
int32_t i32 = strtol(str, &ptr, 0);
|
||||
if (str == ptr || *skipSpace(ptr)) return false;
|
||||
Serial.println(i32);
|
||||
|
||||
str = strtok(nullptr, ",");
|
||||
if (!str) return false;
|
||||
|
||||
// strtoul accepts a leading minus with unexpected results.
|
||||
if (*skipSpace(str) == '-') return false;
|
||||
|
||||
// Convert string to unsigned long integer.
|
||||
uint32_t u32 = strtoul(str, &ptr, 0);
|
||||
if (str == ptr || *skipSpace(ptr)) return false;
|
||||
Serial.println(u32);
|
||||
|
||||
str = strtok(nullptr, ",");
|
||||
if (!str) return false;
|
||||
|
||||
// Convert string to double.
|
||||
double d = strtod(str, &ptr);
|
||||
if (str == ptr || *skipSpace(ptr)) return false;
|
||||
Serial.println(d);
|
||||
|
||||
// Check for extra fields.
|
||||
return strtok(nullptr, ",") == nullptr;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize the SD.
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
return;
|
||||
}
|
||||
// Remove any existing file.
|
||||
if (sd.exists("ReadCsvDemo.csv")) {
|
||||
sd.remove("ReadCsvDemo.csv");
|
||||
}
|
||||
// Create the file.
|
||||
if (!file.open("ReadCsvDemo.csv", FILE_WRITE)) {
|
||||
error("open failed");
|
||||
}
|
||||
// Write test data.
|
||||
file.print(
|
||||
F("abc,123,456,7.89\r\n"
|
||||
"def,-321,654,-9.87\r\n"
|
||||
"ghi,333,0xff,5.55"));
|
||||
|
||||
// Rewind file for read.
|
||||
file.rewind();
|
||||
|
||||
while (file.available()) {
|
||||
int n = file.fgets(line, sizeof(line));
|
||||
if (n <= 0) {
|
||||
error("fgets failed");
|
||||
}
|
||||
if (line[n - 1] != '\n' && n == (sizeof(line) - 1)) {
|
||||
error("line too long");
|
||||
}
|
||||
if (!parseLine(line)) {
|
||||
error("parseLine failed");
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
file.close();
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
235
libraries/SdFat/examples/RtcTimestampTest/RtcTimestampTest.ino
Normal file
235
libraries/SdFat/examples/RtcTimestampTest/RtcTimestampTest.ino
Normal file
@@ -0,0 +1,235 @@
|
||||
// Test of time-stamp callback.
|
||||
// Set the callback with this statement.
|
||||
// FsDateTime::setCallback(dateTime);
|
||||
#include "SdFat.h"
|
||||
// https://github.com/adafruit/RTClib
|
||||
#include "RTClib.h"
|
||||
// Set RTC_TYPE for file timestamps.
|
||||
// 0 - millis()
|
||||
// 1 - DS1307
|
||||
// 2 - DS3231
|
||||
// 3 - PCF8523
|
||||
#define RTC_TYPE 3
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
#if RTC_TYPE == 0
|
||||
RTC_Millis rtc;
|
||||
#elif RTC_TYPE == 1
|
||||
RTC_DS1307 rtc;
|
||||
#elif RTC_TYPE == 2
|
||||
RTC_DS3231 rtc;
|
||||
#elif RTC_TYPE == 3
|
||||
RTC_PCF8523 rtc;
|
||||
#else // RTC_TYPE == type
|
||||
#error RTC_TYPE type not implemented.
|
||||
#endif // RTC_TYPE == type
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Call back for file timestamps. Only called for file create and sync().
|
||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
||||
DateTime now = rtc.now();
|
||||
|
||||
// Return date using FS_DATE macro to format fields.
|
||||
*date = FS_DATE(now.year(), now.month(), now.day());
|
||||
|
||||
// Return time using FS_TIME macro to format fields.
|
||||
*time = FS_TIME(now.hour(), now.minute(), now.second());
|
||||
|
||||
// Return low time bits in units of 10 ms, 0 <= ms10 <= 199.
|
||||
*ms10 = now.second() & 1 ? 100 : 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
#define error(msg) (Serial.println(F("error " msg)), false)
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void getLine(char* line, size_t size) {
|
||||
size_t i = 0;
|
||||
uint32_t t;
|
||||
line[0] = '\0';
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
while (true) {
|
||||
t = millis() + 10;
|
||||
while (!Serial.available()) {
|
||||
if (millis() > t) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
int c = Serial.read();
|
||||
if (i >= (size - 1) || c == '\r' || c == '\n') {
|
||||
return;
|
||||
}
|
||||
line[i++] = c;
|
||||
line[i] = '\0';
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printField(Print* pr, char sep, uint8_t v) {
|
||||
if (sep) {
|
||||
pr->write(sep);
|
||||
}
|
||||
if (v < 10) {
|
||||
pr->write('0');
|
||||
}
|
||||
pr->print(v);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printNow(Print* pr) {
|
||||
DateTime now = rtc.now();
|
||||
pr->print(now.year());
|
||||
printField(pr, '-', now.month());
|
||||
printField(pr, '-', now.day());
|
||||
printField(pr, ' ', now.hour());
|
||||
printField(pr, ':', now.minute());
|
||||
printField(pr, ':', now.second());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
bool setRtc() {
|
||||
uint16_t y;
|
||||
uint8_t m, d, hh, mm, ss;
|
||||
char line[30];
|
||||
char* ptr;
|
||||
|
||||
clearSerialInput();
|
||||
Serial.println(F("Enter: YYYY-MM-DD hh:mm:ss"));
|
||||
getLine(line, sizeof(line));
|
||||
Serial.print(F("Input: "));
|
||||
Serial.println(line);
|
||||
|
||||
y = strtol(line, &ptr, 10);
|
||||
if (*ptr++ != '-' || y < 2000 || y > 2099) return error("year");
|
||||
m = strtol(ptr, &ptr, 10);
|
||||
if (*ptr++ != '-' || m < 1 || m > 12) return error("month");
|
||||
d = strtol(ptr, &ptr, 10);
|
||||
if (d < 1 || d > 31) return error("day");
|
||||
hh = strtol(ptr, &ptr, 10);
|
||||
if (*ptr++ != ':' || hh > 23) return error("hour");
|
||||
mm = strtol(ptr, &ptr, 10);
|
||||
if (*ptr++ != ':' || mm > 59) return error("minute");
|
||||
ss = strtol(ptr, &ptr, 10);
|
||||
if (ss > 59) return error("second");
|
||||
|
||||
rtc.adjust(DateTime(y, m, d, hh, mm, ss));
|
||||
Serial.print(F("RTC set to "));
|
||||
printNow(&Serial);
|
||||
Serial.println();
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
#if RTC_TYPE == 0
|
||||
rtc.begin(DateTime(F(__DATE__), F(__TIME__)));
|
||||
#else // RTC_TYPE
|
||||
if (!rtc.begin()) {
|
||||
Serial.println(F("rtc.begin failed"));
|
||||
return;
|
||||
}
|
||||
if (!rtc.isrunning()) {
|
||||
Serial.println(F("RTC is NOT running!"));
|
||||
return;
|
||||
// following line sets the RTC to the date & time this sketch was compiled
|
||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
// This line sets the RTC with an explicit date & time, for example to set
|
||||
// January 21, 2014 at 3am you would call:
|
||||
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
|
||||
}
|
||||
#endif // RTC_TYPE
|
||||
while (true) {
|
||||
Serial.print(F("DateTime::now "));
|
||||
printNow(&Serial);
|
||||
Serial.println();
|
||||
clearSerialInput();
|
||||
Serial.println(F("Type Y to set RTC, any other character to continue"));
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
if (Serial.read() != 'Y') break;
|
||||
if (setRtc()) break;
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Set callback
|
||||
FsDateTime::setCallback(dateTime);
|
||||
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
// Remove old version to set create time.
|
||||
if (sd.exists("RtcTest.txt")) {
|
||||
sd.remove("RtcTest.txt");
|
||||
}
|
||||
if (!file.open("RtcTest.txt", FILE_WRITE)) {
|
||||
Serial.println(F("file.open failed"));
|
||||
return;
|
||||
}
|
||||
// Print current date time to file.
|
||||
file.print(F("Test file at: "));
|
||||
printNow(&file);
|
||||
file.println();
|
||||
|
||||
file.close();
|
||||
// List files in SD root.
|
||||
sd.ls(LS_DATE | LS_SIZE);
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
20
libraries/SdFat/examples/SdErrorCodes/SdErrorCodes.ino
Normal file
20
libraries/SdFat/examples/SdErrorCodes/SdErrorCodes.ino
Normal file
@@ -0,0 +1,20 @@
|
||||
// Print a list of error codes, symbols, and comments.
|
||||
#include "SdFat.h"
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
delay(1000);
|
||||
Serial.println();
|
||||
Serial.println(F("Code,Symbol - failed operation"));
|
||||
for (uint8_t code = 0; code <= SD_CARD_ERROR_UNKNOWN; code++) {
|
||||
Serial.print(code < 16 ? "0X0" : "0X");
|
||||
Serial.print(code, HEX);
|
||||
Serial.print(",");
|
||||
printSdErrorSymbol(&Serial, code);
|
||||
Serial.print(" - ");
|
||||
printSdErrorText(&Serial, code);
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
void loop() {}
|
||||
253
libraries/SdFat/examples/SdFormatter/SdFormatter.ino
Normal file
253
libraries/SdFat/examples/SdFormatter/SdFormatter.ino
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* This program will format SD/SDHC/SDXC cards.
|
||||
* Warning all data will be deleted!
|
||||
*
|
||||
* This program attempts to match the format
|
||||
* generated by SDFormatter available here:
|
||||
*
|
||||
* http://www.sdcard.org/consumers/formatter/
|
||||
*
|
||||
* For very small cards this program uses FAT16
|
||||
* and the above SDFormatter uses FAT12.
|
||||
*/
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
/*
|
||||
Set DISABLE_CS_PIN to disable a second SPI device.
|
||||
For example, with the Ethernet shield, set DISABLE_CS_PIN
|
||||
to 10 to disable the Ethernet controller.
|
||||
*/
|
||||
const int8_t DISABLE_CS_PIN = -1;
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI
|
||||
and your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
//==============================================================================
|
||||
// Serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
//------------------------------------------------------------------------------
|
||||
uint32_t cardSectorCount = 0;
|
||||
uint8_t sectorBuffer[512];
|
||||
//------------------------------------------------------------------------------
|
||||
// SdCardFactory constructs and initializes the appropriate card.
|
||||
SdCardFactory cardFactory;
|
||||
// Pointer to generic SD card.
|
||||
SdCard* m_card = nullptr;
|
||||
//------------------------------------------------------------------------------
|
||||
#define sdError(msg) \
|
||||
{ \
|
||||
cout << F("error: ") << F(msg) << endl; \
|
||||
sdErrorHalt(); \
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void sdErrorHalt() {
|
||||
if (!m_card) {
|
||||
cout << F("Invalid SD_CONFIG") << endl;
|
||||
} else if (m_card->errorCode()) {
|
||||
if (m_card->errorCode() == SD_CARD_ERROR_CMD0) {
|
||||
cout << F("No card, wrong chip select pin, or wiring error?") << endl;
|
||||
}
|
||||
cout << F("SD errorCode: ") << hex << showbase;
|
||||
printSdErrorSymbol(&Serial, m_card->errorCode());
|
||||
cout << F(" = ") << int(m_card->errorCode()) << endl;
|
||||
cout << F("SD errorData = ") << int(m_card->errorData()) << endl;
|
||||
}
|
||||
while (true) {
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// flash erase all data
|
||||
uint32_t const ERASE_SIZE = 262144L;
|
||||
void eraseCard() {
|
||||
cout << endl << F("Erasing\n");
|
||||
uint32_t firstBlock = 0;
|
||||
uint32_t lastBlock;
|
||||
uint16_t n = 0;
|
||||
|
||||
do {
|
||||
lastBlock = firstBlock + ERASE_SIZE - 1;
|
||||
if (lastBlock >= cardSectorCount) {
|
||||
lastBlock = cardSectorCount - 1;
|
||||
}
|
||||
if (!m_card->erase(firstBlock, lastBlock)) {
|
||||
sdError("erase failed");
|
||||
}
|
||||
cout << '.';
|
||||
if ((n++) % 64 == 63) {
|
||||
cout << endl;
|
||||
}
|
||||
firstBlock += ERASE_SIZE;
|
||||
} while (firstBlock < cardSectorCount);
|
||||
cout << endl;
|
||||
|
||||
if (!m_card->readSector(0, sectorBuffer)) {
|
||||
sdError("readBlock");
|
||||
}
|
||||
cout << hex << showbase << setfill('0') << internal;
|
||||
cout << F("All data set to ") << setw(4) << int(sectorBuffer[0]) << endl;
|
||||
cout << dec << noshowbase << setfill(' ') << right;
|
||||
cout << F("Erase done\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void formatCard() {
|
||||
ExFatFormatter exFatFormatter;
|
||||
FatFormatter fatFormatter;
|
||||
|
||||
// Format exFAT if larger than 32GB.
|
||||
bool rtn = cardSectorCount > 67108864
|
||||
? exFatFormatter.format(m_card, sectorBuffer, &Serial)
|
||||
: fatFormatter.format(m_card, sectorBuffer, &Serial);
|
||||
|
||||
if (!rtn) {
|
||||
sdErrorHalt();
|
||||
}
|
||||
cout << F("Run the SdInfo example for format details.") << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printConfig(SdSpiConfig config) {
|
||||
if (DISABLE_CS_PIN < 0) {
|
||||
cout << F(
|
||||
"\nAssuming the SD is the only SPI device.\n"
|
||||
"Edit DISABLE_CS_PIN to disable an SPI device.\n");
|
||||
} else {
|
||||
cout << F("\nDisabling SPI device on pin ");
|
||||
cout << int(DISABLE_CS_PIN) << endl;
|
||||
pinMode(DISABLE_CS_PIN, OUTPUT);
|
||||
digitalWrite(DISABLE_CS_PIN, HIGH);
|
||||
}
|
||||
cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
|
||||
cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printConfig(SdioConfig config) {
|
||||
(void)config;
|
||||
cout << F("Assuming an SDIO interface.\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
char c;
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
printConfig(SD_CONFIG);
|
||||
cout << F("\nType any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Discard any extra characters.
|
||||
clearSerialInput();
|
||||
|
||||
cout << F(
|
||||
"\n"
|
||||
"This program can erase and/or format SD/SDHC/SDXC cards.\n"
|
||||
"\n"
|
||||
"Erase uses the card's fast flash erase command.\n"
|
||||
"Flash erase sets all data to 0X00 for most cards\n"
|
||||
"and 0XFF for a few vendor's cards.\n"
|
||||
"\n"
|
||||
"Cards up to 2 GiB (GiB = 2^30 bytes) will be formated FAT16.\n"
|
||||
"Cards larger than 2 GiB and up to 32 GiB will be formatted\n"
|
||||
"FAT32. Cards larger than 32 GiB will be formatted exFAT.\n"
|
||||
"\n"
|
||||
"Warning, all data on the card will be erased.\n"
|
||||
"Enter 'Y' to continue: ");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
c = Serial.read();
|
||||
cout << c << endl;
|
||||
if (c != 'Y') {
|
||||
cout << F("Quiting, you did not enter 'Y'.\n");
|
||||
return;
|
||||
}
|
||||
// Read any existing Serial data.
|
||||
clearSerialInput();
|
||||
|
||||
// Select and initialize proper card driver.
|
||||
m_card = cardFactory.newCard(SD_CONFIG);
|
||||
if (!m_card || m_card->errorCode()) {
|
||||
sdError("card init failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
cardSectorCount = m_card->sectorCount();
|
||||
if (!cardSectorCount) {
|
||||
sdError("Get sector count failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
cout << F("\nCard size: ") << cardSectorCount * 5.12e-7;
|
||||
cout << F(" GB (GB = 1E9 bytes)\n");
|
||||
cout << F("Card size: ") << cardSectorCount / 2097152.0;
|
||||
cout << F(" GiB (GiB = 2^30 bytes)\n");
|
||||
|
||||
cout << F("Card will be formated ");
|
||||
if (cardSectorCount > 67108864) {
|
||||
cout << F("exFAT\n");
|
||||
} else if (cardSectorCount > 4194304) {
|
||||
cout << F("FAT32\n");
|
||||
} else {
|
||||
cout << F("FAT16\n");
|
||||
}
|
||||
cout << F(
|
||||
"\n"
|
||||
"Options are:\n"
|
||||
"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()) {
|
||||
yield();
|
||||
}
|
||||
c = Serial.read();
|
||||
cout << c << endl;
|
||||
if (!strchr("EFQ", c)) {
|
||||
cout << F("Quiting, invalid option entered.") << endl;
|
||||
return;
|
||||
}
|
||||
if (c == 'E' || c == 'F') {
|
||||
eraseCard();
|
||||
}
|
||||
if (c == 'F' || c == 'Q') {
|
||||
formatCard();
|
||||
}
|
||||
}
|
||||
void loop() {}
|
||||
281
libraries/SdFat/examples/SdInfo/SdInfo.ino
Normal file
281
libraries/SdFat/examples/SdInfo/SdInfo.ino
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* This program attempts to initialize an SD card and analyze its structure.
|
||||
* The CID and CSD registers are also printed in HEX for use in online
|
||||
* decoders like these.
|
||||
*
|
||||
* https://gurumeditation.org/1342/sd-memory-card-register-decoder/
|
||||
* https://archive.goughlui.com/static/multicid.htm
|
||||
*/
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
/*
|
||||
Set DISABLE_CS_PIN to disable a second SPI device.
|
||||
For example, with the Ethernet shield, set DISABLE_CS_PIN
|
||||
to 10 to disable the Ethernet controller.
|
||||
*/
|
||||
const int8_t DISABLE_CS_PIN = -1;
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI
|
||||
and your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16))
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(16))
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SdFs sd;
|
||||
cid_t cid;
|
||||
csd_t csd;
|
||||
scr_t scr;
|
||||
uint8_t cmd6Data[64];
|
||||
uint32_t eraseSize;
|
||||
uint32_t ocr;
|
||||
static ArduinoOutStream cout(Serial);
|
||||
//------------------------------------------------------------------------------
|
||||
void cidDmp() {
|
||||
cout << F("\nManufacturer ID: ");
|
||||
cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
|
||||
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
|
||||
cout << F("Product: ");
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
cout << cid.pnm[i];
|
||||
}
|
||||
cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
|
||||
cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
|
||||
cout << F("Manufacturing date: ");
|
||||
cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
|
||||
cout << F("CID HEX: ");
|
||||
hexDmp(&cid, sizeof(cid));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void csdDmp() {
|
||||
eraseSize = csd.eraseSize();
|
||||
cout << F("\ncardSize: ") << 0.000512 * csd.capacity();
|
||||
cout << F(" MB (MB = 1,000,000 bytes)\n");
|
||||
|
||||
cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
|
||||
cout << F("eraseSingleBlock: ");
|
||||
if (csd.eraseSingleBlock()) {
|
||||
cout << F("true\n");
|
||||
} else {
|
||||
cout << F("false\n");
|
||||
}
|
||||
cout << F("dataAfterErase: ");
|
||||
if (scr.dataAfterErase()) {
|
||||
cout << F("ones\n");
|
||||
} else {
|
||||
cout << F("zeros\n");
|
||||
}
|
||||
cout << F("CSD HEX: ");
|
||||
hexDmp(&csd, sizeof(csd));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void errorPrint() {
|
||||
if (sd.sdErrorCode()) {
|
||||
cout << F("SD errorCode: ") << hex << showbase;
|
||||
printSdErrorSymbol(&Serial, sd.sdErrorCode());
|
||||
cout << F(" = ") << int(sd.sdErrorCode()) << endl;
|
||||
cout << F("SD errorData = ") << int(sd.sdErrorData()) << dec << endl;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void hexDmp(void* reg, uint8_t size) {
|
||||
uint8_t* u8 = reinterpret_cast<uint8_t*>(reg);
|
||||
cout << hex << noshowbase;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
cout << setw(2) << setfill('0') << int(u8[i]);
|
||||
}
|
||||
cout << dec << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
bool mbrDmp() {
|
||||
MbrSector_t mbr;
|
||||
bool valid = true;
|
||||
if (!sd.card()->readSector(0, (uint8_t*)&mbr)) {
|
||||
cout << F("\nread MBR failed.\n");
|
||||
errorPrint();
|
||||
return false;
|
||||
}
|
||||
cout << F("\nSD Partition Table\n");
|
||||
cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n");
|
||||
for (uint8_t ip = 1; ip < 5; ip++) {
|
||||
MbrPart_t* pt = &mbr.part[ip - 1];
|
||||
if ((pt->boot != 0 && pt->boot != 0X80) ||
|
||||
getLe32(pt->relativeSectors) > csd.capacity()) {
|
||||
valid = false;
|
||||
}
|
||||
cout << int(ip) << ',' << uppercase << showbase << hex;
|
||||
cout << int(pt->boot) << ',';
|
||||
for (int i = 0; i < 3; i++) {
|
||||
cout << int(pt->beginCHS[i]) << ',';
|
||||
}
|
||||
cout << int(pt->type) << ',';
|
||||
for (int i = 0; i < 3; i++) {
|
||||
cout << int(pt->endCHS[i]) << ',';
|
||||
}
|
||||
cout << dec << getLe32(pt->relativeSectors) << ',';
|
||||
cout << getLe32(pt->totalSectors) << endl;
|
||||
}
|
||||
if (!valid) {
|
||||
cout << F("\nMBR not valid, assuming Super Floppy format.\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void dmpVol() {
|
||||
cout << F("\nScanning FAT, please wait.\n");
|
||||
int32_t freeClusterCount = sd.freeClusterCount();
|
||||
if (sd.fatType() <= 32) {
|
||||
cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
|
||||
} else {
|
||||
cout << F("\nVolume is exFAT\n");
|
||||
}
|
||||
cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
|
||||
cout << F("fatStartSector: ") << sd.fatStartSector() << endl;
|
||||
cout << F("dataStartSector: ") << sd.dataStartSector() << endl;
|
||||
cout << F("clusterCount: ") << sd.clusterCount() << endl;
|
||||
cout << F("freeClusterCount: ");
|
||||
if (freeClusterCount >= 0) {
|
||||
cout << freeClusterCount << endl;
|
||||
} else {
|
||||
cout << F("failed\n");
|
||||
errorPrint();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printCardType() {
|
||||
cout << F("\nCard type: ");
|
||||
|
||||
switch (sd.card()->type()) {
|
||||
case SD_CARD_TYPE_SD1:
|
||||
cout << F("SD1\n");
|
||||
break;
|
||||
|
||||
case SD_CARD_TYPE_SD2:
|
||||
cout << F("SD2\n");
|
||||
break;
|
||||
|
||||
case SD_CARD_TYPE_SDHC:
|
||||
if (csd.capacity() < 70000000) {
|
||||
cout << F("SDHC\n");
|
||||
} else {
|
||||
cout << F("SDXC\n");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cout << F("Unknown\n");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printConfig(SdSpiConfig config) {
|
||||
if (DISABLE_CS_PIN < 0) {
|
||||
cout << F(
|
||||
"\nAssuming the SD is the only SPI device.\n"
|
||||
"Edit DISABLE_CS_PIN to disable an SPI device.\n");
|
||||
} else {
|
||||
cout << F("\nDisabling SPI device on pin ");
|
||||
cout << int(DISABLE_CS_PIN) << endl;
|
||||
pinMode(DISABLE_CS_PIN, OUTPUT);
|
||||
digitalWrite(DISABLE_CS_PIN, HIGH);
|
||||
}
|
||||
cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
|
||||
cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printConfig(SdioConfig config) {
|
||||
(void)config;
|
||||
cout << F("Assuming an SDIO interface.\n");
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
cout << F("SdFat version: ") << SD_FAT_VERSION_STR << endl;
|
||||
printConfig(SD_CONFIG);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
// Read any existing Serial data.
|
||||
clearSerialInput();
|
||||
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("\ntype any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
uint32_t t = millis();
|
||||
if (!sd.cardBegin(SD_CONFIG)) {
|
||||
cout << F(
|
||||
"\nSD initialization failed.\n"
|
||||
"Do not reformat the card!\n"
|
||||
"Is the card correctly inserted?\n"
|
||||
"Is there a wiring/soldering problem?\n");
|
||||
if (isSpi(SD_CONFIG)) {
|
||||
cout << F(
|
||||
"Is SD_CS_PIN set to the correct value?\n"
|
||||
"Does another SPI device need to be disabled?\n");
|
||||
}
|
||||
errorPrint();
|
||||
return;
|
||||
}
|
||||
t = millis() - t;
|
||||
cout << F("init time: ") << dec << t << " ms" << endl;
|
||||
|
||||
if (!sd.card()->readCID(&cid) || !sd.card()->readCSD(&csd) ||
|
||||
!sd.card()->readOCR(&ocr) || !sd.card()->readSCR(&scr)) {
|
||||
cout << F("readInfo failed\n");
|
||||
errorPrint();
|
||||
return;
|
||||
}
|
||||
printCardType();
|
||||
cout << F("sdSpecVer: ") << 0.01 * scr.sdSpecVer() << endl;
|
||||
cout << F("HighSpeedMode: ");
|
||||
if (scr.sdSpecVer() > 101 && sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) &&
|
||||
(2 & cmd6Data[13])) {
|
||||
cout << F("true\n");
|
||||
} else {
|
||||
cout << F("false\n");
|
||||
}
|
||||
cidDmp();
|
||||
csdDmp();
|
||||
cout << F("\nOCR: ") << uppercase << showbase;
|
||||
cout << hex << ocr << dec << endl;
|
||||
if (!mbrDmp()) {
|
||||
return;
|
||||
}
|
||||
if (!sd.volumeBegin()) {
|
||||
cout << F("\nvolumeBegin failed. Is the card formatted?\n");
|
||||
errorPrint();
|
||||
return;
|
||||
}
|
||||
dmpVol();
|
||||
}
|
||||
80
libraries/SdFat/examples/SoftwareSpi/SoftwareSpi.ino
Normal file
80
libraries/SdFat/examples/SoftwareSpi/SoftwareSpi.ino
Normal file
@@ -0,0 +1,80 @@
|
||||
// An example of the SoftSpiDriver template class.
|
||||
// This example is for an old Adafruit Data Logging Shield on a Mega.
|
||||
// Software SPI is required on Mega since this shield connects to pins 10-13.
|
||||
// This example will also run on an Uno and other boards using software SPI.
|
||||
//
|
||||
#include "SdFat.h"
|
||||
#if SPI_DRIVER_SELECT == 2 // Must be set in SdFat/SdFatConfig.h
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
//
|
||||
// Chip select may be constant or RAM variable.
|
||||
const uint8_t SD_CS_PIN = 10;
|
||||
//
|
||||
// Pin numbers in templates must be constants.
|
||||
const uint8_t SOFT_MISO_PIN = 12;
|
||||
const uint8_t SOFT_MOSI_PIN = 11;
|
||||
const uint8_t SOFT_SCK_PIN = 13;
|
||||
|
||||
// SdFat software SPI template
|
||||
SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi;
|
||||
// Speed argument is ignored for software SPI.
|
||||
#if ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
|
||||
#else // ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(0), &softSpi)
|
||||
#endif // ENABLE_DEDICATED_SPI
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
if (!file.open("SoftSPI.txt", O_RDWR | O_CREAT)) {
|
||||
sd.errorHalt(F("open failed"));
|
||||
}
|
||||
file.println(F("This line was printed using software SPI."));
|
||||
|
||||
file.rewind();
|
||||
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
Serial.println(F("Done."));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
#else // SPI_DRIVER_SELECT
|
||||
#error SPI_DRIVER_SELECT must be two in SdFat/SdFatConfig.h
|
||||
#endif // SPI_DRIVER_SELECT
|
||||
66
libraries/SdFat/examples/SpiLoopBackTest/SpiLoopBackTest.ino
Normal file
66
libraries/SdFat/examples/SpiLoopBackTest/SpiLoopBackTest.ino
Normal file
@@ -0,0 +1,66 @@
|
||||
// This is a simple SPI loop-back test.
|
||||
//
|
||||
// Connect SD_MISO to SD_MOSI
|
||||
//
|
||||
// Modify these defines for your configuration.
|
||||
#define SD_SPI SPI
|
||||
#define SD_MISO MISO
|
||||
#define SD_MOSI MOSI
|
||||
|
||||
#include "SPI.h"
|
||||
void setup() {
|
||||
uint8_t rx, tx;
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println(F("\nType any character to start"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
Serial.print("Begin, SD_MISO: ");
|
||||
Serial.print(SD_MISO), Serial.print(", SD_MOSI: ");
|
||||
Serial.println(SD_MOSI);
|
||||
pinMode(SD_MISO, INPUT_PULLUP);
|
||||
pinMode(SD_MOSI, OUTPUT);
|
||||
digitalWrite(SD_MOSI, HIGH);
|
||||
if (!digitalRead(SD_MISO)) {
|
||||
Serial.println("Error: SD_MISO not HIGH");
|
||||
goto fail;
|
||||
}
|
||||
digitalWrite(SD_MOSI, LOW);
|
||||
if (digitalRead(SD_MISO)) {
|
||||
Serial.println("Error: SD_MISO not LOW");
|
||||
goto fail;
|
||||
}
|
||||
pinMode(SD_MISO, INPUT);
|
||||
pinMode(SD_MOSI, INPUT);
|
||||
|
||||
// Modify if SD_SPI.begin has arguments and use this style SdFat begin call:
|
||||
// sd.begin(SdSpiConfig(CS_PIN, USER_SPI_BEGIN | <other options>, &SD_SPI));
|
||||
SD_SPI.begin();
|
||||
|
||||
// Start with a 400 kHz clock. Try full speed if success for 400 kHz.
|
||||
SD_SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0));
|
||||
tx = 0;
|
||||
do {
|
||||
rx = SD_SPI.transfer(tx);
|
||||
if (tx != rx) {
|
||||
Serial.print("Error rx: 0x");
|
||||
Serial.print(rx, HEX);
|
||||
Serial.print(" != tx: 0x");
|
||||
Serial.println(tx, HEX);
|
||||
SD_SPI.endTransaction();
|
||||
goto fail;
|
||||
}
|
||||
} while (tx++ < 255);
|
||||
SD_SPI.endTransaction();
|
||||
Serial.println("Success!");
|
||||
return;
|
||||
|
||||
fail:
|
||||
SD_SPI.endTransaction();
|
||||
Serial.println("Is SD_MISO connected to SD_MOSI?");
|
||||
Serial.println("Are SD_MISO and SD_MOSI correct?");
|
||||
}
|
||||
void loop() {}
|
||||
@@ -0,0 +1,214 @@
|
||||
// Test of Teensy exFAT DMA ADC logger.
|
||||
// This is mainly to test use of RingBuf in an ISR.
|
||||
// This example only supports pins on the first ADC.
|
||||
// it has only been tested on Teensy 3.6 and 4.1.
|
||||
// You should modify it for serious use as a data logger.
|
||||
//
|
||||
#include "ADC.h"
|
||||
#include "DMAChannel.h"
|
||||
#include "FreeStack.h"
|
||||
#include "RingBuf.h"
|
||||
#include "SdFat.h"
|
||||
|
||||
// Pin must be on first ADC.
|
||||
#define ADC_PIN A0
|
||||
|
||||
// 400 sector RingBuf - could be larger on Teensy 4.1.
|
||||
const size_t RING_BUF_SIZE = 400 * 512;
|
||||
|
||||
// Preallocate 8GiB file.
|
||||
const uint64_t PRE_ALLOCATE_SIZE = 8ULL << 30;
|
||||
|
||||
// Use FIFO SDIO.
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
|
||||
ADC adc;
|
||||
|
||||
DMAChannel dma(true);
|
||||
|
||||
SdFs sd;
|
||||
|
||||
FsFile file;
|
||||
|
||||
// Ping-pong DMA buffer.
|
||||
DMAMEM static uint16_t __attribute__((aligned(32))) dmaBuf[2][256];
|
||||
|
||||
// Count of DMA interrupts.
|
||||
volatile size_t dmaCount;
|
||||
|
||||
// RingBuf for 512 byte sectors.
|
||||
RingBuf<FsFile, RING_BUF_SIZE> rb;
|
||||
|
||||
// Shared between ISR and background.
|
||||
volatile size_t maxBytesUsed;
|
||||
|
||||
// Overrun error for write to RingBuf.
|
||||
volatile bool overrun;
|
||||
//------------------------------------------------------------------------------
|
||||
// ISR for DMA.
|
||||
static void isr() {
|
||||
if (!overrun) {
|
||||
// Clear cache for buffer filled by DMA to insure read from DMA memory.
|
||||
arm_dcache_delete((void*)dmaBuf[dmaCount & 1], 512);
|
||||
// Enable RingBuf functions to be called in ISR.
|
||||
rb.beginISR();
|
||||
if (rb.write(dmaBuf[dmaCount & 1], 512) == 512) {
|
||||
dmaCount++;
|
||||
if (rb.bytesUsed() > maxBytesUsed) {
|
||||
maxBytesUsed = rb.bytesUsed();
|
||||
}
|
||||
} else {
|
||||
overrun = true;
|
||||
}
|
||||
// End use of RingBuf functions in ISR.
|
||||
rb.endISR();
|
||||
}
|
||||
dma.clearComplete();
|
||||
dma.clearInterrupt();
|
||||
#if defined(__IMXRT1062__)
|
||||
// Handle clear interrupt glitch in Teensy 4.x!
|
||||
asm("DSB");
|
||||
#endif // defined(__IMXRT1062__)
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
#if defined(__IMXRT1062__) // Teensy 4.x
|
||||
#define SOURCE_SADDR ADC1_R0
|
||||
#define SOURCE_EVENT DMAMUX_SOURCE_ADC1
|
||||
#else
|
||||
#define SOURCE_SADDR ADC0_RA
|
||||
#define SOURCE_EVENT DMAMUX_SOURCE_ADC0
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
static void init(uint8_t pin) {
|
||||
dma.begin();
|
||||
dma.attachInterrupt(isr);
|
||||
dma.source((volatile const signed short&)SOURCE_SADDR);
|
||||
dma.destinationBuffer((volatile uint16_t*)dmaBuf, sizeof(dmaBuf));
|
||||
dma.interruptAtHalf();
|
||||
dma.interruptAtCompletion();
|
||||
dma.triggerAtHardwareEvent(SOURCE_EVENT);
|
||||
dma.enable();
|
||||
adc.adc0->enableDMA();
|
||||
adc.adc0->startContinuous(pin);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void stopDma() {
|
||||
adc.adc0->disableDMA();
|
||||
dma.disable();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printTest(Print* pr) {
|
||||
if (file.fileSize() < 1024 * 2) {
|
||||
return;
|
||||
}
|
||||
file.rewind();
|
||||
rb.begin(&file);
|
||||
// Could readIn RING_BUF_SIZE bytes and write to a csv file in a loop.
|
||||
if (rb.readIn(2048) != 2048) {
|
||||
sd.errorHalt("rb.readIn failed");
|
||||
}
|
||||
uint16_t data;
|
||||
for (size_t i = 0; i < 1024; i++) {
|
||||
pr->print(i);
|
||||
pr->print(',');
|
||||
// Test read with: template <typename Type>bool read(Type* data).
|
||||
rb.read(&data);
|
||||
pr->println(data);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void runTest(uint8_t pin) {
|
||||
dmaCount = 0;
|
||||
maxBytesUsed = 0;
|
||||
overrun = false;
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.read() >= 0);
|
||||
|
||||
if (!file.open("IsrLoggerTest.bin", O_CREAT | O_TRUNC | O_RDWR)) {
|
||||
sd.errorHalt("file.open failed");
|
||||
}
|
||||
if (!file.preAllocate(PRE_ALLOCATE_SIZE)) {
|
||||
sd.errorHalt("file.preAllocate failed");
|
||||
}
|
||||
rb.begin(&file);
|
||||
Serial.println("Type any character to stop\n");
|
||||
|
||||
init(pin);
|
||||
uint32_t samplingTime = micros();
|
||||
while (!overrun && !Serial.available()) {
|
||||
size_t n = rb.bytesUsed();
|
||||
if ((n + file.curPosition()) >= (PRE_ALLOCATE_SIZE - 512)) {
|
||||
Serial.println("File full - stopping");
|
||||
break;
|
||||
}
|
||||
if (n >= 512) {
|
||||
if (rb.writeOut(512) != 512) {
|
||||
Serial.println("writeOut() failed");
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
stopDma();
|
||||
samplingTime = micros() - samplingTime;
|
||||
if (!rb.sync()) {
|
||||
Serial.println("sync() failed");
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
if (!file.truncate()) {
|
||||
sd.errorHalt("truncate failed");
|
||||
}
|
||||
if (overrun) {
|
||||
Serial.println("Overrun ERROR!!");
|
||||
}
|
||||
Serial.print("dmsCount ");
|
||||
Serial.println(dmaCount);
|
||||
Serial.print("RingBufSize ");
|
||||
Serial.println(RING_BUF_SIZE);
|
||||
Serial.print("maxBytesUsed ");
|
||||
Serial.println(maxBytesUsed);
|
||||
Serial.print("fileSize ");
|
||||
file.printFileSize(&Serial);
|
||||
Serial.println();
|
||||
Serial.print(0.000001 * samplingTime);
|
||||
Serial.println(" seconds");
|
||||
Serial.print(1.0 * file.fileSize() / samplingTime, 3);
|
||||
Serial.println(" MB/sec\n");
|
||||
printTest(&Serial);
|
||||
file.close();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void waitSerial(const char* msg) {
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.read() >= 0);
|
||||
Serial.println(msg);
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
waitSerial("Type any character to begin");
|
||||
Serial.print("FreeStack: ");
|
||||
Serial.println(FreeStack());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
// Try for max speed.
|
||||
adc.adc0->setAveraging(1);
|
||||
adc.adc0->setResolution(10);
|
||||
adc.adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
|
||||
adc.adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
|
||||
runTest(ADC_PIN);
|
||||
waitSerial("Type any character to run test again");
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Test of time-stamp callback with Teensy 3/4.
|
||||
// The upload time will be used to set the RTC.
|
||||
// You must arrange for syncing the RTC.
|
||||
#include <TimeLib.h>
|
||||
|
||||
#include "SdFat.h"
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Call back for file timestamps. Only called for file create and sync().
|
||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
||||
// Return date using FS_DATE macro to format fields.
|
||||
*date = FS_DATE(year(), month(), day());
|
||||
|
||||
// Return time using FS_TIME macro to format fields.
|
||||
*time = FS_TIME(hour(), minute(), second());
|
||||
|
||||
// Return low time bits in units of 10 ms.
|
||||
*ms10 = second() & 1 ? 100 : 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
time_t getTeensy3Time() { return Teensy3Clock.get(); }
|
||||
//------------------------------------------------------------------------------
|
||||
void printField(Print* pr, char sep, uint8_t v) {
|
||||
if (sep) {
|
||||
pr->write(sep);
|
||||
}
|
||||
if (v < 10) {
|
||||
pr->write('0');
|
||||
}
|
||||
pr->print(v);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void printNow(Print* pr) {
|
||||
pr->print(year());
|
||||
printField(pr, '-', month());
|
||||
printField(pr, '-', day());
|
||||
printField(pr, ' ', hour());
|
||||
printField(pr, ':', minute());
|
||||
printField(pr, ':', second());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
// set the Time library to use Teensy 3.0's RTC to keep time
|
||||
setSyncProvider(getTeensy3Time);
|
||||
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println(F("Type any character to begin"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (timeStatus() != timeSet) {
|
||||
Serial.println("Unable to sync with the RTC");
|
||||
return;
|
||||
}
|
||||
Serial.print(F("DateTime::now "));
|
||||
printNow(&Serial);
|
||||
Serial.println();
|
||||
|
||||
// Set callback
|
||||
FsDateTime::setCallback(dateTime);
|
||||
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
// Remove old version to set create time.
|
||||
if (sd.exists("RtcTest.txt")) {
|
||||
sd.remove("RtcTest.txt");
|
||||
}
|
||||
if (!file.open("RtcTest.txt", FILE_WRITE)) {
|
||||
Serial.println(F("file.open failed"));
|
||||
return;
|
||||
}
|
||||
// Print current date time to file.
|
||||
file.print(F("Test file at: "));
|
||||
printNow(&file);
|
||||
file.println();
|
||||
|
||||
file.close();
|
||||
// List files in SD root.
|
||||
sd.ls(LS_DATE | LS_SIZE);
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
220
libraries/SdFat/examples/TeensySdioDemo/TeensySdioDemo.ino
Normal file
220
libraries/SdFat/examples/TeensySdioDemo/TeensySdioDemo.ino
Normal file
@@ -0,0 +1,220 @@
|
||||
// Simple performance test for Teensy 3.5/3.6 4.0 SDHC.
|
||||
// Demonstrates yield() efficiency for SDIO modes.
|
||||
#include "SdFat.h"
|
||||
|
||||
// Use built-in SD for SPI modes on Teensy 3.5/3.6.
|
||||
// Teensy 4.0 use first SPI port.
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
|
||||
// 32 KiB buffer.
|
||||
const size_t BUF_DIM = 32768;
|
||||
|
||||
// 8 MiB file.
|
||||
const uint32_t FILE_SIZE = 256UL * BUF_DIM;
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
uint8_t buf[BUF_DIM];
|
||||
|
||||
// buffer as uint32_t
|
||||
uint32_t* buf32 = (uint32_t*)buf;
|
||||
|
||||
// Total usec in read/write calls.
|
||||
uint32_t totalMicros = 0;
|
||||
// Time in yield() function.
|
||||
uint32_t yieldMicros = 0;
|
||||
// Number of yield calls.
|
||||
uint32_t yieldCalls = 0;
|
||||
// Max busy time for single yield call.
|
||||
uint32_t yieldMaxUsec = 0;
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void errorHalt(const char* msg) {
|
||||
Serial.print("Error: ");
|
||||
Serial.println(msg);
|
||||
if (sd.sdErrorCode()) {
|
||||
if (sd.sdErrorCode() == SD_CARD_ERROR_ACMD41) {
|
||||
Serial.println("Try power cycling the SD card.");
|
||||
}
|
||||
printSdErrorSymbol(&Serial, sd.sdErrorCode());
|
||||
Serial.print(", ErrorData: 0X");
|
||||
Serial.println(sd.sdErrorData(), HEX);
|
||||
}
|
||||
while (true) {
|
||||
}
|
||||
}
|
||||
bool ready = false;
|
||||
//------------------------------------------------------------------------------
|
||||
bool sdBusy() { return ready ? sd.card()->isBusy() : false; }
|
||||
//------------------------------------------------------------------------------
|
||||
// Replace "weak" system yield() function.
|
||||
void yield() {
|
||||
// Only count cardBusy time.
|
||||
if (!sdBusy()) {
|
||||
return;
|
||||
}
|
||||
uint32_t m = micros();
|
||||
yieldCalls++;
|
||||
while (sdBusy()) {
|
||||
// Do something here.
|
||||
}
|
||||
m = micros() - m;
|
||||
if (m > yieldMaxUsec) {
|
||||
yieldMaxUsec = m;
|
||||
}
|
||||
yieldMicros += m;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void runTest() {
|
||||
// Zero Stats
|
||||
totalMicros = 0;
|
||||
yieldMicros = 0;
|
||||
yieldCalls = 0;
|
||||
yieldMaxUsec = 0;
|
||||
if (!file.open("TeensyDemo.bin", O_RDWR | O_CREAT)) {
|
||||
errorHalt("open failed");
|
||||
}
|
||||
Serial.println("\nsize,write,read");
|
||||
Serial.println("bytes,KB/sec,KB/sec");
|
||||
for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) {
|
||||
uint32_t nRdWr = FILE_SIZE / nb;
|
||||
if (!file.truncate(0)) {
|
||||
errorHalt("truncate failed");
|
||||
}
|
||||
|
||||
Serial.print(nb);
|
||||
Serial.print(',');
|
||||
uint32_t t = micros();
|
||||
for (uint32_t n = 0; n < nRdWr; n++) {
|
||||
// Set start and end of buffer.
|
||||
buf32[0] = n;
|
||||
buf32[nb / 4 - 1] = n;
|
||||
if (nb != file.write(buf, nb)) {
|
||||
errorHalt("write failed");
|
||||
}
|
||||
}
|
||||
t = micros() - t;
|
||||
totalMicros += t;
|
||||
Serial.print(1000.0 * FILE_SIZE / t);
|
||||
Serial.print(',');
|
||||
file.rewind();
|
||||
t = micros();
|
||||
|
||||
for (uint32_t n = 0; n < nRdWr; n++) {
|
||||
if ((int)nb != file.read(buf, nb)) {
|
||||
errorHalt("read failed");
|
||||
}
|
||||
// crude check of data.
|
||||
if (buf32[0] != n || buf32[nb / 4 - 1] != n) {
|
||||
errorHalt("data check");
|
||||
}
|
||||
}
|
||||
t = micros() - t;
|
||||
totalMicros += t;
|
||||
Serial.println(1000.0 * FILE_SIZE / t);
|
||||
}
|
||||
file.close();
|
||||
Serial.print("\ntotalMicros ");
|
||||
Serial.println(totalMicros);
|
||||
Serial.print("yieldMicros ");
|
||||
Serial.println(yieldMicros);
|
||||
Serial.print("yieldCalls ");
|
||||
Serial.println(yieldCalls);
|
||||
Serial.print("yieldMaxUsec ");
|
||||
Serial.println(yieldMaxUsec);
|
||||
// Serial.print("kHzSdClk ");
|
||||
// Serial.println(kHzSdClk());
|
||||
Serial.println("Done");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
static bool warn = true;
|
||||
if (warn) {
|
||||
warn = false;
|
||||
Serial.println(
|
||||
"SD cards must be power cycled to leave\n"
|
||||
"SPI mode so do SDIO tests first.\n"
|
||||
"\nCycle power on the card if an error occurs.");
|
||||
}
|
||||
clearSerialInput();
|
||||
|
||||
Serial.println(
|
||||
"\nType '1' for FIFO SDIO"
|
||||
"\n '2' for DMA SDIO"
|
||||
"\n '3' for Dedicated SPI"
|
||||
"\n '4' for Shared SPI");
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
char c = Serial.read();
|
||||
|
||||
if (c == '1') {
|
||||
if (!sd.begin(SdioConfig(FIFO_SDIO))) {
|
||||
errorHalt("begin failed");
|
||||
}
|
||||
Serial.println("\nFIFO SDIO mode.");
|
||||
} else if (c == '2') {
|
||||
if (!sd.begin(SdioConfig(DMA_SDIO))) {
|
||||
errorHalt("begin failed");
|
||||
}
|
||||
Serial.println("\nDMA SDIO mode - slow for small transfers.");
|
||||
} else if (c == '3') {
|
||||
#if ENABLE_DEDICATED_SPI
|
||||
if (!sd.begin(SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) {
|
||||
errorHalt("begin failed");
|
||||
}
|
||||
Serial.println("\nDedicated SPI mode.");
|
||||
#else // ENABLE_DEDICATED_SPI
|
||||
Serial.println("ENABLE_DEDICATED_SPI must be non-zero.");
|
||||
return;
|
||||
#endif // ENABLE_DEDICATED_SPI
|
||||
} else if (c == '4') {
|
||||
if (!sd.begin(SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50)))) {
|
||||
errorHalt("begin failed");
|
||||
}
|
||||
Serial.println("\nShared SPI mode - slow for small transfers.");
|
||||
} else {
|
||||
Serial.println("Invalid input");
|
||||
return;
|
||||
}
|
||||
ready = true;
|
||||
runTest();
|
||||
ready = false;
|
||||
}
|
||||
151
libraries/SdFat/examples/TeensySdioLogger/TeensySdioLogger.ino
Normal file
151
libraries/SdFat/examples/TeensySdioLogger/TeensySdioLogger.ino
Normal file
@@ -0,0 +1,151 @@
|
||||
// Test Teensy SDIO with write busy in a data logger demo.
|
||||
//
|
||||
// The driver writes to the uSDHC controller's FIFO then returns
|
||||
// while the controller writes the data to the SD. The first sector
|
||||
// puts the controller in write mode and takes about 11 usec on a
|
||||
// Teensy 4.1. About 5 usec is required to write a sector when the
|
||||
// controller is in write mode.
|
||||
|
||||
#include "RingBuf.h"
|
||||
#include "SdFat.h"
|
||||
|
||||
// Use Teensy SDIO
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
|
||||
// Interval between points for 25 ksps.
|
||||
#define LOG_INTERVAL_USEC 40
|
||||
|
||||
// Size to log 10 byte lines at 25 kHz for more than ten minutes.
|
||||
#define LOG_FILE_SIZE 10 * 25000 * 600 // 150,000,000 bytes.
|
||||
|
||||
// Space to hold more than 800 ms of data for 10 byte lines at 25 ksps.
|
||||
#define RING_BUF_CAPACITY 400 * 512
|
||||
#define LOG_FILENAME "SdioLogger.csv"
|
||||
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
|
||||
// RingBuf for File type FsFile.
|
||||
RingBuf<FsFile, RING_BUF_CAPACITY> rb;
|
||||
|
||||
void logData() {
|
||||
// Initialize the SD.
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
// Open or create file - truncate existing file.
|
||||
if (!file.open(LOG_FILENAME, O_RDWR | O_CREAT | O_TRUNC)) {
|
||||
Serial.println("open failed\n");
|
||||
return;
|
||||
}
|
||||
// File must be pre-allocated to avoid huge
|
||||
// delays searching for free clusters.
|
||||
if (!file.preAllocate(LOG_FILE_SIZE)) {
|
||||
Serial.println("preAllocate failed\n");
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
// initialize the RingBuf.
|
||||
rb.begin(&file);
|
||||
Serial.println("Type any character to stop");
|
||||
|
||||
// Max RingBuf used bytes. Useful to understand RingBuf overrun.
|
||||
size_t maxUsed = 0;
|
||||
|
||||
// Min spare micros in loop.
|
||||
int32_t minSpareMicros = INT32_MAX;
|
||||
|
||||
// Start time.
|
||||
uint32_t logTime = micros();
|
||||
// Log data until Serial input or file full.
|
||||
while (!Serial.available()) {
|
||||
// Amount of data in ringBuf.
|
||||
size_t n = rb.bytesUsed();
|
||||
if ((n + file.curPosition()) > (LOG_FILE_SIZE - 20)) {
|
||||
Serial.println("File full - quitting.");
|
||||
break;
|
||||
}
|
||||
if (n > maxUsed) {
|
||||
maxUsed = n;
|
||||
}
|
||||
if (n >= 512 && !file.isBusy()) {
|
||||
// Not busy only allows one sector before possible busy wait.
|
||||
// Write one sector from RingBuf to file.
|
||||
if (512 != rb.writeOut(512)) {
|
||||
Serial.println("writeOut failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Time for next point.
|
||||
logTime += LOG_INTERVAL_USEC;
|
||||
int32_t spareMicros = logTime - micros();
|
||||
if (spareMicros < minSpareMicros) {
|
||||
minSpareMicros = spareMicros;
|
||||
}
|
||||
if (spareMicros <= 0) {
|
||||
Serial.print("Rate too fast ");
|
||||
Serial.println(spareMicros);
|
||||
break;
|
||||
}
|
||||
// Wait until time to log data.
|
||||
while (micros() < logTime) {
|
||||
}
|
||||
|
||||
// Read ADC0 - about 17 usec on Teensy 4, Teensy 3.6 is faster.
|
||||
uint16_t adc = analogRead(0);
|
||||
// Print spareMicros into the RingBuf as test data.
|
||||
rb.print(spareMicros);
|
||||
rb.write(',');
|
||||
// Print adc into RingBuf.
|
||||
rb.println(adc);
|
||||
if (rb.getWriteError()) {
|
||||
// Error caused by too few free bytes in RingBuf.
|
||||
Serial.println("WriteError");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Write any RingBuf data to file.
|
||||
rb.sync();
|
||||
file.truncate();
|
||||
file.rewind();
|
||||
// Print first twenty lines of file.
|
||||
Serial.println("spareMicros,ADC0");
|
||||
for (uint8_t n = 0; n < 20 && file.available();) {
|
||||
int c = file.read();
|
||||
if (c < 0) {
|
||||
break;
|
||||
}
|
||||
Serial.write(c);
|
||||
if (c == '\n') n++;
|
||||
}
|
||||
Serial.print("fileSize: ");
|
||||
Serial.println((uint32_t)file.fileSize());
|
||||
Serial.print("maxBytesUsed: ");
|
||||
Serial.println(maxUsed);
|
||||
Serial.print("minSpareMicros: ");
|
||||
Serial.println(minSpareMicros);
|
||||
file.close();
|
||||
}
|
||||
void clearSerialInput() {
|
||||
for (uint32_t m = micros(); micros() - m < 10000;) {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
}
|
||||
}
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
// Go faster or log more channels. ADC quality will suffer.
|
||||
// analogReadAveraging(1);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
clearSerialInput();
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
clearSerialInput();
|
||||
logData();
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Simple test of Unicode filename.
|
||||
// Unicode is supported as UTF-8 encoded strings.
|
||||
#include "SdFat.h"
|
||||
|
||||
// USE_UTF8_LONG_NAMES must be non-zero in SdFat/src/SdFatCongfig.h
|
||||
#if USE_UTF8_LONG_NAMES
|
||||
|
||||
#define UTF8_FOLDER u8"😀"
|
||||
const char* names[] = {u8"россиянин", u8"très élégant", u8"狗.txt", nullptr};
|
||||
|
||||
// Remove files if non-zero.
|
||||
#define REMOVE_UTF8_FILES 1
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 0
|
||||
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16))
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(16))
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println("Type any character to begin");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
if (!sd.exists(UTF8_FOLDER)) {
|
||||
if (!sd.mkdir(UTF8_FOLDER)) {
|
||||
Serial.println("sd.mkdir failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!sd.chdir(UTF8_FOLDER)) {
|
||||
Serial.println("sd.chdir failed");
|
||||
return;
|
||||
}
|
||||
for (uint8_t i = 0; names[i]; i++) {
|
||||
if (!file.open(names[i], O_WRONLY | O_CREAT)) {
|
||||
Serial.println("file.open failed");
|
||||
return;
|
||||
}
|
||||
file.println(names[i]);
|
||||
file.close();
|
||||
}
|
||||
Serial.println("ls:");
|
||||
sd.ls("/", LS_SIZE | LS_R);
|
||||
#if REMOVE_UTF8_FILES // For debug test of remove and rmdir.
|
||||
for (uint8_t i = 0; names[i]; i++) {
|
||||
sd.remove(names[i]);
|
||||
}
|
||||
sd.chdir();
|
||||
sd.rmdir(UTF8_FOLDER);
|
||||
Serial.println("After remove and rmdir");
|
||||
sd.ls(LS_SIZE | LS_R);
|
||||
#endif // REMOVE_UTF8_FILES
|
||||
Serial.println("Done!");
|
||||
}
|
||||
void loop() {}
|
||||
#else // USE_UTF8_LONG_NAMES
|
||||
#error USE_UTF8_LONG_NAMES must be non-zero in SdFat/src/SdFatCongfig.h
|
||||
#endif // USE_UTF8_LONG_NAMES
|
||||
@@ -0,0 +1,47 @@
|
||||
// An example of an external chip select functions.
|
||||
// Useful for port expanders or replacement of the standard GPIO functions.
|
||||
//
|
||||
#include "SdFat.h"
|
||||
|
||||
// SD_CHIP_SELECT_MODE must be set to one or two in SdFat/SdFatConfig.h.
|
||||
// A value of one allows optional replacement and two requires replacement.
|
||||
#if SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
|
||||
|
||||
// SD chip select pin.
|
||||
#define SD_CS_PIN SS
|
||||
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50))
|
||||
|
||||
SdFat sd;
|
||||
|
||||
// Stats to verify function calls.
|
||||
uint32_t initCalls = 0;
|
||||
uint32_t writeCalls = 0;
|
||||
//------------------------------------------------------------------------------
|
||||
// Modify these functions for your port expander or custom GPIO library.
|
||||
void sdCsInit(SdCsPin_t pin) {
|
||||
initCalls++;
|
||||
pinMode(pin, OUTPUT);
|
||||
}
|
||||
void sdCsWrite(SdCsPin_t pin, bool level) {
|
||||
writeCalls++;
|
||||
digitalWrite(pin, level);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
sd.ls(&Serial, LS_SIZE);
|
||||
|
||||
Serial.print(F("sdCsInit calls: "));
|
||||
Serial.println(initCalls);
|
||||
Serial.print(F("sdCsWrite calls: "));
|
||||
Serial.println(writeCalls);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
#else // SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
|
||||
#error SD_CHIP_SELECT_MODE must be one or two in SdFat/SdFatConfig.h
|
||||
#endif // SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
|
||||
75
libraries/SdFat/examples/UserSPIDriver/UserSPIDriver.ino
Normal file
75
libraries/SdFat/examples/UserSPIDriver/UserSPIDriver.ino
Normal file
@@ -0,0 +1,75 @@
|
||||
// An example of an external SPI driver.
|
||||
//
|
||||
#include "SPI.h" // Only required if you use features in the SPI library.
|
||||
#include "SdFat.h"
|
||||
|
||||
#if SPI_DRIVER_SELECT == 3 // Must be set in SdFat/SdFatConfig.h
|
||||
|
||||
// SD chip select pin.
|
||||
#define SD_CS_PIN SS
|
||||
|
||||
// This is a simple driver based on the the standard SPI.h library.
|
||||
// You can write a driver entirely independent of SPI.h.
|
||||
// It can be optimized for your board or a different SPI port can be used.
|
||||
// The driver must be derived from SdSpiBaseClass.
|
||||
// See: SdFat/src/SpiDriver/SdSpiBaseClass.h
|
||||
class MySpiClass : public SdSpiBaseClass {
|
||||
public:
|
||||
// Activate SPI hardware with correct speed and mode.
|
||||
void activate() { SPI.beginTransaction(m_spiSettings); }
|
||||
// Initialize the SPI bus.
|
||||
void begin(SdSpiConfig config) {
|
||||
(void)config;
|
||||
SPI.begin();
|
||||
}
|
||||
// Deactivate SPI hardware.
|
||||
void deactivate() { SPI.endTransaction(); }
|
||||
// Receive a byte.
|
||||
uint8_t receive() { return SPI.transfer(0XFF); }
|
||||
// Receive multiple bytes.
|
||||
// Replace this function if your board has multiple byte receive.
|
||||
uint8_t receive(uint8_t* buf, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
buf[i] = SPI.transfer(0XFF);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// Send a byte.
|
||||
void send(uint8_t data) { SPI.transfer(data); }
|
||||
// Send multiple bytes.
|
||||
// Replace this function if your board has multiple byte send.
|
||||
void send(const uint8_t* buf, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
SPI.transfer(buf[i]);
|
||||
}
|
||||
}
|
||||
// Save SPISettings for new max SCK frequency
|
||||
void setSckSpeed(uint32_t maxSck) {
|
||||
m_spiSettings = SPISettings(maxSck, MSBFIRST, SPI_MODE0);
|
||||
}
|
||||
|
||||
private:
|
||||
SPISettings m_spiSettings;
|
||||
} mySpi;
|
||||
#if ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50), &mySpi)
|
||||
#else // ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50), &mySpi)
|
||||
#endif // ENABLE_DEDICATED_SPI
|
||||
SdFat sd;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
sd.ls(&Serial, LS_SIZE);
|
||||
Serial.println("Done");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
#else // SPI_DRIVER_SELECT
|
||||
#error SPI_DRIVER_SELECT must be three in SdFat/SdFatConfig.h
|
||||
#endif // SPI_DRIVER_SELECT
|
||||
|
||||
273
libraries/SdFat/examples/bench/bench.ino
Normal file
273
libraries/SdFat/examples/bench/bench.ino
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* This program is a simple binary write/read benchmark.
|
||||
*/
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
#define SD_FAT_TYPE 3
|
||||
/*
|
||||
Change the value of SD_CS_PIN if you are using SPI and
|
||||
your hardware does not use the default value, SS.
|
||||
Common values are:
|
||||
Arduino Ethernet shield: pin 4
|
||||
Sparkfun SD shield: pin 8
|
||||
Adafruit SD shields and modules: pin 10
|
||||
*/
|
||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
||||
#ifndef SDCARD_SS_PIN
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
#else // SDCARD_SS_PIN
|
||||
// Assume built-in SD is used.
|
||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
||||
#endif // SDCARD_SS_PIN
|
||||
|
||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
||||
|
||||
// Try to select the best SD card configuration.
|
||||
#if HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#elif ENABLE_DEDICATED_SPI
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
||||
#else // HAS_SDIO_CLASS
|
||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
||||
#endif // HAS_SDIO_CLASS
|
||||
|
||||
// Set PRE_ALLOCATE true to pre-allocate file clusters.
|
||||
const bool PRE_ALLOCATE = true;
|
||||
|
||||
// Set SKIP_FIRST_LATENCY true if the first read/write to the SD can
|
||||
// be avoid by writing a file header or reading the first record.
|
||||
const bool SKIP_FIRST_LATENCY = true;
|
||||
|
||||
// Size of read/write.
|
||||
const size_t BUF_SIZE = 512;
|
||||
|
||||
// File size in MB where MB = 1,000,000 bytes.
|
||||
const uint32_t FILE_SIZE_MB = 5;
|
||||
|
||||
// Write pass count.
|
||||
const uint8_t WRITE_COUNT = 2;
|
||||
|
||||
// Read pass count.
|
||||
const uint8_t READ_COUNT = 2;
|
||||
//==============================================================================
|
||||
// End of configuration constants.
|
||||
//------------------------------------------------------------------------------
|
||||
// File size in bytes.
|
||||
const uint32_t FILE_SIZE = 1000000UL * FILE_SIZE_MB;
|
||||
|
||||
// Insure 4-byte alignment.
|
||||
uint32_t buf32[(BUF_SIZE + 3) / 4];
|
||||
uint8_t* buf = (uint8_t*)buf32;
|
||||
|
||||
#if SD_FAT_TYPE == 0
|
||||
SdFat sd;
|
||||
File file;
|
||||
#elif SD_FAT_TYPE == 1
|
||||
SdFat32 sd;
|
||||
File32 file;
|
||||
#elif SD_FAT_TYPE == 2
|
||||
SdExFat sd;
|
||||
ExFile file;
|
||||
#elif SD_FAT_TYPE == 3
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
#else // SD_FAT_TYPE
|
||||
#error Invalid SD_FAT_TYPE
|
||||
#endif // SD_FAT_TYPE
|
||||
|
||||
// Serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
//------------------------------------------------------------------------------
|
||||
// Store error strings in flash to save RAM.
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void cidDmp() {
|
||||
cid_t cid;
|
||||
if (!sd.card()->readCID(&cid)) {
|
||||
error("readCID failed");
|
||||
}
|
||||
cout << F("\nManufacturer ID: ");
|
||||
cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
|
||||
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
|
||||
cout << F("Product: ");
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
cout << cid.pnm[i];
|
||||
}
|
||||
cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
|
||||
cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
|
||||
cout << F("Manufacturing date: ");
|
||||
cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
|
||||
cout << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
delay(1000);
|
||||
cout << F("\nUse a freshly formatted SD for best performance.\n");
|
||||
if (!ENABLE_DEDICATED_SPI) {
|
||||
cout << F(
|
||||
"\nSet ENABLE_DEDICATED_SPI nonzero in\n"
|
||||
"SdFatConfig.h for best SPI performance.\n");
|
||||
}
|
||||
// use uppercase in hex and use 0X base prefix
|
||||
cout << uppercase << showbase << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
float s;
|
||||
uint32_t t;
|
||||
uint32_t maxLatency;
|
||||
uint32_t minLatency;
|
||||
uint32_t totalLatency;
|
||||
bool skipLatency;
|
||||
|
||||
// Discard any input.
|
||||
clearSerialInput();
|
||||
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
#if HAS_UNUSED_STACK
|
||||
cout << F("FreeStack: ") << FreeStack() << endl;
|
||||
#endif // HAS_UNUSED_STACK
|
||||
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
if (sd.fatType() == FAT_TYPE_EXFAT) {
|
||||
cout << F("Type is exFAT") << endl;
|
||||
} else {
|
||||
cout << F("Type is FAT") << int(sd.fatType()) << endl;
|
||||
}
|
||||
|
||||
cout << F("Card size: ") << sd.card()->sectorCount() * 512E-9;
|
||||
cout << F(" GB (GB = 1E9 bytes)") << endl;
|
||||
|
||||
cidDmp();
|
||||
|
||||
// open or create file - truncate existing file.
|
||||
if (!file.open("bench.dat", O_RDWR | O_CREAT | O_TRUNC)) {
|
||||
error("open failed");
|
||||
}
|
||||
|
||||
// fill buf with known data
|
||||
if (BUF_SIZE > 1) {
|
||||
for (size_t i = 0; i < (BUF_SIZE - 2); i++) {
|
||||
buf[i] = 'A' + (i % 26);
|
||||
}
|
||||
buf[BUF_SIZE - 2] = '\r';
|
||||
}
|
||||
buf[BUF_SIZE - 1] = '\n';
|
||||
|
||||
cout << F("FILE_SIZE_MB = ") << FILE_SIZE_MB << endl;
|
||||
cout << F("BUF_SIZE = ") << BUF_SIZE << F(" bytes\n");
|
||||
cout << F("Starting write test, please wait.") << endl << endl;
|
||||
|
||||
// do write test
|
||||
uint32_t n = FILE_SIZE / BUF_SIZE;
|
||||
cout << F("write speed and latency") << endl;
|
||||
cout << F("speed,max,min,avg") << endl;
|
||||
cout << F("KB/Sec,usec,usec,usec") << endl;
|
||||
for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
|
||||
file.truncate(0);
|
||||
if (PRE_ALLOCATE) {
|
||||
if (!file.preAllocate(FILE_SIZE)) {
|
||||
error("preAllocate failed");
|
||||
}
|
||||
}
|
||||
maxLatency = 0;
|
||||
minLatency = 9999999;
|
||||
totalLatency = 0;
|
||||
skipLatency = SKIP_FIRST_LATENCY;
|
||||
t = millis();
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
uint32_t m = micros();
|
||||
if (file.write(buf, BUF_SIZE) != BUF_SIZE) {
|
||||
error("write failed");
|
||||
}
|
||||
m = micros() - m;
|
||||
totalLatency += m;
|
||||
if (skipLatency) {
|
||||
// Wait until first write to SD, not just a copy to the cache.
|
||||
skipLatency = file.curPosition() < 512;
|
||||
} else {
|
||||
if (maxLatency < m) {
|
||||
maxLatency = m;
|
||||
}
|
||||
if (minLatency > m) {
|
||||
minLatency = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
file.sync();
|
||||
t = millis() - t;
|
||||
s = file.fileSize();
|
||||
cout << s / t << ',' << maxLatency << ',' << minLatency;
|
||||
cout << ',' << totalLatency / n << endl;
|
||||
}
|
||||
cout << endl << F("Starting read test, please wait.") << endl;
|
||||
cout << endl << F("read speed and latency") << endl;
|
||||
cout << F("speed,max,min,avg") << endl;
|
||||
cout << F("KB/Sec,usec,usec,usec") << endl;
|
||||
|
||||
// do read test
|
||||
for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) {
|
||||
file.rewind();
|
||||
maxLatency = 0;
|
||||
minLatency = 9999999;
|
||||
totalLatency = 0;
|
||||
skipLatency = SKIP_FIRST_LATENCY;
|
||||
t = millis();
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
buf[BUF_SIZE - 1] = 0;
|
||||
uint32_t m = micros();
|
||||
int32_t nr = file.read(buf, BUF_SIZE);
|
||||
if (nr != BUF_SIZE) {
|
||||
error("read failed");
|
||||
}
|
||||
m = micros() - m;
|
||||
totalLatency += m;
|
||||
if (buf[BUF_SIZE - 1] != '\n') {
|
||||
error("data check error");
|
||||
}
|
||||
if (skipLatency) {
|
||||
skipLatency = false;
|
||||
} else {
|
||||
if (maxLatency < m) {
|
||||
maxLatency = m;
|
||||
}
|
||||
if (minLatency > m) {
|
||||
minLatency = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
s = file.fileSize();
|
||||
t = millis() - t;
|
||||
cout << s / t << ',' << maxLatency << ',' << minLatency;
|
||||
cout << ',' << totalLatency / n << endl;
|
||||
}
|
||||
cout << endl << F("Done") << endl;
|
||||
file.close();
|
||||
sd.end();
|
||||
}
|
||||
119
libraries/SdFat/examples/debug/CardBusyTest/CardBusyTest.ino
Normal file
119
libraries/SdFat/examples/debug/CardBusyTest/CardBusyTest.ino
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "SdFat.h"
|
||||
|
||||
#ifdef __AVR__
|
||||
const uint32_t FILE_SIZE_MiB = 10UL;
|
||||
#else // __AVR__
|
||||
const uint32_t FILE_SIZE_MiB = 100UL;
|
||||
#endif
|
||||
|
||||
bool waitBusy = true;
|
||||
|
||||
#define SD_CONFIG SdSpiConfig(SS, DEDICATED_SPI)
|
||||
//#define SD_CONFIG SdSpiConfig(SS, SHARED_SPI)
|
||||
// Config for Teensy 3.5/3.6 buit-in SD.
|
||||
//#define SD_CONFIG SdSpiConfig(SDCARD_SS_PIN, DEDICATED_SPI)
|
||||
//#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const uint64_t FILE_SIZE = (uint64_t)FILE_SIZE_MiB << 20;
|
||||
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
|
||||
uint8_t buf[512];
|
||||
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void clearSerialInput() {
|
||||
uint32_t m = micros();
|
||||
do {
|
||||
if (Serial.read() >= 0) {
|
||||
m = micros();
|
||||
}
|
||||
} while (micros() - m < 10000);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
delay(1000);
|
||||
//------------------------------------------------------------------------------
|
||||
}
|
||||
void loop() {
|
||||
clearSerialInput();
|
||||
Serial.println(F("\nType any character to start\n"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize the SD card.
|
||||
if (!sd.begin(SD_CONFIG)) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
if (!file.open("SdBusyTest.bin", O_RDWR | O_CREAT |O_TRUNC)) {
|
||||
error("file open failed");
|
||||
}
|
||||
if (!file.preAllocate(FILE_SIZE)) {
|
||||
error("preallocate failed");
|
||||
}
|
||||
Serial.print(F("Starting write of "));
|
||||
Serial.print(FILE_SIZE_MiB);
|
||||
Serial.println(F(" MiB."));
|
||||
uint32_t maxWrite = 0;
|
||||
uint32_t minWrite = 99999999;
|
||||
uint32_t ms = millis();
|
||||
uint32_t maxBusy = 0;
|
||||
uint32_t minBusy = UINT32_MAX;
|
||||
|
||||
// Write a dummy sector to start a multi-sector write.
|
||||
if(file.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
error("write failed for first sector");
|
||||
}
|
||||
|
||||
while (file.position() < FILE_SIZE) {
|
||||
uint32_t m = micros();
|
||||
if (waitBusy) {
|
||||
m = micros();
|
||||
while (sd.card()->isBusy()) {}
|
||||
m = micros() - m;
|
||||
if (m < minBusy) {
|
||||
minBusy = m;
|
||||
}
|
||||
if (m > maxBusy) {
|
||||
maxBusy = m;
|
||||
}
|
||||
}
|
||||
m = micros();
|
||||
if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
error("write failed");
|
||||
}
|
||||
m = micros() - m;
|
||||
if (m < minWrite) {
|
||||
minWrite = m;
|
||||
}
|
||||
if (m > maxWrite) {
|
||||
maxWrite = m;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
ms = millis() - ms;
|
||||
Serial.println(F("\nTimes in micros"));
|
||||
if (waitBusy) {
|
||||
Serial.print(F("minBusy: "));
|
||||
Serial.println(minBusy);
|
||||
Serial.print(F("maxBusy: "));
|
||||
Serial.println(maxBusy);
|
||||
}
|
||||
Serial.print(F("minWrite: "));
|
||||
Serial.println(minWrite);
|
||||
Serial.print(F("maxWrite: "));
|
||||
Serial.println(maxWrite);
|
||||
Serial.print(1e-3*ms);
|
||||
Serial.println(F(" Seconds"));
|
||||
Serial.print(1.0*FILE_SIZE/ms);
|
||||
Serial.println(F(" KB/sec"));
|
||||
|
||||
}
|
||||
51
libraries/SdFat/examples/debug/ExFatDbgDmp/ExFatDbgDmp.ino
Normal file
51
libraries/SdFat/examples/debug/ExFatDbgDmp/ExFatDbgDmp.ino
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "SdFat.h"
|
||||
#define DUMP_RAW 0
|
||||
#define DUMP_UPCASE 0
|
||||
const uint8_t CS_PIN = SS;
|
||||
//#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
||||
#define SD_CONFIG SdSpiConfig(CS_PIN)
|
||||
|
||||
SdExFat sd;
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println(F("Type any character to begin"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (!sd.begin(SD_CONFIG)){
|
||||
error("begin failed");
|
||||
}
|
||||
#if DUMP_RAW
|
||||
sd.dmpSector(&Serial, 0);
|
||||
for (uint8_t i = 0; i < 24; i++) {
|
||||
sd.dmpSector(&Serial, 0X8000 + i);
|
||||
Serial.println();
|
||||
}
|
||||
return;
|
||||
#endif // DUMP_RAW
|
||||
ExFatFile root;
|
||||
if (!root.openRoot(&sd)) {
|
||||
error("openRoot failed");
|
||||
}
|
||||
sd.printDir(&Serial, &root);
|
||||
// startSector = 0, sectorCount = 1.
|
||||
sd.dmpFat(&Serial, 0, 1);
|
||||
sd.dmpBitmap(&Serial);
|
||||
sd.printVolInfo(&Serial);
|
||||
|
||||
sd.checkUpcase(&Serial);
|
||||
#if DUMP_UPCASE
|
||||
sd.printUpcase(&Serial);
|
||||
#endif // DUMP_UPCASE
|
||||
// sd.dmpCluster(&Serial, 8, 0, 4);
|
||||
Serial.println("Done");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
|
||||
}
|
||||
43
libraries/SdFat/examples/debug/TestGetName/TestGetName.ino
Normal file
43
libraries/SdFat/examples/debug/TestGetName/TestGetName.ino
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "SdFat.h"
|
||||
SdFs sd;
|
||||
FsFile file;
|
||||
const char* name[] = {
|
||||
"SFN.TXT",
|
||||
"LongFilename.txt",
|
||||
#if USE_UTF8_LONG_NAMES
|
||||
u8"très élégant.txt",
|
||||
#endif // USE_UTF8_LONG_NAMES
|
||||
nullptr};
|
||||
char buf[32];
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {}
|
||||
Serial.println("Type any character to begin");
|
||||
while (!Serial.available()) {}
|
||||
if (!sd.begin(SS)) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
for (uint8_t i = 0; name[i]; i++) {
|
||||
if (!file.open(name[i], O_CREAT |O_RDWR)) {
|
||||
sd.errorHalt("open");
|
||||
}
|
||||
size_t len = strlen(name[i]);
|
||||
size_t rtn = file.getName(buf, len);
|
||||
if (rtn != 0) {
|
||||
Serial.println("fail len");
|
||||
}
|
||||
rtn = file.getName(buf, len + 1);
|
||||
if (rtn != len) {
|
||||
Serial.println("fail len + 1");
|
||||
}
|
||||
Serial.print(rtn);
|
||||
Serial.print(' ');
|
||||
Serial.println(buf);
|
||||
if (!file.remove()) {
|
||||
sd.errorHalt("remove");
|
||||
}
|
||||
}
|
||||
Serial.println("Done");
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
140
libraries/SdFat/examples/debug/TestMkdir/TestMkdir.ino
Normal file
140
libraries/SdFat/examples/debug/TestMkdir/TestMkdir.ino
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
const uint8_t SD_CHIP_SELECT = SS;
|
||||
|
||||
SdFat sd;
|
||||
typedef File file_t;
|
||||
|
||||
// store error strings in flash to save RAM
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
|
||||
/*
|
||||
* create enough files to force a cluster to be allocated to dir.
|
||||
*/
|
||||
void dirAllocTest(file_t* dir) {
|
||||
char buf[32], name[32];
|
||||
file_t file;
|
||||
uint16_t n;
|
||||
uint32_t size = dir->dirSize();
|
||||
|
||||
// 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_WRONLY | 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();
|
||||
Serial.print(F("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->curPosition() > 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_RDONLY)) {
|
||||
error("open for read failed");
|
||||
}
|
||||
|
||||
// open end time and read start time
|
||||
uint32_t t1 = millis();
|
||||
int16_t nr = file.read(buf, sizeof(buf));
|
||||
if (nr < 5) error("file.read failed");
|
||||
|
||||
// read end time
|
||||
uint32_t t2 = millis();
|
||||
|
||||
// check file content
|
||||
if (strlen(name) != (size_t)nr || strncmp(name, buf, nr)) {
|
||||
error("content compare failed");
|
||||
}
|
||||
if (!file.close()) error("close read failed");
|
||||
|
||||
Serial.print(F("RD "));
|
||||
Serial.print(i);
|
||||
Serial.write(' ');
|
||||
|
||||
// print open time
|
||||
Serial.print(t1 - t0);
|
||||
Serial.write(' ');
|
||||
|
||||
// print read time
|
||||
Serial.println(t2 - t1);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
file_t root;
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {} // wait for Leonardo
|
||||
Serial.println(F("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 lower speed if bus errors occur.
|
||||
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
root.openRoot(&sd);
|
||||
uint32_t m = millis();
|
||||
// write files to root if not FAT16
|
||||
if (sd.fatType() != 16) {
|
||||
Serial.println(F("Writing files to root"));
|
||||
dirAllocTest(&root);
|
||||
}
|
||||
|
||||
// create sub1 and write files
|
||||
file_t sub1;
|
||||
if (!sub1.mkdir(&root, "SUB1")) error("makdeDir SUB1 failed");
|
||||
Serial.println(F("Writing files to SUB1"));
|
||||
dirAllocTest(&sub1);
|
||||
|
||||
// create sub2 and write files
|
||||
file_t sub2;
|
||||
if (!sub2.mkdir(&sub1, "SUB2")) error("mkdir SUB2 failed");
|
||||
Serial.println(F("Writing files to SUB2"));
|
||||
dirAllocTest(&sub2);
|
||||
m = millis() - m;
|
||||
Serial.print(F("Done millis: "));
|
||||
Serial.println(m);
|
||||
}
|
||||
|
||||
void loop() { }
|
||||
99
libraries/SdFat/examples/debug/TestRmdir/TestRmdir.ino
Normal file
99
libraries/SdFat/examples/debug/TestRmdir/TestRmdir.ino
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
const uint8_t SD_CHIP_SELECT = SS;
|
||||
|
||||
SdFat sd;
|
||||
typedef File file_t;
|
||||
|
||||
// store error strings in flash to save RAM
|
||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
||||
|
||||
/*
|
||||
* remove all files in dir.
|
||||
*/
|
||||
void deleteFiles(FatFile* dir) {
|
||||
char name[32];
|
||||
file_t 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_WRONLY)) 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();
|
||||
|
||||
Serial.print(F("RM "));
|
||||
Serial.print(n);
|
||||
Serial.write(' ');
|
||||
|
||||
// open time
|
||||
Serial.print(t1 - t0);
|
||||
Serial.write(' ');
|
||||
|
||||
// remove time
|
||||
Serial.println(t2 - t1);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
file_t root;
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {} // wait for Leonardo
|
||||
Serial.println(F("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 lower speed if bus errors occur.
|
||||
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) {
|
||||
sd.initErrorHalt(&Serial);
|
||||
}
|
||||
root.openRoot(&sd);
|
||||
// delete files in root if not FAT16.
|
||||
if (sd.fatType() != 16) {
|
||||
Serial.println(F("Remove files in root"));
|
||||
deleteFiles(&root);
|
||||
}
|
||||
|
||||
// open SUB1 and delete files
|
||||
file_t sub1;
|
||||
if (!sub1.open("SUB1", O_RDONLY)) error("open SUB1 failed");
|
||||
Serial.println(F("Remove files in SUB1"));
|
||||
deleteFiles(&sub1);
|
||||
|
||||
// open SUB2 and delete files
|
||||
file_t sub2;
|
||||
if (!sub2.open(&sub1, "SUB2", O_RDONLY)) error("open SUB2 failed");
|
||||
Serial.println(F("Remove files in SUB2"));
|
||||
deleteFiles(&sub2);
|
||||
|
||||
// remove SUB2
|
||||
if (!sub2.rmdir()) error("sub2.rmdir failed");
|
||||
Serial.println(F("SUB2 removed"));
|
||||
|
||||
// remove SUB1
|
||||
if (!sub1.rmdir()) error("sub1.rmdir failed");
|
||||
Serial.println(F("SUB1 removed"));
|
||||
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
|
||||
void loop() { }
|
||||
162
libraries/SdFat/examples/debug/TimestampTest/TimestampTest.ino
Normal file
162
libraries/SdFat/examples/debug/TimestampTest/TimestampTest.ino
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* This program tests the dateTimeCallback() function
|
||||
* and the timestamp() function.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
SdFs sd;
|
||||
|
||||
FsFile 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(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
* date/time values for debug
|
||||
* normally supplied by a real-time clock or GPS
|
||||
*/
|
||||
// date 1-Oct-21
|
||||
uint16_t year = 2021;
|
||||
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(FsFile& f) {
|
||||
cout << F("Creation: ");
|
||||
f.printCreateDateTime(&Serial);
|
||||
cout << endl << F("Modify: ");
|
||||
f.printModifyDateTime(&Serial);
|
||||
cout << endl << F("Access: ");
|
||||
f.printAccessDateTime(&Serial);
|
||||
cout << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// remove 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_WRONLY | O_CREAT)) {
|
||||
error("open default.txt failed");
|
||||
}
|
||||
cout << F("\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_WRONLY | O_CREAT)) {
|
||||
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 << F("\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_WRONLY | O_CREAT)) {
|
||||
error("open stamp.txt failed");
|
||||
}
|
||||
// set creation date time
|
||||
if (!file.timestamp(T_CREATE, 2021, 11, 10, 1, 2, 3)) {
|
||||
error("set create time failed");
|
||||
}
|
||||
// set write/modification date time
|
||||
if (!file.timestamp(T_WRITE, 2021, 11, 11, 4, 5, 6)) {
|
||||
error("set write time failed");
|
||||
}
|
||||
// set access date
|
||||
if (!file.timestamp(T_ACCESS, 2021, 11, 12, 7, 8, 9)) {
|
||||
error("set access time failed");
|
||||
}
|
||||
cout << F("\nTimes after timestamp() calls\n");
|
||||
printTimestamps(file);
|
||||
|
||||
file.close();
|
||||
cout << F("\nDone\n");
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
@@ -0,0 +1,197 @@
|
||||
// A simple data logger for the Arduino analog pins with optional DS1307
|
||||
// uses RTClib from https://github.com/adafruit/RTClib
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
#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(F(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);
|
||||
|
||||
// Wait for USB Serial.
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << endl << F("FreeStack: ") << FreeStack() << endl;
|
||||
|
||||
#if WAIT_TO_START
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Discard input.
|
||||
do {
|
||||
delay(10);
|
||||
} while(Serial.available() && Serial.read() >= 0);
|
||||
#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 at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) {
|
||||
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 << F("Logging to: ") << name << endl;
|
||||
cout << F("Type any character to stop\n\n");
|
||||
|
||||
// format header in buffer
|
||||
obufstream bout(buf, sizeof(buf));
|
||||
|
||||
bout << F("millis");
|
||||
|
||||
#if USE_DS1307
|
||||
bout << F(",date,time");
|
||||
#endif // USE_DS1307
|
||||
|
||||
for (uint8_t i = 0; i < SENSOR_COUNT; i++) {
|
||||
bout << F(",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 << F("Done!");
|
||||
while (true) {}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Program to test Short File Name character case flags.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
const uint8_t chipSelect = SS;
|
||||
|
||||
SdFat sd;
|
||||
|
||||
SdFile file;
|
||||
const char* name[] = {
|
||||
"low.low", "low.Mix", "low.UP",
|
||||
"Mix.low", "Mix.Mix", "Mix.UP",
|
||||
"UP.low", "UP.Mix", "UP.UP"
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println("type any character to start");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
Serial.println("begin failed");
|
||||
return;
|
||||
}
|
||||
for (uint8_t i = 0; i < 9; i++) {
|
||||
sd.remove(name[i]);
|
||||
if (!file.open(name[i], O_RDWR | O_CREAT | O_EXCL)) {
|
||||
sd.errorHalt(name[i]);
|
||||
}
|
||||
file.println(name[i]);
|
||||
|
||||
file.close();
|
||||
}
|
||||
sd.ls(LS_DATE|LS_SIZE);
|
||||
Serial.println("Done");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// create a serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
delay(2000);
|
||||
|
||||
cout << "Hello, World!\n";
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
#ifdef UDR0 // Must be AVR with serial port zero.
|
||||
#include "MinimumSerial.h"
|
||||
|
||||
MinimumSerial MiniSerial;
|
||||
|
||||
void setup() {
|
||||
MiniSerial.begin(9600);
|
||||
MiniSerial.println(FreeStack());
|
||||
}
|
||||
void loop() {
|
||||
int c;
|
||||
MiniSerial.println(F("Type any Character"));
|
||||
while ((c = MiniSerial.read()) < 0) {}
|
||||
MiniSerial.print(F("Read: "));
|
||||
MiniSerial.println((char)c);
|
||||
while (MiniSerial.read() >= 0) {}
|
||||
}
|
||||
#else // UDR0
|
||||
#error no AVR serial port 0
|
||||
#endif // UDR0
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* This program is a simple Print benchmark.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include <SD.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(const char* s) {
|
||||
Serial.println(s);
|
||||
while (1) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
uint32_t maxLatency;
|
||||
uint32_t minLatency;
|
||||
uint32_t totalLatency;
|
||||
|
||||
// Read any existing Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
// F() stores strings in flash to save RAM
|
||||
Serial.println(F("Type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// 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.getWriteError()) {
|
||||
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,30 @@
|
||||
/*
|
||||
* Program to compare size of Arduino SD library with SdFat.
|
||||
* See SdFatSize.ino for SdFat program.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
|
||||
File file;
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
|
||||
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,33 @@
|
||||
/*
|
||||
* Program to compare size of SdFat with Arduino SD library.
|
||||
* See SD_Size.ino for Arduino SD program.
|
||||
*
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
SdFat sd;
|
||||
|
||||
SdFile file;
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
|
||||
if (!sd.begin()) {
|
||||
Serial.println("begin failed");
|
||||
return;
|
||||
}
|
||||
file.open("SizeTest.txt", O_RDWR | O_CREAT | O_AT_END);
|
||||
|
||||
file.println("Hello");
|
||||
|
||||
file.close();
|
||||
Serial.println("Done");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Simple demo of the Stream parsInt() member function.
|
||||
#include <SPI.h>
|
||||
// The next two lines replace #include <SD.h>.
|
||||
#include "SdFat.h"
|
||||
SdFat SD;
|
||||
|
||||
// SD card chip select pin - Modify the value of csPin for your SD module.
|
||||
const uint8_t csPin = SS;
|
||||
|
||||
File file;
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial.
|
||||
while(!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println(F("Type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize the SD.
|
||||
if (!SD.begin(csPin)) {
|
||||
Serial.println(F("begin error"));
|
||||
return;
|
||||
}
|
||||
// Create and open the file. Use flag to truncate an existing file.
|
||||
file = SD.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC);
|
||||
if (!file) {
|
||||
Serial.println(F("open error"));
|
||||
return;
|
||||
}
|
||||
// Write a test number to the file.
|
||||
file.println("12345");
|
||||
|
||||
// Rewind the file and read the number with parseInt().
|
||||
file.seek(0);
|
||||
int i = file.parseInt();
|
||||
Serial.print(F("parseInt: "));
|
||||
Serial.println(i);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
77
libraries/SdFat/examples/examplesV1/#attic/append/append.ino
Normal file
77
libraries/SdFat/examples/examplesV1/#attic/append/append.ino
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Append Example
|
||||
*
|
||||
* This program shows how to use open for append.
|
||||
* The program will append 100 line each time it opens the file.
|
||||
* The program will open and close the file 100 times.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
// filename for this example
|
||||
char name[] = "append.txt";
|
||||
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << endl << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
cout << F("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,82 @@
|
||||
/*
|
||||
* Calculate the sum and average of a list of floating point numbers
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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("AvgTest.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("AvgTest.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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// write the test file
|
||||
writeTestFile();
|
||||
|
||||
// read the test file and calculate the average
|
||||
calcAverage();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
149
libraries/SdFat/examples/examplesV1/#attic/benchSD/benchSD.ino
Normal file
149
libraries/SdFat/examples/examplesV1/#attic/benchSD/benchSD.ino
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This program 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(const char* s) {
|
||||
Serial.println(s);
|
||||
while (1) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
uint32_t maxLatency;
|
||||
uint32_t minLatency;
|
||||
uint32_t totalLatency;
|
||||
|
||||
// Discard any input.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
// F() stores strings in flash to save RAM
|
||||
Serial.println(F("Type any character to start"));
|
||||
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (!SD.begin(chipSelect)) {
|
||||
error("begin");
|
||||
}
|
||||
|
||||
// open or create file - truncate existing file.
|
||||
file = SD.open("Bench.dat", O_RDWR | O_TRUNC | O_CREAT);
|
||||
if (!file) {
|
||||
error("open failed");
|
||||
}
|
||||
|
||||
// fill buf with known data
|
||||
for (size_t_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,39 @@
|
||||
/*
|
||||
* Use of ibufsteam to parse a line and obufstream to format a line
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
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,39 @@
|
||||
/*
|
||||
* Demo of ArduinoInStream and ArduinoOutStream
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// create serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
// input line buffer
|
||||
char cinBuf[40];
|
||||
|
||||
// create serial input stream
|
||||
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
int32_t n = 0;
|
||||
|
||||
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,62 @@
|
||||
/*
|
||||
* Append a line to a file - demo of pathnames and streams
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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/2014/Jan");
|
||||
|
||||
// create or open a file for append
|
||||
ofstream sdlog("logs/2014/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);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
delay(400); // catch Due reset problem
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// append a line to the logfile
|
||||
logEvent("Another line for the logfile");
|
||||
|
||||
cout << F("Done - check /logs/2014/Jan/logfile.txt on the SD") << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
@@ -0,0 +1,111 @@
|
||||
// Demo of rewriting a line read by fgets
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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(F(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 << F("-----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 << F("\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_WRONLY | O_CREAT | O_TRUNC);
|
||||
|
||||
// check for open error
|
||||
if (!wrfile.isOpen()) {
|
||||
error("MakeTestFile");
|
||||
}
|
||||
|
||||
// write test file
|
||||
wrfile.print(F(
|
||||
"Line A\r\n"
|
||||
"Line B\r\n"
|
||||
"Line C\r\n"
|
||||
"Line D\r\n"
|
||||
"Line E\r\n"
|
||||
));
|
||||
wrfile.close();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
makeTestFile();
|
||||
|
||||
demoFgets();
|
||||
|
||||
cout << F("\nDone\n");
|
||||
}
|
||||
void loop() {}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Read the logfile created by the eventlog.ino example.
|
||||
* Demo of pathnames and working directories
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// set current working directory
|
||||
if (!sd.chdir("logs/2014/Jan/")) {
|
||||
sd.errorHalt("chdir failed. Did you run eventlog.ino?");
|
||||
}
|
||||
// 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() {}
|
||||
34
libraries/SdFat/examples/examplesV1/#attic/readme.txt
Normal file
34
libraries/SdFat/examples/examplesV1/#attic/readme.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
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.
|
||||
|
||||
BaseExtCaseTest - Long file name test.
|
||||
|
||||
benchSD - A read/write benchmark for the standard Arduino SD.h library.
|
||||
|
||||
bufstream - ibufsteam to parse a line and obufstream to format a line.
|
||||
|
||||
cin_cout - Demo of ArduinoInStream and ArduinoOutStream.
|
||||
|
||||
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.
|
||||
|
||||
StreamParseInt - Simple demo of the Stream parsInt() member function.
|
||||
@@ -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 values
|
||||
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 values
|
||||
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 values
|
||||
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,827 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
#ifdef __AVR__
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#include "FreeStack.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 = PIN_COUNT > 1 ? 100 : 0;
|
||||
|
||||
// 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) {sd.errorPrint(F(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 - ISR_SETUP_ADC;
|
||||
|
||||
for (adps = 7; adps > 0; adps--) {
|
||||
if (adcCycles >= (MIN_ADC_CYCLES << adps)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // ADC_PRESCALER
|
||||
meta->adcFrequency = F_CPU >> adps;
|
||||
if (meta->adcFrequency > (RECORD_EIGHT_BITS ? 2000000 : 1000000)) {
|
||||
error("Sample Rate Too High");
|
||||
}
|
||||
#if ROUND_SAMPLE_INTERVAL
|
||||
// Round so interval is multiple of ADC clock.
|
||||
ticks += 1 << (adps - 1);
|
||||
ticks >>= adps;
|
||||
ticks <<= adps;
|
||||
#endif // ROUND_SAMPLE_INTERVAL
|
||||
|
||||
if (PIN_COUNT > 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 (uint8_t i = 0; i < meta->pinCount; i++) {
|
||||
Serial.print(' ');
|
||||
Serial.print(meta->pinNumber[i], DEC);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.print(F("ADC bits: "));
|
||||
Serial.println(meta->recordEightBits ? 8 : 10);
|
||||
Serial.print(F("ADC clock kHz: "));
|
||||
Serial.println(meta->adcFrequency/1000);
|
||||
Serial.print(F("Sample Rate: "));
|
||||
Serial.println(sampleRate);
|
||||
Serial.print(F("Sample interval usec: "));
|
||||
Serial.println(1000000.0/sampleRate, 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(&csvName[BASE_NAME_SIZE + 3], "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) {
|
||||
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(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)) {
|
||||
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(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("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorPrint();
|
||||
fatalBlink();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {
|
||||
// Read any Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && 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()) {
|
||||
yield();
|
||||
}
|
||||
char c = tolower(Serial.read());
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
}
|
||||
// Read any Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
if (c == 'c') {
|
||||
binaryToCsv();
|
||||
} else if (c == 'd') {
|
||||
dumpData();
|
||||
} else if (c == 'e') {
|
||||
checkOverrun();
|
||||
} else if (c == 'r') {
|
||||
logData();
|
||||
} else {
|
||||
Serial.println(F("Invalid entry"));
|
||||
}
|
||||
}
|
||||
#else // __AVR__
|
||||
#error This program is only for AVR.
|
||||
#endif // __AVR__
|
||||
@@ -0,0 +1,102 @@
|
||||
// Example use of lfnOpenNext and open by index.
|
||||
// You can use test files located in
|
||||
// SdFat/examples/LongFileName/testFiles.
|
||||
#include<SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
// SD card chip select pin.
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
|
||||
SdFat sd;
|
||||
SdFile file;
|
||||
SdFile dirFile;
|
||||
|
||||
// Number of files found.
|
||||
uint16_t n = 0;
|
||||
|
||||
// Max of ten files since files are selected with a single digit.
|
||||
const uint16_t nMax = 10;
|
||||
|
||||
// Position of file's directory entry.
|
||||
uint16_t dirIndex[nMax];
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {}
|
||||
delay(1000);
|
||||
|
||||
// Print the location of some test files.
|
||||
Serial.println(F("\r\n"
|
||||
"You can use test files located in\r\n"
|
||||
"SdFat/examples/LongFileName/testFiles"));
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
Serial.println();
|
||||
|
||||
// List files in root directory.
|
||||
if (!dirFile.open("/", O_RDONLY)) {
|
||||
sd.errorHalt("open root failed");
|
||||
}
|
||||
while (n < nMax && file.openNext(&dirFile, O_RDONLY)) {
|
||||
|
||||
// Skip directories and hidden files.
|
||||
if (!file.isSubDir() && !file.isHidden()) {
|
||||
|
||||
// Save dirIndex of file in directory.
|
||||
dirIndex[n] = file.dirIndex();
|
||||
|
||||
// Print the file number and name.
|
||||
Serial.print(n++);
|
||||
Serial.write(' ');
|
||||
file.printName(&Serial);
|
||||
Serial.println();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
int c;
|
||||
|
||||
// Read any existing Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
Serial.print(F("\r\nEnter File Number: "));
|
||||
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
c = Serial.read();
|
||||
uint8_t i = c - '0';
|
||||
if (!isdigit(c) || i >= n) {
|
||||
Serial.println(F("Invald number"));
|
||||
return;
|
||||
}
|
||||
Serial.println(i);
|
||||
if (!file.open(&dirFile, dirIndex[i], O_RDONLY)) {
|
||||
sd.errorHalt(F("open"));
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
char last = 0;
|
||||
|
||||
// Copy up to 500 characters to Serial.
|
||||
for (int k = 0; k < 500 && (c = file.read()) > 0; k++) {
|
||||
Serial.write(last = (char)c);
|
||||
}
|
||||
// Add new line if missing from last line.
|
||||
if (last != '\n') {
|
||||
Serial.println();
|
||||
}
|
||||
file.close();
|
||||
Serial.flush();
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
This is "A long name can be 255 characters.txt"
|
||||
This file has a typical Long File Name.
|
||||
|
||||
The maximum length of a Long File Name is 255 characters.
|
||||
@@ -0,0 +1 @@
|
||||
LFN,NAME.TXT is not 8.3 since it has a comma.
|
||||
@@ -0,0 +1,5 @@
|
||||
MIXCASE.txt does not have a Long File Name.
|
||||
|
||||
Starting with NT, file names of this form,
|
||||
have the basename and extension character case
|
||||
encoded in two bits of the 8.3 directory entry.
|
||||
@@ -0,0 +1,2 @@
|
||||
Not_8_3.txt has a Long File Name
|
||||
since the basename is mixed case.
|
||||
@@ -0,0 +1 @@
|
||||
OK%83.TXT is a valid 8.3 name.
|
||||
@@ -0,0 +1 @@
|
||||
STD_8_3.TXT - a vanilla 8.3 name.
|
||||
@@ -0,0 +1,2 @@
|
||||
With Blank.txt
|
||||
Just another example of a Long File Name.
|
||||
@@ -0,0 +1,2 @@
|
||||
"With Two.dots.txt"
|
||||
Lots of reasons this is a Long File Name.
|
||||
@@ -0,0 +1,5 @@
|
||||
lower.txt does not have a Long File Name.
|
||||
|
||||
Starting with NT, file names of this form,
|
||||
have the basename and extension character case
|
||||
encoded in two bits of the 8.3 directory entry.
|
||||
@@ -0,0 +1,5 @@
|
||||
mixed.TXT does not have a Long File Name.
|
||||
|
||||
Starting with NT, file names of this form,
|
||||
have the basename and extension character case
|
||||
encoded in two bits of the 8.3 directory entry.
|
||||
@@ -0,0 +1,655 @@
|
||||
/**
|
||||
* 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 12 512 byte buffers will be used.
|
||||
*
|
||||
* Data is written to the file using a SD multiple block write command.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
#include "UserTypes.h"
|
||||
|
||||
#ifdef __AVR_ATmega328P__
|
||||
#include "MinimumSerial.h"
|
||||
MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
//==============================================================================
|
||||
// Start of configuration constants.
|
||||
//==============================================================================
|
||||
// Abort run on an overrun. Data before the overrun will be saved.
|
||||
#define ABORT_ON_OVERRUN 1
|
||||
//------------------------------------------------------------------------------
|
||||
//Interval between data records in microseconds.
|
||||
const uint32_t LOG_INTERVAL_USEC = 2000;
|
||||
//------------------------------------------------------------------------------
|
||||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor.
|
||||
// May not work for some cards.
|
||||
#ifndef USE_SHARED_SPI
|
||||
#define USE_SHARED_SPI 0
|
||||
#endif // USE_SHARED_SPI
|
||||
//------------------------------------------------------------------------------
|
||||
// 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
|
||||
// overrun errors and logging continues unless ABORT_ON_OVERRUN
|
||||
// is non-zero.
|
||||
#ifdef ERROR_LED_PIN
|
||||
#undef ERROR_LED_PIN
|
||||
#endif // ERROR_LED_PIN
|
||||
const int8_t ERROR_LED_PIN = -1;
|
||||
//------------------------------------------------------------------------------
|
||||
// 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 if not defined in UserTypes.h
|
||||
#ifndef FILE_BASE_NAME
|
||||
#define FILE_BASE_NAME "data"
|
||||
#endif // FILE_BASE_NAME
|
||||
//------------------------------------------------------------------------------
|
||||
// Buffer definitions.
|
||||
//
|
||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional
|
||||
// buffers.
|
||||
//
|
||||
#ifndef RAMEND
|
||||
// Assume ARM. Use total of ten 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 10;
|
||||
//
|
||||
#elif RAMEND < 0X8FF
|
||||
#error Too little SRAM
|
||||
//
|
||||
#elif RAMEND < 0X10FF
|
||||
// Use total of two 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 2;
|
||||
//
|
||||
#elif RAMEND < 0X20FF
|
||||
// Use total of four 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 4;
|
||||
//
|
||||
#else // RAMEND
|
||||
// Use total of 12 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 FILE_BASE_NAME "##.bin"
|
||||
|
||||
// Size of file base name.
|
||||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
|
||||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7;
|
||||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin";
|
||||
|
||||
SdFat sd;
|
||||
|
||||
SdBaseFile binFile;
|
||||
|
||||
// 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];
|
||||
};
|
||||
//==============================================================================
|
||||
// Error messages stored in flash.
|
||||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();}
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
void fatalBlink() {
|
||||
while (true) {
|
||||
yield();
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
delay(200);
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
delay(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// read data file and check for overruns
|
||||
void checkOverrun() {
|
||||
bool headerPrinted = false;
|
||||
block_t block;
|
||||
uint32_t bn = 0;
|
||||
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println();
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
binFile.rewind();
|
||||
Serial.println();
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
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(binFile.firstBlock() + bn);
|
||||
Serial.print(',');
|
||||
Serial.println(block.overrun);
|
||||
}
|
||||
bn++;
|
||||
}
|
||||
if (!headerPrinted) {
|
||||
Serial.println(F("No errors found"));
|
||||
} else {
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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[FILE_NAME_DIM];
|
||||
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println();
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
|
||||
// Create a new csvFile.
|
||||
strcpy(csvName, binName);
|
||||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
|
||||
|
||||
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
error("open csvFile failed");
|
||||
}
|
||||
binFile.rewind();
|
||||
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 || block.count > DATA_DIM) {
|
||||
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"));
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void createBinFile() {
|
||||
// max number of blocks to erase per erase call
|
||||
const uint32_t ERASE_SIZE = 262144L;
|
||||
uint32_t bgnBlock, endBlock;
|
||||
|
||||
// Delete old tmp file.
|
||||
if (sd.exists(TMP_FILE_NAME)) {
|
||||
Serial.println(F("Deleting tmp file " TMP_FILE_NAME));
|
||||
if (!sd.remove(TMP_FILE_NAME)) {
|
||||
error("Can't remove tmp file");
|
||||
}
|
||||
}
|
||||
// Create new file.
|
||||
Serial.println(F("\nCreating new file"));
|
||||
binFile.close();
|
||||
if (!binFile.createContiguous(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");
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// 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
|
||||
void logData() {
|
||||
createBinFile();
|
||||
recordBinFile();
|
||||
renameBinFile();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void openBinFile() {
|
||||
char name[FILE_NAME_DIM];
|
||||
strcpy(name, binName);
|
||||
Serial.println(F("\nEnter two digit version"));
|
||||
Serial.write(name, BASE_NAME_SIZE);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
char c = Serial.read();
|
||||
Serial.write(c);
|
||||
if (c < '0' || c > '9') {
|
||||
Serial.println(F("\nInvalid digit"));
|
||||
return;
|
||||
}
|
||||
name[BASE_NAME_SIZE + i] = c;
|
||||
}
|
||||
Serial.println(&name[BASE_NAME_SIZE+2]);
|
||||
if (!sd.exists(name)) {
|
||||
Serial.println(F("File does not exist"));
|
||||
return;
|
||||
}
|
||||
binFile.close();
|
||||
strcpy(binName, name);
|
||||
if (!binFile.open(binName, O_RDONLY)) {
|
||||
Serial.println(F("open failed"));
|
||||
return;
|
||||
}
|
||||
Serial.println(F("File opened"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void recordBinFile() {
|
||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1;
|
||||
// Index of last queue location.
|
||||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1;
|
||||
|
||||
// Allocate extra buffer space.
|
||||
block_t block[BUFFER_BLOCK_COUNT - 1];
|
||||
|
||||
block_t* curBlock = 0;
|
||||
|
||||
block_t* emptyStack[BUFFER_BLOCK_COUNT];
|
||||
uint8_t emptyTop;
|
||||
uint8_t minTop;
|
||||
|
||||
block_t* fullQueue[QUEUE_DIM];
|
||||
uint8_t fullHead = 0;
|
||||
uint8_t fullTail = 0;
|
||||
|
||||
// Use SdFat's internal buffer.
|
||||
emptyStack[0] = (block_t*)sd.vol()->cacheClear();
|
||||
if (emptyStack[0] == 0) {
|
||||
error("cacheClear failed");
|
||||
}
|
||||
// Put rest of buffers on the empty stack.
|
||||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) {
|
||||
emptyStack[i] = &block[i - 1];
|
||||
}
|
||||
emptyTop = BUFFER_BLOCK_COUNT;
|
||||
minTop = BUFFER_BLOCK_COUNT;
|
||||
|
||||
// Start a multiple block write.
|
||||
if (!sd.card()->writeStart(binFile.firstBlock())) {
|
||||
error("writeStart failed");
|
||||
}
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
Serial.println(F("Logging - type any character to stop"));
|
||||
bool closeFile = false;
|
||||
uint32_t bn = 0;
|
||||
uint32_t maxLatency = 0;
|
||||
uint32_t overrun = 0;
|
||||
uint32_t overrunTotal = 0;
|
||||
uint32_t logTime = micros();
|
||||
while(1) {
|
||||
// Time for next data record.
|
||||
logTime += LOG_INTERVAL_USEC;
|
||||
if (Serial.available()) {
|
||||
closeFile = true;
|
||||
}
|
||||
if (closeFile) {
|
||||
if (curBlock != 0) {
|
||||
// Put buffer in full queue.
|
||||
fullQueue[fullHead] = curBlock;
|
||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
|
||||
curBlock = 0;
|
||||
}
|
||||
} else {
|
||||
if (curBlock == 0 && emptyTop != 0) {
|
||||
curBlock = emptyStack[--emptyTop];
|
||||
if (emptyTop < minTop) {
|
||||
minTop = emptyTop;
|
||||
}
|
||||
curBlock->count = 0;
|
||||
curBlock->overrun = overrun;
|
||||
overrun = 0;
|
||||
}
|
||||
if ((int32_t)(logTime - micros()) < 0) {
|
||||
error("Rate too fast");
|
||||
}
|
||||
int32_t delta;
|
||||
do {
|
||||
delta = micros() - logTime;
|
||||
} while (delta < 0);
|
||||
if (curBlock == 0) {
|
||||
overrun++;
|
||||
overrunTotal++;
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
}
|
||||
#if ABORT_ON_OVERRUN
|
||||
Serial.println(F("Overrun abort"));
|
||||
break;
|
||||
#endif // ABORT_ON_OVERRUN
|
||||
} else {
|
||||
#if USE_SHARED_SPI
|
||||
sd.card()->spiStop();
|
||||
#endif // USE_SHARED_SPI
|
||||
acquireData(&curBlock->data[curBlock->count++]);
|
||||
#if USE_SHARED_SPI
|
||||
sd.card()->spiStart();
|
||||
#endif // USE_SHARED_SPI
|
||||
if (curBlock->count == DATA_DIM) {
|
||||
fullQueue[fullHead] = curBlock;
|
||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
|
||||
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 = fullTail < QUEUE_LAST ? fullTail + 1 : 0;
|
||||
// Write block to SD.
|
||||
uint32_t usec = micros();
|
||||
if (!sd.card()->writeData((uint8_t*)pBlock)) {
|
||||
error("write data failed");
|
||||
}
|
||||
usec = micros() - usec;
|
||||
if (usec > maxLatency) {
|
||||
maxLatency = usec;
|
||||
}
|
||||
// Move block to empty queue.
|
||||
emptyStack[emptyTop++] = pBlock;
|
||||
bn++;
|
||||
if (bn == FILE_BLOCK_COUNT) {
|
||||
// File full so stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sd.card()->writeStop()) {
|
||||
error("writeStop failed");
|
||||
}
|
||||
Serial.print(F("Min Free buffers: "));
|
||||
Serial.println(minTop);
|
||||
Serial.print(F("Max block write usec: "));
|
||||
Serial.println(maxLatency);
|
||||
Serial.print(F("Overruns: "));
|
||||
Serial.println(overrunTotal);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void recoverTmpFile() {
|
||||
uint16_t count;
|
||||
if (!binFile.open(TMP_FILE_NAME, O_RDWR)) {
|
||||
return;
|
||||
}
|
||||
if (binFile.read(&count, 2) != 2 || count != DATA_DIM) {
|
||||
error("Please delete existing " TMP_FILE_NAME);
|
||||
}
|
||||
Serial.println(F("\nRecovering data in tmp file " TMP_FILE_NAME));
|
||||
uint32_t bgnBlock = 0;
|
||||
uint32_t endBlock = binFile.fileSize()/512 - 1;
|
||||
// find last used block.
|
||||
while (bgnBlock < endBlock) {
|
||||
uint32_t midBlock = (bgnBlock + endBlock + 1)/2;
|
||||
binFile.seekSet(512*midBlock);
|
||||
if (binFile.read(&count, 2) != 2) error("read");
|
||||
if (count == 0 || count > DATA_DIM) {
|
||||
endBlock = midBlock - 1;
|
||||
} else {
|
||||
bgnBlock = midBlock;
|
||||
}
|
||||
}
|
||||
// truncate after last used block.
|
||||
if (!binFile.truncate(512*(bgnBlock + 1))) {
|
||||
error("Truncate " TMP_FILE_NAME " failed");
|
||||
}
|
||||
renameBinFile();
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void renameBinFile() {
|
||||
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]++;
|
||||
}
|
||||
}
|
||||
if (!binFile.rename(binName)) {
|
||||
error("Can't rename file");
|
||||
}
|
||||
Serial.print(F("File renamed: "));
|
||||
Serial.println(binName);
|
||||
Serial.print(F("File size: "));
|
||||
Serial.print(binFile.fileSize()/512);
|
||||
Serial.println(F(" blocks"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testSensor() {
|
||||
const uint32_t interval = 200000;
|
||||
int32_t diff;
|
||||
data_t data;
|
||||
Serial.println(F("\nTesting - type any character to stop\n"));
|
||||
// Wait for Serial Idle.
|
||||
delay(1000);
|
||||
printHeader(&Serial);
|
||||
uint32_t m = micros();
|
||||
while (!Serial.available()) {
|
||||
m += interval;
|
||||
do {
|
||||
diff = m - micros();
|
||||
} while (diff > 0);
|
||||
acquireData(&data);
|
||||
printData(&Serial, &data);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
pinMode(ERROR_LED_PIN, OUTPUT);
|
||||
}
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.print(F("\nFreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
Serial.print(F("Records/block: "));
|
||||
Serial.println(DATA_DIM);
|
||||
if (sizeof(block_t) != 512) {
|
||||
error("Invalid block size");
|
||||
}
|
||||
// Allow userSetup access to SPI bus.
|
||||
pinMode(SD_CS_PIN, OUTPUT);
|
||||
digitalWrite(SD_CS_PIN, HIGH);
|
||||
|
||||
// Setup sensors.
|
||||
userSetup();
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorPrint(&Serial);
|
||||
fatalBlink();
|
||||
}
|
||||
// recover existing tmp file.
|
||||
if (sd.exists(TMP_FILE_NAME)) {
|
||||
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (Serial.read() == 'Y') {
|
||||
recoverTmpFile();
|
||||
} else {
|
||||
error("'Y' not typed, please manually delete " TMP_FILE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {
|
||||
// Read any Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
Serial.println();
|
||||
Serial.println(F("type:"));
|
||||
Serial.println(F("b - open existing bin file"));
|
||||
Serial.println(F("c - convert file to csv"));
|
||||
Serial.println(F("d - dump data to Serial"));
|
||||
Serial.println(F("e - overrun error details"));
|
||||
Serial.println(F("l - list files"));
|
||||
Serial.println(F("r - record data"));
|
||||
Serial.println(F("t - test without logging"));
|
||||
while(!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
#if WDT_YIELD_TIME_MICROS
|
||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||
while (true) {}
|
||||
#endif
|
||||
|
||||
char c = tolower(Serial.read());
|
||||
|
||||
// Discard extra Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
}
|
||||
if (c == 'b') {
|
||||
openBinFile();
|
||||
} else if (c == 'c') {
|
||||
binaryToCsv();
|
||||
} else if (c == 'd') {
|
||||
dumpData();
|
||||
} else if (c == 'e') {
|
||||
checkOverrun();
|
||||
} else if (c == 'l') {
|
||||
Serial.println(F("\nls:"));
|
||||
sd.ls(&Serial, LS_SIZE);
|
||||
} else if (c == 'r') {
|
||||
logData();
|
||||
} else if (c == 't') {
|
||||
testSensor();
|
||||
} else {
|
||||
Serial.println(F("Invalid entry"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "UserTypes.h"
|
||||
// User data functions. Modify these functions for your data items.
|
||||
|
||||
// Start time for data
|
||||
static uint32_t startMicros;
|
||||
|
||||
// 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) {
|
||||
if (startMicros == 0) {
|
||||
startMicros = data->time;
|
||||
}
|
||||
pr->print(data->time - startMicros);
|
||||
for (int i = 0; i < ADC_DIM; i++) {
|
||||
pr->write(',');
|
||||
pr->print(data->adc[i]);
|
||||
}
|
||||
pr->println();
|
||||
}
|
||||
|
||||
// Print data header.
|
||||
void printHeader(Print* pr) {
|
||||
startMicros = 0;
|
||||
pr->print(F("micros"));
|
||||
for (int i = 0; i < ADC_DIM; i++) {
|
||||
pr->print(F(",adc"));
|
||||
pr->print(i);
|
||||
}
|
||||
pr->println();
|
||||
}
|
||||
|
||||
// Sensor setup
|
||||
void userSetup() {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef UserTypes_h
|
||||
#define UserTypes_h
|
||||
#include "Arduino.h"
|
||||
// User data types. Modify for your data items.
|
||||
#define FILE_BASE_NAME "adc4pin"
|
||||
const uint8_t ADC_DIM = 4;
|
||||
struct data_t {
|
||||
uint32_t time;
|
||||
uint16_t adc[ADC_DIM];
|
||||
};
|
||||
void acquireData(data_t* data);
|
||||
void printData(Print* pr, data_t* data);
|
||||
void printHeader(Print* pr);
|
||||
void userSetup();
|
||||
#endif // UserTypes_h
|
||||
@@ -0,0 +1,655 @@
|
||||
/**
|
||||
* 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 12 512 byte buffers will be used.
|
||||
*
|
||||
* Data is written to the file using a SD multiple block write command.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
#include "UserTypes.h"
|
||||
|
||||
#ifdef __AVR_ATmega328P__
|
||||
#include "MinimumSerial.h"
|
||||
MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
//==============================================================================
|
||||
// Start of configuration constants.
|
||||
//==============================================================================
|
||||
// Abort run on an overrun. Data before the overrun will be saved.
|
||||
#define ABORT_ON_OVERRUN 1
|
||||
//------------------------------------------------------------------------------
|
||||
//Interval between data records in microseconds.
|
||||
const uint32_t LOG_INTERVAL_USEC = 2000;
|
||||
//------------------------------------------------------------------------------
|
||||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor.
|
||||
// May not work for some cards.
|
||||
#ifndef USE_SHARED_SPI
|
||||
#define USE_SHARED_SPI 0
|
||||
#endif // USE_SHARED_SPI
|
||||
//------------------------------------------------------------------------------
|
||||
// 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
|
||||
// overrun errors and logging continues unless ABORT_ON_OVERRUN
|
||||
// is non-zero.
|
||||
#ifdef ERROR_LED_PIN
|
||||
#undef ERROR_LED_PIN
|
||||
#endif // ERROR_LED_PIN
|
||||
const int8_t ERROR_LED_PIN = -1;
|
||||
//------------------------------------------------------------------------------
|
||||
// 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 if not defined in UserTypes.h
|
||||
#ifndef FILE_BASE_NAME
|
||||
#define FILE_BASE_NAME "data"
|
||||
#endif // FILE_BASE_NAME
|
||||
//------------------------------------------------------------------------------
|
||||
// Buffer definitions.
|
||||
//
|
||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional
|
||||
// buffers.
|
||||
//
|
||||
#ifndef RAMEND
|
||||
// Assume ARM. Use total of ten 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 10;
|
||||
//
|
||||
#elif RAMEND < 0X8FF
|
||||
#error Too little SRAM
|
||||
//
|
||||
#elif RAMEND < 0X10FF
|
||||
// Use total of two 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 2;
|
||||
//
|
||||
#elif RAMEND < 0X20FF
|
||||
// Use total of four 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 4;
|
||||
//
|
||||
#else // RAMEND
|
||||
// Use total of 12 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 FILE_BASE_NAME "##.bin"
|
||||
|
||||
// Size of file base name.
|
||||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
|
||||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7;
|
||||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin";
|
||||
|
||||
SdFat sd;
|
||||
|
||||
SdBaseFile binFile;
|
||||
|
||||
// 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];
|
||||
};
|
||||
//==============================================================================
|
||||
// Error messages stored in flash.
|
||||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();}
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
void fatalBlink() {
|
||||
while (true) {
|
||||
yield();
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
delay(200);
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
delay(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// read data file and check for overruns
|
||||
void checkOverrun() {
|
||||
bool headerPrinted = false;
|
||||
block_t block;
|
||||
uint32_t bn = 0;
|
||||
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println();
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
binFile.rewind();
|
||||
Serial.println();
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
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(binFile.firstBlock() + bn);
|
||||
Serial.print(',');
|
||||
Serial.println(block.overrun);
|
||||
}
|
||||
bn++;
|
||||
}
|
||||
if (!headerPrinted) {
|
||||
Serial.println(F("No errors found"));
|
||||
} else {
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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[FILE_NAME_DIM];
|
||||
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println();
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
|
||||
// Create a new csvFile.
|
||||
strcpy(csvName, binName);
|
||||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
|
||||
|
||||
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
error("open csvFile failed");
|
||||
}
|
||||
binFile.rewind();
|
||||
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 || block.count > DATA_DIM) {
|
||||
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"));
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void createBinFile() {
|
||||
// max number of blocks to erase per erase call
|
||||
const uint32_t ERASE_SIZE = 262144L;
|
||||
uint32_t bgnBlock, endBlock;
|
||||
|
||||
// Delete old tmp file.
|
||||
if (sd.exists(TMP_FILE_NAME)) {
|
||||
Serial.println(F("Deleting tmp file " TMP_FILE_NAME));
|
||||
if (!sd.remove(TMP_FILE_NAME)) {
|
||||
error("Can't remove tmp file");
|
||||
}
|
||||
}
|
||||
// Create new file.
|
||||
Serial.println(F("\nCreating new file"));
|
||||
binFile.close();
|
||||
if (!binFile.createContiguous(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");
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// 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
|
||||
void logData() {
|
||||
createBinFile();
|
||||
recordBinFile();
|
||||
renameBinFile();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void openBinFile() {
|
||||
char name[FILE_NAME_DIM];
|
||||
strcpy(name, binName);
|
||||
Serial.println(F("\nEnter two digit version"));
|
||||
Serial.write(name, BASE_NAME_SIZE);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
char c = Serial.read();
|
||||
Serial.write(c);
|
||||
if (c < '0' || c > '9') {
|
||||
Serial.println(F("\nInvalid digit"));
|
||||
return;
|
||||
}
|
||||
name[BASE_NAME_SIZE + i] = c;
|
||||
}
|
||||
Serial.println(&name[BASE_NAME_SIZE+2]);
|
||||
if (!sd.exists(name)) {
|
||||
Serial.println(F("File does not exist"));
|
||||
return;
|
||||
}
|
||||
binFile.close();
|
||||
strcpy(binName, name);
|
||||
if (!binFile.open(binName, O_RDONLY)) {
|
||||
Serial.println(F("open failed"));
|
||||
return;
|
||||
}
|
||||
Serial.println(F("File opened"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void recordBinFile() {
|
||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1;
|
||||
// Index of last queue location.
|
||||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1;
|
||||
|
||||
// Allocate extra buffer space.
|
||||
block_t block[BUFFER_BLOCK_COUNT - 1];
|
||||
|
||||
block_t* curBlock = 0;
|
||||
|
||||
block_t* emptyStack[BUFFER_BLOCK_COUNT];
|
||||
uint8_t emptyTop;
|
||||
uint8_t minTop;
|
||||
|
||||
block_t* fullQueue[QUEUE_DIM];
|
||||
uint8_t fullHead = 0;
|
||||
uint8_t fullTail = 0;
|
||||
|
||||
// Use SdFat's internal buffer.
|
||||
emptyStack[0] = (block_t*)sd.vol()->cacheClear();
|
||||
if (emptyStack[0] == 0) {
|
||||
error("cacheClear failed");
|
||||
}
|
||||
// Put rest of buffers on the empty stack.
|
||||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) {
|
||||
emptyStack[i] = &block[i - 1];
|
||||
}
|
||||
emptyTop = BUFFER_BLOCK_COUNT;
|
||||
minTop = BUFFER_BLOCK_COUNT;
|
||||
|
||||
// Start a multiple block write.
|
||||
if (!sd.card()->writeStart(binFile.firstBlock())) {
|
||||
error("writeStart failed");
|
||||
}
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
Serial.println(F("Logging - type any character to stop"));
|
||||
bool closeFile = false;
|
||||
uint32_t bn = 0;
|
||||
uint32_t maxLatency = 0;
|
||||
uint32_t overrun = 0;
|
||||
uint32_t overrunTotal = 0;
|
||||
uint32_t logTime = micros();
|
||||
while(1) {
|
||||
// Time for next data record.
|
||||
logTime += LOG_INTERVAL_USEC;
|
||||
if (Serial.available()) {
|
||||
closeFile = true;
|
||||
}
|
||||
if (closeFile) {
|
||||
if (curBlock != 0) {
|
||||
// Put buffer in full queue.
|
||||
fullQueue[fullHead] = curBlock;
|
||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
|
||||
curBlock = 0;
|
||||
}
|
||||
} else {
|
||||
if (curBlock == 0 && emptyTop != 0) {
|
||||
curBlock = emptyStack[--emptyTop];
|
||||
if (emptyTop < minTop) {
|
||||
minTop = emptyTop;
|
||||
}
|
||||
curBlock->count = 0;
|
||||
curBlock->overrun = overrun;
|
||||
overrun = 0;
|
||||
}
|
||||
if ((int32_t)(logTime - micros()) < 0) {
|
||||
error("Rate too fast");
|
||||
}
|
||||
int32_t delta;
|
||||
do {
|
||||
delta = micros() - logTime;
|
||||
} while (delta < 0);
|
||||
if (curBlock == 0) {
|
||||
overrun++;
|
||||
overrunTotal++;
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
}
|
||||
#if ABORT_ON_OVERRUN
|
||||
Serial.println(F("Overrun abort"));
|
||||
break;
|
||||
#endif // ABORT_ON_OVERRUN
|
||||
} else {
|
||||
#if USE_SHARED_SPI
|
||||
sd.card()->spiStop();
|
||||
#endif // USE_SHARED_SPI
|
||||
acquireData(&curBlock->data[curBlock->count++]);
|
||||
#if USE_SHARED_SPI
|
||||
sd.card()->spiStart();
|
||||
#endif // USE_SHARED_SPI
|
||||
if (curBlock->count == DATA_DIM) {
|
||||
fullQueue[fullHead] = curBlock;
|
||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
|
||||
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 = fullTail < QUEUE_LAST ? fullTail + 1 : 0;
|
||||
// Write block to SD.
|
||||
uint32_t usec = micros();
|
||||
if (!sd.card()->writeData((uint8_t*)pBlock)) {
|
||||
error("write data failed");
|
||||
}
|
||||
usec = micros() - usec;
|
||||
if (usec > maxLatency) {
|
||||
maxLatency = usec;
|
||||
}
|
||||
// Move block to empty queue.
|
||||
emptyStack[emptyTop++] = pBlock;
|
||||
bn++;
|
||||
if (bn == FILE_BLOCK_COUNT) {
|
||||
// File full so stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sd.card()->writeStop()) {
|
||||
error("writeStop failed");
|
||||
}
|
||||
Serial.print(F("Min Free buffers: "));
|
||||
Serial.println(minTop);
|
||||
Serial.print(F("Max block write usec: "));
|
||||
Serial.println(maxLatency);
|
||||
Serial.print(F("Overruns: "));
|
||||
Serial.println(overrunTotal);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void recoverTmpFile() {
|
||||
uint16_t count;
|
||||
if (!binFile.open(TMP_FILE_NAME, O_RDWR)) {
|
||||
return;
|
||||
}
|
||||
if (binFile.read(&count, 2) != 2 || count != DATA_DIM) {
|
||||
error("Please delete existing " TMP_FILE_NAME);
|
||||
}
|
||||
Serial.println(F("\nRecovering data in tmp file " TMP_FILE_NAME));
|
||||
uint32_t bgnBlock = 0;
|
||||
uint32_t endBlock = binFile.fileSize()/512 - 1;
|
||||
// find last used block.
|
||||
while (bgnBlock < endBlock) {
|
||||
uint32_t midBlock = (bgnBlock + endBlock + 1)/2;
|
||||
binFile.seekSet(512*midBlock);
|
||||
if (binFile.read(&count, 2) != 2) error("read");
|
||||
if (count == 0 || count > DATA_DIM) {
|
||||
endBlock = midBlock - 1;
|
||||
} else {
|
||||
bgnBlock = midBlock;
|
||||
}
|
||||
}
|
||||
// truncate after last used block.
|
||||
if (!binFile.truncate(512*(bgnBlock + 1))) {
|
||||
error("Truncate " TMP_FILE_NAME " failed");
|
||||
}
|
||||
renameBinFile();
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void renameBinFile() {
|
||||
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]++;
|
||||
}
|
||||
}
|
||||
if (!binFile.rename(binName)) {
|
||||
error("Can't rename file");
|
||||
}
|
||||
Serial.print(F("File renamed: "));
|
||||
Serial.println(binName);
|
||||
Serial.print(F("File size: "));
|
||||
Serial.print(binFile.fileSize()/512);
|
||||
Serial.println(F(" blocks"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testSensor() {
|
||||
const uint32_t interval = 200000;
|
||||
int32_t diff;
|
||||
data_t data;
|
||||
Serial.println(F("\nTesting - type any character to stop\n"));
|
||||
// Wait for Serial Idle.
|
||||
delay(1000);
|
||||
printHeader(&Serial);
|
||||
uint32_t m = micros();
|
||||
while (!Serial.available()) {
|
||||
m += interval;
|
||||
do {
|
||||
diff = m - micros();
|
||||
} while (diff > 0);
|
||||
acquireData(&data);
|
||||
printData(&Serial, &data);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
pinMode(ERROR_LED_PIN, OUTPUT);
|
||||
}
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.print(F("\nFreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
Serial.print(F("Records/block: "));
|
||||
Serial.println(DATA_DIM);
|
||||
if (sizeof(block_t) != 512) {
|
||||
error("Invalid block size");
|
||||
}
|
||||
// Allow userSetup access to SPI bus.
|
||||
pinMode(SD_CS_PIN, OUTPUT);
|
||||
digitalWrite(SD_CS_PIN, HIGH);
|
||||
|
||||
// Setup sensors.
|
||||
userSetup();
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorPrint(&Serial);
|
||||
fatalBlink();
|
||||
}
|
||||
// recover existing tmp file.
|
||||
if (sd.exists(TMP_FILE_NAME)) {
|
||||
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (Serial.read() == 'Y') {
|
||||
recoverTmpFile();
|
||||
} else {
|
||||
error("'Y' not typed, please manually delete " TMP_FILE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {
|
||||
// Read any Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
Serial.println();
|
||||
Serial.println(F("type:"));
|
||||
Serial.println(F("b - open existing bin file"));
|
||||
Serial.println(F("c - convert file to csv"));
|
||||
Serial.println(F("d - dump data to Serial"));
|
||||
Serial.println(F("e - overrun error details"));
|
||||
Serial.println(F("l - list files"));
|
||||
Serial.println(F("r - record data"));
|
||||
Serial.println(F("t - test without logging"));
|
||||
while(!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
#if WDT_YIELD_TIME_MICROS
|
||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||
while (true) {}
|
||||
#endif
|
||||
|
||||
char c = tolower(Serial.read());
|
||||
|
||||
// Discard extra Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
}
|
||||
if (c == 'b') {
|
||||
openBinFile();
|
||||
} else if (c == 'c') {
|
||||
binaryToCsv();
|
||||
} else if (c == 'd') {
|
||||
dumpData();
|
||||
} else if (c == 'e') {
|
||||
checkOverrun();
|
||||
} else if (c == 'l') {
|
||||
Serial.println(F("\nls:"));
|
||||
sd.ls(&Serial, LS_SIZE);
|
||||
} else if (c == 'r') {
|
||||
logData();
|
||||
} else if (c == 't') {
|
||||
testSensor();
|
||||
} else {
|
||||
Serial.println(F("Invalid entry"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy.
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "UserTypes.h"
|
||||
// User data functions. Modify these functions for your data items.
|
||||
|
||||
// Start time for data
|
||||
static uint32_t startMicros;
|
||||
|
||||
const uint8_t ADXL345_CS = 9;
|
||||
|
||||
const uint8_t POWER_CTL = 0x2D; //Power Control Register
|
||||
const uint8_t DATA_FORMAT = 0x31;
|
||||
const uint8_t DATAX0 = 0x32; //X-Axis Data 0
|
||||
const uint8_t DATAX1 = 0x33; //X-Axis Data 1
|
||||
const uint8_t DATAY0 = 0x34; //Y-Axis Data 0
|
||||
const uint8_t DATAY1 = 0x35; //Y-Axis Data 1
|
||||
const uint8_t DATAZ0 = 0x36; //Z-Axis Data 0
|
||||
const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1
|
||||
|
||||
void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) {
|
||||
// Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1.
|
||||
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3));
|
||||
digitalWrite(ADXL345_CS, LOW);
|
||||
SPI.transfer(registerAddress);
|
||||
SPI.transfer(value);
|
||||
digitalWrite(ADXL345_CS, HIGH);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
void userSetup() {
|
||||
SPI.begin();
|
||||
pinMode(ADXL345_CS, OUTPUT);
|
||||
digitalWrite(ADXL345_CS, HIGH);
|
||||
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
|
||||
writeADXL345Register(DATA_FORMAT, 0x01);
|
||||
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
|
||||
writeADXL345Register(POWER_CTL, 0x08); //Measurement mode
|
||||
}
|
||||
|
||||
// Acquire a data record.
|
||||
void acquireData(data_t* data) {
|
||||
// Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1.
|
||||
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3));
|
||||
data->time = micros();
|
||||
digitalWrite(ADXL345_CS, LOW);
|
||||
// Read multiple bytes so or 0XC0 with address.
|
||||
SPI.transfer(DATAX0 | 0XC0);
|
||||
data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8);
|
||||
data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8);
|
||||
data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8);
|
||||
digitalWrite(ADXL345_CS, HIGH);
|
||||
SPI.endTransaction();
|
||||
}
|
||||
|
||||
// Print a data record.
|
||||
void printData(Print* pr, data_t* data) {
|
||||
if (startMicros == 0) {
|
||||
startMicros = data->time;
|
||||
}
|
||||
pr->print(data->time - startMicros);
|
||||
for (int i = 0; i < ACCEL_DIM; i++) {
|
||||
pr->write(',');
|
||||
pr->print(data->accel[i]);
|
||||
}
|
||||
pr->println();
|
||||
}
|
||||
|
||||
// Print data header.
|
||||
void printHeader(Print* pr) {
|
||||
startMicros = 0;
|
||||
pr->println(F("micros,ax,ay,az"));
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef UserTypes_h
|
||||
#define UserTypes_h
|
||||
#include "Arduino.h"
|
||||
#include "SPI.h"
|
||||
#define USE_SHARED_SPI 1
|
||||
#define FILE_BASE_NAME "ADXL4G"
|
||||
// User data types. Modify for your data items.
|
||||
const uint8_t ACCEL_DIM = 3;
|
||||
struct data_t {
|
||||
uint32_t time;
|
||||
int16_t accel[ACCEL_DIM];
|
||||
};
|
||||
void acquireData(data_t* data);
|
||||
void printData(Print* pr, data_t* data);
|
||||
void printHeader(Print* pr);
|
||||
void userSetup();
|
||||
#endif // UserTypes_h
|
||||
@@ -0,0 +1 @@
|
||||
Test of shared SPI for LowLatencyLogger.
|
||||
@@ -0,0 +1,655 @@
|
||||
/**
|
||||
* 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 12 512 byte buffers will be used.
|
||||
*
|
||||
* Data is written to the file using a SD multiple block write command.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
#include "UserTypes.h"
|
||||
|
||||
#ifdef __AVR_ATmega328P__
|
||||
#include "MinimumSerial.h"
|
||||
MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
//==============================================================================
|
||||
// Start of configuration constants.
|
||||
//==============================================================================
|
||||
// Abort run on an overrun. Data before the overrun will be saved.
|
||||
#define ABORT_ON_OVERRUN 1
|
||||
//------------------------------------------------------------------------------
|
||||
//Interval between data records in microseconds.
|
||||
const uint32_t LOG_INTERVAL_USEC = 2000;
|
||||
//------------------------------------------------------------------------------
|
||||
// Set USE_SHARED_SPI non-zero for use of an SPI sensor.
|
||||
// May not work for some cards.
|
||||
#ifndef USE_SHARED_SPI
|
||||
#define USE_SHARED_SPI 0
|
||||
#endif // USE_SHARED_SPI
|
||||
//------------------------------------------------------------------------------
|
||||
// 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
|
||||
// overrun errors and logging continues unless ABORT_ON_OVERRUN
|
||||
// is non-zero.
|
||||
#ifdef ERROR_LED_PIN
|
||||
#undef ERROR_LED_PIN
|
||||
#endif // ERROR_LED_PIN
|
||||
const int8_t ERROR_LED_PIN = -1;
|
||||
//------------------------------------------------------------------------------
|
||||
// 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 if not defined in UserTypes.h
|
||||
#ifndef FILE_BASE_NAME
|
||||
#define FILE_BASE_NAME "data"
|
||||
#endif // FILE_BASE_NAME
|
||||
//------------------------------------------------------------------------------
|
||||
// Buffer definitions.
|
||||
//
|
||||
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT-1 additional
|
||||
// buffers.
|
||||
//
|
||||
#ifndef RAMEND
|
||||
// Assume ARM. Use total of ten 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 10;
|
||||
//
|
||||
#elif RAMEND < 0X8FF
|
||||
#error Too little SRAM
|
||||
//
|
||||
#elif RAMEND < 0X10FF
|
||||
// Use total of two 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 2;
|
||||
//
|
||||
#elif RAMEND < 0X20FF
|
||||
// Use total of four 512 byte buffers.
|
||||
const uint8_t BUFFER_BLOCK_COUNT = 4;
|
||||
//
|
||||
#else // RAMEND
|
||||
// Use total of 12 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 FILE_BASE_NAME "##.bin"
|
||||
|
||||
// Size of file base name.
|
||||
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
|
||||
const uint8_t FILE_NAME_DIM = BASE_NAME_SIZE + 7;
|
||||
char binName[FILE_NAME_DIM] = FILE_BASE_NAME "00.bin";
|
||||
|
||||
SdFat sd;
|
||||
|
||||
SdBaseFile binFile;
|
||||
|
||||
// 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];
|
||||
};
|
||||
//==============================================================================
|
||||
// Error messages stored in flash.
|
||||
#define error(msg) {sd.errorPrint(&Serial, F(msg));fatalBlink();}
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
void fatalBlink() {
|
||||
while (true) {
|
||||
yield();
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
delay(200);
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
delay(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// read data file and check for overruns
|
||||
void checkOverrun() {
|
||||
bool headerPrinted = false;
|
||||
block_t block;
|
||||
uint32_t bn = 0;
|
||||
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println();
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
binFile.rewind();
|
||||
Serial.println();
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
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(binFile.firstBlock() + bn);
|
||||
Serial.print(',');
|
||||
Serial.println(block.overrun);
|
||||
}
|
||||
bn++;
|
||||
}
|
||||
if (!headerPrinted) {
|
||||
Serial.println(F("No errors found"));
|
||||
} else {
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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[FILE_NAME_DIM];
|
||||
|
||||
if (!binFile.isOpen()) {
|
||||
Serial.println();
|
||||
Serial.println(F("No current binary file"));
|
||||
return;
|
||||
}
|
||||
Serial.println();
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
|
||||
// Create a new csvFile.
|
||||
strcpy(csvName, binName);
|
||||
strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
|
||||
|
||||
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
error("open csvFile failed");
|
||||
}
|
||||
binFile.rewind();
|
||||
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 || block.count > DATA_DIM) {
|
||||
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"));
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void createBinFile() {
|
||||
// max number of blocks to erase per erase call
|
||||
const uint32_t ERASE_SIZE = 262144L;
|
||||
uint32_t bgnBlock, endBlock;
|
||||
|
||||
// Delete old tmp file.
|
||||
if (sd.exists(TMP_FILE_NAME)) {
|
||||
Serial.println(F("Deleting tmp file " TMP_FILE_NAME));
|
||||
if (!sd.remove(TMP_FILE_NAME)) {
|
||||
error("Can't remove tmp file");
|
||||
}
|
||||
}
|
||||
// Create new file.
|
||||
Serial.println(F("\nCreating new file"));
|
||||
binFile.close();
|
||||
if (!binFile.createContiguous(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");
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// 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
|
||||
void logData() {
|
||||
createBinFile();
|
||||
recordBinFile();
|
||||
renameBinFile();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void openBinFile() {
|
||||
char name[FILE_NAME_DIM];
|
||||
strcpy(name, binName);
|
||||
Serial.println(F("\nEnter two digit version"));
|
||||
Serial.write(name, BASE_NAME_SIZE);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
char c = Serial.read();
|
||||
Serial.write(c);
|
||||
if (c < '0' || c > '9') {
|
||||
Serial.println(F("\nInvalid digit"));
|
||||
return;
|
||||
}
|
||||
name[BASE_NAME_SIZE + i] = c;
|
||||
}
|
||||
Serial.println(&name[BASE_NAME_SIZE+2]);
|
||||
if (!sd.exists(name)) {
|
||||
Serial.println(F("File does not exist"));
|
||||
return;
|
||||
}
|
||||
binFile.close();
|
||||
strcpy(binName, name);
|
||||
if (!binFile.open(binName, O_RDONLY)) {
|
||||
Serial.println(F("open failed"));
|
||||
return;
|
||||
}
|
||||
Serial.println(F("File opened"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void recordBinFile() {
|
||||
const uint8_t QUEUE_DIM = BUFFER_BLOCK_COUNT + 1;
|
||||
// Index of last queue location.
|
||||
const uint8_t QUEUE_LAST = QUEUE_DIM - 1;
|
||||
|
||||
// Allocate extra buffer space.
|
||||
block_t block[BUFFER_BLOCK_COUNT - 1];
|
||||
|
||||
block_t* curBlock = 0;
|
||||
|
||||
block_t* emptyStack[BUFFER_BLOCK_COUNT];
|
||||
uint8_t emptyTop;
|
||||
uint8_t minTop;
|
||||
|
||||
block_t* fullQueue[QUEUE_DIM];
|
||||
uint8_t fullHead = 0;
|
||||
uint8_t fullTail = 0;
|
||||
|
||||
// Use SdFat's internal buffer.
|
||||
emptyStack[0] = (block_t*)sd.vol()->cacheClear();
|
||||
if (emptyStack[0] == 0) {
|
||||
error("cacheClear failed");
|
||||
}
|
||||
// Put rest of buffers on the empty stack.
|
||||
for (int i = 1; i < BUFFER_BLOCK_COUNT; i++) {
|
||||
emptyStack[i] = &block[i - 1];
|
||||
}
|
||||
emptyTop = BUFFER_BLOCK_COUNT;
|
||||
minTop = BUFFER_BLOCK_COUNT;
|
||||
|
||||
// Start a multiple block write.
|
||||
if (!sd.card()->writeStart(binFile.firstBlock())) {
|
||||
error("writeStart failed");
|
||||
}
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
Serial.println(F("Logging - type any character to stop"));
|
||||
bool closeFile = false;
|
||||
uint32_t bn = 0;
|
||||
uint32_t maxLatency = 0;
|
||||
uint32_t overrun = 0;
|
||||
uint32_t overrunTotal = 0;
|
||||
uint32_t logTime = micros();
|
||||
while(1) {
|
||||
// Time for next data record.
|
||||
logTime += LOG_INTERVAL_USEC;
|
||||
if (Serial.available()) {
|
||||
closeFile = true;
|
||||
}
|
||||
if (closeFile) {
|
||||
if (curBlock != 0) {
|
||||
// Put buffer in full queue.
|
||||
fullQueue[fullHead] = curBlock;
|
||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
|
||||
curBlock = 0;
|
||||
}
|
||||
} else {
|
||||
if (curBlock == 0 && emptyTop != 0) {
|
||||
curBlock = emptyStack[--emptyTop];
|
||||
if (emptyTop < minTop) {
|
||||
minTop = emptyTop;
|
||||
}
|
||||
curBlock->count = 0;
|
||||
curBlock->overrun = overrun;
|
||||
overrun = 0;
|
||||
}
|
||||
if ((int32_t)(logTime - micros()) < 0) {
|
||||
error("Rate too fast");
|
||||
}
|
||||
int32_t delta;
|
||||
do {
|
||||
delta = micros() - logTime;
|
||||
} while (delta < 0);
|
||||
if (curBlock == 0) {
|
||||
overrun++;
|
||||
overrunTotal++;
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
}
|
||||
#if ABORT_ON_OVERRUN
|
||||
Serial.println(F("Overrun abort"));
|
||||
break;
|
||||
#endif // ABORT_ON_OVERRUN
|
||||
} else {
|
||||
#if USE_SHARED_SPI
|
||||
sd.card()->spiStop();
|
||||
#endif // USE_SHARED_SPI
|
||||
acquireData(&curBlock->data[curBlock->count++]);
|
||||
#if USE_SHARED_SPI
|
||||
sd.card()->spiStart();
|
||||
#endif // USE_SHARED_SPI
|
||||
if (curBlock->count == DATA_DIM) {
|
||||
fullQueue[fullHead] = curBlock;
|
||||
fullHead = fullHead < QUEUE_LAST ? fullHead + 1 : 0;
|
||||
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 = fullTail < QUEUE_LAST ? fullTail + 1 : 0;
|
||||
// Write block to SD.
|
||||
uint32_t usec = micros();
|
||||
if (!sd.card()->writeData((uint8_t*)pBlock)) {
|
||||
error("write data failed");
|
||||
}
|
||||
usec = micros() - usec;
|
||||
if (usec > maxLatency) {
|
||||
maxLatency = usec;
|
||||
}
|
||||
// Move block to empty queue.
|
||||
emptyStack[emptyTop++] = pBlock;
|
||||
bn++;
|
||||
if (bn == FILE_BLOCK_COUNT) {
|
||||
// File full so stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sd.card()->writeStop()) {
|
||||
error("writeStop failed");
|
||||
}
|
||||
Serial.print(F("Min Free buffers: "));
|
||||
Serial.println(minTop);
|
||||
Serial.print(F("Max block write usec: "));
|
||||
Serial.println(maxLatency);
|
||||
Serial.print(F("Overruns: "));
|
||||
Serial.println(overrunTotal);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void recoverTmpFile() {
|
||||
uint16_t count;
|
||||
if (!binFile.open(TMP_FILE_NAME, O_RDWR)) {
|
||||
return;
|
||||
}
|
||||
if (binFile.read(&count, 2) != 2 || count != DATA_DIM) {
|
||||
error("Please delete existing " TMP_FILE_NAME);
|
||||
}
|
||||
Serial.println(F("\nRecovering data in tmp file " TMP_FILE_NAME));
|
||||
uint32_t bgnBlock = 0;
|
||||
uint32_t endBlock = binFile.fileSize()/512 - 1;
|
||||
// find last used block.
|
||||
while (bgnBlock < endBlock) {
|
||||
uint32_t midBlock = (bgnBlock + endBlock + 1)/2;
|
||||
binFile.seekSet(512*midBlock);
|
||||
if (binFile.read(&count, 2) != 2) error("read");
|
||||
if (count == 0 || count > DATA_DIM) {
|
||||
endBlock = midBlock - 1;
|
||||
} else {
|
||||
bgnBlock = midBlock;
|
||||
}
|
||||
}
|
||||
// truncate after last used block.
|
||||
if (!binFile.truncate(512*(bgnBlock + 1))) {
|
||||
error("Truncate " TMP_FILE_NAME " failed");
|
||||
}
|
||||
renameBinFile();
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void renameBinFile() {
|
||||
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]++;
|
||||
}
|
||||
}
|
||||
if (!binFile.rename(binName)) {
|
||||
error("Can't rename file");
|
||||
}
|
||||
Serial.print(F("File renamed: "));
|
||||
Serial.println(binName);
|
||||
Serial.print(F("File size: "));
|
||||
Serial.print(binFile.fileSize()/512);
|
||||
Serial.println(F(" blocks"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testSensor() {
|
||||
const uint32_t interval = 200000;
|
||||
int32_t diff;
|
||||
data_t data;
|
||||
Serial.println(F("\nTesting - type any character to stop\n"));
|
||||
// Wait for Serial Idle.
|
||||
delay(1000);
|
||||
printHeader(&Serial);
|
||||
uint32_t m = micros();
|
||||
while (!Serial.available()) {
|
||||
m += interval;
|
||||
do {
|
||||
diff = m - micros();
|
||||
} while (diff > 0);
|
||||
acquireData(&data);
|
||||
printData(&Serial, &data);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
pinMode(ERROR_LED_PIN, OUTPUT);
|
||||
}
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.print(F("\nFreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
Serial.print(F("Records/block: "));
|
||||
Serial.println(DATA_DIM);
|
||||
if (sizeof(block_t) != 512) {
|
||||
error("Invalid block size");
|
||||
}
|
||||
// Allow userSetup access to SPI bus.
|
||||
pinMode(SD_CS_PIN, OUTPUT);
|
||||
digitalWrite(SD_CS_PIN, HIGH);
|
||||
|
||||
// Setup sensors.
|
||||
userSetup();
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorPrint(&Serial);
|
||||
fatalBlink();
|
||||
}
|
||||
// recover existing tmp file.
|
||||
if (sd.exists(TMP_FILE_NAME)) {
|
||||
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
if (Serial.read() == 'Y') {
|
||||
recoverTmpFile();
|
||||
} else {
|
||||
error("'Y' not typed, please manually delete " TMP_FILE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {
|
||||
// Read any Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
Serial.println();
|
||||
Serial.println(F("type:"));
|
||||
Serial.println(F("b - open existing bin file"));
|
||||
Serial.println(F("c - convert file to csv"));
|
||||
Serial.println(F("d - dump data to Serial"));
|
||||
Serial.println(F("e - overrun error details"));
|
||||
Serial.println(F("l - list files"));
|
||||
Serial.println(F("r - record data"));
|
||||
Serial.println(F("t - test without logging"));
|
||||
while(!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
#if WDT_YIELD_TIME_MICROS
|
||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||
while (true) {}
|
||||
#endif
|
||||
|
||||
char c = tolower(Serial.read());
|
||||
|
||||
// Discard extra Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, LOW);
|
||||
}
|
||||
if (c == 'b') {
|
||||
openBinFile();
|
||||
} else if (c == 'c') {
|
||||
binaryToCsv();
|
||||
} else if (c == 'd') {
|
||||
dumpData();
|
||||
} else if (c == 'e') {
|
||||
checkOverrun();
|
||||
} else if (c == 'l') {
|
||||
Serial.println(F("\nls:"));
|
||||
sd.ls(&Serial, LS_SIZE);
|
||||
} else if (c == 'r') {
|
||||
logData();
|
||||
} else if (c == 't') {
|
||||
testSensor();
|
||||
} else {
|
||||
Serial.println(F("Invalid entry"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// User data functions. Modify these functions for your data items.
|
||||
#include "UserTypes.h"
|
||||
#include "Wire.h"
|
||||
#include "I2Cdev.h"
|
||||
#include "MPU6050.h"
|
||||
//------------------------------------------------------------------------------
|
||||
MPU6050 mpu;
|
||||
static uint32_t startMicros;
|
||||
// Acquire a data record.
|
||||
void acquireData(data_t* data) {
|
||||
data->time = micros();
|
||||
mpu.getMotion6(&data->ax, &data->ay, &data->az,
|
||||
&data->gx, &data->gy, &data->gz);
|
||||
}
|
||||
|
||||
// setup AVR I2C
|
||||
void userSetup() {
|
||||
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
|
||||
Wire.begin();
|
||||
Wire.setClock(400000);
|
||||
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
|
||||
Fastwire::setup(400, true);
|
||||
#endif
|
||||
mpu.initialize();
|
||||
}
|
||||
|
||||
// Print a data record.
|
||||
void printData(Print* pr, data_t* data) {
|
||||
if (startMicros == 0) {
|
||||
startMicros = data->time;
|
||||
}
|
||||
pr->print(data->time- startMicros);
|
||||
pr->write(',');
|
||||
pr->print(data->ax);
|
||||
pr->write(',');
|
||||
pr->print(data->ay);
|
||||
pr->write(',');
|
||||
pr->print(data->az);
|
||||
pr->write(',');
|
||||
pr->print(data->gx);
|
||||
pr->write(',');
|
||||
pr->print(data->gy);
|
||||
pr->write(',');
|
||||
pr->println(data->gz);
|
||||
}
|
||||
|
||||
// Print data header.
|
||||
void printHeader(Print* pr) {
|
||||
startMicros = 0;
|
||||
pr->println(F("micros,ax,ay,az,gx,gy,gz"));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef UserTypes_h
|
||||
#define UserTypes_h
|
||||
#include "Arduino.h"
|
||||
#define FILE_BASE_NAME "mpuraw"
|
||||
struct data_t {
|
||||
unsigned long time;
|
||||
int16_t ax;
|
||||
int16_t ay;
|
||||
int16_t az;
|
||||
int16_t gx;
|
||||
int16_t gy;
|
||||
int16_t gz;
|
||||
};
|
||||
void acquireData(data_t* data);
|
||||
void printData(Print* pr, data_t* data);
|
||||
void printHeader(Print* pr);
|
||||
void userSetup();
|
||||
#endif // UserTypes_h
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* This program is a simple Print benchmark.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#include "FreeStack.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(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
uint32_t maxLatency;
|
||||
uint32_t minLatency;
|
||||
uint32_t totalLatency;
|
||||
|
||||
// Read any existing Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
delay(400); // catch Due reset problem
|
||||
|
||||
cout << F("FreeStack: ") << FreeStack() << endl;
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
|
||||
|
||||
cout << F("Starting print test. Please wait.\n\n");
|
||||
|
||||
// do write test
|
||||
for (int test = 0; test < 6; test++) {
|
||||
char fileName[13] = "bench0.txt";
|
||||
fileName[5] = '0' + test;
|
||||
// open or create file - truncate existing file.
|
||||
if (!file.open(fileName, O_RDWR | O_CREAT | O_TRUNC)) {
|
||||
error("open failed");
|
||||
}
|
||||
maxLatency = 0;
|
||||
minLatency = 999999;
|
||||
totalLatency = 0;
|
||||
switch(test) {
|
||||
case 0:
|
||||
cout << F("Test of println(uint16_t)\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
cout << F("Test of printField(uint16_t, char)\n");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
cout << F("Test of println(uint32_t)\n");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
cout << F("Test of printField(uint32_t, char)\n");
|
||||
break;
|
||||
case 4:
|
||||
cout << F("Test of println(float)\n");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
cout << F("Test of printField(float, char)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
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(12345678UL + i);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
file.printField(12345678UL + i, '\n');
|
||||
break;
|
||||
|
||||
case 4:
|
||||
file.println((float)0.01*i);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
file.printField((float)0.01*i, '\n');
|
||||
break;
|
||||
}
|
||||
if (file.getWriteError()) {
|
||||
error("write failed");
|
||||
}
|
||||
m = micros() - m;
|
||||
if (maxLatency < m) {
|
||||
maxLatency = m;
|
||||
}
|
||||
if (minLatency > m) {
|
||||
minLatency = m;
|
||||
}
|
||||
totalLatency += m;
|
||||
}
|
||||
file.close();
|
||||
t = millis() - t;
|
||||
double s = file.fileSize();
|
||||
cout << F("Time ") << 0.001*t << F(" sec\n");
|
||||
cout << F("File size ") << 0.001*s << F(" KB\n");
|
||||
cout << F("Write ") << s/t << F(" KB/sec\n");
|
||||
cout << F("Maximum latency: ") << maxLatency;
|
||||
cout << F(" usec, Minimum Latency: ") << minLatency;
|
||||
cout << F(" usec, Avg Latency: ");
|
||||
cout << totalLatency/N_PRINT << F(" usec\n\n");
|
||||
}
|
||||
cout << F("Done!\n\n");
|
||||
}
|
||||
180
libraries/SdFat/examples/examplesV1/RawWrite/RawWrite.ino
Normal file
180
libraries/SdFat/examples/examplesV1/RawWrite/RawWrite.ino
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* This program illustrates raw write functions in SdFat that
|
||||
* can be used for high speed data logging.
|
||||
*
|
||||
* This program simulates logging from a source that produces
|
||||
* data at a constant rate of RATE_KB_PER_SEC.
|
||||
*
|
||||
* Note: Apps should create a very large file then truncates it
|
||||
* to the length that is used for a logging. 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 <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
// SD chip select pin
|
||||
const uint8_t chipSelect = SS;
|
||||
|
||||
const uint32_t RATE_KB_PER_SEC = 100;
|
||||
|
||||
const uint32_t TEST_TIME_SEC = 100;
|
||||
|
||||
// Time between printing progress dots
|
||||
const uint32_t DOT_TIME_MS = 5000UL;
|
||||
|
||||
// number of blocks in the contiguous file
|
||||
const uint32_t BLOCK_COUNT = (1000*RATE_KB_PER_SEC*TEST_TIME_SEC + 511)/512;
|
||||
|
||||
// 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(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {
|
||||
// Read any extra Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
cout << F("FreeStack: ") << FreeStack() << endl;
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// delete possible existing file
|
||||
sd.remove("RawWrite.txt");
|
||||
|
||||
// create a contiguous file
|
||||
if (!file.createContiguous("RawWrite.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 << F("Start raw write of ") << file.fileSize()/1000UL << F(" KB\n");
|
||||
cout << F("Target rate: ") << RATE_KB_PER_SEC << F(" KB/sec\n");
|
||||
cout << F("Target time: ") << TEST_TIME_SEC << F(" 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
|
||||
|
||||
delay(1000);
|
||||
uint32_t dotCount = 0;
|
||||
uint32_t maxQueuePrint = 0;
|
||||
uint32_t maxWriteTime = 0;
|
||||
uint32_t minWriteTime = 9999999;
|
||||
uint32_t totalWriteTime = 0;
|
||||
uint32_t maxQueueSize = 0;
|
||||
uint32_t nWrite = 0;
|
||||
uint32_t b = 0;
|
||||
|
||||
// write data
|
||||
uint32_t startTime = millis();
|
||||
while (nWrite < BLOCK_COUNT) {
|
||||
uint32_t nProduced = RATE_KB_PER_SEC*(millis() - startTime)/512UL;
|
||||
uint32_t queueSize = nProduced - nWrite;
|
||||
if (queueSize == 0) continue;
|
||||
if (queueSize > maxQueueSize) {
|
||||
maxQueueSize = queueSize;
|
||||
}
|
||||
if ((millis() - startTime - dotCount*DOT_TIME_MS) > DOT_TIME_MS) {
|
||||
if (maxQueueSize != maxQueuePrint) {
|
||||
cout << F("\nQ: ") << maxQueueSize << endl;
|
||||
maxQueuePrint = maxQueueSize;
|
||||
} else {
|
||||
cout << ".";
|
||||
if (++dotCount%10 == 0) {
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
totalWriteTime += tw;
|
||||
// check for max write time
|
||||
if (tw > maxWriteTime) {
|
||||
maxWriteTime = tw;
|
||||
}
|
||||
if (tw < minWriteTime) {
|
||||
minWriteTime = tw;
|
||||
}
|
||||
nWrite++;
|
||||
}
|
||||
uint32_t endTime = millis();
|
||||
uint32_t avgWriteTime = totalWriteTime/BLOCK_COUNT;
|
||||
// end multiple block write mode
|
||||
if (!sd.card()->writeStop()) {
|
||||
error("writeStop failed");
|
||||
}
|
||||
|
||||
cout << F("\nDone\n");
|
||||
cout << F("maxQueueSize: ") << maxQueueSize << endl;
|
||||
cout << F("Elapsed time: ") << setprecision(3)<< 1.e-3*(endTime - startTime);
|
||||
cout << F(" seconds\n");
|
||||
cout << F("Min block write time: ") << minWriteTime << F(" micros\n");
|
||||
cout << F("Max block write time: ") << maxWriteTime << F(" micros\n");
|
||||
cout << F("Avg block write time: ") << avgWriteTime << F(" micros\n");
|
||||
// close file for next pass of loop
|
||||
file.close();
|
||||
Serial.println();
|
||||
}
|
||||
212
libraries/SdFat/examples/examplesV1/ReadCsv/ReadCsv.ino
Normal file
212
libraries/SdFat/examples/examplesV1/ReadCsv/ReadCsv.ino
Normal file
@@ -0,0 +1,212 @@
|
||||
|
||||
// Functions to read a CSV text file one field at a time.
|
||||
//
|
||||
#include <limits.h>
|
||||
#include <SPI.h>
|
||||
|
||||
// next line for SD.h
|
||||
//#include <SD.h>
|
||||
|
||||
// next two lines for SdFat
|
||||
#include <SdFat.h>
|
||||
SdFat SD;
|
||||
|
||||
#define CS_PIN SS
|
||||
|
||||
// example can use comma or semicolon
|
||||
#define CSV_DELIM ','
|
||||
|
||||
File file;
|
||||
|
||||
/*
|
||||
* Read a file one field at a time.
|
||||
*
|
||||
* file - File to read.
|
||||
*
|
||||
* str - Character array for the field.
|
||||
*
|
||||
* size - Size of str array.
|
||||
*
|
||||
* delim - csv delimiter.
|
||||
*
|
||||
* return - negative value for failure.
|
||||
* delimiter, '\n' or zero(EOF) for success.
|
||||
*/
|
||||
int csvReadText(File* file, char* str, size_t size, char delim) {
|
||||
char ch;
|
||||
int rtn;
|
||||
size_t n = 0;
|
||||
while (true) {
|
||||
// check for EOF
|
||||
if (!file->available()) {
|
||||
rtn = 0;
|
||||
break;
|
||||
}
|
||||
if (file->read(&ch, 1) != 1) {
|
||||
// read error
|
||||
rtn = -1;
|
||||
break;
|
||||
}
|
||||
// Delete CR.
|
||||
if (ch == '\r') {
|
||||
continue;
|
||||
}
|
||||
if (ch == delim || ch == '\n') {
|
||||
rtn = ch;
|
||||
break;
|
||||
}
|
||||
if ((n + 1) >= size) {
|
||||
// string too long
|
||||
rtn = -2;
|
||||
n--;
|
||||
break;
|
||||
}
|
||||
str[n++] = ch;
|
||||
}
|
||||
str[n] = '\0';
|
||||
return rtn;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
int csvReadInt32(File* file, int32_t* num, char delim) {
|
||||
char buf[20];
|
||||
char* ptr;
|
||||
int rtn = csvReadText(file, buf, sizeof(buf), delim);
|
||||
if (rtn < 0) return rtn;
|
||||
*num = strtol(buf, &ptr, 10);
|
||||
if (buf == ptr) return -3;
|
||||
while(isspace(*ptr)) ptr++;
|
||||
return *ptr == 0 ? rtn : -4;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
int csvReadInt16(File* file, int16_t* num, char delim) {
|
||||
int32_t tmp;
|
||||
int rtn = csvReadInt32(file, &tmp, delim);
|
||||
if (rtn < 0) return rtn;
|
||||
if (tmp < INT_MIN || tmp > INT_MAX) return -5;
|
||||
*num = tmp;
|
||||
return rtn;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
int csvReadUint32(File* file, uint32_t* num, char delim) {
|
||||
char buf[20];
|
||||
char* ptr;
|
||||
int rtn = csvReadText(file, buf, sizeof(buf), delim);
|
||||
if (rtn < 0) return rtn;
|
||||
*num = strtoul(buf, &ptr, 10);
|
||||
if (buf == ptr) return -3;
|
||||
while(isspace(*ptr)) ptr++;
|
||||
return *ptr == 0 ? rtn : -4;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
int csvReadUint16(File* file, uint16_t* num, char delim) {
|
||||
uint32_t tmp;
|
||||
int rtn = csvReadUint32(file, &tmp, delim);
|
||||
if (rtn < 0) return rtn;
|
||||
if (tmp > UINT_MAX) return -5;
|
||||
*num = tmp;
|
||||
return rtn;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
int csvReadDouble(File* file, double* num, char delim) {
|
||||
char buf[20];
|
||||
char* ptr;
|
||||
int rtn = csvReadText(file, buf, sizeof(buf), delim);
|
||||
if (rtn < 0) return rtn;
|
||||
*num = strtod(buf, &ptr);
|
||||
if (buf == ptr) return -3;
|
||||
while(isspace(*ptr)) ptr++;
|
||||
return *ptr == 0 ? rtn : -4;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
int csvReadFloat(File* file, float* num, char delim) {
|
||||
double tmp;
|
||||
int rtn = csvReadDouble(file, &tmp, delim);
|
||||
if (rtn < 0)return rtn;
|
||||
// could test for too large.
|
||||
*num = tmp;
|
||||
return rtn;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize the SD.
|
||||
if (!SD.begin(CS_PIN)) {
|
||||
Serial.println("begin failed");
|
||||
return;
|
||||
}
|
||||
// Remove existing file.
|
||||
SD.remove("READTEST.TXT");
|
||||
|
||||
// Create the file.
|
||||
file = SD.open("READTEST.TXT", FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("open failed");
|
||||
return;
|
||||
}
|
||||
// Write test data.
|
||||
file.print(F(
|
||||
#if CSV_DELIM == ','
|
||||
"36,23.20,20.70,57.60,79.50,01:08:14,23.06.16\r\n"
|
||||
"37,23.21,20.71,57.61,79.51,02:08:14,23.07.16\r\n"
|
||||
#elif CSV_DELIM == ';'
|
||||
"36;23.20;20.70;57.60;79.50;01:08:14;23.06.16\r\n"
|
||||
"37;23.21;20.71;57.61;79.51;02:08:14;23.07.16\r\n"
|
||||
#else
|
||||
#error "Bad CSV_DELIM"
|
||||
#endif
|
||||
));
|
||||
|
||||
// Rewind the file for read.
|
||||
file.seek(0);
|
||||
|
||||
// Read the file and print fields.
|
||||
int16_t tcalc;
|
||||
float t1, t2, h1, h2;
|
||||
// Must be dim 9 to allow for zero byte.
|
||||
char timeS[9], dateS[9];
|
||||
while (file.available()) {
|
||||
if (csvReadInt16(&file, &tcalc, CSV_DELIM) != CSV_DELIM
|
||||
|| csvReadFloat(&file, &t1, CSV_DELIM) != CSV_DELIM
|
||||
|| csvReadFloat(&file, &t2, CSV_DELIM) != CSV_DELIM
|
||||
|| csvReadFloat(&file, &h1, CSV_DELIM) != CSV_DELIM
|
||||
|| csvReadFloat(&file, &h2, CSV_DELIM) != CSV_DELIM
|
||||
|| csvReadText(&file, timeS, sizeof(timeS), CSV_DELIM) != CSV_DELIM
|
||||
|| csvReadText(&file, dateS, sizeof(dateS), CSV_DELIM) != '\n') {
|
||||
Serial.println("read error");
|
||||
int ch;
|
||||
int nr = 0;
|
||||
// print part of file after error.
|
||||
while ((ch = file.read()) > 0 && nr++ < 100) {
|
||||
Serial.write(ch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Serial.print(tcalc);
|
||||
Serial.print(CSV_DELIM);
|
||||
Serial.print(t1);
|
||||
Serial.print(CSV_DELIM);
|
||||
Serial.print(t2);
|
||||
Serial.print(CSV_DELIM);
|
||||
Serial.print(h1);
|
||||
Serial.print(CSV_DELIM);
|
||||
Serial.print(h2);
|
||||
Serial.print(CSV_DELIM);
|
||||
Serial.print(timeS);
|
||||
Serial.print(CSV_DELIM);
|
||||
Serial.println(dateS);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// Read a two dimensional array from a CSV file.
|
||||
//
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#define CS_PIN SS
|
||||
|
||||
// 5 X 4 array
|
||||
#define ROW_DIM 5
|
||||
#define COL_DIM 4
|
||||
|
||||
SdFat SD;
|
||||
File file;
|
||||
|
||||
/*
|
||||
* Read a file one field at a time.
|
||||
*
|
||||
* file - File to read.
|
||||
*
|
||||
* str - Character array for the field.
|
||||
*
|
||||
* size - Size of str array.
|
||||
*
|
||||
* delim - String containing field delimiters.
|
||||
*
|
||||
* return - length of field including terminating delimiter.
|
||||
*
|
||||
* Note, the last character of str will not be a delimiter if
|
||||
* a read error occurs, the field is too long, or the file
|
||||
* does not end with a delimiter. Consider this an error
|
||||
* if not at end-of-file.
|
||||
*
|
||||
*/
|
||||
size_t readField(File* file, char* str, size_t size, const char* delim) {
|
||||
char ch;
|
||||
size_t n = 0;
|
||||
while ((n + 1) < size && file->read(&ch, 1) == 1) {
|
||||
// Delete CR.
|
||||
if (ch == '\r') {
|
||||
continue;
|
||||
}
|
||||
str[n++] = ch;
|
||||
if (strchr(delim, ch)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
str[n] = '\0';
|
||||
return n;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
#define errorHalt(msg) {Serial.println(F(msg)); while (true) {}}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize the SD.
|
||||
if (!SD.begin(CS_PIN)) {
|
||||
errorHalt("begin failed");
|
||||
}
|
||||
// Create or open the file.
|
||||
file = SD.open("READNUM.TXT", FILE_WRITE);
|
||||
if (!file) {
|
||||
errorHalt("open failed");
|
||||
}
|
||||
// Rewind file so test data is not appended.
|
||||
file.rewind();
|
||||
|
||||
// Write test data.
|
||||
file.print(F(
|
||||
"11,12,13,14\r\n"
|
||||
"21,22,23,24\r\n"
|
||||
"31,32,33,34\r\n"
|
||||
"41,42,43,44\r\n"
|
||||
"51,52,53,54" // Allow missing endl at eof.
|
||||
));
|
||||
|
||||
// Rewind the file for read.
|
||||
file.rewind();
|
||||
|
||||
// Array for data.
|
||||
int array[ROW_DIM][COL_DIM];
|
||||
int i = 0; // First array index.
|
||||
int j = 0; // Second array index
|
||||
size_t n; // Length of returned field with delimiter.
|
||||
char str[20]; // Must hold longest field with delimiter and zero byte.
|
||||
char *ptr; // Test for valid field.
|
||||
|
||||
// Read the file and store the data.
|
||||
|
||||
for (i = 0; i < ROW_DIM; i++) {
|
||||
for (j = 0; j < COL_DIM; j++) {
|
||||
n = readField(&file, str, sizeof(str), ",\n");
|
||||
if (n == 0) {
|
||||
errorHalt("Too few lines");
|
||||
}
|
||||
array[i][j] = strtol(str, &ptr, 10);
|
||||
if (ptr == str) {
|
||||
errorHalt("bad number");
|
||||
}
|
||||
while (*ptr == ' ') {
|
||||
ptr++;
|
||||
}
|
||||
if (*ptr != ',' && *ptr != '\n' && *ptr != '\0') {
|
||||
errorHalt("extra characters in field");
|
||||
}
|
||||
if (j < (COL_DIM-1) && str[n-1] != ',') {
|
||||
errorHalt("line with too few fields");
|
||||
}
|
||||
}
|
||||
// Allow missing endl at eof.
|
||||
if (str[n-1] != '\n' && file.available()) {
|
||||
errorHalt("missing endl");
|
||||
}
|
||||
}
|
||||
|
||||
// Print the array.
|
||||
for (i = 0; i < ROW_DIM; i++) {
|
||||
for (j = 0; j < COL_DIM; j++) {
|
||||
if (j) {
|
||||
Serial.print(' ');
|
||||
}
|
||||
Serial.print(array[i][j]);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
Serial.println("Done");
|
||||
file.close();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This example reads a simple CSV, comma-separated values, file.
|
||||
* Each line of the file has a label and three values, a long and two floats.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
// read and print CSV test file
|
||||
void readFile() {
|
||||
long lg = 0;
|
||||
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 << F(
|
||||
"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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// create test file
|
||||
writeFile();
|
||||
|
||||
cout << endl;
|
||||
|
||||
// read and print test
|
||||
readFile();
|
||||
|
||||
cout << "\nDone!" << endl;
|
||||
}
|
||||
void loop() {}
|
||||
81
libraries/SdFat/examples/examplesV1/ReadWrite/ReadWrite.ino
Normal file
81
libraries/SdFat/examples/examplesV1/ReadWrite/ReadWrite.ino
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
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
|
||||
|
||||
created Nov 2010
|
||||
by David A. Mellis
|
||||
modified 9 Apr 2012
|
||||
by Tom Igoe
|
||||
|
||||
This example code is in the public domain.
|
||||
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
//#include <SD.h>
|
||||
#include "SdFat.h"
|
||||
SdFat SD;
|
||||
|
||||
#define SD_CS_PIN SS
|
||||
File myFile;
|
||||
|
||||
void setup() {
|
||||
// Open serial communications and wait for port to open:
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
; // wait for serial port to connect. Needed for native USB port only
|
||||
}
|
||||
|
||||
|
||||
Serial.print("Initializing SD card...");
|
||||
|
||||
if (!SD.begin(SD_CS_PIN)) {
|
||||
Serial.println("initialization failed!");
|
||||
return;
|
||||
}
|
||||
Serial.println("initialization done.");
|
||||
|
||||
// open the file. note that only one file can be open at a time,
|
||||
// so you have to close this one before opening another.
|
||||
myFile = SD.open("test.txt", FILE_WRITE);
|
||||
|
||||
// if the file opened okay, write to it:
|
||||
if (myFile) {
|
||||
Serial.print("Writing to test.txt...");
|
||||
myFile.println("testing 1, 2, 3.");
|
||||
// close the file:
|
||||
myFile.close();
|
||||
Serial.println("done.");
|
||||
} else {
|
||||
// if the file didn't open, print an error:
|
||||
Serial.println("error opening test.txt");
|
||||
}
|
||||
|
||||
// re-open the file for reading:
|
||||
myFile = SD.open("test.txt");
|
||||
if (myFile) {
|
||||
Serial.println("test.txt:");
|
||||
|
||||
// read from the file until there's nothing else in it:
|
||||
while (myFile.available()) {
|
||||
Serial.write(myFile.read());
|
||||
}
|
||||
// close the file:
|
||||
myFile.close();
|
||||
} else {
|
||||
// if the file didn't open, print an error:
|
||||
Serial.println("error opening test.txt");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// nothing happens after setup
|
||||
}
|
||||
|
||||
|
||||
175
libraries/SdFat/examples/examplesV1/STM32Test/STM32Test.ino
Normal file
175
libraries/SdFat/examples/examplesV1/STM32Test/STM32Test.ino
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Example use of two SPI ports on an STM32 board.
|
||||
* Note SPI speed is limited to 18 MHz.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
#error See new Version 2 STM32 example
|
||||
// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes
|
||||
|
||||
// Use first SPI port
|
||||
SdFat sd1;
|
||||
// SdFatEX sd1;
|
||||
const uint8_t SD1_CS = PA4; // chip select for sd1
|
||||
|
||||
// Use second SPI port
|
||||
SPIClass SPI_2(2);
|
||||
SdFat sd2(&SPI_2);
|
||||
// SdFatEX sd2(&SPI_2);
|
||||
|
||||
const uint8_t SD2_CS = PB12; // chip select for sd2
|
||||
|
||||
const uint8_t BUF_DIM = 100;
|
||||
uint8_t buf[BUF_DIM];
|
||||
|
||||
const uint32_t FILE_SIZE = 1000000;
|
||||
const uint16_t NWRITE = FILE_SIZE/BUF_DIM;
|
||||
//------------------------------------------------------------------------------
|
||||
// print error msg, any SD error codes, and halt.
|
||||
// store messages in flash
|
||||
#define errorExit(msg) errorHalt(F(msg))
|
||||
#define initError(msg) initErrorHalt(F(msg))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
}
|
||||
|
||||
// fill buffer with known data
|
||||
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||
buf[i] = i;
|
||||
}
|
||||
|
||||
Serial.println(F("type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
Serial.print(F("FreeStack: "));
|
||||
Serial.println(FreeStack());
|
||||
|
||||
// initialize the first card
|
||||
if (!sd1.begin(SD1_CS, SD_SCK_MHZ(18))) {
|
||||
sd1.initError("sd1:");
|
||||
}
|
||||
// create Dir1 on sd1 if it does not exist
|
||||
if (!sd1.exists("/Dir1")) {
|
||||
if (!sd1.mkdir("/Dir1")) {
|
||||
sd1.errorExit("sd1.mkdir");
|
||||
}
|
||||
}
|
||||
// initialize the second card
|
||||
if (!sd2.begin(SD2_CS, SD_SCK_MHZ(18))) {
|
||||
sd2.initError("sd2:");
|
||||
}
|
||||
// create Dir2 on sd2 if it does not exist
|
||||
if (!sd2.exists("/Dir2")) {
|
||||
if (!sd2.mkdir("/Dir2")) {
|
||||
sd2.errorExit("sd2.mkdir");
|
||||
}
|
||||
}
|
||||
// list root directory on both cards
|
||||
Serial.println(F("------sd1 root-------"));
|
||||
sd1.ls();
|
||||
Serial.println(F("------sd2 root-------"));
|
||||
sd2.ls();
|
||||
|
||||
// make /Dir1 the default directory for sd1
|
||||
if (!sd1.chdir("/Dir1")) {
|
||||
sd1.errorExit("sd1.chdir");
|
||||
}
|
||||
// remove test.bin from /Dir1 directory of sd1
|
||||
if (sd1.exists("test.bin")) {
|
||||
if (!sd1.remove("test.bin")) {
|
||||
sd2.errorExit("remove test.bin");
|
||||
}
|
||||
}
|
||||
// make /Dir2 the default directory for sd2
|
||||
if (!sd2.chdir("/Dir2")) {
|
||||
sd2.errorExit("sd2.chdir");
|
||||
}
|
||||
// remove rename.bin from /Dir2 directory of sd2
|
||||
if (sd2.exists("rename.bin")) {
|
||||
if (!sd2.remove("rename.bin")) {
|
||||
sd2.errorExit("remove rename.bin");
|
||||
}
|
||||
}
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 Dir1-------"));
|
||||
sd1.ls();
|
||||
Serial.println(F("------sd2 Dir2-------"));
|
||||
sd2.ls();
|
||||
Serial.println(F("---------------------"));
|
||||
|
||||
// set the current working directory for open() to sd1
|
||||
sd1.chvol();
|
||||
|
||||
// create or open /Dir1/test.bin and truncate it to zero length
|
||||
SdFile file1;
|
||||
if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) {
|
||||
sd1.errorExit("file1");
|
||||
}
|
||||
Serial.println(F("Writing test.bin to sd1"));
|
||||
|
||||
// write data to /Dir1/test.bin on sd1
|
||||
for (uint16_t i = 0; i < NWRITE; i++) {
|
||||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
sd1.errorExit("sd1.write");
|
||||
}
|
||||
}
|
||||
// set the current working directory for open() to sd2
|
||||
sd2.chvol();
|
||||
|
||||
// create or open /Dir2/copy.bin and truncate it to zero length
|
||||
SdFile file2;
|
||||
if (!file2.open("copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
sd2.errorExit("file2");
|
||||
}
|
||||
Serial.println(F("Copying test.bin to copy.bin"));
|
||||
|
||||
// copy file1 to file2
|
||||
file1.rewind();
|
||||
uint32_t t = millis();
|
||||
|
||||
while (1) {
|
||||
int n = file1.read(buf, sizeof(buf));
|
||||
if (n < 0) {
|
||||
sd1.errorExit("read1");
|
||||
}
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
if ((int)file2.write(buf, n) != n) {
|
||||
sd2.errorExit("write2");
|
||||
}
|
||||
}
|
||||
t = millis() - t;
|
||||
Serial.print(F("File size: "));
|
||||
Serial.println(file2.fileSize());
|
||||
Serial.print(F("Copy time: "));
|
||||
Serial.print(t);
|
||||
Serial.println(F(" millis"));
|
||||
// close test.bin
|
||||
file1.close();
|
||||
file2.close();
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 -------"));
|
||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("------sd2 -------"));
|
||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("---------------------"));
|
||||
Serial.println(F("Renaming copy.bin"));
|
||||
// rename the copy
|
||||
if (!sd2.rename("copy.bin", "rename.bin")) {
|
||||
sd2.errorExit("sd2.rename");
|
||||
}
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 -------"));
|
||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("------sd2 -------"));
|
||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("---------------------"));
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
215
libraries/SdFat/examples/examplesV1/StdioBench/StdioBench.ino
Normal file
215
libraries/SdFat/examples/examplesV1/StdioBench/StdioBench.ino
Normal file
@@ -0,0 +1,215 @@
|
||||
// Benchmark comparing SdFile and StdioStream.
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// Define PRINT_FIELD nonzero to use printField.
|
||||
#define PRINT_FIELD 0
|
||||
|
||||
// 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];
|
||||
const 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 printSize = 0;
|
||||
uint32_t stdioSize = 0;
|
||||
uint32_t printTime = 0;
|
||||
uint32_t stdioTime = 0;
|
||||
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
|
||||
Serial.println(F("Type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
Serial.println(F("Starting test"));
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
|
||||
sd.errorHalt();
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 100; i++) {
|
||||
f[i] = 123.0 + 0.1234*i;
|
||||
}
|
||||
|
||||
for (uint8_t dataType = 0; dataType < 5; dataType++) {
|
||||
for (uint8_t fileType = 0; fileType < 2; fileType++) {
|
||||
if (!fileType) {
|
||||
if (!printFile.open("print.txt", O_RDWR | O_CREAT | O_TRUNC)) {
|
||||
Serial.println(F("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(F("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++) {
|
||||
uint32_t n = i + 1000000000UL;
|
||||
#if PRINT_FIELD
|
||||
stdioFile.printField(n, '\n');
|
||||
#else // PRINT_FIELD
|
||||
stdioFile.println(n);
|
||||
#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(F("fileSize: "));
|
||||
if (printSize != stdioSize) {
|
||||
Serial.print(printSize);
|
||||
Serial.print(F(" != "));
|
||||
}
|
||||
Serial.println(stdioSize);
|
||||
Serial.print(F("print millis: "));
|
||||
Serial.println(printTime);
|
||||
Serial.print(F("stdio millis: "));
|
||||
Serial.println(stdioTime);
|
||||
Serial.print(F("ratio: "));
|
||||
Serial.println((float)printTime/(float)stdioTime);
|
||||
Serial.println();
|
||||
printFile.close();
|
||||
stdioFile.fclose();
|
||||
}
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
void loop() {}
|
||||
162
libraries/SdFat/examples/examplesV1/Timestamp/Timestamp.ino
Normal file
162
libraries/SdFat/examples/examplesV1/Timestamp/Timestamp.ino
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* This program tests the dateTimeCallback() function
|
||||
* and the timestamp() function.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
* date/time values for debug
|
||||
* normally supplied by a real-time clock or GPS
|
||||
*/
|
||||
// date 1-Oct-14
|
||||
uint16_t year = 2014;
|
||||
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) {
|
||||
cout << F("Creation: ");
|
||||
f.printCreateDateTime(&Serial);
|
||||
cout << endl << F("Modify: ");
|
||||
f.printModifyDateTime(&Serial);
|
||||
cout << endl << F("Access: ");
|
||||
f.printAccessDateTime(&Serial);
|
||||
cout << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// remove 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_WRONLY | O_CREAT)) {
|
||||
error("open default.txt failed");
|
||||
}
|
||||
cout << F("\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_WRONLY | O_CREAT)) {
|
||||
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 << F("\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_WRONLY | O_CREAT)) {
|
||||
error("open stamp.txt failed");
|
||||
}
|
||||
// set creation date time
|
||||
if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) {
|
||||
error("set create time failed");
|
||||
}
|
||||
// set write/modification date time
|
||||
if (!file.timestamp(T_WRITE, 2014, 11, 11, 4, 5, 6)) {
|
||||
error("set write time failed");
|
||||
}
|
||||
// set access date
|
||||
if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) {
|
||||
error("set access time failed");
|
||||
}
|
||||
cout << F("\nTimes after timestamp() calls\n");
|
||||
printTimestamps(file);
|
||||
|
||||
file.close();
|
||||
cout << F("\nDone\n");
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
170
libraries/SdFat/examples/examplesV1/TwoCards/TwoCards.ino
Normal file
170
libraries/SdFat/examples/examplesV1/TwoCards/TwoCards.ino
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Warning This example requires extra RAM and may crash on Uno.
|
||||
* Example use of two SD cards.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
SdFat sd1;
|
||||
const uint8_t SD1_CS = 10; // chip select for sd1
|
||||
|
||||
SdFat sd2;
|
||||
const uint8_t SD2_CS = 4; // chip select for sd2
|
||||
|
||||
const uint8_t BUF_DIM = 100;
|
||||
uint8_t buf[BUF_DIM];
|
||||
|
||||
const uint32_t FILE_SIZE = 1000000;
|
||||
const uint32_t NWRITE = FILE_SIZE/BUF_DIM;
|
||||
//------------------------------------------------------------------------------
|
||||
// print error msg, any SD error codes, and halt.
|
||||
// store messages in flash
|
||||
#define errorExit(msg) errorHalt(F(msg))
|
||||
#define initError(msg) initErrorHalt(F(msg))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.print(F("FreeStack: "));
|
||||
|
||||
Serial.println(FreeStack());
|
||||
|
||||
// fill buffer with known data
|
||||
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||
buf[i] = i;
|
||||
}
|
||||
|
||||
Serial.println(F("type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// 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
|
||||
Serial.println(F("------sd1 root-------"));
|
||||
sd1.ls();
|
||||
Serial.println(F("------sd2 root-------"));
|
||||
sd2.ls();
|
||||
|
||||
// make /Dir1 the default directory for sd1
|
||||
if (!sd1.chdir("/Dir1")) {
|
||||
sd1.errorExit("sd1.chdir");
|
||||
}
|
||||
|
||||
// make /Dir2 the default directory for sd2
|
||||
if (!sd2.chdir("/Dir2")) {
|
||||
sd2.errorExit("sd2.chdir");
|
||||
}
|
||||
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 Dir1-------"));
|
||||
sd1.ls();
|
||||
Serial.println(F("------sd2 Dir2-------"));
|
||||
sd2.ls();
|
||||
Serial.println(F("---------------------"));
|
||||
|
||||
// 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");
|
||||
}
|
||||
Serial.println(F("Writing test.bin to sd1"));
|
||||
|
||||
// write data to /Dir1/test.bin on sd1
|
||||
for (uint32_t i = 0; i < NWRITE; i++) {
|
||||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
sd1.errorExit("sd1.write");
|
||||
}
|
||||
}
|
||||
// set the current working directory for open() to sd2
|
||||
sd2.chvol();
|
||||
|
||||
// create or open /Dir2/copy.bin and truncate it to zero length
|
||||
SdFile file2;
|
||||
if (!file2.open("copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
sd2.errorExit("file2");
|
||||
}
|
||||
Serial.println(F("Copying test.bin to copy.bin"));
|
||||
|
||||
// copy file1 to file2
|
||||
file1.rewind();
|
||||
uint32_t t = millis();
|
||||
|
||||
while (1) {
|
||||
int n = file1.read(buf, sizeof(buf));
|
||||
if (n < 0) {
|
||||
sd1.errorExit("read1");
|
||||
}
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
if ((int)file2.write(buf, n) != n) {
|
||||
sd2.errorExit("write2");
|
||||
}
|
||||
}
|
||||
t = millis() - t;
|
||||
Serial.print(F("File size: "));
|
||||
Serial.println(file2.fileSize());
|
||||
Serial.print(F("Copy time: "));
|
||||
Serial.print(t);
|
||||
Serial.println(F(" millis"));
|
||||
// close test.bin
|
||||
file1.close();
|
||||
file2.close();
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 -------"));
|
||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("------sd2 -------"));
|
||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("---------------------"));
|
||||
Serial.println(F("Renaming copy.bin"));
|
||||
// rename the copy
|
||||
if (!sd2.rename("copy.bin", "rename.bin")) {
|
||||
sd2.errorExit("sd2.rename");
|
||||
}
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 -------"));
|
||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("------sd2 -------"));
|
||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("---------------------"));
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This program demonstrates the freeClusterCount() call.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
/*
|
||||
* SD chip select pin. Common values are:
|
||||
*
|
||||
* Arduino Ethernet shield, pin 4.
|
||||
* SparkFun SD shield, pin 8.
|
||||
* Adafruit Datalogging shield, pin 10.
|
||||
* Default SD chip select is the SPI SS pin.
|
||||
*/
|
||||
const uint8_t chipSelect = SS;
|
||||
|
||||
#define TEST_FILE "Cluster.test"
|
||||
// file system
|
||||
SdFat sd;
|
||||
|
||||
// test file
|
||||
SdFile file;
|
||||
|
||||
// Serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
//------------------------------------------------------------------------------
|
||||
void printFreeSpace() {
|
||||
cout << F("freeClusterCount() call time: ");
|
||||
uint32_t m = micros();
|
||||
uint32_t volFree = sd.vol()->freeClusterCount();
|
||||
cout << micros() - m << F(" micros\n");
|
||||
cout << F("freeClusters: ") << volFree << setprecision(3) << endl;
|
||||
float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
|
||||
cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
if (!MAINTAIN_FREE_CLUSTER_COUNT) {
|
||||
cout << F("Please edit SdFatConfig.h and set\n");
|
||||
cout << F("MAINTAIN_FREE_CLUSTER_COUNT nonzero for\n");
|
||||
cout << F("maximum freeClusterCount() performance.\n\n");
|
||||
}
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
// Insure no TEST_FILE.
|
||||
sd.remove(TEST_FILE);
|
||||
|
||||
cout << F("\nFirst call to freeClusterCount scans the FAT.\n\n");
|
||||
printFreeSpace();
|
||||
|
||||
cout << F("Create and write to ") << TEST_FILE << endl;
|
||||
if (!file.open(TEST_FILE, O_WRONLY | O_CREAT)) {
|
||||
sd.errorHalt(F("Create failed"));
|
||||
}
|
||||
file.print(F("Cause a cluster to be allocated"));
|
||||
file.close();
|
||||
|
||||
cout << F("\nSecond freeClusterCount call is faster if\n");
|
||||
cout << F("MAINTAIN_FREE_CLUSTER_COUNT is nonzero.\n\n");
|
||||
|
||||
printFreeSpace();
|
||||
|
||||
cout << F("Remove ") << TEST_FILE << endl << endl;
|
||||
sd.remove(TEST_FILE);
|
||||
printFreeSpace();
|
||||
cout << F("Done") << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
150
libraries/SdFat/examples/examplesV1/dataLogger/dataLogger.ino
Normal file
150
libraries/SdFat/examples/examplesV1/dataLogger/dataLogger.ino
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Simple data logger.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#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 = 1000;
|
||||
|
||||
// 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) sd.errorHalt(F(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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
delay(1000);
|
||||
|
||||
Serial.println(F("Type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// 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_WRONLY | O_CREAT | O_EXCL)) {
|
||||
error("file.open");
|
||||
}
|
||||
// Read any Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && 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 (true) {}
|
||||
}
|
||||
}
|
||||
88
libraries/SdFat/examples/examplesV1/fgets/fgets.ino
Normal file
88
libraries/SdFat/examples/examplesV1/fgets/fgets.ino
Normal file
@@ -0,0 +1,88 @@
|
||||
// Demo of fgets function to read lines from a file.
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void demoFgets() {
|
||||
char line[25];
|
||||
int n;
|
||||
// open test file
|
||||
SdFile rdfile("fgets.txt", O_RDONLY);
|
||||
|
||||
// check for open error
|
||||
if (!rdfile.isOpen()) {
|
||||
error("demoFgets");
|
||||
}
|
||||
|
||||
cout << endl << F(
|
||||
"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_WRONLY | O_CREAT | O_TRUNC);
|
||||
|
||||
// check for open error
|
||||
if (!wrfile.isOpen()) {
|
||||
error("MakeTestFile");
|
||||
}
|
||||
|
||||
// write test file
|
||||
wrfile.print(F(
|
||||
"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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
delay(400); // catch Due reset problem
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
makeTestFile();
|
||||
|
||||
demoFgets();
|
||||
|
||||
cout << F("\nDone\n");
|
||||
}
|
||||
void loop(void) {}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Print a table with various formatting options
|
||||
* Format dates
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
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) {}
|
||||
84
libraries/SdFat/examples/examplesV1/getline/getline.ino
Normal file
84
libraries/SdFat/examples/examplesV1/getline/getline.ino
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.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 << F(
|
||||
"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);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// make the test file
|
||||
makeTestFile();
|
||||
|
||||
// run the example
|
||||
testGetline();
|
||||
cout << "\nDone!\n";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {}
|
||||
43
libraries/SdFat/examples/examplesV1/wipe/wipe.ino
Normal file
43
libraries/SdFat/examples/examplesV1/wipe/wipe.ino
Normal file
@@ -0,0 +1,43 @@
|
||||
// Example to wipe all data from an already formatted SD.
|
||||
#error wipe is not supported in SdFat V2. Use bool format(print_t* pr = nullptr).
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
const int chipSelect = SS;
|
||||
|
||||
SdFat sd;
|
||||
|
||||
void setup() {
|
||||
int c;
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
yield();
|
||||
}
|
||||
Serial.println("Type 'Y' to wipe all data.");
|
||||
while (!Serial.available()) {
|
||||
yield();
|
||||
}
|
||||
c = Serial.read();
|
||||
if (c != 'Y') {
|
||||
sd.errorHalt("Quitting, you did not type 'Y'.");
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
// Use wipe() for no dot progress indicator.
|
||||
if (!sd.wipe(&Serial)) {
|
||||
sd.errorHalt("Wipe failed.");
|
||||
}
|
||||
// Must reinitialize after wipe.
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.errorHalt("Second init failed.");
|
||||
}
|
||||
Serial.println("Done");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user