Корректная реализация разностной схемы ПИД регулятора / Habr

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

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

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

Причем проверить качество реализации ПИД регулятора крайне легко.

Рассмотрим простейший пример: терморегулятор. Для проверки его качестве лучше всего подходит быстрый, малоинерциальный, маломощный объект. Классика жанра: обычная лампочка на 100Вт с прикрученной к ней тонкой термопарой (ХА). И первое, на чем следует проверять ПИД регулятор — деградация ПИД до просто П-регулятора. То есть коэффициенты интегральной и дифференциальной ставим в ноль, а пропорциональную ставим в максимум.

Включаем регулятор, проверяем: текущая температура 22 градуса, уставка 16 градусов. Лампочка не горит. Начинаем начинаем увеличивать уставку: 16.1, 16.3, 16.7, 18… 19… лампочка загорелась. Как?! Откуда?! Останавливаемся — выключилась. Итак, мы встретились с первой классической ошибкой реализации ПИД регулятора.

Небольшое математическое отступление: вспомним еще раз интегральную запись указанную выше. Мы реализуем её програмно, а значит — дискретно. То есть с завидной регулярностью производим измерение входной величины, сравниваем её с уставкой, вычисляем воздействие, выдаём, повторить. А значит, надо из интегральной формы перейти в конечно-разностную схему. При переходе обычно используется переход «в лоб»:


где E(n) = X(n) - X0(n) — то есть величина рассогласования между текущим и заданным значением регулируемого параметра.

Использование прямой формулы потребует во-1х считать и хранить интеграл рассогласований за большой период, во-2х требует работы с плавающей точкой высокой точности (так как интегральный коэффициент Ki всегда < 1), либо операции деления (представляя коэффициент в форме 1/Ki) большой разрядности. Всё это требует вычислительных ресурсов, коих в embedded как правило сильно ограничено… Поэтому, вместо реализации прямой схемы, реализуют рекуррентную формулу:

использование рекуррентной формулы позволяет сократить объём вычислений и разрядность промежуточных значений.

Итак, вернёмся к нашему регулятору. Итак, есть регулируемый объект: лампочка. Для управления подаваемой на неё мощностью, используют простой подход: сеть питания (220В 50Гц) подаётся через симистор на нагрузку. Симистор в момент перехода полуволны через ноль выключается, и остаётся выключен до тех пор, пока не будет подан сигнал на управляющий электрод. Таким образом, чем раньше после начала полуволны мы подадим управляющий сигнал, тем больше энергии от этой полуволны достигнет управляемого объекта. Правильно расчитав время для линейности площади части полуволны с момента времени X до конца полуволны, мы получаем возможность выдавать мощность от 0 до 100% с точностью, с которой расчитали таблицу линеаризации.

Итак, мы можем выдавать мощность от 0 до 100%. В реальных объектах, часто нельзя выдавать 100% мощности — например, это черевато перегоранием нагревательного элемента. Поэтому, все приборы имеют настройку минимальной и максимальной выдаваемой мощности на объект.

Итак, после вычисления U(n) по вышестоящей формуле, добавляется еще ограничение результата:
if Un < Umin then Un := Umin; if Un>Umax then Un := Umax;
После чего, вычисленное Un и есть требуемая выходная мощность на текущий момент. Та-дам! Именно вот эта реализация и создаёт ошибку, о которой написано выше.

Причина банальна: в момент перехода от дикретной к конечно-разностной схеме, мы «выносим за скобки» операцию вычисления интеграла, и на каждом шаге мы прибавляем производную к накопленной сумме U(n-1). Наложив же на неё ограничение, мы весь вычисленный интеграл фактически обнуляем. (Ну не сколько обнуляем, сколько приводим к диапазону 0-100, что в данном случае несущественно). Таким образом, мы дифференциурем ПИД регулятор, и остаётся дифференциально-ускорительный. Что в реальности выглядит как просто дифференциальный регулятор — мощность при этом подаётся пропорционально изменению уставки или регулируемой величины, а не пропорционально разности между уставкой и регулируемой величиной.

Вывод №1: вычисление U(n) нельзя ограничивать. Для ограничения мощности подаваемой на выходное устройство следует заводить отдельную переменную.

Теперь, когда мы завели Urn, для ограниченной мощности, перезаливаем, продолжаем тестировать.
Включаем регулятор, проверяем: текущая температура 22 градуса, уставка 16 градусов. Лампочка не горит.
Начинаем прибавлять уставку: 16.1, 16.4, 17, 18, 20, 22, 24 (опа! засветилось! ура!), 28, 30, 40, 60… Красота! Работает!
Наблюдаем за процессом — вышло примерно на 60, болтается чуток туда-сюда, но держит. Вроде, всё красиво. Выдыхаем, проверяем управление с ПК: задаём 600 градусов. И… Лампочка выключается. Как так? Уставка 600, текущая 60, а лампочка не горит?

Пока ждём и медленно осознаём, что мы явно напоролись на какой-то «Классический Косяк №2» ™ лампочка медленно разгорается, выходит на 100% мощности, и так и остаётся — 600-то градусов она выдать ну никак не может.

Возвращаемся снова к нашей разностной схеме. U(n) = U(n-1) + Kp*(dE + ...). К текущему расчетному значению воздействия прибавляется разность невязки помноженная на коэффициент пропорциональности. У нас была уставка 60, температура 60, то есть невязка нулевая. Выходная мощность так же была нулевая. И тут разом, скачком, уставку увеличили до 600 градусов. невязка резко стала 540 градусов, помножили еще и на коэффициент пропорциональности… и вылетели за разрядность хранения U(n). Не смейтесь, использование математики с фиксированной точки, вместо плавающей точки. При разнице в 540 градусов и работе через 1/16, при коэффициенте пропорциональности 20, получаем… 540*20*16=172800, а если у нас 16тиразрядный U(n), да еще и знаковый, то фактически, в результате вычисления мы получили A300h = −8960. Опачки. Вместо большого плюса — ощутимый такой минус.

Вывод №2: вычисления должны проводиться с корректной поддержкой переполнения. Переполнилось? Ограничь предельным числом, уж никак не заворачивать.

Итак, нарастили разрядность U(n), перетранслировали, зашили, запускаем. Лампочка еще не совсем остыла, там 80 градусов, уставка всё те же 600. Лампочка зажигается… и тухнет. Зажигается и тухнет. Как так? Уставка 600, лампочка 80 — и поддерживает вполне себе свои 80! Как так-то?! Явно у нас вылез Жучок №3.

