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
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.
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.
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
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.
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.
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.
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.
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....
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!
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.
@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.
>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.
"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.
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
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
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.
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
> 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!
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;
}
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
> 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.
> 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.
> 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.
@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> ;-)
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!
>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
>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...
> 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
// 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.
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
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