Ардуино условный переход два условия: Уроки Arduino. Условия и сравнения

Содержание

Прекратите использовать оператор If-else / Блог компании SkillFactory / Хабр

Вы просмотрели бесчисленные учебные пособия, использующие операторы If-else. Вы, вероятно, также читали книги по программированию, пропагандирующие использование If-else в качестве фактического метода ветвления.

Возможно, это даже ваш режим по умолчанию, чтобы использовать If-else. Но давайте покончим с этим прямо сейчас, заменив If-else объектами состояния.

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

Даже если вы слышали о шаблоне состояния, вы можете задаться вопросом, как он реализован в готовом к продакшену коде.

Для тех, кто все еще находится в неведении, вот очень краткое введение.

Вы увеличите сложность с любым новым условным требованием, реализованным с помощью If-else.

Применяя шаблон состояния, вы просто изменяете поведение объектов, используя специализированные объекты состояния вместо операторов If-else.

Прошли те дни, когда код выглядел примерно так, как показано ниже.

Вы, конечно, писали более сложные ветвления раньше. Несколько лет назад я был уверен в этом.

Приведенная выше логика ветвления даже не очень сложна — но попробуйте добавить новые условия, и вы увидите, что она усложняется в разы.

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

Даже лучше, это сделает вашу кодовую базу более SOLID, за исключением буквы ”D».

«Хорошо, я поверил, что if-else — это зло, теперь покажите мне, как избежать беспорядочного ветвящегося кода»

Мы рассмотрим, как я заменяю If-else в готовом к продакшену коде. Это выдуманный пример, но подход тот же, что я использовал в кодовых базах для крупных клиентов.

Давайте создадим очень простой класс Booking, который имеет несколько состояний. Он также будет иметь два паблик метода: Accept() и Cancel().

Я нарисовал диаграмму в меру своих возможностей, которая отображает различные состояния, в которых может находиться бронирование.

Рефакторинг ветвящейся логики из нашего кода представляет собой трехэтапный процесс:

  1. Создать абстрактный класс базового состояния
  2. Реализовать каждое состояние как отдельный класс, наследующийся от базового состояния
  3. Пусть класс Booking имеет приватный или внутренний метод, который принимает класс базового состояния в качестве параметра

Реализация

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

Обратите внимание, что этот класс также имеет два метода, Accept и Cancel — хотя здесь они помечены как внутренние.

Кроме того, базовое состояние имеет ”специальный» метод EnterState(Booking booking). Он вызывается всякий раз, когда объекту бронирования присваивается новое состояние.

Во-вторых, мы создаем отдельные классы для каждого состояния, которое хотим представлять.

Обратите внимание, как каждый класс представляет состояние, описанное на красивой диаграмме выше. Кроме того, CancelledState не позволит нашему бронированию перейти в новое состояние. Этот класс очень похож по духу на шаблон нулевого объекта.

Наконец, сам класс бронирования.

Посмотрим, класс бронирования — это просто делегирование полномочий осуществления принятия и отклонения?

Это позволяет нам устранить большую часть условной логики и позволяет каждому состоянию сосредоточиться только на том, что важно для него самого — текущее состояние также имеет возможность перевести бронирование в новое состояние.

Как бороться с новыми условиями

Если новая функция обычно была реализована с помощью некоторой условной проверки, то теперь вы можете просто создать новый класс состояния.

Это очень просто. Вам больше не придется иметь дело с громоздким оператором if-else.

Как сохранить объект состояния в базе данных

Объект состояния не имеет значения при сохранении объекта, например, в базе данных SQL или NoSQL. Важно только знать состояние объекта и то, как оно должно быть отображено в столбце.
Вы можете сопоставить состояние с читаемым именем, перечислением или целым числом. Все, что вам удобно, пока у вас есть какой-то способ преобразовать сохраненное значение обратно в объект состояния.

Но ты все еще используешь «if»

Да, он очень важен. Особенно при использовании в качестве проверочных точек. Именно сочетание If-else является основной причиной головных болей поддержки кода.

Это же куча дополнительных классов

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

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

Узнайте подробности, как получить востребованную профессию с нуля или Level Up по навыкам и зарплате, пройдя платные онлайн-курсы SkillFactory:

Читать еще

Бюджетная GSM сигнализация с мозгами из Arduino

Внимание! Команды выделенные жирным шрифтом могут быть выполнены только с основного номера, так как отвечают за конфигурацию устройства. Остальные команды могут быть выполнены с номеров с признаком «Management».

SMS — команды управления не чувствительны к регистру:
AddPhone — Добавить номер телефона. Всего может быть добавлено не более 9 номеров + 1 основной номер который автоматически сохраняется в память при первом звонке на устройство после сброса на заводские установки командами ResetPhone или FullReset. Т.е. кто первый позвонил на устройство после его сброска на заводские установки тот и и «главный», этот номер заносится в первую ячейку памяти и его невозможно изменить или удалить через смс. Возможно добавить два одинаковых номера, но тогда у номера дубликата автоматически остаётся только признак «r» — исключительно для повторного голосового вызова.

Пример команды:

До версии GSM_2017_05_26-20-22.hex:

AddPhone:2+71234567891m

AddPhone:3+71234567892a

AddPhone:4+71234567893

AddPhone:5+71234567894ma

Начиная с версии GSM_2017_05_26-20-22.hex:

AddPhone:2+71234567891mrsp

AddPhone:3+71234567892ms

AddPhone:4+71234567893sp

AddPhone:5+71234567894r

Синтаксис команды:

AddPhone — команда

: — разделитель

5 — записать в пятую ячейку памяти

+71234567890 — номер телефона

До версии GSM_2017_05_26-20-22.hex:

а — Параметр «Alarm» — на номера с этим параметром будут отправляться смс — сообщения о срабатывании сигнализации и сообщения постановке или снятии с охраны.
Начиная с версии GSM_2017_05_26-20-22.hex:

m — Параметр «Management» — разрешено управление сигнализацией

s — Параметр «SMS» — будет отправлено sms сообщение при срабатывании датчиков

r — Параметр «Ring» — будет совершен голосовой вызов при срабатывании датчиков

p — Параметр «Power» — будет отправлено sms сообщение при включении/отключении внешнего питания

i — Параметр «Info» — будет отправлено sms сообщение при постановке или снятии с охраны
При отсутствии параметров «m», «s», «r», «p»,«i» телефон заносится в память, но никак не используется.

DeletePhone — Удалить номер телефона.
Пример команды:

DeletePhone:+71234567891

Синтаксис команды:

DeletePhone — команда

: — разделитель

+71234567891 — номер телефона

EditMainPhone — Изменить параметры «s», «r», «p», «i» основного телефона, этот номер занесён в первую ячейку памяти.
Пример команды:

EditMainPhone:spri

Синтаксис команды:

EditMainPhone — команда

: — разделитель

srpi — параметры

BalanceNum — Изменение номера запроса баланса и обработка длины ответа запроса. Значение по умолчанию для Beeline: #100#L22.
Пример команды:

BalanceNum:#103#L24

Синтаксис команды:

BalanceNum — команда

: — разделитель

#103# — номер запроса баланса

L24 — Длина (len) пересылаемого ответа 24 символа, обрезаем спам из запроса баланса.

EditSensor — Изменить название датчика и логический уровень срабатывания. Всего может быть не более 8 дополнительных датчиков. После изменения параметров необходима перезагрузка устройства.
Пример команды:

EditSensor:1+Datchik dvizheniya v koridore#h

EditSensor:2+Gerkon na okne

Синтаксис команды:

EditSensor — команда

: — разделитель

1 — записать в первую ячейку памяти

+ — разделитель

Datchik dvizheniya v koridore — название датчика, не может превышать 36 символов, включая пробелы.

#h — Признак высокого логического уровня с датчика, при получении которого сработает сигнализация. Если отсутствует «#h», сигнализация будет срабатывать при получении с датчика низкого логического уровня.

SleepTime — Время «засыпания» сигнализации при получении смс — команды «Pause», указывается в минутах. Значение по умолчанию: 15, не может быть менее 1 и более 60.
Пример команды:

SleepTime:20

Синтаксис команды:

SleepTime — команда

: — разделитель

20 — 20 минут «сна».

AlarmPinTime — Время на которое включается/выключается тревожный/инверсный пин, указывается в секундах. Значение по умолчанию: 60, не может быть менее 1 секунды и более 43200 секунд (12 часов).
Пример команды:

AlarmPinTime:30

Синтаксис команды:

AlarmPinTime — команда

: — разделитель

30 — 30 секунд включения/выключения тревожного пина.

DelayBeforeGuard — Время до постановки устройства на охрану, после получения соответствующей команды.
Пример команды:

DelayBeforeGuard:25

Синтаксис команды:

DelayBeforeGuard — команда

: — разделитель

25 — 25 секунд до постановки на охрану

DelayBeforeAlarm — Время по истечении которого будет отправлено «тревожное» смс уведомление, если за этот период времени сигнализация не была снята с охраны. Заменена расширенными командами начиная с версии GSM_2017_08_10-12-17.hex
Пример команды:

DelayBeforeAlarm:40

Синтаксис команды:

DelayBeforeAlarm — команда

: — разделитель

40 — 40 секунд до отправки «тревожного» уведомления

WatchPowerTime — Время в минутах по истечении которого будет отправлено смс сообщение об отключении внешнего источника питания. Если внешнее питание будет восстановлено до истечения установленного времени, то сообщение не будет отправлено.
Пример команды:

WatchPowerTime:5

Синтаксис команды:

WatchPowerTime — команда

: — разделитель

5 — 5 минут до отправки смс сообщения

RingTime — Длительность тревожного голосового вызова, параметр может иметь значение от 10 до 255 секунд.
Пример команды:

RingTime:40

Синтаксис команды:

RingTime — команда

: — разделитель

40 — 40 длительность вызова составит 40 секунд, после чего будет вызван следующий абонент.

ModemID — Принудительная установка модели используемого модема. Возможные значения: 0 — автоопределение модема, 1 — M590, 2 — SIM800l, 3 — A6_Mini.
Пример команды:

ModemID:2

Синтаксис команды:

ModemID — команда

: — разделитель

2 — ID модема.

ExtDeviceTime — Количество секунд на которое изменится уровень сигнала на выходе управления внешним устройством.
Пример команды:

ExtDeviceTime:5

Синтаксис команды:

ExtDeviceTime- команда

: — разделитель

5 — 5 секунд

ExtDeviceLevelLow — Внешнее устройство подключенное к выходу A3 управляется низким уровнем сигнала (GND). На выходе по умолчанию будет присутствовать высокий уровень +5В, пока не поступит команда управления внешним устройством
ExtDeviceLevelHigh — Внешнее устройство подключенное к выходу A3 управляется высоким уровнем сигнала (+5V). На выходе по умолчанию будет присутствовать низкий уровень GND, пока не поступит команда управления внешним устройством

ResetSensor — сброс параметров датчиков расширителя порта

ResetConfig — сброс настроек на заводские установки

ResetPhone — удаление из памяти всех телефонных номеров

FullReset — сброс настроек, удаление из памяти всех телефонных номеров, восстановление значения по умолчанию команды BalanceNum.

RingOn — включить уведомление звонком на «главный» номер записанный в первую ячейку памяти при срабатывании датчика. Удалена начиная с версии GSM_2017_06_11-00-07.hex
RingOff — выключить уведомление звонком при срабатывании датчика. Удалена начиная с версии GSM_2017_06_11-00-07. hex

SmsOn — включить sms-уведомление при срабатывании датчика. Удалена начиная с версии GSM_2017_08_10-12-17.hex
SmsOff — выключить sms-уведомление при срабатывании датчика. Удалена начиная с версии GSM_2017_08_10-12-17.hex

PIROn — включить обработку датчика движения
PIROff — выключить обработку датчика движения

ReedSwitchOn — включить обработку основного герконового датчика
ReedSwitchOff — выключить обработку основного герконового датчика

WatchPowerOn — включить контроль внешнего питания, смс сообщение об отключении внешнего питания будет отправлено при условии что сигнализация поставлена на охрану. Удалена начиная с версии GSM_2017_03_01-23-37.

WatchPowerOn1 — включить контроль внешнего питания, смс сообщение об отключении внешнего питания будет отправлено при условии что сигнализация поставлена на охрану.
WatchPowerOn2 — включить контроль внешнего питания, смс сообщение об отключении внешнего питания в любом случае будет отправлено

WatchPowerOff — выключить контроль внешнего питания

GuardButtonOn — управление сигнализацией внешними устройствами или кнопкой включено Удалена начиная с версии GSM_2017_04_16-12-00.
GuardButtonOn1 — функция постановки или снятия охраны внешними устройствами или кнопкой включена
GuardButtonOn2 — функция только постановки на охрану внешними устройствами или кнопкой включена, снятие с охраны производится по звонку на устройство или с помощью смс команды.
GuardButtonOff — управление сигнализацией внешними устройствами или кнопкой выключено

Reboot — перезагрузка устройства (Arduino)

PCFForceOn — постоянный мониторинг группы всех датчиков модуля расширения
PCFForceOff — мониторинг группы всех датчиков модуля расширения только при постановке устройства на охрану

MainSensorLevelHigh — тревожное уведомление будет отправлено при появлении сигнала высокого уровня (+5 В) на входе (D6) от датчика
MainSensorLevelLow — тревожное уведомление будет отправлено при появлении сигнала низкого уровня (GND) на входе (D6) от датчика
MainSensorLevelOff — обработка датчика на входе (D6) отключена

SecondSensorLevelHigh — тревожное уведомление будет отправлено при появлении сигнала высокого уровня (+5 В) на входе (A0) от датчика
SecondSensorLevelLow — тревожное уведомление будет отправлено при появлении сигнала низкого уровня (GND) на входе (A0) от датчика
SecondSensorLevelOff — обработка датчика на входе (A0) отключена

MainDelayBeforeAlarm — время по истечении которого будет отправлено «тревожное» смс уведомление при срабатывании основного датчика (D6), если за этот период времени сигнализация не была снята с охраны. Синтаксис аналогичен команде DelayBeforeAlarm.
SecondDelayBeforeAlarm — время по истечении которого будет отправлено «тревожное» смс уведомление при срабатывании дополнительного датчика (A0), если за этот период времени сигнализация не была снята с охраны. Синтаксис аналогичен команде DelayBeforeAlarm.
PCFDelayBeforeAlarm — время по истечении которого будет отправлено «тревожное» смс уведомление при срабатывании датчиков платы расширения (PCF8574), если за этот период времени сигнализация не была снята с охраны. Синтаксис аналогичен команде DelayBeforeAlarm.

GuardOn — поставить на охрану

GuardOff — снять охраны

Open — команда управления внешним устройством

Info — проверить состояние, в ответ на это сообщение будет отправлено sms с информацией о том с какого номера была включена/выключена охрана

Pause — приостанавливает работу системы на время установленное командой sleeptime в минутах, система не реагирует на срабатывания датчика.

TestOn — включается тестовый режим, мигает синим светодиодом.

TestOff — выключается тестовый режим.

LedOff — выключает светодиод режима ожидания.

LedOn — включает светодиод режима ожидания.

Money — запроса баланса.

ClearSms — Удалить из памяти все sms

Консольные команды (до версии GSM_2017_04_24-13-22.hex) — вводятся в мониторе порта Arduino IDE:

AddPhone — аналогична sms-команде AddPhone

DeletePhone — аналогична sms-команде DeletePhone

EditSensor — аналогична sms-команде EditSensor

ListPhone — вывод в монитор порта списка сохранённых в памяти телефонов

ResetConfig — аналогична sms-команде ResetConfig

ResetPhone — аналогична sms-команде ResetPhone

FullReset — аналогична sms-команде FullReset

ClearSms — аналогична sms-команде ClearSms

WatchPowerOn1 — аналогична sms-команде WatchPowerOn1

WatchPowerOn2 — аналогична sms-команде WatchPowerOn2

WatchPowerOff — аналогична sms-команде WatchPowerOff

GuardButtonOn — аналогична sms-команде GuardButtonOn . Удалена начиная с версии GSM_2017_04_16-12-00

GuardButtonOn1 — аналогична sms-команде GuardButtonOn1

GuardButtonOn2 — аналогична sms-команде GuardButtonOn2

GuardButtonOff — аналогична sms-команде GuardButtonOff

Memtest — тест энергонезависимой памяти устройства, все настройки устройства будут сброшены, аналогично команде FullReset.

I2CScan — поиск и инициализация поддерживаемых устройств на шине I2C.

ListConfig — вывод в монитор порта текущей конфигурации устройства.

ListSensor — вывод в монитор порта текущей конфигурации датчиков.

