Ein kleines Beispiel (Version1) wie ein OLED2 SSD1306 in Assembler initialisiert werden kann. Ein ganz großes Dankeschön an Steffen, wichtige Routinen konnte ich von ihm nutzen, super! Beitrag "[ASM] SSD1306 text library für oled displays + AVR 0- und 1-Series" Der 8MHz getaktete ATmega, ohne viel Schnick-Schnack bedient dieses kleine Display. Ganz bewußt habe ich alle Vorgänge verlangsamt, SCL und der µC-Takt kann nach belieben verändert und das keine Programm (12% Flash, 1008 Byte) aufgebohrt werden. Besonderheit, der SSD1306 kann nur vertikale 8Bit Pixel darstellen. Horizontale vermutlich nicht. Um das gesamte Display zu löschen (clear) sind 1024 TWI/I2C Bytes zu schreiben. Bei 16MHz Takt, SCL 444,444kHz, gelingt das in 11,6ms. Sehen wir kurz nach Progammstart einen wunderschönen "Sternenhimmel", dann ist das schon ganz gut, der Bildspeicher des Displays hat einen undefinierten Zustand eingenommen. Erst durch beschreiben des z.B. Clear Display, ändert sich dieser Zustand. LED-grün: bereit LED-gelb: TWI Aktivität LED-rot: TWI Error, ev. Hardwareproblem, z.B. Problem mit dem Sklave, SCL zu hoch Anmerkung: Bei 400kHz SCL könnten ev. bei kurzen Anschlussleitungen keine zusätzlichen Widerstände (SCL, SDA) gegen Vcc erforderlich sein. Diesen Beitrag fand ich u.a. auch sehr interessant: Beitrag "SSD1306/1309 Library zum Darstellen von Text auf OLED Displays" Für Hinweise und konstruktive Vorschläge bin ich sehr dankbar. Bernhard
:
Bearbeitet durch User
Version2 mit einem ATmega328p, als Grundgerüst für weitere Projekte, er bietet mehr SRAM um einen 1.024Byte großen virtuellen Bildspeicher im SRAM zu ermöglichen, das schafft leider der ATmega8 nicht. Das Display wir in dieser Version nur gelöscht und anschließend wieder komplett beschrieben, sieht aus, als würde es blinken, s.Video^^
:
Bearbeitet durch User
Version3 - mit einem virtuellen 1.024 Byte großen SRAM-Bild-Speicher für Text und Grafic. - wird Byte0 des Bildspeichers mit ffh beschrieben entsteht eine horizontale 8 Pixel Linie oben links - ist diese Byte eine 01h entsteht ein Pixel in der äusersten linken oberen Ecke, mit diesm Verfahren lassen sich sehr einfach einzelne Pixel plazieren, Linien und Kreise zeichnen - im Timerinterrupt wird alle 100ms der Graficspeicher komplett übetragen, dauer ca. 50ms da einige Operationen notwendig sind um die Bits zu bearbeiten, damit eine horizontale Linie entsteht - der Graficspeicher muss nicht zwingend im 100ms Interrupt erfolgen - man könnte auch nur dann das Display refreshen, wenn eine Änderung im Bildspeicher vorliegt -die gewohnte Clear Diplay Funktion ist natürlich in der üblichen Form nicht mehr vorhanden, jetzt mussen wir den virtuellen Bildspeicher löschen^^
:
Bearbeitet durch User
Oh, das ist ja super zu sehen - du kommst weiter mit dem OLED Display. Bernhard S. schrieb: > - ist diese Byte eine 01h entsteht ein Pixel in der äusersten linken > oberen Ecke, mit diesm Verfahren lassen sich sehr einfach einzelne > Pixel plazieren, Linien und Kreise zeichnen Da bin ich aber mal gespannt. Das hat mir einiges an Hirnschmalz gekostet dies umzusetzen :) Gruß Steffen
Steffen H. schrieb: > Da bin ich aber mal gespannt. Das hat mir einiges an Hirnschmalz > gekostet dies umzusetzen :) Ja, das war nicht ganz ohne :-) Das "i" in "OLED_REFRESHi" steht für Interrupt-tauglich Prinzip: es werden imer 8 SRAM-Bytes geladen, 1 Bock mit 64 Pixeln, welche horizontal polarisiert, also graficfreundlich sind (temp1...8) in der "OLED_REFRESH_BLOCK_s" Routine erfolgt das bitweise zusammenstellen des neuen Sendebytes, also von horizontal auf vertikal Zuerst einsammeln Bit0 von temp1...8 im Register temp, anschließend temp senden dann Bit1 von temp1...8 und senden usw. und die Neuberechnung der Adresse für den SRAM
1 | OLED_REFRESHi: |
2 | rcall GO_HOME |
3 | ;------------------------------------------------------------------------------- |
4 | ldi temp1,(SSD1306_ADR) |
5 | rcall TWI_MT_START_TEMP1 |
6 | ;------------------------------------------------------------------------------- |
7 | ldi temp,(SSD1306_CONTROL_BYTE_DATA_STREAM) ; CONTROL Byte: DATA STREAM |
8 | rcall TWI_MT_TX_TEMP |
9 | ;------------------------------------------------------------------------------- |
10 | ldi ZL,low (adr_BILD_BEREICH+0) |
11 | ldi ZH,high(adr_BILD_BEREICH+0) |
12 | rcall OLED_REFRESH_ZEILE |
13 | ldi ZL,low (adr_BILD_BEREICH+1*128) |
14 | ldi ZH,high(adr_BILD_BEREICH+1*128) |
15 | rcall OLED_REFRESH_ZEILE |
16 | ldi ZL,low (adr_BILD_BEREICH+2*128) |
17 | ldi ZH,high(adr_BILD_BEREICH+2*128) |
18 | rcall OLED_REFRESH_ZEILE |
19 | ldi ZL,low (adr_BILD_BEREICH+3*128) |
20 | ldi ZH,high(adr_BILD_BEREICH+3*128) |
21 | rcall OLED_REFRESH_ZEILE |
22 | ldi ZL,low (adr_BILD_BEREICH+4*128) |
23 | ldi ZH,high(adr_BILD_BEREICH+4*128) |
24 | rcall OLED_REFRESH_ZEILE |
25 | ldi ZL,low (adr_BILD_BEREICH+5*128) |
26 | ldi ZH,high(adr_BILD_BEREICH+5*128) |
27 | rcall OLED_REFRESH_ZEILE |
28 | ldi ZL,low (adr_BILD_BEREICH+6*128) |
29 | ldi ZH,high(adr_BILD_BEREICH+6*128) |
30 | rcall OLED_REFRESH_ZEILE |
31 | ldi ZL,low (adr_BILD_BEREICH+7*128) |
32 | ldi ZH,high(adr_BILD_BEREICH+7*128) |
33 | rjmp OLED_REFRESH_ZEILE |
34 | ;------------------------------------------------------------------------------- |
35 | OLED_REFRESH_ZEILE: |
36 | rcall OLED_REFRESH_BLOCK |
37 | rcall OLED_REFRESH_BLOCK |
38 | rcall OLED_REFRESH_BLOCK |
39 | rcall OLED_REFRESH_BLOCK |
40 | rcall OLED_REFRESH_BLOCK |
41 | rcall OLED_REFRESH_BLOCK |
42 | rcall OLED_REFRESH_BLOCK |
43 | rcall OLED_REFRESH_BLOCK |
44 | rcall OLED_REFRESH_BLOCK |
45 | rcall OLED_REFRESH_BLOCK |
46 | rcall OLED_REFRESH_BLOCK |
47 | rcall OLED_REFRESH_BLOCK |
48 | rcall OLED_REFRESH_BLOCK |
49 | rcall OLED_REFRESH_BLOCK |
50 | rcall OLED_REFRESH_BLOCK |
51 | rjmp OLED_REFRESH_BLOCK |
52 | ;------------------------------------------------------------------------------- |
53 | OLED_REFRESH_BLOCK: |
54 | LD temp1,Z |
55 | adiw ZL,16 |
56 | LD temp2,Z |
57 | adiw ZL,16 |
58 | LD temp3,Z |
59 | adiw ZL,16 |
60 | LD temp4,Z |
61 | adiw ZL,16 |
62 | LD temp5,Z |
63 | adiw ZL,16 |
64 | LD temp6,Z |
65 | adiw ZL,16 |
66 | LD temp7,Z |
67 | adiw ZL,16 |
68 | LD temp8,Z |
69 | |
70 | sbiw ZL,60 ; -111 |
71 | sbiw ZL,51 |
72 | ;------------------------------------------------------------------------------- |
73 | ldi temp9,8 |
74 | OLED_REFRESH_BLOCK_s: |
75 | LSR temp1 |
76 | ROR temp |
77 | LSR temp2 |
78 | ROR temp |
79 | LSR temp3 |
80 | ROR temp |
81 | LSR temp4 |
82 | ROR temp |
83 | LSR temp5 |
84 | ROR temp |
85 | LSR temp6 |
86 | ROR temp |
87 | LSR temp7 |
88 | ROR temp |
89 | LSR temp8 |
90 | ROR temp |
91 | rcall TWI_MT_TX_TEMP |
92 | dec temp9 |
93 | brne OLED_REFRESH_BLOCK_s |
94 | ret |
:
Bearbeitet durch User
Bernhard S. schrieb: > Um das gesamte Display zu löschen (clear) sind 1024 TWI/I2C Bytes zu > schreiben. > > Bei 16MHz Takt, SCL 444,444kHz, gelingt das in 11,6ms. 11,6ms, da hast du dich wohl etwas vertan. Rein rechnerisch sind bei 1024Bytes zu je 8Bits bei einer Bitzeit von 1/444,444kHz keine 11,6ms drin. Das wären dann ohne Overhead (Startadresse,Controll Byte, Adressierungsmode, Startadressen.., usw.) minimal (1024*8)/444444 = 18,4ms ;) Ich habe mal 2 Animierte Bilder angehangen, wo die beiden Adressierungsarten verdeutlicht werden. Gruß Steffen
Steffen H. schrieb: > 11,6ms, da hast du dich wohl etwas vertan Oh ja, die SCL Frequenz stimmt nicht, sie ist wesentlich höher als 400kHz. Hatte die Bit-Rate auf NULL gesetzt, allerdings lt. Datenblatt nicht empfehlenswert, daher die 11,6ms. 1.024Bytes, bei einer noch zulässigen Bitrate von 10 und 16MHz Takt habe ich 22,6ms gemessen
1 | .equ TWI_BIT_RATE =0 ; Bit-Rate |
2 | .equ TWI_PRESCALER =0 ; Vorteiler: 0=1 1=4 2=16 3=64 |
3 | |
4 | ldi temp, TWI_BIT_RATE |
5 | out TWBR, temp |
6 | |
7 | ldi temp, TWI_PRESCALER |
8 | out TWSR, temp |
Gruß Bernhard
:
Bearbeitet durch User
Steffen H. schrieb: > Ich habe mal 2 Animierte Bilder angehangen, wo die beiden > Adressierungsarten verdeutlicht werden. angehangen, angeschalten, aufgebauen, aufgezeichnen, eingekaufen, frisch gewachsen, aufgeputzen, angekleben, eingebauen ....
Angehangen :-) habe ich ein 128x64 Blau-Gelb I2C/TWI 128x64 Display. https://www.roboter-bausatz.de/p/0.96-oled-display-blau-gelb-i2c-iic-twi-128x64-pixel-fuer-arduino Kennt jemand Bezugsquellen für 128x128 RGB I2C TWI OLED Display? z.B. mit SSD1331 https://de.aliexpress.com/item/4001256343574.html?src=google&albch=shopping&acnt=494-037-6276&isdl=y&slnk=&plac=&mtctp=&albbt=Google_7_shopping&aff_platform=google&aff_short_key=UneMJZVf&&albagn=888888&albcp=1705854617&albag=67310370915&trgt=743612850874&crea=de4001256343574&netw=u&device=c&albpg=743612850874&albpd=de4001256343574&gclid=EAIaIQobChMI9fy0nqmr7gIVZLR3Ch06ywctEAQYBSABEgIgavD_BwE&gclsrc=aw.ds
:
Bearbeitet durch User
Bernhard S. schrieb: > 1.024Bytes, bei einer noch zulässigen Bitrate von 10 und 16MHz Takt habe > ich 22,6ms gemessen Hallo Bernhard Das kommt schon eher hin. Das hab ich gestern nämlich auch geschafft. 500kHz fTWI.. Und das Display hat sogar noch mitgespielt. Bernhard S. schrieb: > Kennt jemand Bezugsquellen für 128x128 RGB I2C TWI OLED Display? > > z.B. mit SSD1331 Ich habe mir bei ebay mal ein 1,5Zoll RGB OLED mit SPI und SSD1351 geholt. Hat ca. 15€ gekostet. Die Adressierung ist bei diesem Display einfacher. https://www.ebay.de/itm/283708847757 Kommst du mit deiner Pixelgenauen Adressierung weiter? Ich habe deine Bit/Byte Schubserei nicht mehr folgen können :) Jadiedeutschesprache schrieb: > Steffen H. schrieb: >> Ich habe mal 2 Animierte Bilder angehangen, wo die beiden >> Adressierungsarten verdeutlicht werden. > > angehangen, angeschalten, aufgebauen, aufgezeichnen, eingekaufen, > frisch gewachsen, aufgeputzen, angekleben, eingebauen .... Darüber hatte ich kurz mal nachgedacht Ich fand es trollig
:
Bearbeitet durch User
Steffen H. schrieb: > 500kHz fTWI.. > Und das Display hat sogar noch mitgespielt. Und das ohne zusätzliche Widerstände gegen +5V, SDA und SCL am Display sind vermutlich relativ niederohmig. Eine Strommessung Display-SCL gegen GND ergab 0,7mA. R=U/I = 5V:0,7mA = 7k Widerstand, und wenn dann noch der Pull-UP des Pins aktiviert ist, sind wir bei ca. 5k. Sind die Leitungskapazitäten klein, dann halten sich die Signalverfälschungen in Grenzen. Oh, demnach können durchaus über 1,4mA Strom nur durch SCL und SDA fließen, die Energiebilanz sieht etwas wacklig aus. Steffen H. schrieb: > Kommst du mit deiner Pixelgenauen Adressierung weiter? Ist in Arbeit, ist aber kein Problem, bin noch mit der ASCII Darstellung beschäftigt. Steffen H. schrieb: > Darüber hatte ich kurz mal nachgedacht > Ich fand es trollig Wir dürfen nie nachdenken, wir müssen immer vordenken ;-) https://www.mikrocontroller.net/attachment/489878/OLED_RGB_SSD1351.jpg Ist leider SPI, TWI wäre mir lieber, benötigt weniger Anschlussleitungen, Greta würde sich feuen.
:
Bearbeitet durch User
Version4 mit ASCII Zeichensatz 8x12 und 16x8. Das Display wird nur refresht, wenn sich im Bildspeicher etwas ändert, spart Strom. Strombedarf im Bild 16x24 ca. 6mA bei 5V. Der Schriftfont ist in einer Tabelle hinterlegt und so konstruiert nach dem Motto, wie man es optisch sieht, so wird er auch auf dem Display dargestellt. Kann nach belieben einfach geändert werden. Leider sind die beiden Fonts sehr speicherintensiv, momentan sind schon über 54% des Flashs belegt :( Könnte man ev. in einem externen TWI-EEPROM ablegen :)
1 | ;-------------------------65 (h41) 'A' |
2 | .db 0b00000000,0 |
3 | .db 0b00111100,0 |
4 | .db 0b01000010,0 |
5 | .db 0b01000010,0 |
6 | .db 0b01000010,0 |
7 | .db 0b01000010,0 |
8 | .db 0b01111110,0 |
9 | .db 0b01000010,0 |
10 | .db 0b01000010,0 |
11 | .db 0b01000010,0 |
12 | .db 0b01000010,0 |
13 | .db 0b00000000,0 |
:
Bearbeitet durch User
Hier mal als Diskussionsbeitrag meine OLED SSD1306 / SH1106 Assembler Routinen als Teil eins RDA5807 Radios mit ATtiny45. - Thomas
Thomas N. schrieb: > ...RDA5807 Radios mit ATtiny45 Toll, Respekt! Hättest Du noch eine Schaltung für uns? Mir viel gleich eine Besonderheit auf, Du verwendest keinen virtuellen Bildschirmspeicher im SRAM, sondern konstruierst die Grafic direkt im Display. Wo konntest Du dieses Radiomodul beziehen? Wie sind die Empfangseigenschaften, besonders bei RDS-Empfang? Was genau wird mit der "Initab" im OLED eingestellt? Bernhard
:
Bearbeitet durch User
Anbei die Schaltung (geht aber eigentlich aus dem Listing hervor). Die RDA5807 Module hatte ich vor ca. 1 1/2 Jahren via EBay erworben. Da findet man auch hiesige Lieferanten. Die Module haben eine hohe Empfangsempfindlichkeit und liefern eine überraschend gute Klangqualität. Von den RDS Signalen habe ich den Radiotext als Lauftext und die Uhrzeit decodiert (wie in dem Foto vom 21.01.2021 erkennbar ist). Außerdem wird bei Verkehrsfunk Durchsagen ein "!" eingeblendet. Die doppelte Zeichengröße leite ich aus einer Tabelle durch Wiederholung der Pixel ab. Eigentlich erlaubt die RDS Spezifikation EN50067 auch Fehlererkennung- und Korrektur der Datenübertragung. Meine Implementierung liefert aber nur die ungefilterten Rohdaten, was meist ganz brauchbar ist. Leider ist das englische Datenblatt an den entscheidenden Stellen hier unvollständig und aus dem chinesischen Programming Manual bin ich erst recht nicht schlau geworden ;) Ich vermute, dem RDA5807 fehlen hierzu auch einige Register. Da ist man mit dem Si4703 besser bedient. Hier habe ich noch ein paar weitere Erläuterungen: http://tneveling.bplaced.net/radiobasteln.html
Thomas N. schrieb: > Hier mal als Diskussionsbeitrag meine OLED SSD1306 / SH1106 Assembler > Routinen als Teil eins RDA5807 Radios mit ATtiny45 Ein sehr schönes Projekt. Vielen Dank dafür. Ich hab es mir soeben auf meine Liste geschrieben. Ich mag auch deinen Programmierstil. Sehr übersichtlich und nachvollziehbar. Wie du das mit der doppelten Schriftgröße machst find ich gut. Deine Multiplikation sehr schön gelöst. Mach ich auch lieber so wenn es eine 2^x Multiplikation ist. Wenn ich das richtig sehe hast du eine repeat Funktion in der Tastenabfrage integriert. Da du ja nur 3 Tasten verwendest, kannst du da mal bitte die einzelnen Funktionen (Menu) der Tasten zusammenstellen? Über Mode zeigst du ja die verschieden Menüs an, oder? Toll gemacht! Bernhard S. schrieb: > Mir viel gleich eine Besonderheit auf, Du verwendest keinen virtuellen > Bildschirmspeicher im SRAM, sondern konstruierst die Grafic direkt im > Display. Wenn man den Text oder was auch immer an Grafik immer in den 8Bit einer Spalte abbilden kann, wozu dann einen virtuellen Speicher? Ich habe diesen nur eingeführt wenn man echte Grafik zeichnen will. Denn dann muss man Pixelweise adressieren können und da wird es bei den Displays unmöglich ohne virtuellen Abbild. Denn man kann den Displayspeicher der kleinen OLED's nicht lesen! Und genau da beginnt die Pixel und Bit Schubserei.. Siehe hier: Beitrag "Re: [ASM] SSD1306 text library für oled displays + AVR 0- und 1-Series" Versuche mal etwas anders - universeller - an die Textgenerierung heran zu gehen. Jetzt gerade baust du für jede Schrift/Font ein riesiges Konstrukt um diese in den virtuellen Speicher zu bringen. Bitte nicht falsch verstehen Bernhard. Das soll keine Kritik sein, nur ein Hinweis. Gruß Steffen
Steffen H. schrieb: > Versuche mal etwas anders - universeller - an die Textgenerierung heran > zu gehen. Jetzt gerade baust du für jede Schrift/Font ein riesiges > Konstrukt um diese in den virtuellen Speicher zu bringen. Ok, ich suche nach einer Lösung :-) Eventuell hilft eine Funktion "PSET_XY". Momentan sind die Font-Tabellen so aufgenaut, daß sie einfach geändert werden können. So wie ein Datensatz in der Tabelle sichtbar ist z.B. "C", so wird er auch auf dem Display dargestellt und genau das macht den Assemblercode komplizierter mit der "ASCII_BITUMKEHR_TEMP", Zeilen und Spalten müssen auch gezählt werden. > Wie du das mit der doppelten Schriftgröße machst find ich gut. Deine > Multiplikation sehr schön gelöst. Eine Tolle Idee, den gerade mein Font mit doppelter Schriftgröße klaut viel Flash. Ich schau mir das Schriftbild mit der Schriftverdopplung mal genauer an. Bernhard
:
Bearbeitet durch User
Bernhard S. schrieb: > Eine Tolle Idee, den gerade mein Font mit doppelter Schriftgröße klaut > mit viel Flash. Man darf nicht vergessen, Thomas hat das alles in ein ATtiny45 bekommen. 4k Speicher und 128bit RAM !!! Respect nochmal dafür..
Version5 mit Punkt setzen und löschen XY. Schaut Euch mal bitte die "PUNKT_SET_XY" Routine an, ob man die noch schneller machen kann, hier kommt es auf jeden Takt an, sonst dauert das Zeichen der von Figuren, wie Zeiger, Kreise, gefüllte Rechtecke ziemlich lange. Bedingung die Register XYZ düfen nach verlassen der Routine nicht verändert werden, nur temp(R16),R0 und R1. Im Bild "B2" ist im Hintergrund ein Sternenhimmel zu sehen, per ZUfallsgenerator werden Punkte gesetzt und gelöscht, sieht putzig aus :-) Steffen H. schrieb: > Thomas hat das alles in ein ATtiny45 bekommen Dafür hat er auch von mir einen dicken Pluspunkt bekommen, das hat er sich verdient. Nachtrag: push ZL, push ZH konnte ich ersetzen durch MOVW ZL_KOPIE,ZL. Kostet allerdings 2 wertvolle Register :(
:
Bearbeitet durch User
Ich bin noch ein paar Antworten schuldig geblieben: >@Bernhard: Was genau wird mit der "Initab" im OLED eingestellt? Wenn man sich auf die Einstellungen beschränkt, die nicht schon beim Reset als Standard definiert sind, kommt man mit 5 Byte zur Initialisierung aus! 0x8D 0x14 Ladungspumpe aktivieren 0xAF Display einschalten 0xA1 Column 127 wird auf Segment 0 gemappt 0xC0 Normal Scan Direction >@Steffen: Tasten & Menues Die "Mode" Taste (nicht repetierend) schaltet zwischen "Volume" (Lautstärke), "Mute" (Stummschaltung) und "Seek" (Sendersuche) um. Ein entsprechender Text in der Mitte des Displays zeigt die aktuelle Einstellung. Mit den Tasten "up" und "down" kann im Modus "Volume" die Lautstärke geändert werden (repetierend mit Balkenanzeige) Im Modus "Seek" wird die Sendersuche aufwärts / abwärts gestartet (nicht repetierend). Der zuletzt eingestellte Sender wird im EEPROM gespeichert und steht beim nächsten Einschalten wieder zur Verfügung.
@Thomas ich schau mir gerade Deine Font-Tabelle an (8x8 Pixel), so sieht nach meiner Meinung Dein "A" theoretisch mit Bitverdopplung aus 8x16. Kannst Du uns mal bitte ein Foto mit einem "A" senden. Welchen Font-Generator verwendest Du? Danke
:
Bearbeitet durch User
@Bernhard Zeichensatz und hierzu passende OLED Init Sequenz wurde aus Beitrag "(sehr) kleines "FrameWork" für ATtiny44" (in C codiert) abgeleitet. Ich habe das Ganze zunächst auch in C auf einen ATtiny85 portiert und dann in mehreren Iterationen auf Assembler umgestellt, den RDS Decoder ergänzt etc. Die doppelte Zeichengröße ist aber auch in dem ursprünglichen C-Code schon vorzufinden. Die Pixel werden sowohl horizontal als auch vertikal dupliziert, so dass aus dem 8x8 Zeichen aus der Font Tabelle ein 16x16 Zeichen entsteht (siehe Foto von dem gewünschten "A"). Damit ich da selbst später noch durchblicke hatte ich damals eine kleine Doku über die OLED Ansteuerung und die Verdopplung der Zeichengröße erstellt, die vielleicht weitere Fragen beantwortet.
Bernhard S. schrieb: > Könnte man ev. in einem externen TWI-EEPROM ablegen :) Also da hätte ich noch eine Idee, wenn schon ein weiterer Chip im Gespräch ist, wie wäre es mit einen kleinen Attiny. Damit könnte gewissermaßen ein intelligentes Display gebaut werden. Also in etwa so: Auf die Displayplatine kommt ein Tiny der sich nur um die Oled-Steuerung kümmert. Dieser wiederum kann per I2C von einen anderen µC angesprochen werden. Dafür gibt es dann eine Befehlsliste, wie OLED_Init; SET_Curser....
Bernhard S. schrieb: > OLED_REFRESH_ZEILE: > rcall OLED_REFRESH_BLOCK > rcall OLED_REFRESH_BLOCK Und beim naechsten mal lernen wir das voellig neue Konstrukt einer Schleife ... SCNR, leo
Bernhard S. schrieb: > Schaut Euch mal bitte die "PUNKT_SET_XY" Routine an, ob man die noch > schneller machen kann, hier kommt es auf jeden Takt an, sonst dauert das > Zeichen der von Figuren, wie Zeiger, Kreise, gefüllte Rechtecke ziemlich > lange. Hallo Bernhard Ich hab es versucht - blicke aber bei deinem Algorithmus zur Adressierung nicht durch. Kann das sein, dass du das alles sehr kompliziert machst weil du dich so sehr auf die einfach ersichtliche Datenabbildund deiner Fontdaten konzentrierst? In welchem Adressoerungsmode schreibst du denn die Daten zum Display? Verikal oder Horizontal? Bedenke bitte, dass selbst wenn deine Daten im RAM schön in - ich sag mal Reihe - Byte1,Byte2,..,ByteX im RAM liegt, kann es doch im Display trotzdem nur Senkrecht und nicht - wie von dir erzwungen - waagerecht abgebildet werden. Dies alles verkompliziert, meiner Meinung nach, die ganze Adressierung. Denn wenn du ein Byte mit mehreren gesetzten Bits (Pixeln) hast rechnest du diese irgendwie erst in Byte|Bit um und setzt jedes Bit aus dem Ursprungsdatenbyte dann einzeln im RAM. Das dauert doch alles viel zu lange. @Thomas Heute ist der Radio Empfänger gekommen. Bin voll in Bastellaune :)
@alle hat jemand eine Idee, wie man die Font-Tabelle komprimieren kann, um wertvollen Speicher zu sparen? Thomas N. schrieb: > eine kleine Doku über die OLED Ansteuerung und die Verdopplung der > Zeichengröße erstellt Es kommt nicht immer auf die Größe an, sehr gut gelungen, Daumen hoch! Einen kleinen selbsgefummelten Visual-Basic Fontgenerator nutze ich gern. leo schrieb: >> rcall OLED_REFRESH_BLOCK >> rcall OLED_REFRESH_BLOCK > Und beim naechsten mal lernen wir das voellig neue Konstrukt einer > Schleife ... Hat Gründe. z.B. Schnelligkeit und Registerersparnis um pushen und popen zu ersparen, Vorbereitung auf Graficfähigkeit Frank D. schrieb: >> Könnte man ev. in einem externen TWI-EEPROM ablegen :) Oder in einem internen EEPROM, ca.1k müsste er groß sein. > Also da hätte ich noch eine Idee, wenn schon ein weiterer Chip im > Gespräch ist, wie wäre es mit einen kleinen Attiny. Damit könnte > gewissermaßen ein intelligentes Display gebaut werden. Es wäre gut, wenn er eine Harware TWI besäße, ausgangsseitig kann das Display mit einer Software TWI angesteuert werden. Für ASCII-Darstellung wäre das eine gute Idee. Grafic, so wie in einem sehr teuren TFT: Beitrag "Spektrumanalysator Frequenzspektrumanalysator Frequenzspektrometer Speki Wobbelgenerator TFT Atmega8" Ein ATmega328p hätte genügend SRAM, um derartige Figuren zu zeichnen. Steffen H. schrieb: > Heute ist der Radio Empfänger gekommen. Bin voll in Bastellaune :) Wo konntest Du ihn bestellen? Ich wäre auch gern in derartiger Bastellaune :) Steffen H. schrieb: > Ich hab es versucht - blicke aber bei deinem Algorithmus zur > Adressierung nicht durch. Nicht schlimm, das ist auch nicht ganz einfach zu durchschauen, gebe ich echt zu, schlussendlich sollen solche Figuren entstehen: Beitrag "NF Spectrum Analyzer selber bauen Eigenbau TV FBAS ATmega1284p Assembler low cost" > Kann das sein, dass du das alles sehr > kompliziert machst weil du dich so sehr auf die einfach ersichtliche > Datenabbildund deiner Fontdaten konzentrierst? Ja, die zu entstehende Grafic z.B. schräge Linien, muss später schnell errechenbar sein, sonst dauert die Figurendarstellung zu lange, das bereitet wenig Freude. > In welchem Adressoerungsmode schreibst du denn die Daten zum Display? > Verikal oder Horizontal? Die Daten werden Vertikal geschrieben, aber horizontal im SRAM abgelegt. Das Konstrukt rcall OLED_REFRESH_BLOCK rcall OLED_REFRESH_BLOCK rcall OLED_REFRESH_BLOCK ... dient dazu das horizontale flink in das vertikale umzuwandeln und zu senden. Es werden immer 8x8Pixel in einem Block zusammengefasst und dann dieser Block mit 8Bytes gesendet, so das Prinzip. > Bedenke bitte, dass selbst wenn deine Daten im RAM schön in - ich sag > mal Reihe - Byte1,Byte2,..,ByteX im RAM liegt, kann es doch im Display > trotzdem nur Senkrecht und nicht - wie von dir erzwungen - waagerecht > abgebildet werden. Dies alles verkompliziert, meiner Meinung nach, die > ganze Adressierung. Ja, Du hast Recht. Wenn Du ein 8Pixel breiten horizontalen Strich darstellen möchtest, brauchst Du nur ein SRAM Byte auf ffh setzen, den Rest erledigt die seltsam ausschauende "LED_REFRESH_BLOCK" Routine, die flott sein muss. > Denn wenn du ein Byte mit mehreren gesetzten Bits (Pixeln) hast rechnest > du diese irgendwie erst in Byte|Bit um und setzt jedes Bit aus dem > Ursprungsdatenbyte dann einzeln > im RAM. Das dauert doch alles viel zu lange. Das erledigt alles die "LED_REFRESH_BLOCK" Routine, der gesamte Display Inhalt wird in 128 Blöcke unterteilt (8x8 Pixel), jeder Block besteht aus 8 vertikalen Bytes die an das Display gesendet werden. Ich find Thorstens Font, gestreckt auf 8x16, auch ganz schick^^ Somit passen 16 große schlanke Asciis auf eine Zeile, so wie die pummeligen^^
:
Bearbeitet durch User
@Thorsten könntest Du, wenn's keine große Umstände macht, die Bits in Deinem Font tauschen Bsp: 0b00000001 ---> 0b10000000 Momentanes Bitgefummle:
1 | clr temp |
2 | sbrc temp1,0 ;Skip if Bit in Register Cleared |
3 | ori temp,0b10000000 |
4 | sbrc temp1,1 ; |
5 | ori temp,0b01000000 |
6 | sbrc temp1,2 ; |
7 | ori temp,0b00100000 |
8 | sbrc temp1,3 ; |
9 | ori temp,0b00010000 |
10 | sbrc temp1,4 ; |
11 | ori temp,0b00001000 |
12 | sbrc temp1,5 ; |
13 | ori temp,0b00000100 |
14 | sbrc temp1,6 ; |
15 | ori temp,0b00000010 |
16 | sbrc temp1,7 ; |
17 | ori temp,0b00000001 |
Danke PS: die 3 Schriftgrößen, generiert aus einem Font, sehen vernünftig aus, finde ich.
:
Bearbeitet durch User
@ die Radiobastler: Hier noch ein kleines Update meines Assembler Codes zur Korrektur der Zeitzone beim RDS Decoder. Wie gesagt hatte ich das Ganze zunächst in C programmiert und erst danach in Assembler. Meinen C-Code findet ihr hier: Beitrag "Re: DIY UKW-Receiver mit Padauk PFS154 und RDA5807" Läuft auf der gleichen Hardware und passt immer noch in einen ATtiny45.
Bernhard S. schrieb: > Steffen H. schrieb: >> Heute ist der Radio Empfänger gekommen. Bin voll in Bastellaune :) > > Wo konntest Du ihn bestellen? > > Ich wäre auch gern in derartiger Bastellaune :) Tja, schön wäre es gewesen, aber das Modul funktioniert nicht. Echt schade. Ich hab es geahnt als ich es ausgepackt habe. Da war schon Lötzinn an dem Vcc und GND Anschluss. Da konnt ich es mir schon denken. Der Empfänger gibt immer ein NACK zurück. Ist also nicht ansprechbar. Ich hab dem Verkäufer heute eine Mail geschickt. Er will mir umgehend ein Ersatz schicken. Na dann heißt es wieder warten.. @Bernhard: Das RAD5807M Modul hatte ich bei ebay bestellt. Es gibt da mehrere Anbieter in Deutschland, die diese Vorrätig haben für wenig Geld (ca. 4€, Versand kostenlos im Brief). https://www.ebay.de/itm/Mini-I2c-Stereo-FM-Radio-Modul-RDA5807M-RRD-102V2-0-fur-Arduino-Raspberry-Pi-DIY/171832050685?_trkparms=ispr%3D1&hash=item2801fd03fd:g:GNoAAOSwsB9WCNIe&amdata=enc%3AAQAFAAACcBaobrjLl8XobRIiIML1V4Imu%252Fn%252BzU5L90Z278x5ickkSG%252BOFgrj2Yvbvmrj2TAdMztoD3pA0tr5%252BY6r7O%252Bh2XECOSTrjQBwSB2WWLr3pknDhiZt3rJXljxUPZZXgQ8UeKw%252BX35o8Wgvl9jjRTqurYxHkyijnbElUK1EQjpNXSxiZWo1zqnm2RDjZ3O4NrXNh%252Bk%252FrJF6jYvV25hnpHzFAoJaKOSaUBYvHiJh6GM3i6cStYzUep2Qodf4D5KCW3ZpD669VthtV32Bi6pee%252FORcs%252FxDqCLDfgFb0Mdq8e1DbdPATjORg5U21JTCi9geTapWw3pMRj30UvuSMx1KPmozluPhdfciev8xU74LyWJ1aOnp4iExP3AYTpqBCbUi5jqObyWGgcpfGv69d4AUPGHN1RBafYbbb913GocQpaqD1HRH8WXDSWVM7NvR6efh1XouNFLRs%252FJhDvjbsR72K5im7nbozIR6tl5M6AJw9OrjHBQxSk7priHp%252BKXjymiEfk%252BTxown1E%252BTehI8gA5XwNrZ5tiZ%252B%252FHsLFHGEpe8ZYbIfR7QW3lYY%252FSDuP7rOSnOq%252Bf8XlEdY7%252BA8Iu7sTwJQvPyx6%252BZgOKXKvrVPO7xKjUi5hHK3VWafv6NbnL46z3ADGae7s%252Fkl4W5mQQBpx54RCrL2PQASiU52%252BZSR1Ff3Z03jrgFOk6kc0cbH6r7Ija8ag0lg%252FWSPca3XRtzSLuhTNsJ0fS%252FkjQD2WlcnbAOVCjpijr5fDM1Y65coXF%252Fp8FX7DuD4TPHS1bv%252FOsc83qOj8UKf0MWUsDNuP2LulGHUSJGxghMrQcrwhDmlbJo4A0aemLYQ%253D%253D%7Ccksum%3A1718320506851f309b3836904b2b946f2ff38d80a274%7Campid%3APL_CLK%7Cclp%3A2334524 oder https://www.ebay.de/itm/RDA5807-FM-Radio-Empfanger-Modul-V2-0-I2C-ESP8266-ESP32-STM-32-Arduino-Raspberry/284124772008?hash=item422727f6a8:g:7fUAAOSwhadf4yAX
Bernhard S. schrieb: > @Thorsten > > könntest Du, wenn's keine große Umstände macht, die Bits in Deinem Font > tauschen Bsp: 0b00000001 ---> 0b10000000 Du reitest dich immer tiefer rein und verkomplizierst alles. Oha, ich glaub das Wort gibt es gar nicht, aber egal. Ich sagte schon, du musst umdenken. Wenn du erst eine Linie oder sogar einen Kreis nach Bresenhan zeichnen willst wird das ganze Ausmaße annehmen und langsam werden. Bernhard S. schrieb: > PS: die 3 Schriftgrößen, generiert aus einem Font, sehen vernünftig aus, > finde ich. Find ich auch. @Thomas -> Danke für deine Codes
:
Bearbeitet durch User
Steffen H. schrieb: > Du reitest dich immer tiefer rein und verkomplizierst alles. Du hast bestimmt für den Grafic-Modus bessere Ideen, lass es uns wissen :-) > Wenn du erst eine Linie oder sogar einen Kreis nach Bresenhan > zeichnen willst wird das ganze Ausmaße annehmen und langsam werden. Ja, es wird langsamer, aber möglich, eine Uhr mit Sekundenzeiger ist der Prüfstein, die alte Zeigerstellung muss berechnet und gelöscht, die neue Zeigerstellung berechnet und gezeichnet werden und so ganz nebenbei ein FBAS-Bild generiert werden, ein 22MHz getakteter AVR kommt dabei ganz schön in's Schwitzen.
:
Bearbeitet durch User
Version6, eine kleine Spielerei^^ B0 Begrüßung B1 verfügbare Schrift-Fonts Font0, Font1, Font2 B2 TWI-Scanner, momentan nur h3c (Display am Bus) B3 Zeit für ein Display-Clear B4 Uhrzeit, Zeit für diese Anzeige, Betriebsspannung, Frequenzzähler B5 oh Schreck, der ATmega8 ist schon zu 48% belegt Die größeren Fonts sind etwas rechenintensiver, 32 Bytes SRAM werden benötigt. Zuerst wird der obere Teil des ASCII-Zeichens (8 oder 16Bytes) gezeichnet, anschließend der untere. Hat den Vorteil, dass nicht ständig die zeitintensive "OLED_GOTO_XY" aufgerufen werden muss. Ein gut gefülltes Display s. "B4" benötigt ca, 84ms. Könnt Ihr mal bei Euch nachmessen? Wenn die Bitrate auf die nicht empfehlenswerte 1 gesetzt wird sind es nur noch 48ms. PS: Momentan versuche ich den Code für schnelle Darstellung zu optimieren.
:
Bearbeitet durch User
Bernhard S. schrieb: > Ja, es wird langsamer, aber möglich, eine Uhr mit Sekundenzeiger ist der > Prüfstein, die alte Zeigerstellung muss berechnet und gelöscht, die neue > Zeigerstellung berechnet und gezeichnet werden und so ganz nebenbei ein > FBAS-Bild generiert werden, ein 22MHz getakteter AVR kommt dabei ganz > schön in's Schwitzen. Da hast du dir aber ganz schön was vorgenommen. Wegen der Uhr, wenn man genug RAM an Bord hat kann man mit mehreren Layern arbeiten. Das Ziffernblatt kann man in einem Layer halten und die Zeiger dann in einem Anderen. Falls du dich fragst, Warum? Der Gedanke dahinter ist folgender: Wenn du den Zeiger zum Beispiel über das Ziffernblatt oder eine Ziffer zeichnest (Sekundenzeiger), dann ist das ja kein Problem. Löscht du diesen aber nun um den nächsten zu zeichnen, dann löscht du auch dein Ziffernblatt oder Ziffern an der Stelle wo vorher der Zeiger war. Bernhard S. schrieb: > Du hast bestimmt für den Grafic-Modus bessere Ideen, lass es uns wissen > :-) Ich hab das schon alles hinter mir. Wie schnell meine Routinen bei einem einzelnen Pixel sind, hab ich noch nicht verifiziert. Aber den Schriften bin ich schnell und sehr flexiebel. Die Fonts sind gepackt. Das bedeutet zwar mehr rechnen aber das geht schon. Dabei bediene ich mich der Tatsache, das verschiedene Zeichen verschiedene Pixelweiten haben und somit nicht mit konstanter Datenbreite pro Zeichen als Fontdaten gespeichert werden muss. Zum Beispiel: Ein "I" ist schmaler als ein "W". Außerdem benötigt man eher selten den kompletten ASCII Zeichensatz (256 Zeichen). Ich fange zum Beispiel erst beim Leerzeichen (0×20) an und dann bloß 96 Zeichen bis zum Charter 127 (~). Das spart Platz im Speicher. Dazu gibt es dann zu jedem Font einen Daten Header (Kopf) wo die feste Pixelhöhe der Zeichen, den Charcode des ersten Zeichens der Chardaten und die Anzahl der Zeichen für die Daten vorliegen. Bevor jetzt die eigentlichen Daten der Zeichen kommen, ist da noch ein Datensatz zu den Pixelbreiten jedes einzelnen Zeichens. Also bei 96 Zeichen sind das dann auch 96 Bytes. Danach beginnen die Daten für die einzelnen Zeichen. Ich könnt noch mehr darüber schreiben, aber das wäre dann wohl eher was für eine Bachelor Arbeit oder einen Artikel hier im Forum. Ich hoffe ich habe dich/euch nicht verwirrt.
@Bernhard Aber die 3 verschiedenen Schriftgrößen aus ein und demselben Datensatz sieht gut aus. Vielleicht werde ich auch noch eine abgespeckte Textversion meiner Lib bauen. Übrigens, wenn du deine RAM Variablen mal anders deklarieren würdest, dann siehst du auch deinen RAM Verbrauch.. Statt:
1 | .equ adr_DATEN = SRAM_START+0 |
2 | .equ adr_ss10 = SRAM_START+10 |
3 | .equ adr_ss = SRAM_START+11 |
gibt man einfach die Anzahl zu reservierender Bytes der Daten an. Der Name bleibt erhalten. Und zwar so:
1 | .dseg |
2 | .org SRAM_START |
3 | adr_DATEN: .BYTE 10 ; reserviere 10 Bytes im SRAM |
4 | adr_ss10: .BYTE 1 ; reserviere 1 Byte |
5 | adr_ss: .BYTE 1 ; reserviere 1 Byte |
6 | .... |
7 | .... |
8 | .org SRAM_START+99 |
9 | adr_SRAM_CHECK_A: .BYTE 1 |
10 | adr_BILD_BEREICH: .BYTE 1024-1 |
11 | adr_BILD_BEREICH_ENDE: .BYTE 1024 |
12 | adr_SRAM_CHECK_B: .BYTE 1 |
13 | |
14 | .org SRAM_END |
15 | adr_SRAM_CHECK: .BYTE 1 |
Das sind alles Hilfen des AVR Assemblers.. MfG Steffen
@Bernhard Sag mal, bist jetzt doch wieder zurück auf einen ATmega8 gewechselt??? Laut deinem letztem Post habe ich einen ATmega328 verwendet und nur Fehlermeldungen bekommen. Dann hab ich entdeckt, dass du da einen Mega8 eingebunden hast und du alles nicht mehr über ein im RAM angelegten virtuellen Displayspeicher machst. Wie gesagt, bei Schrift mit vordefinierter Zeichenhöhe von 8,16,24,32,.. Pixeln geht das noch. Bei Linien und anderen grafischen Funktionen bin ich schon gespannt wie du das machen willst. Ich hab dein letzten Post (Version6) für einen ATmega328 umgestrickt und getestet. Läuft gut. Auch wenn die Error LED ständig leuchtet. Warum auch immer.. Ich habe auch die Zeit zum Display löschen mit einem LA kontrolliert. 45,79ms hab ich gemessen. Stimmt also. Was ich auch noch nicht verstanden habe sind deine Variablen SRAM_CHECK_ und _BILD_BEREICH_ENDE ? Wozu sind die gut? MfG Steffen
:
Bearbeitet durch User
Hier noch die geänderten Dateien
Version-7, Achtung wieder ATmega328p ASCII, Punkte, Linien, gerade und schräge. B0 freundliche Begrüßung mit Helligkeitsänderung B1 Zeitmessung für OLED-Clear B2 Zeitmessung für 1s Wait, die 1ms Timerinterrupts bremsen die Warteschleifen aus B3 Linienbeispiele B4 Zeit, Zeit Bildberechnung mit Refresh des Displays Betreibsspannung, OLED 1Bytes auslesen Nun macht sich der "seltsame" Aufbau des SRAMs bezahlt, die Berechnung der Linien ist relativ einfach. Die Koordinaten für X1, Y1, X2, Y2 sind in den Registern R2 bis R5 hinterlegt. Linienberechnung: Zuerst wird untersucht, ob es senkrechhte oder waagerechte Linen sind, diese lassen sich sehr flott berechnen. Bei den schrägen linien wirds komplizierter, hier untersuche ich den Anstieg, ob positiv oder negativ. Anschließend wird die Linie 2x gezeichnet, beim ersten Durchgang wird X hochgezählt und Y berechnet, beim zweiten Y hochezählt und X berechnet, damit auch sehr flache und sehr steile Linien ohne Lücken gezeichnet werden, sonnst sieht etwas komisch aus. Steffen H. schrieb: > Was ich auch noch nicht verstanden habe sind deine Variablen > SRAM_CHECK_ und _BILD_BEREICH_ENDE ? > Wozu sind die gut? SRAM_CHECK dient zur Programmüberwachung, wenn dieses Byte überschrieben wird, warum auch immer, blinken die LEDs wie wild. BILD_BEREICH_ENDE... Dummy, keine Bedeutung mehr > Übrigens, wenn du deine RAM Variablen mal anders deklarieren würdest, > dann siehst du auch deinen RAM Verbrauch.. Da hast Du Recht, gib mir angemessen Zeit, um mich daran zu gewöhnen, danke für den Tipp > Dann hab ich entdeckt, dass du da einen Mega8 > eingebunden hast und du alles nicht mehr über ein im RAM angelegten > virtuellen Displayspeicher machst. Und nun ist es wieder ein ATmega328p. > Wegen der Uhr, wenn man genug RAM an Bord hat kann man mit mehreren > Layern arbeiten. Das stimmt, ist aber in diesem Fall nicht erforderlich, Linien bzw Zeiger lassen sich sehr schnell zeichnen, wenn der jedes mal der Bildbereich im SRAM gelöscht wird, die Uhr dann komplett neu berechnet und anschließend das Display refresht wird, dann sieht das richtig schick aus, selbst mit einem Sekundenzeiger. Steffen H. schrieb: > Ein "I" ist schmaler als ein "W". > Dazu gibt es dann zu jedem Font einen Daten Header (Kopf) Man könnte auch im Programm diese Header-Daten berechnen, d.h. ein "i" wird aus einem Standart-Font geladen un dann untersucht, auf welche Spalten könnte man versichten, damits noch schöner aussieht :-) Dieser Assemblercode besitzt noch eine Menge Optimierungspotential, momentan steht die Zeichengeschwindigkeit im Vordergrund^^ Bernhard
:
Bearbeitet durch User
Steffen H. schrieb: > Ich habe mir bei ebay mal ein 1,5Zoll RGB OLED mit SPI und SSD1351 > geholt. Die Adressierung ist bei diesem Display > einfacher. Sieht auch schick aus :-) https://www.mikrocontroller.net/attachment/489878/OLED_RGB_SSD1351.jpg @Steffen, nun ist auch bei mir ein solches SSD1351 Display eingetroffen, noch ist es dunkel, hättest Du ein Assembler-Beispiel und eine Schaltung für uns? Ein hex-File für einen ATmega8 wäre schön, um wenigstens die Hardware zu testen. So mehr ich mich mit diesem OLED 128x128 RGB Color Farbe theoretisch beschäftige, um so mehr Fragen ergeben sich z.B. - Betriebsspannung max wirklich nur 3,3V? - Welche Pins müssen wie beschaltet werden? - muss Reset beschaltet werden? - wie erfolgt die Initialisierung? Da das Display nicht ausgelesen werden kann, wäre z.B. der Betrieb einer SD-Card am SPI-Bus ohne Aufwand möglich. Danke Bernhard
:
Bearbeitet durch User
Bernhard S. schrieb: > nun ist auch bei mir ein solches SSD1351 Display eingetroffen, noch ist > es dunkel, hättest Du ein Assembler-Beispiel und eine Schaltung für uns? > > Ein hex-File für einen ATmega8 wäre schön, um wenigstens die Hardware zu > testen. Ich mache nicht mehr viel mit den uralten AVR wie dem Mega8. Ich habe mich jetzt vollkommen auf die neuen AVR der Serie-0 und Serie-1 eingeschossen. Da ich vorher schon einiges mit den Xmegas vorher zu tun hatte, war es nicht so schwer sich in die neue Architektur einzuarbeiten. Außerdem benötigen die neuen AVR nur noch eine einzige Ader (und GND natürlich) um diese zu programmieren und zu debuggen! Nennt sich UPDI. Ich habe mir meinen eigenen UPDI Programmer/Debugger selber gebaut. Aus einem Arduino Micro. Firmware drauf und schon hat man einen UPDI Programmer/Debugger. Ich hab dir aber mal ein Testfile für einen ATtiny1614 dran gehangen. Da kannst dir wenigstens die Initialisierung raus suchen. Ach ja, mit dabei sind ein paar pdf Dokumente. Bernhard S. schrieb: > So mehr ich mich mit diesem OLED 128x128 RGB Color Farbe theoretisch > beschäftige, um so mehr Fragen ergeben sich z.B. > > - Betriebsspannung max wirklich nur 3,3V? JA! > - Welche Pins müssen wie beschaltet werden? CLK, DIN, CS, DC, RESET, GND, Vcc Also alle! > - muss Reset beschaltet werden? JA > - wie erfolgt die Initialisierung? Siehe ZIP-File Mfg Steffen
Bernhard, ich hab hier mal eine .hex Datei für einen ATmega328 mit einer Testsequenz zum SSD1351 aus Arduino erstellt. Wenn du diesen mit nur 3,3V und 8MHz betreibst, sollte das doch auch ohne Level Shifter gehen. In einen Mega8 passt das ganze schon nicht mehr hinein. MfG Steffen
> Das Display wird nur refresht, wenn sich im Bildspeicher etwas ändert, > spart Strom. Ein kleiner Tip: Ich mach das so das ich den Bildspeicher aufteile. Zum Beispiel in 4Segmente und 8Pages. Dann ist ein solches Segment nur 32Byte gross. (bei 128x64Pixel) Ausserdem hab ich noch ein paar Byte in dem ich mir merke wenn sich in einem Segment etwas gaendert hat. Dann macht man einen Ticker-IRQ und sorgt dafuer das alle 5ms ein Segment uebertragen wird wenn es sich geaendert haben sollte. Dadurch hat man in der Praxis kaum Datenuebertragung zum Display da sich selten viel aendert und trotzdem wirkt das Display extrem responsiv. Olaf zusatztip: Seit kurzem findet man immer mehr Oled (1.3") mit dem SH1106. Der ist fast baugleich zum SSD1306, lediglich die Uebergabe der Columnadresse braucht einen anderen Befehl.
Olaf schrieb: > zusatztip: Seit kurzem findet man immer mehr Oled (1.3") mit dem SH1106. > Der ist fast baugleich zum SSD1306, lediglich die Uebergabe der > Columnadresse braucht einen anderen Befehl. Könnten wir einmal einen aktuellen Benchmark im Vergleich zur Adafruit Library bekommen? Der Charme der Adafruit Library ist: extrem umfangreicher Funktionsumfang, sehr viele Displays werden unterstüzt, und das API ist (fast) identisch für verschiedene Displays, was Portierung auf unterschiedliche Display extrem einfach macht... minimalste Sourcecode-Änderungen. Ich frage mich halt, ob der Performanz-Vorsprung den man durch diese Library evtl. erreicht (gegenüber Adafruit Libraries) den Zeitaufwand rechtfertigt. Eine Entscheidungshilfe wäre nett!
:
Bearbeitet durch User
Pat B. schrieb: > Ich frage mich halt, ob der Performanz-Vorsprung den man durch diese > Library evtl. erreicht (gegenüber Adafruit Libraries) den Zeitaufwand > rechtfertigt. Eine Entscheidungshilfe wäre nett! Ich denke nicht, dass das entwickeln eines Treibers für die kleinen OLED Dinger etwas mit Performance als Entscheodungshilfe zu tun hat. Auch wenn Bernhard immer schreibt wie schnell doch seine Routinen sind. Der Hauptgrund ist sicherlich dies selber mal gemacht zu haben um das "Dahinter" zu verstehen. Es in Assembler zu machen und nur das zu implementieren, was man auch wirklich braucht! Und nicht so eine aufgeblasene Bibliothek in C oder C++. Auch wenn die Bibliotheken wirklich gut sind. Bestes Beispiel ist das RDS Radio mit OLED und ATtiny45 ! MfG Steffen
:
Bearbeitet durch User
> Ich denke nicht, dass das entwickeln eines Treibers für die kleinen OLED > Dinger etwas mit Performance als Entscheodungshilfe zu tun hat. Doch, aber nicht so wie man zuerst denkt. Keiner wird den Treiber fuer ein 128x64 Pixel Display so optimieren eine moeglichst hohe Framerate zu schaffen damit er damit Videos darstellen kann. Aber es gibt andere Optimierungsziele. 1. Mir ist es heute wichtig das mein (eigenes!) printf moeglichst schnell ist. Damit kann es auch mal in zeitkritischen Funktionen verwenden. (Z.b fuer Diagnosezwecke in einem PID) 2. Mir war es frueher mal wichtig das meine LCD Ansteuerung moeglichst wenig Ram hatte weil meine Controller nur 1-2kb hatten. (Bufferram war gepackt) 3. Im professionellem Bereich kann Redundanz interessant sein. Wenn du sagen wir mal alle 100ms das Display neu initialisiert dann ist es nicht schlimm wenn der LCD-Controller beim ESD Test abstuerzt. 4. Bei manchen Anwendungen will man das moeglichst selten auf ein Display geschrieben wird weil der Controller moeglichst lange im Sleepmode sein soll damit die Batterie so lange wie moeglich haelt. Diese Ziele widersprechen sich. Deshalb ist die Verwendung von fertigen Libaries anderer Leute immer schlechter als was eigenes zu machen. Ausnahmen gibt es nur dort wo man massiv mit unbekannter Hardware im Kontakt kommt. (SD-Karten, Bluetooth) Olaf
Olaf schrieb: > Diese Ziele widersprechen sich. Deshalb ist die Verwendung von fertigen > Libaries anderer Leute immer schlechter als was eigenes zu machen. > Ausnahmen gibt es nur dort wo man massiv mit unbekannter Hardware im > Kontakt kommt. (SD-Karten, Bluetooth) Sofern man unendlich viel Zeit hat sich mit Problemen zu beschäftigen, die andere schon vor Jahrzehnten gelöst haben, UND wenn die Hardware nicht sonderlich komplex ist, ist das natürlich richtig. Am Besten baut man sich auch noch seinen eigenen Computer dann, dann kommt man richtig schnell vom Fleck (sofern man denn auch noch die Zeit hat, das Rad neu zu erfinden) :-D
> Sofern man unendlich viel Zeit hat sich mit Problemen zu beschäftigen, > die andere schon vor Jahrzehnten gelöst haben, UND wenn die Hardware Du hast ein Verstaendnisproblem. Andere haben vor Jahrzehnten genau ihr Problem geloest. Fuer dich haben sie nur eine Loesung der zweiten Wahl. Zweite Wahl mag manchmal ausreichend sein, aber nicht immer. Hinzu kommt das Leute die es sich angewoehnen immer nur die zweite Wahl zu verwenden niemals die Faehigkeiten entwickeln selber etwas auf die Beine zu stellen. Ich sehe ja sogar oft Leute welche die Libaries anderer Leute verwenden die derart dumme Fragen stellen das klar ist das sie da noch nicht mal versucht haben reinzuschauen, vom echten Verstaendnis sind sie dann noch meilenweit entfernt. Das schafft dann auch irgendwie Probleme. .-) Olaf
Olaf schrieb: >> Sofern man unendlich viel Zeit hat sich mit Problemen zu beschäftigen, >> die andere schon vor Jahrzehnten gelöst haben, UND wenn die Hardware > > Du hast ein Verstaendnisproblem. Ach ja, jetzt wird wieder selektiv zitiert... nein habe ich nicht. Ich verstehe genau was Du meinst, und wie gesagt, wenn die Hardware hinreichend einfach gestrickt ist, kann man mit halbwegs vertretbarem Aufwand so einen Treiber auch noch in Assembler machen. Damals haben ein Freund und ich unseren eigenen VGA Graphik-Treiber bzw. schnellen Linienalgorithmus (Bresenham) für TopSpeed Modula 2 in Assembler auf dem PC programmiert (1992?), weil das mitgelieferte Zeug von TopSpeed zu lahm war für mein selbstgeschriebenes CAD-Programm. Allerdings ist Dein Anspruch "ich mache alles selbst, alles ist besser was ich mache!" nicht realistisch. Du hast wohl nicht verstanden, über wieviele Abstraktionsebenen wir heutzutage operieren. Ohne Abstraktion geht GAR NIX mehr heutzutage. D.h., Libraries und Layers and Layers and Layers and Layers of abstraction. Für triviales Zeug braucht man das nicht, da gebe ich Dir Recht! Es war nicht meine Absicht, Deinen Treiber schlecht zu machen. Deine Aussage in dieser Allgemeinheit ist allerdings Müll. Wie gesagt, der ANSPRUCH hinter die Dinge auf der untersten Ebene zu schauen, und selbst einen kleinen Treiber in Assembler für ein nicht sehr kompliziertes Device zu schreiben, ist sicherlich ehrenwert. Aber SKALIERBAR für ECHTE SYSTEME is dieser Vorgehen bzw. Anspruch - mit vertretbarem Zeitaufwand für Projekte in der Praxis - eben nicht. Lobenswert ist es natürlich, es dennoch zu machen. Würde ich meine Zeit lieber in etwas anderes stecken? Bestimmt :-)
@Steffen, Danke für Dein Testprogramm, es funktionierte auf Anhieb. Was mich wunderte, der ATmega328p scheint schon total vollgepackt zu sein, bleibt da noch Platz für weiteres, oder ist das nur der OLED-Treiber? Mit Deinem Test-Programm konnte ich mich noch nicht beschäftigen, hab's aber schon auf dem Radar. Kannst Du uns mal ganz grob erklären, wie ein farbiger Balken entsteht, schließlich müssen die Farbwerte auch mit übertragen werden. Danke Steffen H. schrieb: > Wenn du diesen mit nur > 3,3V und 8MHz betreibst, sollte das doch auch ohne Level Shifter gehen. Notfalls ist dieser durch Widerstände schnell zusammengefriemelt. Vorsichtshalber werde ich an das Display eine 3,3V Z-Diode anschließen, es könnte ev. sein, daß der AVR-Rogrammer über SPI eine Spannung über SPI einspeist, wie empfindlich der SSD1351 ist, kann ich nicht einschätzen. Olaf schrieb: > Dann macht man einen Ticker-IRQ und sorgt dafuer das alle 5ms ein > Segment uebertragen wird wenn es sich geaendert haben sollte. Dadurch > hat man in der Praxis kaum Datenuebertragung zum Display da sich selten > viel aendert und trotzdem wirkt das Display extrem responsiv. Ein sehr guter Tipp :-) Damit befindet sich zwar der µC eine gewisse Zeit in der Interrupt-Routine, aber für einige Anwendungen sicherlich gut geeignet. PS: Wer Assembler, klein und verdammt schnell, nicht mag, kein Problem <ALT+F4> hilft garantiert ^^
:
Bearbeitet durch User
Bernhard S. schrieb: > Linienberechnung: > Zuerst wird untersucht, ob es senkrechhte oder waagerechte Linen sind, > diese lassen sich sehr flott berechnen. > > Bei den schrägen linien wirds komplizierter, hier untersuche ich den > Anstieg, ob positiv oder negativ. Anschließend wird die Linie 2x > gezeichnet, beim ersten Durchgang wird X hochgezählt und Y berechnet, > beim zweiten Y hochezählt und X berechnet, damit auch sehr flache und > sehr steile Linien ohne Lücken gezeichnet werden, sonnst sieht etwas > komisch aus. Da würde ich Dir den Bresenham empfehlen. Auch da braucht man das Rad nicht neu erfinden (und schneller als da geht's auch nicht...): https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm >> von Olaf (Gast) > Du hast ein Verstaendnisproblem. Andere haben vor Jahrzehnten genau ihr > Problem geloest. Fuer dich haben sie nur eine Loesung der zweiten Wahl. Ist das so? Wahrscheinlich ist das so eine Art Geheim-Wissenschaft, wie man den Bresenham in Assembler implementiert?
Michael W. schrieb: > Da würde ich Dir den Bresenham empfehlen. Erklär uns mal bitte einfach diesen Algorithmus. An einer ganz flachen Linie: Linie X1,Y1=0,0 nach X2,Y2=127,1 Vielleicht ist dann die Umsetzung in ASM gar nicht so schwer. Würde mich interessieren.
:
Bearbeitet durch User
Bernhard S. schrieb: > Michael W. schrieb: >> Da würde ich Dir den Bresenham empfehlen. > > Erklär uns mal bitte einfach diesen Algorithmus. > > An einer ganz flachen Linie: Linie X1,Y1=0,0 nach X2,Y2=127,1 > > Vielleicht ist dann die Umsetzung in ASM gar nicht so schwer. > > Würde mich interessieren.
1 | x0 = 0 |
2 | y0 = 0 |
3 | |
4 | x1 = 127 |
5 | y1 = 1 |
6 | |
7 | abs(y1 - y0) = 1 |
8 | abs(x1 - x0) = 127 |
9 | |
10 | plotLine(x0, y0, x1, y1) |
11 | if abs(y1 - y0) < abs(x1 - x0) |
12 | if x0 > x1 |
13 | plotLineLow(x1, y1, x0, y0) |
14 | else |
15 | plotLineLow(x0, y0, x1, y1) |
16 | ************************** HIER GEHTS WEITER! |
17 | end if |
18 | else |
19 | if y0 > y1 |
20 | plotLineHigh(x1, y1, x0, y0) |
21 | else |
22 | plotLineHigh(x0, y0, x1, y1) |
23 | end if |
24 | end if |
25 | |
26 | |
27 | Also: |
28 | |
29 | plotLineLow(x0, y0, x1, y1) |
30 | dx = x1 - x0 = 127 |
31 | dy = y1 - y0 = 1 |
32 | yi = 1 |
33 | /* |
34 | if dy < 0 |
35 | yi = -1 |
36 | dy = -dy |
37 | end if |
38 | */ |
39 | D = (2 * dy) - dx = 2 - 127 = - 125 |
40 | y = y0 = 0 |
41 | |
42 | for x from 0 to 127 |
43 | plot(x, y) |
44 | if D > 0 |
45 | // y = y + yi |
46 | // D = D + (2 * (dy - dx)) |
47 | y = y + 1 |
48 | D = D + 2 * ( 1 - 127 ) = D + 2 * - 126 = D - 254 |
49 | end if |
50 | else |
51 | // D = D + 2*dy |
52 | D = D + 2 |
53 | |
54 | D.h., nach x + 63 Schritten wird von plot(x, 0) auf plot(x, 1) "umgeschaltet". |
55 | |
56 | Also: |
57 | |
58 | von x = 0 bis 63: plot(x,0) |
59 | von x = 63 bis 127: plot(x,1) |
60 | |
61 | (Toleranz: +/-1 ) ;-) :-D |
Interessant ist nun natürlich, wie Du die horizontalen Segmente plot(x,0) und plot(x,1) in DrawLine umsetzt... mit BlockMove / Copy oder was auch immer ;-) Auf dem Z80 nahm dafür dann die BlockMove Befehle wenn ich mich recht erinnere um ohne explizite Schleife auszukommen etc.
Also wie man Linien in der Rastergrafik zeichnet ist nach Bresenham überall, Wikipedia, YouTube, u.s.w wirklich sehr gut beschrieben und erklärt. Und das sogar auf Deutsch! ;) https://www.youtube.com/watch?v=vlZFSzCIwoc&t=40s Und hier ein Auszug aus meinem Assembler Code:
1 | oled_draw_line: ; XL = Xstart | XH = Xend | YL = Ystart | YH = Yend |
2 | sbrc XL,7 |
3 | ldi XL,((display_Xsize)-1) |
4 | sbrc XH,7 |
5 | ldi XH,((display_Xsize)-1) |
6 | sbrc YL,6 |
7 | ldi YL,((display_Ysize)-1) |
8 | sbrc YH,6 |
9 | ldi YH,((display_Ysize)-1) |
10 | movw r2,XL ; copy Xstart | Xend |
11 | movw r4,YL ; copy Ystart | Yend |
12 | cp r2,r3 ; (x0 == x1) |
13 | brne oled_draw_line_b ; -------------> (x0 != x1) |
14 | cp r5,r4 ; if (x0 == x1) --> (y0 > y1)? -> (y1 <= y0)? |
15 | brsh oled_draw_line_a ; -> (y1 >= y0)? |
16 | mov YL,r5 |
17 | mov YH,r4 ; swap(Ystart,Yend) |
18 | oled_draw_line_a: |
19 | sub YH,YL |
20 | inc YH ; Ylenght = Yend - Ystart |
21 | rjmp oled_draw_vline |
22 | oled_draw_line_b: ; -------------> (x0 != x1) |
23 | cp r4,r5 ; (y0 == y1) |
24 | brne oled_write_line ; -------------> (y0 != y1) |
25 | cp r3,r2 ; if (y0 == y1) --> (x0 > x1)? -> (x1 <= x0)? |
26 | brsh oled_draw_line_c ; -> (x1 >= x0)? |
27 | mov XL,r3 |
28 | mov XH,r2 ; swap(Xstart,Xend) |
29 | oled_draw_line_c: |
30 | sub XH,XL |
31 | inc XH ; Xlenght = Xend - Xstart |
32 | rjmp oled_draw_hline |
33 | ;*************************************************************************************************************************************** |
34 | oled_write_line: |
35 | ; bool steep = abs(y1 - y0) > abs(x1 - x0) |
36 | mov r6,r3 |
37 | sub r6,r2 ; r6 = (x1 - x0) |
38 | sbrc r6,7 |
39 | neg r6 ; r6 = abs(x1 - x0) |
40 | mov r7,r5 |
41 | sub r7,r4 ; r7 = (y1 - y0) |
42 | sbrc r7,7 |
43 | neg r7 ; r7 = abs(y1 - y0) |
44 | clr r12 ; clr --> steep = 0 (bool) |
45 | cp r6,r7 ; wenn (abs(x1 - x0) >= abs(y1 - y0)) -----> ueberspringe folgende Anweisungsliste |
46 | brsh oled_write_line_aa |
47 | ; if (steep) |
48 | mov r12,ONE ; set --> steep = 1 (bool) |
49 | ; swap(x0, y0) |
50 | mov r8,r4 ; (temp) r8 <-- r4 (y0) |
51 | mov r4,r2 ; (y0) r4 <-- r2 (x0) |
52 | mov r2,r8 ; (x0) r2 <-- r8 (temp/y0) |
53 | ; swap(x1, y1) |
54 | mov r8,r5 ; (temp) r8 <-- r5 (y1) |
55 | mov r5,r3 ; (y1) r5 <-- r3 (x1) |
56 | mov r3,r8 ; (x1) r3 <-- r8 (temp/y1) |
57 | oled_write_line_aa: |
58 | ; if (x0 > x1) |
59 | cp r3,r2 ; wenn (x1 >= x0) -----> ueberspringe folgende Anweisungsliste |
60 | brsh oled_write_line_bb |
61 | ; swap(x0, x1) |
62 | mov r8,r3 ; (temp) r8 <-- r3 (x1) |
63 | mov r3,r2 ; (x1) r3 <-- r2 (x0) |
64 | mov r2,r8 ; (x0) r2 <-- r8 (temp/x1) |
65 | ; swap(y0, y1) |
66 | mov r8,r5 ; (temp) r8 <-- r5 (y1) |
67 | mov r5,r4 ; (y1) r5 <-- r4 (y0) |
68 | mov r4,r8 ; (y0) r4 <-- r8 (temp/y1) |
69 | oled_write_line_bb: |
70 | ; dx = x1 - x0 |
71 | mov r6,r3 ; r6 = dx |
72 | sub r6,r2 ; r6 = (x1 - x0) |
73 | ; dy = abs(y1 - y0) |
74 | mov r7,r5 ; r7 = dy |
75 | sub r7,r4 ; r7 = (y1 - y0) |
76 | sbrc r7,7 |
77 | neg r7 ; r7 = abs(y1 - y0) |
78 | ; err = dx / 2 |
79 | mov r10,r6 ; r10 = err(lo) |
80 | clr r11 ; r11 = err(hi) |
81 | lsr r10 ; r10 = dx/2 |
82 | ; if(y0 < y1) |
83 | cp r4,r5 ; wenn (y0 >= y1) -----> ueberspringe folgende Anweisungsliste |
84 | brsh oled_write_line_cc |
85 | ; ystep = 1 |
86 | mov r9,ONE ; r9 = ystep = 1 |
87 | rjmp oled_write_line_loop |
88 | oled_write_line_cc: |
89 | ; ystep = -1 |
90 | clr r9 |
91 | dec r9 ; r9 = ystep = -1 |
92 | oled_write_line_loop: |
93 | ; if(; x1 < x0) ----> break |
94 | cp r3,r2 |
95 | brlo oled_write_line_end |
96 | ; if(steep) |
97 | sbrs r12,0 |
98 | rjmp oled_write_line_loop_else |
99 | ; Draw_Pixel(y0, x0) |
100 | mov XL,r4 |
101 | mov YL,r2 |
102 | rcall oled_set_pixel |
103 | rjmp oled_write_line_loop_err |
104 | oled_write_line_loop_else: |
105 | ; Draw_Pixel(x0, y0) |
106 | mov XL,r2 |
107 | mov YL,r4 |
108 | rcall oled_set_pixel |
109 | oled_write_line_loop_err: |
110 | ; err -= dy |
111 | sub r10,r7 |
112 | sbc r11,ZERO |
113 | ; if(err < 0) |
114 | cp ZERO,r10 |
115 | cpc ZERO,r11 ; wenn if(0 >= err) -----> ueberspringe folgende Anweisungsliste |
116 | brlt oled_write_line_loop_next |
117 | ; y0 += ystep |
118 | add r4,r9 |
119 | ; err += dx |
120 | add r10,r6 |
121 | adc r11,ZERO |
122 | oled_write_line_loop_next: |
123 | ; x0++ |
124 | add r2,ONE |
125 | rjmp oled_write_line_loop |
126 | oled_write_line_end: |
127 | ret |
Die Funktionen "draw_hline", "draw_vline" und "draw_pixel" hab ich jetzt nicht mit hinzugefügt.
:
Bearbeitet durch User
Bernhard S. schrieb: > Danke für Dein Testprogramm, es funktionierte auf Anhieb. Bitte schön Bernhard S. schrieb: > Was mich wunderte, der ATmega328p scheint schon total vollgepackt zu > sein, bleibt da noch Platz für weiteres, oder ist das nur der > OLED-Treiber? Ja, ein mächtiger OLED Treiber womit man auch viel machen kann. Aber voll ist der ATmega328 noch nicht. 17114 Bytes Flash (53%) 683 Bytes RAM (33%) Bernhard S. schrieb: > Kannst Du uns mal ganz grob erklären, wie ein farbiger Balken entsteht, > schließlich müssen die Farbwerte auch mit übertragen werden. Man schreibt die Pixeladresse pixelgenau an das Display und dann ein Write zum DRAM Command gefolgt von 2 Datenbytes für jeden Pixel (RGB555 Farbwert). Der interne Adresszähler des DRAM wird nach jedem Pixel hochgezählt.
Steffen H. schrieb: > Und hier ein Auszug aus meinem Assembler Code: Sehr gut, wie von der Wikipedia-Seite bzw. aus dem Lehrbuch! Also doch keine Geheimwissenschaft :-)
@Steffen ich würde gern zur SSD1351 Initialisierung einen separaten Beitrag verfassen, vorausgesetzt Du stimmst dem zu, da ich sehr viel Assembler-Code aus Deinem Programm nutze. Danke
Bernhard S. schrieb: > @Steffen > ich würde gern zur SSD1351 Initialisierung einen separaten Beitrag > verfassen, vorausgesetzt Du stimmst dem zu, da ich sehr viel > Assembler-Code aus Deinem Programm nutze. > > Danke Kein Problem, kanst machen.
OLED128x128 SPI SSD1351 RGB Initialisierung: Beitrag "OLED128x128 SSD1351 RGB Initialisierung Color Farbe bunt SPI 3.3V AVR ATmega8 ATmega328p Assembler"
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.