Hallo zusammen, ich sitze aktuell an einem kleinen Projekt mit einem Atmel Mega16 wo ich einfach keine Struktur in den Code reinbringe. Der kleine Käfer soll folgendes machen: -> Ansteuerung eines LCD's (HD44780, LIB hier aus dem Forum). -> Taster abfragen (alle 10mS mit Timer, LIB hier aus dem Forum). -> hin und wieder eine AD Wandlung durchführen und I2C Werte einlesen (völlig zeitunkritisch). -> hin und wieder Werte über I2C übertragen die zuvor über Tasten und LCD eingestellt wurden. -> möglichst wenig Stromverbrauch da Batteriebetrieb. Gleich mal vorweg, ich hab das schon fertig geschrieben. Leider halt schlecht dokumentiert und immer wieder etwas geändert -> ein ziemliches durcheinander aber es läuft. Ich würde es jetzt gerne nochmals neu machen und dieses mal mit Struktur. Wie geht man an sowas am besten heran? Ich denke der einzig fixe Punkt in dieser Sache ist der 10mS Timer für die Tastenentprellung. Dann könnte man doch einen PowerDown Mode wählen wo dieser Timer an bleibt, oder? Wie realisiert man dann dass die restlichen Dinge sauber ablaufen und z.b. nicht den Timer blockieren? (das hatte ich nämlich auch schon, wollte die AD Wandlung mit in den Timer packen dann reagierten die Tasten nicht mehr sauber). Desweiteren interessiert mich ob z.b. die LCD Routinen mit in die main.c müssen oder ob man die auch auslagern kann? Leider macht das den Code etwas unübersichtlich aber alle Versuche bisher scheiterten weil mein Compiler die Funktionen dann nicht mehr findet. Wäre um ein paar Denkanstöße sehr dankbar.
Matthias M. schrieb: > Leider macht das den Code > etwas unübersichtlich aber alle Versuche bisher scheiterten weil mein > Compiler die Funktionen dann nicht mehr findet. dann müsste wir mal sehen wie du es gemacht hast. Die anzeige sollte in der Main-schleife erfolgen, aber nicht direkt in der Main stehen. Sie sollte in einer extra Prozedur liegen.
Matthias M. schrieb: > Wie realisiert man dann dass die restlichen Dinge sauber ablaufen und > z.b. nicht den Timer blockieren? Ohne delay() und mit kurzen Interruptroutinen
Peter II schrieb: > Matthias M. schrieb: >> Leider macht das den Code >> etwas unübersichtlich aber alle Versuche bisher scheiterten weil mein >> Compiler die Funktionen dann nicht mehr findet. > > dann müsste wir mal sehen wie du es gemacht hast. Die anzeige sollte in > der Main-schleife erfolgen, aber nicht direkt in der Main stehen. Sie > sollte in einer extra Prozedur liegen. der Code ist leider seeeehr unübersichtlich, ich weiß nicht ob es eine gut Idee ist ihn hier komplett zu posten. Für alle hartgesonnenen -> Anhang ;-)
Peter II schrieb: > Die anzeige sollte in > der Main-schleife erfolgen, aber nicht direkt in der Main stehen. Sie > sollte in einer extra Prozedur liegen. Das ist schon mein nächstes Problem. Die Ausgabe auf dem LCD ist sehr zerrupft. Sprich es ist bei jedem Menü ein Kraus erstmal das Display zu leeren weil noch irgendwelche Alten Dinge angezeigt werden. Sollte ich das besser mit einer einzigen Funktion machen die ich von verschiedenen Positionen im Programm aufrufe? Sprich so?: Funktion AusgabeLCD (Zeile1, Zeile2) { Ausgabe Zeile1; Ausgabe Zeile2; } . . . . . main { // Schreibe Willkomemsmeldung: Ausgabe LCD("Willkommen",""); . . . // Schreibe Status ASRC: Ausgabe LCD("Status ASRC1:","downsampling"); } Also natürlich sehr vereinfacht, aber ist der Denkansatz richitg?
Du solltest einfach etwas strukturierter programmieren. Was mir auf den ersten Blick auffällt: * Haufenweise globale Variablen: das solltest du vermeiden. Deklariere Variablen in dem Scope, in dem du sie brauchst, und tausche Informationen nur über Parameter und Rückgabewerte von Funktionen aus. Globale Variablen brauchst du dann sehr selten. Du hast dann auch weniger Seiteneffekte und der Code sollte besser verständlich sein. * Mangelhafte Kapselung: Teile die Aufgaben sinnvoll auf die Funktionen auf. Deine state machine in main() z.B. sollte wirklich nur eine state machine sein, sonst wird das zu unübersichtlich. Extrahiere die Funktionalität der einzelnen States immer in Funktionen. * Teile eventuell den Sourcecode in mehrere Dateien auf, du kannst beispielsweise alles was mit dem LCD oder mit I2C zu tun hat jeweils in eine separate Datei auslagern. Das sorgt auch dafür, dass du dir Gedanken über saubere Interfaces machen musst.
Zerlege das Programm in Tasks, also die Aufgaben, die Du ganz oben genannt hast. Jeder Task ist eine funktion. Starte einen 10ms Timer, der den Controller aufwachen lässt. Das Hauptprogramm ist eine Schleife, welche die Tasks nacheinander ausführt und dann als letzten den Controller schlafen legt. So werden alle Tasks im 10ms Rythmus ausgeführt - vorausgesetzt alle Tasks zusammen dauern nicht länger als 10ms. Die Displayausgabe könnte zu lange dauern. Lege Dir einen Puffer an (char array) und übertrage bei jedem Schleifendurchlau nur ein (oder ein par) Zeichen. Wenn die 10ms pro Schleifendurchlauf nicht unbedingt eingehalten werden müssen, kannst Du Dir den Aufwand sparen.
1 | int main(...) { |
2 | while (1) { |
3 | task_tasten_abfragen(); |
4 | task_a_d_wandlung(); |
5 | task_i2c_uebertragen(); |
6 | task_display_ausgabe(); // falls nicht in den anderen Funktionen eingebettet |
7 | sleep_cpu(); |
8 | } |
9 | } |
Der timer-interrupt wäre dabei im einfachsten Fall eine leere Funktion. Du könntest da auch einen Zeitmesser unterbringen, der bei jeden Imterrupt eine unsinged int (oder long int) Variable um 1 erhöht.
1 | volatile unsigned int systemtimer; |
2 | ISR(TIMER0_OVF_vect) |
3 | { |
4 | systemtimer++; |
5 | } |
Der Timer wird z.B. nützlich, wenn ein Task z.B. ein mal pro Sekunde ausgeführt werden soll.
1 | function task_sekunden_blinker() { |
2 | if (systemtimer%100==0) { |
3 | // toggle LED |
4 | PORTB ^= ( 1 << PB0 ); |
5 | } |
6 | } |
Du kannst den Timer auch nutzen, um Zeiten zu messen (im 10ms Raster):
1 | if (taste_gedrueckt) { |
2 | start_time=systemtimer; |
3 | } |
4 | else { |
5 | unsinged int duration=systemtimer-start_time; |
6 | } |
Das funktioniert auch, wenn der Timer zwischenzeitlich einmal (aber nicht mehrmals) überlauft.
Wow danke für die hilfreichen Tipps :) Werd mich gleich ransetzen und sehen wie ich das umsetzen kann.
greg schrieb: > Teile eventuell den Sourcecode in mehrere Dateien auf, du kannst > beispielsweise alles was mit dem LCD oder mit I2C zu tun hat jeweils in > eine separate Datei auslagern. Das sorgt auch dafür, dass du dir > Gedanken über saubere Interfaces machen musst. Wie mache ich das? Ich hatte das schon versucht, also eine eigene Datei z.b. lcd_functions.c zu erstellen wo alle LCD Funktionen drin lagen. Diese Datei hab ich dann gleich zu Anfang des Programmes mit include "lcd_functions.c"; eingebunden. Der Compiler meckert dann allerdings dass er die Funktionen nicht findet.
Matthias M. schrieb: > Wie mache ich das? Indem du dich informierst, was eine Header-Datei ist, dann diese schreibst und in deiner Hauptdatei einbindest. Und dann kompilierst du auch die neue Datei lcd_function.c und linkst die mit dazu.
Hmmm, es gibt Optimierungspotential ;-) typedef struct { uint8_t reg_off; uint8_t reg_val_D24_8;uint8_t reg_val_D14_8;uint8_t reg_val_D08_8;uint8_t reg_val_D00_8; uint8_t reg_val_D24_7;uint8_t reg_val_D14_7;uint8_t reg_val_D08_7;uint8_t reg_val_D00_7; uint8_t reg_val_D24_6;uint8_t reg_val_D14_6;uint8_t reg_val_D08_6;uint8_t reg_val_D00_6; uint8_t reg_val_D24_5;uint8_t reg_val_D14_5;uint8_t reg_val_D08_5;uint8_t reg_val_D00_5; uint8_t reg_val_D24_4;uint8_t reg_val_D14_4;uint8_t reg_val_D08_4;uint8_t reg_val_D00_4; uint8_t reg_val_D24_3;uint8_t reg_val_D14_3;uint8_t reg_val_D08_3;uint8_t reg_val_D00_3; uint8_t reg_val_D24_2;uint8_t reg_val_D14_2;uint8_t reg_val_D08_2;uint8_t reg_val_D00_2; uint8_t reg_val_D24_1;uint8_t reg_val_D14_1;uint8_t reg_val_D08_1;uint8_t reg_val_D00_1; uint8_t reg_val_D24_0;uint8_t reg_val_D14_0;uint8_t reg_val_D08_0;uint8_t reg_val_D00_0; } reg_value256; Das schreit nach einem Array! Die LCD Sachen gehören in eine separate .c Datei mit dazugehörger .h. datei, so wie es im Forum/Wiki schon drin steht. Gleiches gilt für TWI Ebenso die ganzen Menusachen. Deine Hauptschleife rennt mit maximaler Prozessorgeschwindigkeit, das ist werder nötig noch sinnvoll. Dort sollte eine "Bremse" rein, sprich, nur alle ???ms ein Durchlauf erfolgen. Das kann man prima per Timer und Interrupt steuern.
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.