Compare commits

...

57 commits

Author SHA1 Message Date
Tyeth Gundry
daa55e618c
Update library.properties - bump version to 2.3.54 2025-08-05 13:36:14 +01:00
Carter Nelson
8b5835acb1
Merge pull request #37 from caternuson/example_update
Update  Rp2040SdioSetup example for Feather RP2040 Adalogger
2025-08-01 10:47:28 -07:00
caternuson
0aa617af6b update preproc for feather rp2040 adalogger 2025-07-31 08:40:49 -07:00
Ha Thach
a0f096ca6b
Update library.properties 2025-06-09 23:23:52 +07:00
Ha Thach
4d977c909d
Merge pull request #35 from adafruit/pio-sdio-pin-48
fix rp2350B with gpio up to 48
2025-06-09 23:23:35 +07:00
hathach
b5e4bca6ad
update example to work with metro rp2350 and fruitjam 2025-06-09 22:41:03 +07:00
hathach
3bd304897a
fix rp2350B with gpio up to 48 2025-06-09 16:32:44 +07:00
Ha Thach
96a164f548
Merge pull request #34 from adafruit/fix-backward-compatible-cardsize
allow cardSize() for backward compatible with v1
2025-06-09 14:59:31 +07:00
hathach
e542d30095
allow cardSize() for backward compatible with v1 2025-06-06 10:58:23 +07:00
Ha Thach
27e40de668
Update library.properties 2025-06-05 16:25:42 +07:00
Ha Thach
5cff10a770
Merge pull request #33 from adafruit/fix-copy-constructor
fix copy constructor backward compatible for libraries such as Adafruit_WavePlayer
2025-06-05 16:24:42 +07:00
hathach
67bb09a685
FILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC for backward compatible for libraries such as Adafruit_WavePlayer 2025-06-05 11:18:31 +07:00
Liz
54986f09ed Update library.properties 2025-06-03 09:42:18 -04:00
Ha Thach
3c0b262331
Merge pull request #32 from adafruit/update-sdfat-v2.3.0
Update sdfat v2.3.0
2025-05-30 19:48:55 +07:00
hathach
7726ab3b8c
fixing ci: skip test for metro rp2040, esp32s2/s3 2025-05-30 17:11:39 +07:00
hathach
af49d122ad
add metro_rp2040 to ci build 2025-05-30 13:23:44 +07:00
hathach
e4f7c08f04
Merge remote-tracking branch 'greiman/master' into update-sdfat-v2.3.0
# Conflicts:
#	examples/BufferedPrint/BufferedPrint.ino
#	examples/DirectoryFunctions/DirectoryFunctions.ino
#	examples/OpenNext/OpenNext.ino
#	examples/ReadCsvFile/ReadCsvFile.ino
#	examples/RtcTimestampTest/RtcTimestampTest.ino
#	examples/SoftwareSpi/SoftwareSpi.ino
#	examples/UnicodeFilenames/UnicodeFilenames.ino
#	examples/bench/bench.ino
#	examples/rename/rename.ino
#	library.properties
#	src/FatLib/FatPartition.h
#	src/FatLib/FatVolume.h
#	src/SdCard/SdSpiCard.h
#	src/SdFat.h
#	src/SdFatConfig.h
2025-05-30 12:47:33 +07:00
Bill Greiman
a4ecec6055
Update README.md 2025-03-15 04:24:41 -07:00
Ha Thach
1d3b2a2d18
Merge pull request #31 from brentru/fix-for-pico
Fix SDFat conflict with Arduino-Pico BSP
2025-03-04 18:26:39 +07:00
brentru
caf9e91b4e add @hathach review 2025-03-03 09:44:09 -05:00
brentru
d40da246f7 try bump down prop 2025-02-28 16:22:04 -05:00
Brent Rubell
fd3f1dcdc4
Rename SDFat_Adafruit_Fork.h to SdFat_Adafruit_Fork.h 2025-02-28 15:02:28 -05:00
brentru
9d69c08a88 bump semver over 2.3.x 2025-02-28 14:48:11 -05:00
brentru
72230805bf Add new fork file, bump lib prop 2025-02-28 14:42:38 -05:00
brentru
3aff08c143 fix for pico bsp 2025-02-28 10:05:16 -05:00
Bill Greiman
67e26476f1 Add SDIO support for RP2040/RP2350 2025-01-01 07:54:38 -08:00
Bill Greiman
052d38e2c6 Fix bad error code return 2024-06-04 04:50:51 -07:00
Bill Greiman
aadedbf206 Added Move constructor and bug fixes. 2024-05-11 13:07:52 -07:00
Tyeth Gundry
f3bba10a3c
Update library.properties to 2.2.3 2023-12-12 13:01:02 +00:00
Limor "Ladyada" Fried
cc8787f4bb
Merge pull request #20 from adafruit/fix_unicode_errors
🙏
2023-12-11 19:33:19 -05:00
ladyada
ad9dd5ac84 🙏 2023-12-11 19:06:43 -05:00
dherrada
4f43a678f0 Update CI action versions 2023-05-12 11:24:14 -04:00
Bill Greiman
57900b21d2 Reformat, user SPI begin, SPI array functions, bug fixes 2023-04-05 05:34:17 -07:00
Ha Thach
5c0b73ada2
Re-Enable ci for m0/m4/rp2040/s2/s3 (#14)
* try to re-add ci
* enable USE_SPI_ARRAY_TRANSFER and USE_UTF8_LONG_NAMES (if not avr)
* add skip test and slightly update examples for passing ci for all platforms
* add feather s3
* install RTClib for ci
* install SD lib
2022-10-15 20:49:37 +07:00
hathach
d63799cf4e update library for fork 2022-10-14 16:53:41 +07:00
hathach
0b713a014c backward-compatible: support super floppy disk (SFD) by default
- sfd may be used in existing board (with SdFormat), this allow to use
v2 without reformat board
- add backward-compatible define
2022-10-12 15:46:23 +07:00
hathach
21862d20d3 add cardSize() and readBlock/writeBlock() for backward-compatible 2022-10-11 17:51:50 +07:00
hathach
d8351e10ea configure sdfat for spiflash and fat12 usage
- SDFAT_FILE_TYPE = 1
- USE_BLOCK_DEVICE_INTERFACE = 1
- FAT12_SUPPORT = 1
- comment out FS.h warning
- add File32 name() function for backward compilable
2022-10-11 16:18:02 +07:00
hathach
84abec80a8 support filesystem with only 1 fat 2022-10-05 18:11:00 +07:00
Bill Greiman
628effa1c2 New features, major internal changes, bug fixes 2022-07-04 10:24:03 -07:00
Bill Greiman
bab0061a51 Restructure for RTOS use, getName mods, bug fixes 2021-11-24 05:30:36 -08:00
Bill Greiman
fb7415151b Fix SdInfo example and file.timestamp. Remove Syscall::yield and SysCall::halt
Fix SdInfo example and file.timestamp. Remove Syscall::yield and SysCall::halt.

Major restructuring for future portability to RTOS systems and RPI Pico.
2021-10-31 07:11:21 -07:00
Bill Greiman
a5e4bdeb76 Add UTF-8 support 2021-08-23 05:17:02 -07:00
Bill Greiman
caece65d13 Fix Cortex-M0 Hard Fault 2021-06-17 09:33:27 -07:00
Bill Greiman
a9f2b6f886 Fix for SparkFun Mbed Apollo3 Core 2021-03-22 11:15:32 -07:00
Bill Greiman
4582dd4d6c Improved Teensy SDIO, new features, bug fixes 2021-02-25 08:52:43 -08:00
Bill Greiman
cb5a7d2afb Add SdFs::format(), improve "const correctness", zip doc 2020-12-30 07:06:43 -08:00
Bill Greiman
ca42ad3319 Change SD_FAT_VERSION from string to integer 2020-12-26 06:52:23 -08:00
Bill Greiman
f62283abb3 Restructure cache classes to single class 2020-12-26 06:39:40 -08:00
Bill Greiman
1535ac2b03 Timestamp fix, edit src for cplint errors
Add time st
2020-12-05 05:53:23 -08:00
Bill Greiman
e7b775a1fd Update documentation 2020-12-05 05:36:26 -08:00
Bill Greiman
887e0a6730 Fix cache bug for FAT16/FAT32 2020-11-10 06:48:22 -08:00
Bill Greiman
4dbec4b45e Major update to version 2.0.0 2020-11-09 08:34:52 -08:00
Bill Greiman
7d0261d144 Fix open bug 2020-05-16 08:47:17 -07:00
Bill Greiman
d8fe044c5a Add null pointer checks 2020-05-15 09:43:12 -07:00
Bill Greiman
74d464a03b Use fcntl.h for ESP32 2020-02-24 08:33:42 -08:00
Bill Greiman
41d8e42f2c Fix FatFile::printName() bug 2020-01-25 06:58:08 -08:00
897 changed files with 32735 additions and 126471 deletions

92
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View file

@ -0,0 +1,92 @@
name: Bug Report
description: Report a problem
labels: 'Bug'
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
It's okay to leave some blank if it doesn't apply to your problem.
- type: dropdown
attributes:
label: Operating System
options:
- Linux
- MacOS
- RaspberryPi OS
- Windows 7
- Windows 10
- Windows 11
- Others
validations:
required: true
- type: input
attributes:
label: IDE version
placeholder: e.g Arduino 1.8.15
validations:
required: true
- type: input
attributes:
label: Board
placeholder: e.g Feather nRF52840 Express
validations:
required: true
- type: input
attributes:
label: BSP version
description: Can be found under "Board Manager" menu
validations:
required: true
- type: input
attributes:
label: SPIFlash Library version
placeholder: "Release version or github latest"
validations:
required: true
- type: textarea
attributes:
label: Sketch
placeholder: |
e.g examples/flash_info
If it is custom sketch, please provide links to your minimal sources or as attached files.
validations:
required: true
- type: textarea
attributes:
label: What happened ?
placeholder: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: How to reproduce ?
placeholder: |
1. Go to '...'
2. Click on '....'
3. See error
validations:
required: true
- type: textarea
attributes:
label: Debug Log
placeholder: |
Debug log where the issue occurred as attached txt file, best with comments to explain the actual events.
validations:
required: false
- type: textarea
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
validations:
required: false

4
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,4 @@
contact_links:
- name: Adafruit Support Forum
url: https://forums.adafruit.com
about: If you have other questions or need help, post it here.

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: Feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

42
.github/workflows/githubci.yml vendored Normal file
View file

@ -0,0 +1,42 @@
name: Build
on: [pull_request, push, repository_dispatch]
jobs:
build:
strategy:
fail-fast: false
matrix:
arduino-platform:
- 'feather_esp32s2'
- 'feather_esp32s3'
- 'feather_rp2040'
- 'metro_rp2040'
- 'metro_m0'
- 'metro_m4'
- 'nrf52840'
runs-on: ubuntu-latest
steps:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout adafruit/ci-arduino
uses: actions/checkout@v4
with:
repository: adafruit/ci-arduino
path: ci
- name: pre-install
run: bash ci/actions_install.sh
- name: Install Libraries for building examples
run: arduino-cli lib install RTClib SD
- name: test platforms
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}

223
.gitignore vendored
View file

@ -1,174 +1,3 @@
#################
## Eclipse
#################
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#############
## Windows detritus
#############
# Windows image file caches
Thumbs.db
ehthumbs.db
@ -179,37 +8,29 @@ Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# =========================
# Operating System Files
# =========================
# OSX
# =========================
.DS_Store
.AppleDouble
.LSOverride
# Icon must ends with two \r.
Icon
#############
## Python
#############
# Thumbnails
._*
*.py[co]
# Packages
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
# Files that might appear on external disk
.Spotlight-V100
.Trashes

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2011..2017 Bill Greiman
Copyright (c) 2011..2020 Bill Greiman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

179
README.md
View file

