Timing im Embedded Projekt



  • Guten Morgen,

    naja, ich hätte gestern vor Feierabend noch ein Gespräch mit dem Zuständigen, wenn es um die CAN-Angelegenheiten geht.
    Wir haben in der API mehrere Klassen, die für das, was ich machen soll, zuständig sind und auch genutzt werden sollen.

    Ich glaube gerade, dass mir damit ein wenig die Hände gebunden sind in der Programmierfreiheit.

    Ich werde also die Klassen der API nehmen müssen.
    Auch werde ich boost nicht nehmen können, weil mein Vorgesetzter meinte, dass alles, was ich brauche die API mit bringt und ich keine externe Library zusätzlich brauche.

    Momentan arbeite ich noch auf dem Firmenrechner auf Windows und nutze erstmal DevC++. das ist ganz in Ordnung. Sobald ich auf dem Embedded Linux arbeite, muss ich mir was überlegen. Muss aber sagen, dass ich bisher immer Probleme mit Eclipse hatte. Eclipse ist meiner Meinung nach eine sehr komplexe und schwierig zu verstehende IDE, aber wenn ich nicht drumrumkomme, werde ich mich der Herausforderung stellen es zu verstehen 😃 😃 😃

    Ich stelle den Client dar und soll die JSON-Strings via Socket an ein anderes System, welches eine andere Abteilung hier programmiert weiterleiten. Diese stellen den Server dar.

    Die Daten, die ich vom CAN bekomme, sind reine binär. Die muss ich auch noch irgendwie umrechnen, um mit denen rechnen zu können.
    Eine Idee ? 😋 😋 😋

    Ich werde mit zwei CAN-Schnittstellen arbeiten müssen.
    Die eine stellt die Daten zur Verfügung, die aller 40ms kommen und ich verarbeiten muss.
    Die andere stellt Daten zur Verfügung, die aller 1s abgegriffen werden sollen und einfach durchgeschleust werden und mit den berechneten daten via Socket versendet werden sollen.



  • Ich glaube gerade, dass mir damit ein wenig die Hände gebunden sind in der Programmierfreiheit.

    Ich werde also die Klassen der API nehmen müssen.

    Und wir dir dann eben nur eingeschränkt helfen können 🙂
    Also können wir eben nur allgemeine Tipps geben 🙂

    Momentan arbeite ich noch auf dem Firmenrechner auf Windows und nutze erstmal DevC++. das ist ganz in Ordnung.

    Um Gottes WIllen !!!

    February 21th 2005 : Dev-C++ 5 Beta 9.2 (4.9.9.2) released !

    Die IDE ist veraltet (oder es gibt nen neuen branch den ich ned kenn ^^)

    Tu dir selber nen gefallen, und nimm was, was zumindest C++11 kann ^^

    Auch werde ich boost nicht nehmen können, weil mein Vorgesetzter meinte, dass alles, was ich brauche die API mit bringt und ich keine externe Library zusätzlich brauche.

    Obwohl nach der Aussage wär ich mir auch ned Sicher, ob da wirklich frei entscheiden ... darfst ^^

    Falls doch, gute Kandidaten sind:

    eclipse + cdt
    netbeans mit c++
    code::blocks (kommt devcpp vielleicht am nächsten)
    QDevelop
    die sollten immer gehen

    falls Geld ausgeben kannst / willst ...
    CLion (cmake basierend)
    Visual C++ (nur auf windows, man kann tortzdem plattformunabhängig und embedded programmieren)
    IntelliJ mit C++

    Oder auf Linux
    Anjuta (fand ich mal ganz ok, aber lange nix mehr gemacht)
    Kdevelop (komm ich z.b. gar ned klar mit, ich versteh die Konzepte da ned ^^)

    am komfortablesten -> Visual Studio
    am generichsten, aka geht fast immer und überall -> eclipse

    Generell empfehl ich immer, die IDE und den Compiler als Konstante aus der Geschichte rauszunehmen, und via buildgnerator das Projekt zu managen.
    cmake, qmake jam sind da die großen Player.
    Da kannst auch unter eclipse z.b. mit dem VS compiler bauen (nur debuggen in eclipse geht ned )
    Aber das sind fortgeschrittene Themen und erstmal solltes den EInstieg ins Projekt kriegen ...

    reicht das, um das timing erfolgreich zu gestalten ?

    Generell kannst das "timing" aka den Pollthread auch selber mit c++11 mitteln schreiben.

    thread erzeugen -> std::thread
    in der thread proc:
    - schleife mit Abbruchbedingung (um den thread koordiniert zu stoppen)
    - Zeit (Performancecounter) nehmen -> std::chrono::high_resolution_clock
    - Abrage ausführen (was zu machen hasst, aka daten holen)
    - Zeit wieder nehmen ....
    - differenz bis 40ms errechnen
    - den thread die errechnete zeit schlafen legen -> std::sleep_until
    - schleife ende

    das sollte langen um "einigermassen" genau pollen zu koennen.
    genauer im userspace ist sowieso schwer möglich, wenn dein system "hängt", wirds eh ungenau, damit muss dein programm also eh klarkommen ...

    Ciao ...

    Ciao ...



  • Hallo,

    DevC++ kann C++11, das hab ich in den Settings eingestellt.

    Es ist für mich momentan das komfortableste, was ich habe.
    Auch wenn ich schon viel darüber gelesen habe, wie schlecht es sein soll ...

    Visual Studio ist zu teuer und CLion hat bei mir nicht funktioniert.

    Mit Eclipse hab ich so meine Schwierigkeiten, aber wenn ich auf dem Linux später bin, kompiliere ich von Hand mit gcc und MakeFile.

    Okay, inzwischen hab ich auch die Infos, die ich brauche, um die CAN Daten zu bekommen.

    ich werde dann mal probieren, ob es klappt und programmieren.

    Bei allgemeinen 😃 😃 Fragen werde ich nochmal auf euch zurückkommen ...

    Aber ihr habt mir dennoch gut geholfen .... Vielen Dank 👍 👍 👍

    EmbSofti 😋 😋 😋

    EDIT:
    wir haben immer so schön mit pthreads gearbeitet in C.
    die funktionieren in C++ nicht oder ???



  • Hallo EmbSofti,

    ich stimme ja im großen ganzen mit dem überein, was RHBaum so schreibt. Mit einer Ausnahme:
    DO NOT POLL!
    mag ja sein, dass es in der embedded Welt Sachen gibt, wo einem nichts anderes übrig bleibt; z.B. um auf einen Statuswechsel an einen digitalen Eingang zu warten. Und mag ja sein, dass man in der embedded Welt allein auf der CPU unterwegs ist, und es daher eh' Wurscht ist.
    meine Empfehlung bleibt: nicht pollen, solange es nur irgendwie geht. Und in einer einer Welt, in der ein 'vernünftiges' Betriebssystem zur Verfügung steht, sollte es immer irgendwie gehen. Und Linux zähle ich dazu, auch wenn ich da noch nie darauf programmiert habe.

    EmbSofti schrieb:

    Ich werde also die Klassen der API nehmen müssen.
    Auch werde ich boost nicht nehmen können, weil mein Vorgesetzter meinte, dass alles, was ich brauche die API mit bringt und ich keine externe Library zusätzlich brauche.

    OK - derartige 'Probleme' sind bekannt. So was gibt es woanders auch - bitte doch Deinen Vorgesetzten gleich mal, dass er Dir ein Beispiel zeigt, wie man mit der firmeneigenen API eine asynchronen write auf einen Socket hin bekommt. Es geht sicher - fragt sich nur wie?

    Ich hatte oben schon erwähnt, dass ich Dir unabhängig von boost oder Firmen-API eine bestimmte Struktur vorschlage.
    Zunächst wäre da eine einheitliche Wartestelle.
    boost:

    asio::io_service io_service; // (fast) fertig
    

    Firmen-API etwa so:

    class Service // auf eine Minimum reduziert
    {
    public:
        typedef std::function< void() > event_type;
        void post( const event_type& ev ); 
        void run(); // in 'run' wird auf 'Ereignisse' gewartet und diese ausgeführt, wenn sie eintreffen
    
    private:
        //## Mutex class
        //## Signal class
        std::queue< event_type > m_queue;
    };
    

    überall da, wo '##' steht, musst Du etwas aus Deiner Firmen-API einfügen.
    Skizze der Implementierung:

    void Service::run()
    {
        for(;;)
        {
            //## warte auf Signal
            // ggf. for-Schleife beenden
            bool events_present = true;
            do
            {
                //## lock Mutex (RAII!) // ich unterstelle, Du weißt, was RAII ist; siehe auch: std::lock_guard<>
                if( m_queue.empty() )
                    events_present = false;
                else
                {
                    auto ev = m_queue.front();
                    m_queue.pop();
                    //## unlock Mutex // Wichtig, da der Funktor selbst wieder ein post machen könnte!
                    ev();
                }
            }
            while( events_present );
        }
    }
    
    void Service::post( const event_type& ev )
    {
        //## lock Mutex (RAII)
        m_queue.push( ev );
        //## notify Signal
    }
    

    Weiter empfehle ich Dir, ein CAN-Telegramm in eine Klasse zu fassen - etwa so in der Form:

    class CanTelegram
    {
    public:
        CanTelegram( long canId, const char* data, unsigned len );
    
        long CanId() const;
        typedef char* iterator;
        friend iterator begin( CanTelegram& tele );
        friend iterator end( CanTelegram& tele );
    };
    

    Dann brauchst Du auch noch jemanden, der sich um alle Daten kümmert und diese sammelt und vielleicht noch die Zusammenstellung des JSON-Strings verantwortet - ich nenne ihn mal Collector:

    class Collector
    {
    public:
        Collector( ipAdresseDesServers );
        void receive1( const CanTelegram& tele ); // wird gerufen falls ein CAN-Telegramm von dem hochfrequenten Kanal einfällt
        void receive40( const CanTelegram& tele ); // wie oben, aber von dem niederfreqenten Kanal
        void onTimer(); // wird jede Sekunde gerufen
        void OnWriteDone(); // wird gerufen, sobald der write auf dem Socket durch ist
    };
    

    Hier spielt die eigentliche Musik Deiner Applikation. Da findet die gesamte Datenhaltung und der Aufbau und das Versenden der JSON-Strings statt.

    Noch eine Funktion, die ausschließlich CAN-Telegramme empfängt:

    void receive_can( CANParamter param, Service& service, std::function< void( CanTelegram ) > sink )
    {
        canOpen/Init( param );  // Bus#, Baudrate, Filter, sonstiges
        for(;;)
        {
            char data[8];
            long canId;
            unsigned len;
            auto err = canRead( &canId, data, &len );
            if( err )
                break;  // irgendeine Fehlerausausgabe
            // -- hier ggf. schon filtern
            CanTelegram tele( canId, data, len );
            service.post( [sink, tele]() { sink( tele ); } ); // alternativ: service.post( std::bind( sink, tele ) );
        }
    }
    

    'sink' ist der Funktor, der ausgeführt wird, wenn ein CAN-Telegramm einfällt.

    Und zum Schluss noch alles zusammen stecken:

    int main()
    {
        Service theService;
        Collector collector( ipServer );
        std::thread can1Thrd( [&collector, &theService]() {  //## hier die Thread-Klasse einsetzen
            receive_can( 0, theService, [&collector]( const CanTelegram& tele ) { collector.receive1( tele ); } ); 
        } );
        std::thread can2Thrd( [&collector, &theService]() { 
            receive_can( 1, theService, [&collector]( const CanTelegram& tele ) { collector.receive40( tele ); } ); 
        } );
        std::thread timerThrd( [&collector, &theService]() {
            for(;;) // ?? Beenden
            {
                //## warte 1s
                theService.post( [&collector]() { collector.onTimer(); } );
            }
        } );
        theService.run();
    
        return 0;
    }
    

    ich gehe mal davon aus, das die Firmen-Thread-Klasse Funktoren aufnehmen kann.
    Hier werden Service und Collector angelegt, zwei Threads gestartet, für die beiden CAN-Schnittstellen. Wenn beide in einem Thread abgearbeitet werden können, dann nimm nur einen. Beide Funktionen posten die CAN-Telegarmme in den Hauptthread genauer zum Collector-Objekt. Ein Timer ruft jede Sekunde 'OnTimer()'. Hier kann dann der asynchrone Write gestartet werden und mit 'WriteDone()' meldet sich dieser zurück.
    Das Beenden der Applikation habe ich mal großzügig weggelassen. Ansonsten fehlen noch die Details zur Socket-Kommunikation.
    .. im Prinzip war's das.

    EmbSofti schrieb:

    Die Daten, die ich vom CAN bekomme, sind reine binär.

    schon klar, bei 8 Byte kann man kein XML-Format erwarten 😉
    Ich hätte nur gerne gewusst, 'was' darin steht. Sind es die Werte eines Temperatursensors? oder hörst Du die Kommunikation auf dem CAN-Bus eines VWs ab?

    EmbSofti schrieb:

    Die muss ich auch noch irgendwie umrechnen, um mit denen rechnen zu können.
    Eine Idee ?

    Ja sicher - vielleicht findest Du es zu abgefahren, aber ich nehme hier tatsächlich Streams her- ein Beispiel zu einem Binärstream findest Du hier. Das ganze Gedöns mit Endian und 2- und 4-Byte Integer oder enums lesen kann man dann darin schön kapseln.

    Gruß
    Werner



  • EmbSofti schrieb:

    Visual Studio ist zu teuer

    Mach dich mal schlau über die Nutzungsbedingungen von der Visual Studio Community Edition -- für kleinere Firmen (Mitarbeiteranzahl, Jahresumsatz) ist die gratis.

    Falls du noch weitere Tips möchtest fehlen mir hier allerdings noch Antworten auf einige Fragen.
    z.B.:

    * Wie genau muss das Timing eingehalten werden?

    * Ist es wichtig die "Sampling-Frequenz" über einen grösseren Zeitraum möglichst genau zu treffen, oder ist immer nur der Abstand zwischen zwei Samples wichtig?
    Die "Sampling-Frequenz" über einen grösseren Zeitraum könnte wichtig sein wenn die Auswertung der Daten die danach erfolgt sich darauf verlässt dass N Samples auch wirklich einer bestimmten Zeitdauer entsprechen. z.B. wenn da Fourier-Transformationen oder ähnliches verwendet werden. Wenn die Daten nur mit einer bestimmten Latenz ausgewertet oder angezeigt werden sollen könnte es allerdings egal sein.

    * Wie kommst du an die Daten dran? Gibt es ne Möglichkeit die Daten über eine Callback-Funktion reinzubekommen ("OnXxxReceived"), oder musst du sie aktiv abfragen?

    * Falls aktiv abfragen: wie lange dauert so eine Abfrage inetwa? Und läuft sie synchron oder asynchron?

    (Bis hierher alles jeweils 1x pro "Daten-Typ", also 1x für die 40ms Sache und 1x für die 1s Sache.)

    * Wie viele Cores hat die Zielplattform?

    * Wie viel RAM/Rechenpower hat die Zielplattform, und was soll/muss da noch drauf laufen?

    EmbSofti schrieb:

    EDIT:
    wir haben immer so schön mit pthreads gearbeitet in C.
    die funktionieren in C++ nicht oder ???

    pthreads kannst du mit C++ genau so gut oder schlecht verwenden wie mit C.
    Ich persönlich würde aber eher die Threading-Klassen der Standard-Library verwenden. Speziell wenn Cross-Plattform entwickelt/getestet/deployed werden soll (und eine dieser Plattformen Windows ist).
    Und vom Semaphoren würde ich persönlich die Finger lassen - Mutexen und Condition-Variablen sind i.A. alles was man braucht.



  • Hallo Werner Salomon,

    danke für die Tipps. schau ich mir gleich in ruhe an 😉

    Hallo hustbaer,

    Visual Studio wird zu teuer, wir sind eine riesengroße Firma.
    Außerdem programmieren wir ausschließlich auf Linux.
    Nur für die ersten Versuchen, wo ich die Hardware noch nicht brauche, bin ich auf Win

    die Daten werden in jeweiligen Intervallen zur Verfügung gestellt.
    ich kann mir die Daten über Signals abgreifen.
    muss diese natürlich dann aussortieren, denn die meisten kommen öfter,
    als ich sie brauche

    ich habe ARM9 prozessor mit 450 MIPS
    128 MB RAM und 512 MB Flash

    Es wird kein Cross Platform genutzt.
    Es ist reines Linux und ich habe unter Linux oft mit Pthreads gearbeitet und habe gute Erfahrung wenigstens etwas Ahnung davon.

    Alles andere ist Neuland für mich und es muss bis erste Nov-woche fertig sein



  • EmbSofti schrieb:

    Alles andere ist Neuland für mich und es muss bis erste Nov-woche fertig sein

    Muss es nicht, denn sonst hätte man jemanden beauftragt, der sich mit dem Thema auskennt.



  • ich werd auch mit meinem chef sprechen müssen.

    das projekt liegt schon ne weile da und jetzt geraten die unter zeitdruck, weil es im november im fahrzeug getestet werden soll

    und ich hab es abbekommen, weil ich neu bin und mein eigentliches projekt noch ned richtig anläuft



  • Hallo Leute,

    ich habe folgendes entdeckt und find es passend, um die ms abzuwarten.

    http://codereview.stackexchange.com/questions/40915/simple-multithread-timer

    jetzt hab ich aber ein Problem.
    ich will, dass ein thread beginnt und aller 40ms das ausführt, was ich reinschreibe.

    allerdings will ich, dass er nach 1s damit aufhört.

    ich kann jetzt natürlich einen 2. timer thread machen, der aller 40ms cnt abfragt, welches ich im ersten thread immer erhöhen lasse.

    aber wie komme ich aus dem zweiten thread wieder raus ???
    der endet in einer endlosschleife 😞 😞 😞

    #include <iostream>
    #include "Timer.h"
    
    using namespace std;
    
    int cnt = 0;
    
    /* Code */
    
    void counter(){
    
    	cnt = cnt + 40;
    	cout << "Counter steht bei " << cnt << endl;
    
    }
    
    int main (void) {
    
        Timer tHello([]()
        {
        	counter();
        });
    
        tHello.setSingleShot(false);
        tHello.setInterval(Timer::Interval(40));
        tHello.start(true);		
    
        Timer tStop([&]()
        {
            if (cnt == 1000)
        	    tHello.stop();
        });
    
        tStop.setSingleShot(false);
        tStop.setInterval(Timer::Interval(40));
        tStop.start();
    
        cout << "Beide Threads wurden beendet !!" << endl;
    
        return 0;
    }
    


  • Wieso nimmst du nicht einfach std::thread und std::this_thread::sleep_for ?
    Bzw. wenn du die Möglichkeit brauchst den Thread kontrolliert zu beenden ohne auf den nächsten 40ms Tick warten zu müssen dann halt std::condition_variable_any::wait_for statt std::this_thread::sleep_for .

    EmbSofti schrieb:

    allerdings will ich, dass er nach 1s damit aufhört.

    Wieso?
    Du kannst doch das Abspeichern der 40ms Daten, das Abspeichern der 1s Daten, das Schnüren des Gesamtpakets und das Versenden alles im selben Thread machen.

    Also

    Thread-Function:
        $message = new message
    
        while not shutdown:
            wait for next 40ms tick or shutdown
            if shutdown:
                break
    
            $d40 = fetch current 40ms data
            append $d40 to $message
    
            if one second since last message was sent:
                $d1000 = fetch current 1s data
                append $d1000 to $message
                send $message
                $message = new message
    

    Wobei send $message halt alles beinhaltet was zum Senden der Nachricht erforderlich ist, also z.B. auch die JSON Formatierung.
    Falls das zu lange dauern sollte (z.B. weil das Senden der Nachricht synchron erfolgt) kannst du ja noch einen eigenen Sende-Thread machen, und die Daten aus dem Erfassungs-Thread nur an den Sende-Thread übergeben.

    Oder du machst es vom Prinzip her so wie Werner Salomon es schon vorgeschlagen hat: jedes mal wenn Daten reinkommen pflegst du diese in ein Objekt ein das die Daten für die nächste Nachricht "sammelt".

    D.h. beim Einpflegen der 40ms Daten musst du halt gucken ob du den neuesten 40ms Datenblock im "Sammler-Objekt" ("Collector" in Werners Beitrag) überschreibst (weil noch nicht genug Zeit vergangen ist) oder einen neuen Datenblock anhängst.
    Beim Einpflegen der 1s Daten im Prinzip das selbe.
    Dann kannst du jederzeit aus den Daten im Collector einen JSON Schnippel bauen und verschicken, da der Collector immer die passenden Daten bereithält.
    Und genau das machst du dann in einem Thread periodisch jede Sekunde.
    Die Zugriffe auf den Collector musst du dabei natürlich über z.B. ne Mutex synchronisieren, was aber halbwegs trivial ist.



  • hi Leute,

    ich hab inzwischen bissel programmieren können.

    die Firmen API stellt alles zur Verfügung,
    die haben da ne eigene art alles abzuholen 🙄

    wie sende ich jetzt ein JSON-string via Socket ??? 😕

    ich hab schon viel von client-server gehört und irgendwann im studium damit berührung gehabt.
    Nur gibt es hier nicht den Server im eigentlichen Sinne.

    Ich hab meine Software auf der Hardware, also mein Programm.
    Ein anderes Programm auf dieser HW will die Daten aller 1s haben,
    als JSON String, weil die alles in Java programmieren.

    für JSON hab ich folgendes aus einem anderen Thread von mir:

    https://www.c-plusplus.net/forum/p2470836#2470836

    aber wie sende ich einen socket, wenn ich keinen server habe.
    ich soll die daten als JSON-string über http an einen bestimmten port schicken und die anderen holen sich den dort ab.
    Oder stellen die anderen dadurch, dass sie auf den port hören, den server da ??? mmh .... 😕

    Ich hoffe, meine Fragen sind ned allzu dumm und ihr könnt mir helfen 😃
    Bin schließlich frisch von der HS und hab noch viel Praxis zu lernen 🙄



  • EmbSofti schrieb:

    Oder stellen die anderen dadurch, dass sie auf den port hören, den server da ??? mmh .... 😕

    Programme die nen "listening" Port haben werden oft als Server bezeichnet, ja.



  • 😃 😃 ja stimmt, die Frage war etwas .... naja 😃 😃

    dann muss ich mich einfach nochmal durch so eine Anleitung quälen und schauen, ob ich es hinbekomme. :p


Anmelden zum Antworten