Forum: Mikrocontroller und Digitale Elektronik Bargraph in Assembler ?


von Rick Dangerous (Gast)


Lesenswert?

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

von Sebastian Wille (Gast)


Lesenswert?

Hi Rick,

vielleicht über eine Nachschlagetabelle!?!

Wie beim AVR der Befehl ".db".

Sebastian

von Benedikt (Gast)


Lesenswert?

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.

von Rick Dangerous (Gast)


Lesenswert?

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...

von Rick Dangerous (Gast)


Lesenswert?

achso das mit dem :6 hab ich verstanden, aber leider geht es nicht!
Die Abstände sind nicht immer 6 Bit lang.

von Benedikt (Gast)


Lesenswert?

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.

von Oliver Rogasch (Gast)


Lesenswert?

Bei Nichtlinearität wirst du da nicht drumherumkommen.
Kennst du die Funktion, der die Eingangswerte an deinem AD-Wandler
unterliegen?

Olli

von Malte Bayer (Gast)


Lesenswert?

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?

von Rick Dangerous (Gast)


Lesenswert?

@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!

von Benedikt (Gast)


Lesenswert?

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.

von Hagen (Gast)


Lesenswert?

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

von Rick Dangerous (Gast)


Lesenswert?

@Benedikt
Danke werds mal probieren.

Hagen und jetzt bitte in Assembler ;-)
In C sieht es schon anders aus.

von Hagen (Gast)


Lesenswert?

.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

von Christof Krüger (Gast)


Lesenswert?

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.

von Hagen (Gast)


Lesenswert?

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

von Hagen (Gast)


Lesenswert?

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

von Christof Krüger (Gast)


Lesenswert?

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.)

von Christof Krüger (Gast)


Angehängte Dateien:

Lesenswert?

natürlich habe ich den Anhang vergessen...

von Hagen (Gast)


Lesenswert?

Denoch, diese Interpolation ist nun nötig um die 40 Elemente Tabelle
korrekt vorauszuberechnen :)

Gruß Hagen

von Christof Krüger (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Christof Krüger (Gast)


Lesenswert?

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].

von Rick Dangerous (Gast)


Lesenswert?

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

von Christof Krüger (Gast)


Lesenswert?

Also doch jetzt den Kurvenverlauf als linear angenommen?

von Rick Dangerous (Gast)


Lesenswert?

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!

von Axel Rühl (Gast)


Lesenswert?

Zwischen welchen Werten ändert sich eigentlich die Anzeige bei kaltem
und warmen Motor?

von Rick Dangerous (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.