Refactor protocol/parsing code; implement address parsing

This commit is contained in:
Lexi / Zoe 2021-04-04 02:50:25 +02:00
parent 28be3c4e8b
commit bc849d9662
Signed by: binaryDiv
GPG Key ID: F8D4956E224DA232
7 changed files with 303 additions and 159 deletions

132
firmware/src/commands.c Normal file
View File

@ -0,0 +1,132 @@
#include "config.h"
#include "commands.h"
#include "parsing.h"
#include "uart.h"
#include "eeprom.h"
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <util/delay.h>
// Execute a single parsed command
void executeCommand(CommandLine cmdLine) {
// Parse command
if (strcmp(cmdLine.command, "INIT") == 0) {
// INIT command: Initializes connection.
commandInit();
}
else if (strcmp(cmdLine.command, "READ") == 0) {
// READ command: Takes a hex address range (or single address) as argument,
// reads data and returns it in hexadecimal ASCII format.
commandRead(cmdLine.arg);
}
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);
}
else if (strcmp(cmdLine.command, "TESTREAD") == 0) {
// TESTREAD command: for testing purposes, reads a few bytes and returns them in a human readable format.
commandTestRead();
}
else if (strcmp(cmdLine.command, "TESTWRITE") == 0) {
// TESTWRITE command: for testing purposes, writes a few bytes
commandTestWrite(cmdLine.arg);
}
else {
// unknown command: return error message
uartPutString("ERR invalid command\r\n");
}
}
void commandInit() {
// TODO init... or something?
uartPutString("OK\n");
}
void commandRead(char* arg) {
if (arg == NULL) {
uartPutString("ERR READ needs an address or address range\r\n");
return;
}
// Parse address(es)
AddressRange range = parseAddressRange(arg);
if (!range.isValid) {
uartPutString("ERR invalid address format\r\n");
return;
}
// TODO read data and output it
uartPutString("ERR not implemented\n");
}
void commandWrite(char* arg) {
if (arg == NULL) {
uartPutString("ERR WRITE needs a start address\r\n");
return;
}
// Parse address
AddressRange range = parseSingleAddress(arg);
if (!range.isValid) {
uartPutString("ERR invalid address format\r\n");
return;
}
// TODO read data from input and write to EEPROM
uartPutString("ERR not implemented\n");
}
void commandErase() {
// TODO
uartPutString("ERR not implemented\n");
}
// TESTREAD command: for testing purposes, reads a few bytes and returns them in a human readable format.
void commandTestRead() {
uint8_t byte;
char outBuffer[20];
eepromSetReadMode();
for (int i = 0x00; i < 0x20; i++) {
itoa(i, outBuffer, 16);
uartPutString("TESTREAD 0x");
uartPutString(outBuffer);
uartPutString(": ");
byte = eepromReadByte(i);
itoa(byte, outBuffer, 16);
uartPutChar(byte);
uartPutString(" (0x");
uartPutString(outBuffer);
uartPutString(")\r\n");
}
}
// TESTWRITE command: for testing purposes, writes a few bytes
void commandTestWrite(char* arg) {
char str[] = "Ohai world";
address_t addr = 0x00;
char* writeStr = str;
if (arg != NULL) {
writeStr = arg;
}
eepromSetWriteMode();
// write input line instead of static string
for (char* p = writeStr; *p != '\0'; p++) {
eepromWriteByte(addr, *p);
//_delay_ms(100);
addr++;
}
// TODO necessary?
_delay_ms(100);
uartPutString("TESTWRITE success\r\n");
}

18
firmware/src/commands.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef COMMANDS_H_
#define COMMANDS_H_
#include "config.h"
#include "parsing.h"
void executeCommand(CommandLine cmdLine);
void commandInit();
void commandRead(char* arg);
void commandWrite(char* arg);
void commandErase();
// TODO commands only for testing
void commandTestRead();
void commandTestWrite(char* arg);
#endif /* COMMANDS_H_ */

View File

@ -1,7 +1,7 @@
#include "config.h"
#include "uart.h"
#include "eeprom.h"
#include "protocol.h"
#include "parsing.h"
#include <util/delay.h>
@ -18,6 +18,6 @@ int main(void) {
while(1) {
// Run main loop endlessly
protocolMainLoop();
parseNextCommand();
}
}

122
firmware/src/parsing.c Normal file
View File

@ -0,0 +1,122 @@
#include "config.h"
#include "parsing.h"
#include "commands.h"
#include "uart.h"
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <util/delay.h>
// Read and parse command line and dispatch command
void parseNextCommand() {
const int bufferLength = 80;
char buffer[bufferLength];
// Read next command
CommandLine cmdLine = readNextCommand(buffer, bufferLength);
// Execute command
executeCommand(cmdLine);
}
// Read next command line from UART
CommandLine readNextCommand(char* buffer, uint8_t bufferLength) {
while (1) {
// Read next command
int readChars = uartGetLine(buffer, bufferLength);
if (readChars >= bufferLength - 1) {
// Reading was aborted after bufferLength-1 characters to prevent buffer overflow.
uartPutString("ERR buffer overflow, discarding line\r\n");
// Discard everything until we read an actual end of line
for (unsigned char c = 0; c != '\n' && c != '\r'; c = uartGetChar());
readChars = 0;
}
if (readChars != 0) {
return tokenizeCommand(buffer);
}
}
}
// Splits a command "CMD ARGS" to two strings "CMD" and "ARGS". Returns pointer
// to "ARGS" or NULL if no arguments were found. Changes the input string!
CommandLine tokenizeCommand(char* cmd) {
CommandLine cmdLine = {
cmd,
NULL
};
if (cmd == NULL) {
return cmdLine;
}
// Search for a space character
for (char* p = cmd; *p != '\0'; p++) {
if (*p == ' ') {
// Split strings by replacing the space by \0, then set argument pointer.
*p++ = '\0';
cmdLine.arg = p;
break;
}
}
return cmdLine;
}
// Address parsing
bool parseSingleAddressTo(char* addressStr, address_t* address) {
if (addressStr == NULL || strlen(addressStr) != 4 || address == NULL) {
return false;
}
for (char* p = addressStr; *p != '\0'; p++) {
if ((*p < '0' || *p > '9') && (*p < 'A' || *p > 'F')) {
return false;
}
}
*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};
}
range.to = range.from;
return range;
}
// Parses a hexadecimal address range string "XXXX:YYYY" to actual addresses.
// A single address "XXXX" is equivalent to "XXXX:XXXX", with from == to.
AddressRange parseAddressRange(char* addressStr) {
if (addressStr == NULL) {
return (AddressRange) {false, 0, 0};
}
int len = strlen(addressStr);
if (len != 4 && len != 9) {
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 + 4, &range.to)) {
return (AddressRange) {false, 0, 0};
}
}
return range;
}

29
firmware/src/parsing.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef PARSING_H_
#define PARSING_H_
#include "config.h"
#include "eeprom.h"
#include <stdbool.h>
// Type definition for a command line with optional argument (actually just two pointers to strings)
typedef struct {
char* command;
char* arg;
} CommandLine;
// Type definition for address ranges (from-to)
typedef struct {
bool isValid;
address_t from;
address_t to;
} AddressRange;
void parseNextCommand();
CommandLine readNextCommand(char* buffer, uint8_t bufferLength);
CommandLine tokenizeCommand(char* cmd);
bool parseSingleAddressTo(char* addressStr, address_t* address);
AddressRange parseSingleAddress(char* addressStr);
AddressRange parseAddressRange(char* addressStr);
#endif /* PARSING_H_ */

View File

@ -1,141 +0,0 @@
#include "config.h"
#include "protocol.h"
#include "uart.h"
#include "eeprom.h"
#include <string.h>
#include <stdlib.h>
#include <util/delay.h>
// Splits a command "CMD ARGS" to two strings "CMD" and "ARGS". Returns pointer
// to "ARGS" or NULL if no arguments were found. Changes the input string!
char* tokenizeCommand(char* cmd) {
if (cmd == NULL) {
return NULL;
}
// Search for a space character
for (char* p = cmd; *p != '\0'; p++) {
if (*p == ' ') {
// Split strings by replacing the space by \0, then return pointer
// to command arguments
*p++ = '\0';
return p;
}
}
// No space character found: command has no arguments, return NULL
return NULL;
}
// Reads, parses and executes next command
void parseNextCommand() {
const int bufferLength = 80;
char buffer[bufferLength];
// Read next command
int readChars = uartGetLine(buffer, bufferLength);
// Check if line is non-empty and has been read completely
if (readChars == 0) {
return;
}
else if (readChars >= bufferLength-1) {
// Reading was aborted after bufferLength-1 characters to prevent
// buffer overflow.
// TODO Actually this isn't quite correct: if exactly bufferLen-1
// characters have been read including the \n, this could be
// true as well... test this?
uartPutString("ERROR buffer overflow while reading line\r\n");
return;
}
// Tokenize command
char* cmd = buffer;
char* args = tokenizeCommand(cmd);
// Parse command
if (strcmp(cmd, "HELLO") == 0) {
// HELLO command: initializes connection
uartPutString("OHAI\r\n");
}
else if (strcmp(cmd, "READ") == 0) {
// READ command: takes a hex address or address range as argument,
// reads data and returns them in hexadecimal ASCII format.
// Check if arguments exist
if (args == NULL) {
uartPutString("ERROR READ needs an address\r\n");
return;
}
// Parse address(es)
// TODO
uartPutString(args);
// Read...
// TODO
}
else if (strcmp(cmd, "TESTREAD") == 0) {
// TESTREAD command: for testing purposes, reads a few bytes and
// returns them in a human readable format.
uint8_t byte;
char outBuffer[20];
eepromSetReadMode();
for (int i = 0x00; i < 0x20; i++) {
itoa(i, outBuffer, 16);
uartPutString("TESTREAD 0x");
uartPutString(outBuffer);
uartPutString(": ");
byte = eepromReadByte(i);
itoa(byte, outBuffer, 16);
uartPutChar(byte);
uartPutString(" (0x");
uartPutString(outBuffer);
uartPutString(")\r\n");
}
}
else if (strcmp(cmd, "TESTWRITE") == 0) {
// TESTWRITE command: for testing purposes, writes a few bytes
char str[] = "Ohai world";
address_t addr = 0x00;
char* writeStr = str;
if (args != NULL) {
writeStr = args;
}
eepromSetWriteMode();
// write input line instead of static string
for (char* p = writeStr; *p != '\0'; p++) {
eepromWriteByte(addr, *p);
//_delay_ms(100);
addr++;
}
// TODO necessary?
_delay_ms(100);
uartPutString("TESTWRITE success\r\n");
}
else {
// unknown command: return error message
uartPutString("ERROR invalid command\r\n");
}
}
// Runs main loop to parse commands
void protocolMainLoop() {
while(1) {
// Parse next command
parseNextCommand();
}
}

View File

@ -1,16 +0,0 @@
#ifndef PROTOCOL_H_
#define PROTOCOL_H_
#include "config.h"
#include "protocol.h"
#include "uart.h"
#include "eeprom.h"
// Reads, parses and executes next command
void parseNextCommand();
// Runs main loop to parse commands
void protocolMainLoop();
#endif /* PROTOCOL_H_ */