Compare commits
59 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8313193820 | ||
|
|
e72d81e8aa | ||
|
|
b11fcfdc59 | ||
|
|
7ec6995235 | ||
|
|
4537faa145 | ||
|
|
fb0d11a405 | ||
|
|
15c39defe5 | ||
|
|
3e298145ce | ||
|
|
5d752a3901 | ||
|
|
cf2a10a274 | ||
|
|
df91320ee6 | ||
|
|
476f8d979a | ||
|
|
8190df112f | ||
|
|
3f952664eb | ||
|
|
0f512b0155 | ||
|
|
ca1cf4e0e1 | ||
|
|
a13979b5ca | ||
|
|
4f3f65cb31 | ||
|
|
d3cbd6511c | ||
|
|
659033f387 | ||
|
|
367cde634d | ||
|
|
68dd2d4d80 | ||
|
|
54ca0c7012 | ||
|
|
850e1aaee1 | ||
|
|
b2a36980bb | ||
|
|
fa76619c03 | ||
|
|
56d6610432 | ||
|
|
cf8f5d8f61 | ||
|
|
28fd5dc4fc | ||
|
|
641f23b353 | ||
|
|
5d726947af | ||
|
|
94aad3094c | ||
|
|
f1f0f93432 | ||
|
|
d0a2565114 | ||
|
|
e157117f9c | ||
|
|
6324f8eed2 | ||
|
|
fca3f1475f | ||
|
|
1b0c9bce15 | ||
|
|
0b0f1296d3 | ||
|
|
81d38972f6 | ||
|
|
cb28106207 | ||
|
|
5de302b19a | ||
|
|
699c9e410e | ||
|
|
18f071dc69 | ||
|
|
31783e24e8 | ||
|
|
013b7ea281 | ||
|
|
829f4925f4 | ||
|
|
88902b576e | ||
|
|
f918ed0077 | ||
|
|
10cdd7964c | ||
|
|
ec247da5da | ||
|
|
f24364bff2 | ||
|
|
9c046af871 | ||
|
|
2b053a1910 | ||
|
|
7a8686d63d | ||
|
|
4d87dfbee3 | ||
|
|
391949068e | ||
|
|
98a88f0389 | ||
|
|
e0acc32b59 |
894 changed files with 126769 additions and 32630 deletions
25
.github/workflows/githubci.yml
vendored
25
.github/workflows/githubci.yml
vendored
|
|
@ -8,26 +8,22 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arduino-platform:
|
arduino-platform:
|
||||||
- 'feather_esp32s2'
|
|
||||||
- 'feather_esp32s3'
|
|
||||||
- 'feather_rp2040'
|
- 'feather_rp2040'
|
||||||
- 'metro_rp2040'
|
|
||||||
- 'metro_m0'
|
- 'metro_m0'
|
||||||
- 'metro_m4'
|
- 'metro_m4'
|
||||||
- 'nrf52840'
|
- 'nrf52840'
|
||||||
|
- 'metroesp32s2'
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v1
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Checkout adafruit/ci-arduino
|
- name: Checkout adafruit/ci-arduino
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: adafruit/ci-arduino
|
repository: adafruit/ci-arduino
|
||||||
path: ci
|
path: ci
|
||||||
|
|
@ -35,8 +31,15 @@ jobs:
|
||||||
- name: pre-install
|
- name: pre-install
|
||||||
run: bash ci/actions_install.sh
|
run: bash ci/actions_install.sh
|
||||||
|
|
||||||
- name: Install Libraries for building examples
|
|
||||||
run: arduino-cli lib install RTClib SD
|
|
||||||
|
|
||||||
- name: test platforms
|
- name: test platforms
|
||||||
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}
|
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}
|
||||||
|
|
||||||
|
#- name: clang
|
||||||
|
# skip clang for fatfs (ff) to make it easier to compare and upgrade
|
||||||
|
# run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -e "./examples/SdFat_format/*" -r .
|
||||||
|
|
||||||
|
#- name: doxygen
|
||||||
|
# env:
|
||||||
|
# GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }}
|
||||||
|
# PRETTYNAME : "Adafruit SPIFlash Library"
|
||||||
|
# run: bash ci/doxy_gen_and_deploy.sh
|
||||||
|
|
|
||||||
223
.gitignore
vendored
223
.gitignore
vendored
|
|
@ -1,3 +1,174 @@
|
||||||
|
#################
|
||||||
|
## Eclipse
|
||||||
|
#################
|
||||||
|
|
||||||
|
*.pydevproject
|
||||||
|
.project
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# CDT-specific
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# PDT-specific
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
|
||||||
|
#################
|
||||||
|
## Visual Studio
|
||||||
|
#################
|
||||||
|
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
x64/
|
||||||
|
build/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.log
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
*.ncrunch*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.Publish.xml
|
||||||
|
*.pubxml
|
||||||
|
|
||||||
|
# NuGet Packages Directory
|
||||||
|
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||||
|
#packages/
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
sql/
|
||||||
|
*.Cache
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file to a newer
|
||||||
|
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
App_Data/*.mdf
|
||||||
|
App_Data/*.ldf
|
||||||
|
|
||||||
|
#############
|
||||||
|
## Windows detritus
|
||||||
|
#############
|
||||||
|
|
||||||
# Windows image file caches
|
# Windows image file caches
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
ehthumbs.db
|
ehthumbs.db
|
||||||
|
|
@ -8,29 +179,37 @@ Desktop.ini
|
||||||
# Recycle Bin used on file shares
|
# Recycle Bin used on file shares
|
||||||
$RECYCLE.BIN/
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
# Windows Installer files
|
# Mac crap
|
||||||
*.cab
|
|
||||||
*.msi
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# Operating System Files
|
|
||||||
# =========================
|
|
||||||
|
|
||||||
# OSX
|
|
||||||
# =========================
|
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
|
|
||||||
# Icon must ends with two \r.
|
|
||||||
Icon
|
|
||||||
|
|
||||||
# Thumbnails
|
#############
|
||||||
._*
|
## Python
|
||||||
|
#############
|
||||||
|
|
||||||
# Files that might appear on external disk
|
*.py[co]
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
# Packages
|
||||||
|
*.egg
|
||||||
|
*.egg-info
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
eggs/
|
||||||
|
parts/
|
||||||
|
var/
|
||||||
|
sdist/
|
||||||
|
develop-eggs/
|
||||||
|
.installed.cfg
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
.coverage
|
||||||
|
.tox
|
||||||
|
|
||||||
|
#Translations
|
||||||
|
*.mo
|
||||||
|
|
||||||
|
#Mr Developer
|
||||||
|
.mr.developer.cfg
|
||||||
|
|
|
||||||
31
.travis.yml
Normal file
31
.travis.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
language: c
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- ~/arduino_ide
|
||||||
|
- ~/.arduino15/packages/
|
||||||
|
|
||||||
|
git:
|
||||||
|
depth: false
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- PRETTYNAME="SdFat (Adafruit Fork)"
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh)
|
||||||
|
|
||||||
|
install:
|
||||||
|
- git clone --quiet http://github.com/jrowberg/i2cdevlib.git $HOME/i2cdevlib
|
||||||
|
- mv $HOME/i2cdevlib/Arduino/* $HOME/Arduino/libraries/
|
||||||
|
- rm -rf $HOME/arduino_ide/libraries/Adafruit_Test_Library/examples/*attic
|
||||||
|
- rm -rf $HOME/arduino_ide/libraries/Adafruit_Test_Library/extras
|
||||||
|
|
||||||
|
script:
|
||||||
|
- build_main_platforms
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh)
|
||||||
|
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh)
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2011..2020 Bill Greiman
|
Copyright (c) 2011..2017 Bill Greiman
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
179
README.md
179
README.md
|
|
@ -1,171 +1,42 @@
|
||||||
### Warning: This version has major internal changes.
|
The Arduino SdFat library provides read/write access to FAT16/FAT32
|
||||||
|
file systems on SD/SDHC flash cards.
|
||||||
|
|
||||||
SdFat version 2.3.0 has major changes to implement RP2040/RP2350 SDIO.
|
SdFat requires Arduino 1.6x or greater.
|
||||||
|
|
||||||
In addition there are number of bug fixes.
|
Key changes:
|
||||||
|
|
||||||
Begin by running the Rp2040SdioSetup example to try RP2040/RP2350 SDIO.
|
Support for multiple SPI ports now uses a pointer to a SPIClass object.
|
||||||
|
|
||||||
This example requires a SDIO Card socket with the following six lines.
|
See the STM32Test example.
|
||||||
|
|
||||||
* CLK - A clock signal sent to the card by the MCU.
|
|
||||||
* CMD - A bidirectional line for for commands and responses.
|
|
||||||
* DAT[0:3] - Four bidirectional lines for data transfer.
|
|
||||||
CLK and CMD can be connected to any GPIO pins. DAT[0:3] can be connected
|
|
||||||
to any four consecutive GPIO pins in the order DAT0, DAT1, DAT2, DAT3.
|
|
||||||
|
|
||||||
Here is an example of SDIO for Pico using an Adafruit socket, PiCowbell
|
|
||||||
Proto and PiCowbell Proto Doubler.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
This Socket supports SDIO with:
|
|
||||||
```
|
```
|
||||||
#define RP_CLK_GPIO 10
|
explicit SdFat(SPIClass* spiPort);
|
||||||
#define RP_CMD_GPIO 11
|
\\ or
|
||||||
#define RP_DAT0_GPIO 12 // DAT1: GPIO13 DAT2: GPIO14, DAT3: GPIO15.
|
explicit SdFatEX(SPIClass* spiPort);
|
||||||
```
|
|
||||||
It also can be used on SPI1 with:
|
|
||||||
```
|
|
||||||
const uint8_t SD_CS_PIN = 15;
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK, &SPI1)
|
|
||||||
|
|
||||||
// In setup
|
|
||||||
SPI1.setSCK(10);
|
|
||||||
SPI1.setTX(11);
|
|
||||||
SPI1.setRX(12);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This setup gets the following result in the bench example using SDIO.
|
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.
|
||||||
|
|
||||||
<pre>
|
Open flags for Particle Gen3 and ARM boards are now defined by fcntl.h.
|
||||||
FILE_SIZE_MB = 5
|
See SdFatConfig.h for options.
|
||||||
BUF_SIZE = 512 bytes
|
|
||||||
Starting write test, please wait.
|
|
||||||
|
|
||||||
write speed and latency
|
Support for particle Gen3 devices.
|
||||||
speed,max,min,avg
|
|
||||||
KB/Sec,usec,usec,usec
|
|
||||||
15014.05,1165,32,32
|
|
||||||
15289.54,1249,32,32
|
|
||||||
|
|
||||||
Starting read test, please wait.
|
New cluster allocation algorithm.
|
||||||
|
|
||||||
read speed and latency
|
Please read changes.txt and the html documentation in the extras folder for more information.
|
||||||
speed,max,min,avg
|
|
||||||
KB/Sec,usec,usec,usec
|
|
||||||
15624.00,58,32,32
|
|
||||||
15624.00,51,32,32
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
|
Please report problems as issues.
|
||||||
File copy constructors and file assignment operators have been made private by
|
|
||||||
default in 2.2.3 to prevent call by value and multiple copies of file instances.
|
|
||||||
|
|
||||||
SdFatConfig.h has options to make file constructors and assignment operators
|
|
||||||
public.
|
|
||||||
|
|
||||||
UTF-8 encoded filenames are supported in v2.1.0 or later.
|
|
||||||
|
|
||||||
Try the UnicodeFilenames example. Here is output from ls:
|
|
||||||
<pre>
|
|
||||||
Type any character to begin
|
|
||||||
ls:
|
|
||||||
0 😀/
|
|
||||||
20 россиянин
|
|
||||||
17 très élégant
|
|
||||||
9 狗.txt
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
SdFat Version 2 supports FAT16/FAT32 and exFAT SD cards. It is mostly
|
|
||||||
backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
|
|
||||||
|
|
||||||
exFAT supports files larger than 4GB so files sizes and positions are
|
|
||||||
type uint64_t for classes that support exFAT.
|
|
||||||
|
|
||||||
exFAT has many features not available in FAT16/FAT32. exFAT has excellent
|
|
||||||
support for contiguous files on flash devices and supports preallocation.
|
|
||||||
|
|
||||||
If the SD card is the only SPI device, use dedicated SPI mode. This can
|
|
||||||
greatly improve performance. See the bench example.
|
|
||||||
|
|
||||||
Here is write performance for an old, 2011, card on a Due board.
|
|
||||||
```
|
|
||||||
Shared SPI:
|
|
||||||
write speed and latency
|
|
||||||
speed,max,min,avg
|
|
||||||
KB/Sec,usec,usec,usec
|
|
||||||
294.45,24944,1398,1737
|
|
||||||
|
|
||||||
Dedicated SPI:
|
|
||||||
write speed and latency
|
|
||||||
speed,max,min,avg
|
|
||||||
KB/Sec,usec,usec,usec
|
|
||||||
3965.11,16733,110,127
|
|
||||||
```
|
|
||||||
The default version of SdFatConfig.h enables support for dedicated SPI and
|
|
||||||
optimized access to contiguous files. This makes SdFat Version 2 slightly
|
|
||||||
larger than Version 1. If these features are disabled, Version 2 is smaller
|
|
||||||
than Version 1.
|
|
||||||
|
|
||||||
The types for the classes SdFat and File are defined in SdFatConfig.h.
|
|
||||||
The default version of SdFatConfig.h defines SdFat to only support FAT16/FAT32.
|
|
||||||
SdFat and File are defined in terms of more basic classes by typedefs. You
|
|
||||||
can use these basic classes in applications.
|
|
||||||
|
|
||||||
Support for exFAT requires a substantial amount of flash. Here are sizes on
|
|
||||||
an UNO for a simple program that opens a file, prints one line, and closes
|
|
||||||
the file.
|
|
||||||
```
|
|
||||||
FAT16/FAT32 only: 9780 bytes flash, 875 bytes SRAM.
|
|
||||||
|
|
||||||
exFAT only: 13830 bytes flash, 938 bytes SRAM.
|
|
||||||
|
|
||||||
FAT16/FAT32/exFAT: 19326 bytes flash, 928 bytes SRAM.
|
|
||||||
```
|
|
||||||
The section below of SdFatConfig.h has been edited to uses FAT16/FAT32 for
|
|
||||||
small AVR boards and FAT16/FAT32/exFAT for all other boards.
|
|
||||||
```
|
|
||||||
/**
|
|
||||||
* File types for SdFat, File, SdFile, SdBaseFile, fstream,
|
|
||||||
* ifstream, and ofstream.
|
|
||||||
*
|
|
||||||
* Set SDFAT_FILE_TYPE to:
|
|
||||||
*
|
|
||||||
* 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
*/
|
|
||||||
#if defined(__AVR__) && FLASHEND < 0X8000
|
|
||||||
// FAT16/FAT32 for 32K AVR boards.
|
|
||||||
#define SDFAT_FILE_TYPE 1
|
|
||||||
#else // defined(__AVR__) && FLASHEND < 0X8000
|
|
||||||
// FAT16/FAT32 and exFAT for all other boards.
|
|
||||||
#define SDFAT_FILE_TYPE 3
|
|
||||||
#endif // defined(__AVR__) && FLASHEND < 0X8000
|
|
||||||
```
|
|
||||||
The SdBaseFile class has no Arduino Stream or Print support.
|
|
||||||
|
|
||||||
The File class is derived from Stream and SdBaseFile.
|
|
||||||
|
|
||||||
The SdFile class is derived from SdBaseFile and Print.
|
|
||||||
|
|
||||||
Please try the examples. Start with SdInfo, bench, and ExFatLogger.
|
|
||||||
|
|
||||||
To use SdFat Version 2, unzip the download file, rename the library folder
|
|
||||||
SdFat and place the SdFat folder into the libraries sub-folder in your main
|
|
||||||
sketch folder.
|
|
||||||
|
|
||||||
For more information see the Manual installation section of this guide:
|
|
||||||
|
|
||||||
http://arduino.cc/en/Guide/Libraries
|
|
||||||
|
|
||||||
A number of configuration options can be set by editing SdFatConfig.h
|
A number of configuration options can be set by editing SdFatConfig.h
|
||||||
define macros. See the html documentation File tab for details.
|
define macros. See the html documentation for details
|
||||||
|
|
||||||
Please read the html documentation for this library in SdFat/doc/SdFat.html.
|
Please read the html documentation for this library. Start with
|
||||||
Start with the Main Page. Next go to the Classes tab and read the
|
html/index.html and read the Main Page. Next go to the Classes tab and
|
||||||
documentation for the classes SdFat32, SdExFat, SdFs, File32, ExFile, FsFile.
|
read the documentation for the classes SdFat, SdFatEX, SdBaseFile,
|
||||||
|
SdFile, File, StdioStream, ifstream, ofstream, and others.
|
||||||
|
|
||||||
The SdFat and File classes are defined in terms of the above classes by
|
Please continue by reading the html documentation in SdFat/extras/html
|
||||||
typedefs. Edit SdFatConfig.h to select class options.
|
|
||||||
|
|
||||||
Please continue by reading the html documentation in the SdFat/doc folder.
|
Updated 28 Dec 2018
|
||||||
|
|
|
||||||
2896
doc/Doxyfile
2896
doc/Doxyfile
File diff suppressed because it is too large
Load diff
|
|
@ -1,51 +0,0 @@
|
||||||
2025-01-01
|
|
||||||
|
|
||||||
Run the SdErrorCode example to produce an updated list.
|
|
||||||
|
|
||||||
Code,Symbol - failed operation
|
|
||||||
0X00,SD_CARD_ERROR_NONE - No error
|
|
||||||
0X01,SD_CARD_ERROR_CMD0 - Card reset failed
|
|
||||||
0X02,SD_CARD_ERROR_CMD2 - SDIO read CID
|
|
||||||
0X03,SD_CARD_ERROR_CMD3 - SDIO publish RCA
|
|
||||||
0X04,SD_CARD_ERROR_CMD6 - Switch card function
|
|
||||||
0X05,SD_CARD_ERROR_CMD7 - SDIO card select
|
|
||||||
0X06,SD_CARD_ERROR_CMD8 - Send and check interface settings
|
|
||||||
0X07,SD_CARD_ERROR_CMD9 - Read CSD data
|
|
||||||
0X08,SD_CARD_ERROR_CMD10 - Read CID data
|
|
||||||
0X09,SD_CARD_ERROR_CMD12 - Stop multiple block transmission
|
|
||||||
0X0A,SD_CARD_ERROR_CMD13 - Read card status
|
|
||||||
0X0B,SD_CARD_ERROR_CMD17 - Read single block
|
|
||||||
0X0C,SD_CARD_ERROR_CMD18 - Read multiple blocks
|
|
||||||
0X0D,SD_CARD_ERROR_CMD24 - Write single block
|
|
||||||
0X0E,SD_CARD_ERROR_CMD25 - Write multiple blocks
|
|
||||||
0X0F,SD_CARD_ERROR_CMD32 - Set first erase block
|
|
||||||
0X10,SD_CARD_ERROR_CMD33 - Set last erase block
|
|
||||||
0X11,SD_CARD_ERROR_CMD38 - Erase selected blocks
|
|
||||||
0X12,SD_CARD_ERROR_CMD58 - Read OCR register
|
|
||||||
0X13,SD_CARD_ERROR_CMD59 - Set CRC mode
|
|
||||||
0X14,SD_CARD_ERROR_ACMD6 - Set SDIO bus width
|
|
||||||
0X15,SD_CARD_ERROR_ACMD13 - Read extended status
|
|
||||||
0X16,SD_CARD_ERROR_ACMD23 - Set pre-erased count
|
|
||||||
0X17,SD_CARD_ERROR_ACMD41 - Activate card initialization
|
|
||||||
0X18,SD_CARD_ERROR_ACMD51 - Read SCR data
|
|
||||||
0X19,SD_CARD_ERROR_READ_TOKEN - Bad read data token
|
|
||||||
0X1A,SD_CARD_ERROR_READ_CRC - Read CRC error
|
|
||||||
0X1B,SD_CARD_ERROR_READ_FIFO - SDIO fifo read timeout
|
|
||||||
0X1C,SD_CARD_ERROR_READ_REG - Read CID or CSD failed.
|
|
||||||
0X1D,SD_CARD_ERROR_READ_START - Bad readStart argument
|
|
||||||
0X1E,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
|
|
||||||
0X1F,SD_CARD_ERROR_STOP_TRAN - Multiple block stop failed
|
|
||||||
0X20,SD_CARD_ERROR_TRANSFER_COMPLETE - SDIO transfer complete
|
|
||||||
0X21,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
|
|
||||||
0X22,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
|
|
||||||
0X23,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
|
|
||||||
0X24,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
|
|
||||||
0X25,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
|
|
||||||
0X26,SD_CARD_ERROR_DMA - DMA transfer failed
|
|
||||||
0X27,SD_CARD_ERROR_ERASE - Card did not accept erase commands
|
|
||||||
0X28,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
|
|
||||||
0X29,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
|
|
||||||
0X2A,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
|
|
||||||
0X2B,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
|
|
||||||
0X2C,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
|
|
||||||
0X2D,SD_CARD_ERROR_UNKNOWN - Unknown error
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
<h3>Replace the content of the html folder by unzipping html.zip.</h3>
|
|
||||||
<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
|
|
||||||
<h3>This makes viewing changes on GitHub difficult.</h3>
|
|
||||||
<p> </p>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
del html\*.md5
|
|
||||||
del html\*.map
|
|
||||||
pause
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
rm html/*.*
|
|
||||||
rm html/search/*.*
|
|
||||||
pause
|
|
||||||
BIN
doc/html.zip
BIN
doc/html.zip
Binary file not shown.
|
|
@ -1,4 +0,0 @@
|
||||||
<h3>Replace the content of the html folder by unzipping html.zip.</h3>
|
|
||||||
<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
|
|
||||||
<h3>This makes viewing changes on GitHub difficult.</h3>
|
|
||||||
<p> </p>
|
|
||||||
289
doc/mainpage.h
289
doc/mainpage.h
|
|
@ -1,289 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2011-2024 Bill Greiman
|
|
||||||
* This file is part of the SdFat library for SD memory cards.
|
|
||||||
*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included
|
|
||||||
* in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
* DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
\mainpage Arduino %SdFat Library
|
|
||||||
|
|
||||||
\section Warn Warnings for SdFat V2
|
|
||||||
|
|
||||||
This is a major new version of SdFat. It is mostly
|
|
||||||
backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
|
|
||||||
|
|
||||||
You should edit SdFatConfig.h to select features. The default version of
|
|
||||||
SdFatConfig.h is suitable for UNO and other small AVR boards.
|
|
||||||
|
|
||||||
\section Intro Introduction
|
|
||||||
|
|
||||||
The Arduino %SdFat library supports FAT16, FAT32, and exFAT file systems
|
|
||||||
on Standard SD, SDHC, and SDXC cards.
|
|
||||||
|
|
||||||
In %SdFat version 1, SdFat and File are the main classes.
|
|
||||||
|
|
||||||
In %SdFat version 2, SdFat and File are defined by typedefs in terms of the
|
|
||||||
following classes.
|
|
||||||
|
|
||||||
The file system classes in the %SdFat library are SdFat32, SdExFat, and SdFs.
|
|
||||||
SdFat32 supports FAT16 and FAT32. SdExFat supports exFAT, SdFs supports
|
|
||||||
FAT16, FAT32, and exFAT.
|
|
||||||
|
|
||||||
The corresponding file classes are File32, ExFile, and FsFile.
|
|
||||||
|
|
||||||
The types for SdFat and File are defined in SdFatConfig.h. This version
|
|
||||||
uses FAT16/FAT32 for small AVR boards and FAT16/FAT32/exFAT for all other
|
|
||||||
boards.
|
|
||||||
|
|
||||||
\code{.cpp}
|
|
||||||
// File types for SdFat, File, SdFile, SdBaseFile, fstream,
|
|
||||||
// ifstream, and ofstream.
|
|
||||||
//
|
|
||||||
// Set SDFAT_FILE_TYPE to:
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
//
|
|
||||||
#if defined(__AVR__) && FLASHEND < 0X8000
|
|
||||||
// FAT16/FAT32 for 32K AVR boards.
|
|
||||||
#define SDFAT_FILE_TYPE 1
|
|
||||||
#else // defined(__AVR__) && FLASHEND < 0X8000
|
|
||||||
// FAT16/FAT32 and exFAT for all other boards.
|
|
||||||
#define SDFAT_FILE_TYPE 3
|
|
||||||
#endif // defined(__AVR__) && FLASHEND < 0X8000
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
It is possible to use option three, support or FAT16/FAT32 and exFat
|
|
||||||
on an Uno or other AVR board with 32KB flash and 2KB SRAM but memory
|
|
||||||
will be very limited.
|
|
||||||
|
|
||||||
Uno memory use for a simple data logger is:
|
|
||||||
|
|
||||||
> option 1, FAT16/FAT32, 11902 bytes of flash and 855 bytes of SRAM.
|
|
||||||
>
|
|
||||||
> option 2, exFAT, 14942 bytes of flash and 895 bytes of SRAM.
|
|
||||||
>
|
|
||||||
> option 3, FAT16/FAT32 and exFAT, 21834 bytes of flash and 908 bytes of SRAM.
|
|
||||||
|
|
||||||
Please read documentation under the above classes tab for more information.
|
|
||||||
|
|
||||||
A number of example are provided in the %SdFat/examples folder. These were
|
|
||||||
developed to test %SdFat and illustrate its use.
|
|
||||||
|
|
||||||
\section exFAT exFAT Features
|
|
||||||
|
|
||||||
exFAT has many features not available in FAT16/FAT32.
|
|
||||||
|
|
||||||
Files larger than 4GiB, 64-bit file size and file position.
|
|
||||||
|
|
||||||
Free space allocation performance improved by using a free space bitmap.
|
|
||||||
|
|
||||||
Removal of the physical "." and ".." directory entries that appear in
|
|
||||||
FAT16/FAT32 subdirectories.
|
|
||||||
|
|
||||||
Better support for large flash pages with boundary alignment offsets
|
|
||||||
for the FAT table and data region.
|
|
||||||
|
|
||||||
exFAT files have two separate 64-bit length fields. The DataLength
|
|
||||||
field indicate how much space is allocate to the file. The ValidDataLength
|
|
||||||
field indicates how much actual data has been written to the file.
|
|
||||||
|
|
||||||
An exFAT file can be contiguous with pre-allocate clusters and bypass the
|
|
||||||
use of the FAT table. In this case the contiguous flag is set in the
|
|
||||||
directory entry. This allows an entire file to be written as one large
|
|
||||||
multi-block write.
|
|
||||||
|
|
||||||
\section SDPath Paths and Working Directories
|
|
||||||
|
|
||||||
Relative paths in %SdFat are resolved in a manner similar to Windows.
|
|
||||||
|
|
||||||
Each instance of SdFat32, SdExFat, and SdFs has a current directory.
|
|
||||||
This directory is called the volume working directory, vwd.
|
|
||||||
Initially this directory is the root directory for the volume.
|
|
||||||
|
|
||||||
The volume working directory is changed by calling the chdir(path).
|
|
||||||
|
|
||||||
The call sd.chdir("/2014") will change the volume working directory
|
|
||||||
for sd to "/2014", assuming "/2014" exists.
|
|
||||||
|
|
||||||
Relative paths for member functions are resolved by starting at
|
|
||||||
the volume working directory.
|
|
||||||
|
|
||||||
For example, the call sd.mkdir("April") will create the directory
|
|
||||||
"/2014/April" assuming the volume working directory is "/2014".
|
|
||||||
|
|
||||||
There is current working directory, cwd, that is used to resolve paths
|
|
||||||
for file.open() calls.
|
|
||||||
|
|
||||||
For a single SD card, the current working directory is always the volume
|
|
||||||
working directory for that card.
|
|
||||||
|
|
||||||
For multiple SD cards the current working directory is set to the volume
|
|
||||||
working directory of a card by calling the chvol() member function.
|
|
||||||
The chvol() call is like the Windows \<drive letter>: command.
|
|
||||||
|
|
||||||
The call sd2.chvol() will set the current working directory to the volume
|
|
||||||
working directory for sd2.
|
|
||||||
|
|
||||||
If the volume working directory for sd2 is "/music" the call
|
|
||||||
|
|
||||||
file.open("BigBand.wav", O_READ);
|
|
||||||
|
|
||||||
will open "/music/BigBand.wav" on sd2.
|
|
||||||
|
|
||||||
\section Install Installation
|
|
||||||
|
|
||||||
You must manually install %SdFat by renaming the download folder %SdFat
|
|
||||||
and copy the %SdFat folder to the Arduino libraries folder in your
|
|
||||||
sketchbook folder.
|
|
||||||
|
|
||||||
It will be necessary to unzip and rename the folder if you download a zip
|
|
||||||
file from GitHub.
|
|
||||||
|
|
||||||
See the Manual installation section of this guide.
|
|
||||||
|
|
||||||
http://arduino.cc/en/Guide/Libraries
|
|
||||||
|
|
||||||
\section SDconfig SdFat Configuration
|
|
||||||
|
|
||||||
Several configuration options may be changed by editing the SdFatConfig.h
|
|
||||||
file in the %SdFat/src folder.
|
|
||||||
|
|
||||||
Here are a few of the key options.
|
|
||||||
|
|
||||||
If the symbol ENABLE_DEDICATED_SPI is nonzero, multi-block SD I/O may
|
|
||||||
be used for better performance. The SPI bus may not be shared with
|
|
||||||
other devices in this mode.
|
|
||||||
|
|
||||||
The symbol SPI_DRIVER_SELECT is used to select the SPI driver.
|
|
||||||
|
|
||||||
> If the symbol SPI_DRIVER_SELECT is:
|
|
||||||
>
|
|
||||||
> 0 - An optimized custom SPI driver is used if it exists
|
|
||||||
> else the standard library driver is used.
|
|
||||||
>
|
|
||||||
> 1 - The standard library driver is always used.
|
|
||||||
>
|
|
||||||
> 2 - The software SPI driver is always used.
|
|
||||||
>
|
|
||||||
> 3 - An external SPI driver derived from SdSpiBaseClass is always used.
|
|
||||||
|
|
||||||
To enable SD card CRC checking in SPI mode set USE_SD_CRC nonzero.
|
|
||||||
|
|
||||||
See SdFatConfig.h for other options.
|
|
||||||
|
|
||||||
\section Hardware Hardware Configuration
|
|
||||||
|
|
||||||
The hardware interface to the SD card should not use a resistor based level
|
|
||||||
shifter. Resistor based level shifters results in signal rise times that are
|
|
||||||
too slow for many newer SD cards.
|
|
||||||
|
|
||||||
|
|
||||||
\section HowTo How to format SD Cards
|
|
||||||
|
|
||||||
The best way to restore an SD card's format on a PC or Mac is to use
|
|
||||||
SDFormatter which can be downloaded from:
|
|
||||||
|
|
||||||
http://www.sdcard.org/downloads
|
|
||||||
|
|
||||||
A formatter program, SdFormatter.ino, is included in the
|
|
||||||
%SdFat/examples/SdFormatter directory. This program attempts to
|
|
||||||
emulate SD Association's SDFormatter.
|
|
||||||
|
|
||||||
SDFormatter aligns flash erase boundaries with file
|
|
||||||
system structures which reduces write latency and file system overhead.
|
|
||||||
|
|
||||||
The PC/Mac SDFormatter does not have an option for FAT type so it may format
|
|
||||||
very small cards as FAT12. Use the %SdFormatter example to force FAT16
|
|
||||||
formatting of small cards.
|
|
||||||
|
|
||||||
Do not format the SD card with an OS utility, OS utilities do not format SD
|
|
||||||
cards in conformance with the SD standard.
|
|
||||||
|
|
||||||
You should use a freshly formatted SD card for best performance. FAT
|
|
||||||
file systems become slower if many files have been created and deleted.
|
|
||||||
This is because the directory entry for a deleted file is marked as deleted,
|
|
||||||
but is not deleted. When a new file is created, these entries must be scanned
|
|
||||||
before creating the file. Also files can become
|
|
||||||
fragmented which causes reads and writes to be slower.
|
|
||||||
|
|
||||||
\section ExampleFiles Examples
|
|
||||||
|
|
||||||
A number of examples are provided in the SdFat/examples folder.
|
|
||||||
|
|
||||||
To access these examples from the Arduino development environment
|
|
||||||
go to: %File -> Examples -> %SdFat -> \<program Name\>
|
|
||||||
|
|
||||||
Compile, upload to your Arduino and click on Serial Monitor to run
|
|
||||||
the example.
|
|
||||||
|
|
||||||
Here is a list:
|
|
||||||
|
|
||||||
AvrAdcLogger - Fast AVR ADC logger using Timer/ADC interrupts.
|
|
||||||
|
|
||||||
BackwardCompatibility - Demonstrate SD.h compatibility with %SdFat.h.
|
|
||||||
|
|
||||||
bench - A read/write benchmark.
|
|
||||||
|
|
||||||
%BufferedPrint - Demo a buffered print class for AVR loggers.
|
|
||||||
|
|
||||||
debug folder - Some of my debug programs - will be remove in the future.
|
|
||||||
|
|
||||||
DirectoryFunctions - Use of chdir(), ls(), mkdir(), and rmdir().
|
|
||||||
|
|
||||||
examplesV1 folder - Examples from SdFat V1 for compatibility tests.
|
|
||||||
|
|
||||||
ExFatLogger - A data-logger optimized for exFAT features.
|
|
||||||
|
|
||||||
MinimumSizeSdReader - Example of small file reader for FAT16/FAT32.
|
|
||||||
|
|
||||||
OpenNext - Open all files in the root dir and print their filename.
|
|
||||||
|
|
||||||
QuickStart - Quick hardware test for SPI card access.
|
|
||||||
|
|
||||||
ReadCsvFile - Function to read a CSV text file one field at a time.
|
|
||||||
|
|
||||||
rename - demonstrates use of rename().
|
|
||||||
|
|
||||||
RtcTimestampTest - Demonstration of timestamps with RTClib.
|
|
||||||
|
|
||||||
SdErrorCodes - Produce a list of error codes.
|
|
||||||
|
|
||||||
SdFormatter - This program will format an SD, SDHC, or SDXC card.
|
|
||||||
|
|
||||||
SdInfo - Initialize an SD card and analyze its structure for trouble shooting.
|
|
||||||
|
|
||||||
SoftwareSpi - Demo of limited Software SPI support in SdFat V2.
|
|
||||||
|
|
||||||
STM32Test - Example use of two SPI ports on an STM32 board.
|
|
||||||
|
|
||||||
TeensyDmaAdcLogger - Fast logger using DMA ADC.
|
|
||||||
|
|
||||||
TeensyRtcTimestamp - %File timestamps for Teensy3.
|
|
||||||
|
|
||||||
TeensySdioDemo - Demo of SDIO and SPI modes for the Teensy 3.5/3.6 built-in SD.
|
|
||||||
|
|
||||||
TeensySdioLogger - Fast logger using a ring buffer.
|
|
||||||
|
|
||||||
UnicodeFilenames - Test program for Unicode file names.
|
|
||||||
|
|
||||||
UserChipSelectFunction - Useful for port expanders or replacement of the standard GPIO functions.
|
|
||||||
|
|
||||||
UserSPIDriver - An example of an external SPI driver.
|
|
||||||
*/
|
|
||||||
|
|
@ -69,7 +69,7 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial.
|
// Wait for USB Serial.
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// F() stores strings in flash to save RAM
|
// F() stores strings in flash to save RAM
|
||||||
cout << endl << F("FreeStack: ") << FreeStack() << endl;
|
cout << endl << F("FreeStack: ") << FreeStack() << endl;
|
||||||
|
|
@ -77,7 +77,7 @@ void setup() {
|
||||||
#if WAIT_TO_START
|
#if WAIT_TO_START
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// Discard input.
|
// Discard input.
|
||||||
do {
|
do {
|
||||||
|
|
@ -193,5 +193,5 @@ void loop() {
|
||||||
}
|
}
|
||||||
logfile.close();
|
logfile.close();
|
||||||
cout << F("Done!");
|
cout << F("Done!");
|
||||||
while (true) {}
|
SysCall::halt();
|
||||||
}
|
}
|
||||||
|
|
@ -20,11 +20,11 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.println("type any character to start");
|
Serial.println("type any character to start");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||||
Serial.println("begin failed");
|
Serial.println("begin failed");
|
||||||
|
|
@ -10,7 +10,7 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
delay(2000);
|
delay(2000);
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sd.begin()) {
|
if (!sd.begin()) {
|
||||||
|
|
@ -13,11 +13,11 @@ void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial.
|
// Wait for USB Serial.
|
||||||
while(!Serial) {
|
while(!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.println(F("Type any character to start"));
|
Serial.println(F("Type any character to start"));
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// Initialize the SD.
|
// Initialize the SD.
|
||||||
if (!SD.begin(csPin)) {
|
if (!SD.begin(csPin)) {
|
||||||
|
|
@ -29,12 +29,12 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// F() stores strings in flash to save RAM
|
// F() stores strings in flash to save RAM
|
||||||
cout << endl << F("Type any character to start\n");
|
cout << endl << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize at the highest speed supported by the board that is
|
// Initialize at the highest speed supported by the board that is
|
||||||
|
|
@ -58,12 +58,12 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// F() stores strings in flash to save RAM
|
// F() stores strings in flash to save RAM
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize at the highest speed supported by the board that is
|
// Initialize at the highest speed supported by the board that is
|
||||||
|
|
@ -61,7 +61,7 @@ void loop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill buf with known data
|
// fill buf with known data
|
||||||
for (size_t i = 0; i < (BUF_SIZE-2); i++) {
|
for (uint16_t i = 0; i < (BUF_SIZE-2); i++) {
|
||||||
buf[i] = 'A' + (i % 26);
|
buf[i] = 'A' + (i % 26);
|
||||||
}
|
}
|
||||||
buf[BUF_SIZE-2] = '\r';
|
buf[BUF_SIZE-2] = '\r';
|
||||||
|
|
@ -16,7 +16,7 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
delay(2000);
|
delay(2000);
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
@ -38,12 +38,12 @@ void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// F() stores strings in flash to save RAM
|
// F() stores strings in flash to save RAM
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
delay(400); // catch Due reset problem
|
delay(400); // catch Due reset problem
|
||||||
|
|
||||||
|
|
@ -89,11 +89,11 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize at the highest speed supported by the board that is
|
// Initialize at the highest speed supported by the board that is
|
||||||
|
|
@ -21,7 +21,7 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// Initialize at the highest speed supported by the board that is
|
// Initialize at the highest speed supported by the board that is
|
||||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
#ifdef __AVR__
|
#ifdef __AVR__
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#include "sdios.h"
|
|
||||||
#include "FreeStack.h"
|
#include "FreeStack.h"
|
||||||
#include "AnalogBinLogger.h"
|
#include "AnalogBinLogger.h"
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
@ -649,7 +648,7 @@ void logData() {
|
||||||
bgnErase = endErase + 1;
|
bgnErase = endErase + 1;
|
||||||
}
|
}
|
||||||
// Start a multiple block write.
|
// Start a multiple block write.
|
||||||
if (!sd.card()->writeStart(bgnBlock)) {
|
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) {
|
||||||
error("writeBegin failed");
|
error("writeBegin failed");
|
||||||
}
|
}
|
||||||
// Write metadata.
|
// Write metadata.
|
||||||
|
|
@ -799,7 +798,7 @@ void loop(void) {
|
||||||
Serial.println(F("r - record ADC data"));
|
Serial.println(F("r - record ADC data"));
|
||||||
|
|
||||||
while(!Serial.available()) {
|
while(!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
char c = tolower(Serial.read());
|
char c = tolower(Serial.read());
|
||||||
if (ERROR_LED_PIN >= 0) {
|
if (ERROR_LED_PIN >= 0) {
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
#ifndef AnalogBinLogger_h
|
|
||||||
#define AnalogBinLogger_h
|
|
||||||
const size_t BLOCK_SIZE = 64;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// First block of file.
|
|
||||||
const size_t PIN_NUM_DIM =
|
|
||||||
BLOCK_SIZE - 3 * sizeof(uint32_t) - 2 * sizeof(uint8_t);
|
|
||||||
struct metadata_t {
|
|
||||||
uint32_t adcFrequency; // ADC clock frequency
|
|
||||||
uint32_t cpuFrequency; // CPU clock frequency
|
|
||||||
uint32_t sampleInterval; // Sample interval in CPU cycles.
|
|
||||||
uint8_t recordEightBits; // Size of ADC values, nonzero for 8-bits.
|
|
||||||
uint8_t pinCount; // Number of analog pins in a sample.
|
|
||||||
uint8_t pinNumber[PIN_NUM_DIM]; // List of pin numbers in a sample.
|
|
||||||
};
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Data block for 8-bit ADC mode.
|
|
||||||
const size_t DATA_DIM8 = (BLOCK_SIZE - 2 * sizeof(uint16_t)) / sizeof(uint8_t);
|
|
||||||
struct block8_t {
|
|
||||||
uint16_t count; // count of data values
|
|
||||||
uint16_t overrun; // count of overruns since last block
|
|
||||||
uint8_t data[DATA_DIM8];
|
|
||||||
};
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Data block for 10-bit ADC mode.
|
|
||||||
const size_t DATA_DIM16 =
|
|
||||||
(BLOCK_SIZE - 2 * sizeof(uint16_t)) / sizeof(uint16_t);
|
|
||||||
struct block16_t {
|
|
||||||
unsigned short count; // count of data values
|
|
||||||
unsigned short overrun; // count of overruns since last block
|
|
||||||
unsigned short data[DATA_DIM16];
|
|
||||||
};
|
|
||||||
#endif // AnalogBinLogger_h
|
|
||||||
|
|
@ -1,905 +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 more 64 byte buffer blocks will be used.
|
|
||||||
*
|
|
||||||
* Each 64 byte data block in the file has a four byte header followed by up
|
|
||||||
* to 60 bytes of data. (60 values in 8-bit mode or 30 values in 10-bit mode)
|
|
||||||
* Each block contains an integral number of samples with unused space at the
|
|
||||||
* end of the block.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifdef __AVR__
|
|
||||||
#include <SPI.h>
|
|
||||||
#include "SdFat.h"
|
|
||||||
#include "AvrAdcLogger.h"
|
|
||||||
#include "BufferedPrint.h"
|
|
||||||
#include "FreeStack.h"
|
|
||||||
|
|
||||||
// Save SRAM if 328.
|
|
||||||
#ifdef __AVR_ATmega328P__
|
|
||||||
#include "MinimumSerial.h"
|
|
||||||
MinimumSerial MinSerial;
|
|
||||||
#define Serial MinSerial
|
|
||||||
#endif // __AVR_ATmega328P__
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// This example was designed for exFAT but will support FAT16/FAT32.
|
|
||||||
//
|
|
||||||
// Note: Uno will not support SD_FAT_TYPE = 3.
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 2
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Set USE_RTC nonzero for file timestamps.
|
|
||||||
// RAM use will be marginal on Uno with RTClib.
|
|
||||||
// Set USE_RTC nonzero for file timestamps.
|
|
||||||
// RAM use will be marginal on Uno with RTClib.
|
|
||||||
// 0 - RTC not used
|
|
||||||
// 1 - DS1307
|
|
||||||
// 2 - DS3231
|
|
||||||
// 3 - PCF8523
|
|
||||||
#define USE_RTC 0
|
|
||||||
#if USE_RTC
|
|
||||||
#include "RTClib.h"
|
|
||||||
#endif // USE_RTC
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Pin definitions.
|
|
||||||
//
|
|
||||||
// Digital pin to indicate an error, set to -1 if not used.
|
|
||||||
// The led blinks for fatal errors. The led goes on solid for SD write
|
|
||||||
// overrun errors and logging continues.
|
|
||||||
const int8_t ERROR_LED_PIN = -1;
|
|
||||||
|
|
||||||
// SD chip select pin.
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Analog pin number list for a sample. Pins may be in any order and pin
|
|
||||||
// numbers may be repeated.
|
|
||||||
const uint8_t PIN_LIST[] = {0, 1, 2, 3, 4};
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Sample rate in samples per second.
|
|
||||||
const float SAMPLE_RATE = 5000; // Must be 0.25 or greater.
|
|
||||||
|
|
||||||
// The interval between samples in seconds, SAMPLE_INTERVAL, may be set to a
|
|
||||||
// constant instead of being calculated from SAMPLE_RATE. SAMPLE_RATE is not
|
|
||||||
// used in the code below. For example, setting SAMPLE_INTERVAL = 2.0e-4
|
|
||||||
// will result in a 200 microsecond sample interval.
|
|
||||||
const float SAMPLE_INTERVAL = 1.0 / SAMPLE_RATE;
|
|
||||||
|
|
||||||
// Setting ROUND_SAMPLE_INTERVAL non-zero will cause the sample interval to
|
|
||||||
// be rounded to a a multiple of the ADC clock period and will reduce sample
|
|
||||||
// time jitter.
|
|
||||||
#define ROUND_SAMPLE_INTERVAL 1
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Reference voltage. See the processor data-sheet for reference details.
|
|
||||||
// uint8_t const ADC_REF = 0; // External Reference AREF pin.
|
|
||||||
uint8_t const ADC_REF = (1 << REFS0); // Vcc Reference.
|
|
||||||
// uint8_t const ADC_REF = (1 << REFS1); // Internal 1.1 (only 644 1284P Mega)
|
|
||||||
// uint8_t const ADC_REF = (1 << REFS1) | (1 << REFS0); // Internal 1.1 or 2.56
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// File definitions.
|
|
||||||
//
|
|
||||||
// Maximum file size in bytes.
|
|
||||||
// The program creates a contiguous file with MAX_FILE_SIZE_MiB bytes.
|
|
||||||
// The file will be truncated if logging is stopped early.
|
|
||||||
const uint32_t MAX_FILE_SIZE_MiB = 100; // 100 MiB file.
|
|
||||||
|
|
||||||
// log file name. Integer field before dot will be incremented.
|
|
||||||
#define LOG_FILE_NAME "AvrAdc00.bin"
|
|
||||||
|
|
||||||
// Maximum length name including zero byte.
|
|
||||||
const size_t NAME_DIM = 40;
|
|
||||||
|
|
||||||
// Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC.
|
|
||||||
#define RECORD_EIGHT_BITS 0
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// FIFO size definition. Use a multiple of 512 bytes for best performance.
|
|
||||||
//
|
|
||||||
#if RAMEND < 0X8FF
|
|
||||||
#error SRAM too small
|
|
||||||
#elif RAMEND < 0X10FF
|
|
||||||
const size_t FIFO_SIZE_BYTES = 512;
|
|
||||||
#elif RAMEND < 0X20FF
|
|
||||||
const size_t FIFO_SIZE_BYTES = 4 * 512;
|
|
||||||
#elif RAMEND < 0X40FF
|
|
||||||
const size_t FIFO_SIZE_BYTES = 12 * 512;
|
|
||||||
#else // RAMEND
|
|
||||||
const size_t FIFO_SIZE_BYTES = 16 * 512;
|
|
||||||
#endif // RAMEND
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// ADC clock rate.
|
|
||||||
// The ADC clock rate is normally calculated from the pin count and sample
|
|
||||||
// interval. The calculation attempts to use the lowest possible ADC clock
|
|
||||||
// rate.
|
|
||||||
//
|
|
||||||
// You can select an ADC clock rate by defining the symbol ADC_PRESCALER to
|
|
||||||
// one of these values. You must choose an appropriate ADC clock rate for
|
|
||||||
// your sample interval.
|
|
||||||
// #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno
|
|
||||||
// #define ADC_PRESCALER 6 // F_CPU/64 250 kHz on an Uno
|
|
||||||
// #define ADC_PRESCALER 5 // F_CPU/32 500 kHz on an Uno
|
|
||||||
// #define ADC_PRESCALER 4 // F_CPU/16 1000 kHz on an Uno
|
|
||||||
// #define ADC_PRESCALER 3 // F_CPU/8 2000 kHz on an Uno (8-bit mode only)
|
|
||||||
//==============================================================================
|
|
||||||
// End of configuration constants.
|
|
||||||
//==============================================================================
|
|
||||||
// Temporary log file. Will be deleted if a reset or power failure occurs.
|
|
||||||
#define TMP_FILE_NAME "tmp_adc.bin"
|
|
||||||
|
|
||||||
// Number of analog pins to log.
|
|
||||||
const uint8_t PIN_COUNT = sizeof(PIN_LIST) / sizeof(PIN_LIST[0]);
|
|
||||||
|
|
||||||
// Minimum ADC clock cycles per sample interval
|
|
||||||
const uint16_t MIN_ADC_CYCLES = 15;
|
|
||||||
|
|
||||||
// Extra cpu cycles to setup ADC with more than one pin per sample.
|
|
||||||
const uint16_t ISR_SETUP_ADC = PIN_COUNT > 1 ? 100 : 0;
|
|
||||||
|
|
||||||
// Maximum cycles for timer0 system interrupt.
|
|
||||||
const uint16_t ISR_TIMER0 = 160;
|
|
||||||
//==============================================================================
|
|
||||||
const uint32_t MAX_FILE_SIZE = MAX_FILE_SIZE_MiB << 20;
|
|
||||||
|
|
||||||
// Max SPI rate for AVR is 10 MHz for F_CPU 20 MHz, 8 MHz for F_CPU 16 MHz.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(10)
|
|
||||||
|
|
||||||
// Select fastest interface.
|
|
||||||
#if ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // ENABLE_DEDICATED_SPI
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
|
||||||
typedef File file_t;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
typedef File32 file_t;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
typedef ExFile file_t;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
typedef FsFile file_t;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
file_t binFile;
|
|
||||||
file_t csvFile;
|
|
||||||
|
|
||||||
char binName[] = LOG_FILE_NAME;
|
|
||||||
|
|
||||||
#if RECORD_EIGHT_BITS
|
|
||||||
const size_t BLOCK_MAX_COUNT = PIN_COUNT * (DATA_DIM8 / PIN_COUNT);
|
|
||||||
typedef block8_t block_t;
|
|
||||||
#else // RECORD_EIGHT_BITS
|
|
||||||
const size_t BLOCK_MAX_COUNT = PIN_COUNT * (DATA_DIM16 / PIN_COUNT);
|
|
||||||
typedef block16_t block_t;
|
|
||||||
#endif // RECORD_EIGHT_BITS
|
|
||||||
|
|
||||||
// Size of FIFO in blocks.
|
|
||||||
size_t const FIFO_DIM = FIFO_SIZE_BYTES / sizeof(block_t);
|
|
||||||
block_t* fifoData;
|
|
||||||
volatile size_t fifoCount = 0; // volatile - shared, ISR and background.
|
|
||||||
size_t fifoHead = 0; // Only accessed by ISR during logging.
|
|
||||||
size_t fifoTail = 0; // Only accessed by writer during logging.
|
|
||||||
//==============================================================================
|
|
||||||
// Interrupt Service Routines
|
|
||||||
|
|
||||||
// Disable ADC interrupt if true.
|
|
||||||
volatile bool isrStop = false;
|
|
||||||
|
|
||||||
// Pointer to current buffer.
|
|
||||||
block_t* isrBuf = nullptr;
|
|
||||||
// overrun count
|
|
||||||
uint16_t isrOver = 0;
|
|
||||||
|
|
||||||
// ADC configuration for each pin.
|
|
||||||
uint8_t adcmux[PIN_COUNT];
|
|
||||||
uint8_t adcsra[PIN_COUNT];
|
|
||||||
uint8_t adcsrb[PIN_COUNT];
|
|
||||||
uint8_t adcindex = 1;
|
|
||||||
|
|
||||||
// Insure no timer events are missed.
|
|
||||||
volatile bool timerError = false;
|
|
||||||
volatile bool timerFlag = false;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// ADC done interrupt.
|
|
||||||
ISR(ADC_vect) {
|
|
||||||
// Read ADC data.
|
|
||||||
#if RECORD_EIGHT_BITS
|
|
||||||
uint8_t d = ADCH;
|
|
||||||
#else // RECORD_EIGHT_BITS
|
|
||||||
// This will access ADCL first.
|
|
||||||
uint16_t d = ADC;
|
|
||||||
#endif // RECORD_EIGHT_BITS
|
|
||||||
|
|
||||||
if (!isrBuf) {
|
|
||||||
if (fifoCount < FIFO_DIM) {
|
|
||||||
isrBuf = fifoData + fifoHead;
|
|
||||||
} else {
|
|
||||||
// no buffers - count overrun
|
|
||||||
if (isrOver < 0XFFFF) {
|
|
||||||
isrOver++;
|
|
||||||
}
|
|
||||||
// Avoid missed timer error.
|
|
||||||
timerFlag = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Start ADC for next pin
|
|
||||||
if (PIN_COUNT > 1) {
|
|
||||||
ADMUX = adcmux[adcindex];
|
|
||||||
ADCSRB = adcsrb[adcindex];
|
|
||||||
ADCSRA = adcsra[adcindex];
|
|
||||||
if (adcindex == 0) {
|
|
||||||
timerFlag = false;
|
|
||||||
}
|
|
||||||
adcindex = adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0;
|
|
||||||
} else {
|
|
||||||
timerFlag = false;
|
|
||||||
}
|
|
||||||
// Store ADC data.
|
|
||||||
isrBuf->data[isrBuf->count++] = d;
|
|
||||||
|
|
||||||
// Check for buffer full.
|
|
||||||
if (isrBuf->count >= BLOCK_MAX_COUNT) {
|
|
||||||
fifoHead = fifoHead < (FIFO_DIM - 1) ? fifoHead + 1 : 0;
|
|
||||||
fifoCount++;
|
|
||||||
// Check for end logging.
|
|
||||||
if (isrStop) {
|
|
||||||
adcStop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Set buffer needed and clear overruns.
|
|
||||||
isrBuf = nullptr;
|
|
||||||
isrOver = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// timer1 interrupt to clear OCF1B
|
|
||||||
ISR(TIMER1_COMPB_vect) {
|
|
||||||
// Make sure ADC ISR responded to timer event.
|
|
||||||
if (timerFlag) {
|
|
||||||
timerError = true;
|
|
||||||
}
|
|
||||||
timerFlag = true;
|
|
||||||
}
|
|
||||||
//==============================================================================
|
|
||||||
// Error messages stored in flash.
|
|
||||||
#define error(msg) (Serial.println(F(msg)), errorHalt())
|
|
||||||
#define assert(e) ((e) ? (void)0 : error("assert: " #e))
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
void fatalBlink() {
|
|
||||||
while (true) {
|
|
||||||
if (ERROR_LED_PIN >= 0) {
|
|
||||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
|
||||||
delay(200);
|
|
||||||
digitalWrite(ERROR_LED_PIN, LOW);
|
|
||||||
delay(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void errorHalt() {
|
|
||||||
// Print minimal error data.
|
|
||||||
// sd.errorPrint(&Serial);
|
|
||||||
// Print extended error info - uses extra bytes of flash.
|
|
||||||
sd.printSdError(&Serial);
|
|
||||||
// Try to save data.
|
|
||||||
binFile.close();
|
|
||||||
fatalBlink();
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printUnusedStack() {
|
|
||||||
Serial.print(F("\nUnused stack: "));
|
|
||||||
Serial.println(UnusedStack());
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#if USE_RTC
|
|
||||||
#if USE_RTC == 1
|
|
||||||
RTC_DS1307 rtc;
|
|
||||||
#elif USE_RTC == 2
|
|
||||||
RTC_DS3231 rtc;
|
|
||||||
#elif USE_RTC == 3
|
|
||||||
RTC_PCF8523 rtc;
|
|
||||||
#else // USE_RTC == type
|
|
||||||
#error USE_RTC type not implemented.
|
|
||||||
#endif // USE_RTC == type
|
|
||||||
// Call back for file timestamps. Only called for file create and sync().
|
|
||||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
|
||||||
DateTime now = rtc.now();
|
|
||||||
|
|
||||||
// Return date using FS_DATE macro to format fields.
|
|
||||||
*date = FS_DATE(now.year(), now.month(), now.day());
|
|
||||||
|
|
||||||
// Return time using FS_TIME macro to format fields.
|
|
||||||
*time = FS_TIME(now.hour(), now.minute(), now.second());
|
|
||||||
|
|
||||||
// Return low time bits in units of 10 ms.
|
|
||||||
*ms10 = now.second() & 1 ? 100 : 0;
|
|
||||||
}
|
|
||||||
#endif // USE_RTC
|
|
||||||
//==============================================================================
|
|
||||||
#if ADPS0 != 0 || ADPS1 != 1 || ADPS2 != 2
|
|
||||||
#error unexpected ADC prescaler bits
|
|
||||||
#endif
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
inline bool adcActive() { return (1 << ADIE) & ADCSRA; }
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// initialize ADC and timer1
|
|
||||||
void adcInit(metadata_t* meta) {
|
|
||||||
uint8_t adps; // prescaler bits for ADCSRA
|
|
||||||
uint32_t ticks =
|
|
||||||
F_CPU * SAMPLE_INTERVAL + 0.5; // Sample interval cpu cycles.
|
|
||||||
|
|
||||||
if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) {
|
|
||||||
error("Invalid ADC reference");
|
|
||||||
}
|
|
||||||
#ifdef ADC_PRESCALER
|
|
||||||
if (ADC_PRESCALER > 7 || ADC_PRESCALER < 2) {
|
|
||||||
error("Invalid ADC prescaler");
|
|
||||||
}
|
|
||||||
adps = ADC_PRESCALER;
|
|
||||||
#else // ADC_PRESCALER
|
|
||||||
// Allow extra cpu cycles to change ADC settings if more than one pin.
|
|
||||||
int32_t adcCycles = (ticks - ISR_TIMER0) / PIN_COUNT - ISR_SETUP_ADC;
|
|
||||||
|
|
||||||
for (adps = 7; adps > 0; adps--) {
|
|
||||||
if (adcCycles >= (MIN_ADC_CYCLES << adps)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // ADC_PRESCALER
|
|
||||||
meta->adcFrequency = F_CPU >> adps;
|
|
||||||
if (meta->adcFrequency > (RECORD_EIGHT_BITS ? 2000000 : 1000000)) {
|
|
||||||
error("Sample Rate Too High");
|
|
||||||
}
|
|
||||||
#if ROUND_SAMPLE_INTERVAL
|
|
||||||
// Round so interval is multiple of ADC clock.
|
|
||||||
ticks += 1 << (adps - 1);
|
|
||||||
ticks >>= adps;
|
|
||||||
ticks <<= adps;
|
|
||||||
#endif // ROUND_SAMPLE_INTERVAL
|
|
||||||
|
|
||||||
if (PIN_COUNT > BLOCK_MAX_COUNT || PIN_COUNT > PIN_NUM_DIM) {
|
|
||||||
error("Too many pins");
|
|
||||||
}
|
|
||||||
meta->pinCount = PIN_COUNT;
|
|
||||||
meta->recordEightBits = RECORD_EIGHT_BITS;
|
|
||||||
|
|
||||||
for (int i = 0; i < PIN_COUNT; i++) {
|
|
||||||
uint8_t pin = PIN_LIST[i];
|
|
||||||
if (pin >= NUM_ANALOG_INPUTS) {
|
|
||||||
error("Invalid Analog pin number");
|
|
||||||
}
|
|
||||||
meta->pinNumber[i] = pin;
|
|
||||||
|
|
||||||
// Set ADC reference and low three bits of analog pin number.
|
|
||||||
adcmux[i] = (pin & 7) | ADC_REF;
|
|
||||||
if (RECORD_EIGHT_BITS) {
|
|
||||||
adcmux[i] |= 1 << ADLAR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is the first pin, trigger on timer/counter 1 compare match B.
|
|
||||||
adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0;
|
|
||||||
#ifdef MUX5
|
|
||||||
if (pin > 7) {
|
|
||||||
adcsrb[i] |= (1 << MUX5);
|
|
||||||
}
|
|
||||||
#endif // MUX5
|
|
||||||
adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps;
|
|
||||||
// First pin triggers on timer 1 compare match B rest are free running.
|
|
||||||
adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup timer1
|
|
||||||
TCCR1A = 0;
|
|
||||||
uint8_t tshift;
|
|
||||||
if (ticks < 0X10000) {
|
|
||||||
// no prescale, CTC mode
|
|
||||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
|
|
||||||
tshift = 0;
|
|
||||||
} else if (ticks < 0X10000 * 8) {
|
|
||||||
// prescale 8, CTC mode
|
|
||||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
|
|
||||||
tshift = 3;
|
|
||||||
} else if (ticks < 0X10000 * 64) {
|
|
||||||
// prescale 64, CTC mode
|
|
||||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << CS10);
|
|
||||||
tshift = 6;
|
|
||||||
} else if (ticks < 0X10000 * 256) {
|
|
||||||
// prescale 256, CTC mode
|
|
||||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12);
|
|
||||||
tshift = 8;
|
|
||||||
} else if (ticks < 0X10000 * 1024) {
|
|
||||||
// prescale 1024, CTC mode
|
|
||||||
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12) | (1 << CS10);
|
|
||||||
tshift = 10;
|
|
||||||
} else {
|
|
||||||
error("Sample Rate Too Slow");
|
|
||||||
}
|
|
||||||
// divide by prescaler
|
|
||||||
ticks >>= tshift;
|
|
||||||
// set TOP for timer reset
|
|
||||||
ICR1 = ticks - 1;
|
|
||||||
// compare for ADC start
|
|
||||||
OCR1B = 0;
|
|
||||||
|
|
||||||
// multiply by prescaler
|
|
||||||
ticks <<= tshift;
|
|
||||||
|
|
||||||
// Sample interval in CPU clock ticks.
|
|
||||||
meta->sampleInterval = ticks;
|
|
||||||
meta->cpuFrequency = F_CPU;
|
|
||||||
float sampleRate = (float)meta->cpuFrequency / meta->sampleInterval;
|
|
||||||
Serial.print(F("Sample pins:"));
|
|
||||||
for (uint8_t i = 0; i < meta->pinCount; i++) {
|
|
||||||
Serial.print(' ');
|
|
||||||
Serial.print(meta->pinNumber[i], DEC);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
Serial.print(F("ADC bits: "));
|
|
||||||
Serial.println(meta->recordEightBits ? 8 : 10);
|
|
||||||
Serial.print(F("ADC clock kHz: "));
|
|
||||||
Serial.println(meta->adcFrequency / 1000);
|
|
||||||
Serial.print(F("Sample Rate: "));
|
|
||||||
Serial.println(sampleRate);
|
|
||||||
Serial.print(F("Sample interval usec: "));
|
|
||||||
Serial.println(1000000.0 / sampleRate);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// enable ADC and timer1 interrupts
|
|
||||||
void adcStart() {
|
|
||||||
// initialize ISR
|
|
||||||
adcindex = 1;
|
|
||||||
isrBuf = nullptr;
|
|
||||||
isrOver = 0;
|
|
||||||
isrStop = false;
|
|
||||||
|
|
||||||
// Clear any pending interrupt.
|
|
||||||
ADCSRA |= 1 << ADIF;
|
|
||||||
|
|
||||||
// Setup for first pin.
|
|
||||||
ADMUX = adcmux[0];
|
|
||||||
ADCSRB = adcsrb[0];
|
|
||||||
ADCSRA = adcsra[0];
|
|
||||||
|
|
||||||
// Enable timer1 interrupts.
|
|
||||||
timerError = false;
|
|
||||||
timerFlag = false;
|
|
||||||
TCNT1 = 0;
|
|
||||||
TIFR1 = 1 << OCF1B;
|
|
||||||
TIMSK1 = 1 << OCIE1B;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
inline void adcStop() {
|
|
||||||
TIMSK1 = 0;
|
|
||||||
ADCSRA = 0;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Convert binary file to csv file.
|
|
||||||
void binaryToCsv() {
|
|
||||||
uint8_t lastPct = 0;
|
|
||||||
block_t* pd;
|
|
||||||
metadata_t* pm;
|
|
||||||
uint32_t t0 = millis();
|
|
||||||
// Use fast buffered print class.
|
|
||||||
BufferedPrint<file_t, 64> bp(&csvFile);
|
|
||||||
block_t binBuffer[FIFO_DIM];
|
|
||||||
|
|
||||||
assert(sizeof(block_t) == sizeof(metadata_t));
|
|
||||||
binFile.rewind();
|
|
||||||
uint32_t tPct = millis();
|
|
||||||
bool doMeta = true;
|
|
||||||
while (!Serial.available()) {
|
|
||||||
pd = binBuffer;
|
|
||||||
int nb = binFile.read(binBuffer, sizeof(binBuffer));
|
|
||||||
if (nb < 0) {
|
|
||||||
error("read binFile failed");
|
|
||||||
}
|
|
||||||
size_t nd = nb / sizeof(block_t);
|
|
||||||
if (nd < 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (doMeta) {
|
|
||||||
doMeta = false;
|
|
||||||
pm = (metadata_t*)pd++;
|
|
||||||
if (PIN_COUNT != pm->pinCount) {
|
|
||||||
error("Invalid pinCount");
|
|
||||||
}
|
|
||||||
bp.print(F("Interval,"));
|
|
||||||
float intervalMicros =
|
|
||||||
1.0e6 * pm->sampleInterval / (float)pm->cpuFrequency;
|
|
||||||
bp.print(intervalMicros, 4);
|
|
||||||
bp.println(F(",usec"));
|
|
||||||
for (uint8_t i = 0; i < PIN_COUNT; i++) {
|
|
||||||
if (i) {
|
|
||||||
bp.print(',');
|
|
||||||
}
|
|
||||||
bp.print(F("pin"));
|
|
||||||
bp.print(pm->pinNumber[i]);
|
|
||||||
}
|
|
||||||
bp.println();
|
|
||||||
if (nd-- == 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < nd; i++, pd++) {
|
|
||||||
if (pd->overrun) {
|
|
||||||
bp.print(F("OVERRUN,"));
|
|
||||||
bp.println(pd->overrun);
|
|
||||||
}
|
|
||||||
for (size_t j = 0; j < pd->count; j += PIN_COUNT) {
|
|
||||||
for (size_t i = 0; i < PIN_COUNT; i++) {
|
|
||||||
if (!bp.printField(pd->data[i + j],
|
|
||||||
i == (PIN_COUNT - 1) ? '\n' : ',')) {
|
|
||||||
error("printField failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((millis() - tPct) > 1000) {
|
|
||||||
uint8_t pct = binFile.curPosition() / (binFile.fileSize() / 100);
|
|
||||||
if (pct != lastPct) {
|
|
||||||
tPct = millis();
|
|
||||||
lastPct = pct;
|
|
||||||
Serial.print(pct, DEC);
|
|
||||||
Serial.println('%');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!bp.sync() || !csvFile.close()) {
|
|
||||||
error("close csvFile failed");
|
|
||||||
}
|
|
||||||
Serial.print(F("Done: "));
|
|
||||||
Serial.print(0.001 * (millis() - t0));
|
|
||||||
Serial.println(F(" Seconds"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void clearSerialInput() {
|
|
||||||
uint32_t m = micros();
|
|
||||||
do {
|
|
||||||
if (Serial.read() >= 0) {
|
|
||||||
m = micros();
|
|
||||||
}
|
|
||||||
} while (micros() - m < 10000);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void createBinFile() {
|
|
||||||
binFile.close();
|
|
||||||
while (sd.exists(binName)) {
|
|
||||||
char* p = strchr(binName, '.');
|
|
||||||
if (!p) {
|
|
||||||
error("no dot in filename");
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
p--;
|
|
||||||
if (p < binName || *p < '0' || *p > '9') {
|
|
||||||
error("Can't create file name");
|
|
||||||
}
|
|
||||||
if (p[0] != '9') {
|
|
||||||
p[0]++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p[0] = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.print(F("Opening: "));
|
|
||||||
Serial.println(binName);
|
|
||||||
if (!binFile.open(binName, O_RDWR | O_CREAT)) {
|
|
||||||
error("open binName failed");
|
|
||||||
}
|
|
||||||
Serial.print(F("Allocating: "));
|
|
||||||
Serial.print(MAX_FILE_SIZE_MiB);
|
|
||||||
Serial.println(F(" MiB"));
|
|
||||||
if (!binFile.preAllocate(MAX_FILE_SIZE)) {
|
|
||||||
error("preAllocate failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool createCsvFile() {
|
|
||||||
char csvName[NAME_DIM];
|
|
||||||
|
|
||||||
if (!binFile.isOpen()) {
|
|
||||||
Serial.println(F("No current binary file"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
binFile.getName(csvName, sizeof(csvName));
|
|
||||||
char* dot = strchr(csvName, '.');
|
|
||||||
if (!dot) {
|
|
||||||
error("no dot in binName");
|
|
||||||
}
|
|
||||||
strcpy(dot + 1, "csv");
|
|
||||||
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
|
|
||||||
error("open csvFile failed");
|
|
||||||
}
|
|
||||||
Serial.print(F("Writing: "));
|
|
||||||
Serial.print(csvName);
|
|
||||||
Serial.println(F(" - type any character to stop"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// log data
|
|
||||||
void logData() {
|
|
||||||
uint32_t t0;
|
|
||||||
uint32_t t1;
|
|
||||||
uint32_t overruns = 0;
|
|
||||||
uint32_t count = 0;
|
|
||||||
uint32_t maxLatencyUsec = 0;
|
|
||||||
size_t maxFifoUse = 0;
|
|
||||||
block_t fifoBuffer[FIFO_DIM];
|
|
||||||
|
|
||||||
adcInit((metadata_t*)fifoBuffer);
|
|
||||||
// Write metadata.
|
|
||||||
if (sizeof(metadata_t) != binFile.write(fifoBuffer, sizeof(metadata_t))) {
|
|
||||||
error("Write metadata failed");
|
|
||||||
}
|
|
||||||
fifoCount = 0;
|
|
||||||
fifoHead = 0;
|
|
||||||
fifoTail = 0;
|
|
||||||
fifoData = fifoBuffer;
|
|
||||||
// Initialize all blocks to save ISR overhead.
|
|
||||||
memset(fifoBuffer, 0, sizeof(fifoBuffer));
|
|
||||||
|
|
||||||
Serial.println(F("Logging - type any character to stop"));
|
|
||||||
// Wait for Serial Idle.
|
|
||||||
Serial.flush();
|
|
||||||
delay(10);
|
|
||||||
|
|
||||||
t0 = millis();
|
|
||||||
t1 = t0;
|
|
||||||
// Start logging interrupts.
|
|
||||||
adcStart();
|
|
||||||
while (1) {
|
|
||||||
uint32_t m;
|
|
||||||
noInterrupts();
|
|
||||||
size_t tmpFifoCount = fifoCount;
|
|
||||||
interrupts();
|
|
||||||
if (tmpFifoCount) {
|
|
||||||
block_t* pBlock = fifoData + fifoTail;
|
|
||||||
// Write block to SD.
|
|
||||||
m = micros();
|
|
||||||
if (sizeof(block_t) != binFile.write(pBlock, sizeof(block_t))) {
|
|
||||||
error("write data failed");
|
|
||||||
}
|
|
||||||
m = micros() - m;
|
|
||||||
t1 = millis();
|
|
||||||
if (m > maxLatencyUsec) {
|
|
||||||
maxLatencyUsec = m;
|
|
||||||
}
|
|
||||||
if (tmpFifoCount > maxFifoUse) {
|
|
||||||
maxFifoUse = tmpFifoCount;
|
|
||||||
}
|
|
||||||
count += pBlock->count;
|
|
||||||
|
|
||||||
// Add overruns and possibly light LED.
|
|
||||||
if (pBlock->overrun) {
|
|
||||||
overruns += pBlock->overrun;
|
|
||||||
if (ERROR_LED_PIN >= 0) {
|
|
||||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Initialize empty block to save ISR overhead.
|
|
||||||
pBlock->count = 0;
|
|
||||||
pBlock->overrun = 0;
|
|
||||||
fifoTail = fifoTail < (FIFO_DIM - 1) ? fifoTail + 1 : 0;
|
|
||||||
|
|
||||||
noInterrupts();
|
|
||||||
fifoCount--;
|
|
||||||
interrupts();
|
|
||||||
|
|
||||||
if (binFile.curPosition() >= MAX_FILE_SIZE) {
|
|
||||||
// File full so stop ISR calls.
|
|
||||||
adcStop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (timerError) {
|
|
||||||
error("Missed timer event - rate too high");
|
|
||||||
}
|
|
||||||
if (Serial.available()) {
|
|
||||||
// Stop ISR interrupts.
|
|
||||||
isrStop = true;
|
|
||||||
}
|
|
||||||
if (fifoCount == 0 && !adcActive()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
// Truncate file if recording stopped early.
|
|
||||||
if (binFile.curPosition() < MAX_FILE_SIZE) {
|
|
||||||
Serial.println(F("Truncating file"));
|
|
||||||
Serial.flush();
|
|
||||||
if (!binFile.truncate()) {
|
|
||||||
error("Can't truncate file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.print(F("Max write latency usec: "));
|
|
||||||
Serial.println(maxLatencyUsec);
|
|
||||||
Serial.print(F("Record time sec: "));
|
|
||||||
Serial.println(0.001 * (t1 - t0), 3);
|
|
||||||
Serial.print(F("Sample count: "));
|
|
||||||
Serial.println(count / PIN_COUNT);
|
|
||||||
Serial.print(F("Overruns: "));
|
|
||||||
Serial.println(overruns);
|
|
||||||
Serial.print(F("FIFO_DIM: "));
|
|
||||||
Serial.println(FIFO_DIM);
|
|
||||||
Serial.print(F("maxFifoUse: "));
|
|
||||||
Serial.println(maxFifoUse + 1); // include ISR use.
|
|
||||||
Serial.println(F("Done"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void openBinFile() {
|
|
||||||
char name[NAME_DIM];
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println(F("Enter file name"));
|
|
||||||
if (!serialReadLine(name, sizeof(name))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!sd.exists(name)) {
|
|
||||||
Serial.println(name);
|
|
||||||
Serial.println(F("File does not exist"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
binFile.close();
|
|
||||||
if (!binFile.open(name, O_RDWR)) {
|
|
||||||
Serial.println(name);
|
|
||||||
Serial.println(F("open failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial.println(F("File opened"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Print data file to Serial
|
|
||||||
void printData() {
|
|
||||||
block_t buf;
|
|
||||||
if (!binFile.isOpen()) {
|
|
||||||
Serial.println(F("No current binary file"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
binFile.rewind();
|
|
||||||
if (binFile.read(&buf, sizeof(buf)) != sizeof(buf)) {
|
|
||||||
error("Read metadata failed");
|
|
||||||
}
|
|
||||||
Serial.println(F("Type any character to stop"));
|
|
||||||
delay(1000);
|
|
||||||
while (!Serial.available() &&
|
|
||||||
binFile.read(&buf, sizeof(buf)) == sizeof(buf)) {
|
|
||||||
if (buf.count == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (buf.overrun) {
|
|
||||||
Serial.print(F("OVERRUN,"));
|
|
||||||
Serial.println(buf.overrun);
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < buf.count; i++) {
|
|
||||||
Serial.print(buf.data[i], DEC);
|
|
||||||
if ((i + 1) % PIN_COUNT) {
|
|
||||||
Serial.print(',');
|
|
||||||
} else {
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.println(F("Done"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool serialReadLine(char* str, size_t size) {
|
|
||||||
size_t n = 0;
|
|
||||||
while (!Serial.available()) {
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
int c = Serial.read();
|
|
||||||
if (c < ' ') break;
|
|
||||||
str[n++] = c;
|
|
||||||
if (n >= size) {
|
|
||||||
Serial.println(F("input too long"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t m = millis();
|
|
||||||
while (!Serial.available() && (millis() - m) < 100) {
|
|
||||||
}
|
|
||||||
if (!Serial.available()) break;
|
|
||||||
}
|
|
||||||
str[n] = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup(void) {
|
|
||||||
if (ERROR_LED_PIN >= 0) {
|
|
||||||
pinMode(ERROR_LED_PIN, OUTPUT);
|
|
||||||
}
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
}
|
|
||||||
Serial.println(F("Type any character to begin."));
|
|
||||||
while (!Serial.available()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
FillStack();
|
|
||||||
|
|
||||||
// Read the first sample pin to init the ADC.
|
|
||||||
analogRead(PIN_LIST[0]);
|
|
||||||
|
|
||||||
#if !ENABLE_DEDICATED_SPI
|
|
||||||
Serial.println(
|
|
||||||
F("\nFor best performance edit SdFatConfig.h\n"
|
|
||||||
"and set ENABLE_DEDICATED_SPI nonzero"));
|
|
||||||
#endif // !ENABLE_DEDICATED_SPI
|
|
||||||
// Initialize SD.
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
error("sd.begin failed");
|
|
||||||
}
|
|
||||||
#if USE_RTC
|
|
||||||
if (!rtc.begin()) {
|
|
||||||
error("rtc.begin failed");
|
|
||||||
}
|
|
||||||
if (!rtc.isrunning()) {
|
|
||||||
// Set RTC to sketch compile date & time.
|
|
||||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
|
||||||
error("RTC is NOT running!");
|
|
||||||
} else {
|
|
||||||
Serial.println(F("RTC is running"));
|
|
||||||
}
|
|
||||||
// Set callback
|
|
||||||
FsDateTime::setCallback(dateTime);
|
|
||||||
#endif // USE_RTC
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop(void) {
|
|
||||||
printUnusedStack();
|
|
||||||
// Read any Serial data.
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println();
|
|
||||||
Serial.println(F("type:"));
|
|
||||||
Serial.println(F("b - open existing bin file"));
|
|
||||||
Serial.println(F("c - convert file to csv"));
|
|
||||||
Serial.println(F("l - list files"));
|
|
||||||
Serial.println(F("p - print data to Serial"));
|
|
||||||
Serial.println(F("r - record ADC data"));
|
|
||||||
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
char c = tolower(Serial.read());
|
|
||||||
Serial.println();
|
|
||||||
if (ERROR_LED_PIN >= 0) {
|
|
||||||
digitalWrite(ERROR_LED_PIN, LOW);
|
|
||||||
}
|
|
||||||
// Read any Serial data.
|
|
||||||
clearSerialInput();
|
|
||||||
|
|
||||||
if (c == 'b') {
|
|
||||||
openBinFile();
|
|
||||||
} else if (c == 'c') {
|
|
||||||
if (createCsvFile()) {
|
|
||||||
binaryToCsv();
|
|
||||||
}
|
|
||||||
} else if (c == 'l') {
|
|
||||||
Serial.println(F("ls:"));
|
|
||||||
sd.ls(&Serial, LS_DATE | LS_SIZE);
|
|
||||||
} else if (c == 'p') {
|
|
||||||
printData();
|
|
||||||
} else if (c == 'r') {
|
|
||||||
createBinFile();
|
|
||||||
logData();
|
|
||||||
} else {
|
|
||||||
Serial.println(F("Invalid entry"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else // __AVR__
|
|
||||||
#error This program is only for AVR.
|
|
||||||
#endif // __AVR__
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
feather_rp2040
|
|
||||||
metro_rp2040
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
// A simple read/write example for SD.h.
|
|
||||||
// Mostly from the SD.h ReadWrite example.
|
|
||||||
//
|
|
||||||
// Your SD must be formatted FAT16/FAT32.
|
|
||||||
//
|
|
||||||
// SD.h does not support some default SdFat features.
|
|
||||||
// To compare flash size, set USE_FAT_FILE_FLAG_CONTIGUOUS,
|
|
||||||
// ENABLE_DEDICATED_SPI, and USE_LONG_FILE_NAMES to zero also
|
|
||||||
// set SDFAT_FILE_TYPE to one in SdFat/src/SdFatCongfig.h
|
|
||||||
//
|
|
||||||
// Set USE_SD_H nonzero to use SD.h.
|
|
||||||
// Set USE_SD_H zero to use SdFat.h.
|
|
||||||
//
|
|
||||||
#define USE_SD_H 0
|
|
||||||
//
|
|
||||||
#if USE_SD_H
|
|
||||||
#include <SD.h>
|
|
||||||
#else // USE_SD_H
|
|
||||||
#include "SdFat.h"
|
|
||||||
SdFat SD;
|
|
||||||
#endif // USE_SD_H
|
|
||||||
|
|
||||||
// Modify SD_CS_PIN for your board.
|
|
||||||
// For Teensy 3.6 and SdFat.h use BUILTIN_SDCARD.
|
|
||||||
#define SD_CS_PIN SS
|
|
||||||
|
|
||||||
File myFile;
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USE_SD_H
|
|
||||||
Serial.println(F("Using SD.h. Set USE_SD_H zero to use SdFat.h."));
|
|
||||||
#else // USE_SD_H
|
|
||||||
Serial.println(F("Using SdFat.h. Set USE_SD_H nonzero to use SD.h."));
|
|
||||||
#endif // USE_SD_H
|
|
||||||
Serial.println(F("\nType any character to begin."));
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.print("Initializing SD card...");
|
|
||||||
|
|
||||||
if (!SD.begin(SD_CS_PIN)) {
|
|
||||||
Serial.println("initialization failed!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial.println("initialization done.");
|
|
||||||
|
|
||||||
// open the file.
|
|
||||||
myFile = SD.open("test.txt", FILE_WRITE);
|
|
||||||
|
|
||||||
// if the file opened okay, write to it:
|
|
||||||
if (myFile) {
|
|
||||||
Serial.print("Writing to test.txt...");
|
|
||||||
myFile.println("testing 1, 2, 3.");
|
|
||||||
// close the file:
|
|
||||||
myFile.close();
|
|
||||||
Serial.println("done.");
|
|
||||||
} else {
|
|
||||||
// if the file didn't open, print an error:
|
|
||||||
Serial.println("error opening test.txt");
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-open the file for reading:
|
|
||||||
myFile = SD.open("test.txt");
|
|
||||||
if (myFile) {
|
|
||||||
Serial.println("test.txt:");
|
|
||||||
|
|
||||||
// read from the file until there's nothing else in it:
|
|
||||||
while (myFile.available()) {
|
|
||||||
Serial.write(myFile.read());
|
|
||||||
}
|
|
||||||
// close the file:
|
|
||||||
myFile.close();
|
|
||||||
} else {
|
|
||||||
// if the file didn't open, print an error:
|
|
||||||
Serial.println("error opening test.txt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void loop() {
|
|
||||||
// nothing happens after setup
|
|
||||||
}
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
// Test and benchmark of the fast bufferedPrint class.
|
|
||||||
//
|
|
||||||
// Mainly for AVR but may improve print performance with other CPUs.
|
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
|
||||||
#include "SdFat.h"
|
|
||||||
#include "BufferedPrint.h"
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI and
|
|
||||||
your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
|
||||||
typedef File file_t;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
typedef File32 file_t;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
typedef ExFile file_t;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
typedef FsFile file_t;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
// number of lines to print
|
|
||||||
const uint16_t N_PRINT = 20000;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void benchmark() {
|
|
||||||
file_t file;
|
|
||||||
BufferedPrint<file_t, 64> bp;
|
|
||||||
// do write test
|
|
||||||
Serial.println();
|
|
||||||
for (int test = 0; test < 6; test++) {
|
|
||||||
char fileName[13] = "bench0.txt";
|
|
||||||
fileName[5] = '0' + test;
|
|
||||||
// open or create file - truncate existing file.
|
|
||||||
if (!file.open(fileName, O_RDWR | O_CREAT | O_TRUNC)) {
|
|
||||||
sd.errorHalt(&Serial, F("open failed"));
|
|
||||||
}
|
|
||||||
if (test & 1) {
|
|
||||||
bp.begin(&file);
|
|
||||||
}
|
|
||||||
uint32_t t = millis();
|
|
||||||
switch (test) {
|
|
||||||
case 0:
|
|
||||||
Serial.println(F("Test of println(uint16_t)"));
|
|
||||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
|
||||||
file.println(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
Serial.println(F("Test of printField(uint16_t, char)"));
|
|
||||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
|
||||||
bp.printField(i, '\n');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
Serial.println(F("Test of println(uint32_t)"));
|
|
||||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
|
||||||
file.println(12345678UL + i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
Serial.println(F("Test of printField(uint32_t, char)"));
|
|
||||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
|
||||||
bp.printField(12345678UL + i, '\n');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
Serial.println(F("Test of println(double)"));
|
|
||||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
|
||||||
file.println((double)0.01 * i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
Serial.println(F("Test of printField(double, char)"));
|
|
||||||
for (uint16_t i = 0; i < N_PRINT; i++) {
|
|
||||||
bp.printField((double)0.01 * i, '\n');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (test & 1) {
|
|
||||||
bp.sync();
|
|
||||||
}
|
|
||||||
if (file.getWriteError()) {
|
|
||||||
sd.errorHalt(&Serial, F("write failed"));
|
|
||||||
}
|
|
||||||
double s = file.fileSize();
|
|
||||||
file.close();
|
|
||||||
t = millis() - t;
|
|
||||||
Serial.print(F("Time "));
|
|
||||||
Serial.print(0.001 * t, 3);
|
|
||||||
Serial.println(F(" sec"));
|
|
||||||
Serial.print(F("File size "));
|
|
||||||
Serial.print(0.001 * s);
|
|
||||||
Serial.println(F(" KB"));
|
|
||||||
Serial.print(F("Write "));
|
|
||||||
Serial.print(s / t);
|
|
||||||
Serial.println(F(" KB/sec"));
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void testMemberFunctions() {
|
|
||||||
BufferedPrint<Print, 32> bp(&Serial);
|
|
||||||
char c = 'c'; // char
|
|
||||||
//#define BASIC_TYPES
|
|
||||||
#ifdef BASIC_TYPES
|
|
||||||
signed char sc = -1; // signed 8-bit
|
|
||||||
unsigned char uc = 1; // unsiged 8-bit
|
|
||||||
signed short ss = -2; // signed 16-bit
|
|
||||||
unsigned short us = 2; // unsigned 16-bit
|
|
||||||
signed long sl = -4; // signed 32-bit
|
|
||||||
unsigned long ul = 4; // unsigned 32-bit
|
|
||||||
#else // BASIC_TYPES
|
|
||||||
int8_t sc = -1; // signed 8-bit
|
|
||||||
uint8_t uc = 1; // unsiged 8-bit
|
|
||||||
int16_t ss = -2; // signed 16-bit
|
|
||||||
uint16_t us = 2; // unsigned 16-bit
|
|
||||||
int32_t sl = -4; // signed 32-bit
|
|
||||||
uint32_t ul = 4; // unsigned 32-bit
|
|
||||||
#endif // BASIC_TYPES
|
|
||||||
float f = -1.234;
|
|
||||||
double d = -5.678;
|
|
||||||
bp.println();
|
|
||||||
bp.println("Test print()");
|
|
||||||
bp.print(c);
|
|
||||||
bp.println();
|
|
||||||
bp.print("string");
|
|
||||||
bp.println();
|
|
||||||
bp.print(F("flash"));
|
|
||||||
bp.println();
|
|
||||||
bp.print(sc);
|
|
||||||
bp.println();
|
|
||||||
bp.print(uc);
|
|
||||||
bp.println();
|
|
||||||
bp.print(ss);
|
|
||||||
bp.println();
|
|
||||||
bp.print(us);
|
|
||||||
bp.println();
|
|
||||||
bp.print(sl);
|
|
||||||
bp.println();
|
|
||||||
bp.print(ul);
|
|
||||||
bp.println();
|
|
||||||
bp.print(f);
|
|
||||||
bp.println();
|
|
||||||
bp.print(d);
|
|
||||||
bp.println();
|
|
||||||
bp.println();
|
|
||||||
|
|
||||||
bp.println("Test println()");
|
|
||||||
bp.println(c);
|
|
||||||
bp.println("string");
|
|
||||||
bp.println(F("flash"));
|
|
||||||
bp.println(sc);
|
|
||||||
bp.println(uc);
|
|
||||||
bp.println(ss);
|
|
||||||
bp.println(us);
|
|
||||||
bp.println(sl);
|
|
||||||
bp.println(ul);
|
|
||||||
bp.println(f);
|
|
||||||
bp.println(d);
|
|
||||||
bp.println();
|
|
||||||
|
|
||||||
bp.println("Test printField()");
|
|
||||||
bp.printField(c, ',');
|
|
||||||
bp.printField("string", ',');
|
|
||||||
bp.printField(F("flash"), ',');
|
|
||||||
bp.printField(sc, ',');
|
|
||||||
bp.printField(uc, ',');
|
|
||||||
bp.printField(ss, ',');
|
|
||||||
bp.printField(us, ',');
|
|
||||||
bp.printField(sl, ',');
|
|
||||||
bp.printField(ul, ',');
|
|
||||||
bp.printField(f, ',');
|
|
||||||
bp.printField(d, '\n');
|
|
||||||
|
|
||||||
bp.sync();
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
}
|
|
||||||
Serial.println("Type any character to begin.");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
}
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
Serial.println(F("Test member funcions:"));
|
|
||||||
testMemberFunctions();
|
|
||||||
Serial.println();
|
|
||||||
Serial.println(
|
|
||||||
F("Benchmark performance for uint16_t, uint32_t, and double:"));
|
|
||||||
benchmark();
|
|
||||||
Serial.println("Done");
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -1,95 +1,62 @@
|
||||||
/*
|
/*
|
||||||
* Example use of chdir(), ls(), mkdir(), and rmdir().
|
* Example use of chdir(), ls(), mkdir(), and rmdir().
|
||||||
*/
|
*/
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#include "sdios.h"
|
#include "sdios.h"
|
||||||
|
// SD card chip select pin.
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
const uint8_t chipSelect = SS;
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI and
|
|
||||||
your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
// File system object.
|
||||||
SdFat sd;
|
SdFat sd;
|
||||||
File file;
|
|
||||||
File root;
|
// Directory file.
|
||||||
#elif SD_FAT_TYPE == 1
|
SdFile root;
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
// Use for file creation in folders.
|
||||||
File32 root;
|
SdFile file;
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
ExFile root;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
FsFile root;
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
// Create a Serial output stream.
|
// Create a Serial output stream.
|
||||||
ArduinoOutStream cout(Serial);
|
ArduinoOutStream cout(Serial);
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Store error strings in flash to save RAM.
|
// Buffer for Serial input.
|
||||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
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() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
delay(1000);
|
delay(1000);
|
||||||
cout << F("Type any character to start\n");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the SD card.
|
cout << F("Type any character to start\n");
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
// Wait for input line and discard.
|
||||||
sd.initErrorHalt(&Serial);
|
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") ||
|
if (sd.exists("Folder1")
|
||||||
sd.exists("Folder1/File2.txt")) {
|
|| sd.exists("Folder1/file1.txt")
|
||||||
|
|| sd.exists("Folder1/File2.txt")) {
|
||||||
error("Please remove existing Folder1, file1.txt, and File2.txt");
|
error("Please remove existing Folder1, file1.txt, and File2.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
int rootFileCount = 0;
|
int rootFileCount = 0;
|
||||||
if (!root.open("/")) {
|
if (!root.open("/")) {
|
||||||
error("open root");
|
error("open root failed");
|
||||||
}
|
}
|
||||||
while (file.openNext(&root, O_RDONLY)) {
|
while (file.openNext(&root, O_RDONLY)) {
|
||||||
if (!file.isHidden()) {
|
if (!file.isHidden()) {
|
||||||
|
|
@ -151,6 +118,7 @@ void setup() {
|
||||||
if (!sd.rmdir("Folder1")) {
|
if (!sd.rmdir("Folder1")) {
|
||||||
error("rmdir for Folder1 failed\n");
|
error("rmdir for Folder1 failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << F("\nFolder1 removed.\n");
|
cout << F("\nFolder1 removed.\n");
|
||||||
cout << F("\nList of files on the SD.\n");
|
cout << F("\nList of files on the SD.\n");
|
||||||
sd.ls(LS_R);
|
sd.ls(LS_R);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
// Avoid IDE problems by defining struct in septate .h file.
|
|
||||||
// Pad record so size is a power of two for best write performance.
|
|
||||||
#ifndef ExFatLogger_h
|
|
||||||
#define ExFatLogger_h
|
|
||||||
const size_t ADC_COUNT = 4;
|
|
||||||
struct data_t {
|
|
||||||
uint16_t adc[ADC_COUNT];
|
|
||||||
};
|
|
||||||
#endif // ExFatLogger_h
|
|
||||||
|
|
@ -1,601 +0,0 @@
|
||||||
// Example to demonstrate write latency for preallocated exFAT files.
|
|
||||||
// I suggest you write a PC program to convert very large bin files.
|
|
||||||
//
|
|
||||||
// The maximum data rate will depend on the quality of your SD,
|
|
||||||
// the size of the FIFO, and using dedicated SPI.
|
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
|
||||||
#include "SdFat.h"
|
|
||||||
#include "ExFatLogger.h"
|
|
||||||
#include "FreeStack.h"
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// This example was designed for exFAT but will support FAT16/FAT32.
|
|
||||||
// Note: Uno will not support SD_FAT_TYPE = 3.
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 2
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Interval between data records in microseconds.
|
|
||||||
// Try 250 with Teensy 3.6, Due, or STM32.
|
|
||||||
// Try 2000 with AVR boards.
|
|
||||||
// Try 4000 with SAMD Zero boards.
|
|
||||||
const uint32_t LOG_INTERVAL_USEC = 2000;
|
|
||||||
|
|
||||||
// Set USE_RTC nonzero for file timestamps.
|
|
||||||
// RAM use will be marginal on Uno with RTClib.
|
|
||||||
// 0 - RTC not used
|
|
||||||
// 1 - DS1307
|
|
||||||
// 2 - DS3231
|
|
||||||
// 3 - PCF8523
|
|
||||||
#define USE_RTC 0
|
|
||||||
#if USE_RTC
|
|
||||||
#include "RTClib.h"
|
|
||||||
#endif // USE_RTC
|
|
||||||
|
|
||||||
// LED to light if overruns occur.
|
|
||||||
#define ERROR_LED_PIN -1
|
|
||||||
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI and
|
|
||||||
your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// FIFO SIZE - 512 byte sectors. Modify for your board.
|
|
||||||
#ifdef __AVR_ATmega328P__
|
|
||||||
// Use 512 bytes for 328 boards.
|
|
||||||
#define FIFO_SIZE_SECTORS 1
|
|
||||||
#elif defined(__AVR__)
|
|
||||||
// Use 2 KiB for other AVR boards.
|
|
||||||
#define FIFO_SIZE_SECTORS 4
|
|
||||||
#else // __AVR_ATmega328P__
|
|
||||||
// Use 8 KiB for non-AVR boards.
|
|
||||||
#define FIFO_SIZE_SECTORS 16
|
|
||||||
#endif // __AVR_ATmega328P__
|
|
||||||
|
|
||||||
// Preallocate 1GiB file.
|
|
||||||
const uint32_t PREALLOCATE_SIZE_MiB = 1024UL;
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
|
|
||||||
// Save SRAM if 328.
|
|
||||||
#ifdef __AVR_ATmega328P__
|
|
||||||
#include "MinimumSerial.h"
|
|
||||||
MinimumSerial MinSerial;
|
|
||||||
#define Serial MinSerial
|
|
||||||
#endif // __AVR_ATmega328P__
|
|
||||||
//==============================================================================
|
|
||||||
// Replace logRecord(), printRecord(), and ExFatLogger.h for your sensors.
|
|
||||||
void logRecord(data_t* data, uint16_t overrun) {
|
|
||||||
if (overrun) {
|
|
||||||
// Add one since this record has no adc data. Could add overrun field.
|
|
||||||
overrun++;
|
|
||||||
data->adc[0] = 0X8000 | overrun;
|
|
||||||
} else {
|
|
||||||
for (size_t i = 0; i < ADC_COUNT; i++) {
|
|
||||||
data->adc[i] = analogRead(A0 + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printRecord(Print* pr, data_t* data) {
|
|
||||||
static uint32_t nr = 0;
|
|
||||||
if (!data) {
|
|
||||||
pr->print(F("LOG_INTERVAL_USEC,"));
|
|
||||||
pr->println(LOG_INTERVAL_USEC);
|
|
||||||
pr->print(F("rec#"));
|
|
||||||
for (size_t i = 0; i < ADC_COUNT; i++) {
|
|
||||||
pr->print(F(",adc"));
|
|
||||||
pr->print(i);
|
|
||||||
}
|
|
||||||
pr->println();
|
|
||||||
nr = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data->adc[0] & 0X8000) {
|
|
||||||
uint16_t n = data->adc[0] & 0X7FFF;
|
|
||||||
nr += n;
|
|
||||||
pr->print(F("-1,"));
|
|
||||||
pr->print(n);
|
|
||||||
pr->println(F(",overuns"));
|
|
||||||
} else {
|
|
||||||
pr->print(nr++);
|
|
||||||
for (size_t i = 0; i < ADC_COUNT; i++) {
|
|
||||||
pr->write(',');
|
|
||||||
pr->print(data->adc[i]);
|
|
||||||
}
|
|
||||||
pr->println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//==============================================================================
|
|
||||||
const uint64_t PREALLOCATE_SIZE = (uint64_t)PREALLOCATE_SIZE_MiB << 20;
|
|
||||||
// Max length of file name including zero byte.
|
|
||||||
#define FILE_NAME_DIM 40
|
|
||||||
// Max number of records to buffer while SD is busy.
|
|
||||||
const size_t FIFO_DIM = 512 * FIFO_SIZE_SECTORS / sizeof(data_t);
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
typedef SdFat sd_t;
|
|
||||||
typedef File file_t;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
typedef SdFat32 sd_t;
|
|
||||||
typedef File32 file_t;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
typedef SdExFat sd_t;
|
|
||||||
typedef ExFile file_t;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
typedef SdFs sd_t;
|
|
||||||
typedef FsFile file_t;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
sd_t sd;
|
|
||||||
|
|
||||||
file_t binFile;
|
|
||||||
file_t csvFile;
|
|
||||||
// You may modify the filename. Digits before the dot are file versions.
|
|
||||||
char binName[] = "ExFatLogger00.bin";
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#if USE_RTC
|
|
||||||
#if USE_RTC == 1
|
|
||||||
RTC_DS1307 rtc;
|
|
||||||
#elif USE_RTC == 2
|
|
||||||
RTC_DS3231 rtc;
|
|
||||||
#elif USE_RTC == 3
|
|
||||||
RTC_PCF8523 rtc;
|
|
||||||
#else // USE_RTC == type
|
|
||||||
#error USE_RTC type not implemented.
|
|
||||||
#endif // USE_RTC == type
|
|
||||||
// Call back for file timestamps. Only called for file create and sync().
|
|
||||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
|
||||||
DateTime now = rtc.now();
|
|
||||||
|
|
||||||
// Return date using FS_DATE macro to format fields.
|
|
||||||
*date = FS_DATE(now.year(), now.month(), now.day());
|
|
||||||
|
|
||||||
// Return time using FS_TIME macro to format fields.
|
|
||||||
*time = FS_TIME(now.hour(), now.minute(), now.second());
|
|
||||||
|
|
||||||
// Return low time bits in units of 10 ms.
|
|
||||||
*ms10 = now.second() & 1 ? 100 : 0;
|
|
||||||
}
|
|
||||||
#endif // USE_RTC
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
|
||||||
#define dbgAssert(e) ((e) ? (void)0 : error("assert " #e))
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Convert binary file to csv file.
|
|
||||||
void binaryToCsv() {
|
|
||||||
uint8_t lastPct = 0;
|
|
||||||
uint32_t t0 = millis();
|
|
||||||
data_t binData[FIFO_DIM];
|
|
||||||
|
|
||||||
if (!binFile.seekSet(512)) {
|
|
||||||
error("binFile.seek failed");
|
|
||||||
}
|
|
||||||
uint32_t tPct = millis();
|
|
||||||
printRecord(&csvFile, nullptr);
|
|
||||||
while (!Serial.available() && binFile.available()) {
|
|
||||||
int nb = binFile.read(binData, sizeof(binData));
|
|
||||||
if (nb <= 0) {
|
|
||||||
error("read binFile failed");
|
|
||||||
}
|
|
||||||
size_t nr = nb / sizeof(data_t);
|
|
||||||
for (size_t i = 0; i < nr; i++) {
|
|
||||||
printRecord(&csvFile, &binData[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((millis() - tPct) > 1000) {
|
|
||||||
uint8_t pct = binFile.curPosition() / (binFile.fileSize() / 100);
|
|
||||||
if (pct != lastPct) {
|
|
||||||
tPct = millis();
|
|
||||||
lastPct = pct;
|
|
||||||
Serial.print(pct, DEC);
|
|
||||||
Serial.println('%');
|
|
||||||
csvFile.sync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Serial.available()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
csvFile.close();
|
|
||||||
Serial.print(F("Done: "));
|
|
||||||
Serial.print(0.001 * (millis() - t0));
|
|
||||||
Serial.println(F(" Seconds"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void clearSerialInput() {
|
|
||||||
uint32_t m = micros();
|
|
||||||
do {
|
|
||||||
if (Serial.read() >= 0) {
|
|
||||||
m = micros();
|
|
||||||
}
|
|
||||||
} while (micros() - m < 10000);
|
|
||||||
}
|
|
||||||
//-------------------------------------------------------------------------------
|
|
||||||
void createBinFile() {
|
|
||||||
binFile.close();
|
|
||||||
while (sd.exists(binName)) {
|
|
||||||
char* p = strchr(binName, '.');
|
|
||||||
if (!p) {
|
|
||||||
error("no dot in filename");
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
p--;
|
|
||||||
if (p < binName || *p < '0' || *p > '9') {
|
|
||||||
error("Can't create file name");
|
|
||||||
}
|
|
||||||
if (p[0] != '9') {
|
|
||||||
p[0]++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p[0] = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!binFile.open(binName, O_RDWR | O_CREAT)) {
|
|
||||||
error("open binName failed");
|
|
||||||
}
|
|
||||||
Serial.println(binName);
|
|
||||||
if (!binFile.preAllocate(PREALLOCATE_SIZE)) {
|
|
||||||
error("preAllocate failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.print(F("preAllocated: "));
|
|
||||||
Serial.print(PREALLOCATE_SIZE_MiB);
|
|
||||||
Serial.println(F(" MiB"));
|
|
||||||
}
|
|
||||||
//-------------------------------------------------------------------------------
|
|
||||||
bool createCsvFile() {
|
|
||||||
char csvName[FILE_NAME_DIM];
|
|
||||||
if (!binFile.isOpen()) {
|
|
||||||
Serial.println(F("No current binary file"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new csvFile.
|
|
||||||
binFile.getName(csvName, sizeof(csvName));
|
|
||||||
char* dot = strchr(csvName, '.');
|
|
||||||
if (!dot) {
|
|
||||||
error("no dot in filename");
|
|
||||||
}
|
|
||||||
strcpy(dot + 1, "csv");
|
|
||||||
if (!csvFile.open(csvName, O_WRONLY | O_CREAT | O_TRUNC)) {
|
|
||||||
error("open csvFile failed");
|
|
||||||
}
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.print(F("Writing: "));
|
|
||||||
Serial.print(csvName);
|
|
||||||
Serial.println(F(" - type any character to stop"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//-------------------------------------------------------------------------------
|
|
||||||
void logData() {
|
|
||||||
int32_t delta; // Jitter in log time.
|
|
||||||
int32_t maxDelta = 0;
|
|
||||||
uint32_t maxLogMicros = 0;
|
|
||||||
uint32_t maxWriteMicros = 0;
|
|
||||||
size_t maxFifoUse = 0;
|
|
||||||
size_t fifoCount = 0;
|
|
||||||
size_t fifoHead = 0;
|
|
||||||
size_t fifoTail = 0;
|
|
||||||
uint16_t overrun = 0;
|
|
||||||
uint16_t maxOverrun = 0;
|
|
||||||
uint32_t totalOverrun = 0;
|
|
||||||
uint32_t fifoBuf[128 * FIFO_SIZE_SECTORS];
|
|
||||||
data_t* fifoData = (data_t*)fifoBuf;
|
|
||||||
|
|
||||||
// Write dummy sector to start multi-block write.
|
|
||||||
dbgAssert(sizeof(fifoBuf) >= 512);
|
|
||||||
memset(fifoBuf, 0, sizeof(fifoBuf));
|
|
||||||
if (binFile.write(fifoBuf, 512) != 512) {
|
|
||||||
error("write first sector failed");
|
|
||||||
}
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println(F("Type any character to stop"));
|
|
||||||
|
|
||||||
// Wait until SD is not busy.
|
|
||||||
while (sd.card()->isBusy()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start time for log file.
|
|
||||||
uint32_t m = millis();
|
|
||||||
|
|
||||||
// Time to log next record.
|
|
||||||
uint32_t logTime = micros();
|
|
||||||
while (true) {
|
|
||||||
// Time for next data record.
|
|
||||||
logTime += LOG_INTERVAL_USEC;
|
|
||||||
|
|
||||||
// Wait until time to log data.
|
|
||||||
delta = micros() - logTime;
|
|
||||||
if (delta > 0) {
|
|
||||||
Serial.print(F("delta: "));
|
|
||||||
Serial.println(delta);
|
|
||||||
error("Rate too fast");
|
|
||||||
}
|
|
||||||
while (delta < 0) {
|
|
||||||
delta = micros() - logTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fifoCount < FIFO_DIM) {
|
|
||||||
uint32_t m = micros();
|
|
||||||
logRecord(fifoData + fifoHead, overrun);
|
|
||||||
m = micros() - m;
|
|
||||||
if (m > maxLogMicros) {
|
|
||||||
maxLogMicros = m;
|
|
||||||
}
|
|
||||||
fifoHead = fifoHead < (FIFO_DIM - 1) ? fifoHead + 1 : 0;
|
|
||||||
fifoCount++;
|
|
||||||
if (overrun) {
|
|
||||||
if (overrun > maxOverrun) {
|
|
||||||
maxOverrun = overrun;
|
|
||||||
}
|
|
||||||
overrun = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
totalOverrun++;
|
|
||||||
overrun++;
|
|
||||||
if (overrun > 0XFFF) {
|
|
||||||
error("too many overruns");
|
|
||||||
}
|
|
||||||
if (ERROR_LED_PIN >= 0) {
|
|
||||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Save max jitter.
|
|
||||||
if (delta > maxDelta) {
|
|
||||||
maxDelta = delta;
|
|
||||||
}
|
|
||||||
// Write data if SD is not busy.
|
|
||||||
if (!sd.card()->isBusy()) {
|
|
||||||
size_t nw = fifoHead > fifoTail ? fifoCount : FIFO_DIM - fifoTail;
|
|
||||||
// Limit write time by not writing more than 512 bytes.
|
|
||||||
const size_t MAX_WRITE = 512 / sizeof(data_t);
|
|
||||||
if (nw > MAX_WRITE) nw = MAX_WRITE;
|
|
||||||
size_t nb = nw * sizeof(data_t);
|
|
||||||
uint32_t usec = micros();
|
|
||||||
if (nb != binFile.write(fifoData + fifoTail, nb)) {
|
|
||||||
error("write binFile failed");
|
|
||||||
}
|
|
||||||
usec = micros() - usec;
|
|
||||||
if (usec > maxWriteMicros) {
|
|
||||||
maxWriteMicros = usec;
|
|
||||||
}
|
|
||||||
fifoTail = (fifoTail + nw) < FIFO_DIM ? fifoTail + nw : 0;
|
|
||||||
if (fifoCount > maxFifoUse) {
|
|
||||||
maxFifoUse = fifoCount;
|
|
||||||
}
|
|
||||||
fifoCount -= nw;
|
|
||||||
if (Serial.available()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.print(F("\nLog time: "));
|
|
||||||
Serial.print(0.001 * (millis() - m));
|
|
||||||
Serial.println(F(" Seconds"));
|
|
||||||
binFile.truncate();
|
|
||||||
binFile.sync();
|
|
||||||
Serial.print(("File size: "));
|
|
||||||
// Warning cast used for print since fileSize is uint64_t.
|
|
||||||
Serial.print((uint32_t)binFile.fileSize());
|
|
||||||
Serial.println(F(" bytes"));
|
|
||||||
Serial.print(F("totalOverrun: "));
|
|
||||||
Serial.println(totalOverrun);
|
|
||||||
Serial.print(F("FIFO_DIM: "));
|
|
||||||
Serial.println(FIFO_DIM);
|
|
||||||
Serial.print(F("maxFifoUse: "));
|
|
||||||
Serial.println(maxFifoUse);
|
|
||||||
Serial.print(F("maxLogMicros: "));
|
|
||||||
Serial.println(maxLogMicros);
|
|
||||||
Serial.print(F("maxWriteMicros: "));
|
|
||||||
Serial.println(maxWriteMicros);
|
|
||||||
Serial.print(F("Log interval: "));
|
|
||||||
Serial.print(LOG_INTERVAL_USEC);
|
|
||||||
Serial.print(F(" micros\nmaxDelta: "));
|
|
||||||
Serial.print(maxDelta);
|
|
||||||
Serial.println(F(" micros"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void openBinFile() {
|
|
||||||
char name[FILE_NAME_DIM];
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println(F("Enter file name"));
|
|
||||||
if (!serialReadLine(name, sizeof(name))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!sd.exists(name)) {
|
|
||||||
Serial.println(name);
|
|
||||||
Serial.println(F("File does not exist"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
binFile.close();
|
|
||||||
if (!binFile.open(name, O_RDONLY)) {
|
|
||||||
Serial.println(name);
|
|
||||||
Serial.println(F("open failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial.println(F("File opened"));
|
|
||||||
}
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void printData() {
|
|
||||||
if (!binFile.isOpen()) {
|
|
||||||
Serial.println(F("No current binary file"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Skip first dummy sector.
|
|
||||||
if (!binFile.seekSet(512)) {
|
|
||||||
error("seek failed");
|
|
||||||
}
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println(F("type any character to stop\n"));
|
|
||||||
delay(1000);
|
|
||||||
printRecord(&Serial, nullptr);
|
|
||||||
while (binFile.available() && !Serial.available()) {
|
|
||||||
data_t record;
|
|
||||||
if (binFile.read(&record, sizeof(data_t)) != sizeof(data_t)) {
|
|
||||||
error("read binFile failed");
|
|
||||||
}
|
|
||||||
printRecord(&Serial, &record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printUnusedStack() {
|
|
||||||
#if HAS_UNUSED_STACK
|
|
||||||
Serial.print(F("\nUnused stack: "));
|
|
||||||
Serial.println(UnusedStack());
|
|
||||||
#endif // HAS_UNUSED_STACK
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool serialReadLine(char* str, size_t size) {
|
|
||||||
size_t n = 0;
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
int c = Serial.read();
|
|
||||||
if (c < ' ') break;
|
|
||||||
str[n++] = c;
|
|
||||||
if (n >= size) {
|
|
||||||
Serial.println(F("input too long"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t m = millis();
|
|
||||||
while (!Serial.available() && (millis() - m) < 100) {
|
|
||||||
}
|
|
||||||
if (!Serial.available()) break;
|
|
||||||
}
|
|
||||||
str[n] = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void testSensor() {
|
|
||||||
const uint32_t interval = 200000;
|
|
||||||
int32_t diff;
|
|
||||||
data_t data;
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println(F("\nTesting - type any character to stop\n"));
|
|
||||||
delay(1000);
|
|
||||||
printRecord(&Serial, nullptr);
|
|
||||||
uint32_t m = micros();
|
|
||||||
while (!Serial.available()) {
|
|
||||||
m += interval;
|
|
||||||
do {
|
|
||||||
diff = m - micros();
|
|
||||||
} while (diff > 0);
|
|
||||||
logRecord(&data, 0);
|
|
||||||
printRecord(&Serial, &data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
if (ERROR_LED_PIN >= 0) {
|
|
||||||
pinMode(ERROR_LED_PIN, OUTPUT);
|
|
||||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
|
||||||
}
|
|
||||||
Serial.begin(9600);
|
|
||||||
|
|
||||||
// Wait for USB Serial
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
delay(1000);
|
|
||||||
Serial.println(F("Type any character to begin"));
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
FillStack();
|
|
||||||
#if !ENABLE_DEDICATED_SPI
|
|
||||||
Serial.println(
|
|
||||||
F("\nFor best performance edit SdFatConfig.h\n"
|
|
||||||
"and set ENABLE_DEDICATED_SPI nonzero"));
|
|
||||||
#endif // !ENABLE_DEDICATED_SPI
|
|
||||||
|
|
||||||
Serial.print(FIFO_DIM);
|
|
||||||
Serial.println(F(" FIFO entries will be used."));
|
|
||||||
|
|
||||||
// Initialize SD.
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
#if USE_RTC
|
|
||||||
if (!rtc.begin()) {
|
|
||||||
error("rtc.begin failed");
|
|
||||||
}
|
|
||||||
if (!rtc.isrunning()) {
|
|
||||||
// Set RTC to sketch compile date & time.
|
|
||||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
|
||||||
error("RTC is NOT running!");
|
|
||||||
}
|
|
||||||
// Set callback
|
|
||||||
FsDateTime::setCallback(dateTime);
|
|
||||||
#endif // USE_RTC
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {
|
|
||||||
printUnusedStack();
|
|
||||||
// Read any Serial data.
|
|
||||||
clearSerialInput();
|
|
||||||
|
|
||||||
if (ERROR_LED_PIN >= 0) {
|
|
||||||
digitalWrite(ERROR_LED_PIN, LOW);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
Serial.println(F("type: "));
|
|
||||||
Serial.println(F("b - open existing bin file"));
|
|
||||||
Serial.println(F("c - convert file to csv"));
|
|
||||||
Serial.println(F("l - list files"));
|
|
||||||
Serial.println(F("p - print data to Serial"));
|
|
||||||
Serial.println(F("r - record data"));
|
|
||||||
Serial.println(F("t - test without logging"));
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
char c = tolower(Serial.read());
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
if (c == 'b') {
|
|
||||||
openBinFile();
|
|
||||||
} else if (c == 'c') {
|
|
||||||
if (createCsvFile()) {
|
|
||||||
binaryToCsv();
|
|
||||||
}
|
|
||||||
} else if (c == 'l') {
|
|
||||||
Serial.println(F("ls:"));
|
|
||||||
sd.ls(&Serial, LS_DATE | LS_SIZE);
|
|
||||||
} else if (c == 'p') {
|
|
||||||
printData();
|
|
||||||
} else if (c == 'r') {
|
|
||||||
createBinFile();
|
|
||||||
logData();
|
|
||||||
} else if (c == 't') {
|
|
||||||
testSensor();
|
|
||||||
} else {
|
|
||||||
Serial.println(F("Invalid entry"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -72,7 +72,7 @@ void loop() {
|
||||||
Serial.print(F("\r\nEnter File Number: "));
|
Serial.print(F("\r\nEnter File Number: "));
|
||||||
|
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
c = Serial.read();
|
c = Serial.read();
|
||||||
uint8_t i = c - '0';
|
uint8_t i = c - '0';
|
||||||
|
|
@ -123,7 +123,7 @@ struct block_t {
|
||||||
//
|
//
|
||||||
void fatalBlink() {
|
void fatalBlink() {
|
||||||
while (true) {
|
while (true) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
if (ERROR_LED_PIN >= 0) {
|
if (ERROR_LED_PIN >= 0) {
|
||||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||||
delay(200);
|
delay(200);
|
||||||
|
|
@ -321,7 +321,7 @@ void openBinFile() {
|
||||||
Serial.write(name, BASE_NAME_SIZE);
|
Serial.write(name, BASE_NAME_SIZE);
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
char c = Serial.read();
|
char c = Serial.read();
|
||||||
Serial.write(c);
|
Serial.write(c);
|
||||||
|
|
@ -566,7 +566,7 @@ void setup(void) {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.print(F("\nFreeStack: "));
|
Serial.print(F("\nFreeStack: "));
|
||||||
Serial.println(FreeStack());
|
Serial.println(FreeStack());
|
||||||
|
|
@ -592,7 +592,7 @@ void setup(void) {
|
||||||
if (sd.exists(TMP_FILE_NAME)) {
|
if (sd.exists(TMP_FILE_NAME)) {
|
||||||
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
if (Serial.read() == 'Y') {
|
if (Serial.read() == 'Y') {
|
||||||
recoverTmpFile();
|
recoverTmpFile();
|
||||||
|
|
@ -617,11 +617,11 @@ void loop(void) {
|
||||||
Serial.println(F("r - record data"));
|
Serial.println(F("r - record data"));
|
||||||
Serial.println(F("t - test without logging"));
|
Serial.println(F("t - test without logging"));
|
||||||
while(!Serial.available()) {
|
while(!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
#if WDT_YIELD_TIME_MICROS
|
#if WDT_YIELD_TIME_MICROS
|
||||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||||
while (true) {}
|
SysCall::halt();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char c = tolower(Serial.read());
|
char c = tolower(Serial.read());
|
||||||
|
|
@ -123,7 +123,7 @@ struct block_t {
|
||||||
//
|
//
|
||||||
void fatalBlink() {
|
void fatalBlink() {
|
||||||
while (true) {
|
while (true) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
if (ERROR_LED_PIN >= 0) {
|
if (ERROR_LED_PIN >= 0) {
|
||||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||||
delay(200);
|
delay(200);
|
||||||
|
|
@ -321,7 +321,7 @@ void openBinFile() {
|
||||||
Serial.write(name, BASE_NAME_SIZE);
|
Serial.write(name, BASE_NAME_SIZE);
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
char c = Serial.read();
|
char c = Serial.read();
|
||||||
Serial.write(c);
|
Serial.write(c);
|
||||||
|
|
@ -566,7 +566,7 @@ void setup(void) {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.print(F("\nFreeStack: "));
|
Serial.print(F("\nFreeStack: "));
|
||||||
Serial.println(FreeStack());
|
Serial.println(FreeStack());
|
||||||
|
|
@ -592,7 +592,7 @@ void setup(void) {
|
||||||
if (sd.exists(TMP_FILE_NAME)) {
|
if (sd.exists(TMP_FILE_NAME)) {
|
||||||
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
if (Serial.read() == 'Y') {
|
if (Serial.read() == 'Y') {
|
||||||
recoverTmpFile();
|
recoverTmpFile();
|
||||||
|
|
@ -617,11 +617,11 @@ void loop(void) {
|
||||||
Serial.println(F("r - record data"));
|
Serial.println(F("r - record data"));
|
||||||
Serial.println(F("t - test without logging"));
|
Serial.println(F("t - test without logging"));
|
||||||
while(!Serial.available()) {
|
while(!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
#if WDT_YIELD_TIME_MICROS
|
#if WDT_YIELD_TIME_MICROS
|
||||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||||
while (true) {}
|
SysCall::halt();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char c = tolower(Serial.read());
|
char c = tolower(Serial.read());
|
||||||
|
|
@ -123,7 +123,7 @@ struct block_t {
|
||||||
//
|
//
|
||||||
void fatalBlink() {
|
void fatalBlink() {
|
||||||
while (true) {
|
while (true) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
if (ERROR_LED_PIN >= 0) {
|
if (ERROR_LED_PIN >= 0) {
|
||||||
digitalWrite(ERROR_LED_PIN, HIGH);
|
digitalWrite(ERROR_LED_PIN, HIGH);
|
||||||
delay(200);
|
delay(200);
|
||||||
|
|
@ -321,7 +321,7 @@ void openBinFile() {
|
||||||
Serial.write(name, BASE_NAME_SIZE);
|
Serial.write(name, BASE_NAME_SIZE);
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
char c = Serial.read();
|
char c = Serial.read();
|
||||||
Serial.write(c);
|
Serial.write(c);
|
||||||
|
|
@ -566,7 +566,7 @@ void setup(void) {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.print(F("\nFreeStack: "));
|
Serial.print(F("\nFreeStack: "));
|
||||||
Serial.println(FreeStack());
|
Serial.println(FreeStack());
|
||||||
|
|
@ -592,7 +592,7 @@ void setup(void) {
|
||||||
if (sd.exists(TMP_FILE_NAME)) {
|
if (sd.exists(TMP_FILE_NAME)) {
|
||||||
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
if (Serial.read() == 'Y') {
|
if (Serial.read() == 'Y') {
|
||||||
recoverTmpFile();
|
recoverTmpFile();
|
||||||
|
|
@ -617,11 +617,11 @@ void loop(void) {
|
||||||
Serial.println(F("r - record data"));
|
Serial.println(F("r - record data"));
|
||||||
Serial.println(F("t - test without logging"));
|
Serial.println(F("t - test without logging"));
|
||||||
while(!Serial.available()) {
|
while(!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
#if WDT_YIELD_TIME_MICROS
|
#if WDT_YIELD_TIME_MICROS
|
||||||
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
|
||||||
while (true) {}
|
SysCall::halt();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char c = tolower(Serial.read());
|
char c = tolower(Serial.read());
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
// Create a text file on the SD with this path using short 8.3 names.
|
|
||||||
#define SFN_PATH "/DIR/TEST.TXT"
|
|
||||||
|
|
||||||
// Modify CS_PIN for your chip select pin.
|
|
||||||
#define CS_PIN SS
|
|
||||||
|
|
||||||
// Set USE_SD_H to one for SD.h or zero for SdFat.
|
|
||||||
#define USE_SD_H 0
|
|
||||||
|
|
||||||
#if USE_SD_H
|
|
||||||
#include "SD.h"
|
|
||||||
File file;
|
|
||||||
#else
|
|
||||||
#include "SdFat.h"
|
|
||||||
// Setting ENABLE_DEDICATED_SPI to zero saves over 200 more bytes.
|
|
||||||
#if ENABLE_DEDICATED_SPI
|
|
||||||
#warning \
|
|
||||||
"Set ENABLE_DEDICATED_SPI zero in SdFat/src/SdFatConfig.h for minimum size"
|
|
||||||
#endif // ENABLE_DEDICATED_SPI
|
|
||||||
// Insure FAT16/FAT32 only.
|
|
||||||
SdFat32 SD;
|
|
||||||
// FatFile does not support Stream functions, just simple read/write.
|
|
||||||
FatFile file;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void error(const char* msg) {
|
|
||||||
Serial.println(msg);
|
|
||||||
while (true) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void setup() {
|
|
||||||
int n;
|
|
||||||
char buf[4];
|
|
||||||
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
}
|
|
||||||
Serial.println("Type any character to begin");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SD.begin(CS_PIN)) error("SD.begin");
|
|
||||||
|
|
||||||
#if USE_SD_H
|
|
||||||
file = SD.open(SFN_PATH);
|
|
||||||
if (!file) error("open");
|
|
||||||
#else
|
|
||||||
// Open existing file with a path of 8.3 names.
|
|
||||||
// Directories will be opened O_RDONLY files O_RDWR.
|
|
||||||
if (!file.openExistingSFN(SFN_PATH)) error("open");
|
|
||||||
#endif
|
|
||||||
while ((n = file.read(buf, sizeof(buf)))) {
|
|
||||||
Serial.write(buf, n);
|
|
||||||
}
|
|
||||||
// close() is only needed if you write to the file. For example, read
|
|
||||||
// config data, modify the data, rewind the file and write the data.
|
|
||||||
// file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -1,92 +1,43 @@
|
||||||
/*
|
/*
|
||||||
* Print size, modify date/time, and name for all files in root.
|
* Print size, modify date/time, and name for all files in root.
|
||||||
*/
|
*/
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
// SD default chip select pin.
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
const uint8_t chipSelect = SS;
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI and
|
|
||||||
your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
// file system object
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
SdFat sd;
|
||||||
File dir;
|
|
||||||
File file;
|
SdFile root;
|
||||||
#elif SD_FAT_TYPE == 1
|
SdFile file;
|
||||||
SdFat32 sd;
|
|
||||||
File32 dir;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile dir;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile dir;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Store error strings in flash to save RAM.
|
|
||||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.println("Type any character to start");
|
Serial.println("Type any character to start");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the SD.
|
// Initialize at the highest speed supported by the board that is
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||||
sd.initErrorHalt(&Serial);
|
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
|
||||||
|
sd.initErrorHalt();
|
||||||
}
|
}
|
||||||
// Open root directory
|
if (!root.open("/")) {
|
||||||
if (!dir.open("/")) {
|
sd.errorHalt("open root failed");
|
||||||
error("dir.open failed");
|
|
||||||
}
|
}
|
||||||
// Open next file in root.
|
// Open next file in root.
|
||||||
// Warning, openNext starts at the current position of dir so a
|
// Warning, openNext starts at the current directory position
|
||||||
// rewind may be necessary in your application.
|
// so a rewind of the directory may be required.
|
||||||
while (file.openNext(&dir, O_RDONLY)) {
|
while (file.openNext(&root, O_RDONLY)) {
|
||||||
file.printFileSize(&Serial);
|
file.printFileSize(&Serial);
|
||||||
Serial.write(' ');
|
Serial.write(' ');
|
||||||
file.printModifyDateTime(&Serial);
|
file.printModifyDateTime(&Serial);
|
||||||
|
|
@ -99,7 +50,7 @@ void setup() {
|
||||||
Serial.println();
|
Serial.println();
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
if (dir.getError()) {
|
if (root.getError()) {
|
||||||
Serial.println("openNext failed");
|
Serial.println("openNext failed");
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Done!");
|
Serial.println("Done!");
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
@ -44,7 +44,7 @@ void loop() {
|
||||||
// F stores strings in flash to save RAM
|
// F stores strings in flash to save RAM
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
delay(400); // catch Due reset problem
|
delay(400); // catch Due reset problem
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ void loop() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
file.printField(12345678UL + i, '\n');
|
file.printField((uint32_t) (12345678UL + i), '\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
// Quick hardware test for SPI card access.
|
// Quick hardware test for SPI card access.
|
||||||
//
|
//
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
|
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#include "sdios.h"
|
#include "sdios.h"
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
//
|
//
|
||||||
// Set DISABLE_CHIP_SELECT to disable a second SPI device.
|
// Set DISABLE_CHIP_SELECT to disable a second SPI device.
|
||||||
// For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
|
// For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
|
||||||
|
|
@ -19,21 +14,9 @@ const int8_t DISABLE_CHIP_SELECT = -1;
|
||||||
// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance.
|
// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance.
|
||||||
#define SPI_SPEED SD_SCK_MHZ(4)
|
#define SPI_SPEED SD_SCK_MHZ(4)
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#if SD_FAT_TYPE == 0
|
// File system object.
|
||||||
SdFat sd;
|
SdFat sd;
|
||||||
File file;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
// Serial streams
|
// Serial streams
|
||||||
ArduinoOutStream cout(Serial);
|
ArduinoOutStream cout(Serial);
|
||||||
|
|
||||||
|
|
@ -49,15 +32,6 @@ void cardOrSpeed() {
|
||||||
cout << F("Edit SPI_SPEED in this program to change it.\n");
|
cout << F("Edit SPI_SPEED in this program to change it.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearSerialInput() {
|
|
||||||
uint32_t m = micros();
|
|
||||||
do {
|
|
||||||
if (Serial.read() >= 0) {
|
|
||||||
m = micros();
|
|
||||||
}
|
|
||||||
} while (micros() - m < 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reformatMsg() {
|
void reformatMsg() {
|
||||||
cout << F("Try reformatting the card. For best results use\n");
|
cout << F("Try reformatting the card. For best results use\n");
|
||||||
cout << F("the SdFormatter program in SdFat/examples or download\n");
|
cout << F("the SdFormatter program in SdFat/examples or download\n");
|
||||||
|
|
@ -69,16 +43,13 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
cout << F("\nSPI pins:\n");
|
cout << F("\nSPI pins:\n");
|
||||||
cout << F("MISO: ") << int(MISO) << endl;
|
cout << F("MISO: ") << int(MISO) << endl;
|
||||||
cout << F("MOSI: ") << int(MOSI) << endl;
|
cout << F("MOSI: ") << int(MOSI) << endl;
|
||||||
cout << F("SCK: ") << int(SCK) << endl;
|
cout << F("SCK: ") << int(SCK) << endl;
|
||||||
cout << F("SS: ") << int(SS) << endl;
|
cout << F("SS: ") << int(SS) << endl;
|
||||||
#ifdef SDCARD_SS_PIN
|
|
||||||
cout << F("SDCARD_SS_PIN: ") << int(SDCARD_SS_PIN) << endl;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
if (DISABLE_CHIP_SELECT < 0) {
|
if (DISABLE_CHIP_SELECT < 0) {
|
||||||
cout << F(
|
cout << F(
|
||||||
|
|
@ -98,7 +69,9 @@ void setup() {
|
||||||
bool firstTry = true;
|
bool firstTry = true;
|
||||||
void loop() {
|
void loop() {
|
||||||
// Read any existing Serial data.
|
// Read any existing Serial data.
|
||||||
clearSerialInput();
|
do {
|
||||||
|
delay(10);
|
||||||
|
} while (Serial.available() && Serial.read() >= 0);
|
||||||
|
|
||||||
if (!firstTry) {
|
if (!firstTry) {
|
||||||
cout << F("\nRestarting\n");
|
cout << F("\nRestarting\n");
|
||||||
|
|
@ -107,7 +80,7 @@ void loop() {
|
||||||
|
|
||||||
cout << F("\nEnter the chip select pin number: ");
|
cout << F("\nEnter the chip select pin number: ");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
cin.readline();
|
cin.readline();
|
||||||
if (cin >> chipSelect) {
|
if (cin >> chipSelect) {
|
||||||
|
|
@ -141,19 +114,18 @@ void loop() {
|
||||||
cout << dec << noshowbase << endl;
|
cout << dec << noshowbase << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cout << F("\nCard successfully initialized.\n");
|
|
||||||
if (sd.vol()->fatType() == 0) {
|
if (sd.vol()->fatType() == 0) {
|
||||||
cout << F("Can't find a valid FAT16/FAT32/exFAT partition.\n");
|
cout << F("Can't find a valid FAT16/FAT32 partition.\n");
|
||||||
reformatMsg();
|
reformatMsg();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cout << F("Can't determine error type\n");
|
cout << F("begin failed, can't determine error type\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cout << F("\nCard successfully initialized.\n");
|
cout << F("\nCard successfully initialized.\n");
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
||||||
uint32_t size = sd.card()->sectorCount();
|
uint32_t size = sd.card()->cardSize();
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
cout << F("Can't determine the card size.\n");
|
cout << F("Can't determine the card size.\n");
|
||||||
cardOrSpeed();
|
cardOrSpeed();
|
||||||
|
|
@ -163,19 +135,15 @@ void loop() {
|
||||||
cout << F("Card size: ") << sizeMB;
|
cout << F("Card size: ") << sizeMB;
|
||||||
cout << F(" MB (MB = 1,000,000 bytes)\n");
|
cout << F(" MB (MB = 1,000,000 bytes)\n");
|
||||||
cout << endl;
|
cout << endl;
|
||||||
if (sd.fatType() <= 32) {
|
cout << F("Volume is FAT") << int(sd.vol()->fatType());
|
||||||
cout << F("\nVolume is FAT") << int(sd.fatType());
|
cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster();
|
||||||
} else {
|
|
||||||
cout << F("\nVolume is exFAT");
|
|
||||||
}
|
|
||||||
cout << F(", Cluster size (bytes): ") << sd.vol()->bytesPerCluster();
|
|
||||||
cout << endl << endl;
|
cout << endl << endl;
|
||||||
|
|
||||||
cout << F("Files found (date time size name):\n");
|
cout << F("Files found (date time size name):\n");
|
||||||
sd.ls(LS_R | LS_DATE | LS_SIZE);
|
sd.ls(LS_R | LS_DATE | LS_SIZE);
|
||||||
|
|
||||||
if ((sizeMB > 1100 && sd.vol()->sectorsPerCluster() < 64) ||
|
if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64)
|
||||||
(sizeMB < 2200 && sd.vol()->fatType() == 32)) {
|
|| (sizeMB < 2200 && sd.vol()->fatType() == 32)) {
|
||||||
cout << F("\nThis card should be reformatted for best performance.\n");
|
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("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");
|
cout << F("Only cards larger than 2 GB should be formatted FAT32.\n");
|
||||||
|
|
@ -183,10 +151,11 @@ void loop() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Read any extra Serial data.
|
// Read any extra Serial data.
|
||||||
clearSerialInput();
|
do {
|
||||||
|
delay(10);
|
||||||
|
} while (Serial.available() && Serial.read() >= 0);
|
||||||
cout << F("\nSuccess! Type any character to restart.\n");
|
cout << F("\nSuccess! Type any character to restart.\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ void setup(void) {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
@ -60,7 +60,7 @@ void loop(void) {
|
||||||
// F stores strings in flash to save RAM
|
// F stores strings in flash to save RAM
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << F("FreeStack: ") << FreeStack() << endl;
|
cout << F("FreeStack: ") << FreeStack() << endl;
|
||||||
|
|
@ -47,18 +47,18 @@ size_t readField(File* file, char* str, size_t size, const char* delim) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#define errorHalt(msg) {Serial.println(F(msg)); while (true) {}}
|
#define errorHalt(msg) {Serial.println(F(msg)); SysCall::halt();}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.println("Type any character to start");
|
Serial.println("Type any character to start");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// Initialize the SD.
|
// Initialize the SD.
|
||||||
if (!SD.begin(CS_PIN)) {
|
if (!SD.begin(CS_PIN)) {
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
|
||||||
#include "SdFat.h"
|
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI and
|
|
||||||
your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
|
||||||
File file;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
char line[40];
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Store error strings in flash to save RAM.
|
|
||||||
#define error(s) sd.errorHalt(&Serial, F(s))
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Check for extra characters in field or find minus sign.
|
|
||||||
char* skipSpace(char* str) {
|
|
||||||
while (isspace(*str)) str++;
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool parseLine(char* str) {
|
|
||||||
char* ptr;
|
|
||||||
|
|
||||||
// Set strtok start of line.
|
|
||||||
str = strtok(str, ",");
|
|
||||||
if (!str) return false;
|
|
||||||
|
|
||||||
// Print text field.
|
|
||||||
Serial.println(str);
|
|
||||||
|
|
||||||
// Subsequent calls to strtok expects a null pointer.
|
|
||||||
str = strtok(nullptr, ",");
|
|
||||||
if (!str) return false;
|
|
||||||
|
|
||||||
// Convert string to long integer.
|
|
||||||
int32_t i32 = strtol(str, &ptr, 0);
|
|
||||||
if (str == ptr || *skipSpace(ptr)) return false;
|
|
||||||
Serial.println(i32);
|
|
||||||
|
|
||||||
str = strtok(nullptr, ",");
|
|
||||||
if (!str) return false;
|
|
||||||
|
|
||||||
// strtoul accepts a leading minus with unexpected results.
|
|
||||||
if (*skipSpace(str) == '-') return false;
|
|
||||||
|
|
||||||
// Convert string to unsigned long integer.
|
|
||||||
uint32_t u32 = strtoul(str, &ptr, 0);
|
|
||||||
if (str == ptr || *skipSpace(ptr)) return false;
|
|
||||||
Serial.println(u32);
|
|
||||||
|
|
||||||
str = strtok(nullptr, ",");
|
|
||||||
if (!str) return false;
|
|
||||||
|
|
||||||
// Convert string to double.
|
|
||||||
double d = strtod(str, &ptr);
|
|
||||||
if (str == ptr || *skipSpace(ptr)) return false;
|
|
||||||
Serial.println(d);
|
|
||||||
|
|
||||||
// Check for extra fields.
|
|
||||||
return strtok(nullptr, ",") == nullptr;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
|
|
||||||
// Wait for USB Serial
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.println("Type any character to start");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
// Initialize the SD.
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Remove any existing file.
|
|
||||||
if (sd.exists("ReadCsvDemo.csv")) {
|
|
||||||
sd.remove("ReadCsvDemo.csv");
|
|
||||||
}
|
|
||||||
// Create the file.
|
|
||||||
if (!file.open("ReadCsvDemo.csv", FILE_WRITE)) {
|
|
||||||
error("open failed");
|
|
||||||
}
|
|
||||||
// Write test data. Test missing CRLF on last line.
|
|
||||||
file.print(
|
|
||||||
F("abc,123,456,7.89\r\n"
|
|
||||||
"def,-321,654,-9.87\r\n"
|
|
||||||
"ghi,333,0xff,5.55"));
|
|
||||||
|
|
||||||
// Rewind file for read.
|
|
||||||
file.rewind();
|
|
||||||
|
|
||||||
while (file.available()) {
|
|
||||||
int n = file.fgets(line, sizeof(line));
|
|
||||||
if (n <= 0) {
|
|
||||||
error("fgets failed");
|
|
||||||
}
|
|
||||||
if (line[n - 1] != '\n' && n == (sizeof(line) - 1)) {
|
|
||||||
error("line too long");
|
|
||||||
}
|
|
||||||
if (line[n - 1] == '\n') {
|
|
||||||
// Remove new line.
|
|
||||||
line[n -1] = 0;
|
|
||||||
}
|
|
||||||
if (!parseLine(line)) {
|
|
||||||
error("parseLine failed");
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
Serial.println(F("Done"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -95,11 +95,11 @@ void setup() {
|
||||||
|
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize at the highest speed supported by the board that is
|
// Initialize at the highest speed supported by the board that is
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
// RP2040 PIO SDIO setup and test.
|
|
||||||
/*
|
|
||||||
This example requires a SDIO Card socket with the following six lines.
|
|
||||||
|
|
||||||
CLK - A clock signal sent to the card by the MCU.
|
|
||||||
CMD - A bidirectional line for for commands and responses.
|
|
||||||
DAT[0:3] - Four bidirectional lines for data transfer.
|
|
||||||
|
|
||||||
CLK and CMD can be connected to any GPIO pins. DAT[0:3] can be connected
|
|
||||||
to any four consecutive GPIO pins in the order DAT0, DAT1, DAT2, DAT3.
|
|
||||||
|
|
||||||
For testing, I use several RP2040/RP3350 boards.
|
|
||||||
The Adafruit Metro RP2040 which has a builtin SDIO socket.
|
|
||||||
|
|
||||||
https://learn.adafruit.com/adafruit-metro-rp2040
|
|
||||||
|
|
||||||
I use this SD socket breakout board for other boards.
|
|
||||||
|
|
||||||
https://learn.adafruit.com/adafruit-microsd-spi-sdio
|
|
||||||
|
|
||||||
Wires should be short since signals can be as faster than 50 MHz.
|
|
||||||
*/
|
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
|
||||||
#include "SdFat_Adafruit_Fork.h"
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Example GPIO definitions I use for debug. Edit for your setup.
|
|
||||||
// Run this example as is to print the symbol for your variant.
|
|
||||||
//
|
|
||||||
#if defined(ARDUINO_ADAFRUIT_METRO_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER)
|
|
||||||
#define RP_CLK_GPIO 18
|
|
||||||
#define RP_CMD_GPIO 19
|
|
||||||
#define RP_DAT0_GPIO 20 // DAT1: GPIO21, DAT2: GPIO22, DAT3: GPIO23.
|
|
||||||
#elif defined(ARDUINO_ADAFRUIT_METRO_RP2350) || defined(ARDUINO_ADAFRUIT_FRUITJAM_RP2350)
|
|
||||||
#define RP_CLK_GPIO 34
|
|
||||||
#define RP_CMD_GPIO 35
|
|
||||||
#define RP_DAT0_GPIO 36 // DAT1: GPIO37, DAT2: GPIO38, DAT3: GPIO39.
|
|
||||||
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2)
|
|
||||||
#define RP_CLK_GPIO 16
|
|
||||||
#define RP_CMD_GPIO 17
|
|
||||||
#define RP_DAT0_GPIO 18 // DAT1: GPIO19, DAT2: GPIO20, DAT3: GPIO21.
|
|
||||||
#elif defined(ARDUINO_ADAFRUIT_FEATHER_RP2350_HSTX)
|
|
||||||
#define RP_CLK_GPIO 11
|
|
||||||
#define RP_CMD_GPIO 10
|
|
||||||
#define RP_DAT0_GPIO 22 // DAT1: GPIO23, DAT2: GPIO24, DAT3: GPIO25.
|
|
||||||
#endif // defined(ARDUINO_ADAFRUIT_METRO_RP2040))
|
|
||||||
|
|
||||||
#if defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#else // defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
#warning "Undefined SD_CONFIG. Run this program for the Variant Symbol."
|
|
||||||
#endif // defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Class File is not defined by SdFat since the RP2040 system defines it.
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.println("Type any character to start\n");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.print("Variant Symbol: ");
|
|
||||||
Serial.print("ARDUINO_");
|
|
||||||
Serial.println(BOARD_NAME);
|
|
||||||
Serial.println();
|
|
||||||
#if defined(SD_CONFIG)
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
Serial.println("Card successfully initialized.");
|
|
||||||
Serial.println("\nls:");
|
|
||||||
sd.ls(LS_A | LS_DATE | LS_SIZE); // Add LS_R for recursive list.
|
|
||||||
Serial.println("\nDone! Try the bench example next.");
|
|
||||||
#else // #if defined(SD_CONFIG)
|
|
||||||
Serial.println("Error: SD_CONFIG undefined for your board.");
|
|
||||||
Serial.println("Define RP_CLK_GPIO, RP_CMD_GPIO, and RP_DAT0_GPIO above.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -1,238 +0,0 @@
|
||||||
// Test of time-stamp callback.
|
|
||||||
// Set the callback with this statement.
|
|
||||||
// FsDateTime::setCallback(dateTime);
|
|
||||||
#include "SdFat.h"
|
|
||||||
// https://github.com/adafruit/RTClib
|
|
||||||
#include "RTClib.h"
|
|
||||||
// Set RTC_TYPE for file timestamps.
|
|
||||||
// 0 - millis()
|
|
||||||
// 1 - DS1307
|
|
||||||
// 2 - DS3231
|
|
||||||
// 3 - PCF8523
|
|
||||||
#define RTC_TYPE 3
|
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI and
|
|
||||||
your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
|
||||||
File file;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
#if RTC_TYPE == 0
|
|
||||||
RTC_Millis rtc;
|
|
||||||
#elif RTC_TYPE == 1
|
|
||||||
RTC_DS1307 rtc;
|
|
||||||
#elif RTC_TYPE == 2
|
|
||||||
RTC_DS3231 rtc;
|
|
||||||
#elif RTC_TYPE == 3
|
|
||||||
RTC_PCF8523 rtc;
|
|
||||||
#else // RTC_TYPE == type
|
|
||||||
#error RTC_TYPE type not implemented.
|
|
||||||
#endif // RTC_TYPE == type
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Call back for file timestamps. Only called for file create and sync().
|
|
||||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
|
||||||
DateTime now = rtc.now();
|
|
||||||
|
|
||||||
// Return date using FS_DATE macro to format fields.
|
|
||||||
*date = FS_DATE(now.year(), now.month(), now.day());
|
|
||||||
|
|
||||||
// Return time using FS_TIME macro to format fields.
|
|
||||||
*time = FS_TIME(now.hour(), now.minute(), now.second());
|
|
||||||
|
|
||||||
// Return low time bits in units of 10 ms, 0 <= ms10 <= 199.
|
|
||||||
*ms10 = now.second() & 1 ? 100 : 0;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#define error(msg) (Serial.println(F("error " msg)), false)
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void clearSerialInput() {
|
|
||||||
uint32_t m = micros();
|
|
||||||
do {
|
|
||||||
if (Serial.read() >= 0) {
|
|
||||||
m = micros();
|
|
||||||
}
|
|
||||||
} while (micros() - m < 10000);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void getLine(char* line, size_t size) {
|
|
||||||
size_t i = 0;
|
|
||||||
uint32_t t;
|
|
||||||
line[0] = '\0';
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
t = millis() + 10;
|
|
||||||
while (!Serial.available()) {
|
|
||||||
if (millis() > t) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int c = Serial.read();
|
|
||||||
if (i >= (size - 1) || c == '\r' || c == '\n') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
line[i++] = c;
|
|
||||||
line[i] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printField(Print* pr, char sep, uint8_t v) {
|
|
||||||
if (sep) {
|
|
||||||
pr->write(sep);
|
|
||||||
}
|
|
||||||
if (v < 10) {
|
|
||||||
pr->write('0');
|
|
||||||
}
|
|
||||||
pr->print(v);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printNow(Print* pr) {
|
|
||||||
DateTime now = rtc.now();
|
|
||||||
pr->print(now.year());
|
|
||||||
printField(pr, '-', now.month());
|
|
||||||
printField(pr, '-', now.day());
|
|
||||||
printField(pr, ' ', now.hour());
|
|
||||||
printField(pr, ':', now.minute());
|
|
||||||
printField(pr, ':', now.second());
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool setRtc() {
|
|
||||||
uint16_t y;
|
|
||||||
uint8_t m, d, hh, mm, ss;
|
|
||||||
char line[30];
|
|
||||||
char* ptr;
|
|
||||||
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println(F("Enter: YYYY-MM-DD hh:mm:ss"));
|
|
||||||
getLine(line, sizeof(line));
|
|
||||||
Serial.print(F("Input: "));
|
|
||||||
Serial.println(line);
|
|
||||||
|
|
||||||
y = strtol(line, &ptr, 10);
|
|
||||||
if (*ptr++ != '-' || y < 2000 || y > 2099) return error("year");
|
|
||||||
m = strtol(ptr, &ptr, 10);
|
|
||||||
if (*ptr++ != '-' || m < 1 || m > 12) return error("month");
|
|
||||||
d = strtol(ptr, &ptr, 10);
|
|
||||||
if (d < 1 || d > 31) return error("day");
|
|
||||||
hh = strtol(ptr, &ptr, 10);
|
|
||||||
if (*ptr++ != ':' || hh > 23) return error("hour");
|
|
||||||
mm = strtol(ptr, &ptr, 10);
|
|
||||||
if (*ptr++ != ':' || mm > 59) return error("minute");
|
|
||||||
ss = strtol(ptr, &ptr, 10);
|
|
||||||
if (ss > 59) return error("second");
|
|
||||||
|
|
||||||
rtc.adjust(DateTime(y, m, d, hh, mm, ss));
|
|
||||||
Serial.print(F("RTC set to "));
|
|
||||||
printNow(&Serial);
|
|
||||||
Serial.println();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
#if RTC_TYPE == 0
|
|
||||||
rtc.begin(DateTime(F(__DATE__), F(__TIME__)));
|
|
||||||
#else // RTC_TYPE
|
|
||||||
if (!rtc.begin()) {
|
|
||||||
Serial.println(F("rtc.begin failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!rtc.isrunning()) {
|
|
||||||
Serial.println(F("RTC is NOT running!"));
|
|
||||||
return;
|
|
||||||
// following line sets the RTC to the date & time this sketch was compiled
|
|
||||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
|
||||||
// This line sets the RTC with an explicit date & time, for example to set
|
|
||||||
// January 21, 2014 at 3am you would call:
|
|
||||||
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
|
|
||||||
}
|
|
||||||
#endif // RTC_TYPE
|
|
||||||
while (true) {
|
|
||||||
Serial.print(F("DateTime::now "));
|
|
||||||
printNow(&Serial);
|
|
||||||
Serial.println();
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println(F("Type Y to set RTC, any other character to continue"));
|
|
||||||
while (!Serial.available()) {
|
|
||||||
}
|
|
||||||
if (Serial.read() != 'Y') break;
|
|
||||||
if (setRtc()) break;
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// Set callback
|
|
||||||
FsDateTime::setCallback(dateTime);
|
|
||||||
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
// Remove old version to set create time.
|
|
||||||
if (sd.exists("RtcTest.txt")) {
|
|
||||||
sd.remove("RtcTest.txt");
|
|
||||||
}
|
|
||||||
if (!file.open("RtcTest.txt", FILE_WRITE)) {
|
|
||||||
Serial.println(F("file.open failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Print current date time to file.
|
|
||||||
file.print(F("Test file at: "));
|
|
||||||
printNow(&file);
|
|
||||||
file.println();
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
// List files in SD root.
|
|
||||||
sd.ls(LS_DATE | LS_SIZE);
|
|
||||||
Serial.println(F("Done"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#include "FreeStack.h"
|
#include "FreeStack.h"
|
||||||
#error See new Version 2 STM32 example
|
|
||||||
// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes
|
// set ENABLE_EXTENDED_TRANSFER_CLASS non-zero to use faster EX classes
|
||||||
|
|
||||||
// Use first SPI port
|
// Use first SPI port
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
// Print a list of error codes, symbols, and comments.
|
|
||||||
#include "SdFat.h"
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
}
|
|
||||||
delay(1000);
|
|
||||||
Serial.println();
|
|
||||||
Serial.println(F("Code,Symbol - failed operation"));
|
|
||||||
for (uint8_t code = 0; code <= SD_CARD_ERROR_UNKNOWN; code++) {
|
|
||||||
Serial.print(code < 16 ? "0X0" : "0X");
|
|
||||||
Serial.print(code, HEX);
|
|
||||||
Serial.print(",");
|
|
||||||
printSdErrorSymbol(&Serial, code);
|
|
||||||
Serial.print(" - ");
|
|
||||||
printSdErrorText(&Serial, code);
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -1,97 +1,411 @@
|
||||||
/*
|
/*
|
||||||
* This program will format SD/SDHC/SDXC cards.
|
* This program will format an SD or SDHC card.
|
||||||
* Warning all data will be deleted!
|
* Warning all data will be deleted!
|
||||||
*
|
*
|
||||||
* This program attempts to match the format
|
* For SD/SDHC cards larger than 64 MB this
|
||||||
|
* program attempts to match the format
|
||||||
* generated by SDFormatter available here:
|
* generated by SDFormatter available here:
|
||||||
*
|
*
|
||||||
* http://www.sdcard.org/consumers/formatter/
|
* http://www.sdcard.org/consumers/formatter/
|
||||||
*
|
*
|
||||||
* For very small cards this program uses FAT16
|
* For smaller cards this program uses FAT16
|
||||||
* and the above SDFormatter uses FAT12.
|
* and SDFormatter uses FAT12.
|
||||||
*/
|
*/
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
|
||||||
|
// 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 "SdFat.h"
|
||||||
#include "sdios.h"
|
#include "sdios.h"
|
||||||
|
#if DEBUG_PRINT
|
||||||
|
#include "FreeStack.h"
|
||||||
|
#endif // DEBUG_PRINT
|
||||||
|
|
||||||
/*
|
|
||||||
Set DISABLE_CS_PIN to disable a second SPI device.
|
|
||||||
For example, with the Ethernet shield, set DISABLE_CS_PIN
|
|
||||||
to 10 to disable the Ethernet controller.
|
|
||||||
*/
|
|
||||||
const int8_t DISABLE_CS_PIN = -1;
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI
|
|
||||||
and your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
//==============================================================================
|
|
||||||
// Serial output stream
|
// Serial output stream
|
||||||
ArduinoOutStream cout(Serial);
|
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 ";
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
uint32_t cardSectorCount = 0;
|
#define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();}
|
||||||
uint8_t sectorBuffer[512];
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// SdCardFactory constructs and initializes the appropriate card.
|
|
||||||
SdCardFactory cardFactory;
|
|
||||||
// Pointer to generic SD card.
|
|
||||||
SdCard* m_card = nullptr;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#define sdError(msg) \
|
|
||||||
{ \
|
|
||||||
cout << F("error: ") << F(msg) << endl; \
|
|
||||||
sdErrorHalt(); \
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void sdErrorHalt() {
|
void sdErrorHalt() {
|
||||||
if (!m_card) {
|
if (card.errorCode()) {
|
||||||
cout << F("Invalid SD_CONFIG") << endl;
|
cout << F("SD error: ") << hex << int(card.errorCode());
|
||||||
} else if (m_card->errorCode()) {
|
cout << ',' << int(card.errorData()) << dec << endl;
|
||||||
if (m_card->errorCode() == SD_CARD_ERROR_CMD0) {
|
|
||||||
cout << F("No card, wrong chip select pin, or wiring error?") << endl;
|
|
||||||
}
|
}
|
||||||
cout << F("SD errorCode: ") << hex << showbase;
|
SysCall::halt();
|
||||||
printSdErrorSymbol(&Serial, m_card->errorCode());
|
}
|
||||||
cout << F(" = ") << int(m_card->errorCode()) << endl;
|
//------------------------------------------------------------------------------
|
||||||
cout << F("SD errorData = ") << int(m_card->errorData()) << endl;
|
#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;
|
||||||
}
|
}
|
||||||
while (true) {
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void clearSerialInput() {
|
// zero cache and optionally set the sector signature
|
||||||
uint32_t m = micros();
|
void clearCache(uint8_t addSig) {
|
||||||
do {
|
memset(&cache, 0, sizeof(cache));
|
||||||
if (Serial.read() >= 0) {
|
if (addSig) {
|
||||||
m = micros();
|
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");
|
||||||
}
|
}
|
||||||
} while (micros() - m < 10000);
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// flash erase all data
|
// flash erase all data
|
||||||
|
|
@ -104,62 +418,44 @@ void eraseCard() {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
lastBlock = firstBlock + ERASE_SIZE - 1;
|
lastBlock = firstBlock + ERASE_SIZE - 1;
|
||||||
if (lastBlock >= cardSectorCount) {
|
if (lastBlock >= cardSizeBlocks) {
|
||||||
lastBlock = cardSectorCount - 1;
|
lastBlock = cardSizeBlocks - 1;
|
||||||
}
|
}
|
||||||
if (!m_card->erase(firstBlock, lastBlock)) {
|
if (!card.erase(firstBlock, lastBlock)) {
|
||||||
sdError("erase failed");
|
sdError("erase failed");
|
||||||
}
|
}
|
||||||
cout << '.';
|
cout << '.';
|
||||||
if ((n++) % 64 == 63) {
|
if ((n++)%32 == 31) {
|
||||||
cout << endl;
|
cout << endl;
|
||||||
}
|
}
|
||||||
firstBlock += ERASE_SIZE;
|
firstBlock += ERASE_SIZE;
|
||||||
} while (firstBlock < cardSectorCount);
|
} while (firstBlock < cardSizeBlocks);
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
||||||
if (!m_card->readSector(0, sectorBuffer)) {
|
if (!card.readBlock(0, cache.data)) {
|
||||||
sdError("readBlock");
|
sdError("readBlock");
|
||||||
}
|
}
|
||||||
cout << hex << showbase << setfill('0') << internal;
|
cout << hex << showbase << setfill('0') << internal;
|
||||||
cout << F("All data set to ") << setw(4) << int(sectorBuffer[0]) << endl;
|
cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl;
|
||||||
cout << dec << noshowbase << setfill(' ') << right;
|
cout << dec << noshowbase << setfill(' ') << right;
|
||||||
cout << F("Erase done\n");
|
cout << F("Erase done\n");
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void formatCard() {
|
void formatCard() {
|
||||||
ExFatFormatter exFatFormatter;
|
cout << endl;
|
||||||
FatFormatter fatFormatter;
|
cout << F("Formatting\n");
|
||||||
|
initSizes();
|
||||||
// Format exFAT if larger than 32GB.
|
if (card.type() != SD_CARD_TYPE_SDHC) {
|
||||||
bool rtn = cardSectorCount > 67108864
|
cout << F("FAT16\n");
|
||||||
? exFatFormatter.format(m_card, sectorBuffer, &Serial)
|
makeFat16();
|
||||||
: fatFormatter.format(m_card, sectorBuffer, &Serial);
|
|
||||||
|
|
||||||
if (!rtn) {
|
|
||||||
sdErrorHalt();
|
|
||||||
}
|
|
||||||
cout << F("Run the SdInfo example for format details.") << endl;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printConfig(SdSpiConfig config) {
|
|
||||||
if (DISABLE_CS_PIN < 0) {
|
|
||||||
cout << F(
|
|
||||||
"\nAssuming the SD is the only SPI device.\n"
|
|
||||||
"Edit DISABLE_CS_PIN to disable an SPI device.\n");
|
|
||||||
} else {
|
} else {
|
||||||
cout << F("\nDisabling SPI device on pin ");
|
cout << F("FAT32\n");
|
||||||
cout << int(DISABLE_CS_PIN) << endl;
|
makeFat32();
|
||||||
pinMode(DISABLE_CS_PIN, OUTPUT);
|
|
||||||
digitalWrite(DISABLE_CS_PIN, HIGH);
|
|
||||||
}
|
}
|
||||||
cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
|
#if DEBUG_PRINT
|
||||||
cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
|
debugPrint();
|
||||||
}
|
#endif // DEBUG_PRINT
|
||||||
//------------------------------------------------------------------------------
|
cout << F("Format done\n");
|
||||||
void printConfig(SdioConfig config) {
|
|
||||||
(void)config;
|
|
||||||
cout << F("Assuming an SDIO interface.\n");
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
@ -167,33 +463,33 @@ void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
printConfig(SD_CONFIG);
|
cout << F("Type any character to start\n");
|
||||||
cout << F("\nType any character to start\n");
|
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// Discard any extra characters.
|
// Discard any extra characters.
|
||||||
clearSerialInput();
|
do {
|
||||||
|
delay(10);
|
||||||
|
} while (Serial.available() && Serial.read() >= 0);
|
||||||
cout << F(
|
cout << F(
|
||||||
"\n"
|
"\n"
|
||||||
"This program can erase and/or format SD/SDHC/SDXC cards.\n"
|
"This program can erase and/or format SD/SDHC cards.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Erase uses the card's fast flash erase command.\n"
|
"Erase uses the card's fast flash erase command.\n"
|
||||||
"Flash erase sets all data to 0X00 for most cards\n"
|
"Flash erase sets all data to 0X00 for most cards\n"
|
||||||
"and 0XFF for a few vendor's cards.\n"
|
"and 0XFF for a few vendor's cards.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Cards up to 2 GiB (GiB = 2^30 bytes) will be formated FAT16.\n"
|
"Cards larger than 2 GB will be formatted FAT32 and\n"
|
||||||
"Cards larger than 2 GiB and up to 32 GiB will be formatted\n"
|
"smaller cards will be formatted FAT16.\n"
|
||||||
"FAT32. Cards larger than 32 GiB will be formatted exFAT.\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"Warning, all data on the card will be erased.\n"
|
"Warning, all data on the card will be erased.\n"
|
||||||
"Enter 'Y' to continue: ");
|
"Enter 'Y' to continue: ");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
c = Serial.read();
|
c = Serial.read();
|
||||||
cout << c << endl;
|
cout << c << endl;
|
||||||
if (c != 'Y') {
|
if (c != 'Y') {
|
||||||
|
|
@ -201,34 +497,10 @@ void setup() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Read any existing Serial data.
|
// Read any existing Serial data.
|
||||||
clearSerialInput();
|
do {
|
||||||
|
delay(10);
|
||||||
|
} while (Serial.available() && Serial.read() >= 0);
|
||||||
|
|
||||||
// Select and initialize proper card driver.
|
|
||||||
m_card = cardFactory.newCard(SD_CONFIG);
|
|
||||||
if (!m_card || m_card->errorCode()) {
|
|
||||||
sdError("card init failed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cardSectorCount = m_card->sectorCount();
|
|
||||||
if (!cardSectorCount) {
|
|
||||||
sdError("Get sector count failed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << F("\nCard size: ") << cardSectorCount * 5.12e-7;
|
|
||||||
cout << F(" GB (GB = 1E9 bytes)\n");
|
|
||||||
cout << F("Card size: ") << cardSectorCount / 2097152.0;
|
|
||||||
cout << F(" GiB (GiB = 2^30 bytes)\n");
|
|
||||||
|
|
||||||
cout << F("Card will be formated ");
|
|
||||||
if (cardSectorCount > 67108864) {
|
|
||||||
cout << F("exFAT\n");
|
|
||||||
} else if (cardSectorCount > 4194304) {
|
|
||||||
cout << F("FAT32\n");
|
|
||||||
} else {
|
|
||||||
cout << F("FAT16\n");
|
|
||||||
}
|
|
||||||
cout << F(
|
cout << F(
|
||||||
"\n"
|
"\n"
|
||||||
"Options are:\n"
|
"Options are:\n"
|
||||||
|
|
@ -239,7 +511,7 @@ void setup() {
|
||||||
"Enter option: ");
|
"Enter option: ");
|
||||||
|
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
c = Serial.read();
|
c = Serial.read();
|
||||||
cout << c << endl;
|
cout << c << endl;
|
||||||
|
|
@ -247,6 +519,28 @@ void setup() {
|
||||||
cout << F("Quiting, invalid option entered.") << endl;
|
cout << F("Quiting, invalid option entered.") << endl;
|
||||||
return;
|
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') {
|
if (c == 'E' || c == 'F') {
|
||||||
eraseCard();
|
eraseCard();
|
||||||
}
|
}
|
||||||
|
|
@ -254,4 +548,5 @@ void setup() {
|
||||||
formatCard();
|
formatCard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
void loop() {}
|
void loop() {}
|
||||||
|
|
@ -1,180 +1,210 @@
|
||||||
/*
|
/*
|
||||||
* This program attempts to initialize an SD card and analyze its structure.
|
* This program attempts to initialize an SD card and analyze its structure.
|
||||||
* The CID and CSD registers are also printed in HEX for use in online
|
|
||||||
* decoders like these.
|
|
||||||
*
|
|
||||||
* https://gurumeditation.org/1342/sd-memory-card-register-decoder/
|
|
||||||
* https://archive.goughlui.com/static/multicid.htm
|
|
||||||
*/
|
*/
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#include "sdios.h"
|
#include "sdios.h"
|
||||||
/*
|
|
||||||
Set DISABLE_CS_PIN to disable a second SPI device.
|
|
||||||
For example, with the Ethernet shield, set DISABLE_CS_PIN
|
|
||||||
to 10 to disable the Ethernet controller.
|
|
||||||
*/
|
|
||||||
const int8_t DISABLE_CS_PIN = -1;
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI
|
|
||||||
and your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
// Set USE_SDIO to zero for SPI card access.
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
#define USE_SDIO 0
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
/*
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
* SD chip select pin. Common values are:
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
*
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
* Arduino Ethernet shield, pin 4.
|
||||||
#elif ENABLE_DEDICATED_SPI
|
* SparkFun SD shield, pin 8.
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16))
|
* Adafruit SD shields and modules, pin 10.
|
||||||
#else // HAS_TEENSY_SDIO
|
* Default SD chip select is the SPI SS pin.
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(16))
|
*/
|
||||||
#endif // HAS_TEENSY_SDIO
|
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
|
||||||
SdFs sd;
|
// Use faster SdioCardEX
|
||||||
cid_t cid;
|
SdFatSdioEX sd;
|
||||||
csd_t csd;
|
// SdFatSdio sd;
|
||||||
scr_t scr;
|
#else // USE_SDIO
|
||||||
uint8_t cmd6Data[64];
|
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;
|
uint32_t eraseSize;
|
||||||
uint32_t ocr;
|
|
||||||
static ArduinoOutStream cout(Serial);
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void cidDmp() {
|
// 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 << F("\nManufacturer ID: ");
|
||||||
cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
|
cout << hex << int(cid.mid) << dec << endl;
|
||||||
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
|
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
|
||||||
cout << F("Product: ");
|
cout << F("Product: ");
|
||||||
for (uint8_t i = 0; i < 5; i++) {
|
for (uint8_t i = 0; i < 5; i++) {
|
||||||
cout << cid.pnm[i];
|
cout << cid.pnm[i];
|
||||||
}
|
}
|
||||||
cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
|
cout << F("\nVersion: ");
|
||||||
cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
|
cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
|
||||||
|
cout << F("Serial number: ") << hex << cid.psn << dec << endl;
|
||||||
cout << F("Manufacturing date: ");
|
cout << F("Manufacturing date: ");
|
||||||
cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
|
cout << int(cid.mdt_month) << '/';
|
||||||
cout << F("CID HEX: ");
|
cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
|
||||||
hexDmp(&cid, sizeof(cid));
|
cout << endl;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void clearSerialInput() {
|
uint8_t csdDmp() {
|
||||||
uint32_t m = micros();
|
csd_t csd;
|
||||||
do {
|
uint8_t eraseSingleBlock;
|
||||||
if (Serial.read() >= 0) {
|
if (!sd.card()->readCSD(&csd)) {
|
||||||
m = micros();
|
sdErrorMsg("readCSD failed");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} while (micros() - m < 10000);
|
if (csd.v1.csd_ver == 0) {
|
||||||
}
|
eraseSingleBlock = csd.v1.erase_blk_en;
|
||||||
//------------------------------------------------------------------------------
|
eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
|
||||||
void csdDmp() {
|
} else if (csd.v2.csd_ver == 1) {
|
||||||
eraseSize = csd.eraseSize();
|
eraseSingleBlock = csd.v2.erase_blk_en;
|
||||||
cout << F("\ncardSize: ") << 0.000512 * csd.capacity();
|
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(" MB (MB = 1,000,000 bytes)\n");
|
||||||
|
|
||||||
cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
|
cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
|
||||||
cout << F("eraseSingleBlock: ");
|
cout << F("eraseSingleBlock: ");
|
||||||
if (csd.eraseSingleBlock()) {
|
if (eraseSingleBlock) {
|
||||||
cout << F("true\n");
|
cout << F("true\n");
|
||||||
} else {
|
} else {
|
||||||
cout << F("false\n");
|
cout << F("false\n");
|
||||||
}
|
}
|
||||||
cout << F("dataAfterErase: ");
|
return true;
|
||||||
if (scr.dataAfterErase()) {
|
|
||||||
cout << F("ones\n");
|
|
||||||
} else {
|
|
||||||
cout << F("zeros\n");
|
|
||||||
}
|
|
||||||
cout << F("CSD HEX: ");
|
|
||||||
hexDmp(&csd, sizeof(csd));
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void errorPrint() {
|
// print partition table
|
||||||
if (sd.sdErrorCode()) {
|
uint8_t partDmp() {
|
||||||
cout << F("SD errorCode: ") << hex << showbase;
|
mbr_t mbr;
|
||||||
printSdErrorSymbol(&Serial, sd.sdErrorCode());
|
if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) {
|
||||||
cout << F(" = ") << int(sd.sdErrorCode()) << endl;
|
sdErrorMsg("read MBR failed");
|
||||||
cout << F("SD errorData = ") << int(sd.sdErrorData()) << dec << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void hexDmp(void* reg, uint8_t size) {
|
|
||||||
uint8_t* u8 = reinterpret_cast<uint8_t*>(reg);
|
|
||||||
cout << hex << noshowbase;
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
cout << setw(2) << setfill('0') << int(u8[i]);
|
|
||||||
}
|
|
||||||
cout << dec << endl;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool mbrDmp() {
|
|
||||||
MbrSector_t mbr;
|
|
||||||
bool valid = true;
|
|
||||||
if (!sd.card()->readSector(0, (uint8_t*)&mbr)) {
|
|
||||||
cout << F("\nread MBR failed.\n");
|
|
||||||
errorPrint();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cout << F("\nSD Partition Table\n");
|
|
||||||
cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n");
|
|
||||||
for (uint8_t ip = 1; ip < 5; ip++) {
|
for (uint8_t ip = 1; ip < 5; ip++) {
|
||||||
MbrPart_t* pt = &mbr.part[ip - 1];
|
part_t *pt = &mbr.part[ip - 1];
|
||||||
if ((pt->boot != 0 && pt->boot != 0X80) ||
|
if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) {
|
||||||
getLe32(pt->relativeSectors) > csd.capacity()) {
|
cout << F("\nNo MBR. Assuming Super Floppy format.\n");
|
||||||
valid = false;
|
return true;
|
||||||
}
|
}
|
||||||
cout << int(ip) << ',' << uppercase << showbase << hex;
|
|
||||||
cout << int(pt->boot) << ',';
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
cout << int(pt->beginCHS[i]) << ',';
|
|
||||||
}
|
}
|
||||||
cout << int(pt->type) << ',';
|
cout << F("\nSD Partition Table\n");
|
||||||
for (int i = 0; i < 3; i++) {
|
cout << F("part,boot,type,start,length\n");
|
||||||
cout << int(pt->endCHS[i]) << ',';
|
for (uint8_t ip = 1; ip < 5; ip++) {
|
||||||
}
|
part_t *pt = &mbr.part[ip - 1];
|
||||||
cout << dec << getLe32(pt->relativeSectors) << ',';
|
cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
|
||||||
cout << getLe32(pt->totalSectors) << endl;
|
cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl;
|
||||||
}
|
|
||||||
if (!valid) {
|
|
||||||
cout << F("\nMBR not valid, assuming Super Floppy format.\n");
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void dmpVol() {
|
void volDmp() {
|
||||||
cout << F("\nScanning FAT, please wait.\n");
|
cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl;
|
||||||
int32_t freeClusterCount = sd.freeClusterCount();
|
cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl;
|
||||||
if (sd.fatType() <= 32) {
|
cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl;
|
||||||
cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
|
cout << F("freeClusters: ");
|
||||||
} else {
|
uint32_t volFree = sd.vol()->freeClusterCount();
|
||||||
cout << F("\nVolume is exFAT\n");
|
cout << volFree << endl;
|
||||||
}
|
float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
|
||||||
cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
|
cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n");
|
||||||
cout << F("fatStartSector: ") << sd.fatStartSector() << endl;
|
cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl;
|
||||||
cout << F("dataStartSector: ") << sd.dataStartSector() << endl;
|
cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl;
|
||||||
cout << F("clusterCount: ") << sd.clusterCount() << endl;
|
cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl;
|
||||||
cout << F("freeClusterCount: ");
|
cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl;
|
||||||
if (freeClusterCount >= 0) {
|
cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl;
|
||||||
cout << freeClusterCount << endl;
|
if (sd.vol()->dataStartBlock() % eraseSize) {
|
||||||
} else {
|
cout << F("Data area is not aligned on flash erase boundaries!\n");
|
||||||
cout << F("failed\n");
|
cout << F("Download and use formatter from www.sdcard.org!\n");
|
||||||
errorPrint();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void printCardType() {
|
void setup() {
|
||||||
cout << F("\nCard type: ");
|
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()) {
|
switch (sd.card()->type()) {
|
||||||
case SD_CARD_TYPE_SD1:
|
case SD_CARD_TYPE_SD1:
|
||||||
cout << F("SD1\n");
|
cout << F("SD1\n");
|
||||||
|
|
@ -185,7 +215,7 @@ void printCardType() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SD_CARD_TYPE_SDHC:
|
case SD_CARD_TYPE_SDHC:
|
||||||
if (csd.capacity() < 70000000) {
|
if (cardSize < 70000000) {
|
||||||
cout << F("SDHC\n");
|
cout << F("SDHC\n");
|
||||||
} else {
|
} else {
|
||||||
cout << F("SDXC\n");
|
cout << F("SDXC\n");
|
||||||
|
|
@ -195,91 +225,24 @@ void printCardType() {
|
||||||
default:
|
default:
|
||||||
cout << F("Unknown\n");
|
cout << F("Unknown\n");
|
||||||
}
|
}
|
||||||
}
|
if (!cidDmp()) {
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printConfig(SdSpiConfig config) {
|
|
||||||
if (DISABLE_CS_PIN < 0) {
|
|
||||||
cout << F(
|
|
||||||
"\nAssuming the SD is the only SPI device.\n"
|
|
||||||
"Edit DISABLE_CS_PIN to disable an SPI device.\n");
|
|
||||||
} else {
|
|
||||||
cout << F("\nDisabling SPI device on pin ");
|
|
||||||
cout << int(DISABLE_CS_PIN) << endl;
|
|
||||||
pinMode(DISABLE_CS_PIN, OUTPUT);
|
|
||||||
digitalWrite(DISABLE_CS_PIN, HIGH);
|
|
||||||
}
|
|
||||||
cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
|
|
||||||
cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printConfig(SdioConfig config) {
|
|
||||||
(void)config;
|
|
||||||
cout << F("Assuming an SDIO interface.\n");
|
|
||||||
}
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
// Wait for USB Serial
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
cout << F("SdFat version: ") << SD_FAT_VERSION_STR << endl;
|
|
||||||
printConfig(SD_CONFIG);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {
|
|
||||||
// Read any existing Serial data.
|
|
||||||
clearSerialInput();
|
|
||||||
|
|
||||||
// F stores strings in flash to save RAM
|
|
||||||
cout << F("\ntype any character to start\n");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
uint32_t t = millis();
|
|
||||||
if (!sd.cardBegin(SD_CONFIG)) {
|
|
||||||
cout << F(
|
|
||||||
"\nSD initialization failed.\n"
|
|
||||||
"Do not reformat the card!\n"
|
|
||||||
"Is the card correctly inserted?\n"
|
|
||||||
"Is there a wiring/soldering problem?\n");
|
|
||||||
if (isSpi(SD_CONFIG)) {
|
|
||||||
cout << F(
|
|
||||||
"Is SD_CS_PIN set to the correct value?\n"
|
|
||||||
"Does another SPI device need to be disabled?\n");
|
|
||||||
}
|
|
||||||
errorPrint();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
t = millis() - t;
|
if (!csdDmp()) {
|
||||||
cout << F("init time: ") << dec << t << " ms" << endl;
|
|
||||||
|
|
||||||
if (!sd.card()->readCID(&cid) || !sd.card()->readCSD(&csd) ||
|
|
||||||
!sd.card()->readOCR(&ocr) || !sd.card()->readSCR(&scr)) {
|
|
||||||
cout << F("readInfo failed\n");
|
|
||||||
errorPrint();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printCardType();
|
uint32_t ocr;
|
||||||
cout << F("sdSpecVer: ") << 0.01 * scr.sdSpecVer() << endl;
|
if (!sd.card()->readOCR(&ocr)) {
|
||||||
cout << F("HighSpeedMode: ");
|
sdErrorMsg("\nreadOCR failed");
|
||||||
if (scr.sdSpecVer() > 101 && sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) &&
|
|
||||||
(2 & cmd6Data[13])) {
|
|
||||||
cout << F("true\n");
|
|
||||||
} else {
|
|
||||||
cout << F("false\n");
|
|
||||||
}
|
|
||||||
cidDmp();
|
|
||||||
csdDmp();
|
|
||||||
cout << F("\nOCR: ") << uppercase << showbase;
|
|
||||||
cout << hex << ocr << dec << endl;
|
|
||||||
if (!mbrDmp()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!sd.volumeBegin()) {
|
cout << F("OCR: ") << hex << ocr << dec << endl;
|
||||||
cout << F("\nvolumeBegin failed. Is the card formatted?\n");
|
if (!partDmp()) {
|
||||||
errorPrint();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dmpVol();
|
if (!sd.fsBegin()) {
|
||||||
|
sdErrorMsg("\nFile System initialization failed.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
volDmp();
|
||||||
}
|
}
|
||||||
|
|
@ -1,60 +1,38 @@
|
||||||
// An example of the SoftSpiDriver template class.
|
// An example of the SdFatSoftSpi template class.
|
||||||
// This example is for an old Adafruit Data Logging Shield on a Mega.
|
// 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.
|
// 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.
|
// This example will also run on an Uno and other boards using software SPI.
|
||||||
//
|
//
|
||||||
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#if SPI_DRIVER_SELECT == 2 // Must be set in SdFat/SdFatConfig.h
|
#if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
//
|
|
||||||
// Chip select may be constant or RAM variable.
|
|
||||||
const uint8_t SD_CS_PIN = 10;
|
|
||||||
//
|
//
|
||||||
// Pin numbers in templates must be constants.
|
// Pin numbers in templates must be constants.
|
||||||
const uint8_t SOFT_MISO_PIN = 12;
|
const uint8_t SOFT_MISO_PIN = 12;
|
||||||
const uint8_t SOFT_MOSI_PIN = 11;
|
const uint8_t SOFT_MOSI_PIN = 11;
|
||||||
const uint8_t SOFT_SCK_PIN = 13;
|
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
|
// SdFat software SPI template
|
||||||
SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi;
|
SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;
|
||||||
// Speed argument is ignored for software SPI.
|
|
||||||
#if ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
|
|
||||||
#else // ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(0), &softSpi)
|
|
||||||
#endif // ENABLE_DEDICATED_SPI
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
// Test file.
|
||||||
SdFat sd;
|
SdFile file;
|
||||||
File file;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.println("Type any character to start");
|
Serial.println("Type any character to start");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
if (!sd.begin(SD_CHIP_SELECT_PIN)) {
|
||||||
sd.initErrorHalt();
|
sd.initErrorHalt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +53,6 @@ void setup() {
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void loop() {}
|
void loop() {}
|
||||||
#else // SPI_DRIVER_SELECT
|
#else // ENABLE_SOFTWARE_SPI_CLASS
|
||||||
#error SPI_DRIVER_SELECT must be two in SdFat/SdFatConfig.h
|
#error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h
|
||||||
#endif // SPI_DRIVER_SELECT
|
#endif //ENABLE_SOFTWARE_SPI_CLASS
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
// This is a simple SPI loop-back test.
|
|
||||||
//
|
|
||||||
// Connect SD_MISO to SD_MOSI
|
|
||||||
//
|
|
||||||
// Modify these defines for your configuration.
|
|
||||||
#define SD_SPI SPI
|
|
||||||
#define SD_MISO MISO
|
|
||||||
#define SD_MOSI MOSI
|
|
||||||
|
|
||||||
#include "SPI.h"
|
|
||||||
void setup() {
|
|
||||||
uint8_t rx, tx;
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.println(F("\nType any character to start"));
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.print("Begin, SD_MISO: ");
|
|
||||||
Serial.print(SD_MISO), Serial.print(", SD_MOSI: ");
|
|
||||||
Serial.println(SD_MOSI);
|
|
||||||
pinMode(SD_MISO, INPUT_PULLUP);
|
|
||||||
pinMode(SD_MOSI, OUTPUT);
|
|
||||||
digitalWrite(SD_MOSI, HIGH);
|
|
||||||
if (!digitalRead(SD_MISO)) {
|
|
||||||
Serial.println("Error: SD_MISO not HIGH");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
digitalWrite(SD_MOSI, LOW);
|
|
||||||
if (digitalRead(SD_MISO)) {
|
|
||||||
Serial.println("Error: SD_MISO not LOW");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
pinMode(SD_MISO, INPUT);
|
|
||||||
pinMode(SD_MOSI, INPUT);
|
|
||||||
|
|
||||||
// Modify if SD_SPI.begin has arguments and use this style SdFat begin call:
|
|
||||||
// sd.begin(SdSpiConfig(CS_PIN, USER_SPI_BEGIN | <other options>, &SD_SPI));
|
|
||||||
SD_SPI.begin();
|
|
||||||
|
|
||||||
// Start with a 400 kHz clock. Try full speed if success for 400 kHz.
|
|
||||||
SD_SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0));
|
|
||||||
tx = 0;
|
|
||||||
do {
|
|
||||||
rx = SD_SPI.transfer(tx);
|
|
||||||
if (tx != rx) {
|
|
||||||
Serial.print("Error rx: 0x");
|
|
||||||
Serial.print(rx, HEX);
|
|
||||||
Serial.print(" != tx: 0x");
|
|
||||||
Serial.println(tx, HEX);
|
|
||||||
SD_SPI.endTransaction();
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
} while (tx++ < 255);
|
|
||||||
SD_SPI.endTransaction();
|
|
||||||
Serial.println("Success!");
|
|
||||||
return;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
SD_SPI.endTransaction();
|
|
||||||
Serial.println("Is SD_MISO connected to SD_MOSI?");
|
|
||||||
Serial.println("Are SD_MISO and SD_MOSI correct?");
|
|
||||||
}
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Benchmark comparing SdFile and StdioStream.
|
// Benchmark comparing SdFile and StdioStream.
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#include "sdios.h"
|
|
||||||
|
|
||||||
// Define PRINT_FIELD nonzero to use printField.
|
// Define PRINT_FIELD nonzero to use printField.
|
||||||
#define PRINT_FIELD 0
|
#define PRINT_FIELD 0
|
||||||
|
|
@ -25,19 +24,19 @@ const char* label[] =
|
||||||
};
|
};
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
void setup() {
|
void setup() {
|
||||||
uint32_t printSize = 0;
|
uint32_t printSize;
|
||||||
uint32_t stdioSize = 0;
|
uint32_t stdioSize = 0;
|
||||||
uint32_t printTime = 0;
|
uint32_t printTime;
|
||||||
uint32_t stdioTime = 0;
|
uint32_t stdioTime = 0;
|
||||||
|
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.println(F("Type any character to start"));
|
Serial.println(F("Type any character to start"));
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.println(F("Starting test"));
|
Serial.println(F("Starting test"));
|
||||||
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
// Test of Teensy exFAT DMA ADC logger.
|
|
||||||
// This is mainly to test use of RingBuf in an ISR.
|
|
||||||
// This example only supports pins on the first ADC.
|
|
||||||
// it has only been tested on Teensy 3.6 and 4.1.
|
|
||||||
// You should modify it for serious use as a data logger.
|
|
||||||
//
|
|
||||||
#include "ADC.h"
|
|
||||||
#include "DMAChannel.h"
|
|
||||||
#include "SdFat.h"
|
|
||||||
#include "FreeStack.h"
|
|
||||||
#include "RingBuf.h"
|
|
||||||
|
|
||||||
// Pin must be on first ADC.
|
|
||||||
#define ADC_PIN A0
|
|
||||||
|
|
||||||
// 400 sector RingBuf - could be larger on Teensy 4.1.
|
|
||||||
const size_t RING_BUF_SIZE = 400 * 512;
|
|
||||||
|
|
||||||
// Preallocate 8GiB file.
|
|
||||||
const uint64_t PRE_ALLOCATE_SIZE = 8ULL << 30;
|
|
||||||
|
|
||||||
// Use FIFO SDIO.
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
|
|
||||||
ADC adc;
|
|
||||||
|
|
||||||
DMAChannel dma(true);
|
|
||||||
|
|
||||||
SdFs sd;
|
|
||||||
|
|
||||||
FsFile file;
|
|
||||||
|
|
||||||
// Ping-pong DMA buffer.
|
|
||||||
DMAMEM static uint16_t __attribute__((aligned(32))) dmaBuf[2][256];
|
|
||||||
|
|
||||||
// Count of DMA interrupts.
|
|
||||||
volatile size_t dmaCount;
|
|
||||||
|
|
||||||
// RingBuf for 512 byte sectors.
|
|
||||||
RingBuf<FsFile, RING_BUF_SIZE> rb;
|
|
||||||
|
|
||||||
// Shared between ISR and background.
|
|
||||||
volatile size_t maxBytesUsed;
|
|
||||||
|
|
||||||
// Overrun error for write to RingBuf.
|
|
||||||
volatile bool overrun;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// ISR for DMA.
|
|
||||||
static void isr() {
|
|
||||||
if (!overrun) {
|
|
||||||
// Clear cache for buffer filled by DMA to insure read from DMA memory.
|
|
||||||
arm_dcache_delete((void*)dmaBuf[dmaCount & 1], 512);
|
|
||||||
// Enable RingBuf functions to be called in ISR.
|
|
||||||
rb.beginISR();
|
|
||||||
if (rb.write(dmaBuf[dmaCount & 1], 512) == 512) {
|
|
||||||
dmaCount++;
|
|
||||||
if (rb.bytesUsed() > maxBytesUsed) {
|
|
||||||
maxBytesUsed = rb.bytesUsed();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
overrun = true;
|
|
||||||
}
|
|
||||||
// End use of RingBuf functions in ISR.
|
|
||||||
rb.endISR();
|
|
||||||
}
|
|
||||||
dma.clearComplete();
|
|
||||||
dma.clearInterrupt();
|
|
||||||
#if defined(__IMXRT1062__)
|
|
||||||
// Handle clear interrupt glitch in Teensy 4.x!
|
|
||||||
asm("DSB");
|
|
||||||
#endif // defined(__IMXRT1062__)
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
#if defined(__IMXRT1062__) // Teensy 4.x
|
|
||||||
#define SOURCE_SADDR ADC1_R0
|
|
||||||
#define SOURCE_EVENT DMAMUX_SOURCE_ADC1
|
|
||||||
#else
|
|
||||||
#define SOURCE_SADDR ADC0_RA
|
|
||||||
#define SOURCE_EVENT DMAMUX_SOURCE_ADC0
|
|
||||||
#endif
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
static void init(uint8_t pin) {
|
|
||||||
dma.begin();
|
|
||||||
dma.attachInterrupt(isr);
|
|
||||||
dma.source((volatile const signed short&)SOURCE_SADDR);
|
|
||||||
dma.destinationBuffer((volatile uint16_t*)dmaBuf, sizeof(dmaBuf));
|
|
||||||
dma.interruptAtHalf();
|
|
||||||
dma.interruptAtCompletion();
|
|
||||||
dma.triggerAtHardwareEvent(SOURCE_EVENT);
|
|
||||||
dma.enable();
|
|
||||||
adc.adc0->enableDMA();
|
|
||||||
adc.adc0->startContinuous(pin);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void stopDma() {
|
|
||||||
adc.adc0->disableDMA();
|
|
||||||
dma.disable();
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printTest(Print* pr) {
|
|
||||||
if (file.fileSize() < 1024 * 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
file.rewind();
|
|
||||||
rb.begin(&file);
|
|
||||||
// Could readIn RING_BUF_SIZE bytes and write to a csv file in a loop.
|
|
||||||
if (rb.readIn(2048) != 2048) {
|
|
||||||
sd.errorHalt("rb.readIn failed");
|
|
||||||
}
|
|
||||||
uint16_t data;
|
|
||||||
for (size_t i = 0; i < 1024; i++) {
|
|
||||||
pr->print(i);
|
|
||||||
pr->print(',');
|
|
||||||
// Test read with: template <typename Type>bool read(Type* data).
|
|
||||||
rb.read(&data);
|
|
||||||
pr->println(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void runTest(uint8_t pin) {
|
|
||||||
dmaCount = 0;
|
|
||||||
maxBytesUsed = 0;
|
|
||||||
overrun = false;
|
|
||||||
do {
|
|
||||||
delay(10);
|
|
||||||
} while (Serial.read() >= 0);
|
|
||||||
|
|
||||||
if (!file.open("IsrLoggerTest.bin", O_CREAT | O_TRUNC | O_RDWR)) {
|
|
||||||
sd.errorHalt("file.open failed");
|
|
||||||
}
|
|
||||||
if (!file.preAllocate(PRE_ALLOCATE_SIZE)) {
|
|
||||||
sd.errorHalt("file.preAllocate failed");
|
|
||||||
}
|
|
||||||
rb.begin(&file);
|
|
||||||
Serial.println("Type any character to stop\n");
|
|
||||||
|
|
||||||
init(pin);
|
|
||||||
uint32_t samplingTime = micros();
|
|
||||||
while (!overrun && !Serial.available()) {
|
|
||||||
size_t n = rb.bytesUsed();
|
|
||||||
if ((n + file.curPosition()) >= (PRE_ALLOCATE_SIZE - 512)) {
|
|
||||||
Serial.println("File full - stopping");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (n >= 512) {
|
|
||||||
if (rb.writeOut(512) != 512) {
|
|
||||||
Serial.println("writeOut() failed");
|
|
||||||
file.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stopDma();
|
|
||||||
samplingTime = micros() - samplingTime;
|
|
||||||
if (!rb.sync()) {
|
|
||||||
Serial.println("sync() failed");
|
|
||||||
file.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!file.truncate()) {
|
|
||||||
sd.errorHalt("truncate failed");
|
|
||||||
}
|
|
||||||
if (overrun) {
|
|
||||||
Serial.println("Overrun ERROR!!");
|
|
||||||
}
|
|
||||||
Serial.print("dmsCount ");
|
|
||||||
Serial.println(dmaCount);
|
|
||||||
Serial.print("RingBufSize ");
|
|
||||||
Serial.println(RING_BUF_SIZE);
|
|
||||||
Serial.print("maxBytesUsed ");
|
|
||||||
Serial.println(maxBytesUsed);
|
|
||||||
Serial.print("fileSize ");
|
|
||||||
file.printFileSize(&Serial);
|
|
||||||
Serial.println();
|
|
||||||
Serial.print(0.000001 * samplingTime);
|
|
||||||
Serial.println(" seconds");
|
|
||||||
Serial.print(1.0 * file.fileSize() / samplingTime, 3);
|
|
||||||
Serial.println(" MB/sec\n");
|
|
||||||
printTest(&Serial);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void waitSerial(const char* msg) {
|
|
||||||
do {
|
|
||||||
delay(10);
|
|
||||||
} while (Serial.read() >= 0);
|
|
||||||
Serial.println(msg);
|
|
||||||
while (!Serial.available()) {
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
waitSerial("Type any character to begin");
|
|
||||||
Serial.print("FreeStack: ");
|
|
||||||
Serial.println(FreeStack());
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
// Try for max speed.
|
|
||||||
adc.adc0->setAveraging(1);
|
|
||||||
adc.adc0->setResolution(10);
|
|
||||||
adc.adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
|
|
||||||
adc.adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
|
|
||||||
runTest(ADC_PIN);
|
|
||||||
waitSerial("Type any character to run test again");
|
|
||||||
}
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
// Test of time-stamp callback with Teensy 3/4.
|
|
||||||
// The upload time will be used to set the RTC.
|
|
||||||
// You must arrange for syncing the RTC.
|
|
||||||
#include <TimeLib.h>
|
|
||||||
|
|
||||||
#include "SdFat.h"
|
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
/*
|
|
||||||
Change the value of SD_CS_PIN if you are using SPI and
|
|
||||||
your hardware does not use the default value, SS.
|
|
||||||
Common values are:
|
|
||||||
Arduino Ethernet shield: pin 4
|
|
||||||
Sparkfun SD shield: pin 8
|
|
||||||
Adafruit SD shields and modules: pin 10
|
|
||||||
*/
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
|
|
||||||
#define SPI_CLOCK SD_SCK_MHZ(50)
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
|
||||||
File file;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Call back for file timestamps. Only called for file create and sync().
|
|
||||||
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
|
||||||
// Return date using FS_DATE macro to format fields.
|
|
||||||
*date = FS_DATE(year(), month(), day());
|
|
||||||
|
|
||||||
// Return time using FS_TIME macro to format fields.
|
|
||||||
*time = FS_TIME(hour(), minute(), second());
|
|
||||||
|
|
||||||
// Return low time bits in units of 10 ms.
|
|
||||||
*ms10 = second() & 1 ? 100 : 0;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
time_t getTeensy3Time() { return Teensy3Clock.get(); }
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printField(Print* pr, char sep, uint8_t v) {
|
|
||||||
if (sep) {
|
|
||||||
pr->write(sep);
|
|
||||||
}
|
|
||||||
if (v < 10) {
|
|
||||||
pr->write('0');
|
|
||||||
}
|
|
||||||
pr->print(v);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void printNow(Print* pr) {
|
|
||||||
pr->print(year());
|
|
||||||
printField(pr, '-', month());
|
|
||||||
printField(pr, '-', day());
|
|
||||||
printField(pr, ' ', hour());
|
|
||||||
printField(pr, ':', minute());
|
|
||||||
printField(pr, ':', second());
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
// set the Time library to use Teensy 3.0's RTC to keep time
|
|
||||||
setSyncProvider(getTeensy3Time);
|
|
||||||
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.println(F("Type any character to begin"));
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
if (timeStatus() != timeSet) {
|
|
||||||
Serial.println("Unable to sync with the RTC");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial.print(F("DateTime::now "));
|
|
||||||
printNow(&Serial);
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// Set callback
|
|
||||||
FsDateTime::setCallback(dateTime);
|
|
||||||
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
// Remove old version to set create time.
|
|
||||||
if (sd.exists("RtcTest.txt")) {
|
|
||||||
sd.remove("RtcTest.txt");
|
|
||||||
}
|
|
||||||
if (!file.open("RtcTest.txt", FILE_WRITE)) {
|
|
||||||
Serial.println(F("file.open failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Print current date time to file.
|
|
||||||
file.print(F("Test file at: "));
|
|
||||||
printNow(&file);
|
|
||||||
file.println();
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
// List files in SD root.
|
|
||||||
sd.ls(LS_DATE | LS_SIZE);
|
|
||||||
Serial.println(F("Done"));
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {}
|
|
||||||
|
|
@ -1,42 +1,23 @@
|
||||||
// Simple performance test for Teensy 3.5/3.6 4.0 SDHC.
|
// Simple performance test for Teensy 3.5/3.6 SDHC.
|
||||||
// Demonstrates yield() efficiency for SDIO modes.
|
// 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"
|
#include "SdFat.h"
|
||||||
|
|
||||||
// Use built-in SD for SPI modes on Teensy 3.5/3.6.
|
|
||||||
// Teensy 4.0 use first SPI port.
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#define SD_FAT_TYPE 3
|
|
||||||
|
|
||||||
// 32 KiB buffer.
|
// 32 KiB buffer.
|
||||||
const size_t BUF_DIM = 32768;
|
const size_t BUF_DIM = 32768;
|
||||||
|
|
||||||
// 8 MiB file.
|
// 8 MiB file.
|
||||||
const uint32_t FILE_SIZE = 256UL * BUF_DIM;
|
const uint32_t FILE_SIZE = 256UL*BUF_DIM;
|
||||||
|
|
||||||
|
SdFatSdio sd;
|
||||||
|
|
||||||
|
SdFatSdioEX sdEx;
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
|
||||||
File file;
|
File file;
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
uint8_t buf[BUF_DIM];
|
uint8_t buf[BUF_DIM];
|
||||||
|
|
||||||
|
|
@ -51,33 +32,24 @@ uint32_t yieldMicros = 0;
|
||||||
uint32_t yieldCalls = 0;
|
uint32_t yieldCalls = 0;
|
||||||
// Max busy time for single yield call.
|
// Max busy time for single yield call.
|
||||||
uint32_t yieldMaxUsec = 0;
|
uint32_t yieldMaxUsec = 0;
|
||||||
//------------------------------------------------------------------------------
|
// Control access to the two versions of SdFat.
|
||||||
void clearSerialInput() {
|
bool useEx = false;
|
||||||
uint32_t m = micros();
|
//-----------------------------------------------------------------------------
|
||||||
do {
|
bool sdBusy() {
|
||||||
if (Serial.read() >= 0) {
|
return useEx ? sdEx.card()->isBusy() : sd.card()->isBusy();
|
||||||
m = micros();
|
|
||||||
}
|
|
||||||
} while (micros() - m < 10000);
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void errorHalt(const char* msg) {
|
void errorHalt(const char* msg) {
|
||||||
Serial.print("Error: ");
|
if (useEx) {
|
||||||
Serial.println(msg);
|
sdEx.errorHalt(msg);
|
||||||
if (sd.sdErrorCode()) {
|
} else {
|
||||||
if (sd.sdErrorCode() == SD_CARD_ERROR_ACMD41) {
|
sd.errorHalt(msg);
|
||||||
Serial.println("Try power cycling the SD card.");
|
|
||||||
}
|
|
||||||
printSdErrorSymbol(&Serial, sd.sdErrorCode());
|
|
||||||
Serial.print(", ErrorData: 0X");
|
|
||||||
Serial.println(sd.sdErrorData(), HEX);
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool ready = false;
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
bool sdBusy() { return ready ? sd.card()->isBusy() : false; }
|
uint32_t kHzSdClk() {
|
||||||
|
return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk();
|
||||||
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Replace "weak" system yield() function.
|
// Replace "weak" system yield() function.
|
||||||
void yield() {
|
void yield() {
|
||||||
|
|
@ -96,7 +68,7 @@ void yield() {
|
||||||
}
|
}
|
||||||
yieldMicros += m;
|
yieldMicros += m;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void runTest() {
|
void runTest() {
|
||||||
// Zero Stats
|
// Zero Stats
|
||||||
totalMicros = 0;
|
totalMicros = 0;
|
||||||
|
|
@ -109,25 +81,22 @@ void runTest() {
|
||||||
Serial.println("\nsize,write,read");
|
Serial.println("\nsize,write,read");
|
||||||
Serial.println("bytes,KB/sec,KB/sec");
|
Serial.println("bytes,KB/sec,KB/sec");
|
||||||
for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) {
|
for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) {
|
||||||
uint32_t nRdWr = FILE_SIZE / nb;
|
file.truncate(0);
|
||||||
if (!file.truncate(0)) {
|
uint32_t nRdWr = FILE_SIZE/nb;
|
||||||
errorHalt("truncate failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.print(nb);
|
Serial.print(nb);
|
||||||
Serial.print(',');
|
Serial.print(',');
|
||||||
uint32_t t = micros();
|
uint32_t t = micros();
|
||||||
for (uint32_t n = 0; n < nRdWr; n++) {
|
for (uint32_t n = 0; n < nRdWr; n++) {
|
||||||
// Set start and end of buffer.
|
// Set start and end of buffer.
|
||||||
buf32[0] = n;
|
buf32[0] = n;
|
||||||
buf32[nb / 4 - 1] = n;
|
buf32[nb/4 - 1] = n;
|
||||||
if (nb != file.write(buf, nb)) {
|
if (nb != file.write(buf, nb)) {
|
||||||
errorHalt("write failed");
|
errorHalt("write failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t = micros() - t;
|
t = micros() - t;
|
||||||
totalMicros += t;
|
totalMicros += t;
|
||||||
Serial.print(1000.0 * FILE_SIZE / t);
|
Serial.print(1000.0*FILE_SIZE/t);
|
||||||
Serial.print(',');
|
Serial.print(',');
|
||||||
file.rewind();
|
file.rewind();
|
||||||
t = micros();
|
t = micros();
|
||||||
|
|
@ -137,13 +106,13 @@ void runTest() {
|
||||||
errorHalt("read failed");
|
errorHalt("read failed");
|
||||||
}
|
}
|
||||||
// crude check of data.
|
// crude check of data.
|
||||||
if (buf32[0] != n || buf32[nb / 4 - 1] != n) {
|
if (buf32[0] != n || buf32[nb/4 - 1] != n) {
|
||||||
errorHalt("data check");
|
errorHalt("data check");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t = micros() - t;
|
t = micros() - t;
|
||||||
totalMicros += t;
|
totalMicros += t;
|
||||||
Serial.println(1000.0 * FILE_SIZE / t);
|
Serial.println(1000.0*FILE_SIZE/t);
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
Serial.print("\ntotalMicros ");
|
Serial.print("\ntotalMicros ");
|
||||||
|
|
@ -154,67 +123,47 @@ void runTest() {
|
||||||
Serial.println(yieldCalls);
|
Serial.println(yieldCalls);
|
||||||
Serial.print("yieldMaxUsec ");
|
Serial.print("yieldMaxUsec ");
|
||||||
Serial.println(yieldMaxUsec);
|
Serial.println(yieldMaxUsec);
|
||||||
// Serial.print("kHzSdClk ");
|
Serial.print("kHzSdClk ");
|
||||||
// Serial.println(kHzSdClk());
|
Serial.println(kHzSdClk());
|
||||||
Serial.println("Done");
|
Serial.println("Done");
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
while (!Serial) {
|
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() {
|
void loop() {
|
||||||
static bool warn = true;
|
do {
|
||||||
if (warn) {
|
delay(10);
|
||||||
warn = false;
|
} while (Serial.available() && Serial.read());
|
||||||
Serial.println(
|
|
||||||
"SD cards must be power cycled to leave\n"
|
|
||||||
"SPI mode so do SDIO tests first.\n"
|
|
||||||
"\nCycle power on the card if an error occurs.");
|
|
||||||
}
|
|
||||||
clearSerialInput();
|
|
||||||
|
|
||||||
Serial.println(
|
Serial.println("Type '1' for SdFatSdioEX or '2' for SdFatSdio");
|
||||||
"\nType '1' for FIFO SDIO"
|
|
||||||
"\n '2' for DMA SDIO"
|
|
||||||
"\n '3' for Dedicated SPI"
|
|
||||||
"\n '4' for Shared SPI");
|
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
}
|
}
|
||||||
char c = Serial.read();
|
char c = Serial.read();
|
||||||
|
if (c != '1' && c != '2') {
|
||||||
if (c == '1') {
|
|
||||||
if (!sd.begin(SdioConfig(FIFO_SDIO))) {
|
|
||||||
errorHalt("begin failed");
|
|
||||||
}
|
|
||||||
Serial.println("\nFIFO SDIO mode.");
|
|
||||||
} else if (c == '2') {
|
|
||||||
if (!sd.begin(SdioConfig(DMA_SDIO))) {
|
|
||||||
errorHalt("begin failed");
|
|
||||||
}
|
|
||||||
Serial.println("\nDMA SDIO mode - slow for small transfers.");
|
|
||||||
} else if (c == '3') {
|
|
||||||
#if ENABLE_DEDICATED_SPI
|
|
||||||
if (!sd.begin(SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50)))) {
|
|
||||||
errorHalt("begin failed");
|
|
||||||
}
|
|
||||||
Serial.println("\nDedicated SPI mode.");
|
|
||||||
#else // ENABLE_DEDICATED_SPI
|
|
||||||
Serial.println("ENABLE_DEDICATED_SPI must be non-zero.");
|
|
||||||
return;
|
|
||||||
#endif // ENABLE_DEDICATED_SPI
|
|
||||||
} else if (c == '4') {
|
|
||||||
if (!sd.begin(SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50)))) {
|
|
||||||
errorHalt("begin failed");
|
|
||||||
}
|
|
||||||
Serial.println("\nShared SPI mode - slow for small transfers.");
|
|
||||||
} else {
|
|
||||||
Serial.println("Invalid input");
|
Serial.println("Invalid input");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ready = true;
|
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();
|
runTest();
|
||||||
ready = false;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
// Test Teensy SDIO with write busy in a data logger demo.
|
|
||||||
//
|
|
||||||
// The driver writes to the uSDHC controller's FIFO then returns
|
|
||||||
// while the controller writes the data to the SD. The first sector
|
|
||||||
// puts the controller in write mode and takes about 11 usec on a
|
|
||||||
// Teensy 4.1. About 5 usec is required to write a sector when the
|
|
||||||
// controller is in write mode.
|
|
||||||
#include "SdFat.h"
|
|
||||||
#include "RingBuf.h"
|
|
||||||
|
|
||||||
// Use Teensy SDIO
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
|
|
||||||
// Interval between points for 25 ksps.
|
|
||||||
#define LOG_INTERVAL_USEC 40
|
|
||||||
|
|
||||||
// Size to log 10 byte lines at 25 kHz for more than ten minutes.
|
|
||||||
#define LOG_FILE_SIZE 10 * 25000 * 600 // 150,000,000 bytes.
|
|
||||||
|
|
||||||
// Space to hold more than 800 ms of data for 10 byte lines at 25 ksps.
|
|
||||||
#define RING_BUF_CAPACITY 400 * 512
|
|
||||||
#define LOG_FILENAME "SdioLogger.csv"
|
|
||||||
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
|
|
||||||
// RingBuf for File type FsFile.
|
|
||||||
RingBuf<FsFile, RING_BUF_CAPACITY> rb;
|
|
||||||
|
|
||||||
void logData() {
|
|
||||||
// Initialize the SD.
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
// Open or create file - truncate existing file.
|
|
||||||
if (!file.open(LOG_FILENAME, O_RDWR | O_CREAT | O_TRUNC)) {
|
|
||||||
Serial.println("open failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// File must be pre-allocated to avoid huge
|
|
||||||
// delays searching for free clusters.
|
|
||||||
if (!file.preAllocate(LOG_FILE_SIZE)) {
|
|
||||||
Serial.println("preAllocate failed\n");
|
|
||||||
file.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// initialize the RingBuf.
|
|
||||||
rb.begin(&file);
|
|
||||||
Serial.println("Type any character to stop");
|
|
||||||
|
|
||||||
// Max RingBuf used bytes. Useful to understand RingBuf overrun.
|
|
||||||
size_t maxUsed = 0;
|
|
||||||
|
|
||||||
// Min spare micros in loop.
|
|
||||||
int32_t minSpareMicros = INT32_MAX;
|
|
||||||
|
|
||||||
// Start time.
|
|
||||||
uint32_t logTime = micros();
|
|
||||||
// Log data until Serial input or file full.
|
|
||||||
while (!Serial.available()) {
|
|
||||||
// Amount of data in ringBuf.
|
|
||||||
size_t n = rb.bytesUsed();
|
|
||||||
if ((n + file.curPosition()) > (LOG_FILE_SIZE - 20)) {
|
|
||||||
Serial.println("File full - quitting.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (n > maxUsed) {
|
|
||||||
maxUsed = n;
|
|
||||||
}
|
|
||||||
if (n >= 512 && !file.isBusy()) {
|
|
||||||
// Not busy only allows one sector before possible busy wait.
|
|
||||||
// Write one sector from RingBuf to file.
|
|
||||||
if (512 != rb.writeOut(512)) {
|
|
||||||
Serial.println("writeOut failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Time for next point.
|
|
||||||
logTime += LOG_INTERVAL_USEC;
|
|
||||||
int32_t spareMicros = logTime - micros();
|
|
||||||
if (spareMicros < minSpareMicros) {
|
|
||||||
minSpareMicros = spareMicros;
|
|
||||||
}
|
|
||||||
if (spareMicros <= 0) {
|
|
||||||
Serial.print("Rate too fast ");
|
|
||||||
Serial.println(spareMicros);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Wait until time to log data.
|
|
||||||
while (micros() < logTime) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read ADC0 - about 17 usec on Teensy 4, Teensy 3.6 is faster.
|
|
||||||
uint16_t adc = analogRead(0);
|
|
||||||
// Print spareMicros into the RingBuf as test data.
|
|
||||||
rb.print(spareMicros);
|
|
||||||
rb.write(',');
|
|
||||||
// Print adc into RingBuf.
|
|
||||||
rb.println(adc);
|
|
||||||
if (rb.getWriteError()) {
|
|
||||||
// Error caused by too few free bytes in RingBuf.
|
|
||||||
Serial.println("WriteError");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Write any RingBuf data to file.
|
|
||||||
rb.sync();
|
|
||||||
file.truncate();
|
|
||||||
file.rewind();
|
|
||||||
// Print first twenty lines of file.
|
|
||||||
Serial.println("spareMicros,ADC0");
|
|
||||||
for (uint8_t n = 0; n < 20 && file.available();) {
|
|
||||||
int c = file.read();
|
|
||||||
if (c < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Serial.write(c);
|
|
||||||
if (c == '\n') n++;
|
|
||||||
}
|
|
||||||
Serial.print("fileSize: ");
|
|
||||||
Serial.println((uint32_t)file.fileSize());
|
|
||||||
Serial.print("maxBytesUsed: ");
|
|
||||||
Serial.println(maxUsed);
|
|
||||||
Serial.print("minSpareMicros: ");
|
|
||||||
Serial.println(minSpareMicros);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
void clearSerialInput() {
|
|
||||||
for (uint32_t m = micros(); micros() - m < 10000;) {
|
|
||||||
if (Serial.read() >= 0) {
|
|
||||||
m = micros();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
}
|
|
||||||
// Go faster or log more channels. ADC quality will suffer.
|
|
||||||
// analogReadAveraging(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
clearSerialInput();
|
|
||||||
Serial.println("Type any character to start");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
}
|
|
||||||
clearSerialInput();
|
|
||||||
logData();
|
|
||||||
}
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include "SdFat.h"
|
#include "SdFat.h"
|
||||||
#include "sdios.h"
|
#include "sdios.h"
|
||||||
|
|
||||||
SdFat sd;
|
SdFat sd;
|
||||||
|
|
||||||
SdFile file;
|
SdFile file;
|
||||||
|
|
@ -51,12 +52,25 @@ void dateTime(uint16_t* date, uint16_t* time) {
|
||||||
* Function to print all timestamps.
|
* Function to print all timestamps.
|
||||||
*/
|
*/
|
||||||
void printTimestamps(SdFile& f) {
|
void printTimestamps(SdFile& f) {
|
||||||
|
dir_t d;
|
||||||
|
if (!f.dirEntry(&d)) {
|
||||||
|
error("f.dirEntry failed");
|
||||||
|
}
|
||||||
|
|
||||||
cout << F("Creation: ");
|
cout << F("Creation: ");
|
||||||
f.printCreateDateTime(&Serial);
|
f.printFatDate(d.creationDate);
|
||||||
cout << endl << F("Modify: ");
|
cout << ' ';
|
||||||
f.printModifyDateTime(&Serial);
|
f.printFatTime(d.creationTime);
|
||||||
cout << endl << F("Access: ");
|
cout << endl;
|
||||||
f.printAccessDateTime(&Serial);
|
|
||||||
|
cout << F("Modify: ");
|
||||||
|
f.printFatDate(d.lastWriteDate);
|
||||||
|
cout <<' ';
|
||||||
|
f.printFatTime(d.lastWriteTime);
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
cout << F("Access: ");
|
||||||
|
f.printFatDate(d.lastAccessDate);
|
||||||
cout << endl;
|
cout << endl;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
@ -64,11 +78,11 @@ void setup(void) {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
cout << F("Type any character to start\n");
|
cout << F("Type any character to start\n");
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
// Initialize at the highest speed supported by the board that is
|
// Initialize at the highest speed supported by the board that is
|
||||||
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
// not over 50 MHz. Try a lower speed if SPI errors occur.
|
||||||
|
|
@ -16,7 +16,7 @@ const uint8_t BUF_DIM = 100;
|
||||||
uint8_t buf[BUF_DIM];
|
uint8_t buf[BUF_DIM];
|
||||||
|
|
||||||
const uint32_t FILE_SIZE = 1000000;
|
const uint32_t FILE_SIZE = 1000000;
|
||||||
const uint32_t NWRITE = FILE_SIZE/BUF_DIM;
|
const uint16_t NWRITE = FILE_SIZE/BUF_DIM;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// print error msg, any SD error codes, and halt.
|
// print error msg, any SD error codes, and halt.
|
||||||
// store messages in flash
|
// store messages in flash
|
||||||
|
|
@ -27,7 +27,7 @@ void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
// Wait for USB Serial
|
// Wait for USB Serial
|
||||||
while (!Serial) {
|
while (!Serial) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
Serial.print(F("FreeStack: "));
|
Serial.print(F("FreeStack: "));
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ void setup() {
|
||||||
|
|
||||||
Serial.println(F("type any character to start"));
|
Serial.println(F("type any character to start"));
|
||||||
while (!Serial.available()) {
|
while (!Serial.available()) {
|
||||||
yield();
|
SysCall::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable sd2 while initializing sd1
|
// disable sd2 while initializing sd1
|
||||||
|
|
@ -107,7 +107,7 @@ void setup() {
|
||||||
Serial.println(F("Writing test.bin to sd1"));
|
Serial.println(F("Writing test.bin to sd1"));
|
||||||
|
|
||||||
// write data to /Dir1/test.bin on sd1
|
// write data to /Dir1/test.bin on sd1
|
||||||
for (uint32_t i = 0; i < NWRITE; i++) {
|
for (uint16_t i = 0; i < NWRITE; i++) {
|
||||||
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
|
if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
|
||||||
sd1.errorExit("sd1.write");
|
sd1.errorExit("sd1.write");
|
||||||
}
|
}
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
feather_esp32s2
|
|
||||||
feather_esp32s3
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
// Simple test of Unicode filename.
|
|
||||||
// Unicode is supported as UTF-8 encoded strings.
|
|
||||||
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
|
|
||||||
#include "SdFat.h"
|
|
||||||
|
|
||||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
|
||||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
|
||||||
#if defined __has_include
|
|
||||||
#if __has_include(<FS.h>)
|
|
||||||
#define SD_FAT_TYPE 3 // Can't use SdFat/File
|
|
||||||
#endif // __has_include(<FS.h>)
|
|
||||||
#endif // defined __has_include
|
|
||||||
|
|
||||||
// USE_UTF8_LONG_NAMES must be non-zero in SdFat/src/SdFatCongfig.h
|
|
||||||
#if USE_UTF8_LONG_NAMES
|
|
||||||
|
|
||||||
#define UTF8_FOLDER u8"😀"
|
|
||||||
const char* names[] = {u8"россиянин", u8"très élégant", u8"狗.txt", nullptr};
|
|
||||||
|
|
||||||
// Remove files if non-zero.
|
|
||||||
#define REMOVE_UTF8_FILES 1
|
|
||||||
|
|
||||||
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
|
|
||||||
#ifndef SDCARD_SS_PIN
|
|
||||||
const uint8_t SD_CS_PIN = SS;
|
|
||||||
#else // SDCARD_SS_PIN
|
|
||||||
// Assume built-in SD is used.
|
|
||||||
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
|
|
||||||
#endif // SDCARD_SS_PIN
|
|
||||||
|
|
||||||
// Try to select the best SD card configuration.
|
|
||||||
#if defined(HAS_TEENSY_SDIO)
|
|
||||||
#define SD_CONFIG SdioConfig(FIFO_SDIO)
|
|
||||||
#elif defined(RP_CLK_GPIO) && defined(RP_CMD_GPIO) && defined(RP_DAT0_GPIO)
|
|
||||||
// See the Rp2040SdioSetup example for RP2040/RP2350 boards.
|
|
||||||
#define SD_CONFIG SdioConfig(RP_CLK_GPIO, RP_CMD_GPIO, RP_DAT0_GPIO)
|
|
||||||
#elif ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16))
|
|
||||||
#else // HAS_TEENSY_SDIO
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(16))
|
|
||||||
#endif // HAS_TEENSY_SDIO
|
|
||||||
|
|
||||||
#if SD_FAT_TYPE == 0
|
|
||||||
SdFat sd;
|
|
||||||
File file;
|
|
||||||
#elif SD_FAT_TYPE == 1
|
|
||||||
SdFat32 sd;
|
|
||||||
File32 file;
|
|
||||||
#elif SD_FAT_TYPE == 2
|
|
||||||
SdExFat sd;
|
|
||||||
ExFile file;
|
|
||||||
#elif SD_FAT_TYPE == 3
|
|
||||||
SdFs sd;
|
|
||||||
FsFile file;
|
|
||||||
#else // SD_FAT_TYPE
|
|
||||||
#error Invalid SD_FAT_TYPE
|
|
||||||
#endif // SD_FAT_TYPE
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
while (!Serial) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
Serial.println("Type any character to begin");
|
|
||||||
while (!Serial.available()) {
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
if (!sd.exists(UTF8_FOLDER)) {
|
|
||||||
if (!sd.mkdir(UTF8_FOLDER)) {
|
|
||||||
Serial.println("sd.mkdir failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!sd.chdir(UTF8_FOLDER)) {
|
|
||||||
Serial.println("sd.chdir failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (uint8_t i = 0; names[i]; i++) {
|
|
||||||
if (!file.open(names[i], O_WRONLY | O_CREAT)) {
|
|
||||||
Serial.println("file.open failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
file.println(names[i]);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
Serial.println("ls:");
|
|
||||||
sd.ls("/", LS_SIZE | LS_R);
|
|
||||||
#if REMOVE_UTF8_FILES // For debug test of remove and rmdir.
|
|
||||||
for (uint8_t i = 0; names[i]; i++) {
|
|
||||||
sd.remove(names[i]);
|
|
||||||
}
|
|
||||||
sd.chdir();
|
|
||||||
sd.rmdir(UTF8_FOLDER);
|
|
||||||
Serial.println("After remove and rmdir");
|
|
||||||
sd.ls(LS_SIZE | LS_R);
|
|
||||||
#endif // REMOVE_UTF8_FILES
|
|
||||||
Serial.println("Done!");
|
|
||||||
}
|
|
||||||
void loop() {}
|
|
||||||
#else // USE_UTF8_LONG_NAMES
|
|
||||||
#error USE_UTF8_LONG_NAMES must be non-zero in SdFat/src/SdFatCongfig.h
|
|
||||||
#endif // USE_UTF8_LONG_NAMES
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// An example of an external chip select functions.
|
|
||||||
// Useful for port expanders or replacement of the standard GPIO functions.
|
|
||||||
//
|
|
||||||
#include "SdFat.h"
|
|
||||||
|
|
||||||
// SD_CHIP_SELECT_MODE must be set to one or two in SdFat/SdFatConfig.h.
|
|
||||||
// A value of one allows optional replacement and two requires replacement.
|
|
||||||
#if SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
|
|
||||||
|
|
||||||
// SD chip select pin.
|
|
||||||
#define SD_CS_PIN SS
|
|
||||||
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50))
|
|
||||||
|
|
||||||
SdFat sd;
|
|
||||||
|
|
||||||
// Stats to verify function calls.
|
|
||||||
uint32_t initCalls = 0;
|
|
||||||
uint32_t writeCalls = 0;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Modify these functions for your port expander or custom GPIO library.
|
|
||||||
void sdCsInit(SdCsPin_t pin) {
|
|
||||||
initCalls++;
|
|
||||||
pinMode(pin, OUTPUT);
|
|
||||||
}
|
|
||||||
void sdCsWrite(SdCsPin_t pin, bool level) {
|
|
||||||
writeCalls++;
|
|
||||||
digitalWrite(pin, level);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
sd.ls(&Serial, LS_SIZE);
|
|
||||||
|
|
||||||
Serial.print(F("sdCsInit calls: "));
|
|
||||||
Serial.println(initCalls);
|
|
||||||
Serial.print(F("sdCsWrite calls: "));
|
|
||||||
Serial.println(writeCalls);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {}
|
|
||||||
#else // SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
|
|
||||||
#error SD_CHIP_SELECT_MODE must be one or two in SdFat/SdFatConfig.h
|
|
||||||
#endif // SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
// An example of an external SPI driver.
|
|
||||||
//
|
|
||||||
#include "SPI.h" // Only required if you use features in the SPI library.
|
|
||||||
#include "SdFat.h"
|
|
||||||
|
|
||||||
#if SPI_DRIVER_SELECT == 3 // Must be set in SdFat/SdFatConfig.h
|
|
||||||
|
|
||||||
// SD chip select pin.
|
|
||||||
#define SD_CS_PIN SS
|
|
||||||
|
|
||||||
// This is a simple driver based on the the standard SPI.h library.
|
|
||||||
// You can write a driver entirely independent of SPI.h.
|
|
||||||
// It can be optimized for your board or a different SPI port can be used.
|
|
||||||
// The driver must be derived from SdSpiBaseClass.
|
|
||||||
// See: SdFat/src/SpiDriver/SdSpiBaseClass.h
|
|
||||||
class MySpiClass : public SdSpiBaseClass {
|
|
||||||
public:
|
|
||||||
// Activate SPI hardware with correct speed and mode.
|
|
||||||
void activate() { SPI.beginTransaction(m_spiSettings); }
|
|
||||||
// Initialize the SPI bus.
|
|
||||||
void begin(SdSpiConfig config) {
|
|
||||||
(void)config;
|
|
||||||
SPI.begin();
|
|
||||||
}
|
|
||||||
// Deactivate SPI hardware.
|
|
||||||
void deactivate() { SPI.endTransaction(); }
|
|
||||||
// Receive a byte.
|
|
||||||
uint8_t receive() { return SPI.transfer(0XFF); }
|
|
||||||
// Receive multiple bytes.
|
|
||||||
// Replace this function if your board has multiple byte receive.
|
|
||||||
uint8_t receive(uint8_t* buf, size_t count) {
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
buf[i] = SPI.transfer(0XFF);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Send a byte.
|
|
||||||
void send(uint8_t data) { SPI.transfer(data); }
|
|
||||||
// Send multiple bytes.
|
|
||||||
// Replace this function if your board has multiple byte send.
|
|
||||||
void send(const uint8_t* buf, size_t count) {
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
SPI.transfer(buf[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Save SPISettings for new max SCK frequency
|
|
||||||
void setSckSpeed(uint32_t maxSck) {
|
|
||||||
m_spiSettings = SPISettings(maxSck, MSBFIRST, SPI_MODE0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
SPISettings m_spiSettings;
|
|
||||||
} mySpi;
|
|
||||||
#if ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50), &mySpi)
|
|
||||||
#else // ENABLE_DEDICATED_SPI
|
|
||||||
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50), &mySpi)
|
|
||||||
#endif // ENABLE_DEDICATED_SPI
|
|
||||||
SdFat sd;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(9600);
|
|
||||||
if (!sd.begin(SD_CONFIG)) {
|
|
||||||
sd.initErrorHalt(&Serial);
|
|
||||||
}
|
|
||||||
sd.ls(&Serial, LS_SIZE);
|
|
||||||
Serial.println("Done");
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void loop() {}
|
|
||||||
#else // SPI_DRIVER_SELECT
|
|
||||||
#error SPI_DRIVER_SELECT must be three in SdFat/SdFatConfig.h
|
|
||||||
#endif // SPI_DRIVER_SELECT
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue