IAR: создание загрузчика для Cortex-M |
![]() |
Добавил(а) microsin | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
В этом документе (перевод TN160822 [1]) приведено руководство по созданию загрузчика в среде IAR Embedded Workbench for ARM. Многие рекомендации здесь общие, хотя проект примера основан на микропроцессоре Cortex-M (STM32L152VB компании STMicroelectronics). При создании загрузчика следует учитывать некоторые аспекты, связанные с настройкой проекта и передачей управления из загрузчика в приложение. См. проект примера для STM32L152VB и выполните следующие действия. Имейте в виду, что это руководство не описывает, как выполняется действительное обновление кода (IAP – in application programming, самопрограммирование) приложения, или как конфигурировать коммуникационные интерфейсы, хотя это общая функция в загрузчиках. Основные шаги по созданию загрузчика следующие: • Создайте 2 отдельных проекта – один для загрузчика, и еще один для приложения. Эти 2 проекта могут быть размещены в одном рабочем пространстве. С отдельными проектами Вы гарантируете, что код и библиотечные функции не будут совместно использоваться загрузчиком и приложением. • Проверьте размер блока flash-памяти (самый маленький стираемый элемент flash). Деление между загрузчиком и приложением должно происходить на границе блока flash, иначе есть риск стирания частей приложения или загрузчика, когда вы перепрограммируете кого-то из них. • Любая инициализация железа обычно должна быть выполнена только один раз, чаще всего в коде загрузчика. Убедитесь, что приложение не делает повторную инициализацию железа, если это не требуется. • Перед тем, как загрузчик передаст управление в приложение, убедитесь в следующем: 1. Прерывания запрещены – чтобы избежать неожиданных прерываний во время старта приложения. __disable_interrupt(); // 1. Запрет прерываний. __set_SP(vector_p->stack_addr); // 2. Настройка указателя стека. SCB->VTOR = (uint32_t)&app_vector; // 3. Конфигурирование VTOR. vector_p->func_p(); // 4. Переход в приложение. [Особенности отладки] Отладка загрузчика и приложения одновременно возможна, однако процедура может быть сложной, например потому что у них обоих есть функция main. Чтобы отладчик знал о наличии двух приложений, выберите в свойствах проекта Debugger -> Images, чтобы загрузить информацию из выходного ELF-файла другого проекта. Убедитесь, что опция Run to main запрещена на закладке Debugger -> Setup, чтобы избежать двусмысленности, какая из функций main должна запускаться (функция main загрузчика или функция main в приложении). После загрузки кода отладчик по умолчанию начинает выполнение по адресу, заданному символом __vector_table. Когда Вы отлаживаете загрузчик и приложение одновременно, может появиться следующее сообщение: Чтобы направить отладчик на корректный символ __vector_table, Вы должны явно указать, где находится таблица векторов загрузчика. Выберите на закладке Debugger -> Extra Options: --drv_vector_table_base=0x08000000 Когда отлаживаете код, используйте окно View -> Images, чтобы выбрать приложение в области действия и избежать проблем с конфликтами имен в двух приложениях. При пошаговом переходе в режиме отладки от загрузчика в приложение помните, что нужно поменять текущую загруженную отладочную информацию (в окне Images). [Запись Flash] Чтобы загрузить приложение и загрузчик в память Flash, есть несколько вариантов. Из-за того, что загрузчик и приложение находятся в разных проектах, они могут быть загружены индивидуально из каждого проекта. Альтернативно Вы можете загрузить их оба в память Flash одновременно, например используя линкер для подключения двоичного кода приложения в проекте загрузчика (или наоборот). Для этого выберите в свойствах проекта закладку Linker -> Input. В файле конфигурации линкера двоичный образ приложения помещается в память Flash следующей строкой: place at address mem: app_vector { readonly section .APP }; [Пример загрузчика] Готовый пример загрузчика, который загружает образ программы пользователя через сеть Ethernet по протоколу TFTP или HTTP, можно найти в проекте LwIP_IAP из пакета библиотек STM32Cube (см. папку Projects\STM324x9I_EVAL\Applications\LwIP). Идея загрузчика состоит в том, что загрузчик находится в младших адресах Flash (с адреса 0x08000000), и он первым получает управление при включении питания/сбросе. Программа пользователя находится дальше в памяти, в других секторах Flash (например с адреса 0x08020000). Итак, при включении питания всегда запускается загрузчик. В этот момент код принимает решение - должен ли оставаться активным загрузчик (чтобы иметь возможность переписать основную программу), или же нужно передать управление в основную программу. Обычно это делается по так называемому условию загрузки - например, нажата ли при включении питания какая-нибудь кнопка. [Как изменить адрес старта загружаемого приложения] Чтобы и загрузчик, и программа пользователя находились в памяти Flash одновременно, их необходимо разнести в памяти по отдельным секторам Flash. В примере, приведенном здесь, загрузчик находится начиная с сектора 0 и может занимать секторы 0, 1, 2, 3 и 4 (адресное пространство 0x08000000 - 0x0801FFFF, размер 128 кбайт). Вся остальная память Flash, начиная с сектора 5 (сектора 5 .. 23, адресное пространство 0x08020000 - 0x081FFFFF, размер 1966080 байт), может быть использована под код пользователя. Таблица 6. Модуль Flash - организация 2 мегабайта dual bank organization (STM32F42xxx и STM32F43xxx).
Чтобы приложение пользователя могло находиться начиная с адреса 0x08020000, необходимо внести некоторые изменение в проект приложения. Процесс по шагам: 1. Откройте в текстовом редакторе файл конфигурации линкера *.icf, и внесите в него следующие изменения (помечено красным цветом): /*###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__ = 0x08020000; /*-Memory Regions-*/ define symbol __ICFEDIT_region_ROM_start__ = 0x08020000; 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. Откройте в редакторе файл system_stm32f4xx.c, и внесите в функцию SystemInit следующие изменения (помечено красным цветом): /** * @brief Setup the microcontroller system * Initialize the FPU setting, vector table location and External memory
* configuration. * @param None
* @retval None */ void SystemInit(void) { /* FPU settings ------------------------------------------------------------*/ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ #endif /* Reset the RCC clock configuration to the default reset state ------------*/ /* Set HSION bit */ RCC->CR |= (uint32_t)0x00000001; /* Reset CFGR register */ RCC->CFGR = 0x00000000; /* Reset HSEON, CSSON and PLLON bits */ RCC->CR &= (uint32_t)0xFEF6FFFF; /* Reset PLLCFGR register */ RCC->PLLCFGR = 0x24003010; /* Reset HSEBYP bit */ RCC->CR &= (uint32_t)0xFFFBFFFF; /* Disable all interrupts */ RCC->CIR = 0x00000000; #if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */ /* Configure the Vector Table location add offset address ------------------*/ #ifdef VECT_TAB_SRAM SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ #else //SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ SCB->VTOR = FLASH_BASE | 0x00020000 | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ #endif
}
[Ссылки] 1. Technical Note 160822 Creating a bootloader for Cortex-M site:iar.com. |