diff --git a/CM4/.settings/language.settings.xml b/CM4/.settings/language.settings.xml
index 2c6de0e..43ab036 100644
--- a/CM4/.settings/language.settings.xml
+++ b/CM4/.settings/language.settings.xml
@@ -5,7 +5,7 @@
-
+
@@ -16,7 +16,7 @@
-
+
diff --git a/CM4/Core/Src/main.c b/CM4/Core/Src/main.c
index 4249f00..f3901e8 100644
--- a/CM4/Core/Src/main.c
+++ b/CM4/Core/Src/main.c
@@ -169,8 +169,8 @@ void MX_QUADSPI_Init(void)
hqspi.Init.ClockPrescaler = 255;
hqspi.Init.FifoThreshold = 1;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
- hqspi.Init.FlashSize = 1;
- hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
+ hqspi.Init.FlashSize = 26;
+ hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
diff --git a/CM7/.cproject b/CM7/.cproject
index 1f428bd..16ba8c9 100644
--- a/CM7/.cproject
+++ b/CM7/.cproject
@@ -146,9 +146,9 @@
-
+
@@ -295,9 +295,9 @@
-
+
diff --git a/CM7/.settings/language.settings.xml b/CM7/.settings/language.settings.xml
index c9ad366..ef00650 100644
--- a/CM7/.settings/language.settings.xml
+++ b/CM7/.settings/language.settings.xml
@@ -5,7 +5,7 @@
-
+
@@ -16,7 +16,7 @@
-
+
diff --git a/CM7/.settings/stm32cubeide.project.prefs b/CM7/.settings/stm32cubeide.project.prefs
index 316c569..21ec017 100644
--- a/CM7/.settings/stm32cubeide.project.prefs
+++ b/CM7/.settings/stm32cubeide.project.prefs
@@ -1,4 +1,4 @@
635E684B79701B039C64EA45C3F84D30=EB92BDC8CA3A439D88C6F56E4DDF7E32
-8DF89ED150041C4CBC7CB9A9CAA90856=E805E3BE0E38C82E0E591B5E374240D4
-DC22A860405A8BF2F2C095E5B6529F12=31D4B83A11F9313509F3357E4EAAF47F
+8DF89ED150041C4CBC7CB9A9CAA90856=AC892FC3BC99DC619B57AAD46AD701C9
+DC22A860405A8BF2F2C095E5B6529F12=AC892FC3BC99DC619B57AAD46AD701C9
eclipse.preferences.version=1
diff --git a/CM7/Components/Data.hpp b/CM7/Components/Data.hpp
new file mode 100644
index 0000000..e3e3da6
--- /dev/null
+++ b/CM7/Components/Data.hpp
@@ -0,0 +1,55 @@
+/*
+ * Data.hpp
+ *
+ */
+
+#ifndef DATA_HPP_
+#define DATA_HPP_
+
+
+#include
+#include "SystemDefines.hpp"
+
+typedef union logFloat {
+ float f;
+ uint8_t u8[4];
+ uint32_t u32;
+} logFloat;
+
+typedef struct IMUData {
+ logFloat xAccel;
+ logFloat yAccel;
+ logFloat zAccel;
+} IMUData;
+
+typedef struct AltimeterData {
+ logFloat altitude;
+ logFloat temp;
+} AltimeterData;
+
+/*
+ * mario = Experiment Board LPS22HH U3
+ * luigi = Experiment Board LPS22HH U4
+ * bowser = Main Board MS5611 U4
+ */
+typedef struct BarometerData {
+ logFloat marioPressure;
+ logFloat marioTemperature;
+ logFloat luigiPressure;
+ logFloat luigiTemperature;
+ logFloat bowserPressure;
+ logFloat bowserTemperature;
+} BarometerData;
+
+/*
+ * Data Containers
+ * Acts as a pointer to the other data structs alongside a timestamp for logging to flash.
+ */
+typedef struct AllData {
+ IMUData* imuData;
+ AltimeterData* altimeterData;
+ BarometerData* barometerData;
+ logFloat time;
+} AllData;
+
+#endif /* DATA_HPP_ */
diff --git a/CM7/Components/Debug/DebugTask.cpp b/CM7/Components/Debug/DebugTask.cpp
index 49de559..10a6512 100644
--- a/CM7/Components/Debug/DebugTask.cpp
+++ b/CM7/Components/Debug/DebugTask.cpp
@@ -16,6 +16,7 @@
// External Tasks (to send debug commands to)
#include "FlightTask.hpp"
#include "GPIO.hpp"
+#include "FlashTask//Inc//FlashTask.hpp"
/* Macros --------------------------------------------------------------------*/
@@ -121,6 +122,22 @@ void DebugTask::HandleDebugMessage(const char* msg)
SOAR_PRINT("Current System Free Heap: %d Bytes\n", xPortGetFreeHeapSize());
SOAR_PRINT("Lowest Ever Free Heap: %d Bytes\n", xPortGetMinimumEverFreeHeapSize());
SOAR_PRINT("Debug Task Runtime \t: %d ms\n\n", TICKS_TO_MS(xTaskGetTickCount()));
+ } else if (strcmp(msg,"testflash") == 0) { //TODO delete me
+ SOAR_PRINT("testing");
+ Command test(TASK_SPECIFIC_COMMAND, FHT_GROUND);
+ uint8_t testData = 230;
+ test.CopyDataToCommand(&testData, 1);
+ FlashTask::Inst().GetEventQueue()->Send(test);
+
+ Command test2(TASK_SPECIFIC_COMMAND, FHT_APOGEE);
+ uint8_t test2Data = 230;
+ test2.CopyDataToCommand(&test2Data, 1);
+ FlashTask::Inst().GetEventQueue()->Send(test2);
+
+ } else if (strcmp(msg, "eraseflash") == 0){
+ SOAR_PRINT("Erasing...");
+ Command erase(TASK_SPECIFIC_COMMAND, FHT_ERASE);
+ FlashTask::Inst().GetEventQueue()->Send(erase);
}
else {
// Single character command, or unknown command
diff --git a/CM7/Components/FlashTask/FlashTask.cpp b/CM7/Components/FlashTask/FlashTask.cpp
new file mode 100644
index 0000000..d2672d1
--- /dev/null
+++ b/CM7/Components/FlashTask/FlashTask.cpp
@@ -0,0 +1,278 @@
+/*
+ * FlashFSHandler.cpp
+ *
+ * Created on: Jun 8, 2024
+ * Author: goada
+ */
+
+#include "Command.hpp"
+#include "Task.hpp"
+#include "FlashTask//Inc//FlashTask.hpp"
+#include "Data.hpp"
+
+FlashTask::FlashTask() : Task(FLASH_TASK_QUEUE_DEPTH_OBJS)
+{
+}
+
+void FlashTask::InitTask()
+{
+ // Make sure the task is not already initialized
+ SOAR_ASSERT(rtTaskHandle == nullptr, "Cannot initialize flash task twice");
+
+ BaseType_t rtValue =
+ xTaskCreate((TaskFunction_t)FlashTask::RunTask,
+ (const char*)"FlashTask",
+ (uint16_t)FLASH_TASK_STACK_DEPTH_WORDS,
+ (void*)this,
+ (UBaseType_t)FLASH_TASK_RTOS_PRIORITY,
+ (TaskHandle_t*)&rtTaskHandle);
+
+ SOAR_ASSERT(rtValue == pdPASS, "FlashTask::InitTask() - xTaskCreate() failed");
+}
+
+
+void FlashTask::Run()
+{
+
+ while(flashChipHandle.init() != FLASH_OK) {osDelay(30);}
+ uint8_t buf = 0xff;
+ stateLoggingAddress = 0;
+ do
+ {
+ flashChipHandle.read(&buf, stateLoggingAddress, 1);
+ stateLoggingAddress += 20;
+ } while (buf == 0);
+
+ address.u32 = stateLoggingAddress;
+ stateLoggingAddress -= 20;
+ buf = 0;
+ flashChipHandle.write(&buf, stateLoggingAddress, 1);
+
+ while (1) {
+
+ //Process commands in blocking mode
+ Command cm;
+ bool res = qEvtQueue->ReceiveWait(cm);
+ if(res)
+ handleCommand(cm);
+
+ }
+}
+
+
+void FlashTask::handleCommand(Command& com)
+{
+ switch(com.GetCommand())
+ {
+ case TASK_SPECIFIC_COMMAND: {
+ switch (com.GetTaskCommand())
+ {
+ case FHT_WRITE_DATA: {
+ /* Expects an logFloat* array where:
+ * - Element 1 is a pointer to an AllData set (cast as uint16_t*)
+ * - 2 is a pointer to the altitude to be recorded
+ */
+
+ logFloat* toWrite[13] =
+ {
+ &(((AllData*)com.GetDataPointer())->time),
+ &(((AllData*)com.GetDataPointer())->imuData->xAccel),
+ &(((AllData*)com.GetDataPointer())->imuData->yAccel),
+ &(((AllData*)com.GetDataPointer())->imuData->zAccel),
+ &(((AllData*)com.GetDataPointer())->altimeterData->altitude),
+ &(((AllData*)com.GetDataPointer())->altimeterData->temp),
+ &(((AllData*)com.GetDataPointer())->barometerData->marioPressure),
+ &(((AllData*)com.GetDataPointer())->barometerData->marioTemperature),
+ &(((AllData*)com.GetDataPointer())->barometerData->luigiPressure),
+ &(((AllData*)com.GetDataPointer())->barometerData->luigiTemperature),
+ &(((AllData*)com.GetDataPointer())->barometerData->bowserPressure),
+ &(((AllData*)com.GetDataPointer())->barometerData->bowserTemperature),
+ (logFloat*)(com.GetDataPointer() + 1)
+ };
+
+ for (uint8_t i = 0; i < 13; i++)
+ {
+ if (address.u32 <= 100000000)
+ {
+ flashChipHandle.write(toWrite[i]->u8, address.u32, 4);
+ address.u32 += 4;
+ } else {
+ return;
+ }
+
+ }
+ break;
+ }
+ case FHT_APOGEE: {
+ flashChipHandle.write(com.GetDataPointer(), stateLoggingAddress + 5, 4);
+ break;
+ }
+ case FHT_MAIN_1: {
+ flashChipHandle.write(com.GetDataPointer(), stateLoggingAddress + 7, 4);
+ break;
+ }
+ case FHT_MAIN_2: {
+ flashChipHandle.write(com.GetDataPointer(), stateLoggingAddress + 13, 4);
+ break;
+ }
+ case FHT_GROUND: {
+ flashChipHandle.write(com.GetDataPointer(), stateLoggingAddress + 17, 4);
+ flashChipHandle.write(address.u8, stateLoggingAddress + 1, 4);
+ break;
+ }
+ case FHT_ERASE: {
+ flashChipHandle.eraseChip();
+ SOAR_PRINT("done\n");
+ break;
+ }
+ }
+ }
+ default:
+ break;
+ }
+}
+
+
+/*
+bool FlashFileSystem::Init() {
+ int mountattempts = 0;
+ e_flash_status f = flashChipHandle.init();
+ if(f == FLASH_OK) {
+ SOAR_PRINT("Initialized flash chip\n");
+ } else {
+ SOAR_PRINT("Could not initialize flash chip.\n");
+ return false;
+ }
+
+#if 0
+ flashChipHandle.eraseChip();
+ uint8_t* test_data = new uint8_t[5000];
+ for(int i = 0; i < 5000; i++) {
+ test_data[i] = i % 256;
+ }
+ flashChipHandle.write(test_data, 0x00, 5000);
+ memset(test_data,0x09,5000);
+ flashChipHandle.read(test_data, 0x00, 5000);
+ for(auto i = 0; i < 5000; i++) {
+ if(test_data[i] != i%256) {
+ SOAR_PRINT("Read fail at %d, wanted %d, was %d\n",i, i%256, test_data[i]);
+ }
+ }
+ SOAR_PRINT("Read complete 1\n");
+
+
+ for(int i = 0; i < 5000; i++) {
+ test_data[i] = (i*i) % 256;
+ }
+ flashChipHandle.write(test_data, 2001, 5000);
+ memset(test_data,0x09,5000);
+ flashChipHandle.read(test_data, 2001, 5000);
+ for(auto i = 0; i < 5000; i++) {
+ if(test_data[i] != (i*i)%256) {
+ SOAR_PRINT("Read fail at %d, wanted %d, was %d\n",i, (i*i)%256, test_data[i]);
+ }
+ }
+
+ SOAR_PRINT("Read complete 2\n");
+ //flashChipHandle.eraseSmallestSection(0x62);
+ //flashChipHandle.write(test_data, 0x62, sizeof(test_data));
+ //memset(test_data,0x00,sizeof(test_data));
+ //flashChipHandle.read(test_data, 0x62, sizeof(test_data));
+#endif
+
+ return true;
+}*/
+
+/*
+void FlashFileSystem::TestVerify() {
+
+ OpenFile("test.hi");
+ char testbuf[123];
+ for(size_t i = 0; i < sizeof(testbuf); i++) {
+ testbuf[i] = i%('~'-'!') + '!';
+ }
+ testbuf[sizeof(testbuf)-1] = 0x00;
+
+ lfs_file_write(&LFS, ¤tOpenFile, testbuf, sizeof(testbuf));
+ lfs_file_sync(&LFS, ¤tOpenFile);
+
+ printf("Written: %s\n",testbuf);
+
+ memset(testbuf,0x00,sizeof(testbuf));
+
+ lfs_file_read(&LFS, ¤tOpenFile, testbuf, sizeof(testbuf));
+
+ printf("Recovered: %s\n",testbuf);
+
+ CloseFile(¤tOpenFile);
+}
+/*
+bool FlashFileSystem::OpenFile(const char *const filename) {
+ if(currentOpenFileValid) {
+ CloseFile(¤tOpenFile);
+ }
+
+ int e = lfs_file_open(&LFS, ¤tOpenFile, filename, LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC);
+ if(e == 0) {
+ currentOpenFileValid = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void FlashFileSystem::CloseFile(lfs_file_t *file) {
+ lfs_file_close(&LFS, file);
+ currentOpenFileValid = false;
+}
+
+int lfs_ReadWrapper (const struct lfs_config *c, lfs_block_t block,
+ lfs_off_t off, void *buffer, lfs_size_t size) {
+ SOAR_PRINT("readwrapper\n");
+
+ e_flash_status s = ((FlashFileSystem*)(c->context))->flashChipHandle.read((uint8_t*)buffer, block*c->block_size + off, size);
+
+ while(((FlashFileSystem*)(c->context))->flashChipHandle.isBusy());
+ return s;
+}
+
+
+int lfs_WriteWrapper (const struct lfs_config *c, lfs_block_t block,
+ lfs_off_t off, const void *buffer, lfs_size_t size) {
+ SOAR_PRINT("writewrapper\n");
+ e_flash_status s = ((FlashFileSystem*)(c->context))->flashChipHandle.write((uint8_t*)buffer, block*c->block_size + off, size);
+
+ while(((FlashFileSystem*)(c->context))->flashChipHandle.isBusy());
+ return s;
+}
+
+int lfs_SyncWrapper (const struct lfs_config *c) {
+ SOAR_PRINT("sync wrapper\n");
+ return 0;
+}
+
+int lfs_EraseWrapper (const struct lfs_config *c, lfs_block_t block) {
+ SOAR_PRINT("erasewrapper\n");
+ e_flash_status s = ((FlashFileSystem*)(c->context))->flashChipHandle.eraseSmallestSection(block*c->block_size);
+
+ while(((FlashFileSystem*)(c->context))->flashChipHandle.isBusy());
+ return s;
+}
+
+bool FlashFileSystem::WriteAt(uint8_t *data, size_t size, uint32_t addr) {
+ return flashChipHandle.write(data, addr, size) == FLASH_OK;
+}
+
+bool FlashFileSystem::ReadAt(uint8_t *dataout, size_t size, uint32_t addr) {
+ return flashChipHandle.read(dataout, addr, size) == FLASH_OK;
+}
+
+bool FlashFileSystem::EraseBlock(uint32_t addr) {
+ return flashChipHandle.eraseSmallestSection(addr) == FLASH_OK;
+}
+
+bool FlashFileSystem::EraseChip() {
+ return flashChipHandle.eraseChip() == FLASH_OK;
+}
+*/
+
diff --git a/CM7/Components/FlashTask/Inc/FlashTask.hpp b/CM7/Components/FlashTask/Inc/FlashTask.hpp
new file mode 100644
index 0000000..b54ce7b
--- /dev/null
+++ b/CM7/Components/FlashTask/Inc/FlashTask.hpp
@@ -0,0 +1,123 @@
+/*
+ * FlashFSHandler.cpp
+ *
+ * Created on: Jun 8, 2024
+ * Author: goada
+ */
+
+
+/*Notes on the organization of things:
+ *
+ * - First page is reserved for the following:
+ * - 8 0's to flag where data is (and prevent it from being overwritten)
+ * - 32 bit address of last raw date write
+ * - 32 bit time of apoggee detection (transition from launch to drogue states)
+ * - 2x 32 bit time of main detection (transition from drouge to main states)
+ * - 32 bit time of ground detection (transition from main to postlaunch)
+ *
+ * - Every page is written as 5 writes of 400 bits
+ * - 1 copy of an AllData struct (descibed in data.hpp)
+ * - unsigned 16 bit filtered altitude
+ *
+ */
+
+#include "Sensors//Inc//W25N01GVSFIG.hpp"
+#include "Task.h"
+#include "SystemDefines.hpp"
+#include "Data.hpp"
+
+enum firstPageDataType {
+ lastWriteAddress,
+ apogeeTime,
+ mainDeactivateTime,
+ mainAcitvateTime,
+ groundTime
+};
+
+enum flashTaskCommands {
+ FHT_WRITE_DATA,
+ FHT_APOGEE,
+ FHT_MAIN_1,
+ FHT_MAIN_2,
+ FHT_GROUND,
+ FHT_ERASE
+};
+
+
+class FlashTask: public Task {
+public:
+
+ static FlashTask& Inst() {
+ static FlashTask inst;
+ return inst;
+ }
+
+ void InitTask();
+
+ bool logTransition(firstPageDataType);
+
+ bool write(struct AllData* data, uint16_t altitude);
+
+protected:
+
+ static void RunTask(void* Pvparams) {FlashTask::Inst().Run();}
+ void Run();
+
+ void handleCommand(Command& com);
+
+private:
+
+ FlashTask(); // Private constructor
+ FlashTask(const FlashTask&); // Prevent copy-construction
+ FlashTask& operator=(const FlashTask&); // Prevent assignment
+
+ void TestVerify();
+
+ W25N01GVSFIG flashChipHandle = {GPIOB, GPIO_PIN_10, nullptr, 0, 0};
+
+ logFloat address;
+ uint8_t stateLoggingAddress;
+
+ /*
+ bool WriteAt(uint8_t* data, size_t size, uint32_t addr);
+ bool ReadAt(uint8_t* dataout, size_t size, uint32_t addr);
+ bool EraseBlock(uint32_t addr);
+ bool EraseChip();
+
+
+ bool initialized;
+
+ lfs_t LFS;
+ lfs_file_t currentOpenFile;
+ bool currentOpenFileValid;
+
+
+ const lfs_config LFSCONFIG = {
+ .context = this,
+ .read = lfs_ReadWrapper,
+ .prog = lfs_WriteWrapper,
+ .erase = lfs_EraseWrapper,
+ .sync = lfs_SyncWrapper,
+
+
+
+ .read_size = 16,
+ .prog_size = 16,
+ .block_size = 0x20000,
+ .block_count = 1024,
+ .block_cycles = 500,
+ .cache_size =16,
+ .lookahead_size = 16,
+
+ //.read_buffer = readbuf,
+ //.prog_buffer = progbuf,
+ //.lookahead_buffer = lookbuf
+
+
+
+
+
+
+ };
+ */
+};
diff --git a/CM7/Components/FlightControl/FlightTask.cpp b/CM7/Components/FlightControl/FlightTask.cpp
index 196a7a6..1469b37 100644
--- a/CM7/Components/FlightControl/FlightTask.cpp
+++ b/CM7/Components/FlightControl/FlightTask.cpp
@@ -9,6 +9,8 @@
#include "GPIO.hpp"
#include "SystemDefines.hpp"
+#include "FlashTask//Inc//FlashTask.hpp" //TODO delete me
+
/**
* @brief Constructor for FlightTask
*/
@@ -45,6 +47,7 @@ void FlightTask::Run(void * pvParams)
osm_ = new OsirisSM(OS_PRELAUNCH, true);
+
while (1) {
//Process commands in blocking mode
diff --git a/CM7/Components/Sensors/Inc/W25N01GVSFIG.hpp b/CM7/Components/Sensors/Inc/W25N01GVSFIG.hpp
new file mode 100644
index 0000000..6dd0559
--- /dev/null
+++ b/CM7/Components/Sensors/Inc/W25N01GVSFIG.hpp
@@ -0,0 +1,104 @@
+/**
+ * @file W25N01GVSFIG.hpp
+ * @brief Header file for the W25N01 Flash chip on the SAC 2024 payload
+ * @author Kail Olson
+ * @date May 18th, 2024
+*/
+
+// Opcodes
+#define OP_W25N01_DEVICE_RESET 0xFFU
+#define OP_W25N01_JEDEC_ID 0x9FU
+#define OP_W25N01_READ_SR 0x0FU
+#define OP_W25N01_WRITE_SR 0x1FU
+#define OP_W25N01_WRITE_EN 0x06U
+#define OP_W25N01_WRITE_DISABLE 0x04U
+#define OP_W25N01_BBM_SWAP_BLOCKS 0xA1U
+#define OP_W25N01_READ_BBM_LUT 0xA5U
+#define OP_W25N01_ECC_FAIL_PG_ADDR 0xA9U
+#define OP_W25N01_BLOCK_ERASE 0xD8U
+#define OP_W25N01_LOAD_PGM_DATA 0x02U
+#define OP_W25N01_RAND_LOAD_PGM_DATA 0x84U
+#define OP_W25N01_Q_LOAD_PGM_DATA 0x32U
+#define OP_W25N01_Q_RAND_LOAD_PGM_DATA 0x34U
+#define OP_W25N01_PGM_EXECUTE 0x10U
+#define OP_W25N01_PG_DATA_READ 0x13U
+#define OP_W25N01_READ 0x03U
+#define OP_W25N01_FAST_READ 0x0BU
+#define OP_W25N01_FAST_READ_4B_ADDR 0x0CU
+#define OP_W25N01_DO_FAST_READ 0x3BU
+#define OP_W25N01_DO_FAST_READ_4B_ADDR 0x3CU
+#define OP_W25N01_QO_FAST_READ 0x6BU
+#define OP_W25N01_QO_FAST_READ_4B_ADDR 0x6CU
+#define OP_W25N01_DIO_FAST_READ 0xBBU
+#define OP_W25N01_DIO_FAST_READ_4B_ADDR 0xBCU
+#define OP_W25N01_QIO_FAST_READ 0xEBU
+#define OP_W25N01_QIO_FAST_READ_4B_ADDR 0xECU
+
+// Special addresses
+#define ADDR_W25N01_SR1 0xA0U
+#define ADDR_W25N01_SR2 0xB0U
+#define ADDR_W25N01_SR3 0xC0U
+
+// Special keys
+#define KEY_W25N01_MFG_ID 0xEFU
+#define KEY_W25N01_DEVICE_ID_U 0xAAU
+#define KEY_W25N01_DEVICE_ID_L 0x21U
+
+// Bitmasks
+#define MASK_W25N01_SR2_RESERVED_BITS 0x07U
+#define MASK_W25N01_SR3_P_FAIL 0x08U
+#define MASK_W25N01_SR3_E_FAIL 0x04U
+#define MASK_W25N01_SR3_WEL 0x02U
+#define MASK_W25N01_SR3_BUSY 0x01U
+
+// Special numbers
+#define INT_W25N01_COLS_PER_PAGE 2112U
+#define INT_W25N01_PAGES_PER_BLOCK 64U
+#define INT_W25N01_NUM_BLOCKS 1024U
+
+// For SPI driver
+#define SPI_W25N01_TIMEOUT 1000U
+
+#include
+#include
+#include "main.h"
+
+// Enum definition
+enum e_flash_status {
+ FLASH_OK,
+ FLASH_NOT_OK,
+};
+typedef enum e_flash_status e_flash_status;
+
+class W25N01GVSFIG {
+ private:
+ // Member variables
+ GPIO_TypeDef* m_GPIOx;
+ uint16_t m_GPIO_Pin;
+ QSPI_HandleTypeDef* m_HSPIPtr;
+ uint16_t m_pageAddress;
+ uint16_t m_columnAddress;
+
+ // Private member functions
+ inline void m_updatePageAndColumnAddress(uint32_t address);
+ void m_pageDataRead();
+ e_flash_status m_writeEnable();
+ e_flash_status m_writeStatReg(uint8_t SRNumber, uint8_t val);
+ e_flash_status m_executeProgram();
+ uint8_t m_checkSR(uint8_t SRNumber);
+ //bool m_QSPIWrite(uint8_t* data, uint16_t size);
+
+ public:
+ // Constructor and destructor
+ W25N01GVSFIG(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, QSPI_HandleTypeDef* HSPIPtr, uint16_t pageAddress, uint16_t columnAddress);
+ ~W25N01GVSFIG();
+
+ // Public member functions
+ e_flash_status init();
+ e_flash_status write(uint8_t* data, uint32_t address, uint32_t size);
+ e_flash_status read(uint8_t* data, uint32_t address, uint32_t size);
+ e_flash_status eraseChip();
+ e_flash_status eraseSmallestSection(uint32_t address);
+ uint32_t getSmallestErasableSectionSize();
+ uint8_t isBusy();
+};
diff --git a/CM7/Components/Sensors/W25N01GVSFIG.cpp b/CM7/Components/Sensors/W25N01GVSFIG.cpp
new file mode 100644
index 0000000..d4546c6
--- /dev/null
+++ b/CM7/Components/Sensors/W25N01GVSFIG.cpp
@@ -0,0 +1,547 @@
+/**
+ * @file W25N01GVSFIG.cpp
+ * @brief Contains code for the W25N01 Flash chip on the SAC 2024 payload
+ * @author Kail Olson
+ * @date May 18th, 2024
+*/
+
+#include "Inc//W25N01GVSFIG.hpp"
+#include "SystemDefines.hpp"
+#include
+
+// Constructor
+W25N01GVSFIG::W25N01GVSFIG(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, QSPI_HandleTypeDef* HSPIPtr, uint16_t pageAddress, uint16_t columnAddress)
+ : m_GPIOx { GPIOx },
+ m_GPIO_Pin { GPIO_Pin },
+ m_HSPIPtr { HSPIPtr },
+ m_pageAddress { pageAddress },
+ m_columnAddress { columnAddress }
+ { /*No other function*/ }
+
+
+ // Destructor
+ W25N01GVSFIG::~W25N01GVSFIG()
+
+ { /*No other function*/
+
+ }
+
+
+// Private member functions
+inline void W25N01GVSFIG::m_updatePageAndColumnAddress(uint32_t address) {
+ m_pageAddress = uint16_t (address / INT_W25N01_COLS_PER_PAGE);
+ m_columnAddress = uint16_t (address % INT_W25N01_COLS_PER_PAGE);
+}
+
+void W25N01GVSFIG::m_pageDataRead() {
+ // Create message
+ uint8_t messagePtr[4];
+ messagePtr[0] = OP_W25N01_PG_DATA_READ;
+ messagePtr[1] = 0x00U; // Garbage
+ messagePtr[2] = (uint8_t) ((m_pageAddress & 0xFF00) >> 8U);
+ messagePtr[3] = (uint8_t) (m_pageAddress & 0x00FF);
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = 0x00;
+ cmd.AddressMode = QSPI_ADDRESS_NONE;
+ cmd.AddressSize = 0;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = 0;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 0;
+ cmd.Instruction = OP_W25N01_PG_DATA_READ;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=3;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ // Transmit "Page Data Read" instruction
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ }
+ if(HAL_QSPI_Transmit(m_HSPIPtr, &messagePtr[1], SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not transmit data\n");
+ }
+
+
+ // Wait 1ms (minimum 5us) required before page is fully read
+ HAL_Delay(1);
+
+ return;
+}
+
+e_flash_status W25N01GVSFIG::m_writeEnable() {
+ e_flash_status flashStatus = FLASH_OK;
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = 0x00;
+ cmd.AddressMode = QSPI_ADDRESS_NONE;
+ cmd.AddressSize = QSPI_ADDRESS_32_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.DataMode = QSPI_DATA_NONE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 0;
+ cmd.Instruction = OP_W25N01_WRITE_EN;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=0;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+ // Return a NOT_OK enum if the write-enable latch was not set
+ if (!(this->m_checkSR(3) & MASK_W25N01_SR3_WEL)) flashStatus = FLASH_NOT_OK;
+
+ return flashStatus;
+}
+
+e_flash_status W25N01GVSFIG::m_writeStatReg(uint8_t SRNumber, uint8_t val) {
+ e_flash_status flashStatus = FLASH_OK;
+
+ static QSPI_CommandTypeDef cmd;
+
+ if(SRNumber == 1) {
+ cmd.Address = ADDR_W25N01_SR1;
+ } else if (SRNumber == 2) {
+ cmd.Address = ADDR_W25N01_SR2;
+ } else {
+ cmd.Address = ADDR_W25N01_SR3;
+ }
+
+ cmd.AddressMode = QSPI_ADDRESS_1_LINE;
+ cmd.AddressSize = QSPI_ADDRESS_8_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 0;
+ cmd.Instruction = OP_W25N01_WRITE_SR;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=1;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+ if(HAL_QSPI_Transmit(m_HSPIPtr, &val, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not transmit data\n");
+ return FLASH_NOT_OK;
+ }
+
+ // Return a NOT_OK enum if the write-enable latch was not set
+ if (!(this->m_checkSR(3) & MASK_W25N01_SR3_WEL)) flashStatus = FLASH_NOT_OK;
+
+ return flashStatus;
+}
+
+e_flash_status W25N01GVSFIG::m_executeProgram() {
+ e_flash_status flashStatus = FLASH_OK;
+
+ // Create message
+ uint8_t messagePtr[4];
+ messagePtr[0] = OP_W25N01_PGM_EXECUTE;
+ messagePtr[1] = 0x00U; // Garbage
+ messagePtr[2] = (uint8_t) ((m_pageAddress & 0xFF00) >> 8U);
+ messagePtr[3] = (uint8_t) (m_pageAddress & 0x00FF);
+
+ // Transmit "Program Execute" instruction
+ //HAL_SPI_Transmit(m_HSPIPtr, messagePtr, 4, SPI_W25N01_TIMEOUT);
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = 0x00;
+ cmd.AddressMode = QSPI_ADDRESS_NONE;
+ cmd.AddressSize = QSPI_ADDRESS_32_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 0;
+ cmd.Instruction = OP_W25N01_PGM_EXECUTE;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=3;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+ if(HAL_QSPI_Transmit(m_HSPIPtr, messagePtr+1, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+ // Wait 1ms (minimum 10us as per datasheet)
+ HAL_Delay(1);
+
+ // Return a NOT_OK enum if the program-fail bit was set
+ if (this->m_checkSR(3) & MASK_W25N01_SR3_P_FAIL) flashStatus = FLASH_NOT_OK;
+
+ return flashStatus;
+}
+
+uint8_t W25N01GVSFIG::m_checkSR(uint8_t SRNumber) {
+ uint8_t SRValue = 0xaa;
+
+ // Prepare message
+ uint8_t* messagePtr = new uint8_t[2];
+ messagePtr[0] = OP_W25N01_READ_SR;
+ if (SRNumber == 1) {
+ messagePtr[1] = ADDR_W25N01_SR1;
+ } else if (SRNumber == 2) {
+ messagePtr[1] = ADDR_W25N01_SR2;
+ } else {
+ messagePtr[1] = ADDR_W25N01_SR3;
+ }
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = messagePtr[1];
+ cmd.AddressMode = QSPI_ADDRESS_1_LINE;
+ cmd.AddressSize = QSPI_ADDRESS_8_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = 0;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 0;
+ cmd.Instruction = OP_W25N01_READ_SR;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=1;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+// if(HAL_QSPI_Transmit(m_HSPIPtr, &messagePtr[1],SPI_W25N01_TIMEOUT) != HAL_OK) {
+// SOAR_PRINT("Could not set qspi transmit\n");
+// return FLASH_NOT_OK;
+// }
+
+
+
+ // Transmit "Read SR" instruction
+ //HAL_SPI_Transmit(m_HSPIPtr, messagePtr, 2, SPI_W25N01_TIMEOUT);
+
+ // Receive SR value
+ //HAL_SPI_Receive(m_HSPIPtr, (uint8_t*) &SRValue, 1, SPI_W25N01_TIMEOUT);
+
+ HAL_QSPI_Receive(m_HSPIPtr, &SRValue, SPI_W25N01_TIMEOUT);
+
+ return SRValue;
+}
+
+
+// Public member functions
+e_flash_status W25N01GVSFIG::init() {
+ e_flash_status flashStatus = FLASH_OK;
+
+ if(m_HSPIPtr ==nullptr) {
+ m_HSPIPtr = new QSPI_HandleTypeDef;
+
+ m_HSPIPtr->Init.ClockPrescaler = 255;
+ m_HSPIPtr->Init.FifoThreshold = 4;
+ m_HSPIPtr->Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
+ m_HSPIPtr->Init.FlashSize = 26;
+ m_HSPIPtr->Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_8_CYCLE;
+ m_HSPIPtr->Init.ClockMode = QSPI_CLOCK_MODE_0;
+ m_HSPIPtr->Init.FlashID = QSPI_FLASH_ID_1;
+ m_HSPIPtr->Init.DualFlash = QSPI_DUALFLASH_DISABLE;
+
+ m_HSPIPtr->Instance = QUADSPI;
+
+ if(HAL_QSPI_Init(m_HSPIPtr) != HAL_OK) {
+ SOAR_PRINT("Could not init QSPI\n");
+ return FLASH_NOT_OK;
+ }
+ }
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = 0x00;
+ cmd.AddressMode = QSPI_ADDRESS_NONE;
+ cmd.AddressSize = QSPI_ADDRESS_32_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 8;
+ cmd.Instruction = OP_W25N01_JEDEC_ID;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=3; // JEDEC ID plus two device IDs
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+ // Create receive buffer
+ uint8_t receivedMessagePtr[3];
+ memset(receivedMessagePtr,0x00,3);
+
+ //Receive IDs
+ HAL_StatusTypeDef result = HAL_QSPI_Receive(m_HSPIPtr, receivedMessagePtr, SPI_W25N01_TIMEOUT);
+
+ if(result != HAL_OK) {
+ SOAR_PRINT("Could not receive JEDEC bytes\n");
+ return FLASH_NOT_OK;
+ } else {
+// SOAR_PRINT("Received: %x %x %x\n",receivedMessagePtr[0],receivedMessagePtr[1],receivedMessagePtr[2]);
+ }
+
+ // Check for correct received message
+ if (receivedMessagePtr[0] != KEY_W25N01_MFG_ID) flashStatus = FLASH_NOT_OK;
+ if (receivedMessagePtr[1] != KEY_W25N01_DEVICE_ID_U) flashStatus = FLASH_NOT_OK;
+ if (receivedMessagePtr[2] != KEY_W25N01_DEVICE_ID_L) flashStatus = FLASH_NOT_OK;
+
+ // Enable writing
+ this->m_writeEnable();
+
+ this->m_writeStatReg(1, 0x00); // SR-1 settings for Osiris application
+
+
+ // Enable writing
+ this->m_writeEnable();
+
+ this->m_writeStatReg(2, 0x08);// SR-2 settings for Osiris application (BUF=1)
+
+
+ // Check that the SR registers were correctly configured
+ if (this->m_checkSR(1) != 0x00U) flashStatus = FLASH_NOT_OK;
+ if ((this->m_checkSR(2) & ~MASK_W25N01_SR2_RESERVED_BITS) != 0x08U) flashStatus = FLASH_NOT_OK;
+
+ // Return
+ return flashStatus;
+}
+
+e_flash_status W25N01GVSFIG::write(uint8_t* data, uint32_t address, uint32_t size) {
+ e_flash_status flashStatus = FLASH_OK;
+
+
+ // Convert given address to page and column
+ this->m_updatePageAndColumnAddress(address);
+
+
+ // Writing data
+ uint32_t remainingData = size;
+ uint32_t sizeToWrite = 0;
+ uint32_t dataOffset = 0;
+
+ if (remainingData > (INT_W25N01_COLS_PER_PAGE - m_columnAddress)) sizeToWrite = INT_W25N01_COLS_PER_PAGE - m_columnAddress;
+ else sizeToWrite = remainingData;
+
+ while (remainingData > 0) {
+ // Read page data into buffer
+ this->m_pageDataRead();
+
+ // Enable writing
+ this->m_writeEnable();
+
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = m_columnAddress;
+ cmd.AddressMode = QSPI_ADDRESS_1_LINE;
+ cmd.AddressSize = QSPI_ADDRESS_16_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 0;
+ cmd.Instruction = OP_W25N01_RAND_LOAD_PGM_DATA;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=sizeToWrite;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ // Transmit "Random Load Program Data" instruction
+ //HAL_SPI_Transmit(m_HSPIPtr, messagePtr, 3, SPI_W25N01_TIMEOUT);
+
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+ if(HAL_QSPI_Transmit(m_HSPIPtr, &data[dataOffset], SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+ // Enable writing
+ this->m_writeEnable();
+
+ // Execute program
+ if (this->m_executeProgram() != FLASH_OK) return FLASH_NOT_OK; // Evil return but who cares
+
+ // Subtract written data from size
+ remainingData -= sizeToWrite;
+
+ // Set offset
+ dataOffset += sizeToWrite;
+
+ // Determine next "sizeToWrite"
+ if (remainingData > INT_W25N01_COLS_PER_PAGE) sizeToWrite = INT_W25N01_COLS_PER_PAGE;
+ else sizeToWrite = remainingData;
+
+
+ // Increment page address and set column address to 0
+ m_pageAddress++;
+ m_columnAddress = 0;
+ }
+
+ return flashStatus;
+}
+
+e_flash_status W25N01GVSFIG::eraseChip() {
+ // Loop through all blocks and erase
+ for (uint32_t i = 0; i < INT_W25N01_NUM_BLOCKS; i++) {
+ if (this->eraseSmallestSection(i * INT_W25N01_PAGES_PER_BLOCK * INT_W25N01_COLS_PER_PAGE) != FLASH_OK) return FLASH_NOT_OK;
+ }
+
+ return FLASH_OK;
+}
+
+e_flash_status W25N01GVSFIG::eraseSmallestSection(uint32_t address) {
+ e_flash_status flashStatus = FLASH_OK;
+
+ // Update page and column address
+ this->m_updatePageAndColumnAddress(address);
+
+ // Prepare message
+ uint8_t messagePtr[4];
+ messagePtr[0] = OP_W25N01_BLOCK_ERASE;
+ messagePtr[1] = 0x00U; // Garbage
+ messagePtr[2] = (uint8_t) ((m_pageAddress & 0xFF00U) >> 8U);
+ messagePtr[3] = (uint8_t) (m_pageAddress & 0x00FFU);
+
+ // Enable writing
+ this->m_writeEnable();
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = 0x00;
+ cmd.AddressMode = QSPI_ADDRESS_NONE;
+ cmd.AddressSize = QSPI_ADDRESS_32_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 0;
+ cmd.Instruction = OP_W25N01_BLOCK_ERASE;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=3;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+ // Transmit "Erase Block" instruction
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+ if(HAL_QSPI_Transmit(m_HSPIPtr, messagePtr+1, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+ // Wait for 1ms (minimum 500us as per datasheet)
+ HAL_Delay(1);
+
+ // Check E-FAIL bit in SR3
+ if (this->m_checkSR(3) & MASK_W25N01_SR3_E_FAIL) {
+ flashStatus = FLASH_NOT_OK;
+ SOAR_PRINT("failed erase\n");
+ }
+
+ return flashStatus;
+}
+
+uint32_t W25N01GVSFIG::getSmallestErasableSectionSize() {
+ return INT_W25N01_COLS_PER_PAGE * INT_W25N01_PAGES_PER_BLOCK;
+}
+
+uint8_t W25N01GVSFIG::isBusy() {
+ // Return value of busy bit in SR3
+ return (this->m_checkSR(3) & MASK_W25N01_SR3_BUSY);
+}
+
+e_flash_status W25N01GVSFIG::read(uint8_t *data, uint32_t address,
+ uint32_t size) {
+ e_flash_status flashStatus = FLASH_OK;
+
+ // Convert given address to page and column
+ this->m_updatePageAndColumnAddress(address);
+
+ // Reading data
+ uint32_t remainingData = size;
+ uint32_t sizeToRead = 0;
+ uint32_t dataOffset = 0;
+
+ if (remainingData > (INT_W25N01_COLS_PER_PAGE - m_columnAddress)) sizeToRead = INT_W25N01_COLS_PER_PAGE - m_columnAddress;
+ else sizeToRead = remainingData;
+
+ while (remainingData > 0) {
+ // Read page data into buffer
+ this->m_pageDataRead();
+
+ static QSPI_CommandTypeDef cmd;
+ cmd.Address = m_columnAddress;
+ cmd.AddressMode = QSPI_ADDRESS_1_LINE;
+ cmd.AddressSize = QSPI_ADDRESS_16_BITS;
+ cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.AlternateBytes = 0;
+ cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
+ cmd.DataMode = QSPI_DATA_1_LINE;
+ cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
+ cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
+ cmd.DummyCycles = 8;
+ cmd.Instruction = OP_W25N01_READ;
+ cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
+ cmd.NbData=sizeToRead;
+ cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
+
+ // Transmit "Random Load Program Data" instruction
+ //HAL_SPI_Transmit(m_HSPIPtr, messagePtr, 3, SPI_W25N01_TIMEOUT);
+
+ if(HAL_QSPI_Command(m_HSPIPtr, &cmd, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+
+ if(HAL_QSPI_Receive(m_HSPIPtr, data+dataOffset, SPI_W25N01_TIMEOUT) != HAL_OK) {
+ SOAR_PRINT("Could not set qspi command\n");
+ return FLASH_NOT_OK;
+ }
+
+ // Subtract written data from size
+ remainingData -= sizeToRead;
+
+
+ // Set offset
+ dataOffset += sizeToRead;
+
+ // Determine next "sizeToWrite"
+ if (remainingData > INT_W25N01_COLS_PER_PAGE) sizeToRead = INT_W25N01_COLS_PER_PAGE;
+ else sizeToRead = remainingData;
+
+ // Increment page address and set column address to 0
+ m_pageAddress++;
+ m_columnAddress = 0;
+ }
+
+ return flashStatus;
+
+}
diff --git a/CM7/Components/SystemDefines.hpp b/CM7/Components/SystemDefines.hpp
index d5b705d..f5557fc 100644
--- a/CM7/Components/SystemDefines.hpp
+++ b/CM7/Components/SystemDefines.hpp
@@ -55,6 +55,11 @@ constexpr uint8_t FLIGHT_TASK_RTOS_PRIORITY = 2; // Priority of the d
constexpr uint8_t FLIGHT_TASK_QUEUE_DEPTH_OBJS = 10; // Size of the debug task queue
constexpr uint16_t FLIGHT_TASK_STACK_DEPTH_WORDS = 512; // Size of the debug task stack
+//Flash Task
+constexpr uint8_t FLASH_TASK_RTOS_PRIORITY = 2; // Priority of the FLASH task
+constexpr uint8_t FLASH_TASK_QUEUE_DEPTH_OBJS = 10; // Size of the FLASH task queue
+constexpr uint16_t FLASH_TASK_STACK_DEPTH_WORDS = 512; // Size of the FLASH task stack
+
#endif // CUBE_MAIN_SYSTEM_DEFINES_H
diff --git a/CM7/Components/main_system.cpp b/CM7/Components/main_system.cpp
index b3faaf8..d11e26b 100644
--- a/CM7/Components/main_system.cpp
+++ b/CM7/Components/main_system.cpp
@@ -13,6 +13,7 @@
#include "CubeTask.hpp"
#include "DebugTask.hpp"
#include "FlightTask.hpp"
+#include "FlashTask//Inc//FlashTask.hpp"
/* Drivers ------------------------------------------------------------------*/
namespace Driver {
@@ -28,6 +29,7 @@ void run_main() {
CubeTask::Inst().InitTask();
DebugTask::Inst().InitTask();
FlightTask::Inst().InitTask();
+ FlashTask::Inst().InitTask();
// Print System Boot Info : Warning, don't queue more than 10 prints before scheduler starts
SOAR_PRINT("\n-- SOAR SYSTEM --\n");
diff --git a/CM7/Core/Src/main.c b/CM7/Core/Src/main.c
index 9d097c6..c5fb106 100644
--- a/CM7/Core/Src/main.c
+++ b/CM7/Core/Src/main.c
@@ -415,8 +415,8 @@ static void MX_QUADSPI_Init(void)
hqspi.Init.ClockPrescaler = 255;
hqspi.Init.FifoThreshold = 1;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
- hqspi.Init.FlashSize = 1;
- hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
+ hqspi.Init.FlashSize = 26;
+ hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
diff --git a/Payload.ioc b/Payload.ioc
index ecf18db..117500f 100644
--- a/Payload.ioc
+++ b/Payload.ioc
@@ -425,6 +425,9 @@ ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=true
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false-CortexM7,2-MX_GPIO_Init-GPIO-false-HAL-true-CortexM7,3-MX_QUADSPI_Init-QUADSPI-false-HAL-true-CortexM7,4-MX_SPI1_Init-SPI1-false-HAL-true-CortexM7,5-MX_I2C1_Init-I2C1-false-HAL-true-CortexM7,6-MX_I2C2_Init-I2C2-false-HAL-true-CortexM7,7-MX_SPI2_Init-SPI2-false-HAL-true-CortexM7,8-MX_SPI5_Init-SPI5-false-HAL-true-CortexM7,9-MX_USART2_UART_Init-USART2-false-HAL-true-CortexM7,10-MX_UART4_Init-UART4-false-HAL-true-CortexM7,11-MX_UART5_Init-UART5-false-HAL-true-CortexM7,12-MX_USART6_UART_Init-USART6-false-LL-true-CortexM7,13-MX_SPI6_Init-SPI6-false-HAL-true-CortexM7,14-MX_FREERTOS_Init-FREERTOS_M7-false-HAL-false-CortexM7,15-MX_CRC_Init-CRC-false-HAL-true-CortexM7,1-MX_QUADSPI_Init-QUADSPI-true-HAL-false-CortexM4,2-MX_CRC_Init-CRC-true-HAL-false-CortexM4,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true-CortexM7,0-MX_CORTEX_M4_Init-CORTEX_M4-false-HAL-true-CortexM4
+QUADSPI.ChipSelectHighTime=QSPI_CS_HIGH_TIME_2_CYCLE
+QUADSPI.FlashSize=26
+QUADSPI.IPParameters=FlashSize,ChipSelectHighTime
RCC.ADCFreq_Value=129000000
RCC.AHB12Freq_Value=64000000
RCC.AHB4Freq_Value=64000000