Hallo,
ich bin am testen meines LPC Boards. Da ich am Display Port einen LVDS
Transmitter habe, habe ich ein 800x600 Display von Unipac angeschlossen.
Prinzipiell habe ich ein Bild, allerdings sieht es aus als würden die
Back und Front porch Angaben nicht stimmen. (Zittern und zu lange
Zeilen)
Es handelt sich um ein UB084S01:
http://www.gblcd.com/datacenter/unipac/UB084S01.pdf
Meine Initialisierung sieht so aus: 1 | PCONP |= (1<<20); //Enable LCD controller
| 2 | LCD_CTRL = 0x2A; //TFT 24 Bit
| 3 | LCD_CFG = 1; //Set divider CCLK/2 = 40 MHz
| 4 | LCD_POL = (0x01 << 26)|(799 << 16)|(0x00 << 14)|(0x01 << 13)|(0x01 << 12)|(0x01 << 11);
| 5 | //Bypass pixel clock divider | 800 clocks per line | invert: panel clock, h clock, v clock
| 6 | LCD_TIMH = (87 << 24)|(39 << 16)|(127 << 8 )|(49 << 2);
| 7 | //Back porch = 88 clk | Front porch = 40 clk | Pulse with = 128 clk | Size = (49 + 1)*16 = 800 px
| 8 | LCD_TIMV = (22 << 24)|(0 << 16)|(3 << 10)|(599);
| 9 | //Back porch = 22 px | Front porch = 1 px | Pulse with = 3 px | Size = 600 px
| 10 | LCD_UPBASE = LCD_VRAM_BASE_ADDR & 0xfffffff8;
| 11 | LCD_LPBASE = LCD_VRAM_BASE_ADDR & 0xfffffff8;
| 12 | LCD_CTRL |= 1|(1<<11); //Enable LCD
|
Habe ich die porch clocks etc. korrekt umgerechnet und eingesetzt? Oder
muss ich meinen Fehler wo anders suchen?
Der LPC2478 läuft auf 80 MHz, das macht keine Probleme, auch mit dem
SDRAM.
Stimmt der Zeilenanfang, d.h. ist der Pixel ganz links in einer Zeile da
wo er sein soll, oder ist er auch schon nach rechts verschoben?
Da das Display das DataEnable Signal auswertet, sind die Front und
Backporchzeiten nicht allzu kritisch.
Wenn ich das Datenblatt des LPC2478 richtig verstehe, ist der CPL Wert
im LCD_POL Register die gesamten Takte pro Zeile, also 1056 und nicht
800.
Der Zeilenanfang stimmt. Das Ändern des CPL Wertes auf 1056 führt dazu,
dass gar nichts mehr angezeigt wird. Wäre 1056 nicht eh zu groß für
dieses Register?! (11 Bit in 9 Bit Register?)
Omega G. schrieb:
> Wäre 1056 nicht eh zu groß für
> dieses Register?! (11 Bit in 9 Bit Register?)
Das Register hat 10 bit, aber das ist immer noch zu klein. Der LPC ist
also etwas beschränkt was die Displaygröße angeht. Stell mal 1023 ein.
Die Summe aus Frontporch + Backporch + Syncimpuls + Pixel auf dem
Display muss gleich dem CPL Wert sein. Vermutlich musst du daher die
Front + Backporch Werte reduzieren, damit die Zeile eben maximal 1024
Pixel lang wird.
Dieses Zittern und die zu langen Zeilen deuten darauf hin, dass du ein
übersprechen von Datenleitungen auf die Clock-Leitung hast. Deine
Aussage, dass der Zeilenanfang stimmt, bestätigt diese Theorie.
Du hast in dener Clock-Leitung einen Widerstand (R20). Löte einmal einen
18pF oder 22pF Kondensator hinter dem Widerstand gegen Masse, sozusagen
ein Tiefpass.
Stimmt 10 Bit!
1023 verändert an der anzeige was, aber natürlich bringt es nicht den
gewünschten Effekt.
Im Anhang das Bild. Es sollen alle 10 Zeilen eine Zeile weiß sein.
Sieht aus, also ob das nichts wird mit diesem LCD, obwohl der Controller
theoretisch bis 1024x768 unterstützen sollte.
Omega G. schrieb:
> 1023 verändert an der anzeige was, aber natürlich bringt es nicht den
> gewünschten Effekt.
Reduzier mal den Frontporch auf 32, den Backporch auf 80 und die
Syncbreite auf 112. Das sollte exakt 1024 Pixel pro Zeile ergeben.
Die Angabe von 799 für CPL stimmt aber!
Erste Zeile stimmt so, die zweite hat etwas rechts von der Mitte aus
eine Unterbrechung. Wenn ich das richtig sehe, stimmt jede zweite Linie.
Kai F. schrieb:
> Die Angabe von 799 für CPL stimmt aber!
Sicher? Dann ist das Datenblatt aber falsch:
CPL Clocks per line.
This field specifies the number of actual LCDDCLK clocks to the
LCD panel on each line.
-> gesamte Anzahl an Takten pro Zeile
PPL Pixels-per-line.
The PPL bit field specifies the number of pixels in each line or
row of the screen.
-> aktiver Bildbereich
Betreibe hier ein 480x272-Display mit dem LPC2478 und da ist 479
richtig.
Mit CPL = 799 zeigt das Display gar nichts an, mit den Angaben von
Benedikt. Wenn ich CPL auf 1023 setze schon.
Mach mal bitte ein Bild vom Display mit den Settings, die du in deinem
ersten Post hattest.
Falls du ein Oszilloskop oder einen Frequenzzähler hast, schau dir mal
das Signal an DataEnable an oder messe zumindest die Frequenz von HSync.
Daran kann man leicht erkennen, ob der CPL Wert korrekt ist.
Bei 1056 Takten pro Zeile sollte die Frequenz 40MHz/1056=37,88Hz
betragen, bei 800 dagegen 50kHz.
Ebenso sollte bei den richtigen Einstellungen DataEnable ein
Tastverhältnis von 800/1056=75,75% haben.
Ich kann gleich mal mit einem Oszilloskop ran. Mir kam noch eine andere
Idee. Wenn ich die CLK Frequenz reduziere, kann ich auch den CPL Wert
reduzieren?!
Ich bin etwas runter mit dem Takt auf 38 MHz, und habe den CPL Wert auf
952. Das führt dazu, dass die Unterbrechungen am weiter am Anfang der
Zeile sind.
Das Bild steht so aber still?
Was hast du in den Framebuffer geschrieben?
DataEnable:
Frequenz: 37,8 kHz
Duty Cyle: 75,7%
HSync:
Frequenz: 37,8 Khz
Duty Cycle: 87,8%
Omega G. schrieb:
> Wenn ich die CLK Frequenz reduziere, kann ich auch den CPL Wert
> reduzieren?!
Nein. Der Pixeltakt hat nichts mit den Pixeln pro Zeile zu tun. Die
Eingangsstufe des Displays ist erstmal rein digital und leitet über
durch DataEnable und HSync synchronisierte Zähler aus dem Pixeltakt alle
weiteren internen Signale an. Das Display möchte im Idealfall 1056 Takte
pro Zeile. Dies kann man durch Verlängern oder Verkürzen der Porch Werte
etwas verändern. Um wie viel, dazu schweigt das Datenblatt leider und
aus Erfahrung kann ich sagen, dass sich hier jedes Display anders
verhält.
> DataEnable:
> Frequenz: 37,8 kHz
> Duty Cyle: 75,7%
Bei welchem CPL Wert?
Der scheint also zu passen.
Mit den Werten im ersten Post. Also CPL = 799.
Bei keinem der Werte Stand das Bild still. Es zittern die stellen wo die
Stellen wo Linien doppelt sind bzw. unterbrochen sind.
Ok, dann hab ich dich in die falsche Richtung gelotst, da die
Beschreibung im Datenblatt falsch ist...
Liegt das HSync Signal in dem Bereich von DataEnable der Low ist?
Wenn DE low ist, geht auch HSync auf low.
Gelb = Data Enabled
Grün = Hsync
Das grundsätzliche Timing sieht gut aus. Daran dürfte es also nicht
liegen, das kann man jetzt ausschließen.
Du setzt das IPC Bit, um den Pixeltakt zu invertieren. Passt das?
Das Display übernimmt die Daten mit der fallenden Flanke, du gibst die
Daten ebenfalls mit der fallenden Flanke aus.
Üblicherweise wählt man die steigende Flanke um den damit verbundenen
Timingproblemen aus dem Weg zu gehen.
Allerdings hängt das auch davon ab, welchen LVDS Encoder du verwendet
hast. Wenn der auch die Daten an der fallenden Flanke einliest, ist das
Bit falsch gesetzt.
Es macht keinen Unterschied ob ich das IPC Bit setze oder nicht.
Den SDRAM habe ich geprüft, funktioniert.
SN75LVDS84A habe ich als LVDS Transmitter.
Der SN75LVDS84A übernimmt die Daten auch mit der fallenden Flanke, das
IPC Bit sollte also nicht gesetzt sein. Es führt aber "nur" dazu, dass
das Bild oder einzelne Pixel ab und zu um einen Pixel hin und her
tanzen.
Lass mal ein Bild ausgeben, dessen linkes viertel rot, dann 1/4 blau,
dann grün und dann weiß ist und mach davon mal ein Foto.
Was hast du denn bisher in den Framebuffer geschrieben, d.h. wie sollte
das Bild eigentlich aussehen? Jede zehnte Zeile weiß, der Rest schwarz?
Mach mal ein Muster mit vertikalen Linien.
Da war wohl einer schneller ... ;-)
Testweise schreibe ich so in den Framebuffer: 1 | for (x = 0; x < 800; x++){
| 2 | for(y = 0; y < 600; y++){
| 3 | for (c = 0; c < 3; c++){
| 4 | if((x%10==0))
| 5 | *(unsigned char*)((unsigned int)&SDRAM_BASE_ADDR+z) = 0xff;
| 6 | z+=1;
| 7 | }
| 8 | }
| 9 | }
|
Das müsste doch alle 10 Zeile eine Linie ergeben, oder denke ich falsch?
1 | for (x = 0; x < 800; x++){
| 2 | for(y = 0; y < 600; y++){
| 3 | for (c = 0; c < 3; c++){
| 4 | if((x%10==0)|(y%10==0))
| 5 | *(unsigned char*)((unsigned int)&SDRAM_BASE_ADDR+z) = 0xff;
| 6 | z+=1;
| 7 | }
| 8 | }
| 9 | }
|
Müsste dann eine Karomuster geben?
Stimmt das so, bevor ich mit falschem Ansatz weitermache?
Omega G. schrieb:
> Testweise schreibe ich so in den Framebuffer:
>
> Das müsste doch alle 10 Zeile eine Linie ergeben, oder denke ich falsch?
Nein, jede 10. Spalte wen du x abfragst. Aber kann es sein, dass du die
x und y Schleifen vertauscht hast? Im Speicher folgt eine Zeile nach der
anderen und nicht eine Spalte nach der anderen.
> 1 | > if((x%10==0)|(y%10==0))
| 2 | >
|
> Müsste dann eine Karomuster geben?
Du wolltest vermutlich anstelle von dem | ein || schreiben (auch wenn es
hier auf das gleich hinausläuft)?
Wieso schreibst du nicht gleich 32 Bit?
1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<800;x++)
| 4 | {
| 5 | if (((x%10)==0) || ((y%10)==0))
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 7 | z++;
| 8 | }
| 9 | }
|
1 | for (x = 0; x < 800; x++){
| 2 | for(y = 0; y < 600; y++){
| 3 | for (c = 0; c < 3; c++){
| 4 | if((x%10==0)||(y%10==0))
| 5 | *(unsigned char*)((unsigned int)&SDRAM_BASE_ADDR+z) = 0xff;
| 6 | z+=1;
| 7 | }
| 8 | }
| 9 | }
|
Ergibt das Bild im Anhang.
1 | for (x = 0; x < 800; x++){
| 2 | for(y = 0; y < 600; y++){
| 3 | //for (c = 0; c < 3; c++){
| 4 | if((x%10==0)||(y%10==0))
| 5 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 6 | z+=1;
| 7 | //}
|
Ergibt nur ein Bild in der oberen Hälfte?!
Du mußt in der äußeren Schleife y inkrementieren und in der inneren x!
Sieht so aus.
Genau wie wenn ich x und y vertausche.
Poste mal den Code der dieses Bild erzeugt hat.
1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<800;x++)
| 4 | {
| 5 | if (((x%10)==0) || ((y%10)==0))
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 7 | z+=4;
| 8 | }
| 9 | }
|
erzeugt dieses Bild. Das Muster im Hintergrund sollten auch Linien
sein...
Also wie sieht der Code jetzt komplett aus?
Probier mal das aus:
1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<800;x++)
| 4 | {
| 5 | if (x<200)
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ff0000;
| 7 | else if (x<400)
| 8 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x0000ff00;
| 9 | else if (x<600)
| 10 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x000000ff;
| 11 | else
| 12 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 13 | z+=4;
| 14 | }
| 15 | }
|
Der Code von Benedikt erzeugt dieses Bild.
Sehr seltsam.
Ich sehe weder rot noch grün, nur blau.
Ersetze mal die letze Zeile in der weiß ausgegeben wird durch rot. Wenn
dann die Streifen rot/blau sind, dann stammen immerhin alle Daten aus
dem Speicher und nicht sonst wo her.
1 | unsigned int TestFunc(void)
| 2 | {
| 3 | unsigned int x;
| 4 | unsigned int y;
| 5 | unsigned int z;
| 6 | z=0;
| 7 | for (y=0;y<600;y++)
| 8 | {
| 9 | for (x=0;x<800;x++)
| 10 | {
| 11 | if (x<200)
| 12 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x00ff0000)
| 13 | {
| 14 | return z;
| 15 | }
| 16 | else if (x<400)
| 17 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x0000ff00)
| 18 | {
| 19 | return z;
| 20 | }
| 21 | else if (x<600)
| 22 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x000000ff)
| 23 | {
| 24 | return z;
| 25 | }
| 26 | else
| 27 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x00ffffff)
| 28 | {
| 29 | return z;
| 30 | }
| 31 | z+=4;
| 32 | }
| 33 | }
| 34 | return 0xffffffff;
| 35 | }
|
Ruf mal diese Testfunktion nach dem beschreiben des Framebuffers auf und
werte den Rückgabewert aus.
Da bekomme ich 0 zurück. Sieht aus als ob doch was mit dem RAM nicht
stimmt. Zumindest im 32 Bit Betrieb.
Meine Testroutine: 1 | for (i = 0; i < 0x100000; i+=sizeof(unsigned int))
| 2 | {
| 3 | *(unsigned int*)((unsigned int)&SDRAM_BASE_ADDR+i) = i;
| 4 | }
| 5 |
| 6 | for (i = 0; i < 0x100000; i+=sizeof(unsigned int))
| 7 | {
| 8 | temp = *(unsigned int*)((unsigned int)&SDRAM_BASE_ADDR+i);
| 9 | if (temp != i)
| 10 | {
| 11 | sendint8_uart0(i);
| 12 | send_uart0(' ');
| 13 | sendint8_uart0(temp);
| 14 | send_uart0(10);
| 15 | send_uart0(13);
| 16 | }
| 17 | }
| 18 | return(1);
|
gibt 1 zurück und sendet nichts über UART.
Ich bin momentan ratlos.
Zeig mal bitte deine SDRAM Initialisierung.
1 | PCONP|=(1<<11);
| 2 | // EnableRAMPower();
| 3 | PINSEL5 = 0x55010115;
| 4 | PINMODE5 = 0xAAaaaaaA;
| 5 | PINSEL6 = 0x55555555;
| 6 | PINMODE6 = 0xAAAAAAAA;
| 7 | PINSEL7 = 0x55555555;
| 8 | PINMODE7 = 0xAAAAAAAA;
| 9 | PINSEL8 = 0x05555555;
| 10 | PINMODE8 = 0x0AAAAAAA;
| 11 | PINSEL9 = (1 << 18);
| 12 | PINMODE9 = 0x80000;
| 13 |
| 14 |
| 15 |
| 16 | EMC_CTRL=1; // enable EMC
| 17 | EMC_DYN_RD_CFG=1;//Configures the dynamic memory read strategy(Command delayed strategy)
| 18 | EMC_DYN_RASCAS0|=0x303;
| 19 | EMC_DYN_RP= 4;//RP
| 20 | EMC_DYN_RAS = 8;//RAS
| 21 | EMC_DYN_SREX = 5;// SREX, XSR not found
| 22 | EMC_DYN_APR = 2; //APR not found
| 23 | EMC_DYN_DAL = 5; //DAL, APW not found
| 24 | EMC_DYN_WR = 3; //WR, DPL, RWL, RDL
| 25 | EMC_DYN_RC = 11;//
| 26 | EMC_DYN_RFC = 11; //rfc rc
| 27 | EMC_DYN_XSR = 5;//txsr not found
| 28 | EMC_DYN_RRD = 3;//RRD
| 29 | EMC_DYN_MRD = 8;//RAS
| 30 | EMC_DYN_CFG0 = (1<<14)|(3<<9)|(1<<8)|(1<<12);//8,9,14
| 31 |
| 32 | for(i = 0; i < 100; i++);
| 33 | EMC_DYN_CTRL = 0x0183; // NOP
| 34 | for(i = 0; i < 100; i++);
| 35 | EMC_DYN_CTRL=0x101;
| 36 | EMC_DYN_RFSH = 1;
| 37 | for(i = 0; i < 100; i++);
| 38 | EMC_DYN_RFSH = 19;
| 39 |
| 40 | EMC_DYN_CTRL=0x81;
| 41 | i = (*(volatile int*)(0xA0000000 | (0x32<<12)));
| 42 | EMC_DYN_CTRL = 0;
| 43 |
| 44 | EMC_DYN_CFG0|=0x80000;
|
Ich habe einen K4S643232E-TC70 drinnen. Ich hoffe nächste Woche bekomme
ich 128 Mbit RAMs.
Ich bin noch nicht durch, aber das ist mir schon mal aufgefallen:
Du schreibst: 1 | EMC_DYN_CFG0 = (1<<14)|(3<<9)|(1<<8)|(1<<12);//8,9,14
|
Wenn man diesen Wert im User Manual anschaut, dann müsstest du einen
"256 MB (8Mx32), 4 banks, row length = 13, column length = 8"
angeschlossen haben.
Du hast aber lt. deiner Bauteilbezeichnung einen "64 MB (2Mx32), 4
banks, row length = 11, column length = 8" -> 0x5300
Also müsste deine Zeile lauten: 1 | EMC_DYN_CFG0 = (1<<14)|(1<<12)|(1<<9)|(2<<7);
|
Du kannst die (1<<12) auch weglassen, dann wird das Ding als High
Performance angesprochen.
Das funktioniert nur, wenn ich Bit 12 setze. Genau wie meine Funktion.
Noch Ideen, ansonsten warte ich auf das vorgesehene RAM.
Wenn du Bit 12 nicht setzt, dann kommt garnichts?
Hast du mal die Zeile so, wie ich sie oben geschrieben habe ausprobiert?
Verändert sich das Bild, oder das Ergebnis des Speichertest dadurch?
Wenn ich Bit 12 nicht setze, dann habe ich nur Speicherfehler mit deiner
Zeile.
Dann habe ich laut meiner Testroutine keine Speicherfehler. Aber die
Funktion zum Balken aufs Display schreiben und lesen, bringt immer noch
0 zurück, also Fehler an Adresse 0.
Noch ein Versuch:
Laß die Zeile 1 | EMC_DYN_CFG0 = (1<<14)|(1<<12)|(1<<9)|(2<<7);
|
und ändere weiter unten die Zeile so ab: 1 | i = (*(volatile int*)(0xA0000000 | (0x32<<10)));
|
(aus 12 mach 10)
Hi,
wenn ich mich auch mal einmischen darf:
Ich glaube, deine SDRAM-Initialisierung ist nicht ganz korrekt.
Ich muss nachher schnell meinen eigenen Code raussuchen, dann kann ich
dir den mal hier rein stellen.
Sicher - die Timings wirst du anpassen müssen (stimmen wirklich alle?),
aber ich glaube meine Initialisierung sieht auch sonst noch ein bisschen
anders aus....
Andere Frage:
hast du zum Vereinfachen des Layouts Adressleitungen oder Datenleitungen
getauscht, verdraht, oder sonst wie anders angeschlossen?
DQMx-Signale richtig herum? DQM0 => D0..7, DQM1 => D8..15 usw.
Wie sieht der Clock aus - am Pin vom LPC sowie am Pins vom SDRAM? ->
mach doch mal schnell ein Bild.
Wenn es dir was bringt, mache ich nachher auch schnell ein paar Bilder
von meinen Signalen, dann kannst du vergleichen, ob bei dir irgendwas
anders aussieht.
Ach ja: sind CLK und CKE mindestens gleich lang oder länger als die
Adressleitungen? Ich meine mal gelesen zu haben, dass das evtl. noch
eine Rolle spielen /könnte.
Und, noch etwas OT:
Hast du das MCI oder Ethernet schon ausprobieren können? ;-)
Unverändert:
Mit Bit 12: RAM test ok, Bild fehlerhaft
Ohne Bit 12: RAM test fehlerhaft, Bild fehlerhaft
Ach ja, was mir auffält:
weiter oben hast du gesagt, du hättest keine Speicherfehler mehr, wenn
du Bit 12 setzt.
Das würde dann ja bedeuten, dass dein SDRAM korrekt funktioniert.
Dummerwise macht dann aber deine "Balken-aufs-Display-zeichen-Funktion"
einen Fehler - welche Funktion ist das nochmal?
Stack richtig initialisiert? (gross genug, an einer gültigen
Speicheradresse).
Ich finde, wenn dein Ram-Test fehlerfrei durch läuft, dann kann der
Fehler auch nicht beim SDRAM liegen - das lässt sich ja dann
offensichtlich beschreiben und lesen....
CLK und CKE sind länger als die Adressleitungen. DQM0 vom LPC geht an
DQMOUT0 vom Controller etc.
Kann es sein, dass es daran liegt, das mein Layout für 128 Mbit augelegt
ist, aber 64 Mbit drinnen sind? (BA0 und BA1 vom RAM an A12 und A13
geplant, allerdings ist A11 bei mir ja unbelegt...)
Würde es vielleicht was bringen die Serienwiderstände von 22 auf 33 Ohm
zu vergrößern? (Mein Prototyp hat scheinbar 33 Ohm gehabt, hier sind 22
Ohm drinnen)
Ich mache gleich Aufnahmen von den CKE und CLK Signalen.
Ethernet und MCI habe ich noch nicht getestet.
1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<800;x++)
| 4 | {
| 5 | if (x<200)
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ff0000;
| 7 | else if (x<400)
| 8 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x0000ff00;
| 9 | else if (x<600)
| 10 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x000000ff;
| 11 | else
| 12 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 13 | z+=4;
| 14 | }
| 15 | }
|
erzeugt Balken
1 | unsigned int TestFunc(void)
| 2 | {
| 3 | unsigned int x;
| 4 | unsigned int y;
| 5 | unsigned int z;
| 6 | z=0;
| 7 | for (y=0;y<600;y++)
| 8 | {
| 9 | for (x=0;x<800;x++)
| 10 | {
| 11 | if (x<200)
| 12 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x00ff0000)
| 13 | {
| 14 | return z;
| 15 | }
| 16 | else if (x<400)
| 17 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x0000ff00)
| 18 | {
| 19 | return z;
| 20 | }
| 21 | else if (x<600)
| 22 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x000000ff)
| 23 | {
| 24 | return z;
| 25 | }
| 26 | else
| 27 | if (*(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) != 0x00ffffff)
| 28 | {
| 29 | return z;
| 30 | }
| 31 | z+=4;
| 32 | }
| 33 | }
| 34 | return 0xffffffff;
| 35 | }
|
Prüft diese Balken
> Kann es sein, dass es daran liegt, das mein Layout für 128 Mbit augelegt
> ist, aber 64 Mbit drinnen sind? (BA0 und BA1 vom RAM an A12 und A13
> geplant, allerdings ist A11 bei mir ja unbelegt...)
Uuuh, das könnte der Grund sein - BAx sollten an A13 und A14 liegen, so
viel ich weiss....
Schau dir vielleich mal angehängtes PDF an; ab Seite 32 wirds für dich
interessant ;-)
Ich meine auf BA0 muß A13 und auf BA1 muß A14, auch für den 128Mbit
SDRAM.
> Ich meine auf BA0 muß A13 und auf BA1 muß A14, auch für den 128Mbit
> SDRAM.
Sach ich doch ;-)
Ja klar, ich war aber (wieder einmal) zu langsam!
:-)
Ich glaube, es geht aus dem Datenblatt des 2478 auch nicht wirklich klar
hervor, dass A13 und A14 mit BA0 und BA1 verbunden werden müssen. Dass
ich das bei meinem ersten Testboard so gemacht hatte, hat sich auch eher
durch Zufall ergeben, weil mein RAM genau 13 Adressleitungen (A0 bis
A12) braucht - dann kommen A13 und 14 automatisch auf BAx.
Aber wie gesagt - auch wenn das RAM nur 10 Adressen oder so braucht -
trotzdem muss man A13 und 14 mit BA verbinden (oder Kai? Das steht
jedenfalls irgendwo in oben angehängtem Dokument).
Okay, das ist schlecht. Schade, das es keiner gesehen hat im Schaltplan
von mir.
Gibt es dafür irgendeine Möglichkeit das Problem softwaremäßig zu lösen?
Oder muss ich Drähte ziehen?
Hi Omega,
tja ich habe mir deinen Schaltplan leider gar nicht angeschaut, sonst
hätte ich es gesehen :-( Ich war mehr am Layout interessiert....
Aber gut, deine Leiterplatte lässt sich trotzdem noch retten - mittels
dünnen Wrapdraht und einem Skalpell.
Softwaremässig wirst du die Bankselects nicht verbieben können, die sind
m.W. hart verdrahtet (im LPC2478 innen drin).
Aber die fehlerhaften beiden Leiterbahnen sind ja schnell abgetrennt und
mittels einer kleinen Drahtbrücke an den richtigen Pins angeschlossen...
Mir ist bei meinem ersten Testboard was ähnliches passiert - Ich hatte
an den DQMx-Pins einen Layoutfehler; DQM0 und 1 waren vertauscht.
Wrapdraht hilft da wunder; jetzt läuft das Board (auch mit 72 MHz).
Darf BA0 und 1 vertauscht werden?
Dann müsste ich nur eine Leitung legen^^
Genau. Die meisten Beispielschaltungen verwenden SDRAMs, die A0-A12, BA0
und BA1 verwenden.
Das User Manual zum LPC2478 schweigt sich zur Belegung leider aus.
Richtig ist aber mit ziemlicher Sicherheit, dass BA0 immer an A13 und
BA1 an A14 kommt. Die oberen Adressleitungen (A12, A11) werden bei
fehlenden Bedarf freigelassen.
Tobias hat also vollkommen recht.
Also ich bin nicht DER SDRAM-Experte, aber wenn man sich diese
Datenblätter so anguckt: das sieht so aus, als wären in einem Chip
eigentlich 4 identische SDRAMs drinnen (eben die 4 Banks), die von BA0
und BA1 ausgewählt werden. Von daher würde ich sagen: könnte gehen!
Allerdings bin ich mir nicht 100%ig sicher; evtl. könnte es ja Ärger mit
dem Refresh geben? Da weiss Kai sicher noch besser Bescheid.
Übrigens, @Kai:
Was ist los mit unserem Projekt? ;-)
Ich wüsste jetzt nicht, was dagegen sprechen würde. -> Ausprobieren!
@Tobias: Das alte Problem: Zu viel zu tun und zu wenig Zeit, aber ich
melde mich in den nächsten Tagen wieder!
So. A14 geht auf BA0 und A13 auf BA1.
Unverändert?! Aber ich habe noch eine Idee, Stichwort PINSEL.
Okay, wart mal. Ich suche jetzt meinen Notebook hervor; dort drauf habe
ich einen funktionierenden Code, den ich nachher hier hochladen werde.
Moment...
So, sieht schon besser aus. Bit 12 ist nun Wurscht.
Wie gesagt, die relevanten Stellen in der Initialisierung des SDRAM sind
diese beiden Zeilen: 1 | EMC_DYN_CFG0 = (1<<14)|(1<<12)|(1<<9)|(2<<7);
| 2 | ...
| 3 | i = (*(volatile int*)(0xA0000000 | (0x32<<10)));
|
Die Initialisierung des Displays haben wir ja schon durch. ;-)
Also, mein SDRAM-Code sieht wie folgt aus:
1 | /*===========================================================================*/
| 2 | void fLowLevelInit(void)
| 3 | /*-----------------------------------------------------------------------------
| 4 | Function:
| 5 | performs a low-level hardware initialization function; called directly
| 6 | after reset.
| 7 | in: none
| 8 | out: none
| 9 | =============================================================================*/
| 10 | {
| 11 | #ifdef DEBUG
| 12 | MEMMAP = 2; /* map interrupt vectors to internal RAM (debug in RAM) */
| 13 | #else
| 14 | MEMMAP = 1; /* no remapping of interrupt vectors */
| 15 | #endif
| 16 |
| 17 | /* initialize the pll */
| 18 | #if CLOCKSOURCE == CLK_MAIN /* check if the main osc is the clk source */
| 19 | SCS |= OSCRANGE; /* first select an oscillator range */
| 20 | SCS |= OSCEN; /* then enable the main oscillator */
| 21 | while(!(SCS & OSCSTAT)); /* wait until the main osc is ready */
| 22 | #endif
| 23 | PLLCON &= ~PLLC; /* disconnect the pll */
| 24 | PLLFEED = 0xAA;
| 25 | PLLFEED = 0x55;
| 26 | PLLCON &= ~PLLE; /* disable the pll */
| 27 | PLLFEED = 0xAA;
| 28 | PLLFEED = 0x55;
| 29 | CCLKCFG = 0; /* disable cpu clock divider to speed up pll configuration */
| 30 | CLKSRCSEL = CLOCKSOURCE; /* select the desired clock source */
| 31 |
| 32 | /* set the N and M values */
| 33 | PLLCFG = (((NSEL - 1) & 0xFF) << 16) | ((MSEL - 1) & 0xFFFF);
| 34 | PLLFEED = 0xAA;
| 35 | PLLFEED = 0x55;
| 36 | PLLCON |= PLLE; /* enable the pll */
| 37 | PLLFEED = 0xAA;
| 38 | PLLFEED = 0x55;
| 39 | CCLKCFG = CCLK_DIV; /* select cpu clock divider */
| 40 | while(!(PLLSTAT & PLOCK)); /* wait for the pll to lock */
| 41 | PLLCON |= PLLC; /* connect the pll */
| 42 | PLLFEED = 0xAA;
| 43 | PLLFEED = 0x55;
| 44 |
| 45 | VICIntEnClr = 0xFFFFFFFF; /* initialize the vic by clearing any interrupts */
| 46 | VICAddress = 0; /* reset vic address register */
| 47 | VICIntSelect = 0; /* switch all interrupts to irq mode */
| 48 |
| 49 | MAMTIM = 3; /* initialize the memory accelerator module */
| 50 | MAMCR = 2;
| 51 |
| 52 | #ifdef HI_SPEED_GPIO
| 53 | SCS |= BIT_00; /* select all gpios as fio instead of legacy */
| 54 | #else
| 55 | SCS &= ~BIT_00; /* select legacy io ports */
| 56 | #endif
| 57 |
| 58 | tDWord dwDelay;
| 59 | PCONP |= BIT_11; /* enable power for the emc */
| 60 | PINSEL5 = 0x55010115; /* enable emc pins */
| 61 | PINMODE5 = 0xAA02022A;
| 62 | PINSEL6 = 0x55555555;
| 63 | PINMODE6 = 0xAAAAAAAA;
| 64 | PINSEL7 = 0x55555555;
| 65 | PINMODE7 = 0xAAAAAAAA;
| 66 | PINSEL8 = 0x15555555;
| 67 | PINMODE8 = 0x2AAAAAAA;
| 68 | PINSEL9 = (1 << 18);
| 69 | PINMODE9 = 0x80000;
| 70 | EMCControl = 1; /* setup the emc and the timings for sdram */
| 71 | EMCDynamicReadConfig = 1; /* select command delayed read strategy */
| 72 | EMCDynamicRasCas0 = 0x203; /* cas latency 2, ras latency 3 */
| 73 | EMCDynamictRP = 3; /* setup all the other timings for the sdram */
| 74 | EMCDynamictRAS = 5; /* see the sdram calculation sheet */
| 75 | EMCDynamictSREX = 2;
| 76 | EMCDynamictAPR = 2;
| 77 | EMCDynamictDAL = 5;
| 78 | EMCDynamictWR = 3;
| 79 | EMCDynamictRC = 6;
| 80 | EMCDynamictRFC = 6;
| 81 | EMCDynamictXSR = 2;
| 82 | EMCDynamictRRD = 3;
| 83 | EMCDynamictMRD = 3;
| 84 | EMCDynamicConfig0 = (BIT_14 | BIT_10 | BIT_09 | BIT_07);
| 85 | for(dwDelay = 0; dwDelay < 100; dwDelay++);
| 86 | EMCDynamicControl = 0x181; /* issue nop command */
| 87 | for(dwDelay = 0; dwDelay < 100; dwDelay++);
| 88 | EMCDynamicControl = 0x101; /* issue precharge all command */
| 89 | EMCDynamicRefresh = 1; /* start the refresh timer */
| 90 | for(dwDelay = 0; dwDelay < 100; dwDelay++);
| 91 | EMCDynamicRefresh = 19; /* set up the refresh timer */
| 92 | EMCDynamicControl = 0x081; /* issue sdram mode command */
| 93 | dwDelay = (*(tpDWord)(0xA0000000 | (0x22 << 13))); /* burst length 4, 2 cas */
| 94 | EMCDynamicControl = BIT_01; /* clkout runs continuously */
| 95 | EMCDynamicConfig0 |= 0x80000; /* enable the buffer */
| 96 | } /* end fLowLevelInit */
|
Die Timings musst du halt noch anpassen, aber wenigstens kannst du
vielleicht mal schauen, in welcher Reihenfolge ich was wo initialisiert
habe, und was nicht.
die Pinsel und Pinmode sind jedenfalls für 32 Bit externes SDRAM
konfiguriert, passen so also zu deinem Design.
Vorsicht! Tobias verwendet einen anderen SDRAM!
Die Zeilen 1 | EMCDynamicConfig0 = (BIT_14 | BIT_10 | BIT_09 | BIT_07);
| 2 | ...
| 3 | dwDelay = (*(tpDWord)(0xA0000000 | (0x22 << 13))); /* burst length 4, 2 cas */
|
treffen für dich nicht zu!
Klar, sorry das hatte ich natürlich vergessen zu erwähnen!
Aber vielleicht bringt der Code ja trotzdem was, ich selber bin
jedenfalls immer froh wenn ich eine Vorlage habe...
1 | void init_ram_high(void){
| 2 | int i;
| 3 |
| 4 | PCONP|=(1<<11);
| 5 | // EnableRAMPower();
| 6 | PINSEL5 = 0x55010115;
| 7 | PINMODE5 = 0xAAaaaaaA;
| 8 | PINSEL6 = 0x55555555;
| 9 | PINMODE6 = 0xAAAAAAAA;
| 10 | PINSEL7 = 0x55555555;
| 11 | PINMODE7 = 0xAAAAAAAA;
| 12 | PINSEL8 = 0x15555555;
| 13 | PINMODE8 = 0x1AAAAAAA;
| 14 | PINSEL9 = (1 << 18);
| 15 | PINMODE9 = 0x80000;
| 16 |
| 17 |
| 18 |
| 19 | EMC_CTRL=1; // enable EMC
| 20 | EMC_DYN_RD_CFG=1;//Configures the dynamic memory read strategy(Command delayed strategy)
| 21 | EMC_DYN_RASCAS0|=0x303;
| 22 | EMC_DYN_RP= 4;//RP
| 23 | EMC_DYN_RAS = 8;//RAS
| 24 | EMC_DYN_SREX = 5;// SREX, XSR not found
| 25 | EMC_DYN_APR = 2; //APR not found
| 26 | EMC_DYN_DAL = 5; //DAL, APW not found
| 27 | EMC_DYN_WR = 3; //WR, DPL, RWL, RDL
| 28 | EMC_DYN_RC = 11;//
| 29 | EMC_DYN_RFC = 11; //rfc rc
| 30 | EMC_DYN_XSR = 5;//txsr not found
| 31 | EMC_DYN_RRD = 3;//RRD
| 32 | EMC_DYN_MRD = 8;//RAS
| 33 | EMC_DYN_CFG0 = (1<<14)|(1<<9)|(2<<7);//8,9,14
| 34 |
| 35 | for(i = 0; i < 100; i++);
| 36 | EMC_DYN_CTRL = 0x0181; // NOP
| 37 | for(i = 0; i < 100; i++);
| 38 | EMC_DYN_CTRL=0x101;
| 39 | EMC_DYN_RFSH = 1;
| 40 | for(i = 0; i < 100; i++);
| 41 | EMC_DYN_RFSH = 19;
| 42 |
| 43 | EMC_DYN_CTRL=0x81;
| 44 | i = (*(volatile int*)(0xA0000000 | (0x32<<12)));
| 45 | EMC_DYN_CTRL = 1;
| 46 |
| 47 | EMC_DYN_CFG0|=0x80000;
| 48 | }
|
Sieht jemand noch Fehler? So läuft mein RAM test durch, allerdings die
Balkensache sieht immernoch unverändert aus.
Wenn der RAM-Test so durch läuft, dann wird das RAM ja wohl jetzt
funktionieren. Dann kann es nur noch an der Initialisierung des LCDs
liegen...
Etwa nicht? :-)
Ich meine - wenn das SDRAM sich beschreiben und lesen lässt, dann sollte
der LCD-Controller das ja auch tun können.
Leider habe ich mich noch nicht mit dem LCD-Controller der 2478 befasst,
aber wenn du deinen Code mal postest, kann dir vielleicht Kai helfen.
Vom LCD habe ich leider nur wenig Ahnung :-(
Sieht das Bild auf dem TFT immer noch exakt so aus wie vorher, jetzt da
der SDRAM geht?
Hast du vielleicht ein anderes Display, das du mal testen könntest? Wenn
das RAM funktioniert und der LCD-Controller richtig initialisiert ist,
kanns nur noch am Display liegen. Oder gar am LVDS-Chip? Bist du dir
sicher, dass dieser LVDS75... oder wie der auch schon wieder hiess,
kompatibel ist zum LPC2478 LCD Controller?
Wie sieht das Bild aus, wenn du z.B. komplett alles rot machst?
Ist dann alles einheitlich gefärbt?
Da der Controller vermutlich nur während des aktiven Bildbereiches Daten
ausgibt, müssten die Datenleitungen für rot dann ein ähnliches Signal
beinhalten wie DataEnable.
Tobias Plüss schrieb:
> Oder gar am LVDS-Chip? Bist du dir
> sicher, dass dieser LVDS75... oder wie der auch schon wieder hiess,
> kompatibel ist zum LPC2478 LCD Controller?
Der LVDS Encoder ist nichts anderes wie ein parallel -> Seriell Wandler,
und der Dekoder macht das ganze umgekehrt. Das einzige was inkompatibel
sein kann, ist die Flanke, aber deren Auswirkungen sind gering.
0x00ff0000 = Alles Blau
0x0000ff00 = Alles Grün
0x000000ff = Alles Rot
Also als Leuchtfläche Funktioniert es. Ich hoffe der LVDS Transmitter
läuft mit dem Controller.
Na, also - als Leuchtfläche geht schon mal. Rein aus Neugier: Kannst du
mit
0xffff00,
0xff00ff,
0x00ffff
alles Cyan, Magenta und Gelb färben? (wird sicher gehen; wie gesagt
reine Neugier).
Dann: sind die Flächen "schön", oder sehen sie auch so zerhackstückelt
aus wie die Bilder oben? Was passiert, wenn du abwechslungsweise z.B.
0x0000ff und 0xff0000 in das RAM schreibst? dann müsste das Bild ja eine
Art Schachbrettmuster geben, wo jeder Pixel abwechslungsweise rot und
dann wieder blau ist.
Gut, die Farben passen schonmal.
Daraus kann man schließen, dass LVDS mäßig alles passt, ansonsten würde
nämlich garnichts funktionieren, da die Farbdaten sowie Timingsignale
gemultiplexed übertragen werden.
Jetzt versuch mal herauszufinden was passiert wenn du z.B. nur den
halben Speicher beschreibst usw.
So, das zittern ist weg! 1 | void TFT_init(){
| 2 | //Set up 24 bit mode etc.
| 3 |
| 4 | PINSEL0 |= 0x00055500;
| 5 | PINSEL3 |= 0x05555500;
| 6 | PINSEL4 |= 0x050FFFFF;
| 7 | PINSEL9 |= 0x0A000000;
| 8 | PINSEL11 |= 0x0000000F;
| 9 |
| 10 | PCONP |= (1<<20); //Enable LCD controller
| 11 | LCD_CTRL = 0x2A; //TFT 24 Bit
| 12 | LCD_CFG = 1; //Set divider CCLK/2 = 40 MHz
| 13 | LCD_POL = (0x01 << 26)|(799 << 16)|(0x00 << 14)|(0x00 << 13)|(0x01 << 12)|(0x01 << 11);
| 14 | //Bypass pixel clock divider | 800 clocks per line | invert: panel clock, h clock, v clock
| 15 | LCD_TIMH = (87 << 24)|(39 << 16)|(127 << 8 )|(49 << 2);
| 16 | //Back porch = 88 clk | Front porch = 40 clk | Pulse with = 128 clk | Size = (49 + 1)*16 = 800 px
| 17 | LCD_TIMV = (22 << 24)|(1 << 16)|(3 << 10)|(599);
| 18 | //Back porch = 22 px | Front porch = 1 px | Pulse with = 3 px | Size = 600 px
| 19 | LCD_UPBASE = LCD_VRAM_BASE_ADDR & 0xfffffff8;
| 20 | LCD_LPBASE = LCD_VRAM_BASE_ADDR & 0xfffffff8;
| 21 | LCD_CTRL |= 1|(1<<11); //Enable LCD
| 22 |
| 23 | }
|
Wenn ich in 1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<800;x++)
| 4 | {
| 5 | if ((x<200)==0)
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x0000ff00;
| 7 | else if (x<400)
| 8 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ff0000;
| 9 | else if (x<600)
| 10 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x000000ff;
| 11 | else
| 12 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 13 | z+=4;
| 14 | }
| 15 | }
|
y nur bis 100 laufen lasse ich nur ein Teil des TFTs gefüllt, der Rest
schwarz.
1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<800;x++)
| 4 | {
| 5 | if (((x==y)))
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 7 | z+=4;
| 8 | }
| 9 | }
|
Sollte doch einen schräge Linie anzeigen. Es zeigt mir verteilte weiße
Punkte an, aber Standbild!
Sieht aus, als ob ich den Framebuffer entweder in x oder y
vergrößern/verkleinern müsste.
Ist das Farbmuster mit den 4 Farbbereichen ok? Also siehst du alle 4
Farben so wie es sein sollte? Falls ja, passt die Bildbreite, sonst
wären die Zeilen versetzt.
Ich habe den Balkencode so abgeändert: 1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<800;x++)
| 4 | {
| 5 | if (x<200)
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x0000ff00;
| 7 | else if ((x<400)&&(x>200))
| 8 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ff0000;
| 9 | else if ((x<600)&&(x>400))
| 10 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x000000ff;
| 11 | else
| 12 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 13 | z+=4;
| 14 | }
| 15 | }
|
Bild 94 zeigt das Bild in Betrieb, Bild 93, wenn man das Display nicht
mehr ansteuert.
Es sind alle Farben vorhanden.
Anstelle der 200 Pixel breiten Farbbalken erscheinen also diese nur
wenige Pixel breiten, verzerrten Streifen?
Bist du sicher, dass das SDRAM Timing passt? Eventuell funktioniert z.B.
der Burst Read nicht, der eventuell für den LCD Controller verwendet
wird?
Meiner Meinung nach sieht das so aus, als ob da irgendwie Zeilen und
Spalten irgendwie vertauscht wären - wenn du den ganzen SDRAM ja mit
0xff0000 füllst ist ja alles schön blau, bei 0x0000ff ist alles rot -
soweit okay. Aber das sagt nichts darüber aus, wie die einzelnen 32 Bit
Wörter im Memory zu den Pixeln korrelieren. Vielleicht ist da ja
irgendwas vertauscht?
Mit der TestFunc weiter oben kann ich nachweisen, das die Daten im RAM
stimmen. Es muss also entweder an der Init des Controllers liegen, dem
Aufbau des Framebuffers oder der Palette des Controllers, der ich
momentan nicht ein Byte würdige.
Vielleicht kann mir jemand sagen, wie wichtig die Palette ist? Ich werde
an der Stelle aus dem Datenblatt echt nicht schlau.
Die Palette ist üblicherweise für den 24bit Modus uninteressant, da man
hier schon alle Farben darstellen kann. Siehe dazu Abschnitt 8. TFT
panels: Nur bei 8bpp und kleiner wird die Palette verwendet.
Wie sieht das Bild aus, wenn du im obigen Code anstelle von den 4
vertikalen Balken 4 horizontale anzeigst?
1 | for (y=0;y<600;y++)
| 2 | {
| 3 | for (x=0;x<400;x++)
| 4 | {
| 5 | if (y<150)
| 6 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x0000ff00;
| 7 | else if ((y<300)&&(y>150))
| 8 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ff0000;
| 9 | else if ((y<450)&&(y>300))
| 10 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x000000ff;
| 11 | else if (y>450)
| 12 | *(unsigned int *)((unsigned int )&SDRAM_BASE_ADDR+z) = 0x00ffffff;
| 13 | z+=4;
| 14 | }
| 15 | }
|
Ich habe etwas experimentiert: Mit dem obigen Code erhalte ich dieses
Bild.
Dass die Daten im RAM stimmen, bezweifelt ja niemand. Was ich eigentlich
meinte mit meinem vorherigen Post war eher folgendes:
Logischerweise Gehört das erste 32 Bit Wort im LCD-Puffer zum Pixel Nr.
0, also links oben. Zu welchem Pixel gehört dann das nächste Wort? Wenn
diese Zuordnung Speicherinhalt -> Pixel nicht stimmt, kommt Murks auf
dem Display raus.
Deshalb meinte ich auch: Füll mal deinen Speicher abwechslungsweise mit
0 und 0xffffff (als Beispiel). Was kommt dann raus? Oder füll den ganzen
Speicher mit 0, und nur das aller erste Wort mit 0xffffff.
So würde ich jedenfalls versuchen, das zu debuggen - immer nur Schritt
für Schritt. Wenn du gleich auf einen Schlag das mit den 4 farbigen
Bereichen machen willst, birgt das doch zu viele Fehlerquellen. Irgend
ein Fehler im Code, Alignment nicht richtig im SDRAM, was weiss ich.
Ach ja, was mir grade einfällt, was du noch versuchen könntest:
Füll den Speicher derart, dass das erste Wort 0 ist, und jedes weitere
dann um eins inkrementiert wird. Dann müsste irgend ein
"Regenbogenmuster" auf dem Display erscheinen (meine ich).
Das sieht für mich so aus, als wenn er mit schnellen Wechseln der
Bilddaten Probleme hätte. Für lange Zeit die gleichen Daten sind dagegen
absolut problemlos.
Bau mal einen Code ein, der einfach sinnlos in einen anderen
Speicherbereich 0en schreibt und lass diesen Code ununterbrochen laufen,
so dass die Daten auf dem Datenbus vom SDRAM mit 0en unterbrochen
werden.
Ich hatte man ein ähnliches Phänomen was sich am Ende als Timingproblem
beim SDRAM rausstellte.
Selbst auf 24 MHz CPU und RAM Takt sieht es unverändert aus. Auch wenn
ich zwischendurch immer auf irgendwas zugreife.
Möglicherweise stimmen meine RAM Timings nicht ganz, oder dieser RAM ist
einfach defekt. (Auf Grund von Ungeduldigikeit von einer Grafikkarte
ausgelötet, von der ich nicht weiß ob die noch funktionierte!)
Was mich aber wundert: warum läuft der Speichertest durch ohne Fehler?!
Um sicherzugehen, dass der RAM Test wirklich funktioniert (auch wenn es
sehr unwahrscheinlich ist dass er es nicht tut): Veränder doch mal
irgendein Byte nach dem Beschreiben. Dann müsste der RAM Test den Fehler
anzeigen.
Irgendwo müssen die Daten durcheinander kommen.
Da die horizontalen Balken passen, kann man das Display sowie dessen
Ansteuerung als Fehler ausschließen. Auch scheinen zumindest ab und zu
die Daten zu passen.
Kannst du mal prüfen ob eventuell ein FIFO Underflow eintritt? Bei 80MHz
SDRAM Takt und 40MHz Displaytakt dürfte das eigentlich nicht vorkommen,
aber man weiß ja nie...
Also RAM Test, ich habe einfach mal ein Byte nach dem beschreiben
verändert. Wurde korrekt gemeldet.
Ja, die Daten müssen irgendwo durcheinander kommen, und ich bin etwas
ratlos wo das passiert.
Display timings:
Dann dürfte sich ja nichts verändern wenn man nicht im Framebuffer
rumpfuscht. Ich habe ja ein Standbild. Also kann man den LVDS
Hochfrequenzteil auch ausschließen.
SDRAM: RAM Test funktioniert, da werden die Daten auch schnell
hintereinander beschrieben, und das erfolgreich.
Wie prüfe ich auf einen FIFO underflow?
Mach mal einen Test:
1. Alles schwarz füllen, nur einen einzigen Punkt weiß.
2. Einen zweiten Punkt links neben dem ersten abwechselnd (vielleich 1
Hz) in weiß und schwarz zeichnen, den Rest so lassen.
Frage: Verändert sich die Position des ersten Punktes auf dem
Bildschirm?
Omega G. schrieb:
> Also RAM Test, ich habe einfach mal ein Byte nach dem beschreiben
> verändert. Wurde korrekt gemeldet.
Also kann man den auch ausschließen.
Der Fehler muss also irgendwo im Bereich des internen LCD Controllers
liegen.
> Display timings:
> Dann dürfte sich ja nichts verändern wenn man nicht im Framebuffer
> rumpfuscht. Ich habe ja ein Standbild. Also kann man den LVDS
> Hochfrequenzteil auch ausschließen.
Ja.
Was du mal probieren könntest: Den Pixeltakt halbieren, den Rest lassen
(also den CPU und SDRAM Takt).
Ist dann zwar außerhalb der Specs, aber es sollte dennoch gehen.
> Wie prüfe ich auf einen FIFO underflow?
Laut Datenblatt wird dann im LCD_INTRAW Register ein Bit gesetzt.
Funktioniert!
Woran lag's:
Benedikt hatte (wie so oft in Sachen Displays) die zündende Idee:
Display Clock zu hoch. Scheinbar kam das SDRAM einfach nicht hinterher.
Wunderbar!
Das wundert mich aber dennoch, denn eigentlich läuft der SDRAM doppelt
so schnell wie die Daten benötigt werden. Allerdings hatte ich mit einem
LCD Controller schon ähnliche Erfahrungen gemacht, daher auch die Frage
nach der zusätzlichen Schleife die Daten ins SDRAM schreibt um das
zusätzlich zu belasten.
Die SDRAM Controller scheinen also nicht perfekt zu sein.
Als Ausweg könntest du entweder das SDRAM Timing verbessern indem du ein
schnelleres SDRAM verwendest (und somit z.B. eine geringere CAS Latency
einstellen kannst, ob das allerdings ausreicht ist zu bezweifeln), oder
aber indem du nur 16bit Farbtiefe verwendest.
Genau! Habe grad mal den LCD-BusLoadCalculator durchlaufen lassen: Bei
60Hz sind's 153% Buslast auf dem Speicher!
Eigentlich ist eh ein anderes RAM vorgesehen, dann optimiere ich das ein
wenig, mal sehen was machbar ist! Vielen Dank für die Hilfe!!!
Offtopic:
Hat jemand ein Erklärung dafür, warum gerade mal 85MByte/s Datenrate zur
Verfügung stehen, obwohl das SDRAM eigentlich 80MHz*32bit=320MByte/s
könnte?
Klar, es gibt einen Overhead bei der Ansteuerung, aber die erklärt
keinen Unterschied von fast Faktor 4.
Und vor allem: Warum sich bei einem 16bit SDRAM die Datenrate gerade mal
auf 67,4Mbyte/s reduziert und nicht halbiert. Das sind gerade mal 26%
Unterschied in der Datenrate.
Yeah, cool dass es jetzt funktioniert!
Geht denn nun auch wirklich alles zuverlässig?
Es scheint soweit zuverlässig zu sein. Ich habe ein Weile eine Plasma
Animation laufen lassen. Ruckt zwar ziemlich, aber macht viele
Speicheroperationen.
Läuft das TFT immer noch mit 20MHz?
Für den richtigen Betrieb würde ich auch 16bpp runtergehen, denn 20MHz
ist weit außerhalb der Specs. Ob das langfristig negative Auswirkungen
auf das TFT hat, kann man daher nicht sagen.
Ich denke ich habe die Ursache für den langsamen SDRAM gefunden:
Der LPC liest dem BusLoadCalculator nach immer 16Bytes auf einmal. Dazu
benötigt er bei einem 32bit SDRAM 15 Takte! Der SDRAM Controller ist
also dumm, da er nichtmal eine bereits geöffnete Bank berücksichtigt
wenn ich das richtig sehe. Das hätte man deutlich besser machen können.
Das würde die Zeit pro Burst auf 11 Takte reduzieren wenn die Bank
gleich ist, was rund 25% mehr Datenrate bedeuten würde.
Es hätte auch schon viel gebracht, wenn er mit 32bit SDRAMs 8er Bursts
unterstützen und somit gleich 32 Bytes lesen würde.
Ein 8er Burst wurde 19 Takte dauern, dafür aber die Datenrate
verdoppeln, macht 134,7MByte/s statt 85,3MByte/s. Rund 58%
Geschwindigkeitssteigerung.
Das noch kombiniert mit dem intelligenteren Controller (da es ein LCD
Controller ist, kann man wohl davon ausgehen dass viele Daten
sequentiell gelesen werden so dass dies Sinn macht, und so aufwendig ist
das auch nicht) wären das 32Bytes in 15 Takten, also 170,7MByte/s, eine
Steigerung um satte 100%.
Anscheinend haben die das Problem erkannt, denn der LPC32x0 liest immer
64Bytes auf einmal, was immerhin 80% der theoretisch maximalen Datenrate
erlaubt (ok, der spielt auch in einer anderen Liga, das ist klar). Der
LPC2478 kommt gerade mal auf schlappe 21,3%.
Der LCD-Controller im LPC2478 und in den LPC32x0 ist ja der gleiche und
stammt ursprünglich von Sharps LH7-Controllern, die NXP aufgekauft hat.
Das Problem ist halt der Speichercontroller, der in den LPC32x0 halt ein
anderer und besserer ist.
Wenn es beim LPC2478 etwas knapp wird mit der Bandbreite kann man den
AHB von Round-Robin auf eine Priorisierung einzelner Peripheriemodule
umstellen. So kann man, auch wenn die Last des Speichercontrollers gegen
100% geht, das Display trotzdem noch flüssig laufen lassen. Näheres im
Usermanual: AHBCFG1 und AHBCFG2.
@Kai,
du schnell eine Frage wegen der SDRAMs!
Ich hatta ja seinerzeit, bei der Inbetriebnahme meines Testboards, etwas
Ärger mit denen, du hast mir dann freundlicherweise einen kleinen
Initialisierungscode gebastelt, den ich bis heute so benutze (weil er
absolut einwandfrei funktioniert ;-).
Mittlerweile verstehe ich den mehr oder weniger, nur eines ist mir heute
aufgefallen, und zwar die Zeile:
dwDelay = (*(tpDWord)(0xA0000000 | (0x22 << 13))); /* burst length 4, 2
cas */
Was bewirkt diese? rein vom Kommentar her würde ich mal drauf tippen,
dass sie das Mode Register initialisiert. Aber das kann nicht sein, da
das Mode Register nur 14 Bits umfasst, und wenn die Konstante 0x22 um 13
nach links geschoben wird, wäre diese ja grösser als das Mode
Register... Ausserdem sind die Bits zum Einstellen der Burstlänge und
der CAS-Latency die Bits 0..2 und 3..4 (oder irgendwie so). Warum muss
da noch um 13 geschoben werden?
Ich benutze übrigens immernoch meine ominösen IS42S16160B.
Das Mode-Register wird ja über die Adressleitung geschrieben, d.h. diese
Zeile ist ja ein (spezieller) Lesezugriff auf eine bestimmte Adresse.
Jetzt ist es aber so, dass die Adressierung bei einem SDRAM nicht in
einem Schritt passiert, sondern in drei (Row, Column und dann in der
Seite). Schau mal in das Dokument, das du oben verlinkt hast.
Das knifflige an den SDRAMS und deren Initialisierung ist dann halt den
Shift-Wert (in den Fall 13) richtig zu bestimmen, dass die Bits auch an
der richtigen Stelle im Mode-Register landen.
Burst-Length steht in Bit[2:0], CAS-Latency in Bit[6:4].
Mit der 13 werden nun diese Bits so verschoben, dass sie in der
Row-Adresse stehen. Wenn ich mich richtig erinnere: Column-Breite +
Bus-Breite (1 für 16 Bit; 2 für 32 Bit) + 2 (für Bänke) -> 9+2+2 = 13.
Hi Kai,
danke! Das erklärt so einiges.
Noch eine Frage:
Es gibt die beiden Register EMCDynamictXSR und EMCDynamictSREX. Diese
beiden Zeiten findet man in den wenigsten SDRAM-Datenblättern; ausserdem
lautet im User's Manual die Beschreibung zu beiden Registern "Dynamic
Memory Self-Refresh exit time", sie bewirken also (rein von der
Beschreibung her) dasselbe. Ist das ein Fehler im Manual, oder sind
diese Register wirklich redundant? Wenn ja - was ergibt das für einen
Sinn?
Und - was tun, wenn man tXSR bzw. tSREX nicht findet in dem Datenblatt
des gewünschten SDRAM?
Und - wie kritisch ist das Timing? Ich habe mir ein Excel-Sheet gebaut,
bei welchem man die Timings des SDRAM-Chips direkt aus dem Datenblatt
eingeben kann, sowie die CPU-Frequenz, und die Werte aller Register
werden dann selbsträndig berechnet.
So komme ich auf leicht andere Werte, als die, die du mir damals
angegeben hast (teilweise bekomme ich mehr, teilweise weniger). Die
ganze Sache scheint mir eine nicht ganz so exakte Wissenschaft zu sein;
kannst du dazu auch was sagen?
Ich könnte mir ausserdem gut vorstellen, dass bei falsch oder ungünstig
eingestellten Timings das SDRAM zwar funktioniert, aber eben nicht
zuverlässig bzw. dass die Burst-Zugriffe oder ähnliches nicht korrekt
funktioniert. Wie kann man dies einigermassen zuverlässig testen?
Ich habe jetzt, da ich die Timings von meinem SDRAM zu optimieren
versucht habe, einen Memory Test implementiert, der jedes einzelne Bit
des SDRAMs testet. Der Test läuft jetzt schon 15 Minuten ohne Fehler in
einer Endlosschleife durch, aber Burstzugriffe oder dergleichen werden
so natürlich nicht wirklich getestet....
Falls übrigens bei jemandem Interesse bestehen sollte, kann ich mein
Excel-Sheet hier hochladen. Es berechnet die Werte für den Refresh
Timer, sowie die anderen Timing-Register; das einzige was man machen
muss ist, die Daten aus dem SDRAM-DB ins Excelsheet zu übertragen und
CPU-Frequenz angeben. Der nächste Schritt ist dann die automatische
Generierung eines Initialisierungscodes.... ;-)
Darf ich das Excel Sheet haben? Ich habe meine Werte von Hand
ausgerechnet. Bzw. teils von anderen Implementationen übernommen weil
Angaben fehlten.
Übrigens läuft das Display momentan mit 16 MHz. Mehr geht noch nicht.
(Ich habe seit gestern Abend keine Zeit mehr gehabt was zu machen.) Ich
würde gerne versuchen den RAM schneller laufen zu lassen (Also an den
Timings drehen) und dann versuchen auf 20 MHz zu gehen.
Na klar kannst du es haben - siehe Anhang :-)
Ich habe jetzt meine Timings gemäss dem Sheet berechnet, und das SDRAM
scheint zuverlässig zu laufen. Von daher denke ich sollten die
Berechnungen stimmen.
Eine weiteres Sheet für den Static Memory Controller werde ich auch noch
machen (der Static Memory Controller lässt sich wunderbar missbrauchen,
um am externen Bus irgendwelche Memory Mapped IOs anzuschliessen; ich
habe mal einen Haufen Flipflops ['HC273] angeschlossen, das ergibt
wunderbare 8 Bit Ports....).
Bei Interesse lade ich das dann auch noch hoch.
Allerdings hat auch das Excel Sheet einen Mangel:
Wenn gewisse Angaben im Datenblatt fehlen (z.B. tSREX), dann können
diese damit natürlich auch nicht berechnet werden :-( da habe ich auch
teils ausprobieren müssen oder einfach geraten.
Übrigens: wer Freude an solchen Excel Sheets hat....
ich hab einige hier, allerdings nicht nur zu LPC24xx Controllern,
sondern auch zur Schaltnetzteilberechnung.
Hi Leute,
mal noch eine Frage wegen des SDRAM-Interface.
Ich habe festgestellt, dass der CLKOUT recht verschliffene Flanken hat,
wenn er mehrere SDRAMs treiben soll.
Auf meinem Testboard sieht er schon fast sinusförmig aus! Natürlich, es
funktioniert, ist aber hässlich. Ich habe jetzt aus ein paar alten
Mainboards Clock-Treiber ausgelötet - sogenannte "Zero Delay Clock
Buffers". Es handelt sich dabei um TI CDCVF2505PWR - ein Clock-Input, 4
Clock Outputs.
Diesen möchte ich jetzt einsetzen als Clock Buffer; er schafft diese
Frequenz natürlich locker (geht bis 200 MHz).
In den Kennlinien kann man jedoch erkennen, dass der Duty Cycle nicht
50% ist - etwas weniger; daher frage ich mich: kann man den dennoch als
Clock Buffer benutzen für den LPC2468? Denn ich möchte noch einen
kleinen FPGA mit dem Clock versorgen und noch weitere ICs, deshalb denke
ich ist ein Clock Buffer schon nötig.
Was meint ihr dazu?
@Omega:
noch eine kleine Frage; kann man deinen Source Code zum Ethernet mal
kriegen? Im Thread Beitrag "Re: NXP verschenkt ARM-Chips"
schreibst du ja, dass das bei dir bereits läuft.
Tobias Plüss schrieb:
> In den Kennlinien kann man jedoch erkennen, dass der Duty Cycle nicht
> 50% ist - etwas weniger; daher frage ich mich: kann man den dennoch als
> Clock Buffer benutzen für den LPC2468?
Solange die steigende Flanke an der selben Stelle bleibt geht das (und
genau das macht das IC). Ob 48 oder 50% Duty macht keinen wirklichen
Unterschied.
Hi Benedikt,
cool danke!
Ja ich glaube, die steigende Flanke sollte wirklich an der selben Stelle
bleiben. Der Skeq ist im ps-Bereich... Sonst wäre ja die Bezeichnung
"Zero Delay Buffer" nicht korrekt ;-)
Mein Ethernet Treiber macht momentan noch nicht viel, da mir unter der
Woche auf Grund von Studium die Zeit doch etwas knapp ist. Aber trotzdem
hier meinen Anfang 1 | int MAC_init(){
| 2 | int tout,i;
| 3 | PCONP |= (1<<30);
| 4 | PINSEL2 |= 0x50150105;
| 5 | PINSEL3 |= 0x5;
| 6 |
| 7 | //Reset MAC
| 8 | MAC_MAC1 = 0x00000100 | 0x000002009 | 0x00000400 | 0x00000800 | 0x00004000 | 0x00008000;
| 9 | MAC_COMMAND = (1<<3) | (1<<4) | (1<<5);
| 10 |
| 11 |
| 12 | for (tout = 0; tout<10000; tout++);
| 13 | MAC_MAC1 = 0x00000002;
| 14 | MAC_MAC2 = 0x00000010 | 0x00000020;
| 15 | MAC_MAXF = 1522;
| 16 | MAC_CLRT = 0x0000370F;
| 17 | MAC_IPGR = 0x00000012;
| 18 |
| 19 | /* Enable Reduced MII interface. */
| 20 | MAC_COMMAND = 0x00000200 | 0x00000040;
| 21 |
| 22 | write_PHY (0, 0x8000); //send reset command
| 23 | while(read_PHY(0) & 0x8000); //wait until reset is complete
| 24 |
| 25 | MAC_MCFG = (0x06 << 2) | 0x00008000;
| 26 | for ( i = 0; i < 0x40; i++ );
| 27 | MAC_MCFG &= (~0x00008000); /* Clear the reset */
| 28 | MAC_MCMD = 0;
| 29 |
| 30 | write_PHY (0, 0x1100); //Setup auto-negotiation, full duplex
| 31 |
| 32 | //tout = 0;
| 33 | //while(!(read_PHY(1) & 4) & (tout < 500000)) //check and wait until ethernet is up
| 34 | // tout++;
| 35 | //
| 36 | MAC_SA0 = (MYMAC_1 << 8) | MYMAC_2;
| 37 | MAC_SA1 = (MYMAC_3 << 8) | MYMAC_4;
| 38 | MAC_SA2 = (MYMAC_5 << 8) | MYMAC_6;
| 39 |
| 40 | MAC_RXDESCRIPTOR = 0x7FE00000;
| 41 |
| 42 | return read_PHY(1);
| 43 | }
|
Ich werde sämtliche Treiber die ich schreibe auch auf meine Homepage
stellen. Dort protokolliere ich auch gefundene Fehler und zeige wie sie
umgeht. Die 50 MHz Clock von der PHY sehen übrigens sehr schön aus.
Soweit ich das mit einem 200 MHz Oszilloskop beurteilen kann.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|