Simple eeprom programmer
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import serial
|
||||||
|
|
||||||
|
|
||||||
|
FRAME_START = 0x55
|
||||||
|
CMD_INFO = ord("I")
|
||||||
|
CMD_READ = ord("R")
|
||||||
|
CMD_WRITE_PAGE = ord("W")
|
||||||
|
|
||||||
|
REPLY_OK = ord("K")
|
||||||
|
REPLY_DATA = ord("D")
|
||||||
|
REPLY_ERROR = ord("E")
|
||||||
|
|
||||||
|
MAX_SIZE = 32768
|
||||||
|
DEFAULT_PAGE_SIZE = 64
|
||||||
|
|
||||||
|
|
||||||
|
class ProtocolError(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def read_exact(port: serial.Serial, length: int) -> bytes:
|
||||||
|
data = bytearray()
|
||||||
|
while len(data) < length:
|
||||||
|
chunk = port.read(length - len(data))
|
||||||
|
if not chunk:
|
||||||
|
raise ProtocolError(f"timeout while reading {length} bytes")
|
||||||
|
data.extend(chunk)
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
|
|
||||||
|
def expect_frame_start(port: serial.Serial) -> int:
|
||||||
|
start = read_exact(port, 1)[0]
|
||||||
|
if start != FRAME_START:
|
||||||
|
raise ProtocolError(f"bad frame start: 0x{start:02x}")
|
||||||
|
return read_exact(port, 1)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def send_command(port: serial.Serial, command: int, payload: bytes = b"") -> int:
|
||||||
|
port.write(bytes([FRAME_START, command]) + payload)
|
||||||
|
port.flush()
|
||||||
|
return expect_frame_start(port)
|
||||||
|
|
||||||
|
|
||||||
|
def query_info(port: serial.Serial) -> tuple[int, int]:
|
||||||
|
reply = send_command(port, CMD_INFO)
|
||||||
|
if reply != REPLY_DATA:
|
||||||
|
raise ProtocolError(f"unexpected INFO reply: {reply!r}")
|
||||||
|
length = read_exact(port, 1)[0]
|
||||||
|
payload = read_exact(port, length)
|
||||||
|
if length != 4:
|
||||||
|
raise ProtocolError(f"unexpected INFO payload length: {length}")
|
||||||
|
page_size = payload[0]
|
||||||
|
total_size = payload[1] | (payload[2] << 8)
|
||||||
|
return page_size, total_size
|
||||||
|
|
||||||
|
|
||||||
|
def read_chunk(port: serial.Serial, address: int, length: int) -> bytes:
|
||||||
|
payload = bytes([address & 0xFF, (address >> 8) & 0xFF, length])
|
||||||
|
reply = send_command(port, CMD_READ, payload)
|
||||||
|
if reply == REPLY_ERROR:
|
||||||
|
code = read_exact(port, 1)[0]
|
||||||
|
raise ProtocolError(f"READ failed with error code {code}")
|
||||||
|
if reply != REPLY_DATA:
|
||||||
|
raise ProtocolError(f"unexpected READ reply: {reply!r}")
|
||||||
|
returned_length = read_exact(port, 1)[0]
|
||||||
|
if returned_length != length:
|
||||||
|
raise ProtocolError(f"short READ reply: {returned_length} != {length}")
|
||||||
|
return read_exact(port, returned_length)
|
||||||
|
|
||||||
|
|
||||||
|
def write_page(port: serial.Serial, address: int, data: bytes) -> None:
|
||||||
|
if not data or len(data) > 255:
|
||||||
|
raise ValueError("invalid page length")
|
||||||
|
payload = bytes([address & 0xFF, (address >> 8) & 0xFF, len(data)]) + data
|
||||||
|
reply = send_command(port, CMD_WRITE_PAGE, payload)
|
||||||
|
if reply == REPLY_ERROR:
|
||||||
|
code = read_exact(port, 1)[0]
|
||||||
|
raise ProtocolError(f"WRITE failed with error code {code} at 0x{address:04x}")
|
||||||
|
if reply != REPLY_OK:
|
||||||
|
raise ProtocolError(f"unexpected WRITE reply: {reply!r}")
|
||||||
|
|
||||||
|
|
||||||
|
def read_image(port: serial.Serial, size: int, page_size: int) -> bytes:
|
||||||
|
image = bytearray()
|
||||||
|
for address in range(0, size, page_size):
|
||||||
|
chunk_len = min(page_size, size - address)
|
||||||
|
image.extend(read_chunk(port, address, chunk_len))
|
||||||
|
return bytes(image)
|
||||||
|
|
||||||
|
|
||||||
|
def write_image(port: serial.Serial, data: bytes, page_size: int) -> None:
|
||||||
|
for address in range(0, len(data), page_size):
|
||||||
|
page = data[address:address + page_size]
|
||||||
|
write_page(port, address, page)
|
||||||
|
verify = read_chunk(port, address, len(page))
|
||||||
|
if verify != page:
|
||||||
|
raise ProtocolError(f"verify failed at 0x{address:04x}")
|
||||||
|
print(f"wrote and verified page 0x{address:04x}")
|
||||||
|
|
||||||
|
|
||||||
|
def open_port(device: str, baud: int) -> serial.Serial:
|
||||||
|
port = serial.Serial(device, baudrate=baud, timeout=2)
|
||||||
|
time.sleep(2.0)
|
||||||
|
port.reset_input_buffer()
|
||||||
|
port.reset_output_buffer()
|
||||||
|
return port
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="CAT28C256 UART programmer")
|
||||||
|
parser.add_argument("mode", choices=["read", "write"])
|
||||||
|
parser.add_argument("serial_port")
|
||||||
|
parser.add_argument("file", type=Path)
|
||||||
|
parser.add_argument("--baud", type=int, default=115200)
|
||||||
|
parser.add_argument("--size", type=int, default=MAX_SIZE, help="bytes to read in read mode")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
if args.mode == "write":
|
||||||
|
data = args.file.read_bytes()
|
||||||
|
if len(data) > MAX_SIZE:
|
||||||
|
raise SystemExit(f"input file too large: {len(data)} > {MAX_SIZE}")
|
||||||
|
else:
|
||||||
|
if args.size < 1 or args.size > MAX_SIZE:
|
||||||
|
raise SystemExit(f"--size must be between 1 and {MAX_SIZE}")
|
||||||
|
|
||||||
|
with open_port(args.serial_port, args.baud) as port:
|
||||||
|
page_size, total_size = query_info(port)
|
||||||
|
if total_size != MAX_SIZE:
|
||||||
|
raise ProtocolError(f"unexpected EEPROM size reported: {total_size}")
|
||||||
|
if page_size != DEFAULT_PAGE_SIZE:
|
||||||
|
print(f"using device page size {page_size}", file=sys.stderr)
|
||||||
|
|
||||||
|
if args.mode == "read":
|
||||||
|
image = read_image(port, args.size, page_size)
|
||||||
|
args.file.write_bytes(image)
|
||||||
|
print(f"read {len(image)} bytes to {args.file}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
write_image(port, data, page_size)
|
||||||
|
print(f"programmed {len(data)} bytes from {args.file}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except ProtocolError as exc:
|
||||||
|
print(f"error: {exc}", file=sys.stderr)
|
||||||
|
raise SystemExit(1)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:nanoatmega328new]
|
||||||
|
platform = atmelavr
|
||||||
|
board = nanoatmega328new
|
||||||
|
framework = arduino
|
||||||
+333
@@ -0,0 +1,333 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
constexpr uint8_t kShiftDataPin = 2; // PD2
|
||||||
|
constexpr uint8_t kShiftClockPin = 3; // PD3
|
||||||
|
constexpr uint8_t kShiftLatchPin = 4; // PD4
|
||||||
|
constexpr uint8_t kWriteEnablePin = 13; // PB5, active low
|
||||||
|
|
||||||
|
constexpr uint8_t kDataPins[8] = {5, 6, 7, 8, 9, 10, 11, 12};
|
||||||
|
|
||||||
|
constexpr uint16_t kAddressMask = 0x7fff;
|
||||||
|
constexpr uint16_t kOutputDisableBit = 0x8000;
|
||||||
|
constexpr uint16_t kMaxSize = 32768;
|
||||||
|
constexpr uint8_t kPageSize = 64;
|
||||||
|
|
||||||
|
constexpr uint8_t kShiftDataMask = _BV(PD2);
|
||||||
|
constexpr uint8_t kShiftClockMask = _BV(PD3);
|
||||||
|
constexpr uint8_t kShiftLatchMask = _BV(PD4);
|
||||||
|
constexpr uint8_t kWriteEnableMask = _BV(PB5);
|
||||||
|
constexpr uint8_t kDataPortDMask = _BV(PD5) | _BV(PD6) | _BV(PD7);
|
||||||
|
constexpr uint8_t kDataPortBMask = _BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB3) | _BV(PB4);
|
||||||
|
|
||||||
|
constexpr uint8_t kFrameStart = 0x55;
|
||||||
|
constexpr uint8_t kReplyOk = 'K';
|
||||||
|
constexpr uint8_t kReplyData = 'D';
|
||||||
|
constexpr uint8_t kReplyError = 'E';
|
||||||
|
|
||||||
|
enum Command : uint8_t {
|
||||||
|
kCmdInfo = 'I',
|
||||||
|
kCmdRead = 'R',
|
||||||
|
kCmdWritePage = 'W',
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ErrorCode : uint8_t {
|
||||||
|
kErrBadCommand = 1,
|
||||||
|
kErrBadLength = 2,
|
||||||
|
kErrOutOfRange = 3,
|
||||||
|
kErrVerify = 4,
|
||||||
|
kErrProtocol = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t pageBuffer[kPageSize];
|
||||||
|
|
||||||
|
uint16_t makeShiftValue(uint16_t address, bool outputDisable) {
|
||||||
|
return (address & kAddressMask) | (outputDisable ? kOutputDisableBit : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setShiftDataLevel(bool high) {
|
||||||
|
if (high) {
|
||||||
|
PORTD |= kShiftDataMask;
|
||||||
|
} else {
|
||||||
|
PORTD &= ~kShiftDataMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void pulseShiftClock() {
|
||||||
|
PORTD |= kShiftClockMask;
|
||||||
|
PORTD &= ~kShiftClockMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setShiftLatchLevel(bool high) {
|
||||||
|
if (high) {
|
||||||
|
PORTD |= kShiftLatchMask;
|
||||||
|
} else {
|
||||||
|
PORTD &= ~kShiftLatchMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void pulseWriteEnable() {
|
||||||
|
PORTB &= ~kWriteEnableMask;
|
||||||
|
delayMicroseconds(2);
|
||||||
|
PORTB |= kWriteEnableMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeShiftRegisters(uint16_t value) {
|
||||||
|
setShiftLatchLevel(false);
|
||||||
|
for (int8_t bit = 15; bit >= 0; --bit) {
|
||||||
|
setShiftDataLevel((value >> bit) & 0x01);
|
||||||
|
pulseShiftClock();
|
||||||
|
}
|
||||||
|
setShiftLatchLevel(true);
|
||||||
|
setShiftLatchLevel(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAddress(uint16_t address, bool outputDisable) {
|
||||||
|
writeShiftRegisters(makeShiftValue(address, outputDisable));
|
||||||
|
}
|
||||||
|
|
||||||
|
void dataBusMode(uint8_t mode) {
|
||||||
|
for (uint8_t i = 0; i < 8; ++i) {
|
||||||
|
pinMode(kDataPins[i], mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t readDataBus() {
|
||||||
|
uint8_t value = 0;
|
||||||
|
for (uint8_t i = 0; i < 8; ++i) {
|
||||||
|
value |= static_cast<uint8_t>(digitalRead(kDataPins[i])) << i;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeDataBus(uint8_t data) {
|
||||||
|
PORTD = (PORTD & ~kDataPortDMask) | ((data & 0x07) << PD5);
|
||||||
|
PORTB = (PORTB & ~kDataPortBMask) | ((data >> 3) & 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t eepromRead(uint16_t address) {
|
||||||
|
dataBusMode(INPUT);
|
||||||
|
setAddress(address, true);
|
||||||
|
delayMicroseconds(1);
|
||||||
|
setAddress(address, false);
|
||||||
|
delayMicroseconds(1);
|
||||||
|
const uint8_t value = readDataBus();
|
||||||
|
setAddress(address, true);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void beginWriteCycle(uint16_t address, uint8_t data) {
|
||||||
|
setAddress(address, true);
|
||||||
|
dataBusMode(OUTPUT);
|
||||||
|
writeDataBus(data);
|
||||||
|
delayMicroseconds(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void endWriteCycle() {
|
||||||
|
dataBusMode(INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rawWriteCycle(uint16_t address, uint8_t data) {
|
||||||
|
beginWriteCycle(address, data);
|
||||||
|
pulseWriteEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waitWriteComplete(uint16_t address, uint8_t expectedData) {
|
||||||
|
const unsigned long start = millis();
|
||||||
|
while (millis() - start < 20) {
|
||||||
|
if (eepromRead(address) == expectedData) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eepromWritePage(uint16_t address, const uint8_t* data, uint8_t length) {
|
||||||
|
if (length == 0 || length > kPageSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((address / kPageSize) != ((address + length - 1) / kPageSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataBusMode(OUTPUT);
|
||||||
|
for (uint8_t i = 0; i < length; ++i) {
|
||||||
|
setAddress(address + i, true);
|
||||||
|
writeDataBus(data[i]);
|
||||||
|
delayMicroseconds(1);
|
||||||
|
pulseWriteEnable();
|
||||||
|
}
|
||||||
|
endWriteCycle();
|
||||||
|
|
||||||
|
delayMicroseconds(150);
|
||||||
|
return waitWriteComplete(address + length - 1, data[length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableSoftwareDataProtection() {
|
||||||
|
dataBusMode(OUTPUT);
|
||||||
|
rawWriteCycle(0x5555, 0xAA);
|
||||||
|
rawWriteCycle(0x2AAA, 0x55);
|
||||||
|
rawWriteCycle(0x5555, 0x80);
|
||||||
|
rawWriteCycle(0x5555, 0xAA);
|
||||||
|
rawWriteCycle(0x2AAA, 0x55);
|
||||||
|
rawWriteCycle(0x5555, 0x20);
|
||||||
|
endWriteCycle();
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readExact(uint8_t* buffer, size_t length) {
|
||||||
|
const unsigned long start = millis();
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < length) {
|
||||||
|
if (Serial.available() > 0) {
|
||||||
|
buffer[offset++] = static_cast<uint8_t>(Serial.read());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (millis() - start > 1000) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t readByteOrProtocolError(bool* ok) {
|
||||||
|
uint8_t value = 0;
|
||||||
|
*ok = readExact(&value, 1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendOk() {
|
||||||
|
Serial.write(kFrameStart);
|
||||||
|
Serial.write(kReplyOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendError(uint8_t code) {
|
||||||
|
Serial.write(kFrameStart);
|
||||||
|
Serial.write(kReplyError);
|
||||||
|
Serial.write(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendInfo() {
|
||||||
|
Serial.write(kFrameStart);
|
||||||
|
Serial.write(kReplyData);
|
||||||
|
Serial.write(static_cast<uint8_t>(4));
|
||||||
|
Serial.write(static_cast<uint8_t>(kPageSize));
|
||||||
|
Serial.write(static_cast<uint8_t>(kMaxSize & 0xff));
|
||||||
|
Serial.write(static_cast<uint8_t>(kMaxSize >> 8));
|
||||||
|
Serial.write(static_cast<uint8_t>(1)); // protocol version
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleRead() {
|
||||||
|
uint8_t header[3];
|
||||||
|
if (!readExact(header, sizeof(header))) {
|
||||||
|
sendError(kErrProtocol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t address = static_cast<uint16_t>(header[0]) | (static_cast<uint16_t>(header[1]) << 8);
|
||||||
|
const uint8_t length = header[2];
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
sendError(kErrBadLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (static_cast<uint32_t>(address) + length > kMaxSize) {
|
||||||
|
sendError(kErrOutOfRange);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.write(kFrameStart);
|
||||||
|
Serial.write(kReplyData);
|
||||||
|
Serial.write(length);
|
||||||
|
for (uint8_t i = 0; i < length; ++i) {
|
||||||
|
Serial.write(eepromRead(address + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleWritePage() {
|
||||||
|
uint8_t header[3];
|
||||||
|
if (!readExact(header, sizeof(header))) {
|
||||||
|
sendError(kErrProtocol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t address = static_cast<uint16_t>(header[0]) | (static_cast<uint16_t>(header[1]) << 8);
|
||||||
|
const uint8_t length = header[2];
|
||||||
|
|
||||||
|
if (length == 0 || length > kPageSize) {
|
||||||
|
sendError(kErrBadLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (static_cast<uint32_t>(address) + length > kMaxSize) {
|
||||||
|
sendError(kErrOutOfRange);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((address / kPageSize) != ((address + length - 1) / kPageSize)) {
|
||||||
|
sendError(kErrBadLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!readExact(pageBuffer, length)) {
|
||||||
|
sendError(kErrProtocol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!eepromWritePage(address, pageBuffer, length)) {
|
||||||
|
sendError(kErrVerify);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processCommand(uint8_t command) {
|
||||||
|
switch (command) {
|
||||||
|
case kCmdInfo:
|
||||||
|
sendInfo();
|
||||||
|
return;
|
||||||
|
case kCmdRead:
|
||||||
|
handleRead();
|
||||||
|
return;
|
||||||
|
case kCmdWritePage:
|
||||||
|
handleWritePage();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
sendError(kErrBadCommand);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
pinMode(kShiftDataPin, OUTPUT);
|
||||||
|
pinMode(kShiftClockPin, OUTPUT);
|
||||||
|
pinMode(kShiftLatchPin, OUTPUT);
|
||||||
|
pinMode(kWriteEnablePin, OUTPUT);
|
||||||
|
|
||||||
|
digitalWrite(kShiftClockPin, LOW);
|
||||||
|
digitalWrite(kShiftLatchPin, LOW);
|
||||||
|
digitalWrite(kWriteEnablePin, HIGH);
|
||||||
|
|
||||||
|
dataBusMode(INPUT);
|
||||||
|
setAddress(0, true);
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
disableSoftwareDataProtection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (Serial.available() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t start = static_cast<uint8_t>(Serial.read());
|
||||||
|
if (start != kFrameStart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
const uint8_t command = readByteOrProtocolError(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
sendError(kErrProtocol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processCommand(command);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user