mikrocontroller.net

Forum: Projekte & Code SSD1306 Library zum Darstellen von Text auf OLED Displays


Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
6 lesenswert
nicht lesenswert
Aktuelle Version hier: 
Beitrag "Re: SSD1306 Library zum Darstellen von Text auf OLED Displays"

Ich habe eine kleine Library für den Displaycontroller SSD1306 
geschrieben, wie er häufig bei den 0.96" OLED-Displays eingesetzt wird. 
Als Schnittstelle wird IIC benutzt.
Die Library zeichnet lediglich Text auf das Display.
Der Vorteil bzgl. Adafruit und u8glib ist u.a., dass bei weitem nicht so 
viel Flash benötigt wird (etwa 1400 kByte wenn man nur "Hallo Welt!" 
aufs Display schreibt, Adafruit braucht da rund 10 kByte, u8glib etwa 8 
kByte) und auch beim RAM sieht es erheblich besser aus als bei den 
üblichen Libs die jeweils mit einem Puffer in Displaygröße arbeiten, 
d.h. man braucht über ein 1kByte RAM alleine für die Lib.
Meine Lib arbeitet ohne Puffer, daher wird hierfür auch kein statischer 
RAM für einen Puffer benötigt. Ich hab es nicht getestet, aber auch 128 
Byte RAM müssten locker ausreichend sein.
Um das gesamte Display zu beschreiben (168 Zeichen) benötigt die Lib 
hier bei 400 kHz IIC-Clock nur ca. 35 ms, für eine Zeile (21 Zeichen) 
sind es ca. 4,5 ms. Adafruit und u8glib benötigen beide alleine für eine 
Zeile schon mehr als 20 ms.
Bei den Befehlen für das Display habe ich mich an den Befehlen von Peter 
Fleury's Lib für die HD44780-Displays gehalten.
Getestet hab ich die Lib mit einem ATMega168PA und einem ATMega328P, 
beide mit 16 MHz Quarz, den II2C hab ich auf 400 kHz eingestellt gehabt, 
sie sollte auch auf einem ATMega88PA laufen.

Interessant dürfte die Lib für all jene sein, die kleinere AVRs 
benutzten wollen, als die, die von ADAFRUIT und u8glib erwartet werden.

Als nächstes werde ich mich an die Portierung für die ATTinys dran 
setzen. Eine Grafik-Variante (mit der man auch Zeichnen kann) hab ich 
auch fertig, allerdings braucht die noch Feintuning und sie benötigt 
einen AVR mit mehr als 1 kByte RAM (sie arbeitet mit einem Puffer).

EDIT: Noch ein Foto angehangen.

: Bearbeitet durch Moderator
Autor: edsyn (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Hallo,
nur zur Ergänzung.
Nicht alle Arduino-Libs für SSD1306 sind so gross.
Bill Greiman hat auch eine eingedampfte Ascii-Lib:
"Sketch uses 2,622 bytes (8%) of program storage space.
Global variables use 53 bytes (2%) of dynamic memory"
https://github.com/greiman/SSD1306Ascii

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
edsyn schrieb:
> Hallo,
> nur zur Ergänzung.
> Nicht alle Arduino-Libs für SSD1306 sind so gross.
> Bill Greiman hat auch eine eingedampfte Ascii-Lib:
> "Sketch uses 2,622 bytes (8%) of program storage space.
> Global variables use 53 bytes (2%) of dynamic memory"
> https://github.com/greiman/SSD1306Ascii

Die schaut auch interessant aus, keine Frage. Hab ich jetzt nur mal 
überflogen aber scheint auch für Arduino zu sein. Das benutze ich gar 
nicht wennauch ich Arduino Boards habe. Die habe ich aber nur weil die 
Boards ansich recht praktisch sind.
53 Byte für Globale Variablen ist schon einiges, beim Überfliegen hab 
ich so jetzt gar nicht direkt gesehen wofür die nötig sind.
However, gefällt mir auch. Vielleicht mach ich die Tage mit der Lib auch 
noch einen Geschwindigkeitstest um sie in den Vergleich zu meiner Lib 
mit aufzunehmen.

Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
3 lesenswert
nicht lesenswert
Ich war dann gestern noch etwas ambitioniert und habe meiner Library 
noch Grafikfunktionen hinzugefügt. Jetzt benötigt die Lib leider schon 
etwas mehr als 4kByte Flash sowie 1029 Byte SRAM.
Da ich die Grafikfunktionen nicht immer benötige hab ich für die 
Grafik-Variante eine eigene Lib gemacht.
In meinem hier gezeigten Beispiel enthält die Refresh-Rate das Zeichnen 
des Rechtecks, des Kreises und des Kreuzes, das Löschen und neu 
Schreiben der letzten Zeile sowie den "Balken" (den lasse ich erst nach 
rechts hin wachsen und wenn er den rechten Rand erreicht hat lösche ich 
da Linie für Linie Richtung links bis er verschwunden ist).
Es wird zunächst in den Puffer geschrieben und dann mittels 
lcd_display() dieser dem Display zur Anzeige geschickt.
Die erste Zeile wird nur beim Start in den Puffer geschrieben. Das Ganze 
dauert, wie man sehen kann, nicht mal 35 ms. IMO gar nicht mal so 
schlecht für den ersten Ansatz.

Autor: Dirk B. (garag)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich finde die Library interessant. Jedoch vermisse ich einen 
Lizenzhinweis. Darf man die Library nun frei verwenden ?

Gruß
Dirk

Autor: M. Köhler (sylaina)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Dirk B. schrieb:
> Darf man die Library nun frei verwenden ?

Upsala, natürlich darf man die Library frei verwenden. Muss mir mal über 
Lizensen und solcher Sachen Gedanken machen, da hast du recht.

Autor: Max MMM (maxmicr)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Ich wollte mich bei dir auch noch für die Lib bedanken, tolle Arbeit!

Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hätte einen kleinen Wunsch: Könntest du noch das Zeichen "°" zum 
Font hinzufügen?

: Bearbeitet durch User
Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
2 lesenswert
nicht lesenswert
Max M. schrieb:
> Ich hätte einen kleinen Wunsch: Könntest du noch das Zeichen "°" zum
> Font hinzufügen?

Ich habe sogar mehr getan. Neben "°" sind nun auch noch die Umlaute 
"üÜöÖäÄ" und das "ß" verfügbar. ;)
Im Anhang die Text-Variante und die Grafik-Variante.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
könnte mir ja gefallen nur....

es fehlt die Version für SH1106 mit SSD1306 Ansprache bekomme ich rechts 
Grafikfehler

ein ATmega 1284p (mighty mini z.B. oder nativ) ist auch noch nicht dabei

schade....

Autor: M. Köhler (sylaina)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Richtig, es ist ja auch in der Entwicklungsphase und ich hab halt nur 
einen Atmega328p (bzw. seine kleineren Brüder) und Attiny45/85. SH1106 
muss ich mal spicken, sollte recht simpel umsetzbar sein da dies nur um 
2 byte verschoben ist gegenüber dem ssd1306 soweit ich das jetzt gelesen 
hatte im Netz.
Ich werd mal schaun was sich da machen lässt. Erstmal muss ich schaun, 
dass ich die Attinys an den Start bekomme. Hatte die letzte Woche nicht 
wirklich Zeit hier mal weiter zu arbeiten, ich hoffe auf nächste Woche 
;)

EDIT: Hab mal geschaut. Der Atmega1284p (hab ich persönlich nicht im 
Einsatz, daher kenne ich ihn auch nicht) ist bzgl. i2c identisch mit dem 
Atmega328, d.h. die Zeile 174 in der C-Datei:
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega48P__)

muss nur gegen die folgende Zeile
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega1284P__)

ersetzt werden und schon dürfte auch der Atemga1284p unterstützt werden.

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> etwa 1400 kByte wenn man nur "Hallo Welt!" aufs Display schreibt

1,4 MB, nun, auf einen modernen ARM passt das schon. ;-) <scnr>

Danke schon mal, habe zwar bislang die Variante mit dem 1-KB-Puffer
genutzt, aber richtig toll fand ich das nie.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke

ich mache es mir einfacher in einer pin.h
#ifndef _PINS_H_
#define _PINS_H_

#if defined(__AVR_ATmega328P__)
  #define   TIMSKx              TIMSK1
  #define   OCIExA              OCIE1A
  #define   TIMERx_COMPA_vect   TIMER1_COMPA_vect  // ATmega
  #define   TCCRxA              TCCR1A
  #define   COMxA0              COM1A0
  #define   OCRxA               OCR1A
  #define   TCCRxB              TCCR1B
  #define   WGMx2               WGM12
  #define   CSx0                CS10

  #define LED_ARDUINO           5
  #define LED_ARDUINO_DDR       DDRB
  #define LED_ARDUINO_PORT      PORTB
  #define LED_ARDUINO_ON        LED_ARDUINO_PORT|=(1<<LED_ARDUINO)
  #define LED_ARDUINO_OFF       LED_ARDUINO_PORT&=~(1<<LED_ARDUINO)
  #define LED_ARDUINO_START  7
  #define LED_ARDUINO_ONOFF_TIME  10 // x10ms

  // arduino nano v3
  // DIGITAL
  // (Reset)        PC6
  // D0 (Rx)        PD0
  // D1 (Tx)        PD1
  // D2 (Int0)      PD2
  // D3 (Int1)      PD3
  // D4 (XCK/T0)    PD4
  // D5 (T1)        PD5
  // D6 (AIN0)      PD6
  // D7 (AIN1)      PD7  
  // D8 (ICP1)      PB0
  // D9 (OC1A)      PB1 (PWM)
  // D10(SS/OC1B)   PB2 (PWM)
  // D11(MOSI/OC2)  PB3 (PWM)
  // D12(MISO)      PB4
  // D13(SCK)       PB5 (LED)
  
  // ANALOG
  // A0 (ADC0)      PC0
  // A1 (ADC1)      PC1
  // A2 (ADC2)      PC2
  // A3 (ADC3)      PC3
  // ----------------- I2C -----------------
  // A4 (ADC4/SDAglb)  PC4
  #define PIN_SDA        4 //PC4 -> A4
  // A5 (ADC5/SCLgrn)  PC5
  #define PIN_SCL        5 //PC5 -> A5
  // D12

 #elif defined(__AVR_ATmega1284P__) // --------------------------------------------------------- m1284p -------------------------------------------------------

  #define   TIMSKx              TIMSK3
  #define   OCIExA              OCIE3A
  #define   TIMERx_COMPA_vect   TIMER3_COMPA_vect  // ATmega
  #define   TCCRxA              TCCR3A
  #define   COMxA0              COM3A0
  #define   OCRxA               OCR3A
  #define   TCCRxB              TCCR3B
  #define   WGMx2               WGM32
  #define   CSx0                CS30
  #define   UBRRnL              UBRR0L
  #define   UBRRnH              UBRR0H
  #define   UCSRnA              UCSR0A
  #define   U2Xn                U2X0
/*
  #define   USARTn_RX_vect      USART0_RX_vect
  #define   USARTn_UDRE_vect    USART0_UDRE_vect
*/  
//                                m1284p
//    mighty ARDUINO          +----\/----+
//                        RST |1  ISP  36| VCC
//              RX0 (D 8) PD0 |2         | PB7 (D 7) PWM/SCK 
  #define LED_ARDUINO 7
  #define LED_ARDUINO_PORT                        PORTB
  #define LED_ARDUINO_DDR                         DDRB
//              TX0 (D 9) PD1 |3         | PB6 (D 6) PWM/MISO 
//        RX1/INT0 (D 10) PD2 |4         | PB5 (D 5) MOSI 
//        TX1/INT1 (D 11) PD3 |5         | PB4 (D 4) PWM/SS
//             PWM (D 12) PD4 |6         | PB3 (D 3) PWM 
//             PWM (D 13) PD5 |7         | PB2 (D 2) INT2 
//             PWM (D 14) PD6 |8         | PB1 (D 1) 
//             PWM (D 15) PD7 |9         | PB0 (D 0) 
//                            |          |
//                            |          |
//          SCLgrn (D 16) PC0 |10      27| PA0 (AI 0 / D24)
  #define  PIN_SCL    16                                    //PC0 -> D16

//          SDAglb (D 17) PC1 |11      26| PA1 (AI 1 / D25)
  #define  PIN_SDA    17                                    //PC1 -> D17
//             TCK (D 18) PC2 |12      25| PA2 (AI 2 / D26)
//             TMS (D 19) PC3 |13      24| PA3 (AI 3 / D27)
//             TDO (D 20) PC4 |14      23| PA4 (AI 4 / D28)
//             TDI (D 21) PC5 |15      22| PA5 (AI 5 / D29)
//                 (D 22) PC6 |16      21| PA6 (AI 6 / D30)
//                 (D 23) PC7 |17      20| PA7 (AI 7 / D31)
//                        GND |18      19| AREF
//                            +----------+
 #endif // defined(__AVR_ATmega1284P__)

  #ifdef LED_ARDUINO
    #ifdef LED_ARDUINO_DDR
      #ifdef LED_ARDUINO_PORT
        #define LED_ARDUINO_ON       LED_ARDUINO_PORT  |= (1<<LED_ARDUINO)
        #define LED_ARDUINO_OFF      LED_ARDUINO_PORT &= ~(1<<LED_ARDUINO)
        #define LED_ARDUINO_START  5
        #define LED_ARDUINO_ONOFF_TIME  2 // x10ms
      #endif // #ifdef LED_ARDUINO_DDR
    #endif // #ifdef LED_ARDUINO_PORT
  #endif // #ifdef LED_ARDUINO


#endif // _PINS_H_ 

Autor: M. Köhler (sylaina)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Danke schon mal, habe zwar bislang die Variante mit dem 1-KB-Puffer
> genutzt, aber richtig toll fand ich das nie.

Das Problem ist dabei halt, dass man beim I2C das Display nicht lesen 
kann. Bzgl. Zeichnen kommt man da halt um den Puffer nicht drum rum. Bei 
reiner Textanwendung aber kann man sich den Puffer tatsächlich sparen. 
Das war eine meiner Motivationen zu der Lib ;)

Joachim B. schrieb:
> ich mache es mir einfacher in einer pin.h

Das ist natürlich auch eine Möglichkeit ;)

Autor: Max MMM (maxmicr)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
M. K. schrieb:
> Ich habe sogar mehr getan. Neben "°" sind nun auch noch die Umlaute
> "üÜöÖäÄ" und das "ß" verfügbar. ;)
> Im Anhang die Text-Variante und die Grafik-Variante.

Super, danke! Mir ist gerade beim portieren auf meinen Controller 
aufgefallen, dass bei mir das '°' nicht den Code 144 hat, deswegen hab 
ich das so verglichen:
  if(c == '°'){
    c = 101;    //°

ist vllt. einfacher zu lesen. Könntest du die lcd_putc-Funktion nicht 
auch deutlich kürzen, wenn du zuerst die Variable c entsprechend 
bestimmst und dann nur einmal die Schleife hinschreibst, in etwa so:
switch (c){
 case 156: //vllt. geht hier auch "case 'ü'"
  c = 95;
  break;
 case 150:
  c = 99;
  break;
 ...
 default:
  c-=32;
  break;
}
for(uint8_t...){
//set buffer
}

Kam mir nur gerade beim Durchschauen. Tolle Arbeit! Läuft auch auf nem 
STM8S103 mit 1kByte RAM (ich hab den Puffer auf ~630Byte verkleinert) 
super! Ich hoffe es ist in Ordnung, wenn ich deinen Code leicht 
verändert benutze?

: Bearbeitet durch User
Autor: M. Köhler (sylaina)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Hallo Max,
sicher kann ich das noch entsprechend umstricken. Ich muss gestehen, ich 
hab das heute Mittag quasi nur so hingerotzt, Verfeinerungen werden 
später noch kommen ;)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Bei reiner Textanwendung aber kann man sich den Puffer tatsächlich
> sparen.

Beim Zeichnen geht es auch, wenn man halt immer komplette 8 Pixel
zeichnet.

Klar, mit Puffer hat auch was.

Habe gerade etwas ähnliches durch bei einem Farb-LCD, aber dort
braucht man pro Pixel ein ganzes Byte, damit keinen Puffer.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Beim Zeichnen geht es auch, wenn man halt immer komplette 8 Pixel
> zeichnet.

