diff --git a/driver/Makefile b/driver/Makefile new file mode 100644 index 0000000..6353eb8 --- /dev/null +++ b/driver/Makefile @@ -0,0 +1,31 @@ +# Name: Makefile +# Project: B15F (board15 Famulus Edition) +# Author: Tristan Krause +# Creation Date: 2019-03-22 + +# Umgebungseinstellungen +COMPILER_PATH = g++ + + +OUTPUT = main +CFLAGS = -std=c++14 -O3 -Wall -Wextra +LDFLAGS = +OBJECTS = main.o drv/usart.o drv/b15f.o drv/plottyfile.o drv/dot.o + +COMPILE = $(COMPILER_PATH) $(CFLAGS) + +B15F: $(OBJECTS) + @echo "Linking..." + $(COMPILE) $(OBJECTS) -o $(OUTPUT) $(LDFLAGS) + +help: + @echo "This Makefile has the following rules:" + @echo "make B15F .... to compile (default)" + @echo "make clean ... to delete objects, elf and hex file" + +clean: + @echo "Cleaning..." + rm -f $(OBJECTS) $(OUTPUT) + +.cpp.o: + $(COMPILE) -c $< -o $@ diff --git a/driver/drv/b15f.cpp b/driver/drv/b15f.cpp new file mode 100644 index 0000000..f53d066 --- /dev/null +++ b/driver/drv/b15f.cpp @@ -0,0 +1,270 @@ +#include "b15f.h" + +B15F* B15F::instance = nullptr; + +B15F::B15F() +{ + init(); +} + +void B15F::init() +{ + + std::string device = exec("bash -c 'ls /dev/ttyUSB*'"); + while(device.find(' ') != std::string::npos || device.find('\n') != std::string::npos || device.find('\t') != std::string::npos) + device.pop_back(); + + if(device.length() == 0) + throw DriverException("Adapter nicht gefunden"); + + std::cout << PRE << "Verwende Adapter: " << device << std::endl; + + + + std::cout << PRE << "Stelle Verbindung mit Adapter her... " << std::flush; + usart.setBaudrate(BAUDRATE); + usart.openDevice(device); + std::cout << "OK" << std::endl; + + + // Temporärer Test + uint8_t block[] = {0, 1, 2, 3}; + while(1) + usart.writeBlock(&block[0], 0, sizeof(block)); + throw std::runtime_error("SCHLUSS"); + + + + std::cout << PRE << "Teste Verbindung... " << std::flush; + uint8_t tries = 3; + while(tries--) + { + // verwerfe Daten, die µC noch hat + discard(); + + if(!testConnection()) + continue; + + if(!testIntConv()) + continue; + + break; + } + if(tries == 0) + throw DriverException("Verbindungstest fehlgeschlagen. Neueste Version im Einsatz?"); + std::cout << "OK" << std::endl; + + // Gib board info aus + std::vector info = getBoardInfo(); + std::cout << PRE << "AVR Firmware Version: " << info[0] << " um " << info[1] << " Uhr (" << info[2] << ")" << std::endl; +} + +void B15F::reconnect() +{ + std::cout << PRE << "Verbindung unterbrochen, stelle Verbindung neu her: " << std::flush; + + uint8_t tries = RECONNECT_TRIES; + while(tries--) + { + delay_ms(RECONNECT_TIMEOUT); + + discard(); + + if(testConnection()) + { + std::cout << "OK" << std::endl << std::flush; + return; + } + + } + + throw DriverException("Verbindung kann nicht repariert werden"); +} + +void B15F::discard(void) +{ + usart.flushOutputBuffer(); + for(uint8_t i = 0; i < 8; i++) + { + usart.writeByte(RQ_DISC); // sende discard Befehl (verwerfe input) + delay_ms((16000 / BAUDRATE) + 1); // warte mindestens eine Millisekunde, gegebenenfalls mehr + } + usart.flushInputBuffer(); +} + +bool B15F::testConnection() +{ + // erzeuge zufälliges Byte + srand(time(NULL)); + uint8_t dummy = rand() % 256; + + usart.writeByte(RQ_TEST); + usart.writeByte(dummy); + + uint8_t aw = usart.readByte(); + uint8_t mirror = usart.readByte(); + + return aw == MSG_OK && mirror == dummy; +} + +bool B15F::testIntConv() +{ + srand(time(NULL)); + uint16_t dummy = rand() % (0xFFFF / 3); + + usart.writeByte(RQ_INT); + usart.writeInt(dummy); + + uint16_t aw = usart.readInt(); + return aw == dummy * 3; +} + + +std::vector B15F::getBoardInfo(void) +{ + std::vector info; + + usart.writeByte(RQ_INFO); + + uint8_t n = usart.readByte(); + while(n--) + { + uint8_t len = usart.readByte(); + std::string str; + + while(len--) + str += static_cast(usart.readByte()); + + info.push_back(str); + } + + uint8_t aw = usart.readByte(); + if(aw != MSG_OK) + throw DriverException("Board Info fehlerhalft: code " + std::to_string((int) aw)); + + return info; +} + +bool B15F::digitalWrite0(uint8_t port) +{ + usart.flushInputBuffer(); + usart.writeByte(RQ_BA0); + usart.writeByte(port); + + uint8_t aw = usart.readByte(); + return aw == MSG_OK; +} + +bool B15F::digitalWrite1(uint8_t port) +{ + usart.flushInputBuffer(); + usart.writeByte(RQ_BA1); + usart.writeByte(port); + + uint8_t aw = usart.readByte(); + return aw == MSG_OK; +} + +uint8_t B15F::digitalRead0() +{ + usart.flushInputBuffer(); + usart.writeByte(RQ_BE0); + return usart.readByte(); +} + +uint8_t B15F::digitalRead1() +{ + usart.flushInputBuffer(); + usart.writeByte(RQ_BE1); + return usart.readByte(); +} + +bool B15F::analogWrite0(uint16_t value) +{ + usart.flushInputBuffer(); + usart.writeByte(RQ_AA0); + usart.writeInt(value); + + uint8_t aw = usart.readByte(); + return aw == MSG_OK; +} + +bool B15F::analogWrite1(uint16_t value) +{ + usart.flushInputBuffer(); + usart.writeByte(RQ_AA1); + usart.writeInt(value); + + uint8_t aw = usart.readByte(); + return aw == MSG_OK; +} + +uint16_t B15F::analogRead(uint8_t channel) +{ + usart.flushInputBuffer(); + usart.writeByte(RQ_ADC); + usart.writeByte(channel); + return usart.readInt(); +} + +bool B15F::analogSequence(uint8_t channel_a, uint16_t* buffer_a, uint32_t offset_a, uint8_t channel_b, uint16_t* buffer_b, uint32_t offset_b, uint16_t start, int16_t delta, uint16_t count) +{ + buffer_a += offset_a; + buffer_b += offset_b; + + usart.flushInputBuffer(); + usart.writeByte(RQ_ADC_DAC_STROKE); + usart.writeByte(channel_a); + usart.writeByte(channel_b); + usart.writeInt(start); + usart.writeInt(static_cast(delta)); + usart.writeInt(count); + uint8_t aw = usart.readByte(); + + if(aw != MSG_OK) + throw DriverException("Mikrocontroller nicht synchron"); + + for(uint16_t i = 0; i < count; i++) + { + buffer_a[i] = usart.readInt(); + buffer_b[i] = usart.readInt(); + } + + aw = usart.readByte(); + if(aw == MSG_OK) + return aw; + + throw DriverException("Sequenz unterbrochen"); +} + +void B15F::delay_ms(uint16_t ms) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +} + +void B15F::delay_us(uint16_t us) +{ + std::this_thread::sleep_for(std::chrono::microseconds(us)); +} + +// https://stackoverflow.com/a/478960 +std::string B15F::exec(std::string cmd) { + std::array buffer; + std::string result; + std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; +} + +B15F& B15F::getInstance(void) +{ + if(!instance) + instance = new B15F(); + + return *instance; +} diff --git a/driver/drv/b15f.h b/driver/drv/b15f.h new file mode 100644 index 0000000..8b1b465 --- /dev/null +++ b/driver/drv/b15f.h @@ -0,0 +1,199 @@ +#ifndef B15F_H +#define B15F_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "usart.h" +#include "driverexception.h" +#include "timeoutexception.h" + +class B15F +{ +private: + // privater Konstruktor + B15F(void); +public: + + /************************************* + * Grundfunktionen des B15F Treibers * + *************************************/ + + /** + * Initialisiert und testet die Verbindung zum B15 + * \throws DriverException + */ + void init(void); + + /** + * Versucht die Verbindung zum B15 wiederherzustellen + * \throws DriverException + */ + void reconnect(void); + + /** + * Verwirft Daten im USART Puffer auf dieser Maschine und B15 + * \throws DriverException + */ + void discard(void); + + /** + * Testet die USART Verbindung auf Funktion + * \throws DriverException + */ + bool testConnection(void); + + /** + * Testet die Integer Konvertierung der USART Verbindung + * \throws DriverException + */ + bool testIntConv(void); + + /** + * Liefert Informationen zur aktuellen Firmware des B15 + * \throws DriverException + */ + std::vector getBoardInfo(void); + + /** + * Lässt den Treiber für eine angegebene Zeit pausieren + * \param ms Verzögerung in Millisekunden + */ + void delay_ms(uint16_t ms); + + /** + * Lässt den Treiber für eine angegebene Zeit pausieren + * \param us Verzögerung in Microsekunden + */ + void delay_us(uint16_t us); + + /** + * Liefert eine Referenz zur aktuellen Treiber-Instanz + * @throws DriverException + */ + static B15F& getInstance(void); + + /** + * Führt ein Befehl auf dieser Maschine aus und liefert stdout zurück + * \param cmd Der Befehl + */ + static std::string exec(std::string cmd); + + /*************************************/ + + + + /************************* + * Steuerbefehle für B15 * + *************************/ + + /** + * Setzt den Wert des digitalen Ausgabeports 0 + * \param port Wert für gesamten Port + * \throws DriverException + */ + bool digitalWrite0(uint8_t); + + /** + * Setzt den Wert des digitalen Ausgabeports 1 + * \param port Wert für gesamten Port + * \throws DriverException + */ + bool digitalWrite1(uint8_t); + + /** + * Liest den Wert des digitalen Eingabeports 0 + * \return Wert für gesamten Port + * \throws DriverException + */ + uint8_t digitalRead0(void); + + /** + * Liest den Wert des digitalen Eingabeports 1 + * \return Wert für gesamten Port + * \throws DriverException + */ + uint8_t digitalRead1(void); + + /** + * Setzt den Wert des Digital-Analog-Converters (DAC / DAU) 0 + * \param port 10-Bit Wert + * \throws DriverException + */ + bool analogWrite0(uint16_t); + + /** + * Setzt den Wert des Digital-Analog-Converters (DAC / DAU) 1 + * \param port 10-Bit Wert + * \throws DriverException + */ + bool analogWrite1(uint16_t); + + /** + * Liest den Wert des Analog-Digital-Converters (ADC / ADU) + * \param channel Kanalwahl von 0 - 7 + * \throws DriverException + */ + uint16_t analogRead(uint8_t channel); + + /** + * \brief Komplexe Analoge Sequenz + * DAC 0 wird auf den Startwert gesetzt und dann schrittweise um Delta inkrementiert. + * Für jeden eingestelleten DAC-Wert werden zwei ADCs (channel_a und channel_b) angesprochen und die Werte übermittelt. + * Die Werte werden in buffer_a für Kanal a und buffer_b für Kanal b gespeichert. + * \param channel_a Auswahl des ADC a, von 0 - 7 + * \param buffer_a Speichertort für Werte des Kanals a + * \param offset_a Anzahl an Werten des Kanals a, die im Speicher übersprungen werden sollen + * \param channel_b Auswahl des ADC b, von 0 - 7 + * \param buffer_b Speichertort für Werte des Kanals b + * \param offset_b Anzahl an Werten des Kanals b, die im Speicher übersprungen werden + * \param start Startwert des DACs + * \param delta Schrittweite, mit welcher der DAC inkrementiert wird + * \param count Anzahl an Inkrementierungen + * \throws DriverException + */ + bool analogSequence(uint8_t channel_a, uint16_t* buffer_a, uint32_t offset_a, uint8_t channel_b, uint16_t* buffer_b, uint32_t offset_b, uint16_t start, int16_t delta, uint16_t count); + + /*************************/ + +private: + USART usart; + + static B15F* instance; + + + // CONSTANTS + const std::string PRE = "[B15F] "; + const std::string SERIAL_DEVICE = "/dev/ttyUSB0"; + constexpr static uint8_t MSG_OK = 0xFF; + constexpr static uint8_t MSG_FAIL = 0xFE; + constexpr static uint16_t RECONNECT_TIMEOUT = 64; // ms + constexpr static uint8_t RECONNECT_TRIES = 3; + constexpr static uint32_t BAUDRATE = 115200; + + // REQUESTS + constexpr static uint8_t RQ_DISC = 0; + constexpr static uint8_t RQ_TEST = 1; + constexpr static uint8_t RQ_INFO = 2; + constexpr static uint8_t RQ_INT = 3; + constexpr static uint8_t RQ_BA0 = 5; + constexpr static uint8_t RQ_BA1 = 6; + constexpr static uint8_t RQ_BE0 = 7; + constexpr static uint8_t RQ_BE1 = 8; + constexpr static uint8_t RQ_AA0 = 9; + constexpr static uint8_t RQ_AA1 = 10; + constexpr static uint8_t RQ_ADC = 11; + constexpr static uint8_t RQ_ADC_DAC_STROKE = 12; +}; + +#endif // B15F_H diff --git a/driver/drv/b15f.o b/driver/drv/b15f.o new file mode 100644 index 0000000..0bd67cc Binary files /dev/null and b/driver/drv/b15f.o differ diff --git a/driver/drv/dot.cpp b/driver/drv/dot.cpp new file mode 100644 index 0000000..29e80fd --- /dev/null +++ b/driver/drv/dot.cpp @@ -0,0 +1,22 @@ +#include "dot.h" + +Dot::Dot(uint16_t x, uint16_t y, uint8_t curve) : x(x), y(y), curve(curve) +{ + if(curve >= 64) + throw std::range_error("Kurvenindex muss im Bereich [0, 63] liegen"); +} + +uint16_t Dot::getX() const +{ + return x; +} + +uint16_t Dot::getY() const +{ + return y; +} + +uint8_t Dot::getCurve(void) const +{ + return curve; +} diff --git a/driver/drv/dot.h b/driver/drv/dot.h new file mode 100644 index 0000000..3f2b7f0 --- /dev/null +++ b/driver/drv/dot.h @@ -0,0 +1,21 @@ +#ifndef DOT_H +#define DOT_H + +#include +#include + +class Dot +{ +public: + Dot(uint16_t x, uint16_t y, uint8_t curve); + uint16_t getX(void) const; + uint16_t getY(void) const; + uint8_t getCurve(void) const; + +private: + uint16_t x, y; + uint8_t curve; +}; + + +#endif // DOT_H diff --git a/driver/drv/dot.h.gch b/driver/drv/dot.h.gch new file mode 100644 index 0000000..2f362f8 Binary files /dev/null and b/driver/drv/dot.h.gch differ diff --git a/driver/drv/dot.o b/driver/drv/dot.o new file mode 100644 index 0000000..dd69b99 Binary files /dev/null and b/driver/drv/dot.o differ diff --git a/driver/drv/driverexception.h b/driver/drv/driverexception.h new file mode 100644 index 0000000..05f8e3e --- /dev/null +++ b/driver/drv/driverexception.h @@ -0,0 +1,32 @@ +#ifndef DRIVEREXCEPTION_H +#define DRIVEREXCEPTION_H + +#include + +// SOURCE: https://stackoverflow.com/a/8152888 + +class DriverException: public std::exception +{ +public: + explicit DriverException(const char* message) : msg_(message) + { + } + + explicit DriverException(const std::string& message) : msg_(message) + { + } + + virtual ~DriverException() throw () + { + } + + virtual const char* what() const throw () + { + return msg_.c_str(); + } + +protected: + std::string msg_; +}; + +#endif // DRIVEREXCEPTION_H diff --git a/driver/drv/plottyfile.cpp b/driver/drv/plottyfile.cpp new file mode 100644 index 0000000..a2d3d5a --- /dev/null +++ b/driver/drv/plottyfile.cpp @@ -0,0 +1,198 @@ +#include "plottyfile.h" + +void PlottyFile::addDot(Dot& dot) +{ + dots.push_back(dot); +} + +void PlottyFile::addDot(Dot dot) +{ + dots.push_back(dot); +} + +void PlottyFile::setFunctionType(FunctionType function_type) +{ + this->function_type = function_type; +} + +void PlottyFile::setQuadrant(uint8_t quadrant) +{ + if(quadrant < 1 || quadrant > 4) + throw std::range_error("Ungueltiger Quadrant"); + this->quadrant = quadrant; +} + +void PlottyFile::setRefX(uint16_t ref_x) +{ + this->ref_x = ref_x; +} + +void PlottyFile::setRefY(uint16_t ref_y) +{ + this->ref_y = ref_y; +} + +void PlottyFile::setParaFirstCurve(uint16_t para_first) +{ + this->para_first = para_first; +} + +void PlottyFile::setParaStepWidth(uint16_t para_stepwidth) +{ + this->para_stepwidth = para_stepwidth; +} + +void PlottyFile::setUnitX(std::string unit_x) +{ + this->unit_x = unit_x; +} + +void PlottyFile::setDescX(std::string desc_x) +{ + this->desc_x = desc_x; +} + +void PlottyFile::setUnitY(std::string unit_y) +{ + this->unit_y = unit_y; +} + +void PlottyFile::setDescY(std::string desc_y) +{ + this->desc_y = desc_y; +} + +void PlottyFile::setUnitPara(std::string unit_para) +{ + this->unit_para = unit_para; +} + +void PlottyFile::setDescPara(std::string desc_para) +{ + this->desc_para = desc_para; +} + +FunctionType PlottyFile::getFunctionType() const +{ + return function_type; +} + +uint8_t PlottyFile::getQuadrant() const +{ + return quadrant; +} + +uint16_t PlottyFile::getRefX() const +{ + return ref_x; +} + +uint16_t PlottyFile::getRefY() const +{ + return ref_y; +} + +uint16_t PlottyFile::getParaFirstCurve() const +{ + return para_first; +} + +uint16_t PlottyFile::getParaStepWidth() const +{ + return para_stepwidth; +} + +std::string PlottyFile::getUnitX() const +{ + return unit_x; +} + +std::string PlottyFile::getDescX() const +{ + return desc_x; +} + +std::string PlottyFile::getUnitY() const +{ + return unit_y; +} + +std::string PlottyFile::getDescY() const +{ + return desc_y; +} + +std::string PlottyFile::getUnitPara() const +{ + return unit_para; +} + +std::string PlottyFile::getDescPara() const +{ + return desc_para; +} + +void PlottyFile::prepStr(std::string& str, uint8_t len) +{ + if(str.length() > len) + throw std::runtime_error("Zu grosser String."); + + if(str.length() != len) + str += '\n'; + + while(str.length() < len) + str += '\0'; +} + +void PlottyFile::writeToFile(std::string filename) +{ + prepStr(unit_x, STR_LEN_SHORT); + prepStr(desc_x, STR_LEN_LARGE); + prepStr(unit_y, STR_LEN_SHORT); + prepStr(desc_y, STR_LEN_LARGE); + prepStr(unit_para, STR_LEN_SHORT); + prepStr(desc_para, STR_LEN_LARGE); + + std::ofstream file(filename); + + // write file header + file.write(reinterpret_cast(&command), 1); + file.write(head.c_str(), head.length()); + file.write(filetype.c_str(), filetype.length()); + file.write(reinterpret_cast(&version), 2); + file.write(reinterpret_cast(&subversion), 2); + file.put(static_cast(function_type)); + file.write(reinterpret_cast(&quadrant), 1); + file.write(reinterpret_cast(&ref_x), 2); + file.write(reinterpret_cast(&ref_y), 2); + file.write(reinterpret_cast(¶_first), 2); + file.write(reinterpret_cast(¶_stepwidth), 2); + file.write(unit_x.c_str(), unit_x.length()); + file.write(desc_x.c_str(), desc_x.length()); + file.write(unit_y.c_str(), unit_y.length()); + file.write(desc_y.c_str(), desc_y.length()); + file.write(unit_para.c_str(), unit_para.length()); + file.write(desc_para.c_str(), desc_para.length()); + file.write(reinterpret_cast(&eof), 1); + + // make sure header size is 256 Byte + while(file.tellp() < 256) + file.put(0); + + for(Dot& dot : dots) + { + file.put((dot.getX() >> 8) | (static_cast(dot.getCurve()) << 2)); + file.put(dot.getX() & 0xFF); + file.put(dot.getY() >> 8); + file.put(dot.getY() & 0xFF); + } + + file.close(); +} + +void PlottyFile::startPlotty(std::string filename) +{ + int code = system(("./plotty --in " + filename).c_str()); + if(code) + throw std::runtime_error("Fehler beim Aufruf von plotty"); +} diff --git a/driver/drv/plottyfile.h b/driver/drv/plottyfile.h new file mode 100644 index 0000000..17b02eb --- /dev/null +++ b/driver/drv/plottyfile.h @@ -0,0 +1,79 @@ +#ifndef PLOTTYFILE_H +#define PLOTTYFILE_H + +#include +#include +#include +#include +#include "dot.h" + +enum FunctionType +{ + CurveFamily = 'S', + Curve = 'C', + Level = 'P' +}; + +class PlottyFile +{ +public: + void addDot(Dot& dot); + void addDot(Dot dot); + + void setFunctionType(FunctionType); + void setQuadrant(uint8_t); + void setRefX(uint16_t); + void setRefY(uint16_t); + void setParaFirstCurve(uint16_t); + void setParaStepWidth(uint16_t); + void setUnitX(std::string); + void setDescX(std::string); + void setUnitY(std::string); + void setDescY(std::string); + void setUnitPara(std::string); + void setDescPara(std::string); + + FunctionType getFunctionType(void) const; + uint8_t getQuadrant(void) const; + uint16_t getRefX(void) const; + uint16_t getRefY(void) const; + uint16_t getParaFirstCurve(void) const; + uint16_t getParaStepWidth(void) const; + std::string getUnitX(void) const; + std::string getDescX(void) const; + std::string getUnitY(void) const; + std::string getDescY(void) const; + std::string getUnitPara(void) const; + std::string getDescPara(void) const; + + void writeToFile(std::string filename); + void startPlotty(std::string filename); +private: + void prepStr(std::string& str, uint8_t len); + + std::vector dots; + + int8_t command = 0x1D; + const std::string head = "HTWK-HWLab"; + const std::string filetype = "MD"; + int16_t version = 1; + int16_t subversion = 0; + FunctionType function_type = FunctionType::Curve; + uint8_t quadrant = 1; + uint16_t ref_x = 1023; + uint16_t ref_y = 1023; + uint16_t para_first = 1; + uint16_t para_stepwidth = 1; + std::string unit_x; + std::string desc_x; + std::string unit_y; + std::string desc_y; + std::string unit_para; + std::string desc_para; + const uint8_t eof = 0xD; + + constexpr static uint8_t STR_LEN_SHORT = 10; + constexpr static uint8_t STR_LEN_LARGE = 20; +}; + +#endif // PLOTTYFILE_H diff --git a/driver/drv/plottyfile.o b/driver/drv/plottyfile.o new file mode 100644 index 0000000..561a627 Binary files /dev/null and b/driver/drv/plottyfile.o differ diff --git a/driver/drv/timeoutexception.h b/driver/drv/timeoutexception.h new file mode 100644 index 0000000..dea32b3 --- /dev/null +++ b/driver/drv/timeoutexception.h @@ -0,0 +1,35 @@ +#ifndef TIMEOUTEXCEPTION_H +#define TIMEOUTEXCEPTION_H + +#include + +// SOURCE: https://stackoverflow.com/a/8152888 + +class TimeoutException: public std::exception +{ +public: + explicit TimeoutException(const char* message, int timeout) : TimeoutException(std::string(message), timeout) + { + } + + explicit TimeoutException(const std::string& message, int timeout) : msg(message), timeout(timeout) + { + if(!msg.length()) + msg = "Timeout reached (" + std::to_string(timeout) + ")"; + } + + virtual ~TimeoutException() throw () + { + } + + virtual const char* what() const throw () + { + return msg.c_str(); + } + +protected: + std::string msg; + int timeout; +}; + +#endif // TIMEOUTEXCEPTION_H diff --git a/driver/drv/usart.cpp b/driver/drv/usart.cpp new file mode 100644 index 0000000..b120c68 --- /dev/null +++ b/driver/drv/usart.cpp @@ -0,0 +1,296 @@ +#include "usart.h" + +void USART::openDevice(std::string device) +{ + file_desc = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + if(file_desc <= 0) + throw USARTException("Fehler beim Öffnen des Gerätes"); + + struct termios options; + int code = tcgetattr(file_desc, &options); + if(code) + throw USARTException("Fehler beim Lesen der Geräteparameter"); + + options.c_cflag = CS8 | CLOCAL | CREAD; + options.c_iflag = IGNPAR; + options.c_oflag = 0; + options.c_lflag = 0; + options.c_cc[VTIME] = timeout; + code = cfsetspeed(&options, baudrate); + if(code) + throw USARTException("Fehler beim Setzen der Baudrate"); + + code = tcsetattr(file_desc, TCSANOW, &options); + if(code) + throw USARTException("Fehler beim Setzen der Geräteparameter"); + + flushOutputBuffer(); + flushInputBuffer(); +} + +void USART::closeDevice() +{ + int code = close(file_desc); + if(code) + throw USARTException("Fehler beim Schließen des Gerätes"); +} + +void USART::flushInputBuffer() +{ + int code = tcflush(file_desc, TCIFLUSH); + if(code) + throw USARTException("Fehler beim Leeren des Eingangspuffers"); +} + +void USART::flushOutputBuffer() +{ + int code = tcflush(file_desc, TCOFLUSH); + if(code) + throw USARTException("Fehler beim Leeren des Ausgangspuffers"); +} + +void USART::writeByte(uint8_t b) +{ + int sent = write(file_desc, &b, 1); + if(sent != 1) + { + std::cout << "WARNUNG: Fehler beim Senden (" << sent << "): writeByte(), wiederhole..." << std::endl; + usleep(100000); + sent = write(file_desc, &b, 1); + if(sent != 1) + throw USARTException("Fehler beim Senden: writeByte()"); + } + +} + +void USART::writeInt(uint16_t d) +{ + int sent = write(file_desc, reinterpret_cast(&d), 2); + if(sent != 2) + throw USARTException("Fehler beim Senden: writeInt()"); +} + + + +int USART::read_timeout(uint8_t* buffer, uint16_t offset, uint8_t len, uint32_t timeout) +{ + uint32_t elapsed = 0; + int n_read = -1; + auto start = std::chrono::steady_clock::now(); + auto end = start; + while(elapsed < timeout) + { + n_read = read(file_desc, buffer + offset, len); + if (n_read == len) + return n_read; + + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start).count(); + } + + return n_read; +} + +int USART::write_timeout(uint8_t* buffer, uint16_t offset, uint8_t len, uint32_t timeout) +{ + uint32_t elapsed = 0; + int n_sent = -1; + auto start = std::chrono::steady_clock::now(); + auto end = start; + while(elapsed < timeout) + { + n_sent = write(file_desc, buffer + offset, len); + if (n_sent == len) + return n_sent; + + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start).count(); + } + + return n_sent; +} + +void USART::writeBlock(uint8_t* buffer, uint16_t offset, uint8_t len) +{ + uint8_t crc; + uint8_t aw; + const uint16_t us_per_bit = (1000000 / baudrate) * 16; + const uint16_t n_total = len + 3; + + do + { + // calc crc + crc = 0; + for(uint8_t i = 0; i < len; i++) + { + crc ^= buffer[i]; + for (uint8_t k = 0; k < 8; k++) + { + if (crc & 1) + crc ^= CRC7_POLY; + crc >>= 1; + } + } + + // construct block + block_buffer[0] = len; + std::memcpy(&block_buffer[1], buffer + offset, len); + block_buffer[len + 1] = crc; + block_buffer[len + 2] = BLOCK_END; + + // send block + int n_sent = write_timeout(&block_buffer[0], 0, len + 3, us_per_bit * n_total); + if(n_sent != n_total) + throw std::runtime_error("fatal (send): " + std::to_string(n_sent)); + flushOutputBuffer(); + + usleep(1000); + + // check response + int n_read = read_timeout(&aw, 0, 1, us_per_bit); + for(uint8_t i = 0; i < 10 && n_read != 1; i++) + { + flushOutputBuffer(); + flushInputBuffer(); + std::cout << "WARNING: read error (" << n_read << "), retry #" << (int) i << std::endl; + usleep(1000000); + n_read = read_timeout(&aw, 0, 1, us_per_bit * 2); + } + + if(n_read == 0) + { + std::cout << "timeout info" << std::endl; + for(uint8_t i = 0; i < MAX_BLOCK_SIZE; i++) + { + writeByte(0x80); // Stoppzeichen für Block + + n_read = read_timeout(&aw, 0, 1, us_per_bit * 2); + if(n_read == 1) + break; + } + } + else if(n_read != 1) + throw std::runtime_error("fatal: " + std::to_string(n_read)); + + flushInputBuffer(); + } + while(aw != 0xFF); + + //std::cout << "OK" << std::endl; +} + +uint8_t USART::readByte(void) +{ + char b; + auto start = std::chrono::steady_clock::now(); + auto end = start; + uint16_t elapsed = 0; + while(elapsed < timeout * 100) + { + int code = read(file_desc, &b, 1); + if (code > 0) + return static_cast(b); + + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start).count(); + } + + throw TimeoutException("Verbindung unterbrochen.", timeout); +} + +uint16_t USART::readInt(void) +{ + return readByte() | readByte() << 8; +} + +bool USART::readBlock(uint8_t* buffer, uint16_t offset) +{ + uint8_t len = readByte(); + uint8_t crc = 0; + buffer += offset; + + uint32_t block_timeout = timeout / 10; + + // wait for block + int n_ready; + uint16_t elapsed = 0; + auto start = std::chrono::steady_clock::now(); + auto end = start; + while(elapsed < block_timeout) + { + int code = ioctl(file_desc, FIONREAD, &n_ready); + if(code != 0) + { + std::cout << "n_ready code: " << code << std::endl; + return false; + } + + if(n_ready >= len + 1) + break; + + end = std::chrono::steady_clock::now(); + elapsed = std::chrono::duration_cast(end - start).count(); + } + if(elapsed >= timeout) + { + std::cout << "block timeout: " << std::endl; + return false; + } + + while(len--) + { + *buffer = readByte(); + + crc ^= *buffer++; + for (uint8_t i = 0; i < 8; i++) + { + if (crc & 1) + crc ^= CRC7_POLY; + crc >>= 1; + } + } + + crc ^= readByte(); + for (uint8_t i = 0; i < 8; i++) + { + if (crc & 1) + crc ^= CRC7_POLY; + crc >>= 1; + } + + if(TEST == 1) + crc = 1; + if(TEST > 100) + TEST = 0; + + if (crc == 0) + { + writeByte(0xFF); + return true; + } + else + { + writeByte(0xFE); + return false; + } +} + +uint32_t USART::getBaudrate() +{ + return baudrate; +} + +uint8_t USART::getTimeout() +{ + return timeout; +} + +void USART::setBaudrate(uint32_t baudrate) +{ + this->baudrate = baudrate; +} + +void USART::setTimeout(uint8_t timeout) +{ + this->timeout = timeout; +} diff --git a/driver/drv/usart.h b/driver/drv/usart.h new file mode 100644 index 0000000..006ffef --- /dev/null +++ b/driver/drv/usart.h @@ -0,0 +1,134 @@ +#ifndef USART_H +#define USART_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "usartexception.h" +#include "timeoutexception.h" + +class USART +{ +public: + + /************************************************* + * Methoden für die Verwaltung der Schnittstelle * + *************************************************/ + + /** + * Öffnet die USART Schnittstelle + * \param device Linux-Gerätepfad + * \throws USARTException + */ + void openDevice(std::string device); + + /** + * Schließt die USART Schnittstelle + * \throws USARTException + */ + void closeDevice(void); + + /** + * Verwirft Daten, die bereits im Puffer liegen, aber noch nicht gelesen wurden + * \throws USARTException + */ + void flushInputBuffer(void); + + /** + * Verwirft Daten, die bereits im Puffer liegen, aber noch nicht gesendet wurden + * \throws USARTException + */ + void flushOutputBuffer(void); + + /*************************************************/ + + + + /************************************* + * Methoden für die Datenübertragung * + *************************************/ + + /** + * Sendet ein Byte über die USART Schnittstelle + * \param b das zu sendende Byte + * \throws USARTException + */ + void writeByte(uint8_t b); + + /** + * Sendet ein Integer über die USART Schnittstelle + * \param b das zu sendende Byte + * \throws USARTException + */ + void writeInt(uint16_t d); + + /** + * Empfängt ein Byte über die USART Schnittstelle + * \throws USARTException + */ + uint8_t readByte(void); + + /** + * Empfängt ein Integer über die USART Schnittstelle + * \throws USARTException + */ + uint16_t readInt(void); + + int read_timeout(uint8_t* buffer, uint16_t offset, uint8_t len, uint32_t timeout); + int write_timeout(uint8_t* buffer, uint16_t offset, uint8_t len, uint32_t timeout); + void writeBlock(uint8_t* buffer, uint16_t offset, uint8_t len); + bool readBlock(uint8_t* buffer, uint16_t offset); + + /*************************************/ + + + + /*************************************** + * Methoden für einstellbare Parameter * + ***************************************/ + + /** + * Liefert die eingestellte Baudrate + * Änderungen werden erst nach einem open() wirksam + */ + uint32_t getBaudrate(void); + + /** + * Liefert den eingestellten Timeout (in Dezisekunden) + * Änderungen werden erst nach einem open() wirksam + */ + uint8_t getTimeout(void); + + /** + * Setzt die Baudrate + * Änderungen werden erst nach einem open() wirksam + */ + void setBaudrate(uint32_t baudrate); + + /** + * Setzt den Timeout (in Dezisekunden) + * Änderungen werden erst nach einem open() wirksam + */ + void setTimeout(uint8_t timeout); + + /***************************************/ + + constexpr static uint8_t CRC7_POLY = 0x91; + constexpr static uint8_t MAX_BLOCK_SIZE = 16; + constexpr static uint8_t BLOCK_END = 0x80; +private: + + int file_desc = -1; // Linux Dateideskriptor + uint32_t baudrate = 9600; + int TEST = 0; + uint8_t timeout = 10; // in Dezisekunden + uint8_t block_buffer[MAX_BLOCK_SIZE + 3]; +}; + + +#endif // USART_H diff --git a/driver/drv/usart.o b/driver/drv/usart.o new file mode 100644 index 0000000..1bc72df Binary files /dev/null and b/driver/drv/usart.o differ diff --git a/driver/drv/usartexception.h b/driver/drv/usartexception.h new file mode 100644 index 0000000..840ce6c --- /dev/null +++ b/driver/drv/usartexception.h @@ -0,0 +1,33 @@ +#ifndef USARTEXCEPTION_H +#define USARTEXCEPTION_H + +#include +#include + +// SOURCE: https://stackoverflow.com/a/8152888 + +class USARTException: public std::exception +{ +public: + explicit USARTException(const char* message) : msg(message) + { + } + + explicit USARTException(const std::string& message) : msg(message) + { + } + + virtual ~USARTException() throw () + { + } + + virtual const char* what() const throw () + { + return msg.c_str(); + } + +protected: + std::string msg; +}; + +#endif // USARTEXCEPTION_H diff --git a/driver/gnuplotscript.gp b/driver/gnuplotscript.gp new file mode 100644 index 0000000..752e2b8 --- /dev/null +++ b/driver/gnuplotscript.gp @@ -0,0 +1,36 @@ +set margin 10,10 +unset key +set grid +set title '' +set xlabel 'U_{GS} [V]' +set ylabel 'I_D [mA]' +set xrange [5:0] +set x2range [5:0] +set y2range [0:50] +set yrange [0:50] +set label at 2,36 'U_{DS} [V] = 300' right +set label at 2,33 'U_{DS} [V] = 325' right +set label at 2,21 'U_{DS} [V] = 350' right +set label at 2,38 'U_{DS} [V] = 375' right +set label at 2,23 'U_{DS} [V] = 400' right +set label at 2,23 'U_{DS} [V] = 425' right +set label at 2,24 'U_{DS} [V] = 450' right +set label at 2,24 'U_{DS} [V] = 475' right +set label at 2,25 'U_{DS} [V] = 500' right +set label at 2,5 'U_{DS} [V] = 525' right +set label at 2,6 'U_{DS} [V] = 550' right +set label at 2,13 'U_{DS} [V] = 575' right +set label at 2,13 'U_{DS} [V] = 600' right +set label at 2,13 'U_{DS} [V] = 625' right +set label at 2,2 'U_{DS} [V] = 650' right +set label at 2,2 'U_{DS} [V] = 675' right +set label at 2,6 'U_{DS} [V] = 700' right +set label at 2,2 'U_{DS} [V] = 725' right +set label at 2,2 'U_{DS} [V] = 725' right +set y2tics +unset ytics +set ytics format '' +unset output +set terminal qt +unset output +plot "/tmp/tempfile1" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 300' w l,"/tmp/tempfile2" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 325' w l,"/tmp/tempfile3" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 350' w l,"/tmp/tempfile4" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 375' w l,"/tmp/tempfile5" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 400' w l,"/tmp/tempfile6" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 425' w l,"/tmp/tempfile7" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 450' w l,"/tmp/tempfile8" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 475' w l,"/tmp/tempfile9" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 500' w l,"/tmp/tempfile10" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 525' w l,"/tmp/tempfile11" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 550' w l,"/tmp/tempfile12" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 575' w l,"/tmp/tempfile13" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 600' w l,"/tmp/tempfile14" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 625' w l,"/tmp/tempfile15" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 650' w l,"/tmp/tempfile16" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 675' w l,"/tmp/tempfile17" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 700' w l,"/tmp/tempfile18" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 725' w l,"/tmp/tempfile19" using ($1*0.004888):($2*0.048876) binary format="%int16%int16" endian=big title 'U_{DS} [V] = 750' w l diff --git a/driver/graph b/driver/graph new file mode 100755 index 0000000..7ca5ed2 Binary files /dev/null and b/driver/graph differ diff --git a/driver/main b/driver/main new file mode 100755 index 0000000..cbb09ce Binary files /dev/null and b/driver/main differ diff --git a/driver/main.cpp b/driver/main.cpp new file mode 100644 index 0000000..40dc56c --- /dev/null +++ b/driver/main.cpp @@ -0,0 +1,145 @@ +#include +#include +#include "drv/usart.h" +#include "drv/b15f.h" +#include "drv/plottyfile.h" + + +void kennlinieErsterQuadrant() +{ + B15F& drv = B15F::getInstance(); + PlottyFile pf; + + uint16_t ba[1024]; + uint16_t bb[1024]; + + const uint16_t sample_count = 100; + const uint16_t delta = 10; + + const uint16_t u_gs_start = 440; + const uint16_t u_gs_delta = 20; + const uint16_t u_gs_end = 600; + + pf.setUnitX("V"); + pf.setUnitY("mA"); + pf.setUnitPara("V"); + pf.setDescX("U_{DS}"); + pf.setDescY("I_D"); + pf.setDescPara("U_{GS}"); + pf.setRefX(5); + pf.setRefY(50); + pf.setParaFirstCurve(u_gs_start); + pf.setParaStepWidth(u_gs_delta); + + uint8_t curve = 0; + for(uint16_t u_gs = u_gs_start; u_gs <= u_gs_end; u_gs += u_gs_delta) + { + drv.analogWrite1(u_gs); + + drv.analogSequence(0, &ba[0], 0, 1, &bb[0], 0, 0, delta, sample_count); + drv.delay_ms(10); + drv.discard(); + drv.delay_ms(10); + + for(uint16_t k = 0; k < sample_count+1; k++) + { + uint16_t i_d = ba[k] - bb[k]; + uint16_t u_ds = bb[k]; + pf.addDot(Dot(u_ds, i_d, curve)); + } + + curve++; + } + + // speichern und plotty starten + pf.writeToFile("test_plot"); + pf.startPlotty("test_plot"); +} + +void kennlinieZweiterQuadrant() +{ + B15F& drv = B15F::getInstance(); + PlottyFile pf; + + uint16_t ba[1024]; + uint16_t bb[1024]; + + const uint16_t sample_count = 1000; + const uint16_t delta = 1; + + const uint16_t u_gs_start = 300; + const uint16_t u_gs_delta = 25; + const uint16_t u_gs_end = 700; + + pf.setQuadrant(2); + pf.setUnitX("V"); + pf.setUnitY("mA"); + pf.setUnitPara("V"); + pf.setDescX("U_{GS}"); + pf.setDescY("I_D"); + pf.setDescPara("U_{DS}"); + pf.setRefX(5); + pf.setRefY(50); + pf.setParaFirstCurve(u_gs_start); + pf.setParaStepWidth(u_gs_delta); + + uint8_t curve = 0; + for(uint16_t u_gs = u_gs_start; u_gs <= u_gs_end; u_gs += u_gs_delta) + { + drv.analogWrite1(u_gs); + + drv.analogSequence(0, &ba[0], 0, 1, &bb[0], 0, 0, delta, sample_count); + + curve = 0; + for(uint16_t k = 0; k < sample_count + 1; k++) + { + if(ba[k] > bb[k] && bb[k] % 50 == 0 && bb[k] > 0) + { + uint16_t i_d = ba[k] - bb[k]; + pf.addDot(Dot(u_gs, i_d, bb[k] / 50)); + } + curve++; + } + std::cout << "u_gs: " << u_gs << std::endl; + } + + // speichern und plotty starten + pf.writeToFile("test_plot"); + pf.startPlotty("test_plot"); +} + +void testFunktionen() +{ + B15F& drv = B15F::getInstance(); + + + drv.digitalWrite0(0xFF); + drv.analogWrite0(128); + std::cout << (int) drv.digitalRead0() << std::endl;; + std::cout << "adc: " << (int) drv.analogRead(4) << std::endl; + + drv.digitalWrite0(0x00); + drv.analogWrite0(0); + std::cout << (int) drv.digitalRead0() << std::endl;; + std::cout << "adc: " << (int) drv.analogRead(4) << std::endl; + + drv.digitalWrite0(0xFF); + drv.analogWrite0(255); + std::cout << (int) drv.digitalRead0() << std::endl; + std::cout << "adc: " << (int) drv.analogRead(4) << std::endl; + + + uint16_t a[1024]; + uint16_t b[1024]; + drv.analogSequence(0, &a[0], 0, 1, &b[0], 0, 0, 1, 1024); + + +} + +int main() +{ + + kennlinieZweiterQuadrant(); + + std::cout << "Schluss." << std::endl; +} diff --git a/driver/main.o b/driver/main.o new file mode 100644 index 0000000..c3c698d Binary files /dev/null and b/driver/main.o differ diff --git a/driver/plotty b/driver/plotty new file mode 100755 index 0000000..042293e Binary files /dev/null and b/driver/plotty differ diff --git a/driver/test_plot b/driver/test_plot new file mode 100644 index 0000000..9dbebdd Binary files /dev/null and b/driver/test_plot differ