Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Программирование ARM-контроллеров STM32 на ядре Cortex-M3. Часть 8. Процедуры на асcемблере для STM32

Для уменьшения размеров кода и улучшения его читабельности, группы инструкций, выполняющие какую-либо элементарную задачу, могут быть оформлены в виде «процедур». Удобство процедур в том, что мы можем обращаться к ним из любого места программы, любое число раз.

Объявляются процедуры с помощью ключевых слов PROC / ENDP или FUNCTION / ENDFUNC. Keil-овский ассемблер для ARM-ов не делает различий между процедурами и функциями. При этом, по желанию, можно разместить функцию в какую-то специально выделенную область памяти. Выглядит объявление процедур следующим образом:

    AREA MyProcedure, CODE, READONLY
    Procedure1   PROC
                 ...   ; тело процедуры
                 ...
                 ENDP

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

Следующий вопрос, — как в ARM-контроллерах осуществляется переход из основной программы в процедуру, а также возврат из процедуры в основную программу. Тут возможны несколько вариантов:

  • Использование команды BL label для перехода к процедуре и команды BX LR для возврата в основную программу.
  • Команда BL label выполняет переход на начало процедуры (заносит в PC адрес первой команды процедуры) и записывает в регистр LR адрес следующей за BL label команды (чтобы знать куда потом вернуться).

                 ...   ; основная программа
                 bl Procedure1 ; вызов процедуры
                 ...   ; продолжение основной программы
    AREA MyProcedure, CODE, READONLY
    Procedure1   PROC
                 ...   ; тело процедуры
                 ...
                 bx LR ; возврат в основную программу
                 ENDP

    Вызванная процедура сама может вызывать какие-то процедуры, такие вызовы называются вложенными. Если при этом использовать описанный выше механизм, то каждый новый вызов будет затирать содержимое регистра LR и вернуться назад, к месту первого вызова, будет невозможно.

  • В этом случае можно сохранить адрес возврата в стеке командой PUSH. Тогда, при правильном управлении стеком, можно будет перед возвратом восстановить оттуда нужный адрес возврата и воспользоваться командой BX LR. Более того, можно вместо двух команд POP{LR}, BX LR восстанавливать адрес возврата сразу в PC, используя команду POP{PC}
  •              ...           ; основная программа
                 bl Procedure1 ; вызов процедуры
                 ...           ; продолжение основной программы
    AREA MyProcedure, CODE, READONLY
    Procedure1   PROC
                 push {LR}
                 ...           ; тело процедуры
                 bl Procedure2 ; вложенный вызов процедуры, который затрёт LR
                 ...
                 pop {PC}      ; возврат в основную программу (в стеке у нас LR)
                 ENDP
  • Третий вариант — использовать для выхода из процедуры команду LDR или LDM с PC в качестве регистра-приёмника. Например, этими командами можно восстанавливать значение прямо из стека (аналогично предыдущему варианту), только нужно понимать, что сам стек при этом останется неизменным (то есть указатель стека не изменится).
  •              ...           ; основная программа
                 bl Procedure1 ; вызов процедуры
                 ...           ; продолжение основной программы
    AREA MyProcedure, CODE, READONLY
    Procedure1   PROC
                 push {LR}
                 ...           ; тело процедуры
                 bl Procedure2 ; вложенный вызов процедуры, который затрёт LR
                 ...
                 LDR PC,[SP]   ; читаем значение PC из стека (там у нас LR), но
                               ; сам стек при этом не меняется
                 ENDP

В принципе, для переходов можно использовать любые команды обработки данных, позволяющие сохранить результат в регистр, например MOV или AND и тому подобные. Не смотря на то, что в руководствах по командам THUMB-2 написано, что в них нельзя использовать PC в качестве регистра-приёмника и Keil-овский ASM будет при компиляции писать Warning, на самом деле всё будет работать. Это конечно уже для гиков и бог его ещё знает что там будет, скажем, с конвейером, но теоретически вы вполне можете скомпилить и запустить что-то подобное (я, например, запускал, чисто ради интереса):

                 dcd Procedure1   ; адрес процедуры
                 dcd Continue1    ; адрес продолжения
    ENTRY
                 ldr R1,[PC,#-12] ; теперь у нас в R1 адрес начала процедуры,
                 ldr R2,[PC,#-12] ; а в R2 адрес продолжения
                 nop
                 ...              ; основная программа
                 mov PC,R1        ; вызов процедуры
    Continue1
                 ...              ; продолжение основной программы
    AREA MyProcedure, CODE, READONLY
    Procedure1   PROC
                 ...              ; тело процедуры
                 ...
                 mov PC,R2        ; возврат из процедуры
                 ENDP

Подробнее про процедуры можно почитать в книжке Assembly Language Programming, Vincent Mahout, главы 3.3.5 (стр.35) и 7.1 (стр.119). Книжка эта на английском, русский перевод мне не встречался.

  1. Часть 1. Установка MDK, создание проекта, основы Keil uVision
  2. Часть 2. Команды и директивы ассемблера, структура и синтаксис программы. Первая программа для STM32
  3. Часть 3. Карта памяти контроллеров STM32, методы работы с памятью
  4. Часть 4. Регистры, старт и режимы работы контроллеров STM32
  5. Часть 5. Как залить прошивку в контроллер
  6. Часть 6. Настройка системы тактирования
  7. Часть 7. Работа с портами ввода-вывода
  8. Часть 8. Процедуры на ассемблере для STM32
  9. Часть 9. Система прерываний
  10. Часть 10. CMSIS, использование стандартных библиотек и функций
  11. Часть 11. Подключение и использование драйверов из пакета StdPeriph
  12. Часть 12. Работа с модулями USART и UART.
  13. Часть 13. Работа с модулями ADC
  14. Часть 14. Использование DMA
  15. Часть 15. Таймеры. Глава 1 — Введение. Простейшие таймеры
  16. Часть 15. Таймеры. Глава 2 — Таймеры общего назначения TIM9 — TIM14
  17. Часть 15. Таймеры. Глава 3 — Таймеры общего назначения TIM2 — TIM5
  18. Часть 15. Таймеры. Глава 4 — Продвинутые таймеры TIM1, TIM8
  19. Часть 16. Создание устройства USB HID в Keil uVision при помощи библиотечного компонента USB
  20. Приложение 1. Набор инструкций THUMB-2 и особенности их использования
  21. Приложение 2. Таблица векторов прерываний для семейств STM32F101, STM32F102, STM32F103
  22. Приложение 3. Драйвера и функции библиотеки StdPeriph

Добавить комментарий