diff --git a/blaze-lite/core/lib/spi_flash/spiFlash.cpp b/blaze-lite/core/lib/spi_flash/spiFlash.cpp new file mode 100644 index 0000000..82c4491 --- /dev/null +++ b/blaze-lite/core/lib/spi_flash/spiFlash.cpp @@ -0,0 +1,199 @@ +/* + * spiFlash.cpp - SPI Flash library for Blaze + * To use: + include the corresponding header file in your main program, DO NOT directly call this cpp file +*/ +#include "spiFlash.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +const uint8_t CS_PIN = PB8; +Adafruit_FlashTransport_SPI flash_Transport(CS_PIN, SPI); +Adafruit_SPIFlash flash_(&flash_Transport); + +FatVolume fatfs; + +// File32 root; +// File32 file; +File32 fd, kfd; + +//TODO: Make private methods to simplify code + +//Constructor: +spiFlash::spiFlash (const size_t buffer_size, const size_t k_buffer_size) + : buffer_size(buffer_size), k_buffer_size(k_buffer_size), buffer_offset(0), k_buffer_offset(0) { + obuff = new char[ buffer_size]; + kbuff = new char[k_buffer_size]; + queuedos = std::priority_queue, std::vector>, cmp_io_priority>(); +} + +spiFlash::~spiFlash () { + kflush(); + flush(); + + #ifdef NOBOARD_TEST + ::close(fd); + ::close(kfd); + #endif + + delete[] obuff; + delete[] kbuff; + +} + +//hardware setup function (this should be called in the main file setup loop): +//https://github.com/adafruit/Adafruit_SPIFlash/blob/master/examples/SdFat_datalogging/SdFat_datalogging.ino +bool spiFlash::startUp() { + if (!flash_.begin()) { + Serial.println("Error, failed to initialize flash_ chip!"); + // while (1) { + // delay(1); + // } + return false; + } + Serial.print("Flash chip JEDEC ID: 0x"); + Serial.println(flash_.getJEDECID(), HEX); + + // First call begin to mount the filesystem. Check that it returns true + // to make sure the filesystem was mounted. + if (!fatfs.begin(&flash_)) { + Serial.println("Error, failed to mount newly formatted filesystem!"); + Serial.println("Was the flash_ chip formatted with the fatfs_format example?"); + // while (1) delay(1); + return false; + } + Serial.println("Mounted filesystem!"); + return true; +} + +//Get Methods: +uint8_t spiFlash::getCS_PIN() { return CS_PIN; } + +// //Set Methods: +// void spiFlash::setCS_PIN(const uint8_t pin) { CS_PIN = pin; } + +//functionality methods: +ssize_t spiFlash::read(const size_t offset, const size_t bytes, char* buffer){ + // //error handling + // if (bytes == 0) return 0; //no bytes to read + // if (buffer == nullptr) return -1; //null data pointer + // if (lseek(fd, offset, SEEK_SET) == -1 ) return -2; //seek error + + // ssize_t bytes_read = ::read(fd, buffer, bytes); + + // return bytes_read; +} + +char spiFlash::queue(size_t bytes, char* data, char priority) { + queuedos.push(std::tie(priority, bytes, data)); + return 0; +} + +char spiFlash::buffer (const size_t bytes, const char* data) { + //function call error handling + if (bytes == 0) return 0; //no bytes to write + if (data == nullptr) return -1; //null data pointer + if (obuff == nullptr) return -2; //null buffer pointer + + ssize_t err = 0; + size_t offset = 0, temp = 0; + + memcpy(obuff + buffer_offset, data, temp = std::min(bytes, buffer_size - buffer_offset)); + buffer_offset += (offset += temp); + + while ((bytes - offset > 0) && (err == 0)) { + if (err = flush() < 0) return err; + flush(); + + memcpy(obuff, data + offset, temp = std::min(bytes - offset, buffer_size)); + buffer_offset += temp; + offset += temp; + } + + return err; +} + +//return value < 0 means error +ssize_t spiFlash::write (const size_t bytes, const char* data) { + #ifdef NOBOARD_TEST + return ::write(fd, data, bytes); + #else + return fd.write(data, bytes); + #endif +} + +ssize_t spiFlash::kwrite (const size_t bytes, const char* data) { + #ifdef NOBOARD_TEST + return ::write(kfd, data, bytes); + #else + return kfd.write(data, bytes); + #endif +} + +char spiFlash::flush (void) { + ssize_t err = write(buffer_offset, obuff); + if (err < 0) return err; + + memset(obuff, 0, buffer_size); + buffer_offset = 0; + + return 0; +} + +ssize_t spiFlash::kLog (const size_t bytes, const char* data) { + //function call error handling + if (bytes == 0) return 0; //no bytes to write + if (data == nullptr) return -1; //null data pointer + if (kbuff == nullptr) return -2; //null buffer pointer + + ssize_t err = 0; + size_t offset = 0, temp = 0; + + memcpy(kbuff + k_buffer_offset, data, temp = std::min(bytes, k_buffer_size - k_buffer_offset)); + k_buffer_offset += (offset += temp); + + while (bytes - offset > 0) { + if (err = kflush() < 0) return err; + memcpy(kbuff, data + offset, temp = std::min(bytes - offset, k_buffer_size - k_buffer_offset)); + k_buffer_offset += temp; + offset += temp; + } + + return err; +} + +char spiFlash::kflush (void) { + ssize_t err = kwrite (k_buffer_offset, kbuff); + if (err < 0) return err; + memset(kbuff, 0, k_buffer_size); + k_buffer_offset = 0; + return err; +} + +//TODO: implement error tracking +ssize_t spiFlash::tick (void) { + if(queuedos.empty()) return 0; + + bool isMandatory = std::get<0>(queuedos.top()) == spiFlash::P_MANDATORY; + ssize_t numBytes = this->buffer(std::get<1>(queuedos.top()), std::get<2>(queuedos.top())); + queuedos.pop(); + + for(; std::get<0>(queuedos.top()) == spiFlash::P_MANDATORY; queuedos.pop()) numBytes += this->buffer(std::get<1>(queuedos.top()), std::get<2>(queuedos.top())); + + if(isMandatory) this->flush(); + return numBytes; +} + +bool spiFlash::cmp_io_priority:: operator()(const std::tuple& l, const std::tuple& r) const { + return std::get<0>(l) > std::get<0>(r); +} diff --git a/blaze-lite/core/lib/spi_flash/spiFlash.h b/blaze-lite/core/lib/spi_flash/spiFlash.h new file mode 100644 index 0000000..f775146 --- /dev/null +++ b/blaze-lite/core/lib/spi_flash/spiFlash.h @@ -0,0 +1,93 @@ +#ifndef SPI_FLASH_H +#define SPI_FLASH_H + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//TODO: Documentation +class spiFlash { + public: + + static constexpr const char + P_MANDATORY = 0, //just force writes this at next tick + P_URGENT = 1, + P_IMPORTANT = 2, + P_STD = 3, + P_UNIMPORTANT = 4, + P_OPTIONAL = 5 + ; + //Constructor: + spiFlash (const size_t buffer_size = 512, const size_t k_buffer_size = 512) ; + + //destructor: + ~spiFlash(); + + //setup function: + bool startUp(); + + //Get Methods: + uint8_t getCS_PIN(); + + //functionality methods: + + char buffer (const size_t bytes, const char* data); + + char flush (void); + + ssize_t kLog (const size_t bytes, const char* data); + + char kflush (void); + + ssize_t kwrite (const size_t bytes, const char* data); + + char queue (size_t bytes, char* data, char priority = P_UNIMPORTANT); + + ssize_t read (const size_t offset, const size_t bytes, char* buffer); + + ssize_t tick (void); + + ssize_t write (const size_t bytes, const char* data); + + //types + struct cmp_io_priority { + bool operator()(const std::tuple& l, const std::tuple& r) const ; + }; + + const size_t buffer_size; + const size_t k_buffer_size; + + private: + uint8_t CS_PIN; + + std::priority_queue< + // priority size data + std::tuple, + std::vector>, + cmp_io_priority + > queuedos; + + char* obuff; + size_t buffer_offset; + + char* kbuff; + size_t k_buffer_offset; + + #ifdef NOBOARD_TEST + int fd, kfd; + #endif +}; + +#endif diff --git a/blaze-lite/core/lib/spi_flash/spliFlashTester.cpp b/blaze-lite/core/lib/spi_flash/spliFlashTester.cpp new file mode 100644 index 0000000..e562b8e --- /dev/null +++ b/blaze-lite/core/lib/spi_flash/spliFlashTester.cpp @@ -0,0 +1,93 @@ +#include "spiFlash.h" +#include + +class SpiFlashTester{ +private: + void printResult(const char* testName, bool passed) { + Serial.print(testName); + Serial.print(": "); + Serial.println(passed ? "PASS" : "FAIL"); + } + + void testStartUp(){ + spiFlash spiFlash(256, 128); + bool ok = spiFlash.startUp(); + printResult("StartUp test: ", ok); + } + + void testReadWrite(){ + spiFlash spiFlash(256, 128); + spiFlash.startUp(); + + const char msg = "HELLO ROCKET TEAM!!"; + char readback[sizeof(msg)] = {0}; // open memory for our data + + ssize_t written = spiFlash.write(sizeof(msg), msg); // save what the flash interprets from our message + ssize_t read = spiFlash.read(0, sizeof(msg), readback); // read the data into space we opened for it + + bool test = ((written == sizeof(msg)) && (read == sizeof(msg)) && !std::memcmp(written, read, sizeof(msg))); + printResult("Basic Read Write test: ", ok); + } + + void testBufferFlush() { + spiFlash spiFlash(256, 128); + spiFlash.startUp(); + + const char msg[] = "BUFFERED"; + char out[sizeof(msg)] = {0}; + + spiFlash.buffer(sizeof(msg), msg); + spiFlash.flush(); + spiFlash.read(0, sizeof(msg), out); + + bool ok = !std::memcmp(msg, out, sizeof(msg)); + printResult("Buffer flush: ", ok); + } + + void testQueuePriority() { + spiFlash spiFlash(256, 128); + spiFlash.startUp(); + + char low[] = "LOW"; + char high[] = "HIGH"; + + spiFlash.queue(sizeof(low), low, spiFlash::P_UNIMPORTANT); + spiFlash.queue(sizeof(high), high, spiFlash::P_URGENT); + + while (spiFlash.tick() > 0) {} + + char out1[5] = {0}; + char out2[4] = {0}; + + spiFlash.read(0, sizeof(high), out1); + spiFlash.read(sizeof(high), sizeof(low), out2); + + bool ok = (std::strcmp(out1, "HIGH") == 0 && std::strcmp(out2, "LOW") == 0); + printResult("Queue priority: ", ok); + } + + +public: + void run() { + Serial.println("SPI Flash Tester: start"); + testStartup(); + testReadWrite(); + testBufferFlush(); + testQueuePriority(); + Serial.println("SPI Flash Tester: done"); + } + ~SpiFlashTester(); + +}; + +SpiFlashTester:: SpiFlashTester() = default; +SpiFlashTester::~SpiFlashTester() = default; + +void setup () { + auto tester = SpiFlashTester(); + tester.run(); +} + +void loop () { + delay(100); //so we dont fry the arduino +} diff --git a/blaze-lite/core/platformio.ini b/blaze-lite/core/platformio.ini index 037ace3..9da3849 100644 --- a/blaze-lite/core/platformio.ini +++ b/blaze-lite/core/platformio.ini @@ -13,6 +13,12 @@ platform = ststm32 board = blackpill_f411ce framework = arduino upload_protocol = dfu +build_flags = + -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC + -D USBCON +lib_deps = + adafruit/SdFat - Adafruit Fork@^2.3.102 + adafruit/Adafruit SPIFlash@^5.1.1 build_flags = -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC -D USBCON diff --git a/blaze-lite/core/test/spiUnittests.cpp b/blaze-lite/core/test/spiUnittests.cpp new file mode 100644 index 0000000..d0431a8 --- /dev/null +++ b/blaze-lite/core/test/spiUnittests.cpp @@ -0,0 +1,138 @@ +/* + This file contains the unit tests for the spiFlash class. +*/ +#define NOBOARD_TEST +#include "spiFlash.h" + +using std::cout, std::endl, std::cin; +/* +tests: +1. write +2. kwrite + +3. buffer - 1 small msg +4. flush +5. kLog - 1 small msg +6. kflush + +7. buffer - 2 small msgs +8. kLog - 2 small msgs + +9. buffer - 1 large msg +10. kLog - 1 large msg + +11. buffer - 1 xl msg +12. kLog - 1 xl msg + +13. buffer - 2 large msgs +14. kLog - 2 large msgs + +15. buffer - 1 xl msg -> 1 l msg +16. kLog - 1 xl msg -> 1 l msg + +17. queue +18. tick +*/ + +#define GREETING "Hello World!\n" +#define MSG1 "This is the first msg.\n" +#define MSG2 "This is the second msg.\n" +#define MSGL msgl +#define MSGL2 msgl2 +#define MSGXL msgxl + +#define BUFF_SIZE 256 + +int main () { + char wait; + char msgl[BUFF_SIZE]; + char msgl2[BUFF_SIZE]; + char msgxl[BUFF_SIZE + 8]; + + memset(msgl, '1', sizeof(msgl)); + memset(msgl2, '2', sizeof(msgl2)); + memset(msgxl, 'X', sizeof(msgxl)); + + spiFlash f(0, BUFF_SIZE, BUFF_SIZE); + + + f.write(sizeof(GREETING), GREETING); + cout << "test 1 done" << endl; + cin >> wait; + + f.kwrite(sizeof(GREETING), GREETING); + cout << "test 2 done" << endl; + cin >> wait; + + f.buffer(sizeof(MSG1), MSG1); + cout << "test 3 done" << endl; + cin >> wait; + + f.flush(); + cout << "test 4 done" << endl; + cin >> wait; + + f.kLog(sizeof(MSG1), MSG1); + cout << "test 5 done" << endl; + cin >> wait; + + f.kflush(); + cout << "test 6 done" << endl; + cin >> wait; + + f.buffer(sizeof(MSG1), MSG1); + f.buffer(sizeof(MSG2), MSG2); + f.flush(); + cout << "test 7 done" << endl; + cin >> wait; + + f.kLog(sizeof(MSG1), MSG1); + f.kLog(sizeof(MSG2), MSG2); + f.kflush(); + cout << "test 8 done" << endl; + cin >> wait; + + f.buffer(sizeof(MSGL), MSGL); + f.flush(); + cout << "test 9 done" << endl; + cin >> wait; + + f.kLog(sizeof(MSGL), MSGL); + f.kflush(); + cout << "test 10 done" << endl; + cin >> wait; + + f.buffer(sizeof(MSGXL), MSGXL); + f.flush(); + cout << "test 11 done" << endl; + cin >> wait; + + f.kLog(sizeof(MSGXL), MSGXL); + f.kflush(); + cout << "test 12 done" << endl; + cin >> wait; + + f.buffer(sizeof(MSGL), MSGL); + f.buffer(sizeof(MSGL2), MSGL2); + f.flush(); + cout << "test 13 done" << endl; + cin >> wait; + + f.kLog(sizeof(MSGL), MSGL); + f.kLog(sizeof(MSGL2), MSGL2); + f.kflush(); + cout << "test 14 done" << endl; + cin >> wait; + + f.buffer(sizeof(MSGXL), MSGXL); + f.buffer(sizeof(MSGL2), MSGL2); + f.flush(); + cout << "test 15 done" << endl; + cin >> wait; + + f.kLog(sizeof(MSGXL), MSGXL); + f.kLog(sizeof(MSGL2), MSGL2); + f.kflush(); + cout << "test 16 done" << endl; + cin >> wait; +}