Forum: Mikrocontroller und Digitale Elektronik grafische Flüssigkristallanzeige schnell löschen


von Wolfgang (Gast)


Lesenswert?

Ich verwende eine Flüssigkristallanzeige mit 128x240 Bildpunkten in der 
Betriebsart Grafik zur Darstellung einer Messkurve.(y=Spannung, x=Zeit) 
Ist die Kurve rechts angekommen, wird, um eine neue Kurve zu zeichnen, 
die gesamte Grafik gelöscht. Z. Zt. lösche ich jedes einzelne 
Grafikzeichen (ca.5220 Zeichen) der Anzeige, was ca. 0,5s dauert. Da 
alle 5ms ein Bildpunkt gesetzt wird, fehlen bei jedem Löschvorgang ca. 
100 einzelne Messwerte.

Als µC wird der MSP430F168 verwendet. Die Flüssigkristallanzeige enthält 
den IS T6963C.
Das Programm ist in 'C' geschrieben.

Gibt es eine Möglichkeit, mit einem einzigen Befehl die gesamte Anzeige 
zu löschen oder könnte man das Programm so gestalten, dass das Löschen 
von Bildpunkten (Kurve) zeitlich nicht ins Gewicht fällt?
Ich danke für jeden Tipp.
Mit freundlichen Grüßen
Wolfgang

von Benedikt K. (benedikt)


Lesenswert?

Nein, solch einen Befehl gibt es nicht.

Deine Grafikroutinen sind aber ziemlich schlecht, wenn du 0,5s für 
5220Byte brauchst. Versuch mal diese zu optimieren.

Ansonsten könnte man eine zweite Speicherseite anlegen, und immer 
abwechselnd eine Seite beschreiben, während die andere angezeigt wird. 
Das setzt aber 32kB RAM auf dem LCD vorraus.

Eine andere Möglichkeit wäre jeder Pixel einzeln zu löschen. Dazu muss 
man speichern welche Pixel wo gesetzt sind, und beim Zeichnen von dem 
Pixel an x=1 wird vorher der gesetzte Pixel an x=1 gelöscht. Die alte 
Kurve wird also abgebaut und die neue gleichzeitig aufgebaut.

von Michael W. (mictronics) Benutzerseite


Lesenswert?

Ich kenne den Grafikkontroller nicht, aber wenn du das LCD umschalten 
zwischen horizontaler und verticaler Addressierung, dann könntest du 
versuchen, immer pro Messwert (1 Pixel auf x-y Achse?) einen senkrechte 
Pixelreihe löschen, das heist du löscht nur den Bereich in dem die Kurve 
gezeichnet wird.
Und wenn die neue Kurve nicht stark von der alten abweicht, dann kannst 
die Länge der Pixelreihe noch verkürzen.

Oder noch besser wenn du weist wo der alte Messwert war kannst du diesen 
Pixel direkt löschen.

von Wolfgang (Gast)


Lesenswert?

Schreibt man zuviel, wird es zu langatmig
Schreib man zu wenig, hat man, wie hier, ein Problem.
Deshalb jetzt etwas konkreter:
Es wird eine einkanalige „EKG“- Kurve  auf der Anzeige dargestellt.
Da die Kurve bei nur 240 möglichen Bildpunkten zu sehr gedehnt ist, 
wird nur jeder 3. Messwert zu Darstellung verwendet, aber jeder 
Messwert( also alle 5ms) wird in ein [Feld] geschrieben, um dann auf 
einer SD- karte gespeichert zu werden. Außerdem wird aus den 
Impulsspitzen der Puls berechnet und im Rhythmus der Herzfrequenz 
angezeigt.
Durch das Löschen entstehen zwei besondere Nachteile: a) es wird der 
Puls immer nach einem Löschvorgang falsch berechnet b) wenn der µC mit 
dem Löschen beschäftigt ist, kann er die Messwerte nicht sammeln und es 
fehlt ein Stück der gespeicherten Kurve.
An den Vorschlag, gesetzte Bildpunkte vor dem Setzen eines neuen 
Bildpunktes zu löschen, hatte ich schon immer gedacht, aber dazu ist mir 
programmtechnisch nichts eingefallen.
Eine so „wandernde Kurve“ würde mir auch besser gefallen.
Gibt es irgendwo dazu ein Beispiel?
Hier noch der Ausschnitt zum Löschen des grafischen Teils. Vielleicht 
sieht jemand eine Möglichkeit zur Beschleunigung (Löschvorgang sollte
 <5ms werden ).

void WriteData(unsigned char d)// Daten ins Datenregister der Anzeige 
schreiben
{
 P4OUT=d;                       //  P4= Datenleitungen
 CD_L;                          // (* C/D = L (Data) *)
 P5OUT &=~(BIT2 + BIT3);        // P5 = Steuerleitungen  WR = L; CE = L
 P5OUT = 0x0F;                            //alles H
}
void WriteCtrl(unsigned char d) // Daten ins Steuerregister der Anzeige 
schreiben
{
  P4OUT=d;
  CD_H;                         //  (* C/D = H (ctrl) *)
  P5OUT &=~(BIT2 + BIT3);       //WR = L; CE = L
  P5OUT=0x0F;                   //alles H
}
void Grafik_loeschen(void)
 {
  int z;
  WriteData(0);
  WriteData(2);
  WriteCtrl(0x24);
// (cGraphResY*((cGraphResX+(cFontWidth-1)) div cFontWidth))-1
  for (z=0; z < 5227; z++)
  {
  WriteData(0);
  WriteCtrl(0xC0);
  }
  }
