From 4dfb0d7edf50d96bb855a6720539b184e72a9b89 Mon Sep 17 00:00:00 2001 From: binaryDiv Date: Sat, 17 Apr 2021 02:35:44 +0200 Subject: [PATCH] Implement WRITE; refactor address parsing --- firmware/src/commands.c | 71 ++++++++++++++++++++++++++++++++++++++--- firmware/src/common.h | 6 ++++ firmware/src/eeprom.c | 49 +++++++++++++++++++++------- firmware/src/eeprom.h | 6 ++-- firmware/src/parsing.c | 69 ++++++++++++++++++++++----------------- firmware/src/parsing.h | 4 +-- 6 files changed, 154 insertions(+), 51 deletions(-) diff --git a/firmware/src/commands.c b/firmware/src/commands.c index 6731767..5dcdc3c 100644 --- a/firmware/src/commands.c +++ b/firmware/src/commands.c @@ -30,11 +30,11 @@ void executeCommand(CommandLine cmdLine) { } else if (strcmp(cmdLine.command, "WRITE") == 0) { // WRITE command: Takes a hex address as argument, reads data from UART and writes it to the EEPROM. - commandRead(cmdLine.arg); + commandWrite(cmdLine.arg); } else if (strcmp(cmdLine.command, "ERASE") == 0) { // ERASE command: Takes a hex address range as argument and writes 0x00 bytes to the specified range. - commandRead(cmdLine.arg); + commandErase(cmdLine.arg); } else if (strcmp(cmdLine.command, "TESTREAD") == 0) { // TESTREAD command: for testing purposes, reads a few bytes and returns them in a human readable format. @@ -111,7 +111,9 @@ void commandRead(char* arg) { do { // Read a single block with up to DATA_BLOCK_SIZE bytes - range.from = eepromReadBlock(range, &buffer); + Address nextAddress = eepromReadBlock(range, &buffer); + range.isValid = nextAddress.isValid; + range.from = nextAddress.value; if (binary_mode) { // Send block as binary "package": @@ -144,6 +146,8 @@ void commandWrite(char* arg) { } // Parse address + Address startAddress = parseSingleAddress(arg); + if (!startAddress.isValid) { uartPutLine("ERROR invalid address format"); return; } @@ -154,8 +158,65 @@ void commandWrite(char* arg) { return; } - // TODO read data from input and write to EEPROM - uartPutLine("ERROR not implemented"); + // Create data buffer + uint8_t byteBuffer[DATA_BLOCK_SIZE]; + DataBuffer buffer = { + .data = byteBuffer, + .maxSize = DATA_BLOCK_SIZE, + .bytes = 0 + }; + + // "OK" indicates that data can be sent now. + uartPutLine("OK START"); + + Address currentAddress = startAddress; + uint8_t block_length = 0; + + do { + // Reset buffer + buffer.bytes = 0; + + // Read and check block length + block_length = uartGetChar(); + + if (block_length > buffer.maxSize) { + uartPutString("ERROR maximal block size is: "); + uartPutInteger(buffer.maxSize); + uartPutLine(NULL); + return; + } + + // Read data bytes + while (buffer.bytes < block_length) { + buffer.data[buffer.bytes++] = uartGetChar(); + } + + // Write blocks to EEPROM until an "end block" (0 bytes) is received + if (buffer.bytes > 0) { + currentAddress = eepromWriteBlock(currentAddress, buffer); + + // TODO wait necessary? + //_delay_ms(100); + + if (!currentAddress.isValid) { + uartPutLine("ERROR reached end of EEPROM while writing block"); + return; + } + else if (currentAddress.value == 0 || currentAddress.value >= HIGHEST_VALID_ADDRESS) { + // Block was written completely, but reached end of EEPROM now. No further data allowed (except for 0 byte "end block"). + currentAddress.isValid = false; + uartPutLine("OK STOP (end of EEPROM)"); + } + else { + // Next block of data can be sent now. + uartPutLine("OK CONTINUE"); + } + } + + } while (block_length > 0); + + // WRITE completed + uartPutLine("OK END"); } void commandErase() { diff --git a/firmware/src/common.h b/firmware/src/common.h index ba6e843..d6cb0e7 100644 --- a/firmware/src/common.h +++ b/firmware/src/common.h @@ -14,6 +14,12 @@ typedef uint16_t address_t; // Define highest valid address for the EEPROM #define HIGHEST_VALID_ADDRESS 0x7fff +// Type definition for a single address (nullable) +typedef struct { + bool isValid; + address_t value; +} Address; + // Type definition for address ranges (from-to) typedef struct { bool isValid; diff --git a/firmware/src/eeprom.c b/firmware/src/eeprom.c index 7b7e00d..abe6e6b 100644 --- a/firmware/src/eeprom.c +++ b/firmware/src/eeprom.c @@ -115,12 +115,16 @@ void eepromWriteByte(address_t address, uint8_t data) { } // Read multiple bytes from EEPROM into a buffer -address_t eepromReadBlock(AddressRange addressRange, DataBuffer* buffer) { +Address eepromReadBlock(AddressRange addressRange, DataBuffer* buffer) { // Set read mode eepromSetReadMode(); buffer->bytes = 0; + if (!addressRange.isValid) { + return (Address) {false, 0}; + } + address_t currentAddress = addressRange.from; address_t highestAddress = addressRange.to; @@ -130,20 +134,41 @@ address_t eepromReadBlock(AddressRange addressRange, DataBuffer* buffer) { while (currentAddress <= highestAddress && buffer->bytes < buffer->maxSize) { buffer->data[buffer->bytes++] = eepromReadByte(currentAddress++); + + // Handle integer overflow + if (currentAddress == 0) { + return (Address) {false, 0}; + } } - return currentAddress; + return (Address) {true, currentAddress}; } -// // Write multiple bytes from a buffer to EEPROM -// address_t eepromWriteBlock(address_t currentAddress, DataBuffer buffer) { -// // Set write mode -// eepromSetWriteMode(); +// Write multiple bytes from a buffer to EEPROM +Address eepromWriteBlock(Address startAddress, DataBuffer buffer) { + // Set write mode + eepromSetWriteMode(); -// for (uint8_t i = 0; currentAddress <= HIGHEST_VALID_ADDRESS && i < buffer.bytes; i++) { -// eepromWriteByte(currentAddress++, buffer.data[i]); -// // TODO address overflow -// } + if (!startAddress.isValid) { + return (Address) {false, 0}; + } -// return currentAddress; -// } + address_t currentAddress = startAddress.value; + uint8_t i; + + for (i = 0; i < buffer.bytes && currentAddress <= HIGHEST_VALID_ADDRESS; i++) { + eepromWriteByte(currentAddress++, buffer.data[i]); + + // Handle integer overflow + if (currentAddress == 0) { + break; + } + } + + if (i < buffer.bytes) { + // There are unwritten bytes left -> must be an address overflow + return (Address) {false, 0}; + } + + return (Address) {true, currentAddress}; +} diff --git a/firmware/src/eeprom.h b/firmware/src/eeprom.h index c12bbd2..0dfdffd 100644 --- a/firmware/src/eeprom.h +++ b/firmware/src/eeprom.h @@ -41,9 +41,9 @@ uint8_t eepromReadByte(address_t address); void eepromWriteByte(address_t address, uint8_t data); // Read multiple bytes from EEPROM into a buffer -address_t eepromReadBlock(AddressRange addressRange, DataBuffer* buffer); +Address eepromReadBlock(AddressRange addressRange, DataBuffer* buffer); -// // Write multiple bytes from a buffer to EEPROM -// address_t eepromWriteBlock(address_t addressRange, DataBuffer buffer); +// Write multiple bytes from a buffer to EEPROM +Address eepromWriteBlock(Address startAddress, DataBuffer buffer); #endif /* EEPROM_H_ */ diff --git a/firmware/src/parsing.c b/firmware/src/parsing.c index 41ec148..8316251 100644 --- a/firmware/src/parsing.c +++ b/firmware/src/parsing.c @@ -8,6 +8,9 @@ #include #include +// Command parsing +// --------------- + // Read and parse command line and dispatch command void parseNextCommand() { const int bufferLength = 80; @@ -66,8 +69,11 @@ CommandLine tokenizeCommand(char* cmd) { } // Address parsing -bool parseSingleAddressTo(char* addressStr, address_t* address) { - if (addressStr == NULL || strlen(addressStr) != 4 || address == NULL) { +// --------------- + +// Validates a single 2-byte hexadecimal address string "XXXX". +bool validateAddressString(char* addressStr) { + if (addressStr == NULL || strlen(addressStr) != 4) { return false; } @@ -77,18 +83,19 @@ bool parseSingleAddressTo(char* addressStr, address_t* address) { } } - *address = strtol(addressStr, NULL, 16); return true; } -// Parses a single 2-byte hexadecimal address string "XXXX:YYYY" to an actual address. -AddressRange parseSingleAddress(char* addressStr) { - AddressRange range = {true, 0, 0}; - if (!parseSingleAddressTo(addressStr, &range.from)) { - return (AddressRange) {false, 0, 0}; +// Parses a single 2-byte hexadecimal address string "XXXX" to an actual address. +Address parseSingleAddress(char* addressStr) { + Address address = {false, 0}; + + if (validateAddressString(addressStr)) { + address.isValid = true; + address.value = strtol(addressStr, NULL, 16); } - range.to = range.from; - return range; + + return address; } // Parses a hexadecimal address range string "XXXX:YYYY" to actual addresses. @@ -98,25 +105,29 @@ AddressRange parseAddressRange(char* addressStr) { return (AddressRange) {false, 0, 0}; } - int len = strlen(addressStr); - if (len != 4 && len != 9) { + int length = strlen(addressStr); + + if (length == 4) { + Address address = parseSingleAddress(addressStr); + + return (AddressRange) { + .isValid = address.isValid, + .from = address.value, + .to = address.value + }; + } + else if (length == 9 && addressStr[4] == ':') { + addressStr[4] = '\0'; + Address fromAddress = parseSingleAddress(addressStr); + Address toAddress = parseSingleAddress(addressStr + 5); + + return (AddressRange) { + .isValid = fromAddress.isValid && toAddress.isValid, + .from = fromAddress.value, + .to = toAddress.value + }; + } + else { return (AddressRange) {false, 0, 0}; } - - if (len == 9) { - if (addressStr[4] != ':') { - return (AddressRange) {false, 0, 0}; - } - addressStr[4] = '\0'; - } - - AddressRange range = parseSingleAddress(addressStr); - - if (range.isValid && len == 9) { - if (!parseSingleAddressTo(addressStr + 5, &range.to)) { - return (AddressRange) {false, 0, 0}; - } - } - - return range; } diff --git a/firmware/src/parsing.h b/firmware/src/parsing.h index fe15c27..13cbe6d 100644 --- a/firmware/src/parsing.h +++ b/firmware/src/parsing.h @@ -14,8 +14,8 @@ void parseNextCommand(); CommandLine readNextCommand(char* buffer, uint8_t bufferLength); CommandLine tokenizeCommand(char* cmd); -bool parseSingleAddressTo(char* addressStr, address_t* address); -AddressRange parseSingleAddress(char* addressStr); +bool validateAddressString(char* addressStr); +Address parseSingleAddress(char* addressStr); AddressRange parseAddressRange(char* addressStr); #endif /* PARSING_H_ */