Hallo Leute, das ist mein erster Beitrag hier, muss mich aber schon für die versch. Tutorials und Beiträge bedanken, sie sind recht nützlich! Nun habe ich auch selbst ein Problem, an dem ich schon eine Weile grüble: Gegeben ist ein AVR-Board(http://ti.tuwien.ac.at/ecs/teaching/courses/mclu/manuals/atmega16) und ein Erweiterungsboard (http://ti.tuwien.ac.at/ecs/teaching/courses/mclu/manuals/i-o-board), das wir auf der Uni verwenden. Ich habe mir das Board selbst zugelegt und möchte nun eine Aufgabe erweitern: Über den ADC möchte ich den Wert von einem Poti einlesen und dann den Wert auf 4 Stk. der 7Segment Anzeigen ausgeben. Nun zu meinen Problemen: Ich rechne den Wert von ADC in 0-5V um, und gebe das auf den Anzeigen aus. Um die einzelnen Stellen bestimmen zu können, experiemntiere ich mal mit einem fixen Wert und beachet den Wert von ADC vorerst nicht. Ich verwende Timer1 (also 16bit, mit Prescaler 8 und CPU_F 16MHz, CTC Mode, OCR1A auf 8000) um meien Displays zu multiplexen. Das funktioniert auch gut, davon vielleicht abgesehen, dass ich bei manchen Ziffern einige Segmente als ganz leicht beleuchtet sehe. Ich nehme an, das könnte davon kommen, dass ich zu schnell multiplexe und auf den Ausgangspins noch ein wenig Spannung von den vorhergehenden Ziffern anliegt (bin dem noch nicht nachgegangen). Was aber für mich verwunderlich ist, sobald ich Timer2 (8bit, Prescaler auf 32, CTC, OCR2 auf 200) nehme, bekomme ich statt 3.906 nur noch 3.880 auf dem Display angezeigt. Woran kann das liegen? Schalte ich meine ADC-Init Routine dazu, dann erscheinen überhaupt nur noch "3.3.", wobei der zweite "3." nur noch halb so hell leuchtet, wie der erste. Das ist mit beiden Timern der Fall. Ich nehme an, die ADC-Konversionsroutine würde zu viel Zeit brauchen, sodass die Anzeige nicht mehr richtig dargestellt werden kann? Da wäre noch dazu zu sagen, dass ich in der Timer-ISR nur die aktuelle Position für das Multiplexing berechne, die Umrechnung und die Anzeige mache ich in der For-Schleife in der main() Funtkion. Verschiebe ich die Berechnung und die Anzeige in die ISR, wird "3.0" anzeigt, wobei auch hier die "0" schwächer leuchtet. Achja, ich programmiere in C, unter Linux. Vielen Dank schon mal im Voraus. MfG BBKing
Wie wäre es, wenn du dein Programm posten würdest? Alles andere, was man dazu sagen kann, ist Wahrsagerei...
okok, ich weiss, ihr habt keine Glaskugel... zum Code: ich weiss, dass man da noch einiges "besser" machen kann, er ist noch lange nicht "vollendet" in der initTimer() Funktion gibt es die Initialisierung für den 8bit bzw. 16bit Timer...wie oben schon beschrieben, ergeben sich die zwei verschiedenen Verhaltensweisen bei der Ausgabe. Weiters verstehe ich nicht, warum initADC() ebenfalls die Ausgabe beeinflusst? VIelen Dank im Voraus. MfG BBKing
Du liest zwei mal ADCH aus. Laut Datenblatt ist es bereits nach dem ersten auslesen auf 0. Für was ist diese float Berechnung? Da kannst du ein Zeitproblem haben, sollte man vermeiden.
Mit der Float-Berechnung skaliere ich den Wert aus ADCH auf 0-5V hinauf, und bestimme danach die Ziffern für die 1er, 10tel, 100stel und 1000stel Stelle. Bin jetzt schon ein bisschen weiter, dieser Thread hat mir einiges geholfen ADC mit Multiplexanzeige, aber dei Anzeige flimmert immer noch irsinnig. Wie müsste man soetwas prinzipiell machen? Danke
Hubert G. schrieb: > Du liest zwei mal ADCH aus. Laut Datenblatt ist es bereits nach dem > ersten auslesen auf 0. Quelle? Ich glaube nämlich nicht, dass das Register nach dem Auslesen gelöscht wird.
Soweit ich verstanden habe, stimmt das mit dem 0-Wert nicht: "If the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read ADCH. Otherwise, ADCL must be read first, then ADCH, to ensure that the content of the Data Registers belongs to the same conversion. Once ADCL is read, ADC access to Data Registers is blocked. This means that if ADCL has been read, and a conversion completes before ADCH is read, neither register is updated and the result from the conversion is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is re-enabled." aber das ist eh egal, weil ich lese ADCH nur einmal, die andere Zeile ist auskommentiert... Ich glaube auch, dass das ein Timing-Problem ist, weiss nur nicht, wie ich anfang soll zu debuggen....
Fließkommaberechnungen sind irre zeitaufwändig, mach das mit Ganzzahlen und dividiere am Schluß. Die Toleranz von Referenzspannung ist sicher größer als die Ungenauigkeit wenn du ein paar Stellen weglässt. Das mit dem ADCH auf 0 stellen ist falsch, the ADC Data Register is not updated until ADCH is read.
>SFIOR &= ~(1<<ADTS2) | ~(1<<ADTS1) | ~(1<<ADTS0);
Ob das den Effekt hat, den du gerne hättest?
Deine ganze Multiplexerei ist .... Müll Du kannst dir ruhig 4 globale unsigned char (zb als ein Array) zurechtlegen, in die du das auszugebende Bitmuster ablegst. In der ISR machst du dann momentan angezeigte Stelle abschalten Stelle um 1 weiterschalten (unter Berücksichtigung des 'Überlaufs' Die für diese Stelle notwendigen LED einschalten (Bitmuster dafür hlst du dir aus dem Array Anzeige einschalten. Und du brauchst auch keine so dermassen hohe Multiplexfrequenz. Wenn deine 4 Stück 7-Segment 100 mal in der Sekunde reihum einmal eingeschaltet werden, dann langt das normalerweise dicke. Wenn du dann eine Ausgabe machen willst, dann schreibst du einfach in das globale Array für die richtige Stelle das Bitmuster, welches die richtigen LED einer Stelle zum Leuchten bringt.
Habe das jetzt ausprobiert, der Unterschied ist irre! Ich bin zwar mit den angezeigten noch nicht zufrieden, aber die Anzeige selbst ist schon viel schöner (100%ig aber noch nicht OK)! Danke!
Ungefähr so
1 | ....
|
2 | unsigned char Stelle[4]; |
3 | |
4 | SIGNAL(SIG_OUTPUT_COMPARE2) |
5 | {
|
6 | // PORTB = Wert, damit die momentane iPos Anzeige dunkel wird
|
7 | // Ich rate mal
|
8 | PORTB = 0; |
9 | |
10 | if (iPos < 3) |
11 | iPos++; |
12 | else
|
13 | iPos = 0; |
14 | |
15 | PORTC = Stelle[iPos]; |
16 | PORTB = aDisplayPos[iPos]; |
17 | }
|
Wenn du einen neuen Wert auszugeben hast, dann schreibst du das Ergebnis von aIntDisplayDecode in die jeweilige Stelle[i], je nach Wert.
1 | void OutNumber( unsigned int Number ) |
2 | {
|
3 | Stelle[3] = aIntDisplayDecode[ Number % 10 ]; |
4 | Number /= 10; |
5 | Stelle[2] = aIntDisplayDecode[ Number % 10 ]; |
6 | Number /= 10; |
7 | Stelle[1] = aIntDisplayDecode[ Number % 10 ]; |
8 | Number /= 10; |
9 | Stelle[0] = aIntDisplayDecode[ Number % 10 ]; |
10 | }
|
(Hab nicht rausgefunden, wie man bei dir eine 7_Segment über PORTB abschalten kann) (Wozu hast du eigentlich verschiedene Decodiertabellen für int und float? Die einzelnen Ziffern 0 bis 9, A bis F sehen doch völlig gleich aus?) Jetzt hast du dann alle Zeit der Welt, eine Zahl in 4 Ziffern zu zerlegen, unabhängig vom Multiplexing der Anzeige. Die Werte treiben das Display! Und nicht die Multiplexfrequenz bestimmt, wann du deine Zahl zerlegen musst!
Balazs B. schrieb: > Mit der Float-Berechnung skaliere ich den Wert aus ADCH auf 0-5V hinauf, > und bestimme danach die Ziffern für die 1er, 10tel, 100stel und 1000stel > Stelle. Wozu? #define ADC_REF_QUOT_8BIT 0.01953125 //= 5/(255+1) Wenn du nicht mal 5 sondern mal 5000 rechnen würdest, dann ist das Ergebnis deiner Umrechnerei eine ganze Zahl von 0 bis 5000. Noch den Dezimalpunkt nach der dritten Stelle einschalten und schon steht auf der Anzeige 5.000 anstatt 5000 oder 3.654 anstelle von 3654. Aber: Du hast alles in int gerechnet (oder in long, wegen der Überläufe) und das geht um einiges schneller als float.
Karl heinz Buchegger schrieb: > Deine ganze Multiplexerei ist .... Müll > > Du kannst dir ruhig 4 globale unsigned char (zb als ein Array) > zurechtlegen, in die du das auszugebende Bitmuster ablegst. > > In der ISR machst du dann > momentan angezeigte Stelle abschalten > Stelle um 1 weiterschalten (unter Berücksichtigung des 'Überlaufs' > Die für diese Stelle notwendigen LED einschalten (Bitmuster > dafür hlst du dir aus dem Array > Anzeige einschalten. ich mach das jetzt so: die Berechnung der gerade aktuellen Displaystelle mach ich in der ISR vom Timer und starte dort die Konvertierung. in der ISR vom ADC lese ich den Wert aus, mache meine Berechnungen und schalte dort die aktuelle Anzeige ein. Jetzt ist die Anzeige wunderschön! > Und du brauchst auch keine so dermassen hohe Multiplexfrequenz. Wenn > deine 4 Stück 7-Segment 100 mal in der Sekunde reihum einmal > eingeschaltet werden, dann langt das normalerweise dicke. > > Wenn du dann eine Ausgabe machen willst, dann schreibst du einfach in > das globale Array für die richtige Stelle das Bitmuster, welches die > richtigen LED einer Stelle zum Leuchten bringt. Das habe ich mittlerweile schon so umgebaut, der Code von Peter Dannegger war dazu echt hilfreich! Habe noch den Code erweitert, der ADC Wert wird auf Timer1 als PWM Impuls ausgegeben um einen Motor anzutreiben. Danke an alle für die Tipps!
Karl heinz Buchegger schrieb: > Ungefähr so > [C] > .... > unsigned char Stelle[4]; > > SIGNAL(SIG_OUTPUT_COMPARE2) > { > // PORTB = Wert, damit die momentane iPos Anzeige dunkel wird > // Ich rate mal > PORTB = 0; geht eh so. Ich möchte dann das ganze in allgemeine Funktionen umschreiben, sodass man die Ports einmal am Anfang angeben muss und sonst nie wieder. Work in Progress... > (Wozu hast du eigentlich verschiedene Decodiertabellen für int und > float? Die einzelnen Ziffern 0 bis 9, A bis F sehen doch völlig gleich > aus?) Vielleicht eine Overkill Lösung, aber im Array mit den Floats habe ich den Punkt noch dazugeschaltet... > Die Werte treiben das Display! Und nicht die Multiplexfrequenz bestimmt, > wann du deine Zahl zerlegen musst! Das ist wohl der Punkt! Die Frage ist nur, wo Du die Funktion dazu aufrufst? In meinem vorigen Posting, das ca. zeitgleich mit Deinem eingetroffen ist, habe ichs ein bisschen beschrieben.... Riesen Dank für die Mühe!
Balazs B. schrieb: >> Die Werte treiben das Display! Und nicht die Multiplexfrequenz bestimmt, >> wann du deine Zahl zerlegen musst! > > Das ist wohl der Punkt! Die Frage ist nur, wo Du die Funktion dazu > aufrufst? Gar nicht. Das Multiplexing findet völlig unabhängig von allem anderen statt. Das globale Array kann man sich als eine Art Displayspeicher darstellen, welches die Anzeige repräsentiert. Schreibt man dort ein Muster für die 7-Segment Anzeige hinein, dann sorgt ein 'magischer Mechanismus' (die ISR) dafür, dass dieses Muster auf der Anzeige erscheint. Für denjenigen Programmteil, der sich um die eigentliche Auswertung kümmert und letztendlich mit einem Zahlenwert hochkommt, ist es völlig unerheblich, wie dieser Mechanismus passiert. Der beschreibt einfach nur seinen 'Displayspeicher'. Für ihn sind diese 4 Elemente des globalen Arrays die 7-Segmentanzeige. Und die Funktion, die eine Zahl in 4 Stellen aufdröselt: Die wird klarerweise aufgerufen, wenn eine neue auszugebende Zahl vorliegt.
Balazs B. schrieb: > > Vielleicht eine Overkill Lösung, aber im Array mit den Floats habe ich > den Punkt noch dazugeschaltet... Wenn du eine allgmeine Lösung haben willst, dann lass dieses zusätzliche Kodierarray weg. Das verwirrt ungemein. Alles was du brauchst ist eine Funktion
1 | void OutNumber( unsigned int Number, unsigned char KommaPosition ) |
2 | {
|
3 | ...
|
der du eine Zahl übergibst und auch noch dazusagst an welcher Stelle der Dezimalpunkt auftauchen soll. Von mir aus mach dir noch eine Funktion für einen float, aber wie schon gesagt wurde, vermeidet man float. In den allermeisten Fällen benötigt man sie nicht. Man macht das eher so, dass alle Zahlen zb mal 1000 genommen werden und bei der Anzeige der Dezimalpunkt an der richtigen Stelle eingeschmuggelt wird. Für den Benutzer, der aufs Display schaut, kommt es aufs gleiche raus. Aber den µC entlastet man damit schon enorm.
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.