Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2754
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Процесс начальной загрузки

583

Однако мы должны проявлять особую осторожность при переходе в системную память. Фактически, загрузчик предназначен для вызова сразу после сброса и предполагает, что ЦПУ и его периферийные устройства установлены в исходное состояние по умолчанию. Лучшее решение может быть достигнуто путем сохранения специального кода в памяти SRAM и последующего принудительного сброса системы в программном обеспечении: мы можем проверить из обработчика исключения сброса Reset этот специальный код и перейти к системной памяти перед любой другой процедурой инициализации. Это защитное значение должно храниться в памяти вне областей .data и .bss, в противном случае оно может быть инициализировано во время начальной загрузки микропрограммы (в качестве альтернативы мы можем поместить этот код в обработчик исключения сброса Reset до инициализации этих областей).

22.2.2.Последовательность начальной загрузки в инструментарии GNU MCU Eclipse

Теперь, когда процесс начальной загрузки понятен, мы можем проанализировать достаточно фундаментальную тему: какие именно шаги выполняются во время начальной загрузки приложением, разработанным с помощью инструментария GNU MCU Eclipse? Ответ не тривиален, и есть несколько важных моментов, которые должен знать опытный программист, работающий с этим инструментарием.

ВГлаве 20 мы глубоко проанализировали, как работает исключение сброса Reset. Однако примеры, приведенные в этой главе, изолированы от реального инструментария: мы разработали минимальное приложение STM32, которое не использует ни CubeHAL, ни startup-файлы начального запуска инструментария GNU MCU Eclipse. Поэтому, чтобы понять фактическую последовательность начальной загрузки, мы должны начать с самого начала: с исключения сброса Reset.

ВГлаве 7 мы увидели, что ассемблерный файл system/src/cmsis/startup_stm32xxxx.S

содержит определение таблицы векторов. Эти файлы предоставлены ST и являются специфическими для конкретного микроконтроллера STM32. Открыв тот, который соответствует вашему микроконтроллеру, вы можете найти определение Reset_Handler примерно в строке 76.

76.section .text.Reset_Handler

77.weak Reset_Handler

78.type Reset_Handler, %function

79Reset_Handler:

80

ldr sp, =_estack /* установка указателя стека */

81

 

82/* Копирование инициализаторов сегмента данных из Flash-памяти в SRAM */

83movs r1, #0

84b LoopCopyDataInit

85

86CopyDataInit:

87ldr r3, =_sidata

88ldr r3, [r3, r1]

89str r3, [r0, r1]

90adds r1, r1, #4

92LoopCopyDataInit:

Процесс начальной загрузки

584

93ldr r0, =_sdata

94ldr r3, =_edata

95adds r2, r0, r1

96cmp r2, r3

97bcc CopyDataInit

98ldr r2, =_sbss

99b LoopFillZerobss

100/* Заполнение нулями сегмента .bss. */

101FillZerobss:

102movs r3, #0

103str r3, [r2], #4

104

105LoopFillZerobss:

106ldr r3, = _ebss

107cmp r2, r3

108bcc FillZerobss

110/* Вызов функции инициализации системы тактирования.*/

111bl SystemInit

112/* Вызов статических конструкторов */

113bl __libc_init_array

114/* Вызов точки входа приложения.*/

115bl main

116bx lr

117.size Reset_Handler, .-Reset_Handler

Он написан на языке ассемблера, но теперь его достаточно легко понять, поскольку мы освоили много фундаментальных концепций. Новая секция с именем

.text.Reset_Handler определяется в строке 76, а тело процедуры начинается со строки 80. Здесь в MSP задано содержимое переменной компоновщика _estack (оно совпадает с концом SRAM). Затем управление передается процедуре LoopCopyDataInit, которая инициализирует секцию .data. После этого управление передается процедуре LoopFillZerobss, которая инициализирует секции .bss и вызывает процедуру SystemInit() (мы проанализируем ее через некоторое время) и вызывает статические конструкторы C++ путем вызова __libc_init_array(). Наконец, она передает управление процедуре

main().

Это и есть исключение сброса Reset, предоставляемое ST. Но подождите! Взглянув на строку 77, вы можете увидеть, что процедура Reset_Handler объявлена слабой (weak): это означает, что другая процедура с таким же именем, определенная в другом месте исходного кода, может переопределить эту. Фактически, если вы откроете файл system/src/cortexm/exception_handlers.c, то увидите, что обработчик переопределен там, примерно в строке 29, и он вызывает функцию _start(), которая определена внутри файла system/src/newlib/_startup.c.

Данная процедура, по существу, выполняет инициализацию .data и .bss и передает управление функции main(), но перед выполнением этих операций она вызывает функ-

цию __initialize_hardware_early(), определенную в файле system/src/cortexm/_initialize_hardware.c. Наиболее важные строки кода этой функции приведены ниже.

Процесс начальной загрузки

585

33void __attribute__((weak))

34__initialize_hardware_early(void)

35{

36// Вызов процедуры инициализации системы CSMSIS.

37SystemInit();

38

39#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)

40// Установка в VTOR фактического адреса, предоставленного скриптом компоновщика.

41// Ручное переопределение при возможной неправильной установке функцией SystemInit().

42SCB->VTOR = (uint32_t)(&__vectors_start);

43#endif

44...

Как видите, она вызывает процедуру SystemInit() и перемещает таблицу векторов по адресу, указанному символьным именем компоновщика __vectors_start (данная операция не выполняется в Cortex-M0).

Процедура CMSIS SystemInit() зависит от платформы и предоставляется ST внутри файла с именем system/src/cmsis/system_stm32xxxx.c. Объяснение точного содержания этой процедуры выходит за рамки данной книги: оно достаточно специфично для конкретного микроконтроллера и, по сути, выполняет раннюю инициализацию некоторых периферийных устройств (в основном, тактового генератора). Однако, если вы посмотрите в конец этой процедуры, то увидите, что ST также перемещает таблицу векторов с помощью данной инструкции:

1 SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;

Как видите, VTOR устанавливается в базовом адресе Flash-памяти плюс смещение (VECT_TAB_OFFSET), которое в конечном итоге может быть определено внутри того же файла.

Таким образом, все это говорит о том, что в действительности перемещение таблицы векторов выполняется процедурой инициализации инструментария GNU MCU Eclipse, а не официальными startup-файлами начального запуска от ST. Это важно иметь в виду, если вы собираетесь разрабатывать собственные последовательности начального запуска, как мы увидим позже.

Наконец, _start() также вызывает процедуру __initialize_hardware(), которая вызывает функцию CMSIS SystemCoreClockUpdate(), предоставляемую ST в файле system/src/cmsis/system_stm32xxxx.c. Это зависящая от платформы процедура, которая обновляет глобальную переменную CMSIS SystemCoreClock в соответствии с конкретными регистрами тактирования. Переменная SystemCoreClock широко используется в коде HAL, и важно обеспечить ее синхронизацию с действующей конфигурацией схемы тактирования, как показано в Главе 10.