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

в 6:36, , рубрики: GNU AS, led blink, STM32F4, программирование микроконтроллеров

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

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


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

[1] книгу Джозеф Ю. Ядро Cortex-M3 Компании ARM. Полное руководство.
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 1.
Ссылку на книгу давать не буду, она легко гуглиться в любом желаемом формате, или покупается на том же озоне.
Несмотря на другое ядро (у микроконтроллера 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, я зачеркнул некорректные суждения:

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

По всей видимости это ошибка при переводе, но как показывает практика — именно такие ошибки «в простом» способны попортить больше всего нервов, так как «копаясь в коде» в самую последнюю очередь заподозришь в неверное работе инструкцию в работе которой абсолютно все понятно. К слову в [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, выводам PH2 и PH3, соответственно примеры я делаю именно под свою отладочную плату, а вот под имеющуюся у вас наверняка STM32F4 Discovery Вам примеры в этой и последующих публикациях не подойдут именно по причине подключения к другим портам GPIO и другим выводам. Разница на самом деле минимальна, но текст программы нужно будет править самостоятельно.
Так что, если Вас действительно тема программирования микроконтроллеров на ассемблере «задела», для Вас это будет:

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

Файл с описанием подключений на отладочной плате STM32F4 Discovery можно найти в документе Discovery kit for STM32F407/417 lines (про подключение светодиодов в параграфе 4.4). Вообще все что предлагает ST по данной плате находиться здесь

По строке поиска «GPIOH» находится строка определение
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 3
Выше найденной строки, в примечании, указано, что это оборудование находится на шине AHB1 peripherals.
Теперь у нас два возможных пути:

1) Найти в Reference manual какой регистр RCC отвечает за тактирование GPIO_H. Регистр будет называться RCC_AHB1_<что_то>. Попутно прочитаете и будете примерно представлять функционал системы регистров RCC.
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 4

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

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

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

Теперь нужно вычислить адрес регистра RCC_AHB1ENR.

Адрес вычисляется следующим образом:
1) берем значение PERIPH_BASE (0x4000 0000)
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 6

2) К нему прибавляем значение RCC_BASE (AHB1PERIPH_BASE + 0x3800)
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 7

3) К полученному прибавляем значение RCC_AHB1ENR (0x30)
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 8

Получается RCC_AHB1ENR = 0x4000 3830

Теперь по битовым определениям регистра RCC_AHB1ENR находим, что GPIOH включается записью значения 0x00000080.
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 9

Как видите найти нужное значение было не так уж и сложно, и зачастую, при некотором опыте в Reference manual заглядывать и не придется по каждой мелочи.

теперь напишем запись найденного значения по найденному адресу при помощи уже известных нам инструкций.
Код программы включающий GPIOH мог бы выглядеть так:

	@ включим тактирование GPIO_H
	MOV32	R0, PERIPH_BASE + RCC_BASE + RCC_AHB1ENR  @ адрес
	MOV32   R1, RCC_AHB1ENR_GPIOHEN  @ значение
	STR	R1, [R0]                     @ запись R1 по адресу указанному в R0

мы не подставляем точный адрес и значение потому, что и считать лень, да и не наглядно будет — куда записываем, что записываем… а вот с указанием имен констант — все понятно (конечно, если вы знакомы с ассемблером).
Написанное выше должно быть Вам АБСОЛЮТНО понятно! понятны инструкции (не углубляясь в написание макроса), понятны константы адреса и значения (откуда взялись, чему равны, где в Reference manual проверить)

Теперь еще одна мелочь: как видно из битового определения RCC_AHB1ENR — если мы просто запишем значение 0х00000080 — то мы включив GPIOH — выключим всю остальную периферию, в нашей программе это допустимо, но вообще так делать не правильно, давайте перепишем таким образом чтобы отключения остальной периферии не происходило:

        @ включим тактирование GPIO_H
	MOV32	R0, PERIPH_BASE + RCC_BASE + RCC_AHB1ENR     @ адрес
	MOV32   R1, RCC_AHB1ENR_GPIOHEN                                      @ значение
	LDR		R2, [R0]				@ прочитали значение регистра
	ORR		R1, R1, R2                       @ логическое, побитовое ИЛИ : R1= R1 ИЛИ R2
	STR	R1, [R0]                                    @ запись R1 по адресу указанному в R0

Теперь необходимо настроить GPIOH вывод PH2 на выход.
Из примера выше мы уже знаем, что за режим работы вывода порта отвечает регистр GPIOx_MODER, тут нужно будет обратиться к Reference manual, описание этого регистра находится на стр. 283, нам нужен режим «General purpose output mode», согласно Reference manual для этого в соответствующее поле регистра (биты 4 и 5) нужно записать значение «01»
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 10

Сделаем это!
Задача 1: узнать адрес регистра и сформировать значение на запись в него.
Вычислим адрес:
1) берем значение PERIPH_BASE (0x4000 0000)
2) к нему прибавим значение GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00)
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 11
3) и к полученному значение GPIO_MODER (0x00)
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 12