Hm, mach ich doch, bzw. geht bei dem Display doch gar nicht anders, das 
ist ja quasi das Problem warum man erst einen Puffer braucht: Man kann 
Pixel eigentlich immer nur im 8er-Pack modifizieren, d.h. selbst wenn 
man nur einen Pixel ändern will muss man dem Display Infos für 8 Pixel 
schicken.

Wie soll das funktionieren also ohne Puffer funktionieren? Nehmen wir 
an, es wurde schon etwas auf das Display gezeichnet. Jetzt soll noch 
zusätzlich was dazu gezeichnet werden. Wie kann man das denn ohne Puffer 
etwas dazu zeichnen ohne den bisherigen Displayinhalt zu löschen? Ohne 
Puffer sehe ich da keinen Weg, vielleicht hast du ja einen Tipp für 
mich.

Bei Text hat man ja nur deshalb den Vorteil den Puffer zu sparen weil 
man idR nicht 2, 3, 4, 5 usw. Buchstaben übereinander zeichnen will, man 
will immer nur einen einzigen Buchstaben in einem Feld stehen haben. 
Deshalb ist es da völlig in Ordnung wenn der vorherige Inhalt des Felds 
gelöscht wird. Will man z.B. den Schnittpunkt von zwei Kurven 
bestimmen/visualisieren ist das recht doof wenn beim Zeichnen der 
zweiten Kurve die erste Kurve gelöscht wird. Ohne irgendwo einen Puffer 
zu haben wüsste ich nicht, wie man das Lösen könnte. Und wie schon 
gesagt, toll wäre es, man könnte das Display-RAM als Puffer nutzen nur 
lässt sich das bei I2C leider nicht auslesen, sprich nicht als Puffer 
nutzen.

Autor: Axel R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
I am very beghosted!
Ich bin sehr begeistert.
Super Arbeit, klasse Beitrag. Jetzt muss ich nur noch mein 0.96"OLED 
wiederfinden.

Nochmal: herzlichen Dank :)

StromTuner

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Wie kann man das denn ohne Puffer etwas dazu zeichnen ohne den
> bisherigen Displayinhalt zu löschen?

Man muss sich halt das Display dann gedanklich in 8er-Schritten
aufteilen – so, wie du es letztlich ja auch beim Text machst.

Braucht mehr Planung (dessen, der damit was machen will), aber
weniger RAM.

OK, so gesehen: der nächstgrößere Controller mit ausreichend RAM
ist wahrscheinlich dann doch die einfachere Wahl.

Autor: Tany (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> dann doch die einfachere Wahl

...und sinnvolle Wahl, die Preis-Differenz ist minimal.

M. K. schrieb:
> Wie kann man das denn ohne Puffer
> etwas dazu zeichnen ohne den bisherigen Displayinhalt zu löschen?

ganz einfach: man löscht nicht den kompletten Displayinhalt, sondern nur 
den Bereich, wo "zusätzlich" geschrieben wird. Es reicht auch zu 
überschreiben.
Dafür braucht man keinen Puffer.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tany schrieb:
> ganz einfach: man löscht nicht den kompletten Displayinhalt, sondern nur
> den Bereich, wo "zusätzlich" geschrieben wird.

beim Arduino TFT Shield habe ich den alten Text mit black vor dem neuen 
Text geschrieben, löscht schneller als rect

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tany schrieb:
> ganz einfach: man löscht nicht den kompletten Displayinhalt, sondern nur
> den Bereich, wo "zusätzlich" geschrieben wird. Es reicht auch zu
> überschreiben.
> Dafür braucht man keinen Puffer.

Einfach mal mit dem Display auseinander setzen, dann wüsstest du, dass 
das nicht geht. Die kleinste Einheit, die man bei diesem Display ändern 
kann, sind "Blöcke" mit 8 Pixel Größe. Wie willst du innerhalb eines 
Blockes einen Pixel ändern, ohne die anderen zu beeinflussen und ohne 
Puffer zu haben? Auch hier wieder bedenken: Via I2C lässt sich das 
Display nicht auslesen, nur beschreiben.
Ich sehe hier keinen Weg ohne Puffer, bin aber für konkrete 
Gegenbeispiele sehr offen.

Autor: Tany (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Einfach mal mit dem Display auseinander setzen, dann wüsstest du, dass
> das nicht geht
So ein klein habe ich nicht, sondern ein 240x128 Display. Und der 
Controller braucht nicht ein Bit, sondern 5 Bits für ein Pixel. Es geht 
trotzdem ohne Puffer.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> und ich hab halt nur
> einen Atmega328p (bzw. seine kleineren Brüder) und Attiny45/85. SH1106
> muss ich mal spicken, sollte recht simpel umsetzbar sein da dies nur um
> 2 byte verschoben ist gegenüber dem ssd1306 soweit ich das jetzt gelesen
> hatte im Netz.
> Ich werd mal schaun was sich da machen lässt.

wäre schön

hier fand ich auch was
https://forum.arduino.cc/index.php?topic=256374.0

aber ich mag nicht in deinem Code patchen, du kennst ihn ja besser!

wie groß ist dein Font eigentlich, ich habe ja auch Nokia 5110 am Start 
mit 84x48 Pixel und 6x8 Font (oder 5x7?)

mit 128 PIX kommt man weiter, komischerweise kann ich auf dem Nokia 14 
Zeichen auf 6 Zeilen, mit Adafruit zwar 16 Zeichen pro Zeile

bei 128 pix in x müssten über 20 Zeichen pro Zeile möglich sein x 8 
Zeilen

: Bearbeitet durch User
Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das schau ich mir heute abend mal an. Im Moment fehlt mir mal wieder die 
Zeit mich genauer mit dem Display zu beschäftigen und da ich es aktuell 
nur am Atmega168 benutze habe ich zur Zeit auch keine Notwendigkeit 
dafür. Hoffe in den nächsten Wochen haben ich wieder etwas mehr Luft 
dafür. Wills ja auch noch für den Attiny45 an den Start bekommen.

Autor: Uwe Der B.B. :-) (monkye)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Die Grafikroutinen werden spürbar kleiner, wenn Du anstatt der vielen 
Mathe-Funktionen sowohl Linien als auch Kreise mit dem Bresenham 
Algorithmus implementierst. Da kommst Du komplett mit Interger zurecht 
und schnell ist es auch.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da hast du recht Uwe, daher habe ich grade noch mal in den Code 
geschaut. Den Kreis hatte ich nämlich schon mit Bresenham implementiert, 
die Linien hab ich vergessen umzubauen. Kommt direkt auf die TODO-Liste 
;)

Autor: Uwe Der B.B. :-) (monkye)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bei den kleinen Displays, bei denen man direkt mit einem Byte zur 
Adressierung auskommt (also max 255 Pixel für eine Achse), braucht man 
nicht mal Funkionen für Absolutwerte/Vorzeichen.

Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Joachim B. schrieb:
> wie groß ist dein Font eigentlich, ich habe ja auch Nokia 5110 am Start
> mit 84x48 Pixel und 6x8 Font (oder 5x7?)

Mein Font ist auch 6x8 Pixel (wobei die erste Spalte und letzte Zeile 
ungenutzt ist) groß.

Joachim B. schrieb:
> bei 128 pix in x müssten über 20 Zeichen pro Zeile möglich sein x 8
> Zeilen

21 Zeichen sind es bei mir in der aktuellen Version.

Und da sind wir auch schon in der Überleitung:
1. Neuerung: Ich lasse nun nur noch 21 Zeichen pro Zeile zu und es gibt 
auch keinen automatischen Zeilenumbruch mehr da ich den etwas unschön 
fand. Oder sollte ich einen Schalter einführen damit sich der User diese 
Funktionalität aussuchen kann?
2. Neuerung: Ich habe die Linien-Routinen noch auf Bresenham umgestellt, 
wie von Uwe vorgeschlagen. Bei meinem obigen Beispielcode sinkt dabei 
die Bildwiederholrate von rund 34 ms auf rund 28 ms (400 kHz 
Busgeschwindigkeit). Der benötigte Flashspeicher ist von rund 4 kByte 
auf unter 3 kByte gesunken. Am RAM hat sich nichts geändert, es werden 
immer noch 1027 Byte benötigt. OK, oben schrieb ich 1029 Byte, 2 Byte 
jedoch waren von einer Hilfsvariablen (uint16_t) meiner Mainloop, die 
ich übersah. Die gehört natürlich nicht zur Library, daher 1027 kByte ;)
Die aktuelle Version der Grafikvariante meiner Library habe ich 
angehangen.

Ein SH1106-Display hab ich mir zum Testen schon mal bestellt, sollten 
meine Ideen funktionieren werde ich demnächst hier darüber berichten und 
auch wieder eine aktuallisierte Version meiner Library online stellen.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kein automatischer Zeilenumbruch, sollen sich die User kümmern was sie 
wollen, über 21 Zeichen splitten und selber für die 2te Zeile sorgen 
oder Laufschrift machen

M. K. schrieb:
> Ein SH1106-Display hab ich mir zum Testen schon mal bestellt

freut mich, ich habe auch noch 2 nachbestellt

vielleicht wäre auch noch alternativ ein größerer Font wählbar möglich

ich finde ja den puren 6x8 zu mickrig, auf dem Nokia passt das.

also 16 Zeichen pro Zeile auf 5 Zeilen wäre eine vergleichbare Größe

: Bearbeitet durch User
Autor: Achim S. (achs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tany schrieb:
> sondern 5 Bits für ein Pixel. Es geht trotzdem ohne Puffer.

Nicht trotzdem sondern deshalb. Zumindest, wenn die 3 andern Bits 
keinem anderen Pixel zugeordnet sind.

Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
2 lesenswert
nicht lesenswert
Joachim B. schrieb:
> freut mich, ich habe auch noch 2 nachbestellt

So, ich hab meine SH1106er nun auch endlich bekommen und konnte heute
experimentieren. Library ist angepasst, Fehler möglich ;).
Es werden nun OLED-Displays mit SSD1306 und SH1106 Controller
unterstützt, entsprechend einzustellen in der jeweiligen Header-Datei.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich warte noch auf meine!

Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hätte noch zwei Fragen zur Library:

Warum verwendest du in der "ssd1306_init_sequence" kein "LCD_DISP_ON" am 
Ende so wie du es in einer früheren Version von deiner Lib getan hast 
(soweit ich das richtig im Kopf hab)? Oder ist dafür der Parameter 
"dispAttr" der Funktion "lcd_init" zuständig? Du sendest in der 
"lcd_command" Funktion auch die I2C Slave Adresse des Displays nicht 
mit, hab ich da was verpasst, musste man das nicht?

: Bearbeitet durch User
Autor: M. Köhler (sylaina)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Max M. schrieb:
> Warum verwendest du in der "ssd1306_init_sequence" kein "LCD_DISP_ON" am
> Ende so wie du es in einer früheren Version von deiner Lib getan hast
> (soweit ich das richtig im Kopf hab)? Oder ist dafür der Parameter
> "dispAttr" der Funktion "lcd_init" zuständig?

Das hast du richtig erkannt, ob das Display an oder aus sein soll nach 
dem Init wird nun über den Parameter dispAttr gesteuert, äquivalent zu 
Peter Fleurys lcd-Library zum Ansteuern von Displays mit 
HD44780-Controllern.

Max M. schrieb:
> Du sendest in der
> "lcd_command" Funktion auch die I2C Slave Adresse des Displays nicht
> mit, hab ich da was verpasst, musste man das nicht?

Ja, da hast du was verpasst und ja, man muss die Adresse senden. Auch in 
der lcd_command-Funktion wird die Adresse gesendet. Als erstes wird in 
lcd_command die Funktion lcd_send_12c_start aufgerufen und diese 
Funktion stellt die Startbedingung her und sendet die Adresse.

Autor: oberallgeier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Michael,

erstmal vielen Dank für die Mühe, das Ganze von arduino.IDE abzulösen - 
sozusagen für simple C-Geister (mein C spricht sich Cäh) wie mich. So 
viel gute Hilfe wie für Deinen Transistortester.

Natürlich wollen Faulpelze immer mehr. Wäre es Dir bitte möglich, ein 
kurzes Main als Beispiel zu posten. So etwas in der Art wie für Dein 
Bild vom 24. Jan. 2017 abends?

Als Gegenleistung könnte ich (wenn das 1306er mal auf meinem NanoClone 
läuft) ein Bildchen bieten vom Oskar zu den I²C-Flanken bei den 400 kHz. 
Ich hatte bei so hohen Frequenzen schon mal Pferde "vor der Apotheke .. 
gesehen".

Danke im Voraus
und viele Grüße
der Joe vom Berg

Autor: Walter Jo (oberallgeier)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uuups - war ohne Login.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Aber klar doch, die Main ist wirklich sehr einfach gestrickt gewesen. 
Allerdings hab ich noch ein wenig gespielt, ich weiß grad nicht wie die 
Main zum Beispielbild oben genau aussah. Daher hier die völlig 
unaufgeräumte Main:
/* Name: main.c
 * Author: Michael Koehler
 * Copyright: <insert your copyright message here>
 * License: <insert your license reference here>
 */

#include "main.h"
#include "lcd_ssd1306_gfx.h"
#include <stdlib.h>
#include <util/delay.h>

volatile uint16_t overflows;

int main(void)
{
    setUpAvr();
    lcd_set_contrast(0x0f);
  char time[6];
  uint8_t myVar = 0;
  uint8_t dir =0;
  DDRB |= (1 << PB5);
  
  //lcd_drawCircle(30, 30, 15, WHITE);
  //lcd_fillCircle(70, 40, 10, WHITE);
    for(;;){
    //PORTB ^= (1 << PB5);
    lcd_drawRect(10, 10, 40, 40, WHITE);
     if( dir == 0) {
      lcd_drawLine(0+myVar, 54, 0+myVar, 46, WHITE);
      myVar++;
       if(myVar == 128) dir = 1;
    } else {
      if(myVar == 0) dir=0;
      myVar--;
      lcd_drawLine(myVar, 46, myVar, 54, BLACK);
      
    }
    
    lcd_drawCircle(65, 25, 15, WHITE);
    lcd_drawLine(90, 10, 120, 40, WHITE);
    lcd_drawLine(90, 40, 120, 10, WHITE);
    lcd_drawLine(105, 10, 105, 40, WHITE);
    lcd_drawLine(90, 25, 120, 25, WHITE);
    lcd_gotoxy(0,0);
    lcd_puts_p(PSTR("M. Köhler 2016/2017"));
    lcd_invert(YES);
    lcd_display();
    TCCR1B = 0;
    dtostrf((overflows*65536UL+TCNT1)*1.0/16.0e3,
        6,
        3,
        time);
    overflows = 0;
    TCNT1 = 0;
    TCCR1B = 1 << CS10;
    lcd_gotoxy(0,7);
    lcd_puts_p(PSTR("                     "));
    lcd_gotoxy(0,7);
    lcd_puts_p(PSTR("Refresh: "));
    lcd_puts(time);
    lcd_puts_p(PSTR("ms"));
    //_delay_ms(1000);
    }
    return 0;   /* never reached */
}

void setUpAvr(void){
    /* insert your hardware initialization here */
  TIMSK1 |= (1 << TOIE1);
    // LCD Initialisierungen
  lcd_init(LCD_DISP_ON);
  sei();
}

ISR(TIMER1_OVF_vect){
  overflows++;
}

