mirror of https://github.com/radex/radmatrix.git
Compare commits
11 Commits
99f4f36d31
...
efb94c2d78
Author | SHA1 | Date |
---|---|---|
radex | efb94c2d78 | |
radex | 2cc5f3d6fc | |
radex | 72045008f8 | |
radex | 20acc815de | |
radex | 6ba449a1a4 | |
radex | da893fd9de | |
radex | 62a69a4c6d | |
radex | 634406bb2e | |
radex | a31ce07582 | |
radex | c2a5081fbc | |
radex | 92edaca309 |
|
@ -0,0 +1,9 @@
|
||||||
|
**/build
|
||||||
|
*.html
|
||||||
|
*.png
|
||||||
|
*.zip
|
||||||
|
*.xlsx
|
||||||
|
*.bak
|
||||||
|
**/.vscode
|
||||||
|
*.tar.gz
|
||||||
|
PlatformIO.notes.txt
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
File diff suppressed because it is too large
Load Diff
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/bench2/.gitignore
vendored
Normal file
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/bench2/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -0,0 +1,45 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:pico]
|
||||||
|
; platform = raspberrypi
|
||||||
|
; board = pico
|
||||||
|
; framework = arduino
|
||||||
|
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
build_type = release
|
||||||
|
|
||||||
|
build_flags =
|
||||||
|
"-Wno-psabi"
|
||||||
|
-D PICO_STACK_SIZE=0x400
|
||||||
|
-D __HEAP_SIZE=0x400
|
||||||
|
-D PICO_USE_STACK_GUARDS=1
|
||||||
|
; -D USE_DBG_PRINTF
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
||||||
|
|
||||||
|
; lib_ldf_mode = deep+
|
|
@ -0,0 +1,363 @@
|
||||||
|
/* Ported from: https://github.com/greiman/SdFat/blob/master/examples/bench/bench.ino
|
||||||
|
*
|
||||||
|
* This program is a simple binary write/read benchmark.
|
||||||
|
*
|
||||||
|
* Warning: this might destroy any data on the SD card(s), depending on configuration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "FatFsSd.h"
|
||||||
|
#include "my_debug.h"
|
||||||
|
|
||||||
|
using namespace FatFsNs;
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.printf("%s\r", s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.printf("%s\r", s);
|
||||||
|
}
|
||||||
|
/* This will not be called unless build_flags include "-D USE_DBG_PRINTF": */
|
||||||
|
// void put_out_debug_message(const char *s) {
|
||||||
|
// Serial1.printf("%s\r", s);
|
||||||
|
// }
|
||||||
|
#define printf Serial1.printf
|
||||||
|
#define puts Serial1.println
|
||||||
|
|
||||||
|
#define error(s) \
|
||||||
|
{ \
|
||||||
|
printf("ERROR: %s\r\n", s); \
|
||||||
|
for (;;) __breakpoint(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
// Whether or not to format the card(s) in setup():
|
||||||
|
static const bool FORMAT = false;
|
||||||
|
|
||||||
|
// Set PRE_ALLOCATE true to pre-allocate file clusters.
|
||||||
|
static 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.
|
||||||
|
static const bool SKIP_FIRST_LATENCY = true;
|
||||||
|
|
||||||
|
// Size of read/write.
|
||||||
|
// static const size_t BUF_SIZE = 512;
|
||||||
|
#define BUF_SIZE (20 * 1024)
|
||||||
|
|
||||||
|
// File size in MiB where MiB = 1048576 bytes.
|
||||||
|
static const uint32_t FILE_SIZE_MiB = 5;
|
||||||
|
|
||||||
|
// Write pass count.
|
||||||
|
static const uint8_t WRITE_COUNT = 2;
|
||||||
|
|
||||||
|
// Read pass count.
|
||||||
|
static const uint8_t READ_COUNT = 2;
|
||||||
|
//==============================================================================
|
||||||
|
// End of configuration constants.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// File size in bytes.
|
||||||
|
// static const uint32_t FILE_SIZE = 1000000UL * FILE_SIZE_MB;
|
||||||
|
static const uint32_t FILE_SIZE = (1024 * 1024 * FILE_SIZE_MiB);
|
||||||
|
|
||||||
|
// First (if s is not NULL and *s is not a null byte ('\0')) the argument string s is printed,
|
||||||
|
// followed by a colon and a blank. Then the FRESULT error message and a new-line.
|
||||||
|
static void chk_result(const char* s, FRESULT fr) {
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
if (s && *s)
|
||||||
|
printf("%s: %s (%d)\r\n", s, FRESULT_str(fr), fr);
|
||||||
|
else
|
||||||
|
printf("%s (%d)\r\n", FRESULT_str(fr), fr);
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// put your setup code here, to run once:
|
||||||
|
|
||||||
|
Serial1.begin(115200); // set up Serial library at 9600 bps
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
printf("\033[2J\033[H"); // Clear Screen
|
||||||
|
printf("\nUse a freshly formatted SD for best performance.\r\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example assumes the following wiring for SD card 0:
|
||||||
|
| GPIO | Function | SD Card | SPI0 |
|
||||||
|
| ---- | -------------------------------- | ------- | -------- |
|
||||||
|
| GP2 | SCK | CLK | SPI0_SCK |
|
||||||
|
| GP3 | MOSI (COPI or Peripheral's SDI) | CMD/DI | SPI0_TX |
|
||||||
|
| GP4 | MISO (CIPO or Peripheral's SDO) | D0/DO | SPI0_RX |
|
||||||
|
| GP7 | SS (CS) | D3/CS | |
|
||||||
|
| GP9 | Card Detect | DET | |
|
||||||
|
|
||||||
|
This example assumes the following wiring for SD card 1:
|
||||||
|
| GPIO | SD Card |
|
||||||
|
| ---- | ------- |
|
||||||
|
| GP16 | CLK |
|
||||||
|
| GP17 | CMD |
|
||||||
|
| GP18 | D0 |
|
||||||
|
| GP19 | D1 |
|
||||||
|
| GP20 | D2 |
|
||||||
|
| GP21 | D3 |
|
||||||
|
| GP22 | DET |
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware Configuration of SPI object */
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi0, // RP2040 SPI component
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.baud_rate = 12 * 1000 * 1000 // Actual frequency: 10416666.
|
||||||
|
};
|
||||||
|
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7 // The SPI slave select GPIO for this SD card
|
||||||
|
};
|
||||||
|
|
||||||
|
static sd_sdio_if_t sdio_if = {
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.baud_rate = 15 * 1000 * 1000 // 15 MHz
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "objects"
|
||||||
|
static sd_card_t sd_cards[] = {
|
||||||
|
{ // sd_cards[0]
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[1]
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_if,
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FatFs::add_sd_card(&sd_cards[0]);
|
||||||
|
FatFs::add_sd_card(&sd_cards[1]);
|
||||||
|
|
||||||
|
if (!FatFs::begin())
|
||||||
|
error("Driver initialization failed\r\n");
|
||||||
|
|
||||||
|
if (FORMAT) {
|
||||||
|
for (size_t i = 0; i < FatFs::SdCard_get_num(); ++i) {
|
||||||
|
SdCard* SdCard_p = FatFs::SdCard_get_by_num(i);
|
||||||
|
printf("Formatting drive %s...\r\n", SdCard_p->get_name());
|
||||||
|
FRESULT fr = SdCard_p->format();
|
||||||
|
chk_result("format", fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
static void bench(char const* logdrv) {
|
||||||
|
File file;
|
||||||
|
float s;
|
||||||
|
uint32_t t;
|
||||||
|
uint32_t maxLatency;
|
||||||
|
uint32_t minLatency;
|
||||||
|
uint32_t totalLatency;
|
||||||
|
bool skipLatency;
|
||||||
|
|
||||||
|
static_assert(0 == FILE_SIZE % BUF_SIZE,
|
||||||
|
"For accurate results, FILE_SIZE must be a multiple of BUF_SIZE.");
|
||||||
|
|
||||||
|
// Insure 4-byte alignment.
|
||||||
|
uint32_t buf32[BUF_SIZE] __attribute__((aligned(4)));
|
||||||
|
uint8_t* buf = (uint8_t*)buf32;
|
||||||
|
|
||||||
|
SdCard* SdCard_p(FatFs::SdCard_get_by_name(logdrv));
|
||||||
|
if (!SdCard_p) {
|
||||||
|
printf("Unknown logical drive name: %s\r\n", logdrv);
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
FRESULT fr = f_chdrive(logdrv);
|
||||||
|
chk_result("f_chdrive", fr);
|
||||||
|
|
||||||
|
switch (SdCard_p->fatfs()->fs_type) {
|
||||||
|
case FS_EXFAT:
|
||||||
|
printf("Type is exFAT\r\n");
|
||||||
|
break;
|
||||||
|
case FS_FAT12:
|
||||||
|
printf("Type is FAT12\r\n");
|
||||||
|
break;
|
||||||
|
case FS_FAT16:
|
||||||
|
printf("Type is FAT16\r\n");
|
||||||
|
break;
|
||||||
|
case FS_FAT32:
|
||||||
|
printf("Type is FAT32\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Card size: ");
|
||||||
|
printf("%.2f", SdCard_p->get_num_sectors() * 512E-9);
|
||||||
|
printf(" GB (GB = 1E9 bytes)\r\n");
|
||||||
|
|
||||||
|
// typedef int (*printer_t)(const char* format, ...);
|
||||||
|
|
||||||
|
SdCard_p->cidDmp(info_message_printf);
|
||||||
|
SdCard_p->csdDmp(info_message_printf);
|
||||||
|
|
||||||
|
SdCard_p->mount();
|
||||||
|
chk_result("f_mount", fr);
|
||||||
|
|
||||||
|
// fill buf with known data
|
||||||
|
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';
|
||||||
|
|
||||||
|
// Open or create file.
|
||||||
|
// FA_CREATE_ALWAYS:
|
||||||
|
// Creates a new file.
|
||||||
|
// If the file is existing, it will be truncated and overwritten.
|
||||||
|
fr = file.open("bench.dat", FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
|
chk_result("open", fr);
|
||||||
|
|
||||||
|
// Open with FA_CREATE_ALWAYS creates a new file,
|
||||||
|
// and if the file is existing, it will be truncated and overwritten.
|
||||||
|
if (PRE_ALLOCATE) {
|
||||||
|
fr = file.expand(FILE_SIZE);
|
||||||
|
chk_result("file.expand", fr);
|
||||||
|
}
|
||||||
|
printf("FILE_SIZE_MB = %lu\r\n", FILE_SIZE_MiB);
|
||||||
|
printf("BUF_SIZE = %zu\r\n", BUF_SIZE);
|
||||||
|
printf("Starting write test, please wait.\n\r\n");
|
||||||
|
|
||||||
|
// do write test
|
||||||
|
uint32_t n = FILE_SIZE / BUF_SIZE;
|
||||||
|
printf("write speed and latency\r\n");
|
||||||
|
printf("speed,max,min,avg\r\n");
|
||||||
|
printf("KB/Sec,usec,usec,usec\r\n");
|
||||||
|
for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
|
||||||
|
fr = file.rewind();
|
||||||
|
chk_result("file.rewind", fr);
|
||||||
|
|
||||||
|
maxLatency = 0;
|
||||||
|
minLatency = 9999999;
|
||||||
|
totalLatency = 0;
|
||||||
|
skipLatency = SKIP_FIRST_LATENCY;
|
||||||
|
t = millis();
|
||||||
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
|
uint32_t m = micros();
|
||||||
|
|
||||||
|
unsigned int bw;
|
||||||
|
fr = file.write(buf, BUF_SIZE, &bw); /* Write it to the destination file */
|
||||||
|
chk_result("file.write", fr);
|
||||||
|
if (bw < BUF_SIZE) { /* error or disk full */
|
||||||
|
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;
|
||||||
|
skipLatency = file.tell() < 512;
|
||||||
|
} else {
|
||||||
|
if (maxLatency < m) {
|
||||||
|
maxLatency = m;
|
||||||
|
}
|
||||||
|
if (minLatency > m) {
|
||||||
|
minLatency = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fr = file.sync();
|
||||||
|
chk_result("file.sync", fr);
|
||||||
|
|
||||||
|
t = millis() - t;
|
||||||
|
s = file.size();
|
||||||
|
printf("%.1f,%lu,%lu", s / t, maxLatency, minLatency);
|
||||||
|
printf(",%lu\r\n", totalLatency / n);
|
||||||
|
}
|
||||||
|
printf("\nStarting read test, please wait.\r\n");
|
||||||
|
printf("\nread speed and latency\r\n");
|
||||||
|
printf("speed,max,min,avg\r\n");
|
||||||
|
printf("KB/Sec,usec,usec,usec\r\n");
|
||||||
|
|
||||||
|
// do read test
|
||||||
|
for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) {
|
||||||
|
fr = file.rewind();
|
||||||
|
chk_result("file.rewind", fr);
|
||||||
|
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;
|
||||||
|
uint32_t m = micros();
|
||||||
|
unsigned int nr;
|
||||||
|
fr = file.read(buf, BUF_SIZE, &nr);
|
||||||
|
chk_result("file.read", fr);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = file.size();
|
||||||
|
t = millis() - t;
|
||||||
|
printf("%.1f,%lu,%lu", s / t, maxLatency, minLatency);
|
||||||
|
printf(",%lu\r\n", totalLatency / n);
|
||||||
|
}
|
||||||
|
printf("\nDone\r\n");
|
||||||
|
fr = file.close();
|
||||||
|
chk_result("file.close", fr);
|
||||||
|
fr = SdCard_p->unmount();
|
||||||
|
chk_result("file.unmount", fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// put your main code here, to run repeatedly:
|
||||||
|
printf("\nTesting drive 0:\r\n");
|
||||||
|
bench("0:");
|
||||||
|
printf("\nTesting drive 1:\r\n");
|
||||||
|
bench("1:");
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,34 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:pico]
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
build_flags =
|
||||||
|
"-Wno-psabi"
|
||||||
|
-D USE_DBG_PRINTF ; Debug output
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
|
@ -0,0 +1,251 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example reads analog input A0 and logs the voltage
|
||||||
|
in a file on SD cards once per second.
|
||||||
|
|
||||||
|
It also demonstates a way to do static configuration.
|
||||||
|
*/
|
||||||
|
#include <time.h>
|
||||||
|
//
|
||||||
|
#include "hardware/adc.h"
|
||||||
|
#include "hardware/rtc.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
//
|
||||||
|
#include "FatFsSd.h"
|
||||||
|
#include "SerialUART.h"
|
||||||
|
#include "iostream/ArduinoStream.h"
|
||||||
|
|
||||||
|
// Serial output stream
|
||||||
|
ArduinoOutStream cout(Serial1);
|
||||||
|
|
||||||
|
using namespace FatFsNs;
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
/* This will not be called unless build_flags include "-D USE_DBG_PRINTF": */
|
||||||
|
// void put_out_debug_message(const char *s) {
|
||||||
|
// Serial1.write(s);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
// Check the FRESULT of a library call.
|
||||||
|
// (See http://elm-chan.org/fsw/ff/doc/rc.html.)
|
||||||
|
#define FAIL(s, fr) \
|
||||||
|
{ \
|
||||||
|
cout << __FILE__ << ":" << __LINE__ << ": " << s << ": " \
|
||||||
|
<< FRESULT_str(fr) << " (" << fr << ")" << endl; \
|
||||||
|
for (;;) __breakpoint(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CHK_FRESULT(const char* s, FRESULT fr) {
|
||||||
|
if (FR_OK != fr)
|
||||||
|
FAIL(s, fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASSERT(pred) \
|
||||||
|
{ \
|
||||||
|
if (!(pred)) { \
|
||||||
|
cout << __FILE__ << ":" << __LINE__ << ": " \
|
||||||
|
<< "Assertion failed: " << #pred << endl; \
|
||||||
|
for (;;) __breakpoint(); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
static bool print_heading(File& file) {
|
||||||
|
FRESULT fr = file.lseek(file.size());
|
||||||
|
CHK_FRESULT("lseek", fr);
|
||||||
|
if (0 == file.tell()) {
|
||||||
|
// Print header
|
||||||
|
if (file.printf("Date,Time,Voltage\n") < 0) {
|
||||||
|
cout << "printf error" << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool open_file(File& file) {
|
||||||
|
const time_t timer = time(NULL);
|
||||||
|
struct tm tmbuf;
|
||||||
|
localtime_r(&timer, &tmbuf);
|
||||||
|
char filename[64];
|
||||||
|
int n = snprintf(filename, sizeof filename, "/data");
|
||||||
|
ASSERT(0 < n && n < (int)sizeof filename);
|
||||||
|
FRESULT fr = Dir::mkdir(filename);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
FAIL("mkdir", fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// tm_year int years since 1900
|
||||||
|
// tm_mon int months since January 0-11
|
||||||
|
// tm_mday int day of the month 1-31
|
||||||
|
n += snprintf(filename + n, sizeof filename - n, "/%04d-%02d-%02d",
|
||||||
|
tmbuf.tm_year + 1900, tmbuf.tm_mon + 1, tmbuf.tm_mday);
|
||||||
|
ASSERT(0 < n && n < (int)sizeof filename);
|
||||||
|
fr = Dir::mkdir(filename);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
FAIL("mkdir", fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t nw = strftime(filename + n, sizeof filename - n, "/%H.csv", &tmbuf);
|
||||||
|
ASSERT(nw);
|
||||||
|
fr = file.open(filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
FAIL("open", fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!print_heading(file)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_logger() {
|
||||||
|
/* It's very inefficient to open and close the file for every record,
|
||||||
|
but you're less likely to lose data that way. But also see f_sync
|
||||||
|
(http://elm-chan.org/fsw/ff/doc/sync.html). */
|
||||||
|
|
||||||
|
FRESULT fr;
|
||||||
|
File file;
|
||||||
|
|
||||||
|
bool rc = open_file(file);
|
||||||
|
if (!rc) return false;
|
||||||
|
|
||||||
|
// Form date-time string
|
||||||
|
char buf[128];
|
||||||
|
const time_t secs = time(NULL);
|
||||||
|
struct tm tmbuf;
|
||||||
|
struct tm* ptm = localtime_r(&secs, &tmbuf);
|
||||||
|
size_t n = strftime(buf, sizeof buf, "%F,%T,", ptm);
|
||||||
|
ASSERT(n);
|
||||||
|
|
||||||
|
/* Assuming something analog is connected to A0 */
|
||||||
|
int sensorValue = analogRead(A0);
|
||||||
|
float voltage = 3.3f * sensorValue / 1024;
|
||||||
|
int nw = snprintf(buf + n, sizeof buf - n, "%.3f\n", (double)voltage);
|
||||||
|
// Notice that only when this returned value is non-negative and less than n,
|
||||||
|
// the string has been completely written.
|
||||||
|
ASSERT(0 < nw && nw < (int)sizeof buf);
|
||||||
|
n += nw;
|
||||||
|
cout << buf << "\r";
|
||||||
|
|
||||||
|
UINT bw;
|
||||||
|
fr = file.write(buf, n, &bw);
|
||||||
|
CHK_FRESULT("write", fr);
|
||||||
|
if (bw < n) {
|
||||||
|
cout << "Short write!" << endl;
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
fr = file.close();
|
||||||
|
CHK_FRESULT("close", fr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool logger_enabled;
|
||||||
|
static const uint32_t period = 1000;
|
||||||
|
static absolute_time_t next_log_time;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial1.begin(115200); // set up Serial library at 9600 bps
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
cout << "Hello, world!" << endl;
|
||||||
|
|
||||||
|
adc_init(); // Reading voltage on A0
|
||||||
|
|
||||||
|
time_init();
|
||||||
|
// You might want to ask the user for the time,
|
||||||
|
// but it is hardcoded here for simplicity:
|
||||||
|
datetime_t t = {
|
||||||
|
.year = 2023,
|
||||||
|
.month = 2,
|
||||||
|
.day = 10,
|
||||||
|
.dotw = 5, // 0 is Sunday, so 5 is Friday
|
||||||
|
.hour = 17,
|
||||||
|
.min = 5,
|
||||||
|
.sec = 0};
|
||||||
|
rtc_set_datetime(&t);
|
||||||
|
|
||||||
|
/* This example assumes the following wiring:
|
||||||
|
| GPIO | SPI1 | SD Card |
|
||||||
|
| ---- | -------- | ------- |
|
||||||
|
| GP8 | SPI1_RX | D0/DO |
|
||||||
|
| GP10 | SPI1_SCK | CLK |
|
||||||
|
| GP11 | SPI1_TX | CMD/DI |
|
||||||
|
| GP12 | | D3/CS |
|
||||||
|
| GP14 | | DET |
|
||||||
|
*/
|
||||||
|
// GPIO numbers, not Pico pin numbers!
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI object:
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi1, // RP2040 SPI component
|
||||||
|
.miso_gpio = 8, // GPIO number (not Pico pin number)
|
||||||
|
.mosi_gpio = 11,
|
||||||
|
.sck_gpio = 10,
|
||||||
|
.baud_rate = 12 * 1000 * 1000, // Actual frequency: 10416666
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI Interface object:
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 12, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card object:
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 14,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true};
|
||||||
|
|
||||||
|
FatFsNs::SdCard* SdCard_p(FatFsNs::FatFs::add_sd_card(&sd_card));
|
||||||
|
|
||||||
|
// The H/W config must be set up before this is called:
|
||||||
|
sd_init_driver();
|
||||||
|
|
||||||
|
FRESULT fr = SdCard_p->mount();
|
||||||
|
CHK_FRESULT("mount", fr);
|
||||||
|
|
||||||
|
next_log_time = delayed_by_ms(get_absolute_time(), period);
|
||||||
|
logger_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (absolute_time_diff_us(get_absolute_time(), next_log_time) < 0) {
|
||||||
|
FRESULT fr;
|
||||||
|
if (logger_enabled) {
|
||||||
|
if (!process_logger())
|
||||||
|
logger_enabled = false;
|
||||||
|
}
|
||||||
|
next_log_time = delayed_by_ms(next_log_time, period);
|
||||||
|
}
|
||||||
|
}
|
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/hw_debug/.gitignore
vendored
Normal file
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/hw_debug/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,27 @@
|
||||||
|
[env:pico]
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
build_flags =
|
||||||
|
-D USE_DBG_PRINTF # Debug output
|
||||||
|
-D PICO_USE_STACK_GUARDS=1
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
||||||
|
|
||||||
|
; evaluate C/C++ Preprocessor conditional syntax
|
||||||
|
; lib_ldf_mode = chain+
|
|
@ -0,0 +1,471 @@
|
||||||
|
/*----------------------------------------------------------------------/
|
||||||
|
/ Low level disk I/O module function checker /
|
||||||
|
/-----------------------------------------------------------------------/
|
||||||
|
/ WARNING: The data on the target drive will be lost!
|
||||||
|
*/
|
||||||
|
/* app4-IO_module_function_checker.c
|
||||||
|
Originally from [Compatibility Checker for Storage Device Control Module]
|
||||||
|
(http://elm-chan.org/fsw/ff/res/app4.c).
|
||||||
|
*/
|
||||||
|
/*----------------------------------------------------------------------------/
|
||||||
|
/ FatFs - Generic FAT Filesystem Module Rx.xx /
|
||||||
|
/-----------------------------------------------------------------------------/
|
||||||
|
/
|
||||||
|
/ Copyright (C) 20xx, ChaN, all right reserved.
|
||||||
|
/
|
||||||
|
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||||
|
/ source and binary forms, with or without modification, are permitted provided
|
||||||
|
/ that the following condition is met:
|
||||||
|
/
|
||||||
|
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
/ this condition and the following disclaimer.
|
||||||
|
/
|
||||||
|
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||||
|
/ and any warranties related to this software are DISCLAIMED.
|
||||||
|
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||||
|
/ by use of this software.
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
Modifications: Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example runs a low level I/O test than can be helpful for debugging hardware.
|
||||||
|
It will destroy the format of the SD card!
|
||||||
|
*/
|
||||||
|
#include "FatFsSd_C.h"
|
||||||
|
#include "SerialUART.h"
|
||||||
|
|
||||||
|
#define printf Serial1.printf
|
||||||
|
#define puts Serial1.println
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
// This will not be called unless build_flags include "-D USE_DBG_PRINTF":
|
||||||
|
void put_out_debug_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
/*
|
||||||
|
This example assumes the following wiring for SD card 0:
|
||||||
|
| GPIO | Function | SD Card | SPI0 |
|
||||||
|
| ---- | -------------------------------- | ------- | -------- |
|
||||||
|
| GP2 | SCK | CLK | SPI0_SCK |
|
||||||
|
| GP3 | MOSI (COPI or Peripheral's SDI) | CMD/DI | SPI0_TX |
|
||||||
|
| GP4 | MISO (CIPO or Peripheral's SDO) | D0/DO | SPI0_RX |
|
||||||
|
| GP7 | SS (CS) | D3/CS | |
|
||||||
|
| GP9 | Card Detect | DET | |
|
||||||
|
|
||||||
|
This example assumes the following wiring for SD card 1:
|
||||||
|
| GPIO | SD Card |
|
||||||
|
| ---- | ------- |
|
||||||
|
| GP16 | CLK |
|
||||||
|
| GP17 | CMD |
|
||||||
|
| GP18 | D0 |
|
||||||
|
| GP19 | D1 |
|
||||||
|
| GP20 | D2 |
|
||||||
|
| GP21 | D3 |
|
||||||
|
| GP22 | DET |
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware Configuration of SPI object */
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi0, // RP2040 SPI component
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.baud_rate = 12 * 1000 * 1000 // Actual frequency: 10416666.
|
||||||
|
};
|
||||||
|
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7 // The SPI slave select GPIO for this SD card
|
||||||
|
};
|
||||||
|
|
||||||
|
static sd_sdio_if_t sdio_if = {
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.baud_rate = 15 * 1000 * 1000 // 15 MHz
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "objects"
|
||||||
|
static sd_card_t sd_cards[] = {
|
||||||
|
{ // sd_cards[0]
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[1]
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_if,
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
The following *get_num, *get_by_num functions are required by the library API.
|
||||||
|
They are how the library finds out about the configuration.
|
||||||
|
*/
|
||||||
|
extern "C" size_t sd_get_num() { return count_of(sd_cards); }
|
||||||
|
|
||||||
|
extern "C" sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (num <= sd_get_num()) {
|
||||||
|
return &sd_cards[num];
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial1.begin(115200); // set up Serial library at 9600 bps
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD pn( /* Pseudo random number generator */
|
||||||
|
DWORD pns /* !0:Initialize, 0:Read */
|
||||||
|
) {
|
||||||
|
static DWORD lfsr;
|
||||||
|
UINT n;
|
||||||
|
|
||||||
|
if (pns) {
|
||||||
|
lfsr = pns;
|
||||||
|
for (n = 0; n < 32; n++) pn(0);
|
||||||
|
}
|
||||||
|
if (lfsr & 1) {
|
||||||
|
lfsr >>= 1;
|
||||||
|
lfsr ^= 0x80200003;
|
||||||
|
} else {
|
||||||
|
lfsr >>= 1;
|
||||||
|
}
|
||||||
|
return lfsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_diskio(
|
||||||
|
BYTE pdrv, /* Physical drive number to be checked (all data on the drive will be lost) */
|
||||||
|
UINT ncyc, /* Number of test cycles */
|
||||||
|
DWORD *buff, /* Pointer to the working buffer */
|
||||||
|
UINT sz_buff /* Size of the working buffer in unit of byte */
|
||||||
|
) {
|
||||||
|
UINT n, cc, ns;
|
||||||
|
DWORD sz_drv, lba, lba2, sz_eblk, pns = 1;
|
||||||
|
WORD sz_sect;
|
||||||
|
BYTE *pbuff = (BYTE *)buff;
|
||||||
|
DSTATUS ds;
|
||||||
|
DRESULT dr;
|
||||||
|
|
||||||
|
printf("test_diskio(%u, %u, 0x%08X, 0x%08X)\n", pdrv, ncyc, (UINT)buff, sz_buff);
|
||||||
|
|
||||||
|
if (sz_buff < FF_MAX_SS + 8) {
|
||||||
|
printf("Insufficient work area to run the program.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cc = 1; cc <= ncyc; cc++) {
|
||||||
|
printf("**** Test cycle %u of %u start ****\n", cc, ncyc);
|
||||||
|
|
||||||
|
printf(" disk_initalize(%u)", pdrv);
|
||||||
|
ds = disk_initialize(pdrv);
|
||||||
|
if (ds & STA_NOINIT) {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("**** Get drive size ****\n");
|
||||||
|
printf(" disk_ioctl(%u, GET_SECTOR_COUNT, 0x%08X)", pdrv, (UINT)&sz_drv);
|
||||||
|
sz_drv = 0;
|
||||||
|
dr = disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_drv);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (sz_drv < 128) {
|
||||||
|
printf("Failed: Insufficient drive size to test.\n");
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
printf(" Number of sectors on the drive %u is %lu.\n", pdrv, sz_drv);
|
||||||
|
|
||||||
|
#if FF_MAX_SS != FF_MIN_SS
|
||||||
|
printf("**** Get sector size ****\n");
|
||||||
|
printf(" disk_ioctl(%u, GET_SECTOR_SIZE, 0x%X)", pdrv, (UINT)&sz_sect);
|
||||||
|
sz_sect = 0;
|
||||||
|
dr = disk_ioctl(pdrv, GET_SECTOR_SIZE, &sz_sect);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
printf(" Size of sector is %u bytes.\n", sz_sect);
|
||||||
|
#else
|
||||||
|
sz_sect = FF_MAX_SS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("**** Get block size ****\n");
|
||||||
|
printf(" disk_ioctl(%u, GET_BLOCK_SIZE, 0x%X)", pdrv, (UINT)&sz_eblk);
|
||||||
|
sz_eblk = 0;
|
||||||
|
dr = disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_eblk);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
}
|
||||||
|
if (dr == RES_OK || sz_eblk >= 2) {
|
||||||
|
printf(" Size of the erase block is %lu sectors.\n", sz_eblk);
|
||||||
|
} else {
|
||||||
|
printf(" Size of the erase block is unknown.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single sector write test */
|
||||||
|
printf("**** Single sector write test ****\n");
|
||||||
|
lba = 0;
|
||||||
|
for (n = 0, pn(pns); n < sz_sect; n++) pbuff[n] = (BYTE)pn(0);
|
||||||
|
printf(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_write(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
printf(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
memset(pbuff, 0, sz_sect);
|
||||||
|
printf(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_read(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); n < sz_sect && pbuff[n] == (BYTE)pn(0); n++)
|
||||||
|
;
|
||||||
|
if (n == sz_sect) {
|
||||||
|
printf(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
printf(" Read data differs from the data written.\n");
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
printf("**** Multiple sector write test ****\n");
|
||||||
|
lba = 5;
|
||||||
|
ns = sz_buff / sz_sect;
|
||||||
|
if (ns > 4) ns = 4;
|
||||||
|
if (ns > 1) {
|
||||||
|
for (n = 0, pn(pns); n < (UINT)(sz_sect * ns); n++) pbuff[n] = (BYTE)pn(0);
|
||||||
|
printf(" disk_write(%u, 0x%X, %lu, %u)", pdrv, (UINT)pbuff, lba, ns);
|
||||||
|
dr = disk_write(pdrv, pbuff, lba, ns);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
printf(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
memset(pbuff, 0, sz_sect * ns);
|
||||||
|
printf(" disk_read(%u, 0x%X, %lu, %u)", pdrv, (UINT)pbuff, lba, ns);
|
||||||
|
dr = disk_read(pdrv, pbuff, lba, ns);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); n < (UINT)(sz_sect * ns) && pbuff[n] == (BYTE)pn(0); n++)
|
||||||
|
;
|
||||||
|
if (n == (UINT)(sz_sect * ns)) {
|
||||||
|
printf(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
printf(" Read data differs from the data written.\n");
|
||||||
|
return 14;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf(" Test skipped.\n");
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
printf("**** Single sector write test (unaligned buffer address) ****\n");
|
||||||
|
lba = 5;
|
||||||
|
for (n = 0, pn(pns); n < sz_sect; n++) pbuff[n + 3] = (BYTE)pn(0);
|
||||||
|
printf(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff + 3), lba);
|
||||||
|
dr = disk_write(pdrv, pbuff + 3, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
printf(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
memset(pbuff + 5, 0, sz_sect);
|
||||||
|
printf(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff + 5), lba);
|
||||||
|
dr = disk_read(pdrv, pbuff + 5, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); n < sz_sect && pbuff[n + 5] == (BYTE)pn(0); n++)
|
||||||
|
;
|
||||||
|
if (n == sz_sect) {
|
||||||
|
printf(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
printf(" Read data differs from the data written.\n");
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
printf("**** 4GB barrier test ****\n");
|
||||||
|
if (sz_drv >= 128 + 0x80000000 / (sz_sect / 2)) {
|
||||||
|
lba = 6;
|
||||||
|
lba2 = lba + 0x80000000 / (sz_sect / 2);
|
||||||
|
for (n = 0, pn(pns); n < (UINT)(sz_sect * 2); n++) pbuff[n] = (BYTE)pn(0);
|
||||||
|
printf(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_write(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 19;
|
||||||
|
}
|
||||||
|
printf(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff + sz_sect), lba2);
|
||||||
|
dr = disk_write(pdrv, pbuff + sz_sect, lba2, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
printf(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
memset(pbuff, 0, sz_sect * 2);
|
||||||
|
printf(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_read(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
printf(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff + sz_sect), lba2);
|
||||||
|
dr = disk_read(pdrv, pbuff + sz_sect, lba2, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
printf(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
printf(" - failed.\n");
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); pbuff[n] == (BYTE)pn(0) && n < (UINT)(sz_sect * 2); n++)
|
||||||
|
;
|
||||||
|
if (n == (UINT)(sz_sect * 2)) {
|
||||||
|
printf(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
printf(" Read data differs from the data written.\n");
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf(" Test skipped.\n");
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
printf("**** Test cycle %u of %u completed ****\n\n", cc, ncyc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int main (int argc, char* argv[])
|
||||||
|
int lliot(size_t pnum) {
|
||||||
|
int rc;
|
||||||
|
DWORD buff[FF_MAX_SS]; /* Working buffer (4 sector in size) */
|
||||||
|
|
||||||
|
/* Check function/compatibility of the physical drive #0 */
|
||||||
|
rc = test_diskio(pnum, 3, buff, sizeof buff);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
printf("Sorry the function/compatibility test failed. (rc=%d)\nFatFs will not work with this disk driver.\n", rc);
|
||||||
|
} else {
|
||||||
|
printf("Congratulations! The disk driver works well.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
for (size_t i = 0; i < sd_get_num(); ++i) {
|
||||||
|
printf("\nTesting drive %lu\n", i);
|
||||||
|
lliot(i);
|
||||||
|
sleep_ms(10000);
|
||||||
|
}
|
||||||
|
}
|
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/one_SDIO/.gitignore
vendored
Normal file
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/one_SDIO/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,26 @@
|
||||||
|
[env:pico]
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
; platform = raspberrypi
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
build_flags =
|
||||||
|
-D USE_DBG_PRINTF
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
||||||
|
|
||||||
|
; evaluate C/C++ Preprocessor conditional syntax
|
||||||
|
; lib_ldf_mode = chain+
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Write "Hello, world!\n" to SD Card */
|
||||||
|
#include <string.h>
|
||||||
|
#include "FatFsSd_C.h"
|
||||||
|
//
|
||||||
|
#include "SerialUART.h"
|
||||||
|
|
||||||
|
#define printf Serial1.printf
|
||||||
|
#define puts Serial1.println
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
// This will not be called unless build_flags include "-D USE_DBG_PRINTF":
|
||||||
|
// void put_out_debug_message(const char *s) {
|
||||||
|
// Serial1.write(s);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example assumes the following wiring:
|
||||||
|
| GPIO | SD Card |
|
||||||
|
| ---- | ------- |
|
||||||
|
| GP16 | CLK |
|
||||||
|
| GP17 | CMD |
|
||||||
|
| GP18 | D0 |
|
||||||
|
| GP19 | D1 |
|
||||||
|
| GP20 | D2 |
|
||||||
|
| GP21 | D3 |
|
||||||
|
| GP22 | DET |
|
||||||
|
*/
|
||||||
|
static sd_sdio_if_t sdio_if = {
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.baud_rate = 15 * 1000 * 1000 // 15 MHz
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "objects"
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_if,
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
The following functions are required by the library API.
|
||||||
|
They are how the library finds out about the configuration.
|
||||||
|
*/
|
||||||
|
extern "C" size_t sd_get_num() { return 1; }
|
||||||
|
|
||||||
|
extern "C" sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (0 == num) {
|
||||||
|
return &sd_card;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the FRESULT of a library call.
|
||||||
|
// (See http://elm-chan.org/fsw/ff/doc/rc.html.)
|
||||||
|
#define CHK_FRESULT(s, fr) \
|
||||||
|
if (FR_OK != fr) { \
|
||||||
|
printf("%s:%d %s error: %s (%d)\n", \
|
||||||
|
__FILE__, __LINE__, s, FRESULT_str(fr), fr); \
|
||||||
|
for (;;) __breakpoint(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial1.begin(115200); // set up Serial library at 9600 bps
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
printf("\033[2J\033[H"); // Clear Screen
|
||||||
|
puts("Hello, world!");
|
||||||
|
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
sd_card_t *pSD = sd_get_by_num(0);
|
||||||
|
FRESULT fr = f_mount(&pSD->state.fatfs, "", 1);
|
||||||
|
CHK_FRESULT("f_mount", fr);
|
||||||
|
FIL fil;
|
||||||
|
const char* const filename = "filename.txt";
|
||||||
|
fr = f_open(&fil, filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
CHK_FRESULT("f_open", fr);
|
||||||
|
char const * const str = "Hello, world!\n";
|
||||||
|
if (f_printf(&fil, str) < strlen(str)) {
|
||||||
|
printf("f_printf failed\n");
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
fr = f_close(&fil);
|
||||||
|
CHK_FRESULT("f_close", fr);
|
||||||
|
fr = f_unmount("");
|
||||||
|
CHK_FRESULT("f_unmount", fr);
|
||||||
|
|
||||||
|
puts("Goodbye, world!");
|
||||||
|
}
|
||||||
|
void loop() {}
|
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,33 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:pico]
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
build_flags = "-Wno-psabi"
|
||||||
|
"-D USE_DBG_PRINTF"
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "FatFsSd.h"
|
||||||
|
//
|
||||||
|
#include "SerialUART.h"
|
||||||
|
#include "iostream/ArduinoStream.h"
|
||||||
|
|
||||||
|
// Serial output stream
|
||||||
|
ArduinoOutStream cout(Serial1);
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.printf("%s\r", s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.printf("%s\r", s);
|
||||||
|
}
|
||||||
|
// This will not be called unless build_flags include "-D USE_DBG_PRINTF":
|
||||||
|
void put_out_debug_message(const char *s) {
|
||||||
|
Serial1.printf("%s\r", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
// Check the FRESULT of a library call.
|
||||||
|
// (See http://elm-chan.org/fsw/ff/doc/rc.html.)
|
||||||
|
#define CHK_RESULT(s, fr) \
|
||||||
|
if (FR_OK != fr) { \
|
||||||
|
cout << __FILE__ << ":" << __LINE__ << ": " << s << ": " \
|
||||||
|
<< FRESULT_str(fr) << " (" << fr << ")" << endl; \
|
||||||
|
for (;;) __breakpoint(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial1.begin(115200); // set up Serial library
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
|
||||||
|
/* This example assumes the following wiring:
|
||||||
|
| GPIO | SPI1 | SD Card |
|
||||||
|
| ---- | -------- | ------- |
|
||||||
|
| GP8 | SPI1_RX | D0/DO |
|
||||||
|
| GP10 | SPI1_SCK | CLK |
|
||||||
|
| GP11 | SPI1_TX | CMD/DI |
|
||||||
|
| GP12 | | D3/CS |
|
||||||
|
| GP14 | | DET |
|
||||||
|
*/
|
||||||
|
|
||||||
|
// GPIO numbers, not Pico pin numbers!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hardware Configuration of SPI "objects"
|
||||||
|
Note: multiple SD cards can be driven by one SPI if they use different slave selects.
|
||||||
|
Note: None, either or both of the RP2040 SPI components can be used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI object:
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi1, // RP2040 SPI component
|
||||||
|
.miso_gpio = 8, // GPIO number (not Pico pin number)
|
||||||
|
.mosi_gpio = 11,
|
||||||
|
.sck_gpio = 10,
|
||||||
|
.baud_rate = 12 * 1000 * 1000, // Actual frequency: 10416666
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI Interface object:
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 12 // The SPI slave select GPIO for this SD card
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card object:
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 14,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// static FatFsNs::SdCard card = FatFsNs::SdCard(&sd_card);
|
||||||
|
FatFsNs::SdCard* card_p(FatFsNs::FatFs::add_sd_card(&sd_card));
|
||||||
|
|
||||||
|
// The H/W config must be set up before this is called:
|
||||||
|
sd_init_driver();
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
cout << "\033[2J\033[H"; // Clear Screen
|
||||||
|
cout << "Hello, world!" << endl;
|
||||||
|
FRESULT fr = card_p->mount();
|
||||||
|
CHK_RESULT("mount", fr);
|
||||||
|
FatFsNs::File file;
|
||||||
|
char const* const filename = "filename.txt";
|
||||||
|
fr = file.open(filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
CHK_RESULT("open", fr);
|
||||||
|
char const* const str = "Hello, world!\n";
|
||||||
|
if (file.printf(str) < strlen(str)) {
|
||||||
|
cout << "printf failed\n"
|
||||||
|
<< endl;
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
fr = file.close();
|
||||||
|
CHK_RESULT("close", fr);
|
||||||
|
fr = card_p->unmount();
|
||||||
|
CHK_RESULT("unmount", fr);
|
||||||
|
|
||||||
|
cout << "Goodbye, world!" << endl;
|
||||||
|
}
|
||||||
|
void loop() {}
|
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/one_SPI.C/.gitignore
vendored
Normal file
5
firmware/lib/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main/examples/PlatformIO/one_SPI.C/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,25 @@
|
||||||
|
[env:pico]
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
; platform = raspberrypi
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
; build_flags =
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
||||||
|
|
||||||
|
; evaluate C/C++ Preprocessor conditional syntax
|
||||||
|
; lib_ldf_mode = chain+
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FatFsSd_C.h"
|
||||||
|
//
|
||||||
|
#include "SerialUART.h"
|
||||||
|
|
||||||
|
#define printf Serial1.printf
|
||||||
|
#define puts Serial1.println
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
// This will not be called unless build_flags include "-D USE_DBG_PRINTF":
|
||||||
|
void put_out_debug_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
/* This example assumes the following wiring:
|
||||||
|
| GPIO | Function | SD Card | SPI0 |
|
||||||
|
| ---- | -------------------------------------- | ------- | -------- |
|
||||||
|
| GP2 | CLK/SCK | CLK | SPI0_SCK |
|
||||||
|
| GP3 | CMD/MOSI (COPI or Peripheral's SDI) | CMD/DI | SPI0_TX |
|
||||||
|
| GP4 | DATA 0/MISO (CIPO or Peripheral's SDO) | D0/DO | SPI0_RX |
|
||||||
|
| GP7 | DATA 3/CS | D3/CS | |
|
||||||
|
| GP9 | Card Detect | DET | |
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI "objects"
|
||||||
|
// Note: multiple SD cards can be driven by one SPI if they use different slave
|
||||||
|
// selects.
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi0, // RP2040 SPI component
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.baud_rate = 12 * 1000 * 1000 // Actual frequency: 10416666.
|
||||||
|
};
|
||||||
|
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7 // The SPI slave select GPIO for this SD card
|
||||||
|
} ;
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "objects"
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
|
||||||
|
};
|
||||||
|
extern "C" size_t sd_get_num() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
extern "C" sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (0 == num) {
|
||||||
|
return &sd_card;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial1.begin(115200); // set up Serial library at 9600 bps
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
time_init();
|
||||||
|
|
||||||
|
puts("Hello, world!");
|
||||||
|
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
sd_card_t *pSD = sd_get_by_num(0);
|
||||||
|
FRESULT fr = f_mount(&pSD->state.fatfs, "", 1);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
for (;;) __BKPT(1);
|
||||||
|
}
|
||||||
|
FIL fil;
|
||||||
|
const char* const filename = "filename.txt";
|
||||||
|
fr = f_open(&fil, filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
printf("f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
||||||
|
for (;;) __BKPT(2);
|
||||||
|
}
|
||||||
|
if (f_printf(&fil, "Hello, world!\n") < 0) {
|
||||||
|
printf("f_printf failed\n");
|
||||||
|
for (;;) __BKPT(3);
|
||||||
|
}
|
||||||
|
fr = f_close(&fil);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
for (;;) __BKPT(4);
|
||||||
|
}
|
||||||
|
f_unmount("");
|
||||||
|
|
||||||
|
puts("Goodbye, world!");
|
||||||
|
}
|
||||||
|
void loop() {}
|
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,29 @@
|
||||||
|
[env:pico]
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
; build_unflags = -fno-exceptions
|
||||||
|
; build_flags = -fexceptions
|
||||||
|
|
||||||
|
build_flags = "-Wno-psabi"
|
||||||
|
; "-D USE_DBG_PRINTF"
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
||||||
|
|
||||||
|
; evaluate C/C++ Preprocessor conditional syntax
|
||||||
|
; lib_ldf_mode = chain+
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "FatFsSd.h"
|
||||||
|
#include "SerialUART.h"
|
||||||
|
#include "iostream/ArduinoStream.h"
|
||||||
|
|
||||||
|
// Serial output stream
|
||||||
|
ArduinoOutStream cout(Serial1);
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
// This will not be called unless build_flags include "-D USE_DBG_PRINTF":
|
||||||
|
void put_out_debug_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial1.begin(115200); // set up Serial library at 9600 bps
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
cout << "\033[2J\033[H"; // Clear Screen
|
||||||
|
cout << "Hello, world!" << endl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example assumes the following wiring for SD card 0:
|
||||||
|
| GPIO | Function | SD Card | SPI0 |
|
||||||
|
| ---- | -------------------------------- | ------- | -------- |
|
||||||
|
| GP2 | SCK | CLK | SPI0_SCK |
|
||||||
|
| GP3 | MOSI (COPI or Peripheral's SDI) | CMD/DI | SPI0_TX |
|
||||||
|
| GP4 | MISO (CIPO or Peripheral's SDO) | D0/DO | SPI0_RX |
|
||||||
|
| GP7 | SS (CS) | D3/CS | |
|
||||||
|
| GP9 | Card Detect | DET | |
|
||||||
|
|
||||||
|
This example assumes the following wiring for SD card 1:
|
||||||
|
| GPIO | SD Card |
|
||||||
|
| ---- | ------- |
|
||||||
|
| GP16 | CLK |
|
||||||
|
| GP17 | CMD |
|
||||||
|
| GP18 | D0 |
|
||||||
|
| GP19 | D1 |
|
||||||
|
| GP20 | D2 |
|
||||||
|
| GP21 | D3 |
|
||||||
|
| GP22 | DET |
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware Configuration of SPI object */
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi0, // RP2040 SPI component
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.baud_rate = 12 * 1000 * 1000 // Actual frequency: 10416666.
|
||||||
|
};
|
||||||
|
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7 // The SPI slave select GPIO for this SD card
|
||||||
|
};
|
||||||
|
|
||||||
|
static sd_sdio_if_t sdio_if = {
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.baud_rate = 15 * 1000 * 1000 // 15 MHz
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "objects"
|
||||||
|
static sd_card_t sd_cards[] = {
|
||||||
|
{ // sd_cards[0]
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[1]
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_if,
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FatFsNs::FatFs::add_sd_card(&sd_cards[0]);
|
||||||
|
FatFsNs::FatFs::add_sd_card(&sd_cards[1]);
|
||||||
|
|
||||||
|
// The H/W config must be set up before this is called:
|
||||||
|
sd_init_driver();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
// Check the FRESULT of a library call.
|
||||||
|
// (See http://elm-chan.org/fsw/ff/doc/rc.html.)
|
||||||
|
#define CHK_FRESULT(s, fr) \
|
||||||
|
if (FR_OK != fr) { \
|
||||||
|
cout << __FILE__ << ":" << __LINE__ << ": " << s << ": " \
|
||||||
|
<< FRESULT_str(fr) << " (" << fr << ")" << endl; \
|
||||||
|
for (;;) __breakpoint(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
void local_ls(const char *dir) {
|
||||||
|
char cwdbuf[FF_LFN_BUF] = {0};
|
||||||
|
FRESULT fr; /* Return value */
|
||||||
|
char const *dir_str;
|
||||||
|
if (dir[0]) {
|
||||||
|
dir_str = dir;
|
||||||
|
} else {
|
||||||
|
fr = FatFsNs::Dir::getcwd(cwdbuf, sizeof cwdbuf);
|
||||||
|
CHK_FRESULT("getcwd", fr);
|
||||||
|
dir_str = cwdbuf;
|
||||||
|
}
|
||||||
|
cout << "Directory Listing: " << dir_str << endl;
|
||||||
|
FILINFO fno = {}; /* File information */
|
||||||
|
FatFsNs::Dir dirobj;
|
||||||
|
fr = dirobj.findfirst(&fno, dir_str, "*");
|
||||||
|
CHK_FRESULT("findfirst", fr);
|
||||||
|
while (fr == FR_OK && fno.fname[0]) { /* Repeat while an item is found */
|
||||||
|
/* Create a string that includes the file name, the file size and the
|
||||||
|
attributes string. */
|
||||||
|
const char *pcWritableFile = "writable file",
|
||||||
|
*pcReadOnlyFile = "read only file",
|
||||||
|
*pcDirectory = "directory";
|
||||||
|
const char *pcAttrib;
|
||||||
|
/* Point pcAttrib to a string that describes the file. */
|
||||||
|
if (fno.fattrib & AM_DIR) {
|
||||||
|
pcAttrib = pcDirectory;
|
||||||
|
} else if (fno.fattrib & AM_RDO) {
|
||||||
|
pcAttrib = pcReadOnlyFile;
|
||||||
|
} else {
|
||||||
|
pcAttrib = pcWritableFile;
|
||||||
|
}
|
||||||
|
/* Create a string that includes the file name, the file size and the
|
||||||
|
attributes string. */
|
||||||
|
cout << fno.fname << " [" << pcAttrib << "]"
|
||||||
|
<< "[size=" << fno.fsize << "]" << endl;
|
||||||
|
|
||||||
|
fr = dirobj.findnext(&fno); /* Search for next item */
|
||||||
|
}
|
||||||
|
dirobj.closedir();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test(FatFsNs::SdCard *SdCard_p) {
|
||||||
|
FRESULT fr;
|
||||||
|
|
||||||
|
cout << endl << "Testing drive " << SdCard_p->get_name() << endl;
|
||||||
|
|
||||||
|
fr = SdCard_p->mount();
|
||||||
|
CHK_FRESULT("mount", fr);
|
||||||
|
fr = FatFsNs::FatFs::chdrive(SdCard_p->get_name());
|
||||||
|
CHK_FRESULT("chdrive", fr);
|
||||||
|
local_ls(NULL);
|
||||||
|
|
||||||
|
FatFsNs::File file;
|
||||||
|
fr = file.open("filename.txt", FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
CHK_FRESULT("open", fr);
|
||||||
|
{
|
||||||
|
char const *const str = "Hello, world!\n";
|
||||||
|
if (file.printf(str) < strlen(str)) {
|
||||||
|
cout << "printf failed" << endl;
|
||||||
|
;
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fr = file.close();
|
||||||
|
CHK_FRESULT("close", fr);
|
||||||
|
|
||||||
|
local_ls("/");
|
||||||
|
|
||||||
|
fr = FatFsNs::Dir::mkdir("subdir");
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
cout << "mkdir error: " << FRESULT_str(fr) << "(" << fr << ")" << endl;
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
fr = FatFsNs::Dir::chdir("subdir");
|
||||||
|
CHK_FRESULT("chdir", fr);
|
||||||
|
fr = file.open("filename2.txt", FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
CHK_FRESULT("open", fr);
|
||||||
|
{
|
||||||
|
char const *const str = "Hello again\n";
|
||||||
|
UINT bw;
|
||||||
|
fr = file.write(str, strlen(str) + 1, &bw);
|
||||||
|
CHK_FRESULT("write", fr);
|
||||||
|
if (strlen(str) + 1 != bw) {
|
||||||
|
cout << "Short write!" << endl;
|
||||||
|
;
|
||||||
|
for (;;) __breakpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fr = file.close();
|
||||||
|
CHK_FRESULT("close", fr);
|
||||||
|
|
||||||
|
local_ls(NULL);
|
||||||
|
|
||||||
|
fr = FatFsNs::Dir::chdir("/");
|
||||||
|
CHK_FRESULT("chdir", fr);
|
||||||
|
|
||||||
|
local_ls(NULL);
|
||||||
|
|
||||||
|
fr = SdCard_p->unmount();
|
||||||
|
CHK_FRESULT("unmount", fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
for (size_t i = 0; i < FatFsNs::FatFs::SdCard_get_num(); ++i)
|
||||||
|
test(FatFsNs::FatFs::SdCard_get_by_num(i));
|
||||||
|
sleep_ms(1000);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -0,0 +1,22 @@
|
||||||
|
[env:pico]
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
board = pico
|
||||||
|
framework = arduino
|
||||||
|
board_build.core = earlephilhower
|
||||||
|
debug_tool = cmsis-dap
|
||||||
|
upload_protocol = cmsis-dap
|
||||||
|
monitor_port = COM8
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
; build_flags =
|
||||||
|
|
||||||
|
; Normal way:
|
||||||
|
; lib_deps =
|
||||||
|
; carlk3/no-OS-FatFS-SD-SPI-RPi-Pico@^1.0.2
|
||||||
|
|
||||||
|
; Get the latest straight from github:
|
||||||
|
; lib_deps = https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git
|
||||||
|
|
||||||
|
; Use local copy:
|
||||||
|
lib_deps = no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
lib_extra_dirs = ../../../..
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <vector>
|
||||||
|
//
|
||||||
|
#include "FatFsSd.h"
|
||||||
|
//
|
||||||
|
#include "SerialUART.h"
|
||||||
|
#include "iostream/ArduinoStream.h"
|
||||||
|
|
||||||
|
static const uint led_pin = PICO_DEFAULT_LED_PIN;
|
||||||
|
|
||||||
|
// Serial output stream
|
||||||
|
ArduinoOutStream cout(Serial1);
|
||||||
|
|
||||||
|
/* Implement library message callbacks */
|
||||||
|
void put_out_error_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
void put_out_info_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
// This will not be called unless build_flags include "-D USE_DBG_PRINTF":
|
||||||
|
void put_out_debug_message(const char *s) {
|
||||||
|
Serial1.write(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
void test(FatFsNs::SdCard* SdCard_p) {
|
||||||
|
|
||||||
|
cout << "Testing drive " << SdCard_p->get_name() << endl;
|
||||||
|
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
FRESULT fr = SdCard_p->mount();
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
cout << "mount error: " << FRESULT_str(fr) << " (" << fr << ")" << endl;
|
||||||
|
for (;;) __BKPT(1);
|
||||||
|
}
|
||||||
|
fr = FatFsNs::FatFs::chdrive(SdCard_p->get_name());
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
cout << "chdrive error: " << FRESULT_str(fr) << " (" << fr << ")" << endl;
|
||||||
|
for (;;) __BKPT(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
FatFsNs::File file;
|
||||||
|
const char *const filename = "filename.txt";
|
||||||
|
fr = file.open(filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
cout << "open(" << filename << ") error: " << FRESULT_str(fr) << " (" << fr << ")" << endl;
|
||||||
|
for (;;) __BKPT(3);
|
||||||
|
}
|
||||||
|
if (file.printf("Hello, world!\n") < 0) {
|
||||||
|
cout << "printf failed" << endl;
|
||||||
|
for (;;) __BKPT(4);
|
||||||
|
}
|
||||||
|
fr = file.close();
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
cout << "close error: " << FRESULT_str(fr) << " (" << fr << ")" << endl;
|
||||||
|
for (;;) __BKPT(5);
|
||||||
|
}
|
||||||
|
SdCard_p->unmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
/*
|
||||||
|
This example assumes the following wiring for SD card 0:
|
||||||
|
| GPIO | Function | SD Card | SPI0 |
|
||||||
|
| ---- | -------------------------------- | ------- | -------- |
|
||||||
|
| GP2 | SCK | CLK | SPI0_SCK |
|
||||||
|
| GP3 | MOSI (COPI or Peripheral's SDI) | CMD/DI | SPI0_TX |
|
||||||
|
| GP4 | MISO (CIPO or Peripheral's SDO) | D0/DO | SPI0_RX |
|
||||||
|
| GP7 | SS (CS) | D3/CS | |
|
||||||
|
| GP9 | Card Detect | DET | |
|
||||||
|
|
||||||
|
This example assumes the following wiring for SD card 1:
|
||||||
|
| GPIO | SD Card |
|
||||||
|
| ---- | ------- |
|
||||||
|
| GP16 | CLK |
|
||||||
|
| GP17 | CMD |
|
||||||
|
| GP18 | D0 |
|
||||||
|
| GP19 | D1 |
|
||||||
|
| GP20 | D2 |
|
||||||
|
| GP21 | D3 |
|
||||||
|
| GP22 | DET |
|
||||||
|
*/
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial1.begin(115200); // set up Serial library at 9600 bps
|
||||||
|
while (!Serial1)
|
||||||
|
; // Serial is via USB; wait for enumeration
|
||||||
|
cout << "\033[2J\033[H"; // Clear Screen
|
||||||
|
cout << "Hello, world!" << endl;
|
||||||
|
|
||||||
|
gpio_init(led_pin);
|
||||||
|
gpio_set_dir(led_pin, GPIO_OUT);
|
||||||
|
|
||||||
|
time_init();
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI "object"
|
||||||
|
spi_t *spi_p = new spi_t();
|
||||||
|
assert(spi_p);
|
||||||
|
spi_p->hw_inst = spi0; // RP2040 SPI component
|
||||||
|
spi_p->sck_gpio = 2; // GPIO number (not Pico pin number)
|
||||||
|
spi_p->mosi_gpio = 3;
|
||||||
|
spi_p->miso_gpio = 4;
|
||||||
|
spi_p->set_drive_strength = true;
|
||||||
|
spi_p->mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA;
|
||||||
|
spi_p->sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA;
|
||||||
|
spi_p->DMA_IRQ_num = DMA_IRQ_0;
|
||||||
|
spi_p->use_exclusive_DMA_IRQ_handler = true;
|
||||||
|
spi_p->baud_rate = 12 * 1000 * 1000; // Actual frequency: 10416666
|
||||||
|
|
||||||
|
// Hardware Configurtion of the SPI Interface "object"
|
||||||
|
sd_spi_if_t *spi_if_p = new sd_spi_if_t();
|
||||||
|
assert(spi_if_p);
|
||||||
|
spi_if_p->spi = spi_p; // Pointer to the SPI driving this card
|
||||||
|
spi_if_p->ss_gpio = 7; // The SPI slave select GPIO for this SD card
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "object"
|
||||||
|
sd_card_t *sd_card_p = new sd_card_t();
|
||||||
|
assert(sd_card_p);
|
||||||
|
sd_card_p->type = SD_IF_SPI;
|
||||||
|
sd_card_p->spi_if_p = spi_if_p; // Pointer to the SPI interface driving this card
|
||||||
|
sd_card_p->use_card_detect = true;
|
||||||
|
sd_card_p->card_detect_gpio = 9;
|
||||||
|
sd_card_p->card_detected_true = 0; // What the GPIO read returns when a card is present
|
||||||
|
sd_card_p->card_detect_use_pull = true;
|
||||||
|
sd_card_p->card_detect_pull_hi = true;
|
||||||
|
|
||||||
|
FatFsNs::FatFs::add_sd_card(sd_card_p);
|
||||||
|
|
||||||
|
// Hardware Configurtion of the SDIO Interface "object"
|
||||||
|
sd_sdio_if_t *sd_sdio_if_p = new sd_sdio_if_t();
|
||||||
|
assert(sd_sdio_if_p);
|
||||||
|
sd_sdio_if_p->CMD_gpio = 17,
|
||||||
|
sd_sdio_if_p->D0_gpio = 18,
|
||||||
|
sd_sdio_if_p->SDIO_PIO = pio1,
|
||||||
|
sd_sdio_if_p->DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
sd_sdio_if_p->baud_rate = 15 * 1000 * 1000; // 15 MHz
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "object"
|
||||||
|
sd_card_p = new sd_card_t();
|
||||||
|
assert(sd_card_p);
|
||||||
|
sd_card_p->type = SD_IF_SDIO;
|
||||||
|
sd_card_p->sdio_if_p = sd_sdio_if_p;
|
||||||
|
sd_card_p->use_card_detect = true;
|
||||||
|
sd_card_p->card_detect_gpio = 22;
|
||||||
|
sd_card_p->card_detected_true = 0; // What the GPIO read returns when a card is present
|
||||||
|
sd_card_p->card_detect_use_pull = true;
|
||||||
|
sd_card_p->card_detect_pull_hi = true;
|
||||||
|
|
||||||
|
FatFsNs::FatFs::add_sd_card(sd_card_p);
|
||||||
|
|
||||||
|
// The H/W config must be set up before this is called:
|
||||||
|
sd_init_driver();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < FatFsNs::FatFs::SdCard_get_num(); ++i)
|
||||||
|
test(FatFsNs::FatFs::SdCard_get_by_num(i));
|
||||||
|
|
||||||
|
cout << "Goodbye, world!" << endl;
|
||||||
|
}
|
||||||
|
void loop() {
|
||||||
|
while (true) {
|
||||||
|
gpio_put(led_pin, 1);
|
||||||
|
sleep_ms(250);
|
||||||
|
gpio_put(led_pin, 0);
|
||||||
|
sleep_ms(250);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
# Pull in Pico SDK (must be before project)
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(command_line C CXX ASM)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# add_compile_options(-Os) # Optimize for size (place before pico_sdk_init)
|
||||||
|
|
||||||
|
# Initialise the Pico SDK
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_subdirectory(../../src build)
|
||||||
|
|
||||||
|
# Add executable. Default name is the project name, version 0.1
|
||||||
|
add_executable(command_line
|
||||||
|
config/hw_config.c
|
||||||
|
main.cpp
|
||||||
|
src/command.cpp
|
||||||
|
src/data_log_demo.c
|
||||||
|
tests/app4-IO_module_function_checker.c
|
||||||
|
tests/bench.c
|
||||||
|
tests/big_file_test.c
|
||||||
|
tests/CreateAndVerifyExampleFiles.c
|
||||||
|
tests/ff_stdio_tests_with_cwd.c
|
||||||
|
tests/simple.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf
|
||||||
|
target_compile_definitions(command_line PRIVATE
|
||||||
|
PICO_STACK_SIZE=0x1000
|
||||||
|
PICO_CORE1_STACK_SIZE=0x800
|
||||||
|
# PICO_HEAP_SIZE=0x20000
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(command_line PUBLIC
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wno-unused-parameter
|
||||||
|
-Wstack-usage=2048
|
||||||
|
-fanalyzer
|
||||||
|
)
|
||||||
|
|
||||||
|
# This program is useless without standard standard input and output.
|
||||||
|
add_compile_definitions(
|
||||||
|
USE_PRINTF
|
||||||
|
#USE_DBG_PRINTF
|
||||||
|
)
|
||||||
|
|
||||||
|
# Disable CRC checking for SPI-attached cards.
|
||||||
|
# add_compile_definitions(SD_CRC_ENABLED=0)
|
||||||
|
|
||||||
|
# Use Pico's LED to show drive activity.
|
||||||
|
# Ensure that PICO_DEFAULT_LED_PIN is set correctly.
|
||||||
|
# Note that Pico W uses GPIO 25 for SPI communication to the CYW43439.
|
||||||
|
# add_compile_definitions(USE_LED=1)
|
||||||
|
|
||||||
|
# target_compile_definitions(<TARGET> PRIVATE PARAM_ASSERTIONS_ENABLE_ALL=1)
|
||||||
|
add_compile_definitions(
|
||||||
|
PARAM_ASSERTIONS_ENABLE_ALL=1
|
||||||
|
PICO_USE_STACK_GUARDS=1
|
||||||
|
PICO_MALLOC_PANIC=1
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET command_line APPEND_STRING PROPERTY LINK_FLAGS
|
||||||
|
"-Wl,--print-memory-usage"
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_set_program_name(command_line "command_line")
|
||||||
|
pico_set_program_version(command_line "1.2.2")
|
||||||
|
|
||||||
|
pico_set_linker_script(command_line ${CMAKE_SOURCE_DIR}/linker/memmap.ld)
|
||||||
|
|
||||||
|
# See 4.1. Serial input and output on Raspberry Pi Pico in Getting started with Raspberry Pi Pico (https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf)
|
||||||
|
# and 2.7.1. Standard Input/Output (stdio) Support in Raspberry Pi Pico C/C++ SDK (https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf):
|
||||||
|
pico_enable_stdio_uart(command_line 1)
|
||||||
|
pico_enable_stdio_usb(command_line 1)
|
||||||
|
|
||||||
|
target_include_directories(command_line PUBLIC
|
||||||
|
include/
|
||||||
|
)
|
||||||
|
target_link_libraries(command_line
|
||||||
|
no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
hardware_clocks
|
||||||
|
hardware_adc
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(command_line)
|
|
@ -0,0 +1,229 @@
|
||||||
|
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
There should be one element of the spi[] array for each RP2040 hardware SPI used.
|
||||||
|
|
||||||
|
There should be one element of the spi_ifs[] array for each SPI interface object.
|
||||||
|
* Each element of spi_ifs[] must point to an spi_t instance with member "spi".
|
||||||
|
|
||||||
|
There should be one element of the sdio_ifs[] array for each SDIO interface object.
|
||||||
|
|
||||||
|
There should be one element of the sd_cards[] array for each SD card slot.
|
||||||
|
* Each element of sd_cards[] must point to its interface with spi_if_p or sdio_if_p.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware configuration for Pico SD Card Development Board
|
||||||
|
See https://oshwlab.com/carlk3/rp2040-sd-card-dev
|
||||||
|
|
||||||
|
See https://docs.google.com/spreadsheets/d/1BrzLWTyifongf_VQCc2IpJqXWtsrjmG7KnIbSBy-CPU/edit?usp=sharing,
|
||||||
|
tab "Dev Brd", for pin assignments assumed in this configuration file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
//
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI "objects"
|
||||||
|
// Note: multiple SD cards can be driven by one SPI if they use different slave
|
||||||
|
// selects (or "chip selects").
|
||||||
|
static spi_t spis[] = { // One for each RP2040 SPI component used
|
||||||
|
{ // spis[0]
|
||||||
|
.hw_inst = spi0, // RP2040 SPI component
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.no_miso_gpio_pull_up = true,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_0,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 10 // 12500000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
},
|
||||||
|
{ // spis[1]
|
||||||
|
.hw_inst = spi1, // RP2040 SPI component
|
||||||
|
.miso_gpio = 8, // GPIO number (not Pico pin number)
|
||||||
|
.sck_gpio = 10,
|
||||||
|
.mosi_gpio = 11,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.no_miso_gpio_pull_up = true,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_0,
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 12
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 10 // 12500000 Hz
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SPI Interfaces */
|
||||||
|
static sd_spi_if_t spi_ifs[] = {
|
||||||
|
{ // spi_ifs[0]
|
||||||
|
.spi = &spis[0], // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
|
||||||
|
},
|
||||||
|
{ // spi_ifs[1]
|
||||||
|
.spi = &spis[1], // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 12, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
|
||||||
|
},
|
||||||
|
{ // spi_ifs[2]
|
||||||
|
.spi = &spis[1], // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 13, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SDIO Interfaces */
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
static sd_sdio_if_t sdio_ifs[] = {
|
||||||
|
{ // sdio_ifs[0]
|
||||||
|
.CMD_gpio = 3,
|
||||||
|
.D0_gpio = 4,
|
||||||
|
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
},
|
||||||
|
{ // sdio_ifs[1]
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Hardware Configuration of the SD Card "objects"
|
||||||
|
These correspond to SD card sockets
|
||||||
|
*/
|
||||||
|
static sd_card_t sd_cards[] = { // One for each SD card
|
||||||
|
#ifdef SPI_SD0
|
||||||
|
{ // sd_cards[0]: Socket sd0
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_ifs[0], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
#else
|
||||||
|
{ // sd_cards[0]: Socket sd0
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[0], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
{ // sd_cards[1]: Socket sd1
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_ifs[1], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 14,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[2]: Socket sd2
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_ifs[2], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 15,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[3]: Socket sd3
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[1], // Pointer to the interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return count_of(sd_cards); }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
assert(num < sd_get_num());
|
||||||
|
if (num < sd_get_num()) {
|
||||||
|
return &sd_cards[num];
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,77 @@
|
||||||
|
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware configuration for Expansion Module Type A
|
||||||
|
See https://oshwlab.com/carlk3/rpi-pico-sd-card-expansion-module-1
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
/* SDIO Interface */
|
||||||
|
static sd_sdio_if_t sdio_if = {
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
.CMD_gpio = 3,
|
||||||
|
.D0_gpio = 4,
|
||||||
|
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card socket "object"
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_if,
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0 // What the GPIO read returns when a card is present.
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return 1; }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (0 == num)
|
||||||
|
return &sd_card;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,77 @@
|
||||||
|
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware configuration for Expansion Module Type A
|
||||||
|
See https://oshwlab.com/carlk3/rpi-pico-sd-card-expansion-module-1
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI "object"
|
||||||
|
static spi_t spi = { // One for each RP2040 SPI component used
|
||||||
|
.hw_inst = spi0, // SPI component
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA,
|
||||||
|
.no_miso_gpio_pull_up = true,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_0,
|
||||||
|
.use_exclusive_DMA_IRQ_handler = true,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SPI Interface */
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "object"
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is present.
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return 1; }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (0 == num) {
|
||||||
|
return &sd_card;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,229 @@
|
||||||
|
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
There should be one element of the spi[] array for each RP2040 hardware SPI used.
|
||||||
|
|
||||||
|
There should be one element of the spi_ifs[] array for each SPI interface object.
|
||||||
|
* Each element of spi_ifs[] must point to an spi_t instance with member "spi".
|
||||||
|
|
||||||
|
There should be one element of the sdio_ifs[] array for each SDIO interface object.
|
||||||
|
|
||||||
|
There should be one element of the sd_cards[] array for each SD card slot.
|
||||||
|
* Each element of sd_cards[] must point to its interface with spi_if_p or sdio_if_p.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware configuration for Pico SD Card Development Board
|
||||||
|
See https://oshwlab.com/carlk3/rp2040-sd-card-dev
|
||||||
|
|
||||||
|
See https://docs.google.com/spreadsheets/d/1BrzLWTyifongf_VQCc2IpJqXWtsrjmG7KnIbSBy-CPU/edit?usp=sharing,
|
||||||
|
tab "Dev Brd", for pin assignments assumed in this configuration file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
//
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI "objects"
|
||||||
|
// Note: multiple SD cards can be driven by one SPI if they use different slave
|
||||||
|
// selects (or "chip selects").
|
||||||
|
static spi_t spis[] = { // One for each RP2040 SPI component used
|
||||||
|
{ // spis[0]
|
||||||
|
.hw_inst = spi0, // RP2040 SPI component
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.no_miso_gpio_pull_up = true,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_0,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 10 // 12500000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
},
|
||||||
|
{ // spis[1]
|
||||||
|
.hw_inst = spi1, // RP2040 SPI component
|
||||||
|
.miso_gpio = 8, // GPIO number (not Pico pin number)
|
||||||
|
.sck_gpio = 10,
|
||||||
|
.mosi_gpio = 11,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.no_miso_gpio_pull_up = true,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_0,
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 12
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 10 // 12500000 Hz
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SPI Interfaces */
|
||||||
|
static sd_spi_if_t spi_ifs[] = {
|
||||||
|
{ // spi_ifs[0]
|
||||||
|
.spi = &spis[0], // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
|
||||||
|
},
|
||||||
|
{ // spi_ifs[1]
|
||||||
|
.spi = &spis[1], // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 12, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
|
||||||
|
},
|
||||||
|
{ // spi_ifs[2]
|
||||||
|
.spi = &spis[1], // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 13, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SDIO Interfaces */
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
static sd_sdio_if_t sdio_ifs[] = {
|
||||||
|
{ // sdio_ifs[0]
|
||||||
|
.CMD_gpio = 3,
|
||||||
|
.D0_gpio = 4,
|
||||||
|
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
},
|
||||||
|
{ // sdio_ifs[1]
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Hardware Configuration of the SD Card "objects"
|
||||||
|
These correspond to SD card sockets
|
||||||
|
*/
|
||||||
|
static sd_card_t sd_cards[] = { // One for each SD card
|
||||||
|
#ifdef SPI_SD0
|
||||||
|
{ // sd_cards[0]: Socket sd0
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_ifs[0], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
#else
|
||||||
|
{ // sd_cards[0]: Socket sd0
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[0], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
{ // sd_cards[1]: Socket sd1
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_ifs[1], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 14,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[2]: Socket sd2
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_ifs[2], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 15,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[3]: Socket sd3
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[1], // Pointer to the interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return count_of(sd_cards); }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
assert(num < sd_get_num());
|
||||||
|
if (num < sd_get_num()) {
|
||||||
|
return &sd_cards[num];
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
// Hardware Configuration of SPI "objects"
|
||||||
|
// Note: multiple SD cards can be driven by one SPI if they use different slave
|
||||||
|
// selects.
|
||||||
|
static spi_t spi = { // One for each RP2040 SPI component used
|
||||||
|
.hw_inst = spi1, // SPI component
|
||||||
|
.miso_gpio = 12, // GPIO number (not pin number)
|
||||||
|
.mosi_gpio = 11,
|
||||||
|
.sck_gpio = 10,
|
||||||
|
.baud_rate = 12500 * 1000,
|
||||||
|
//.baud_rate = 25 * 1000 * 1000, // Actual frequency: 20833333.
|
||||||
|
};
|
||||||
|
/* SPI Interfaces */
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 15 // The SPI slave select GPIO for this SD card
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware Configuration of the SD Card "objects"
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return 1; }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (0 == num) {
|
||||||
|
return &sd_card;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,112 @@
|
||||||
|
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
There should be one element of the spi[] array for each RP2040 hardware SPI used.
|
||||||
|
|
||||||
|
There should be one element of the spi_ifs[] array for each SPI interface object.
|
||||||
|
* Each element of spi_ifs[] must point to an spi_t instance with member "spi".
|
||||||
|
|
||||||
|
There should be one element of the sdio_ifs[] array for each SDIO interface object.
|
||||||
|
|
||||||
|
There should be one element of the sd_cards[] array for each SD card slot.
|
||||||
|
* Each element of sd_cards[] must point to its interface with spi_if_p or sdio_if_p.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware configuration for Pico SD Card Development Board
|
||||||
|
See https://oshwlab.com/carlk3/rp2040-sd-card-dev
|
||||||
|
|
||||||
|
See https://docs.google.com/spreadsheets/d/1BrzLWTyifongf_VQCc2IpJqXWtsrjmG7KnIbSBy-CPU/edit?usp=sharing,
|
||||||
|
tab "Monster", for pin assignments assumed in this configuration file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
#include "hw_config.h"
|
||||||
|
#include "my_debug.h"
|
||||||
|
|
||||||
|
/* SDIO Interfaces */
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
static sd_sdio_if_t sdio_ifs[] = {
|
||||||
|
{ // sdio_ifs[0]
|
||||||
|
.CMD_gpio = 3,
|
||||||
|
.D0_gpio = 4,
|
||||||
|
.SDIO_PIO = pio0,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_0,
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 5 // 25 MHz
|
||||||
|
},
|
||||||
|
{ // sdio_ifs[1]
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 5 // 25 MHz
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Hardware Configuration of the SD Card "objects"
|
||||||
|
These correspond to SD card sockets
|
||||||
|
*/
|
||||||
|
static sd_card_t sd_cards[] = { // One for each SD card
|
||||||
|
{ // sd_cards[0]
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[0], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[1]
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[1],
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 1, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return count_of(sd_cards); }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
myASSERT(num < sd_get_num());
|
||||||
|
if (num < sd_get_num()) {
|
||||||
|
return &sd_cards[num];
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
/* Hardware Configuration of SPI "object" */
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi1, // SPI component
|
||||||
|
.miso_gpio = 12, // GPIO number (not pin number)
|
||||||
|
.mosi_gpio = 15,
|
||||||
|
.sck_gpio = 14,
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA,
|
||||||
|
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA,
|
||||||
|
|
||||||
|
.baud_rate = 25 * 1000 * 1000, // Actual frequency: 20833333.
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SPI Interface */
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 9, // The SPI slave select GPIO for this SD card
|
||||||
|
.set_drive_strength = true,
|
||||||
|
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Hardware Configuration of the SD Card "objects" */
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if, // Pointer to the SPI interface driving this card
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return 1; }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (num < sd_get_num()) {
|
||||||
|
return &sd_card;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern bool logger_enabled;
|
||||||
|
extern const uint32_t period;
|
||||||
|
extern absolute_time_t next_log_time;
|
||||||
|
|
||||||
|
void process_stdio(int cRxedChar);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
int lliot(size_t pnum);
|
||||||
|
void ls(const char *dir);
|
||||||
|
void simple();
|
||||||
|
void bench(char const* logdrv);
|
||||||
|
void big_file_test(const char *const pathname, size_t size,
|
||||||
|
uint32_t seed);
|
||||||
|
void vCreateAndVerifyExampleFiles(const char *pcMountPath);
|
||||||
|
void vStdioWithCWDTest(const char *pcMountPath);
|
||||||
|
bool process_logger();
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,248 @@
|
||||||
|
/* Based on GCC ARM embedded samples.
|
||||||
|
Defines the following symbols for use by code:
|
||||||
|
__exidx_start
|
||||||
|
__exidx_end
|
||||||
|
__etext
|
||||||
|
__data_start__
|
||||||
|
__preinit_array_start
|
||||||
|
__preinit_array_end
|
||||||
|
__init_array_start
|
||||||
|
__init_array_end
|
||||||
|
__fini_array_start
|
||||||
|
__fini_array_end
|
||||||
|
__data_end__
|
||||||
|
__bss_start__
|
||||||
|
__bss_end__
|
||||||
|
__end__
|
||||||
|
end
|
||||||
|
__HeapLimit
|
||||||
|
__StackLimit
|
||||||
|
__StackTop
|
||||||
|
__stack (== StackTop)
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
|
||||||
|
/* Physical RAM is ORIGIN = 0x20000000, LENGTH = 0x40000 (256k) */
|
||||||
|
SCRATCH_X(rwx) : ORIGIN = 0x20000000, LENGTH = 0x1000
|
||||||
|
SCRATCH_Y(rwx) : ORIGIN = 0x20001000, LENGTH = 0x1000
|
||||||
|
RAM(rwx) : ORIGIN = 0x20000000 + 0x2000, LENGTH = 0x40000 - 0x2000
|
||||||
|
/* Last byte of physical RAM is at 0x2003FFFF */
|
||||||
|
}
|
||||||
|
|
||||||
|
ENTRY(_entry_point)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
|
||||||
|
and checksummed. It is usually built by the boot_stage2 target
|
||||||
|
in the Raspberry Pi Pico SDK
|
||||||
|
*/
|
||||||
|
|
||||||
|
.flash_begin : {
|
||||||
|
__flash_binary_start = .;
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
.boot2 : {
|
||||||
|
__boot2_start__ = .;
|
||||||
|
KEEP (*(.boot2))
|
||||||
|
__boot2_end__ = .;
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
ASSERT(__boot2_end__ - __boot2_start__ == 256,
|
||||||
|
"ERROR: Pico second stage bootloader must be 256 bytes in size")
|
||||||
|
|
||||||
|
/* The second stage will always enter the image at the start of .text.
|
||||||
|
The debugger will use the ELF entry point, which is the _entry_point
|
||||||
|
symbol if present, otherwise defaults to start of .text.
|
||||||
|
This can be used to transfer control back to the bootrom on debugger
|
||||||
|
launches only, to perform proper flash setup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
__logical_binary_start = .;
|
||||||
|
KEEP (*(.vectors))
|
||||||
|
KEEP (*(.binary_info_header))
|
||||||
|
__binary_info_header_end = .;
|
||||||
|
KEEP (*(.reset))
|
||||||
|
/* TODO revisit this now memset/memcpy/float in ROM */
|
||||||
|
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
|
||||||
|
* FLASH ... we will include any thing excluded here in .data below by default */
|
||||||
|
*(.init)
|
||||||
|
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
|
||||||
|
*(.fini)
|
||||||
|
/* Pull all c'tors into .text */
|
||||||
|
*crtbegin.o(.ctors)
|
||||||
|
*crtbegin?.o(.ctors)
|
||||||
|
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
|
||||||
|
*(SORT(.ctors.*))
|
||||||
|
*(.ctors)
|
||||||
|
/* Followed by destructors */
|
||||||
|
*crtbegin.o(.dtors)
|
||||||
|
*crtbegin?.o(.dtors)
|
||||||
|
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
|
||||||
|
*(SORT(.dtors.*))
|
||||||
|
*(.dtors)
|
||||||
|
|
||||||
|
*(.eh_frame*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
__exidx_start = .;
|
||||||
|
.ARM.exidx :
|
||||||
|
{
|
||||||
|
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||||
|
} > FLASH
|
||||||
|
__exidx_end = .;
|
||||||
|
|
||||||
|
/* Machine inspectable binary information */
|
||||||
|
. = ALIGN(4);
|
||||||
|
__binary_info_start = .;
|
||||||
|
.binary_info :
|
||||||
|
{
|
||||||
|
KEEP(*(.binary_info.keep.*))
|
||||||
|
*(.binary_info.*)
|
||||||
|
} > FLASH
|
||||||
|
__binary_info_end = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
|
||||||
|
.ram_vector_table (NOLOAD): {
|
||||||
|
*(.ram_vector_table)
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
__data_start__ = .;
|
||||||
|
*(vtable)
|
||||||
|
|
||||||
|
*(.time_critical*)
|
||||||
|
|
||||||
|
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
|
||||||
|
*(.text*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
|
||||||
|
*(.data*)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.after_data.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* preinit data */
|
||||||
|
PROVIDE_HIDDEN (__mutex_array_start = .);
|
||||||
|
KEEP(*(SORT(.mutex_array.*)))
|
||||||
|
KEEP(*(.mutex_array))
|
||||||
|
PROVIDE_HIDDEN (__mutex_array_end = .);
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* preinit data */
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
|
KEEP(*(SORT(.preinit_array.*)))
|
||||||
|
KEEP(*(.preinit_array))
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* init data */
|
||||||
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
|
KEEP(*(SORT(.init_array.*)))
|
||||||
|
KEEP(*(.init_array))
|
||||||
|
PROVIDE_HIDDEN (__init_array_end = .);
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* finit data */
|
||||||
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
*(SORT(.fini_array.*))
|
||||||
|
*(.fini_array)
|
||||||
|
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||||
|
|
||||||
|
*(.jcr)
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* All data end */
|
||||||
|
__data_end__ = .;
|
||||||
|
} > RAM AT> FLASH
|
||||||
|
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
|
||||||
|
__etext = LOADADDR(.data);
|
||||||
|
|
||||||
|
.uninitialized_data (NOLOAD): {
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.uninitialized_data*)
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
/* Start and end symbols must be word-aligned */
|
||||||
|
.scratch_x : {
|
||||||
|
__scratch_x_start__ = .;
|
||||||
|
*(.scratch_x.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__scratch_x_end__ = .;
|
||||||
|
} > SCRATCH_X AT > FLASH
|
||||||
|
__scratch_x_source__ = LOADADDR(.scratch_x);
|
||||||
|
|
||||||
|
.scratch_y : {
|
||||||
|
__scratch_y_start__ = .;
|
||||||
|
*(.scratch_y.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__scratch_y_end__ = .;
|
||||||
|
} > SCRATCH_Y AT > FLASH
|
||||||
|
__scratch_y_source__ = LOADADDR(.scratch_y);
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_start__ = .;
|
||||||
|
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_end__ = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.heap (NOLOAD):
|
||||||
|
{
|
||||||
|
__end__ = .;
|
||||||
|
end = __end__;
|
||||||
|
KEEP(*(.heap*))
|
||||||
|
__HeapLimit = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
/* .stack*_dummy section doesn't contains any symbols. It is only
|
||||||
|
* used for linker to calculate size of stack sections, and assign
|
||||||
|
* values to stack symbols later
|
||||||
|
*
|
||||||
|
* stack1 section may be empty/missing if platform_launch_core1 is not used */
|
||||||
|
|
||||||
|
.stack1_dummy (NOLOAD):
|
||||||
|
{
|
||||||
|
__StackOneBottom = .;
|
||||||
|
*(.stack1*)
|
||||||
|
__StackOneTop = .;
|
||||||
|
} > SCRATCH_Y /* Was SCRATCH_X */
|
||||||
|
|
||||||
|
.stack_dummy (NOLOAD):
|
||||||
|
{
|
||||||
|
__StackBottom = .;
|
||||||
|
KEEP(*(.stack*))
|
||||||
|
__StackTop = .;
|
||||||
|
} > SCRATCH_X /* Was SCRATCH_Y */
|
||||||
|
|
||||||
|
PROVIDE(__stack = __StackTop);
|
||||||
|
|
||||||
|
.flash_end : {
|
||||||
|
PROVIDE(__flash_binary_end = .);
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
/* stack limit is poorly named, but historically is maximum heap ptr */
|
||||||
|
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
|
||||||
|
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
|
||||||
|
/* todo assert on extra code */
|
||||||
|
}
|
|
@ -0,0 +1,251 @@
|
||||||
|
/* Based on GCC ARM embedded samples.
|
||||||
|
Defines the following symbols for use by code:
|
||||||
|
__exidx_start
|
||||||
|
__exidx_end
|
||||||
|
__etext
|
||||||
|
__data_start__
|
||||||
|
__preinit_array_start
|
||||||
|
__preinit_array_end
|
||||||
|
__init_array_start
|
||||||
|
__init_array_end
|
||||||
|
__fini_array_start
|
||||||
|
__fini_array_end
|
||||||
|
__data_end__
|
||||||
|
__bss_start__
|
||||||
|
__bss_end__
|
||||||
|
__end__
|
||||||
|
end
|
||||||
|
__HeapLimit
|
||||||
|
__StackLimit
|
||||||
|
__StackTop
|
||||||
|
__stack (== StackTop)
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
|
||||||
|
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
|
||||||
|
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
|
||||||
|
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
|
||||||
|
}
|
||||||
|
|
||||||
|
ENTRY(_entry_point)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
|
||||||
|
and checksummed. It is usually built by the boot_stage2 target
|
||||||
|
in the Raspberry Pi Pico SDK
|
||||||
|
*/
|
||||||
|
|
||||||
|
.flash_begin : {
|
||||||
|
__flash_binary_start = .;
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
.boot2 : {
|
||||||
|
__boot2_start__ = .;
|
||||||
|
KEEP (*(.boot2))
|
||||||
|
__boot2_end__ = .;
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
ASSERT(__boot2_end__ - __boot2_start__ == 256,
|
||||||
|
"ERROR: Pico second stage bootloader must be 256 bytes in size")
|
||||||
|
|
||||||
|
/* The second stage will always enter the image at the start of .text.
|
||||||
|
The debugger will use the ELF entry point, which is the _entry_point
|
||||||
|
symbol if present, otherwise defaults to start of .text.
|
||||||
|
This can be used to transfer control back to the bootrom on debugger
|
||||||
|
launches only, to perform proper flash setup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
__logical_binary_start = .;
|
||||||
|
KEEP (*(.vectors))
|
||||||
|
KEEP (*(.binary_info_header))
|
||||||
|
__binary_info_header_end = .;
|
||||||
|
KEEP (*(.reset))
|
||||||
|
/* TODO revisit this now memset/memcpy/float in ROM */
|
||||||
|
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
|
||||||
|
* FLASH ... we will include any thing excluded here in .data below by default */
|
||||||
|
*(.init)
|
||||||
|
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
|
||||||
|
*(.fini)
|
||||||
|
/* Pull all c'tors into .text */
|
||||||
|
*crtbegin.o(.ctors)
|
||||||
|
*crtbegin?.o(.ctors)
|
||||||
|
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
|
||||||
|
*(SORT(.ctors.*))
|
||||||
|
*(.ctors)
|
||||||
|
/* Followed by destructors */
|
||||||
|
*crtbegin.o(.dtors)
|
||||||
|
*crtbegin?.o(.dtors)
|
||||||
|
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
|
||||||
|
*(SORT(.dtors.*))
|
||||||
|
*(.dtors)
|
||||||
|
|
||||||
|
*(.eh_frame*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
__exidx_start = .;
|
||||||
|
.ARM.exidx :
|
||||||
|
{
|
||||||
|
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||||
|
} > FLASH
|
||||||
|
__exidx_end = .;
|
||||||
|
|
||||||
|
/* Machine inspectable binary information */
|
||||||
|
. = ALIGN(4);
|
||||||
|
__binary_info_start = .;
|
||||||
|
.binary_info :
|
||||||
|
{
|
||||||
|
KEEP(*(.binary_info.keep.*))
|
||||||
|
*(.binary_info.*)
|
||||||
|
} > FLASH
|
||||||
|
__binary_info_end = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
|
||||||
|
.ram_vector_table (NOLOAD): {
|
||||||
|
*(.ram_vector_table)
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
__data_start__ = .;
|
||||||
|
*(vtable)
|
||||||
|
|
||||||
|
*(.time_critical*)
|
||||||
|
|
||||||
|
/* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
|
||||||
|
*(.text*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
|
||||||
|
*(.data*)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.after_data.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* preinit data */
|
||||||
|
PROVIDE_HIDDEN (__mutex_array_start = .);
|
||||||
|
KEEP(*(SORT(.mutex_array.*)))
|
||||||
|
KEEP(*(.mutex_array))
|
||||||
|
PROVIDE_HIDDEN (__mutex_array_end = .);
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* preinit data */
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
|
KEEP(*(SORT(.preinit_array.*)))
|
||||||
|
KEEP(*(.preinit_array))
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* init data */
|
||||||
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
|
KEEP(*(SORT(.init_array.*)))
|
||||||
|
KEEP(*(.init_array))
|
||||||
|
PROVIDE_HIDDEN (__init_array_end = .);
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* finit data */
|
||||||
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
*(SORT(.fini_array.*))
|
||||||
|
*(.fini_array)
|
||||||
|
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||||
|
|
||||||
|
*(.jcr)
|
||||||
|
. = ALIGN(4);
|
||||||
|
/* All data end */
|
||||||
|
__data_end__ = .;
|
||||||
|
} > RAM AT> FLASH
|
||||||
|
/* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
|
||||||
|
__etext = LOADADDR(.data);
|
||||||
|
|
||||||
|
.uninitialized_data (NOLOAD): {
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.uninitialized_data*)
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
/* Start and end symbols must be word-aligned */
|
||||||
|
.scratch_x : {
|
||||||
|
__scratch_x_start__ = .;
|
||||||
|
*(.scratch_x.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__scratch_x_end__ = .;
|
||||||
|
} > SCRATCH_X AT > FLASH
|
||||||
|
__scratch_x_source__ = LOADADDR(.scratch_x);
|
||||||
|
|
||||||
|
.scratch_y : {
|
||||||
|
__scratch_y_start__ = .;
|
||||||
|
*(.scratch_y.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__scratch_y_end__ = .;
|
||||||
|
} > SCRATCH_Y AT > FLASH
|
||||||
|
__scratch_y_source__ = LOADADDR(.scratch_y);
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_start__ = .;
|
||||||
|
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_end__ = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.heap (NOLOAD):
|
||||||
|
{
|
||||||
|
__end__ = .;
|
||||||
|
end = __end__;
|
||||||
|
KEEP(*(.heap*))
|
||||||
|
__HeapLimit = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
/* .stack*_dummy section doesn't contains any symbols. It is only
|
||||||
|
* used for linker to calculate size of stack sections, and assign
|
||||||
|
* values to stack symbols later
|
||||||
|
*
|
||||||
|
* stack1 section may be empty/missing if platform_launch_core1 is not used */
|
||||||
|
|
||||||
|
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
|
||||||
|
* stack is not used then all of SCRATCH_X is free.
|
||||||
|
*/
|
||||||
|
.stack1_dummy (NOLOAD):
|
||||||
|
{
|
||||||
|
*(.stack1*)
|
||||||
|
} > SCRATCH_X
|
||||||
|
.stack_dummy (NOLOAD):
|
||||||
|
{
|
||||||
|
KEEP(*(.stack*))
|
||||||
|
} > SCRATCH_Y
|
||||||
|
|
||||||
|
.flash_end : {
|
||||||
|
PROVIDE(__flash_binary_end = .);
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
|
/* stack limit is poorly named, but historically is maximum heap ptr */
|
||||||
|
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
|
||||||
|
__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
|
||||||
|
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
|
||||||
|
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
|
||||||
|
PROVIDE(__stack = __StackTop);
|
||||||
|
|
||||||
|
/* Check if data + heap + stack exceeds RAM limit */
|
||||||
|
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
|
||||||
|
|
||||||
|
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
|
||||||
|
/* todo assert on extra code */
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
//
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
//
|
||||||
|
#include "command.h"
|
||||||
|
#include "crash.h"
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "hw_config.h"
|
||||||
|
#include "rtc.h"
|
||||||
|
#include "tests.h"
|
||||||
|
#include "sd_card.h"
|
||||||
|
//
|
||||||
|
#include "diskio.h" /* Declarations of disk functions */
|
||||||
|
|
||||||
|
#ifndef USE_PRINTF
|
||||||
|
#error This program is useless without standard input and output.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static volatile bool card_det_int_pend;
|
||||||
|
static volatile uint card_det_int_gpio;
|
||||||
|
|
||||||
|
static void process_card_detect_int() {
|
||||||
|
card_det_int_pend = false;
|
||||||
|
for (size_t i = 0; i < sd_get_num(); ++i) {
|
||||||
|
sd_card_t *sd_card_p = sd_get_by_num(i);
|
||||||
|
if (!sd_card_p)
|
||||||
|
continue;
|
||||||
|
if (sd_card_p->card_detect_gpio == card_det_int_gpio) {
|
||||||
|
if (sd_card_p->state.mounted) {
|
||||||
|
DBG_PRINTF("(Card Detect Interrupt: unmounting %s)\n", sd_get_drive_prefix(sd_card_p));
|
||||||
|
FRESULT fr = f_unmount(sd_get_drive_prefix(sd_card_p));
|
||||||
|
if (FR_OK == fr) {
|
||||||
|
sd_card_p->state.mounted = false;
|
||||||
|
} else {
|
||||||
|
printf("f_unmount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sd_card_p->state.m_Status |= STA_NOINIT; // in case medium is removed
|
||||||
|
sd_card_detect(sd_card_p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the card is physically removed, unmount the filesystem:
|
||||||
|
static void card_detect_callback(uint gpio, uint32_t events) {
|
||||||
|
// This is actually an interrupt service routine!
|
||||||
|
card_det_int_gpio = gpio;
|
||||||
|
card_det_int_pend = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
crash_handler_init();
|
||||||
|
stdio_init_all();
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 1); // specify that the stream should be unbuffered
|
||||||
|
time_init();
|
||||||
|
|
||||||
|
printf("\033[2J\033[H"); // Clear Screen
|
||||||
|
|
||||||
|
// Check fault capture from RAM:
|
||||||
|
crash_info_t const *const pCrashInfo = crash_handler_get_info();
|
||||||
|
if (pCrashInfo) {
|
||||||
|
printf("*** Fault Capture Analysis (RAM): ***\n");
|
||||||
|
int n = 0;
|
||||||
|
do {
|
||||||
|
char buf[256] = {0};
|
||||||
|
n = dump_crash_info(pCrashInfo, n, buf, sizeof(buf));
|
||||||
|
if (buf[0]) printf("\t%s", buf);
|
||||||
|
} while (n != 0);
|
||||||
|
}
|
||||||
|
printf("\n> ");
|
||||||
|
stdio_flush();
|
||||||
|
|
||||||
|
// Implicitly called by disk_initialize,
|
||||||
|
// but called here to set up the GPIOs
|
||||||
|
// before enabling the card detect interrupt:
|
||||||
|
sd_init_driver();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sd_get_num(); ++i) {
|
||||||
|
sd_card_t *sd_card_p = sd_get_by_num(i);
|
||||||
|
if (!sd_card_p)
|
||||||
|
continue;
|
||||||
|
if (sd_card_p->use_card_detect) {
|
||||||
|
// Set up an interrupt on Card Detect to detect removal of the card
|
||||||
|
// when it happens:
|
||||||
|
gpio_set_irq_enabled_with_callback(
|
||||||
|
sd_card_p->card_detect_gpio, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL,
|
||||||
|
true, &card_detect_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) { // Super Loop
|
||||||
|
if (logger_enabled &&
|
||||||
|
absolute_time_diff_us(get_absolute_time(), next_log_time) < 0) {
|
||||||
|
if (!process_logger()) logger_enabled = false;
|
||||||
|
next_log_time = delayed_by_ms(next_log_time, period);
|
||||||
|
}
|
||||||
|
if (card_det_int_pend)
|
||||||
|
process_card_detect_int();
|
||||||
|
int cRxedChar = getchar_timeout_us(0);
|
||||||
|
/* Get the character from terminal */
|
||||||
|
if (PICO_ERROR_TIMEOUT != cRxedChar)
|
||||||
|
process_stdio(cRxedChar);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,832 @@
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
//
|
||||||
|
#include "hardware/adc.h"
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
#include "hardware/rtc.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "RP2040.h"
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "crash.h"
|
||||||
|
#include "hw_config.h"
|
||||||
|
#include "my_debug.h"
|
||||||
|
#include "sd_card.h"
|
||||||
|
#include "tests.h"
|
||||||
|
//
|
||||||
|
#include "diskio.h" /* Declarations of disk functions */
|
||||||
|
//
|
||||||
|
#include "command.h"
|
||||||
|
|
||||||
|
static char *saveptr; // For strtok_r
|
||||||
|
static volatile bool die;
|
||||||
|
|
||||||
|
bool logger_enabled;
|
||||||
|
const uint32_t period = 1000;
|
||||||
|
absolute_time_t next_log_time;
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||||
|
#ifdef NDEBUG
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-variable"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void missing_argument_msg() {
|
||||||
|
printf("Missing argument\n");
|
||||||
|
}
|
||||||
|
static void extra_argument_msg(const char *s) {
|
||||||
|
printf("Unexpected argument: %s\n", s);
|
||||||
|
}
|
||||||
|
static bool expect_argc(const size_t argc, const char *argv[], const size_t expected) {
|
||||||
|
if (argc < expected) {
|
||||||
|
missing_argument_msg();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (argc > expected) {
|
||||||
|
extra_argument_msg(argv[expected]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const char *chk_dflt_log_drv(const size_t argc, const char *argv[]) {
|
||||||
|
if (argc > 1) {
|
||||||
|
extra_argument_msg(argv[1]);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!argc) {
|
||||||
|
if (1 == sd_get_num()) {
|
||||||
|
return sd_get_drive_prefix(sd_get_by_num(0));
|
||||||
|
} else {
|
||||||
|
printf("Missing argument: Specify logical drive\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return argv[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_setrtc(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 6)) return;
|
||||||
|
|
||||||
|
int date = atoi(argv[0]);
|
||||||
|
int month = atoi(argv[1]);
|
||||||
|
int year = atoi(argv[2]) + 2000;
|
||||||
|
int hour = atoi(argv[3]);
|
||||||
|
int min = atoi(argv[4]);
|
||||||
|
int sec = atoi(argv[5]);
|
||||||
|
|
||||||
|
datetime_t t = {.year = static_cast<int16_t>(year),
|
||||||
|
.month = static_cast<int8_t>(month),
|
||||||
|
.day = static_cast<int8_t>(date),
|
||||||
|
.dotw = 0, // 0 is Sunday, so 5 is Friday
|
||||||
|
.hour = static_cast<int8_t>(hour),
|
||||||
|
.min = static_cast<int8_t>(min),
|
||||||
|
.sec = static_cast<int8_t>(sec)};
|
||||||
|
rtc_set_datetime(&t);
|
||||||
|
}
|
||||||
|
static void run_date(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
char buf[128] = {0};
|
||||||
|
time_t epoch_secs = time(NULL);
|
||||||
|
struct tm *ptm = localtime(&epoch_secs);
|
||||||
|
size_t n = strftime(buf, sizeof(buf), "%c", ptm);
|
||||||
|
assert(n);
|
||||||
|
printf("%s\n", buf);
|
||||||
|
strftime(buf, sizeof(buf), "%j",
|
||||||
|
ptm); // The day of the year as a decimal number (range
|
||||||
|
// 001 to 366).
|
||||||
|
printf("Day of year: %s\n", buf);
|
||||||
|
}
|
||||||
|
static char const *fs_type_string(int fs_type) {
|
||||||
|
switch (fs_type) {
|
||||||
|
case FS_FAT12:
|
||||||
|
return "FAT12";
|
||||||
|
case FS_FAT16:
|
||||||
|
return "FAT16";
|
||||||
|
case FS_FAT32:
|
||||||
|
return "FAT32";
|
||||||
|
case FS_EXFAT:
|
||||||
|
return "EXFAT";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void run_info(const size_t argc, const char *argv[]) {
|
||||||
|
const char *arg = chk_dflt_log_drv(argc, argv);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
sd_card_t *sd_card_p = sd_get_by_drive_prefix(arg);
|
||||||
|
if (!sd_card_p) {
|
||||||
|
printf("Unknown logical drive id: \"%s\"\n", arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int ds = sd_card_p->init(sd_card_p);
|
||||||
|
if (STA_NODISK & ds || STA_NOINIT & ds) {
|
||||||
|
printf("SD card initialization failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Card IDendtification register. 128 buts wide.
|
||||||
|
cidDmp(sd_card_p, printf);
|
||||||
|
// Card-Specific Data register. 128 bits wide.
|
||||||
|
csdDmp(sd_card_p, printf);
|
||||||
|
|
||||||
|
// SD Status
|
||||||
|
size_t au_size_bytes;
|
||||||
|
bool ok = sd_allocation_unit(sd_card_p, &au_size_bytes);
|
||||||
|
if (ok)
|
||||||
|
printf("\nSD card Allocation Unit (AU_SIZE) or \"segment\": %zu bytes (%zu sectors)\n",
|
||||||
|
au_size_bytes, au_size_bytes / sd_block_size);
|
||||||
|
|
||||||
|
if (!sd_card_p->state.mounted) {
|
||||||
|
printf("Drive \"%s\" is not mounted\n", argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get volume information and free clusters of drive */
|
||||||
|
FATFS *fs_p = &sd_card_p->state.fatfs;
|
||||||
|
if (!fs_p) {
|
||||||
|
printf("Unknown logical drive id: \"%s\"\n", arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DWORD fre_clust, fre_sect, tot_sect;
|
||||||
|
FRESULT fr = f_getfree(arg, &fre_clust, &fs_p);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_getfree error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Get total sectors and free sectors */
|
||||||
|
tot_sect = (fs_p->n_fatent - 2) * fs_p->csize;
|
||||||
|
fre_sect = fre_clust * fs_p->csize;
|
||||||
|
/* Print the free space (assuming 512 bytes/sector) */
|
||||||
|
printf("\n%10lu KiB (%lu MiB) total drive space.\n%10lu KiB (%lu MiB) available.\n",
|
||||||
|
tot_sect / 2, tot_sect / 2 / 1024,
|
||||||
|
fre_sect / 2, fre_sect / 2 / 1024);
|
||||||
|
|
||||||
|
#if FF_USE_LABEL
|
||||||
|
// Report label:
|
||||||
|
TCHAR buf[34] = {};/* [OUT] Volume label */
|
||||||
|
DWORD vsn; /* [OUT] Volume serial number */
|
||||||
|
fr = f_getlabel(arg, buf, &vsn);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_getlabel error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
printf("\nVolume label: %s\nVolume serial number: %lu\n", buf, vsn);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Report format
|
||||||
|
printf("\nFilesystem type: %s\n", fs_type_string(fs_p->fs_type));
|
||||||
|
|
||||||
|
// Report Partition Starting Offset
|
||||||
|
// uint64_t offs = fs_p->volbase;
|
||||||
|
// printf("Partition Starting Offset: %llu sectors (%llu bytes)\n",
|
||||||
|
// offs, offs * sd_block_size);
|
||||||
|
printf("Volume base sector: %llu\n", fs_p->volbase);
|
||||||
|
printf("FAT base sector: %llu\n", fs_p->fatbase);
|
||||||
|
printf("Root directory base sector (FAT12/16) or cluster (FAT32/exFAT): %llu\n", fs_p->dirbase);
|
||||||
|
printf("Data base sector: %llu\n", fs_p->database);
|
||||||
|
|
||||||
|
// Report cluster size ("allocation unit")
|
||||||
|
printf("FAT Cluster size (\"allocation unit\"): %d sectors (%llu bytes)\n",
|
||||||
|
fs_p->csize,
|
||||||
|
(uint64_t)sd_card_p->state.fatfs.csize * FF_MAX_SS);
|
||||||
|
}
|
||||||
|
static void run_format(const size_t argc, const char *argv[]) {
|
||||||
|
const char *arg = chk_dflt_log_drv(argc, argv);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
sd_card_t *sd_card_p = sd_get_by_drive_prefix(arg);
|
||||||
|
if (!sd_card_p) {
|
||||||
|
printf("Unknown logical drive id: \"%s\"\n", arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int ds = sd_card_p->init(sd_card_p);
|
||||||
|
if (STA_NODISK & ds || STA_NOINIT & ds) {
|
||||||
|
printf("SD card initialization failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I haven't been able to find a way to obtain the layout produced
|
||||||
|
by the SD Association's "SD Memory Card Formatter"
|
||||||
|
(https://www.sdcard.org/downloads/formatter/).
|
||||||
|
|
||||||
|
SD Memory Card Formatter:
|
||||||
|
Volume base sector: 8192
|
||||||
|
FAT base sector: 8790
|
||||||
|
Root directory base sector (FAT12/16) or cluster (FAT32/exFAT): 2
|
||||||
|
Data base sector: 16384
|
||||||
|
FAT Cluster size ("allocation unit"): 64 sectors (32768 bytes)
|
||||||
|
|
||||||
|
f_mkfs:
|
||||||
|
Volume base sector: 63
|
||||||
|
FAT base sector: 594
|
||||||
|
Root directory base sector (FAT12/16) or cluster (FAT32/exFAT): 2
|
||||||
|
Data base sector: 8192
|
||||||
|
FAT Cluster size ("allocation unit"): 64 sectors (32768 bytes)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Attempt to align partition to SD card segment (AU) */
|
||||||
|
size_t au_size_bytes;
|
||||||
|
bool ok = sd_allocation_unit(sd_card_p, &au_size_bytes);
|
||||||
|
if (!ok || !au_size_bytes)
|
||||||
|
au_size_bytes = 4194304; // Default to 4 MiB
|
||||||
|
UINT n_align = au_size_bytes / sd_block_size;
|
||||||
|
|
||||||
|
MKFS_PARM opt = {
|
||||||
|
FM_ANY, /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
||||||
|
2, /* Number of FATs */
|
||||||
|
n_align, /* Data area alignment (sector) */
|
||||||
|
0, /* Number of root directory entries */
|
||||||
|
0 /* Cluster size (byte) */
|
||||||
|
};
|
||||||
|
/* Format the drive */
|
||||||
|
FRESULT fr = f_mkfs(arg, &opt, 0, FF_MAX_SS * 2);
|
||||||
|
if (FR_OK != fr) printf("f_mkfs error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
|
||||||
|
/* This only works if the drive is mounted: */
|
||||||
|
#if FF_USE_LABEL
|
||||||
|
TCHAR label[32];
|
||||||
|
snprintf(label, sizeof(label), "%sFatFS", arg);
|
||||||
|
fr = f_setlabel(label);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
static void run_mount(const size_t argc, const char *argv[]) {
|
||||||
|
const char *arg = chk_dflt_log_drv(argc, argv);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
sd_card_t *sd_card_p = sd_get_by_drive_prefix(arg);
|
||||||
|
if (!sd_card_p) {
|
||||||
|
printf("Unknown logical drive id: \"%s\"\n", arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FATFS *fs_p = &sd_card_p->state.fatfs;
|
||||||
|
FRESULT fr = f_mount(fs_p, arg, 1);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sd_card_p->state.mounted = true;
|
||||||
|
}
|
||||||
|
static void run_unmount(const size_t argc, const char *argv[]) {
|
||||||
|
const char *arg = chk_dflt_log_drv(argc, argv);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sd_card_t *sd_card_p = sd_get_by_drive_prefix(arg);
|
||||||
|
if (!sd_card_p) {
|
||||||
|
printf("Unknown logical drive id: \"%s\"\n", arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FRESULT fr = f_unmount(arg);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_unmount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sd_card_p->state.mounted = false;
|
||||||
|
sd_card_p->state.m_Status |= STA_NOINIT; // in case medium is removed
|
||||||
|
}
|
||||||
|
static void run_chdrive(const size_t argc, const char *argv[]) {
|
||||||
|
const char *arg = chk_dflt_log_drv(argc, argv);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FRESULT fr = f_chdrive(arg);
|
||||||
|
if (FR_OK != fr) printf("f_chdrive error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
static void run_cd(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 1)) return;
|
||||||
|
|
||||||
|
FRESULT fr = f_chdir(argv[0]);
|
||||||
|
if (FR_OK != fr) printf("f_chdir error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
static void run_mkdir(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 1)) return;
|
||||||
|
|
||||||
|
FRESULT fr = f_mkdir(argv[0]);
|
||||||
|
if (FR_OK != fr) printf("f_mkdir error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
static void run_ls(const size_t argc, const char *argv[]) {
|
||||||
|
if (argc > 1) {
|
||||||
|
extra_argument_msg(argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argc)
|
||||||
|
ls(argv[0]);
|
||||||
|
else
|
||||||
|
ls("");
|
||||||
|
}
|
||||||
|
static void run_pwd(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
char buf[512];
|
||||||
|
FRESULT fr = f_getcwd(buf, sizeof buf);
|
||||||
|
if (FR_OK != fr)
|
||||||
|
printf("f_getcwd error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
else
|
||||||
|
printf("%s", buf);
|
||||||
|
}
|
||||||
|
static void run_cat(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 1)) return;
|
||||||
|
|
||||||
|
FIL fil;
|
||||||
|
FRESULT fr = f_open(&fil, argv[0], FA_READ);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char buf[256];
|
||||||
|
while (f_gets(buf, sizeof buf, &fil)) {
|
||||||
|
printf("%s", buf);
|
||||||
|
}
|
||||||
|
fr = f_close(&fil);
|
||||||
|
if (FR_OK != fr) printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
static void run_cp(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 2)) return;
|
||||||
|
|
||||||
|
FIL fsrc, fdst; /* File objects */
|
||||||
|
FRESULT fr; /* FatFs function common result code */
|
||||||
|
UINT br, bw; /* File read/write count */
|
||||||
|
|
||||||
|
/* Open source file on the drive 1 */
|
||||||
|
fr = f_open(&fsrc, argv[0], FA_READ);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Create destination file on the drive 0 */
|
||||||
|
fr = f_open(&fdst, argv[1], FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&fsrc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Copy source to destination */
|
||||||
|
FSIZE_t buffer_sz = f_size(&fsrc);
|
||||||
|
if (buffer_sz > 32768)
|
||||||
|
buffer_sz = 32768;
|
||||||
|
/* File copy buffer */
|
||||||
|
BYTE *buffer = (BYTE *)malloc(buffer_sz);
|
||||||
|
if (!buffer) {
|
||||||
|
printf("malloc(%llu) failed\n", buffer_sz);
|
||||||
|
f_close(&fdst);
|
||||||
|
f_close(&fsrc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
fr = f_read(&fsrc, buffer, buffer_sz, &br); /* Read a chunk of data from the source file */
|
||||||
|
if (FR_OK != fr) printf("f_read error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
if (br == 0) break; /* error or eof */
|
||||||
|
fr = f_write(&fdst, buffer, br, &bw); /* Write it to the destination file */
|
||||||
|
if (FR_OK != fr) printf("f_write error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
if (bw < br) break; /* error or disk full */
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
/* Close open files */
|
||||||
|
fr = f_close(&fsrc);
|
||||||
|
if (FR_OK != fr) printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
fr = f_close(&fdst);
|
||||||
|
if (FR_OK != fr) printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
static void run_mv(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 2)) return;
|
||||||
|
|
||||||
|
FRESULT fr = f_rename(argv[0], argv[1]);
|
||||||
|
if (FR_OK != fr) printf("f_rename error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
static void run_lliot(const size_t argc, const char *argv[]) {
|
||||||
|
const char *arg = chk_dflt_log_drv(argc, argv);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t pnum = 0;
|
||||||
|
pnum = strtoul(arg, NULL, 0);
|
||||||
|
lliot(pnum);
|
||||||
|
}
|
||||||
|
static void run_big_file_test(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 3)) return;
|
||||||
|
|
||||||
|
const char *pcPathName = argv[0];
|
||||||
|
size_t size = strtoul(argv[1], 0, 0);
|
||||||
|
uint32_t seed = atoi(argv[2]);
|
||||||
|
big_file_test(pcPathName, size, seed);
|
||||||
|
}
|
||||||
|
static void del_node(const char *path) {
|
||||||
|
FILINFO fno;
|
||||||
|
char buff[256];
|
||||||
|
/* Directory to be deleted */
|
||||||
|
strlcpy(buff, path, sizeof(buff));
|
||||||
|
/* Delete the directory */
|
||||||
|
FRESULT fr = delete_node(buff, sizeof buff / sizeof buff[0], &fno);
|
||||||
|
/* Check the result */
|
||||||
|
if (fr) {
|
||||||
|
printf("Failed to delete the directory %s. ", path);
|
||||||
|
printf("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void run_del_node(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 1)) return;
|
||||||
|
del_node(argv[0]);
|
||||||
|
}
|
||||||
|
static void run_rm(const size_t argc, const char *argv[]) {
|
||||||
|
if (argc < 1) {
|
||||||
|
missing_argument_msg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (argc > 2) {
|
||||||
|
extra_argument_msg(argv[2]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (2 == argc) {
|
||||||
|
if (0 == strcmp("-r", argv[0])) {
|
||||||
|
del_node(argv[1]);
|
||||||
|
} else if (0 == strcmp("-d", argv[0])) {
|
||||||
|
FRESULT fr = f_unlink(argv[1]);
|
||||||
|
if (FR_OK != fr) printf("f_unlink error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF("Unknown option: %s\n", argv[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FRESULT fr = f_unlink(argv[0]);
|
||||||
|
if (FR_OK != fr) printf("f_unlink error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void run_simple(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
simple();
|
||||||
|
}
|
||||||
|
static void run_bench(const size_t argc, const char *argv[]) {
|
||||||
|
const char *arg = chk_dflt_log_drv(argc, argv);
|
||||||
|
if (!arg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bench(arg);
|
||||||
|
}
|
||||||
|
static void run_cdef(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
f_mkdir("/cdef"); // fake mountpoint
|
||||||
|
vCreateAndVerifyExampleFiles("/cdef");
|
||||||
|
}
|
||||||
|
static void run_swcwdt(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
vStdioWithCWDTest("/cdef");
|
||||||
|
}
|
||||||
|
static void run_loop_swcwdt(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
die = false;
|
||||||
|
do {
|
||||||
|
f_chdir("/");
|
||||||
|
del_node("/cdef");
|
||||||
|
f_mkdir("/cdef"); // fake mountpoint
|
||||||
|
vCreateAndVerifyExampleFiles("/cdef");
|
||||||
|
vStdioWithCWDTest("/cdef");
|
||||||
|
} while (!die);
|
||||||
|
}
|
||||||
|
static void run_start_logger(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
adc_init();
|
||||||
|
adc_set_temp_sensor_enabled(true);
|
||||||
|
logger_enabled = true;
|
||||||
|
next_log_time = delayed_by_ms(get_absolute_time(), period);
|
||||||
|
}
|
||||||
|
static void run_stop_logger(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
logger_enabled = false;
|
||||||
|
}
|
||||||
|
static void run_mem_stats(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
// extern ptrdiff_t __StackTop, __StackBottom, __StackOneTop, __StackOneBottom;
|
||||||
|
// printf("__StackTop - __StackBottom = %zu\n", __StackTop - __StackBottom);
|
||||||
|
// printf("__StackOneTop - __StackOneBottom = %zu\n", __StackOneTop - __StackOneBottom);
|
||||||
|
|
||||||
|
malloc_stats();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Derived from pico-examples/clocks/hello_48MHz/hello_48MHz.c */
|
||||||
|
static void run_measure_freqs(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);
|
||||||
|
uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);
|
||||||
|
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);
|
||||||
|
uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
|
||||||
|
uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);
|
||||||
|
uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);
|
||||||
|
uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);
|
||||||
|
uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);
|
||||||
|
|
||||||
|
printf("pll_sys = %dkHz\n", f_pll_sys);
|
||||||
|
printf("pll_usb = %dkHz\n", f_pll_usb);
|
||||||
|
printf("rosc = %dkHz\n", f_rosc);
|
||||||
|
printf("clk_sys = %dkHz\treported = %lukHz\n", f_clk_sys, clock_get_hz(clk_sys) / KHZ);
|
||||||
|
printf("clk_peri = %dkHz\treported = %lukHz\n", f_clk_peri, clock_get_hz(clk_peri) / KHZ);
|
||||||
|
printf("clk_usb = %dkHz\treported = %lukHz\n", f_clk_usb, clock_get_hz(clk_usb) / KHZ);
|
||||||
|
printf("clk_adc = %dkHz\treported = %lukHz\n", f_clk_adc, clock_get_hz(clk_adc) / KHZ);
|
||||||
|
printf("clk_rtc = %dkHz\treported = %lukHz\n", f_clk_rtc, clock_get_hz(clk_rtc) / KHZ);
|
||||||
|
|
||||||
|
// Can't measure clk_ref / xosc as it is the ref
|
||||||
|
}
|
||||||
|
static void run_set_sys_clock_48mhz(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
set_sys_clock_48mhz();
|
||||||
|
setup_default_uart();
|
||||||
|
}
|
||||||
|
static void run_set_sys_clock_khz(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 1)) return;
|
||||||
|
|
||||||
|
int khz = atoi(argv[0]);
|
||||||
|
|
||||||
|
bool configured = set_sys_clock_khz(khz, false);
|
||||||
|
if (!configured) {
|
||||||
|
printf("Not possible. Clock not configured.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
By default, when reconfiguring the system clock PLL settings after runtime initialization,
|
||||||
|
the peripheral clock is switched to the 48MHz USB clock to ensure continuity of peripheral operation.
|
||||||
|
There seems to be a problem with running the SPI 2.4 times faster than the system clock,
|
||||||
|
even at the same SPI baud rate.
|
||||||
|
Anyway, for now, reconfiguring the peripheral clock to the system clock at its new frequency works OK.
|
||||||
|
*/
|
||||||
|
bool ok = clock_configure(clk_peri,
|
||||||
|
0,
|
||||||
|
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
|
||||||
|
clock_get_hz(clk_sys),
|
||||||
|
clock_get_hz(clk_sys));
|
||||||
|
assert(ok);
|
||||||
|
|
||||||
|
setup_default_uart();
|
||||||
|
}
|
||||||
|
static void set(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 1)) return;
|
||||||
|
|
||||||
|
int gp = atoi(argv[0]);
|
||||||
|
|
||||||
|
gpio_init(gp);
|
||||||
|
gpio_set_dir(gp, GPIO_OUT);
|
||||||
|
gpio_put(gp, 1);
|
||||||
|
}
|
||||||
|
static void clr(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 1)) return;
|
||||||
|
|
||||||
|
int gp = atoi(argv[0]);
|
||||||
|
|
||||||
|
gpio_init(gp);
|
||||||
|
gpio_set_dir(gp, GPIO_OUT);
|
||||||
|
gpio_put(gp, 0);
|
||||||
|
}
|
||||||
|
static void run_test(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
extern bool my_test();
|
||||||
|
my_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_help(const size_t argc, const char *argv[]);
|
||||||
|
|
||||||
|
typedef void (*p_fn_t)(const size_t argc, const char *argv[]);
|
||||||
|
typedef struct {
|
||||||
|
char const *const command;
|
||||||
|
p_fn_t const function;
|
||||||
|
char const *const help;
|
||||||
|
} cmd_def_t;
|
||||||
|
|
||||||
|
static cmd_def_t cmds[] = {
|
||||||
|
{"setrtc", run_setrtc,
|
||||||
|
"setrtc <DD> <MM> <YY> <hh> <mm> <ss>:\n"
|
||||||
|
" Set Real Time Clock\n"
|
||||||
|
" Parameters: new date (DD MM YY) new time in 24-hour format "
|
||||||
|
"(hh mm ss)\n"
|
||||||
|
"\te.g.:setrtc 16 3 21 0 4 0"},
|
||||||
|
{"date", run_date, "date:\n Print current date and time"},
|
||||||
|
{"format", run_format,
|
||||||
|
"format [<drive#:>]:\n"
|
||||||
|
" Creates an FAT/exFAT volume on the logical drive.\n"
|
||||||
|
"\te.g.: format 0:"},
|
||||||
|
{"mount", run_mount,
|
||||||
|
"mount [<drive#:>]:\n"
|
||||||
|
" Register the work area of the volume\n"
|
||||||
|
"\te.g.: mount 0:"},
|
||||||
|
{"unmount", run_unmount,
|
||||||
|
"unmount <drive#:>:\n"
|
||||||
|
" Unregister the work area of the volume"},
|
||||||
|
{"chdrive", run_chdrive,
|
||||||
|
"chdrive <drive#:>:\n"
|
||||||
|
" Changes the current directory of the logical drive.\n"
|
||||||
|
" <path> Specifies the directory to be set as current directory.\n"
|
||||||
|
"\te.g.: chdrive 1:"},
|
||||||
|
{"info", run_info,
|
||||||
|
"info [<drive#:>]:\n"
|
||||||
|
" Print information about an SD card"},
|
||||||
|
{"cd", run_cd,
|
||||||
|
"cd <path>:\n"
|
||||||
|
" Changes the current directory of the logical drive.\n"
|
||||||
|
" <path> Specifies the directory to be set as current directory.\n"
|
||||||
|
"\te.g.: cd /dir1"},
|
||||||
|
{"mkdir", run_mkdir,
|
||||||
|
"mkdir <path>:\n"
|
||||||
|
" Make a new directory.\n"
|
||||||
|
" <path> Specifies the name of the directory to be created.\n"
|
||||||
|
"\te.g.: mkdir /dir1"},
|
||||||
|
// {"del_node", run_del_node,
|
||||||
|
// "del_node <path>:\n"
|
||||||
|
// " Remove directory and all of its contents.\n"
|
||||||
|
// " <path> Specifies the name of the directory to be deleted.\n"
|
||||||
|
// "\te.g.: del_node /dir1"},
|
||||||
|
{"rm", run_rm,
|
||||||
|
"rm [options] <pathname>:\n"
|
||||||
|
" Removes (deletes) a file or directory\n"
|
||||||
|
" <pathname> Specifies the path to the file or directory to be removed\n"
|
||||||
|
" Options:\n"
|
||||||
|
" -d Remove an empty directory\n"
|
||||||
|
" -r Recursively remove a directory and its contents"},
|
||||||
|
{"cp", run_cp,
|
||||||
|
"cp <source file> <dest file>:\n"
|
||||||
|
" Copies <source file> to <dest file>"},
|
||||||
|
{"mv", run_mv,
|
||||||
|
"mv <source file> <dest file>:\n"
|
||||||
|
" Moves (renames) <source file> to <dest file>"},
|
||||||
|
{"pwd", run_pwd,
|
||||||
|
"pwd:\n"
|
||||||
|
" Print Working Directory"},
|
||||||
|
{"ls", run_ls, "ls [pathname]:\n List directory"},
|
||||||
|
// {"dir", run_ls, "dir:\n List directory"},
|
||||||
|
{"cat", run_cat, "cat <filename>:\n Type file contents"},
|
||||||
|
{"simple", run_simple, "simple:\n Run simple FS tests"},
|
||||||
|
{"lliot", run_lliot,
|
||||||
|
"lliot <physical drive#>:\n !DESTRUCTIVE! Low Level I/O Driver Test\n"
|
||||||
|
"The SD card will need to be reformatted after this test.\n"
|
||||||
|
"\te.g.: lliot 1"},
|
||||||
|
{"bench", run_bench, "bench <drive#:>:\n A simple binary write/read benchmark"},
|
||||||
|
{"big_file_test", run_big_file_test,
|
||||||
|
"big_file_test <pathname> <size in MiB> <seed>:\n"
|
||||||
|
" Writes random data to file <pathname>.\n"
|
||||||
|
" Specify <size in MiB> in units of mebibytes (2^20, or 1024*1024 bytes)\n"
|
||||||
|
"\te.g.: big_file_test 0:/bf 1 1\n"
|
||||||
|
"\tor: big_file_test 1:big3G-3 3072 3"},
|
||||||
|
{"cdef", run_cdef,
|
||||||
|
"cdef:\n Create Disk and Example Files\n"
|
||||||
|
" Expects card to be already formatted and mounted"},
|
||||||
|
{"swcwdt", run_swcwdt,
|
||||||
|
"swcwdt:\n Stdio With CWD Test\n"
|
||||||
|
"Expects card to be already formatted and mounted.\n"
|
||||||
|
"Note: run cdef first!"},
|
||||||
|
{"loop_swcwdt", run_loop_swcwdt,
|
||||||
|
"loop_swcwdt:\n Run Create Disk and Example Files and Stdio With CWD "
|
||||||
|
"Test in a loop.\n"
|
||||||
|
"Expects card to be already formatted and mounted.\n"
|
||||||
|
"Note: Hit Enter key to quit."},
|
||||||
|
{"start_logger", run_start_logger,
|
||||||
|
"start_logger:\n"
|
||||||
|
" Start Data Log Demo"},
|
||||||
|
{"stop_logger", run_stop_logger,
|
||||||
|
"stop_logger:\n"
|
||||||
|
" Stop Data Log Demo"},
|
||||||
|
{"mem-stats", run_mem_stats,
|
||||||
|
"mem-stats:\n"
|
||||||
|
" Print memory statistics"},
|
||||||
|
// // Clocks testing:
|
||||||
|
// {"set_sys_clock_48mhz", run_set_sys_clock_48mhz,
|
||||||
|
// "set_sys_clock_48mhz:\n"
|
||||||
|
// " Set the system clock to 48MHz"},
|
||||||
|
// {"set_sys_clock_khz", run_set_sys_clock_khz,
|
||||||
|
// "set_sys_clock_khz <khz>:\n"
|
||||||
|
// " Set the system clock system clock frequency in khz."},
|
||||||
|
// {"measure_freqs", run_measure_freqs,
|
||||||
|
// "measure_freqs:\n"
|
||||||
|
// " Count the RP2040 clock frequencies and report."},
|
||||||
|
// {"clr", clr, "clr <gpio #>: clear a GPIO"},
|
||||||
|
// {"set", set, "set <gpio #>: set a GPIO"},
|
||||||
|
// {"test", run_test, "test:\n"
|
||||||
|
// " Development test"},
|
||||||
|
{"help", run_help,
|
||||||
|
"help:\n"
|
||||||
|
" Shows this command help."}
|
||||||
|
};
|
||||||
|
static void run_help(const size_t argc, const char *argv[]) {
|
||||||
|
if (!expect_argc(argc, argv, 0)) return;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count_of(cmds); ++i) {
|
||||||
|
printf("%s\n\n", cmds[i].help);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break command
|
||||||
|
static void chars_available_callback(void *ptr) {
|
||||||
|
(void)ptr;
|
||||||
|
int cRxedChar = getchar_timeout_us(0);
|
||||||
|
switch (cRxedChar) {
|
||||||
|
case 3: // Ctrl-C
|
||||||
|
SYSTEM_RESET();
|
||||||
|
break;
|
||||||
|
case 27: // Esc
|
||||||
|
__BKPT();
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
die = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_cmd(size_t cmd_sz, char *cmd) {
|
||||||
|
assert(cmd);
|
||||||
|
assert(cmd[0]);
|
||||||
|
char *cmdn = strtok_r(cmd, " ", &saveptr);
|
||||||
|
if (cmdn) {
|
||||||
|
assert(cmdn < cmd + cmd_sz);
|
||||||
|
|
||||||
|
/* Breaking with Unix tradition of argv[0] being command name,
|
||||||
|
argv[0] is first argument after command name */
|
||||||
|
|
||||||
|
size_t argc = 0;
|
||||||
|
const char *argv[10] = {0}; // Arbitrary limit of 10 arguments
|
||||||
|
const char *arg_p;
|
||||||
|
do {
|
||||||
|
arg_p = strtok_r(NULL, " ", &saveptr);
|
||||||
|
if (arg_p) {
|
||||||
|
assert(arg_p < cmd + cmd_sz);
|
||||||
|
if (argc >= count_of(argv)) {
|
||||||
|
extra_argument_msg(arg_p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
argv[argc++] = arg_p;
|
||||||
|
}
|
||||||
|
} while (arg_p);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < count_of(cmds); ++i) {
|
||||||
|
if (0 == strcmp(cmds[i].command, cmdn)) {
|
||||||
|
|
||||||
|
// get notified when there are input characters available
|
||||||
|
stdio_set_chars_available_callback(chars_available_callback, NULL);
|
||||||
|
// run the command
|
||||||
|
(*cmds[i].function)(argc, argv);
|
||||||
|
stdio_set_chars_available_callback(NULL, NULL);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count_of(cmds) == i) printf("Command \"%s\" not found\n", cmdn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_stdio(int cRxedChar) {
|
||||||
|
static char cmd[256];
|
||||||
|
static size_t ix;
|
||||||
|
|
||||||
|
if (!(0 < cRxedChar && cRxedChar <= 0x7F))
|
||||||
|
return; // Not dealing with multibyte characters
|
||||||
|
if (!isprint(cRxedChar) && !isspace(cRxedChar) && '\r' != cRxedChar &&
|
||||||
|
'\b' != cRxedChar && cRxedChar != 127)
|
||||||
|
return;
|
||||||
|
printf("%c", cRxedChar); // echo
|
||||||
|
stdio_flush();
|
||||||
|
if (cRxedChar == '\r') {
|
||||||
|
/* Just to space the output from the input. */
|
||||||
|
printf("%c", '\n');
|
||||||
|
stdio_flush();
|
||||||
|
|
||||||
|
if (!cmd[0]) { // Empty input
|
||||||
|
printf("> ");
|
||||||
|
stdio_flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the input string received prior to the newline. */
|
||||||
|
process_cmd(sizeof cmd, cmd);
|
||||||
|
|
||||||
|
/* Reset everything for next cmd */
|
||||||
|
ix = 0;
|
||||||
|
memset(cmd, 0, sizeof cmd);
|
||||||
|
printf("\n> ");
|
||||||
|
stdio_flush();
|
||||||
|
} else { // Not newline
|
||||||
|
if (cRxedChar == '\b' || cRxedChar == (char)127) {
|
||||||
|
/* Backspace was pressed. Erase the last character
|
||||||
|
in the string - if any. */
|
||||||
|
if (ix > 0) {
|
||||||
|
ix--;
|
||||||
|
cmd[ix] = '\0';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* A character was entered. Add it to the string
|
||||||
|
entered so far. When a \n is entered the complete
|
||||||
|
string will be passed to the command interpreter. */
|
||||||
|
if (ix < sizeof cmd - 1) {
|
||||||
|
cmd[ix] = cRxedChar;
|
||||||
|
ix++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/* data_log_demo.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
//
|
||||||
|
#include "hardware/adc.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
//
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "my_debug.h"
|
||||||
|
|
||||||
|
#define DEVICENAME "0:"
|
||||||
|
|
||||||
|
#define TRACE_PRINTF(fmt, args...)
|
||||||
|
//#define TRACE_PRINTF printf
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-variable"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool print_header(FIL *fp) {
|
||||||
|
TRACE_PRINTF("%s\n", __func__);
|
||||||
|
assert(fp);
|
||||||
|
FRESULT fr = f_lseek(fp, f_size(fp));
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_lseek error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (0 == f_tell(fp)) {
|
||||||
|
// Print header
|
||||||
|
if (f_printf(fp, "Date,Time,Temperature (°C)\n") < 0) {
|
||||||
|
printf("f_printf error\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool open_file(FIL *fp) {
|
||||||
|
TRACE_PRINTF("%s\n", __func__);
|
||||||
|
assert(fp);
|
||||||
|
const time_t timer = time(NULL);
|
||||||
|
struct tm tmbuf;
|
||||||
|
localtime_r(&timer, &tmbuf);
|
||||||
|
char filename[64];
|
||||||
|
int n = snprintf(filename, sizeof filename, "/data");
|
||||||
|
assert(0 < n && n < (int)sizeof filename);
|
||||||
|
FRESULT fr = f_mkdir(filename);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
printf("f_mkdir error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// tm_year int years since 1900
|
||||||
|
// tm_mon int months since January 0-11
|
||||||
|
// tm_mday int day of the month 1-31
|
||||||
|
n += snprintf(filename + n, sizeof filename - n, "/%04d-%02d-%02d",
|
||||||
|
tmbuf.tm_year + 1900, tmbuf.tm_mon + 1, tmbuf.tm_mday);
|
||||||
|
assert(0 < n && n < (int)sizeof filename);
|
||||||
|
fr = f_mkdir(filename);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
printf("f_mkdir error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t nw = strftime(filename + n, sizeof filename - n, "/%H.csv", &tmbuf);
|
||||||
|
assert(nw);
|
||||||
|
fr = f_open(fp, filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
printf("f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!print_header(fp)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_logger() {
|
||||||
|
TRACE_PRINTF("%s\n", __func__);
|
||||||
|
/* It's very inefficient to open and close the file for every record,
|
||||||
|
but you're less likely to lose data that way. But also see f_sync
|
||||||
|
(http://elm-chan.org/fsw/ff/doc/sync.html). */
|
||||||
|
FIL fil;
|
||||||
|
bool rc = open_file(&fil);
|
||||||
|
if (!rc) return false;
|
||||||
|
|
||||||
|
// Form date-time string
|
||||||
|
char buf[128];
|
||||||
|
const time_t secs = time(NULL);
|
||||||
|
struct tm tmbuf;
|
||||||
|
struct tm *ptm = localtime_r(&secs, &tmbuf);
|
||||||
|
size_t n = strftime(buf, sizeof buf, "%F,%T,", ptm);
|
||||||
|
assert(n);
|
||||||
|
|
||||||
|
// The temperature sensor is on input 4:
|
||||||
|
adc_select_input(4);
|
||||||
|
uint16_t result = adc_read();
|
||||||
|
// 12-bit conversion, assume max value == ADC_VREF == 3.3 V
|
||||||
|
const float conversion_factor = 3.3f / (1 << 12);
|
||||||
|
float voltage = conversion_factor * result;
|
||||||
|
TRACE_PRINTF("Raw value: 0x%03x, voltage: %f V\n", result, (double)voltage);
|
||||||
|
|
||||||
|
// Temperature sensor values can be approximated in centigrade as:
|
||||||
|
// T = 27 - (ADC_Voltage - 0.706)/0.001721
|
||||||
|
float Tc = 27.0f - (voltage - 0.706f) / 0.001721f;
|
||||||
|
TRACE_PRINTF("Temperature: %.1f °C\n", (double)Tc);
|
||||||
|
int nw = snprintf(buf + n, sizeof buf - n, "%.3g\n", (double)Tc);
|
||||||
|
assert(0 < nw && nw < (int)sizeof buf);
|
||||||
|
|
||||||
|
if (f_printf(&fil, "%s", buf) < 0) {
|
||||||
|
printf("f_printf failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FRESULT fr = f_close(&fil);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,498 @@
|
||||||
|
/* CreateAndVerifyExampleFiles.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
|
||||||
|
|
||||||
|
This file is part of the FreeRTOS distribution.
|
||||||
|
|
||||||
|
FreeRTOS is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License (version 2) as published by the
|
||||||
|
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
|
||||||
|
|
||||||
|
***************************************************************************
|
||||||
|
>>! NOTE: The modification to the GPL is included to allow you to !<<
|
||||||
|
>>! distribute a combined work that includes FreeRTOS without being !<<
|
||||||
|
>>! obliged to provide the source code for proprietary components !<<
|
||||||
|
>>! outside of the FreeRTOS kernel. !<<
|
||||||
|
***************************************************************************
|
||||||
|
|
||||||
|
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. Full license text is available on the following
|
||||||
|
link: http://www.freertos.org/a00114.html
|
||||||
|
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* FreeRTOS provides completely free yet professionally developed, *
|
||||||
|
* robust, strictly quality controlled, supported, and cross *
|
||||||
|
* platform software that is more than just the market leader, it *
|
||||||
|
* is the industry's de facto standard. *
|
||||||
|
* *
|
||||||
|
* Help yourself get started quickly while simultaneously helping *
|
||||||
|
* to support the FreeRTOS project by purchasing a FreeRTOS *
|
||||||
|
* tutorial book, reference manual, or both: *
|
||||||
|
* http://www.FreeRTOS.org/Documentation *
|
||||||
|
* *
|
||||||
|
***************************************************************************
|
||||||
|
|
||||||
|
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
|
||||||
|
the FAQ page "My application does not run, what could be wrong?". Have you
|
||||||
|
defined configASSERT()?
|
||||||
|
|
||||||
|
http://www.FreeRTOS.org/support - In return for receiving this top quality
|
||||||
|
embedded software for free we request you assist our global community by
|
||||||
|
participating in the support forum.
|
||||||
|
|
||||||
|
http://www.FreeRTOS.org/training - Investing in training allows your team to
|
||||||
|
be as productive as possible as early as possible. Now you can receive
|
||||||
|
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
|
||||||
|
Ltd, and the world's leading authority on the world's leading RTOS.
|
||||||
|
|
||||||
|
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
|
||||||
|
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
|
||||||
|
compatible FAT file system, and our tiny thread aware UDP/IP stack.
|
||||||
|
|
||||||
|
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
|
||||||
|
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
|
||||||
|
|
||||||
|
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
|
||||||
|
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
|
||||||
|
licenses offer ticketed support, indemnification and commercial middleware.
|
||||||
|
|
||||||
|
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
|
||||||
|
engineered and independently SIL3 certified version for use in safety and
|
||||||
|
mission critical applications that require provable dependability.
|
||||||
|
|
||||||
|
1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create and verify a few example files using both line based and character
|
||||||
|
* based reads and writes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
/* FreeRTOS+FAT headers. */
|
||||||
|
//#include "ff_headers.h"
|
||||||
|
#include "ff_stdio.h"
|
||||||
|
//
|
||||||
|
#include "my_debug.h"
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
# warning "This test relies on asserts to verify test results!"
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#define TRACE_PRINTF(fmt, args...)
|
||||||
|
#define TRACE_PRINTF DBG_PRINTF
|
||||||
|
|
||||||
|
#define configASSERT myASSERT
|
||||||
|
|
||||||
|
/* The number of bytes read/written to the example files at a time. */
|
||||||
|
#define fsRAM_BUFFER_SIZE 200
|
||||||
|
|
||||||
|
/* The number of bytes written to the file that uses f_putc() and f_getc(). */
|
||||||
|
#define fsPUTC_FILE_SIZE 100
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a set of example files in the root directory of the volume using
|
||||||
|
* ff_fwrite().
|
||||||
|
*/
|
||||||
|
static void prvCreateDemoFilesUsing_ff_fwrite( const char *pcMountPath );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use ff_fread() to read back and verify the files that were created by
|
||||||
|
* prvCreateDemoFilesUsing_ff_fwrite().
|
||||||
|
*/
|
||||||
|
static void prvVerifyDemoFileUsing_ff_fread( void );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an example file in a sub-directory using f_putc().
|
||||||
|
*/
|
||||||
|
static void prvCreateDemoFileUsing_ff_fputc( const char *pcMountPath );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use f_getc() to read back and verify the file that was created by
|
||||||
|
* prvCreateDemoFileUsing_f_putc().
|
||||||
|
*/
|
||||||
|
static void prvVerifyDemoFileUsing_ff_fgetc( const char *pcMountPath );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the configHTTP_ROOT directory, and add a basic default web page.
|
||||||
|
*/
|
||||||
|
#if( ipconfigUSE_HTTP == 1 )
|
||||||
|
|
||||||
|
#ifndef configHTTP_ROOT
|
||||||
|
#error configHTTP_ROOT must be defined to a string that holds the directory to be used as the root for the HTTP server
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void prvCreateDefaultWebPage( void );
|
||||||
|
|
||||||
|
#endif /* ipconfigUSE_HTTP */
|
||||||
|
|
||||||
|
/* Names of directories that are created. */
|
||||||
|
static const char *pcDirectory1 = "SUB1", *pcDirectory2 = "SUB2", *pcFullPath = "/SUB1/SUB2";
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vCreateAndVerifyExampleFiles( const char *pcMountPath )
|
||||||
|
{
|
||||||
|
TRACE_PRINTF("%s(pcMountPath=%s)\n", __FUNCTION__, pcMountPath);
|
||||||
|
|
||||||
|
// Pretend mount path:
|
||||||
|
int lResult = ff_mkdir(pcMountPath);
|
||||||
|
if (-1 == lResult && errno != EEXIST) {
|
||||||
|
EMSG_PRINTF("ff_mkdir %s failed: %s (%d)\n", pcMountPath,
|
||||||
|
strerror(errno), errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create and verify a few example files using both line based and character
|
||||||
|
based reads and writes. */
|
||||||
|
prvCreateDemoFilesUsing_ff_fwrite( pcMountPath );
|
||||||
|
// vTaskDelay(rand() % 5);
|
||||||
|
prvVerifyDemoFileUsing_ff_fread();
|
||||||
|
// vTaskDelay(rand() % 5);
|
||||||
|
prvCreateDemoFileUsing_ff_fputc( pcMountPath );
|
||||||
|
// vTaskDelay(rand() % 5);
|
||||||
|
prvVerifyDemoFileUsing_ff_fgetc( pcMountPath );
|
||||||
|
// vTaskDelay(rand() % 5);
|
||||||
|
|
||||||
|
#if( ipconfigUSE_HTTP == 1 )
|
||||||
|
{
|
||||||
|
prvCreateDefaultWebPage();
|
||||||
|
}
|
||||||
|
#endif /* ipconfigUSE_HTTP */
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvCreateDemoFilesUsing_ff_fwrite( const char *pcMountPath )
|
||||||
|
{
|
||||||
|
TRACE_PRINTF("%s(pcMountPath=%s)\n", __FUNCTION__, pcMountPath);
|
||||||
|
BaseType_t xFileNumber, xWriteNumber;
|
||||||
|
const BaseType_t xMaxFiles = 5;
|
||||||
|
int32_t lItemsWritten;
|
||||||
|
int32_t lResult;
|
||||||
|
FF_FILE *pxFile;
|
||||||
|
char *pcRAMBuffer, *pcFileName;
|
||||||
|
|
||||||
|
/* Allocate buffers used to hold date written to/from the disk, and the
|
||||||
|
file names. */
|
||||||
|
pcRAMBuffer = ( char * ) pvPortMalloc( fsRAM_BUFFER_SIZE );
|
||||||
|
pcFileName = ( char * ) pvPortMalloc( ffconfigMAX_FILENAME );
|
||||||
|
configASSERT( pcRAMBuffer );
|
||||||
|
configASSERT( pcFileName );
|
||||||
|
|
||||||
|
/* Ensure in the root of the mount being used. */
|
||||||
|
lResult = ff_chdir( pcMountPath );
|
||||||
|
configASSERT( lResult >= 0 );
|
||||||
|
|
||||||
|
/* Create xMaxFiles files. Each created file will be
|
||||||
|
( xFileNumber * fsRAM_BUFFER_SIZE ) bytes in length, and filled
|
||||||
|
with a different repeating character. */
|
||||||
|
for( xFileNumber = 1; xFileNumber <= xMaxFiles; xFileNumber++ )
|
||||||
|
{
|
||||||
|
/* Generate a file name. */
|
||||||
|
snprintf( pcFileName, ffconfigMAX_FILENAME, "root%03d.txt", ( int ) xFileNumber );
|
||||||
|
|
||||||
|
/* Obtain the current working directory and print out the file name and
|
||||||
|
the directory into which the file is being written. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "Creating file %s in %s\r\n", pcFileName, pcRAMBuffer );
|
||||||
|
|
||||||
|
/* Open the file, creating the file if it does not already exist. */
|
||||||
|
pxFile = ff_fopen( pcFileName, "w" );
|
||||||
|
configASSERT( pxFile );
|
||||||
|
|
||||||
|
/* Fill the RAM buffer with data that will be written to the file. This
|
||||||
|
is just a repeating ascii character that indicates the file number. */
|
||||||
|
memset( pcRAMBuffer, ( int ) ( '0' + xFileNumber ), fsRAM_BUFFER_SIZE );
|
||||||
|
|
||||||
|
/* Write the RAM buffer to the opened file a number of times. The
|
||||||
|
number of times the RAM buffer is written to the file depends on the
|
||||||
|
file number, so the length of each created file will be different. */
|
||||||
|
for( xWriteNumber = 0; xWriteNumber < xFileNumber; xWriteNumber++ )
|
||||||
|
{
|
||||||
|
lItemsWritten = ff_fwrite( pcRAMBuffer, fsRAM_BUFFER_SIZE, 1, pxFile );
|
||||||
|
configASSERT( lItemsWritten == 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the file so another file can be created. */
|
||||||
|
ff_fclose( pxFile );
|
||||||
|
}
|
||||||
|
|
||||||
|
vPortFree( pcRAMBuffer );
|
||||||
|
vPortFree( pcFileName );
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvVerifyDemoFileUsing_ff_fread( void )
|
||||||
|
{
|
||||||
|
TRACE_PRINTF("%s()\n", __FUNCTION__);
|
||||||
|
BaseType_t xFileNumber, xReadNumber;
|
||||||
|
const BaseType_t xMaxFiles = 5;
|
||||||
|
size_t xItemsRead, xChar;
|
||||||
|
FF_FILE *pxFile;
|
||||||
|
char *pcRAMBuffer, *pcFileName;
|
||||||
|
|
||||||
|
/* Allocate buffers used to hold date written to/from the disk, and the
|
||||||
|
file names. */
|
||||||
|
pcRAMBuffer = ( char * ) pvPortMalloc( fsRAM_BUFFER_SIZE );
|
||||||
|
pcFileName = ( char * ) pvPortMalloc( ffconfigMAX_FILENAME );
|
||||||
|
configASSERT( pcRAMBuffer );
|
||||||
|
configASSERT( pcFileName );
|
||||||
|
|
||||||
|
/* Read back the files that were created by
|
||||||
|
prvCreateDemoFilesUsing_ff_fwrite(). */
|
||||||
|
for( xFileNumber = 1; xFileNumber <= xMaxFiles; xFileNumber++ )
|
||||||
|
{
|
||||||
|
/* Generate the file name. */
|
||||||
|
snprintf( pcFileName, ffconfigMAX_FILENAME, "root%03d.txt", ( int ) xFileNumber );
|
||||||
|
|
||||||
|
/* Obtain the current working directory and print out the file name and
|
||||||
|
the directory from which the file is being read. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "Reading file %s from %s\r\n", pcFileName, pcRAMBuffer );
|
||||||
|
|
||||||
|
/* Open the file for reading. */
|
||||||
|
pxFile = ff_fopen( pcFileName, "r" );
|
||||||
|
FF_PRINTF("FF_fopen(%s): %s (%d)\r\r\n", pcFileName, strerror(errno), errno);
|
||||||
|
|
||||||
|
configASSERT( pxFile );
|
||||||
|
|
||||||
|
/* Read the file into the RAM buffer, checking the file contents are as
|
||||||
|
expected. The size of the file depends on the file number. */
|
||||||
|
for( xReadNumber = 0; xReadNumber < xFileNumber; xReadNumber++ )
|
||||||
|
{
|
||||||
|
/* Start with the RAM buffer clear. */
|
||||||
|
memset( pcRAMBuffer, 0x00, fsRAM_BUFFER_SIZE );
|
||||||
|
|
||||||
|
xItemsRead = ff_fread( pcRAMBuffer, fsRAM_BUFFER_SIZE, 1, pxFile );
|
||||||
|
configASSERT( xItemsRead == 1 );
|
||||||
|
|
||||||
|
/* Check the RAM buffer is filled with the expected data. Each
|
||||||
|
file contains a different repeating ascii character that indicates
|
||||||
|
the number of the file. */
|
||||||
|
for( xChar = 0; xChar < fsRAM_BUFFER_SIZE; xChar++ )
|
||||||
|
{
|
||||||
|
configASSERT( pcRAMBuffer[ xChar ] == ( '0' + ( char ) xFileNumber ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the file. */
|
||||||
|
ff_fclose( pxFile );
|
||||||
|
}
|
||||||
|
|
||||||
|
vPortFree( pcRAMBuffer );
|
||||||
|
vPortFree( pcFileName );
|
||||||
|
|
||||||
|
/*_RB_ also test what happens when attempting to read using too large item
|
||||||
|
sizes, etc. */
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvCreateDemoFileUsing_ff_fputc( const char *pcMountPath )
|
||||||
|
{
|
||||||
|
TRACE_PRINTF("%s(pcMountPath=%s)\n", __FUNCTION__, pcMountPath);
|
||||||
|
int32_t iReturn, iByte, iReturned;
|
||||||
|
FF_FILE *pxFile;
|
||||||
|
char *pcRAMBuffer, *pcFileName;
|
||||||
|
|
||||||
|
/* Allocate buffers used to hold date written to/from the disk, and the
|
||||||
|
file names. */
|
||||||
|
pcRAMBuffer = ( char * ) pvPortMalloc( fsRAM_BUFFER_SIZE );
|
||||||
|
pcFileName = ( char * ) pvPortMalloc( ffconfigMAX_FILENAME );
|
||||||
|
configASSERT( pcRAMBuffer );
|
||||||
|
configASSERT( pcFileName );
|
||||||
|
|
||||||
|
/* Obtain and print out the working directory. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "In directory %s\r\n", pcRAMBuffer );
|
||||||
|
|
||||||
|
/* Create a sub directory. */
|
||||||
|
iReturn = ff_mkdir( pcDirectory1 );
|
||||||
|
configASSERT( iReturn == pdFREERTOS_ERRNO_NONE );
|
||||||
|
|
||||||
|
/* Move into the created sub-directory. */
|
||||||
|
iReturn = ff_chdir( pcDirectory1 );
|
||||||
|
if (iReturn != pdFREERTOS_ERRNO_NONE) {
|
||||||
|
FF_PRINTF("ff_chdir error: %s (%d)\n", strerror(errno), errno);
|
||||||
|
configASSERT( iReturn == pdFREERTOS_ERRNO_NONE );
|
||||||
|
}
|
||||||
|
/* Obtain and print out the working directory. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "In directory %s\r\n", pcRAMBuffer );
|
||||||
|
|
||||||
|
/* Create a subdirectory in the new directory. */
|
||||||
|
iReturn = ff_mkdir( pcDirectory2 );
|
||||||
|
configASSERT( iReturn == pdFREERTOS_ERRNO_NONE );
|
||||||
|
|
||||||
|
/* Move into the directory just created - now two directories down from
|
||||||
|
the root. */
|
||||||
|
iReturn = ff_chdir( pcDirectory2 );
|
||||||
|
configASSERT( iReturn == pdFREERTOS_ERRNO_NONE );
|
||||||
|
|
||||||
|
/* Obtain and print out the working directory. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "In directory %s\r\n", pcRAMBuffer );
|
||||||
|
snprintf( pcFileName, ffconfigMAX_FILENAME, "%s%s", pcMountPath, pcFullPath );
|
||||||
|
if (0 != strcmp( pcRAMBuffer, pcFileName )) {
|
||||||
|
FF_PRINTF( "pcRAMBuffer:%s,pcFileName:%s\n", pcRAMBuffer, pcFileName);
|
||||||
|
configASSERT( strcmp( pcRAMBuffer, pcFileName ) == 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate the file name. */
|
||||||
|
snprintf( pcFileName, ffconfigMAX_FILENAME, "%s.txt", pcDirectory2 );
|
||||||
|
|
||||||
|
/* Print out the file name and the directory into which the file is being
|
||||||
|
written. */
|
||||||
|
FF_PRINTF( "Writing file %s in %s\r\n", pcFileName, pcRAMBuffer );
|
||||||
|
|
||||||
|
pxFile = ff_fopen( pcFileName, "w" );
|
||||||
|
configASSERT( pxFile );
|
||||||
|
|
||||||
|
/* Create a file 1 byte at a time. The file is filled with incrementing
|
||||||
|
ascii characters starting from '0'. */
|
||||||
|
for( iByte = 0; iByte < fsPUTC_FILE_SIZE; iByte++ )
|
||||||
|
{
|
||||||
|
iReturned = ff_fputc( ( ( int ) '0' + iByte ), pxFile );
|
||||||
|
configASSERT( iReturned == ( ( int ) '0' + iByte ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finished so close the file. */
|
||||||
|
ff_fclose( pxFile );
|
||||||
|
|
||||||
|
/* Move back to the root directory. */
|
||||||
|
iReturned = ff_chdir( "../.." );
|
||||||
|
configASSERT( iReturn == pdFREERTOS_ERRNO_NONE );
|
||||||
|
|
||||||
|
/* Obtain and print out the working directory. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "Back in root directory %s\r\n", pcRAMBuffer );
|
||||||
|
configASSERT( strcmp( pcRAMBuffer, pcMountPath ) == 0 );
|
||||||
|
|
||||||
|
vPortFree( pcRAMBuffer );
|
||||||
|
vPortFree( pcFileName );
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvVerifyDemoFileUsing_ff_fgetc( const char *pcMountPath )
|
||||||
|
{
|
||||||
|
TRACE_PRINTF("%s(pcMountPath=%s)\n", __FUNCTION__, pcMountPath);
|
||||||
|
int iByte, iReturned;
|
||||||
|
FF_FILE *pxFile;
|
||||||
|
char *pcRAMBuffer, *pcFileName;
|
||||||
|
|
||||||
|
/* Allocate buffers used to hold date written to/from the disk, and the
|
||||||
|
file names. */
|
||||||
|
pcRAMBuffer = ( char * ) pvPortMalloc( fsRAM_BUFFER_SIZE );
|
||||||
|
pcFileName = ( char * ) pvPortMalloc( ffconfigMAX_FILENAME );
|
||||||
|
configASSERT( pcRAMBuffer );
|
||||||
|
configASSERT( pcFileName );
|
||||||
|
|
||||||
|
/* Move into the directory in which the file was created. */
|
||||||
|
snprintf( pcFileName, ffconfigMAX_FILENAME, "%s%s", pcMountPath, pcFullPath );
|
||||||
|
iReturned = ff_chdir( pcFileName );
|
||||||
|
configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
|
||||||
|
|
||||||
|
/* Obtain and print out the working directory. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "Back in directory %s\r\n", pcRAMBuffer );
|
||||||
|
configASSERT( strcmp( pcRAMBuffer, pcFileName ) == 0 );
|
||||||
|
|
||||||
|
/* pcFileName is about to be overwritten - take a copy. */
|
||||||
|
strcpy( pcRAMBuffer, pcFileName );
|
||||||
|
|
||||||
|
/* Generate the file name. */
|
||||||
|
sprintf( pcFileName, "%s.txt", pcDirectory2 );
|
||||||
|
|
||||||
|
/* Print out the file name and the directory from which the file is being
|
||||||
|
read. */
|
||||||
|
FF_PRINTF( "Reading file %s in %s\r\n", pcFileName, pcRAMBuffer );
|
||||||
|
|
||||||
|
/* This time the file is opened for reading. */
|
||||||
|
pxFile = ff_fopen( pcFileName, "r" );
|
||||||
|
|
||||||
|
/* Read the file 1 byte at a time. */
|
||||||
|
for( iByte = 0; iByte < fsPUTC_FILE_SIZE; iByte++ )
|
||||||
|
{
|
||||||
|
iReturned = ff_fgetc( pxFile );
|
||||||
|
if (iReturned != ( ( int ) '0' + iByte )) {
|
||||||
|
TRACE_PRINTF("iReturned=%d, ( ( int ) '0' + iByte ))=%d\n", iReturned, ( ( int ) '0' + iByte ));
|
||||||
|
}
|
||||||
|
configASSERT( iReturned == ( ( int ) '0' + iByte ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Should not be able to read another bytes. */
|
||||||
|
iReturned = ff_fgetc( pxFile );
|
||||||
|
configASSERT( iReturned == FF_EOF );
|
||||||
|
|
||||||
|
/* Finished so close the file. */
|
||||||
|
ff_fclose( pxFile );
|
||||||
|
|
||||||
|
/* Move back to the root directory. */
|
||||||
|
iReturned = ff_chdir( "../.." );
|
||||||
|
|
||||||
|
/* Obtain and print out the working directory. */
|
||||||
|
ff_getcwd( pcRAMBuffer, fsRAM_BUFFER_SIZE );
|
||||||
|
FF_PRINTF( "Back in root directory %s\r\n", pcRAMBuffer );
|
||||||
|
|
||||||
|
vPortFree( pcRAMBuffer );
|
||||||
|
vPortFree( pcFileName );
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if( ipconfigUSE_HTTP == 1 )
|
||||||
|
|
||||||
|
static void prvCreateDefaultWebPage( void )
|
||||||
|
{
|
||||||
|
int iReturned;
|
||||||
|
size_t x;
|
||||||
|
FF_FILE *pxFile;
|
||||||
|
|
||||||
|
/* Create the directory used as the root of the HTTP server. */
|
||||||
|
iReturned = ff_mkdir( configHTTP_ROOT );
|
||||||
|
|
||||||
|
if( iReturned == pdFREERTOS_ERRNO_NONE )
|
||||||
|
{
|
||||||
|
/* Move into the configHTTP_ROOT directory. */
|
||||||
|
ff_chdir( configHTTP_ROOT );
|
||||||
|
|
||||||
|
/* Create each file defined by the xHTTPFilesToCopy array, which is
|
||||||
|
defined in DefaultWebPages.h. */
|
||||||
|
for( x = 0; x < sizeof( xHTTPFilesToCopy ) / sizeof( xFileToCopy_t ); x++ )
|
||||||
|
{
|
||||||
|
/* Create the file. */
|
||||||
|
pxFile = ff_fopen( xHTTPFilesToCopy[ x ].pcFileName, "w+" );
|
||||||
|
|
||||||
|
if( pxFile != NULL )
|
||||||
|
{
|
||||||
|
/* Write out all the data to the file. */
|
||||||
|
ff_fwrite( xHTTPFilesToCopy[ x ].pucFileData,
|
||||||
|
xHTTPFilesToCopy[ x ].xFileSize,
|
||||||
|
1,
|
||||||
|
pxFile );
|
||||||
|
|
||||||
|
ff_fclose( pxFile );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ipconfigUSE_HTTP */
|
||||||
|
/*-----------------------------------------------------------*/
|
|
@ -0,0 +1,358 @@
|
||||||
|
/*----------------------------------------------------------------------/
|
||||||
|
/ Low level disk I/O module function checker /
|
||||||
|
/-----------------------------------------------------------------------/
|
||||||
|
/ WARNING: The data on the target drive will be lost!
|
||||||
|
*/
|
||||||
|
/* app4-IO_module_function_checker.c
|
||||||
|
Originally from [Compatibility Checker for Storage Device Control Module](http://elm-chan.org/fsw/ff/res/app4.c).
|
||||||
|
*/
|
||||||
|
/*----------------------------------------------------------------------------/
|
||||||
|
/ FatFs - Generic FAT Filesystem Module Rx.xx /
|
||||||
|
/-----------------------------------------------------------------------------/
|
||||||
|
/
|
||||||
|
/ Copyright (C) 20xx, ChaN, all right reserved.
|
||||||
|
/
|
||||||
|
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||||
|
/ source and binary forms, with or without modification, are permitted provided
|
||||||
|
/ that the following condition is met:
|
||||||
|
/
|
||||||
|
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
/ this condition and the following disclaimer.
|
||||||
|
/
|
||||||
|
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||||
|
/ and any warranties related to this software are DISCLAIMED.
|
||||||
|
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||||
|
/ by use of this software.
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
Modifications: Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h> // malloc
|
||||||
|
#include <string.h>
|
||||||
|
#include "ff.h" /* Declarations of sector size */
|
||||||
|
#include "diskio.h" /* Declarations of disk functions */
|
||||||
|
//
|
||||||
|
#include "hardware/gpio.h" // gpio_put
|
||||||
|
//
|
||||||
|
#include <my_debug.h>
|
||||||
|
|
||||||
|
|
||||||
|
static DWORD pn ( /* Pseudo random number generator */
|
||||||
|
DWORD pns /* !0:Initialize, 0:Read */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
static DWORD lfsr;
|
||||||
|
UINT n;
|
||||||
|
|
||||||
|
|
||||||
|
if (pns) {
|
||||||
|
lfsr = pns;
|
||||||
|
for (n = 0; n < 32; n++) pn(0);
|
||||||
|
}
|
||||||
|
if (lfsr & 1) {
|
||||||
|
lfsr >>= 1;
|
||||||
|
lfsr ^= 0x80200003;
|
||||||
|
} else {
|
||||||
|
lfsr >>= 1;
|
||||||
|
}
|
||||||
|
return lfsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int test_diskio (
|
||||||
|
BYTE pdrv, /* Physical drive number to be checked (all data on the drive will be lost) */
|
||||||
|
UINT ncyc, /* Number of test cycles */
|
||||||
|
DWORD* buff, /* Pointer to the working buffer */
|
||||||
|
UINT sz_buff /* Size of the working buffer in unit of byte */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
UINT n, cc, ns;
|
||||||
|
DWORD sz_drv, lba, lba2, sz_eblk, pns = 1;
|
||||||
|
WORD sz_sect;
|
||||||
|
BYTE *pbuff = (BYTE*)buff;
|
||||||
|
DSTATUS ds;
|
||||||
|
DRESULT dr;
|
||||||
|
|
||||||
|
IMSG_PRINTF("test_diskio(%u, %u, 0x%08X, 0x%08X)\n", pdrv, ncyc, (UINT)buff, sz_buff);
|
||||||
|
|
||||||
|
if (sz_buff < FF_MAX_SS + 8) {
|
||||||
|
EMSG_PRINTF("Insufficient work area to run the program.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cc = 1; cc <= ncyc; cc++) {
|
||||||
|
IMSG_PRINTF("**** Test cycle %u of %u start ****\n", cc, ncyc);
|
||||||
|
|
||||||
|
IMSG_PRINTF(" disk_initalize(%u)", pdrv);
|
||||||
|
ds = disk_initialize(pdrv);
|
||||||
|
if (ds & STA_NOINIT) {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
IMSG_PRINTF("**** Get drive size ****\n");
|
||||||
|
IMSG_PRINTF(" disk_ioctl(%u, GET_SECTOR_COUNT, 0x%08X)", pdrv, (UINT)&sz_drv);
|
||||||
|
sz_drv = 0;
|
||||||
|
dr = disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_drv);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (sz_drv < 128) {
|
||||||
|
EMSG_PRINTF("Failed: Insufficient drive size to test.\n");
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" Number of sectors on the drive %u is %lu.\n", pdrv, sz_drv);
|
||||||
|
|
||||||
|
#if FF_MAX_SS != FF_MIN_SS
|
||||||
|
IMSG_PRINTF("**** Get sector size ****\n");
|
||||||
|
IMSG_PRINTF(" disk_ioctl(%u, GET_SECTOR_SIZE, 0x%X)", pdrv, (UINT)&sz_sect);
|
||||||
|
sz_sect = 0;
|
||||||
|
dr = disk_ioctl(pdrv, GET_SECTOR_SIZE, &sz_sect);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" Size of sector is %u bytes.\n", sz_sect);
|
||||||
|
#else
|
||||||
|
sz_sect = FF_MAX_SS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IMSG_PRINTF("**** Get block size ****\n");
|
||||||
|
IMSG_PRINTF(" disk_ioctl(%u, GET_BLOCK_SIZE, 0x%X)", pdrv, (UINT)&sz_eblk);
|
||||||
|
sz_eblk = 0;
|
||||||
|
dr = disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_eblk);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
}
|
||||||
|
if (dr == RES_OK || sz_eblk >= 2) {
|
||||||
|
IMSG_PRINTF(" Size of the erase block is %lu sectors.\n", sz_eblk);
|
||||||
|
} else {
|
||||||
|
IMSG_PRINTF(" Size of the erase block is unknown.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single sector write test */
|
||||||
|
IMSG_PRINTF("**** Single sector write test ****\n");
|
||||||
|
lba = 0;
|
||||||
|
for (n = 0, pn(pns); n < sz_sect; n++) pbuff[n] = (BYTE)pn(0);
|
||||||
|
IMSG_PRINTF(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_write(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
memset(pbuff, 0, sz_sect);
|
||||||
|
IMSG_PRINTF(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_read(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); n < sz_sect && pbuff[n] == (BYTE)pn(0); n++) ;
|
||||||
|
if (n == sz_sect) {
|
||||||
|
IMSG_PRINTF(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" Read data differs from the data written.\n");
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
IMSG_PRINTF("**** Multiple sector write test ****\n");
|
||||||
|
lba = 5; ns = sz_buff / sz_sect;
|
||||||
|
if (ns > 4) ns = 4;
|
||||||
|
if (ns > 1) {
|
||||||
|
for (n = 0, pn(pns); n < (UINT)(sz_sect * ns); n++) pbuff[n] = (BYTE)pn(0);
|
||||||
|
IMSG_PRINTF(" disk_write(%u, 0x%X, %lu, %u)", pdrv, (UINT)pbuff, lba, ns);
|
||||||
|
dr = disk_write(pdrv, pbuff, lba, ns);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
memset(pbuff, 0, sz_sect * ns);
|
||||||
|
IMSG_PRINTF(" disk_read(%u, 0x%X, %lu, %u)", pdrv, (UINT)pbuff, lba, ns);
|
||||||
|
dr = disk_read(pdrv, pbuff, lba, ns);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); n < (UINT)(sz_sect * ns) && pbuff[n] == (BYTE)pn(0); n++) ;
|
||||||
|
if (n == (UINT)(sz_sect * ns)) {
|
||||||
|
IMSG_PRINTF(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" Read data differs from the data written.\n");
|
||||||
|
return 14;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IMSG_PRINTF(" Test skipped.\n");
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
IMSG_PRINTF("**** Single sector write test (unaligned buffer address) ****\n");
|
||||||
|
lba = 5;
|
||||||
|
for (n = 0, pn(pns); n < sz_sect; n++) pbuff[n+3] = (BYTE)pn(0);
|
||||||
|
IMSG_PRINTF(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff+3), lba);
|
||||||
|
dr = disk_write(pdrv, pbuff+3, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
memset(pbuff+5, 0, sz_sect);
|
||||||
|
IMSG_PRINTF(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff+5), lba);
|
||||||
|
dr = disk_read(pdrv, pbuff+5, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); n < sz_sect && pbuff[n+5] == (BYTE)pn(0); n++) ;
|
||||||
|
if (n == sz_sect) {
|
||||||
|
IMSG_PRINTF(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" Read data differs from the data written.\n");
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
IMSG_PRINTF("**** 4GB barrier test ****\n");
|
||||||
|
if (sz_drv >= 128 + 0x80000000 / (sz_sect / 2)) {
|
||||||
|
lba = 6; lba2 = lba + 0x80000000 / (sz_sect / 2);
|
||||||
|
for (n = 0, pn(pns); n < (UINT)(sz_sect * 2); n++) pbuff[n] = (BYTE)pn(0);
|
||||||
|
IMSG_PRINTF(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_write(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 19;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" disk_write(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff+sz_sect), lba2);
|
||||||
|
dr = disk_write(pdrv, pbuff+sz_sect, lba2, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" disk_ioctl(%u, CTRL_SYNC, NULL)", pdrv);
|
||||||
|
dr = disk_ioctl(pdrv, CTRL_SYNC, 0);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
memset(pbuff, 0, sz_sect * 2);
|
||||||
|
IMSG_PRINTF(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)pbuff, lba);
|
||||||
|
dr = disk_read(pdrv, pbuff, lba, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF(" disk_read(%u, 0x%X, %lu, 1)", pdrv, (UINT)(pbuff+sz_sect), lba2);
|
||||||
|
dr = disk_read(pdrv, pbuff+sz_sect, lba2, 1);
|
||||||
|
if (dr == RES_OK) {
|
||||||
|
IMSG_PRINTF(" - ok.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" - failed.\n");
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
for (n = 0, pn(pns); pbuff[n] == (BYTE)pn(0) && n < (UINT)(sz_sect * 2); n++) ;
|
||||||
|
if (n == (UINT)(sz_sect * 2)) {
|
||||||
|
IMSG_PRINTF(" Read data matched.\n");
|
||||||
|
} else {
|
||||||
|
EMSG_PRINTF(" Read data differs from the data written.\n");
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IMSG_PRINTF(" Test skipped.\n");
|
||||||
|
}
|
||||||
|
pns++;
|
||||||
|
|
||||||
|
IMSG_PRINTF("**** Test cycle %u of %u completed ****\n\n", cc, ncyc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//int main (int argc, char* argv[])
|
||||||
|
int lliot(size_t pnum)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
// DWORD buff[FF_MAX_SS]; /* Working buffer (4 sector in size) */
|
||||||
|
size_t buff_sz = FF_MAX_SS * sizeof(DWORD);
|
||||||
|
DWORD *buff = malloc(buff_sz);
|
||||||
|
if (!buff) {
|
||||||
|
EMSG_PRINTF("malloc(%zu) failed!\n", buff_sz);
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check function/compatibility of the physical drive #0 */
|
||||||
|
rc = test_diskio(pnum, 3, buff, buff_sz);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
EMSG_PRINTF("Sorry the function/compatibility test failed. (rc=%d)\nFatFs will not work with this disk driver.\n", rc);
|
||||||
|
} else {
|
||||||
|
IMSG_PRINTF("Congratulations! The disk driver works well.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buff);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
/* Ported from: https://github.com/greiman/SdFat/blob/master/examples/bench/bench.ino
|
||||||
|
*
|
||||||
|
* This program is a simple binary write/read benchmark.
|
||||||
|
*/
|
||||||
|
#include <my_debug.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "SDIO/SdioCard.h"
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "sd_card.h"
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
#define error(s) \
|
||||||
|
{ \
|
||||||
|
EMSG_PRINTF("ERROR: %s\n", s); \
|
||||||
|
__breakpoint(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t millis() {
|
||||||
|
return to_ms_since_boot(get_absolute_time());
|
||||||
|
}
|
||||||
|
static uint64_t micros() {
|
||||||
|
return to_us_since_boot(get_absolute_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set PRE_ALLOCATE true to pre-allocate file clusters.
|
||||||
|
static 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.
|
||||||
|
static const bool SKIP_FIRST_LATENCY = true;
|
||||||
|
|
||||||
|
// Size of read/write in bytes
|
||||||
|
#define BUF_SIZE 65536 // size of an erasable sector
|
||||||
|
|
||||||
|
// File size in MiB where MiB = 1048576 bytes.
|
||||||
|
#define FILE_SIZE_MiB 5
|
||||||
|
|
||||||
|
// Write pass count.
|
||||||
|
static const uint8_t WRITE_COUNT = 2;
|
||||||
|
|
||||||
|
// Read pass count.
|
||||||
|
static const uint8_t READ_COUNT = 2;
|
||||||
|
//==============================================================================
|
||||||
|
// End of configuration constants.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// File size in bytes.
|
||||||
|
// static const uint32_t FILE_SIZE = 1000000UL * FILE_SIZE_MB;
|
||||||
|
#define FILE_SIZE (1024 * 1024 * FILE_SIZE_MiB)
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
static void bench_test(FIL* file_p, uint8_t buf[BUF_SIZE]) {
|
||||||
|
float s;
|
||||||
|
uint32_t t;
|
||||||
|
uint32_t maxLatency;
|
||||||
|
uint32_t minLatency;
|
||||||
|
uint32_t totalLatency;
|
||||||
|
bool skipLatency;
|
||||||
|
|
||||||
|
IMSG_PRINTF("FILE_SIZE_MB = %d\n", FILE_SIZE_MiB); // << FILE_SIZE_MB << endl;
|
||||||
|
IMSG_PRINTF("BUF_SIZE = %zu\n", BUF_SIZE); // << BUF_SIZE << F(" bytes\n");
|
||||||
|
IMSG_PRINTF("Starting write test, please wait.\n\n"); // << endl
|
||||||
|
// << endl;
|
||||||
|
// do write test
|
||||||
|
uint32_t n = FILE_SIZE / BUF_SIZE;
|
||||||
|
IMSG_PRINTF("write speed and latency\n");
|
||||||
|
IMSG_PRINTF("speed,max,min,avg\n");
|
||||||
|
IMSG_PRINTF("KB/Sec,usec,usec,usec\n");
|
||||||
|
for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
|
||||||
|
FRESULT fr = f_rewind(file_p);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_rewind error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
maxLatency = 0;
|
||||||
|
minLatency = 9999999;
|
||||||
|
totalLatency = 0;
|
||||||
|
skipLatency = SKIP_FIRST_LATENCY;
|
||||||
|
t = millis();
|
||||||
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
|
uint32_t m = micros();
|
||||||
|
unsigned int bw;
|
||||||
|
fr = f_write(file_p, buf, BUF_SIZE, &bw); /* Write it to the destination file */
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_write error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bw < BUF_SIZE) { /* error or disk full */
|
||||||
|
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;
|
||||||
|
skipLatency = f_tell(file_p) < 512;
|
||||||
|
} else {
|
||||||
|
if (maxLatency < m) {
|
||||||
|
maxLatency = m;
|
||||||
|
}
|
||||||
|
if (minLatency > m) {
|
||||||
|
minLatency = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fr = f_sync(file_p);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_sync error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
t = millis() - t;
|
||||||
|
s = f_size(file_p);
|
||||||
|
IMSG_PRINTF("%.1f,%lu,%lu", s / t, maxLatency, minLatency);
|
||||||
|
IMSG_PRINTF(",%lu\n", totalLatency / n);
|
||||||
|
}
|
||||||
|
IMSG_PRINTF("\nStarting read test, please wait.\n");
|
||||||
|
IMSG_PRINTF("\nread speed and latency\n");
|
||||||
|
IMSG_PRINTF("speed,max,min,avg\n");
|
||||||
|
IMSG_PRINTF("KB/Sec,usec,usec,usec\n");
|
||||||
|
|
||||||
|
// do read test
|
||||||
|
for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) {
|
||||||
|
FRESULT fr = f_rewind(file_p);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_rewind error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
uint32_t m = micros();
|
||||||
|
unsigned int nr;
|
||||||
|
fr = f_read(file_p, buf, BUF_SIZE, &nr);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_read error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = f_size(file_p);
|
||||||
|
t = millis() - t;
|
||||||
|
IMSG_PRINTF("%.1f,%lu,%lu", s / t, maxLatency, minLatency);
|
||||||
|
IMSG_PRINTF(",%lu\n", totalLatency / n);
|
||||||
|
}
|
||||||
|
IMSG_PRINTF("\nDone\n");
|
||||||
|
}
|
||||||
|
static void bench_open_close(sd_card_t* sd_card_p, uint8_t* buf) {
|
||||||
|
// Open or create file.
|
||||||
|
// FA_CREATE_ALWAYS:
|
||||||
|
// Creates a new file.
|
||||||
|
// If the file is existing, it will be truncated and overwritten.
|
||||||
|
FIL file = {};
|
||||||
|
FRESULT fr = f_open(&file, "bench.dat", FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (PRE_ALLOCATE) {
|
||||||
|
// prepares or allocates a contiguous data area to the file:
|
||||||
|
fr = f_expand(&file, FILE_SIZE, 1);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_expand error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bench_test(&file, buf);
|
||||||
|
|
||||||
|
fr = f_close(&file);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench(char const* logdrv) {
|
||||||
|
static_assert(0 == FILE_SIZE % BUF_SIZE,
|
||||||
|
"For accurate results, FILE_SIZE must be a multiple of BUF_SIZE.");
|
||||||
|
|
||||||
|
sd_card_t* sd_card_p = sd_get_by_drive_prefix(logdrv);
|
||||||
|
if (!sd_card_p) {
|
||||||
|
EMSG_PRINTF("Unknown logical drive name: %s\n", logdrv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FRESULT fr = f_chdrive(logdrv);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_chdrive error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (sd_card_p->state.fatfs.fs_type) {
|
||||||
|
case FS_EXFAT:
|
||||||
|
IMSG_PRINTF("Type is exFAT\n");
|
||||||
|
break;
|
||||||
|
case FS_FAT12:
|
||||||
|
IMSG_PRINTF("Type is FAT12\n");
|
||||||
|
break;
|
||||||
|
case FS_FAT16:
|
||||||
|
IMSG_PRINTF("Type is FAT16\n");
|
||||||
|
break;
|
||||||
|
case FS_FAT32:
|
||||||
|
IMSG_PRINTF("Type is FAT32\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMSG_PRINTF("Card size: ");
|
||||||
|
IMSG_PRINTF("%.2f", sd_card_p->get_num_sectors(sd_card_p) * 512E-9);
|
||||||
|
IMSG_PRINTF(" GB (GB = 1E9 bytes)\n");
|
||||||
|
|
||||||
|
cidDmp(sd_card_p, info_message_printf);
|
||||||
|
|
||||||
|
uint8_t* buf = malloc(BUF_SIZE);
|
||||||
|
if (!buf) {
|
||||||
|
EMSG_PRINTF("malloc(%d) failed\n", BUF_SIZE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill buf with known data
|
||||||
|
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';
|
||||||
|
|
||||||
|
bench_open_close(sd_card_p, buf);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
/* big_file_test.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
//
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
//
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "my_debug.h"
|
||||||
|
|
||||||
|
#define FF_MAX_SS 512
|
||||||
|
#define BUFFSZ (64 * FF_MAX_SS) // Should be a factor of 1 Mebibyte
|
||||||
|
|
||||||
|
#define PRE_ALLOCATE true
|
||||||
|
|
||||||
|
typedef uint32_t DWORD;
|
||||||
|
typedef unsigned int UINT;
|
||||||
|
|
||||||
|
static void report(uint64_t size, uint64_t elapsed_us) {
|
||||||
|
double elapsed = (double)elapsed_us / 1000 / 1000;
|
||||||
|
IMSG_PRINTF("Elapsed seconds %.3g\n", elapsed);
|
||||||
|
IMSG_PRINTF("Transfer rate ");
|
||||||
|
if ((double)size / elapsed / 1024 / 1024 > 1.0) {
|
||||||
|
IMSG_PRINTF("%.3g MiB/s (%.3g MB/s), or ",
|
||||||
|
(double)size / elapsed / 1024 / 1024,
|
||||||
|
(double)size / elapsed / 1000 / 1000);
|
||||||
|
}
|
||||||
|
IMSG_PRINTF("%.3g KiB/s (%.3g kB/s) (%.3g kb/s)\n",
|
||||||
|
(double)size / elapsed / 1024, (double)size / elapsed / 1000, 8.0 * size / elapsed / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a file of size "size" bytes filled with random data seeded with "seed"
|
||||||
|
static bool create_big_file(const char *const pathname, uint64_t size,
|
||||||
|
unsigned seed, DWORD *buff) {
|
||||||
|
FRESULT fr;
|
||||||
|
FIL file; /* File object */
|
||||||
|
|
||||||
|
srand(seed); // Seed pseudo-random number generator
|
||||||
|
|
||||||
|
/* Open the file, creating the file if it does not already exist. */
|
||||||
|
fr = f_open(&file, pathname, FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (PRE_ALLOCATE) {
|
||||||
|
#if 0
|
||||||
|
FRESULT fr = f_lseek(&file, size);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_lseek error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (f_tell(&file) != size) {
|
||||||
|
EMSG_PRINTF("Disk full?\n");
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fr = f_rewind(&file);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_rewind error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
fr = f_truncate(&file);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_truncate error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// prepares or allocates a contiguous data area to the file:
|
||||||
|
fr = f_expand(&file, size, 1);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_expand error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IMSG_PRINTF("Writing...\n");
|
||||||
|
|
||||||
|
uint64_t cum_time = 0;
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < size / BUFFSZ; ++i) {
|
||||||
|
size_t n;
|
||||||
|
for (n = 0; n < BUFFSZ / sizeof(DWORD); n++) buff[n] = rand();
|
||||||
|
UINT bw;
|
||||||
|
|
||||||
|
absolute_time_t xStart = get_absolute_time();
|
||||||
|
fr = f_write(&file, buff, BUFFSZ, &bw);
|
||||||
|
if (bw < BUFFSZ) {
|
||||||
|
EMSG_PRINTF("f_write(%s,,%d,): only wrote %d bytes\n", pathname, BUFFSZ, bw);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_write error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cum_time += absolute_time_diff_us(xStart, get_absolute_time());
|
||||||
|
}
|
||||||
|
/* Close the file */
|
||||||
|
f_close(&file);
|
||||||
|
|
||||||
|
report(size, cum_time);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a file of size "size" bytes filled with random data seeded with "seed"
|
||||||
|
// and verify the data
|
||||||
|
static bool check_big_file(char *pathname, uint64_t size,
|
||||||
|
uint32_t seed, DWORD *buff) {
|
||||||
|
FRESULT fr;
|
||||||
|
FIL file; /* File object */
|
||||||
|
|
||||||
|
srand(seed); // Seed pseudo-random number generator
|
||||||
|
|
||||||
|
fr = f_open(&file, pathname, FA_READ);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
IMSG_PRINTF("Reading...\n");
|
||||||
|
|
||||||
|
uint64_t cum_time = 0;
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < size / BUFFSZ; ++i) {
|
||||||
|
UINT br;
|
||||||
|
|
||||||
|
absolute_time_t xStart = get_absolute_time();
|
||||||
|
fr = f_read(&file, buff, BUFFSZ, &br);
|
||||||
|
if (br < BUFFSZ) {
|
||||||
|
EMSG_PRINTF("f_read(,%s,%d,):only read %u bytes\n", pathname, BUFFSZ, br);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_read error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cum_time += absolute_time_diff_us(xStart, get_absolute_time());
|
||||||
|
|
||||||
|
/* Check the buffer is filled with the expected data. */
|
||||||
|
size_t n;
|
||||||
|
for (n = 0; n < BUFFSZ / sizeof(DWORD); n++) {
|
||||||
|
unsigned int expected = rand();
|
||||||
|
unsigned int val = buff[n];
|
||||||
|
if (val != expected) {
|
||||||
|
EMSG_PRINTF("Data mismatch at dword %llu: expected=0x%8x val=0x%8x\n",
|
||||||
|
(i * sizeof(buff)) + n, expected, val);
|
||||||
|
f_close(&file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Close the file */
|
||||||
|
f_close(&file);
|
||||||
|
|
||||||
|
report(size, cum_time);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Specify size in Mebibytes (1024x1024 bytes)
|
||||||
|
void big_file_test(char *pathname, size_t size_MiB, uint32_t seed) {
|
||||||
|
// /* Working buffer */
|
||||||
|
DWORD *buff = malloc(BUFFSZ);
|
||||||
|
myASSERT(buff);
|
||||||
|
myASSERT(size_MiB);
|
||||||
|
if (4095 < size_MiB) {
|
||||||
|
EMSG_PRINTF("Warning: Maximum file size: 2^32 - 1 bytes on FAT volume\n");
|
||||||
|
}
|
||||||
|
uint64_t size_B = (uint64_t)size_MiB * 1024 * 1024;
|
||||||
|
|
||||||
|
if (create_big_file(pathname, size_B, seed, buff))
|
||||||
|
check_big_file(pathname, size_B, seed, buff);
|
||||||
|
|
||||||
|
free(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,165 @@
|
||||||
|
/* simple.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
// Adapted from "FATFileSystem example"
|
||||||
|
// at https://os.mbed.com/docs/mbed-os/v5.15/apis/fatfilesystem.html
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
//
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "my_debug.h"
|
||||||
|
|
||||||
|
// Maximum number of elements in buffer
|
||||||
|
#define BUFFER_MAX_LEN 16
|
||||||
|
|
||||||
|
#define TRACE_PRINTF(fmt, args...)
|
||||||
|
//#define TRACE_PRINTF printf
|
||||||
|
|
||||||
|
void simple() {
|
||||||
|
IMSG_PRINTF("\nSimple Test\n");
|
||||||
|
|
||||||
|
char cwdbuf[FF_LFN_BUF - 12] = {0};
|
||||||
|
FRESULT fr = f_getcwd(cwdbuf, sizeof cwdbuf);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_getcwd error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Open the numbers file
|
||||||
|
IMSG_PRINTF("Opening \"numbers.txt\"... ");
|
||||||
|
FIL f;
|
||||||
|
fr = f_open(&f, "numbers.txt", FA_READ | FA_WRITE);
|
||||||
|
IMSG_PRINTF("%s\n", (FR_OK != fr ? "Fail :(" : "OK"));
|
||||||
|
fflush(stdout);
|
||||||
|
if (FR_OK != fr && FR_NO_FILE != fr) {
|
||||||
|
EMSG_PRINTF("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
} else if (FR_NO_FILE == fr) {
|
||||||
|
// Create the numbers file if it doesn't exist
|
||||||
|
IMSG_PRINTF("No file found, creating a new file... ");
|
||||||
|
fflush(stdout);
|
||||||
|
fr = f_open(&f, "numbers.txt", FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
|
||||||
|
IMSG_PRINTF("%s\n", (FR_OK != fr ? "Fail :(" : "OK"));
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
IMSG_PRINTF("\rWriting numbers (%d/%d)... ", i, 10);
|
||||||
|
fflush(stdout);
|
||||||
|
// When the string was written successfuly, it returns number of
|
||||||
|
// character encoding units written to the file. When the function
|
||||||
|
// failed due to disk full or any error, a negative value will be
|
||||||
|
// returned.
|
||||||
|
int rc = f_printf(&f, " %d\n", i);
|
||||||
|
if (rc < 0) {
|
||||||
|
EMSG_PRINTF("Fail :(\n");
|
||||||
|
EMSG_PRINTF("f_printf error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IMSG_PRINTF("\rWriting numbers (%d/%d)... OK\n", 10, 10);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
IMSG_PRINTF("Seeking file... ");
|
||||||
|
fr = f_lseek(&f, 0);
|
||||||
|
IMSG_PRINTF("%s\n", (FR_OK != fr ? "Fail :(" : "OK"));
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_lseek error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
// Go through and increment the numbers
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
IMSG_PRINTF("\nIncrementing numbers (%d/%d)... ", i, 10);
|
||||||
|
|
||||||
|
// Get current stream position
|
||||||
|
long pos = f_tell(&f);
|
||||||
|
|
||||||
|
// Parse out the number and increment
|
||||||
|
char buf[BUFFER_MAX_LEN];
|
||||||
|
if (!f_gets(buf, BUFFER_MAX_LEN, &f)) {
|
||||||
|
EMSG_PRINTF("error: f_gets returned NULL\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *endptr;
|
||||||
|
int32_t number = strtol(buf, &endptr, 10);
|
||||||
|
if (endptr == buf) {
|
||||||
|
EMSG_PRINTF("No character was read\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*endptr && *endptr != '\n') {
|
||||||
|
EMSG_PRINTF("The whole input was not converted\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
number += 1;
|
||||||
|
|
||||||
|
// Seek to beginning of number
|
||||||
|
fr = f_lseek(&f, pos);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_lseek error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Store number
|
||||||
|
f_printf(&f, " %d\n", (int)number);
|
||||||
|
|
||||||
|
// Flush between write and read on same file
|
||||||
|
f_sync(&f);
|
||||||
|
}
|
||||||
|
IMSG_PRINTF("\rIncrementing numbers (%d/%d)... OK\n", 10, 10);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
// Close the file which also flushes any cached writes
|
||||||
|
IMSG_PRINTF("Closing \"numbers.txt\"... ");
|
||||||
|
fr = f_close(&f);
|
||||||
|
IMSG_PRINTF("%s\n", (FR_OK != fr ? "Fail :(" : "OK"));
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
ls("");
|
||||||
|
|
||||||
|
// Display the numbers file
|
||||||
|
char pathbuf[FF_LFN_BUF] = {0};
|
||||||
|
snprintf(pathbuf, sizeof pathbuf, "%s/%s", cwdbuf, "numbers.txt");
|
||||||
|
IMSG_PRINTF("Opening \"%s\"... ", pathbuf);
|
||||||
|
fr = f_open(&f, pathbuf, FA_READ);
|
||||||
|
IMSG_PRINTF("%s\n", (FR_OK != fr ? "Fail :(" : "OK"));
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
EMSG_PRINTF("f_open error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
IMSG_PRINTF("numbers:\n");
|
||||||
|
while (!f_eof(&f)) {
|
||||||
|
char c;
|
||||||
|
UINT br;
|
||||||
|
fr = f_read(&f, &c, sizeof c, &br);
|
||||||
|
if (FR_OK != fr)
|
||||||
|
EMSG_PRINTF("f_read error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
else
|
||||||
|
IMSG_PRINTF("%c", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMSG_PRINTF("\nClosing \"%s\"... ", pathbuf);
|
||||||
|
fr = f_close(&f);
|
||||||
|
IMSG_PRINTF("%s\n", (FR_OK != fr ? "Fail :(" : "OK"));
|
||||||
|
if (FR_OK != fr) EMSG_PRINTF("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Generated Cmake Pico project file
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# initalize pico_sdk from installed location
|
||||||
|
# (note this can come from environment, CMake cache etc)
|
||||||
|
#set(PICO_SDK_PATH "/home/carlk/pi/pico/pico-sdk")
|
||||||
|
|
||||||
|
# Pull in Raspberry Pi Pico SDK (must be before project)
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(dynamic_config_example C CXX ASM)
|
||||||
|
|
||||||
|
# Initialise the Raspberry Pi Pico SDK
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_subdirectory(../../src build)
|
||||||
|
|
||||||
|
# Add executable. Default name is the project name, version 0.1
|
||||||
|
add_executable(dynamic_config_example
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Can leave these off for silent mode:
|
||||||
|
#add_compile_definitions(USE_PRINTF USE_DBG_PRINTF)
|
||||||
|
#add_compile_definitions(USE_PRINTF)
|
||||||
|
|
||||||
|
pico_set_program_name(dynamic_config_example "dynamic_config_example")
|
||||||
|
pico_set_program_version(dynamic_config_example "0.1")
|
||||||
|
|
||||||
|
# Choose source and destination for standard input and output:
|
||||||
|
# See 4.1. Serial input and output on Raspberry Pi Pico in Getting started with Raspberry Pi Pico (https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf)
|
||||||
|
# and 2.7.1. Standard Input/Output (stdio) Support in Raspberry Pi Pico C/C++ SDK (https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf):
|
||||||
|
pico_enable_stdio_uart(dynamic_config_example 1)
|
||||||
|
pico_enable_stdio_usb(dynamic_config_example 1)
|
||||||
|
|
||||||
|
# Add the standard library and FatFS/SPI to the build
|
||||||
|
target_link_libraries(dynamic_config_example
|
||||||
|
pico_stdlib
|
||||||
|
no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(dynamic_config_example)
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
/* Instead of a statically linked hw_config.c,
|
||||||
|
create configuration dynamically.
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "ff.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "rtc.h"
|
||||||
|
//
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
static std::vector<spi_t *> spis; // SPI H/W components
|
||||||
|
static std::vector<sd_spi_if_t *> spi_ifs; // SPI Interfaces
|
||||||
|
static std::vector<sd_sdio_if_t *> sdio_ifs; // SDIO Interfaces
|
||||||
|
static std::vector<sd_card_t *> sd_cards; // SD Card Sockets
|
||||||
|
|
||||||
|
size_t sd_get_num() { return sd_cards.size(); }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (num <= sd_get_num()) {
|
||||||
|
return sd_cards[num];
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test(sd_card_t *sd_card_p) {
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
char const * const drive_prefix = sd_get_drive_prefix(sd_card_p);
|
||||||
|
printf("Testing drive %s\n", drive_prefix);
|
||||||
|
FRESULT fr = f_mount(&sd_card_p->state.fatfs, drive_prefix, 1);
|
||||||
|
if (FR_OK != fr) panic("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
fr = f_chdrive(drive_prefix);
|
||||||
|
if (FR_OK != fr) panic("f_chdrive error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
|
||||||
|
FIL fil;
|
||||||
|
const char *const filename = "filename.txt";
|
||||||
|
fr = f_open(&fil, filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr)
|
||||||
|
panic("f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
||||||
|
if (f_printf(&fil, "Hello, world!\n") < 0) {
|
||||||
|
printf("f_printf failed\n");
|
||||||
|
}
|
||||||
|
fr = f_close(&fil);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
f_unmount(drive_prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
time_init();
|
||||||
|
|
||||||
|
puts("Hello, world!");
|
||||||
|
|
||||||
|
/* Hardware Configuration of SPI "objects" */
|
||||||
|
|
||||||
|
// spis[0]
|
||||||
|
spi_t *spi_p = new spi_t();
|
||||||
|
assert(spi_p);
|
||||||
|
spi_p->hw_inst = spi0; // RP2040 SPI component
|
||||||
|
spi_p->sck_gpio = 2; // GPIO number (not Pico pin number)
|
||||||
|
spi_p->mosi_gpio = 3;
|
||||||
|
spi_p->miso_gpio = 4;
|
||||||
|
spi_p->set_drive_strength = true;
|
||||||
|
spi_p->mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA;
|
||||||
|
spi_p->sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA;
|
||||||
|
spi_p->DMA_IRQ_num = DMA_IRQ_0;
|
||||||
|
spi_p->use_exclusive_DMA_IRQ_handler = true;
|
||||||
|
spi_p->baud_rate = 12 * 1000 * 1000; // Actual frequency: 10416666
|
||||||
|
spis.push_back(spi_p);
|
||||||
|
|
||||||
|
// spis[1]
|
||||||
|
spi_p = new spi_t();
|
||||||
|
assert(spi_p);
|
||||||
|
spi_p->hw_inst = spi1; // RP2040 SPI component
|
||||||
|
spi_p->miso_gpio = 8; // GPIO number (not Pico pin number)
|
||||||
|
spi_p->sck_gpio = 10;
|
||||||
|
spi_p->mosi_gpio = 11;
|
||||||
|
spi_p->set_drive_strength = true;
|
||||||
|
spi_p->mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA;
|
||||||
|
spi_p->sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA;
|
||||||
|
spi_p->DMA_IRQ_num = DMA_IRQ_1;
|
||||||
|
spi_p->baud_rate = 12 * 1000 * 1000; // Actual frequency: 10416666spi_p->
|
||||||
|
spis.push_back(spi_p);
|
||||||
|
|
||||||
|
/* SPI Interfaces */
|
||||||
|
|
||||||
|
// spi_ifs[0]
|
||||||
|
sd_spi_if_t *spi_if_p = new sd_spi_if_t();
|
||||||
|
assert(spi_if_p);
|
||||||
|
|
||||||
|
spi_if_p->spi = spis[0]; // Pointer to the SPI driving this card
|
||||||
|
spi_if_p->ss_gpio = 7; // The SPI slave select GPIO for this SD card
|
||||||
|
spi_ifs.push_back(spi_if_p);
|
||||||
|
|
||||||
|
// spi_ifs[1]
|
||||||
|
spi_if_p = new sd_spi_if_t();
|
||||||
|
assert(spi_if_p);
|
||||||
|
spi_if_p->spi = spis[1]; // Pointer to the SPI driving this card
|
||||||
|
spi_if_p->ss_gpio = 12; // The SPI slave select GPIO for this SD card
|
||||||
|
spi_ifs.push_back(spi_if_p);
|
||||||
|
|
||||||
|
// spi_ifs[2]
|
||||||
|
spi_if_p = new sd_spi_if_t();
|
||||||
|
assert(spi_if_p);
|
||||||
|
spi_if_p->spi = spis[1]; // Pointer to the SPI driving this card
|
||||||
|
spi_if_p->ss_gpio = 13; // The SPI slave select GPIO for this SD card
|
||||||
|
spi_ifs.push_back(spi_if_p);
|
||||||
|
|
||||||
|
/* SDIO Interfaces */
|
||||||
|
sd_sdio_if_t *sd_sdio_if_p = new sd_sdio_if_t();
|
||||||
|
assert(sd_sdio_if_p);
|
||||||
|
// sdio_ifs[0]
|
||||||
|
sd_sdio_if_p->CMD_gpio = 17;
|
||||||
|
sd_sdio_if_p->D0_gpio = 18;
|
||||||
|
sd_sdio_if_p->SDIO_PIO = pio1;
|
||||||
|
sd_sdio_if_p->DMA_IRQ_num = DMA_IRQ_1;
|
||||||
|
sd_sdio_if_p->baud_rate = 15 * 1000 * 1000; // 15 MHz
|
||||||
|
sdio_ifs.push_back(sd_sdio_if_p);
|
||||||
|
|
||||||
|
/* Hardware Configuration of the SD Card "objects" */
|
||||||
|
|
||||||
|
// sd_cards[0]
|
||||||
|
sd_card_t *sd_card_p = new sd_card_t();
|
||||||
|
assert(sd_card_p);
|
||||||
|
sd_card_p->type = SD_IF_SPI;
|
||||||
|
sd_card_p->spi_if_p = spi_ifs[0]; // Pointer to the SPI interface driving this card
|
||||||
|
sd_card_p->use_card_detect = true;
|
||||||
|
sd_card_p->card_detect_gpio = 9;
|
||||||
|
sd_card_p->card_detected_true = 0; // What the GPIO read returns when a card is present
|
||||||
|
sd_card_p->card_detect_use_pull = true;
|
||||||
|
sd_card_p->card_detect_pull_hi = true;
|
||||||
|
sd_cards.push_back(sd_card_p);
|
||||||
|
|
||||||
|
// sd_cards[1]: Socket sd1
|
||||||
|
sd_card_p = new sd_card_t();
|
||||||
|
assert(sd_card_p);
|
||||||
|
sd_card_p->type = SD_IF_SPI;
|
||||||
|
sd_card_p->spi_if_p = spi_ifs[1]; // Pointer to the SPI interface driving this card
|
||||||
|
sd_card_p->use_card_detect = true;
|
||||||
|
sd_card_p->card_detect_gpio = 14;
|
||||||
|
sd_card_p->card_detected_true = 0; // What the GPIO read returns when a card is present
|
||||||
|
sd_card_p->card_detect_use_pull = true;
|
||||||
|
sd_card_p->card_detect_pull_hi = true;
|
||||||
|
sd_cards.push_back(sd_card_p);
|
||||||
|
|
||||||
|
// sd_cards[2]: Socket sd2
|
||||||
|
sd_card_p = new sd_card_t();
|
||||||
|
assert(sd_card_p);
|
||||||
|
sd_card_p->type = SD_IF_SPI;
|
||||||
|
sd_card_p->spi_if_p = spi_ifs[2]; // Pointer to the SPI interface driving this card
|
||||||
|
sd_card_p->use_card_detect = true;
|
||||||
|
sd_card_p->card_detect_gpio = 15;
|
||||||
|
sd_card_p->card_detected_true = 0; // What the GPIO read returns when a card is present
|
||||||
|
sd_card_p->card_detect_use_pull = true;
|
||||||
|
sd_card_p->card_detect_pull_hi = true;
|
||||||
|
sd_cards.push_back(sd_card_p);
|
||||||
|
|
||||||
|
// sd_cards[3]: Socket sd3
|
||||||
|
sd_card_p = new sd_card_t();
|
||||||
|
assert(sd_card_p);
|
||||||
|
sd_card_p->type = SD_IF_SDIO;
|
||||||
|
sd_card_p->sdio_if_p = sdio_ifs[0];
|
||||||
|
sd_card_p->use_card_detect = true;
|
||||||
|
sd_card_p->card_detect_gpio = 22;
|
||||||
|
sd_card_p->card_detected_true = 0; // What the GPIO read returns when a card is present
|
||||||
|
sd_card_p->card_detect_use_pull = true;
|
||||||
|
sd_card_p->card_detect_pull_hi = true;
|
||||||
|
sd_cards.push_back(sd_card_p);
|
||||||
|
|
||||||
|
// The H/W config must be set up before this is called:
|
||||||
|
sd_init_driver();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sd_get_num(); ++i)
|
||||||
|
test(sd_get_by_num(i));
|
||||||
|
|
||||||
|
puts("Goodbye, world!");
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
;
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Generated Cmake Pico project file
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# initalize pico_sdk from installed location
|
||||||
|
# (note this can come from environment, CMake cache etc)
|
||||||
|
#set(PICO_SDK_PATH "/home/carlk/pi/pico/pico-sdk")
|
||||||
|
|
||||||
|
# Pull in Raspberry Pi Pico SDK (must be before project)
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(simple_example C CXX ASM)
|
||||||
|
|
||||||
|
# Initialise the Raspberry Pi Pico SDK
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_subdirectory(../../src build)
|
||||||
|
|
||||||
|
# Add executable. Default name is the project name, version 0.1
|
||||||
|
add_executable(simple_example
|
||||||
|
main.c
|
||||||
|
hw_config.c
|
||||||
|
)
|
||||||
|
# Can leave these off for silent mode:
|
||||||
|
# add_compile_definitions(USE_PRINTF USE_DBG_PRINTF)
|
||||||
|
add_compile_definitions(USE_PRINTF)
|
||||||
|
|
||||||
|
# Add the standard library and FatFS/SPI to the build
|
||||||
|
target_link_libraries(simple_example
|
||||||
|
pico_stdlib
|
||||||
|
no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_set_program_name(simple_example "simple_example")
|
||||||
|
pico_set_program_version(simple_example "0.1")
|
||||||
|
|
||||||
|
# Choose source and destination for standard input and output:
|
||||||
|
# See 4.1. Serial input and output on Raspberry Pi Pico in Getting started with Raspberry Pi Pico (https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf)
|
||||||
|
# and 2.7.1. Standard Input/Output (stdio) Support in Raspberry Pi Pico C/C++ SDK (https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf):
|
||||||
|
pico_enable_stdio_uart(simple_example 1)
|
||||||
|
pico_enable_stdio_usb(simple_example 1)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(simple_example)
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
/* Configuration of RP2040 hardware SPI object */
|
||||||
|
static spi_t spi = {
|
||||||
|
.hw_inst = spi0, // RP2040 SPI component
|
||||||
|
.sck_gpio = 2, // GPIO number (not Pico pin number)
|
||||||
|
.mosi_gpio = 3,
|
||||||
|
.miso_gpio = 4,
|
||||||
|
.baud_rate = 12 * 1000 * 1000 // Actual frequency: 10416666.
|
||||||
|
};
|
||||||
|
|
||||||
|
/* SPI Interface */
|
||||||
|
static sd_spi_if_t spi_if = {
|
||||||
|
.spi = &spi, // Pointer to the SPI driving this card
|
||||||
|
.ss_gpio = 7 // The SPI slave select GPIO for this SD card
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Configuration of the SD Card socket object */
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SPI,
|
||||||
|
.spi_if_p = &spi_if // Pointer to the SPI interface driving this card
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return 1; }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (0 == num) {
|
||||||
|
return &sd_card;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
//
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "ff.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "rtc.h"
|
||||||
|
//
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
time_init();
|
||||||
|
|
||||||
|
puts("Hello, world!");
|
||||||
|
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
FATFS fs;
|
||||||
|
FRESULT fr = f_mount(&fs, "", 1);
|
||||||
|
if (FR_OK != fr) panic("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
FIL fil;
|
||||||
|
const char* const filename = "filename.txt";
|
||||||
|
fr = f_open(&fil, filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr)
|
||||||
|
panic("f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
||||||
|
if (f_printf(&fil, "Hello, world!\n") < 0) {
|
||||||
|
printf("f_printf failed\n");
|
||||||
|
}
|
||||||
|
fr = f_close(&fil);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
f_unmount("");
|
||||||
|
|
||||||
|
puts("Goodbye, world!");
|
||||||
|
for (;;);
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Generated Cmake Pico project file
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# initalize pico_sdk from installed location
|
||||||
|
# (note this can come from environment, CMake cache etc)
|
||||||
|
#set(PICO_SDK_PATH "/home/carlk/pi/pico/pico-sdk")
|
||||||
|
|
||||||
|
# Pull in Raspberry Pi Pico SDK (must be before project)
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(simple_sdio C CXX ASM)
|
||||||
|
|
||||||
|
# Initialise the Raspberry Pi Pico SDK
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_subdirectory(../../src build)
|
||||||
|
|
||||||
|
# Add executable. Default name is the project name, version 0.1
|
||||||
|
add_executable(simple_sdio main.c)
|
||||||
|
|
||||||
|
# Can leave these off for silent mode:
|
||||||
|
# add_compile_definitions(USE_PRINTF USE_DBG_PRINTF)
|
||||||
|
add_compile_definitions(USE_PRINTF)
|
||||||
|
|
||||||
|
# Add the standard library and FatFS/SPI to the build
|
||||||
|
target_link_libraries(simple_sdio
|
||||||
|
pico_stdlib
|
||||||
|
no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_set_program_name(simple_sdio "simple_sdio")
|
||||||
|
pico_set_program_version(simple_sdio "0.1")
|
||||||
|
|
||||||
|
# Choose source and destination for standard input and output:
|
||||||
|
# See 4.1. Serial input and output on Raspberry Pi Pico in Getting started with Raspberry Pi Pico (https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf)
|
||||||
|
# and 2.7.1. Standard Input/Output (stdio) Support in Raspberry Pi Pico C/C++ SDK (https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf):
|
||||||
|
pico_enable_stdio_uart(simple_sdio 1)
|
||||||
|
pico_enable_stdio_usb(simple_sdio 1)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(simple_sdio)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/* main.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
//
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
//
|
||||||
|
#include "hw_config.h"
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "ff.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
/* SDIO Interface */
|
||||||
|
static sd_sdio_if_t sdio_if = {
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.baud_rate = 15 * 1000 * 1000 // 15 MHz
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Hardware Configuration of the SD Card socket "object" */
|
||||||
|
static sd_card_t sd_card = {
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_if
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Callbacks used by the library: */
|
||||||
|
size_t sd_get_num() { return 1; }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
if (0 == num)
|
||||||
|
return &sd_card;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
puts("Hello, world!");
|
||||||
|
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
FATFS fs;
|
||||||
|
FRESULT fr = f_mount(&fs, "", 1);
|
||||||
|
if (FR_OK != fr) panic("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
FIL fil;
|
||||||
|
const char* const filename = "filename.txt";
|
||||||
|
fr = f_open(&fil, filename, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr)
|
||||||
|
panic("f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
||||||
|
if (f_printf(&fil, "Hello, world!\n") < 0) {
|
||||||
|
printf("f_printf failed\n");
|
||||||
|
}
|
||||||
|
fr = f_close(&fil);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
}
|
||||||
|
f_unmount("");
|
||||||
|
|
||||||
|
puts("Goodbye, world!");
|
||||||
|
for (;;);
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Generated Cmake Pico project file
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# initalize pico_sdk from installed location
|
||||||
|
# (note this can come from environment, CMake cache etc)
|
||||||
|
#set(PICO_SDK_PATH "/home/carlk/pi/pico/pico-sdk")
|
||||||
|
|
||||||
|
# Pull in Raspberry Pi Pico SDK (must be before project)
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(unix_like C CXX ASM)
|
||||||
|
|
||||||
|
# Initialise the Raspberry Pi Pico SDK
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_subdirectory(../../src build)
|
||||||
|
|
||||||
|
# Add executable. Default name is the project name, version 0.1
|
||||||
|
add_executable(unix_like
|
||||||
|
main.c
|
||||||
|
hw_config.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the standard library and FatFS/SPI to the build
|
||||||
|
target_link_libraries(unix_like
|
||||||
|
pico_stdlib
|
||||||
|
no-OS-FatFS-SD-SDIO-SPI-RPi-Pico
|
||||||
|
)
|
||||||
|
|
||||||
|
# Can leave these off for silent mode:
|
||||||
|
# add_compile_definitions(USE_PRINTF USE_DBG_PRINTF)
|
||||||
|
add_compile_definitions(USE_PRINTF)
|
||||||
|
|
||||||
|
# NOTE: for this to work, src\ff15\source\ffconf.h must be removed or renamed.
|
||||||
|
target_include_directories(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico BEFORE INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_set_program_name(unix_like "unix_like")
|
||||||
|
pico_set_program_version(unix_like "0.1")
|
||||||
|
|
||||||
|
# Choose source and destination for standard input and output:
|
||||||
|
# See 4.1. Serial input and output on Raspberry Pi Pico in Getting started with Raspberry Pi Pico (https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf)
|
||||||
|
# and 2.7.1. Standard Input/Output (stdio) Support in Raspberry Pi Pico C/C++ SDK (https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf):
|
||||||
|
pico_enable_stdio_uart(unix_like 1)
|
||||||
|
pico_enable_stdio_usb(unix_like 1)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(unix_like)
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
|
||||||
|
/* hw_config.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file should be tailored to match the hardware design.
|
||||||
|
|
||||||
|
See
|
||||||
|
https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico/tree/main#customizing-for-the-hardware-configuration
|
||||||
|
|
||||||
|
There should be one element of the spi[] array for each RP2040 hardware SPI used.
|
||||||
|
|
||||||
|
There should be one element of the spi_ifs[] array for each SPI interface object.
|
||||||
|
* Each element of spi_ifs[] must point to an spi_t instance with member "spi".
|
||||||
|
|
||||||
|
There should be one element of the sdio_ifs[] array for each SDIO interface object.
|
||||||
|
|
||||||
|
There should be one element of the sd_cards[] array for each SD card slot.
|
||||||
|
* Each element of sd_cards[] must point to its interface with spi_if_p or sdio_if_p.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hardware configuration for Pico SD Card Development Board
|
||||||
|
See https://oshwlab.com/carlk3/rp2040-sd-card-dev
|
||||||
|
|
||||||
|
See https://docs.google.com/spreadsheets/d/1BrzLWTyifongf_VQCc2IpJqXWtsrjmG7KnIbSBy-CPU/edit?usp=sharing,
|
||||||
|
tab "Dev Brd", for pin assignments assumed in this configuration file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
//
|
||||||
|
#include "hw_config.h"
|
||||||
|
|
||||||
|
/* SDIO Interfaces */
|
||||||
|
/*
|
||||||
|
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
|
||||||
|
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
|
||||||
|
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
|
||||||
|
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
|
||||||
|
which is -2 in mod32 arithmetic, so:
|
||||||
|
CLK_gpio = D0_gpio -2.
|
||||||
|
D1_gpio = D0_gpio + 1;
|
||||||
|
D2_gpio = D0_gpio + 2;
|
||||||
|
D3_gpio = D0_gpio + 3;
|
||||||
|
*/
|
||||||
|
static sd_sdio_if_t sdio_ifs[] = {
|
||||||
|
{ // sdio_ifs[0]
|
||||||
|
.CMD_gpio = 3,
|
||||||
|
.D0_gpio = 4,
|
||||||
|
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.SDIO_PIO = pio1,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
},
|
||||||
|
{ // sdio_ifs[1]
|
||||||
|
.CMD_gpio = 17,
|
||||||
|
.D0_gpio = 18,
|
||||||
|
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
|
||||||
|
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
|
||||||
|
.DMA_IRQ_num = DMA_IRQ_1,
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
|
||||||
|
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
|
||||||
|
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
|
||||||
|
//.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Hardware Configuration of the SD Card "objects"
|
||||||
|
These correspond to SD card sockets
|
||||||
|
*/
|
||||||
|
static sd_card_t sd_cards[] = { // One for each SD card
|
||||||
|
{ // sd_cards[0]
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[0], // Pointer to the SPI interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 9,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
},
|
||||||
|
{ // sd_cards[1]
|
||||||
|
.type = SD_IF_SDIO,
|
||||||
|
.sdio_if_p = &sdio_ifs[1], // Pointer to the interface driving this card
|
||||||
|
// SD Card detect:
|
||||||
|
.use_card_detect = true,
|
||||||
|
.card_detect_gpio = 22,
|
||||||
|
.card_detected_true = 0, // What the GPIO read returns when a card is
|
||||||
|
// present.
|
||||||
|
.card_detect_use_pull = true,
|
||||||
|
.card_detect_pull_hi = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// See http://elm-chan.org/fsw/ff/doc/config.html#volumes
|
||||||
|
// and ffconf.h
|
||||||
|
const char *VolumeStr[FF_VOLUMES] = {"sd0", "sd1"}; /* Pre-defined volume ID */
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
size_t sd_get_num() { return count_of(sd_cards); }
|
||||||
|
|
||||||
|
sd_card_t *sd_get_by_num(size_t num) {
|
||||||
|
assert(num < sd_get_num());
|
||||||
|
if (num < sd_get_num()) {
|
||||||
|
return &sd_cards[num];
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [] END OF FILE */
|
|
@ -0,0 +1,296 @@
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Configurations of FatFs Module
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define FFCONF_DEF 80286 /* Revision ID */
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Function Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define FF_FS_READONLY 0
|
||||||
|
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||||
|
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||||
|
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||||
|
/ and optional writing functions as well. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_FS_MINIMIZE 0
|
||||||
|
/* This option defines minimization level to remove some basic API functions.
|
||||||
|
/
|
||||||
|
/ 0: Basic functions are fully enabled.
|
||||||
|
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||||
|
/ are removed.
|
||||||
|
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||||
|
/ 3: f_lseek() function is removed in addition to 2. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_FIND 1
|
||||||
|
/* This option switches filtered directory read functions, f_findfirst() and
|
||||||
|
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_MKFS 1
|
||||||
|
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_FASTSEEK 1
|
||||||
|
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_EXPAND 1
|
||||||
|
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_CHMOD 0
|
||||||
|
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||||
|
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_LABEL 0
|
||||||
|
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||||
|
/ (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_FORWARD 0
|
||||||
|
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_STRFUNC 1
|
||||||
|
#define FF_PRINT_LLI 1
|
||||||
|
#define FF_PRINT_FLOAT 1
|
||||||
|
#define FF_STRF_ENCODE 3
|
||||||
|
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
|
||||||
|
/ f_printf().
|
||||||
|
/
|
||||||
|
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
|
||||||
|
/ 1: Enable without LF-CRLF conversion.
|
||||||
|
/ 2: Enable with LF-CRLF conversion.
|
||||||
|
/
|
||||||
|
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
|
||||||
|
/ makes f_printf() support floating point argument. These features want C99 or later.
|
||||||
|
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
|
||||||
|
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
|
||||||
|
/ to be read/written via those functions.
|
||||||
|
/
|
||||||
|
/ 0: ANSI/OEM in current CP
|
||||||
|
/ 1: Unicode in UTF-16LE
|
||||||
|
/ 2: Unicode in UTF-16BE
|
||||||
|
/ 3: Unicode in UTF-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Locale and Namespace Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define FF_CODE_PAGE 437
|
||||||
|
/* This option specifies the OEM code page to be used on the target system.
|
||||||
|
/ Incorrect code page setting can cause a file open failure.
|
||||||
|
/
|
||||||
|
/ 437 - U.S.
|
||||||
|
/ 720 - Arabic
|
||||||
|
/ 737 - Greek
|
||||||
|
/ 771 - KBL
|
||||||
|
/ 775 - Baltic
|
||||||
|
/ 850 - Latin 1
|
||||||
|
/ 852 - Latin 2
|
||||||
|
/ 855 - Cyrillic
|
||||||
|
/ 857 - Turkish
|
||||||
|
/ 860 - Portuguese
|
||||||
|
/ 861 - Icelandic
|
||||||
|
/ 862 - Hebrew
|
||||||
|
/ 863 - Canadian French
|
||||||
|
/ 864 - Arabic
|
||||||
|
/ 865 - Nordic
|
||||||
|
/ 866 - Russian
|
||||||
|
/ 869 - Greek 2
|
||||||
|
/ 932 - Japanese (DBCS)
|
||||||
|
/ 936 - Simplified Chinese (DBCS)
|
||||||
|
/ 949 - Korean (DBCS)
|
||||||
|
/ 950 - Traditional Chinese (DBCS)
|
||||||
|
/ 0 - Include all code pages above and configured by f_setcp()
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_LFN 3
|
||||||
|
#define FF_MAX_LFN 255
|
||||||
|
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||||
|
/
|
||||||
|
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||||
|
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||||
|
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||||
|
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||||
|
/
|
||||||
|
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||||
|
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||||
|
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||||
|
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||||
|
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
|
||||||
|
/ specification.
|
||||||
|
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||||
|
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||||
|
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_LFN_UNICODE 2
|
||||||
|
/* This option switches the character encoding on the API when LFN is enabled.
|
||||||
|
/
|
||||||
|
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||||
|
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||||
|
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||||
|
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||||
|
/
|
||||||
|
/ Also behavior of string I/O functions will be affected by this option.
|
||||||
|
/ When LFN is not enabled, this option has no effect. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_LFN_BUF 255
|
||||||
|
#define FF_SFN_BUF 12
|
||||||
|
/* This set of options defines size of file name members in the FILINFO structure
|
||||||
|
/ which is used to read out directory items. These values should be suffcient for
|
||||||
|
/ the file names to read. The maximum possible length of the read file name depends
|
||||||
|
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_FS_RPATH 2
|
||||||
|
/* This option configures support for relative path.
|
||||||
|
/
|
||||||
|
/ 0: Disable relative path and remove related functions.
|
||||||
|
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||||
|
/ 2: f_getcwd() function is available in addition to 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Drive/Volume Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
# define FF_VOLUMES 4
|
||||||
|
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_STR_VOLUME_ID 2
|
||||||
|
// #define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
|
||||||
|
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||||
|
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||||
|
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||||
|
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||||
|
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||||
|
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||||
|
/ not defined, a user defined volume string table is needed as:
|
||||||
|
/
|
||||||
|
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_MULTI_PARTITION 0
|
||||||
|
/* This option switches support for multiple volumes on the physical drive.
|
||||||
|
/ By default (0), each logical drive number is bound to the same physical drive
|
||||||
|
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||||
|
/ When this function is enabled (1), each logical drive number can be bound to
|
||||||
|
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||||
|
/ function will be available. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_MIN_SS 512
|
||||||
|
#define FF_MAX_SS 512
|
||||||
|
/* This set of options configures the range of sector size to be supported. (512,
|
||||||
|
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||||
|
/ harddisk, but a larger value may be required for on-board flash memory and some
|
||||||
|
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||||
|
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||||
|
/ GET_SECTOR_SIZE command. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_LBA64 1
|
||||||
|
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
|
||||||
|
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_MIN_GPT 0x10000000
|
||||||
|
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
|
||||||
|
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_USE_TRIM 0
|
||||||
|
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||||
|
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||||
|
/ disk_ioctl() function. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ System Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define FF_FS_TINY 0
|
||||||
|
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||||
|
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||||
|
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||||
|
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_FS_EXFAT 1
|
||||||
|
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||||
|
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||||
|
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_FS_NORTC 0
|
||||||
|
#define FF_NORTC_MON 1
|
||||||
|
#define FF_NORTC_MDAY 1
|
||||||
|
#define FF_NORTC_YEAR 2022
|
||||||
|
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
|
||||||
|
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
|
||||||
|
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
|
||||||
|
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||||
|
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||||
|
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||||
|
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||||
|
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_FS_NOFSINFO 0
|
||||||
|
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||||
|
/ option, and f_getfree() function at the first time after volume mount will force
|
||||||
|
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||||
|
/
|
||||||
|
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||||
|
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||||
|
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||||
|
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_FS_LOCK 16
|
||||||
|
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||||
|
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||||
|
/ is 1.
|
||||||
|
/
|
||||||
|
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||||
|
/ should avoid illegal open, remove and rename to the open objects.
|
||||||
|
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||||
|
/ can be opened simultaneously under file lock control. Note that the file
|
||||||
|
/ lock control is independent of re-entrancy. */
|
||||||
|
|
||||||
|
|
||||||
|
#define FF_FS_REENTRANT 0
|
||||||
|
#define FF_FS_TIMEOUT 1000
|
||||||
|
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||||
|
/ module itself. Note that regardless of this option, file access to different
|
||||||
|
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||||
|
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||||
|
/ to the same volume is under control of this featuer.
|
||||||
|
/
|
||||||
|
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
|
||||||
|
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||||
|
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
|
||||||
|
/ function, must be added to the project. Samples are available in ffsystem.c.
|
||||||
|
/
|
||||||
|
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--- End of configuration options ---*/
|
|
@ -0,0 +1,100 @@
|
||||||
|
/* main.c
|
||||||
|
Copyright 2021 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This example illustrates use of Unix style drive prefix.
|
||||||
|
See http://elm-chan.org/fsw/ff/doc/filename.html. */
|
||||||
|
|
||||||
|
/* For this example, we
|
||||||
|
#define FF_STR_VOLUME_ID 2
|
||||||
|
in include/ffconf.h, add
|
||||||
|
const char *VolumeStr[] = {"sd0", "sd1"};
|
||||||
|
in hw_config.c, and add include to the target_include_directories
|
||||||
|
in CMakeLists.txt.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Expected output:
|
||||||
|
Hello, world!
|
||||||
|
Writing to /sd0/file0.txt
|
||||||
|
Writing to /sd1/file1.txt
|
||||||
|
Goodbye, world!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* NOTE: for this to work, src\ff15\source\ffconf.h must be removed or renamed. */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
//
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
//
|
||||||
|
#include "f_util.h"
|
||||||
|
#include "hw_config.h"
|
||||||
|
#include "sd_card.h"
|
||||||
|
|
||||||
|
static bool write_file(const char *pathname) {
|
||||||
|
printf("Writing to %s\n", pathname);
|
||||||
|
FIL fil = {};
|
||||||
|
FRESULT fr = f_open(&fil, pathname, FA_OPEN_APPEND | FA_WRITE);
|
||||||
|
if (FR_OK != fr && FR_EXIST != fr) {
|
||||||
|
printf("f_open(%s) error: %s (%d)\n", pathname, FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (f_printf(&fil, "Hello, world!\n") < 0) {
|
||||||
|
printf("f_printf failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fr = f_close(&fil);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
// This must be called before sd_get_drive_prefix:
|
||||||
|
sd_init_driver();
|
||||||
|
|
||||||
|
puts("Hello, world!");
|
||||||
|
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sd_get_num(); ++i) {
|
||||||
|
sd_card_t *sd_card_p = sd_get_by_num(i);
|
||||||
|
assert(sd_card_p);
|
||||||
|
char const *drive_prefix = sd_get_drive_prefix(sd_card_p);
|
||||||
|
|
||||||
|
FRESULT fr = f_mount(&sd_card_p->state.fatfs, drive_prefix, 1);
|
||||||
|
if (FR_OK != fr) {
|
||||||
|
printf("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
char buf[128];
|
||||||
|
snprintf(buf, sizeof buf, "%s/file%d.txt", drive_prefix, i);
|
||||||
|
if (!write_file(buf))
|
||||||
|
exit(2);
|
||||||
|
|
||||||
|
// ls(drive_prefix);
|
||||||
|
|
||||||
|
f_unmount(drive_prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("Goodbye, world!");
|
||||||
|
for (;;)
|
||||||
|
;
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,249 @@
|
||||||
|
/* FatFsSd.h
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// See FatFs - Generic FAT Filesystem Module, "Application Interface",
|
||||||
|
// http://elm-chan.org/fsw/ff/00index_e.html
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
//
|
||||||
|
#include "FatFsSd_C.h"
|
||||||
|
//
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace FatFsNs {
|
||||||
|
|
||||||
|
class SdCard {
|
||||||
|
protected:
|
||||||
|
sd_card_t *m_sd_card_p = 0;
|
||||||
|
SdCard() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
SdCard(sd_card_t *sd_card_p):
|
||||||
|
m_sd_card_p(sd_card_p) {};
|
||||||
|
|
||||||
|
SdCard(const SdCard&) = default;
|
||||||
|
|
||||||
|
const char* get_name() { return sd_get_drive_prefix(m_sd_card_p); }
|
||||||
|
|
||||||
|
FRESULT mount() {
|
||||||
|
return f_mount(&m_sd_card_p->state.fatfs, sd_get_drive_prefix(m_sd_card_p), 1);
|
||||||
|
}
|
||||||
|
FRESULT unmount() {
|
||||||
|
return f_unmount(sd_get_drive_prefix(m_sd_card_p));
|
||||||
|
}
|
||||||
|
static FRESULT getfree(const TCHAR* path, DWORD* nclst, FATFS** fatfs) { /* Get number of free clusters on the drive */
|
||||||
|
return f_getfree(path, nclst, fatfs);
|
||||||
|
}
|
||||||
|
static FRESULT getlabel(const TCHAR* path, TCHAR* label, DWORD* vsn) { /* Get volume label */
|
||||||
|
return f_getlabel(path, label, vsn);
|
||||||
|
}
|
||||||
|
static FRESULT setlabel(const TCHAR* label) { /* Set volume label */
|
||||||
|
return f_setlabel(label);
|
||||||
|
}
|
||||||
|
static FRESULT mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len) { /* Create a FAT volume */
|
||||||
|
return f_mkfs(path, opt, work, len);
|
||||||
|
}
|
||||||
|
/* Create a FAT volume: format with defaults */
|
||||||
|
FRESULT format() {
|
||||||
|
const char* name = get_name();
|
||||||
|
return mkfs(name, 0, 0, FF_MAX_SS * 2);
|
||||||
|
}
|
||||||
|
static FRESULT fdisk(BYTE pdrv, const LBA_t ptbl[], void* work) { /* Divide a physical drive into some partitions */
|
||||||
|
return f_fdisk(pdrv, ptbl, work);
|
||||||
|
}
|
||||||
|
// bool readCID(cid_t* cid) {
|
||||||
|
// return m_sd_card_p->sd_readCID(&m_sd_card, cid);
|
||||||
|
// }
|
||||||
|
FATFS* fatfs() {
|
||||||
|
return &m_sd_card_p->state.fatfs;
|
||||||
|
}
|
||||||
|
uint64_t get_num_sectors() {
|
||||||
|
return m_sd_card_p->get_num_sectors(m_sd_card_p);
|
||||||
|
}
|
||||||
|
void cidDmp(printer_t printer) {
|
||||||
|
::cidDmp(m_sd_card_p, printer);
|
||||||
|
}
|
||||||
|
void csdDmp(printer_t printer) {
|
||||||
|
::csdDmp(m_sd_card_p, printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend sd_card_t* ::sd_get_by_num(size_t num);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FatFs {
|
||||||
|
// static std::vector<Spi> Spis;
|
||||||
|
static std::vector<SdCard> SdCards;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static SdCard* add_sd_card(SdCard& SdCard) {
|
||||||
|
SdCards.push_back(SdCard);
|
||||||
|
return &SdCards.back();
|
||||||
|
}
|
||||||
|
static SdCard* add_sd_card(sd_card_t *sd_card_p) {
|
||||||
|
SdCards.push_back(SdCard(sd_card_p));
|
||||||
|
return &SdCards.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
static FRESULT chdrive(const TCHAR* path) {
|
||||||
|
return f_chdrive(path);
|
||||||
|
}
|
||||||
|
static FRESULT setcp(WORD cp) { /* Set current code page */
|
||||||
|
return f_setcp(cp);
|
||||||
|
}
|
||||||
|
static bool begin();
|
||||||
|
|
||||||
|
static size_t SdCard_get_num() {
|
||||||
|
return SdCards.size();
|
||||||
|
}
|
||||||
|
static SdCard* SdCard_get_by_num(size_t num) {
|
||||||
|
if (num <= SdCard_get_num()) {
|
||||||
|
return &SdCards[num];
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static SdCard* SdCard_get_by_name(const char* const name) {
|
||||||
|
for (size_t i = 0; i < SdCard_get_num(); ++i)
|
||||||
|
if (0 == strcmp(SdCard_get_by_num(i)->get_name(), name))
|
||||||
|
return SdCard_get_by_num(i);
|
||||||
|
// printf("%s: unknown name %s\n", __func__, name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class File {
|
||||||
|
FIL fil;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~File() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
FRESULT open(const TCHAR* path, BYTE mode) { /* Open or create a file */
|
||||||
|
return f_open(&fil, path, mode);
|
||||||
|
}
|
||||||
|
FRESULT close() { /* Close an open file object */
|
||||||
|
return f_close(&fil);
|
||||||
|
}
|
||||||
|
FRESULT read(void* buff, UINT btr, UINT* br) { /* Read data from the file */
|
||||||
|
return f_read(&fil, buff, btr, br);
|
||||||
|
}
|
||||||
|
FRESULT write(const void* buff, UINT btw, UINT* bw) { /* Write data to the file */
|
||||||
|
return f_write(&fil, buff, btw, bw);
|
||||||
|
}
|
||||||
|
FRESULT lseek(FSIZE_t ofs) { /* Move file pointer of the file object */
|
||||||
|
return f_lseek(&fil, ofs);
|
||||||
|
}
|
||||||
|
/* Prepares or allocates a contiguous data area to the file: */
|
||||||
|
FRESULT expand(uint64_t file_size) {
|
||||||
|
return f_expand(&fil, file_size, 1);
|
||||||
|
}
|
||||||
|
FRESULT truncate() { /* Truncate the file */
|
||||||
|
return f_truncate(&fil);
|
||||||
|
}
|
||||||
|
FRESULT sync() { /* Flush cached data of the writing file */
|
||||||
|
return f_sync(&fil);
|
||||||
|
}
|
||||||
|
int putc(TCHAR c) { /* Put a character to the file */
|
||||||
|
return f_putc(c, &fil);
|
||||||
|
}
|
||||||
|
int puts(const TCHAR* str) { /* Put a string to the file */
|
||||||
|
return f_puts(str, &fil);
|
||||||
|
}
|
||||||
|
int printf(const TCHAR* str, ...); /* Put a formatted string to the file. Returns -1 on error. */
|
||||||
|
TCHAR* gets(TCHAR* buff, int len) { /* Get a string from the file */
|
||||||
|
return f_gets(buff, len, &fil);
|
||||||
|
}
|
||||||
|
bool eof() {
|
||||||
|
return f_eof(&fil);
|
||||||
|
}
|
||||||
|
BYTE error() {
|
||||||
|
return f_error(&fil);
|
||||||
|
}
|
||||||
|
FSIZE_t tell() {
|
||||||
|
return f_tell(&fil);
|
||||||
|
}
|
||||||
|
FSIZE_t size() {
|
||||||
|
return f_size(&fil);
|
||||||
|
}
|
||||||
|
FRESULT rewind() {
|
||||||
|
return f_rewind(&fil);
|
||||||
|
}
|
||||||
|
FRESULT forward(UINT (*func)(const BYTE*, UINT), UINT btf, UINT* bf) { /* Forward data to the stream */
|
||||||
|
return f_forward(&fil, func, btf, bf);
|
||||||
|
}
|
||||||
|
FRESULT expand(FSIZE_t fsz, BYTE opt) { /* Allocate a contiguous block to the file */
|
||||||
|
return f_expand(&fil, fsz, opt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Dir {
|
||||||
|
DIR dir = {};
|
||||||
|
|
||||||
|
public:
|
||||||
|
~Dir() {
|
||||||
|
closedir();
|
||||||
|
}
|
||||||
|
FRESULT rewinddir() {
|
||||||
|
return f_rewinddir(&dir);
|
||||||
|
}
|
||||||
|
FRESULT rmdir(const TCHAR* path) {
|
||||||
|
return f_rmdir(path);
|
||||||
|
}
|
||||||
|
FRESULT opendir(const TCHAR* path) { /* Open a directory */
|
||||||
|
return f_opendir(&dir, path);
|
||||||
|
}
|
||||||
|
FRESULT closedir() { /* Close an open directory */
|
||||||
|
return f_closedir(&dir);
|
||||||
|
}
|
||||||
|
FRESULT readdir(FILINFO* fno) { /* Read a directory item */
|
||||||
|
return f_readdir(&dir, fno);
|
||||||
|
}
|
||||||
|
FRESULT findfirst(FILINFO* fno, const TCHAR* path, const TCHAR* pattern) { /* Find first file */
|
||||||
|
return f_findfirst(&dir, fno, path, pattern);
|
||||||
|
}
|
||||||
|
FRESULT findnext(FILINFO* fno) { /* Find next file */
|
||||||
|
return f_findnext(&dir, fno);
|
||||||
|
}
|
||||||
|
static FRESULT mkdir(const TCHAR* path) { /* Create a sub directory */
|
||||||
|
return f_mkdir(path);
|
||||||
|
}
|
||||||
|
static FRESULT unlink(const TCHAR* path) { /* Delete an existing file or directory */
|
||||||
|
return f_unlink(path);
|
||||||
|
}
|
||||||
|
static FRESULT rename(const TCHAR* path_old, const TCHAR* path_new) { /* Rename/Move a file or directory */
|
||||||
|
return f_rename(path_old, path_new);
|
||||||
|
}
|
||||||
|
static FRESULT stat(const TCHAR* path, FILINFO* fno) { /* Get file status */
|
||||||
|
return f_stat(path, fno);
|
||||||
|
}
|
||||||
|
static FRESULT chmod(const TCHAR* path, BYTE attr, BYTE mask) { /* Change attribute of a file/dir */
|
||||||
|
return f_chmod(path, attr, mask);
|
||||||
|
}
|
||||||
|
static FRESULT utime(const TCHAR* path, const FILINFO* fno) { /* Change timestamp of a file/dir */
|
||||||
|
return f_utime(path, fno);
|
||||||
|
}
|
||||||
|
static FRESULT chdir(const TCHAR* path) { /* Change current directory */
|
||||||
|
return f_chdir(path);
|
||||||
|
}
|
||||||
|
static FRESULT chdrive(const TCHAR* path) { /* Change current drive */
|
||||||
|
return f_chdrive(path);
|
||||||
|
}
|
||||||
|
static FRESULT getcwd(TCHAR* buff, UINT len) { /* Get current directory */
|
||||||
|
return f_getcwd(buff, len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace FatFsNs
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* FatFsSd_C.h
|
||||||
|
Copyright 2023 Carl John Kugler III
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// #include "pico/stdlib.h"
|
||||||
|
// #include "hardware/sync.h"
|
||||||
|
// #include "pico/sync.h"
|
||||||
|
// #include "hardware/gpio.h"
|
||||||
|
//
|
||||||
|
#include "../src/ff15/source/ff.h"
|
||||||
|
//
|
||||||
|
#include "../src/ff15/source/diskio.h" /* Declarations of disk functions */
|
||||||
|
#include "../src/include/f_util.h"
|
||||||
|
#include "../src/include/rtc.h"
|
||||||
|
#include "../src/sd_driver/sd_card.h"
|
||||||
|
#include "../src/sd_driver/SDIO/rp2040_sdio.h"
|
||||||
|
#include "../src/sd_driver/SPI/my_spi.h"
|
||||||
|
#include "../src/include/hw_config.h"
|
|
@ -0,0 +1,219 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "FsLib.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
FsBaseFile::FsBaseFile(const FsBaseFile& from) {
|
||||||
|
m_fFile = nullptr;
|
||||||
|
m_xFile = nullptr;
|
||||||
|
if (from.m_fFile) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
*m_fFile = *from.m_fFile;
|
||||||
|
} else if (from.m_xFile) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
*m_xFile = *from.m_xFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
FsBaseFile& FsBaseFile::operator=(const FsBaseFile& from) {
|
||||||
|
if (this == &from) {return *this;}
|
||||||
|
close();
|
||||||
|
if (from.m_fFile) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
*m_fFile = *from.m_fFile;
|
||||||
|
} else if (from.m_xFile) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
*m_xFile = *from.m_xFile;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::close() {
|
||||||
|
bool rtn = m_fFile ? m_fFile->close() : m_xFile ? m_xFile->close() : true;
|
||||||
|
m_fFile = nullptr;
|
||||||
|
m_xFile = nullptr;
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::mkdir(FsBaseFile* dir, const char* path, bool pFlag) {
|
||||||
|
close();
|
||||||
|
if (dir->m_fFile) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
if (m_fFile->mkdir(dir->m_fFile, path, pFlag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_fFile = nullptr;
|
||||||
|
} else if (dir->m_xFile) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
if (m_xFile->mkdir(dir->m_xFile, path, pFlag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_xFile = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::open(FsVolume* vol, const char* path, oflag_t oflag) {
|
||||||
|
if (!vol) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
if (vol->m_fVol) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
if (m_fFile && m_fFile->open(vol->m_fVol, path, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_fFile = nullptr;
|
||||||
|
} else if (vol->m_xVol) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
if (m_xFile && m_xFile->open(vol->m_xVol, path, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_xFile = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::open(FsBaseFile* dir, const char* path, oflag_t oflag) {
|
||||||
|
close();
|
||||||
|
if (dir->m_fFile) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
if (m_fFile->open(dir->m_fFile, path, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_fFile = nullptr;
|
||||||
|
} else if (dir->m_xFile) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
if (m_xFile->open(dir->m_xFile, path, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_xFile = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::open(FsBaseFile* dir, uint32_t index, oflag_t oflag) {
|
||||||
|
close();
|
||||||
|
if (dir->m_fFile) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
if (m_fFile->open(dir->m_fFile, index, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_fFile = nullptr;
|
||||||
|
} else if (dir->m_xFile) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
if (m_xFile->open(dir->m_xFile, index, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_xFile = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::openCwd() {
|
||||||
|
close();
|
||||||
|
if (FsVolume::m_cwv && FsVolume::m_cwv->m_fVol) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
if (m_fFile->openCwd()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_fFile = nullptr;
|
||||||
|
} else if (FsVolume::m_cwv && FsVolume::m_cwv->m_xVol) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
if (m_xFile->openCwd()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_xFile = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::openNext(FsBaseFile* dir, oflag_t oflag) {
|
||||||
|
close();
|
||||||
|
if (dir->m_fFile) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
if (m_fFile->openNext(dir->m_fFile, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_fFile = nullptr;
|
||||||
|
} else if (dir->m_xFile) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
if (m_xFile->openNext(dir->m_xFile, oflag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_xFile = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::openRoot(FsVolume* vol) {
|
||||||
|
if (!vol) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
if (vol->m_fVol) {
|
||||||
|
m_fFile = new (m_fileMem) FatFile;
|
||||||
|
if (m_fFile && m_fFile->openRoot(vol->m_fVol)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_fFile = nullptr;
|
||||||
|
} else if (vol->m_xVol) {
|
||||||
|
m_xFile = new (m_fileMem) ExFatFile;
|
||||||
|
if (m_xFile && m_xFile->openRoot(vol->m_xVol)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_xFile = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::remove() {
|
||||||
|
if (m_fFile) {
|
||||||
|
if (m_fFile->remove()) {
|
||||||
|
m_fFile = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (m_xFile) {
|
||||||
|
if (m_xFile->remove()) {
|
||||||
|
m_xFile = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsBaseFile::rmdir() {
|
||||||
|
if (m_fFile) {
|
||||||
|
if (m_fFile->rmdir()) {
|
||||||
|
m_fFile = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (m_xFile) {
|
||||||
|
if (m_xFile->rmdir()) {
|
||||||
|
m_xFile = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,858 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef FsFile_h
|
||||||
|
#define FsFile_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief FsBaseFile include file.
|
||||||
|
*/
|
||||||
|
#include "FsNew.h"
|
||||||
|
#include "FatLib/FatLib.h"
|
||||||
|
#include "ExFatLib/ExFatLib.h"
|
||||||
|
/**
|
||||||
|
* \class FsBaseFile
|
||||||
|
* \brief FsBaseFile class.
|
||||||
|
*/
|
||||||
|
class FsBaseFile {
|
||||||
|
public:
|
||||||
|
/** Create an instance. */
|
||||||
|
FsBaseFile() {}
|
||||||
|
/** Create a file object and open it in the current working directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path for a file to be opened.
|
||||||
|
*
|
||||||
|
* \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
|
||||||
|
* OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
|
||||||
|
*/
|
||||||
|
FsBaseFile(const char* path, oflag_t oflag) {
|
||||||
|
open(path, oflag);
|
||||||
|
}
|
||||||
|
|
||||||
|
~FsBaseFile() {close();}
|
||||||
|
/** Copy constructor.
|
||||||
|
*
|
||||||
|
* \param[in] from Object used to initialize this instance.
|
||||||
|
*/
|
||||||
|
FsBaseFile(const FsBaseFile& from);
|
||||||
|
/** Copy assignment operator
|
||||||
|
* \param[in] from Object used to initialize this instance.
|
||||||
|
* \return assigned object.
|
||||||
|
*/
|
||||||
|
FsBaseFile& operator=(const FsBaseFile& from);
|
||||||
|
/** The parenthesis operator.
|
||||||
|
*
|
||||||
|
* \return true if a file is open.
|
||||||
|
*/
|
||||||
|
operator bool() const {return isOpen();}
|
||||||
|
/**
|
||||||
|
* \return user settable file attributes for success else -1.
|
||||||
|
*/
|
||||||
|
int attrib() {
|
||||||
|
return m_fFile ? m_fFile->attrib() :
|
||||||
|
m_xFile ? m_xFile->attrib() : -1;
|
||||||
|
}
|
||||||
|
/** Set file attributes
|
||||||
|
*
|
||||||
|
* \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
|
||||||
|
* FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
|
||||||
|
*
|
||||||
|
* \note attrib() will fail for set read-only if the file is open for write.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool attrib(uint8_t bits) {
|
||||||
|
return m_fFile ? m_fFile->attrib(bits) :
|
||||||
|
m_xFile ? m_xFile->attrib(bits) : false;
|
||||||
|
}
|
||||||
|
/** \return number of bytes available from the current position to EOF
|
||||||
|
* or INT_MAX if more than INT_MAX bytes are available.
|
||||||
|
*/
|
||||||
|
int available() const {
|
||||||
|
return m_fFile ? m_fFile->available() :
|
||||||
|
m_xFile ? m_xFile->available() : 0;
|
||||||
|
}
|
||||||
|
/** \return The number of bytes available from the current position
|
||||||
|
* to EOF for normal files. Zero is returned for directory files.
|
||||||
|
*/
|
||||||
|
uint64_t available64() const {
|
||||||
|
return m_fFile ? m_fFile->available32() :
|
||||||
|
m_xFile ? m_xFile->available64() : 0;
|
||||||
|
}
|
||||||
|
/** Clear writeError. */
|
||||||
|
void clearWriteError() {
|
||||||
|
if (m_fFile) m_fFile->clearWriteError();
|
||||||
|
if (m_xFile) m_xFile->clearWriteError();
|
||||||
|
}
|
||||||
|
/** Close a file and force cached data and directory information
|
||||||
|
* to be written to the storage device.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool close();
|
||||||
|
/** Check for contiguous file and return its raw sector range.
|
||||||
|
*
|
||||||
|
* \param[out] bgnSector the first sector address for the file.
|
||||||
|
* \param[out] endSector the last sector address for the file.
|
||||||
|
*
|
||||||
|
* Set contiguous flag for FAT16/FAT32 files.
|
||||||
|
* Parameters may be nullptr.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector) {
|
||||||
|
return m_fFile ? m_fFile->contiguousRange(bgnSector, endSector) :
|
||||||
|
m_xFile ? m_xFile->contiguousRange(bgnSector, endSector) : false;
|
||||||
|
}
|
||||||
|
/** \return The current cluster number for a file or directory. */
|
||||||
|
uint32_t curCluster() const {
|
||||||
|
return m_fFile ? m_fFile->curCluster() :
|
||||||
|
m_xFile ? m_xFile->curCluster() : 0;
|
||||||
|
}
|
||||||
|
/** \return The current position for a file or directory. */
|
||||||
|
uint64_t curPosition() const {
|
||||||
|
return m_fFile ? m_fFile->curPosition() :
|
||||||
|
m_xFile ? m_xFile->curPosition() : 0;
|
||||||
|
}
|
||||||
|
/** \return Directory entry index. */
|
||||||
|
uint32_t dirIndex() const {
|
||||||
|
return m_fFile ? m_fFile->dirIndex() :
|
||||||
|
m_xFile ? m_xFile->dirIndex() : 0;
|
||||||
|
}
|
||||||
|
/** Test for the existence of a file in a directory
|
||||||
|
*
|
||||||
|
* \param[in] path Path of the file to be tested for.
|
||||||
|
*
|
||||||
|
* The calling instance must be an open directory file.
|
||||||
|
*
|
||||||
|
* dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory
|
||||||
|
* dirFile.
|
||||||
|
*
|
||||||
|
* \return true if the file exists else false.
|
||||||
|
*/
|
||||||
|
bool exists(const char* path) {
|
||||||
|
return m_fFile ? m_fFile->exists(path) :
|
||||||
|
m_xFile ? m_xFile->exists(path) : false;
|
||||||
|
}
|
||||||
|
/** get position for streams
|
||||||
|
* \param[out] pos struct to receive position
|
||||||
|
*/
|
||||||
|
void fgetpos(fspos_t* pos) const {
|
||||||
|
if (m_fFile) m_fFile->fgetpos(pos);
|
||||||
|
if (m_xFile) m_xFile->fgetpos(pos);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get a string from a file.
|
||||||
|
*
|
||||||
|
* fgets() reads bytes from a file into the array pointed to by \a str, until
|
||||||
|
* \a num - 1 bytes are read, or a delimiter is read and transferred to \a str,
|
||||||
|
* or end-of-file is encountered. The string is then terminated
|
||||||
|
* with a null byte.
|
||||||
|
*
|
||||||
|
* fgets() deletes CR, '\\r', from the string. This insures only a '\\n'
|
||||||
|
* terminates the string for Windows text files which use CRLF for newline.
|
||||||
|
*
|
||||||
|
* \param[out] str Pointer to the array where the string is stored.
|
||||||
|
* \param[in] num Maximum number of characters to be read
|
||||||
|
* (including the final null byte). Usually the length
|
||||||
|
* of the array \a str is used.
|
||||||
|
* \param[in] delim Optional set of delimiters. The default is "\n".
|
||||||
|
*
|
||||||
|
* \return For success fgets() returns the length of the string in \a str.
|
||||||
|
* If no data is read, fgets() returns zero for EOF or -1 if an error occurred.
|
||||||
|
*/
|
||||||
|
int fgets(char* str, int num, char* delim = nullptr) {
|
||||||
|
return m_fFile ? m_fFile->fgets(str, num, delim) :
|
||||||
|
m_xFile ? m_xFile->fgets(str, num, delim) : -1;
|
||||||
|
}
|
||||||
|
/** \return The total number of bytes in a file. */
|
||||||
|
uint64_t fileSize() const {
|
||||||
|
return m_fFile ? m_fFile->fileSize() :
|
||||||
|
m_xFile ? m_xFile->fileSize() : 0;
|
||||||
|
}
|
||||||
|
/** \return Address of first sector or zero for empty file. */
|
||||||
|
uint32_t firstSector() const {
|
||||||
|
return m_fFile ? m_fFile->firstSector() :
|
||||||
|
m_xFile ? m_xFile->firstSector() : 0;
|
||||||
|
}
|
||||||
|
/** Ensure that any bytes written to the file are saved to the SD card. */
|
||||||
|
void flush() {sync();}
|
||||||
|
/** set position for streams
|
||||||
|
* \param[in] pos struct with value for new position
|
||||||
|
*/
|
||||||
|
void fsetpos(const fspos_t* pos) {
|
||||||
|
if (m_fFile) m_fFile->fsetpos(pos);
|
||||||
|
if (m_xFile) m_xFile->fsetpos(pos);
|
||||||
|
}
|
||||||
|
/** Get a file's access date and time.
|
||||||
|
*
|
||||||
|
* \param[out] pdate Packed date for directory entry.
|
||||||
|
* \param[out] ptime Packed time for directory entry.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool getAccessDateTime(uint16_t* pdate, uint16_t* ptime) {
|
||||||
|
return m_fFile ? m_fFile->getAccessDateTime(pdate, ptime) :
|
||||||
|
m_xFile ? m_xFile->getAccessDateTime(pdate, ptime) : false;
|
||||||
|
}
|
||||||
|
/** Get a file's create date and time.
|
||||||
|
*
|
||||||
|
* \param[out] pdate Packed date for directory entry.
|
||||||
|
* \param[out] ptime Packed time for directory entry.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool getCreateDateTime(uint16_t* pdate, uint16_t* ptime) {
|
||||||
|
return m_fFile ? m_fFile->getCreateDateTime(pdate, ptime) :
|
||||||
|
m_xFile ? m_xFile->getCreateDateTime(pdate, ptime) : false;
|
||||||
|
}
|
||||||
|
/** \return All error bits. */
|
||||||
|
uint8_t getError() const {
|
||||||
|
return m_fFile ? m_fFile->getError() :
|
||||||
|
m_xFile ? m_xFile->getError() : 0XFF;
|
||||||
|
}
|
||||||
|
/** Get a file's Modify date and time.
|
||||||
|
*
|
||||||
|
* \param[out] pdate Packed date for directory entry.
|
||||||
|
* \param[out] ptime Packed time for directory entry.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime) {
|
||||||
|
return m_fFile ? m_fFile->getModifyDateTime(pdate, ptime) :
|
||||||
|
m_xFile ? m_xFile->getModifyDateTime(pdate, ptime) : false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get a file's name followed by a zero byte.
|
||||||
|
*
|
||||||
|
* \param[out] name An array of characters for the file's name.
|
||||||
|
* \param[in] len The size of the array in bytes. The array
|
||||||
|
* must be at least 13 bytes long. The file's name will be
|
||||||
|
* truncated if the file's name is too long.
|
||||||
|
* \return The length of the returned string.
|
||||||
|
*/
|
||||||
|
size_t getName(char* name, size_t len) {
|
||||||
|
*name = 0;
|
||||||
|
return m_fFile ? m_fFile->getName(name, len) :
|
||||||
|
m_xFile ? m_xFile->getName(name, len) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \return value of writeError */
|
||||||
|
bool getWriteError() const {
|
||||||
|
return m_fFile ? m_fFile->getWriteError() :
|
||||||
|
m_xFile ? m_xFile->getWriteError() : true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check for FsBlockDevice busy.
|
||||||
|
*
|
||||||
|
* \return true if busy else false.
|
||||||
|
*/
|
||||||
|
bool isBusy() {
|
||||||
|
return m_fFile ? m_fFile->isBusy() :
|
||||||
|
m_xFile ? m_xFile->isBusy() : true;
|
||||||
|
}
|
||||||
|
/** \return True if the file is contiguous. */
|
||||||
|
bool isContiguous() const {
|
||||||
|
#if USE_FAT_FILE_FLAG_CONTIGUOUS
|
||||||
|
return m_fFile ? m_fFile->isContiguous() :
|
||||||
|
m_xFile ? m_xFile->isContiguous() : false;
|
||||||
|
#else // USE_FAT_FILE_FLAG_CONTIGUOUS
|
||||||
|
return m_xFile ? m_xFile->isContiguous() : false;
|
||||||
|
#endif // USE_FAT_FILE_FLAG_CONTIGUOUS
|
||||||
|
}
|
||||||
|
/** \return True if this is a directory else false. */
|
||||||
|
bool isDir() const {
|
||||||
|
return m_fFile ? m_fFile->isDir() :
|
||||||
|
m_xFile ? m_xFile->isDir() : false;
|
||||||
|
}
|
||||||
|
/** This function reports if the current file is a directory or not.
|
||||||
|
* \return true if the file is a directory.
|
||||||
|
*/
|
||||||
|
bool isDirectory() const {return isDir();}
|
||||||
|
/** \return True if this is a normal file. */
|
||||||
|
bool isFile() const {
|
||||||
|
return m_fFile ? m_fFile->isFile() :
|
||||||
|
m_xFile ? m_xFile->isFile() : false;
|
||||||
|
}
|
||||||
|
/** \return True if this is a normal file or sub-directory. */
|
||||||
|
bool isFileOrSubDir() const {
|
||||||
|
return m_fFile ? m_fFile->isFileOrSubDir() :
|
||||||
|
m_xFile ? m_xFile->isFileOrSubDir() : false;
|
||||||
|
}
|
||||||
|
/** \return True if this is a hidden file else false. */
|
||||||
|
bool isHidden() const {
|
||||||
|
return m_fFile ? m_fFile->isHidden() :
|
||||||
|
m_xFile ? m_xFile->isHidden() : false;
|
||||||
|
}
|
||||||
|
/** \return True if this is an open file/directory else false. */
|
||||||
|
bool isOpen() const {return m_fFile || m_xFile;}
|
||||||
|
/** \return True file is readable. */
|
||||||
|
bool isReadable() const {
|
||||||
|
return m_fFile ? m_fFile->isReadable() :
|
||||||
|
m_xFile ? m_xFile->isReadable() : false;
|
||||||
|
}
|
||||||
|
/** \return True if file is read-only */
|
||||||
|
bool isReadOnly() const {
|
||||||
|
return m_fFile ? m_fFile->isReadOnly() :
|
||||||
|
m_xFile ? m_xFile->isReadOnly() : false;
|
||||||
|
}
|
||||||
|
/** \return True if this is a sub-directory file else false. */
|
||||||
|
bool isSubDir() const {
|
||||||
|
return m_fFile ? m_fFile->isSubDir() :
|
||||||
|
m_xFile ? m_xFile->isSubDir() : false;
|
||||||
|
}
|
||||||
|
/** \return True file is writable. */
|
||||||
|
bool isWritable() const {
|
||||||
|
return m_fFile ? m_fFile->isWritable() :
|
||||||
|
m_xFile ? m_xFile->isWritable() : false;
|
||||||
|
}
|
||||||
|
#if ENABLE_ARDUINO_SERIAL
|
||||||
|
/** List directory contents.
|
||||||
|
*
|
||||||
|
* \param[in] flags The inclusive OR of
|
||||||
|
*
|
||||||
|
* LS_DATE - %Print file modification date
|
||||||
|
*
|
||||||
|
* LS_SIZE - %Print file size.
|
||||||
|
*
|
||||||
|
* LS_R - Recursive list of subdirectories.
|
||||||
|
*/
|
||||||
|
bool ls(uint8_t flags) {
|
||||||
|
return ls(&Serial, flags);
|
||||||
|
}
|
||||||
|
/** List directory contents. */
|
||||||
|
bool ls() {
|
||||||
|
return ls(&Serial);
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ARDUINO_SERIAL
|
||||||
|
/** List directory contents.
|
||||||
|
*
|
||||||
|
* \param[in] pr Print object.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls(print_t* pr) {
|
||||||
|
return m_fFile ? m_fFile->ls(pr) :
|
||||||
|
m_xFile ? m_xFile->ls(pr) : false;
|
||||||
|
}
|
||||||
|
/** List directory contents.
|
||||||
|
*
|
||||||
|
* \param[in] pr Print object.
|
||||||
|
* \param[in] flags The inclusive OR of
|
||||||
|
*
|
||||||
|
* LS_DATE - %Print file modification date
|
||||||
|
*
|
||||||
|
* LS_SIZE - %Print file size.
|
||||||
|
*
|
||||||
|
* LS_R - Recursive list of subdirectories.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls(print_t* pr, uint8_t flags) {
|
||||||
|
return m_fFile ? m_fFile->ls(pr, flags) :
|
||||||
|
m_xFile ? m_xFile->ls(pr, flags) : false;
|
||||||
|
}
|
||||||
|
/** Make a new directory.
|
||||||
|
*
|
||||||
|
* \param[in] dir An open FatFile instance for the directory that will
|
||||||
|
* contain the new directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for the new directory.
|
||||||
|
*
|
||||||
|
* \param[in] pFlag Create missing parent directories if true.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool mkdir(FsBaseFile* dir, const char* path, bool pFlag = true);
|
||||||
|
/** Open a file or directory by name.
|
||||||
|
*
|
||||||
|
* \param[in] dir An open file instance for the directory containing
|
||||||
|
* the file to be opened.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
|
||||||
|
*
|
||||||
|
* \param[in] oflag Values for \a oflag are constructed by a
|
||||||
|
* bitwise-inclusive OR of flags from the following list
|
||||||
|
*
|
||||||
|
* O_RDONLY - Open for reading only..
|
||||||
|
*
|
||||||
|
* O_READ - Same as O_RDONLY.
|
||||||
|
*
|
||||||
|
* O_WRONLY - Open for writing only.
|
||||||
|
*
|
||||||
|
* O_WRITE - Same as O_WRONLY.
|
||||||
|
*
|
||||||
|
* O_RDWR - Open for reading and writing.
|
||||||
|
*
|
||||||
|
* O_APPEND - If set, the file offset shall be set to the end of the
|
||||||
|
* file prior to each write.
|
||||||
|
*
|
||||||
|
* O_AT_END - Set the initial position at the end of the file.
|
||||||
|
*
|
||||||
|
* O_CREAT - If the file exists, this flag has no effect except as noted
|
||||||
|
* under O_EXCL below. Otherwise, the file shall be created
|
||||||
|
*
|
||||||
|
* O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
|
||||||
|
*
|
||||||
|
* O_TRUNC - If the file exists and is a regular file, and the file is
|
||||||
|
* successfully opened and is not read only, its length shall be truncated to 0.
|
||||||
|
*
|
||||||
|
* WARNING: A given file must not be opened by more than one file object
|
||||||
|
* or file corruption may occur.
|
||||||
|
*
|
||||||
|
* \note Directory files must be opened read only. Write and truncation is
|
||||||
|
* not allowed for directory files.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool open(FsBaseFile* dir, const char* path, oflag_t oflag = O_RDONLY);
|
||||||
|
/** Open a file by index.
|
||||||
|
*
|
||||||
|
* \param[in] dir An open FsFile instance for the directory.
|
||||||
|
*
|
||||||
|
* \param[in] index The \a index of the directory entry for the file to be
|
||||||
|
* opened. The value for \a index is (directory file position)/32.
|
||||||
|
*
|
||||||
|
* \param[in] oflag bitwise-inclusive OR of open flags.
|
||||||
|
* See see FsFile::open(FsFile*, const char*, uint8_t).
|
||||||
|
*
|
||||||
|
* See open() by path for definition of flags.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool open(FsBaseFile* dir, uint32_t index, oflag_t oflag = O_RDONLY);
|
||||||
|
/** Open a file or directory by name.
|
||||||
|
*
|
||||||
|
* \param[in] vol Volume where the file is located.
|
||||||
|
*
|
||||||
|
* \param[in] path A path for a file to be opened.
|
||||||
|
*
|
||||||
|
* \param[in] oflag Values for \a oflag are constructed by a
|
||||||
|
* bitwise-inclusive OR of open flags.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool open(FsVolume* vol, const char* path, oflag_t oflag = O_RDONLY);
|
||||||
|
/** Open a file or directory by name.
|
||||||
|
*
|
||||||
|
* \param[in] path A path for a file to be opened.
|
||||||
|
*
|
||||||
|
* \param[in] oflag Values for \a oflag are constructed by a
|
||||||
|
* bitwise-inclusive OR of open flags.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool open(const char* path, oflag_t oflag = O_RDONLY) {
|
||||||
|
return FsVolume::m_cwv && open(FsVolume::m_cwv, path, oflag);
|
||||||
|
}
|
||||||
|
/** Open a file or directory by index in the current working directory.
|
||||||
|
*
|
||||||
|
* \param[in] index The \a index of the directory entry for the file to be
|
||||||
|
* opened. The value for \a index is (directory file position)/32.
|
||||||
|
*
|
||||||
|
* \param[in] oflag Values for \a oflag are constructed by a
|
||||||
|
* bitwise-inclusive OR of open flags.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool open(uint32_t index, oflag_t oflag = O_RDONLY) {
|
||||||
|
FsBaseFile cwd;
|
||||||
|
return cwd.openCwd() && open(&cwd, index, oflag);
|
||||||
|
}
|
||||||
|
/** Open the current working directory.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool openCwd();
|
||||||
|
/** Opens the next file or folder in a directory.
|
||||||
|
* \param[in] dir directory containing files.
|
||||||
|
* \param[in] oflag open flags.
|
||||||
|
* \return a file object.
|
||||||
|
*/
|
||||||
|
bool openNext(FsBaseFile* dir, oflag_t oflag = O_RDONLY);
|
||||||
|
/** Open a volume's root directory.
|
||||||
|
*
|
||||||
|
* \param[in] vol The SdFs volume containing the root directory to be opened.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool openRoot(FsVolume* vol);
|
||||||
|
/** \return the current file position. */
|
||||||
|
uint64_t position() const {return curPosition();}
|
||||||
|
/** Return the next available byte without consuming it.
|
||||||
|
*
|
||||||
|
* \return The byte if no error and not at eof else -1;
|
||||||
|
*/
|
||||||
|
int peek() {
|
||||||
|
return m_fFile ? m_fFile->peek() :
|
||||||
|
m_xFile ? m_xFile->peek() : -1;
|
||||||
|
}
|
||||||
|
/** Allocate contiguous clusters to an empty file.
|
||||||
|
*
|
||||||
|
* The file must be empty with no clusters allocated.
|
||||||
|
*
|
||||||
|
* The file will contain uninitialized data for FAT16/FAT32 files.
|
||||||
|
* exFAT files will have zero validLength and dataLength will equal
|
||||||
|
* the requested length.
|
||||||
|
*
|
||||||
|
* \param[in] length size of the file in bytes.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool preAllocate(uint64_t length) {
|
||||||
|
return m_fFile ? length < (1ULL << 32) && m_fFile->preAllocate(length) :
|
||||||
|
m_xFile ? m_xFile->preAllocate(length) : false;
|
||||||
|
}
|
||||||
|
/** Print a file's access date and time
|
||||||
|
*
|
||||||
|
* \param[in] pr Print stream for output.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
size_t printAccessDateTime(print_t* pr) {
|
||||||
|
return m_fFile ? m_fFile->printAccessDateTime(pr) :
|
||||||
|
m_xFile ? m_xFile->printAccessDateTime(pr) : 0;
|
||||||
|
}
|
||||||
|
/** Print a file's creation date and time
|
||||||
|
*
|
||||||
|
* \param[in] pr Print stream for output.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
size_t printCreateDateTime(print_t* pr) {
|
||||||
|
return m_fFile ? m_fFile->printCreateDateTime(pr) :
|
||||||
|
m_xFile ? m_xFile->printCreateDateTime(pr) : 0;
|
||||||
|
}
|
||||||
|
/** Print a number followed by a field terminator.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] term The field terminator. Use '\\n' for CR LF.
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
size_t printField(double value, char term, uint8_t prec = 2) {
|
||||||
|
return m_fFile ? m_fFile->printField(value, term, prec) :
|
||||||
|
m_xFile ? m_xFile->printField(value, term, prec) : 0;
|
||||||
|
}
|
||||||
|
/** Print a number followed by a field terminator.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] term The field terminator. Use '\\n' for CR LF.
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
size_t printField(float value, char term, uint8_t prec = 2) {
|
||||||
|
return printField(static_cast<double>(value), term, prec);
|
||||||
|
}
|
||||||
|
/** Print a number followed by a field terminator.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] term The field terminator. Use '\\n' for CR LF.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
size_t printField(Type value, char term) {
|
||||||
|
return m_fFile ? m_fFile->printField(value, term) :
|
||||||
|
m_xFile ? m_xFile->printField(value, term) : 0;
|
||||||
|
}
|
||||||
|
/** Print a file's size.
|
||||||
|
*
|
||||||
|
* \param[in] pr Print stream for output.
|
||||||
|
*
|
||||||
|
* \return The number of characters printed is returned
|
||||||
|
* for success and zero is returned for failure.
|
||||||
|
*/
|
||||||
|
size_t printFileSize(print_t* pr) {
|
||||||
|
return m_fFile ? m_fFile->printFileSize(pr) :
|
||||||
|
m_xFile ? m_xFile->printFileSize(pr) : 0;
|
||||||
|
}
|
||||||
|
/** Print a file's modify date and time
|
||||||
|
*
|
||||||
|
* \param[in] pr Print stream for output.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
size_t printModifyDateTime(print_t* pr) {
|
||||||
|
return m_fFile ? m_fFile->printModifyDateTime(pr) :
|
||||||
|
m_xFile ? m_xFile->printModifyDateTime(pr) : 0;
|
||||||
|
}
|
||||||
|
/** Print a file's name
|
||||||
|
*
|
||||||
|
* \param[in] pr Print stream for output.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
size_t printName(print_t* pr) {
|
||||||
|
return m_fFile ? m_fFile->printName(pr) :
|
||||||
|
m_xFile ? m_xFile->printName(pr) : 0;
|
||||||
|
}
|
||||||
|
/** Read the next byte from a file.
|
||||||
|
*
|
||||||
|
* \return For success return the next byte in the file as an int.
|
||||||
|
* If an error occurs or end of file is reached return -1.
|
||||||
|
*/
|
||||||
|
int read() {
|
||||||
|
uint8_t b;
|
||||||
|
return read(&b, 1) == 1 ? b : -1;
|
||||||
|
}
|
||||||
|
/** Read data from a file starting at the current position.
|
||||||
|
*
|
||||||
|
* \param[out] buf Pointer to the location that will receive the data.
|
||||||
|
*
|
||||||
|
* \param[in] count Maximum number of bytes to read.
|
||||||
|
*
|
||||||
|
* \return For success read() returns the number of bytes read.
|
||||||
|
* A value less than \a count, including zero, will be returned
|
||||||
|
* if end of file is reached.
|
||||||
|
* If an error occurs, read() returns -1. Possible errors include
|
||||||
|
* read() called before a file has been opened, corrupt file system
|
||||||
|
* or an I/O error occurred.
|
||||||
|
*/
|
||||||
|
int read(void* buf, size_t count) {
|
||||||
|
return m_fFile ? m_fFile->read(buf, count) :
|
||||||
|
m_xFile ? m_xFile->read(buf, count) : -1;
|
||||||
|
}
|
||||||
|
/** Remove a file.
|
||||||
|
*
|
||||||
|
* The directory entry and all data for the file are deleted.
|
||||||
|
*
|
||||||
|
* \note This function should not be used to delete the 8.3 version of a
|
||||||
|
* file that has a long name. For example if a file has the long name
|
||||||
|
* "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool remove();
|
||||||
|
/** Remove a file.
|
||||||
|
*
|
||||||
|
* The directory entry and all data for the file are deleted.
|
||||||
|
*
|
||||||
|
* \param[in] path Path for the file to be removed.
|
||||||
|
*
|
||||||
|
* Example use: dirFile.remove(filenameToRemove);
|
||||||
|
*
|
||||||
|
* \note This function should not be used to delete the 8.3 version of a
|
||||||
|
* file that has a long name. For example if a file has the long name
|
||||||
|
* "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool remove(const char* path) {
|
||||||
|
return m_fFile ? m_fFile->remove(path) :
|
||||||
|
m_xFile ? m_xFile->remove(path) : false;
|
||||||
|
}
|
||||||
|
/** Rename a file or subdirectory.
|
||||||
|
*
|
||||||
|
* \param[in] newPath New path name for the file/directory.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rename(const char* newPath) {
|
||||||
|
return m_fFile ? m_fFile->rename(newPath) :
|
||||||
|
m_xFile ? m_xFile->rename(newPath) : false;
|
||||||
|
}
|
||||||
|
/** Rename a file or subdirectory.
|
||||||
|
*
|
||||||
|
* \param[in] dir Directory for the new path.
|
||||||
|
* \param[in] newPath New path name for the file/directory.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rename(FsBaseFile* dir, const char* newPath) {
|
||||||
|
return m_fFile && dir->m_fFile ? m_fFile->rename(dir->m_fFile, newPath) :
|
||||||
|
m_xFile && dir->m_xFile ? m_xFile->rename(dir->m_xFile, newPath) :
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
/** Set the file's current position to zero. */
|
||||||
|
void rewind() {
|
||||||
|
if (m_fFile) m_fFile->rewind();
|
||||||
|
if (m_xFile) m_xFile->rewind();
|
||||||
|
}
|
||||||
|
/** Rewind a file if it is a directory */
|
||||||
|
void rewindDirectory() {
|
||||||
|
if (isDir()) rewind();
|
||||||
|
}
|
||||||
|
/** Remove a directory file.
|
||||||
|
*
|
||||||
|
* The directory file will be removed only if it is empty and is not the
|
||||||
|
* root directory. rmdir() follows DOS and Windows and ignores the
|
||||||
|
* read-only attribute for the directory.
|
||||||
|
*
|
||||||
|
* \note This function should not be used to delete the 8.3 version of a
|
||||||
|
* directory that has a long name. For example if a directory has the
|
||||||
|
* long name "New folder" you should not delete the 8.3 name "NEWFOL~1".
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rmdir();
|
||||||
|
/** Seek to a new position in the file, which must be between
|
||||||
|
* 0 and the size of the file (inclusive).
|
||||||
|
*
|
||||||
|
* \param[in] pos the new file position.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool seek(uint64_t pos) {return seekSet(pos);}
|
||||||
|
/** Set the files position to current position + \a pos. See seekSet().
|
||||||
|
* \param[in] offset The new position in bytes from the current position.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool seekCur(int64_t offset) {
|
||||||
|
return seekSet(curPosition() + offset);
|
||||||
|
}
|
||||||
|
/** Set the files position to end-of-file + \a offset. See seekSet().
|
||||||
|
* Can't be used for directory files since file size is not defined.
|
||||||
|
* \param[in] offset The new position in bytes from end-of-file.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool seekEnd(int64_t offset = 0) {
|
||||||
|
return seekSet(fileSize() + offset);
|
||||||
|
}
|
||||||
|
/** Sets a file's position.
|
||||||
|
*
|
||||||
|
* \param[in] pos The new position in bytes from the beginning of the file.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool seekSet(uint64_t pos) {
|
||||||
|
return m_fFile ? pos < (1ULL << 32) && m_fFile->seekSet(pos) :
|
||||||
|
m_xFile ? m_xFile->seekSet(pos) : false;
|
||||||
|
}
|
||||||
|
/** \return the file's size. */
|
||||||
|
uint64_t size() const {return fileSize();}
|
||||||
|
/** The sync() call causes all modified data and directory fields
|
||||||
|
* to be written to the storage device.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool sync() {
|
||||||
|
return m_fFile ? m_fFile->sync() :
|
||||||
|
m_xFile ? m_xFile->sync() : false;
|
||||||
|
}
|
||||||
|
/** Set a file's timestamps in its directory entry.
|
||||||
|
*
|
||||||
|
* \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
|
||||||
|
* OR of flags from the following list
|
||||||
|
*
|
||||||
|
* T_ACCESS - Set the file's last access date and time.
|
||||||
|
*
|
||||||
|
* T_CREATE - Set the file's creation date and time.
|
||||||
|
*
|
||||||
|
* T_WRITE - Set the file's last write/modification date and time.
|
||||||
|
*
|
||||||
|
* \param[in] year Valid range 1980 - 2107 inclusive.
|
||||||
|
*
|
||||||
|
* \param[in] month Valid range 1 - 12 inclusive.
|
||||||
|
*
|
||||||
|
* \param[in] day Valid range 1 - 31 inclusive.
|
||||||
|
*
|
||||||
|
* \param[in] hour Valid range 0 - 23 inclusive.
|
||||||
|
*
|
||||||
|
* \param[in] minute Valid range 0 - 59 inclusive.
|
||||||
|
*
|
||||||
|
* \param[in] second Valid range 0 - 59 inclusive
|
||||||
|
*
|
||||||
|
* \note It is possible to set an invalid date since there is no check for
|
||||||
|
* the number of days in a month.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* Modify and access timestamps may be overwritten if a date time callback
|
||||||
|
* function has been set by dateTimeCallback().
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day,
|
||||||
|
uint8_t hour, uint8_t minute, uint8_t second) {
|
||||||
|
return m_fFile ?
|
||||||
|
m_fFile->timestamp(flags, year, month, day, hour, minute, second) :
|
||||||
|
m_xFile ?
|
||||||
|
m_xFile->timestamp(flags, year, month, day, hour, minute, second) :
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
/** Truncate a file to the current position.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool truncate() {
|
||||||
|
return m_fFile ? m_fFile->truncate() :
|
||||||
|
m_xFile ? m_xFile->truncate() : false;
|
||||||
|
}
|
||||||
|
/** Truncate a file to a specified length.
|
||||||
|
* The current file position will be set to end of file.
|
||||||
|
*
|
||||||
|
* \param[in] length The desired length for the file.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool truncate(uint64_t length) {
|
||||||
|
return m_fFile ? length < (1ULL << 32) && m_fFile->truncate(length) :
|
||||||
|
m_xFile ? m_xFile->truncate(length) : false;
|
||||||
|
}
|
||||||
|
/** Write a string to a file. Used by the Arduino Print class.
|
||||||
|
* \param[in] str Pointer to the string.
|
||||||
|
* Use getWriteError to check for errors.
|
||||||
|
* \return count of characters written for success or -1 for failure.
|
||||||
|
*/
|
||||||
|
size_t write(const char* str) {
|
||||||
|
return write(str, strlen(str));
|
||||||
|
}
|
||||||
|
/** Write a byte to a file. Required by the Arduino Print class.
|
||||||
|
* \param[in] b the byte to be written.
|
||||||
|
* Use getWriteError to check for errors.
|
||||||
|
* \return 1 for success and 0 for failure.
|
||||||
|
*/
|
||||||
|
size_t write(uint8_t b) {return write(&b, 1);}
|
||||||
|
/** Write data to an open file.
|
||||||
|
*
|
||||||
|
* \note Data is moved to the cache but may not be written to the
|
||||||
|
* storage device until sync() is called.
|
||||||
|
*
|
||||||
|
* \param[in] buf Pointer to the location of the data to be written.
|
||||||
|
*
|
||||||
|
* \param[in] count Number of bytes to write.
|
||||||
|
*
|
||||||
|
* \return For success write() returns the number of bytes written, always
|
||||||
|
* \a nbyte. If an error occurs, write() returns zero and writeError is set.
|
||||||
|
*/
|
||||||
|
size_t write(const void* buf, size_t count) {
|
||||||
|
return m_fFile ? m_fFile->write(buf, count) :
|
||||||
|
m_xFile ? m_xFile->write(buf, count) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
newalign_t m_fileMem[FS_ALIGN_DIM(ExFatFile, FatFile)];
|
||||||
|
FatFile* m_fFile = nullptr;
|
||||||
|
ExFatFile* m_xFile = nullptr;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* \class FsFile
|
||||||
|
* \brief FsBaseFile file with Arduino Stream.
|
||||||
|
*/
|
||||||
|
class FsFile : public StreamFile<FsBaseFile, uint64_t> {
|
||||||
|
public:
|
||||||
|
/** Opens the next file or folder in a directory.
|
||||||
|
*
|
||||||
|
* \param[in] oflag open flags.
|
||||||
|
* \return a FatStream object.
|
||||||
|
*/
|
||||||
|
FsFile openNextFile(oflag_t oflag = O_RDONLY) {
|
||||||
|
FsFile tmpFile;
|
||||||
|
tmpFile.openNext(this, oflag);
|
||||||
|
return tmpFile;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // FsFile_h
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef FsFormatter_h
|
||||||
|
#define FsFormatter_h
|
||||||
|
#include "FatLib/FatLib.h"
|
||||||
|
#include "ExFatLib/ExFatLib.h"
|
||||||
|
/**
|
||||||
|
* \class FsFormatter
|
||||||
|
* \brief Format a exFAT/FAT volume.
|
||||||
|
*/
|
||||||
|
class FsFormatter {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Format a FAT volume.
|
||||||
|
*
|
||||||
|
* \param[in] dev Block device for volume.
|
||||||
|
* \param[in] secBuffer buffer for writing to volume.
|
||||||
|
* \param[in] pr Print device for progress output.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool format(FsBlockDevice* dev, uint8_t* secBuffer, print_t* pr = nullptr) {
|
||||||
|
uint32_t sectorCount = dev->sectorCount();
|
||||||
|
if (sectorCount == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return sectorCount <= 67108864 ?
|
||||||
|
m_fFmt.format(dev, secBuffer, pr) :
|
||||||
|
m_xFmt.format(dev, secBuffer, pr);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
FatFormatter m_fFmt;
|
||||||
|
ExFatFormatter m_xFmt;
|
||||||
|
};
|
||||||
|
#endif // FsFormatter_h
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef FsLib_h
|
||||||
|
#define FsLib_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief FsLib include file.
|
||||||
|
*/
|
||||||
|
#include "FsVolume.h"
|
||||||
|
#include "FsFile.h"
|
||||||
|
#include "FsFormatter.h"
|
||||||
|
#endif // FsLib_h
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "FsNew.h"
|
||||||
|
void* operator new(size_t size, newalign_t* ptr) {
|
||||||
|
(void)size;
|
||||||
|
return ptr;
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef FsNew_h
|
||||||
|
#define FsNew_h
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/** 32-bit alignment */
|
||||||
|
typedef uint32_t newalign_t;
|
||||||
|
|
||||||
|
/** Size required for exFAT or FAT class. */
|
||||||
|
#define FS_SIZE(etype, ftype) \
|
||||||
|
(sizeof(ftype) < sizeof(etype) ? sizeof(etype) : sizeof(ftype))
|
||||||
|
|
||||||
|
/** Dimension of aligned area. */
|
||||||
|
#define NEW_ALIGN_DIM(n) \
|
||||||
|
(((size_t)(n) + sizeof(newalign_t) - 1U)/sizeof(newalign_t))
|
||||||
|
|
||||||
|
/** Dimension of aligned area for etype or ftype class. */
|
||||||
|
#define FS_ALIGN_DIM(etype, ftype) NEW_ALIGN_DIM(FS_SIZE(etype, ftype))
|
||||||
|
|
||||||
|
/** Custom new placement operator */
|
||||||
|
void* operator new(size_t size, newalign_t* ptr);
|
||||||
|
#endif // FsNew_h
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "FsLib.h"
|
||||||
|
FsVolume* FsVolume::m_cwv = nullptr;
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsVolume::begin(FsBlockDevice* blockDev, bool setCwv,
|
||||||
|
uint8_t part, uint32_t volStart) {
|
||||||
|
m_blockDev = blockDev;
|
||||||
|
m_fVol = nullptr;
|
||||||
|
m_xVol = new (m_volMem) ExFatVolume;
|
||||||
|
if (m_xVol && m_xVol->begin(m_blockDev, false, part, volStart)) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
m_xVol = nullptr;
|
||||||
|
m_fVol = new (m_volMem) FatVolume;
|
||||||
|
if (m_fVol && m_fVol->begin(m_blockDev, false, part, volStart)) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
m_fVol = nullptr;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (setCwv || !m_cwv) {
|
||||||
|
m_cwv = this;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool FsVolume::ls(print_t* pr, const char* path, uint8_t flags) {
|
||||||
|
FsBaseFile dir;
|
||||||
|
return dir.open(this, path, O_RDONLY) && dir.ls(pr, flags);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
FsFile FsVolume::open(const char *path, oflag_t oflag) {
|
||||||
|
FsFile tmpFile;
|
||||||
|
tmpFile.open(this, path, oflag);
|
||||||
|
return tmpFile;
|
||||||
|
}
|
||||||
|
#if ENABLE_ARDUINO_STRING
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
FsFile FsVolume::open(const String &path, oflag_t oflag) {
|
||||||
|
return open(path.c_str(), oflag );
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ARDUINO_STRING
|
|
@ -0,0 +1,410 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef FsVolume_h
|
||||||
|
#define FsVolume_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief FsVolume include file.
|
||||||
|
*/
|
||||||
|
#include "FsNew.h"
|
||||||
|
#include "../FatLib/FatLib.h"
|
||||||
|
#include "../ExFatLib/ExFatLib.h"
|
||||||
|
|
||||||
|
class FsFile;
|
||||||
|
/**
|
||||||
|
* \class FsVolume
|
||||||
|
* \brief FsVolume class.
|
||||||
|
*/
|
||||||
|
class FsVolume {
|
||||||
|
public:
|
||||||
|
FsVolume() {}
|
||||||
|
|
||||||
|
~FsVolume() {end();}
|
||||||
|
/** Get file's user settable attributes.
|
||||||
|
* \param[in] path path to file.
|
||||||
|
* \return user settable file attributes for success else -1.
|
||||||
|
*/
|
||||||
|
int attrib(const char* path) {
|
||||||
|
return m_fVol ? m_fVol->attrib(path) :
|
||||||
|
m_xVol ? m_xVol->attrib(path) : -1;
|
||||||
|
}
|
||||||
|
/** Set file's user settable attributes.
|
||||||
|
* \param[in] path path to file.
|
||||||
|
* \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
|
||||||
|
* FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool attrib(const char* path, uint8_t bits) {
|
||||||
|
return m_fVol ? m_fVol->attrib(path, bits) :
|
||||||
|
m_xVol ? m_xVol->attrib(path, bits) : false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initialize an FatVolume object.
|
||||||
|
* \param[in] blockDev Device block driver.
|
||||||
|
* \param[in] setCwv Set current working volume if true.
|
||||||
|
* \param[in] part partition to initialize.
|
||||||
|
* \param[in] volStart Start sector of volume if part is zero.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool begin(FsBlockDevice* blockDev, bool setCwv = true, uint8_t
|
||||||
|
part = 1, uint32_t volStart = 0);
|
||||||
|
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||||
|
uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
|
||||||
|
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
||||||
|
/** \return the number of bytes in a cluster. */
|
||||||
|
uint32_t bytesPerCluster() const {
|
||||||
|
return m_fVol ? m_fVol->bytesPerCluster() :
|
||||||
|
m_xVol ? m_xVol->bytesPerCluster() : 0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set volume working directory to root.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool chdir() {
|
||||||
|
return m_fVol ? m_fVol->chdir() :
|
||||||
|
m_xVol ? m_xVol->chdir() : false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set volume working directory.
|
||||||
|
* \param[in] path Path for volume working directory.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool chdir(const char* path) {
|
||||||
|
return m_fVol ? m_fVol->chdir(path) :
|
||||||
|
m_xVol ? m_xVol->chdir(path) : false;
|
||||||
|
}
|
||||||
|
/** Change global working volume to this volume. */
|
||||||
|
void chvol() {m_cwv = this;}
|
||||||
|
/** \return The total number of clusters in the volume. */
|
||||||
|
uint32_t clusterCount() const {
|
||||||
|
return m_fVol ? m_fVol->clusterCount() :
|
||||||
|
m_xVol ? m_xVol->clusterCount() : 0;
|
||||||
|
}
|
||||||
|
/** \return The logical sector number for the start of file data. */
|
||||||
|
uint32_t dataStartSector() const {
|
||||||
|
return m_fVol ? m_fVol->dataStartSector() :
|
||||||
|
m_xVol ? m_xVol->clusterHeapStartSector() : 0;
|
||||||
|
}
|
||||||
|
/** End access to volume
|
||||||
|
* \return pointer to sector size buffer for format.
|
||||||
|
*/
|
||||||
|
uint8_t* end() {
|
||||||
|
m_fVol = nullptr;
|
||||||
|
m_xVol = nullptr;
|
||||||
|
static_assert(sizeof(m_volMem) >= 512, "m_volMem too small");
|
||||||
|
return reinterpret_cast<uint8_t*>(m_volMem);
|
||||||
|
}
|
||||||
|
/** Test for the existence of a file in a directory
|
||||||
|
*
|
||||||
|
* \param[in] path Path of the file to be tested for.
|
||||||
|
*
|
||||||
|
* \return true if the file exists else false.
|
||||||
|
*/
|
||||||
|
bool exists(const char* path) {
|
||||||
|
return m_fVol ? m_fVol->exists(path) :
|
||||||
|
m_xVol ? m_xVol->exists(path) : false;
|
||||||
|
}
|
||||||
|
/** \return The logical sector number for the start of the first FAT. */
|
||||||
|
uint32_t fatStartSector() const {
|
||||||
|
return m_fVol ? m_fVol->fatStartSector() :
|
||||||
|
m_xVol ? m_xVol->fatStartSector() : 0;
|
||||||
|
}
|
||||||
|
/** \return Partition type, FAT_TYPE_EXFAT, FAT_TYPE_FAT32,
|
||||||
|
* FAT_TYPE_FAT16, or zero for error.
|
||||||
|
*/
|
||||||
|
uint8_t fatType() const {
|
||||||
|
return m_fVol ? m_fVol->fatType() :
|
||||||
|
m_xVol ? m_xVol->fatType() : 0;
|
||||||
|
}
|
||||||
|
/** \return free cluster count or -1 if an error occurs. */
|
||||||
|
int32_t freeClusterCount() const {
|
||||||
|
return m_fVol ? m_fVol->freeClusterCount() :
|
||||||
|
m_xVol ? m_xVol->freeClusterCount() : -1;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check for device busy.
|
||||||
|
*
|
||||||
|
* \return true if busy else false.
|
||||||
|
*/
|
||||||
|
bool isBusy() {
|
||||||
|
return m_fVol ? m_fVol->isBusy() :
|
||||||
|
m_xVol ? m_xVol->isBusy() : false;
|
||||||
|
}
|
||||||
|
/** List directory contents.
|
||||||
|
*
|
||||||
|
* \param[in] pr Print object.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls(print_t* pr) {
|
||||||
|
return m_fVol ? m_fVol->ls(pr) :
|
||||||
|
m_xVol ? m_xVol->ls(pr) : false;
|
||||||
|
}
|
||||||
|
/** List directory contents.
|
||||||
|
*
|
||||||
|
* \param[in] pr Print object.
|
||||||
|
* \param[in] flags The inclusive OR of
|
||||||
|
*
|
||||||
|
* LS_DATE - %Print file modification date
|
||||||
|
*
|
||||||
|
* LS_SIZE - %Print file size.
|
||||||
|
*
|
||||||
|
* LS_R - Recursive list of subdirectories.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls(print_t* pr, uint8_t flags) {
|
||||||
|
return m_fVol ? m_fVol->ls(pr, flags) :
|
||||||
|
m_xVol ? m_xVol->ls(pr, flags) : false;
|
||||||
|
}
|
||||||
|
/** List the directory contents of a directory.
|
||||||
|
*
|
||||||
|
* \param[in] pr Print stream for list.
|
||||||
|
*
|
||||||
|
* \param[in] path directory to list.
|
||||||
|
*
|
||||||
|
* \param[in] flags The inclusive OR of
|
||||||
|
*
|
||||||
|
* LS_DATE - %Print file modification date
|
||||||
|
*
|
||||||
|
* LS_SIZE - %Print file size.
|
||||||
|
*
|
||||||
|
* LS_R - Recursive list of subdirectories.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls(print_t* pr, const char* path, uint8_t flags);
|
||||||
|
/** Make a subdirectory in the volume root directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
|
||||||
|
*
|
||||||
|
* \param[in] pFlag Create missing parent directories if true.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool mkdir(const char *path, bool pFlag = true) {
|
||||||
|
return m_fVol ? m_fVol->mkdir(path, pFlag) :
|
||||||
|
m_xVol ? m_xVol->mkdir(path, pFlag) : false;
|
||||||
|
}
|
||||||
|
/** open a file
|
||||||
|
*
|
||||||
|
* \param[in] path location of file to be opened.
|
||||||
|
* \param[in] oflag open flags.
|
||||||
|
* \return a FsBaseFile object.
|
||||||
|
*/
|
||||||
|
FsFile open(const char* path, oflag_t oflag = O_RDONLY);
|
||||||
|
/** Remove a file from the volume root directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for the file.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool remove(const char *path) {
|
||||||
|
return m_fVol ? m_fVol->remove(path) :
|
||||||
|
m_xVol ? m_xVol->remove(path) : false;
|
||||||
|
}
|
||||||
|
/** Rename a file or subdirectory.
|
||||||
|
*
|
||||||
|
* \param[in] oldPath Path name to the file or subdirectory to be renamed.
|
||||||
|
*
|
||||||
|
* \param[in] newPath New path name of the file or subdirectory.
|
||||||
|
*
|
||||||
|
* The \a newPath object must not exist before the rename call.
|
||||||
|
*
|
||||||
|
* The file to be renamed must not be open. The directory entry may be
|
||||||
|
* moved and file system corruption could occur if the file is accessed by
|
||||||
|
* a file object that was opened before the rename() call.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rename(const char *oldPath, const char *newPath) {
|
||||||
|
return m_fVol ? m_fVol->rename(oldPath, newPath) :
|
||||||
|
m_xVol ? m_xVol->rename(oldPath, newPath) : false;
|
||||||
|
}
|
||||||
|
/** Remove a subdirectory from the volume's root directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
|
||||||
|
*
|
||||||
|
* The subdirectory file will be removed only if it is empty.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rmdir(const char *path) {
|
||||||
|
return m_fVol ? m_fVol->rmdir(path) :
|
||||||
|
m_xVol ? m_xVol->rmdir(path) : false;
|
||||||
|
}
|
||||||
|
/** \return The volume's cluster size in sectors. */
|
||||||
|
uint32_t sectorsPerCluster() const {
|
||||||
|
return m_fVol ? m_fVol->sectorsPerCluster() :
|
||||||
|
m_xVol ? m_xVol->sectorsPerCluster() : 0;
|
||||||
|
}
|
||||||
|
#if ENABLE_ARDUINO_SERIAL
|
||||||
|
/** List directory contents.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls() {
|
||||||
|
return ls(&Serial);
|
||||||
|
}
|
||||||
|
/** List directory contents.
|
||||||
|
*
|
||||||
|
* \param[in] flags The inclusive OR of
|
||||||
|
*
|
||||||
|
* LS_DATE - %Print file modification date
|
||||||
|
*
|
||||||
|
* LS_SIZE - %Print file size.
|
||||||
|
*
|
||||||
|
* LS_R - Recursive list of subdirectories.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls(uint8_t flags) {
|
||||||
|
return ls(&Serial, flags);
|
||||||
|
}
|
||||||
|
/** List the directory contents of a directory to Serial.
|
||||||
|
*
|
||||||
|
* \param[in] path directory to list.
|
||||||
|
*
|
||||||
|
* \param[in] flags The inclusive OR of
|
||||||
|
*
|
||||||
|
* LS_DATE - %Print file modification date
|
||||||
|
*
|
||||||
|
* LS_SIZE - %Print file size.
|
||||||
|
*
|
||||||
|
* LS_R - Recursive list of subdirectories.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool ls(const char* path, uint8_t flags = 0) {
|
||||||
|
return ls(&Serial, path, flags);
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ARDUINO_SERIAL
|
||||||
|
#if ENABLE_ARDUINO_STRING
|
||||||
|
/**
|
||||||
|
* Set volume working directory.
|
||||||
|
* \param[in] path Path for volume working directory.
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool chdir(const String& path) {
|
||||||
|
return chdir(path.c_str());
|
||||||
|
}
|
||||||
|
/** Test for the existence of a file in a directory
|
||||||
|
*
|
||||||
|
* \param[in] path Path of the file to be tested for.
|
||||||
|
*
|
||||||
|
* \return true if the file exists else false.
|
||||||
|
*/
|
||||||
|
bool exists(const String &path) {
|
||||||
|
return exists(path.c_str());
|
||||||
|
}
|
||||||
|
/** Make a subdirectory in the volume root directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
|
||||||
|
*
|
||||||
|
* \param[in] pFlag Create missing parent directories if true.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool mkdir(const String &path, bool pFlag = true) {
|
||||||
|
return mkdir(path.c_str(), pFlag);
|
||||||
|
}
|
||||||
|
/** open a file
|
||||||
|
*
|
||||||
|
* \param[in] path location of file to be opened.
|
||||||
|
* \param[in] oflag open flags.
|
||||||
|
* \return a FsBaseFile object.
|
||||||
|
*/
|
||||||
|
FsFile open(const String &path, oflag_t oflag = O_RDONLY);
|
||||||
|
/** Remove a file from the volume root directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for the file.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool remove(const String &path) {
|
||||||
|
return remove(path.c_str());
|
||||||
|
}
|
||||||
|
/** Rename a file or subdirectory.
|
||||||
|
*
|
||||||
|
* \param[in] oldPath Path name to the file or subdirectory to be renamed.
|
||||||
|
*
|
||||||
|
* \param[in] newPath New path name of the file or subdirectory.
|
||||||
|
*
|
||||||
|
* The \a newPath object must not exist before the rename call.
|
||||||
|
*
|
||||||
|
* The file to be renamed must not be open. The directory entry may be
|
||||||
|
* moved and file system corruption could occur if the file is accessed by
|
||||||
|
* a file object that was opened before the rename() call.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rename(const String& oldPath, const String& newPath) {
|
||||||
|
return rename(oldPath.c_str(), newPath.c_str());
|
||||||
|
}
|
||||||
|
/** Remove a subdirectory from the volume's root directory.
|
||||||
|
*
|
||||||
|
* \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
|
||||||
|
*
|
||||||
|
* The subdirectory file will be removed only if it is empty.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rmdir(const String &path) {
|
||||||
|
return rmdir(path.c_str());
|
||||||
|
}
|
||||||
|
/** Rename a file or subdirectory.
|
||||||
|
*
|
||||||
|
* \param[in] oldPath Path name to the file or subdirectory to be renamed.
|
||||||
|
*
|
||||||
|
* \param[in] newPath New path name of the file or subdirectory.
|
||||||
|
*
|
||||||
|
* The \a newPath object must not exist before the rename call.
|
||||||
|
*
|
||||||
|
* The file to be renamed must not be open. The directory entry may be
|
||||||
|
* moved and file system corruption could occur if the file is accessed by
|
||||||
|
* a file object that was opened before the rename() call.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
#endif // ENABLE_ARDUINO_STRING
|
||||||
|
|
||||||
|
protected:
|
||||||
|
newalign_t m_volMem[FS_ALIGN_DIM(ExFatVolume, FatVolume)];
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** FsBaseFile allowed access to private members. */
|
||||||
|
friend class FsBaseFile;
|
||||||
|
static FsVolume* cwv() {return m_cwv;}
|
||||||
|
FsVolume(const FsVolume& from);
|
||||||
|
FsVolume& operator=(const FsVolume& from);
|
||||||
|
|
||||||
|
static FsVolume* m_cwv;
|
||||||
|
FatVolume* m_fVol = nullptr;
|
||||||
|
ExFatVolume* m_xVol = nullptr;
|
||||||
|
FsBlockDevice* m_blockDev;
|
||||||
|
};
|
||||||
|
#endif // FsVolume_h
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ArduinoStream_h
|
||||||
|
#define ArduinoStream_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief ArduinoInStream and ArduinoOutStream classes
|
||||||
|
*/
|
||||||
|
#include "bufstream.h"
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class ArduinoInStream
|
||||||
|
* \brief Input stream for Arduino Stream objects
|
||||||
|
*/
|
||||||
|
class ArduinoInStream : public ibufstream {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* \param[in] hws hardware stream
|
||||||
|
* \param[in] buf buffer for input line
|
||||||
|
* \param[in] size size of input buffer
|
||||||
|
*/
|
||||||
|
ArduinoInStream(Stream &hws, char* buf, size_t size) {
|
||||||
|
m_hw = &hws;
|
||||||
|
m_line = buf;
|
||||||
|
m_size = size;
|
||||||
|
}
|
||||||
|
/** read a line. */
|
||||||
|
void readline() {
|
||||||
|
size_t i = 0;
|
||||||
|
uint32_t t;
|
||||||
|
m_line[0] = '\0';
|
||||||
|
while (!m_hw->available()) {
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
t = millis();
|
||||||
|
while (!m_hw->available()) {
|
||||||
|
if ((millis() - t) > 10) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i >= (m_size - 1)) {
|
||||||
|
setstate(failbit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_line[i++] = m_hw->read();
|
||||||
|
m_line[i] = '\0';
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
init(m_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Internal - do not use.
|
||||||
|
* \param[in] off
|
||||||
|
* \param[in] way
|
||||||
|
* \return true/false.
|
||||||
|
*/
|
||||||
|
bool seekoff(off_type off, seekdir way) {
|
||||||
|
(void)off;
|
||||||
|
(void)way;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/** Internal - do not use.
|
||||||
|
* \param[in] pos
|
||||||
|
* \return true/false.
|
||||||
|
*/
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
(void)pos;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *m_line;
|
||||||
|
size_t m_size;
|
||||||
|
Stream* m_hw;
|
||||||
|
};
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class ArduinoOutStream
|
||||||
|
* \brief Output stream for Arduino Print objects
|
||||||
|
*/
|
||||||
|
class ArduinoOutStream : public ostream {
|
||||||
|
public:
|
||||||
|
/** constructor
|
||||||
|
*
|
||||||
|
* \param[in] pr Print object for this ArduinoOutStream.
|
||||||
|
*/
|
||||||
|
explicit ArduinoOutStream(print_t& pr) : m_pr(&pr) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
/**
|
||||||
|
* Internal do not use
|
||||||
|
* \param[in] c
|
||||||
|
*/
|
||||||
|
void putch(char c) {
|
||||||
|
if (c == '\n') {
|
||||||
|
m_pr->write('\r');
|
||||||
|
}
|
||||||
|
m_pr->write(c);
|
||||||
|
}
|
||||||
|
void putstr(const char* str) {
|
||||||
|
m_pr->write(str);
|
||||||
|
}
|
||||||
|
bool seekoff(off_type off, seekdir way) {
|
||||||
|
(void)off;
|
||||||
|
(void)way;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
(void)pos;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool sync() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pos_type tellpos() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
private:
|
||||||
|
ArduinoOutStream() {}
|
||||||
|
print_t* m_pr;
|
||||||
|
};
|
||||||
|
#endif // ArduinoStream_h
|
|
@ -0,0 +1,451 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "StdioStream.h"
|
||||||
|
#include "../common/FmtNumber.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::fclose() {
|
||||||
|
int rtn = 0;
|
||||||
|
if (!m_status) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
if (m_status & S_SWR) {
|
||||||
|
if (!flushBuf()) {
|
||||||
|
rtn = EOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!StreamBaseFile::close()) {
|
||||||
|
rtn = EOF;
|
||||||
|
}
|
||||||
|
m_r = 0;
|
||||||
|
m_w = 0;
|
||||||
|
m_status = 0;
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::fflush() {
|
||||||
|
if ((m_status & (S_SWR | S_SRW)) && !(m_status & S_SRD)) {
|
||||||
|
if (flushBuf() && StreamBaseFile::sync()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
char* StdioStream::fgets(char* str, size_t num, size_t* len) {
|
||||||
|
char* s = str;
|
||||||
|
size_t n;
|
||||||
|
if (num-- == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (num) {
|
||||||
|
if ((n = m_r) == 0) {
|
||||||
|
if (!fillBuf()) {
|
||||||
|
if (s == str) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
n = m_r;
|
||||||
|
}
|
||||||
|
if (n > num) {
|
||||||
|
n = num;
|
||||||
|
}
|
||||||
|
uint8_t* end = reinterpret_cast<uint8_t*>(memchr(m_p, '\n', n));
|
||||||
|
if (end != 0) {
|
||||||
|
n = ++end - m_p;
|
||||||
|
memcpy(s, m_p, n);
|
||||||
|
m_r -= n;
|
||||||
|
m_p = end;
|
||||||
|
s += n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(s, m_p, n);
|
||||||
|
m_r -= n;
|
||||||
|
m_p += n;
|
||||||
|
s += n;
|
||||||
|
num -= n;
|
||||||
|
}
|
||||||
|
*s = 0;
|
||||||
|
if (len) {
|
||||||
|
*len = s - str;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool StdioStream::fopen(const char* path, const char* mode) {
|
||||||
|
oflag_t oflag;
|
||||||
|
uint8_t m;
|
||||||
|
switch (*mode++) {
|
||||||
|
case 'a':
|
||||||
|
m = O_WRONLY;
|
||||||
|
oflag = O_CREAT | O_APPEND;
|
||||||
|
m_status = S_SWR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
m = O_RDONLY;
|
||||||
|
oflag = 0;
|
||||||
|
m_status = S_SRD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'w':
|
||||||
|
m = O_WRONLY;
|
||||||
|
oflag = O_CREAT | O_TRUNC;
|
||||||
|
m_status = S_SWR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
while (*mode) {
|
||||||
|
switch (*mode++) {
|
||||||
|
case '+':
|
||||||
|
m_status = S_SRW;
|
||||||
|
m = O_RDWR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
oflag |= O_EXCL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oflag |= m;
|
||||||
|
if (!StreamBaseFile::open(path, oflag)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
m_r = 0;
|
||||||
|
m_w = 0;
|
||||||
|
m_p = m_buf;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
m_status = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::fputs(const char* str) {
|
||||||
|
size_t len = strlen(str);
|
||||||
|
return fwrite(str, 1, len) == len ? len : EOF;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
size_t StdioStream::fread(void* ptr, size_t size, size_t count) {
|
||||||
|
uint8_t* dst = reinterpret_cast<uint8_t*>(ptr);
|
||||||
|
size_t total = size*count;
|
||||||
|
if (total == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t need = total;
|
||||||
|
while (need > m_r) {
|
||||||
|
memcpy(dst, m_p, m_r);
|
||||||
|
dst += m_r;
|
||||||
|
m_p += m_r;
|
||||||
|
need -= m_r;
|
||||||
|
if (!fillBuf()) {
|
||||||
|
return (total - need)/size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(dst, m_p, need);
|
||||||
|
m_r -= need;
|
||||||
|
m_p += need;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::fseek(int32_t offset, int origin) {
|
||||||
|
int32_t pos;
|
||||||
|
if (m_status & S_SWR) {
|
||||||
|
if (!flushBuf()) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (origin) {
|
||||||
|
case SEEK_CUR:
|
||||||
|
pos = ftell();
|
||||||
|
if (pos < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
pos += offset;
|
||||||
|
if (!StreamBaseFile::seekCur(pos)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEEK_SET:
|
||||||
|
if (!StreamBaseFile::seekSet(offset)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEEK_END:
|
||||||
|
if (!StreamBaseFile::seekEnd(offset)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
m_r = 0;
|
||||||
|
m_p = m_buf;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int32_t StdioStream::ftell() {
|
||||||
|
uint32_t pos = StreamBaseFile::curPosition();
|
||||||
|
if (m_status & S_SRD) {
|
||||||
|
if (m_r > pos) {
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
pos -= m_r;
|
||||||
|
} else if (m_status & S_SWR) {
|
||||||
|
pos += m_p - m_buf;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) {
|
||||||
|
return write(ptr, count*size) < 0 ? EOF : count;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::write(const void* buf, size_t count) {
|
||||||
|
const uint8_t* src = static_cast<const uint8_t*>(buf);
|
||||||
|
size_t todo = count;
|
||||||
|
|
||||||
|
while (todo > m_w) {
|
||||||
|
memcpy(m_p, src, m_w);
|
||||||
|
m_p += m_w;
|
||||||
|
src += m_w;
|
||||||
|
todo -= m_w;
|
||||||
|
if (!flushBuf()) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(m_p, src, todo);
|
||||||
|
m_p += todo;
|
||||||
|
m_w -= todo;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#if (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN)
|
||||||
|
size_t StdioStream::print(const __FlashStringHelper *str) {
|
||||||
|
const char *p = (const char*)str;
|
||||||
|
uint8_t c;
|
||||||
|
while ((c = pgm_read_byte(p))) {
|
||||||
|
if (putc(c) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return p - (const char*)str;
|
||||||
|
}
|
||||||
|
#endif // (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN)
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::printDec(float value, uint8_t prec) {
|
||||||
|
char buf[24];
|
||||||
|
char *ptr = fmtDouble(buf + sizeof(buf), value, prec, false);
|
||||||
|
return write(ptr, buf + sizeof(buf) - ptr);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::printDec(signed char n) {
|
||||||
|
if (n < 0) {
|
||||||
|
if (fputc('-') < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n = -n;
|
||||||
|
}
|
||||||
|
return printDec((unsigned char)n);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::printDec(int16_t n) {
|
||||||
|
int s;
|
||||||
|
uint8_t rtn = 0;
|
||||||
|
if (n < 0) {
|
||||||
|
if (fputc('-') < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n = -n;
|
||||||
|
rtn++;
|
||||||
|
}
|
||||||
|
if ((s = printDec((uint16_t)n)) < 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::printDec(uint16_t n) {
|
||||||
|
char buf[5];
|
||||||
|
char *ptr = fmtBase10(buf + sizeof(buf), n);
|
||||||
|
uint8_t len = buf + sizeof(buf) - ptr;
|
||||||
|
return write(ptr, len);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::printDec(int32_t n) {
|
||||||
|
uint8_t s = 0;
|
||||||
|
if (n < 0) {
|
||||||
|
if (fputc('-') < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n = -n;
|
||||||
|
s = 1;
|
||||||
|
}
|
||||||
|
int rtn = printDec((uint32_t)n);
|
||||||
|
return rtn > 0 ? rtn + s : -1;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::printDec(uint32_t n) {
|
||||||
|
char buf[10];
|
||||||
|
char *ptr = fmtBase10(buf + sizeof(buf), n);
|
||||||
|
uint8_t len = buf + sizeof(buf) - ptr;
|
||||||
|
return write(ptr, len);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::printHex(uint32_t n) {
|
||||||
|
char buf[8];
|
||||||
|
char *ptr = fmtHex(buf + sizeof(buf), n);
|
||||||
|
uint8_t len = buf + sizeof(buf) - ptr;
|
||||||
|
return write(ptr, len);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool StdioStream::rewind() {
|
||||||
|
if (m_status & S_SWR) {
|
||||||
|
if (!flushBuf()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StreamBaseFile::seekSet(0);
|
||||||
|
m_r = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::ungetc(int c) {
|
||||||
|
// error if EOF.
|
||||||
|
if (c == EOF) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
// error if not reading.
|
||||||
|
if ((m_status & S_SRD) == 0) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
// error if no space.
|
||||||
|
if (m_p == m_buf) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
m_r++;
|
||||||
|
m_status &= ~S_EOF;
|
||||||
|
return *--m_p = (uint8_t)c;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
// private
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::fillGet() {
|
||||||
|
if (!fillBuf()) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
m_r--;
|
||||||
|
return *m_p++;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// private
|
||||||
|
bool StdioStream::fillBuf() {
|
||||||
|
if (!(m_status &
|
||||||
|
S_SRD)) { // check for S_ERR and S_EOF ??/////////////////
|
||||||
|
if (!(m_status & S_SRW)) {
|
||||||
|
m_status |= S_ERR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_status & S_SWR) {
|
||||||
|
if (!flushBuf()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_status &= ~S_SWR;
|
||||||
|
m_status |= S_SRD;
|
||||||
|
m_w = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_p = m_buf + UNGETC_BUF_SIZE;
|
||||||
|
int nr = StreamBaseFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE);
|
||||||
|
if (nr <= 0) {
|
||||||
|
m_status |= nr < 0 ? S_ERR : S_EOF;
|
||||||
|
m_r = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_r = nr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// private
|
||||||
|
bool StdioStream::flushBuf() {
|
||||||
|
if (!(m_status &
|
||||||
|
S_SWR)) { // check for S_ERR ??////////////////////////
|
||||||
|
if (!(m_status & S_SRW)) {
|
||||||
|
m_status |= S_ERR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_status &= ~S_SRD;
|
||||||
|
m_status |= S_SWR;
|
||||||
|
m_r = 0;
|
||||||
|
m_w = sizeof(m_buf);
|
||||||
|
m_p = m_buf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint8_t n = m_p - m_buf;
|
||||||
|
m_p = m_buf;
|
||||||
|
m_w = sizeof(m_buf);
|
||||||
|
if (StreamBaseFile::write(m_buf, n) == n) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_status |= S_ERR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StdioStream::flushPut(uint8_t c) {
|
||||||
|
if (!flushBuf()) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
m_w--;
|
||||||
|
return *m_p++ = c;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
char* StdioStream::fmtSpace(uint8_t len) {
|
||||||
|
if (m_w < len) {
|
||||||
|
if (!flushBuf() || m_w < len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len > m_w) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
m_p += len;
|
||||||
|
m_w -= len;
|
||||||
|
return reinterpret_cast<char*>(m_p);
|
||||||
|
}
|
|
@ -0,0 +1,663 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef StdioStream_h
|
||||||
|
#define StdioStream_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief StdioStream class
|
||||||
|
*/
|
||||||
|
#include <limits.h>
|
||||||
|
#include "ios.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** Total size of stream buffer. The entire buffer is used for output.
|
||||||
|
* During input UNGETC_BUF_SIZE of this space is reserved for ungetc.
|
||||||
|
*/
|
||||||
|
const uint8_t STREAM_BUF_SIZE = 64;
|
||||||
|
/** Amount of buffer allocated for ungetc during input. */
|
||||||
|
const uint8_t UNGETC_BUF_SIZE = 2;
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Get rid of any macros defined in <stdio.h>.
|
||||||
|
#include <stdio.h>
|
||||||
|
#undef clearerr
|
||||||
|
#undef fclose
|
||||||
|
#undef feof
|
||||||
|
#undef ferror
|
||||||
|
#undef fflush
|
||||||
|
#undef fgetc
|
||||||
|
#undef fgetpos
|
||||||
|
#undef fgets
|
||||||
|
#undef fopen
|
||||||
|
#undef fprintf
|
||||||
|
#undef fputc
|
||||||
|
#undef fputs
|
||||||
|
#undef fread
|
||||||
|
#undef freopen
|
||||||
|
#undef fscanf
|
||||||
|
#undef fseek
|
||||||
|
#undef fsetpos
|
||||||
|
#undef ftell
|
||||||
|
#undef fwrite
|
||||||
|
#undef getc
|
||||||
|
#undef getchar
|
||||||
|
#undef gets
|
||||||
|
#undef perror
|
||||||
|
//#undef printf // NOLINT
|
||||||
|
#undef putc
|
||||||
|
#undef putchar
|
||||||
|
#undef puts
|
||||||
|
#undef remove
|
||||||
|
#undef rename
|
||||||
|
#undef rewind
|
||||||
|
#undef scanf
|
||||||
|
#undef setbuf
|
||||||
|
#undef setvbuf
|
||||||
|
//#undef sprintf // NOLINT
|
||||||
|
#undef sscanf
|
||||||
|
#undef tmpfile
|
||||||
|
#undef tmpnam
|
||||||
|
#undef ungetc
|
||||||
|
#undef vfprintf
|
||||||
|
#undef vprintf
|
||||||
|
#undef vsprintf
|
||||||
|
|
||||||
|
// make sure needed macros are defined
|
||||||
|
#ifndef EOF
|
||||||
|
/** End-of-file return value. */
|
||||||
|
#define EOF (-1)
|
||||||
|
#endif // EOF
|
||||||
|
#ifndef NULL
|
||||||
|
/** Null pointer */
|
||||||
|
#define NULL 0
|
||||||
|
#endif // NULL
|
||||||
|
#ifndef SEEK_CUR
|
||||||
|
/** Seek relative to current position. */
|
||||||
|
#define SEEK_CUR 1
|
||||||
|
#endif // SEEK_CUR
|
||||||
|
#ifndef SEEK_END
|
||||||
|
/** Seek relative to end-of-file. */
|
||||||
|
#define SEEK_END 2
|
||||||
|
#endif // SEEK_END
|
||||||
|
#ifndef SEEK_SET
|
||||||
|
/** Seek relative to start-of-file. */
|
||||||
|
#define SEEK_SET 0
|
||||||
|
#endif // SEEK_SET
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \class StdioStream
|
||||||
|
* \brief StdioStream implements a minimal stdio stream.
|
||||||
|
*
|
||||||
|
* StdioStream does not support subdirectories or long file names.
|
||||||
|
*/
|
||||||
|
class StdioStream : private StreamBaseFile {
|
||||||
|
public:
|
||||||
|
/** Constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
StdioStream() {}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Clear the stream's end-of-file and error indicators. */
|
||||||
|
void clearerr() {
|
||||||
|
m_status &= ~(S_ERR | S_EOF);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Close a stream.
|
||||||
|
*
|
||||||
|
* A successful call to the fclose function causes the stream to be
|
||||||
|
* flushed and the associated file to be closed. Any unwritten buffered
|
||||||
|
* data is written to the file; any unread buffered data is discarded.
|
||||||
|
* Whether or not the call succeeds, the stream is disassociated from
|
||||||
|
* the file.
|
||||||
|
*
|
||||||
|
* \return zero if the stream was successfully closed, or EOF if any any
|
||||||
|
* errors are detected.
|
||||||
|
*/
|
||||||
|
int fclose();
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Test the stream's end-of-file indicator.
|
||||||
|
* \return non-zero if and only if the end-of-file indicator is set.
|
||||||
|
*/
|
||||||
|
int feof() {
|
||||||
|
return (m_status & S_EOF) != 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Test the stream's error indicator.
|
||||||
|
* \return return non-zero if and only if the error indicator is set.
|
||||||
|
*/
|
||||||
|
int ferror() {
|
||||||
|
return (m_status & S_ERR) != 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Flush the stream.
|
||||||
|
*
|
||||||
|
* If stream is an output stream or an update stream in which the most
|
||||||
|
* recent operation was not input, any unwritten data is written to the
|
||||||
|
* file; otherwise the call is an error since any buffered input data
|
||||||
|
* would be lost.
|
||||||
|
*
|
||||||
|
* \return sets the error indicator for the stream and returns EOF if an
|
||||||
|
* error occurs, otherwise it returns zero.
|
||||||
|
*/
|
||||||
|
int fflush();
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Get a byte from the stream.
|
||||||
|
*
|
||||||
|
* \return If the end-of-file indicator for the stream is set, or if the
|
||||||
|
* stream is at end-of-file, the end-of-file indicator for the stream is
|
||||||
|
* set and the fgetc function returns EOF. Otherwise, the fgetc function
|
||||||
|
* returns the next character from the input stream.
|
||||||
|
*/
|
||||||
|
int fgetc() {
|
||||||
|
return m_r-- == 0 ? fillGet() : *m_p++;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Get a string from a stream.
|
||||||
|
*
|
||||||
|
* The fgets function reads at most one less than the number of
|
||||||
|
* characters specified by num from the stream into the array pointed
|
||||||
|
* to by str. No additional characters are read after a new-line
|
||||||
|
* character (which is retained) or after end-of-file. A null character
|
||||||
|
* is written immediately after the last character read into the array.
|
||||||
|
*
|
||||||
|
* \param[out] str Pointer to an array of where the string is copied.
|
||||||
|
*
|
||||||
|
* \param[in] num Maximum number of characters including the null
|
||||||
|
* character.
|
||||||
|
*
|
||||||
|
* \param[out] len If len is not null and fgets is successful, the
|
||||||
|
* length of the string is returned.
|
||||||
|
*
|
||||||
|
* \return str if successful. If end-of-file is encountered and no
|
||||||
|
* characters have been read into the array, the contents of the array
|
||||||
|
* remain unchanged and a null pointer is returned. If a read error
|
||||||
|
* occurs during the operation, the array contents are indeterminate
|
||||||
|
* and a null pointer is returned.
|
||||||
|
*/
|
||||||
|
char* fgets(char* str, size_t num, size_t* len = 0);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Open a stream.
|
||||||
|
*
|
||||||
|
* Open a file and associates the stream with it.
|
||||||
|
*
|
||||||
|
* \param[in] path file to be opened.
|
||||||
|
*
|
||||||
|
* \param[in] mode a string that indicates the open mode.
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <tr>
|
||||||
|
* <td>"r" or "rb"</td>
|
||||||
|
* <td>Open a file for reading. The file must exist.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>"w" or "wb"</td>
|
||||||
|
* <td>Truncate an existing to zero length or create an empty file
|
||||||
|
* for writing.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>"wx" or "wbx"</td>
|
||||||
|
* <td>Create a file for writing. Fails if the file already exists.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>"a" or "ab"</td>
|
||||||
|
* <td>Append; open or create file for writing at end-of-file.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>"r+" or "rb+" or "r+b"</td>
|
||||||
|
* <td>Open a file for update (reading and writing).</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>"w+" or "w+b" or "wb+"</td>
|
||||||
|
* <td>Truncate an existing to zero length or create a file for update.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>"w+x" or "w+bx" or "wb+x"</td>
|
||||||
|
* <td>Create a file for update. Fails if the file already exists.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>"a+" or "a+b" or "ab+"</td>
|
||||||
|
* <td>Append; open or create a file for update, writing at end-of-file.</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
* The character 'b' shall have no effect, but is allowed for ISO C
|
||||||
|
* standard conformance.
|
||||||
|
*
|
||||||
|
* Opening a file with append mode causes all subsequent writes to the
|
||||||
|
* file to be forced to the then current end-of-file, regardless of
|
||||||
|
* intervening calls to the fseek function.
|
||||||
|
*
|
||||||
|
* When a file is opened with update mode, both input and output may be
|
||||||
|
* performed on the associated stream. However, output shall not be
|
||||||
|
* directly followed by input without an intervening call to the fflush
|
||||||
|
* function or to a file positioning function (fseek, or rewind), and
|
||||||
|
* input shall not be directly followed by output without an intervening
|
||||||
|
* call to a file positioning function, unless the input operation
|
||||||
|
* encounters end-of-file.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool fopen(const char* path, const char* mode);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write a byte to a stream.
|
||||||
|
*
|
||||||
|
* \param[in] c the byte to be written (converted to an unsigned char).
|
||||||
|
*
|
||||||
|
* \return Upon successful completion, fputc() returns the value it
|
||||||
|
* has written. Otherwise, it returns EOF and sets the error indicator for
|
||||||
|
* the stream.
|
||||||
|
*/
|
||||||
|
int fputc(int c) {
|
||||||
|
return m_w-- == 0 ? flushPut(c) : *m_p++ = c;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write a string to a stream.
|
||||||
|
*
|
||||||
|
* \param[in] str a pointer to the string to be written.
|
||||||
|
*
|
||||||
|
* \return for success, fputs() returns a non-negative
|
||||||
|
* number. Otherwise, it returns EOF and sets the error indicator for
|
||||||
|
* the stream.
|
||||||
|
*/
|
||||||
|
int fputs(const char* str);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Binary input.
|
||||||
|
*
|
||||||
|
* Reads an array of up to count elements, each one with a size of size
|
||||||
|
* bytes.
|
||||||
|
* \param[out] ptr pointer to area of at least (size*count) bytes where
|
||||||
|
* the data will be stored.
|
||||||
|
*
|
||||||
|
* \param[in] size the size, in bytes, of each element to be read.
|
||||||
|
*
|
||||||
|
* \param[in] count the number of elements to be read.
|
||||||
|
*
|
||||||
|
* \return number of elements successfully read, which may be less than
|
||||||
|
* count if a read error or end-of-file is encountered. If size or count
|
||||||
|
* is zero, fread returns zero and the contents of the array and the
|
||||||
|
* state of the stream remain unchanged.
|
||||||
|
*/
|
||||||
|
size_t fread(void* ptr, size_t size, size_t count);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Set the file position for the stream.
|
||||||
|
*
|
||||||
|
* \param[in] offset number of offset from the origin.
|
||||||
|
*
|
||||||
|
* \param[in] origin position used as reference for the offset. It is
|
||||||
|
* specified by one of the following constants.
|
||||||
|
*
|
||||||
|
* SEEK_SET - Beginning of file.
|
||||||
|
*
|
||||||
|
* SEEK_CUR - Current position of the file pointer.
|
||||||
|
*
|
||||||
|
* SEEK_END - End of file.
|
||||||
|
*
|
||||||
|
* \return zero for success. Otherwise, it returns non-zero and sets the
|
||||||
|
* error indicator for the stream.
|
||||||
|
*/
|
||||||
|
int fseek(int32_t offset, int origin);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Get the current position in a stream.
|
||||||
|
*
|
||||||
|
* \return If successful, ftell return the current value of the position
|
||||||
|
* indicator. On failure, ftell returns −1L.
|
||||||
|
*/
|
||||||
|
int32_t ftell();
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Binary output.
|
||||||
|
*
|
||||||
|
* Writes an array of up to count elements, each one with a size of size
|
||||||
|
* bytes.
|
||||||
|
* \param[in] ptr pointer to (size*count) bytes of data to be written.
|
||||||
|
*
|
||||||
|
* \param[in] size the size, in bytes, of each element to be written.
|
||||||
|
*
|
||||||
|
* \param[in] count the number of elements to be written.
|
||||||
|
*
|
||||||
|
* \return number of elements successfully written. if this number is
|
||||||
|
* less than count, an error has occurred. If size or count is zero,
|
||||||
|
* fwrite returns zero.
|
||||||
|
*/
|
||||||
|
size_t fwrite(const void * ptr, size_t size, size_t count);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Get a byte from the stream.
|
||||||
|
*
|
||||||
|
* getc and fgetc are equivalent but getc is in-line so it is faster but
|
||||||
|
* require more flash memory.
|
||||||
|
*
|
||||||
|
* \return If the end-of-file indicator for the stream is set, or if the
|
||||||
|
* stream is at end-of-file, the end-of-file indicator for the stream is
|
||||||
|
* set and the fgetc function returns EOF. Otherwise, the fgetc function
|
||||||
|
* returns the next character from the input stream.
|
||||||
|
*/
|
||||||
|
inline __attribute__((always_inline))
|
||||||
|
int getc() {
|
||||||
|
return m_r-- == 0 ? fillGet() : *m_p++;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write a byte to a stream.
|
||||||
|
*
|
||||||
|
* putc and fputc are equivalent but putc is in-line so it is faster but
|
||||||
|
* require more flash memory.
|
||||||
|
*
|
||||||
|
* \param[in] c the byte to be written (converted to an unsigned char).
|
||||||
|
*
|
||||||
|
* \return Upon successful completion, fputc() returns the value it
|
||||||
|
* has written. Otherwise, it returns EOF and sets the error indicator for
|
||||||
|
* the stream.
|
||||||
|
*/
|
||||||
|
inline __attribute__((always_inline))
|
||||||
|
int putc(int c) {
|
||||||
|
return m_w-- == 0 ? flushPut(c) : *m_p++ = c;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write a CR/LF.
|
||||||
|
*
|
||||||
|
* \return two, the number of bytes written, for success or -1 for failure.
|
||||||
|
*/
|
||||||
|
inline __attribute__((always_inline))
|
||||||
|
int putCRLF() {
|
||||||
|
if (m_w < 2) {
|
||||||
|
if (!flushBuf()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*m_p++ = '\r';
|
||||||
|
*m_p++ = '\n';
|
||||||
|
m_w -= 2;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write a character.
|
||||||
|
* \param[in] c the character to write.
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
size_t print(char c) {
|
||||||
|
return putc(c) < 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write a string.
|
||||||
|
*
|
||||||
|
* \param[in] str the string to be written.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
size_t print(const char* str) {
|
||||||
|
int n = fputs(str);
|
||||||
|
return n < 0 ? 0 : n;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
#if (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN)
|
||||||
|
/** Print a string stored in flash memory.
|
||||||
|
*
|
||||||
|
* \param[in] str the string to print.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
size_t print(const __FlashStringHelper *str);
|
||||||
|
#endif // (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN)
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a floating point number.
|
||||||
|
*
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
*
|
||||||
|
* \param[in] val the number to be printed.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
size_t print(double val, uint8_t prec = 2) {
|
||||||
|
return print(static_cast<float>(val), prec);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a floating point number.
|
||||||
|
*
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
*
|
||||||
|
* \param[in] val the number to be printed.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
size_t print(float val, uint8_t prec = 2) {
|
||||||
|
int n = printDec(val, prec);
|
||||||
|
return n > 0 ? n : 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a number.
|
||||||
|
*
|
||||||
|
* \param[in] val the number to be printed.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
size_t print(T val) {
|
||||||
|
int n = printDec(val);
|
||||||
|
return n > 0 ? n : 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write a CR/LF.
|
||||||
|
*
|
||||||
|
* \return two, the number of bytes written, for success or zero for failure.
|
||||||
|
*/
|
||||||
|
size_t println() {
|
||||||
|
return putCRLF() > 0 ? 2 : 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a floating point number followed by CR/LF.
|
||||||
|
*
|
||||||
|
* \param[in] val the number to be printed.
|
||||||
|
*
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
size_t println(double val, uint8_t prec = 2) {
|
||||||
|
return println(static_cast<float>(val), prec);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a floating point number followed by CR/LF.
|
||||||
|
*
|
||||||
|
* \param[in] val the number to be printed.
|
||||||
|
*
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
size_t println(float val, uint8_t prec = 2) {
|
||||||
|
int n = printDec(val, prec);
|
||||||
|
return n > 0 && putCRLF() > 0 ? n + 2 : 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print an item followed by CR/LF
|
||||||
|
*
|
||||||
|
* \param[in] val the item to be printed.
|
||||||
|
*
|
||||||
|
* \return the number of bytes written.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
size_t println(T val) {
|
||||||
|
int n = print(val);
|
||||||
|
return putCRLF() > 0 ? n + 2 : 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a char as a number.
|
||||||
|
* \param[in] n number to be printed.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(char n) {
|
||||||
|
if (CHAR_MIN == 0) {
|
||||||
|
return printDec((unsigned char)n);
|
||||||
|
} else {
|
||||||
|
return printDec((signed char)n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** print a signed 8-bit integer
|
||||||
|
* \param[in] n number to be printed.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(signed char n);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print an unsigned 8-bit number.
|
||||||
|
* \param[in] n number to be print.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(unsigned char n) {
|
||||||
|
return printDec((uint16_t)n);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a int16_t
|
||||||
|
* \param[in] n number to be printed.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(int16_t n);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** print a uint16_t.
|
||||||
|
* \param[in] n number to be printed.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(uint16_t n);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a signed 32-bit integer.
|
||||||
|
* \param[in] n number to be printed.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(int32_t n);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Write an unsigned 32-bit number.
|
||||||
|
* \param[in] n number to be printed.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(uint32_t n);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a double.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(double value, uint8_t prec) {
|
||||||
|
return printDec(static_cast<float>(value), prec);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a float.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printDec(float value, uint8_t prec);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a number followed by a field terminator.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] term The field terminator.
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printField(double value, char term, uint8_t prec = 2) {
|
||||||
|
return printField(static_cast<float>(value), term, prec) > 0;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a number followed by a field terminator.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] term The field terminator.
|
||||||
|
* \param[in] prec Number of digits after decimal point.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printField(float value, char term, uint8_t prec = 2) {
|
||||||
|
int rtn = printDec(value, prec);
|
||||||
|
return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print a number followed by a field terminator.
|
||||||
|
* \param[in] value The number to be printed.
|
||||||
|
* \param[in] term The field terminator.
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
int printField(T value, char term) {
|
||||||
|
int rtn = printDec(value);
|
||||||
|
return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print HEX
|
||||||
|
* \param[in] n number to be printed as HEX.
|
||||||
|
*
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printHex(uint32_t n);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Print HEX with CRLF
|
||||||
|
* \param[in] n number to be printed as HEX.
|
||||||
|
*
|
||||||
|
* \return The number of bytes written or -1 if an error occurs.
|
||||||
|
*/
|
||||||
|
int printHexln(uint32_t n) {
|
||||||
|
int rtn = printHex(n);
|
||||||
|
return rtn < 0 || putCRLF() != 2 ? -1 : rtn + 2;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Set position of a stream to the beginning.
|
||||||
|
*
|
||||||
|
* The rewind function sets the file position to the beginning of the
|
||||||
|
* file. It is equivalent to fseek(0L, SEEK_SET) except that the error
|
||||||
|
* indicator for the stream is also cleared.
|
||||||
|
*
|
||||||
|
* \return true for success or false for failure.
|
||||||
|
*/
|
||||||
|
bool rewind();
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** Push a byte back into an input stream.
|
||||||
|
*
|
||||||
|
* \param[in] c the byte (converted to an unsigned char) to be pushed back.
|
||||||
|
*
|
||||||
|
* One character of push-back is guaranteed. If the ungetc function is
|
||||||
|
* called too many times without an intervening read or file positioning
|
||||||
|
* operation on that stream, the operation may fail.
|
||||||
|
*
|
||||||
|
* A successful intervening call to a file positioning function (fseek,
|
||||||
|
* fsetpos, or rewind) discards any pushed-back characters for the stream.
|
||||||
|
*
|
||||||
|
* \return Upon successful completion, ungetc() returns the byte pushed
|
||||||
|
* back after conversion. Otherwise it returns EOF.
|
||||||
|
*/
|
||||||
|
int ungetc(int c);
|
||||||
|
//============================================================================
|
||||||
|
private:
|
||||||
|
bool fillBuf();
|
||||||
|
int fillGet();
|
||||||
|
bool flushBuf();
|
||||||
|
int flushPut(uint8_t c);
|
||||||
|
char* fmtSpace(uint8_t len);
|
||||||
|
int write(const void* buf, size_t count);
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// S_SRD and S_WR are never simultaneously asserted
|
||||||
|
static const uint8_t S_SRD = 0x01; // OK to read
|
||||||
|
static const uint8_t S_SWR = 0x02; // OK to write
|
||||||
|
static const uint8_t S_SRW = 0x04; // open for reading & writing
|
||||||
|
static const uint8_t S_EOF = 0x10; // found EOF
|
||||||
|
static const uint8_t S_ERR = 0x20; // found error
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
uint8_t m_buf[STREAM_BUF_SIZE];
|
||||||
|
uint8_t m_status = 0;
|
||||||
|
uint8_t* m_p = m_buf;
|
||||||
|
uint8_t m_r = 0;
|
||||||
|
uint8_t m_w;
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endif // StdioStream_h
|
|
@ -0,0 +1,160 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include "fstream.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int16_t StreamBaseClass::getch() {
|
||||||
|
uint8_t c;
|
||||||
|
int8_t s = StreamBaseFile::read(&c, 1);
|
||||||
|
if (s != 1) {
|
||||||
|
if (s < 0) {
|
||||||
|
setstate(badbit);
|
||||||
|
} else {
|
||||||
|
setstate(eofbit);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (c != '\r' || (getmode() & ios::binary)) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
s = StreamBaseFile::read(&c, 1);
|
||||||
|
if (s == 1 && c == '\n') {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (s == 1) {
|
||||||
|
StreamBaseFile::seekCur(-1);
|
||||||
|
}
|
||||||
|
return '\r';
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void StreamBaseClass::open(const char* path, ios::openmode mode) {
|
||||||
|
oflag_t oflag;
|
||||||
|
clearWriteError();
|
||||||
|
switch (mode & (app | in | out | trunc)) {
|
||||||
|
case app | in:
|
||||||
|
case app | in | out:
|
||||||
|
oflag = O_RDWR | O_APPEND | O_CREAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case app:
|
||||||
|
case app | out:
|
||||||
|
oflag = O_WRONLY | O_APPEND | O_CREAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in:
|
||||||
|
oflag = O_RDONLY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in | out:
|
||||||
|
oflag = O_RDWR | O_CREAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in | out | trunc:
|
||||||
|
oflag = O_RDWR | O_TRUNC | O_CREAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case out:
|
||||||
|
case out | trunc:
|
||||||
|
oflag = O_WRONLY | O_TRUNC | O_CREAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (mode & ios::ate) {
|
||||||
|
oflag |= O_AT_END;
|
||||||
|
}
|
||||||
|
if (!StreamBaseFile::open(path, oflag)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
setmode(mode);
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
StreamBaseFile::close();
|
||||||
|
setstate(failbit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void StreamBaseClass::putch(char c) {
|
||||||
|
if (c == '\n' && !(getmode() & ios::binary)) {
|
||||||
|
write('\r');
|
||||||
|
}
|
||||||
|
write(c);
|
||||||
|
if (getWriteError()) {
|
||||||
|
setstate(badbit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void StreamBaseClass::putstr(const char* str) {
|
||||||
|
size_t n = 0;
|
||||||
|
while (1) {
|
||||||
|
char c = str[n];
|
||||||
|
if (c == '\0' || (c == '\n' && !(getmode() & ios::binary))) {
|
||||||
|
if (n > 0) {
|
||||||
|
write(str, n);
|
||||||
|
}
|
||||||
|
if (c == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write('\r');
|
||||||
|
str += n;
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
if (getWriteError()) {
|
||||||
|
setstate(badbit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool StreamBaseClass::seekoff(off_type off, seekdir way) {
|
||||||
|
pos_type pos;
|
||||||
|
switch (way) {
|
||||||
|
case beg:
|
||||||
|
pos = off;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cur:
|
||||||
|
pos = StreamBaseFile::curPosition() + off;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case end:
|
||||||
|
pos = StreamBaseFile::fileSize() + off;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return seekpos(pos);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int StreamBaseClass::write(const void* buf, size_t n) {
|
||||||
|
return StreamBaseFile::write(buf, n);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void StreamBaseClass::write(char c) {
|
||||||
|
StreamBaseFile::write(&c, 1);
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef bufstream_h
|
||||||
|
#define bufstream_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief \ref ibufstream and \ref obufstream classes
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include "iostream.h"
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class ibufstream
|
||||||
|
* \brief parse a char string
|
||||||
|
*/
|
||||||
|
class ibufstream : public istream {
|
||||||
|
public:
|
||||||
|
/** Constructor */
|
||||||
|
ibufstream() {}
|
||||||
|
/** Constructor
|
||||||
|
* \param[in] str pointer to string to be parsed
|
||||||
|
* Warning: The string will not be copied so must stay in scope.
|
||||||
|
*/
|
||||||
|
explicit ibufstream(const char* str) {
|
||||||
|
init(str);
|
||||||
|
}
|
||||||
|
/** Initialize an ibufstream
|
||||||
|
* \param[in] str pointer to string to be parsed
|
||||||
|
* Warning: The string will not be copied so must stay in scope.
|
||||||
|
*/
|
||||||
|
void init(const char* str) {
|
||||||
|
m_buf = str;
|
||||||
|
m_len = strlen(m_buf);
|
||||||
|
m_pos = 0;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
int16_t getch() {
|
||||||
|
if (m_pos < m_len) {
|
||||||
|
return m_buf[m_pos++];
|
||||||
|
}
|
||||||
|
setstate(eofbit);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
void getpos(pos_t* pos) {
|
||||||
|
pos->position = m_pos;
|
||||||
|
}
|
||||||
|
bool seekoff(off_type off, seekdir way) {
|
||||||
|
(void)off;
|
||||||
|
(void)way;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
if (pos < m_len) {
|
||||||
|
m_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void setpos(pos_t* pos) {
|
||||||
|
m_pos = pos->position;
|
||||||
|
}
|
||||||
|
pos_type tellpos() {
|
||||||
|
return m_pos;
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
private:
|
||||||
|
const char* m_buf = nullptr;
|
||||||
|
size_t m_len = 0;
|
||||||
|
size_t m_pos;
|
||||||
|
};
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class obufstream
|
||||||
|
* \brief format a char string
|
||||||
|
*/
|
||||||
|
class obufstream : public ostream {
|
||||||
|
public:
|
||||||
|
/** constructor */
|
||||||
|
obufstream() {}
|
||||||
|
/** Constructor
|
||||||
|
* \param[in] buf buffer for formatted string
|
||||||
|
* \param[in] size buffer size
|
||||||
|
*/
|
||||||
|
obufstream(char *buf, size_t size) {
|
||||||
|
init(buf, size);
|
||||||
|
}
|
||||||
|
/** Initialize an obufstream
|
||||||
|
* \param[in] buf buffer for formatted string
|
||||||
|
* \param[in] size buffer size
|
||||||
|
*/
|
||||||
|
void init(char *buf, size_t size) {
|
||||||
|
m_buf = buf;
|
||||||
|
buf[0] = '\0';
|
||||||
|
m_size = size;
|
||||||
|
m_in = 0;
|
||||||
|
}
|
||||||
|
/** \return a pointer to the buffer */
|
||||||
|
char* buf() {
|
||||||
|
return m_buf;
|
||||||
|
}
|
||||||
|
/** \return the length of the formatted string */
|
||||||
|
size_t length() {
|
||||||
|
return m_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
void putch(char c) {
|
||||||
|
if ((m_in + 1) >= m_size) {
|
||||||
|
setstate(badbit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_buf[m_in++] = c;
|
||||||
|
m_buf[m_in] = '\0';
|
||||||
|
}
|
||||||
|
void putstr(const char *str) {
|
||||||
|
while (*str) {
|
||||||
|
putch(*str++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool seekoff(off_type off, seekdir way) {
|
||||||
|
(void)off;
|
||||||
|
(void)way;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
if (pos > m_in) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_in = pos;
|
||||||
|
m_buf[m_in] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool sync() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pos_type tellpos() {
|
||||||
|
return m_in;
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
private:
|
||||||
|
char *m_buf = nullptr;
|
||||||
|
size_t m_size = 0;
|
||||||
|
size_t m_in = 0;
|
||||||
|
};
|
||||||
|
#endif // bufstream_h
|
|
@ -0,0 +1,330 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 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.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief iostreams for files.
|
||||||
|
*/
|
||||||
|
#ifndef fstream_h
|
||||||
|
#define fstream_h
|
||||||
|
#include "iostream.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* \class StreamBaseClass
|
||||||
|
* \brief base type for FAT and exFAT streams
|
||||||
|
*/
|
||||||
|
class StreamBaseClass : protected StreamBaseFile, virtual public ios {
|
||||||
|
protected:
|
||||||
|
void clearWriteError() {
|
||||||
|
StreamBaseFile::clearWriteError();
|
||||||
|
}
|
||||||
|
/* Internal do not use
|
||||||
|
* \return mode
|
||||||
|
*/
|
||||||
|
int16_t getch();
|
||||||
|
bool getWriteError() {
|
||||||
|
return StreamBaseFile::getWriteError();
|
||||||
|
}
|
||||||
|
void open(const char* path, ios::openmode mode);
|
||||||
|
/** Internal do not use
|
||||||
|
* \return mode
|
||||||
|
*/
|
||||||
|
ios::openmode getmode() {
|
||||||
|
return m_mode;
|
||||||
|
}
|
||||||
|
void putch(char c);
|
||||||
|
void putstr(const char *str);
|
||||||
|
bool seekoff(off_type off, seekdir way);
|
||||||
|
/** Internal do not use
|
||||||
|
* \param[in] pos
|
||||||
|
*/
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
return StreamBaseFile::seekSet(pos);
|
||||||
|
}
|
||||||
|
/** Internal do not use
|
||||||
|
* \param[in] mode
|
||||||
|
*/
|
||||||
|
void setmode(ios::openmode mode) {
|
||||||
|
m_mode = mode;
|
||||||
|
}
|
||||||
|
int write(const void* buf, size_t n);
|
||||||
|
void write(char c);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ios::openmode m_mode;
|
||||||
|
};
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class fstream
|
||||||
|
* \brief file input/output stream.
|
||||||
|
*/
|
||||||
|
class fstream : public iostream, StreamBaseClass {
|
||||||
|
public:
|
||||||
|
using iostream::peek;
|
||||||
|
fstream() {}
|
||||||
|
/** Constructor with open
|
||||||
|
* \param[in] path file to open
|
||||||
|
* \param[in] mode open mode
|
||||||
|
*/
|
||||||
|
explicit fstream(const char* path, openmode mode = in | out) {
|
||||||
|
open(path, mode);
|
||||||
|
}
|
||||||
|
#if DESTRUCTOR_CLOSES_FILE
|
||||||
|
~fstream() {}
|
||||||
|
#endif // DESTRUCTOR_CLOSES_FILE
|
||||||
|
/** Clear state and writeError
|
||||||
|
* \param[in] state new state for stream
|
||||||
|
*/
|
||||||
|
void clear(iostate state = goodbit) {
|
||||||
|
ios::clear(state);
|
||||||
|
StreamBaseClass::clearWriteError();
|
||||||
|
}
|
||||||
|
/** Close a file and force cached data and directory information
|
||||||
|
* to be written to the storage device.
|
||||||
|
*/
|
||||||
|
void close() {
|
||||||
|
StreamBaseClass::close();
|
||||||
|
}
|
||||||
|
/** Open a fstream
|
||||||
|
* \param[in] path path to open
|
||||||
|
* \param[in] mode open mode
|
||||||
|
*
|
||||||
|
* Valid open modes are (at end, ios::ate, and/or ios::binary may be added):
|
||||||
|
*
|
||||||
|
* ios::in - Open file for reading.
|
||||||
|
*
|
||||||
|
* ios::out or ios::out | ios::trunc - Truncate to 0 length, if existent,
|
||||||
|
* or create a file for writing only.
|
||||||
|
*
|
||||||
|
* ios::app or ios::out | ios::app - Append; open or create file for
|
||||||
|
* writing at end-of-file.
|
||||||
|
*
|
||||||
|
* ios::in | ios::out - Open file for update (reading and writing).
|
||||||
|
*
|
||||||
|
* ios::in | ios::out | ios::trunc - Truncate to zero length, if existent,
|
||||||
|
* or create file for update.
|
||||||
|
*
|
||||||
|
* ios::in | ios::app or ios::in | ios::out | ios::app - Append; open or
|
||||||
|
* create text file for update, writing at end of file.
|
||||||
|
*/
|
||||||
|
void open(const char* path, openmode mode = in | out) {
|
||||||
|
StreamBaseClass::open(path, mode);
|
||||||
|
}
|
||||||
|
/** \return True if stream is open else false. */
|
||||||
|
bool is_open() {
|
||||||
|
return StreamBaseFile::isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
/** Internal - do not use
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
int16_t getch() {
|
||||||
|
return StreamBaseClass::getch();
|
||||||
|
}
|
||||||
|
/** Internal - do not use
|
||||||
|
* \param[out] pos
|
||||||
|
*/
|
||||||
|
void getpos(pos_t* pos) {
|
||||||
|
StreamBaseFile::fgetpos(pos);
|
||||||
|
}
|
||||||
|
/** Internal - do not use
|
||||||
|
* \param[in] c
|
||||||
|
*/
|
||||||
|
void putch(char c) {
|
||||||
|
StreamBaseClass::putch(c);
|
||||||
|
}
|
||||||
|
/** Internal - do not use
|
||||||
|
* \param[in] str
|
||||||
|
*/
|
||||||
|
void putstr(const char *str) {
|
||||||
|
StreamBaseClass::putstr(str);
|
||||||
|
}
|
||||||
|
/** Internal - do not use
|
||||||
|
* \param[in] pos
|
||||||
|
*/
|
||||||
|
bool seekoff(off_type off, seekdir way) {
|
||||||
|
return StreamBaseClass::seekoff(off, way);
|
||||||
|
}
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
return StreamBaseClass::seekpos(pos);
|
||||||
|
}
|
||||||
|
void setpos(pos_t* pos) {
|
||||||
|
StreamBaseFile::fsetpos(pos);
|
||||||
|
}
|
||||||
|
bool sync() {
|
||||||
|
return StreamBaseClass::sync();
|
||||||
|
}
|
||||||
|
pos_type tellpos() {
|
||||||
|
return StreamBaseFile::curPosition();
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
};
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class ifstream
|
||||||
|
* \brief file input stream.
|
||||||
|
*/
|
||||||
|
class ifstream : public istream, StreamBaseClass {
|
||||||
|
public:
|
||||||
|
using istream::peek;
|
||||||
|
ifstream() {}
|
||||||
|
/** Constructor with open
|
||||||
|
* \param[in] path file to open
|
||||||
|
* \param[in] mode open mode
|
||||||
|
*/
|
||||||
|
explicit ifstream(const char* path, openmode mode = in) {
|
||||||
|
open(path, mode);
|
||||||
|
}
|
||||||
|
#if DESTRUCTOR_CLOSES_FILE
|
||||||
|
~ifstream() {}
|
||||||
|
#endif // DESTRUCTOR_CLOSES_FILE
|
||||||
|
/** Close a file and force cached data and directory information
|
||||||
|
* to be written to the storage device.
|
||||||
|
*/
|
||||||
|
void close() {
|
||||||
|
StreamBaseClass::close();
|
||||||
|
}
|
||||||
|
/** \return True if stream is open else false. */
|
||||||
|
bool is_open() {
|
||||||
|
return StreamBaseFile::isOpen();
|
||||||
|
}
|
||||||
|
/** Open an ifstream
|
||||||
|
* \param[in] path file to open
|
||||||
|
* \param[in] mode open mode
|
||||||
|
*
|
||||||
|
* \a mode See fstream::open() for valid modes.
|
||||||
|
*/
|
||||||
|
void open(const char* path, openmode mode = in) {
|
||||||
|
StreamBaseClass::open(path, mode | in);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
/** Internal - do not use
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
int16_t getch() {
|
||||||
|
return StreamBaseClass::getch();
|
||||||
|
}
|
||||||
|
/** Internal - do not use
|
||||||
|
* \param[out] pos
|
||||||
|
*/
|
||||||
|
void getpos(pos_t* pos) {
|
||||||
|
StreamBaseFile::fgetpos(pos);
|
||||||
|
}
|
||||||
|
/** Internal - do not use
|
||||||
|
* \param[in] pos
|
||||||
|
*/
|
||||||
|
bool seekoff(off_type off, seekdir way) {
|
||||||
|
return StreamBaseClass::seekoff(off, way);
|
||||||
|
}
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
return StreamBaseClass::seekpos(pos);
|
||||||
|
}
|
||||||
|
void setpos(pos_t* pos) {
|
||||||
|
StreamBaseFile::fsetpos(pos);
|
||||||
|
}
|
||||||
|
pos_type tellpos() {
|
||||||
|
return StreamBaseFile::curPosition();
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
};
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class ofstream
|
||||||
|
* \brief file output stream.
|
||||||
|
*/
|
||||||
|
class ofstream : public ostream, StreamBaseClass {
|
||||||
|
public:
|
||||||
|
ofstream() {}
|
||||||
|
/** Constructor with open
|
||||||
|
* \param[in] path file to open
|
||||||
|
* \param[in] mode open mode
|
||||||
|
*/
|
||||||
|
explicit ofstream(const char* path, openmode mode = out) {
|
||||||
|
open(path, mode);
|
||||||
|
}
|
||||||
|
#if DESTRUCTOR_CLOSES_FILE
|
||||||
|
~ofstream() {}
|
||||||
|
#endif // DESTRUCTOR_CLOSES_FILE
|
||||||
|
/** Clear state and writeError
|
||||||
|
* \param[in] state new state for stream
|
||||||
|
*/
|
||||||
|
void clear(iostate state = goodbit) {
|
||||||
|
ios::clear(state);
|
||||||
|
StreamBaseClass::clearWriteError();
|
||||||
|
}
|
||||||
|
/** Close a file and force cached data and directory information
|
||||||
|
* to be written to the storage device.
|
||||||
|
*/
|
||||||
|
void close() {
|
||||||
|
StreamBaseClass::close();
|
||||||
|
}
|
||||||
|
/** Open an ofstream
|
||||||
|
* \param[in] path file to open
|
||||||
|
* \param[in] mode open mode
|
||||||
|
*
|
||||||
|
* \a mode See fstream::open() for valid modes.
|
||||||
|
*/
|
||||||
|
void open(const char* path, openmode mode = out) {
|
||||||
|
StreamBaseClass::open(path, mode | out);
|
||||||
|
}
|
||||||
|
/** \return True if stream is open else false. */
|
||||||
|
bool is_open() {
|
||||||
|
return StreamBaseFile::isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
/**
|
||||||
|
* Internal do not use
|
||||||
|
* \param[in] c
|
||||||
|
*/
|
||||||
|
void putch(char c) {
|
||||||
|
StreamBaseClass::putch(c);
|
||||||
|
}
|
||||||
|
void putstr(const char* str) {
|
||||||
|
StreamBaseClass::putstr(str);
|
||||||
|
}
|
||||||
|
bool seekoff(off_type off, seekdir way) {
|
||||||
|
return StreamBaseClass::seekoff(off, way);
|
||||||
|
}
|
||||||
|
bool seekpos(pos_type pos) {
|
||||||
|
return StreamBaseClass::seekpos(pos);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Internal do not use
|
||||||
|
* \param[in] b
|
||||||
|
*/
|
||||||
|
bool sync() {
|
||||||
|
return StreamBaseClass::sync();
|
||||||
|
}
|
||||||
|
pos_type tellpos() {
|
||||||
|
return StreamBaseFile::curPosition();
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
};
|
||||||
|
#endif // fstream_h
|
|
@ -0,0 +1,448 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ios_h
|
||||||
|
#define ios_h
|
||||||
|
#include "../FsLib/FsLib.h"
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief \ref ios_base and \ref ios classes
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
/** For internal use in c++ streams */
|
||||||
|
typedef fspos_t pos_t;
|
||||||
|
//==============================================================================
|
||||||
|
#if SDFAT_FILE_TYPE == 1 || defined(DOXYGEN)
|
||||||
|
/** Set File type for iostreams. */
|
||||||
|
typedef FatFile StreamBaseFile;
|
||||||
|
#elif SDFAT_FILE_TYPE == 2
|
||||||
|
typedef ExFatFile StreamBaseFile;
|
||||||
|
#elif SDFAT_FILE_TYPE == 3
|
||||||
|
typedef FsBaseFile StreamBaseFile;
|
||||||
|
#else // SDFAT_FILE_TYPE
|
||||||
|
#error Invalid SDFAT_FILE_TYPE
|
||||||
|
#endif // SDFAT_FILE_TYPE
|
||||||
|
/**
|
||||||
|
* \class ios_base
|
||||||
|
* \brief Base class for all streams
|
||||||
|
*/
|
||||||
|
class ios_base {
|
||||||
|
public:
|
||||||
|
/** typedef for iostate bitmask */
|
||||||
|
typedef unsigned char iostate;
|
||||||
|
// State flags.
|
||||||
|
/** iostate for no flags */
|
||||||
|
static const iostate goodbit = 0x00;
|
||||||
|
/** iostate bad bit for a nonrecoverable error. */
|
||||||
|
static const iostate badbit = 0X01;
|
||||||
|
/** iostate bit for end of file reached */
|
||||||
|
static const iostate eofbit = 0x02;
|
||||||
|
/** iostate fail bit for nonfatal error */
|
||||||
|
static const iostate failbit = 0X04;
|
||||||
|
#if SDFAT_FILE_TYPE == 1
|
||||||
|
/**
|
||||||
|
* unsigned size that can represent maximum file size.
|
||||||
|
* (violates spec - should be signed)
|
||||||
|
*/
|
||||||
|
typedef uint32_t streamsize;
|
||||||
|
/** type for absolute seek position */
|
||||||
|
typedef uint32_t pos_type;
|
||||||
|
/** type for relative seek offset */
|
||||||
|
typedef int32_t off_type;
|
||||||
|
#else // SDFAT_FILE_TYPE
|
||||||
|
/**
|
||||||
|
* unsigned size that can represent maximum file size.
|
||||||
|
* (violates spec - should be signed)
|
||||||
|
*/
|
||||||
|
typedef uint64_t streamsize;
|
||||||
|
/** type for absolute seek position */
|
||||||
|
typedef uint64_t pos_type;
|
||||||
|
/** type for relative seek offset */
|
||||||
|
typedef int64_t off_type;
|
||||||
|
#endif // SDFAT_FILE_TYPE
|
||||||
|
/** enumerated type for the direction of relative seeks */
|
||||||
|
enum seekdir {
|
||||||
|
/** seek relative to the beginning of the stream */
|
||||||
|
beg,
|
||||||
|
/** seek relative to the current stream position */
|
||||||
|
cur,
|
||||||
|
/** seek relative to the end of the stream */
|
||||||
|
end
|
||||||
|
};
|
||||||
|
/** type for format flags */
|
||||||
|
typedef unsigned int fmtflags;
|
||||||
|
/** left adjust fields */
|
||||||
|
static const fmtflags left = 0x0001;
|
||||||
|
/** right adjust fields */
|
||||||
|
static const fmtflags right = 0x0002;
|
||||||
|
/** fill between sign/base prefix and number */
|
||||||
|
static const fmtflags internal = 0x0004;
|
||||||
|
/** base 10 flag*/
|
||||||
|
static const fmtflags dec = 0x0008;
|
||||||
|
/** base 16 flag */
|
||||||
|
static const fmtflags hex = 0x0010;
|
||||||
|
/** base 8 flag */
|
||||||
|
static const fmtflags oct = 0x0020;
|
||||||
|
// static const fmtflags fixed = 0x0040;
|
||||||
|
// static const fmtflags scientific = 0x0080;
|
||||||
|
/** use strings true/false for bool */
|
||||||
|
static const fmtflags boolalpha = 0x0100;
|
||||||
|
/** use prefix 0X for hex and 0 for oct */
|
||||||
|
static const fmtflags showbase = 0x0200;
|
||||||
|
/** always show '.' for floating numbers */
|
||||||
|
static const fmtflags showpoint = 0x0400;
|
||||||
|
/** show + sign for nonnegative numbers */
|
||||||
|
static const fmtflags showpos = 0x0800;
|
||||||
|
/** skip initial white space */
|
||||||
|
static const fmtflags skipws = 0x1000;
|
||||||
|
// static const fmtflags unitbuf = 0x2000;
|
||||||
|
/** use uppercase letters in number representations */
|
||||||
|
static const fmtflags uppercase = 0x4000;
|
||||||
|
/** mask for adjustfield */
|
||||||
|
static const fmtflags adjustfield = left | right | internal;
|
||||||
|
/** mask for basefield */
|
||||||
|
static const fmtflags basefield = dec | hex | oct;
|
||||||
|
// static const fmtflags floatfield = scientific | fixed;
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/** typedef for iostream open mode */
|
||||||
|
typedef uint8_t openmode;
|
||||||
|
|
||||||
|
// Openmode flags.
|
||||||
|
/** seek to end before each write */
|
||||||
|
static const openmode app = 0X4;
|
||||||
|
/** open and seek to end immediately after opening */
|
||||||
|
static const openmode ate = 0X8;
|
||||||
|
/** perform input and output in binary mode (as opposed to text mode) */
|
||||||
|
static const openmode binary = 0X10;
|
||||||
|
/** open for input */
|
||||||
|
static const openmode in = 0X20;
|
||||||
|
/** open for output */
|
||||||
|
static const openmode out = 0X40;
|
||||||
|
/** truncate an existing stream when opening */
|
||||||
|
static const openmode trunc = 0X80;
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ios_base() : m_fill(' '), m_fmtflags(dec | right | skipws)
|
||||||
|
, m_precision(2), m_width(0) {}
|
||||||
|
/** \return fill character */
|
||||||
|
char fill() {
|
||||||
|
return m_fill;
|
||||||
|
}
|
||||||
|
/** Set fill character
|
||||||
|
* \param[in] c new fill character
|
||||||
|
* \return old fill character
|
||||||
|
*/
|
||||||
|
char fill(char c) {
|
||||||
|
char r = m_fill;
|
||||||
|
m_fill = c;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/** \return format flags */
|
||||||
|
fmtflags flags() const {
|
||||||
|
return m_fmtflags;
|
||||||
|
}
|
||||||
|
/** set format flags
|
||||||
|
* \param[in] fl new flag
|
||||||
|
* \return old flags
|
||||||
|
*/
|
||||||
|
fmtflags flags(fmtflags fl) {
|
||||||
|
fmtflags tmp = m_fmtflags;
|
||||||
|
m_fmtflags = fl;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
/** \return precision */
|
||||||
|
int precision() const {
|
||||||
|
return m_precision;
|
||||||
|
}
|
||||||
|
/** set precision
|
||||||
|
* \param[in] n new precision
|
||||||
|
* \return old precision
|
||||||
|
*/
|
||||||
|
int precision(unsigned int n) {
|
||||||
|
int r = m_precision;
|
||||||
|
m_precision = n;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/** set format flags
|
||||||
|
* \param[in] fl new flags to be or'ed in
|
||||||
|
* \return old flags
|
||||||
|
*/
|
||||||
|
fmtflags setf(fmtflags fl) {
|
||||||
|
fmtflags r = m_fmtflags;
|
||||||
|
m_fmtflags |= fl;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/** modify format flags
|
||||||
|
* \param[in] mask flags to be removed
|
||||||
|
* \param[in] fl flags to be set after mask bits have been cleared
|
||||||
|
* \return old flags
|
||||||
|
*/
|
||||||
|
fmtflags setf(fmtflags fl, fmtflags mask) {
|
||||||
|
fmtflags r = m_fmtflags;
|
||||||
|
m_fmtflags &= ~mask;
|
||||||
|
m_fmtflags |= fl;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/** clear format flags
|
||||||
|
* \param[in] fl flags to be cleared
|
||||||
|
*/
|
||||||
|
void unsetf(fmtflags fl) {
|
||||||
|
m_fmtflags &= ~fl;
|
||||||
|
}
|
||||||
|
/** \return width */
|
||||||
|
unsigned width() {
|
||||||
|
return m_width;
|
||||||
|
}
|
||||||
|
/** set width
|
||||||
|
* \param[in] n new width
|
||||||
|
* \return old width
|
||||||
|
*/
|
||||||
|
unsigned width(unsigned n) {
|
||||||
|
unsigned r = m_width;
|
||||||
|
m_width = n;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** \return current number base */
|
||||||
|
uint8_t flagsToBase() {
|
||||||
|
uint8_t f = flags() & basefield;
|
||||||
|
return f == oct ? 8 : f != hex ? 10 : 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char m_fill;
|
||||||
|
fmtflags m_fmtflags;
|
||||||
|
unsigned char m_precision;
|
||||||
|
unsigned int m_width;
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** function for boolalpha manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& boolalpha(ios_base& str) {
|
||||||
|
str.setf(ios_base::boolalpha);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for dec manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& dec(ios_base& str) {
|
||||||
|
str.setf(ios_base::dec, ios_base::basefield);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for hex manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& hex(ios_base& str) {
|
||||||
|
str.setf(ios_base::hex, ios_base::basefield);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for internal manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& internal(ios_base& str) {
|
||||||
|
str.setf(ios_base::internal, ios_base::adjustfield);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for left manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& left(ios_base& str) {
|
||||||
|
str.setf(ios_base::left, ios_base::adjustfield);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for noboolalpha manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& noboolalpha(ios_base& str) {
|
||||||
|
str.unsetf(ios_base::boolalpha);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for noshowbase manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& noshowbase(ios_base& str) {
|
||||||
|
str.unsetf(ios_base::showbase);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for noshowpoint manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& noshowpoint(ios_base& str) {
|
||||||
|
str.unsetf(ios_base::showpoint);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for noshowpos manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& noshowpos(ios_base& str) {
|
||||||
|
str.unsetf(ios_base::showpos);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for noskipws manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& noskipws(ios_base& str) {
|
||||||
|
str.unsetf(ios_base::skipws);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for nouppercase manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& nouppercase(ios_base& str) {
|
||||||
|
str.unsetf(ios_base::uppercase);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for oct manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& oct(ios_base& str) {
|
||||||
|
str.setf(ios_base::oct, ios_base::basefield);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for right manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& right(ios_base& str) {
|
||||||
|
str.setf(ios_base::right, ios_base::adjustfield);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for showbase manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& showbase(ios_base& str) {
|
||||||
|
str.setf(ios_base::showbase);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for showpos manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& showpos(ios_base& str) {
|
||||||
|
str.setf(ios_base::showpos);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for showpoint manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& showpoint(ios_base& str) {
|
||||||
|
str.setf(ios_base::showpoint);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for skipws manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& skipws(ios_base& str) {
|
||||||
|
str.setf(ios_base::skipws);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
/** function for uppercase manipulator
|
||||||
|
* \param[in] str The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ios_base& uppercase(ios_base& str) {
|
||||||
|
str.setf(ios_base::uppercase);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class ios
|
||||||
|
* \brief Error and state information for all streams
|
||||||
|
*/
|
||||||
|
class ios : public ios_base {
|
||||||
|
public:
|
||||||
|
/** Create ios with no error flags set */
|
||||||
|
ios() {}
|
||||||
|
|
||||||
|
/** \return null pointer if fail() is true. */
|
||||||
|
operator const void*() const {
|
||||||
|
return !fail() ? reinterpret_cast<const void*>(this) : nullptr;
|
||||||
|
}
|
||||||
|
/** \return true if fail() else false. */
|
||||||
|
bool operator!() const {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
/** \return false if fail() else true. */
|
||||||
|
explicit operator bool() const {return !fail();}
|
||||||
|
/** \return The iostate flags for this file. */
|
||||||
|
iostate rdstate() const {
|
||||||
|
return m_iostate;
|
||||||
|
}
|
||||||
|
/** \return True if no iostate flags are set else false. */
|
||||||
|
bool good() const {
|
||||||
|
return m_iostate == goodbit;
|
||||||
|
}
|
||||||
|
/** \return true if end of file has been reached else false.
|
||||||
|
*
|
||||||
|
* Warning: An empty file returns false before the first read.
|
||||||
|
*
|
||||||
|
* Moral: eof() is only useful in combination with fail(), to find out
|
||||||
|
* whether EOF was the cause for failure
|
||||||
|
*/
|
||||||
|
bool eof() const {
|
||||||
|
return m_iostate & eofbit;
|
||||||
|
}
|
||||||
|
/** \return true if any iostate bit other than eof are set else false. */
|
||||||
|
bool fail() const {
|
||||||
|
return m_iostate & (failbit | badbit);
|
||||||
|
}
|
||||||
|
/** \return true if bad bit is set else false. */
|
||||||
|
bool bad() const {
|
||||||
|
return m_iostate & badbit;
|
||||||
|
}
|
||||||
|
/** Clear iostate bits.
|
||||||
|
*
|
||||||
|
* \param[in] state The flags you want to set after clearing all flags.
|
||||||
|
**/
|
||||||
|
void clear(iostate state = goodbit) {
|
||||||
|
m_iostate = state;
|
||||||
|
}
|
||||||
|
/** Set iostate bits.
|
||||||
|
*
|
||||||
|
* \param[in] state Bitts to set.
|
||||||
|
**/
|
||||||
|
void setstate(iostate state) {
|
||||||
|
m_iostate |= state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
iostate m_iostate = 0;
|
||||||
|
};
|
||||||
|
#endif // ios_h
|
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef iostream_h
|
||||||
|
#define iostream_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief \ref iostream class
|
||||||
|
*/
|
||||||
|
#include "istream.h"
|
||||||
|
#include "ostream.h"
|
||||||
|
/** Skip white space
|
||||||
|
* \param[in] is the Stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline istream& ws(istream& is) {
|
||||||
|
is.skipWhite();
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
/** insert endline
|
||||||
|
* \param[in] os The Stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ostream& endl(ostream& os) {
|
||||||
|
os.put('\n');
|
||||||
|
#if ENDL_CALLS_FLUSH
|
||||||
|
os.flush();
|
||||||
|
#endif // ENDL_CALLS_FLUSH
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
/** flush manipulator
|
||||||
|
* \param[in] os The stream
|
||||||
|
* \return The stream
|
||||||
|
*/
|
||||||
|
inline ostream& flush(ostream& os) {
|
||||||
|
os.flush();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* \struct setfill
|
||||||
|
* \brief type for setfill manipulator
|
||||||
|
*/
|
||||||
|
struct setfill {
|
||||||
|
/** fill character */
|
||||||
|
char c;
|
||||||
|
/** constructor
|
||||||
|
*
|
||||||
|
* \param[in] arg new fill character
|
||||||
|
*/
|
||||||
|
explicit setfill(char arg) : c(arg) {}
|
||||||
|
};
|
||||||
|
/** setfill manipulator
|
||||||
|
* \param[in] os the stream
|
||||||
|
* \param[in] arg set setfill object
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
inline ostream &operator<< (ostream &os, const setfill &arg) {
|
||||||
|
os.fill(arg.c);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
/** setfill manipulator
|
||||||
|
* \param[in] obj the stream
|
||||||
|
* \param[in] arg set setfill object
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
inline istream &operator>>(istream &obj, const setfill &arg) {
|
||||||
|
obj.fill(arg.c);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \struct setprecision
|
||||||
|
* \brief type for setprecision manipulator
|
||||||
|
*/
|
||||||
|
struct setprecision {
|
||||||
|
/** precision */
|
||||||
|
unsigned int p;
|
||||||
|
/** constructor
|
||||||
|
* \param[in] arg new precision
|
||||||
|
*/
|
||||||
|
explicit setprecision(unsigned int arg) : p(arg) {}
|
||||||
|
};
|
||||||
|
/** setprecision manipulator
|
||||||
|
* \param[in] os the stream
|
||||||
|
* \param[in] arg set setprecision object
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
inline ostream &operator<< (ostream &os, const setprecision &arg) {
|
||||||
|
os.precision(arg.p);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
/** setprecision manipulator
|
||||||
|
* \param[in] is the stream
|
||||||
|
* \param[in] arg set setprecision object
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
inline istream &operator>>(istream &is, const setprecision &arg) {
|
||||||
|
is.precision(arg.p);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** \struct setw
|
||||||
|
* \brief type for setw manipulator
|
||||||
|
*/
|
||||||
|
struct setw {
|
||||||
|
/** width */
|
||||||
|
unsigned w;
|
||||||
|
/** constructor
|
||||||
|
* \param[in] arg new width
|
||||||
|
*/
|
||||||
|
explicit setw(unsigned arg) : w(arg) {}
|
||||||
|
};
|
||||||
|
/** setw manipulator
|
||||||
|
* \param[in] os the stream
|
||||||
|
* \param[in] arg set setw object
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
inline ostream &operator<< (ostream &os, const setw &arg) {
|
||||||
|
os.width(arg.w);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
/** setw manipulator
|
||||||
|
* \param[in] is the stream
|
||||||
|
* \param[in] arg set setw object
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
inline istream &operator>>(istream &is, const setw &arg) {
|
||||||
|
is.width(arg.w);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class iostream
|
||||||
|
* \brief Input/Output stream
|
||||||
|
*/
|
||||||
|
class iostream : public istream, public ostream {
|
||||||
|
};
|
||||||
|
#endif // iostream_h
|
|
@ -0,0 +1,395 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <float.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "istream.h"
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int istream::get() {
|
||||||
|
int c;
|
||||||
|
m_gcount = 0;
|
||||||
|
c = getch();
|
||||||
|
if (c < 0) {
|
||||||
|
setstate(failbit);
|
||||||
|
} else {
|
||||||
|
m_gcount = 1;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
istream& istream::get(char& c) {
|
||||||
|
int tmp = get();
|
||||||
|
if (tmp >= 0) {
|
||||||
|
c = tmp;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
istream& istream::get(char *str, streamsize n, char delim) {
|
||||||
|
int c;
|
||||||
|
pos_t pos;
|
||||||
|
m_gcount = 0;
|
||||||
|
while ((m_gcount + 1) < n) {
|
||||||
|
c = getch(&pos);
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == delim) {
|
||||||
|
setpos(&pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str[m_gcount++] = c;
|
||||||
|
}
|
||||||
|
if (n > 0) {
|
||||||
|
str[m_gcount] = '\0';
|
||||||
|
}
|
||||||
|
if (m_gcount == 0) {
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void istream::getBool(bool *b) {
|
||||||
|
if ((flags() & boolalpha) == 0) {
|
||||||
|
getNumber(b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef __AVR__
|
||||||
|
PGM_P truePtr = PSTR("true");
|
||||||
|
PGM_P falsePtr = PSTR("false");
|
||||||
|
#else // __AVR__
|
||||||
|
const char* truePtr = "true";
|
||||||
|
const char* falsePtr = "false";
|
||||||
|
#endif // __AVR
|
||||||
|
const uint8_t true_len = 4;
|
||||||
|
const uint8_t false_len = 5;
|
||||||
|
bool trueOk = true;
|
||||||
|
bool falseOk = true;
|
||||||
|
uint8_t i = 0;
|
||||||
|
int c = readSkip();
|
||||||
|
while (1) {
|
||||||
|
#ifdef __AVR__
|
||||||
|
falseOk = falseOk && c == pgm_read_byte(falsePtr + i);
|
||||||
|
trueOk = trueOk && c == pgm_read_byte(truePtr + i);
|
||||||
|
#else // __AVR__
|
||||||
|
falseOk = falseOk && c == falsePtr[i];
|
||||||
|
trueOk = trueOk && c == truePtr[i];
|
||||||
|
#endif // __AVR__
|
||||||
|
if (trueOk == false && falseOk == false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
if (trueOk && i == true_len) {
|
||||||
|
*b = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (falseOk && i == false_len) {
|
||||||
|
*b = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
c = getch();
|
||||||
|
}
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void istream::getChar(char* ch) {
|
||||||
|
int16_t c = readSkip();
|
||||||
|
if (c < 0) {
|
||||||
|
setstate(failbit);
|
||||||
|
} else {
|
||||||
|
*ch = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// http://www.exploringbinary.com/category/numbers-in-computers/
|
||||||
|
//
|
||||||
|
int16_t const EXP_LIMIT = 100;
|
||||||
|
static const uint32_t uint32_max = (uint32_t)-1;
|
||||||
|
bool istream::getDouble(double* value) {
|
||||||
|
bool got_digit = false;
|
||||||
|
bool got_dot = false;
|
||||||
|
bool neg;
|
||||||
|
int16_t c;
|
||||||
|
bool expNeg = false;
|
||||||
|
int16_t exp = 0;
|
||||||
|
int16_t fracExp = 0;
|
||||||
|
uint32_t frac = 0;
|
||||||
|
pos_t endPos;
|
||||||
|
double pow10;
|
||||||
|
double v;
|
||||||
|
|
||||||
|
getpos(&endPos);
|
||||||
|
c = readSkip();
|
||||||
|
neg = c == '-';
|
||||||
|
if (c == '-' || c == '+') {
|
||||||
|
c = getch();
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
if (isdigit(c)) {
|
||||||
|
got_digit = true;
|
||||||
|
if (frac < uint32_max/10) {
|
||||||
|
frac = frac * 10 + (c - '0');
|
||||||
|
if (got_dot) {
|
||||||
|
fracExp--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!got_dot) {
|
||||||
|
fracExp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!got_dot && c == '.') {
|
||||||
|
got_dot = true;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
c = getch(&endPos);
|
||||||
|
}
|
||||||
|
if (!got_digit) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (c == 'e' || c == 'E') {
|
||||||
|
c = getch();
|
||||||
|
expNeg = c == '-';
|
||||||
|
if (c == '-' || c == '+') {
|
||||||
|
c = getch();
|
||||||
|
}
|
||||||
|
while (isdigit(c)) {
|
||||||
|
if (exp > EXP_LIMIT) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
exp = exp * 10 + (c - '0');
|
||||||
|
c = getch(&endPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v = static_cast<double>(frac);
|
||||||
|
exp = expNeg ? fracExp - exp : fracExp + exp;
|
||||||
|
expNeg = exp < 0;
|
||||||
|
if (expNeg) {
|
||||||
|
exp = -exp;
|
||||||
|
}
|
||||||
|
pow10 = 10.0;
|
||||||
|
while (exp) {
|
||||||
|
if (exp & 1) {
|
||||||
|
if (expNeg) {
|
||||||
|
// check for underflow
|
||||||
|
if (v < DBL_MIN * pow10 && frac != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
v /= pow10;
|
||||||
|
} else {
|
||||||
|
// check for overflow
|
||||||
|
if (v > DBL_MAX / pow10) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
v *= pow10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pow10 *= pow10;
|
||||||
|
exp >>= 1;
|
||||||
|
}
|
||||||
|
setpos(&endPos);
|
||||||
|
*value = neg ? -v : v;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
// error restore position to last good place
|
||||||
|
setpos(&endPos);
|
||||||
|
setstate(failbit);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
istream& istream::getline(char *str, streamsize n, char delim) {
|
||||||
|
pos_t pos;
|
||||||
|
int c;
|
||||||
|
m_gcount = 0;
|
||||||
|
if (n > 0) {
|
||||||
|
str[0] = '\0';
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
c = getch(&pos);
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == delim) {
|
||||||
|
m_gcount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((m_gcount + 1) >= n) {
|
||||||
|
setpos(&pos);
|
||||||
|
setstate(failbit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str[m_gcount++] = c;
|
||||||
|
str[m_gcount] = '\0';
|
||||||
|
}
|
||||||
|
if (m_gcount == 0) {
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool istream::getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num) {
|
||||||
|
int16_t c;
|
||||||
|
int8_t any = 0;
|
||||||
|
int8_t have_zero = 0;
|
||||||
|
uint8_t neg;
|
||||||
|
uint32_t val = 0;
|
||||||
|
uint32_t cutoff;
|
||||||
|
uint8_t cutlim;
|
||||||
|
pos_t endPos;
|
||||||
|
uint8_t f = flags() & basefield;
|
||||||
|
uint8_t base = f == oct ? 8 : f != hex ? 10 : 16;
|
||||||
|
getpos(&endPos);
|
||||||
|
c = readSkip();
|
||||||
|
|
||||||
|
neg = c == '-' ? 1 : 0;
|
||||||
|
if (c == '-' || c == '+') {
|
||||||
|
c = getch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base == 16 && c == '0') { // TESTSUITE
|
||||||
|
c = getch(&endPos);
|
||||||
|
if (c == 'X' || c == 'x') {
|
||||||
|
c = getch();
|
||||||
|
// remember zero in case no hex digits follow x/X
|
||||||
|
have_zero = 1;
|
||||||
|
} else {
|
||||||
|
any = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set values for overflow test
|
||||||
|
cutoff = neg ? negMax : posMax;
|
||||||
|
cutlim = cutoff % base;
|
||||||
|
cutoff /= base;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (isdigit(c)) {
|
||||||
|
c -= '0';
|
||||||
|
} else if (isalpha(c)) {
|
||||||
|
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c >= base) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (val > cutoff || (val == cutoff && c > cutlim)) {
|
||||||
|
// indicate overflow error
|
||||||
|
any = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
val = val * base + c;
|
||||||
|
c = getch(&endPos);
|
||||||
|
any = 1;
|
||||||
|
}
|
||||||
|
setpos(&endPos);
|
||||||
|
if (any > 0 || (have_zero && any >= 0)) {
|
||||||
|
*num = neg ? -val : val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
setstate(failbit);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void istream::getStr(char *str) {
|
||||||
|
pos_t pos;
|
||||||
|
uint16_t i = 0;
|
||||||
|
uint16_t m = width() ? width() - 1 : 0XFFFE;
|
||||||
|
if (m != 0) {
|
||||||
|
getpos(&pos);
|
||||||
|
int c = readSkip();
|
||||||
|
|
||||||
|
while (i < m) {
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (isspace(c)) {
|
||||||
|
setpos(&pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str[i++] = c;
|
||||||
|
c = getch(&pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str[i] = '\0';
|
||||||
|
if (i == 0) {
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
width(0);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
istream& istream::ignore(streamsize n, int delim) {
|
||||||
|
int c;
|
||||||
|
m_gcount = 0;
|
||||||
|
while (m_gcount < n) {
|
||||||
|
c = getch();
|
||||||
|
if (c < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_gcount++;
|
||||||
|
if (c == delim) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int istream::peek() {
|
||||||
|
int16_t c;
|
||||||
|
pos_t pos;
|
||||||
|
m_gcount = 0;
|
||||||
|
getpos(&pos);
|
||||||
|
c = getch();
|
||||||
|
if (c < 0) {
|
||||||
|
if (!bad()) {
|
||||||
|
setstate(eofbit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setpos(&pos);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int16_t istream::readSkip() {
|
||||||
|
int16_t c;
|
||||||
|
do {
|
||||||
|
c = getch();
|
||||||
|
} while (isspace(c) && (flags() & skipws));
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/** used to implement ws() */
|
||||||
|
void istream::skipWhite() {
|
||||||
|
int c;
|
||||||
|
pos_t pos;
|
||||||
|
do {
|
||||||
|
c = getch(&pos);
|
||||||
|
} while (isspace(c));
|
||||||
|
setpos(&pos);
|
||||||
|
}
|
|
@ -0,0 +1,384 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef istream_h
|
||||||
|
#define istream_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief \ref istream class
|
||||||
|
*/
|
||||||
|
#include "ios.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class istream
|
||||||
|
* \brief Input Stream
|
||||||
|
*/
|
||||||
|
class istream : public virtual ios {
|
||||||
|
public:
|
||||||
|
istream() {}
|
||||||
|
/** call manipulator
|
||||||
|
* \param[in] pf function to call
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
istream& operator>>(istream& (*pf)(istream& str)) {
|
||||||
|
return pf(*this);
|
||||||
|
}
|
||||||
|
/** call manipulator
|
||||||
|
* \param[in] pf function to call
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
istream& operator>>(ios_base& (*pf)(ios_base& str)) {
|
||||||
|
pf(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** call manipulator
|
||||||
|
* \param[in] pf function to call
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
istream& operator>>(ios& (*pf)(ios& str)) {
|
||||||
|
pf(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a character string
|
||||||
|
* \param[out] str location to store the string.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>>(char *str) {
|
||||||
|
getStr(str);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a character
|
||||||
|
* \param[out] ch location to store the character.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>>(char& ch) {
|
||||||
|
getChar(&ch);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a character string
|
||||||
|
* \param[out] str location to store the string.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>>(signed char *str) {
|
||||||
|
getStr(reinterpret_cast<char*>(str));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a character
|
||||||
|
* \param[out] ch location to store the character.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>>(signed char& ch) {
|
||||||
|
getChar(reinterpret_cast<char*>(&ch));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a character string
|
||||||
|
* \param[out] str location to store the string.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>>(unsigned char *str) {
|
||||||
|
getStr(reinterpret_cast<char*>(str));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a character
|
||||||
|
* \param[out] ch location to store the character.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>>(unsigned char& ch) {
|
||||||
|
getChar(reinterpret_cast<char*>(&ch));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type bool.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>>(bool& arg) {
|
||||||
|
getBool(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type short.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>>(short& arg) { // NOLINT
|
||||||
|
getNumber(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type unsigned short.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>>(unsigned short& arg) { // NOLINT
|
||||||
|
getNumber(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type int.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>>(int& arg) {
|
||||||
|
getNumber(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type unsigned int.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>>(unsigned int& arg) {
|
||||||
|
getNumber(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type long.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>>(long& arg) { // NOLINT
|
||||||
|
getNumber(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type unsigned long.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>>(unsigned long& arg) { // NOLINT
|
||||||
|
getNumber(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type double.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>> (double& arg) {
|
||||||
|
getDouble(&arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type float.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream &operator>> (float& arg) {
|
||||||
|
double v;
|
||||||
|
getDouble(&v);
|
||||||
|
arg = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a value of type void*.
|
||||||
|
* \param[out] arg location to store the value.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& operator>> (void*& arg) {
|
||||||
|
uint32_t val;
|
||||||
|
getNumber(&val);
|
||||||
|
arg = reinterpret_cast<void*>(val);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* \return The number of characters extracted by the last unformatted
|
||||||
|
* input function.
|
||||||
|
*/
|
||||||
|
streamsize gcount() const {
|
||||||
|
return m_gcount;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract a character if one is available.
|
||||||
|
*
|
||||||
|
* \return The character or -1 if a failure occurs. A failure is indicated
|
||||||
|
* by the stream state.
|
||||||
|
*/
|
||||||
|
int get();
|
||||||
|
/**
|
||||||
|
* Extract a character if one is available.
|
||||||
|
*
|
||||||
|
* \param[out] ch location to receive the extracted character.
|
||||||
|
*
|
||||||
|
* \return always returns *this. A failure is indicated by the stream state.
|
||||||
|
*/
|
||||||
|
istream& get(char& ch);
|
||||||
|
/**
|
||||||
|
* Extract characters.
|
||||||
|
*
|
||||||
|
* \param[out] str Location to receive extracted characters.
|
||||||
|
* \param[in] n Size of str.
|
||||||
|
* \param[in] delim Delimiter
|
||||||
|
*
|
||||||
|
* Characters are extracted until extraction fails, n is less than 1,
|
||||||
|
* n-1 characters are extracted, or the next character equals
|
||||||
|
* \a delim (delim is not extracted). If no characters are extracted
|
||||||
|
* failbit is set. If end-of-file occurs the eofbit is set.
|
||||||
|
*
|
||||||
|
* \return always returns *this. A failure is indicated by the stream state.
|
||||||
|
*/
|
||||||
|
istream& get(char *str, streamsize n, char delim = '\n');
|
||||||
|
/**
|
||||||
|
* Extract characters
|
||||||
|
*
|
||||||
|
* \param[out] str Location to receive extracted characters.
|
||||||
|
* \param[in] n Size of str.
|
||||||
|
* \param[in] delim Delimiter
|
||||||
|
*
|
||||||
|
* Characters are extracted until extraction fails,
|
||||||
|
* the next character equals \a delim (delim is extracted), or n-1
|
||||||
|
* characters are extracted.
|
||||||
|
*
|
||||||
|
* The failbit is set if no characters are extracted or n-1 characters
|
||||||
|
* are extracted. If end-of-file occurs the eofbit is set.
|
||||||
|
*
|
||||||
|
* \return always returns *this. A failure is indicated by the stream state.
|
||||||
|
*/
|
||||||
|
istream& getline(char *str, streamsize n, char delim = '\n');
|
||||||
|
/**
|
||||||
|
* Extract characters and discard them.
|
||||||
|
*
|
||||||
|
* \param[in] n maximum number of characters to ignore.
|
||||||
|
* \param[in] delim Delimiter.
|
||||||
|
*
|
||||||
|
* Characters are extracted until extraction fails, \a n characters
|
||||||
|
* are extracted, or the next input character equals \a delim
|
||||||
|
* (the delimiter is extracted). If end-of-file occurs the eofbit is set.
|
||||||
|
*
|
||||||
|
* Failures are indicated by the state of the stream.
|
||||||
|
*
|
||||||
|
* \return *this
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
istream& ignore(streamsize n = 1, int delim = -1);
|
||||||
|
/**
|
||||||
|
* Return the next available character without consuming it.
|
||||||
|
*
|
||||||
|
* \return The character if the stream state is good else -1;
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int peek();
|
||||||
|
// istream& read(char *str, streamsize count);
|
||||||
|
// streamsize readsome(char *str, streamsize count);
|
||||||
|
/**
|
||||||
|
* \return the stream position
|
||||||
|
*/
|
||||||
|
pos_type tellg() {
|
||||||
|
return tellpos();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the stream position
|
||||||
|
* \param[in] pos The absolute position in which to move the read pointer.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& seekg(pos_type pos) {
|
||||||
|
if (!seekpos(pos)) {
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the stream position.
|
||||||
|
*
|
||||||
|
* \param[in] off An offset to move the read pointer relative to way.
|
||||||
|
* \a off is a signed 32-bit int so the offset is limited to +- 2GB.
|
||||||
|
* \param[in] way One of ios::beg, ios::cur, or ios::end.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
istream& seekg(off_type off, seekdir way) {
|
||||||
|
if (!seekoff(off, way)) {
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
void skipWhite();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
/**
|
||||||
|
* Internal - do not use
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
virtual int16_t getch() = 0;
|
||||||
|
/**
|
||||||
|
* Internal - do not use
|
||||||
|
* \param[out] pos
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
int16_t getch(pos_t* pos) {
|
||||||
|
getpos(pos);
|
||||||
|
return getch();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Internal - do not use
|
||||||
|
* \param[out] pos
|
||||||
|
*/
|
||||||
|
virtual void getpos(pos_t* pos) = 0;
|
||||||
|
/**
|
||||||
|
* Internal - do not use
|
||||||
|
* \param[in] pos
|
||||||
|
*/
|
||||||
|
virtual bool seekoff(off_type off, seekdir way) = 0;
|
||||||
|
virtual bool seekpos(pos_type pos) = 0;
|
||||||
|
virtual void setpos(pos_t* pos) = 0;
|
||||||
|
virtual pos_type tellpos() = 0;
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
|
private:
|
||||||
|
void getBool(bool *b);
|
||||||
|
void getChar(char* ch);
|
||||||
|
bool getDouble(double* value);
|
||||||
|
template <typename T> void getNumber(T* value);
|
||||||
|
bool getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num);
|
||||||
|
void getStr(char *str);
|
||||||
|
int16_t readSkip();
|
||||||
|
|
||||||
|
size_t m_gcount;
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
|
void istream::getNumber(T* value) {
|
||||||
|
uint32_t tmp;
|
||||||
|
if ((T)-1 < 0) {
|
||||||
|
// number is signed, max positive value
|
||||||
|
uint32_t const m = ((uint32_t)-1) >> (33 - sizeof(T) * 8);
|
||||||
|
// max absolute value of negative number is m + 1.
|
||||||
|
if (getNumber(m, m + 1, &tmp)) {
|
||||||
|
*value = (T)tmp;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// max unsigned value for T
|
||||||
|
uint32_t const m = (T)-1;
|
||||||
|
if (getNumber(m, m, &tmp)) {
|
||||||
|
*value = (T)tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // istream_h
|
|
@ -0,0 +1,153 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include "ostream.h"
|
||||||
|
#ifndef PSTR
|
||||||
|
#define PSTR(x) x
|
||||||
|
#endif // PSTR
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::do_fill(unsigned len) {
|
||||||
|
for (; len < width(); len++) {
|
||||||
|
putch(fill());
|
||||||
|
}
|
||||||
|
width(0);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::fill_not_left(unsigned len) {
|
||||||
|
if ((flags() & adjustfield) != left) {
|
||||||
|
do_fill(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::putBool(bool b) {
|
||||||
|
if (flags() & boolalpha) {
|
||||||
|
if (b) {
|
||||||
|
putPgm(PSTR("true"));
|
||||||
|
} else {
|
||||||
|
putPgm(PSTR("false"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
putChar(b ? '1' : '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::putChar(char c) {
|
||||||
|
fill_not_left(1);
|
||||||
|
putch(c);
|
||||||
|
do_fill(1);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::putDouble(double n) {
|
||||||
|
uint8_t nd = precision();
|
||||||
|
double round = 0.5;
|
||||||
|
char sign;
|
||||||
|
char buf[13]; // room for sign, 10 digits, '.', and zero byte
|
||||||
|
char *ptr = buf + sizeof(buf) - 1;
|
||||||
|
char *str = ptr;
|
||||||
|
// terminate string
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
// get sign and make nonnegative
|
||||||
|
if (n < 0.0) {
|
||||||
|
sign = '-';
|
||||||
|
n = -n;
|
||||||
|
} else {
|
||||||
|
sign = flags() & showpos ? '+' : '\0';
|
||||||
|
}
|
||||||
|
// check for larger than uint32_t
|
||||||
|
if (n > 4.0E9) {
|
||||||
|
putPgm(PSTR("BIG FLT"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// round up and separate int and fraction parts
|
||||||
|
for (uint8_t i = 0; i < nd; ++i) {
|
||||||
|
round *= 0.1;
|
||||||
|
}
|
||||||
|
n += round;
|
||||||
|
uint32_t intPart = n;
|
||||||
|
double fractionPart = n - intPart;
|
||||||
|
|
||||||
|
// format intPart and decimal point
|
||||||
|
if (nd || (flags() & showpoint)) {
|
||||||
|
*--str = '.';
|
||||||
|
}
|
||||||
|
str = fmtNum(intPart, str, 10);
|
||||||
|
|
||||||
|
// calculate length for fill
|
||||||
|
uint8_t len = sign ? 1 : 0;
|
||||||
|
len += nd + ptr - str;
|
||||||
|
|
||||||
|
// extract adjust field
|
||||||
|
fmtflags adj = flags() & adjustfield;
|
||||||
|
if (adj == internal) {
|
||||||
|
if (sign) {
|
||||||
|
putch(sign);
|
||||||
|
}
|
||||||
|
do_fill(len);
|
||||||
|
} else {
|
||||||
|
// do fill for right
|
||||||
|
fill_not_left(len);
|
||||||
|
if (sign) {
|
||||||
|
*--str = sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putstr(str);
|
||||||
|
// output fraction
|
||||||
|
while (nd-- > 0) {
|
||||||
|
fractionPart *= 10.0;
|
||||||
|
int digit = static_cast<int>(fractionPart);
|
||||||
|
putch(digit + '0');
|
||||||
|
fractionPart -= digit;
|
||||||
|
}
|
||||||
|
// do fill if not done above
|
||||||
|
do_fill(len);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::putNum(int32_t n) {
|
||||||
|
bool neg = n < 0 && flagsToBase() == 10;
|
||||||
|
putNum((uint32_t)(neg ? -n : n), neg);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::putNum(int64_t n) {
|
||||||
|
bool neg = n < 0 && flagsToBase() == 10;
|
||||||
|
putNum((uint64_t)(neg ? -n : n), neg);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::putPgm(const char* str) {
|
||||||
|
int n;
|
||||||
|
for (n = 0; pgm_read_byte(&str[n]); n++) {}
|
||||||
|
fill_not_left(n);
|
||||||
|
for (uint8_t c; (c = pgm_read_byte(str)); str++) {
|
||||||
|
putch(c);
|
||||||
|
}
|
||||||
|
do_fill(n);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void ostream::putStr(const char *str) {
|
||||||
|
unsigned n = strlen(str);
|
||||||
|
fill_not_left(n);
|
||||||
|
putstr(str);
|
||||||
|
do_fill(n);
|
||||||
|
}
|
|
@ -0,0 +1,348 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2011-2022 Bill Greiman
|
||||||
|
* This file is part of the SdFat library for SD memory cards.
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef ostream_h
|
||||||
|
#define ostream_h
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief \ref ostream class
|
||||||
|
*/
|
||||||
|
#include "ios.h"
|
||||||
|
//==============================================================================
|
||||||
|
/**
|
||||||
|
* \class ostream
|
||||||
|
* \brief Output Stream
|
||||||
|
*/
|
||||||
|
class ostream : public virtual ios {
|
||||||
|
public:
|
||||||
|
ostream() {}
|
||||||
|
|
||||||
|
/** call manipulator
|
||||||
|
* \param[in] pf function to call
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream& operator<< (ostream& (*pf)(ostream& str)) {
|
||||||
|
return pf(*this);
|
||||||
|
}
|
||||||
|
/** call manipulator
|
||||||
|
* \param[in] pf function to call
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream& operator<< (ios_base& (*pf)(ios_base& str)) {
|
||||||
|
pf(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output bool
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (bool arg) {
|
||||||
|
putBool(arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output string
|
||||||
|
* \param[in] arg string to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (const char *arg) {
|
||||||
|
putStr(arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output string
|
||||||
|
* \param[in] arg string to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (const signed char *arg) {
|
||||||
|
putStr((const char*)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output string
|
||||||
|
* \param[in] arg string to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (const unsigned char *arg) {
|
||||||
|
putStr((const char*)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#if ENABLE_ARDUINO_STRING
|
||||||
|
/** Output string
|
||||||
|
* \param[in] arg string to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (const String& arg) {
|
||||||
|
putStr(arg.c_str());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ARDUINO_STRING
|
||||||
|
/** Output character
|
||||||
|
* \param[in] arg character to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (char arg) {
|
||||||
|
putChar(arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output character
|
||||||
|
* \param[in] arg character to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (signed char arg) {
|
||||||
|
putChar(static_cast<char>(arg));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output character
|
||||||
|
* \param[in] arg character to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (unsigned char arg) {
|
||||||
|
putChar(static_cast<char>(arg));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output double
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (double arg) {
|
||||||
|
putDouble(arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output float
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (float arg) {
|
||||||
|
putDouble(arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output signed short
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (short arg) { // NOLINT
|
||||||
|
putNum((int32_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output unsigned short
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (unsigned short arg) { // NOLINT
|
||||||
|
putNum((uint32_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output signed int
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (int arg) {
|
||||||
|
putNum((int32_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output unsigned int
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (unsigned int arg) {
|
||||||
|
putNum((uint32_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output signed long
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (long arg) { // NOLINT
|
||||||
|
putNum((int32_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output unsigned long
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (unsigned long arg) { // NOLINT
|
||||||
|
putNum((uint32_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output signed long long
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (long long arg) { // NOLINT
|
||||||
|
putNum((int64_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output unsigned long long
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (unsigned long long arg) { // NOLINT
|
||||||
|
putNum((uint64_t)arg);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output pointer
|
||||||
|
* \param[in] arg value to output
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream& operator<< (const void* arg) {
|
||||||
|
putNum(reinterpret_cast<uint32_t>(arg));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/** Output a string from flash using the Arduino F() macro.
|
||||||
|
* \param[in] arg pointing to flash string
|
||||||
|
* \return the stream
|
||||||
|
*/
|
||||||
|
ostream &operator<< (const __FlashStringHelper *arg) {
|
||||||
|
putPgm(reinterpret_cast<const char*>(arg));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Puts a character in a stream.
|
||||||
|
*
|
||||||
|
* The unformatted output function inserts the element \a ch.
|
||||||
|
* It returns *this.
|
||||||
|
*
|
||||||
|
* \param[in] ch The character
|
||||||
|
* \return A reference to the ostream object.
|
||||||
|
*/
|
||||||
|
ostream& put(char ch) {
|
||||||
|
putch(ch);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
// ostream& write(char *str, streamsize count);
|
||||||
|
/**
|
||||||
|
* Flushes the buffer associated with this stream. The flush function
|
||||||
|
* calls the sync function of the associated file.
|
||||||
|
* \return A reference to the ostream object.
|
||||||
|
*/
|
||||||
|
ostream& flush() {
|
||||||
|
if (!sync()) {
|
||||||
|
setstate(badbit);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* \return the stream position
|
||||||
|
*/
|
||||||
|
pos_type tellp() {
|
||||||
|
return tellpos();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the stream position
|
||||||
|
* \param[in] pos The absolute position in which to move the write pointer.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
ostream& seekp(pos_type pos) {
|
||||||
|
if (!seekpos(pos)) {
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the stream position.
|
||||||
|
*
|
||||||
|
* \param[in] off An offset to move the write pointer relative to way.
|
||||||
|
* \a off is a signed 32-bit int so the offset is limited to +- 2GB.
|
||||||
|
* \param[in] way One of ios::beg, ios::cur, or ios::end.
|
||||||
|
* \return Is always *this. Failure is indicated by the state of *this.
|
||||||
|
*/
|
||||||
|
ostream& seekp(off_type off, seekdir way) {
|
||||||
|
if (!seekoff(off, way)) {
|
||||||
|
setstate(failbit);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond SHOW_PROTECTED
|
||||||
|
/** Put character with binary/text conversion
|
||||||
|
* \param[in] ch character to write
|
||||||
|
*/
|
||||||
|
virtual void putch(char ch) = 0;
|
||||||
|
virtual void putstr(const char *str) = 0;
|
||||||
|
virtual bool seekoff(off_type pos, seekdir way) = 0;
|
||||||
|
virtual bool seekpos(pos_type pos) = 0;
|
||||||
|
virtual bool sync() = 0;
|
||||||
|
virtual pos_type tellpos() = 0;
|
||||||
|
/// @endcond
|
||||||
|
private:
|
||||||
|
void do_fill(unsigned len);
|
||||||
|
void fill_not_left(unsigned len);
|
||||||
|
void putBool(bool b);
|
||||||
|
void putChar(char c);
|
||||||
|
void putDouble(double n);
|
||||||
|
void putNum(int32_t n);
|
||||||
|
void putNum(int64_t n);
|
||||||
|
void putNum(uint32_t n) {putNum(n, false);}
|
||||||
|
void putNum(uint64_t n) {putNum(n, false);}
|
||||||
|
void putPgm(const char* str);
|
||||||
|
void putStr(const char* str);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
char* fmtNum(T n, char *ptr, uint8_t base) {
|
||||||
|
char a = flags() & uppercase ? 'A' - 10 : 'a' - 10;
|
||||||
|
do {
|
||||||
|
T m = n;
|
||||||
|
n /= base;
|
||||||
|
char c = m - base * n;
|
||||||
|
*--ptr = c < 10 ? c + '0' : c + a;
|
||||||
|
} while (n);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void putNum(T n, bool neg) {
|
||||||
|
char buf[(8*sizeof(T) + 2)/3 + 2];
|
||||||
|
char* ptr = buf + sizeof(buf) - 1;
|
||||||
|
char* num;
|
||||||
|
char* str;
|
||||||
|
uint8_t base = flagsToBase();
|
||||||
|
*ptr = '\0';
|
||||||
|
str = num = fmtNum(n, ptr, base);
|
||||||
|
if (base == 10) {
|
||||||
|
if (neg) {
|
||||||
|
*--str = '-';
|
||||||
|
} else if (flags() & showpos) {
|
||||||
|
*--str = '+';
|
||||||
|
}
|
||||||
|
} else if (flags() & showbase) {
|
||||||
|
if (flags() & hex) {
|
||||||
|
*--str = flags() & uppercase ? 'X' : 'x';
|
||||||
|
}
|
||||||
|
*--str = '0';
|
||||||
|
}
|
||||||
|
uint8_t len = ptr - str;
|
||||||
|
fmtflags adj = flags() & adjustfield;
|
||||||
|
if (adj == internal) {
|
||||||
|
while (str < num) {
|
||||||
|
putch(*str++);
|
||||||
|
}
|
||||||
|
do_fill(len);
|
||||||
|
} else {
|
||||||
|
// do fill for right
|
||||||
|
fill_not_left(len);
|
||||||
|
}
|
||||||
|
putstr(str);
|
||||||
|
do_fill(len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // ostream_h
|
|
@ -0,0 +1,102 @@
|
||||||
|
{
|
||||||
|
"name": "no-OS-FatFS-SD-SDIO-SPI-RPi-Pico",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"description": "Library for SD Cards on the RP2040",
|
||||||
|
"keywords": ["Data Storage", "FatFs", "SD card", "Secure Digital card", "SPI", "SDIO"],
|
||||||
|
"repository":
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/carlk3/no-OS-FatFS-SD-SDIO-SPI-RPi-Pico.git"
|
||||||
|
},
|
||||||
|
"authors":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Carl Kugler",
|
||||||
|
"email": "carlk3@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 AND MIT-Modern-Variant",
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": "raspberrypi",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"name": "Example1_one_SD_card_on_SPI",
|
||||||
|
"base": "examples/PlatformIO/one_SPI.C++",
|
||||||
|
"files": [
|
||||||
|
"platformio.ini",
|
||||||
|
"src/main.cpp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example2_one_SD_card_on_SDIO",
|
||||||
|
"base": "examples/PlatformIO/one_SDIO",
|
||||||
|
"files": [
|
||||||
|
"platformio.ini",
|
||||||
|
"src/main.cpp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example3_one_SPI_one_SDIO",
|
||||||
|
"base": "examples/PlatformIO/one_SPI_one_SDIO",
|
||||||
|
"files": [
|
||||||
|
"platformio.ini",
|
||||||
|
"src/main.cpp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example4_data_logger",
|
||||||
|
"base": "examples/PlatformIO/data_logger",
|
||||||
|
"files": [
|
||||||
|
"platformio.ini",
|
||||||
|
"src/main.cpp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example5_bench",
|
||||||
|
"base": "examples/PlatformIO/bench2",
|
||||||
|
"files": [
|
||||||
|
"platformio.ini",
|
||||||
|
"src/main.cpp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example6_hw_debug",
|
||||||
|
"base": "examples/PlatformIO/hw_debug",
|
||||||
|
"files": [
|
||||||
|
"platformio.ini",
|
||||||
|
"src/main.cpp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"build": {
|
||||||
|
"srcFilter": [
|
||||||
|
"-<.git/> -<.svn/>",
|
||||||
|
"+<ff15/source/ff.c>",
|
||||||
|
"+<ff15/source/ffsystem.c>",
|
||||||
|
"+<ff15/source/ffunicode.c>",
|
||||||
|
"+<sd_driver/dma_interrupts.c>",
|
||||||
|
"+<sd_driver/sd_card.c>",
|
||||||
|
"+<sd_driver/SDIO/rp2040_sdio.c>",
|
||||||
|
"+<sd_driver/SDIO/sd_card_sdio.c>",
|
||||||
|
"+<sd_driver/SPI/crc.c>",
|
||||||
|
"+<sd_driver/SPI/sd_card_spi.c>",
|
||||||
|
"+<sd_driver/SPI/sd_spi.c>",
|
||||||
|
"+<sd_driver/SPI/my_spi.c>",
|
||||||
|
"+<src/f_util.c>",
|
||||||
|
"+<src/FatFsSd.cpp>",
|
||||||
|
"+<src/glue.c>",
|
||||||
|
"+<src/my_debug.c>",
|
||||||
|
"+<src/rtc.c>",
|
||||||
|
"+<src/rtc.c>",
|
||||||
|
"+<src/util.c>"
|
||||||
|
],
|
||||||
|
"flags": [
|
||||||
|
"-I include",
|
||||||
|
"-I src/sd_driver",
|
||||||
|
"-I src/include",
|
||||||
|
"-I src/ff15/source",
|
||||||
|
"-Wno-psabi",
|
||||||
|
"-D PICO_MAX_SHARED_IRQ_HANDLERS=8u"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
add_library(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico INTERFACE)
|
||||||
|
|
||||||
|
pico_generate_pio_header(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico ${CMAKE_CURRENT_LIST_DIR}/sd_driver/SDIO/rp2040_sdio.pio)
|
||||||
|
|
||||||
|
target_compile_definitions(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico INTERFACE
|
||||||
|
PICO_MAX_SHARED_IRQ_HANDLERS=8u
|
||||||
|
)
|
||||||
|
# target_compile_options(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico INTERFACE -ffile-prefix-map=${CMAKE_CURRENT_LIST_DIR}=)
|
||||||
|
|
||||||
|
target_sources(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ff.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ffsystem.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ffunicode.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/sd_card.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/dma_interrupts.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SDIO/rp2040_sdio.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SDIO/sd_card_sdio.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SPI/crc.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SPI/sd_spi.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SPI/sd_card_spi.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SPI/my_spi.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/crash.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/f_util.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/ff_stdio.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/glue.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/my_debug.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/rtc.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/util.c
|
||||||
|
)
|
||||||
|
target_include_directories(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico INTERFACE
|
||||||
|
ff15/source
|
||||||
|
sd_driver
|
||||||
|
include
|
||||||
|
)
|
||||||
|
target_link_libraries(no-OS-FatFS-SD-SDIO-SPI-RPi-Pico INTERFACE
|
||||||
|
hardware_dma
|
||||||
|
hardware_pio
|
||||||
|
hardware_rtc
|
||||||
|
hardware_spi
|
||||||
|
pico_stdlib
|
||||||
|
cmsis_core
|
||||||
|
)
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
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
|
||||||
|
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.
|
|
@ -0,0 +1,110 @@
|
||||||
|
### Warning: This is SdFat Version 2.
|
||||||
|
|
||||||
|
Earlier releases of Version 1 are here:
|
||||||
|
|
||||||
|
https://github.com/greiman/SdFat/releases
|
||||||
|
|
||||||
|
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 File tab for details.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The SdFat and File classes are defined in terms of the above classes by
|
||||||
|
typedefs. Edit SdFatConfig.h to select class options.
|
||||||
|
|
||||||
|
Please continue by reading the html documentation in the SdFat/doc folder.
|
|
@ -0,0 +1,25 @@
|
||||||
|
Software
|
||||||
|
-----
|
||||||
|
|
||||||
|
ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
|
||||||
|
|
||||||
|
ZuluSCSI™ is based on both SCSI2SD V6 and BlueSCSI codebases,
|
||||||
|
and licensed under the GPL version 3 or any later version.
|
||||||
|
|
||||||
|
https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
----
|
||||||
|
SCSI2SD code is Copyright (C) 2016-2022 Michael McMaster.
|
||||||
|
BlueSCSI code is Copyright (C) 2021 Eric Helgeson.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue