Forum: Mikrocontroller und Digitale Elektronik STM32F429 Disco Board: SDRAM benutzen


von Christian J. (Gast)


Lesenswert?

Hallo,

könnte mir jemand mal in einfachen Worten und mit Beispiel erklären wie 
ich das 8MB SDRAM des Disco Board vom F429 benutzen kann? Ist das 
irgendwie "banked" oder als Ganzes am Stück ab 0xD0000000? Wie 
initialisiert man es und sagt der CPU wie sie es zu verwalten hat?

Konkret möchte ich folgendes realisieren mit der CooCox IDE, nur weiss 
ich noch nicht wie. Das reicht bei mir dann auch für alle Anwendungen.


1. Den Heap auf das SDRAM legen, da ich oft mit heap arbeite.

2. Wie lege ich einen normalen Array über das SDRAM?

3. Bonus: Kann ich das Datensegment meines Codes da drauf legen, so dass 
er keinen internal RAM mehr benutzt? (Muss sicher im Linker irgendwo 
eingestellt werden).

4. Kann man Variablen irgendwie als "xdata" defklarieren, wie zb bei den 
MCS51er 8 Bit CPU, die idata und xdata beim Keil Compiler kennen?

Eine anfängergerechte Antwort mit Beispiel für CooCox User wäre sehr 
nett, danke!

Christian

von Oliver J. (skriptkiddy)


Lesenswert?

Christian J. schrieb:
> Wie lege ich einen normalen Array über das SDRAM?

Das geht übers Linker-Script. Der RAM muss dafür aber inititalisert 
sein.

Grüße Oliver

von hp-freund (Gast)


Lesenswert?


von Christian J. (Gast)


Lesenswert?

Es finden sich schon in CMSIS_Boot Init Routinen für den Memory 
Controller, ob man noch mehr als die braucht weiss ich nicht.


#ifdef DATA_IN_ExtSRAM
/**
  * @brief  Setup the external memory controller.
  *         Called in startup_stm32f4xx.s before jump to main.
  *         This function configures the external SRAM mounted on 
STM324xG_EVAL/STM324x7I boards
  *         This SRAM will be used as program data memory (including 
heap and stack).
  * @param  None
  * @retval None
  */

void SystemInit_ExtMemCtl(void)


Ich weiss nur nicht wo ich diese defines aktiviere, im eigenen Source 
Code?. Wie gesagt... für Anfänger..... der nichts weiter vor sich hat 
als

void main()
{
     InitSystem();

      ......


}

und gern da Code einfügen möchte, um das Gesagte zu machen.

von Christian J. (Gast)


Lesenswert?

Mein Wissen ist einige Jahre alt aber beim LPC2368 mit GCC
konnte ich sowas verwenden:

uint32_t batram_id _attribute_ ((section(".batram")));

d.h. "batram_id" wurde aufs Segment .batram abgebildet.

oder

// Der Zeichenpuffer das Display hat 40 x 20 Zeichen = 800 Zeichen
uint8_t GD_CharBuf[MAX_LINE][MAX_LPOS+1]  _attribute_ 
((section(".usbram")));
uint8_t GD_OldBuf[MAX_LINE][MAX_LPOS+1]  _attribute_ 
((section(".usbram")));

das eben aufs ".usbram" weil der uC unterschiedliche RAM Bereiche hatte, 
einen gepufferten, einen für USB usw. Alles mit dem GCC gemacht worden.

von Lasse S. (cowz) Benutzerseite


Lesenswert?

Spricht ja auch nichts dagegen, dieses Wissen heute anzuwenden, oder? 
Die sections musst du im Linkerscript natürlich angeben - und aufpassen, 
dass der uC nicht versucht vor der FSMC-Initialisierung darauf 
zuzugreifen (also den Startup-Code nochmal anschauen)

von Christian J. (Gast)


Lesenswert?

Lasse S. schrieb:

> Die sections musst du im Linkerscript natürlich angeben

Duuu... wo finde ich das Ding in einem "automatisierten" CooCox Projekt? 
wo man sich die Konfig ja zusammen klickt .... seufz

von Christian J. (Gast)


