mirror of
https://git.mirrors.martin98.com/https://github.com/luc-github/ESP3D.git
synced 2025-08-13 20:55:59 +08:00
Add SDFat for ESP32
This commit is contained in:
parent
e7818cb5e6
commit
bbadbc2be3
@ -104,9 +104,9 @@
|
||||
#endif //PIN_RESET_FEATURE
|
||||
|
||||
//SD_DEVICE: to access SD Card files directly instead of access by serial using printer Board FW
|
||||
//ESP_SD_NATIVE 1 //esp32 only for the momment
|
||||
//ESP_SD_NATIVE 1 //esp32 / esp8266
|
||||
//ESP_SDIO 2 //esp32 only
|
||||
//ESP_SDFAT 3 //Not available yet
|
||||
//ESP_SDFAT 3 //esp8266 (same as native) / esp32
|
||||
#define SD_DEVICE ESP_SD_NATIVE
|
||||
|
||||
//FILESYSTEM_TIMESTAMP_FEATURE: allow to get last write time from FILESYSTEM files
|
||||
@ -122,7 +122,7 @@
|
||||
//ESP_SPIFFS_FILESYSTEM 0
|
||||
//ESP_FAT_FILESYSTEM 1
|
||||
//ESP_LITTLEFS_FILESYSTEM 2
|
||||
#define FILESYSTEM_FEATURE ESP_SPIFFS_FILESYSTEM
|
||||
#define FILESYSTEM_FEATURE ESP_FAT_FILESYSTEM
|
||||
|
||||
//DIRECT_PIN_FEATURE: allow to access pin using ESP201 command
|
||||
#define DIRECT_PIN_FEATURE
|
||||
|
@ -26,11 +26,14 @@
|
||||
#endif //SD_TIMESTAMP_FEATURE
|
||||
|
||||
#define ESP_MAX_SD_OPENHANDLE 4
|
||||
#if (SD_DEVICE == ESP_SD_NATIVE) && defined (ARDUINO_ARCH_ESP8266)
|
||||
#if ((SD_DEVICE == ESP_SD_NATIVE) || (SD_DEVICE == ESP_SDFAT)) && defined (ARDUINO_ARCH_ESP8266)
|
||||
#define FS_NO_GLOBALS
|
||||
#define NO_GLOBAL_SD
|
||||
#include "SdFat.h"
|
||||
sdfat::File tSDFile_handle[ESP_MAX_SD_OPENHANDLE];
|
||||
#elif (SD_DEVICE == ESP_SDFAT) && defined (ARDUINO_ARCH_ESP32)
|
||||
#include "SdFat.h"
|
||||
File tSDFile_handle[ESP_MAX_SD_OPENHANDLE];
|
||||
#else
|
||||
#include <FS.h>
|
||||
File tSDFile_handle[ESP_MAX_SD_OPENHANDLE];
|
||||
|
@ -19,7 +19,7 @@ sd_native_esp8266.cpp - ESP3D sd support class
|
||||
*/
|
||||
#include "../../../include/esp3d_config.h"
|
||||
#if defined (ARDUINO_ARCH_ESP8266) && defined(SD_DEVICE)
|
||||
#if (SD_DEVICE == ESP_SD_NATIVE)
|
||||
#if (SD_DEVICE == ESP_SD_NATIVE) || (SD_DEVICE == ESP_SDFAT)
|
||||
#define FS_NO_GLOBALS
|
||||
#include "../esp_sd.h"
|
||||
#include "../../../core/genLinkedList.h"
|
||||
@ -70,7 +70,7 @@ bool ESP_SD::begin()
|
||||
if (_spi_speed_divider <= 0) {
|
||||
_spi_speed_divider = 1;
|
||||
}
|
||||
if (getState(true) == ESP_SDCARD_IDLE){
|
||||
if (getState(true) == ESP_SDCARD_IDLE) {
|
||||
freeBytes();
|
||||
}
|
||||
return _started;
|
||||
@ -97,7 +97,7 @@ uint64_t ESP_SD::usedBytes()
|
||||
uint64_t ESP_SD::freeBytes()
|
||||
{
|
||||
static uint64_t volFree;
|
||||
if (_sizechanged){
|
||||
if (_sizechanged) {
|
||||
volFree = SD.vol()->freeClusterCount();
|
||||
_sizechanged = false;
|
||||
}
|
||||
@ -268,12 +268,16 @@ ESP_SDFile::ESP_SDFile(void* handle, bool isdir, bool iswritemode, const char *
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//todo need also to add short filename
|
||||
const char* ESP_SDFile::shortname() const
|
||||
{
|
||||
//static char shorname[13];
|
||||
//entry.getSFN(tmps);
|
||||
return _filename.c_str();
|
||||
static char sname[13];
|
||||
File ftmp = SD.open(_filename.c_str());
|
||||
if (ftmp) {
|
||||
ftmp.getSFN(sname);
|
||||
ftmp.close();
|
||||
return sname;
|
||||
} else return _name.c_str();
|
||||
}
|
||||
|
||||
void ESP_SDFile::close()
|
||||
@ -306,12 +310,14 @@ ESP_SDFile ESP_SDFile::openNextFile()
|
||||
return ESP_SDFile();
|
||||
}
|
||||
sdfat::File tmp = tSDFile_handle[_index].openNextFile();
|
||||
if (tmp){
|
||||
if (tmp) {
|
||||
char tmps[255];
|
||||
tmp.getName(tmps,254);
|
||||
log_esp3d("tmp name :%s %s", tmps, (tmp.isDir())?"isDir":"isFile");
|
||||
String s = _filename ;
|
||||
if (s!="/")s+="/";
|
||||
if (s!="/") {
|
||||
s+="/";
|
||||
}
|
||||
s += tmps;
|
||||
ESP_SDFile esptmp(&tmp, tmp.isDir(),false, s.c_str());
|
||||
esptmp.close();
|
||||
|
333
esp3d/src/modules/filesystem/sd/sd_sdfat_esp32.cpp
Normal file
333
esp3d/src/modules/filesystem/sd/sd_sdfat_esp32.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
sd_native_esp8266.cpp - ESP3D sd support class
|
||||
|
||||
Copyright (c) 2014 Luc Lebosse. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "../../../include/esp3d_config.h"
|
||||
#if defined (ARDUINO_ARCH_ESP32) && defined(SD_DEVICE)
|
||||
#if (SD_DEVICE == ESP_SDFAT)
|
||||
#include "../esp_sd.h"
|
||||
#include "../../../core/genLinkedList.h"
|
||||
#include "../../../core/settings_esp3d.h"
|
||||
#include "SdFat.h"
|
||||
extern File tSDFile_handle[ESP_MAX_SD_OPENHANDLE];
|
||||
|
||||
//Max Freq Working
|
||||
#define FREQMZ 40
|
||||
SdFat SD;
|
||||
|
||||
uint8_t ESP_SD::getState(bool refresh)
|
||||
{
|
||||
#if defined(ESP_SD_DETECT_PIN) && ESP_SD_DETECT_PIN != -1
|
||||
//no need to go further if SD detect is not correct
|
||||
if (!((digitalRead (ESP_SD_DETECT_PIN) == ESP_SD_DETECT_VALUE) ? true : false)) {
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
return _state;
|
||||
}
|
||||
#endif //ESP_SD_DETECT_PIN
|
||||
//if busy doing something return state
|
||||
if (!((_state == ESP_SDCARD_NOT_PRESENT) || _state == ESP_SDCARD_IDLE)) {
|
||||
return _state;
|
||||
}
|
||||
if (!refresh) {
|
||||
return _state; //to avoid refresh=true + busy to reset SD and waste time
|
||||
}
|
||||
//SD is idle or not detected, let see if still the case
|
||||
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
//using default value for speed ? should be parameter
|
||||
//refresh content if card was removed
|
||||
if (SD.begin((ESP_SD_CS_PIN == -1)?SS:ESP_SD_CS_PIN, SD_SCK_MHZ(FREQMZ/_spi_speed_divider))) {
|
||||
if (SD.card()->cardSize() > 0 ) {
|
||||
_state = ESP_SDCARD_IDLE;
|
||||
}
|
||||
}
|
||||
return _state;
|
||||
}
|
||||
|
||||
bool ESP_SD::begin()
|
||||
{
|
||||
_started = true;
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
_spi_speed_divider = Settings_ESP3D::read_byte(ESP_SD_SPEED_DIV);
|
||||
//sanity check
|
||||
if (_spi_speed_divider <= 0) {
|
||||
_spi_speed_divider = 1;
|
||||
}
|
||||
if (getState(true) == ESP_SDCARD_IDLE) {
|
||||
freeBytes();
|
||||
}
|
||||
return _started;
|
||||
}
|
||||
|
||||
void ESP_SD::end()
|
||||
{
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
_started = false;
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::totalBytes()
|
||||
{
|
||||
uint64_t volTotal = SD.vol()->clusterCount();
|
||||
uint8_t blocks = SD.vol()->blocksPerCluster();
|
||||
return volTotal * blocks * 512;
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::usedBytes()
|
||||
{
|
||||
return totalBytes() - freeBytes();
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::freeBytes()
|
||||
{
|
||||
static uint64_t volFree;
|
||||
if (_sizechanged) {
|
||||
volFree = SD.vol()->freeClusterCount();
|
||||
_sizechanged = false;
|
||||
}
|
||||
uint8_t blocks = SD.vol()->blocksPerCluster();
|
||||
return volFree * blocks * 512;
|
||||
};
|
||||
|
||||
bool ESP_SD::format()
|
||||
{
|
||||
//not available yet
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_SDFile ESP_SD::open(const char* path, uint8_t mode)
|
||||
{
|
||||
//do some check
|
||||
if(((strcmp(path,"/") == 0) && ((mode == ESP_SD_FILE_WRITE) || (mode == ESP_SD_FILE_APPEND))) || (strlen(path) == 0)) {
|
||||
_sizechanged = true;
|
||||
return ESP_SDFile();
|
||||
}
|
||||
// path must start by '/'
|
||||
if (path[0] != '/') {
|
||||
return ESP_SDFile();
|
||||
}
|
||||
if (mode != ESP_SD_FILE_READ) {
|
||||
//check container exists
|
||||
String p = path;
|
||||
p.remove(p.lastIndexOf('/') +1);
|
||||
if (!exists(p.c_str())) {
|
||||
log_esp3d("Error opening: %s", path);
|
||||
return ESP_SDFile();
|
||||
}
|
||||
}
|
||||
File tmp = SD.open(path, (mode == ESP_SD_FILE_READ)?FILE_READ:(mode == ESP_SD_FILE_WRITE)?FILE_WRITE:FILE_WRITE);
|
||||
ESP_SDFile esptmp(&tmp, tmp.isDir(),(mode == ESP_SD_FILE_READ)?false:true, path);
|
||||
return esptmp;
|
||||
}
|
||||
|
||||
bool ESP_SD::exists(const char* path)
|
||||
{
|
||||
bool res = false;
|
||||
//root should always be there if started
|
||||
if (strcmp(path, "/") == 0) {
|
||||
return _started;
|
||||
}
|
||||
res = SD.exists(path);
|
||||
if (!res) {
|
||||
ESP_SDFile root = ESP_SD::open(path, ESP_SD_FILE_READ);
|
||||
if (root) {
|
||||
res = root.isDirectory();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ESP_SD::remove(const char *path)
|
||||
{
|
||||
_sizechanged = true;
|
||||
return SD.remove(path);
|
||||
}
|
||||
|
||||
bool ESP_SD::mkdir(const char *path)
|
||||
{
|
||||
return SD.mkdir(path);
|
||||
}
|
||||
|
||||
bool ESP_SD::rmdir(const char *path)
|
||||
{
|
||||
if (!exists(path)) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
GenLinkedList<String > pathlist;
|
||||
String p = path;
|
||||
pathlist.push(p);
|
||||
while (pathlist.count() > 0) {
|
||||
File dir = SD.open(pathlist.getLast().c_str());
|
||||
dir.rewindDirectory();
|
||||
File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
if (f.isDir()) {
|
||||
candelete = false;
|
||||
String newdir;
|
||||
char tmp[255];
|
||||
f.getName(tmp,254);
|
||||
newdir = tmp;
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = File();
|
||||
} else {
|
||||
char tmp[255];
|
||||
f.getName(tmp,254);
|
||||
_sizechanged = true;
|
||||
SD.remove(tmp);
|
||||
f.close();
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
if (candelete) {
|
||||
if (pathlist.getLast() !="/") {
|
||||
res = SD.rmdir(pathlist.getLast().c_str());
|
||||
}
|
||||
pathlist.pop();
|
||||
}
|
||||
dir.close();
|
||||
}
|
||||
p = String();
|
||||
log_esp3d("count %d", pathlist.count());
|
||||
return res;
|
||||
}
|
||||
|
||||
void ESP_SD::closeAll()
|
||||
{
|
||||
for (uint8_t i = 0; i < ESP_MAX_SD_OPENHANDLE; i++) {
|
||||
tSDFile_handle[i].close();
|
||||
tSDFile_handle[i] = File();
|
||||
}
|
||||
}
|
||||
|
||||
ESP_SDFile::ESP_SDFile(void* handle, bool isdir, bool iswritemode, const char * path)
|
||||
{
|
||||
_isdir = isdir;
|
||||
_dirlist = "";
|
||||
_isfakedir = false;
|
||||
_index = -1;
|
||||
_filename = "";
|
||||
_name = "";
|
||||
#ifdef FILESYSTEM_TIMESTAMP_FEATURE
|
||||
memset (&_lastwrite,0,sizeof(time_t));
|
||||
#endif //FILESYSTEM_TIMESTAMP_FEATURE
|
||||
_iswritemode = iswritemode;
|
||||
_size = 0;
|
||||
if (!handle) {
|
||||
return ;
|
||||
}
|
||||
bool set =false;
|
||||
for (uint8_t i=0; (i < ESP_MAX_SD_OPENHANDLE) && !set; i++) {
|
||||
if (!tSDFile_handle[i]) {
|
||||
tSDFile_handle[i] = *((File*)handle);
|
||||
//filename
|
||||
char tmp[255];
|
||||
tSDFile_handle[i].getName(tmp,254);
|
||||
_filename = path;
|
||||
//name
|
||||
_name = tmp;
|
||||
if (_name.endsWith("/")) {
|
||||
_name.remove( _name.length() - 1,1);
|
||||
_isfakedir = true;
|
||||
_isdir = true;
|
||||
}
|
||||
if (_name[0] == '/') {
|
||||
_name.remove( 0, 1);
|
||||
}
|
||||
int pos = _name.lastIndexOf('/');
|
||||
if (pos != -1) {
|
||||
_name.remove( 0, pos+1);
|
||||
}
|
||||
//size
|
||||
_size = tSDFile_handle[i].size();
|
||||
//time
|
||||
#ifdef FILESYSTEM_TIMESTAMP_FEATURE
|
||||
_lastwrite = tSDFile_handle[i].getLastWrite();
|
||||
#endif //FILESYSTEM_TIMESTAMP_FEATURE
|
||||
_index = i;
|
||||
//log_esp3d("Opening File at index %d",_index);
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//todo need also to add short filename
|
||||
const char* ESP_SDFile::shortname() const
|
||||
{
|
||||
static char sname[13];
|
||||
File ftmp = SD.open(_filename.c_str());
|
||||
if (ftmp) {
|
||||
ftmp.getSFN(sname);
|
||||
ftmp.close();
|
||||
return sname;
|
||||
} else return _name.c_str();
|
||||
}
|
||||
|
||||
void ESP_SDFile::close()
|
||||
{
|
||||
if (_index != -1) {
|
||||
//log_esp3d("Closing File at index %d", _index);
|
||||
tSDFile_handle[_index].close();
|
||||
//reopen if mode = write
|
||||
//udate size + date
|
||||
if (_iswritemode && !_isdir) {
|
||||
File ftmp = SD.open(_filename.c_str());
|
||||
if (ftmp) {
|
||||
_size = ftmp.size();
|
||||
#ifdef FILESYSTEM_TIMESTAMP_FEATURE
|
||||
_lastwrite = ftmp.getLastWrite();
|
||||
#endif //FILESYSTEM_TIMESTAMP_FEATURE
|
||||
ftmp.close();
|
||||
}
|
||||
}
|
||||
tSDFile_handle[_index] = File();
|
||||
//log_esp3d("Closing File at index %d",_index);
|
||||
_index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_SDFile ESP_SDFile::openNextFile()
|
||||
{
|
||||
if ((_index == -1) || !_isdir) {
|
||||
log_esp3d("openNextFile failed");
|
||||
return ESP_SDFile();
|
||||
}
|
||||
File tmp = tSDFile_handle[_index].openNextFile();
|
||||
if (tmp) {
|
||||
char tmps[255];
|
||||
tmp.getName(tmps,254);
|
||||
log_esp3d("tmp name :%s %s", tmps, (tmp.isDir())?"isDir":"isFile");
|
||||
String s = _filename ;
|
||||
if (s!="/") {
|
||||
s+="/";
|
||||
}
|
||||
s += tmps;
|
||||
ESP_SDFile esptmp(&tmp, tmp.isDir(),false, s.c_str());
|
||||
esptmp.close();
|
||||
return esptmp;
|
||||
}
|
||||
return ESP_SDFile();
|
||||
}
|
||||
|
||||
const char * ESP_SD::FilesystemName()
|
||||
{
|
||||
return "SDfat";
|
||||
}
|
||||
|
||||
#endif //SD_DEVICE == ESP_SD_NATIVE
|
||||
#endif //ARCH_ESP32 && SD_DEVICE
|
22
libraries/SdFat-1.1.0/.gitattributes
vendored
Normal file
22
libraries/SdFat-1.1.0/.gitattributes
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
215
libraries/SdFat-1.1.0/.gitignore
vendored
Normal file
215
libraries/SdFat-1.1.0/.gitignore
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
#################
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.pubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
#packages/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
#############
|
||||
## Windows detritus
|
||||
#############
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
||||
*.py[co]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist/
|
||||
build/
|
||||
eggs/
|
||||
parts/
|
||||
var/
|
||||
sdist/
|
||||
develop-eggs/
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
21
libraries/SdFat-1.1.0/LICENSE.md
Normal file
21
libraries/SdFat-1.1.0/LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2011..2017 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.
|
42
libraries/SdFat-1.1.0/README.md
Normal file
42
libraries/SdFat-1.1.0/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
The Arduino SdFat library provides read/write access to FAT16/FAT32
|
||||
file systems on SD/SDHC flash cards.
|
||||
|
||||
SdFat requires Arduino 1.6x or greater.
|
||||
|
||||
Key changes:
|
||||
|
||||
Support for multiple SPI ports now uses a pointer to a SPIClass object.
|
||||
|
||||
See the STM32Test example.
|
||||
```
|
||||
explicit SdFat(SPIClass* spiPort);
|
||||
\\ or
|
||||
explicit SdFatEX(SPIClass* spiPort);
|
||||
```
|
||||
|
||||
Open flags now follow POSIX conventions. O_RDONLY is the same as O_READ and O_WRONLY is the
|
||||
same as O_WRITE. One and only one of of the mode flags, O_RDONLY, O_RDWR, or
|
||||
O_WRONLY is required.
|
||||
|
||||
Open flags for Particle Gen3 and ARM boards are now defined by fcntl.h.
|
||||
See SdFatConfig.h for options.
|
||||
|
||||
Support for particle Gen3 devices.
|
||||
|
||||
New cluster allocation algorithm.
|
||||
|
||||
Please read changes.txt and the html documentation in the extras folder for more information.
|
||||
|
||||
Please report problems as issues.
|
||||
|
||||
A number of configuration options can be set by editing SdFatConfig.h
|
||||
define macros. See the html documentation for details
|
||||
|
||||
Please read the html documentation for this library. Start with
|
||||
html/index.html and read the Main Page. Next go to the Classes tab and
|
||||
read the documentation for the classes SdFat, SdFatEX, SdBaseFile,
|
||||
SdFile, File, StdioStream, ifstream, ofstream, and others.
|
||||
|
||||
Please continue by reading the html documentation in SdFat/extras/html
|
||||
|
||||
Updated 28 Dec 2018
|
@ -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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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!");
|
||||
SysCall::halt();
|
||||
}
|
@ -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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
Serial.println("type any character to start");
|
||||
while (!Serial.available()) {
|
||||
SysCall::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) {
|
||||
SysCall::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"));
|
||||
}
|
30
libraries/SdFat-1.1.0/examples/#attic/SD_Size/SD_Size.ino
Normal file
30
libraries/SdFat-1.1.0/examples/#attic/SD_Size/SD_Size.ino
Normal file
@ -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) {
|
||||
SysCall::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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
Serial.println(F("Type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
SysCall::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-1.1.0/examples/#attic/append/append.ino
Normal file
77
libraries/SdFat-1.1.0/examples/#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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << endl << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
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() {}
|
82
libraries/SdFat-1.1.0/examples/#attic/average/average.ino
Normal file
82
libraries/SdFat-1.1.0/examples/#attic/average/average.ino
Normal file
@ -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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// write the test file
|
||||
writeTestFile();
|
||||
|
||||
// read the test file and calculate the average
|
||||
calcAverage();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
149
libraries/SdFat-1.1.0/examples/#attic/benchSD/benchSD.ino
Normal file
149
libraries/SdFat-1.1.0/examples/#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 (uint16_t i = 0; i < (BUF_SIZE-2); i++) {
|
||||
buf[i] = 'A' + (i % 26);
|
||||
}
|
||||
buf[BUF_SIZE-2] = '\r';
|
||||
buf[BUF_SIZE-1] = '\n';
|
||||
|
||||
Serial.print(F("File size "));
|
||||
Serial.print(FILE_SIZE_MB);
|
||||
Serial.println(F("MB"));
|
||||
Serial.print(F("Buffer size "));
|
||||
Serial.print(BUF_SIZE);
|
||||
Serial.println(F(" bytes"));
|
||||
Serial.println(F("Starting write test. Please wait up to a minute"));
|
||||
|
||||
// do write test
|
||||
uint32_t n = FILE_SIZE/sizeof(buf);
|
||||
maxLatency = 0;
|
||||
minLatency = 999999;
|
||||
totalLatency = 0;
|
||||
uint32_t t = millis();
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
uint32_t m = micros();
|
||||
if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
error("write failed");
|
||||
}
|
||||
m = micros() - m;
|
||||
if (maxLatency < m) {
|
||||
maxLatency = m;
|
||||
}
|
||||
if (minLatency > m) {
|
||||
minLatency = m;
|
||||
}
|
||||
totalLatency += m;
|
||||
}
|
||||
file.flush();
|
||||
t = millis() - t;
|
||||
double s = file.size();
|
||||
Serial.print(F("Write "));
|
||||
Serial.print(s/t);
|
||||
Serial.print(F(" KB/sec\n"));
|
||||
Serial.print(F("Maximum latency: "));
|
||||
Serial.print(maxLatency);
|
||||
Serial.print(F(" usec, Minimum Latency: "));
|
||||
Serial.print(minLatency);
|
||||
Serial.print(F(" usec, Avg Latency: "));
|
||||
Serial.print(totalLatency/n);
|
||||
Serial.print(F(" usec\n\n"));
|
||||
Serial.println(F("Starting read test. Please wait up to a minute"));
|
||||
// do read test
|
||||
file.seek(0);
|
||||
maxLatency = 0;
|
||||
minLatency = 99999;
|
||||
totalLatency = 0;
|
||||
t = millis();
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
buf[BUF_SIZE-1] = 0;
|
||||
uint32_t m = micros();
|
||||
if (file.read(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
error("read failed");
|
||||
}
|
||||
m = micros() - m;
|
||||
if (maxLatency < m) {
|
||||
maxLatency = m;
|
||||
}
|
||||
if (minLatency > m) {
|
||||
minLatency = m;
|
||||
}
|
||||
totalLatency += m;
|
||||
if (buf[BUF_SIZE-1] != '\n') {
|
||||
error("data check");
|
||||
}
|
||||
}
|
||||
t = millis() - t;
|
||||
Serial.print(F("Read "));
|
||||
Serial.print(s/t);
|
||||
Serial.print(F(" KB/sec\n"));
|
||||
Serial.print(F("Maximum latency: "));
|
||||
Serial.print(maxLatency);
|
||||
Serial.print(F(" usec, Minimum Latency: "));
|
||||
Serial.print(minLatency);
|
||||
Serial.print(F(" usec, Avg Latency: "));
|
||||
Serial.print(totalLatency/n);
|
||||
Serial.print(F(" usec\n\n"));
|
||||
Serial.print(F("Done\n\n"));
|
||||
file.close();
|
||||
}
|
@ -0,0 +1,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) {
|
||||
SysCall::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() {}
|
39
libraries/SdFat-1.1.0/examples/#attic/cin_cout/cin_cout.ino
Normal file
39
libraries/SdFat-1.1.0/examples/#attic/cin_cout/cin_cout.ino
Normal file
@ -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) {
|
||||
SysCall::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;
|
||||
}
|
62
libraries/SdFat-1.1.0/examples/#attic/eventlog/eventlog.ino
Normal file
62
libraries/SdFat-1.1.0/examples/#attic/eventlog/eventlog.ino
Normal file
@ -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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// F() stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
makeTestFile();
|
||||
|
||||
demoFgets();
|
||||
|
||||
cout << F("\nDone\n");
|
||||
}
|
||||
void loop() {}
|
51
libraries/SdFat-1.1.0/examples/#attic/readlog/readlog.ino
Normal file
51
libraries/SdFat-1.1.0/examples/#attic/readlog/readlog.ino
Normal file
@ -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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// 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-1.1.0/examples/#attic/readme.txt
Normal file
34
libraries/SdFat-1.1.0/examples/#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,826 @@
|
||||
/**
|
||||
* 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 "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, FILE_BLOCK_COUNT)) {
|
||||
error("writeBegin failed");
|
||||
}
|
||||
// Write metadata.
|
||||
if (!sd.card()->writeData((uint8_t*)&block[0])) {
|
||||
error("Write metadata failed");
|
||||
}
|
||||
// Initialize queues.
|
||||
emptyHead = emptyTail = 0;
|
||||
fullHead = fullTail = 0;
|
||||
|
||||
// Use SdFat buffer for one block.
|
||||
emptyQueue[emptyHead] = (block_t*)cache;
|
||||
emptyHead = queueNext(emptyHead);
|
||||
|
||||
// Put rest of buffers in the empty queue.
|
||||
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
|
||||
emptyQueue[emptyHead] = &block[i];
|
||||
emptyHead = queueNext(emptyHead);
|
||||
}
|
||||
// Give SD time to prepare for big write.
|
||||
delay(1000);
|
||||
Serial.println(F("Logging - type any character to stop"));
|
||||
// Wait for Serial Idle.
|
||||
Serial.flush();
|
||||
delay(10);
|
||||
uint32_t bn = 1;
|
||||
uint32_t t0 = millis();
|
||||
uint32_t t1 = t0;
|
||||
uint32_t overruns = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t maxLatency = 0;
|
||||
|
||||
// Start logging interrupts.
|
||||
adcStart();
|
||||
while (1) {
|
||||
if (fullHead != fullTail) {
|
||||
// Get address of block to write.
|
||||
block_t* pBlock = fullQueue[fullTail];
|
||||
|
||||
// Write block to SD.
|
||||
uint32_t usec = micros();
|
||||
if (!sd.card()->writeData((uint8_t*)pBlock)) {
|
||||
error("write data failed");
|
||||
}
|
||||
usec = micros() - usec;
|
||||
t1 = millis();
|
||||
if (usec > maxLatency) {
|
||||
maxLatency = usec;
|
||||
}
|
||||
count += pBlock->count;
|
||||
|
||||
// Add overruns and possibly light LED.
|
||||
if (pBlock->overrun) {
|
||||
overruns += pBlock->overrun;
|
||||
if (ERROR_LED_PIN >= 0) {
|
||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||
}
|
||||
}
|
||||
// Move block to empty queue.
|
||||
emptyQueue[emptyHead] = pBlock;
|
||||
emptyHead = queueNext(emptyHead);
|
||||
fullTail = queueNext(fullTail);
|
||||
bn++;
|
||||
if (bn == FILE_BLOCK_COUNT) {
|
||||
// File full so stop ISR calls.
|
||||
adcStop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (timerError) {
|
||||
error("Missed timer event - rate too high");
|
||||
}
|
||||
if (Serial.available()) {
|
||||
// Stop ISR calls.
|
||||
adcStop();
|
||||
if (isrBuf != 0 && isrBuf->count >= PIN_COUNT) {
|
||||
// Truncate to last complete sample.
|
||||
isrBuf->count = PIN_COUNT*(isrBuf->count/PIN_COUNT);
|
||||
// Put buffer in full queue.
|
||||
fullQueue[fullHead] = isrBuf;
|
||||
fullHead = queueNext(fullHead);
|
||||
isrBuf = 0;
|
||||
}
|
||||
if (fullHead == fullTail) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sd.card()->writeStop()) {
|
||||
error("writeStop failed");
|
||||
}
|
||||
// Truncate file if recording stopped early.
|
||||
if (bn != FILE_BLOCK_COUNT) {
|
||||
Serial.println(F("Truncating file"));
|
||||
if (!binFile.truncate(512L * bn)) {
|
||||
error("Can't truncate file");
|
||||
}
|
||||
}
|
||||
if (!binFile.rename(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()) {
|
||||
SysCall::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,129 @@
|
||||
/*
|
||||
* Example use of chdir(), ls(), mkdir(), and rmdir().
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
// SD card chip select pin.
|
||||
const uint8_t chipSelect = SS;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// File system object.
|
||||
SdFat sd;
|
||||
|
||||
// Directory file.
|
||||
SdFile root;
|
||||
|
||||
// Use for file creation in folders.
|
||||
SdFile file;
|
||||
|
||||
// Create a Serial output stream.
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
// Buffer for Serial input.
|
||||
char cinBuf[40];
|
||||
|
||||
// Create a serial input stream.
|
||||
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
|
||||
//==============================================================================
|
||||
// Error messages stored in flash.
|
||||
#define error(msg) sd.errorHalt(F(msg))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
delay(1000);
|
||||
|
||||
cout << F("Type any character to start\n");
|
||||
// Wait for input line and discard.
|
||||
cin.readline();
|
||||
cout << endl;
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
if (sd.exists("Folder1")
|
||||
|| sd.exists("Folder1/file1.txt")
|
||||
|| sd.exists("Folder1/File2.txt")) {
|
||||
error("Please remove existing Folder1, file1.txt, and File2.txt");
|
||||
}
|
||||
|
||||
int rootFileCount = 0;
|
||||
if (!root.open("/")) {
|
||||
error("open root failed");
|
||||
}
|
||||
while (file.openNext(&root, O_RDONLY)) {
|
||||
if (!file.isHidden()) {
|
||||
rootFileCount++;
|
||||
}
|
||||
file.close();
|
||||
if (rootFileCount > 10) {
|
||||
error("Too many files in root. Please use an empty SD.");
|
||||
}
|
||||
}
|
||||
if (rootFileCount) {
|
||||
cout << F("\nPlease use an empty SD for best results.\n\n");
|
||||
delay(1000);
|
||||
}
|
||||
// Create a new folder.
|
||||
if (!sd.mkdir("Folder1")) {
|
||||
error("Create Folder1 failed");
|
||||
}
|
||||
cout << F("Created Folder1\n");
|
||||
|
||||
// Create a file in Folder1 using a path.
|
||||
if (!file.open("Folder1/file1.txt", O_WRONLY | O_CREAT)) {
|
||||
error("create Folder1/file1.txt failed");
|
||||
}
|
||||
file.close();
|
||||
cout << F("Created Folder1/file1.txt\n");
|
||||
|
||||
// Change volume working directory to Folder1.
|
||||
if (!sd.chdir("Folder1")) {
|
||||
error("chdir failed for Folder1.\n");
|
||||
}
|
||||
cout << F("chdir to Folder1\n");
|
||||
|
||||
// Create File2.txt in current directory.
|
||||
if (!file.open("File2.txt", O_WRONLY | O_CREAT)) {
|
||||
error("create File2.txt failed");
|
||||
}
|
||||
file.close();
|
||||
cout << F("Created File2.txt in current directory\n");
|
||||
|
||||
cout << F("\nList of files on the SD.\n");
|
||||
sd.ls("/", LS_R);
|
||||
|
||||
// Remove files from current directory.
|
||||
if (!sd.remove("file1.txt") || !sd.remove("File2.txt")) {
|
||||
error("remove failed");
|
||||
}
|
||||
cout << F("\nfile1.txt and File2.txt removed.\n");
|
||||
|
||||
// Change current directory to root.
|
||||
if (!sd.chdir()) {
|
||||
error("chdir to root failed.\n");
|
||||
}
|
||||
|
||||
cout << F("\nList of files on the SD.\n");
|
||||
sd.ls(LS_R);
|
||||
|
||||
// Remove Folder1.
|
||||
if (!sd.rmdir("Folder1")) {
|
||||
error("rmdir for Folder1 failed\n");
|
||||
}
|
||||
|
||||
cout << F("\nFolder1 removed.\n");
|
||||
cout << F("\nList of files on the SD.\n");
|
||||
sd.ls(LS_R);
|
||||
cout << F("Done!\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Nothing happens in loop.
|
||||
void loop() {}
|
102
libraries/SdFat-1.1.0/examples/LongFileName/LongFileName.ino
Normal file
102
libraries/SdFat-1.1.0/examples/LongFileName/LongFileName.ino
Normal file
@ -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()) {
|
||||
SysCall::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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
#if WDT_YIELD_TIME_MICROS
|
||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||
SysCall::halt();
|
||||
#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() {
|
||||
}
|
15
libraries/SdFat-1.1.0/examples/LowLatencyLogger/UserTypes.h
Normal file
15
libraries/SdFat-1.1.0/examples/LowLatencyLogger/UserTypes.h
Normal file
@ -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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
#if WDT_YIELD_TIME_MICROS
|
||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||
SysCall::halt();
|
||||
#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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
#if WDT_YIELD_TIME_MICROS
|
||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||
SysCall::halt();
|
||||
#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
|
60
libraries/SdFat-1.1.0/examples/OpenNext/OpenNext.ino
Normal file
60
libraries/SdFat-1.1.0/examples/OpenNext/OpenNext.ino
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Print size, modify date/time, and name for all files in root.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
// SD default chip select pin.
|
||||
const uint8_t chipSelect = SS;
|
||||
|
||||
// file system object
|
||||
SdFat sd;
|
||||
|
||||
SdFile root;
|
||||
SdFile file;
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
if (!root.open("/")) {
|
||||
sd.errorHalt("open root failed");
|
||||
}
|
||||
// Open next file in root.
|
||||
// Warning, openNext starts at the current directory position
|
||||
// so a rewind of the directory may be required.
|
||||
while (file.openNext(&root, O_RDONLY)) {
|
||||
file.printFileSize(&Serial);
|
||||
Serial.write(' ');
|
||||
file.printModifyDateTime(&Serial);
|
||||
Serial.write(' ');
|
||||
file.printName(&Serial);
|
||||
if (file.isDir()) {
|
||||
// Indicate a directory.
|
||||
Serial.write('/');
|
||||
}
|
||||
Serial.println();
|
||||
file.close();
|
||||
}
|
||||
if (root.getError()) {
|
||||
Serial.println("openNext failed");
|
||||
} else {
|
||||
Serial.println("Done!");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
152
libraries/SdFat-1.1.0/examples/PrintBenchmark/PrintBenchmark.ino
Normal file
152
libraries/SdFat-1.1.0/examples/PrintBenchmark/PrintBenchmark.ino
Normal file
@ -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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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");
|
||||
}
|
161
libraries/SdFat-1.1.0/examples/QuickStart/QuickStart.ino
Normal file
161
libraries/SdFat-1.1.0/examples/QuickStart/QuickStart.ino
Normal file
@ -0,0 +1,161 @@
|
||||
// Quick hardware test for SPI card access.
|
||||
//
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
//
|
||||
// Set DISABLE_CHIP_SELECT to disable a second SPI device.
|
||||
// For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
|
||||
// to 10 to disable the Ethernet controller.
|
||||
const int8_t DISABLE_CHIP_SELECT = -1;
|
||||
//
|
||||
// Test with reduced SPI speed for breadboards. SD_SCK_MHZ(4) will select
|
||||
// the highest speed supported by the board that is not over 4 MHz.
|
||||
// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance.
|
||||
#define SPI_SPEED SD_SCK_MHZ(4)
|
||||
//------------------------------------------------------------------------------
|
||||
// File system object.
|
||||
SdFat sd;
|
||||
|
||||
// Serial streams
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
// input buffer for line
|
||||
char cinBuf[40];
|
||||
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
|
||||
|
||||
// SD card chip select
|
||||
int chipSelect;
|
||||
|
||||
void cardOrSpeed() {
|
||||
cout << F("Try another SD card or reduce the SPI bus speed.\n");
|
||||
cout << F("Edit SPI_SPEED in this program to change it.\n");
|
||||
}
|
||||
|
||||
void reformatMsg() {
|
||||
cout << F("Try reformatting the card. For best results use\n");
|
||||
cout << F("the SdFormatter program in SdFat/examples or download\n");
|
||||
cout << F("and use SDFormatter from www.sdcard.org/downloads.\n");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cout << F("\nSPI pins:\n");
|
||||
cout << F("MISO: ") << int(MISO) << endl;
|
||||
cout << F("MOSI: ") << int(MOSI) << endl;
|
||||
cout << F("SCK: ") << int(SCK) << endl;
|
||||
cout << F("SS: ") << int(SS) << endl;
|
||||
|
||||
if (DISABLE_CHIP_SELECT < 0) {
|
||||
cout << F(
|
||||
"\nBe sure to edit DISABLE_CHIP_SELECT if you have\n"
|
||||
"a second SPI device. For example, with the Ethernet\n"
|
||||
"shield, DISABLE_CHIP_SELECT should be set to 10\n"
|
||||
"to disable the Ethernet controller.\n");
|
||||
}
|
||||
cout << F(
|
||||
"\nSD chip select is the key hardware option.\n"
|
||||
"Common values are:\n"
|
||||
"Arduino Ethernet shield, pin 4\n"
|
||||
"Sparkfun SD shield, pin 8\n"
|
||||
"Adafruit SD shields and modules, pin 10\n");
|
||||
}
|
||||
|
||||
bool firstTry = true;
|
||||
void loop() {
|
||||
// Read any existing Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
if (!firstTry) {
|
||||
cout << F("\nRestarting\n");
|
||||
}
|
||||
firstTry = false;
|
||||
|
||||
cout << F("\nEnter the chip select pin number: ");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cin.readline();
|
||||
if (cin >> chipSelect) {
|
||||
cout << chipSelect << endl;
|
||||
} else {
|
||||
cout << F("\nInvalid pin number\n");
|
||||
return;
|
||||
}
|
||||
if (DISABLE_CHIP_SELECT < 0) {
|
||||
cout << F(
|
||||
"\nAssuming the SD is the only SPI device.\n"
|
||||
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
|
||||
} else {
|
||||
cout << F("\nDisabling SPI device on pin ");
|
||||
cout << int(DISABLE_CHIP_SELECT) << endl;
|
||||
pinMode(DISABLE_CHIP_SELECT, OUTPUT);
|
||||
digitalWrite(DISABLE_CHIP_SELECT, HIGH);
|
||||
}
|
||||
if (!sd.begin(chipSelect, SPI_SPEED)) {
|
||||
if (sd.card()->errorCode()) {
|
||||
cout << F(
|
||||
"\nSD initialization failed.\n"
|
||||
"Do not reformat the card!\n"
|
||||
"Is the card correctly inserted?\n"
|
||||
"Is chipSelect set to the correct value?\n"
|
||||
"Does another SPI device need to be disabled?\n"
|
||||
"Is there a wiring/soldering problem?\n");
|
||||
cout << F("\nerrorCode: ") << hex << showbase;
|
||||
cout << int(sd.card()->errorCode());
|
||||
cout << F(", errorData: ") << int(sd.card()->errorData());
|
||||
cout << dec << noshowbase << endl;
|
||||
return;
|
||||
}
|
||||
if (sd.vol()->fatType() == 0) {
|
||||
cout << F("Can't find a valid FAT16/FAT32 partition.\n");
|
||||
reformatMsg();
|
||||
return;
|
||||
}
|
||||
cout << F("begin failed, can't determine error type\n");
|
||||
return;
|
||||
}
|
||||
cout << F("\nCard successfully initialized.\n");
|
||||
cout << endl;
|
||||
|
||||
uint32_t size = sd.card()->cardSize();
|
||||
if (size == 0) {
|
||||
cout << F("Can't determine the card size.\n");
|
||||
cardOrSpeed();
|
||||
return;
|
||||
}
|
||||
uint32_t sizeMB = 0.000512 * size + 0.5;
|
||||
cout << F("Card size: ") << sizeMB;
|
||||
cout << F(" MB (MB = 1,000,000 bytes)\n");
|
||||
cout << endl;
|
||||
cout << F("Volume is FAT") << int(sd.vol()->fatType());
|
||||
cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster();
|
||||
cout << endl << endl;
|
||||
|
||||
cout << F("Files found (date time size name):\n");
|
||||
sd.ls(LS_R | LS_DATE | LS_SIZE);
|
||||
|
||||
if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64)
|
||||
|| (sizeMB < 2200 && sd.vol()->fatType() == 32)) {
|
||||
cout << F("\nThis card should be reformatted for best performance.\n");
|
||||
cout << F("Use a cluster size of 32 KB for cards larger than 1 GB.\n");
|
||||
cout << F("Only cards larger than 2 GB should be formatted FAT32.\n");
|
||||
reformatMsg();
|
||||
return;
|
||||
}
|
||||
// Read any extra Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
cout << F("\nSuccess! Type any character to restart.\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
}
|
180
libraries/SdFat-1.1.0/examples/RawWrite/RawWrite.ino
Normal file
180
libraries/SdFat-1.1.0/examples/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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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-1.1.0/examples/ReadCsv/ReadCsv.ino
Normal file
212
libraries/SdFat-1.1.0/examples/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() {
|
||||
}
|
||||
|
139
libraries/SdFat-1.1.0/examples/ReadCsvArray/ReadCsvArray.ino
Normal file
139
libraries/SdFat-1.1.0/examples/ReadCsvArray/ReadCsvArray.ino
Normal file
@ -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)); SysCall::halt();}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// Initialize 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() {
|
||||
}
|
||||
|
121
libraries/SdFat-1.1.0/examples/ReadCsvStream/ReadCsvStream.ino
Normal file
121
libraries/SdFat-1.1.0/examples/ReadCsvStream/ReadCsvStream.ino
Normal file
@ -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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// create test file
|
||||
writeFile();
|
||||
|
||||
cout << endl;
|
||||
|
||||
// read and print test
|
||||
readFile();
|
||||
|
||||
cout << "\nDone!" << endl;
|
||||
}
|
||||
void loop() {}
|
81
libraries/SdFat-1.1.0/examples/ReadWrite/ReadWrite.ino
Normal file
81
libraries/SdFat-1.1.0/examples/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-1.1.0/examples/STM32Test/STM32Test.ino
Normal file
175
libraries/SdFat-1.1.0/examples/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"
|
||||
|
||||
// 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() {}
|
552
libraries/SdFat-1.1.0/examples/SdFormatter/SdFormatter.ino
Normal file
552
libraries/SdFat-1.1.0/examples/SdFormatter/SdFormatter.ino
Normal file
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* This program will format an SD or SDHC card.
|
||||
* Warning all data will be deleted!
|
||||
*
|
||||
* For SD/SDHC cards larger than 64 MB this
|
||||
* program attempts to match the format
|
||||
* generated by SDFormatter available here:
|
||||
*
|
||||
* http://www.sdcard.org/consumers/formatter/
|
||||
*
|
||||
* For smaller cards this program uses FAT16
|
||||
* and SDFormatter uses FAT12.
|
||||
*/
|
||||
|
||||
// Set USE_SDIO to zero for SPI card access.
|
||||
#define USE_SDIO 0
|
||||
//
|
||||
// Change the value of chipSelect if your hardware does
|
||||
// not use the default value, SS. Common values are:
|
||||
// Arduino Ethernet shield: pin 4
|
||||
// Sparkfun SD shield: pin 8
|
||||
// Adafruit SD shields and modules: pin 10
|
||||
const uint8_t chipSelect = SS;
|
||||
|
||||
// Initialize at highest supported speed not over 50 MHz.
|
||||
// Reduce max speed if errors occur.
|
||||
#define SPI_SPEED SD_SCK_MHZ(50)
|
||||
|
||||
// Print extra info for debug if DEBUG_PRINT is nonzero
|
||||
#define DEBUG_PRINT 0
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#if DEBUG_PRINT
|
||||
#include "FreeStack.h"
|
||||
#endif // DEBUG_PRINT
|
||||
|
||||
// Serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
#if USE_SDIO
|
||||
// Use faster SdioCardEX
|
||||
SdioCardEX card;
|
||||
// SdioCard card;
|
||||
#else // USE_SDIO
|
||||
Sd2Card card;
|
||||
#endif // USE_SDIO
|
||||
|
||||
uint32_t cardSizeBlocks;
|
||||
uint32_t cardCapacityMB;
|
||||
|
||||
// cache for SD block
|
||||
cache_t cache;
|
||||
|
||||
// MBR information
|
||||
uint8_t partType;
|
||||
uint32_t relSector;
|
||||
uint32_t partSize;
|
||||
|
||||
// Fake disk geometry
|
||||
uint8_t numberOfHeads;
|
||||
uint8_t sectorsPerTrack;
|
||||
|
||||
// FAT parameters
|
||||
uint16_t reservedSectors;
|
||||
uint8_t sectorsPerCluster;
|
||||
uint32_t fatStart;
|
||||
uint32_t fatSize;
|
||||
uint32_t dataStart;
|
||||
|
||||
// constants for file system structure
|
||||
uint16_t const BU16 = 128;
|
||||
uint16_t const BU32 = 8192;
|
||||
|
||||
// strings needed in file system structures
|
||||
char noName[] = "NO NAME ";
|
||||
char fat16str[] = "FAT16 ";
|
||||
char fat32str[] = "FAT32 ";
|
||||
//------------------------------------------------------------------------------
|
||||
#define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();}
|
||||
//------------------------------------------------------------------------------
|
||||
void sdErrorHalt() {
|
||||
if (card.errorCode()) {
|
||||
cout << F("SD error: ") << hex << int(card.errorCode());
|
||||
cout << ',' << int(card.errorData()) << dec << endl;
|
||||
}
|
||||
SysCall::halt();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
#if DEBUG_PRINT
|
||||
void debugPrint() {
|
||||
cout << F("FreeStack: ") << FreeStack() << endl;
|
||||
cout << F("partStart: ") << relSector << endl;
|
||||
cout << F("partSize: ") << partSize << endl;
|
||||
cout << F("reserved: ") << reservedSectors << endl;
|
||||
cout << F("fatStart: ") << fatStart << endl;
|
||||
cout << F("fatSize: ") << fatSize << endl;
|
||||
cout << F("dataStart: ") << dataStart << endl;
|
||||
cout << F("clusterCount: ");
|
||||
cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl;
|
||||
cout << endl;
|
||||
cout << F("Heads: ") << int(numberOfHeads) << endl;
|
||||
cout << F("Sectors: ") << int(sectorsPerTrack) << endl;
|
||||
cout << F("Cylinders: ");
|
||||
cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl;
|
||||
}
|
||||
#endif // DEBUG_PRINT
|
||||
//------------------------------------------------------------------------------
|
||||
// write cached block to the card
|
||||
uint8_t writeCache(uint32_t lbn) {
|
||||
return card.writeBlock(lbn, cache.data);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// initialize appropriate sizes for SD capacity
|
||||
void initSizes() {
|
||||
if (cardCapacityMB <= 6) {
|
||||
sdError("Card is too small.");
|
||||
} else if (cardCapacityMB <= 16) {
|
||||
sectorsPerCluster = 2;
|
||||
} else if (cardCapacityMB <= 32) {
|
||||
sectorsPerCluster = 4;
|
||||
} else if (cardCapacityMB <= 64) {
|
||||
sectorsPerCluster = 8;
|
||||
} else if (cardCapacityMB <= 128) {
|
||||
sectorsPerCluster = 16;
|
||||
} else if (cardCapacityMB <= 1024) {
|
||||
sectorsPerCluster = 32;
|
||||
} else if (cardCapacityMB <= 32768) {
|
||||
sectorsPerCluster = 64;
|
||||
} else {
|
||||
// SDXC cards
|
||||
sectorsPerCluster = 128;
|
||||
}
|
||||
|
||||
cout << F("Blocks/Cluster: ") << int(sectorsPerCluster) << endl;
|
||||
// set fake disk geometry
|
||||
sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63;
|
||||
|
||||
if (cardCapacityMB <= 16) {
|
||||
numberOfHeads = 2;
|
||||
} else if (cardCapacityMB <= 32) {
|
||||
numberOfHeads = 4;
|
||||
} else if (cardCapacityMB <= 128) {
|
||||
numberOfHeads = 8;
|
||||
} else if (cardCapacityMB <= 504) {
|
||||
numberOfHeads = 16;
|
||||
} else if (cardCapacityMB <= 1008) {
|
||||
numberOfHeads = 32;
|
||||
} else if (cardCapacityMB <= 2016) {
|
||||
numberOfHeads = 64;
|
||||
} else if (cardCapacityMB <= 4032) {
|
||||
numberOfHeads = 128;
|
||||
} else {
|
||||
numberOfHeads = 255;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// zero cache and optionally set the sector signature
|
||||
void clearCache(uint8_t addSig) {
|
||||
memset(&cache, 0, sizeof(cache));
|
||||
if (addSig) {
|
||||
cache.mbr.mbrSig0 = BOOTSIG0;
|
||||
cache.mbr.mbrSig1 = BOOTSIG1;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// zero FAT and root dir area on SD
|
||||
void clearFatDir(uint32_t bgn, uint32_t count) {
|
||||
clearCache(false);
|
||||
#if USE_SDIO
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (!card.writeBlock(bgn + i, cache.data)) {
|
||||
sdError("Clear FAT/DIR writeBlock failed");
|
||||
}
|
||||
if ((i & 0XFF) == 0) {
|
||||
cout << '.';
|
||||
}
|
||||
}
|
||||
#else // USE_SDIO
|
||||
if (!card.writeStart(bgn, count)) {
|
||||
sdError("Clear FAT/DIR writeStart failed");
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if ((i & 0XFF) == 0) {
|
||||
cout << '.';
|
||||
}
|
||||
if (!card.writeData(cache.data)) {
|
||||
sdError("Clear FAT/DIR writeData failed");
|
||||
}
|
||||
}
|
||||
if (!card.writeStop()) {
|
||||
sdError("Clear FAT/DIR writeStop failed");
|
||||
}
|
||||
#endif // USE_SDIO
|
||||
cout << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// return cylinder number for a logical block number
|
||||
uint16_t lbnToCylinder(uint32_t lbn) {
|
||||
return lbn / (numberOfHeads * sectorsPerTrack);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// return head number for a logical block number
|
||||
uint8_t lbnToHead(uint32_t lbn) {
|
||||
return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// return sector number for a logical block number
|
||||
uint8_t lbnToSector(uint32_t lbn) {
|
||||
return (lbn % sectorsPerTrack) + 1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// format and write the Master Boot Record
|
||||
void writeMbr() {
|
||||
clearCache(true);
|
||||
part_t* p = cache.mbr.part;
|
||||
p->boot = 0;
|
||||
uint16_t c = lbnToCylinder(relSector);
|
||||
if (c > 1023) {
|
||||
sdError("MBR CHS");
|
||||
}
|
||||
p->beginCylinderHigh = c >> 8;
|
||||
p->beginCylinderLow = c & 0XFF;
|
||||
p->beginHead = lbnToHead(relSector);
|
||||
p->beginSector = lbnToSector(relSector);
|
||||
p->type = partType;
|
||||
uint32_t endLbn = relSector + partSize - 1;
|
||||
c = lbnToCylinder(endLbn);
|
||||
if (c <= 1023) {
|
||||
p->endCylinderHigh = c >> 8;
|
||||
p->endCylinderLow = c & 0XFF;
|
||||
p->endHead = lbnToHead(endLbn);
|
||||
p->endSector = lbnToSector(endLbn);
|
||||
} else {
|
||||
// Too big flag, c = 1023, h = 254, s = 63
|
||||
p->endCylinderHigh = 3;
|
||||
p->endCylinderLow = 255;
|
||||
p->endHead = 254;
|
||||
p->endSector = 63;
|
||||
}
|
||||
p->firstSector = relSector;
|
||||
p->totalSectors = partSize;
|
||||
if (!writeCache(0)) {
|
||||
sdError("write MBR");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// generate serial number from card size and micros since boot
|
||||
uint32_t volSerialNumber() {
|
||||
return (cardSizeBlocks << 8) + micros();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// format the SD as FAT16
|
||||
void makeFat16() {
|
||||
uint32_t nc;
|
||||
for (dataStart = 2 * BU16;; dataStart += BU16) {
|
||||
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
|
||||
fatSize = (nc + 2 + 255)/256;
|
||||
uint32_t r = BU16 + 1 + 2 * fatSize + 32;
|
||||
if (dataStart < r) {
|
||||
continue;
|
||||
}
|
||||
relSector = dataStart - r + BU16;
|
||||
break;
|
||||
}
|
||||
// check valid cluster count for FAT16 volume
|
||||
if (nc < 4085 || nc >= 65525) {
|
||||
sdError("Bad cluster count");
|
||||
}
|
||||
reservedSectors = 1;
|
||||
fatStart = relSector + reservedSectors;
|
||||
partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
|
||||
if (partSize < 32680) {
|
||||
partType = 0X01;
|
||||
} else if (partSize < 65536) {
|
||||
partType = 0X04;
|
||||
} else {
|
||||
partType = 0X06;
|
||||
}
|
||||
// write MBR
|
||||
writeMbr();
|
||||
clearCache(true);
|
||||
fat_boot_t* pb = &cache.fbs;
|
||||
pb->jump[0] = 0XEB;
|
||||
pb->jump[1] = 0X00;
|
||||
pb->jump[2] = 0X90;
|
||||
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
|
||||
pb->oemId[i] = ' ';
|
||||
}
|
||||
pb->bytesPerSector = 512;
|
||||
pb->sectorsPerCluster = sectorsPerCluster;
|
||||
pb->reservedSectorCount = reservedSectors;
|
||||
pb->fatCount = 2;
|
||||
pb->rootDirEntryCount = 512;
|
||||
pb->mediaType = 0XF8;
|
||||
pb->sectorsPerFat16 = fatSize;
|
||||
pb->sectorsPerTrack = sectorsPerTrack;
|
||||
pb->headCount = numberOfHeads;
|
||||
pb->hidddenSectors = relSector;
|
||||
pb->totalSectors32 = partSize;
|
||||
pb->driveNumber = 0X80;
|
||||
pb->bootSignature = EXTENDED_BOOT_SIG;
|
||||
pb->volumeSerialNumber = volSerialNumber();
|
||||
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
||||
memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType));
|
||||
// write partition boot sector
|
||||
if (!writeCache(relSector)) {
|
||||
sdError("FAT16 write PBS failed");
|
||||
}
|
||||
// clear FAT and root directory
|
||||
clearFatDir(fatStart, dataStart - fatStart);
|
||||
clearCache(false);
|
||||
cache.fat16[0] = 0XFFF8;
|
||||
cache.fat16[1] = 0XFFFF;
|
||||
// write first block of FAT and backup for reserved clusters
|
||||
if (!writeCache(fatStart)
|
||||
|| !writeCache(fatStart + fatSize)) {
|
||||
sdError("FAT16 reserve failed");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// format the SD as FAT32
|
||||
void makeFat32() {
|
||||
uint32_t nc;
|
||||
relSector = BU32;
|
||||
for (dataStart = 2 * BU32;; dataStart += BU32) {
|
||||
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
|
||||
fatSize = (nc + 2 + 127)/128;
|
||||
uint32_t r = relSector + 9 + 2 * fatSize;
|
||||
if (dataStart >= r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// error if too few clusters in FAT32 volume
|
||||
if (nc < 65525) {
|
||||
sdError("Bad cluster count");
|
||||
}
|
||||
reservedSectors = dataStart - relSector - 2 * fatSize;
|
||||
fatStart = relSector + reservedSectors;
|
||||
partSize = nc * sectorsPerCluster + dataStart - relSector;
|
||||
// type depends on address of end sector
|
||||
// max CHS has lbn = 16450560 = 1024*255*63
|
||||
if ((relSector + partSize) <= 16450560) {
|
||||
// FAT32
|
||||
partType = 0X0B;
|
||||
} else {
|
||||
// FAT32 with INT 13
|
||||
partType = 0X0C;
|
||||
}
|
||||
writeMbr();
|
||||
clearCache(true);
|
||||
|
||||
fat32_boot_t* pb = &cache.fbs32;
|
||||
pb->jump[0] = 0XEB;
|
||||
pb->jump[1] = 0X00;
|
||||
pb->jump[2] = 0X90;
|
||||
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
|
||||
pb->oemId[i] = ' ';
|
||||
}
|
||||
pb->bytesPerSector = 512;
|
||||
pb->sectorsPerCluster = sectorsPerCluster;
|
||||
pb->reservedSectorCount = reservedSectors;
|
||||
pb->fatCount = 2;
|
||||
pb->mediaType = 0XF8;
|
||||
pb->sectorsPerTrack = sectorsPerTrack;
|
||||
pb->headCount = numberOfHeads;
|
||||
pb->hidddenSectors = relSector;
|
||||
pb->totalSectors32 = partSize;
|
||||
pb->sectorsPerFat32 = fatSize;
|
||||
pb->fat32RootCluster = 2;
|
||||
pb->fat32FSInfo = 1;
|
||||
pb->fat32BackBootBlock = 6;
|
||||
pb->driveNumber = 0X80;
|
||||
pb->bootSignature = EXTENDED_BOOT_SIG;
|
||||
pb->volumeSerialNumber = volSerialNumber();
|
||||
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
||||
memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
|
||||
// write partition boot sector and backup
|
||||
if (!writeCache(relSector)
|
||||
|| !writeCache(relSector + 6)) {
|
||||
sdError("FAT32 write PBS failed");
|
||||
}
|
||||
clearCache(true);
|
||||
// write extra boot area and backup
|
||||
if (!writeCache(relSector + 2)
|
||||
|| !writeCache(relSector + 8)) {
|
||||
sdError("FAT32 PBS ext failed");
|
||||
}
|
||||
fat32_fsinfo_t* pf = &cache.fsinfo;
|
||||
pf->leadSignature = FSINFO_LEAD_SIG;
|
||||
pf->structSignature = FSINFO_STRUCT_SIG;
|
||||
pf->freeCount = 0XFFFFFFFF;
|
||||
pf->nextFree = 0XFFFFFFFF;
|
||||
// write FSINFO sector and backup
|
||||
if (!writeCache(relSector + 1)
|
||||
|| !writeCache(relSector + 7)) {
|
||||
sdError("FAT32 FSINFO failed");
|
||||
}
|
||||
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster);
|
||||
clearCache(false);
|
||||
cache.fat32[0] = 0x0FFFFFF8;
|
||||
cache.fat32[1] = 0x0FFFFFFF;
|
||||
cache.fat32[2] = 0x0FFFFFFF;
|
||||
// write first block of FAT and backup for reserved clusters
|
||||
if (!writeCache(fatStart)
|
||||
|| !writeCache(fatStart + fatSize)) {
|
||||
sdError("FAT32 reserve failed");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// flash erase all data
|
||||
uint32_t const ERASE_SIZE = 262144L;
|
||||
void eraseCard() {
|
||||
cout << endl << F("Erasing\n");
|
||||
uint32_t firstBlock = 0;
|
||||
uint32_t lastBlock;
|
||||
uint16_t n = 0;
|
||||
|
||||
do {
|
||||
lastBlock = firstBlock + ERASE_SIZE - 1;
|
||||
if (lastBlock >= cardSizeBlocks) {
|
||||
lastBlock = cardSizeBlocks - 1;
|
||||
}
|
||||
if (!card.erase(firstBlock, lastBlock)) {
|
||||
sdError("erase failed");
|
||||
}
|
||||
cout << '.';
|
||||
if ((n++)%32 == 31) {
|
||||
cout << endl;
|
||||
}
|
||||
firstBlock += ERASE_SIZE;
|
||||
} while (firstBlock < cardSizeBlocks);
|
||||
cout << endl;
|
||||
|
||||
if (!card.readBlock(0, cache.data)) {
|
||||
sdError("readBlock");
|
||||
}
|
||||
cout << hex << showbase << setfill('0') << internal;
|
||||
cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl;
|
||||
cout << dec << noshowbase << setfill(' ') << right;
|
||||
cout << F("Erase done\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void formatCard() {
|
||||
cout << endl;
|
||||
cout << F("Formatting\n");
|
||||
initSizes();
|
||||
if (card.type() != SD_CARD_TYPE_SDHC) {
|
||||
cout << F("FAT16\n");
|
||||
makeFat16();
|
||||
} else {
|
||||
cout << F("FAT32\n");
|
||||
makeFat32();
|
||||
}
|
||||
#if DEBUG_PRINT
|
||||
debugPrint();
|
||||
#endif // DEBUG_PRINT
|
||||
cout << F("Format done\n");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
char c;
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// Discard any extra characters.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
cout << F(
|
||||
"\n"
|
||||
"This program can erase and/or format SD/SDHC cards.\n"
|
||||
"\n"
|
||||
"Erase uses the card's fast flash erase command.\n"
|
||||
"Flash erase sets all data to 0X00 for most cards\n"
|
||||
"and 0XFF for a few vendor's cards.\n"
|
||||
"\n"
|
||||
"Cards larger than 2 GB will be formatted FAT32 and\n"
|
||||
"smaller cards will be formatted FAT16.\n"
|
||||
"\n"
|
||||
"Warning, all data on the card will be erased.\n"
|
||||
"Enter 'Y' to continue: ");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
c = Serial.read();
|
||||
cout << c << endl;
|
||||
if (c != 'Y') {
|
||||
cout << F("Quiting, you did not enter 'Y'.\n");
|
||||
return;
|
||||
}
|
||||
// Read any existing Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
cout << F(
|
||||
"\n"
|
||||
"Options are:\n"
|
||||
"E - erase the card and skip formatting.\n"
|
||||
"F - erase and then format the card. (recommended)\n"
|
||||
"Q - quick format the card without erase.\n"
|
||||
"\n"
|
||||
"Enter option: ");
|
||||
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
c = Serial.read();
|
||||
cout << c << endl;
|
||||
if (!strchr("EFQ", c)) {
|
||||
cout << F("Quiting, invalid option entered.") << endl;
|
||||
return;
|
||||
}
|
||||
#if USE_SDIO
|
||||
if (!card.begin()) {
|
||||
sdError("card.begin failed");
|
||||
}
|
||||
#else // USE_SDIO
|
||||
if (!card.begin(chipSelect, SPI_SPEED)) {
|
||||
cout << F(
|
||||
"\nSD initialization failure!\n"
|
||||
"Is the SD card inserted correctly?\n"
|
||||
"Is chip select correct at the top of this program?\n");
|
||||
sdError("card.begin failed");
|
||||
}
|
||||
#endif
|
||||
cardSizeBlocks = card.cardSize();
|
||||
if (cardSizeBlocks == 0) {
|
||||
sdError("cardSize");
|
||||
}
|
||||
cardCapacityMB = (cardSizeBlocks + 2047)/2048;
|
||||
|
||||
cout << F("Card Size: ") << setprecision(0) << 1.048576*cardCapacityMB;
|
||||
cout << F(" MB, (MB = 1,000,000 bytes)") << endl;
|
||||
|
||||
if (c == 'E' || c == 'F') {
|
||||
eraseCard();
|
||||
}
|
||||
if (c == 'F' || c == 'Q') {
|
||||
formatCard();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
248
libraries/SdFat-1.1.0/examples/SdInfo/SdInfo.ino
Normal file
248
libraries/SdFat-1.1.0/examples/SdInfo/SdInfo.ino
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* This program attempts to initialize an SD card and analyze its structure.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// Set USE_SDIO to zero for SPI card access.
|
||||
#define USE_SDIO 0
|
||||
/*
|
||||
* SD chip select pin. Common values are:
|
||||
*
|
||||
* Arduino Ethernet shield, pin 4.
|
||||
* SparkFun SD shield, pin 8.
|
||||
* Adafruit SD shields and modules, pin 10.
|
||||
* Default SD chip select is the SPI SS pin.
|
||||
*/
|
||||
const uint8_t SD_CHIP_SELECT = SS;
|
||||
/*
|
||||
* Set DISABLE_CHIP_SELECT to disable a second SPI device.
|
||||
* For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
|
||||
* to 10 to disable the Ethernet controller.
|
||||
*/
|
||||
const int8_t DISABLE_CHIP_SELECT = -1;
|
||||
|
||||
#if USE_SDIO
|
||||
// Use faster SdioCardEX
|
||||
SdFatSdioEX sd;
|
||||
// SdFatSdio sd;
|
||||
#else // USE_SDIO
|
||||
SdFat sd;
|
||||
#endif // USE_SDIO
|
||||
|
||||
// serial output steam
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
// global for card size
|
||||
uint32_t cardSize;
|
||||
|
||||
// global for card erase size
|
||||
uint32_t eraseSize;
|
||||
//------------------------------------------------------------------------------
|
||||
// store error strings in flash
|
||||
#define sdErrorMsg(msg) sd.errorPrint(F(msg));
|
||||
//------------------------------------------------------------------------------
|
||||
uint8_t cidDmp() {
|
||||
cid_t cid;
|
||||
if (!sd.card()->readCID(&cid)) {
|
||||
sdErrorMsg("readCID failed");
|
||||
return false;
|
||||
}
|
||||
cout << F("\nManufacturer ID: ");
|
||||
cout << hex << int(cid.mid) << dec << endl;
|
||||
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
|
||||
cout << F("Product: ");
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
cout << cid.pnm[i];
|
||||
}
|
||||
cout << F("\nVersion: ");
|
||||
cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
|
||||
cout << F("Serial number: ") << hex << cid.psn << dec << endl;
|
||||
cout << F("Manufacturing date: ");
|
||||
cout << int(cid.mdt_month) << '/';
|
||||
cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
|
||||
cout << endl;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
uint8_t csdDmp() {
|
||||
csd_t csd;
|
||||
uint8_t eraseSingleBlock;
|
||||
if (!sd.card()->readCSD(&csd)) {
|
||||
sdErrorMsg("readCSD failed");
|
||||
return false;
|
||||
}
|
||||
if (csd.v1.csd_ver == 0) {
|
||||
eraseSingleBlock = csd.v1.erase_blk_en;
|
||||
eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
|
||||
} else if (csd.v2.csd_ver == 1) {
|
||||
eraseSingleBlock = csd.v2.erase_blk_en;
|
||||
eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low;
|
||||
} else {
|
||||
cout << F("csd version error\n");
|
||||
return false;
|
||||
}
|
||||
eraseSize++;
|
||||
cout << F("cardSize: ") << 0.000512*cardSize;
|
||||
cout << F(" MB (MB = 1,000,000 bytes)\n");
|
||||
|
||||
cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
|
||||
cout << F("eraseSingleBlock: ");
|
||||
if (eraseSingleBlock) {
|
||||
cout << F("true\n");
|
||||
} else {
|
||||
cout << F("false\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// print partition table
|
||||
uint8_t partDmp() {
|
||||
mbr_t mbr;
|
||||
if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) {
|
||||
sdErrorMsg("read MBR failed");
|
||||
return false;
|
||||
}
|
||||
for (uint8_t ip = 1; ip < 5; ip++) {
|
||||
part_t *pt = &mbr.part[ip - 1];
|
||||
if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) {
|
||||
cout << F("\nNo MBR. Assuming Super Floppy format.\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cout << F("\nSD Partition Table\n");
|
||||
cout << F("part,boot,type,start,length\n");
|
||||
for (uint8_t ip = 1; ip < 5; ip++) {
|
||||
part_t *pt = &mbr.part[ip - 1];
|
||||
cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
|
||||
cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void volDmp() {
|
||||
cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl;
|
||||
cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl;
|
||||
cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl;
|
||||
cout << F("freeClusters: ");
|
||||
uint32_t volFree = sd.vol()->freeClusterCount();
|
||||
cout << volFree << endl;
|
||||
float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
|
||||
cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n");
|
||||
cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl;
|
||||
cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl;
|
||||
cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl;
|
||||
cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl;
|
||||
cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl;
|
||||
if (sd.vol()->dataStartBlock() % eraseSize) {
|
||||
cout << F("Data area is not aligned on flash erase boundaries!\n");
|
||||
cout << F("Download and use formatter from www.sdcard.org!\n");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// use uppercase in hex and use 0X base prefix
|
||||
cout << uppercase << showbase << endl;
|
||||
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
|
||||
#if !USE_SDIO
|
||||
if (DISABLE_CHIP_SELECT < 0) {
|
||||
cout << F(
|
||||
"\nAssuming the SD is the only SPI device.\n"
|
||||
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
|
||||
} else {
|
||||
cout << F("\nDisabling SPI device on pin ");
|
||||
cout << int(DISABLE_CHIP_SELECT) << endl;
|
||||
pinMode(DISABLE_CHIP_SELECT, OUTPUT);
|
||||
digitalWrite(DISABLE_CHIP_SELECT, HIGH);
|
||||
}
|
||||
cout << F("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT);
|
||||
cout << F("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n");
|
||||
#endif // !USE_SDIO
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
// Read any existing Serial data.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("\ntype any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
uint32_t t = millis();
|
||||
#if USE_SDIO
|
||||
if (!sd.cardBegin()) {
|
||||
sdErrorMsg("\ncardBegin failed");
|
||||
return;
|
||||
}
|
||||
#else // USE_SDIO
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) {
|
||||
sdErrorMsg("cardBegin failed");
|
||||
return;
|
||||
}
|
||||
#endif // USE_SDIO
|
||||
t = millis() - t;
|
||||
|
||||
cardSize = sd.card()->cardSize();
|
||||
if (cardSize == 0) {
|
||||
sdErrorMsg("cardSize failed");
|
||||
return;
|
||||
}
|
||||
cout << F("\ninit time: ") << t << " ms" << endl;
|
||||
cout << F("\nCard type: ");
|
||||
switch (sd.card()->type()) {
|
||||
case SD_CARD_TYPE_SD1:
|
||||
cout << F("SD1\n");
|
||||
break;
|
||||
|
||||
case SD_CARD_TYPE_SD2:
|
||||
cout << F("SD2\n");
|
||||
break;
|
||||
|
||||
case SD_CARD_TYPE_SDHC:
|
||||
if (cardSize < 70000000) {
|
||||
cout << F("SDHC\n");
|
||||
} else {
|
||||
cout << F("SDXC\n");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cout << F("Unknown\n");
|
||||
}
|
||||
if (!cidDmp()) {
|
||||
return;
|
||||
}
|
||||
if (!csdDmp()) {
|
||||
return;
|
||||
}
|
||||
uint32_t ocr;
|
||||
if (!sd.card()->readOCR(&ocr)) {
|
||||
sdErrorMsg("\nreadOCR failed");
|
||||
return;
|
||||
}
|
||||
cout << F("OCR: ") << hex << ocr << dec << endl;
|
||||
if (!partDmp()) {
|
||||
return;
|
||||
}
|
||||
if (!sd.fsBegin()) {
|
||||
sdErrorMsg("\nFile System initialization failed.\n");
|
||||
return;
|
||||
}
|
||||
volDmp();
|
||||
}
|
58
libraries/SdFat-1.1.0/examples/SoftwareSpi/SoftwareSpi.ino
Normal file
58
libraries/SdFat-1.1.0/examples/SoftwareSpi/SoftwareSpi.ino
Normal file
@ -0,0 +1,58 @@
|
||||
// An example of the SdFatSoftSpi template class.
|
||||
// This example is for an Adafruit Data Logging Shield on a Mega.
|
||||
// Software SPI is required on Mega since this shield connects to pins 10-13.
|
||||
// This example will also run on an Uno and other boards using software SPI.
|
||||
//
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h
|
||||
//
|
||||
// Pin numbers in templates must be constants.
|
||||
const uint8_t SOFT_MISO_PIN = 12;
|
||||
const uint8_t SOFT_MOSI_PIN = 11;
|
||||
const uint8_t SOFT_SCK_PIN = 13;
|
||||
//
|
||||
// Chip select may be constant or RAM variable.
|
||||
const uint8_t SD_CHIP_SELECT_PIN = 10;
|
||||
|
||||
// SdFat software SPI template
|
||||
SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;
|
||||
|
||||
// Test file.
|
||||
SdFile file;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
if (!sd.begin(SD_CHIP_SELECT_PIN)) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
if (!file.open("SoftSPI.txt", O_RDWR | O_CREAT)) {
|
||||
sd.errorHalt(F("open failed"));
|
||||
}
|
||||
file.println(F("This line was printed using software SPI."));
|
||||
|
||||
file.rewind();
|
||||
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
Serial.println(F("Done."));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
#else // ENABLE_SOFTWARE_SPI_CLASS
|
||||
#error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h
|
||||
#endif //ENABLE_SOFTWARE_SPI_CLASS
|
214
libraries/SdFat-1.1.0/examples/StdioBench/StdioBench.ino
Normal file
214
libraries/SdFat-1.1.0/examples/StdioBench/StdioBench.ino
Normal file
@ -0,0 +1,214 @@
|
||||
// Benchmark comparing SdFile and StdioStream.
|
||||
#include <SPI.h>
|
||||
#include "SdFat.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;
|
||||
uint32_t stdioSize = 0;
|
||||
uint32_t printTime;
|
||||
uint32_t stdioTime = 0;
|
||||
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
Serial.println(F("Type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
SysCall::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() {}
|
169
libraries/SdFat-1.1.0/examples/TeensySdioDemo/TeensySdioDemo.ino
Normal file
169
libraries/SdFat-1.1.0/examples/TeensySdioDemo/TeensySdioDemo.ino
Normal file
@ -0,0 +1,169 @@
|
||||
// Simple performance test for Teensy 3.5/3.6 SDHC.
|
||||
// Demonstrates yield() efficiency.
|
||||
|
||||
// Warning SdFatSdio and SdFatSdioEX normally should
|
||||
// not both be used in a program.
|
||||
// Each has its own cache and member variables.
|
||||
|
||||
#include "SdFat.h"
|
||||
|
||||
// 32 KiB buffer.
|
||||
const size_t BUF_DIM = 32768;
|
||||
|
||||
// 8 MiB file.
|
||||
const uint32_t FILE_SIZE = 256UL*BUF_DIM;
|
||||
|
||||
SdFatSdio sd;
|
||||
|
||||
SdFatSdioEX sdEx;
|
||||
|
||||
File file;
|
||||
|
||||
uint8_t buf[BUF_DIM];
|
||||
|
||||
// buffer as uint32_t
|
||||
uint32_t* buf32 = (uint32_t*)buf;
|
||||
|
||||
// Total usec in read/write calls.
|
||||
uint32_t totalMicros = 0;
|
||||
// Time in yield() function.
|
||||
uint32_t yieldMicros = 0;
|
||||
// Number of yield calls.
|
||||
uint32_t yieldCalls = 0;
|
||||
// Max busy time for single yield call.
|
||||
uint32_t yieldMaxUsec = 0;
|
||||
// Control access to the two versions of SdFat.
|
||||
bool useEx = false;
|
||||
//-----------------------------------------------------------------------------
|
||||
bool sdBusy() {
|
||||
return useEx ? sdEx.card()->isBusy() : sd.card()->isBusy();
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void errorHalt(const char* msg) {
|
||||
if (useEx) {
|
||||
sdEx.errorHalt(msg);
|
||||
} else {
|
||||
sd.errorHalt(msg);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
uint32_t kHzSdClk() {
|
||||
return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Replace "weak" system yield() function.
|
||||
void yield() {
|
||||
// Only count cardBusy time.
|
||||
if (!sdBusy()) {
|
||||
return;
|
||||
}
|
||||
uint32_t m = micros();
|
||||
yieldCalls++;
|
||||
while (sdBusy()) {
|
||||
// Do something here.
|
||||
}
|
||||
m = micros() - m;
|
||||
if (m > yieldMaxUsec) {
|
||||
yieldMaxUsec = m;
|
||||
}
|
||||
yieldMicros += m;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void runTest() {
|
||||
// Zero Stats
|
||||
totalMicros = 0;
|
||||
yieldMicros = 0;
|
||||
yieldCalls = 0;
|
||||
yieldMaxUsec = 0;
|
||||
if (!file.open("TeensyDemo.bin", O_RDWR | O_CREAT)) {
|
||||
errorHalt("open failed");
|
||||
}
|
||||
Serial.println("\nsize,write,read");
|
||||
Serial.println("bytes,KB/sec,KB/sec");
|
||||
for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) {
|
||||
file.truncate(0);
|
||||
uint32_t nRdWr = FILE_SIZE/nb;
|
||||
Serial.print(nb);
|
||||
Serial.print(',');
|
||||
uint32_t t = micros();
|
||||
for (uint32_t n = 0; n < nRdWr; n++) {
|
||||
// Set start and end of buffer.
|
||||
buf32[0] = n;
|
||||
buf32[nb/4 - 1] = n;
|
||||
if (nb != file.write(buf, nb)) {
|
||||
errorHalt("write failed");
|
||||
}
|
||||
}
|
||||
t = micros() - t;
|
||||
totalMicros += t;
|
||||
Serial.print(1000.0*FILE_SIZE/t);
|
||||
Serial.print(',');
|
||||
file.rewind();
|
||||
t = micros();
|
||||
|
||||
for (uint32_t n = 0; n < nRdWr; n++) {
|
||||
if ((int)nb != file.read(buf, nb)) {
|
||||
errorHalt("read failed");
|
||||
}
|
||||
// crude check of data.
|
||||
if (buf32[0] != n || buf32[nb/4 - 1] != n) {
|
||||
errorHalt("data check");
|
||||
}
|
||||
}
|
||||
t = micros() - t;
|
||||
totalMicros += t;
|
||||
Serial.println(1000.0*FILE_SIZE/t);
|
||||
}
|
||||
file.close();
|
||||
Serial.print("\ntotalMicros ");
|
||||
Serial.println(totalMicros);
|
||||
Serial.print("yieldMicros ");
|
||||
Serial.println(yieldMicros);
|
||||
Serial.print("yieldCalls ");
|
||||
Serial.println(yieldCalls);
|
||||
Serial.print("yieldMaxUsec ");
|
||||
Serial.println(yieldMaxUsec);
|
||||
Serial.print("kHzSdClk ");
|
||||
Serial.println(kHzSdClk());
|
||||
Serial.println("Done");
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {
|
||||
}
|
||||
Serial.println("SdFatSdioEX uses extended multi-block transfers without DMA.");
|
||||
Serial.println("SdFatSdio uses a traditional DMA SDIO implementation.");
|
||||
Serial.println("Note the difference is speed and busy yield time.\n");
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void loop() {
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read());
|
||||
|
||||
Serial.println("Type '1' for SdFatSdioEX or '2' for SdFatSdio");
|
||||
while (!Serial.available()) {
|
||||
}
|
||||
char c = Serial.read();
|
||||
if (c != '1' && c != '2') {
|
||||
Serial.println("Invalid input");
|
||||
return;
|
||||
}
|
||||
if (c =='1') {
|
||||
useEx = true;
|
||||
if (!sdEx.begin()) {
|
||||
sd.initErrorHalt("SdFatSdioEX begin() failed");
|
||||
}
|
||||
// make sdEx the current volume.
|
||||
sdEx.chvol();
|
||||
} else {
|
||||
useEx = false;
|
||||
if (!sd.begin()) {
|
||||
sd.initErrorHalt("SdFatSdio begin() failed");
|
||||
}
|
||||
// make sd the current volume.
|
||||
sd.chvol();
|
||||
}
|
||||
runTest();
|
||||
}
|
176
libraries/SdFat-1.1.0/examples/Timestamp/Timestamp.ino
Normal file
176
libraries/SdFat-1.1.0/examples/Timestamp/Timestamp.ino
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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) {
|
||||
dir_t d;
|
||||
if (!f.dirEntry(&d)) {
|
||||
error("f.dirEntry failed");
|
||||
}
|
||||
|
||||
cout << F("Creation: ");
|
||||
f.printFatDate(d.creationDate);
|
||||
cout << ' ';
|
||||
f.printFatTime(d.creationTime);
|
||||
cout << endl;
|
||||
|
||||
cout << F("Modify: ");
|
||||
f.printFatDate(d.lastWriteDate);
|
||||
cout <<' ';
|
||||
f.printFatTime(d.lastWriteTime);
|
||||
cout << endl;
|
||||
|
||||
cout << F("Access: ");
|
||||
f.printFatDate(d.lastAccessDate);
|
||||
cout << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// remove 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-1.1.0/examples/TwoCards/TwoCards.ino
Normal file
170
libraries/SdFat-1.1.0/examples/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 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) {
|
||||
SysCall::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()) {
|
||||
SysCall::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 (uint16_t i = 0; i < NWRITE; i++) {
|
||||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
sd1.errorExit("sd1.write");
|
||||
}
|
||||
}
|
||||
// set the current working directory for open() to sd2
|
||||
sd2.chvol();
|
||||
|
||||
// create or open /Dir2/copy.bin and truncate it to zero length
|
||||
SdFile file2;
|
||||
if (!file2.open("copy.bin", O_WRONLY | O_CREAT | O_TRUNC)) {
|
||||
sd2.errorExit("file2");
|
||||
}
|
||||
Serial.println(F("Copying test.bin to copy.bin"));
|
||||
|
||||
// copy file1 to file2
|
||||
file1.rewind();
|
||||
uint32_t t = millis();
|
||||
|
||||
while (1) {
|
||||
int n = file1.read(buf, sizeof(buf));
|
||||
if (n < 0) {
|
||||
sd1.errorExit("read1");
|
||||
}
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
if ((int)file2.write(buf, n) != n) {
|
||||
sd2.errorExit("write2");
|
||||
}
|
||||
}
|
||||
t = millis() - t;
|
||||
Serial.print(F("File size: "));
|
||||
Serial.println(file2.fileSize());
|
||||
Serial.print(F("Copy time: "));
|
||||
Serial.print(t);
|
||||
Serial.println(F(" millis"));
|
||||
// close test.bin
|
||||
file1.close();
|
||||
file2.close();
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 -------"));
|
||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("------sd2 -------"));
|
||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("---------------------"));
|
||||
Serial.println(F("Renaming copy.bin"));
|
||||
// rename the copy
|
||||
if (!sd2.rename("copy.bin", "rename.bin")) {
|
||||
sd2.errorExit("sd2.rename");
|
||||
}
|
||||
// list current directory on both cards
|
||||
Serial.println(F("------sd1 -------"));
|
||||
sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("------sd2 -------"));
|
||||
sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
|
||||
Serial.println(F("---------------------"));
|
||||
Serial.println(F("Done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
@ -0,0 +1,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) {
|
||||
SysCall::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()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
// 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() {}
|
222
libraries/SdFat-1.1.0/examples/bench/bench.ino
Normal file
222
libraries/SdFat-1.1.0/examples/bench/bench.ino
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* This program is a simple binary write/read benchmark.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
// Set USE_SDIO to zero for SPI card access.
|
||||
#define USE_SDIO 0
|
||||
|
||||
// SD chip select pin
|
||||
const uint8_t chipSelect = SS;
|
||||
|
||||
// Size of read/write.
|
||||
const size_t BUF_SIZE = 512;
|
||||
|
||||
// File size in MB where MB = 1,000,000 bytes.
|
||||
const uint32_t FILE_SIZE_MB = 5;
|
||||
|
||||
// Write pass count.
|
||||
const uint8_t WRITE_COUNT = 2;
|
||||
|
||||
// Read pass count.
|
||||
const uint8_t READ_COUNT = 2;
|
||||
//==============================================================================
|
||||
// End of configuration constants.
|
||||
//------------------------------------------------------------------------------
|
||||
// File size in bytes.
|
||||
const uint32_t FILE_SIZE = 1000000UL*FILE_SIZE_MB;
|
||||
|
||||
uint8_t buf[BUF_SIZE];
|
||||
|
||||
// file system
|
||||
#if USE_SDIO
|
||||
// Traditional DMA version.
|
||||
// SdFatSdio sd;
|
||||
// Faster version.
|
||||
SdFatSdioEX sd;
|
||||
#else // USE_SDIO
|
||||
SdFat sd;
|
||||
#endif // USE_SDIO
|
||||
|
||||
// Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O.
|
||||
// Requires dedicated use of the SPI bus.
|
||||
// SdFatEX sd;
|
||||
|
||||
// Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI.
|
||||
// Args are misoPin, mosiPin, sckPin.
|
||||
// SdFatSoftSpi<6, 7, 5> sd;
|
||||
|
||||
// test file
|
||||
SdFile file;
|
||||
|
||||
// Serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
//------------------------------------------------------------------------------
|
||||
// Store error strings in flash to save RAM.
|
||||
#define error(s) sd.errorHalt(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void cidDmp() {
|
||||
cid_t cid;
|
||||
if (!sd.card()->readCID(&cid)) {
|
||||
error("readCID failed");
|
||||
}
|
||||
cout << F("\nManufacturer ID: ");
|
||||
cout << hex << int(cid.mid) << dec << endl;
|
||||
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
|
||||
cout << F("Product: ");
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
cout << cid.pnm[i];
|
||||
}
|
||||
cout << F("\nVersion: ");
|
||||
cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
|
||||
cout << F("Serial number: ") << hex << cid.psn << dec << endl;
|
||||
cout << F("Manufacturing date: ");
|
||||
cout << int(cid.mdt_month) << '/';
|
||||
cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
|
||||
cout << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
delay(1000);
|
||||
cout << F("\nUse a freshly formatted SD for best performance.\n");
|
||||
|
||||
// use uppercase in hex and use 0X base prefix
|
||||
cout << uppercase << showbase << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
float s;
|
||||
uint32_t t;
|
||||
uint32_t maxLatency;
|
||||
uint32_t minLatency;
|
||||
uint32_t totalLatency;
|
||||
|
||||
// Discard any input.
|
||||
do {
|
||||
delay(10);
|
||||
} while (Serial.available() && Serial.read() >= 0);
|
||||
|
||||
// F( stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cout << F("chipSelect: ") << int(chipSelect) << endl;
|
||||
cout << F("FreeStack: ") << FreeStack() << endl;
|
||||
|
||||
#if USE_SDIO
|
||||
if (!sd.begin()) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
#else // USE_SDIO
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
#endif // USE_SDIO
|
||||
cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
|
||||
cout << F("Card size: ") << sd.card()->cardSize()*512E-9;
|
||||
cout << F(" GB (GB = 1E9 bytes)") << endl;
|
||||
|
||||
cidDmp();
|
||||
|
||||
// open or create file - truncate existing file.
|
||||
if (!file.open("bench.dat", O_RDWR | O_CREAT | O_TRUNC)) {
|
||||
error("open failed");
|
||||
}
|
||||
|
||||
// fill buf with known data
|
||||
for (size_t i = 0; i < (BUF_SIZE-2); i++) {
|
||||
buf[i] = 'A' + (i % 26);
|
||||
}
|
||||
buf[BUF_SIZE-2] = '\r';
|
||||
buf[BUF_SIZE-1] = '\n';
|
||||
|
||||
cout << F("File size ") << FILE_SIZE_MB << F(" MB\n");
|
||||
cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n");
|
||||
cout << F("Starting write test, please wait.") << endl << endl;
|
||||
|
||||
// do write test
|
||||
uint32_t n = FILE_SIZE/sizeof(buf);
|
||||
cout <<F("write speed and latency") << endl;
|
||||
cout << F("speed,max,min,avg") << endl;
|
||||
cout << F("KB/Sec,usec,usec,usec") << endl;
|
||||
for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
|
||||
file.truncate(0);
|
||||
maxLatency = 0;
|
||||
minLatency = 9999999;
|
||||
totalLatency = 0;
|
||||
t = millis();
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
uint32_t m = micros();
|
||||
if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||
sd.errorPrint("write failed");
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
m = micros() - m;
|
||||
if (maxLatency < m) {
|
||||
maxLatency = m;
|
||||
}
|
||||
if (minLatency > m) {
|
||||
minLatency = m;
|
||||
}
|
||||
totalLatency += m;
|
||||
}
|
||||
file.sync();
|
||||
t = millis() - t;
|
||||
s = file.fileSize();
|
||||
cout << s/t <<',' << maxLatency << ',' << minLatency;
|
||||
cout << ',' << totalLatency/n << endl;
|
||||
}
|
||||
cout << endl << F("Starting read test, please wait.") << endl;
|
||||
cout << endl <<F("read speed and latency") << endl;
|
||||
cout << F("speed,max,min,avg") << endl;
|
||||
cout << F("KB/Sec,usec,usec,usec") << endl;
|
||||
|
||||
// do read test
|
||||
for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) {
|
||||
file.rewind();
|
||||
maxLatency = 0;
|
||||
minLatency = 9999999;
|
||||
totalLatency = 0;
|
||||
t = millis();
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
buf[BUF_SIZE-1] = 0;
|
||||
uint32_t m = micros();
|
||||
int32_t nr = file.read(buf, sizeof(buf));
|
||||
if (nr != sizeof(buf)) {
|
||||
sd.errorPrint("read failed");
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
m = micros() - m;
|
||||
if (maxLatency < m) {
|
||||
maxLatency = m;
|
||||
}
|
||||
if (minLatency > m) {
|
||||
minLatency = m;
|
||||
}
|
||||
totalLatency += m;
|
||||
if (buf[BUF_SIZE-1] != '\n') {
|
||||
error("data check");
|
||||
}
|
||||
}
|
||||
s = file.fileSize();
|
||||
t = millis() - t;
|
||||
cout << s/t <<',' << maxLatency << ',' << minLatency;
|
||||
cout << ',' << totalLatency/n << endl;
|
||||
}
|
||||
cout << endl << F("Done") << endl;
|
||||
file.close();
|
||||
}
|
150
libraries/SdFat-1.1.0/examples/dataLogger/dataLogger.ino
Normal file
150
libraries/SdFat-1.1.0/examples/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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
delay(1000);
|
||||
|
||||
Serial.println(F("Type any character to start"));
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// 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"));
|
||||
SysCall::halt();
|
||||
}
|
||||
}
|
88
libraries/SdFat-1.1.0/examples/fgets/fgets.ino
Normal file
88
libraries/SdFat-1.1.0/examples/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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::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) {}
|
73
libraries/SdFat-1.1.0/examples/formatting/formatting.ino
Normal file
73
libraries/SdFat-1.1.0/examples/formatting/formatting.ino
Normal file
@ -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) {
|
||||
SysCall::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-1.1.0/examples/getline/getline.ino
Normal file
84
libraries/SdFat-1.1.0/examples/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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// F stores strings in flash to save RAM
|
||||
cout << F("Type any character to start\n");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// make the test file
|
||||
makeTestFile();
|
||||
|
||||
// run the example
|
||||
testGetline();
|
||||
cout << "\nDone!\n";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop(void) {}
|
106
libraries/SdFat-1.1.0/examples/rename/rename.ino
Normal file
106
libraries/SdFat-1.1.0/examples/rename/rename.ino
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* This program demonstrates use of SdFile::rename()
|
||||
* and SdFat::rename().
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
// SD chip select pin
|
||||
const uint8_t chipSelect = SS;
|
||||
|
||||
// file system
|
||||
SdFat sd;
|
||||
|
||||
// Serial print stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
//------------------------------------------------------------------------------
|
||||
// store error strings in flash to save RAM
|
||||
#define error(s) sd.errorHalt(F(s))
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
cout << F("Insert an empty SD. Type any character to start.") << endl;
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
// Initialize at the highest speed supported by the board that is
|
||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
// Remove file/dirs from previous run.
|
||||
if (sd.exists("dir2/DIR3/NAME3.txt")) {
|
||||
cout << F("Removing /dir2/DIR3/NAME3.txt") << endl;
|
||||
if (!sd.remove("dir2/DIR3/NAME3.txt") ||
|
||||
!sd.rmdir("dir2/DIR3/") ||
|
||||
!sd.rmdir("dir2/")) {
|
||||
error("remove/rmdir failed");
|
||||
}
|
||||
}
|
||||
// create a file and write one line to the file
|
||||
SdFile file("Name1.txt", O_WRONLY | O_CREAT);
|
||||
if (!file.isOpen()) {
|
||||
error("Name1.txt");
|
||||
}
|
||||
file.println("A test line for Name1.txt");
|
||||
|
||||
// rename the file name2.txt and add a line.
|
||||
if (!file.rename("name2.txt")) {
|
||||
error("name2.txt");
|
||||
}
|
||||
file.println("A test line for name2.txt");
|
||||
|
||||
// list files
|
||||
cout << F("------") << endl;
|
||||
sd.ls(LS_R);
|
||||
|
||||
// make a new directory - "Dir1"
|
||||
if (!sd.mkdir("Dir1")) {
|
||||
error("Dir1");
|
||||
}
|
||||
|
||||
// move file into Dir1, rename it NAME3.txt and add a line
|
||||
if (!file.rename("Dir1/NAME3.txt")) {
|
||||
error("NAME3.txt");
|
||||
}
|
||||
file.println("A line for Dir1/NAME3.txt");
|
||||
|
||||
// list files
|
||||
cout << F("------") << endl;
|
||||
sd.ls(LS_R);
|
||||
|
||||
// make directory "dir2"
|
||||
if (!sd.mkdir("dir2")) {
|
||||
error("dir2");
|
||||
}
|
||||
|
||||
// close file before rename(oldPath, newPath)
|
||||
file.close();
|
||||
|
||||
// move Dir1 into dir2 and rename it DIR3
|
||||
if (!sd.rename("Dir1", "dir2/DIR3")) {
|
||||
error("dir2/DIR3");
|
||||
}
|
||||
|
||||
// open file for append in new location and add a line
|
||||
if (!file.open("dir2/DIR3/NAME3.txt", O_WRONLY | O_APPEND)) {
|
||||
error("dir2/DIR3/NAME3.txt");
|
||||
}
|
||||
file.println("A line for dir2/DIR3/NAME3.txt");
|
||||
file.close();
|
||||
|
||||
// list files
|
||||
cout << F("------") << endl;
|
||||
sd.ls(LS_R);
|
||||
|
||||
cout << F("Done") << endl;
|
||||
}
|
||||
void loop() {}
|
42
libraries/SdFat-1.1.0/examples/wipe/wipe.ino
Normal file
42
libraries/SdFat-1.1.0/examples/wipe/wipe.ino
Normal file
@ -0,0 +1,42 @@
|
||||
// Example to wipe all data from an already formatted SD.
|
||||
#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) {
|
||||
SysCall::yield();
|
||||
}
|
||||
Serial.println("Type 'Y' to wipe all data.");
|
||||
while (!Serial.available()) {
|
||||
SysCall::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() {
|
||||
}
|
BIN
libraries/SdFat-1.1.0/extras/AnalogBinLoggerExtras/ADC_ENOB.PNG
Normal file
BIN
libraries/SdFat-1.1.0/extras/AnalogBinLoggerExtras/ADC_ENOB.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 87 KiB |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,98 @@
|
||||
Static Tests of the Arduino Internal ADC.
|
||||
|
||||
Several people have asked about the DC accuracy of the Arduino ADC when used in my data logging applications at slow sample rates.
|
||||
|
||||
Here are my results of some "hobby level" measurements of the Arduino ADC.
|
||||
|
||||
One question is how important is the ADC clock rate. I did measurents for an ADC clock rate of 125 kHz to 2MHz.
|
||||
|
||||
Another question is how much does Noise Reduction Mode help. I did a series of measurements using this mode.
|
||||
|
||||
Noise Reduction Mode only reduced the mean absolute error slightly.
|
||||
|
||||
I do calibration to remove Offset Error and Gain Error. Calibration is very important for good accuracy.
|
||||
|
||||
These tests depend on the Arduino voltage regulator providing a stable voltage during the tests. The Arduino ADC reference voltage is Vcc for these tests. This may not be realistic for practical applications
|
||||
|
||||
Integral Non-linearity (INL) is the main remaining source of error.
|
||||
|
||||
Here are my results for static (DC) tests of the internal ADC for three UNOs.
|
||||
|
||||
The Arduinos are powered by a high quality nine volt power supply.
|
||||
|
||||
These tests measure a DC level so do not include problems due to time jitter, S/H time, and other dynamic errors.
|
||||
There are several studies of the dynamic behavior of the Arduino ADC that determine ENOB (Effective Number Of Bits).
|
||||
|
||||
I used a shield with a 12-bit MCP4921 DAC to generate voltage levels. This ADC has an output buffer so it provides a very low impedance source.
|
||||
|
||||
I measured the voltage of the DAC with a calibrated 18-bit MCP3422 ADC on the shield.
|
||||
|
||||
I used DAC levels from 20 to 4075 to avoid zero offset errors at low voltages and DAC buffer problems at high voltages.
|
||||
|
||||
Each series of measurements has 4056 data points.
|
||||
|
||||
This is a voltage range of about 0.023 to 4.972 volts.
|
||||
|
||||
I calibrated the Arduino ADC for each series of measurements with a linear fit of the form.
|
||||
|
||||
v = a + b*adcValue
|
||||
|
||||
Errors are the difference between the value measured with the 18-bit ADC and the calibrated value measured with the AVR ADC.
|
||||
|
||||
I also show the results for no calibration, the NoCal column, using the datasheet formula.
|
||||
|
||||
Vin = Vref*adcValue/1024
|
||||
|
||||
|
||||
The rows in the tables tables are.
|
||||
|
||||
Min - minimum error in millivolts
|
||||
|
||||
Max - maximum error in millivolts
|
||||
|
||||
MAE - mean absolute error in millivolts
|
||||
|
||||
|
||||
The columns in the tables are:
|
||||
|
||||
Ideal - results for a perfect 10-bit ADC for comparison.
|
||||
|
||||
NoCal - datasheet formula (5/1024)*adcValue with Noise Reduction Mode.
|
||||
|
||||
NR128 - Noise Reduction mode with Prescaler of 128 (ADC clock of 125 kHz).
|
||||
|
||||
PS128 - analogRead with Prescaler of 128 (ADC clock of 125 kHz).
|
||||
|
||||
PS64 - analogRead with Prescaler of 64 (ADC clock of 250 kHz).
|
||||
|
||||
PS32 - analogRead with Prescaler of 32 (ADC clock of 500 kHz).
|
||||
|
||||
PS16 - analogRead with Prescaler of 16 (ADC clock of 1 MHz).
|
||||
|
||||
PS8 - analogRead with Prescaler of 8 (ADC clock of 2 MHz).
|
||||
|
||||
|
||||
Results for three UNO Arduinos
|
||||
|
||||
First Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -2.43 -3.72 -4.01 -3.88 -4.53 -6.57 -27.18
|
||||
Max 2.44 11.69 3.74 4.24 4.15 5.17 8.69 23.21
|
||||
MAE 1.22 5.02 1.33 1.38 1.37 1.44 1.96 4.11
|
||||
|
||||
Second Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -9.24 -4.87 -4.86 -5.05 -5.34 -6.52 -24.04
|
||||
Max 2.44 11.62 3.95 4.64 4.69 5.71 8.41 21.29
|
||||
MAE 1.22 5.33 1.41 1.43 1.44 1.53 2.02 4.05
|
||||
|
||||
Third Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -7.88 -4.12 -4.40 -4.32 -4.41 -6.97 -26.93
|
||||
Max 2.44 12.53 3.80 4.04 4.18 5.27 8.84 24.59
|
||||
MAE 1.22 4.85 1.29 1.33 1.34 1.42 1.91 4.10
|
||||
|
||||
|
BIN
libraries/SdFat-1.1.0/extras/AnalogBinLoggerExtras/DATA.png
Normal file
BIN
libraries/SdFat-1.1.0/extras/AnalogBinLoggerExtras/DATA.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
BIN
libraries/SdFat-1.1.0/extras/AnalogBinLoggerExtras/FFT.png
Normal file
BIN
libraries/SdFat-1.1.0/extras/AnalogBinLoggerExtras/FFT.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
@ -0,0 +1,21 @@
|
||||
Maximum Sample Rate Table
|
||||
|
||||
ADC clock kHz
|
||||
125 250 500 1000
|
||||
pins
|
||||
1 7692 14286 25000 40000
|
||||
2 3810 6667 11111 16667
|
||||
3 2572 4790 8421 13559
|
||||
4 1942 3636 6452 10526
|
||||
5 1559 2930 5229 8602
|
||||
6 1303 2454 4396 7273
|
||||
7 1119 2111 3791 6299
|
||||
8 980 1852 3333 5556
|
||||
9 872 1649 2974 4969
|
||||
10 786 1487 2685 4494
|
||||
11 715 1354 2446 4103
|
||||
12 656 1242 2247 3774
|
||||
13 606 1148 2078 3493
|
||||
14 563 1067 1932 3252
|
||||
15 525 996 1806 3042
|
||||
16 493 935 1695 2857
|
@ -0,0 +1,39 @@
|
||||
#ifndef AnalogBinLogger_h
|
||||
#define AnalogBinLogger_h
|
||||
//------------------------------------------------------------------------------
|
||||
// First block of file.
|
||||
struct metadata_t {
|
||||
unsigned long adcFrequency; // ADC clock frequency
|
||||
unsigned long cpuFrequency; // CPU clock frequency
|
||||
unsigned long sampleInterval; // Sample interval in CPU cycles.
|
||||
unsigned long recordEightBits; // Size of ADC values, nonzero for 8-bits.
|
||||
unsigned long pinCount; // Number of analog pins in a sample.
|
||||
unsigned long pinNumber[123]; // List of pin numbers in a sample.
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for 8-bit ADC mode.
|
||||
const size_t DATA_DIM8 = 508;
|
||||
struct block8_t {
|
||||
unsigned short count; // count of data bytes
|
||||
unsigned short overrun; // count of overruns since last block
|
||||
unsigned char data[DATA_DIM8];
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for 10-bit ADC mode.
|
||||
const size_t DATA_DIM16 = 254;
|
||||
struct block16_t {
|
||||
unsigned short count; // count of data bytes
|
||||
unsigned short overrun; // count of overruns since last block
|
||||
unsigned short data[DATA_DIM16];
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
// Data block for PC use
|
||||
struct adcdata_t {
|
||||
unsigned short count; // count of data bytes
|
||||
unsigned short overrun; // count of overruns since last block
|
||||
union {
|
||||
unsigned char u8[DATA_DIM8];
|
||||
unsigned short u16[DATA_DIM16];
|
||||
} data;
|
||||
};
|
||||
#endif // AnalogBinLogger_h
|
@ -0,0 +1,82 @@
|
||||
#include <stdio.h>
|
||||
#include "AnalogBinLogger.h"
|
||||
FILE *source;
|
||||
FILE *destination;
|
||||
int count = 0;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
metadata_t meta;
|
||||
adcdata_t adc;
|
||||
// Make sure no padding/size problems.
|
||||
if (sizeof(meta) != 512 || sizeof(adc) != 512) {
|
||||
printf("block size error\n");
|
||||
return 0;
|
||||
}
|
||||
if (argc != 3) {
|
||||
printf("missing arguments:\n");
|
||||
printf("%s binFile csvFile\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
source = fopen(argv[1], "rb");
|
||||
if (!source) {
|
||||
printf("open failed for %s\n", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
if (fread(&meta, sizeof(meta), 1, source) != 1) {
|
||||
printf("read meta data failed\n");
|
||||
return 0;
|
||||
}
|
||||
if ( meta.pinCount == 0
|
||||
|| meta.pinCount > (sizeof(meta.pinNumber)/sizeof(meta.pinNumber[0]))
|
||||
|| meta.adcFrequency < 50000 || meta.adcFrequency > 4000000) {
|
||||
printf("Invalid meta data\n");
|
||||
return 0;
|
||||
}
|
||||
destination = fopen(argv[2], "w");
|
||||
if (!destination) {
|
||||
printf("open failed for %s\n", argv[2]);
|
||||
return 0;
|
||||
}
|
||||
int pinCount = meta.pinCount;
|
||||
printf("pinCount: %d\n", pinCount);
|
||||
printf("Sample pins:");
|
||||
for (unsigned i = 0; i < meta.pinCount; i++) {
|
||||
printf(" %d", meta.pinNumber[i]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("ADC clock rate: %g kHz\n", 0.001*meta.adcFrequency);
|
||||
float sampleInterval = (float)meta.sampleInterval/(float)meta.cpuFrequency;
|
||||
printf("Sample rate: %g per sec\n", 1.0/sampleInterval);
|
||||
printf("Sample interval: %.4f usec\n", 1.0e6*sampleInterval);
|
||||
|
||||
fprintf(destination, "Interval,%.4f,usec\n", 1.0e6*sampleInterval);
|
||||
// Write header with pin numbers
|
||||
for (int i = 0; i < ((int)meta.pinCount - 1); i++) {
|
||||
fprintf(destination, "pin%d,", meta.pinNumber[i]);
|
||||
}
|
||||
fprintf(destination, "pin%d\n", meta.pinNumber[meta.pinCount - 1]);
|
||||
unsigned maxCount = meta.recordEightBits ? DATA_DIM8 : DATA_DIM16;
|
||||
while (!feof(source)) {
|
||||
if (fread(&adc, sizeof(adc), 1, source) != 1) break;
|
||||
if (adc.count > maxCount) {
|
||||
printf("****Invalid data block****\n");
|
||||
return 0;
|
||||
}
|
||||
if (adc.overrun) {
|
||||
fprintf(destination, "Overruns,%d\n", adc.overrun);
|
||||
}
|
||||
for (int i = 0; i < adc.count; i++) {
|
||||
unsigned value = meta.recordEightBits ? adc.data.u8[i] : adc.data.u16[i];
|
||||
if ((i + 1)%pinCount) {
|
||||
fprintf(destination, "%d,", value);
|
||||
} else {
|
||||
fprintf(destination, "%d\n", value);
|
||||
}
|
||||
}
|
||||
count += adc.count;
|
||||
}
|
||||
printf("%d ADC values read\n", count);
|
||||
fclose(source);
|
||||
fclose(destination);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
AnalogBinLogger.ino logs analog data to a binary SD file at high rates.
|
||||
|
||||
Samples are logged at regular intervals by using timer1. Timer/Counter1
|
||||
Compare Match B is used to trigger the ADC for the first pin in a sample.
|
||||
The ADC is triggered for remaining sample pins in the ADC conversion complete
|
||||
interrupt routine.
|
||||
|
||||
Data is captured in the ADC interrupt routine and saved in 512 byte buffers.
|
||||
|
||||
Buffered data is written to the SD in a function called from loop(). The
|
||||
entire data set is written to a large contiguous file as a single multi-block
|
||||
write. This reduces write latency problems.
|
||||
|
||||
Many inexpensive SD cards work well at lower rates. I used a $6.00
|
||||
SanDisk 4 GB class 4 card for testing.
|
||||
|
||||
SanDisk class 4 cards work well at fairly high rates. I used the 4 GB SanDisk
|
||||
card to log a single pin at 40,000 samples per second.
|
||||
|
||||
You may need to increase the time between samples if your card has higher
|
||||
latency. Using a Mega Arduino can help since it has more buffering.
|
||||
|
||||
The bintocsv folder contains a PC program for converting binary files to
|
||||
CSV files. Build it from the included source files. bintocvs is a command line program.
|
||||
|
||||
bintocsv binFile csvFile
|
||||
|
||||
AnalogBinLogger requires a recent version of the SdFat library. The SdFat
|
||||
folder contains a beta version I used for development.
|
||||
|
||||
The latest stable version is here:
|
||||
http://code.google.com/p/sdfatlib/downloads/list
|
||||
|
||||
You also need to install the included BufferedWriter library. It provides
|
||||
fast text formatting.
|
||||
|
||||
Example data for a 2 kHz sine wave logged at 40,000 samples per second is
|
||||
shown in DATA.PNG and FFT.PNG shows a FFT of the data. See ExcelFFT.pdf
|
||||
in the ADCdocs folder for details on calculating a FFT.
|
||||
|
||||
The accuracy of the ADC samples depends on the ADC clock rate. See the
|
||||
ADC_ENOB.PNG file for a plot of accuracy vs ADC clock frequency.
|
||||
|
||||
See files in the ADCdocs folder for more information on ADC accuracy.
|
||||
|
||||
To modify this program you will need a good knowledge of the Arduino
|
||||
ADC, timer1 and C++ programming. This is not for the newbie.
|
||||
|
||||
I have an LED and resistor connected to pin 3 to signal fatal errors and
|
||||
data overruns. Fatal errors are indicated by a blinking led. Overrun errors
|
||||
are indicated by a solid lit led. The count of samples dropped is written
|
||||
to the SD and data logging continues.
|
||||
|
||||
You can disable the error led feature by setting the error pin number negative:
|
||||
|
||||
To use AnalogBinLogger, install these items.
|
||||
|
||||
Place the BufferWriter and SdFat folders in your sketchbook libraries folder.
|
||||
|
||||
Place the AnalogIsrLogger folder in your sketchbook folder.
|
||||
|
||||
You must edit the configuration constants at the beginning of the program
|
||||
to set the sample pins, sample rate, and other configuration values.
|
||||
|
||||
Initially the program is setup to log the first five analog pins at 5000
|
||||
samples per second. Change these values to suit your needs.
|
||||
|
||||
See RateTable.txt for maximum allowed sample rates vs pin count and ADC clock
|
||||
frequency.
|
||||
|
||||
The program has four commands:
|
||||
|
||||
c - convert file to CSV
|
||||
d - dump data to Serial
|
||||
e - overrun error details
|
||||
r - record ADC data
|
||||
|
||||
All commands can be terminated by entering a character from the serial monitor.
|
||||
|
||||
The c command converts the current binary file to a text file. Entering a
|
||||
character on the serial monitor terminates the command.
|
||||
|
||||
The d command converts the binary file to text and displays it on the serial
|
||||
monitor. Entering a character on the serial monitor terminates the command.
|
||||
|
||||
The e command displays details about overruns in the current binary file.
|
||||
Data overruns happen when data samples are lost due to long write latency
|
||||
of the SD.
|
||||
|
||||
The r command will record ADC data to a binary file. It will terminate
|
||||
when a character is entered on the serial monitor or the the maximum file
|
||||
block count has been reached.
|
||||
|
||||
A number of program options can be set by changing constants at the beginning
|
||||
of the program.
|
403
libraries/SdFat-1.1.0/extras/MainPage/SdFatmainpage.h
Normal file
403
libraries/SdFat-1.1.0/extras/MainPage/SdFatmainpage.h
Normal file
@ -0,0 +1,403 @@
|
||||
/**
|
||||
* Copyright (c) 20011-2018 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
|
||||
<CENTER>Copyright © 2012-2018 by William Greiman
|
||||
</CENTER>
|
||||
|
||||
\section Intro Introduction
|
||||
The Arduino %SdFat Library is a minimal implementation of FAT16 and FAT32
|
||||
file systems on SD flash memory cards. Standard SD and high capacity SDHC
|
||||
cards are supported.
|
||||
|
||||
Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT
|
||||
nonzero in SdFatConfig.h.
|
||||
|
||||
The %SdFat library supports Long %File Names or short 8.3 names.
|
||||
Edit the SdFatConfig.h file to select short or long file names.
|
||||
|
||||
The main classes in %SdFat are SdFat, SdFatEX, SdFatSoftSpi, SdFatSoftSpiEX,
|
||||
SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream,
|
||||
and \ref ofstream.
|
||||
|
||||
The SdFat, SdFatEX, SdFatSoftSpi and SdFatSoftSpiEX classes maintain a
|
||||
FAT volume, a current working directory, and simplify initialization
|
||||
of other classes. The SdFat and SdFatEX classes uses a fast custom hardware SPI
|
||||
implementation. The SdFatSoftSpi and SdFatSoftSpiEX classes uses software SPI.
|
||||
|
||||
the SdFatEX and SdFatSoftSpiEX use extended multi-block I/O for enhanced
|
||||
performance. These classes must have exclusive use of the SPI bus.
|
||||
|
||||
The SdBaseFile class provides basic file access functions such as open(),
|
||||
binary read(), binary write(), close(), remove(), and sync(). SdBaseFile
|
||||
is the smallest file class.
|
||||
|
||||
The SdFile class has all the SdBaseFile class functions plus the Arduino
|
||||
Print class functions.
|
||||
|
||||
The File class has all the SdBaseFile functions plus the functions in
|
||||
the Arduino SD.h File class. This provides compatibility with the
|
||||
Arduino SD.h library.
|
||||
|
||||
The StdioStream class implements functions similar to Linux/Unix standard
|
||||
buffered input/output.
|
||||
|
||||
The \ref fstream class implements C++ iostreams for both reading and writing
|
||||
text files.
|
||||
|
||||
The \ref ifstream class implements C++ iostreams for reading text files.
|
||||
|
||||
The \ref ofstream class implements C++ iostreams for writing text files.
|
||||
|
||||
The classes \ref ifstream, \ref ofstream, \ref istream, and \ref ostream
|
||||
follow the C++ \ref iostream standard when possible.
|
||||
|
||||
There are many tutorials and much documentation about using C++ iostreams
|
||||
on the web.
|
||||
|
||||
http://www.cplusplus.com/ is a good C++ site for learning iostreams.
|
||||
|
||||
The classes \ref ibufstream and \ref obufstream format and parse character
|
||||
strings in memory buffers.
|
||||
|
||||
the classes ArduinoInStream and ArduinoOutStream provide iostream functions
|
||||
for Serial, LiquidCrystal, and other devices.
|
||||
|
||||
A number of example are provided in the %SdFat/examples folder. These were
|
||||
developed to test %SdFat and illustrate its use.
|
||||
|
||||
\section Install Installation
|
||||
|
||||
You must manually install SdFat by copying the SdFat folder from the download
|
||||
package to the Arduino libraries folder in your sketch folder.
|
||||
|
||||
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 folder.
|
||||
|
||||
Set USE_LONG_FILE_NAMES nonzero to enable Long %File Names. By default,
|
||||
Long %File Names are enabled. For the leanest fastest library disable
|
||||
Long %File Names. Long %File names require extra flash but no extra RAM.
|
||||
Opening Long %File Names can be slower than opening Short %File Names.
|
||||
Data read and write performance is not changed by the type of %File Name.
|
||||
|
||||
If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX
|
||||
will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero,
|
||||
the class SdFatSoftSpiEX will be defined.
|
||||
These classes used extended multi-block SD I/O for better performance.
|
||||
the SPI bus may not be shared with other devices in this mode.
|
||||
|
||||
Set USE_STANDARD_SPI_LIBRARY and ENABLE_SOFTWARE_SPI_CLASS to
|
||||
enable various SPI options. set USE_STANDARD_SPI_LIBRARY to use the standard
|
||||
Arduino SPI library. set ENABLE_SOFTWARE_SPI_CLASS to enable the SdFatSoftSpi
|
||||
class which uses software SPI.
|
||||
|
||||
To enable SD card CRC checking set USE_SD_CRC nonzero.
|
||||
|
||||
Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes.
|
||||
FAT12 has not been well tested and requires additional flash.
|
||||
|
||||
\section SDPath Paths and Working Directories
|
||||
|
||||
Relative paths in SdFat are resolved in a manner similar to Windows.
|
||||
|
||||
Each instance of SdFat has a current directory. In SdFat 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 SdFat::chdir(path).
|
||||
|
||||
The call sd.chdir("/2014") will change the volume working directory
|
||||
for sd to "/2014", assuming "/2014" exists.
|
||||
|
||||
Relative paths for SdFat 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".
|
||||
|
||||
SdFat has a 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 SdFat::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 then open "/music/BigBand.wav" on sd2.
|
||||
|
||||
The following functions are used to change or get current directories.
|
||||
See the html documentation for more information.
|
||||
@code
|
||||
bool SdFat::chdir(bool set_cwd = false);
|
||||
bool SdFat::chdir(const char* path, bool set_cwd = false);
|
||||
void SdFat::chvol();
|
||||
SdBaseFile* SdFat::vwd();
|
||||
static SdBaseFile* SdBaseFile::cwd();
|
||||
@endcode
|
||||
|
||||
\section SDcard SD\SDHC Cards
|
||||
|
||||
Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and
|
||||
most consumer devices use the 4-bit parallel SD protocol. A card that
|
||||
functions well on A PC or Mac may not work well on the Arduino.
|
||||
|
||||
Most cards have good SPI read performance but cards vary widely in SPI
|
||||
write performance. Write performance is limited by how efficiently the
|
||||
card manages internal erase/remapping operations. The Arduino cannot
|
||||
optimize writes to reduce erase operations because of its limit RAM.
|
||||
|
||||
SanDisk cards generally have good write performance. They seem to have
|
||||
more internal RAM buffering than other cards and therefore can limit
|
||||
the number of flash erase operations that the Arduino forces due to its
|
||||
limited RAM.
|
||||
|
||||
\section Hardware Hardware Configuration
|
||||
|
||||
%SdFat was developed using an
|
||||
<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A>
|
||||
Data Logging Shield.
|
||||
|
||||
The hardware interface to the SD card should not use a resistor based level
|
||||
shifter. %SdFat sets the SPI bus frequency to 8 MHz which results in signal
|
||||
rise times that are too slow for the edge detectors in many newer SD card
|
||||
controllers when resistor voltage dividers are used.
|
||||
|
||||
The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the
|
||||
74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield
|
||||
uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the
|
||||
74LCX245.
|
||||
|
||||
If you are using a resistor based level shifter and are having problems try
|
||||
setting the SPI bus frequency to 4 MHz. This can be done by using
|
||||
card.init(SPI_HALF_SPEED) to initialize the SD card.
|
||||
|
||||
A feature to use software SPI is available. Software SPI is slower
|
||||
than hardware SPI but allows any digital pins to be used. See
|
||||
SdFatConfig.h for software SPI definitions.
|
||||
|
||||
\section comment Bugs and Comments
|
||||
|
||||
If you wish to report bugs or have comments, send email to
|
||||
fat16lib@sbcglobal.net. If possible, include a simple program that illustrates
|
||||
the bug or problem.
|
||||
|
||||
\section Trouble Troubleshooting
|
||||
|
||||
The two example programs QuickStart, and SdInfo are useful for troubleshooting.
|
||||
|
||||
A message like this from SdInfo with errorCode 0X1 indicates the SD card
|
||||
is not seen by SdFat. This is often caused by a wiring error and reformatting
|
||||
the card will not solve the problem.
|
||||
<PRE>
|
||||
cardBegin failed
|
||||
SD errorCode: 0X1
|
||||
SD errorData: 0XFF
|
||||
</PRE>
|
||||
Here is a similar message from QuickStart:
|
||||
<PRE>
|
||||
SD initialization failed.
|
||||
Do not reformat the card!
|
||||
Is the card correctly inserted?
|
||||
Is chipSelect set to the correct value?
|
||||
Does another SPI device need to be disabled?
|
||||
Is there a wiring/soldering problem?
|
||||
|
||||
errorCode: 0x1, errorData: 0xff
|
||||
</PRE>
|
||||
Here is a message from QuickStart that indicates a formatting problem:
|
||||
<PRE>
|
||||
Card successfully initialized.
|
||||
Can't find a valid FAT16/FAT32 partition.
|
||||
Try reformatting the card. For best results use
|
||||
the SdFormatter program in SdFat/examples or download
|
||||
and use SDFormatter from www.sdcard.org/downloads.
|
||||
</PRE>
|
||||
|
||||
The best source of recent information and help is the Arduino forum.
|
||||
|
||||
http://arduino.cc/forum/
|
||||
|
||||
Also search the Adafruit forum.
|
||||
|
||||
http://forums.adafruit.com/
|
||||
|
||||
If you are using a Teensy try.
|
||||
|
||||
http://forum.pjrc.com/forum.php
|
||||
|
||||
\section SdFatClass SdFat Usage
|
||||
|
||||
SdFat supports Long File Names. Long names in SdFat are limited to 7-bit
|
||||
ASCII characters in the range 0X20 - 0XFE The following are reserved characters:
|
||||
<ul>
|
||||
<li>< (less than)
|
||||
<li>> (greater than)
|
||||
<li>: (colon)
|
||||
<li>" (double quote)
|
||||
<li>/ (forward slash)
|
||||
<li>\ (backslash)
|
||||
<li>| (vertical bar or pipe)
|
||||
<li>? (question mark)
|
||||
<li>* (asterisk)
|
||||
</ul>
|
||||
%SdFat uses a slightly restricted form of short names.
|
||||
Short names are limited to 8 characters followed by an optional period (.)
|
||||
and extension of up to 3 characters. The characters may be any combination
|
||||
of letters and digits. The following special characters are also allowed:
|
||||
|
||||
$ % ' - _ @ ~ ` ! ( ) { } ^ # &
|
||||
|
||||
Short names are always converted to upper case and their original case
|
||||
value is lost. Files that have a base-name where all characters have the
|
||||
same case and an extension where all characters have the same case will
|
||||
display properly. Examples this type name are UPPER.low, lower.TXT,
|
||||
UPPER.TXT, and lower.txt.
|
||||
|
||||
An application which writes to a file using print(), println() or
|
||||
write() must close the file or call sync() at the appropriate time to
|
||||
force data and directory information to be written to the SD Card.
|
||||
|
||||
Applications must use care calling sync()
|
||||
since 2048 bytes of I/O is required to update file and
|
||||
directory information. This includes writing the current data block, reading
|
||||
the block that contains the directory entry for update, writing the directory
|
||||
block back and reading back the current data block.
|
||||
|
||||
It is possible to open a file with two or more instances of a file object.
|
||||
A file may be corrupted if data is written to the file by more than one
|
||||
instance of a file object.
|
||||
|
||||
\section HowTo How to format SD Cards as FAT Volumes
|
||||
|
||||
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 SdFat formatter 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 ExampleFilder Examples
|
||||
|
||||
A number of examples are provided in the SdFat/examples folder.
|
||||
See the html documentation for a list.
|
||||
|
||||
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:
|
||||
|
||||
AnalogBinLogger - Fast AVR ADC logger - see the AnalogBinLoggerExtras folder.
|
||||
|
||||
bench - A read/write benchmark.
|
||||
|
||||
dataLogger - A simple modifiable data logger.
|
||||
|
||||
DirectoryFunctions - Demo of chdir(), ls(), mkdir(), and rmdir().
|
||||
|
||||
fgets - Demo of the fgets read line/string function.
|
||||
|
||||
formating - Print a table with various formatting options.
|
||||
|
||||
getline - Example of getline from section 27.7.1.3 of the C++ standard.
|
||||
|
||||
LongFileName - Example use of openNext, printName, and open by index.
|
||||
|
||||
LowLatencyLogger - A data logger for higher data rates. ADC version.
|
||||
|
||||
LowLatencyLoggerADXL345 - A data logger for higher data rates. ADXL345 SPI.
|
||||
|
||||
LowLatencyLoggerMPU6050 - A data logger for higher data rates. MPU6050 I2C.
|
||||
|
||||
OpenNext - Open all files in the root dir and print their filename.
|
||||
|
||||
PrintBenchmark - A simple benchmark for printing to a text file.
|
||||
|
||||
QuickStart - A program to quickly test your SD card and SD shield/module.
|
||||
|
||||
RawWrite - A test of raw write functions for contiguous files.
|
||||
|
||||
ReadCsv - Function to read a CSV text file one field at a time.
|
||||
|
||||
ReadCsvStream - Read a comma-separated value file using iostream extractors.
|
||||
|
||||
ReadCsvArray - Read a two dimensional array from a CSV file.
|
||||
|
||||
ReadWrite - Compatibility test of Arduino SD ReadWrite example.
|
||||
|
||||
rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath).
|
||||
|
||||
SdFormatter - This program will format an SD or SDHC card.
|
||||
|
||||
SoftwareSpi - Simple demonstration of the SdFatSoftSpi template class.
|
||||
|
||||
SdInfo - Initialize an SD card and analyze its structure for trouble shooting.
|
||||
|
||||
StdioBench - Demo and test of stdio style stream.
|
||||
|
||||
Timestamp - Sets file create, modify, and access timestamps.
|
||||
|
||||
TwoCards - Example using two SD cards.
|
||||
|
||||
VolumeFreeSpace - Demonstrate the freeClusterCount() call.
|
||||
|
||||
wipe - Example to wipe all data from an already formatted SD.
|
||||
*/
|
10
libraries/SdFat-1.1.0/extras/SdFat.html
Normal file
10
libraries/SdFat-1.1.0/extras/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>
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 20011-2017 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.
|
||||
*/
|
||||
#include <SdFatTestSuite.h>
|
||||
static uint16_t failCount;
|
||||
static uint16_t testCount;
|
||||
static Print* testOut = &Serial;
|
||||
//------------------------------------------------------------------------------
|
||||
static size_t strlenPGM(PGM_P str) {
|
||||
PGM_P end = str;
|
||||
while (pgm_read_byte(end++)) {}
|
||||
return end - str;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testBegin() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {} // wait for leonardo
|
||||
testOut = &Serial;
|
||||
Serial.println(F("Type any character to begin."));
|
||||
while (Serial.read() <= 0) {}
|
||||
delay(200); // Catch Due reset problem
|
||||
|
||||
testOut->print(F("FreeStack: "));
|
||||
testOut->println(FreeStack());
|
||||
testOut->println();
|
||||
failCount = 0;
|
||||
testCount = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testEnd() {
|
||||
testOut->println();
|
||||
testOut->println(F("Compiled: " __DATE__ " " __TIME__));
|
||||
testOut->print(F("FreeStack: "));
|
||||
testOut->println(FreeStack());
|
||||
testOut->print(F("Test count: "));
|
||||
testOut->println(testCount);
|
||||
testOut->print(F("Fail count: "));
|
||||
testOut->println(failCount);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
static void testResult(bool b, uint8_t n) {
|
||||
while (n++ < 60) testOut->write(' ');
|
||||
if (b) {
|
||||
testOut->println(F("..ok"));
|
||||
} else {
|
||||
testOut->println(F("FAIL"));
|
||||
failCount++;
|
||||
}
|
||||
testCount++;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testVerify_P(char* result, PGM_P expect) {
|
||||
testOut->write('"');
|
||||
testOut->print(result);
|
||||
testOut->print("\",\"");
|
||||
testOut->print((const __FlashStringHelper*)expect);
|
||||
testOut->write('"');
|
||||
uint8_t n = strlen(result) + strlenPGM(expect) + 5;
|
||||
testResult(!strcmp_P(result, expect), n);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testVerify_P(bool b, PGM_P msg) {
|
||||
testOut->print((const __FlashStringHelper*)msg);
|
||||
uint8_t n = strlenPGM(msg);
|
||||
testResult(b, n);
|
||||
}
|
50
libraries/SdFat-1.1.0/extras/SdFatTestSuite/SdFatTestSuite.h
Normal file
50
libraries/SdFat-1.1.0/extras/SdFatTestSuite/SdFatTestSuite.h
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 20011-2017 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.
|
||||
*/
|
||||
#ifndef SdFatTestSuite_h
|
||||
#define SdFatTestSuite_h
|
||||
#include "SdFat.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
#if defined(__arm__) && !defined(strcmp_P)
|
||||
#define strcmp_P(a, b) strcmp((a), (b))
|
||||
#endif // strcmp_P
|
||||
|
||||
#if defined(__arm__) && !defined(strncpy_P)
|
||||
#define strncpy_P(s, t, n) strncpy(s, t, n)
|
||||
#endif // strncpy_P
|
||||
|
||||
#if defined(__arm__) && !defined(strlen_P)
|
||||
#define strlen_P(str) strlen(str)
|
||||
#endif // strlen_P
|
||||
|
||||
#define testVerifyBool(result) testVerify_P(result, PSTR(#result))
|
||||
#define testVerifyMsg(result, msg) testVerify_P(result, PSTR(msg))
|
||||
#define testVerifyStr(result, expect) testVerify_P(result, PSTR(expect))
|
||||
|
||||
void testBegin();
|
||||
void testEnd();
|
||||
void testVerify_P(bool b, PGM_P msg);
|
||||
void testVerify_P(char* result, PGM_P expect);
|
||||
#endif // SdFatTestSuite_h
|
@ -0,0 +1,105 @@
|
||||
// modified from ArduinoTestSuite 0022 by William Greiman
|
||||
// Tests writing to and reading from a file, in particular the
|
||||
// the Stream implementation (e.g. read() and peek()).
|
||||
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatTestSuite.h>
|
||||
SdFat SD;
|
||||
#define ATS_PrintTestStatus(msg, b) testVerify_P(b, PSTR(msg))
|
||||
void setup() {
|
||||
boolean b;
|
||||
SdFile f;
|
||||
uint32_t fs;
|
||||
|
||||
testBegin();
|
||||
|
||||
ATS_PrintTestStatus("SD.begin()", b = SD.begin());
|
||||
if (!b) goto done;
|
||||
|
||||
SD.remove("test.txt");
|
||||
|
||||
f.open("test.txt", FILE_WRITE);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
|
||||
f.print("abc");
|
||||
f.print("de");
|
||||
f.close();
|
||||
|
||||
f.open("test.txt", FILE_WRITE);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
|
||||
f.print("fgh");
|
||||
f.close();
|
||||
|
||||
f.open("test.txt", O_READ);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
fs =f.fileSize();
|
||||
ATS_PrintTestStatus("read()", f.read() == 'a');
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'b');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'b');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'c');
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'd');
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'd');
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'd');
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'd');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'd');
|
||||
ATS_PrintTestStatus("available()", f.curPosition() != fs);
|
||||
ATS_PrintTestStatus("read()", f.read() == 'e');
|
||||
ATS_PrintTestStatus("available()", f.curPosition() != fs);
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'f');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'f');
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'g');
|
||||
ATS_PrintTestStatus("available()", f.curPosition() != fs);
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'g');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'g');
|
||||
ATS_PrintTestStatus("available()", f.curPosition() != fs);
|
||||
ATS_PrintTestStatus("available()", f.curPosition() != fs);
|
||||
ATS_PrintTestStatus("available()", f.curPosition() != fs);
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'h');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'h');
|
||||
ATS_PrintTestStatus("available()", f.curPosition() == fs);
|
||||
ATS_PrintTestStatus("peek()", f.peek() == -1);
|
||||
ATS_PrintTestStatus("read()", f.read() == -1);
|
||||
ATS_PrintTestStatus("peek()", f.peek() == -1);
|
||||
ATS_PrintTestStatus("read()", f.read() == -1);
|
||||
|
||||
f.close();
|
||||
|
||||
SD.remove("test2.txt");
|
||||
|
||||
f.open("test2.txt", FILE_WRITE);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
|
||||
f.print("ABC");
|
||||
f.close();
|
||||
|
||||
f.open("test.txt", O_READ);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'a');
|
||||
|
||||
f.close();
|
||||
|
||||
f.open("test2.txt", O_READ);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'A');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'A');
|
||||
|
||||
f.close();
|
||||
|
||||
done:
|
||||
testEnd();
|
||||
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
|
||||
|
@ -0,0 +1,75 @@
|
||||
// modified from ArduinoTestSuite 0022 by William Greiman
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatTestSuite.h>
|
||||
SdFat SD;
|
||||
#define ATS_PrintTestStatus(msg, b) testVerify_P(b, PSTR(msg))
|
||||
|
||||
void setup() {
|
||||
boolean b;
|
||||
SdFile f;
|
||||
|
||||
testBegin();
|
||||
|
||||
ATS_PrintTestStatus("SD.begin()", b = SD.begin());
|
||||
if (!b) goto done;
|
||||
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf.txt"));
|
||||
ATS_PrintTestStatus("SD.open()", f.open("asdf.txt", FILE_WRITE)); f.close();
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf.txt"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("/asdf.txt"));
|
||||
ATS_PrintTestStatus("SD.remove()", SD.remove("asdf.txt"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf.txt"));
|
||||
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
|
||||
ATS_PrintTestStatus("SD.mkdir()", SD.mkdir("asdf"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("/asdf"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf/"));
|
||||
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("asdf"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
|
||||
|
||||
ATS_PrintTestStatus("SD.mkdir()", SD.mkdir("x/y/z"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x/"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y/"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y/z"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y/z/"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("/x/y/z/"));
|
||||
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("x/y/z"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x/y"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y/z"));
|
||||
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("x/y/"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("x"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y/z"));
|
||||
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("/x"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("x/y/z"));
|
||||
|
||||
ATS_PrintTestStatus("!SD.open()", !(f.open("asdf/asdf.txt", FILE_WRITE))); f.close();
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf.txt"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf/asdf.txt"));
|
||||
ATS_PrintTestStatus("SD.mkdir()", SD.mkdir("asdf"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
|
||||
ATS_PrintTestStatus("SD.open()", f.open("asdf/asdf.txt", FILE_WRITE)); f.close();
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf/asdf.txt"));
|
||||
ATS_PrintTestStatus("!SD.rmdir()", !SD.rmdir("asdf"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf/asdf.txt"));
|
||||
ATS_PrintTestStatus("SD.remove()", SD.remove("asdf/asdf.txt"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf/asdf.txt"));
|
||||
ATS_PrintTestStatus("SD.exists()", SD.exists("asdf"));
|
||||
ATS_PrintTestStatus("SD.rmdir()", SD.rmdir("asdf"));
|
||||
ATS_PrintTestStatus("!SD.exists()", !SD.exists("asdf"));
|
||||
|
||||
done:
|
||||
|
||||
testEnd();
|
||||
|
||||
}
|
||||
void loop() {}
|
@ -0,0 +1,108 @@
|
||||
// modified from ArduinoTestSuite 0022 by William Greiman
|
||||
// Tests writing to and reading from a file, in particular the
|
||||
// the Stream implementation (e.g. read() and peek()).
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatTestSuite.h>
|
||||
SdFat SD;
|
||||
#define ATS_PrintTestStatus(msg, b) testVerify_P(b, PSTR(msg))
|
||||
|
||||
void setup() {
|
||||
boolean b;
|
||||
SdFile f;
|
||||
|
||||
testBegin();
|
||||
|
||||
ATS_PrintTestStatus("SD.begin()", b = SD.begin());
|
||||
if (!b) goto done;
|
||||
|
||||
SD.remove("test.txt");
|
||||
|
||||
f.open("test.txt", FILE_WRITE);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
|
||||
ATS_PrintTestStatus("initial position", f.curPosition() == 0);
|
||||
ATS_PrintTestStatus("initial size", f.fileSize() == 0);
|
||||
|
||||
f.print("0123456789");
|
||||
|
||||
ATS_PrintTestStatus("position after writing", f.curPosition() == 10);
|
||||
ATS_PrintTestStatus("size after writing", f.fileSize() == 10);
|
||||
|
||||
f.seekSet(0);
|
||||
|
||||
ATS_PrintTestStatus("size after seek", f.fileSize() == 10);
|
||||
ATS_PrintTestStatus("position after seek", f.curPosition() == 0);
|
||||
|
||||
f.seekSet(7);
|
||||
|
||||
ATS_PrintTestStatus("position after seek", f.curPosition() == 7);
|
||||
ATS_PrintTestStatus("reading after seek", f.read() == '7');
|
||||
ATS_PrintTestStatus("position after reading after seeking", f.curPosition() == 8);
|
||||
ATS_PrintTestStatus("reading after reading after seeking", f.read() == '8');
|
||||
|
||||
f.seekSet(3);
|
||||
|
||||
ATS_PrintTestStatus("position after seeking", f.curPosition() == 3);
|
||||
ATS_PrintTestStatus("peeking after seeking", f.peek() == '3');
|
||||
ATS_PrintTestStatus("position after peeking after seeking", f.curPosition() == 3);
|
||||
ATS_PrintTestStatus("peeking after peeking after seeking", f.peek() == '3');
|
||||
ATS_PrintTestStatus("position after peeking after seeking", f.curPosition() == 3);
|
||||
ATS_PrintTestStatus("peeking after peeking after seeking", f.read() == '3');
|
||||
ATS_PrintTestStatus("position after peeking after seeking", f.curPosition() == 4);
|
||||
|
||||
f.seekSet(1);
|
||||
|
||||
ATS_PrintTestStatus("position after seeking", f.curPosition() == 1);
|
||||
ATS_PrintTestStatus("peeking after seeking", f.peek() == '1');
|
||||
|
||||
f.seekSet(4);
|
||||
|
||||
ATS_PrintTestStatus("position after seeking", f.curPosition() == 4);
|
||||
ATS_PrintTestStatus("peeking after seeking", f.peek() == '4');
|
||||
|
||||
f.seekSet(7);
|
||||
|
||||
ATS_PrintTestStatus("position()", f.curPosition() == 7);
|
||||
ATS_PrintTestStatus("read()", f.read() == '7');
|
||||
|
||||
f.seekSet(0);
|
||||
f.peek();
|
||||
f.print("AB");
|
||||
|
||||
ATS_PrintTestStatus("position()", f.curPosition() == 2);
|
||||
ATS_PrintTestStatus("size()", f.fileSize() == 10);
|
||||
ATS_PrintTestStatus("read()", f.read() == '2');
|
||||
|
||||
f.seekSet(0);
|
||||
|
||||
ATS_PrintTestStatus("read()", f.read() == 'A');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'B');
|
||||
ATS_PrintTestStatus("read()", f.read() == '2');
|
||||
|
||||
f.close();
|
||||
|
||||
f.open("test.txt", O_READ);
|
||||
ATS_PrintTestStatus("SD.open()", f.isOpen());
|
||||
if (!f.isOpen()) goto done;
|
||||
|
||||
ATS_PrintTestStatus("position()", f.curPosition() == 0);
|
||||
ATS_PrintTestStatus("size()", f.fileSize() == 10);
|
||||
ATS_PrintTestStatus("peek()", f.peek() == 'A');
|
||||
ATS_PrintTestStatus("read()", f.read() == 'A');
|
||||
|
||||
f.seekSet(4);
|
||||
|
||||
ATS_PrintTestStatus("position()", f.curPosition() == 4);
|
||||
ATS_PrintTestStatus("size()", f.fileSize() == 10);
|
||||
ATS_PrintTestStatus("peek()", f.peek() == '4');
|
||||
ATS_PrintTestStatus("read()", f.read() == '4');
|
||||
|
||||
f.close();
|
||||
|
||||
done:
|
||||
testEnd();
|
||||
}
|
||||
|
||||
void loop() {}
|
@ -0,0 +1,76 @@
|
||||
// This stress test will create and write files until the SD is full.
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
// SD chip select pin.
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
|
||||
// Set write buffer size.
|
||||
#ifdef __arm__
|
||||
#ifndef CORE_TEENSY
|
||||
// Due
|
||||
const size_t BUF_SIZE = 32768;
|
||||
#else // CORE_TEENSY
|
||||
// Teensy 3.0
|
||||
const size_t BUF_SIZE = 8192;
|
||||
#endif // CORE_TEENSY
|
||||
#elif defined(RAMEND) && RAMEND > 5000
|
||||
// AVR with more than 4 KB RAM
|
||||
const size_t BUF_SIZE = 4096;
|
||||
#else // __arm__
|
||||
// other
|
||||
const size_t BUF_SIZE = 512;
|
||||
#endif // __arm__
|
||||
|
||||
const size_t FILE_SIZE_KB = 10240;
|
||||
const uint16_t BUFS_PER_FILE = (1024L*FILE_SIZE_KB/BUF_SIZE);
|
||||
|
||||
SdFat sd;
|
||||
|
||||
SdFile file;
|
||||
|
||||
uint8_t buf[BUF_SIZE];
|
||||
char name[13];
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.print("BUF_SIZE ");
|
||||
Serial.println(BUF_SIZE);
|
||||
Serial.println("Type any character to start");
|
||||
while (Serial.read() < 0) {}
|
||||
|
||||
if (!sd.begin(SD_CS_PIN))sd.errorHalt("sd.begin");
|
||||
|
||||
// Fill buf with known value.
|
||||
for (size_t i = 0; i < BUF_SIZE; i++) buf[i] = i;
|
||||
|
||||
// Wait to begin.
|
||||
do {delay(10);} while (Serial.read() >= 0);
|
||||
Serial.println("Type any character to stop after next file");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {
|
||||
// Free KB on SD.
|
||||
uint32_t freeKB = sd.vol()->freeClusterCount()*sd.vol()->blocksPerCluster()/2;
|
||||
|
||||
Serial.print("Free KB: ");
|
||||
Serial.println(freeKB);
|
||||
if (freeKB < 2*FILE_SIZE_KB) {
|
||||
Serial.println(" Done!");
|
||||
while(1);
|
||||
}
|
||||
sprintf(name, "%lu.DAT", freeKB);
|
||||
if (!file.open(name, O_WRITE | O_CREAT | O_TRUNC)) {
|
||||
sd.errorHalt("Open error!");
|
||||
}
|
||||
for (uint16_t i = 0; i < BUFS_PER_FILE; i++) {
|
||||
if (file.write(buf, BUF_SIZE) != BUF_SIZE) {
|
||||
sd.errorHalt("Write error!");
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
if (Serial.available()) {
|
||||
Serial.println("Stopped!");
|
||||
while(1);
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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 <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatUtil.h>
|
||||
|
||||
const uint8_t SD_CHIP_SELECT = SS;
|
||||
|
||||
SdFat sd;
|
||||
|
||||
// store error strings in flash to save RAM
|
||||
#define error(s) sd.errorHalt(F(s))
|
||||
|
||||
/*
|
||||
* create enough files to force a cluster to be allocated to dir.
|
||||
*/
|
||||
void dirAllocTest(FatFile* dir) {
|
||||
char buf[32], name[32];
|
||||
SdFile 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_WRITE | O_CREAT | O_EXCL)) {
|
||||
error("open for write failed");
|
||||
}
|
||||
|
||||
// open end time and write start time
|
||||
uint32_t t1 = millis();
|
||||
// write file name to file
|
||||
file.print(name);
|
||||
if (!file.close()) error("close write");
|
||||
|
||||
// write end time
|
||||
uint32_t t2 = millis();
|
||||
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_READ)) {
|
||||
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() {
|
||||
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 SPI_HALF_SPEED if bus errors occur.
|
||||
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt();
|
||||
|
||||
uint32_t m = millis();
|
||||
// write files to root if FAT32
|
||||
if (sd.vol()->fatType() == 32) {
|
||||
Serial.println(F("Writing files to root"));
|
||||
dirAllocTest(sd.vwd());
|
||||
}
|
||||
|
||||
// create sub1 and write files
|
||||
SdFile sub1;
|
||||
if (!sub1.mkdir(sd.vwd(), "SUB1")) error("makdeDir SUB1 failed");
|
||||
Serial.println(F("Writing files to SUB1"));
|
||||
dirAllocTest(&sub1);
|
||||
|
||||
// create sub2 and write files
|
||||
SdFile 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() { }
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatUtil.h>
|
||||
|
||||
const uint8_t SD_CHIP_SELECT = SS;
|
||||
|
||||
SdFat sd;
|
||||
|
||||
// store error strings in flash to save RAM
|
||||
#define error(s) sd.errorHalt(F(s))
|
||||
|
||||
/*
|
||||
* remove all files in dir.
|
||||
*/
|
||||
void deleteFiles(FatFile* dir) {
|
||||
char name[32];
|
||||
SdFile file;
|
||||
|
||||
// open and delete files
|
||||
for (uint16_t n = 0; ; n++){
|
||||
sprintf(name, "%u.TXT", n);
|
||||
|
||||
// open start time
|
||||
uint32_t t0 = millis();
|
||||
|
||||
// assume done if open fails
|
||||
if (!file.open(dir, name, O_WRITE)) return;
|
||||
|
||||
// open end time and remove start time
|
||||
uint32_t t1 = millis();
|
||||
if (!file.remove()) error("file.remove failed");
|
||||
|
||||
// remove end time
|
||||
uint32_t t2 = millis();
|
||||
|
||||
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() {
|
||||
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 SPI_HALF_SPEED if bus errors occur.
|
||||
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt();
|
||||
|
||||
|
||||
// delete files in root if FAT32
|
||||
if (sd.vol()->fatType() == 32) {
|
||||
Serial.println(F("Remove files in root"));
|
||||
deleteFiles(sd.vwd());
|
||||
}
|
||||
|
||||
// open SUB1 and delete files
|
||||
SdFile sub1;
|
||||
if (!sub1.open("SUB1", O_READ)) error("open SUB1 failed");
|
||||
Serial.println(F("Remove files in SUB1"));
|
||||
deleteFiles(&sub1);
|
||||
|
||||
// open SUB2 and delete files
|
||||
SdFile sub2;
|
||||
if (!sub2.open(&sub1, "SUB2", O_READ)) error("open SUB2 failed");
|
||||
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() { }
|
@ -0,0 +1,94 @@
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatTestSuite.h>
|
||||
SdFat sd;
|
||||
const char *testName = "SDFAT.TST";
|
||||
//------------------------------------------------------------------------------
|
||||
void fstreamOpen() {
|
||||
ios::openmode nocreate[] = {ios::in, ios::in | ios::out};
|
||||
ios::openmode create[] =
|
||||
{ios::out, ios::out | ios::app, ios::app, ios::out | ios::trunc,
|
||||
ios::in | ios::out | ios::trunc, ios::in | ios::out | ios::app,
|
||||
ios::in | ios::app};
|
||||
ios::openmode illegal[] =
|
||||
{0, ios::trunc, ios::app | ios::trunc, ios::in | ios::app | ios::trunc,
|
||||
ios::in | ios::trunc, ios::out | ios::app | ios::trunc,
|
||||
ios::in | ios::out | ios::app | ios::trunc};
|
||||
|
||||
sd.remove(testName);
|
||||
fstream file(testName);
|
||||
testVerifyMsg(!file.is_open()&& !sd.exists(testName), "fstream constructor");
|
||||
|
||||
for (uint8_t i = 0 ; i < sizeof(nocreate)/sizeof(nocreate[1]); i++) {
|
||||
file.close();
|
||||
sd.remove(testName);
|
||||
file.open(testName, nocreate[i]);
|
||||
testVerifyMsg(!sd.exists(testName) && !file.is_open(), "fstream nocreate !exists");
|
||||
}
|
||||
for (uint8_t i = 0 ; i < sizeof(create)/sizeof(create[1]); i++) {
|
||||
file.close();
|
||||
sd.remove(testName);
|
||||
file.open(testName, create[i]);
|
||||
testVerifyMsg(sd.exists(testName) && file.is_open(), "fstream create openmode");
|
||||
}
|
||||
for (uint8_t i = 0 ; i < sizeof(illegal)/sizeof(illegal[1]); i++) {
|
||||
file.close();
|
||||
file.open(testName, illegal[i]);
|
||||
testVerifyMsg(sd.exists(testName) && !file.is_open(), "fstream illegal openmode");
|
||||
}
|
||||
for (uint8_t i = 0 ; i < sizeof(nocreate)/sizeof(nocreate[1]); i++) {
|
||||
file.close();
|
||||
file.open(testName, nocreate[i]);
|
||||
testVerifyMsg(sd.exists(testName) && file.is_open(), "fstream nocreate exists");
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void testPosition() {
|
||||
sd.remove(testName);
|
||||
ofstream ofs(testName);
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 0);
|
||||
ofs.seekp(0, ios::end);
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 0);
|
||||
ofs << "abcde";
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 5);
|
||||
ofs.seekp(4);
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 4);
|
||||
ofs.seekp(-1, ios::cur);
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 3);
|
||||
ofs.close();
|
||||
ifstream ifs(testName, ios::ate);
|
||||
testVerifyBool(ifs.good() && ifs.tellg() == 5);
|
||||
ifs.seekg(0);
|
||||
testVerifyBool(ifs.get() == 'a' && ifs.get() == 'b');
|
||||
testVerifyBool(ifs.tellg() == 2 && ifs.good());
|
||||
ifs.seekg(3, ios::cur);
|
||||
testVerifyBool(ifs.tellg() == 5 && ifs.good());
|
||||
ifs.seekg(4, ios::beg);
|
||||
testVerifyBool(ifs.good() && ifs.tellg() == 4);
|
||||
ifs.close();
|
||||
ofs.open(testName, ios::app);
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 0);
|
||||
ofs << 'f';
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 6);
|
||||
ofs.close();
|
||||
ofs.open(testName, ios::trunc);
|
||||
ofs.seekp(0, ios::end);
|
||||
testVerifyBool(ofs.good() && ofs.tellp() == 0);
|
||||
ofs << "ABCDEF";
|
||||
ofs.close();
|
||||
fstream fs(testName);
|
||||
testVerifyBool(fs.good() && fs.tellp() == 0 && fs.tellg() == 0);
|
||||
fs.seekg(2);
|
||||
testVerifyBool(fs.good() && fs.get() == 'C');
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
|
||||
testBegin();
|
||||
if (!sd.begin()) sd.initErrorHalt();
|
||||
fstreamOpen();
|
||||
testPosition();
|
||||
testEnd();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
@ -0,0 +1,261 @@
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatTestSuite.h>
|
||||
|
||||
char buf[100];
|
||||
ibufstream ib;
|
||||
#define ibInit(s) ibInit_P(PSTR(s))
|
||||
|
||||
//----------------------------------------------------------
|
||||
void ibInit_P(PGM_P p) {
|
||||
if (strlen_P(p) >= sizeof(buf)) {
|
||||
ib.init("");
|
||||
ib.setstate(ios::badbit);
|
||||
} else {
|
||||
ib.clear();
|
||||
strncpy_P(buf, p, sizeof(buf));
|
||||
ib.init(buf);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void istreamBool() {
|
||||
bool b;
|
||||
ibInit(" 0 1 2");
|
||||
testVerifyBool((ib >> b) && !b);
|
||||
testVerifyBool((ib >> b) && b);
|
||||
testVerifyBool(!(ib >> b) && !ib.good());
|
||||
|
||||
ibInit(" true false err");
|
||||
testVerifyBool((ib >> boolalpha >> b) && b && ib.good());
|
||||
testVerifyBool((ib >> b) && !b && ib.good());
|
||||
testVerifyBool(!(ib >> b) && ib.fail());
|
||||
|
||||
ibInit("1");
|
||||
testVerifyBool((ib >> noboolalpha >> b) && b && ib.eof());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void istreamChar() {
|
||||
char c;
|
||||
signed char sc;
|
||||
unsigned char uc;
|
||||
|
||||
ibInit("c s u g");
|
||||
testVerifyBool((ib >> c) && ib.good() && c == 'c');
|
||||
testVerifyBool((ib >> sc) && ib.good() && sc == 's');
|
||||
testVerifyBool((ib >> uc) && ib.good() && uc == 'u');
|
||||
testVerifyBool(ib.get() == ' ');
|
||||
testVerifyBool(ib.peek() == 'g' && ib.good());
|
||||
testVerifyBool(ib.get() == 'g' && ib.good());
|
||||
testVerifyBool(ib.get() == -1 && ib.eof());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void istreamDouble() {
|
||||
double f;
|
||||
ibInit("0 .1 1. 2 3.4 .1e5 1e5 -1E6 +2.3e-3 -123.4567");
|
||||
testVerifyBool((ib >> f) && f == 0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 0.1 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 1.0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 2.0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 3.4 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 10000.0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 1e5 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == -1E6 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 2.3e-3 && ib.good());
|
||||
testVerifyBool((ib >> f) && fabs(f + 123.4567) < 1e-5 && ib.eof());
|
||||
if (fabs(f + 123.4567) >= 1e-5) Serial.println(f, 8);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void istreamFloat() {
|
||||
float f;
|
||||
ibInit("0 .1 1. 2 3.4 .1e5 1e5 -1E6 +2.3e-3 -123.4567");
|
||||
testVerifyBool((ib >> f) && f == 0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 0.1f && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 1.0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 2.0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 3.4f && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 10000.0 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 1e5 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == -1E6 && ib.good());
|
||||
testVerifyBool((ib >> f) && f == 2.3e-3f && ib.good());
|
||||
testVerifyBool((ib >> f) && fabs(f + 123.4567f) < 1e-5 && ib.eof());
|
||||
if (fabs(f + 123.4567) >= 1e-5) Serial.println(f, 8);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void istreamGet() {
|
||||
char s[4];
|
||||
ibInit("ab c");
|
||||
testVerifyBool(ib.get() == 'a' && ib.good() && ib.gcount() == 1);
|
||||
testVerifyBool(ib.get() == 'b' && ib.good() && ib.gcount() == 1);
|
||||
testVerifyBool(ib.get() == ' ' && ib.good() && ib.gcount() == 1);
|
||||
testVerifyBool(ib.get() == 'c' && ib.good() && ib.gcount() == 1);
|
||||
testVerifyBool(ib.get() == -1 && ib.eof() && ib.gcount() == 0);
|
||||
|
||||
ibInit("ab\ncdef");
|
||||
ib.get(s, sizeof(s));
|
||||
testVerifyBool(ib.good() && ib.gcount() == 2);
|
||||
testVerifyStr(s, "ab");
|
||||
testVerifyBool(ib.get() == '\n' && ib.good() && ib.gcount() == 1);
|
||||
ib.get(s, sizeof(s));
|
||||
testVerifyBool(ib.good() && ib.gcount() == 3);
|
||||
testVerifyStr(s, "cde");
|
||||
ib.get(s, sizeof(s));
|
||||
testVerifyBool(ib.eof() && ib.gcount() == 1);
|
||||
testVerifyStr(s, "f");
|
||||
|
||||
ibInit(
|
||||
"short line\n"
|
||||
"\n"
|
||||
"17 character line\n"
|
||||
"too long for buffer\n"
|
||||
"line with no nl"
|
||||
);
|
||||
char buf[18];
|
||||
ib.getline(buf, sizeof(buf));
|
||||
testVerifyBool(ib.good() && ib.gcount() == 11);
|
||||
testVerifyStr(buf, "short line");
|
||||
ib.getline(buf, sizeof(buf));
|
||||
testVerifyBool(ib.good() && ib.gcount() == 1 && buf[0] == '\0');
|
||||
ib.getline(buf, sizeof(buf));
|
||||
testVerifyBool(ib.good() && ib.gcount() == 18);
|
||||
testVerifyStr(buf, "17 character line");
|
||||
ib.getline(buf, sizeof(buf));
|
||||
testVerifyBool(ib.fail() && !ib.eof() && ib.gcount() == 17);
|
||||
testVerifyStr(buf, "too long for buff");
|
||||
ib.clear();
|
||||
ib.getline(buf, sizeof(buf));
|
||||
testVerifyBool(ib.good() && !ib.eof() && ib.gcount() == 3);
|
||||
testVerifyStr(buf, "er");
|
||||
ib.getline(buf, sizeof(buf));
|
||||
testVerifyBool(!ib.fail() && ib.eof() && ib.gcount() == 15);
|
||||
testVerifyStr(buf, "line with no nl");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void istreamNumber() {
|
||||
short s;
|
||||
signed short ss;
|
||||
unsigned short us;
|
||||
int i;
|
||||
signed int si;
|
||||
unsigned int ui;
|
||||
long l;
|
||||
signed long sl;
|
||||
unsigned long ul;
|
||||
|
||||
ibInit("-32769");
|
||||
testVerifyBool(!(ib >> s) && ib.fail());
|
||||
ibInit("-32768 0 32767 32768");
|
||||
testVerifyBool((ib >> s) && s == -32768 && ib.good());
|
||||
testVerifyBool((ib >> s) && s == 0 && ib.good());
|
||||
testVerifyBool((ib >> s) && s == 32767 && ib.good());
|
||||
testVerifyBool(!(ib >> s) && ib.fail());
|
||||
|
||||
ibInit("-32769");
|
||||
testVerifyBool(!(ib >> ss) && ib.fail());
|
||||
ibInit("-32768 0 32767 32768");
|
||||
testVerifyBool((ib >> ss) && ss == -32768 && ib.good());
|
||||
testVerifyBool((ib >> ss) && ss == 0 && ib.good());
|
||||
testVerifyBool((ib >> ss) && ss == 32767 && ib.good());
|
||||
testVerifyBool(!(ib >> ss) && ib.fail());
|
||||
|
||||
ibInit("0 65535 65536");
|
||||
testVerifyBool((ib >> us) && us == 0 && ib.good());
|
||||
testVerifyBool((ib >> us) && us == 65535 && ib.good());
|
||||
testVerifyBool(!(ib >> us) && ib.fail());
|
||||
|
||||
if (sizeof(int) == 2) {
|
||||
ibInit("-32769");
|
||||
testVerifyBool(!(ib >> i) && ib.fail());
|
||||
ibInit("-32768 0 32767 32768");
|
||||
testVerifyBool((ib >> i) && i == -32768 && ib.good());
|
||||
testVerifyBool((ib >> i) && i == 0 && ib.good());
|
||||
testVerifyBool((ib >> i) && i == 32767 && ib.good());
|
||||
testVerifyBool(!(ib >> i) && ib.fail());
|
||||
|
||||
ibInit("-32769");
|
||||
testVerifyBool(!(ib >> si) && ib.fail());
|
||||
ibInit("-32768 0 32767 32768");
|
||||
testVerifyBool((ib >> si) && si == -32768 && ib.good());
|
||||
testVerifyBool((ib >> si) && si == 0 && ib.good());
|
||||
testVerifyBool((ib >> si) && si == 32767 && ib.good());
|
||||
testVerifyBool(!(ib >> si) && ib.fail());
|
||||
|
||||
ibInit("0 65535 65536");
|
||||
testVerifyBool((ib >> ui) && ui == 0 && ib.good());
|
||||
testVerifyBool((ib >> ui) && ui == 65535 && ib.good());
|
||||
testVerifyBool(!(ib >> ui) && ib.fail());
|
||||
} else {
|
||||
ibInit("-2147483649");
|
||||
testVerifyBool(!(ib >> i) && ib.fail());
|
||||
ibInit("-2147483648 0 2147483647 2147483648");
|
||||
testVerifyBool((ib >> i) && i == -2147483648 && ib.good());
|
||||
testVerifyBool((ib >> i) && i == 0 && ib.good());
|
||||
testVerifyBool((ib >> i) && i == 2147483647 && ib.good());
|
||||
testVerifyBool(!(ib >> i) && ib.fail());
|
||||
|
||||
ibInit("-2147483649");
|
||||
testVerifyBool(!(ib >> si) && ib.fail());
|
||||
ibInit("-2147483648 0 2147483647 2147483648");
|
||||
testVerifyBool((ib >> si) && si == -2147483648 && ib.good());
|
||||
testVerifyBool((ib >> si) && si == 0 && ib.good());
|
||||
testVerifyBool((ib >> si) && si == 2147483647 && ib.good());
|
||||
testVerifyBool(!(ib >> si) && ib.fail());
|
||||
|
||||
ibInit("0 4294967295 4294967296");
|
||||
testVerifyBool((ib >> ui) && ui == 0 && ib.good());
|
||||
testVerifyBool((ib >> ui) && ui == 4294967295 && ib.good());
|
||||
testVerifyBool(!(ib >> ui) && ib.fail());
|
||||
}
|
||||
ibInit("-2147483649");
|
||||
testVerifyBool(!(ib >> l) && ib.fail());
|
||||
ibInit("-2147483648 0 2147483647 2147483648");
|
||||
testVerifyBool((ib >> l) && l == -2147483648 && ib.good());
|
||||
testVerifyBool((ib >> l) && l == 0 && ib.good());
|
||||
testVerifyBool((ib >> l) && l == 2147483647 && ib.good());
|
||||
testVerifyBool(!(ib >> l) && ib.fail());
|
||||
|
||||
ibInit("-2147483649");
|
||||
testVerifyBool(!(ib >> sl) && ib.fail());
|
||||
ibInit("-2147483648 0 2147483647 2147483648");
|
||||
testVerifyBool((ib >> sl) && sl == -2147483648 && ib.good());
|
||||
testVerifyBool((ib >> sl) && sl == 0 && ib.good());
|
||||
testVerifyBool((ib >> sl) && sl == 2147483647 && ib.good());
|
||||
testVerifyBool(!(ib >> sl) && ib.fail());
|
||||
|
||||
ibInit("0 4294967295 4294967296");
|
||||
testVerifyBool((ib >> ul) && ul == 0 && ib.good());
|
||||
testVerifyBool((ib >> ul) && ul == 4294967295 && ib.good());
|
||||
testVerifyBool(!(ib >> ul) && ib.fail());
|
||||
|
||||
// octal hex
|
||||
ibInit("123 abc 0xdef 0XABC 567");
|
||||
testVerifyBool((ib >> oct >> i) && i == 83);
|
||||
testVerifyBool((ib >> hex >> i) && i == 0xabc);
|
||||
testVerifyBool((ib >> i) && i == 0xdef);
|
||||
testVerifyBool((ib >> i) && i == 0xabc);
|
||||
testVerifyBool((ib >> dec >> i) && i ==567);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void istreamStr() {
|
||||
char str[20];
|
||||
ibInit("abc def\r\n hij");
|
||||
testVerifyBool((ib >> str) && ib.good());
|
||||
testVerifyStr(str, "abc");
|
||||
testVerifyBool((ib >> str) && ib.good());
|
||||
testVerifyStr(str, "def");
|
||||
testVerifyBool((ib >> str) && ib.eof());
|
||||
testVerifyStr(str, "hij");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
testBegin();
|
||||
istreamBool();
|
||||
istreamChar();
|
||||
istreamDouble();
|
||||
istreamFloat();
|
||||
istreamGet();
|
||||
istreamNumber();
|
||||
istreamStr();
|
||||
testEnd();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
@ -0,0 +1,36 @@
|
||||
// Program to compare size of SdFat with the SD.h library.
|
||||
#include <SPI.h>
|
||||
// Select the test library by commenting out one of the following two lines.
|
||||
// #include <SD.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
// SD chip select pin.
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
|
||||
#ifdef __SD_H__
|
||||
File file;
|
||||
#else // __SD_H__
|
||||
SdFat SD;
|
||||
SdFile file;
|
||||
#endif // __SD_H__
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) {} // wait for Leonardo
|
||||
|
||||
if (!SD.begin(SD_CS_PIN)) {
|
||||
Serial.println("begin failed");
|
||||
return;
|
||||
}
|
||||
#ifdef __SD_H__
|
||||
file = SD.open("SFN_file.txt", FILE_WRITE);
|
||||
#else // __SD_H__
|
||||
file.open("LFN_file.txt", O_RDWR | O_CREAT);
|
||||
#endif // __SD_H__
|
||||
|
||||
file.println("Hello");
|
||||
file.close();
|
||||
Serial.println("Done");
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
@ -0,0 +1,234 @@
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatUtil.h>
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
SdFat sd;
|
||||
SdFile file;
|
||||
char name[260];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* testName[] = {
|
||||
"low.low",
|
||||
"low.Mix",
|
||||
"low.UP",
|
||||
"Mix.low",
|
||||
"Mix.Mix",
|
||||
"Mix.UP",
|
||||
"UP.low",
|
||||
"UP.Mix",
|
||||
"UP.UP",
|
||||
".dot",
|
||||
".dot.dot",
|
||||
"A b c . txt",
|
||||
" Leading space and no extension",
|
||||
"Trailing dots and space . . .",
|
||||
"Long extension.extension",
|
||||
"Space after dot. txt",
|
||||
"Dot.dot.test.txt",
|
||||
"Dot.dot.test.seq.txt",
|
||||
"LOW.LOW",
|
||||
"MIX.MIX",
|
||||
"Invalid character *.test"
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
bool checkName(char first, size_t len) {
|
||||
size_t i;
|
||||
if (len < 5 || len > sizeof(name)) {
|
||||
return false;
|
||||
}
|
||||
if ( name[0] != first) {
|
||||
return false;
|
||||
}
|
||||
for (i = 1; i < (len - 4); i++) {
|
||||
if (name[i] != (char)('0' + (i + 1) %10)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const char* p = ".txt";
|
||||
while (*p) {
|
||||
if (name[i++] != *p++) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return name[i] == 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void makeName(char first, size_t len) {
|
||||
size_t i;
|
||||
if (len > sizeof(name)) {
|
||||
len = 255;
|
||||
}
|
||||
if (len < 5) {
|
||||
len = 5;
|
||||
}
|
||||
name[0] = first;
|
||||
for (i = 1; i < (len - 4); i++) {
|
||||
name[i] = '0' + (i + 1) %10;
|
||||
}
|
||||
const char* p = ".txt";
|
||||
while (*p) name[i++] = *p++;
|
||||
name[i] = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// test open, remove, getName, and ls.
|
||||
void basicTest() {
|
||||
size_t i;
|
||||
size_t n = sd.vol()->fatType() == 32 ? 255 : 99;
|
||||
uint16_t maxIndex = 0;
|
||||
|
||||
makeName('Z', 256);
|
||||
if (!file.open(name, O_RDWR | O_CREAT)) {
|
||||
Serial.println(F("255 limit OK"));
|
||||
} else {
|
||||
sd.errorHalt(F("255 limit"));
|
||||
}
|
||||
for (i = 5; i <= n; i++) {
|
||||
makeName('A', i);
|
||||
|
||||
if (!file.open(name, O_RDWR | O_CREAT)) {
|
||||
sd.errorHalt(F("open A"));
|
||||
}
|
||||
file.println(name);
|
||||
Serial.print(i);
|
||||
Serial.write(' ');
|
||||
Serial.print(file.dirIndex());
|
||||
Serial.write(' ');
|
||||
Serial.print(file.fileSize());
|
||||
Serial.println(F(" open A"));
|
||||
if (file.fileSize() != (i + 2)) {
|
||||
sd.errorHalt(F("file size A"));
|
||||
}
|
||||
if (file.dirIndex() >= maxIndex) {
|
||||
maxIndex = file.dirIndex();
|
||||
} else {
|
||||
Serial.print(maxIndex); Serial.print(',');Serial.println(file.dirIndex());
|
||||
sd.errorHalt(F("dirIndex"));
|
||||
}
|
||||
file.close();
|
||||
if (!file.open(sd.vwd(), maxIndex, O_READ)) {
|
||||
sd.errorHalt(F("open by index"));
|
||||
}
|
||||
memset(name, 0, sizeof(name));
|
||||
if (!file.getName(name, sizeof(name))) {
|
||||
sd.errorHalt(F("getName"));
|
||||
}
|
||||
if (!checkName('A', i)) {
|
||||
Serial.println(name);
|
||||
sd.errorHalt(F("checkName"));
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
for (i = n; i >= 5; i -= 2) {
|
||||
makeName('A', i);
|
||||
Serial.print(i);
|
||||
Serial.println(F( " rm A"));
|
||||
if (!sd.remove(name)) {
|
||||
sd.errorHalt(F("remove A"));
|
||||
}
|
||||
}
|
||||
for (i = n; i >= 5; i -= 2) {
|
||||
makeName('B', i);
|
||||
if (!file.open(name, O_RDWR | O_CREAT)) {
|
||||
sd.errorHalt(F("open B"));
|
||||
}
|
||||
file.println(name);
|
||||
Serial.print(i);
|
||||
Serial.write(' ');
|
||||
Serial.print(file.dirIndex());
|
||||
Serial.write(' ');
|
||||
Serial.print(file.fileSize());
|
||||
Serial.println(F(" open B"));
|
||||
if (file.fileSize() != (i + 2)) {
|
||||
sd.errorHalt(F("file size B"));
|
||||
}
|
||||
if (file.dirIndex() > maxIndex) {
|
||||
sd.errorHalt(F("maxIndex"));
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
Serial.println(F("----- ls ------"));
|
||||
sd.ls();
|
||||
for (i = 5; i <= n; i++) {
|
||||
char fc = i & 1 ? 'B' : 'A';
|
||||
makeName(fc, i);
|
||||
Serial.print(i);
|
||||
Serial.print(F(" rm "));
|
||||
Serial.println(fc);
|
||||
if (!sd.remove(name)) {
|
||||
sd.errorHalt(F("remove A/B"));
|
||||
}
|
||||
}
|
||||
if (file.openNext(sd.vwd())) {
|
||||
sd.errorHalt(F("remove all"));
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println(F("basicTest done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void nameTest() {
|
||||
Serial.println();
|
||||
uint8_t n = sizeof(testName)/sizeof(char*);
|
||||
for (uint8_t i = 0; i < n; i++) {
|
||||
Serial.print(F("Name: "));
|
||||
Serial.write('"');
|
||||
Serial.print(testName[i]);
|
||||
Serial.println('"');
|
||||
if(!file.open(testName[i], O_CREAT | O_RDWR)) {
|
||||
Serial.println(F("Open failed"));
|
||||
} else {
|
||||
file.println(testName[i]);
|
||||
if (!file.getName(name, sizeof(name))) {
|
||||
sd.errorHalt(F("getFilemame"));
|
||||
}
|
||||
file.println(name);
|
||||
Serial.print(F("LFN: "));
|
||||
Serial.write('"');
|
||||
Serial.print(name);
|
||||
Serial.println('"');
|
||||
Serial.print(F("SFN: "));
|
||||
Serial.write('"');
|
||||
file.printSFN(&Serial);
|
||||
Serial.println('"');
|
||||
Serial.print(F("Index: "));
|
||||
if (file.dirIndex() < 10) {
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println(file.dirIndex());
|
||||
file.close();
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
Serial.println(F("----- ls ------"));
|
||||
sd.ls();
|
||||
Serial.println();
|
||||
Serial.println(F("nameTest done"));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while(!Serial);
|
||||
Serial.print(F("\r\nFreeRam: "));
|
||||
Serial.println(FreeRam());
|
||||
Serial.println(F("Type any character to start."));
|
||||
while (Serial.read() < 0) {}
|
||||
if (!sd.begin(SD_CS_PIN)) sd.initErrorHalt();
|
||||
if (file.openNext(sd.vwd())) {
|
||||
file.close();
|
||||
delay(100);
|
||||
while (Serial.read() >= 0) {}
|
||||
Serial.print(F("Type 'W' to wipe the card: "));
|
||||
int c;
|
||||
while ((c = Serial.read()) < 0) {}
|
||||
if (c != 'W') {
|
||||
sd.errorHalt(F("Invalid"));
|
||||
}
|
||||
Serial.println((char)c);
|
||||
if (!sd.wipe(&Serial) || !sd.begin(SD_CS_PIN)) {
|
||||
sd.errorHalt(F("wipe failed"));
|
||||
}
|
||||
}
|
||||
basicTest();
|
||||
nameTest();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
@ -0,0 +1,218 @@
|
||||
#include <SPI.h>
|
||||
#include <SdFat.h>
|
||||
#include <SdFatUtil.h>
|
||||
const uint8_t SD_CS_PIN = SS;
|
||||
SdFat sd;
|
||||
SdFile file;
|
||||
char name[260];
|
||||
|
||||
// Serial output stream
|
||||
ArduinoOutStream cout(Serial);
|
||||
|
||||
// Serial in buffer.
|
||||
char cinBuf[10];
|
||||
|
||||
// Serial input stream
|
||||
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
|
||||
//------------------------------------------------------------------------------
|
||||
const char* testName[] = {
|
||||
"low.low",
|
||||
"low.Mix",
|
||||
"low.UP",
|
||||
"Mix.low",
|
||||
"Mix.Mix",
|
||||
"Mix.UP",
|
||||
"UP.low",
|
||||
"UP.Mix",
|
||||
"UP.UP",
|
||||
".dot",
|
||||
".dot.dot",
|
||||
"A b c . txt",
|
||||
" Leading space and no extension",
|
||||
"Trailing dots and space . . .",
|
||||
"Long extension.extension",
|
||||
"Space after dot. txt",
|
||||
"Dot.dot.test.txt",
|
||||
"Dot.dot.test.seq.txt",
|
||||
"LOW.LOW",
|
||||
"MIX.MIX",
|
||||
"Invalid character *.test"
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
bool checkName(char first, size_t len) {
|
||||
size_t i;
|
||||
if (len < 5 || len > sizeof(name)) {
|
||||
return false;
|
||||
}
|
||||
if ( name[0] != first) {
|
||||
return false;
|
||||
}
|
||||
for (i = 1; i < (len - 4); i++) {
|
||||
if (name[i] != (char)('0' + (i + 1) %10)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const char* p = ".txt";
|
||||
while (*p) {
|
||||
if (name[i++] != *p++) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return name[i] == 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void makeName(char first, size_t len) {
|
||||
size_t i;
|
||||
if (len > sizeof(name)) {
|
||||
len = 255;
|
||||
}
|
||||
if (len < 5) {
|
||||
len = 5;
|
||||
}
|
||||
name[0] = first;
|
||||
for (i = 1; i < (len - 4); i++) {
|
||||
name[i] = '0' + (i + 1) %10;
|
||||
}
|
||||
const char* p = ".txt";
|
||||
while (*p) name[i++] = *p++;
|
||||
name[i] = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// test open, remove, getName, and ls.
|
||||
void basicTest() {
|
||||
size_t i;
|
||||
size_t n = sd.vol()->fatType() == 32 ? 255 : 99;
|
||||
uint16_t maxIndex = 0;
|
||||
|
||||
makeName('Z', 256);
|
||||
if (!file.open(name, O_RDWR | O_CREAT)) {
|
||||
cout << F("255 limit OK") << endl;
|
||||
} else {
|
||||
sd.errorHalt(F("255 limit"));
|
||||
}
|
||||
for (i = 5; i <= n; i++) {
|
||||
makeName('A', i);
|
||||
|
||||
if (!file.open(name, O_RDWR | O_CREAT)) {
|
||||
sd.errorHalt(F("open A"));
|
||||
}
|
||||
file.println(name);
|
||||
cout << setw(3) << i << setw(5) << file.dirIndex() << F(" open A") << endl;
|
||||
|
||||
if (file.fileSize() != (i + 2)) {
|
||||
sd.errorHalt(F("file size A"));
|
||||
}
|
||||
if (file.dirIndex() >= maxIndex) {
|
||||
maxIndex = file.dirIndex();
|
||||
} else {
|
||||
sd.errorHalt(F("dirIndex"));
|
||||
}
|
||||
file.close();
|
||||
if (!file.open(sd.vwd(), maxIndex, O_READ)) {
|
||||
sd.errorHalt(F("open by index"));
|
||||
}
|
||||
memset(name, 0, sizeof(name));
|
||||
if (!file.getName(name, sizeof(name))) {
|
||||
sd.errorHalt(F("getName"));
|
||||
}
|
||||
if (!checkName('A', i)) {
|
||||
cout << name << endl;
|
||||
sd.errorHalt(F("checkName"));
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
for (i = n; i >= 5; i -= 2) {
|
||||
makeName('A', i);
|
||||
cout << setw(3) << i << F( " rm A") << endl;
|
||||
if (!sd.remove(name)) {
|
||||
sd.errorHalt(F("remove A"));
|
||||
}
|
||||
}
|
||||
for (i = n; i >= 5; i -= 2) {
|
||||
makeName('B', i);
|
||||
if (!file.open(name, O_RDWR | O_CREAT)) {
|
||||
sd.errorHalt(F("open B"));
|
||||
}
|
||||
file.println(name);
|
||||
|
||||
cout << setw(3) << i << setw(5) << file.dirIndex() << F(" open B") << endl;
|
||||
|
||||
if (file.fileSize() != (i + 2)) {
|
||||
sd.errorHalt(F("file size B"));
|
||||
}
|
||||
if (file.dirIndex() > maxIndex) {
|
||||
sd.errorHalt(F("maxIndex"));
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
cout << endl << F("----- ls ------") << endl;
|
||||
sd.ls();
|
||||
for (i = 5; i <= n; i++) {
|
||||
char fc = i & 1 ? 'B' : 'A';
|
||||
makeName(fc, i);
|
||||
cout << setw(3) << i << F(" rm ") << fc << endl;
|
||||
if (!sd.remove(name)) {
|
||||
sd.errorHalt(F("remove A/B"));
|
||||
}
|
||||
}
|
||||
if (file.openNext(sd.vwd())) {
|
||||
sd.errorHalt(F("remove all"));
|
||||
}
|
||||
cout << endl << F("basicTest done") << endl;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void nameTest() {
|
||||
cout << endl;
|
||||
uint8_t n = sizeof(testName)/sizeof(char*);
|
||||
for (uint8_t i = 0; i < n; i++) {
|
||||
cout << F("Name: \"") << testName[i] << '"' << endl;
|
||||
if(!file.open(testName[i], O_CREAT | O_RDWR)) {
|
||||
cout <<F("Open failed") << endl;
|
||||
} else {
|
||||
file.println(testName[i]);
|
||||
if (!file.getName(name, sizeof(name))) {
|
||||
sd.errorHalt(F("getFilemame"));
|
||||
}
|
||||
cout << F("LFN: \"") << name << '"' << endl;
|
||||
cout << F("SFN: \"");
|
||||
file.printSFN(&Serial);
|
||||
cout << '"' << endl;
|
||||
cout <<F("Index: ") << setw(2) << file.dirIndex() << endl;
|
||||
file.close();
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
cout << F("----- ls ------") << endl;
|
||||
sd.ls();
|
||||
cout << endl << F("nameTest done") << endl;
|
||||
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while(!Serial); // Wait for USB Serial.
|
||||
|
||||
cout << endl << F("FreeRam: ") << FreeRam() << endl;
|
||||
cout << F("Type any character to start.") << endl;
|
||||
cin.readline();
|
||||
if (!sd.begin(SD_CS_PIN)) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
if (file.openNext(sd.vwd())) {
|
||||
file.close();
|
||||
cout << F("Type 'W' to wipe the card: ");
|
||||
cin.readline();
|
||||
char c = cin.get();
|
||||
cout << c << endl;
|
||||
if (c != 'W') {
|
||||
sd.errorHalt(F("Invalid"));
|
||||
}
|
||||
if (!sd.wipe(&Serial) || !sd.begin(SD_CS_PIN)) {
|
||||
sd.errorHalt(F("wipe failed"));
|
||||
}
|
||||
}
|
||||
basicTest();
|
||||
nameTest();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user