Compare commits

..

59 commits

Author SHA1 Message Date
Limor "Ladyada" Fried
8313193820
Update library.properties
bump
2022-01-14 11:05:11 -05:00
Ha Thach
e72d81e8aa
Merge pull request #12 from adafruit/work-with-rp2040
Work with rp2040
2022-01-14 18:48:02 +07:00
hathach
b11fcfdc59
correct skip test 2022-01-14 18:23:09 +07:00
hathach
7ec6995235
skip SD example for feather rp2040 2022-01-14 18:14:36 +07:00
hathach
4537faa145
also include samd, nrf52,esp32 2022-01-14 18:01:47 +07:00
hathach
fb0d11a405
specify rp2040 in architectures to be selected instead of built-in SdFat of rp2040 2022-01-14 17:58:07 +07:00
hathach
15c39defe5
add feather_rp2040_tinyusb to ci, expect ci to fail 2022-01-14 17:37:38 +07:00
Eva Herrada
3e298145ce
Bump to 1.4.0 2022-01-03 16:39:05 -05:00
Limor "Ladyada" Fried
5d752a3901
Merge pull request #11 from Travelbacon/master
Add support for Arduinu Uno WiFi R2
2021-12-29 12:10:17 -05:00
George
cf2a10a274
Add support for Arduinu Uno WiFi R2
Added support for the Arduino Uno WiFi R2. Solution found at https://forums.adafruit.com/viewtopic.php?f=47&t=161677#p799163 found by the user thorx.
2021-12-29 15:10:13 +01:00
Eva Herrada
df91320ee6
1.3.0 2021-12-20 23:04:59 -05:00
Ha Thach
476f8d979a
Merge pull request #9 from adafruit/fix-esp32-fcntl-warning
USE_FCNTL_H for esp32/s2
2021-12-13 23:59:50 +07:00
hathach
8190df112f
more ci fix 2021-12-13 23:40:02 +07:00
hathach
3f952664eb
more ci test 2021-12-13 22:31:21 +07:00
hathach
0f512b0155
include SD as dependency for ci 2021-12-13 18:44:46 +07:00
hathach
ca1cf4e0e1
sd dependency 2021-12-13 18:37:38 +07:00
hathach
a13979b5ca
fix some ci 2021-12-13 18:16:29 +07:00
hathach
4f3f65cb31
add ci build 2021-12-13 17:44:47 +07:00
hathach
d3cbd6511c
USE_FCNTL_H for esp32/s2 2021-12-13 17:25:35 +07:00
Limor "Ladyada" Fried
659033f387
Merge pull request #8 from paulhayes/master
Allow USE_STANDARD_SPI_LIBRARY to be set by compilation definition flag
2021-12-07 12:30:44 -05:00
Paul Hayes
367cde634d
Allow USE_STANDARD_SPI_LIBRARY to be set by compilation definition flag
I had to do this to allow alternative SPI when using Platformio. Here's how this is used in platformio.ini:
```
build_flags =
  -DUSE_STANDARD_SPI_LIBRARY=2
```
2021-12-07 13:12:07 +00:00
Dylan Herrada
68dd2d4d80
Bump to 1.2.4 2021-06-28 15:28:05 -04:00
Limor "Ladyada" Fried
54ca0c7012
Merge pull request #7 from adafruit/fix-warnings
fix warnings
2021-06-16 17:53:37 -04:00
hathach
850e1aaee1
fix warnings
warning: taking address of packed member of 'directoryEntry' may result
in an unaligned pointer value [-Waddress-of-packed-member]
2021-05-26 09:49:02 +07:00
hathach
b2a36980bb
Update library.properties 2019-09-09 21:59:36 +07:00
hathach
fa76619c03
Merge pull request #6 from hathach/master
clean up warnings
2019-09-09 21:58:35 +07:00
hathach
56d6610432 clean up warnings 2019-09-09 21:57:32 +07:00
Limor "Ladyada" Fried
cf8f5d8f61
Merge pull request #5 from hathach/master
fix writing to 2nd FAT with 1-fat filesystem
2019-09-05 02:02:20 -04:00
hathach
28fd5dc4fc fix writing to 2nd FAT with 1-fat filesystem 2019-09-05 11:56:55 +07:00
Ladyada
641f23b353 more esp32 skips and fix one compiletime 2019-07-27 18:23:42 -04:00
ladyada
5d726947af skip somre moe tests 2019-07-27 17:37:33 -04:00
ladyada
94aad3094c fix name 2019-07-27 16:48:22 -04:00
Limor "Ladyada" Fried
f1f0f93432
Update library.properties 2019-07-23 00:38:17 -04:00
Limor "Ladyada" Fried
d0a2565114
Update library.properties 2019-07-23 00:37:52 -04:00
ladyada
e157117f9c skip some more tests 2019-07-16 19:06:07 -04:00
ladyada
6324f8eed2 fix skipname 2019-07-16 18:42:13 -04:00
ladyada
fca3f1475f some skips, dont test extras 2019-07-16 18:40:43 -04:00
ladyada
1b0c9bce15 path 2019-07-16 18:30:49 -04:00
ladyada
0b0f1296d3 fix git clone 2019-07-16 18:10:35 -04:00
ladyada
81d38972f6 try installing i2cdev 2019-07-16 17:51:22 -04:00
ladyada
cb28106207 rm dir 2019-07-16 16:27:59 -04:00
ladyada
5de302b19a skip attic 2019-07-16 16:05:23 -04:00
ladyada
699c9e410e travis 2019-07-16 15:47:05 -04:00
Limor "Ladyada" Fried
18f071dc69
Merge pull request #3 from hathach/master
add m_fatCount to support either 1 or 2 FAT
2019-07-16 15:14:21 -04:00
Limor "Ladyada" Fried
31783e24e8
Merge branch 'master' into master 2019-07-16 15:13:50 -04:00
ladyada
013b7ea281 dont break things :( 2019-07-16 13:20:18 -04:00
hathach
829f4925f4 add m_fatCount to support either 1 or 2 FAT 2019-07-16 12:40:21 +07:00
ladyada
88902b576e support 1 or 2 FATs! 2019-07-16 00:14:01 -04:00
ladyada
f918ed0077 allow 1 or 2 FATs 2019-07-15 23:51:48 -04:00
ladyada
10cdd7964c dont use DMA by default (Audio doesnt like it) 2019-07-07 19:11:14 -04:00
Limor "Ladyada" Fried
ec247da5da
Merge pull request #2 from hathach/master
enable ENABLE_EXTENDED_TRANSFER_CLASS and FAT12_SUPPORT
2019-06-17 14:18:53 -04:00
hathach
f24364bff2 enable ENABLE_EXTENDED_TRANSFER_CLASS and FAT12_SUPPORT to support smal size block device such as external flash 2019-06-18 01:09:35 +07:00
Limor "Ladyada" Fried
9c046af871
Merge pull request #1 from adafruit/pb-samd-dma
Add DMA to SAMD21 and SAMD51
2019-05-14 00:52:28 -04:00
ladyada
2b053a1910 spacey boi 2019-05-14 00:51:58 -04:00
ladyada
7a8686d63d get back to original sdfat state in case we do a PR 2019-05-14 00:51:33 -04:00
ladyada
4d87dfbee3 Merge branch 'pb-samd-dma' of github.com:adafruit/SdFat into pb-samd-dma 2019-05-14 00:49:29 -04:00
ladyada
391949068e sending w/DMA isnt working yet (?) but reading does so lets just enable that 2019-05-14 00:48:39 -04:00
Phillip Burgess
98a88f0389 Remove debug println 2019-05-10 22:42:42 -07:00
Phillip Burgess
e0acc32b59 SAMD DMA support (requires Adafruit_ZeroDMA lib) 2019-05-10 21:43:10 -07:00
894 changed files with 126769 additions and 32630 deletions

View file

@ -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
View file

@ -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
View 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)