И снова лирически-математическое отступление. Итак, есть наша разностная схема: U(n) = G(U(n-1), dE(n)). Еще раз: новое значение воздействия есть сумма прошлого воздействия и некого воздействия, зависящего от разности невязки в текущий момент и предыдущий. А что такое предыдущий момент? А какой момент предыдущий у предыдущего? Ну-ка, вспомнили школу. Доказательство по индукции. Если можно построить доказательство для K+1, считая что доказательство для K верно, И доказать отдельно что верно для K=0, тогда доказательство истинно. Итак, а как мы считаем U(0)?

Часто встречающееся решение: всё обнуляем, уставку считываем из флешпамяи уставки, ждём 1 цикл опроса, и считываем X(0). Вот, ноль готов, теперь работаем. И… И не правильно. Почему? Потому что рекурентная формула отталкивается от изменений в невязке. А проинициализировав нулём и загрузив текущие значения мы потеряли стартовые условия. Всё — вместо поддержания абсолютного значения температуры на уровне равном абсолютной уставке регулятор начинает держать температуру равную стартовой плюс разнице уставки. То есть было 80 градусов и уставка 200, включили прибор — он держет 80. Сменили уставку на 240 — он начал держать 120.

Правильная инициализация разностной схемы: обнулить _вообще всё_. То есть
X(0) = 0, X0(0) = 0. U(0) = 0. E(0)=X(0)-X0(0)=0.
И на первом же цикле вычислений у нас как бы скачком появляются уставке и текущее значение:
X(1) = 80. X0(1)=200. U(1) = U(0)+Kp*(E(1)-E(0)) = U(0)+Kp*(X(1)-X0(1)-E(0)) = 0 + 20*(200 - 80 - 0) = 2400


Вот теперь схема работает правильно.

Вывод №3: корректно инициализируй стартовые условия.

Правильно ли? Hу-ка, ну-ка… Еще раз… Ставим уставку 20. Ждём охлаждения… Выключаем. Включаем. Итак, красота: текущая 20, уставка 20. Ставим скачком 600. Поехало греться. 100, 120… ставим уставку 20. Отключилось, пошло охлаждаться. Ждём чуток (120… 110… 100… 90… 80…) и ставим уставку 100. Поехало греться… 105 градусов, отключилось. Стоп. А почему оно держит 105? У нас же сейчас работает только пропорциональная составляющая. При правильной реализации из физического смысла процесса колебательный процесс не может держать уставку выше чем задано. Строго ниже. А держит на 5 градусов больше чем попросили. Это наблюдается Прикол №4.

Итак, вспоминаем что у нас было выше: Вывод№2: U(n) нельзя ограничивать. И Вывод№3: при переполнении ограничить всё-таки придётся. Да-да. Иначе «рабочая точка» смещается на ограниченный момент. Что же делать? Увеличить разрядость? Хорошо, если хватает вычислительной мощности. А надо ли? Собственно, что плохого, что у нас U(n) = 9999.99, а не 29999.99? В общем-то только то, что мы потеряли 20000. Но сейчас-то для работы нам так и так надо вваливать просто 100% мощности, правильно? Правильно. Значит, проблемы с ограничением в полку нет, до тех пор, пока мы не отходим от предела. Таким образом, в случае переполнения надо ставить флаг, и по достижении, например, половины диапазона (то есть как U(n) после 9999.9 опустилось ниже 5000.00), заново реинициализировать схему. То есть отбрасывать историю, сказать что n=0 и см. выше Вывод №3. Пытливый ум уже сообразил, что в случае полной схемы, когда все три компоненты не равны нулю, обнуляя в процессе итеративный процесс, мы в том числе обнуляем накопленный интеграл интегральной составляющей. Однако, в связи с тем, что обнуляем мы значительно заранее, он успеет подкопиться за время довырабатывания остатка. Да и не совсем корректно копить интеграл на «больших» перегонах, так как цель интегральной составляющей — «выбрать» невязку, которую не может отработать пропорциональная составляющая отдельно.

Вывод №4: если по какой-то причине U(n) было ограничено, схему следует переинициализировать как только создалось впечатление, что схема вернулась в нормальное состояние.

В следующем выпуске: а так ли надо реализовывать разностную схему? Подробная реализация прямой дискретной схемы, обладающей простыми и понятными настраиваемыми коэффициентами, с прямым физическим смыслом, которая без проблем вычисляет управляющее воздействие с частотой 25Гц на процессоре ADuC847 (быстрый 8-битный контроллер, с ядром 8051), оставляя еще море процессорного времени для других процессов.

(Картинки с изображением формул взяты из статьи ПИД-Регулятор в Wikipedia)

habr.com

Настройка коэффициентов ПИД-регулятора. | MicroTechnics

В предыдущей статье мы разобрались с принципом работы ПИД-регулятора (ссылка). Теперь, как и обещал, рассмотрим основные методы настройки и подбора его коэффициентов ) Вообще, по большому счету, при использовании ПИД-регулятора необходимо построить модель всей системы в целом и математически вычислить необходимые значения коэффициентов. Так делать правильно. Но, естественно, так никто не делает 😉 На самом деле, математический расчет коэффициентов задача далеко не тривиальная, требует глубоких знаний теории автоматического управления, поэтому и используются другие, упрощенные, методы настройки.


Наиболее часто использующимся методом настройки коэффициентов является метод Циглера-Никольса. Заключается он в следующем…

Метод Циглера-Никольса.

  • Для начала обнуляем все коэффициенты регулятора (пропорциональный, интегральный и дифференциальный)
  • Постепенно начинаем увеличивать пропорциональный коэффициент и следим за реакцией системы. При определенном значении возникнут незатухающие колебания регулируемой величины.
  • Фиксируем коэффициент  , при котором это произошло. Кроме того, замеряем период колебаний системы .

Собственно, на этом практическая часть метода заканчивается ) Из полученного коэффициента рассчитываем пропорциональный коэффициент ПИД-регулятора:

   

А из него получаем и остальные:

   

   

Метод довольно прост, но применить его можно далеко не всегда. Если честно, мне еще ни разу не приходилось настраивать регулятор таким образом 😉 Но тем не менее, этот метод является основным и, по большому счету, единственным широко известным. Просто подходит не всем и не всегда.

Что же делать, если метод Циглера-Никольса не сработал? Тут придет на помощь “аналитический” метод настройки =)

Опять же обнуляем все коэффициенты и начинаем увеличивать пропорциональный. Но теперь не ждем появления колебаний, а просто фиксируем поведение системы для каждого значения коэффициента (отличным вариантом будет построение графика величины, которую необходимо стабилизировать, для каждого значения коэффициента). Если видим, что, например, система очень медленно выходит на нужное значение, увеличиваем пропорциональный коэффициент. Система начинает сильно колебаться относительно нужной величины? Значит, коэффициент слишком велик, уменьшаем и переходим к настройке других составляющих.

Понимая, как работает ПИД-регулятор в целом, и представляя, как должна работать настраиваемая система, можно довольно-таки быстро и точно настроить коэффициенты регулятора. Особенно, если есть возможность построить графические зависимости и визуально следить за поведением системы.

Вот некоторые правила, которые могут помочь при настройке:

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

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

На этом на сегодня все, возможно, как-нибудь рассмотрим практическую реализацию ПИД-регулятора 😉

microtechnics.ru

ПИД-регулятор. Принцип работы. | MicroTechnics

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


Итак, что у нас имеется?

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

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

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

Пропорциональная составляющая.

Здесь все просто, берем значение нужной нам температуры (уставку) и вычитаем из него значение текущей температуры. Получаем рассогласование (невязку). Умножаем полученную невязку на коэффициент и получаем значение мощности, которое и передаем на нагреватель. Вот и все ) Но при использовании только пропорциональной составляющей есть два больших минуса – во-первых, эффект от нашего воздействия наступает не моментально, а с запаздыванием, и, во-вторых, пропорциональная составляющая никак не учитывает воздействие окружающей среды на объект. Например, когда мы добились того, чтобы температуры объекта была равна нужному нам значению, невязка стала равна нулю, а вместе с ней и выдаваемая мощность стала нулевой. Но температура не может просто так оставаться постоянной, поскольку происходит теплообмен с окружающей средой и объект охлаждается. Таким образом, при использовании только пропорциональной составляющей температура будет колебаться около нужного нам значения.

Давайте разбираться, как ПИД-регулятор решает две выявленные проблемы )

Для решения первой используется дифференциальная составляющая. Она противодействует предполагаемым отклонениям регулируемой величины, которые могут произойти в будущем. Каким образом? Сейчас разберемся!

Итак, пусть у нас текущая температура меньше нужного нам значения. Пропорциональная составляющая начинает выдавать мощность и нагревать объект. Дифференциальная составляющая вносит свой вклад в мощность и представляет из себя производную невязки, взятую также с определенным коэффициентом. Температура растет и приближается к нужному значению, а следовательно невязка в предыдущий момент больше текущего значения невязки, а производная отрицательная. Таким образом, дифференциальная составляющая начинает постепенно снижать мощность до того, как температура достигла необходимого значения. С этим вроде разобрались, вспоминаем про вторую проблему регулятора 😉

А с ней нам поможет справиться интегральная составляющая. Как нам в программе получить интеграл? А легко – просто суммированием (накоплением) значений невязки, на то он и интеграл ) Возвращаемся к нашему примеру. Температура ниже значения уставки, начинаем подогревать. Пока мы нагреваем, значение невязки положительное и накапливается в интегральной составляющей. Когда температура “дошла” до нужного нам значения, пропорциональная и дифференциальная составляющая стали равны нулю, а интегральная перестала изменяться, но ее значение не стало равным нулю. Таким образом, благодаря накопленному интегралу мы продолжаем выдавать мощность и нагреватель поддерживает нужную нам температуру, не давая объекту охлаждаться. Вот так вот просто и эффективно =)

В итоге мы получаем следующую формулу ПИД-регулятора:

Тут  u(t) – искомое выходное воздействие, а e(t) – значение невязки.

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

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

microtechnics.ru

STMicroelectronics анонсировала новую DSP библиотеку для микроконтроллеров семейства STM32 | Новости микроэлектроники

ООО “ДИАЛ-ЭЛЕКТРОЛЮКС” → STMicroelectronics анонсировала новую DSP библиотеку для микроконтроллеров семейства STM32

STMicroelectronics, мировой лидер в производстве микроконтроллеров, анонсировала 28 октября 2008 года DSP библиотеку для своего семейства STM32.

Это программное обеспечение позволяет разработчикам использовать все преимущества STM32 для обработки сигналов и управляющих функций на одном и том же ядре. ST является первым поставщиком микроконтроллеров на базе ядра ARM Cortex-M3, который обеспечивает свою продукцию расширенной DSP библиотекой, и позволяет оптимизировать приложения по производительности и экономить время разработки и выхода на рынок.

Библиотека DSP STM32 абсолютно свободна от платежей как лицензионных так и от процентов с продаж устройств (так называемых royalty).

Среди многочисленных полезных функций, которые включены в библиотеку: ПИД-регулятор (PID controller), функции преобразования Фурье, и набор цифровых фильтров, например 16-битный КИХ-фильтр и БИХ-фильтр в прямой и канонической форме. Все функции готовы к использованию, легко интегрируются в проект и подробно документированы.

Функции библиотек используют в полной мере возможности ядра ARM Cortex-M3 по цифровой обработке сигнала, что позволяет достигнуть максимальной производительности. Например такие операции как умножение с накоплением и аппаратное деление выполняется в пределах двух циклов. Например, демо-программа запущенная на STM32F103 Performance Line выполняет 16-битное Быстрое Преобразование Фурье по 256 точкам с корнем-4 в течении всего 362мкс.

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

Новая DSP библиотека может использоваться совместно со средствами разработки от IAR, Keil и Raisonance для STM32. Это позволит разработчикам сократить стоимость системы и ускорить разработку в таких приложениях как цифровое управление питанием, включая солнечные батареи, импульсные источники питания с обратной связью, цифровая обработка звука, речи и изображений.

Помимо DSP библиотеки для STM32, разработчики получают от этого семейства выдающиеся параметры по энергопотреблению. Не стоит забывать про богатый набор периферии, который включают сдвоенный 12-битный ЦАП, и 12-битный АЦП со скоростью до 1 миллионов выборок в секунду и 16-битные ШИМ таймеры. На данный момент доступны три линейки STM32. Это Access Line с таковой частотой 36МГц и Performance Line 72МГц с объемом Flash памяти от 16Кбайт до 512Кбайт, а также Access Line с тактовой частотой 48МГц с Flash памятью до 128Кбайт представленные в Августе 2008 года. В итоге семейство STM32 насчитывает 60 моделей, все совместимы аппаратно, программно и поддерживаются одними и теми же средствами разработки.

