1
0
Fork 0

Compare commits

...

11 Commits

Author SHA1 Message Date
radex efb94c2d78
Merge branch 'sd2' 2024-06-02 15:44:51 +02:00
radex 2cc5f3d6fc
clean up 2024-06-02 15:44:49 +02:00
radex 72045008f8
use new sd lib 2024-06-02 15:31:43 +02:00
radex 20acc815de
Merge branch 'main' into sd2 2024-06-02 14:06:35 +02:00
radex 6ba449a1a4
gamma 2024-06-02 14:01:57 +02:00
radex da893fd9de
wip gamma correction 2024-06-02 12:05:42 +02:00
radex 62a69a4c6d
pio-based everything 8-bit color work! 2024-06-02 11:02:47 +02:00
radex 634406bb2e
huh still builds 2024-06-01 13:03:32 +02:00
radex a31ce07582
add no-OS-FatFS-SD-SDIO-SPI-RPi-Pico-main (wonderful name ain't it) 2024-06-01 12:54:07 +02:00
radex c2a5081fbc
switch to another arduino core? 2024-06-01 12:47:54 +02:00
radex 92edaca309
test: comment out old sd implementation 2024-05-31 23:44:39 +02:00
170 changed files with 49947 additions and 210 deletions

View File

@ -0,0 +1,9 @@
**/build
*.html
*.png
*.zip
*.xlsx
*.bak
**/.vscode
*.tar.gz
PlatformIO.notes.txt

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

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

View File

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

View File

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

View File

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

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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++;
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

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

View File

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

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

View File

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

View File

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

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

View File

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

View 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 ---*/

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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