Forum: Compiler & IDEs gcc auf dem pc ausführen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Karl K. (leluno)


Lesenswert?

hallo,

ich arbeite an einer Steuerung für einen Wechselrichter. Daten per 
modbus einlesen, Regelung berechne, regeln mit atmega 328 nano.

Ich würde gerne einen Teil der c-code-Entwicklung auf den pc auslagern - 
am liebsten mit c#, weil das debugging und testen des codes etwas 
umständlich ist. Messen und Regeln per modbus geht genau so gut auf dem 
ps wie auf dem nano.

Problem ist eigentlich nur die unterschiedliche Variablen-Definition. 
Wenn es in c# #defines gäbe wäre das kein Problem. also etwa
#defines u8 byte

gibt es aber wohl nicht.

Ich gehe mal davon aus, dass es für meine Anforderungen fertigem 
Lösungen gibt. also z.B.
1
void wr_on(void)
2
{
3
wr_off();
4
wait;
5
wr_status_0off_1on=1;
6
7
    {
8
    u8 buffer[8]={0x00, 0x10, 0x00, 0x50, 0x00, 0x01, 0x01, 0x00, 0x01, 0x96, 0x50};
9
    }
10
    wr_write(0x001000,0x5000010100019650);
11
12
_delay_ms(500);//rx abwarten
13
14
sub_protocoll(2);
15
16
}

in c# programmieren und in c#  ausführen und dann auf den m328 
übertragen.

Kann natürlich auch was anderes als c# sein- Grafische Oberfläche zum 
testen des lcd wäre aber schön.

: Verschoben durch Moderator
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Wie wäre es mit C oder C++ statt C#? Dann ist die Syntax identisch. 
Achja, das geht dann sogar mit dem GCC. Der normale x86 GCC eben statt 
des AVR-GCC. Das kann man auch gut debuggen.

Allerdings ist es insbesondere unter Windows i.A. einfacher, Visual 
Studio ("Community" Version ist gratis) statt GCC zu nehmen, weil da 
alles praktisch integriert ist, auch ein guter Debugger. Der C-Support 
ist dort etwas vernachlässigt/veraltet, aber C++ geht gut (wobei GCC das 
noch besser kann). Dass du Typen wie "byte" nutzt klingt als würdest du 
Arduino nutzen - das ist ja auch sowieso C++, nicht C.

Statt "u8" oder "byte" kannst du einfach "uint8_t" nutzen, das gibt es 
auf beiden Plattformen ohne "#define" Gebastel (typedef wäre sowieso 
besser).

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Karl K. schrieb:
> Ich würde gerne einen Teil der c-code-Entwicklung auf den pc auslagern

Kann es sein, daß du noch nie von der Existenz von Cross-Compilern 
gehört hast? Und wo entwickelst du denn dann für den AVR, wenn nicht am 
PC?

> am liebsten mit c#

AFAIK gibt es in der gcc (GNU Compiler Collection) keinen C# Compiler 
für das AVR backend. Insofern geht C# auf AVR erstmal nicht.

> Wenn es in c# #defines gäbe wäre das kein Problem. also etwa
> #defines u8 byte

u8 und byte sind keine Standardtypen. uint_8t hingegen schon. Tu 
dir selber einen Gefallen und verwende diesen. Definiert sind diese 
Typen in stdint.h. Übrigens: ein Byte ist im Kontext von C etwas anderes 
als das was du glaubst.

https://stackoverflow.com/questions/11868211/does-sizeof-return-the-number-of-bytes-or-the-number-of-octets-of-a-type-in-c

von Richard W. (richardw)


Lesenswert?

Sehe ich auch so. Die gängige Verfahrensweise ist, die Applikationslogik 
von der Hardwareansteuerung zu trennen und plattformunabhängig zu 
implementieren. Dann nimmt man sich ein beliebiges Testframework her - 
für den Anfang gerne etwas kleines übersichtliches wie 
https://github.com/mity/acutest - und schreibt Testfälle mit denen man 
die Applikationslogik als PC-Programm ohne Hardware testen und debuggen 
kann. Die benötigten Hardwarefunktionen müssen natürlich trotzdem als 
Fake implementiert werden, aber halt nur mit so viel Funktionalität wie 
für den jeweiligen Testfall benötigt (Stubs).

Alternativ oder zusätzlich lässt man das mit dem PC-Programm bleiben und 
baut ein Hardware-In-the-Loop Testsystem mit dem man die Hardware direkt 
als Blackbox testen kann. Dafür bildet man die Umgebung deiner Steuerung 
so nach dass man ihn so testen kann als wäre er richtig eingebaut. Das 
ist in meinen Augen am Ende wertvoller weil näher an der Realität aber 
mit hohem initialen Zeitaufwand verbunden. On-Chip debugging ist nicht 
viel weniger komfortabel als debuggen auf dem PC. Und man braucht es eh.

