Add base for USB FS (work in progress do not use)

This commit is contained in:
Luc 2021-02-28 09:52:08 +01:00
parent eaba22990a
commit ce24d85e8d
34 changed files with 5153 additions and 4 deletions

View File

@ -105,6 +105,11 @@
#define ESP_SDCARD_NOT_PRESENT 1 #define ESP_SDCARD_NOT_PRESENT 1
#define ESP_SDCARD_BUSY 2 #define ESP_SDCARD_BUSY 2
//USB state
#define ESP_USB_IDLE 0
#define ESP_USB_NOT_PRESENT 1
#define ESP_USB_BUSY 2
//Notifications //Notifications
#define ESP_NO_NOTIFICATION 0 #define ESP_NO_NOTIFICATION 0
#define ESP_PUSHOVER_NOTIFICATION 1 #define ESP_PUSHOVER_NOTIFICATION 1

View File

@ -26,7 +26,6 @@
#include <esp_ota_ops.h> #include <esp_ota_ops.h>
#endif //ARDUINO_ARCH_ESP32 #endif //ARDUINO_ARCH_ESP32
#define ESP_MAX_OPENHANDLE 4
File tFile_handle[ESP_MAX_OPENHANDLE]; File tFile_handle[ESP_MAX_OPENHANDLE];
bool ESP_FileSystem::_started = false; bool ESP_FileSystem::_started = false;

View File

@ -24,7 +24,6 @@
#include "../../core/genLinkedList.h" #include "../../core/genLinkedList.h"
#include <time.h> #include <time.h>
#define ESP_MAX_SD_OPENHANDLE 4
#if ((SD_DEVICE == ESP_SD_NATIVE) || (SD_DEVICE == ESP_SDFAT)) && defined (ARDUINO_ARCH_ESP8266) #if ((SD_DEVICE == ESP_SD_NATIVE) || (SD_DEVICE == ESP_SDFAT)) && defined (ARDUINO_ARCH_ESP8266)
#define FS_NO_GLOBALS #define FS_NO_GLOBALS
#define NO_GLOBAL_SD #define NO_GLOBAL_SD

View File

