STM32F4: GNU AS: Оживление (Мигаем светодиодом)

от автора

Это вторая публикация на тему программирования микроконтроллеров STM32 на языке ассемблера, первая часть находится Здесь.

Итак, в прошлой статье мы создали инструмент при помощи которого, можно произвести компиляцию и компоновку (линковку) проекта на языке ассемблера. Теперь напишем программу за работой которой можно наблюдать.


Нам понадобиться знание языка ассемблера микроконтроллера STM32. Чтобы было проще начать если Вы вообще не сталкивались и не знаете с что почитать «по теме» то могу предложить следующие источники:

[1] книгу Джозеф Ю. Ядро Cortex-M3 Компании ARM. Полное руководство.
.
Ссылку на книгу давать не буду, она легко гуглиться в любом желаемом формате, или покупается на том же озоне.
Несмотря на другое ядро (у микроконтроллера STM32F4xx — Cortex-M4) инструкции ассемблера у всего семейства одинаковы, русский же язык повествования книги сможет облегчить задачу первоначального «вхождения».
Обращаю внимание, что некоторые русскоязычные ресурсы и указанная мною книга тоже (!), могут содержать небольшие неточности и ошибки, поэтому не ленитесь проверять те или положения по другим источникам и даташитам производителя

[2] Статью "Изучение системы команд процессора ARM" с ресурса marsohod которая размещена Здесь.
В этой статье затронуты некоторые практические, но отнюдь не всегда очевидные аспекты использования инструкций ARM. В этой статье рассмотрены вопросы каким образом кодируются инструкции, наглядно показано откуда идут ограничения, где есть преимущества.

Далее идут англоязычные руководства и документы:

[3] Cortex-M4 Devices. Generic User Guide. Скачать можно с сайта источника, размещено Здесь Система команд рассматривается в Chapter 3 «The Cortex-M4 Instruction Set», хотя несомненно необходимо этот документ попытаться почитать, выбирая информацию перекрестно с [1] (если знания английского не позволяют читать «напрямую»)

[4] STM32F3 and STM32F4 Series Cortex-M4 programming manual. Искать на st.com, прямая Ссылка. Фактически это англоязычная версия книги [1], но читать ее все равно стоит, там есть примеры которых нет в [1].

[5] RM0090. Reference manual. STM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439 advanced ARM-based 32-bit MCUs. Искать тоже на st.com, прямая Ссылка. Это руководство содержит в себе описание архитектуры микроконтроллера, его периферию, регистры управления ею.


Со своей стороны я сделаю лишь некоторые важные, на мой взгляд, замечания:

1) О загрузке 32-ух разрядного значения в регистр.
Как Вы уже узнали, регистры микроконтроллера 32-ух битные, между тем команды прямой загрузки в регистр 32-ух разрядного числа в системе команд микроконтроллера нет.
Выход который применяется всеми тривиален: загрузка верхних / нижних 16-ти бит слова отдельно командами MOVW/MOVT, например для R0:

MOVW R0, lower16 MOVT R0, upper16 

Зачастую эту конструкцию даже объединяют в макрос, обычно называемый MOV32

.macro	MOV32 regnum,number 	MOVW \regnum,:lower16:\number 	MOVT \regnum,:upper16:\number .endm 

Данные команды транслируются в 8 байт (2 слова по 32 бита) кода, и исполняются 2 такта процессора.

Еще один способ загрузки 32-ух битного значения в регистр это применение инструкции LDR Rg,=val32

LDR R0, =0x11223344 @ 32-ух битное значение 

Данная инструкция не является отдельной инструкцией микроконтроллера, и при компиляции она заменяется на команду загрузки 32-ух битного значения из памяти по смещению относительно PC и константой в FLASH памяти с загружаемым значением:

LDR R0, [pc, offset to_M]  ... to_M: .word 0x11223344   @ 32-ух битное значение 