Wie auch immer, am Ende ist das wichtigste dass man seinen Kram 
irgendwie (automatisiert) getestet bekommt. Software ohne Testabdeckung 
ist wie klettern ohne Seil.

Ob der Atmega für deinen Anwendungsfall noch zeitgemäß ist? Ich verwende 
für alle Steuerungsaufgaben zu Hause mittlerweile den esp32. Die direkte 
Anbindung ans wlan gepaart mit der Möglichkeit zum Firmwareupdate 
over-the-air ist Gold wert und spart unglaublich viel Zeit.

Und wenn deine Anwendung nicht all zu zeitkritisch ist, gäbe es auch 
micropython für den esp32. Das macht die SW Entwicklung noch mal 
eknfacher.

von Oliver S. (oliverso)


Lesenswert?

Axel S. schrieb:
> Übrigens: ein Byte ist im Kontext von C etwas anderes als das was du
> glaubst.

Nee, ist es nicht. Zwar erlaubt der Standard da beliebige Größen, in der 
Praxis gibt's aber bis auf ein paar ganz seltene abstruse 
Exotenhardwares nur 8 Bit.

Ansonsten ist die Idee, auf dem PC in C# und auf dem Zielsystem C zu 
benutzen, nicht sonderlich sinnvoll.

PC ja, aber auch in C.

Oliver

von J. S. (jojos)


Lesenswert?

Das geht auch gut mit Simulatoren wie Wokwi, der kann auch ESP32 und 
einiges an Hardware emulieren.
Ansonsten eher Cortex-M, debugger ist Gold wert.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Niklas G. schrieb:
> ohne "#define" Gebastel (typedef wäre sowieso
> besser).

using, nicht typedef.

Wenn wir hier schon in der C++ Welt sind.

von Peter D. (peda)


Lesenswert?

Karl K. schrieb:
1
{
2
  u8 buffer[8]={0x00, 0x10, 0x00, 0x50, 0x00, 0x01, 0x01, 0x00, 0x01, 0x96, 0x50};
3
}

Was soll dieser Code bewirken?
buffer ist nach der Klammer wieder weg.

von Roland F. (rhf)


Lesenswert?

Hallo,
Peter D. schrieb:
> Was soll dieser Code bewirken?
> buffer ist nach der Klammer wieder weg.

Und warum wird ein Puffer mit einer Größe von 8 Elementen definiert, 
aber dann mit 11 Elementen initialisiert?

rhf

von Oliver S. (oliverso)


Lesenswert?

Roland F. schrieb:
> Hallo,
> Peter D. schrieb:
>> Was soll dieser Code bewirken?
>> buffer ist nach der Klammer wieder weg.
>
> Und warum wird ein Puffer mit einer Größe von 8 Elementen definiert,
> aber dann mit 11 Elementen initialisiert?
> rhf

Damit es was zum debuggen gibt.

Oliver

: Bearbeitet durch User
von Peter (pittyj)


Lesenswert?

Karl K. schrieb:
> wr_write(0x001000,0x5000010100019650);

Das müßte ja auch ein uint64_t sein.
Arbeiten die kleinen AVRs überhaupt mit solchen Zahlen? Und die 
wr_write() Funktion?

von Karl K. (leluno)


Lesenswert?

Peter D. schrieb:
> buffer ist nach der Klammer wieder weg.

stimmt - da stand wohl mal noch was zum senden, was nicht richtig 
funktioniert hat. Wurde dann durch eine alte Funktion
   wr_write(0x001000,0x5000010100019650);
ersetzt. So etwas würde ich gerne am PC austesten bis es funktioniert.

von Rolf M. (rmagnus)


Lesenswert?

Niklas G. schrieb:
> Wie wäre es mit C oder C++ statt C#? Dann ist die Syntax identisch.
> Achja, das geht dann sogar mit dem GCC. Der normale x86 GCC eben statt
> des AVR-GCC. Das kann man auch gut debuggen.

Hat allerdings den Nachteil, dass die Datentypen unterschiedlich groß 
sind, insbesondere int (wäre natürlich bei C# auch nicht besser - da 
wären zusätzlich auch noch die Konvertierungsregeln anders). Dadurch 
können sich gewisse Fehler einschleichen (Stichwort integer-promotion).

Axel S. schrieb:
>> Wenn es in c# #defines gäbe wäre das kein Problem. also etwa
>> #defines u8 byte
>
> u8 und byte sind keine Standardtypen. uint_8t hingegen schon.