@ -0,0 +1,209 @@
/*
esp_usb.cpp - ESP3D USB support class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with This code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../../include/esp3d_config.h"
#ifdef USB_DEVICE
#include "esp_usb.h"
#include "../../core/genLinkedList.h"
#include <time.h>
//File tUSBFile_handle[ESP_MAX_SD_OPENHANDLE];
bool ESP_USB::_started = false;
uint8_t ESP_USB::_state = ESP_USBCARD_NOT_PRESENT;
uint8_t ESP_USB::_spi_speed_divider = 1;
bool ESP_USB::_sizechanged = true;
uint8_t ESP_USB::setState(uint8_t flag)
{
_state = flag;
return _state;
}
bool ESP_USB::accessSD()
{
bool res = false;
#if SD_DEVICE_CONNECTION == ESP_SHARED_SD
//need to send the current state to avoid
res = (digitalRead(ESP_FLAG_SHARED_SD_PIN) == ESP_FLAG_SHARED_SD_VALUE);
if (!res) {
digitalWrite(ESP_FLAG_SHARED_SD_PIN, ESP_FLAG_SHARED_SD_VALUE);
}
#endif //SD_DEVICE_CONNECTION == ESP_SHARED_SD
return res;
}
void ESP_USB::releaseSD()
{
#if SD_DEVICE_CONNECTION == ESP_SHARED_SD
digitalWrite(ESP_FLAG_SHARED_SD_PIN, !ESP_FLAG_SHARED_SD_VALUE);
#endif //SD_DEVICE_CONNECTION == ESP_SHARED_SD
}
void ESP_USB::handle()
{
}
//helper to format size to readable string
String & ESP_USB::formatBytes (uint64_t bytes)
{
static String res;
if (bytes < 1024) {
res = String ((uint16_t)bytes) + " B";
} else if (bytes < (1024 * 1024) ) {
res = String ((float)(bytes / 1024.0),2) + " KB";
} else if (bytes < (1024 * 1024 * 1024) ) {
res = String ((float)(bytes / 1024.0 / 1024.0),2) + " MB";
} else {
res = String ((float)(bytes / 1024.0 / 1024.0 / 1024.0),2) + " GB";
}
return res;
}
ESP_USBFile::ESP_USBFile(const char * name, const char * filename, bool isdir, size_t size)
{
_isdir = isdir;
_dirlist = "";
_index = -1;
_filename = filename;
_name = name;
_lastwrite = 0;
_iswritemode = false;
_size = size;
}
ESP_USBFile::~ESP_USBFile()
{
//log_esp3d("Destructor %s index %d",(_isdir)?"Dir":"File", _index);
}
ESP_USBFile::operator bool() const
{
if ((_index != -1) || (_filename.length() > 0)) {
//log_esp3d("Bool yes %d %d",_index, _filename.length());
return true;
} else {
return false;
}
}
bool ESP_USBFile::isOpen()
{
return !(_index == -1);
}
const char* ESP_USBFile::name() const
{
return _name.c_str();
}
const char* ESP_USBFile::filename() const
{
return _filename.c_str();
}
bool ESP_USBFile::isDirectory()
{
return _isdir;
}
size_t ESP_USBFile::size()
{
return _size;
}
time_t ESP_USBFile::getLastWrite()
{
return _lastwrite;
}
int ESP_USBFile::available()
{
if (_index == -1 || _isdir) {
return 0;
}
return tSDFile_handle[_index].available();
}
size_t ESP_USBFile::write(uint8_t i)
{
if ((_index == -1) || _isdir) {
return 0;
}
return tSDFile_handle[_index].write (i);
}
size_t ESP_USBFile::write(const uint8_t *buf, size_t size)
{
if ((_index == -1) || _isdir) {
return 0;
}
return tSDFile_handle[_index].write (buf, size);
}
int ESP_USBFile::read()
{
if ((_index == -1) || _isdir) {
return -1;
}
return tSDFile_handle[_index].read();
}
size_t ESP_USBFile::read(uint8_t* buf, size_t size)
{
if ((_index == -1) || _isdir) {
return -1;
}
return tSDFile_handle[_index].read(buf, size);
}
void ESP_USBFile::flush()
{
if ((_index == -1) || _isdir) {
return;
}
tSDFile_handle[_index].flush();
}
ESP_USBFile& ESP_USBFile::operator=(const ESP_USBFile & other)
{
//log_esp3d("Copy %s", other._filename.c_str());
_isdir = other._isdir;
_index = other._index;
_filename = other._filename;
_name = other._name;
_size = other._size;
_iswritemode = other._iswritemode;
_dirlist = other._dirlist;
_lastwrite = other._lastwrite;
return *this;
}
bool ESP_USB::setSPISpeedDivider(uint8_t speeddivider)
{
if (speeddivider > 0) {
_spi_speed_divider = speeddivider;
return true;
}
return false;
}
#endif //SD_DEVICE

View File

@ -0,0 +1,103 @@
/*
esp_usb.h - ESP3D USB support class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with This code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _ESP_USB_H
#define _ESP_USB_H
#include "../../include/esp3d_config.h"
#include "../../core/esp3doutput.h"
#include <time.h>
#define ESP_USB_FS_HEADER "/USB"
#define ESP_MAX_USB_OPENHANDLE 4
class ESP_USBFile
{
public:
ESP_USBFile(void * handle = nullptr, bool isdir =false, bool iswritemode = false, const char * path = nullptr);
ESP_USBFile(const char * name, const char * filename, bool isdir = true, size_t size =0);
~ESP_USBFile();
operator bool() const;
bool isDirectory();
bool seek(uint32_t pos, uint8_t mode = ESP_SEEK_SET);
const char* name() const;
const char* shortname() const;
const char* filename() const;
void close();
bool isOpen();
ESP_USBFile & operator=(const ESP_USBFile & other);
size_t size();
time_t getLastWrite();
int available();
size_t write(uint8_t i);
size_t write(const uint8_t *buf, size_t size);
int read();
size_t read(uint8_t* buf, size_t size);
void flush();
ESP_USBFile openNextFile();
private:
String _dirlist;
bool _isdir;
bool _iswritemode;
int8_t _index;
String _filename;
String _name;
size_t _size;
time_t _lastwrite;
};
class ESP_SD
{
public:
static String & formatBytes (uint64_t bytes);
static bool begin();
static bool accessSD();
static void releaseSD();
static void handle();
static void end();
static uint8_t getState(bool refresh);
static uint8_t setState(uint8_t state);
static uint64_t totalBytes();
static uint64_t usedBytes();
static uint64_t freeBytes();
static uint maxPathLength();
static const char * FilesystemName();
static bool format(ESP3DOutput * output = nullptr);
static ESP_USBFile open(const char* path, uint8_t mode = ESP_FILE_READ);
static bool exists(const char* path);
static bool remove(const char *path);
static bool mkdir(const char *path);
static bool rmdir(const char *path);
static bool rename(const char *oldpath, const char *newpath);
static void closeAll();
static uint8_t getSPISpeedDivider()
{
return _spi_speed_divider;
}
static bool setSPISpeedDivider(uint8_t speeddivider);
private:
static bool _started;
static uint8_t _state;
static uint8_t _spi_speed_divider;
static bool _sizechanged;
};
#endif //_ESP_USB_H

View File

@ -235,14 +235,14 @@ ESP_File::ESP_File(void* handle, bool isdir, bool iswritemode, const char * path
set = true; set = true;
} else { } else {
log_esp3d("File %d busy", i); log_esp3d("File %d busy", i);
log_esp3d(tFile_handle[i].name()); log_esp3d("%s", String(tFile_handle[i].name()).c_str());
} }
} }
if(!set) { if(!set) {
log_esp3d("No handle available"); log_esp3d("No handle available");
#if defined(ESP_DEBUG_FEATURE) #if defined(ESP_DEBUG_FEATURE)
for (uint8_t i=0; (i < ESP_MAX_OPENHANDLE) ; i++) { for (uint8_t i=0; (i < ESP_MAX_OPENHANDLE) ; i++) {
log_esp3d(tFile_handle[i].name()); log_esp3d("%s",String(tFile_handle[i].name()).c_str());
} }
#endif #endif
} }

View File

@ -0,0 +1,843 @@
/*
usb_ch376s.cpp - ESP3D usb support class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with This code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../../../include/esp3d_config.h"
#if defined (ARDUINO_ARCH_ESP32) && defined(USB_DEVICE)
#if (USB_DEVICE == ESP_CH376S)
#include "../esp_sd.h"
#include "../../../core/genLinkedList.h"
#include "../../../core/settings_esp3d.h"
#include <Ch376msc.h>
extern File tSDFile_handle[ESP_MAX_SD_OPENHANDLE];
//Max Freq Working
SdFat SD;
void dateTime (uint16_t* date, uint16_t* dtime)
{
struct tm tmstruct;
time_t now;
time (&now);
localtime_r (&now, &tmstruct);
*date = FAT_DATE ( (tmstruct.tm_year) + 1900, ( tmstruct.tm_mon) + 1, tmstruct.tm_mday);
*dtime = FAT_TIME (tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
}
time_t getDateTimeFile(File & filehandle)
{
static time_t dt = 0;
struct tm timefile;
dir_t d;
if(filehandle) {
if (filehandle.dirEntry(&d)) {
timefile.tm_year = FAT_YEAR(d.lastWriteDate) - 1900;
timefile.tm_mon = FAT_MONTH(d.lastWriteDate) - 1;
timefile.tm_mday = FAT_DAY(d.lastWriteDate);
timefile.tm_hour = FAT_HOUR(d.lastWriteTime);
timefile.tm_min = FAT_MINUTE(d.lastWriteTime);
timefile.tm_sec = FAT_SECOND(d.lastWriteTime);
timefile.tm_isdst = -1;
dt = mktime(&timefile);
if (dt == -1) {
log_esp3d("mktime failed");
}
} else {
log_esp3d("stat file failed");
}
} else {
log_esp3d("check stat file failed");
}
return dt;
}
uint8_t ESP_SD::getState(bool refresh)
{
#if defined(ESP_SD_DETECT_PIN) && ESP_SD_DETECT_PIN != -1
//no need to go further if SD detect is not correct
if (!((digitalRead (ESP_SD_DETECT_PIN) == ESP_SD_DETECT_VALUE) ? true : false)) {
_state = ESP_SDCARD_NOT_PRESENT;
return _state;
}
#endif //ESP_SD_DETECT_PIN
//if busy doing something return state
if (!((_state == ESP_SDCARD_NOT_PRESENT) || _state == ESP_SDCARD_IDLE)) {
return _state;
}
if (!refresh) {
return _state; //to avoid refresh=true + busy to reset SD and waste time
}
//SD is idle or not detected, let see if still the case
_state = ESP_SDCARD_NOT_PRESENT;
bool isactive = accessSD();
log_esp3d("Spi : CS: %d, Miso: %d, Mosi: %d, SCK: %d",ESP_SD_CS_PIN!=-1?ESP_SD_CS_PIN:SS, ESP_SD_MISO_PIN!=-1?ESP_SD_MISO_PIN:MISO, ESP_SD_MOSI_PIN!=-1?ESP_SD_MOSI_PIN:MOSI, ESP_SD_SCK_PIN!=-1?ESP_SD_SCK_PIN:SCK);
//refresh content if card was removed
if (SD.begin((ESP_SD_CS_PIN == -1)?SS:ESP_SD_CS_PIN, SD_SCK_MHZ(FREQMZ/_spi_speed_divider))) {
if (SD.card()->cardSize() > 0 ) {
_state = ESP_SDCARD_IDLE;
}
}
if (!isactive) {
releaseSD();
}
return _state;
}
bool ESP_SD::begin()
{
#if (ESP_SD_CS_PIN != -1) || (ESP_SD_MISO_PIN != -1) || (ESP_SD_MOSI_PIN != -1) || (ESP_SD_SCK_PIN != -1)
SPI.begin(ESP_SD_SCK_PIN, ESP_SD_MISO_PIN, ESP_SD_MOSI_PIN, ESP_SD_CS_PIN);
#endif
_started = true;
_state = ESP_SDCARD_NOT_PRESENT;
_spi_speed_divider = Settings_ESP3D::read_byte(ESP_SD_SPEED_DIV);
//sanity check
if (_spi_speed_divider <= 0) {
_spi_speed_divider = 1;
}
#ifdef SD_TIMESTAMP_FEATURE
//set callback to get time on files on SD
SdFile::dateTimeCallback (dateTime);
#endif //SD_TIMESTAMP_FEATURE
//Setup pins
#if defined(ESP_SD_DETECT_PIN) && ESP_SD_DETECT_PIN != -1
pinMode (ESP_SD_DETECT_PIN, INPUT);
#endif //ESP_SD_DETECT_PIN
#if SD_DEVICE_CONNECTION == ESP_SHARED_SD
#if defined(ESP_FLAG_SHARED_SD_PIN) && ESP_FLAG_SHARED_SD_PIN != -1
pinMode (ESP_FLAG_SHARED_SD_PIN, OUTPUT);
digitalWrite(ESP_FLAG_SHARED_SD_PIN, !ESP_FLAG_SHARED_SD_VALUE);
#endif //ESP_FLAG_SHARED_SD_PIN
#endif //SD_DEVICE_CONNECTION == ESP_SHARED_SD
return _started;
}
void ESP_SD::end()
{
_state = ESP_SDCARD_NOT_PRESENT;
_started = false;
}
uint64_t ESP_SD::totalBytes()
{
uint64_t volTotal = SD.vol()->clusterCount();
uint8_t blocks = SD.vol()->blocksPerCluster();
return volTotal * blocks * 512;
}
uint64_t ESP_SD::usedBytes()
{
if(freeBytes() >totalBytes() ) {
_sizechanged = true;
}
return totalBytes() - freeBytes();
}
uint ESP_SD::maxPathLength()
{
return 255;
}
uint64_t ESP_SD::freeBytes()
{
static uint64_t volFree;
if (_sizechanged) {
volFree = SD.vol()->freeClusterCount();
_sizechanged = false;
}
uint8_t blocks = SD.vol()->blocksPerCluster();
return volFree * blocks * 512;
}
bool ESP_SD::rename(const char *oldpath, const char *newpath)
{
return SD.rename(oldpath,newpath);
}
// strings needed in file system structures
#define noName "NO NAME "
#define fat16str "FAT16 "
#define fat32str "FAT32 "
// constants for file system structure
#define BU16 128
#define BU32 8192
#define ERASE_SIZE 262144L
//------------------------------------------------------------------------------
// write cached block to the card
uint8_t writeCache(uint32_t lbn, Sd2Card & card, cache_t & cache)
{
return card.writeBlock(lbn, cache.data);
}
//------------------------------------------------------------------------------
// initialize appropriate sizes for SD capacity
bool initSizes(uint32_t cardCapacityMB, uint8_t & sectorsPerCluster, uint8_t & numberOfHeads, uint8_t & sectorsPerTrack)
{
if (cardCapacityMB <= 6) {
return false;
} else if (cardCapacityMB <= 16) {
sectorsPerCluster = 2;
} else if (cardCapacityMB <= 32) {
sectorsPerCluster = 4;
} else if (cardCapacityMB <= 64) {
sectorsPerCluster = 8;
} else if (cardCapacityMB <= 128) {
sectorsPerCluster = 16;
} else if (cardCapacityMB <= 1024) {
sectorsPerCluster = 32;
} else if (cardCapacityMB <= 32768) {
sectorsPerCluster = 64;
} else {
// SDXC cards
sectorsPerCluster = 128;
}
// set fake disk geometry
sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63;
if (cardCapacityMB <= 16) {
numberOfHeads = 2;
} else if (cardCapacityMB <= 32) {
numberOfHeads = 4;
} else if (cardCapacityMB <= 128) {
numberOfHeads = 8;
} else if (cardCapacityMB <= 504) {
numberOfHeads = 16;
} else if (cardCapacityMB <= 1008) {
numberOfHeads = 32;
} else if (cardCapacityMB <= 2016) {
numberOfHeads = 64;
} else if (cardCapacityMB <= 4032) {
numberOfHeads = 128;
} else {
numberOfHeads = 255;
}
return true;
}
//------------------------------------------------------------------------------
// zero cache and optionally set the sector signature
void clearCache(uint8_t addSig, cache_t & cache)
{
memset(&cache, 0, sizeof(cache));
if (addSig) {
cache.mbr.mbrSig0 = BOOTSIG0;
cache.mbr.mbrSig1 = BOOTSIG1;
}
}
//------------------------------------------------------------------------------
// zero FAT and root dir area on SD
bool clearFatDir(uint32_t bgn, uint32_t count, Sd2Card & card, cache_t & cache, ESP3DOutput * output)
{
clearCache(false, cache);
if (!card.writeStart(bgn, count)) {
return false;
}
for (uint32_t i = 0; i < count; i++) {
if ((i & 0XFF) == 0) {
if (output) {
output->print(".");
}
}
if (!card.writeData(cache.data)) {
return false;
}
}
if (!card.writeStop()) {
return false;
}
return true;
}
//------------------------------------------------------------------------------
// return cylinder number for a logical block number
uint16_t lbnToCylinder(uint32_t lbn, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
{
return lbn / (numberOfHeads * sectorsPerTrack);
}
//------------------------------------------------------------------------------
// return head number for a logical block number
uint8_t lbnToHead(uint32_t lbn, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
{
return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
}
//------------------------------------------------------------------------------
// return sector number for a logical block number
uint8_t lbnToSector(uint32_t lbn, uint8_t sectorsPerTrack)
{
return (lbn % sectorsPerTrack) + 1;
}
//------------------------------------------------------------------------------
// format and write the Master Boot Record
bool writeMbr(Sd2Card & card, cache_t & cache, uint8_t partType, uint32_t relSector, uint32_t partSize, uint8_t numberOfHeads, uint8_t sectorsPerTrack)
{
clearCache(true, cache);
part_t* p = cache.mbr.part;
p->boot = 0;
uint16_t c = lbnToCylinder(relSector, numberOfHeads, sectorsPerTrack);
if (c > 1023) {
return false;
}
p->beginCylinderHigh = c >> 8;
p->beginCylinderLow = c & 0XFF;
p->beginHead = lbnToHead(relSector, numberOfHeads, sectorsPerTrack);
p->beginSector = lbnToSector(relSector, sectorsPerTrack);
p->type = partType;
uint32_t endLbn = relSector + partSize - 1;
c = lbnToCylinder(endLbn,numberOfHeads, sectorsPerTrack);
if (c <= 1023) {
p->endCylinderHigh = c >> 8;
p->endCylinderLow = c & 0XFF;
p->endHead = lbnToHead(endLbn, numberOfHeads, sectorsPerTrack);
p->endSector = lbnToSector(endLbn, sectorsPerTrack);
} else {
// Too big flag, c = 1023, h = 254, s = 63
p->endCylinderHigh = 3;
p->endCylinderLow = 255;
p->endHead = 254;
p->endSector = 63;
}
p->firstSector = relSector;
p->totalSectors = partSize;
if (!writeCache(0, card, cache)) {
return false;
}
return true;
}
//------------------------------------------------------------------------------
// generate serial number from card size and micros since boot
uint32_t volSerialNumber(uint32_t cardSizeBlocks)
{
return (cardSizeBlocks << 8) + micros();
}
// format the SD as FAT16
bool makeFat16(uint32_t & dataStart, Sd2Card & card, cache_t & cache, uint8_t numberOfHeads, uint8_t sectorsPerTrack, uint32_t cardSizeBlocks, uint8_t sectorsPerCluster, uint32_t &relSector, uint32_t partSize, uint8_t & partType, uint32_t &fatSize, uint32_t &fatStart, uint16_t reservedSectors, ESP3DOutput * output)
{
uint32_t nc;
for (dataStart = 2 * BU16;; dataStart += BU16) {
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 255)/256;
uint32_t r = BU16 + 1 + 2 * fatSize + 32;
if (dataStart < r) {
continue;
}
relSector = dataStart - r + BU16;
break;
}
// check valid cluster count for FAT16 volume
if (nc < 4085 || nc >= 65525) {
return false;
}
reservedSectors = 1;
fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
if (partSize < 32680) {
partType = 0X01;
} else if (partSize < 65536) {
partType = 0X04;
} else {
partType = 0X06;
}
// write MBR
if (!writeMbr(card, cache, partType, relSector, partSize, numberOfHeads, sectorsPerTrack)) {
return false;
}
clearCache(true, cache);
fat_boot_t* pb = &cache.fbs;
pb->jump[0] = 0XEB;
pb->jump[1] = 0X00;
pb->jump[2] = 0X90;
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
pb->oemId[i] = ' ';
}
pb->bytesPerSector = 512;
pb->sectorsPerCluster = sectorsPerCluster;
pb->reservedSectorCount = reservedSectors;
pb->fatCount = 2;
pb->rootDirEntryCount = 512;
pb->mediaType = 0XF8;
pb->sectorsPerFat16 = fatSize;
pb->sectorsPerTrack = sectorsPerTrack;
pb->headCount = numberOfHeads;
pb->hidddenSectors = relSector;
pb->totalSectors32 = partSize;
pb->driveNumber = 0X80;
pb->bootSignature = EXTENDED_BOOT_SIG;
pb->volumeSerialNumber = volSerialNumber(cardSizeBlocks);
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType));
// write partition boot sector
if (!writeCache(relSector, card, cache)) {
return false;
}
// clear FAT and root directory
clearFatDir(fatStart, dataStart - fatStart, card, cache, output);
clearCache(false, cache);
cache.fat16[0] = 0XFFF8;
cache.fat16[1] = 0XFFFF;
// write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart, card, cache)
|| !writeCache(fatStart + fatSize, card, cache)) {
return false;
}
return true;
}
// format the SD as FAT32
bool makeFat32(uint32_t & dataStart, Sd2Card & card, cache_t & cache, uint8_t numberOfHeads, uint8_t sectorsPerTrack, uint32_t cardSizeBlocks, uint8_t sectorsPerCluster, uint32_t &relSector, uint32_t partSize, uint8_t & partType, uint32_t &fatSize, uint32_t &fatStart, uint16_t reservedSectors, ESP3DOutput * output)
{
uint32_t nc;
relSector = BU32;
for (dataStart = 2 * BU32;; dataStart += BU32) {
nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
fatSize = (nc + 2 + 127)/128;
uint32_t r = relSector + 9 + 2 * fatSize;
if (dataStart >= r) {
break;
}
}
// error if too few clusters in FAT32 volume
if (nc < 65525) {
return false;
}
reservedSectors = dataStart - relSector - 2 * fatSize;
fatStart = relSector + reservedSectors;
partSize = nc * sectorsPerCluster + dataStart - relSector;
// type depends on address of end sector
// max CHS has lbn = 16450560 = 1024*255*63
if ((relSector + partSize) <= 16450560) {
// FAT32
partType = 0X0B;
} else {
// FAT32 with INT 13
partType = 0X0C;
}
if (!writeMbr(card, cache, partType, relSector, partSize, numberOfHeads, sectorsPerTrack)) {
return false;
}
clearCache(true, cache);
fat32_boot_t* pb = &cache.fbs32;
pb->jump[0] = 0XEB;
pb->jump[1] = 0X00;
pb->jump[2] = 0X90;
for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
pb->oemId[i] = ' ';
}
pb->bytesPerSector = 512;
pb->sectorsPerCluster = sectorsPerCluster;
pb->reservedSectorCount = reservedSectors;
pb->fatCount = 2;
pb->mediaType = 0XF8;
pb->sectorsPerTrack = sectorsPerTrack;
pb->headCount = numberOfHeads;
pb->hidddenSectors = relSector;
pb->totalSectors32 = partSize;
pb->sectorsPerFat32 = fatSize;
pb->fat32RootCluster = 2;
pb->fat32FSInfo = 1;
pb->fat32BackBootBlock = 6;
pb->driveNumber = 0X80;
pb->bootSignature = EXTENDED_BOOT_SIG;
pb->volumeSerialNumber = volSerialNumber(cardSizeBlocks);
memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
// write partition boot sector and backup
if (!writeCache(relSector, card, cache)
|| !writeCache(relSector + 6, card, cache)) {
return false;
}
clearCache(true, cache);
// write extra boot area and backup
if (!writeCache(relSector + 2, card, cache)
|| !writeCache(relSector + 8, card, cache)) {
return false;
}
fat32_fsinfo_t* pf = &cache.fsinfo;
pf->leadSignature = FSINFO_LEAD_SIG;
pf->structSignature = FSINFO_STRUCT_SIG;
pf->freeCount = 0XFFFFFFFF;
pf->nextFree = 0XFFFFFFFF;
// write FSINFO sector and backup
if (!writeCache(relSector + 1, card, cache)
|| !writeCache(relSector + 7, card, cache)) {
return false;
}
clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster, card, cache, output);
clearCache(false, cache);
cache.fat32[0] = 0x0FFFFFF8;
cache.fat32[1] = 0x0FFFFFFF;
cache.fat32[2] = 0x0FFFFFFF;
// write first block of FAT and backup for reserved clusters
if (!writeCache(fatStart, card, cache)
|| !writeCache(fatStart + fatSize, card, cache)) {
return false;
}
return true;
}
bool eraseCard(Sd2Card & card, cache_t & cache, uint32_t cardSizeBlocks, ESP3DOutput * output)
{
uint32_t firstBlock = 0;
uint32_t lastBlock = 0;
//uint16_t n = 0;
if (output) {
output->printMSG("Erasing ", false);
}
do {
lastBlock = firstBlock + ERASE_SIZE - 1;
if (lastBlock >= cardSizeBlocks) {
lastBlock = cardSizeBlocks - 1;
}
if (!card.erase(firstBlock, lastBlock)) {
return false;
}
if (output) {
output->print(".");
}
firstBlock += ERASE_SIZE;
} while (firstBlock < cardSizeBlocks);
if (!card.readBlock(0, cache.data)) {
return false;
}
if (output) {
output->printLN("");
}
return true;
}
bool formatCard(uint32_t & dataStart, Sd2Card & card,
cache_t & cache, uint8_t numberOfHeads,
uint8_t sectorsPerTrack, uint32_t cardSizeBlocks,
uint8_t sectorsPerCluster, uint32_t &relSector,
uint32_t partSize, uint8_t & partType,
uint32_t &fatSize, uint32_t &fatStart,
uint32_t cardCapacityMB, uint16_t reservedSectors, ESP3DOutput * output)
{
initSizes(cardCapacityMB, sectorsPerCluster, numberOfHeads, sectorsPerTrack);
if (card.type() != SD_CARD_TYPE_SDHC) {
if (output) {
output->printMSG("Formating FAT16 ");
}
if(!makeFat16(dataStart, card, cache, numberOfHeads, sectorsPerTrack, cardSizeBlocks, sectorsPerCluster, relSector, partSize, partType, fatSize, fatStart, reservedSectors, output)) {
return false;
}
} else {
if (output) {
output->printMSG("Formating FAT32 ", false);
}
if(!makeFat32(dataStart, card, cache, numberOfHeads, sectorsPerTrack, cardSizeBlocks, sectorsPerCluster, relSector, partSize, partType, fatSize, fatStart, reservedSectors, output)) {
return false;
}
}
if (output) {
output->printLN("");
}
return true;
}
bool ESP_SD::format(ESP3DOutput * output)
{
if (ESP_SD::getState(true) == ESP_SDCARD_IDLE) {
Sd2Card card;
uint32_t cardSizeBlocks;
uint32_t cardCapacityMB;
// cache for SD block
cache_t cache;
// MBR information
uint8_t partType = 0;
uint32_t relSector = 0;
uint32_t partSize = 0;
// Fake disk geometry
uint8_t numberOfHeads = 0;
uint8_t sectorsPerTrack = 0;
// FAT parameters
uint16_t reservedSectors = 0;
uint8_t sectorsPerCluster = 0;
uint32_t fatStart = 0;
uint32_t fatSize = 0;
uint32_t dataStart = 0;
if (!card.begin((ESP_SD_CS_PIN == -1)?SS:ESP_SD_CS_PIN, SD_SCK_MHZ(FREQMZ/_spi_speed_divider))) {
return false;
}
cardSizeBlocks = card.cardSize();
if (cardSizeBlocks == 0) {
return false;
}
cardCapacityMB = (cardSizeBlocks + 2047)/2048;
if (output) {
String s = "Capacity detected :" + String((1.048576*cardCapacityMB)/1024) + "GB";
output->printMSG(s.c_str());
}
if (!eraseCard(card, cache, cardSizeBlocks, output)) {
return false;
}
if (!formatCard(dataStart, card, cache, numberOfHeads,
sectorsPerTrack, cardSizeBlocks,
sectorsPerCluster, relSector, partSize, partType,
fatSize, fatStart, cardCapacityMB, reservedSectors,output)) {
return false;
}
return true;
}
return false;
}
ESP_SDFile ESP_SD::open(const char* path, uint8_t mode)
{
//do some check
if(((strcmp(path,"/") == 0) && ((mode == ESP_FILE_WRITE) || (mode == ESP_FILE_APPEND))) || (strlen(path) == 0)) {
_sizechanged = true;
return ESP_SDFile();
}
// path must start by '/'
if (path[0] != '/') {
return ESP_SDFile();
}
if (mode != ESP_FILE_READ) {
//check container exists
String p = path;
p.remove(p.lastIndexOf('/') +1);
if (!exists(p.c_str())) {
log_esp3d("Error opening: %s", path);
return ESP_SDFile();
}
}
File tmp = SD.open(path, (mode == ESP_FILE_READ)?FILE_READ:(mode == ESP_FILE_WRITE)?FILE_WRITE:FILE_WRITE);
ESP_SDFile esptmp(&tmp, tmp.isDir(),(mode == ESP_FILE_READ)?false:true, path);
return esptmp;
}
bool ESP_SD::exists(const char* path)
{
bool res = false;
//root should always be there if started
if (strcmp(path, "/") == 0) {
return _started;
}
res = SD.exists(path);
if (!res) {
ESP_SDFile root = ESP_SD::open(path, ESP_FILE_READ);
if (root) {
res = root.isDirectory();
}
}
return res;
}
bool ESP_SD::remove(const char *path)
{
_sizechanged = true;
return SD.remove(path);
}
bool ESP_SD::mkdir(const char *path)
{
return SD.mkdir(path);
}
bool ESP_SD::rmdir(const char *path)
{
if (!exists(path)) {
return false;
}
bool res = true;
GenLinkedList<String > pathlist;
String p = path;
pathlist.push(p);
while (pathlist.count() > 0) {
File dir = SD.open(pathlist.getLast().c_str());
dir.rewindDirectory();
File f = dir.openNextFile();
bool candelete = true;
while (f) {
if (f.isDir()) {
candelete = false;
String newdir;
char tmp[255];
f.getName(tmp,254);
newdir = tmp;
pathlist.push(newdir);
f.close();
f = File();
} else {
char tmp[255];
f.getName(tmp,254);
_sizechanged = true;
SD.remove(tmp);
f.close();
f = dir.openNextFile();
}
}
if (candelete) {
if (pathlist.getLast() !="/") {
res = SD.rmdir(pathlist.getLast().c_str());
}
pathlist.pop();
}
dir.close();
}
p = String();
log_esp3d("count %d", pathlist.count());
return res;
}
void ESP_SD::closeAll()
{
for (uint8_t i = 0; i < ESP_MAX_SD_OPENHANDLE; i++) {
tSDFile_handle[i].close();
tSDFile_handle[i] = File();
}
}
bool ESP_SDFile::seek(uint32_t pos, uint8_t mode)
{
if (mode == SeekEnd) {
return tSDFile_handle[_index].seek(-pos); //based on SDFS comment
}
return tSDFile_handle[_index].seek(pos);
}
ESP_SDFile::ESP_SDFile(void* handle, bool isdir, bool iswritemode, const char * path)
{
_isdir = isdir;
_dirlist = "";
_index = -1;
_filename = "";
_name = "";
_lastwrite = 0 ;
_iswritemode = iswritemode;
_size = 0;
if (!handle) {
return ;
}
bool set =false;
for (uint8_t i=0; (i < ESP_MAX_SD_OPENHANDLE) && !set; i++) {
if (!tSDFile_handle[i]) {
tSDFile_handle[i] = *((File*)handle);
//filename
char tmp[255];
tSDFile_handle[i].getName(tmp,254);
_filename = path;
//name
_name = tmp;
if (_name.endsWith("/")) {
_name.remove( _name.length() - 1,1);
_isdir = true;
}
if (_name[0] == '/') {
_name.remove( 0, 1);
}
int pos = _name.lastIndexOf('/');
if (pos != -1) {
_name.remove( 0, pos+1);
}
if (_name.length() == 0) {
_name = "/";
}
//size
_size = tSDFile_handle[i].size();
//time
if (!_isdir && !iswritemode) {
_lastwrite = getDateTimeFile(tSDFile_handle[i]);
} else {
//no need date time for directory
_lastwrite = 0;
}
_index = i;
//log_esp3d("Opening File at index %d",_index);
set = true;
}
}
}
//todo need also to add short filename
const char* ESP_SDFile::shortname() const
{
static char sname[13];
File ftmp = SD.open(_filename.c_str());
if (ftmp) {
ftmp.getSFN(sname);
ftmp.close();
return sname;
} else {
return _name.c_str();
}
}
void ESP_SDFile::close()
{
if (_index != -1) {
//log_esp3d("Closing File at index %d", _index);
tSDFile_handle[_index].close();
//reopen if mode = write
//udate size + date
if (_iswritemode && !_isdir) {
File ftmp = SD.open(_filename.c_str());
if (ftmp) {
_size = ftmp.size();
_lastwrite = getDateTimeFile(ftmp);
ftmp.close();
}
}
tSDFile_handle[_index] = File();
//log_esp3d("Closing File at index %d",_index);
_index = -1;
}
}
ESP_SDFile ESP_SDFile::openNextFile()
{
if ((_index == -1) || !_isdir) {
log_esp3d("openNextFile failed");
return ESP_SDFile();
}
File tmp = tSDFile_handle[_index].openNextFile();
if (tmp) {
char tmps[255];
tmp.getName(tmps,254);
log_esp3d("tmp name :%s %s", tmps, (tmp.isDir())?"isDir":"isFile");
String s = _filename ;
if (s!="/") {
s+="/";
}
s += tmps;
ESP_SDFile esptmp(&tmp, tmp.isDir(),false, s.c_str());
esptmp.close();
return esptmp;
}
return ESP_SDFile();
}
const char * ESP_SD::FilesystemName()
{
return "SDFat";
}
#endif //SD_DEVICE == ESP_SD_NATIVE
#endif //ARCH_ESP32 && SD_DEVICE

28
libraries/Ch376msc-1.4.4/.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2019 György Kovács
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,332 @@
# Arduino library for CH376 / CH375 file manager control chip
Supports read/write files to USB flash drive or SD card (CH375 only support USB flash drive).
>Why use this chip if there is already a library to handle the SD card and it is easier to just hook up the SD card(with resistors or SD card module) to Arduino?
>The SD library is widely used and is reliable, the only problem is the Arduino does't have to much memory and with the SD lib the MCU has to cope with the FAT file system,
>and we're just talking about SD card management, the USB storage drive handling is a more complicated and memory consuming procedure and you need a USB-HOST chip.
>The CH376 chip easily can write and read files even if they are on SD card or on Usb thumb-drive(CH375 only support USB thumb-drive). The chip supports FAT12, FAT16 and FAT32 file systems, meaning the chip does the hard work,
>the MCU does not have to deal with the FAT file system, it only sends instructions to the chip on the communication bus you like (SPI, UART (HW serial, SW serial)), and the magic happens in the chip.
>The chip can do more, e.g to handle HID devices(usb keyboard, mouse, joystick ...) but this feature is not yet used in the library, maybe in the future.
Click [here](https://github.com/djuseeq/Ch376msc#test) to see the difference between libraries about memory usage.
## Getting Started
Configure the jumpers on the module depending on which communication protocol you are using(see [API reference](https://github.com/djuseeq/Ch376msc#api-reference))
![Alt text](extras/JumperSelect.png?raw=true "Setting")
### PCB modding for SD card
> If you planning to use the chip for SD card also and you have a pcb like on the photo above, then some soldering skill is required.
> First of all with a DMM check the pins of the chip(26,25,23 and 7) are they floating or connected to GND/VCC.
> On mine pcb the chip pin 23 (SD_CS) is connected to ground, like you can [see here](extras/schematic.png),
> pins or the chip have incorrect marking(looks like CH375 which one doesn't support SD card) . [Link](https://www.mpja.com/download/31813MPSch.pdf) for the module's schematic diagram.
> I used soldering iron and tweezer to lift up the pin from the pcb(be careful, you can easily break the chip's leg).
> Follow [this schema](extras/modPcb.png) to make the proper connection between the chip and SD card socket.
> I used a [SD card adapter](extras/sdAdapter.jpg) and for sake of stability, use the capacitors+1R resistor on Vcc line.
> The SD card operate from 3.3V and this board already have a 3.3V voltage regulator so that is fine.
> Here are some photos from the ugly modding ;) [Photo1](extras/board1.jpg) [Photo2](extras/board2.jpg).
## Versions
v1.4.4 Sep 29, 2020
- error handling improvement
- new function, getChipVer()
- bug fix, issue #34 Variable Naming conflicts with core ESP32 Variables
v1.4.3 Feb 06, 2020
- bug fix issue #22 unknown partition
- new functions as requested in #21 , #23
- reorganizing the library
- added function-like macros to easy configure the SPI clock rate(see in examples/lcd_menu)
v1.4.2 Jan 07, 2020
> - support SD card manage(API ref. - setSource(),if the SD card socket is not available on the module,
> then modification on the module is required, please read [Pcb modding for sd card](https://github.com/djuseeq/Ch376msc#pcb-modding-for-sd-card) section)
> - a new example of using an SD card
> - the checkDrive function name was misleading, renamed to checkIntMessage
> - improvements, bug fixes
> - unnecessary examples removed
v1.4.1 Dec 22, 2019
- supports more architectures(see Tested boards table below) - issue #11
- constructor update (BUSY pin is not longer used)
- improved logic to the mount/unmount flash drive
- directory support ( cd(); function )
- use advanced file listing with (*) wildcard character(API reference, listDir() function)
v1.4.0 Sep 26, 2019
- new functions
- getTotalSectors() - returns a unsigned long number, total sectors on the drive
- getFreeSectors() - returns a unsigned long number, free sectors on the drive
- getFileSystem() - returns a byte number, 0x01-FAT12, 0x02-FAT16, 0x03-FAT32
- updated example files with a new functions
- new example file, searching for the oldest/newest file on the flash drive
v1.3.1 Sep 20, 2019
- rearrange the folder structure to be 1.5 library format compatible
v1.3 Sep 17, 2019
- bug fix for moveCursor issue #3 , minor changes
v1.2.1 Apr 24, 2019
- In use of SPI, CS pin on the module must to be pulled to VCC otherwise communication can be instable on a higher clock rate
- bug fix for timing issue on a higher clock rate (TSC)
v1.2 Apr 20, 2019
- extended with SPI communication
v1.1 Feb 25, 2019
- initial version with UART communication
## API Reference
```C++
//The default SPI communication speed is reduced to 125 kHz because of stability if long cables or breadboard is used.
// to change the SPI Clock rate, during instantiation use e.g. SPI_SCK_KHZ(500) - to use 500kHz
// or e.g. SPI_SCK_MHZ(8) - to use 8MHz (see in examples/lcd_menu)
//CONSTRUCTORS
//UART
//For hardware serial leave the communication settings on the module at default speed (9600bps)
Ch376msc(HardwareSerial, speed);//Select the serial port to which the module is connected and the desired speed(9600, 19200, 57600, 115200)
//For software serial select the desired communication speed on the module(look on the picture above)
Ch376msc(SoftwareSerial);
//SPI
//If no other device is connected to the SPI port it`s possible to save one MCU pin
Ch376msc(spiSelect, *optional SPI CLK rate*);// ! Don`t use this if the SPI port is shared with other devices
//If the SPI port is shared with other devices, use this constructor and one extra MCU pin need to be sacrificed for the INT pin
Ch376msc(spiSelect, interruptPin, *optional SPI CLK rate*);
////////////////////
// Must be initialized before any other command are called from this class.
init();
// call frequently to get any interrupt message of the module(attach/detach drive)
checkIntMessage(); //return TRUE if an interrupt request has been received, FALSE if not.
// can call before any file operation
driveReady(); //returns FALSE if no drive is present or TRUE if drive is attached and ready.
// check the communication between MCU and the CH376
pingDevice(); //returns FALSE if there is a communication failure, TRUE if communication is ok
// 8.3 filename, also called a short filename is accepted
setFileName(filename);//8 char long name + 3 char long extension
// open file before any file operation. Use first setFileName() function
openFile();
// always call this after finishing with file operations otherwise data loss or file corruption may occur
closeFile();
// repeatedly call this function to read data to buffer until the return value is TRUE
readFile(buffer, length);// buffer - char array, buffer size
// Read text until reach the terminator character, rest is same as readFile
readFileUntil(terminator, buffer, length);//returns boolean true if the given buffer
// is full and not reached the terminator character
//Same as readFile except the buffer type is byte(uint8) array and not added terminating 0 char
readRaw(buffer, length);// buffer - byte array, buffer size
//Read, extract numbers of txt file, read until reach EOF (see getEOF())
readLong(terminator);//returns long value,terminator char is optional, default char is '\n'
readULong(terminator);//returns unsigned long value,terminator char is optional, default char is '\n'
readDouble(terminator);//returns double value,terminator char is optional, default char is '\n'
//Write, construct string of number and write on the storage(byte, int, u int, long, u long, double)
writeNum(number);// write the given number
writeNumln(number);// write the given number in new line
//Write one character on the storage
writeChar(char);// e.g. new line character '\n' or comma ',' to
// repeatedly call this function to write data to the drive until there is no more data for write or the return value is FALSE
writeFile(buffer, length);// buffer - char array, string size in the buffer
// switch between source drive's, 0 = USB(default), 1 = SD card
// !!Before calling this function and activate the SD card please do the required modification
// on the pcb, please read **PCB modding for SD card** section otherwise you can damage the CH376 chip.
setSource(srcDrive);// 0 or 1
setYear(year); // 1980 - 2099
setMonth(month);// 1 - 12
setDay(day);// 1 - 31
setHour(hour);// 0 - 23
setMinute(minute);// 0 - 59
setSecond(second);// 0 - 59 saved with 2 second resolution (0, 2, 4 ... 58)
// when new file is created the defult file creation date/time is (2004-1-1 0.0.0),
// it is possible to change date/time with this function, use first set functions above to set the file attributes
saveFileAttrb();
// move the file cursor to specified position
moveCursor(position);// 00000000h - FFFFFFFFh
// delete the specified file, use first setFileName() function
deleteFile();
// repeatedly call this function with getFileName until the return value is TRUE to get the file names from the current directory
// limited possibility to use with wildcard character e.g. listDir("AB*") will list files with names starting with AB
// listDir("*AB") will not work, wildcard char+string must to be less than 8 character long
// if no argument is passed while calling listDir(), all files will be printed from the current directory
listDir();// returns FALSE if no more file is in the current directory
// reset file process state machine to default
// useful e.g. to make LCD menu with file's list without using large buffer to store the file names
resetFileList();
//dirPath = e.g. "/DIR1/DIR2/DIR3" , "/" - root dir
//CreateDir = 0(open directories if they not exist, don`t create them) or 1(create directories if they do not exist and open them)
//if working in subfolders, before file operations ALWAYS call this function with the full directory path
//limited to 3 subfolders depth (see /src/Ch376msc.h file. MAXDIRDEPTH) and 8 character long directory names
cd(dirPath,CreateDir);// returns byte value,see example .ino
getFreeSectors();// returns unsigned long value
getTotalSectors();// returns unsigned long value
getFileSize();// returns unsigned long value (byte)
getSource();// returns boolean value, false USB, true SD card
getYear();// returns int value
getMonth();// returns int value
getDay();// returns int value
getHour();// returns int value
getMinute();// returns int value
getSecond();// returns int value
// get the last error code (see datasheet and/or CommDef.h)
getError();// returns byte value
getFileSystem();// returns byte value, 01h-FAT12, 02h-FAT16, 03h-FAT32
getFileName();// returns the file name in a 11+1 character long string value
getFileSizeStr();// returns file size in a formatted 9+1 character long string value
getFileAttrb();// returns byte value, see /src/CommDef.h , (File attributes)
getCursorPos();// returns unsigned long value
getEOF();// returns boolean value, true EOF is reached
getChipVer();// returns byte value, returns the CH chip firmware version number
```
## Tested boards
|Board(arch) | SPI | HW Serial | SW Serial|
|:---|:---:|:---:|:---:|
|Arduino (AVR)|OK|OK|OK|
|DUE (SAM)|OK(with INT pin)|OK|NO|
|ZERO (SAMD)|OK|?|NO|
|*STM32 cores|OK|!NO|NO|
|**STM32duino|OK|OK|NO|
|***ESP8266|OK(with INT pin)|NO|OK|
|ESP32 ([ref](https://github.com/djuseeq/Ch376msc/issues/18))|OK|?|?|
Be careful when choosing SoftSerial because it has its own limitations. See [issues#15](https://github.com/djuseeq/Ch376msc/issues/15)
> `*` Tested on NUCLEO F446RE(no signal at all on UART ports)
> `**` Tested on Generic STM32F103C alias Blue pill with STM32duino bootloader
> `***` Tested on NodeMCU,(i'm not familiar with ESP MCUs) it looks they have default enabled WDT so i have to call
> `yield()` periodically during file operations, otherwise ESP will restart with a ugly message.
> Working SPI configuration (for me)is MISO-12(D6), MOSI-13(D7), SCK-14(D5), CS-4(D2), INT-5(D1)
### Test
I compared the two libraries with the same instructions, create file, write some text in it and read back the created file and send it to serial (SPI used)
Used Arduino IDE 1.8.10 on x64 linux, ArduinoUno board choosed
Sketch from SD library(SparkFun 1.2.4) ReadWrite example:
Program space used: 10704 bytes 33% ,
SRAM used: 882 bytes 43%
```C++
#include <SPI.h>
#include <SD.h>
File myFile;
void setup() {
Serial.begin(9600);
SD.begin(4);
myFile = SD.open("TEST.TXT", FILE_WRITE);
if (myFile) {
myFile.println("testing 1, 2, 3.");
myFile.close();
}
myFile = SD.open("TEST.TXT");
while (myFile.available()) {
Serial.write(myFile.read());
}
myFile.close();
}
void loop() {}
```
Second sketch is with Ch376msc library(1.4.2)
1. if i put in comments the setSorce function and use the default USB storage
Program space used: 6760 bytes 20% ,
SRAM used: 315 bytes 15%
2. with setSorce function choosed USB storage
Program space used: 6810 bytes 21% ,
SRAM used: 315 bytes 15%
3. with setSorce function choosed SD storage
Program space used: 6824 bytes 21% ,
SRAM used: 315 bytes 15%
```C++
#include <Ch376msc.h>
Ch376msc flashDrive(10); // chipSelect
char adat[]={"testing 1, 2, 3."};
boolean readMore = true;
void setup() {
Serial.begin(9600);
flashDrive.init();
flashDrive.setSource(0);//0 - USB, 1 - SD
flashDrive.setFileName("TEST.TXT");
flashDrive.openFile();
flashDrive.writeFile(adat, strlen(adat));
flashDrive.closeFile();
flashDrive.setFileName("TEST.TXT");
flashDrive.openFile();
while(readMore){
readMore = flashDrive.readFile(adat, sizeof(adat));
Serial.print(adat);
}
flashDrive.closeFile();
}
void loop() {}
```
### Acknowledgments
Thanks for the idea to [Scott C](https://arduinobasics.blogspot.com/2015/05/ch376s-usb-readwrite-module.html)
## License
The MIT License (MIT)
Copyright (c) 2019 György Kovács
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,254 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 28 Mar 2019 |
* Description: Basic usage of CH376 with hardware serial |
* Thanks for the idea to Scott C , https://arduinobasics.blogspot.com/2015/05/ch376s-usb-readwrite-module.html |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
//..............................................................................................................................
// Leave the default jumper settings for the baud rate (9600) on the CH376, the library will set it up the chosen speed(HW serial only)
Ch376msc flashDrive(Serial1, 115200); // Ch376 object with hardware Serial1 on arduino mega baudrate: 9600, 19200, 57600, 115200
//..............................................................................................................................
// buffer for reading
char adatBuffer[255];// max length 255 = 254 char + 1 NULL character
//..............................................................................................................................
// strings for writing to file
char adat[]="Vivamus nec nisl molestie, blandit diam vel, varius mi. Fusce luctus cursus sapien in vulputate.\n";
char adat2[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis efficitur ac est eu pharetra. \n";
//..............................................................................................................................
unsigned long totSect = 0;
unsigned long freeSect = 0;
byte percentg = 0;
byte tmpCommand; //used to store data coming from serial port
boolean readMore;
static char helpString[]= {"h:Print this help\n\n1:Create\n2:Append\n3:Read\n4:Read date/time\n"
"5:Modify date/time\n6:Delete\n7:List dir\n8:Print free space"
"\n9:Open/Create folder(s)/subfolder(s)"};
void setup() {
Serial.begin(115200);
flashDrive.init();
printInfo(helpString);
}
void loop() {
if(flashDrive.checkIntMessage()){
if(flashDrive.getDeviceStatus()){
Serial.println(F("Flash drive attached!"));
} else {
Serial.println(F("Flash drive detached!"));
}
}
if(Serial.available()){
tmpCommand = Serial.read(); //read incoming bytes from the serial monitor
if(((tmpCommand > 48)&&(tmpCommand < 58)) && !flashDrive.driveReady()){ // if the data is ASCII 1 - 9 and no flash drive are attached
printInfo("Attach flash drive first!");
tmpCommand = 10; // change the command byte
}
switch (tmpCommand) {
case 49: //1
printInfo("COMMAND1: Create and write data to file : TEST1.TXT"); // Create a file called TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
flashDrive.writeFile(adat, strlen(adat)); //string, string length
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 50: //2
printInfo("COMMAND2: Append data to file: TEST1.TXT"); // Append data to the end of the file.
flashDrive.setFileName("TEST1.TXT"); //set the file name
if(flashDrive.openFile() == ANSW_USB_INT_SUCCESS){ //open the file
flashDrive.moveCursor(CURSOREND); //if the file exist, move the "virtual" cursor at end of the file, with CURSORBEGIN we actually rewrite our old file
//flashDrive.moveCursor(flashDrive.getFileSize()); // is almost the same as CURSOREND, because we put our cursor at end of the file
}
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
if(flashDrive.getFreeSectors()){ //check the free space on the drive
flashDrive.writeFile(adat2, strlen(adat2)); //string, string length
} else {
printInfo("Disk full");
}
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 51: //3
printInfo("COMMAND3: Read File: TEST1.TXT"); // Read the contents of this file on the USB disk, and display contents in the Serial Monitor
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
readMore = true;
//read data from flash drive until we reach EOF
while(readMore){ // our temporary buffer where we read data from flash drive and the size of that buffer
readMore = flashDrive.readFile(adatBuffer, sizeof(adatBuffer));
Serial.print(adatBuffer); //print the contents of the temporary buffer
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 52: //4
printInfo("COMMAND4: Read File date/time: TEST1.TXT"); // Read the date and time of file, default 2004.01.01 - 00:00:00
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
//print informations about the file
Serial.println(flashDrive.getFileName());
Serial.print(flashDrive.getYear());
Serial.print("y\t");
Serial.print(flashDrive.getMonth());
Serial.print("m\t");
Serial.print(flashDrive.getDay());
Serial.print("d\t");
Serial.print(flashDrive.getHour());
Serial.print("h\t");
Serial.print(flashDrive.getMinute());
Serial.print("m\t");
Serial.print(flashDrive.getSecond());
Serial.println('s');
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 53: //5
printInfo("COMMAND5: Modify File date/time: TEST1.TXT"); // Modify the file date/time and save
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
flashDrive.setYear(2019);
flashDrive.setMonth(12);
flashDrive.setDay(19);
flashDrive.setHour(03);
flashDrive.setMinute(38);
flashDrive.setSecond(42);
flashDrive.saveFileAttrb(); //save the changed data
flashDrive.closeFile(); //and yes again, close the file after when you don`t use it
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 54: //6
printInfo("COMMAND6: Delete File: TEST1.TXT"); // Delete the file named TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.deleteFile(); //delete file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 55: //7
printInfo("COMMAND7: List directory"); //Print all file names in the current directory
while(flashDrive.listDir()){ // reading next file
if(flashDrive.getFileAttrb() == CH376_ATTR_DIRECTORY){//directory
Serial.print('/');
Serial.println(flashDrive.getFileName()); // get the actual file name
} else {
Serial.print(flashDrive.getFileName()); // get the actual file name
Serial.print(" : ");
Serial.print(flashDrive.getFileSize()); // get the actual file size in bytes
Serial.print(" >>>\t");
Serial.println(flashDrive.getFileSizeStr()); // get the actual file size in formatted string
}
}
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 56: //8
totSect = flashDrive.getTotalSectors(); // get the total sector number
freeSect = flashDrive.getFreeSectors(); // get the available sector number
percentg = map(freeSect,totSect,0,0,100); // convert it to percentage (0-100)
Serial.print("Disk size in bytes: ");
/*if the sector number is more than 8388607 (8388607 * 512 = 4294966784 byte = 4Gb (fits in a 32bit variable) )
e.g. 8388608 * 512 = 4294967296 byte (32bit variable overflows) */
if(totSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(totSect * SECTORSIZE);
}
Serial.print("\tFree space in bytes: ");
if(freeSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(freeSect * SECTORSIZE);
}
Serial.print(F("\tDisk usage :"));
Serial.print(percentg);
Serial.print(F("%"));
switch (flashDrive.getFileSystem()) { //1-FAT12, 2-FAT16, 3-FAT32
case 1:
Serial.println(F("\tFAT12 partition"));
break;
case 2:
Serial.println(F("\tFAT16 partition"));
break;
case 3:
Serial.println(F("\tFAT32 partition"));
break;
default:
Serial.println(F("\tNo valid partition"));
break;
}
break;
//*****************************************************************************************************************************************************
case 57: //9
switch(flashDrive.cd("/DIR1/DIR2/DIR3",1)){
case CH376_ERR_LONGFILENAME: //0x01
Serial.println(F("Directory name is too long"));
break;
case ANSW_USB_INT_SUCCESS: //0x14
Serial.println(F("Directory created successfully"));
break;
case ANSW_ERR_OPEN_DIR: //0x41
Serial.println(F("Directory opened successfully"));
break;
case ANSW_ERR_MISS_FILE: //0x42
Serial.println(F("Directory doesn't exist"));
break;
case ANSW_ERR_FOUND_NAME: //0x43
Serial.println(F("File exist with the given name"));
break;
default:
break;
}
break;
//*****************************************************************************************************************************************************
case 104: //h
printInfo(helpString);
break;
default:
break;
}//end switch
}//endif serial available
}//end loop
//Print information
void printInfo(const char info[]){
int infoLength = strlen(info);
if(infoLength > 40){
infoLength = 40;
}
Serial.print(F("\n\n"));
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.println();
Serial.println(info);
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.print(F("\n\n"));
}

