├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── custom.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── stale.yml │ └── sync_issues.yml ├── CONTRIBUTING.md ├── src ├── Seeed_Arduino_FS.h ├── SDMMC │ ├── Seeed_SDMMC_Interface.h │ ├── Seeed_SDMMC.h │ ├── Seeed_SDMMC.cpp │ └── Seeed_SDMMC_Interface.cpp ├── SD │ ├── Seeed_sdcard_hal.h │ ├── Seeed_SD.h │ ├── Seeed_SD.cpp │ ├── sd_diskio_crc.c │ └── Seeed_sdcard_hal.cpp ├── fatfs │ ├── integer.h │ ├── diskio.c │ ├── diskio.h │ ├── ffconf.h │ ├── ff.h │ └── ccsbcs.c ├── Seeed_FS.h └── Seeed_FS.cpp ├── library.properties ├── .travis.yml ├── keywords.txt ├── LICENSE ├── examples ├── ReadWrite │ └── ReadWrite.ino ├── listfiles │ └── listfiles.ino └── SD_Test │ └── SD_Test.ino ├── CODE_OF_CONDUCT.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing guidelines 2 | 3 | All guidelines for contributing to the Seeed_Arduino_FS repository can be found at [`How to contribute guideline`](https://github.com/Seeed-Studio/Seeed_Arduino_FS/wiki/How_to_contribute). 4 | -------------------------------------------------------------------------------- /src/Seeed_Arduino_FS.h: -------------------------------------------------------------------------------- 1 | #ifdef ARDUINO_ARCH_NRF52840 2 | /* use for NRF52840(eg. XIAO BLE) */ 3 | #include 4 | 5 | #else 6 | 7 | #include "Seeed_FS.h" 8 | #include "SD/Seeed_SD.h" 9 | 10 | #ifdef WIO_LITE_AI 11 | #include "SDMMC/Seeed_SDMMC.h" 12 | #endif 13 | 14 | #endif -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Seeed Arduino FS 2 | version=2.1.3 3 | author=Hongtai.liu 4 | maintainer=Hongtai.liu 5 | sentence=A friendly library for file operation. 6 | paragraph=Gives an example to read/ write from/to an SD card. 7 | category=Data Storage 8 | url=https://github.com/Seeed-Studio/Seeed_Arduino_FS 9 | architectures=* 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | dist: bionic 3 | sudo: false 4 | cache: 5 | directories: 6 | - ~/arduino_ide 7 | - ~/.arduino15/packages/ 8 | 9 | before_install: 10 | - wget -c https://files.seeedstudio.com/arduino/seeed-arduino-simple-ci.sh 11 | 12 | script: 13 | - chmod +x seeed-arduino-simple-ci.sh 14 | - cat $PWD/seeed-arduino-simple-ci.sh 15 | - bash $PWD/seeed-arduino-simple-ci.sh -b "Seeeduino:samd:seeed_wio_terminal" -s listfiles/ReadWrite/SD_Test 16 | 17 | notifications: 18 | email: 19 | on_success: change 20 | on_failure: change -------------------------------------------------------------------------------- /src/SDMMC/Seeed_SDMMC_Interface.h: -------------------------------------------------------------------------------- 1 | #ifndef __SEEED_SDMMC_INTERFACE_H_ 2 | #define __SEEED_SDMMC_INTERFACE_H_ 3 | #ifdef WIO_LITE_AI 4 | 5 | #include 6 | #include 7 | uint8_t MX_SDMMC1_SD_Init(void); 8 | DSTATUS SD_initialize(BYTE pdrv); 9 | DSTATUS SD_status(BYTE pdrv); 10 | DRESULT SD_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count); 11 | DRESULT SD_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); 12 | DRESULT SD_ioctl(BYTE pdrv, BYTE cmd, void* buff); 13 | 14 | extern SD_HandleTypeDef hsd1; 15 | 16 | #endif 17 | #endif 18 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 4 * * *' 7 | 8 | jobs: 9 | stale: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Checkout script repository 17 | uses: actions/checkout@v4 18 | with: 19 | repository: Seeed-Studio/sync-github-all-issues 20 | path: ci 21 | 22 | - name: Run script 23 | run: ./ci/tools/stale.sh 24 | env: 25 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/sync_issues.yml: -------------------------------------------------------------------------------- 1 | name: Automate Issue Management 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - edited 8 | - assigned 9 | - unassigned 10 | - labeled 11 | - unlabeled 12 | - reopened 13 | 14 | jobs: 15 | add_issue_to_project: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Add issue to GitHub Project 19 | uses: actions/add-to-project@v1.0.2 20 | with: 21 | project-url: https://github.com/orgs/Seeed-Studio/projects/17 22 | github-token: ${{ secrets.ISSUE_ASSEMBLE }} 23 | labeled: bug 24 | label-operator: NOT -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map SD 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | SD KEYWORD1 SD 10 | File KEYWORD1 SD 11 | SDFile KEYWORD1 SD 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | begin KEYWORD2 17 | exists KEYWORD2 18 | mkdir KEYWORD2 19 | remove KEYWORD2 20 | rmdir KEYWORD2 21 | open KEYWORD2 22 | close KEYWORD2 23 | seek KEYWORD2 24 | position KEYWORD2 25 | size KEYWORD2 26 | 27 | ####################################### 28 | # Constants (LITERAL1) 29 | ####################################### 30 | FILE_READ LITERAL1 31 | FILE_WRITE LITERAL1 32 | -------------------------------------------------------------------------------- /src/SD/Seeed_sdcard_hal.h: -------------------------------------------------------------------------------- 1 | #ifndef SEEED_SDCARD_HAL_H 2 | #define SEEED_SDCARD_HAL_H 3 | 4 | 5 | #include "SPI.h" 6 | #include 7 | #include "Arduino.h" 8 | 9 | typedef enum { 10 | CARD_NONE, 11 | CARD_MMC, 12 | CARD_SD, 13 | CARD_SDHC, 14 | CARD_UNKNOWN 15 | } sdcard_type_t; 16 | 17 | typedef struct { 18 | uint8_t ssPin; 19 | SPIClass* spi; 20 | int frequency; 21 | sdcard_type_t type; 22 | unsigned long sectors; 23 | bool supports_crc; 24 | int status; 25 | } ardu_sdcard_t; 26 | 27 | uint8_t sdcard_init(uint8_t cs, SPIClass* spi, int hz); 28 | uint8_t sdcard_uninit(uint8_t pdrv); 29 | 30 | bool sdcard_mount(uint8_t pdrv); 31 | uint8_t sdcard_unmount(uint8_t pdrv); 32 | 33 | sdcard_type_t sdcard_type(uint8_t pdrv); 34 | uint32_t sdcard_num_sectors(uint8_t pdrv); 35 | uint32_t sdcard_sector_size(uint8_t pdrv); 36 | 37 | #endif -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/SDMMC/Seeed_SDMMC.h: -------------------------------------------------------------------------------- 1 | #ifndef __SEEED_SDMMC_H__ 2 | #define __SEEED_SDMMC_H__ 3 | 4 | #include 5 | 6 | #ifdef WIO_LITE_AI 7 | 8 | #include 9 | #include 10 | 11 | namespace fs { 12 | 13 | class SDMMCFS : public FS { 14 | private: 15 | uint8_t _pdrv; 16 | TCHAR _drv[2] = {_T('0' + _pdrv), _T(':')}; 17 | public: 18 | SDMMCFS() {} 19 | ~SDMMCFS() {} 20 | // This needs to be called to set up the connection to the SD card 21 | // before other methods are used. 22 | boolean begin(); 23 | 24 | //call this when a card is removed. It will allow you to insert and initialise a new card. 25 | void end(); 26 | 27 | uint8_t getPhysicalDriveNumber(); 28 | String getDriveLetter(); 29 | 30 | int cardType(); 31 | uint64_t cardSize(); 32 | uint64_t totalBytes(); 33 | uint64_t usedBytes(); 34 | 35 | }; 36 | extern SDMMCFS SDMMC; 37 | }; 38 | 39 | #endif 40 | 41 | #endif -------------------------------------------------------------------------------- /src/fatfs/integer.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------*/ 2 | /* Integer type definitions for FatFs module */ 3 | /*-------------------------------------------*/ 4 | 5 | #ifndef _FF_INTEGER 6 | #define _FF_INTEGER 7 | 8 | #ifdef _WIN32 /* FatFs development platform */ 9 | 10 | #include 11 | #include 12 | typedef unsigned __int64 QWORD; 13 | 14 | 15 | #else /* Embedded platform */ 16 | 17 | /* These types MUST be 16-bit or 32-bit */ 18 | typedef int INT; 19 | typedef unsigned int UINT; 20 | 21 | /* This type MUST be 8-bit */ 22 | typedef unsigned char BYTE; 23 | 24 | /* These types MUST be 16-bit */ 25 | typedef short SHORT; 26 | typedef unsigned short WORD; 27 | typedef unsigned short WCHAR; 28 | 29 | /* These types MUST be 32-bit */ 30 | typedef long LONG; 31 | typedef unsigned long DWORD; 32 | 33 | /* This type MUST be 64-bit (Remove this for C89 compatibility) */ 34 | typedef unsigned long long QWORD; 35 | 36 | #endif 37 | 38 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Seeed Studio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/SD/Seeed_SD.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SD - a slightly more friendly wrapper for sdfatlib 4 | 5 | This library aims to expose a subset of SD card functionality 6 | in the form of a higher level "wrapper" object. 7 | 8 | License: GNU General Public License V3 9 | (Because sdfatlib is licensed with this.) 10 | 11 | (C) Copyright 2010 SparkFun Electronics 12 | 13 | */ 14 | 15 | #ifndef __SD_H__ 16 | #define __SD_H__ 17 | 18 | #include 19 | #include "Seeed_sdcard_hal.h" 20 | #include 21 | #include 22 | 23 | 24 | 25 | namespace fs { 26 | 27 | class SDFS : public FS { 28 | public: 29 | SDFS() {} 30 | ~SDFS() {} 31 | // This needs to be called to set up the connection to the SD card 32 | // before other methods are used. 33 | boolean begin(uint8_t ssPin = 11, SPIClass& spi = SPI, int hz = 4000000); 34 | 35 | //call this when a card is removed. It will allow you to insert and initialise a new card. 36 | void end(); 37 | 38 | uint8_t getPhysicalDriveNumber(); 39 | String getDriveLetter(); 40 | 41 | sdcard_type_t cardType(); 42 | uint64_t cardSize(); 43 | uint64_t totalBytes(); 44 | uint64_t usedBytes(); 45 | }; 46 | 47 | extern SDFS SD; 48 | }; 49 | 50 | using namespace fs; 51 | typedef fs::File SDFile; 52 | typedef fs::SDFS SDFileSystemClass; 53 | #define SDFileSystem SD 54 | 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /examples/ReadWrite/ReadWrite.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SD card read/write 3 | 4 | This example shows how to read and write data to and from an SD card file 5 | The circuit: 6 | SD card attached to SPI bus or SDMMC 7 | 8 | created Nov 2010 9 | by David A. Mellis 10 | modified 9 Apr 2012 11 | by Tom Igoe 12 | modified 18 June 2021 13 | by Hongtai.liu 14 | 15 | This example code is in the public domain. 16 | 17 | */ 18 | #include 19 | 20 | 21 | #ifdef WIO_LITE_AI 22 | #define DEV SDMMC 23 | #else 24 | #define DEV SD 25 | #ifdef _SAMD21_ 26 | #define SDCARD_SS_PIN 1 27 | #define SDCARD_SPI SPI 28 | #endif 29 | #endif 30 | 31 | 32 | #define LOG Serial 33 | 34 | void setup() { 35 | LOG.begin(115200); 36 | pinMode(5, OUTPUT); 37 | digitalWrite(5, HIGH); 38 | while (!LOG) {}; 39 | #ifdef WIO_LITE_AI 40 | while (!DEV.begin()) { 41 | LOG.println("Card Mount Failed"); 42 | return; 43 | } 44 | #else 45 | while (!DEV.begin(SDCARD_SS_PIN,SDCARD_SPI,4000000UL)) { 46 | LOG.println("Card Mount Failed"); 47 | return; 48 | } 49 | #endif 50 | LOG.println("initialization done."); 51 | 52 | // open the file. note that only one file can be open at a time, 53 | // so you have to close this one before opening another. 54 | 55 | File RootWrite = DEV.open("/hello.txt", "w"); 56 | // File RootWrite = DEV.open("/hello.txt", FILE_WRITE); 57 | 58 | // if the file opened okay, write to it: 59 | if (RootWrite) { 60 | LOG.print("Writing to hello.txt..."); 61 | RootWrite.println("hello 1, 2, 3."); 62 | // close the file: 63 | RootWrite.close(); 64 | LOG.println("done."); 65 | } else { 66 | // if the file didn't open, print an error: 67 | LOG.println("error opening hello.txt"); 68 | } 69 | 70 | // re-open the file for reading: 71 | File RootRead= DEV.open("/hello.txt"); 72 | if (RootRead) { 73 | LOG.println("hello.txt:"); 74 | 75 | // read from the file until there's nothing else in it: 76 | while (RootRead.available()) { 77 | LOG.write(RootRead.read()); 78 | } 79 | // close the file: 80 | RootRead.close(); 81 | } else { 82 | // if the file didn't open, print an error: 83 | LOG.println("error opening hello.txt"); 84 | } 85 | } 86 | 87 | void loop() { 88 | // nothing happens after setup 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/fatfs/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control modules to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | #ifndef ARDUINO_ARCH_NRF52840 10 | #include "ff.h" /* Obtains integer types */ 11 | #include "diskio.h" /* Declarations of disk functions */ 12 | #include 13 | #include 14 | #include 15 | #include "integer.h" 16 | 17 | static ff_diskio_impl_t * s_impls[_VOLUMES] = { NULL }; 18 | 19 | #if _MULTI_PARTITION /* Multiple partition configuration */ 20 | PARTITION VolToPart[] = { 21 | {0, 0}, /* Logical drive 0 ==> Physical drive 0, auto detection */ 22 | {1, 0} /* Logical drive 1 ==> Physical drive 1, auto detection */ 23 | }; 24 | #endif 25 | 26 | 27 | uint8_t ff_diskio_get_drive(BYTE* out_pdrv) 28 | { 29 | BYTE i; 30 | for(i=0; i<_VOLUMES; i++) { 31 | if (!s_impls[i]) { 32 | *out_pdrv = i; 33 | return 0; 34 | } 35 | } 36 | return 1; 37 | } 38 | 39 | void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl) 40 | { 41 | assert(pdrv < _VOLUMES); 42 | 43 | if (s_impls[pdrv]) { 44 | ff_diskio_impl_t* im = s_impls[pdrv]; 45 | s_impls[pdrv] = NULL; 46 | free(im); 47 | } 48 | 49 | if (!discio_impl) { 50 | return; 51 | } 52 | 53 | ff_diskio_impl_t * impl = (ff_diskio_impl_t *)malloc(sizeof(ff_diskio_impl_t)); 54 | assert(impl != NULL); 55 | memcpy(impl, discio_impl, sizeof(ff_diskio_impl_t)); 56 | s_impls[pdrv] = impl; 57 | } 58 | 59 | DSTATUS disk_initialize (BYTE pdrv) 60 | { 61 | return s_impls[pdrv]->init(pdrv); 62 | } 63 | DSTATUS disk_status (BYTE pdrv) 64 | { 65 | return s_impls[pdrv]->status(pdrv); 66 | } 67 | DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) 68 | { 69 | return s_impls[pdrv]->read(pdrv, buff, sector, count); 70 | } 71 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) 72 | { 73 | return s_impls[pdrv]->write(pdrv, buff, sector, count); 74 | } 75 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff) 76 | { 77 | return s_impls[pdrv]->ioctl(pdrv, cmd, buff); 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /src/SD/Seeed_SD.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | a wrapper for SDCard. 3 | 4 | Created by Hongtai.liu 13 July 2019 5 | 6 | */ 7 | 8 | #include "Seeed_SD.h" 9 | #include "Seeed_sdcard_hal.h" 10 | #include 11 | 12 | namespace fs 13 | { 14 | 15 | boolean SDFS::begin(uint8_t ssPin, SPIClass &spi, int hz) 16 | { 17 | _pdrv = sdcard_init(ssPin, &spi, hz); 18 | spi.begin(); 19 | FRESULT status; 20 | _drv[0] = _T('0' + _pdrv); 21 | status = f_mount(&root, _drv, 1); 22 | if (status != FR_OK) 23 | { 24 | return false; 25 | } 26 | else 27 | { 28 | return true; 29 | } 30 | } 31 | 32 | void SDFS::end() 33 | { 34 | if (_pdrv != 0xFF) 35 | { 36 | f_mount(NULL, _drv, 1); 37 | sdcard_uninit(_pdrv); 38 | _pdrv = 0xFF; 39 | } 40 | } 41 | 42 | uint8_t SDFS::getPhysicalDriveNumber() 43 | { 44 | return _pdrv; 45 | } 46 | 47 | String SDFS::getDriveLetter() 48 | { 49 | if (_pdrv == 0xFF) { 50 | // Card not initialized 51 | return ""; 52 | } 53 | return String(_pdrv) + ":"; 54 | } 55 | 56 | sdcard_type_t SDFS::cardType() 57 | { 58 | if (_pdrv == 0xFF) 59 | { 60 | return CARD_NONE; 61 | } 62 | return sdcard_type(_pdrv); 63 | } 64 | 65 | uint64_t SDFS::cardSize() 66 | { 67 | if (_pdrv == 0xFF) 68 | { 69 | return 0; 70 | } 71 | size_t sectors = sdcard_num_sectors(_pdrv); 72 | size_t sectorSize = sdcard_sector_size(_pdrv); 73 | return (uint64_t)sectors * sectorSize; 74 | } 75 | 76 | uint64_t SDFS::totalBytes() 77 | { 78 | FATFS *fsinfo; 79 | DWORD fre_clust; 80 | if (f_getfree(_drv, &fre_clust, &fsinfo) != 0) 81 | { 82 | return 0; 83 | } 84 | uint64_t size = ((uint64_t)(fsinfo->csize)) * (fsinfo->n_fatent - 2) 85 | #if _MAX_SS != 512 86 | * (fsinfo->ssize); 87 | #else 88 | * 512; 89 | #endif 90 | return size; 91 | } 92 | 93 | uint64_t SDFS::usedBytes() 94 | { 95 | FATFS *fsinfo; 96 | DWORD fre_clust; 97 | if (f_getfree(_drv, &fre_clust, &fsinfo) != 0) 98 | { 99 | return 0; 100 | } 101 | uint64_t size = ((uint64_t)(fsinfo->csize)) * ((fsinfo->n_fatent - 2) - (fsinfo->free_clst)) 102 | #if _MAX_SS != 512 103 | * (fsinfo->ssize); 104 | #else 105 | * 512; 106 | #endif 107 | return size; 108 | } 109 | 110 | SDFS SD; 111 | }; 112 | -------------------------------------------------------------------------------- /src/SDMMC/Seeed_SDMMC.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WIO_LITE_AI 2 | #include "Seeed_SDMMC.h" 3 | #include "Seeed_SDMMC_Interface.h" 4 | #include 5 | #include "Arduino.h" 6 | 7 | namespace fs{ 8 | 9 | boolean SDMMCFS::begin() 10 | { 11 | _pdrv = 0xff; 12 | if (ff_diskio_get_drive(&_pdrv) != 0 || _pdrv == 0xFF) { 13 | return false; 14 | } 15 | static const ff_diskio_impl_t flash_impl = { 16 | .init = &SD_initialize, 17 | .status = &SD_status, 18 | .read = &SD_read, 19 | .write = &SD_write, 20 | .ioctl = &SD_ioctl 21 | }; 22 | if(MX_SDMMC1_SD_Init() != HAL_OK) 23 | { 24 | return false; 25 | } 26 | ff_diskio_register(_pdrv, &flash_impl); 27 | 28 | FRESULT status; 29 | _drv[0] = _T('0' + _pdrv); 30 | status = f_mount(&root, _drv, 0); 31 | if (status == FR_OK) { 32 | return true; 33 | } else { 34 | return false; 35 | } 36 | return true; 37 | } 38 | 39 | void SDMMCFS::end() 40 | { 41 | if (_pdrv != 0xFF) { 42 | f_mount(NULL, _drv, 1); 43 | HAL_SD_MspDeInit(&hsd1); 44 | _pdrv = 0xFF; 45 | } 46 | } 47 | 48 | uint8_t SDMMCFS::getPhysicalDriveNumber() { 49 | return _pdrv; 50 | } 51 | 52 | String SDMMCFS::getDriveLetter() { 53 | if (_pdrv == 0xFF) { 54 | // Card not initialized 55 | return ""; 56 | } 57 | return String(_pdrv) + ":"; 58 | } 59 | 60 | 61 | int SDMMCFS::cardType() 62 | { 63 | if (_pdrv == 0xFF) { 64 | return CARD_NONE; 65 | } 66 | return (hsd1.SdCard.CardType + 1); 67 | } 68 | 69 | uint64_t SDMMCFS::cardSize() 70 | { 71 | if (_pdrv == 0xFF) { 72 | return 0; 73 | } 74 | return (uint64_t)((uint64_t)hsd1.SdCard.BlockNbr * (uint64_t)hsd1.SdCard.BlockSize); 75 | } 76 | 77 | uint64_t SDMMCFS::totalBytes() 78 | { 79 | FATFS* fsinfo; 80 | DWORD fre_clust; 81 | if (f_getfree(_drv, &fre_clust, &fsinfo) != 0) { 82 | return 0; 83 | } 84 | uint64_t size = ((uint64_t)(fsinfo->csize)) * (fsinfo->n_fatent - 2) 85 | #if _MAX_SS != 512 86 | * (fsinfo->ssize); 87 | #else 88 | * 512; 89 | #endif 90 | return size; 91 | } 92 | 93 | uint64_t SDMMCFS::usedBytes() 94 | { 95 | FATFS* fsinfo; 96 | DWORD fre_clust; 97 | if (f_getfree(_drv, &fre_clust, &fsinfo) != 0) { 98 | return 0; 99 | } 100 | uint64_t size = ((uint64_t)(fsinfo->csize)) * ((uint64_t)(fsinfo->n_fatent - 2) - (uint64_t)(fsinfo->free_clst)) 101 | #if _MAX_SS != 512 102 | * (fsinfo->ssize); 103 | #else 104 | * 512; 105 | #endif 106 | return size; 107 | } 108 | SDMMCFS SDMMC; 109 | } 110 | #endif -------------------------------------------------------------------------------- /examples/listfiles/listfiles.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Listfiles 3 | 4 | This example shows how print out the files in a 5 | directory on a SD card 6 | 7 | The circuit: 8 | SD card attached to SPI bus or SDMMC 9 | 10 | 11 | created Nov 2010 12 | by David A. Mellis 13 | modified 9 Apr 2012 14 | by Tom Igoe 15 | modified 2 Feb 2014 16 | by Scott Fitzgerald 17 | modified 18 July 2021 18 | by Hongtai.liu 19 | 20 | This example code is in the public domain. 21 | 22 | */ 23 | #include 24 | 25 | 26 | #ifdef WIO_LITE_AI 27 | #define DEV SDMMC 28 | #else 29 | #define DEV SD 30 | #ifdef _SAMD21_ 31 | #define SDCARD_SS_PIN 1 32 | #define SDCARD_SPI SPI 33 | #endif 34 | #endif 35 | 36 | 37 | #define LOG Serial 38 | 39 | 40 | void setup() { 41 | // Open LOG communications and wait for port to open: 42 | pinMode(LED_BUILTIN, OUTPUT); 43 | LOG.begin(115200); 44 | while (!LOG) { 45 | ; // wait for LOG port to connect. Needed for native USB port only 46 | } 47 | 48 | LOG.println("Initializing SD card..."); 49 | 50 | #ifdef WIO_LITE_AI 51 | if (!DEV.begin()) { 52 | LOG.println("initialization failed!"); 53 | } 54 | #else 55 | if (!DEV.begin(SDCARD_SS_PIN,SDCARD_SPI,4000000UL)) { 56 | LOG.println("initialization failed!"); 57 | } 58 | #endif 59 | LOG.println("initialization done."); 60 | 61 | File root = DEV.open("/"); 62 | printDirectory(root, 0); 63 | 64 | LOG.println(" - - - - - - - - - - - - - - - - - - - - - - - - -"); 65 | listDir(DEV,"/",0); 66 | 67 | LOG.println("done!"); 68 | root.close(); 69 | } 70 | 71 | void loop() { 72 | // nothing happens after setup finishes. 73 | digitalWrite(LED_BUILTIN, LOW); 74 | delay(50); 75 | digitalWrite(LED_BUILTIN, HIGH); 76 | delay(500); 77 | } 78 | 79 | void printDirectory(File dir, int numTabs) { 80 | while (true) { 81 | 82 | File entry = dir.openNextFile(); 83 | if (! entry) { 84 | // no more files 85 | break; 86 | } 87 | for (uint8_t i = 0; i < numTabs; i++) { 88 | LOG.print('\t'); 89 | } 90 | LOG.print(entry.name()); 91 | if (entry.isDirectory()) { 92 | LOG.println("/"); 93 | printDirectory(entry, numTabs + 1); 94 | } else { 95 | // files have sizes, directories do not 96 | LOG.print("\t\t"); 97 | LOG.print(entry.size(), DEC); 98 | LOG.println(" bytes"); 99 | } 100 | entry.close(); 101 | } 102 | } 103 | 104 | void listDir( fs::FS& fs, const char* dirname, int numTabs ) 105 | { 106 | File dir= fs.open(dirname); 107 | 108 | while (true) { 109 | 110 | File entry = dir.openNextFile(); 111 | if (! entry) { 112 | // no more files 113 | break; 114 | } 115 | for (uint8_t i = 0; i < numTabs; i++) { 116 | LOG.print('\t'); 117 | } 118 | LOG.print(entry.name()); 119 | if (entry.isDirectory()) { 120 | LOG.println("/"); 121 | listDir(fs,entry.name(), numTabs + 1); 122 | } else { 123 | // files have sizes, directories do not 124 | LOG.print("\t\t"); 125 | LOG.print(entry.size(), DEC); 126 | LOG.println(" bytes"); 127 | } 128 | entry.close(); 129 | } 130 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at zuobaozhu@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /src/fatfs/diskio.h: -------------------------------------------------------------------------------- 1 | /* -----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2014 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | #include "Arduino.h" 12 | #include "integer.h" 13 | 14 | 15 | /* Status of Disk Functions */ 16 | typedef BYTE DSTATUS; 17 | 18 | /* Results of Disk Functions */ 19 | typedef enum { 20 | RES_OK = 0, /* 0: Successful */ 21 | RES_ERROR, /* 1: R/W Error */ 22 | RES_WRPRT, /* 2: Write Protected */ 23 | RES_NOTRDY, /* 3: Not Ready */ 24 | RES_PARERR /* 4: Invalid Parameter */ 25 | } DRESULT; 26 | 27 | 28 | /*---------------------------------------*/ 29 | /* Prototypes for disk control functions */ 30 | 31 | 32 | DSTATUS disk_initialize(BYTE pdrv); 33 | DSTATUS disk_status(BYTE pdrv); 34 | DRESULT disk_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count); 35 | DRESULT disk_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); 36 | DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void* buff); 37 | 38 | /** 39 | Structure of pointers to disk IO driver functions. 40 | 41 | See FatFs documentation for details about these functions 42 | */ 43 | typedef struct { 44 | DSTATUS(*init)(BYTE pdrv); /*!< disk initialization function */ 45 | DSTATUS(*status)(BYTE pdrv); /*!< disk status check function */ 46 | DRESULT(*read)(BYTE pdrv, BYTE* buff, DWORD sector, UINT count); /*!< sector read function */ 47 | DRESULT(*write)(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); /*!< sector write function */ 48 | DRESULT(*ioctl)(BYTE pdrv, BYTE cmd, void* buff); /*!< function to get info about disk and do some misc operations */ 49 | } ff_diskio_impl_t; 50 | 51 | /** 52 | Register or unregister diskio driver for given drive number. 53 | 54 | When FATFS library calls one of disk_xxx functions for driver number pdrv, 55 | corresponding function in discio_impl for given pdrv will be called. 56 | 57 | @param pdrv drive number 58 | @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions 59 | or NULL to unregister and free previously registered drive 60 | */ 61 | void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); 62 | 63 | 64 | /** 65 | Get next available drive number 66 | 67 | @param out_pdrv pointer to the byte to set if successful 68 | 69 | @return true on success 70 | false if all drives are attached 71 | */ 72 | uint8_t ff_diskio_get_drive(BYTE* out_pdrv); 73 | 74 | 75 | 76 | /* Disk Status Bits (DSTATUS) */ 77 | 78 | #define STA_NOINIT 0x01 /* Drive not initialized */ 79 | #define STA_NODISK 0x02 /* No medium in the drive */ 80 | #define STA_PROTECT 0x04 /* Write protected */ 81 | 82 | 83 | /* Command code for disk_ioctrl fucntion */ 84 | 85 | /* Generic command (Used by FatFs) */ 86 | #define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ 87 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ 88 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ 89 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ 90 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ 91 | 92 | /* Generic command (Not used by FatFs) */ 93 | #define CTRL_POWER 5 /* Get/Set power status */ 94 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 95 | #define CTRL_EJECT 7 /* Eject media */ 96 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 97 | 98 | /* MMC/SDC specific ioctl command */ 99 | #define MMC_GET_TYPE 10 /* Get card type */ 100 | #define MMC_GET_CSD 11 /* Get CSD */ 101 | #define MMC_GET_CID 12 /* Get CID */ 102 | #define MMC_GET_OCR 13 /* Get OCR */ 103 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 104 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 105 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 106 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 107 | 108 | /* ATA/CF specific ioctl command */ 109 | #define ATA_GET_REV 20 /* Get F/W revision */ 110 | #define ATA_GET_MODEL 21 /* Get model name */ 111 | #define ATA_GET_SN 22 /* Get serial number */ 112 | 113 | #ifdef __cplusplus 114 | } 115 | #endif 116 | 117 | #endif -------------------------------------------------------------------------------- /src/SD/sd_diskio_crc.c: -------------------------------------------------------------------------------- 1 | /* SD/MMC File System Library 2 | * Copyright (c) 2014 Neil Thiessen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const char m_CRC7Table[] = { 18 | 0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 19 | 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77, 20 | 0x19, 0x10, 0x0B, 0x02, 0x3D, 0x34, 0x2F, 0x26, 21 | 0x51, 0x58, 0x43, 0x4A, 0x75, 0x7C, 0x67, 0x6E, 22 | 0x32, 0x3B, 0x20, 0x29, 0x16, 0x1F, 0x04, 0x0D, 23 | 0x7A, 0x73, 0x68, 0x61, 0x5E, 0x57, 0x4C, 0x45, 24 | 0x2B, 0x22, 0x39, 0x30, 0x0F, 0x06, 0x1D, 0x14, 25 | 0x63, 0x6A, 0x71, 0x78, 0x47, 0x4E, 0x55, 0x5C, 26 | 0x64, 0x6D, 0x76, 0x7F, 0x40, 0x49, 0x52, 0x5B, 27 | 0x2C, 0x25, 0x3E, 0x37, 0x08, 0x01, 0x1A, 0x13, 28 | 0x7D, 0x74, 0x6F, 0x66, 0x59, 0x50, 0x4B, 0x42, 29 | 0x35, 0x3C, 0x27, 0x2E, 0x11, 0x18, 0x03, 0x0A, 30 | 0x56, 0x5F, 0x44, 0x4D, 0x72, 0x7B, 0x60, 0x69, 31 | 0x1E, 0x17, 0x0C, 0x05, 0x3A, 0x33, 0x28, 0x21, 32 | 0x4F, 0x46, 0x5D, 0x54, 0x6B, 0x62, 0x79, 0x70, 33 | 0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31, 0x38, 34 | 0x41, 0x48, 0x53, 0x5A, 0x65, 0x6C, 0x77, 0x7E, 35 | 0x09, 0x00, 0x1B, 0x12, 0x2D, 0x24, 0x3F, 0x36, 36 | 0x58, 0x51, 0x4A, 0x43, 0x7C, 0x75, 0x6E, 0x67, 37 | 0x10, 0x19, 0x02, 0x0B, 0x34, 0x3D, 0x26, 0x2F, 38 | 0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C, 39 | 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, 40 | 0x6A, 0x63, 0x78, 0x71, 0x4E, 0x47, 0x5C, 0x55, 41 | 0x22, 0x2B, 0x30, 0x39, 0x06, 0x0F, 0x14, 0x1D, 42 | 0x25, 0x2C, 0x37, 0x3E, 0x01, 0x08, 0x13, 0x1A, 43 | 0x6D, 0x64, 0x7F, 0x76, 0x49, 0x40, 0x5B, 0x52, 44 | 0x3C, 0x35, 0x2E, 0x27, 0x18, 0x11, 0x0A, 0x03, 45 | 0x74, 0x7D, 0x66, 0x6F, 0x50, 0x59, 0x42, 0x4B, 46 | 0x17, 0x1E, 0x05, 0x0C, 0x33, 0x3A, 0x21, 0x28, 47 | 0x5F, 0x56, 0x4D, 0x44, 0x7B, 0x72, 0x69, 0x60, 48 | 0x0E, 0x07, 0x1C, 0x15, 0x2A, 0x23, 0x38, 0x31, 49 | 0x46, 0x4F, 0x54, 0x5D, 0x62, 0x6B, 0x70, 0x79 50 | }; 51 | 52 | char CRC7(const char* data, int length) 53 | { 54 | char crc = 0; 55 | for (int i = 0; i < length; i++) { 56 | crc = m_CRC7Table[(crc << 1) ^ data[i]]; 57 | } 58 | return crc; 59 | } 60 | 61 | const unsigned short m_CRC16Table[256] = { 62 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 63 | 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 64 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 65 | 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 66 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 67 | 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 68 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 69 | 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 70 | 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 71 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 72 | 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 73 | 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 74 | 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 75 | 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 76 | 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 77 | 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 78 | 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 79 | 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 80 | 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 81 | 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 82 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 83 | 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 84 | 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 85 | 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 86 | 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 87 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 88 | 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 89 | 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 90 | 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 91 | 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 92 | 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 93 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 94 | }; 95 | 96 | unsigned short CRC16(const char* data, int length) 97 | { 98 | unsigned short crc = 0; 99 | for (int i = 0; i < length; i++) { 100 | crc = (crc << 8) ^ m_CRC16Table[((crc >> 8) ^ data[i]) & 0x00FF]; 101 | } 102 | return crc; 103 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Seeed-Arduino-FS [![Build Status](https://travis-ci.com/Seeed-Studio/Seeed_Arduino_FS.svg?branch=master)](https://travis-ci.com/Seeed-Studio/Seeed_Arduino_FS) 2 | 3 | ## Introduction 4 | 5 | A lightweight port of FatFs for Arduino. This library contains a routine to drive an SD card via SPI.The file system part is generic, which means you can easily port it to other types of memory, such as QSPI flash, emmc, etc. 6 | 7 | An example is included in [Seeed_Arduino_SFUD](https://github.com/Seeed-Studio/Seeed_Arduino_SFUD) 8 | 9 | ## Usage 10 | 11 | **this code has been tested at wio terminal.** 12 | 13 | ```c++ 14 | 15 | #include 16 | 17 | #define LOG Serial 18 | #define DEV SD 19 | 20 | #ifdef _SAMD21_ 21 | #define SDCARD_SS_PIN 1 22 | #define SDCARD_SPI SPI 23 | #endif 24 | 25 | 26 | void setup() { 27 | LOG.begin(115200); 28 | pinMode(5, OUTPUT); 29 | digitalWrite(5, HIGH); 30 | while (!LOG) {}; 31 | while (!DEV.begin(SDCARD_SS_PIN,SDCARD_SPI,4000000UL)) { 32 | LOG.println("Card Mount Failed"); 33 | return; 34 | } 35 | 36 | LOG.println("initialization done."); 37 | 38 | // open the file. note that only one file can be open at a time, 39 | // so you have to close this one before opening another. 40 | 41 | File RootWrite = DEV.open("/hello.txt", "w"); 42 | // File RootWrite = DEV.open("/hello.txt", FILE_WRITE); 43 | 44 | // if the file opened okay, write to it: 45 | if (RootWrite) { 46 | LOG.print("Writing to hello.txt..."); 47 | RootWrite.println("hello 1, 2, 3."); 48 | // close the file: 49 | RootWrite.close(); 50 | LOG.println("done."); 51 | } else { 52 | // if the file didn't open, print an error: 53 | LOG.println("error opening hello.txt"); 54 | } 55 | 56 | // re-open the file for reading: 57 | File RootRead= DEV.open("/hello.txt"); 58 | if (RootRead) { 59 | LOG.println("hello.txt:"); 60 | 61 | // read from the file until there's nothing else in it: 62 | while (RootRead.available()) { 63 | LOG.write(RootRead.read()); 64 | } 65 | // close the file: 66 | RootRead.close(); 67 | } else { 68 | // if the file didn't open, print an error: 69 | LOG.println("error opening hello.txt"); 70 | } 71 | } 72 | 73 | void loop() { 74 | // nothing happens after setup 75 | } 76 | 77 | ``` 78 | 79 | ## API Reference 80 | 81 | - boolean begin(uint8_t ssPin, SPIClass& sp, int hz) : config the SPI to control storage device 82 | 83 | ```c++ 84 | DEV.begin(SDCARD_SS_PIN,SDCARD_SPI,4000000UL) 85 | // DEV.begin(104000000UL) //use qspi flash 86 | ``` 87 | 88 | - sdcard_type_t cardType() : get SD card type 89 | 90 | **Note** : only work with SD card 91 | 92 | ```c++ 93 | uint8_t cardType = DEV.cardType(); 94 | if (cardType == CARD_NONE) { 95 | SERIAL.println("No SD card attached"); 96 | return; 97 | } 98 | ``` 99 | 100 | - sfud_type_t flashType() : get flash type 101 | 102 | **Note** : only work with flash 103 | 104 | ```c++ 105 | uint8_t flashType = DEV.flashType(); 106 | if (flashType == FLASH_NONE) { 107 | SERIAL.println("No flash attached"); 108 | return; 109 | } 110 | ``` 111 | 112 | - uint64_t cardSize(): get SD card size 113 | 114 | **Note** : only work with SD card 115 | 116 | ```c++ 117 | uint64_t cardSize = DEV.cardSize() / (1024 * 1024); 118 | SERIAL.print("SD Card Size: "); 119 | SERIAL.print((uint32_t)cardSize); 120 | SERIAL.println("MB"); 121 | ``` 122 | 123 | - uint64_t flashSize() : get flash size 124 | 125 | **Note** : only work with flash 126 | 127 | ```c++ 128 | uint32_t flashSize = DEV.flashSize() / (1024 * 1024); 129 | SERIAL.print("flash Size: "); 130 | SERIAL.print((uint32_t)flashSize); 131 | SERIAL.println("MB"); 132 | ``` 133 | 134 | - uint64_t totalBytes(): return total Bytes of storage device 135 | 136 | ```c++ 137 | uint32_t totalBytes = DEV.totalBytes(); 138 | SERIAL.print("Total space: "); 139 | SERIAL.print(totalBytes / (1024 * 1024)); 140 | SERIAL.println("MB"); 141 | ``` 142 | 143 | - uint64_t usedBytes(): return used Bytes of storage device 144 | 145 | ```c++ 146 | uint32_t usedBytes = DEV.usedBytes(); 147 | SERIAL.print("Used space: "); 148 | SERIAL.print(usedBytes / (1024 * 1024)); 149 | SERIAL.println("MB"); 150 | ``` 151 | 152 | ---- 153 | 154 | This software is written by seeed studio
155 | and is licensed under [The MIT License](http://opensource.org/licenses/mit-license.php). Check License.txt for more information.
156 | 157 | Contributing to this software is warmly welcomed. You can do this basically by
158 | [forking](https://help.github.com/articles/fork-a-repo), committing modifications and then [pulling requests](https://help.github.com/articles/using-pull-requests) (follow the links above
159 | for operating guide). Adding change log and your contact into file header is encouraged.
160 | Thanks for your contribution. 161 | 162 | Seeed Studio is an open hardware facilitation company based in Shenzhen, China.
163 | Benefiting from local manufacture power and convenient global logistic system,
164 | we integrate resources to serve new era of innovation. Seeed also works with
165 | global distributors and partners to push open hardware movement.
166 | 167 | 168 | [![Analytics](https://ga-beacon.appspot.com/UA-46589105-3/Grove_LED_Bar)](https://github.com/igrigorik/ga-beacon) 169 | 170 | -------------------------------------------------------------------------------- /examples/SD_Test/SD_Test.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Connect the SD card to the following pins: 3 | 4 | SD card attached to SPI or SDMMC 5 | 6 | modified 18 June 2021 7 | by Hongtai.liu 8 | 9 | */ 10 | 11 | #include 12 | 13 | 14 | #ifdef WIO_LITE_AI 15 | #define DEV SDMMC 16 | #else 17 | #define DEV SD 18 | #ifdef _SAMD21_ 19 | #define SDCARD_SS_PIN 1 20 | #define SDCARD_SPI SPI 21 | #endif 22 | #endif 23 | 24 | 25 | #define LOG Serial 26 | 27 | void listDir( fs::FS& fs, const char* dirname, int numTabs ) 28 | { 29 | File dir= fs.open(dirname); 30 | 31 | while (true) { 32 | 33 | File entry = dir.openNextFile(); 34 | if (! entry) { 35 | // no more files 36 | break; 37 | } 38 | for (uint8_t i = 0; i < numTabs; i++) { 39 | LOG.print('\t'); 40 | } 41 | LOG.print(entry.name()); 42 | if (entry.isDirectory()) { 43 | LOG.println("/"); 44 | listDir(fs,entry.name(), numTabs + 1); 45 | } else { 46 | // files have sizes, directories do not 47 | LOG.print("\t\t"); 48 | LOG.print(entry.size(), DEC); 49 | LOG.println(" bytes"); 50 | } 51 | entry.close(); 52 | } 53 | } 54 | 55 | void createDir(fs::FS& fs, const char* path) { 56 | LOG.print("Creating Dir: "); 57 | LOG.println(path); 58 | if (fs.mkdir(path)) { 59 | LOG.println("Dir created"); 60 | } else { 61 | LOG.println("mkdir failed"); 62 | } 63 | } 64 | 65 | void removeDir(fs::FS& fs, const char* path) { 66 | LOG.print("Removing Dir: "); 67 | LOG.println(path); 68 | if (fs.rmdir(path)) { 69 | LOG.println("Dir removed"); 70 | } else { 71 | LOG.println("rmdir failed"); 72 | } 73 | } 74 | 75 | void readFile(fs::FS& fs, const char* path) { 76 | LOG.print("Reading Dir: "); 77 | LOG.println(path); 78 | File file = fs.open(path); 79 | if (!file) { 80 | LOG.println("Failed to open file for reading"); 81 | return; 82 | } 83 | 84 | LOG.print("Read from file: "); 85 | while (file.available()) { 86 | LOG.write(file.read()); 87 | } 88 | file.close(); 89 | } 90 | 91 | void writeFile(fs::FS& fs, const char* path, const char* message) { 92 | LOG.print("Writing file: "); 93 | LOG.println(path); 94 | File file = fs.open(path, FILE_WRITE); 95 | if (!file) { 96 | LOG.println("Failed to open file for writing"); 97 | return; 98 | } 99 | 100 | if (file.print(message)) { 101 | LOG.println("File written"); 102 | } else { 103 | LOG.println("Write failed"); 104 | } 105 | 106 | file.close(); 107 | 108 | } 109 | 110 | void appendFile(fs::FS& fs, const char* path, const char* message) { 111 | LOG.print("Appending to file: "); 112 | LOG.println(path); 113 | 114 | File file = fs.open(path, FILE_APPEND); 115 | if (!file) { 116 | LOG.println("Failed to open file for appending"); 117 | return; 118 | } 119 | if (file.print(message)) { 120 | LOG.println("Message appended"); 121 | } else { 122 | LOG.println("Append failed"); 123 | } 124 | file.close(); 125 | } 126 | 127 | void renameFile(fs::FS& fs, const char* path1, const char* path2) { 128 | LOG.print("Renaming file "); 129 | LOG.print(path1); 130 | LOG.print(" to "); 131 | LOG.println(path2); 132 | if (fs.rename(path1, path2)) { 133 | LOG.println("File renamed"); 134 | } else { 135 | LOG.println("Rename failed"); 136 | } 137 | } 138 | 139 | void deleteFile(fs::FS& fs, const char* path) { 140 | LOG.print("Deleting file: "); 141 | LOG.println(path); 142 | if (fs.remove(path)) { 143 | LOG.println("File deleted"); 144 | } else { 145 | LOG.println("Delete failed"); 146 | } 147 | } 148 | 149 | void testFileIO(fs::FS& fs, const char* path) { 150 | File file = fs.open(path); 151 | static uint8_t buf[512]; 152 | size_t len = 0; 153 | uint32_t start = micros(); 154 | uint32_t end = start; 155 | if (file) { 156 | len = file.size(); 157 | size_t flen = len; 158 | start = micros(); 159 | while (len) { 160 | size_t toRead = len; 161 | if (toRead > 512) { 162 | toRead = 512; 163 | } 164 | file.read(buf, toRead); 165 | len -= toRead; 166 | } 167 | end = micros() - start; 168 | LOG.print(flen); 169 | LOG.print(" bytes read for "); 170 | LOG.print(end); 171 | LOG.println(" ns"); 172 | file.close(); 173 | } else { 174 | LOG.println("Failed to open file for reading"); 175 | } 176 | } 177 | 178 | void setup() { 179 | LOG.begin(115200); 180 | pinMode(5, OUTPUT); 181 | digitalWrite(5, HIGH); 182 | while (!LOG) {}; 183 | 184 | delay(1000); 185 | 186 | #ifdef WIO_LITE_AI 187 | while (!DEV.begin()) { 188 | LOG.println("Card Mount Failed"); 189 | return; 190 | } 191 | #else 192 | while (!DEV.begin(SDCARD_SS_PIN,SDCARD_SPI,4000000UL)) { 193 | LOG.println("Card Mount Failed"); 194 | return; 195 | } 196 | #endif 197 | 198 | 199 | uint8_t cardType = DEV.cardType(); 200 | if (cardType == CARD_NONE) { 201 | LOG.println("No SD card attached"); 202 | return; 203 | } 204 | 205 | uint32_t cardSize = DEV.cardSize() / (1024 * 1024); 206 | LOG.print("SD Card Size: "); 207 | LOG.print((uint32_t)cardSize); 208 | LOG.println("MB"); 209 | 210 | 211 | listDir(DEV, "/", 0); 212 | createDir(DEV, "/mydir"); 213 | listDir(DEV, "/", 0); 214 | removeDir(DEV, "/mydir"); 215 | listDir(DEV, "/", 2); 216 | writeFile(DEV, "/hello.txt", "Hello "); 217 | appendFile(DEV, "/hello.txt", "World!\n"); 218 | readFile(DEV, "/hello.txt"); 219 | deleteFile(DEV, "/foo.txt"); 220 | renameFile(DEV, "/hello.txt", "/foo.txt"); 221 | readFile(DEV, "/foo.txt"); 222 | testFileIO(DEV, "/foo.txt"); 223 | uint32_t totalBytes = DEV.totalBytes(); 224 | LOG.print("Total space: "); 225 | LOG.print(totalBytes / (1024 * 1024)); 226 | LOG.println("MB"); 227 | uint32_t usedBytes = DEV.usedBytes(); 228 | LOG.print("Used space: "); 229 | uint32_t _usedBytes = usedBytes / (1024 * 1024); 230 | LOG.print(_usedBytes); 231 | LOG.println("MB"); 232 | } 233 | 234 | void loop() { 235 | 236 | } -------------------------------------------------------------------------------- /src/Seeed_FS.h: -------------------------------------------------------------------------------- 1 | #ifndef __SEEED_FS__ 2 | #define __SEEED_FS__ 3 | 4 | /* 5 | ________________________________________________________________________________________________________________________________________ 6 | | Flags | Meaning | 7 | |----------------------------------------------------------------------------------------------------------------------------------------| 8 | | FA_READ | Specifies read access to the object. Data can be read from the file. | 9 | | FA_WRITE | Specifies write access to the object. Data can be written to the file. Combine with FA_READ for read-write access. | 10 | | FA_OPEN_EXISTING | Opens the file. The function fails if the file is not existing. (Default) | 11 | | FA_CREATE_NEW | Creates a new file. The function fails with FR_EXIST if the file is existing. | 12 | | FA_CREATE_ALWAYS | Creates a new file. If the file is existing, it will be truncated and overwritten. | 13 | | FA_OPEN_ALWAYS | Opens the file if it is existing. If not, a new file will be created. | 14 | | FA_OPEN_APPEND | Same as FA_OPEN_ALWAYS except the read/write pointer is set end of the file. | 15 | |__________________|_____________________________________________________________________________________________________________________| 16 | */ 17 | 18 | /* 19 | _____________________________________________________ 20 | | POSIX | FatFs | 21 | |-------------|---------------------------------------| 22 | |"r" |FA_READ | 23 | |"r+" |FA_READ | FA_WRITE | 24 | |"w" |FA_CREATE_ALWAYS | FA_WRITE | 25 | |"w+" |FA_CREATE_ALWAYS | FA_WRITE | FA_READ | 26 | |"a" |FA_OPEN_APPEND | FA_WRITE | 27 | |"a+" |FA_OPEN_APPEND | FA_WRITE | FA_READ | 28 | |"wx" |FA_CREATE_NEW | FA_WRITE | 29 | |"w+x" |FA_CREATE_NEW | FA_WRITE | FA_READ | 30 | |_____________|_______________________________________| 31 | */ 32 | 33 | #define FILE_READ FA_READ 34 | #define FILE_WRITE (FA_CREATE_ALWAYS | FA_WRITE | FA_READ) 35 | #define FILE_APPEND (FA_OPEN_APPEND | FA_WRITE) 36 | 37 | extern "C" 38 | { 39 | #include "./fatfs/diskio.h" 40 | #include "./fatfs/ffconf.h" 41 | #include "./fatfs/ff.h" 42 | char CRC7(const char *data, int length); 43 | unsigned short CRC16(const char *data, int length); 44 | } 45 | 46 | namespace fs 47 | { 48 | 49 | enum SeekMode 50 | { 51 | SeekSet = 0, 52 | SeekCur = 1, 53 | SeekEnd = 2 54 | }; 55 | 56 | class File : public Stream 57 | { 58 | private: 59 | char _name[_MAX_LFN + 2]; // file name 60 | FIL *_file; // underlying file pointer 61 | DIR *_dir; // if open a dir 62 | FILINFO *_fno; // for traverse directory 63 | 64 | public: 65 | File(FIL f, const char *name); // wraps an underlying SdFile 66 | File(DIR d, const char *name); 67 | File(void); // 'empty' constructor 68 | ~File(); 69 | virtual size_t write(uint8_t); 70 | virtual size_t write(const uint8_t *buf, size_t size); 71 | virtual int read(); 72 | virtual int peek(); 73 | virtual int available(); 74 | virtual void flush(); 75 | size_t read(void *buf, uint32_t nbyte); 76 | char *gets(char *str, uint32_t nbyte); 77 | bool seek(uint32_t pos); 78 | bool seek(uint32_t pos, SeekMode mode); 79 | uint32_t position(); 80 | uint32_t tell(); 81 | uint32_t size(); 82 | void close(); 83 | operator bool() const; 84 | char *name(); 85 | 86 | boolean isDirectory(void); 87 | File openNextFile(uint8_t mode = FA_READ); 88 | void rewindDirectory(void); 89 | 90 | File &operator=(const File &f); 91 | 92 | using Print::write; 93 | }; 94 | class FS 95 | { 96 | protected: 97 | FATFS root; 98 | uint8_t _pdrv; 99 | TCHAR _drv[2] = {_T(char('0' + _pdrv)), _T(':')}; 100 | 101 | public: 102 | static TCHAR _path[_MAX_LFN + 2]; 103 | // Open the specified file/directory with the supplied mode (e.g. read or 104 | // write, etc). Returns a File object for interacting with the file. 105 | // Note that currently only one file can be open at a time. 106 | File open(const char *filepath, uint8_t mode = FILE_READ); 107 | File open(const String &filepath, uint8_t mode = FILE_READ) 108 | { 109 | return open(filepath.c_str(), mode); 110 | } 111 | File open(const char *filepath, const char *mode); 112 | File open(const String &filepath, const char *mode) 113 | { 114 | return open(filepath.c_str(), mode); 115 | } 116 | 117 | // Methods to determine if the requested file path exists. 118 | boolean exists(const char *filepath); 119 | boolean exists(const String &filepath) 120 | { 121 | return exists(filepath.c_str()); 122 | } 123 | 124 | // Create the requested directory heirarchy--if intermediate directories 125 | // do not exist they will be created. 126 | boolean mkdir(const char *filepath); 127 | boolean mkdir(const String &filepath) 128 | { 129 | return mkdir(filepath.c_str()); 130 | } 131 | 132 | boolean rename(const char *pathFrom, const char *pathTo); 133 | boolean rename(const String &pathFrom, const String &pathTo) 134 | { 135 | return rename(pathFrom.c_str(), pathTo.c_str()); 136 | }; 137 | 138 | // Delete the file. 139 | boolean remove(const char *filepath); 140 | boolean remove(const String &filepath) 141 | { 142 | return remove(filepath.c_str()); 143 | } 144 | 145 | boolean rmdir(const char *filepath); 146 | boolean rmdir(const String &filepath) 147 | { 148 | return rmdir(filepath.c_str()); 149 | } 150 | }; 151 | }; 152 | using namespace fs; 153 | 154 | #endif -------------------------------------------------------------------------------- /src/fatfs/ffconf.h: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------/ 2 | / FatFs - FAT file system module configuration file 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #define _FFCONF 68020 /* Revision ID */ 6 | 7 | /* ---------------------------------------------------------------------------/ 8 | / Function Configurations 9 | /---------------------------------------------------------------------------*/ 10 | 11 | #define _FS_READONLY 0 12 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 13 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 14 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 15 | / and optional writing functions as well. */ 16 | 17 | 18 | #define _FS_MINIMIZE 0 19 | /* This option defines minimization level to remove some basic API functions. 20 | / 21 | / 0: All basic functions are enabled. 22 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 23 | / are removed. 24 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 25 | / 3: f_lseek() function is removed in addition to 2. */ 26 | 27 | 28 | #define _USE_STRFUNC 2 29 | /* This option switches string functions, f_gets(), f_putc(), f_puts() and 30 | / f_printf(). 31 | / 32 | / 0: Disable string functions. 33 | / 1: Enable without LF-CRLF conversion. 34 | / 2: Enable with LF-CRLF conversion. */ 35 | 36 | 37 | #define _USE_FIND 2 38 | /* This option switches filtered directory read functions, f_findfirst() and 39 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 40 | 41 | 42 | #define _USE_MKFS 1 43 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 44 | 45 | 46 | #define _USE_FASTSEEK 1 47 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 48 | 49 | 50 | #define _USE_EXPAND 1 51 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 52 | 53 | 54 | #define _USE_CHMOD 1 55 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 56 | / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ 57 | 58 | 59 | #define _USE_LABEL 0 60 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 61 | / (0:Disable or 1:Enable) */ 62 | 63 | 64 | #define _USE_FORWARD 1 65 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 66 | 67 | 68 | /* ---------------------------------------------------------------------------/ 69 | / Locale and Namespace Configurations 70 | /---------------------------------------------------------------------------*/ 71 | 72 | #define _CODE_PAGE 437 73 | /* This option specifies the OEM code page to be used on the target system. 74 | / Incorrect setting of the code page can cause a file open failure. 75 | / 76 | / 1 - ASCII (No extended character. Non-LFN cfg. only) 77 | / 437 - U.S. 78 | / 720 - Arabic 79 | / 737 - Greek 80 | / 771 - KBL 81 | / 775 - Baltic 82 | / 850 - Latin 1 83 | / 852 - Latin 2 84 | / 855 - Cyrillic 85 | / 857 - Turkish 86 | / 860 - Portuguese 87 | / 861 - Icelandic 88 | / 862 - Hebrew 89 | / 863 - Canadian French 90 | / 864 - Arabic 91 | / 865 - Nordic 92 | / 866 - Russian 93 | / 869 - Greek 2 94 | / 932 - Japanese (DBCS) 95 | / 936 - Simplified Chinese (DBCS) 96 | / 949 - Korean (DBCS) 97 | / 950 - Traditional Chinese (DBCS) 98 | */ 99 | 100 | 101 | #define _USE_LFN 2 102 | #define _MAX_LFN 255 103 | /* The _USE_LFN switches the support of long file name (LFN). 104 | / 105 | / 0: Disable support of LFN. _MAX_LFN has no effect. 106 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 107 | / 2: Enable LFN with dynamic working buffer on the STACK. 108 | / 3: Enable LFN with dynamic working buffer on the HEAP. 109 | / 110 | / To enable the LFN, Unicode handling functions (option/unicode.c) must be added 111 | / to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and 112 | / additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. 113 | / It should be set 255 to support full featured LFN operations. 114 | / When use stack for the working buffer, take care on stack overflow. When use heap 115 | / memory for the working buffer, memory management functions, ff_memalloc() and 116 | / ff_memfree(), must be added to the project. */ 117 | 118 | 119 | #define _LFN_UNICODE 0 120 | /* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) 121 | / To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. 122 | / This option also affects behavior of string I/O functions. */ 123 | 124 | 125 | #define _STRF_ENCODE 0 126 | /* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to 127 | / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). 128 | / 129 | / 0: ANSI/OEM 130 | / 1: UTF-16LE 131 | / 2: UTF-16BE 132 | / 3: UTF-8 133 | / 134 | / This option has no effect when _LFN_UNICODE == 0. */ 135 | 136 | 137 | #define _FS_RPATH 1 138 | /* This option configures support of relative path. 139 | / 140 | / 0: Disable relative path and remove related functions. 141 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 142 | / 2: f_getcwd() function is available in addition to 1. 143 | */ 144 | 145 | 146 | /* ---------------------------------------------------------------------------/ 147 | / Drive/Volume Configurations 148 | /---------------------------------------------------------------------------*/ 149 | 150 | #define _VOLUMES 2 151 | /* Number of volumes (logical drives) to be used. */ 152 | 153 | 154 | #define _STR_VOLUME_ID 0 155 | #define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" 156 | /* _STR_VOLUME_ID switches string support of volume ID. 157 | / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive 158 | / number in the path name. _VOLUME_STRS defines the drive ID strings for each 159 | / logical drives. Number of items must be equal to _VOLUMES. Valid characters for 160 | / the drive ID strings are: A-Z and 0-9. */ 161 | 162 | 163 | #define _MULTI_PARTITION 0 164 | /* This option switches support of multi-partition on a physical drive. 165 | / By default (0), each logical drive number is bound to the same physical drive 166 | / number and only an FAT volume found on the physical drive will be mounted. 167 | / When multi-partition is enabled (1), each logical drive number can be bound to 168 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 169 | / funciton will be available. */ 170 | 171 | 172 | #define _MIN_SS 512 173 | #define _MAX_SS 4096 174 | /* These options configure the range of sector size to be supported. (512, 1024, 175 | / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and 176 | / harddisk. But a larger value may be required for on-board flash memory and some 177 | / type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured 178 | / to variable sector size and GET_SECTOR_SIZE command must be implemented to the 179 | / disk_ioctl() function. */ 180 | 181 | 182 | #define _USE_TRIM 0 183 | /* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) 184 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 185 | / disk_ioctl() function. */ 186 | 187 | 188 | #define _FS_NOFSINFO 0 189 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 190 | / option, and f_getfree() function at first time after volume mount will force 191 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 192 | / 193 | / bit0=0: Use free cluster count in the FSINFO if available. 194 | / bit0=1: Do not trust free cluster count in the FSINFO. 195 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 196 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 197 | */ 198 | 199 | 200 | 201 | /* ---------------------------------------------------------------------------/ 202 | / System Configurations 203 | /---------------------------------------------------------------------------*/ 204 | 205 | #define _FS_TINY 0 206 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 207 | / At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. 208 | / Instead of private sector buffer eliminated from the file object, common sector 209 | / buffer in the file system object (FATFS) is used for the file data transfer. */ 210 | 211 | 212 | #define _FS_EXFAT 1 213 | /* This option switches support of exFAT file system. (0:Disable or 1:Enable) 214 | / When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) 215 | / Note that enabling exFAT discards C89 compatibility. */ 216 | 217 | 218 | #define _FS_NORTC 1 219 | #define _NORTC_MON 1 220 | #define _NORTC_MDAY 1 221 | #define _NORTC_YEAR 2018 222 | /* The option _FS_NORTC switches timestamp functiton. If the system does not have 223 | / any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable 224 | / the timestamp function. All objects modified by FatFs will have a fixed timestamp 225 | / defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. 226 | / To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be 227 | / added to the project to get current time form real-time clock. _NORTC_MON, 228 | / _NORTC_MDAY and _NORTC_YEAR have no effect. 229 | / These options have no effect at read-only configuration (_FS_READONLY = 1). */ 230 | 231 | 232 | #define _FS_LOCK 0 233 | /* The option _FS_LOCK switches file lock function to control duplicated file open 234 | / and illegal operation to open objects. This option must be 0 when _FS_READONLY 235 | / is 1. 236 | / 237 | / 0: Disable file lock function. To avoid volume corruption, application program 238 | / should avoid illegal open, remove and rename to the open objects. 239 | / >0: Enable file lock function. The value defines how many files/sub-directories 240 | / can be opened simultaneously under file lock control. Note that the file 241 | / lock control is independent of re-entrancy. */ 242 | 243 | 244 | #define _FS_REENTRANT 0 245 | #define _FS_TIMEOUT 1000 246 | #define _SYNC_t HANDLE 247 | /* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 248 | / module itself. Note that regardless of this option, file access to different 249 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 250 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 251 | / to the same volume is under control of this function. 252 | / 253 | / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. 254 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 255 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 256 | / function, must be added to the project. Samples are available in 257 | / option/syscall.c. 258 | / 259 | / The _FS_TIMEOUT defines timeout period in unit of time tick. 260 | / The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, 261 | / SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be 262 | / included somewhere in the scope of ff.h. */ 263 | 264 | /* #include // O/S definitions */ 265 | 266 | 267 | /*--- End of configuration options ---*/ -------------------------------------------------------------------------------- /src/SDMMC/Seeed_SDMMC_Interface.cpp: -------------------------------------------------------------------------------- 1 | #ifdef WIO_LITE_AI 2 | 3 | #include "Seeed_SDMMC_Interface.h" 4 | #include "stm32h7xx_hal.h" 5 | #include "../fatfs/diskio.h" 6 | #include "../fatfs/ffconf.h" 7 | #include "../fatfs/ff.h" 8 | 9 | /* SD status structure definition */ 10 | #define MSD_OK ((uint8_t)0) 11 | #define MSD_ERROR ((uint8_t)1) 12 | #define MSD_ERROR_SD_NOT_PRESENT ((uint8_t)2) 13 | 14 | /* Common Error codes */ 15 | #define BSP_ERROR_NONE 0 16 | #define BSP_ERROR_NO_INIT -1 17 | #define BSP_ERROR_WRONG_PARAM -2 18 | #define BSP_ERROR_BUSY -3 19 | #define BSP_ERROR_PERIPH_FAILURE -4 20 | #define BSP_ERROR_COMPONENT_FAILURE -5 21 | #define BSP_ERROR_UNKNOWN_FAILURE -6 22 | #define BSP_ERROR_UNKNOWN_COMPONENT -7 23 | #define BSP_ERROR_BUS_FAILURE -8 24 | #define BSP_ERROR_CLOCK_FAILURE -9 25 | #define BSP_ERROR_MSP_FAILURE -10 26 | #define BSP_ERROR_FEATURE_NOT_SUPPORTED -11 27 | 28 | /* BSP OSPI error codes */ 29 | #define BSP_ERROR_OSPI_SUSPENDED -20 30 | #define BSP_ERROR_OSPI_ASSIGN_FAILURE -24 31 | #define BSP_ERROR_OSPI_SETUP_FAILURE -25 32 | #define BSP_ERROR_OSPI_MMP_LOCK_FAILURE -26 33 | #define BSP_ERROR_OSPI_MMP_UNLOCK_FAILURE -27 34 | 35 | /* BSP TS error code */ 36 | #define BSP_ERROR_TS_TOUCH_NOT_DETECTED -30 37 | 38 | /* BSP BUS error codes */ 39 | #define BSP_ERROR_BUS_TRANSACTION_FAILURE -100 40 | #define BSP_ERROR_BUS_ARBITRATION_LOSS -101 41 | #define BSP_ERROR_BUS_ACKNOWLEDGE_FAILURE -102 42 | #define BSP_ERROR_BUS_PROTOCOL_FAILURE -103 43 | 44 | #define BSP_ERROR_BUS_MODE_FAULT -104 45 | #define BSP_ERROR_BUS_FRAME_ERROR -105 46 | #define BSP_ERROR_BUS_CRC_ERROR -106 47 | #define BSP_ERROR_BUS_DMA_FAILURE -107 48 | 49 | #define SD_INSTANCES_NBR 2UL 50 | 51 | 52 | #define SD_TIMEOUT 3 * 1000 53 | #define SD_DEFAULT_BLOCK_SIZE 512 54 | 55 | #ifndef SD_WRITE_TIMEOUT 56 | #define SD_WRITE_TIMEOUT 100U 57 | #endif 58 | 59 | #ifndef SD_READ_TIMEOUT 60 | #define SD_READ_TIMEOUT 100U 61 | #endif 62 | 63 | /** 64 | * @brief SD transfer state definition 65 | */ 66 | #define SD_TRANSFER_OK 0U 67 | #define SD_TRANSFER_BUSY 1U 68 | 69 | /** 70 | * @brief SD-detect signal 71 | */ 72 | #define SD_PRESENT 1U 73 | #define SD_NOT_PRESENT 0U 74 | 75 | #define SECTORSIZE 4096 76 | #define ENABLE_SD_DMA_CACHE_MAINTENANCE 1 77 | #define ENABLE_SCRATCH_BUFFER 1 78 | 79 | #if defined(ENABLE_SCRATCH_BUFFER) 80 | #if defined (ENABLE_SD_DMA_CACHE_MAINTENANCE) 81 | ALIGN_32BYTES(static uint8_t scratch[BLOCKSIZE]); // 32-Byte aligned for cache maintenance 82 | #else 83 | __ALIGN_BEGIN static uint8_t scratch[BLOCKSIZE] __ALIGN_END; 84 | #endif 85 | #endif 86 | 87 | 88 | SD_HandleTypeDef hsd1; 89 | static volatile DSTATUS SD_State = STA_NOINIT; 90 | static volatile UINT ReadStatus = 0; 91 | static volatile UINT WriteStatus = 0; 92 | 93 | static void SD_ClockConfig(void) 94 | { 95 | 96 | } 97 | 98 | 99 | uint8_t MX_SDMMC1_SD_Init(void) 100 | { 101 | 102 | /* USER CODE BEGIN SDMMC1_Init 0 */ 103 | 104 | /* USER CODE END SDMMC1_Init 0 */ 105 | 106 | /* USER CODE BEGIN SDMMC1_Init 1 */ 107 | 108 | /* USER CODE END SDMMC1_Init 1 */ 109 | hsd1.Instance = SDMMC1; 110 | hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; 111 | hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; 112 | hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B; 113 | hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; 114 | hsd1.Init.ClockDiv = 5; 115 | 116 | if (HAL_SD_Init(&hsd1) != HAL_OK) 117 | { 118 | return HAL_ERROR; 119 | } 120 | /* USER CODE BEGIN SDMMC1_Init 2 */ 121 | 122 | /* USER CODE END SDMMC1_Init 2 */ 123 | return HAL_OK; 124 | } 125 | 126 | void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle) 127 | { 128 | RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; 129 | GPIO_InitTypeDef GPIO_InitStruct = {0}; 130 | if(sdHandle->Instance==SDMMC1) 131 | { 132 | /* USER CODE BEGIN SDMMC1_MspInit 0 */ 133 | 134 | /* USER CODE END SDMMC1_MspInit 0 */ 135 | PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; 136 | PeriphClkInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; 137 | if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) 138 | { 139 | Error_Handler(); 140 | } 141 | /* SDMMC1 clock enable */ 142 | __HAL_RCC_SDMMC1_CLK_ENABLE(); 143 | 144 | __HAL_RCC_GPIOC_CLK_ENABLE(); 145 | __HAL_RCC_GPIOD_CLK_ENABLE(); 146 | /**SDMMC1 GPIO Configuration 147 | PC12 ------> SDMMC1_CK 148 | PC10 ------> SDMMC1_D2 149 | PC11 ------> SDMMC1_D3 150 | PD2 ------> SDMMC1_CMD 151 | PC9 ------> SDMMC1_D1 152 | PC8 ------> SDMMC1_D0 153 | */ 154 | GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_9 155 | |GPIO_PIN_8; 156 | GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 157 | GPIO_InitStruct.Pull = GPIO_NOPULL; 158 | GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 159 | GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1; 160 | HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); 161 | 162 | GPIO_InitStruct.Pin = GPIO_PIN_2; 163 | GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 164 | GPIO_InitStruct.Pull = GPIO_NOPULL; 165 | GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 166 | GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1; 167 | HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); 168 | 169 | /* USER CODE BEGIN SDMMC1_MspInit 1 */ 170 | 171 | /* USER CODE END SDMMC1_MspInit 1 */ 172 | } 173 | } 174 | 175 | void HAL_SD_MspDeInit(SD_HandleTypeDef* sdHandle) 176 | { 177 | 178 | if(sdHandle->Instance==SDMMC1) 179 | { 180 | /* USER CODE BEGIN SDMMC1_MspDeInit 0 */ 181 | 182 | /* USER CODE END SDMMC1_MspDeInit 0 */ 183 | /* Peripheral clock disable */ 184 | __HAL_RCC_SDMMC1_CLK_DISABLE(); 185 | 186 | /**SDMMC1 GPIO Configuration 187 | PC12 ------> SDMMC1_CK 188 | PC10 ------> SDMMC1_D2 189 | PC11 ------> SDMMC1_D3 190 | PD2 ------> SDMMC1_CMD 191 | PC9 ------> SDMMC1_D1 192 | PC8 ------> SDMMC1_D0 193 | */ 194 | HAL_GPIO_DeInit(GPIOC, GPIO_PIN_12|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_9 195 | |GPIO_PIN_8); 196 | 197 | HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2); 198 | 199 | /* USER CODE BEGIN SDMMC1_MspDeInit 1 */ 200 | 201 | /* USER CODE END SDMMC1_MspDeInit 1 */ 202 | } 203 | } 204 | 205 | static int32_t BSP_SD_GetCardState(uint32_t Instance) 206 | { 207 | return (int32_t)((HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_TRANSFER ) ? SD_TRANSFER_OK : SD_TRANSFER_BUSY); 208 | } 209 | 210 | static DSTATUS SD_CheckStatus(BYTE lun) 211 | { 212 | SD_State = STA_NOINIT; 213 | 214 | if(BSP_SD_GetCardState(0) == BSP_ERROR_NONE) 215 | { 216 | SD_State &= ~STA_NOINIT; 217 | } 218 | return SD_State; 219 | } 220 | 221 | static int SD_CheckStatusWithTimeout(uint32_t timeout) 222 | { 223 | uint32_t timer = HAL_GetTick(); 224 | /* block until SDIO IP is ready again or a timeout occur */ 225 | while(HAL_GetTick() - timer < timeout) 226 | { 227 | if (BSP_SD_GetCardState(0) == SD_TRANSFER_OK) 228 | { 229 | return 0; 230 | } 231 | } 232 | 233 | return -1; 234 | } 235 | 236 | int32_t BSP_SD_Erase(uint32_t Instance, uint32_t BlockIdx, uint32_t BlocksNbr) 237 | { 238 | int32_t ret; 239 | 240 | if(Instance >= SD_INSTANCES_NBR) 241 | { 242 | ret = BSP_ERROR_WRONG_PARAM; 243 | } 244 | else if(HAL_SD_Erase(&hsd1, BlockIdx, BlockIdx + BlocksNbr) != HAL_OK) 245 | { 246 | ret = BSP_ERROR_PERIPH_FAILURE; 247 | } 248 | else 249 | { 250 | ret = BSP_ERROR_NONE; 251 | } 252 | /* Return BSP status */ 253 | return ret; 254 | } 255 | 256 | int32_t BSP_SD_ReadBlocks_DMA(uint32_t Instance, uint32_t *pData, uint32_t BlockIdx, uint32_t BlocksNbr) 257 | { 258 | int32_t ret; 259 | 260 | if(Instance >= SD_INSTANCES_NBR) 261 | { 262 | ret = BSP_ERROR_WRONG_PARAM; 263 | } 264 | else if(HAL_SD_ReadBlocks_DMA(&hsd1, (uint8_t *)pData, BlockIdx, BlocksNbr) != HAL_OK) 265 | { 266 | ret = BSP_ERROR_PERIPH_FAILURE; 267 | } 268 | else 269 | { 270 | ret = BSP_ERROR_NONE; 271 | } 272 | /* Return BSP status */ 273 | return ret; 274 | } 275 | 276 | int32_t BSP_SD_WriteBlocks_DMA(uint32_t Instance, uint32_t *pData, uint32_t BlockIdx, uint32_t BlocksNbr) 277 | { 278 | int32_t ret; 279 | 280 | if(Instance >= SD_INSTANCES_NBR) 281 | { 282 | ret = BSP_ERROR_WRONG_PARAM; 283 | } 284 | else if(HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t *)pData, BlockIdx, BlocksNbr) != HAL_OK) 285 | { 286 | ret = BSP_ERROR_PERIPH_FAILURE; 287 | } 288 | else 289 | { 290 | ret = BSP_ERROR_NONE; 291 | } 292 | /* Return BSP status */ 293 | return ret; 294 | } 295 | 296 | int32_t BSP_SD_GetCardInfo(uint32_t Instance, HAL_SD_CardInfoTypeDef *CardInfo) 297 | { 298 | int32_t ret; 299 | 300 | if(Instance >= SD_INSTANCES_NBR) 301 | { 302 | ret = BSP_ERROR_WRONG_PARAM; 303 | } 304 | else if(HAL_SD_GetCardInfo(&hsd1, CardInfo) != HAL_OK) 305 | { 306 | ret = BSP_ERROR_PERIPH_FAILURE; 307 | } 308 | else 309 | { 310 | ret = BSP_ERROR_NONE; 311 | } 312 | /* Return BSP status */ 313 | return ret; 314 | } 315 | 316 | /** 317 | * @brief Rx Transfer completed callbacks 318 | * @param hsd: SD handle 319 | * @retval None 320 | */ 321 | void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd) 322 | { 323 | ReadStatus = 1; 324 | } 325 | 326 | /** 327 | * @brief Tx Transfer completed callbacks 328 | * @param hsd: SD handle 329 | * @retval None 330 | */ 331 | void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd) 332 | { 333 | WriteStatus = 1; 334 | } 335 | 336 | /******************************************************************************/ 337 | /* STM32H7xx Peripheral Interrupt Handlers */ 338 | /* Add here the Interrupt Handlers for the used peripherals. */ 339 | /* For the available peripheral interrupt handler names, */ 340 | /* please refer to the startup file (startup_stm32h7xx.s). */ 341 | /******************************************************************************/ 342 | 343 | /** 344 | * @brief This function handles SDMMC1 global interrupt. 345 | */ 346 | void SDMMC1_IRQHandler(void) 347 | { 348 | HAL_SD_IRQHandler(&hsd1); 349 | } 350 | 351 | __weak uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout) 352 | { 353 | uint8_t sd_state = MSD_OK; 354 | 355 | if (HAL_SD_ReadBlocks(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) != HAL_OK) 356 | { 357 | sd_state = MSD_ERROR; 358 | } 359 | 360 | return sd_state; 361 | } 362 | 363 | __weak uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout) 364 | { 365 | uint8_t sd_state = MSD_OK; 366 | 367 | if (HAL_SD_WriteBlocks(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) != HAL_OK) 368 | { 369 | sd_state = MSD_ERROR; 370 | } 371 | 372 | return sd_state; 373 | } 374 | 375 | /* Register function */ 376 | 377 | DSTATUS SD_initialize(BYTE pdrv) 378 | { 379 | 380 | //SD_ClockConfig(); 381 | 382 | // if(MX_SDMMC1_SD_Init()== BSP_ERROR_NONE) 383 | // { 384 | SD_State = SD_CheckStatus(pdrv); 385 | //} 386 | return SD_State; 387 | } 388 | 389 | DSTATUS SD_status(BYTE pdrv) 390 | { 391 | return SD_CheckStatus(pdrv); 392 | } 393 | 394 | DRESULT SD_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) 395 | { 396 | DRESULT res = RES_ERROR; 397 | 398 | if(BSP_SD_ReadBlocks((uint32_t*)buff, 399 | (uint32_t) (sector), 400 | count, SD_TIMEOUT) == MSD_OK) 401 | { 402 | /* wait until the read operation is finished */ 403 | while(BSP_SD_GetCardState(0)!= MSD_OK) 404 | { 405 | } 406 | res = RES_OK; 407 | } 408 | return res; 409 | } 410 | 411 | DRESULT SD_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) 412 | { 413 | DRESULT res = RES_ERROR; 414 | 415 | if(BSP_SD_WriteBlocks((uint32_t*)buff, 416 | (uint32_t)(sector), 417 | count, SD_TIMEOUT) == 0) 418 | { 419 | /* wait until the Write operation is finished */ 420 | while(BSP_SD_GetCardState(0) != 0) 421 | { 422 | } 423 | res = RES_OK; 424 | } 425 | 426 | return res; 427 | } 428 | 429 | DRESULT SD_ioctl(BYTE pdrv, BYTE cmd, void* buff) 430 | { 431 | uint8_t res = RES_ERROR; 432 | HAL_SD_CardInfoTypeDef CardInfo; 433 | 434 | if (SD_State & STA_NOINIT) return RES_NOTRDY; 435 | 436 | switch (cmd) 437 | { 438 | /* Make sure that no pending write process */ 439 | case CTRL_SYNC : 440 | res = RES_OK; 441 | break; 442 | 443 | /* Get number of sectors on the disk (DWORD) */ 444 | case GET_SECTOR_COUNT : 445 | res = BSP_SD_GetCardInfo(0, &CardInfo); 446 | *(DWORD*)buff = CardInfo.LogBlockNbr; 447 | break; 448 | 449 | /* Get R/W sector size (WORD) */ 450 | case GET_SECTOR_SIZE : 451 | res = BSP_SD_GetCardInfo(0, &CardInfo); 452 | *(WORD*)buff = CardInfo.LogBlockSize; 453 | break; 454 | 455 | /* Get erase block size in unit of sector (DWORD) */ 456 | case GET_BLOCK_SIZE : 457 | res = BSP_SD_GetCardInfo(0, &CardInfo); 458 | *(DWORD*)buff = CardInfo.LogBlockSize / SD_DEFAULT_BLOCK_SIZE; 459 | break; 460 | 461 | default: 462 | res = RES_PARERR; 463 | } 464 | return (DRESULT)res; 465 | } 466 | 467 | #endif -------------------------------------------------------------------------------- /src/Seeed_FS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SD - a slightly more friendly wrapper for fatfs. 4 | 5 | 6 | Created by hongtai.liu 13 July 2019 7 | 8 | 9 | */ 10 | #include 11 | #include 12 | 13 | namespace fs 14 | { 15 | 16 | TCHAR FS::_path[] = {0}; 17 | 18 | File::File(FIL f, const char *n) 19 | { 20 | // is a file. 21 | _file = new FIL(f); 22 | if (_file) 23 | { 24 | _dir = NULL; 25 | sprintf((char *)_name, "%s", n); 26 | } 27 | _fno = NULL; 28 | } 29 | 30 | File::File(DIR d, const char *n) 31 | { 32 | // is a directory 33 | _dir = new DIR(d); 34 | if (_dir) 35 | { 36 | _file = NULL; 37 | sprintf((char *)_name, "%s", n); 38 | } 39 | _fno = NULL; 40 | } 41 | 42 | File::File(void) 43 | { 44 | _file = NULL; 45 | _dir = NULL; 46 | _name[0] = 0; 47 | // Serial.print("Created empty file object"); 48 | _fno = NULL; 49 | } 50 | 51 | File::~File() 52 | { 53 | // close(); 54 | } 55 | 56 | // returns a pointer to the file name 57 | char *File::name(void) 58 | { 59 | return _name; 60 | } 61 | 62 | // a directory is a special type of file 63 | boolean File::isDirectory(void) 64 | { 65 | FRESULT ret = FR_OK; 66 | FILINFO v_fileinfo; 67 | 68 | if (_dir && !_file) 69 | { 70 | return true; 71 | } 72 | if ((ret = f_stat(_name, &v_fileinfo)) == FR_OK) 73 | { 74 | if (v_fileinfo.fattrib & AM_DIR) 75 | { 76 | return true; 77 | } 78 | else 79 | { 80 | return false; 81 | } 82 | } 83 | else 84 | { 85 | return false; 86 | } 87 | return false; 88 | } 89 | 90 | size_t File::write(uint8_t val) 91 | { 92 | return write(&val, 1); 93 | } 94 | 95 | size_t File::write(const uint8_t *buf, size_t size) 96 | { 97 | UINT t; 98 | FRESULT ret = FR_OK; 99 | if (!_file) 100 | { 101 | return false; 102 | } 103 | ret = f_write(_file, buf, size, &t); 104 | 105 | if (FR_OK == ret) 106 | { 107 | return t; 108 | } 109 | return false; 110 | } 111 | 112 | // return the peek value 113 | int File::peek() 114 | { 115 | if (!_file) 116 | { 117 | return 0; 118 | } 119 | 120 | int c = read(); 121 | if (c != -1) 122 | { 123 | f_lseek(_file, f_tell(_file) - 1); 124 | } 125 | return c; 126 | } 127 | 128 | // read a value 129 | int File::read() 130 | { 131 | uint8_t val; 132 | if (_file) 133 | { 134 | return read(&val, 1) == 1 ? val : -1; 135 | } 136 | return -1; 137 | } 138 | 139 | char *File::gets(char *str, uint32_t nbyte) 140 | { 141 | if (!_file) 142 | { 143 | return NULL; 144 | } 145 | return f_gets(str, nbyte, _file); 146 | } 147 | 148 | size_t File::read(void *buf, uint32_t nbyte) 149 | { 150 | UINT t; 151 | if (!_file) 152 | { 153 | return 0; 154 | } 155 | if (f_read(_file, buf, nbyte, &t) == FR_OK) 156 | { 157 | 158 | return (size_t)t; 159 | } 160 | else 161 | { 162 | return 0; 163 | } 164 | } 165 | 166 | int File::available() 167 | { 168 | if (!_file) 169 | { 170 | return 0; 171 | } 172 | 173 | return !f_eof(_file); 174 | } 175 | 176 | void File::flush() 177 | { 178 | if (_file) 179 | { 180 | f_sync(_file); 181 | } 182 | } 183 | 184 | boolean File::seek(uint32_t pos) 185 | { 186 | if (!_file) 187 | { 188 | return false; 189 | } 190 | 191 | return f_lseek(_file, pos); 192 | } 193 | 194 | bool File::seek(uint32_t pos, SeekMode mode) 195 | { 196 | if (!_file) 197 | { 198 | return false; 199 | } 200 | switch (mode) 201 | { 202 | case SeekSet: 203 | return f_lseek(_file, pos); 204 | break; 205 | case SeekCur: 206 | f_lseek(_file, f_tell(_file) + pos); 207 | break; 208 | case SeekEnd: 209 | return f_lseek(_file, f_size(_file) - pos); 210 | break; 211 | default: 212 | return false; 213 | break; 214 | } 215 | return false; 216 | } 217 | 218 | uint32_t File::position() 219 | { 220 | if (!_file) 221 | { 222 | return -1; 223 | } 224 | return f_tell(_file); 225 | } 226 | 227 | uint32_t File::tell() 228 | { 229 | if (!_file) 230 | { 231 | return -1; 232 | } 233 | return f_tell(_file); 234 | } 235 | 236 | uint32_t File::size() 237 | { 238 | return f_size(_file); 239 | } 240 | 241 | void File::close() 242 | { 243 | if (_dir) 244 | { 245 | f_closedir(_dir); 246 | delete _dir; 247 | _dir = NULL; 248 | } 249 | if (_file) 250 | { 251 | f_close(_file); 252 | delete _file; 253 | _file = NULL; 254 | } 255 | if (_fno) 256 | { 257 | delete _fno; 258 | _file = NULL; 259 | } 260 | } 261 | 262 | // allows you to recurse into a directory 263 | File File::openNextFile(uint8_t mode) 264 | { 265 | FRESULT res; 266 | UINT i; 267 | static char path[257]; 268 | 269 | strcpy(path, _name); 270 | if (!_fno) 271 | { 272 | _fno = new FILINFO; 273 | } 274 | 275 | for (; _fno;) 276 | { 277 | res = f_readdir(_dir, _fno); /* Read a directory item */ 278 | if (res != FR_OK || _fno->fname[0] == 0) 279 | { 280 | break; /* Break on error or end of dir */ 281 | } 282 | if (_fno->fattrib == 255) 283 | { 284 | continue; /*ignore if the addr was removed*/ 285 | } 286 | 287 | i = strlen(path); 288 | if (i && path[i - 1] != '/') 289 | { 290 | path[i++] = '/'; 291 | } 292 | sprintf((char *)path + i, "%s", _fno->fname); 293 | 294 | if (_fno->fattrib & AM_DIR) 295 | { 296 | /* It is a directory */ 297 | DIR dir; 298 | if ((res = f_opendir(&dir, path)) == FR_OK) 299 | { 300 | return File(dir, path); 301 | } 302 | else 303 | { 304 | return File(); 305 | } 306 | } 307 | else 308 | { 309 | FIL file; 310 | if ((res = f_open(&file, path, mode)) == FR_OK) 311 | { 312 | return File(file, path); 313 | } 314 | else 315 | { 316 | return File(); 317 | } 318 | } 319 | } 320 | return File(); 321 | } 322 | 323 | void File::rewindDirectory(void) 324 | { 325 | if (isDirectory()) 326 | { 327 | f_rewinddir(_dir); 328 | } 329 | } 330 | 331 | File::operator bool() const 332 | { 333 | if (_file || _dir) 334 | { 335 | return true; 336 | } 337 | return false; 338 | } 339 | 340 | File &File::operator=(const File &f) 341 | { 342 | if (this != &f) 343 | { 344 | close(); 345 | this->_file = f._file; 346 | this->_dir = f._dir; 347 | this->_fno = f._fno; 348 | strcpy(this->_name, f._name); 349 | } 350 | return *this; 351 | } 352 | 353 | File FS::open(const char *filepath, uint8_t mode) 354 | { 355 | if(filepath == NULL){ 356 | return File(); 357 | } 358 | 359 | FRESULT ret = FR_OK; 360 | FILINFO v_fileinfo; 361 | FS::_path[0] = _T('0' + _pdrv); 362 | FS::_path[1] = _T(':'); 363 | FS::_path[2] = _T('/'); 364 | FS::_path[3] = '\0'; 365 | 366 | strcat(FS::_path, filepath); 367 | 368 | if(FS::_path[3] == '\0' || (FS::_path[3] == '/' && FS::_path[4] == '\0')) // root dir 369 | { 370 | DIR dir; 371 | if ((ret = f_opendir(&dir, FS::_path)) == FR_OK) 372 | { 373 | return File(dir, FS::_path); 374 | } 375 | else 376 | { 377 | return File(); 378 | } 379 | } 380 | 381 | if ((ret = f_stat(FS::_path, &v_fileinfo)) == FR_OK) 382 | { 383 | if (v_fileinfo.fattrib & AM_DIR) 384 | { 385 | DIR dir; 386 | if ((ret = f_opendir(&dir, FS::_path)) == FR_OK) 387 | { 388 | return File(dir, FS::_path); 389 | } 390 | else 391 | { 392 | return File(); 393 | } 394 | } 395 | else 396 | { 397 | FIL file; 398 | if ((ret = f_open(&file, FS::_path, mode)) == FR_OK) 399 | { 400 | return File(file, FS::_path); 401 | } 402 | else 403 | { 404 | return File(); 405 | } 406 | } 407 | } 408 | else 409 | { 410 | FIL file; 411 | if ((ret = f_open(&file, FS::_path, mode)) == FR_OK) 412 | { 413 | return File(file, FS::_path); 414 | } 415 | else 416 | { 417 | return File(); 418 | } 419 | } 420 | } 421 | 422 | File FS::open(const char *filepath, const char *mode) 423 | { 424 | if (strlen(mode) > 3) 425 | { 426 | return File(); 427 | } 428 | if (!strcmp(mode, "r")) 429 | { 430 | return open(filepath, FA_READ); 431 | } 432 | if (!strcmp(mode, "r+")) 433 | { 434 | return open(filepath, FA_READ | FA_WRITE); 435 | } 436 | if (!strcmp(mode, "w")) 437 | { 438 | return open(filepath, FA_CREATE_ALWAYS | FA_WRITE); 439 | } 440 | if (!strcmp(mode, "w+")) 441 | { 442 | return open(filepath, FA_CREATE_ALWAYS | FA_WRITE | FA_READ); 443 | } 444 | if (!strcmp(mode, "a")) 445 | { 446 | return open(filepath, FA_OPEN_APPEND | FA_WRITE); 447 | } 448 | if (!strcmp(mode, "a+")) 449 | { 450 | return open(filepath, FA_OPEN_APPEND | FA_WRITE | FA_READ); 451 | } 452 | if (!strcmp(mode, "wx")) 453 | { 454 | return open(filepath, FA_CREATE_NEW | FA_WRITE); 455 | } 456 | if (!strcmp(mode, "w+x")) 457 | { 458 | return open(filepath, FA_CREATE_NEW | FA_WRITE | FA_READ); 459 | } 460 | 461 | return File(); 462 | } 463 | 464 | boolean FS::exists(const char *filepath) 465 | { 466 | FRESULT ret = FR_OK; 467 | FILINFO v_fileinfo; 468 | FS::_path[0] = _T('0' + _pdrv); 469 | FS::_path[1] = _T(':'); 470 | FS::_path[2] = _T('/'); 471 | FS::_path[3] = '\0'; 472 | 473 | strcat(FS::_path, filepath); 474 | if ((ret = f_stat(FS::_path, &v_fileinfo)) == FR_OK) 475 | { 476 | return true; 477 | } 478 | else 479 | { 480 | return false; 481 | } 482 | } 483 | 484 | boolean FS::mkdir(const char *filepath) 485 | { 486 | FRESULT ret = FR_OK; 487 | FS::_path[0] = _T('0' + _pdrv); 488 | FS::_path[1] = _T(':'); 489 | FS::_path[2] = _T('/'); 490 | FS::_path[3] = '\0'; 491 | 492 | strcat(FS::_path, filepath); 493 | 494 | ret = f_mkdir(FS::_path); 495 | 496 | if (ret == FR_OK) 497 | { 498 | return true; 499 | } 500 | else 501 | { 502 | return false; 503 | } 504 | } 505 | 506 | boolean FS::rename(const char *pathFrom, const char *pathTo) 507 | { 508 | FRESULT ret = FR_OK; 509 | char file[_MAX_LFN + 2]; 510 | 511 | FS::_path[0] = _T('0' + _pdrv); 512 | FS::_path[1] = _T(':'); 513 | FS::_path[2] = _T('/'); 514 | FS::_path[3] = '\0'; 515 | 516 | strcpy(file, FS::_path); 517 | strcat(FS::_path, pathFrom); 518 | strcat(file, pathTo); 519 | 520 | ret = f_rename(FS::_path, file); 521 | 522 | if (ret == FR_OK) 523 | { 524 | return true; 525 | } 526 | else 527 | { 528 | return false; 529 | } 530 | } 531 | 532 | boolean FS::rmdir(const char *filepath) 533 | { 534 | char file[_MAX_LFN + 2]; 535 | 536 | FS::_path[0] = _T('0' + _pdrv); 537 | FS::_path[1] = _T(':'); 538 | FS::_path[2] = _T('/'); 539 | FS::_path[3] = '\0'; 540 | 541 | strcat(FS::_path, filepath); 542 | 543 | FRESULT status; 544 | DIR dj; 545 | FILINFO fno; 546 | status = f_findfirst(&dj, &fno, FS::_path, _T("*")); 547 | while (status == FR_OK && fno.fname[0]) 548 | { 549 | sprintf((char *)file, "%s/%s", filepath, fno.fname); 550 | if (fno.fattrib & AM_DIR) 551 | { 552 | rmdir(file); 553 | } 554 | else 555 | { 556 | remove(file); 557 | } 558 | status = f_findnext(&dj, &fno); 559 | } 560 | f_closedir(&dj); 561 | if (remove(filepath)) 562 | { 563 | return true; 564 | } 565 | else 566 | { 567 | return false; 568 | } 569 | } 570 | 571 | boolean FS::remove(const char *filepath) 572 | { 573 | FRESULT ret = FR_OK; 574 | 575 | FS::_path[0] = _T('0' + _pdrv); 576 | FS::_path[1] = _T(':'); 577 | FS::_path[2] = _T('/'); 578 | FS::_path[3] = '\0'; 579 | 580 | strcat(FS::_path, filepath); 581 | 582 | ret = f_unlink(FS::_path); 583 | if (ret == FR_OK) 584 | { 585 | return true; 586 | } 587 | else 588 | { 589 | return false; 590 | } 591 | } 592 | }; // namespace fs 593 | -------------------------------------------------------------------------------- /src/fatfs/ff.h: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------------/ 2 | / FatFs - Generic FAT file system module R0.12b / 3 | /-----------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2016, ChaN, all right reserved. 6 | / 7 | / FatFs module is an open source software. Redistribution and use of FatFs in 8 | / source and binary forms, with or without modification, are permitted provided 9 | / that the following condition is met: 10 | 11 | / 1. Redistributions of source code must retain the above copyright notice, 12 | / this condition and the following disclaimer. 13 | / 14 | / This software is provided by the copyright holder and contributors "AS IS" 15 | / and any warranties related to this software are DISCLAIMED. 16 | / The copyright owner or contributors be NOT LIABLE for any damages caused 17 | / by use of this software. 18 | /----------------------------------------------------------------------------*/ 19 | 20 | 21 | #ifndef _FATFS 22 | #define _FATFS 68020 /* Revision ID */ 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | #include "integer.h" /* Basic integer types */ 29 | #include "ffconf.h" /* FatFs configuration options */ 30 | 31 | #if _FATFS != _FFCONF 32 | #error Wrong configuration file (ffconf.h). 33 | #endif 34 | 35 | 36 | 37 | /* Definitions of volume management */ 38 | 39 | #if _MULTI_PARTITION /* Multiple partition configuration */ 40 | typedef struct { 41 | BYTE pd; /* Physical drive number */ 42 | BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ 43 | } PARTITION; 44 | extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ 45 | #define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */ 46 | #define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */ 47 | 48 | #else /* Single partition configuration */ 49 | #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ 50 | #define LD2PT(vol) 0 /* Find first valid partition or in SFD */ 51 | 52 | #endif 53 | 54 | 55 | 56 | /* Type of path name strings on FatFs API */ 57 | 58 | #if _LFN_UNICODE /* Unicode (UTF-16) string */ 59 | #if _USE_LFN == 0 60 | #error _LFN_UNICODE must be 0 at non-LFN cfg. 61 | #endif 62 | #ifndef _INC_TCHAR 63 | typedef WCHAR TCHAR; 64 | #define _T(x) L ## x 65 | #define _TEXT(x) L ## x 66 | #endif 67 | #else /* ANSI/OEM string */ 68 | #ifndef _INC_TCHAR 69 | typedef char TCHAR; 70 | #define _T(x) x 71 | #define _TEXT(x) x 72 | #endif 73 | #endif 74 | 75 | 76 | 77 | /* Type of file size variables */ 78 | 79 | #if _FS_EXFAT 80 | #if _USE_LFN == 0 81 | #error LFN must be enabled when enable exFAT 82 | #endif 83 | typedef QWORD FSIZE_t; 84 | #else 85 | typedef DWORD FSIZE_t; 86 | #endif 87 | 88 | 89 | 90 | /* File system object structure (FATFS) */ 91 | 92 | typedef struct { 93 | BYTE fs_type; /* File system type (0:N/A) */ 94 | BYTE drv; /* Physical drive number */ 95 | BYTE n_fats; /* Number of FATs (1 or 2) */ 96 | BYTE wflag; /* win[] flag (b0:dirty) */ 97 | BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ 98 | WORD id; /* File system mount ID */ 99 | WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ 100 | WORD csize; /* Cluster size [sectors] */ 101 | #if _MAX_SS != _MIN_SS 102 | WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ 103 | #endif 104 | #if _USE_LFN != 0 105 | WCHAR* lfnbuf; /* LFN working buffer */ 106 | #endif 107 | #if _FS_EXFAT 108 | BYTE* dirbuf; /* Directory entry block scratchpad buffer */ 109 | #endif 110 | #if _FS_REENTRANT 111 | _SYNC_t sobj; /* Identifier of sync object */ 112 | #endif 113 | #if !_FS_READONLY 114 | DWORD last_clst; /* Last allocated cluster */ 115 | DWORD free_clst; /* Number of free clusters */ 116 | #endif 117 | #if _FS_RPATH != 0 118 | DWORD cdir; /* Current directory start cluster (0:root) */ 119 | #if _FS_EXFAT 120 | DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ 121 | DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ 122 | DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ 123 | #endif 124 | #endif 125 | DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ 126 | DWORD fsize; /* Size of an FAT [sectors] */ 127 | DWORD volbase; /* Volume base sector */ 128 | DWORD fatbase; /* FAT base sector */ 129 | DWORD dirbase; /* Root directory base sector/cluster */ 130 | DWORD database; /* Data base sector */ 131 | DWORD winsect; /* Current sector appearing in the win[] */ 132 | BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ 133 | } FATFS; 134 | 135 | 136 | 137 | /* Object ID and allocation information (_FDID) */ 138 | 139 | typedef struct { 140 | FATFS* fs; /* Pointer to the owner file system object */ 141 | WORD id; /* Owner file system mount ID */ 142 | BYTE attr; /* Object attribute */ 143 | BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */ 144 | DWORD sclust; /* Object start cluster (0:no cluster or root directory) */ 145 | FSIZE_t objsize; /* Object size (valid when sclust != 0) */ 146 | #if _FS_EXFAT 147 | DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */ 148 | DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ 149 | DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ 150 | DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */ 151 | #endif 152 | #if _FS_LOCK != 0 153 | UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ 154 | #endif 155 | } _FDID; 156 | 157 | 158 | 159 | /* File object structure (FIL) */ 160 | 161 | typedef struct { 162 | _FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ 163 | BYTE flag; /* File status flags */ 164 | BYTE err; /* Abort flag (error code) */ 165 | FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ 166 | DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */ 167 | DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ 168 | #if !_FS_READONLY 169 | DWORD dir_sect; /* Sector number containing the directory entry */ 170 | BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ 171 | #endif 172 | #if _USE_FASTSEEK 173 | DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ 174 | #endif 175 | #if !_FS_TINY 176 | BYTE buf[_MAX_SS]; /* File private data read/write window */ 177 | #endif 178 | } FIL; 179 | 180 | 181 | 182 | /* Directory object structure (DIR) */ 183 | 184 | typedef struct { 185 | _FDID obj; /* Object identifier */ 186 | DWORD dptr; /* Current read/write offset */ 187 | DWORD clust; /* Current cluster */ 188 | DWORD sect; /* Current sector */ 189 | BYTE* dir; /* Pointer to the directory item in the win[] */ 190 | BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ 191 | #if _USE_LFN != 0 192 | DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ 193 | #endif 194 | #if _USE_FIND 195 | const TCHAR* pat; /* Pointer to the name matching pattern */ 196 | #endif 197 | } DIR; 198 | 199 | 200 | 201 | /* File information structure (FILINFO) */ 202 | 203 | typedef struct { 204 | FSIZE_t fsize; /* File size */ 205 | WORD fdate; /* Modified date */ 206 | WORD ftime; /* Modified time */ 207 | BYTE fattrib; /* File attribute */ 208 | #if _USE_LFN != 0 209 | TCHAR altname[13]; /* Altenative file name */ 210 | TCHAR fname[_MAX_LFN + 1]; /* Primary file name */ 211 | #else 212 | TCHAR fname[13]; /* File name */ 213 | #endif 214 | } FILINFO; 215 | 216 | 217 | 218 | /* File function return code (FRESULT) */ 219 | 220 | typedef enum { 221 | FR_OK = 0, /* (0) Succeeded */ 222 | FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ 223 | FR_INT_ERR, /* (2) Assertion failed */ 224 | FR_NOT_READY, /* (3) The physical drive cannot work */ 225 | FR_NO_FILE, /* (4) Could not find the file */ 226 | FR_NO_PATH, /* (5) Could not find the path */ 227 | FR_INVALID_NAME, /* (6) The path name format is invalid */ 228 | FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ 229 | FR_EXIST, /* (8) Access denied due to prohibited access */ 230 | FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ 231 | FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ 232 | FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ 233 | FR_NOT_ENABLED, /* (12) The volume has no work area */ 234 | FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ 235 | FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ 236 | FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ 237 | FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ 238 | FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ 239 | FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ 240 | FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ 241 | } FRESULT; 242 | 243 | 244 | 245 | /*--------------------------------------------------------------*/ 246 | /* FatFs module application interface */ 247 | 248 | FRESULT f_open(FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ 249 | FRESULT f_close(FIL* fp); /* Close an open file object */ 250 | FRESULT f_read(FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ 251 | FRESULT f_write(FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ 252 | FRESULT f_lseek(FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ 253 | FRESULT f_truncate(FIL* fp); /* Truncate the file */ 254 | FRESULT f_sync(FIL* fp); /* Flush cached data of the writing file */ 255 | FRESULT f_opendir(DIR* dp, const TCHAR* path); /* Open a directory */ 256 | FRESULT f_closedir(DIR* dp); /* Close an open directory */ 257 | FRESULT f_readdir(DIR* dp, FILINFO* fno); /* Read a directory item */ 258 | FRESULT f_findfirst(DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ 259 | FRESULT f_findnext(DIR* dp, FILINFO* fno); /* Find next file */ 260 | FRESULT f_mkdir(const TCHAR* path); /* Create a sub directory */ 261 | FRESULT f_unlink(const TCHAR* path); /* Delete an existing file or directory */ 262 | FRESULT f_rename(const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ 263 | FRESULT f_stat(const TCHAR* path, FILINFO* fno); /* Get file status */ 264 | FRESULT f_chmod(const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ 265 | FRESULT f_utime(const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ 266 | FRESULT f_chdir(const TCHAR* path); /* Change current directory */ 267 | FRESULT f_chdrive(const TCHAR* path); /* Change current drive */ 268 | FRESULT f_getcwd(TCHAR* buff, UINT len); /* Get current directory */ 269 | FRESULT f_getfree(const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ 270 | FRESULT f_getlabel(const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ 271 | FRESULT f_setlabel(const TCHAR* label); /* Set volume label */ 272 | FRESULT f_forward(FIL* fp, UINT(*func)(const BYTE*, UINT), UINT btf, UINT* bf); /* Forward data to the stream */ 273 | FRESULT f_expand(FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ 274 | FRESULT f_mount(FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ 275 | FRESULT f_mkfs(const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ 276 | FRESULT f_fdisk(BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ 277 | int f_putc(TCHAR c, FIL* fp); /* Put a character to the file */ 278 | int f_puts(const TCHAR* str, FIL* cp); /* Put a string to the file */ 279 | int f_printf(FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ 280 | TCHAR* f_gets(TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ 281 | 282 | #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) 283 | #define f_error(fp) ((fp)->err) 284 | #define f_tell(fp) ((fp)->fptr) 285 | #define f_size(fp) ((fp)->obj.objsize) 286 | #define f_rewind(fp) f_lseek((fp), 0) 287 | #define f_rewinddir(dp) f_readdir((dp), 0) 288 | 289 | #ifndef EOF 290 | #define EOF (-1) 291 | #endif 292 | 293 | 294 | 295 | 296 | /*--------------------------------------------------------------*/ 297 | /* Additional user defined functions */ 298 | 299 | /* RTC function */ 300 | #if !_FS_READONLY && !_FS_NORTC 301 | DWORD get_fattime(void); 302 | #endif 303 | 304 | /* Unicode support functions */ 305 | #if _USE_LFN != 0 /* Unicode - OEM code conversion */ 306 | WCHAR ff_convert(WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ 307 | WCHAR ff_wtoupper(WCHAR chr); /* Unicode upper-case conversion */ 308 | #if _USE_LFN == 3 /* Memory functions */ 309 | void* ff_memalloc(UINT msize); /* Allocate memory block */ 310 | void ff_memfree(void* mblock); /* Free memory block */ 311 | #endif 312 | #endif 313 | 314 | /* Sync functions */ 315 | #if _FS_REENTRANT 316 | int ff_cre_syncobj(BYTE vol, _SYNC_t* sobj); /* Create a sync object */ 317 | int ff_req_grant(_SYNC_t sobj); /* Lock sync object */ 318 | void ff_rel_grant(_SYNC_t sobj); /* Unlock sync object */ 319 | int ff_del_syncobj(_SYNC_t sobj); /* Delete a sync object */ 320 | #endif 321 | 322 | 323 | 324 | 325 | /*--------------------------------------------------------------*/ 326 | /* Flags and offset address */ 327 | 328 | 329 | /* File access mode and open method flags (3rd argument of f_open) */ 330 | #define FA_READ 0x01 331 | #define FA_WRITE 0x02 332 | #define FA_OPEN_EXISTING 0x00 333 | #define FA_CREATE_NEW 0x04 334 | #define FA_CREATE_ALWAYS 0x08 335 | #define FA_OPEN_ALWAYS 0x10 336 | #define FA_OPEN_APPEND 0x30 337 | 338 | /* Fast seek controls (2nd argument of f_lseek) */ 339 | #define CREATE_LINKMAP ((FSIZE_t)0 - 1) 340 | 341 | /* Format options (2nd argument of f_mkfs) */ 342 | #define FM_FAT 0x01 343 | #define FM_FAT32 0x02 344 | #define FM_EXFAT 0x04 345 | #define FM_ANY 0x07 346 | #define FM_SFD 0x08 347 | 348 | /* Filesystem type (FATFS.fs_type) */ 349 | #define FS_FAT12 1 350 | #define FS_FAT16 2 351 | #define FS_FAT32 3 352 | #define FS_EXFAT 4 353 | 354 | /* File attribute bits for directory entry (FILINFO.fattrib) */ 355 | #define AM_RDO 0x01 /* Read only */ 356 | #define AM_HID 0x02 /* Hidden */ 357 | #define AM_SYS 0x04 /* System */ 358 | #define AM_DIR 0x10 /* Directory */ 359 | #define AM_ARC 0x20 /* Archive */ 360 | 361 | 362 | #ifdef __cplusplus 363 | } 364 | #endif 365 | 366 | #endif /* _FATFS */ -------------------------------------------------------------------------------- /src/SD/Seeed_sdcard_hal.cpp: -------------------------------------------------------------------------------- 1 | #include "Seeed_sdcard_hal.h" 2 | #include "Arduino.h" 3 | #include "SPI.h" 4 | 5 | 6 | 7 | typedef enum 8 | { 9 | GO_IDLE_STATE = 0, 10 | SEND_OP_COND = 1, 11 | SEND_CID = 2, 12 | SEND_RELATIVE_ADDR = 3, 13 | SEND_SWITCH_FUNC = 6, 14 | SEND_IF_COND = 8, 15 | SEND_CSD = 9, 16 | STOP_TRANSMISSION = 12, 17 | SEND_STATUS = 13, 18 | SET_BLOCKLEN = 16, 19 | READ_BLOCK_SINGLE = 17, 20 | READ_BLOCK_MULTIPLE = 18, 21 | SEND_NUM_WR_BLOCKS = 22, 22 | SET_WR_BLK_ERASE_COUNT = 23, 23 | WRITE_BLOCK_SINGLE = 24, 24 | WRITE_BLOCK_MULTIPLE = 25, 25 | APP_OP_COND = 41, 26 | APP_CLR_CARD_DETECT = 42, 27 | APP_CMD = 55, 28 | READ_OCR = 58, 29 | CRC_ON_OFF = 59 30 | } ardu_sdcard_command_t; 31 | 32 | static ardu_sdcard_t *s_cards[_VOLUMES] = {NULL, NULL}; 33 | 34 | namespace 35 | { 36 | 37 | struct AcquireSPI 38 | { 39 | ardu_sdcard_t *card; 40 | explicit AcquireSPI(ardu_sdcard_t *card) 41 | : card(card) 42 | { 43 | card->spi->beginTransaction(SPISettings(card->frequency, MSBFIRST, SPI_MODE0)); 44 | } 45 | AcquireSPI(ardu_sdcard_t *card, int frequency) 46 | : card(card) 47 | { 48 | card->spi->beginTransaction(SPISettings(card->frequency, MSBFIRST, SPI_MODE0)); 49 | } 50 | ~AcquireSPI() 51 | { 52 | card->spi->endTransaction(); 53 | } 54 | 55 | private: 56 | AcquireSPI(AcquireSPI const &); 57 | AcquireSPI &operator=(AcquireSPI const &); 58 | }; 59 | 60 | } // namespace 61 | 62 | /* 63 | SD SPI 64 | * */ 65 | 66 | bool sdWait(uint8_t pdrv, int timeout) 67 | { 68 | char resp; 69 | uint32_t start = millis(); 70 | 71 | do 72 | { 73 | resp = s_cards[pdrv]->spi->transfer(0xFF); 74 | } while (resp == 0x00 && (millis() - start) < (unsigned int)timeout); 75 | 76 | return (resp > 0x00); 77 | } 78 | 79 | void sdStop(uint8_t pdrv) 80 | { 81 | s_cards[pdrv]->spi->transfer(0xFD); 82 | } 83 | 84 | void sdDeselectCard(uint8_t pdrv) 85 | { 86 | ardu_sdcard_t *card = s_cards[pdrv]; 87 | digitalWrite(card->ssPin, HIGH); 88 | } 89 | 90 | bool sdSelectCard(uint8_t pdrv) 91 | { 92 | ardu_sdcard_t *card = s_cards[pdrv]; 93 | digitalWrite(card->ssPin, LOW); 94 | sdWait(pdrv, 300); 95 | return true; 96 | } 97 | 98 | bool sdReadBytes(uint8_t pdrv, char *buffer, int length) 99 | { 100 | char token; 101 | unsigned short crc; 102 | ardu_sdcard_t *card = s_cards[pdrv]; 103 | char *p = buffer; 104 | 105 | uint32_t start = millis(); 106 | do 107 | { 108 | token = card->spi->transfer(0xFF); 109 | } while (token == 0xFF && (millis() - start) < 500); 110 | 111 | if (token != 0xFE) 112 | { 113 | return false; 114 | } 115 | 116 | for (; p < buffer + length; p++) 117 | { 118 | *p = card->spi->transfer(0xFF); 119 | } 120 | crc = card->spi->transfer16(0xFFFF); 121 | return (!card->supports_crc || crc == CRC16(buffer, length)); 122 | } 123 | 124 | char sdWriteBytes(uint8_t pdrv, const char *buffer, char token) 125 | { 126 | ardu_sdcard_t *card = s_cards[pdrv]; 127 | unsigned short crc = (card->supports_crc) ? CRC16(buffer, 512) : 0xFFFF; 128 | if (!sdWait(pdrv, 500)) 129 | { 130 | return false; 131 | } 132 | card->spi->transfer(token); 133 | card->spi->transfer((uint8_t *)buffer, 512); 134 | card->spi->transfer16(crc); 135 | return (card->spi->transfer(0xFF) & 0x1F); 136 | } 137 | 138 | char sdCommand(uint8_t pdrv, char cmd, unsigned int arg, unsigned int *resp) 139 | { 140 | 141 | char token; 142 | ardu_sdcard_t *card = s_cards[pdrv]; 143 | 144 | for (int f = 0; f < 3; f++) 145 | { 146 | if (cmd == SEND_NUM_WR_BLOCKS || cmd == SET_WR_BLK_ERASE_COUNT || cmd == APP_OP_COND || cmd == APP_CLR_CARD_DETECT) 147 | { 148 | token = sdCommand(pdrv, APP_CMD, 0, NULL); 149 | sdDeselectCard(pdrv); 150 | if (token > 1) 151 | { 152 | return token; 153 | } 154 | if (!sdSelectCard(pdrv)) 155 | { 156 | return 0xFF; 157 | } 158 | } 159 | 160 | char cmdPacket[7]; 161 | cmdPacket[0] = cmd | 0x40; 162 | cmdPacket[1] = arg >> 24; 163 | cmdPacket[2] = arg >> 16; 164 | cmdPacket[3] = arg >> 8; 165 | cmdPacket[4] = arg; 166 | if (card->supports_crc || cmd == GO_IDLE_STATE || cmd == SEND_IF_COND) 167 | { 168 | cmdPacket[5] = (CRC7(cmdPacket, 5) << 1) | 0x01; 169 | } 170 | else 171 | { 172 | cmdPacket[5] = 0x01; 173 | } 174 | cmdPacket[6] = 0xFF; 175 | 176 | card->spi->transfer((uint8_t *)cmdPacket, (cmd == STOP_TRANSMISSION) ? 7 : 6); 177 | 178 | for (int i = 0; i < 9; i++) 179 | { 180 | token = card->spi->transfer(0xFF); 181 | if (!(token & 0x80)) 182 | { 183 | break; 184 | } 185 | } 186 | 187 | if (token == 0xFF) 188 | { 189 | sdDeselectCard(pdrv); 190 | delay(100); 191 | sdSelectCard(pdrv); 192 | continue; 193 | } 194 | else if (token & 0x08) 195 | { 196 | sdDeselectCard(pdrv); 197 | delay(100); 198 | sdSelectCard(pdrv); 199 | continue; 200 | } 201 | else if (token > 1) 202 | { 203 | break; 204 | } 205 | 206 | if (cmd == SEND_STATUS && resp) 207 | { 208 | *resp = card->spi->transfer(0xFF); 209 | } 210 | else if ((cmd == SEND_IF_COND || cmd == READ_OCR) && resp) 211 | { 212 | for (uint8_t i = 0; i < 4; i++) 213 | { 214 | uint8_t temp = card->spi->transfer(0xFF); 215 | *resp = *resp * 256 + temp; 216 | } 217 | } 218 | break; 219 | } 220 | return token; 221 | } 222 | 223 | /* 224 | SPI SDCARD Communication 225 | * */ 226 | 227 | char sdTransaction(uint8_t pdrv, char cmd, unsigned int arg, unsigned int *resp) 228 | { 229 | if (!sdSelectCard(pdrv)) 230 | { 231 | return 0xFF; 232 | } 233 | char token = sdCommand(pdrv, cmd, arg, resp); 234 | sdDeselectCard(pdrv); 235 | return token; 236 | } 237 | 238 | bool sdReadSector(uint8_t pdrv, char *buffer, unsigned long long sector) 239 | { 240 | for (int f = 0; f < 3; f++) 241 | { 242 | if (!sdSelectCard(pdrv)) 243 | { 244 | break; 245 | } 246 | if (!sdCommand(pdrv, READ_BLOCK_SINGLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) 247 | { 248 | bool success = sdReadBytes(pdrv, buffer, 512); 249 | sdDeselectCard(pdrv); 250 | if (success) 251 | { 252 | return true; 253 | } 254 | } 255 | else 256 | { 257 | break; 258 | } 259 | } 260 | sdDeselectCard(pdrv); 261 | return false; 262 | } 263 | 264 | bool sdReadSectors(uint8_t pdrv, char *buffer, unsigned long long sector, int count) 265 | { 266 | for (int f = 0; f < 3;) 267 | { 268 | if (!sdSelectCard(pdrv)) 269 | { 270 | break; 271 | } 272 | 273 | if (!sdCommand(pdrv, READ_BLOCK_MULTIPLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) 274 | { 275 | do 276 | { 277 | if (!sdReadBytes(pdrv, buffer, 512)) 278 | { 279 | f++; 280 | break; 281 | } 282 | 283 | sector++; 284 | buffer += 512; 285 | f = 0; 286 | } while (--count); 287 | 288 | if (sdCommand(pdrv, STOP_TRANSMISSION, 0, NULL)) 289 | { 290 | break; 291 | } 292 | 293 | sdDeselectCard(pdrv); 294 | if (count == 0) 295 | { 296 | return true; 297 | } 298 | } 299 | else 300 | { 301 | break; 302 | } 303 | } 304 | sdDeselectCard(pdrv); 305 | return false; 306 | } 307 | 308 | bool sdWriteSector(uint8_t pdrv, const char *buffer, unsigned long long sector) 309 | { 310 | for (int f = 0; f < 3; f++) 311 | { 312 | if (!sdSelectCard(pdrv)) 313 | { 314 | break; 315 | } 316 | if (!sdCommand(pdrv, WRITE_BLOCK_SINGLE, (s_cards[pdrv]->type == CARD_SDHC) ? sector : sector << 9, NULL)) 317 | { 318 | 319 | char token = sdWriteBytes(pdrv, buffer, 0xFE); 320 | sdDeselectCard(pdrv); 321 | 322 | if (token == 0x0A) 323 | { 324 | continue; 325 | } 326 | else if (token == 0x0C) 327 | { 328 | return false; 329 | } 330 | 331 | unsigned int resp; 332 | if (sdTransaction(pdrv, SEND_STATUS, 0, &resp) || resp) 333 | { 334 | return false; 335 | } 336 | return true; 337 | } 338 | else 339 | { 340 | break; 341 | } 342 | } 343 | sdDeselectCard(pdrv); 344 | return false; 345 | } 346 | 347 | bool sdWriteSectors(uint8_t pdrv, const char *buffer, unsigned long long sector, int count) 348 | { 349 | char token; 350 | const char *currentBuffer = buffer; 351 | unsigned long long currentSector = sector; 352 | int currentCount = count; 353 | ardu_sdcard_t *card = s_cards[pdrv]; 354 | 355 | for (int f = 0; f < 3;) 356 | { 357 | if (card->type != CARD_MMC) 358 | { 359 | if (sdTransaction(pdrv, SET_WR_BLK_ERASE_COUNT, currentCount, NULL)) 360 | { 361 | break; 362 | } 363 | } 364 | 365 | if (!sdSelectCard(pdrv)) 366 | { 367 | break; 368 | } 369 | 370 | if (!sdCommand(pdrv, WRITE_BLOCK_MULTIPLE, (card->type == CARD_SDHC) ? currentSector : currentSector << 9, NULL)) 371 | { 372 | do 373 | { 374 | token = sdWriteBytes(pdrv, currentBuffer, 0xFC); 375 | if (token != 0x05) 376 | { 377 | f++; 378 | break; 379 | } 380 | currentBuffer += 512; 381 | f = 0; 382 | } while (--currentCount); 383 | 384 | if (!sdWait(pdrv, 500)) 385 | { 386 | break; 387 | } 388 | 389 | if (currentCount == 0) 390 | { 391 | sdStop(pdrv); 392 | sdDeselectCard(pdrv); 393 | 394 | unsigned int resp; 395 | if (sdTransaction(pdrv, SEND_STATUS, 0, &resp) || resp) 396 | { 397 | return false; 398 | } 399 | return true; 400 | } 401 | else 402 | { 403 | if (sdCommand(pdrv, STOP_TRANSMISSION, 0, NULL)) 404 | { 405 | break; 406 | } 407 | 408 | sdDeselectCard(pdrv); 409 | 410 | if (token == 0x0A) 411 | { 412 | unsigned int writtenBlocks = 0; 413 | if (card->type != CARD_MMC && sdSelectCard(pdrv)) 414 | { 415 | if (!sdCommand(pdrv, SEND_NUM_WR_BLOCKS, 0, NULL)) 416 | { 417 | char acmdData[4]; 418 | if (sdReadBytes(pdrv, acmdData, 4)) 419 | { 420 | writtenBlocks = acmdData[0] << 24; 421 | writtenBlocks |= acmdData[1] << 16; 422 | writtenBlocks |= acmdData[2] << 8; 423 | writtenBlocks |= acmdData[3]; 424 | } 425 | } 426 | sdDeselectCard(pdrv); 427 | } 428 | currentBuffer = buffer + (writtenBlocks << 9); 429 | currentSector = sector + writtenBlocks; 430 | currentCount = count - writtenBlocks; 431 | continue; 432 | } 433 | else 434 | { 435 | return false; 436 | } 437 | } 438 | } 439 | else 440 | { 441 | break; 442 | } 443 | } 444 | sdDeselectCard(pdrv); 445 | return false; 446 | } 447 | 448 | unsigned long sdGetSectorsCount(uint8_t pdrv) 449 | { 450 | for (int f = 0; f < 3; f++) 451 | { 452 | if (!sdSelectCard(pdrv)) 453 | { 454 | break; 455 | } 456 | 457 | if (!sdCommand(pdrv, SEND_CSD, 0, NULL)) 458 | { 459 | char csd[16]; 460 | bool success = sdReadBytes(pdrv, csd, 16); 461 | sdDeselectCard(pdrv); 462 | if (success) 463 | { 464 | if ((csd[0] >> 6) == 0x01) 465 | { 466 | unsigned long size = (((unsigned long)(csd[7] & 0x3F) << 16) | ((unsigned long)csd[8] << 8) | csd[9]) + 1; 467 | return size << 10; 468 | } 469 | unsigned long size = (((unsigned long)(csd[6] & 0x03) << 10) | ((unsigned long)csd[7] << 2) | ((csd[8] & 0xC0) >> 6)) + 1; 470 | size <<= (( 471 | ((csd[9] & 0x03) << 1) | ((csd[10] & 0x80) >> 7)) + 472 | 2); 473 | size <<= (csd[5] & 0x0F); 474 | return size >> 9; 475 | } 476 | } 477 | else 478 | { 479 | break; 480 | } 481 | } 482 | 483 | sdDeselectCard(pdrv); 484 | return 0; 485 | } 486 | 487 | /* 488 | FATFS API 489 | * */ 490 | 491 | DSTATUS sd_disk_initialize(uint8_t pdrv) 492 | { 493 | char token; 494 | unsigned int resp; 495 | unsigned int start; 496 | ardu_sdcard_t *card = s_cards[pdrv]; 497 | if (!(card->status & STA_NOINIT)) 498 | { 499 | return card->status; 500 | } 501 | 502 | AcquireSPI card_locked(card, 400000); 503 | 504 | digitalWrite(card->ssPin, HIGH); 505 | for (uint8_t i = 0; i < 20; i++) 506 | { 507 | card->spi->transfer(0XFF); 508 | } 509 | 510 | if (sdTransaction(pdrv, GO_IDLE_STATE, 0, NULL) != 1) 511 | { 512 | goto unknown_card; 513 | } 514 | 515 | token = sdTransaction(pdrv, CRC_ON_OFF, 0, NULL); 516 | if (token == 0x5) 517 | { 518 | card->supports_crc = false; 519 | } 520 | else if (token != 1) 521 | { 522 | goto unknown_card; 523 | } 524 | card->supports_crc = false; 525 | 526 | if (sdTransaction(pdrv, SEND_IF_COND, 0x1AA, &resp) == 1) 527 | { 528 | if ((resp & 0xFFF) != 0x1AA) 529 | { 530 | goto unknown_card; 531 | } 532 | 533 | if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) 534 | { 535 | goto unknown_card; 536 | } 537 | 538 | start = millis(); 539 | do 540 | { 541 | token = sdTransaction(pdrv, APP_OP_COND, 0x40000000, NULL); 542 | } while (token == 1 && (millis() - start) < 1000); 543 | 544 | if (token) 545 | { 546 | goto unknown_card; 547 | } 548 | 549 | if (!sdTransaction(pdrv, READ_OCR, 0, &resp)) 550 | { 551 | if (resp & (1 << 30)) 552 | { 553 | card->type = CARD_SDHC; 554 | } 555 | else 556 | { 557 | card->type = CARD_SD; 558 | } 559 | } 560 | else 561 | { 562 | goto unknown_card; 563 | } 564 | } 565 | else 566 | { 567 | if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) 568 | { 569 | goto unknown_card; 570 | } 571 | 572 | start = millis(); 573 | do 574 | { 575 | token = sdTransaction(pdrv, APP_OP_COND, 0x100000, NULL); 576 | } while (token == 0x01 && (millis() - start) < 1000); 577 | 578 | if (!token) 579 | { 580 | card->type = CARD_SD; 581 | } 582 | else 583 | { 584 | start = millis(); 585 | do 586 | { 587 | token = sdTransaction(pdrv, SEND_OP_COND, 0x100000, NULL); 588 | } while (token != 0x00 && (millis() - start) < 1000); 589 | 590 | if (token == 0x00) 591 | { 592 | card->type = CARD_MMC; 593 | } 594 | else 595 | { 596 | goto unknown_card; 597 | } 598 | } 599 | } 600 | 601 | if (card->type != CARD_MMC) 602 | { 603 | if (sdTransaction(pdrv, APP_CLR_CARD_DETECT, 0, NULL)) 604 | { 605 | goto unknown_card; 606 | } 607 | } 608 | 609 | if (card->type != CARD_SDHC) 610 | { 611 | if (sdTransaction(pdrv, SET_BLOCKLEN, 512, NULL) != 0x00) 612 | { 613 | goto unknown_card; 614 | } 615 | } 616 | 617 | card->sectors = sdGetSectorsCount(pdrv); 618 | 619 | if (card->frequency > 25000000) 620 | { 621 | card->frequency = 25000000; 622 | } 623 | 624 | card->status &= ~STA_NOINIT; 625 | return card->status; 626 | 627 | unknown_card: 628 | card->type = CARD_UNKNOWN; 629 | return card->status; 630 | } 631 | 632 | DSTATUS sd_disk_status(uint8_t pdrv) 633 | { 634 | return s_cards[pdrv]->status; 635 | } 636 | 637 | DRESULT sd_disk_read(uint8_t pdrv, uint8_t *buffer, DWORD sector, UINT count) 638 | { 639 | ardu_sdcard_t *card = s_cards[pdrv]; 640 | if (card->status & STA_NOINIT) 641 | { 642 | return RES_NOTRDY; 643 | } 644 | DRESULT res = RES_OK; 645 | 646 | AcquireSPI lock(card); 647 | 648 | if (count > 1) 649 | { 650 | res = sdReadSectors(pdrv, (char *)buffer, sector, count) ? RES_OK : RES_ERROR; 651 | } 652 | else 653 | { 654 | res = sdReadSector(pdrv, (char *)buffer, sector) ? RES_OK : RES_ERROR; 655 | } 656 | return res; 657 | } 658 | 659 | DRESULT sd_disk_write(uint8_t pdrv, const uint8_t *buffer, DWORD sector, UINT count) 660 | { 661 | ardu_sdcard_t *card = s_cards[pdrv]; 662 | if (card->status & STA_NOINIT) 663 | { 664 | return RES_NOTRDY; 665 | } 666 | 667 | if (card->status & STA_PROTECT) 668 | { 669 | return RES_WRPRT; 670 | } 671 | DRESULT res = RES_OK; 672 | 673 | AcquireSPI lock(card); 674 | 675 | if (count > 1) 676 | { 677 | res = sdWriteSectors(pdrv, (const char *)buffer, sector, count) ? RES_OK : RES_ERROR; 678 | } 679 | else 680 | { 681 | res = sdWriteSector(pdrv, (const char *)buffer, sector) ? RES_OK : RES_ERROR; 682 | } 683 | return res; 684 | } 685 | 686 | DRESULT sd_disk_ioctl(uint8_t pdrv, uint8_t cmd, void *buff) 687 | { 688 | switch (cmd) 689 | { 690 | case CTRL_SYNC: 691 | { 692 | AcquireSPI lock(s_cards[pdrv]); 693 | if (sdSelectCard(pdrv)) 694 | { 695 | sdDeselectCard(pdrv); 696 | return RES_OK; 697 | } 698 | } 699 | return RES_ERROR; 700 | case GET_SECTOR_COUNT: 701 | *((unsigned long *)buff) = s_cards[pdrv]->sectors; 702 | return RES_OK; 703 | case GET_SECTOR_SIZE: 704 | *((WORD *)buff) = 512; 705 | return RES_OK; 706 | case GET_BLOCK_SIZE: 707 | *((uint32_t *)buff) = 1; 708 | return RES_OK; 709 | } 710 | return RES_PARERR; 711 | } 712 | 713 | /* 714 | Public methods 715 | * */ 716 | 717 | uint8_t sdcard_uninit(uint8_t pdrv) 718 | { 719 | ardu_sdcard_t *card = s_cards[pdrv]; 720 | if (pdrv >= _VOLUMES || card == NULL) 721 | { 722 | return 1; 723 | } 724 | ff_diskio_register(pdrv, NULL); 725 | s_cards[pdrv] = NULL; 726 | free(card); 727 | 728 | return 0; 729 | } 730 | 731 | uint8_t sdcard_init(uint8_t cs, SPIClass *spi, int hz) 732 | { 733 | 734 | uint8_t pdrv = 0xFF; 735 | if (ff_diskio_get_drive(&pdrv) != 0 || pdrv == 0xFF) 736 | { 737 | return pdrv; 738 | } 739 | 740 | ardu_sdcard_t *card = (ardu_sdcard_t *)malloc(sizeof(ardu_sdcard_t)); 741 | if (!card) 742 | { 743 | return 0xFF; 744 | } 745 | 746 | card->frequency = hz; 747 | card->spi = spi; 748 | card->ssPin = cs; 749 | 750 | card->supports_crc = true; 751 | card->type = CARD_NONE; 752 | card->status = STA_NOINIT; 753 | 754 | pinMode(card->ssPin, OUTPUT); 755 | digitalWrite(card->ssPin, HIGH); 756 | 757 | s_cards[pdrv] = card; 758 | 759 | static const ff_diskio_impl_t sd_impl = { 760 | .init = &sd_disk_initialize, 761 | .status = &sd_disk_status, 762 | .read = &sd_disk_read, 763 | .write = &sd_disk_write, 764 | .ioctl = &sd_disk_ioctl}; 765 | ff_diskio_register(pdrv, &sd_impl); 766 | 767 | return pdrv; 768 | } 769 | 770 | uint8_t sdcard_unmount(uint8_t pdrv) 771 | { 772 | ardu_sdcard_t *card = s_cards[pdrv]; 773 | if (pdrv >= _VOLUMES || card == NULL) 774 | { 775 | return 1; 776 | } 777 | card->status |= STA_NOINIT; 778 | card->type = CARD_NONE; 779 | 780 | TCHAR drv[3] = {_T(char('0' + pdrv)), _T(':'), _T('0')}; 781 | 782 | f_mount(NULL, drv, 0); 783 | return 0; 784 | } 785 | 786 | bool sdcard_mount(uint8_t pdrv) 787 | { 788 | ardu_sdcard_t *card = s_cards[pdrv]; 789 | if (pdrv >= _VOLUMES || card == NULL) 790 | { 791 | 792 | return false; 793 | } 794 | 795 | FATFS fs; 796 | TCHAR drv[3] = {_T(char('0' + pdrv)), _T(':'), _T('0')}; 797 | FRESULT res = f_mount(&fs, drv, 1); 798 | if (res != FR_OK) 799 | { 800 | return false; 801 | } 802 | AcquireSPI lock(card); 803 | return true; 804 | } 805 | 806 | uint32_t sdcard_num_sectors(uint8_t pdrv) 807 | { 808 | ardu_sdcard_t *card = s_cards[pdrv]; 809 | if (pdrv >= _VOLUMES || card == NULL) 810 | { 811 | return 0; 812 | } 813 | return card->sectors; 814 | } 815 | 816 | uint32_t sdcard_sector_size(uint8_t pdrv) 817 | { 818 | if (pdrv >= _VOLUMES || s_cards[pdrv] == NULL) 819 | { 820 | return 0; 821 | } 822 | return 512; 823 | } 824 | 825 | sdcard_type_t sdcard_type(uint8_t pdrv) 826 | { 827 | ardu_sdcard_t *card = s_cards[pdrv]; 828 | if (pdrv >= _VOLUMES || card == NULL) 829 | { 830 | return CARD_NONE; 831 | } 832 | return card->type; 833 | } -------------------------------------------------------------------------------- /src/fatfs/ccsbcs.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Unicode - Local code bidirectional converter (C)ChaN, 2015 */ 3 | /* (SBCS code pages) */ 4 | /*------------------------------------------------------------------------*/ 5 | /* 437 U.S. 6 | / 720 Arabic 7 | / 737 Greek 8 | / 771 KBL 9 | / 775 Baltic 10 | / 850 Latin 1 11 | / 852 Latin 2 12 | / 855 Cyrillic 13 | / 857 Turkish 14 | / 860 Portuguese 15 | / 861 Icelandic 16 | / 862 Hebrew 17 | / 863 Canadian French 18 | / 864 Arabic 19 | / 865 Nordic 20 | / 866 Russian 21 | / 869 Greek 2 22 | */ 23 | #ifndef ARDUINO_ARCH_NRF52840 24 | #include "ff.h" 25 | 26 | 27 | #if _CODE_PAGE == 437 28 | #define _TBLDEF 1 29 | static 30 | const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ 31 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 32 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 33 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 34 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 35 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 36 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 37 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 38 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 39 | }; 40 | 41 | #elif _CODE_PAGE == 720 42 | #define _TBLDEF 1 43 | static 44 | const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ 45 | 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, 46 | 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, 47 | 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, 48 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 49 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 50 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 51 | 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 52 | 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 53 | }; 54 | 55 | #elif _CODE_PAGE == 737 56 | #define _TBLDEF 1 57 | static 58 | const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ 59 | 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 60 | 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 61 | 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 62 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 63 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 64 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 65 | 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, 66 | 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 67 | }; 68 | 69 | #elif _CODE_PAGE == 771 70 | #define _TBLDEF 1 71 | static 72 | const WCHAR Tbl[] = { /* CP771(0x80-0xFF) to Unicode conversion table */ 73 | 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 74 | 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 75 | 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 76 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 77 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 78 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D, 79 | 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 80 | 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0 81 | }; 82 | 83 | #elif _CODE_PAGE == 775 84 | #define _TBLDEF 1 85 | static 86 | const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ 87 | 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, 88 | 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, 89 | 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, 90 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, 91 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, 92 | 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 93 | 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, 94 | 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 95 | }; 96 | 97 | #elif _CODE_PAGE == 850 98 | #define _TBLDEF 1 99 | static 100 | const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ 101 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 102 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 103 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 104 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 105 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 106 | 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 107 | 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, 108 | 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 109 | }; 110 | 111 | #elif _CODE_PAGE == 852 112 | #define _TBLDEF 1 113 | static 114 | const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ 115 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, 116 | 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, 117 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, 118 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, 119 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 120 | 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, 121 | 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, 122 | 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 123 | }; 124 | 125 | #elif _CODE_PAGE == 855 126 | #define _TBLDEF 1 127 | static 128 | const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ 129 | 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, 130 | 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, 131 | 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, 132 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, 133 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 134 | 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, 135 | 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, 136 | 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 137 | }; 138 | 139 | #elif _CODE_PAGE == 857 140 | #define _TBLDEF 1 141 | static 142 | const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ 143 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, 144 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, 145 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 146 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 147 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 148 | 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 149 | 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, 150 | 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 151 | }; 152 | 153 | #elif _CODE_PAGE == 860 154 | #define _TBLDEF 1 155 | static 156 | const WCHAR Tbl[] = { /* CP860(0x80-0xFF) to Unicode conversion table */ 157 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, 158 | 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, 159 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 160 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 161 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 162 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 163 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 164 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 165 | }; 166 | 167 | #elif _CODE_PAGE == 861 168 | #define _TBLDEF 1 169 | static 170 | const WCHAR Tbl[] = { /* CP861(0x80-0xFF) to Unicode conversion table */ 171 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, 172 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, 173 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 174 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 175 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 176 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 177 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 178 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 179 | }; 180 | 181 | #elif _CODE_PAGE == 862 182 | #define _TBLDEF 1 183 | static 184 | const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ 185 | 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 186 | 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 187 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 188 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 189 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 190 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 191 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 192 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 193 | }; 194 | 195 | #elif _CODE_PAGE == 863 196 | #define _TBLDEF 1 197 | static 198 | const WCHAR Tbl[] = { /* CP863(0x80-0xFF) to Unicode conversion table */ 199 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0, 200 | 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, 201 | 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB, 202 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 203 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 204 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 205 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219, 206 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 207 | }; 208 | 209 | #elif _CODE_PAGE == 864 210 | #define _TBLDEF 1 211 | static 212 | const WCHAR Tbl[] = { /* CP864(0x80-0xFF) to Unicode conversion table */ 213 | 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, 214 | 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000, 215 | 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, 216 | 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, 217 | 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, 218 | 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, 219 | 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, 220 | 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000 221 | }; 222 | 223 | #elif _CODE_PAGE == 865 224 | #define _TBLDEF 1 225 | static 226 | const WCHAR Tbl[] = { /* CP865(0x80-0xFF) to Unicode conversion table */ 227 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 228 | 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, 229 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, 230 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 231 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 232 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 233 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 234 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 235 | }; 236 | 237 | #elif _CODE_PAGE == 866 238 | #define _TBLDEF 1 239 | static 240 | const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ 241 | 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 242 | 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 243 | 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 244 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 245 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 246 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 247 | 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 248 | 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 249 | }; 250 | 251 | #elif _CODE_PAGE == 869 252 | #define _TBLDEF 1 253 | static 254 | const WCHAR Tbl[] = { /* CP869(0x80-0xFF) to Unicode conversion table */ 255 | 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, 256 | 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, 257 | 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, 258 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, 259 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, 260 | 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, 261 | 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, 262 | 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0 263 | }; 264 | 265 | #endif 266 | 267 | 268 | #if !_TBLDEF || !_USE_LFN 269 | #error This file is not needed at current configuration. Remove from the project. 270 | #endif 271 | 272 | 273 | 274 | 275 | WCHAR ff_convert ( /* Converted character, Returns zero on error */ 276 | WCHAR chr, /* Character code to be converted */ 277 | UINT dir /* 0: Unicode to OEM code, 1: OEM code to Unicode */ 278 | ) 279 | { 280 | WCHAR c; 281 | 282 | 283 | if (chr < 0x80) { /* ASCII */ 284 | c = chr; 285 | 286 | } else { 287 | if (dir) { /* OEM code to Unicode */ 288 | c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80]; 289 | 290 | } else { /* Unicode to OEM code */ 291 | for (c = 0; c < 0x80; c++) { 292 | if (chr == Tbl[c]) break; 293 | } 294 | c = (c + 0x80) & 0xFF; 295 | } 296 | } 297 | 298 | return c; 299 | } 300 | 301 | 302 | 303 | WCHAR ff_wtoupper ( /* Returns upper converted character */ 304 | WCHAR chr /* Unicode character to be upper converted (BMP only) */ 305 | ) 306 | { 307 | /* Compressed upper conversion table */ 308 | static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ 309 | /* Basic Latin */ 310 | 0x0061,0x031A, 311 | /* Latin-1 Supplement */ 312 | 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, 313 | /* Latin Extended-A */ 314 | 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, 315 | /* Latin Extended-B */ 316 | 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, 317 | 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, 318 | 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, 319 | /* IPA Extensions */ 320 | 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, 321 | /* Greek, Coptic */ 322 | 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, 323 | 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, 324 | 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, 325 | /* Cyrillic */ 326 | 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, 327 | /* Armenian */ 328 | 0x0561,0x0426, 329 | 330 | 0x0000 331 | }; 332 | static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ 333 | /* Phonetic Extensions */ 334 | 0x1D7D,0x0001,0x2C63, 335 | /* Latin Extended Additional */ 336 | 0x1E00,0x0196, 0x1EA0,0x015A, 337 | /* Greek Extended */ 338 | 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, 339 | 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, 340 | 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, 341 | 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, 342 | 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, 343 | /* Letterlike Symbols */ 344 | 0x214E,0x0001,0x2132, 345 | /* Number forms */ 346 | 0x2170,0x0210, 0x2184,0x0001,0x2183, 347 | /* Enclosed Alphanumerics */ 348 | 0x24D0,0x051A, 0x2C30,0x042F, 349 | /* Latin Extended-C */ 350 | 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, 351 | /* Coptic */ 352 | 0x2C80,0x0164, 353 | /* Georgian Supplement */ 354 | 0x2D00,0x0826, 355 | /* Full-width */ 356 | 0xFF41,0x031A, 357 | 358 | 0x0000 359 | }; 360 | const WCHAR *p; 361 | WCHAR bc, nc, cmd; 362 | 363 | 364 | p = chr < 0x1000 ? cvt1 : cvt2; 365 | for (;;) { 366 | bc = *p++; /* Get block base */ 367 | if (!bc || chr < bc) break; 368 | nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ 369 | if (chr < bc + nc) { /* In the block? */ 370 | switch (cmd) { 371 | case 0: chr = p[chr - bc]; break; /* Table conversion */ 372 | case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ 373 | case 2: chr -= 16; break; /* Shift -16 */ 374 | case 3: chr -= 32; break; /* Shift -32 */ 375 | case 4: chr -= 48; break; /* Shift -48 */ 376 | case 5: chr -= 26; break; /* Shift -26 */ 377 | case 6: chr += 8; break; /* Shift +8 */ 378 | case 7: chr -= 80; break; /* Shift -80 */ 379 | case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ 380 | } 381 | break; 382 | } 383 | if (!cmd) p += nc; 384 | } 385 | 386 | return chr; 387 | } 388 | 389 | #endif 390 | --------------------------------------------------------------------------------