Получаем адрес как сумму: PERIPH_BASE + GPIOH_BASE + GPIO_MODER

Теперь разберемся со значением: чтобы задать режим вывода PH2 GPIOH необходимо установить bit4=1, bit5=0, то есть, в двоичном виде:
0000 0000 0000 0000 0000 0000 0001 0000 = 0x00000010

Такое значение есть в файле определений:
STM32F4: GNU AS: Мигаем светодиодом (Оживление) (Часть 2) - 13

Задача 2: напишем код устанавливающий режим работы вывода PH2 GPIOH.
Задумались? код ведь от предыдущего будет отличаться только константами, и может выглядеть так:

	@ установим режим GPIOH pin_2
	MOV32	R0, PERIPH_BASE + GPIOH_BASE + GPIO_MODER  @ адрес
	MOV32   R1, GPIO_MODER_MODER2_0                                    @ значение
	LDR		R2, [R0]				 @ прочитали значение регистра
	ORR		R1, R1, R2                        @ логическое, побитовое ИЛИ: R1= R1 ИЛИ R2
	STR	R1, [R0]                                     @ запись R1 по адресу указанному в R0

Есть еще один способ определения значения режима работы вывода PH2 GPIOH: можно использовать прямое значение из файла определений: GPIO_MODER_MODER_GENERAL_OUT (0x00000001) сдвинув его на соответствующую позицию регистра.
Поскольку у нас размер настройки 2 бита, и установить нужно настройку для вывода 2 — то количество бит на которые нужно сдвинуть константу можно посчитать по формуле: <размер настройки> * <номер вывода>, получается конструкция вида: MOV32 R1, GPIO_MODER_MODER_GENERAL_OUT << (2*2), и код настройки может выглядеть так:

	@ установим режим GPIOH PH2
	MOV32	R0, PERIPH_BASE + GPIOH_BASE + GPIO_MODER     @ адрес
	MOV32   R1, GPIO_MODER_MODER_GENERAL_OUT << (2*2)   @ значение полученное сдвигом константы
	LDR		R2, [R0]				 @ прочитали значение регистра
	ORR		R1, R1, R2                        @ логическое, побитовое ИЛИ: R1= R1 ИЛИ R2
	STR	R1, [R0]                                    @ запись R1 по адресу указанному в R0

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

  1. регистр GPIOx_OTYPER (схема выхода) по умолчанию установлен в «0» (что означает режим работы выхода «push-pull» — то что нам и нужно!)
  2. регистр GPIOx_OSPEEDR (скорость выхода) для портов после GPIO_B установлен в «0» что означает Low speed (а нам быстрее пары/тройки герц и не нужно будет)
  3. регистр GPIOx_PUPDR (подтяжка) так же для портов после GPIO_B установлен в «0», что означает No pull-Ip, pull-down

Теперь осталось научиться управлять состоянием вывода PH2 нашего GPIOH чтобы мигать светодиодом.
Сначала хотел сделать это через GPIOx_ODR (стр. 285 Reference manual), но потом решил что проще будет сделать через регистр GPIOx_BSRR.
Его отличие от GPIOx_ODR в том что запись в нижние 16 бит приводит к установке логической «1» на соответствующем выводе, а запись в верхние 16 бит — приводит к сбросу в логический «о», так же на соответствующем выводе порта
Давайте сформируем конкретные значения для примера [ двоичное (шестнадцатеричное) ]:

  1. 0000 0000 0000 0000 0000 0000 0000 0100 (0x00000004)- мы включить светодиод
  2. 0000 0000 0000 0100 0000 0000 0000 0000 (0x00040000)- мы выключить светодиод

Думаю что код реализующий включение и выключение светодиода (без паузы между ними) вы сможете написать сами, но без этого кода статья будет не полной так что пишу сам (проверьте себя!), заодно применим новую для нас команду вызова подпрограммы BL:

BLINK_LOOP:
	@ включим светодиод
	MOV32	R0, PERIPH_BASE + GPIOH_BASE + GPIO_BSRR  @ адрес
	MOV32   R1, 0x04   @ значение
	STR	R1, [R0]           @ запись R1 по адресу указанному в R0
	
	BL	DELAY   @  пауза
	
	@ выключим светодиод
	MOV32	R0, PERIPH_BASE + GPIOH_BASE + GPIO_BSRR  @ адрес
	MOV32   R1, 0x04 << (1*16)         @ значение 1-размер поля, 16-во второе полуслово
	STR	R1, [R0]                                 @ запись R1 по адресу указанному в R0

	BL	DELAY   @  пауза	

	B	BLINK_LOOP @ делаем цикл

Про команды BL / BX: это команды организации вызова подпрограмм, адрес возврата при переходе с помощью BL сохраняется в регистра LR, никаких сохранений в стек не происходит (в отличие от 8ми битных процессоров, которые сохраняли на стек адрес возврата из подпрограммы). Соответственно команда BX просто осуществляет возврат из подпрограммы по адресу возврата в LR.

Ну и код реализующий паузу (задержку), в данный момент ничего умнее простого цикла со счетчиком мы не придумаем:

