Hallo, mitlerweile habe ich schon kleine Erfahrungen mit den ATMEL µC gesammelt - ATMEGA32. Mein jungstes Projekt ist eine Gewächshaussteuerung. Kurzbeschreibung: -Lüftung und Temperatursteuerung durch das Öffnen/Schliessen der Fenster und Türen -Wasseraufladung: EInlassen durch ein Ventil in ein Kanister und Zirkulation des Wassers durch eine kleine Gleichstrompumpe -Beregnung: Zum Abend, wenn das Wasser einigermaßen warm geworden ist kann ´beregnet werden - mir Garden Tropfsystem Hardware: 1 Gleichstrommotor für Fenster (H-Brücke) 1 Gleichstrommotor für Eingangstür (H-Brücke) 1 Ventil um Wasser von der Pumpe einzulassen 1 Pumpe für die Umwälzung: Aufwärmen von Wasser 1 Pumpe für die Beregnung 1 Taster (Innen und aussen) zum Öffnen der Tür 1 LCD display 1 Keypad 4x3 1 RS232 1 Windmesser (um beim Sturm die Türen sofort zu schließen) Die Hardware funktioniert schon. Die Software teilweise. Jetzt bin ich fast schon an meine Grenzen gestoßen und hab mir überlegt was wäre es wenn es ein kleines Betriebssystem in C wäre womit ich dann die TASKS einfach parallel arbeiten lassen kann und brauche mich nicht mehr ind die Abarbeitungsreihenfolge kümmern. Mein Problem ist: Wenn z. B. Ich die Tür öffnen lasse, dann dauer es ca. 10 Sekunden. In dieser Zeit kann der µC nichts anderes machen und das ist schade. Hat jemand hier eine goldene Lösung. Falls nötig schicke auch Bilder. EverlastingLoop
Hi, vielleicht hilft Dir der Scheduler aus der Codesammlung: http://www.mikrocontroller.net/forum/read-4-49709.html#new Gruss Andreas
Du musst diese 10 Sekunden für die Tür ja nicht wartend verbringen, sondern z.B. auf die Timer zurückgreifen. Ein Betriebsystem wäre glaube ich ein wenig viel dafür und würde die Sache wohl auch noch unnötig erschweren. Wie wäre es wenn du es so z.B. machen würdest (pseudo-code):
1 | int main() { |
2 | while (1) { task1(); task2(); task3(); } |
3 | return 0; |
4 | }
|
5 | |
6 | void task1() { |
7 | if (aenderung_noetig) aendere(); |
8 | }
|
9 | |
10 | void task2() { ... etc usw bla |
und bei länger andauernden Prozessen speicherst du einfach den Sollzustand und fragst bei der Fallunterscheidung ("if (aenderung_noetig)") ab, ob dieser erreicht ist.
Hallo, danke für den Beitrag. Anbei noch das Gewächhausschema. Damit könnte man gut leben. Bloß ich habe die Impulsgeber für die Drehzahlerfassung auf ganz normale Pins gelegt und Frage die in einer while schlafe ab solange der Motor angesteuert wird und das geht ca. 8 Sekunden. Ich denke ich müßte alles umstrukturieren und die Pins auf die Interrupt-Pins legen und während des Interrupts incrementieren. Wie kann man ein Task in der Mitte der Ausführung für eine andere Task unterbrechen? Habt ihr Links zu einem Betriebssystem? Aber alles Umschraiben wäre ein großer Aufwand. //////////////////////////////////////////////////////////////////////// ////// void doorClose() { u08 imp_state=0; powerCard(MOT_2_LEFT); door_status=CLOSING; //lcd_WR_string(LCD_Line_1+5,"Dcls"); while(CHECKBIT_FOR_ZERO(PINC,7)) {switch(imp_state) { case 0: {if(CHECKBIT(PINA,7)) {imp_state=1; } break; } case 1: {if(CHECKBIT_FOR_ZERO(PINA,7)) {imp_state=0;iImpCount_Door--; } break; } } delus(5); if(CHECKBIT(PINC,7)) {door_status=CLOSED;iImpCount_Door=0;break;} // if reed contact is shorted, then interrupt closing if(door_status==STOP_REQUEST) {door_status=MIDDLE;break;} } powerCard(MOT_2_OFF); } //////////////////////////////////////////////////////////////////////// ////// Andreas Ortner
Das wirst du sowieso müssen :-) Ob du nun ein BS hast oder nicht. Auf einem µC baut man sowas ganz anders auf. Grundsätzlich gilt die Devise: Keine Warteschleifen die länger als ein paar µs sind. Man macht das zb. so, dass man sich einen Automaten baut, der verschiedene Zustände hat. Deine Tür zb. hat die Zustände: offen, öffnend, geschlossen, schliessend. Dann überlegt man sich, welche Auslöser es gibt um von einem Zustand in einen anderen Zustand zu gelangen. Zb. mag die Tür im Zustand 'geschlossen' sein. Ein Auslöser könnte zb. das Drücken eines Tasters sein. Dadurch wird eine Aktion ausgelöst (Motor ein) und die Tür geht über in den Zustand 'öffnend'. Im Zustand 'öffnend' kann ein anderes Ereignis auftreten: zb. kannst du einen anderen Taster drücken, worauf hin als Aktion der Motor umgeschaltet wird und die Tür in den Zustand 'schliessend' übergeht. Oder ein Endschalter spricht an worauf hin als Aktion der Motor abgeschaltet wird und die Tür in den Zustand 'offen' übergeht. Man kann das auch schön visualisieren, schau dir mal das beigelegte GIF an. Dort findest du die 4 Zustände wieder und Pfeile. Jeder Pfeil beschreibt den Übergang von einem Zustand in einen anderen Zustand. Der jeweilige Auslöser ist jeweils in Blau angegeben, während die Aktion, die durch den Übergang ausgelöst wird in Rot wiedergegeben ist. Ist also die Tür geschlossen, so gibt es nur eine Möglichkeit diesen Zustand zu verlassen: jemand drückt den Taster für 'Schliessen'. Als Folge davon, wird der Motor für Schliessen gestartet und die Tür wechselt in den Zustand 'Schliessend'. Natürlich wirst du da noch andere Auslöser an- bringen wollen (Windgeschwindigkeit über dem Limit, Lichtsensor signalisiert Nacht, Temperatursensor meldet zu heiss), aber es geht hier nur ums Prinzip. Sowas programmiert sich dann schon fast von alleine: #define DOOR_OPEN 0 #define DOOR_CLOSED 1 #define DOOR_OPENING 2 #define DOOR_CLOSING 3 unsigned char DoorState = DOOR_OPEN; Weiters gibt es eine Hauptschleife, die die jeweiligen Auslöser überwacht und mit den Zuständen in Beziehung setzt und die Aktionen auslöst: int main() { while( 1 ) { if( DoorState == DOOR_CLOSED && Taster_Oeffnen_gedrückt ) DoorOpening(); if( DoorState == DOOR_OPENING ) { if( Endschalter_Open_betätigt ) DoorOpen(); if( Taster_Schliessen_gedrückt ) DoorClosing(); } if( DoorState == DOOR_CLOSING ) { if( Endschalter_Close_betätigt ) DoorClosed(); if( Taster_Oeffnen_gedrückt ) DoorOpening(); } if( DoorState == DOOR_OPEN && Taster_Schliessen_gedrückt ) DoorClosing(); } } void DoorOpening() { Schalte Motor auf Öffnen; DoorState = DOOR_OPENING; } void DoorOpen() { Motor abschalten; DoorState = DOOR_OPEN; } void DoorClosing() { Schalte Motor auf Schliessen; DoorState = DOOR_CLOSING; } void DoorClosed() { Motor abschalten; DoorState = DOOR_CLOSED; } Siehst du: nirgendwo ist eine Warteschleife. (Klar musst du jetzt die Abfragen wie 'Taster_Schliessen_gedrückt' durch deine entsprechenden PIN Abfragen ersetzten). Der µC muss nirgends warten, bis eine Aktion abgeschlossen ist. Die Aktion (öffnen, schliessen) wird einfach als Zustand des Systems aufgefasst, in dem es sich befinden kann und aus dem es durch ein Ereignis (Endschalter, was auch immer) herausgeholt wird und in einen anderen Zustand übergeht.
ich würde auch sagen der µC ist hier eher die Steuereinheit, er checkt die lage und gibt dann den befehl. (Wind stark->Tür Zu) dass die Tür jetzt geschlossen wird sollte nicht der µC selbst steuern, eher eine Steuerungskarte mit einer Rückkopplung. Sobald die Tür (sensorisch) geschlossen ist. Kann der µC der Steuerung wieder sagen Sie kann jetzt aufhören mit dem schalten. Diese Prozesse kann er ja dauernd checken, das dauert wahrscheinlich ein paar ms je nach anzahl der Prozesse. Ich glaub eine einfach Steuerung ließe sich entweder mit FETs oder Dauerrelais realisieren. Achja noch ne frage ;). Was für motoren setzt du denn da ein? Langsame gleichstrom getriebemotoren? oder Schrittmotoren?
Hallo zusammen, danke Karl Heinz für deine ausführliche Antwort. Ähnlich habe ich es schon realisiert (siehe Anhang). Aber ich denke ich muß noch mehr zustände definieren. Geht es denn nicht auf die Ressourcen des Stacks da ich ohnenhin schon viele globale Variablen definiert habe. Zur Harware: siehe ebenfalls Anhang.
Donnerwetter! Das ist doch mal ne Superdoku für ein Selbstbauprojekt. Respekt! Ich wünschte sowas würde ich öfter im Beruf sehen.
Ja das sieht doch schon nicht schlecht aus (Hab den Code nur überflogen). Zum Thema Stack: Globale Variablen haben nichts mit dem Stack zu tun, ausser dass sie ein Byte im Speicher verbrauchen. Am Stack kommen funktionslokale Variablen bzw. die Informationen zum Rücksprung aus Unterprogrammen. Was ich an deinem Code auch noch machen würde: Die Funktionen nach Themenkreisen sortieren und für jeden Themenkreis eine eigene *.c / *.h Datei anlegen. Jetzt ist das ganze schon ein bischen groß geworden und die Übersichtlichkeit leidet schon darunter. Siehe zb. http://www.mikrocontroller.net/articles/Funktionen_auslagern_%28C%29 Falls der Link nicht geht: http://www.mikrocontroller.net/articles/Spezial:Allpages und dann 'Funktionen auslagern (C)' suchen.
die doku ist wirklich spitze ;). da fühle ich mich ja ganz schlecht wenn ich an mein eigenes projekt denke. aber vielleicht schreibe ich da auch noch was
Habe jetzt versucht aufzuräumen und zu strukturieren. Habe aber Probleme wenn ich die Zeile #include "LCD_i2c.c" ausblende. Die Zeile #include "keypad_i2c.h" ist vorher angegeben. Wenn ich die Zeile #include xxx.c rausnehme, kommt die Fehlermeldung. Wass könnte es sein? Fehler: !ERROR file 'Gewaechshaus_2006_07.o': undefined symbol '_lcd_i2c_command' !ERROR file 'Gewaechshaus_2006_07.o': undefined symbol '_snake' !ERROR file 'Gewaechshaus_2006_07.o': undefined symbol '_lcd_i2c_init' !ERROR file 'Gewaechshaus_2006_07.o': undefined symbol '_lcd_WR_string' !ERROR file 'Gewaechshaus_2006_07.o': undefined symbol '_lcd_goto' F:\Eigene_Dateien\07_Mikro_electronics\Programme\ICC\Feriges_Programm\ic c_\bin\imakew.exe: Error code 1 Done: there are error(s). Exit code: 1 Include Zeilen: #include <macros.h> #include <STDIO.h> #include <iom32v.h> #include <STDLIB.h> #include <eeprom.h> #include "STRING.H" #include "uart.c" #include "LCD_i2c.h" #include "miscelaneous.h" #include "i2cmaster.h" #include "keypad_i2c.h" #include "uart.h" #include "PCF8573.h" // Beim Ausblenden dieser Zeilen kommen die Fehlermeldungen #include "miscelaneous.c" #include "i2cmaster.c" //#include "LCD_i2c.c" #include "keypad_i2c.c" Danke.
erst mal:
>#include "LCD_i2c.c"
das ist gut so, dass du die raus schmeist. Das macht man
nicht in C.
C funktioniert so:
Du hast den Source code auf mehrere *.c Dateien aufgeteilt.
Dann wird jede einzelne *.c einzelen und für sich alleine
zu sog. Object-Dateien kompiliert (übliche Dateiendung dafür
ist *.o bzw *.obj).
Erst dann, wenn alle Einzelteile kompiliert sind, werden die
Einzelteile zum fertigen Program zusammen-ge-linkt (aus
dem engl. to link - verbinden).
Wenn sich der Linker bei dir also beschwert, dass er eine
Funktion nicht finden kann, dann liegt das meist daran, dass
du ihm nicht alle Einzelteile angegeben hast, die er zum fertigen
Programm zusammen linken soll.
Wie das bei deinem System gemacht wird, musst du in der Doku
nachlesen. Ich kenn den ICC nicht.
Und bevor du fragst: Nein, die
#include "irgendwas.c"
sind keine Lösung dafür. Der Mechanismus des 'Einzelteile
getrennt kompilieren und dann zusammenlinken' hat schon seinen
Grund und ist ein wichtiges Instrument das man nicht leichtfertig
aus der Hand gibt.
Habe jetzt alles verworfen und möchte die ganze Struktur vom Neuen anfangen. Bin auf die Seite http://www.mikrocontroller.net/articles/DCF77-Funkwecker_mit_AVR gestoßen. Statt Header-files zu includieren werden da .c-files includiert. ########## #include "timerfunctions.c" #include "_7_segment.c" #include "dcf77.c" int main (void) { .... ########## Warem den so. Überall wird gesprochen nur .h zu includieren? Bitte um Rat.
"Auf einem µC baut man sowas ganz anders auf. Grundsätzlich gilt die Devise: Keine Warteschleifen die länger als ein paar µs sind." Naja, prinzipiell gilt "Keine Warteschleifen". Auch ein paar us sind Verschwendung und alles worauf man als uC warten muss, lässt sich früh genug antriggern, um es dann zum point zero abholen zu können. ("code in code" Technik). Wer damit nicht so kann, dem sei empfohlen, während der scheinbar unumgänglichen Wartezeit z.B. die LEDs - oder irgendwelche Ports abzuarbeiten.
> Warem den so. > > Überall wird gesprochen nur .h zu includieren? > Und das ist auch richtig so. > Bitte um Rat. Ein Grund warum man keine *.c Files inkludierst. Weil du dann jedesmal den kompletten Source-Code kompilierst. Und das kann dauern.das ist im Prinzip genauso wie wenn du ein einzelnes riesiges Source Code File hast. Beispiel: Das letzte mittelgroße Projekt, an dem ich beteiligt war hatte einen Unfang von ca. 1,5 Mio Lines-of-Code. Ein 1 Ghz PC (meine Arbeitsmaschine) braucht knapp an die 2 Stunden um den Code komplett zu kompilieren. Jetzt stell dir mal vor, ich muss einen kleinen Fehler ausbessern: Fehler ausbessern, 2 Stunden warten. Mist: da war noch eine Kleinigkeit. Wieder 2 Stunden warten. Im Vergleich dazu der richtige Ansatz: In der einen Source- Code Datei die es betrifft den Fehler korrigieren, nur diese eine Source-Code Datei kompilieren (eine Sache von Sekunden) und die Einzelteile zum fertigen Programm linken. Das alles ist eine Sache von ein paar Sekunden und schon gehts zum nächsten Test.
> Bin auf die Seite > http://www.mikrocontroller.net/articles/DCF77-Funkwecker_mit_AVR > gestoßen. Das ist genau ein Beispiel dafür, wie man es nicht macht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.