
Введение
========

LDmicro генерирует машинный код для некоторых микроконтроллеров (далее MCU)
Microchip PIC16 и Atmel AVR. Обычно программное обеспечение для этих MCU написано
на таких языках программирования, как ассемблер, C, или BASIC. Программы для этих
языков программирования состоят  из списка команд. Эти мощный инструментарий и хорошо
подходит для архитектуры MCU.

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

   ||                                                                    ||
   ||    Xbutton1           Tdon           Rchatter           Yred       ||
 1 ||-------]/[---------[TON 1.000 s]-+-------]/[--------------( )-------||
   ||                                 |                                  ||
   ||    Xbutton2           Tdof      |                                  ||
   ||-------]/[---------[TOF 2.000 s]-+                                  ||
   ||                                                                    ||
   ||                                                                    ||
   ||                                                                    ||
   ||    Rchatter            Ton             Tnew           Rchatter     ||
 2 ||-------]/[---------[TON 1.000 s]----[TOF 1.000 s]---------( )-------||
   ||                                                                    ||
   ||                                                                    ||
   ||                                                                    ||
   ||------[END]---------------------------------------------------------||
   ||                                                                    ||
   ||                                                                    ||

Здесь:
TON - таймер с задержкой на ВКЛ., TOF - таймер с задержкой на ВЫКЛ..
Выражение --] [--  - вход, который мы можем представить в приближении
контактом реле (контакт далее). Выражение --( )-- - выход, представляем
его как катушку реле (реле далее). Много хороших материалов по лестничной
логике доступны в Интернете и в соответствующей литературе. Детали специфики
реализации лестничной логики в программе LDmicro описаны ниже.

Отличия ЛЛ от обычного языка программирование очевидны:

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

    * В целом, программа выглядят как электрическая схема с
    контактами (входы) и реле (выходы). Это интуитивно
    понятно людям со знанием теории электрических цепей (электрикам,
    электронщикам, энергетикам и тд.).

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

LDmicro компилирует ЛЛ в код для PIC16 и AVR. Следующие MCU поддерживаются:
     *PIC16F877
     *PIC16F628
     *PIC16F876 (неиспытан)
     *PIC16F88 (неиспытан)
     *PIC16F819 (неиспытан)
     *PIC16F887 (неиспытан)
     *PIC16F886 (неиспытан)
     *ATmega128
     *ATmega64
     *ATmega162 (неиспытан)
     *ATmega32 (неиспытан)
     *ATmega16 (неиспытан)
     *ATmega8 (неиспытан)

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

Достаточно легко включить большее кол.-во представителей семейств MCU AVR или PIC16,
но я не могу лично проверить их сам (незабываем что программа GPL 3 т.е. почти free
прим. переводчика). Если Вы хотите добавить какой-либо чип, то свяжитесь со мной и я
посмотрю, что можно сделать.


Используя LDmicro Вы рисуете алгоритм ЛЛ в графическом виде.
Затем вы можете протестировать программу, моделируя ее в режиме
реального времени. После того как программа отлажена в режиме
симуляции, можно связать пины микроконтроллера с входами и выходами
 релейной схемы и генерировать PIC или AVR код. На выходе Компилятора мы получаем
'.hex' файл, который Вы можете запрограммировать в ваш микроконтроллер с помощью
имеющегося у Вас PIC / AVR программатора.

LDmicro разработан, так чтобы быть в чем-то похожим на коммерческие ПЛК
систем программирования. Есть некоторые исключения, и многие аспекты не являются
стандартными в отрасли так или иначе. Внимательно прочитайте описание
каждой инструкции, даже если это кажется знакомым. Этот документ предполагает,
базовые знания в области релейной логики и структуры программного обеспечения ПЛК
(выполнение цикла: чтение входов, вычисление, запись выходов).

Дополнения
==========

Кроме того, можно генерировать C код из лестничных диаграмм. Вы можете
использовать его на любом процессоре, для которого
у вас есть компилятор C. Но вы ответственны за поддержку ран-тайм.
Это значит, что LDmicro просто генерирует код функции PlcCycle().
Вы отвечаете за вызов данной функции PlcCycle() во время основого цикла
программы, и Вы реализуете весь I/O (чтение/запись цифровых входов, и т.д.)
который вызывается через вызовы PlcCycle(). См. комментарии в генерируемом
исходнике для дальнейших пояснений.

В конечном итоге LDmicro может даже генерировать процессоро-независимый байт-код,
для виртуальной машины (далее VM) предназначенной для исполения кода ЛЛ. Я написал пример
интерпретатора/(VM), на хорошо портируемом С (см. исходники проекта файл ldinterpret.c
 прим. переводчика). Таким образом ЛЛ будет работать НА ЛЮБОЙ платформе, на которой вы
запустите VM. Это может показаться полезным если вы хотите использовать ЛЛ в качестве
'скриптового языка' для крупных программ. См. комментарии в примере интерпретатора для
дальнейших пояснений (ldinterpret.c).

Опции Командной строки
=======================

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

Если LDmicro передано имя файла через командную строку,
(например 'ldmicro.exe asd.ld'), тогда LDmicro попытается открыть `asd.ld',
если он существует. Появится сообщение об ошибке если 'asd.ld' не существует.
Т.е. Вы можете ассоциировать ldmicro.exe с файлами '.ld', и они запустятся автоматически при двойном клике на '.ld' файле.

Если LDmicro будет передан аргумент командной строки вида
'ldmicro.exe /c src.ld dest.hex', тогда будет скомпилирован 'src.ld',
и  результат сохранен в 'dest.hex'. LDmicro прервет свою работу после
компиляции, независимо от результата работы. Все сообщения компилятора
при этом будут выведены на консоль. Этот режим возможен только при запуске
LDmicro из консоли.


Основы
======

Запуск LDmicro без аргументов запустит пустую программу с одной пустой ступенью.
LDmicro использует свой внутренний формат для схем ЛЛ, при этом не импортируются
ЛЛ из других программных пакетов.

Вы можете добавлять инструкции, например
Вы можете добавить контакт (Инструкции -> Вставить Контакт) названный
`Xnew'. Префикс 'X' означает, что контакт будет привязан к входу MCU.
Привязать пин можно позднее, после выбора типа MCU и переименования
контактов. Первая буква имени показывает тип обьекта. Например:

    * Xname -- привязка к вводу MCU
    * Yname -- привязка к выводу MCU
    * Rname -- 'внутреннее реле: бит в памяти
    * Tname -- таймер; с задержкой на ВКЛ, с задержкой на ВЫКЛ, с памятью
    * Cname -- счетчик, или инкремент (+) или декремент (-)
    * Aname -- целочисленное (integer 16bit) чтение из АЦП
    * name  -- переменная общего назначения (integer 16bit)

Назначайте оставшуюся часть имени объекта осмысленно,
чтобы оно было уникально для программы. Совпадающие имена указывают на один
и тот же объект в программе. Например, будет ошибкой назвать таймер (TON) -
 'Tdelay' и таймер (TOF) - 'Tdelay' в одной программе (оба таймеры задержки),
так как каждый из них выделяет свою память для счета. С другой стороны, правильно
назвать Сохраняемый таймер (RTO) - 'Tdelay' и сбрасывать его командой (RES сброс
RTO/Cчетчика) связанной с - 'Tdelay', так как обе команды работают с одним
таймером.

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

Команды (MOV, ADD, EQU и т.д.) могут работать с любыми переменными.
Это означает, что они могут получить доступ к таймерам и Счетчикам.
Иногда это может быть полезно, например, Вы можете проверить,
что таймер находится в определенном временном диапазоне.

Переменные всегда 16 битные числа. Это означает, что они могут быть
от -32768 до 32767. Переменные всегда рассматриваются как знаковое.
Вы можете указать литералы как обычные десятичные числа (0, 1234, -56).
Вы также можете указать их как символы ASCII ('A', 'z'), заключив символ
в одиночные кавычки. Вы можете использовать ASCII код символов в
большинстве случаев, в которых используются десятичные числа.

В нижней части экрана вы видите список всех объектов программы.
Этот список автоматически генерируется из программы;
нет необходимости его модифицировать без надобности. Большинство объектов не
требуют никакой конфигурации. 'XName','Yname' и 'Aname' необходимо
тем не менее привязать к пинам MCU. Сначала выберите микроконтроллер который
Вы используете (Установки -> микроконтроллер). Затем необходимо назначить
ваши порты ввода / вывода (I/O), дважды щелкнув их в списке.

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

Программа состоит минимум из одной ступени (rung). Вы можете добавить больше
ступеней, выбрав "Редактировать -> Вставить Ступень До / После" в  меню.
Вы можете получить тот же эффект путем размещения множества параллельных ветвей
в одной ступени. Но более удобно использовать несколько ступеней.

После того как вы написали программу, вы можете протестировать ее в режиме симуляции,
а затем Вы можете скомпилировать ее в HEX файл для целевого микроконтроллера.

СИМУЛЯЦИЯ
==========

Чтобы войти в режим симуляции, выберите "Симуляция -> Режим Симуляции" или нажмите
<Ctrl+M>. Программа выглядит по-другому в режиме симуляции. Курсора больше нет.
Инструкции, которые выполняются (TRUE) подсвечиваются ярким красным, инструкции,
которые не выполняются (FALSE) подсвечены серым. Нажмите <Пробел>, чтобы
запустить один цикл ПЛК. Для непрерывной симуляции в режиме реального времени, выберите
"Симуляция -> Старт Ран-Тайм Симуляции", или нажмите <Ctrl+R>.Состояние программы будет
обновляться в режиме реального времени, анимируя состояние схемы.

Вы можете установить состояние входов в программе, дважды щелкнув
их в списке в нижней части экрана, либо с помощью двойного щелчка
по контакту инструкции 'XName" в программе. При изменении состояния
входа изменения не будут анимированы пока программа не завершит цикл ПЛК.Это произойдет
автоматически, если вы запустите симуляцию в режиме реального времени, или при нажатии
клавиши пробела (выполнение одного цикла).

Компиляция в Нативный Код
=========================

В конечном итоге пора создать '.HEX' файл, который можно запрограммировать
в ваш MCU. Сначала Вы должны выбрать тип MCU, в меню "Установки ->
Микроконтроллер". После этого Вам необходимо назначить ввода / вывода для
каждого объекта 'XName' и 'Yname'. Для этого дважды щелкните имя объекта
в списке в нижней части экрана. Появится диалоговое окно, где можно выбрать
пин из списка нераспределенных выводов.

Затем Вы должны выбрать время цикла, с которым будет работать программа, и
Вы должны сообщить компилятору, какова будет тактовая частота MCU. Эти настройки
устанавливаются в меню "Установки -> Параметры Процессора...".  Как правило, Вы
не должны изменять время цикла; 10 мс является хорошим значением для большинства
приложений. Введите частоту кристалла (или керамического резонатора и т.д.),
который вы будете использовать с микроконтроллером  и нажмите кнопку "ОК".

Теперь вы можете сгенерировать код для Вашей программы. Выберите "Компиляция ->
Компилировать", или "Компиляция -> Компилировать как...". Если нет ошибок, то
LDmicro cгенерирует файл Intel IHEX готовый для программирования в Ваш чип.

Используйте соответствующий софт и программатор, для загрузки "HEX" файла в MCU.
Не забудьте установить биты конфигурации (фьюзы)! Для процессоров PIC16, биты
конфигурации включены в "HEX" файл, и большинство программаторов "прошьет" их
автоматически. Для AVR процессоров необходимо установить биты конфигурации вручную.

Справочник Команд
=================

> CONTACT, NORMALLY OPEN
 КОНТАКТ,НОРМАЛЬНО ОТКРЫТЫЙ     Xname           Rname          Yname
                              ---] [----     ----] [----    ----] [----

    Контакт  Открытый.
    (Очевидная по электрике вещь прим. переводчика)



> CONTACT, NORMALLY CLOSED
 КОНТАКТ,НОРМАЛЬНО ЗАКРЫТЫЙ    Xname           Rname          Yname
                             ----]/[----     ----]/[----    ----]/[----

    Контакт  Закрытый.
    Эта команда инверсна по отношению к "КОНТАКТ,НОРМАЛЬНО ОТКРЫТЫЙ"


> COIL, NORMAL
 РЕЛЕ, НОРМА                   Rname           Yname
                             ----( )----     ----( )----

    Реле Нормальное.
    Команда должна быть крайней справа в ступени (завершающей)

> COIL, NEGATED
 РЕЛЕ, Инверсн.                 Rname           Yname
                             ----(/)----     ----(/)----

    Реле Инверсное. Инверсно по отношению к "Реле Нормальное"
    Команда должна быть крайней справа в ступени (завершающей)


> COIL, SET-ONLY
 РЕЛЕ, ТОЛЬКО УСТАНОВ          Rname           Yname
                             ----(S)----     ----(S)----

    Проще представить как триггер со входом SET.
    Обычно используется в паре с "РЕЛЕ, ТОЛЬКО СБРОС"
    Команда должна быть крайней справа в ступени (завершающей)


> COIL, RESET-ONLY
 РЕЛЕ, ТОЛЬКО СБРОС            Rname           Yname
                             ----(R)----     ----(R)----

    Проще представить как триггер со входом RESET.
    Обычно используется в паре с "РЕЛЕ, ТОЛЬКО УСТАНОВ"
    Команда должна быть крайней справа в ступени (завершающей)


> TURN-ON DELAY
 Таймер задержки ВКЛ           Tdon
                           -[TON 1.000 s]-

    При нарастающем фронте входного сигнала 0->1 выход в состоянии "0" на
    1 сек. а затем переходит в состояние "1". При спадающем фронте 1->0
    выход переходит в "0" немедленно. Таймер сбрасывается каждый раз, при
    входном уровне "0". Задержка в 1 сек. настраиваемая (дискрета настройки
    привязана к интервалу цикла ПЛК, т.е. если цикл. - 10 ms можно изменять
    интервал задержки с шагом 10 ms прим. переводчика и тд.).

    Переменная 'Tname' считает в "+" от 0 до значения TON. Возможно принудительно
    устанавливать 'Tname', например командой 'MOV'.
    Подходит для реализации входа с защитой от дребезга например, и как реле
    времени (прим. переводчика).


> TURN-OFF DELAY
 Таймер задержки ВЫКЛ          Tdoff
                           -[TOF 1.000 s]-

    При спадающем фронте входного сигнала 1->0 выход в состоянии "1" на
    1 сек. а затем переходит в состояние "0". При нарастающем фронте 0->1
    выход переходит в "1" немедленно. Таймер сбрасывается каждый раз, при
    входном уровне "1". Задержка в 1 сек. настраиваемая (дискрета настройки
    привязана к интервалу цикла ПЛК, т.е. если цикл. - 10 ms можно изменять
    интервал задержки с шагом 10 ms прим. переводчика и тд.).

    Переменная 'Tname' считает в "+" от 0 до значения TOF. Возможно принудительно
    устанавливать 'Tname', например командой 'MOV'.



> RETENTIVE TIMER
 Сохраняемый Таймер            Trto
                           -[RTO 1.000 s]-

    Эта команда запоминает длительность сигнала "1". Если длительность
    "1" не менее 1 сек., выход переходит в "1". Заметьте что сигнал
    может прерываться, т.е. вход "1" - 0.6сек, затем "0" - 2сек, а затем
    "1" - 0.4сек, только тогда выход переходит в "1" (т.е. происходит накопление
    длительности состояния входа "1" прим. переводчика). После этого
    независимо от состояния входного сигнала, выход в "1", до тех пор,
    пока он не будет сброшен командой RES (Сброс RTO/Счетчика).

    Переменная 'Tname' считает в "+" от 0 до значения RTO. Возможно принудительно
    устанавливать 'Tname', например командой 'MOV'.



> RESET
 RESET                        Trto             Citems
                           ----{RES}----     ----{RES}----

    Эта инструкция сбрасывает таймер или счетчик. Для таймеров задержки
    ВКЛ/ВЫКЛ - эта команда не нужна. Для сохраняемого Таймера и Счетчиков
    обязательна, они сбрасываются только этой инструкцией. При входе
    "1" происходит сброс, "0" не оказывает влияние на привязанный Таймер
    или Счетчик. Команда должна быть крайней справа в ступени (завершающей).
    Note! RES resets only numeric counter inside RTO, CTU/CTD,
    not their outputs!


> ONE-SHOT RISING
 Одновибратор по Нарастанию      _    _
                            --[_/ OSR/ \_]--

    Для электронщиков коротко: это одновибратор по нарастающему фронту.
    Для остальных: Обычное состояние выхода "0". Если в текущем шаге
    ПЛК вход "1", а в предыдущем шаге вход был "0" выход переходит
    в состояние "1", тем самым на длительность цикла ПЛК формируется
    положительный импульс на выходе. Этот инструкцию удобно использовать,
    как триггер нарастающего фронта сигнала.

> ONE-SHOT FALLING
 Одновибратор по Спаду         _      _
                            --[ \_OSF/ \_]--

    Для электронщиков коротко: это одновибратор по спадающему фронту.
    Для остальных: Обычное состояние выхода "0". Если в текущем шаге
    ПЛК вход "0", а в предыдущем шаге вход был "1" выход переходит
    в состояние "1", тем самым на длительность цикла ПЛК формируется
    положительный импульс на выходе. Этот инструкцию удобно использовать,
    как триггер спадающего фронта сигнала.


> SHORT CIRCUIT, OPEN CIRCUIT
 Перемычка, Разрыв
                           ----+----+----      ----+     +----

    Все очевидно из названия, в основном применяется при отладке.

> MASTER CONTROL RELAY
 МАСТЕР-РЕЛЕ
                           -{MASTER RLY}-

    По умолчанию все ступени выполняются на каждом цикле ПЛК. Если
    на входе МАСТЕР-РЕЛЕ "0", то на входах всех последующих ступеней
    "0" (расположенных ниже по схеме прим. переводчика). Инструкции
    по управлению МАСТЕР-РЕЛЕ должны идти в паре: одна на его выключение
    (возможно по условию), вторая на включение.


> MOVE
 Перемещение(Присвоение)   {destvar :=  }      {Tret :=     }
                          -{ 123     MOV}-    -{ srcvar  MOV}-

    Когда на входе команды "1" происходит перемещение в destvar
    значения (123) или переменной (srcvar). При входе "0" ничего не
    происходит. В качестве destvar (цели) могут использоваться таймеры и
    счетчики. Загружая в них 0 мы их сбрасываем. Команда должна быть крайней
    справа в ступени (завершающей).


> ARITHMETIC OPERATION
 АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ    {ADD  kay  :=}       {SUB  Ccnt :=}
                           -{ 'a' + 10   }-     -{ Ccnt - 10  }-

                            {MUL  dest :=}       {DIV  dv :=  }
                           -{ var * -990 }-     -{ dv / -10000}-

    Когда на входе команды "1", над целью производится соответсвующая
    арифметическая операция. Операнд может быть или переменной (включая
    таймеры и счетчики) или константой. Инструкции signed 16 bit.
    Помните, что если на входе 'висит' "1" - выражение вычисляется
    при каждом цикле ПЛК. Если вы хотите, чтобы выражение вычислялось
    один раз, поставьте последовательно с этим операндом одновибратор
    по нарастанию или спаду. При Делении частное обрезается, например
    8 / 3 = 2.Команда должна быть крайней справа в ступени (завершающей).

> COMPARE
 Сравнение             [var ==]        [var >]        [1 >=]
                      -[ var2 ]-      -[ 1   ]-      -[ Ton]-

                       [var !=]       [-4 <   ]       [1 <=]
                      -[ var2 ]-     -[ vartwo]-     -[ Cup]-
    Если на входе команды "0" то выход "0". Если на входе команды "1" то
    на выходе "1", но только тогда когда условие сравнения выполняется.
    Эта инструкция может быть использована для сравнения (равно,
    больше, больше или равно, не равно, меньше, меньше или равно)
    переменной с переменной, или сравнения переменной с 16-битовой константой.

> COUNTER
 Счетчик                      Cname          Cname
                           --[CTU >=5]--  --[CTD > -5]--

    Счетчик инкрементирует (CTU, +) или декрементирует (CTD, -) переменную
    Cname по каждому положительному фронту входной ступени (т.е. 0->1).
    Выход "1" при значении CTU >= 5 (CTD > -5), и "0" в противном случае.
    Можно назначать пару счетчиков CTU и CTD на одну переменную Cname.
    Один будет увеличивать переменную, второй уменьшать. Сбросить
    счетчик можно операцией RESET, или операций MOVE Cname->0 например.
    Интервал счета от -32768 до 32767, значение по умолчанию 0.

> CIRCULAR COUNTER
 Циклический Счетчик          Cname
                           --{CTC 0:7}--

    Круговой счетчик работает как обычный счетчик CTU, за исключением того,
    что после достижения верхнего предела, он сбрасывает свое значение
    обратно в 0. Например, счетчик показанный выше будет считать: 0, 1,
    2, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 2,.... Это полезно в
    сочетании с компаратором "==" по переменной 'CNAME '. Вы можете использовать
    эту конструкцию, как конечный автомат. Счетчик инкрементирует переменную
    Cname по каждому положительному фронту входной ступени (т.е. 0->1).
    Команда должна быть крайней справа в ступени (завершающей).


> SHIFT REGISTER
 Сдвиговый Регистр         {SHIFT REG   }
                           -{ reg0..3    }-

    Регистр сдвига связан с массивом переменных (правильно его считать чем-то
    наподобие FIFO-очереди прим. переводчика).
    Например,этот регистр сдвига связан с переменными 'reg0', 'reg1', 'reg2'и
    'reg3'.Входное число помещается в  'reg0' (командой MOV например
    прим. переводчика). на каждый положительный фронт входа ступени '0->1',
    регистр сдвига будет сдвигать свое содержимое вправо. Это означает, что
    присвоение данных происходит так: 'reg3: = reg2','reg2: = reg1' и 'reg1: = reg0'.
    'reg0" остается без изменений. Большой массив очереди сдвига может легко занять
    много памяти. Команда должна быть крайней справа в ступени (завершающей).

> LOOK-UP TABLE
 Таблица Просмотра         {dest :=     }
                          -{ LUT[i]     }-

    Справочная таблица представляет собой массив 16-ти битных значений.
    Когда вход ступени "1" , В Переменную 'Dest' записывается значение из таблицы
    LUT[i].Индекс начинается с нуля, поэтому "i" в интервале между 0 и (n-1).
    Поведение этой инструкции не предсказуемо, если индекс находится за пределами
    этого диапазона. Команда должна быть крайней справа в ступени (завершающей).

> PIECEWISE LINEAR TABLE
 Таблица Аппроксимации     {yvar :=     }
                          -{ PWL[xvar]  }-

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

        f(0)   = 2
        f(5)   = 10
        f(10)  = 50
        f(100) = 100

    Другими словами точки

        (x0, y0)   = (  0,   2)
        (x1, y1)   = (  5,  10)
        (x2, y2)   = ( 10,  50)
        (x3, y3)   = (100, 100)

    лежат на этой кривой. Вы можете ввести эти 4 точки в таблицу
    аппроксимации. Соообразно значению "xvar" будет установлено значение
    "yvar". Например, если вы установите xvar = 10, то yvar = 50.

    Но если значение xvar получает значение, что лежит между двумя
    значениями в таблице аппроксимации, то значение yvar будет вычислено
    значением на прямой линии, соединяющей эти две точки в таблице. Например,
    xvar =  55 дает выход yvar = 75. (Две точки в таблице - (10, 50) и (100, 100).
    55 между 10 и 100, и 75 находится между 50 и 100, так что (55, 75) лежит на
    линии, которая соединяет эти две точки.)

    Точки должны быть указаны в порядке возрастания координаты "х". Иногда
    может оказаться невозможным для выполнения математических операций, значений
    в справочной таблице с использованием 16-битной целочисленной арифметики, тогда
    LDmicro выдаст предупреждение. Например, эта таблица будет выдавать ошибку:

        (x0, y0)    = (  0,   0)
        (x1, y1)    = (300, 300)

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

        (x0, y0)    = (  0,   0)
        (x1, y1)    = (150, 150)
        (x2, y2)    = (300, 300)

    Вряд-ли оправдано использование более чем пяти или шести точек в таблице.
    Добавление большего кол.-ва элементов таблицы, увеличивает ваш код в размере
    и замедляет программу. Поведение, таблицы если вы передадите значение 'xvar'
    больше наибольшей координаты "x" в таблице или меньше, чем наименьшее "х"
    в таблице не предсказуемо. Команда должна быть крайней справа в ступени
    (завершающей).

> A/D CONVERTER READ
 Чтение из АЦП               Aname
                           --{READ ADC}--

    LDmicro может генерировать код для использования АЦП встроенного в
    некоторые MCU. Если входное условие "1", то производится выборка из
    АЦП которая сохраняется  в переменной 'Aname'. Эта переменная
    впоследствии может быть подвергнута арифметической обработке или
    сравнению. Пин АЦП должен быть привязан к выводу MCU как и цифровой вход.
    Если входное условие ступени ложно, то переменная 'Aname' остается без
    изменений.

    Для всех поддерживаемых устройств, 0 вольт входного напряжения соотв.
    чтению из АЦП "0", и вход равный Vdd (напряжение питания) соответствует
    чтению из АЦП "1023". Если вы используете AVR, то  подключайте AREF к Vdd.
    Вы можете использовать арифметические операции для масштабирования
    результата вычисления, но помните, что вы используете целочисленную математику.
    Не все выводы будут доступны для использования с преобразователем АЦП .
    Программа не позволит вам связать не -АЦП входы с командой "Чтение из АЦП".
    Команда должна быть крайней справа в ступени (завершающей).

> SET PWM DUTY CYCLE
 PWM Выход                  duty_cycle
                           -{PWM 32.8 kHz}-

    LDmicro может генерировать код для использования ШИМ-выхода встроенного в
    некоторые MCU. Если входное условие "1", то выход PWM выдает частоту со
    скважностью переменной duty_cycle. Скважность должна быть числом
    от 0 до 100. 0 соответствует минимальному уровню выхода, а 100 соответствует
    максимальному уровню выхода.

    Вы можете указать целевую частоту ШИМ, в Гц. Частота, которую Вы определили,
    не может быть в точности достижима, в зависимости от того, как она соотносится
    к тактовой частоте микроконтроллера. LDmicro выберет ближайшую достижимую
    частоту. Если ошибка большая, то он предупредит Вас. Более высокая скорость
    соответствует меньшему разрешению.

    Команда должна быть крайней справа в ступени (завершающей).
    Ран-Тайм Алгоритм ПЛК забирает ресурсы одного таймера для измерения временного
    интервала рабочего цикла. Поэтому PWM доступен на MCU не менее чем с 2-мя
    походящими таймерами. PWM использует пин CCP2 (НЕ CCP1) в PIC16 и OC2 (НЕ OC1A)
    в AVR.

> MAKE PERSISTENT
 Запоминаемая Переменная    saved_var
                         --{PERSIST}--

    Если на входе команды "1" то соответсвующая переменная "saved_var" автоматически
    сохраняется в EEPROM. Что означает, что "saved_var" сохранится, даже если MCU
    будет обесточен. Переменная будет перезаписываться только при изменении ее
    значения. "saved_var" восстановит свое значение при подаче питания. Не следует
    сохранять в Запоминаемую Переменную значение которое изменяется очень часто,
    поскольку кол-во допустимых циклов перезаписи яйчейки EEPROM ~ 100000.
    Команда должна быть крайней справа в ступени (завершающей).

> UART (SERIAL) RECEIVE
 Получить из UART                 var
                           --{UART RECV}--

    LDmicro может генерировать код для использования UART встроенного в MCU.
    У AVR с несколькими портами UART поддерживается только UART1 (не UART0).
    Настройте скорость передачи используя "Установки -> Параметры Процессора".
    Некоторые скорости передачи данных не могут быть достигнуты с определенными
    частотами кварца. LDmicro предупредит Вас, в этом случае об ошибке.

    Если на входе команды "0" то ничего не происходит. Если на входе команды
    "1" то оператор пытается получить одиночный символ из UART.
    Если не удается прочитать символ, выходное условие - "false". Если символ
    прочитан, тогда ASCII значение сохраняется в 'var' и выходное условие
    оператора - "true" до конца одного рабочего цикла ПЛК.

> UART (SERIAL) SEND
 Послать в UART              var
                           --{UART SEND}--

    LDmicro может генерировать код для использования UART встроенного в MCU.
    У AVR с несколькими портами UART поддерживается только UART1 (не UART0).
    Настройте скорость передачи используя "Установки -> Параметры Процессора".
    Некоторые скорости передачи данных не могут быть достигнуты с определенными
    частотами кварца. LDmicro предупредит Вас, в этом случае об ошибке.

    Если на входе команды "0", то ничего не происходит. Если на входе команды
    "1", то оператор записывает одиночный символ в UART. ASCII значение символа
    должно быть предварительно сохранено в 'var'. Выходное условие
    оператора - "true" пока UART занят (передает символ прим. переводчика), и
    наоборот как символ будет передан через UART передатчика - перейдет в
    состояние"false".

    Помните, что символу потребуется некоторое время для передачи. Проверьте
    выход состояния этой инструкции, чтобы убедиться что символ передан перед
    тем, как отправить следующий, или используйте таймер, чтобы вставить
    гарантированную задержку между символами. Вы должны передавать символ
    (выставлять на вход команды "true"), только если выход оператора "false",
    т.е. передатчик UART не занят.

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

> FORMATTED STRING OVER UART
 Форматированная Cтрока через UART         var
                                   -{"Pressure: \3\r\n"}-

    LDmicro может генерировать код для использования UART встроенного в MCU.
    У AVR с несколькими портами UART поддерживается только UART1 (не UART0).
    Настройте скорость передачи используя "Установки -> Параметры Процессора".
    Некоторые скорости передачи данных не могут быть достигнуты с определенными
    частотами кварца. LDmicro предупредит Вас, в этом случае об ошибке.

    По положительному фронту входной ступени (т.е. 0->1). начинается передача
    символьной строки через UART. Если в строке содержится кодовая
    последовательность '\3' (или \1.. \5.. \10 и т.д. прим. переводчика), она
    заменяется на значение переменной "var", которое автоматически
    конвертируется в символьный вид. В данном случае манипулятор ('\3') будет
    сконвертирован ровно в 3 символа, например: 'var' = 35, тогда передаваемая
    через UART строка будет точно - 'Pressure:  35\r\n' (обратите внимание на
    добавленный символ пробела перед 35). Если 'var' = 1432, содержимое
    выводимой строки непредсказуемо, поскольку в числе 1432 более 3-х цифр.
    В данном случае необходимо использовать манипулятор '\4'.


    Если переменная отрицательна, используйте манипулятор '\-3d' (или `\-4d'
    и т.д.). Это заставит LDmicro печатать дополнительный пробел перед
    положительным числом, и знак '-' перед отрицательным.

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

    Также возможно передавать этой командой обычные текстовые строки, без
    каких- либо манипуляторов с числами. Просто не включайте в таком случае
    манипулятор '\xx' в передаваемую строку.

    Используйте манипулятор '\\' для передачи символа "\". Также возможно
    передавать следующие управляющие символы в строке:
        * \r   -- carriage return (возврат каретки)
        * \n   -- newline (переход строки)
        * \f   -- formfeed (перевод страницы)
        * \b   -- backspace (забой)
        * \t   -- horizontal tab
        * \v   -- vertical tab
        * \a   -- alert
        * \xAB -- символ ASCII c числовым значение 0xAB (hex)

    Выход инструкции "1" во время передачи данных, в противном случае "0".
    Эта команда занимает большой обьем памяти программы (незабываем,
    что архитектура процессоров AVR и PIC16 - Гарвардская прим. переводчика).
    Поэтому используйте данную инструкцию с осторожностью.
    Текущая реализация этой команды не оптимизирована, но лучшая реализация
    потребует полной модификации ядра программы.


Замечания по использованию Арифметики
=====================================

Помните, что LDmicro выполняет вычисления только над 16-bit числами.
Это означает, что конечный результат любого вычисления, которое Вы
выполняете должнен быть числом от -32768 до 32767. Это также означает,
что промежуточные результаты Ваших расчетов всегда должны быть в
пределах этого диапазона.

Например, скажем, что Вы хотите вычислить у = (1 / х) * 1200,
где "х" в интервале между 1 и 20. Тогда "у" будет между 1200 и 60 -
результат вписывается в 16-разрядное целое число, так что теоретически
возможно выполнить вычисления. Есть два способа, которыми Вы можете
вычислить выражение. Вы можете поделить, а затем умножить:

   ||         {DIV  temp  :=}          ||
   ||---------{ 1 / x       }----------||
   ||                                  ||
   ||          {MUL  y  :=  }          ||
   ||----------{ temp * 1200}----------||
   ||                                  ||

Или выполнить деление сразу, одним шагом:

   ||           {DIV  y  :=}           ||
   ||-----------{ 1200 / x }-----------||

Математически, оба выражения эквивалентны, но первое даст неправильный
результат: y = 0. Потому, что переменная 'temp' меньше единицы.
Например, когда x = 3, то (1 / x) = 0.333, но это не целое; округление
даст temp = 0. Поэтому y = temp * 1200 = 0. Во втором случае нет
промежуточного результата , и все будет OK.

Если у Вас появится ошибка в вычислениях, проверьте промежуточный результат
на предмет ошибки округления до 0 (см. выше прим. переводчика), или переполнения
которое "завернет" число, например: 32767 + 1 = -32768. Если возможно, выбирайте
числа в диапазоне от -100 до 100 (ограничьтесь диапазоном signed char
прим. переводчика).

Когда Вам необходимо смасштабировать переменную на некоторую величину,
используйте умножение и деление. Например, чтобы получить масштаб у = 1.8*x,
примените выражение y = (9/5)*x (что эквивалентно, так как 1.8 = 9/5), и
запрограммируйте его как y = (9*x)/5, первым вычисляя произведение:

   ||         {MUL  temp  :=}          ||
   ||---------{ x * 9       }----------||
   ||                                  ||
   ||           {DIV  y  :=}           ||
   ||-----------{ temp / 5 }-----------||

Этот код будет работать правильно при  x < (32767 / 9) или x < 3640. Для больших
значений 'x', переменная 'temp' будет переполнена. Аналогично ограничен нижний
предел x (надо считать допустимую погрешность округления при малых значениях х,
прим. переводчика).


Стиль программирования
======================


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

   ||       Xa               Ya        ||
 1 ||-------] [--------------( )-------||
   ||                                  ||
   ||       Xb               Yb        ||
   ||-------] [------+-------( )-------||
   ||                |                 ||
   ||                |       Yc        ||
   ||                +-------( )-------||
   ||                                  ||

Вместо этого:

   ||       Xa               Ya        ||
 1 ||-------] [--------------( )-------||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||       Xb               Yb        ||
 2 ||-------] [--------------( )-------||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||       Xb               Yc        ||
 3 ||-------] [--------------( )-------||
   ||                                  ||


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

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

                  *                 *                  *

В целом, считается плохим тоном писать программу таким образом, что
результат вычислений зависит от порядка ступеней. Например, этот код
работает не очень хорошо, если оба входа 'Xa' и 'Xb' будут замкнуты
(результат входа Xb будет "перекрывать" Xa, что должен учитывать программист
прим. переводчика):

   ||       Xa         {v  :=       }  ||
 1 ||-------] [--------{ 12      MOV}--||
   ||                                  ||
   ||       Xb         {v  :=       }  ||
   ||-------] [--------{ 23      MOV}--||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||      [v >]             Yc        ||
 2 ||------[ 15]-------------( )-------||
   ||                                  ||

Я тем не менее нарушаю это правило, если при этом я могу сделать кусок кода
значительно более компактным. Например, вот как я преобразую 4-разрядное
двоичное число из входов 'XB3:0' в целое число 'v':

   ||                                   {v  :=       }  ||
 3 ||-----------------------------------{ 0       MOV}--||
   ||                                                   ||
   ||       Xb0                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 1    }-----------||
   ||                                                   ||
   ||       Xb1                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 2    }-----------||
   ||                                                   ||
   ||       Xb2                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 4    }-----------||
   ||                                                   ||
   ||       Xb3                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 8    }-----------||
   ||                                                   ||


Если команда MOV будет перемещена вниз программы вместо верхнего
положения, тогда 'v' если она будет читаться где-либо в программе будет
всегда 0. Надо учитывать что реальное выполнение ступеней по шагам идет
сверху вниз.

ОШИБКИ
======

LDmicro не генерирует очень эффективный код, он медленен и не оптимально
расходует флэш-память и оперативую память. Несмотря на это, среднего класса
PIC или AVR может делать все, что небольшие ПЛК , так что это не беспокоит
меня сильно.

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

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

Нерегулярное сохранения в файл сохранения/загрузки програмы ('.ld'), вероятно,
может привести к краху программы или порче файла '.ld' (Чаще сохраняйте
результат работы LDmicro, делайте резерные копии программы прим. переводчика).

Пожалуйста, сообщайте о ошибках и задавайте вопросы автору..

Благодарности:
    * Marcelo Solano, for reporting a UI bug under Win98
    * Serge V. Polubarjev, for not only noticing that RA3:0 on the
      PIC16F628 didn't work but also telling me how to fix it
    * Maxim Ibragimov, for reporting and diagnosing major problems
      with the till-then-untested ATmega16 and ATmega162 targets
    * Bill Kishonti, for reporting that the simulator crashed when the
      ladder logic program divided by zero
    * Mohamed Tayae, for reporting that persistent variables were broken
      on the PIC16F628
    * David Rothwell, for reporting several user interface bugs and a
      problem with the "Export as Text" function


Ограничения, и Правовая ответственность
=======================================

НЕ ИСПОЛЬЗУЙТЕ КОД, СГЕНЕРИРОВАННЫЙ LDMICRO В ПРИЛОЖЕНИЯХ, ГДЕ ОТКАЗ
ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ МОЖЕТ ПРИВЕСТИ К ОПАСНОСТИ ДЛЯ ЧЕЛОВЕЧЕСКОЙ ЖИЗНИ
ИЛИ ПОВРЕЖДЕНИЮ ИМУЩЕСТВА. АВТОР НЕ НЕСЕТ ОТВЕТСТВЕННОСТИ ЗА УЩЕРБ В
РЕЗУЛЬТАТЕ ВОЗМОЖНЫХ ПОВРЕЖДЕНИЙ ОТ ИСПОЛЬЗОВАНИЯ LDMICRO ИЛИ КОДА,
СГЕНЕРИРОВАННОГО LDMICRO.

Данная программа является свободным программным обеспечением:
Вы можете распространять и / или изменять ее на условиях
GNU General Public License, опубликованных Free Software Foundation,
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

Эта программа распространяется в надежде, что она будет полезной, но
БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ. Смотрите GNU General Public License для более
подробной информации.

Вы должны получить копию GNU General Public License вместе
с этой программой. Если нет, смотрите <http://www.gnu.org/licenses/>.


COPYING, AND DISCLAIMER
=======================

DO NOT USE CODE GENERATED BY LDMICRO IN APPLICATIONS WHERE SOFTWARE
FAILURE COULD RESULT IN DANGER TO HUMAN LIFE OR DAMAGE TO PROPERTY. THE
AUTHOR ASSUMES NO LIABILITY FOR ANY DAMAGES RESULTING FROM THE OPERATION
OF LDMICRO OR CODE GENERATED BY LDMICRO.

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.


Jonathan Westhues

Rijswijk      -- Dec 2004
Waterloo ON   -- Jun, Jul 2005
Cambridge MA  -- Sep, Dec 2005
                 Feb, Mar 2006
                 Feb 2007
Seattle WA    -- Feb 2009

Email: user jwesthues, at host cq.cx



Переведено: Ибрагимов М.Р.
Россия Тольятти 06.2011.г.


