driver rename

This commit is contained in:
devfix 2019-05-16 09:36:20 +02:00
parent 7a1efabe49
commit 281a00ddc0
35 changed files with 0 additions and 0 deletions

322
control/src/drv/b15f.cpp Normal file
View file

@ -0,0 +1,322 @@
#include "b15f.h"
B15F* B15F::instance = nullptr;
errorhandler_t B15F::errorhandler = 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)
abort("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;
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)
abort("Verbindungstest fehlgeschlagen. Neueste Version im Einsatz?");
std::cout << "OK" << std::endl;
// Gib board info aus
std::vector<std::string> info = getBoardInfo();
std::cout << PRE << "AVR Firmware Version: " << info[0] << " um " << info[1] << " Uhr (" << info[2] << ")" << std::endl;
}
void B15F::reconnect()
{
uint8_t tries = RECONNECT_TRIES;
while(tries--)
{
delay_ms(RECONNECT_TIMEOUT);
discard();
if(testConnection())
return;
}
abort("Verbindung kann nicht repariert werden");
}
void B15F::discard(void)
{
try
{
usart.clearOutputBuffer();
for(uint8_t i = 0; i < 16; i++)
{
usart.writeByte(RQ_DISC); // sende discard Befehl (verwerfe input)
delay_ms(4);
}
usart.clearInputBuffer();
}
catch(std::exception& ex)
{
abort(ex);
}
}
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<std::string> B15F::getBoardInfo(void)
{
std::vector<std::string> 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<char>(usart.readByte());
}
info.push_back(str);
}
uint8_t aw = usart.readByte();
if(aw != MSG_OK)
abort("Board Info fehlerhalft: code " + std::to_string((int) aw));
return info;
}
bool B15F::activateSelfTestMode()
{
usart.writeByte(RQ_ST);
uint8_t aw = usart.readByte();
return aw == MSG_OK;
}
bool B15F::digitalWrite0(uint8_t port)
{
usart.writeByte(RQ_BA0);
usart.writeByte(port);
uint8_t aw = usart.readByte();
delay_us(10);
return aw == MSG_OK;
}
bool B15F::digitalWrite1(uint8_t port)
{
usart.writeByte(RQ_BA1);
usart.writeByte(port);
uint8_t aw = usart.readByte();
delay_us(10);
return aw == MSG_OK;
}
uint8_t B15F::digitalRead0()
{
usart.clearInputBuffer();
usart.writeByte(RQ_BE0);
uint8_t byte = usart.readByte();
delay_us(10);
return byte;
}
uint8_t B15F::digitalRead1()
{
usart.clearInputBuffer();
usart.writeByte(RQ_BE1);
uint8_t byte = usart.readByte();
delay_us(10);
return byte;
}
uint8_t B15F::readDipSwitch()
{
usart.clearInputBuffer();
usart.writeByte(RQ_DSW);
uint8_t byte = usart.readByte();
delay_us(10);
return byte;
}
bool B15F::analogWrite0(uint16_t value)
{
usart.writeByte(RQ_AA0);
usart.writeInt(value);
uint8_t aw = usart.readByte();
delay_us(10);
return aw == MSG_OK;
}
bool B15F::analogWrite1(uint16_t value)
{
usart.writeByte(RQ_AA1);
usart.writeInt(value);
uint8_t aw = usart.readByte();
delay_us(10);
return aw == MSG_OK;
}
uint16_t B15F::analogRead(uint8_t channel)
{
usart.clearInputBuffer();
if(channel > 7)
abort("Bad ADC channel: " + std::to_string(channel));
uint8_t rq[] = {
RQ_ADC,
channel
};
int n_sent = usart.write_timeout(&rq[0], 0, sizeof(rq), 1000);
if(n_sent != sizeof(rq))
abort("Sent failed");
uint16_t adc = usart.readInt();
if(adc > 1023)
abort("Bad ADC data detected (1)");
return adc;
}
void 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.clearInputBuffer();
usart.writeByte(RQ_ADC_DAC_STROKE);
usart.writeByte(channel_a);
usart.writeByte(channel_b);
usart.writeInt(start);
usart.writeInt(static_cast<uint16_t>(delta));
usart.writeInt(count);
for(uint16_t i = 0; i < count; i++)
{
buffer_a[i] = usart.readInt();
buffer_b[i] = usart.readInt();
if(buffer_a[i] > 1023 || buffer_b[i] > 1023)
abort("Bad ADC data detected (2)");
}
uint8_t aw = usart.readByte();
if(aw != MSG_OK)
abort("Sequenz unterbrochen");
delay_us(10);
}
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));
}
B15F& B15F::getInstance(void)
{
if(!instance)
instance = new B15F();
return *instance;
}
// https://stackoverflow.com/a/478960
std::string B15F::exec(std::string cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> 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;
}
void B15F::abort(std::string msg)
{
DriverException ex(msg);
abort(ex);
}
void B15F::abort(std::exception& ex)
{
if(errorhandler)
errorhandler(ex);
else
{
std::cerr << "NOTICE: B15F::errorhandler not set" << std::endl;
std::cout << ex.what() << std::endl;
throw DriverException(ex.what());
}
}
void B15F::setAbortHandler(errorhandler_t func)
{
errorhandler = func;
}

