Assembler Monochrome 1.3" 128x64 OLED graphic display(SSD1306) initialisieren

Senpai

Mitglied
14. Mai 2012
143
0
16
Sprachen
  1. Assembler
Hallo Forumsgemeinde:D
Ich habe mirhier aus dem Shop folgendes Grafik-Display gekauft
http://mikrocontroller-praxis.de/de...nochrome-1.3-128x64-OLED-graphic-display.html
und hänge jetzt daran das Ding ans laufen zu bekommen

hat vielleicht jemand schonmal das gute Stück in Assembler betrieben? oder kann mir tipps geben?

Ich habe bsher nur informationen für bascom und C gefunden und mit den Arduinogeschichten von Adafruit kann ich auch nichts anfangen.

Im Datenblatt des SSD1306
www.mikrocontroller-praxis.de/data_article/AF938/SSD1306.pdf

Steht einmal auf Seite 23 etwas von einem reset
und auf seite 64 ein kompletter ablauf der Softwarekonfiguration.
Muss ich diese Konfiguration noch durchführen nach dem reset?

vielen dank schonmal für eure Hilfe:D
 
Hallo Senpai,

nach einem Reset des Displaycontrollers initialisiert dieser seine Register mit bestimmten Werten. Die Werte werden allerdings nicht zu der aktuellen Applikation (angeschlossenens OLED) passen. Auf der Seite 64 ist eine typische Initialsierung (insbesondere die Reihenfolge ist hier manchmal wichtig) beschrieben.

Am besten wäre es, wenn du dir ein Beispiel für das aktuelle Display von Adafruit ansiehst und versuchst, dieses in Assembler zu übersetzen. Ich habe mir das Display selber noch nicht so genau angesehen, viele setzen es anscheinend mit Bascom oder Arduino ein. Wenn ich mehr Zeit hätte, würde ich hier mal die notwendigen Grundroutinen in Assembler schreiben :rolleyes:

Dirk :ciao:
 
Hallo,

Im Datenblatt des SSD1306
www.mikrocontroller-praxis.de/data_article/AF938/SSD1306.pdf

Steht einmal auf Seite 23 etwas von einem reset
und auf seite 64 ein kompletter ablauf der Softwarekonfiguration.
Muss ich diese Konfiguration noch durchführen nach dem reset?

Auf Seite 23 stehen die Werte die nach einem Reset aktiv sind. Wenn man was anderes haben will muß man das wie auf Seite 64 einstellen (initialisieren).

3 Software Configuration
SSD1306 has internal command registers that are used to configure the operations of the driver IC.
After reset, the registers should be set with appropriate values in order to function well.
Das ist vergleichbar mit der Initialisierung eines normalen Character-LCDs. Das muß (wie im Text gesagt) nach jedem Reset erfolgen. Ob jetzt nach Stromausfall oder über den Resetpin ist dabei egal.

Gruß
Dino
 
Hallo Senpai,

mit dem Display kenne ich mich nicht so aus, aber laut Beispielcode von adafruit auf Github sieht es so aus:

Man sieht zumindest die Hex-Initialisierungswerte ...