Lesenswert?

Ok, habs gefunden, ich meine nicht das Skript aber die Lösung von Uwe 
Becker:


   .HeapMemSection :
    {
        *(.HeapMemSection)
    } > sdram


Leider wenig Ahnug von der seltsamen Sytax aber da müsste sich doch auch 
noch ein weiteres Segment, zb .data reinquetschen lassen, ohne dass die 
kollidieren. Hier ist der Heap jetzt dorthin bugsiert worden.

Rest kompiliert und spielt wie vermutet.....

#define  BUFFER_SIZE  (1024*1024)
uint8_t buffer[BUFFER_SIZE]  attribute__((section(".HeapMemSection")));

/* 18.12.2013 angepasst von UB auf STM32f429i-Disco-Board */


OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
/* Internal Memory Map*/
MEMORY
{
  rom (rx)    : ORIGIN = 0x08000000, LENGTH = 0x00200000
  ram (rwx)   : ORIGIN = 0x20000000, LENGTH = 0x00030000
  ram1 (rwx)  : ORIGIN = 0x10000000, LENGTH = 0x00010000
  sdram (rwx) : ORIGIN = 0xD0100000, LENGTH = 0x00400000
}

_eram = 0x20000000 + 0x00020000;
/* Section Definitions */
SECTIONS
{
    .text :
    {
        KEEP(*(.isr_vector .isr_vector.*))
        *(.text .text.* .gnu.linkonce.t.*)
        *(.glue_7t) *(.glue_7)
        *(.rodata .rodata* .gnu.linkonce.r.*)
    } > rom

    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > rom

    __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > rom
    __exidx_end = .;

    . = ALIGN(4);
    _etext = .;
    _sidata = .;

    .data : AT (_etext)
    {
        _sdata = .;
        *(.data .data.*)
        . = ALIGN(4);
        _edata = . ;
    } > ram

    /* .bss section which is used for uninitialized data */
    .bss (NOLOAD) :
    {
        _sbss = . ;
        *(.bss .bss.*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = . ;
    } > ram

    /* stack section */
    .co_stack (NOLOAD):
    {
        . = ALIGN(8);
        *(.co_stack .co_stack.*)
    } > ram

    . = ALIGN(4);
    _end = . ;

    .HeapMemSection :
    {
        *(.HeapMemSection)
    } > sdram
}

[/code]

von Christian J. (Gast)


Lesenswert?

Hallo,

Ich schiebs nochmal hoch. Dass es kompilierte heisst ja noch nichts.

Ist es überhaupt möglich das SDRAM in einer Section zu verwenden? Denn 
diese Zuweisung spielt sich bereits beim Kompilieren ab, bevor das SDRAM 
überhaupt da ist, denn dieses wird erst im Hauptprogramm durch den 
Memory Controller in seiner Geometrie initialisiert und mit einem Takt 
versorgt. Nun haben Compiler aber auch Initsequenzen, wo sie statische 
Variablen aus dem ROM ins Ram kopieren im Startup Code usw.

Jedenfalls ist es bei mir nicht möglich sich den Inhalt des SDRAM im 
Debugger anzeigen zu lassen, da steht nur wirres Zeugs drin. 
Einschrieben von Vars geht aber und sie werden auch korrekt gelesen.

Wie steht es aber nun zb mit der verwendung als Stack? Oder Heap?

Gruss,
Christian

von Jens (Gast)


Lesenswert?

Christian J. schrieb:
> Wie steht es aber nun zb mit der verwendung als Stack? Oder Heap?
Was sollte der Verwendung entgegen stehen?
Jeder PC arbeitet mit dynamischen Speicher.

Du mußt nur sicherstellen, das der Speicher vor Verwendung richitg 
initialisiert wird. Das da an Anfang wirres Zeug drin steht, ist völlig 
normal. Nicht umsonst wir bei C die .bss-Sektion erstmal mit '0' 
gefüllt.

Jens

von Christian J. (Gast)


Lesenswert?

Jens schrieb:

> Was sollte der Verwendung entgegen stehen?
> Jeder PC arbeitet mit dynamischen Speicher.

Tja, nur haben Compiler sowas wie ein _INIT_GLOBALS Section, die sehe 
ich deutlich im Startupcode wo Variablen im Ram aus einer Tabelle im Rom 
geladen werden. Alles was als static markiert wurde und einen Startwert 
hat wird da geladen. Und zu dem Zeitpunkt gibt es noch kein SDRAM, d.h. 
die Adressen zeigen ins Leere, was vermutlich eine CPU Exception 
auslösen wird. Mich hat ja schon stutzig gemacht dass ich den SDRAM 
Berich mit dem Debugger nicht sehen kann. Die eingeschrieben Werte aus 
dem programm sind nicht zu finden.

von Jens (Gast)


Lesenswert?

Christian J. schrieb:
> Tja, nur haben Compiler sowas wie ein _INIT_GLOBALS Section, die sehe
> ich deutlich im Startupcode wo Variablen im Ram aus einer Tabelle im Rom
> geladen werden. Alles was als static markiert wurde und einen Startwert
> hat wird da geladen.
Alles richtig. Deshalb muß die SDRAM-Initialisierung mit in den 
Startup-Code und zwar logischerweise vor dem Kopieren der Variablen.

Jens

von Christian J. (Gast)


Lesenswert?

Jens schrieb:

> Alles richtig. Deshalb muß die SDRAM-Initialisierung mit in den
> Startup-Code und zwar logischerweise vor dem Kopieren der Variablen.

Schonmal gesehen wie der Memory Controller in C behandelt wird? 10.000 
kryptische Einstellungen? Ich werde den nicht ernsthaft in ARM Assembler 
schreiben, da ich davon keine Ahnung habe (und nicht haben will).

Aber scheinbar ist das der einzige Weg .... oder man verwendet die 
Section wirklich nur nach dem Init() lokal und nicht als globale 
Variable. Ich werde mal ausprobieren wie es mit dem Heap ausschaut ob 
man den dorthin bugsieren kann.

Danke!

von Uwe B. (derexponent)


Lesenswert?

die Initialisierung muss nicht in Assembler gemacht werden.

von Clive gibt es einen angepassten Startupcode der mit dem Define
"DATA_IN_ExtSDRAM" aktiviert werden sollte

schau mal hier :

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex%5fmx%5fstm32%2fstm32f429%20and%20sdram&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=375

Zitat :
1
This function configures the external SDRAM mounted on STM32F429I-DISCO board
2
  *         This SDRAM will be used as program data memory (including heap and stack).



Gruss Uwe

: Bearbeitet durch User
von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Hi Uwe,

nach ein wenig rumbasteln ist jetzt klar, dass es funktioniert, wenn man 
Sections definiert und in Main dann den Init setzt. Das SDRAM ist nur 
laaaaaangsam, 8Mb vollzukritzeln dauert schon ein paar Sekunden. Aber 
was macht das schon wenn man volle 8MB an einen uC hat! Das ist wie 
Weihnachten.
Wenn ich so dran denke, dass ich vor 4 Wochen noch von Nix ne Ahnung 
hatte bei dem Board :-) und jetzt steht schon eine UART mit Terminal für 
Ausgabe, ein Ringpuffer, ein fertiges Template für EmBlocks....