Инструкция LDR R0, [pc, offset ] является 16-ти битной (Thumb формат), плюс 32 бита в памяти на хранение самой константы — ИТОГО расход памяти 48 бит (6 байт), вместе 64 бит (8 байт) при использовании набора команд макроса MOV32
По времени исполнения — команда LDR будет выполнена за 1 такт, а набор команд макроса MOV32 за 2 такта. Ограничение у способа загрузки 32-ух битного значения при помощи команды LDR rg, [pc, offset] в «длине рук» величине возможного используемого смещения.
К слову команда LDR R0, =var32 очень не корректно описана в [1], стр. 69, я зачеркнул некорректные суждения:

По всей видимости это ошибка при переводе, но как показывает практика — именно такие ошибки «в простом» способны попортить больше всего нервов, так как «копаясь в коде» в самую последнюю очередь заподозришь в неверное работе инструкцию в работе которой абсолютно все понятно. К слову в [4] команда LDR Rg, =val32 вообще не выделяется как отдельная, что логично, это обычная загрузка значения регистра из ячейки памяти. Этот момент нужно обязательно, что называется, прочувствовать — обязательно проверим и будем использовать.

2) Условное исполнение операций
Еще одна «фича» микроконтроллера (и вообще «камней» с ядром ARM) возможность указания в инструкциях условия исполнения по флагам (EQ/NE, CS/CC, MI/PL и так далее).
Однако не все так просто: для возможности условного исполнения инструкций они должны быть включены в так называемый IT блок.
Этот блок может в себя включать от 1 до 4 инструкций, инструкция IT подразумевает наличие после себя одной команды с условным исполнением, еще до трех команд (всего должно быть не более 4-х команд!) расширяется указанием условий:

  1. «T» — для исполнения инструкции при выполнении условия указанного в инструкции IT
  2. «E» — для исполнения инструкции при невыполнении условия указанного в инструкции IT

Примеры:

IT EQ  @ одна команда, по флагу EQ (равенства нулю) MOVEQ R0, 0x0000  ITT NE @ две команды, по флагу NE (не равенства нулю) MOVNE R0, 0x0001 ADDNE R1, R0, R2  ITTE CS @ две команды по флагу CS (перенос), одна команда при СС (нет переноса) MOVCS R0, R4 ADDCS R1, R0, R2 SUBCC R1, R4 

Использование IT блока оптимизирует работу конвейера ядра микроконтроллера.
Дополнительно необходимо помнить, что например в отличие от инструкций ассемблера восьмибитных процессоров прошлых поколений — инструкции ARM влияют на флаги только если это напрямую указано суффиксом «S»
После команд которые влияют на флаги можно использовать IT блок. Для команд перехода (B) с условием IT блок не нужен.

3) Программы на ядре ARM являются перемещаемыми !
Если Вы внимательно изучали форматы инструкций — то наверное заметили полное отсутствие команд с прямой адресацией! нет ни загрузок из ячеек памяти с абсолютно указанным адресом, ни переходов! Все ячейки для операций записи/чтения, все переходы осуществляются по относительным адресам по смещению от PC, конечно при желании можно сделать переход по абсолютному адресу, но в общем случае программа является перемещаемой. Поэтому при анализе текста скомпилированной программы будьте готовы к тому, что в программе не будет абсолютных адресов, и возможно, иногда придется пересчитывать смещения в адреса для проверки правильности компиляции (это будет не часто, но «предупрежден значит вооружен»), как это делать смотрите в прошлой статье.


Теперь давайте перейдем к написанию программы, работу которой мы сможем увидеть (не люблю называть такие программы «Hello Word!» просто потому что никакого «Hello» в мигании светодиодом не вижу, скорее это программы «оживляющие» микроконтроллер).
Для написания программы нам потребуется понимание работы следующих инструкций: MOV, MOVT, LDR, STR, ORR, B, BL, BX, SUB, в условных переходах мы будем использовать условие NE (дойдя до этого места вы должны себе представлять что делает та или иная инструкция)

Считаю что будет уместным сделать небольшую отсылку к Статье, описывающей аналогичный функционал, но на языке Си для среды CooCox, там дано краткое описание регистров которые нужно установить и их возможных значений. После прочтения статьи Вам должно стать понятно что именно мы должны будем сделать, на языке ассемблера, для того чтобы получить «мигалку».

