Ich progge gerade an einem AT89S52. Die Aufgabe ist es einen Wert vom A/D-Wandler in einem Bargraph umzusetzen. Ausgegeben auf einem 40Zeichen Display. Wie programmiere ich es am geschicktesten OHNE 255 mal "cjne" zu verwenden? Sowas habe ich schon bei Drehzahlmessern etc. gemacht und konnte mich einfach an die 1000'er Stelle vom auzugebenden Wert hängen. Also bei 1000 -> einen Balken erzeugen; bei 2000-> zwei Balken etc. ABER das Problem jetzt ist, dass es eine NICHTLINEARE Kennlinie ist, die eingelesen wird, so dass ich mit einfachen Bitoperationen oder Betrachtung von Wertigkeiten nicht allzuweit komme. Wie kann ich das denn sonst lösen? Grüße Rick Dangerous
Wenn ich dich richtig verstehe, dann willst du auf einem LCD einen Balken darstellen, dessen Länge abhängig von einem AD Wert ist. Es gibt einige Lösungen, je nach Auflösung: Soll nur Zeichenweise angezeigt werden (Auflösung: 40 Schritte), dann kannst du einfach den Messwert durch 6 Teilen und das Ergebnis auf 40 begrenzen. Nun machst du zwei Schleifen die eine läuft von 0 bis Wert, und schreibt Blöcke ins LCD, die zweite läuft von Wert+1 bis 40 und schreibt Leerzeichen.
diesen ".db" befehl habe ich leider nicht beim 89s52. "den Messwert durch 6 Teilen" wieso denn durch 6? Sorry aber kannst du die Rechnung etwas erläutern, komme jetzt nicht drauf. Das Problem ist die nicht lineare Kennlinie am AD-Wandler Eingang. Ich kann also meine 256 Werte nicht einfach durch 40 Teilen und den ganzzahligen Anteil als Balken ausgeben! Ich habe z.B. von 0-10 einen Balken, dann wieder von 11-16 usw, also die Abstände sind nicht immer gleich. Die Ausgabe ist kein Problem, nur die Rechnung fehlt mir und den Wert 256 mal zu vergleichen ist eine Heiden-Arbeit...
achso das mit dem :6 hab ich verstanden, aber leider geht es nicht! Die Abstände sind nicht immer 6 Bit lang.
Versteh ich nicht. Und die Aussage es gibt keine Tabelle beim 89S52 ist falsch ! Dann machs doch einfach so: Eine Tabelle, 256Bytes lang. mov dptr, Tabellenstartadresse mov a, Messwert movc a, @a+dptr Mit dem Ergebnis (das im Bereich 0-40 liegt, jenachdem wie du die Tabelle schreibst), gehst du zur Anzeige wie ich es beschrieben habe.
Bei Nichtlinearität wirst du da nicht drumherumkommen. Kennst du die Funktion, der die Eingangswerte an deinem AD-Wandler unterliegen? Olli
Wenn ich Benedikt und dich richtig verstehe dann lies das, ansonsten ignorier den Eintrag: A/D Wert geht von 0-255 Dein Balken geht von 0-39 (40 Zeichen lang) Jetzt willst du mit dem Balken 0% - 100% anzeigen (entspricht A/D Wert 0-255). oder? Also mach es so wie benedikt schon gesagt hat: 255 (100%) / 6 = 42,ungerade da das mehr als 40 zeichen balkenlänge wären: ----------------- CPI AD_IN, 40 BRLT Weiter LDI AD_IN, 39 Weiter: ----------------- Damit hast du in AD_IN dann nur noch Zahlen von 0-39 stehen, also maximal eben 40 Zeichen. die 100% laut dem Balken erreichst du dann schon bei AD_IN = 237 aber das macht ja nix (oder doch?) :)= Und für die Darstellung des Balkens dann eben wie benedikt schon gesagt hat, die beiden schleifen setzen. fertig, wo liegt das problem?
@Benedikt, das mit dptr und dem erzeugen einer tabelle, ist eine Möglichkeit, danke dir! Habe bis jetzt noch nicht mit dem dptr gearbeitet, nur zum löschen des Speichers beim Anlaufen. Mal sehen, ob ich das hinbekomme. Nein ich kenne die Funktion nicht, das ist ja das Problem! Ich habe nur 20 Messpunkte, brauche aber 40 (Barghraph), also habe ich einfach eine Kennlinie aus den 20 Punkten gemacht und weitere 20 hinzugefügt. Bei dem Projekt gehts um die LambdaSpannung im Auto! Die Spannung geht von 0-1V wird verstärkt auf 0-5V und geht dann zu einem AD-Wandler, der am AT89S52 hängt. Habe dazu mal im Net folgende Tabelle gefunden, allerdings kann ich mit dem .db nichts anfangen http://www.taunus-biker.de/~mdvp/Lambda/L_U.dat Hier sieht man auch, dass ich mit den einfachen Formeln nicht drangehen kann. Muss über eine Tabelle laufen!
Schau dir mal die Dokumentation des Assemblers an, wie man Tabellen anlegt usw. Meist mit db (Data Base) so z.B.: db 0,0,1,1,1,1,1,1,2,2,2 Im Bereich 0-1 zeigt das LCD nichts an, 2-7 ein Segment, ab 8 zwei Segmente usw.
Also, du baust eine Tabelle mit 40 Werten, wobei jeder Wert den ADC Schritt zum nächsten Wert beschreibt. D.h. in der Tabelle sind nur die ADC Differenzen von Balken zu Balken. Nun hast du im Programm eine Schleife von 0 bis 39 die vom aktuellen ADC Wert jeweils den Step aus der tabelle lädt und subtrahiert. Gibt es einen Unterlauf, d.h. das Ergebnis der Subtraktion wird Negativ so wird diese Schleife abgebrochen. Exakt in diesem Moment beschreibt der Zählerwert der Schleife die Anzahl der einzuschaltenden Blacken. uint16_t Steps[40] = {10,20,30,40,50....}; uint8_t Compute(int16_t ADC) { for (uint8_t i = 0; i++; i < 40) { ADC -= Steps[i]; if (ADC <= 0) return(i); } } Wie du nun deine Schritte in Steps scalierst ist DEINE Sache, wichtig ist nur das die Summe aller Schritte dem maximal möglichen ADC Wert entspricht. Im Falle eine 10 Bit ADC also 1024. Eine Lineare Scalierung entspräche also 1024 / 40 = 25.6 rund 26 pro Schritt. In Steps[] würde also jedes Element 26 sein. Dann wäre es eine lineare Scalierung. Gruß Hagen
@Benedikt Danke werds mal probieren. Hagen und jetzt bitte in Assembler ;-) In C sieht es schon anders aus.
.def ADC_L = r2 .def ADC_H = r3 .def BargraphCount = r4 .def Zero = r5 ldi ZL, lo8(Steps) ldi ZH, hi8(Steps) ldi BargraphCount, 0xFF clr Zero Loop: inc BargraphCount lpm r0, Z+ sub ADC_L, r0 sbc ADC_H, Zero brsh Loop So, mal in AVR ASM, musste noch translieren da ich mich nicht so gut auskenne, sorry ;) Gruß Hagen
Eine weitere Idee wäre die Mathematische. Ich habe mich jetzt damit nicht genauer befasst, aber es sollte Speicherplatz auf Kosten von Rechenzeit sparen. Du hast eine Abbildung von anscheinend 256 Ganzzahlenwerten auf 40 Ganzzahlenwerte. Diese Abbildung kennst du in 20 Punkten, wie du gesagt hast. Durch Interpolation kannst du wahrscheinlich eine recht einfache Funktion bilden, die in diesen 20 Punkten der Abbildung entspricht und für dazwischenliegende Werte ebenfalls sinnvolle Werte ausgibt. Wahrscheinlich kommst du mit einem Polynom recht niedrigen Grades aus. Jetzt brauchst du nur das Polynom zu speichern. Möchtest du nun für einen gegebenen Eingabewert die Bargraph-Anzeige herausbekommen, musst du nur noch dein Polynom auf den Eingabewert loslassen. Wenn du dich mit Mathematik nicht so gut auskennen solltest, dann kann ich dir auch gerne dabei helfen.
Wobei sich die Frage stellt ob die Speicherung des Polynoms plus der Verbrauch an Code für die kompliziertere Mathematik, wirklich weniger Resource benötigt als mein obiger Vorschlag. Der benötigt 40 Bytes für die Tabelle + ca. 18 Bytes für den Code == 58 Bytes. Ich glaube kaum das eine komplizierte Polynomarithmetik mit 58 Bytes im gesamten auskommt. Ein weiterer Vorteil des obigen Vorschlages ist das die Tabelle Steps[] ja absolut NICHT-Linear sein kann, was bei vergleichbaren Polynomen diese wesentlich größer und komplexer machen wird. Denoch, ein kleines Beispiel deinerseits würde ich mal sehen wollen ;) Gruß Hagen
Ohne Tabelle, bzw. Tabelle im Code benötigst du 40 Subtraktionen + 39 Branches + 39 Inkrements bei einem 8Bit ADC macht ca. 240 Bytes, also so: sub ADC, Step1 brcs Exit inc Bargraph sub ADC, Step2 brcs Exit inc Bargraph sub ADC, Step3 brcs Exit inc Bargraph ... Exit: Aber das ist ja das was du NICHT wolltest. Gruß Hagen
Für dieses kleine Beispiel ist eine allgemeine Interpolation vielleicht wirklich etwas Overkill. Ich habe es aber näherungsweise mal versucht. Im Anhang befindet sich eine Grafik, die das Interpolationsverhalten darstellen sollte. Die Kurve wurde in 4 Bereiche eingeteilt, die jeweils mit einem Polynom dritten Grades interpoliert werden. Der zweite Abschnitt sogar nur mit einem Polynom zweiten Grades. Die Berechnung erfolgt jetzt mit den hardgecodeten Werten nach dem Horner Schema. Der Aufwand ist hierfür doch recht gross, denn genaue Arithmetik mit nur 8 Bit breiten Ganzzahlen ist nicht so einfach. Ich habe mir zum Test mal die Mühe gemacht und den ersten Bereich in Matlab implementiert, wobei ich teilweise 16-bit-Breite Register vorausgesetzt habe. Zudem müsste man negative Zahlen entsprechend behandeln, all das wäre dann Implementierungssache. Wie man also sieht, ist mein Vorschlag in diesem Fall vollkommen inpraktikabel. Die gezackte Linie zeigt das Ergebnisse meines Versuchs. Naja, es war mal ne interessante Sache, sich damit zu befassen und zu sehen, was möglich ist und was nicht. Wenn sowieso eine Floating-Point-Emulation existiert, würde es die Berechnung um ein Vielfaches einfacher machen (Einfach Horner-Schema benutzen, macht bei einem Polynom dritten Grades 3 FP-Multiplikationen und 3 FP-Additionen.)
Denoch, diese Interpolation ist nun nötig um die 40 Elemente Tabelle korrekt vorauszuberechnen :) Gruß Hagen
Ich finde, dass sie diese Funktion aber ganz gut in mehrere lineare Bereiche gliedern lässt. Da eine auflösung von 40 LEDs auch nicht besonders gross ist, kann man das wohl ganz gut machen. Ich habe es mal in 6 Bereiche eingeteilt (s. Anhang). Man braucht jetzt nur noch 14 Bytes für die 7 Stützpunkte. Der Code ist dann auch recht einfach: ----8<------------------------ Sei A der gelesene Wert. Sei X[0..5] ein Array mit den X-Differenzen der linearen Intervalle Sei Y[0..5] ein Array mit den Y-Differenzen der linearen Intervalle int i; int wert = 40; for (i = 0; i < 6; i++) { A -= X[i]; if (A < 0) { A += X[i]; goto gefunden; } wert -= Y[i]; } gefunden: int steigung = Y[i]/X[i]; wert -= A * steigung; ----8<------------------------ Ich hoffe, ich habe da jetzt keinen Denkfehler drin! Die Idee ist, dass man durch sukzessives Subtrahieren prüft, in welchem Intervall man sich befindet. Wird A < 0, hat man das Intervall gefunden, geht also wieder einen Schritt zurück. Der Y-Wert (variable "wert") fängt ja bei 40 an und wird auch bei jedem Schritt subtrahiert, wenn wir ein Intervall weiter gehen. Haben wir das Intervall gefunden, verlassen wir die Schleife und berechnen mit dem Differenzenquotienten die Steigung. Diese wird mit dem X-Wert relativ zum Intervallanfang multipliziert (der jetzt ja in A steht) und von "wert" subtrahiert. Dies sollte nicht allzu schwer in ASM zu implementieren sein. Der Code ist zudem unabhängig von der Anzahl der linearen Intervalle, einfach nur die Schleifengrenze hochsetzen und es sollte klappen.
Nachtrag: In ASM sollte man int steigung = Y[i]/X[i]; wert -= A * steigung; besser andersherum machen, erst die Multiplikation A * Y[i] und dann erst die Division durch X[i].
OK habs zum laufen gekriegt. Habe mir einfach die Kennlinie nochmal vorgenommen und dann das so gemacht wie du gemeint hast Benedikt. Also mit dem AD-Wert :6. Funktioniert wunderbar, allerdings muss ich noch quelltext optimieren... Grüße und Danke
Habe mir die Kennlinie letztendlich linearisiert und meine Skala angepasst. D.h. die Schritte in der Mitte sind enger aneinander als die Schritte außen z.B. Balken Nr.1 = 1,18 lambda Balken Nr.2 = 1,165 lambda ... Balken Nr.20 = 1,0 Balken Nr.21 = 0.996 ... War die schnellste Lösung, die gleichzeitig einen Vorteil hat: und zwar vergrößere ich mir meine Auflösung der Kennlinie im interessantesten Bereich!
Zwischen welchen Werten ändert sich eigentlich die Anzeige bei kaltem und warmen Motor?
Die Werte der Sonde sind extrem Temperatur abhänig! Im Grunde genommen ist es Schwachsinn, auf 3 Nachkommastellen genau rechnen zu wollen! Wenn du googlest findest du Funktionen, die dir die Temperaturabhänigkeit zeigen. Habe Bilder grad nicht da...sorry
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.