Emblocks ist echt klasse, komme ich 10mal besser mit klar als mit 
CooCoxKrampf.  Für das das Debuggen im RAM zur Schonung des Flash auch 
ein Setup gebaut und nur noch 1 Mausklick dafür.

Ich habe die Libs von Majerle genommen (bitte nicht böse sein :-), der 
füllt auch nur die Structs und ruft Init dann auf und die wiederum rufen 
fmc.c auf.
1
/* 1 clock cycle = 1 / 90MHz = 11.1ns */
2
  FMC_SDRAMTimingInitDef.FMC_LoadToActiveDelay    = 2;
3
  /* TXSR: min=70ns (7x11.10ns) */
4
  FMC_SDRAMTimingInitDef.FMC_ExitSelfRefreshDelay = 7;
5
  /* TRAS: min=42ns (4x11.10ns) max=120k (ns) */
6
  FMC_SDRAMTimingInitDef.FMC_SelfRefreshTime      = 4;
7
  /* TRC:  min=70 (7x11.10ns) */
8
  FMC_SDRAMTimingInitDef.FMC_RowCycleDelay        = 7;
9
  /* TWR:  min=1+ 7ns (1+1x11.10ns) */
10
  FMC_SDRAMTimingInitDef.FMC_WriteRecoveryTime    = 2;
