www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik LCD Befehl zu langsam in BASCOM


Autor: Gregor (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
$regfile = "M8def.dat"
$initmicro
$crystal = 8000000
$hwstack = 32
$swstack = 10
$framesize = 40
$baud = 9600

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cursor Off

Txd_enable Alias Ucsrb.txen
Txd_pullup Alias Portd.1
Rxd_enable Alias Ucsrb.rxen
Rxd_pullup Alias Portd.0

Enable Interrupts


Config Com1 = 9600 , Synchrone = 0 , Parity = Odd , Stopbits = 1 , Databits = 9 , Clockpol = 0
Open "com1:" For Random As #1
On Urxc Rx_isr
Enable Urxc
Text = ""



Do

Reset Txd_enable                                            'TX ausschalten
Set Rxd_enable                                              'RX einschalten

If Len(text) = 34 Then                                      'Wenn 34 bytes empfangen

'lcd text;                                                   ' am Display anzeigen

Set Txd_enable                                              'TX einschalten
Reset Rxd_enable                                            'RX ausschalten


Waitms 4
Ucsrb.txb8 = 0
Udr = 240                                                   'Das Zeichen F0 senden
Do : Loop Until Ucsra.udre = 1
Waitms 10

Text = ""
End If

Loop
End

'*************************************************************************

Rx_isr:
Text = Text + Chr(udr)
Return

_init_micro:
Config Portd = &B11111100
Set Rxd_pullup
Reset Txd_pullup
Return


Danke, Gregor

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> wirft es alles über den Haufen und die Timings stimmen nicht mehr!

Was stimmt denn genau nicht mehr?

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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...

Autor: micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Gregor (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Gregor (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Gregor (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?
$regfile = "M8def.dat"
$initmicro
$crystal = 8000000
$hwstack = 32
$swstack = 10
$framesize = 40
$baud = 9600

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cursor Off


Tasterauf Alias Portc.2
Portc.2 = 1
Tasterunter Alias Portc.1
Portc.1 = 1
Txd_enable Alias Ucsrb.txen
Txd_pullup Alias Portd.1
Rxd_enable Alias Ucsrb.rxen
Rxd_pullup Alias Portd.0

Dim Rs232byte As Byte
Dim R As Byte
Dim I As Byte
Dim Z As Byte
Dim L As Byte
Dim X As Byte
Dim Taste As Byte
Dim Text As String * 40
'Dim Rs232buffer(41) As Byte At Text Overlay
Dim Oben As String * 17
Dim Unten As String * 17
R = 0
I = 0
Z = 0
L = 2
X = 0
Config Timer1 = Timer , Prescale = 1                        'Konfiguriere Timer1
Enable Timer1                                               'schalte den Timer1 ein
On Timer1 Isr_von_timer1                                    'verzweige bei Timer1 überlauf zu   Isr_von_Timer1
Timer1 = 49536


Config Com1 = 9600 , Synchrone = 0 , Parity = Odd , Stopbits = 1 , Databits = 9 , Clockpol = 0
Open "com1:" For Random As #1
On Urxc Rx_isr
Enable Urxc
Enable Interrupts



Do

Reset Txd_enable                                            'TX ausschalten
Set Rxd_enable                                              'RX einschalten

If Len(text) = 34 Then
Oben = Mid(text , 2 , 16)                                   '16 bytes oben
Unten = Mid(text , 18 , 16)                                 '16 bytes unten
Set Txd_enable                                              'TX ausschalten
Reset Rxd_enable
Waitms 5
Ucsrb.txb8 = 0
Udr = Taste                                                 'zeichen senden
Do : Loop Until Ucsra.udre = 1
Waitms 10
Text = ""
End If

Loop
End

'*************************************************************************

Rx_isr:
Incr I
Text = Text + Chr(udr)
Return

Isr_von_timer1:                                             'ISR von Timer1
Timer1 = 49536                                              '49536Timer1 soll wieder von 34285 wegzählen
Incr Z
Locate 1 , Z
Lcd Mid(oben , Z , 1);                                      'Mid(oben , 1 , 1);
If Z = 16 Then
Z = 0
End If
Return

_init_micro:
Config Portd = &B11111100
Config Portc = &B00000000
Set Rxd_pullup
Reset Txd_pullup
Return


Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
#define LCD_COLUMN      20
#define LCD_LINE        2


uint8_t lcd_buff[LCD_LINE * LCD_COLUMN];        // text display memory


ISR( TIMER0_OVF_vect )                          // interrupt every 1ms
{
  static uint8_t i = 0;                         // statemachine counter

  i++;                                          // count up
  switch( i ){
    case 1:                                     // state 1:
        lcd_pos( 0, 0 ); break;                 // line 0, column 0

    case 2 ... 1 + LCD_COLUMN:                  // state 2 ... 21:
        lcd_data( lcd_buff[i-2] ); break;       // data of 1. line

    case 2 + LCD_COLUMN:                        // state 22:
        lcd_pos( 1, 0 ); break;                 // line 1, column 0

    case 3 + LCD_COLUMN ... 2 + 2 * LCD_COLUMN: // state 23 ... 42:
        lcd_data( lcd_buff[i-3] ); break;       // data of 2. line

    default:                                    // state 43:
        i = 0;                                  // and again
  }
}

Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.
Lcd Mid(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.
Timer1 = 49536                                              '49536Timer1 soll wieder von 34285 wegzählen

Wenn du den Timer voll durchzählen lässt, wird es dann besser?

Autor: Gregor (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.