Hallo und grüß euch,
ich habe ein Problem, ich hoffe ihr könnt mir helfen.
Ich habe ein vordeffiniertes Protokoll, dass von einem M8 über die usart
weggeschickt wird, also die Daten halt.Es ist eine eindraht rs232
Verbindung.Also TX und RX sind zusammengeschaltet. Auf dem zweitem M8
ist ein 2x16 Display angeschlossen. Der soll die Daten anzeigen, die er
vom ersten M8 bekommt und ein byte zurückschicken.
Das Protokoll sieht so aus: der erste m8 schickt 34 byts los,das dauert
40ms. Danach soll der zweite M8 mit dem Display die Daten anzeigen und
aber 5ms,nachdem er die bytes erhalten hat, ein byte wegschicken. danach
sind noch mal 25ms pause. Also das gesamte Spiel dauert 70ms. Dann geht
alles wieder von vorne los.
Es funktioniert alles perfekt, solange ich den LCD-Befehl weglasse!
führe ich ihn aus, wirft es alles über den Haufen und die Timings
stimmen nicht mehr!
Was könnte ich tun?
Hier mal mein Code:
Wieso gibst du nicht einfach jedes Zeichen sofort nach dem Empfang auf
dem Display aus?
BTW:
Deine Programmstruktur/Denkweise ist grundlegend falsch :-o
Üblicherweise sollte der Ablauf eines Programms vom User-Interface
entkoppelt werden.
D.h. hauptsächlich wird eingelesen, gearbeitet und geschrieben. Und nur
nebenher angezeigt. Denn es macht überhaupt nichts aus, wenn das Display
z.B. nur 3 mal pro Sekunde aktualisiert wird. Oder wenn wegen hohen
Rechenbedarfs das Display mal eine Sekunde stillsteht...
Kenne kein BASCOM :-)
Mach Dir eine eigene LCD-Ausgabe Routine, am besten per Interrupt und
Zeichenweise, nicht den ganzen String auf einmal. Wahrscheinlich besteht
die vorandene darauf, allen Text zu schreiben, evetuell mit hart
codierten Waitstates nach jedem Zeichen. Wenn das "nur" 10 ms sind macht
das für die ganzen 34 Zeichen schon 5 von deinen Protokoll-Zyklen. Und
Update des Textes am LCD nur wenn nötig. Alle 70ms kannst Du sowieso auf
einem LCD nicht sehen. 1 mal pro Secunde reicht evetuell auch.
Es gibt 2 Sachen, die die Programmstruktur einfacher machen.
1. Du legst ne FIFO für den UART-Empfang an.
Damit wird der Empfang von der Auswertung entkoppelt.
D.h. es können ruhig schon neue Zeichen eintreffen und nichts geht
verloren:
Beitrag "AVR-GCC: UART mit FIFO"
2. Du legst nen Textspeicher für das LCD an und gibst im Hintergrund per
Timerinterrupt (z.B. alle 1ms) ein Byte an das LCD.
Damit kann die Applikation direkt in den Speicher schreiben und die
LCD-Zeit stört nicht mehr:
Beitrag "Formatierte Zahlenausgabe in C"
Der Mensch kann eh nicht mehr als 2..5 Werte pro s ablesen.
Mit 70ms (14/s) bist Du also schon zu schnell, d.h. nicht mehr
ergonomisch.
Peter
Vielen lieben Dank für eure Antworten,
Dass der code nicht ordentlich durchdacht und geschrieben ist, kommt
davon, dass ich es einfach nicht besser kann. Bin Einsteiger, aber ein
bisschen was geht ja.
Nun kann ich mich genauer in das Thema einarbeiten, da ich ein paar neue
Stichwörter habe, woran ich mich halten kann!
Ich werd jetzt mal versuchen, nicht das ganze Display auf einmal zu
beschreiben, sondern byteweise mit 1ms dauer dazwischen. Und natürlich
in einem Interrupt.
Wie das mit dem aktualisieren und so geht, muss ich auch noch schauen!
vielen Dank mal, ich hab jetzt wirklich genug zu tun, wie ich das sehe
;o)
Lg
Gregor
@Peter,darf ich dich noch was fragen bitte?
Dein zweiter Punkt behandelte ja den Textspeicher. Da ich C nicht kann
und somit den Code nicht verstehe, wollte ich dich fragen, ob du mir das
vielleicht kurz genauer erklären könntest?
Habe ich da in der Hauptroutine den LCD Befehl drinnen? Wie lege ich
einen Textspeicher an? Meinst du einen String?
Ich weis, blöde Fragen, aber ich weis es leider nicht...
Lg
Gregor
Du hast ein 2-Zeilen / 20 Zeichen Display.
Dann machst du dir im Programm 2 String Variable mit jeweils 20 Zeichen.
Jeder String korrespondiert mit einer Zeile im Display.
Dazu einen Timer, der einen regelmässigen Interrupt auslöst.
In diesem Interrupt wird ein Zeichen aus dem String an seine jeweilge
Position im LCD ausgegeben.
Will dein Programm eine Ausgabe machen, dann schreibt es das
auszugebende einfach in den String an die richtige Position. Der
Timerinterrupt sorgt dann dafür, dass irgenwann später diese Ausgabe
auch tatsächlich auf dem LCD landet.
Vorteil: Dein Progamm wird nicht durch Ausgaben aufgehalten. In den
String schreiben geht schnell
Nachteil: Ausgaben erscheinen etwas zeitverzögert auf dem Display. Wobei
man diesen 'Nachteil' relativieren muss. Die Zeitverzögerung ist so
gering, dass sie niemandem auffällt.
Hallo Karl,
ich habe das jetzt mal soweit probiert, deine Lösung umzusetzen. Vorerst
mal nur die obere Zeile des 2x16 Displays.
In der Timer Interrupt Routine wird der String an das Display
weitergegeben. Nur leider funktioniert es nicht. Lasse ich den LCD und
Locate Befehl weg, funktioniert die Verbindung! Überprüfe ich mit Hterm
und RS232.
Hier mal mein Code. Könntest du mir sagen, was ich falsch mache?
Gregor schrieb:
> weitergegeben. Nur leider funktioniert es nicht. Lasse ich den LCD und> Locate Befehl weg, funktioniert die Verbindung! Überprüfe ich mit Hterm> und RS232.
Diese beiden Befehle sollten eigentlich nur max 100µs dauern, d.h. bei
1ms Interrupt kein Problem.
Vielleicht hast Du was falsch eingestellt, schau mal in die Erklärung zu
den beiden Befehlen.
Am besten ist natürlich die Befehle selber zu programmieren, dann kann
man sie optimal anpassen.
Ich hab in den Interrupt zusätzliche States eingefügt, die den Befehl
zum Setzen der 1. bzw. 2. Zeile machen.
Damit erfolgt pro Interrupt nur ein LCD-Zugriff und man kann das
Busywaiting komplett weglassen.
Denn bis zum nächsten Interrupt sind ja garantiert 50µs vergangen.
Den MID-Befehl als Arrayzugriff zu mißbrauchen, könnte auch viel länger
dauern als nötig. Besser per Index auf das Arrayelement zugreifen.
> Hier mal mein Code. Könntest du mir sagen, was ich falsch mache?
Sieht eigentlich o.k. aus (außer dem seltsamen MID-Befehl), aber Bascom
ist nicht meine Stärke.
Hier mal die Statemachine in C, wie ich das meine:
1
#define LCD_COLUMN 20
2
#define LCD_LINE 2
3
4
5
uint8_tlcd_buff[LCD_LINE*LCD_COLUMN];// text display memory
6
7
8
ISR(TIMER0_OVF_vect)// interrupt every 1ms
9
{
10
staticuint8_ti=0;// statemachine counter
11
12
i++;// count up
13
switch(i){
14
case1:// state 1:
15
lcd_pos(0,0);break;// line 0, column 0
16
17
case2...1+LCD_COLUMN:// state 2 ... 21:
18
lcd_data(lcd_buff[i-2]);break;// data of 1. line
19
20
case2+LCD_COLUMN:// state 22:
21
lcd_pos(1,0);break;// line 1, column 0
22
23
case3+LCD_COLUMN...2+2*LCD_COLUMN:// state 23 ... 42:
Den Locate zb brauchst du ja nicht bei jeder Zeichenausgabe. Das LCD
setzt seinen internen Cursor ja sowieso nach dem Ausgeben eines Zeichens
an die nächste Position.
Locate brauchst du doch nur, wenn die Zeile gewechselt werden muss.
1
LcdMid(oben,Z,1);'Mid(oben,1,1);
Hmm. Da ist mein BASCOM zu schwach. Aber das sieht mir nicht sehr
zeitsparend aus. Da wird offenbar Stringverarbeitung betrieben um ein
einzelnes Zeichen auszugeben.
Hallo,
danke für eure Antworten, da habe ich wieder einiges zu probieren.
Nur so nebenbei:
Was ist besser für eine Uart Empfangsroutine?
1:
Rx_isr:
Incr I
Text = Text + Chr(udr)
Return
oder
2:
Rx_isr:
rs232buffer(i)=UDR
return
Bei der ersten Routine empfange ich ja Strings, bei der zweiten Routine
kommen die Werte in ein Array.
Was findet ihr, wäre besser?
Lg
Gregor
>Bei der ersten Routine empfange ich ja Strings, bei der zweiten Routine>kommen die Werte in ein Array.>Was findet ihr, wäre besser?
Aus C-Sicht betachtet: Egal. in C sind Strings auch nichts anderes als
Arrays, die noch ein Endezeichen besitzen.
Die Antwort dürfte ziemlich sinnlos gewesen sein.
Auf das Array kannst doch einfacher zugreifen, weil du einen Index auf
die Elemente hast, und nicht erst großartig mit "mid(..." rumbasteln
musst.