Make SD-card work
This commit is contained in:
@@ -1,28 +0,0 @@
|
|||||||
add_library(FatFs_SPI INTERFACE)
|
|
||||||
target_sources(FatFs_SPI INTERFACE
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ffsystem.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ffunicode.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ff.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/sd_driver/sd_spi.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/sd_driver/demo_logging.c
|
|
||||||
# ${CMAKE_CURRENT_LIST_DIR}/sd_driver/hw_config.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/sd_driver/spi.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/sd_driver/sd_card.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/sd_driver/crc.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/glue.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/f_util.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ff_stdio.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/my_debug.c
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/rtc.c
|
|
||||||
)
|
|
||||||
target_include_directories(FatFs_SPI INTERFACE
|
|
||||||
ff15/source
|
|
||||||
sd_driver
|
|
||||||
include
|
|
||||||
)
|
|
||||||
target_link_libraries(FatFs_SPI INTERFACE
|
|
||||||
hardware_spi
|
|
||||||
hardware_dma
|
|
||||||
hardware_rtc
|
|
||||||
pico_stdlib
|
|
||||||
)
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
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.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
FatFs License
|
|
||||||
|
|
||||||
FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files.
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------/
|
|
||||||
/ 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.
|
|
||||||
/----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
Therefore FatFs license is one of the BSD-style licenses, but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, do not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses include GNU GPL. When you redistribute the FatFs source code with changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license.
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
* {margin: 0; padding: 0; border-width: 0;}
|
|
||||||
body {margin: 8px; background-color: #e0ffff; font-color: black; font-family: serif; line-height: 133%; max-width: 1024px;}
|
|
||||||
a:link {color: blue;}
|
|
||||||
a:visited {color: darkmagenta;}
|
|
||||||
a:hover {background-color: #a0ffff;}
|
|
||||||
a:active {color: darkmagenta; overflow: hidden; outline:none; position: relative; top: 1px; left: 1px;}
|
|
||||||
abbr {border-width: 1px;}
|
|
||||||
|
|
||||||
p {margin: 0 0 0.3em 1em;}
|
|
||||||
i {margin: 0 0.3em 0 0;}
|
|
||||||
b {margin: 0 0.1em;}
|
|
||||||
em {font-style: normal; font-weight: bold; margin: 0 0.1em;}
|
|
||||||
strong {}
|
|
||||||
pre {border: 1px dashed gray; margin: 0.5em 1em; padding: 0.5em; line-height: 1.2em; font-size: 85%; font-family: "Consolas", "Courier New", monospace; background-color: white;}
|
|
||||||
pre span.c {color: green;}
|
|
||||||
pre span.k {color: blue;}
|
|
||||||
pre span.e {color: red;}
|
|
||||||
pre span.b {font-weight: bold;}
|
|
||||||
pre span.arg {font-style: italic;}
|
|
||||||
tt {margin: 0 0.2em; font-size: 0.85em; font-family: "Consolas", "Courier New", monospace; }
|
|
||||||
tt.arg {font-style: italic;}
|
|
||||||
ol {margin: 0.5em 2.5em;}
|
|
||||||
ul {margin: 0.5em 2em;}
|
|
||||||
ul ul {margin: 0 2em 0.5em 1em;}
|
|
||||||
dl {margin: 0.5em 1em;}
|
|
||||||
dd {margin: 0 2em;}
|
|
||||||
dt {font-size: 0.85em; font-family: "Consolas", "Courier New", monospace;}
|
|
||||||
dl.par dt {margin: 0.5em 0 0 0 ; font-style: italic; }
|
|
||||||
dl.ret dt {margin: 0.5em 0 0 0 ; font-size: 0.85em; font-family: "Consolas", "Courier New", monospace; font-weight: bold; }
|
|
||||||
hr {border-width: 1px; margin: 1em;}
|
|
||||||
div.abst {font-family: sans-serif;}
|
|
||||||
div.para {clear: both; font-family: serif;}
|
|
||||||
div.ret a {font-size: 0.85em; font-family: "Consolas", "Courier New", monospace; }
|
|
||||||
.equ {text-indent: 0; margin: 1em 2em 1em;}
|
|
||||||
.indent {margin-left: 2em;}
|
|
||||||
.rset {float: right; margin: 0.3em 0 0.5em 0.5em;}
|
|
||||||
.lset {float: left; margin: 0.3em 0.5em 0.5em 0.5em;}
|
|
||||||
ul.flat li {list-style-type: none; margin: 0;}
|
|
||||||
a.imglnk img {border: 1px solid;}
|
|
||||||
.iequ {white-space: nowrap; font-weight: bold;}
|
|
||||||
.clr {clear: both;}
|
|
||||||
.it {font-style: italic;}
|
|
||||||
.mfd {font-size: 0.7em; padding: 0 1px; border: 1px solid; white-space : nowrap}
|
|
||||||
.ral {text-align: right; }
|
|
||||||
.lal {text-align: left; }
|
|
||||||
.cal {text-align: center; }
|
|
||||||
|
|
||||||
h1 {line-height: 1em; font-size: 2em; font-family: sans-serif; padding: 0.3em 0 0.3em;}
|
|
||||||
h2 {font-size: 2em; font-family: sans-serif; background-color: #d8d8FF; padding: 0.5em 0.5em; margin: 0 0 0.5em;}
|
|
||||||
h3 {font-size: 1.5em; font-family: sans-serif; margin: 1.5em 0 0.5em;}
|
|
||||||
div.doc h3 {border-color: #b0d8d8; border-style: solid; border-width: 0px 0px 4px 12px; padding: 4px; margin-top: 3em;}
|
|
||||||
h4 {font-size: 1.2em; font-family: sans-serif; margin: 2em 0 0.2em;}
|
|
||||||
h5 {font-size: 1em; font-family: sans-serif; margin: 1em 0 0em;}
|
|
||||||
p.hdd {float: right; text-align: right; margin-top: 0.5em;}
|
|
||||||
hr.hds {clear: both; margin-bottom: 1em;}
|
|
||||||
kbd {letter-spacing: 0;}
|
|
||||||
small {font-size: 80%;}
|
|
||||||
.indent {margin-left: 2em;}
|
|
||||||
|
|
||||||
/* Tables */
|
|
||||||
table {margin: 0.5em 1em; border-collapse: collapse; border: 2px solid gray; }
|
|
||||||
table caption {font-family: sans-serif; font-weight: bold;}
|
|
||||||
table th {background-color: white; border-style: solid; border-width: 1px 1px 2px; border-color: gray; padding: 0 3px; vertical-align: top; white-space: nowrap;}
|
|
||||||
table td {background-color: white; border: 1px solid gray; padding: 0 3px; vertical-align: top; line-height: 1.3em;}
|
|
||||||
table.lst td:first-child {font-size: 0.85em; font-family: "Consolas", "Courier New", monospace; white-space: nowrap;}
|
|
||||||
table.lst2 td {font-size: 0.85em; font-family: "Consolas", "Courier New", monospace; white-space: nowrap;}
|
|
||||||
table.lst3 td {font-family: "Consolas", "Courier New", monospace; white-space: nowrap;}
|
|
||||||
tr.lst3 td {border-width: 2px 1px 1px; }
|
|
||||||
table.lst4 td {padding: 0.3em;}
|
|
||||||
table.lst4 td:nth-child(2) {width: 45%;}
|
|
||||||
table.lst4 td:nth-child(3) {width: 45%;}
|
|
||||||
|
|
||||||
p.foot {clear: both; text-indent: 0; margin: 1em 0.5em 1em;}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/*------------------------------------------------------------/
|
|
||||||
/ Open or create a file in append mode
|
|
||||||
/ (This function was sperseded by FA_OPEN_APPEND flag at FatFs R0.12a)
|
|
||||||
/------------------------------------------------------------*/
|
|
||||||
|
|
||||||
FRESULT open_append (
|
|
||||||
FIL* fp, /* [OUT] File object to create */
|
|
||||||
const char* path /* [IN] File name to be opened */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
FRESULT fr;
|
|
||||||
|
|
||||||
/* Opens an existing file. If not exist, creates a new file. */
|
|
||||||
fr = f_open(fp, path, FA_WRITE | FA_OPEN_ALWAYS);
|
|
||||||
if (fr == FR_OK) {
|
|
||||||
/* Seek to end of the file to append data */
|
|
||||||
fr = f_lseek(fp, f_size(fp));
|
|
||||||
if (fr != FR_OK)
|
|
||||||
f_close(fp);
|
|
||||||
}
|
|
||||||
return fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
FRESULT fr;
|
|
||||||
FATFS fs;
|
|
||||||
FIL fil;
|
|
||||||
|
|
||||||
/* Open or create a log file and ready to append */
|
|
||||||
f_mount(&fs, "", 0);
|
|
||||||
fr = open_append(&fil, "logfile.txt");
|
|
||||||
if (fr != FR_OK) return 1;
|
|
||||||
|
|
||||||
/* Append a line */
|
|
||||||
f_printf(&fil, "%02u/%02u/%u, %2u:%02u\n", Mday, Mon, Year, Hour, Min);
|
|
||||||
|
|
||||||
/* Close the file */
|
|
||||||
f_close(&fil);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
/*------------------------------------------------------------/
|
|
||||||
/ Delete a sub-directory even if it contains any file
|
|
||||||
/-------------------------------------------------------------/
|
|
||||||
/ The delete_node() function is for R0.12+.
|
|
||||||
/ It works regardless of FF_FS_RPATH.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
FRESULT delete_node (
|
|
||||||
TCHAR* path, /* Path name buffer with the sub-directory to delete */
|
|
||||||
UINT sz_buff, /* Size of path name buffer (items) */
|
|
||||||
FILINFO* fno /* Name read buffer */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
UINT i, j;
|
|
||||||
FRESULT fr;
|
|
||||||
DIR dir;
|
|
||||||
|
|
||||||
|
|
||||||
fr = f_opendir(&dir, path); /* Open the sub-directory to make it empty */
|
|
||||||
if (fr != FR_OK) return fr;
|
|
||||||
|
|
||||||
for (i = 0; path[i]; i++) ; /* Get current path length */
|
|
||||||
path[i++] = _T('/');
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
fr = f_readdir(&dir, fno); /* Get a directory item */
|
|
||||||
if (fr != FR_OK || !fno->fname[0]) break; /* End of directory? */
|
|
||||||
j = 0;
|
|
||||||
do { /* Make a path name */
|
|
||||||
if (i + j >= sz_buff) { /* Buffer over flow? */
|
|
||||||
fr = 100; break; /* Fails with 100 when buffer overflow */
|
|
||||||
}
|
|
||||||
path[i + j] = fno->fname[j];
|
|
||||||
} while (fno->fname[j++]);
|
|
||||||
if (fno->fattrib & AM_DIR) { /* Item is a sub-directory */
|
|
||||||
fr = delete_node(path, sz_buff, fno);
|
|
||||||
} else { /* Item is a file */
|
|
||||||
fr = f_unlink(path);
|
|
||||||
}
|
|
||||||
if (fr != FR_OK) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
path[--i] = 0; /* Restore the path name */
|
|
||||||
f_closedir(&dir);
|
|
||||||
|
|
||||||
if (fr == FR_OK) fr = f_unlink(path); /* Delete the empty sub-directory */
|
|
||||||
return fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main (void) /* How to use */
|
|
||||||
{
|
|
||||||
FRESULT fr;
|
|
||||||
FATFS fs;
|
|
||||||
TCHAR buff[256];
|
|
||||||
FILINFO fno;
|
|
||||||
|
|
||||||
|
|
||||||
f_mount(&fs, _T("5:"), 0);
|
|
||||||
|
|
||||||
/* Directory to be deleted */
|
|
||||||
_tcscpy(buff, _T("5:dir"));
|
|
||||||
|
|
||||||
/* Delete the directory */
|
|
||||||
fr = delete_node(buff, sizeof buff / sizeof buff[0], &fno);
|
|
||||||
|
|
||||||
/* Check the result */
|
|
||||||
if (fr) {
|
|
||||||
_tprintf(_T("Failed to delete the directory. (%u)\n"), fr);
|
|
||||||
return fr;
|
|
||||||
} else {
|
|
||||||
_tprintf(_T("The directory and the contents have successfully been deleted.\n"), buff);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
/*----------------------------------------------------------------------/
|
|
||||||
/ Allocate a contiguous area to the file
|
|
||||||
/-----------------------------------------------------------------------/
|
|
||||||
/ This function checks if the file is contiguous with desired size.
|
|
||||||
/ If not, a block of contiguous sectors is allocated to the file.
|
|
||||||
/ If the file has been opened without FA_WRITE flag, it only checks if
|
|
||||||
/ the file is contiguous and returns the resulut.
|
|
||||||
/-----------------------------------------------------------------------/
|
|
||||||
/ This function can work with FatFs R0.09 - R0.11a.
|
|
||||||
/ It is incompatible with R0.12+. Use f_expand function instead.
|
|
||||||
/----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* Declarations of FatFs internal functions accessible from applications.
|
|
||||||
/ This is intended to be used for disk checking/fixing or dirty hacks :-) */
|
|
||||||
DWORD clust2sect (FATFS* fs, DWORD clst);
|
|
||||||
DWORD get_fat (FATFS* fs, DWORD clst);
|
|
||||||
FRESULT put_fat (FATFS* fs, DWORD clst, DWORD val);
|
|
||||||
|
|
||||||
|
|
||||||
DWORD allocate_contiguous_clusters ( /* Returns the first sector in LBA (0:error or not contiguous) */
|
|
||||||
FIL* fp, /* Pointer to the open file object */
|
|
||||||
DWORD len /* Number of bytes to allocate */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DWORD csz, tcl, ncl, ccl, cl;
|
|
||||||
|
|
||||||
|
|
||||||
if (f_lseek(fp, 0) || !len) /* Check if the given parameters are valid */
|
|
||||||
return 0;
|
|
||||||
csz = 512UL * fp->fs->csize; /* Cluster size in unit of byte (assuming 512 bytes/sector) */
|
|
||||||
tcl = (len + csz - 1) / csz; /* Total number of clusters required */
|
|
||||||
len = tcl * csz; /* Round-up file size to the cluster boundary */
|
|
||||||
|
|
||||||
/* Check if the existing cluster chain is contiguous */
|
|
||||||
if (len == fp->fsize) {
|
|
||||||
ncl = 0; ccl = fp->sclust;
|
|
||||||
do {
|
|
||||||
cl = get_fat(fp->fs, ccl); /* Get the cluster status */
|
|
||||||
if (cl + 1 < 3) return 0; /* Hard error? */
|
|
||||||
if (cl != ccl + 1 && cl < fp->fs->n_fatent) break; /* Not contiguous? */
|
|
||||||
ccl = cl;
|
|
||||||
} while (++ncl < tcl);
|
|
||||||
if (ncl == tcl) /* Is the file contiguous? */
|
|
||||||
return clust2sect(fp->fs, fp->sclust); /* File is contiguous. Return the start sector */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* File is not contiguous */
|
|
||||||
#if _FS_READONLY
|
|
||||||
return 0; /* Exit if in read-only cfg. */
|
|
||||||
#else
|
|
||||||
if (!(fp->flag & FA_WRITE)) return 0; /* Exit if the file object is for read-only */
|
|
||||||
|
|
||||||
if (f_truncate(fp)) return 0; /* Remove the non-contiguous chain */
|
|
||||||
|
|
||||||
/* Find a free contiguous area */
|
|
||||||
ccl = cl = 2; ncl = 0;
|
|
||||||
do {
|
|
||||||
if (cl >= fp->fs->n_fatent) return 0; /* No contiguous area is found. */
|
|
||||||
if (get_fat(fp->fs, cl)) { /* Encounterd a cluster in use */
|
|
||||||
do { /* Skip the block of used clusters */
|
|
||||||
cl++;
|
|
||||||
if (cl >= fp->fs->n_fatent) return 0; /* No contiguous area is found. */
|
|
||||||
} while (get_fat(fp->fs, cl));
|
|
||||||
ccl = cl; ncl = 0;
|
|
||||||
}
|
|
||||||
cl++; ncl++;
|
|
||||||
} while (ncl < tcl);
|
|
||||||
|
|
||||||
/* Create a contiguous cluster chain */
|
|
||||||
fp->fs->last_clust = ccl - 1;
|
|
||||||
if (f_lseek(fp, len)) return 0;
|
|
||||||
|
|
||||||
return clust2sect(fp->fs, fp->sclust); /* Return file start sector */
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
FRESULT fr;
|
|
||||||
DRESULT dr;
|
|
||||||
FATFS fs;
|
|
||||||
FIL fil;
|
|
||||||
DWORD org;
|
|
||||||
|
|
||||||
|
|
||||||
/* Open or create a file to write */
|
|
||||||
f_mount(&fs, "", 0);
|
|
||||||
fr = f_open(&fil, "fastrec.log", FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
|
|
||||||
if (fr) return 1;
|
|
||||||
|
|
||||||
/* Check if the file is 256MB in size and occupies a contiguous area.
|
|
||||||
/ If not, a contiguous area will be re-allocated to the file. */
|
|
||||||
org = allocate_contiguous_clusters(&fil, 0x10000000);
|
|
||||||
if (!org) {
|
|
||||||
printf("Function failed due to any error or insufficient contiguous area.\n");
|
|
||||||
f_close(&fil);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now you can read/write the file without filesystem layer. */
|
|
||||||
...
|
|
||||||
dr = disk_write(fil.fs->drv, Buff, org, 1024); /* Write 512KiB from top of the file */
|
|
||||||
...
|
|
||||||
|
|
||||||
f_close(&fil);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,315 +0,0 @@
|
|||||||
/*----------------------------------------------------------------------/
|
|
||||||
/ Low level disk I/O module function checker /
|
|
||||||
/-----------------------------------------------------------------------/
|
|
||||||
/ WARNING: The data on the target drive will be lost!
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "ff.h" /* Declarations of sector size */
|
|
||||||
#include "diskio.h" /* Declarations of disk functions */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
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 rc;
|
|
||||||
DWORD buff[FF_MAX_SS]; /* Working buffer (4 sector in size) */
|
|
||||||
|
|
||||||
/* Check function/compatibility of the physical drive #0 */
|
|
||||||
rc = test_diskio(0, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*----------------------------------------------------------------------/
|
|
||||||
/ Test if the file is contiguous /
|
|
||||||
/----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
FRESULT test_contiguous_file (
|
|
||||||
FIL* fp, /* [IN] Open file object to be checked */
|
|
||||||
int* cont /* [OUT] 1:Contiguous, 0:Fragmented or zero-length */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DWORD clst, clsz, step;
|
|
||||||
FSIZE_t fsz;
|
|
||||||
FRESULT fr;
|
|
||||||
|
|
||||||
|
|
||||||
*cont = 0;
|
|
||||||
fr = f_rewind(fp); /* Validates and prepares the file */
|
|
||||||
if (fr != FR_OK) return fr;
|
|
||||||
|
|
||||||
#if FF_MAX_SS == FF_MIN_SS
|
|
||||||
clsz = (DWORD)fp->obj.fs->csize * FF_MAX_SS; /* Cluster size */
|
|
||||||
#else
|
|
||||||
clsz = (DWORD)fp->obj.fs->csize * fp->obj.fs->ssize;
|
|
||||||
#endif
|
|
||||||
fsz = f_size(fp);
|
|
||||||
if (fsz > 0) {
|
|
||||||
clst = fp->obj.sclust - 1; /* A cluster leading the first cluster for first test */
|
|
||||||
while (fsz) {
|
|
||||||
step = (fsz >= clsz) ? clsz : (DWORD)fsz;
|
|
||||||
fr = f_lseek(fp, f_tell(fp) + step); /* Advances file pointer a cluster */
|
|
||||||
if (fr != FR_OK) return fr;
|
|
||||||
if (clst + 1 != fp->clust) break; /* Is not the cluster next to previous one? */
|
|
||||||
clst = fp->clust; fsz -= step; /* Get current cluster for next test */
|
|
||||||
}
|
|
||||||
if (fsz == 0) *cont = 1; /* All done without fail? */
|
|
||||||
}
|
|
||||||
|
|
||||||
return FR_OK;
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------*/
|
|
||||||
/* Raw Read/Write Throughput Checker */
|
|
||||||
/*---------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <systimer.h>
|
|
||||||
#include "diskio.h"
|
|
||||||
#include "ff.h"
|
|
||||||
|
|
||||||
|
|
||||||
int test_raw_speed (
|
|
||||||
BYTE pdrv, /* Physical drive number */
|
|
||||||
DWORD lba, /* Start LBA for read/write test */
|
|
||||||
DWORD len, /* Number of bytes to read/write (must be multiple of sz_buff) */
|
|
||||||
void* buff, /* Read/write buffer */
|
|
||||||
UINT sz_buff /* Size of read/write buffer (must be multiple of FF_MAX_SS) */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
WORD ss;
|
|
||||||
DWORD ofs, tmr;
|
|
||||||
|
|
||||||
|
|
||||||
#if FF_MIN_SS != FF_MAX_SS
|
|
||||||
if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) {
|
|
||||||
printf("\ndisk_ioctl() failed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
ss = FF_MAX_SS;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
printf("Starting raw write test at sector %lu in %u bytes of data chunks...", lba, sz_buff);
|
|
||||||
tmr = systimer();
|
|
||||||
for (ofs = 0; ofs < len / ss; ofs += sz_buff / ss) {
|
|
||||||
if (disk_write(pdrv, buff, lba + ofs, sz_buff / ss) != RES_OK) {
|
|
||||||
printf("\ndisk_write() failed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) {
|
|
||||||
printf("\ndisk_ioctl() failed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
tmr = systimer() - tmr;
|
|
||||||
printf("\n%lu bytes written and it took %lu timer ticks.\n", len, tmr);
|
|
||||||
|
|
||||||
printf("Starting raw read test at sector %lu in %u bytes of data chunks...", lba, sz_buff);
|
|
||||||
tmr = systimer();
|
|
||||||
for (ofs = 0; ofs < len / ss; ofs += sz_buff / ss) {
|
|
||||||
if (disk_read(pdrv, buff, lba + ofs, sz_buff / ss) != RES_OK) {
|
|
||||||
printf("\ndisk_read() failed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmr = systimer() - tmr;
|
|
||||||
printf("\n%lu bytes read and it took %lu timer ticks.\n", len, tmr);
|
|
||||||
|
|
||||||
printf("Test completed.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,369 +0,0 @@
|
|||||||
----------------------------------------------------------------------------
|
|
||||||
Revision history of FatFs module
|
|
||||||
----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
R0.00 (February 26, 2006)
|
|
||||||
|
|
||||||
Prototype.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.01 (April 29, 2006)
|
|
||||||
|
|
||||||
The first release.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.02 (June 01, 2006)
|
|
||||||
|
|
||||||
Added FAT12 support.
|
|
||||||
Removed unbuffered mode.
|
|
||||||
Fixed a problem on small (<32M) partition.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.02a (June 10, 2006)
|
|
||||||
|
|
||||||
Added a configuration option (_FS_MINIMUM).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.03 (September 22, 2006)
|
|
||||||
|
|
||||||
Added f_rename().
|
|
||||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.03a (December 11, 2006)
|
|
||||||
|
|
||||||
Improved cluster scan algorithm to write files fast.
|
|
||||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.04 (February 04, 2007)
|
|
||||||
|
|
||||||
Added f_mkfs().
|
|
||||||
Supported multiple drive system.
|
|
||||||
Changed some interfaces for multiple drive system.
|
|
||||||
Changed f_mountdrv() to f_mount().
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.04a (April 01, 2007)
|
|
||||||
|
|
||||||
Supported multiple partitions on a physical drive.
|
|
||||||
Added a capability of extending file size to f_lseek().
|
|
||||||
Added minimization level 3.
|
|
||||||
Fixed an endian sensitive code in f_mkfs().
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.04b (May 05, 2007)
|
|
||||||
|
|
||||||
Added a configuration option _USE_NTFLAG.
|
|
||||||
Added FSINFO support.
|
|
||||||
Fixed DBCS name can result FR_INVALID_NAME.
|
|
||||||
Fixed short seek (<= csize) collapses the file object.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.05 (August 25, 2007)
|
|
||||||
|
|
||||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
|
||||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
|
||||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.05a (February 03, 2008)
|
|
||||||
|
|
||||||
Added f_truncate() and f_utime().
|
|
||||||
Fixed off by one error at FAT sub-type determination.
|
|
||||||
Fixed btr in f_read() can be mistruncated.
|
|
||||||
Fixed cached sector is not flushed when create and close without write.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.06 (April 01, 2008)
|
|
||||||
|
|
||||||
Added fputc(), fputs(), fprintf() and fgets().
|
|
||||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.07 (April 01, 2009)
|
|
||||||
|
|
||||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
|
||||||
Added long file name feature. (_USE_LFN)
|
|
||||||
Added multiple code page feature. (_CODE_PAGE)
|
|
||||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
|
||||||
Added auto cluster size selection to f_mkfs().
|
|
||||||
Added rewind option to f_readdir().
|
|
||||||
Changed result code of critical errors.
|
|
||||||
Renamed string functions to avoid name collision.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.07a (April 14, 2009)
|
|
||||||
|
|
||||||
Septemberarated out OS dependent code on reentrant cfg.
|
|
||||||
Added multiple sector size feature.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.07c (June 21, 2009)
|
|
||||||
|
|
||||||
Fixed f_unlink() can return FR_OK on error.
|
|
||||||
Fixed wrong cache control in f_lseek().
|
|
||||||
Added relative path feature.
|
|
||||||
Added f_chdir() and f_chdrive().
|
|
||||||
Added proper case conversion to extended character.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.07e (November 03, 2009)
|
|
||||||
|
|
||||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
|
||||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
|
||||||
Fixed name matching error on the 13 character boundary.
|
|
||||||
Added a configuration option, _LFN_UNICODE.
|
|
||||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.08 (May 15, 2010)
|
|
||||||
|
|
||||||
Added a memory configuration option. (_USE_LFN = 3)
|
|
||||||
Added file lock feature. (_FS_SHARE)
|
|
||||||
Added fast seek feature. (_USE_FASTSEEK)
|
|
||||||
Changed some types on the API, XCHAR->TCHAR.
|
|
||||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
|
||||||
String functions support UTF-8 encoding files on Unicode cfg.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.08a (August 16, 2010)
|
|
||||||
|
|
||||||
Added f_getcwd(). (_FS_RPATH = 2)
|
|
||||||
Added sector erase feature. (_USE_ERASE)
|
|
||||||
Moved file lock semaphore table from fs object to the bss.
|
|
||||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.08b (January 15, 2011)
|
|
||||||
|
|
||||||
Fast seek feature is also applied to f_read() and f_write().
|
|
||||||
f_lseek() reports required table size on creating CLMP.
|
|
||||||
Extended format syntax of f_printf().
|
|
||||||
Ignores duplicated directory separators in given path name.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.09 (September 06, 2011)
|
|
||||||
|
|
||||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
|
||||||
Added f_fdisk().
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.09a (August 27, 2012)
|
|
||||||
|
|
||||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
|
||||||
Changed option name _FS_SHARE to _FS_LOCK.
|
|
||||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.09b (January 24, 2013)
|
|
||||||
|
|
||||||
Added f_setlabel() and f_getlabel().
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.10 (October 02, 2013)
|
|
||||||
|
|
||||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
|
||||||
Added f_closedir().
|
|
||||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
|
||||||
Added forced mount feature with changes of f_mount().
|
|
||||||
Improved behavior of volume auto detection.
|
|
||||||
Improved write throughput of f_puts() and f_printf().
|
|
||||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
|
||||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
|
||||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.10a (January 15, 2014)
|
|
||||||
|
|
||||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
|
||||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
|
||||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
|
||||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
|
||||||
Fixed f_close() invalidates the file object without volume lock.
|
|
||||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
|
||||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.10b (May 19, 2014)
|
|
||||||
|
|
||||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
|
||||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.10c (November 09, 2014)
|
|
||||||
|
|
||||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
|
||||||
Changed option name _USE_ERASE to _USE_TRIM.
|
|
||||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
|
||||||
Fixed a potential problem of FAT access that can appear on disk error.
|
|
||||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.11 (February 09, 2015)
|
|
||||||
|
|
||||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
|
||||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
|
||||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.11a (September 05, 2015)
|
|
||||||
|
|
||||||
Fixed wrong media change can lead a deadlock at thread-safe configuration.
|
|
||||||
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
|
|
||||||
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
|
|
||||||
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
|
|
||||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.12 (April 12, 2016)
|
|
||||||
|
|
||||||
Added support for exFAT file system. (_FS_EXFAT)
|
|
||||||
Added f_expand(). (_USE_EXPAND)
|
|
||||||
Changed some members in FINFO structure and behavior of f_readdir().
|
|
||||||
Added an option _USE_CHMOD.
|
|
||||||
Removed an option _WORD_ACCESS.
|
|
||||||
Fixed errors in the case conversion table of Unicode (cc*.c).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.12a (July 10, 2016)
|
|
||||||
|
|
||||||
Added support for creating exFAT volume with some changes of f_mkfs().
|
|
||||||
Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
|
|
||||||
f_forward() is available regardless of _FS_TINY.
|
|
||||||
Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
|
|
||||||
Fixed wrong memory read in create_name(). (appeared at R0.12)
|
|
||||||
Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.12b (September 04, 2016)
|
|
||||||
|
|
||||||
Made f_rename() be able to rename objects with the same name but case.
|
|
||||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
|
||||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
|
||||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
|
||||||
Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
|
|
||||||
Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
|
|
||||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
|
||||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.12c (March 04, 2017)
|
|
||||||
|
|
||||||
Improved write throughput at the fragmented file on the exFAT volume.
|
|
||||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
|
||||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
|
||||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.13 (May 21, 2017)
|
|
||||||
|
|
||||||
Changed heading character of configuration keywords "_" to "FF_".
|
|
||||||
Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead.
|
|
||||||
Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0)
|
|
||||||
Improved cluster allocation time on stretch a deep buried cluster chain.
|
|
||||||
Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3.
|
|
||||||
Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous.
|
|
||||||
Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12)
|
|
||||||
Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c)
|
|
||||||
Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.13a (October 14, 2017)
|
|
||||||
|
|
||||||
Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2)
|
|
||||||
Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF).
|
|
||||||
Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk().
|
|
||||||
Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09)
|
|
||||||
Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c)
|
|
||||||
Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.13b (April 07, 2018)
|
|
||||||
|
|
||||||
Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3)
|
|
||||||
Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2)
|
|
||||||
Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c)
|
|
||||||
Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.13c (October 14, 2018)
|
|
||||||
Supported stdint.h for C99 and later. (integer.h was included in ff.h)
|
|
||||||
Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12)
|
|
||||||
Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12)
|
|
||||||
Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.14 (October 14, 2019)
|
|
||||||
Added support for 64-bit LBA and GUID partition table (FF_LBA64 = 1)
|
|
||||||
Changed some API functions, f_mkfs() and f_fdisk().
|
|
||||||
Fixed f_open() function cannot find the file with file name in length of FF_MAX_LFN characters.
|
|
||||||
Fixed f_readdir() function cannot retrieve long file names in length of FF_MAX_LFN - 1 characters.
|
|
||||||
Fixed f_readdir() function returns file names with wrong case conversion. (appeared at R0.12)
|
|
||||||
Fixed f_mkfs() function can fail to create exFAT volume in the second partition. (appeared at R0.12)
|
|
||||||
|
|
||||||
|
|
||||||
R0.14a (December 5, 2020)
|
|
||||||
Limited number of recursive calls in f_findnext().
|
|
||||||
Fixed old floppy disks formatted with MS-DOS 2.x and 3.x cannot be mounted.
|
|
||||||
Fixed some compiler warnings.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.14b (April 17, 2021)
|
|
||||||
Made FatFs uses standard library <string.h> for copy, compare and search instead of built-in string functions.
|
|
||||||
Added support for long long integer and floating point to f_printf(). (FF_STRF_LLI and FF_STRF_FP)
|
|
||||||
Made path name parser ignore the terminating separator to allow "dir/".
|
|
||||||
Improved the compatibility in Unix style path name feature.
|
|
||||||
Fixed the file gets dead-locked when f_open() failed with some conditions. (appeared at R0.12a)
|
|
||||||
Fixed f_mkfs() can create wrong exFAT volume due to a timing dependent error. (appeared at R0.12)
|
|
||||||
Fixed code page 855 cannot be set by f_setcp().
|
|
||||||
Fixed some compiler warnings.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
R0.15 (November 6, 2022)
|
|
||||||
Changed user provided synchronization functions in order to completely eliminate the platform dependency from FatFs code.
|
|
||||||
FF_SYNC_t is removed from the configuration options.
|
|
||||||
Fixed a potential error in f_mount when FF_FS_REENTRANT.
|
|
||||||
Fixed file lock control FF_FS_LOCK is not mutal excluded when FF_FS_REENTRANT && FF_VOLUMES > 1 is true.
|
|
||||||
Fixed f_mkfs() creates broken exFAT volume when the size of volume is >= 2^32 sectors.
|
|
||||||
Fixed string functions cannot write the unicode characters not in BMP when FF_LFN_UNICODE == 2 (UTF-8).
|
|
||||||
Fixed a compatibility issue in identification of GPT header.
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
FatFs Module Source Files R0.15
|
|
||||||
|
|
||||||
|
|
||||||
FILES
|
|
||||||
|
|
||||||
00readme.txt This file.
|
|
||||||
00history.txt Revision history.
|
|
||||||
ff.c FatFs module.
|
|
||||||
ffconf.h Configuration file of FatFs module.
|
|
||||||
ff.h Common include file for FatFs and application module.
|
|
||||||
diskio.h Common include file for FatFs and disk I/O module.
|
|
||||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
|
||||||
ffunicode.c Optional Unicode utility functions.
|
|
||||||
ffsystem.c An example of optional O/S related functions.
|
|
||||||
|
|
||||||
|
|
||||||
Low level disk I/O module is not included in this archive because the FatFs
|
|
||||||
module is only a generic file system layer and it does not depend on any specific
|
|
||||||
storage device. You need to provide a low level disk I/O module written to
|
|
||||||
control the storage device that attached to the target system.
|
|
||||||
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* If a working storage control module is available, it should be */
|
|
||||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
|
||||||
/* This is an example of glue functions to attach various exsisting */
|
|
||||||
/* storage control modules to the FatFs module with a defined API. */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "ff.h" /* Obtains integer types */
|
|
||||||
#include "diskio.h" /* Declarations of disk functions */
|
|
||||||
|
|
||||||
/* Definitions of physical drive number for each drive */
|
|
||||||
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
|
|
||||||
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
|
|
||||||
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Get Drive Status */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DSTATUS disk_status (
|
|
||||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DSTATUS stat;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
switch (pdrv) {
|
|
||||||
case DEV_RAM :
|
|
||||||
result = RAM_disk_status();
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return stat;
|
|
||||||
|
|
||||||
case DEV_MMC :
|
|
||||||
result = MMC_disk_status();
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return stat;
|
|
||||||
|
|
||||||
case DEV_USB :
|
|
||||||
result = USB_disk_status();
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return stat;
|
|
||||||
}
|
|
||||||
return STA_NOINIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Inidialize a Drive */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DSTATUS disk_initialize (
|
|
||||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DSTATUS stat;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
switch (pdrv) {
|
|
||||||
case DEV_RAM :
|
|
||||||
result = RAM_disk_initialize();
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return stat;
|
|
||||||
|
|
||||||
case DEV_MMC :
|
|
||||||
result = MMC_disk_initialize();
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return stat;
|
|
||||||
|
|
||||||
case DEV_USB :
|
|
||||||
result = USB_disk_initialize();
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return stat;
|
|
||||||
}
|
|
||||||
return STA_NOINIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Read Sector(s) */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DRESULT disk_read (
|
|
||||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
|
||||||
BYTE *buff, /* Data buffer to store read data */
|
|
||||||
LBA_t sector, /* Start sector in LBA */
|
|
||||||
UINT count /* Number of sectors to read */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DRESULT res;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
switch (pdrv) {
|
|
||||||
case DEV_RAM :
|
|
||||||
// translate the arguments here
|
|
||||||
|
|
||||||
result = RAM_disk_read(buff, sector, count);
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case DEV_MMC :
|
|
||||||
// translate the arguments here
|
|
||||||
|
|
||||||
result = MMC_disk_read(buff, sector, count);
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case DEV_USB :
|
|
||||||
// translate the arguments here
|
|
||||||
|
|
||||||
result = USB_disk_read(buff, sector, count);
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RES_PARERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Write Sector(s) */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#if FF_FS_READONLY == 0
|
|
||||||
|
|
||||||
DRESULT disk_write (
|
|
||||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
|
||||||
const BYTE *buff, /* Data to be written */
|
|
||||||
LBA_t sector, /* Start sector in LBA */
|
|
||||||
UINT count /* Number of sectors to write */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DRESULT res;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
switch (pdrv) {
|
|
||||||
case DEV_RAM :
|
|
||||||
// translate the arguments here
|
|
||||||
|
|
||||||
result = RAM_disk_write(buff, sector, count);
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case DEV_MMC :
|
|
||||||
// translate the arguments here
|
|
||||||
|
|
||||||
result = MMC_disk_write(buff, sector, count);
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case DEV_USB :
|
|
||||||
// translate the arguments here
|
|
||||||
|
|
||||||
result = USB_disk_write(buff, sector, count);
|
|
||||||
|
|
||||||
// translate the reslut code here
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RES_PARERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Miscellaneous Functions */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DRESULT disk_ioctl (
|
|
||||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
|
||||||
BYTE cmd, /* Control code */
|
|
||||||
void *buff /* Buffer to send/receive control data */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DRESULT res;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
switch (pdrv) {
|
|
||||||
case DEV_RAM :
|
|
||||||
|
|
||||||
// Process of the command for the RAM drive
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case DEV_MMC :
|
|
||||||
|
|
||||||
// Process of the command for the MMC/SD card
|
|
||||||
|
|
||||||
return res;
|
|
||||||
|
|
||||||
case DEV_USB :
|
|
||||||
|
|
||||||
// Process of the command the USB drive
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RES_PARERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/*-----------------------------------------------------------------------/
|
|
||||||
/ Low level disk interface modlue include file (C)ChaN, 2019 /
|
|
||||||
/-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#ifndef _DISKIO_DEFINED
|
|
||||||
#define _DISKIO_DEFINED
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Status of Disk Functions */
|
|
||||||
typedef BYTE DSTATUS;
|
|
||||||
|
|
||||||
/* Results of Disk Functions */
|
|
||||||
typedef enum {
|
|
||||||
RES_OK = 0, /* 0: Successful */
|
|
||||||
RES_ERROR, /* 1: R/W Error */
|
|
||||||
RES_WRPRT, /* 2: Write Protected */
|
|
||||||
RES_NOTRDY, /* 3: Not Ready */
|
|
||||||
RES_PARERR /* 4: Invalid Parameter */
|
|
||||||
} DRESULT;
|
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------*/
|
|
||||||
/* Prototypes for disk control functions */
|
|
||||||
|
|
||||||
|
|
||||||
DSTATUS disk_initialize (BYTE pdrv);
|
|
||||||
DSTATUS disk_status (BYTE pdrv);
|
|
||||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
|
||||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
|
||||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
|
||||||
|
|
||||||
|
|
||||||
/* Disk Status Bits (DSTATUS) */
|
|
||||||
|
|
||||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
|
||||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
|
||||||
#define STA_PROTECT 0x04 /* Write protected */
|
|
||||||
|
|
||||||
|
|
||||||
/* Command code for disk_ioctrl fucntion */
|
|
||||||
|
|
||||||
/* Generic command (Used by FatFs) */
|
|
||||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
|
||||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
|
||||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
|
||||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
|
||||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
|
||||||
|
|
||||||
/* Generic command (Not used by FatFs) */
|
|
||||||
#define CTRL_POWER 5 /* Get/Set power status */
|
|
||||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
|
||||||
#define CTRL_EJECT 7 /* Eject media */
|
|
||||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
|
||||||
|
|
||||||
/* MMC/SDC specific ioctl command */
|
|
||||||
#define MMC_GET_TYPE 10 /* Get card type */
|
|
||||||
#define MMC_GET_CSD 11 /* Get CSD */
|
|
||||||
#define MMC_GET_CID 12 /* Get CID */
|
|
||||||
#define MMC_GET_OCR 13 /* Get OCR */
|
|
||||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
|
||||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
|
||||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
|
||||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
|
||||||
|
|
||||||
/* ATA/CF specific ioctl command */
|
|
||||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
|
||||||
#define ATA_GET_MODEL 21 /* Get model name */
|
|
||||||
#define ATA_GET_SN 22 /* Get serial number */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,429 +0,0 @@
|
|||||||
/*----------------------------------------------------------------------------/
|
|
||||||
/ FatFs - Generic FAT Filesystem module R0.15 /
|
|
||||||
/-----------------------------------------------------------------------------/
|
|
||||||
/
|
|
||||||
/ Copyright (C) 2022, 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.
|
|
||||||
/
|
|
||||||
/----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FF_DEFINED
|
|
||||||
#define FF_DEFINED 80286 /* Revision ID */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ffconf.h" /* FatFs configuration options */
|
|
||||||
|
|
||||||
#if FF_DEFINED != FFCONF_DEF
|
|
||||||
#error Wrong configuration file (ffconf.h).
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Integer types used for FatFs API */
|
|
||||||
|
|
||||||
#if defined(_WIN32) /* Windows VC++ (for development only) */
|
|
||||||
#define FF_INTDEF 2
|
|
||||||
#include <windows.h>
|
|
||||||
typedef unsigned __int64 QWORD;
|
|
||||||
#include <float.h>
|
|
||||||
#define isnan(v) _isnan(v)
|
|
||||||
#define isinf(v) (!_finite(v))
|
|
||||||
|
|
||||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
|
||||||
#define FF_INTDEF 2
|
|
||||||
#include <stdint.h>
|
|
||||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
|
||||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
|
||||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
|
||||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
|
||||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
|
||||||
typedef WORD WCHAR; /* UTF-16 character type */
|
|
||||||
|
|
||||||
#else /* Earlier than C99 */
|
|
||||||
#define FF_INTDEF 1
|
|
||||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
|
||||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
|
||||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
|
||||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
|
||||||
typedef WORD WCHAR; /* UTF-16 character type */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Type of file size and LBA variables */
|
|
||||||
|
|
||||||
#if FF_FS_EXFAT
|
|
||||||
#if FF_INTDEF != 2
|
|
||||||
#error exFAT feature wants C99 or later
|
|
||||||
#endif
|
|
||||||
typedef QWORD FSIZE_t;
|
|
||||||
#if FF_LBA64
|
|
||||||
typedef QWORD LBA_t;
|
|
||||||
#else
|
|
||||||
typedef DWORD LBA_t;
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#if FF_LBA64
|
|
||||||
#error exFAT needs to be enabled when enable 64-bit LBA
|
|
||||||
#endif
|
|
||||||
typedef DWORD FSIZE_t;
|
|
||||||
typedef DWORD LBA_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Type of path name strings on FatFs API (TCHAR) */
|
|
||||||
|
|
||||||
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
|
||||||
typedef WCHAR TCHAR;
|
|
||||||
#define _T(x) L ## x
|
|
||||||
#define _TEXT(x) L ## x
|
|
||||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
|
||||||
typedef char TCHAR;
|
|
||||||
#define _T(x) u8 ## x
|
|
||||||
#define _TEXT(x) u8 ## x
|
|
||||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
|
||||||
typedef DWORD TCHAR;
|
|
||||||
#define _T(x) U ## x
|
|
||||||
#define _TEXT(x) U ## x
|
|
||||||
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
|
||||||
#error Wrong FF_LFN_UNICODE setting
|
|
||||||
#else /* ANSI/OEM code in SBCS/DBCS */
|
|
||||||
typedef char TCHAR;
|
|
||||||
#define _T(x) x
|
|
||||||
#define _TEXT(x) x
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Definitions of volume management */
|
|
||||||
|
|
||||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
|
||||||
typedef struct {
|
|
||||||
BYTE pd; /* Physical drive number */
|
|
||||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
|
||||||
} PARTITION;
|
|
||||||
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FF_STR_VOLUME_ID
|
|
||||||
#ifndef FF_VOLUME_STRS
|
|
||||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Filesystem object structure (FATFS) */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
|
||||||
BYTE pdrv; /* Volume hosting physical drive */
|
|
||||||
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
|
|
||||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
|
||||||
BYTE wflag; /* win[] status (b0:dirty) */
|
|
||||||
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
|
|
||||||
WORD id; /* Volume mount ID */
|
|
||||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
|
||||||
WORD csize; /* Cluster size [sectors] */
|
|
||||||
#if FF_MAX_SS != FF_MIN_SS
|
|
||||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
|
||||||
#endif
|
|
||||||
#if FF_USE_LFN
|
|
||||||
WCHAR* lfnbuf; /* LFN working buffer */
|
|
||||||
#endif
|
|
||||||
#if FF_FS_EXFAT
|
|
||||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
|
||||||
#endif
|
|
||||||
#if !FF_FS_READONLY
|
|
||||||
DWORD last_clst; /* Last allocated cluster */
|
|
||||||
DWORD free_clst; /* Number of free clusters */
|
|
||||||
#endif
|
|
||||||
#if FF_FS_RPATH
|
|
||||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
|
||||||
#if FF_FS_EXFAT
|
|
||||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
|
||||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
|
||||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
|
||||||
DWORD fsize; /* Number of sectors per FAT */
|
|
||||||
LBA_t volbase; /* Volume base sector */
|
|
||||||
LBA_t fatbase; /* FAT base sector */
|
|
||||||
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
|
|
||||||
LBA_t database; /* Data base sector */
|
|
||||||
#if FF_FS_EXFAT
|
|
||||||
LBA_t bitbase; /* Allocation bitmap base sector */
|
|
||||||
#endif
|
|
||||||
LBA_t winsect; /* Current sector appearing in the win[] */
|
|
||||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
|
||||||
} FATFS;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Object ID and allocation information (FFOBJID) */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
|
||||||
WORD id; /* Hosting volume's mount ID */
|
|
||||||
BYTE attr; /* Object attribute */
|
|
||||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
|
||||||
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
|
||||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
|
||||||
#if FF_FS_EXFAT
|
|
||||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
|
||||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
|
||||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
|
||||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
|
||||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
|
||||||
#endif
|
|
||||||
#if FF_FS_LOCK
|
|
||||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
|
||||||
#endif
|
|
||||||
} FFOBJID;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* File object structure (FIL) */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
|
||||||
BYTE flag; /* File status flags */
|
|
||||||
BYTE err; /* Abort flag (error code) */
|
|
||||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
|
||||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
|
||||||
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
|
|
||||||
#if !FF_FS_READONLY
|
|
||||||
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
|
||||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
|
||||||
#endif
|
|
||||||
#if FF_USE_FASTSEEK
|
|
||||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
|
||||||
#endif
|
|
||||||
#if !FF_FS_TINY
|
|
||||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
|
||||||
#endif
|
|
||||||
} FIL;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Directory object structure (DIR) */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FFOBJID obj; /* Object identifier */
|
|
||||||
DWORD dptr; /* Current read/write offset */
|
|
||||||
DWORD clust; /* Current cluster */
|
|
||||||
LBA_t sect; /* Current sector (0:Read operation has terminated) */
|
|
||||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
|
||||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
|
||||||
#if FF_USE_LFN
|
|
||||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
|
||||||
#endif
|
|
||||||
#if FF_USE_FIND
|
|
||||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
|
||||||
#endif
|
|
||||||
} DIR;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* File information structure (FILINFO) */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FSIZE_t fsize; /* File size */
|
|
||||||
WORD fdate; /* Modified date */
|
|
||||||
WORD ftime; /* Modified time */
|
|
||||||
BYTE fattrib; /* File attribute */
|
|
||||||
#if FF_USE_LFN
|
|
||||||
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
|
|
||||||
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
|
||||||
#else
|
|
||||||
TCHAR fname[12 + 1]; /* File name */
|
|
||||||
#endif
|
|
||||||
} FILINFO;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Format parameter structure (MKFS_PARM) */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
|
||||||
BYTE n_fat; /* Number of FATs */
|
|
||||||
UINT align; /* Data area alignment (sector) */
|
|
||||||
UINT n_root; /* Number of root directory entries */
|
|
||||||
DWORD au_size; /* Cluster size (byte) */
|
|
||||||
} MKFS_PARM;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* File function return code (FRESULT) */
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
FR_OK = 0, /* (0) Succeeded */
|
|
||||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
|
||||||
FR_INT_ERR, /* (2) Assertion failed */
|
|
||||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
|
||||||
FR_NO_FILE, /* (4) Could not find the file */
|
|
||||||
FR_NO_PATH, /* (5) Could not find the path */
|
|
||||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
|
||||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
|
||||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
|
||||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
|
||||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
|
||||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
|
||||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
|
||||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
|
||||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
|
||||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
|
||||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
|
||||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
|
||||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
|
||||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
|
||||||
} FRESULT;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------*/
|
|
||||||
/* FatFs Module Application Interface */
|
|
||||||
/*--------------------------------------------------------------*/
|
|
||||||
|
|
||||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
|
||||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
|
||||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
|
||||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
|
||||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
|
||||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
|
||||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
|
||||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
|
||||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
|
||||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
|
||||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
|
||||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
|
||||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
|
||||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
|
||||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
|
||||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
|
||||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
|
||||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
|
||||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
|
||||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
|
||||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
|
||||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
|
||||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
|
||||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
|
||||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
|
||||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
|
||||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
|
||||||
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
|
||||||
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
|
|
||||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
|
||||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
|
||||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
|
||||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
|
||||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
|
||||||
|
|
||||||
/* Some API fucntions are implemented as macro */
|
|
||||||
|
|
||||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
|
||||||
#define f_error(fp) ((fp)->err)
|
|
||||||
#define f_tell(fp) ((fp)->fptr)
|
|
||||||
#define f_size(fp) ((fp)->obj.objsize)
|
|
||||||
#define f_rewind(fp) f_lseek((fp), 0)
|
|
||||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
|
||||||
#define f_rmdir(path) f_unlink(path)
|
|
||||||
#define f_unmount(path) f_mount(0, path, 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------*/
|
|
||||||
/* Additional Functions */
|
|
||||||
/*--------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* RTC function (provided by user) */
|
|
||||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
|
||||||
DWORD get_fattime (void); /* Get current time */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* LFN support functions (defined in ffunicode.c) */
|
|
||||||
|
|
||||||
#if FF_USE_LFN >= 1
|
|
||||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
|
||||||
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
|
||||||
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* O/S dependent functions (samples available in ffsystem.c) */
|
|
||||||
|
|
||||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
|
||||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
|
||||||
void ff_memfree (void* mblock); /* Free memory block */
|
|
||||||
#endif
|
|
||||||
#if FF_FS_REENTRANT /* Sync functions */
|
|
||||||
int ff_mutex_create (int vol); /* Create a sync object */
|
|
||||||
void ff_mutex_delete (int vol); /* Delete a sync object */
|
|
||||||
int ff_mutex_take (int vol); /* Lock sync object */
|
|
||||||
void ff_mutex_give (int vol); /* Unlock sync object */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------*/
|
|
||||||
/* Flags and Offset Address */
|
|
||||||
/*--------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/* File access mode and open method flags (3rd argument of f_open) */
|
|
||||||
#define FA_READ 0x01
|
|
||||||
#define FA_WRITE 0x02
|
|
||||||
#define FA_OPEN_EXISTING 0x00
|
|
||||||
#define FA_CREATE_NEW 0x04
|
|
||||||
#define FA_CREATE_ALWAYS 0x08
|
|
||||||
#define FA_OPEN_ALWAYS 0x10
|
|
||||||
#define FA_OPEN_APPEND 0x30
|
|
||||||
|
|
||||||
/* Fast seek controls (2nd argument of f_lseek) */
|
|
||||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
|
||||||
|
|
||||||
/* Format options (2nd argument of f_mkfs) */
|
|
||||||
#define FM_FAT 0x01
|
|
||||||
#define FM_FAT32 0x02
|
|
||||||
#define FM_EXFAT 0x04
|
|
||||||
#define FM_ANY 0x07
|
|
||||||
#define FM_SFD 0x08
|
|
||||||
|
|
||||||
/* Filesystem type (FATFS.fs_type) */
|
|
||||||
#define FS_FAT12 1
|
|
||||||
#define FS_FAT16 2
|
|
||||||
#define FS_FAT32 3
|
|
||||||
#define FS_EXFAT 4
|
|
||||||
|
|
||||||
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
|
||||||
#define AM_RDO 0x01 /* Read only */
|
|
||||||
#define AM_HID 0x02 /* Hidden */
|
|
||||||
#define AM_SYS 0x04 /* System */
|
|
||||||
#define AM_DIR 0x10 /* Directory */
|
|
||||||
#define AM_ARC 0x20 /* Archive */
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* FF_DEFINED */
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------/
|
|
||||||
/ 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 0
|
|
||||||
/* 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 2
|
|
||||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
|
||||||
|
|
||||||
|
|
||||||
#define FF_STR_VOLUME_ID 0
|
|
||||||
#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 ---*/
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* A Sample Code of User Provided OS Dependent Functions for FatFs */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "ff.h"
|
|
||||||
|
|
||||||
|
|
||||||
#if FF_USE_LFN == 3 /* Use dynamic memory allocation */
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* Allocate/Free a Memory Block */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include <stdlib.h> /* with POSIX API */
|
|
||||||
|
|
||||||
|
|
||||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
|
||||||
UINT msize /* Number of bytes to allocate */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return malloc((size_t)msize); /* Allocate a new memory block */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ff_memfree (
|
|
||||||
void* mblock /* Pointer to the memory block to free (no effect if null) */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
free(mblock); /* Free the memory block */
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* Definitions of Mutex */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */
|
|
||||||
|
|
||||||
|
|
||||||
#if OS_TYPE == 0 /* Win32 */
|
|
||||||
#include <windows.h>
|
|
||||||
static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
|
|
||||||
|
|
||||||
#elif OS_TYPE == 1 /* uITRON */
|
|
||||||
#include "itron.h"
|
|
||||||
#include "kernel.h"
|
|
||||||
static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
|
|
||||||
|
|
||||||
#elif OS_TYPE == 2 /* uc/OS-II */
|
|
||||||
#include "includes.h"
|
|
||||||
static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */
|
|
||||||
|
|
||||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
|
||||||
#include "FreeRTOS.h"
|
|
||||||
#include "semphr.h"
|
|
||||||
static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
|
|
||||||
|
|
||||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
|
||||||
#include "cmsis_os.h"
|
|
||||||
static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* Create a Mutex */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* This function is called in f_mount function to create a new mutex
|
|
||||||
/ or semaphore for the volume. When a 0 is returned, the f_mount function
|
|
||||||
/ fails with FR_INT_ERR.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */
|
|
||||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
#if OS_TYPE == 0 /* Win32 */
|
|
||||||
Mutex[vol] = CreateMutex(NULL, FALSE, NULL);
|
|
||||||
return (int)(Mutex[vol] != INVALID_HANDLE_VALUE);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 1 /* uITRON */
|
|
||||||
T_CMTX cmtx = {TA_TPRI,1};
|
|
||||||
|
|
||||||
Mutex[vol] = acre_mtx(&cmtx);
|
|
||||||
return (int)(Mutex[vol] > 0);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
|
||||||
OS_ERR err;
|
|
||||||
|
|
||||||
Mutex[vol] = OSMutexCreate(0, &err);
|
|
||||||
return (int)(err == OS_NO_ERR);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
|
||||||
Mutex[vol] = xSemaphoreCreateMutex();
|
|
||||||
return (int)(Mutex[vol] != NULL);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
|
||||||
osMutexDef(cmsis_os_mutex);
|
|
||||||
|
|
||||||
Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex));
|
|
||||||
return (int)(Mutex[vol] != NULL);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* Delete a Mutex */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* This function is called in f_mount function to delete a mutex or
|
|
||||||
/ semaphore of the volume created with ff_mutex_create function.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */
|
|
||||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
#if OS_TYPE == 0 /* Win32 */
|
|
||||||
CloseHandle(Mutex[vol]);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 1 /* uITRON */
|
|
||||||
del_mtx(Mutex[vol]);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
|
||||||
OS_ERR err;
|
|
||||||
|
|
||||||
OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
|
||||||
vSemaphoreDelete(Mutex[vol]);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
|
||||||
osMutexDelete(Mutex[vol]);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* Request a Grant to Access the Volume */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* This function is called on enter file functions to lock the volume.
|
|
||||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */
|
|
||||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
#if OS_TYPE == 0 /* Win32 */
|
|
||||||
return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 1 /* uITRON */
|
|
||||||
return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
|
||||||
OS_ERR err;
|
|
||||||
|
|
||||||
OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err));
|
|
||||||
return (int)(err == OS_NO_ERR);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
|
||||||
return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
|
||||||
return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* Release a Grant to Access the Volume */
|
|
||||||
/*------------------------------------------------------------------------*/
|
|
||||||
/* This function is called on leave file functions to unlock the volume.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void ff_mutex_give (
|
|
||||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
#if OS_TYPE == 0 /* Win32 */
|
|
||||||
ReleaseMutex(Mutex[vol]);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 1 /* uITRON */
|
|
||||||
unl_mtx(Mutex[vol]);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
|
||||||
OSMutexPost(Mutex[vol]);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
|
||||||
xSemaphoreGive(Mutex[vol]);
|
|
||||||
|
|
||||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
|
||||||
osMutexRelease(Mutex[vol]);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* FF_FS_REENTRANT */
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
|||||||
/* f_util.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include "ff.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *FRESULT_str(FRESULT i);
|
|
||||||
FRESULT delete_node (
|
|
||||||
TCHAR* path, /* Path name buffer with the sub-directory to delete */
|
|
||||||
UINT sz_buff, /* Size of path name buffer (items) */
|
|
||||||
FILINFO* fno /* Name read buffer */
|
|
||||||
);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/* ff_stdio.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
// For compatibility with FreeRTOS+FAT API
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
//
|
|
||||||
#include "ff.h"
|
|
||||||
//
|
|
||||||
#include "my_debug.h"
|
|
||||||
|
|
||||||
#define BaseType_t int
|
|
||||||
#define FF_FILE FIL
|
|
||||||
|
|
||||||
#define ff_rewind f_rewind
|
|
||||||
#define pvPortMalloc malloc
|
|
||||||
#define vPortFree free
|
|
||||||
#define ffconfigMAX_FILENAME 250
|
|
||||||
#define configASSERT myASSERT
|
|
||||||
#define FF_PRINTF printf
|
|
||||||
#define pdFREERTOS_ERRNO_NONE 0
|
|
||||||
#define FF_EOF (-1)
|
|
||||||
#define FF_SEEK_SET 0
|
|
||||||
#define FF_SEEK_CUR 1
|
|
||||||
#define FF_SEEK_END 2
|
|
||||||
#define pdFALSE 0
|
|
||||||
#define pdTRUE 1
|
|
||||||
#define ff_filelength f_size
|
|
||||||
#define ff_feof f_eof
|
|
||||||
|
|
||||||
typedef struct FF_STAT {
|
|
||||||
uint32_t st_size; /* Size of the object in number of bytes. */
|
|
||||||
// uint16_t st_mode; /* The mode (attribute bits) of this
|
|
||||||
// file or directory. */
|
|
||||||
} FF_Stat_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DIR dir;
|
|
||||||
FILINFO fileinfo;
|
|
||||||
const char *pcFileName;
|
|
||||||
uint32_t ulFileSize;
|
|
||||||
//uint8_t ucAttributes;
|
|
||||||
} FF_FindData_t;
|
|
||||||
|
|
||||||
FF_FILE *ff_fopen(const char *pcFile, const char *pcMode);
|
|
||||||
int ff_fclose(FF_FILE *pxStream);
|
|
||||||
int ff_stat(const char *pcFileName, FF_Stat_t *pxStatBuffer);
|
|
||||||
size_t ff_fwrite(const void *pvBuffer, size_t xSize, size_t xItems,
|
|
||||||
FF_FILE *pxStream);
|
|
||||||
size_t ff_fread(void *pvBuffer, size_t xSize, size_t xItems, FF_FILE *pxStream);
|
|
||||||
int ff_chdir(const char *pcDirectoryName);
|
|
||||||
char *ff_getcwd(char *pcBuffer, size_t xBufferLength);
|
|
||||||
int ff_mkdir(const char *pcPath);
|
|
||||||
int ff_fputc(int iChar, FF_FILE *pxStream);
|
|
||||||
int ff_fgetc(FF_FILE *pxStream);
|
|
||||||
int ff_rmdir(const char *pcDirectory);
|
|
||||||
int ff_remove(const char *pcPath);
|
|
||||||
long ff_ftell(FF_FILE *pxStream);
|
|
||||||
int ff_fseek(FF_FILE *pxStream, int iOffset, int iWhence);
|
|
||||||
int ff_findfirst(const char *pcDirectory, FF_FindData_t *pxFindData);
|
|
||||||
int ff_findnext( FF_FindData_t *pxFindData );
|
|
||||||
FF_FILE *ff_truncate( const char * pcFileName, long lTruncateSize );
|
|
||||||
int ff_seteof( FF_FILE *pxStream );
|
|
||||||
int ff_rename( const char *pcOldName, const char *pcNewName, int bDeleteIfExists );
|
|
||||||
char *ff_fgets(char *pcBuffer, size_t xCount, FF_FILE *pxStream);
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/* my_debug.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void my_printf(const char *pcFormat, ...) __attribute__((format(__printf__, 1, 2)));
|
|
||||||
|
|
||||||
void my_assert_func(const char *file, int line, const char *func,
|
|
||||||
const char *pred);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
//#if defined(DEBUG) && !defined(NDEBUG)
|
|
||||||
#define DBG_PRINTF my_printf
|
|
||||||
//#else
|
|
||||||
//#define DBG_PRINTF(fmt, args...) /* Don't do anything in release builds*/
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
#define myASSERT(__e) \
|
|
||||||
((__e) ? (void)0 : my_assert_func(__FILE__, __LINE__, __func__, #__e))
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/* rtc.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void time_init();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/* util.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
#ifndef _UTIL_H_
|
|
||||||
#define _UTIL_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "hardware/structs/scb.h"
|
|
||||||
|
|
||||||
// works with negative index
|
|
||||||
static inline int wrap_ix(int index, int n)
|
|
||||||
{
|
|
||||||
return ((index % n) + n) % n;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline)) static inline uint32_t calculate_checksum(uint32_t const *p, size_t const size){
|
|
||||||
uint32_t checksum = 0;
|
|
||||||
for (uint32_t i = 0; i < (size/sizeof(uint32_t))-1; i++){
|
|
||||||
checksum ^= *p;
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
return checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// from Google Chromium's codebase:
|
|
||||||
#ifndef COUNT_OF
|
|
||||||
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__attribute__((always_inline)) static inline void __DSB(void) {
|
|
||||||
__asm volatile("dsb 0xF" ::: "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patterned after CMSIS NVIC_SystemReset
|
|
||||||
__attribute__((__noreturn__)) static inline void system_reset() {
|
|
||||||
__DSB(); /* Ensure all outstanding memory accesses included
|
|
||||||
buffered write are completed before reset */
|
|
||||||
scb_hw->aircr = ((0x5FAUL << 16U) | (1UL << 2U));
|
|
||||||
__DSB(); /* Ensure completion of memory access */
|
|
||||||
for (;;) {
|
|
||||||
__asm volatile("nop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\brief Disable IRQ Interrupts
|
|
||||||
\details Disables IRQ interrupts by setting the I-bit in the CPSR.
|
|
||||||
Can only be executed in Privileged modes.
|
|
||||||
*/
|
|
||||||
__attribute__((always_inline)) static inline void __disable_irq(void) {
|
|
||||||
__asm volatile("cpsid i" : : : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
/* crc.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.
|
|
||||||
*/
|
|
||||||
/* Derived from:
|
|
||||||
* SD/MMC File System Library
|
|
||||||
* Copyright (c) 2016 Neil Thiessen
|
|
||||||
*
|
|
||||||
* 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 "crc.h"
|
|
||||||
|
|
||||||
static const char m_Crc7Table[] = {0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36,
|
|
||||||
0x3F, 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77, 0x19, 0x10, 0x0B,
|
|
||||||
0x02, 0x3D, 0x34, 0x2F, 0x26, 0x51, 0x58, 0x43, 0x4A, 0x75, 0x7C, 0x67,
|
|
||||||
0x6E, 0x32, 0x3B, 0x20, 0x29, 0x16, 0x1F, 0x04, 0x0D, 0x7A, 0x73, 0x68,
|
|
||||||
0x61, 0x5E, 0x57, 0x4C, 0x45, 0x2B, 0x22, 0x39, 0x30, 0x0F, 0x06, 0x1D,
|
|
||||||
0x14, 0x63, 0x6A, 0x71, 0x78, 0x47, 0x4E, 0x55, 0x5C, 0x64, 0x6D, 0x76,
|
|
||||||
0x7F, 0x40, 0x49, 0x52, 0x5B, 0x2C, 0x25, 0x3E, 0x37, 0x08, 0x01, 0x1A,
|
|
||||||
0x13, 0x7D, 0x74, 0x6F, 0x66, 0x59, 0x50, 0x4B, 0x42, 0x35, 0x3C, 0x27,
|
|
||||||
0x2E, 0x11, 0x18, 0x03, 0x0A, 0x56, 0x5F, 0x44, 0x4D, 0x72, 0x7B, 0x60,
|
|
||||||
0x69, 0x1E, 0x17, 0x0C, 0x05, 0x3A, 0x33, 0x28, 0x21, 0x4F, 0x46, 0x5D,
|
|
||||||
0x54, 0x6B, 0x62, 0x79, 0x70, 0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31,
|
|
||||||
0x38, 0x41, 0x48, 0x53, 0x5A, 0x65, 0x6C, 0x77, 0x7E, 0x09, 0x00, 0x1B,
|
|
||||||
0x12, 0x2D, 0x24, 0x3F, 0x36, 0x58, 0x51, 0x4A, 0x43, 0x7C, 0x75, 0x6E,
|
|
||||||
0x67, 0x10, 0x19, 0x02, 0x0B, 0x34, 0x3D, 0x26, 0x2F, 0x73, 0x7A, 0x61,
|
|
||||||
0x68, 0x57, 0x5E, 0x45, 0x4C, 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D,
|
|
||||||
0x04, 0x6A, 0x63, 0x78, 0x71, 0x4E, 0x47, 0x5C, 0x55, 0x22, 0x2B, 0x30,
|
|
||||||
0x39, 0x06, 0x0F, 0x14, 0x1D, 0x25, 0x2C, 0x37, 0x3E, 0x01, 0x08, 0x13,
|
|
||||||
0x1A, 0x6D, 0x64, 0x7F, 0x76, 0x49, 0x40, 0x5B, 0x52, 0x3C, 0x35, 0x2E,
|
|
||||||
0x27, 0x18, 0x11, 0x0A, 0x03, 0x74, 0x7D, 0x66, 0x6F, 0x50, 0x59, 0x42,
|
|
||||||
0x4B, 0x17, 0x1E, 0x05, 0x0C, 0x33, 0x3A, 0x21, 0x28, 0x5F, 0x56, 0x4D,
|
|
||||||
0x44, 0x7B, 0x72, 0x69, 0x60, 0x0E, 0x07, 0x1C, 0x15, 0x2A, 0x23, 0x38,
|
|
||||||
0x31, 0x46, 0x4F, 0x54, 0x5D, 0x62, 0x6B, 0x70, 0x79};
|
|
||||||
|
|
||||||
static const unsigned short m_Crc16Table[256] = {0x0000, 0x1021, 0x2042,
|
|
||||||
0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B,
|
|
||||||
0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5,
|
|
||||||
0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C,
|
|
||||||
0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4,
|
|
||||||
0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
|
||||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B,
|
|
||||||
0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5,
|
|
||||||
0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E,
|
|
||||||
0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96,
|
|
||||||
0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79,
|
|
||||||
0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03,
|
|
||||||
0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68,
|
|
||||||
0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
|
||||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188,
|
|
||||||
0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1,
|
|
||||||
0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB,
|
|
||||||
0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2,
|
|
||||||
0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E,
|
|
||||||
0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447,
|
|
||||||
0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D,
|
|
||||||
0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
|
||||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844,
|
|
||||||
0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C,
|
|
||||||
0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37,
|
|
||||||
0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D,
|
|
||||||
0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2,
|
|
||||||
0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA,
|
|
||||||
0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1,
|
|
||||||
0x1EF0};
|
|
||||||
|
|
||||||
char crc7(const char* data, int length)
|
|
||||||
{
|
|
||||||
//Calculate the CRC7 checksum for the specified data block
|
|
||||||
char crc = 0;
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
crc = m_Crc7Table[(crc << 1) ^ data[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
//Return the calculated checksum
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short crc16(const char* data, int length)
|
|
||||||
{
|
|
||||||
//Calculate the CRC16 checksum for the specified data block
|
|
||||||
unsigned short crc = 0;
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
crc = (crc << 8) ^ m_Crc16Table[((crc >> 8) ^ data[i]) & 0x00FF];
|
|
||||||
}
|
|
||||||
|
|
||||||
//Return the calculated checksum
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_crc16(unsigned short *pCrc16, const char data[], size_t length) {
|
|
||||||
for (size_t i = 0; i < length; i++) {
|
|
||||||
*pCrc16 = (*pCrc16 << 8) ^ m_Crc16Table[((*pCrc16 >> 8) ^ data[i]) & 0x00FF];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/* crc.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
/* Derived from:
|
|
||||||
* SD/MMC File System Library
|
|
||||||
* Copyright (c) 2016 Neil Thiessen
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SD_CRC_H
|
|
||||||
#define SD_CRC_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
char crc7(const char* data, int length);
|
|
||||||
unsigned short crc16(const char* data, int length);
|
|
||||||
void update_crc16(unsigned short *pCrc16, const char data[], size_t length);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
/* demo_logging.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!
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
void vLoggingPrintf( const char *pcFormat, ... )
|
|
||||||
{
|
|
||||||
char pcBuffer[256] = {0};
|
|
||||||
va_list xArgs;
|
|
||||||
va_start( xArgs, pcFormat );
|
|
||||||
vsnprintf( pcBuffer, sizeof(pcBuffer), pcFormat, xArgs );
|
|
||||||
va_end( xArgs );
|
|
||||||
printf("%s", pcBuffer);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
/*-----------------------------------------------------------*/
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/* hw_config.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ff.h"
|
|
||||||
#include "sd_card.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t sd_get_num();
|
|
||||||
sd_card_t *sd_get_by_num(size_t num);
|
|
||||||
|
|
||||||
size_t spi_get_num();
|
|
||||||
spi_t *spi_get_by_num(size_t num);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* [] END OF FILE */
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,93 +0,0 @@
|
|||||||
/* sd_card.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Note: The model used here is one FatFS per SD card.
|
|
||||||
// Multiple partitions on a card are not supported.
|
|
||||||
|
|
||||||
#ifndef _SD_CARD_H_
|
|
||||||
#define _SD_CARD_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
//
|
|
||||||
#include "hardware/gpio.h"
|
|
||||||
#include "pico/mutex.h"
|
|
||||||
//
|
|
||||||
#include "ff.h"
|
|
||||||
//
|
|
||||||
#include "spi.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// "Class" representing SD Cards
|
|
||||||
typedef struct {
|
|
||||||
const char *pcName;
|
|
||||||
spi_t *spi;
|
|
||||||
// Slave select is here in sd_card_t because multiple SDs can share an SPI
|
|
||||||
uint ss_gpio; // Slave select for this SD card
|
|
||||||
bool use_card_detect;
|
|
||||||
uint card_detect_gpio; // Card detect; ignored if !use_card_detect
|
|
||||||
uint card_detected_true; // Varies with card socket; ignored if !use_card_detect
|
|
||||||
// Drive strength levels for GPIO outputs.
|
|
||||||
// enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2,
|
|
||||||
// GPIO_DRIVE_STRENGTH_12MA = 3 }
|
|
||||||
bool set_drive_strength;
|
|
||||||
enum gpio_drive_strength ss_gpio_drive_strength;
|
|
||||||
|
|
||||||
// Following fields are used to keep track of the state of the card:
|
|
||||||
int m_Status; // Card status
|
|
||||||
uint64_t sectors; // Assigned dynamically
|
|
||||||
int card_type; // Assigned dynamically
|
|
||||||
mutex_t mutex;
|
|
||||||
FATFS fatfs;
|
|
||||||
bool mounted;
|
|
||||||
} sd_card_t;
|
|
||||||
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_NONE 0
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_UNUSABLE -5007 /*!< unusable card */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE -5008 /*!< No response from device */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_CRC -5009 /*!< CRC error */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */
|
|
||||||
#define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */
|
|
||||||
|
|
||||||
///* Disk Status Bits (DSTATUS) */
|
|
||||||
// See diskio.h.
|
|
||||||
//enum {
|
|
||||||
// STA_NOINIT = 0x01, /* Drive not initialized */
|
|
||||||
// STA_NODISK = 0x02, /* No medium in the drive */
|
|
||||||
// STA_PROTECT = 0x04 /* Write protected */
|
|
||||||
//};
|
|
||||||
|
|
||||||
bool sd_init_driver();
|
|
||||||
int sd_init(sd_card_t *pSD);
|
|
||||||
int sd_write_blocks(sd_card_t *pSD, const uint8_t *buffer,
|
|
||||||
uint64_t ulSectorNumber, uint32_t blockCnt);
|
|
||||||
int sd_read_blocks(sd_card_t *pSD, uint8_t *buffer, uint64_t ulSectorNumber,
|
|
||||||
uint32_t ulSectorCount);
|
|
||||||
bool sd_card_detect(sd_card_t *pSD);
|
|
||||||
uint64_t sd_sectors(sd_card_t *pSD);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/* sd_spi.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Standard includes. */
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
//
|
|
||||||
#include "hardware/gpio.h"
|
|
||||||
//
|
|
||||||
#include "my_debug.h"
|
|
||||||
#include "sd_card.h"
|
|
||||||
#include "sd_spi.h"
|
|
||||||
#include "spi.h"
|
|
||||||
|
|
||||||
//#define TRACE_PRINTF(fmt, args...)
|
|
||||||
#define TRACE_PRINTF printf // task_printf
|
|
||||||
|
|
||||||
void sd_spi_go_high_frequency(sd_card_t *pSD) {
|
|
||||||
uint actual = spi_set_baudrate(pSD->spi->hw_inst, pSD->spi->baud_rate);
|
|
||||||
TRACE_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual);
|
|
||||||
}
|
|
||||||
void sd_spi_go_low_frequency(sd_card_t *pSD) {
|
|
||||||
uint actual = spi_set_baudrate(pSD->spi->hw_inst, 400 * 1000); // Actual frequency: 398089
|
|
||||||
TRACE_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sd_spi_lock(sd_card_t *pSD) {
|
|
||||||
spi_lock(pSD->spi);
|
|
||||||
}
|
|
||||||
static void sd_spi_unlock(sd_card_t *pSD) {
|
|
||||||
spi_unlock(pSD->spi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Would do nothing if pSD->ss_gpio were set to GPIO_FUNC_SPI.
|
|
||||||
static void sd_spi_select(sd_card_t *pSD) {
|
|
||||||
gpio_put(pSD->ss_gpio, 0);
|
|
||||||
// A fill byte seems to be necessary, sometimes:
|
|
||||||
uint8_t fill = SPI_FILL_CHAR;
|
|
||||||
spi_write_blocking(pSD->spi->hw_inst, &fill, 1);
|
|
||||||
LED_ON();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sd_spi_deselect(sd_card_t *pSD) {
|
|
||||||
gpio_put(pSD->ss_gpio, 1);
|
|
||||||
LED_OFF();
|
|
||||||
/*
|
|
||||||
MMC/SDC enables/disables the DO output in synchronising to the SCLK. This
|
|
||||||
means there is a posibility of bus conflict with MMC/SDC and another SPI
|
|
||||||
slave that shares an SPI bus. Therefore to make MMC/SDC release the MISO
|
|
||||||
line, the master device needs to send a byte after the CS signal is
|
|
||||||
deasserted.
|
|
||||||
*/
|
|
||||||
uint8_t fill = SPI_FILL_CHAR;
|
|
||||||
spi_write_blocking(pSD->spi->hw_inst, &fill, 1);
|
|
||||||
}
|
|
||||||
/* Some SD cards want to be deselected between every bus transaction */
|
|
||||||
void sd_spi_deselect_pulse(sd_card_t *pSD) {
|
|
||||||
sd_spi_deselect(pSD);
|
|
||||||
// tCSH Pulse duration, CS high 200 ns
|
|
||||||
sd_spi_select(pSD);
|
|
||||||
}
|
|
||||||
void sd_spi_acquire(sd_card_t *pSD) {
|
|
||||||
sd_spi_lock(pSD);
|
|
||||||
sd_spi_select(pSD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sd_spi_release(sd_card_t *pSD) {
|
|
||||||
sd_spi_deselect(pSD);
|
|
||||||
sd_spi_unlock(pSD);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sd_spi_transfer(sd_card_t *pSD, const uint8_t *tx, uint8_t *rx,
|
|
||||||
size_t length) {
|
|
||||||
return spi_transfer(pSD->spi, tx, rx, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t sd_spi_write(sd_card_t *pSD, const uint8_t value) {
|
|
||||||
// TRACE_PRINTF("%s\n", __FUNCTION__);
|
|
||||||
uint8_t received = SPI_FILL_CHAR;
|
|
||||||
#if 0
|
|
||||||
int num = spi_write_read_blocking(pSD->spi->hw_inst, &value, &received, 1);
|
|
||||||
myASSERT(1 == num);
|
|
||||||
#else
|
|
||||||
bool success = spi_transfer(pSD->spi, &value, &received, 1);
|
|
||||||
myASSERT(success);
|
|
||||||
#endif
|
|
||||||
return received;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sd_spi_send_initializing_sequence(sd_card_t * pSD) {
|
|
||||||
bool old_ss = gpio_get(pSD->ss_gpio);
|
|
||||||
// Set DI and CS high and apply 74 or more clock pulses to SCLK:
|
|
||||||
gpio_put(pSD->ss_gpio, 1);
|
|
||||||
uint8_t ones[10];
|
|
||||||
memset(ones, 0xFF, sizeof ones);
|
|
||||||
absolute_time_t timeout_time = make_timeout_time_ms(1);
|
|
||||||
do {
|
|
||||||
sd_spi_transfer(pSD, ones, NULL, sizeof ones);
|
|
||||||
} while (0 < absolute_time_diff_us(get_absolute_time(), timeout_time));
|
|
||||||
gpio_put(pSD->ss_gpio, old_ss);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sd_spi_init_pl022(sd_card_t *pSD) {
|
|
||||||
// Let the PL022 SPI handle it.
|
|
||||||
// the CS line is brought high between each byte during transmission.
|
|
||||||
gpio_set_function(pSD->ss_gpio, GPIO_FUNC_SPI);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/* sd_spi.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SD_SPI_H_
|
|
||||||
#define _SD_SPI_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "sd_card.h"
|
|
||||||
|
|
||||||
/* Transfer tx to SPI while receiving SPI to rx.
|
|
||||||
tx or rx can be NULL if not important. */
|
|
||||||
bool sd_spi_transfer(sd_card_t *pSD, const uint8_t *tx, uint8_t *rx, size_t length);
|
|
||||||
uint8_t sd_spi_write(sd_card_t *pSD, const uint8_t value);
|
|
||||||
void sd_spi_deselect_pulse(sd_card_t *pSD);
|
|
||||||
void sd_spi_acquire(sd_card_t *pSD);
|
|
||||||
void sd_spi_release(sd_card_t *pSD);
|
|
||||||
void sd_spi_go_low_frequency(sd_card_t *this);
|
|
||||||
void sd_spi_go_high_frequency(sd_card_t *this);
|
|
||||||
|
|
||||||
/*
|
|
||||||
After power up, the host starts the clock and sends the initializing sequence on the CMD line.
|
|
||||||
This sequence is a contiguous stream of logical ‘1’s. The sequence length is the maximum of 1msec,
|
|
||||||
74 clocks or the supply-ramp-uptime; the additional 10 clocks
|
|
||||||
(over the 64 clocks after what the card should be ready for communication) is
|
|
||||||
provided to eliminate power-up synchronization problems.
|
|
||||||
*/
|
|
||||||
void sd_spi_send_initializing_sequence(sd_card_t * pSD);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
/* spi.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 <stdbool.h>
|
|
||||||
//
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
#include "pico/mutex.h"
|
|
||||||
#include "pico/sem.h"
|
|
||||||
//
|
|
||||||
#include "my_debug.h"
|
|
||||||
//
|
|
||||||
#include "spi.h"
|
|
||||||
|
|
||||||
static bool irqChannel1 = false;
|
|
||||||
static bool irqShared = true;
|
|
||||||
|
|
||||||
void spi_irq_handler(spi_t *pSPI) {
|
|
||||||
if (irqChannel1) {
|
|
||||||
if (dma_hw->ints1 & 1u << pSPI->rx_dma) { // Ours?
|
|
||||||
dma_hw->ints1 = 1u << pSPI->rx_dma; // clear it
|
|
||||||
myASSERT(!dma_channel_is_busy(pSPI->rx_dma));
|
|
||||||
sem_release(&pSPI->sem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (dma_hw->ints0 & 1u << pSPI->rx_dma) { // Ours?
|
|
||||||
dma_hw->ints0 = 1u << pSPI->rx_dma; // clear it
|
|
||||||
myASSERT(!dma_channel_is_busy(pSPI->rx_dma));
|
|
||||||
sem_release(&pSPI->sem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_spi_dma_irq_channel(bool useChannel1, bool shared) {
|
|
||||||
irqChannel1 = useChannel1;
|
|
||||||
irqShared = shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI Transfer: Read & Write (simultaneously) on SPI bus
|
|
||||||
// If the data that will be received is not important, pass NULL as rx.
|
|
||||||
// If the data that will be transmitted is not important,
|
|
||||||
// pass NULL as tx and then the SPI_FILL_CHAR is sent out as each data
|
|
||||||
// element.
|
|
||||||
bool spi_transfer(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length) {
|
|
||||||
// myASSERT(512 == length || 1 == length);
|
|
||||||
myASSERT(tx || rx);
|
|
||||||
// myASSERT(!(tx && rx));
|
|
||||||
|
|
||||||
// tx write increment is already false
|
|
||||||
if (tx) {
|
|
||||||
channel_config_set_read_increment(&pSPI->tx_dma_cfg, true);
|
|
||||||
} else {
|
|
||||||
static const uint8_t dummy = SPI_FILL_CHAR;
|
|
||||||
tx = &dummy;
|
|
||||||
channel_config_set_read_increment(&pSPI->tx_dma_cfg, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rx read increment is already false
|
|
||||||
if (rx) {
|
|
||||||
channel_config_set_write_increment(&pSPI->rx_dma_cfg, true);
|
|
||||||
} else {
|
|
||||||
static uint8_t dummy = 0xA5;
|
|
||||||
rx = &dummy;
|
|
||||||
channel_config_set_write_increment(&pSPI->rx_dma_cfg, false);
|
|
||||||
}
|
|
||||||
// Clear the interrupt request.
|
|
||||||
dma_hw->ints0 = 1u << pSPI->rx_dma;
|
|
||||||
|
|
||||||
dma_channel_configure(pSPI->tx_dma, &pSPI->tx_dma_cfg,
|
|
||||||
&spi_get_hw(pSPI->hw_inst)->dr, // write address
|
|
||||||
tx, // read address
|
|
||||||
length, // element count (each element is of
|
|
||||||
// size transfer_data_size)
|
|
||||||
false); // start
|
|
||||||
dma_channel_configure(pSPI->rx_dma, &pSPI->rx_dma_cfg,
|
|
||||||
rx, // write address
|
|
||||||
&spi_get_hw(pSPI->hw_inst)->dr, // read address
|
|
||||||
length, // element count (each element is of
|
|
||||||
// size transfer_data_size)
|
|
||||||
false); // start
|
|
||||||
|
|
||||||
// start them exactly simultaneously to avoid races (in extreme cases
|
|
||||||
// the FIFO could overflow)
|
|
||||||
dma_start_channel_mask((1u << pSPI->tx_dma) | (1u << pSPI->rx_dma));
|
|
||||||
|
|
||||||
/* Timeout 1 sec */
|
|
||||||
uint32_t timeOut = 1000;
|
|
||||||
/* Wait until master completes transfer or time out has occured. */
|
|
||||||
bool rc = sem_acquire_timeout_ms(
|
|
||||||
&pSPI->sem, timeOut); // Wait for notification from ISR
|
|
||||||
if (!rc) {
|
|
||||||
// If the timeout is reached the function will return false
|
|
||||||
DBG_PRINTF("Notification wait timed out in %s\n", __FUNCTION__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Shouldn't be necessary:
|
|
||||||
dma_channel_wait_for_finish_blocking(pSPI->tx_dma);
|
|
||||||
dma_channel_wait_for_finish_blocking(pSPI->rx_dma);
|
|
||||||
|
|
||||||
myASSERT(!dma_channel_is_busy(pSPI->tx_dma));
|
|
||||||
myASSERT(!dma_channel_is_busy(pSPI->rx_dma));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void spi_lock(spi_t *pSPI) {
|
|
||||||
myASSERT(mutex_is_initialized(&pSPI->mutex));
|
|
||||||
mutex_enter_blocking(&pSPI->mutex);
|
|
||||||
}
|
|
||||||
void spi_unlock(spi_t *pSPI) {
|
|
||||||
myASSERT(mutex_is_initialized(&pSPI->mutex));
|
|
||||||
mutex_exit(&pSPI->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool my_spi_init(spi_t *pSPI) {
|
|
||||||
auto_init_mutex(my_spi_init_mutex);
|
|
||||||
mutex_enter_blocking(&my_spi_init_mutex);
|
|
||||||
if (!pSPI->initialized) {
|
|
||||||
//// The SPI may be shared (using multiple SSs); protect it
|
|
||||||
//pSPI->mutex = xSemaphoreCreateRecursiveMutex();
|
|
||||||
//xSemaphoreTakeRecursive(pSPI->mutex, portMAX_DELAY);
|
|
||||||
if (!mutex_is_initialized(&pSPI->mutex)) mutex_init(&pSPI->mutex);
|
|
||||||
spi_lock(pSPI);
|
|
||||||
|
|
||||||
// For the IRQ notification:
|
|
||||||
sem_init(&pSPI->sem, 0, 1);
|
|
||||||
|
|
||||||
/* Configure component */
|
|
||||||
// Enable SPI at 100 kHz and connect to GPIOs
|
|
||||||
spi_init(pSPI->hw_inst, 100 * 1000);
|
|
||||||
spi_set_format(pSPI->hw_inst, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
|
||||||
|
|
||||||
gpio_set_function(pSPI->miso_gpio, GPIO_FUNC_SPI);
|
|
||||||
gpio_set_function(pSPI->mosi_gpio, GPIO_FUNC_SPI);
|
|
||||||
gpio_set_function(pSPI->sck_gpio, GPIO_FUNC_SPI);
|
|
||||||
// ss_gpio is initialized in sd_init_driver()
|
|
||||||
|
|
||||||
// Slew rate limiting levels for GPIO outputs.
|
|
||||||
// enum gpio_slew_rate { GPIO_SLEW_RATE_SLOW = 0, GPIO_SLEW_RATE_FAST = 1 }
|
|
||||||
// void gpio_set_slew_rate (uint gpio,enum gpio_slew_rate slew)
|
|
||||||
// Default appears to be GPIO_SLEW_RATE_SLOW.
|
|
||||||
|
|
||||||
// Drive strength levels for GPIO outputs.
|
|
||||||
// enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2,
|
|
||||||
// GPIO_DRIVE_STRENGTH_12MA = 3 }
|
|
||||||
// enum gpio_drive_strength gpio_get_drive_strength (uint gpio)
|
|
||||||
if (pSPI->set_drive_strength) {
|
|
||||||
gpio_set_drive_strength(pSPI->mosi_gpio, pSPI->mosi_gpio_drive_strength);
|
|
||||||
gpio_set_drive_strength(pSPI->sck_gpio, pSPI->sck_gpio_drive_strength);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SD cards' DO MUST be pulled up.
|
|
||||||
gpio_pull_up(pSPI->miso_gpio);
|
|
||||||
|
|
||||||
// Grab some unused dma channels
|
|
||||||
pSPI->tx_dma = dma_claim_unused_channel(true);
|
|
||||||
pSPI->rx_dma = dma_claim_unused_channel(true);
|
|
||||||
|
|
||||||
pSPI->tx_dma_cfg = dma_channel_get_default_config(pSPI->tx_dma);
|
|
||||||
pSPI->rx_dma_cfg = dma_channel_get_default_config(pSPI->rx_dma);
|
|
||||||
channel_config_set_transfer_data_size(&pSPI->tx_dma_cfg, DMA_SIZE_8);
|
|
||||||
channel_config_set_transfer_data_size(&pSPI->rx_dma_cfg, DMA_SIZE_8);
|
|
||||||
|
|
||||||
// We set the outbound DMA to transfer from a memory buffer to the SPI
|
|
||||||
// transmit FIFO paced by the SPI TX FIFO DREQ The default is for the
|
|
||||||
// read address to increment every element (in this case 1 byte -
|
|
||||||
// DMA_SIZE_8) and for the write address to remain unchanged.
|
|
||||||
channel_config_set_dreq(&pSPI->tx_dma_cfg, spi_get_index(pSPI->hw_inst)
|
|
||||||
? DREQ_SPI1_TX
|
|
||||||
: DREQ_SPI0_TX);
|
|
||||||
channel_config_set_write_increment(&pSPI->tx_dma_cfg, false);
|
|
||||||
|
|
||||||
// We set the inbound DMA to transfer from the SPI receive FIFO to a
|
|
||||||
// memory buffer paced by the SPI RX FIFO DREQ We coinfigure the read
|
|
||||||
// address to remain unchanged for each element, but the write address
|
|
||||||
// to increment (so data is written throughout the buffer)
|
|
||||||
channel_config_set_dreq(&pSPI->rx_dma_cfg, spi_get_index(pSPI->hw_inst)
|
|
||||||
? DREQ_SPI1_RX
|
|
||||||
: DREQ_SPI0_RX);
|
|
||||||
channel_config_set_read_increment(&pSPI->rx_dma_cfg, false);
|
|
||||||
|
|
||||||
/* Theory: we only need an interrupt on rx complete,
|
|
||||||
since if rx is complete, tx must also be complete. */
|
|
||||||
|
|
||||||
// Configure the processor to run dma_handler() when DMA IRQ 0/1 is
|
|
||||||
// asserted:
|
|
||||||
int irq = irqChannel1 ? DMA_IRQ_1 : DMA_IRQ_0;
|
|
||||||
if (irqShared) {
|
|
||||||
irq_add_shared_handler(
|
|
||||||
irq, pSPI->dma_isr,
|
|
||||||
PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
|
||||||
} else {
|
|
||||||
irq_set_exclusive_handler(irq, pSPI->dma_isr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the DMA to raise IRQ line 0/1 when the channel finishes a block
|
|
||||||
if (irqChannel1) {
|
|
||||||
dma_channel_set_irq1_enabled(pSPI->rx_dma, true);
|
|
||||||
} else {
|
|
||||||
dma_channel_set_irq0_enabled(pSPI->rx_dma, true);
|
|
||||||
}
|
|
||||||
irq_set_enabled(irq, true);
|
|
||||||
LED_INIT();
|
|
||||||
pSPI->initialized = true;
|
|
||||||
spi_unlock(pSPI);
|
|
||||||
}
|
|
||||||
mutex_exit(&my_spi_init_mutex);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
/* spi.h
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
//
|
|
||||||
// Pico includes
|
|
||||||
#include "hardware/dma.h"
|
|
||||||
#include "hardware/gpio.h"
|
|
||||||
#include "hardware/irq.h"
|
|
||||||
#include "hardware/spi.h"
|
|
||||||
#include "pico/mutex.h"
|
|
||||||
#include "pico/sem.h"
|
|
||||||
#include "pico/types.h"
|
|
||||||
|
|
||||||
#define SPI_FILL_CHAR (0xFF)
|
|
||||||
|
|
||||||
// "Class" representing SPIs
|
|
||||||
typedef struct {
|
|
||||||
// SPI HW
|
|
||||||
spi_inst_t *hw_inst;
|
|
||||||
uint miso_gpio; // SPI MISO GPIO number (not pin number)
|
|
||||||
uint mosi_gpio;
|
|
||||||
uint sck_gpio;
|
|
||||||
uint baud_rate;
|
|
||||||
|
|
||||||
// Drive strength levels for GPIO outputs.
|
|
||||||
// enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2,
|
|
||||||
// GPIO_DRIVE_STRENGTH_12MA = 3 }
|
|
||||||
bool set_drive_strength;
|
|
||||||
enum gpio_drive_strength mosi_gpio_drive_strength;
|
|
||||||
enum gpio_drive_strength sck_gpio_drive_strength;
|
|
||||||
|
|
||||||
// State variables:
|
|
||||||
uint tx_dma;
|
|
||||||
uint rx_dma;
|
|
||||||
dma_channel_config tx_dma_cfg;
|
|
||||||
dma_channel_config rx_dma_cfg;
|
|
||||||
irq_handler_t dma_isr;
|
|
||||||
bool initialized;
|
|
||||||
semaphore_t sem;
|
|
||||||
mutex_t mutex;
|
|
||||||
} spi_t;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// SPI DMA interrupts
|
|
||||||
void __not_in_flash_func(spi_irq_handler)(spi_t *pSPI);
|
|
||||||
|
|
||||||
bool __not_in_flash_func(spi_transfer)(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length);
|
|
||||||
void spi_lock(spi_t *pSPI);
|
|
||||||
void spi_unlock(spi_t *pSPI);
|
|
||||||
bool my_spi_init(spi_t *pSPI);
|
|
||||||
void set_spi_dma_irq_channel(bool useChannel1, bool shared);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NO_PICO_LED
|
|
||||||
# define USE_LED 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if USE_LED
|
|
||||||
# define LED_PIN 25
|
|
||||||
# define LED_INIT() \
|
|
||||||
{ \
|
|
||||||
gpio_init(LED_PIN); \
|
|
||||||
gpio_set_dir(LED_PIN, GPIO_OUT); \
|
|
||||||
}
|
|
||||||
# define LED_ON() gpio_put(LED_PIN, 1)
|
|
||||||
# define LED_OFF() gpio_put(LED_PIN, 0)
|
|
||||||
#else
|
|
||||||
# define LED_ON()
|
|
||||||
# define LED_OFF()
|
|
||||||
# define LED_INIT()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* [] END OF FILE */
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
/* f_util.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 "ff.h"
|
|
||||||
|
|
||||||
const char *FRESULT_str(FRESULT i) {
|
|
||||||
switch (i) {
|
|
||||||
case FR_OK:
|
|
||||||
return "Succeeded";
|
|
||||||
case FR_DISK_ERR:
|
|
||||||
return "A hard error occurred in the low level disk I/O layer";
|
|
||||||
case FR_INT_ERR:
|
|
||||||
return "Assertion failed";
|
|
||||||
case FR_NOT_READY:
|
|
||||||
return "The physical drive cannot work";
|
|
||||||
case FR_NO_FILE:
|
|
||||||
return "Could not find the file";
|
|
||||||
case FR_NO_PATH:
|
|
||||||
return "Could not find the path";
|
|
||||||
case FR_INVALID_NAME:
|
|
||||||
return "The path name format is invalid";
|
|
||||||
case FR_DENIED:
|
|
||||||
return "Access denied due to prohibited access or directory full";
|
|
||||||
case FR_EXIST:
|
|
||||||
return "Access denied due to prohibited access (exists)";
|
|
||||||
case FR_INVALID_OBJECT:
|
|
||||||
return "The file/directory object is invalid";
|
|
||||||
case FR_WRITE_PROTECTED:
|
|
||||||
return "The physical drive is write protected";
|
|
||||||
case FR_INVALID_DRIVE:
|
|
||||||
return "The logical drive number is invalid";
|
|
||||||
case FR_NOT_ENABLED:
|
|
||||||
return "The volume has no work area (mount)";
|
|
||||||
case FR_NO_FILESYSTEM:
|
|
||||||
return "There is no valid FAT volume";
|
|
||||||
case FR_MKFS_ABORTED:
|
|
||||||
return "The f_mkfs() aborted due to any problem";
|
|
||||||
case FR_TIMEOUT:
|
|
||||||
return "Could not get a grant to access the volume within defined "
|
|
||||||
"period";
|
|
||||||
case FR_LOCKED:
|
|
||||||
return "The operation is rejected according to the file sharing "
|
|
||||||
"policy";
|
|
||||||
case FR_NOT_ENOUGH_CORE:
|
|
||||||
return "LFN working buffer could not be allocated";
|
|
||||||
case FR_TOO_MANY_OPEN_FILES:
|
|
||||||
return "Number of open files > FF_FS_LOCK";
|
|
||||||
case FR_INVALID_PARAMETER:
|
|
||||||
return "Given parameter is invalid";
|
|
||||||
default:
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FRESULT delete_node (
|
|
||||||
TCHAR* path, /* Path name buffer with the sub-directory to delete */
|
|
||||||
UINT sz_buff, /* Size of path name buffer (items) */
|
|
||||||
FILINFO* fno /* Name read buffer */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
UINT i, j;
|
|
||||||
FRESULT fr;
|
|
||||||
DIR dir;
|
|
||||||
|
|
||||||
|
|
||||||
fr = f_opendir(&dir, path); /* Open the sub-directory to make it empty */
|
|
||||||
if (fr != FR_OK) return fr;
|
|
||||||
|
|
||||||
for (i = 0; path[i]; i++) ; /* Get current path length */
|
|
||||||
path[i++] = '/';
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
fr = f_readdir(&dir, fno); /* Get a directory item */
|
|
||||||
if (fr != FR_OK || !fno->fname[0]) break; /* End of directory? */
|
|
||||||
j = 0;
|
|
||||||
do { /* Make a path name */
|
|
||||||
if (i + j >= sz_buff) { /* Buffer over flow? */
|
|
||||||
fr = 100; break; /* Fails with 100 when buffer overflow */
|
|
||||||
}
|
|
||||||
path[i + j] = fno->fname[j];
|
|
||||||
} while (fno->fname[j++]);
|
|
||||||
if (fno->fattrib & AM_DIR) { /* Item is a sub-directory */
|
|
||||||
fr = delete_node(path, sz_buff, fno);
|
|
||||||
} else { /* Item is a file */
|
|
||||||
fr = f_unlink(path);
|
|
||||||
}
|
|
||||||
if (fr != FR_OK) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
path[--i] = 0; /* Restore the path name */
|
|
||||||
f_closedir(&dir);
|
|
||||||
|
|
||||||
if (fr == FR_OK) fr = f_unlink(path); /* Delete the empty sub-directory */
|
|
||||||
return fr;
|
|
||||||
}
|
|
||||||
@@ -1,463 +0,0 @@
|
|||||||
/* ff_stdio.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.
|
|
||||||
*/
|
|
||||||
// For compatibility with FreeRTOS+FAT API
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "my_debug.h"
|
|
||||||
//
|
|
||||||
#include "f_util.h"
|
|
||||||
#include "ff_stdio.h"
|
|
||||||
|
|
||||||
#define TRACE_PRINTF(fmt, args...) {}
|
|
||||||
//#define TRACE_PRINTF printf
|
|
||||||
|
|
||||||
static BYTE posix2mode(const char *pcMode) {
|
|
||||||
if (0 == strcmp("r", pcMode)) return FA_READ;
|
|
||||||
if (0 == strcmp("r+", pcMode)) return FA_READ | FA_WRITE;
|
|
||||||
if (0 == strcmp("w", pcMode)) return FA_CREATE_ALWAYS | FA_WRITE;
|
|
||||||
if (0 == strcmp("w+", pcMode)) return FA_CREATE_ALWAYS | FA_WRITE | FA_READ;
|
|
||||||
if (0 == strcmp("a", pcMode)) return FA_OPEN_APPEND | FA_WRITE;
|
|
||||||
if (0 == strcmp("a+", pcMode)) return FA_OPEN_APPEND | FA_WRITE | FA_READ;
|
|
||||||
if (0 == strcmp("wx", pcMode)) return FA_CREATE_NEW | FA_WRITE;
|
|
||||||
if (0 == strcmp("w+x", pcMode)) return FA_CREATE_NEW | FA_WRITE | FA_READ;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fresult2errno(FRESULT fr) {
|
|
||||||
switch (fr) {
|
|
||||||
case FR_OK:
|
|
||||||
return 0;
|
|
||||||
case FR_DISK_ERR:
|
|
||||||
return EIO;
|
|
||||||
case FR_INT_ERR:
|
|
||||||
return EIO;
|
|
||||||
case FR_NOT_READY:
|
|
||||||
return EIO;
|
|
||||||
case FR_NO_FILE:
|
|
||||||
return ENOENT;
|
|
||||||
case FR_NO_PATH:
|
|
||||||
return ENOENT;
|
|
||||||
case FR_INVALID_NAME:
|
|
||||||
return ENAMETOOLONG;
|
|
||||||
case FR_DENIED:
|
|
||||||
return EACCES;
|
|
||||||
case FR_EXIST:
|
|
||||||
return EEXIST;
|
|
||||||
case FR_INVALID_OBJECT:
|
|
||||||
return EIO;
|
|
||||||
case FR_WRITE_PROTECTED:
|
|
||||||
return EACCES;
|
|
||||||
case FR_INVALID_DRIVE:
|
|
||||||
return ENOENT;
|
|
||||||
case FR_NOT_ENABLED:
|
|
||||||
return ENOENT;
|
|
||||||
case FR_NO_FILESYSTEM:
|
|
||||||
return ENOENT;
|
|
||||||
case FR_MKFS_ABORTED:
|
|
||||||
return EIO;
|
|
||||||
case FR_TIMEOUT:
|
|
||||||
return EIO;
|
|
||||||
case FR_LOCKED:
|
|
||||||
return EACCES;
|
|
||||||
case FR_NOT_ENOUGH_CORE:
|
|
||||||
return ENOMEM;
|
|
||||||
case FR_TOO_MANY_OPEN_FILES:
|
|
||||||
return ENFILE;
|
|
||||||
case FR_INVALID_PARAMETER:
|
|
||||||
return ENOSYS;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FF_FILE *ff_fopen(const char *pcFile, const char *pcMode) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);
|
|
||||||
// /* Open or create a file */ FRESULT f_open (
|
|
||||||
// FIL* fp, /* [OUT] Pointer to the file object structure */
|
|
||||||
// const TCHAR* path, /* [IN] File name */
|
|
||||||
// BYTE mode /* [IN] Mode flags */
|
|
||||||
//);
|
|
||||||
FIL *fp = malloc(sizeof(FIL));
|
|
||||||
if (!fp) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
FRESULT fr = f_open(fp, pcFile, posix2mode(pcMode));
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK != fr) {
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
free(fp);
|
|
||||||
fp = 0;
|
|
||||||
}
|
|
||||||
return fp;
|
|
||||||
}
|
|
||||||
int ff_fclose(FF_FILE *pxStream) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_close (
|
|
||||||
// FIL* fp /* [IN] Pointer to the file object */
|
|
||||||
//);
|
|
||||||
FRESULT fr = f_close(pxStream);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
free(pxStream);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// Populates an ff_stat_struct with information about a file.
|
|
||||||
int ff_stat(const char *pcFileName, FF_Stat_t *pxStatBuffer) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_stat (
|
|
||||||
// const TCHAR* path, /* [IN] Object name */
|
|
||||||
// FILINFO* fno /* [OUT] FILINFO structure */
|
|
||||||
//);
|
|
||||||
myASSERT(pxStatBuffer);
|
|
||||||
FILINFO filinfo;
|
|
||||||
FRESULT fr = f_stat(pcFileName, &filinfo);
|
|
||||||
pxStatBuffer->st_size = filinfo.fsize;
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
size_t ff_fwrite(const void *pvBuffer, size_t xSize, size_t xItems,
|
|
||||||
FF_FILE *pxStream) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_write (
|
|
||||||
// FIL* fp, /* [IN] Pointer to the file object structure */
|
|
||||||
// const void* buff, /* [IN] Pointer to the data to be written */
|
|
||||||
// UINT btw, /* [IN] Number of bytes to write */
|
|
||||||
// UINT* bw /* [OUT] Pointer to the variable to return number of
|
|
||||||
// bytes written */
|
|
||||||
//);
|
|
||||||
UINT bw = 0;
|
|
||||||
FRESULT fr = f_write(pxStream, pvBuffer, xSize * xItems, &bw);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
return bw / xSize;
|
|
||||||
}
|
|
||||||
size_t ff_fread(void *pvBuffer, size_t xSize, size_t xItems,
|
|
||||||
FF_FILE *pxStream) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_read (
|
|
||||||
// FIL* fp, /* [IN] File object */
|
|
||||||
// void* buff, /* [OUT] Buffer to store read data */
|
|
||||||
// UINT btr, /* [IN] Number of bytes to read */
|
|
||||||
// UINT* br /* [OUT] Number of bytes read */
|
|
||||||
//);
|
|
||||||
UINT br = 0;
|
|
||||||
FRESULT fr = f_read(pxStream, pvBuffer, xSize * xItems, &br);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
return br / xSize;
|
|
||||||
}
|
|
||||||
int ff_chdir(const char *pcDirectoryName) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_chdir (
|
|
||||||
// const TCHAR* path /* [IN] Path name */
|
|
||||||
//);
|
|
||||||
FRESULT fr = f_chdir(pcDirectoryName);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
char *ff_getcwd(char *pcBuffer, size_t xBufferLength) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_getcwd (
|
|
||||||
// TCHAR* buff, /* [OUT] Buffer to return path name */
|
|
||||||
// UINT len /* [IN] The length of the buffer */
|
|
||||||
//);
|
|
||||||
char buf[xBufferLength];
|
|
||||||
FRESULT fr = f_getcwd(buf, xBufferLength);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
// If the current working directory name was successfully written to
|
|
||||||
// pcBuffer then pcBuffer is returned. Otherwise NULL is returned.
|
|
||||||
if (FR_OK == fr) {
|
|
||||||
if ('/' != buf[0]) {
|
|
||||||
// Strip off drive prefix:
|
|
||||||
char *p = strchr(buf, ':');
|
|
||||||
if (p)
|
|
||||||
++p;
|
|
||||||
else
|
|
||||||
p = buf;
|
|
||||||
strncpy(pcBuffer, p, xBufferLength);
|
|
||||||
}
|
|
||||||
return pcBuffer;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int ff_mkdir(const char *pcDirectoryName) {
|
|
||||||
TRACE_PRINTF("%s(pxStream=%s)\n", __func__, pcDirectoryName);
|
|
||||||
FRESULT fr = f_mkdir(pcDirectoryName);
|
|
||||||
if (FR_OK != fr && FR_EXIST != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr || FR_EXIST == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int ff_fputc(int iChar, FF_FILE *pxStream) {
|
|
||||||
// TRACE_PRINTF("%s(iChar=%c,pxStream=%p)\n", __func__, iChar, pxStream);
|
|
||||||
// FRESULT f_write (
|
|
||||||
// FIL* fp, /* [IN] Pointer to the file object structure */
|
|
||||||
// const void* buff, /* [IN] Pointer to the data to be written */
|
|
||||||
// UINT btw, /* [IN] Number of bytes to write */
|
|
||||||
// UINT* bw /* [OUT] Pointer to the variable to return number of
|
|
||||||
// bytes written */
|
|
||||||
//);
|
|
||||||
UINT bw = 0;
|
|
||||||
uint8_t buff[1];
|
|
||||||
buff[0] = iChar;
|
|
||||||
FRESULT fr = f_write(pxStream, buff, 1, &bw);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
// On success the byte written to the file is returned. If any other value
|
|
||||||
// is returned then the byte was not written to the file and the task's
|
|
||||||
// errno will be set to indicate the reason.
|
|
||||||
if (1 == bw)
|
|
||||||
return iChar;
|
|
||||||
else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int ff_fgetc(FF_FILE *pxStream) {
|
|
||||||
// TRACE_PRINTF("%s(pxStream=%p)\n", __func__, pxStream);
|
|
||||||
// FRESULT f_read (
|
|
||||||
// FIL* fp, /* [IN] File object */
|
|
||||||
// void* buff, /* [OUT] Buffer to store read data */
|
|
||||||
// UINT btr, /* [IN] Number of bytes to read */
|
|
||||||
// UINT* br /* [OUT] Number of bytes read */
|
|
||||||
//);
|
|
||||||
uint8_t buff[1] = {0};
|
|
||||||
UINT br;
|
|
||||||
FRESULT fr = f_read(pxStream, buff, 1, &br);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
// On success the byte read from the file system is returned. If a byte
|
|
||||||
// could not be read from the file because the read position is already at
|
|
||||||
// the end of the file then FF_EOF is returned.
|
|
||||||
if (1 == br)
|
|
||||||
return buff[0];
|
|
||||||
else
|
|
||||||
return FF_EOF;
|
|
||||||
}
|
|
||||||
int ff_rmdir(const char *pcDirectory) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_unlink (
|
|
||||||
// const TCHAR* path /* [IN] Object name */
|
|
||||||
//);
|
|
||||||
FRESULT fr = f_unlink(pcDirectory);
|
|
||||||
// If the directory was removed successfully then zero is returned. If the
|
|
||||||
// directory could not be removed then -1 is returned and the task's errno
|
|
||||||
// is set to indicate the reason.
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int ff_remove(const char *pcPath) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
FRESULT fr = f_unlink(pcPath);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
long ff_ftell(FF_FILE *pxStream) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FSIZE_t f_tell (
|
|
||||||
// FIL* fp /* [IN] File object */
|
|
||||||
//);
|
|
||||||
FSIZE_t pos = f_tell(pxStream);
|
|
||||||
myASSERT(pos < LONG_MAX);
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
int ff_fseek(FF_FILE *pxStream, int iOffset, int iWhence) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
FRESULT fr = -1;
|
|
||||||
switch (iWhence) {
|
|
||||||
case FF_SEEK_CUR: // The current file position.
|
|
||||||
if ((int)f_tell(pxStream) + iOffset < 0) return -1;
|
|
||||||
fr = f_lseek(pxStream, f_tell(pxStream) + iOffset);
|
|
||||||
break;
|
|
||||||
case FF_SEEK_END: // The end of the file.
|
|
||||||
if ((int)f_size(pxStream) + iOffset < 0) return -1;
|
|
||||||
fr = f_lseek(pxStream, f_size(pxStream) + iOffset);
|
|
||||||
break;
|
|
||||||
case FF_SEEK_SET: // The beginning of the file.
|
|
||||||
if (iOffset < 0) return -1;
|
|
||||||
fr = f_lseek(pxStream, iOffset);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
myASSERT(!"Bad iWhence");
|
|
||||||
}
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int ff_findfirst(const char *pcDirectory, FF_FindData_t *pxFindData) {
|
|
||||||
TRACE_PRINTF("%s(%s)\n", __func__, pcDirectory);
|
|
||||||
// FRESULT f_findfirst (
|
|
||||||
// DIR* dp, /* [OUT] Poninter to the directory object */
|
|
||||||
// FILINFO* fno, /* [OUT] Pointer to the file information structure
|
|
||||||
// */ const TCHAR* path, /* [IN] Pointer to the directory name to be
|
|
||||||
// opened */ const TCHAR* pattern /* [IN] Pointer to the matching pattern
|
|
||||||
// string */
|
|
||||||
//);
|
|
||||||
char buf1[ffconfigMAX_FILENAME] = {0};
|
|
||||||
if (pcDirectory[0]) {
|
|
||||||
FRESULT fr = f_getcwd(buf1, sizeof buf1);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK != fr) return -1;
|
|
||||||
fr = f_chdir(pcDirectory);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK != fr) return -1;
|
|
||||||
}
|
|
||||||
char buf2[ffconfigMAX_FILENAME] = {0};
|
|
||||||
FRESULT fr = f_getcwd(buf2, sizeof buf2);
|
|
||||||
TRACE_PRINTF("%s: f_findfirst(path=%s)\n", __func__, buf2);
|
|
||||||
fr = f_findfirst(&pxFindData->dir, &pxFindData->fileinfo, buf2, "*");
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
pxFindData->pcFileName = pxFindData->fileinfo.fname;
|
|
||||||
pxFindData->ulFileSize = pxFindData->fileinfo.fsize;
|
|
||||||
TRACE_PRINTF("%s: fname=%s\n", __func__, pxFindData->fileinfo.fname);
|
|
||||||
if (pcDirectory[0]) {
|
|
||||||
FRESULT fr2 = f_chdir(buf1);
|
|
||||||
errno = fresult2errno(fr2);
|
|
||||||
if (FR_OK != fr2) return -1;
|
|
||||||
}
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int ff_findnext(FF_FindData_t *pxFindData) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_findnext (
|
|
||||||
// DIR* dp, /* [IN] Poninter to the directory object */
|
|
||||||
// FILINFO* fno /* [OUT] Pointer to the file information structure
|
|
||||||
// */
|
|
||||||
//);
|
|
||||||
FRESULT fr = f_findnext(&pxFindData->dir, &pxFindData->fileinfo);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
pxFindData->pcFileName = pxFindData->fileinfo.fname;
|
|
||||||
pxFindData->ulFileSize = pxFindData->fileinfo.fsize;
|
|
||||||
TRACE_PRINTF("%s: fname=%s\n", __func__, pxFindData->fileinfo.fname);
|
|
||||||
if (FR_OK == fr && pxFindData->fileinfo.fname[0]) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FF_FILE *ff_truncate(const char *pcFileName, long lTruncateSize) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
FIL *fp = malloc(sizeof(FIL));
|
|
||||||
if (!fp) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
FRESULT fr = f_open(fp, pcFileName, FA_OPEN_APPEND | FA_WRITE);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
printf("%s: f_open error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK != fr) return NULL;
|
|
||||||
while (f_tell(fp) < (FSIZE_t)lTruncateSize) {
|
|
||||||
UINT bw = 0;
|
|
||||||
char c = 0;
|
|
||||||
fr = f_write(fp, &c, 1, &bw);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (1 != bw) return NULL;
|
|
||||||
}
|
|
||||||
fr = f_lseek(fp, lTruncateSize);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
printf("%s: f_lseek error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
|
|
||||||
if (FR_OK != fr) return NULL;
|
|
||||||
fr = f_truncate(fp);
|
|
||||||
if (FR_OK != fr)
|
|
||||||
printf("%s: f_truncate error: %s (%d)\n", __func__, FRESULT_str(fr),
|
|
||||||
fr);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return fp;
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
int ff_seteof(FF_FILE *pxStream) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
FRESULT fr = f_truncate(pxStream);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return FF_EOF;
|
|
||||||
}
|
|
||||||
int ff_rename(const char *pcOldName, const char *pcNewName,
|
|
||||||
int bDeleteIfExists) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
// FRESULT f_rename (
|
|
||||||
// const TCHAR* old_name, /* [IN] Old object name */
|
|
||||||
// const TCHAR* new_name /* [IN] New object name */
|
|
||||||
//);
|
|
||||||
// Any object with this path name except old_name must not be exist, or the
|
|
||||||
// function fails with FR_EXIST.
|
|
||||||
if (bDeleteIfExists) f_unlink(pcNewName);
|
|
||||||
FRESULT fr = f_rename(pcOldName, pcNewName);
|
|
||||||
errno = fresult2errno(fr);
|
|
||||||
if (FR_OK == fr)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
char *ff_fgets(char *pcBuffer, size_t xCount, FF_FILE *pxStream) {
|
|
||||||
TRACE_PRINTF("%s\n", __func__);
|
|
||||||
TCHAR *p = f_gets(pcBuffer, xCount, pxStream);
|
|
||||||
// On success a pointer to pcBuffer is returned. If there is a read error
|
|
||||||
// then NULL is returned and the task's errno is set to indicate the reason.
|
|
||||||
if (p == pcBuffer)
|
|
||||||
return pcBuffer;
|
|
||||||
else {
|
|
||||||
errno = EIO;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
/* glue.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.
|
|
||||||
*/
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* If a working storage control module is available, it should be */
|
|
||||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
|
||||||
/* This is an example of glue functions to attach various exsisting */
|
|
||||||
/* storage control modules to the FatFs module with a defined API. */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
#include <stdio.h>
|
|
||||||
//
|
|
||||||
#include "ff.h" /* Obtains integer types */
|
|
||||||
//
|
|
||||||
#include "diskio.h" /* Declarations of disk functions */
|
|
||||||
//
|
|
||||||
#include "hw_config.h"
|
|
||||||
#include "my_debug.h"
|
|
||||||
#include "sd_card.h"
|
|
||||||
|
|
||||||
#define TRACE_PRINTF(fmt, args...)
|
|
||||||
//#define TRACE_PRINTF printf // task_printf
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Get Drive Status */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
|
|
||||||
) {
|
|
||||||
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
|
|
||||||
sd_card_t *p_sd = sd_get_by_num(pdrv);
|
|
||||||
if (!p_sd) return RES_PARERR;
|
|
||||||
sd_card_detect(p_sd); // Fast: just a GPIO read
|
|
||||||
return p_sd->m_Status; // See http://elm-chan.org/fsw/ff/doc/dstat.html
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Inidialize a Drive */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DSTATUS disk_initialize(
|
|
||||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
|
||||||
) {
|
|
||||||
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
|
|
||||||
sd_card_t *p_sd = sd_get_by_num(pdrv);
|
|
||||||
if (!p_sd) return RES_PARERR;
|
|
||||||
return sd_init(p_sd); // See http://elm-chan.org/fsw/ff/doc/dstat.html
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sdrc2dresult(int sd_rc) {
|
|
||||||
switch (sd_rc) {
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_NONE:
|
|
||||||
return RES_OK;
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_UNUSABLE:
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_NO_RESPONSE:
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_NO_INIT:
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_NO_DEVICE:
|
|
||||||
return RES_NOTRDY;
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_PARAMETER:
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_UNSUPPORTED:
|
|
||||||
return RES_PARERR;
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED:
|
|
||||||
return RES_WRPRT;
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_CRC:
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK:
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_ERASE:
|
|
||||||
case SD_BLOCK_DEVICE_ERROR_WRITE:
|
|
||||||
default:
|
|
||||||
return RES_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Read Sector(s) */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DRESULT disk_read(BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
|
||||||
BYTE *buff, /* Data buffer to store read data */
|
|
||||||
LBA_t sector, /* Start sector in LBA */
|
|
||||||
UINT count /* Number of sectors to read */
|
|
||||||
) {
|
|
||||||
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
|
|
||||||
sd_card_t *p_sd = sd_get_by_num(pdrv);
|
|
||||||
if (!p_sd) return RES_PARERR;
|
|
||||||
int rc = sd_read_blocks(p_sd, buff, sector, count);
|
|
||||||
return sdrc2dresult(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Write Sector(s) */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#if FF_FS_READONLY == 0
|
|
||||||
|
|
||||||
DRESULT disk_write(BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
|
||||||
const BYTE *buff, /* Data to be written */
|
|
||||||
LBA_t sector, /* Start sector in LBA */
|
|
||||||
UINT count /* Number of sectors to write */
|
|
||||||
) {
|
|
||||||
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
|
|
||||||
sd_card_t *p_sd = sd_get_by_num(pdrv);
|
|
||||||
if (!p_sd) return RES_PARERR;
|
|
||||||
int rc = sd_write_blocks(p_sd, buff, sector, count);
|
|
||||||
return sdrc2dresult(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Miscellaneous Functions */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
|
|
||||||
BYTE cmd, /* Control code */
|
|
||||||
void *buff /* Buffer to send/receive control data */
|
|
||||||
) {
|
|
||||||
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
|
|
||||||
sd_card_t *p_sd = sd_get_by_num(pdrv);
|
|
||||||
if (!p_sd) return RES_PARERR;
|
|
||||||
switch (cmd) {
|
|
||||||
case GET_SECTOR_COUNT: { // Retrieves number of available sectors, the
|
|
||||||
// largest allowable LBA + 1, on the drive
|
|
||||||
// into the LBA_t variable pointed by buff.
|
|
||||||
// This command is used by f_mkfs and f_fdisk
|
|
||||||
// function to determine the size of
|
|
||||||
// volume/partition to be created. It is
|
|
||||||
// required when FF_USE_MKFS == 1.
|
|
||||||
static LBA_t n;
|
|
||||||
n = sd_sectors(p_sd);
|
|
||||||
*(LBA_t *)buff = n;
|
|
||||||
if (!n) return RES_ERROR;
|
|
||||||
return RES_OK;
|
|
||||||
}
|
|
||||||
case GET_BLOCK_SIZE: { // Retrieves erase block size of the flash
|
|
||||||
// memory media in unit of sector into the DWORD
|
|
||||||
// variable pointed by buff. The allowable value
|
|
||||||
// is 1 to 32768 in power of 2. Return 1 if the
|
|
||||||
// erase block size is unknown or non flash
|
|
||||||
// memory media. This command is used by only
|
|
||||||
// f_mkfs function and it attempts to align data
|
|
||||||
// area on the erase block boundary. It is
|
|
||||||
// required when FF_USE_MKFS == 1.
|
|
||||||
static DWORD bs = 1;
|
|
||||||
*(DWORD *)buff = bs;
|
|
||||||
return RES_OK;
|
|
||||||
}
|
|
||||||
case CTRL_SYNC:
|
|
||||||
return RES_OK;
|
|
||||||
default:
|
|
||||||
return RES_PARERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/* my_debug.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 <stdarg.h>
|
|
||||||
#include "my_debug.h"
|
|
||||||
|
|
||||||
void my_printf(const char *pcFormat, ...) {
|
|
||||||
char pcBuffer[256] = {0};
|
|
||||||
va_list xArgs;
|
|
||||||
va_start(xArgs, pcFormat);
|
|
||||||
vsnprintf(pcBuffer, sizeof(pcBuffer), pcFormat, xArgs);
|
|
||||||
va_end(xArgs);
|
|
||||||
printf("%s", pcBuffer);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void my_assert_func(const char *file, int line, const char *func,
|
|
||||||
const char *pred) {
|
|
||||||
printf("assertion \"%s\" failed: file \"%s\", line %d, function: %s\n",
|
|
||||||
pred, file, line, func);
|
|
||||||
fflush(stdout);
|
|
||||||
__asm volatile("cpsid i" : : : "memory"); /* Disable global interrupts. */
|
|
||||||
while (1) {
|
|
||||||
__asm("bkpt #0");
|
|
||||||
}; // Stop in GUI as if at a breakpoint (if debugging, otherwise loop
|
|
||||||
// forever)
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
/* rtc.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 <time.h>
|
|
||||||
//
|
|
||||||
#include "hardware/rtc.h"
|
|
||||||
#include "pico/stdio.h"
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
#include "pico/util/datetime.h"
|
|
||||||
//
|
|
||||||
#include "ff.h"
|
|
||||||
#include "util.h" // calculate_checksum
|
|
||||||
//
|
|
||||||
#include "rtc.h"
|
|
||||||
|
|
||||||
static time_t epochtime;
|
|
||||||
|
|
||||||
// Make an attempt to save a recent time stamp across reset:
|
|
||||||
typedef struct rtc_save {
|
|
||||||
uint32_t signature;
|
|
||||||
datetime_t datetime;
|
|
||||||
uint32_t checksum; // last, not included in checksum
|
|
||||||
} rtc_save_t;
|
|
||||||
static rtc_save_t rtc_save __attribute__((section(".uninitialized_data")));
|
|
||||||
|
|
||||||
static void update_epochtime() {
|
|
||||||
bool rc = rtc_get_datetime(&rtc_save.datetime);
|
|
||||||
if (rc) {
|
|
||||||
rtc_save.signature = 0xBABEBABE;
|
|
||||||
struct tm timeinfo = {
|
|
||||||
.tm_sec = rtc_save.datetime
|
|
||||||
.sec, /* Seconds. [0-60] (1 leap second) */
|
|
||||||
.tm_min = rtc_save.datetime.min, /* Minutes. [0-59] */
|
|
||||||
.tm_hour = rtc_save.datetime.hour, /* Hours. [0-23] */
|
|
||||||
.tm_mday = rtc_save.datetime.day, /* Day. [1-31] */
|
|
||||||
.tm_mon = rtc_save.datetime.month - 1, /* Month. [0-11] */
|
|
||||||
.tm_year = rtc_save.datetime.year - 1900, /* Year - 1900. */
|
|
||||||
.tm_wday = 0, /* Day of week. [0-6] */
|
|
||||||
.tm_yday = 0, /* Days in year.[0-365] */
|
|
||||||
.tm_isdst = -1 /* DST. [-1/0/1]*/
|
|
||||||
};
|
|
||||||
rtc_save.checksum = calculate_checksum((uint32_t *)&rtc_save,
|
|
||||||
offsetof(rtc_save_t, checksum));
|
|
||||||
epochtime = mktime(&timeinfo);
|
|
||||||
rtc_save.datetime.dotw = timeinfo.tm_wday;
|
|
||||||
// configASSERT(-1 != epochtime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t time(time_t *pxTime) {
|
|
||||||
update_epochtime();
|
|
||||||
if (pxTime) {
|
|
||||||
*pxTime = epochtime;
|
|
||||||
}
|
|
||||||
return epochtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void time_init() {
|
|
||||||
rtc_init();
|
|
||||||
datetime_t t = {0, 0, 0, 0, 0, 0, 0};
|
|
||||||
rtc_get_datetime(&t);
|
|
||||||
if (!t.year && rtc_save.datetime.year) {
|
|
||||||
uint32_t xor_checksum = calculate_checksum(
|
|
||||||
(uint32_t *)&rtc_save, offsetof(rtc_save_t, checksum));
|
|
||||||
if (rtc_save.signature == 0xBABEBABE &&
|
|
||||||
rtc_save.checksum == xor_checksum) {
|
|
||||||
// Set rtc
|
|
||||||
rtc_set_datetime(&rtc_save.datetime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by FatFs:
|
|
||||||
DWORD get_fattime(void) {
|
|
||||||
datetime_t t = {0, 0, 0, 0, 0, 0, 0};
|
|
||||||
bool rc = rtc_get_datetime(&t);
|
|
||||||
if (!rc) return 0;
|
|
||||||
|
|
||||||
DWORD fattime = 0;
|
|
||||||
// bit31:25
|
|
||||||
// Year origin from the 1980 (0..127, e.g. 37 for 2017)
|
|
||||||
uint8_t yr = t.year - 1980;
|
|
||||||
fattime |= (0b01111111 & yr) << 25;
|
|
||||||
// bit24:21
|
|
||||||
// Month (1..12)
|
|
||||||
uint8_t mo = t.month;
|
|
||||||
fattime |= (0b00001111 & mo) << 21;
|
|
||||||
// bit20:16
|
|
||||||
// Day of the month (1..31)
|
|
||||||
uint8_t da = t.day;
|
|
||||||
fattime |= (0b00011111 & da) << 16;
|
|
||||||
// bit15:11
|
|
||||||
// Hour (0..23)
|
|
||||||
uint8_t hr = t.hour;
|
|
||||||
fattime |= (0b00011111 & hr) << 11;
|
|
||||||
// bit10:5
|
|
||||||
// Minute (0..59)
|
|
||||||
uint8_t mi = t.min;
|
|
||||||
fattime |= (0b00111111 & mi) << 5;
|
|
||||||
// bit4:0
|
|
||||||
// Second / 2 (0..29, e.g. 25 for 50)
|
|
||||||
uint8_t sd = t.sec / 2;
|
|
||||||
fattime |= (0b00011111 & sd);
|
|
||||||
return fattime;
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/**
|
|
||||||
* Original source code from https://github.com/carlk3/no-OS-FatFS-SD-SPI-RPi-Pico
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <pico/stdlib.h>
|
|
||||||
#include <pico/stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "my_debug.h"
|
|
||||||
#include "f_util.h"
|
|
||||||
#include "ff.h"
|
|
||||||
#include "sd_card.h"
|
|
||||||
#include "diskio.h"
|
|
||||||
#include "rtc.h"
|
|
||||||
|
|
||||||
void spi_dma_isr();
|
|
||||||
|
|
||||||
static spi_t spis[]={
|
|
||||||
{
|
|
||||||
.hw_inst=spi1,
|
|
||||||
.miso_gpio=12,
|
|
||||||
.mosi_gpio=15,
|
|
||||||
.sck_gpio=14,
|
|
||||||
.baud_rate=12500*1000,
|
|
||||||
.dma_isr=spi_dma_isr
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static sd_card_t sd_cards[]={
|
|
||||||
{
|
|
||||||
.pcName="0:",
|
|
||||||
.spi=&spis[0],
|
|
||||||
.ss_gpio=13,
|
|
||||||
.use_card_detect=false,
|
|
||||||
.m_Status=STA_NOINIT
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void spi_dma_isr() {
|
|
||||||
spi_irq_handler(&spis[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sd_get_num() {
|
|
||||||
return count_of(sd_cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
sd_card_t *sd_get_by_num(size_t num) {
|
|
||||||
if (num<=sd_get_num()) {
|
|
||||||
return &sd_cards[num];
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t spi_get_num() {
|
|
||||||
return count_of(spis);
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_t *spi_get_by_num(size_t num) {
|
|
||||||
if (num <= sd_get_num()) {
|
|
||||||
return &spis[num];
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,7 @@ lib_ldf_mode = chain+
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
bodmer/TFT_eSPI@^2.5.43
|
bodmer/TFT_eSPI@^2.5.43
|
||||||
xreef/PCF8574 library@^2.3.7
|
xreef/PCF8574 library@^2.3.7
|
||||||
|
greiman/SdFat@^2.3.1
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
-I.
|
-I.
|
||||||
@@ -27,12 +28,20 @@ build_flags =
|
|||||||
-DENABLE_SOUND=1
|
-DENABLE_SOUND=1
|
||||||
-DENABLE_LCD=1
|
-DENABLE_LCD=1
|
||||||
-DENABLE_SDCARD=1
|
-DENABLE_SDCARD=1
|
||||||
|
-DENABLE_LCD_DMA=1
|
||||||
|
-DENABLE_LCD_FRAMEBUFFER=1
|
||||||
|
-DENABLE_USB_STORAGE_DEVICE=1
|
||||||
-DPEANUT_FULL_GBC_SUPPORT=1
|
-DPEANUT_FULL_GBC_SUPPORT=1
|
||||||
|
-DUSE_TINYUSB
|
||||||
|
|
||||||
[env:pico]
|
[env:pico]
|
||||||
extends = pico-base
|
extends = pico-base
|
||||||
board = rpipico
|
board = rpipico
|
||||||
|
# Reserve ~1MB for ROMs, i.e. ~800KB for code
|
||||||
|
board_build.filesystem_size = 1280k
|
||||||
|
|
||||||
[env:pico2]
|
[env:pico2]
|
||||||
extends = pico-base
|
extends = pico-base
|
||||||
board = rpipico2
|
board = rpipico2
|
||||||
|
# Reserve 3MB for ROMs, i.e. 1MB for code
|
||||||
|
board_build.filesystem_size = 3072k
|
||||||
|
|||||||
+244
-154
@@ -1,209 +1,299 @@
|
|||||||
#if ENABLE_SDCARD
|
/**
|
||||||
|
* Copyright (C) 2022 by Mahyar Koshkouei <mk@deltabeard.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ENABLE_SDCARD
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "SdFat.h"
|
||||||
|
#include "hardware/flash.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "gb.h"
|
||||||
|
#include "card_loader.h"
|
||||||
|
|
||||||
|
#define RAM_SAVENAME_LENGTH 16
|
||||||
|
|
||||||
|
SdFs sd;
|
||||||
|
|
||||||
|
gpio_function_t UseSDPinFunctionScope::sd_sck_pin_func = GPIO_FUNC_NULL;
|
||||||
|
gpio_function_t UseSDPinFunctionScope::sd_mosi_pin_func = GPIO_FUNC_NULL;
|
||||||
|
|
||||||
|
bool init_sdcard_hardware() {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
SPI.setMISO(SD_MISO_PIN);
|
||||||
|
SPI.setMOSI(SD_MOSI_PIN);
|
||||||
|
SPI.setSCK(SD_SCK_PIN);
|
||||||
|
bool success = sd.begin(SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(50), &SPI));
|
||||||
|
if (success) {
|
||||||
|
UseSDPinFunctionScope::init();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_sdcard() {
|
||||||
|
Serial.println("Initialize SD-Card ...");
|
||||||
|
|
||||||
|
if (!init_sdcard_hardware()) {
|
||||||
|
tft.setCursor(0, ERROR_TEXT_OFFSET, FONT_ID);
|
||||||
|
tft.setTextColor(TFT_RED);
|
||||||
|
sd.printSdError(&tft);
|
||||||
|
sd.printSdError(&Serial);
|
||||||
|
reset(5000);
|
||||||
|
}
|
||||||
|
if (sd.vol()->fatType() == 0) { // vol() and fatType() do not access the sd-card
|
||||||
|
error("Can't find a valid FAT16/FAT32/exFAT partition");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("SD-Card initialized: FAT-Type=%d\n", sd.vol()->fatType());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a save file from the SD card
|
* Load a save file from the SD card
|
||||||
*/
|
*/
|
||||||
void read_cart_ram_file(struct gb_s* gb) {
|
void read_cart_ram_file(struct gb_s* gb) {
|
||||||
char filename[16];
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
char filename[RAM_SAVENAME_LENGTH];
|
||||||
uint_fast32_t save_size;
|
uint_fast32_t save_size;
|
||||||
UINT br;
|
FsFile file;
|
||||||
|
|
||||||
gb_get_rom_name(gb, filename);
|
gb_get_rom_name(gb, filename);
|
||||||
save_size = gb_get_save_size(gb);
|
save_size = gb_get_save_size(gb);
|
||||||
if (save_size > 0) {
|
if (save_size > 0) {
|
||||||
sd_card_t* pSD = sd_get_by_num(0);
|
if (!file.open(filename, O_RDONLY)) {
|
||||||
FRESULT fr = f_mount(&pSD->fatfs, pSD->pcName, 1);
|
Serial.printf("E f_open(%s) error\n", filename);
|
||||||
if (FR_OK != fr) {
|
|
||||||
printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FIL fil;
|
|
||||||
fr = f_open(&fil, filename, FA_READ);
|
|
||||||
if (fr == FR_OK) {
|
|
||||||
f_read(&fil, ram, f_size(&fil), &br);
|
|
||||||
} else {
|
} else {
|
||||||
printf("E f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
file.read(ram, file.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
fr = f_close(&fil);
|
if (!file.close()) {
|
||||||
if (fr != FR_OK) {
|
Serial.printf("E f_close error\n");
|
||||||
printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
}
|
}
|
||||||
f_unmount(pSD->pcName);
|
|
||||||
}
|
}
|
||||||
printf("I read_cart_ram_file(%s) COMPLETE (%lu bytes)\n", filename, save_size);
|
|
||||||
|
Serial.printf("I read_cart_ram_file(%s) COMPLETE (%lu bytes)\n", filename, save_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a save file to the SD card
|
* Write a save file to the SD card
|
||||||
*/
|
*/
|
||||||
void write_cart_ram_file(struct gb_s* gb) {
|
void write_cart_ram_file(struct gb_s* gb) {
|
||||||
char filename[16];
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
char filename[RAM_SAVENAME_LENGTH];
|
||||||
uint_fast32_t save_size;
|
uint_fast32_t save_size;
|
||||||
UINT bw;
|
FsFile file;
|
||||||
|
|
||||||
gb_get_rom_name(gb, filename);
|
gb_get_rom_name(gb, filename);
|
||||||
save_size = gb_get_save_size(gb);
|
save_size = gb_get_save_size(gb);
|
||||||
if (save_size > 0) {
|
if (save_size > 0) {
|
||||||
sd_card_t* pSD = sd_get_by_num(0);
|
if (!file.open(filename, O_WRONLY | O_CREAT)) {
|
||||||
FRESULT fr = f_mount(&pSD->fatfs, pSD->pcName, 1);
|
Serial.printf("E f_open(%s) error\n", filename);
|
||||||
if (FR_OK != fr) {
|
|
||||||
printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FIL fil;
|
file.write(ram, save_size);
|
||||||
fr = f_open(&fil, filename, FA_CREATE_ALWAYS | FA_WRITE);
|
if (!file.close()) {
|
||||||
if (fr == FR_OK) {
|
Serial.printf("E f_close error\n");
|
||||||
f_write(&fil, ram, save_size, &bw);
|
|
||||||
} else {
|
|
||||||
printf("E f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fr = f_close(&fil);
|
|
||||||
if (fr != FR_OK) {
|
|
||||||
printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
}
|
|
||||||
f_unmount(pSD->pcName);
|
|
||||||
}
|
}
|
||||||
printf("I write_cart_ram_file(%s) COMPLETE (%lu bytes)\n", filename, save_size);
|
|
||||||
|
Serial.printf("I write_cart_ram_file(%s) COMPLETE (%lu bytes)\n", filename, save_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_rom_sector_to_flash(FsFile& file, uint8_t* buffer, uint32_t offset) {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
int nread = file.read(buffer, FLASH_SECTOR_SIZE);
|
||||||
|
if (nread < 0) {
|
||||||
|
scope.close();
|
||||||
|
error("Failed to read file!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nread == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t flash_offset = ((uint32_t) &rom[offset]) - XIP_BASE;
|
||||||
|
|
||||||
|
uint32_t ints = save_and_disable_interrupts();
|
||||||
|
flash_range_erase(flash_offset, FLASH_SECTOR_SIZE);
|
||||||
|
flash_range_program(flash_offset, buffer, FLASH_SECTOR_SIZE);
|
||||||
|
restore_interrupts(ints);
|
||||||
|
|
||||||
|
/* Read back target region and check programming */
|
||||||
|
if (memcmp(&rom[offset], buffer, FLASH_SECTOR_SIZE) != 0) {
|
||||||
|
scope.close();
|
||||||
|
error("Programming failed - Flash mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void open_rom_file(FsFile& file, char* filename) {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
if (!file.open(filename, O_RDONLY)) {
|
||||||
|
scope.close();
|
||||||
|
error("Failed to open ROM: " + String(filename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_rom_file(FsFile& file) {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
if (!file.close()) {
|
||||||
|
Serial.printf("E f_close error\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a .gb rom file in flash from the SD card
|
* Load a .gb rom file in flash from the SD card
|
||||||
*/
|
*/
|
||||||
void load_cart_rom_file(char* filename) {
|
void load_cart_rom_file(char* filename) {
|
||||||
UINT br;
|
|
||||||
uint8_t buffer[FLASH_SECTOR_SIZE];
|
uint8_t buffer[FLASH_SECTOR_SIZE];
|
||||||
bool mismatch = false;
|
|
||||||
sd_card_t* pSD = sd_get_by_num(0);
|
tft.fillScreen(TFT_BLACK);
|
||||||
FRESULT fr = f_mount(&pSD->fatfs, pSD->pcName, 1);
|
tft.setCursor(0, 0, FONT_ID);
|
||||||
if (FR_OK != fr) {
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
tft.println("Loading ROM: ");
|
||||||
|
|
||||||
|
FsFile file;
|
||||||
|
open_rom_file(file, filename);
|
||||||
|
|
||||||
|
Serial.printf("I Program target region...\n");
|
||||||
|
|
||||||
|
uint32_t offset = 0;
|
||||||
|
while (write_rom_sector_to_flash(file, buffer, offset)) {
|
||||||
|
tft.print("#");
|
||||||
|
|
||||||
|
/* Next sector */
|
||||||
|
offset += FLASH_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_rom_file(file);
|
||||||
|
|
||||||
|
Serial.printf("I load_cart_rom_file(%s) COMPLETE\n", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t read_file_page_from_card(char filename[FILES_PER_PAGE][MAX_PATH_LENGTH], uint16_t num_page) {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
FsFile dir;
|
||||||
|
FsFile file;
|
||||||
|
|
||||||
|
/* clear the filenames array */
|
||||||
|
for (uint8_t ifile = 0; ifile < FILES_PER_PAGE; ifile++) {
|
||||||
|
strcpy(filename[ifile], "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir.open("/")) {
|
||||||
|
scope.close();
|
||||||
|
error("Failed to open root dir");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* search *.gb files */
|
||||||
|
uint16_t num_files = 0;
|
||||||
|
uint16_t num_file_offset = num_page * FILES_PER_PAGE;
|
||||||
|
char currentFilename[MAX_PATH_LENGTH];
|
||||||
|
while (file.openNext(&dir, O_RDONLY)) {
|
||||||
|
file.getName(currentFilename, sizeof(currentFilename));
|
||||||
|
|
||||||
|
auto currentFilenameStr = String(currentFilename);
|
||||||
|
currentFilenameStr.toLowerCase();
|
||||||
|
if (!currentFilenameStr.endsWith(".gb") && !currentFilenameStr.endsWith(".gbc")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_files < num_file_offset) {
|
||||||
|
// skip the first N pages
|
||||||
|
} else {
|
||||||
|
// store the filenames of this page
|
||||||
|
strcpy(filename[num_files], currentFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
num_files++;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (num_files >= num_file_offset + FILES_PER_PAGE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dir.close();
|
||||||
|
|
||||||
|
return num_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void print_file_entry(char* s, uint8_t index, uint8_t num_files, bool selected = false) {
|
||||||
|
if (num_files == 0) {
|
||||||
|
const char* no_files = "<No files on card>";
|
||||||
|
tft.drawString(no_files, ERROR_TEXT_OFFSET, index * FONT_HEIGHT, FONT_ID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FIL fil;
|
|
||||||
fr = f_open(&fil, filename, FA_READ);
|
|
||||||
if (fr == FR_OK) {
|
|
||||||
uint32_t flash_target_offset = FLASH_TARGET_OFFSET;
|
|
||||||
for (;;) {
|
|
||||||
f_read(&fil, buffer, sizeof buffer, &br);
|
|
||||||
if (br == 0)
|
|
||||||
break; /* end of file */
|
|
||||||
|
|
||||||
printf("I Erasing target region...\n");
|
tft.setTextColor(TFT_WHITE, selected ? TFT_RED : TFT_BLACK);
|
||||||
flash_range_erase(flash_target_offset, FLASH_SECTOR_SIZE);
|
tft.drawString(s, 0, index * FONT_HEIGHT, FONT_ID);
|
||||||
printf("I Programming target region...\n");
|
|
||||||
flash_range_program(flash_target_offset, buffer, FLASH_SECTOR_SIZE);
|
|
||||||
|
|
||||||
/* Read back target region and check programming */
|
|
||||||
printf("I Done. Reading back target region...\n");
|
|
||||||
for (uint32_t i = 0; i < FLASH_SECTOR_SIZE; i++) {
|
|
||||||
if (rom[flash_target_offset + i] != buffer[i]) {
|
|
||||||
mismatch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Next sector */
|
|
||||||
flash_target_offset += FLASH_SECTOR_SIZE;
|
|
||||||
}
|
|
||||||
if (mismatch) {
|
|
||||||
printf("I Programming successful!\n");
|
|
||||||
} else {
|
|
||||||
printf("E Programming failed!\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
printf("E f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
fr = f_close(&fil);
|
|
||||||
if (fr != FR_OK) {
|
|
||||||
printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
}
|
|
||||||
f_unmount(pSD->pcName);
|
|
||||||
|
|
||||||
printf("I load_cart_rom_file(%s) COMPLETE (%lu bytes)\n", filename, br);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function used by the rom file selector to display one page of .gb rom files
|
* Function used by the rom file selector to display one page of .gb rom files
|
||||||
*/
|
*/
|
||||||
uint16_t rom_file_selector_display_page(char filename[22][256], uint16_t num_page) {
|
static uint16_t rom_file_selector_display_page(char filename[FILES_PER_PAGE][MAX_PATH_LENGTH], uint16_t num_page) {
|
||||||
sd_card_t* pSD = sd_get_by_num(0);
|
uint16_t num_files = read_file_page_from_card(filename, num_page);
|
||||||
DIR dj;
|
|
||||||
FILINFO fno;
|
|
||||||
FRESULT fr;
|
|
||||||
|
|
||||||
fr = f_mount(&pSD->fatfs, pSD->pcName, 1);
|
|
||||||
if (FR_OK != fr) {
|
|
||||||
printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear the filenames array */
|
|
||||||
for (uint8_t ifile = 0; ifile < 22; ifile++) {
|
|
||||||
strcpy(filename[ifile], "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* search *.gb files */
|
|
||||||
uint16_t num_file = 0;
|
|
||||||
fr = f_findfirst(&dj, &fno, "", "*.gb");
|
|
||||||
|
|
||||||
/* skip the first N pages */
|
|
||||||
if (num_page > 0) {
|
|
||||||
while (num_file < num_page * 22 && fr == FR_OK && fno.fname[0]) {
|
|
||||||
num_file++;
|
|
||||||
fr = f_findnext(&dj, &fno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store the filenames of this page */
|
|
||||||
num_file = 0;
|
|
||||||
while (num_file < 22 && fr == FR_OK && fno.fname[0]) {
|
|
||||||
strcpy(filename[num_file], fno.fname);
|
|
||||||
num_file++;
|
|
||||||
fr = f_findnext(&dj, &fno);
|
|
||||||
}
|
|
||||||
f_closedir(&dj);
|
|
||||||
f_unmount(pSD->pcName);
|
|
||||||
|
|
||||||
/* display *.gb rom files on screen */
|
/* display *.gb rom files on screen */
|
||||||
lcd_fill(0x0000);
|
tft.fillScreen(TFT_BLACK);
|
||||||
for (uint8_t ifile = 0; ifile < num_file; ifile++) {
|
for (uint8_t ifile = 0; ifile < num_files; ifile++) {
|
||||||
lcd_text(filename[ifile], 0, ifile * 8, 0xFFFF, 0x0000);
|
print_file_entry(filename[ifile], ifile, num_files);
|
||||||
}
|
}
|
||||||
return num_file;
|
|
||||||
|
return num_files;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ROM selector displays pages of up to 22 rom files
|
* The ROM selector displays pages of up to FILES_PER_PAGE rom files
|
||||||
* allowing the user to select which rom file to start
|
* allowing the user to select which rom file to start
|
||||||
* Copy your *.gb rom files to the root directory of the SD card
|
* Copy your *.gb rom files to the root directory of the SD card
|
||||||
*/
|
*/
|
||||||
void rom_file_selector() {
|
void rom_file_selector() {
|
||||||
uint16_t num_page;
|
uint16_t num_page = 0;
|
||||||
char filename[22][256];
|
char filename[FILES_PER_PAGE][MAX_PATH_LENGTH];
|
||||||
uint16_t num_file;
|
uint16_t num_files;
|
||||||
|
|
||||||
/* display the first page with up to 22 rom files */
|
/* display the first page with up to FILES_PER_PAGE rom files */
|
||||||
num_file = rom_file_selector_display_page(filename, num_page);
|
num_files = rom_file_selector_display_page(filename, num_page);
|
||||||
|
|
||||||
/* select the first rom */
|
/* select the first rom */
|
||||||
uint8_t selected = 0;
|
uint8_t selected = 0;
|
||||||
lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800);
|
print_file_entry(filename[selected], selected, num_files, true);
|
||||||
|
|
||||||
/* get user's input */
|
/* get user's input */
|
||||||
bool up, down, left, right, a, b, select, start;
|
bool up, down, left, right, a, b, select, start;
|
||||||
while (true) {
|
while (true) {
|
||||||
up = gpio_get(GPIO_UP);
|
up = readJoypad(PIN_UP);
|
||||||
down = gpio_get(GPIO_DOWN);
|
down = readJoypad(PIN_DOWN);
|
||||||
left = gpio_get(GPIO_LEFT);
|
left = readJoypad(PIN_LEFT);
|
||||||
right = gpio_get(GPIO_RIGHT);
|
right = readJoypad(PIN_RIGHT);
|
||||||
a = gpio_get(GPIO_A);
|
a = readJoypad(PIN_A);
|
||||||
b = gpio_get(GPIO_B);
|
b = readJoypad(PIN_B);
|
||||||
select = gpio_get(GPIO_SELECT);
|
select = readJoypad(PIN_SELECT);
|
||||||
start = gpio_get(GPIO_START);
|
start = readJoypad(PIN_START);
|
||||||
if (!start) {
|
if (!start) {
|
||||||
/* re-start the last game (no need to reprogram flash) */
|
/* re-start the last game (no need to reprogram flash) */
|
||||||
break;
|
break;
|
||||||
@@ -215,45 +305,45 @@ void rom_file_selector() {
|
|||||||
}
|
}
|
||||||
if (!down) {
|
if (!down) {
|
||||||
/* select the next rom */
|
/* select the next rom */
|
||||||
lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0x0000);
|
print_file_entry(filename[selected], selected, num_files);
|
||||||
selected++;
|
selected++;
|
||||||
if (selected >= num_file)
|
if (selected >= num_files)
|
||||||
selected = 0;
|
selected = 0;
|
||||||
lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800);
|
print_file_entry(filename[selected], selected, num_files, true);
|
||||||
sleep_ms(150);
|
sleep_ms(150);
|
||||||
}
|
}
|
||||||
if (!up) {
|
if (!up) {
|
||||||
/* select the previous rom */
|
/* select the previous rom */
|
||||||
lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0x0000);
|
print_file_entry(filename[selected], selected, num_files);
|
||||||
if (selected == 0) {
|
if (selected == 0) {
|
||||||
selected = num_file - 1;
|
selected = num_files - 1;
|
||||||
} else {
|
} else {
|
||||||
selected--;
|
selected--;
|
||||||
}
|
}
|
||||||
lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800);
|
print_file_entry(filename[selected], selected, num_files, true);
|
||||||
sleep_ms(150);
|
sleep_ms(150);
|
||||||
}
|
}
|
||||||
if (!right) {
|
if (!right) {
|
||||||
/* select the next page */
|
/* select the next page */
|
||||||
num_page++;
|
num_page++;
|
||||||
num_file = rom_file_selector_display_page(filename, num_page);
|
num_files = rom_file_selector_display_page(filename, num_page);
|
||||||
if (num_file == 0) {
|
if (num_files == 0) {
|
||||||
/* no files in this page, go to the previous page */
|
/* no files in this page, go to the previous page */
|
||||||
num_page--;
|
num_page--;
|
||||||
num_file = rom_file_selector_display_page(filename, num_page);
|
num_files = rom_file_selector_display_page(filename, num_page);
|
||||||
}
|
}
|
||||||
/* select the first file */
|
/* select the first file */
|
||||||
selected = 0;
|
selected = 0;
|
||||||
lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800);
|
print_file_entry(filename[selected], selected, num_files, true);
|
||||||
sleep_ms(150);
|
sleep_ms(150);
|
||||||
}
|
}
|
||||||
if ((!left) && num_page > 0) {
|
if ((!left) && num_page > 0) {
|
||||||
/* select the previous page */
|
/* select the previous page */
|
||||||
num_page--;
|
num_page--;
|
||||||
num_file = rom_file_selector_display_page(filename, num_page);
|
num_files = rom_file_selector_display_page(filename, num_page);
|
||||||
/* select the first file */
|
/* select the first file */
|
||||||
selected = 0;
|
selected = 0;
|
||||||
lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800);
|
print_file_entry(filename[selected], selected, num_files, true);
|
||||||
sleep_ms(150);
|
sleep_ms(150);
|
||||||
}
|
}
|
||||||
tight_loop_contents();
|
tight_loop_contents();
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "SdFat.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define FILES_PER_PAGE 22
|
||||||
|
#define MAX_PATH_LENGTH 256
|
||||||
|
|
||||||
|
extern SdFs sd;
|
||||||
|
|
||||||
|
void init_sdcard();
|
||||||
|
|
||||||
|
void read_cart_ram_file(struct gb_s* gb);
|
||||||
|
|
||||||
|
void write_cart_ram_file(struct gb_s* gb);
|
||||||
|
|
||||||
|
void rom_file_selector();
|
||||||
|
|
||||||
|
class UseSDPinFunctionScope {
|
||||||
|
public:
|
||||||
|
UseSDPinFunctionScope() :
|
||||||
|
old_sck_pin_func(gpio_get_function(SD_SCK_PIN)),
|
||||||
|
old_mosi_pin_func(gpio_get_function(SD_MOSI_PIN))
|
||||||
|
{
|
||||||
|
if (sd_sck_pin_func != GPIO_FUNC_NULL) {
|
||||||
|
gpio_set_function(SD_SCK_PIN, sd_sck_pin_func);
|
||||||
|
}
|
||||||
|
if (old_mosi_pin_func != GPIO_FUNC_NULL) {
|
||||||
|
gpio_set_function(SD_MOSI_PIN, sd_sck_pin_func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~UseSDPinFunctionScope() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
sd_sck_pin_func = gpio_get_function(SD_SCK_PIN);
|
||||||
|
sd_mosi_pin_func = gpio_get_function(SD_MOSI_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
gpio_set_function(SD_SCK_PIN, old_sck_pin_func);
|
||||||
|
gpio_set_function(SD_MOSI_PIN, old_mosi_pin_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static gpio_function_t sd_sck_pin_func;
|
||||||
|
static gpio_function_t sd_mosi_pin_func;
|
||||||
|
|
||||||
|
gpio_function_t old_sck_pin_func = gpio_get_function(SD_SCK_PIN);
|
||||||
|
gpio_function_t old_mosi_pin_func = gpio_get_function(SD_MOSI_PIN);
|
||||||
|
};
|
||||||
+59
-4
@@ -1,13 +1,65 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#if ENABLE_LCD
|
||||||
|
#include <TFT_eSPI.h>
|
||||||
|
#endif
|
||||||
|
#include <SdFat.h>
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
|
/* Joypad Pins. */
|
||||||
|
#define USE_JOYPAD_I2C_IO_EXPANDER
|
||||||
|
#ifdef USE_JOYPAD_I2C_IO_EXPANDER
|
||||||
|
// Use PCF8574 for Joypad. Only required if an LCD with 16-bit parallel bus is used,
|
||||||
|
// as Pico does not have enough pins for all peripherals. With 8-bit parallel or SPI LCDs this should not be necessary.
|
||||||
|
#define PCF8574_ADDR 0x20
|
||||||
|
#define PCF8574_SDA 20
|
||||||
|
#define PCF8574_SCL 21
|
||||||
|
// pins below are on the IO expander
|
||||||
|
#define PIN_UP 0
|
||||||
|
#define PIN_DOWN 1
|
||||||
|
#define PIN_LEFT 2
|
||||||
|
#define PIN_RIGHT 3
|
||||||
|
#define PIN_A 5
|
||||||
|
#define PIN_B 4
|
||||||
|
#define PIN_SELECT 6
|
||||||
|
#define PIN_START 7
|
||||||
|
#else
|
||||||
|
// Use GPIOs directly on Pico for Joypad
|
||||||
|
#define PIN_UP 2
|
||||||
|
#define PIN_DOWN 3
|
||||||
|
#define PIN_LEFT 4
|
||||||
|
#define PIN_RIGHT 5
|
||||||
|
#define PIN_A 6
|
||||||
|
#define PIN_B 7
|
||||||
|
#define PIN_SELECT 8
|
||||||
|
#define PIN_START 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_SOUND
|
||||||
|
#define I2S_DIN_PIN 26
|
||||||
|
#define I2S_BCLK_LRC_PIN_BASE 27 // BCLK + LRC (28)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_SDCARD
|
||||||
|
#define SD_CS_PIN 17
|
||||||
|
#define SD_SCK_PIN 18
|
||||||
|
#define SD_MOSI_PIN 19
|
||||||
|
#define SD_MISO_PIN 16
|
||||||
|
|
||||||
|
extern uint8_t _FS_start;
|
||||||
|
extern uint8_t _FS_end;
|
||||||
|
#define MAX_ROM_SIZE (&_FS_end - &_FS_start)
|
||||||
|
#endif
|
||||||
|
|
||||||
// display is rotated, so TFT_WIDTH/HEIGHT cannot be used
|
// display is rotated, so TFT_WIDTH/HEIGHT cannot be used
|
||||||
#define DISPLAY_WIDTH TFT_HEIGHT
|
#define DISPLAY_WIDTH TFT_HEIGHT
|
||||||
#define DISPLAY_HEIGHT TFT_WIDTH
|
#define DISPLAY_HEIGHT TFT_WIDTH
|
||||||
|
|
||||||
|
#define FONT_HEIGHT 8
|
||||||
|
#define ERROR_TEXT_OFFSET FONT_HEIGHT
|
||||||
|
#define FONT_ID 1
|
||||||
|
|
||||||
enum class ScalingMode {
|
enum class ScalingMode {
|
||||||
NORMAL = 0,
|
NORMAL = 0,
|
||||||
STRETCH,
|
STRETCH,
|
||||||
@@ -17,9 +69,8 @@ enum class ScalingMode {
|
|||||||
|
|
||||||
extern volatile ScalingMode scalingMode;
|
extern volatile ScalingMode scalingMode;
|
||||||
|
|
||||||
extern struct gb_s gb;
|
|
||||||
extern palette_t palette; // Colour palette
|
|
||||||
extern uint_fast32_t frames;
|
extern uint_fast32_t frames;
|
||||||
|
extern TFT_eSPI tft;
|
||||||
|
|
||||||
/* Multicore command structure. */
|
/* Multicore command structure. */
|
||||||
union core_cmd {
|
union core_cmd {
|
||||||
@@ -45,8 +96,12 @@ void nextPalette();
|
|||||||
|
|
||||||
void prevPalette();
|
void prevPalette();
|
||||||
|
|
||||||
|
void lcd_init(bool isCore1);
|
||||||
void lcd_draw_line(struct gb_s* gb, const uint8_t* pixels, const uint_fast8_t line);
|
void lcd_draw_line(struct gb_s* gb, const uint8_t* pixels, const uint_fast8_t line);
|
||||||
|
void lcd_fill(uint16_t color);
|
||||||
|
|
||||||
void core1_init();
|
void core1_init();
|
||||||
|
|
||||||
void reset();
|
void reset(uint32_t sleepMs = 0);
|
||||||
|
|
||||||
|
void error(String message);
|
||||||
|
|||||||
+64
@@ -4,3 +4,67 @@
|
|||||||
#include "peanut_gb.h"
|
#include "peanut_gb.h"
|
||||||
|
|
||||||
#include "gbcolors.h"
|
#include "gbcolors.h"
|
||||||
|
#include "gb.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
struct gb_s gb;
|
||||||
|
palette_t palette; // Colour palette
|
||||||
|
|
||||||
|
uint8_t ram[RAM_SIZE];
|
||||||
|
|
||||||
|
// Definition of ROM data
|
||||||
|
#if !ENABLE_SDCARD
|
||||||
|
#include "game_bin.h"
|
||||||
|
const uint8_t *rom = GAME_DATA;
|
||||||
|
#else
|
||||||
|
/** Definition of ROM data
|
||||||
|
* We're going to erase and reprogram the region defines as "Filesystem" in platformio.ini (see board_build.filesystem_size).
|
||||||
|
* This is available from _FS_start (i.e. XIP_BASE + program size) to _FS_end. Note that the last sector is reserved for EEPROM.
|
||||||
|
* Game Boy DMG ROM size ranges from 32768 bytes (e.g. Tetris) to 1,048,576 bytes (e.g. Pokemod Red)
|
||||||
|
*/
|
||||||
|
const uint8_t *rom = (const uint8_t *)(&_FS_start);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static unsigned char rom_bank0[65536];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a byte from the ROM file at the given address.
|
||||||
|
*/
|
||||||
|
static uint8_t gb_rom_read(struct gb_s* gb, const uint_fast32_t addr) {
|
||||||
|
(void)gb;
|
||||||
|
if (addr < sizeof(rom_bank0))
|
||||||
|
return rom_bank0[addr];
|
||||||
|
|
||||||
|
return rom[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a byte from the cartridge RAM at the given address.
|
||||||
|
*/
|
||||||
|
static uint8_t gb_cart_ram_read(struct gb_s* gb, const uint_fast32_t addr) {
|
||||||
|
(void)gb;
|
||||||
|
return ram[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a given byte to the cartridge RAM at the given address.
|
||||||
|
*/
|
||||||
|
static void gb_cart_ram_write(struct gb_s* gb, const uint_fast32_t addr,
|
||||||
|
const uint8_t val) {
|
||||||
|
ram[addr] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gb_error(struct gb_s* gb, const enum gb_error_e gb_err, const uint16_t addr) {
|
||||||
|
const char* gb_err_str[4] = {
|
||||||
|
"UNKNOWN",
|
||||||
|
"INVALID OPCODE",
|
||||||
|
"INVALID READ",
|
||||||
|
"INVALID WRITE"};
|
||||||
|
error(String("Error ") + gb_err + " occurred: " + gb_err_str[gb_err] + " at 0x" + String(addr, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_init_error_e initGbContext() {
|
||||||
|
memcpy(rom_bank0, rom, sizeof(rom_bank0));
|
||||||
|
return gb_init(&gb, &gb_rom_read, &gb_cart_ram_read,
|
||||||
|
&gb_cart_ram_write, &gb_error, NULL);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,3 +5,12 @@
|
|||||||
|
|
||||||
#define GBCOLOR_HEADER_ONLY
|
#define GBCOLOR_HEADER_ONLY
|
||||||
#include "gbcolors.h"
|
#include "gbcolors.h"
|
||||||
|
|
||||||
|
extern struct gb_s gb;
|
||||||
|
extern palette_t palette; // Colour palette
|
||||||
|
extern const uint8_t* rom;
|
||||||
|
|
||||||
|
#define RAM_SIZE 32768
|
||||||
|
extern uint8_t ram[RAM_SIZE];
|
||||||
|
|
||||||
|
gb_init_error_e initGbContext();
|
||||||
|
|||||||
+85
-40
@@ -1,10 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2022 by Mahyar Koshkouei <mk@deltabeard.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "i2s-audio.h"
|
|
||||||
|
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
|
#if ENABLE_SDCARD
|
||||||
|
#include "card_loader.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_SOUND
|
||||||
|
#include "i2s-audio.h"
|
||||||
extern i2s_config_t i2s_config;
|
extern i2s_config_t i2s_config;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern uint8_t gamma_int;
|
||||||
|
|
||||||
static struct
|
static struct
|
||||||
{
|
{
|
||||||
@@ -18,13 +40,30 @@ static struct
|
|||||||
unsigned down : 1;
|
unsigned down : 1;
|
||||||
} prev_joypad_bits;
|
} prev_joypad_bits;
|
||||||
|
|
||||||
#ifndef USE_PAD_GPIO
|
#ifdef USE_JOYPAD_I2C_IO_EXPANDER
|
||||||
static PCF8574 pcf8574(0x20, 16, 17);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
static PCF8574 pcf8574(PCF8574_ADDR, PCF8574_SDA, PCF8574_SCL);
|
||||||
|
|
||||||
#ifdef USE_PAD_GPIO
|
static void initJoypadI2CIoExpander() {
|
||||||
void initJoypad() {
|
for (int pin = 0; pin < 8; ++pin) {
|
||||||
|
pcf8574.pinMode(pin, INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcf8574.setLatency(5);
|
||||||
|
|
||||||
|
if (!pcf8574.begin()){
|
||||||
|
Serial.println("PCF8574 initialization failed");
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readJoypad(uint8_t pin) {
|
||||||
|
return pcf8574.digitalRead(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void initJoypadGpios() {
|
||||||
gpio_set_function(GPIO_UP, GPIO_FUNC_SIO);
|
gpio_set_function(GPIO_UP, GPIO_FUNC_SIO);
|
||||||
gpio_set_function(GPIO_DOWN, GPIO_FUNC_SIO);
|
gpio_set_function(GPIO_DOWN, GPIO_FUNC_SIO);
|
||||||
gpio_set_function(GPIO_LEFT, GPIO_FUNC_SIO);
|
gpio_set_function(GPIO_LEFT, GPIO_FUNC_SIO);
|
||||||
@@ -50,25 +89,25 @@ void initJoypad() {
|
|||||||
gpio_pull_up(GPIO_A);
|
gpio_pull_up(GPIO_A);
|
||||||
gpio_pull_up(GPIO_B);
|
gpio_pull_up(GPIO_B);
|
||||||
gpio_pull_up(GPIO_SELECT);
|
gpio_pull_up(GPIO_SELECT);
|
||||||
gpio_pull_up(GPIO_START);
|
gpio_pull_up(GPIO_START);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
void initJoypad() {
|
|
||||||
for (int pin = 0; pin < 8; ++pin) {
|
|
||||||
pcf8574.pinMode(pin, INPUT_PULLUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcf8574.setLatency(5);
|
bool readJoypad(uint8_t pin) {
|
||||||
|
return gpio_get(pin);
|
||||||
if (pcf8574.begin()){
|
|
||||||
Serial.println("PCF8574 initialized");
|
|
||||||
}else{
|
|
||||||
Serial.println("PCF8574 initialization failed");
|
|
||||||
while (true) ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void initJoypad() {
|
||||||
|
Serial.println("Init Joypad IOs ...");
|
||||||
|
#ifdef USE_JOYPAD_I2C_IO_EXPANDER
|
||||||
|
initJoypadI2CIoExpander();
|
||||||
|
#else
|
||||||
|
initJoypadGpios();
|
||||||
|
#endif
|
||||||
|
Serial.println("Joypad IOs initialized");
|
||||||
|
}
|
||||||
|
|
||||||
void handleSerial() {
|
void handleSerial() {
|
||||||
static uint64_t start_time = time_us_64();
|
static uint64_t start_time = time_us_64();
|
||||||
|
|
||||||
@@ -158,7 +197,7 @@ void handleSerial() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handlePad() {
|
void handleJoypad() {
|
||||||
/* Update buttons state */
|
/* Update buttons state */
|
||||||
prev_joypad_bits.up = gb.direct.joypad_bits.up;
|
prev_joypad_bits.up = gb.direct.joypad_bits.up;
|
||||||
prev_joypad_bits.down = gb.direct.joypad_bits.down;
|
prev_joypad_bits.down = gb.direct.joypad_bits.down;
|
||||||
@@ -169,16 +208,6 @@ void handlePad() {
|
|||||||
prev_joypad_bits.select = gb.direct.joypad_bits.select;
|
prev_joypad_bits.select = gb.direct.joypad_bits.select;
|
||||||
prev_joypad_bits.start = gb.direct.joypad_bits.start;
|
prev_joypad_bits.start = gb.direct.joypad_bits.start;
|
||||||
|
|
||||||
#ifdef USE_PAD_GPIO
|
|
||||||
gb.direct.joypad_bits.up = gpio_get(PIN_UP);
|
|
||||||
gb.direct.joypad_bits.down = gpio_get(PIN_DOWN);
|
|
||||||
gb.direct.joypad_bits.left = gpio_get(PIN_LEFT);
|
|
||||||
gb.direct.joypad_bits.right = gpio_get(PIN_RIGHT);
|
|
||||||
gb.direct.joypad_bits.a = gpio_get(PIN_A);
|
|
||||||
gb.direct.joypad_bits.b = gpio_get(PIN_B);
|
|
||||||
gb.direct.joypad_bits.select = gpio_get(PIN_SELECT);
|
|
||||||
gb.direct.joypad_bits.start = gpio_get(PIN_START);
|
|
||||||
#else
|
|
||||||
#if 0
|
#if 0
|
||||||
Serial.printf("pins:\n");
|
Serial.printf("pins:\n");
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
@@ -188,14 +217,30 @@ void handlePad() {
|
|||||||
Serial.printf("\n");
|
Serial.printf("\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gb.direct.joypad_bits.up = pcf8574.digitalRead(PIN_UP);
|
gb.direct.joypad_bits.up = readJoypad(PIN_UP);
|
||||||
gb.direct.joypad_bits.down = pcf8574.digitalRead(PIN_DOWN);
|
gb.direct.joypad_bits.down = readJoypad(PIN_DOWN);
|
||||||
gb.direct.joypad_bits.left = pcf8574.digitalRead(PIN_LEFT);
|
gb.direct.joypad_bits.left = readJoypad(PIN_LEFT);
|
||||||
gb.direct.joypad_bits.right = pcf8574.digitalRead(PIN_RIGHT);
|
gb.direct.joypad_bits.right = readJoypad(PIN_RIGHT);
|
||||||
gb.direct.joypad_bits.a = pcf8574.digitalRead(PIN_A);
|
gb.direct.joypad_bits.a = readJoypad(PIN_A);
|
||||||
gb.direct.joypad_bits.b = pcf8574.digitalRead(PIN_B);
|
gb.direct.joypad_bits.b = readJoypad(PIN_B);
|
||||||
gb.direct.joypad_bits.select = pcf8574.digitalRead(PIN_SELECT);
|
gb.direct.joypad_bits.select = readJoypad(PIN_SELECT);
|
||||||
gb.direct.joypad_bits.start = pcf8574.digitalRead(PIN_START);
|
gb.direct.joypad_bits.start = readJoypad(PIN_START);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (!gb.direct.joypad_bits.up) {
|
||||||
|
if (!gb.direct.joypad_bits.select && prev_joypad_bits.select) {
|
||||||
|
gamma_int++;
|
||||||
|
Serial.printf("gamma: %d\n", gamma_int);
|
||||||
|
get_colour_palette(palette, 0, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!gb.direct.joypad_bits.down) {
|
||||||
|
if (!gb.direct.joypad_bits.select && prev_joypad_bits.select) {
|
||||||
|
gamma_int--;
|
||||||
|
get_colour_palette(palette, 0, 5);
|
||||||
|
Serial.printf("gamma: %d\n", gamma_int);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* hotkeys (select + * combo)*/
|
/* hotkeys (select + * combo)*/
|
||||||
|
|||||||
+2
-22
@@ -2,28 +2,8 @@
|
|||||||
|
|
||||||
#include <PCF8574.h>
|
#include <PCF8574.h>
|
||||||
|
|
||||||
/* GPIO Connections. */
|
|
||||||
#ifdef USE_PAD_GPIO
|
|
||||||
#define PIN_UP 2
|
|
||||||
#define PIN_DOWN 3
|
|
||||||
#define PIN_LEFT 4
|
|
||||||
#define PIN_RIGHT 5
|
|
||||||
#define PIN_A 6
|
|
||||||
#define PIN_B 7
|
|
||||||
#define PIN_SELECT 8
|
|
||||||
#define PIN_START 9
|
|
||||||
#else
|
|
||||||
#define PIN_UP 0
|
|
||||||
#define PIN_DOWN 1
|
|
||||||
#define PIN_LEFT 2
|
|
||||||
#define PIN_RIGHT 3
|
|
||||||
#define PIN_A 5
|
|
||||||
#define PIN_B 4
|
|
||||||
#define PIN_SELECT 6
|
|
||||||
#define PIN_START 7
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void initJoypad();
|
void initJoypad();
|
||||||
|
bool readJoypad(uint8_t pin);
|
||||||
|
|
||||||
void handlePad();
|
void handleJoypad();
|
||||||
void handleSerial();
|
void handleSerial();
|
||||||
|
|||||||
+62
-29
@@ -1,11 +1,24 @@
|
|||||||
#include <TFT_eSPI.h>
|
/**
|
||||||
|
* Copyright (C) 2022 by Mahyar Koshkouei <mk@deltabeard.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <TFT_eSPI.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
//#define USE_FRAMEBUFFER
|
TFT_eSPI tft = TFT_eSPI();
|
||||||
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
static TFT_eSPI tft = TFT_eSPI();
|
|
||||||
#ifdef USE_FRAMEBUFFER
|
|
||||||
static TFT_eSprite framebuffer = TFT_eSprite(&tft);
|
static TFT_eSprite framebuffer = TFT_eSprite(&tft);
|
||||||
#else
|
#else
|
||||||
static TFT_eSPI& framebuffer = tft;
|
static TFT_eSPI& framebuffer = tft;
|
||||||
@@ -30,10 +43,13 @@ static void calcExtraLineTable() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_init(void) {
|
void lcd_init(bool isCore1) {
|
||||||
tft.init();
|
tft.init();
|
||||||
#ifdef USE_FRAMEBUFFER
|
#if ENABLE_LCD_DMA
|
||||||
tft.initDMA();
|
// do not enable DMA on core0 as it fails on core1 if it is already enabled
|
||||||
|
if (isCore1 && !tft.initDMA(/*ctrl_cs not supported in RP2040 implementation*/)) {
|
||||||
|
error("Failed to initialize TFT DMA");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
tft.setRotation(1);
|
tft.setRotation(1);
|
||||||
tft.fillScreen(TFT_BLACK);
|
tft.fillScreen(TFT_BLACK);
|
||||||
@@ -56,7 +72,7 @@ void lcd_draw_line(struct gb_s* gb, const uint8_t pixels[LCD_WIDTH], const uint_
|
|||||||
multicore_fifo_push_blocking(cmd.full);
|
multicore_fifo_push_blocking(cmd.full);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
void lcd_pushColors(size_t offset, const uint16_t* pixels, uint_fast16_t count) {
|
void lcd_pushColors(size_t offset, const uint16_t* pixels, uint_fast16_t count) {
|
||||||
uint16_t* image = (uint16_t*) framebuffer.getPointer();
|
uint16_t* image = (uint16_t*) framebuffer.getPointer();
|
||||||
memcpy(&image[offset], pixels, count * sizeof(uint16_t));
|
memcpy(&image[offset], pixels, count * sizeof(uint16_t));
|
||||||
@@ -64,7 +80,18 @@ void lcd_pushColors(size_t offset, const uint16_t* pixels, uint_fast16_t count)
|
|||||||
#else
|
#else
|
||||||
void lcd_pushColors(uint16_t colOffset, uint16_t lineOffset, const uint16_t* pixels, uint_fast16_t count) {
|
void lcd_pushColors(uint16_t colOffset, uint16_t lineOffset, const uint16_t* pixels, uint_fast16_t count) {
|
||||||
framebuffer.setAddrWindow(colOffset, lineOffset, count, 1);
|
framebuffer.setAddrWindow(colOffset, lineOffset, count, 1);
|
||||||
|
#if ENABLE_LCD_DMA
|
||||||
|
// DMA mode does not have a measurable effect without a framebuffer
|
||||||
|
static uint16_t dmaBuffer[DISPLAY_WIDTH];
|
||||||
|
tft.dmaWait();
|
||||||
|
memcpy(dmaBuffer, pixels, count * sizeof(uint16_t));
|
||||||
|
tft.setSwapBytes(true);
|
||||||
|
tft.startWrite(); // manual start required as DMA transfer is asynchronous
|
||||||
|
tft.pushPixelsDMA((uint16_t*) dmaBuffer, count);
|
||||||
|
//tft.endWrite(); // do not call endWrite(), as it will wait for the DMA transfer to finish, which results in no performance gain
|
||||||
|
#else
|
||||||
tft.pushColors((uint16_t*) pixels, count, true);
|
tft.pushColors((uint16_t*) pixels, count, true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -72,7 +99,7 @@ void lcd_write_pixels_normal(const uint16_t* pixels, uint8_t line, uint_fast16_t
|
|||||||
const uint16_t colOffset = (DISPLAY_WIDTH - count) / 2;
|
const uint16_t colOffset = (DISPLAY_WIDTH - count) / 2;
|
||||||
const uint16_t screenLineOffset = (DISPLAY_HEIGHT - LCD_HEIGHT) / 2;
|
const uint16_t screenLineOffset = (DISPLAY_HEIGHT - LCD_HEIGHT) / 2;
|
||||||
const uint16_t lineOffset = screenLineOffset + line;
|
const uint16_t lineOffset = screenLineOffset + line;
|
||||||
#ifdef USE_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
lcd_pushColors(lineOffset * DISPLAY_WIDTH + colOffset, pixels, count);
|
lcd_pushColors(lineOffset * DISPLAY_WIDTH + colOffset, pixels, count);
|
||||||
#else
|
#else
|
||||||
lcd_pushColors(colOffset, lineOffset, pixels, count);
|
lcd_pushColors(colOffset, lineOffset, pixels, count);
|
||||||
@@ -90,7 +117,7 @@ void lcd_write_pixels_stretched(const uint16_t* pixels, uint8_t line, uint_fast1
|
|||||||
|
|
||||||
const uint8_t lineRepeated = IS_REPEATED(line);
|
const uint8_t lineRepeated = IS_REPEATED(line);
|
||||||
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
||||||
#ifdef USE_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
size_t offset = lineOffset * DISPLAY_WIDTH;
|
size_t offset = lineOffset * DISPLAY_WIDTH;
|
||||||
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
||||||
if (lineRepeated) {
|
if (lineRepeated) {
|
||||||
@@ -119,7 +146,7 @@ void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line
|
|||||||
|
|
||||||
uint8_t lineRepeated = IS_REPEATED(line);
|
uint8_t lineRepeated = IS_REPEATED(line);
|
||||||
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
||||||
#ifdef USE_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
size_t offset = lineOffset * DISPLAY_WIDTH + colOffset;
|
size_t offset = lineOffset * DISPLAY_WIDTH + colOffset;
|
||||||
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
||||||
if (lineRepeated) {
|
if (lineRepeated) {
|
||||||
@@ -133,6 +160,7 @@ void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes pixels to screen or framebuffer
|
||||||
void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
||||||
switch (scalingMode)
|
switch (scalingMode)
|
||||||
{
|
{
|
||||||
@@ -149,19 +177,28 @@ void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
|
// Writes framebuffer to screen
|
||||||
|
void lcd_write_framebuffer_to_screen() {
|
||||||
|
tft.setSwapBytes(true);
|
||||||
|
#ifdef ENABLE_LCD_DMA
|
||||||
|
tft.startWrite(); // manual start required as DMA transfer is asynchronous
|
||||||
|
tft.pushImageDMA(0, 0, framebuffer.width(), framebuffer.height(), (uint16_t *) framebuffer.getPointer());
|
||||||
|
//tft.endWrite(); // do not call endWrite(), as it will wait for the DMA transfer to finish, which results in no performance gain
|
||||||
|
#else
|
||||||
|
tft.pushImage(0, 0, framebuffer.width(), framebuffer.height(), (uint16_t *) framebuffer.getPointer());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void lcd_fill(uint16_t color) {
|
void lcd_fill(uint16_t color) {
|
||||||
#ifdef USE_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
framebuffer.fillSprite(color);
|
framebuffer.fillSprite(color);
|
||||||
#else
|
#else
|
||||||
tft.fillScreen(color);
|
tft.fillScreen(color);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_text(char* s, uint8_t x, uint8_t y, uint16_t color, uint16_t bgcolor) {
|
|
||||||
framebuffer.setTextColor(TFT_WHITE, TFT_BLACK); // TODO
|
|
||||||
framebuffer.drawString(s, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void core1_lcd_draw_line(const uint_fast8_t line) {
|
void core1_lcd_draw_line(const uint_fast8_t line) {
|
||||||
static uint16_t fb[LCD_WIDTH];
|
static uint16_t fb[LCD_WIDTH];
|
||||||
|
|
||||||
@@ -179,10 +216,9 @@ void core1_lcd_draw_line(const uint_fast8_t line) {
|
|||||||
lcd_write_pixels(fb, line, LCD_WIDTH);
|
lcd_write_pixels(fb, line, LCD_WIDTH);
|
||||||
__atomic_store_n(&lcd_line_busy, 0, __ATOMIC_SEQ_CST);
|
__atomic_store_n(&lcd_line_busy, 0, __ATOMIC_SEQ_CST);
|
||||||
|
|
||||||
#ifdef USE_FRAMEBUFFER
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
if (line == LCD_HEIGHT - 1) {
|
if (line == LCD_HEIGHT - 1) {
|
||||||
tft.setSwapBytes(true);
|
lcd_write_framebuffer_to_screen();
|
||||||
tft.pushImageDMA(0, 0, framebuffer.width(), framebuffer.height(), (uint16_t *) framebuffer.getPointer());
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -209,20 +245,17 @@ void core1DispatchLoop() {
|
|||||||
|
|
||||||
void core1_init() {
|
void core1_init() {
|
||||||
/* Initialise and control LCD on core 1. */
|
/* Initialise and control LCD on core 1. */
|
||||||
lcd_init();
|
lcd_init(true);
|
||||||
|
|
||||||
/* Clear LCD screen. */
|
#if ENABLE_LCD_FRAMEBUFFER
|
||||||
lcd_fill(TFT_BLACK);
|
|
||||||
|
|
||||||
#ifdef USE_FRAMEBUFFER
|
|
||||||
framebuffer.setColorDepth(16);
|
framebuffer.setColorDepth(16);
|
||||||
framebuffer.createSprite(tft.width(), tft.height());
|
framebuffer.createSprite(tft.width(), tft.height());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
calcExtraLineTable();
|
/* Clear LCD screen. */
|
||||||
|
lcd_fill(TFT_BLACK);
|
||||||
|
|
||||||
// Sleep used for debugging LCD window.
|
calcExtraLineTable();
|
||||||
// sleep_ms(1000);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
core1DispatchLoop();
|
core1DispatchLoop();
|
||||||
|
|||||||
+73
-116
@@ -15,29 +15,32 @@
|
|||||||
|
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
/* C Headers */
|
// C Headers
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* RP2040 Headers */
|
// RP2040 Headers
|
||||||
#include <hardware/vreg.h>
|
#include <hardware/vreg.h>
|
||||||
|
|
||||||
/* Project headers */
|
// Project headers
|
||||||
#include "hedley.h"
|
#include "hedley.h"
|
||||||
#include "minigb_apu.h"
|
|
||||||
|
|
||||||
// #include "sdcard.h"
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "game_bin.h"
|
|
||||||
#include "i2s-audio.h"
|
|
||||||
|
|
||||||
#define GBCOLOR_HEADER_ONLY
|
|
||||||
#include "gbcolors.h"
|
|
||||||
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
|
||||||
|
#if ENABLE_SDCARD
|
||||||
|
#include "card_loader.h"
|
||||||
|
#include "SdFat.h"
|
||||||
|
#ifdef ENABLE_USB_STORAGE_DEVICE
|
||||||
|
#include "msc.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ENABLE_SOUND
|
#if ENABLE_SOUND
|
||||||
|
#include "i2s-audio.h"
|
||||||
|
#include "minigb_apu.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global variables for audio task
|
* Global variables for audio task
|
||||||
* stream contains N=AUDIO_SAMPLES samples
|
* stream contains N=AUDIO_SAMPLES samples
|
||||||
@@ -46,86 +49,24 @@
|
|||||||
* This is intended to be played at AUDIO_SAMPLE_RATE Hz
|
* This is intended to be played at AUDIO_SAMPLE_RATE Hz
|
||||||
*/
|
*/
|
||||||
uint16_t* stream;
|
uint16_t* stream;
|
||||||
|
|
||||||
|
i2s_config_t i2s_config;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Definition of ROM data
|
|
||||||
* We're going to erase and reprogram a region 1Mb from the start of the flash
|
|
||||||
* Once done, we can access this at XIP_BASE + 1Mb.
|
|
||||||
* Game Boy DMG ROM size ranges from 32768 bytes (e.g. Tetris) to 1,048,576 bytes (e.g. Pokemod Red)
|
|
||||||
*/
|
|
||||||
// #define FLASH_TARGET_OFFSET (1024 * 1024)
|
|
||||||
// const uint8_t *rom = (const uint8_t *)(XIP_BASE + FLASH_TARGET_OFFSET);
|
|
||||||
const uint8_t* rom = GAME_DATA;
|
|
||||||
static unsigned char rom_bank0[65536];
|
|
||||||
|
|
||||||
static uint8_t ram[32768];
|
|
||||||
static uint8_t manual_palette_selected = 0;
|
static uint8_t manual_palette_selected = 0;
|
||||||
|
|
||||||
struct gb_s gb;
|
|
||||||
palette_t palette; // Colour palette
|
|
||||||
i2s_config_t i2s_config;
|
|
||||||
uint_fast32_t frames = 0;
|
uint_fast32_t frames = 0;
|
||||||
|
|
||||||
#define putstdio(x) write(1, x, strlen(x))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a byte from the ROM file at the given address.
|
|
||||||
*/
|
|
||||||
uint8_t gb_rom_read(struct gb_s* gb, const uint_fast32_t addr) {
|
|
||||||
(void)gb;
|
|
||||||
if (addr < sizeof(rom_bank0))
|
|
||||||
return rom_bank0[addr];
|
|
||||||
|
|
||||||
return rom[addr];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a byte from the cartridge RAM at the given address.
|
|
||||||
*/
|
|
||||||
uint8_t gb_cart_ram_read(struct gb_s* gb, const uint_fast32_t addr) {
|
|
||||||
(void)gb;
|
|
||||||
return ram[addr];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a given byte to the cartridge RAM at the given address.
|
|
||||||
*/
|
|
||||||
void gb_cart_ram_write(struct gb_s* gb, const uint_fast32_t addr,
|
|
||||||
const uint8_t val) {
|
|
||||||
ram[addr] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ignore all errors.
|
|
||||||
*/
|
|
||||||
void gb_error(struct gb_s* gb, const enum gb_error_e gb_err, const uint16_t addr) {
|
|
||||||
#if 1
|
|
||||||
const char* gb_err_str[4] = {
|
|
||||||
"UNKNOWN",
|
|
||||||
"INVALID OPCODE",
|
|
||||||
"INVALID READ",
|
|
||||||
"INVALID WRITE"};
|
|
||||||
Serial.printf("Error %d occurred: %s at %04X\n.\n", gb_err, gb_err_str[gb_err], addr);
|
|
||||||
// abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void startEmulator() {
|
void startEmulator() {
|
||||||
#if ENABLE_LCD
|
#if ENABLE_LCD
|
||||||
#if ENABLE_SDCARD
|
#if ENABLE_SDCARD
|
||||||
/* ROM File selector */
|
Serial.println("Starting ROM file selector ...");
|
||||||
lcd_init();
|
|
||||||
tft.fillScreen(TFT_BLACK);
|
|
||||||
rom_file_selector();
|
rom_file_selector();
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Initialise GB context. */
|
Serial.println("Init GB context ...");
|
||||||
memcpy(rom_bank0, rom, sizeof(rom_bank0));
|
auto ret = initGbContext();
|
||||||
auto ret = gb_init(&gb, &gb_rom_read, &gb_cart_ram_read,
|
|
||||||
&gb_cart_ram_write, &gb_error, NULL);
|
|
||||||
Serial.println("GB ");
|
|
||||||
|
|
||||||
if (ret != GB_INIT_NO_ERROR) {
|
if (ret != GB_INIT_NO_ERROR) {
|
||||||
Serial.printf("Error: %d\n", ret);
|
Serial.printf("Error: %d\n", ret);
|
||||||
reset();
|
reset();
|
||||||
@@ -139,32 +80,36 @@ void startEmulator() {
|
|||||||
gb_init_lcd(&gb, &lcd_draw_line);
|
gb_init_lcd(&gb, &lcd_draw_line);
|
||||||
|
|
||||||
/* Start Core1, which processes requests to the LCD. */
|
/* Start Core1, which processes requests to the LCD. */
|
||||||
Serial.println("CORE1 ");
|
Serial.println("Starting Core1 ...");
|
||||||
multicore_launch_core1(core1_init);
|
multicore_launch_core1(core1_init);
|
||||||
|
|
||||||
Serial.println("LCD ");
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ENABLE_SOUND
|
#if ENABLE_SOUND
|
||||||
// Initialize audio emulation
|
// Initialize audio emulation
|
||||||
|
Serial.println("Starting audio ...");
|
||||||
audio_init();
|
audio_init();
|
||||||
|
|
||||||
Serial.println("AUDIO ");
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ENABLE_SDCARD
|
#if ENABLE_SDCARD
|
||||||
/* Load Save File. */
|
//Serial.println("Load save file ...");
|
||||||
read_cart_ram_file(&gb);
|
//read_cart_ram_file(&gb);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Serial.print("\n> ");
|
Serial.print("\n> ");
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset(uint32_t sleepMs) {
|
||||||
Serial.println("\nEmulation Ended");
|
Serial.println("\nEmulation Ended");
|
||||||
|
|
||||||
|
sleep_ms(sleepMs);
|
||||||
|
|
||||||
/* stop lcd task running on core 1 */
|
/* stop lcd task running on core 1 */
|
||||||
multicore_reset_core1();
|
multicore_reset_core1();
|
||||||
watchdog_reboot(0,0,0);
|
watchdog_reboot(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void halt() {
|
||||||
|
while (true) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void overclock() {
|
void overclock() {
|
||||||
@@ -177,33 +122,10 @@ void overclock() {
|
|||||||
sleep_ms(2);
|
sleep_ms(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void initSound() {
|
||||||
overclock();
|
|
||||||
|
|
||||||
/* Initialise USB serial connection for debugging. */
|
|
||||||
Serial.begin(115200);
|
|
||||||
//while (!Serial) ;
|
|
||||||
//delay(2000);
|
|
||||||
|
|
||||||
#if ENABLE_SDCARD
|
|
||||||
time_init();
|
|
||||||
#endif
|
|
||||||
// sleep_ms(5000);
|
|
||||||
Serial.println("INIT: ");
|
|
||||||
|
|
||||||
/* Initialise joypad. */
|
|
||||||
initJoypad();
|
|
||||||
|
|
||||||
/* Set SPI clock to use high frequency. */
|
|
||||||
#if 0
|
|
||||||
clock_configure(clk_peri, 0,
|
|
||||||
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
|
|
||||||
125 * 1000 * 1000, 125 * 1000 * 1000);
|
|
||||||
spi_init(spi0, 30*1000*1000);
|
|
||||||
spi_set_format(spi0, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_SOUND
|
#if ENABLE_SOUND
|
||||||
|
Serial.println("Initialize Sound ...");
|
||||||
|
|
||||||
// Allocate memory for the stream buffer
|
// Allocate memory for the stream buffer
|
||||||
stream = (uint16_t*) malloc(AUDIO_BUFFER_SIZE_BYTES);
|
stream = (uint16_t*) malloc(AUDIO_BUFFER_SIZE_BYTES);
|
||||||
assert(stream != NULL);
|
assert(stream != NULL);
|
||||||
@@ -213,11 +135,37 @@ void setup() {
|
|||||||
i2s_config = i2s_get_default_config();
|
i2s_config = i2s_get_default_config();
|
||||||
i2s_config.sample_freq = AUDIO_SAMPLE_RATE;
|
i2s_config.sample_freq = AUDIO_SAMPLE_RATE;
|
||||||
i2s_config.dma_trans_count = AUDIO_SAMPLES;
|
i2s_config.dma_trans_count = AUDIO_SAMPLES;
|
||||||
i2s_config.data_pin = 26, // DIN
|
i2s_config.data_pin = I2S_DIN_PIN,
|
||||||
i2s_config.clock_pin_base = 27, // BCLK + LRC
|
i2s_config.clock_pin_base = I2S_BCLK_LRC_PIN_BASE,
|
||||||
i2s_volume(&i2s_config, 2);
|
i2s_volume(&i2s_config, 2);
|
||||||
i2s_init(&i2s_config);
|
i2s_init(&i2s_config);
|
||||||
|
|
||||||
|
Serial.println("Sound initialized");
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
#if ENABLE_LCD
|
||||||
|
lcd_init(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
overclock();
|
||||||
|
|
||||||
|
// Initialise USB serial connection for debugging.
|
||||||
|
Serial.begin(115200);
|
||||||
|
//while (!Serial) ;
|
||||||
|
//delay(2000);
|
||||||
|
|
||||||
|
#if ENABLE_SDCARD
|
||||||
|
init_sdcard();
|
||||||
|
#if ENABLE_USB_STORAGE_DEVICE
|
||||||
|
initUsbStorageDevice();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
initJoypad();
|
||||||
|
|
||||||
|
initSound();
|
||||||
|
|
||||||
startEmulator();
|
startEmulator();
|
||||||
}
|
}
|
||||||
@@ -248,6 +196,15 @@ void loop() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
handlePad();
|
handleJoypad();
|
||||||
handleSerial();
|
handleSerial();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void error(String message) {
|
||||||
|
tft.fillScreen(TFT_BLACK);
|
||||||
|
tft.setTextColor(TFT_RED);
|
||||||
|
tft.drawString(message, 0, ERROR_TEXT_OFFSET, FONT_ID);
|
||||||
|
Serial.printf("E %s\n", message.c_str());
|
||||||
|
Serial.flush();
|
||||||
|
reset(5000);
|
||||||
|
}
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
#if ENABLE_USB_STORAGE_DEVICE
|
||||||
|
|
||||||
|
#include <Adafruit_TinyUSB.h>
|
||||||
|
|
||||||
|
#include "card_loader.h"
|
||||||
|
|
||||||
|
static Adafruit_USBD_MSC usb_msc;
|
||||||
|
|
||||||
|
// Callback invoked when received READ10 command.
|
||||||
|
static int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
bool rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
|
||||||
|
return rc ? bufsize : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback invoked when received WRITE10 command.
|
||||||
|
static int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
bool rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
|
||||||
|
return rc ? bufsize : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
|
||||||
|
static void msc_flush_cb (void) {
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
|
||||||
|
sd.card()->syncDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUsbStorageDevice() {
|
||||||
|
usb_msc.setID("GB", "SD Card", "1.0");
|
||||||
|
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
|
||||||
|
usb_msc.setUnitReady(false);
|
||||||
|
usb_msc.begin();
|
||||||
|
|
||||||
|
if (TinyUSBDevice.mounted()) {
|
||||||
|
TinyUSBDevice.detach();
|
||||||
|
delay(10);
|
||||||
|
TinyUSBDevice.attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scope = UseSDPinFunctionScope();
|
||||||
|
uint32_t block_count = sd.card()->sectorCount();
|
||||||
|
usb_msc.setCapacity(block_count, 512);
|
||||||
|
usb_msc.setUnitReady(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -62,11 +62,11 @@
|
|||||||
#define TFT_D5 5
|
#define TFT_D5 5
|
||||||
#define TFT_D6 6
|
#define TFT_D6 6
|
||||||
#define TFT_D7 7
|
#define TFT_D7 7
|
||||||
#define TFT_WR 19 // Write strobe for modified Raspberry Pi TFT only
|
#define TFT_WR 18 // Write strobe for modified Raspberry Pi TFT only
|
||||||
#define TFT_DC 20 // Data Command control pin
|
#define TFT_DC 19 // Data Command control pin
|
||||||
//#define TFT_CS 21 // Chip select control pin D8
|
#define TFT_CS 22 // Chip select control pin D8
|
||||||
#define TFT_RST 22 // Reset pin (could connect to NodeMCU RST, see next line)
|
//#define TFT_RST 22 // Reset pin (could connect to NodeMCU RST, see next line)
|
||||||
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
|
#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
|
||||||
|
|
||||||
// ##################################################################################
|
// ##################################################################################
|
||||||
//
|
//
|
||||||
@@ -79,8 +79,8 @@
|
|||||||
// normally necessary. If all fonts are loaded the extra FLASH space required is
|
// normally necessary. If all fonts are loaded the extra FLASH space required is
|
||||||
// about 17Kbytes. To save FLASH space only enable the fonts you need!
|
// about 17Kbytes. To save FLASH space only enable the fonts you need!
|
||||||
|
|
||||||
#if 0
|
|
||||||
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
|
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
|
||||||
|
#if 0
|
||||||
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
|
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
|
||||||
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
|
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
|
||||||
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
|
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
|
||||||
|
|||||||
Reference in New Issue
Block a user