<<Ich kenne den Grafikkontroller nicht>> s. o. --> T6963C

Mit freundlichen Grüßen
Wolfgang

von Thomas K. (thkais)


Lesenswert?

Ein kleiner Ideenschub zu der "wandernden" Anzeige:
Man nimmt ein Array mit 240 Elementen.
Soll nun ein Messpunkt auf dem LCD dargestellt werden, wird zuerst im 
Array nachgeschaut, welcher Punkt vorher gesetzt war und dieser wird 
gelöscht. Dann wird der aktuelle Punkt gesetzt und im Array gespeichert. 
Das geht man dann von 0-239 durch.
Das komplette löschen des LCDs entfällt dadurch komplett.

von Philipp B. (philipp_burch)


Lesenswert?

Mach es doch so wie bei den "echten" EKGs. Einfach alle Spalten des 
Bildschirms durchgehen und mit dem aktuellen Messewert überschreiben. 
Dann musst du überhaupt nie was löschen.

von Wolfgang (Gast)


Lesenswert?

Hallo Philipp,
hast du zu deinem Vorschlag mal ein Programmbeispiel passend zum T6963C?
dann wäre das Problem wahrscheinlich schon gelöst
MfG
Wolfgang

von unscheinbarer WM-Rahul (Gast)


Lesenswert?

Du mußt dir doch "einfach nur" merken, in welcher Spalte du dich gerade 
aufhälst.
Aber so wie du das schilderst, schreibst du vermutlich immer einen 
kompletten Datensatz auf die Anzeige.
Was Philipp meint, ist, den Messwert quasi sofort auf dem Display in der 
aktuellen Spalte auszugeben (weiterverwenden kann man ihn hinterher 
immer noch). Sobald man in der 240. Spalte angekommen ist, springt der 
"Spalten-Merker" auf die 1. zurück ("popelige" If-Abfrage).
Dabei behandelt man das Display wie einen Ringpuffer.

von Thomas K. (thkais)


Lesenswert?

Nur dass das spaltenweise Überschreiben beim T6963 in üble 
Bitschiebereien ausartet. Der Speicher ist horizontal organisiert, 
Zeilen löschen wäre kein Problem. Wenn man sich den Wert in einem Array 
vorhält, spart man da viel Zeit.

von unsichtbarer WM-Rahul (Gast)


Lesenswert?

Da werde ich mir den Controller wohl mal angucken müssen.
Und die "üble Bitschiebereien" sollte man durch eine 
Pixel(x,y,color)-Funktion vereinfachen können....

von Fabian (Gast)


Lesenswert?

also entweder würde ich die jeweiligen werte, auf welcher höhe ein pixel 
gesetzt wird im stack zwischenspeichern und dann einfach wieder von 
rechts nach links löschen, um dann erneut von links nach rechts zu 
schreiben. In diesem Fall hast du dann auch gleich den ADP im Idealfall 
richtig gesetzt.
Außerdem scheint es mir momentan so, dass du jedes einzelne bit der 
anzeige mit einem bytebefehl einzeln löscht. Du Könntest einfach in 
deinen Darstellungsbereich ein Bild mit FF,FF,FF .... laden. Das sollte 
niemals 0,5 Sekunden dauern!

von Rooney B. (rooney)


Lesenswert?

Also ich hab für meinen MP3 Player einen Display Treiber programmiert, 
der dir vielleicht weiterhelfen könnte.
Verwende ihn um ein Balken-Frequenzspektrum anzuzeigen.
Der aktualisiert nur das was wirklich nötig ist. Ein kompletter 
Bildaufbau ist nur in den seltesten Fällen notwendig.
Der Treiber ermöglicht es auch auf einem Bildschirm mehrere "Fenster" zu 
öffnen um mehrere Kurven gleichzeitig darstellen zu können. Wie bei 
einem LP12 (Defi + EKG Funktion) wäre es also denkbar die drei 
relevanten EKG Kurven anzuzeigen.

Das Projekt klingt interessant. Kann man dazu mehr Informationen haben?
Ich arbeite beim Samariterbund Österreich und so ein DIY EKG Gerät für 
die private Notfalltasche wäre schon sehr verlockend. Auch wenn es 
vielleicht kein Profi-Gerät sein sollte, je nachdem wie viel Know-How 
darin steckt, etliche der Herzerkrankungen wären dennoch erkennbar.

von thkais (Gast)


Lesenswert?

@unsichtbarer....
Ich meine, dass man nichts gewonnen hat, wenn man das LCD spaltenweise 
löscht. Das wären dann 128 Zugriffe auf das LCD pro Spalte, also 240*128 
= 30720 Zugriffe bei einem Bildschirm. Da sind dann die 3840 Zugriffe 
beim Löschen (bzw. 5120 bei Pixelwidth = 6) noch effektiver, und das ist 
dem Fragesteller ja zu langsam. Wenn ich die vorhergehende Position des 
Pixels gespeichert habe, sind das 480 Zugriffe / Bildschirm (240 zum 
Löschen, 240 zum neu setzen). Der T6963 hat sogar eine Pixel-Set / 
Pixel-Clear Unterstützung, die man hierfür direkt verwenden könnte.

von unsichtbarer WM-Rahul (Gast)


Lesenswert?

>Ich meine, dass man nichts gewonnen hat, wenn man das LCD spaltenweise
>löscht.

