Forum: Mikrocontroller und Digitale Elektronik LCD Busy flag bei Initialisierung


von Thomas Frosch (Gast)


Lesenswert?

Wie bei vielen anderen Beiträgen hier will ich auch weg vom delay und 
zwar vollständig. So ist die Frage ob ich bei der Initialisierung des 
LC-Displays auch mit dem Busy Flag statt mit einem Delay arbeiten kann.

Da ja für die Initialisierung Enable auch in bestimmten Zyklen an und 
wieder abgeschalten wird, wie ja auch bei der Abfrage des busy bits.

Es kommen auch in der lcd_enable() Funktion einige Delays vor diese kann 
ich ja sicherlich auch nicht durch das abwarten von busy ersetzen. Was 
gibt es noch für Möglichkeiten?

Hier mal ein Auszug aus den ersten Anweisungen. Ich benutze bisher die 
Datei vom Tutorial.
1
void lcd_init(void)
2
{
3
 LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN);   // Port auf Ausgang schalten
4
 
5
   // muss 3mal hintereinander gesendet werden zur Initialisierung
6
 
7
   _delay_ms(40);
8
   LCD_PORT &= 0xF0;
9
   LCD_PORT |= 0x03;            
10
   LCD_PORT &= ~(1<<LCD_RS);      // RS auf 0
11
   lcd_enable();
12
 
13
   _delay_ms(5);
14
   lcd_enable();
15
 
16
   _delay_ms(1);
17
   lcd_enable();

usw.

von Benedikt K. (benedikt)


Lesenswert?

Thomas Frosch schrieb:
> Wie bei vielen anderen Beiträgen hier will ich auch weg vom delay und
> zwar vollständig. So ist die Frage ob ich bei der Initialisierung des
> LC-Displays auch mit dem Busy Flag statt mit einem Delay arbeiten kann.

Schau mal ins Datenblatt von dem verwendeten LCD Controller (oder in ein 
HD44780 Datenblatt), da sollte das ausdrücklich stehen ab wann man das 
Busy verwenden darf. Das Busy ist nämlich erst nach der grundlegenden 
Init abfragbar.

von Thomas Frosch (Gast)


Angehängte Dateien:

Lesenswert?

Das ist alles was ich dazu habe. Im Web hab ich nicht mehr dazu 
gefunden. Hat jemand was vergleichbares?

http://www.allspectrum.com/store/product_info.php?cPath=54&products_id=967&osCsid=c2b1aff1e026edf5a725cb7053918299&sdesc=20x2+character+LCD+display+with+LED+backlight%2C+Data+Vision+Model+%23+DV-20208

www.aeontekasia.com/pdf/DV-20200.pdf

von Thomas Frosch (Gast)


Lesenswert?

Ok hab was gefunden. Geht erst nach dem dreimaligen senden am Anfang. 
Aber trotzdem. Was gibt es für Möglichkeiten bei einer lcd_enable() 
funktion? Könnte mein Quarzoszilator natürlich kleiner wählen (läuft mit 
16MHz) aber das möchte ich erstmal nicht.

von Benedikt K. (benedikt)


Lesenswert?

Was stört dich an der delay Funktion? Die wird einmalig in der Init beim 
Einschalten durchlaufen und danach nie wieder. Dafür viel Aufwand zu 
treiben, lohnt sich nicht wirklich.

von Thomas Frosch (Gast)


Lesenswert?

Ja kommt mir jetzt auch langsam so vor! Vorallem werde ich um ein delay 
beim enable nicht herumkommen bei dieser Frequenz. Möchte einfach von 
Delay und warte while schleifen weg und mehr hin zum multitaskingfähigen 
Programmablauf.

Hatte in der Vergangenheit einige Probleme mit While schleifen und 
delay. Also wird jetzt alles umgebaut auf Interruptbetrieb mit Puffer 
und State Machine doch leider geht dass bei LCD nicht so einfach.

Ich dachte es hat sich schon mal jemand damit auseinandergesetzt.

Trotzdem vielen Dank falls doch noch Vorschläge kommen wäre ich dankbar!

von Gast (Gast)


Lesenswert?

Einfach aus und abschalten genügt bei HD44780. Man kommt fast immer 
damit weg ohne Initilisierungsdelays. Kondizionen: LCD war langer als 1 
ms abgeschaltet und bei wieder anschalten soll ab Anschaltmoment 
inenerhalb 10ms wieder 4,5 Volt sein. Es reicht dann nur der 
CheckBusyFlag um weiter zu machen.

von Peter D. (peda)


Lesenswert?