11
  /* TRP:  20ns => 2x11.10ns */
12
  FMC_SDRAMTimingInitDef.FMC_RPDelay              = 2;
13
  /* TRCD: 20ns => 2x11.10ns */
14
  FMC_SDRAMTimingInitDef.FMC_RCDDelay             = 2;
15
16
* Send the command */
17
  FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
18
19
  /* Set the refresh rate counter */
20
  /* (7.81 us x Freq) - 20 = (7.81 * 90MHz) - 20 = 683 */
21
  /* Set the device refresh counter */
22
  FMC_SetRefreshCount(680);

Ob man da noch was opmieren kann weiss ich nicht. Die Werte sind 
sicherlich auch irgendwo abgeschrieben.

Linker Script:

Ganz sicher bin ich mir hier nicht, das ist nur intuitiv, weil anderes 
auch so gemacht wurde. Kenne die Syntax eines Linker Scripts leider 
nicht.


MEMORY
{
    ROM  (rx)   : ORIGIN = 0x08000000, LENGTH = 0x00200000
    CCRAM (rwx) : ORIGIN = 0x10000000, LENGTH = 0x00010000 /* 16kb */
    RAM (rwx)   : ORIGIN = 0x20000000, LENGTH = 0x00030000 /* 192kb */
    SDRAM (rwx) : ORIGIN = 0xD0100000, LENGTH = 0x00400000 /* 8 MB */
}

später:

    /* 8MB SDRAM Chip */
    .sdram (NOLOAD):
    {
        . = ALIGN(4);
        *(.sdram)
    } > SDRAM

von Uwe B. (derexponent)


Lesenswert?

1
SDRAM (rwx) : ORIGIN = 0xD0100000, LENGTH = 0x00400000 /* 8 MB */

da passt der Text zum define nicht
0x400000 sind nur 4MB

falls du das von mir kopiert hast (wovon ich ausgehe)
hier eine Erklärung :

das SDRAM beginnt bei Adresse 0xD0000000 und hat 8MB
falls du also das komplette SDRAM nutzen willst,
"müsste" der Eintrag im Linker-Script so lauten :
1
SDRAM (rwx) : ORIGIN = 0xD0000000, LENGTH = 0x00800000 /* 8 MB */

Vorsicht !!
das kolidiert dann aber mit dem LCD-Display auf dem STM32F429-Disco

das Display nutzt als Grafik-RAM auch das SD-RAM
ab der Startadresse 0xD0000000

ich habe den Bereich "SDRAM" im Linker-Script nur hinzugefügt
um einen Teil vom SD-RAM (4MB) für Variabeln Nutzen zu können
(zusätzlich zum Display)

für das Display habe ich 1MB reserviert darum beginnt
die Startadresse im Linker-Script für die Variabeln
bei 0xD0100000

ist etwas konfus aber ich musste meine Librarys abwärtskompatibel halten


du kannst ja bei dir die 8MB aufteilen in :
1MB für das Display und
7MB für Variabeln (Stack/Heap)

Gruss Uwe

: Bearbeitet durch User
von Uwe B. (derexponent)


Lesenswert?

Christian J. schrieb:
> Das SDRAM ist nur
> laaaaaangsam, 8Mb vollzukritzeln dauert schon ein paar Sekunden.

dann benutz mal den DMA2D zum kopieren von Daten und nicht die CPU
da wirst du staunen wie schnell 8MB gefüllt sind :-)

Gruss Uwe

: Bearbeitet durch User
von Stephan G. (stephan_g35)


Lesenswert?

Hier eine Lib:

http://mikrocontroller.bplaced.net/wordpress/?page_id=2747

Vielleicht kannst du da noch ein paar Infos rausziehen...