Hab ich nie behauptet.
Eigentlich braucht man nur ein Array für die 240 Bildpunkte in dem die 
jeweilge Position gespeichert wird.
Da wird dann der neue Wert eingetragen, sobald das Pixel an der alten 
Position gelöscht und an der neuen Position gesetzt wurde.
Ich werde mir das Controller-Datenblatt nachher mal näher angucken.

von Christoph Kessler (db1uq) (Gast)


Lesenswert?

"Wandernde Kurve" wäre möglicherweise mit Hardware machbar. Manche 
LCD-Controller haben die Möglichkeit "hard scoll" oder so ähnlich 
eingebaut, das spart das Software-scrollen durch Bitschaufelei. Wenn man 
den Controller selbst baut, z.B. im FPGA, geht das einfach durch einen 
Addierer vor den Adresseingängen des Bildspeichers.

von Christoph Kessler (db1uq) (Gast)


Lesenswert?

skoll! Silvester ist wieder nahe, dinner for one... sollte scroll heißen

von Wolfgang (Gast)


Lesenswert?

Ich versuche mal zusammenzufassen, ohne jemanden zu nahe treten zu 
wollen und es soll auch auf keinen Fall die Diskussion beendet werden, 
denn es wird immer interessanter.

-das Löschen einer grafischen Anzeige unter Verwendung des Controllers 
T6963C ist mit einem einzigen Befehl nicht möglich
-nahezu alle Vorschläge basieren auf theoretischen Gedankengängen, wobei 
zum Teil die spezifischen Eigenschaften einer LCD, die als Controller 
den IS T6963C enthält, nicht berücksichtigt werden


@Thomas
Natürlich kann ich weitere Informationen zur Verfügung stellen. Entweder 
wir eröffnen ein neues Thema oder du gibt’s mir mal deine 
E-Mail-Adresse.
Eine mögliche Zusammenarbeit würde ich (medizinischer Laie) sehr 
begrüßen, um noch besser medizinische  Gesichtpunkte berücksichtigen zu 
können.
In Kurzform:
Der Grundgedanke war, Herzrhythmusstörungen  im Urlaub zu erkennen,
deshalb klein und Batteriebetrieb über wenige Stunden,  nur 2 Ableit- 
elektroden  (+ 1 Elektrode zur Brummspannungsunterdrückung)
Nachträglich ist die Speicherung der Kurve auf einer  SD-Speicherkarte 
hinzugekommen, wo alle (z. Zt. 5ms) Messwerte in voller A/D –Auflösung 
(12 oder 10Bit) aufgezeichnet werden.
Problem: fehlenden Werte durch Löschen der Anzeige --> Hauptgrund zur 
Eröffnung des obigen Themas
Größe: passend in Videokasettenhülle  (ca. 180x105x25mm )
Stromversorgung: 2x Akku R3
Eingangsverstärker:  Instrumentenverstärker INA326 + LMV324 zur 
automatischen Nulllageregelung

Was versteht man unter „LP12 (Defi + EKG Funktion)“ und  „DIY EKG 
Gerät“?

@Fabian
Was versteht man unter ADP?
Es wird nicht jeder einzelne Bildpunkt gelöscht (128x240= 30720! 
Bildpunkte)

Mit freundlichen Grüßen
Wolfgang

von arc (Gast)


Lesenswert?

Im Simulator braucht die Schleife knapp 314000 Taktzyklen.
D.h. der MSP läuft nur mit 628kHz!
Optimierungsmöglichkeiten:
- keine Funktionsaufrufe, loop-unrolling, nur noch ~200k
  for (int i = 0; i < 5227; i+=2) {
    P4OUT=0;
  CD_H;                         //  (* C/D = H (ctrl) *)
  P5OUT &=~(BIT2 + BIT3);       //WR = L; CE = L
  P5OUT=0x0F;                   //alles H

  P4OUT=d;
  CD_H;                         //  (* C/D = H (ctrl) *)
  P5OUT &=~(BIT2 + BIT3);       //WR = L; CE = L
  P5OUT=0x0F;                   //alles H

    P4OUT=0;
  CD_H;                         //  (* C/D = H (ctrl) *)
  P5OUT &=~(BIT2 + BIT3);       //WR = L; CE = L
  P5OUT=0x0F;                   //alles H

  P4OUT=d;
  CD_H;                         //  (* C/D = H (ctrl) *)
  P5OUT &=~(BIT2 + BIT3);       //WR = L; CE = L
  P5OUT=0x0F;                   //alles H

}

- falls möglich Data-Auto-Write des T6963 benutzen ~107k-Zyklen
  WriteData(0);
  WriteData(2);
  WriteCtrl(0x24);
  WriteCtrl(0xb0); // auto write on
  for (z=0; z < 5227; z+=2)
  {
 P4OUT=0;                       //  P4= Datenleitungen
 CD_L;                          // (* C/D = L (Data) *)
 P5OUT &=~(BIT2 + BIT3);        // P5 = Steuerleitungen  WR = L; CE = L
 P5OUT = 0x0F;                            //alles H
 P4OUT=0;                       //  P4= Datenleitungen
 CD_L;                          // (* C/D = L (Data) *)
 P5OUT &=~(BIT2 + BIT3);        // P5 = Steuerleitungen  WR = L; CE = L
 P5OUT = 0x0F;                            //alles H
  }
    // auto write reset
    WriteCtrl(0xb2);

  }



