Guten Abend, ich versuche gerade, die Adressierung der einzelnen Pixel bei einem SSD1306 zu verstehen, leider blicke ich da nicht ganz durch. Datenblatt dazu gibt es hier: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf Die effizienteste Speicherung wäre wahrscheinlich in einem uint8_t-Array mit der Länge 1024. Ich weiß, es gibt dazu genügend Beispiele / Libraries, aber ich würde es gerne selber verstehen (auch wenns vllt. länger dauert) da ich für das Display auch einen Font bauen möchte. Im "horizontal addressing mode" geht der Controller von links nach rechts und von oben nach unten. Wobei es nach rechts 128 Spalten und nach unten 8 Zeilen gibt. Daher nehme ich an, dass 8 Y-Werte in einem Byte gespeichert werden (dann kommt man wieder auf die 64-Pixel in der Höhe). Kann mir jemand einen Tipp geben, wie ich bei einer gegebenen x- und y-Koordinate auf den entsprechenden Index in meinem Array (uint8_t[1024])schließen kann?
meint du sowas?
1 | uint8_t array[1024]; |
2 | |
3 | int idx = 42; |
4 | |
5 | int x = idx % MODULE_SIZE_Y; |
6 | int y = floor(idx / MODULE_SIZE_X); |
rückwärts:
1 | int idx = x * MODULE_SIZE_X + y; |
Ich verstehe noch nicht, was du mit dem 1024er Array willst. Ich habe dir mal meine Library in den Anhang gepackt die ich grade diese Woche erstellt habe. Sie dient lediglich dazu, die schicken kleinen OLED-Displays als Ersatz benutzen zu können für die üblichen, alpha-numerischen Displays mit HD44780-Controllern, die ich bisher mit der Lib von Peter Fleury benutzt habe. Das Display kann natürlich noch viel mehr, daran werde ich mich vielleicht nächste Woche dran setzen und Zeichenroutinen dazu entwickeln. Ich habe mich bei dem Display für eine eigene Lib entschieden da mir die U8GLib und die von Adafruit u. a. viel zu groß waren, die brauchen weit über 4k byte, meine Lib braucht aktuell keine 1.4k byte. Vielleicht hilft es dir ja.
Icke schrieb: > meint du sowas? Ich bin mir nicht sicher, aber ich glaube, das passt nicht zum horizontalen Modus. Ich hab mir das im Kopf so vorgestellt (siehe Anhang). Horizontal hat man 128 Elemente (die Pixel). Vertikal dagegen nur 8 (da eines aus einem Byte also 8-Bit besteht, demnach werden in einem Array Element 8 y-Pixel gespeichert). Wenn man das in ein 1D-Array ausbreiten würde, sieht es in meiner Vorstellung so aus (siehe Anhang). Davon bekomme ich einen Knoten im Gehirn :( M. K. schrieb: > Ich verstehe noch nicht, was du mit dem 1024er Array willst. Du machst das anscheinend ohne Buffer, geht auch. Wie würdest du vorgehen, wenn du einen einzigen Pixel setzten müsstest?
:
Bearbeitet durch User
M. K. schrieb: > Ich verstehe noch nicht, was du mit dem 1024er Array willst. Per DMA im Hintergrund an den Controller übertragen? Wenn man nicht den lausigen I2C, sondern SPI nutzt, gibt es nichts Effizienteres.
Max M. schrieb: > Du machst das anscheinend ohne Buffer, geht auch. Wie würdest du > vorgehen, wenn du einen einzigen Pixel setzten müsstest? Wie du vielleicht gesehen hast übertrage ich immer 8 bit. Ich würde also an die entsprechende Page/Column springen und den Pixel zeichnen. Problem: Alle Pixel, die schon an der Stelle gesetzt sind, werden dann gelöscht. Bei SPI/I2C kommt man wohl nicht um den Buffer rum da man ja das RAM des Displays dabei nicht auslesen kann wenn ich das bisher recht gesehen hab. Also wenn man mehr als Schrift auf das Display bringen will brauchts wohl den Buffer.
> Also wenn man mehr als Schrift auf das Display bringen will brauchts > wohl den Buffer. Buffer empfiehlt sich immer. Du kannst dann naemlich in deinem Programm an beliebiger Stelle ohne Ruecksicht auf die Geschwindigkeit etwas ausgeben weil die Daten sehr schnell in den Buffer geschrieben werden. Die Weitergabe an das Display kann dann mit einem IRQ von sehr niedriger Prioritaet geschehen und natuerlich auch nur wenn sich etwas geaendert hat. Olaf
Olaf schrieb: > Buffer empfiehlt sich immer. Wofür ich meine Library schrieb steht oben. Ich komme ohne Buffer aus. Erkläre bitte warum sich auch da ein Buffer empfiehlt. In meinem Falle hielte ich ihn für mehr als überflüssig (deshalb hab ich ja auch keinen). Ein Buffer empfiehlt sich nur wenn man in einer Column einer Page lediglich ein einzelnes Pixel ändern will, die anderen aber so belassen will wie sie sind und man nicht die Möglichkeit hat, das Display auszulesen (wie es bei SPI/i2C beim SSD1306 der Fall ist). Max M. schrieb: > Wie würdest du > vorgehen, wenn du einen einzigen Pixel setzten müsstest? Ich hab mir mal Gedanken gemacht wie ich ein einzelnes Pixel im Buffer setzen kann und den zum Display übertragen kann (in Anlehnung meiner Lib)
1 | ...
|
2 | uint8_t displayBuffer[1024]; |
3 | for (uint16_t i = 0; i < 1023; i++){ |
4 | displayBuffer[i] = 0x00; |
5 | }
|
6 | ...
|
7 | void lcd_setPixel(uint8_t x, uint8_t y){ |
8 | displayBuffer[(uint8_t)(y / 8) * 128 +x] |= (1 << (y % 8)); |
9 | lcd_gotoxy_gfx(x, y); |
10 | lcd_send_i2c_start(); |
11 | lcd_send_i2c_byte(0x40); |
12 | lcd_send_i2c_byte(displayBuffer[(uint8_t)(y / 8) * 128 +x]); |
13 | lcd_send_i2c_stop(); |
14 | }
|
15 | ...
|
16 | void lcd_gotoxy_gfx(uint8_t x, uint8_t y){ |
17 | x = x; |
18 | lcd_send_i2c_start(); |
19 | lcd_send_i2c_byte(0x00); // 0x00 for command, 0x40 for data |
20 | lcd_send_i2c_byte(0xb0 + y); |
21 | lcd_send_i2c_byte(((x & 0xf0) >> 4) | 0x10); // | 0x10 |
22 | lcd_send_i2c_byte((x & 0x0f) | 0x01); |
23 | lcd_send_i2c_stop(); |
24 | }
|
25 | ...
|
Ich habs noch nicht getestet, hab grad das Display nicht zur Hand, aber so in der Richtung würde ich es wohl versuchen zu lösen...das gibt viel Spass noch nächste Woche...denke ich... ;)
> Erkläre bitte warum sich auch da ein Buffer empfiehlt.
Ich dachte das haette ich bereits getan. Nochmal einfacher...
Stell dir vor du hast einen Regler im Controller laufen, oder irgendwas
zeitkritisches in einem IRQ. Dann kannst du nicht einfach an beliebiger
Stelle mal printf aufrufen weil die Ausgabe an dein Display zu langsam
ist. Das kann im Normalbetrieb schon schlecht sein, besonders schlimm
ist es wenn du dir zu Debugzwecken mal irgendwo was ausgeben willst.
Olaf
M. K. schrieb: > Olaf schrieb: >> Buffer empfiehlt sich immer. > > Wofür ich meine Library schrieb steht oben. Ich komme ohne Buffer aus. > Erkläre bitte warum sich auch da ein Buffer empfiehlt. In meinem Falle > hielte ich ihn für mehr als überflüssig (deshalb hab ich ja auch > keinen). > Ein Buffer empfiehlt sich nur wenn man in einer Column einer Page > lediglich ein einzelnes Pixel ändern will, die anderen aber so belassen > will wie sie sind und man nicht die Möglichkeit hat, das Display > auszulesen (wie es bei SPI/i2C beim SSD1306 der Fall ist). Das ist nicht richtig. Nur wenn Speicherplatz ein Problem ist, empfiehlt sich ein Puffer. Dein Ansatz verbraucht erheblich mehr Rechenzeit als ein Ansatz mit Puffer und DMA. Flexibler und jederzeit erweiterbar ist letzterer sowieso. Daher kann man grundsätzlich zum DMA-/Puffer-Ansatz und nur im Falle von RAM-Knappheit Deinen Weg empfehlen.
Olaf schrieb: > Stell dir vor du hast einen Regler im Controller laufen, oder irgendwas > zeitkritisches in einem IRQ. Dann kannst du nicht einfach an beliebiger > Stelle mal printf aufrufen weil die Ausgabe an dein Display zu langsam > ist. Das kann im Normalbetrieb schon schlecht sein, besonders schlimm > ist es wenn du dir zu Debugzwecken mal irgendwo was ausgeben willst. Hast du dir mal meine Lib angesehen? Ich hab doch gar kein printf in Verwendung um auf das Display zu schreiben ;) Ein Buffer ist nur sinnvoll, damit man weiß was im Display steht. Wenn man das nicht wissen muss sehe ich keinen Sinn in der Verwendung eines Buffers bei dem hier genannten Displaytyp. Das wäre lediglich eine Verschwendung von Speicher. Jesus schrieb: > Nur wenn Speicherplatz ein Problem ist, empfiehlt sich ein Puffer. Quatsch, grade da empfiehlt sich kein Puffer denn grade der braucht ja Speicherplatz und wenn der knapp ist ist ein Puffer die denkbar schlechteste Lösung. Jesus schrieb: > Daher kann man grundsätzlich zum DMA-/Puffer-Ansatz und nur im Falle von > RAM-Knappheit Deinen Weg empfehlen. Selbst widersprochen? However, genau das ist doch das Ding bei mir. Ich hab im Atmega328 nunmal nur 2k RAM, soll ich da jetzt schonmal 1k nur für den Puffer, den ich nicht brauche, verschwenden?
:
Bearbeitet durch User
M. K. schrieb: > Ich hab mir mal Gedanken gemacht wie ich ein einzelnes Pixel im Buffer > setzen kann und den zum Display übertragen kann (in Anlehnung meiner > Lib) Danke für deine Antwort. Kannst du mir bitte erklären wie du auf das hier kommst:
1 | displayBuffer[(uint8_t)(y / 8) * 128 +x] |= (1 << (y % 8)); |
bzw. auf:
1 | lcd_send_i2c_byte(((x & 0xf0) >> 4) | 0x10); // | 0x10 |
2 | lcd_send_i2c_byte((x & 0x0f) | 0x01); |
Diese logischen Operationen erschließen sich mir nicht aus dem Datenblatt :(
:
Bearbeitet durch User
M. K. schrieb: >> Daher kann man grundsätzlich zum DMA-/Puffer-Ansatz und nur im Falle von >> RAM-Knappheit Deinen Weg empfehlen. > Selbst widersprochen? However, genau das ist doch das Ding bei mir. Ich > hab im Atmega328 nunmal nur 2k RAM, soll ich da jetzt schonmal 1k nur > für den Puffer, den ich nicht brauche, verschwenden? Ja, bei Dir ist das richtig. Aber Max MMM hat die Frage allgemein gestellt und nicht von einem bestimmten Controller geschrieben. Bei einem STM32 z.B. ist Dein Ansatz meistens der schlechtere.
> Hast du dir mal meine Lib angesehen? Ich hab doch gar kein printf in > Verwendung um auf das Display zu schreiben ;) Es ist irrelevant wie deine Funktion heisst. Du kennst mein printf ja schliesslich auch nicht. Der Punkt ist das die Ausgabe zum Display relativ langsam ist. Olaf
Hat nicht jeder Displaycontroller s.g. SRAM? Ist's nicht so bei jedem Controller, dass man zuerst die RAM Adresse setzen muß, bevor man Daten in den RAM (also Buffer) schreibt?
Olaf schrieb: > Es ist irrelevant wie deine Funktion heisst. Du kennst mein printf ja > schliesslich auch nicht. Der Punkt ist das die Ausgabe zum Display > relativ langsam ist. Der Punkt ist, dass du immer den ganzen Puffer zum Display schicken musst während ich immer nur das Byte zum Puffer schicke, dass ich grade geändert habe. Warum das hier jetzt der schlechtere Ansatz sein soll verstehe ich nicht. Mein uC muss nur 1 Byte (oder 2 oder 3 usw) zum Controller senden während die Puffer-Lösung immer alle 1024 Byte zum Controller schicken muss, d.h. mein Controller ist schon längst wieder im Tiefschlaf während dein Controller nicht mal ein Zehntel der Daten übertragen hat. Du hast dir entweder mein Lib gar nicht angeschaut oder du weißt gar nicht wie das Display arbeitet. Andy schrieb: > Ja, bei Dir ist das richtig. Aber Max MMM hat die Frage allgemein > gestellt und nicht von einem bestimmten Controller geschrieben. Deswegen schrieb ich ihm ja auch wie ich das Ganze gelöst habe für meinen Fall. Max M. schrieb: > Danke für deine Antwort. Kannst du mir bitte erklären wie du auf das > hier kommst: Klar. Es geht ja dabei den Pixel mit den Koordinaten x,y auf dem Display zu setzen. Der Puffer habe 1024 Byte, das entspricht 8 Zeilen mit jeweils 128 Columns. Der Puffer stelle alle Zeilen hintereinander da, sei also eindimensional. Wo x liegt ist recht einfach, das ist ja direkt die Column des Displays. Ich muss also nur raus bekommen, in welcher Zeile ich bin und das macht man indem man y durch die Zeilenhöhe teilt, das ist hier 8. Die Zeilennummer wird mit der Länge multipliziert und dann schlicht x hinzu addiert. Nehmen wir mal ein Beispiel: Wir wollen den Punkt 10,10 setzen. Schaun wir mal wo der läge: Die Column ist leicht, das ist ja direkt der x-Wert, also 10. Die Page/Zeile ist: (uint8_t) 10/8 = 1, also Zeile mit der Nummer 1, da das Display bei 0 beginnt ist das also die 2. Zeile In unserem Puffer beginnt die 2. Zeile bei Index 128 (Index 0 ist das 1. Byte, Index 1 das 2. usw.). 10. Column in der 2. Zeile muss demnach also das Byte mit dem Index 138 sein und (uint8_t)(10/8)*128+10 ergibt also 138. Das ist anscheinend richtig. Und welcher Pixel muss in der Column gesetzt werden? Auch einfach: Die Zeilen sind ja alle nur 8 Pixel hoch. Der Rest von y, der sich nicht mehr durch 8 teilen lässt, muss also der zu setzende Pixel sein: y % 8 liefert uns diesen Pixel. Und wenn wir alle anderen Pixel in dieser Column nicht ändern wollen nehmen wir eine ein, schieben sie an die Pixelstelle und verknüpfen die Column mittels oder, daher also das |= (1 << (y % 8) Max M. schrieb: > bzw. auf: > lcd_send_i2c_byte(((x & 0xf0) >> 4) | 0x10); // | 0x10 > lcd_send_i2c_byte((x & 0x0f) | 0x01); > > Diese logischen Operationen erschließen sich mir nicht aus dem > Datenblatt :( Damit wird eigentlich nur der Cursor an die Stelle der Zeile/Column gesetzt, in der der Pixel geändert werden soll. ;)
:
Bearbeitet durch User
> Controller senden während die Puffer-Lösung immer alle 1024 Byte zum > Controller schicken muss, Wie kommst du auf dieses duenne Brett? So sieht z.B bei mir der Buffer fuer ein SSD1306 aus: #define SEGMENTSIZE 32 typedef struct{ unsigned char segment[SEGMENTSIZE]; unsigned char dirty; } OLEDColumnType; #define PAGES 8 #define SEGMENTS 4 OLEDColumnType OLED_Buff[SEGMENTS][PAGES]; Rate mal wofuer 'dirty' ist. Und rate mal was passiert wenn die Verbindung zum Display kurz unterbrochen wird oder dessen Inhalt beschaedigt wird. .-) Aber ich muss dich nicht bekehren. Olaf
Jesus schrieb: > M. K. schrieb: > Ich verstehe noch nicht, was du mit dem 1024er Array willst. > > Per DMA im Hintergrund an den Controller übertragen? > Wenn man nicht den lausigen I2C, sondern SPI nutzt, gibt es nichts > Effizienteres. Sorry, ich habe nur eine kurze Zwischenfrage, ich will keinesfalls diesen Thread kappern. Ich habe dieses Display. Siehe Bild im Anhang. Könnte man dieses auch mit SPI betreiben?
Olaf schrieb: >> Controller senden während die Puffer-Lösung immer alle 1024 Byte > zum >> Controller schicken muss, > > Wie kommst du auf dieses duenne Brett? Gut, dann hast du immer Päckchen von 32 byte, immer noch mehr als bei mir. Den Speicher für den Puffer musst du dennoch vorhalten. Aber vielleicht zeigst du uns ja mal deine komplette Lib und wie sie funktiniert, dann wird mir vielleicht auch klar wie die funktioniert. Wie bekommst du eigentlich mit, dass der Inhalt des Displays beschädigt wird? In SPI/IIC gibts ja keine Infos vom Display. Alter Lateiner schrieb: > Jesus schrieb: >> M. K. schrieb: >> Ich verstehe noch nicht, was du mit dem 1024er Array willst. >> >> Per DMA im Hintergrund an den Controller übertragen? >> Wenn man nicht den lausigen I2C, sondern SPI nutzt, gibt es nichts >> Effizienteres. > > Sorry, ich habe nur eine kurze Zwischenfrage, ich will keinesfalls > diesen Thread kappern. Ich habe dieses Display. Siehe Bild im Anhang. > Könnte man dieses auch mit SPI betreiben? Ist es genau das vom Foto? Dann geht damit nur IIC. Die SPI-Variante hat 5 oder 6 Pins ;)
Alter Lateiner schrieb: > Könnte man dieses auch mit SPI betreiben? Der Controller selber kann das schon, es ist aber bei dem Flexkabel mit dem kleinen Pitch schwierig etwas zu ändern. Im Datenblatt https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf auf S.15 ist eine Tabelle mit den Belegungen. Am ehesten geht 3-Wire SPI, da musst du versuchen an CS dranzukommen, das ist für I2C ja fest auf GND gelegt. Es gibt viele Varianten des Breakoutboards, die bekommt man sogar einzeln und könnte das umlöten. Aber das würde ich mir nicht antun sondern gleich ein passendes neues bestellen, auf eBay findest du die reichlich.
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.