mirror of
https://github.com/luc-github/ESP3D.git
synced 2025-10-31 11:56:48 -07:00
Fix rmdir does not remove not empty directory
Remove SDFat V1 support as not necessary now have SDFat 2.x Remove SPIFFS esp8266 support as now removed from core Bump version Remove outdated test scripts
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -10,7 +10,8 @@
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp"
|
||||
"initializer_list": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
},
|
||||
"cmake.configureOnOpen": false
|
||||
}
|
||||
@@ -245,7 +245,6 @@
|
||||
/* SD card library
|
||||
* ESP_SD_NATIVE //esp32 / esp8266
|
||||
* ESP_SDIO //esp32 only
|
||||
* ESP_SDFAT //esp8266 / esp32
|
||||
* ESP_SDFAT2 //esp8266 / esp32
|
||||
*/
|
||||
//#define SD_DEVICE ESP_SDFAT2
|
||||
|
||||
@@ -200,8 +200,7 @@
|
||||
//SD READER FS type supported
|
||||
#define ESP_SD_NATIVE 1
|
||||
#define ESP_SDIO 2
|
||||
#define ESP_SDFAT 3
|
||||
#define ESP_SDFAT2 4
|
||||
#define ESP_SDFAT2 3
|
||||
|
||||
//SDIO Mode
|
||||
#define SD_ONE_BIT_MODE 1
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define _VERSION_ESP3D_H
|
||||
|
||||
//version and sources location
|
||||
#define FW_VERSION "3.0.0.a208"
|
||||
#define FW_VERSION "3.0.0.a209"
|
||||
#define REPOSITORY "https://github.com/luc-github/ESP3D/tree/3.0"
|
||||
|
||||
#endif //_VERSION_ESP3D_H
|
||||
|
||||
@@ -143,35 +143,39 @@ bool ESP_FileSystem::mkdir(const char *path)
|
||||
bool ESP_FileSystem::rmdir(const char *path)
|
||||
{
|
||||
String p = path;
|
||||
if(p[0]!='/') {
|
||||
p="/"+p;
|
||||
if (!p.startsWith("/")) {
|
||||
p = '/'+p;
|
||||
}
|
||||
if (p[p.length()-1] == '/') {
|
||||
if (p!="/") {
|
||||
if (p!= "/") {
|
||||
if (p.endsWith("/")) {
|
||||
p.remove(p.length()-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists(p.c_str())) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
std::stack <String > pathlist;
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
while (pathlist.size() > 0 && res) {
|
||||
File dir = FFat.open(pathlist.top().c_str());
|
||||
File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
while (f && res) {
|
||||
if (f.isDirectory()) {
|
||||
candelete = false;
|
||||
String newdir = f.name();
|
||||
String newdir = pathlist.top()+ '/';
|
||||
newdir+= f.name();
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = File();
|
||||
} else {
|
||||
FFat.remove(f.name());
|
||||
String filepath = pathlist.top()+ '/';
|
||||
filepath+= f.name();
|
||||
f.close();
|
||||
if (!FFat.remove(filepath.c_str())) {
|
||||
res = false;
|
||||
}
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,35 +150,39 @@ bool ESP_FileSystem::mkdir(const char *path)
|
||||
bool ESP_FileSystem::rmdir(const char *path)
|
||||
{
|
||||
String p = path;
|
||||
if(p[0]!='/') {
|
||||
p="/"+p;
|
||||
if (!p.startsWith("/")) {
|
||||
p = '/'+p;
|
||||
}
|
||||
if (p[p.length()-1] == '/') {
|
||||
if (p!="/") {
|
||||
if (p!= "/") {
|
||||
if (p.endsWith("/")) {
|
||||
p.remove(p.length()-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists(p.c_str())) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
std::stack <String> pathlist;
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
while (pathlist.size() > 0 && res) {
|
||||
File dir = LittleFS.open(pathlist.top().c_str());
|
||||
File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
while (f && res) {
|
||||
if (f.isDirectory()) {
|
||||
candelete = false;
|
||||
String newdir = f.name();
|
||||
String newdir = pathlist.top()+ '/';
|
||||
newdir+= f.name();
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = File();
|
||||
} else {
|
||||
LittleFS.remove(f.name());
|
||||
String filepath = pathlist.top()+ '/';
|
||||
filepath+= f.name();
|
||||
f.close();
|
||||
if (!LittleFS.remove(filepath.c_str())) {
|
||||
res = false;
|
||||
}
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,46 +153,40 @@ bool ESP_FileSystem::mkdir(const char *path)
|
||||
|
||||
bool ESP_FileSystem::rmdir(const char *path)
|
||||
{
|
||||
if (!exists(path)) {
|
||||
String p = path;
|
||||
if (!p.endsWith("/")) {
|
||||
p+= '/';
|
||||
}
|
||||
if (!p.startsWith("/")) {
|
||||
p = '/'+p;
|
||||
}
|
||||
if (!exists(p.c_str())) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
std::stack <String> pathlist;
|
||||
String spath = path;
|
||||
spath.trim();
|
||||
if (spath[spath.length()-1] != '/') {
|
||||
spath+="/";
|
||||
}
|
||||
if (spath[0] != '/') {
|
||||
spath ="/" + spath;
|
||||
}
|
||||
pathlist.push(spath);
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
spath=pathlist.top();
|
||||
|
||||
bool candelete = true;
|
||||
if (LittleFS.exists(spath.c_str())) {
|
||||
Dir dir = LittleFS.openDir(pathlist.top().c_str());
|
||||
while (dir.next()) {
|
||||
if (dir.isDirectory()) {
|
||||
candelete = false;
|
||||
String newdir = pathlist.top() + dir.fileName() + "/";
|
||||
pathlist.push(newdir);
|
||||
} else {
|
||||
log_esp3d("remove %s", dir.fileName().c_str());
|
||||
String s = spath + dir.fileName();
|
||||
LittleFS.remove(s);
|
||||
}
|
||||
Dir dir = LittleFS.openDir(pathlist.top().c_str());
|
||||
while (dir.next()) {
|
||||
if (dir.isDirectory()) {
|
||||
candelete = false;
|
||||
String newdir = pathlist.top() + dir.fileName() + "/";
|
||||
pathlist.push(newdir);
|
||||
} else {
|
||||
String filepath = pathlist.top()+ '/';
|
||||
filepath+= dir.fileName();
|
||||
log_esp3d("remove %s", filepath.c_str());
|
||||
LittleFS.remove(filepath.c_str());
|
||||
}
|
||||
}
|
||||
if (candelete) {
|
||||
if (spath !="/") {
|
||||
if (spath[spath.length()-1] == '/') {
|
||||
spath.remove(spath.length()-1);
|
||||
if (pathlist.top() !="/") {
|
||||
if (LittleFS.exists(pathlist.top().c_str())) {
|
||||
res = LittleFS.rmdir(pathlist.top().c_str());
|
||||
}
|
||||
if (LittleFS.exists(spath.c_str())) {
|
||||
res = LittleFS.rmdir(spath.c_str());
|
||||
}
|
||||
log_esp3d("rmdir %s %d", spath.c_str(), res);
|
||||
}
|
||||
pathlist.pop();
|
||||
}
|
||||
|
||||
@@ -168,11 +168,11 @@ bool ESP_FileSystem::rmdir(const char *path)
|
||||
{
|
||||
String spath = path;
|
||||
spath.trim();
|
||||
if(spath[0]!='/') {
|
||||
spath="/"+spath;
|
||||
if (!spath.startsWith("/")) {
|
||||
spath = '/'+spath;
|
||||
}
|
||||
if (spath[spath.length()-1] == '/') {
|
||||
if (spath!="/") {
|
||||
if (spath!= "/") {
|
||||
if (spath.endsWith("/")) {
|
||||
spath.remove(spath.length()-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
spiffs_8266_filesystem.cpp - ESP3D filesystem configuration class
|
||||
|
||||
Copyright (c) 2014 Luc Lebosse. All rights reserved.
|
||||
|
||||
This code 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 code 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 code; 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 (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) && defined (ARDUINO_ARCH_ESP8266)
|
||||
#include "../esp_filesystem.h"
|
||||
#include <FS.h>
|
||||
Dir tDir_handle[ESP_MAX_OPENHANDLE];
|
||||
extern File tFile_handle[ESP_MAX_OPENHANDLE];
|
||||
|
||||
bool ESP_FileSystem::begin()
|
||||
{
|
||||
_started = SPIFFS.begin();
|
||||
return _started;
|
||||
}
|
||||
void ESP_FileSystem::end()
|
||||
{
|
||||
_started = false;
|
||||
SPIFFS.end();
|
||||
}
|
||||
|
||||
size_t ESP_FileSystem::freeBytes()
|
||||
{
|
||||
return totalBytes() - usedBytes();
|
||||
}
|
||||
|
||||
size_t ESP_FileSystem::totalBytes()
|
||||
{
|
||||
fs::FSInfo info;
|
||||
SPIFFS.info (info);
|
||||
return info.totalBytes;
|
||||
}
|
||||
|
||||
size_t ESP_FileSystem::usedBytes()
|
||||
{
|
||||
fs::FSInfo info;
|
||||
SPIFFS.info (info);
|
||||
return info.usedBytes;
|
||||
}
|
||||
|
||||
uint ESP_FileSystem::maxPathLength()
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
bool ESP_FileSystem::rename(const char *oldpath, const char *newpath)
|
||||
{
|
||||
return SPIFFS.rename(oldpath,newpath);
|
||||
}
|
||||
|
||||
const char * ESP_FileSystem::FilesystemName()
|
||||
{
|
||||
return "SPIFFS";
|
||||
}
|
||||
|
||||
bool ESP_FileSystem::format()
|
||||
{
|
||||
bool res = SPIFFS.format();
|
||||
if (res) {
|
||||
res = begin();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ESP_File ESP_FileSystem::open(const char* path, uint8_t mode)
|
||||
{
|
||||
//do some check
|
||||
if(((strcmp(path,"/") == 0) && ((mode == ESP_FILE_WRITE) || (mode == ESP_FILE_APPEND))) || (strlen(path) == 0)) {
|
||||
return ESP_File();
|
||||
}
|
||||
// path must start by '/'
|
||||
if (path[0] != '/') {
|
||||
return ESP_File();
|
||||
}
|
||||
File ftmp = SPIFFS.open(path, (mode == ESP_FILE_READ)?"r":(mode == ESP_FILE_WRITE)?"w":"a");
|
||||
if(ftmp) {
|
||||
log_esp3d("Success openening file: %s", path);
|
||||
ESP_File esptmp(&ftmp, false,(mode == ESP_FILE_READ)?false:true, path);
|
||||
return esptmp;
|
||||
}
|
||||
(void)mode;
|
||||
Dir dtmp = SPIFFS.openDir(path);
|
||||
ESP_File esptmp(&dtmp, true, false, path);
|
||||
log_esp3d("Success openening dir: %s", path);
|
||||
return esptmp;
|
||||
}
|
||||
|
||||
bool ESP_FileSystem::exists(const char* path)
|
||||
{
|
||||
bool res = false;
|
||||
//root should always be there if started
|
||||
if (strcmp(path, "/") == 0) {
|
||||
return _started;
|
||||
}
|
||||
String spath = path;
|
||||
spath.trim();
|
||||
if (spath[spath.length()-1] == '/') {
|
||||
if (spath!="/") {
|
||||
spath.remove(spath.length()-1);
|
||||
}
|
||||
}
|
||||
res = SPIFFS.exists(spath.c_str());
|
||||
if (!res) {
|
||||
String newpath = spath;
|
||||
if (newpath[newpath.length()-1] != '/') {
|
||||
newpath+="/";
|
||||
}
|
||||
newpath+=".";
|
||||
log_esp3d("Check %s", newpath.c_str());
|
||||
res = SPIFFS.exists(newpath);
|
||||
if (!res) {
|
||||
ESP_File f = ESP_FileSystem::open(path, ESP_FILE_READ);
|
||||
if (f) {
|
||||
//Check directories
|
||||
ESP_File sub = f.openNextFile();
|
||||
if (sub) {
|
||||
sub.close();
|
||||
res = true;
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ESP_FileSystem::remove(const char *path)
|
||||
{
|
||||
String p = path;
|
||||
if(p[0]!='/') {
|
||||
p="/"+p;
|
||||
}
|
||||
return SPIFFS.remove(p);
|
||||
}
|
||||
|
||||
bool ESP_FileSystem::mkdir(const char *path)
|
||||
{
|
||||
//Use file named . to simulate directory
|
||||
String p = path;
|
||||
if (p[p.length()-1] != '/') {
|
||||
p+="/";
|
||||
}
|
||||
p+=".";
|
||||
log_esp3d("Dir create : %s", p.c_str());
|
||||
ESP_File f = open(p.c_str(), ESP_FILE_WRITE);
|
||||
if (f) {
|
||||
f.close();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ESP_FileSystem::rmdir(const char *path)
|
||||
{
|
||||
Dir dtmp = SPIFFS.openDir(path);
|
||||
log_esp3d("Deleting : %s",path);
|
||||
while (dtmp.next()) {
|
||||
if (!SPIFFS.remove(dtmp.fileName().c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ESP_FileSystem::closeAll()
|
||||
{
|
||||
for (uint8_t i = 0; i < ESP_MAX_OPENHANDLE; i++) {
|
||||
tDir_handle[i] = Dir();
|
||||
tFile_handle[i].close();
|
||||
tFile_handle[i] = File();
|
||||
}
|
||||
}
|
||||
|
||||
ESP_File::ESP_File(void* handle, bool isdir, bool iswritemode, const char * path)
|
||||
{
|
||||
_isdir = isdir;
|
||||
_dirlist = "";
|
||||
_isfakedir = false;
|
||||
_index = -1;
|
||||
_filename = "";
|
||||
_name = "";
|
||||
_lastwrite = 0;
|
||||
_iswritemode = iswritemode;
|
||||
_size = 0;
|
||||
if (!handle) {
|
||||
log_esp3d("No handle");
|
||||
return ;
|
||||
}
|
||||
bool set =false;
|
||||
if (_isdir) {
|
||||
for (uint8_t i=0; (i < ESP_MAX_OPENHANDLE) && !set; i++) {
|
||||
if (tDir_handle[i].fileName().length() == 0) {
|
||||
tDir_handle[i] = *((Dir *)handle);
|
||||
_index = i;
|
||||
//Path = filename
|
||||
if (path) {
|
||||
_filename = path;
|
||||
if (_filename == "/") {
|
||||
_filename = "/.";
|
||||
}
|
||||
if (_filename[_filename.length()-1] != '.') {
|
||||
if (_filename[_filename.length()-2] != '/') {
|
||||
_filename+="/";
|
||||
}
|
||||
_filename+=".";
|
||||
}
|
||||
log_esp3d("Filename: %s", _filename.c_str());
|
||||
//Name
|
||||
if (_filename == "/.") {
|
||||
_name = "/";
|
||||
} else {
|
||||
_name = _filename;
|
||||
if (_name.length() >=2) {
|
||||
if ((_name[_name.length() - 1] == '.') && (_name[_name.length() - 2] == '/')) {
|
||||
_name.remove( _name.length() - 2,2);
|
||||
}
|
||||
}
|
||||
_name.remove( 0, _name.lastIndexOf('/')+1);
|
||||
}
|
||||
}
|
||||
log_esp3d("Dir: %s index: %d", _name.c_str(), _index);
|
||||
log_esp3d("name: %s", _name.c_str());
|
||||
log_esp3d("filename: %s", _filename.c_str());
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (uint8_t i=0; (i < ESP_MAX_OPENHANDLE) && !set; i++) {
|
||||
if (!tFile_handle[i]) {
|
||||
tFile_handle[i] = *((File*)handle);
|
||||
//filename
|
||||
_filename = tFile_handle[i].name();
|
||||
|
||||
//if root
|
||||
if (_filename == "/") {
|
||||
_filename = "/.";
|
||||
}
|
||||
if (_isdir) {
|
||||
if (_filename[_filename.length()-1] != '.') {
|
||||
if (_filename[_filename.length()-2] != '/') {
|
||||
_filename+="/";
|
||||
}
|
||||
_filename+=".";
|
||||
}
|
||||
}
|
||||
//name
|
||||
if (_filename == "/.") {
|
||||
_name = "/";
|
||||
} else {
|
||||
_name = _filename;
|
||||
if (_name.endsWith("/.")) {
|
||||
_name.remove( _name.length() - 2,2);
|
||||
_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 = tFile_handle[i].size();
|
||||
//time
|
||||
_lastwrite = tFile_handle[i].getLastWrite();
|
||||
_index = i;
|
||||
log_esp3d("Opening File at index %d",_index);
|
||||
log_esp3d("name: %s", _name.c_str());
|
||||
log_esp3d("filename: %s", _filename.c_str());
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ESP_File::seek(uint32_t pos, uint8_t mode)
|
||||
{
|
||||
return tFile_handle[_index].seek(pos, (SeekMode)mode);
|
||||
}
|
||||
|
||||
void ESP_File::close()
|
||||
{
|
||||
if (_index != -1) {
|
||||
if (_isdir && !_isfakedir) {
|
||||
log_esp3d("Closing Dir at index %d", _index);
|
||||
tDir_handle[_index] = Dir();
|
||||
_index = -1;
|
||||
return;
|
||||
}
|
||||
log_esp3d("Closing File at index %d", _index);
|
||||
tFile_handle[_index].close();
|
||||
//reopen if mode = write
|
||||
//udate size + date
|
||||
if (_iswritemode && !_isdir) {
|
||||
File ftmp = SPIFFS.open(_filename.c_str(), "r");
|
||||
if (ftmp) {
|
||||
_size = ftmp.size();
|
||||
_lastwrite = ftmp.getLastWrite();
|
||||
ftmp.close();
|
||||
}
|
||||
}
|
||||
_index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_File ESP_File::openNextFile()
|
||||
{
|
||||
if ((_index == -1) || !_isdir) {
|
||||
log_esp3d("openNextFile failed");
|
||||
return ESP_File();
|
||||
}
|
||||
if(tDir_handle[_index].next()) {
|
||||
log_esp3d("Getting next file from %s", _filename.c_str());
|
||||
File tmp = tDir_handle[_index].openFile("r");
|
||||
while (tmp) {
|
||||
ESP_File esptmp(&tmp);
|
||||
esptmp.close();
|
||||
String sub = esptmp.filename();
|
||||
sub.remove(0,_filename.length()-1);
|
||||
int pos = sub.indexOf("/");
|
||||
if (pos!=-1) {
|
||||
//is subdir
|
||||
sub = sub.substring(0,pos);
|
||||
log_esp3d("file name:%s name: %s %s sub:%s root:%s", esptmp.filename(), esptmp.name(), (esptmp.isDirectory())?"isDir":"isFile", sub.c_str(), _filename.c_str());
|
||||
String tag = "*" + sub + "*";
|
||||
//test if already in directory list
|
||||
if (_dirlist.indexOf(tag) == -1) {//not in list so add it and return the info
|
||||
_dirlist+= tag;
|
||||
String fname = _filename.substring(0,_filename.length()-1) + sub + "/.";
|
||||
log_esp3d("Found dir # name: %s filename:%s", sub.c_str(), fname.c_str());
|
||||
if (sub == ".") {
|
||||
log_esp3d("Dir tag, ignore it");
|
||||
if(!tDir_handle[_index].next()) {
|
||||
return ESP_File();
|
||||
} else {
|
||||
tmp = tDir_handle[_index].openFile("r");
|
||||
}
|
||||
} else {
|
||||
esptmp = ESP_File(sub.c_str(), fname.c_str());
|
||||
return esptmp;
|
||||
}
|
||||
} else { //already in list so ignore it
|
||||
log_esp3d("Dir name: %s already in list", sub.c_str());
|
||||
if(!tDir_handle[_index].next()) {
|
||||
return ESP_File();
|
||||
} else {
|
||||
tmp = tDir_handle[_index].openFile("r");
|
||||
}
|
||||
}
|
||||
} else { //is file
|
||||
log_esp3d("file name:%s name: %s %s sub:%s root:%s", esptmp.filename(), esptmp.name(), (esptmp.isDirectory())?"isDir":"isFile", sub.c_str(), _filename.c_str());
|
||||
if (sub == ".") {
|
||||
log_esp3d("Dir tag, ignore it");
|
||||
if(!tDir_handle[_index].next()) {
|
||||
return ESP_File();
|
||||
} else {
|
||||
tmp = tDir_handle[_index].openFile("r");
|
||||
}
|
||||
} else {
|
||||
log_esp3d("Found file # name: %s filename:%s", esptmp.filename(), esptmp.name());
|
||||
return esptmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_File();
|
||||
}
|
||||
|
||||
|
||||
#endif //ESP_SPIFFS_FILESYSTEM
|
||||
@@ -209,30 +209,42 @@ bool ESP_SD::mkdir(const char *path)
|
||||
|
||||
bool ESP_SD::rmdir(const char *path)
|
||||
{
|
||||
if (!exists(path)) {
|
||||
|
||||
String p = path;
|
||||
if (!p.startsWith("/")) {
|
||||
p = '/'+p;
|
||||
}
|
||||
if (p!= "/") {
|
||||
if (p.endsWith("/")) {
|
||||
p.remove(p.length()-1);
|
||||
}
|
||||
}
|
||||
if (!exists(p.c_str())) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
std::stack <String > pathlist;
|
||||
String p = path;
|
||||
if (p.endsWith("/")) {
|
||||
p.remove( p.length() - 1,1);
|
||||
}
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
while (pathlist.size() > 0 && res) {
|
||||
File dir = SD.open(pathlist.top().c_str());
|
||||
File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
while (f && res) {
|
||||
if (f.isDirectory()) {
|
||||
candelete = false;
|
||||
String newdir = f.name();
|
||||
String newdir = pathlist.top()+ '/';
|
||||
newdir+= f.name();
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = File();
|
||||
} else {
|
||||
SD.remove(f.name());
|
||||
|
||||
String filepath = pathlist.top()+ '/';
|
||||
filepath+= f.name();
|
||||
f.close();
|
||||
if(!SD.remove(filepath.c_str())) {
|
||||
res = false;
|
||||
}
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,29 +252,39 @@ bool ESP_SD::mkdir(const char *path)
|
||||
|
||||
bool ESP_SD::rmdir(const char *path)
|
||||
{
|
||||
if (!exists(path)) {
|
||||
String p = path;
|
||||
if (!p.endsWith("/")) {
|
||||
p+= '/';
|
||||
}
|
||||
if (!p.startsWith("/")) {
|
||||
p = '/'+p;
|
||||
}
|
||||
if (!exists(p.c_str())) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
std::stack <String > pathlist;
|
||||
String p = path;
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
while (pathlist.size() > 0 && res) {
|
||||
File dir = SD.open(pathlist.top().c_str());
|
||||
dir.rewindDirectory();
|
||||
File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
while (f && res) {
|
||||
if (f.isDirectory()) {
|
||||
candelete = false;
|
||||
String newdir = f.name();
|
||||
String newdir = pathlist.top() + f.name();
|
||||
newdir+="/";
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = File();
|
||||
} else {
|
||||
_sizechanged = true;
|
||||
SD.remove(f.fullName());
|
||||
String filepath = pathlist.top() + f.name();
|
||||
f.close();
|
||||
if (!SD.remove(filepath.c_str())) {
|
||||
res= false;
|
||||
}
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,25 +362,33 @@ bool ESP_SD::mkdir(const char *path)
|
||||
|
||||
bool ESP_SD::rmdir(const char *path)
|
||||
{
|
||||
if (!exists(path)) {
|
||||
|
||||
String p = path;
|
||||
if (!p.endsWith("/")) {
|
||||
p+= '/';
|
||||
}
|
||||
if (!p.startsWith("/")) {
|
||||
p = '/'+p;
|
||||
}
|
||||
if (!exists(p.c_str())) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
std::stack <String > pathlist;
|
||||
String p = path;
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
while (pathlist.size() > 0 && res) {
|
||||
File dir = SD.open(pathlist.top().c_str());
|
||||
dir.rewindDirectory();
|
||||
File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
while (f && res) {
|
||||
if (f.isDir()) {
|
||||
candelete = false;
|
||||
String newdir;
|
||||
char tmp[255];
|
||||
f.getName(tmp,254);
|
||||
newdir = tmp;
|
||||
newdir = pathlist.top() + tmp;
|
||||
newdir+="/";
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = File();
|
||||
@@ -388,8 +396,11 @@ bool ESP_SD::rmdir(const char *path)
|
||||
char tmp[255];
|
||||
f.getName(tmp,254);
|
||||
_sizechanged = true;
|
||||
SD.remove(tmp);
|
||||
String filepath = pathlist.top() + tmp;
|
||||
f.close();
|
||||
if (!SD.remove(filepath.c_str())) {
|
||||
res= false;
|
||||
}
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
@@ -402,7 +413,7 @@ bool ESP_SD::rmdir(const char *path)
|
||||
dir.close();
|
||||
}
|
||||
p = String();
|
||||
log_esp3d("count %d", pathlist.size());
|
||||
log_esp3d("count %d has error %d\n",pathlist.size(), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -347,25 +347,32 @@ bool ESP_SD::mkdir(const char *path)
|
||||
|
||||
bool ESP_SD::rmdir(const char *path)
|
||||
{
|
||||
if (!exists(path)) {
|
||||
String p = path;
|
||||
if (!p.endsWith("/")) {
|
||||
p+= '/';
|
||||
}
|
||||
if (!p.startsWith("/")) {
|
||||
p = '/'+p;
|
||||
}
|
||||
if (!exists(p.c_str())) {
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
std::stack <String > pathlist;
|
||||
String p = path;
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
while (pathlist.size() > 0 && res) {
|
||||
sdfat::File dir = SD.open(pathlist.top().c_str());
|
||||
dir.rewindDirectory();
|
||||
sdfat::File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
while (f && res) {
|
||||
if (f.isDir()) {
|
||||
candelete = false;
|
||||
String newdir;
|
||||
char tmp[255];
|
||||
f.getName(tmp,254);
|
||||
newdir = tmp;
|
||||
newdir = pathlist.top() + tmp;
|
||||
newdir+="/";
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = sdfat::File();
|
||||
@@ -373,8 +380,11 @@ bool ESP_SD::rmdir(const char *path)
|
||||
char tmp[255];
|
||||
f.getName(tmp,254);
|
||||
_sizechanged = true;
|
||||
SD.remove(tmp);
|
||||
String filepath = pathlist.top() + tmp;
|
||||
f.close();
|
||||
if (!SD.remove(filepath.c_str())) {
|
||||
res= false;
|
||||
}
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,853 +0,0 @@
|
||||
/*
|
||||
sd_sdfat_esp32.cpp - ESP3D sd support class
|
||||
|
||||
Copyright (c) 2014 Luc Lebosse. All rights reserved.
|
||||
|
||||
This code 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 code 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 code; 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 <stack>
|
||||
#include "../../../core/settings_esp3d.h"
|
||||
#include <SdFat.h>
|
||||
extern File tSDFile_handle[ESP_MAX_SD_OPENHANDLE];
|
||||
|
||||
//Max Freq Working
|
||||
#define FREQMZ 40
|
||||
SdFat SD;
|
||||
|
||||
void dateTime (uint16_t* date, uint16_t* dtime)
|
||||
{
|
||||
struct tm tmstruct;
|
||||
time_t now;
|
||||
time (&now);
|
||||
localtime_r (&now, &tmstruct);
|
||||
*date = FAT_DATE ( (tmstruct.tm_year) + 1900, ( tmstruct.tm_mon) + 1, tmstruct.tm_mday);
|
||||
*dtime = FAT_TIME (tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
|
||||
}
|
||||
|
||||
time_t getDateTimeFile(File & filehandle)
|
||||
{
|
||||
static time_t dt = 0;
|
||||
#ifdef SD_TIMESTAMP_FEATURE
|
||||
struct tm timefile;
|
||||
dir_t d;
|
||||
if(filehandle) {
|
||||
if (filehandle.dirEntry(&d)) {
|
||||
timefile.tm_year = FAT_YEAR(d.lastWriteDate) - 1900;
|
||||
timefile.tm_mon = FAT_MONTH(d.lastWriteDate) - 1;
|
||||
timefile.tm_mday = FAT_DAY(d.lastWriteDate);
|
||||
timefile.tm_hour = FAT_HOUR(d.lastWriteTime);
|
||||
timefile.tm_min = FAT_MINUTE(d.lastWriteTime);
|
||||
timefile.tm_sec = FAT_SECOND(d.lastWriteTime);
|
||||
timefile.tm_isdst = -1;
|
||||
dt = mktime(&timefile);
|
||||
if (dt == -1) {
|
||||
log_esp3d("mktime failed");
|
||||
}
|
||||
} else {
|
||||
log_esp3d("stat file failed");
|
||||
}
|
||||
} else {
|
||||
log_esp3d("check stat file failed");
|
||||
}
|
||||
#endif //SD_TIMESTAMP_FEATURE
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
} else {
|
||||
_sizechanged = true;
|
||||
}
|
||||
//SD is idle or not detected, let see if still the case
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
log_esp3d("Spi : CS: %d, Miso: %d, Mosi: %d, SCK: %d",ESP_SD_CS_PIN!=-1?ESP_SD_CS_PIN:SS, ESP_SD_MISO_PIN!=-1?ESP_SD_MISO_PIN:MISO, ESP_SD_MOSI_PIN!=-1?ESP_SD_MOSI_PIN:MOSI, ESP_SD_SCK_PIN!=-1?ESP_SD_SCK_PIN:SCK);
|
||||
//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()
|
||||
{
|
||||
#if (ESP_SD_CS_PIN != -1) || (ESP_SD_MISO_PIN != -1) || (ESP_SD_MOSI_PIN != -1) || (ESP_SD_SCK_PIN != -1)
|
||||
SPI.begin(ESP_SD_SCK_PIN, ESP_SD_MISO_PIN, ESP_SD_MOSI_PIN, ESP_SD_CS_PIN);
|
||||
#endif
|
||||
_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;
|
||||
}
|
||||
#ifdef SD_TIMESTAMP_FEATURE
|
||||
//set callback to get time on files on SD
|
||||
SdFile::dateTimeCallback (dateTime);
|
||||
#endif //SD_TIMESTAMP_FEATURE
|
||||
//Setup pins
|
||||
#if defined(ESP_SD_DETECT_PIN) && ESP_SD_DETECT_PIN != -1
|
||||
pinMode (ESP_SD_DETECT_PIN, INPUT);
|
||||
#endif //ESP_SD_DETECT_PIN
|
||||
#if SD_DEVICE_CONNECTION == ESP_SHARED_SD
|
||||
#if defined(ESP_FLAG_SHARED_SD_PIN) && ESP_FLAG_SHARED_SD_PIN != -1
|
||||
pinMode (ESP_FLAG_SHARED_SD_PIN, OUTPUT);
|
||||
digitalWrite(ESP_FLAG_SHARED_SD_PIN, !ESP_FLAG_SHARED_SD_VALUE);
|
||||
#endif //ESP_FLAG_SHARED_SD_PIN
|
||||
#endif //SD_DEVICE_CONNECTION == ESP_SHARED_SD
|
||||
return _started;
|
||||
}
|
||||
|
||||
void ESP_SD::end()
|
||||
{
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
_started = false;
|
||||
}
|
||||
|
||||
void ESP_SD::refreshStats(bool force)
|
||||
{
|
||||
if (force || _sizechanged) {
|
||||
usedBytes(true);
|
||||
}
|
||||
_sizechanged = false;
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::totalBytes(bool refresh)
|
||||
{
|
||||
static uint64_t _totalBytes = 0;
|
||||
if (refresh || _totalBytes==0) {
|
||||
_totalBytes = SD.vol()->clusterCount();
|
||||
uint8_t blocks = SD.vol()->blocksPerCluster();
|
||||
_totalBytes = _totalBytes * blocks * 512;
|
||||
}
|
||||
return _totalBytes;
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::usedBytes(bool refresh)
|
||||
{
|
||||
return totalBytes(refresh) - freeBytes(refresh);
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::freeBytes(bool refresh)
|
||||
{
|
||||
static uint64_t _freeBytes = 0;
|
||||
if (refresh || _freeBytes==0) {
|
||||
uint64_t volFree = SD.vol()->freeClusterCount();
|
||||
uint8_t blocks = SD.vol()->blocksPerCluster();
|
||||
_freeBytes = volFree * blocks * 512;
|
||||
}
|
||||
return _freeBytes;
|
||||
}
|
||||
|
||||
uint ESP_SD::maxPathLength()
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
bool ESP_SD::rename(const char *oldpath, const char *newpath)
|
||||
{
|
||||
return SD.rename(oldpath,newpath);
|
||||
}
|
||||
|
||||
// strings needed in file system structures
|
||||
#define noName "NO NAME "
|
||||
#define fat16str "FAT16 "
|
||||
#define fat32str "FAT32 "
|
||||
// constants for file system structure
|
||||
#define BU16 128
|
||||
#define BU32 8192
|
||||
#define ERASE_SIZE 262144L
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// write cached block to the card
|
||||
uint8_t writeCache(uint32_t lbn, Sd2Card & card, cache_t & cache)
|
||||
{
|
||||
return card.writeBlock(lbn, cache.data);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// initialize appropriate sizes for SD capacity
|
||||
bool initSizes(uint32_t cardCapacityMB, uint8_t & sectorsPerCluster, uint8_t & numberOfHeads, uint8_t & sectorsPerTrack)
|
||||
{
|
||||
if (cardCapacityMB <= 6) {
|
||||
return false;
|
||||
} 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// zero cache and optionally set the sector signature
|
||||
void clearCache(uint8_t addSig, cache_t & cache)
|
||||
{
|
||||
memset(&cache, 0, sizeof(cache));
|
||||
if (addSig) {
|
||||
cache.mbr.mbrSig0 = BOOTSIG0;
|
||||
cache.mbr.mbrSig1 = BOOTSIG1;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// zero FAT and root dir area on SD
|
||||
bool clearFatDir(uint32_t bgn, uint32_t count, Sd2Card & card, cache_t & cache, ESP3DOutput * output)
|
||||
{
|
||||
clearCache(false, cache);
|
||||
if (!card.writeStart(bgn, count)) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if ((i & 0XFF) == 0) {
|
||||
if (output) {
|
||||
output->print(".");
|
||||
}
|
||||
}
|
||||
if (!card.writeData(cache.data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!card.writeStop()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// return cylinder number for a logical block number
|
||||
uint16_t lbnToCylinder(uint32_t lbn, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
|
||||
{
|
||||
return lbn / (numberOfHeads * sectorsPerTrack);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// return head number for a logical block number
|
||||
uint8_t lbnToHead(uint32_t lbn, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
|
||||
{
|
||||
return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// return sector number for a logical block number
|
||||
uint8_t lbnToSector(uint32_t lbn, uint8_t sectorsPerTrack)
|
||||
{
|
||||
return (lbn % sectorsPerTrack) + 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// format and write the Master Boot Record
|
||||
bool writeMbr(Sd2Card & card, cache_t & cache, uint8_t partType, uint32_t relSector, uint32_t partSize, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
|
||||
{
|
||||
clearCache(true, cache);
|
||||
part_t* p = cache.mbr.part;
|
||||
p->boot = 0;
|
||||
uint16_t c = lbnToCylinder(relSector, numberOfHeads, sectorsPerTrack);
|
||||
if (c > 1023) {
|
||||
return false;
|
||||
}
|
||||
p->beginCylinderHigh = c >> 8;
|
||||
p->beginCylinderLow = c & 0XFF;
|
||||
p->beginHead = lbnToHead(relSector, numberOfHeads, sectorsPerTrack);
|
||||
p->beginSector = lbnToSector(relSector, sectorsPerTrack);
|
||||
p->type = partType;
|
||||
uint32_t endLbn = relSector + partSize - 1;
|
||||
c = lbnToCylinder(endLbn,numberOfHeads, sectorsPerTrack);
|
||||
if (c <= 1023) {
|
||||
p->endCylinderHigh = c >> 8;
|
||||
p->endCylinderLow = c & 0XFF;
|
||||
p->endHead = lbnToHead(endLbn, numberOfHeads, sectorsPerTrack);
|
||||
p->endSector = lbnToSector(endLbn, sectorsPerTrack);
|
||||
} 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, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// generate serial number from card size and micros since boot
|
||||
uint32_t volSerialNumber(uint32_t cardSizeBlocks)
|
||||
{
|
||||
return (cardSizeBlocks << 8) + micros();
|
||||
}
|
||||
|
||||
// format the SD as FAT16
|
||||
bool makeFat16(uint32_t & dataStart, Sd2Card & card, cache_t & cache, uint8_t numberOfHeads, uint8_t sectorsPerTrack, uint32_t cardSizeBlocks, uint8_t sectorsPerCluster, uint32_t &relSector, uint32_t partSize, uint8_t & partType, uint32_t &fatSize, uint32_t &fatStart, uint16_t reservedSectors, ESP3DOutput * output)
|
||||
{
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
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
|
||||
if (!writeMbr(card, cache, partType, relSector, partSize, numberOfHeads, sectorsPerTrack)) {
|
||||
return false;
|
||||
}
|
||||
clearCache(true, cache);
|
||||
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(cardSizeBlocks);
|
||||
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
||||
memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType));
|
||||
// write partition boot sector
|
||||
if (!writeCache(relSector, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
// clear FAT and root directory
|
||||
clearFatDir(fatStart, dataStart - fatStart, card, cache, output);
|
||||
clearCache(false, cache);
|
||||
cache.fat16[0] = 0XFFF8;
|
||||
cache.fat16[1] = 0XFFFF;
|
||||
// write first block of FAT and backup for reserved clusters
|
||||
if (!writeCache(fatStart, card, cache)
|
||||
|| !writeCache(fatStart + fatSize, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// format the SD as FAT32
|
||||
bool makeFat32(uint32_t & dataStart, Sd2Card & card, cache_t & cache, uint8_t numberOfHeads, uint8_t sectorsPerTrack, uint32_t cardSizeBlocks, uint8_t sectorsPerCluster, uint32_t &relSector, uint32_t partSize, uint8_t & partType, uint32_t &fatSize, uint32_t &fatStart, uint16_t reservedSectors, ESP3DOutput * output)
|
||||
{
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (!writeMbr(card, cache, partType, relSector, partSize, numberOfHeads, sectorsPerTrack)) {
|
||||
return false;
|
||||
}
|
||||
clearCache(true, cache);
|
||||
|
||||
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(cardSizeBlocks);
|
||||
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
||||
memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
|
||||
// write partition boot sector and backup
|
||||
if (!writeCache(relSector, card, cache)
|
||||
|| !writeCache(relSector + 6, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
clearCache(true, cache);
|
||||
// write extra boot area and backup
|
||||
if (!writeCache(relSector + 2, card, cache)
|
||||
|| !writeCache(relSector + 8, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
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, card, cache)
|
||||
|| !writeCache(relSector + 7, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster, card, cache, output);
|
||||
clearCache(false, cache);
|
||||
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, card, cache)
|
||||
|| !writeCache(fatStart + fatSize, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eraseCard(Sd2Card & card, cache_t & cache, uint32_t cardSizeBlocks, ESP3DOutput * output)
|
||||
{
|
||||
uint32_t firstBlock = 0;
|
||||
uint32_t lastBlock = 0;
|
||||
//uint16_t n = 0;
|
||||
if (output) {
|
||||
output->printMSG("Erasing ", false);
|
||||
}
|
||||
do {
|
||||
lastBlock = firstBlock + ERASE_SIZE - 1;
|
||||
if (lastBlock >= cardSizeBlocks) {
|
||||
lastBlock = cardSizeBlocks - 1;
|
||||
}
|
||||
if (!card.erase(firstBlock, lastBlock)) {
|
||||
return false;
|
||||
}
|
||||
if (output) {
|
||||
output->print(".");
|
||||
}
|
||||
firstBlock += ERASE_SIZE;
|
||||
} while (firstBlock < cardSizeBlocks);
|
||||
|
||||
if (!card.readBlock(0, cache.data)) {
|
||||
return false;
|
||||
}
|
||||
if (output) {
|
||||
output->printLN("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool formatCard(uint32_t & dataStart, Sd2Card & card,
|
||||
cache_t & cache, uint8_t numberOfHeads,
|
||||
uint8_t sectorsPerTrack, uint32_t cardSizeBlocks,
|
||||
uint8_t sectorsPerCluster, uint32_t &relSector,
|
||||
uint32_t partSize, uint8_t & partType,
|
||||
uint32_t &fatSize, uint32_t &fatStart,
|
||||
uint32_t cardCapacityMB, uint16_t reservedSectors, ESP3DOutput * output)
|
||||
{
|
||||
initSizes(cardCapacityMB, sectorsPerCluster, numberOfHeads, sectorsPerTrack);
|
||||
if (card.type() != SD_CARD_TYPE_SDHC) {
|
||||
if (output) {
|
||||
output->printMSG("Formating FAT16 ");
|
||||
}
|
||||
if(!makeFat16(dataStart, card, cache, numberOfHeads, sectorsPerTrack, cardSizeBlocks, sectorsPerCluster, relSector, partSize, partType, fatSize, fatStart, reservedSectors, output)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (output) {
|
||||
output->printMSG("Formating FAT32 ", false);
|
||||
}
|
||||
if(!makeFat32(dataStart, card, cache, numberOfHeads, sectorsPerTrack, cardSizeBlocks, sectorsPerCluster, relSector, partSize, partType, fatSize, fatStart, reservedSectors, output)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (output) {
|
||||
output->printLN("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ESP_SD::format(ESP3DOutput * output)
|
||||
{
|
||||
if (ESP_SD::getState(true) == ESP_SDCARD_IDLE) {
|
||||
Sd2Card card;
|
||||
uint32_t cardSizeBlocks;
|
||||
uint32_t cardCapacityMB;
|
||||
// cache for SD block
|
||||
cache_t cache;
|
||||
|
||||
// MBR information
|
||||
uint8_t partType = 0;
|
||||
uint32_t relSector = 0;
|
||||
uint32_t partSize = 0;
|
||||
|
||||
// Fake disk geometry
|
||||
uint8_t numberOfHeads = 0;
|
||||
uint8_t sectorsPerTrack = 0;
|
||||
|
||||
// FAT parameters
|
||||
uint16_t reservedSectors = 0;
|
||||
uint8_t sectorsPerCluster = 0;
|
||||
uint32_t fatStart = 0;
|
||||
uint32_t fatSize = 0;
|
||||
uint32_t dataStart = 0;
|
||||
if (!card.begin((ESP_SD_CS_PIN == -1)?SS:ESP_SD_CS_PIN, SD_SCK_MHZ(FREQMZ/_spi_speed_divider))) {
|
||||
return false;
|
||||
}
|
||||
cardSizeBlocks = card.cardSize();
|
||||
if (cardSizeBlocks == 0) {
|
||||
return false;
|
||||
}
|
||||
cardCapacityMB = (cardSizeBlocks + 2047)/2048;
|
||||
if (output) {
|
||||
String s = "Capacity detected :" + String((1.048576*cardCapacityMB)/1024) + "GB";
|
||||
output->printMSG(s.c_str());
|
||||
}
|
||||
if (!eraseCard(card, cache, cardSizeBlocks, output)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!formatCard(dataStart, card, cache, numberOfHeads,
|
||||
sectorsPerTrack, cardSizeBlocks,
|
||||
sectorsPerCluster, relSector, partSize, partType,
|
||||
fatSize, fatStart, cardCapacityMB, reservedSectors,output)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_SDFile ESP_SD::open(const char* path, uint8_t mode)
|
||||
{
|
||||
//do some check
|
||||
if(((strcmp(path,"/") == 0) && ((mode == ESP_FILE_WRITE) || (mode == ESP_FILE_APPEND))) || (strlen(path) == 0)) {
|
||||
_sizechanged = true;
|
||||
return ESP_SDFile();
|
||||
}
|
||||
// path must start by '/'
|
||||
if (path[0] != '/') {
|
||||
return ESP_SDFile();
|
||||
}
|
||||
if (mode != ESP_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_FILE_READ)?FILE_READ:(mode == ESP_FILE_WRITE)?FILE_WRITE:FILE_WRITE);
|
||||
ESP_SDFile esptmp(&tmp, tmp.isDir(),(mode == ESP_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_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;
|
||||
std::stack <String > pathlist;
|
||||
String p = path;
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
File dir = SD.open(pathlist.top().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.top() !="/") {
|
||||
res = SD.rmdir(pathlist.top().c_str());
|
||||
}
|
||||
pathlist.pop();
|
||||
}
|
||||
dir.close();
|
||||
}
|
||||
p = String();
|
||||
log_esp3d("count %d", pathlist.size());
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
bool ESP_SDFile::seek(uint32_t pos, uint8_t mode)
|
||||
{
|
||||
if (mode == ESP_SEEK_END) {
|
||||
return tSDFile_handle[_index].seek(-pos); //based on SDFS comment
|
||||
}
|
||||
return tSDFile_handle[_index].seek(pos);
|
||||
}
|
||||
|
||||
ESP_SDFile::ESP_SDFile(void* handle, bool isdir, bool iswritemode, const char * path)
|
||||
{
|
||||
_isdir = isdir;
|
||||
_dirlist = "";
|
||||
_index = -1;
|
||||
_filename = "";
|
||||
_name = "";
|
||||
_lastwrite = 0 ;
|
||||
_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);
|
||||
_isdir = true;
|
||||
}
|
||||
if (_name[0] == '/') {
|
||||
_name.remove( 0, 1);
|
||||
}
|
||||
int pos = _name.lastIndexOf('/');
|
||||
if (pos != -1) {
|
||||
_name.remove( 0, pos+1);
|
||||
}
|
||||
if (_name.length() == 0) {
|
||||
_name = "/";
|
||||
}
|
||||
//size
|
||||
_size = tSDFile_handle[i].size();
|
||||
//time
|
||||
if (!_isdir && !iswritemode) {
|
||||
_lastwrite = getDateTimeFile(tSDFile_handle[i]);
|
||||
|
||||
} else {
|
||||
//no need date time for directory
|
||||
_lastwrite = 0;
|
||||
}
|
||||
_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();
|
||||
_lastwrite = getDateTimeFile(ftmp);
|
||||
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 - " SD_FAT_VERSION_STR ;
|
||||
}
|
||||
|
||||
#endif //SD_DEVICE == ESP_SDFAT
|
||||
#endif //ARCH_ESP32 && SD_DEVICE
|
||||
@@ -1,864 +0,0 @@
|
||||
/*
|
||||
sd_sdfat_esp8266.cpp - ESP3D sd support class
|
||||
|
||||
Copyright (c) 2014 Luc Lebosse. All rights reserved.
|
||||
|
||||
This code 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 code 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 code; 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_ESP8266) && defined(SD_DEVICE)
|
||||
#if (SD_DEVICE == ESP_SDFAT)
|
||||
#define FS_NO_GLOBALS
|
||||
#include "../esp_sd.h"
|
||||
#include <stack>
|
||||
#include "../../../core/settings_esp3d.h"
|
||||
#define NO_GLOBAL_SD
|
||||
#include <SdFat.h>
|
||||
extern sdfat::File tSDFile_handle[ESP_MAX_SD_OPENHANDLE];
|
||||
using namespace sdfat;
|
||||
SdFat SD;
|
||||
|
||||
void dateTime (uint16_t* date, uint16_t* dtime)
|
||||
{
|
||||
struct tm tmstruct;
|
||||
time_t now;
|
||||
time (&now);
|
||||
localtime_r (&now, &tmstruct);
|
||||
*date = FAT_DATE ( (tmstruct.tm_year) + 1900, ( tmstruct.tm_mon) + 1, tmstruct.tm_mday);
|
||||
*dtime = FAT_TIME (tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
|
||||
}
|
||||
|
||||
time_t getDateTimeFile(sdfat::File & filehandle)
|
||||
{
|
||||
static time_t dt = 0;
|
||||
#ifdef SD_TIMESTAMP_FEATURE
|
||||
struct tm timefile;
|
||||
dir_t d;
|
||||
if(filehandle) {
|
||||
if (filehandle.dirEntry(&d)) {
|
||||
timefile.tm_year = FAT_YEAR(d.lastWriteDate) - 1900;
|
||||
timefile.tm_mon = FAT_MONTH(d.lastWriteDate) - 1;
|
||||
timefile.tm_mday = FAT_DAY(d.lastWriteDate);
|
||||
timefile.tm_hour = FAT_HOUR(d.lastWriteTime);
|
||||
timefile.tm_min = FAT_MINUTE(d.lastWriteTime);
|
||||
timefile.tm_sec = FAT_SECOND(d.lastWriteTime);
|
||||
timefile.tm_isdst = -1;
|
||||
dt = mktime(&timefile);
|
||||
if (dt == -1) {
|
||||
log_esp3d("mktime failed");
|
||||
}
|
||||
} else {
|
||||
log_esp3d("stat file failed");
|
||||
}
|
||||
} else {
|
||||
log_esp3d("check file for stat failed");
|
||||
}
|
||||
#endif //SD_TIMESTAMP_FEATURE
|
||||
return dt;
|
||||
}
|
||||
|
||||
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)) {
|
||||
log_esp3d("No SD State %d vs %d", digitalRead (ESP_SD_DETECT_PIN), ESP_SD_DETECT_VALUE);
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
return _state;
|
||||
} else {
|
||||
log_esp3d("SD Detect Pin ok");
|
||||
}
|
||||
#endif //ESP_SD_DETECT_PIN
|
||||
//if busy doing something return state
|
||||
if (!((_state == ESP_SDCARD_NOT_PRESENT) || _state == ESP_SDCARD_IDLE)) {
|
||||
log_esp3d("Busy SD State");
|
||||
return _state;
|
||||
}
|
||||
if (!refresh) {
|
||||
log_esp3d("SD State cache is %d", _state);
|
||||
return _state; //to avoid refresh=true + busy to reset SD and waste time
|
||||
} else {
|
||||
_sizechanged = true;
|
||||
}
|
||||
//SD is idle or not detected, let see if still the case
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
//refresh content if card was removed
|
||||
if (SD.begin((ESP_SD_CS_PIN == -1)?SS:ESP_SD_CS_PIN, SD_SCK_HZ(F_CPU/_spi_speed_divider))) {
|
||||
log_esp3d("Init SD State ok");
|
||||
if (SD.card()->cardSize() > 0 ) {
|
||||
log_esp3d("SD available");
|
||||
_state = ESP_SDCARD_IDLE;
|
||||
} else {
|
||||
log_esp3d("Cannot get card size");
|
||||
}
|
||||
} else {
|
||||
log_esp3d("Init SD State failed");
|
||||
}
|
||||
log_esp3d("SD State is %d", _state);
|
||||
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;
|
||||
}
|
||||
#ifdef SD_TIMESTAMP_FEATURE
|
||||
//set callback to get time on files on SD
|
||||
SdFile::dateTimeCallback (dateTime);
|
||||
#endif //SD_TIMESTAMP_FEATURE
|
||||
//Setup pins
|
||||
#if defined(ESP_SD_DETECT_PIN) && ESP_SD_DETECT_PIN != -1
|
||||
pinMode (ESP_SD_DETECT_PIN, INPUT);
|
||||
#endif //ESP_SD_DETECT_PIN
|
||||
#if SD_DEVICE_CONNECTION == ESP_SHARED_SD
|
||||
#if defined(ESP_FLAG_SHARED_SD_PIN) && ESP_FLAG_SHARED_SD_PIN != -1
|
||||
pinMode (ESP_FLAG_SHARED_SD_PIN, OUTPUT);
|
||||
digitalWrite(ESP_FLAG_SHARED_SD_PIN, !ESP_FLAG_SHARED_SD_VALUE);
|
||||
#endif //ESP_FLAG_SHARED_SD_PIN
|
||||
#endif //SD_DEVICE_CONNECTION == ESP_SHARED_SD
|
||||
return _started;
|
||||
}
|
||||
|
||||
void ESP_SD::end()
|
||||
{
|
||||
_state = ESP_SDCARD_NOT_PRESENT;
|
||||
_started = false;
|
||||
}
|
||||
|
||||
void ESP_SD::refreshStats(bool force)
|
||||
{
|
||||
if (force || _sizechanged) {
|
||||
usedBytes(true);
|
||||
}
|
||||
_sizechanged = false;
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::totalBytes(bool refresh)
|
||||
{
|
||||
static uint64_t _totalBytes = 0;
|
||||
if (refresh || _totalBytes==0) {
|
||||
_totalBytes = SD.vol()->clusterCount();
|
||||
uint8_t blocks = SD.vol()->blocksPerCluster();
|
||||
_totalBytes = _totalBytes * blocks * 512;
|
||||
}
|
||||
return _totalBytes;
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::usedBytes(bool refresh)
|
||||
{
|
||||
return totalBytes(refresh) - freeBytes(refresh);
|
||||
}
|
||||
|
||||
uint64_t ESP_SD::freeBytes(bool refresh)
|
||||
{
|
||||
static uint64_t _freeBytes = 0;
|
||||
if (_freeBytes== 0 || refresh) {
|
||||
_freeBytes = SD.vol()->freeClusterCount();
|
||||
uint8_t blocks = SD.vol()->blocksPerCluster();
|
||||
_freeBytes = _freeBytes * blocks * 512;
|
||||
}
|
||||
|
||||
return _freeBytes;
|
||||
}
|
||||
|
||||
uint ESP_SD::maxPathLength()
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
bool ESP_SD::rename(const char *oldpath, const char *newpath)
|
||||
{
|
||||
return SD.rename(oldpath,newpath);
|
||||
}
|
||||
|
||||
// strings needed in file system structures
|
||||
#define noName "NO NAME "
|
||||
#define fat16str "FAT16 "
|
||||
#define fat32str "FAT32 "
|
||||
// constants for file system structure
|
||||
#define BU16 128
|
||||
#define BU32 8192
|
||||
#define ERASE_SIZE 262144L
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// write cached block to the card
|
||||
uint8_t writeCache(uint32_t lbn, Sd2Card & card, cache_t & cache)
|
||||
{
|
||||
return card.writeBlock(lbn, cache.data);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// initialize appropriate sizes for SD capacity
|
||||
bool initSizes(uint32_t cardCapacityMB, uint8_t & sectorsPerCluster, uint8_t & numberOfHeads, uint8_t & sectorsPerTrack)
|
||||
{
|
||||
if (cardCapacityMB <= 6) {
|
||||
return false;
|
||||
} 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// zero cache and optionally set the sector signature
|
||||
void clearCache(uint8_t addSig, cache_t & cache)
|
||||
{
|
||||
memset(&cache, 0, sizeof(cache));
|
||||
if (addSig) {
|
||||
cache.mbr.mbrSig0 = BOOTSIG0;
|
||||
cache.mbr.mbrSig1 = BOOTSIG1;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// zero FAT and root dir area on SD
|
||||
bool clearFatDir(uint32_t bgn, uint32_t count, Sd2Card & card, cache_t & cache, ESP3DOutput * output)
|
||||
{
|
||||
clearCache(false, cache);
|
||||
if (!card.writeStart(bgn, count)) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if ((i & 0XFF) == 0) {
|
||||
if (output) {
|
||||
output->print(".");
|
||||
}
|
||||
}
|
||||
if (!card.writeData(cache.data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!card.writeStop()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// return cylinder number for a logical block number
|
||||
uint16_t lbnToCylinder(uint32_t lbn, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
|
||||
{
|
||||
return lbn / (numberOfHeads * sectorsPerTrack);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// return head number for a logical block number
|
||||
uint8_t lbnToHead(uint32_t lbn, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
|
||||
{
|
||||
return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// return sector number for a logical block number
|
||||
uint8_t lbnToSector(uint32_t lbn, uint8_t sectorsPerTrack)
|
||||
{
|
||||
return (lbn % sectorsPerTrack) + 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// format and write the Master Boot Record
|
||||
bool writeMbr(Sd2Card & card, cache_t & cache, uint8_t partType, uint32_t relSector, uint32_t partSize, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
|
||||
{
|
||||
clearCache(true, cache);
|
||||
part_t* p = cache.mbr.part;
|
||||
p->boot = 0;
|
||||
uint16_t c = lbnToCylinder(relSector, numberOfHeads, sectorsPerTrack);
|
||||
if (c > 1023) {
|
||||
return false;
|
||||
}
|
||||
p->beginCylinderHigh = c >> 8;
|
||||
p->beginCylinderLow = c & 0XFF;
|
||||
p->beginHead = lbnToHead(relSector, numberOfHeads, sectorsPerTrack);
|
||||
p->beginSector = lbnToSector(relSector, sectorsPerTrack);
|
||||
p->type = partType;
|
||||
uint32_t endLbn = relSector + partSize - 1;
|
||||
c = lbnToCylinder(endLbn,numberOfHeads, sectorsPerTrack);
|
||||
if (c <= 1023) {
|
||||
p->endCylinderHigh = c >> 8;
|
||||
p->endCylinderLow = c & 0XFF;
|
||||
p->endHead = lbnToHead(endLbn, numberOfHeads, sectorsPerTrack);
|
||||
p->endSector = lbnToSector(endLbn, sectorsPerTrack);
|
||||
} 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, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// generate serial number from card size and micros since boot
|
||||
uint32_t volSerialNumber(uint32_t cardSizeBlocks)
|
||||
{
|
||||
return (cardSizeBlocks << 8) + micros();
|
||||
}
|
||||
|
||||
// format the SD as FAT16
|
||||
bool makeFat16(uint32_t & dataStart, Sd2Card & card, cache_t & cache, uint8_t numberOfHeads, uint8_t sectorsPerTrack, uint32_t cardSizeBlocks, uint8_t sectorsPerCluster, uint32_t &relSector, uint8_t & partType, uint32_t &fatSize, uint32_t &fatStart, ESP3DOutput * output)
|
||||
{
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
uint16_t reservedSectors = 1;
|
||||
fatStart = relSector + reservedSectors;
|
||||
uint32_t partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
|
||||
if (partSize < 32680) {
|
||||
partType = 0X01;
|
||||
} else if (partSize < 65536) {
|
||||
partType = 0X04;
|
||||
} else {
|
||||
partType = 0X06;
|
||||
}
|
||||
// write MBR
|
||||
if (!writeMbr(card, cache, partType, relSector, partSize, numberOfHeads, sectorsPerTrack)) {
|
||||
return false;
|
||||
}
|
||||
clearCache(true, cache);
|
||||
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(cardSizeBlocks);
|
||||
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
||||
memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType));
|
||||
// write partition boot sector
|
||||
if (!writeCache(relSector, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
// clear FAT and root directory
|
||||
clearFatDir(fatStart, dataStart - fatStart, card, cache, output);
|
||||
clearCache(false, cache);
|
||||
cache.fat16[0] = 0XFFF8;
|
||||
cache.fat16[1] = 0XFFFF;
|
||||
// write first block of FAT and backup for reserved clusters
|
||||
if (!writeCache(fatStart, card, cache)
|
||||
|| !writeCache(fatStart + fatSize, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// format the SD as FAT32
|
||||
bool makeFat32(uint32_t & dataStart, Sd2Card & card, cache_t & cache, uint8_t numberOfHeads, uint8_t sectorsPerTrack, uint32_t cardSizeBlocks, uint8_t sectorsPerCluster, uint32_t &relSector, uint8_t & partType, uint32_t &fatSize, uint32_t &fatStart, ESP3DOutput * output)
|
||||
{
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
uint16_t reservedSectors = dataStart - relSector - 2 * fatSize;
|
||||
fatStart = relSector + reservedSectors;
|
||||
uint32_t 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;
|
||||
}
|
||||
if (!writeMbr(card, cache, partType, relSector, partSize, numberOfHeads, sectorsPerTrack)) {
|
||||
return false;
|
||||
}
|
||||
clearCache(true, cache);
|
||||
|
||||
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(cardSizeBlocks);
|
||||
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
|
||||
memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
|
||||
// write partition boot sector and backup
|
||||
if (!writeCache(relSector, card, cache)
|
||||
|| !writeCache(relSector + 6, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
clearCache(true, cache);
|
||||
// write extra boot area and backup
|
||||
if (!writeCache(relSector + 2, card, cache)
|
||||
|| !writeCache(relSector + 8, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
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, card, cache)
|
||||
|| !writeCache(relSector + 7, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster, card, cache, output);
|
||||
clearCache(false, cache);
|
||||
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, card, cache)
|
||||
|| !writeCache(fatStart + fatSize, card, cache)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eraseCard(Sd2Card & card, cache_t & cache, uint32_t cardSizeBlocks, ESP3DOutput * output)
|
||||
{
|
||||
uint32_t firstBlock = 0;
|
||||
uint32_t lastBlock;
|
||||
if (output) {
|
||||
output->printMSG("Erasing ", false);
|
||||
}
|
||||
do {
|
||||
lastBlock = firstBlock + ERASE_SIZE - 1;
|
||||
if (lastBlock >= cardSizeBlocks) {
|
||||
lastBlock = cardSizeBlocks - 1;
|
||||
}
|
||||
if (!card.erase(firstBlock, lastBlock)) {
|
||||
return false;
|
||||
}
|
||||
if (output) {
|
||||
output->print(".");
|
||||
}
|
||||
firstBlock += ERASE_SIZE;
|
||||
} while (firstBlock < cardSizeBlocks);
|
||||
|
||||
if (!card.readBlock(0, cache.data)) {
|
||||
return false;
|
||||
}
|
||||
if (output) {
|
||||
output->printLN("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool formatCard(uint32_t & dataStart, Sd2Card & card,
|
||||
cache_t & cache, uint32_t cardSizeBlocks,
|
||||
uint32_t &relSector,
|
||||
uint8_t & partType,
|
||||
uint32_t &fatSize, uint32_t &fatStart,
|
||||
uint32_t cardCapacityMB, ESP3DOutput * output)
|
||||
{
|
||||
// Fake disk geometry
|
||||
uint8_t numberOfHeads;
|
||||
uint8_t sectorsPerTrack;
|
||||
// FAT parameters
|
||||
uint8_t sectorsPerCluster;
|
||||
|
||||
initSizes(cardCapacityMB, sectorsPerCluster, numberOfHeads, sectorsPerTrack);
|
||||
if (card.type() != SD_CARD_TYPE_SDHC) {
|
||||
if (output) {
|
||||
output->printMSG("Formating FAT16 ");
|
||||
}
|
||||
if(!makeFat16(dataStart, card, cache, numberOfHeads, sectorsPerTrack, cardSizeBlocks, sectorsPerCluster, relSector, partType, fatSize, fatStart, output)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (output) {
|
||||
output->printMSG("Formating FAT32 ", false);
|
||||
}
|
||||
if(!makeFat32(dataStart, card, cache, numberOfHeads, sectorsPerTrack, cardSizeBlocks, sectorsPerCluster, relSector, partType, fatSize, fatStart, output)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (output) {
|
||||
output->printLN("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ESP_SD::format(ESP3DOutput * output)
|
||||
{
|
||||
if (ESP_SD::getState(true) == ESP_SDCARD_IDLE) {
|
||||
Sd2Card card;
|
||||
uint32_t cardSizeBlocks;
|
||||
uint32_t cardCapacityMB;
|
||||
// cache for SD block
|
||||
cache_t cache;
|
||||
|
||||
// MBR information
|
||||
uint8_t partType;
|
||||
uint32_t relSector;
|
||||
|
||||
// FAT parameters
|
||||
uint32_t fatStart;
|
||||
uint32_t fatSize;
|
||||
uint32_t dataStart;
|
||||
if (!card.begin((ESP_SD_CS_PIN == -1)?SS:ESP_SD_CS_PIN, SD_SCK_HZ(F_CPU/_spi_speed_divider))) {
|
||||
return false;
|
||||
}
|
||||
cardSizeBlocks = card.cardSize();
|
||||
if (cardSizeBlocks == 0) {
|
||||
return false;
|
||||
}
|
||||
cardCapacityMB = (cardSizeBlocks + 2047)/2048;
|
||||
if (output) {
|
||||
String s = "Capacity detected :" + String((1.048576*cardCapacityMB)/1024) + "GB";
|
||||
output->printMSG(s.c_str());
|
||||
}
|
||||
if (!eraseCard(card, cache, cardSizeBlocks, output)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!formatCard(dataStart, card, cache, cardSizeBlocks,
|
||||
relSector, partType,
|
||||
fatSize, fatStart, cardCapacityMB,output)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_SDFile ESP_SD::open(const char* path, uint8_t mode)
|
||||
{
|
||||
//do some check
|
||||
if(((strcmp(path,"/") == 0) && ((mode == ESP_FILE_WRITE) || (mode == ESP_FILE_APPEND))) || (strlen(path) == 0)) {
|
||||
_sizechanged = true;
|
||||
return ESP_SDFile();
|
||||
}
|
||||
// path must start by '/'
|
||||
if (path[0] != '/') {
|
||||
return ESP_SDFile();
|
||||
}
|
||||
if (mode != ESP_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();
|
||||
}
|
||||
}
|
||||
sdfat::File tmp = SD.open(path, (mode == ESP_FILE_READ)?FILE_READ:(mode == ESP_FILE_WRITE)?FILE_WRITE:FILE_WRITE);
|
||||
ESP_SDFile esptmp(&tmp, tmp.isDir(),(mode == ESP_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;
|
||||
}
|
||||
log_esp3d("%s exists ?", path);
|
||||
res = SD.exists(path);
|
||||
if (!res) {
|
||||
log_esp3d("Seems not - trying open it");
|
||||
ESP_SDFile root = ESP_SD::open(path, ESP_FILE_READ);
|
||||
if (root) {
|
||||
res = root.isDirectory();
|
||||
}
|
||||
}
|
||||
log_esp3d("Seems %s", res?"yes":"no");
|
||||
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;
|
||||
std::stack <String > pathlist;
|
||||
String p = path;
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
sdfat::File dir = SD.open(pathlist.top().c_str());
|
||||
dir.rewindDirectory();
|
||||
sdfat::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 = sdfat::File();
|
||||
} else {
|
||||
char tmp[255];
|
||||
f.getName(tmp,254);
|
||||
_sizechanged = true;
|
||||
SD.remove(tmp);
|
||||
f.close();
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
if (candelete) {
|
||||
if (pathlist.top() !="/") {
|
||||
res = SD.rmdir(pathlist.top().c_str());
|
||||
}
|
||||
pathlist.pop();
|
||||
}
|
||||
dir.close();
|
||||
}
|
||||
p = String();
|
||||
log_esp3d("count %d", pathlist.size());
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ESP_SDFile::seek(uint32_t pos, uint8_t mode)
|
||||
{
|
||||
if (mode == SeekCur) {
|
||||
return tSDFile_handle[_index].seekCur(pos);
|
||||
}
|
||||
if (mode == SeekEnd) {
|
||||
return tSDFile_handle[_index].seekEnd(pos);
|
||||
}
|
||||
// if (mode == SeekSet)
|
||||
return tSDFile_handle[_index].seekSet(pos);
|
||||
}
|
||||
|
||||
void ESP_SD::closeAll()
|
||||
{
|
||||
for (uint8_t i = 0; i < ESP_MAX_SD_OPENHANDLE; i++) {
|
||||
tSDFile_handle[i].close();
|
||||
tSDFile_handle[i] = sdfat::File();
|
||||
}
|
||||
}
|
||||
|
||||
ESP_SDFile::ESP_SDFile(void* handle, bool isdir, bool iswritemode, const char * path)
|
||||
{
|
||||
_isdir = isdir;
|
||||
_dirlist = "";
|
||||
_index = -1;
|
||||
_filename = "";
|
||||
_name = "";
|
||||
_lastwrite = 0;
|
||||
_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] = *((sdfat::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);
|
||||
_isdir = true;
|
||||
}
|
||||
if (_name[0] == '/') {
|
||||
_name.remove( 0, 1);
|
||||
}
|
||||
int pos = _name.lastIndexOf('/');
|
||||
if (pos != -1) {
|
||||
_name.remove( 0, pos+1);
|
||||
}
|
||||
if (_name.length() == 0) {
|
||||
_name = "/";
|
||||
}
|
||||
//size
|
||||
_size = tSDFile_handle[i].size();
|
||||
//time
|
||||
if (!_isdir) {
|
||||
_lastwrite = getDateTimeFile(tSDFile_handle[i]);
|
||||
|
||||
} else {
|
||||
//no need date time for directory
|
||||
_lastwrite = 0;
|
||||
}
|
||||
_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];
|
||||
sdfat::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) {
|
||||
sdfat::File ftmp = SD.open(_filename.c_str());
|
||||
if (ftmp) {
|
||||
_size = ftmp.size();
|
||||
_lastwrite = getDateTimeFile(ftmp);
|
||||
ftmp.close();
|
||||
}
|
||||
}
|
||||
tSDFile_handle[_index] = sdfat::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();
|
||||
}
|
||||
sdfat::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 - " SD_FAT_VERSION_STR ;
|
||||
}
|
||||
|
||||
#endif //SD_DEVICE == ESP_SDFAT
|
||||
#endif //ARCH_ESP8266 && SD_DEVICE
|
||||
@@ -243,20 +243,25 @@ bool ESP_SD::rmdir(const char *path)
|
||||
p.remove( p.length() - 1,1);
|
||||
}
|
||||
pathlist.push(p);
|
||||
while (pathlist.size() > 0) {
|
||||
while (pathlist.size() > 0 && res) {
|
||||
File dir = SD_MMC.open(pathlist.top().c_str());
|
||||
File f = dir.openNextFile();
|
||||
bool candelete = true;
|
||||
while (f) {
|
||||
while (f && res) {
|
||||
if (f.isDirectory()) {
|
||||
candelete = false;
|
||||
String newdir = f.name();
|
||||
String newdir = pathlist.top()+ '/';
|
||||
newdir+= f.name();
|
||||
pathlist.push(newdir);
|
||||
f.close();
|
||||
f = File();
|
||||
} else {
|
||||
SD_MMC.remove(f.name());
|
||||
String filepath = pathlist.top()+ '/';
|
||||
filepath+= f.name();
|
||||
f.close();
|
||||
if (!SD_MMC.remove(filepath.c_str())) {
|
||||
res = false;
|
||||
}
|
||||
f = dir.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ void HTTP_Server::SDFileupload ()
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pos+1 >= path.length()-1) {
|
||||
if ((size_t)(pos+1) >= path.length()-1) {
|
||||
pos=-1;
|
||||
break;
|
||||
} else {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# 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
|
||||
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
@@ -1,17 +0,0 @@
|
||||
This is a downstream version of Bill Greiman's <fat16lib@sbcglobal.net>
|
||||
"SdFat" library for Arduino.
|
||||
|
||||
No code changes were intended to be made. Only a custom namespace called
|
||||
"sdfat" was wrapped around all objects and structures in the library.
|
||||
This namespace is required because the ESP8266 already has global class
|
||||
types whose names conflict with SdFat's built-in ones (File, others).
|
||||
|
||||
"using namespace sdfat;" at the top of a sketch should make it work as-is,
|
||||
and all examples have been so updated.
|
||||
|
||||
By performing this wrapping I hope to be able to integrate the latest
|
||||
updates from upstream and pull them into an ESP8266 compatible FS (like
|
||||
SPIFFS or LittleFS) to make it much more useful and work much better with
|
||||
the ESP8266 Arduino ecosystem.
|
||||
|
||||
-Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
@@ -1,42 +0,0 @@
|
||||
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
|
||||
@@ -1,199 +0,0 @@
|
||||
// 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
#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();
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Program to test Short File Name character case flags.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {}
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,31 +0,0 @@
|
||||
// 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* This program is a simple Print benchmark.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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"));
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Program to compare size of Arduino SD library with SdFat.
|
||||
* See SdFatSize.ino for SdFat program.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Program to compare size of SdFat with Arduino SD library.
|
||||
* See SD_Size.ino for Arduino SD program.
|
||||
*
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Simple demo of the Stream parsInt() member function.
|
||||
#include <SPI.h>
|
||||
// The next two lines replace #include <SD.h>.
|
||||
#include "SdFat.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Calculate the sum and average of a list of floating point numbers
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* This program is a simple binary write/read benchmark
|
||||
* for the standard Arduino SD.h library.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Use of ibufsteam to parse a line and obufstream to format a line
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Demo of ArduinoInStream and ArduinoOutStream
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Append a line to a file - demo of pathnames and streams
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,113 +0,0 @@
|
||||
// Demo of rewriting a line read by fgets
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Read the logfile created by the eventlog.ino example.
|
||||
* Demo of pathnames and working directories
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,34 +0,0 @@
|
||||
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.
|
||||
@@ -1,39 +0,0 @@
|
||||
#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
|
||||
@@ -1,829 +0,0 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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__
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Example use of chdir(), ls(), mkdir(), and rmdir().
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,104 +0,0 @@
|
||||
// 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
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.
|
||||
@@ -1 +0,0 @@
|
||||
LFN,NAME.TXT is not 8.3 since it has a comma.
|
||||
@@ -1,5 +0,0 @@
|
||||
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.
|
||||
@@ -1,2 +0,0 @@
|
||||
Not_8_3.txt has a Long File Name
|
||||
since the basename is mixed case.
|
||||
@@ -1 +0,0 @@
|
||||
OK%83.TXT is a valid 8.3 name.
|
||||
@@ -1 +0,0 @@
|
||||
STD_8_3.TXT - a vanilla 8.3 name.
|
||||
@@ -1,2 +0,0 @@
|
||||
With Blank.txt
|
||||
Just another example of a Long File Name.
|
||||
@@ -1,2 +0,0 @@
|
||||
"With Two.dots.txt"
|
||||
Lots of reasons this is a Long File Name.
|
||||
@@ -1,5 +0,0 @@
|
||||
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.
|
||||
@@ -1,5 +0,0 @@
|
||||
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.
|
||||
@@ -1,658 +0,0 @@
|
||||
/**
|
||||
* 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"
|
||||
sdfat::MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
//==============================================================================
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#include "UserTypes.h"
|
||||
// User data functions. Modify these functions for your data items.
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#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
|
||||
@@ -1,658 +0,0 @@
|
||||
/**
|
||||
* 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"
|
||||
sdfat::MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
//==============================================================================
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
// Empty file with name LowLatencyLoggerADXL345.ino to make IDE happy.
|
||||
@@ -1,72 +0,0 @@
|
||||
#include "UserTypes.h"
|
||||
// User data functions. Modify these functions for your data items.
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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"));
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#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
|
||||
@@ -1 +0,0 @@
|
||||
Test of shared SPI for LowLatencyLogger.
|
||||
@@ -1,658 +0,0 @@
|
||||
/**
|
||||
* 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"
|
||||
sdfat::MinimumSerial MinSerial;
|
||||
#define Serial MinSerial
|
||||
#endif // __AVR_ATmega328P__
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
//==============================================================================
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
// Empty file with name LowLatencyLoggerMPU6050.ino to make IDE happy.
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// User data functions. Modify these functions for your data items.
|
||||
#include "UserTypes.h"
|
||||
#include "Wire.h"
|
||||
#include "I2Cdev.h"
|
||||
#include "MPU6050.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
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"));
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#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
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Print size, modify date/time, and name for all files in root.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* This program is a simple Print benchmark.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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");
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
// Quick hardware test for SPI card access.
|
||||
//
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
|
||||
// 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>
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
// 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
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,554 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,250 +0,0 @@
|
||||
/*
|
||||
* This program attempts to initialize an SD card and analyze its structure.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
#if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h
|
||||
//
|
||||
// Pin numbers in templates must be constants.
|
||||
const uint8_t SOFT_MISO_PIN = 12;
|
||||
const uint8_t SOFT_MOSI_PIN = 11;
|
||||
const uint8_t SOFT_SCK_PIN = 13;
|
||||
//
|
||||
// Chip select may be constant or RAM variable.
|
||||
const uint8_t SD_CHIP_SELECT_PIN = 10;
|
||||
|
||||
// SdFat software SPI template
|
||||
SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;
|
||||
|
||||
// Test file.
|
||||
SdFile file;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
// Wait for USB Serial
|
||||
while (!Serial) {
|
||||
SysCall::yield();
|
||||
}
|
||||
Serial.println("Type any character to start");
|
||||
while (!Serial.available()) {
|
||||
SysCall::yield();
|
||||
}
|
||||
|
||||
if (!sd.begin(SD_CHIP_SELECT_PIN)) {
|
||||
sd.initErrorHalt();
|
||||
}
|
||||
|
||||
if (!file.open("SoftSPI.txt", O_RDWR | O_CREAT)) {
|
||||
sd.errorHalt(F("open failed"));
|
||||
}
|
||||
file.println(F("This line was printed using software SPI."));
|
||||
|
||||
file.rewind();
|
||||
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
Serial.println(F("Done."));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void loop() {}
|
||||
#else // ENABLE_SOFTWARE_SPI_CLASS
|
||||
#error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h
|
||||
#endif //ENABLE_SOFTWARE_SPI_CLASS
|
||||
@@ -1,216 +0,0 @@
|
||||
// Benchmark comparing SdFile and StdioStream.
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,171 +0,0 @@
|
||||
// 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* This program tests the dateTimeCallback() function
|
||||
* and the timestamp() function.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* This program demonstrates the freeClusterCount() call.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
/*
|
||||
* 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() {}
|
||||
@@ -1,224 +0,0 @@
|
||||
/*
|
||||
* This program is a simple binary write/read benchmark.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
#include "FreeStack.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Simple data logger.
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Demo of fgets function to read lines from a file.
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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) {}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Print a table with various formatting options
|
||||
* Format dates
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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) {}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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) {}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* This program demonstrates use of SdFile::rename()
|
||||
* and SdFat::rename().
|
||||
*/
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
#include "sdios.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
// 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() {}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Example to wipe all data from an already formatted SD.
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
using namespace sdfat;
|
||||
|
||||
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() {
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 87 KiB |
Binary file not shown.
Binary file not shown.
@@ -1,98 +0,0 @@
|
||||
Static Tests of the Arduino Internal ADC.
|
||||
|
||||
Several people have asked about the DC accuracy of the Arduino ADC when used in my data logging applications at slow sample rates.
|
||||
|
||||
Here are my results of some "hobby level" measurements of the Arduino ADC.
|
||||
|
||||
One question is how important is the ADC clock rate. I did measurents for an ADC clock rate of 125 kHz to 2MHz.
|
||||
|
||||
Another question is how much does Noise Reduction Mode help. I did a series of measurements using this mode.
|
||||
|
||||
Noise Reduction Mode only reduced the mean absolute error slightly.
|
||||
|
||||
I do calibration to remove Offset Error and Gain Error. Calibration is very important for good accuracy.
|
||||
|
||||
These tests depend on the Arduino voltage regulator providing a stable voltage during the tests. The Arduino ADC reference voltage is Vcc for these tests. This may not be realistic for practical applications
|
||||
|
||||
Integral Non-linearity (INL) is the main remaining source of error.
|
||||
|
||||
Here are my results for static (DC) tests of the internal ADC for three UNOs.
|
||||
|
||||
The Arduinos are powered by a high quality nine volt power supply.
|
||||
|
||||
These tests measure a DC level so do not include problems due to time jitter, S/H time, and other dynamic errors.
|
||||
There are several studies of the dynamic behavior of the Arduino ADC that determine ENOB (Effective Number Of Bits).
|
||||
|
||||
I used a shield with a 12-bit MCP4921 DAC to generate voltage levels. This ADC has an output buffer so it provides a very low impedance source.
|
||||
|
||||
I measured the voltage of the DAC with a calibrated 18-bit MCP3422 ADC on the shield.
|
||||
|
||||
I used DAC levels from 20 to 4075 to avoid zero offset errors at low voltages and DAC buffer problems at high voltages.
|
||||
|
||||
Each series of measurements has 4056 data points.
|
||||
|
||||
This is a voltage range of about 0.023 to 4.972 volts.
|
||||
|
||||
I calibrated the Arduino ADC for each series of measurements with a linear fit of the form.
|
||||
|
||||
v = a + b*adcValue
|
||||
|
||||
Errors are the difference between the value measured with the 18-bit ADC and the calibrated value measured with the AVR ADC.
|
||||
|
||||
I also show the results for no calibration, the NoCal column, using the datasheet formula.
|
||||
|
||||
Vin = Vref*adcValue/1024
|
||||
|
||||
|
||||
The rows in the tables tables are.
|
||||
|
||||
Min - minimum error in millivolts
|
||||
|
||||
Max - maximum error in millivolts
|
||||
|
||||
MAE - mean absolute error in millivolts
|
||||
|
||||
|
||||
The columns in the tables are:
|
||||
|
||||
Ideal - results for a perfect 10-bit ADC for comparison.
|
||||
|
||||
NoCal - datasheet formula (5/1024)*adcValue with Noise Reduction Mode.
|
||||
|
||||
NR128 - Noise Reduction mode with Prescaler of 128 (ADC clock of 125 kHz).
|
||||
|
||||
PS128 - analogRead with Prescaler of 128 (ADC clock of 125 kHz).
|
||||
|
||||
PS64 - analogRead with Prescaler of 64 (ADC clock of 250 kHz).
|
||||
|
||||
PS32 - analogRead with Prescaler of 32 (ADC clock of 500 kHz).
|
||||
|
||||
PS16 - analogRead with Prescaler of 16 (ADC clock of 1 MHz).
|
||||
|
||||
PS8 - analogRead with Prescaler of 8 (ADC clock of 2 MHz).
|
||||
|
||||
|
||||
Results for three UNO Arduinos
|
||||
|
||||
First Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -2.43 -3.72 -4.01 -3.88 -4.53 -6.57 -27.18
|
||||
Max 2.44 11.69 3.74 4.24 4.15 5.17 8.69 23.21
|
||||
MAE 1.22 5.02 1.33 1.38 1.37 1.44 1.96 4.11
|
||||
|
||||
Second Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -9.24 -4.87 -4.86 -5.05 -5.34 -6.52 -24.04
|
||||
Max 2.44 11.62 3.95 4.64 4.69 5.71 8.41 21.29
|
||||
MAE 1.22 5.33 1.41 1.43 1.44 1.53 2.02 4.05
|
||||
|
||||
Third Arduino - Error Millivolts
|
||||
|
||||
Ideal NoCal NR128 PS128 PS64 PS32 PS16 PS8
|
||||
Min -2.44 -7.88 -4.12 -4.40 -4.32 -4.41 -6.97 -26.93
|
||||
Max 2.44 12.53 3.80 4.04 4.18 5.27 8.84 24.59
|
||||
MAE 1.22 4.85 1.29 1.33 1.34 1.42 1.91 4.10
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
@@ -1,21 +0,0 @@
|
||||
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
|
||||
@@ -1,39 +0,0 @@
|
||||
#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
|
||||
@@ -1,82 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
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.
|
||||
@@ -1,403 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -1,10 +0,0 @@
|
||||
<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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user