DELAY:
		MOV32	R2, 0x00100000 	@ повтор цикла задержки 0x0010 0000 раз.
Delay_loop:	SUBS	R2, R2, 1
		BNE	Delay_loop
		BX	LR

Новая команда BNE — это команда перехода с условием NE — «не равно».

Код паузы (задержки) работает следующим образом:
в R2 загрузили начальное значение (0x00100000),
далее из R2 вычитаем 1, причем команда вычитания у нас с суффиксом «S» — то есть после своей работы обновляет флаги,
к команде BNE подходим со сброшенным (если в R2 не ноль) флагом, и переходим на метку Delay_loop (на вычитание из R2 единицы),
и так будет до тех пор пока R2 не станет равным нулю, в этом случае команда BNE выполнена не будет (еще раз повторю: NE-это выполнение при неравенстве нулю) и произойдет возврат из подпрограммы командой BX LR.

Ну вот и все. Наша программа написана, теперь осталось собрать ее кусочки в один файл и откомилировать / скомпоновать (меня тут немного пожурили за слово «линковать» в первой публикации).

полный текст моей программы выглядит следующим образом:

@GNU AS

@ Настройки компилятора
.syntax unified    @ тип синтаксиса
.thumb                @ тип используемых инструкций Thumb
.cpu cortex-m4   @ микроконтроллер

.include "stm32f40x.inc"   @ файл определений микроконтроллера

@ макрос псевдокоманды MOV32, пока просто используем, не вникая как, что и почему
.macro	MOV32 regnum,number
	MOVW regnum,:lower16:number
	MOVT regnum,:upper16:number
.endm

@ таблица векторов прерываний
.section .text

.word	0x20020000	@ Вершина стека
.word	Reset+1		@ Вектор сброса

Reset:
		@ включим тактирование GPIOH
		MOV32	R0, PERIPH_BASE + RCC_BASE + RCC_AHB1ENR  @ адрес
		MOV32   R1, RCC_AHB1ENR_GPIOHEN                                   @ значение
		LDR	R2, [R0]		 @ прочитали значение регистра
		ORR	R1, R1, R2       @ логическое, побитовое ИЛИ: R1= R1 ИЛИ R2
		STR	R1, [R0]            @ запись R1 по адресу указанному в R0

		@ установим режим GPIOH PH2
		MOV32	R0, PERIPH_BASE + GPIOH_BASE + GPIO_MODER  @ адрес
		MOV32   R1, GPIO_MODER_MODER2_0                                    @ значение
		LDR	R2, [R0]		 @ прочитали значение регистра
		ORR	R1, R1, R2       @ логическое, побитовое ИЛИ: R1= R1 ИЛИ R2
		STR	R1, [R0]            @ запись R1 по адресу указанному в R0

BLINK_LOOP:
		@ включим светодиод
		MOV32	R0, PERIPH_BASE + GPIOH_BASE + GPIO_BSRR  @ адрес
		MOV32   R1, 0x04                                                                      @ значение
		STR	R1, [R0]           @ запись R1 по адресу указанному в R0
	
		BL	DELAY             @   пауза
	
		@ выключим светодиод
		MOV32	R0, PERIPH_BASE + GPIOH_BASE + GPIO_BSRR  @ адрес
		MOV32   R1, 0x04 << (1*16)                                                      @ значение 1-размер поля, 16-во второе полуслово
		STR	R1, [R0]           @ запись R1 по адресу указанному в R0

		BL	DELAY             @   пауза	

		B	BLINK_LOOP         @ делаем цикл

DELAY:
		MOV32	R2, 0x00100000 	@ повтор цикла задержки 0x0010 0000 раз.
Delay_loop:	
		SUBS	R2, R2, 1
		BNE	Delay_loop
		BX	LR

Обратите внимание!
После последней команды в файле программы я оставил одну пустую строку! если этого не сделать, то компилятор будет ругаться. Служебную команды .end можно не использовать.

Текст программы компилируется в binary файл размером 116 байт.

Залить в микроконтроллер нашу программу можно при помощи DFU, подробнее как это делается я описывал в статье STM32F4: Программирование через интерфейс USB (DFU/DfuSe). Только обратите внимание что для прошивки у нас «готовиться» файл с расширением .bin — чтобы корректно «прошить» необходимо будет указать адрес записи его во FLASH = 0x08000000 (это вы должны помнить из прошлой публикации)

p.s. Записали? в STM32F4 Discovery? и не работает? ПРАВИЛЬНО не работает! у STM32F4 Discovery светодиоды подключены к другим GPIO, и чтобы программа заработала ее нужно поправить! Об успехах (и не очень) можно делиться в комментариях или на электронную почту: gorbukov<здесь что то тявкало]яндекс.ru

для тех кому некогда заниматься созданием своего проекта «с нуля» даю ссылку на скачивание готового проекта и настроенной среды компиляции (с некоторыми «плюшками» в виде вывода статистики, генерации .hex файла прошивки, генерацией вспомогательных файлов для контроля компиляции и т.д.)

Автор: VitGo

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js