Программа для лабораторного блока питания с микропроцессорным управлением на ATMega16

Обещанный релиз первого варианта «правильной» программы к статье Сделай сам лабораторный блок питания с микропроцессорным управлением на ATMega16. Вторая жизнь Back-UPS.

Содержание / Contents

Это свободная программа; вы можете повторно распространять ее и/или модифицировать ее в соответствии с Универсальной Общественной Лицензией GNU. Я уверен, что в Датагории исключительно разумные жители, для которых дальнейший юридическо-образный текст не более чем констатация того состояния дел, как оно должно бы быть, так что скрытый текст можно не читать и переходить сразу к более интересному следующему разделу.
Программа Power_Man служит для управления и отображения данных совместно с лабораторным блоком питания с микропроцессорным управлением на ATMega16.

С проектом можно ознакомиться на сайте datagor.ru

Автор заранее благодарит за любую помощь в развитии проекта.
Автор заинтересован в развитии проекта, однако данный проект является частным и некоммерческим, начат и развивается в свободное от основной работы автора время, поэтому оперативная техническая поддержка не гарантируется.

Copyright (C) 2012г. Асташев Максим Евгеньевич, к.б.н.

Это свободная программа; вы можете повторно распространять ее и/или модифицировать ее в соответствии с Универсальной Общественной Лицензией
GNU, опубликованной Фондом Свободного ПО; либо версии 2, либо (по вашему выбору) любой более поздней версии.

Эта программа распространяется в надежде, что она будет полезной, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без подразумеваемых гарантий КОММЕРЧЕСКОЙ ЦЕННОСТИ или ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ. Для получения подробных сведений смотрите Универсальную Общественную Лицензию GNU.
Текст GNU GPL на английском языке вы можете прочитать на официальном сайте или Русский неофициальный перевод. Данный текст также является частью исходного кода программы как описание ее функциональных особенностей, и лицензия также распространяется и на данный текст.

В программе использована библиотека Ararat Synapse, Copyright (c)2001-2011, Lukas Gebauer.
Условия распространия библиотеки смотрите в исходниках или на сайте //synapse.ararat.cz/doku.php. Права на текст этой библиотеки принадлежат ее авторам.

Я написал программу (условное название Power_Man, в честь первого донора запчастей для блока питания) в среде программирования Lazarus версии 0.9.30.4 на языке программирования Free Pascal. Данная среда программирования распространяется сайтом //www.lazarus.freepascal.org по лицензии GNU GPL, т.е. без особых ограничений по использованию и распространению.

Lazarus — это Delphi-подобная интегрированная среда разработки для создания графических и консольных приложений при помощи компилятора Free Pascal. Free Pascal — это компилятор языков Pascal и Object Pascal, работающий под Windows, Linux, Mac OS X, FreeBSD, и другими ОС.

Lazarus позволяет разрабатывать программы для всех вышеперечисленных платформ в Delphi-подобном окружении. Эта среда разработки является инструментом RAD, включающим в себя дизайнер форм.

В архиве я поместил все необходимые исходники (если чего забыл — пишите), таким образом Вы можете модифицировать программу под собственные нужды.

Бинарный код программы Power_Man.exe скомпилирован для работы в операционной системе MS Windows (c), однако при минимальных изменениях в исходном коде (касающихся наименования COM-портов в разных ОС) ее можно перекомпилировать для всех вышеперечисленных платформ.

Данная версия программы 0.0.1 носит ознакомительно-информативный характер, и я рассматриваю ее как декларацию о намерениях. Полное и безошибочное выполнение всех заявленных характеристик я не гарантирую. Предложения по дополнению функционала приветствую и рассмотрю. Я надеюсь, что весь этот проект будет жить как развивающийся и уточняющийся поиск единственно правильного решения, и надеюсь на интерес и участие в дополнении проекта. Так что, если Вы вдруг изменили код программы с целью устранения какой-либо ошибки, или дополнили функционал, я прошу Вас сообщить мне об этом через личное сообщение на сайте datagor.ru с указанием выявленной проблемы или описанием дополнения, и замен/дополнений в исходном тексте программы. Таким образом я надеюсь поддерживать какую-то целостность проекта.

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

Организация главного меню:
Файл->Сохранить — Данная команда позволяет сохранить измеренные данные, хранящиеся во временном файле temp.txt в заданный пользователем файл. Сам файл temp.txt формируется при активации команды Аппарат->Подключить, и заполняется во время работы программы и в случае непредвиденных сбоев дает возможность вручную восстановить данные.
Аппарат->Подключить — Данная команда проводит настройку СОМ-порта компьютера (задает скорость, количество бит, четность и прочие настройки) и запускает процедуру получения данных с блока питания и управления блоком. Номер COM-порта программа узнает из настройки «СОМ-порт» с вкладки настройки.
Протокол->Новый — Данная команда очищает таблицу протокола
Протокол->Открыть — Данная команда загружает предварительно составленный и сохраненный протокол из файла.
Протокол->Сохранить — Данная команда сохраняет предварительно составленный протокол в файл.

Ниже главного меню расположены настройки ограничений тока и напряжения. Для передачи настройки в блок питания надо после редактирования настройки нажать Enter. Только в этом случае в блок будет отправлена соответствующая команда «i=» или «u=». Кнопка «Сохранить в EEPROM» отправляет в блок команду «store».

Организация вкладок главного окна:

Вкладка Графики содержит два поля для графиков зависимости тока и напряжения от времени. На графиках зеленой линией отображаются реально измеренные величины, красной — заданные для данных моментов времени ограничения тока и напряжения.

Вкладка Протокол содержит средства для элементарного программирования динамического поведения блока питания.

Вкладка Настройки содержит настройки номера COM-порта, интервала обмена с блоком питания и замечания относительно лицензии.

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

Протокол представляет собой набор следующих друг за другом установок ограничения тока и напряжения, организованных в циклически повторяющиеся «Прогоны», состоящие, в свою очередь, из «шагов». Настройка шагов организована в виде таблицы, по строкам которой расположены различные параметры шагов, а в столбцах настройки для конкретного номера шага.
Каждый шаг включает в себя набор следующих настроек:
Тип шага — задает форму изменения установок тока и напряжения (ступенька или пила) или признак завершения прогона (Пусто). При установке ступенька установки тока и напряжения вступают в силу вначале шага и действуют постоянно в течении всего данного шага. При установке пила установки тока и напряжения линейно изменяются в течении всего шага от установок предыдущего шага до достижения установок данного шага в конце данного шага. Таким образом можно просканировать некоторый диапазон значений тока или напряжения за один шаг.
Напряжение (В) — задает установки ограничения напряжения для данного шага.
Ток (А) — задает установки ограничения тока для данного шага.
Интервал (мин) — задает промежуток времени в течение которого будет выполняться данный шаг. Переход к следующему шагу возможен и по выполнению условий, заданных настройками Условие перехода и Значение перехода. Однако расчет скоростей изменения установок тока и напряжения при типе шага «пила» происходит именно с учетом данной настройки. Сочетание типе шага «пила» с условием перехода отличным от «Время» может привести к непредсказуемому поведению программы!
Шаг напряжения (В) — задает шаг изменения параметра Напряжение (В) при переходе от одного прогона к другому. Например, если заданы значения Напряжение (В) = 0, Шаг напряжения (В)=1, Число прогонов=11, то на первом прогоне будет установлено напряжение 0В, на втором — 1В, и так далее до 10В на последнем 11-м прогоне. Таким образом на каждом прогоне можно установить линейно изменяющуюся характеристику.
Шаг тока (A) — задает шаг изменения параметра Ток (А) при переходе от одного прогона к другому.
Шаг интервала (мин) — задает шаг изменения параметра Интервал (мин) при переходе от одного прогона к другому. Таким образом на каждом прогоне можно задать увеличивающееся(уменьшающееся) время выполнения данного шага.
Условие перехода — задает условие перехода к следующему шагу (прогону, если тип следующего шага равен «Пусто»). Допустимые условия: «Время«, «V>«, «V«, «I«, «dV«, «dI<«. Это позволяет реализовать условный способ перехода к следующему шагу протокола. Например, при зарядке кислотного аккумулятора 12В емкостью 7Ач можно задать Напряжение (В) = 14,4В, Ток (А) = 0,7А, Условие перехода I<, Значение перехода = 0,3. При этом аккумулятор будет заряжаться постоянным током 0,7А до достижения напряжения 14,4В, далее напряжение стабилизируется и зарядный ток будет уменьшаться, и когда достигнет 0,3А шаг закончится, прогон переместится на следующий шаг. При необходимости разрядки свинцового аккумулятора 12В 7Ач до напряжения 12В можно параллельно к аккумулятору подключить резистор 20 Ом и составить такой 1-й шаг: Напряжение (В) = 14,4В, Ток (А) = 0А, Условие перехода V<, Значение перехода = 12,1. 2-й шаг: Напряжение (В) = 13,8В, Ток (А) = 1А, Интервал (мин) = 900. В этом случае аккумулятор разрядится через резистор до напряжения 12В и затем перейдет в режим буферной зарядки. В случае зарядки NiMH аккумуляторов с помощью Условие перехода dV
Значение перехода — задает пороговое значение для предыдущей настройки условного перехода.
Слева от таблицы настройки шагов расположен график изменения установок для предварительного просмотра формируемого протокола.

Внимание! Тестовые прогоны составленных Вами протоколов я рекомендую проводить при непрерывном контроле правильного выполнения программы на старых, отслуживших свое аккумуляторах или модельных нагрузках! Я не несу и не собираюсь нести ответственность за возможную порчу Вашего оборудования в результате использования этой программы!

По окончанию сеанса работы с программой основные настройки (положение и размер окон, графиков, установки напряжения и тока, интервал обмена с блоком питания, номер используемого порта) автоматически сохраняются в файле Main_settings.xml и при следующем запуске программы используются для стартовой настройки.

Кроме того, в папке «Простой текстовый терминал» находится простая программа (тоже написаная в Lazarus) — эквивалент терминала Arduino, которую можно использовать на начальном этапе проверки и настройки блока питания путем отправки текстовых команд и просмотра ответа от него.

Архив с программой и исходниками
▼ Файловый сервис недоступен. Зарегистрируйтесь или авторизуйтесь на сайте.

Камрад, рассмотри датагорские рекомендации

🌻 Купон до 1000₽ для новичка на Aliexpress

Никогда не затаривался у китайцев? Пришло время начать!
Камрад, регистрируйся на Али по нашей ссылке.
Ты получишь скидочный купон на первый заказ. Не тяни, условия акции меняются.

🌼 Полезные и проверенные железяки, можно брать

Куплено и опробовано читателями или в лаборатории редакции.

 

Arduino / Обработка против .NET Micro Framework? Лучший язык? Лучшее оборудование?

Если вы хотите перейти от новичка к среднему языку, вам нужно выучить язык C. Даже если вы отложите всю дискуссию о блокировке Windows, вам нужно хорошо разбираться в программировании на C, прежде чем вы сможете выполнять качественную работу над микроконтроллер на языке более высокого уровня, как .NET Micro или C ++.

Встроенные системы состоят из пирамиды знаний, и вам действительно нужно знать хотя бы некоторые из каждого шага, чтобы стать хорошим дизайнером:
^ Код пользователя
^^ Операционные системы
^^^ Язык C
^^^^ Язык ассемблера
^^^ ^^ Архитектура микроконтроллера
^^^^^^ Цифровой дизайн
^^^^^^^ Полупроводники ^^^^^^^^
Базовая электроника (закон Ома)

Платформа Arduino обеспечивает удобный хобби для любителей пирамиды где-то между языком Си и операционной системой.

Конкретные к вашему платформе .NET Framework Micro вопроса, О говорит:

Типичное устройство .NET Micro Framework имеет 32-разрядный процессор без модуля управления внешней памятью (MMU) и может иметь всего 64 КБ оперативной памяти (RAM).

Кроме того, брошюра отличает ее от Windows Mobile , Windows Embedded , CE 6.0 и .NET Compact Framework и сравнивает с Linux, Real-Time, Java и пользовательскими операционными системами. Это огромный скачок от фреймворка Arduino / Processing.

Ваш Arduino имеет 8-битный процессор с 1 КБ ОЗУ. В дополнение к 8-битной и 32-битной потере мощности, он также работает менее чем в два раза быстрее, чем большинство перечисленных процессоров. Хотя я бы не стал отговаривать вас от перехода на 32-разрядный процессор, я бы порекомендовал его как шаг от среднего до продвинутого.

Это действительно легко использовать много времени и памяти с несколькими строками в C # или C ++, которые незначительны для двухъядерного процессора, работающего на пару гигагерц с гигабайтами оперативной памяти, но которые могут иметь огромное значение для встроенного устройства , Пока вы не разбираетесь в ассемблере и / или C или не являетесь гуру в C # или C ++, я бы не рекомендовал использовать его для встроенного программирования.

Итак, я бы начал с загрузки WinAVR и запрограммировал бы простую процедуру мигания светодиода на C. Если C полностью вас смущает, сделайте немного собственного кода («Hello World») на вашем ПК, а затем перейдите к микроконтроллер, но это не должно быть необходимым. Затем перейдите к общению через UART, начните использовать прерывания и переделайте некоторые из ваших проектов Arduino на C. Затем найдите (или создайте!) Новую плату разработки с другим микроконтроллером, возможно, PIC или ARM , и некоторые лакомства, такие как ЖК-экран, Ethernet, SD-карта или все, что вы хотите, и попробуйте изучить новую систему. Добравшись туда, вы будете лучше знать, куда хотите пойти.

Мы будем здесь, чтобы помочь вам на этом пути!

Мигалка на ассемблере и ATtiny2313