Библиотека DSP STM32 доступна на сайте www.st.com Стоимость микроконтроллеров STM32 начинается от 1,68$* для партий 10000 штук.

Подробное описание библиотеки (DataSheet)

www.chipfind.ru

Написать программу для STM 32 : ПИД-регулятор… : написать программу, фриланс, FL.ru

Бесплатно зарегистрируйся и получай уведомления о новых проектах по работе

Заказчик

Отзывы фрилансеров: + 0 0 – 0

Зарегистрирован на сайте 7 лет и 6 месяцев

Бюджет: 5000 р./проект

76.15 $ — 66.54 €

Ищу программиста который напишет программу для STM 32!
Задача: обеспечить стабильность скорости при разного рода механических нагрузках на валу.
Т.е. нужен ПИД-регулятор (или даже ПИ-регулятор)

Есть двигатель постоянного тока.
Есть трехфазный тиристорный мост которая от регулятора подает на якорь установленное значение напряжения и двигатель вращается от 5 до 1500 об/мин.
В качестве датчика скорости планирую использовать как энкодер так и тахогенератор.
Контроллер планируется STM 32 (конкретную модель (платку) нужно выбрать с тем кто возьмется написать программу.
Есть структурная схема. Вышлю для ознакомления при конкретном обсуждении. Кратко: STM 32 + аналоговые входа + цифровой вход энкодера + 4 кнопки + ЖК от нокиа + выхода
Если у кого то есть реальный опыт в подобном проекте то это приветствуется.

Разделы:

Опубликован:

16.06.2017 | 09:47

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

www.fl.ru

AVR221: Дискретный ПИД-регулятор – chipenable.ru

   

• Простой дискретный алгоритм ПИД регулятора

• Поддерживается всеми микроконтроллерами AVR

• ПИД функция использует 534 байта flash памяти и 877 циклов процессора (IAR — low size оптимизация)

   Это руководство описывает простую реализацию дискретного пропорционально-интегрально-дифференциального (ПИД) контроллера.

 

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

   
   В течение долгого времени в области управления использовались многие решения, но ПИД-регуляторы могут стать “промышленным стандартом” из-за своей простоты и хорошей производительности.

 

   Для получения дополнительной информации о ПИД-регуляторах и их применении читатель должен обратиться к другим источникам, например, PID Controllers by K. J. Astrom & T. Hagglund (1995)

 

Рисунок 1-1. Типичные отклик ПИД-регулятора на ступенчатое изменение опорного сигнала

   На рисунке 2-1 показана схема системы с ПИД-регулятором. ПИД-регулятор сравнивает измеренное значение процесса Y с заданным опорным значением Y0. Затем разница, или ошибка, E, обрабатывается для расчета нового входного процесса U. Этот новый входной процесс будет пытаться приблизить значение измеряемого процесса к заданному значению.

 

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

 

Рисунок 2-1. Управляющая система с замкнутым контуром на основе ПИД-регулятора


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

 

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

 

   Рисунок 2-2 показывает схемное решение ПИД-регулятора, где Тр, Ti, и Td обозначают постоянные времени пропорциональной, интегральной и дифференциальной составляющих соответственно. 

 

Рисунок 2-2. Схема ПИД-регулятора

 

   Пропорциональная составляющая (П) дает управляющий сигнал пропорционально вычисленной ошибке. Использование только одного пропорционального управления дает стационарную ошибку всегда, кроме случаев, когда управляющий сигнал равен нулю, а значение системного процесса равно требуемой величине. На рис. 2-3 стационарная ошибка в значении системного процесса появляется после изменения опорного сигнала (ref). Использование слишком большого П-члена даст неустойчивую систему.

 

Рисунок 2-3. Отклик П контроллера на ступенчатое изменение опорного сигнала


 

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

 

Рисунок 2-4. Отклик И- и ПИ-контроллера на ступенчатое изменение контролируемой величины

 

   Дифференциальная составляющая (Д) представляет собой скорость изменения ошибки. Добавление этой составляющей улучшает отклик системы на внезапное изменение ее состояния. Дифференциальная составляющая Д обычно используется с П или ПИ алгоритмами, как ПД или ПИД контроллеры. Большая дифференциальная составляющая Д обычно дает неустойчивую систему. Рисунок 2-5 показывает отклики Д и ПД- контроллера. Отклик ПД-контроллера дает быстрый рост значения процесса, чем П контроллер. Обратите внимание, что дифференциальная составляющая Д ведет себя по существу как фильтр верхних частот для сигнала ошибки и, таким образом легко делает систему нестабильной и более чувствительной к шуму.

 

Рисунок 2-5. Отклик Д- и ПД-контроллера на ступенчатое изменение опорного сигнала

 

   ПИД-регулятор дает лучшую производительность, поскольку использует все составляющие вместе. Рисунок 2-6 сравнивает П, ПИ, и ПИД-регуляторы. ПИ улучшает П, удалив стационарную ошибку, и ПИД улучшает ПИ более быстрым откликом.

 

Рисунок 2-6. Отклик П-, ПИ- и ПИД-регулятора на ступенчатое изменение опорного сигнала


 

   Наилучший путь найти необходимые параметры ПИД алгоритма – это использование математической модели системы. Однако часто подробного математического описания системы нет и настройки параметров ПИД-регулятора могут быть выполнены только экспериментально. Поиск параметров для ПИД-регулятора может быть сложной задачей. Здесь большое значение имеют данные о свойствах системы и различных условиях ее работы. Некоторые процессы не должны позволить перерегулирования процесса переменной от заданного значения. Другие процессы должны минимизировать потребление энергии. Также важнейшим требованием является стабильность. Процесс не должен колебаться ни при каких условиях. Кроме того, стабилизация должна наступать в течение определенного времени.

 

   Существуют некоторые методы для настройки ПИД-регулятора. Выбор метода будет зависеть в значительной степени от того, может ли быть процесс автономным для настройки или нет. Метод Циглера-Николса это известный не автономная метод настройки. Первым шагом в этом методе является установка И и Д коэффициентов усиления в нуль, увеличивая усиление П до устойчивого и стабильного колебаний (как можно ближе). Тогда критический коэффициент усиления Кс и период колебаний Pc записывается и П, И и Д значения корректируются с использованием Таблицы 2-1.

 

Таблица 2-1. Расчет параметров по методу Циглера-Николса 

 

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

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

   В отличие от простых алгоритмов управления, ПИД-регулятор способен манипулировать управляющим сигналом на основе истории и скорости изменения измеряемого сигнала. Это дает более точный и стабильный метод управления.

 

   На рисунке 2-2 показано схемное решение ПИД-регулятора, где Тр, Ti, и Td обозначают постоянные времени пропорциональной, интегральной, и дифференциальной составляющих соответственно.

 

Передаточная функция системы, изображенной на рисунке 2-2 имеет вид:

 

 

Это дает U по отношению к e в интервале времени

 

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


 

Где n является дискретным шагом времени t.

Это дает контроллер:

 

Где 

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


  К этому документу прилагается рабочее приложение, реализованное на C. Полную описание исходного кода и информации о компиляции можно найти в файле “readme.html”.

Рисунок 3-1. Блок-схема демонстрационного приложения



  На рисунке 3-1 показана упрощенная схема демо приложения.

   ПИД-регулятор использует структуру для хранения своего статуса и параметров. Эта структура инициализируется функцией main, и только указатель на него передается функциям Init_PID() и PID(). 

 

   Функция PID () должна быть вызвана для каждого интервала времени T, это задается таймером, который устанавливает флаг PID_timer, когда время выборки прошло. Когда PID_timer флаг установлен, основная программа читает эталонное значение процесса и системное значение процесса, вызывается функция PID () и выводится результат на управляющий вход.

   Для повышения точности p_factor, i_factor и  d_factor увеличиваются в 128 раз. Результат ПИД алгоритма позже уменьшается путем деления на 128. Значение 128 используется для обеспечения оптимизации при компиляции.


Кроме того, влияние Ifactor и Dfactor будет зависеть от времени T.

   Когда входной процесс, U, достигает достаточно высокого значения, он становится ограниченным. Либо внутренним числовом диапазоном ПИД-регулятора, либо выходным диапазоном контроллера или подавляется в усилителях. Это произойдет, если есть достаточно большая разница между измеряемым значением и опорным значением, как правило, это происходит потому что процесс имеет большие нарушения , чем система способна обрабатывать.

 

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

 

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

   ПИД-регулятор, представленый здесь, является упрощенным примером. Контроллер должен работать хорошо, но в некоторых приложениях может быть необходимо, чтобы контроллер был еще более надежным. Может быть необходимо добавление насыщения коррекции в интегральной составляющей, на основе пропорциональной составляющей только на значении процесса.

 

   В расчете Ifactor и Dfactor время выборки T это часть уравнения. Если время выборки Т использоваться намного меньше или больше чем на 1 секунду, точность либо Ifactor или Dfactor будет недостаточной. Можно переписать алгоритм ПИД и масштабирования, чтобы точность интегральной и диффиренциальной составляющих сохранилась.

K. J. Astrom & T. Hagglund, 1995: PID Controllers: Theory, Design, and Tuning.  
International Society for Measurement and Con.

AVR221.rar

Перевел Кирилл Владимиров по просьбе ChipEnable.Ru 

chipenable.ru

ПИД-регулятор своими руками / Habr

; PID управление
CalcMainEnd:
	; Вычисления, Go-Go.
CalcPid:
	;	1. Eo = E				 | 16bit
Pid1:
	MOV Err0H, ErrH
	MOV Err0L, ErrL
	;	2. E = Y-X				 | 16bit
Pid2:
	CLR C
	MOV A, SettingL
	SUBB A, ThermoL
	MOV ErrL, A
	MOV A, SettingH
	SUBB A, ThermoH
	MOV ErrH, A
	JNB  OV, Pid2Ov
	JB	ACC.7, Pid2Max
Pid2Min:
	MOV ErrL, #LOW(-500*32)
	MOV ErrH, #HIGH(-500*32)
	SJMP Pid2End
Pid2Max:
	MOV ErrL, #LOW(500*32)
	MOV ErrH, #HIGH(500*32)
	SJMP Pid2End
Pid2Ov:
	JNB ACC.7, Pid2OvP
Pid2OvN: ; Проверим на ограничение вниз
	CLR C
	MOV A, ErrL
	SUBB A, #LOW(-500*32)
	MOV A, ErrH
	SUBB A, #HIGH(-500*32)
	JNC Pid2End ; Если > -500 => всё ок
	SJMP Pid2Min
Pid2OvP:
	CLR C
	MOV A, ErrL
	SUBB A, #LOW(500*32)
	MOV A, ErrH
	SUBB A, #HIGH(500*32)
	JNC Pid2Max ; Если < 500 => всё ок
Pid2End:

	;	3. Int = Int + (E+Eo)/2  | 32bit+16bit
Pid3:
	JNB PowerReady, Pid3End ; Если нет сети -- интегральную часть не копим
	MOV A, ErrL
	ADD A, Err0L
	MOV R0, A ; временно
	MOV A, ErrH
	ADDC A, Err0H
	MOV C, ACC.7 ; Полусумма всегда влезает в 16 бит, поэтому при сдвиге надо сохранить знак
	RRC A	; Поделим без потери знака
	XCH A, R0 ; A= младшая часть, R0 - старшая часть полусуммы
	RRC A	; Доделили
	JNB  IntS, Pid3IntPos
	; Int отрицательный, изменим знак для R0:A, тем самым можно будет просто сложить с Int
	CLR C
	CPL A
	ADD A, #1
	XCH A, R0
	CPL A
	ADDC A, #0
	XCH A, R0
Pid3IntPos:
	; У Int и R0:A сейчас согласованы знаки, поэтому складываем обычным образом
	ADD A, IntLL
	MOV IntLL, A
	MOV A, IntLH
	ADDC A, R0
	MOV IntLH, A
	MOV A, R0
	JB	ACC.7, Pid3Neg ; Прибавляли отрицательную разность?
	; Если разность положительная, просто распространим перенос
	JNC jPid3End  ; Если прибавили слово и переноса небыло -- делать нам ничего не требуется.
	INC IntHL ; Распространяем перенос выше
	MOV A, IntHL
	JNZ Pid3End  ; Если перенос не ушел в 4й байт -- всё нормально
	INC IntHH ; Распространяем перенос на САМЫЙ старший байт
	MOV A, IntHH
	JNZ Pid3End  ; Если перенос не ушел еще выше -- всё нормально
	MOV IntHH, #0FFh ; Если перенс был выше -- ограничиваем интеграл потолком
	MOV IntHL, #0FFh
	MOV IntLH, #0FFh
	MOV IntLL, #0FFh
jPid3End:
	SJMP Pid3End
Pid3Neg: ; Если разность отрицательная, то надо продолжать добавлять оба раза, но FFh
	MOV A, IntHL
	ADDC A, #0FFh
	MOV IntHL, A
	MOV A, IntHH
	ADDC A, #0FFh
	MOV IntHH, A
	JC	Pid3End  ; Если тут был перенос, значит знак интеграла не изменился
	CPL IntS ; Если переноса небыло, значит у интеграла изменился знак
	CPL C		 ; Обратим знак получившегося числа
	MOV A, #0
	SUBB A, IntLL
	MOV IntLL, A
	MOV A, #0
	SUBB A, IntLH
	MOV IntLH, A
	MOV A, #0
	SUBB A, IntHL
	MOV IntHL, A
	MOV A, #0
	SUBB A, IntHH
	MOV IntHH, A
	; так как оно стало отрицательным -- то перенос тут будет всегда
Pid3End:

	;	5. cI = Int*(1/Ti)		 | 32*32=>32bit
Pid5: ; R3:R2:R1:R0 = Int*(1/Ti)
	JB Ti_sh2, Pid5Calc ; если Ti_sh2=0, то 1/Ti=1 или Ti=0. и ничего делать не надо
	MOV A, Ti_mLL
	ORL A, Ti_mLH
	ORL A, Ti_mHL
	ORL A, Ti_mHH
	JZ	Pid5Zero
	MOV R0, IntLL
	MOV R1, IntLH
	MOV R2, IntHL
	MOV R3, IntHH
	AJMP Pid5End
Pid5Zero:
	MOV A, #0
	MOV R0, A
	MOV R1, A
	MOV R2, A
	MOV R3, A
	MOV IntLL, A
	MOV IntLH, A
	MOV IntHL, A
	MOV IntHH, A
	AJMP Pid5End
Pid5Calc:
	; R7:R6:R5:R4[:R3] = MULUH(Int*Ti_m) // R3 считаем как часть для округления
	MOV R2, #0
	;; R7:R6 = IntHH*Ti_mHH
	MOV A, IntHH
	MOV B, Ti_mHH
	MUL AB
	MOV R7, B
	MOV R6, A
	; R6:R5 += IntHL*Ti_mHH
	MOV A, IntHL
	MOV B, Ti_mHH
	MUL AB
	MOV R5, A
	MOV A, R6
	ADD A, B
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	; R5:R4 += IntLH*Ti_mHH
	MOV A, IntLH
	MOV B, Ti_mHH
	MUL AB
	MOV R4, A
	MOV A, R5
	ADD A, B
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	; R4:R3 += IntLL*Ti_mHH
	MOV A, IntLL
	MOV B, Ti_mHH
	MUL AB
	MOV R3, A
	MOV A, R4
	ADD A, B
	MOV R4, A
	MOV A, R2 ; A=0
	ADDC A, R5
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	;; R6:R5 += IntHH*Ti_mHL
	MOV A, IntHH
	MOV B, Ti_mHL
	MUL AB
	ADD A, R5
	MOV R5, A
	MOV A, R6
	ADDC A, B
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	; R5:R4 += IntHL*Ti_mHL
	MOV A, IntHL
	MOV B, Ti_mHL
	MUL AB
	ADD A, R4
	MOV R4, A
	MOV A, R5
	ADDC A, B
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	; R4:R3 += IntLH*Ti_mHL
	MOV A, IntLH
	MOV B, Ti_mHL
	MUL AB
	MOV A, R3
	MOV R3, A
	MOV A, R4
	ADDC A, B
	MOV R4, A
	MOV A, R2 ; A=0
	ADDC A, R5
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	;; R5:R4 += IntHH*Ti_mLH
	MOV A, IntHH
	MOV B, Ti_mLH
	MUL AB
	ADD A, R4
	MOV R4, A
	MOV A, R5
	ADDC A, B
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	; R4:R3 += IntHL*Ti_mLH
	MOV A, IntHL
	MOV B, Ti_mLH
	MUL AB
	ADD A, R3
	MOV R3, A
	MOV A, R4
	ADDC A, B
	MOV R4, A
	MOV A, R2 ; A=0
	ADDC A, R5
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	;; R4:R3 += IntHH*Ti_mLL
	MOV A, IntHH
	MOV B, Ti_mLL
	MUL AB
	ADD A, R3
	MOV R3, A
	MOV A, R4
	ADDC A, B
	MOV R4, A
	MOV A, R2 ; A=0
	ADDC A, R5
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	;;; Если R3 > 7F --
	MOV A, R3
	JNB ACC.7, Pid5Shift ; Если R3<80 -- округление не надо
	ANL A, #7Fh
	JZ	Pid5Round ; Если = 80 -- округляем до нечетного
	MOV A, #1
	ADD A, R4
	MOV R4, A
	MOV A, R2 ; A=0
	ADDC A, R5
	MOV R5, A
	MOV A, R2 ; A=0
	ADDC A, R6
	MOV R6, A
	MOV A, R2 ; A=0
	ADDC A, R7
	MOV R7, A
	SJMP Pid5Shift
Pid5Round:
	MOV A, R4
	ORL A, #01h
	MOV R4, A
	;JMP Pid5Shift

Pid5Shift:
	; R3:R2:R1:R0 = (Int-R7:R6:R5:R4) >> 1
	CLR C
	MOV A, IntLL
	SUBB A, R4
	MOV R0, A
	MOV A, IntLH
	SUBB A, R5
	MOV R1, A
	MOV A, IntHL
	SUBB A, R6
	MOV R2, A
	MOV A, IntHH
	SUBB A, R7
	RRC A ; >>1 без потери переноса
	MOV R3, A
	MOV A, R2
	RRC A
	MOV R2, A
	MOV A, R1
	RRC A
	MOV R1, A
	MOV A, R0
	RRC A
	;MOV R0, A
	; R3:R2:R1:R0 += R7:R6:R5:R4
	;MOV A, R0
	ADD A, R4
	MOV R0, A
	MOV A, R1
	ADDC A, R5
	MOV R1, A
	MOV A, R2
	ADDC A, R6
	MOV R2, A
	MOV A, R3
	ADDC A, R7
	MOV R7, A
	; Теперь сдвинуть вправо на sh3.
	; sh3 может быть до 16 (так как у нас Ti 16разрядный; проверим необходимость сдвига на 16 бит)
	MOV A, Ti_sh3
	JNB ACC.4, Pid5ShiftUnder16
	; Надо сдвинуть >=16 -- 2 байта сдвинем mov'ами
	MOV R0, 18h+2; R2, bank 3
	MOV R1, 18h+3; R3, bank 3
	MOV R2, #0
	MOV R3, #0
Pid5ShiftUnder16:
	JNB ACC.3, Pid5ShiftUnder8
	; Надо сдвинуть на >=8 -- 1 байт сдвигаем mov'ами
	MOV R0, 18h+1; R1, bank 3
	MOV R1, 18h+2; R2, bank 3
	MOV R2, 18h+3; R3, bank 3
	MOV R3, #0
Pid5ShiftUnder8:
	ANL A, #07h
	JZ Pid5End ; Если внутри байта двигать не надо -- всё
	MOV R4, A
	SJMP Pid5ShiftRight
Pid5NextShift:
	CLR C
	; К этому моменту C у нас еще возможнозначимый старший бит!
Pid5ShiftRight:
	MOV A, R3
	RRC A
	MOV R3, A
	MOV A, R2
	RRC A
	MOV R2, A
	MOV A, R1
	RRC A
	MOV R1, A
	MOV A, R0
	RRC A
	MOV R0, A
	DJNZ R4, Pid5NextShift
	; Всё, после всех сдвигов получили результат
	; Не забываем, что у вычисленного в R3:R2:R1:R0
	; сейчас число положительное, а знак его в IntS
Pid5End:

	;	4. PID += [ cD = Td * (E-Eo) ]		| 16*16=>32bit
Pid4: ; cD = R7:R6:R5:R4; ErrD = E-Eo
	CLR C
	MOV A, ErrL
	SUBB A, Err0L
	MOV DiffL, A
	MOV A, ErrH
	SUBB A, Err0H
	MOV DiffH, A
	MOV C, ACC.7 ; Берём знак результата
	MOV DiffS, C ; Сохраним знак E-Eo
	JNC Pid4Mul
	; Diff -- орицательный, обратим знак
	MOV A, DiffL
	CPL A
	ADD A, #1
	MOV DiffL, A
	MOV A, DiffH
	CPL A
	ADDC A, #0
	MOV DiffH, A
Pid4Mul:
	; R7:R6 = DiffH*TdH
	; MOV A, DiffH = в любом случае A=DiffH
	MOV B, TdH
	MUL AB
	MOV R6, A
	MOV R7, B
	; R5:R4 = DiffL*TdL
	MOV A, DiffL
	MOV B, TdL
	MUL AB
	MOV R4, A
	MOV R5, B
	; R6:R5 += DiffH*TdL
	MOV A, DiffH
	MOV B, TdL
	MUL AB
	ADD A, R5
	MOV R5, A
	MOV A, R6
	ADD A, B
	MOV R6, A
	MOV A, R7
	ADDC A, #0
	MOV R7, A
	; R6:R5 += DiffL*TdH
	MOV A, DiffL
	MOV B, TdH
	MUL AB
	ADD A, R5
	MOV R5, A
	MOV A, R6
	ADD A, B
	MOV R6, A
	MOV A, R7
	ADDC A, #0
	MOV R7, A

	;	6. PID = E + cI + cD	 | 32bit
Pid6:	; R3:R2:R1:R0 равно cI, знак в IntS;
	; R7:R6:R5:R4 = cD; знак в DiffS
	; E в обратном дополнительном коде

	JB IntS, ChkDiffN
	JNB DiffS, Pid6Add ; Int>0, Diff>0 => Add
	SJMP Pid6Sub ; Int>0, Diff<0 => Sub
ChkDiffN:
	JNB DiffS, Pid6Sub ; Int<0, Diff>0 => Sub
	; Int<0, Diff<0 => Add
Pid6Add:
	; Одинаковый знак => складываем их с проверкой на переполнение
	MOV A, R0
	ADD A, R4
	MOV R0, A
	MOV A, R1
	ADDC A, R5
	MOV R1, A
	MOV A, R2
	ADDC A, R6
	MOV R2, A
	MOV A, R3
	ADDC A, R7
	MOV R3, A
	JNC Pid6Err ; Если нет переноса - в результате сложения переполнения небыло
	MOV R3, #0FFh
	MOV R2, #0FFh
	MOV R1, #0FFh
	MOV R0, #0FFh
	SJMP Pid6Err
Pid6Sub:
	; Знаки разные -- вычтем одно из другого и проверим знак результата
	CLR C
	MOV A, R4
	SUBB A, R0
	MOV R0, A
	MOV A, R5
	SUBB A, R1
	MOV R1, A
	MOV A, R6
	SUBB A, R2
	MOV R2, A
	MOV A, R7
	SUBB A, R3
	MOV R3, A
	JNC Pid6Err ; Если нет заимствования -- знак результата равен знаку DiffS
	CPL DiffS ; Если заимствование было, у DiffS и результата надо обратить знак
	MOV R6, #0 ; R6=0
	MOV A, R0
	CPL A
	ADDC A, R6 ; R6=0, C=1 => действие +1
	MOV R0, A
	MOV A, R1
	CPL A
	ADDC A, R6 ; +перенос
	MOV R1, A
	MOV A, R2
	CPL A
	ADDC A, R6
	MOV R2, A
	MOV A, R3
	CPL A
	ADDC A, R6
	MOV R3, A

Pid6Err:
	MOV R6, #0 ; R6=0
	; В R3:R2:R1:R0 -- лежит cI+cD; знак суммы в DiffS
	; надо прибавить/отнять Err, записанное в обратном коде
	; Приведём знак Err к DiffS
	MOV R4, ErrL
	MOV A, ErrH
	JB ACC.7, Pid6ChkDiffS
	JNB DiffS, Pid6SumErrNoInv ; Err>0, Diff>0 => NoInv
	SJMP Pid6SumErrInv
Pid6ChkDiffS:
	JNB DiffS, Pid6SumErrNoInv ; Err<0, Diff>0 => NoInv
Pid6SumErrInv:
	; У Err знак отличается от DiffS -- инвертируем
	SETB C ; Не уверен в состоянии C
	MOV A, ErrL
	CPL A
	ADDC A, R6 ; A+=R6+C, R6=0	C=1 => A+=1
	MOV R4, A ; R4=ErrL
	MOV A, ErrH
	CPL A
	ADDC A, R6
Pid6SumErrNoInv:
	MOV R5, A ; ErrH
Pid6SumErr:
	; Итак, в R5:R4 лежит Err, знак которого согласован с DiffS; но в обратно-дополнительном коде
	MOV A, R0
	ADD A, R4
	MOV R0, A
	MOV A, R5
	CLR F0
	JNB ACC.7, Pid6SubErrPos
	SETB F0
	MOV R6, #0FFh ; Добавляем отрицательное => дополняем FFами
Pid6SubErrPos:
	ADDC A, R1
	MOV R1, A
	MOV A, R2
	ADDC A, R6 ; +расширение
	MOV R2, A
	MOV A, R3
	ADDC A, R6 ; +расширение
	MOV R3, A
	MOV R6, #0
	; Надо проверить нет ли смены знака итоговой суммы
	JNC Pid6ChkF0
	JB F0, Pid7 ; Err<0, был перенос => Знак не сменился, переполнения нет
	SJMP Pid6SumOv ; Err>0, был перенос => переполнение
Pid6ChkF0:
	JNB F0, Pid7 ; Err>0, небыло переноса => нет переполнения
	;SJMP Pid6SumUf ; Err<0, небыло переноса => сменился знак
Pid6SumUf:
	; Если Err<0 и небыло переноса => сменился знак
	CPL DiffS
	MOV A, R0
	CPL A
	ADD A, #1 ; C=?, поэтому прибавляем 1 обычным методом
	MOV R0, A
	MOV A, R1
	CPL A
	ADDC A, R6
	MOV R1, A
	MOV A, R2
	CPL A
	ADDC A, R6
	MOV R2, A
	MOV A, R3
	CPL A
	ADDC A, R6
	MOV R3, A
	SJMP Pid7 ; Знак у результата и DiffS приведены в норму
Pid6SumOv:
	; Было переполнение => округляем до максимума
	MOV R0, #0FFh
	MOV R1, #0FFh
	MOV R2, #0FFh
	MOV R3, #0FFh

	;	7. U = K*PID/256		 | 32bit*16bit/8bit => 40bit,
	;					 | которые усекаются до 10bit
	;					 | при вычислениях
Pid7: ; В R3:R2:R1:R0 лежит результат PID, в DiffS его знак
	  ; Нужно вычислить K*PID/256, ограничив результат до 10бит
	  ; K всегда положительно, поэтому если PID < 0 => минимум
	JB DiffS, Pid7Umin
	; поскольку мы можем жестко ограничить сверху 16ю битами,
	; то если R3 != 0 => ставим максимум в любом случае
	MOV A, R3
	JNZ Pid7Umax
	; [R2:R1:R0 * KH:HL] = [R7:R6:R5:R4:R3]
	; вычисляем, учитывая что должно получиться R7=0 R6=0,
	; иначе переполнение, поэтому R7 и R6 вообще не трогаем
	; но проверяем результат
	; R7:R6 = R2*KH
	MOV A, R2
	JZ Pid7S1
	MOV A, KH
	JNZ Pid7Umax ; Если R2!=0 и KH!=0 => R7:R6>0 => переполнение
Pid7S1:
	; R6:R5 = R2*KL
	MOV A, R2
	MOV B, KL
	MUL AB
	MOV R5, A
	MOV A, B
	JNZ Pid7Umax ; Если R6 > 0 => переполнение
	; R6:R5 = R1*KH
	MOV A, R1
	MOV B, KH
	MUL AB
	ADD A, R5
	JC	Pid7Umax ; Если R6 > 0 => переполнение
	MOV R5, A
	MOV A, B
	JNZ Pid7Umax ; Если R6 > 0 => переполнение
	; R5:R4 = R0*KH
	MOV A, R0
	MOV B, KH
	MUL AB
	MOV R4, A
	MOV A, R5
	ADD A, B
	JC	Pid7Umax ; Если R6 > 0 => переполнение
	MOV R5, A
	; R5:R4 = R1*KL
	MOV A, R1
	MOV B, KL
	MUL AB
	ADD A, R4
	MOV R4, A
	MOV A, R5
	ADDC A, B
	JC	Pid7Umax ; Если R6 > 0 => переполнение
	MOV R5, A
	; R4:R3 = R0*KL
	MOV A, R0
	MOV B, KL
	MUL AB
	RLC A ; C = R3>=0x80, Z=R3>0x80
	MOV R3, #0FFh ; R3<>0x80 => ничего
	JNZ Pid7S2
	MOV R3, #0FEh ; R3==0x80 => округление до четного
Pid7S2:
	MOV A, R4
	ADDC A, B ; Складываем умножение, регистр, и перенос-округление
	ANL A, R3 ; А так же если округление до четного -- отбрасываем после младший бит
	MOV R4, A
	MOV A, R5
	ADDC A, R6 ; R6=0 у нас с давних пор, хоть мы туда и не складывали ничего во время перемножения
	JC	Pid7Umax ; Если R6 > 0 => переполнение
	MOV R5, A
	; R5:R4 => ограниченный в 16 бит результат
	; Теперь надо ограничить R5:R4 до Umax/Umin
	MOV A, UmaxL
	SUBB A, R4 ; C=0 на текущий момент
	MOV A, UmaxH
	SUBB A, R5
	JC Pid7Umax ; Если R5:R4>Umax => R5:R4 = Umax
	MOV A, UminL
	SUBB A, R4 ; C=0 на текущий момент
	MOV A, UminH
	SUBB A, R5
	JNC Pid7Umin ; Если R5:R4<Umin => R5:R4 = Umin
	; Мощность вычислена
	MOV UH, R5
	MOV UL, R4
	SETB UReady
	AJMP CalcExit
Pid7Umax: ; Установить максимальную мощность
	MOV UH, UmaxH
	MOV UL, UmaxL
	SETB UReady
	AJMP CalcExit
Pid7Umin: ; Установить минимальную мощность
	MOV UH, UminH
	MOV UL, UminL
	SETB UReady
	AJMP CalcExit

habr.com

alexxlab

leave a Comment