Thomas Frosch schrieb:
> Hatte in der Vergangenheit einige Probleme mit While schleifen und
> delay. Also wird jetzt alles umgebaut auf Interruptbetrieb mit Puffer
> und State Machine doch leider geht dass bei LCD nicht so einfach.
>
> Ich dachte es hat sich schon mal jemand damit auseinandergesetzt.

Ja und deshalb gemerkt, daß das Delay überhaupt nicht das Problem ist.
Je nachdem, wie das Delay länger ist, als die tatsächliche Busyzeit, 
wird Dir das Busypolling kaum was bringen.

Du zäumst das Pferd von hinten auf, d.h. gehst völlig falsch ran.


Ein Lösung ist, die LCD-Ausgaben nicht 1000-mal öfter zu machen, als ein 
Mensch sie ablesen kann. Damit verpulverst Du nur CPU-Zeit, auch mit 
Busypolling.
In vielen Anwendungen rufe ich die LCD-Ausgabe nur alle 200..512ms auf, 
dann ist die CPU-Last unerheblich. Und außerdem wirkt das ergonomisch, 
da die Anzeige nicht undeutlich flackert, wenn z.B. ein Wert sich 
schnell ändert.


Die andere Möglichkeit ist, in einen Puffer zu schreiben und ein 
Timerinterrupt gibt jeweils nur ein Byte aus. Die Interruptrate ist 
länger als die Busyzeit und schon wird weder gewartet, noch Busy 
gepollt.
Die Interruptrate sollte aber noch länger sein, damit man wieder auf 
eine ergonomische Darstellrate kommt, z.B. 200ms / Zeichenanzahl.
Hier mal ein Beispielcode:

Beitrag "Formatierte Zahlenausgabe in C"


Ein Textpuffer lohnt sich insbesondere dann, wenn Texte an viele 
verschiedene Positionen zu schreiben sind, dann erspart man sich das 
ständige Kursor umsetzen.
Der Text wird dann einfach nur an die richtige Pufferadresse kopiert.
Es kostet dann auch keine wertvolle CPU-Zeit, wenn häufiger geschrieben 
wird, als notwendig, da es ja nur schnelle SRAM-Zugriffe sind.


Peter

von >>> (Gast)


Lesenswert?

>Ja kommt mir jetzt auch langsam so vor! Vorallem werde ich um ein delay
beim enable nicht herumkommen bei dieser Frequenz. Möchte einfach von
Delay und warte while schleifen weg und mehr hin zum multitaskingfähigen
Programmablauf.

Ja. Waehrend einem Delay wartet man natuerlich nicht. Dafuer hat man 
einen TimerInterrupt. Der setzt eine Boolean, die man min main zyklisch 
abfragt.
Ich hab zB immer einen 10ms Timer Tick.

von Dietmar (Gast)


Lesenswert?

> Möchte einfach von Delay und warte while schleifen weg und mehr hin zum 
multitaskingfähigen Programmablauf.

Das eine hat mit dem anderen nichts zu tun. Multitasking-freundlich 
machen? Dann benutze bei langen Wartezeiten (ms) die 
multitasking-freundliche Delay-Funktion des Kernels (die den Task sofort 
suspendiert) und bei sehr kurzen Wartezeiten lass es einfach beim 
normalen Delay - das preemptive Task-Switching lässt den anderen Tasks 
schon genug Zeit zukommen und unterbricht ggf. die Delays. Bei diesem 
Vorgehen werden einige Wartezyklen zwar viel länger - wenn gerade ein 
Taskwechsel dazwischenkommt - aber das sollte dem Display egal sein. 
Wenn der Display-Task dann noch auf niedriger Pirorität läuft und 
andere, höher priorisierte Tasks jederzeit von ihren zugeordneten 
Ereignissen (Interrupts) aufgeweckt werden können, belegt das Display am 
Ende so gut wie keine CPU-Zeit. Insbesondere dann nicht, wenn es was 
wichtigeres zu tun gibt.

von Michael U. (amiga)


Lesenswert?

Hallo,

Dietmar schrieb:
>> Möchte einfach von Delay und warte while schleifen weg und mehr hin zum
> multitaskingfähigen Programmablauf.
>
> Das eine hat mit dem anderen nichts zu tun. Multitasking-freundlich
> machen? Dann benutze bei langen Wartezeiten (ms) die
> multitasking-freundliche Delay-Funktion des Kernels (die den Task sofort
> suspendiert) und bei sehr kurzen Wartezeiten lass es einfach beim
> normalen Delay - das preemptive Task-Switching lässt den anderen Tasks
> schon genug Zeit zukommen und unterbricht ggf. die Delays.

Welcher Kernel läuft denn bei Dir zur Zeit auf dem Mega16?

