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 }; ... FrameBuffer 0xc000'0000 0x7f800 Data Gb vars.o [1] FreeRTOSerrorHandler 0x801'1f5f 0x2 Code Gb errors.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. |