von Rooney B. (rooney)


Lesenswert?

Ein LP12 ist ein halbautomatischer Defibrillator wie er bei 
Rettungsorganisationen eingesetzt wird.
Wer ihn sich ansehen will, hier ein Link:
http://www.lifepak.de/produkte/lp12.pdf

Mit DIY EKG Gerät bezeichne ich ein Do-It-Yourself EKG Gerät, was man 
beispielsweise als Bausatz verkaufen könnte. Handelsübliche EKG Geräte 
sind sehr teuer. Der LP12 kostet beispielsweise 10000 Euro (je nach 
Ausstattung). Herzrhythmusstörungen zu erkennen sollte aber mit einem 
"selbstgebauten" auch relativ einfach sein.

EKG Diagramme zu lesen ist nicht trivial. Wer wirklich ein EKG Diagramm 
verstehen und interpretieren will muss dafür lange lernen und Kurse auf 
der UNI besuchen. Viele Ärzte können es nicht einmal. Ich geb's zu, ich 
kann es auch nur teilweise. Aber als Rettungssanitäter muss ich das 
ohnehin nicht können...


Schon mal daran gedacht das ganze mit einem FPGA zu lösen? Hätte vor 
allem was Performance betrifft sehr große Vorteile.

Wenn du einer Zusammenarbeit nicht abgeneigt bist, dann würde ich gerne 
mitmachen.
Ich habe insofern großes Interesse daran, da dieses Gerät beispielsweise 
für Remote-Monitoring eingesetzt werden könnte.
Dies genau zu erläutern würde den Rahmen dieses Threads sprengen. 
Einsetzbar wäre es zur Früherkennung von Herzerkrankungen und darauf 
basierender autonomer Alamierung eines Notarztteams. Selbstverständlich 
wäre für dieses Monitoring nur EKG zu wenig. Hierzu wären noch weitere 
Vitalparameter relevant.




-------------------------
www.poms-engineering.at

von Philipp B. (philipp_burch)


Lesenswert?

Oh, ist ja einiges gegangen in dem Thread seit ich das letzte mal 
vorbeigeschaut habe.

Also, eigentlich hat "unscheinbarer WM-Rahul" ziemlich genau beschrieben 
was ich gemeint habe. Du legst dir ein Array mit 240 Elementen an. Dazu 
brauchst du noch einen Index, der am Anfang auf Null steht. Wenn du nun 
einen Messwert hast, dann nimmst du erstmal den Wert im Array, der von 
Index angezeigt wird und löschst das betreffende Pixel. Danach setzt du 
das Pixel mit dem neuen Wert und erhöhst Index um Eins. Sobald Index 240 
erreicht setzt du ihn auf 0. Braucht dann für jeden Messwert gerademal 
zwei Zugriffe auf's LCD, das sollte in 5ms wohl zu schaffen sein. Da 
könntest du wohl eher noch Probleme mit dem Speichern auf die SD-Karte 
bekommen, einzelne Schreibzugriffe sind da recht langsam. Wenn du einen 
Controller mit genügend RAM hast (F168 ist gut, 1kB brauchst du ja eh um 
auf die SD zu schreiben), dann kannst du dir ein zweites Array anlegen, 
in dem du jeweils beim "Überlauf" von Index alle Werte 
zwischenspeicherst und dann die Funktion zum Speichern auf die SD 
aufrufst. Dann muss das Neuzeichnen halt in einem Interrupt passieren.

von Wolfgang (Gast)


Lesenswert?

Interessante Simulation; nuur 628kHz Taktfrequenz für dem MSP. Das muss 
ich mal mit dem Oszi näher untersuchen, ob ich da einen Fehler in meiner 
Beschaltung finde. Lt. Datenblatt soll ein MSP mit ca. 5Mhz laufen, was 
eine
8 fach höhere Geschwindigkeit ergeben würde.
Die Optimierungsvorschläge werde ich mal einarbeiten.
Was versteht man unter loop-unrolling ?


@ Philipp
Im Prinzip hab ich schon verstanden, was du meinst, muss aber zu meiner 
Schande gestehen, dass ich mit meinen bescheidenen 
Programmierkenntnissen es nicht in ein Programm umsetzen kann. Ich bin 
auch etwas skeptisch, ob das überhaupt gehen könnte. Vielleicht findest 
du mal die Zeit, in C die Programmzeilen zu schreiben. evtl. mein 
Programm im Anhang abändern (möglichst mit viel Kommentar, damit auch 
ich es begreife)
<<Dann muss das Neuzeichnen halt in einem Interrupt passieren. >>  Wie 
ist das gemeint? Bei mir läuft der TIMERA, welcher alle 5ms ein ISR 
Programm anstößt, wo weitere Unterprogramme aufgerufen werden. s. Anhang

Im Anhang der wesentliche Teil meines Programm zum Zeichnen der Kurve.

Mit freundlichen Grüßen
Wolfgang

von Wolfgang (Gast)


Angehängte Dateien:

Lesenswert?

Mist--- Anhang fehlt

von Karl heinz B. (kbucheg)


Lesenswert?

> Im Prinzip hab ich schon verstanden, was du meinst, muss aber zu meiner
> Schande gestehen, dass ich mit meinen bescheidenen
> Programmierkenntnissen es nicht in ein Programm umsetzen kann.

Wo liegt das Problem?