View file

@ -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
View file

@ -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.
![Alt text](images/SdioSpi.jpg)
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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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>&nbsp;</p>

View file

@ -1,3 +0,0 @@
del html\*.md5
del html\*.map
pause

View file

@ -1,3 +0,0 @@
rm html/*.*
rm html/search/*.*
pause

Binary file not shown.

View file

@ -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>&nbsp;</p>

View file

@ -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.
*/

View file

@ -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();
} }

View file

@ -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");

View file

@ -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);

View file

@ -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()) {

View file

@ -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)) {

View file

@ -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

View file

@ -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

View file

@ -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';

View file

@ -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);

View file

@ -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();
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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) {

View file

@ -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

View file

@ -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__

View file

@ -1,2 +0,0 @@
feather_rp2040
metro_rp2040

View file

@ -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
}

View file

@ -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() {}

View file

@ -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);

View file

@ -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

View file

@ -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"));
}
}

View file

@ -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';

View file

@ -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());

View file

@ -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());

View file

@ -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());

View file

@ -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() {}

View file

@ -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!");

View file

@ -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:

View file

@ -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();
} }
} }

View file

@ -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;

View file

@ -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)) {

View file

@ -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() {}

View file

@ -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

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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();
} }

View file

@ -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

View file

@ -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() {}

View file

@ -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"));

View file

@ -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");
}

View file

@ -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() {}

View file

@ -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;
} }

View file

@ -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();
}

View file

@ -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.

View file

@ -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");
} }

View file

@ -1,2 +0,0 @@
feather_esp32s2
feather_esp32s3

View file

@ -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

View file

@ -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

View file

@ -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