Hallo zusammen,
seit langer Zeit wollte ich mal wieder etwas programmieren und merke
nun, dass ich die Grundlagen nicht mehr beherrsche ...
Ich habe ein STK500 mit einem Atmega8, Taster and PC, LED an PD und
lasse darauf die folgenden Zeilen laufen (inzwischen auf das Kernproblem
vereinfacht):
1
intmain(void)
2
{
3
DDRD|=(1<<DDD0);// Pin 0 von Port D als Ausgang
4
PORTD|=(1<<PD0);// PD0 im PORTD setzen
5
6
for(;;){// main loop
7
}
8
return(0);
9
}
Ergebnis: wenn ich "PORTD |= (1 << PD0);" weg lasse brennt die LED0,
warum?
Nächster Versuch:
1
intmain(void)
2
{
3
DDRD|=(1<<DDD0);// Pin 0 von Port D als Ausgang
4
PORTD|=(1<<PD0);// PD0 im PORTD setzen
5
PORTC|=(1<<DDC0);// internen Pull-Up an PC7 aktivieren
6
7
for(;;){// main loop
8
9
if(PINC&(1<<PINC0))
10
PORTD=PIND^(1<<PD0);// LED an Port PD0 an- bzw. aus
11
12
}
13
return(0);
14
}
Ergebnis: Die LED brennt wieder, auch wenn ich die Tast PC0 nicht
gedrückt habe, warum?
Wahrscheinlich fällt das unter die Rubrik "hätte ich auch selber drauf
kommen können", aber ...
Vielen Dank schon mal für einen Tip, wie ich hier weiter komme!
Gruß
Thomas
Thomas schrieb:> Ergebnis: wenn ich "PORTD |= (1 << PD0);" weg lasse brennt die LED0,> warum?
Ist Dein RS232 vom STK500 verbunden? PD0 ist beim ATmega8 RX-Pin.
Thomas schrieb:> Nächster Versuch:
Bitte setze bei weiteren Versuchen selbständig die [c] Tags um deinem
Code. So wie es über jeder Eingabebox beschrieben ist.
Thomas schrieb:> Ergebnis: wenn ich "PORTD |= (1 << PD0);" weg lasse brennt die LED0,> warum?
Wie ist die LED am Portpin angeschlossen?
> Ergebnis: Die LED brennt wieder, auch wenn ich die Tast PC0 nicht> gedrückt habe, warum?
Wie ist der Taster angeschlossen? Und was passiert, wenn du den Taster
drückst?
Merke: es ist sinnvoll, den Pegel an den Portpins mit einem Messgerät
gegen Masse zu messen und sich nicht auf "sekundäre" Elemente wie "LED
leuchtet" oder "Taster ist gedrückt" zu verlassen. Es kann nämlich sein,
dass die LED leuchtet, wenn der Pin low ist. Oder dass am Pin ein low
anliegt, wenn der Taster gedrückt ist.
@Walter: ja, RS232 ist verbunden, macht aber keinen Unterschied wenn ich
denn Stecker abziehe.
@Lothar:
[c] Tags ... Eingabebox ? Was meinst Du?
Die LEDs sind auf dem STK500 mit dem PORTD über das Flachbandkabel
verbunden. Genauso die Taster mit PORTC. Beides funktioniert auch
einwandfrei, z.B. mit dem Entprellprogramm von Peter D.
Solange ich den Taster gedrückt halte geht die LED aus. Wenn ich im
falschen Moment drücke, bleibt sie an, aber das ist ja auch ok so.
Lies einfach beim nächsten Posten mal den Text, der über Deinem
Eingabefenster steht, siehe auch Screenshot.
Auf Deutsch: Du sollst beim nächsten Mal Deinen C-Code in
1
[c]......[/c]
einbetten, damit man ihn auch lesen kann. Sehr wichtig für
Proportionalschrift-Leser.
Thomas schrieb:> Ergebnis: Die LED brennt wieder, auch wenn ich die Tast PC0 nicht> gedrückt habe, warum?
Sie leuchtet aber nur mit 50% Helligkeit.
Thomas schrieb:> Ergebnis: wenn ich "PORTD |= (1 << PD0);" weg lasse brennt die LED0,> warum?
Ein Blick in das Manual des STK500 zeigt, daß Tasten und LEDs low aktiv
sind.
Frank M. schrieb:> Auf Deutsch: Du sollst beim nächsten Mal Deinen C-Code ...> einbetten, damit man ihn auch lesen kann.
Und gratis gibts dazu dann noch das Hochlicht namens
"Syntax-Highlighting".
Thomas schrieb:> Die LEDs sind auf dem STK500 mit dem PORTD über das Flachbandkabel> verbunden. Genauso die Taster mit PORTC.
Das war es nicht, was ich mit "Beschaltung" gemeint habe.
Sondern ich meinte, bei welchem Pegel am Pin die LED leuchtet: leuchtet
sie, wenn du 0/low/0V ausgibst, oder leuchtet sie wenn du 1/high/5V
ausgibst?
Und welchen Pegel bekommst du am Pin, wenn du den Taster drückst? Und
welchen, wenn du den Taster nicht drückst.
Und wenn du das herausgefunden hast, dann siehst du dir den Schaltplan
an und überlegst, ob das alles soweit zusammenpasst.
> Solange ich den Taster gedrückt halte geht die LED aus. Wenn ich im> falschen Moment drücke, bleibt sie an, aber das ist ja auch ok so.
Wenn du dieses Verhalten von deinem Code erwartest, dann ist ja alles
gut. Auf jeden Fall macht der µC das, was du programmiert hast. Auch
wenn sich das nicht unbedingt mit dem deckt, was du erwartest.
Thomas schrieb:> for(;;) {> // main loop> }
Die leere Main loop ist nicht notwendig, denn der Compiler fügt sie ggf.
automatisch in den Maschinencode ein. Wenn da aber sinnvoller Code drin
steht, ist sie natürlich notwendig.
> return (0);
Das return Statement ist nutzlos, weil die main() Funktion niemals
endet.
Stefan ⛄ F. schrieb:> Das return Statement ist nutzlos, weil die main() Funktion niemals> endet.
Aber wenn man eine Funktion definiert die einen Return-Wert
ergibt dann schreibt man "anständigerweise" auch eine
Return-Operation hin. Bei nicht-main Funktionen geht das
Weglassen sowieso nicht.
Ältliche und voll-warnende Compiler melden an dieser Stelle
zumindest eine Warning wenn nix zurückgegeben wird. YMMV.
Stefan ⛄ F. schrieb:> Die leere Main loop ist nicht notwendig, denn der Compiler fügt sie ggf.> automatisch in den Maschinencode ein.
Eine leere Endlosschleife ist prinzipiell bei µCs richtig, um den µC
gezielt an einer Stelle anzuhalten.
Dass bei AVRs tatsächlich nach Verlassen der main-Funktion eine
Endlosschleife ausgeführt wird, ist hier zufälligerweise richtig. Denn
es ist überhaupt nicht garantiert bei irgendewelchen
µC-Laufzeitumgebungen. Von daher halte ich Deinen Hinweis für
überflüssig - überflüssiger jedenfalls als die Endlosschleife selbst.
Abgesehen davon ist Deine Aussage in der Allgemeinheit sogar falsch.
> Das return Statement ist nutzlos, weil die main() Funktion niemals> endet.
Da die main()-Funktion vom Typ int ist, geziemt es sich durchaus, auch
ein formales return-Statement vorzusehen - auch wenn neuere C-Standards
mittlerweile fordern, dass ein fehlendes return in der main-Funktion
(und nur in der main-Funktion!) implizit vom C-Compiler hinzugefügt
wird. Aber das macht ein selbst eingefügtes return-Statement *nicht
falsch*.
F*ck schrieb:> Ältliche und voll-warnende Compiler melden an dieser Stelle> zumindest eine Warning wenn nix zurückgegeben wird.
Und moderneren kann man ein "No Return" Attribut mitgeben.
Stefan ⛄ F. schrieb:> Die leere Main loop ist nicht notwendig, denn der Compiler fügt sie ggf.> automatisch in den Maschinencode ein.
Blanker Unsinn!
Quelle:
Arduino Fanboy D. schrieb:> Blanker Unsinn!
Halber Unsinn, sie wird nach dem Return ausgeführt ;-)
"Eingefügt" wird sie jedenfalls nicht vom Compiler, denn sie ist mit
drin in der avr-libc. Von daher ist es natürlich Unsinn, was Stefanus
schreibt.
Frank M. schrieb:> sie wird nach dem Return ausgeführt ;-)
Du meinst:
1
00000086<_exit>:
2
86:f894cli
3
4
00000088<__stop_program>:
5
88:ffcfrjmp.-2;0x88<__stop_program>
Wo dann auch der Return landet.
1
74:0e944000call0x80;0x80<main>
2
78:0c944300jmp0x86;0x86<_exit>
Das hat aber nichts mit main() und einfügen zu tun.
Das Ende ist immer da.
Und nicht
Stefan ⛄ F. schrieb:> ggf.
"Immer" ist nicht ggf.
Frank M. schrieb:> Von daher ist es natürlich Unsinn, was Stefanus> schreibt.
So ist es.
F*ck schrieb:> Aber wenn man eine Funktion definiert die einen Return-Wert> ergibt dann schreibt man "anständigerweise" auch eine> Return-Operation hin. Bei nicht-main Funktionen geht das> Weglassen sowieso nicht.>> Ältliche und voll-warnende Compiler melden an dieser Stelle> zumindest eine Warning wenn nix zurückgegeben wird. YMMV.
Dann würden sie aber etwas monieren, was im Standard ausdrücklich
vorgesehen ist:
"The body of the main function does not need to contain the return
statement: if control reaches the end of main without encountering a
return statement, the effect is that of executing return 0;"
https://en.cppreference.com/w/cpp/language/main_function
Frank M. schrieb:> Dass bei AVRs tatsächlich nach Verlassen der main-Funktion eine> Endlosschleife ausgeführt wird, ist hier zufälligerweise richtig. Denn> es ist überhaupt nicht garantiert
Es ist garantiert, siehe oben.
Arduino Fanboy D. schrieb:> Blanker Unsinn!Frank M. schrieb:> Von daher ist es natürlich Unsinn, was Stefanus> schreibt.
Ihr seid ja komisch drauf. Habt gerade meine Aussage bestätigt* aber
nennt sie immer noch Unsinn. Tssss
*) Ich schrieb:
Stefan ⛄ F. schrieb:> Die leere Main loop ist nicht notwendig, denn der Compiler fügt sie ggf.> automatisch in den Maschinencode ein.
Ich habe nicht geschrieben, dass der code in die main()-Funktion
eingefügt wird.
Ja, die Endlosschleife wird nicht in main() eingefügt, sondern direkt
hinter der Stelle, wo sie aufgerufen wurde. Das ist natürlich ein
Himmwelweiter - nein ein Galaxieweiter - Unterschied.
Man kann sich auch die Hose mit der Kneifzange anziehen.
Stefan ⛄ F. schrieb:> *) Ja, die Endlosschleife wird nicht in main() eingefügt, sondern direkt> hinter der Stelle, wo sie aufgerufen wurde. Das ist natürlich ein> Himmwelweiter - nein ein Galaxieweiter - Unterschied.
Wie ich schon schrieb: Das ist bei der AVR-Libc zufälligerweise so -
dokumentiert und damit garantiert ist dies nicht!
Für andere µC gilt Deine Aussage auch nicht. Von daher sollte man sich
nicht darauf verlassen und immer eine eigene Endlosschleife vorsehen.
> Man kann sich auch die Hose mit der Kneifzange anziehen.
Man kann auch portabel programmieren und muss nicht jedes
undokumentierte Schmankerl ausnutzen.
Frank M. schrieb:> Für andere µC gilt Deine Aussage auch nicht.
Habe ich auch nicht behauptet.
In der Doku zur avr-lib befinden sich übrigens zahlreiche Beispiele, wo
main() nicht in einer Endlosschleife endet. Insofern betrachte ich diese
Eigenart doch als (indirekt) dokumentiert.
Beispielsweise https://www.nongnu.org/avr-libc/user-manual/FAQ.html
So ist das Stefan. Will man andere lehren so muss man präzise Ausführen.
Und deine Ausführungen zur main() waren einfach falsch. Eine
Endlosschleife wird danach eingefügt. Du schriebst aber was anderes. So
einfach ist das manchmal. Den Fehler dann bockig nicht einsehen zu
wollen zeugt nicht gerade von Größe.
Thomas schrieb:> PORTD = PIND ^ ( 1 << PD0 ); // LED an
Eher nicht.
> Das Programm tut was es soll und ich verstehe es soweit :)
Eher nicht.
Darum schreibt man einfach gleich:
PORTD &= ~( 1 << PD0 );
und
PORTD |= ( 1 << PD0 );
Und spart sich den ganzen Rest mit der PIND abfrage.
Stefan ⛄ F. schrieb:> *) Ich schrieb:> Stefan ⛄ F. schrieb:>> Die leere Main loop ist nicht notwendig, denn der Compiler fügt sie ggf.>> automatisch in den Maschinencode ein.>> Ich habe nicht geschrieben, dass der code in die main()-Funktion> eingefügt wird.
Nein?
> Die leere Main loop
Das ist das Subjekt des Satzes.
> sie
Bezieht sich auf das Subjekt
Ist also eine Wiederholung des
> Die leere Main loop
Das ist falsch!
Eine irreführende Information.
Siehe:
> Ich habe nicht geschrieben, dass der code in die main()-Funktion> eingefügt wird.
Doch, hast du wohl.
Denn wenn eine main loop nicht in der Main sein soll, wo ist sie denn
dann eingefügt worden?
> ggf
ggf sagt, dass es Alternativen gibt. Dass es Situationsabhängig ist.
Das "gegebenenfalls" ist an der Stelle falsch.
Eine irreführende Information.
Denn das wird nicht Fall abhängig eingefügt.
Du merkst:
1. Du hast dich geirrt.
2. Bist berichtigt worden
3. Und jetzt uneinsichtig.
Johannes S. schrieb:> Sehr schöne Fehler gibt es wenn das main() verlassen wird und dann> C++> Objekte zerstört werden wenn sie auf dem main Stack lagen.
Und/Oder zwischendurch die Interrupts abgeschaltet werden, weil man ja
keine main loop braucht, denn sie wird ja "ggf automatisch eingefügt".
Cyblord -. schrieb:>> PORTD = PIND ^ ( 1 << PD0 ); // LED an> Eher nicht.
Unsinn, das funktioniert tadellos. Nur weil man auch PORTD anstelle von
PIND schreiben kann, muss man es nicht.
Das ist mit bereits um 13:00 aufgefallen, aber ich kann im Gegensatz zu
dir über unwichtige Details hinweg sehen.
Stefan ⛄ F. schrieb:> Cyblord -. schrieb:>> Ein Endlosschleife wird danach eingefügt. Du schriebst aber was anderes> Wo? Bitte zitiere das.
Merksatz:
> Irren ist menschlich.> Im Irrtum verharren, ist Dummheit
Wenn es vorher ein Irrtum oder eine unglückliche Formulierung war, jetzt
wird es, nach den Hinweisen darauf und durch dein unbeirrt fortgesetztes
Beharrungsvermögen, zu einer dummdreisten Lüge.
Alles rumeiern wird dir nicht helfen, du bist überführt.
Arduino Fanboy D. schrieb:> Alles rumeiern wird dir nicht helfen, du bist überführt.
Weiter behaupten ohne Nachweis ist auch nicht besser.
Du und dein Kumpel Cyblord, ihr könnten auch einfach mal zugeben, dass
ihr etwas missverstanden habt. Da ihr mich nicht leiden könnt, ist das
nur natürlich.
Stefan ⛄ F. schrieb:> Weiter behaupten ohne Nachweis ist auch nicht besser.
Hier bitte:
Stefan ⛄ F. schrieb:> Die leere Main loop ist nicht notwendig, denn der Compiler fügt sie ggf.> automatisch in den Maschinencode ein.
Der Compiler fügt aber keine "leere Main-Loop" ein. Denn diese müsste in
main() stehen, um "Main-Loop" zu heißen.
Außerdem fügt der Compiler da sowieso nichts ein. Wenn, dann der Linker,
denn die Endlosschleife nach Aufruf von main() wird nicht vom Compiler
erzeugt, sondern vom Linker aus der avr-libc hinzugefügt. Und das ist
unabhängig davon, ob Du in der main-Funktion selbst eine
Endlosschleife stehen hast oder nicht. Das hat mit der Formulierung
"ggf." überhaupt nichts zu tun.
Der Maschinencode ist das Ergebnis des Compilers. Dort wird die finale
Endlosschleife eingefügt.
Wollen wir jetzt darüber diskutieren, ob der Compiler oder der Linker es
einfügt? Das wird mir echt zu blöd.
Stefan ⛄ F. schrieb:> Der Maschinencode ist das Ergebnis des Compilers. Dort wird die> finale Endlosschleife eingefügt.
Falsch. Der Compiler übersetzt C-Files. Die finale Endlosschleife steht
aber in libc.a. Dieser Code wird vom Linker hinzugefügt.
Immerhin nennst Du die "Main Loop" nun nicht mehr so, denn auch der
Linker kann keine "Main Loop" in main() selbst einfügen.
Aber für mich ist hier EOD - diese Diskussion ist mir zu blöd.
Stefan ⛄ F. schrieb:> Cyblord -. schrieb:>>> PORTD = PIND ^ ( 1 << PD0 ); // LED an>> Eher nicht.>> Unsinn, das funktioniert tadellos. Nur weil man auch PORTD anstelle von> PIND schreiben kann, muss man es nicht.
Nein, das war schon richtig erkannt von Cyberlord.
IMHO toggled diese Anweisung den PORTD0.
PORTD = PIND ^ ( 1 << PD0 );
Sie macht das selbe wie
PORTD ^= (1 << PD0);
und (fast) dasselbe wie
PIND = (1 << PD0);
Fast, weil bei der 3. Variante PIND0 konstant bleibt, bei den beiden
anderen wird das gegenläufig mit getoggled.
Dass beim TO die LED mit dem Befehl leuchtet liegt daran, dass sie in
schneller Folge ein- und ausgeschaltet wird, solange die Taste gedrückt
wird - eben dann mit reduzierter Leuchtkraft. Das fällt meist nicht auf.
Richtig ist zur Einschalten am STK500, wie Cyberlord schrieb:
PORTD &= ~( 1 << PD0 );
wobei dieser Befehl den Port nur setzt:
PIND ^= (1 << PD0);
also die STK500-LEDs diese ausschaltet.
Zumindest behauptet das der Simulator in meinem AVR-Studio so ...
HildeK schrieb:> Stefan ⛄ F. schrieb:>> Cyblord -. schrieb:>>>> PORTD = PIND ^ ( 1 << PD0 ); // LED an>>> Eher nicht.>>>> Unsinn, das funktioniert tadellos. Nur weil man auch PORTD anstelle von>> PIND schreiben kann, muss man es nicht.>> Nein, das war schon richtig erkannt von Cyberlord.> IMHO toggled diese Anweisung den PORTD0.
Korrekt. Nicht mal Bitoperationen kann Stefan fehlerfrei bewerten. Seit
wann setzt man bits mit XOR? Und seit wann liest man dafür die
Inputwerte eines Ports zurück? Das ist beides Unsinn den man lassen
sollte.
Das bringt nix....
Stefan ⛄ F. schrieb:> Da ihr mich nicht leiden könnt, ....
Da er meint, wir könnten ihn nicht leidern, wird er uns nicht zuhören,
oder gar Irrtümer einsehen.
Ich war davon ausgegangen, dass er die LED toggeln wollte.
Das ist falsch zitiert:
> PORTD = PIND ^ ( 1 << PD0 ); // LED an
Der Kommentar lautet:
> // LED an Port PD0 an- bzw. aus
Stefan ⛄ F. schrieb:> Cyblord -. schrieb:>>> PORTD = PIND ^ ( 1 << PD0 ); // LED an>> Eher nicht.>> Unsinn, das funktioniert tadellos. Nur weil man auch PORTD anstelle von> PIND schreiben kann, muss man es nicht.
Das sind zwei völlig verschiedene Operationen.
Einfach und problemlos ist nur die Verwendung von PORTD als Quelle des
Bits. Das hat dort halt sicher den Zustand, der zuletzt gesetzt wurde.
Bei PIND sieht das aber völlig anders aus. Das ist der Status des
Eingangs. Da sind zwei Sachen zu berücksichtigen:
1) Der Einfluß der äußeren Beschaltung. Das ist fundamental. Mit
hinreichend idiotischer äußerer Beschaltung kann man jede beliebige
Abweichung von der Idee "PINyx=PORTyx" locker hinbekommen.
2) Selbst wenn außen garnix dranhängt, hängt der Status von PINxy
mindestens einen Systemtakt-Tick dem von PORTxy hinterher. D.h.:
ldi R16,0
out PORTB,R16
ldi R16,1
out PORTB,R16
in R16,PINB
wird im niederwertigsten Bit von R16 NICHT 1 liefern, sondern 0.
Ergänzt man hingegen so:
ldi R16,0
out PORTB,R16
ldi R16,1
out PORTB,R16
nop
in R16,PINB
dann wird es 1 liefern, obwohl NOP lt. Definition nix tut, außer einen
Takt Zeit zu verbrauchen...
Du mußt noch sehr, sehr viel lernen...
c-hater schrieb:> Du mußt noch sehr, sehr viel lernen...
Der Unterschied ist mir durchaus bekannt, für die Anwendung des TO
jedoch irrelevant.
Möchte noch jemand Nachtreten? Kommt schon, ich bin gerade in Stimmung.
Hier versammelt sich gerade die creme de la creme des Forums.
Stefan ⛄ F. schrieb:> Der Unterschied ist mir durchaus bekannt, für die Anwendung des TO> jedoch irrelevant.
Du bist also auch noch Hellseher? Ich habe jedenfalls im Thread kein
Schaltschema des TO entdecken können, was eine abschließende Bewertung
zur Relevanz ermöglicht hätte...
Stefan ⛄ F. schrieb:> Möchte noch jemand Nachtreten? Kommt schon, ich bin gerade in Stimmung.
Kein Problem....
Stefan ⛄ F. schrieb:> Ich war davon ausgegangen, dass er die LED toggeln wollte.
Dann tut man das so:
PIND = (1 << PD0);
(aber das auch wurde schon gezeigt)
c-hater schrieb:> Du bist also auch noch Hellseher?
Warum fragst du nicht den TO, was er beabsichtigt hatte?
Die folgenden zwei Anweisungen tun in seiner Anwendung effektiv da
gleiche:
PORTD = PIND ^ ( 1 << PD0 );
PORTD = PORTD ^ ( 1 << PD0 );
Nur darum ging es bei meiner Aussage
> Nur weil man auch PORTD anstelle von> PIND schreiben kann, muss man es nicht.
Hier in diesem Thread sieht man mal wieder sehr deutlich, dass
missverstanden wird, wenn man missverstehen will. Bzw. man findet Fehler
wenn man danach sucht.
Ihr kritisiert, dass ich den TO falsch lehre. Glaubt ihr im Ernst, dass
er aus euren Beiträgen etwas gelernt hat? Ich glaube, er hat gelernt,
mit wem er besser nicht diskutiert.
Die Frage des TO interessiert euch kein bisschen mehr. Ich habt euch
darauf eingeschossen, mich bloß zu stellen. Und das zieht ihr jetzt
knallhart durch. Völlig egal, ob damit irgend wem geholfen wird.
Es hilft weder euch noch mir. Am Ende verfestigt sich nur unsere
gegenseitige Abneigung. Seid ihr darauf Stolz?
Ich bin es nicht. Wie gesagt: ich bereue das alles längst.
Stefan ⛄ F. schrieb:> aber die folgenden drei Anweisungen tun in seiner Anwendung> effektiv da gleiche:>> PORTD = PIND ^ ( 1 << PD0 );> PORTD = PORTD ^ ( 1 << PD0 );
Nein, das sind sie eben nicht notwendigerweise. Ich habe dir
detaillierte Gegenindikationen vorgestellt.
Begreifst du das nichtmal dann, wenn man es dir förmlich "mit der
nackten Faust ins Gesicht rammt"?
Oder bist du nur nicht bereit, darüber auch nur nachzudenken?
Oder weißt du inzwischen, dass du falsch lagst, willst es nur nicht
zugeben?
Fragen über Fragen...
c-hater schrieb:> Nein, das sind sie eben nicht notwendigerweise. Ich habe dir> detaillierte Gegenindikationen vorgestellt.
Welche in der Anwendung des TO keine Rolle spielen.
c-hater schrieb:> Begreifst du das nichtmal dann, wenn man es dir förmlich "mit der> nackten Faust ins Gesicht rammt"?
Auf diese Art begreifst du gar nichts.
Stefan ⛄ F. schrieb:> c-hater schrieb:>> Nein, das sind sie eben nicht notwendigerweise. Ich habe dir>> detaillierte Gegenindikationen vorgestellt.>> Welche in der Anwendung des TO keine Rolle spielen.
Was du woher genau weißt?
Es steht ja schließlich immer noch das Problem des fehlenden Schemas der
Hardware, was aber nach der Erkenntnis, dass die halt bei der Verwendung
der Eingänge zwingend eine entscheidende Rolle spielt, doch deutlich
störend für eine Bewertung ist, inwiefern die Verwendung von PIND statt
PORTD eine Rolle spielt.
Also doch Hellseher-Syndrom...
Oder, noch schlimmer: bist du etwa (auch) der TO? Wäre logisch
denkbar...
Dann kennst du natürlich die Schaltung, wärest aber ein unsäglicher
Troll...
Frag doch den TO, anstatt meine Annahmen zu kritisieren.
> bist du etwa der TO?
nein.
Aber interessant, dass du mir hier zutraust, so eine Show abzuziehen:
Erst eine Frage stellen und mir dann selbst antworten.
Wie kommst du auf diese Idee, machst du so etwas etwa?
Es macht IMO absolut keinen Sinn mehr über technische Dinge mit Stefan
zu diskutieren. Die offensichtlichsten Fakten werden ignoriert und
geleugnet. Die einfachsten Dinge unmöglich verkompliziert. Ich erkenne
auch keine Fachkompetenz. Nur ein wenig Halbwissen und viel Starrsinn.
Gerade Anfänger werden hier durch solche Nutzer stark verunsichert.
Anfänger brauche eine klare Ansage wie es zu machen ist. Und keine
verschwurbelten Diskussionen unter welchen unwahrscheinlichen Umständen
irgendeine falsche Lösung vielleicht doch mal richtig sein könnte. Das
hilft niemandem. Nur Stefans Ego. Echt ärmlich.
...und ich dachte, ich hätte eine ganz einfache Frage gestellt...
habe in meinem Code nun das
PORTD = PIND ^ ( 1 << PD0 );
durch
PORTD &= ~( 1 << PD0 );
ersetzt.
Damit funktioniert es so wie ich das wollte, solange die Taste gedrückt
ist leuchtet die LED, sonst ist sie aus:
1
for(;;){// main loop
2
3
while(!(PINC&(1<<PINC0))){// Prüfen ob PC0 low !!!
4
PORTD&=~(1<<PD0);// LED an
5
}
6
PORTD|=(1<<PD0);// LED aus
7
8
}
Das ganze war auch nur eine Übung für mein Verständnis, weil ich
folgendes realisieren wollte:
1
intmain(void)
2
{
3
4
PORTC|=(1<<DDC0)|(1<<DDC0);// interne Pull-Up an PC0 und PC1 aktivieren
while(!(PINC&(1<<PINC0))){// Prüfen ob PC0 low !!!
23
OCR1A=500;// 62 Hz
24
}
25
26
while(!(PINC&(1<<PINC1))){// Prüfen ob PC1 low !!!
27
OCR1A=830;// 37 Hz
28
}
29
30
}
31
return(0);
32
}
Und das tut zu meiner Freude auch!
Seltsam ist nur, dass es manchmal (d.h. nicht immer) beim Umschalten der
Frequenz hakt. Es braucht dann ca. eine Sekunde bis das Oszi das
Rechtecksignal und die Frequenz wieder anzeigt.
Könnte es daran liegen, wann ich im CTC Zyklus die Taste drücke?
thomas schrieb:> Seltsam ist nur, dass es manchmal (d.h. nicht immer) beim Umschalten der> Frequenz hakt. Es braucht dann ca. eine Sekunde bis das Oszi das> Rechtecksignal und die Frequenz wieder anzeigt.
Vielleicht liegt es daran, dass du z.B. den PINC1 zu einem Zeitpunkt
loslässt, wo der TCNT1 schon über 500 oder 625 ist, aber noch unter 830
steht. Dann wird TCNT1 einmal komplett durchzählen mit Überlauf bei
65535. Das dauert bei 62kHz schon etwas mehr als eine Sekunde.
Es könnte auch sein, dass da das Skope eine Triggerpause macht.
Unwahrscheinlich.
Zum Testen könntest du mit dem Prescaler das Ganz so langsam machen,
dass du die Frequenzänderung direkt am LED-Blinken sehen kannst.
Joachim B. schrieb:
> was ist mit Tastenprellen?
Dachte ich erst auch, aber da ich die Tasten ja gedrückt halte brauche
ich es vermutlich doch nicht. Habe ich auch in verschiedenen Posts so
gelesen...
Wenn es egal ist, dass auch der Ausgang potentiell prellt (also beim
Drücken der Taste von 50Hz auf 37Hz auf 50Hz auf 37Hz springt) und erst
nach ein paar zig Millisekunden stabil auf 37Hz liegt, brauchst du dir
um Prellen in dieser Anwendung keine Sorgen zu machen. Bei den
Frequenzen fällt das wahrscheinlich noch nichtmal auf, bei dem
LED-Beispiel vorher hättest du halt fast 1:1 das Prellen an die LED
durchgereicht. Der das auch nichts ausmacht.
Eine ziemlich undefinierte Entprellung hast du jetzt bei der
Frequenzumschaltung aus Versehen ja auch schon dabei, weil du wartest,
bis der Timer 0 erreicht.
MfG, Arno
Ach da gibts noch ganz andere Probleme....
thomas schrieb:> TCNT1 == 0
Dann kann/wird zu Sorgen führen, wenn noch weitere Sachen erledigt
werden wollen.
z.B. der ADC steht noch auf dem Plan.
Dann die 2 While Schleifen...
Schön ist das nicht.
Mein erster Gedanke wäre:
1. 3 Zustände
2. Tasten gesteuert
Das schreit nach einem endlichen Automaten. Sei er auch noch so
primitiv.
Das kostet etwas Hirnschmalz, und auch Flash.
Aber dafür schafft es den Zeitraum für viele viele weitere Abhandlungen.
In dem Zuge kann man auch das entprellen und Flanken erkennen abhandeln.
Ist ja auch nur ein weiterer kleiner Automat.
Arno schrieb:> Eine ziemlich undefinierte Entprellung hast du jetzt bei der> Frequenzumschaltung aus Versehen ja auch schon dabei, weil du wartest,> bis der Timer 0 erreicht.
Ja.
Aber selbst wenn man ganz nahe am Überlauf drückt und die Prellung zum
Überlaufzeitpunkt noch nicht abgeklungen ist, dann wird schlimmstenfalls
noch eine Periode mit der alten oder der Frequenz bei 'nichts gedrückt'
ausgegeben.
Ob das stört, hängt von der Anwendung ab.
Stefan ⛄ F. schrieb:> Wie oft willst du noch nachtreten?
Es hätte gereicht wenn du ganz am Anfang einfach mal die
Fresse gehalten hättest.
Nicht weil der eine oder der andere Recht hat sondern weil du
zu jedem Thema einfach deinen Senf dazu geben musst obwohl
ihn keiner braucht.
OMG schrieb:> weil du zu jedem Thema einfach deinen Senf dazu geben musst
Das ist nicht einmal Ansatzweise wahr.
Wenn mich jemand missverstanden hat (evtl. weil ich mich unklar
ausgedrückt habe), dann scheint mir eine Klarstellung durchaus für
Sinnvoll. gewisse Personen nutzen solche Fälle allerdings als
Gelegenheit, andere regelrecht nieder zu machen - nicht nur mich.
Ich denke nicht, dass ich daran direkt Schuld bin. Jedoch habe ich
bereits zweimal geschrieben, dass ich bereue, diesen Scheißdreck
ausgelöst zu haben.
Hi,
Also,
dann lass mal den Debugger (Simulator) im ATMEL Studio drüber laufen.
(Hab WinAVR gerade nicht installiert. So wäre es für mich nötig, in ASM
rückzuübersetzen.) Dann sieht man, was da passiert.
Da war jemand schneller.
HildeK schrieb:> Zumindest behauptet das der Simulator in meinem AVR-Studio so ...Lothar M. schrieb:> Frank M. schrieb:>> Auf Deutsch: Du sollst beim nächsten Mal Deinen C-Code ...>> einbetten, damit man ihn auch lesen kann.> Und gratis gibts dazu dann noch das Hochlicht namens> "Syntax-Highlighting".
Und ich speicher die Programmschnipsel immer als Textdatei ab und nenne
sie dann in *.asm um, oder was gerade gefordert wird. (Das Gedöns mit
den Tags klappt bei mir nicht. Ich kann weder fett noch irgendwas
anderes editieren.)
Dann lade ich sie als Anlage hoch.
Sonst wird bei etwas umfangreicheren Beispielen die Sache (gerade bei
Smartphoneusern) hier extrem unübersichtlich.
ciao
gustav
Stefan ⛄ F. schrieb:> Ich denke nicht, dass ich daran direkt Schuld bin.
Du hast Unsinn geschrieben.
Gibst es nicht zu.
Und daran sind natürlich die anderen Schuld.
(ihr liebt mich nicht)
Stefan, ich bitte Dich direkt am Anfang, diesen Beitrag erst *dreimal
gründlich durchzulesen*, bevor Du wieder innerhalb weniger Minuten
reflexartig antwortest. Mir drängt sich nämlich immer bei Deinen
schnellen Antworten der Verdacht auf, dass Du die Antworten auf Deine
Beiträge immer nur querliest und nur die Hälfte mitbekommst.
Stefan ⛄ F. schrieb:> OMG schrieb:>> weil du zu jedem Thema einfach deinen Senf dazu geben musst>> Das ist nicht einmal Ansatzweise wahr.
Deinen ursprünglichen Erst-Beitrag hättest Du Dir einfach sparen sollen.
Begründung:
1. Endlosschleife
Deine Info, auf eine explizite eigene Endlosschleife zu verzichten, ist
komplett überflüssig und sogar schädlich. Jeder nutzt eine eigene
Endlosschleife, wenn er einen µC anhalten will und nutzt dafür nicht
eine zufällige Eigenschaft irgendeiner libc - hier der avr-libc.
Schließlich fehlte auch von Dir die Anmerkung, dass dieses "Feature"
auch nur bei AVRs so funktioniert und der TO bei einem Wechsel zu einer
anderen Mikroconcontroller-Familie durchaus auf die Nase fliegen kann.
Deine Erklärung, dass der Compiler ggf eine Main-Loop einfügt, war
nicht zutreffend. Hier muss man schon ganz genau sein, um den
Sachverhalt zu erklären. Sonst setzt sich Deine nicht zutreffende
Schilderung in den Köpfen der geneigten Leser fest. Genau daran haben
sich einige hier gestört. Das hat auch nichts mit persönlichen Angriffen
zu tun.
2. Return in main()
Wie ich schon schrieb, ist es grundsätzlich nicht falsch, ein eigenes
return in main() zu verwenden - schon gar nicht schädlich. Ältere
Compiler meckern sogar, wenn das return in main() fehlt, denn erst
neuere Compiler kennen diese neue C-Regel.
Fazit:
Dein Beitrag war einfach hyperfluid und erweckt den Eindruck an den TO,
er hätte zwei Fehler gemacht. Hat er aber nicht - ganz im Gegenteil. Er
hat es - was Endlosschleife und Return betrifft - absolut richtig
gemacht.
So wie Du es vorschlugtest, macht man es jedenfalls nicht. Und nein,
man muss nicht für jeden Sachverhalt seinen eigenen Senf hinzugeben -
jedenfalls nicht dann, wenn man es gar nicht so genau weiß.
Frank M. schrieb:> Deinen ursprünglichen Erst-Beitrag hättest Du Dir einfach sparen sollen.
Das ist mir schon lange klar. Ich weise jetzt zum vierten mal darauf
hin, dass es mir leid tut.
Frank M. schrieb:> man muss nicht für jeden Sachverhalt seinen eigenen Senf hinzugeben -> jedenfalls nicht dann, wenn man es gar nicht so genau weiß.
Richtig. Manchmal glaubt man allerdings, es zu wissen. Und manchmal
macht man (mit oder ohne Absicht) den Fehler, nicht die korrekten Worte
zu verwenden.
Was das return(0) angeht:
Hinter einer Endlosschleife ist es sinnlos. Diese Zeile wird nie
erreicht. Manche Quelttext-Checker meckern das sogar an (unreachable
statement). Java würde so etwas nicht einmal compilieren.
Ihr dürft diese Info gerne "Unsinn" nennen, und das so oft wiederholen
wir ihr wollt. Mich überzeugt ihr damit nicht.
Arduino Fanboy D. schrieb:
>Ach da gibts noch ganz andere Probleme....
Das ist leider nur allzu richtig!
Die while Schleifen sind zwar nicht schön, aber sie hatten für meine
Zwecke ausreichend gut funktioniert bis ich den ADC mit Interupt
eingeschaltet habe :(
Für den "endlichen Automaten" könnte ich gut noch einen Tip oder ein
Beispiel gebrauchen...
Mein Hirnschmalz ist ohnehin schon ziemlich dünnflüssig.
Letztendlich soll das die Ansteuerung eines Synchronmotors von einem
alten russischen Teleskop werden der mit 50Hz läuft. Mit den Tasten will
ich ihn schneller oder langsamer drehen lassen können, mit dem Poti die
50Hz gegebenenfalls etwas regulieren. Ich denke, dass die Anwendung ein
paar Ungenauigkeiten schon verkraftet, wenn nach dem Einstellen die
eingestellte Frequenz durchläuft.
Stefan ⛄ F. schrieb:> Was das return(0) angeht:> Hinter einer Endlosschleife ist es sinnlos. Diese Zeile wird nie> erreicht. Manche Quelttext-Checker meckern das sogar an (unreachable> statement).
aber meist ist main auch als int definiert und andere
"Quelttext-Checker" oder Quältext-Checker? meckern dann das es kein
return gibt.
Also sauber programmiert ist es immer mit return auch wenn das nie
erreicht wird!
Verstehe eine die Programmierer die mal das Eine und mal das Andere
monieren!
thomas schrieb:> Für den "endlichen Automaten" könnte ich gut noch einen Tip oder ein> Beispiel gebrauchen...
Hi,
leider nur ASM.
Aber Suchbegriff "Job"register könnte evtl. weiterhelfen.
ciao
gustav
thomas schrieb:> Die while Schleifen sind zwar nicht schön, aber sie hatten für meine> Zwecke ausreichend gut funktioniert bis ich den ADC mit Interupt> eingeschaltet habe :(
Sie sind auch nicht nötig. In der jetzigen Routine fragst du die Tasten
ja bei jedem Durchlauf ab.
Es reicht, wenn man bei den Tasten feststellst, ob sich deren Zustand
geändert hat. Und nur dann änderst du entsprechend OCR1A. Jetzt wird
aber eine Entprellung notwendig sein.
ADC-Interrupt? Kann man nehmen, muss man aber nicht. Immer mal wieder
aufrufen im Single Conversion Mode. Am besten in Kombination (oder jetzt
anstatt?) mit den Tasten in einem Timerinterrupt.
Neue Werte für den ADC wirst du seltener benötigen, ein Zähler im
Timerinterrupt hilft das gröbere Raster für den ADC-Aufruf zu markieren.
Joachim B. schrieb:> Stefan ⛄ F. schrieb:>> Was das return(0) angeht:>> Hinter einer Endlosschleife ist es sinnlos. Diese Zeile wird nie>> erreicht. Manche Quelttext-Checker meckern das sogar an (unreachable>> statement).>> aber meist ist main auch als int definiert und andere> "Quelttext-Checker" oder Quältext-Checker? meckern dann das es kein> return gibt.
Wobei return bei main optional ist, zumindest wenn ich C11 (hosted)
richtig interpretiere:
1
5.1.2.2.3 Program termination
2
3
1 [...] reaching the } that terminates the main function
4
returns a value of 0. [...]
und für C++
1
3.6.1 Main function [basic.start.main]
2
3
5 [...] If control reaches the end of main without encountering
4
a return statement, the effect is that of executing return 0;
Wenn also irgendein Checker meckert, ist das nicht gerechtfertigt. Und
GCC zum Beispiel meckert main ohne finales return ebenfalls nicht an,
auch dann nicht, wenn es erreichbar ist.
Johann L. schrieb:> GCC zum Beispiel meckert main ohne finales return ebenfalls nicht an
es gibt ja nicht nur GCC, mein Text war eben beispielhaft für alles was
mir schon begegnet ist!
Es gibt auch noch Compileroptionen und wehe man aktiviert mal alle
Warnungen, soviel Fehler kann man kaum machen was da alles bemängelt
wird.
Ändert doch nix dran, dass ein Programm mit return 0 am Ende von main
semantisch das gleiche ist wie ein Programm ohne return am Ende von
main.
Wenn ein Feld-Wald-und-Wiesen-Programmierer das nicht weiß ist es ok,
aber wenn ein Tool-Hersteller das nicht weiß, dann spricht das nicht für
diesen Hersteller.
da hast du Recht!
Nur diese Diskusion habe ich meist aufgegeben, führt zu nichts, man muss
es hinnehmen und so machen wie der Compiler es will, sinnvoll oder nicht
ist nicht gefragt!
Das "return 0" optional ist, war laut Spezifikation schon immer so. Ich
wiederhole nochmal mein Zitat:
"The body of the main function does not need to contain the return
statement: if control reaches the end of main without encountering a
return statement, the effect is that of executing return 0;"
https://en.cppreference.com/w/cpp/language/main_function
Dort gibt es keinen Hinweis, dass das erst ab einer bestimmten Version
gilt.
Auch in C ist und war es gemäß der Webseite schon immer optional:
"If the main function executes a return that specifies no value or,
which is the same, reaches the terminating } without executing a return,
the termination status returned to the host environment is undefined.
(until C99)
If the returned type is compatible with int and control reaches the
terminating }, the value returned to the environment is the same as if
executing return 0; (since C99)"
https://en.cppreference.com/w/c/language/main_function
@Hilde,
danke, ich guck mal was ich da so hinkriege...
@Dietrich,
faszinierend der Link mit der Statemachine, übersteigt aber im Moment
meine programmiertechnischen Fähigkeiten.
@Stefan,
soll ich das "return 0" jetzt besser nicht verwenden?
thomas schrieb:> @Hilde,
Wenn du kich meinst, dann HildeK! :-)
> soll ich das "return 0" jetzt besser nicht verwenden?
Ein 'return' in main() bei μCs ist nutzlos, schadet aber auch nicht. Es
gib ja keinen aufrufenden Prozess, an den etwas zurückgegeben werden
kann.
C wird aber auch in Umgebungen mit Betriebssystemen verwendet und da ist
es durchaus sinnvoll auf diesem Weg eine Erfolgs- oder
Misserfolgsmeldung auszugeben.
thomas schrieb:> soll ich das "return 0" jetzt besser nicht verwenden?
Du hast ja im Main immer eine Endlosschleife, d.h. das Main wird nie
verlassen. Also ist alles, was hinter der Schleife steht, toter Code und
wird auch nicht implementiert. Der Compiler ist ja nicht doof.
Du kannst also alles dahinter schreiben, was Du lustig bist.
Persönlich gefällt es mir besser, wenn kein toter Code hingeschrieben
wird, d.h. hinter der schließenden Klammer der Endlosschleife folgt nur
noch die schließende Klammer des Main.
Du darfst natürlich auch auf MCs Programme schreiben, die das Main
verlassen, aber sowas ist recht sinnfrei. Falls es wirklich nichts mehr
zu tun gibt, bis zum nächsten Reset, dann geht man doch besser in Sleep
und spart Strom.
Prinzipiell kann ein Main auch sich selber rekursiv aufrufen, aber sowas
macht man nur im Obfuscated C Code Contest.
Peter D. schrieb:> Prinzipiell kann ein Main auch sich selber rekursiv aufrufen, aber sowas> macht man nur im Obfuscated C Code Contest.
Darüber habe ich noch nie nachgedacht, aber: Was passiert, wenn ich am
Ende von main() main() wieder aufrufe? Habe ich dann einen sauberen
Neustart (da End-Rekursiv) oder wächst mein Stack dann immer tiefer?
(Lassen wir mal den Heap außen vor. Den nutzen ja anständige Menschen
auf dem AVR eh nicht.)
Peter D. schrieb:> Prinzipiell kann ein Main auch sich selber rekursiv aufrufen,
Nicht in C++
1
3.6.1 Main function [basic.start.main]
2
3
3 The function main shall not be used (3.2) within a program. [...]
Und was C angeht, hat avr-gcc eine Optimierung, die Aufrufe von main im
Endeffekt UB machen, es sei denn man verwendet -mno-main-is-OS_task,
siehe
http://gcc.gnu.org/gcc-8/changes.html#avr
Logischerweise darf main dann auch weder Attribut OS_task noch OS_main
tragen.
> Du darfst natürlich auch auf MCs Programme schreiben, die das Main> verlassen, aber sowas ist recht sinnfrei.
Es gibt mindestens eine Anwendung, wo das sehr sinnvoll ist, nämlich bei
Ausführung der GCC Testsuite — wobei das streng genommen nicht "auf MC"
ist:
Es ist wesentlich einfacher und konsistenter, das Verlassen von main
standardkonform zu behandeln, als händisch Testfälle auszuschließen, die
* atexit testen / verwenden
* Statische Destruktoren testen / verwenden (C++)
* __attribute__((_destructor_)) testen / verwenden (GCC Erweiterung)
* Code in Section .fini9 ... .fini0 haben (avr-spezifisch)
Wobei der Simulator (avrtest) hierzu noch etwas Extra-Code braucht:
https://sourceforge.net/p/winavr/code/272/tree/trunk/avrtest/dejagnuboards/exit.c#l100
Das ist auch der Grund, warum in der avr-libc am Ende des Startup-Codes
in .init9 steht