Autor: STM8-C-Anfänger (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen,

ich bin Programmieranfänger. Ich wollte Deine Bibliothek gern für meinen 
STM8S105-Aufbau benutzen. Mittlerweile läuft es auch, (siehe Anhang) ich 
hätte aber trotzdem noch Fragen dazu.

Du hast eine Abstraktion vom hardware-abhängigen Teil eingeführt, 
benutzt sie aber praktisch nicht. Warum?

void lcd_command(uint8_t cmd) {
    lcd_send_i2c_start();
    lcd_send_i2c_byte(0x00);  // 0x00 for command, 0x40 for data
    lcd_send_i2c_byte(cmd);
    lcd_send_i2c_stop();
}
void lcd_data(uint8_t data) {
    lcd_send_i2c_start();
    lcd_send_i2c_byte(0x40);  // 0x00 for command, 0x40 for data
    lcd_send_i2c_byte(data);
    lcd_send_i2c_stop();
} 

Das hätte die Portierung erheblich vereinfacht, da der Umgang mit i2c 
bei mir ganz anders gestaltet ist.

Schwierigkeiten hatte ich auch mit den Multibyte-Literalen (Umlaute) im 
Quelltext, was sicher auch anderen so gehen dürfte. (siehe Beitrag von 
maxmicr weiter oben) Nutzt Du einen Mac oder so?

Der Frage nach einem Benutzungsbeispiel aka main.c schließe ich mich 
übrigens an.

PS: Ich habe in meiner Version lcd auf OLED umbenannt, weil ich parallel 
ein I2C-LCD benutze und beides klarer abgrenzen wollte. Es ist 
strenggenommen auch kein LCD.

Autor: STM8-C-Anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
STM8-C-Anfänger schrieb:
> Der Frage nach einem Benutzungsbeispiel aka main.c schließe ich mich
> übrigens an.

Oh, zeitliche Überschneidung, hat sich erledigt, danke. :)

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
STM8-C-Anfänger schrieb:
> Du hast eine Abstraktion vom hardware-abhängigen Teil eingeführt,
> benutzt sie aber praktisch nicht. Warum?

Da hast du recht, das liegt daran, dass ich hier noch nicht weiter 
entwickelt habe. Die Funktionen lcd_command und lcd_data können in der 
aktuellen Version immer nur ein Byte übertragen. Es gibt aber auch 
Situationen, da werden/müssen mehrere Bytes übertragen (Beispiel: 
lcd_gotoxy).
Für eine einfache Portierung musst du nicht lcd_command und lcd_data 
anpassen sondern die vier i2c-Funktionen(init, start, stop und byte), 
das erschien mir logischer weil diese Funktionen die 
Prozessorabhängigkeit beinhalten. ;)

Autor: Manni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie wird eigentlich die Schriftfarbe geändert ?

Autor: Nico W. (nico_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manni schrieb:
> Wie wird eigentlich die Schriftfarbe geändert ?

Normal gar nicht. Das Display oben hat ne andere Farbe in dem Bereich. 
Die kann man aber auch nicht ändern.

Autor: STM8-C-Anfänger (Gast)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Manni schrieb:
> Wie wird eigentlich die Schriftfarbe geändert ?

Diese Displays sind monochrom, auch wenn das auf meinem ersten Foto 
anders aussieht. Da ist ein Streifen gelb, der Rest blau.


M. K. schrieb:
> Es gibt aber auch
> Situationen, da werden/müssen mehrere Bytes übertragen (Beispiel:
> lcd_gotoxy).

Das geht auch gut so:
void oled_gotoxy(uint8_t x, uint8_t y){
 
  if( x > 21 || y > 7) return;  // out of display
    x = x * 6;      // one char: 6 pixel width

    oled_command(0xb0 + y);     // set page start to y
    oled_command(0x21);        // set column start to x
    
#if defined SSD1306
    oled_command(x);

#elif defined SH1106
    oled_command(0x00+((2+x) & (0x0f)));             //lowbyte
    oled_command(0x10+( ((2+x) & (0xf0)) >> 4 ) );   //highbyte

#endif
    oled_command( 0x7f);        // set column end to 127
}

oder so:
void oled_init(uint8_t dispAttr){
  for (uint8_t i = 0; i < sizeof (ssd1306_init_sequence); i++) {
    oled_command(ssd1306_init_sequence[i]);
                delay_us(50);
  }    
  oled_command(dispAttr);
    oled_clrscr();
}

Jedenfalls funktioniert es so bei mir.

> Für eine einfache Portierung musst du nicht lcd_command und lcd_data
> anpassen sondern die vier i2c-Funktionen(init, start, stop und byte),
> das erschien mir logischer weil diese Funktionen die
> Prozessorabhängigkeit beinhalten. ;)

Ich finde es nicht gut, I2C so häufig innerhalb der OLED-Bibliothek zu 
verwenden. Was passiert z.B., wenn auf SPI umgestellt werden soll? Es 
gibt diese Displays ja auch mit SPI. Dann muss man die ganze Bibliothek 
umschreiben oder aus den I2C-Funktionen leere bzw. SPI-Funktionen 
machen, die aber im Namen I2C tragen. Wäre sehr verwirrend. Ist auch so 
schon verwirrend, jedenfalls für mich. Weniger Zeilen und sprechende 
Funktionsnamen sind einfach besser zu nachzuvollziehen.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
STM8-C-Anfänger schrieb:
> Das geht auch gut so:

Ja, das geht natürlich auch. Dafür steigt aber auch der Overhead was 
merklich auf die Refresh-Rate geht ;)

STM8-C-Anfänger schrieb:
> Ich finde es nicht gut, I2C so häufig innerhalb der OLED-Bibliothek zu
> verwenden. Was passiert z.B., wenn auf SPI umgestellt werden soll?

Es war auch nie Ziel der Bibliothek, SPI zu unterstützen. Sie war 
ursprünglich nur für I2C gedacht.

Deine Anmerkungen sind sehr gut, ich werde es beherzigen und sehen wie 
ich die Bibliothek allgemeiner halten kann damit es klarer wird, vielen 
Dank dafür.

Autor: STM8-C-Anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für Deine Antwort. Meine Beiträge wirken beim Nachlesen 
etwas undankbar, so ist es aber nicht gemeint. Ich freue mich immer, 
wenn jemand für mich brauchbare Software veröffentlicht. Die Fragen 
kamen dann beim Beschäftigen damit auf. Ich mag eben gern einfache, 
klare Strukturen. (hab früher viel hobbymäßig Turbo-Pascal programmiert)

Autor: Walter Jo (oberallgeier)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Aber klar doch, die Main ist wirklich sehr einfach gestrickt gewesen.

Danke für die Mühe. Einfach sicher - wenn man weiß, wo´s lang geht. Ich 
geh in den nächsten zwei, drei Tagen dran (danach bin ich etwas wech..).

Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
STM8-C-Anfänger schrieb:
> Vielen Dank für Deine Antwort. Meine Beiträge wirken beim Nachlesen
> etwas undankbar, so ist es aber nicht gemeint. Ich freue mich immer,
> wenn jemand für mich brauchbare Software veröffentlicht. Die Fragen
> kamen dann beim Beschäftigen damit auf. Ich mag eben gern einfache,
> klare Strukturen. (hab früher viel hobbymäßig Turbo-Pascal programmiert)

Es hat ein wenig länger gedauert. Grund hierfür waren zwei Fehler. 1. 
ist mir mein AVRISP krepiert. Das dauerte allerdings einige Zeit dies 
festzustellen da ich zunächst meinen Aufbau in Verdacht hatte.
Der 2. Fehler war, dass mein SH1106-Display ebenfalls das Zeitliche 
gesegnet hat.
Diese zwei Fehler haben mich einige überflüssige Oszilloskop-Einsätze 
gekostet.
However, im Anhang die modifizierte Grafik-Lib. Jetzt wird lcd_command 
und lcd_data auch wesentlich sinnvoller eingesetzt und die 
i2c-Funktionen werden nur noch da, in der lcd_command und der lcd_data, 
verwendet.

Walter J. schrieb:
> Danke für die Mühe. Einfach sicher - wenn man weiß, wo´s lang geht. Ich
> geh in den nächsten zwei, drei Tagen dran (danach bin ich etwas wech..).

Ich hab mal ein paar Kommentare eingepflegt, vielleicht macht das es 
noch einfacher.
/* Name: main.c
 * Author: Michael Koehler
 * Copyright: <insert your copyright message here>
 * License: <insert your license reference here>
 */

#include "main.h"
#include "lcd_gfx.h"
#include <stdlib.h>

volatile uint16_t overflows;

int main(void)
{
    // Init und Setup Atmega328
    setUpAvr();
    // LCD Contrast einstellen
    lcd_set_contrast(0x0f);
    // Variable definieren zur Aufnahme/Anzeige der verstrichenen Zeit
  char time[6];
    // diverse Hilfsvariablen zum Zeichnen eines wachsenden/schrumpfenden Balkens
  uint8_t myVar = 0;
  uint8_t dir =0;
    // PB5 als Ausgang schalten (Beim Arduino Uno haengt hier eine LED dran: PIN 13)
  DDRB |= (1 << PB5);
    lcd_clrscr();
  // zeichnen eines Kreises in den Buffer
  //lcd_drawCircle(30, 30, 15, WHITE);
    // zeichnen eines gefuellten Kreises in den Buffer
  //lcd_fillCircle(70, 40, 10, WHITE);
    for(;;){
        // LED toggeln
    PORTB ^= (1 << PB5);
        lcd_drawPixel(20, 20, WHITE);
        // zeichnen eines Rechtecks
    lcd_drawRect(10, 10, 40, 40, WHITE);
        // zeichnen eines immer groeßer bzw. kleiner werdenen
        // Balkens (in den Buffer). Ob der Balken wächst oder schrumpft hängt von
        // seine aktuellen Position ab. Er wächst von links nach rechts bzw.
        // schrumpft von rechts nach links
     if( dir == 0) {
      lcd_drawLine(0+myVar, 54, 0+myVar, 46, WHITE);
      myVar++;
       if(myVar == 128) dir = 1;
    } else {
      if(myVar == 0) dir=0;
      myVar--;
      lcd_drawLine(myVar, 46, myVar, 54, BLACK);
      
    }
    // zeichnen eines Kreises in den Buffer
    lcd_drawCircle(65, 25, 15, WHITE);
        // zeichnen eines X mit Linien in den Buffer
    lcd_drawLine(90, 10, 120, 40, WHITE);
    lcd_drawLine(90, 40, 120, 10, WHITE);
    lcd_drawLine(105, 10, 105, 40, WHITE);
    lcd_drawLine(90, 25, 120, 25, WHITE);
        // schreiben von Text in die erste Zeile in den Buffer
    lcd_gotoxy(0,0);
    lcd_puts_p(PSTR("M. Köhler 2016/2017"));
        // uebertragen des Buffers an das Display
    lcd_display();
        // timer1 für die Zeitmessung anhalten
    TCCR1B = 0;
        // timer1 Value wird in eine Zeit umgerechnet und in einen String gewandelt fuer
        // die Anzeige
    dtostrf((overflows*65536UL+TCNT1)*1.0/16.0e3,
        6,
        3,
        time);
        // hilfsvariable zur Zeitmessung wird zurueckgesetzt
    overflows = 0;
        // timer1 Value wird zurueckgesetzt
    TCNT1 = 0;
        // timer1 wird wieder gestartet
    TCCR1B = 1 << CS10;
        // Schreiben von Text in die letzte Zeile (in den Buffer)
    lcd_gotoxy(0,7);
        // Text wird aus dem Flash geholt und in den Buffer geschrieben,
        // hier erstmal den vorhandene Text loeschen
        lcd_puts_p(PSTR("                     "));
        // Cursor fuer Text wird wieder an den Anfang der letzten Zeile gesetzt
    lcd_gotoxy(0,7);
        // Text wird aus dem Flash geholt
    lcd_puts_p(PSTR("Refresh: "));
        // Die umgerechnete Zeit wird in den Buffer geschrieben
    lcd_puts(time);
        // Text wird aus dem Flash geholt
    lcd_puts_p(PSTR("ms"));
    }
    return 0;   /* never reached */
}

void setUpAvr(void){
    /* insert your hardware initialization here */
    // Timer1 Overflow Interrupt einschalten
  TIMSK1 |= (1 << TOIE1);
    // LCD Initialisieren
  lcd_init(LCD_DISP_ON);
    // globale Interrupts einschalten
  sei();
}

ISR(TIMER1_OVF_vect){
    // Hilfsvariable um eins erhoehen
  overflows++;
}

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
cool

ich hoffe ich kann es bald nutzen
meine OLED 0.96" und 1.3" sind angekommen

ich hänge nur noch an einer anderen Baustelle fest.

Autor: STM-8-C-Anfänger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> However, im Anhang die modifizierte Grafik-Lib.

Vielen Dank dafür. Leider hab ich erst nächste Woche Zeit und Ruhe, mal 
tiefer reinzugucken. Deine Änderungen machen mir die Portierung sicher 
deutlich leichter.

Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Der Fehlerteufel hat sich eingeschlichen und es gibt den ein und anderen 
Klammerfehler im SH1106-Bereich. Ein freundlicher User hat mich drauf 
hingewiesen. Die hier angehängte Version sollte nun aber problemlos auch 
mit SH1106-Displays funktionieren (wegen meines defekten SH1106-Displays 
kann ich derzeit leider nicht testen) ;)

Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Auf manche Sachen stößt man auch selbst. Der Library fehlte noch eine 
Funktion zum Schreiben vom Text, der im EEPROM des AVRs liegt. Diese 
Funktion habe ich nun auch ergänzt (void lcd_puts_e(const char* 
eemem_s)).

Autor: Walter Jo (oberallgeier)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, dass ich die versprochenen Tests noch nicht habe, nicht mal 
angefangen hatte. Ich komm grad vom Schifahren zurück - einfach zu viel 
Tage mit zu viel Spass beim Cross (ohne KO!) und so.

Kurz:
Die ersten Arbeiten ergaben etliche Fehler (Lib´s fehlten eben) wie z.B.
"..
adafruit_ssd1306.h

../Adafruit_SSD1306.h:25:23: error: WProgram.h: No such file or 
directory
../Adafruit_SSD1306.h:48:17: error: SPI.h: No such file or directory
../Adafruit_SSD1306.h:49:26: error: Adafruit_GFX.h: No such file or 
directory
../Adafruit_SSD1306.h:144: error: expected '=', ',', ';', 'asm' or 
'__attribute__' before 'Adafruit_SSD1306'
.."


Danke sylaina für die ausführlich(er) kommentierte Version vom 
28.03.2017 11:09, da gabs auf die Schnelle auch noch Meckereien vom 
Compiler :-/:

"Build started 3.4.2017 at 12:00:34
avr-gcc  -mmcu=atmega328p -Wall -gdwarf-2 -std=gnu99 -DF_CPU=16000000UL 
-Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD 
-MP -MT arno_OLED1306.o -MF dep/arno_OLED1306.o.d  -c 
../arno_OLED1306.c
../arno_OLED1306.c:7:18: error: main.h: No such file or directory
../arno_OLED1306.c: In function 'main':
../arno_OLED1306.c:16: warning: implicit declaration of function 
'setUpAvr'
../arno_OLED1306.c: At top level:
../arno_OLED1306.c:94: warning: conflicting types for 'setUpAvr'
../arno_OLED1306.c:16: warning: previous implicit declaration of 
'setUpAvr' was here
../arno_OLED1306.c: In function 'setUpAvr':
../arno_OLED1306.c:101: warning: implicit declaration of function 'sei'
../arno_OLED1306.c: At top level:
../arno_OLED1306.c:104: warning: return type defaults to 'int'
../arno_OLED1306.c: In function 'ISR':
../arno_OLED1306.c:104: warning: type of '__vector_13' defaults to 'int'
../arno_OLED1306.c:107: warning: control reaches end of non-void 
function
make: *** [arno_OLED1306.o] Error 1
Build failed with 1 errors and 7 warnings..."

Ich werde mir mal selbst die ganze Geschichte ausführlich zu Gemüte 
führen.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal nur zu meiner lib:

Hast du den Code 1:1 kopiert? Zu meiner mein.c gibt es eine main.h die 
u.a. die Funktion setUpAvr() bekannt macht. Entweder kopierst du die 
setUpAvr in der main.c vor die main()-Funktion oder erstellst noch 
passend eine Header-Datei, den Code packe ich dir hier ans Ende des Post 
(als main.h abspeichern).
Der Rest der Fehlermeldungen sind alles Folgefehler weil die main.h 
fehlt (u.a. wird in der main.h die avr/interrupt.h includiert ;))
//
//  main.h
//  OLED-Demo
//
//  Created by Michael Köhler
//
//

#ifndef main_h
#define main_h
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

void setUpAvr(void);

#endif /* main_h */

: Bearbeitet durch User
Autor: Johannes R. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Habe die Bibliothek eben mit einem kleineren OLED-Display (0,69 Zoll, 
96x16 Pixel) getestet, leider mit mäßigem Erfolg.