Du definierst dir ein Array. Im Beispiel mach ichs mal 256
Einträge gross. In jedem Eintrag wird abgelegt, auf welcher
Höhe das Pixel gesetzt wurde.


unsigned char Werte[256];


und wenn du jetzt einen neuen Wert hast, der in Spalte x ausgegeben
werden soll, löscht du das Pixel, dass im Durchgang zuvor gesetzt
wurde. Welches das ist, steht im Array


     clear_pixel( x, Werte[x] );
     set_pixel( x, NeuerWert );
     Werte[x] = NeuerWert;

x wird dabei immer grösser, bis du den rechten Rand erreicht
hast, danach beginnt x wieder bei 0 (der nächste Durchgang).

Mach dir unbedingt ein paar Funktionen zum setzen und
löschen von Pixel an einer bestimmten Position. Vereinfacht
die weitere Programmierung ungemein!

von Karl heinz B. (kbucheg)


Lesenswert?

Und bau dein Programm um.
So wie du das machst zeichnest du jedesmal im
Interrupt das komplette Bild neu. Das ist ziemlich
unsinnig. Im Interrupt brauchst du nur den neuen Messwert
feststellen, das x um 1 erhöhen und obigen Lösch/Neuzeichnen
Prozedur ausführen. x nach abprüfen ob der rechte Rand
erreicht ist und gegenenfalls x wieder auf 0 zurücksetzen.

Sinngemäss also so:


unsigned char Werte[128];
unsigned char x;


interrupt()
{

   besorge neuen Messwert;

   clear_pixel( x, Werte[x] );
   set_pixel( x, Messwert );
   Werte[x] = Messwert;

   x++;
   if( x == 128 )
     x = 0;
}

von Karl heinz B. (kbucheg)


Lesenswert?

Entschuldigung.
128 waren ja die Zeilen, du hast ja 240 Spalten.
Also:

unsigned char Werte[240];
unsigned char x;


interrupt()
{

   besorge neuen Messwert;

   clear_pixel( x, Werte[x] );
   set_pixel( x, Messwert );
   Werte[x] = Messwert;

   x++;
   if( x == 240 )
     x = 0;
}

von Wolfgang (Gast)


Angehängte Dateien:

Lesenswert?

Da habe ich doch zuviel aus dem Ursprungsprogramm herausgenommen.
Deshalb noch einmal ohne SD- Kartenspeicherung und Pulsberechnung s. 
Anhang

@Karl Heinz,
genauso mach ich es schon.
Mit MWroh = ADC12MEM0 wird der neue Messwert geholt und mit dem Aufruf 
EKG() wird der neue Bildpunkt berechnet.
Ist x am rechten Bildrand angekommen (bei mir mit XW bezeichnet) wird 
mit
if (XW > 239)
   {
    XW=0;
    B=0;
    Bild2 = Bild2+1;
    }
wieder bei x = 0 angefangen, wobei zunächst eine um 50 Zeilen 
verschobene 2. Kurve gezeichnet wird, bevor nach dem 2. Durchgang das 
eigentliche Löschen des gesamten grafischen Teils der Anzeige beginnt.

.>> In jedem Eintrag wird abgelegt, auf welcher Höhe das Pixel gesetzt 
wurde. <<
Muss ich aber mal probieren, ob das bei mir was wird.

Allerdings muss noch folgendes Problem gelöst werden:
Wenn ein Impuls sehr steil ist, werden unter Umständen nur 2 bis 3 
Bildpunkte in y-Richtung gesetzt, wodurch das Diagramm schwer lesbar 
wird. Deshalb lasse ich bei Messwertdifferenzen von >4 Bildpunkten 
zusätzliche Bildpunkte in y-Richtung setzen.
Das könnte auch später noch gelöst werden.
Jetzt mal eine Frage in ganz anderer Richtung: Ich dachte bisher, dass 
ein vom TIMER ausgelöster Interrupt  ein laufendes Programm unterbricht, 
der letzte ausgeführte Befehl sich gemerkt wird, um nach Abarbeitung der 
ISR, dort wieder weiterzumachen. Wenn das so ist, müsste doch der 
Löschvorgang (ca. 0,5s) alle 5ms unterbrochen werden , die ISR 
abgearbeitet werden, um dann den Löschvorgang fortzusetzen Wie ist das 
wirklich? oder müsste man noch etwas einstellen.

Wolfgang

von Karl heinz B. (kbucheg)


Lesenswert?

> wieder bei x = 0 angefangen, wobei zunächst eine um 50 Zeilen
> verschobene 2. Kurve gezeichnet wird, bevor nach dem 2. Durchgang das
> eigentliche Löschen des gesamten grafischen Teils der Anzeige beginnt.

Genau darum geht es.
Wozu den graphischen Teil komplett löschen?
Im Array steht für jede Spalte in welcher Zeile ein Pixel
gesetzt wurde. Nur dieses 1 Pixel wird gelöscht und
dann ist diese komplette Spalte wieder leer.

> Wenn ein Impuls sehr steil ist, werden unter Umständen nur 2 bis 3
> Bildpunkte in y-Richtung gesetzt, wodurch das Diagramm schwer lesbar
> wird. Deshalb lasse ich bei Messwertdifferenzen von >4 Bildpunkten
> zusätzliche Bildpunkte in y-Richtung setzen.
> Das könnte auch später noch gelöst werden.

