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.
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.
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).
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 byteu8 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
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.
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
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
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
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?
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.
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.
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
typedefParameterTypeMeinArray[42];
2
typedefAusgabeType(*CallBackType)(ParameterType);
Vielleicht gehts nur mir so, aber ich muss da schon manchmal einen
Gedanken mehr drauf verschwenden: "Was wird da überhaupt definiert?"
1
usingMeinArray=ParameterType[42];
2
usingCallBackType=AusgabeType(*)(ParameterType);
Die using Variante erscheint mir da ein wenig
eingängiger/offensichtlicher zu sein.
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.
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
ParameterTypeMeinArray[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(intsig,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.
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(intsig,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 ;)
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:
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.
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.
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ß.
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.
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.
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
voidtest_mul16(void)
4
{
5
uint16_ta=0;
6
do
7
{
8
uint8_tb=0;
9
do
10
{
11
__uint24ab1=(__uint24)a*b;
12
__asm("":"+r"(a),"+r"(b));
13
__uint24ab2=(__uint24)b*a;
14
15
if(ab1!=ab2)
16
__builtin_abort();
17
}while(++b);
18
}while(++a);
19
}
20
21
intmain(void)
22
{
23
test_mul16();
24
return0;
25
}
Simuliert werden 436470188 Cycles in ca. 3.5 Sekunden, das entspricht
einem AVR auf 125 MHz.
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.
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
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.
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.
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.
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.
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.
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.
Arduino F. schrieb:> Array Bezeichner zerfallen automatisch zu einem Zeiger auf das erste> Element.
Nur wenn es sich um Funktionsparameter handelt. Ansonsten nicht.
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.
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.
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
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*
*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).
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_tu64_rand(void)
5
{
6
uint32_tlo=avrtest_prand();
7
uint32_thi=avrtest_prand();
8
return(uint64_t)hi<<32|lo;
9
}
10
11
intmain(void)
12
{
13
for(inti=0;i<6;++i)
14
LOG_FMT_U64("Wert = 0x%016llx\n",u64_rand());
15
16
return0;
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.
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.