Gruß aus Berlin
Michael

von Bernhard M. (boregard)


Lesenswert?

Peter Dannegger schrieb:
> Die andere Möglichkeit ist, in einen Puffer zu schreiben und ein
> Timerinterrupt gibt jeweils nur ein Byte aus. Die Interruptrate ist
> länger als die Busyzeit und schon wird weder gewartet, noch Busy
> gepollt.
> Die Interruptrate sollte aber noch länger sein, damit man wieder auf
> eine ergonomische Darstellrate kommt, z.B. 200ms / Zeichenanzahl.

Ich verwende die Interruptvariante eigentlich auch ganz gern.
Ein Nachteil der Interruptvariante ist, daß man (z.B. für Eingabefelder 
bei Menusteuerung) den Hardwarecursor des Displays nicht benutzen kann 
(da er ja im Interrupt verschoben wird). Allerdings habe ich das so 
gelöst, daß ich die Ausgabe doch etwas öfter mache (1ms Interrupt bei 
einem 2*16 Display, also ca. 30Hz Wiederholfrequenz) und einen 
Softwarecursor erzeuge.
Der Cursor ist der Unterstrich '_', in jedem Interruptdurchlauf wird der 
abwechselnd mit dem richtigen Zeichen dargestellt. Durch die Trägheit 
des Displays (und / oder des Auges) sieht man ihn dann gleichzeitig mit 
dem Zeichen...

Gruß,
Boregard

von Dietmar (Gast)


Lesenswert?

Ich verwende FreeRTOS auf dem pinkompatiblen ATMega644 (ATMega16 würde 
ich nicht kaufen - geringer Preisunterschied, schlechte Daten).

von philipp (Gast)


Lesenswert?

Ja, also ich hab mal so ne verrückte idee die ich nächste woche mal 
ausprobieren will, obwohl sie einen MEGA-AUFWAND ( für meine 
verhältnisse darstellt).

Den init lasse ich so wie er ist, aber beim senden von daten werde ich 
auf den delay verzichten indem ich die daten gleich in einen Ringbuffer 
schreibe. Der buffer hat 12 adressen und 2 pointer. ( in assembler 
selber programmieren ist hier angesagt^^)
wenn ich ein zeichen in den buffer schreibe, dann geht pointer_a auf die 
nächste adresse, in welche das zeichen auch geschrieben wird.
wenn busy auf low geht, dann schreibt der Atmel das zeichen aus 
pointer_b  in das Display und geht mit pointer_b eins hoch, ausser wenn 
pointer_a=pointer_b, dann gibt es keine neuen zeichen zu schreiben.

der buffer muss ausserdem Ringförmig sein, damit die pointer nicht immer 
weiter hoch gehtn, nach der 12ten adresse gehts also wieder in die erste 
zurück.

da ich in assembler nicht ganz so fit bin, werd ich mindestens ne woche 
brauchen, aber danach sag ich bescheid obs geklappt hat^^

von Peter D. (peda)


Lesenswert?

philipp schrieb:
> Den init lasse ich so wie er ist, aber beim senden von daten werde ich
> auf den delay verzichten indem ich die daten gleich in einen Ringbuffer
> schreibe. Der buffer hat 12 adressen und 2 pointer.

Was soll das bringen?
Wo wird dabei das Delay eingespart?
Und was passiert beim Überlauf?

Was hast Du gegen die bewährte Lösung mit Textspeicher und 
Timerinterrupt?


> ( in assembler
> selber programmieren ist hier angesagt^^)

Was hast Du gegen C?


> da ich in assembler nicht ganz so fit bin, werd ich mindestens ne woche
> brauchen, aber danach sag ich bescheid obs geklappt hat^^

Dann solltest Du vielleicht verständlicher beschreiben, was Du vorhast, 
ehe Du ne Woche umsonst probierst.


Peter

von ?? (Gast)


Lesenswert?

Wenn eine Woche Stochern guenstiger als Nachdenken ist...

von philipp (Gast)


Lesenswert?

@ peter
der vorteil ist: wenn ich was schreiben will schreibe ich einfach sofort 
in den puffer, ohne darauf zu warten bis das flag weg ist.
timer hat man nicht so unendlich viele, da ist es doch gut wenn man ohne 
auskommt oder?

ausserdem finde ich eure kommentare ziemlich mies, ich hab eine idee, 
die meiner meinung nach ganz gut ist und will sie mal ausprobieren, ( ja 
ausprobieren, das impoliziert die möglichkeit dass es auch nicht klappen 
könnte).