View File

@ -0,0 +1,260 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 22 Apr 2019 |
* Description: Basic usage of CH376 with SPI port |
* Thanks for the idea to Scott C , https://arduinobasics.blogspot.com/2015/05/ch376s-usb-readwrite-module.html |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
//..............................................................................................................................
// Connect to SPI port: MISO, MOSI, SCK
// use this if no other device are attached to SPI port(MISO pin used as interrupt)
Ch376msc flashDrive(10); // chipSelect
//If the SPI port shared with other devices e.g SD card, display, etc. remove from comment the code below and put the code above in a comment
//Ch376msc flashDrive(10, 9); // chipSelect, interrupt pin
//..............................................................................................................................
// buffer for reading
char adatBuffer[255];// max length 255 = 254 char + 1 NULL character
//..............................................................................................................................
// strings for writing to file
char adat[]="Vivamus nec nisl molestie, blandit diam vel, varius mi. Fusce luctus cursus sapien in vulputate.\n";
char adat2[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis efficitur ac est eu pharetra. \n";
//..............................................................................................................................
unsigned long totSect = 0;
unsigned long freeSect = 0;
byte percentg = 0;
byte tmpCommand; //used to store data coming from serial port
boolean readMore;
static char helpString[]= {"h:Print this help\n\n1:Create\n2:Append\n3:Read\n4:Read date/time\n"
"5:Modify date/time\n6:Delete\n7:List dir\n8:Print free space"
"\n9:Open/Create folder(s)/subfolder(s)"};
void setup() {
Serial.begin(115200);
flashDrive.init();
printInfo(helpString);
}
void loop() {
if(flashDrive.checkIntMessage()){
if(flashDrive.getDeviceStatus()){
Serial.println(F("Flash drive attached!"));
} else {
Serial.println(F("Flash drive detached!"));
}
}
if(Serial.available()){
tmpCommand = Serial.read(); //read incoming bytes from the serial monitor
if(((tmpCommand > 48)&&(tmpCommand < 58)) && !flashDrive.driveReady()){ // if the data is ASCII 1 - 9 and no flash drive are attached
printInfo("Attach flash drive first!");
tmpCommand = 10; // change the command byte
}
switch (tmpCommand) {
case 49: //1
printInfo("COMMAND1: Create and write data to file : TEST1.TXT"); // Create a file called TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
flashDrive.writeFile(adat, strlen(adat)); //string, string length
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 50: //2
printInfo("COMMAND2: Append data to file: TEST1.TXT"); // Append data to the end of the file.
flashDrive.setFileName("TEST1.TXT"); //set the file name
if(flashDrive.openFile() == ANSW_USB_INT_SUCCESS){ //open the file
flashDrive.moveCursor(CURSOREND); //if the file exist, move the "virtual" cursor at end of the file, with CURSORBEGIN we actually rewrite our old file
//flashDrive.moveCursor(flashDrive.getFileSize()); // is almost the same as CURSOREND, because we put our cursor at end of the file
}
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
if(flashDrive.getFreeSectors()){ //check the free space on the drive
flashDrive.writeFile(adat2, strlen(adat2)); //string, string length
} else {
printInfo("Disk full");
}
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 51: //3
printInfo("COMMAND3: Read File: TEST1.TXT"); // Read the contents of this file on the USB disk, and display contents in the Serial Monitor
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
readMore = true;
//read data from flash drive until we reach EOF
while(readMore){ // our temporary buffer where we read data from flash drive and the size of that buffer
readMore = flashDrive.readFile(adatBuffer, sizeof(adatBuffer));
Serial.print(adatBuffer); //print the contents of the temporary buffer
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 52: //4
printInfo("COMMAND4: Read File date/time: TEST1.TXT"); // Read the date and time of file, default 2004.01.01 - 00:00:00
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
//print informations about the file
Serial.println(flashDrive.getFileName());
Serial.print(flashDrive.getYear());
Serial.print("y\t");
Serial.print(flashDrive.getMonth());
Serial.print("m\t");
Serial.print(flashDrive.getDay());
Serial.print("d\t");
Serial.print(flashDrive.getHour());
Serial.print("h\t");
Serial.print(flashDrive.getMinute());
Serial.print("m\t");
Serial.print(flashDrive.getSecond());
Serial.println('s');
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 53: //5
printInfo("COMMAND5: Modify File date/time: TEST1.TXT"); // Modify the file date/time and save
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
flashDrive.setYear(2019);
flashDrive.setMonth(12);
flashDrive.setDay(19);
flashDrive.setHour(03);
flashDrive.setMinute(38);
flashDrive.setSecond(42);
flashDrive.saveFileAttrb(); //save the changed data
flashDrive.closeFile(); //and yes again, close the file after when you don`t use it
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 54: //6
printInfo("COMMAND6: Delete File: TEST1.TXT"); // Delete the file named TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.deleteFile(); //delete file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 55: //7
printInfo("COMMAND7: List directory"); //Print all file names in the current directory
while(flashDrive.listDir()){ // reading next file
if(flashDrive.getFileAttrb() == CH376_ATTR_DIRECTORY){//directory
Serial.print('/');
Serial.println(flashDrive.getFileName()); // get the actual file name
} else {
Serial.print(flashDrive.getFileName()); // get the actual file name
Serial.print(" : ");
Serial.print(flashDrive.getFileSize()); // get the actual file size in bytes
Serial.print(" >>>\t");
Serial.println(flashDrive.getFileSizeStr()); // get the actual file size in formatted string
}
}
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 56: //8
totSect = flashDrive.getTotalSectors(); // get the total sector number
freeSect = flashDrive.getFreeSectors(); // get the available sector number
percentg = map(freeSect,totSect,0,0,100); // convert it to percentage (0-100)
Serial.print("Disk size in bytes: ");
/*if the sector number is more than 8388607 (8388607 * 512 = 4294966784 byte = 4Gb (fits in a 32bit variable) )
e.g. 8388608 * 512 = 4294967296 byte (32bit variable overflows) */
if(totSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(totSect * SECTORSIZE);
}
Serial.print("\tFree space in bytes: ");
if(freeSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(freeSect * SECTORSIZE);
}
Serial.print(F("\tDisk usage :"));
Serial.print(percentg);
Serial.print(F("%"));
switch (flashDrive.getFileSystem()) { //1-FAT12, 2-FAT16, 3-FAT32
case 1:
Serial.println(F("\tFAT12 partition"));
break;
case 2:
Serial.println(F("\tFAT16 partition"));
break;
case 3:
Serial.println(F("\tFAT32 partition"));
break;
default:
Serial.println(F("\tNo valid partition"));
break;
}
break;
//*****************************************************************************************************************************************************
case 57: //9
switch(flashDrive.cd("/DIR1/DIR2/DIR3",1)){
case CH376_ERR_LONGFILENAME: //0x01
Serial.println(F("Directory name is too long"));
break;
case ANSW_USB_INT_SUCCESS: //0x14
Serial.println(F("Directory created successfully"));
break;
case ANSW_ERR_OPEN_DIR: //0x41
Serial.println(F("Directory opened successfully"));
break;
case ANSW_ERR_MISS_FILE: //0x42
Serial.println(F("Directory doesn't exist"));
break;
case ANSW_ERR_FOUND_NAME: //0x43
Serial.println(F("File exist with the given name"));
break;
default:
break;
}
break;
//*****************************************************************************************************************************************************
case 104: //h
printInfo(helpString);
break;
default:
break;
}//end switch
}//endif serial available
}//end loop
//Print information
void printInfo(const char info[]){
int infoLength = strlen(info);
if(infoLength > 40){
infoLength = 40;
}
Serial.print(F("\n\n"));
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.println();
Serial.println(info);
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.print(F("\n\n"));
}

View File

@ -0,0 +1,268 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 07 Jan 2020 |
* Description: Basic usage of CH376 with SPI port, switching between SD card and USB drive |
* ! WARNING ! Before you upload and test this sketch, read https://github.com/djuseeq/Ch376msc#getting-started, |
* PCB modding for SD card section, otherwise you can DAMAGE the CH376 module with switching source to SD card |
* if your module is ready to SD card mode, remove from comment the SD card enable line in beginning of loop fnc.|
* Thanks for the idea to Scott C , https://arduinobasics.blogspot.com/2015/05/ch376s-usb-readwrite-module.html |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
//..............................................................................................................................
// Connect to SPI port: MISO, MOSI, SCK
// use this if no other device are attached to SPI port(MISO pin used as interrupt)
Ch376msc flashDrive(10); // chipSelect
//If the SPI port shared with other devices e.g TFT display, etc. remove from comment the code below and put the code above in a comment
//Ch376msc flashDrive(10, 9); // chipSelect, interrupt pin
//..............................................................................................................................
// buffer for reading
char adatBuffer[255];// max length 255 = 254 char + 1 NULL character
//..............................................................................................................................
// strings for writing to file
char adat[]="Vivamus nec nisl molestie, blandit diam vel, varius mi. Fusce luctus cursus sapien in vulputate.\n";
char adat2[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis efficitur ac est eu pharetra. \n";
//..............................................................................................................................
unsigned long totSect = 0;
unsigned long freeSect = 0;
byte percentg = 0;
byte tmpCommand; //used to store data coming from serial port
boolean readMore;
static char helpString[]= {"h:Print this help\n\n1:Create\n2:Append\n3:Read\n4:Read date/time\n"
"5:Modify date/time\n6:Delete\n7:List dir\n8:Print free space"
"\n9:Open/Create folder(s)/subfolder(s)\nu:Source USB\ns:Source SD card"};
void setup() {
Serial.begin(115200);
flashDrive.init();
printInfo(helpString);
}
void loop() {
if(flashDrive.checkIntMessage()){
if(flashDrive.getDeviceStatus()){
Serial.println(F("Flash drive attached!"));
} else {
Serial.println(F("Flash drive detached!"));
}
}
if(Serial.available()){
tmpCommand = Serial.read(); //read incoming bytes from the serial monitor
//Enable SD card below this comment, remove it from comment only after reading warning message in sketch header
//and delete(put in comment) the Serial print message line
//if(tmpCommand == 115) flashDrive.setSource(1);//SD card enable
if(tmpCommand == 115) Serial.println(F("Please first read the Warning message in sketch header before you change source to SD card!"));
if(tmpCommand == 117) flashDrive.setSource(0);//USB enable
if(((tmpCommand > 48)&&(tmpCommand < 58)) && !flashDrive.driveReady()){ // if the data is ASCII 1 - 9 and no flash drive are attached
printInfo("Attach flash/SD drive first!");
tmpCommand = 10; // change the command byte
}
switch (tmpCommand) {
case 49: //1
printInfo("COMMAND1: Create and write data to file : TEST1.TXT"); // Create a file called TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
flashDrive.writeFile(adat, strlen(adat)); //string, string length
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 50: //2
printInfo("COMMAND2: Append data to file: TEST1.TXT"); // Append data to the end of the file.
flashDrive.setFileName("TEST1.TXT"); //set the file name
if(flashDrive.openFile() == ANSW_USB_INT_SUCCESS){ //open the file
flashDrive.moveCursor(CURSOREND); //if the file exist, move the "virtual" cursor at end of the file, with CURSORBEGIN we actually rewrite our old file
//flashDrive.moveCursor(flashDrive.getFileSize()); // is almost the same as CURSOREND, because we put our cursor at end of the file
}
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
if(flashDrive.getFreeSectors()){ //check the free space on the drive
flashDrive.writeFile(adat2, strlen(adat2)); //string, string length
} else {
printInfo("Disk full");
}
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 51: //3
printInfo("COMMAND3: Read File: TEST1.TXT"); // Read the contents of this file on the USB disk, and display contents in the Serial Monitor
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
readMore = true;
//read data from flash drive until we reach EOF
while(readMore){ // our temporary buffer where we read data from flash drive and the size of that buffer
readMore = flashDrive.readFile(adatBuffer, sizeof(adatBuffer));
Serial.print(adatBuffer); //print the contents of the temporary buffer
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 52: //4
printInfo("COMMAND4: Read File date/time: TEST1.TXT"); // Read the date and time of file, default 2004.01.01 - 00:00:00
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
//print informations about the file
Serial.println(flashDrive.getFileName());
Serial.print(flashDrive.getYear());
Serial.print("y\t");
Serial.print(flashDrive.getMonth());
Serial.print("m\t");
Serial.print(flashDrive.getDay());
Serial.print("d\t");
Serial.print(flashDrive.getHour());
Serial.print("h\t");
Serial.print(flashDrive.getMinute());
Serial.print("m\t");
Serial.print(flashDrive.getSecond());
Serial.println('s');
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 53: //5
printInfo("COMMAND5: Modify File date/time: TEST1.TXT"); // Modify the file date/time and save
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
flashDrive.setYear(2019);
flashDrive.setMonth(12);
flashDrive.setDay(19);
flashDrive.setHour(03);
flashDrive.setMinute(38);
flashDrive.setSecond(42);
flashDrive.saveFileAttrb(); //save the changed data
flashDrive.closeFile(); //and yes again, close the file after when you don`t use it
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 54: //6
printInfo("COMMAND6: Delete File: TEST1.TXT"); // Delete the file named TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.deleteFile(); //delete file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 55: //7
printInfo("COMMAND7: List directory"); //Print all file names in the current directory
while(flashDrive.listDir()){ // reading next file
if(flashDrive.getFileAttrb() == CH376_ATTR_DIRECTORY){//directory
Serial.print('/');
Serial.println(flashDrive.getFileName()); // get the actual file name
} else {
Serial.print(flashDrive.getFileName()); // get the actual file name
Serial.print(" : ");
Serial.print(flashDrive.getFileSize()); // get the actual file size in bytes
Serial.print(" >>>\t");
Serial.println(flashDrive.getFileSizeStr()); // get the actual file size in formatted string
}
}
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 56: //8
totSect = flashDrive.getTotalSectors(); // get the total sector number
freeSect = flashDrive.getFreeSectors(); // get the available sector number
percentg = map(freeSect,totSect,0,0,100); // convert it to percentage (0-100)
Serial.print("Disk size in bytes: ");
/*if the sector number is more than 8388607 (8388607 * 512 = 4294966784 byte = 4Gb (fits in a 32bit variable) )
e.g. 8388608 * 512 = 4294967296 byte (32bit variable overflows) */
if(totSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(totSect * SECTORSIZE);
}
Serial.print("\tFree space in bytes: ");
if(freeSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(freeSect * SECTORSIZE);
}
Serial.print(F("\tDisk usage :"));
Serial.print(percentg);
Serial.print(F("%"));
switch (flashDrive.getFileSystem()) { //1-FAT12, 2-FAT16, 3-FAT32
case 1:
Serial.println(F("\tFAT12 partition"));
break;
case 2:
Serial.println(F("\tFAT16 partition"));
break;
case 3:
Serial.println(F("\tFAT32 partition"));
break;
default:
Serial.println(F("\tNo valid partition"));
break;
}
break;
//*****************************************************************************************************************************************************
case 57: //9
switch(flashDrive.cd("/DIR1/DIR2/DIR3",1)){
case CH376_ERR_LONGFILENAME: //0x01
Serial.println(F("Directory name is too long"));
break;
case ANSW_USB_INT_SUCCESS: //0x14
Serial.println(F("Directory created successfully"));
break;
case ANSW_ERR_OPEN_DIR: //0x41
Serial.println(F("Directory opened successfully"));
break;
case ANSW_ERR_MISS_FILE: //0x42
Serial.println(F("Directory doesn't exist"));
break;
case ANSW_ERR_FOUND_NAME: //0x43
Serial.println(F("File exist with the given name"));
break;
default:
break;
}
break;
//*****************************************************************************************************************************************************
case 104: //h
printInfo(helpString);
break;
default:
break;
}//end switch
}//endif serial available
}//end loop
//Print information
void printInfo(const char info[]){
int infoLength = strlen(info);
if(infoLength > 40){
infoLength = 40;
}
Serial.print(F("\n\n"));
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.println();
Serial.println(info);
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.print(F("\n\n"));
}

View File

@ -0,0 +1,257 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 28 Mar 2019 |
* Description: Basic usage of CH376 with software serial |
* Thanks for the idea to Scott C , https://arduinobasics.blogspot.com/2015/05/ch376s-usb-readwrite-module.html |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
#include <SoftwareSerial.h>
//Important! First create a soft serial object, after create a Ch376 object
SoftwareSerial mySerial(7, 6); // RX, TX pins on arduino
//..............................................................................................................................
Ch376msc flashDrive(mySerial); // Ch376 object with software serial
//..............................................................................................................................
// buffer for reading
char adatBuffer[255];// max length 255 = 254 char + 1 NULL character
//..............................................................................................................................
// strings for writing to file
char adat[]="Vivamus nec nisl molestie, blandit diam vel, varius mi. Fusce luctus cursus sapien in vulputate.\n";
char adat2[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis efficitur ac est eu pharetra. \n";
//..............................................................................................................................
unsigned long totSect = 0;
unsigned long freeSect = 0;
byte percentg = 0;
byte tmpCommand; //used to store data coming from serial port
boolean readMore;
static char helpString[]= {"h:Print this help\n\n1:Create\n2:Append\n3:Read\n4:Read date/time\n"
"5:Modify date/time\n6:Delete\n7:List dir\n8:Print free space"
"\n9:Open/Create folder(s)/subfolder(s)"};
void setup() {
Serial.begin(115200);
mySerial.begin(9600);// Important! First initialize soft serial object and after Ch376
flashDrive.init();
printInfo(helpString);
}
void loop() {
if(flashDrive.checkIntMessage()){
if(flashDrive.getDeviceStatus()){
Serial.println(F("Flash drive attached!"));
} else {
Serial.println(F("Flash drive detached!"));
}
}
if(Serial.available()){
tmpCommand = Serial.read(); //read incoming bytes from the serial monitor
if(((tmpCommand > 48)&&(tmpCommand < 58)) && !flashDrive.driveReady()){ // if the data is ASCII 1 - 9 and no flash drive are attached
printInfo("Attach flash drive first!");
tmpCommand = 10; // change the command byte
}
switch (tmpCommand) {
case 49: //1
printInfo("COMMAND1: Create and write data to file : TEST1.TXT"); // Create a file called TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
flashDrive.writeFile(adat, strlen(adat)); //string, string length
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 50: //2
printInfo("COMMAND2: Append data to file: TEST1.TXT"); // Append data to the end of the file.
flashDrive.setFileName("TEST1.TXT"); //set the file name
if(flashDrive.openFile() == ANSW_USB_INT_SUCCESS){ //open the file
flashDrive.moveCursor(CURSOREND); //if the file exist, move the "virtual" cursor at end of the file, with CURSORBEGIN we actually rewrite our old file
//flashDrive.moveCursor(flashDrive.getFileSize()); // is almost the same as CURSOREND, because we put our cursor at end of the file
}
for(int a = 0; a < 20; a++){ //write text from string(adat) to flash drive 20 times
if(flashDrive.getFreeSectors()){ //check the free space on the drive
flashDrive.writeFile(adat2, strlen(adat2)); //string, string length
} else {
printInfo("Disk full");
}
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 51: //3
printInfo("COMMAND3: Read File: TEST1.TXT"); // Read the contents of this file on the USB disk, and display contents in the Serial Monitor
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
readMore = true;
//read data from flash drive until we reach EOF
while(readMore){ // our temporary buffer where we read data from flash drive and the size of that buffer
readMore = flashDrive.readFile(adatBuffer, sizeof(adatBuffer));
Serial.print(adatBuffer); //print the contents of the temporary buffer
}
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 52: //4
printInfo("COMMAND4: Read File date/time: TEST1.TXT"); // Read the date and time of file, default 2004.01.01 - 00:00:00
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
//print informations about the file
Serial.println(flashDrive.getFileName());
Serial.print(flashDrive.getYear());
Serial.print("y\t");
Serial.print(flashDrive.getMonth());
Serial.print("m\t");
Serial.print(flashDrive.getDay());
Serial.print("d\t");
Serial.print(flashDrive.getHour());
Serial.print("h\t");
Serial.print(flashDrive.getMinute());
Serial.print("m\t");
Serial.print(flashDrive.getSecond());
Serial.println('s');
flashDrive.closeFile(); //at the end, close the file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 53: //5
printInfo("COMMAND5: Modify File date/time: TEST1.TXT"); // Modify the file date/time and save
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
flashDrive.setYear(2019);
flashDrive.setMonth(12);
flashDrive.setDay(19);
flashDrive.setHour(03);
flashDrive.setMinute(38);
flashDrive.setSecond(42);
flashDrive.saveFileAttrb(); //save the changed data
flashDrive.closeFile(); //and yes again, close the file after when you don`t use it
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 54: //6
printInfo("COMMAND6: Delete File: TEST1.TXT"); // Delete the file named TEST1.TXT
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.deleteFile(); //delete file
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 55: //7
printInfo("COMMAND7: List directory"); //Print all file names in the current directory
while(flashDrive.listDir()){ // reading next file
if(flashDrive.getFileAttrb() == CH376_ATTR_DIRECTORY){//directory
Serial.print('/');
Serial.println(flashDrive.getFileName()); // get the actual file name
} else {
Serial.print(flashDrive.getFileName()); // get the actual file name
Serial.print(" : ");
Serial.print(flashDrive.getFileSize()); // get the actual file size in bytes
Serial.print(" >>>\t");
Serial.println(flashDrive.getFileSizeStr()); // get the actual file size in formatted string
}
}
printInfo("Done!");
break;
//*****************************************************************************************************************************************************
case 56: //8
totSect = flashDrive.getTotalSectors(); // get the total sector number
freeSect = flashDrive.getFreeSectors(); // get the available sector number
percentg = map(freeSect,totSect,0,0,100); // convert it to percentage (0-100)
Serial.print("Disk size in bytes: ");
/*if the sector number is more than 8388607 (8388607 * 512 = 4294966784 byte = 4Gb (fits in a 32bit variable) )
e.g. 8388608 * 512 = 4294967296 byte (32bit variable overflows) */
if(totSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(totSect * SECTORSIZE);
}
Serial.print("\tFree space in bytes: ");
if(freeSect > 8388607){
Serial.print(">4Gb");
} else {
Serial.print(freeSect * SECTORSIZE);
}
Serial.print(F("\tDisk usage :"));
Serial.print(percentg);
Serial.print(F("%"));
switch (flashDrive.getFileSystem()) { //1-FAT12, 2-FAT16, 3-FAT32
case 1:
Serial.println(F("\tFAT12 partition"));
break;
case 2:
Serial.println(F("\tFAT16 partition"));
break;
case 3:
Serial.println(F("\tFAT32 partition"));
break;
default:
Serial.println(F("\tNo valid partition"));
break;
}
break;
//*****************************************************************************************************************************************************
case 57: //9
switch(flashDrive.cd("/DIR1/DIR2/DIR3",1)){
case CH376_ERR_LONGFILENAME: //0x01
Serial.println(F("Directory name is too long"));
break;
case ANSW_USB_INT_SUCCESS: //0x14
Serial.println(F("Directory created successfully"));
break;
case ANSW_ERR_OPEN_DIR: //0x41
Serial.println(F("Directory opened successfully"));
break;
case ANSW_ERR_MISS_FILE: //0x42
Serial.println(F("Directory doesn't exist"));
break;
case ANSW_ERR_FOUND_NAME: //0x43
Serial.println(F("File exist with the given name"));
break;
default:
break;
}
break;
//*****************************************************************************************************************************************************
case 104: //h
printInfo(helpString);
break;
default:
break;
}//end switch
}//endif serial available
}//end loop
//Print information
void printInfo(const char info[]){
int infoLength = strlen(info);
if(infoLength > 40){
infoLength = 40;
}
Serial.print(F("\n\n"));
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.println();
Serial.println(info);
for(int a = 0; a < infoLength; a++){
Serial.print('*');
}
Serial.print(F("\n\n"));
}

View File

@ -0,0 +1,152 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 05 Feb 2020 |
* Description: One variation of the LCD menu without using a large buffer to store filenames |
* In fact, we select the current file based on the file number (Folder handling is not implemented) |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
#include <LiquidCrystal.h>
//..............................................................................................................................
// Connect to SPI port: MISO, MOSI, SCK
// These function-like macros are optionally used when creating an object
//e.g. SPI_SCK_KHZ(500) - set the clock speed to 500 kHz
//e.g. SPI_SCK_MHZ(1) - set the clock speed to 1 MHz
// use this if no other device are attached to SPI port(MISO pin used as interrupt)
Ch376msc flashDrive(10,SPI_SCK_KHZ(500)); // chipSelect
//If the SPI port shared with other devices e.g TFT display, etc. remove from comment the code below and put the code above in a comment
//Ch376msc flashDrive(10, 9); // chipSelect, interrupt pin
//LCD in 4bit mode (RS,E,D4,D5,D6,D7)
LiquidCrystal lcd(3, 2, 7, 6, 5, 4);
#define BTN_UP A0 // Button UP
#define BTN_DOWN A1 // Button DOWN
#define BTN_OK A2 // Button OK
#define LCD_ROW 2 // Lcd Row count
#define LCD_CLMN 16 // Lcd Column count
//..............................................................................................................................
void setup() {
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_OK, INPUT_PULLUP);
Serial.begin(115200);
flashDrive.init();
lcd.begin(LCD_CLMN,LCD_ROW);
lcd.print(F("Press OK"));
}
void loop() {
if(digitalRead(BTN_OK) == LOW){ // Press OK to show files
delay(100);// debounce
int fileCnt = 0;// variable to store the total file count
if(flashDrive.driveReady()){// if drive is attached
flashDrive.resetFileList();
while(flashDrive.listDir()){
fileCnt++; // count all the files + directories
}
menu(fileCnt);// start menu
lcd.clear();
lcd.print(F("Press OK"));
} //end if drive attached
}//end if button OK pressed
}//end loop
void menu(int fileCount){
boolean b_exit = false;
int cursorPos = 0;//cursor position
int filePos = 0;//file position
int oldFilePos = 0;//old file position
char adatBuffer[25];// max length 255 = 254 char + 1 NULL character
fileListToLCD(filePos);//lcd print first files
while(!b_exit){ // stay in while loop as long as b_exit value is false
if(digitalRead(BTN_DOWN) == LOW && filePos < (fileCount-1)){
delay(200);
filePos++;
}
if(digitalRead(BTN_UP) == LOW && filePos > 0){
delay(200);
filePos--;
}
if(filePos != oldFilePos) { // when button pressed
lcd.setCursor(0,cursorPos);// delete the old cursor
lcd.print(" ");
if(filePos > oldFilePos){ //move forward or backward depending on the button press
cursorPos++;
} else {
cursorPos--;
}
oldFilePos = filePos;
if(cursorPos>(LCD_ROW-1) ){// if moved forward and reached the last row
fileListToLCD(filePos);//get the next files
cursorPos = 0;// put the cursor on the first line
} else if(cursorPos < 0){//if moved backward and reached the first row
flashDrive.resetFileList();//reset the state machine
searchFileName(filePos);// find the previous file by its number
fileListToLCD(filePos);//print them on to lcd
cursorPos = 0;//put the cursor on the first line
filePos -= (LCD_ROW-1);
oldFilePos = filePos;
}
lcd.setCursor(0,cursorPos);// print cursor to lcd
lcd.print(">");
}
if(digitalRead(BTN_OK) == LOW) {// choose a file and press ok to print content to serial
flashDrive.resetFileList();//reset the state machine
while(flashDrive.listDir()){ // find the selected file name by its number
if(!filePos) break;
filePos--;
}
if(flashDrive.getFileAttrb() == CH376_ATTR_DIRECTORY){// if the selected item is directory the do nothing
b_exit = true;// done with menu function
} else {// if the selected item is a valid file then print it to serial
flashDrive.setFileName();
flashDrive.openFile();
while(!flashDrive.getEOF()){ //read until EOF
flashDrive.readFile(adatBuffer, sizeof(adatBuffer));
Serial.print(adatBuffer); //print the contents of the temporary buffer
}
flashDrive.closeFile();
b_exit = true;// done with menu function
}//end if directory
}//end if OK pressed
}//end if while
delay(500);
}
void fileListToLCD(byte a) {
lcd.clear();
for (byte i=0; i<LCD_ROW; i++){
lcd.setCursor(1, i);
if(flashDrive.listDir()){
if(flashDrive.getFileAttrb() == CH376_ATTR_DIRECTORY){
lcd.print('/');
}
lcd.print(flashDrive.getFileName());
} else {
break;
}
}
if(!a){
lcd.setCursor(0,0);
lcd.print(">");
}
}
void searchFileName(int fileNum){
fileNum-=(LCD_ROW-1);
while(fileNum && flashDrive.listDir()){
fileNum--;
}
}

View File

@ -0,0 +1,49 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 27 Jan 2020 |
* Description: Basic usage of function readFileUntil(); |
* Create TEST1.TXT file with e.g. basicUsageSPI.ino |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
// use this if no other device are attached to SPI port(MISO pin used as interrupt)
Ch376msc flashDrive(10); // chipSelect
//If the SPI port shared with other devices e.g TFT display, etc. remove from comment the code below and put the code above in a comment
//Ch376msc flashDrive(10, 9); // chipSelect, interrupt pin
// buffer for reading
char adatBuffer[25];// max length 255 = 254 char + 1 NULL character
boolean readMore = true;
int trmCharCounter = 0;
void setup() {
Serial.begin(115200);
flashDrive.init();
if(flashDrive.driveReady()){
flashDrive.setFileName("TEST1.TXT"); //set the file name
flashDrive.openFile(); //open the file
//read data from flash drive until we reach EOF
while(!flashDrive.getEOF()){
readMore = true;
while(readMore){
// terminator character, temporary buffer where we read data from flash drive and the size of that buffer
// returns boolean true if the given buffer is full and didn't found the terminator character
readMore = flashDrive.readFileUntil('\n', adatBuffer, sizeof(adatBuffer));//new line character
Serial.print(adatBuffer); //print the contents of the temporary buffer
}//end while readMore
trmCharCounter++;
}//end while not EOF
Serial.print(F("Total trm. character found: "));
Serial.println(trmCharCounter);
} else {
Serial.println(F("Drive not initialized"));
}//end if driveReady
}//end setup
void loop() {
}

View File

@ -0,0 +1,76 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 03 Feb 2020 |
* Description: Basic usage of functions read/write numbers |
* |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
// use this if no other device are attached to SPI port(MISO pin used as interrupt)
Ch376msc flashDrive(10); // chipSelect
//If the SPI port shared with other devices e.g TFT display, etc. remove from comment the code below and put the code above in a comment
//Ch376msc flashDrive(10, 9); // chipSelect, interrupt pin
// buffer for reading
char adatBuffer[25];// max length 255 = 254 char + 1 NULL character
void setup() {
Serial.begin(115200);
flashDrive.init();
if(flashDrive.driveReady()){
flashDrive.setFileName("INTEGER.TXT");
flashDrive.openFile();
Serial.println(F("Writing integer numbers :"));
for(int a = 0; a < 20; a++){
int dd = random(-4000, 4000);
Serial.println(dd);
flashDrive.writeNumln(dd);
}
flashDrive.closeFile();
Serial.println(F("Writing done"));
Serial.println();
////////////////////////////////////////
flashDrive.setFileName("INTEGER.TXT");
flashDrive.openFile();
Serial.println(F("Reading integer numbers :"));
while(!flashDrive.getEOF()){
Serial.println(flashDrive.readLong());
}
flashDrive.closeFile();
Serial.println(F("Reading done"));
Serial.println();
/////////////////////////////////////////////
flashDrive.setFileName("FLOAT.TXT");
flashDrive.openFile();
Serial.println(F("Writing float numbers :"));
for(int a = 0; a < 20; a++){
float dd = random(-40, 40) * 3.14;
Serial.println(dd);
flashDrive.writeNumln(dd);
}
flashDrive.closeFile();
Serial.println(F("Writing done"));
Serial.println();
////////////////////////////////////////
flashDrive.setFileName("FLOAT.TXT");
flashDrive.openFile();
Serial.println(F("Reading float numbers :"));
while(!flashDrive.getEOF()){
Serial.println(flashDrive.readDouble());
}
flashDrive.closeFile();
Serial.println(F("Reading done"));
Serial.println();
} else {
Serial.println(F("Drive error"));
}//end drive ready
}//end setup
void loop() {
}

View File

@ -0,0 +1,123 @@
/*------------------------------------------------------------------------------------------------------------------
* Author: György Kovács |
* Created: 26 Sep 2019 |
* Description: Finding the oldest file, it can be useful e.g. writing log files to drive and |
* if the flash drive becomes full then we can delete the oldest. |
*------------------------------------------------------------------------------------------------------------------
*/
#include <Ch376msc.h>
//..............................................................................................................................
// Connect to SPI port: MISO, MOSI, SCK
// use this if no other device are attached to SPI port(MISO pin used as interrupt)
Ch376msc flashDrive(10); // chipSelect
//If the SPI port shared with other devices e.g SD card, display, etc. remove from comment the code below and put the code above in a comment
//Ch376msc flashDrive(10, 9); // chipSelect, interrupt pin
//..............................................................................................................................
//..............................................................................................................................
char fname[12] = "TEST1.TXT";
char adat[] = "Lorem ipsum dolor sit amet";
int fyear = 0;
int fmonth = 0;
int fday = 0;
int fhour = 0;
int fminute = 0;
int fsecond = 0;
unsigned long oldUnTime = 0;
void setup() {
Serial.begin(115200);
//Read more about randomSeed at https://www.arduino.cc/reference/en/language/functions/random-numbers/randomseed/
randomSeed(analogRead(0));
flashDrive.init();
makeFiles(); // create 10 files on the flash drive
while(flashDrive.listDir()){ // read root directory
if((convUnixTime() < oldUnTime) || !oldUnTime){ //looking for the oldest file or change the '<' symbol to '>'
updateOldestFile(); // if you looking for the newest file
}//end if file is older
}//end while
printFileData(); // print the oldest file name in to serial terminal
}//end setup
void loop() {
// do nothing
} //end loop
unsigned long convUnixTime(){ // calculate "quasi" Unix time without taking into account leap years
// Unix time is the number of seconds that have elapsed since 1970.01.01 00:00:00
// for the multipliers check the link https://www.epochconverter.com/
unsigned long unxTime;
int yyr = flashDrive.getYear();
yyr -= 1970; //elapsed years since 1970
unxTime = (yyr * 31556926UL);
unxTime += (flashDrive.getMonth() * 2629743UL);
unxTime += (flashDrive.getDay() * 86400UL);
unxTime += (flashDrive.getHour() * 3600UL);
unxTime += (flashDrive.getMinute() * 60UL);
unxTime += flashDrive.getSecond();
return unxTime;
}
void updateOldestFile(){
fyear = flashDrive.getYear();
fmonth = flashDrive.getMonth();
fday = flashDrive.getDay();
fhour = flashDrive.getHour();
fminute = flashDrive.getMinute();
fsecond = flashDrive.getSecond();
strcpy(fname,flashDrive.getFileName()); //copy file name to fname variable
oldUnTime = convUnixTime(); // update the oldest time variable
}
void printFileData(){ // Print data to the serial terminal
Serial.print("The oldest file is:\t");
Serial.print(fname);
Serial.print(' ');
Serial.print(fyear);
Serial.print('.');
Serial.print(fmonth);
Serial.print('.');
Serial.print(fday);
Serial.print(' ');
Serial.print(fhour);
Serial.print(':');
Serial.print(fminute);
Serial.print(':');
Serial.println(fsecond);
}
void makeFiles(){
for(byte a = 0; a < 10; a++){
fname[4] = (char)(a+0x30);//change the number in the file name(a + ASCII hex number(0))
Serial.println(fname);
flashDrive.setFileName(fname);
flashDrive.openFile();
flashDrive.writeFile(adat, strlen(adat));
flashDrive.closeFile();
flashDrive.setFileName(fname);
flashDrive.openFile();
flashDrive.setYear(random(1990, 2020)); // generate and set random year
flashDrive.setMonth(random(1, 12)); // generate and set random month
flashDrive.setDay(random(1, 30)); // generate and set random day
flashDrive.setHour(random(0, 23)); // generate and set random hour
flashDrive.setMinute(random(0, 59)); // generate and set random minute
flashDrive.setSecond(random(0, 59)); // generate and set random second
flashDrive.saveFileAttrb(); //save the changed data
flashDrive.closeFile();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 KiB

View File

@ -0,0 +1,75 @@
#######################################
# Syntax Coloring Map For Ch376
#######################################
# Class
#######################################
Ch376msc KEYWORD1
#######################################
# Methods and Functions
#######################################
init KEYWORD2
driveReady KEYWORD2
saveFileAttrb KEYWORD2
openFile KEYWORD2
closeFile KEYWORD2
moveCursor KEYWORD2
deleteFile KEYWORD2
pingDevice KEYWORD2
listDir KEYWORD2
readFile KEYWORD2
readFileUntil KEYWORD2
readRaw KEYWORD2
readLong KEYWORD2
readULong KEYWORD2
readDouble KEYWORD2
writeNum KEYWORD2
writeNumln KEYWORD2
writeFile KEYWORD2
writeRaw KEYWORD2
writeChar KEYWORD2
checkIntMessage KEYWORD2
cd KEYWORD2
resetFileList KEYWORD2
getFreeSectors KEYWORD2
getTotalSectors KEYWORD2
getFileSize KEYWORD2
getFileAttrb KEYWORD2
getYear KEYWORD2
getMonth KEYWORD2
getDay KEYWORD2
getHour KEYWORD2
getMinute KEYWORD2
getSecond KEYWORD2
getStatus KEYWORD2
getFileSystem KEYWORD2
getFileName KEYWORD2
getFileSizeStr KEYWORD2
getDeviceStatus KEYWORD2
getCHpresence KEYWORD2
getSource KEYWORD2
getError KEYWORD2
getCursorPos KEYWORD2
getEOF KEYWORD2
getChipVer KEYWORD2
setFileName KEYWORD2
setYear KEYWORD2
setMonth KEYWORD2
setDay KEYWORD2
setHour KEYWORD2
setMinute KEYWORD2
setSecond KEYWORD2
setSource KEYWORD2
#######################################
# Constants
#######################################
CURSORBEGIN LITERAL1
CURSOREND LITERAL1
SECTORSIZE LITERAL1
SPI_SCK_KHZ LITERAL1
SPI_SCK_MHZ LITERAL1

View File

@ -0,0 +1,9 @@
name=Ch376msc
version=1.4.4
author=György Kovács
maintainer=György Kovács <djusee@gmail.com>
sentence=A library for CH376 file manager control chip.
paragraph=Supports UART and SPI communication with the chip, read/write text files to USB flash drive and SD card.
category=Data Storage
url=https://github.com/djuseeq/Ch376msc/
architectures=avr,sam,samd,stm32,STM32F1,esp8266,esp32

View File

@ -0,0 +1,302 @@
/*
* API.cpp
*
* Created on: Jan 29, 2020
* Author: György Kovács
*/
#include "Ch376msc.h"
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::dirInfoRead(){
uint8_t tmpReturn;
if(_interface == UARTT){
sendCommand(CMD_DIR_INFO_READ);// max 16 files 0x00 - 0x0f
write(0xff);// current file is 0xff
tmpReturn = readSerDataUSB();
} else {//spi
spiBeginTransfer();
sendCommand(CMD_DIR_INFO_READ);// max 16 files 0x00 - 0x0f
write(0xff);// current file is 0xff
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
rdFatInfo();
return tmpReturn;
}
/////////////////////////////////////////////////////////////////
void Ch376msc::writeFatData(){// see fat info table under next filename
uint8_t fatInfBuffer[32]; //temporary buffer for raw file FAT info
memcpy ( &fatInfBuffer, &_fileData, sizeof(fatInfBuffer) ); //copy raw data to temporary buffer
if(_interface == SPII) spiBeginTransfer();
sendCommand(CMD_WR_OFS_DATA);
write((uint8_t)0x00);
write(32);
for(uint8_t d = 0;d < 32; d++){
write(fatInfBuffer[d]);
}
if(_interface == SPII) spiEndTransfer();
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::mount(){ // return ANSWSUCCESS or ANSW DISK DISCON
uint8_t tmpReturn = 0;
if(_interface == UARTT) {
sendCommand(CMD_DISK_MOUNT);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_DISK_MOUNT);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}//end if interface
if(!_errorCode && tmpReturn != ANSW_USB_INT_SUCCESS){
setError(tmpReturn);
}
return tmpReturn;
}
///////////////////////////////////////////////////////////////
uint8_t Ch376msc::fileEnumGo(){
uint8_t tmpReturn = 0;
if(_interface == UARTT){
sendCommand(CMD_FILE_ENUM_GO);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_FILE_ENUM_GO);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
if(!_errorCode && (tmpReturn != ANSW_USB_INT_DISK_READ) && (tmpReturn != ANSW_ERR_MISS_FILE)){
setError(tmpReturn);
}
return tmpReturn;
}
//////////////////////////////////////////////////////////////
uint8_t Ch376msc::byteRdGo(){
uint8_t tmpReturn = 0;
if(_interface == UARTT) {
sendCommand(CMD_BYTE_RD_GO);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_BYTE_RD_GO);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
if(!_errorCode && (tmpReturn != ANSW_USB_INT_DISK_READ) && (tmpReturn != ANSW_USB_INT_SUCCESS)){
setError(tmpReturn);
}
return tmpReturn;
}
//////////////////////////////////////////////////////////////
uint8_t Ch376msc::fileCreate(){
uint8_t tmpReturn = 0;
if(_interface == UARTT) {
sendCommand(CMD_FILE_CREATE);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_FILE_CREATE);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
return tmpReturn;
}
void Ch376msc::rdFatInfo(){
uint8_t fatInfBuffer[32]; //temporary buffer for raw file FAT info
uint8_t dataLength;
bool owrflow = false;
if(_interface == UARTT){
sendCommand(CMD_RD_USB_DATA0);
dataLength = readSerDataUSB();
if(dataLength > sizeof(fatInfBuffer)){
owrflow = true;
dataLength = sizeof(fatInfBuffer);
}
for(uint8_t s =0;s < dataLength;s++){
fatInfBuffer[s] = readSerDataUSB();// fill up temporary buffer
}//end for
} else {
spiBeginTransfer();
sendCommand(CMD_RD_USB_DATA0);
dataLength = spiReadData();
if(dataLength > sizeof(fatInfBuffer)){
owrflow = true;
dataLength = sizeof(fatInfBuffer);
}
for(uint8_t s =0;s < dataLength;s++){
fatInfBuffer[s] = spiReadData();// fill up temporary buffer
}//end for
spiEndTransfer();
}
if(owrflow){
setError(CH376_ERR_OVERFLOW);
} else {
memcpy ( &_fileData, &fatInfBuffer, sizeof(fatInfBuffer) ); //copy raw data to structured variable
}
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::byteWrGo(){
uint8_t tmpReturn = 0;
if(_interface == UARTT) {
sendCommand(CMD_BYTE_WR_GO);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_BYTE_WR_GO);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
if(!_errorCode && (tmpReturn != ANSW_USB_INT_DISK_WRITE) && (tmpReturn != ANSW_USB_INT_SUCCESS)){
setError(tmpReturn);
}
return tmpReturn;
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::reqByteRead(uint8_t a){
uint8_t tmpReturn = 0;
if(_interface == UARTT){
sendCommand(CMD_BYTE_READ);
write(a); // request data stream length for reading, 00 - FF
write((uint8_t)0x00);
tmpReturn= readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_BYTE_READ);
write(a); // request data stream length for reading, 00 - FF
write((uint8_t)0x00);
spiEndTransfer();
tmpReturn= spiWaitInterrupt();
}
if(!_errorCode && (tmpReturn != ANSW_USB_INT_SUCCESS) && (tmpReturn != ANSW_USB_INT_DISK_READ)){
setError(tmpReturn);
}
return tmpReturn;
}
////////////////////////////////////////////////////////////////
uint8_t Ch376msc::reqByteWrite(uint8_t a){
uint8_t tmpReturn = 0;
if(_interface == UARTT) {
sendCommand(CMD_BYTE_WRITE);
write(a); // request data stream length for writing, 00 - FF
write((uint8_t)0x00);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_BYTE_WRITE);
write(a); // request data stream length for writing, 00 - FF
write((uint8_t)0x00);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
if(!_errorCode && (tmpReturn != ANSW_USB_INT_SUCCESS) && (tmpReturn != ANSW_USB_INT_DISK_WRITE)){
setError(tmpReturn);
}
return tmpReturn;
}
/////////////////////////////////////////////////////////////////
void Ch376msc::sendFilename(){
if(_interface == SPII) spiBeginTransfer();
sendCommand(CMD_SET_FILE_NAME);
//write(0x2f); // "/" root directory
print(_filename); // filename
//write(0x5C); // ez a "\" jel
write((uint8_t)0x00); // terminating null character
if(_interface == SPII) spiEndTransfer();
}
/////////////////////////////////////////////////////////////////
void Ch376msc::rdDiskInfo(){
uint8_t dataLength;
uint8_t tmpReturn;
uint8_t tmpdata[9];
if(_interface == UARTT){
sendCommand(CMD_DISK_QUERY);
tmpReturn= readSerDataUSB();
if(tmpReturn == ANSW_USB_INT_SUCCESS){
sendCommand(CMD_RD_USB_DATA0);
dataLength = readSerDataUSB();
for(uint8_t s =0;s < dataLength;s++){
tmpdata[s] = readSerDataUSB();// fill up temporary buffer
}//end for
}//end if success
} else {
spiBeginTransfer();
sendCommand(CMD_DISK_QUERY);
spiEndTransfer();
tmpReturn= spiWaitInterrupt();
if(tmpReturn == ANSW_USB_INT_SUCCESS){
spiBeginTransfer();
sendCommand(CMD_RD_USB_DATA0);
dataLength = spiReadData();
for(uint8_t s =0;s < dataLength;s++){
tmpdata[s] = spiReadData();// fill up temporary buffer
}//end for
spiEndTransfer();
}//end if success
}//end if UART
if(tmpReturn != ANSW_USB_INT_SUCCESS){// unknown partition issue #22
if(!_errorCode){
setError(tmpReturn);
}//end if error
} else {
clearError();
_deviceAttached = true;
memcpy ( &_diskData, &tmpdata, sizeof(tmpdata) ); //copy raw data to structured variable
}
}
///////////////////////////////////////////////////////////////
uint8_t Ch376msc::dirCreate(){
uint8_t tmpReturn;
if(_interface == UARTT) {
sendCommand(CMD_DIR_CREATE);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_DIR_CREATE);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
return tmpReturn;
}
/////////////////////////////////////////////////////////////////
void Ch376msc::driveAttach(){
uint8_t tmpReturn = 0;
if(_driveSource == 0){//if USB
setMode(MODE_HOST_1);//TODO:if 5F failure
setMode(MODE_HOST_2);
if(_interface == UARTT){
tmpReturn = readSerDataUSB();
} else {
tmpReturn = spiWaitInterrupt();
}//end if interface
}//end if usb
if(tmpReturn == ANSW_USB_INT_CONNECT){// TODO: itt figyelni
for(uint8_t a = 0;a < 5;a++){//try to mount, delay in worst case ~(number of attempts * ANSWTIMEOUT ms)
tmpReturn = mount();
if(tmpReturn == ANSW_USB_INT_SUCCESS){
clearError();
_deviceAttached = true;
break;
} else if(_errorCode != CH376_ERR_TIMEOUT){
break;
}//end if Success
}//end for
} else driveDetach();
if(_deviceAttached) rdDiskInfo();
}
///////////////
void Ch376msc::driveDetach(){
if(_driveSource == 0){//if USB
setMode(MODE_HOST_0);
}
_deviceAttached = false;
rstDriveContainer();
rstFileContainer();
}

View File

@ -0,0 +1,368 @@
/*
* Ch376msc.cpp
*
* Created on: Feb 25, 2019
* Author: György Kovács
*/
#include "Ch376msc.h"
//with HW serial
Ch376msc::Ch376msc(HardwareSerial &usb, uint32_t speed) { // @suppress("Class members should be properly initialized")
_interface = UARTT;
_comPortHW = &usb;
_comPort = &usb;
_speed = speed;
_hwSerial = true;
}
//with soft serial
Ch376msc::Ch376msc(Stream &sUsb) { // @suppress("Class members should be properly initialized")
_interface = UARTT;
_comPort = &sUsb;
_speed = 9600;
_hwSerial = false;
}
Ch376msc::Ch376msc(uint8_t spiSelect, uint8_t intPin, SPISettings speed){ // @suppress("Class members should be properly initialized")
_interface = SPII;
_intPin = intPin;
_spiChipSelect = spiSelect;
//_speed = speed;
_spiSpeed = speed;
}
//with SPI, MISO as INT pin(the SPI bus is only available for CH376, SPI bus can`t be shared with other SPI devices)
Ch376msc::Ch376msc(uint8_t spiSelect, SPISettings speed){ // @suppress("Class members should be properly initialized")
_interface = SPII;
_intPin = MISO; // use the SPI MISO for interrupt JUST if no other device is using the SPI bus!!
_spiChipSelect = spiSelect;
//_speed = speed;
_spiSpeed = speed;
}
Ch376msc::~Ch376msc() {
// Auto-generated destructor stub
}
/////////////////////////////////////////////////////////////////
void Ch376msc::init(){
delay(60);//wait for VCC to normalize
if(_interface == SPII){
if(_intPin != MISO){
pinMode(_intPin, INPUT_PULLUP);
}
pinMode(_spiChipSelect, OUTPUT);
digitalWrite(_spiChipSelect, HIGH);
SPI.begin();
spiBeginTransfer();
sendCommand(CMD_RESET_ALL);
spiEndTransfer();
delay(100);// wait after reset command
if(_intPin == MISO){ // if we use MISO as interrupt pin, then tell it to the device ;)
spiBeginTransfer();
sendCommand(CMD_SET_SD0_INT);
write(0x16);
write(0x90);
spiEndTransfer();
}//end if
} else {//UART
if(_hwSerial) _comPortHW->begin(9600);// start with default speed
sendCommand(CMD_RESET_ALL);
delay(100);// wait after reset command, according to the datasheet 35ms is required, but that was too short
if(_hwSerial){ // if Hardware serial is initialized
setSpeed(); // Dynamically configure the com speed
}
}//end if UART
_controllerReady = pingDevice();// check the communication
if(_controllerReady) clearError();// reinit clear last error code
setMode(MODE_HOST_0);
checkIntMessage();
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::pingDevice(){
uint8_t tmpReturn = 0;
if(_interface == UARTT){
sendCommand(CMD_CHECK_EXIST);
write(0x01); // ez ertek negaltjat adja vissza
if(readSerDataUSB() == 0xFE){
tmpReturn = 1;//true
}
} else {
spiBeginTransfer();
sendCommand(CMD_CHECK_EXIST);
write(0x01); // ez ertek negaltjat adja vissza
if(spiReadData() == 0xFE){
tmpReturn = 1;//true
}
spiEndTransfer();
}
if(!tmpReturn){
setError(CH376_ERR_NO_RESPONSE);
}
return tmpReturn;
}
/////////////////////////////////////////////////////////////////
bool Ch376msc::driveReady(){//returns TRUE if the drive ready
uint8_t tmpReturn = 0;
if(_driveSource == 1){//if SD
if(!_dirDepth){// just check SD card if it's in root dir
setMode(MODE_DEFAULT);//reinit otherwise is not possible to detect if the SD card is removed
setMode(MODE_HOST_SD);
tmpReturn = mount();
if(tmpReturn == ANSW_USB_INT_SUCCESS){
rdDiskInfo();
} else {
driveDetach(); // do reinit otherwise mount will return always "drive is present"
}//end if INT_SUCCESS
} else tmpReturn = ANSW_USB_INT_SUCCESS;//end if not ROOT
} else {//if USB
tmpReturn = mount();
if(tmpReturn == ANSW_USB_INT_SUCCESS){
rdDiskInfo();
}//end if not INT SUCCESS
}//end if interface
return _deviceAttached;
}
/////////////////////////////////////////////////////////////////
bool Ch376msc::checkIntMessage(){ //always call this function to get INT# message if thumb drive are attached/detached
uint8_t tmpReturn = 0;
bool intRequest = false;
if(_interface == UARTT){
while(_comPort->available()){ // while is needed, after connecting media, the ch376 send 3 message(connect, disconnect, connect)
tmpReturn = readSerDataUSB();
}//end while
} else {//spi
while(!digitalRead(_intPin)){
tmpReturn = getInterrupt(); // get int message
delay(10);//sadly but it required for stability, sometime prior attaching drive the CH376 produce more interrupts
}// end while
}//end if interface
switch(tmpReturn){ // 0x15 device attached, 0x16 device disconnect
case ANSW_USB_INT_CONNECT:
intRequest = true;
driveAttach();//device attached
break;
case ANSW_USB_INT_DISCONNECT:
intRequest = true;
driveDetach();//device detached
break;
}//end switch
return intRequest;
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::openFile(){
if(!_deviceAttached) return 0x00;
if(_interface == UARTT){
sendCommand(CMD_FILE_OPEN);
_answer = readSerDataUSB();
} else {//spi
spiBeginTransfer();
sendCommand(CMD_FILE_OPEN);
spiEndTransfer();
_answer = spiWaitInterrupt();
}
if(_answer == ANSW_USB_INT_SUCCESS){ // get the file size
dirInfoRead();
}
return _answer;
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::saveFileAttrb(){
uint8_t tmpReturn = 0;
if(!_deviceAttached) return 0x00;
_fileWrite = 1;
if(_interface == UARTT) {
sendCommand(CMD_DIR_INFO_READ);
write(0xff);// current file is 0xff
readSerDataUSB();
writeFatData();//send fat data
sendCommand(CMD_DIR_INFO_SAVE);
tmpReturn = readSerDataUSB();
} else {//spi
spiBeginTransfer();
sendCommand(CMD_DIR_INFO_READ);
write(0xff);// current file is 0xff
spiEndTransfer();
spiWaitInterrupt();
writeFatData();//send fat data
spiBeginTransfer();
sendCommand(CMD_DIR_INFO_SAVE);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
return tmpReturn;
}
////////////////////////////////////////////////////////////////
uint8_t Ch376msc::closeFile(){ // 0x00 - w/o filesize update, 0x01 with filesize update
uint8_t tmpReturn = 0;
uint8_t d = 0x00;
if(!_deviceAttached) return 0x00;
if(_fileWrite == 1){ // if closing file after write procedure
d = 0x01; // close with 0x01 (to update file length)
}
if(_interface == UARTT){
sendCommand(CMD_FILE_CLOSE);
write(d);
tmpReturn = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_FILE_CLOSE);
write(d);
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
cd("/", 0);//back to the root directory if any file operation has occurred
rstFileContainer();
return tmpReturn;
}
////////////////////////////////////////////////////////////////
uint8_t Ch376msc::deleteFile(){
if(!_deviceAttached) return 0x00;
if(_interface == UARTT) {
sendCommand(CMD_FILE_ERASE);
_answer = readSerDataUSB();
} else {
spiBeginTransfer();
sendCommand(CMD_FILE_ERASE);
spiEndTransfer();
_answer = spiWaitInterrupt();
}
cd("/",0);
return _answer;
}
///////////////////Listing files////////////////////////////
uint8_t Ch376msc::listDir(const char* filename){
/* __________________________________________________________________________________________________________
* | 00 - 07 | 08 - 0A | 0B | 0C | 0D | 0E - 0F | 10 - 11 | 12 - 13| 14 - 15 |
* |Filename |Extension|File attrib|User attrib|First ch del|Create time|Create date|Owner ID|Acc rights|
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* | 16 - 17 | 18 - 19 | 1A - 1B | 1C - 1F |
* |Mod. time|Mod. date|Start cluster| File size |
*
* https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system
* http://www.tavi.co.uk/phobos/fat.html
*/
bool moreFiles = true; // more files waiting for read out
bool doneFiles = false; // done with reading a file
uint32_t tmOutCnt = millis();
while(!doneFiles){
if(millis() - tmOutCnt >= ANSWTIMEOUT) setError(CH376_ERR_TIMEOUT);
if(!_deviceAttached){
moreFiles = false;
break;
}
switch (fileProcesSTM) {
case REQUEST:
setFileName(filename);
_answer = openFile();
//_fileWrite = 2; // if in subdir
fileProcesSTM = READWRITE;
break;
case READWRITE:
if(_answer == ANSW_ERR_MISS_FILE){
fileProcesSTM =DONE;
moreFiles = false;// no more files in the directory
}//end if
if(_answer == ANSW_USB_INT_DISK_READ){
rdFatInfo(); // read data to fatInfo buffer
fileProcesSTM = NEXT;
}
break;
case NEXT:
_answer = fileEnumGo(); // go for the next filename
fileProcesSTM = DONE;
break;
case DONE:
if(!moreFiles){
//closeFile(); // if no more files in the directory, close the file
//closing file is not required after print dir (return value was always 0xB4 File is closed)
fileProcesSTM = REQUEST;
} else {
fileProcesSTM = READWRITE;
}
doneFiles = true;
break;
}// end switch
}//end while
return moreFiles;
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::moveCursor(uint32_t position){
uint8_t tmpReturn = 0;
if(!_deviceAttached) return 0x00;
if(position > _fileData.size){ //fix for moveCursor issue #3 Sep 17, 2019
_sectorCounter = _fileData.size % SECTORSIZE;
} else {
_sectorCounter = position % SECTORSIZE;
}
_cursorPos.value = position;//temporary
if(_interface == SPII) spiBeginTransfer();
sendCommand(CMD_BYTE_LOCATE);
write(_cursorPos.b[0]);
write(_cursorPos.b[1]);
write(_cursorPos.b[2]);
write(_cursorPos.b[3]);
if(_interface == UARTT){
tmpReturn = readSerDataUSB();
} else {
spiEndTransfer();
tmpReturn = spiWaitInterrupt();
}
if(_cursorPos.value > _fileData.size){
_cursorPos.value = _fileData.size;//set the valid position
}
return tmpReturn;
}
//////////////////////////////////////////////////////////////////////
uint8_t Ch376msc::cd(const char* dirPath, bool mkDir){
uint8_t tmpReturn = 0;
uint8_t pathLen = strlen(dirPath);
if(!_deviceAttached) return 0x00;
_dirDepth = 0;
if(pathLen < ((MAXDIRDEPTH*8)+(MAXDIRDEPTH+1)) ){//depth*(8char filename)+(directory separators)
char input[pathLen + 1];
strcpy(input,dirPath);
setFileName("/");
tmpReturn = openFile();
char* command = strtok(input, "/");//split path into tokens
while (command != NULL && !_errorCode){
if(strlen(command) > 8){//if a dir name is longer than 8 char
tmpReturn = CH376_ERR_LONGFILENAME;
break;
}
setFileName(command);
tmpReturn = openFile();
if(tmpReturn == ANSW_USB_INT_SUCCESS){//if file already exist with this name
tmpReturn = ANSW_ERR_FOUND_NAME;
closeFile();
break;
} else if(mkDir && (tmpReturn == ANSW_ERR_MISS_FILE)){
tmpReturn = dirCreate();
if(tmpReturn != ANSW_USB_INT_SUCCESS) break;
}//end if file exist
_dirDepth++;
command = strtok (NULL, "/");
}
} else {
tmpReturn = CH376_ERR_LONGFILENAME;
}//end if path is to long
return tmpReturn;
}

View File

@ -0,0 +1,257 @@
/*
* Ch376msc.h
*
* Created on: Feb 25, 2019
* Author: György Kovács
* Copyright (c) 2019, György Kovács
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
******************************************************
* Versions: *
* ****************************************************
* v1.4.4 Sep 29, 2020
* - error handling improvement
* - new function, getChipVer()
* - bug fix, issue #34 Variable Naming conflicts with core ESP32 Variables
*
* v1.4.3 Feb 06, 2020
* - bug fix issue #22 unknown partition
* - new functions as requested in #21 , #23
* - reorganizing the library
* - added function-like macros to easy configure the SPI clock rate
*
* v1.4.2 Jan 07, 2020
* - support SD card manage(API ref. - setSource())
* - a new example of using an SD card
* - the checkDrive function name was misleading, renamed to checkIntMessage
* - improvements, bug fixes
* - unnecessary examples removed
* ****************************************************
* v1.4.1 Dec 22, 2019
* - supports other architectures
* - constructor update (skip use BUSY pin)
* - improved logic to the mount/unmount flash drive
* - directory support ( cd(); function )
* - advanced file listing with (*) wildcard character(API reference, listDir() function)
*
******************************************************
* v1.4.0 Sep 26, 2019
* - new functions
* getTotalSectors() - returns a unsigned long number, total sectors on the drive
* getFreeSectors() - returns a unsigned long number, free sectors on the drive
* getFileSystem() - returns a byte number, 0x01-FAT12, 0x02-FAT16, 0x03-FAT32
* - updated example files with a new functions
* - new example file, searching the oldest/newest file on the flash drive
* ****************************************************
* v1.3 Sep 17, 2019
* -bug fix for moveCursor issue #3
* https://github.com/djuseeq/Ch376msc/issues/3
* ****************************************************
* v1.2 Apr 24, 2019
* -bug fix for timing issue on higher SPI clock
* datasheet 7.3 Time Sequence table (TSC)
******************************************************
* v1.2 Apr 20, 2019
* -extended with SPI communication
******************************************************
* v1.1 Feb 25, 2019
* -initial version with UART communication
******************************************************
*/
#ifndef Ch376msc_H_
#define Ch376msc_H_
#include <Arduino.h>
#include "CommDef.h"
#include <Stream.h>
#include <SPI.h>
#if defined(__STM32F1__)
#include "itoa.h"
#endif
#if defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD)
#include "avr/dtostrf.h"
#endif
#define ANSWTIMEOUT 1000 // waiting for data from CH
#define MAXDIRDEPTH 3 // 3 = /subdir1/subdir2/subdir3
class Ch376msc {
public:
/////////////Constructors////////////////////////
Ch376msc(HardwareSerial &usb, uint32_t speed);//HW serial
Ch376msc(Stream &sUsb);// SW serial
//Ch376msc(uint8_t spiSelect, uint8_t intPin, uint32_t speed = SPI_SCK_KHZ(125));
//Ch376msc(uint8_t spiSelect, uint32_t speed = SPI_SCK_KHZ(125));//SPI with MISO as Interrupt pin
Ch376msc(uint8_t spiSelect, uint8_t intPin, SPISettings speed = SPI_SCK_KHZ(125));
Ch376msc(uint8_t spiSelect, SPISettings speed = SPI_SCK_KHZ(125));//SPI with MISO as Interrupt pin
virtual ~Ch376msc();//destructor
////////////////////////////////////////////////
void init();
uint8_t saveFileAttrb();
uint8_t openFile();
uint8_t closeFile();
uint8_t moveCursor(uint32_t position);
uint8_t deleteFile();
uint8_t pingDevice();
uint8_t listDir(const char* filename = "*");
uint8_t readFile(char* buffer, uint8_t b_size);
uint8_t readRaw(uint8_t* buffer, uint8_t b_size);
int32_t readLong(char trmChar = '\n');
uint32_t readULong(char trmChar = '\n');
double readDouble(char trmChar = '\n');
uint8_t writeChar(char trmChar);
uint8_t writeFile(char* buffer, uint8_t b_size);
uint8_t writeRaw(uint8_t* buffer, uint8_t b_size);
uint8_t writeNum(uint8_t buffer);
uint8_t writeNum(int8_t buffer);
uint8_t writeNum(uint16_t buffer);
uint8_t writeNum(int16_t buffer);
uint8_t writeNum(uint32_t buffer);
uint8_t writeNum(int32_t buffer);
uint8_t writeNum(double buffer);
uint8_t writeNumln(uint8_t buffer);
uint8_t writeNumln(int8_t buffer);
uint8_t writeNumln(uint16_t buffer);
uint8_t writeNumln(int16_t buffer);
uint8_t writeNumln(uint32_t buffer);
uint8_t writeNumln(int32_t buffer);
uint8_t writeNumln(double buffer);
uint8_t cd(const char* dirPath, bool mkDir);
bool readFileUntil(char trmChar, char* buffer, uint8_t b_size);
bool checkIntMessage(); // check is it any interrupt message came from CH(drive attach/detach)
bool driveReady(); // call before file operation to check thumb drive or SD card are present
void resetFileList();
//set/get
uint32_t getFreeSectors();
uint32_t getTotalSectors();
uint32_t getFileSize();
uint32_t getCursorPos();
uint16_t getYear();
uint16_t getMonth();
uint16_t getDay();
uint16_t getHour();
uint16_t getMinute();
uint16_t getSecond();
uint8_t getStatus();
uint8_t getFileSystem();
uint8_t getFileAttrb();
uint8_t getSource();
uint8_t getError();
uint8_t getChipVer();
char* getFileName();
char* getFileSizeStr();
bool getDeviceStatus(); // usb device mounted, unmounted
bool getCHpresence();
bool getEOF();
void setFileName(const char* filename = "");
void setYear(uint16_t year);
void setMonth(uint16_t month);
void setDay(uint16_t day);
void setHour(uint16_t hour);
void setMinute(uint16_t minute);
void setSecond(uint16_t second);
void setSource(uint8_t inpSource);
private:
void write(uint8_t data);
void print(const char str[]);
void spiBeginTransfer();
void spiEndTransfer();
void driveAttach();
void driveDetach();
uint8_t spiWaitInterrupt();
uint8_t spiReadData();
uint8_t mount();
uint8_t getInterrupt();
uint8_t fileEnumGo();
uint8_t byteRdGo();
uint8_t fileCreate();
uint8_t byteWrGo();
uint8_t reqByteRead(uint8_t a);
uint8_t reqByteWrite(uint8_t a);
uint8_t readSerDataUSB();
uint8_t writeMachine(uint8_t* buffer, uint8_t b_size);
uint8_t writeDataFromBuff(uint8_t* buffer);
uint8_t readDataToBuff(uint8_t* buffer, uint8_t siz);
uint8_t readMachine(uint8_t* buffer, uint8_t b_size);
uint8_t dirInfoRead();
uint8_t setMode(uint8_t mode);
uint8_t dirCreate();
void rdFatInfo();
void setSpeed();
void setError(uint8_t errCode);
void clearError();
void sendCommand(uint8_t b_parancs);
void sendFilename();
void writeFatData();
void constructDate(uint16_t value, uint8_t ymd);
void constructTime(uint16_t value, uint8_t hms);
void rdDiskInfo();
void rstFileContainer();
void rstDriveContainer();
///////Global Variables///////////////////////////////
bool _deviceAttached = false; //false USB detached, true attached
bool _controllerReady = false; // ha sikeres a kommunikacio
bool _hwSerial;
uint8_t _fileWrite = 0; // read or write mode, needed for close operation
uint8_t _dirDepth = 0;// Don't check SD card if it's in subdir
uint8_t _byteCounter = 0; //vital variable for proper reading,writing
uint8_t _answer = 0; //a CH jelenlegi statusza INTERRUPT
uint8_t _driveSource = 0;//0 = USB, 1 = SD
uint8_t _spiChipSelect; // chip select pin SPI
uint8_t _intPin; // interrupt pin
uint8_t _errorCode = 0; // Store the last error code(see datasheet or CommDef.h)
uint16_t _sectorCounter = 0;// variable for proper reading
uint32_t _speed ; // Serial communication speed
fSizeContainer _cursorPos; //unsigned long union
char _filename[12];
HardwareSerial* _comPortHW; // Serial interface
Stream* _comPort;
SPISettings _spiSpeed;
commInterface _interface;
fileProcessENUM fileProcesSTM = REQUEST;
fatFileInfo _fileData;
diskInfo _diskData;
};//end class
#endif /* Ch376msc_H_ */

View File

@ -0,0 +1,93 @@
/*
* Comm.cpp
*
* Created on: Apr 6, 2019
* Author: György Kovács
*/
#include "Ch376msc.h"
/////////////////Alap parancs kuldes az USB fele/////////////////
void Ch376msc::sendCommand(uint8_t b_parancs){
if(_interface == UARTT){
write(0x57);// UART first sync command
write(0xAB);// UART second sync command
}//end if
write(b_parancs);
}
//////////////////////////////////////////////////
uint8_t Ch376msc::readSerDataUSB(){
uint32_t oldMillis = millis();
while (!_comPort->available()){ // wait until data is arrive
if ((millis()- oldMillis) > ANSWTIMEOUT){
setError(CH376_ERR_TIMEOUT);
return 0x00; // Timeout valasz
}//end if
}//end while
return _comPort->read();
}
void Ch376msc::write(uint8_t data){
if(_interface == UARTT){
_comPort->write(data);
} else { // SPI
delayMicroseconds(2);//datasheet TSC min 1.5uSec
SPI.transfer(data);
}
}//end SPI
uint8_t Ch376msc::spiReadData(){
delayMicroseconds(2);//datasheet TSC min 1.5uSec
return SPI.transfer(0x00);
}
void Ch376msc::print(const char str[]){
uint8_t stringCounter = 0;
if(_interface == UARTT){
_comPort->print(str);
} else { // SPI
while(str[stringCounter]){ ///while not NULL
write(str[stringCounter]);
stringCounter++;
}
}
}
uint8_t Ch376msc::spiWaitInterrupt(){
uint8_t answ = 0xFF;
uint32_t oldMillis = millis();
while(digitalRead(_intPin)){
if ((millis()- oldMillis) > ANSWTIMEOUT){
setError(CH376_ERR_TIMEOUT);
return 0x00;
}//end if
}//end while
answ = getInterrupt();
return answ;
}
uint8_t Ch376msc::getInterrupt(){
uint8_t _tmpReturn = 0;
spiBeginTransfer();
sendCommand(CMD_GET_STATUS);
_tmpReturn = spiReadData();
spiEndTransfer();
return _tmpReturn;
}
void Ch376msc::spiBeginTransfer(){
delayMicroseconds(2);
//SPI.beginTransaction(SPISettings(_speed, MSBFIRST, SPI_MODE0));
SPI.beginTransaction(_spiSpeed);
digitalWrite(_spiChipSelect, LOW);
}
void Ch376msc::spiEndTransfer(){
digitalWrite(_spiChipSelect, HIGH);
SPI.endTransaction();
}

View File

@ -0,0 +1,315 @@
const uint32_t CURSORBEGIN = 0x00000000;
const uint32_t CURSOREND = 0xFFFFFFFF;
const uint16_t SECTORSIZE = 512;
//#define SPI_SCK_KHZ(speedKhz) (1000UL * speedKhz) //get the speed in Hz
//#define SPI_SCK_MHZ(speedMhz) (1000000UL * speedMhz) //get the speed in Hz
#define SPI_SCK_KHZ(speedKhz) SPISettings(1000UL * speedKhz, MSBFIRST, SPI_MODE0) //with this macro i save ~100byte program space
#define SPI_SCK_MHZ(speedMhz) SPISettings(1000000UL * speedMhz, MSBFIRST, SPI_MODE0)
////////////Commands/////////
const uint8_t CMD_GET_IC_VER = 0x01;
//Result: 1 byte in data register, version number & 0x3F
const uint8_t CMD_SET_BAUDRATE = 0x02;
//Serial port speed
//const uint8_t CMD_ENTER_SLEEP = 0x03;
//Put device into sleep mode.
//const uint8_t CMD_SET_USB_SPEED = 0x04;
/* The command sets the USB bus speed. The command requires a data input for selecting USB bus speed, corresponding to 00H
* 12Mbps full mode, 01H at full speed corresponding to 1.5Mbps mode (non-standard mode),
* 02H 1.5Mbps corresponding to the low speed mode. CH376 USB bus speed of 12Mbps full-speed mode by default,
* and execution will be automatically restored to full speed 12Mbps mode after CMD_SET_USB_MODE command sets USB mode.
*/
const uint8_t CMD_RESET_ALL = 0x05;
//Need to wait 35ms before device is ready again
const uint8_t CMD_CHECK_EXIST = 0x06;
//Test that the interface exists and works.
//Input: one data byte
//Output: !input
const uint8_t CMD_SET_SD0_INT = 0x0b; // use SPI MISO pin as INT input
//const uint8_t CMD_SET_RETRY = 0x0b;
// Input: 0x25, setup retry times
// bit7=1 for infinite retry, bit3~0 retry times
const uint8_t CMD_GET_FILE_SIZE = 0x0c;
//Input: 0x68
//Output: file length on 4 bytes
//const uint8_t CMD_SET_USB_ADDRESS = 0x13;
/* This command sets the USB device address.
* The command requires a data input for selecting the USB device address is operated. After a reset or a USB device is
* connected or disconnected, the USB device address is always 00H, 00H and the
* microcontroller through a USB device Default address communication. If the microcontroller through a
* standard USB requests an address set up USB device, then you must also set the same USB device address by this command,
* in order to address the new CH376 USB device communication. //Chinese doc
*/
const uint8_t CMD_SET_USB_MODE = 0x15;
/* Switch between different USB modes.
Input:
00: invalid device mode (reset default)
01: usb device, "peripheral firmware"
02: usb device, "inner firmware"
03: SD host, manage SD cards
04: invalid usb host
05: usb host, don't generate SOF
06: usb host, generate SOF
07: usb host, bus reset
Output:
0x51: success
0x5F: failure
*/
const uint8_t MODE_HOST_0 = 0x05;
const uint8_t MODE_HOST_1 = 0x07;
const uint8_t MODE_HOST_2 = 0x06;
const uint8_t MODE_HOST_SD = 0x03;
const uint8_t MODE_DEFAULT = 0x00;
const uint8_t CMD_GET_STATUS = 0x22;
//Get interrupt status after an interrupt was triggered.
//Output: interrupt status (see below)
const uint8_t CMD_RD_USB_DATA0 = 0x27;
/* Read data from interrupt port, or USB receive buffer.
Output: length + data
*/
const uint8_t CMD_WR_USB_DATA = 0x2c;
//Write data to transfer buffer
//Input: length + data
const uint8_t CMD_WR_REQ_DATA = 0x2d;
/* Write requested data
Used when writing to files
Output (before input!): length of chunk to write
Input: data to fill the requested length
*/
const uint8_t CMD_WR_OFS_DATA = 0x2e;
//Write data to buffer with offset
//Input: offset, length, data
const uint8_t CMD_SET_FILE_NAME = 0x2f;
/* Set file or directory name for filesystem operations
Input: null-terminated string
The command accepts at most 14 characters. File name must start with '/'.
Special values:
"": do not open anything
"*": list every files
"/": open root directory
"/FOO.TXT": file in root directory
"FOO.TXT": file in current directory
*/
//These commands have no direct output, instead they trigger an interrupt when done running.
const uint8_t CMD_DISK_CONNECT = 0x30;
//Wait for USB mass storage to be connected
//Interrupt with USB_INT_SUCCESS if drive is ready.
const uint8_t CMD_DISK_MOUNT = 0x31;
//Mount detected USB drive.
//Triggers USB_INT_SUCCESS and returns 36 byte drive identifier in interrupt buffer.
const uint8_t CMD_FILE_OPEN = 0x32;
/* Open a file or directory.
Can also return ERR_MISS_FILE if the file is not found.
*/
const uint8_t CMD_FILE_ENUM_GO = 0x33;
/* Enumerate next file
Used for reading directory catalog, get next FAT32 entry
Use CMD_SET_FILE_NAME with a pattern (eg. "/ *" to list all files in root dir).
Then use FILE_OPEN to get the first matching file.
Interrupt status will be USB_INT_DISK_READ, data will be the FAT32 directory entry
Then use this command to move on to the next matching file until the interrupt is ERR_MISS_FILE.
*/
const uint8_t CMD_FILE_CREATE = 0x34;
/* Create a file (or truncate an existing file).
The file must be open (you will get ERR_MISS_FILE) before creating.
The default date is 2004/1/1 and length is 1 byte.
USe DIR_INFO_READ and DIR_INFO_SAVE to edit the directory entry.
*/
const uint8_t CMD_FILE_ERASE = 0x35;
/* Delete a file.
Make sure the current file is closed first or it will also be deleted!
Use SET_FILE_NAME then CMD_FILE_ERASE
*/
const uint8_t CMD_FILE_CLOSE = 0x36;
/* Close an open file.
* Input: 1 to update file length, 0 to leave it unchanged
*/
const uint8_t CMD_DIR_INFO_READ = 0x37;
/* Read directory info
* Input one byte which is the id of the file to get info from (in the current dir). Only the first
* 16 entries can be accessed this way!
* Otherwise, first open the file then query for entry 0xFF. The FAT entry for the currently open
* file will be returned.
* The data is returned in the interrupt stream.
*/
const uint8_t CMD_DIR_INFO_SAVE = 0x38;
/* Update directory info
* You can modify the directory entry using WR_OFS_DATA and then write it again using this command.
*/
const uint8_t CMD_BYTE_LOCATE = 0x39;
/* Seek to position in file
* Input: 4 byte file offset
* Returns USB_INT_SUCCESS with new (absolute) offset or FFFFFFFF if reached end of file.
* Moving to FFFFFFFF actually seeks to the end of the file (to write in append mode)
*/
const uint8_t CMD_BYTE_READ = 0x3a;
/* Read from file
* Data is returned in chunks of 255 bytes max at a time as interrupt data, then BYTE_RD_GO must be
* used to get next chunk (as long as the interrupt status is USB_INT_DISK_READ).
* If the pointer becomes USB_INT_SUCCESS before the requested number of bytes has been read, it
* means the EOF was reached.
* Input: number of bytes to read (16 bit)
*/
const uint8_t CMD_BYTE_RD_GO = 0x3b;
//Get next chunk of data after BYTE_READ
const uint8_t CMD_BYTE_WRITE = 0x3c;
/* Write to file
* Triggers interrupt USB_INT_DISK_WRITE. MCU should ask how much bytes to write using WR_REQ_DATA
* and send the bytes. Operation is finished when the interrupt is USB_INT_SUCCESS.
* Size in FAT will be updated when closing the file.
*/
const uint8_t CMD_BYTE_WR_GO = 0x3d;
//Continue write operation, after a WR_REQ_DATA if the interrupt is not INT_SUCCESS yet.
const uint8_t CMD_DISK_CAPACITY = 0x3e;
//Get the number of sectors on disk (interrupt return, 4 bytes).
const uint8_t CMD_DISK_QUERY = 0x3f;
/* Get the info about the FAT partition via interrupt data:
* 4 bytes: total number of sectors
* 4 bytes: number of free sectors
* 1 byte: partition type
*/
const uint8_t CMD_DIR_CREATE = 0x40;
/* Create and open a directory (name must be set using SET_FILE_NAME).
* Open an already existing directory (does not truncate)
* Returns ERR_FOUND_NAME if the name exists but is a file
* As with FILE_CREATE, the FAT entry can be edited (default values are the same except size is 0 and
* directory attribute is set)
*/
//const uint8_t CMD_SET_ADDRESS = 0x45;
/* The command is to set the USB control transfer command address. The command requires a data input,
* a new USB device address is specified, the effective address is 00H ~ 7FH.
* This command is used to simplify the standard USB requests SET_ADDRESS,
* CH376 interrupt request to the MCU after the command is completed,
* if the interrupt status is USB_INT_SUCCESS, then the command is executed successfully.//Chinese doc
*/
//const uint8_t CMD_GET_DESCR = 0x46;
/* This command is to obtain a control transfer command descriptor. This command needs to input data specifying
* the type of the descriptor to be acquired, effective type is 1 or 2, corresponding respectively to DEVICE device descriptors
* and CONFIGURATION configuration descriptor, wherein the configuration descriptor further includes an interface descriptor,
* and endpoint descriptor symbol. This command is used to simplify USB request GET_DESCRIPTOR,
* CH376 interrupt request to the microcontroller upon completion of the command, if the interrupt status is USB_INT_SUCCESS,
* then the command is executed successfully, the device can be acquired by CMD_RD_USB_DATA0 command descriptor data.
* Since the control of the transmission buffer CH376 only 64 bytes, when the descriptor is longer than 64 bytes,
* the returning operation state CH376 USB_INT_BUF_OVER, for the USB device, the device can be controlled by CMD_ISSUE_TKN_X command transmission process itself.
*/
//const uint8_t CMD_SET_CONFIG = 0x49;
/* The command set is a control transfer instruction USB configuration. The command requires a data input,
* to specify a new USB configuration values, configuration 0,configuration is canceled, or should the configuration descriptor from the USB device.
* This command is used to simplify the standard USB requests SET_CONFIGURATION,CH376 interrupt request to the MCU after the command is completed,
* if the interrupt status is USB_INT_SUCCESS, then the command is executed successfully.//Chinese doc
*/
//const uint8_t CMD_AUTO_CONFIG = 0x4D;
/* This command is used to automatically configure the USB device does not support SD card.
* This command is used to simplify the initialization step ordinary USB device corresponds GET_DESCR, SET_ADDRESS,
* SET_CONFIGURATION like plurality of command sequences. CH376 After completion of the command request interrupt
* to the microcontroller, if the interrupt status is USB_INT_SUCCESS, then the command is executed successfully.
*/
//const uint8_t CMD_ISSUE_TKN_X = 0x4E;
/* The command used to trigger data transfers with the USB devices.
* The second parameter tells we are performing a control transfer (0x80), on endpoint 0 (the 4 high bits).
* An USB device has several endpoints, which are like independent communication channels.
* Endpoint 0 is used for control transfers, specific commands to configure the device.
*/
/* /////////Answers from CH376///////
*
* Interrupt status
* ================
*
* Bit 6 of the status port is 0 when an interrupt is pending.
* As read from command 0x22, status of interrupts (also clears the interrupt)
* 00 to 0F is for USB device mode (see CH372 docs)
*
* 0x2*, 0x3*: usb device error
* bit 4: parity valid (if the bit is 0 data may be corrupt)
* Low nibble:
* 0xA: NAK
* 0xE: stalled transfer
* xx00: timeout
* other: PID of device
*/
///////////////////////////////////////////////////////////////////////////////////
const uint8_t ANSW_RET_SUCCESS = 0x51; //Operation successful
const uint8_t ANSW_USB_INT_SUCCESS = 0x14; //Operation successful, no further data
const uint8_t ANSW_USB_INT_CONNECT = 0x15; //New USB device connected
const uint8_t ANSW_USB_INT_DISCONNECT = 0x16;//USB device unplugged!
const uint8_t ANSW_USB_INT_USB_READY = 0x18; //Device is ready
const uint8_t ANSW_USB_INT_DISK_READ = 0x1d; //Disk read operation
const uint8_t ANSW_USB_INT_DISK_WRITE = 0x1e;//Disk write operation
const uint8_t ANSW_RET_ABORT = 0x5F; //Operation failure
const uint8_t ANSW_USB_INT_DISK_ERR = 0x1f; //USB storage device error
const uint8_t ANSW_USB_INT_BUF_OVER = 0x17; //Buffer overflow
const uint8_t ANSW_ERR_OPEN_DIR = 0x41; //Tried to open a directory with FILE_OPEN
const uint8_t ANSW_ERR_MISS_FILE = 0x42; //File not found
const uint8_t ANSW_ERR_FOUND_NAME = 0x43;
const uint8_t ANSW_ERR_DISK_DISCON = 0x82; //Disk disconnected
const uint8_t ANSW_ERR_LARGE_SECTOR = 0x84; //Sector size is not 512 bytes
const uint8_t ANSW_ERR_TYPE_ERROR = 0x92; //Invalid partition type, reformat drive
const uint8_t ANSW_ERR_BPB_ERROR = 0xa1; //Partition not formatted
const uint8_t ANSW_ERR_DISK_FULL = 0xb1; //Disk full
const uint8_t ANSW_ERR_FDT_OVER = 0xb2; //Directory full
const uint8_t ANSW_ERR_FILE_CLOSE = 0xb4; //Attempted operation on closed file
////////////////////////////////////////////
const uint8_t CH376_ERR_OVERFLOW = 0x03;
const uint8_t CH376_ERR_TIMEOUT = 0x02;
const uint8_t CH376_ERR_NO_RESPONSE = 0x01;
const uint8_t CH376_ERR_LONGFILENAME = 0x04;
//File attributes
const uint8_t CH376_ATTR_READ_ONLY = 0x01; //read-only file
const uint8_t CH376_ATTR_HIDDEN = 0x02; //hidden file
const uint8_t CH376_ATTR_SYSTEM = 0x04; //system file
const uint8_t CH376_ATTR_VOLUME_ID = 0x08; //Volume label
const uint8_t CH376_ATTR_DIRECTORY = 0x10; //subdirectory (folder)
const uint8_t CH376_ATTR_ARCHIVE = 0x20; //archive (normal) file
////////////////////////////////////////////
enum commInterface{
UARTT,
SPII
};
enum fileProcessENUM { // for file read/write state machine
REQUEST,
NEXT,
READWRITE,
DONE
};
///////////************/////////////
union fSizeContainer
{
uint8_t b[4]; //byte
uint32_t value; //unsigned long
};
////////////////
///https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system
struct fatFileInfo{
char name[11];//11
uint8_t fattr;//1
uint8_t uattr;//1
uint8_t del; //1
uint16_t crTime;//2
uint16_t crDate;//2
uint16_t ownId;//2
uint16_t accRight;//2
uint16_t modTime;//2
uint16_t modDate;//2
uint16_t startCl;//2
uint32_t size;//4
};
///////////////
struct diskInfo{ //disk information
uint32_t totalSector; //the number of total sectors (low byte first)
uint32_t freeSector; //the number of free sectors (low byte first)
uint8_t diskFat; //FAT type: 0x01-FAT12, 0x02-FAT16, 0x03-FAT32
};

View File

@ -0,0 +1,162 @@
/*
* Read.cpp
*
* Created on: Jan 30, 2020
* Author: György Kovács
*/
#include "Ch376msc.h"
bool Ch376msc::readFileUntil(char trmChar, char* buffer, uint8_t b_size){//buffer for reading, buffer size
char tmpBuff[2];//temporary buffer to read string and analyze
bool readMore = true;
uint8_t charCnt = 0;
b_size --;// last byte is reserved for NULL terminating character
if(!_deviceAttached) return false;
for(; charCnt < b_size; charCnt ++){
readMore = readFile(tmpBuff, sizeof(tmpBuff));
buffer[charCnt] = tmpBuff[0];
if((tmpBuff[0] == trmChar) || !readMore){// reach terminate character or EOF
readMore = false;
break;
}
}
buffer[charCnt+1] = 0x00;//string terminate
return readMore;
}
///////////////////////////////////////////////////////////////////
uint8_t Ch376msc::readFile(char* buffer, uint8_t b_size){
uint8_t tmpReturn;
b_size--;// last byte is reserved for NULL terminating character
tmpReturn = readMachine((uint8_t*) buffer,b_size);
buffer[_byteCounter] = '\0';// NULL terminating char
_cursorPos.value += _byteCounter;
_byteCounter = 0;
return tmpReturn;
}
//////////////////////////////////////////////////////////////////
uint8_t Ch376msc::readRaw(uint8_t* buffer, uint8_t b_size){
uint8_t tmpReturn;
tmpReturn = readMachine(buffer,b_size);
_cursorPos.value += _byteCounter;
_byteCounter = 0; // make it 0 when the buffer is full
return tmpReturn;
}
//////////////////////////////////////////////////////////////////
int32_t Ch376msc::readLong(char trmChar){
char workBuffer[18];
int32_t retval;
readFileUntil(trmChar,workBuffer,14);
retval = atol(workBuffer);
return retval;
}
//////////////////////////////////////////////////////////////////
uint32_t Ch376msc::readULong(char trmChar){
char workBuffer[18];
uint32_t retval;
readFileUntil(trmChar,workBuffer,14);
retval = atol(workBuffer);
return retval;
}
//////////////////////////////////////////////////////////////////
double Ch376msc::readDouble(char trmChar){
double retval;
char workBuffer[18];
readFileUntil(trmChar,workBuffer,14);
retval = atof(workBuffer);
return retval;
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::readDataToBuff(uint8_t* buffer, uint8_t siz){
uint8_t oldCounter = _byteCounter; //old buffer counter
uint8_t dataLength; // data stream size
if(_interface == UARTT) {
sendCommand(CMD_RD_USB_DATA0);
dataLength = readSerDataUSB(); // data stream size
if(dataLength > siz){
setError(CH376_ERR_OVERFLOW);//overflow
return 0;
}
while(_byteCounter < (dataLength + oldCounter)){
buffer[_byteCounter]=readSerDataUSB(); // incoming data add to buffer
_byteCounter ++;
}//end while
} else {
spiBeginTransfer();
sendCommand(CMD_RD_USB_DATA0);
dataLength = spiReadData(); // data stream size
if(dataLength > siz){
setError(CH376_ERR_OVERFLOW);//overflow
return 0;
}
while(_byteCounter < (dataLength + oldCounter)){
buffer[_byteCounter]=spiReadData(); // incoming data add to buffer
_byteCounter ++;
}//end while
spiEndTransfer();
}
return dataLength;
}
//////////////////////////////////////////////////////////////
uint8_t Ch376msc::readMachine(uint8_t* buffer, uint8_t b_size){ //buffer for reading, buffer size
uint8_t tmpReturn = 0;// more data
uint8_t byteForRequest = 0 ;
bool bufferFull = false;
uint32_t tmOutCnt = 0;
_fileWrite = 0; // read mode, required for close procedure
if(_answer == ANSW_ERR_FILE_CLOSE || _answer == ANSW_ERR_MISS_FILE){
bufferFull = true;
tmpReturn = 0;// we have reached the EOF
}
tmOutCnt = millis();
while(!bufferFull){
if(millis() - tmOutCnt >= ANSWTIMEOUT) setError(CH376_ERR_TIMEOUT);
if(!_deviceAttached){
tmpReturn = 0;
break;
}
switch (fileProcesSTM) {
case REQUEST:
byteForRequest = b_size - _byteCounter;
if(_sectorCounter == SECTORSIZE){ //if one sector has read out
_sectorCounter = 0;
fileProcesSTM = NEXT;
break;
} else if((_sectorCounter + byteForRequest) > SECTORSIZE){
byteForRequest = SECTORSIZE - _sectorCounter;
}
////////////////
_answer = reqByteRead(byteForRequest);
if(_answer == ANSW_USB_INT_DISK_READ){
fileProcesSTM = READWRITE;
tmpReturn = 1; //we have not reached the EOF
} else if(_answer == ANSW_USB_INT_SUCCESS){ // no more data, EOF
fileProcesSTM = DONE;
tmpReturn = 0;
}
break;
case READWRITE:
_sectorCounter += readDataToBuff(buffer, byteForRequest); //fillup the buffer
if(_byteCounter != b_size) {
fileProcesSTM = REQUEST;
} else {
fileProcesSTM = DONE;
}
break;
case NEXT:
_answer = byteRdGo();
fileProcesSTM = REQUEST;
break;
case DONE:
fileProcesSTM = REQUEST;
bufferFull = true;
break;
}//end switch
}//end while
return tmpReturn;
}

View File

@ -0,0 +1,363 @@
/*
* SetGet.cpp
*
* Created on: Mar 27, 2019
* Author: György Kovács
*/
#include "Ch376msc.h"
//////////////////SetGet////////////////////////////
void Ch376msc::setSpeed(){ //set communication speed for HardwareSerial and device
if(_speed == 9600){ // default speed for CH
// do nothing
} else {
sendCommand(CMD_SET_BAUDRATE);
switch (_speed) {
case 19200:
_comPortHW->write(uint8_t(0x02));//detach freq. coef
_comPortHW->write(uint8_t(0xD9));//detach freq. constant
break;
case 57600:
_comPortHW->write(uint8_t(0x03));
_comPortHW->write(uint8_t(0x98));
break;
case 115200:
_comPortHW->write(uint8_t(0x03));
_comPortHW->write(uint8_t(0xCC));
break;
default:
_speed = 9600;
break;
}//end switch
_comPortHW->end();
_comPortHW->begin(_speed);
delay(2);// according to datasheet 2ms
}// end if
}
//////////////////////////////////////////////////////////
void Ch376msc::setSource(uint8_t inpSource){
if(_driveSource != inpSource){
_driveSource = inpSource;
if(_driveSource == 1){// SD mode
driveDetach();
setMode(MODE_DEFAULT);
setMode(MODE_HOST_SD);
driveAttach();
} else {// USB mode
driveDetach();
driveAttach();
}//end if SD
}//end if not
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::setMode(uint8_t mode){
uint8_t tmpReturn = 0;
if(_interface == UARTT){
sendCommand(CMD_SET_USB_MODE);
write(mode);
tmpReturn = readSerDataUSB();
} else {//spi
spiBeginTransfer();
sendCommand(CMD_SET_USB_MODE);
write(mode);
delayMicroseconds(10);
tmpReturn = spiReadData();
spiEndTransfer();
delayMicroseconds(40);
}
return tmpReturn; // success or fail
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::getSource(){
return _driveSource;
}
bool Ch376msc::getDeviceStatus(){
return _deviceAttached;
}
bool Ch376msc::getCHpresence(){
return _controllerReady;
}
uint8_t Ch376msc::getFileAttrb(){
return _fileData.fattr;
}
char* Ch376msc::getFileName(){
strncpy(_filename,_fileData.name,11);
_filename[11] = '\0';
return _filename;
}
void Ch376msc::setFileName(const char* filename){
if(_deviceAttached){
if(strlen(filename)){//copy if file name is given
strncpy(_filename,filename,12);//copy the filename string to internal filename variable
} else {
getFileName();
}
sendFilename(); // send to the CH376
}
}
uint8_t Ch376msc::getStatus(){
return _answer;
}
uint32_t Ch376msc::getFileSize(){
return _fileData.size;
}
uint32_t Ch376msc::getCursorPos(){
return _cursorPos.value;
}
char* Ch376msc::getFileSizeStr(){ // make formatted file size string from unsigned long
// final string is declared as static, return value
static char fsizeString[10];// e.g 1023 byte\0 , 9 char long + NULL terminating char
fsizeString[0] = 0; // or 1023,9 Kb\0
uint32_t ul_size = _fileData.size;
float fl_size;
char strNumber[7]; // e.g 1023,9\0 , temporary buffer
if(_deviceAttached){
if(ul_size >= 1048576){ // if the filesize is 1Mb or bigger
fl_size = ul_size / 1048576.0;
dtostrf(fl_size, 1, 1, fsizeString);//convert float to string
strcat(fsizeString," Mb"); // concatenate unit symbol
} else if(ul_size >= 1024){ // if the filesize is in Kb range
fl_size = ul_size / 1024.0;
dtostrf(fl_size, 1, 1, fsizeString);//convert float to string
strcat(fsizeString," Kb");
} else { // if the filesize is in byte range
ltoa(ul_size, strNumber, 10);// convert long to string
strcpy(fsizeString,strNumber);// copy to the final string
strcat(fsizeString," byte");// concatenate unit symbol
}//end size
}//end if attached
return fsizeString; //return the final string
}
void Ch376msc::setYear(uint16_t year){ //Year(0 = 1980, 119 = 2099 supported under DOS/Windows, theoretically up to 127 = 2107)
if(year > 2099) year = 2099;
if(year < 1980) year = 1980;
year -= 1980;
constructDate(year, 0);
}
uint16_t Ch376msc::getYear(){
uint16_t year = _fileData.modDate;
year = year>> 9;
year += 1980;
return year;
}
void Ch376msc::setMonth(uint16_t month){
if(month > 12) month = 12;
if(month < 1) month = 1;
constructDate(month, 1);
}
uint16_t Ch376msc::getMonth(){
uint16_t month = _fileData.modDate;
month = month << 7;
month = month >> 12;
return month;
}
void Ch376msc::setDay(uint16_t day){
if(day > 31) day = 31;
if(day < 1) day = 1;
constructDate(day, 2);
}
uint16_t Ch376msc::getDay(){
uint16_t day = _fileData.modDate;
day = day << 11;
day = day >> 11;
return day;
}
//
void Ch376msc::setHour(uint16_t hour){ //Coordinated Universal Time (UTC)
if(hour > 23) hour = 23;
constructTime(hour, 0);
}
uint16_t Ch376msc::getHour(){
uint16_t hour = _fileData.modTime;
hour = hour >> 11;
return hour;
}
void Ch376msc::setMinute(uint16_t minute){
if(minute > 59) minute = 59;
constructTime(minute, 1);
}
uint16_t Ch376msc::getMinute(){
uint16_t minute = _fileData.modTime;
minute = minute << 5;
minute = minute >> 10;
return minute;
}
void Ch376msc::setSecond(uint16_t second){ //! 0-58 2sec steps
if(second > 59) second = 59;
second /= 2;
constructTime(second, 2);
}
uint16_t Ch376msc::getSecond(){
uint16_t second = _fileData.modTime;
second = second << 11;
second = second >> 11;
second *= 2;
return second;
}
///////////////////////////////////
void Ch376msc::constructDate(uint16_t value, uint8_t ymd){ // 0-year, 1-month, 2-day
uint16_t tmpInt = _fileData.modDate;
uint16_t year;
uint16_t month;
uint16_t day;
/*<------- 0x19 --------> <------- 0x18 -------->
*15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
*y y y y y y y m m m m d d d d d
*/
if(_deviceAttached){
year = tmpInt >> 9;
year = year << 9;
month = tmpInt << 7;
month = month >> 12;
month = month << 5;
day = tmpInt << 11;
day = day >> 11;
switch (ymd) {
case 0://year
year = value;
year = year << 9;
break;
case 1://month
month = value;
month = month << 5;
break;
case 2://day
day = value;
break;
default:
break;
}//end switch
_fileData.modDate = year + month + day;
}//end if attached
}
void Ch376msc::constructTime(uint16_t value, uint8_t hms){
uint16_t tmpInt = _fileData.modTime;
uint16_t hour;
uint16_t minute;
uint16_t second;
/*<------- 0x17 --------> <------- 0x16 -------->
*15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
*h h h h h m m m m m m s s s s s
*/
if(_deviceAttached){
hour = tmpInt >> 11; //cut minute and second
hour = hour << 11; // set hour
minute = tmpInt << 5; // cut hour
minute = minute >> 10;// cut seconds
minute = minute << 5; // set minute
second = tmpInt << 11; // cut hour and minute
second = second >> 11; // set second
switch (hms) {
case 0://hour
hour = value;
hour = hour << 11;
break;
case 1://minute
minute = value;
minute = minute << 5;
break;
case 2://second
second = value;
break;
default:
break;
}//end switch
_fileData.modTime = hour + minute + second;
}//end if attached
}
//////////////////////////////////////////////////////
uint32_t Ch376msc::getTotalSectors(){ // disk(partition?) size in bytes = totalSector * SECTORSIZE
return _diskData.totalSector;
}
//////////////////////////////////////////////////////
uint32_t Ch376msc::getFreeSectors(){ // total free bytes = freeSector * SECTORSIZE
return _diskData.freeSector;
}
//////////////////////////////////////////////////////
uint8_t Ch376msc::getFileSystem(){ //0x01-FAT12, 0x02-FAT16, 0x03-FAT32
return _diskData.diskFat;
}
//////////////////////////////////////////////////////
void Ch376msc::clearError(){
_errorCode = 0;
}
void Ch376msc::setError(uint8_t errCode){
_errorCode = errCode;
_deviceAttached = false;
_dirDepth = 0;
_byteCounter = 0;
_answer = 0;
resetFileList();
rstDriveContainer();
rstFileContainer();
}
void Ch376msc::rstDriveContainer(){
memset(&_diskData, 0, sizeof(_diskData));// fill up with NULL disk data container
}
void Ch376msc::rstFileContainer(){
memset(&_fileData, 0, sizeof(_fileData));// fill up with NULL file data container
_filename[0] = '\0'; // put NULL char at the first place in a name string
_fileWrite = 0;
_sectorCounter = 0;
_cursorPos.value = 0;
}
uint8_t Ch376msc::getError(){
return _errorCode;
}
void Ch376msc::resetFileList(){
fileProcesSTM = REQUEST;
}
bool Ch376msc::getEOF(){
if(_cursorPos.value < _fileData.size){
return false;
} else {
return true;
}
}
uint8_t Ch376msc::getChipVer(){
uint8_t tmpReturn = 0;
if(_interface == UARTT){
sendCommand(CMD_GET_IC_VER);
//write(mode);
tmpReturn = readSerDataUSB();
} else {//spi
spiBeginTransfer();
sendCommand(CMD_GET_IC_VER);
//write(mode);
delayMicroseconds(10);
tmpReturn = spiReadData();
spiEndTransfer();
//delayMicroseconds(40);
}
return tmpReturn;
}

View File

@ -0,0 +1,228 @@
/*
* Write.cpp
*
* Created on: Jan 30, 2020
* Author: György Kovács
*/
#include "Ch376msc.h"
uint8_t Ch376msc::writeFile(char* buffer, uint8_t b_size){
return writeMachine((uint8_t*)buffer,b_size);
}
////////////////////////////////////////////////////////////
uint8_t Ch376msc::writeRaw(uint8_t* buffer, uint8_t b_size){
return writeMachine(buffer,b_size);
}
////////////////////////////////////////////////////////////
uint8_t Ch376msc::writeChar(char trmChar){
return writeMachine((uint8_t*)&trmChar,1);
}
/////////////////////////////////////////////////////////////
// Numbers
///////////////////////////8////////////////////////////////
uint8_t Ch376msc::writeNum(uint8_t buffer){
char strBuffer[4];//max 255 = 3+1 char
itoa(buffer, strBuffer, 10);
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
// // // // //
uint8_t Ch376msc::writeNum(int8_t buffer){
char strBuffer[5];//max -128 = 4+1 char
itoa(buffer, strBuffer, 10);
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
///////////////////////////ln8/////////////////////////////////
uint8_t Ch376msc::writeNumln(uint8_t buffer){
char strBuffer[6];//max 255 = 3+2+1 char
itoa(buffer, strBuffer, 10);
strcat(strBuffer,"\r\n");
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
// // // // //
uint8_t Ch376msc::writeNumln(int8_t buffer){
char strBuffer[7];//max -128 = 4+2+1 char
itoa(buffer, strBuffer, 10);
strcat(strBuffer,"\r\n");
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
/////////////////////////////////////////////////////////////
///////////////////////////16////////////////////////////////
uint8_t Ch376msc::writeNum(uint16_t buffer){
char strBuffer[6];//max 65535 = 5+1 char
itoa(buffer, strBuffer, 10);
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
// // // // //
uint8_t Ch376msc::writeNum(int16_t buffer){
char strBuffer[7];//max -32768 = 6+1 char
itoa(buffer, strBuffer, 10);
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
///////////////////////////ln16/////////////////////////////////
uint8_t Ch376msc::writeNumln(uint16_t buffer){
char strBuffer[8];//max 65535 = 5+2+1 char
itoa(buffer, strBuffer, 10);
strcat(strBuffer,"\r\n");
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
// // // // //
uint8_t Ch376msc::writeNumln(int16_t buffer){
char strBuffer[9];//max -32768 = 6+2+1 char
itoa(buffer, strBuffer, 10);
strcat(strBuffer,"\r\n");
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
/////////////////////////////////////////////////////////////
///////////////////////////32////////////////////////////////
uint8_t Ch376msc::writeNum(uint32_t buffer){
char strBuffer[11];//max 4 294 967 295 = 10+1 char
ltoa(buffer, strBuffer, 10);
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
// // // // //
uint8_t Ch376msc::writeNum(int32_t buffer){
char strBuffer[12];//max -2147483648 = 11+1 char
ltoa(buffer, strBuffer, 10);
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
///////////////////////////ln32/////////////////////////////////
uint8_t Ch376msc::writeNumln(uint32_t buffer){
char strBuffer[13];//max 4 294 967 295 = 10+2+1 char
ltoa(buffer, strBuffer, 10);
strcat(strBuffer,"\r\n");
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
// // // // //
uint8_t Ch376msc::writeNumln(int32_t buffer){
char strBuffer[14];//max -2147483648 = 11+2+1 char
ltoa(buffer, strBuffer, 10);
strcat(strBuffer,"\r\n");
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
////////////////////////////////////////////////////////////////
////////////////////////double//////////////////////////////////
uint8_t Ch376msc::writeNum(double buffer){
char strBuffer[15];
if(buffer > 4100000.00 || buffer < -4100000.00){//beyond these values, the accuracy decreases rapidly
strcpy(strBuffer,"ovf"); //constant determined by trial and error
} else {
dtostrf(buffer, 1, 2, strBuffer);
}
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
// // // // //
uint8_t Ch376msc::writeNumln(double buffer){
char strBuffer[15];
if(buffer > 4100000.00 || buffer < -4100000.00){
strcpy(strBuffer,"ovf");
} else {
dtostrf(buffer, 1, 2, strBuffer);
}
strcat(strBuffer,"\r\n");
return writeMachine((uint8_t*)strBuffer,strlen(strBuffer));
}
/////////////////////////////////////////////////////////////////
uint8_t Ch376msc::writeDataFromBuff(uint8_t* buffer){//====================
uint8_t oldCounter = _byteCounter; //old buffer counter
uint8_t dataLength; // data stream size
if(_interface == UARTT) {
sendCommand(CMD_WR_REQ_DATA);
dataLength = readSerDataUSB(); // data stream size
} else {
spiBeginTransfer();
sendCommand(CMD_WR_REQ_DATA);
dataLength = spiReadData(); // data stream size
}
while(_byteCounter < (dataLength + oldCounter)){
write(buffer[_byteCounter]); // read data from buffer and write to serial port
_byteCounter ++;
}//end while
if(_interface == SPII) spiEndTransfer();
return dataLength;
}
////////////////////////////////////////////////////////////////
uint8_t Ch376msc::writeMachine(uint8_t* buffer, uint8_t b_size){
bool diskFree = true; //free space on a disk
bool bufferFull = true; //continue to write while there is data in the temporary buffer
uint32_t tmOutCnt = 0;
if(!_deviceAttached) return 0x00;
_fileWrite = 1; // read mode, required for close procedure
_byteCounter = 0;
if(_diskData.freeSector == 0){
diskFree = false;
return diskFree;
}
if(_answer == ANSW_ERR_MISS_FILE){ // no file with given name
_answer = fileCreate();
}//end if CREATED
if(_answer == ANSW_ERR_FILE_CLOSE){
_answer = openFile();
}
if(_answer == ANSW_USB_INT_SUCCESS){ // file created succesfully
tmOutCnt = millis();
while(bufferFull){
if(millis() - tmOutCnt >= ANSWTIMEOUT) setError(CH376_ERR_TIMEOUT);
if(!_deviceAttached){
diskFree = false;
break;
}
switch (fileProcesSTM) {
case REQUEST:
_answer = reqByteWrite(b_size - _byteCounter);
if(_answer == ANSW_USB_INT_SUCCESS){
fileProcesSTM = NEXT;
} else if(_answer == ANSW_USB_INT_DISK_WRITE){
fileProcesSTM = READWRITE;
}
break;
case READWRITE:
writeDataFromBuff(buffer);
if(_byteCounter != b_size) {
fileProcesSTM = NEXT;
} else {
fileProcesSTM = DONE;
}
break;
case NEXT:
if(_diskData.freeSector > 0){
_diskData.freeSector --;
_answer = byteWrGo();
if(_answer == ANSW_USB_INT_SUCCESS){
fileProcesSTM = REQUEST;
} else if(_byteCounter != b_size ){
fileProcesSTM = READWRITE;
}
} else { // if disk is full
fileProcesSTM = DONE;
diskFree = false;
}
break;
case DONE:
fileProcesSTM = REQUEST;
_cursorPos.value += _byteCounter;
_byteCounter = 0;
_answer = byteWrGo();
bufferFull = false;
break;
}//end switch
}//end while
}// end file created
return diskFree;
}