Ist immer noch das gleiche Prinzip.
Du musst dir in einem Durchlauf von links nach rechts Information
hinterlassen, wo du welche Pixel gesetzt hast. Im nächsten
links-rechts Durchlauf nimmst du diese Information her um gezielt
nur diese gesetzten Pixel zu löschen.
Anstatt den kompletten Graphikbereich mit x-tausend Pixel zu löschen,
löscht du nur die 3 oder 4 Pixel in der Spalte in der du als
nächstes auszugeben gedenkst.

Wenn mehrere Pixel gesetzt werden, dann würde och mir halt 2
Arrys machen. Eines für die untere Grenze, eines für
die obere Grenze. Alle dazwischenliegenden Pixel sind zu löschen.

> Wie ist das wirklich? oder müsste man noch etwas einstellen.

Sodern die Interrupts freigegeben sind. Ja klar. Genau das
ist ja das Prinzip eines Interrupts: Er unterbricht die momentane
Programmausführung und wendet sich was anderem zu.
Haarig wird es nur, wenn bereits ein Interrupt behandelt wird
und während dieser Behandlung ein neuer Interrupt auftritt.


von Bernhard S. (bernhard)


Lesenswert?

Ist nur so eine Idee:

Und wenn man kurzzeitig das Display von der Versorgungsspannung trennt, 
um den Inhalt zu löschen?


Bernhard


von Karl heinz B. (kbucheg)


Lesenswert?

> genauso mach ich es schon.

Ganz ehrlich.
Dein Programm ist ein dermassen undurchsichtiger,
unübersichtlicher Kauderwelsch. Da mach ich mir
nicht die Mühe, dass im Detail zu analysieren.

Wenn du
  * konsistente und vor allem sinnvolle Einrückungen
  * vernünftige Variablennamen
  * Für 'Pixel setzen', 'Pixel löschen' eigene Funktionen

machen würdest, wäre schon viel gewonnen.

Und komm mir nicht mit: Funktionsaufrufe dauern so lange.
Die paar µS für den gesparten Funktionsaufruf retten
dein Timing auch nicht.

von Karl heinz B. (kbucheg)


Lesenswert?

> Ist nur so eine Idee:
>
> Und wenn man kurzzeitig das Display von der Versorgungsspannung
> trennt, um den Inhalt zu löschen?

* so ein Display muss in der Regel initialisiert werden
* auf dem Display ist ja wahrscheinlich nicht nur die
  Kurve drauf. Da gibt es sicher auch noch Achsenkreuze
  mit Beschriftungen. All das muss dann wieder restauriert werden.


Das ist doch kein Problem. 5ms sind für einen Prozessor
eine halbe Ewigkeit. Die paar Pixel zu löschen kann doch
nicht das Problem sein.

Wenn ich mir allerdings die Schleifen anschaue die die
senkrechten Linien malen, wird mir schlecht.


von guelcki (Gast)


Lesenswert?

@Wolfgang
<klugscheisser_modus>
Mit einem EKG kann man keine Pulsfrequenz messen, nur die Frequenz der 
elektrischen Aktivität am Herzen (=Herzfrequenz). Was peripher ankommt 
weiß nur der liebe Gott, oder wenn du per Hand (mal von elekt. SpO2 
Messung abgesehen) den Puls misst.
</klugscheisser_modus> ;-)

von Fabian (Gast)


Lesenswert?

ADP = addresspointer

und in der Fragestellung wird nicht deutlich, ob der Grafikbereich nun 
im Pixelmodus oder im bytemodus betrieben wird, da nicht angegeben wird, 
ob das gesamte Display oder nur ein Ausschnitt daraus gelöscht wird.
und ich bevorzuge immernoch die Methode, die einzelnen Pixel mir ihrer 
jeweiligen y-Koordinate auf dem Stackzu speichern und dann von rechts 
nach links zu löschen!

von Wolfgang (Gast)


Lesenswert?

>Dein Programm ist ein dermassen undurchsichtiger,
unübersichtlicher Kauderwelsch.

Hart, aber gerecht.
Programmieren habe ich nie gelernt, nur angelesen und Beispiele z. T. 
nachempfunden. Deshalb auch meine einfachen Fragen im Forum nach 
Programmbeispielen.
Und damit geht es schon los:
clear_pixel( x, Werte[x] );
set_pixel( x, Messwert );
damit kann ich Moment nichts anfangen, da ich es noch nicht verstehe.

> Wenn du
  * konsistente und vor allem sinnvolle Einrückungen
  * vernünftige Variablennamen
  * Für 'Pixel setzen', 'Pixel löschen' eigene Funktionen

machen würdest, wäre schon viel gewonnen.

Dazu kann ich nur sagen: ich werde mir Mühe geben.

> Wenn ich mir allerdings die Schleifen anschaue die die
senkrechten Linien malen, wird mir schlecht.

Das war nicht beabsichtigt.

>Mit einem EKG kann man keine Pulsfrequenz messen,

„Pulsfrequenz „  kann ich in keinem Beitrag finden. Puls ist schon eine 
Frequenz und läst sich durch Messung der Zeit zwischen 2 Impulsen 
berechnen Das weiß aber nicht der liebe Gott.

Mit freundlichen Grüßen
Wolfgang

von Wolfgang (Gast)


Lesenswert?

aha, so werden Zitate in einer anderen Farbe dargestellt

von unscheinbarer WM-Rahul (Gast)


Lesenswert?

>aha, so werden Zitate in einer anderen Farbe dargestellt
>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>>>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>>>>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>>>>>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>>>>>>>aha, so werden Zitate in einer anderen Farbe dargestellt
>>>>>>>>>>aha, so werden Zitate in einer anderen Farbe dargestellt

