STM32F4: как в IAR использовать SDRAM Печать
Добавил(а) microsin   

Для использования SDRAM есть 2 способа - либо прямым обращением по абсолютному адресу, либо с помощью линкера (использование секций памяти).

[Инициализация контроллера SDRAM]

Чтобы работать с внешней динамической памятью, требуется её предварительная инициализация. Самый простой способ выполнить инициализацию - воспользоваться готовыми библиотеками, предоставленными в пакете STM32Cube для Вашего процессора и примерами их использования. Здесь я кратко рассмотрю инициализацию SDRAM для плат разработчика 32F429IDISCOVERY и STM32429I-EVAL.

С помощью функции BSP_SDRAM_Init (находится в модуле stm32f429i_discovery_sdram.c) можно инициализировать память SDRAM. После этого она становится доступна как для прямого обращения по адресам 0xD0000000 .. 0xD07FFFFF, так и с помощью вызова функций BSP_SDRAM_ReadData, BSP_SDRAM_ReadData_DMA, BSP_SDRAM_WriteData и т. д.

Все, что нужно - раскомментировать константу DATA_IN_ExtSDRAM (по умолчанию она закомментирована в модуле system_stm32f4xx.c). Альтернативно можно оставить её закомментированной, но тогда нужно обеспечить вызов функции BSP_SDRAM_Init (она находится в модуле stm324x9i_eval_sdram.c) перед любым обращением к данным в SDRAM.

[Обращение к SDRAM по абсолютному адресу]

Если у Вас предполагается использование SDRAM только для какой-то опеделенной цели - например как буфер экрана, или как хранилище для лога, то более простым может оказаться этот вариант. Просто инициализируйте указатель на абсолютный адрес в SDRAM, и обращайтесь к памяти по данному адресу через указатель. У разных вариантов подключения SDRAM к процессору может быть разный начальный адрес SDRAM в адресном пространстве микроконтроллера. Например, у плат 32F429IDISCOVERY память SDRAM начинается с адреса 0xD0000000, а у STM32429I-EVAL с адреса 0xC0000000. Пример использования SDRAM под буфер сообщения CAN:

#define SDRAM_BEGIN 0xD0000000
 
TCANmsg *pCANbufTX = (TCANmsg*)SDRAM_BEGIN;
pCANbufRX->DLC = 0x200;
... 

Все примеры из STM32Cube для STM32429I-EVAL, использующие SDRAM под буфер кадра для индикатора LCD, используют как раз этот способ обращения к SDRAM:

#define LCD_FB_START_ADDRESS ((uint32_t)0xC0000000)
 
