Ich hätte als 74-jähriger autodidaktischer C-Lehrling eine grosse Bitte!
Wäre jemand von euch Profis so nett, mir einen Ratschlag zu folgendem Programm zu geben, bei dem 3 Stk. 7-Segment-Anzeigen im Multiplexbetrieb über PORTB angesteuert werden sollen. Verwendet wird ein ATMega48.
Die Ausgabezahl ist vorgegeben fürs Erste.
Ich erhalte folgende Warnmeldungen:
return type of main is not 'int'
(verstehe ich zwar auf Grund der Berechnungen,aber wie korrigieren?)
und
unused variable 'segmenttable'
1
/* 7-SEGMENT-ANZEIGE Multiplexbetrieb mit drei 7-Segment-Anzeigen, CA,
2
Ansteuerung an PORTB0, PORTB1 und PORTB2 mit PNP Transistoren
return type of main is not 'int'
(verstehe ich zwar auf Grund der Berechnungen,aber wie korrigieren?)
Dein Compiler ist etwas hyperaktiv. :)
void main (void)
Du sagst hier das dein main keinen Wert zurueckliefert. Das machst ja auch nicht und das macht embedded auch nicht viel sinn. In PC-Umgebungen liefern Programm aber ueblicherweise einen Rueckgabewert wenn sie sich beenden.
Also ignorieren, abschalten oder auf int wechseln.
unused variable 'segmenttable'
Du definierst hier:
const uint8_t segmenttabel[11] =
Eine Variable und verwendest aber: segmenttable
Das mag ja fuer menschliche Gehirne aehnlich aussehen unterscheidet sich aber doch.
Danke euch schön. Werde weiter testen.
Ich habe wirklich herumgegoogelt wie der Böse, konnte aber zu keinem positiven Resultat gelangen.
Wenn ich am Kopf die Deklaration von 'segmenttable' weglasse, komme ich zu einer Fehlermeldung, aber ich werde schauen, dass ich mit eurer freundlichen Hilfe weiterkomme.
Gruss,
Gerhard
Wenn ich am Kopf die Deklaration von 'segmenttable' weglasse, komme ich
zu einer Fehlermeldung, aber ich werde schauen, dass ich mit eurer
freundlichen Hilfe weiterkomme.
Du hast die Variable zweimal erzeugt, versehendlich, mit ähnlichem Namen und verwendest die Falsche. Nenne deine Variable mal in Segemts um, evtl. siehst du es dann ;)
Danke für die Hinweise.
Die falsche Schreibweise ist mir in der Tat nicht aufgefallen, Schande. Passiert mir sonst so gut wie nie.
Leider hatte ich bisher trotzdem keinen Erfolg.
return(0); ergibt die Meldung:
'return' with a value, in function returning void
Ferner erhalte ich folgende Warn- und Fehlermeldungen:
near initialization for 'segmenttable'
excess elements in scalar initializer
subscripted value is neither array nor pointer nor vector
variable 'segmenttable' set but not used
Danke für die Hinweise.
Die falsche Schreibweise ist mir in der Tat nicht aufgefallen, Schande.
Passiert mir sonst so gut wie nie.
Leider hatte ich bisher trotzdem keinen Erfolg.
return(0); ergibt die Meldung:
'return' with a value, in function returning void
Leider hast Du die obigen Hinweise nicht beachtet.
Ferner erhalte ich folgende Warn- und Fehlermeldungen:
near initialization for 'segmenttable'
excess elements in scalar initializer
subscripted value is neither array nor pointer nor vector
variable 'segmenttable' set but not used
Du definierst eine Tabelle mit 11 Elementen für die Segmente, obwohl du nur 10 Ziffern hast.
Deine Segmentanordnung darin ist mir nicht wirklich transparent.
Jedesmal erst über PORTB den neuen Digit-Treiber zu aktivieren, während der alte Segmentstatus noch aktiv ist, bringt "Ghosting". Besser am Ende der Zeitscheibe für eine Stelle erstmal alle Stellen ausschalten (das wäre bei dir PORTB=0xff;), dann die neue Segmentbelegung aktivieren, dann erst den Digit-Treiber dafür einschalten.
Kein Thema, das kann schnell passieren. Jedoch musst du, wenn du in einer Funktion (in diesem Fall main) etwas zurückgeben willst (return 0) auch den Rückgabetyp entsprechend anpassen, in dem Fall den Rückgabetyp von Main, wurde oben schon erwähnt.
Das anhand des Main() zu erklären ist echt ein blödes Beispiel für den Einstieg in die Gestaltung von Funktionen in C.
Beispiel:
uint8_ t Addiere (uint8_t a, uint8_t b)
{
uint8_t ergebnis=0;
ergebnis=a+b;
return ergebnis;
}
du kannst dann zb im main code schreiben:
void main (void)
{
uint8_t variable=0;
...
variable=Addiere(7,8);
...
}
Die Funktion Addiere() gibt einen Wert zurück...
Main wird auch als Funktion "gesehen" und gibt u.u etwas ans System zurück... Das ist aber für deine uc Anwendung völlig irrelevant...deswegen ein schlechtes Beispiel...
Makros, die direkt irgendein Ereignis bewirken (hier: eine Ausgabe auf Port B) schreibt man besser in der funktions-ähnlichen Schreibweise, also mit Klammern. Dann steht da auch im Code selbst das Klammernpaar, und man weiß beim Draufgucken sofort: "Da passiert direkt was."
An die Mimik mit "&= ~(1 << bitnummer)" und "|= (1 << bitnummer)" zum Löschen und Setzen von Bits sollte man sich gewöhnen, das ist völliger Standard in diesem Umfeld.
Falls du eine bereits fertige Lösung benötigst, melde dich nochmal. Ich möchte dir jedoch nicht den Ehrgeiz und das Erfolgserlebnis für eine eigene Lösung wegnehmen, sonst hätte ich es direkt angehängt.😀
Ich habe für ein dreistelliges LED-Display ein kleines Progrämmchen einem MEGA8 in Verwendung - genau deine Anforderung. Müsste sogar auf dem MEGA48 direkt laufen (ungeprüft), es wird nichts besonderes verwendet.
Eigenschaften:
mit Jumper ist eine invertierte Darstellung wählbar (STK500)
mit Jumper für die Auswahl der Darstellung in DEZ oder HEX bzw. BCD
Unterdrückung führender Nullen bei DEZ
für Common-Anode Display, Spaltentreiber mit PNP-Transistoren
Segmente werden direkt über den PortC angesteuert
8 Bit Input auf PortB
Spaltenansteuerung und Lesen der Jumper über PortD
(Portbelegung ist etwas anders als bei dir.)
Ich verwende es u.a. für die leichtere Interpretierbarkeit anstelle der acht LEDs am STK500 und bei anderen Debugaufgaben.
In deinem Programm hat die Funktion zerlege_wert() eigentlich auch drei Rückgabewerte (einer, zehner und hunderter)
In C kann eine Funktion aber nur einen Rückgabewert haben. In deiner Lösung "schummelst" du mit den drei globalen Variablen, das sollte man wenn möglich immer vermeiden.
Es gibt viele Möglichkeiten, eine wäre die Funktion in drei Funktionen zu zerlegen:
In deinem Main könntest du sie dann so nutzen:
void main (void)
{
uint8_t einer=0;
...
einer = gibEiner(display_value);
...
PORTD = segmenttable[einer];
...
}
oder sogar direkt ohne den Umweg über die Variable "einer":
void main (void)
{
...
PORTD = segmenttable[gibEiner(display_value)];
...
}
Das geht beides in C, du kannst das theoretisch alles in eine Zeile schreiben, jedoch ist es irgendwann nicht mehr lesbar :-)
In deinem Main könntest du sie dann so nutzen:
void main (void)
Vielleicht solltest Du hier mal auch wirklich ein
1
intmain(void)
hinschreiben. Weil nur dies (und andere Signaturen) standardisiert sind.
Zwar ist eine Implementierung frei weitere Startpunkte eines Programm festzulegen, der gcc macht das aber erstmal nicht und deswegen gibt es die Warnung.
mit einem "int main(void)" kompiliert es dann vollkommen
fehlerfrei.
Für dich wäre es zuerst wichtig zu verstehen was der Rückgabewert einer
Funktion überhaupt ist.
Bei uC main() und gcc wäre Deine Erklärung tatsächlich hilfreich für mich, warum gcc auf eine Rückgabe Erklärung besteht.
main.cpp:30:16: error: '::main' must return 'int'
void main (void)
Das Ende von main() wird in der Regel bei uC nie erreicht. Andere uC Compiler die ich verwende, haben damit auch kein Problem (PIC, STM32). Wenn gcc darauf besteht, dann mache ich es eben, damit das Meckern aufhört. Ich sehe da keinerlei Gefahren. Aber die Leute vom gcc werden schon ihre guten Gründe haben...
In deinem Programm fehlt nun der Rückgabewert, also das return()
Schau dir mal mein Beispiel dazu an:
Beitrag "Re: 7-Segment-Ansteuerung"
Der gcc 5.4.0 braucht übrigens nur 498 bytes
Der gcc 7.3.0 braucht 586 bytes für das selbe Programm
Delays über Delays im Code und keiner verliert ein Wort darüber!
Ich glaube, bei einer LED-Siebensegment-Anzeige brauchst du nicht drüber zu diskutieren, ob der Controller jetzt noch 5 mA sparen kann, indem er während der Wartezeit schläft. ;-)
Hmmm... was'n hier los?
Delays über Delays im Code und keiner verliert ein Wort darüber!
Das ist doch sonst, der Trigger schlecht hin!?-O
Moin,
Ich finde es in diesen Fall folgerichtig, weil es von Gerhard nur ein einfachstes Testprogramm sein soll, um die HW zu testen. Finde ich absolut nichts falsch daran.
In einer realen Anwendung mache ich es sonst immer als Timer-ISR im Hintergrund ohne irgendwelche Delays. Da braucht main() dann nur eine leere „Erhaltungsschleife“ enthalten und man kann im restlichen Code machen was man will, ohne den Display Scan zu stören.
Abgesehen davon sind viele von uns im Forum, einschließlich yours truly, nur "Hobby Programmierer". Nur industriell/professionelle Maßstäbe anlegen zu wollen, ist unangebracht. Viele Wege führen nach Rom; mehr oder weniger holprig.
Noch etwas, auch wenn es bei gcc üblich ist hartnäckig auf einen Rückgabewert der main() zu bestehen, gibt es viele embedded Werkzeuge, denen das gleich ist und beides akzeptieren. Gcc ist nicht das Maß aller Dinge. Etwas Recherche ergab, dass man es zwar machen soll, einfach, weil es technisch gesehen, korrekter sein soll. Ich bin kein professioneller Programmierer, der die ganzen gcc Unterlagen studiert hat. Wenn es für mich so, wie erwartet, in traditioneller Weise funktioniert, reicht es mir aus. Ich habe jahrelang mit nicht-gcc Werkzeugen gearbeitet, wo noch andere Maßstäbe geherrscht haben und bin diese Ära gewöhnt. Auch gcc wurde vielfach revidiert. Mit jeder neuen Generation gibt es subtile Veränderungen, die oft alten Code brechen. Was ich damit sagen will ist, dass man nicht immer vom hohen Pferd herunter „schimpfen“ soll. Mir ist für alte Projekte auch langfristig Rückwerts-Kompatibilität wichtiger, als den neuesten Mode-Sprachenststandard zu folgen. Vieles, was vor 30 Jahren in C durchging, wird heute nicht mehr gern gesehen bzw. erlaubt. Es gibt noch viele Anwendungen, wo man in freiem Stil Probleme lösen kann und funktionierende Anwendungen auf den Tisch stellen kann.
OK, jetzt dürft ihr meine gebührenden Minuspunkte auf mich ausschütten:-)
Hmmm... was'n hier los?
Delays über Delays im Code und keiner verliert ein Wort darüber!
Das ist doch sonst, der Trigger schlecht hin!?-O
Moin,
Ich finde es in diesen Fall folgerichtig, weil es von Gerhard nur ein
einfachstes Testprogramm sein soll, um die HW zu testen. Finde ich
absolut nichts falsch daran.
Eben, was hat hier, dieser RAM geifernder Optimierungs-Hickack, zu suchen?!
OK, jetzt dürft ihr meine gebührenden Minuspunkte auf mich
ausschütten:-)
Nein, überhaupt nicht. Ich stimme dir in (fast) allem zu!
Ob ein 'delay' böse ist oder nicht, kommt ganz auf den Anwendungsfall an. Entweder es stört oder eben nicht. Soweit kenne ich meine Programme schon, dass ich das entscheiden kann. Das Ergebnis zählt!
Zum anderen Punk: Der Compiler besteht nicht auf 'int main ()', er warnt nur.
Das kann man bereinigen, aber auch einfach die Warnung lesen und abnicken. Es passiert nichts...
Das kann man bereinigen, aber auch einfach die Warnung lesen und
abnicken. Es passiert nichts...
Man kann aber auch einfach den Scheiß so hinschreiben daß der Compiler Ruhe gibt! Einen Compilerdurchlauf ohne Warnungen sollte man anstreben, erst recht, wenn es SOOO einfach machbar ist!
OK, jetzt dürft ihr meine gebührenden Minuspunkte auf mich
ausschütten:-)
Nein, überhaupt nicht. Ich stimme dir in (fast) allem zu!
Ob ein 'delay' böse ist oder nicht, kommt ganz auf den Anwendungsfall
an. Entweder es stört oder eben nicht. Soweit kenne ich meine Programme
schon, dass ich das entscheiden kann. Das Ergebnis zählt!
Zum anderen Punk: Der Compiler besteht nicht auf 'int main ()', er warnt
nur.
Das kann man bereinigen, aber auch einfach die Warnung lesen und
abnicken. Es passiert nichts...
Moin,
Im Arduino IDE weigert er sich und beendet den Build process. Ob man das mit Einstellungen vermeiden könnte, ist ohne Recherche nicht zu beantworten. Andrerseits ist es ja in Ordnung, die main(), so wie der Compiler es wünscht, zu formulieren.
Mir war das nur ungewohnt, weil andere, ältere Compiler für PIC, CORTEX, IAR, Keil, T.I. mit denen ich vertraut bin, nicht darauf bestanden haben und es mir im embedded Bereich in über 25 Jahren auch niemals begegnete. Abgesehen davon wurde das in ihren Beispielen in den Handbüchern auch mit void deklariert. Ist eben eine gcc Sache. Ich war aber ein bisschen verärgert, weil man mich hier deswegen etwas angemacht hatte. Gcc ist für mich noch neu. Da ich gcc nur im Arduino Bereich verwende, sehe ich auch seine main() Funktion fast nie.
Ich habe natürlich die Angelegenheit danach etwas recherchiert und der Konzensus der Experten ist, main() immer als int zu deklarieren, weil es angeblich so professioneller ist, auch wenn es oberflächlich gesehen, sinnfrei scheint. Die meisten Compiler entscheiden ohnehin selber was unter der Haube gemacht werden muß.
Intuitiv ist es etwas wunderlich in embeddete Anwendungen die main() Funktion mit einem int zu deklarieren. Aber es ist für mich in Ordnung wenn das Werkzeug darauf besteht - eben halt wieder etwas dazu gelernt. Es besteht kein Grund für eine Bockigkeit:-)
Aber wie hätte ich es auch wissen können? Das passierte mir erstmalig gestern. Und ich verdiene mir mit Programmieren nicht meine Brötchen und ich mache das nur als Hobby. Im embedded Bereich macht es auch noch viel Freude mit uC etwas auf die Beine zu stellen. Ich bin da etwas mehr HW orientiert.
Zwar gestattet der C-Standard auch, dass andere implementierungsabhängige Formen von main() akzeptiert sein können (deshalb kann dir Microchip eben die Sache mit "void" nahelegen). Auch der GCC besteht nur mit dem (voreingestellten) -fhosted darauf, denn er existiert für so viele verschiedene Plattformen, dass er schlicht keine Ahnung hat, dass deine Umgebung tatsächlich in einer Endllosschleife enden wird. Nimmt man -ffreestanding, dann ist ihm der Prototyp von main() egal, allerdings nimmt er dann wirklich gar keine Optimierungen mehr vor, die er für ein "hosted environment" vornehmen darf. Dann wird eben auch für
1
i=strlen("foo");
wirklich die Funktion strlen() aufgerufen, statt da gleich eine 3 einzusetzen.
Insofern sind heutige Embedded-Umgebungen eben gar nicht so sehr "freestanding", wie man das auf den ersten Blick erwarten würde. Die mitgelieferten Bibliotheken und restliche Teile der Umgebung orientieren sich, soweit irgend möglich, am C-Standard und verhalten sich dann auch genau so, sodass der Unterschied zu einem hosted environment minimiert wird.
Gerhard O. hat andere Themen die er hier diskutiert und damit alle verwirrt vielleicht sogar inkl. Gerhard H. der ja noch alles durch akkert.
Gerhard O. könnte ja auch einen Thread auf machen und dort seine Bytes zu zählen, auch um den blutigen Anfänger hier nicht noch mehr zu verwirren, aber vielleicht ist ihm das zu Bytelastig :-)
Ich glaube, bei einer LED-Siebensegment-Anzeige brauchst du nicht drüber
zu diskutieren, ob der Controller jetzt noch 5 mA sparen kann, indem er
während der Wartezeit schläft. ;-)
Das ist nicht der Punkt.
Niemand will eine Konstante anzeigen, sondern einen Wert, z.B. von einem DS18B20 Temperatursensor. Und dann geht mit dem Delay das große Flackern los. Delay ist also immer eine Sackgasse.
Statt erstmal Babysprache zu lernen, es gleich richtig zu machen (Timerinterrupt) spart jede Menge Zeit.
War das mit dem "Ghosting" eigentlich geklärt?
Ich finde so die logischen Abläufe zu klären in jenem Falle wichtiger, als sich über delays etc. aufzuregen.
(LED-Matrix 16x10)ich hatte einen 1 aus 10 Decoder für die Zeilen und hab dann immer die aktuelle zeile leuchten lassen und dann die "Zeile11" adressiert, die es nicht gab, die neuen Zeileninformationen (2x8bit) geladen und DANACH erst die anzuzeigende Zeile adressiert. Hier sind es eben die Dezimalstellen.
Statt erstmal Babysprache zu lernen, es gleich richtig zu machen
(Timerinterrupt) spart jede Menge Zeit.
Solange er sich noch damit rumschlägt, die Grundlagen der Sprache zu
erlernen, brauchst du noch nicht mit Interrupts daher kommen.
Das mit dem Delay ist doch gut für den Anfang. Danach kann er vllt einen Zustandsautomat bauen für die Anzeigephasen des Displays. Dann braucht er nur noch einmal Delay, und dann ... kann er einen Timer nehmen.
Ich widerspreche der Aussage ja nicht, dass man es so machen soll. Alle meine Programme haben 'int main(void)'!
Wichtig ist aber auch, dass man weiß, was für eine Auswirkung eine solche Warnung hat. Das wusste Gerhard H. wohl noch nicht.
Anderes Beispiel:
während der Programmentwicklungsphase kann es durchaus vorkommen, dass man eine Variable definiert hat aber (zunächst) noch nicht verwendet. Gefahrlos, denn wenn der Grund ein Tippfehler war, dann taucht ein Error auf bei der versuchten Verwendung der falsch geschriebenen Variable.
Andererseits ist ein Lesen einer definierten Variablen ohne vorherige Wertzuweisung auch 'nur' eine Warnung, das aber meist funktionale Auswirkungen haben wird.
Ich widerspreche der Aussage ja nicht, dass man es so machen soll. Alle
meine Programme haben 'int main(void)'!
Wichtig ist aber auch, dass man weiß, was für eine Auswirkung eine
solche Warnung hat. Das wusste Gerhard H. wohl noch nicht.
Als Programmiranfänger in C oder C++ sollte man keine Warnung zulassen, am besten mit -Werror arbeiten. Denn der Anfänger kann meistens nicht entscheiden, ob das wichtig ist, zu IB oder UB führt.
Anderes Beispiel:
während der Programmentwicklungsphase kann es durchaus vorkommen, dass
man eine Variable definiert hat aber (zunächst) noch nicht verwendet.
Gefahrlos, denn wenn der Grund ein Tippfehler war, dann taucht ein Error
auf bei der versuchten Verwendung der falsch geschriebenen Variable.
Andererseits ist ein Lesen einer definierten Variablen ohne vorherige
Wertzuweisung auch 'nur' eine Warnung, das aber meist funktionale
Auswirkungen haben wird.
Nicht nur das: es ist UB!
Daher hatte ich ja auch schon gesagt, das Objekte primitiver DT immer expilizit initialisiert werden müssen.
Ich hab früher viel mit TTL-ICs gebastelt. Da der 7-Segment Decoder P147 in der DDR schweineteuer war, hab ich die Anzeigen gemultiplext.
Dann später auf dem 8051 habe ich dafür automatisch den Timerinterrupt benutzt.
Auf die Idee mit dem Delay bin ich gar nicht erst gekommen. Es schien mir von Anfang an viel zu riskant, das nebenwirkungslos mit den Aufgaben in der Mainloop verwursten zu können. Auch hatte ich Angst, daß bei Fehlern in der Mainloop die Anzeigen durchbrennen könnten.
Auch hatte ich Angst, daß bei
Fehlern in der Mainloop die Anzeigen durchbrennen könnten.
das geht zB. bei ner VQC10 sehr schnell, dass die da ne komplette Zeile wegbrennt, wenn in der Software was schief läuft, beim entwickeln oder beim "Messen".
Aber: nimmt ja eh niemand mehr
Für mich ist dieses Thema nun ohnehin abgehakt, weil ich jetzt weiß, warum der Arduino Build Script so eingestellt ist und richte mich eben danach, wenn ich mit gcc in der Weise arbeite.
Ich hätte ja noch was in dem C-Buch Thread geschrieben, der aber auf wundersame Weise mal wieder für Gäste gesperrt ist.
Es ist hilfreich, ein gutes Nachschlagewerk zum Thema Digitaltechnik parat zu haben.
Digitaltechnik von Beuth ist ziemlich gut, aber es gibt auch noch andere.
Auch hatte ich Angst, daß bei
Fehlern in der Mainloop die Anzeigen durchbrennen könnten.
das geht zB. bei ner VQC10 sehr schnell, dass die da ne komplette Zeile
wegbrennt, wenn in der Software was schief läuft, beim entwickeln oder
beim "Messen".
Aber: nimmt ja eh niemand mehr
Ich hätte ja noch was in dem C-Buch Thread geschrieben, der aber
auf
wundersame Weise mal wieder für Gäste gesperrt ist.
Es ist hilfreich, ein gutes Nachschlagewerk zum Thema Digitaltechnik
parat zu haben.
Digitaltechnik von Beuth ist ziemlich gut, aber es gibt auch noch
andere.
Beitrag "Empfehlenswerte Literatur zum Erlernen von C"
Dankeschön!
Gruß,
Gerhard
In diesen Thread scheint ein (grantiger und kleinlicher) Minusmann herumzugeistern. Man darf sich nicht einmal mehr bedanken, ohne angefahren zu werden.
In diesen Thread scheint ein (grantiger und kleinlicher) Minusmann
herumzugeistern. Man darf sich nicht einmal mehr bedanken, ohne
angefahren zu werden.