(Achtung: ich gehe davon aus, es passt auch für das 1,3" 128x64 OLED Display)

Code:
   ssd1306_command(SSD1306_CHARGEPUMP);                    // 0x8D
   if (vccstate == SSD1306_EXTERNALVCC) 

   {
      ssd1306_command(0x10); 
   } else {
      ssd1306_command(0x14);
   }

Also wenn du externe Spannung verwendest, dann 0x10 verwenden, ansonsten 0x14. (Was die Werte in den Registern bewirken, kann ich nicht sagen, soweit habe ich das Datenblatt nicht durchgearbeitet)

Hier der Beispielcode von adafruit für SSD1306:
https://github.com/adafruit/Adafruit_SSD1306

Der relevante Bereich für die Initialisierung eines 128x64 Displays:
(ist zwar kein Assembler sondern Arduino/Cpp, aber ich denke, du kommst damit zurecht, es ist auch soweit gut kommentiert.)

Vielleicht hilft dir dies weiter.

Dirk :ciao:

Code:
  delay(1);
  // bring reset low
  digitalWrite(rst, LOW);
  // wait 10ms
  delay(10);
  // bring out of reset
  digitalWrite(rst, HIGH);
  // turn on VCC (9V?)



    // Init sequence for 128x64 OLED module
    ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE
    ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
    ssd1306_command(0x80); // the suggested ratio 0x80
    ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8
    ssd1306_command(0x3F);
    ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3
    ssd1306_command(0x0); // no offset
    ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0
    ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D
    if (vccstate == SSD1306_EXTERNALVCC)
      { ssd1306_command(0x10); }
    else
      { ssd1306_command(0x14); }
    ssd1306_command(SSD1306_MEMORYMODE); // 0x20
    ssd1306_command(0x00); // 0x0 act like ks0108
    ssd1306_command(SSD1306_SEGREMAP | 0x1);
    ssd1306_command(SSD1306_COMSCANDEC);
    ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
    ssd1306_command(0x12);
    ssd1306_command(SSD1306_SETCONTRAST); // 0x81
    if (vccstate == SSD1306_EXTERNALVCC)
      { ssd1306_command(0x9F); }
    else
      { ssd1306_command(0xCF); }
    ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
    if (vccstate == SSD1306_EXTERNALVCC)
      { ssd1306_command(0x22); }
    else
      { ssd1306_command(0xF1); }
    ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB
    ssd1306_command(0x40);
    ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4
    ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6

  
  ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel
 
Ja danke das war ne gute hilfe um s nochmal mit dem datenblatt zu vergleichen

habe jetzt auch den initialisierungscode soweit fertig(denke ich zumindest:confused::rolleyes:)
und überlege gerade wie ich zum Testen am einfachsten das Display "komplett eingefärbt" bekomme.

Hier mal das bisherige Programm

Code:
 ;******************************************************************************
 ;GLCD mit SSD1306-Controller an ATmega16
 ;******************************************************************************

 ;Display: Monochrome 1.3" 128x64 OLED graphic display(AVR-praxis.de)

 ;fmcu=16MHz

 ;Controllerpin   :Displaypin
 ;PB1				5 CS
 ;PB2				4 RST
 ;PB3				3 D/C
 ;PB5 MOSI			1 DATA
 ;PB7 CLK			2 CLK

 ;******************************************************************************
 ;Registerdefinition
 ;******************************************************************************

 .def temp			=r16
 .def temp1			=r17
 .def counter		=r18

 .equ display		=PORTB
 .equ cs			=1
 .equ rst			=2
 .equ dc			=3

 ;******************************************************************************
 ;Interuptvektoren
 ;******************************************************************************

 .org 0x00 rjmp init	;reset

 ;******************************************************************************
 ;Initialisieren
 ;******************************************************************************

 init:
 ;Register löschen
 clr temp
 clr temp1
 clr counter

 ;stack initialisieren
 ldi temp,high(ramend)
 out sph,temp
 ldi temp,low(ramend)
 out spl,temp

 ;Spi initialisieren
 ldi temp,0b10111110	        ;Setze sck,MOSI,PB4(DAder Atmega sonst in den slavemodus geht),
 out DDRB,temp			;D/C(PB3),RST(PB2) und CS(PB1) als Ausgang

 ldi temp,0b01010001	        ;Bit7 spi interrupt enable off, Bit6 SPI enable, Bit5 MSB first, Bit4 SPI Master					
 out SPCR,temp			;Bit3 Clockpolarity high, Bit2 Clock Phase Alpha, Bit1,0 f/16

 ;Display initialisieren
 cbi display,rst		       ;Display resetten
 rcall hundertms

 sbi display,rst
 rcall hundertms

 ldi temp1,0xAE			;Display Ausschalten
 rcall command
 
 ldi temp1,0x00			;definiere low Byte für Page adressierung
 rcall command

 ldi temp1,0x10			;definiere high Byte für Page adressierung
 rcall command

 ldi temp1,0x40			;Startposition festlegen
 rcall command

ldi temp1,0x81			;Kontrast einstellen
rcall command
ldi temp1,0xCF
rcall command

ldi temp1,0xA1			;Zuordnung Spaltenadresse 127 zu segment 0
rcall command

ldi temp1,0xA6			;Anzeige normal/nicht invertiert
rcall command

ldi temp1,0xA8			;Multiplexrate auf 1/64 einstellen
rcall command
ldi temp1,0x3F
rcall command

ldi temp1,0xD3			;setze vertikalen Display offset
rcall command			
ldi	temp1,0x00
rcall command

ldi temp1,0xD5			;setze Displayoszillatorfrequenz
rcall command
ldi temp1,0x80
rcall command

ldi temp1,0xD9			;setze pr-charge periode
rcall command
ldi temp1,0x22
rcall command

ldi temp1,0xDA			;setze Compin-Hardwarekonfiguration
rcall command
ldi temp1,0x12
rcall command

ldi temp1,0xDB			;Vcomh level festlegen
rcall command
ldi temp1,0x40
rcall command

ldi temp1,0x8D			;Deaktiviere charge-pump
rcall command
ldi temp1,0x10
rcall command

ldi temp1,0xAF			;Schalte Display ein
rcall command


 ;******************************************************************************
 ;Hauptprogramm
 ;******************************************************************************
 main:
 rjmp init

 ;******************************************************************************
 ;Unterprogramme
 ;******************************************************************************

 ;sende Kommando
 command:
 cbi display,dc
 rjmp send

 ;sende Daten
 data:
 sbi display,dc

 ;Senden über SPI
 send:
 cbi display,cs			        ;CS auf low ziehen
 out SPDR,temp1			;Byte aus Temp1 nach SPDR schieben und somit senden starten
 			
 spiwarten:	
 sbis SPSR, SPIF		               ;wenn das SPIF flag gesetzt ist wurde das Byte gesendet
 rjmp spiwarten	

 in temp1,SPDR			      ;lese empfangenes byte und lösche SPIF
 sbi display,cs 
 ret

 ;Warteroutinen
 hundertms:
 ldi counter,9
 
 zehnms:
 ldi xh,high(40000)
 ldi xl,low(40000)
 
 loop:
 sbiw xl,1
 brne loop

 cpi counter,0
 breq endloop

 dec counter
 rjmp zehnms

 endloop:
 ret
 
Ich habe gerade nicht so viel Zeit, ich schaue aber mal, ob ich die ScreenFill Routine in Assembler übersetze. Du kannst aber einfach mal in den C-Code schauen, da ist bestimmt etwas enthalten, mit dem du zurecht kommst.

Was mir aufgefallen ist ...

Code:
;****************************************************************************** 
;Hauptprogramm 
;****************************************************************************** 
main: 
rjmp init

Hier initialisierst du immer wieder.

In Assembler bin ich es so gewohnt, dass ich nach Reset initialisiere und dann in einem main_loop lande.

Code:
; Reset

; initialisierungen

main_loop: 
rjmp main_loop

Dirk :ciao:
 
Das mit dem rjmp init war absicht,da ich zum testen mit dem logikanalyzer geguckt hatte ob was aus dem AVR rauskommt
 
Das mit dem rjmp init war absicht,da ich zum testen mit dem logikanalyzer geguckt hatte ob was aus dem AVR rauskommt

OK :)