static void LCDinit (void)
{
   BSP_LCD_InitEx(LCD_MAX_PCLK);
   BSP_LCD_LayerDefaultInit(1, LCD_FB_START_ADDRESS);
   BSP_LCD_SelectLayer(1);
   ...

Используемую таким образом память линкер не видит, и за её распределение отвечаете только Вы, с помощью определения символических констант и вычисления вручную используемого размера областей памяти.

[Использование линкера]

Этот способ немного сложнее, но он удобнее тем, что позволяет размещать разные переменные в SDRAM во время их определения, и после этого использовать эти переменные в коде, как обычно. Процесс по шагам:

1. Сначала нужно определить секцию памяти, которая соответствует области адресного пространства SDRAM. Для этого нужно знать физические адреса SDRAM, и создать её описание в файле конфигурации линкера (файл с расширением *.icf, который используется в конфигурации проекта). Пример определения секции памяти, относящейся к SDRAM платы разработчика STM32429I-EVAL:

define symbol __region_SDRAM_start__ = 0xC0000000;
define symbol __region_SDRAM_end__   = 0xC1FFFFFF;
define region SDRAM_region = mem:[from __region_SDRAM_start__ to __region_SDRAM_end__];
place in SDRAM_region   { section SDRAM_DATA };

Добавленные участки конфигурации линкера выделены синим шрифтом.

/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x08000000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__    = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__      = 0x081FFFFF;
define symbol __ICFEDIT_region_RAM_start__    = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__      = 0x2002FFFF;
define symbol __ICFEDIT_region_CCMRAM_start__ = 0x10000000;
define symbol __ICFEDIT_region_CCMRAM_end__   = 0x1000FFFF;
define symbol __ICFEDIT_region_SDRAM_start__ = 0xC0000000;
define symbol __ICFEDIT_region_SDRAM_end__   = 0xC1FFFFFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x400;
define symbol __ICFEDIT_size_heap__   = 0x200;
/**** End of ICF editor section. ###ICF###*/
 
define memory mem with size = 4G;
define region ROM_region      = mem:[from __ICFEDIT_region_ROM_start__
   to __ICFEDIT_region_ROM_end__];
define region RAM_region      = mem:[from __ICFEDIT_region_RAM_start__
   to __ICFEDIT_region_RAM_end__];
define region SDRAM_region    = mem:[from __ICFEDIT_region_SDRAM_start__
   to __ICFEDIT_region_SDRAM_end__];
define region CCMRAM_region   = mem:[from __ICFEDIT_region_CCMRAM_start__
   to __ICFEDIT_region_CCMRAM_end__];
 
define block CSTACK    with alignment = 8, size = __ICFEDIT_size_cstack__   { };
define block HEAP      with alignment = 8, size = __ICFEDIT_size_heap__     { };
 
initialize by copy { readwrite };
do not initialize  { section .noinit };
 
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
place in ROM_region   { readonly };
place in RAM_region   { readwrite,
                        block CSTACK, block HEAP };
place in SDRAM_region   { section SDRAM_DATA };

2. Чтобы разместить любой объект (переменную, структуру, массив) в памяти SDRAM, определяйте его с помощью специального символа @, указав имя секции, соответствующей SDRAM. Пример:

__no_init uint32_t FrameBuffer [480*272] @ "SDRAM_DATA";
__no_init TCANmsg canbufrx[CANBUF_SIZE] @ "SDRAM_DATA";

Атрибут __no_init переменной следует использовать для того, чтобы низкоуровневый код запуска (startup code), который находится в модуле ассемблера (файл наподобие startup_stm32f429xx.s, зависит от используемого микроконтроллера) не пытался инициализировать нулями эти переменные до того, как будет инициализирован контроллер SDRAM. Альтернативно можно вызвать из кода запуска функцию инициализации SDRAM. Как это делается, можно подсмотреть в тех же примерах STM32Cube. Например, существует константа DATA_IN_ExtSDRAM (по умолчанию она закомментирована в модуле system_stm32f4xx.c) - если она определена, то из кода ассемблера startup code будет вызвана функция SystemInit_ExtMemCtl, которая отвечает за инициализацию контроллера SDRAM.

На этом все. Размещением определенных через использование секций памяти переменных теперь будет управлять линкер, и это можно увидеть в MAP-файле проекта:

*******************************************************************************
*** PLACEMENT SUMMARY
***
"A0":  place at 0x800'0000 { ro section .intvec };
"P1":  place in [from 0x800'0000 to 0x81f'ffff] { ro };
define block CSTACK with size = 1K, alignment = 8 { };
define block HEAP with size = 512, alignment = 8 { };
"P2":  place in [from 0x2000'0000 to 0x2002'ffff] {
          rw, block CSTACK, block HEAP };
"P3":  place in [from 0xc000'0000 to 0xc1ff'ffff] { section SDRAM_DATA };
initialize by copy { rw };
...

"P3": 0x87800 SDRAM_DATA uninit 0xc000'0000 0x7f800 vars.o [1] SDRAM_DATA uninit 0xc007'f800 0x8000 canapp.o [1] - 0xc008'7800 0x87800
...
Unused ranges: From To Size ---- -- ---- 0x801'ee6f 0x81f'ffff 0x1e'1191 0x2000'0193 0x2000'0193 0x1 0x2001'1734 0x2001'1737 0x4 0x2001'1b38 0x2002'ffff 0x1'e4c8 0xc008'7800 0xc1ff'ffff 0x1f7'8800 ...

Font24_Table 0x801'7738 0x1ab8 Data Gb stm324x9i_eval_lcd.o [1]
FrameBuffer             0xc000'0000  0x7f800  Data  Gb  vars.o [1]
FreeRTOSerrorHandler     0x801'1f5f      0x2  Code  Gb  errors.o [1]
...

canTask 0x801'0ec3 0xd6 Code Lc main.o [1]
canbufrx                0xc007'f800   0x8000  Data  Gb  canapp.o [1]
clean_terminal_line      0x801'31b9     0x5a  Code  Lc  USARTconsole.o [1]
...

Существуют и другие способы размещения переменных и кода функций в определенных секциях памяти (например, директивами #pragma section = "имя_секции" [выравнивание], #pragma location=адрес), подробнее см. [1, 2, 3].

[Ссылки]

1. IAR C/C++ Development Guide site:iar.com.
2. Technical Note 27498 Placing a group of functions or variables in a specific section site:iar.com.
3. Technical Note 43262 Absolute located variable site:iar.com.
4. IAR 4.0: размещение переменных и данных по заданному адресу.