Ich frage mich, wie oft das geht...

von Karl H. (kbuchegg)


Lesenswert?

> clear_pixel( x, Werte[x] );
> set_pixel( x, Messwert );
> damit kann ich Moment nichts anfangen, da ich es noch nicht verstehe.

Das sind ganz normale Funktionsaufrufe.
Genauso wie du dir eine EKG Funktion definiert hast.
Nur werden halt diesen Funktionen noch Werte mitgegeben,
damit die Funktionen damit rechnen können.

Du brauchst also eine Funktion set_pixel. Diese Funktion
soll natürlich mitbekommen, die Zeile und die Spalte
in der das Pixel zu setzen ist.

Die Funktion schaut also so aus. (Ich hab keine
Ahnung ob die Berechnung so stimmt, ich übernehm das
aus deinem Code. Du musst also die Berechnung kontrollieren).


void clear_pixel( unsigned char Zeile, unsigned char Spalte )
{
  ...

Hier wird also vereinbart dass es eine Funktion clear_pixel
geben soll. Diese Funktion bekommt 2 Argumente mit. Die
Zeilennummer und die Spaltennummer. Innerhalb der Funktion
sollen die mitgegebenen Argumente in den Variablen Zeile
und Spalte abgelegt sein. Ach ja: Die Argumente machen
wir unsigned char, weil das völlig ausreicht. Ein unsigned
char kann Zahlen von 0 bis 255 aufnehmen. Deine Zeilennummern
bewegen sich von 0 bis 240. Die Spaltennummern von 0 bis 128.
Alles also im Bereich 0 bis 255.

Wichtig: Innerhalb der Funktion konzentrieren wir uns
nur darauf was es bedeutet an eine bestimmte Funktion
ein Pixel zu setzen. Das ist die Aufgabe dieser Funktion.
Nicht mehr und nicht weniger.

Weiter im Text
1
#define PIXEL_PRO_SPALTE   128
2
#define PIXEL_PRO_ZEILE    240
3
4
#define PIXEL_PRO_BYTE       6
5
#define BYTE_PRO_ZEILE     ( PIXEL_PRO_ZEILE / PIXEL_PRO_BYTE )
6
#define START_GRAPHIK      0x200  /* Die Startadresse im Bildspeicher */
7
8
void clear_pixel( unsigned char Spalte, unsigned char Zeile )
9
{
10
  unsigned int StartAddr;
11
12
  // Wir wollen den 0/0 Punkt links unten im Eck
13
  // haben. Das Display hat aber den 0/0 Punkt links oben
14
  // im Eck. Also drehen wir die Zeilennummer entsprechend um
15
16
  Zeile = PIXEL_PRO_SPALTE - Zeile;
17
18
  // Die Startadresse im Bildspeicher berechnen
19
  StartAddr = START_GRAPHIK +
20
              Zeile * BYTE_PRO_ZEILE +
21
              Spalte / PIXEL_PRO_BYTE;
22
23
  WriteData( StartAddr & 0xFF );     // Das Low Byte ausgeben
24
  WriteData( StartAddr >> 8 );       // Das High Byte ausgeben
25
  WriteCtrl( 0x24 );
26
  WriteCtrl( 0xF0 | (5 - Spalte % PIXEL_PRO_BYTE ) );    // 1111 0 xxx   0  löscht einen Bildpunkt;
27
}

Mangels Display kann ich die Funktion natürlich nicht testen.
Deine Aufgabe ist es, die entsprechende set_pixel Funktion zu
schreiben. Schau dir auch an, wie ich mit ein paar defines
die meisten Zahlen aus der Funktion verbannt habe. Zum einen
erreiche ich dadurch eine gute Anpassbarkeit an andere Displays
mit andern Pixelzahlen. Zum anderen wird dadurch die Funktion
selbstdokumentierend.

Um jetzt sagen wir mal eine senkrechte Linie zu zeichnen, kann
ich zb eine Funktion schreiben:

void VertLine( unsigned char Spalte,
               unsigned char ZeileStart,
               unsigned char ZeileEnd )
{
  // Die Funktion zeichnet eine senkrechte Linie in einer Spalte
  // Die Linie beginnt in der Zeile ZeileStart und endet in
  // der Zeile ZeileEnd. Die Pixel Spalte/ZeileStart bzw.
  // Spalte/ZeileEnd werden gesetzt!

  unsigned char Zeile;

  // Wenn ZeileStart grösser ZeileEnd ist, dann werden beide
  // vertauscht. Dadurch kann die Linie immer von Start nach
  // End gezeichnet werden

  if( ZeileStart > ZeileEnd ) {
    unsigned char Temp = ZeileStart;
    ZeileStart = ZeileEnd;
    ZeileEnd = Temp;
  }

  // Jetzt die Linie zeichnen
  for( Zeile = ZeileStart; Zeile < ZeileEnd; ++Zeile )
    set_pixel( Spalte, Zeile );
}

Siehst du. Um eine senrechte Linie zu zeichnen wird einfach
nur ausgerechnet, welche Pixel zu setzen sind. Das eigentliche
Pixel setzen übernimmt die Funktion set_pixel. Dadurch erreichen
wir zunächst mal, dass sich diese Funktion nicht mehr um die
Low-Level Details der Byte Adressierung kümmern muss. Das ist
auch nicht ihre Aufgabe. Zum anderen wird die Funktion einfach
genug, dass man durch hinschauen feststellen kann ob das
stimmen kann. Genau das ist nämlich eines der Geheimnisse in
der Programmierung: Probleme so aufteilen, dass man die Komplexität
beherrschen kann. Diese Funktion löst bereits ein nicht so triviales
Problem, nämlich das Zeichnen einer senkrechten Linie. Durch
das sinnvolle unterteilen in Bausteine (Pixel setzen) und Verwendung
dieses Bausteins, wird die Funktion aber wieder einfach genug, um
sie ohne grosse Probleme schreiben zu können. Ein guter Programmierer
ist nicht einer, der 1000 Zeilen Funktionen schreiben und verstehen
kann. Ein guter Programmierer ist jener, der es schafft
komplizierte Dinge in einfachere Bausteine aufzuteilen und mit diesen
einfacheren Bausteinen das komplizierte Problem zu lösen.

Damit haben wir aber schon die Bausteine beisammen um
uns der EKG Funktion zuwenden zu können:

Was ist ihre Aufgabe. Sie soll den Messwert übernehmen
und in der nächsten Spalte ausgeben. Dabei soll sie aber
auch berücksichtigen, dass bei einer Differenz von 1 zum
letzten Messwert nur 1 Pixel gesetzt werden soll und wenn
die Differenz größer als 4 Pixel ist, soll eine senkrechte
Linie gezeichnet werden.

Na dann schreiben wir das doch so

void EKG( unsigned char Messwert )
{
  char Differenz;

  // Die Different zum vorhergehenden Messwert berechnen
  // Achtung: Die Differenz kann auch negativ sein! Das darf
  // aber nicht sein, da wir unsigned rechnen!
  // Das naheliegende Differenz = abs( Alt - Jetzt )
  // funktioniert also nicht.
  //
  if( Messwert > MesswertAlt )
    Differenz = Messwert - MesswertAlt;
  else
    Different = MesswertAlt - Messwert;

  if( Differenz < 4 )
    set_pixel( DieseSpalte, Messwert );

  else
    VertLine( DieseSpalte, MesswertAlt, Messwert );
}

Siehst du, wie simpel auf einmal die Funktion geworden ist.
Alle Schritte sind auch für einen Blinden nachvollziehbar.

Das ist jetzt die Überarbeitete Version dessen was du
bereits hast. Zum Rest, wie du das Löschen besser organsieren
kannst, wurde ja schon genug geschrieben.

Besorg dir unbedingt Literatur. Wenn du über Programmorganisation
nicht viel weist, ist das verzeihlich. Wenn du aber aus Unkenntnis
der Möglichkeiten in C schlechte Programme schreibst, so ist das
einfach nur nachlässig. Und gerade bei einem Gerät, das im
medizinischen Bereich eingesetzt werden soll, ist Nachlässigkeit
absolut inakzeptabel.

von Wolfgang (Gast)


Lesenswert?

Danke deine Mühe und die sehr ausführlichen Erläuterungen und 
Programmvorschläge. Das bringt mich bestimmt jetzt weiter.
Wenn ich alles verdaut habe, werde ich es deine Programmteile 
einarbeiten und zu gegebener Zeit kurz berichten.
Etwas Literatur habe ich, wenn auch für Delphi (Doberenz/Kowalski 
Delphi7 Grundlagen und Profiwissen) mit 1025!! Seiten. Hat 
offensichtlich bei mir noch nicht allzu viel gebracht.
Mit freundlichen Grüßen
Wolfgang

von Wolfgang (Gast)


Lesenswert?

das Problem konnte dank Eurer Hilfe gelöst werden.
Und so funktioniert es (in Kurzform):
Die Messwerte werden nach einer Größenanpassung sowohl als Bildpunkt auf 
der Flüssigkristallanzeige angezeigt als auch in einem [Feld] 
abgespeichert.
Ist der y-Abstand zweier aufeinander folgenden Bildpunkte größer 4, wird 
eine vertikale Linie berechnet und gezeichnet, um die Lesbarkeit zu 
verbessern.
Wenn die Kurve am rechten Bildrand (x=239) angekommen ist, wird x auf 0 
gesetzt, und der alte Kurvenzug erneuert, indem vor dem Setzen eines 
neuen Bildpunktes der alte y-Wert aus dem [Feld ] ausgelesen und der 
Bildpunkt  gelöscht wird.
Um auch die im vorherigen Durchgang berechneten vertikalen Linien zu 
löschen, erfolgt eine Berechnung der zu löschenden Bildpunkte jeweils an 
der Stelle x+1.
Mit dieser Verfahrensweise kann das Löschen des gesamten Bildschirmes 
(wie schon von einigen Beratern vorgeschlagen) entfallen und es erfolgt 
eine kontinuierliche Aufzeichnung des Kurvenzuges.
Besonderen Dank an Karl Heinz, der sich mit mir so viel Mühe gemacht 
hat.

Philipp Burch schrieb:
>Da könntest du wohl eher noch Probleme mit dem Speichern auf die SD-Karte
>bekommen, einzelne Schreibzugriffe sind da recht langsam.
Wie könnte man den Zeitbedarf für das Beschreiben eines Sektors einer SD 
karte abschätzen?

>Dann muss das Neuzeichnen halt in einem Interrupt passieren.
Wie muss man das verstehen?

Mit freundlichen Grüßen
Wolfgang

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.