diff --git a/firmware/Makefile b/firmware/Makefile index 1020d16..6ee8916 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -7,7 +7,7 @@ OBJCOPY = avr-objcopy AVRDUDE ?= avrdude # Compiler and linker flags -CFLAGS = -Wall -std=c11 -Os +CFLAGS = -Wall -pedantic -std=c11 -Os LDFLAGS = # Target platform and programmer diff --git a/firmware/src/BitIO.h b/firmware/src/BitIO.h index 13f6605..4c5630c 100644 --- a/firmware/src/BitIO.h +++ b/firmware/src/BitIO.h @@ -16,19 +16,19 @@ static inline void BIT_SET(volatile uint8_t *target, uint8_t bit) __attribute__((always_inline)); static inline void BIT_SET(volatile uint8_t *target, uint8_t bit){ *target |= (1<", buffer.bytes); + uartPutChar('<'); + uartPutInteger(buffer.bytes); + uartPutChar('>'); for (int i = 0; i < buffer.bytes; i++) { - uartPrintf(" %02X", buffer.data[i]); + uartPutChar(' '); + uartPutHexByte(buffer.data[i]); } uartPutLine(NULL); } @@ -136,38 +141,96 @@ void commandRead(char* arg) { void commandWrite(char* arg) { if (arg == NULL) { - uartPutLine("ERR WRITE needs a start address"); + uartPutLine("ERROR WRITE needs a start address"); return; } // Parse address - AddressRange range = parseSingleAddress(arg); - if (!range.isValid) { - uartPutLine("ERR invalid address format"); + Address startAddress = parseSingleAddress(arg); + if (!startAddress.isValid) { + uartPutLine("ERROR invalid address format"); return; } // Only binary mode if (!binary_mode) { - uartPutLine("ERR WRITE in ASCII mode is not implemented"); + uartPutLine("ERROR WRITE in ASCII mode is not implemented"); return; } - // TODO read data from input and write to EEPROM - uartPutLine("ERR 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() { - // TODO - uartPutLine("ERR not implemented"); + uartPutLine("ERROR not implemented"); } // TESTREAD command: for testing purposes, reads a few bytes and returns them in a human readable format. void commandTestRead() { eepromSetReadMode(); - for (int i = 0x00; i < 0x20; i++) { - uartPrintf("TESTREAD 0x%02X: ", i); + for (uint8_t i = 0x00; i < 0x20; i++) { + uartPutString("TESTREAD 0x"); + uartPutHexByte(i); + uartPutString(": "); uint8_t byte = eepromReadByte(i); @@ -177,7 +240,9 @@ void commandTestRead() { uartPutChar('?'); } - uartPrintf(" (0x%02X)\n"); + uartPutString(" (0x"); + uartPutHexByte(byte); + uartPutLine(")"); } } 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 7df21e7..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; @@ -28,7 +31,7 @@ CommandLine readNextCommand(char* buffer, uint8_t bufferLength) { if (readChars >= bufferLength - 1) { // Reading was aborted after bufferLength-1 characters to prevent buffer overflow. - uartPutLine("ERR buffer overflow, discarding line"); + uartPutLine("ERROR buffer overflow, discarding line"); // Discard everything until we read an actual end of line for (unsigned char c = 0; c != '\n' && c != '\r'; c = uartGetChar()); @@ -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_ */ diff --git a/firmware/src/uart.c b/firmware/src/uart.c index 1d6fcba..e2dc588 100644 --- a/firmware/src/uart.c +++ b/firmware/src/uart.c @@ -56,17 +56,18 @@ void uartPutLine(char* data) { uartPutChar('\n'); } -// Transmit a sprintf formatted string (maximum buffer: 256 bytes!) -void uartPrintf(const char* format, ...) { - char outBuffer[256]; - - va_list args; - va_start(args, format); - - vsprintf(outBuffer, format, args); +// Convert an integer to decimal ASCII and transmit +void uartPutInteger(int value) { + char outBuffer[32]; + snprintf(outBuffer, 32, "%d", value); uartPutString(outBuffer); +} - va_end(args); +// Convert a byte to hexadecimal ASCII and transmit +void uartPutHexByte(uint8_t byte) { + char outBuffer[8]; + snprintf(outBuffer, 8, "%02hhX", byte); + uartPutString(outBuffer); } // Receive a single character (blocking) diff --git a/firmware/src/uart.h b/firmware/src/uart.h index 7421af4..9065993 100644 --- a/firmware/src/uart.h +++ b/firmware/src/uart.h @@ -16,8 +16,11 @@ void uartPutString(char* data); // Transmit a string followed by a line break void uartPutLine(char* data); -// Transmit a sprintf formatted string (maximum buffer: 256 bytes!) -void uartPrintf(const char* format, ...); +// Convert an integer to decimal ASCII and transmit +void uartPutInteger(int value); + +// Convert a byte to hexadecimal ASCII and transmit +void uartPutHexByte(uint8_t byte); // Read a single character (blocking) unsigned char uartGetChar();