Hallo Community,
ich arbeite an einem C-Projekt, bei dem ich die variable Argumentliste
(va_list) in einem Arduino Mega2560 verwenden muss. In meinem Code
verwende ich die folgende Konstruktion:
// >>>>>>>>> was steht in arg drin, das ist die Frage
15
16
// Nächstes Argument aus der Argumentenliste holen
17
arg=va_arg(args,constchar*);
18
}
19
while(arg!=NULL);
20
21
va_end(args);// Argumentenliste aufräumen
22
}
Ich würde gerne wissen, welche Werte die va_list-Variablen args und
num_args auf diesem spezifischen System zurückgeben.
Ich verwende das so:
1
voidCheck_MOD(uint16_tZeilenNr,uint8_tMOD_ix)
2
{
3
jprint("MOD RangeErr in ",ZeilenNr,": ",MOD_ix," ",4711);
4
}
und jprint fasst alle Argumente zusammen.
Kann mir jemand helfen, diese Rückgabewerte richtig zu verstehen für
Argumente vom Typ char*, uint16_t und uint8_t und zu erklären, wie sie
auf einem Arduino Mega2560 interpretiert werden können? Ich möchte dem
Argument keine Info mitgeben, welchen Typ er hat.
Im Kern geht es um die Frage: Was steht in arg?
Vielen Dank für Eure Hilfe!
Reinhard schrieb:> arg = va_arg(args, const char*);
Falsch!
> Ich möchte dem Argument keine Info mitgeben, welchen Typ er hat.
Das funktioniert nicht. va_arg() funktioniert nur richtig, wenn Du den
Typ des Arguments korrekt mitteilst. printf() und Konsorten machen das
auch, nämlich durch den Format-String.
> Im Kern geht es um die Frage: Was steht in arg?
So, wie Du das aufrufst: Müll. Spätestens bei der Verarbeitung des
Arguments 4711, was kein C-String ist.
Gib mal bei Google "man va_arg" ein und lies erstmal das Manual.
Reinhard schrieb:> Was steht in arg?
ein paar Bits. Ob das als uint16_t oder Pointer übergeben wurde, kann
man innerhalb der Funktion nicht mehr feststellen. Insbesondere kann man
NULL (das Ende der Liste) nicht von MOD_ix=0 unterscheiden. Eine
Notlösung wäre, immer abwechselnd char * und int zu übergeben. Dann wäre
der Typ aller Parameter fest und NULL am Ende wieder eindeutig. Trotzdem
könnte die Liste beliebig lang sein.
Nachdem die Ähnlichkeit mit printf() nicht zu übersehen ist: was ist der
Vorteil von dieser Konstruktion?
Reinhard schrieb:> ich arbeite an einem C-Projekt, bei dem ich die variable Argumentliste> (va_list) in einem Arduino Mega2560 verwenden muss.
Arduino-Code ist C++-Code, nicht C.
> jprint("MOD RangeErr in ",ZeilenNr,": ",MOD_ix," ",4711);
Warum schreibst Du nicht einfach C++:
1
Stringstr=(String)"MOD RangeErr in "+ZeilenNr+": "+MOD_ix+" "+4711;
2
Serial.write(str);// oder irgendeine andere Ausgabefunktion
Du kannst auch auf die Variable str verzichten und das Ganze rechts vom
Gleichheitszeichen direkt Deiner Ausgabefunktion übergeben -
vorausgesetzt, sie kann mit einem C++-String als Argument umgehen.
Notfalls, wenn Du wirklich noch mit "char *" arbeitest, musst Du den
C++-String in einen C-String zurückwandeln.
Frank M. schrieb:> Arduino-Code ist C++-Code, nicht C.
Wenn du die Arduino-IDE benutzt, magst du recht haben. Ich verwende
Atmel Studio und programmiere in C.
Bauform B. schrieb:>> Eine> Notlösung wäre, immer abwechselnd char * und int zu übergeben. Dann wäre> der Typ aller Parameter fest und NULL am Ende wieder eindeutig. Trotzdem> könnte die Liste beliebig lang sein.
Das ist vielleicht eine Idee.
> Nachdem die Ähnlichkeit mit printf() nicht zu übersehen ist: was ist der> Vorteil von dieser Konstruktion?
printf() hat einen ziemlich großen Overhead, und das ist auf einem
Arduino nicht so toll. Ich programmiere auch auf dem Uno und Nano.
Kann ich printf() auch benutzen, um nur den Ausgabestring zu erzeugen,
ohne ihn auszugeben? Also in ein char output[100] ablegen?
Frank M. schrieb:> Reinhard schrieb:>> ich arbeite an einem C-Projekt, bei dem ich die variable Argumentliste>> (va_list) in einem Arduino Mega2560 verwenden muss.>> Arduino-Code ist C++-Code, nicht C
Ein Arduino Mega2560 ist erst mal nur ein Stück Hardware, auf dem man
auch C-Programme laufen lassen kann.
Reinhard schrieb:> Ich möchte dem Argument keine Info mitgeben, welchen Typ er hat.
Wie schon gesagt wurde, geht das mit varargs nicht. Du musst in deiner
Funktion beim Aufruf von va_arg() angeben, welchen Typ das Argument hat.
Es liegt also an dir, den Typ zu kennen und korrekt anzugeben. Wie du an
den Typ kommst, ist dir überlassen.
In C++ könnte man sowas mit einem variadischen Template machen.
Reinhard schrieb:> Kann ich printf() auch benutzen, um nur den Ausgabestring zu erzeugen,> ohne ihn auszugeben? Also in ein char output[100] ablegen?
Dazu gibt es sprintf, bzw. besser snprintf.
https://en.cppreference.com/w/c/io/fprintf
Reinhard schrieb:> printf() hat einen ziemlich großen Overhead
Den hat es vor allem deshalb, weil es eben "allen Furz und Feuerstein"
unterstützen muss und erst zur Laufzeit weiß, was davon wirklich
gebraucht wird.
Daher gibt es in der avr-libc auch noch Varianten ohne
Gleitkomma-Support sowie völlig minimalistische, bei denen noch einige
andere Features weggelassen wurden.
Gerade auf dem ATmega2560 würde ich mich aber an deiner Stelle nicht
verrenken und erst einmal so lange printf (oder sprintf) benutzen bis
klar ist, dass wirklich die Säge klemmt und auch diese zu viel sind.
Davor lohnt sich der Bau eigener Ersatzfunktionen eigentlich eher nicht,
zumal du ja immer das Risiko eingehst, statt relativ gut debuggter
Software eigene neue Fehler einzubauen.
Rolf M. schrieb:> Reinhard schrieb:>> Kann ich printf() auch benutzen, um nur den Ausgabestring zu erzeugen,>> ohne ihn auszugeben? Also in ein char output[100] ablegen?>> Dazu gibt es sprintf, bzw. besser snprintf.> https://en.cppreference.com/w/c/io/fprintf
Für das Beispiel aus dem ersten Beitrag würde auch etwas wie itoa()
ausreichen. Das war doch beim AVR irgendwo dabei?
Bauform B. schrieb:> Rolf M. schrieb:>> Reinhard schrieb:>>> Kann ich printf() auch benutzen, um nur den Ausgabestring zu erzeugen,>>> ohne ihn auszugeben? Also in ein char output[100] ablegen?>>>> Dazu gibt es sprintf, bzw. besser snprintf.>> https://en.cppreference.com/w/c/io/fprintf>> Für das Beispiel aus dem ersten Beitrag würde auch etwas wie itoa()> ausreichen.
Würde es? Soweit ich verstehe, zeigt es eine Ausgabefunktion im Stile
von printf (nur ohne Formatstring), die entsprechend auch mit mehreren
Argumenten von unterschiedlichem Typ zurecht kommen und das dann zu
einem Gesamtstring zusammenbauen soll.
In C++ wäre es ziemlich einfach mit variadischen templates. Hier gezeigt
mit Unterstützung für signed Integers, unsigned Integers, char, char*,
String Literale. Man kann beliebig eigene "convert" Funktionen
hinzufügen für weitere Typen (z.B. float oder auch Klassen). Es wird
automatisch auf Buffer Overflows geprüft. Die Ausgabe erfolgt testweise
auf stdout, kann natürlich beliebig geändert werden. Es ist potentiell
auch effizienter als sprintf, weil kein Parsen eines Format-Strings und
Laufzeitunterscheidung nötig ist sowie keine komplizierte
Stack-Akrobatik (va_arg).
Das Atmel Studio kann auch C++, du musst vermutlich nur die Datei mit
der Endung .cpp speichern.
Rolf M. schrieb:> Bauform B. schrieb:>> Rolf M. schrieb:>>> Reinhard schrieb:>>>> Kann ich printf() auch benutzen, um nur den Ausgabestring zu erzeugen,>>>> ohne ihn auszugeben? Also in ein char output[100] ablegen?>>>>>> Dazu gibt es sprintf, bzw. besser snprintf.>>> https://en.cppreference.com/w/c/io/fprintf>>>> Für das Beispiel aus dem ersten Beitrag würde auch etwas wie itoa()>> ausreichen.>> Würde es? Soweit ich verstehe, zeigt es eine Ausgabefunktion im Stile> von printf (nur ohne Formatstring), die entsprechend auch mit mehreren> Argumenten von unterschiedlichem Typ zurecht kommen und das dann zu> einem Gesamtstring zusammenbauen soll.
Naja, wenn va_list für die ursprüngliche Idee funktionieren würde,
brauchte man doch kaum mehr als itoa() und strncat(). Aber snprintf()
ist wirklich vernünftiger, besonders, wenn es abgespeckte Varianten
gibt.
Wenn die immer noch zu groß sind, würde ich eher einen eigenen
snprintf()-Ersatz bauen, kleiner, aber voll kompatibel. Schon alleine,
weil der gcc dann nette Warnungen ausgeben kann
Niklas G. schrieb:> In C++ wäre es ziemlich einfach mit variadischen templates.
Aber ob das Ergebnis dann wirklich ressourcenschonender wird als
snprintf()?
Jörg W. schrieb:> Aber ob das Ergebnis dann wirklich ressourcenschonender wird als> snprintf()?
Schon möglich. Hängt davon ab wie gut der Compiler es optimiert bekommt
(inlining & Co). Eventuell wird bei zahlreichen Aufrufen der Code
länger, aber die Ausführungszeit dürfte kürzer sein. Man könnte
alternativ auch den Format-String per templates automatisch generieren
und dann snprintf aufrufen ;-)
An den Integer-Convert-Funktionen kann man bestimmt auch noch was
optimieren, hab da einfach die erstbeste Implementierung hin
geschrieben. Aber das ist ein gelöstes Problem
Jetzt gibt es Zahlen über die Speicherbelegung.
> ohne snprintf: Program Memory Usage : 33284 bytes
Data Memory Usage : 6690 bytes
> mit snprintf: Program Memory Usage : 34796 bytes
Data Memory Usage : 6714 bytes
Ich habe das Makro P() dazugenommen. P() ist ein Makro, das den String
im PROGMEM platziert und einen const char* zurückgibt. Ich verwende es
so:
1
snprintf(jou,sizeof(jou),P("MOD RangeErr in %d: %d"),ZeilenNr,MOD_ix);
Dadurch reduziert sich der Verbrauch des RAM, und das ist ja der
Knackpunkt beim Arduino.
> mit P(): Program Memory Usage : 34810 bytes
Data Memory Usage : 6690 bytes
Der String in jou ist "MOD RangeErr in 1945: 1", so wie ich es möchte.
Niklas G. schrieb:> In C++ wäre es ziemlich einfach mit variadischen templates.
Najaaa, wenn ich mich recht entsinne, erzeugt der Compiler dann Code für
jede der im Quellcode vorkommenden Parametrierungen, oder?
Ein T. schrieb:> Najaaa, wenn ich mich recht entsinne, erzeugt der Compiler dann Code für> jede der im Quellcode vorkommenden Parametrierungen, oder?
Ja. Aber das sind nur ein paar Funktionsaufrufe. Die eigentliche Arbeit
passiert in den convert-Funktionen, welche nur 1x pro Typ erzeugt
werden.
Jörg W. schrieb:> Davor lohnt sich der Bau eigener Ersatzfunktionen eigentlich eher nicht,> zumal du ja immer das Risiko eingehst, statt relativ gut debuggter> Software eigene neue Fehler einzubauen.
Ja, das habe ich auch nur früher gemacht, wo Atmel noch recht geizig mit
Flash und RAM war (AT90S2313, AT90S4433, ATtiny15, ATtiny26).
Jörg W. schrieb:> Dann musst du aber auch snprintf_P() benutzen. Das ist die Variante der> Funktion, die den Format-String im Progmem erwartet.
Habe ich getestet: verbraucht mehr RAM als meine Version.
Reinhard schrieb:> Jörg W. schrieb:>> Dann musst du aber auch snprintf_P() benutzen. Das ist die Variante der>> Funktion, die den Format-String im Progmem erwartet.>> Habe ich getestet: verbraucht mehr RAM als meine Version.
Dann solltest du, statt hier irgendwie herum zu stochern, das genau
analysieren.
Format-String im Flash braucht die _P-Funktionen, sonst kommt Käse raus.
Die normale s[n]printf-Funktion hat schlicht keine Idee, dass der ihr
übergebene Zeiger in den Flash zeigen soll, sodass sie an der gleichen
Adresse im RAM stattdessen liest.
Intern geht das eh alles auf das gleiche Backend (vfprintf), das wird
nur mit unterschiedlichen Flags aufgerufen je nach dem benutzten
Frontend. Kannst du gut erkennen, wenn du dir den Sourcecode ansiehst:
1
% ls -sk
2
total 367
3
5 Files.am 5 fscanf.c 5 sprintf_p.c
4
45 Makefile 5 fscanf_p.c 5 sscanf.c
5
5 Makefile.am 5 fwrite.c 5 sscanf_p.c
6
49 Makefile.in 5 getc.S 5 stdio_private.h
7
5 Rules.am 5 getchar.c 9 ultoa_invert.S
8
5 clearerr.c 5 gets.c 5 ungetc.c
9
5 fclose.c 5 iob.c 21 vfprintf.c
10
9 fdevopen.c 5 printf.c 5 vfprintf_p.c
11
5 feof.c 5 printf_p.c 25 vfscanf.c
12
5 ferror.c 5 putc.S 5 vfscanf_p.c
13
5 fgetc.c 5 putchar.c 5 vprintf.c
14
5 fgets.c 5 puts.c 5 vscanf.c
15
5 fprintf.c 5 puts_p.c 5 vsnprintf.c
16
5 fprintf_p.c 5 scanf.c 5 vsnprintf_p.c
17
5 fputc.c 5 scanf_p.c 5 vsprintf.c
18
5 fputs.c 5 snprintf.c 5 vsprintf_p.c
19
5 fputs_p.c 5 snprintf_p.c 5 xtoa_fast.h
20
5 fread.c 5 sprintf.c
Gut zu erkennen, dass der komplette Code für stdio in den beiden
Implementierungen für vpfprintf.c und vfscanf.c steckt. Alles andere
sind Wrapper darum.
Jörg W. schrieb:> Reinhard schrieb:>> Jörg W. schrieb:>>> Dann musst du aber auch snprintf_P() benutzen. Das ist die Variante der>>> Funktion, die den Format-String im Progmem erwartet.>>>> Habe ich getestet: verbraucht mehr RAM als meine Version.>> Dann solltest du, statt hier irgendwie herum zu stochern, das genau> analysieren.>
Meine Güte, warum gleich so aggressiv? Arbeite mal dran.
Ich habe für mich die Lösung, die am wenigsten den RAM belastet. Warum
soll ich da weitere Zeit reinstecken?
Reinhard schrieb:>> Dann solltest du, statt hier irgendwie herum zu stochern, das genau>> analysieren.>>> Meine Güte, warum gleich so aggressiv? Arbeite mal dran.
Das war nicht aggressiv gemeint. Kommunikation über Computer ist halt
zuweilen missverständlich.
> Ich habe für mich die Lösung, die am wenigsten den RAM belastet. Warum> soll ich da weitere Zeit reinstecken?
Weil sie sehr wahrscheinlich Amok laufen wird (und damit keine "Lösung"
ist). Den Grund habe ich dir versucht zu erklären.
Jörg W. schrieb:> Weil sie sehr wahrscheinlich Amok laufen wird (und damit keine "Lösung"> ist).
Spekulation, oder hast du es selbst getestet? Ich stelle meinen Code
gerne zur Verfügung.
Bei mir funzt es bestens :-)
Reinhard schrieb:> Spekulation, oder hast du es selbst getestet?
Ich kann nichts testen, was ich nicht habe.
Ich habe nur versucht nachzuvollziehen, was du hier erzählt hast, und
ich weiß, wie der stdio-Code gestrickt ist, da ich insbesondere diesen
Teil mit den vfprintf/vfscanf-Backends und den diversen Wrappern selbst
entworfen habe (auch wenn Dmitry Xmelkov dann in den Backends vieles
optimiert hat).
> Bei mir funzt es bestens
Dann machst du was anderes als was ich deinen Ausführungen entnommen
habe.
Reinhard schrieb:> #define P(str) ({ \>> char buffer[strlen_P(str)]; \>> strcpy_P(buffer, PSTR(str)); \
OK, das erklärt es.
Das braucht aber trotzdem mehr RAM als die Lösung mit sprintf_P() – nur
auf dem Stack, dadurch fällt dir das nicht direkt auf. Es gibt aber
eigentlich keinen Grund, das erst in den RAM umzukopieren, da eben
vfprintf() in der Lage ist, den Format-String auch gleich direkt aus dem
Flash zu lesen.
Warum??? Es ist umständlich, gefährlich, langsam, braucht zusätzlichen
(Stack-/)Speicher. Einen stdio-Stream zu erstellen ist so trivial
(Beispiel-wrapper für uart_putc/getc):
fehlerhaft - es liefert eine Adresse, die beim Erreichen der "}"
ungültig wird. Dazu kommt noch, dass str mehrmals ausgewertet wird.
Und lokale var-arrays ohne Längenbeschränkung sind (insb auf Systemen
ohne dynamischen Stack, wie Mikrocontrollern) eh Buggy.
PS: ne +1 fehlt auch noch.
PPS: Zu meinen, allein durch Benutzung von "n"-Funktionen (snprintf,
strncpy, etc), "sicher" zu programmieren, ist ein Irrtum - man
verschiebt nur den Angriffsvektor: von Buffer overflow nach String
truncation.
Foobar schrieb:> es liefert eine Adresse, die beim Erreichen der "}" ungültig wird.
Das ist richtig und funktioniert deshalb nur zufällig. Lösung: das
char-Array "buffer" static machen.
Und siehe da: Jetzt sieht man den (versteckten) zusätzlichen RAM-Bedarf,
den Jörg prognostiziert hat, auch wieder :-)
Wie ich weiter oben schrieb: eigene Versuche produzieren meist eine
höhere Bug-Wahrscheinlichkeit, als wenn man gleich die "gut abgehangene"
fertige Implementierung benutzt. Allein der zusätzliche RAM-Bedarf für
das Umkopieren des Strings frisst ja den durch den im Flash platzierten
Format-String angestrebten Vorteil zur Hälfte schon wieder auf.
Ich finde ja das Konzept der generic Pointer beim Keil C51 genial.
Der Programmierer muß keinerlei Rücksicht darauf nehmen, in welchem
Segment eine Variable angelegt wurde. Ein zusätzliches Byte des Pointers
merkt sich den Segmenttyp und es wird automatisch zu dem richtigen
Befehl compiliert. Läßt sich das zur Compilezeit noch nicht auflösen,
wird eben eine Unterfunktion aufgerufen, die dann das Typ-Byte prüft und
entsprechend den Zugriff ausführt.
Ohne irgendwelche Sonderlocken schreiben zu müssen, wird der
Formatstring des printf automatisch im Flash abgelegt.
Hier mal ein Beispiel:
Peter D. schrieb:> Ich finde ja das Konzept der generic Pointer beim Keil C51 genial.
Außer halt, dass es Overhead kostet, sowohl im Zeiger selbst als auch
für die Auswertung zur Laufzeit. Ist aber wurscht: hat beim AVR keiner
implementiert und wird auch keiner mehr implementieren.
Jörg W. schrieb:> Außer halt, dass es Overhead kostet, sowohl im Zeiger selbst als auch> für die Auswertung zur Laufzeit.
Das hatte selbst bei 12MHz / 12 = 1MIPS nicht sonderlich gestört. Die
Ableserate des Menschen ist oftmals der Flaschenhals.
Peter D. schrieb:> Jörg W. schrieb:>> Außer halt, dass es Overhead kostet, sowohl im Zeiger selbst als auch>> für die Auswertung zur Laufzeit.>> Das hatte selbst bei 12MHz / 12 = 1MIPS nicht sonderlich gestört. Die> Ableserate des Menschen ist oftmals der Flaschenhals.
In diesem Falle schon, aber generic pointer waren ja ein allgemeines
Konzept, nicht nur hinsichtlich stdio, oder?
Aber wie schon geschrieben: wird bei AVR keiner mehr machen. Zu viel
Sonderlocke, und die Domäne für größere Flashs geht eh klar in Richtung
ARM oder RISC-V. Bei kleineren Flashs wiederum gibt es mittlerweile die
Option, den Flash in den normalen Adressbereich einzublenden, da braucht
man solche Verrenkungen dann auch nicht mehr.
Jörg W. schrieb:> Ist aber wurscht: hat beim AVR keiner> implementiert und wird auch keiner mehr implementieren.
Ja, für die ollen 8-Bitter macht keiner mehr was.
Blinklicht nicht unter 32Bit ist der neue Scheiß.
Johann L. schrieb:> Zumindest kennt der Compiler __memx, außer für reduced Tiny.
Müsste sich aber trotzdem jemand hinsetzen, dafür die
Bibliotheksfunktionen zu machen. Da fände ich 64-bit Gleitkomma in libm
deutlich interessanter. ;)
Peter D. schrieb:>> Ist aber wurscht: hat beim AVR keiner>> implementiert und wird auch keiner mehr implementieren.>> Ja, für die ollen 8-Bitter macht keiner mehr was.
Du musst mir die Worte nicht im Mund rumdrehen. Microchip konstruiert
nach wie vor neue AVRs am laufenden Band, aber sie müssen natürlich
Dinge produzieren, die sich auch verkaufen lassen. Da haben sie wohl
erkannt, dass es jenseits von 128 KiB Flash (die man noch ohne
irgendwelche Verrenkungen adressieren kann) keinen Sinn hat, auf
8-Bitter zu setzen.
Jörg W. schrieb:> Microchip konstruiert> nach wie vor neue AVRs am laufenden Band
Ja, das habe ich auch nicht verstanden, die 0-, 1-, 2-Serie mit nur
minimalen Unterschieden in schneller Folge rauszuhauen. Ich vermute
darin öfteren Führungswechsel, wo jeder seine Duftmarke setzen will.
Ich werde schon schief angesehen, wenn ich noch was mit dem AVR machen
will. Bisher eingesetzte Typen haben Bestandsschutz, weitere anzulegen
ist verboten.
Für das aktuelle Projekt ist ein i.MX RT1176 vorgesehen.
https://www.embeddedartists.com/products/imx-rt1176-ucom/
Peter D. schrieb:> Ich vermute darin öfteren Führungswechsel, wo jeder seine Duftmarke> setzen will.
Ich vermute, dass deine Vermutung reichlich daneben ist.
Die Entwicklung eines IC kostet Millionen – selbst, wenn man vielleicht
auf eine Art „Baukastensystem“ für die IP-Blöcke zurück greifen kann.
Diese Millionen muss der Chip dann auch erstmal einspielen, ansonsten
wäre ziemlich schnell Schicht im Schacht.
Das Maxim-Prinzip (viele Ideen auf MPWs und dann erst gucken, was sich
verkaufen lässt, den Rest wieder einstampfen) scheint bei Microchip nun
eher nicht vorzuherrschen.
Peter D. schrieb:> Ja, für die ollen 8-Bitter macht keiner mehr was.> Blinklicht nicht unter 32Bit ist der neue Scheiß.
Ob man generische Pointer jetzt für ein Blinklicht so dringend braucht,
ist dann die andere Frage.
Peter D. schrieb:> Für das aktuelle Projekt ist ein i.MX RT1176 vorgesehen.> https://www.embeddedartists.com/products/imx-rt1176-ucom/
Das ist aber ein ganz anderes Kaliber als ein AVR. Ich hoffe doch, dass
der nachher etwas mehr macht als ein Blinklicht 😉.
Jörg W. schrieb:> Die Entwicklung eines IC kostet Millionen
Da wundert es mich aber doch, warum sie in kurzer Folge die 8-Pinner
ATtiny12, 15, 22, 13, 25-85 rausgebracht haben. Ich habe sie alle
gekauft und mich geärgert, als die besseren verfügbar waren. Nun liegen
sie rum, außer dem ATtiny25, der alle Vorgänger ersetzen kann.
Und mit den neuen Serien geht dieses Spielchen ja munter weiter.
Nur der ATmega328PB ist irgendwie ein Ausreißer, da ist recht viel
hinzugekommen.
Peter D. schrieb:> Da wundert es mich aber doch, warum sie in kurzer Folge die 8-Pinner> ATtiny12, 15, 22, 13, 25-85 rausgebracht haben.
wundert mich auch also vermute ich
Jörg W. schrieb:> Die Entwicklung eines IC kostet Millionen
stimmt nicht oder ist egal wenn ein Manager seine Duftmarke setzt!
Man kann nicht mal so dumm denken wie manche Führungsetage ist!
Peter D. schrieb:> Da wundert es mich aber doch, warum sie in kurzer Folge die 8-Pinner> ATtiny12, 15, 22, 13, 25-85 rausgebracht haben.
Da schmeißt du aber einige Jahre, wenn nicht Jahrzehnte in einen Topf.
25 … 85 (die letztlich eine IC-Entwicklung erstmal sind) ist eine Art
Nachfolger / Ablösung des 15 gewesen, mehr oder weniger
aufwärtskomptabibel.
12 (und dessen Bruder 11) waren wohl die ersten Tinys überhaupt, mit dem
13 hat man dann ein deutlich universelleres Teil nachgeliefert, das bis
heute noch Bestand hat.
22 … keine Ahnung. Ich glaube, ich habe mal einen in den Fingern gehabt.
Möglich, dass der eher ein Verlustgeschäft für Atmel war damals. Auch
möglich, dass da ein einzelner großer Kunde als Auftraggeber dahinter
stand, sowas ist nicht ungewöhnlich in der IC-Industrie.
Joachim B. schrieb:> Man kann nicht mal so dumm denken wie manche Führungsetage ist!
Frage vom Abteilungsleiter:
"kennen sie sich aus mit Commodore mit kleiner Tastatur? (PET2001 mit
Datasette)"
Anwort vom Bewerber "ja (und meinte den Commodore Taschenrechner)"
Vom 6502 und Innenleben hatte er NULL Ahnung, auch nicht das man beim
Programmieren ENTER drückt und nicht mit den Cursortasten in die nächste
Zeile geht.
Ein anderer Entscheider stolz wie Bolle, er hat seinen ersten Dr.Ing
eingestellt, einer der Prüfautomaten und Programme baute die auch ohne
angeschlossenen Prüfling gut testeten!
Jörg W. schrieb:> Da schmeißt du aber einige Jahre, wenn nicht Jahrzehnte in einen Topf.
ATtiny22: 04/1999
ATtiny12: 10/1999
ATtiny15: 12/2001
ATtiny13: 06/2003
ATtiny25..85: 02/2005
Peter D. schrieb:> Jörg W. schrieb:>> Da schmeißt du aber einige Jahre, wenn nicht Jahrzehnte in einen Topf.>> ATtiny22: 04/1999> ATtiny12: 10/1999> ATtiny15: 12/2001> ATtiny13: 06/2003> ATtiny25..85: 02/2005
Keine Jahrzehnte, aber schon einige Jahre dazwischen. Bedenke auch, dass
die die Anfangszeit der AVRs war. Der AT90S1200 hatte das Feld
ausgelotet (kurz danach der ATmega103), ab da konnte man dann Feedback
von potentiellen und realen Kunden sammeln.
Dass eine Entwicklung einer kompletten MCU-Serie "from scratch" zu
Anfang defizitär ist, dürfte normal sein.
Reinhard schrieb:> Ich würde gerne wissen, welche Werte die va_list-Variablen args und> num_args auf diesem spezifischen System zurückgeben.
num_args kann so nicht verwendet werden, und eine Dokumentation /
Beispiel, die das so verwendet, kann du getrost in die Tonne kloppen.
Der Typ von args wird durch den 2. Parameter von va_arg festgelegt.
Am einfachsten verwendet man in solchen Fehlerausgaben die printf
Funktionen die Libc:
*diagnostic.h*
Der Format-String von error() muss dabei ein gültiges Argument für
PSTR() darstellen. Oder man lässt das PSTR für den Format-String weg,
aber dann belegt der String i.d.R. RAM.
[v]fprintf ist ein zielmlicher Brummer, und bei Platzproblemen -- die
man auf einem ATmega2560 eher weniger hat -- implementiert man ienfach
eine abgespeckte Version, die nur das nötogste mitbringt wie %s, %S, %d,
%u und %x.
Johann L. schrieb:> implementiert man ienfach eine abgespeckte Version, die nur das nötogste> mitbringt wie %s, %S, %d, %u und %x.
Wobei es ja schon eine abgespeckte Variante gibt, die man erstmal
stattdessen probieren kann. ;-)
Jörg W. schrieb:> Johann L. schrieb:>> implementiert man einfach eine abgespeckte Version, die nur das nötigste>> mitbringt wie %s, %S, %d, %u und %x.>> Wobei es ja schon eine abgespeckte Variante gibt, die man erstmal> stattdessen probieren kann. ;-)
Ja, aber das printf aus der Libc ist immer noch ziemlich fett. Das
liegt nicht zuletzt auch dran, dass viele Spezialitäten wie %*, %+, %-,
%0 etc unterstützt werden, die man meist nicht braucht; schon gar nicht
bei Fehlerausgabe.
Johann L. schrieb:> eine abgespeckte Version, die nur das nötogste mitbringt wie %s, %S, %d,> %u und %x.
Das nötigste, %S, echt jetzt? Benutzt hier tatsächlich jemand wchar_t?
Bauform B. schrieb:> Das nötigste, %S, echt jetzt? Benutzt hier tatsächlich jemand wchar_t?
RTFDoc, bevor du herum unkst.
%S macht auf dem AVR was anderes …
Ich bin kein Freund von Logging per printf/fprintf/snprintf…
Sie kosten einiges an Platz und die Formatierung zur Laufzeit braucht
Zeit und ist fehleranfällig (wobei es ja Compiler gibt, die
Forrmierstring Fehler erkennen.)
Wenn möglich würde ich (wie oben schon empfohlen) eher auf C++
variadische Template umsteigen. Das ist flexibler und
ressourcenschonender.
Unter C11 kann man so etwas auch per Makros und _Generic() nachbauen,
geht dann aber nur mit C-Compiler die C11 unterstützen und sieht
irgendwie gemurkst aus….
Für die schnelle Ausgabe von Messages wäre TRICE
(Beitrag "Open Source Projekt TRICE - ein Software-Tracer & Logger für alle µC: PIC, AVR, ARM, TI, Infinion,..") von Thomas Höhenleitner
noch empfehlenswert.
Klaus H. schrieb:>> Unter C11 kann man so etwas auch per Makros und _Generic() nachbauen,> geht dann aber nur mit C-Compiler die C11 unterstützen und sieht> irgendwie gemurkst aus….>
C11 kennt __flash nicht.
Reinhard schrieb:> Klaus H. schrieb:>>>> Unter C11 kann man so etwas auch per Makros und _Generic() nachbauen,>> geht dann aber nur mit C-Compiler die C11 unterstützen und sieht>> irgendwie gemurkst aus….>>>> C11 kennt __flash nicht.
Na dann eben GNU-C11
Klaus schrieb:
> Ich bin kein Freund von Logging per printf/fprintf/snprintf…>> Sie kosten einiges an Platz und die Formatierung zur Laufzeit braucht> Zeit [...]
Mit dem Platz ist relativ: bei vielen libc-stdio-Implementation zieht
die Benutzung von sprintf gleich das gesamte stdio-Subsystem mit rein -
bei shared-libs kein Problem, bei statischen Builds wird's eins, ja.
Der meiste Platz dürfte aber für die Float-to-String conversion drauf
gehen (eine qualitativ hochwertig Implementation frißt mehrere dutzend
kB, wenns schnell sein soll auch mal deutlich über 100kB).
Die avr-libc ist eine mMn sehr gute Implementation für µC - die
Grundroutinen sind schlank, sie ziehen keinen Rattenschwanz an
abhängigen Funktionen rein und die FP-version ist optional.
Und meist sieht es ja so aus: anfangs nur einen String ausgeben - wozu
printf, einfach println o.ä. Aber je mehr das Projekt wächst, umso mehr
steigen die Anforderungen und man schreibt sich immer mehr Hilfsroutinen
(für signed/unsigned dezimal-Zahlen, hex-Zahlen, fix-width, etc) und
bevor man sich versieht, hat man sprintf neu erfunden.
Und was die Geschwindigkeit angeht: printf ist nicht langsam! Das
Bottleneck ist üblicherweise die anschließende Ausgabe, nicht die
Formatierung (bei Ausgabe über UART sowieso).
> Wenn möglich würde ich (wie oben schon empfohlen) eher auf C++> variadische Template umsteigen. Das ist flexibler und> ressourcenschonender.
Flexibler mag sein, das mit dem ressourcenschonender möchte ich
bezweifeln. Statt in einer zentralen Formatierungsfunktion findet bei
den Templates die Formatierung bei jedem einzelnen Aufrufer statt.
Sieht kurz auf, es wird aber für jedes Argument extra Code generiert.
Das läppert sich ... Gewinnen kann man da nur, wenn die Argumente
statisch sind, so dass der Compiler das wegoptimieren kann.
Foobar schrieb:> Und meist sieht es ja so aus: anfangs nur einen String ausgeben - wozu> printf, einfach println o.ä. Aber je mehr das Projekt wächst, umso mehr> steigen die Anforderungen und man schreibt sich immer mehr Hilfsroutinen> (für signed/unsigned dezimal-Zahlen, hex-Zahlen, fix-width, etc) und> bevor man sich versieht, hat man sprintf neu erfunden.
Genau so siehts bei mir aus.
Foobar schrieb:> Flexibler mag sein, das mit dem ressourcenschonender möchte ich> bezweifeln. Statt in einer zentralen Formatierungsfunktion findet bei> den Templates die Formatierung bei jedem einzelnen Aufrufer statt.
Das kommt auf die Implementierung an. Wenn für jeden Parameter ein
(überladener) Funktionsaufruf genutzt wird, ist der Speicherplatz nicht
viel größer als bei ...printf Übergabe, man spart aber die Dekodierung
des Formatstrings und ist typsicher. Klar man hat mehrere
Funktionsaufrufe,
aber das parsen des Formatstrings kostet auch Zeit.