uint8_t.

Oliver S. schrieb:
> Axel S. schrieb:
>> Übrigens: ein Byte ist im Kontext von C etwas anderes als das was du
>> glaubst.
>
> Nee, ist es nicht. Zwar erlaubt der Standard da beliebige Größen, in der
> Praxis gibt's aber bis auf ein paar ganz seltene abstruse
> Exotenhardwares nur 8 Bit.

So abstrus sind die nun auch wieder nicht. Zum Beispiel irgendwelche 
DSPs, bei denen es nix kleineres als 24 Bit gibt. Das dann in C auf die 
üblichen 8, 16, 32, 64 Bit abzubilden würde C auf sowas komplett 
unbrauchbar machen.

> Ansonsten ist die Idee, auf dem PC in C# und auf dem Zielsystem C zu
> benutzen, nicht sonderlich sinnvoll.
>
> PC ja, aber auch in C.

Ja.

Arduino F. schrieb:
> Niklas G. schrieb:
>> ohne "#define" Gebastel (typedef wäre sowieso
>> besser).
>
> using, nicht typedef.
>
> Wenn wir hier schon in der C++ Welt sind.

Ich finde typedef trotzdem besser. Einzig der Name ist unglücklich 
gewählt, weil man damit keinen Typ definiert.

Peter schrieb:
> Karl K. schrieb:
>> wr_write(0x001000,0x5000010100019650);
>
> Das müßte ja auch ein uint64_t sein.
> Arbeiten die kleinen AVRs überhaupt mit solchen Zahlen? Und die
> wr_write() Funktion?

Dem AVR ist das egal, der kann eh nix größeres als 8 Bit. Da kümmert 
sich der Compiler drum. Allerdings ist der Umgang mit so großen 
Datentypen mitunter nicht sehr effizient. Man sollte das daher nur tun, 
wenn man auch wirklich so große Zahlen braucht.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Rolf M. schrieb:
> Ich finde typedef trotzdem besser. Einzig der Name ist unglücklich
> gewählt, weil man damit keinen Typ definiert.

Auch die Syntax ist ein wenig quer.
Mir schmeckt zB. nicht, dass das zu definierende manchmal mitten im 
Ausdruck erscheint, erscheinen muss.

Beispiele:
1
typedef ParameterType MeinArray[42];
2
typedef AusgabeType (*CallBackType)(ParameterType);
Vielleicht gehts nur mir so, aber ich muss da schon manchmal einen 
Gedanken mehr drauf verschwenden: "Was wird da überhaupt definiert?"

1
using MeinArray = ParameterType[42];
2
using CallBackType = AusgabeType (*)(ParameterType);
Die using Variante erscheint mir da ein wenig 
eingängiger/offensichtlicher zu sein.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf M. schrieb:
> Niklas G. schrieb:
>> Wie wäre es mit C oder C++ statt C#? Dann ist die Syntax identisch.
>> Achja, das geht dann sogar mit dem GCC. Der normale x86 GCC eben statt
>> des AVR-GCC. Das kann man auch gut debuggen.
>
> Hat allerdings den Nachteil, dass die Datentypen unterschiedlich groß
> sind

Wenn man anstelle des GCC einen 16-Bit-Compiler (bspw. Watcom) nimmt,
ist dieser Nachteil beseitigt.


Arduino F. schrieb:
> Rolf M. schrieb:
>> Ich finde typedef trotzdem besser. Einzig der Name ist unglücklich
>> gewählt, weil man damit keinen Typ definiert.
>
> Auch die Syntax ist ein wenig quer.
> Mir schmeckt zB. nicht, dass das zu definierende manchmal mitten im
> Ausdruck erscheint, erscheinen muss.

Die Syntax entspricht der von Variablendeklarationen, nur dass der
Variablenname durch den Namen des Typ-Alias ersetzt wird. Da C++ nach
wie vor die Variablendeklarationssyntax von C benutzt, wirkt es recht
komisch, wenn ausgerechnet für Typ-Aliase (die ja viel seltener als
Variablendeklarationen benötigt werden) eine andere Syntax verwendet
wird.

von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> Die Syntax entspricht der von Variablendeklarationen, nur dass der
> Variablenname durch den Namen des Typ-Alias ersetzt wird.
> Da C++ nach wie vor die Variablendeklarationssyntax von C benutzt, wirkt
> es recht komisch, wenn ausgerechnet für Typ-Aliase (die ja viel seltener
> als Variablendeklarationen benötigt werden) eine andere Syntax verwendet
> wird.

