Forum: Analoge Elektronik und Schaltungstechnik Xmega SPI Problem


von Christian G. (Firma: privat) (dotti)


Angehängte Dateien:

Lesenswert?

Hallo,

folgendes Problem hier:

- Xmega 32A4U mit SSD1306 (OLED) funktioniert (Xprotolab - gekauft..)
- Xmega 32A4U (Dev-Board) mit SSD1306 Display (Eigenbau) lässt das 
Display auch korrekt anzeigen (einfach Xprotolab Software geflasht - 
prima)
- Xmega 64A1 mit selbigem Eigenbau Display: Pixelsuppe
- komisch: an der Software nichts geändert, nur Device geändert (Atmel 
Studio 6.2)
;(

Pixelsuppe ist immer statisch, d.h. es gibt statt dem (erwarteten) Logo 
dieselbe Pixelsuppe zu sehen.

Software komplett entkernt (war recht Spagetti), so dass sie nur das 
Display initialisiert und das Logo anzeigt, so behält man die Ports 
unter strikter Kontrolle (das nicht irgendwo ungewollte IO passiert, wo 
man es nicht erwartet/möchte) -> Pixelsuppe

Mit/ohne externem Oszillator (16Mhz, per PLL auf 32Mhz) probiert -> 
Pixelsuppe

SPI Baudrate gedrosselt um Faktor 10^7 (war: 8Mhz) -> Pixelsuppe

Oszi angeschaut von SPI CLK und SDIN: Clock clockt und Data datat. Works 
as designed?! Aber (noch) nicht verglichen mit 32A4U Case (wird 
vermutlich anders aussehen, klar). Auf dem Gebiet bin ich wohl recht 
Newbie.

Mein Latein ist aber hier erst einmal am Ende.

Der USART wird per DMA versorgt, evtl. gibt es da noch ein Problem?
Aber scheint mir recht eindeutig und funktioniert für alle probierten 
Ports immer mit identische Pixelsuppe (auf A1) bzw. mit Logo (auf A4U) - 
hier ists mal PORTD:

// Transfer buffer to display
void dma_display(void) {
    clrbit(LCD_CTRL, LCD_CS);    // Select
    clrbit(LCD_CTRL,LCD_RS);            // Instruction mode
    setbit(DMA.CH2.CTRLA,6);            // reset DMA CH0
    DMA.CH2.ADDRCTRL  = 0b00010000;     // Increment source, Destination 
fixed
    DMA.CH2.TRFCNT    = 3;              // buffer size
    DMA.CH2.DESTADDR0 = (((uint16_t) &USARTD0.DATA)>>0*8) & 0xFF;
    DMA.CH2.DESTADDR1 = (((uint16_t) &USARTD0.DATA)>>1*8) & 0xFF;
    DMA.CH2.TRIGSRC   = DMA_CH_TRIGSRC_USARTD0_DRE_gc;
    DMA.CH2.SRCADDR0  = (((uint16_t)(Disp_send.display_setup))>>0*8) & 
0xFF;
    DMA.CH2.SRCADDR1  = (((uint16_t)(Disp_send.display_setup))>>1*8) & 
0xFF;
    DMA.CH2.CTRLA     = 0b10000100;     // no repeat, 1 byte burst
    _delay_us(4);       // Wait for 3 bytes to be sent
    setbit(LCD_CTRL, LCD_RS);           // Data mode
    DMA.CH2.TRFCNT    = 1024;           // buffer size
    DMA.CH2.CTRLB     = 0b00010001;     // Low priority interrupt on 
complete
    DMA.CH2.CTRLA     = 0b10000100;     // no repeat, 1 byte burst
}

// DMA done, now at most 2 bytes are left to be sent
ISR(DMA_CH2_vect) {
    _delay_us(3);              // Wait for last byte to transfer
    setbit(LCD_CTRL, LCD_CS);               // No Select
    setbit(DMA.INTFLAGS, 0);
}


Hat von euch jemand Erfahrung/Tipps? Bin grad was angefressen nach zwei 
Tagen testen mit (immerhin) reproduzierbaren, aber unerwünschten 
Ergebnissen. Kann es ein Softwareproblem sein? Wie beschrieben, die 
identische Software läuft auf dem kleineren Referenz Devboard mit 32A4U 
ohne Mucken mit demselben Display(-Board). Pins auf Hardwareseite sind 
getestet (Kontakte + Led-Testprogramm). Identische Pixelsuppe gibts auf 
verschiedenen verwendeten Ports. Versorgung und Blocking überprüft und 
erstere getauscht - sollte eigentlich OK sein. Ohnehin: da das Problem 
auch bei verschiedenen Baudraten in derselben Pixelsuppe mündet, würde 
ich Hardwareprobleme eher ausschliessen.

Wer reinschauen mag in den Source: 
https://github.com/ganzziani/XScopes-Firmware

Will eigentlich die Firmware nutzen, um zu verstehen. Menüstruktur 
könnte ich gebrauchen und Display (obwohl seriell, aber da DMA) ist 
rasend schnell - der Mann hat schon gut Ahnung von dem, was er tut :)