@ -1,42 +1,171 @@
The Arduino SdFat library provides read/write access to FAT16/FAT32
file systems on SD/SDHC flash cards.
### Warning: This version has major internal changes.
SdFat requires Arduino 1.6x or greater.
SdFat version 2.3.0 has major changes to implement RP2040/RP2350 SDIO.
Key changes:
In addition there are number of bug fixes.
Support for multiple SPI ports now uses a pointer to a SPIClass object.
Begin by running the Rp2040SdioSetup example to try RP2040/RP2350 SDIO.
See the STM32Test example.
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.
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:
```
explicit SdFat(SPIClass* spiPort);
\\ or
explicit SdFatEX(SPIClass* spiPort);
#define RP_CLK_GPIO 10
#define RP_CMD_GPIO 11
#define RP_DAT0_GPIO 12 // DAT1: GPIO13 DAT2: GPIO14, DAT3: GPIO15.
```
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);
```
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.
This setup gets the following result in the bench example using SDIO.
Open flags for Particle Gen3 and ARM boards are now defined by fcntl.h.
See SdFatConfig.h for options.
<pre>
FILE_SIZE_MB = 5
BUF_SIZE = 512 bytes
Starting write test, please wait.
Support for particle Gen3 devices.
write speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
15014.05,1165,32,32
15289.54,1249,32,32
New cluster allocation algorithm.
Starting read test, please wait.
Please read changes.txt and the html documentation in the extras folder for more information.
read speed and latency
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
define macros. See the html documentation for details
define macros. See the html documentation File tab for details.
Please read the html documentation for this library. Start with
html/index.html and read the Main Page. Next go to the Classes tab and
read the documentation for the classes SdFat, SdFatEX, SdBaseFile,
SdFile, File, StdioStream, ifstream, ofstream, and others.
Please read the html documentation for this library in SdFat/doc/SdFat.html.
Start with the Main Page. Next go to the Classes tab and read the
documentation for the classes SdFat32, SdExFat, SdFs, File32, ExFile, FsFile.
Please continue by reading the html documentation in SdFat/extras/html
The SdFat and File classes are defined in terms of the above classes by
typedefs. Edit SdFatConfig.h to select class options.
Updated 28 Dec 2018
Please continue by reading the html documentation in the SdFat/doc folder.

2896
doc/Doxyfile Normal file

File diff suppressed because it is too large Load diff

51
doc/SdErrorCodes.txt Normal file
View file

@ -0,0 +1,51 @@
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

4
doc/ZipMsg/index.html Normal file
View file

@ -0,0 +1,4 @@
<h3>Replace the content of the html folder by unzipping html.zip.</h3>
<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
<h3>This makes viewing changes on GitHub difficult.</h3>
<p>&nbsp;</p>

3
doc/clean_html.bat Normal file
View file

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

3
doc/del_html.bat Normal file
View file

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

BIN
doc/html.zip Normal file

Binary file not shown.

4
doc/html/index.html Normal file
View file

@ -0,0 +1,4 @@
<h3>Replace the content of the html folder by unzipping html.zip.</h3>
<h3>I have zipped the documentation since Doxygen changes every file each time it runs.</h3>
<h3>This makes viewing changes on GitHub difficult.</h3>
<p>&nbsp;</p>

289
doc/mainpage.h Normal file
View file

@ -0,0 +1,289 @@
/**
* Copyright (c) 2011-2024 Bill Greiman
* This file is part of the SdFat library for SD memory cards.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/**
\mainpage Arduino %SdFat Library
\section Warn Warnings for SdFat V2
This is a major new version of SdFat. It is mostly
backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
You should edit SdFatConfig.h to select features. The default version of
SdFatConfig.h is suitable for UNO and other small AVR boards.
\section Intro Introduction
The Arduino %SdFat library supports FAT16, FAT32, and exFAT file systems
on Standard SD, SDHC, and SDXC cards.
In %SdFat version 1, SdFat and File are the main classes.
In %SdFat version 2, SdFat and File are defined by typedefs in terms of the
following classes.
The file system classes in the %SdFat library are SdFat32, SdExFat, and SdFs.
SdFat32 supports FAT16 and FAT32. SdExFat supports exFAT, SdFs supports
FAT16, FAT32, and exFAT.
The corresponding file classes are File32, ExFile, and FsFile.
The types for SdFat and File are defined in SdFatConfig.h. This version
uses FAT16/FAT32 for small AVR boards and FAT16/FAT32/exFAT for all other
boards.
\code{.cpp}
// File types for SdFat, File, SdFile, SdBaseFile, fstream,
// ifstream, and ofstream.
//
// Set SDFAT_FILE_TYPE to:
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
//
#if defined(__AVR__) && FLASHEND < 0X8000
// FAT16/FAT32 for 32K AVR boards.
#define SDFAT_FILE_TYPE 1
#else // defined(__AVR__) && FLASHEND < 0X8000
// FAT16/FAT32 and exFAT for all other boards.
#define SDFAT_FILE_TYPE 3
#endif // defined(__AVR__) && FLASHEND < 0X8000
\endcode
It is possible to use option three, support or FAT16/FAT32 and exFat
on an Uno or other AVR board with 32KB flash and 2KB SRAM but memory
will be very limited.
Uno memory use for a simple data logger is:
> option 1, FAT16/FAT32, 11902 bytes of flash and 855 bytes of SRAM.
>
> option 2, exFAT, 14942 bytes of flash and 895 bytes of SRAM.
>
> option 3, FAT16/FAT32 and exFAT, 21834 bytes of flash and 908 bytes of SRAM.
Please read documentation under the above classes tab for more information.
A number of example are provided in the %SdFat/examples folder. These were
developed to test %SdFat and illustrate its use.
\section exFAT exFAT Features
exFAT has many features not available in FAT16/FAT32.
Files larger than 4GiB, 64-bit file size and file position.
Free space allocation performance improved by using a free space bitmap.
Removal of the physical "." and ".." directory entries that appear in
FAT16/FAT32 subdirectories.
Better support for large flash pages with boundary alignment offsets
for the FAT table and data region.
exFAT files have two separate 64-bit length fields. The DataLength
field indicate how much space is allocate to the file. The ValidDataLength
field indicates how much actual data has been written to the file.
An exFAT file can be contiguous with pre-allocate clusters and bypass the
use of the FAT table. In this case the contiguous flag is set in the
directory entry. This allows an entire file to be written as one large
multi-block write.
\section SDPath Paths and Working Directories
Relative paths in %SdFat are resolved in a manner similar to Windows.
Each instance of SdFat32, SdExFat, and SdFs has a current directory.
This directory is called the volume working directory, vwd.
Initially this directory is the root directory for the volume.
The volume working directory is changed by calling the chdir(path).
The call sd.chdir("/2014") will change the volume working directory
for sd to "/2014", assuming "/2014" exists.
Relative paths for member functions are resolved by starting at
the volume working directory.
For example, the call sd.mkdir("April") will create the directory
"/2014/April" assuming the volume working directory is "/2014".
There is current working directory, cwd, that is used to resolve paths
for file.open() calls.
For a single SD card, the current working directory is always the volume
working directory for that card.
For multiple SD cards the current working directory is set to the volume
working directory of a card by calling the chvol() member function.
The chvol() call is like the Windows \<drive letter>: command.
The call sd2.chvol() will set the current working directory to the volume
working directory for sd2.
If the volume working directory for sd2 is "/music" the call
file.open("BigBand.wav", O_READ);
will open "/music/BigBand.wav" on sd2.
\section Install Installation
You must manually install %SdFat by renaming the download folder %SdFat
and copy the %SdFat folder to the Arduino libraries folder in your
sketchbook folder.
It will be necessary to unzip and rename the folder if you download a zip
file from GitHub.
See the Manual installation section of this guide.
http://arduino.cc/en/Guide/Libraries
\section SDconfig SdFat Configuration
Several configuration options may be changed by editing the SdFatConfig.h
file in the %SdFat/src folder.
Here are a few of the key options.
If the symbol ENABLE_DEDICATED_SPI is nonzero, multi-block SD I/O may
be used for better performance. The SPI bus may not be shared with
other devices in this mode.
The symbol SPI_DRIVER_SELECT is used to select the SPI driver.
> If the symbol SPI_DRIVER_SELECT is:
>
> 0 - An optimized custom SPI driver is used if it exists
> else the standard library driver is used.
>
> 1 - The standard library driver is always used.
>
> 2 - The software SPI driver is always used.
>
> 3 - An external SPI driver derived from SdSpiBaseClass is always used.
To enable SD card CRC checking in SPI mode set USE_SD_CRC nonzero.
See SdFatConfig.h for other options.
\section Hardware Hardware Configuration
The hardware interface to the SD card should not use a resistor based level
shifter. Resistor based level shifters results in signal rise times that are
too slow for many newer SD cards.
\section HowTo How to format SD Cards
The best way to restore an SD card's format on a PC or Mac is to use
SDFormatter which can be downloaded from:
http://www.sdcard.org/downloads
A formatter program, SdFormatter.ino, is included in the
%SdFat/examples/SdFormatter directory. This program attempts to
emulate SD Association's SDFormatter.
SDFormatter aligns flash erase boundaries with file
system structures which reduces write latency and file system overhead.
The PC/Mac SDFormatter does not have an option for FAT type so it may format
very small cards as FAT12. Use the %SdFormatter example to force FAT16
formatting of small cards.
Do not format the SD card with an OS utility, OS utilities do not format SD
cards in conformance with the SD standard.
You should use a freshly formatted SD card for best performance. FAT
file systems become slower if many files have been created and deleted.
This is because the directory entry for a deleted file is marked as deleted,
but is not deleted. When a new file is created, these entries must be scanned
before creating the file. Also files can become
fragmented which causes reads and writes to be slower.
\section ExampleFiles Examples
A number of examples are provided in the SdFat/examples folder.
To access these examples from the Arduino development environment
go to: %File -> Examples -> %SdFat -> \<program Name\>
Compile, upload to your Arduino and click on Serial Monitor to run
the example.
Here is a list:
AvrAdcLogger - Fast AVR ADC logger using Timer/ADC interrupts.
BackwardCompatibility - Demonstrate SD.h compatibility with %SdFat.h.
bench - A read/write benchmark.
%BufferedPrint - Demo a buffered print class for AVR loggers.
debug folder - Some of my debug programs - will be remove in the future.
DirectoryFunctions - Use of chdir(), ls(), mkdir(), and rmdir().
examplesV1 folder - Examples from SdFat V1 for compatibility tests.
ExFatLogger - A data-logger optimized for exFAT features.
MinimumSizeSdReader - Example of small file reader for FAT16/FAT32.
OpenNext - Open all files in the root dir and print their filename.
QuickStart - Quick hardware test for SPI card access.
ReadCsvFile - Function to read a CSV text file one field at a time.
rename - demonstrates use of rename().
RtcTimestampTest - Demonstration of timestamps with RTClib.
SdErrorCodes - Produce a list of error codes.
SdFormatter - This program will format an SD, SDHC, or SDXC card.
SdInfo - Initialize an SD card and analyze its structure for trouble shooting.
SoftwareSpi - Demo of limited Software SPI support in SdFat V2.
STM32Test - Example use of two SPI ports on an STM32 board.
TeensyDmaAdcLogger - Fast logger using DMA ADC.
TeensyRtcTimestamp - %File timestamps for Teensy3.
TeensySdioDemo - Demo of SDIO and SPI modes for the Teensy 3.5/3.6 built-in SD.
TeensySdioLogger - Fast logger using a ring buffer.
UnicodeFilenames - Test program for Unicode file names.
UserChipSelectFunction - Useful for port expanders or replacement of the standard GPIO functions.
UserSPIDriver - An example of an external SPI driver.
*/

View file

View file

@ -0,0 +1,33 @@
#ifndef AnalogBinLogger_h
#define AnalogBinLogger_h
const size_t BLOCK_SIZE = 64;
//------------------------------------------------------------------------------
// First block of file.
const size_t PIN_NUM_DIM =
BLOCK_SIZE - 3 * sizeof(uint32_t) - 2 * sizeof(uint8_t);
struct metadata_t {
uint32_t adcFrequency; // ADC clock frequency
uint32_t cpuFrequency; // CPU clock frequency
uint32_t sampleInterval; // Sample interval in CPU cycles.
uint8_t recordEightBits; // Size of ADC values, nonzero for 8-bits.
uint8_t pinCount; // Number of analog pins in a sample.
uint8_t pinNumber[PIN_NUM_DIM]; // List of pin numbers in a sample.
};
//------------------------------------------------------------------------------
// Data block for 8-bit ADC mode.
const size_t DATA_DIM8 = (BLOCK_SIZE - 2 * sizeof(uint16_t)) / sizeof(uint8_t);
struct block8_t {
uint16_t count; // count of data values
uint16_t overrun; // count of overruns since last block
uint8_t data[DATA_DIM8];
};
//------------------------------------------------------------------------------
// Data block for 10-bit ADC mode.
const size_t DATA_DIM16 =
(BLOCK_SIZE - 2 * sizeof(uint16_t)) / sizeof(uint16_t);
struct block16_t {
unsigned short count; // count of data values
unsigned short overrun; // count of overruns since last block
unsigned short data[DATA_DIM16];
};
#endif // AnalogBinLogger_h

View file

@ -0,0 +1,905 @@
/**
* 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

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

View file

@ -0,0 +1,84 @@
// A simple read/write example for SD.h.
// Mostly from the SD.h ReadWrite example.
//
// Your SD must be formatted FAT16/FAT32.
//
// SD.h does not support some default SdFat features.
// To compare flash size, set USE_FAT_FILE_FLAG_CONTIGUOUS,
// ENABLE_DEDICATED_SPI, and USE_LONG_FILE_NAMES to zero also
// set SDFAT_FILE_TYPE to one in SdFat/src/SdFatCongfig.h
//
// Set USE_SD_H nonzero to use SD.h.
// Set USE_SD_H zero to use SdFat.h.
//
#define USE_SD_H 0
//
#if USE_SD_H
#include <SD.h>
#else // USE_SD_H
#include "SdFat.h"
SdFat SD;
#endif // USE_SD_H
// Modify SD_CS_PIN for your board.
// For Teensy 3.6 and SdFat.h use BUILTIN_SDCARD.
#define SD_CS_PIN SS
File myFile;
void setup() {
Serial.begin(9600);
while (!Serial) {
}
#if USE_SD_H
Serial.println(F("Using SD.h. Set USE_SD_H zero to use SdFat.h."));
#else // USE_SD_H
Serial.println(F("Using SdFat.h. Set USE_SD_H nonzero to use SD.h."));
#endif // USE_SD_H
Serial.println(F("\nType any character to begin."));
while (!Serial.available()) {
yield();
}
Serial.print("Initializing SD card...");
if (!SD.begin(SD_CS_PIN)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
// open the file.
myFile = SD.open("test.txt", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
Serial.print("Writing to test.txt...");
myFile.println("testing 1, 2, 3.");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
// re-open the file for reading:
myFile = SD.open("test.txt");
if (myFile) {
Serial.println("test.txt:");
// read from the file until there's nothing else in it:
while (myFile.available()) {
Serial.write(myFile.read());
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}
void loop() {
// nothing happens after setup
}

View file

@ -0,0 +1,239 @@
// 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,62 +1,95 @@
/*
* Example use of chdir(), ls(), mkdir(), and rmdir().
*/
#include <SPI.h>
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
#include "SdFat.h"
#include "sdios.h"
// SD card chip select pin.
const uint8_t chipSelect = SS;
// 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
//------------------------------------------------------------------------------
// File system object.
#if SD_FAT_TYPE == 0
SdFat sd;
// Directory file.
SdFile root;
// Use for file creation in folders.
SdFile file;
File file;
File root;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
File32 root;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
ExFile root;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
FsFile root;
#endif // SD_FAT_TYPE
// Create a Serial output stream.
ArduinoOutStream cout(Serial);
// Buffer for Serial input.
char cinBuf[40];
// Create a serial input stream.
ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
//==============================================================================
// Error messages stored in flash.
#define error(msg) sd.errorHalt(F(msg))
//------------------------------------------------------------------------------
// Store error strings in flash to save RAM.
#define error(s) sd.errorHalt(&Serial, F(s))
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
delay(1000);
cout << F("Type any character to start\n");
// Wait for input line and discard.
cin.readline();
cout << endl;
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
while (!Serial.available()) {
yield();
}
if (sd.exists("Folder1")
|| sd.exists("Folder1/file1.txt")
|| sd.exists("Folder1/File2.txt")) {
// Initialize the SD card.
if (!sd.begin(SD_CONFIG)) {
sd.initErrorHalt(&Serial);
}
if (sd.exists("Folder1") || sd.exists("Folder1/file1.txt") ||
sd.exists("Folder1/File2.txt")) {
error("Please remove existing Folder1, file1.txt, and File2.txt");
}
int rootFileCount = 0;
if (!root.open("/")) {
error("open root failed");
error("open root");
}
while (file.openNext(&root, O_RDONLY)) {
if (!file.isHidden()) {
@ -118,7 +151,6 @@ void setup() {
if (!sd.rmdir("Folder1")) {
error("rmdir for Folder1 failed\n");
}
cout << F("\nFolder1 removed.\n");
cout << F("\nList of files on the SD.\n");
sd.ls(LS_R);

View file

@ -0,0 +1,9 @@
// Avoid IDE problems by defining struct in septate .h file.
// Pad record so size is a power of two for best write performance.
#ifndef ExFatLogger_h
#define ExFatLogger_h
const size_t ADC_COUNT = 4;
struct data_t {
uint16_t adc[ADC_COUNT];
};
#endif // ExFatLogger_h

View file

@ -0,0 +1,601 @@
// 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

@ -0,0 +1,60 @@
// Create a text file on the SD with this path using short 8.3 names.
#define SFN_PATH "/DIR/TEST.TXT"
// Modify CS_PIN for your chip select pin.
#define CS_PIN SS
// Set USE_SD_H to one for SD.h or zero for SdFat.
#define USE_SD_H 0
#if USE_SD_H
#include "SD.h"
File file;
#else
#include "SdFat.h"
// Setting ENABLE_DEDICATED_SPI to zero saves over 200 more bytes.
#if ENABLE_DEDICATED_SPI
#warning \
"Set ENABLE_DEDICATED_SPI zero in SdFat/src/SdFatConfig.h for minimum size"
#endif // ENABLE_DEDICATED_SPI
// Insure FAT16/FAT32 only.
SdFat32 SD;
// FatFile does not support Stream functions, just simple read/write.
FatFile file;
#endif
void error(const char* msg) {
Serial.println(msg);
while (true) {
}
}
void setup() {
int n;
char buf[4];
Serial.begin(9600);
while (!Serial) {
}
Serial.println("Type any character to begin");
while (!Serial.available()) {
}
if (!SD.begin(CS_PIN)) error("SD.begin");
#if USE_SD_H
file = SD.open(SFN_PATH);
if (!file) error("open");
#else
// Open existing file with a path of 8.3 names.
// Directories will be opened O_RDONLY files O_RDWR.
if (!file.openExistingSFN(SFN_PATH)) error("open");
#endif
while ((n = file.read(buf, sizeof(buf)))) {
Serial.write(buf, n);
}
// close() is only needed if you write to the file. For example, read
// config data, modify the data, rewind the file and write the data.
// file.close();
}
void loop() {}

View file

@ -1,43 +1,92 @@
/*
* Print size, modify date/time, and name for all files in root.
*/
#include <SPI.h>
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
#include "SdFat.h"
// SD default chip select pin.
const uint8_t chipSelect = SS;
// 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
*/
// file system object
// 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;
SdFile root;
SdFile file;
File dir;
File file;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 dir;
File32 file;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile dir;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile dir;
FsFile file;
#else // SD_FAT_TYPE
#error invalid SD_FAT_TYPE
#endif // SD_FAT_TYPE
//------------------------------------------------------------------------------
// Store error strings in flash to save RAM.
#define error(s) sd.errorHalt(&Serial, F(s))
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
Serial.println("Type any character to start");
while (!Serial.available()) {
SysCall::yield();
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
// Initialize the SD.
if (!sd.begin(SD_CONFIG)) {
sd.initErrorHalt(&Serial);
}
if (!root.open("/")) {
sd.errorHalt("open root failed");
// Open root directory
if (!dir.open("/")) {
error("dir.open failed");
}
// Open next file in root.
// Warning, openNext starts at the current directory position
// so a rewind of the directory may be required.
while (file.openNext(&root, O_RDONLY)) {
// Warning, openNext starts at the current position of dir so a
// rewind may be necessary in your application.
while (file.openNext(&dir, O_RDONLY)) {
file.printFileSize(&Serial);
Serial.write(' ');
file.printModifyDateTime(&Serial);
@ -50,7 +99,7 @@ void setup() {
Serial.println();
file.close();
}
if (root.getError()) {
if (dir.getError()) {
Serial.println("openNext failed");
} else {
Serial.println("Done!");

View file

@ -1,8 +1,13 @@
// Quick hardware test for SPI card access.
//
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
#define SD_FAT_TYPE 3
//
// Set DISABLE_CHIP_SELECT to disable a second SPI device.
// For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
@ -14,9 +19,21 @@ const int8_t DISABLE_CHIP_SELECT = -1;
// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance.
#define SPI_SPEED SD_SCK_MHZ(4)
//------------------------------------------------------------------------------
// File system object.
#if SD_FAT_TYPE == 0
SdFat sd;
File file;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
#else // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif // SD_FAT_TYPE
// Serial streams
ArduinoOutStream cout(Serial);
@ -32,6 +49,15 @@ void cardOrSpeed() {
cout << F("Edit SPI_SPEED in this program to change it.\n");
}
void clearSerialInput() {
uint32_t m = micros();
do {
if (Serial.read() >= 0) {
m = micros();
}
} while (micros() - m < 10000);
}
void reformatMsg() {
cout << F("Try reformatting the card. For best results use\n");
cout << F("the SdFormatter program in SdFat/examples or download\n");
@ -43,13 +69,16 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
cout << F("\nSPI pins:\n");
cout << F("MISO: ") << int(MISO) << endl;
cout << F("MOSI: ") << int(MOSI) << endl;
cout << F("SCK: ") << int(SCK) << endl;
cout << F("SS: ") << int(SS) << endl;
#ifdef SDCARD_SS_PIN
cout << F("SDCARD_SS_PIN: ") << int(SDCARD_SS_PIN) << endl;
#endif // SDCARD_SS_PIN
if (DISABLE_CHIP_SELECT < 0) {
cout << F(
@ -69,9 +98,7 @@ void setup() {
bool firstTry = true;
void loop() {
// Read any existing Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
clearSerialInput();
if (!firstTry) {
cout << F("\nRestarting\n");
@ -80,7 +107,7 @@ void loop() {
cout << F("\nEnter the chip select pin number: ");
while (!Serial.available()) {
SysCall::yield();
yield();
}
cin.readline();
if (cin >> chipSelect) {
@ -114,18 +141,19 @@ void loop() {
cout << dec << noshowbase << endl;
return;
}
cout << F("\nCard successfully initialized.\n");
if (sd.vol()->fatType() == 0) {
cout << F("Can't find a valid FAT16/FAT32 partition.\n");
cout << F("Can't find a valid FAT16/FAT32/exFAT partition.\n");
reformatMsg();
return;
}
cout << F("begin failed, can't determine error type\n");
cout << F("Can't determine error type\n");
return;
}
cout << F("\nCard successfully initialized.\n");
cout << endl;
uint32_t size = sd.card()->cardSize();
uint32_t size = sd.card()->sectorCount();
if (size == 0) {
cout << F("Can't determine the card size.\n");
cardOrSpeed();
@ -135,15 +163,19 @@ void loop() {
cout << F("Card size: ") << sizeMB;
cout << F(" MB (MB = 1,000,000 bytes)\n");
cout << endl;
cout << F("Volume is FAT") << int(sd.vol()->fatType());
cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster();
if (sd.fatType() <= 32) {
cout << F("\nVolume is FAT") << int(sd.fatType());
} else {
cout << F("\nVolume is exFAT");
}
cout << F(", Cluster size (bytes): ") << sd.vol()->bytesPerCluster();
cout << endl << endl;
cout << F("Files found (date time size name):\n");
sd.ls(LS_R | LS_DATE | LS_SIZE);
if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64)
|| (sizeMB < 2200 && sd.vol()->fatType() == 32)) {
if ((sizeMB > 1100 && sd.vol()->sectorsPerCluster() < 64) ||
(sizeMB < 2200 && sd.vol()->fatType() == 32)) {
cout << F("\nThis card should be reformatted for best performance.\n");
cout << F("Use a cluster size of 32 KB for cards larger than 1 GB.\n");
cout << F("Only cards larger than 2 GB should be formatted FAT32.\n");
@ -151,11 +183,10 @@ void loop() {
return;
}
// Read any extra Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
clearSerialInput();
cout << F("\nSuccess! Type any character to restart.\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
}

View file

@ -0,0 +1,163 @@
#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

@ -0,0 +1,97 @@
// 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

@ -0,0 +1,238 @@
// 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

@ -0,0 +1,20 @@
// Print a list of error codes, symbols, and comments.
#include "SdFat.h"
void setup() {
Serial.begin(9600);
while (!Serial) {
}
delay(1000);
Serial.println();
Serial.println(F("Code,Symbol - failed operation"));
for (uint8_t code = 0; code <= SD_CARD_ERROR_UNKNOWN; code++) {
Serial.print(code < 16 ? "0X0" : "0X");
Serial.print(code, HEX);
Serial.print(",");
printSdErrorSymbol(&Serial, code);
Serial.print(" - ");
printSdErrorText(&Serial, code);
Serial.println();
}
}
void loop() {}

View file

@ -1,411 +1,97 @@
/*
* This program will format an SD or SDHC card.
* This program will format SD/SDHC/SDXC cards.
* Warning all data will be deleted!
*
* For SD/SDHC cards larger than 64 MB this
* program attempts to match the format
* This program attempts to match the format
* generated by SDFormatter available here:
*
* http://www.sdcard.org/consumers/formatter/
*
* For smaller cards this program uses FAT16
* and SDFormatter uses FAT12.
* For very small cards this program uses FAT16
* and the above SDFormatter uses FAT12.
*/
// Set USE_SDIO to zero for SPI card access.
#define USE_SDIO 0
//
// Change the value of chipSelect if your hardware does
// not use the default value, SS. Common values are:
// Arduino Ethernet shield: pin 4
// Sparkfun SD shield: pin 8
// Adafruit SD shields and modules: pin 10
const uint8_t chipSelect = SS;
// Initialize at highest supported speed not over 50 MHz.
// Reduce max speed if errors occur.
#define SPI_SPEED SD_SCK_MHZ(50)
// Print extra info for debug if DEBUG_PRINT is nonzero
#define DEBUG_PRINT 0
#include <SPI.h>
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
#include "SdFat.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
ArduinoOutStream cout(Serial);
#if USE_SDIO
// Use faster SdioCardEX
SdioCardEX card;
// SdioCard card;
#else // USE_SDIO
Sd2Card card;
#endif // USE_SDIO
uint32_t cardSizeBlocks;
uint32_t cardCapacityMB;
// cache for SD block
cache_t cache;
// MBR information
uint8_t partType;
uint32_t relSector;
uint32_t partSize;
// Fake disk geometry
uint8_t numberOfHeads;
uint8_t sectorsPerTrack;
// FAT parameters
uint16_t reservedSectors;
uint8_t sectorsPerCluster;
uint32_t fatStart;
uint32_t fatSize;
uint32_t dataStart;
// constants for file system structure
uint16_t const BU16 = 128;
uint16_t const BU32 = 8192;
// strings needed in file system structures
char noName[] = "NO NAME ";
char fat16str[] = "FAT16 ";
char fat32str[] = "FAT32 ";
//------------------------------------------------------------------------------
#define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();}
uint32_t cardSectorCount = 0;
uint8_t sectorBuffer[512];
//------------------------------------------------------------------------------
// SdCardFactory constructs and initializes the appropriate card.
SdCardFactory cardFactory;
// Pointer to generic SD card.
SdCard* m_card = nullptr;
//------------------------------------------------------------------------------
#define sdError(msg) \
{ \
cout << F("error: ") << F(msg) << endl; \
sdErrorHalt(); \
}
//------------------------------------------------------------------------------
void sdErrorHalt() {
if (card.errorCode()) {
cout << F("SD error: ") << hex << int(card.errorCode());
cout << ',' << int(card.errorData()) << dec << endl;
if (!m_card) {
cout << F("Invalid SD_CONFIG") << endl;
} else if (m_card->errorCode()) {
if (m_card->errorCode() == SD_CARD_ERROR_CMD0) {
cout << F("No card, wrong chip select pin, or wiring error?") << endl;
}
SysCall::halt();
}
//------------------------------------------------------------------------------
#if DEBUG_PRINT
void debugPrint() {
cout << F("FreeStack: ") << FreeStack() << endl;
cout << F("partStart: ") << relSector << endl;
cout << F("partSize: ") << partSize << endl;
cout << F("reserved: ") << reservedSectors << endl;
cout << F("fatStart: ") << fatStart << endl;
cout << F("fatSize: ") << fatSize << endl;
cout << F("dataStart: ") << dataStart << endl;
cout << F("clusterCount: ");
cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl;
cout << endl;
cout << F("Heads: ") << int(numberOfHeads) << endl;
cout << F("Sectors: ") << int(sectorsPerTrack) << endl;
cout << F("Cylinders: ");
cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl;
}
#endif // DEBUG_PRINT
//------------------------------------------------------------------------------
// write cached block to the card
uint8_t writeCache(uint32_t lbn) {
return card.writeBlock(lbn, cache.data);
}
//------------------------------------------------------------------------------
// initialize appropriate sizes for SD capacity
void initSizes() {
if (cardCapacityMB <= 6) {
sdError("Card is too small.");
} else if (cardCapacityMB <= 16) {
sectorsPerCluster = 2;
} else if (cardCapacityMB <= 32) {
sectorsPerCluster = 4;
} else if (cardCapacityMB <= 64) {
sectorsPerCluster = 8;
} else if (cardCapacityMB <= 128) {
sectorsPerCluster = 16;
} else if (cardCapacityMB <= 1024) {
sectorsPerCluster = 32;
} else if (cardCapacityMB <= 32768) {
sectorsPerCluster = 64;
} else {
// SDXC cards
sectorsPerCluster = 128;
cout << F("SD errorCode: ") << hex << showbase;
printSdErrorSymbol(&Serial, m_card->errorCode());
cout << F(" = ") << int(m_card->errorCode()) << endl;
cout << F("SD errorData = ") << int(m_card->errorData()) << endl;
}
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;
while (true) {
}
}
//------------------------------------------------------------------------------
// zero cache and optionally set the sector signature
void clearCache(uint8_t addSig) {
memset(&cache, 0, sizeof(cache));
if (addSig) {
cache.mbr.mbrSig0 = BOOTSIG0;
cache.mbr.mbrSig1 = BOOTSIG1;
}
}
//------------------------------------------------------------------------------
// zero FAT and root dir area on SD
void clearFatDir(uint32_t bgn, uint32_t count) {
clearCache(false);
#if USE_SDIO
for (uint32_t i = 0; i < count; i++) {
if (!card.writeBlock(bgn + i, cache.data)) {
sdError("Clear FAT/DIR writeBlock failed");
}
if ((i & 0XFF) == 0) {
cout << '.';
}
}
#else // USE_SDIO
if (!card.writeStart(bgn, count)) {
sdError("Clear FAT/DIR writeStart failed");
}
for (uint32_t i = 0; i < count; i++) {
if ((i & 0XFF) == 0) {
cout << '.';
}
if (!card.writeData(cache.data)) {
sdError("Clear FAT/DIR writeData failed");
}
}
if (!card.writeStop()) {
sdError("Clear FAT/DIR writeStop failed");
}
#endif // USE_SDIO
cout << endl;
}
//------------------------------------------------------------------------------
// return cylinder number for a logical block number
uint16_t lbnToCylinder(uint32_t lbn) {
return lbn / (numberOfHeads * sectorsPerTrack);
}
//------------------------------------------------------------------------------
// return head number for a logical block number
uint8_t lbnToHead(uint32_t lbn) {
return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
}
//------------------------------------------------------------------------------
// return sector number for a logical block number
uint8_t lbnToSector(uint32_t lbn) {
return (lbn % sectorsPerTrack) + 1;
}
//------------------------------------------------------------------------------
// format and write the Master Boot Record
void writeMbr() {
clearCache(true);
part_t* p = cache.mbr.part;
p->boot = 0;
uint16_t c = lbnToCylinder(relSector);
if (c > 1023) {
sdError("MBR CHS");
}
p->beginCylinderHigh = c >> 8;
p->beginCylinderLow = c & 0XFF;
p->beginHead = lbnToHead(relSector);
p->beginSector = lbnToSector(relSector);
p->type = partType;
uint32_t endLbn = relSector + partSize - 1;
c = lbnToCylinder(endLbn);
if (c <= 1023) {
p->endCylinderHigh = c >> 8;
p->endCylinderLow = c & 0XFF;
p->endHead = lbnToHead(endLbn);
p->endSector = lbnToSector(endLbn);
} else {
// Too big flag, c = 1023, h = 254, s = 63
p->endCylinderHigh = 3;
p->endCylinderLow = 255;
p->endHead = 254;
p->endSector = 63;
}
p->firstSector = relSector;
p->totalSectors = partSize;
if (!writeCache(0)) {
sdError("write MBR");
}
}
//------------------------------------------------------------------------------
// generate serial number from card size and micros since boot
uint32_t volSerialNumber() {
return (cardSizeBlocks << 8) + micros();
}
//------------------------------------------------------------------------------
// format the SD as FAT16
void makeFat16() {
uint32_t nc;
for (dataStart = 2 * BU16;; dataStart += BU16) {
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 255)/256;
uint32_t r = BU16 + 1 + 2 * fatSize + 32;
if (dataStart < r) {
continue;
}
relSector = dataStart - r + BU16;
break;
}
// check valid cluster count for FAT16 volume
if (nc < 4085 || nc >= 65525) {
sdError("Bad cluster count");
}
reservedSectors = 1;
fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
if (partSize < 32680) {
partType = 0X01;
} else if (partSize < 65536) {
partType = 0X04;
} else {
partType = 0X06;
}
// write MBR
writeMbr();
clearCache(true);
fat_boot_t* pb = &cache.fbs;
pb->jump[0] = 0XEB;
pb->jump[1] = 0X00;
pb->jump[2] = 0X90;
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
pb->oemId[i] = ' ';
}
pb->bytesPerSector = 512;
pb->sectorsPerCluster = sectorsPerCluster;
pb->reservedSectorCount = reservedSectors;
pb->fatCount = 2;
pb->rootDirEntryCount = 512;
pb->mediaType = 0XF8;
pb->sectorsPerFat16 = fatSize;
pb->sectorsPerTrack = sectorsPerTrack;
pb->headCount = numberOfHeads;
pb->hidddenSectors = relSector;
pb->totalSectors32 = partSize;
pb->driveNumber = 0X80;
pb->bootSignature = EXTENDED_BOOT_SIG;
pb->volumeSerialNumber = volSerialNumber();
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType));
// write partition boot sector
if (!writeCache(relSector)) {
sdError("FAT16 write PBS failed");
}
// clear FAT and root directory
clearFatDir(fatStart, dataStart - fatStart);
clearCache(false);
cache.fat16[0] = 0XFFF8;
cache.fat16[1] = 0XFFFF;
// write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart)
|| !writeCache(fatStart + fatSize)) {
sdError("FAT16 reserve failed");
}
}
//------------------------------------------------------------------------------
// format the SD as FAT32
void makeFat32() {
uint32_t nc;
relSector = BU32;
for (dataStart = 2 * BU32;; dataStart += BU32) {
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 127)/128;
uint32_t r = relSector + 9 + 2 * fatSize;
if (dataStart >= r) {
break;
}
}
// error if too few clusters in FAT32 volume
if (nc < 65525) {
sdError("Bad cluster count");
}
reservedSectors = dataStart - relSector - 2 * fatSize;
fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + dataStart - relSector;
// type depends on address of end sector
// max CHS has lbn = 16450560 = 1024*255*63
if ((relSector + partSize) <= 16450560) {
// FAT32
partType = 0X0B;
} else {
// FAT32 with INT 13
partType = 0X0C;
}
writeMbr();
clearCache(true);
fat32_boot_t* pb = &cache.fbs32;
pb->jump[0] = 0XEB;
pb->jump[1] = 0X00;
pb->jump[2] = 0X90;
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
pb->oemId[i] = ' ';
}
pb->bytesPerSector = 512;
pb->sectorsPerCluster = sectorsPerCluster;
pb->reservedSectorCount = reservedSectors;
pb->fatCount = 2;
pb->mediaType = 0XF8;
pb->sectorsPerTrack = sectorsPerTrack;
pb->headCount = numberOfHeads;
pb->hidddenSectors = relSector;
pb->totalSectors32 = partSize;
pb->sectorsPerFat32 = fatSize;
pb->fat32RootCluster = 2;
pb->fat32FSInfo = 1;
pb->fat32BackBootBlock = 6;
pb->driveNumber = 0X80;
pb->bootSignature = EXTENDED_BOOT_SIG;
pb->volumeSerialNumber = volSerialNumber();
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
// write partition boot sector and backup
if (!writeCache(relSector)
|| !writeCache(relSector + 6)) {
sdError("FAT32 write PBS failed");
}
clearCache(true);
// write extra boot area and backup
if (!writeCache(relSector + 2)
|| !writeCache(relSector + 8)) {
sdError("FAT32 PBS ext failed");
}
fat32_fsinfo_t* pf = &cache.fsinfo;
pf->leadSignature = FSINFO_LEAD_SIG;
pf->structSignature = FSINFO_STRUCT_SIG;
pf->freeCount = 0XFFFFFFFF;
pf->nextFree = 0XFFFFFFFF;
// write FSINFO sector and backup
if (!writeCache(relSector + 1)
|| !writeCache(relSector + 7)) {
sdError("FAT32 FSINFO failed");
}
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster);
clearCache(false);
cache.fat32[0] = 0x0FFFFFF8;
cache.fat32[1] = 0x0FFFFFFF;
cache.fat32[2] = 0x0FFFFFFF;
// write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart)
|| !writeCache(fatStart + fatSize)) {
sdError("FAT32 reserve failed");
void clearSerialInput() {
uint32_t m = micros();
do {
if (Serial.read() >= 0) {
m = micros();
}
} while (micros() - m < 10000);
}
//------------------------------------------------------------------------------
// flash erase all data
@ -418,44 +104,62 @@ void eraseCard() {
do {
lastBlock = firstBlock + ERASE_SIZE - 1;
if (lastBlock >= cardSizeBlocks) {
lastBlock = cardSizeBlocks - 1;
if (lastBlock >= cardSectorCount) {
lastBlock = cardSectorCount - 1;
}
if (!card.erase(firstBlock, lastBlock)) {
if (!m_card->erase(firstBlock, lastBlock)) {
sdError("erase failed");
}
cout << '.';
if ((n++)%32 == 31) {
if ((n++) % 64 == 63) {
cout << endl;
}
firstBlock += ERASE_SIZE;
} while (firstBlock < cardSizeBlocks);
} while (firstBlock < cardSectorCount);
cout << endl;
if (!card.readBlock(0, cache.data)) {
if (!m_card->readSector(0, sectorBuffer)) {
sdError("readBlock");
}
cout << hex << showbase << setfill('0') << internal;
cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl;
cout << F("All data set to ") << setw(4) << int(sectorBuffer[0]) << endl;
cout << dec << noshowbase << setfill(' ') << right;
cout << F("Erase done\n");
}
//------------------------------------------------------------------------------
void formatCard() {
cout << endl;
cout << F("Formatting\n");
initSizes();
if (card.type() != SD_CARD_TYPE_SDHC) {
cout << F("FAT16\n");
makeFat16();
} else {
cout << F("FAT32\n");
makeFat32();
ExFatFormatter exFatFormatter;
FatFormatter fatFormatter;
// Format exFAT if larger than 32GB.
bool rtn = cardSectorCount > 67108864
? exFatFormatter.format(m_card, sectorBuffer, &Serial)
: fatFormatter.format(m_card, sectorBuffer, &Serial);
if (!rtn) {
sdErrorHalt();
}
#if DEBUG_PRINT
debugPrint();
#endif // DEBUG_PRINT
cout << F("Format done\n");
cout << F("Run the SdInfo example for format details.") << endl;
}
//------------------------------------------------------------------------------
void printConfig(SdSpiConfig config) {
if (DISABLE_CS_PIN < 0) {
cout << F(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CS_PIN to disable an SPI device.\n");
} else {
cout << F("\nDisabling SPI device on pin ");
cout << int(DISABLE_CS_PIN) << endl;
pinMode(DISABLE_CS_PIN, OUTPUT);
digitalWrite(DISABLE_CS_PIN, HIGH);
}
cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
}
//------------------------------------------------------------------------------
void printConfig(SdioConfig config) {
(void)config;
cout << F("Assuming an SDIO interface.\n");
}
//------------------------------------------------------------------------------
void setup() {
@ -463,33 +167,33 @@ void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
cout << F("Type any character to start\n");
printConfig(SD_CONFIG);
cout << F("\nType any character to start\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
// Discard any extra characters.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
clearSerialInput();
cout << F(
"\n"
"This program can erase and/or format SD/SDHC cards.\n"
"This program can erase and/or format SD/SDHC/SDXC cards.\n"
"\n"
"Erase uses the card's fast flash erase command.\n"
"Flash erase sets all data to 0X00 for most cards\n"
"and 0XFF for a few vendor's cards.\n"
"\n"
"Cards larger than 2 GB will be formatted FAT32 and\n"
"smaller cards will be formatted FAT16.\n"
"Cards up to 2 GiB (GiB = 2^30 bytes) will be formated FAT16.\n"
"Cards larger than 2 GiB and up to 32 GiB will be formatted\n"
"FAT32. Cards larger than 32 GiB will be formatted exFAT.\n"
"\n"
"Warning, all data on the card will be erased.\n"
"Enter 'Y' to continue: ");
while (!Serial.available()) {
SysCall::yield();
yield();
}
c = Serial.read();
cout << c << endl;
if (c != 'Y') {
@ -497,10 +201,34 @@ void setup() {
return;
}
// Read any existing Serial data.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
clearSerialInput();
// Select and initialize proper card driver.
m_card = cardFactory.newCard(SD_CONFIG);
if (!m_card || m_card->errorCode()) {
sdError("card init failed.");
return;
}
cardSectorCount = m_card->sectorCount();
if (!cardSectorCount) {
sdError("Get sector count failed.");
return;
}
cout << F("\nCard size: ") << cardSectorCount * 5.12e-7;
cout << F(" GB (GB = 1E9 bytes)\n");
cout << F("Card size: ") << cardSectorCount / 2097152.0;
cout << F(" GiB (GiB = 2^30 bytes)\n");
cout << F("Card will be formated ");
if (cardSectorCount > 67108864) {
cout << F("exFAT\n");
} else if (cardSectorCount > 4194304) {
cout << F("FAT32\n");
} else {
cout << F("FAT16\n");
}
cout << F(
"\n"
"Options are:\n"
@ -511,7 +239,7 @@ void setup() {
"Enter option: ");
while (!Serial.available()) {
SysCall::yield();
yield();
}
c = Serial.read();
cout << c << endl;
@ -519,28 +247,6 @@ void setup() {
cout << F("Quiting, invalid option entered.") << endl;
return;
}
#if USE_SDIO
if (!card.begin()) {
sdError("card.begin failed");
}
#else // USE_SDIO
if (!card.begin(chipSelect, SPI_SPEED)) {
cout << F(
"\nSD initialization failure!\n"
"Is the SD card inserted correctly?\n"
"Is chip select correct at the top of this program?\n");
sdError("card.begin failed");
}
#endif
cardSizeBlocks = card.cardSize();
if (cardSizeBlocks == 0) {
sdError("cardSize");
}
cardCapacityMB = (cardSizeBlocks + 2047)/2048;
cout << F("Card Size: ") << setprecision(0) << 1.048576*cardCapacityMB;
cout << F(" MB, (MB = 1,000,000 bytes)") << endl;
if (c == 'E' || c == 'F') {
eraseCard();
}
@ -548,5 +254,4 @@ void setup() {
formatCard();
}
}
//------------------------------------------------------------------------------
void loop() {}

View file

@ -1,210 +1,180 @@
/*
* This program attempts to initialize an SD card and analyze its structure.
* The CID and CSD registers are also printed in HEX for use in online
* decoders like these.
*
* https://gurumeditation.org/1342/sd-memory-card-register-decoder/
* https://archive.goughlui.com/static/multicid.htm
*/
#include <SPI.h>
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
#include "SdFat.h"
#include "sdios.h"
// Set USE_SDIO to zero for SPI card access.
#define USE_SDIO 0
/*
* SD chip select pin. Common values are:
*
* Arduino Ethernet shield, pin 4.
* SparkFun SD shield, pin 8.
* Adafruit SD shields and modules, pin 10.
* Default SD chip select is the SPI SS pin.
*/
const uint8_t SD_CHIP_SELECT = SS;
Set DISABLE_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;
/*
* 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;
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
#if USE_SDIO
// Use faster SdioCardEX
SdFatSdioEX sd;
// SdFatSdio sd;
#else // USE_SDIO
SdFat sd;
#endif // USE_SDIO
// 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
// serial output steam
ArduinoOutStream cout(Serial);
// global for card size
uint32_t cardSize;
// global for card erase size
//------------------------------------------------------------------------------
SdFs sd;
cid_t cid;
csd_t csd;
scr_t scr;
uint8_t cmd6Data[64];
uint32_t eraseSize;
uint32_t ocr;
static ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// 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;
}
void cidDmp() {
cout << F("\nManufacturer ID: ");
cout << hex << int(cid.mid) << dec << endl;
cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
cout << F("Product: ");
for (uint8_t i = 0; i < 5; i++) {
cout << cid.pnm[i];
}
cout << F("\nVersion: ");
cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
cout << F("Serial number: ") << hex << cid.psn << dec << endl;
cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
cout << F("Manufacturing date: ");
cout << int(cid.mdt_month) << '/';
cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
cout << endl;
return true;
cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
cout << F("CID HEX: ");
hexDmp(&cid, sizeof(cid));
}
//------------------------------------------------------------------------------
uint8_t csdDmp() {
csd_t csd;
uint8_t eraseSingleBlock;
if (!sd.card()->readCSD(&csd)) {
sdErrorMsg("readCSD failed");
return false;
void clearSerialInput() {
uint32_t m = micros();
do {
if (Serial.read() >= 0) {
m = micros();
}
if (csd.v1.csd_ver == 0) {
eraseSingleBlock = csd.v1.erase_blk_en;
eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
} else if (csd.v2.csd_ver == 1) {
eraseSingleBlock = csd.v2.erase_blk_en;
eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low;
} else {
cout << F("csd version error\n");
return false;
}
eraseSize++;
cout << F("cardSize: ") << 0.000512*cardSize;
} while (micros() - m < 10000);
}
//------------------------------------------------------------------------------
void csdDmp() {
eraseSize = csd.eraseSize();
cout << F("\ncardSize: ") << 0.000512 * csd.capacity();
cout << F(" MB (MB = 1,000,000 bytes)\n");
cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
cout << F("eraseSingleBlock: ");
if (eraseSingleBlock) {
if (csd.eraseSingleBlock()) {
cout << F("true\n");
} else {
cout << F("false\n");
}
return true;
cout << F("dataAfterErase: ");
if (scr.dataAfterErase()) {
cout << F("ones\n");
} else {
cout << F("zeros\n");
}
cout << F("CSD HEX: ");
hexDmp(&csd, sizeof(csd));
}
//------------------------------------------------------------------------------
// print partition table
uint8_t partDmp() {
mbr_t mbr;
if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) {
sdErrorMsg("read MBR failed");
void errorPrint() {
if (sd.sdErrorCode()) {
cout << F("SD errorCode: ") << hex << showbase;
printSdErrorSymbol(&Serial, sd.sdErrorCode());
cout << F(" = ") << int(sd.sdErrorCode()) << endl;
cout << F("SD errorData = ") << int(sd.sdErrorData()) << dec << endl;
}
}
//------------------------------------------------------------------------------
void hexDmp(void* reg, uint8_t size) {
uint8_t* u8 = reinterpret_cast<uint8_t*>(reg);
cout << hex << noshowbase;
for (size_t i = 0; i < size; i++) {
cout << setw(2) << setfill('0') << int(u8[i]);
}
cout << dec << endl;
}
//------------------------------------------------------------------------------
bool mbrDmp() {
MbrSector_t mbr;
bool valid = true;
if (!sd.card()->readSector(0, (uint8_t*)&mbr)) {
cout << F("\nread MBR failed.\n");
errorPrint();
return false;
}
for (uint8_t ip = 1; ip < 5; ip++) {
part_t *pt = &mbr.part[ip - 1];
if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) {
cout << F("\nNo MBR. Assuming Super Floppy format.\n");
return true;
}
}
cout << F("\nSD Partition Table\n");
cout << F("part,boot,type,start,length\n");
cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n");
for (uint8_t ip = 1; ip < 5; ip++) {
part_t *pt = &mbr.part[ip - 1];
cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl;
MbrPart_t* pt = &mbr.part[ip - 1];
if ((pt->boot != 0 && pt->boot != 0X80) ||
getLe32(pt->relativeSectors) > csd.capacity()) {
valid = false;
}
cout << int(ip) << ',' << uppercase << showbase << hex;
cout << int(pt->boot) << ',';
for (int i = 0; i < 3; i++) {
cout << int(pt->beginCHS[i]) << ',';
}
cout << int(pt->type) << ',';
for (int i = 0; i < 3; i++) {
cout << int(pt->endCHS[i]) << ',';
}
cout << dec << getLe32(pt->relativeSectors) << ',';
cout << getLe32(pt->totalSectors) << endl;
}
if (!valid) {
cout << F("\nMBR not valid, assuming Super Floppy format.\n");
}
return true;
}
//------------------------------------------------------------------------------
void volDmp() {
cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl;
cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl;
cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl;
cout << F("freeClusters: ");
uint32_t volFree = sd.vol()->freeClusterCount();
cout << volFree << endl;
float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n");
cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl;
cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl;
cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl;
cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl;
cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl;
if (sd.vol()->dataStartBlock() % eraseSize) {
cout << F("Data area is not aligned on flash erase boundaries!\n");
cout << F("Download and use formatter from www.sdcard.org!\n");
}
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
}
// use uppercase in hex and use 0X base prefix
cout << uppercase << showbase << endl;
// F stores strings in flash to save RAM
cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
#if !USE_SDIO
if (DISABLE_CHIP_SELECT < 0) {
cout << F(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CHIP_SELECT to disable another device.\n");
void dmpVol() {
cout << F("\nScanning FAT, please wait.\n");
int32_t freeClusterCount = sd.freeClusterCount();
if (sd.fatType() <= 32) {
cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
} else {
cout << F("\nDisabling SPI device on pin ");
cout << int(DISABLE_CHIP_SELECT) << endl;
pinMode(DISABLE_CHIP_SELECT, OUTPUT);
digitalWrite(DISABLE_CHIP_SELECT, HIGH);
cout << F("\nVolume is exFAT\n");
}
cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
cout << F("fatStartSector: ") << sd.fatStartSector() << endl;
cout << F("dataStartSector: ") << sd.dataStartSector() << endl;
cout << F("clusterCount: ") << sd.clusterCount() << endl;
cout << F("freeClusterCount: ");
if (freeClusterCount >= 0) {
cout << freeClusterCount << endl;
} else {
cout << F("failed\n");
errorPrint();
}
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;
void printCardType() {
cout << F("\nCard type: ");
switch (sd.card()->type()) {
case SD_CARD_TYPE_SD1:
cout << F("SD1\n");
@ -215,7 +185,7 @@ void loop() {
break;
case SD_CARD_TYPE_SDHC:
if (cardSize < 70000000) {
if (csd.capacity() < 70000000) {
cout << F("SDHC\n");
} else {
cout << F("SDXC\n");
@ -225,24 +195,91 @@ void loop() {
default:
cout << F("Unknown\n");
}
if (!cidDmp()) {
return;
}
if (!csdDmp()) {
return;
}
uint32_t ocr;
if (!sd.card()->readOCR(&ocr)) {
sdErrorMsg("\nreadOCR failed");
return;
}
cout << F("OCR: ") << hex << ocr << dec << endl;
if (!partDmp()) {
return;
}
if (!sd.fsBegin()) {
sdErrorMsg("\nFile System initialization failed.\n");
return;
}
volDmp();
}
//------------------------------------------------------------------------------
void printConfig(SdSpiConfig config) {
if (DISABLE_CS_PIN < 0) {
cout << F(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CS_PIN to disable an SPI device.\n");
} else {
cout << F("\nDisabling SPI device on pin ");
cout << int(DISABLE_CS_PIN) << endl;
pinMode(DISABLE_CS_PIN, OUTPUT);
digitalWrite(DISABLE_CS_PIN, HIGH);
}
cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
}
//------------------------------------------------------------------------------
void printConfig(SdioConfig config) {
(void)config;
cout << F("Assuming an SDIO interface.\n");
}
//-----------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
cout << F("SdFat version: ") << SD_FAT_VERSION_STR << endl;
printConfig(SD_CONFIG);
}
//------------------------------------------------------------------------------
void loop() {
// Read any existing Serial data.
clearSerialInput();
// F stores strings in flash to save RAM
cout << F("\ntype any character to start\n");
while (!Serial.available()) {
yield();
}
uint32_t t = millis();
if (!sd.cardBegin(SD_CONFIG)) {
cout << F(
"\nSD initialization failed.\n"
"Do not reformat the card!\n"
"Is the card correctly inserted?\n"
"Is there a wiring/soldering problem?\n");
if (isSpi(SD_CONFIG)) {
cout << F(
"Is SD_CS_PIN set to the correct value?\n"
"Does another SPI device need to be disabled?\n");
}
errorPrint();
return;
}
t = millis() - t;
cout << F("init time: ") << dec << t << " ms" << endl;
if (!sd.card()->readCID(&cid) || !sd.card()->readCSD(&csd) ||
!sd.card()->readOCR(&ocr) || !sd.card()->readSCR(&scr)) {
cout << F("readInfo failed\n");
errorPrint();
return;
}
printCardType();
cout << F("sdSpecVer: ") << 0.01 * scr.sdSpecVer() << endl;
cout << F("HighSpeedMode: ");
if (scr.sdSpecVer() > 101 && sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) &&
(2 & cmd6Data[13])) {
cout << F("true\n");
} else {
cout << F("false\n");
}
cidDmp();
csdDmp();
cout << F("\nOCR: ") << uppercase << showbase;
cout << hex << ocr << dec << endl;
if (!mbrDmp()) {
return;
}
if (!sd.volumeBegin()) {
cout << F("\nvolumeBegin failed. Is the card formatted?\n");
errorPrint();
return;
}
dmpVol();
}

View file

View file

@ -1,38 +1,60 @@
// An example of the SdFatSoftSpi template class.
// This example is for an Adafruit Data Logging Shield on a Mega.
// An example of the SoftSpiDriver template class.
// This example is for an old Adafruit Data Logging Shield on a Mega.
// Software SPI is required on Mega since this shield connects to pins 10-13.
// This example will also run on an Uno and other boards using software SPI.
//
#include <SPI.h>
#include "SdFat.h"
#if ENABLE_SOFTWARE_SPI_CLASS // Must be set in SdFat/SdFatConfig.h
#if SPI_DRIVER_SELECT == 2 // Must be set in SdFat/SdFatConfig.h
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
#define SD_FAT_TYPE 3
//
// Chip select may be constant or RAM variable.
const uint8_t SD_CS_PIN = 10;
//
// Pin numbers in templates must be constants.
const uint8_t SOFT_MISO_PIN = 12;
const uint8_t SOFT_MOSI_PIN = 11;
const uint8_t SOFT_SCK_PIN = 13;
//
// Chip select may be constant or RAM variable.
const uint8_t SD_CHIP_SELECT_PIN = 10;
// SdFat software SPI template
SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;
SoftSpiDriver<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> softSpi;
// Speed argument is ignored for software SPI.
#if ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
#else // ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(0), &softSpi)
#endif // ENABLE_DEDICATED_SPI
// Test file.
SdFile file;
#if SD_FAT_TYPE == 0
SdFat sd;
File file;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
#else // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif // SD_FAT_TYPE
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
Serial.println("Type any character to start");
while (!Serial.available()) {
SysCall::yield();
yield();
}
if (!sd.begin(SD_CHIP_SELECT_PIN)) {
if (!sd.begin(SD_CONFIG)) {
sd.initErrorHalt();
}
@ -53,6 +75,6 @@ void setup() {
}
//------------------------------------------------------------------------------
void loop() {}
#else // ENABLE_SOFTWARE_SPI_CLASS
#error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h
#endif //ENABLE_SOFTWARE_SPI_CLASS
#else // SPI_DRIVER_SELECT
#error SPI_DRIVER_SELECT must be two in SdFat/SdFatConfig.h
#endif // SPI_DRIVER_SELECT

View file

@ -0,0 +1,66 @@
// This is a simple SPI loop-back test.
//
// Connect SD_MISO to SD_MOSI
//
// Modify these defines for your configuration.
#define SD_SPI SPI
#define SD_MISO MISO
#define SD_MOSI MOSI
#include "SPI.h"
void setup() {
uint8_t rx, tx;
Serial.begin(9600);
while (!Serial) {
yield();
}
Serial.println(F("\nType any character to start"));
while (!Serial.available()) {
yield();
}
Serial.print("Begin, SD_MISO: ");
Serial.print(SD_MISO), Serial.print(", SD_MOSI: ");
Serial.println(SD_MOSI);
pinMode(SD_MISO, INPUT_PULLUP);
pinMode(SD_MOSI, OUTPUT);
digitalWrite(SD_MOSI, HIGH);
if (!digitalRead(SD_MISO)) {
Serial.println("Error: SD_MISO not HIGH");
goto fail;
}
digitalWrite(SD_MOSI, LOW);
if (digitalRead(SD_MISO)) {
Serial.println("Error: SD_MISO not LOW");
goto fail;
}
pinMode(SD_MISO, INPUT);
pinMode(SD_MOSI, INPUT);
// Modify if SD_SPI.begin has arguments and use this style SdFat begin call:
// sd.begin(SdSpiConfig(CS_PIN, USER_SPI_BEGIN | <other options>, &SD_SPI));
SD_SPI.begin();
// Start with a 400 kHz clock. Try full speed if success for 400 kHz.
SD_SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0));
tx = 0;
do {
rx = SD_SPI.transfer(tx);
if (tx != rx) {
Serial.print("Error rx: 0x");
Serial.print(rx, HEX);
Serial.print(" != tx: 0x");
Serial.println(tx, HEX);
SD_SPI.endTransaction();
goto fail;
}
} while (tx++ < 255);
SD_SPI.endTransaction();
Serial.println("Success!");
return;
fail:
SD_SPI.endTransaction();
Serial.println("Is SD_MISO connected to SD_MOSI?");
Serial.println("Are SD_MISO and SD_MOSI correct?");
}
void loop() {}

View file

@ -0,0 +1,214 @@
// Test of Teensy exFAT DMA ADC logger.
// This is mainly to test use of RingBuf in an ISR.
// This example only supports pins on the first ADC.
// it has only been tested on Teensy 3.6 and 4.1.
// You should modify it for serious use as a data logger.
//
#include "ADC.h"
#include "DMAChannel.h"
#include "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

@ -0,0 +1,135 @@
// Test of time-stamp callback with Teensy 3/4.
// The upload time will be used to set the RTC.
// You must arrange for syncing the RTC.
#include <TimeLib.h>
#include "SdFat.h"
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
#define SD_FAT_TYPE 3
/*
Change the value of SD_CS_PIN if you are using SPI and
your hardware does not use the default value, SS.
Common values are:
Arduino Ethernet shield: pin 4
Sparkfun SD shield: pin 8
Adafruit SD shields and modules: pin 10
*/
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else // SDCARD_SS_PIN
// Assume built-in SD is used.
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif // SDCARD_SS_PIN
// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
#define SPI_CLOCK SD_SCK_MHZ(50)
// Try to select the best SD card configuration.
#if 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

View file

@ -1,23 +1,42 @@
// Simple performance test for Teensy 3.5/3.6 SDHC.
// Demonstrates yield() efficiency.
// Warning SdFatSdio and SdFatSdioEX normally should
// not both be used in a program.
// Each has its own cache and member variables.
// Simple performance test for Teensy 3.5/3.6 4.0 SDHC.
// Demonstrates yield() efficiency for SDIO modes.
#include "SdFat.h"
// Use built-in SD for SPI modes on Teensy 3.5/3.6.
// Teensy 4.0 use first SPI port.
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else // SDCARD_SS_PIN
// Assume built-in SD is used.
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif // SDCARD_SS_PIN
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
#define SD_FAT_TYPE 3
// 32 KiB buffer.
const size_t BUF_DIM = 32768;
// 8 MiB file.
const uint32_t FILE_SIZE = 256UL*BUF_DIM;
SdFatSdio sd;
SdFatSdioEX sdEx;
const uint32_t FILE_SIZE = 256UL * BUF_DIM;
#if SD_FAT_TYPE == 0
SdFat sd;
File file;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
#else // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif // SD_FAT_TYPE
uint8_t buf[BUF_DIM];
@ -32,24 +51,33 @@ uint32_t yieldMicros = 0;
uint32_t yieldCalls = 0;
// Max busy time for single yield call.
uint32_t yieldMaxUsec = 0;
// Control access to the two versions of SdFat.
bool useEx = false;
//-----------------------------------------------------------------------------
bool sdBusy() {
return useEx ? sdEx.card()->isBusy() : sd.card()->isBusy();
}
//-----------------------------------------------------------------------------
void errorHalt(const char* msg) {
if (useEx) {
sdEx.errorHalt(msg);
} else {
sd.errorHalt(msg);
//------------------------------------------------------------------------------
void clearSerialInput() {
uint32_t m = micros();
do {
if (Serial.read() >= 0) {
m = micros();
}
} while (micros() - m < 10000);
}
//------------------------------------------------------------------------------
uint32_t kHzSdClk() {
return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk();
void errorHalt(const char* msg) {
Serial.print("Error: ");
Serial.println(msg);
if (sd.sdErrorCode()) {
if (sd.sdErrorCode() == SD_CARD_ERROR_ACMD41) {
Serial.println("Try power cycling the SD card.");
}
printSdErrorSymbol(&Serial, sd.sdErrorCode());
Serial.print(", ErrorData: 0X");
Serial.println(sd.sdErrorData(), HEX);
}
while (true) {
}
}
bool ready = false;
//------------------------------------------------------------------------------
bool sdBusy() { return ready ? sd.card()->isBusy() : false; }
//------------------------------------------------------------------------------
// Replace "weak" system yield() function.
void yield() {
@ -68,7 +96,7 @@ void yield() {
}
yieldMicros += m;
}
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
void runTest() {
// Zero Stats
totalMicros = 0;
@ -81,22 +109,25 @@ void runTest() {
Serial.println("\nsize,write,read");
Serial.println("bytes,KB/sec,KB/sec");
for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) {
file.truncate(0);
uint32_t nRdWr = FILE_SIZE/nb;
uint32_t nRdWr = FILE_SIZE / nb;
if (!file.truncate(0)) {
errorHalt("truncate failed");
}
Serial.print(nb);
Serial.print(',');
uint32_t t = micros();
for (uint32_t n = 0; n < nRdWr; n++) {
// Set start and end of buffer.
buf32[0] = n;
buf32[nb/4 - 1] = n;
buf32[nb / 4 - 1] = n;
if (nb != file.write(buf, nb)) {
errorHalt("write failed");
}
}
t = micros() - t;
totalMicros += t;
Serial.print(1000.0*FILE_SIZE/t);
Serial.print(1000.0 * FILE_SIZE / t);
Serial.print(',');
file.rewind();
t = micros();
@ -106,13 +137,13 @@ void runTest() {
errorHalt("read failed");
}
// 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");
}
}
t = micros() - t;
totalMicros += t;
Serial.println(1000.0*FILE_SIZE/t);
Serial.println(1000.0 * FILE_SIZE / t);
}
file.close();
Serial.print("\ntotalMicros ");
@ -123,47 +154,67 @@ void runTest() {
Serial.println(yieldCalls);
Serial.print("yieldMaxUsec ");
Serial.println(yieldMaxUsec);
Serial.print("kHzSdClk ");
Serial.println(kHzSdClk());
// Serial.print("kHzSdClk ");
// Serial.println(kHzSdClk());
Serial.println("Done");
}
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
while (!Serial) {
}
Serial.println("SdFatSdioEX uses extended multi-block transfers without DMA.");
Serial.println("SdFatSdio uses a traditional DMA SDIO implementation.");
Serial.println("Note the difference is speed and busy yield time.\n");
}
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
void loop() {
do {
delay(10);
} while (Serial.available() && Serial.read());
static bool warn = true;
if (warn) {
warn = false;
Serial.println(
"SD cards must be power cycled to leave\n"
"SPI mode so do SDIO tests first.\n"
"\nCycle power on the card if an error occurs.");
}
clearSerialInput();
Serial.println("Type '1' for SdFatSdioEX or '2' for SdFatSdio");
Serial.println(
"\nType '1' for FIFO SDIO"
"\n '2' for DMA SDIO"
"\n '3' for Dedicated SPI"
"\n '4' for Shared SPI");
while (!Serial.available()) {
}
char c = Serial.read();
if (c != '1' && 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");
return;
}
if (c =='1') {
useEx = true;
if (!sdEx.begin()) {
sd.initErrorHalt("SdFatSdioEX begin() failed");
}
// make sdEx the current volume.
sdEx.chvol();
} else {
useEx = false;
if (!sd.begin()) {
sd.initErrorHalt("SdFatSdio begin() failed");
}
// make sd the current volume.
sd.chvol();
}
ready = true;
runTest();
ready = false;
}

View file

@ -0,0 +1,150 @@
// 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

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

View file

@ -0,0 +1,105 @@
// 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

@ -0,0 +1,47 @@
// An example of an external chip select functions.
// Useful for port expanders or replacement of the standard GPIO functions.
//
#include "SdFat.h"
// SD_CHIP_SELECT_MODE must be set to one or two in SdFat/SdFatConfig.h.
// A value of one allows optional replacement and two requires replacement.
#if SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
// SD chip select pin.
#define SD_CS_PIN SS
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50))
SdFat sd;
// Stats to verify function calls.
uint32_t initCalls = 0;
uint32_t writeCalls = 0;
//------------------------------------------------------------------------------
// Modify these functions for your port expander or custom GPIO library.
void sdCsInit(SdCsPin_t pin) {
initCalls++;
pinMode(pin, OUTPUT);
}
void sdCsWrite(SdCsPin_t pin, bool level) {
writeCalls++;
digitalWrite(pin, level);
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
if (!sd.begin(SD_CONFIG)) {
sd.initErrorHalt(&Serial);
}
sd.ls(&Serial, LS_SIZE);
Serial.print(F("sdCsInit calls: "));
Serial.println(initCalls);
Serial.print(F("sdCsWrite calls: "));
Serial.println(writeCalls);
}
//------------------------------------------------------------------------------
void loop() {}
#else // SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
#error SD_CHIP_SELECT_MODE must be one or two in SdFat/SdFatConfig.h
#endif // SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2

View file

View file

@ -0,0 +1,75 @@
// An example of an external SPI driver.
//
#include "SPI.h" // Only required if you use features in the SPI library.
#include "SdFat.h"
#if SPI_DRIVER_SELECT == 3 // Must be set in SdFat/SdFatConfig.h
// SD chip select pin.
#define SD_CS_PIN SS
// This is a simple driver based on the the standard SPI.h library.
// You can write a driver entirely independent of SPI.h.
// It can be optimized for your board or a different SPI port can be used.
// The driver must be derived from SdSpiBaseClass.
// See: SdFat/src/SpiDriver/SdSpiBaseClass.h
class MySpiClass : public SdSpiBaseClass {
public:
// Activate SPI hardware with correct speed and mode.
void activate() { SPI.beginTransaction(m_spiSettings); }
// Initialize the SPI bus.
void begin(SdSpiConfig config) {
(void)config;
SPI.begin();
}
// Deactivate SPI hardware.
void deactivate() { SPI.endTransaction(); }
// Receive a byte.
uint8_t receive() { return SPI.transfer(0XFF); }
// Receive multiple bytes.
// Replace this function if your board has multiple byte receive.
uint8_t receive(uint8_t* buf, size_t count) {
for (size_t i = 0; i < count; i++) {
buf[i] = SPI.transfer(0XFF);
}
return 0;
}
// Send a byte.
void send(uint8_t data) { SPI.transfer(data); }
// Send multiple bytes.
// Replace this function if your board has multiple byte send.
void send(const uint8_t* buf, size_t count) {
for (size_t i = 0; i < count; i++) {
SPI.transfer(buf[i]);
}
}
// Save SPISettings for new max SCK frequency
void setSckSpeed(uint32_t maxSck) {
m_spiSettings = SPISettings(maxSck, MSBFIRST, SPI_MODE0);
}
private:
SPISettings m_spiSettings;
} mySpi;
#if ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(50), &mySpi)
#else // ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50), &mySpi)
#endif // ENABLE_DEDICATED_SPI
SdFat sd;
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
if (!sd.begin(SD_CONFIG)) {
sd.initErrorHalt(&Serial);
}
sd.ls(&Serial, LS_SIZE);
Serial.println("Done");
}
//------------------------------------------------------------------------------
void loop() {}
#else // SPI_DRIVER_SELECT
#error SPI_DRIVER_SELECT must be three in SdFat/SdFatConfig.h
#endif // SPI_DRIVER_SELECT

View file

@ -1,16 +1,66 @@
/*
* This program is a simple binary write/read benchmark.
*/
#include <SPI.h>
#define DISABLE_FS_H_WARNING // Disable warning for type File not defined.
#include "SdFat.h"
#include "sdios.h"
#include "FreeStack.h"
#include "sdios.h"
// Set USE_SDIO to zero for SPI card access.
#define USE_SDIO 0
// 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
// SD chip select pin
const uint8_t chipSelect = SS;
#ifndef SD_FAT_TYPE
#define SD_FAT_TYPE 0 // Use SdFat/File
#endif // SD_FAT_TYPE
/*
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)
// Example SDIO definition for RP2040/RP2350. See the Rp2040SdioSetup example.
#if defined(ARDUINO_ADAFRUIT_METRO_RP2040) && !defined(RP_CLK_GPIO)
#define RP_CLK_GPIO 18
#define RP_CMD_GPIO 19
#define RP_DAT0_GPIO 20 // DAT1: GPIO21, DAT2: GPIO22, DAT3: GPIO23.
#endif // defined(ARDUINO_ADAFRUIT_METRO_RP2040)
// 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
// Set PRE_ALLOCATE true to pre-allocate file clusters.
const bool PRE_ALLOCATE = true;
// Set SKIP_FIRST_LATENCY true if the first read/write to the SD can
// be avoid by writing a file header or reading the first record.
const bool SKIP_FIRST_LATENCY = true;
// Size of read/write.
const size_t BUF_SIZE = 512;
@ -27,36 +77,33 @@ const uint8_t READ_COUNT = 2;
// End of configuration constants.
//------------------------------------------------------------------------------
// File size in bytes.
const uint32_t FILE_SIZE = 1000000UL*FILE_SIZE_MB;
const uint32_t FILE_SIZE = 1000000UL * FILE_SIZE_MB;
uint8_t buf[BUF_SIZE];
// Insure 4-byte alignment.
uint32_t buf32[(BUF_SIZE + 3) / 4];
uint8_t* buf = (uint8_t*)buf32;
// file system
#if USE_SDIO
// Traditional DMA version.
// SdFatSdio sd;
// Faster version.
SdFatSdioEX sd;
#else // USE_SDIO
#if SD_FAT_TYPE == 0
SdFat sd;
#endif // USE_SDIO
// Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O.
// Requires dedicated use of the SPI bus.
// SdFatEX sd;
// Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI.
// Args are misoPin, mosiPin, sckPin.
// SdFatSoftSpi<6, 7, 5> sd;
// test file
SdFile file;
File file;
#elif SD_FAT_TYPE == 1
SdFat32 sd;
File32 file;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile file;
#else // SD_FAT_TYPE
#error Invalid SD_FAT_TYPE
#endif // SD_FAT_TYPE
// Serial output stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// Store error strings in flash to save RAM.
#define error(s) sd.errorHalt(F(s))
#define error(s) sd.errorHalt(&Serial, F(s))
//------------------------------------------------------------------------------
void cidDmp() {
cid_t cid;
@ -64,31 +111,47 @@ void cidDmp() {
error("readCID failed");
}
cout << F("\nManufacturer ID: ");
cout << hex << int(cid.mid) << dec << endl;
cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
cout << F("Product: ");
for (uint8_t i = 0; i < 5; i++) {
cout << cid.pnm[i];
}
cout << F("\nVersion: ");
cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
cout << F("Serial number: ") << hex << cid.psn << dec << endl;
cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
cout << F("Manufacturing date: ");
cout << int(cid.mdt_month) << '/';
cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
cout << endl;
}
//------------------------------------------------------------------------------
void clearSerialInput() {
uint32_t m = micros();
do {
if (Serial.read() >= 0) {
m = micros();
}
} while (micros() - m < 10000);
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
delay(1000);
cout << F("\nUse a freshly formatted SD for best performance.\n");
if (!ENABLE_DEDICATED_SPI) {
cout << F(
"\nSet ENABLE_DEDICATED_SPI nonzero in\n"
"SdFatConfig.h for best SPI performance.\n");
}
if (!SD_HAS_CUSTOM_SPI && !USE_SPI_ARRAY_TRANSFER && isSpi(SD_CONFIG)) {
cout << F(
"\nSetting USE_SPI_ARRAY_TRANSFER nonzero in\n"
"SdFatConfig.h may improve SPI performance.\n");
}
// use uppercase in hex and use 0X base prefix
cout << uppercase << showbase << endl;
}
@ -99,33 +162,30 @@ void loop() {
uint32_t maxLatency;
uint32_t minLatency;
uint32_t totalLatency;
bool skipLatency;
// Discard any input.
do {
delay(10);
} while (Serial.available() && Serial.read() >= 0);
clearSerialInput();
// F( stores strings in flash to save RAM
// F() stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
cout << F("chipSelect: ") << int(chipSelect) << endl;
#if HAS_UNUSED_STACK
cout << F("FreeStack: ") << FreeStack() << endl;
#endif // HAS_UNUSED_STACK
#if USE_SDIO
if (!sd.begin()) {
sd.initErrorHalt();
if (!sd.begin(SD_CONFIG)) {
sd.initErrorHalt(&Serial);
}
#else // USE_SDIO
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
if (sd.fatType() == FAT_TYPE_EXFAT) {
cout << F("Type is exFAT") << endl;
} else {
cout << F("Type is FAT") << int(sd.fatType()) << endl;
}
#endif // USE_SDIO
cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
cout << F("Card size: ") << sd.card()->cardSize()*512E-9;
cout << F("Card size: ") << sd.card()->sectorCount() * 512E-9;
cout << F(" GB (GB = 1E9 bytes)") << endl;
cidDmp();
@ -136,51 +196,62 @@ void loop() {
}
// fill buf with known data
for (size_t i = 0; i < (BUF_SIZE-2); i++) {
if (BUF_SIZE > 1) {
for (size_t i = 0; i < (BUF_SIZE - 2); i++) {
buf[i] = 'A' + (i % 26);
}
buf[BUF_SIZE-2] = '\r';
buf[BUF_SIZE-1] = '\n';
buf[BUF_SIZE - 2] = '\r';
}
buf[BUF_SIZE - 1] = '\n';
cout << F("File size ") << FILE_SIZE_MB << F(" MB\n");
cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n");
cout << F("FILE_SIZE_MB = ") << FILE_SIZE_MB << endl;
cout << F("BUF_SIZE = ") << BUF_SIZE << F(" bytes\n");
cout << F("Starting write test, please wait.") << endl << endl;
// do write test
uint32_t n = FILE_SIZE/sizeof(buf);
cout <<F("write speed and latency") << endl;
uint32_t n = FILE_SIZE / BUF_SIZE;
cout << F("write speed and latency") << endl;
cout << F("speed,max,min,avg") << endl;
cout << F("KB/Sec,usec,usec,usec") << endl;
for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
file.truncate(0);
if (PRE_ALLOCATE) {
if (!file.preAllocate(FILE_SIZE)) {
error("preAllocate failed");
}
}
maxLatency = 0;
minLatency = 9999999;
totalLatency = 0;
skipLatency = SKIP_FIRST_LATENCY;
t = millis();
for (uint32_t i = 0; i < n; i++) {
uint32_t m = micros();
if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
sd.errorPrint("write failed");
file.close();
return;
if (file.write(buf, BUF_SIZE) != BUF_SIZE) {
error("write failed");
}
m = micros() - m;
totalLatency += m;
if (skipLatency) {
// Wait until first write to SD, not just a copy to the cache.
skipLatency = file.curPosition() < 512;
} else {
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m;
}
}
file.sync();
t = millis() - t;
s = file.fileSize();
cout << s/t <<',' << maxLatency << ',' << minLatency;
cout << ',' << totalLatency/n << endl;
cout << s / t << ',' << maxLatency << ',' << minLatency;
cout << ',' << totalLatency / n << endl;
}
cout << endl << F("Starting read test, please wait.") << endl;
cout << endl <<F("read speed and latency") << endl;
cout << endl << F("read speed and latency") << endl;
cout << F("speed,max,min,avg") << endl;
cout << F("KB/Sec,usec,usec,usec") << endl;
@ -190,33 +261,37 @@ void loop() {
maxLatency = 0;
minLatency = 9999999;
totalLatency = 0;
skipLatency = SKIP_FIRST_LATENCY;
t = millis();
for (uint32_t i = 0; i < n; i++) {
buf[BUF_SIZE-1] = 0;
buf[BUF_SIZE - 1] = 0;
uint32_t m = micros();
int32_t nr = file.read(buf, sizeof(buf));
if (nr != sizeof(buf)) {
sd.errorPrint("read failed");
file.close();
return;
int32_t nr = file.read(buf, BUF_SIZE);
if (nr != BUF_SIZE) {
error("read failed");
}
m = micros() - m;
totalLatency += m;
if (buf[BUF_SIZE - 1] != '\n') {
error("data check error");
}
if (skipLatency) {
skipLatency = false;
} else {
if (maxLatency < m) {
maxLatency = m;
}
if (minLatency > m) {
minLatency = m;
}
totalLatency += m;
if (buf[BUF_SIZE-1] != '\n') {
error("data check");
}
}
s = file.fileSize();
t = millis() - t;
cout << s/t <<',' << maxLatency << ',' << minLatency;
cout << ',' << totalLatency/n << endl;
cout << s / t << ',' << maxLatency << ',' << minLatency;
cout << ',' << totalLatency / n << endl;
}
cout << endl << F("Done") << endl;
file.close();
sd.end();
}

View file

@ -0,0 +1,119 @@
#include "SdFat.h"
#ifdef __AVR__
const uint32_t FILE_SIZE_MiB = 10UL;
#else // __AVR__
const uint32_t FILE_SIZE_MiB = 100UL;
#endif
bool waitBusy = true;
#define SD_CONFIG SdSpiConfig(SS, DEDICATED_SPI)
//#define SD_CONFIG SdSpiConfig(SS, SHARED_SPI)
// Config for Teensy 3.5/3.6 buit-in SD.
//#define SD_CONFIG SdSpiConfig(SDCARD_SS_PIN, DEDICATED_SPI)
//#define SD_CONFIG SdioConfig(FIFO_SDIO)
//------------------------------------------------------------------------------
const uint64_t FILE_SIZE = (uint64_t)FILE_SIZE_MiB << 20;
SdFs sd;
FsFile file;
uint8_t buf[512];
#define error(s) sd.errorHalt(&Serial, F(s))
//------------------------------------------------------------------------------
void clearSerialInput() {
uint32_t m = micros();
do {
if (Serial.read() >= 0) {
m = micros();
}
} while (micros() - m < 10000);
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
delay(1000);
//------------------------------------------------------------------------------
}
void loop() {
clearSerialInput();
Serial.println(F("\nType any character to start\n"));
while (!Serial.available()) {
yield();
}
// Initialize the SD card.
if (!sd.begin(SD_CONFIG)) {
sd.initErrorHalt();
}
if (!file.open("SdBusyTest.bin", O_RDWR | O_CREAT |O_TRUNC)) {
error("file open failed");
}
if (!file.preAllocate(FILE_SIZE)) {
error("preallocate failed");
}
Serial.print(F("Starting write of "));
Serial.print(FILE_SIZE_MiB);
Serial.println(F(" MiB."));
uint32_t maxWrite = 0;
uint32_t minWrite = 99999999;
uint32_t ms = millis();
uint32_t maxBusy = 0;
uint32_t minBusy = UINT32_MAX;
// Write a dummy sector to start a multi-sector write.
if(file.write(buf, sizeof(buf)) != sizeof(buf)) {
error("write failed for first sector");
}
while (file.position() < FILE_SIZE) {
uint32_t m = micros();
if (waitBusy) {
m = micros();
while (sd.card()->isBusy()) {}
m = micros() - m;
if (m < minBusy) {
minBusy = m;
}
if (m > maxBusy) {
maxBusy = m;
}
}
m = micros();
if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
error("write failed");
}
m = micros() - m;
if (m < minWrite) {
minWrite = m;
}
if (m > maxWrite) {
maxWrite = m;
}
}
file.close();
ms = millis() - ms;
Serial.println(F("\nTimes in micros"));
if (waitBusy) {
Serial.print(F("minBusy: "));
Serial.println(minBusy);
Serial.print(F("maxBusy: "));
Serial.println(maxBusy);
}
Serial.print(F("minWrite: "));
Serial.println(minWrite);
Serial.print(F("maxWrite: "));
Serial.println(maxWrite);
Serial.print(1e-3*ms);
Serial.println(F(" Seconds"));
Serial.print(1.0*FILE_SIZE/ms);
Serial.println(F(" KB/sec"));
}

View file

@ -0,0 +1,51 @@
#include "SdFat.h"
#define DUMP_RAW 0
#define DUMP_UPCASE 0
const uint8_t CS_PIN = SS;
//#define SD_CONFIG SdioConfig(FIFO_SDIO)
#define SD_CONFIG SdSpiConfig(CS_PIN)
SdExFat sd;
#define error(s) sd.errorHalt(&Serial, F(s))
void setup() {
Serial.begin(9600);
while (!Serial) {
yield();
}
Serial.println(F("Type any character to begin"));
while (!Serial.available()) {
yield();
}
if (!sd.begin(SD_CONFIG)){
error("begin failed");
}
#if DUMP_RAW
sd.dmpSector(&Serial, 0);
for (uint8_t i = 0; i < 24; i++) {
sd.dmpSector(&Serial, 0X8000 + i);
Serial.println();
}
return;
#endif // DUMP_RAW
ExFatFile root;
if (!root.openRoot(&sd)) {
error("openRoot failed");
}
sd.printDir(&Serial, &root);
// startSector = 0, sectorCount = 1.
sd.dmpFat(&Serial, 0, 1);
sd.dmpBitmap(&Serial);
sd.printVolInfo(&Serial);
sd.checkUpcase(&Serial);
#if DUMP_UPCASE
sd.printUpcase(&Serial);
#endif // DUMP_UPCASE
// sd.dmpCluster(&Serial, 8, 0, 4);
Serial.println("Done");
}
void loop() {
// put your main code here, to run repeatedly:
}

View file

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

View file

@ -0,0 +1,43 @@
#include "SdFat.h"
SdFs sd;
FsFile file;
const char* name[] = {
"SFN.TXT",
"LongFilename.txt",
#if USE_UTF8_LONG_NAMES
u8"très élégant.txt",
#endif // USE_UTF8_LONG_NAMES
nullptr};
char buf[32];
void setup() {
Serial.begin(9600);
while (!Serial) {}
Serial.println("Type any character to begin");
while (!Serial.available()) {}
if (!sd.begin(SS)) {
sd.initErrorHalt();
}
for (uint8_t i = 0; name[i]; i++) {
if (!file.open(name[i], O_CREAT |O_RDWR)) {
sd.errorHalt("open");
}
size_t len = strlen(name[i]);
size_t rtn = file.getName(buf, len);
if (rtn != 0) {
Serial.println("fail len");
}
rtn = file.getName(buf, len + 1);
if (rtn != len) {
Serial.println("fail len + 1");
}
Serial.print(rtn);
Serial.print(' ');
Serial.println(buf);
if (!file.remove()) {
sd.errorHalt("remove");
}
}
Serial.println("Done");
}
void loop() {}

View file

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

View file

@ -10,23 +10,22 @@
* Note: Some cards may 'stutter' others just get slow due
* to the number of flash erases this program causes.
*/
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
const uint8_t SD_CHIP_SELECT = SS;
SdFat sd;
typedef File file_t;
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
#define error(s) sd.errorHalt(&Serial, F(s))
/*
* create enough files to force a cluster to be allocated to dir.
*/
void dirAllocTest(FatFile* dir) {
void dirAllocTest(file_t* dir) {
char buf[32], name[32];
SdFile file;
file_t file;
uint16_t n;
uint32_t size = dir->dirSize();
@ -37,7 +36,7 @@ void dirAllocTest(FatFile* dir) {
// open start time
uint32_t t0 = millis();
if (!file.open(dir, name, O_WRITE | O_CREAT | O_EXCL)) {
if (!file.open(dir, name, O_WRONLY | O_CREAT | O_EXCL)) {
error("open for write failed");
}
@ -70,7 +69,7 @@ void dirAllocTest(FatFile* dir) {
// open start time
uint32_t t0 = millis();
if (!file.open(dir, name, O_READ)) {
if (!file.open(dir, name, O_RDONLY)) {
error("open for read failed");
}
@ -102,6 +101,7 @@ void dirAllocTest(FatFile* dir) {
}
void setup() {
file_t root;
Serial.begin(9600);
while (!Serial) {} // wait for Leonardo
Serial.println(F("Type any character to start"));
@ -109,24 +109,26 @@ void setup() {
delay(200); // Catch Due reset problem
// initialize the SD card at SPI_FULL_SPEED for best performance.
// try SPI_HALF_SPEED if bus errors occur.
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt();
// try lower speed if bus errors occur.
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) {
sd.initErrorHalt(&Serial);
}
root.openRoot(&sd);
uint32_t m = millis();
// write files to root if FAT32
if (sd.vol()->fatType() == 32) {
// write files to root if not FAT16
if (sd.fatType() != 16) {
Serial.println(F("Writing files to root"));
dirAllocTest(sd.vwd());
dirAllocTest(&root);
}
// create sub1 and write files
SdFile sub1;
if (!sub1.mkdir(sd.vwd(), "SUB1")) error("makdeDir SUB1 failed");
file_t sub1;
if (!sub1.mkdir(&root, "SUB1")) error("makdeDir SUB1 failed");
Serial.println(F("Writing files to SUB1"));
dirAllocTest(&sub1);
// create sub2 and write files
SdFile sub2;
file_t sub2;
if (!sub2.mkdir(&sub1, "SUB2")) error("mkdir SUB2 failed");
Serial.println(F("Writing files to SUB2"));
dirAllocTest(&sub2);

View file

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

View file

@ -6,23 +6,22 @@
* of flash erase operations caused by many random
* writes to file structures.
*/
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
const uint8_t SD_CHIP_SELECT = SS;
SdFat sd;
typedef File file_t;
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
#define error(s) sd.errorHalt(&Serial, F(s))
/*
* remove all files in dir.
*/
void deleteFiles(FatFile* dir) {
char name[32];
SdFile file;
file_t file;
// open and delete files
for (uint16_t n = 0; ; n++){
@ -32,7 +31,7 @@ void deleteFiles(FatFile* dir) {
uint32_t t0 = millis();
// assume done if open fails
if (!file.open(dir, name, O_WRITE)) return;
if (!file.open(dir, name, O_WRONLY)) return;
// open end time and remove start time
uint32_t t1 = millis();
@ -55,6 +54,7 @@ void deleteFiles(FatFile* dir) {
}
void setup() {
file_t root;
Serial.begin(9600);
while (!Serial) {} // wait for Leonardo
Serial.println(F("Type any character to start"));
@ -62,25 +62,26 @@ void setup() {
delay(200); // Catch Due reset problem
// initialize the SD card at SPI_FULL_SPEED for best performance.
// try SPI_HALF_SPEED if bus errors occur.
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) sd.initErrorHalt();
// delete files in root if FAT32
if (sd.vol()->fatType() == 32) {
// try lower speed if bus errors occur.
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED)) {
sd.initErrorHalt(&Serial);
}
root.openRoot(&sd);
// delete files in root if not FAT16.
if (sd.fatType() != 16) {
Serial.println(F("Remove files in root"));
deleteFiles(sd.vwd());
deleteFiles(&root);
}
// open SUB1 and delete files
SdFile sub1;
if (!sub1.open("SUB1", O_READ)) error("open SUB1 failed");
file_t sub1;
if (!sub1.open("SUB1", O_RDONLY)) error("open SUB1 failed");
Serial.println(F("Remove files in SUB1"));
deleteFiles(&sub1);
// open SUB2 and delete files
SdFile sub2;
if (!sub2.open(&sub1, "SUB2", O_READ)) error("open SUB2 failed");
file_t sub2;
if (!sub2.open(&sub1, "SUB2", O_RDONLY)) error("open SUB2 failed");
Serial.println(F("Remove files in SUB2"));
deleteFiles(&sub2);

View file

@ -0,0 +1,162 @@
/*
* This program tests the dateTimeCallback() function
* and the timestamp() function.
*/
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
SdFs sd;
FsFile file;
// Default SD chip select is SS pin
const uint8_t chipSelect = SS;
// create Serial stream
ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
// store error strings in flash to save RAM
#define error(s) sd.errorHalt(F(s))
//------------------------------------------------------------------------------
/*
* date/time values for debug
* normally supplied by a real-time clock or GPS
*/
// date 1-Oct-21
uint16_t year = 2021;
uint8_t month = 10;
uint8_t day = 1;
// time 20:30:40
uint8_t hour = 20;
uint8_t minute = 30;
uint8_t second = 40;
//------------------------------------------------------------------------------
/*
* User provided date time callback function.
* See SdFile::dateTimeCallback() for usage.
*/
void dateTime(uint16_t* date, uint16_t* time) {
// User gets date and time from GPS or real-time
// clock in real callback function
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(year, month, day);
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(hour, minute, second);
}
//------------------------------------------------------------------------------
/*
* Function to print all timestamps.
*/
void printTimestamps(FsFile& f) {
cout << F("Creation: ");
f.printCreateDateTime(&Serial);
cout << endl << F("Modify: ");
f.printModifyDateTime(&Serial);
cout << endl << F("Access: ");
f.printAccessDateTime(&Serial);
cout << endl;
}
//------------------------------------------------------------------------------
void setup(void) {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
yield();
}
cout << F("Type any character to start\n");
while (!Serial.available()) {
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
sd.initErrorHalt();
}
// remove files if they exist
sd.remove("callback.txt");
sd.remove("default.txt");
sd.remove("stamp.txt");
// create a new file with default timestamps
if (!file.open("default.txt", O_WRONLY | O_CREAT)) {
error("open default.txt failed");
}
cout << F("\nOpen with default times\n");
printTimestamps(file);
// close file
file.close();
/*
* Test the date time callback function.
*
* dateTimeCallback() sets the function
* that is called when a file is created
* or when a file's directory entry is
* modified by sync().
*
* The callback can be disabled by the call
* SdFile::dateTimeCallbackCancel()
*/
// set date time callback function
SdFile::dateTimeCallback(dateTime);
// create a new file with callback timestamps
if (!file.open("callback.txt", O_WRONLY | O_CREAT)) {
error("open callback.txt failed");
}
cout << ("\nOpen with callback times\n");
printTimestamps(file);
// change call back date
day += 1;
// must add two to see change since FAT second field is 5-bits
second += 2;
// modify file by writing a byte
file.write('t');
// force dir update
file.sync();
cout << F("\nTimes after write\n");
printTimestamps(file);
// close file
file.close();
/*
* Test timestamp() function
*
* Cancel callback so sync will not
* change access/modify timestamp
*/
SdFile::dateTimeCallbackCancel();
// create a new file with default timestamps
if (!file.open("stamp.txt", O_WRONLY | O_CREAT)) {
error("open stamp.txt failed");
}
// set creation date time
if (!file.timestamp(T_CREATE, 2021, 11, 10, 1, 2, 3)) {
error("set create time failed");
}
// set write/modification date time
if (!file.timestamp(T_WRITE, 2021, 11, 11, 4, 5, 6)) {
error("set write time failed");
}
// set access date
if (!file.timestamp(T_ACCESS, 2021, 11, 12, 7, 8, 9)) {
error("set access time failed");
}
cout << F("\nTimes after timestamp() calls\n");
printTimestamps(file);
file.close();
cout << F("\nDone\n");
}
void loop() {}

View file

@ -69,7 +69,7 @@ void setup() {
// Wait for USB Serial.
while (!Serial) {
SysCall::yield();
yield();
}
// F() stores strings in flash to save RAM
cout << endl << F("FreeStack: ") << FreeStack() << endl;
@ -77,7 +77,7 @@ void setup() {
#if WAIT_TO_START
cout << F("Type any character to start\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
// Discard input.
do {
@ -193,5 +193,5 @@ void loop() {
}
logfile.close();
cout << F("Done!");
SysCall::halt();
while (true) {}
}

View file

@ -20,11 +20,11 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
Serial.println("type any character to start");
while (!Serial.available()) {
SysCall::yield();
yield();
}
if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
Serial.println("begin failed");

View file

@ -10,7 +10,7 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
delay(2000);

View file

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

View file

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

View file

@ -15,7 +15,7 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
if (!sd.begin()) {

View file

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

View file

@ -13,11 +13,11 @@ void setup() {
Serial.begin(9600);
// Wait for USB Serial.
while(!Serial) {
SysCall::yield();
yield();
}
Serial.println(F("Type any character to start"));
while (!Serial.available()) {
SysCall::yield();
yield();
}
// Initialize the SD.
if (!SD.begin(csPin)) {

View file

@ -29,12 +29,12 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
// F() stores strings in flash to save RAM
cout << endl << F("Type any character to start\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
// Initialize at the highest speed supported by the board that is

View file

@ -58,12 +58,12 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
// F() stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
// Initialize at the highest speed supported by the board that is

View file

@ -0,0 +1,4 @@
feather_rp2040
metro_rp2040
feather_esp32s2
feather_esp32s3

View file

@ -61,7 +61,7 @@ void loop() {
}
// fill buf with known data
for (uint16_t i = 0; i < (BUF_SIZE-2); i++) {
for (size_t i = 0; i < (BUF_SIZE-2); i++) {
buf[i] = 'A' + (i % 26);
}
buf[BUF_SIZE-2] = '\r';

View file

@ -16,7 +16,7 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
delay(2000);

View file

@ -18,7 +18,7 @@ void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
}
//------------------------------------------------------------------------------

View file

@ -38,12 +38,12 @@ void setup() {
Serial.begin(9600);
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
// F() stores strings in flash to save RAM
cout << F("Type any character to start\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
delay(400); // catch Due reset problem

View file

@ -89,11 +89,11 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
cout << F("Type any character to start\n");
while (!Serial.available()) {
SysCall::yield();
yield();
}
// Initialize at the highest speed supported by the board that is

View file

@ -21,7 +21,7 @@ void setup() {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.

View file

@ -22,6 +22,7 @@
#ifdef __AVR__
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
#include "FreeStack.h"
#include "AnalogBinLogger.h"
//------------------------------------------------------------------------------
@ -648,7 +649,7 @@ void logData() {
bgnErase = endErase + 1;
}
// Start a multiple block write.
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) {
if (!sd.card()->writeStart(bgnBlock)) {
error("writeBegin failed");
}
// Write metadata.
@ -798,7 +799,7 @@ void loop(void) {
Serial.println(F("r - record ADC data"));
while(!Serial.available()) {
SysCall::yield();
yield();
}
char c = tolower(Serial.read());
if (ERROR_LED_PIN >= 0) {

View file

@ -72,7 +72,7 @@ void loop() {
Serial.print(F("\r\nEnter File Number: "));
while (!Serial.available()) {
SysCall::yield();
yield();
}
c = Serial.read();
uint8_t i = c - '0';

View file

@ -123,7 +123,7 @@ struct block_t {
//
void fatalBlink() {
while (true) {
SysCall::yield();
yield();
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
delay(200);
@ -321,7 +321,7 @@ void openBinFile() {
Serial.write(name, BASE_NAME_SIZE);
for (int i = 0; i < 2; i++) {
while (!Serial.available()) {
SysCall::yield();
yield();
}
char c = Serial.read();
Serial.write(c);
@ -566,7 +566,7 @@ void setup(void) {
// Wait for USB Serial
while (!Serial) {
SysCall::yield();
yield();
}
Serial.print(F("\nFreeStack: "));
Serial.println(FreeStack());
@ -592,7 +592,7 @@ void setup(void) {
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("\nType 'Y' to recover existing tmp file " TMP_FILE_NAME));
while (!Serial.available()) {
SysCall::yield();
yield();
}
if (Serial.read() == 'Y') {
recoverTmpFile();
@ -617,11 +617,11 @@ void loop(void) {
Serial.println(F("r - record data"));
Serial.println(F("t - test without logging"));
while(!Serial.available()) {
SysCall::yield();
yield();
}
#if WDT_YIELD_TIME_MICROS
Serial.println(F("LowLatencyLogger can not run with watchdog timer"));
SysCall::halt();
while (true) {}
#endif
char c = tolower(Serial.read());

Some files were not shown because too many files have changed in this diff Show more