Укрупненно, для того чтобы включать выключать светодиод нам нужна программа следующего вида:

	< блок настройки вывода на выход >  BLINK_LOOP:        < блок  включения светодиода >         < блок паузы >         < блок  выключения светодиода >         < блок паузы >         B     BLINK_LOOP    @ операция зацикливания последних 4 блоков  

Напишем Блок настройки периферии программы

Вся периферия «на кристалле» STM32 тактируется. Тактирование может быть включено/выключено отдельно для каждого устройства микроконтроллера — это сделано и для экономии энергии и для устранения конфликтов различных устройств при использовании одних и тех же ресурсов.
Поскольку вся возможная периферия доступна в виде регистров адресуемых как ячейки памяти, эти регистры описаны обычно в файлах-определениях, эти файлы определения существуют для различных семейств микроконтроллеров, внутри семейства обычно не отличаются. К сожалению для GNU ассемблера мне не удалось найти готовый файл, поэтому я его сделал из файла-определения среды CooCox — фактически заменил префиксы дефиниций, знаки "=" поменял на "," символы примечания на "@" и получился вот такой файл stm32f40x.inc. Файл определений не догма, и не что то неизменное — при написании программ я постоянно его дополняю (старые значения стараюсь не убирать, никогда не знаешь заранее какие значения могут понадобиться), чего и вам желаю. Одновременно именно этот файл нужно передавать компилятору вместе с текстом программы или, например, «товарищу» на проверку, так как «любой другой» файл определений может просто нужных определений не иметь и при компиляции будет сгенерирована ошибка.
Формат файла определений — проще некуда:

@    Имя константы       Значение     Примечание .equ FLASH_BASE      ,0x08000000  @ FLASH(up to 1 MB) base address */ .equ CCMDATARAM_BASE ,0x10000000  @ CCM(core coupled memory) data RAM(64 KB)*/ .equ SRAM1_BASE      ,0x20000000  @ SRAM1(112 KB) base address */ . . . 

Откроем этот файл в режиме редактирования (F4) в FAR, находясь в самом его начале запускаем поиск по строке «GPIOH»

В качестве отладочной платы я использую китайскую «поделку» Open407I-C на ее «борту» стоит микроконтроллер STM32F407IGT, светодиоды на моей плате подключены к GPIO_H, выводам 2 и 3, соответственно примеры я делаю именно под свою отладочную плату, а вот под имеющуюся у вас наверняка STM32F4 Discovery Вам примеры в этой и последующих публикациях не подойдут именно по причине подключения к другим портам GPIO и другим выводам. Разница на самом деле минимальна, но текст программы нужно будет править самостоятельно.
Так что, если Вас действительно тема программирования микроконтроллеров на ассемблере «задела», для Вас это будет:

  1. интересно (вы не занимаетесь простым копированием кода который я привел — в отличие от первой статьи где достаточно было просто внимательно читать, в этой публикации Вам придется еще и самостоятельно проходить тот же путь что и я, но под свою отладочную плату),
  2. полезно (сделанное один раз своими руками и головой запоминается намного лучше просто скопированного). Файл-определений для контроллеров семейства STM32F407 одинаков (вам не придется делать свой).

По строке поиска «GPIOH» находится строка определение

Выше найденной строки, в примечании, указано, что это оборудование находится на шине AHB1 peripherals.
Теперь у нас два возможных пути:

1) Найти в Reference manual какой регистр RCC отвечает за тактирование GPIO_H. Регистр будет называться RCC_AHB1_<что_то>. Попутно прочитаете и будете примерно представлять функционал системы регистров RCC.

2) Не уходя с stm32f40x.inc ищем регистр RCC который разрешает тактирование для шины AHB1, для этого опять идем в начало файла определений (Ctrl + Home) и ищем строку «RCC_AHB1», попутно находки проверяем в Reference manual, вы должны найти:

  1. RCC_AHB1RSTR (будет найдено смещение регистра и его битовые определения), взглянув в Reference manual узнаем что этот регистр отвечает за сброс устройств — не наш случай.
  2. RCC_AHB1ENR, с битовыми определениями совпадающими с названием устройств, из Reference manual узнаем что это действительно тот самый регистр который мы ищем

ссылка на оригинал статьи http://habrahabr.ru/post/274579/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *