aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2025-09-06 13:24:11 -0400
committerSam Anthony <sam@samanthony.xyz>2025-09-06 13:24:11 -0400
commit366330d3943ec4e066197a8d034d90caf8422a1d (patch)
treeeef4e0cc7882f77be931b81c8b5bb4c91dbcc0aa
parentdf3ed451bf11d40e6369b0333fef92c536c57b4d (diff)
downloadcan-gauge-interface-366330d3943ec4e066197a8d034d90caf8422a1d.zip
fw: handle "w" write eeprom command
-rw-r--r--doc/protocol13
-rw-r--r--fw/eeprom.c2
-rw-r--r--fw/eeprom.h2
-rw-r--r--fw/types.c8
-rw-r--r--fw/types.h9
-rw-r--r--fw/usb.c168
6 files changed, 165 insertions, 37 deletions
diff --git a/doc/protocol b/doc/protocol
index 59e807d..f144c50 100644
--- a/doc/protocol
+++ b/doc/protocol
@@ -1,5 +1,5 @@
<command> ::= <opcode> " " <args> "\n"
-<response> ::= (<retval>|"nack") "\n"
+<response> ::= (<retval> | "nack") "\n"
Echo:
<opcode> ::= "e"
@@ -8,7 +8,7 @@ Echo:
Write EEPROM:
<opcode> ::= "w"
-<args> ::= <addr> " " <size> " " <bytes>
+<args> ::= <addrHi> " " <addrLo> " " <size> " " <bytes>
<retval> ::= "ok"
Read EEPROM:
@@ -16,10 +16,9 @@ Read EEPROM:
<args> ::= <addr> " " <size>
<retval> ::= <bytes>{size}
-<addr> ::= "0"--"2047"
+<addrHi> ::= <byte>
+<addrLo> :: <byte>
+<size> ::= <byte>
<bytes> ::= <byte> <bytetail> | ""
<bytetail> ::= " " <byte> <bytetail> | ""
-<byte> ::= "0"--"FF"
-<size> ::= "0"--"2048"
-
-Messages must not contain NUL ('\0').
+<byte> ::= "00"--"FF"
diff --git a/fw/eeprom.c b/fw/eeprom.c
index ce8aed7..686be40 100644
--- a/fw/eeprom.c
+++ b/fw/eeprom.c
@@ -90,7 +90,7 @@ eepromWrite(U16 addr, U8 *data, U8 size) {
while (size--) {
(void)spiTx(*data);
data++;
- incU16(&addr);
+ addU16(&addr, 1u);
// Check if write crosses page boundary
if (isPageStart(addr) && size) {
diff --git a/fw/eeprom.h b/fw/eeprom.h
index 07438a0..16d9d72 100644
--- a/fw/eeprom.h
+++ b/fw/eeprom.h
@@ -5,10 +5,8 @@
*
* Usage:
*
- * #include <xc.h>
* #include <stdint.h>
* #include "types.h"
- * #include "spi.h"
* #include "eeprom.h"
*/
diff --git a/fw/types.c b/fw/types.c
index c18392e..a6c2411 100644
--- a/fw/types.c
+++ b/fw/types.c
@@ -5,9 +5,9 @@
#include "types.h"
void
-incU16(U16 *x) {
- x->lo++;
- if (x->lo == 0u) {
- x->hi++;
+addU16(U16 *a, U8 b) {
+ a->lo += b;
+ if (STATUSbits.C) {
+ a->hi++;
}
}
diff --git a/fw/types.h b/fw/types.h
index 22a651e..835491d 100644
--- a/fw/types.h
+++ b/fw/types.h
@@ -7,10 +7,15 @@
* #include "types.h"
*/
+typedef enum {
+ OK,
+ FAIL,
+} Status;
+
typedef uint8_t U8;
typedef struct {
- U8 lo, hi;
+ U8 hi, lo;
} U16;
-void incU16(U16 *x);
+void addU16(U16 *a, U8 b);
diff --git a/fw/usb.c b/fw/usb.c
index 0b03250..ae95de7 100644
--- a/fw/usb.c
+++ b/fw/usb.c
@@ -1,17 +1,23 @@
#include <xc.h>
+#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
-#include <string.h>
#include <usb.h>
#include <usb_device.h>
#include <usb_device_cdc.h>
+#include "types.h"
+#include "eeprom.h"
+
#include "usb.h"
/***** Constants *****/
+// Safety counter
+#define BAILOUT 100u
+
// Line coding
// See struct USB_CDC_LINE_CODING
enum {
@@ -30,8 +36,8 @@ typedef struct State {
// Input buffer
typedef struct {
- uint8_t data[CDC_DATA_OUT_EP_SIZE];
- uint8_t len, head;
+ U8 data[CDC_DATA_OUT_EP_SIZE];
+ U8 len, head;
} RxQueue;
/***** Function Declarations *****/
@@ -44,27 +50,47 @@ static State *readEepromState(void);
/***** Global Variables *****/
-static uint8_t txBuf[CDC_DATA_IN_EP_SIZE];
+static U8 txBuf[CDC_DATA_IN_EP_SIZE];
static RxQueue rxBuf = {.len = 0u, .head = 0u};
/***** Function Definitions *****/
-// Return the next Rx'd char, or '\0' if no data.
-static char
-getchar(void) {
+// Rx a char from USB.
+// Returns FAIL if no data.
+static Status
+getchar(char *c) {
if (rxBuf.len <= 0u) {
rxBuf.len = getsUSBUSART(rxBuf.data, sizeof(rxBuf.data));
rxBuf.head = 0u;
}
if (rxBuf.len > 0u) {
+ *c = rxBuf.data[rxBuf.head];
+ rxBuf.head++;
rxBuf.len--;
- return rxBuf.data[rxBuf.head++];
+ return OK;
} else {
- return '\0';
+ return FAIL;
}
}
+// Attempt to Rx the next char up to retries+1 times.
+static Status
+getcharBlock(char *c, U8 retries) {
+ Status status;
+
+ do {
+ status = getchar(c);
+ if (status == OK) {
+ return OK;
+ }
+
+ USBDeviceTasks();
+ } while (--retries + 1u);
+
+ return FAIL;
+}
+
void
usbTask(void) {
static State state = {idleState};
@@ -86,25 +112,31 @@ usbTask(void) {
CDCTxService();
}
+static void
+nack(void) {
+ putsUSBUSART("nack\n");
+}
+
// Read (the start of) a command from USB.
static State *
idleState(void) {
static State state;
- char opcode;
+ char opcode, junk;
+ Status status;
state.next = idleState;
// Read opcode
- opcode = getchar();
- if (!opcode) {
+ status = getchar(&opcode);
+ if (status != OK) {
// No data
return &state;
}
// Skip space
- if (!getchar()) {
+ if (getchar(&junk) != OK) {
// Incomplete command
- putsUSBUSART("nack\n");
+ nack();
return &state;
}
@@ -115,7 +147,7 @@ idleState(void) {
case 'r': state.next = readEepromState; break;
default: // invalid command
rxBuf.len = 0u; // discard input
- putsUSBUSART("nack\n");
+ nack();
break;
}
@@ -126,35 +158,129 @@ idleState(void) {
static State *
echoState(void) {
static State state;
- uint8_t i;
+ static U8 i = 0u;
+ Status status;
state.next = echoState;
- i = 0u;
while (i < sizeof(txBuf)) {
- txBuf[i] = getchar();
- if (txBuf[i++] == '\n') {
+ status = getchar((char *) &txBuf[i]);
+ if (status == OK && txBuf[i++] == '\n') {
// End of command
state.next = idleState;
break;
+ } else if (status != OK) {
+ // Wait to receive more data and continue on next call
+ return &state;
}
}
if (i > 0u) {
putUSBUSART(txBuf, i);
+ i = 0u;
}
return &state;
}
+// Parse a 2-digit hex number and consume the space after it.
+static Status
+parseU8(U8 *n) {
+ U8 i;
+ char c;
+ Status status;
+
+ *n = 0u;
+ for (i = 0u; i < 2u; i++) {
+ *n <<= 4u;
+ status = getcharBlock(&c, BAILOUT);
+ if (status != OK) {
+ return FAIL;
+ }
+ if (isdigit(c)) {
+ *n += c - '0';
+ } else if (c >= 'A' && c <= 'F') {
+ *n += 10u + (c - 'A');
+ } else if (c >= 'a' && c <= 'f') {
+ *n += 10u + (c - 'a');
+ } else {
+ return FAIL;
+ }
+ }
+
+ // Skip space
+ status = getcharBlock(&c, BAILOUT);
+ if (status != OK || !isspace(c)) {
+ return FAIL;
+ }
+
+ return OK;
+}
+
// Handle "w" write eeprom command.
static State *
writeEepromState(void) {
static State state;
-
- // TODO
+ U16 addr;
+ U8 size;
+ U8 i;
+ char c;
+ Status status;
state.next = idleState;
+
+ // Read <addrHi/Lo> and <size>
+ if (parseU8(&addr.hi) != OK) {
+ nack();
+ return &state;
+ }
+ if (parseU8(&addr.lo) != OK) {
+ nack();
+ return &state;
+ }
+ if (parseU8(&size) != OK) {
+ nack();
+ return &state;
+ }
+
+ eepromWriteEnable();
+
+ // Read <bytes> into buffer
+ for (i = 0u; i < size; i++) {
+ // Reuse txBuf to save memory
+
+ // Receive byte
+ status = getcharBlock(&c, BAILOUT);
+ if (status != OK) {
+ nack();
+ eepromWriteDisable();
+ return &state;
+ }
+
+ // Check for overflow
+ if (i > 0u && (i%sizeof(txBuf)) == 0u) {
+ eepromWrite(addr, txBuf, sizeof(txBuf));
+ addU16(&addr, sizeof(txBuf));
+ }
+
+ txBuf[i%sizeof(txBuf)] = c;
+ }
+
+ // Flush buffer to eeprom
+ if (i > 0u) {
+ eepromWrite(addr, txBuf, i%sizeof(txBuf));
+ }
+
+ eepromWriteDisable();
+
+ // Consume '\n'
+ status = getcharBlock(&c, BAILOUT);
+ if (status != OK || c != '\n') {
+ nack();
+ }
+
+ putsUSBUSART("ok\n");
+
return &state;
}