Guten Tag!
Ich habe eine Frage:
Text-LCD ist ziemlich langsam: man muß immer ca. 40 us nach jedem Befehl
warten. Bei 16 MHz AVR bedeutet das 640 CPU-Takt. Diese Zeit könnte ich
viel besser für Datenanalyse benutzen, stattdessen wird diese Zeit
einfach vergeudet. USART kann zwar noch in dieser Zeit dank Interrupts
etwas machen, aber sowieso, es bleibt CPU-Zeit, die man besser nützen
könnte... Das alles hat zu Folge, daß ich für mit LCD gezeigte
MIDI-Daten ziemlich große 256 bytes-Puffer benutzen muß, sonst kommt es
zu Datenverlust, wenn ein MIDI-Regler gedreht wird.
Wie wäre es möglich, statt nutzlos auf Bereitschaft von LCD zu warten,
etwas nützliches zu machen?
Wenn Timer-Interrupts, dann wie richte ich Prozeß ein? Irgendwie komme
ich mit Gedanken nicht klar...
Könnte mir jemand vielleicht helfen?
Ich hatte schon Gedanken, einfach ein ATMEGA88 extra für LCD benutzen,
dafür zweite USART von ATMEGA1284P für Datenaustausch benutzen:
Hauptkontroller kann dann schnell Daten an ATMEGA88 schicken und weiter
machen, während ATMEGA88 alle Befehle in Puffer legt und ohne Eile mit
LCD kommuniziert.
Aber vielleicht gibt es reine Programmlösung? Das wäre schon allein als
Übung für mich interessant...
Vielen Dank im voraus!
bei einfachen Text LCD bietet es sich an den Text 1:1 im RAM abzulegen
und im Interrupt immer nur ein Zeichen zu schreiben. Zeilen *Spalten
*4-5 Aktualisierungen pro Sekunde. zB.
Hmm ...
Timer -> Interrupt -> Funktionsaufruf zum Befehl an LCD senden ?
Timer -> Variable auf 1 -> im Ablauf senden (wenn diese Funktion
regelmäßig im Programmablauf drin ist) Variable wieder auf 0
eine Mischung daraus ?
Bisher habe ich so gemacht: ein Text-Puffer 4x20=80 bytes. Alle
Manipulationen in Puffer, danach nach LCD alles senden. Aber 4x20=80
Zeichen x ca. 45 us = 3,6 ms, für jeden Austausch mit LCD.
Wenn ich MIDI-Daten anzeigen will, dann bekomme ich ca. jede 350 us ein
MIDI-byte, schiebe Daten in RAM-Puffer (ca. 50 us) um 3 Zeichen nach
links (zwei Ziffer + Leerzeichen) und sende Daten nach LCD (noch 3,6
ms). Das ist zu viel...
Stephan schrieb:> immer nur ein Zeichen zu schreiben. Zeilen *Spalten> *4-5 Aktualisierungen pro Sekunde. zB.
Seh ich auch so.
Das BusyBit abfragen dauert genauso lang. Das zu nutzen für 'nö nächstes
mal bitte' ist eigentlich völlig unnötig, Taktverschwendung.
Stells dir als Warteschlange vor, die alle >~100msn ein Zeichen sendet,
bis die Schlange leer ist.
Teo D. schrieb:> Das BusyBit abfragen dauert genauso lang. Das zu nutzen für 'nö nächstes> mal bitte' ist eigentlich völlig unnötig, Taktverschwendung.
Ja, ich habe das nun auch verstanden: BUSY bringt in Realität nichts.
Aber wenn ich das nicht erprobt hätte, so hätte ich wohl immer gedacht,
daß es mit BUSY schneller geht :)
Idee, volle LCD-Erneuerung nur ein paar Mal pro Sekunde zu machen - das
scheint mir eine Lösung zu sein! Danke!
Aber diese 40-45 us - kann man sie doch irgendwie besser auszutzen, als
einfach warten?
Am besten verwendest Du einen kleinen Schedulder, z.B. FreeRTOS, dann
machst Du für das LCD einen eigenen Task, der dann beliebig langsam die
Befehle ausführen kann und den Rest Deiner Anwendung nicht tangiert.
Maxim B. schrieb:> Ja, das wäre auch möglich.> Aber ich wollte alle gezeigten Ziffer endlos nach links schieben...> Nicht möglich?
Also einen DisplayShift. Den kannst du eh nur max. alle ~150ms senden
(oben solltens eigentlich >~ 100µs sein), sonnst brauchst du garnicht
Shiften. LCDs sind nicht wirklich gut für Lauftext geeignet,
lesen/erkennen soll man ja auch noch was(?). Nur schnell was
reinschiften als Animation, geht auch nicht viel schneller, da sonnst
Augenkrebs droht. ;)
Maxim B. schrieb:> Ja, ich habe das nun auch verstanden: BUSY bringt in Realität nichts.>> Aber wenn ich das nicht erprobt hätte, so hätte ich wohl immer gedacht,> daß es mit BUSY schneller geht :)
Das auszulesen ordne ich auch nur als "Ja ich kanns :-)" ein.
Oder was für faule Programmierer, die sich auch nich um die sehr
langsamen Befehle kümmern o. auch nur dran denken wollen. :)
Maxim B. schrieb:> Für Schedulder muß ich noch wachsen. Das ist noch zu kompliziert für> mich.
Echt, alle xx-ms ein Flag zu setzen, das in der Hautschleife abfragen
und löschen....
Lass dich durch so schwurblige begriffen nur nich verwirren. ;)
> Bisher habe ich so gemacht: ein Text-Puffer 4x20=80 bytes. Alle> Manipulationen in Puffer, danach nach LCD alles senden.
Teil das ganze in acht halbe LCDs auf. Dann brauchst du nur ein Byte
fuer die DirtyBits. Alle 50 bis 200ms (Geschmacksache) pruefst du im IRQ
ob etwas Dirty ist und uebertraegst dann einen Block.
Sollte es nix zutun geben so kannst du einen Zaehler decrementieren. Ist
der auf null initialisiert du dein Display und setzt alles auf Dirty. So
kannst du dein LCD im laufenden Betrieb an und abstecken und es faengt
sich auch falls es mal (z.B wegen EMV) abstuerzen sollte.
Das ganze kann man natuerlich noch lustig erweitern. So koennte es zwei
Textbuffer geben. Einen fuer normale Anwendungsinfos und einen Zweiten
fuer Debuginformationen. Dann kannst du einfach dazwischen hin und
herschalten.
Olaf
Ich habe jede 1ms einen IRQ. Die meisten LCD Befehle sind in 1ms durch.
Ich habe einen Ringbuffer wie bei einer RS232 implementiert, der jede
1ms ein Zeichen hinaus schickt.Zur Sicherheit frage ich in der IRQ
Routine den Status ab und lasse halt ggf den einen IRQ aus. Im nächsten
ist es dann draußen.
Im nächsten Schritt habe ich dann für jede Zeile Platz im RAM genommen
und diese nur dann komplett hinaus gesandt, wenn sich was geändert hat.
Mario
Ich hab schon ne Weile nix mehr mit AVRs gemacht, aber wie wäre es wenn
du das BUSY dazu nutzt, um dir einen externen Interrupt zu generieren?
Sowas wie EXTx oder PCINTx oder so...den Pin von A nach E
umzukonfigurieren sollte nicht soviel Zeit beanspruchen.
Christoph K. schrieb:> Die Busyflag-Abfrage beim HD44780 ist relativ kurz, ich habe das in> Assembler, allerdings in einer Schleife, so gelöst:
Dauert nur in etwas solange wie ein ein Schreibvorgang.
Wenn eh gesichert ist, das nur geschrieben wird wenn das LCD nichts zu
tun hat, warum dann noch Zeit mit der Busy-Abfrage verschwenden.
PS: Hab mir dein Listing nicht angesehen, aber was passiert wenn keine
Freigabe vom Busy_Flag kommt? ;)
Olaf schrieb:> Sollte es nix zutun geben so kannst du einen Zaehler decrementieren. Ist> der auf null initialisiert du dein Display und setzt alles auf Dirty.
Kommt halt immer drauf an, wo man da seine Prämissen setzen möchte.
Bzw. wie weit man den Aufwand treiben möchte, dieses /Gebäude
erdbebensicher/ zu machen.
Ich bin auch immer geneigt das wie Olaf zu lösen, dann aber meist doch
zu faul. |-}
Vielen Dank an alle!!!
Nun habe ich neuen Ideen!!!
Im allgemeinen klar, was ich machen muß.
>> Teo Derix: Echt, alle xx-ms ein Flag zu setzen, das in der Hautschleife>> abfragen>>und löschen....
das habe ich immer so und nicht anders gemacht, um Interrupts so kurz
wie nur möglich zu halten.
Normalerweise benutze ich dafür GPIOR0, da hier Compiler mit sbi und cbi
alles kürzer macht. In Interrupt, auch ISR_NAKED, wird lediglich Bit in
GPIOR0 gesetzt, alles anderes in Hauptschleife.
Mit Busy ist so: ich habe zwei Varianten für bedingte Compielen gemacht,
mit Busy und mit Delay.
}while(data);// warten bis BUSY=1, weiter wenn BUSY=0
30
31
// RW in Schreibemodus (LCD als Eingang)
32
LCD_PORT_R_W&=~(1<<LCD_R_W);
33
34
// DB4-DB7 als Ausgaenge schalten
35
LCD_DDR_DB7|=(1<<LCD_DB7);
36
LCD_DDR_DB6|=(1<<LCD_DB6);
37
LCD_DDR_DB5|=(1<<LCD_DB5);
38
LCD_DDR_DB4|=(1<<LCD_DB4);
39
}
40
#endif /* LCD_BUSY */
und weiter immer so ähnlich:
1
// Sendet ein Datenbyte an das LCD
2
voidlcd_data(uint8_tdata){
3
4
#ifdef LCD_BUSY
5
lcd_busy_null();
6
#endif
7
8
LCD_PORT_RS|=(1<<LCD_RS);// RS auf 1 setzen
9
lcd_out(data);// zuerst die oberen,
10
lcd_out(data<<4);// dann die unteren 4 Bit senden
11
12
#ifdef LCD_DELAY
13
_delay_us(LCD_WRITEDATA_US);
14
#endif
15
}
In Theorie kann das etwas Zeit sparen, da Busy erst vor dem neuen
Schreiben geprüft wird. D.h. MK schreibt in LCD, dann macht etwas
anderes, bis noch mal ein Schreibvorgang in Frage kommt und erst dann
wird es auf Busy gewartet.
In Praxis habe ich aber keine Unterschiede bemerkt. Aber wenn ich
versuche etwas mit Simulator zu prüfen, klappt es mit Busy-Variante
nicht. Ich habe deshalb alles zuerst mit Delay geprüft und dann auf Busy
umgeschaltet.
Solange meine Platte auch fürs Lesen aus LCD gerichtet ist, kann ich
wohl auch Busy weiter nutzen. Aber ich denke, wenn ich etwas neues
mache, dann bleibt nur Delay. Einfach kein Sinn.
Viele Grüße,
Teo D. schrieb:> Wenn eh gesichert ist, das nur geschrieben wird wenn das LCD nichts zu> tun hat, warum dann noch Zeit mit der Busy-Abfrage verschwenden.
Man stelle sich vor, man tauscht mal gegen ein langsameres LCD aus. Oder
es braucht halt wegen irgendeinem nicht einkalkuliertem Faktor grade mal
etwas länger.
Darum ist es immer sinnvoll auf das Busy Flag zu warten bevor man was
schreibt.
Also, ob ich mir das richtig einbilde:
In Timer-Interrupt so einmal im 5 ms wird Flag "LCD_Daten_Bereit"
abgefragt und wenn gesetzt, wird Flag "LCD_Daten_Schreiben" gesetzt.
In der Hauptschleife wird ab und zu (wenn alles anderes schon erledigt)
Flag "LCD_Daten_Schreiben" abgefragt und wenn gesetzt, wird
Schreibvorgang für 1 Byte durchgeführt, LCD-Schreibpuffer aktualisiert
und wenn noch nicht leer, wird Flag "LCD_Daten_Bereit" wieder gesetzt -
bis durch Timer-Interrupt wieder Zeit kommt, wo Schreiben erlaubt wird.
Etwa so?
So kann LCD-Schreiben nicht öfter als ein Zeichen pro 5 ms gemacht
werden, für 80 Zeichen 400 ms gebraucht, was noch als akzeptabel für
langsame LCD erscheint...
Dann brauche ich evtl. weder Busy noch Delay...
Statt 5 ms kann das auch 1 ms oder gar 100 us heißen, Hauptsache
garantiert mehr als notwendige Delay.
Richtig?
Dann brauche ich zwei LCD-Puffer: in einem werden alle Veränderungen
gemacht. Zweite macht Anpassung zwischen Schreib-Wünschen und
timer-gesteuerten Schreibvorgängen.
Cyblord -. schrieb:> Darum ist es immer sinnvoll auf das Busy Flag zu warten bevor man was> schreibt.
Bei meinen Projekten ist der Konstruktor der alleinige Bediener, oder
nur einen Anruf weit entfernt. :)
Für den Außeneinsatz, auf zB. einem 450Kg schweren Bodenverdichter, hab
ich auch noch nichts gemacht.
Hab noch nicht viel mit LCDs in C gemacht, ist sozusagen noch /im
Aufbau/. Da kommt halt nur das gerade benötigte dazu. Und ja, seit die
Busy-Abfrage drin ist, nutz ich diese, aus faulheit.
PS: Manchmal nervt das programmieren so, das ich mich manchmal mit solch
halb sinnvollen Postings ablenke.... :)
Maxim B. schrieb:> Also, ob ich mir das richtig einbilde:> ...> Etwa so?> ...> Richtig?>
So macht man das.
> Dann brauche ich zwei LCD-Puffer: in einem werden alle Veränderungen> gemacht. Zweite macht Anpassung zwischen Schreib-Wünschen und> timer-gesteuerten Schreibvorgängen.
Zwei Puffer brauchst du dann, wenn du nur die Änderungen übertragen
willst. Ist eleganter, zeitsparender und bei höheren Refreshraten
natürlich auch flimmerfreier.
Wenn man alle Pins des LCD an einem Port hat, muss man nicht die Bits
einzeln setzen, wie oben:
" // DB4-DB7 als Ausgaenge schalten
LCD_DDR_DB7 |= (1<<LCD_DB7);
LCD_DDR_DB6 |= (1<<LCD_DB6);
LCD_DDR_DB5 |= (1<<LCD_DB5);
LCD_DDR_DB4 |= (1<<LCD_DB4);"
usw.
Das LCD hat ja üblicherweise 7 Bit, dann muss man nur noch das achte
Portbit berücksichtigen, dass das nicht ungewollt mitklappert.
Auch das ist beim LCD-Keypad-shield ziermlich ungünstig verteilt.
http://www.shieldlist.org/dfrobot/lcd
Das hier meine ich, wird unter vielen Herstellernamen angeboten.
Ich habe mir eine universelle Variante gemacht, wo ich alle Pins so
legen kann, wie das für die Platte bequemer ist.
Da LCD sowieso sehr langsam ist, macht das kaum noch Nachteile für
Geschwindigkeit. Dafür viel mehr Flexibilität, als alles auf einem Port
zu halten.
MK hat viele Funktionen, die mit bestimmten Pins fest verbunden sind. So
kann man für LCD einfach nehmen, was übrig bleibt.
>> Auch das ist beim LCD-Keypad-shield
Ich mache gewöhnlich eine eigene Platte. Das geht recht schnell mit
DipTrace und LED-UV (selbst gemacht).
das auf dem Bild habe ich jetzt.
Maxim B. schrieb:> wahrscheinlich hat es Sinn, Warteschlange-lcdpuffer als int zu machen?> Dann können in diesem Puffer Daten sowohl auch Befehle gespeichert> werden.
Eine Warteschlange hat aber den Nachteil, daß Ausgaben auf die gleiche
Zeichenpositionen veralten können und das sie überlaufen kann.
Besser ist es daher, einen LCD-Puffer für alle Zeichenstellen anzulegen,
in den man direkt reinschreibt. Und der Timerinterrupt (1ms) kümmert
sich im Hintergrund um das Schreiben ins LCD. Alle 80ms (4*20 LCD) ist
dann das gesamte LCD refresht. So habe ich das im obigen Beispiel
gemacht.
Peter D. schrieb:> Maxim B. schrieb:>> wahrscheinlich hat es Sinn, Warteschlange-lcdpuffer als int zu machen?>> Dann können in diesem Puffer Daten sowohl auch Befehle gespeichert>> werden.>> Eine Warteschlange hat aber den Nachteil, daß Ausgaben auf die gleiche> Zeichenpositionen veralten können und das sie überlaufen kann.
Hat Vor- und Nachteile, ich habe das LCD beim letzen Projekt an einem
Schieberegister (Hallo Peda) angeschlossen, und dieses via Hardware SPI
angesteuert.
Da fand ich eine Warteschlange schon sinnvoll, da ich auch viel am CGRAM
arbeite. Ich hatte jetzt keine Speichernot auf dem AVR, daher hab ich
auch gleich noch pro Befehl die Ausführungszeit mit in die Warteschlange
gepackt.
Die Lösung ist aus Sicht des Hauptprogramms "shoot and forget".
Wird vom SPI Transfer Complete Interrupt und zusätzlich mit einer
Callback-Routine von meiner Hauptuhr abgearbeitet.
Die SPI-Frequenz liegt auch recht niedrig, das LCD ist ja ziemlich
langsam, da kann man schon noch was im Hauptprogramm abarbeiten.
Gruß Dominik
Teo D. schrieb:> PS: Hab mir dein Listing nicht angesehen, aber was passiert wenn keine> Freigabe vom Busy_Flag kommt? ;)
Den Watchdog aktivieren.
OK,
zum Thema.
Habe hier den DCF77-Decoder, der eine "starre" Timerinterruptportabfage
braucht, direkte LCD-Ausgaberoutine würde da erheblich stören.
Insgesamt brauche ich mindestens 3 SRAM Puffer.
(Für die Plausibilitätsprüfung n+1 evtl. noch mehr.)
Die LCD-Ausgabe erfolgt byteweise, charakterweise aus der Pufferabfrage
in der Main-Routine. Dabei werden noch Umwandlungsroutinen BCD nach
binär
binär nach ASCII etc. durchlaufen.
ciao
gustav
Peter D. schrieb:> Besser ist es daher, einen LCD-Puffer für alle Zeichenstellen anzulegen,> in den man direkt reinschreibt.
So habe ich von Anfang an gemacht. 80 bytes Puffer, dort alles nach
Bedarf geändert und mit einer Funktion immer ganze LCD erneuert. Aber zu
oft erneuert, nach jeder Veränderung in Puffer, das war mein Fehler.
> Und der Timerinterrupt (1ms) kümmert> sich im Hintergrund um das Schreiben ins LCD. Alle 80ms (4*20 LCD) ist> dann das gesamte LCD refresht. So habe ich das im obigen Beispiel> gemacht.
Ich werde das auch ausprobieren, Danke!
Übrigens, mit Tasten und Drehgeber benutze ich Ihre Variante, noch mal
Danke!
Mit Drehgeber ist zwar nicht ganz so gut, obwohl zweite Variante, mit
weniger Impulse - trotzdem bleibt jede zweite increment/dekrement
zwischen Rasterpunkt. Chinesische Drehgeber, ich habe sie leider 20
Stück gekauft...
Aber das ist ein anderes Thema, hier nur nebenbei...
Also, Hauptprogramm verändert nach Bedarf die Daten in Puffer.
Und in Interrupts wird permanent Zeichen um Zeichen nach LCD geschickt.
Da für Befehl CURSOR_HOME bis 2 ms gebraucht wird, muß ich bei
CURSOR_HOME 1 Takt weglassen oder als Takt 2 ms nehmen, richtig?
Maxim B. schrieb:> Da für Befehl CURSOR_HOME bis 2 ms gebraucht wird, muß ich bei> CURSOR_HOME 1 Takt weglassen oder als Takt 2 ms nehmen, richtig?
Den Befehl braucht man nicht.
Man nimmt "Set DDRAM address".
Wenn man ganz faul ist, eine schnelle Ausgabe braucht und sich etwas
Besonderes leisten möchte, kann man auch eine OLED-Anzeige verwenden.
Der µC muß nicht warten, sondern die Anzeige wartet auf den µC ;-)
m.n. schrieb:> Wenn man ganz faul ist, eine schnelle Ausgabe braucht und sich etwas> Besonderes leisten möchte,
Mein nächstes Spielzeug wird bestimmt ein Funk-LED-Liednummerbrett für
die Kirche (ich bin zu faul geworden, die Liednummer einzustecken). Ich
kaufe schon langsam chinesische LED-Module dafür (langsam, um
22-Euro-Grenze nicht zu überschreiten :) ). Dort kann man wohl sehr
schnell alles schreiben... Allerdings kommt AVR dann auch auf die
Grenze, wenn auch aus einem anderem Grund...
Aber vielleicht mache ich das mit MAX7219, ich werde noch überlegen...
Die Proben habe ich schon gemacht, alles ist realisierbar...
Aber zuerst MIDI.
Karl B. schrieb:> Habe hier den DCF77-Decoder, der eine "starre" Timerinterruptportabfage> braucht, direkte LCD-Ausgaberoutine würde da erheblich stören.> Insgesamt brauche ich mindestens 3 SRAM Puffer.> (Für die Plausibilitätsprüfung n+1 evtl. noch mehr.)
Erschließt sich mir nicht ganz, scannst Du den Pin?
Also mein DCF77-Decoder hängt über einen Tiefpass am µC und steuert dann
einen externen Interrupt...
Am Anfang der ISR frage ich dann den timeStamp von der internen Clock ab
und werte anhand der Differenz zur letzen fallenden Flanke aus ob es
sich um ein gültiges Bit handelt... dann nachdem alle bits reingeshiftet
wurden und zum Minutenbeginn die ~2 Sec Pause kommt wird der Code
zwischengespeichert für den Parity check...
Gruß Dominik
Dominik schrieb:> Erschließt sich mir nicht ganz, scannst Du den Pin?
Hi,
ist zwar OT, aber möchte interessehalber trotzdem antworten.
Habe zwei Varianten ausprobiert. Die mit den Intererupts auf
Flankentriggerung hatte den Nachteil, das sie in gestörter Umgebung
dauernd INT0 Interrupts feuerte, die das Program schließlich abstürzen
ließen. Auch bei nachfolgend wieder ungestörtem Empfang synchronisierte
sich das Programm nicht mehr.
Da es bei meiner Anwendung nicht auf die genaue Sekundenflankenerkennung
ankommt, sondern um eine halbwegs 50-Hz-netzfrequenzunabhängige (;-) Uhr
handelt, polle ich mit 1/100 sec Interrupt den Empfangs-Port.
Dieser Timer-Interrupt sollte nicht gestört werden. Wenn eine
LCD-Ausgabe in diese ISR reinkommt, stürzt das Programm ab. Deswegen
Ausgabe-Routine in der Main-Loop und über diverse SRAM-Puffer.
Ansonsten zeigt das Programm bei Empfangsstörungen (Kaffemühle neben
die Antenne gestellt z.B.) zwar Lottozahlen, bleibt aber nicht hängen
und synchronisiert bei einwandfreiem Empfang wieder.
Experimentiere gerade mit der "Gangreserve". Die funktioniert zwar bei
völlig fehlendem Signal, werde sie aber noch so umstricken müssen, dass
sie den Decoder abklemmt, wenn Signal zugemüllt.
Die handelsüblichen Funkuhren synchronisieren ja zu festgelegten
angenommenen störungsarmen Zeiten mitten in der Nacht. Meine Uhr
ständig.
soweit OT
ciao
gustav
Stimmt ist OT, aber irgendwie gehört es ja zum dem Interrupt Thema:
Ich habe mit dem Tiefpass ganz gute Erfahrungen gemacht (Kommt natürlich
auch auf das jeweilige DCF-Modul an), breche die ISR aber auch sofort
ab, wenn keine Mindestzeit vorliegt, zusätzlich könnte man den Interrupt
natürlich auch je für z.B. 0.9 Sekunden sperren.
Karl B. schrieb:> Ansonsten zeigt das Programm bei Empfangsstörungen (Kaffemühle neben> die Antenne gestellt z.B.) zwar Lottozahlen, bleibt aber nicht hängen> und synchronisiert bei einwandfreiem Empfang wieder.> Experimentiere gerade mit der "Gangreserve". Die funktioniert zwar bei> völlig fehlendem Signal, werde sie aber noch so umstricken müssen, dass> sie den Decoder abklemmt, wenn Signal zugemüllt.
Ich bin schlussendlich dazu übergegangen nur noch ein komplettes Signal
zuzulassen und auf Gültigkeit zu prüfen, ist zwar auch keine Garantie,
aber bisher hatte ich keine Lottozahlen mehr. Die Prüfung ob die
empfangende Zeit nach der vorherigen liegt wäre noch eine Verbesserung.
Hier eine nicht besonders schöner aber funktionaler Codeauschnitt zur
DCF-Paritätsprüfung:
if(*day==32)
ich würde auf größer 31 abfragen.
if(*month==12)
hier muss es gleich 13 oder größer 12 heißen.
Und die Schaltjahrabfrage ist auch nicht ganz korrekt (2100 ist kein
SJ).:)
eProfi schrieb:> if(*day==32)> ich würde auf größer 31 abfragen.> if(*month==12)> hier muss es gleich 13 oder größer 12 heißen.> Und die Schaltjahrabfrage ist auch nicht ganz korrekt (2100 ist kein> SJ).:)
In der parity abfrage wird größer geprüft... der andere code block ist
ja ne schleife da ist gleich schon ok und die ==12 ist der jahreswechsel
bei tag 32. was nicht heißen soll, das der code absolut fehlerfrei ist,
der ist q+d ;-)
Das mit dem Schaltjahr könnte allerdings ein Problem werden...
Bei guter Temperierung ist das Programm dann vielleicht wirklich noch im
flash...
und vllt. braucht man dann sogar die Langzeitgangreserve weil das
DCF-Signal abgeschaltet wurde...
gruß Dominik
// Voreinstellungen, um erste Befehl war: (LCD_SET_DDADR + LCD_DDADR_LINE1)
153
lcd_erneuern--;
154
155
// Puffer incrementieren
156
lcd_adr_zeichen++;
157
if(lcd_adr_zeichen==LCD_ZEICHEN_PRO_ZEILE){
158
lcd_adr_zeichen=(-1);
159
LCD_PORT_RS&=~(1<<LCD_RS);// Befehl
160
switch(++lcd_adr_zeile){
161
case0:
162
data=(LCD_SET_DDADR+LCD_DDADR_LINE1);// LCD auf Line 1
163
break;
164
case1:
165
data=(LCD_SET_DDADR+LCD_DDADR_LINE2);// LCD auf Line 2
166
break;
167
case2:
168
data=(LCD_SET_DDADR+LCD_DDADR_LINE3);// LCD auf Line 3
169
break;
170
case3:
171
data=(LCD_SET_DDADR+LCD_DDADR_LINE4);// LCD auf Line 4
172
lcd_adr_zeile=(-1);
173
break;
174
default:
175
data=0;
176
break;
177
}
178
}else{
179
LCD_PORT_RS|=(1<<LCD_RS);// Daten
180
data=lcd_puff[lcd_adr_zeile][lcd_adr_zeichen];
181
}
182
183
// Auf LCD schreiben
184
lcd_out(data);
185
lcd_out(data<<4);
186
}
187
}
Mir scheint es bequemer zu sein, Puffer als 4x20 zu machen, da so
einfacher geht, Befehle in die gesamte Schleife einzubinden.
Um nicht zwecklos immer auf LCD zu schreiben, ist ein Zähler deklariert:
lcd_erneuern. Nach jeder Aktualisierung von Puffer wird lcd_erneuern mit
einer Zahl aktiviert, Zeichenzahl + Befehle. So wird sich diese
Automatik nach 84 Schreibvorgänge beruhigen, falls im Puffer keine
Änderungen vorkommen.
Geschickter wäre es wohl, Puffer als Ring einzulegen, so könnte CPU viel
Zeit beim Schieben sparen...
Guten Tag,
ich habe eine weitere Frage:
das ist alles schön und gut: ich schreibe in RAM-Puffer, und alles kommt
auf LCD.
Aber wie könnte ich LCD über Puffer noch vollständiger simulieren?
Ich möchte gerne Cursor sichtbar machen, wo Eingabe gerade stattfindet -
aber Eingabe sollte über RAM-Puffer erfolgen.
Ist das machbar?
Vielen Dank,
Danke! Ich werde das ausprobieren!
Wahrscheinlich brauche ich dann zwei char: Zeiger für blinkende Stelle
und data für kommenden Wert. Evtl. auch dritte als Zähler für
Blinkfrequenz.
Ich habe schon von Anfang an auch LED so etwa gesteuert: in 1 ms-ISR
wird int decrementiert und wenn 1->0, wird LED gelöscht. Zum Einschalten
schreibt Hauptprogramm gewünschte Wert in ms in int. Deshalb denke ich,
hier sollte das auch klappen.
So nebenbei habe ich auch nun DDS ausprobiert, für MIDI-Noten von Nr. 0
bis Nr. 127, Fd = 50 KHz.
1
voidtim2_synth(void){// Wenn F_SYNTH = 1
2
if(FLAGG&(1<<F_SYNTH)){
3
synth_freq+=tonfrequenz[synth_note];
4
if(synth_freq&0x800000){
5
ZV_PORT|=(1<<ZV);
6
}else{
7
ZV_PORT&=~(1<<ZV);
8
}
9
}
10
}
Funktioniert ohne alles anderes zu stören (LCD, Tasten, Drehgeber,
MIDI-Empfang)! Sehr nützliche Versuchsbau - wenn AVR nach 10000 mal am
Ende wird, so werde ich viel mehr über Programmieren wissen, als vorher.
Es lohnt sich, so etwas selber zu bauen und nicht mit fertigen fremden
Platten.
Wenn Du generell mit ISR und Puffer arbeitest, kannst Du den R/W vom
Display auf GND legen. Du liest ja nicht vom Display und hast einen Port
übrig, bei Bedarf.
Schreib die Daten aufs Display und nur aktualisieren wenn sich wirklich
was ändert.
Hier muss man sich jetzt die Frage stellen ob den gesamten Inhalt
aktualisiert oder nur die entsprechenden Zeichen.
Stephan schrieb:> Wenn Du generell mit ISR und Puffer arbeitest, kannst Du den R/W vom> Display auf GND legen.
Danke, schon gemacht. Nur habe ich keine Lust, diese schon fertige
Platte zu ändern. Deshalb bekommt R/~W schon bei INIT 0 und so auch
bleibt.
Für die Zukunft mache ich diese Dummheit nicht mehr. Aber ich sollte
auch das ausprobieren :)
chris schrieb:> Schreib die Daten aufs Display und nur aktualisieren wenn sich wirklich> was ändert.
Danke, schon gemacht. Dafür dient in Programm für LCD-erneuern
1
#define PUF_ERNEUERN 0
2
#define LCD_ZY_MAX 84
3
staticunsignedcharlcd_erneuern=PUF_ERNEUERN;
Alle, die in Puffer schreiben, schreiben auch lcd_erneuern =
PUF_ERNEUERN;
1
if(lcd_erneuern>=LCD_ZY_MAX)
, wird Erneuerung zu Ende.
chris schrieb:> Hier muss man sich jetzt die Frage stellen ob den gesamten Inhalt> aktualisiert oder nur die entsprechenden Zeichen.
Ich denke, das wäre zu aufwendig und bringt so gut wie nichts, da
AVR-Auslastung ziemlich klein bleibt, einmal in 1 ms eine Operation mit
LCD durchzuführen.
Ich weiß zwar wirklich nicht, ob diese Lösung mit ISR so universell ist.
Aber Systemtakt gibt es so gut wie immer, und nach jedem Schreiben
während den nächsten 40 us etwas nützliches zu tun (bei 16 MHz sind das
640 Befehle! )- das ist Grund, ein paar Zeilen mehr zu schreiben.
Vor mir steht nun eine andere Frage:
ich habe LCD-Puffer als Ring gemacht. Das beschleunigt Schieben auf LCD:
1
voidlcd_puff_schieb_links(unsignedchardata){// alle Symbole in LCD-Puffer nach links
/* aus base und index wird faktische Adresse gerechnet */
3
unsignedcharyy;
4
yy=base+index;
5
if(yy>=LCD_PUFFER)yy-=LCD_PUFFER;
6
returnyy;
7
}
Wird Puffer statisch gemacht, so wird es einfacher mit Adresse, aber
Schieben wird um fast zwei Ordnungen langsamer. Von anderer Seite wird
Schieben von LCD-Inhalt nicht so oft gebraucht...
Welche Variante ist sinnvoller?
Aber das sind Operationen, die nur gemacht werden, wenn in Puffer
geschrieben wird. Nicht die jede 1 ms 84 mal nach Erneuerung gemacht
werden.
Ich habe für weitere Entwicklung doch etwas geändert. Unschön, mit
Drähtchen, aber AVR habe ich fest gelötet. Deshalb möchte ich schon
solange umprogrammieren, wie es noch geht, vor dem ich eine andere
Platte mache (10 000 mal ja von Atmel versprochen).
Nun sind LED mit Data von LCD gemeinsam, da kein Lesen von LCD mehr
vorgesehen ist. Ich hoffe, kurze Unregelmäßigkeiten während
Datenaustausch werden auf LED kaum sichtbar.
Auch Erweiterungsbuchse bekommt etwas mehr Leitungen, so daß für +5 Volt
und Grund nur je eine Leitung bleibt - hoffentlich bringt das kein
Problem, es werden keine stromhungrige Erweiterungsplatten benutzt.
Da Programmodule von Anfang an mit beliebigen Ports für Tasten usw.
gedacht sind, wird nur minimale Programmkorrektur gebraucht.