Hier mal ein Beispiel in Assembler, was das Display füllen sollte. Es ist angelehnt an die Routine
void Adafruit_SSD1306::display(void)
von Adafruit. Das DisplayRAM wird mit 0x00 beschrieben. Ich greife in meiner Routine innerhalb einer Schleife auf das SPI Datenregister zu, da ich hier CS die ganze Zeit auf low lasse. Man kann auch deine "data" Routine verwenden.

Den Assembler Code habe ich "aus der Hand" geschrieben ...

Code:
Display_Clear:
  ldi temp1, 0x00        ; SSD1306_SETLOWCOLUMN
  rcall command
  ldi temp1, 0x10        ; SSD1306_SETHIGHCOLUMN
  rcall command
  ldi temp1, 0x40        ; SSD1306_SETSTARTLINE
  rcall command

  ; Daten übertragen
  sbi display,cs                    ; CS high
  sbi display,dc                ; DC high
  cbi display,cs                    ; CS low

  ldi zl, low(128*8)        ; 128x8 Byte senden. 128 Spalten, 8 Zeilen
  ldi zh, high(128*8)
  ldi temp1, 0x00        ; Daten zum senden (zum Füllen 0xFF verwenden)
  display_clear_loop:

     out SPDR,temp1                         
     spiwarten2:    
     sbis SPSR, SPIF          
     rjmp spiwarten2    

    sbiw zl, 1
  brne display_clear_loop

  sbi display,cs                    ; CS high

ret


Hier der C Code von Adafruit (Nur SPI Bereich, Adafruit bildet das DisplayRAM im SRAM des AVR ab, die Daten kommen hier vom Array buffer[])
Code:
void Adafruit_SSD1306::display(void) {
  ssd1306_command(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0
  ssd1306_command(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0
  ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0

  if (sid != -1)
  {
    // SPI
    *csport |= cspinmask;
    *dcport |= dcpinmask;
    *csport &= ~cspinmask;

    for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
      fastSPIwrite(buffer[i]);
      //ssd1306_data(buffer[i]);
    }
    // i wonder why we have to do this (check datasheet)
    if (SSD1306_LCDHEIGHT == 32) {
      for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
        //ssd1306_data(0);
        fastSPIwrite(0);
      }
    }
    *csport |= cspinmask;
  }
}

Dieses ...
Code:
// i wonder why we have to do this (check datasheet)
if (SSD1306_LCDHEIGHT == 32) {
...
   }

habe ich weggelassen, hier wundere ich mich ebenfalls ;)


Dirk :ciao:
 
