diff --git a/.cproject b/.cproject
index e498386..eed3eb1 100644
--- a/.cproject
+++ b/.cproject
@@ -80,7 +80,7 @@
-
+
diff --git a/Core/Src/main.c b/Core/Src/main.c
index 824509b..6f5df16 100644
--- a/Core/Src/main.c
+++ b/Core/Src/main.c
@@ -64,7 +64,7 @@ UART_HandleTypeDef huart3;
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
- .stack_size = 128 * 4,
+ .stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* USER CODE BEGIN PV */
diff --git a/KPI_Rover/Database/scripts/updateMetadataEnum.py b/KPI_Rover/Database/scripts/updateMetadataEnum.py
new file mode 100755
index 0000000..1f8358c
--- /dev/null
+++ b/KPI_Rover/Database/scripts/updateMetadataEnum.py
@@ -0,0 +1,218 @@
+#!/bin/python3
+
+from pathlib import Path
+
+lines = open(Path(__file__).parent / "../../KPIRover.c").read().split('\n')
+
+decoder_active = False
+
+params = []
+
+def log(text):
+ types = {"ERROR": True,
+ "WARN": True,
+ "INFO": True,
+ "DEBUG": True}
+
+ if len(text) == 0 or text[0] != "[":
+ print(text)
+ return
+
+ msg_type = text.split("]", 1)[0][1:]
+
+ if msg_type not in types:
+ print(f'[INTERNAL ERROR] msg_type "{msg_type}" does not exist')
+ exit(2)
+
+ if types[msg_type]:
+ print(text)
+
+def process_offset(v):
+ global params
+
+ offset = int(v)
+
+ if offset != 0:
+ log("[WARN] Offsets are overridden by the database and should be kept as 0 in the metadata")
+ return False
+
+ params[-1]["offset"] = int(v)
+
+ return True
+
+def process_type(v):
+ global params
+
+ if v not in ["INT8", "UINT8", "INT16", "UINT16", "INT32", "UINT32", "FLOAT"]:
+ log(f'[ERROR] Invalid type: "{v}"')
+ return False
+
+ params[-1]["type"] = v
+
+ return True
+
+def process_persistent(v):
+ global params
+
+ if v not in ["true", "false"]:
+ log(f"[ERROR] Invalid boolean value: {v}")
+ return False
+
+ params[-1]["persistent"] = True if v == "true" else False
+
+ if len(params) > 1:
+ if params[-1]["persistent"] and not params[-2]["persistent"]:
+ log("[ERROR] Persistent parameters must come before the non-persistent ones")
+ return False
+
+ return True
+
+def process_defaultValue(v):
+ if params[-1]["type"] == "FLOAT":
+ try:
+ params[-1]["defaultValue"] = float(v.strip("fF"))
+ except:
+ log(f"[ERROR] Invalid floating-point value: {v}")
+ return False
+ else:
+ try:
+ params[-1]["defaultValue"] = int(v)
+ except:
+ log(f"[ERROR] Invalid integer value: {v}")
+ return False
+
+ ''' unfinished and broken, no reason to fix right now
+ elif params[-1]["type"] == "INT32":
+ try:
+ params[-1]["defaultValue"] = int(v)
+
+ if params[-1]["defaultValue"] > 0x7fffffff:
+ print(f"[WARN] defaultValue overflows it's data type: {v} ({hex(params[-1]['defaultValue'])}) > 0x7fffffff")
+ elif params[-1]["defaultValue"] < -0x80000000:
+ print(f"[WARN] defaultValue overflows it's data type: {v} ({hex(params[-1]['defaultValue'] % 0x100000000)}) < -0x80000000")
+ except:
+ log(f"[ERROR] Invalid floating-point value: {v}")
+ return False
+ '''
+
+ return True
+
+for l in lines:
+ log("> " + l)
+ if 'struct ulDatabase_ParamMetadata ulDatabase_params[]' in l:
+ log("[DEBUG] Starting to read metadata")
+ decoder_active = True
+ continue
+
+ if ('}' in l) and ('{' not in l):
+ log("[DEBUG] End of metadata")
+ break
+
+ if not decoder_active:
+ continue
+
+ metadata = [i.strip() for i in l.split('}')[0].split('{')[1].split(',')]
+ name = l.split("//")[1].strip().strip(",")
+
+ params.append({'name': name})
+
+ metadata_item_pool = [
+ {
+ "name": "offset",
+ "func": process_offset
+ },
+ {
+ "name": "type",
+ "func": process_type
+ },
+ {
+ "name": "persistent",
+ "func": process_persistent
+ },
+ {
+ "name": "defaultValue",
+ "func": process_defaultValue
+ }
+ ]
+
+ for i, v in enumerate(metadata_item_pool):
+ v["position"] = i
+
+ for c, t in enumerate(metadata):
+ index = -1
+ result = False
+ value = ""
+
+ if '=' in t:
+ item, value = [i.strip() for i in t.split("=")]
+
+ try:
+ index = [i['name'] for i in metadata_item_pool].index(item)
+ except ValueError:
+ log(f'[ERROR] {i}: "{item}": no such metadata key')
+ else:
+ index = 0
+ value = t
+
+ if index != -1:
+ result = metadata_item_pool.pop(index)['func'](value)
+
+ if not result:
+ unwrapped_l = l.replace("\t", " " * 8)
+ log(f"[ERROR] > {unwrapped_l}")
+
+ arrow_offset = unwrapped_l.index("{") + 1
+ ll = unwrapped_l[arrow_offset:]
+
+ for _ in range(c):
+ arrow_offset += ll.index(",") + 1
+ ll = unwrapped_l[arrow_offset:]
+
+ while unwrapped_l[arrow_offset] == ' ':
+ arrow_offset += 1
+
+ underline_len = len(t) - 1
+
+ while unwrapped_l[arrow_offset + underline_len] == ' ':
+ underline_len -= 1
+
+ log("[ERROR] > " + " " * arrow_offset + "^" + "~" * underline_len)
+ log("[ERROR] Error detected, not continuing")
+ exit(1)
+
+log("[INFO] Result:")
+
+for i in params:
+ print("[INFO] " + i["name"])
+
+
+log("[INFO] Patching ulDatabase.h...")
+
+ulDatabase_h = open(Path(__file__).parent / "../ulDatabase.h").read().split('\n')
+while ulDatabase_h[-1] == "":
+ del ulDatabase_h[-1]
+
+enum_since = [i.startswith("enum ulDatabase_ParamId {") for i in ulDatabase_h].index(True) + 1
+enum_len = [i.startswith("};") for i in ulDatabase_h[enum_since:]].index(True)
+
+log("[DEBUG]: Removing old enum data:")
+for i, l in enumerate(ulDatabase_h):
+ if enum_since <= i < enum_since + enum_len:
+ log("[DEBUG] -" + l)
+ else:
+ log("[DEBUG] " + l)
+
+del ulDatabase_h[enum_since:enum_since+enum_len]
+
+ulDatabase_h.insert(enum_since, "\tPARAM_COUNT")
+for i in params[::-1]:
+ ulDatabase_h.insert(enum_since, f"\t{i['name']},")
+
+log("[DEBUG] Writing new ulDatabase.h...")
+
+with open(Path(__file__).parent / "../ulDatabase.h", "w") as f:
+ for i in ulDatabase_h:
+ print(i + '\n', end = '')
+ f.write(i + '\n')
+
+log("[DEBUG] ulDatabase.h closed")
diff --git a/KPI_Rover/Database/ulDatabase.c b/KPI_Rover/Database/ulDatabase.c
index 80e69c1..fe664a4 100644
--- a/KPI_Rover/Database/ulDatabase.c
+++ b/KPI_Rover/Database/ulDatabase.c
@@ -403,6 +403,18 @@ bool ulDatabase_reset(uint16_t id)
return true;
}
+uint8_t *ulDatabase_freeze(void)
+{
+ DB_LOCK();
+
+ return db.dataArray;
+}
+
+void ulDatabase_unfreeze(void)
+{
+ DB_FREE();
+}
+
struct ulDatabase_ParamMetadata *ulDatabase_getMetadata(uint16_t id)
{
if (!ulDatabase_validateId(id))
diff --git a/KPI_Rover/Database/ulDatabase.h b/KPI_Rover/Database/ulDatabase.h
index 724e9ec..ed44684 100644
--- a/KPI_Rover/Database/ulDatabase.h
+++ b/KPI_Rover/Database/ulDatabase.h
@@ -17,12 +17,12 @@ enum ulDatabase_ParamType {
};
enum ulDatabase_ParamId {
+ ENCODER_CONTROL_PERIOD_MS,
+ ENCODER_TICKS_PER_REVOLUTION,
MOTOR_FL_RPM,
MOTOR_FR_RPM,
MOTOR_RL_RPM,
MOTOR_RR_RPM,
- ENCODER_CONTROL_PERIOD_MS,
- ENCODER_TICKS_PER_REVOLUTION,
PARAM_COUNT
};
@@ -56,5 +56,7 @@ bool ulDatabase_getInt32(uint16_t id, int32_t *value);
bool ulDatabase_setFloat(uint16_t id, float value);
bool ulDatabase_getFloat(uint16_t id, float *value);
bool ulDatabase_reset(uint16_t id);
+uint8_t *ulDatabase_freeze(void);
+void ulDatabase_unfreeze(void);
struct ulDatabase_ParamMetadata *ulDatabase_getMetadata(uint16_t id);
bool ulDatabase_validateId(uint16_t id);
diff --git a/KPI_Rover/Database/ulStorage.c b/KPI_Rover/Database/ulStorage.c
new file mode 100644
index 0000000..a787661
--- /dev/null
+++ b/KPI_Rover/Database/ulStorage.c
@@ -0,0 +1,413 @@
+#include
+
+#include "ulDatabase.h"
+#include "ulStorage.h"
+
+#include "ulog.h"
+
+extern char _sccmram[]; // value exported by linker script
+
+#define PAGE_START _sccmram // CCM RAM (to protect actual flash memory)
+#define PAGE_SIZE 0x4000 // model 16KiB FLASH memory
+#define MARKER_SAVE_BEGIN 0xAA
+
+static uint32_t persistent_db_field_size;
+
+
+// flash emulation section start
+static bool flash_unlocked;
+
+static void HAL_FLASH_Unlock(void)
+{
+ flash_unlocked = 1;
+}
+
+static void HAL_FLASH_Lock(void)
+{
+ flash_unlocked = 0;
+}
+
+enum Size {
+ FLASH_TYPEPROGRAM_BYTE
+};
+
+static void HAL_FLASH_Erase(void)
+{
+ if (!flash_unlocked) {
+ ULOG_WARNING("Erase command on locked flash");
+ return;
+ }
+
+ memset((void *) PAGE_START, 0xFF, PAGE_SIZE);
+}
+
+static void HAL_FLASH_Program(enum Size s, const void * const addr, const uint64_t value)
+{
+ (void) s;
+
+ if (!flash_unlocked) {
+ ULOG_WARNING("Write command on locked flash: %p <- 0x%02hhx", addr, (uint8_t) value);
+ return;
+ }
+
+ if ((addr >= (void *) (PAGE_START + PAGE_SIZE)) || (addr < (void *) PAGE_START)) {
+ ULOG_CRITICAL("OUT OF BOUNDS FLASH WRITE GENERATED");
+ ULOG_CRITICAL("TRIED TO WRITE VALUE 0x%02hhx TO ADDRESS %p", (uint8_t) value, addr);
+ return;
+ }
+
+ *(uint8_t *) addr = (uint8_t) value;
+}
+// flash emulation section finish
+
+static uint32_t crc32(const uint8_t * const data, const uint32_t data_size, const uint32_t remainder)
+{
+ register uint32_t reg_a = 0,
+ reg_b = 0;
+
+ static const uint32_t divisor = 0x04C11DB7;
+
+ uint32_t read_offset = 0;
+ uint32_t xor_ready = 0;
+
+ while ((data_size - read_offset) >= 4) {
+ reg_b = *(uint32_t *) &(data[read_offset]);
+ read_offset += 4;
+
+ for (unsigned int i = 0; i < 32; i++) {
+ // check if XOR can be done yet
+ xor_ready = reg_a & 0x80000000;
+
+ // shift in
+ reg_a <<= 1;
+
+ if (reg_b & 0x80000000)
+ reg_a |= 0x1;
+
+ reg_b <<= 1;
+
+ // XOR if ready
+ if (xor_ready)
+ reg_a ^= divisor;
+ }
+ }
+
+ if (data_size - read_offset != 0) {
+ // fill reg_b
+ for (unsigned int i = 0; i < (data_size - read_offset); i++) {
+ reg_b |= data[read_offset + i];
+ reg_b <<= 8;
+ }
+
+ reg_b <<= 8 * (3 - (data_size - read_offset));
+
+ // finish calculating main part
+ for (unsigned int i = 0; i < 8 * (data_size - read_offset); i++) {
+ // check if XOR can be done yet
+ xor_ready = reg_a & 0x80000000;
+
+ // shift in
+ reg_a <<= 1;
+
+ if (reg_b & 0x80000000)
+ reg_a |= 0x1;
+
+ reg_b <<= 1;
+
+ // XOR if ready
+ if (xor_ready)
+ reg_a ^= divisor;
+ }
+ }
+
+ // now finish with 32 bits of the remainder
+ reg_b = remainder;
+
+ // finish calculating main part
+ for (unsigned int i = 0; i < 32; i++) {
+ // check if XOR can be done yet
+ xor_ready = reg_a & 0x80000000;
+
+ // shift in
+ reg_a <<= 1;
+
+ if (reg_b & 0x80000000)
+ reg_a |= 0x1;
+
+ reg_b <<= 1;
+
+ // XOR if ready
+ if (xor_ready)
+ reg_a ^= divisor;
+ }
+
+ return reg_a;
+}
+
+static uint32_t crc32_calculate(const uint8_t * const data, const uint32_t data_size)
+{
+ return crc32(data, data_size, 0);
+}
+
+static uint32_t crc32_verify(const uint8_t * const data, const uint32_t data_size, const uint32_t remainder)
+{
+ return crc32(data, data_size, remainder);
+}
+
+static uint32_t ulStorage_get_save_size(void)
+{
+ uint32_t persistent_fields_len = 0;
+
+ for (uint16_t i = 0; i < PARAM_COUNT; i++) {
+ struct ulDatabase_ParamMetadata *p = ulDatabase_getMetadata(i);
+
+ if (p == NULL)
+ return false;
+
+ if (!p->persistent)
+ break;
+
+ switch (p->type) {
+ case INT8:
+ case UINT8:
+ persistent_fields_len += 1;
+ break;
+ case INT16:
+ case UINT16:
+ persistent_fields_len += 2;
+ break;
+ case INT32:
+ case UINT32:
+ case FLOAT:
+ persistent_fields_len += 4;
+ break;
+ }
+ }
+
+ return persistent_fields_len;
+}
+
+static bool ulStorage_verify_sector_header(void)
+{
+ uint32_t sector_header = *(uint32_t *) PAGE_START;
+
+ // verify the sector is erased
+ if (sector_header & 0x80) {
+ ULOG_WARNING("Sector is not formatted: ERASED flag is 1");
+ return false;
+ }
+
+ // verify no invalid flags are present
+ if ((sector_header & 0x2F) != 0x2F) {
+ ULOG_WARNING("Sector is not formatted: garbage flags found");
+ return false;
+ }
+
+ // verify save size matches current persistent field size
+ if ((sector_header >> 20) != persistent_db_field_size) {
+ ULOG_WARNING("Save size does not match with current size");
+ return false;
+ }
+
+ // verify hash is not there yet
+ if ((sector_header & 0x000FFF00) != 0x000FFF00) {
+ ULOG_WARNING("Garbage found in HASH header field");
+ return false;
+ }
+
+ return true;
+}
+
+static uint8_t *ulStorage_find_first_free_block(void)
+{
+ uint8_t *seek_head;
+
+ for (
+ seek_head = (uint8_t *) PAGE_START + 4;
+ (seek_head < (uint8_t *) (PAGE_START + PAGE_SIZE)) && (*seek_head != 0xFF);
+ seek_head += 1 + persistent_db_field_size + 4);
+
+ return seek_head;
+}
+
+static bool ulStorage_find_last_save(uint8_t **buffer)
+{
+ if (!ulStorage_verify_sector_header())
+ return false;
+
+ uint8_t *seek_head = ulStorage_find_first_free_block();
+
+ // finding last valid save data
+ uint32_t save_checksum = 0xFFFFFFFF;
+
+ while ((seek_head > (uint8_t *) PAGE_START + 4) && save_checksum) {
+ seek_head -= 1 + persistent_db_field_size + 4;
+ ULOG_DEBUG("Verifying save hash for %p", seek_head);
+
+ save_checksum = crc32_verify(seek_head + 1, persistent_db_field_size, *(uint32_t *)(seek_head + 1 + persistent_db_field_size));
+ ULOG_DEBUG("Result: 0x%08X", save_checksum);
+ }
+
+ // if none found, stop
+ if (save_checksum) {
+ ULOG_WARNING("No valid saves found");
+ return false;
+ }
+
+ // otherwise set the correct pointer
+ *buffer = seek_head + 1;
+
+ return true;
+}
+
+static bool ulStorage_find_new_save_block(uint8_t ** block)
+{
+ if (!ulStorage_verify_sector_header())
+ return false;
+
+ uint8_t *new_block = ulStorage_find_first_free_block();
+
+ if ((new_block + 1 + persistent_db_field_size + 4) >= (uint8_t *) (PAGE_START + PAGE_SIZE))
+ return false;
+
+ *block = new_block;
+
+ return true;
+}
+
+static void ulStorage_format_sector(void)
+{
+ uint32_t new_header = 0xFFFFFFFF;
+
+ // mark ERASED bit
+ new_header &= 0xFFFFFF7F;
+
+ // mark save size
+ new_header &= (persistent_db_field_size << 20) | (0x000FFFFF);
+
+ // --- ENTER UNSAFE ZONE ---
+
+ HAL_FLASH_Unlock();
+
+ HAL_FLASH_Erase();
+
+ // write header to flash
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (void *) (PAGE_START + 0), (new_header >> 0) & 0xFF);
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (void *) (PAGE_START + 1), (new_header >> 8) & 0xFF);
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (void *) (PAGE_START + 2), (new_header >> 16) & 0xFF);
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (void *) (PAGE_START + 3), (new_header >> 24) & 0xFF);
+
+ HAL_FLASH_Lock();
+
+ // --- LEAVE UNSAFE ZONE ---
+}
+
+static void ulStorage_write_save(uint8_t * const write_at, const uint8_t * const save_data)
+{
+ uint32_t checksum = crc32_calculate(save_data, persistent_db_field_size);
+
+ // --- ENTER UNSAFE ZONE ---
+
+ HAL_FLASH_Unlock();
+
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, write_at, MARKER_SAVE_BEGIN);
+
+ for (uint32_t i = 0; i < persistent_db_field_size; i++)
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, write_at + 1 + i, save_data[i]);
+
+ for (uint32_t i = 0; i < 4; i++)
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, write_at + 1 + persistent_db_field_size + i, (checksum >> (8 * i)) & 0xFF);
+
+ HAL_FLASH_Lock();
+
+ // --- LEAVE UNSAFE ZONE ---
+}
+
+bool ulStorage_init(void)
+{
+ persistent_db_field_size = ulStorage_get_save_size();
+
+ bool r = ulStorage_load();
+ if (r) {
+ ULOG_INFO("Loaded save from storage");
+ } else {
+ ULOG_INFO("Loading save from storage has failed");
+ }
+
+ return true;
+}
+
+bool ulStorage_load(void)
+{
+ if (!persistent_db_field_size)
+ return true;
+
+ uint8_t *buffer;
+
+ if (!ulStorage_find_last_save(&buffer))
+ return false;
+
+ uint8_t *db_data = ulDatabase_freeze();
+ memcpy(db_data, buffer, persistent_db_field_size);
+ ulDatabase_unfreeze();
+
+ return true;
+}
+
+bool ulStorage_save(void)
+{
+ if (!persistent_db_field_size)
+ return true;
+
+ // get a copy of persistent fields
+ uint8_t new_save_data[persistent_db_field_size];
+
+ uint8_t *db_data = ulDatabase_freeze();
+ memcpy(new_save_data, db_data, persistent_db_field_size);
+ ulDatabase_unfreeze();
+
+ // try to load last save
+ uint8_t *last_save_data;
+ if (ulStorage_find_last_save(&last_save_data))
+ if (!memcmp(new_save_data, last_save_data, persistent_db_field_size)) {
+ ULOG_INFO("Old save is valid and identical to a new one; not writing another block");
+ return true;
+ }
+
+ // write a new save
+ uint8_t *new_save_address;
+
+ if (!ulStorage_find_new_save_block(&new_save_address)) {
+ // the storage is not formatted or is full - either way needs formatting
+ ULOG_INFO("Sector is full or not formatted; formatting the sector and writing new save");
+ ulStorage_format_sector();
+ ulStorage_write_save((uint8_t *) (PAGE_START + 4), new_save_data);
+ } else {
+ ULOG_INFO("Writing new save to %p", new_save_address);
+ ulStorage_write_save(new_save_address, new_save_data);
+ }
+
+ return true;
+}
+
+bool ulStorage_erase(void)
+{
+ if (!persistent_db_field_size)
+ return true;
+
+ ulStorage_format_sector();
+
+ return true;
+}
+
+bool ulStorage_factoryReset(void)
+{
+ for (uint16_t i = 0; ulDatabase_reset(i); i++);
+
+ if (!persistent_db_field_size)
+ return true;
+
+ ulStorage_format_sector();
+
+ return true;
+}
diff --git a/KPI_Rover/Database/ulStorage.h b/KPI_Rover/Database/ulStorage.h
new file mode 100644
index 0000000..7fb32a7
--- /dev/null
+++ b/KPI_Rover/Database/ulStorage.h
@@ -0,0 +1,12 @@
+#ifndef __ULSTORAGE_H
+#define __ULSTORAGE_H
+
+#include
+
+bool ulStorage_init(void);
+bool ulStorage_load(void);
+bool ulStorage_save(void);
+bool ulStorage_erase(void);
+bool ulStorage_factoryReset(void);
+
+#endif /* __ULSTORAGE_H */
diff --git a/KPI_Rover/KPIRover.c b/KPI_Rover/KPIRover.c
index d04d367..4b2eea6 100644
--- a/KPI_Rover/KPIRover.c
+++ b/KPI_Rover/KPIRover.c
@@ -2,18 +2,25 @@
#include "cmsis_os.h"
#include "Database/ulDatabase.h"
+#include "Database/ulStorage.h"
+#include "Encoders/ulEncoder.h"
+#include "ulog.h"
+#include "ul_ulog.h"
static struct ulDatabase_ParamMetadata ulDatabase_params[] = {
+ {0, UINT16, true, 5}, // ENCODER_CONTROL_PERIOD_MS,
+ {0, FLOAT, true, 820.0f}, // ENCODER_TICKS_PER_REVOLUTION,
{0, INT32, false, 0}, // MOTOR_FL_RPM,
{0, INT32, false, 0}, // MOTOR_FR_RPM,
{0, INT32, false, 0}, // MOTOR_RL_RPM,
{0, INT32, false, 0}, // MOTOR_RR_RPM,
- {0, UINT16, true, 5}, // ENCODER_CONTROL_PERIOD_MS,
- {0, FLOAT, true, 820.0f} // ENCODER_TICKS_PER_REVOLUTION,
};
void KPIRover_Init(void) {
+ ULOG_INIT();
+ ULOG_SUBSCRIBE(ul_ulog_send, ULOG_DEBUG_LEVEL);
ulDatabase_init(ulDatabase_params, sizeof(ulDatabase_params) / sizeof(struct ulDatabase_ParamMetadata));
+ ulStorage_init();
ulEncoder_Init();
}
diff --git a/KPI_Rover/Logger/ul_ulog.c b/KPI_Rover/Logger/ul_ulog.c
index 0238c59..ae4c47d 100644
--- a/KPI_Rover/Logger/ul_ulog.c
+++ b/KPI_Rover/Logger/ul_ulog.c
@@ -46,6 +46,11 @@ static StaticQueue_t xUlogQueueBuffer;
static uint8_t ucUlogQueueStorage[LOG_QUEUE_LENGTH * MAX_LOG_MESSAGE_SIZE];
static QueueHandle_t xULogQueue = NULL;
+volatile uint32_t ulogUsbIsEstablished;
+osEventFlagsId_t ulogFlags;
+#define ULOG_CDC_TRANSMIT_FINISH_FLAG 0x1
+#define ULOG_DEV_CONNECTED_WAKEUP_FLAG 0x2
+
static const osThreadAttr_t ulogTask_attributes = {
.name = "ulogTask",
.stack_size = 128 * 4,
@@ -68,6 +73,9 @@ void ul_ulog_init()
{
Error_Handler();
}
+
+ ulogFlags = osEventFlagsNew(NULL);
+ ulogUsbIsEstablished = 0;
}
void ul_ulog_send(ulog_level_t level, const char *filename, char *msg)
@@ -113,24 +121,23 @@ void ul_ulog_send(ulog_level_t level, const char *filename, char *msg)
logBuffer[sizeof(logBuffer) - 1] = '\0';
}
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
- if (xPortIsInsideInterrupt()) {
- xQueueSendFromISR(xULogQueue, logBuffer, &xHigherPriorityTaskWoken);
- portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
- } else {
- xQueueSend(xULogQueue, logBuffer, pdMS_TO_TICKS(10));
- }
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ if (uxQueueSpacesAvailable(xULogQueue))
+ {
+ if (xPortIsInsideInterrupt())
+ {
+ xQueueSendFromISR(xULogQueue, logBuffer, &xHigherPriorityTaskWoken);
+ portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+ }
+ else
+ {
+ xQueueSend(xULogQueue, logBuffer, pdMS_TO_TICKS(10));
+ }
+ }
}
static void ulogTask(void *argument)
{
- // TODO: Try to get rid of this delay
- // Wait a bit for USB to initialize before starting logging
- osDelay(2000);
-
- ULOG_INIT();
- ULOG_SUBSCRIBE(ul_ulog_send, ULOG_DEBUG_LEVEL);
-
for (;;)
{
char logMessage[MAX_LOG_MESSAGE_SIZE];
@@ -139,7 +146,15 @@ static void ulogTask(void *argument)
if (result == pdTRUE)
{
logMessage[MAX_LOG_MESSAGE_SIZE - 1] = '\0';
+
+ while (ulogUsbIsEstablished != 1)
+ {
+ osEventFlagsWait(ulogFlags, ULOG_DEV_CONNECTED_WAKEUP_FLAG, 0, osWaitForever);
+ osDelay(2000);
+ }
+
CDC_Transmit_FS((uint8_t *)logMessage, strlen(logMessage));
+ osEventFlagsWait(ulogFlags, ULOG_CDC_TRANSMIT_FINISH_FLAG, 0, 100);
}
}
}
diff --git a/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c b/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c
index bcf571e..6757b0a 100644
--- a/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c
+++ b/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c
@@ -978,6 +978,11 @@ USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev,
return USBD_OK;
}
+#include "cmsis_os2.h"
+extern volatile uint32_t ulogUsbIsEstablished;
+extern osEventFlagsId_t ulogFlags;
+#define ULOG_DEV_CONNECTED_WAKEUP_FLAG 0x2
+
/**
* @brief USBD_LL_DevConnected
* Handle device connection event
@@ -989,6 +994,9 @@ USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev)
/* Prevent unused argument compilation warning */
UNUSED(pdev);
+ ulogUsbIsEstablished = 1;
+ osEventFlagsSet(ulogFlags, ULOG_DEV_CONNECTED_WAKEUP_FLAG);
+
return USBD_OK;
}
@@ -1002,6 +1010,9 @@ USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev)
{
USBD_StatusTypeDef ret = USBD_OK;
+ ulogUsbIsEstablished = 0;
+ osEventFlagsClear(ulogFlags, ULOG_DEV_CONNECTED_WAKEUP_FLAG);
+
/* Free Class Resources */
pdev->dev_state = USBD_STATE_DEFAULT;
diff --git a/STM32F407VGTX_FLASH_custom.ld b/STM32F407VGTX_FLASH_custom.ld
new file mode 100644
index 0000000..38d48f1
--- /dev/null
+++ b/STM32F407VGTX_FLASH_custom.ld
@@ -0,0 +1,209 @@
+/*
+******************************************************************************
+**
+** @file : LinkerScript.ld
+**
+** @author : Auto-generated by STM32CubeIDE
+**
+** Abstract : Linker script for STM32F407G-DISC1 Board embedding STM32F407VGTx Device from stm32f4 series
+** 1024KBytes FLASH
+** 64KBytes CCMRAM
+** 128KBytes RAM
+**
+** Set heap size, stack size and stack location according
+** to application requirements.
+**
+** Set memory bank area and size if external memory is used
+**
+** Target : STMicroelectronics STM32
+**
+** Distribution: The file is distributed as is, without any warranty
+** of any kind.
+**
+******************************************************************************
+** @attention
+**
+** Copyright (c) 2024 STMicroelectronics.
+** All rights reserved.
+**
+** This software is licensed under terms that can be found in the LICENSE file
+** in the root directory of this software component.
+** If no LICENSE file comes with this software, it is provided AS-IS.
+**
+******************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200; /* required amount of heap */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Memories definition */
+MEMORY
+{
+ CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
+ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
+ FLASH_BOOT (rx) : ORIGIN = 0x8000000, LENGTH = 16K
+ FLASH (rx) : ORIGIN = 0x8008000, LENGTH = 992K
+}
+
+/* Sections */
+SECTIONS
+{
+ /* The startup code into "FLASH" Rom type memory */
+ .isr_vector :
+ {
+ . = ALIGN(4);
+ KEEP(*(.isr_vector)) /* Startup code */
+ . = ALIGN(4);
+ } >FLASH_BOOT
+
+ /* The program code and other data into "FLASH" Rom type memory */
+ .text :
+ {
+ . = ALIGN(4);
+ *(.text) /* .text sections (code) */
+ *(.text*) /* .text* sections (code) */
+ *(.glue_7) /* glue arm to thumb code */
+ *(.glue_7t) /* glue thumb to arm code */
+ *(.eh_frame)
+
+ KEEP (*(.init))
+ KEEP (*(.fini))
+
+ . = ALIGN(4);
+ _etext = .; /* define a global symbols at end of code */
+ } >FLASH
+
+ /* Constant data into "FLASH" Rom type memory */
+ .rodata :
+ {
+ . = ALIGN(4);
+ *(.rodata) /* .rodata sections (constants, strings, etc.) */
+ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */
+ . = ALIGN(4);
+ } >FLASH
+
+ .ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+ {
+ . = ALIGN(4);
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ . = ALIGN(4);
+ } >FLASH
+
+ .ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+ {
+ . = ALIGN(4);
+ __exidx_start = .;
+ *(.ARM.exidx*)
+ __exidx_end = .;
+ . = ALIGN(4);
+ } >FLASH
+
+ .preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+ {
+ . = ALIGN(4);
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array*))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ . = ALIGN(4);
+ } >FLASH
+
+ .init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+ {
+ . = ALIGN(4);
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array*))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ . = ALIGN(4);
+ } >FLASH
+
+ .fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+ {
+ . = ALIGN(4);
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(SORT(.fini_array.*)))
+ KEEP (*(.fini_array*))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ . = ALIGN(4);
+ } >FLASH
+
+ /* Used by the startup to initialize data */
+ _sidata = LOADADDR(.data);
+
+ /* Initialized data sections into "RAM" Ram type memory */
+ .data :
+ {
+ . = ALIGN(4);
+ _sdata = .; /* create a global symbol at data start */
+ *(.data) /* .data sections */
+ *(.data*) /* .data* sections */
+ *(.RamFunc) /* .RamFunc sections */
+ *(.RamFunc*) /* .RamFunc* sections */
+
+ . = ALIGN(4);
+ _edata = .; /* define a global symbol at data end */
+
+ } >RAM AT> FLASH
+
+ _siccmram = LOADADDR(.ccmram);
+
+ /* CCM-RAM section
+ *
+ * IMPORTANT NOTE!
+ * If initialized variables will be placed in this section,
+ * the startup code needs to be modified to copy the init-values.
+ */
+ .ccmram :
+ {
+ . = ALIGN(4);
+ _sccmram = .; /* create a global symbol at ccmram start */
+ *(.ccmram)
+ *(.ccmram*)
+
+ . = ALIGN(4);
+ _eccmram = .; /* create a global symbol at ccmram end */
+ } >CCMRAM AT> FLASH
+
+ /* Uninitialized data section into "RAM" Ram type memory */
+ . = ALIGN(4);
+ .bss :
+ {
+ /* This is used by the startup in order to initialize the .bss section */
+ _sbss = .; /* define a global symbol at bss start */
+ __bss_start__ = _sbss;
+ *(.bss)
+ *(.bss*)
+ *(COMMON)
+
+ . = ALIGN(4);
+ _ebss = .; /* define a global symbol at bss end */
+ __bss_end__ = _ebss;
+ } >RAM
+
+ /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+ ._user_heap_stack :
+ {
+ . = ALIGN(8);
+ PROVIDE ( end = . );
+ PROVIDE ( _end = . );
+ . = . + _Min_Heap_Size;
+ . = . + _Min_Stack_Size;
+ . = ALIGN(8);
+ } >RAM
+
+ /* Remove information from the compiler libraries */
+ /DISCARD/ :
+ {
+ libc.a ( * )
+ libm.a ( * )
+ libgcc.a ( * )
+ }
+
+ .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/USB_DEVICE/App/usbd_cdc_if.c b/USB_DEVICE/App/usbd_cdc_if.c
index dfb73d2..1a4653c 100644
--- a/USB_DEVICE/App/usbd_cdc_if.c
+++ b/USB_DEVICE/App/usbd_cdc_if.c
@@ -22,7 +22,7 @@
#include "usbd_cdc_if.h"
/* USER CODE BEGIN INCLUDE */
-
+#include "cmsis_os2.h"
/* USER CODE END INCLUDE */
/* Private typedef -----------------------------------------------------------*/
@@ -62,6 +62,7 @@
*/
/* USER CODE BEGIN PRIVATE_DEFINES */
+#define ULOG_CDC_TRANSMIT_FINISH_FLAG 0x1
/* USER CODE END PRIVATE_DEFINES */
/**
@@ -109,7 +110,7 @@ uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];
extern USBD_HandleTypeDef hUsbDeviceFS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
-
+extern osEventFlagsId_t ulogFlags;
/* USER CODE END EXPORTED_VARIABLES */
/**
@@ -311,6 +312,8 @@ static int8_t CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum)
UNUSED(Buf);
UNUSED(Len);
UNUSED(epnum);
+
+ osEventFlagsSet(ulogFlags, ULOG_CDC_TRANSMIT_FINISH_FLAG);
/* USER CODE END 13 */
return result;
}