237
control/src/drv/b15f.h Normal file
View file

@ -0,0 +1,237 @@
#ifndef B15F_H
#define B15F_H
#include <iostream>
#include <bits/stdc++.h>
#include <string>
#include <fstream>
#include <cstdlib>
#include <chrono>
#include <cstdint>
#include <vector>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "usart.h"
#include "driverexception.h"
#include "timeoutexception.h"
typedef std::function<void(std::exception&)> errorhandler_t;
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<std::string> 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);
/**
* Multithread sicherer Abbruch des B15F-Treibers
* \param msg Beschreibung der Abbruchursache
*/
static void abort(std::string msg);
/**
* Multithread sicherer Abbruch des B15F-Treibers
* \param ex Exception als Abbruchursache
*/
static void abort(std::exception& ex);
/**
* Setzt eine Fehlerbehandlungsroutine für den Treiberabbruch (abort)
* \param func Funktion, die Exception als Parameter bekommt
*/
static void setAbortHandler(errorhandler_t func);
/*************************************/
/*************************
* Steuerbefehle für B15 *
*************************/
/**
* Versetzt das Board in den Selbsttest-Modus
* WICHTIG: Es darf dabei nichts an den Klemmen angeschlossen sein!
* \throws DriverException
*/
bool activateSelfTestMode(void);
/**
* 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);
/**
* Liest den Wert des digitalen Eingabeports, an dem der DIP-switch angeschlossen ist (S7)
* \return Wert für gesamten Port
* \throws DriverException
*/
uint8_t readDipSwitch(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
*/
void 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);
/*************************/
// CONSTANTS
const std::string PRE = "[B15F] ";
constexpr static uint8_t MSG_OK = 0xFF;
constexpr static uint8_t MSG_FAIL = 0xFE;
constexpr static uint16_t RECONNECT_TIMEOUT = 64; // ms
constexpr static uint16_t WDT_TIMEOUT = 15; // ms
constexpr static uint8_t RECONNECT_TRIES = 3;
constexpr static uint32_t BAUDRATE = 57600;
private:
USART usart;
static B15F* instance;
static errorhandler_t errorhandler;
// 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_ST = 4;
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_DSW = 9;
constexpr static uint8_t RQ_AA0 = 10;
constexpr static uint8_t RQ_AA1 = 11;
constexpr static uint8_t RQ_ADC = 12;
constexpr static uint8_t RQ_ADC_DAC_STROKE = 13;
};
#endif // B15F_H

22
control/src/drv/dot.cpp Normal file
View file

@ -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;
}

21
control/src/drv/dot.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef DOT_H
#define DOT_H
#include <cstdint>
#include <stdexcept>
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

View file

@ -0,0 +1,33 @@
#ifndef DRIVEREXCEPTION_H
#define DRIVEREXCEPTION_H
#include <exception>
// 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

View file