Bild OLED_1.jpg zeigt das Ergebnis, wenn ich die oben gepostete main.c 
ohne Anpassungen verwende. Dass das nicht alles auf den kleinen 
Bildschirm passt ist verständlich, aber es wirkt noch dazu verzerrt.

Bild OLED_2.jpg zeigt den Versuch, nur eine Zeile Text anzuzeigen:
  lcd_gotoxy(0,6);
  lcd_puts_p(PSTR("mikrocontroller.net"));
  lcd_display();

Die DISPLAY_WIDTH und DISPLAY_HEIGHT aus der lcd_gfx.h hab ich dazu 
nicht verändert, aber wenn ich sie auf 96 und 16 setze funktioniert 
überhaupt nichts mehr.
Ich vermute, dass ich auch den Startup-Code in der init_sequence[] 
anpassen müsste. Leider bin ich diesbezüglich aus dem Datenblatt für den 
SSD1306 nicht recht schlau geworden. Hat jemand eine Idee, welche 
Parameter dabei eine Rolle spielen?

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Versuche mal die Init-Sequence nach diesem Datenblatt (vgl. Seite 10 
ff.) anzupassen 
http://www.buydisplay.com/download/manual/ER-OLED0...

Ich hab das Display leider nicht, daher kann ich leider nicht testen, 
denke aber auch, dass hier die Init-Sequence angepasst werden muss.

Autor: Johannes R. (Gast)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Danke für die schnelle Antwort! Das Dokument kannte ich noch nicht. Hab 
jetzt ein bisschen mit der Initialisierungssequenz auf Seite 12 gespielt 
und herausgefunden, dass nur zwei Änderungen gegenüber deinem Code nötig 
sind.

Die "multiplex ratio" muss auf Anzahl der Zeilen minus eins 
(DISPLAY_HEIGHT-1) gesetzt werden, also 0x0F für 16 Zeilen statt 0x3F 
für 64 Zeilen.
  0xA8, 0x0F,             // Set multiplex ratio
Das ist allgemeingültig und könntest du als "0xA8, DISPLAY_HEIGHT-1," 
auch in die Bibliothek übernehmen.

Die zweite Änderung hängt mit der internen Verschaltung von SSD1306 und 
Display zusammen. Ich benötige die "Sequential COM pin configuration" an 
Stelle der bei dir verwendeten "Alternative COM pin configuration".
  0xDA, 0x02,             // Set com pins hardware configuration

So ganz hundertprozentig funktioniert es leider immer noch nicht. 
Angehängtes Bild OLED_3.jpg zeigt das Ergebnis für folgenden Code:
  lcd_gotoxy(0,0);
  lcd_puts_p(PSTR("1234567890123456"));
  lcd_gotoxy(0,1);
  lcd_puts_p(PSTR("abcdefghijklmnop"));
  lcd_gotoxy(0,2);
  lcd_puts_p(PSTR("ABCDEFGHIJKLMNOP"));
  lcd_display();
Irgendwie ist die zweite Zeile also um 8 Pixel nach links verschoben, 
und die (eigentlich unsichtbare) dritte Zeile fängt schon in der zweiten 
Zeile an. Ich fürchte da muss ich noch ein bisschen länger mit den 
Parametern spielen.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das mit dem Display Ratio hab ich direkt mal umgesetzt ;)

Die Verschiebung um 8 Pixel erscheint mir interessant zu sein. Da bin 
ich mal gespannt was das sein wird, weiß aber noch nicht wann ich dazu 
komme mir das genauer anzuschaun.

Die Sache mit der dritten Zeile liegt einfach darin begründet, dass das 
lcd_gotoxy schaut ob die Parameter auch zulässig sind, also x und y 
innerhalb des Displays liegen. Ist dem nicht so wird die Funktion direkt 
wieder verlassen und der Cursor nicht weiter bewegt,  in deinem Fall 
bleibt er dann am Ende des Textes der zweiten Zeile stehen und da wird 
dann der Text hingeschrieben, der eigentlich für die dritte Zeile 
bestimmt war.

Autor: Johannes R. (Gast)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Den Grund für die Verschiebung der zweiten Zeile habe ich gefunden. Der 
SSD1306 adressiert intern immer 128x64 Pixel und merkt nicht, wenn ein 
kleineres Display angeschlossen ist. Wenn man ihm einen schmäleren 
Puffer übergibt landet der Anfang der zweiten Zeile am (unsichtbaren) 
Ende der ersten Zeile, was in meinem Fall zu einer Verschiebung um 32 
Pixel führt.
Beim Schreiben von displayBuffer[] muss man also die fehlenden Spalten 
mit Dummy-Bytes auffüllen.
void lcd_writeBuffer() {
  lcd_send_i2c_start();
  lcd_send_i2c_byte(0x40);
  for (uint8_t i=0; i<(DISPLAY_HEIGHT/8); i++) {
    for (uint8_t j=0; j<DISPLAY_WIDTH; j++) {
      lcd_send_i2c_byte(displayBuffer[i*DISPLAY_WIDTH + j]);
    }
    for (uint8_t h=DISPLAY_WIDTH; h<128; h++) {
      lcd_send_i2c_byte(0x00);
    }
  }
  lcd_send_i2c_stop();
}

OLED_4.jpg zeigt das Ergebnis für den folgenden Code:
  lcd_gotoxy(0,0);
  lcd_puts_p(PSTR("1234567890123456"));
  lcd_gotoxy(0,1);
  lcd_puts_p(PSTR("abcdefghijklmnop"));
  lcd_display();

Mit den Grafikfunktionen hatte ich auch noch Probleme und musste die 
Funktion lcd_drawPixel() anpassen. Was war der Grund, hier 
DISPLAY_HEIGHT zu referenzieren? Statt durch DISPLAY_HEIGHT/8 wird 
einfach durch 8 geteilt, weil jede Page über acht Zeilen reicht. Die 
Höhe des Displays spielt keine Rolle.
  displayBuffer[(uint8_t)(y / 8) * DISPLAY_WIDTH + x] |= (1 << (y % 8));

Jetzt funktioniert alles. Danke für den Code!

