Hallo zusammen,
ich versuche irgendwie das Busy flag einer LCD anzeige abzufragen. Dazu
habe ich die lcd funktionen aus dem Tutorial hier erweiert. Nur leider
wills noch nicht richtig funktionierne.
Der Source ist im Anhang.
Folgendes ist mir aufgefallen.
Wenn ich statt lcd_wait_busy, delay5ms aufrufe in der "funktion"
lcd_data funktioniert alles einwandfrei. Sobald ich nun delay5ms durch
lcd_wait_busy ersetzte wird nur noch der erste Buchstabe ausgegeben.
Eine Endlosschleife scheint es aber nicht zu geben, da die LEDs an PortB
Leuchten.
Also dachte ich mir, wird wohl die busy schleife nicht richtig
funktionieren und ist zu kurz ... Daher habe ich in lcd_wait_busy
delay5ms aufgerufen. Die Zeit müsste also reichen, da es bei direktem
delay5ms ja auch funktioniert hat. Aber von wegen ... die Leds an PortB
leuchten wieder, es wird aber trotzdem nur der erste Buchstaben
angezeigt.
Ich steh nun völlig auf em Schlauch. Das einzige was ich mir erklären
kann, das die busy funktion irgend einen müll ausgibt :/ kann aber
keinen fehler finden. (Wobei ist das enable in busy nötig? Macht aber in
hinblick auf meinen Fehler keinen unterschied :()
Hat hierzu jemand eine Idee?
Toni
Ja, das Enable ist (immer!) nötig, die Daten die das Display ausgibt
(genauer: der LCD-controller) werden nur auf einer der Enable-Flanken
aktualisiert (welche: steht im Datasheet), d.h. kein enable->keine
Änderung des Busy-flags.
Warum?
weil das ein Bus ist, sobald da mehrere Geräte dranhängen ist es
relativ ungünstig wenn ein gerät einfachso (ungefragt!) die Pegel
ändert
danke für die schnelle Antwort.
Jetzt hab ich wenigstens schonmal das begriffen ;-). Ich setzte nun den
enabled pin in der warteschleife immer kurz auf high und prüfe danach
wieder den busy pin. Source ist im Anhang.
Jetzt müsste doch diese schleife stimmen oder?
Trotzdem wird nur ein A angezeigt. Die Leds an PortB leuchten trotzdem.
Was geht jetzt noch schief?
Toni
Wenn das Busy-Flag gelesen wird, ist nicht nur dieses eine Bit des
Display ein Ausgang, sondern alle. Also nicht nur Bit 3 wird Eingang.
Ebenso ist dieser Lesezyklus ein vollständiger 8bit-Zugriff mit 2
4bit-Hälften, grad so wie die Schreibzyklen.
Den Busy pin abzufragen ist eine schlechte Idee, die selten gemacht
wird. Diesen Pin kann man sich sparen, wenn man fuer die 2-3 Befehle,
bei denen Busy etwas bringt mit einem Timer macht.
Der Busy-Pin macht dann Sinn wenn die Displayausgabe, unabhängig von der
Taktfrequenz des µC, die maximale Geschwindigkeit haben und keine
Zeichen 'verschlucken' soll. Bei verwendung von Pausenzeiten mus u.U.
bei jedem neuen Displaytyp (neue Charge) oder anderem µC die Software
angepasst werden. Bei Hobbybastlern kein Thema, in der Massenproduktion
ein tödlicher Kostenfaktor. Der Busy-Pin (das Busy-Flag) macht also
durchaus Sinn!
andi temp2,balbla ist unsinnig, Du kannst eine 0 solange schieben wie Du
willst, das bleibt eine 0, es ist letztlich also andi temp2,0xF0 und das
löscht Bit 0...3 ist das so beabsichtigt?
in temp2,LCD_PORT
andi temp2,0xF0 ; Datenbist auf 0
or temp2,temp1
out LCD_PORT,temp2 macht das gleiche.
Busy-Flag: daß alle Datenbist auf Eingang müssen, damit es keine
Busprügelei gibt, wurde schon gesagt, daß der Lesezugriff im 4Bit-Mode
aus 2 Zyklen wie der Schreibzugriff besteht, auch.
Außerdem: E muß H sein, um die Daten zu lesen, wenn E L ist, geht das
Display wieder in TriState...
Also
sbi LCD_PORT,PIN_E ; E auf H
nop ; warten E geht erst im nächsten AVR-Clock auf H und das Display
braucht auch einen Moment...
nop
in temp_1,LCD_PORT ; Bit 7...4 einlesen
cbi LCD_PORT,PIN_E ; E hier wieder auf L
sbi LCD_PORT,PIN_E ; E auf H
nop ; warten E geht erst im nächsten AVR-Clock auf H und das Display
braucht auch einen Moment...
nop
in temp_2,LCD_PORT ; Bit 3...0 einlesen
cbi LCD_PORT,PIN_E ; E hier wieder auf L
Liest Du nicht beides ein und benutzt einen Loop, bis Busy ok ist,
bekommst Du in jeder 2. Runde die Bits 3...0 zurück.
Gruß aus Berlin
Michael
Man kann sich taeuschen mit dem Busy pin. Bei hohen Stueckzahlen nimmt
man gerne den kleinst moeglichen Controller, im kleinst moeglichen
Gehaeuse. Dann spart man sich natuerlich den Busy pin. Zumal der Display
als User interface nicht schneller als der Benutzer sein muss. Das
heisst, bevor ich den Display polle mach ich der ADC etwas anderes
prozessrelevantes schneller.
> Dann spart man sich natuerlich den Busy pin.
Das ist bei HD44780-Controllern kein extra Pin, sondern lediglich ein
Bit im ausgelesenen Datenwort und demzufolge ergibt das direkt keine
Einsparung an Pins. Was man sich bei einem Verzicht auf die Busy-Abfrage
allerdings meist sparen kann ist der Read/Write-Pin, da man bei 99%
aller Anwendungen eh nur schreibt und nicht vom Display liest.
Vielen Dank für die Antworten.
Habe nun versucht alle Tips ins Programm umzusetzten. Außer die Sache
hier ;-) andi temp2, (0<<PIN_RS) | (0<<PIN_RW) | (0<<PIN_E) | 0xF0
Klingt logisch dass das Sinnlos ist ;-). Muss ich mir nochmal anschauen.
Aber im wesentlichen machts das was ich will. Zumindest das was ich mir
im Simulator angeschaut habe.
Alle anderen sachen habe ich nun im Anhang drin. Um es gleich vorweg zu
nehmen. Das Display zeigt immer noch nur den ersten Buchstaben ein.
Ich habe nun in lcd_wait_busy die beiden 4 bit nibbles eingelesen und im
register temp2 zusammen gefügt. So müsste das doch passen oder? Dabei
habe ich darauf geachtet, dass enable währen dem lesen auf high ist. (So
im nachhinein auch logisch G).
Auch das DDRD müsste nun stimmen.
Sieht noch jemand ein paar Fehler? g Soviele könnens ja nicht mehr
sein.
Vielen Dank schonmal.
Toni
Ich bin inzwischen wieder von der Busy-Abfrage weg.
Stattdessen verwende ich etwas "Bildschirmspeicher" im SRAM des AVRs, in
den die Routine "LCD_DATA" die Zeichen reinlegt. Das geht sehr schnell,
da keinerlei Warterei nötig ist. Der Bildschirmspeicher ist linear
organisiert, kann also mit "Fließtext" beschrieben werden, der auch
Zeilenumbrüche (13) enthalten kann.
In der Mainloop wird dann (durch einen Timer synchronisiert) alle
1ms..2ms der Job "LCD_UPDATE" aufgerufen, der das nächste Zeichen an das
LCD schickt und dabei die verschachtelten Ausgabepositionen entwirrt.
Somit wird eine Wartezeit von 1..2ms eingehalten, ohne dass das Programm
irgendwo in einer Warteschleife hängt und Rechenzeit verschwendet.
Natürlich bekommt dieser Job keinen exclusiv-Interrupt, die
Synchronisation übernimmt ein sowiso laufender Timer-Interrupt ganz
nebenbei.
Angenehmer Nebeneffekt: Nimmt man das LCD zum Debuggen, so bremst es das
Programm nicht (nennenswert) aus. Man darf sogar schneller in den
Bildschirmspeicher schreiben, als das Update zum LCD erfolgt, ohne dass
es irgendwo einen Stau gibt (das ist bei einem herkömmlichen Ringbuffer
nicht ungestraft möglich).
Die Routinen im Anhang sind für das WINTEK-LCD von Pollin (4 x 27
Zeichen) mit zwei Controllern ausgelegt. Der Einsatz erfolgt derzeit auf
Mega8 und Mega48. Eine Version für LCD 2 x 24 Zeichen läuft auch auf dem
Tiny2313, eine Version für LCD 8 x 24 Zeichen (Controller MS50530) ist
auf einem Mega8535 im Einsatz.
...
Hallo,
ich habe mir Deine Source hier jetzt nicht komplett angeschaut, ganz
allgemein:
Das Display wird nur durch den E-Takt gesteuert, solange E auf L ist,
kann mit den anderen Leitungen passieren, was will. RW, RS und die Daten
können bei Ausgabe also schon vor der L/H-Flanke gesetzt werden und
müssen dann bin nach der H/L-Flanke stabil bleiben. Solange der nächste
Zyklus das gleiche macht, müssen nur die nächsten Daten ran und wieder
E-Clock.
Beim Lesen genauso, nur daß während E auf H ist, gelesen werden muß.
Die jeweiligen Zeiten aus dem Datzenblatt des Display-Controllers
beachten, im Zweifel erstmal ein paar NOP überall dazwischen, zu langsam
stört nicht.
Steht beim Lesen RS richtig? Ansonsten kann RS und RW während der ganzen
Lesegeschichte stehen bleiben, nur E taktet.
Gruß aus Berlin
Michael
Guten Abend zusammen.
Ich habe den Fehler nun glaube ich gefunden. Hab aber keine Ahnung wie
ich ihn lösen soll.
Das Problem tritt in lcd_change_port auf. Wenn dort das erste mal das RW
bit gesetzt wurde, steht es beim zweiten aufruf immer noch da. Folge
ist, dass die nachfolgenden Zeichen nach dme A mit gesetzem RW bit
gesendet wird :(.
Die ganze lcd_change_port gechichte mach ich eignetlich nur aus dem
Grund, dass ich die lcd funktionen flexibel halten kann. Oben wird
definiert welcher PIN welche funktion hat. Nicht verwendete pins des
Port (bei mir pin 7) sollen aber nicht vom den lcd funktionen
beeinflusst werden. Daher habe ich versucht mit
andi temp2, (0<<PIN_RS) | (0<<PIN_RW) | (0<<PIN_E) | 0xF0
Alle pins die verwendet werden auf 0 zu setzten, um nachher mit ori die
neuen werte anzulegen. Nur funktioneirt die Sache leider nicht mit dem
"auf 0 setzten" :(.
Gäbe es eine andere Möglichkeit die Pins zu ändern, sodass der nicht
verwendete PIN, nicht von der neuen ausgabe beeinflusst wrid.
Das einzige was mir eingefallen ist:
cbr LCD_PORT, PIN_RS
cbr LCD_PORT, PIN_RW
...
aber das geht doch bestimmt schöner oder?
Thx hannes für deinen Source :). Die Technik klingt interessant :).
Trotz allem ;-) will ich das ncoh hinbekommen mit dem busy flag ;-9
Schönen Abend noch.
Toni
CBR ist schon richtig, aber CBR erfordert nicht die Bitnummern, sondern
die Bitmaske.
cbr temp2, (1<<PIN_RS) | (1<<PIN_RW) | (1<<PIN_E)
löscht also in temp2 die angegebenen Bits (in einem Rutsch).
Danach können mit ORI oder SBR die entsprechenden Bits gesetzt werden
oder mit OR die Bits eines anderen Registers dazugeholt werden.
Eine ältere Version des LCD-Treibers mit Busywait findest Du hier:
Beitrag "Re: Hilfe! LCD-Ansteuerung"
Da sollte allerdings die Zählschleife für den Notausstieg deaktiviert
werden, die war bei schnellem Takt etwas zu kurz.
Das war auch einer der Gründe, auf virtuelles LCD (Bildschirmspeicher im
AVR-SRAM) umzustellen.
Übrigens: Ich finde es gut, dass Du versuchst, bis ins Detail
vorzudringen. Das machen heutzutage nicht mehr viele...
...
Ich bin ja nur ein schnöder C Programmierer, aber ich würde
das so machen:
lcd_change_port:
in temp2, LCD_PIN
andi temp2, (0<<PIN_RS) | (0<<PIN_RW) | (0<<PIN_E) | 0xF0 ;Alle
verwendeten Bits auf null setzten
or temp2, temp1 ;Daten in temp1 übernehmen
out LCD_PORT, temp2 ;Ausgabe an Port
ret
push und pop temp2 weglassen !
Hmmm.....
> lcd_change_port:> in temp2, LCD_PIN
Warum LCD_PIN? Man will ja nicht von außen anliegende Ereignisse
einlesen, sondern den aktuellen Ausgangszustand des Ports, also:
in temp2,LCD_PORT
> andi temp2, (0<<PIN_RS) | (0<<PIN_RW) | (0<<PIN_E) | 0xF0 ;Alle> verwendeten Bits auf null setzten
Da kann man auch gleich schreiben: andi temp2,0xF0, denn nach links
geschobene Nullen löschen keine Bits. Bits lassen sich in ASM sehr
einfach mit cbr temp2,bitmaske löschen.
> or temp2, temp1 ;Daten in temp1 übernehmen> out LCD_PORT, temp2 ;Ausgabe an Port
...
So :)
jetzt funktionierts endlich :). Die sache bei change_port war
ausschalggebend :). Jetzt muss ich nur mal den ganzen source entrümpeln
:(. Sieht furchtbar aus mitterlweile.
Wo hast du das mit LCD_PIN gefunden? Habe das nur noch beim busy lesen.
Hast du den aller ersten source angeschaut?
Die andi sache ist mir nun klar g Mit cbr funktionierts nun
einwandfrei :).
Eine Frage hätte ich noch:
In lcd Data beim setzten des RC pins hab ich auch müll gemacht:
andi temp1, (1<<PIN_RS) | 0b00001111
Logisch dass RS nie high wird. Bzw nur durch eine Zufall wenn temp1
zufällig an der stelle auch ne 1 hat. Habe das nun durch:
andi temp1, 0b00001111
sbr temp1, (1<<PIN_RS)
ersetzt. Lässt sich das irgendwie zusammenfassen? Eher weniger oder?
Toni
ODER Bit setzen
XOR Bit toggeln
UND Bit löschen, Bit zur Abfrage ausmaskieren
Bit setzen
ori r16, 0b10101111
alle 1 Bits werden in r16 geseztt
Bit löschen
andi r16, 0b10101111
alle 0 bits werden in r16 gelöscht
> UND Bit löschen, Bit zur Abfrage ausmaskieren
Dabei muss man aber die Bits angeben, die NICHT gelöscht werden sollen.
Und damit das einfacher aussieht, unterstützt der AVR-Assembler auch die
Befehle SBR (entspricht ORI) und CBR (entspricht ANDI mit dem
invertierten Wert).
Also
SBR (Set Bits in Register) setzt die angegebenen Bits,
CBR (Clear Bits in Register) löscht die angegebenen Bits.
Die Bits können dabei wie bei LDI oder ORI angegeben werden, also auch
durch nach links geshiftete Einsen.
...
Hallo,
ich habe mal eine wahrscheinlich blöde Frage zu dem Thema busy flag
lesen. Ich versuche das auch gerade, da das Timing als schwierig
gestaltet und ich generell denke das es so besser wäre. Ob sichs lohnt
ist eine ander Frage, ist ja Hobby :-). Also wenn ich das busy flag
lesen muss dann passiert das auf der selben "Leitung" mit der ich auch
die schreibenden Zugriffe mache. Müsste ich da nicht erst den port von
output auf input umstellen?
Noch eine Frage zu Displays, ich habe ein 4x16 und kann mittlerweile
alle Felder ansteuern, aber wenn ich in jedes Feld ein Zeichen setze
fangen manche pixel an zu flackern oder verschwinden ganz. Ist das
Display defekt oder gibt es einen anderen Grund dafür?
Viele Grüsse,
André