Для программирования микроконтроллеров существуют два основных языка — это язык Си и Ассемблер. Бывают и другие языки но Си и Ассемблер используются чаще других т.к. их можно использовать для создания программ оптимально использующих ресурсы микроконтроллера. Язык Си более прост в изучении чем Ассемблер, более прост для понимания человеком вообще (на нем можно писать более читабельный код) а также более просто переносим между разными микроконтроллерами и вообще разными устройствами. Ассемблер же не так прост в понимании и хуже переносим т.к. он имеет множество разных команд которые отличаются друг от друга при программировании разных микроконтроллеров и устройств и даже при программировании одного и того же устройства но при использовании разных компиляторов. Однако язык ассемблер дает больше возможностей управлять ресурсами микроконтроллера. Программы написанные на Ассемблере могут быть более оптимальными чем программы на Си. Также изучение Ассемблера позволяет лучше понять работу микроконтроллера и писать более оптимальные программы на языке Си. Поэтому данный низкоуровневый язык стоит изучить даже если необходимость в его изучении и применении на практике никогда не возникала. Данный язык сильно отличается для разных микроконтроллеров но всё таки некоторые общие принципы наверняка должны присутствовать в той или иной степени для разных устройств. Начать скорее всего будет лучше с какого нибудь простого микроконтроллера. Например, для старта, можно взять ATtiny2313 и написать для него мигалку а если это получиться то делать далее уже более сложные устройства и переходить на более сложные микроконтроллеры например какие нибудь из STM32. Чтобы писать саму программу можно использовать какой либо текстовый редактор. Обычно в ОС Windows присутствует текстовый редактор «блокнот». После написания программы в «блокноте»её можно сохранить в формате с расширением .asm потом откомпилировать в файл с расширением .hex каким нибудь компилятором ассемблера для микроконтроллеров avr напр. компилятором avra (который можно скачать по ссылке https://sourceforge.net/projects/avra/files/1.2.3/ (для windows нужно скачать avra-1.2.3-win32.zip)) (для установки достаточно разархивировать) и записать полученный файл с расширением .hex в микроконтроллер утилитой avrdude (устанавливается вместе с arduino IDE, WinAVR или отдельно) через программатор. Рассмотрим схему устройства которое будем программировать:

Рисунок 1 — Светодиодная мигалка на микроконтроллере ATtiny2313 с программатором

На рисунке выше приведена схема светодиодной мигалки на микроконтроллере ATtiny2313 с программатором. Микроконтроллер ATtiny2313 имеет встроенный тактовый RC генератор и встроенный внутренний резистор для подтяжки вывода reset к нужному для работы микроконтроллера уровню также ему не нужно много внешних конденсаторов и прочей дополнительной обвязки (этим данный микроконтроллер прост и хорош). В качестве программатора можно использовать Arduino с записанным в неё скетчем ISP программатора:

Рисунок 2 — Светодиодная мигалка на микроконтроллере ATtiny2313 с программатором на Arduino Uno

Если все необходимые элементы имеются то можно начинать писать программу. Давайте рассмотрим простую программу мигания светодиодом на Ассемблере:
.CSEG ; начало сегмента кода
.ORG 0x0000 ; начальное значение для адресации

; — устанавливаем пин 0 порта D на вывод —
LDI R16, 0b00000001 ; поместим в регистр R16 число 1
OUT 0x11, R16 ; загрузим значение из регистра R16 в DDRD (адрес DDRD = 0x11 (attiny2313,atmega8))

; — основной цикл программы ( адрес PORTD = 0x12 (attiny2313,atmega8) ) —
Start:
SBI 0x12, 0 ; подача на пин 0 порта D высокого уровня

; — задержка 1 —
ldi r18, 250
ldi r19, 250
L1: dec r19
brne L1
dec r18
brne L1

CBI 0x12, 0 ; подача на пин 0 порта D низкого уровня

; — задержка 2 —
ldi r18, 250
ldi r19, 250
L2: dec r19
brne L2
dec r18
brne L2

RJMP Start

Ассемблер, в отличии от Си, регистронезависимый язык поэтому команды можно писать и большими буквами и маленькими и вперемешку. Команды которые начинаются с точки называются директивами. Директивы — это команды не для микроконтроллера а для компилятора. Директива .CSEG означает начало сегмента кода, помимо сегмента кода существует также сегмент данных и сегмент EEPROM. Директива .ORG указывает адрес с которого начинается сегмент. В ассемблере можно делать комментарии т.е. строки которые игнорируются компилятором и нужны для помощи программисту, комментарии начинаются с точки с запятой и завершаются концом строки. Команда LDI нужна для помещения в регистр какой либо константы. Константу можно записать в десятичном, шестнадцатеричном или двоичном виде. Если она записана в двоичном виде то перед ней ставиться нолик и английская буква b т.е. например 0b00000001. Двоичные числа удобны для конфигурирования порта микроконтроллера т.к. по ним наглядно видно в каком бите есть ноль а в каком единица. Если константа пишется в десятичном виде то перед ней ставить ничего не надо а надо просто написать число. Если константа пишется в шестнадцатеричном виде то перед ней ставиться 0x. Чтобы настроить второй пин микроконтроллера на выход нужно в записать единицу в нулевой разряд регистра DDRD. DDRD — это просто название регистра для удобства, на самом деле важно знать что этот регистр имеет какой то адрес в памяти и для того чтобы записать в этот регистр какое либо число и т.о. поместить либо ноль либо единицу в нужный его разряд, нужно записать это число по соответствующему адресу для регистра DDRD в микроконтроллере ATtiny2313 этот 0x11. Для того чтобы настроить второй пин микроконтроллера ATtiny2313 на выход мы сначала командой LDI записываем в регистр R16 число 0b00000001 а потом командой OUT из регистра R16 мы это число помещаем в регистр DDRD по тому что записать сразу константу в DDRD нельзя. Далее идет основной цикл программы т.е. «бесконечный» цикл в котором выполняются основные постоянные действия. Чтобы организовать этот цикл на ассемблере используется оператор безусловного перехода RJMP. В самом низу кода мы видим строку RJMP Start а в самом верху основного цикла мы видим метку Start. Команда RJMP просто переводит выполнение программы на то место где стоит метка т.о. если метку поставить перед этой командой после которой идет название этой метки то можно получить бесконечный цикл. Командой SBI мы устанавливаем высокий уровень т.е. пять вольт на нужном пине микроконтроллера. Если мы используем пин который связан с нулевым битом порта D то после команды SBI мы пишем адрес порта D (для микроконтроллера ATtiny2313 это 0x12) ставим запятую и после этого пишем номер бита который мы хотим установить в логическую единицу чтобы на нужном пине стало пять вольт. Аналогично командой CBI мы делаем ноль вольт на нужном нам пине. Для создания задержки сначала в регистр r18 записывается число 250 (максимальное которое можно записать = 255, минимальное = 0) потом 250 записывается в регистр r19. После чего командой dec (декремент) происходит уменьшение на единицу числа находящегося в регистре r19. Далее имеется команда brne — это оператор условного перехода т.е. он переносит выполнение программы на место с меткой при выполнении условия. Если результатом операции предшествовавшей данной команде был ноль то перехода туда где стоит метка не происходит и выполнение программы продолжается, если был получился не ноль то происходит переход на строку с меткой в данном случае это L1. Т.о. получается цикл выход из которого происходит тогда когда число в регистре r19 становиться равным нулю. Далее происходит декремент числа в регистре r18 и так как сразу там ноль не получиться то переход происходит на строку с меткой L1 а там стоит декремент числа в регистре r18 но так как там уже ноль то декремент делает там 255 и всё повторяется заново т.о. получается вложенный цикл. Эти циклы выполняются какое то время и делают задержку для того чтобы можно было наблюдать мигание светодиода.
После того как программа написана (например в имеющемся в windows текстовом редакторе «блокнот» или любом другом подходящем текстовом редакторе (неважно каком (это дело вкуса (на результат не повлияет (если текстовый редактор подходит для данных целей и позволяет сохранять текст в формате .asm))))) её можно сохранить в формате .asm и откомпилировать в cmd (командной строке windows) командой:
путь_до_компилятора_avra путь_до_файла.asm
например:
F:\avra-1.2.3\bin\avra F:\avr\blink.asm
Если компиляция пройдет успешно то компилятор создаст файл с расширением .hex который можно загрузить в микроконтроллер например командой:
avrdude -c avrisp -P COM3 -b 19200 -p t2313 -U flash:w:blink.hex
(avrdude перед этим надо установить на компьютер)
Эта команда сработает если используется микроконтроллер ATtiny2313 (-p t2313), программатор avrisp (-c avrisp (он же arduino, он же stk500, он же «пять проводков» (avrdude не поймет «пять проводков» (остальное должна понять)))) который подключен к порту который называется COM3 (-P COM3) (если называется по другому то в команду надо вписать соответствующее название). blink.hex — это название откомпилированного файла который надо загрузить в микроконтроллер. При этом в данной программе не прописана установка фьюзов и считается что микроконтроллер либо новый с завод и на нем установлены фьюзы по молчанию либо фьюзы ранее были установлены как надо и менять их не требуется. Если hex файл успешно запишется то светодиод должен замигать!

КАРТА БЛОГА (содержание)

Лекция по языку ассемблера и компьютерной архитектуре (CS 301)

Лекция по языку ассемблера и компьютерной архитектуре (CS
301)

CS 301:
Лекция по программированию на ассемблере, доктор Лоулор

Прыжок
инструкция, такая как «jmp», просто переключает ЦП на выполнение
другой фрагмент кода. Это ассемблерный эквивалент
«goto», но, в отличие от goto, прыжки не считаются постыдными в
сборка. (Дейкстра написал в 1968 году статью под названием «Goto
Считается вредным «.С тех пор goto обычно
считались вредными, за исключением
Linux.)

Вы говорите, куда перейти, используя «ярлык перехода», который может быть любым
имя строки с двоеточием после него. (Такой же точный синтаксис
используется в C / C ++)

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

Инструкция Полезно для …
jmp Всегда прыгать
je Перейти, если cmp равно
jne Перейти, если cmp не равно
jg Подписано>
(больше)
jge Подпись> =
jl Подпись < (менее)
ил Подпись <=
и Без знака> (вверху)
иже Без знака> =
сп Без знака <(внизу)
jbe Без знака <=
jecxz Перейти, если ecx 0
(Серьезно !?)
jc Перейти при переносе: используется для неподписанных
переполнение,
или multiprecision добавить
ио Перейти, если там был подписан
перелив

Есть также
«n» версий НЕ для каждого прыжка; например «jno» прыгает, если есть
НЕ переполняется.

Условный
Прыжки: разветвление в сборке

В сборе,
все ветвления выполняются с использованием двух типов инструкций:

  • Команда сравнения, такая как «cmp», сравнивает два значения.
    Внутри он делает это путем их вычитания.
  • Команда условного перехода, например «je» (переход, если равен),
    делает переход где-нибудь, если два значения удовлетворяют
    правильное состояние. Например, если значения равны,
    их вычитание дает ноль, поэтому «je» то же самое, что «jz».

Вот как
используйте сравнение и прыжок, если равно («je»):

 mov eax, 3 
cmp eax, 3; как eax по сравнению с 3?
je lemme_outta_here; если равно, то прыгать
mov eax, 999; <- не выполняется * если * перепрыгиваем
lemme_outta_here:
ret

(Попробуйте
это сейчас в NetRun!)

Вот сравнить
и прыжок-если-меньше-чем («jl»):

 mov eax, 1 
cmp eax, 3; как eax по сравнению с 3?
jl lemme_outta_here; если меньше, то перескочить на
mov eax, 999; <- не выполняется * если * перепрыгиваем
lemme_outta_here:
ret

(Попробуйте
это сейчас в NetRun!)

C ++
эквивалентно сравнить-и-прыгать-если-что бы то ни было «если (что-то) перейти
где-то;».

Петли

Чтобы зациклиться, вы
просто вернитесь к началу кода. Где-то ты делаешь
нужно условие, или вы создали бесконечный цикл!

В этом
Например, мы отсчитываем edi, пока он не достигнет нуля.

; edi - наш первый аргумент функции 
mov eax, 0; сумма добавлена ​​здесь Начало: ; цикл начинается здесь добавить eax, 10; добавлять каждый раз по петле sub edi, 1; приращение цикла cmp edi, 0; петлевой тест jg start; продолжить цикл, если edi> 0 ret

(Попробуйте
это сейчас в NetRun!)

Это
построчно эквивалентно этому коду C ++:

 int foo (int bar) {
int sum = 0;

Начало:
сумма + = 10;
бар--;
если (bar> 0) перейти к началу;

сумма возврата;
} 
 (Попробуйте сейчас в NetRun!) 

Оф
конечно, это очень уродливый код на C ++! Это более идиоматично
напишите здесь цикл «for»:

 int foo (int bar) {
int sum = 0;
for (int count = bar; count> 0; count--)
сумма + = 10;
сумма возврата;
} 

(Попробуйте
это сейчас в NetRun!)

Подробнее
Сложный поток управления: C —

Можно
на самом деле написать очень своеобразный вариант C ++, где «если»
операторы содержат только операторы goto.Мое шутливое название для
этот C ++ в стиле сборки — «C—»: вы используете только «+ =» и «* =»
арифметика и «если (простой тест) перейти куда-нибудь»; управление потоком.

Например, это совершенно допустимый C ++ в стиле «C—«:

 int main () {
int я = 0;
if (i> = 10) goto byebye;
std :: cout << "Не слишком большой! \ N";
до свидания: возврат 0;
}

Этот способ
написание C ++ очень похоже на сборку — фактически, есть
взаимно однозначное соответствие между строками кода C, написанными таким образом
и инструкции на машинном языке.Более сложный C ++,
подобно конструкции «for», расширяется до многих линий сборки.

 int i, n = 10; 
for (i = 0; i std :: cout << "В цикле: i ==" << i << "\ n";
}

Вот один
расширенная версия этого C / C ++ цикла «for»:

 int i = 0, n = 10; 
start: std :: cout << "В цикле: i ==" << i << "\ n";
i ++;
, если (i

(исполняемый
Ссылка NetRun)

Вы должны убедить себя, что это действительно эквивалентно
цикл «for» во всех случаях.Осторожно - если n - параметр,
это не! (Что, если n> = i?)

Все конструкции управления потоком C могут быть написаны с использованием только «if» и
"goto", которые обычно сопоставляют "один к одному" для сравнения и перехода.
последовательность в сборке.

Нормальный C Расширенный C
если (A) {
...
}
if (! A) goto END;
{
...
}
КОНЕЦ:
если (! A) {
...
}
if (A) goto END;
{
...
}
КОНЕЦ:
если (A && B) {
...
}
if (! A) goto END;
if (! B) goto END;
{
...
}
КОНЕЦ:
если (A || B) {
...
}
if (A) goto STUFF;
, если (B) перейти к STUFF;
goto END;
ПЕРСОНАЛ:
{
...
}
КОНЕЦ:
, а (А) {
...
}
goto TEST;
START:
{
...
}
TEST: если (A) перейти к START;
do {
...
} в то время как (A)
START:
{
...
}
if (A) перейти к START;
для (i = 0; i {
...
}
я = 0;
/ * Версия A * /
goto TEST;
START:
{
...
}
i ++;
ТЕСТ: if (i
для (i = 0; i {
...
}
я = 0;
/ * Версия B * /
START: if (i> = n) goto END;
{
...
}
i ++;
goto START;
КОНЕЦ:

Обратите внимание, что последние два перевода понятия «для» (помечены
Версия A и версия B) вычисляют одно и то же. Который
один быстрее? Если цикл повторяется много раз, я требую версию
(A) обычно быстрее, так как есть только один (условный) goto
каждый раз по циклу вместо двух goto в версии
(Б) - одно условное и одно безусловное.Но версия (B)
вероятно, быстрее, если n часто равно 0, потому что в этом случае
быстро переходит в END (за один условный переход).

ВНИМАНИЕ:
Философское содержание

Ядерная
оружие настолько мощное, что обычно считается плохой идеей
используйте их для решения большинства проблем.

Аналогично,
Операторы goto настолько мощны, что обычно считаются плохими
идея использовать их для большинства задач. Проблема в том, когда ты
увидеть goto, вы не можете сказать, используется ли он для имитации for,
while, вызов функции или какой-то более странный их гибрид.Таким образом
goto может быть достаточно легко написать, но его труднее читать,
и поскольку код в больших проектах пишется только один раз, но получает
прочитал много раз много разных людей за эти годы, это больше
важно сделать ваш код легко читаемым.

Я больше всего замечаю
программисты-самоучки (включая меня), как правило, предпочитают goto или
его немного более классный кузен, цикл while, потому что они более
Генеральная. Но после того, как вы испортили цикл "while" достаточно раз,
например, отказавшись от приращения цикла и непреднамеренно сделав
бесконечный цикл, вы в конечном итоге по умолчанию используете циклы "for",
потому что синтаксис, предписанный компилятором ("for (int i = 0; i

Лекция по языку ассемблера и компьютерной архитектуре (CS 301)

Лекция по языку ассемблера и компьютерной архитектуре (CS
301)

CS 301: Сборка
Лекция по языковому программированию, доктор Лоулор

Прыжок
инструкция, такая как "jmp", просто переключает ЦП на выполнение
другой фрагмент кода. Это ассемблерный эквивалент
"goto", но в отличие от ненавистного goto в C / C ++, переходы не считаются постыдными в
сборка. (Дейкстра написал в 1968 году статью под названием «Goto
Считается вредным ".С тех пор goto обычно
считались вредными, за исключением
Linux.)

Вы говорите, куда перейти, используя "ярлык перехода", который может быть любым
имя строки с двоеточием после него. (Такой же точный синтаксис
используется в C / C ++)

В обоих случаях мы возвращаем 3, потому что мы перепрыгиваем через 999
назначение.
Прыжок в некоторой степени полезен для пропуска плохого кода, но он
действительно становится полезным, когда вы добавляете условные переходы, как этот C ++:
if (a> = c) goto summer;

В сборе,
это делится на две инструкции: "cmp rax, rcx" сравнивает
два значения a и c, тогда "jge summer" подскакивает, если сравнивать
вышло больше или равно.

Вот
самые полезные инструкции по прыжкам. Условные версии
почти всегда происходит после команды сравнения "cmp".

Инструкция Полезно для ...
jmp Всегда прыгать
je Перейти, если cmp равно
jne Перейти, если cmp не равно
jg Подписано>
(больше)
jge Подпись> =
jl Подпись < (менее)
ил Подпись <=
и Без знака> (вверху)
иже Без знака> =
сп Без знака <(внизу)
jbe Без знака <=
jrcxz Перейти, если rcx равно 0
(Серьезно !?)
jc Перейти при переносе: используется для неподписанных
переполнение,
или multiprecision добавить
ио Перейти, если там был подписан
перелив

Есть также
"n" версий НЕ для каждого прыжка; например "jno" прыгает, если есть
НЕ переполняется.Посмотреть полный список
инструкции по переходу на x86 здесь.

Условный

Прыжки: разветвление в сборке

В сборе,
все ветвления выполняются с использованием двух типов инструкций:

  • Команда сравнения, такая как «cmp», сравнивает два значения.
    Внутри он делает это путем их вычитания.
  • Команда условного перехода, например "je" (переход, если равен),
    делает переход где-нибудь, если два значения удовлетворяют
    правильное состояние.Например, если значения равны,
    их вычитание дает ноль, поэтому "je" то же самое, что "jz".

Вот как
используйте сравнение и прыжок, если равно ("je"):

 mov rax, 3 
cmp rax, 3; как rax сравнить с 3?
je lemme_outta_here; если равно, то прыгать
mov rax, 999; <- не выполняется * если * перепрыгиваем
lemme_outta_here:
ret

(Попробуйте

это сейчас в NetRun!)

Вот сравнить
и прыжок-если-меньше-чем ("jl"):

 mov rax, 1 
cmp rax, 3; как rax сравнить с 3?
j l lemme_outta_here; если меньше, то прыгать
mov rax, 999; <- не выполняется * если * перепрыгиваем
lemme_outta_here:
ret

C ++
эквивалентно сравнить-и-прыгать-если-что бы то ни было "если (что-то) перейти
где-то;".

Петли

Чтобы зациклиться, вы
просто вернитесь к началу кода. Где-то ты делаешь
нужно условие, или вы создали бесконечный цикл!

В этом
Например, мы отсчитываем rdi, пока не дойдем до нуля. Мы также
отступ в теле цикла для удобства чтения.

; rdi - это наш первый аргумент функции 
mov rax, 0; сумма добавлена ​​здесь Начало: ; цикл начинается здесь добавить rax, 10; добавлять каждый раз по петле sub rdi, 1; приращение цикла cmp rdi, 0; петлевой тест jg start; продолжить цикл, если rdi> 0 ret

(Попробуйте

это сейчас в NetRun!)

Это
построчно эквивалентно этому коду C ++:

 int foo (int bar) {
    int sum = 0;

Начало:
        сумма + = 10; 

бар -; если (bar> 0) перейти к началу; сумма возврата; }

 (Попробуйте сейчас в NetRun!) 

Оф
конечно, это очень уродливый код на C ++! Это более идиоматично
напишите здесь цикл "for":

 int foo (int bar) {
int sum = 0;
for (int count = bar; count> 0; count--)
сумма + = 10;
сумма возврата;
} 

(Попробуйте

это сейчас в NetRun!)

Подробнее

Сложный поток управления: C -

Можно
на самом деле написать очень своеобразный вариант C ++, где "если"
операторы содержат только операторы goto.Мое шутливое название для
этот C ++ в стиле сборки - «C--»: вы используете только «+ =» и «* =»
арифметика и «если (простой тест) перейти куда-нибудь»; управление потоком.

Например, это совершенно допустимый C ++ в стиле "C--":

 int main () {
int я = 0;
if (i> = 10) goto byebye;
std :: cout << "Не слишком большой! \ N";
до свидания: возврат 0;
}

Этот способ
написание C ++ очень похоже на сборку - фактически, есть
взаимно однозначное соответствие между строками кода C, написанными таким образом
и инструкции на машинном языке.Более сложный C ++,
подобно конструкции "for", расширяется до многих линий сборки.

 int i, n = 10; 
for (i = 0; i std :: cout << "В цикле: i ==" << i << "\ n";
}

Вот один
расширенная версия этого C / C ++ цикла "for":

 int i = 0, n = 10; 
start: std :: cout << "В цикле: i ==" << i << "\ n";
i ++;
, если (i

(исполняемый

Ссылка NetRun)

Вы должны убедить себя, что это действительно эквивалентно
цикл «for» во всех случаях.Осторожно - если n - параметр,
это не! (Что, если n> = i?)

Все конструкции управления потоком C могут быть написаны с использованием только «if» и
"goto", которые обычно сопоставляют "один к одному" для сравнения и перехода.
последовательность в сборке. Некоторые из них немного сложно
выяснить, хотя.

Нормальный C Расширенный C
если (A) {
...
}
if (! A) goto END;
{
...
}
КОНЕЦ:
если (! A) {
...
}
if (A) goto END;
{
...
}
КОНЕЦ:
если (A && B) {
...
}
if (! A) goto END;
if (! B) goto END;
{
...
}
КОНЕЦ:
если (A || B) {
...
}
if (A) goto STUFF;
, если (B) перейти к STUFF;
goto END;
ПЕРСОНАЛ:
{
...
}
КОНЕЦ:
в то время как (A) {
...
}
goto TEST;
START:
{
...
}
TEST: если (A) перейти к START;
do {
...
} в то время как (A)
START:
{
...
}
if (A) перейти к START;
для (i = 0; i {
...
}
я = 0;

/ * Версия A * /
goto TEST;
НАЧАЛО:
{
...
}
i ++;
ТЕСТ: if (i

для (i = 0; i {
...
}
я = 0;

/ * Версия B * /
START: if (i> = n) goto END;
{
...
}
i ++;
goto START;
КОНЕЦ:

Обратите внимание, что последние два перевода понятия «для» (помечены
Версия A и версия B) вычисляют одно и то же. Который
один быстрее? Если цикл повторяется много раз, я требую версию
(A) обычно быстрее, так как есть только один (условный) goto
каждый раз по циклу вместо двух goto в версии
(Б) - одно условное и одно безусловное.Но версия (B)
вероятно, быстрее, если n часто равно 0, потому что в этом случае
быстро переходит в END (за один условный переход).

ВНИМАНИЕ:

Философское содержание

Ядерная
оружие настолько мощное, что обычно считается плохой идеей
используйте их для решения большинства проблем.

Аналогично,
Операторы goto настолько мощны, что обычно считаются плохими
идея использовать их для большинства задач. Проблема в том, когда ты
увидеть goto, вы не можете сказать, используется ли он для имитации for,
while, вызов функции или какой-то более странный их гибрид.Таким образом
goto может быть достаточно легко написать, но его труднее читать,
и поскольку код в больших проектах пишется только один раз, но получает
прочитал много раз много разных людей за эти годы, это больше
важно сделать ваш код легко читаемым.

Я больше всего замечаю
программисты-самоучки (включая меня), как правило, предпочитают goto или
его немного более классный кузен, цикл while, потому что они более
Генеральная. Но после того, как вы испортили цикл "while" достаточно раз,
например, отказавшись от приращения цикла и непреднамеренно сделав
бесконечный цикл, вы в конечном итоге по умолчанию используете циклы "for",
поскольку предписанный компилятором синтаксис "for (int i = 0; i напомнить вам, что нужно включать каждую из частей, которые вы
нужно, и выдаст вам удобную ошибку компиляции, если вы забудете какую-либо часть
Это.

Структура управления

Структура управления

Большинство программ содержат некоторую форму условной логики, например ЕСЛИ
операторы, которые влияют на обычный линейный поток управления в
программа. Программирование условной логики означает включение
логику псевдокода в операторы, написанные в мнемонической форме для
Ассемблер. ЦП не имеет IF, WHILE, FOR или других структурированных
заявления; мы должны использовать доступные инструкции потока управления
нам реализовать структурированный код.

Условные операторы содержат выражение test , которое
возвращает ИСТИНА или ЛОЖЬ , например ЕСЛИ (количество ==
0)
, WHILE (sum> = MAX) и т.д. Самый простой тест
выражение - это тест, сравнивающий две величины, например
А <В . Назовем эти две величины переменной
называет A и B и разрабатывает перевод
тестовые выражения с двумя переменными для инструкций Ассемблера.Более
сложные выражения могут быть построены с использованием разработанных методик
здесь.

Базовая структура IF

Базовые инструкции Ассемблера, которые изменяют
поток управления - это операторы JUMP. Прыжки могут быть
безусловный, например JMP, или они могут быть обусловлены состоянием
флагов состояния, например JG, JA, JLE, JBE. Мы должны использовать эти
инструкции для реализации нашего псевдокода управления IF и WHILE
операторы потока.Структура псевдокода для простого IF
заявление (без предложения ELSE) выглядит следующим образом:

// Псевдокод простой структуры оператора IF:
ЕСЛИ (тестовое выражение с использованием A и B - ИСТИНА) ТОГДА
           Выполните многие
           заявления, которые
           тело IF здесь ...
ENDIF
        Продолжить остальную часть программы

; реализация IF с использованием языка ассемблера:
ЕСЛИ: оцените тестовое выражение, используя A и B
        УСЛОВНО ПЕРЕЙТИ к ENDIF (конец оператора IF), если FALSE
           Выполните многие
           заявления, которые
           тело IF здесь...
ENDIF: продолжить остальную часть программы
 

Обратите внимание на размещение условного прыжка.
инструкция выше. Функция JUMP - получить около
тело оператора IF. Мы должны закодировать тестовое выражение и
Инструкция JUMP, позволяющая потоку управления перейти в тело
IF, только если тестовое выражение ИСТИНА. Если тест
выражение - ЛОЖЬ, JUMP должен управлять телом
оператора IF.

Обратите особое внимание на эту деталь:
условный прыжок вокруг тела IF должен иметь место, если тест
выражение ЛОЖЬ, а не ИСТИНА.

Математика и флаги состояния

Каждый тип условной инструкции JUMP проверяет настройки
различных флагов состояния: отрицательный, ноль, положительный, перенос, переполнение,
и т.п.
Некоторые из условных переходов - это простые проверки одного из флагов состояния,
е.грамм. JC, JZ и т. Д., Но некоторые из них являются переходами, которые должны проверять несколько флагов,
например JBE, JA, JLE, JG и т. Д.
Только математика, например ДОБАВИТЬ, ВЫЧИТАТЬ, СРАВНИТЬ, устанавливает
флаги состояния; поэтому, чтобы использовать условные операторы JUMP для
изменить поток управления в нашей программе, мы должны сначала выполнить
немного математики, используя наши две переменные A и B.
математика установит флаги состояния ЦП на основе
отношения между A и B, и тогда мы можем условно перейти в соответствии с
установка флагов.

Условные переходы Intel

Условные переходы, проверяющие флажки, размещаются либо после фактического
арифметические операции (например, ADD, SUB и т. д.) или после оператора сравнения CMP, который
устанавливает флаги, как если бы SUB был выполнен, но не меняет никаких значений.
Давайте посмотрим, как оператор CMP соотносится с настройками флага для
флаги Carry и Zero и операторы условного перехода Intel
которые используют эти флаги:

Инструкция: CMP A, B
Отношение: А А A == B A> = B А> В А! = В
Флаги установлены всегда: C NZ NC Z NC NC NZ NZ
Intel прыжки: JC / JB / JNAE JBE / JNA JZ / JE JAE / JNB / JNC JA / JNBE JNZ / JNE

Обратите внимание, что некоторые из приведенных выше условных переходов являются простыми тестами на одном из
два флага, e.грамм. JC, JZ, но некоторые прыжки должны проверять оба флага, например JBE, JA.

Кодирование условных переходов

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

если (a == b) то
    a = a + b // ИСТИННОЕ тело
endif
а = а + 1
 

Мы написали выражение, которое вызывает контроль
поток, чтобы войти в ИСТИННОЕ тело, только если A равно B.С помощью
Структура оператора IF, приведенная выше, нам нужно выбрать несколько
математика, чтобы установить огни флага и следовать математике с
Инструкция JUMP, которая разветвляется вокруг тела IF, если тест
выражение - ЛОЖЬ.

Если мы хотим проверить равенство, A == B, мы должны
устроить прыжок вокруг тела IF при обратном условии, что
есть, когда A! = B. Итак, мы всегда выбираем условный JUMP, который
противоположное используемому нами условию проверки.Если тест
"равенство", JUMP - "прыжок, если не равен". Если тест
"меньше чем", JUMP - "прыжок, если больше или равно",
пр.

Используя приведенную выше структуру IF, вот перевод
приведенного выше псевдокода в инструкции Ассемблера:

ЕСЛИ: MOV DX, A; один операнд должен быть в регистре
        CMP DX, B; выполните математические вычисления на A и B, чтобы установить флаги состояния
        JNE ENDIF; A не равно B - прыгайте
        MOV CX, B; тело ИФ... (одна операция в регистре)
        ДОБАВИТЬ A, CX; тело IF ... (a = a + b)
ENDIF: INC A; конец оператора IF - продолжить
 

Если A и B равны, их сравнение будет
заставит загораться флаг Zero. Инструкция JNE работает только
если флаг Zero выключен, то прыжок не произойдет и тело
оператор IF будет выполнен. Если A и B не равны ,
Нулевой флаг не загорится, и JNE будет работать, чтобы пропустить
поверх тела оператора IF.Мы всегда инвертируем
условие прыжка, потому что он должен прыгнуть на вокруг тела
оператор IF.

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

  1. Выполните математические вычисления, используя A и B, чтобы установить
    флаги состояния.
  2. Используйте условную инструкцию JUMP для перехода
    по всему телу.
    (Не забудьте прыгнуть на , инверсный
    контрольная работа.)

Как мы выбираем, какую математику выполнять, и
какую инструкцию JUMP использовать?

Выбор условного JUMP

Все управляющие структуры (IF, IF / ELSE,
WHILE, FOR) имеют одинаковую структуру для своего теста
условия:

  1. Выполните математические вычисления, используя A и B, чтобы установить
    флаги состояния.
  2. Используйте условную инструкцию JUMP для перехода
    около корпус.
    (Не забудьте прыгнуть на , инверсный
    тест.)

Как мы выбираем, какую математику выполнять, используя
A и B, и какую инструкцию JUMP использовать?

Поскольку мы проверяем связь между A и
B - равно, меньше, больше и т. Д. - операция, которая
лучше всего для установки флагов состояния ЦП является ВЫЧИСЛЕНИЕ. Вычитание
A из B или B из A устанавливает флаги в соответствии с относительным
значения A и B; ДОБАВЛЕНИЕ не имело бы такого же эффекта.

Поскольку мы часто выполняем ВЫЧИСЛЕНИЕ только для
установить флаги состояния - на самом деле мы не хотим использовать значение
вычитание - большинство процессоров имеют инструкцию COMPARE (CMP), которая
устанавливает флаги, как если бы вычитание было выполнено, но
не меняет ни один из операндов и не сохраняет результаты
вычитания.

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

Больше против выше, меньше против ниже

Процессоры Intel предоставляют условные инструкции JUMP для двоичных файлов без знака.
математика и для знаковой математики с дополнением до 2.

При сравнении 16-битных величин FFFFh считается
быть выше 0001h, если рассматривать их как беззнаковые значения. (FFFFh
число без знака намного больше 1.) Однако, если рассматривать его как
со знаком, два дополнительных значения, FFFFh отрицательное значение (-1), поэтому FFFFh равно
говорят, что на меньше 0001ч.Условные инструкции Intel JUMP
отражают это различие:

Коды операций Тип условной JUMP
JA, JAE (= JNC) Выше, Выше или Равно (= Без переноса) ;
без знака
JB (= JC) , JBE ниже (= переносить) , ниже или равно;
без знака
JG, JGE Больше, больше или равно;
подписано
JL, JLE Меньше, Меньше или Равно;
подписано
JE, JZ Равно, ноль
JNE, JNZ Не равно, не ноль

После сравнения двух величин,
выберите условную инструкцию JUMP, соответствующую тому,
подписаны две величины (например,грамм. JG) или без знака (например, JA).

Как работают условные прыжки

Некоторые условные переходы - это простые проверки одного из флагов состояния,
например JC, JZ и т. Д., А некоторые - прыжки, которые должны проверять несколько флагов,
например JBE, JA, JLE, JG и т. Д.
Вы можете увидеть, какие флаги используются в каждом условном переходе по этому URL:
http://unixwiz.net/techtips/x86-jumps.html

Примеры условных переходов со знаком и без знака

Учитывая этот фрагмент псевдокода, закодируйте его как подписанный, так и беззнаковый тест.
(помните, что мы всегда инвертируем условие JUMP, чтобы перейти к ENDIF):

если (a> = b) то...
 

В качестве беззнакового теста:

ЕСЛИ: MOV CX, A
        CMP CX, B
        JB ENDIF; JumpBelow: пропустить тело, если A 

В качестве подписанного теста:

ЕСЛИ: MOV CX, A
        CMP CX, B
        JL ENDIF; JumpLess: пропустить тело, если A 

Как программист на ассемблере вы должны знать, какой тип
данные, которые вы сравниваете (подписанные или неподписанные), и выберите соответствующий
условный JUMP.Компилятор языка высокого уровня делает этот выбор за вас, когда
вы объявляете свои переменные как «подписанные» или «беззнаковые».

IF с ELSE
статья

IF-операторов с предложениями ELSE лишь незначительно
сложнее, чем простые операторы ЕСЛИ. Вот псевдокод
фрагмент программы с использованием оператора IF / ELSE:

1. если (a == b), то
2. а = а + б
3. еще
4.б = б - а
5. endif
6. b ++
 

Вот перевод указанного выше псевдокода.
фрагмент в Ассемблер, с номерами операторов, вставленными как
комментариев:

ЕСЛИ: MOV DX, A; 1: один операнд должен быть в регистре
        CMP DX, B; сделать математику на A и B, чтобы установить флаги
        JNE ELSE
        MOV AX, B; 2: один операнд должен быть в регистре
        ДОБАВИТЬ A, AX; а = а + б
        JMP ENDIF
Иначе: MOV CX, A; 4: один операнд должен быть в регистре
        SUB B, CX; б = б - а
ENDIF: INC B; 6: ENDIF; после IF
 

Сравните приведенный выше код на ассемблере
с тем, что используется в простом операторе IF.Обратите внимание на идентичный
размещение тестового выражения математики и инструкции JUMP
в строке 1 выше. Функция первого прыжка еще предстоит получить
вокруг ИСТИННОГО тела оператора IF; однако вместо
переходя к концу оператора IF, он переходит к первому
заявление в предложении ELSE. (Если тестовое выражение
Оператор IF / ELSE имеет значение FALSE, управление передается через тело TRUE и
в ЛОЖНОЕ тело.)

Новая инструкция "JMP ENDIF" добавлена ​​в
конец ИСТИННОГО тела оператора IF (непосредственно перед ELSE
label), чтобы обойти тело FALSE, составляющее часть ELSE
IF.Если бы этого прыжка не было, поток управления
перейдет от операторов в конце ИСТИННОГО тела к
операторы в начале тела FALSE.

A WHILE Заявление

Обратите внимание, что тест в операторе IF и тест в WHILE
утверждения имеют точно такую ​​же структуру; оба выражения
вернуть TRUE или FALSE и изменить поток управления вокруг
прилагаемая часть выписок:

если (a == b) то
    a = a + b // ИСТИННОЕ тело
endif

в то время как (a == b) делать
    a = a + b // ИСТИННОЕ тело
в конце концов
 

В обоих приведенных выше фрагментах кода поток управления
входит в тело ИСТИНА, только если тестовое выражение, сравнивающее A и
B для равенства верно.Если мы умеем закодировать один тест в
Инструкции ассемблера, мы умеем кодировать другие. Нам нужно
сосредоточьтесь только на коде самого тестового выражения.

Следующий фрагмент псевдокода почти
идентичен простому примеру IF, который использовался ранее; только
ключевые слова IF были изменены на WHILE:

в то время как (a == b) делать
    a = a + b // ИСТИННОЕ тело
в конце концов
а = а + 1
 

Опять же, мы написали выражение, которое вызывает
поток управления для входа в ИСТИННОЕ тело, только если A равно B.Мы
нужно подобрать немного математики, чтобы установить огни флага и использовать
условная инструкция JUMP для перехода на вокруг WHILE
тело. Мы используем почти ту же структуру оператора, что и для
простой оператор IF. Вот перевод вышеизложенного
псевдокод в инструкции Asembler:

ПОКА: MOV DX, A; один операнд должен быть в регистре
        CMP DX, B; выполните математические вычисления на A и B, чтобы установить флаги состояния
        JNE ENDWH; A не равно B - прыгайте
        MOV CX, B; тело WHILE... (одна операция в регистре)
        ДОБАВИТЬ A, CX; тело WHILE ... (a = a + b)
        JMP WHILE; WHILE - это цикл - подпрыгните, чтобы снова проверить
ENDWH: INC A; конец оператора WHILE - продолжить
 

Анализ приведенного выше кода Ассемблера таков:
аналогично анализу кода для простого оператора IF:
Если A и B равны, их вычитание приведет к тому, что флаг Zero будет
Ну же. Инструкция JNE требует снятия флага Zero, поэтому она
не прыгаем и входим в ПОКА тело.Если A и B не равны,
Нулевой флаг не загорится, и JNE перескочит на вокруг
тело оператора WHILE.

Ключевое различие между приведенным выше кодом и
код для простого оператора IF - это добавление "JMP TEST"
инструкция в нижней части тела оператора WHILE.
Операторы WHILE должны зацикливаться; IF-выражения - нет. Прыжок
возвращает управление в начало цикла WHILE, то есть в
тестовое выражение, с которого начинается цикл.

ДЛЯ Выписок

Операторы

FOR - это просто сокращенный способ
написание эквивалентных операторов WHILE. Чтобы закодировать FOR
оператор, превратите его в эквивалентный оператор WHILE, а затем
переведите это на ассемблер:

// исходный оператор FOR
        for (i = 0; i <9; i ++) делать
            сумма = сумма + я
        конец

// преобразование в оператор WHILE
        я = 0
        в то время как (я <9) делать
            сумма = сумма + я
            я = я + 1
        в конце концов
 
; превратился в ассемблер Intel
        MOV I, 0
ПОКА: CMP I, 9; делать математику, чтобы установить флаги состояния
        JGE ENDWH; если I> = 9 - прыгать
        MOV AX, I; один операнд должен быть в регистре
        ДОБАВИТЬ СУММУ, ТОПОР; сумма = сумма + я
        INC I; я = я + 1
        JMP WHILE; WHILE - это цикл - начать сначала с теста цикла
ENDWH:...; конец оператора WHILE - продолжить
 

Предложение инициализации цикла FOR предшествует
оператор WHILE. Тело цикла WHILE заканчивается
Предложение приращения цикла FOR непосредственно перед тем, как вернуться к
верх петли. Сам цикл WHILE имеет точно такое же
структура как прежде:

  1. Выполните математические вычисления, используя A и B, чтобы установить
    флаги состояния.
  2. Используйте условную инструкцию JUMP для перехода
    по всему телу.
    (Не забудьте прыгнуть на , инверсный
    тест.)

Распространенной ошибкой при программировании циклов FOR является
неправильно закодировать переход к началу цикла. Некоторые люди ошибочно
закодируйте переход, чтобы перейти к первому оператору инициализации
пункт. Правильный переход - к первому утверждению теста.
часть цикла WHILE, а не предложение инициализации.

Вложенная логика И

Вот более сложный пример, использующий два теста
выражения, соединенные оператором AND:

если (a! = b && c> = d), то
    ... тело идет сюда ...
endif
 

Вышеупомянутое тестовое выражение не простое
с участием двух величин, поэтому методы, которые мы использовали, так
далеко работать напрямую не получится. Однако мы можем переписать приведенное выше
составное тестовое выражение на два вложенных оператора ЕСЛИ, каждый из
который использует простое выражение типа A / B, которое мы знаем, как
перевести:

если (a! = b) то
    если (c> = d), то
        ... ЕСЛИ тело идет сюда ...
    endif
endif
 

Поскольку операторы IF вложены,
тело не будет выполнено, если внешнее И внутреннее IF
оба условия верны. Теперь мы можем перевести эти два IF
операторы непосредственно в ассемблер LMC:

IF1: MOV DX, A; один операнд должен быть в регистре
        CMP DX, B; это внешний тест IF
        JE ENDIF1; пропустить тело, если A == B
IF2: MOV DX, C; один операнд должен быть в регистре
        CMP DX, D; это внутренний тест IF
        JL ENDIF2; пропустить тело, если C 

И ENDIF1, и ENDIF2 на самом деле являются метками для
то же место; переход к любой метке переходит к тому же коду в
конец оператора IF. Поэтому мы могли бы упростить
код и используйте только одну метку ENDIF для обоих переходов.

Вложенная логика ИЛИ

Вот еще один сложный пример, на этот раз с использованием
два выражения, соединенные оператором OR:

если (a! = b || c> = d), то
    ... ЕСЛИ тело идет сюда ...
endif
 

Два приведенных выше условных тестовых выражения:
соединены оператором ИЛИ, поэтому мы не можем реализовать решение, используя два вложенных
IF, как мы это сделали, когда к ним присоединился оператор AND.
Перепишем код следующим образом:

if (a! = b) goto dobody;
if (c> = d) goto dobody;
идти вокруг
dobody: ... ЕСЛИ тело идет сюда ...
        ... ЕСЛИ тело идет сюда...
вокруг: ... линейный код возобновляется здесь ...
 

Каждый GOTO становится оператором JUMP в Ассемблере:

IF1: MOV DX, A; один операнд должен быть в регистре
        CMP DX, B; это внешний тест IF
        JNE DOBODY; перейти к телу, если A! = B
IF2: MOV DX, C; один операнд должен быть в регистре
        CMP DX, D; это внутренний тест IF
        JGE DOBODY; перейти к телу, если C> = D
        JMP AROUND; пропустить тело
ДОБОДЫ:... ЕСЛИ тело идет сюда ...
        ... ЕСЛИ тело идет сюда ...
Вокруг: ... здесь возобновляется линейный код ...
 

В отличие от предыдущих примеров, где прыжки
разветвленный вокруг тела IF, в приведенном выше коде используется
метки ДОБОДИ, чтобы вести к тело IF
для реализации функции ИЛИ.

За счет некоторой ясности можно было оптимизировать
код, объединив два соседних оператора JGE / JMP в
одиночный оператор JL; но тогда было бы не так ясно
перевод предоставленного псевдокода.

Стиль C Условное выражение на языке Ассемблер!

Условные операторы используются для принятия решений.
базовая форма, решение - это своего рода ветвь в коде, которая
переключается между двумя возможными путями выполнения на основе некоторых
condition. Обычно (но не всегда) условная инструкция
последовательности реализуются с помощью инструкций условного перехода.
Условные инструкции в HLL, например C / C ++, следующие:
1. if-else
2. switch
3.иначе-если

Например:
if (выражение)
оператор1
else
оператор2
Здесь вычисляется выражение; если истинно, выполняется оператор1. если оно ложно и есть часть else (часть else не обязательна), выполняется оператор 2.

При этом условные инструкции в сборке:
Проверка условий перехода для команд
JE Перейти, если равно ZF = 1
JNE Перейти, если не равно ZF = 0
JG Перейти, если больше (ZF = 0) И (SF = OF)
JGE Перейти если больше или равно SF = OF
JL Перейти, если меньше SF? OF
JLE Перейти, если меньше или равно (ZF = 1) ИЛИ (SF? OF)

Давайте обсудим инструкцию CMPL, прежде чем обсуждать условные инструкции в сборке.

cmpl Инструкция: The CMPL
инструкция сравнивает два операнда. Обычно используется в условных
исполнение. Эта инструкция в основном вычитает один операнд из
другой для сравнения, равны ли операнды или нет. Это не
нарушить операнды назначения или источника. Он используется вместе с
инструкция условного перехода для принятия решения.

Рассмотрим следующий оператор C / C ++:
if (a == b)
{
/ * Код истинного перехода здесь * /
}
else
{
/ * Код ложного перехода здесь * /
}
/ * На This Point, Reconverge * /

Подход к преобразованию этого оператора в язык ассемблера производит:
movl a,% eax
movl b,% ebx

cmpl% eax,% ebx

je true_branch

JMP реконвергент

true_branch:
# True Branch Code Здесь
reconverge:
#recoverge to this point

В первых двух строках мы перемещаем a и b в регистры для сравнения.В следующей строке мы сравниваем регистры eax и ebx. Другими словами, мы просто сравниваем a и b. После сравнения a и b, в следующей инструкции мы проверяем, равны ли a и b или нет. если a и b равны, то переходите к метке true_branch. После выполнения кода true_branch, true_branch восстанавливается, чтобы повторно сходиться метка.
как
вы можете видеть, поскольку язык ассемблера линейный, блоки должны прыгать
вокруг друг друга. Восстановлением занимается программист, а не
система.

Ссылка: 1.Реализация общих структур управления на языке ассемблера
2. Программирование с нуля.

Если вам понравился этот пост или у вас есть вопросы, не стесняйтесь комментировать!

Учебники по

AVR - Условное ветвление

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

Управление потоком программы означает, что мы инструктируем микроконтроллер перейти от одного адреса в коде к другому - это называется переходом . Это можно сделать тремя разными способами:

  • Условное ветвление
  • Прыжки
  • Вызов подпрограммы

Условное ветвление позволяет проверить условие (например,грамм. независимо от того, установлен ли бит или равен ли регистр определенному значению) и переходят в новое место, если условие истинно. Если это не так, микроконтроллер перейдет к следующей инструкции.

Прыжки - это форма Безусловный брекет . Он перенаправляет поток программы независимо от условий. Переход позволяет вам просто перейти к по новому адресу в программной памяти.

Вызов подпрограммы - это еще одна форма безусловного перехода .Он работает аналогично Jumping в том, что заставляет микроконтроллер переходить на новый адрес в программной памяти. Однако микроконтроллер запомнит адрес, с которого был сделан вызов подпрограммы, и по достижении конца подпрограммы вернется к инструкции, следующей сразу после вызова подпрограммы.

В этом руководстве мы специально рассмотрим Условное ветвление , названное так, поскольку оно создает несколько путей в программе.

Установка флагов в регистре состояния

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

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

Мнемоника Описание
CP сравнить
бит за канал сравнить с переноской
cpi сравнить с немедленным
тст тест на ноль или минус

Инструкция cp позволяет нам сравнивать два регистра.

  ldi r16,0x01; загрузить r16 с помощью 0x01
ldi r17,0x02; загрузить r17 с помощью 0x02
cp r16, r17; сравнить r16 и r17
  

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

В приведенном выше примере флаг переноса будет установлен в регистре состояния после сравнения, поскольку мы, по сути, вычисляем 0x01 - 0x02.Из этого можно сделать вывод, что второй операнд больше первого.

В качестве альтернативы ниже мы проверяем, равны ли значения двух регистров.

  ldi r16,0x01; загрузить r16 с помощью 0x01
ldi r17,0x01; загрузить r17 с помощью 0x01
cp r16, r17; сравнить r16 и r17
  

После сравнения будет установлен нулевой флаг , так как 0x01 - 0x01 = 0x00. Таким образом, мы можем заключить, что два операнда равны.

Если необходимо сравнить более одного байта, можно использовать инструкцию cpc, которая будет включать бит переноса в сравнение.Например, для сравнения 16-битных чисел 0xAA01 и 0xAA02

  ldi r16,0x01; загрузить r16 с помощью 0x01
ldi r17,0xAA; загрузить r17 с помощью 0xAA
ldi r18,0x02; загрузить r18 с помощью 0x02
ldi r19,0xAA; загрузить r19 с помощью 0xAA

cp r16, r18; сравнить r16 и r18
cpc r17, r19; сравнить r19 и r17 с переноской  

После первого сравнения будет установлен флаг переноса , указывающий, что первое число меньше второго.

При втором сравнении, даже если старшие байты обоих чисел равны, Флаг переноса все равно будет установлен, правильно указывая, что 0xAA02 больше, чем 0xAA01.Если бы вместо этого использовалась инструкция cp, регистр состояния неверно указывал бы, что два числа равны.

Для регистров с 16 по 31 у нас есть немедленное сравнение cpi.

  ldi r16,0x05; загрузить r16 с помощью 0x05
cpi r16,0x05; сравните r16 с 0x05  

В приведенном выше примере будет установлен нулевой флаг , указывающий, что r16 равно константе, с которой мы его сравниваем.

Наконец, инструкция tst может использоваться в одном регистре, чтобы проверить, является ли он отрицательным или нулевым.tst установит как Sign , так и Negative Flag , если установлен бит 7 регистра, или установит нулевой флаг , если регистр равен нулю. tst всегда сбрасывает Флаг переполнения двух дополнений .

  ldi r16,0x80; загрузить r16 с помощью 0x80
tst r16; тест r16  

В приведенном выше примере будут установлены Sign и Negative Flag , поскольку бит 7 0x80 равен 1.

Условное ветвление

После установки флагов в регистре состояния существует множество команд условного перехода , которые могут управлять ходом выполнения программы.

Ветвь

Ветвь

Ветвь

Ветка

Филиал

Мнемоника Описание
brbs ветвь, если установлен флаг состояния
brbc, если флаг состояния сброшен
breq, если она равна
brne, если не равно
brcs ответвление при переноске
brcc филиал при наличии разрешения на перевозку
брш филиал, если такой же или выше
brlo ответвление если нижнее
brmi если минус
брпл если плюс
brge ветвь, если больше или равно, со знаком
brlt филиал, если меньше, подписано
brhs ответвление при наполовину переносном комплекте
brhc филиал, если половина груза очищена
brts ветвь, если установлен флаг t
brts ветвь, если флаг t сброшен
brvs переход, если установлен флаг переполнения
brvc переход при сбросе флага переполнения
бри переход, если прерывание разрешено
мост ветвь, если прерывание отключено

Синтаксис инструкции ветвления просто

Если условие, указанное в инструкции ветвления, удовлетворяется, выполняется переход к адресу, отмеченному меткой.В противном случае он перейдет к следующей инструкции.

Например, следующий код сравнивает r16 и r17 и выполняет переход к lbl, если они равны.

  cp r16, r17; сравнить r16 и r17
...
breq lbl; ветвь, если r16 == r17
...
фунт:  

Некоторые инструкции условного ветвления могут показаться избыточными, но это только потому, что существует беззнаковая версия и подписанная версия . Например, brsh - ветвь, если такая же или выше , используется с беззнаковыми значениями

  ldi r16,0x9C; загрузить r16 с 0x9C (156 без знака)
ldi r17,0x0F; загрузить r17 с помощью 0x0F (15 беззнаковых)
cp r16, r17; сравнить r16 и r17
...
brsh lbl; r16> = r17, код будет ветвиться
...
фунт:  

Инструкция brge - ветвь, если она больше или равна , выполняет тот же тип сравнения, но работает со значениями со знаком.

  ldi r16,0x9C; загрузить r16 с 0x9C (-100 подписано)
ldi r17,0x0F; загрузить r17 с 0x0F (15 со знаком)
cp r16, r17; сравнить r16 и r17
...
brsh lbl; r16 не> = r17, код не будет ветвиться
...
фунт:  

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

  ldi r16,0x9C; загрузить r16 с 0x9C (156 без знака)
ldi r17,0x0F; загрузить r17 с помощью 0x0F (15 беззнаковых)
cp r17, r16; сравнить r17 и r16
...
brsh lbl; r16 не <= r17, код не будет ветвиться
...
фунт:  

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

Цикл пока

A В то время как петля в сборке может быть сконструирована следующим образом

  петля: cpi r16,100; сравните r16 и 100
...
brsh next; ветвь, если r16> = 100
rjmp loop; повторить цикл

следующий:  

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

Это эквивалентно следующему коду C, предполагая, что r16 хранится в переменной x.

Цикл Do While

В качестве альтернативы, цикл Do While Loop может быть сконструирован в сборке как

  петля: ...
cpi r16,0; проверьте, равно ли r16 нулю
брючная петля; повторить цикл, если r16 не равно 0  

Обратите внимание, что в цикле Do While Loop нам не нужна инструкция rjmp.Условный переход используется для повторения цикла, и программа просто переходит к следующей инструкции, если условие не выполняется.

Это эквивалентно следующему коду C, предполагая, что r16 хранится в переменной x.

Для петли

Сборка для петли показана ниже

  clr r16; очистить r16
цикл: cpi r16,11; сравните r16 с 11
breq next; прервать цикл, если он равен
...
inc r16; инкремент r16
rjmp loop; повторить цикл

следующий:  

В этом примере наш индекс For Loop хранится в r16 и инициализируется значением 0.r16 сравнивается с числом 11 и выйдет из цикла, когда оно станет равным. Обратите внимание, что это наш способ проверить, меньше ли r16 или равно 10.

Это эквивалентно следующему коду C, предполагая, что r16 хранится в переменной x.

Разветвление "Скип"

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

Мнемоника Описание
сбрц пропустить, если бит в регистре очищен
сбрс пропустить, если бит в регистре установлен
сбич пропустить, если бит в регистре ввода-вывода очищен
сбис пропустить, если бит в регистре ввода-вывода установлен

sbrc и sbrs позволяют «пропустить» следующую инструкцию, если бит в рабочем регистре общего назначения установлен или очищен.Например, следующее создаст цикл, который будет выполняться до тех пор, пока не будет установлен бит 3 r0.

  петля: ...; код цикла
сбрс р0,3; прервать цикл, если бит 3 r0 равен 0
rjmp loop; повторить цикл  

Кроме того, sbic и sbis могут использоваться одинаковым образом для регистраторов ввода-вывода. Например, следующее создаст цикл, который будет выполняться до тех пор, пока не будет очищен PINB3.

  петля: ...; код цикла
сбич ПИНБ, 3; прервать цикл, когда PINB3 равен 0
rjmp loop; повторить цикл  

Заключение

Вот и все, условное ветвление в сборке AVR.Понимание вышеизложенного - значительный шаг вперед в написании полезных программ на ассемблере.

# 5: Ветвление AVR - Arxterra

ЧТЕНИЕ
Микроконтроллер AVR и встроенные системы с использованием сборки и C
Мухаммада Али Мазиди, Сармада Наими и Сепера Наими

Разделы: 3.1

ДОПОЛНИТЕЛЬНОЕ ЧТЕНИЕ
Введение в программирование на ассемблере AVR для начинающих, контроль последовательного выполнения программы http: // www.avr-asm-tutorial.net/avr_en/beginner/JUMP.html

ИНСТРУКЦИЯ ПО АРХИТЕКТУРЕ (ОБЗОР)

Архитектура набора команд (ISA) микропроцессора включает в себя все регистры, доступные программисту. Другими словами, регистры, которые могут быть изменены набором команд процессора. Что касается проиллюстрированного здесь ЦП AVR, эти регистры ISA включают в себя 32 x 8-битных резистора общего назначения, резистор состояния (SREG), указатель стека (SP) и счетчик программ (PC).

Передача данных Инструкции используются для загрузки и сохранения данных в регистры общего назначения, также известные как файл регистров . Исключениями являются инструкции push и pop, которые изменяют указатель стека. По определению эти инструкции не изменяют регистр состояния (SREG).

Арифметические и логические команды , а также команды битов и битовых тестов используют ALU для работы с данными, содержащимися в регистрах общего назначения. Флаги, содержащиеся в регистре состояния (SREG), предоставляют важную информацию о результатах этих операций.Например, если вы складываете два числа со знаком вместе, вы захотите узнать, правильный ли ответ. Состояние бита флага переполнения (OV) в SREG дает вам ответ на этот вопрос (1 = ошибка, 0 нет ошибки).

Control Transfer Инструкции позволяют изменять содержимое ПК условно или безоговорочно. Продолжая наш пример, если ошибка возникает в результате сложения двух чисел со знаком, мы можем условно (OV = 1) перейти к процедуре обработки ошибок.По мере того, как процессор AVR выбирает и выполняет инструкции, он автоматически увеличивает счетчик программ (ПК), поэтому он всегда указывает на следующую команду, которая должна быть выполнена .

НАБОР ИНСТРУКЦИИ (ОБЗОР)

Набор команд нашего процессора AVR можно функционально разделить (или классифицировать) на следующие части:

  • Инструкции по передаче данных
  • Арифметические и логические инструкции
  • Bit and Bit-Test Instructions
  • Инструкции по передаче управления (ответвление)
  • Инструкции по управлению MCU

ИНСТРУКЦИЯ ДЛЯ ПЕРЕХОДА

  • Существует два основных типа инструкций по передаче управления - Безусловная и Условная .
  • С точки зрения программиста, безусловная инструкция или инструкция перехода переходит к указанной метке. Например, цикл jmp безоговорочно перейдет к циклу меток в вашей программе.
  • Вот безусловные команды перехода на передачу управления процессора AVR

    Direct jmp, звонок
    Относительное (1) rjmp, rcall
    Косвенный ijmp, icall
    Подпрограмма и возврат по прерыванию рет, рети

Примечание:

  1. Переход относительно ПК + (, где k = 12) ПК-2048 на ПК + 2047, в адресном пространстве слов 16 КБ ATmega328P

КАК DIRECT

UNCONDITIONAL CONTROL TRANSFERINSTRUCTIONS JMP AND CALL WORK

  • С точки зрения компьютерного инженера прямой переход осуществляется путем загрузки целевого адреса в программный счетчик (ПК).В этом примере целевой адрес приравнивается к метке «цикл».
    • Чтобы предоставить более конкретный пример, предположим, что цикл меток соответствует адресу 0x0123 во флэш-памяти программ.
    • Для выполнения этой инструкции управляющая логика центрального процессора (ЦП) загружает в 16-битный регистр программного счетчика (ПК) значение 0x123.
    • Следовательно, в следующем цикле выборки выбирается и затем выполняется инструкция в ячейке 0x0123. Управлению программы было передано на этот адрес .

Рисунок 2: Машинный код JMP и CALL, сохраненный во флэш-памяти программ

КАК РАБОТАЕТ ОТНОСИТЕЛЬНАЯ ИНСТРУКЦИЯ ПО ПЕРЕДАЧЕ УПРАВЛЕНИЯ

НЕПОСРЕДСТВЕННАЯ RJMP И RCALL

  • С точки зрения компьютерного инженера относительный скачок достигается добавлением 12-битного смещения со знаком к счетчику программ (ПК). Результат, соответствующий целевому адресу. В этом примере целевой адрес приравнивается к метке «цикл».
    • Чтобы предоставить более конкретный пример, предположим, что цикл меток соответствует адресу 0x0123 во флэш-памяти программ (целевой адрес).
    • Инструкция цикла rjmp находится по адресу 0x206. Когда выполняется rjmp, ПК в настоящее время извлекает то, что, по его мнению, является следующей инструкцией, которая должна быть выполнена по адресу 0x207.
    • Для выполнения этого перехода относительный адрес (kkkk kkkk kkkk) равен 0xF1C (т.е. 0x123 - 0x207).
    • Следовательно, в следующем цикле выборки выбирается и затем выполняется инструкция в ячейке 0x0123. Управлению программы было передано на этот адрес .

Рисунок 3: Машинный код RJMP и RCALL, сохраненный во флэш-памяти программ

ИНСТРУКЦИЯ ДЛЯ ОТРАСЛИ

  • При выполнении условной инструкции или инструкции перехода может произойти одно из двух.
  1. Если условие проверки истинно, то переход будет выполнен (см. Инструкции перехода).
  2. Если условие проверки ложно, ничего не происходит (см. Инструкцию nop).
    Примечание: это утверждение не совсем точное. Поскольку счетчик программ всегда указывает на следующую инструкцию, которая должна быть выполнена, в состоянии выполнения ничего не делать означает выборку следующей инструкции.
  • «Условие проверки» является функцией одного бита флага SREG. Например, инструкции ветвления, если равно (breq) или не равно (brne) проверяют флаг Z.

КАК РАБОТАЕТ ОТНОСИТЕЛЬНАЯ ИНСТРУКЦИЯ ПО ПЕРЕДАЧЕ УСЛОВНОГО КОНТРОЛЯ

  • Если выполняется относительное ветвление (условие проверки истинно), к ПК добавляется 7-битное смещение со знаком. Результат, соответствующий целевому адресу. В этом примере целевой адрес приравнивается к метке «совпадение».
    • Чтобы предоставить более конкретный пример, предположим, что метка nomatch соответствует адресу 0x0123 во флэш-памяти программ (целевой адрес).
    • Инструкция brne nomatch находится по адресу 0x0112. Когда выполняется инструкция brne, ПК в настоящее время извлекает то, что, по его мнению, является следующей инструкцией, которая должна быть выполнена по адресу 0x0113.
    • Для выполнения этого перехода относительный адрес (kk kkkk) равен 0b01_0000 (т.е. 0x123 - 0x113).
    • Следовательно, в следующем цикле выборки выбирается и затем выполняется инструкция в ячейке 0x0123. Управлению программы было передано на этот адрес .

Рисунок 4: Ветвь меняет положение счетчика указателя

ИНСТРУКЦИЯ ДЛЯ ОТРАСЛИ

  • Все инструкции условного ветвления могут быть реализованы как brbs s, k или brbc s, k, где s - это номер бита флага SREG. Например, brbs 6, битовый набор будет разветвляться на битовый набор меток, если установлен бит SREG T.
  • Чтобы сделать ваш код более читабельным, ассемблер AVR добавляет следующие инструкции «псевдонима».
    • SREG Флаговый бит очищен (brFlagc) или установлен (brFlags) по имени (I, T , H , S, V , N, Z, C ) или бит (brbc, brbs).
    • Эти биты флага SREG (I, T, H, S , V, N , Z , C ) используют более наглядную мнемонику.
      • Переход, если равен (breq) или не равен (brne), проверяет флаг Z.
      • Арифметическая ветвь без знака , если плюс (brpl) или минус (brmi) проверяет флаг N, а ветвь, если она равна или выше (brsh) или ниже (brlo), проверяет флаг C и эквивалентна brcc и brcs соответственно.
      • Арифметическая ветвь с дополнением до 2 со знаком , если число меньше нуля (brlt) или больше или равно нулю (brge), проверить флаг S

Пропустить, если…

  • Бит (b) в регистре очищен (sbrc) или установлен (sbrs).
  • Бит (b) в регистре ввода / вывода сброшен (sbic) или установлен (sbis). Ограничено до адресов ввода / вывода 0-31

Примечание:

  1. Все инструкции ветвления относятся к PC + (, где k = 7) + 1 PC-64 к PC + 63
  2. Инструкции пропуска могут занимать 1, 2 или 3 цикла в зависимости от того, не выполняется ли пропуск, и от количества слов флэш-памяти программ в инструкции, которую необходимо пропустить (1 или 2).

УСЛОВНОЕ КОДИРОВАНИЕ ОТРАСЛИ

Вот как кодируются инструкции сборки brbs, brbc и их псевдонимов.

Рисунок 6: Код машины разветвления

ПОСЛЕДОВАТЕЛЬНОСТЬ ПЕРЕДАЧИ УСЛОВНОГО УПРАВЛЕНИЯ (ОТДЕЛЕНИЕ)

Последовательность условной передачи управления (ветвления) обычно состоит из двух (2) инструкций.
1. Первая инструкция выполняет некоторую арифметическую или логическую операцию с использованием ALU процессора.

  • Примеры этого первого типа инструкций включают: cp, cpc, cpi, tst
  • Эти операции ALU приводят к установке или сбросу битов флага SREG с 5 по 0 (т.е.е., H, S, V, N, Z, C).
  • Чтобы обеспечить возможность тестирования нескольких условий ветвления, эти инструкции обычно не изменяют ни один из наших 32 регистров общего назначения.
  • Команды сравнения cp, cpc, cpi следует использовать, если вы хотите понять взаимосвязь между двумя регистрами . Для команд сравнения это достигается путем выполнения операции вычитания без операнда-адресата (cp r16, r17 эквивалентно r16 - r17).
  • Инструкцию tst следует использовать, если вы хотите проверить, является ли число в одном регистре отрицательным или нулевым.Для тестовой инструкции это достигается путем выполнения операции and с одинаковыми регистрами назначения и источника (tst r16 эквивалентно and r16, r16).

ПРЕДУПРЕЖДЕНИЕ. В документе Atmel «Сводка набора команд» неправильно классифицируются инструкции сравнения ( cp, cpc, cpi ) как «инструкции перехода». Они должны быть перечислены в разделе «Арифметические и логические инструкции». Чтобы подчеркнуть это несоответствие со стороны Atmel, инструкция tst правильно указана в разделе «Арифметические и логические инструкции.”

2. Вторая инструкция - это инструкция условного перехода, проверяющая один или несколько битов флага SREG.

КРАТКАЯ ИНСТРУКЦИЯ ПО УСЛОВНОМУ ОТДЕЛЕНИЮ

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

Пример условной передачи управления (переход)

Вот как в ассемблере будет реализован алмаз для принятия решения о языке высокого уровня.

Рисунок 7: Блок-схема ветвления If

; направления (см. примечание)
.EQU юг = 0b00; 6 старших разрядов ноль
.EQU восток = 0b01
.EQU запад = 0b10
.EQU север = 0b11

cpi r16, север; шаг 1: установлен флаг Z, если r16 = 0b00000011
breq yes; шаг 2: переход, если установлен флаг Z

Примечание. Эти эквиваленты включены в тестовую среду.inc

РЕАЛИЗАЦИЯ ЗАЯВЛЕНИЯ IF ВЫСОКОГО УРОВНЯ

Рисунок 8: Блок-схема ветвления верхнего уровня If

  • Оператор if верхнего уровня обычно состоит из…
    • Последовательность передачи условного управления (последний слайд), где реализовано дополнение (не) условного выражения высокого уровня.
    • Процедурный блок кода высокого уровня преобразуется в сборку.
  • C ++ Выражение IF высокого уровня
    if (r16 == north) {
    блок кода, который должен быть выполнен, если ответ положительный.
    }
  • Версия сборки

cpi r16, север; Медведь смотрит на север?
brne no; переход, если флаг Z снят (не равен)
блок кода, который должен быть выполнен, если ответ положительный.
№:

РЕАЛИЗАЦИЯ ВЫСОКОГО УРОВНЯ ЕСЛИ… ДРУГОЕ ЗАЯВЛЕНИЕ

Рисунок 9: Блок-схема ветвления If-else

  • Оператор if… else высокого уровня обычно состоит из…
    • Последовательность передачи условного управления, в которой реализовано дополнение (не) условного выражения высокого уровня.
    • Высокоуровневый процедурный блок кода для условия «да» (истина).
    • Безусловный переход через блок кода no (false).
    • Высокоуровневый процедурный блок кода для отсутствия (ложного) условия.
  • C ++ Высокоуровневый if… else Выражение
    if (r16 == north) {
    блок кода, который должен быть выполнен, если ответ да (истина).
    }
    else {
    блок кода, который будет выполнен, если ответ не (ложь).
    }
  • Версия сборки

cpi r16, север; Медведь смотрит на север?
brne остальное; переход, если флаг Z снят (не равен)
блок кода, который должен быть выполнен, если ответ положительный.
rjmp end_if
else:
блок кода, который будет выполнен, если ответ отрицательный.
end_if:

ОПТИМИЗАЦИЯ СБОРКИ ВЫСОКОГО УРОВНЯ, ЕСЛИ… ДРУГОЕ ЗАЯВЛЕНИЕ - РАСШИРЕННАЯ ТЕМА -

Рисунок 10: Блок-схема If-else

  • Если блоки кода if-else могут быть выполнены в одной строке сборки, то поток программы модифицируется, чтобы угадать наиболее вероятный результат теста.
    • Это возможно, если значение переменной (например, сегменты 7-сегментного дисплея, которые должны быть включены) - единственное, что выполняется в каждом блоке.
    • Этот оптимизированный для поток программы всегда будет выполняться так же быстро, как и обычный поток программы if..else (если предположение неверно), и быстрее, если оно верное.
    • Эта реализация также более компактна и часто более проста для понимания.
  • Версия сборки

    ; 7-сегментный дисплей (см. Примечание)
    .EQU seg_a = 0
    .EQU seg_b = 1
    .EQU seg_c = 2

    ldi r17,1 <
    cpi r16, север; Медведь смотрит на север?
    breq done
    clear (not equal)
    блок кода, который должен быть выполнен, если предположение было неверным.
    выполнено:

Примечание. Эти эквиваленты включены в spi_shield.inc

.

Примеры программ: группа A или B - пример псевдокода

  • Цель
    Назначьте 4 наименее значимых переключателя на щите CSULB группе A, а наиболее значимые - группе B. В зависимости от ввода пользователя отобразите A или B в зависимости от того, какая группа имеет более высокое значение. В случае ничьей отобразить E как равное. Для этой задачи программирования предположим, что люди выбирают A в 50% случаев, B в 40% случаев и устанавливают переключатели равными друг другу в 10% случаев.

Пеленгатор - два программных решения

Разработайте цифровую схему с двумя (2) переключателями, которые включат одну из комнат. 4 светодиодных сегмента, указывающих направление, в котором вы хотите, чтобы ваш медведь шел

  • Таблица преобразования направления в сегмент
  • Справочная карта программиста

Пеленгатор - Реализация таблицы истинности

 lds r16, dir // перемещаем биты направления в рабочий регистр

 // лицом на восток (сегмент b)
 bst r16,0 // сохранить бит направления 0 в T
 bld var_B, 0 // загружаем r16 бит 0 из T
 bst r16,1 // сохраняем бит направления 1 в T
 bld var_A, 0 // загружаем r17 бит 0 из T
 com var_A // B = / A * B
 и var_B, var_A
 bst var_B, 0 // сохранить r16 бит 0 в T
 bld spi7SEG, seg_b // загружаем бит 1 r8 из T

  Реализация логических выражений для сегментов a, f и g (принципиальная схема)  

Пеленгатор - использование условных выражений

lds r16, реж.

ldi r17, 1 breq done
ldi r17, 1 breq done
ldi r17, 1 breq done
ldi r17, 1 <

выполнено:

mov spi7SEG, r17; ответ на 7-сегментный регистр
вызов WriteDisplay

Псевдо-инструкции TurnLeft, TurnRight и TurnAround

Используя переключатели 3 и 2, расположенные на контактах 3 и 2 порта C соответственно, введите действие, которое должен предпринять медведь.Три возможных действия: ничего не делать, поворот влево, поворот вправо и поворот. Напишите подпрограмму с именем WhichWay для выполнения правильного действия, как определено в следующей таблице.

Таблица 5.2: Таблица истинности указателей поворота

; ————————–
; - Куда мне идти? -

call ReadSwitches // входные выводы порта C (0x06) в регистр r7
bst switch, 3 // сохраняем бит переключателя 3 в T
brts cond_1X // переход, если T установлен
bst switch, 2 // сохраняем бит переключателя 2 в T
brts cond_01 // переход, если T установлен

cond_00:

rjmp, конец

cond_01:

rcall TurnRight
rjmp whichEnd

cond_1X:

// переход в зависимости от состояния бита 2 переключателя
:

cond_10:

:

cond_11:

:

Конец:

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

InForest и реализация выражения IF… ELSE

  • Подпрограмма inForest сообщает нам, находится ли медведь в лесу (т. Е. Выбрался ли он из лабиринта).
  • Строки и столбцы лабиринта пронумерованы от 0 до 19 (13h), начиная с верхнего левого угла.
  • Когда медведь выбрался из лабиринта, он оказывается в ряду минус один (-1). Подпрограмма должна вернуть истину (r25: r24! = 0), если медведь находится в лесу, и ложь (r25: r24 == 0) в противном случае.
  • Регистровая пара r25: r24 - это то место, где C ++ ищет возвращаемые значения для типа данных BYTE.

Рисунок 16: Блок-схема inForest

InForest и реализация выражения IF… ELSE - продолжение -

; ————————–
; ——- В лесу ——–
; Вызывается из подпрограммы whichWay
; Вход: строка Выходы: C ++ регистр возврата (r24)
; Никакие другие регистры или флаги не изменяются этой подпрограммой

влес:

push reg_F // нажимаем любые флаги или регистры, измененные
в reg_F, SREG
push r16
lds r16, row

проверить, находится ли медведь в лесу

endForest:

clr r25 // расширение нуля
pop r16 // удаление любых флагов или регистров, помещенных в стек
out SREG, reg_F
pop reg_F
ret

Приложение

ПРИЛОЖЕНИЕ A: КОДИРОВАНИЕ ИНСТРУКЦИЙ ПЕРЕДАЧИ УПРАВЛЕНИЯ

Прямой

Все режимы адресации передачи управления изменяют программный счетчик.

Машинный код JMP и CALL, хранящийся во флэш-памяти программ

КОДИРОВАНИЕ ИНСТРУКЦИЙ ПЕРЕДАЧИ УПРАВЛЕНИЯ - Косвенное

Машинный код косвенных команд

КОДИРОВАНИЕ ИНСТРУКЦИИ ПЕРЕДАЧИ УПРАВЛЕНИЯ - Относительный

Машинный код относительного ветвления, хранящийся во флэш-памяти программ

ПРИЛОЖЕНИЕ B - РЕГИСТР СОСТОЯНИЯ АРН (SREG)

Регистр состояния

Без ALU
  • Бит 7 - I: Разрешение глобального прерывания
    Бит разрешения глобального прерывания должен быть установлен для разрешения прерываний.Затем управление разрешениями отдельных прерываний выполняется в отдельных регистрах управления. Бит I сбрасывается аппаратно после возникновения прерывания и устанавливается инструкцией reti. I-бит также может быть установлен и сброшен приложением с помощью инструкций sei и cli.
  • Бит 6 - T: хранилище битовых копий
    Команды битового копирования bld (Bit LoaD) и bst (Bit STore) используют T-бит в качестве источника или назначения. Бит из регистра может быть скопирован в T (R b T) командой bst, а бит в T может быть скопирован в бит в регистре (T Rb) командой bld.
ALU

Арифметика с дополнением до двух со знаком

  • Бит 4 - S: бит знака, S = N ⊕ V
    Бит устанавливается, если ответ отрицательный без ошибок или если оба числа были отрицательными и произошла ошибка, в противном случае - ноль.
  • Бит 3 - V: Флаг переполнения дополнения до двух
    Бит устанавливается, если ошибка возникла в результате арифметической операции, в противном случае - ноль.
  • Бит 2 - N: отрицательный флаг
    Бит устанавливается, если результат отрицательный, в противном случае - ноль.
Беззнаковая арифметика
  • Бит 5 - H: Флаг переноса половины
    Переносит от младшего полубайта к старшему полубайту. Half Carry полезен в арифметике BCD.
  • Бит 0 - C: Флаг переноса
    Флаг переноса C указывает перенос в арифметической операции. Бит устанавливается, если ошибка возникла в результате беззнаковой арифметической операции, в противном случае - ноль.
Арифметика и логика
  • Бит 1 - Z: нулевой флаг
    Нулевой флаг Z указывает нулевой результат в арифметической или логической операции.
ПРИЛОЖЕНИЕ C - ИНСТРУКЦИИ ПО ПЕРЕДАЧЕ УПРАВЛЕНИЯ (ОТДЕЛЕНИЕ)

Сравнить и протестировать cp, cpc, cpi, tst, bst

Безусловный

  • Относительный (1) rjmp, rcall
  • Direct jmp, звонок
  • Непрямой ijmp, icall
  • Subr. & Интер. Возврат ret, reti

Условно

  • Переход, если (2)…
    • SREG Флаговый бит очищен (brFlagc) или установлен (brFlags) по имени (I, T , H , S, V , N, Z, C ) или бит (brbc, brbs).
    • Эти биты флага SREG (I, T, H, S , V, N , Z , C ) используют более наглядную мнемонику.
      • Переход, если равен (breq) или не равен (brne), проверяет флаг Z.
      • Арифметическая ветвь без знака , если плюс (brpl) или минус (brmi) проверяет флаг N, а ветвь, если она равна или выше (brsh) или ниже (brlo), проверяет флаг C и эквивалентна brcc и brcs соответственно.
      • Арифметическая ветвь с дополнением до 2 со знаком , если число меньше нуля (brlt) или больше или равно нулю (brge), проверить флаг S
  • Пропустить, если…
    • Бит (b) в регистре очищен (sbrc) или установлен (sbrs).
    • Бит (b) в регистре ввода / вывода сброшен (sbic) или установлен (sbis). Ограничено адресами ввода / вывода 0-31

Примечание:

  1. Переход относительно ПК + (, где k = 12) + 1 ПК-2047 к ПК + 2048, в адресном пространстве слов 16 КБ ATmega328P
  2. Вся ветвь относительно ПК + (, где k = 7) + 1 от ПК-64 к ПК + 63, в адресном пространстве слов 16 КБ ATmega328P

ПРИЛОЖЕНИЕ D - НАБОР ИНСТРУКЦИЙ ATMEGA328P

Meeting Assembly - Мигающий код Arduino Hello World | автор J3 | Jungletronics

На этой странице я расскажу вам, как программа мигания Arduino работает в сборке,
, как создать проект на Atmel Studio 7, и некоторые подробности об AVR.

Теперь пора взглянуть на то, что мы хотим сделать и как это можно сделать.

1 - Цели учебного пособия:

После завершения этого учебного пособия по микроконтроллеру AVR читатели должны уметь:

. Напишите ассемблерный код для инициализации AVR ATmega 328P и запустите на нем код
. Понять, как это работает построчно,
. Пробуем это в симуляторе Atmel Studio 7
. Загрузите код в Arduino Uno непосредственно из Atmel Studio 7 IDE

2 - Десять (10) используемых инструкций:

Набор инструкций AVR - Руководство

Инструкция: Цикл: Описания

ldi : 1: Немедленная загрузка ; Загружает 8-битную константу непосредственно в регистры.С 16 по 31.

cbi : 1: Очистить бит в регистре ввода-вывода - Очищает указанный бит в регистре ввода-вывода.

sbi : 1: Установить бит в регистре ввода / вывода - Устанавливает указанный бит в регистре ввода / вывода.

out : 1: Сохранить регистр в расположение ввода / вывода - Сохраняет данные из регистра Rr в файле регистров в пространстве ввода / вывода (порты, таймеры, регистры конфигурации и т. Д.).

dec : 1: Декремент - вычитает единицу из содержимого регистра Rd и помещает результат в регистр назначения Rd.

adiw : 2: Добавить сразу в слово - Добавляет немедленное значение (0–63) в регистровую пару и помещает результат в регистровую пару.

brne : 2: Ветвь, если не равно - Условное относительное ветвление. Проверяет нулевой флаг (Z) и разветвляется относительно ПК, если Z сброшен.

rcall : 1: Относительный вызов подпрограммы - Относительный вызов адреса внутри ПК

ret : 1: Возврат из подпрограммы - Возврат из подпрограммы.

rjmp : 1: Relative Jump - Относительный переход к адресу.

3 - Индекс решения :

Точка 00 - МОТИВАЦИЯ
Точка 01 - Открыть техническое описание - Подготовить решение
Точка 02 - Открыть проект моделирования
Точка 03 - Открыть целевой проект
Точка 04 - Инициализировать код
Точка 05 - Выполните математику
Пункт 06 - Запустите моделирование на Atmega 238P
Пункт 07 - Выполните на реальной плате - Arduino UNO
Пункт 08 - Возьмите свой проект моделирования как шаблон
Пункт 09 - Возьмите целевой проект как шаблон
Пункт 10 - Подготовка следующего проекта

Симулятор очень важен, потому что он позволяет нам поэтапно проходить программу и программный код построчно, анализировать и понимать, что делает ваш код, прежде чем вы фактически загрузите его на свою доску и запустите.
Хотя оборудование так же важно, как и программное обеспечение, оно только говорит вам, работает ваш код или нет.
Я хотел бы видеть Arduino Internals (под капотом) и Atmel Studio 7 с открытым исходным кодом. Спасибо команде Atmel!
Это видео для меня как воспоминание, так что когда-нибудь позже я смогу сохранить самую важную информацию, не забывая все это.
Я делюсь своим делом из-за страсти к сообществу DIY. Итак, поехали! Давайте начнем!

Пункт 01 - Открыть техническое описание - Подготовьте решение

, чтобы любой проект пошел дальше, необходимо прочитать техническое описание (ATmega328P).Давайте откроем документ и сделаем первые Close Encounters of the Third Kind 🙂

Если вы нажмете на этот флаг (вверху справа) и перейдете к Register Summary , вы найдете все записи и объекты наших исследований. Здесь вы найдете три основных регистра портов ввода-вывода Atmel AVR:

 PORTx, DDRx и PINx. 

источник: http://www.avrbeginners.net/architecture/ioports/ioports.html

Вот рисунок их основных функций: как вы можете видеть, есть внутреннее подтягивание для каждого контакта (на PIC, 18- K's Families вперед).Его можно активировать, установив бит DDR вывода на 0, а бит порта на 1. Очищенный бит DDR означает, что вывод является входным. Таким образом, вывод отключен от регистра порта (см. Драйвер на рисунке?), И вывод находится в плавающем состоянии. В этом случае бит порта управляет подтягиванием.

Мы выделяем конфигурацию, которую мы возьмем в нашем проекте: мы устанавливаем DDRx на 1 и будем переключать PORTx между высоким и низким состояниями, чтобы мигать светодиод.

Вернуться к таблице данных из сводки по набору команд , мы будем использовать в нашем коде только 10 из 131 (да! Всего сто тридцать один! Тьфу…) Мощные инструкции, которые: cbi , ldi , out , dec , adwi , brne , rcall , ret , rjmp и sbi (подробности выше).
Теперь откройте Atmel Studio 7 :

  Файл> Создать> Проект / решение  

Закройте стартовую страницу , выберите Atmel Studio Solution> Пустое решение на вкладке , установленной . Назовите его как хотите ... Я назову его Solution_AVR1 , настройте каталог вашего проекта, чтобы вы знали, где его разместить.

Теперь в Solution Explorer, щелкните правой кнопкой мыши и выберите Add> New Project , выберите Assembler> AVR Assembler Project, и Device Selection , введите 328p и выберите ATmega328P и назовите его как ты хочешь.Как только вы откроете проект, вы должны поместить Block Comment Header (BCH), чтобы кто-нибудь знал, для чего предназначен код (как хорошие методы программирования). см. ссылки на отличный веб-сайт МПБ с примерами здесь: D

; 
; _27_arduserie_Simulae_328P.asm
; *** вот идет BCH ****
;

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

Point 02 - Откройте проект моделирования

_27_arduserie_Simulae_328P для целей моделирования.Прохладный!

Point 03 - Откройте целевой проект

_27_arduserie_Target_328P для нацеливания на цель, которая является аппаратным кодом, Arduino Uno, конечно…

Point 04 - Инициируйте свой код

Давайте посмотрим, вероятно, на самую простую из возможных программ :

  .ORG 0x000 ; следующая инструкция должна быть написана для прибавления 0x0000 ;  бесконечный цикл  
START:
; это метка «START»
rjmp START ; R elative J u MP to START

Когда инструкция выполняется, CPU ( ALU - Arithmetic Logic Unit ) переходит к START .Прыжок будет повторяться снова и снова, что приведет к бесконечному циклу. Хорошо, в этом нет ничего страшного…

Пункт 05 - Сделайте математику

Давайте продолжим разработку кода.
Если предположить, что наш Arduino AVR работает на частоте 16 МГц (16 миллионов тактовых циклов в секунду), сколько времени это займет?

 T = 1 / F, затем T = 1 / 16E6 
T = 0,0000000625 секунд, а не точно 0,06 мкс

Это довольно быстро! На этой частоте мы не видим мигания светодиода. Итак, подойдя к ответу, экспериментально было обнаружено, что человеческий глаз не может различить различие в кадре изображения, если оно отображается менее чем на 16-13 мс (0,016–0,013 с).Следовательно, чисто на основании этого мы можем сказать, что частота дискретизации составляет от 60 Гц до 80 Гц (около 0,06 и 0,08 МГц).
Таким образом, нам нужно знать, сколько раз мы будем выполнять цикл с точки зрения цикла, например, полсекунды выключения и полсекунды, чтобы светодиод мигал каждые полсекунды с частотой 16 МГц, нам нужно x циклов. Вот как:

, если 1 цикл занимает ----------- 0,0000000625 секунд 
x циклов займет ----------- 0,5 секунды
это составляет 0,5 / 0,0000000625 с = 8 000 0000 циклов

Как добиться всех этих циклов, если я могу считать только до 255 (помните, у нас 8-битный чип)… магия? Волшебная палочка?
Расслабьтесь, с техникой, которую я представлю, все станет легко.

Для этого нам нужно реализовать два цикла : внутренний и выходной . Посмотрите, как:
вычислений - внутренний цикл обрабатывается как одна БОЛЬШАЯ инструкция, требующая 262145 тактов. См .: Сначала внутренний цикл - как вы знаете, регистры могут использоваться парами, что позволяет работать со значениями от 0 до 65 535. Это слово .
Следующий фрагмент кода очищает регистры 24 и 25 и увеличивает их в цикле, пока они снова не переполнятся до нуля.
Когда возникает это условие, цикл больше не повторяется.Вперед!

  clr r24 ; clr требуется 1 цикл 
clr r25 ; clr требуется 1 цикл DELAY_05: ; нам нужна задержка .5s
adiw r24, 1 ; adiw требует 2 цикла и
brne DELAY_05 ; brne требуется 2 цикла, если ветвление выполнено
; и 1 в противном случае

Каждый раз, когда регистры не переполняются, цикл занимает adiw (2) + brne (2) = 4 цикла.
Это делается 0xFFFF (65 535) раз до того, как произойдет переполнение.В следующий раз для цикла потребуется всего 3 цикла, потому что ветвление не выполняется.
В сумме получается 4 * 65 535 (цикл) + 3 (переполнение) + 2 (clr) = 262 145 циклов. Этого все еще недостаточно: 8 000 000/262 145 ~ 30,51.

«Внешний» цикл будет вести обратный отсчет от 31 до нуля с использованием R16.

  ldi r16, 31   OUTER_LOOP: ; этикетка внешней петли 
ldi r24, 0 ; очистить регистр 24
ldi r25, 0 ; очистить регистр 25 DELAY_05 :; этикетка петли
adiw r24, 1 ; «Добавить сразу к слову»: r24: r25 -
; увеличено
brne DELAY_05
dec r16 ; декремент r16
brne OUTER_LOOP ; загрузить в r16 8

Для общего цикла требуется: 262 145 (внутренний цикл) + 1 (уб.) + 2 (brne) = 262 148 * 31 = 8 126 588 циклов.
Это больше похоже на то, что мы хотим, но 126 588 циклов слишком длинны.

Вот тут-то и появляется тонкая настройка - нам нужно изменить начальное значение r24: r25.
Внешний цикл выполняется 31 раз и включает в себя «инструкцию большого внутреннего цикла».
Мы должны вычесть некоторый цикл с из внутреннего цикла: 126588/31 = 4083 цикла на внутренний цикл.
Это то, что внутренний цикл должен быть короче. Каждая итерация внутреннего цикла занимает 4 цикла (последний цикл занимает 3, но это не так важно), поэтому давайте разделим эти 4083 на 4.
Это на 1 020,8 или на 1 021 итерацию меньше.

Это наше новое значение инициализации для r24: r25!
Теперь, если хотите, проделайте все эти вычисления еще раз: результат - 8 000 000 тактов!

Теперь просто поместите это в отдельную процедуру и вызовите ее из основного цикла мигания светодиода. Вот полная программа:

  .ORG 0x0000 ; следующая инструкция должна быть записана; адрес 0x0000  rjmp START ; вектор сброса: переход к «основному»  START:   ldi r16, low (RAMEND) ; настраиваем стек  out SPL, r16   ldi r16, high (RAMEND)   out SPH, r16   ldi r16, 0xFF ; загрузить регистр 16 с 0xFF (все биты 1)  из DDRB, r16 ; записать значение r16 (0xFF) в Data; Регистр направления B  LOOP:  sbi PortB, 5; выключить светодиод rcall delay_05; ждать полсекунды cbi PortB, 5; включить rcall delay_05; ждать полсекунды rjmp LOOP; перейти к циклу  
DELAY_05:
; подпрограмма: ldi r16, 31; загрузить в r16 31 OUTER_LOOP: ; метка внешней петли ldi r24, low (1021); загружаем регистры r24: r25 с 1021, наш новый; начальное значение ldi r25, высокое (1021); метка цикла DELAY_LOOP: ; "добавить сразу в слово": r24: r25 are; увеличен adiw r24, 1; если нет переполнения («ветвь, если не равно»), идти; вернуться к "delay_loop" brne DELAY_LOOP dec r16; уменьшение r16 brne OUTER_LOOP; и цикл, если внешний цикл не завершен ret; возврат из подпрограммы

Пункт 06 - запустить моделирование на Atmega 238P

Итак, давайте продолжим и запустим сеанс отладки или моделирования.
Вам просто нужно нажать Build> Build Solution и продолжить, нажав кнопку Start Debugging and Break (Alt + F5) в меню Debug .
Давайте просто сделаем это и посмотрим, что произойдет…

Ой: ошибка? Выберите подключенные инструменты и интерфейс и попробуйте еще раз… , для этого нажмите Продолжить и в Tool> Selected debugger / programmer выберите Simulator.

Воспользуйтесь преимуществом и перейдите к Toolchain и под Общие отключите Создать файл HEX .Это функция безопасности. Поскольку мы здесь моделируем, мы не хотим помещать этот файл в чип, верно?

Ctrl + S , закройте окно свойств и снова Alt + F5 .

Итак, отладчик запустился, а остановка означает паузу в первой строке (или как вам угодно…). Выберите в окне I / O PORTB .

Обратите внимание на желтую стрелку останавливается прямо на нулевом адресе. Он указывает на инструкцию, которая будет выполнена следующей (нажатие Reset приведет к этому), но еще не выполнено.
Если вы хотите пройти код построчно, вам необходимо знать две основные команды: Шаг за (F10) и Шаг с заходом в (F11).
Посмотрим, как это работает.
Если я нажимаю F11 , происходит переход к следующей инструкции. Программа счетчика начинает отсчет. Машинный цикл пройден.
В частотном диапазоне Arduino это занимает всего 0,06 микросекунды.
На следующем шаге мы заметим, что регистр R16 изменит свое значение.Это будет 0xFF значение первого полубайта из указателя стека.

Чтобы разобраться, лучше перейти по этой ссылке.

Эти операции позволяют нам использовать 16 бит (одно слово) для выполнения необходимого подсчета.

Примечание:

8-битный указатель стека микроконтроллера AVR может состоять либо из одного регистра ввода-вывода SPL (низкий указатель стека), либо из двух (2) регистров ввода-вывода SPL и SPH (высокий указатель стека). Размер указателя стека зависит от объема памяти данных, который содержит микрокроллер.Если вся память данных может быть адресована с использованием 8 битов, тогда указатель стека имеет ширину 8 бит, то есть только SPL, в противном случае указатель стека состоит из SPL и SPH.

Хорошо. Теперь нам нужно настроить нашу DDRB . При 0xFF все биты на выходе равны 1.
Теперь осталось только настроить PORTB поочередно вверх и вниз, используя sb i для установки бита и cbi для его очистки.

Теперь мы используем rcall для перехода к циклу delay_05 , который ждет полсекунды.Для этого хита сейчас F10 - Step Over .

Через короткую секунду часы остановятся в цикле числа 8 миллионов согласно нашим расчетам выше.

Теперь, если мы посчитаем секундомер , будет показывать ровно полсекунды. Посмотрите:

 500000000 us = 0,5 секунды 

Замечательно !!!

Когда вы закончите отладку, нажмите кнопку Остановить отладку ( Ctrl + Shift + F5 ).

Нам наконец-то удалось превратить ассемблер в Hello world !!!.

Point 07 - Run On Real Board - Arduino UNO

Скопируйте и вставьте этот код в проект target .
В нашем предыдущем видео у нас уже есть необходимые инструменты для загрузки этого кода для Arduino непосредственно из Atmel Studio 7 IDE. Просто зайдите в меню Инструменты , нажмите:

  Отправить в ArduinoUNO  

и готово! Вот видео для этой установки.

Пункт 08 - Возьмите свой проект моделирования как шаблон

Теперь давайте превратим весь проект в шаблон. Для этого используйте код моделирования.

Мы будем использовать это как заголовок комментария блока (скопируйте и вставьте в файл симуляции main.asm):

 / * ====================== ============================================ 
| Проект: НОМЕР ЗАДАНИЯ И НАЗВАНИЕ
|
| Автор: ЗДЕСЬ ИМЯ СТУДЕНТА
| Язык: НАЗВАНИЕ ЯЗЫКА, НА КОТОРОЙ НАПИСАНА ПРОГРАММА И
| НАИМЕНОВАНИЕ КОМПИЛЯТОРА, ИСПОЛЬЗУЕМОГО ДЛЯ СОСТАВЛЕНИЯ ЭТОГО ПРИ ЭТОМ
| ПРОТЕСТИРОВАЛСЯ
| Решение: ВАШЕ РЕШЕНИЕ ATMEL STUDIO 7 NAME
|
| Проекты: _ ## _ #### _ Simulation_ATMEGA328P &
| _ ## _ #### _ Target_ATMEGA328P
|
| Для компиляции: ОБЪЯСНИТЕ, КАК СОБИРАТЬ ЭТУ ПРОГРАММУ
|
| Программное обеспечение: НАЗВАНИЕ И НАЗВАНИЕ КЛАССА, ДЛЯ КОТОРОГО БЫЛА ЭТА ПРОГРАММА
| ПИСЬМЕННЫЙ
| Версия ОС: НОМЕР ВЕРСИИ ВАШЕЙ КОМПЬЮТЕРНОЙ ПРОГРАММЫ
|
| Платформа: ВАША КОМПЬЮТЕРНАЯ ПРОГРАММА
|
| Преподаватель: ИМЯ ИНСТРУКТОРА ВАШЕГО КУРСА
|
| Срок выполнения: ДАТА И ВРЕМЯ, ЧТО ЭТА ПРОГРАММА ЯВЛЯЕТСЯ / БЫЛА
| ПРЕДСТАВЛЕНО
|
+ ------------------------------------------------ -----------------
|
| Описание: ОПИСАТЬ ПРОБЛЕМУ, КОТОРАЯ НАПИСАНА ЭТА ПРОГРАММА
| РЕШАТЬ.
|
| Ввод: ОПИСАТЬ ВХОД, ТРЕБУЕМЫЙ ПРОГРАММЕ.
|
| Выход: ОПИСАТЬ РЕЗУЛЬТАТ ПРОГРАММЫ.
|
| Алгоритм: ОПИСАНИЕ ПОДХОДА, ИСПОЛЬЗУЕМОГО ПРОГРАММОЙ ДЛЯ РЕШЕНИЯ ПРОБЛЕМЫ
| ПРОБЛЕМА.
|
| Не включены обязательные функции: ОПИСАТЬ ЛЮБЫЕ ТРЕБОВАНИЯ
| ЗАДАНИЕ, КОТОРОЕ ПРОГРАММА НЕ ПЫТАЕТСЯ РЕШИТЬ.
|
| Известные ошибки: ЕСЛИ ПРОГРАММА НЕ РАБОТАЕТ ПРАВИЛЬНО В НЕКОТОРЫХ
| СИТУАЦИИ, ОПИСАТЬ СИТУАЦИИ И ПРОБЛЕМЫ ЗДЕСЬ.
|
* ================================================ ================ * / .INCLUDE "m328pdef.inc"; включаются автоматически
.ORG 0x0000
; начальная инструкция
rjmp START ; вектор сброса: перейти к «основному»; *** разместите свои данные здесь ***

START:
; *** ваш код перейдет к ней ***
LOOP:
; *** процедура цикла * **
RJMP LOOP

; *** ваша процедура здесь ***
.ВЫХОД ; говорит ассемблеру, что он здесь!

Crtl + s . Теперь нажмите Файл> Экспорт шаблона…
Выберите Тип шаблона , нажмите Шаблон проекта .
Хит Далее .
Выберите Имя шаблона (simulae_template_328P ) , Описание шаблона, введите небольшой текст, чтобы напомнить вам позже (шаблон моделирования / целевого проекта для Arduino Uno) и Расположение вывода (новый каталог).Оставьте все по умолчанию.
В результате вы получите ZIP-файл с точкой, в моем случае C: \ Users \ giljr \ Documents \ Visual Studio 2015 \ My Exported Templates.

Повторите ту же процедуру для целевого проекта. Назовите его target_template_328P.

Круто !!! перезагрузите Atmel Studio 7 IDE и перейдите в File> New> Project n бум !!! Вот так !!!

Пункт 09 - Возьмите целевой проект как шаблон

Повторите ту же процедуру для целевого проекта.

Пункт 10 - Подготовка следующего проекта

Файл> Импорт> Шаблон проекта и выберите файл .zip интересующего проекта.

Сохранить как « Solution_AVR2 » и « _28_arduserie_Simulation_ATMEGA328P » и « _28_arduserie_Target_ATMEGA328P »

Для следующего проекта можно использовать таймеры «» и «

».

Давайте углубимся в плату Arduino !!!

Но это проблемы для следующего видео… спасибо за уделенное время !!! до свидания!!!

BTW - Некоторые важные понятия:

Что такое сборка?
Ассемблер - это язык низкого уровня.Язык ассемблера (или ассемблера), часто обозначаемый аббревиатурой asm , представляет собой язык программирования низкого уровня для компьютера или другого программируемого устройства, в котором существует очень сильное (обычно однозначное) соответствие между языком и инструкции машинного кода архитектуры. Язык ассемблера преобразуется в исполняемый машинный код служебной программой, называемой ассемблером (Википедия).

Что такое стек?
Стек - это последовательный блок памяти данных, выделенный программистом.
Этот блок памяти может использоваться как внутренним управлением микроконтроллера, так и программистом для временного хранения данных. Стек работает с механизмом Last In First Out ( LIFO ), то есть последнее, что хранится в стеке, - это первое, что извлекается из стека.

Что такое указатель стека?
Указатель стека - это, по сути, регистр или регистры, которые содержат либо «адрес памяти последней ячейки в этом стеке, где были сохранены данные», либо «адрес памяти следующей доступной ячейки в стеке для хранения данных.”Определение указателя стека зависит от конструкции микроконтроллера. В микроконтроллерах AVR, таких как ATMega8515, ATMega16, ATTiny13 и т. Д., Указатель стека содержит адрес следующего доступного места в стеке, доступного для хранения данных.
8-битный указатель стека микроконтроллера AVR может состоять либо из одного регистра ввода-вывода SPL (низкий указатель стека), либо из двух (2) регистров ввода-вывода SPL и SPH (высокий указатель стека). Размер указателя стека зависит от объема памяти данных, который содержит микроконтроллер.Если вся память данных может быть адресована с использованием 8 битов, тогда указатель стека имеет ширину 8 бит, то есть только SPL, в противном случае указатель стека состоит из SPL и SPH.

Что, черт возьми, означает RAMEND?
RAM END - это метка, которая представляет адрес последней ячейки памяти в SRAM. Чтобы использовать эту метку, вы ДОЛЖНЫ убедиться, что вы включили файл заголовка определения для конкретного микроконтроллера. Функции low () и high () используются ассемблером для возврата младшего и старшего байтов соответственно 16-битного слова.Помните, что мы имеем дело с 8-битным микроконтроллером, который может обрабатывать только 8-бит за раз.

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

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