Autor: Nico W. (nico_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Problem hatte ich auch mal. Das kann man aber mit der 
Initialisierungssequenz korrekt einstellen. Bin mir nicht ganz sicher 
mehr wo das war. Irgendwas mit addressing mode.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Auf manche Sachen stößt man auch selbst.

leider nicht auf viele

ich versuche gerade den Code in meinen Arduino zu integrieren
void lcd_init(uint8_t dispAttr)
{ if(LCD_INIT_I2C == YES) i2c_init();
  uint8_t commandSequence[sizeof(init_sequence)+1];
  for(uint8_t my_lcd_init_i = 0; my_lcd_init_i < sizeof (init_sequence); my_lcd_init_i++)

du hattest als Laufvariable i gewählt, OK mache ich auch öfter, aber bei 
der Integration was tun wenn i schon anderweitig belegt ist?

Deswegen mache ich immer eindeutige Namen wie my_lcd_init_i

hilft aber hier nicht, wo klemmst denn?

ich mag ja fremde Codes intergrieren, aber derlei Stolpersteine 
vermiesen es, eigentlich wollte ich meine Probleme minimieren und keine 
neuen Nebenschauplätze eröffnen.

lcd_gfx.c: In function 'lcd_init':
lcd_gfx.c:240: error: 'for' loop initial declaration used outside C99 
mode

: Bearbeitet durch Moderator
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> was tun wenn i schon anderweitig belegt ist?

Interessiert kein Schwein, solange es kein Makro ist (die benennt man
daher ja auch üblicherweise in GROSSBUCHSTABEN), denn die Variable
ist ausschließlich lokal zur Schleife und kollidiert dabei mit rein
gar nichts.

> lcd_gfx.c:240: error: 'for' loop initial declaration used outside C99
> mode

Dann schalt' halt den C99-Modus endlich ein, wenn du's bislang noch
nicht getan hast.  -std=c99 oder -std=gnu99.  Ist eigentlich
unverständlich, warum der Default beim GCC nach wie vor -std=gnu89
ist.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Dann schalt' halt den C99-Modus endlich ein, wenn du's bislang noch
> nicht getan hast.

bin halt kein Informatiker und stolpere das erste Mal drüber,

Jörg W. schrieb:
> Ist eigentlich
> unverständlich, warum der Default beim GCC nach wie vor -std=gnu89
> ist.

da hast du Recht, an wen wendet sich denn der gcc an Hobbyprogger wie 
mich.

Allerding zur LIB

warum in einer Funktion in jeder for Schleife zig mal for ( uint8_i = 
0...

steht ist mir auch ein Rätsel


wenn ich in einer Funktion i öfter benötige, dann wird es in der 
Funktion einmal am Anfang initialisiert.

uint8_t i=0;

das muss nicht in jeder for Schleife, wer tippt denn so gerne uint8_t ?

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Joachim B. schrieb:

> da hast du Recht, an wen wendet sich denn der gcc an Hobbyprogger wie
> mich.

Keineswegs, der ist genausogut profesionell im Einsatz.

C99 ist ja nun mittlerweile durchaus schon recht betagt (C11 gibt's
bereits als Nachfolger seit einiger Zeit).  Ein großer Teil der
C99-Erweiterungen war im GCC bereits vor dem 1999er Standard aktiv
und ist daher in der Voreinstellung -std=gnu89 schon enthalten, die
Schleifenvariablen der for-Anweisung sind da die wohl prominenteste
Ausnahme.


> warum in einer Funktion in jeder for Schleife zig mal for ( uint8_i =
> 0...
>
> steht ist mir auch ein Rätsel

Weil damit der Gültigkeitsbereich der Variablen (“scope”) explizit
nur auf die Anweisung innerhalb der for-Schleife limitiert wird.
Limitierung des Geltungsbereichs einer Variablen auf das notwendige
Minimum ist durchaus guter Stil.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Weil damit der Gültigkeitsbereich der Variablen (“scope”) explizit
> nur auf die Anweisung innerhalb der for-Schleife limitiert wird.
> Limitierung des Geltungsbereichs einer Variablen auf das notwendige
> Minimum ist durchaus guter Stil.

OK aber das ist mir noch unklar, werden lokale Variablen nicht immer auf 
dem Stack initialisiert?

In jeder for Schleife neu?
Bringt das nicht unnötigen overhead?

Warum sollte man das innerhalb einer Funktion machen? mal abgesehen vom 
Stil.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:

> OK aber das ist mir noch unklar, werden lokale Variablen nicht immer auf
> dem Stack initialisiert?

Es geht hier nicht um den Speicherort der Variablen (der wird am
Ende sehr wahrscheinlich ein Register sein), sondern um den
Gültigkeitsbereich.
  for (int i = 0; i < 10; i++)
     tuwas(i);

Nach der Zeile „tuwas(i);“ gibt es die Variable „i“ bereits nicht
mehr.  Das ist danach sowohl dem Menschen als auch dem Compiler klar.

Das nächste „i“ in der nächsten Schleife ist dann bereits eine neue
Variable, die mehr oder minder zufällig genauso heißt.

Der Compiler würde sie natürlich auch in diesem Falle:
  int i;
  for (i = 0; i < 10; i++)
    tuwas(i);

hernach wegwerfen, wenn mit dem „i“ nichts mehr gemacht wird, aber
es könnte dann später nochmal jemand daherkommen, und er müsste für
Code, der gar nichts damit zu tun hat, u. U. noch Verrenkungen
machen, um den Wert von „i“ zu erhalten (sofern er nicht folgern
kann, dass dieser später sowieso auf jeden Fall überschrieben wird).

Da der Gültigkeitsbereich so gut überschaubar ist, muss man eben auch
nicht das_ist_i_in_der_funktion_xyz schreiben, sondern kann sich eine
kurze und knappe Benennung leisten, denn der Betrachter des Codes
kann sofort erkennen, auf wie wenigen Zeilen die Variable tatsächlich
benutzt werden kann.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK ich gebe auf,

kann den Schalter C99 nicht finden, bekomme bei jeder Fehlermeldung die 
ich beseitige neue.

Habe gegoogelt wo man beim Arduino den C99 Schalter setzt, nix gefunden 
aber zurück zum A-Studio 4.18 möchte ich an dieser Stelle nicht.

Das wars dann hier für mich, ich kann das Display ja ansprechen nur eben 
mit einer dickeren LIB statt schlank!

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> Habe gegoogelt wo man beim Arduino den C99 Schalter setzt

https://github.com/01org/corelibs-arduino101/issues/220

Du müsstest also wohl irgendwo eine Datei platform.txt finden, in der
du diese Option ergänzen kannst.

Du kannst auch probieren, ob Michaels Code sich als C++ übersetzen
lässt, indem du die Datei nicht „lcd.c“, sondern „lcd.cc“ oder auch
Arduino-mäßig „lcd.ino“ nennst.  C++ kennt diese Variante der
Laufvariablen in for-Anweisungen schon lange.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> oder auch
> Arduino-mäßig „lcd.ino“ nennst.

OK ich versuche es nochmal, danke dir für deine Unterstützung!

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Du kannst auch probieren, ob Michaels Code sich als C++ übersetzen lässt

Bei mir lässt er sich klaglos auf diese Weise compilieren.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hmmm, kompiliert fehlerfrei mit *.ino
  lcd_init(LCD_INIT_I2C);
  lcd_clrscr();
  lcd_gotoxy(0, 0);
  lcd_puts("huhu");

aber Display beibt duster,

welche Adresse nutzt er denn? habe ich noch nicht gefunden

meine erfolgreichen Versuche mit anderer LIB waren so
      switch(address<<1)
      { case 0x78:
          DEBUG_PRINTLN(F(" I2C OLED"));
          i2c_test_flags|=(1<<I2C_OLED);          
          break;
        case 0xA0:
          DEBUG_PRINTLN(F(" I2C EEPROM"));
          i2c_test_flags|=(1<<I2C_EEPROM);          
          break;
        case 0xD0:
          Wire.beginTransmission(DS1307_ID);
          printIIC(0x3F);
          (Wire.endTransmission()) ? i2c_test_flags|=(1<<I2C_RTC_3231) : i2c_test_flags|=(1<<I2C_RTC_1307);
          (i2c_test_flags&(1<<I2C_RTC_3231)) ? DEBUG_PRINTLN(F(" DS3231 RTC")) : DEBUG_PRINTLN(F(" DS1307 RTC"));
          break;
        default:
          DEBUG_PRINTLN(F(""));
          break;

: Bearbeitet durch Moderator
Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das muss noch was mit den I2C Routinen zu tun haben
#if defined(ARDUINO) 
  #if ARDUINO >= 100
//check busy
    Wire.beginTransmission(DS1307_ID);
    printIIC(STATUS_REGISTER);
    Wire.endTransmission();
    Wire.requestFrom( ( DS1307_ID ), 1);       // request Status Register 1 Byte
    if ((readIIC() & 0x00ff) & (1<<BSY))       // auf busy pruefen mit 0x00ff unddieren weil Arduino 16 Bit liest aber nur 8 Bit kommen.
      i2c_test_flags|=(1<<I2C_RTC_TEMP_BSY);           // Flag busy setzen
    else
      i2c_test_flags&=~(1<<I2C_RTC_TEMP_BSY);          // Flag busy loeschen
    Wire.endTransmission();
    if( !(i2c_test_flags&(1<<I2C_RTC_TEMP_BSY)) )
    { // read Temp
      Wire.beginTransmission(DS1307_ID);       // DS anwaehlen
      printIIC(MSB_TEMP);                      // MSB Byte anwaehlen
      Wire.endTransmission();        
      Wire.requestFrom( ( DS1307_ID ), 2);     // request temp Hi und Temp low 2 Byte
      __temp = (readIIC() & 0x00ff);           // mit 0x00ff unddieren weil Arduino 16 Bit liest aber nur 8 Bit kommen.
      __temp <<= 8;      
      __temp |= (readIIC() & 0x00ff);          // mit 0x00ff unddieren weil Arduino 16 Bit liest aber nur 8 Bit kommen.    
      Wire.endTransmission();
  #else 
    #include "Arduino_<_100_undefiniert.h"     //#warning gibt es ja in der Arduino Ide nicht
  #endif // #if ARDUINO < 100
#else // #if not(ARDUINO) 
      // nutze die AVR Lib I2C von Fleury      http://homepage.hispeed.ch/peterfleury/i2cmaster.zip
      i2c_start_wait(DS3231+I2C_WRITE);        // set device address and write mode
      i2c_write(MSB_TEMP);
      i2c_rep_start(DS3231+I2C_READ);          // set device address and read mode
      __temp=i2c_readAck();
      __temp<<=8;
      __temp|=i2c_readNak();
      i2c_stop();
#endif // # AVR != ARDUINO

: Bearbeitet durch Moderator
Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mist, auf dem Steckbrett versteckt, die alten Augen

So nun bin ich weiter, seine OLED Adresse ist identisch mit meiner!

hängt noch am I2C

mit lcd_init gibt es Schrott auf Serial.print in einer Endlosschleife, 
dabei sollte da noch nichts passieren:
void setup() {
  Serial.begin(19200);
  Serial.println();
  Serial.println();
  Serial.println();
  Serial.println();
  Serial.print(F("File:       "));  Serial.println(_name()); 
  compile_time();
  Serial.print(F("kompiliert: "));  Serial.println(c_str); 
  Serial.println();
  lcd_init(LCD_INIT_I2C);
/*
  lcd_clrscr();
  lcd_gotoxy(0, 0);
  lcd_puts("huhu");
*/
}

sorry Mod!für code Tags -> hier gleich verbessert

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> mit lcd_init gibt es Schrott auf Serial.print in einer Endlosschleife,
> dabei sollte da noch nichts passieren

Läuft da irgendwo der RAM über (ggf. auch Stack)?

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
noch ziemlich unwahrscheinlich, ist echt schmal:

doch das war es!!!!

mit OLED

Binäre Sketchgröße: 8.964 Bytes (von einem Maximum von 30.720 Bytes)
advanced_OK.cpp.elf"
   text    data     bss     dec     hex
   8724     240    1760   10724    29e4

hey warum verbraucht die schmale LIB soviel SRAM?

ohne OLED

mein pures Proggi
Binäre Sketchgröße: 6.294 Bytes (von einem Maximum von 30.720 Bytes)
dvanced_OK.cpp.elf"
   text    data     bss     dec     hex
   6070     224     543    6837    1ab5

Autor: Joachim B. (jar)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
da komme ich ja im SRAM mit RTC, OLED und NOKIA schmaler hin

Binäre Sketchgröße: 30.134 Bytes (von einem Maximum von 30.720 Bytes)
C_OLE_EEP_OK.cpp.elf"
   text    data     bss     dec     hex
  29560     574    1031   31165    79bd

: Bearbeitet durch User
Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also 1k statischer Displaybuffer ist mir zu viel

#define DISPLAYSIZE    DISPLAY_WIDTH*DISPLAY_HEIGHT/8
// 128*64/8

nun verstehe ich nix mehr:

M. K. schrieb:
> Meine Lib arbeitet ohne Puffer, daher wird hierfür auch kein statischer
> RAM für einen Puffer benötigt. Ich hab es nicht getestet, aber auch 128
> Byte RAM müssten locker ausreichend sein.

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Diese Aussage bezieht sich auf die eingangs gepostete Bibliothek zum
Schreiben von Text auf so ein Display.

Die von dir genannte Variable dagegen gehört in die Grafiklib, die
Michael später gepostet hat.  Bei einer solchen ist der Puffer
praktisch unumgänglich, hatten wir weiter oben schon diskutiert.

„Jetzt benötigt die Lib leider schon etwas mehr als 4kByte Flash sowie 
1029 Byte SRAM.“ schrieb er dann auch selbst.

: Bearbeitet durch Moderator
Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Ich finde ich Michaels Code keine einzige globale oder statische
> RAM-Variable.

im letzten zip
Beitrag "Re: SSD1306 Library zum Darstellen von Text auf OLED Displays"

in lcd_gfx.h
#define DISPLAYSIZE    DISPLAY_WIDTH*DISPLAY_HEIGHT/8

genutzt von in
lcd_gfx.ino // lcd_gfx.c

static uint8_t displayBuffer[DISPLAYSIZE];

wie soll ich das sonst nennen?

ist das nicht statisch und na ja local/global?

ich bin ja kein Progger aber diese Zeile dachte ich verstanden zu haben

static uint8_t displayBuffer[DISPLAYSIZE];

ergibt 1k SRAM Verbrauch bei 128 x 64 Pixel / 8

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, hatte erst danach bemerkt, dass es ja zwei Libs sind und
daher meinen Beitrag nochmal geändert.

Mit chronischer RAM-Knappheit hast du auf diesen Displays keine Chance,
auch noch Grafik zu machen.  Das liegt einfach daran, dass sich die
Pixel ja nicht wieder auslesen lassen.  Da acht Pixel in einem Byte
liegen (Monochrom), muss man daher eine Schattenkopie des Displayinhalts
im RAM halten, wenn man Grafik machen will.

Seine Text-Lib war genau darauf ausgelegt, diese Schattenkopie nicht
zu brauchen, aber dann kann man eben nur Text darstellen.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Seine Text-Lib war genau darauf ausgelegt, diese Schattenkopie nicht
> zu brauchen, aber dann kann man eben nur Text darstellen.

Genau, das Hauptaugenmerk lag bei mir auf der Variante, die nur Text 
darstellt. Diese Lib verbraucht keinen RAM. Die Grafik-Lib kommt um den 
RAM nicht rum wenn man nicht überzeichnen will.

Willst du, Joachim, also nur Text darstellen dann benutze die 
lcd.c/lcd.h Lib, die ich weiter oben gepostet habe. Diese Lib alleine 
braucht keine 2k Flash-Speicher.

Und bzgl. der Arduino-Umgebung: Da hab ich leider absolut keine Ahnung 
wie gut sich meine Lib in diese Umgebung integrieren lässt. Das sind so 
Dinge, die auf meine immer länger werdende ToDo-Liste kommt.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Die Grafik-Lib kommt um den RAM nicht rum wenn man nicht überzeichnen
> will.

Das führt zu dem Paradoxon: wenn man einen kleinen Controller mit wenig
SRAM hat, kommt man am Ende besser, statt eines monochromen
OLED-Displays ein RGB-Display zu nehmen.  Dort beschreibt man mit einem
Byte immer ein Pixel auf einmal, braucht daher keinen Puffer und kann
trotzdem Grafik machen.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Willst du, Joachim, also nur Text darstellen dann benutze die
> lcd.c/lcd.h Lib, die ich weiter oben gepostet habe. Diese Lib alleine
> braucht keine 2k Flash-Speicher.

wo weiter oben, du hattest viel gepostet, hast du bitte einen Link?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gleich im ersten Beitrag.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Gleich im ersten Beitrag.

danach kamen aber noch updates wegen verschiedener Controller 13xx zu 
11xx wegen der Pixelgrenze und Bugs wurden später auch noch berichtigt, 
warum also zur ersten ZIP ?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> danach kamen aber noch updates

Stimmt, ein Update sehe ich noch:

Beitrag "Re: SSD1306 Library zum Darstellen von Text auf OLED Displays"

@Michael: soll ich mal deinen ersten Threadbeitrag editieren und die
Links auf die jeweils aktuelle Version einfügen?

Autor: Johannes R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nico W. schrieb:
> Das Problem hatte ich auch mal. Das kann man aber mit der
> Initialisierungssequenz korrekt einstellen. Bin mir nicht ganz sicher
> mehr wo das war. Irgendwas mit addressing mode.

Ja, dachte ich auch. Die Lib nutzt den "horizontal addressing mode", 
also hatte ich in der Init-Sequenz den Befehl 0x21 ausprobiert um Start- 
sowie Endadresse der zu nutzenden Spalten im RAM zu setzen:
  0x21, 0x00, DISPLAY_WIDTH-1, 

Leider zunächst erfolglos. Auf deinen Beitrag hin habe ich mir das eben 
aber nochmal angeschaut und festgestellt, dass die Funktion lcd_gotoxy() 
den gleichen Befehl nutzt und die Einstellung aus der Init-Sequenz 
überschrieben hat. Wenn man die betreffende Zeile abändert funktioniert 
es. Damit ist dann auch meine Funktion lcd_writeBuffer() von oben 
überflüssig.
  uint8_t commandSequence[] = {0xb0+y, 0x21, x, DISPLAY_WIDTH-1 };

Autor: DraconiX (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cool danke, schön gemacht. Nur bei der GotoXY hat es bei meinem Display 
nicht funktioniert, ich stelle heute Abend mal meine Source für die 
Funktion ein.

Das man um den 1K RAM nicht drumrum kommt liegt an der 
Speicheraddressierung des SSD1306.

Hätte er einen Framebuffer wäre das alles halb so wild :-D

Autor: DraconiX (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal auf einem STM32F1... Grafikbibliothek und 1Bit Bild.

Autor: M. Köhler (sylaina)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
DraconiX schrieb:
> Das man um den 1K RAM nicht drumrum kommt liegt an der
> Speicheraddressierung des SSD1306.

Nö, das liegt an der Schnittstelle. Bei i2c und spi kann man nur 
schreibend auf das Display zugreifen. Geht man ans parallele Interface 
des SSD1306 ran kann man das Display auch auslesen, dann braucht man den 
RAM nicht.

Jörg W. schrieb:
> @Michael: soll ich mal deinen ersten Threadbeitrag editieren und die
> Links auf die jeweils aktuelle Version einfügen?

Gute Idee, versuche ich gleich umzusetzen. Ich hab auch überlegt bei 
github ein Repository  jeweils für die Text-Variante und für die 
Grafik-Variante zu erstellen.

Joachim B. schrieb:
> wo weiter oben, du hattest viel gepostet, hast du bitte einen Link?

Ich hab beide Varianten in der aktuellen Version diesem Beitrag 
angehangen, aus dem ersten Beitrag werde ich gleich noch hierauf 
verlinken lassen.

EDIT: Jörg, ich darf den ersten Post nicht mehr editieren. Das ist wohl 
eine Option, die nur Moderatoren/Administratoren zur Verfügung steht, 
nicht aber "normalen" Usern. Wärst du vielleicht so nett?

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht lesenswert
M. K. schrieb:
> Wärst du vielleicht so nett?

Ja, natürlich, darum hatte ich dir das ja angeboten.

Autor: DraconiX (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Geht man ans parallele Interface des SSD1306 ran kann man das Display
> auch auslesen, dann braucht man den RAM nicht.

Ah okay, das wusste ich nicht! :-D Danke für die Info, über die 
parallele Kommunikation hab ich mir beim SSD1306 noch keine Gedanken 
gemacht / im DB gestöbert weil es kaum Displays gibt die dies so 
ausgeführt haben.

Theoretisch, gerade auf großen MCs, könnte man sich auch Gedanken machen 
darüber immer nur eine Page vorzuhalten - dies wiederum braucht aber 
mehr Flash und Rechenzeit, da ja alle 8 Pages nacheinander neu berechnet 
und geschrieben werden müssen. Irgendwie...

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> M. K. schrieb:
>> Wärst du vielleicht so nett?
>
> Ja, natürlich, darum hatte ich dir das ja angeboten.

irgendwas ist da schiefgelaufen,

die SH1106 Wahl ist wieder nicht drin, ich bekomme verschobene Pixel und 
bei   lcd_clrscr(); habe ich Rauschen auf dem Screen!
// init OLED
  lcd_init(LCD_INIT_I2C);
  lcd_clrscr();
  lcd_puts("huhu");
  lcd_gotoxy(8, 8);
//  lcd_gotoxy(16, 16);
  lcd_puts("hallo");

sollte ja nicht so schwer werden :)

: Bearbeitet durch User
Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ach ich liebe debugging

lcd.ino: In function 'void lcd_clrscr()':
lcd:324: error: invalid conversion from 'int' to 'uint8_t*'
lcd:324: error: initializing argument 1 of 'void lcd_data(uint8_t*, 
uint16_t)'

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hier klemmts irgendwo
#elif defined SH1106
    for (uint8_t j = 0; j <= DISPLAY_HEIGHT/8; j++){
        for (uint8_t i = 0; i < DISPLAY_WIDTH ; i++) {
//          vvvvvvvvvvvvvv  

            lcd_data(0x0f, 1);
//                   ^^^^

        }
        lcd_gotoxy(0,j);
    }
  
#endif

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tja, manches, was bei C einfach noch so durchgeht, ist bei C++ nicht
mehr zulässig, da ist das Typkonzept pingeliger.

Aber: irgendwas passt da nicht.  Wenn lcd_data() als erstes einen
Zeiger haben will, dann hat das einen Grund, ihm einfach 0x0F zu
übergeben, kollidiert zu Recht.

Probier's mal damit:
void lcd_clrscr(void){
    lcd_home();
    uint8_t data = 0x0f;
#if defined SSD1306
    for (uint16_t i = 0; i < DISPLAYSIZE; i++) {
        lcd_data(&data, 1);
    }
#elif defined SH1106
    for (uint8_t j = 0; j <= DISPLAY_HEIGHT/8; j++){
        for (uint8_t i = 0; i < DISPLAY_WIDTH ; i++) {
            lcd_data(&data, 1);
        }
        lcd_gotoxy(0,j);
    }
        
#endif
        lcd_home();
}

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal was ganz anderes, Michael: GPL für Code, den man im
Embedded-Bereich benutzen können soll, finde ich persönlich nicht
sehr sinnvoll.  Das dürfte für so ziemlich jeden, der das kommerziell
verwenden möchte, schnell zum KO-Kriterium werden.  Selbst LGPL ist
bei so kleinen Prozessoren wie einem AVR nicht sinnvoll, denn dann
brauchst du auch keine security fuse mehr, wenn du den Leuten sowieso
disassemblierfähige Objektdateien mitliefern musst.

Musst du natürlich am Ende selbst wissen, aber falls die Entscheidung
eher nicht vorsätzlich so gefallen ist: es gibt einen Grund, warum
bspw. die avr-libc eine BSD-Lizenz hat, obwohl sie sich ansonsten in
eine GNU-dominierte Toolchain integriert.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> ihm einfach 0x0F zu
> übergeben, kollidiert zu Recht.

verstehe ich ja, ich bin nur nicht der richtige Ansprechpartner,

der TO nannte es LIB und die möchte ich nutzen ;)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> verstehe ich ja, ich bin nur nicht der richtige Ansprechpartner,

Du bist aber derjenige, der es jetzt gerade nutzen will, insofern kannst
du den von mir vorgeschlagenen Ersatz ja zumindest mal testen.  Ich
denke, dass Michael da in der Hektik einen Bug eingesammelt hat, den
sollte er korrigieren – wenn du den Bugfix aber als „funktioniert“
bestätigst, ist das ganz sicher hilfreich für ihn.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> kollidiert zu Recht.
>
> Probier's mal damit:

hmmm, Fehler im lcd_clrscr ist weg,

Text auch nicht mehr sichtbar mit der 1306 Version ging wenigstens Text 
wenn auch buggy

nun muss es noch was mit der Initialisierung sein, Timing oder Bit oder 
?

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Du bist aber derjenige, der es jetzt gerade nutzen will, insofern kannst
> du den von mir vorgeschlagenen Ersatz ja zumindest mal testen.

äh ja ich bin doch dabei!

nochmal, vorher hatte ich mit der ersten LIB "hallo" und "huhu" zu 
Display 1106 gebracht noch mit 1306 Code!

hallo und huhu landete aber auf falsche Zeilen weil ich ein 1106 habe,
das Display war auch nicht clean nach clr(); sondern verrauscht mit 
Pixelmüll um den Text.

Dann die andere LIB eingespielt mit Compilerfehler

deine Korrektur eingebracht, Kompilerfehler weg aber auch nur ein leeres 
unverrauschtes Display, kein Text mehr sichtbar.

nun hänge ich.....

BTW

wäre hier ein #define nicht besser?

statt:
void lcd_home(void)
{ lcd_gotoxy(0, 0);
}

besser?:
#define LCD_HOME() lcd_gotoxy(0, 0);

ich hänge immer noch.....

: Bearbeitet durch User
Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Aber: irgendwas passt da nicht.  Wenn lcd_data() als erstes einen
> Zeiger haben will, dann hat das einen Grund, ihm einfach 0x0F zu
> übergeben, kollidiert zu Recht.

In der Tat, wie ist mir denn das passiert? Vor die 0x0f hätte noch ein 
(uint8_t*) gehört (wenn ich das recht im Kopf hab)...bei mir hat das der 
Compiler zunächst nicht mal angemeckert, daher ist mir das wohl durch 
die Lappen gegangen...

Joachim B. schrieb:
> deine Korrektur eingebracht, Kompilerfehler weg aber auch nur ein leeres
> unverrauschtes Display, kein Text mehr sichtbar.

Gibt der Compiler noch ein Warning oder ähnliches bei dir aus? Ich habe 
beim SH1106 auch ein 128*64 Pixel großes Display, benutzt du auch so 
eines? Wenn nicht liegt es vielleicht auch an der Displaygröße und da 
muss noch was eingestellt werden, dass ich bisher übersehen habe.
Es tut mir echt leid, dass es bei dir solche Probleme macht, ich hoffen 
wir können das Problem schnell lösen.
Joachim B. schrieb:
> wäre hier ein #define nicht besser?

Hm, so tief bin ich bei weitem nicht drin. Ich habe hier eine Funktion 
gewählt da ich mit dem OLED-Display auch ein paar meiner alten 
LCD-Displays ersetzt habe (mit HD48780-Controller) und hierbei die Lib 
von Peter Fleury verwendet habe. Damit ich meinen alten Code nicht 
umschreiben muss sondern wirklich nur die lcd-Lib austauschen muss habe 
ich mich bzgl. der Funktionsnamen ein wenig an Peters Lib orientiert ;)

Jörg W. schrieb:
> Mal was ganz anderes, Michael: GPL für Code, den man im
> Embedded-Bereich benutzen können soll, finde ich persönlich nicht
> sehr sinnvoll.
> ...

Ich hab da wenig Ahnung von und hab daher mal in meinem Dunstkreis 
rumgefragt und da wurde mit GPL empfohlen. In Stein gemeißelt ist das 
aber nicht.

: Bearbeitet durch User
Autor: DraconiX (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal meine, angepasst für einen STM32F103 - mit StdPeriphLib - 
gleich dazu mit der I2C Initialiesrung - Quasi LCD an I2C1 (PB6 und 
PB7). Dann noch die Header einbinden und los gehts:



#include "stm32f10x.h"
#include "SSD1306_i2c.h"

int main(void)
{

   ssd1306_InitializeDisplay();

   while(1)
   {

     ssd1306_clrscr();
     ssd1306_puts("Das sind 22 Zeichen!");
     ssd1306_gotoxy(0,7);
     ssd1306_puts("Und sieben Zeilen");

     delay_ms(2000);

            
     // Könnte man sich auch sparen mit dem display_buffer
     // in diesem Fall! Man könnte es direkt raussenden.
     // Aber ich hab es mal mit dazugenommen
   
     for(int i=0;i<1024;i++)
     {
      display_buffer[i] = starwars[i];
     }

     ssd1306_TransferBuffer();
     delay_ms(2000);

   }
}


Ich erweitere meine auch noch so nach und nach, so das ich die Register 
direkt beschreibe und keine Lib brauche, und das ich noch Standard I2C2 
und I2C3 mit rein nehme.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> ich hoffen
> wir können das Problem schnell lösen.

hoffe ich auch

M. K. schrieb:
> Gibt der Compiler noch ein Warning oder ähnliches bei dir aus? Ich habe
> beim SH1106 auch ein 128*64 Pixel großes Display,

ja ist identisch

M. K. schrieb:
> und hierbei die Lib
> von Peter Fleury verwendet habe. Damit ich meinen alten Code nicht
> umschreiben muss sondern wirklich nur die lcd-Lib austauschen muss habe
> ich mich bzgl. der Funktionsnamen ein wenig an Peters Lib orientiert ;)

ist ja kein Problem, die hatte ich auch verwendet als ich noch auf pure 
AVR und lcd mit Studio 4.18 unterwegs war

deswegen kenne ich die ja ganz gut

momentan mache ich halt mit der "U8glib.h" weiter

vielleicht klappts ja mit deiner irgendwann, Testaufbau ist vorhanden 
und wenn du was findest probiere ich weiter.

wie gesagt mit 1306 init lief es auf 1106 mal nur mit Pixelmüll und 
falscher xy Adressierung

nun ist der Pixelmüll weg, aber Text kommt nicht, nicht mal falsch 
adressiert.

M. K. schrieb:
> Gibt der Compiler noch ein Warning oder ähnliches bei dir aus?

nein

: Bearbeitet durch User
Autor: DraconiX (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
M. K. schrieb:
> DraconiX schrieb:
>> Das man um den 1K RAM nicht drumrum kommt liegt an der
>> Speicheraddressierung des SSD1306.
>
> Nö, das liegt an der Schnittstelle. Bei i2c und spi kann man nur
> schreibend auf das Display zugreifen. Geht man ans parallele Interface
> des SSD1306 ran kann man das Display auch auslesen, dann braucht man den
> RAM nicht.
>
> Jörg W. schrieb:
>> @Michael: soll ich mal deinen ersten Threadbeitrag editieren und die
>> Links auf die jeweils aktuelle Version einfügen?
>
> Gute Idee, versuche ich gleich umzusetzen. Ich hab auch überlegt bei
> github ein Repository  jeweils für die Text-Variante und für die
> Grafik-Variante zu erstellen.
>
> Joachim B. schrieb:
>> wo weiter oben, du hattest viel gepostet, hast du bitte einen Link?
>
> Ich hab beide Varianten in der aktuellen Version diesem Beitrag
> angehangen, aus dem ersten Beitrag werde ich gleich noch hierauf
> verlinken lassen.
>
> EDIT: Jörg, ich darf den ersten Post nicht mehr editieren. Das ist wohl
> eine Option, die nur Moderatoren/Administratoren zur Verfügung steht,
> nicht aber "normalen" Usern. Wärst du vielleicht so nett?

Ich habe einen Fehler in deiner Lib gefunden - schau mal bitte danach - 
bei mir hat der Compiler immer eine Warnung rausgeworfen aber ich kam 
selber nicht dahiner.

Bei deiner DrawRect, DrawLine etc... überall wo die Überprüfung des 
Displaybereiches ist.

von:
void lcd_drawRect(uint8_t px1, uint8_t py1, uint8_t px2, uint8_t py2, uint8_t color){
    if( ((px1 || px2) > DISPLAY_WIDTH-1) ||
       ((py1 || py2) > DISPLAY_HEIGHT-1) ) return;
    lcd_drawLine(px1, py1, px2, py1, color);
    lcd_drawLine(px2, py1, px2, py2, color);
    lcd_drawLine(px2, py2, px1, py2, color);
    lcd_drawLine(px1, py2, px1, py1, color);
}

zu:
void lcd_drawRect(uint8_t px1, uint8_t py1, uint8_t px2, uint8_t py2, uint8_t color){
    if( (px1 > DISPLAY_WIDTH-1) ||
        (px2 > DISPLAY_WIDTH-1) ||
        (py1 > DISPLAY_HEIGHT-1)||
        (py2 > DISPLAY_HEIGHT-1)) return;

    lcd_drawLine(px1, py1, px2, py1, color);
    lcd_drawLine(px2, py1, px2, py2, color);
    lcd_drawLine(px2, py2, px1, py2, color);
    lcd_drawLine(px1, py2, px1, py1, color);
}


Sie dazu auch hier: Beitrag "C Fehler: Vergleich von Const mit bool immer falsch"

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
DraconiX schrieb:
> Ich habe einen Fehler in deiner Lib gefunden - schau mal bitte danach -
> bei mir hat der Compiler immer eine Warnung rausgeworfen aber ich kam
> selber nicht dahiner.
>
> Bei deiner DrawRect, DrawLine etc... überall wo die Überprüfung des
> Displaybereiches ist.


betrifft das nicht nur die Grafik Version?
Momentan gehts um Text 1306 zu 1106, jedenfalls für mich, nur um mal 
wieder Ordnung in die Postings zu bekommen.

Autor: DraconiX (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> betrifft das nicht nur die Grafik Version?
> Momentan gehts um Text 1306 zu 1106, jedenfalls für mich, nur um mal
> wieder Ordnung in die Postings zu bekommen.

Ja das betrifft nur die Grafik Version. Zum Thema 1106 kann ich leider 
nix sagen da ich hier keinen habe, sonst hätte ich mich auch darauf 
geworfen.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> wie gesagt mit 1306 init lief es auf 1106 mal nur mit Pixelmüll und
> falscher xy Adressierung
>
> nun ist der Pixelmüll weg, aber Text kommt nicht, nicht mal falsch
> adressiert.

Ja, das ist "normales" Verhalten, dass beim "falschen" Controller die 
Adressierung nicht passt und man Pixel-Salat bekommt.

Aber dass da gar nix kommt ist auch ungewöhnlich soweit ich das jetzt 
übersehen konnte. Ich könnte mir vorstellen, dass da irgendwo ein 
OutOfBounds auftritt.

Ich hab mir mal Gedanken um die lcd_clrscr()-Funktion gemacht und sie 
"vereinfacht". Es gibt da jetzt keinen Unterschied mehr zwischen SSD1306 
und SH1106:
void lcd_clrscr(void){
    uint8_t clearLine[DISPLAY_WIDTH];
    for (uint8_t i=0; i<DISPLAY_WIDTH; i++) {
        clearLine[i]=0x00;
    }
    for (uint8_t j = 0; j <= DISPLAY_HEIGHT/8-1; j++){
        lcd_gotoxy(0,j);
        lcd_data(clearLine, sizeof(clearLine));
        
    }
  lcd_home();
}
Vorteil: Das Löschen geht jetzt etwas schneller.
Nachteil: Man braucht jetzt min. 129 Byte SRAM (bei 128 Pixel Breite: 
128 für clearLine, 1 Byte für die Laufvariable i).
Ich glaubs zwar nicht, bei mir hat das am Display-Verhalten selbst 
nichts geändert, aber vielleicht ändert das ja was bei dir.
Wie sieht denn deine Main dazu aus? Ich glaube zwar nicht, dass es daran 
liegt, aber man weiß ja nie.
Weißt du eigentlich, dass das mich ein wenig fuchsig macht, dass es bei 
dir nicht laufen will? ;)

DraconiX schrieb:
> Bei deiner DrawRect, DrawLine etc... überall wo die Überprüfung des
> Displaybereiches ist.

In der Tat, da hast du recht. Es ist aber nur die DrawRect und die 
DrawLine, in der die Prüfung praktisch wirkungslos ist. Hab ich hier 
schon geändert, kommt also mit dem nächsten Upload...sobald wir den 
Fehler bei Johannes raus haben ;).

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:

> for (uint8_t i=0; i<DISPLAY_WIDTH; i++) {
>         clearLine[i]=0x00;
>     }

Das würde ich als
memset(clearLine, 0, DISPLAY_WIDTH);
 schreiben.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> M. K. schrieb:
>
>> for (uint8_t i=0; i<DISPLAY_WIDTH; i++) {
>>         clearLine[i]=0x00;
>>     }
>
> Das würde ich als
memset(clearLine, 0, DISPLAY_WIDTH);
 schreiben.

ganz genau, memset nutze ich gerne ist schliesslich integriert und man 
bekommt es ja kaum besser hin!

M. K. schrieb:
> Weißt du eigentlich, dass das mich ein wenig fuchsig macht, dass es bei
> dir nicht laufen will? ;)

das freut mich, ich möchte ja auch das es läuft :)

: Bearbeitet durch User
Autor: Rüdiger S. (ruediger9)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal eine Erweiterung zur Anzeige von Bildern:

Dazu einfach den Code aus der angehängten Txt-Datei in die lcd_gfx.h und 
lcd_gfx.c einfügen.

Bilder mit Gimp etc. vorbereiten und skalieren. Dabei die maximale Größe 
beachten, es darf nicht größer als die Displaygröße sein.
Die Höhe muss glatt durch 8 teilbar sein.
Bei einem 128x64 Display wäre also z.B. eine Größe von 60x48 gültig.

Mit OledBm.exe in Programmcode umwandeln.
Es gibt hier noch die Möglichkeit, die Schwellwerte für RGB zu ändern, 
um z.B. schnell aus farbigen Bildern eine Monochrom-Bitmap zu 
generieren.
Bessere Ergebnisse ergeben sich, wenn das schon mit dem 
Bildverarbeitungsprogramm gemacht wird.
"Code erstellen" und "Copy Clipboard", dann in main.c oder wo immer es 
von der Funktion sichtbar ist, einfügen.

Funktionsaufruf sieht dann so aus:
lcd_drawBitmap(34, 5, wcfrei);

Es können natürlich soviele Bitmaps eingefügt werden, wie der 
Code-Speicher zulässt.

Damit konnte ich dann meine WC-Besetzt-Anzeige mit Bewegungsmelder 
realisieren. :-)
(Zuhause schließen wir keine Türen ab, weil im Notfall erschwerte 
Rettungsmöglichkeit.)

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rüdiger S. schrieb:
> Hier mal eine Erweiterung zur Anzeige von Bildern:
> Damit konnte ich dann meine WC-Besetzt-Anzeige mit Bewegungsmelder
> realisieren. :-)
> (Zuhause schließen wir keine Türen ab, weil im Notfall erschwerte
> Rettungsmöglichkeit.)

cool, trotzdem meine WC Tür hat aussen einen Schlitz der sofort mit 
Spachtel, Schlüsselanhänger, Messer, Tortenheber geöffnet werden kann!

Autor: DraconiX (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Coole Sache mit dem Bild-C-Dings-Bums! Schreibt er das Array anhand der 
Bildgröße raus?!

 Ich hab nun noch ein SIN Generator hinzugefügt und nun bin ich vollends 
zufrieden.

Kann es sein das der SSD1306 ab ca. 700khz bei I2C schlapp macht? Höher 
schafft er es nimmer.

Autor: Johannes R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> In der Tat, da hast du recht. Es ist aber nur die DrawRect und die
> DrawLine, in der die Prüfung praktisch wirkungslos ist. Hab ich hier
> schon geändert, kommt also mit dem nächsten Upload...sobald wir den
> Fehler bei Johannes raus haben ;).

Das ging in der Flut überflüssiger Posts von Joachim B. vielleicht 
unter, aber die von mir beobachteten Probleme konnte ich durch die 
weiter oben beschriebenen Anpassungen in lcd_drawPixel() und 
lcd_gotoxy() erfolgreich beheben. Meiner Einschätzung nach sind die 
allgemeingültig und können von dir in die Bibliothek übernommen werden.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
DraconiX schrieb:
> 700khz bei I2C

I²C ist ja normal auch nur für 400 kHz spezifiziert.

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> DraconiX schrieb:
>> 700khz bei I2C
>
> I²C ist ja normal auch nur für 400 kHz spezifiziert.

Das ist ein typisches i2c Problem. Wenn ein Device nur 399 kHz schafft, 
fällt es in die Klasse Standard, 100kHz, genau wie eins, das bei 101 kHz 
nicht mehr kann. Schafft es keine 3,4 MHz, ist es nur Fast, also 400kHz. 
Zwischenwerte gibt es nicht.

MfG Klaus

Autor: Rüdiger S. (ruediger9)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
DraconiX schrieb:
> Coole Sache mit dem Bild-C-Dings-Bums! Schreibt er das Array anhand der
> Bildgröße raus?!

Ja, je nach Bildgröße enthält das Array auch mehr oder weniger Daten.
Die ersten beiden Bytes im Array beschreiben die Bildgröße.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rüdiger S. schrieb:
> Mit OledBm.exe in Programmcode umwandeln.

Quellcode dafür wäre natürlich nicht schlecht.

Alternative: mit Gimp als XBM abspeichern. Das ist C-Quzelltext. :)