Genau deshalb mag ich typedef lieber. Es weicht nicht von der Syntax von 
Variablendeklarationen ab. Das finde ich konsistenter.

Arduino F. schrieb:
> Beispiele:
> typedef ParameterType MeinArray[42];
> typedef AusgabeType (*CallBackType)(ParameterType);

Variablendeklaration:
1
ParameterType MeinArray[42];
2
AusgabeType (*CallBackFunktion)(ParameterType);

Wenn ich dafür eigene Typ-Aliase will, schreib ich einfach typedef 
davor, und den Rest kann ich genau gleich lassen. Bei using sieht es 
deutlich anders aus, also uneinheitliche Syntax.

> Vielleicht gehts nur mir so, aber ich muss da schon manchmal einen
> Gedanken mehr drauf verschwenden: "Was wird da überhaupt definiert?"

So ähnlich ging es mir mal nur bei der Funktion signal(), die einen 
Funktionszeiger als Parameter bekommt und einen zurückgibt:
1
void (*signal(int sig, void (*func)(int)))(int);
signal selbst ist hier übrigens kein Funktionszeiger.
Aber das ist eben eine Funktionsdeklaration. Deren Syntax bleibt also 
so.
(klar kann - und sollte - man sich für den Signalhandler ein eigenes 
Typ-Alias anlegen)

> using MeinArray = ParameterType[42];
> using CallBackType = AusgabeType (*)(ParameterType);
> Die using Variante erscheint mir da ein wenig
> eingängiger/offensichtlicher zu sein.

Ob jetzt (*)() für den Namen einen Funktionszeiger-Typs besonders 
eingängig ist, ist Ansichtssache.

: Bearbeitet durch User
von 900ss (900ss)


Lesenswert?

Rolf M. schrieb:
> Zum Beispiel irgendwelche DSPs, bei denen es nix kleineres als 24 Bit
> gibt.

32 Bit auch :)
Wenn man nur x86 und AVR kennt dann sind DSPs schon abstruse Exoten ;)

Rolf M. schrieb:
1
> void (*signal(int sig, void (*func)(int)))(int);
C Programmierer sind schon ein wenig masochistisch weil sie sich so 
etwas antun. Ich glaube alleine wegen obigen Konstrukt wurde typedef 
erfunden ;)

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

Da benutze ich das typedef lieber für den Functionpointer. Spätestens 
wenn man die std Container verwendet wird das deutlich lesbarer.

von Rolf M. (rmagnus)


Lesenswert?

900ss schrieb:
> Rolf M. schrieb:
> void (*signal(int sig, void (*func)(int)))(int);
> C Programmierer sind schon ein wenig masochistisch weil sie sich so
> etwas antun. Ich glaube alleine wegen obigen Konstrukt wurde typedef
> erfunden ;)

Möglicherweise. Mit typedef sieht's auch schon bedeutend übersichtlicher 
aus:
1
typedef void (*sighandler_t)(int);
2
3
sighandler_t signal(int signum, sighandler_t handler);

Yalu X. schrieb:
> Wenn man anstelle des GCC einen 16-Bit-Compiler (bspw. Watcom) nimmt,
> ist dieser Nachteil beseitigt.

Aber kann man das auf heutigen PCs und deren Betriebssystemen noch nativ 
ausführen und debuggen? Darum ging es ja schließlich. Man könnte 
natürlich einen Emulator nehmen wie DosBox, aber dann könnte man auch 
gleich den AVR emulieren.

von Karl K. (leluno)


Lesenswert?

Axel S. schrieb:
> u8 und byte sind keine Standardtypen. uint_8t

du meinst uint8_t? c#-Programm geschrieben. beides probiert. beides 
kennt c# nicht?

> typedef ParameterType MeinArray[42];
> typedef AusgabeType (*CallBackType)(ParameterType);

> using MeinArray = ParameterType[42];
> using CallBackType = AusgabeType (*)(ParameterType);


using und typedef kennt c# auch nicht. Fehlt ein Verweis?


Nachtrag:
using u8 = System.Byte;
scheint doch zu gehen.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Karl K. schrieb:
> Axel S. schrieb:
>> u8 und byte sind keine Standardtypen. uint_8t
>
> du meinst uint8_t? c#-Programm geschrieben. beides probiert. beides
> kennt c# nicht?

Damit zeigst du ja selber, warum C# für dein Vorhaben nicht geeignet 
ist.

> using und typedef kennt c# auch nicht. Fehlt ein Verweis?

Hmm, using hätte es eigentlich kennen müssen soweit ich weiß.

von J. S. (jojos)


Lesenswert?

