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
voidlcd_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
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.
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.
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.
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!
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.
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
>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.
> 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.
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
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
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^^
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
@ 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
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
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
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?
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
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.
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