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