C# wäre mir auch zu weit weg für so eine Emulation. Und gerade der 
kritische Teil mit dem Modbus wird kaum kompatibel mit der µC Software 
sein. Außer vielleicht Modbus über TCP, aber das passt nicht zum Nano.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf M. schrieb:
> Yalu X. schrieb:
>> Wenn man anstelle des GCC einen 16-Bit-Compiler (bspw. Watcom) nimmt,
>> ist dieser Nachteil beseitigt.
>
> Aber kann man das auf heutigen PCs und deren Betriebssystemen noch nativ
> ausführen und debuggen?

Je nachdem, welches Betriebssystem man nativ laufen hat ;-)

> Darum ging es ja schließlich. Man könnte natürlich einen Emulator
> nehmen wie DosBox

Ja, das ist eine einfach aufzusetzende Möglichkeit, den 16-Bit-Code zu
testen.

Ich selber benutze das Kompilieren auf dem PC vor allem, um etwas
kompliziertere Berechnungen oder Algorithmen vorab ausgiebig mit
verschiedenen reproduzierbaren Eingabedatensätzen zu testen, weil das
auf dem Target-Mikrocontroller meist sehr mühsam ist. Erst wenn diese
Tests erfolgreich sind, wird das Programm auf das Target geflasht. Dort
kann ich mich dann voll auf das Testen der I/O-Funktionen und des
Echtzeitverhaltens konzentrieren (was wiederum auf dem PC kaum möglich
ist), weil ich weiß, dass der ganze Rest bereits fehlerfrei ist.

Das Testen mit einem 16-Bit-Compiler (OpenWatcom) habe ich erst vor
Kurzem ausprobiert, da ich mich gefragt habe, wie man auch Fehler auf
Grund von Integer-Überläufen bereits beim Testen auf dem PC erkennen
kann.

> aber dann könnte man auch gleich den AVR emulieren.

Ja, das geht natürlich auch, nur ist es damit nicht so einfach, den
Eingabedatenstrom von einer Datei zu lesen und die Ergebnisse wieder in
eine Datei zu schreiben, um sie genauer analysieren zu können.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Das Testen mit einem 16-Bit-Compiler (OpenWatcom) habe ich erst vor
> Kurzem ausprobiert, da ich mich gefragt habe, wie man auch Fehler auf
> Grund von Integer-Überläufen bereits beim Testen auf dem PC erkennen
> kann.
>
>> aber dann könnte man auch gleich den AVR emulieren.
>
> Ja, das geht natürlich auch, nur ist es damit nicht so einfach, den
> Eingabedatenstrom von einer Datei zu lesen und die Ergebnisse wieder in
> eine Datei zu schreiben, um sie genauer analysieren zu können.

Für rein algorithmische Tests (also keine SFRs oder IRQs involviert), 
verwende ich AVRtest als Simulator.

Als ich zum Beispiel eine 64-Bit double Implementation gemacht have, war 
das sehr hilfreich -- das auf einem komplett anderen System mit bereits 
funktionierender double-Arithmetik zu testen wäre ziemlich sinnlos 
gewesen...

Daten aus AVRtest raus bekommen ist einfach:  Im einfachsten Falle per 
printf (was auf dem stdout des Hosts landet), oder per spezieller 
Syscalls, die den Overhead von printf vermeiden.

Daten von Platte zu lesen geht prinzipiell auch, ist aber wegen des 
Designs der AVR-LibC etwas umständlich.  AVRtest stellt dafür ein 
Target-Modul zur Verfügung, das man nur noch in die AVR-Applikation 
reinzulinken brauch; und dann funktioniert alles wie per stdio.h.

AVRtest ist auch einigermaßen fix.  Beispielsweise folgender Test, der 
ca. 2*16000000 24-Bit Multiplikation testet
1
#include <stdint.h>
2
3
void test_mul16 (void)
4
{
5
    uint16_t a = 0;
6
    do
7
    {
8
        uint8_t b = 0;
9
        do
10
        {
11
            __uint24 ab1 = (__uint24) a * b;
12
            __asm ("" : "+r" (a), "+r" (b));
13
            __uint24 ab2 = (__uint24) b * a;
14
15
            if (ab1 != ab2)
16
                __builtin_abort ();
17
        } while (++b);
18
    } while (++a);
19
}
20
21
int main (void)
22
{
23
    test_mul16 ();
24
    return 0;
25
}

Simuliert werden 436470188 Cycles in ca. 3.5 Sekunden, das entspricht 
einem AVR auf 125 MHz.

: Bearbeitet durch User
von Karl K. (leluno)


Lesenswert?

1
using u8 = System.Byte;
2
3
4
u8[] uart_str_rx = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
5
6
7
wird mit gcc
8
9
u8* uart_str_rx = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