ich muss zugeben, dass ich das hier nur sehr ungenau beschrieben habe, 
aber der springende punkt ist:
ICH HABE DICH GARNICHT NACH DEINER MEINUNG GEFRAGT !

mir ist nur eingefallen wie man das machen könnte, und ich hab lust es 
auszuprobieren, und wenns klappt dann wollte ich es sharen, ich weiss 
garnicht wieso man mich hier dumm anmacht

@ ??
genau das gleiche + ich habe das schon gut durchdacht, in c++ wärs auch 
schnell geschrieben, in asm bin ich halt nicht so fit, ausserdem lerne 
ich dabei und würde das nicht als "rumstochern" bezeichnen.
ein schöner code erfordert auch eine ganze menge denkarbeit, stupides 
copy-paste spripten eher weniger

von philipp (Gast)


Lesenswert?

ok, das ist jetzt ein bischen agressiv rübergekommen, so war das icht 
gemeint, danke, dass du dir gedanken drüber gemacht hast peter, aber 
beim kommentar von ?? istz mir ein bischen der kragen geplatzt

von Peter D. (peda)


Lesenswert?

philipp schrieb:
> @ peter
> der vorteil ist: wenn ich was schreiben will schreibe ich einfach sofort
> in den puffer, ohne darauf zu warten bis das flag weg ist.

Die Frage war aber, was passiert mit dem Puffer?


> timer hat man nicht so unendlich viele, da ist es doch gut wenn man ohne
> auskommt oder?

Wenn man nicht welche für PWM braucht oder zum Zählen einer externen 
Frequenz, reicht ein einziger Timer völlig aus, um alle Zeit-Tasks 
auszuführen.


> ich muss zugeben, dass ich das hier nur sehr ungenau beschrieben habe,
> aber der springende punkt ist:
> ICH HABE DICH GARNICHT NACH DEINER MEINUNG GEFRAGT !

Es ist mir doch vollkommen schnurz, wie Du Deine Zeit verbringst.
Ich hatte nur den Eindruck gewonnen, Du bist auf dem totalen Holzweg.


> ich weiss
> garnicht wieso man mich hier dumm anmacht

Dumm anmachen, da bist Du definitiv auf dem Holzweg.
Es war nur ein Tip und nicht mehr.
Geh mal davon aus, daß ich schon einige Tage länger LCDs programmiere, 
als Du.


Peter

von philipp (Gast)


Lesenswert?

ja wie gesagt, ich hab deinen kommentar in den falschen hals gekriegt, 
sry.
der puffer wird halt abgearbeitet wenn das busy-flag auf low geht und 
klar, das darf dann halt nicht überlaufen.
wenn man ganz sicher sein will macht man den puffer halt so gross wie 
das lcd stellen hat, dann machts nix wenn der eine pointer den anderen 
mal überholt.

aber wie kommst du nur mit einem timer aus? ich kann doch mit einem 
timer höchstens dem compare match interrupt und den overflow interrupt 
auslösen, mehr als zwei sachen kann ich also nicht machen, es sei denn 
ich frage immer was im timer gerade drin steht.

wie machst du das also?

von philipp (Gast)


Lesenswert?

oder setzt du bei jedem compare match das register mit dem verglichen 
werden soll neu?

von Peter D. (peda)


Lesenswert?

philipp schrieb:
> aber wie kommst du nur mit einem timer aus? ich kann doch mit einem
> timer höchstens dem compare match interrupt und den overflow interrupt
> auslösen, mehr als zwei sachen kann ich also nicht machen, es sei denn
> ich frage immer was im timer gerade drin steht.

Du setzt den Interrupt auf den größten gemeinsamen Teiler.

Du hast z.B. eine Task bei 3ms und eine andere bei 7ms.
Dann erfolgt der Timerinterupt alle 1ms und eine Variable zählt bis 3, 
um das 3ms Ding zu machen und eine weitere bis 7.


Peter

von ?? (Gast)


Lesenswert?

Man hat einen TimerInterrupt, der macht im Wesentlichen einen Reload 
des Timer registers. Und setzt eine Boolean : TimerCame. Im Main kann 
man auf dieses TimerCame einen Haufen von Dingen anhaengen. Ich lese 
Tasten, re-starte den ADC falls noetig, schiebe ein Byte zum LCD, Mache 
einen Rechenschritt fuer eine Regelung, usw.

von philipp (Gast)


Lesenswert?

ok, das ist natürlich eine gute lösung, darauf hätte man auch kömmen 
müssen^^

den puffer programmiere ich aber trotzdem mal, ich brauche ihn ja auch 
mit timer wenn ich sofort was schreiben will ohne auf das flag zu warten

danke für die tipps

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.