So habe das mal in meinProgramm übernommennur die sache bleibt leider Dunkel:( habe auch schon verschiedene kontrasteinstellungen versucht aber nix passiert

Hiermal das Aktuelle Programm

Code:
 ;******************************************************************************
 ;GLCD mit SSD1306-Controller an ATmega16
 ;******************************************************************************

 ;Display: Monochrome 1.3" 128x64 OLED graphic display(AVR-praxis.de)

 ;fmcu=16MHz

 ;Controllerpin   :Displaypin
 ;PB1				5 CS
 ;PB2				4 RST
 ;PB3				3 D/C
 ;PB5 MOSI			1 DATA
 ;PB7 CLK			2 CLK

 ;******************************************************************************
 ;Registerdefinition
 ;******************************************************************************

 .def temp			=r16
 .def temp1			=r17
 .def counter		=r18

 .equ display		=PORTB
 .equ cs			=1
 .equ rst			=2
 .equ dc			=3

 ;******************************************************************************
 ;Interuptvektoren
 ;******************************************************************************

 .org 0x00 rjmp init	;reset

 ;******************************************************************************
 ;Initialisieren
 ;******************************************************************************

 init:
 ;Register löschen
 clr temp
 clr temp1
 clr counter

 ;stack initialisieren
 ldi temp,high(ramend)
 out sph,temp
 ldi temp,low(ramend)
 out spl,temp

 ;Spi initialisieren
 ldi temp,0b10111110	;Setze sck,MOSI,PB4(DAder Atmega sonst in den slavemodus geht),
 out DDRB,temp			;D/C(PB3),RST(PB2) und CS(PB1) als Ausgang

 ldi temp,0b01010001	;Bit7 spi interrupt enable off, Bit6 SPI enable, Bit5 MSB first, Bit4 SPI Master					
 out SPCR,temp			;Bit3 Clockpolarity high, Bit2 Clock Phase Alpha, Bit1,0 f/16

 ;Display initialisieren
 cbi display,rst		;Display resetten
 rcall hundertms

 sbi display,rst
 rcall hundertms

 ldi temp1,0xAE			;Display Ausschalten
 rcall command
 
 ldi temp1,0x00			;definiere low Byte für Page adressierung
 rcall command

 ldi temp1,0x10			;definiere high Byte für Page adressierung
 rcall command

 ldi temp1,0x40			;Startposition festlegen
 rcall command

ldi temp1,0x81			;Kontrast einstellen
rcall command
ldi temp1,0xCF
rcall command

ldi temp1,0xA1			;Zuordnung Spaltenadresse 127 zu segment 0
rcall command

ldi temp1,0xA6			;Anzeige normal/nicht invertiert
rcall command

ldi temp1,0xA8			;Multiplexrate auf 1/64 einstellen
rcall command
ldi temp1,0x3F
rcall command

ldi temp1,0xD3			;setze vertikalen Display offset
rcall command			
ldi	temp1,0x00
rcall command

ldi temp1,0xD5			;setze Displayoszillatorfrequenz
rcall command
ldi temp1,0x80
rcall command

ldi temp1,0xD9			;setze pr-charge periode
rcall command
ldi temp1,0x22
rcall command

ldi temp1,0xDA			;setze Compin-Hardwarekonfiguration
rcall command
ldi temp1,0x12
rcall command

ldi temp1,0xDB			;Vcomh level festlegen
rcall command
ldi temp1,0x40
rcall command

ldi temp1,0x8D			;Deaktiviere charge-pump
rcall command
ldi temp1,0x10
rcall command

ldi temp1,0xAF			;Schalte Display ein
rcall command


 ;******************************************************************************
 ;Hauptprogramm
 ;******************************************************************************
 main:
 ;Fülle Display
 ldi temp1, 0x00        ;SSD1306_SETLOWCOLUMN
 rcall command
 ldi temp1, 0x10        ;SSD1306_SETHIGHCOLUMN
 rcall command
 ldi temp1, 0x40        ;SSD1306_SETSTARTLINE
 rcall command


 ldi zl, low(128*8)     ;128x8 Byte senden. 128 Spalten, 8 Zeilen
 ldi zh, high(128*8)

 filldisplay:
 ldi temp1, 0xFF        ;Daten zum senden (zum Füllen 0xFF verwenden)
 rcall data
 sbiw zl,1
 brne filldisplay

 rjmp main

 ;******************************************************************************
 ;Unterprogramme
 ;******************************************************************************

 ;sende Kommando
 command:
 cbi display,dc
 rjmp send

 ;sende Daten
 data:
 sbi display,dc

 ;Senden über SPI
 send:
 cbi display,cs			;CS auf low ziehen
 out SPDR,temp1			;Byte aus Temp1 nach SPDR schieben und somit senden starten
 			
 spiwarten:	
 sbis SPSR, SPIF		;wenn das SPIF flag gesetzt ist wurde das Byte gesendet
 rjmp spiwarten	

 in temp1,SPDR			;lese empfangenes byte und lösche SPIF
 sbi display,cs 
 ret

 ;Warteroutinen
 hundertms:
 ldi counter,9
 
 zehnms:
 ldi xh,high(40000)
 ldi xl,low(40000)
 
 loop:
 sbiw xl,1
 brne loop

 cpi counter,0
 breq endloop

 dec counter
 rjmp zehnms

 endloop:
 ret

Kann es sein das ich den chipselect erst nach der kompletten übertragung auf high setzen darf?:confused:
 
Kann es sein das ich den chipselect erst nach der kompletten übertragung auf high setzen darf?:confused:

Ich bin mir hier nicht ganz sicher. Bei Command und Data wird in den Adafruit Routinen
  1. CS auf high gesetzt
  2. DC geändert
  3. CS auf low
  4. SPI Übertragung
  5. CS auf high

Bei der Routine, die den gesamten GrafikRAM Inhalt überträgt ist es ähnlich, nur dass bei Punkt 4 alle Daten übertragen werden.

Du könntest auch nochmal nach der SPI Einstellung schauen, ClockPolarity und ClockPhase. Adafruit verwendet hier SoftwareSPI (siehe ganz unten), da könntest du mal "abschauen".

Ich kann mir das heute Abend nochmal genauer anschauen, habe leider gerade wenig Zeit.

Dirk :ciao:



Command

Code:
    // SPI
    //digitalWrite(cs, HIGH);
    *csport |= cspinmask;
    //digitalWrite(dc, LOW);
    *dcport &= ~dcpinmask;
    //digitalWrite(cs, LOW);
    *csport &= ~cspinmask;
    fastSPIwrite(c);
    //digitalWrite(cs, HIGH);
    *csport |= cspinmask;



Data

Code:
    // SPI
    //digitalWrite(cs, HIGH);
    *csport |= cspinmask;
    //digitalWrite(dc, HIGH);
    *dcport |= dcpinmask;
    //digitalWrite(cs, LOW);
    *csport &= ~cspinmask;
    fastSPIwrite(c);
    //digitalWrite(cs, HIGH);
    *csport |= cspinmask;


Daten komplett übertragen

Code:
    // SPI
    *csport |= cspinmask;
    *dcport |= dcpinmask;
    *csport &= ~cspinmask;

    for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
      fastSPIwrite(buffer[i]);
      //ssd1306_data(buffer[i]);
    }
    // i wonder why we have to do this (check datasheet)
    if (SSD1306_LCDHEIGHT == 32) {
      for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
        //ssd1306_data(0);
        fastSPIwrite(0);
      }
    }
    *csport |= cspinmask;


Software SPI

Code:
inline void Adafruit_SSD1306::fastSPIwrite(uint8_t d) {
  
  for(uint8_t bit = 0x80; bit; bit >>= 1) {
    *clkport &= ~clkpinmask;
    if(d & bit) *mosiport |= mosipinmask;
    else *mosiport &= ~mosipinmask;
    *clkport |= clkpinmask;
  }
  //*csport |= cspinmask;
}
 