wie bekomme ich den Austausch mit einem #define hin? u8[] zu u8* hin?

#define u8[] u8*  //geht leider nicht

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Karl K. schrieb:
> wird mit gcc
> wie bekomme ich den Austausch mit einem #define hin? u8[] zu u8* hin?
>
> #define u8[] u8*  //geht leider nicht


Array Bezeichner zerfallen automatisch zu einem Zeiger auf das erste 
Element.
Da brauchts kein  define für.

: Bearbeitet durch User
von Karl K. (leluno)


Lesenswert?

Arduino F. schrieb:
> Array Bezeichner zerfallen automatisch zu einem Zeiger auf das erste
> Element.
> Da brauchts kein  define für.

kannst du das erklären - mit u8* bekomme ich in c# eine Fehlermeldung - 
mit u8[] kommt die Fehlermeldung bei gcc?


> von J. S. (jojos)▼
> C# wäre mir auch zu weit weg für so eine Emulation. Und gerade der
> kritische Teil mit dem Modbus wird kaum kompatibel mit der µC Software
> sein. Außer vielleicht Modbus über TCP, aber das passt nicht zum Nano.


Der Modbus-Teil ist relativ einfach
1
        private void x_main()
2
        {
3
            u8[] wr_test = { 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x85, 0xdb };
4
         serial_tx(wr_test, 8);
5
6
}
7
//c#:
8
          void serial_tx(u8[] array, u8 ct)
9
          {
10
              serialPort1.Write(array, 0, ct);
11
          }
12
13
//avr
14
      void serial_tx(u8* array, u8 ct)
15
      {
16
         // serialPort1.Write(array, 0, ct);
17
18
          for (u8 i = 0; i < ct; i++)
19
          {
20
    while(!(UCSR0A & (1<<UDRE0)));
21
  UDR0 = buf[i];
22
          }
23
      }


Ich hänge ja nicht an c# - eher an gccc. Womit geht es denn leichter? 
Wichtig wäre eine grafische Oberfläche um ein lcd zu simulieren und 
serial tx/rx.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Karl K. schrieb:
> eher an gcc

Den GCC gibt's auch für ARM oder ESP32, wäre also kein Hindernis zum 
wechseln...

Karl K. schrieb:
> Wichtig wäre eine grafische Oberfläche um ein lcd zu simulieren und
> serial tx/rx.

Das können so gut wie alle Programmiersprachen/Systeme. Wie gesagt, 
Visual Studio würde sich anbieten; identischer C/C++ Code, GUI Kram dran 
flanschen.

von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> Das Testen mit einem 16-Bit-Compiler (OpenWatcom) habe ich erst vor
> Kurzem ausprobiert, da ich mich gefragt habe, wie man auch Fehler auf
> Grund von Integer-Überläufen bereits beim Testen auf dem PC erkennen
> kann.

Da wäre ein gcc für aktuelle PC-Systeme praktisch, bei dem aber die 
Typen entsprechend denen vom AVR definiert sind. Aber das wäre wohl 
etwas komplizierter, weil das dann nicht mehr mit den bestehenden 
Bibliotheken (inklusive libc) funktionieren würde.

Karl K. schrieb:
> wie bekomme ich den Austausch mit einem #define hin? u8[] zu u8* hin?

Gar nicht. Ich weiß, du willst das nicht hören, aber hier nochmal in 
aller Deutlichkeit: C und C# sind zwei gänzlich verschiedene Sprachen. 
Es macht keinen Sinn, zum Testen C-Code als C# übersetzen zu wollen. Zum 
einen müsstest du es dann irgendwie hinbekommen, haufenweise derartige 
Fälle zu mappen, zum anderen hast du am Ende trotzdem kein 
aussagekräftiges Ergebnis.

von J. S. (jojos)


Lesenswert?

Karl K. schrieb:

> Ich hänge ja nicht an c# - eher an gccc. Womit geht es denn leichter?
> Wichtig wäre eine grafische Oberfläche um ein lcd zu simulieren und
> serial tx/rx.

Wie geschrieben, schaue dir Wokwi an.

Serial.write ist aber nur die halbe Miete bei Modbus, da gibt es 
Timeouts die so nicht beachtet werden. Modbus schreibt in Register, das 
wird hier mit einer fertigen Sequenz versteckt.

