Na wunderbar. :)
Ich fang mal hinten beim return() an: Programme in C sind eigentlich für Rechner mit Betriebssystem gedacht und bilden für diese Betriebssysteme eine weitere Funktion. Das Betriebssystem ruft das Programm auf und übergibt dabei gegebenenfalls auch ein paar Parameter und das Programm übergibt beim Beenden irgendwelche Rückgabewerte an das Betriebssystem zurück, z.b. eventuelle Fehlercodes. Um das Programm zu starten ruft das Betriebssystem die Funktion main() als Unterprogramm auf und legt die Rücksprungadresse auf den Stapel. Wenn die Funktion main() beendet werden soll, wird die Funktion return() aufgerufen und der Rückgabewert an das Betriebssystem weitergegeben. Dieses holt die Rücksprungadresse vom Stapel und setzt dort seine Arbeit fort. (Alles stark vereinfacht)
Beim Microkontroller existiert aber kein übergeordnetes Betriebssystem, der Anfang der Funktion main() wird einfach direkt nach dem Einschalten angesprungen. Deshalb gibt es auch keine Parameter die beim Start mitgeteilt werden können und auch niemand der sich für den Rückgabewert interessiert. Da auch GCC ein echtes C ist sieht der korrekte Aufbau von main() immer so aus:
int main(void) // Rückgabewert ist(muss!) ein Integer (int) sein und kein Parameter wird übergeben (void)
{
// Funktionskörper
return(0); // weil C für eine Funktion den Rückgabewert verlangt motzt auch GCC beim Kompilieren ein Fehlen des Returns an.
}
Was passiert, wenn die Funktion main() beendet wird? Das weiß eigentlich niemand genau, denn es geschieht unkontrollierbares. Mit Return holt sich der Kontroller irgendwas vom Stapel und interpretiert dies als die Adresse des nächsten Befehls. Ohne Return macht der Kontroller hinter der }-Klammer weiter, egal was da im Speicher steht. Beidesmal ist es reiner Zufall welchen Inhalt die Speicherstellen besitzen die jetzt abgearbeitet werden und welche Maschinenbefehle hinter den einzelnen Bitmustern stecken. Von "Der Kontroller macht endlos weiter" über "Ausgangspins werden verändert und können Hardware beschädigen" bis hin "zum Überschreiben der Watchdogeinstellungen" ist eigentlich alles möglich (, aber zum Glück nicht gleich wahrscheinlich).
Deshalb darf weder das return() noch die letzte }-Klammer je erreicht werden. Wer sicher ist, dass er immer eine Endlosschleife programmiert, der kann das return() auch weglassen (und die Kompilierwarnung ignorieren). Ängstliche (so wie ich auch) machen im Zweifel immer ein while(1); vor dem return(). Ächz, Ende return().
Zu while(1): Beim Abarbeiten des Programm wird meist eine einmalige Initialisierung von Hardware und Daten ausgeführt und dann der Rest als Schleife endlos wiederholt. Diese Endlosschleife kann zwar immer wieder unterschiedlich verzweigen, wird aber nie verlassen (von goto und break mal abgesehen). Ob das nun als while(1), while(1==1), while(true) oder gar als for(;;) umgesetzt wird ist egal.
Zu Sleep(255): Die endlose Schleife sah erst etwa so aus:
while(1)
{
if(PollSwitch()==1)
{
}
}
Ohne Tastendruck wird if() nie erfüllt und die Schleife rast unglaublich schnell vor sich hin. Lediglich PollSwitch() bremst durch die AD-Wandelzeit (und ein paar unerklärliche Sleeps) etwas. Aber eigentlich wird nach dem Wandeln sofort der nächste Wert gewandelt, das könnte eventuell zu Wandelfehlern führen. Deshalb erzwingt Sleep(255) einen kurzen Abstand zwischen den Lesungen. Zudem werden so auch deutlich weniger Werte und damit auch Fehler gelesen.
Zu Weiterfahren: Wenn anstelle des Stops ein Zurückfahren mit Ausweichen programmiert wird, kann man das "while(1); // Programmende" entfernen.
Bei aller Begeisterung solltest du aber daran denken, dass der Resttastenhub der Taster nach dem Schaltpunkt deutlich kürzer als der Anhalteweg des asuro nach Erkennen des Tastendrucks ist. Rausgeschleuderte Mega8s sind noch die harmlosesten Folgen solcher Kollisionsschaltertests...
Gruß
mic
Lesezeichen