Vielleicht liegt es am SPI Mode.

Du hast SPI Mode 0 eingestellt (CPOL=0, CPHA=0).
Code:
ldi temp,0b0101[B]00[/B]01    ;Bit7 spi interrupt enable off, Bit6 SPI enable, Bit5 MSB first, Bit4 SPI Master
out SPCR,temp          ;Bit3 Clockpolarity [B][COLOR=#b22222]high[/COLOR][/B], Bit2 Clock Phase Alpha, Bit1,0 f/16

Versuche es mal mit Mode 3 (CPHA=1, CPOL=1). Dies entspricht dann der SoftwareSPI Lösung von Adafruit.
Code:
inline void Adafruit_SSD1306::fastSPIwrite(uint8_t d) {
  
  for(uint8_t bit = 0x80; bit; bit >>= 1) {
    *clkport &= ~clkpinmask;
    if(d & bit) *mosiport |=  mosipinmask;
    else        *mosiport &= ~mosipinmask;
    *clkport |=  clkpinmask;
  }
  //*csport |= cspinmask;
}

spi_modi.png
 
Mit den Arduino und C sachen kann ich leider nicht viel anfangen da ich da ncht ganz durchblicke:rolleyes:

ich habe die "main" schleife jetzt so geändert das CS erst nach der kompletten grafikübertragung high gesetzt wird
aber sowohl im SPI "0-mode" als auch im SPI "3-mode" tut sich nichts


Code:
 ;******************************************************************************
 ;GLCD mit SSD1306-Controller an ATmega16
 ;******************************************************************************

 ;Display: Monochrome 1.3" 128x64 OLED graphic display(AVR-praxis.de)

 ;fmcu=16MHz

 ;Controllerpin   :Displaypin
 ;PB1				5 CS
 ;PB2				4 RST
 ;PB3				3 D/C
 ;PB5 MOSI			1 DATA
 ;PB7 CLK			2 CLK

 ;******************************************************************************
 ;Registerdefinition
 ;******************************************************************************

 .def temp			=r16
 .def temp1			=r17
 .def counter		=r18

 .equ display		=PORTB
 .equ cs			=1
 .equ rst			=2
 .equ dc			=3

 ;******************************************************************************
 ;Interuptvektoren
 ;******************************************************************************

 .org 0x00 rjmp init	;reset

 ;******************************************************************************
 ;Initialisieren
 ;******************************************************************************

 init:
 ;Register löschen
 clr temp
 clr temp1
 clr counter
 clr zh
 clr zl

 ;stack initialisieren
 ldi temp,high(ramend)
 out sph,temp
 ldi temp,low(ramend)
 out spl,temp

 ;Spi initialisieren
 ldi temp,0b10111110	;Setze sck,MOSI,PB4(da der Atmega sonst in den slavemodus geht),
 out DDRB,temp			;D/C(PB3),RST(PB2) und CS(PB1) als Ausgang

 ldi temp,0b01011101	;Bit7 spi interrupt enable off, Bit6 SPI enable, Bit5 MSB first, Bit4 SPI Master					
 out SPCR,temp			;Bit3 Clockpolarity high, Bit2 Clock Phase Alpha, Bit1,0 f/16

 ;Display initialisieren
 cbi display,rst		;Display resetten
 rcall hundertms

 sbi display,rst
 rcall hundertms

 sbi display,cs			;CS auf high setzen

 ldi temp1,0xAE			;Display Ausschalten
 rcall command
 
 ldi temp1,0x00			;definiere low Byte für Page adressierung
 rcall command

 ldi temp1,0x10			;definiere high Byte für Page adressierung
 rcall command

 ldi temp1,0x40			;Startposition festlegen
 rcall command

ldi temp1,0x81			;Kontrast einstellen
rcall command
ldi temp1,0xCF
rcall command

ldi temp1,0xA1			;Zuordnung Spaltenadresse 127 zu segment 0
rcall command

ldi temp1,0xA6			;Anzeige normal/nicht invertiert
rcall command

ldi temp1,0xA8			;Multiplexrate auf 1/64 einstellen
rcall command
ldi temp1,0x3F
rcall command

ldi temp1,0xD3			;setze vertikalen Display offset
rcall command			
ldi	temp1,0x00
rcall command

ldi temp1,0xD5			;setze Displayoszillatorfrequenz
rcall command
ldi temp1,0x80
rcall command

ldi temp1,0xD9			;setze pr-charge periode
rcall command
ldi temp1,0x22
rcall command

ldi temp1,0xDA			;setze Compin-Hardwarekonfiguration
rcall command
ldi temp1,0x12
rcall command

ldi temp1,0xDB			;Vcomh level festlegen
rcall command
ldi temp1,0x40
rcall command

ldi temp1,0x8D			;Deaktiviere charge-pump
rcall command
ldi temp1,0x10
rcall command

ldi temp1,0xAF			;Schalte Display ein
rcall command


 ;******************************************************************************
 ;Hauptprogramm
 ;******************************************************************************
 main:
 ;Fülle Display
 ldi temp1, 0x00        ;SSD1306_SETLOWCOLUMN
 rcall command
 ldi temp1, 0x10        ;SSD1306_SETHIGHCOLUMN
 rcall command
 ldi temp1, 0x40        ;SSD1306_SETSTARTLINE
 rcall command

sbi display,cs
sbi display,dc
cbi display,cs

 ldi zl, low(128*8)     ;128x8 Byte senden. 128 Spalten, 8 Zeilen
 ldi zh, high(128*8)
 ldi temp1, 0xFF        ;Daten zum senden (zum Füllen 0xFF verwenden)

 filldisplay:
 out SPDR,temp1 
                         
 spiwarten2:    
 sbis SPSR, SPIF          
 rjmp spiwarten2 

 sbiw zl,1
 brne filldisplay

 sbi display,cs

 rjmp main

 ;******************************************************************************
 ;Unterprogramme
 ;******************************************************************************

 ;sende Kommando
 command:
 cbi display,dc
 rjmp send

 ;sende Daten
 data:
 sbi display,dc

 ;Senden über SPI
 send:
 cbi display,cs			;CS auf low ziehen
 out SPDR,temp1			;Byte aus Temp1 nach SPDR schieben und somit senden starten
 			
 spiwarten:	
 sbis SPSR, SPIF		;wenn das SPIF flag gesetzt ist wurde das Byte gesendet
 rjmp spiwarten	

 in temp1,SPDR			;lese empfangenes byte und lösche SPIF
 sbi display,cs 
 ret

 ;Warteroutinen
 hundertms:
 ldi counter,9
 
 zehnms:
 ldi xh,high(40000)
 ldi xl,low(40000)
 
 loop:
 sbiw xl,1
 brne loop

 cpi counter,0
 breq endloop

 dec counter
 rjmp zehnms

 endloop:
 ret
 
Hallo Senpai,

hier ist nun ChipSelect immer high, das Display übernimmt dann keine Daten ...

Code:
 main:
 ;Fülle Display
 ldi temp1, 0x00        ;SSD1306_SETLOWCOLUMN
 rcall command
 ldi temp1, 0x10        ;SSD1306_SETHIGHCOLUMN
 rcall command
 ldi temp1, 0x40        ;SSD1306_SETSTARTLINE
 rcall command


 ldi zl, low(128*8)     ;128x8 Byte senden. 128 Spalten, 8 Zeilen
 ldi zh, high(128*8)
 ldi temp1, 0xFF        ;Daten zum senden (zum Füllen 0xFF verwenden)

 filldisplay:
 out SPDR,temp1 
                         
 spiwarten2:    
 sbis SPSR, SPIF          
 rjmp spiwarten2 

 sbiw zl,1
 brne filldisplay

 sbi display,cs

 rjmp main


Wenn es immer noch nicht geht, versuche ich es mir später noch einmal anzusehen.

Dirk :ciao:


EDIT: Ich habe gesehen, du hast es nun geändert.
 
hatte es schon geändert
es war nur ein fehler beim kopieren des programms gewesen
aber auch mit funktionierendem CS ändert sich eben nix
 
Nur um auf Nr. sicher zu gehen hier nochmal ein gezeichneter Plan wie das Display am ATmega dranhängt.
 

Anhänge

  • GLCD(SSD1306)-an-ATmega16.GIF
    GLCD(SSD1306)-an-ATmega16.GIF
    46 KB · Aufrufe: 13
Hich habe mir noch einmal die Initialisierung angesehen. Adafruit hat eine etwas andere Initialisierung wie du (Ich bin mir aber nicht hundertprozentig sicher, ob es genau zu dem Display passt)

Kommando COMSCANDEC fehlt zum Beispiel?!

Werte für "aktivierte Chargepump" habe ich hier auskommentiert.

Da ich den Code unten schnell zusammengeschrieben habe, kann ich nicht garantieren, dass dort kein Schreibfehler drin ist.
Also vielleicht da auch nochmal selber kontrollieren.

Quelle:
https://github.com/adafruit/Adafruit_SSD1306/blob/master/Adafruit_SSD1306.cpp
https://github.com/adafruit/Adafruit_SSD1306/blob/master/Adafruit_SSD1306.h

Dies ist die umgeschriebene Initialisierung ...

Code:
ldi temp1, 0xAE        ; SSD1306_DISPLAYOFF
rcall command

ldi temp1, 0xD5        ; SSD1306_SETDISPLAYCLOCKDIV
rcall command
ldi temp1, 0x80
rcall command

ldi temp1, 0xA8        ; SSD1306_SETMULTIPLEX
rcall command
ldi temp1, 0x3F
rcall command

ldi temp1, 0xD3        ; SSD1306_SETDISPLAYOFFSET
rcall command
ldi temp1, 0x00
rcall command

ldi temp1, 0x40        ; SSD1306_SETSTARTLINE | 0x0
rcall command

ldi temp1, 0x8D        ; SSD1306_CHARGEPUMP
rcall command
ldi temp1, 0x10        ; off
;ldi temp1, 0x14    ; on
rcall command

ldi temp1, 0x20        ; SSD1306_MEMORYMODE
rcall command
ldi temp1, 0x00
rcall command

ldi temp1, 0xA1        ; SSD1306_SEGREMAP | 0x1
rcall command

ldi temp1, 0xC8        ; SSD1306_COMSCANDEC
rcall command

ldi temp1, 0xDA        ; SSD1306_SETCOMPINS
rcall command
ldi temp1, 0x12
rcall command

ldi temp1, 0x81        ; SSD1306_SETCONTRAST
rcall command
ldi temp1, 0x9F        ; external VCC
;ldi temp1, 0xCF    ; no external VCC
rcall command

ldi temp1, 0xD9        ; SSD1306_SETPRECHARGE
rcall command
ldi temp1, 0x22        ; external VCC
;ldi temp1, 0xF1    ; no external VCC
rcall command

ldi temp1, 0xDB        ; SSD1306_SETVCOMDETECT
rcall command
ldi temp1, 0x40
rcall command

ldi temp1, 0xA4        ; SSD1306_DISPLAYALLON_RESUME
rcall command

ldi temp1, 0xA6        ; SSD1306_NORMALDISPLAY
rcall command
    
ldi temp1, 0xAF        ; SSD1306_DISPLAYON
rcall command

Hier nochmal der originale Code für 128x64 OLED ...
Code:
    // Init sequence for 128x64 OLED module
    ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE
    ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
    ssd1306_command(0x80); // the suggested ratio 0x80
    ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8
    ssd1306_command(0x3F);
    ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3
    ssd1306_command(0x0); // no offset
    ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0
    ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D
    if (vccstate == SSD1306_EXTERNALVCC)
      { ssd1306_command(0x10); }
    else
      { ssd1306_command(0x14); }
    ssd1306_command(SSD1306_MEMORYMODE); // 0x20
    ssd1306_command(0x00); // 0x0 act like ks0108
    ssd1306_command(SSD1306_SEGREMAP | 0x1);
    ssd1306_command(SSD1306_COMSCANDEC);
    ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
    ssd1306_command(0x12);
    ssd1306_command(SSD1306_SETCONTRAST); // 0x81
    if (vccstate == SSD1306_EXTERNALVCC)
      { ssd1306_command(0x9F); }
    else
      { ssd1306_command(0xCF); }
    ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
    if (vccstate == SSD1306_EXTERNALVCC)
      { ssd1306_command(0x22); }
    else
      { ssd1306_command(0xF1); }
    ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB
    ssd1306_command(0x40);
    ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4
    ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6
  
  ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel
 
Habe nun mal deine Initialisierung 1 zu 1 übernommen aber ohne erfolg:angry:

Code:
 ;******************************************************************************
 ;GLCD mit SSD1306-Controller an ATmega16
 ;******************************************************************************

 ;Display: Monochrome 1.3" 128x64 OLED graphic display(AVR-praxis.de)

 ;fmcu=16MHz

 ;Controllerpin   :Displaypin
 ;PB1				5 CS
 ;PB2				4 RST
 ;PB3				3 D/C
 ;PB5 MOSI			1 DATA
 ;PB7 CLK			2 CLK

 ;******************************************************************************
 ;Registerdefinition
 ;******************************************************************************

 .def temp			=r16
 .def temp1			=r17
 .def counter		=r18

 .equ display		=PORTB
 .equ cs			=1
 .equ rst			=2
 .equ dc			=3

 ;******************************************************************************
 ;Interuptvektoren
 ;******************************************************************************

 .org 0x00 rjmp init	;reset

 ;******************************************************************************
 ;Initialisieren
 ;******************************************************************************

 init:
 ;Register löschen
 clr temp
 clr temp1
 clr counter
 clr zh
 clr zl

 ;stack initialisieren
 ldi temp,high(ramend)
 out sph,temp
 ldi temp,low(ramend)
 out spl,temp

 ;Spi initialisieren
 ldi temp,0b10111110	;Setze sck,MOSI,PB4(da der Atmega sonst in den slavemodus geht),
 out DDRB,temp			;D/C(PB3),RST(PB2) und CS(PB1) als Ausgang

 ldi temp,0b01011101	;Bit7 spi interrupt enable off, Bit6 SPI enable, Bit5 MSB first, Bit4 SPI Master					
 out SPCR,temp			;Bit3 Clockpolarity high, Bit2 Clock Phase Alpha, Bit1,0 f/16

 ;Display initialisieren
/* cbi display,rst		;Display resetten
 rcall hundertms

 sbi display,rst
 rcall hundertms

 sbi display,cs			;CS auf high setzen

 ldi temp1,0xAE			;Display Ausschalten
 rcall command
 
 ldi temp1,0x00			;definiere low Byte für Page adressierung
 rcall command

 ldi temp1,0x10			;definiere high Byte für Page adressierung
 rcall command

 ldi temp1,0x40			;Startposition festlegen
 rcall command

 ldi temp1,0x81			;Kontrast einstellen
 rcall command
 ldi temp1,0xCF
 rcall command

 ldi temp1,0xA1			;Zuordnung Spaltenadresse 127 zu segment 0
 rcall command

 ldi temp1,0xA6			;Anzeige normal/nicht invertiert
 rcall command

 ldi temp1,0xA8			;Multiplexrate auf 1/64 einstellen
 rcall command
 ldi temp1,0x3F
 rcall command

 ldi temp1,0xD3			;setze vertikalen Display offset
 rcall command			
 ldi	temp1,0x00
 rcall command

 ldi temp1,0xD5			;setze Displayoszillatorfrequenz
 rcall command
 ldi temp1,0x80
 rcall command

 ldi temp1,0xD9			;setze pr-charge periode
 rcall command
 ldi temp1,0x22
 rcall command

 ldi temp1,0xDA			;setze Compin-Hardwarekonfiguration
 rcall command
 ldi temp1,0x12
 rcall command

 ldi temp1,0xDB			;Vcomh level festlegen
 rcall command
 ldi temp1,0x40
 rcall command

 ldi temp1,0x8D			;Deaktiviere charge-pump
 rcall command
 ldi temp1,0x10
 rcall command

 ldi temp1,0xAF			;Schalte Display ein
 rcall command*/

 ldi temp1, 0xAE        ; SSD1306_DISPLAYOFF
 rcall command

 ldi temp1, 0xD5        ; SSD1306_SETDISPLAYCLOCKDIV
 rcall command
 ldi temp1, 0x80
 rcall command

 ldi temp1, 0xA8        ; SSD1306_SETMULTIPLEX
 rcall command
 ldi temp1, 0x3F
 rcall command

 ldi temp1, 0xD3        ; SSD1306_SETDISPLAYOFFSET
 rcall command
 ldi temp1, 0x00
 rcall command

 ldi temp1, 0x40        ; SSD1306_SETSTARTLINE | 0x0
 rcall command

 ldi temp1, 0x8D        ; SSD1306_CHARGEPUMP
 rcall command
 ldi temp1, 0x10        ; off
 ;ldi temp1, 0x14		; on
 rcall command

 ldi temp1, 0x20        ; SSD1306_MEMORYMODE
 rcall command
 ldi temp1, 0x00
 rcall command

 ldi temp1, 0xA1        ; SSD1306_SEGREMAP | 0x1
 rcall command

 ldi temp1, 0xC8        ; SSD1306_COMSCANDEC
 rcall command

 ldi temp1, 0xDA        ; SSD1306_SETCOMPINS
 rcall command
 ldi temp1, 0x12
 rcall command

 ldi temp1, 0x81        ; SSD1306_SETCONTRAST
 rcall command
 ldi temp1, 0x9F        ; external VCC
 ;ldi temp1, 0xCF		; no external VCC
 rcall command

 ldi temp1, 0xD9        ; SSD1306_SETPRECHARGE
 rcall command
 ldi temp1, 0x22        ; external VCC
 ;ldi temp1, 0xF1		; no external VCC
 rcall command

 ldi temp1, 0xDB        ; SSD1306_SETVCOMDETECT
 rcall command
 ldi temp1, 0x40
 rcall command

 ldi temp1, 0xA4        ; SSD1306_DISPLAYALLON_RESUME
 rcall command

 ldi temp1, 0xA6        ; SSD1306_NORMALDISPLAY
 rcall command
    
 ldi temp1, 0xAF        ; SSD1306_DISPLAYON
 rcall command


 ;******************************************************************************
 ;Hauptprogramm
 ;******************************************************************************
 main:
 ;Fülle Display
 ldi temp1, 0x00        ;SSD1306_SETLOWCOLUMN
 rcall command
 ldi temp1, 0x10        ;SSD1306_SETHIGHCOLUMN
 rcall command
 ldi temp1, 0x40        ;SSD1306_SETSTARTLINE
 rcall command

 sbi display,cs
 sbi display,dc
 cbi display,cs

 ldi zl, low(128*8)     ;128x8 Byte senden. 128 Spalten, 8 Zeilen
 ldi zh, high(128*8)
 ldi temp1, 0xFF        ;Daten zum senden (zum Füllen 0xFF verwenden)

 filldisplay:
 out SPDR,temp1 
                         
 spiwarten2:    
 sbis SPSR, SPIF          
 rjmp spiwarten2 

 sbiw zl,1
 brne filldisplay

 sbi display,cs

 rjmp main

 ;******************************************************************************
 ;Unterprogramme
 ;******************************************************************************

 ;sende Kommando
 command:
 cbi display,dc
 rjmp send

 ;sende Daten
 data:
 sbi display,dc

 ;Senden über SPI
 send:
 cbi display,cs			;CS auf low ziehen
 out SPDR,temp1			;Byte aus Temp1 nach SPDR schieben und somit senden starten
 			
 spiwarten:	
 sbis SPSR, SPIF		;wenn das SPIF flag gesetzt ist wurde das Byte gesendet
 rjmp spiwarten	

 in temp1,SPDR			;lese empfangenes byte und lösche SPIF
 sbi display,cs 
 ret

 ;Warteroutinen
 hundertms:
 ldi counter,9
 
 zehnms:
 ldi xh,high(40000)
 ldi xl,low(40000)
 
 loop:
 sbiw xl,1
 brne loop

 cpi counter,0
 breq endloop

 dec counter
 rjmp zehnms

 endloop:
 ret
 
Habe nun mal deine Initialisierung 1 zu 1 übernommen aber ohne erfolg:angry:

Du hast jetzt den Display Reset mit der Pause am Anfang auskommentiert:

Code:
/* cbi display,rst        ;Display resetten
 rcall hundertms

 sbi display,rst
 rcall hundertms

 sbi display,cs            ;CS auf high setzen

Ob das was ausmacht und ob das Display selbst einen PowerOnReset macht, weiß ich nicht. Ohne Pause ist auf jedenfall nicht gut.

Ob mein Code stimmt, kann ich nicht versprechen, ich hatte diesen gestern schnell mal geschrieben, als ich bemerkt hatte, dass dein Initialisierungscode nicht hundertprozentig mit dem von Adafruit übereinstimmt. Also am besten da selber nochmal die Initialisierungswerte vergleichen. Und auch mal die Sache mit externere Spannung/Chargepump prüfen. Je nach Hardwarekonfiguration musst du das entsprechend initialisieren (Adafruit hat hier einen "Compilerschalter" verwendet).

Dirk :ciao:
 
oh das mit dem reset hab ich in der eile übersehen

was meinst du mit compilerschalter?
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)