@ -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<char*>(&command), 1);
file.write(head.c_str(), head.length());
file.write(filetype.c_str(), filetype.length());
file.write(reinterpret_cast<char*>(&version), 2);
file.write(reinterpret_cast<char*>(&subversion), 2);
file.put(static_cast<uint8_t>(function_type));
file.write(reinterpret_cast<char*>(&quadrant), 1);
file.write(reinterpret_cast<char*>(&ref_x), 2);
file.write(reinterpret_cast<char*>(&ref_y), 2);
file.write(reinterpret_cast<char*>(&para_first), 2);
file.write(reinterpret_cast<char*>(&para_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<const char*>(&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<uint8_t>(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");
}

View file

@ -0,0 +1,79 @@
#ifndef PLOTTYFILE_H
#define PLOTTYFILE_H
#include <iostream>
#include <fstream>
#include <exception>
#include <vector>
#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<Dot> 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

View file

@ -0,0 +1,35 @@
#ifndef TIMEOUTEXCEPTION_H
#define TIMEOUTEXCEPTION_H
#include <exception>
// 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), m_timeout(timeout)
{
if(!msg.length())
msg = "Timeout reached (" + std::to_string(m_timeout) + ")";
}
virtual ~TimeoutException() throw ()
{
}
virtual const char* what() const throw ()
{
return msg.c_str();
}
protected:
std::string msg;
int m_timeout;
};
#endif // TIMEOUTEXCEPTION_H

324
control/src/drv/usart.cpp Normal file
View file

@ -0,0 +1,324 @@
#include "usart.h"
void USART::openDevice(std::string device)
{
file_desc = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY /* | O_NONBLOCK*/);
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[VMIN] = 0; // #bytes read returns at least
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");
clearOutputBuffer();
clearInputBuffer();
}
void USART::closeDevice()
{
int code = close(file_desc);
if(code)
throw USARTException("Fehler beim Schließen des Gerätes");
}
void USART::clearInputBuffer()
{
int code = tcflush(file_desc, TCIFLUSH);
if(code)
throw USARTException("Fehler beim Leeren des Eingangspuffers");
}
void USART::clearOutputBuffer()
{
int code = tcflush(file_desc, TCOFLUSH);
if(code)
throw USARTException("Fehler beim Leeren des Ausgangspuffers");
}
void USART::flushOutputBuffer()
{
int code = tcdrain(file_desc);
if(code)
throw USARTException("Fehler beim Versenden des Ausgangspuffers");
}
void USART::printStatistics()
{
double pz = 1e2 * n_blocks_failed / n_blocks_total;
pz = std::round(pz * 1e2) / 1e2;
std::cout << "blocks total: " << n_blocks_total << " ok: " << (n_blocks_total - n_blocks_failed) << " failed: " << n_blocks_failed << " (" << pz << "%)" << std::endl;
}
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<char*>(&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<std::chrono::microseconds>(end - start).count();
}
return 0;
}
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);
flushOutputBuffer();
if (n_sent == len)
return n_sent;
end = std::chrono::steady_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::microseconds>(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;
n_blocks_total++;
bool failed = false;
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
clearOutputBuffer();
clearInputBuffer();
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));
/*for(uint8_t i = 0; i < len + 3; i++)
{
write_timeout(&block_buffer[i], 0, 1, us_per_bit * n_total);
//tcdrain(file_desc);
//usleep(1000);
}*/
// flush output data
tcdrain(file_desc);
//usleep(us_per_bit * n_total * 10);
// check response
int n_read = read_timeout(&aw, 0, 1, us_per_bit * n_blocks_total * 10);
for(uint16_t i = 0; i < 255 && n_read != 1; i++)
{
writeByte(0x80); // Stoppzeichen für Block
if(tcdrain(file_desc))
{
std::cout << "drain failed" << std::endl;
}
std::cout << "WARNING: read error (" << n_read << "), retry #" << (int) i << std::endl;
usleep(us_per_bit*100);
n_read = read_timeout(&aw, 0, 1, us_per_bit);
}
if(n_read != 1)
throw std::runtime_error("fatal: " + std::to_string(n_read));
//clearInputBuffer();
if(aw != 0xFF) {
if(!failed)
n_blocks_failed++;
failed = true;
std::cout << "block failed, retry" << std::endl;
}
}
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<uint8_t>(b);
end = std::chrono::steady_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(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<std::chrono::milliseconds>(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;
}

150
control/src/drv/usart.h Normal file
View file

@ -0,0 +1,150 @@
#ifndef USART_H
#define USART_H
#include <iostream>
#include <cstdint>
#include <chrono>
#include <unistd.h>
#include <cstring>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <cmath>
#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 clearInputBuffer(void);
/**
* Verwirft Daten, die bereits im Puffer liegen, aber noch nicht gesendet wurden
* \throws USARTException
*/
void clearOutputBuffer(void);
/**
* Schreibt Daten, die bereits im Puffer liegen, aber noch nicht gesendet wurden
* \throws USARTException
*/
void flushOutputBuffer(void);
/**
* Gibt Anzahl an erfolgreichen und fehlgeschlagenen Block-Übertragungen an
*/
void printStatistics(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
* <b>Änderungen werden erst nach einem open() wirksam</b>
*/
uint32_t getBaudrate(void);
/**
* Liefert den eingestellten Timeout (in Dezisekunden)
* <b>Änderungen werden erst nach einem open() wirksam</b>
*/
uint8_t getTimeout(void);
/**
* Setzt die Baudrate
* <b>Änderungen werden erst nach einem open() wirksam</b>
*/
void setBaudrate(uint32_t baudrate);
/**
* Setzt den Timeout (in Dezisekunden)
* <b>Änderungen werden erst nach einem open() wirksam</b>
*/
void setTimeout(uint8_t timeout);
/***************************************/
constexpr static uint8_t CRC7_POLY = 0x91;
constexpr static uint8_t MAX_BLOCK_SIZE = 64;
constexpr static uint8_t BLOCK_END = 0x80;
private:
int file_desc = -1; // Linux Dateideskriptor
uint32_t baudrate = 9600; // Standard-Baudrate, sollte mit setBaudrate() überschrieben werden!
int TEST = 0;
uint8_t timeout = 10; // in Dezisekunden
uint8_t block_buffer[MAX_BLOCK_SIZE + 3];
// debug statistics
uint32_t n_blocks_total = 0;
uint32_t n_blocks_failed = 0;
};
#endif // USART_H

View file

@ -0,0 +1,33 @@
#ifndef USARTEXCEPTION_H
#define USARTEXCEPTION_H
#include <exception>
#include <string>
// 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