: Bearbeitet durch User
von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Hi Uwe,

habs schon bemerkt, ja das habe ich rauskorpiet. Ich benutze deine Libs 
ja noch nicht, daher brauche ich das mit dem Display derzeit auch nicht. 
Man muss sich wohl entscheiden zwischen Deinen und denen von Majerle, 
sind beide sehr ähnlich.

Übrigens seltsame Kiste mit dem Startup Code. Der von Emblocks war nicht 
release fähig... Ich habe deinen reinkopiert ohne nachzudenken und er 
spielt. Ist aber eben ein C File und kein .s File wie vorher. Das 
eröffnet natürlich auch neue Möglichkeiten, wie Speicher Init usw. Die 
Init des SDRAM werde ich da auch reinnehmen. Nutze derzeit die von 
Majerle. Routinen sind die gleichen wie bei dir.

Stellt sich allerdings die Frage wie man die Routine von clive1 
einbindet. Sie enthält keinerlei inlcude Files und ist so nicht 
kompilerbar.

Habe sie beide mal angehängt, vielleicht kannst du ja eine draus machen, 
die fehlerfrei durchkompiliert.

Gruss,
Christian

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Christian J. schrieb:
> könnte mir jemand mal in einfachen Worten und mit Beispiel erklären wie..

Nö.
Mit einfachen Worten ist das Ganze nicht erreichbar. Du mußt dich schon 
selber mit der Materie befassen, sonst wird da nix. Zuerst, wie der 
externe Busanschluß ei deinem µC konkret aufgebaut ist und wie er 
eingerichtet wird. Dann, was für ein SDRAM tatsächlich dranhängt und wie 
dieser RAM zu initialisieren ist, also vor allem Organisation und 
Blockgrößen.

Ich hänge dir mal ein Beispiel dran. Das ist zwar für einen 32 Bit 
breiten SDRAM und für einen LPC2478, aber das Wesentliche kannst du dir 
dort abgucken.

W.S.

von Uwe B. (derexponent)


Lesenswert?

>Stellt sich allerdings die Frage wie man die Routine von clive1
>einbindet. Sie enthält keinerlei inlcude Files und ist so nicht
>kompilerbar.

füge einfach den Teil von Clive anstelle vom original ST-Code
in das File "system_stm32f4xx.c" hinzu

(da gibt es die gleiche Funktion, die aber fehlerhaft bzw. nicht passend
für das Disco-Board ist)

also die komplette Funktion "void SystemInit_ExtMemCtl(void)"
durch die von Clive ersetzen

ich habe es selbst nicht ausprobiert aber falls
includes fehlen kann es eigentlich nur die vom fmc und gpio sein

#include "stm32f4xx_fmc.h"
#include "stm32f4xx_gpio.h"

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Uwe B. schrieb:

> also die komplette Funktion "void SystemInit_ExtMemCtl(void)"
> durch die von Clive ersetzen

Hi,

Habs mir grad mal von zu hause aufs Handy geladen. Fragt sich nur ob 
diese Funktion explizit aufgerufen werden muss, zb aus dem Startup Code 
oder ob sie durch geheime Mechanismen schon aufgerufen wird.

Was ich etwas vermisse ist ein ausführliches Map File. In dem vom GCC 
steht viel drin aber das vom SDCC ist deutlich übersichtlicher.

Ich melde es später mal nach dem Sport was draus geworden ist.....

von Christian J. (Gast)


Lesenswert?

PS:

Es funktioniert nicht mit der clive1 Routine. Durchlaufen wird alles 
aber beim ersten Zugriff auf das SDRAM klebt er im Exception Handler 
drin.... erst der ausdrückliche Aufruf von TM_Init_SDRAM() lässt es 
laufen.

edit 10 Minuten später: geht doch, Fehler beim Copy & Paste.

Puh, na, also....

PS: Wenn es mal regnet und Du Langeweile hast, schreib deinen Pac Man 
doch mal auf Autoplay um (aber nicht schummeln) oder so, dass die 
Geister einen sehen müssen damit sie die Spur aufnehmen können. So wie 
in einem echten Labyrynath auch.

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.