Für den AVR sollte man dann aber das XBM damit nachbearbeiten:
.SUFFIXES: .xbm

.xbm.h:
        sed -e 's/^.* \([a-zA-Z0-9_]*_bits\).*$$/static const uint8_t \1[] __attribute__((progmem)) = {/' $< > $@.tmp && mv $@.tmp $@

(Makefile-Schnipsel, Kommandos bitte mit <TAB> einrücken)

Damit wird ein progmem-Objekt draus gemacht.

Autor: DraconiX (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> DraconiX schrieb:
> 700khz bei I2C
>
> I²C ist ja normal auch nur für 400 kHz spezifiziert.

Naaaa... Es gibt ja auch den I2C High-Speed-Mode mit Clockstretching bis 
3,4MHz.

Autor: Rüdiger S. (ruediger9)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
>> Mit OledBm.exe in Programmcode umwandeln.
>
> Quellcode dafür wäre natürlich nicht schlecht.

Kein Problem, werde das nach dem Aufräumen veröffentlichen. Ist C#, kann 
also mit der frei verfügbaren Visual Studio Community Edition kompiliert 
werden.

>
> Alternative: mit Gimp als XBM abspeichern. Das ist C-Quzelltext. :)
>

Klasse, man lernt immer was dazu. :)

Autor: Stromtuner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
.SUFFIXES: .xbm

