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

Организация памяти

535

совершенно бессмысленен (для _symbol нет соответствующего значения).

Способ работы с символьными именами компоновщика может быть очевидным, если _symbol является ячейкой памяти, при этом оно является источником множества ошибок в случае, если _symbol является постоянным значением. Например, чтобы получить значение в Си, мы должны использовать следующий код:

unsigned int heapSize = (unsigned int)&_Heap_Size;

Опять же, _Heap_Size, содержит размер кучи в качестве адреса (то есть 0x0000 0190), но оно не является корректным адресом STM32. Этот факт также можно проанализировать, проверив таблицу символьных имен конечного бинарного файла, используя инструмент objdump с опцией командной строки -t.

20.2.5. Проверка размера Кучи и Стека на этапе компиляции

Микроконтроллеры имеют ограниченные ресурсы памяти. В микроконтроллерах STM32 линеек Value line очень часто превышается максимальный объем памяти SRAM. Мы также можем использовать скрипт компоновщика для добавления своего рода «статической» проверки максимальной загруженности памяти. Следующая секция скрипта компоновщика помогает гарантировать, что мы не используем слишком много SRAM:

_Min_Stack_Size = 0x200;

/* Секция User_heap_stack, используемая для проверки того, что осталось достаточно ОЗУ */

._user_heap_stack :

{

. = ALIGN(4);

. = . + _Heap_Size;

. = . + _Min_Stack_Size;

. = ALIGN(4); } >SRAM

С помощью приведенного выше кода мы определяем «фиктивную (dummy)» секцию в конечном бинарном файле. Используя оператор счетчика адресов (“.”), Мы увеличиваем размер этой секции, чтобы она имела размер, равный максимальному размеру кучи и «примерному» минимальному размеру стека. Если сумма областей .data, .bss, стека и кучи превышают размер SRAM, компоновщик выдаст ошибку, как показано ниже:

arm-none-eabi-g++ ... ./src/ch10/main-ex5.o

../../../../arm-none-eabi/bin/ld: nucleo-f401RE.elf section `._user_heap_stack' will not\ fit in region `SRAM'

../../../../arm-none-eabi/bin/ld: region `SRAM' overflowed by 9520 bytes collect2: error: ld returned 1 exit status

make: *** [nucleo-f401RE.elf] Error 1

Важно подчеркнуть, что это статическая проверка, и она не контролирует действия микропрограммы во время выполнения. Для обнаружения переполнения стека необходимы разные стратегии, и достаточно сложно найти полноценное решение для встраиваемой системы. Мы проанализируем эту тему в Главе 22.

Организация памяти

536

20.2.6. Различия с файлами скриптов инструментария

Созданный до сих пор скрипт компоновщика хорошо работает для большинства приложений STM32. Однако, если вы собираетесь писать код своей микропрограммы на C++ или просто использовать библиотеки, созданные на C++, то этих скриптов компоновщика и последовательностей запуска недостаточно. Чтобы понять почему так, рассмотрим следующее приложение C++:

1class MyClass {

2int i;

3

4public:

5MyClass() {

6i = 100;

7}

8

9void increment() {

10i++;

11}

12};

13

14 MyClass instance;

15

16int main() {

17instance.increment();

18for (;;);

19}

Давайте сосредоточим наше внимание на строке 14. Здесь мы определяем экземпляр класса MyClass. Экземпляр определяется как глобальная переменная. Но объявление экземпляра класса предполагает, что автоматически вызывается конструктор этого класса. Итак, для ясности, когда мы вызываем метод increment() в строке 17, атрибут i экземпляра будет равен 101. Но кто позаботится о вызове конструктора экземпляра? Когда экземпляр создается локально (то есть из глобальной функции или другого метода), он может вызываться для инициализации класса. Но когда это происходит в глобальной области видимости, он зависит от других процедур инициализации. Обычно компилятор автоматически генерирует массив указателей на функции, которые будут содержать процедуры инициализации для всех глобальных и статически выделенных объектов. Эти массивы обычно называются __init_array и __fini_array (которые содержат вызов деструкторов объектов).

И скрипты компоновщика, и процедуры запуска, предоставляемые плагином GNU MCU и ST в его HAL, содержат весь необходимый код для обработки этих и других действий инициализации. Их объяснение выходит за рамки данной книги (оно также включает в себя глубокий анализ некоторых действий libc, выполняемых при запуске). Однако теперь, когда мы знаем, как понять содержимое скрипта компоновщика, разобраться с ними не вызовет особых сложностей.