Hallo alle zusammen, ich versuche gerade die Kommunikation zwischen einem NXP LPC1857 und einem SDRAM MT48LC4M32B2P-6 hinzubekommen. Dazu verwende ich das KEIL MCB1857 Entwicklungsboard. Als Entwicklungsumgebung nutze ich LPCXpresso IDE mit dem LPC-Link. Ich habe ein einfaches Projekt angelegt. Der EMC wird in der emc.c initialisiert und in der main.c wird dann ein Testprogramm ausgeführt, welches mir bei erfolgreichem Schreiben auf den SDRAM sowie bei fehlgeschlagenem Schreiben je eine LED ansteuert. Mein Problem ist jetzt, dass es nicht funktioniert. Als EMC Takt verwende ich 120MHz, die ich einfach nur in der CGU_Init()-Funktion mit SetPLL1(10) eingestellt hab. Den CLK0 (rot) und den CKE0 (gelb) habe ich gemessen (siehe Bild im Anhang). Ich habe beide Signale mit einem anderen Board und einem funktionierenden Beispielcode verglichen. Also der Clock scheint schon mal in Ordnung zu sein. Das Linker-Script habe ich manuell im MEMORY-Bereich erweitert (siehe Anhang SDRAM16). Könnte sich bitte jemand meine Dateien mal ansehen, ob er eventuell den Fehler findet, warum ich keine erfolgreiche Kommunikation hinbekomme? Vielen Dank! :-) Beste Grüße, Martin
Guten morgen :-) hat jemand Erfahrungen mit den LPC18xx Controllern und Linker-Skripten in LPCXpresso IDE oder der Code Red IDE? Reicht es folgende Einstellungen vorzunehmen: In der ..._mem.ld Datei:
1 | MEMORY
|
2 | {
|
3 | ...
|
4 | ...
|
5 | SDRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x1000000 |
6 | }
|
7 | |
8 | ...
|
9 | ...
|
10 | __top_SDRAM = 0x28000000 + 0x10000000; |
In der ... .ld Datei: Erstellen einer section (Beispiel):
1 | .VRAM (NOLOAD) : ALIGN(4) |
2 | {
|
3 | *(.VRAM) |
4 | } > SDRAM |
Ist es ausreichend, nur die Einstellungen in der ...mem.ld vorzunehmen, um nur einen Wert in den Speicherbereich des SDRAM zu schreiben? Die section benötige ich doch nur, wenn ich eine Variable oder eine Funktion in diesen Bereich ablegen will, oder? Danke. Mfg Martin
Martin schrieb: > Mein Problem ist jetzt, dass es nicht funktioniert. > > Als EMC Takt verwende ich 120MHz Der EMC in den LPC178x (!) verträgt nur 80MHz: http://www.lpcware.com/content/forum/dk-57vts-lpc1788-configuring-emc-sdram Würde mich wundern, wenn es beim LPC18xx anders wäre, es sei denn, es steht explizit im Usermanual. bye, Arne PS: SDRAM am LPC1788 steht mir auch noch bevor...
Hallo Arne, danke für den Hinweis, ich werd gleich nochmal schauen, wie hoch die Frequenz sein darf. Ein funktionierendes Beispiel für den EMC des 1788 findest du im BSP von emWin.
Also die typische maximale EMC Frequenz ist 120MHz. Das heißt ich betreib den gerade am Maximum. Naja ich werd mal mit 60 oder 72MHz versuchen.
Unterscheidet sich der EMC vom 178x zum 1857? Sonst könntest Du doch auf das emWin Bsp. zurückgreifen. Ich meine der EMC ist sogar in LPC2xxx ARM7 drin.
BTW: hast Du da nicht ein Henne-Ei Problem? Der StartUp Code "kennt" anhand des Linkerskripts das externe SDRAM und versucht u.U. da das BSS zu nullen, bzw. das DATA Segment aus dem int. Flash dahin zu kopieren. Und das alles bevor der EMC samt SDRAM korrekt konfiguriert ist? Ich werde bei mir einen Bootloader programmieren, der das ext.RAM vom Linkerskript nicht kennt, aber den EMC und das SDRAM bereits korrekt initialisiert. Dann erfolgt der Sprung in die Applikation, die vom Linkerskript das ext.RAM bereits kennt. Dann kann vom BSS/DATA kopieren nichts schief gehen.
Im Wesentlichen habe ich die EMC Configuration vom 1788 genommen. Es mussten nur die Pinkonfigurationsregister für den 1857 angepasst werden und halt die Daten vom neuen SDRAM. Trotzdem gehts nicht. Zum Linker-Script: Ich kann ja zum einen den SDRAM unter MCU Settings per Edit im Memory Configuration Editor hinzufügen. Danach "Manage Linker script" auswählen und das Linker script wird automatisch erstellt. Dabei entstehen neben der Memory-Region "SDRAM" auch noch .DATA- und .BSS-Sektionen für "SDRAM". Zum anderen kann man "Manage Linker Script" auch abwählen und das existierende (ohne SDRAM) manuell bearbeiten. Im emWin-Beispiel vom 1788 wurden lediglich die Einträge für die Memory-Region in der _mem.ld Datei vorgenommen und die Sektionen .GUI_RAM und .VRAM in der .ld erstellt. Da gibt es keine .DATA oder .BSS-Sektionen für den SDRAM. Also habe ich mich an das Beispiel gehalten und nur eine neue Memory-Region für den SDRAM erstellt, da ich im Moment für den Test die .VRAM und .GUI_RAM Sektionen nicht benötige. Ich habe nichts bei .DATA und .BSS programmiert. Wie sollte denn das Linker-Skript richtig programmiert werden? Benötige ich die .DATA- und .BSS-Sektionen? Anbei habe ich die Linker-skript Dateien vom emWin BSP anghängt.
Ich habe jetzt noch einmal meine Testroutine geändert. Jetzt sieht es so aus, als ob das Schreiben auf den SDRAM funktioniert. Testroutine vorher:
1 | unsigned int SDRAM_Test (void) |
2 | // Test-Routine aus der NXP AppNote 10771
|
3 | {
|
4 | unsigned int i; |
5 | // 32 bits access
|
6 | for (i = 0; i < 0x2000000; i+=sizeof(unsigned int)) |
7 | {
|
8 | *(unsigned int *)((unsigned int )SDRAM_BASE_ADDR+i) = i; |
9 | }
|
10 | for (i = 0; i < 0x2000000; i+=sizeof(unsigned int )) |
11 | {
|
12 | if (*(unsigned int *)((unsigned int )SDRAM_BASE_ADDR+i) != i) |
13 | {
|
14 | return(0); |
15 | }
|
16 | }
|
17 | |
18 | return(1); |
19 | }
|
Aktuelle Testroutine:
1 | unsigned int SDRAM_Test (void) |
2 | // Test-Routine aus der NXP AppNote 10771
|
3 | {
|
4 | unsigned int i; |
5 | // 32 bits access
|
6 | for (i = 0; i < 0x2000000; i+=sizeof(unsigned int)) |
7 | {
|
8 | *(unsigned int *)((unsigned int )SDRAM_BASE_ADDR+i) = i; |
9 | |
10 | if (*(unsigned int *)((unsigned int )SDRAM_BASE_ADDR+i) != i) |
11 | {
|
12 | return(0); |
13 | }
|
14 | }
|
15 | |
16 | return(1); |
17 | }
|
Jetzt müsste es doch so sein, dass der aktuelle Wert von i an die Stelle SDRAM_BASE_ADDR+i geschrieben und gleich im Anschluss überprüft wird. Vorher wurden alle Werte i nacheinander in den SDRAM geschrieben. Danach wurden wieder alle Werte i hochgezählt und überprüft. Dabei werden sich ja bestimmte Werte überschrieben haben und es kam zum Fehlschlagen der Überprüfung. Passt meine Überlegung oder hab ich da einen Denkfehler drin? Das Programm funktioniert jedenfalls erst einmal.
Ich habe jetzt noch einmal die Test_SDRAM - Routine vom emWin LPC1788 Board support package ausprobiert und festgestellt, dass meine Kommunikation mit dem SDRAM nicht funktioniert. Warum klappt es aber mit der Test-Routine aus dem letzten Post? Kann es sein, dass ich dort gar nicht wirklich auf den SDRAM schreibe??
Habe nichts mit dem LPC1857 gemacht, aber der soll doch kompatibel zum LPC4357 sein bzw. umgekehrt, und damit vielleicht auch der EMC: http://www.lpc4350.com/lpc43xx/Examples/SDRAM_Test/
Habs immer noch nicht hinbekommen :-( Hab jetzt folgende EMC_Init-Routine:
1 | void emc_WaitUS(volatile uint32_t us) |
2 | {
|
3 | us *= ((SystemCoreClock / 1000000)/3); |
4 | while(us--); |
5 | }
|
6 | |
7 | void emc_WaitMS(volatile uint32_t ms) |
8 | {
|
9 | ms *= ((SystemCoreClock / 1000)/3); |
10 | while(ms--); |
11 | }
|
12 | |
13 | /*********************************************************************
|
14 | *
|
15 | * _EMC_Init()
|
16 | *
|
17 | * Purpose:
|
18 | * Initializes external memory controller for SDRAM
|
19 | */
|
20 | |
21 | void _EMC_Init(void) { |
22 | |
23 | volatile U32 CmdDly; |
24 | volatile U32 Dummy; |
25 | volatile U32 i; |
26 | |
27 | LPC_SCU->EMCDELAYCLK = 0x7777; |
28 | //LPC_CREG->CREG6 |= (1 << 16);
|
29 | |
30 | LPC_EMC->CONTROL = 0x1; // EMC enable |
31 | LPC_EMC->CONFIG = 0x0; //Little Endian Mode |
32 | //
|
33 | // Port init
|
34 | //
|
35 | LPC_SCU->SFSP1_7 = (MD_PLN_FAST | FUNC3); //D0 |
36 | LPC_SCU->SFSP1_8 = (MD_PLN_FAST | FUNC3); //D1 |
37 | LPC_SCU->SFSP1_9 = (MD_PLN_FAST | FUNC3); //D2 |
38 | LPC_SCU->SFSP1_10 = (MD_PLN_FAST | FUNC3); //D3 |
39 | LPC_SCU->SFSP1_11 = (MD_PLN_FAST | FUNC3); //D4 |
40 | LPC_SCU->SFSP1_12 = (MD_PLN_FAST | FUNC3); //D5 |
41 | LPC_SCU->SFSP1_13 = (MD_PLN_FAST | FUNC3); //D6 |
42 | LPC_SCU->SFSP1_14 = (MD_PLN_FAST | FUNC3); //D7 |
43 | LPC_SCU->SFSP5_4 = (MD_PLN_FAST | FUNC2); //D8 |
44 | LPC_SCU->SFSP5_5 = (MD_PLN_FAST | FUNC2); //D9 |
45 | LPC_SCU->SFSP5_6 = (MD_PLN_FAST | FUNC2); //D10 |
46 | LPC_SCU->SFSP5_7 = (MD_PLN_FAST | FUNC2); //D11 |
47 | LPC_SCU->SFSP5_0 = (MD_PLN_FAST | FUNC2); //D12 |
48 | LPC_SCU->SFSP5_1 = (MD_PLN_FAST | FUNC2); //D13 |
49 | LPC_SCU->SFSP5_2 = (MD_PLN_FAST | FUNC2); //D14 |
50 | LPC_SCU->SFSP5_3 = (MD_PLN_FAST | FUNC2); //D15 |
51 | LPC_SCU->SFSPD_2 = (MD_PLN_FAST | FUNC2); //D16 |
52 | LPC_SCU->SFSPD_3 = (MD_PLN_FAST | FUNC2); //D17 |
53 | LPC_SCU->SFSPD_4 = (MD_PLN_FAST | FUNC2); //D18 |
54 | LPC_SCU->SFSPD_5 = (MD_PLN_FAST | FUNC2); //D19 |
55 | LPC_SCU->SFSPD_6 = (MD_PLN_FAST | FUNC2); //D20 |
56 | LPC_SCU->SFSPD_7 = (MD_PLN_FAST | FUNC2); //D21 |
57 | LPC_SCU->SFSPD_8 = (MD_PLN_FAST | FUNC2); //D22 |
58 | LPC_SCU->SFSPD_9 = (MD_PLN_FAST | FUNC2); //D23 |
59 | LPC_SCU->SFSPE_5 = (MD_PLN_FAST | FUNC3); //D24 |
60 | LPC_SCU->SFSPE_6 = (MD_PLN_FAST | FUNC3); //D25 |
61 | LPC_SCU->SFSPE_7 = (MD_PLN_FAST | FUNC3); //D26 |
62 | LPC_SCU->SFSPE_8 = (MD_PLN_FAST | FUNC3); //D27 |
63 | LPC_SCU->SFSPE_9 = (MD_PLN_FAST | FUNC3); //D28 |
64 | LPC_SCU->SFSPE_10 = (MD_PLN_FAST | FUNC3); //D29 |
65 | LPC_SCU->SFSPE_11 = (MD_PLN_FAST | FUNC3); //D30 |
66 | LPC_SCU->SFSPE_12 = (MD_PLN_FAST | FUNC3); //D31 |
67 | |
68 | LPC_SCU->SFSP2_9 = (MD_PLN_FAST | FUNC3); //A0 |
69 | LPC_SCU->SFSP2_10 = (MD_PLN_FAST | FUNC3); //A1 |
70 | LPC_SCU->SFSP2_11 = (MD_PLN_FAST | FUNC3); //A2 |
71 | LPC_SCU->SFSP2_12 = (MD_PLN_FAST | FUNC3); //A3 |
72 | LPC_SCU->SFSP2_13 = (MD_PLN_FAST | FUNC3); //A4 |
73 | LPC_SCU->SFSP1_0 = (MD_PLN_FAST | FUNC2); //A5 |
74 | LPC_SCU->SFSP1_1 = (MD_PLN_FAST | FUNC2); //A6 |
75 | LPC_SCU->SFSP1_2 = (MD_PLN_FAST | FUNC2); //A7 |
76 | LPC_SCU->SFSP2_8 = (MD_PLN_FAST | FUNC3); //A8 |
77 | LPC_SCU->SFSP2_7 = (MD_PLN_FAST | FUNC3); //A9 |
78 | LPC_SCU->SFSP2_6 = (MD_PLN_FAST | FUNC2); //A10 |
79 | LPC_SCU->SFSP2_2 = (MD_PLN_FAST | FUNC2); //A11 |
80 | LPC_SCU->SFSP2_1 = (MD_PLN_FAST | FUNC2); // A12 |
81 | LPC_SCU->SFSP2_0 = (MD_PLN_FAST | FUNC2); //A13 |
82 | LPC_SCU->SFSP6_8 = (MD_PLN_FAST | FUNC1); //A14 |
83 | LPC_SCU->SFSP6_7 = (MD_PLN_FAST | FUNC1); // A15 |
84 | LPC_SCU->SFSPD_16= (MD_PLN_FAST | FUNC2); // A16 |
85 | LPC_SCU->SFSPD_15= (MD_PLN_FAST | FUNC2); // A17 |
86 | LPC_SCU->SFSPE_0 = (MD_PLN_FAST | FUNC3); // A18 |
87 | LPC_SCU->SFSPE_1 = (MD_PLN_FAST | FUNC3); // A19 |
88 | LPC_SCU->SFSPE_2 = (MD_PLN_FAST | FUNC3); // A20 |
89 | LPC_SCU->SFSPE_3 = (MD_PLN_FAST | FUNC3); // A21 |
90 | LPC_SCU->SFSPE_4 = (MD_PLN_FAST | FUNC3); // A22 |
91 | LPC_SCU->SFSPA_4 = (MD_PLN_FAST | FUNC3); // A23*/ |
92 | |
93 | LPC_SCU->SFSP1_3 = (MD_PLN_FAST | FUNC3); // OE |
94 | LPC_SCU->SFSP1_6 = (MD_PLN_FAST | FUNC3); //WE |
95 | LPC_SCU->SFSP1_4 = (MD_PLN_FAST | FUNC3); // BLS0 |
96 | LPC_SCU->SFSP6_6 = (MD_PLN_FAST | FUNC1); // BLS1 |
97 | LPC_SCU->SFSPD_13 = (MD_PLN_FAST | FUNC2); // BLS2 |
98 | LPC_SCU->SFSPD_10 = (MD_PLN_FAST | FUNC2); // BLS3 |
99 | LPC_SCU->SFSP1_5 = (MD_PLN_FAST | FUNC3); // CS0 |
100 | LPC_SCU->SFSP6_3 = (MD_PLN_FAST | FUNC3); // CS1 |
101 | LPC_SCU->SFSPD_12 = (MD_PLN_FAST | FUNC2); // CS2 |
102 | LPC_SCU->SFSPD_11 = (MD_PLN_FAST | FUNC2); // CS3 |
103 | LPC_SCU->SFSP6_4 = (MD_PLN_FAST | FUNC3); //CAS |
104 | LPC_SCU->SFSP6_5 = (MD_PLN_FAST | FUNC3); //RAS |
105 | LPC_SCU->SFSCLK_0 = (MD_PLN_FAST | FUNC0); //CLK0 |
106 | LPC_SCU->SFSCLK_1 = (MD_PLN_FAST | FUNC0); //CLK1 |
107 | LPC_SCU->SFSCLK_2 = (MD_PLN_FAST | FUNC0); //CLK2 |
108 | LPC_SCU->SFSCLK_3 = (MD_PLN_FAST | FUNC0); //CLK3 |
109 | LPC_SCU->SFSP6_9 = (MD_PLN_FAST | FUNC3); //DYCS0 |
110 | LPC_SCU->SFSP6_1 = (MD_PLN_FAST | FUNC1); // DYCS1 |
111 | LPC_SCU->SFSPD_14 = (MD_PLN_FAST | FUNC2); // DYCS2 |
112 | LPC_SCU->SFSPE_14 = (MD_PLN_FAST | FUNC3); // DYCS3 |
113 | LPC_SCU->SFSP6_11 = (MD_PLN_FAST | FUNC3); //CKE0 |
114 | LPC_SCU->SFSP6_2 = (MD_PLN_FAST | FUNC1); // CKEOUT1 |
115 | LPC_SCU->SFSPD_1 = (MD_PLN_FAST | FUNC2); // CKEOUT2 |
116 | LPC_SCU->SFSPE_15 = (MD_PLN_FAST | FUNC3); // CKEOUT3 |
117 | LPC_SCU->SFSP6_12 = (MD_PLN_FAST | FUNC3); //DQMOUT0 |
118 | LPC_SCU->SFSP6_10 = (MD_PLN_FAST | FUNC3); //DQMOUT1 |
119 | LPC_SCU->SFSPD_0 = (MD_PLN_FAST | FUNC2); //DQMOUT2 |
120 | LPC_SCU->SFSPE_13 = (MD_PLN_FAST | FUNC3); //DQMOUT3 |
121 | //
|
122 | // Setup EMC config for SDRAM, timings for 72MHz bus => 13,9ns
|
123 | // SDRAM: MT48LC4M32B2 on Keil MCB1857 Board
|
124 | //
|
125 | LPC_EMC->DYNAMICCONFIG0 = 0x00004500; //128Mb, 4Mx32, 4 banks, 12 rows, 8 columns |
126 | |
127 | LPC_EMC->DYNAMICRP = 0x1; // tRP = n + 1 clock cycles # min. 18ns |
128 | LPC_EMC->DYNAMICRAS = 0x3; // tRAS = n + 1 clock cycles # min. 42ns |
129 | LPC_EMC->DYNAMICSREX = 0x5; // tSREX = tXSR = n + 1 clock cycles # min. 70ns |
130 | LPC_EMC->DYNAMICAPR = 0x1; // no tAPR => using tRCD = n + 1 clock cycles # min. 18ns |
131 | LPC_EMC->DYNAMICDAL = 0x5; // tDAL = n clock cycles # 5Cks = min. 30ns |
132 | LPC_EMC->DYNAMICWR = 0x1; // tWR = n + 1 clock cycles # min. 12ns |
133 | LPC_EMC->DYNAMICRC = 0x4; // tRC = n + 1 clock cycles # min. 60ns |
134 | LPC_EMC->DYNAMICRFC = 0x4; // tRFC = n + 1 clock cycles # min. 60ns |
135 | LPC_EMC->DYNAMICXSR = 0x4; // tXSR = n + 1 clock cycles # min. 60ns |
136 | LPC_EMC->DYNAMICRRD = 0x1; // tRRD = n + 1 clock cycles # min. 12ns |
137 | LPC_EMC->DYNAMICMRD = 0x1; // tMRD = n + 1 clock cycles # 2CKs = min. 12ns |
138 | //emc_WaitMS(200);
|
139 | LPC_EMC->DYNAMICCONTROL = 0x00000183; |
140 | emc_WaitMS(100); |
141 | LPC_EMC->DYNAMICCONTROL = 0x00000103; |
142 | LPC_EMC->DYNAMICREFRESH = 0x00000002; // n * 16 clock cycles |
143 | //for (i = 0; i < 0x80; i++); // Wait 128 AHB clock cycles
|
144 | emc_WaitMS(100); |
145 | LPC_EMC->DYNAMICREFRESH = 50; // n * 16 clock cycles (~ 1 pro MHz) |
146 | LPC_EMC->DYNAMICRASCAS0 = 0x00000202; |
147 | LPC_EMC->DYNAMICCONFIG0 = 0x00004500; |
148 | //emc_WaitUS(200);
|
149 | //
|
150 | // Init SDRAM
|
151 | //
|
152 | LPC_EMC->DYNAMICCONTROL = 0x00000083;// CLock Enable = driven HIGH, CLKOUT runs continously, Issue MODE command |
153 | Dummy = *((volatile U32*)(SDRAM_BASE_ADDR | (0x22 << (2+8+2)))); // 4 burst, 2 CAS latency |
154 | Dummy = Dummy; |
155 | //for (i = 0; i < 0x80; i++);
|
156 | LPC_EMC->DYNAMICCONTROL = 0x00000000;//NORMAL; // Issue NORMAL command |
157 | LPC_EMC->DYNAMICCONFIG0 = 0x00084500;//128Mb, 4Mx32, 4 banks, 12 rows, 8 columns, buffer enable |
158 | }
|
Ich will den SDRAM mit emWin nutzen. Rufe _EMC_Init() in der HWConf.c unter _low_level_init() auf. Wenn ich die .GUI_RAM section im RamLoc32 lasse, dann läuft es und ich bekomme etwas auf dem Display angezeigt. Will ich aber nun die section in den SDRAM verlinken, dann funktioniert es nicht mehr. Reicht es im Linker script aus, wenn ich den Eintrag wie folgt mache? (Ich nutze LPCXpresso IDE) in der ...mem.ld-Datei
1 | MEMORY
|
2 | {
|
3 | /* Define each memory region */
|
4 | MFlashA512 (rx) : ORIGIN = 0x1a000000, LENGTH = 0x80000 /* 512k */ |
5 | MFlashB512 (rx) : ORIGIN = 0x1b000000, LENGTH = 0x80000 /* 512k */ |
6 | RamLoc32 (rwx) : ORIGIN = 0x10000000, LENGTH = 0x8000 /* 32k */ |
7 | RamLoc40 (rwx) : ORIGIN = 0x10080000, LENGTH = 0xa000 /* 40k */ |
8 | RamAHB32 (rwx) : ORIGIN = 0x20000000, LENGTH = 0x8000 /* 32k */ |
9 | RamAHB16 (rwx) : ORIGIN = 0x20008000, LENGTH = 0x4000 /* 16k */ |
10 | RamAHB_ETB16 (rwx) : ORIGIN = 0x2000c000, LENGTH = 0x4000 /* 16k */ |
11 | SDRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x01000000 /* 16M */ |
12 | |
13 | }
|
14 | /* Define a symbol for the top of each memory region */
|
15 | __top_MFlashA512 = 0x1a000000 + 0x80000; |
16 | __top_MFlashB512 = 0x1b000000 + 0x80000; |
17 | __top_RamLoc32 = 0x10000000 + 0x8000; |
18 | __top_RamLoc40 = 0x10080000 + 0xa000; |
19 | __top_RamAHB32 = 0x20000000 + 0x8000; |
20 | __top_RamAHB16 = 0x20008000 + 0x4000; |
21 | __top_RamAHB_ETB16 = 0x2000c000 + 0x4000; |
22 | __top_SDRAM = 0x28000000 + 0x1000000; |
und in der .ld-Datei
1 | ...
|
2 | /* MAIN BSS SECTION */
|
3 | .bss : ALIGN(4) |
4 | {
|
5 | _bss = .; |
6 | *(.bss*) |
7 | *(COMMON) |
8 | . = ALIGN(4) ; |
9 | _ebss = .; |
10 | PROVIDE(end = .); |
11 | } > RamLoc32 |
12 | |
13 | .GUI_RAM (NOLOAD): ALIGN(4) /* hier ist mein Eintrag */ |
14 | {
|
15 | *(.GUI_RAM) |
16 | } > SDRAM |
17 | |
18 | /* NOINIT section for RamLoc40 */
|
19 | .noinit_RAM2 (NOLOAD) : ALIGN(4) |
20 | {
|
21 | *(.noinit.$RAM2*) |
22 | *(.noinit.$RamLoc40*) |
23 | . = ALIGN(4) ; |
24 | } > RamLoc40 |
25 | /* NOINIT section for RamAHB32 */
|
26 | .noinit_RAM3 (NOLOAD) : ALIGN(4) |
27 | {
|
28 | *(.noinit.$RAM3*) |
29 | |
30 | ...
|
Habe ich irgendeine Einstellung oder Anpassung beim Portieren vergessen bzw. übersehen? Kann mir vielleicht jemand helfen oder bestätigen, dass die Initialisierungsfunktion bzw. der Aufruf im Linker-script in Ordnung sind? Gruß Martin
Zum Code kann ich nix sagen, aber Warteroutinen wie oben durchzuführen ist m.E. nach "bäääähhh". Warum nimmst Du nicht den M3 internen Cyclecounter?
Guten Morgen Arne, der Cyclecounter sagt mir noch gar nichts oder meinst du damit den Systick? Ich wollte die Warteroutine sowieso noch auf Systick umstricken. Das werd ich wahrscheinlich gleich mal machen. An irgendwas muss es ja liegen..
Nein.. nicht den Systick (wobei Du beim LPC18xx ja auch noch den RIT hättest). Ich meine den Cyclecounter, den jeder M3 im Kern haben sollte und der mit jedem ALU Clock inkrementiert wird (wie z.B. m.W. auch ab Pentium P5). Ist im M3 ein 32bit Counter. Ich schalte an 0xE000EDFC Bit 24 ein und an 0xE0001000 Bit 0 und Bit 12. Der Counterwert liegt an 0xE0001004. Zieh dir mal http://infocenter.arm.com/help/topic/com.arm.doc.ddi0337i/DDI0337I_cortexm3_r2p1_trm.pdf Dort Kap.8.2. Ich hab mir da ein kleines API herum gebastelt.
Hab mir jetzt mal den Cycle Counter angeschaut. Das klingt ja schon mal nicht schlecht. Wie gehe ich dann mit dem Counterwert um? Ich verwende einen 72MHz Systemtakt. Also muss der Counter bis 72 zählen, damit ich 1 µs erhalte. Jetzt kann ich ja ein Tap bei Bit 6 oder 10 festlegen. So wie ich das verstanden habe, zählt, sobald CYCCNT dieses Tap-Bit erreicht hat, der POSTCNT ein Bit runter. Der Anfangswert von POSTCNT wird mit POSTPRESET festgelegt. Wenn der Countdown (POSTCNT) == 0 ist, dann wird ein PC Sample Event ausgelöst. Damit kann ich aber meinen Zählwert von 72 nicht realisieren (nur mit einem gewissen Fehler)(da Tap-Bit 6 = 64 und Tap-Bit 10 = 1024 ist). Also kann ich mir doch das ganze so vorstellen, als ob das Tap-Bit für den Prescaler steht - Prescaler 64 oder 1024 !? Will ich 1 ms haben, dann muss der Counter bis 72000 zählen. Jetzt kann ich das Bit 6 als Tap-Bit wählen und den POSTPRESET mit 1125 vorladen. Das PC Sample Event gibt mir dann die Millisekunde an. Arne, wie hast du das gelöst?
Sicher, dass Du da nicht was verkomplizierst? 1. setze das TCRA Bit: Schreibe (1 << 12) in 0xE000EDFC 2. lass den Counter loslaufen: schreibe ((1u << 12u) | (1u << 0)) an 0xE0001000 3. lese für Zeitmessungen 0xE0001004 als uint32_t aus. Jetzt kannst Du Dir eine Wait Funktion basteln oder eine wieviele-Ticks-sind-seit-Zeitstempel-x-vergangen Funktion. Umrechnen von Ticks in µs und zurück kannst Du ja generisch machen, so dass das immer funktioniert, egal welcher Takt an der ALU anliegt.
Fehler vom Amt. Es muss heißen: 1. setze das TCRA Bit: Schreibe (1 << 24) in 0xE000EDFC Bau mal so eine Warteschleife von 1s Blinktakt:
1 | while(1) { |
2 | LED_ON(); |
3 | Wait(72000000); /* warte 72Mio clocks = 1s */ |
4 | LED_OFF(); |
5 | Wait(72000000); /* warte 72Mio clocks = 1s */ |
6 | }
|
Häng ein Oszi an die LED und schaue, ob die Flanken schön mit 1s kommen. Sollte klappen.
Hi Arne, ja das klappt auch super!! Danke für deine Tips. Den Timer zu realisieren ist ja wirklich nicht ganz so kompliziert, wenn man es mal gemacht hat. Für Werte im Millisekundenbereich bekomme ich auch relativ genaue Zeiten hin, aber im 100µs-Bereich und darunter macht sich der Fehler bemerkbar. Hier mal mein Code:
1 | #define CoreClock 72000000
|
2 | |
3 | /* ========== Debug Watchpoint Timer - DWT ============ */
|
4 | #define DWT_CTRL *((volatile unsigned long *) (0xE0001000))
|
5 | #define DWT_CYCCNT *((volatile unsigned long *) (0xE0001004))
|
6 | #define DWT_ENABLE *((volatile unsigned long *) (0xE000EDFC))
|
7 | |
8 | void emc_WaitUS(volatile uint32_t us) |
9 | {
|
10 | DWT_ENABLE |= (1 << 24); //enable DWT |
11 | DWT_CTRL = (1 << 12) | (1 << 0); //Bit 0: Enable CYCCNT |
12 | |
13 | DWT_CYCCNT = 0; |
14 | while (DWT_CYCCNT < ((CoreClock/1000000)*us)); |
15 | }
|
16 | |
17 | void emc_WaitMS(volatile uint32_t ms) |
18 | {
|
19 | DWT_ENABLE |= (1 << 24); //enable DWT |
20 | DWT_CTRL = (1 << 12) | (1 << 0); //Bit 0: Enable CYCCNT |
21 | |
22 | DWT_CYCCNT = 0; |
23 | while (DWT_CYCCNT < ((CoreClock/1000)*ms)); |
24 | }
|
Liegt der Fehler im µs-Bereich am DWT Timer oder eher an meinem Code (der WHILE-Schleife)? Ich werde morgen gleich mal den EMC mit dem Timer testen. Glaube aber, dass das nicht das eigentliche Problem ist. Muss ich eigentlich beim Einbinden des externen Speichers im Startup-Code etwas ändern oder reicht die Änderung im Linker-Script?
Bei den Warteschleifen kommt es ja i.d.R. nur darauf an eine Mindestzeit nicht zu unterschreiten. Dafür ist das Ding ganz nützlich.
Hi Arne, hab es jetzt hinbekommen, die _EMC_Init() wurde an der falschen Stelle aufgerufen. Hab jetzt die _low_level_init() in die startup-Datei integriert (wie im emWin_EA1788_BSP), sodass der EMC vor der main() initialisiert wird. Dann habe ich die Initialisierung noch mal fein abgestimmt und für die Wartezeiten den Cycle Counter verwendet. Jetzt gehts... :-) VIELEN DANK!!! :-)
Na dann kannst Du Dich ja jetzt an den RAM-Test wagen: http://www.barrgroup.com/Embedded-Systems/How-To/Memory-Test-Suite-C Dass EMC_Init() vor dem Kopieren des DATA Segmentes in das SDRAM und auch vor dem Ausnullen des BSS geschehen muss war schon klar, oder? Ansonsten hast Du das von mir eingangs erwähnte "Henne-Ei" Problem.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.