.xbm.h:
        sed -e 's/^.* \([a-zA-Z0-9_]*_bits\).*$$/static const uint8_t 
\1[] __attribute__((progmem)) = {/' $< > $@.tmp && mv $@.tmp $@

Schräg !!! :)

Autor: Walter Jo (oberallgeier)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
oberallgeier schrieb:
> Hallo Michael,
> ...
> Als Gegenleistung .. Bildchen .. vom Oskar .. I²C-Flanken bei den 400 kHz.

So Manfred, am 11.03.2017 hatte ich Dir versprochen die Bildchen vom 
I²C-Bus zu zeigen, der mit 400 kHz betrieben wird. Ich habe nun eine 
(selbst ge-copy´n´paste-e) Version aus den Tiefen des WWW 
(*.cpp-Version) adaptiert und das Display 1306 zum Laufen bekommen.

Hier die Bilder, einmal mit 400 kHz, einmal mit 200 kHz. Man sieht bei 
den 400 kHz-Flanken deutlich, dass der Pegel bei 5V eindeutig nicht mehr 
erreicht wird - vergleiche dazu die 200-kHz-Flanken. Dieser Bustakt 
dürfte also schon grenzwertig sein.

Sonstige Daten: nano-Clone mit 20 (!) Mhz, 5V-Pegel, je 1x 4k7 auf SDA 
und SCL, Datenleitung von den Steckleisten eines nanoclones bis 
zumSteckbrett ca. 20 cm, Display ohne Kondensator von der 
Nano-Steckerleiste (GND, 5V) versorgt.

DraconiX schrieb:
> Kann es sein das der SSD1306 ab ca. 700khz bei I2C schlapp macht?

Es gilt ja:
TWBR = ((F_CPU/SCL_CLOCK)-16)/2
bei (meinem Clone mit) 20 MHz ergibt sich also ein TWBR von 17, bei 
16MHz UND 700 kHz ist TWBR gleich 3 (3,4..). Soweit ich die 
I²C-Spezifikation kenne, ist bei TWBR von etwa 10 Schluss, dann wird 
sowieso clock gestretcht (KANN gestretcht werden gg).

: Bearbeitet durch User
Autor: Walter Jo (oberallgeier)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag zu den Bildern: Gould DSO1602, 20 MHz.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter J. schrieb:
> Hier die Bilder, einmal mit 400 kHz, einmal mit 200 kHz. Man sieht bei
> den 400 kHz-Flanken deutlich, dass der Pegel bei 5V eindeutig nicht mehr
> erreicht wird - vergleiche dazu die 200-kHz-Flanken. Dieser Bustakt
> dürfte also schon grenzwertig sein.

So aus der Hüfte geschossen würde ich da aber sagen, dass deine Pull-Ups 
für 400 kHz schlicht schon zu groß sind. Schau dir mal das hier von TI 
dazu an:
http://www.ti.com/lit/an/slva689/slva689.pdf

;)

Autor: Walter Jo (oberallgeier)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb ..
> .. aus der Hüfte geschossen .. Pull-Ups für 400 kHz .. zu groß ..

Huiii, danke, was für eine praktische, gute Anleitung von TI; die kannte 
ich nicht.
Nun hab ich mal die Leitung vom nano-Pinn SCL zum/inclusive Steckbrett 
gemessen. Uuuuups, 33 µF (mit Billig-DMM), das sind >> 450 pF aus Fig. 
3, und laut Fig. 2 können <1,6kΩ passen.
Also je 1x1k6 Ω gegen Vcc an jede I²C-Leitung - am Steckbrett, 
Übertragungsrate auf 400 kHz (ERinnerung: nanoclone 20 MHz) gestellt und 
Oskar angeworfen.

Genau, jetzt sieht es auch bei 400 kHz nicht mehr (so) grenzwertig aus. 
Die Auflösung meines alten DSOs ist zwar leider nur bis 10 µs/DIV. Am 
5V-Pegel ist trotzdem bei jedem Zacken/Puls vom SCL noch eine eindeutige 
Linie zu erkennen ?~1µs?. Foto könnte ich nachreichen, aber vielleicht 
reicht meine Beschreibung.

Danke für die Hilfe(n).

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter J. schrieb:
> Uuuuups, 33 µF

Dann solltest du entweder den Messwert deines Schätzeisens grundlegend
in Zweifel ziehen oder aber den 33-µF-Elko schnellstens von der
SCL-Leitung wieder ablöten. :-)

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
33µF aus einer Leitung, RESPEKT wo gibt es das Kabel oder wie lang ist 
es?

Direktleitung zur ISS?

Autor: Walter Jo (oberallgeier)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oberallgeier schrieb:
> .. Datenleitung .. nanoclones bis zumSteckbrett ca. 20 cm ..
Jörg W. schrieb:
> .. entweder den Messwert deines Schätzeisens .. in Zweifel ziehen ..
Na ich dachte "Uuuuups, 33 µF .." sei Kommentar genug, aber man sollte 
halt nie zuviel voraussetzen.

Gut, ne Nachmessung mit meinem selbstgebauten Transistortester nach 
Markus F. zeigt später "kein Bauteil" an; einen 100 pF misst der 
(Kontrollmessung) noch mit 0,14 nF.

Autor: DraconiX (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter J. schrieb:
> Es gilt ja:
> TWBR = ((F_CPU/SCL_CLOCK)-16)/2
> bei (meinem Clone mit) 20 MHz ergibt sich also ein TWBR von 17, bei
> 16MHz UND 700 kHz ist TWBR gleich 3 (3,4..). Soweit ich die
> I²C-Spezifikation kenne, ist bei TWBR von etwa 10 Schluss, dann wird
> sowieso clock gestretcht (KANN gestretcht werden gg).

Ja bei mir läuft es über ein STM32 welcher auf 64Mhz läuft und das I²C 
mit DMA gefeuert wird. Der würde seine 4Mhz locker schaffen, aber mir 
ist bis dato auch noch kein HighSpeed-Slave untergekommen, nichteinmal 
Kollegen die die Clock gestrecht haben, wenn es ihnen zu schnell war.

Autor: Rainer K. (zebra)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich versuche die Lib auf einem Tiny85 zum Laufen zu bekommen. Das geht 
auf Anhieb gar nicht. Wie ich festgestellt habe, fehlt bei den i2c 
bit-bang Routinen die Slave Adresse, deshalb fühlt sich das OLED Display 
(SSD1306) gar nicht angesprochen.

Gruß

Rainer

Autor: Walter Jo (oberallgeier)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rainer K. schrieb:
> .. Lib auf einem Tiny85 .. fehlt bei den i2c ..

Gibts beim Tiny überhaupt ein I²C - na ja, aber eben nur "handgestrickt" 
?

In "meiner" SSD1306.h steht die Adresse als
#define SSD1306_DEFAULT_ADDRESS 0x78
aber der Code ist eben für Hardware-TWI beim ATMEGA328p.

: Bearbeitet durch User
Autor: zebra (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter J. schrieb:
> #define SSD1306_DEFAULT_ADDRESS 0x78
> aber der Code ist eben für Hardware-TWI beim ATMEGA328p

Ja, aber es wurde auch versucht zumindestens die Text-Lib mit bit-bang 
I2C für die Tinys zu implementieren. Scheinbar hat vor mir das niemand 
ausprobiert :-) Ohne slave-Adresse in den bit-bang I2C Funktionen geht 
es aber nicht...das Diplay bleibt einfach dunkel. Mir ist noch nicht 
ganz klar, wo man am Einfachsten das Schreiben der Slave-Adresse 
einfügt.

Gruß

Rainer

Autor: Dietmar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,
wie Schwierig ist es einen anderen Font zu integrieren ?
lg Dietmar

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dietmar schrieb:
> wie Schwierig ist es einen anderen Font zu integrieren ?

Kommt auf den Font an. Aktuell ists nur für 6x8-Fonts gemacht. Wenn du 
also einen anderen 6x8-Font möchtest sollte das kein Problem sein.

Autor: Dietmar (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die Antwort und das erstellen der Library !
Ich finde die Library ausgezeichnet und sie ist auch nicht so überladen
wie die U8G Lib. Ich verwende den 6x8 Font aber der ist für mich als 
älteres Semester nicht so gut lesbat ;-)
lg Dietmar

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dietmar schrieb:
> Ich verwende den 6x8 Font aber der ist für mich als
> älteres Semester nicht so gut lesbat ;-)

Kenn ich. Es gibt auch Pin- und Funktionskompatible 1,3" Oleds. Da ist 
es ein wenig besser.

MfG Klaus

Autor: Dietmar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
meine quick and dirty Variante :-)
void lcd_putc(char c,char xpos,char ypos)
{
   unsigned char x,y,w;

   for (y=0; y<FONT_Y_SIZE; y+=1)
   {
       w=pgm_read_byte(&font_8x12[(unsigned char)c][y]);
       
       for (x=0; x<FONT_X_SIZE; x++)
       {
         if (w&1)
         lcd_drawPixel(actualIndex+x+xpos, y+ypos,1);
         else
         lcd_drawPixel(actualIndex+x+xpos, y+ypos,0);
         w=w>>1;
       }

   }
 
 actualIndex += FONT_X_SIZE+1;
}