Vielen Dank fürs Grübeln!

von NoDelayISR (Gast)


Lesenswert?

Christian G. schrieb:
> ISR(DMA_CH2_vect) {
>     _delay_us(3);

Wer in einer ISR ein delay einbaut, dem ist nicht mehr zu helfen.

von Dennis X. (Gast)


Lesenswert?

Schau mal ob der verwendete spi Modus der richtige hatte da vor kurzem 
mal ziemlich Probleme. Allerdings mit de D4 Reihe der xmegas.

von Basti (Gast)


Lesenswert?

@NoDelay... das würde ich so nicht pauschalisieren.... Ganz neben bei 
hat der XMega ja 3 Interrupt Prioritäten...
Aber meist geht es trotzdem ohne... kommt halt auf den Aufwand an, den 
man reingesteckt hat ...

Wenn du DMA benutzt musst du die UART zum SPI umbauen... dann ändert 
sich auch die Pinbelegung SPI <-> USART SPI

von Christian G. (Firma: privat) (dotti)


Lesenswert?

Basti schrieb:
> Wenn du DMA benutzt musst du die UART zum SPI umbauen... dann ändert
> sich auch die Pinbelegung SPI <-> USART SPI

Belegung ist korrekt. Code funktioniert einwandfrei im 32A4U (wie 
geschrieben) eben nur nicht im 64A1. Ich weiss, ist ein 
Totschlagargument.


NoDelayISR schrieb:
> Wer in einer ISR ein delay einbaut, dem ist nicht mehr zu helfen.

Ist schon recht hemdsärmelig gebaut, das stimmt, nicht nur an dieser 
Stelle. Sieht mir nach gebliebenem Provisorium aus. Trotzdem danke.

von Timmo H. (masterfx)


Lesenswert?

Also diese Pixelsuppe bekommst du auf zwei Wege. Entweder du 
initialisierst nur das Display, aber schreibst keine Daten in den 
Grafikspeicher (z.B. dein Controller schmiert ab, bleibt hängen oder 
sonst was) oder du schreibst Daten aus einem falschen Bereich deines µCs 
in den Grafikspeicher des Displays.

Ich habe das Display schon sehr lange im Einsatz und auch Problemlos auf 
A4, A4U, A3 etc am laufen gehabt.

Lass mal erstmal das DMA-Geschlunse weg und versuche anstatt 
dma_display() mal folgende Routine:
1
void show_display(void) {
2
    uint16_t i;
3
    uint8_t *p=display_buffer;
4
  
5
    clrbit(LCD_CTRL, LCD_CS);               // Select
6
7
    LcdInstructionWrite(LCD_SET_PAGE);
8
    LcdInstructionWrite(LCD_SET_COL_HI);    // Set column at 0
9
    LcdInstructionWrite(LCD_SET_COL_LO);
10
    cli();
11
    setbit(LCD_CTRL, LCD_RS);               // Data mode
12
     // Send data to display
13
    for(i=0; i<1024; i++) {
14
        while(!testbit(USARTD0.STATUS,5));  // Wait for DATA empty
15
      USARTD0.DATA= *p++;
16
    }
17
  
18
    while(!testbit(USARTD0.STATUS,6));      // Wait until transmit done
19
   
20
    setbit(USARTD0.STATUS,6);               // Clear flag
21
    setbit(LCD_CTRL, LCD_CS);               // No Select
22
    sei();
23
}

Den Uart musst du natürlich auf deinen Verwendeten verdrehen. 
"display_buffer" wäre bei dir dann wohl Disp_data.display_data

meine PortInit sieht so aus:
1
void GLCD_LcdPortInit(void){
2
    
3
  // MOSI, CLK
4
  PORTD.DIR = (1<<PIN1) | (1<<PIN3);
5
  PORTD.OUTCLR = (1<<PIN1) | (1<<PIN3);
6
    
7
  //Set Pins to output for LCD_RS, LCD_RESET, LCD_CS
8
  PORTC.DIR = (1<<PIN5) | (1<<PIN6) | (1<<PIN7);
9
  PORTC.OUTCLR = (1<<PIN5) | (1<<PIN6) | (1<<PIN7);
10
  PORTCFG.VPCTRLA = 0x02;
11
        
12
        // Initialize USARTD0 for OLED in SPI mode
13
        USARTD0.BAUDCTRLA = 0x01;   // (SPI clock = 8MHz)
14
        USARTD0.CTRLC     = USART_CMODE_MSPI_gc;   // Master SPI mode,
15
        USARTD0.CTRLB     = USART_TXEN_bm;   // Enable TX
16
17
18
}

und meine GLCD Init so
1
void GLCD_LcdInit(void)  {
2
    // Recommended power up sequence
3
    clrbit(LCD_CTRL, LCD_RESET);         // Reset Low for 3 uS
4
  _delay_us(3);
5
    setbit(LCD_CTRL, LCD_RESET);         // Reset Low for 3 uS
6
    _delay_us(3);
7
    // Recommended initialization sequence
8
    LcdInstructionWrite(LCD_DISP_OFF);
9
    LcdInstructionWrite(LCD_SET_RATIO_OSC);
10
    LcdInstructionWrite(0x80);
11
    LcdInstructionWrite(LCD_MULTIPLEX);
12
    LcdInstructionWrite(0x3F);
13
    LcdInstructionWrite(LCD_SET_OFFSET);
14
    LcdInstructionWrite(0x00);
15
    LcdInstructionWrite(LCD_SET_LINE);
16
    LcdInstructionWrite(LCD_CHARGE_PUMP);
17
    LcdInstructionWrite(LCD_PUMP_ON);
18
19
    LcdInstructionWrite(LCD_SET_PADS);
20
    LcdInstructionWrite(0x12);
21
    LcdInstructionWrite(LCD_SET_CONTRAST);
22
    LcdInstructionWrite(0x8F);
23
    LcdInstructionWrite(LCD_SET_CHARGE);
24
    LcdInstructionWrite(0xF1);
25
    LcdInstructionWrite(LCD_SET_VCOM);
26
    LcdInstructionWrite(0x40);
27
    LcdInstructionWrite(LCD_EON_OFF);
28
    LcdInstructionWrite(LCD_DISP_NOR);
29
    LcdInstructionWrite(LCD_MEM_ADDRESSING);
30
    LcdInstructionWrite(0x00);          // Horizontal Addressing mode
31
    LcdInstructionWrite(LCD_DISP_ON);
32
  
33
}

von Christian G. (Firma: privat) (dotti)


Lesenswert?

@Timmo:

Danke für den konstruktiven Beitrag.
Leider habe ich den erst eben gesehen, sonst hätte ich es natürlich 
gestern schon ausprobiert.
So habe ich gestern noch weiter mit der DMA Variante probiert und es 
auch irgendwann hinbekommen, allerdings könnte ich jetzt nicht mal sagen 
woran es gelegen hat. Habe mir dazu eine Kopie meines Codes zum 
Zerspielen gemacht und überall die vorhandenen Delays etwas erhöht, ein 
zusätzliches clr_display() am Anfang eingebaut (und noch paar Sachen 
mehr) und dann lief es plötzlich. Allerdings nicht in jedem Fall stabil 
(Text erscheint schonmal etwas zu hoch, zu tief oder auch gespiegelt 
nach wiederholten Powerups).

Deine Variante habe ich gerade aber auch noch probiert, lief perfekt 
gleich nach erstem Kompilieren (ist ja recht übersichtlich). Also soweit 
erstmal vielen Dank!

Da ich die nicht funktionierende Variante aber auch noch habe, schaue 
ich mir spätestens bei weiteren Unstimmigkeiten das Delta im Coding noch 
genauer an - bin da schon neugierig. Nach zweieinhalb Tagen Hirn 
Zermartern (und in der Hitze zurzeit in D) mag ich jetzt erstmal ein 
wenig weiterkommen mit dem eigentlichen Vorhaben. Es wird aber zu seiner 
Zeit noch einmal ein Update hier geben.


Was die Pixelpampe angeht: Ich hatte auch direkt die Idee, dass ich an 
die falsche Stelle schreibe (oder an falscher Stelle im Speicher lese) - 
aber das kann es ja irgendwie nicht sein. Auf der anderen Seite: Wenn 
der Text (in der o.g. besseren aber leicht instabilen Version) an 
falscher Stelle bzw. gespiegelt erscheint, dann spricht das ja doch für 
diese Theorie (Spiegeln entsteht dann vielleicht durch "Glitches" 
innerhalb der 3 Command Bytes). Wird also noch erforscht, wenn die Zeit 
reif ist.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.