initial commit
This commit is contained in:
parent
bedd5f86ef
commit
283076a9ab
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
spi-transfer
|
||||
*build*
|
||||
host/argparse
|
172
arduino/arduino.ino
Normal file
172
arduino/arduino.ino
Normal file
@ -0,0 +1,172 @@
|
||||
#include <SPI.h>
|
||||
#include "api/Compat.h"
|
||||
#include "common.hpp"
|
||||
#include <WiFiNINA.h>
|
||||
|
||||
|
||||
uint8_t buffer[0x8000];
|
||||
const long SERIAL_TIMEOUT = 6000; // ms
|
||||
SPISettings spiSettings(100, MSBFIRST, SPI_MODE0); // max freq [Hz], bit order, mode
|
||||
// lowest frequency seems to be 2000 Hz
|
||||
|
||||
/* void maskedWrite(uint32_t mask, uint32_t bits) { */
|
||||
/* for (uint32_t i = 0; i < 32; i++) { */
|
||||
/* if (mask & (1 << i)) { */
|
||||
/* digitalWrite(i, bits & (1 << i)); */
|
||||
/* } */
|
||||
/* } */
|
||||
/* } */
|
||||
void blinkLED(unsigned n=5, unsigned delay_=200) {
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(delay_);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(delay_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sendErrorMSG(const String& msg1, int i1, const String& msg2="", int i2=-2, int mode=DEC) {
|
||||
Serial.write(ControlBytes::PRINT);
|
||||
Serial.print(msg1);
|
||||
Serial.print(i1, mode);
|
||||
Serial.print(msg2);
|
||||
if (i2 != -2)
|
||||
Serial.print(i2, mode);
|
||||
Serial.println("");
|
||||
}
|
||||
|
||||
|
||||
uint8_t receiveControlByte(const String& fname) {
|
||||
uint8_t recByte;
|
||||
// not using Serial.read() because it doesnt use the set serial timeout
|
||||
if (Serial.readBytes(&recByte, 1) != 1) {
|
||||
Serial.write(ControlBytes::PRINT);
|
||||
Serial.print(fname);
|
||||
Serial.print(": failed to receive control byte");
|
||||
return 0xff;
|
||||
}
|
||||
return recByte;
|
||||
}
|
||||
|
||||
|
||||
buffer_t receiveSize() {
|
||||
// receive size to read or write
|
||||
buffer_t size = 0;
|
||||
for (unsigned i = 0; i < sizeof(buffer_t); i++) {
|
||||
// not using Serial.read() because it doesnt use the set serial timeout
|
||||
uint8_t receivedByte = 0;
|
||||
if (Serial.readBytes(&receivedByte, 1) != 1) {
|
||||
sendErrorMSG("receiveSize: could not read bufferSize from Serial, failed at byte nr. ", i+1);
|
||||
return 0;
|
||||
}
|
||||
size |= (receivedByte << i * 8);
|
||||
}
|
||||
if (size > MAX_BUFFER_SIZE) {
|
||||
sendErrorMSG("Received bufferSize=", size, " is larger than MAX_BUFFER_SIZE=", MAX_BUFFER_SIZE, HEX);
|
||||
return 0;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
void write() {
|
||||
buffer_t size = receiveSize();
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
int receivedBytes = Serial.readBytes(buffer, size);
|
||||
if (receivedBytes != size) {
|
||||
sendErrorMSG("write: Received buffer of size=", receivedBytes, ", expected size=", size, HEX);
|
||||
return;
|
||||
}
|
||||
uint8_t ctrlByte = receiveControlByte("write");
|
||||
if (ctrlByte != ControlBytes::WRITE) {
|
||||
sendErrorMSG("write: Did not receive control byte write: byte=", ctrlByte);
|
||||
return;
|
||||
}
|
||||
/* // debug */
|
||||
/* Serial.write(ControlBytes::PRINT); */
|
||||
/* Serial.print("BufSize="); */
|
||||
/* Serial.print(size, HEX); */
|
||||
/* Serial.print(", buffer="); */
|
||||
/* for (unsigned i = 0; i < size; i++) { */
|
||||
/* Serial.print(buffer[i], HEX); */
|
||||
/* Serial.print(","); */
|
||||
/* } */
|
||||
/* Serial.println("."); */
|
||||
|
||||
SPI.transfer(buffer, size);
|
||||
Serial.write(ControlBytes::WRITE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
pinMode(LEDR, OUTPUT);
|
||||
pinMode(LEDG, OUTPUT);
|
||||
pinMode(LEDB, OUTPUT);
|
||||
digitalWrite(LEDR, LOW);
|
||||
digitalWrite(LEDG, HIGH);
|
||||
digitalWrite(LEDB, HIGH);
|
||||
|
||||
|
||||
Serial.begin(9600);
|
||||
// wait until available
|
||||
/* while (!Serial); { */
|
||||
/* delay(100); */
|
||||
/* } */
|
||||
/* digitalWrite(LED_BUILTIN, LOW); */
|
||||
// empty buffer
|
||||
while (Serial.read() != -1);
|
||||
Serial.setTimeout(SERIAL_TIMEOUT);
|
||||
|
||||
SPI.begin();
|
||||
SPI.beginTransaction(spiSettings);
|
||||
|
||||
digitalWrite(LEDR, LOW);
|
||||
digitalWrite(LEDG, LOW);
|
||||
digitalWrite(LEDB, LOW);
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
if (!Serial) {
|
||||
delay(100);
|
||||
digitalWrite(LEDG, LOW);
|
||||
digitalWrite(LEDB, HIGH);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
digitalWrite(LEDG, HIGH);
|
||||
digitalWrite(LEDB, LOW);
|
||||
}
|
||||
|
||||
int ctrlByte = Serial.read();
|
||||
if (ctrlByte == -1) {
|
||||
delay(100);
|
||||
return;
|
||||
}
|
||||
/* blinkLED(3, 50); */
|
||||
switch(ctrlByte) {
|
||||
case ControlBytes::READY: {
|
||||
Serial.write(ControlBytes::READY);
|
||||
break;
|
||||
}
|
||||
case ControlBytes::WRITE: {
|
||||
digitalWrite(LEDG, LOW);
|
||||
digitalWrite(LEDR, HIGH);
|
||||
write();
|
||||
digitalWrite(LEDR, LOW);
|
||||
digitalWrite(LEDG, HIGH);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
digitalWrite(LEDR, HIGH);
|
||||
digitalWrite(LEDG, LOW);
|
||||
digitalWrite(LEDB, HIGH);
|
||||
sendErrorMSG("loop: received invalid ControlByte: byte=", ctrlByte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
40
arduino/common.hpp
Normal file
40
arduino/common.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifndef NO_ARDUINO
|
||||
#include <Arduino.h>
|
||||
using string_type = String;
|
||||
#else
|
||||
#include <cstdint>
|
||||
using string_type = std::string;
|
||||
#endif
|
||||
|
||||
|
||||
using buffer_t = uint16_t;
|
||||
using address_t = uint32_t;
|
||||
|
||||
const buffer_t MAX_BUFFER_SIZE = 0x8000;
|
||||
|
||||
enum ControlBytes : uint8_t {
|
||||
WRITE = 1,
|
||||
READ = 2,
|
||||
SET_ADDRESS = 3,
|
||||
PRINT = 4,
|
||||
READY = 5,
|
||||
MEM_256KB = 11,
|
||||
MEM_2M = 12,
|
||||
MAX_ENUM,
|
||||
};
|
||||
string_type ControlBytesString(ControlBytes ctrl) {
|
||||
switch (ctrl) {
|
||||
case ControlBytes::WRITE: { return "WRITE"; break; }
|
||||
case ControlBytes::READ: { return "READ"; break; }
|
||||
case ControlBytes::SET_ADDRESS: { return "SET_ADDRESS"; break; }
|
||||
case ControlBytes::PRINT: { return "PRINT"; break; }
|
||||
case ControlBytes::READY: { return "READY"; break; }
|
||||
case ControlBytes::MEM_256KB: { return "MEM_256KB"; break; }
|
||||
case ControlBytes::MEM_2M: { return "MEM_2M"; break; }
|
||||
case ControlBytes::MAX_ENUM: {return "MAX_ENUM"; break; }
|
||||
default: {return "UNKNOWN"; }
|
||||
}
|
||||
}
|
35
arduino/compile_commands.json
Normal file
35
arduino/compile_commands.json
Normal file
@ -0,0 +1,35 @@
|
||||
[
|
||||
{
|
||||
"directory": "/home/matth/Projekte/tool/cpp-ad-eeprom/eeprom",
|
||||
"arguments": [
|
||||
"/home/matth/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-g++",
|
||||
"-c",
|
||||
"-w",
|
||||
"-g3",
|
||||
"@/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/variants/NANO_RP2040_CONNECT/defines.txt",
|
||||
"@/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/variants/NANO_RP2040_CONNECT/cxxflags.txt",
|
||||
"-DARDUINO_ARCH_RP2040",
|
||||
"-MMD",
|
||||
"-mcpu=cortex-m0plus",
|
||||
"-DARDUINO=10607",
|
||||
"-DARDUINO_NANO_RP2040_CONNECT",
|
||||
"-DARDUINO_ARCH_MBED_NANO",
|
||||
"-DARDUINO_ARCH_MBED",
|
||||
"-DARDUINO_LIBRARY_DISCOVERY_PHASE=0",
|
||||
"-I/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/cores/arduino",
|
||||
"-I/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/variants/NANO_RP2040_CONNECT",
|
||||
"-I/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/cores/arduino/api/deprecated",
|
||||
"-I/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/cores/arduino/api/deprecated-avr-comp",
|
||||
"/home/matth/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/arm-none-eabi/include/",
|
||||
"/home/matth/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/arm-none-eabi/include/c++/7.2.1/",
|
||||
"/home/matth/.arduino15/internal/arduino_arm-none-eabi-gcc_7-2017q4_7b7be9f526b2cb64/arm-none-eabi/include/c++/7.2.1",
|
||||
"/home/matth/.arduino15/internal/arduino_arm-none-eabi-gcc_7-2017q4_7b7be9f526b2cb64/arm-none-eabi/include/c++/7.2.1/arm-none-eabi/",
|
||||
"-iprefix/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/cores/arduino",
|
||||
"@/home/matth/.arduino15/packages/arduino/hardware/mbed_nano/4.0.8/variants/NANO_RP2040_CONNECT/includes.txt",
|
||||
"/tmp/arduino/sketches/C40DAEE1A4F3FCAB8238A459D50B91D5/sketch/eeprom.ino.cpp",
|
||||
"-o",
|
||||
"/tmp/arduino/sketches/C40DAEE1A4F3FCAB8238A459D50B91D5/sketch/eeprom.ino.cpp.o"
|
||||
],
|
||||
"file": "/tmp/arduino/sketches/C40DAEE1A4F3FCAB8238A459D50B91D5/sketch/eeprom.ino.cpp"
|
||||
}
|
||||
]
|
8
arduino/sketch.yaml
Normal file
8
arduino/sketch.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
profiles:
|
||||
nanorp:
|
||||
fqbn: arduino:mbed_nano:nanorp2040connect
|
||||
port: /dev/ttyACM0
|
||||
platforms:
|
||||
- platform: arduino:mbed_nano (4.0.2)
|
||||
default_port: /dev/ttyACM0
|
||||
default_fqbn: arduino:mbed_nano:nanorp2040connect
|
3
host/.clangd
Normal file
3
host/.clangd
Normal file
@ -0,0 +1,3 @@
|
||||
CompileFlags: # Tweak the parse settings
|
||||
Add: [-std=c++2a, -Wall, -Wextra, -Wpedantic, -Iargparse/include]
|
||||
# https://clangd.llvm.org/config
|
57
host/Makefile
Normal file
57
host/Makefile
Normal file
@ -0,0 +1,57 @@
|
||||
CXX = /usr/bin/g++
|
||||
CXXFLAGS = -std=c++20 -MMD -MP -Wall -Wpedantic -Wextra
|
||||
LDFLAGS =
|
||||
LDLIBS = -lserial
|
||||
IFLAGS = -Iargparse/include
|
||||
|
||||
|
||||
OBJECT_DIR = ../build
|
||||
EXEC_NAME = spi-transfer
|
||||
EXEC = ../$(EXEC_NAME)
|
||||
|
||||
SRC = $(wildcard *.cpp) $(wildcard */*.cpp)
|
||||
# OBJECTS = $(SRC:%.cpp=$(OBJECT_DIR)/%.o)
|
||||
OBJECTS = $($(notdir SRC):%.cpp=$(OBJECT_DIR)/%.o)
|
||||
DEPENDS = ${OBJECTS:.o=.d}
|
||||
|
||||
CXXFLAGS += $(IFLAGS)
|
||||
|
||||
|
||||
default: $(EXEC)
|
||||
echo $(OBJECTS)
|
||||
|
||||
.PHONY: release install debug run clean
|
||||
release: CXXFLAGS += -O3
|
||||
release : default
|
||||
|
||||
# rule for the executable
|
||||
$(EXEC): $(OBJECT_DIR) $(OBJECTS)
|
||||
$(CXX) $(OBJECTS) -o $@ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
|
||||
# include the makefiles generated by the -M flag
|
||||
-include $(DEPENDS)
|
||||
|
||||
$(OBJECT_DIR)/%.o: %.cpp
|
||||
$(CXX) -c $< -o $@ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
$(OBJECT_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
# Extra Options
|
||||
install:
|
||||
install -D -m 751 $(EXEC) $(DESTDIR)/$(EXEC_NAME)
|
||||
|
||||
|
||||
# with debug flags
|
||||
debug: CXXFLAGS += -g
|
||||
debug: default
|
||||
|
||||
# make with debug flags and run afterwards
|
||||
run: CXXFLAGS += -g
|
||||
run: default
|
||||
./$(EXEC)
|
||||
|
||||
# remove all object and dependecy files
|
||||
clean:
|
||||
-rm -r $(OBJECT_DIR)
|
||||
-rm $(EXEC)
|
||||
|
330
host/main.cpp
Normal file
330
host/main.cpp
Normal file
@ -0,0 +1,330 @@
|
||||
#include <bits/fs_fwd.h>
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <format>
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
#include <serial/serial.h>
|
||||
#include "argparse/argparse.hpp"
|
||||
|
||||
#define NO_ARDUINO
|
||||
#include "../arduino/common.hpp"
|
||||
|
||||
#include <gz-util/string/conversion.hpp>
|
||||
|
||||
static bool stopRequested = false;
|
||||
|
||||
|
||||
// TODO: remove when c++23 is used
|
||||
namespace std {
|
||||
template <typename... Args>
|
||||
inline void println(const std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::cout << std::vformat(fmt.get(), std::make_format_args(args...)) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wrapper for numbers, so that the default get from argparse isnt used for ints, since it does not handle 0x and 0b prefixes
|
||||
*/
|
||||
/* template<std::integral T> */
|
||||
/* class NumberWrapper { */
|
||||
/* public: */
|
||||
/* NumberWrapper() : t(0) {}; */
|
||||
/* NumberWrapper(const std::string& s) { */
|
||||
/* if (s.size() >= 2) { */
|
||||
/* if (s.at(1) == 'x') { */
|
||||
/* t = gz::fromHexString<T>(s); */
|
||||
/* return; */
|
||||
/* } */
|
||||
/* else if (s.at(1) == 'b') { */
|
||||
/* t = gz::fromBinString<T>(s); */
|
||||
/* return; */
|
||||
/* } */
|
||||
/* else if (s.at(1) == 'o') { */
|
||||
/* t = gz::fromOctString<T>(s); */
|
||||
/* return; */
|
||||
/* } */
|
||||
/* } */
|
||||
/* t = gz::fromString<T>(s); */
|
||||
/* } */
|
||||
/* operator T() const { return t; } */
|
||||
/* private: */
|
||||
/* T t; */
|
||||
/* }; */
|
||||
/* // overload the argparse::get, which is used to convert the strings */
|
||||
/* namespace argparse { */
|
||||
/* template<typename T> */
|
||||
/* inline NumberWrapper<T> get(const std::string& v) { return NumberWrapper<T>(v); }; */
|
||||
/* } */
|
||||
|
||||
|
||||
|
||||
// formater for ControlBytes enum
|
||||
template<>
|
||||
struct std::formatter<ControlBytes> : std::formatter<std::string> {
|
||||
template<class FormatContext>
|
||||
auto format(ControlBytes c, FormatContext& fc) const {
|
||||
return std::formatter<std::string>::format(ControlBytesString(c), fc);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ArduinoException : public std::exception {
|
||||
public:
|
||||
ArduinoException(const std::string& message) : message(std::format("Unexpected Arduino behaviour: '{}'", message)) {}
|
||||
virtual const char* what() const noexcept {
|
||||
return message.c_str();
|
||||
}
|
||||
private:
|
||||
const std::string message;
|
||||
};
|
||||
class ConnectionException : public std::exception {
|
||||
public:
|
||||
ConnectionException(const std::string& message) : message(std::format("Connection error: '{}'", message)) {}
|
||||
virtual const char* what() const noexcept {
|
||||
return message.c_str();
|
||||
}
|
||||
private:
|
||||
const std::string message;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read data from a file and print it
|
||||
*/
|
||||
void signalHandler(int signal) {
|
||||
std::println("Caught signal {}, exiting.", signal);
|
||||
stopRequested = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Wait for a response from the Arduino
|
||||
* @details
|
||||
* The response must always start with a ControlByte.
|
||||
* If the ControlByte is invalid, <retry> more bytes are tried.
|
||||
* If the ControlByte is PRINT, the message following the byte is printed and PRINT is returned.
|
||||
* Else returnes the ControlByte
|
||||
*/
|
||||
ControlBytes waitArduino(serial::Serial& s, const std::string& fname) {
|
||||
uint8_t ctrl;
|
||||
while (!stopRequested) {
|
||||
if (s.read(&ctrl, 1) == 0)
|
||||
throw ConnectionException(std::format("{}: did not receive answer from Arduino", fname));
|
||||
/* std::println("waitArduino: Received ctrl: {}", static_cast<ControlBytes>(ctrl)); */
|
||||
switch (ctrl) {
|
||||
case ControlBytes::PRINT: {
|
||||
std::println("Arduino: {}", s.readline());
|
||||
return ControlBytes::PRINT;
|
||||
}
|
||||
case ControlBytes::READ:
|
||||
case ControlBytes::WRITE:
|
||||
case ControlBytes::READY:
|
||||
case ControlBytes::MEM_256KB:
|
||||
case ControlBytes::MEM_2M:
|
||||
case ControlBytes::SET_ADDRESS: {
|
||||
return static_cast<ControlBytes>(ctrl);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
std::println("waitArduino: Received invalid ControlByte: '{}'", ctrl);
|
||||
return ControlBytes::MAX_ENUM;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ControlBytes::MAX_ENUM;
|
||||
}
|
||||
|
||||
|
||||
void sendControlByte(serial::Serial& s, ControlBytes ctrl, const std::string& fname) {
|
||||
uint8_t ctrl8 = static_cast<uint8_t>(ctrl);
|
||||
if (s.write(&ctrl8, 1) != 1) {
|
||||
throw ConnectionException(std::format("{}: could not send {}", fname, ControlBytesString(ctrl)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void receiveControlByte(serial::Serial& s, ControlBytes ctrl, const std::string& fname) {
|
||||
ControlBytes receivedCtrl = waitArduino(s, fname);
|
||||
if (receivedCtrl != ctrl) {
|
||||
std::string err = std::format("{}: did not receive {}", fname, ControlBytesString(ctrl));
|
||||
// SerialException saves the string, no ptr problems here
|
||||
throw ConnectionException(err.data());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a ready? command
|
||||
* @details
|
||||
* 1) Send `READY`
|
||||
* 2) Receive `READY` or throw ArduinoException
|
||||
*/
|
||||
void getReady(serial::Serial& s) {
|
||||
sendControlByte(s, ControlBytes::READY, "getReady");
|
||||
receiveControlByte(s, ControlBytes::READY, "getReady");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a write command and buffer
|
||||
* @details
|
||||
* 1) Send `WRITE` - `buffer size` - `buffer` - `WRITE`
|
||||
* 2) Receive `WRITE` or throw ArduinoException
|
||||
*/
|
||||
void write(serial::Serial& s, std::vector<uint8_t> buffer) {
|
||||
sendControlByte(s, ControlBytes::WRITE, "write(1)");
|
||||
uint8_t cmd;
|
||||
for (unsigned i = 0; i < sizeof(buffer_t); i++) {
|
||||
cmd = (buffer.size() >> i * 8);
|
||||
if (s.write(&cmd, 1) != 1)
|
||||
throw ConnectionException("write: Could not send buffer size");
|
||||
}
|
||||
if (s.write(buffer) != buffer.size())
|
||||
throw ConnectionException("write: Could not send buffer");
|
||||
sendControlByte(s, ControlBytes::WRITE, "write(2)");
|
||||
receiveControlByte(s, ControlBytes::WRITE, "write");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a read command and receive the buffer
|
||||
* @details
|
||||
* 1) Send `READ` - `buffer size` - `READ`
|
||||
* 2) Receive: `READ` - `buffer size` - `buffer` - `READ` or throw ArduinoException
|
||||
*/
|
||||
void read(serial::Serial& s, std::vector<uint8_t>& buffer, buffer_t bufferSize) {
|
||||
sendControlByte(s, ControlBytes::READ, "read(1)");
|
||||
uint8_t cmd;
|
||||
std::println("read: bufferSize={}", bufferSize);
|
||||
for (unsigned i = 0; i < sizeof(buffer_t); i++) {
|
||||
cmd = (bufferSize >> i * 8);
|
||||
/* std::println("read: sending={}", cmd); */
|
||||
if (s.write(&cmd, 1) != 1)
|
||||
throw ConnectionException("read: Could not send buffer size");
|
||||
}
|
||||
sendControlByte(s, ControlBytes::READ, "read(2)");
|
||||
|
||||
receiveControlByte(s, ControlBytes::READ, "read(1)");
|
||||
buffer_t announcedBufferSize = 0;
|
||||
for (unsigned i = 0; i < sizeof(buffer_t); i++) {
|
||||
if (s.read(&cmd, 1) == 0)
|
||||
throw ArduinoException(std::format("read: Could not receive buffer size"));
|
||||
/* std::println("read: received={}", cmd); */
|
||||
announcedBufferSize |= (cmd << i * 8);
|
||||
}
|
||||
if (announcedBufferSize != bufferSize)
|
||||
throw ArduinoException(std::format("read: bufferSize={:#0x}, but announcedBufferSize={:#0x}", bufferSize, announcedBufferSize));
|
||||
|
||||
buffer.clear();
|
||||
size_t receivedBufferSize = s.read(buffer, bufferSize);
|
||||
if (receivedBufferSize != bufferSize)
|
||||
throw ArduinoException(std::format("read: bufferSize={:#0x}, but receivedBufferSize={:#0x}", bufferSize, receivedBufferSize));
|
||||
std::println("read: Received buffer size {}", receivedBufferSize);
|
||||
receiveControlByte(s, ControlBytes::READ, "read(2)");
|
||||
}
|
||||
|
||||
|
||||
void validate(const std::vector<uint8_t>& correctData, const std::vector<uint8_t>& readData, address_t startAddress, bool fileStartsFromZero=false) {
|
||||
size_t correctDataSize = fileStartsFromZero ? correctData.size() - startAddress : correctData.size();
|
||||
unsigned errors = 0;
|
||||
if (correctDataSize != readData.size()) {
|
||||
std::println("validate: Buffers have different sizes. correctData.size={}, readData.size={}", correctDataSize, readData.size());
|
||||
return;
|
||||
}
|
||||
for (unsigned i = 0; i < correctData.size(); i++) {
|
||||
uint8_t correct = correctData.at(i);
|
||||
uint8_t actual = readData.at(i);
|
||||
if (correct != actual) {
|
||||
std::println("validate: address=[0x{:04x} 0b{:015b}]: correct={{0x{:02x} 0b{:08b}}} - actual={{0x{:02x} 0b{:08b}}}", startAddress+i, startAddress+i, correct, correct, actual, actual);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
std::println("validate: {: 4} mismatches found", errors);
|
||||
}
|
||||
|
||||
|
||||
void readFile(const std::string& path, std::vector<uint8_t>& buffer) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
|
||||
// TODO: check if required
|
||||
// Stop eating new lines in binary mode!!!
|
||||
file.unsetf(std::ios::skipws);
|
||||
|
||||
std::streampos fileSize;
|
||||
file.seekg(0, std::ios::end);
|
||||
fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
buffer.clear();
|
||||
buffer.reserve(fileSize);
|
||||
// read the data:
|
||||
buffer.insert(buffer.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
|
||||
}
|
||||
|
||||
|
||||
struct Arguments : public argparse::Args {
|
||||
std::string& filename = kwarg("f,file", "path to a binary file");
|
||||
bool& verbose = flag("verbose", "Also print successful operations");
|
||||
/* bool& write = flag("w,write", "Write data"); */
|
||||
/* bool& read = flag("r,read", "Read data"); */
|
||||
// timeout needs to be high enough for the entire write cycle
|
||||
unsigned& timeout = kwarg("timeout", "Timeout in ms").set_default(5000);
|
||||
std::string& device = kwarg("device", "Path to the serial device (Arduino)").set_default("/dev/ttyACM0");
|
||||
/* virtual void welcome() { */
|
||||
/* std::println("EEEPROM-programmer"); */
|
||||
/* } */
|
||||
};
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
auto args = argparse::parse<Arguments>(argc, argv);
|
||||
if (!fs::exists(fs::path(args.device))) {
|
||||
std::println("Error: device '{}' not found", args.device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!fs::exists(fs::path(args.device))) {
|
||||
std::println("Error: device '{}' not found", args.device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!fs::exists(fs::path(args.filename))) {
|
||||
std::println("Error: file '{}' not found", args.filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// read file
|
||||
std::vector<uint8_t> binFromFile{};
|
||||
if (!args.filename.empty()) {
|
||||
readFile(args.filename, binFromFile);
|
||||
}
|
||||
|
||||
const uint32_t baud_rate = 9600;
|
||||
serial::Serial s(args.device, baud_rate);
|
||||
auto timeout = serial::Timeout::simpleTimeout(args.timeout);
|
||||
s.setTimeout(timeout);
|
||||
s.flush();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
|
||||
try {
|
||||
getReady(s);
|
||||
write(s, binFromFile);
|
||||
}
|
||||
catch (const ArduinoException& e) {
|
||||
std::println("ArduinoException: {}", e.what());
|
||||
for (const auto& s : s.readlines()) {
|
||||
std::println("From Arduino: '{}'", s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
s.close();
|
||||
}
|
BIN
host/print.bin
Normal file
BIN
host/print.bin
Normal file
Binary file not shown.
BIN
host/spi.bin
Normal file
BIN
host/spi.bin
Normal file
Binary file not shown.
@ -1,7 +0,0 @@
|
||||
{
|
||||
"cpu": {
|
||||
"fqbn": "arduino:mbed_nano:nanorp2040connect",
|
||||
"name": "Arduino Nano RP2040 Connect",
|
||||
"port": "serial:///dev/ttyACM0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user