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.
Hallo Philipp, hast du zu deinem Vorschlag mal ein Programmbeispiel passend zum T6963C? dann wäre das Problem wahrscheinlich schon gelöst MfG Wolfgang
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.
skoll! Silvester ist wieder nahe, dinner for one... sollte scroll heißen
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
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); }
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; }
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; }
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.
Ist nur so eine Idee: Und wenn man kurzzeitig das Display von der Versorgungsspannung trennt, um den Inhalt zu löschen? Bernhard
> 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 >>>>>>>>>>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
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.