: Bearbeitet durch Moderator
Autor: Daniel Ross (jimminy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich bin hier auf die OLED Library gestoßen und wollte die ganz gerne 
verwenden. Leider Bekomme ich beim Compilieren immer den fehler:

"undefined reference to `lcd_gotoxy(unsigned char, unsigned char)"

Dieser Fehler tritt bei jeder Funktion auf.
Hat einer eine Idee woran das liegen könnte?
Ich benutze Atmel Studio 7.
Würde mich über ein Tipp sehr freuen, Danke!
Mfg

Autor: Tippgeber (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel R. schrieb:
> Leider Bekomme ich beim Compilieren immer den fehler:
>
> "undefined reference to `lcd_gotoxy(unsigned char, unsigned char)"
>
> Dieser Fehler tritt bei jeder Funktion auf.
> Hat einer eine Idee woran das liegen könnte?

#include "SSD1306_i2c.h" vergessen?

Autor: Tippgeber (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, das Ding heißt ja: #include "lcd_ssd1306_gfx.h"

Autor: Dieter F. (jim_quakenbush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Beitrag "Re: SSD1306 Library zum Darstellen von Text auf OLED Displays"

Sag mal bitte  - woher kommt denn der Font von der Mini-Schrift am 
unterenb Rand? Findet man den irgendwo?

Autor: Daniel Ross (jimminy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wo bekomme ich die "lcd_ssd1306_gfx.h"?
Dachte die lcd_gfx.h und lcd_gfx.c wäre das was ich brauche?
Danke!

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter F. schrieb:
> Beitrag "Re: SSD1306 Library zum Darstellen von Text auf OLED Displays"
>
> Sag mal bitte  - woher kommt denn der Font von der Mini-Schrift am
> unterenb Rand? Findet man den irgendwo?

ach DU MEINST mich?

da muss man erst mal drauf kommen ohne Zitat, was du siehst ist die 
eingebundene originale UG8LIB, da kann man size wählen

beim Nokia bekomme ich 6 Zeilen a 14 Zeichen und in derselben lesbaren 
Größe nur 5 Zeilen a 16 Zeichen und deswegen habe ich die 6te Zeile 
verkleinert.

: Bearbeitet durch User
Autor: Dieter F. (jim_quakenbush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> ach DU MEINST mich?

Ja, der Link ist eigentlich deutlich - oder?

Danke für den Hinweis auf die Skalierung.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter F. schrieb:
> Joachim B. schrieb:
>> ach DU MEINST mich?
>
> Ja, der Link ist eigentlich deutlich - oder?

dazu müsste man jedem Link folgen was ich nur selten mache, wozu auch 
wenns mich nicht betrifft?

Deswgen die Zitatfunktion nutzen dann wird es eher gesehen wen es 
betrifft!

Autor: Dieter F. (jim_quakenbush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> dazu müsste man jedem Link folgen was ich nur selten mache, wozu auch
> wenns mich nicht betrifft?
>
Aufwändig - 1 Klick

> Deswgen die Zitatfunktion nutzen dann wird es eher gesehen wen es
> betrifft!

O.K. - werde ich künftig berücksichtigen

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter F. schrieb:
> Joachim B. schrieb:
>> dazu müsste man jedem Link folgen was ich nur selten mache, wozu auch
>> wenns mich nicht betrifft?
>>
> Aufwändig - 1 Klick

ja bei begrenzter Restlebenszeit :)
(PS. Mein Nachbar klickt auch immer ALLES und wundert sich wenn sein 
Computer wieder mal spinnt)

>> Deswgen die Zitatfunktion nutzen dann wird es eher gesehen wen es
>> betrifft!
>
> O.K. - werde ich künftig berücksichtigen

danke dafür!

: Bearbeitet durch User
Autor: Dieter F. (jim_quakenbush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim B. schrieb:
> ja bei begrenzter Restlebenszeit :)
> (PS. Mein Nachbar klickt auch immer ALLES und wundert sich wenn sein
> Computer wieder mal spinnt)

Hier im Forum ... ja, wird wohl so sein ...
(Wer hat keine begrenzte Restlebenszeit?)

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter F. schrieb:
> Joachim B. schrieb:
>> ja bei begrenzter Restlebenszeit :)
>> (PS. Mein Nachbar klickt auch immer ALLES und wundert sich wenn sein
>> Computer wieder mal spinnt)
>
> Hier im Forum ... ja, wird wohl so sein ...

natürlich nicht, sollte nur mal aufzeigen das es (überall) keine gute 
Idee ist auf ALLES zu klicken!

> (Wer hat keine begrenzte Restlebenszeit?)

ja aber scheinbar stört es manche nicht oder sie haben Mutti Vati Frau 
Partner der das Leben organisiert und demzufolge genug Zeit übrig um 
alles zu klicken.

Autor: Daniel Ross (jimminy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich habe mein fehler gefunden. Mein Projekt habe ich in C++ geschrieben. 
Da die lcd_gfx.c ja in C geschrieben ist, hat das der Compiler nicht auf 
reihe bekommen. Hab dann die lcd_gfx.c in eine .cpp abgespeichert und 
läuft jetzt auch soweit.
Eine Frage habe ich allerdings noch. Kann man die Anzahl der Reihen oder 
die Schriftgröße verändern?
Danke!

Autor: Julius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich liebe diese Endlos Threads.

Anleitung zum Verbreiten von eigener Sofware:

1. Die finale/stable Version im ersten Post verlinken, am besten dort 
alle Versionen auflisten.

NIEMAND liesst alle Threads nur um die letzte Version zu finden.

2. Code Dokumentieren. Natürlich wissen die eingefuchsten Programmierer 
wie man diese Library benutzt. Aber für Anfänger gehören minimale 
Verwendungshinweise als Comment in den Code.

In der Art:

erst initialisieren,
dann bla,
dann zeichen senden

Ein Hello World Beispiel, am besten gleich am Anfang des Codes.

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel R. schrieb:
> Hallo,
> ich habe mein fehler gefunden. Mein Projekt habe ich in C++ geschrieben.
> Da die lcd_gfx.c ja in C geschrieben ist, hat das der Compiler nicht auf
> reihe bekommen. Hab dann die lcd_gfx.c in eine .cpp abgespeichert und
> läuft jetzt auch soweit.
> Eine Frage habe ich allerdings noch. Kann man die Anzahl der Reihen oder
> die Schriftgröße verändern?
> Danke!

Die Anzahl der Reihen (Pages) wird von der Displaygröße vorgegeben, eine 
Variation der Schriftgröße ist (noch) nicht implementiert, mir fehlt 
aktuell leider die Zeit weiter daran zu arbeiten, es darf sich aber 
jeder frei fühlen die Library um entsprechende Funktionen zu erweitern.

Julius schrieb:
> 1. Die finale/stable Version im ersten Post verlinken, am besten dort
> alle Versionen auflisten.

Die aktuelle Version ist im ersten Post verlinkt, ich kann ihn leider 
nicht editieren. Dazu muss ich dann immer einen Moderator/Admin fragen. 
;)

Julius schrieb:
> 2. Code Dokumentieren. Natürlich wissen die eingefuchsten Programmierer
> wie man diese Library benutzt. Aber für Anfänger gehören minimale
> Verwendungshinweise als Comment in den Code.
>
> In der Art:
>
> erst initialisieren,
> dann bla,
> dann zeichen senden
>
> Ein Hello World Beispiel, am besten gleich am Anfang des Codes.

Ja, einen Beispiel-Code hätte ich hier noch implementieren können. Fand 
ich jetzt hierbei überflüssig da man, wenn man sich wirklich mit 
Displays beschäftigt, sehr schnell dahinter kommt, dass man das Display 
erst initialisieren muss und erst dann drauf schreiben kann. Ich werde 
aber mal einen Beipsiel-Code als Comment mit reinpacken.

: Bearbeitet durch User
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel R. schrieb:
> Da die lcd_gfx.c ja in C geschrieben ist, hat das der Compiler nicht auf
> reihe bekommen.

Dafür hätte man im Headerfile die Funktionsdeklarationen in
#ifdef __cplusplus
extern "C" {
#endif

// hier kommen die Funktionsprototypen
int a(char b);

#ifdef __cplusplus
}
#endif

einschließen müssen.  Das sagt dem C++-Compiler, dass die entsprechenden
Funktionen in einer C-Datei stehen und daher eine andere 
Aufrufkonvention
zu benutzen ist.

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> .......
> einschließen müssen.  Das sagt dem C++-Compiler, dass die entsprechenden
> Funktionen in einer C-Datei stehen und daher eine andere
> Aufrufkonvention
> zu benutzen ist.

das verstehe ich zwar nicht, aber in Arduino habe ich meinen alten 
C-Quellcode einfach zu s_tools.ino (aus s_tools.c) umbenannt das 
funktionierte, genauso wie in der *.ino #include "s_tools.c"

aber s_tools.ino kam mir praktischer vor als das include!

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Joachim B. schrieb:
> in Arduino habe ich meinen alten C-Quellcode einfach zu s_tools.ino (aus
> s_tools.c) umbenannt

Das mag für vielen C-Code funktionieren, aber nicht jedes gültige
C-Programm ist auch ein gültiges C++-Programm.  Das fängt bei
offensichtlichen Problemen an wie die Benutzung von Schlüsselwörtern,
die in C++ reserviert sind (class, new), aber es gibt subtilere
Unterschiede, bis hin zu C99-Features, die in C++ keinen Einzug
gefunden haben (named initializers).

> das verstehe ich zwar nicht

C++-Funktionen bekommen einen „verwürgten“ (engl.: mangled) Namen, in
dem Anzahl und Typ der Argumente codiert werden.  Dadurch hat man ein
typesafe linking und kann Funktionen gleichen Namens aber mit
unterschiedlichen Argumenten unterscheiden.

C kennt sowas nicht.  Durch die Deklaration als „extern "C"“ zeigt man
dem C++-Compiler an, das die entsprechenden Funktionen der C-Notation
folgen.

Autor: Daniel Ross (jimminy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
danke für die Antworten.
@sylaina
Du hattest mal ein Stück Beispielcode hier relativ am Anfang gepostet, 
daran hatte ich mich orientiert. Gut zu wissen das es noch nicht 
Implementiert ist, dann brauch ich da nicht weiter zu suchen. Vielleicht 
bekomme ich da auch eine Lösung hin.

Jörg W. schrieb:
> .......
> einschließen müssen.  Das sagt dem C++-Compiler, dass die entsprechenden
> Funktionen in einer C-Datei stehen und daher eine andere
> Aufrufkonvention
> zu benutzen ist.
Das werde ich bei Gelegenheit mal Testen. Kann man ja nur dazu lernen.
Vielen Dank!

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo ihr,

finde die Library prima. Nach ein paar Handgriffen konnte ich damit mein 
Display an einem ATmega32 betreiben. Danke auch für den Codeauszug. Ohne 
diesen wäre ich wohl nicht so schnell auf die unscheinbare Funktion 
lcd_display() aufmerksam geworden und daraufhin verzweifelt.

Was jedoch nicht funktioniert, ist die Ausgabe der Umlaute und 
Sonderzeichen. Diese werden von
 if((c > 127 ||
        c < 32) &&
       (c != 'ü' &&
...
        c != 'Ä' ) ) return;
einfach verschluckt, da die Anweisung sie nicht erkennt und die Funktion 
abbricht. Kommentiere ich die Überprüfung aus, wird mir an den 
entsprechenden Stellen des Displays nur Pixelsalat angezeigt. Ich 
schätze, da wird auf irgendwelche Speicherbereiche außerhalb des Arrays 
verwiesen.


Das AtmelStudio wirft mir weiterhin die Warnungen "Case label value is 
less than minimum value for type" und "Multi-character character 
constant [-Wmultichar]" aus. Diese beziehen sich auf den Bereich von 
lcd_putc(), in dem die Sonderzeichen vorkommen.

Die erste Meldung habe ich mit einem Cast in der switch-Anweisung 
beseitigen können
switch((uint16_t) c) {
      case 'ü':
      c = 95; // ü - 188
      break;

Die zweite Warnung rührt wohl daher, dass die switch-Anweisung die 
Unterscheidungen als Zeichenkette und nicht als einzelnes Zeichen 
interpretiert. Trage ich hier die vermeintlichen Zahlenwerte der 
Sonderzeichen ein, wie bspw.
      case 153:
        c = 100; // Ö
        break;
verschwindet zwar die Fehlermeldung, aber es bleibt beim Pixelsalat, da 
ich mit 153 über dem Wertebereich vom Datentyp char liege, den die 
gesamte Funktion ja ursprünglich als Variable erhält.
Auch ein ändern in unsigned char oder uint16_t brachte hier keine 
Verbesserung.

Irgendwie scheinen die Zeichen einfach nicht richtig erkannt zu werden. 
Weiß hier jemand Rat?

Autor: DraconiX (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Sonderlaute müssen auch in deinem Font-Array vorhanden sein.

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vorhanden sind sie. Habe an den Fonts nicht verändert. Sie werden auch 
korrekt dargestellt, wenn ich sie händisch aufrufe
c = 100;  
for (uint8_t i = 0; i < 6; i++)
  {
    displayBuffer[actualIndex+i] = pgm_read_byte(&ssd1306oled_font6x8[c * 6 + i]);  // print font to ram, print 6 columns
  }
  
    actualIndex += 6;
Obiges Beispiel liefert beispielsweise das "Ö", welches sich an Position 
100 befindet.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus schrieb:
> "Multi-character character constant [-Wmultichar]"

Das deutet darauf hin, dass du mit UTF-8 als Sourcecode-Zeichensatz
arbeitest.  Damit funktioniert diese einfache switch/case-basierte
Implementierung nicht, die kommt nur mit 8-Bit-Zeichen zurecht.

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, das war ein guter Hinweis. Sobald ich im Atmel Studio die Option 
"Auto-detect UTF-8 encoding without signature" deaktiviere, wird jeder 
Umlaut durch zwei andere Zeichen ersetzt. Habe nun versucht, den 
Zeichensatz zu ändern, was aber nicht geglückt ist.

Jedoch habe ich während meiner Odyssey im Internet herausgefunden, dass 
sich die Zeichen durch den Zahlenwert des HTML-Unicode 
(https://de.wikipedia.org/wiki/Hilfe:Sonderzeichenreferenz) ersetzen 
lassen.
      case 246:
      c = 99; // ö
      break;
 funktioniert beispielsweise einwandfrei. Ist zwar etwas komisch zu 
lesen und es geht sicherlich eleganter, aber ich schätze, es wird dann 
wohl bei allen existierenden Sonderzeichen funktionieren, die man 
irgendwie verarbeiten möchte.

Jedenfalls ist es eine mögliche Lösung für Leute, die irgendwann mal ein 
ähnliches Problem haben.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Markus schrieb:
> Zahlenwert des HTML-Unicode

Genauer gesagt: Latin-1 encoding (ISO 8859-1).  Das ist ein
Ein-Byte-Encoding, bei dem die untere Hälfte ASCII ist und die obere
Hälfte allerlei mittel-, nord- und westeuropäische Schriftzeichen
enthält.

ISO 8859-1 ist das Standard-Encoding bei HTTP, sofern nichts anderes
vom Server angegeben ist.

Autor: jps2000 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi
Spiele mich gerade mit der Textversion herum
Blöde Frage:
Wie kann man eigentlich Zahlen mit Dezimalstelle (einfach) darstellen ? 
also zB zur Ausgabe einer Temperatur etc:  27.2 Grad ?

text geht ja einfach

lcd_gotoxy(0,0);
lcd_puts_p(PSTR("1234567890123456"));

Ich bräuchte sowas wie lcd.print (temp,1); zusätzlich zu text

any suggestions?

danke

Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jps2000 schrieb:
> Hi
> Spiele mich gerade mit der Textversion herum
> Blöde Frage:
> Wie kann man eigentlich Zahlen mit Dezimalstelle (einfach) darstellen ?
> also zB zur Ausgabe einer Temperatur etc:  27.2 Grad ?
>
> text geht ja einfach
>
> lcd_gotoxy(0,0);
> lcd_puts_p(PSTR("1234567890123456"));
>
> Ich bräuchte sowas wie lcd.print (temp,1); zusätzlich zu text
>
> any suggestions?
>
> danke

Aus der Standard-Lib (stdlib.h):

char * dtostrf(
  double __val,
  signed char __width,
  unsigned char __prec,
  char * __s)

Damit wandelst du Floats/Doubles in einen String um.
Für Integer gibts

char * itoa(
  int val,
  char * s,
  int radix)

Für dich vielleicht auch interessant: 
http://www.atmel.com/webdoc/avrlibcreferencemanual/

Ist ne recht gute Referenz

Autor: jps2000 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Antwort.- Ich hab nicht so rasch mit einer Antwort 
gerechnet und was gebastelt

void lcd_print_var(uint8_t x, uint8_t y,float var){

// calculate digits
  uint16_t var1  =  abs(var) * 10;                //single decimal --> 
multiply by ten and convert to uint
  uint8_t tenth = 48 + var1 %10;                  // 48 is Ascii 0
  var1 /= 10;
  uint8_t single = 48 + var1 %10;
  var1 /= 10;
  uint8_t ten = 48 + var1 %10;
  var1 /= 10;
  uint8_t hundred = 48 + var1 %10;

// blank leading zeroes
  if (hundred == 48) hundred = 32;
  if (hundred == 32 && ten == 48) ten = 32;

// arrange characters
  char buf[6] = {32,hundred,ten,single,46,tenth}; // first sign is blank 
(32) 46 is decimal point
  if (var < 0) buf[0] = 45;                       // first sign is -

// write to display
  lcd_gotoxy(x-4,y);                              //sign position  x, y 
is decimal position
  lcd_puts(buf);         //write digit
  }


was du schreibst klingt interessant

kannst du bitte noch ergänzend für mein kleines Hirn
den syntax  aufschreiben
Nehmen wir an ich will -24.5 darstellen (float val = -24.5f)
was schreibe ich dann?

  lcd_puts(......);

mir helfen immer Beispiele

Danke

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. K. schrieb:
> Ist ne recht gute Referenz

„Das ist alles nur geklaut, das ist alles gar nicht meine!“ ;-)

Sie verletzen die avr-libc-Lizenz, da sie nicht erwähnen, dass das die
Doku der avr-libc ist, nur äußerlich ein bisschen auf Atmel getrimmt.

Aber irgendeinen Webmaster oder dergleichen, dem man sowas melden
könnte, sucht man bei Microchip vergeblich.  Wahrscheinlich muss man
wirklich der Rechtabteilung einen Drohbrief schreiben. :(

p.s.: Der Klassiker für sowas wäre natürlich sprintf().

: Bearbeitet durch Moderator
Autor: M. Köhler (sylaina)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Sie verletzen die avr-libc-Lizenz, da sie nicht erwähnen, dass das die
> Doku der avr-libc ist, nur äußerlich ein bisschen auf Atmel getrimmt.

Stimmt, da hast du völlig recht. Vielleicht kannst du das ja noch für 
mich ergänzen damit hier niemand Probleme bekommt. ;)

jps2000 schrieb:
> was du schreibst klingt interessant
>
> kannst du bitte noch ergänzend für mein kleines Hirn
> den syntax  aufschreiben
> Nehmen wir an ich will -24.5 darstellen (float val = -24.5f)
> was schreibe ich dann?
>
>   lcd_puts(......);
>
> mir helfen immer Beispiele

Kein Problem, hier ein kleines Beispiel dazu
...
#include <stdlib.h>
...
float myTestValue = -24.5;
char myValueToPrint[6];
...
lcd_gotoxy(1,0); // mal zu irgend einer Zeile gehen
lcd_puts_p(PSTR("Der Wert ist: "));
// dtostrf(): Variable die zu einem String umgewandelt werden soll, Stellen, die angezeigt werden sollen incl. Komma, Zahl der Nachkommastellen, Stringvariable(genauer: Zeiger auf die erste Speicherstelle der Stringvariablen) die den umgewandelten Wert aufnehmen soll
dtostrf(myTestValue, 
        6, 
        2, 
        myValueToPrint);
lcd_puts(myValueToPrint);
...

Jörg W. schrieb:
> p.s.: Der Klassiker für sowas wäre natürlich sprintf().

Post Scriptum kürzt man PS ab, nicht P.S. und auch nicht PS. oder 
ähnliches ;)
Und zu Sprint(): das hat ja u.U. den ein und anderen Haken.

Autor: jps2000 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank
jetzt habe ich es verstanden
damit schrumpft die Funktion zu

void lcd_print_var(uint8_t x, uint8_t y,float var, uint8_t digits, 
uint8_t resolution ){
  char buf[digits];
  dtostrf(var, digits, resolution, buf);
  lcd_gotoxy(x-resolution,y);      //sign position  x, y is decimal 
position
  lcd_puts(buf);                   //write digit
  }

Der einzige Pferdefuß ist dass dtostrf mehr Programmspeicher braucht

Autor: Stefan Us (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Halo sylaina,
ist die Library aus dem Beitrag 
Beitrag "Re: SSD1306 Library zum Darstellen von Text auf OLED Displays" noch aktuell? Falls 
nicht, wo kann ich die aktuelle Version finden?

Autor: Marco H. (damarco)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da muss ich dich leider eines besseren belehren ;) Der RAM Verbrauch der 
u8g2 lib lässt sich beeinflussen. das mit der Memory Konfiguration, man 
kann auch nur eine ROW bzw. Page als Buffer benutzen.

Ein kleines Problem von einigen Controller ist leider das man nicht im 
RAM Zeichnen kann, da sie keinen Refresh Command besitzen. Heißt das 
dies sofort sichtbar wird und womit der Display Buffer nötig wird um 
Overlays etc. zu erzeugen.

Das schöne an der Lib ist wenn man es erst mal geschafft hat einen 
Displaytreiber zu schreiben es portierbar funktioniert. Bei vielen libs 
muss man oft die Display Konfiguration oder die Ausgabe anpassen.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marco H. schrieb:
> Da muss ich dich leider eines besseren belehren

Wenn den eigentlich?  Der Thread ruht seit mehr als 2 Monaten in der
Kiste, und auf die Frage, die wirklich neu gestellt worden ist,
beziehst du dich ja ganz offensichtlich nicht.

Wenn man sich nicht auf den Beitrag direkt darüber bezieht, sollte man
besser den Beitrag zitieren, zu dem man etwas antworten möchte.  So
steht deine Wortmeldung ziemlich zusammenhanglos im Raum.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.