Es ist keine Schande für so eine Anwendung Arduino und eine fertige 
Modbus Lib zu nehmen, genauso wie für das Display. Und eben lieber einen 
ESP32, der kostet ja auch nix im Vergleich zum gesamten Aufwand für so 
ein Projekt. Nebenbei kann der einen Webserver per WLAN bereitstellen um 
z.B. den Status anzuzeigen.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Das Testen mit einem 16-Bit-Compiler (OpenWatcom) habe ich erst vor
> Kurzem ausprobiert, da ich mich gefragt habe, wie man auch Fehler auf
> Grund von Integer-Überläufen bereits beim Testen auf dem PC erkennen
> kann.

Macht es Sinn sich bei sowas auf so eine exotische Software zu 
verlassen...?

Man könnte auch in C++ den uint16_t durch eine eigene Klasse ersetzen 
welche das exakte Promotion&Overflow Verhalten des AVRs emuliert sodass 
man es auf dem PC verifizieren kann. Ist zwar langsam aber spielt da ja 
keine Rolle. Wäre aber etwas fummelig das Verhalten wirklich korrekt zu 
implementieren.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Karl K. schrieb:
> kannst du das erklären - mit u8* bekomme ich in c# eine Fehlermeldung -
> mit u8[] kommt die Fehlermeldung bei gcc?

Ähmmm...
Damit ich dich richtig verstehe:
Du möchtest das gleiche Programm wahlweise mit einem c oder C++ Compiler 
übersetzen und mit C# ?

C und C++ bekommt man ja gerade noch so einigermaßen unter einen Hut.
Aber c# ist da eine etwas andere Welt.

Ich rate davon ab.
So beschäftigst du dich mehr mit den Sprachunterschieden, als mit der 
Programmfunktionalität.


Stattdessen:
Wähle C oder C++, die laufen auf quasi jedem Kesselchen.
Und wenn Arduino, dann bist du sowieso in der C++ Welt.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Array Bezeichner zerfallen automatisch zu einem Zeiger auf das erste
> Element.

Nur wenn es sich um Funktionsparameter handelt. Ansonsten nicht.

von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
> Arduino F. schrieb:
>> Array Bezeichner zerfallen automatisch zu einem Zeiger auf das erste
>> Element.
>
> Nur wenn es sich um Funktionsparameter handelt. Ansonsten nicht.

Doch, auch sonst, bis auf wenige Ausnahmen. Wenn sie links von einem 
Zuweisungsoperator oder in sizeof stehen, dann passiert das nicht.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Niklas G. schrieb:
> Yalu X. schrieb:
>> Das Testen mit einem 16-Bit-Compiler (OpenWatcom) habe ich erst vor
>> Kurzem ausprobiert, da ich mich gefragt habe, wie man auch Fehler auf
>> Grund von Integer-Überläufen bereits beim Testen auf dem PC erkennen
>> kann.
>
> Macht es Sinn sich bei sowas auf so eine exotische Software zu
> verlassen...?

So arg exotisch ist OpenWatcom ja auch wieder nicht. Das ist immerhin
ein aktiv gepflegtes Produkt (letzte Version vom Dezemeber 2024), das
für mehrere Betriebssysteme (DOS, Windows, OS/2 und Linux) verfügbar
ist.

Oder man bleibt beim (AVR-)GCC und verfolgt den Vorschlag von Johannes:

Johann L. schrieb:
> Für rein algorithmische Tests (also keine SFRs oder IRQs involviert),
> verwende ich AVRtest als Simulator.

> Man könnte auch in C++ den uint16_t durch eine eigene Klasse ersetzen
> welche das exakte Promotion&Overflow Verhalten des AVRs emuliert sodass
> man es auf dem PC verifizieren kann.

Nur uint16_t anzupassen, reicht nicht. Man müsste entsprechendes auch
mit int tun, um bspw. zu bewirken, dass 1000*1000 nicht 1000000, sondern
16960 ergibt. Das geht aber nicht, da int in C++ ein feststehender
Datentyp ist.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Das geht aber nicht, da int in C++ ein feststehender
> Datentyp ist.

Man muss eben alle Literale direkt in die eigene Klasse stecken sodass 
kein "normaler" Int draus wird. Man braucht mehrere Klassen für die 
diversen Typen und muss die Rechenregeln entsprechend implementieren. 
Wie gesagt, recht aufwendig aber möglich

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Rolf M. schrieb:
>> aber dann könnte man auch gleich den AVR emulieren.
>
> Ja, das geht natürlich auch, nur ist es damit nicht so einfach, den
> Eingabedatenstrom von einer Datei zu lesen und die Ergebnisse wieder in
> eine Datei zu schreiben, um sie genauer analysieren zu können.

Mal ein Minimalbeispiel mit AVRtest über stdin / stdout:

*in.c*
1
#include <stdio.h>
2
3
int main (void)
4
{
5
    while (1)
6
    {
7
        char buf[10];
8
        char *s = fgets (buf, sizeof (buf), stdin);
9
        if (s)
10
            printf ("READ:%s", s);
11
        else
12
            break;
13
    }
14
15
    return 0;
16
}
1
$ avr-gcc -mmcu=atmega128 -Os in.c -o in.elf $AVRTEST/exit-atmega128.o
2
$ avrtest -q in.elf < in.c

*Ausgabe:*
1
READ:#include READ:<stdio.h>READ:
2
READ:
3
READ:int main READ:(void)
4
READ:{
5
READ:    whileREAD: (1)
6
...

*Erklärung:*

exit-atmega128.o ist ein Target-Modul, das von AVRtest beim Build 
erstellt wird und stdin, stdout und stderr auf den PC umleitet. 
$AVRTEST ist der Pfad wo auch das avrtest Executable liegt.

-q ist quiet Mode, d.h. AVRtest zeigt am Ende keine Zusammenfassung.

< in.c leitet die Textdatei in.c zum stdin des PCs, und AVRtest leitet 
es weiter zum stdin des simulierten Programms.

Die Ausgabe erscheint auf stdout des PCs, von wo aus sie 
weiterverarbeitet oder gespeichert werden kann, etwa mit > in.txt

Um einen String direkt einzugeben kann man z.B:
1
$ avrtest -q in.elf <<< "in.c"
2
READ:in.c

Nachteil eines Simulators ist, dass man auf die Resourcen eines AVRs 
beschränkt ist in Flash- und RAM-Größe.  Die Flash-Größe dürfte kein 
großes Hindernis sein, man simuliert einfach einen AVR, der genug Flash 
hat.

Und mit der RAM-Größe ist man nicht an die Hardware gebunden, d.h. per 
Linker-Flags kann man ein größeres RAM darstellen.  Man kann etwa einen 
ATmega4808 nehmen, und ihm knapp 16K RAM verpassen bis 0x4000.  (Ab 4000 
ist der Flash im RAM-Adressraum sichtbar, man braucht also auch kein 
PROGMEM und so Gehampel um RAM zu sparen, da .rodata im Flash liegt).

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
> Mal ein Minimalbeispiel mit AVRtest über stdin / stdout:

Sehr gut, danke, hab's gerade erfolgreich ausprobiert :)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Johann L. schrieb:
>> Mal ein Minimalbeispiel mit AVRtest über stdin / stdout:
>
> Sehr gut, danke, hab's gerade erfolgreich ausprobiert :)

Weitere Möglichkeit, Daten ins Programm zu bekommen, ist per #include 
oder #embed.  Braucht zwar Flash, ist dafür aber einfacher und schneller 
als erst alles durch scanf etc. zu wursten.

Oder (Pseudo)-Zufall um Test-Daten (reproduzierbar) zu erzeugen:
1
#include <stdint.h>
2
#include "avrtest.h"
3
4
uint64_t u64_rand (void)
5
{
6
    uint32_t lo = avrtest_prand ();
7
    uint32_t hi = avrtest_prand ();
8
    return (uint64_t) hi << 32 | lo;
9
}
10
11
int main (void)
12
{
13
    for (int i = 0; i < 6; ++i)
14
        LOG_FMT_U64 ("Wert = 0x%016llx\n", u64_rand());
15
16
    return 0;
17
}
1
Wert = 0x161391eacafebabe
2
Wert = 0x3d6bc2c35c3d5af4
3
Wert = 0x7ec540f4f07af5bd
4
Wert = 0x9adcc96a584c6c52
5
Wert = 0xed8934b23b5e0b7f
6
Wert = 0x71e4bc36667f8b1c

Als Option für avr-gcc brauch's dann noch ein -I$AVRTEST, damit der 
avrtest.h Header gefunden wird, der avrtest_prand() etc. implementiert.

avrtest_prand und LOG_FMT_U64 sind als AVRtest Syscalls implementiert, 
d.h. die eigentliche Arbeit übernimmt der Simulator.  Das macht die 
Tests merklich effizienter, da man auf printf und rand verzichten kann.

: Bearbeitet durch User
von Karl K. (leluno)


Lesenswert?

Rolf M. schrieb:
>> wie bekomme ich den Austausch mit einem #define hin? u8[] zu u8* hin?
>
> Gar nicht.

ganz einfach:
mittels c#-Funktion im Programm den Programmcode laden, u8[] durch u8* 
ersetzen und im gcc-Verzeichnis abspeichern. Das bekomme ich hin.

Ich habe mir codeblocks mit gcc für avr angeschaut. Aber überzeugt hat 
mich das noch nicht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.