Фронты по алгоритму ловятся все ,а не один ,но кроме фронта нужного я еще всегда анализирую потенциал на противоположном входе ,что бы уменьшить влияние помех .
Вид для печати
Фронты по алгоритму ловятся все ,а не один ,но кроме фронта нужного я еще всегда анализирую потенциал на противоположном входе ,что бы уменьшить влияние помех .
Рад за вас и ваш алгоритм. Но в моей PRU программе используется не ваш алгоритм.
Как и обещал, проверил на случайном вращении "виртуального энкодера".
Забегая вперёд скажу, что проблем не нашлось. Т.е. программа работает верно.
Иными словами, вращение из любого состояния в любом направлении будет отрабатывать правильно.
10'000'000 импульсов эмулируются за 2-4 секунды.
Собственно, класс "эталонного энкодера". Это тот, который выдаёт эталонные A-B импульсы.
Теперь натравливаем рандомное вращение на исходную PRU программу:Код:package com.github.vlsi.pru;
import static com.github.vlsi.pru.VirtualAbEncoder.EncoderState.S01;
import static com.github.vlsi.pru.VirtualAbEncoder.EncoderState.S10;
import static com.github.vlsi.pru.VirtualAbEncoder.EncoderState.S11;
public class VirtualAbEncoder {
private final static EncoderState[] ENCODER_STATES = EncoderState.values();
enum EncoderState {
S00,
S01,
S11,
S10,
}
private EncoderState state = EncoderState.S00;
private int count;
private int position;
public boolean getA() {
return state == S10 || state == S11;
}
public boolean getB() {
return state == S01 || state == S11;
}
public void reset() {
state = EncoderState.S00;
count = 0;
position = 0;
}
public void left() {
step(true);
}
public void right() {
step(false);
}
public void step(boolean left) {
count++;
int diff = left ? 1 : -1;
position += diff;
state = getStateByOrdinal(state.ordinal() + diff);
}
private EncoderState getStateByOrdinal(int i) {
int length = ENCODER_STATES.length;
return ENCODER_STATES[(i + length) % length];
}
public int getPosition() {
return position;
}
public int getCount() {
return count;
}
}
Ничего особенного не происходит, всё отрабатывает верно.Код:@Test
public void drunkEncoder() {
PRU_ABZ_ENCODER_CodeGenerator abz = new PRU_ABZ_ENCODER_CodeGenerator();
Pru cpu = new Pru();
CodeEmitter ce = new CodeEmitter();
abz.accept(ce);
cpu.setCode(ce.visitEnd());
VirtualAbEncoder encoder = new VirtualAbEncoder();
ThreadLocalRandom random = ThreadLocalRandom.current();
for(int i = 0; i < 10_000_000; i++) {
encoder.step(random.nextBoolean());
abz.setA(cpu, encoder.getA() ? 1 : 0);
abz.setB(cpu, encoder.getB() ? 1 : 0);
executeBlock(cpu);
assertEquals(abz.getCounter(cpu), encoder.getCount() & 0xffff, "counter");
assertEquals(abz.getPosition(cpu), encoder.getPosition() & 0xffff, "position");
}
}
Вы попробуйте менять направление вращения случайным и независимым генератором (ГСЧ) , чтобы он попадал в разные моменты и не был синхронизован в выполнением основной программы .Кроме того ,хороший алгоритм должен исходить из того что могут быть высокочастотные помехи ,которые сравнимы по длительности с фронтами и которые сглаживать фильтром нельзя ,что бы не потерять основные импульсы с энкодера при больших скоростях .
В общем, если у кого-нибудь есть энкодер -- прошу протестировать программу энкодера.
Теоретически, программа должна успешно ловить импульсы вплоть до 1 мкс (~1 МГц)
Казалось бы, зачем нужна ещё одна программа обработки энкодера, если в прошивке ПЛК и так энкодер уже обрабатывается?
Отвечаю: когда PRUграммирование станет доступно для всех, то модуль энкодера уже будет в базовой поставке.
Попробовал "облагородить" обмен данными между PRU и основной программой -- столкнулся с тем, что мне не хватает задач.
Ещё есть кандидаты на PRU программы?
Надо кому-нибудь что-нибудь на тему "быстрого управления"?
Поясню, что мне не нравится в моём текущем подходе.
Вот фрагмент программы, которая следит за энкодером и выключает двигатель как только энкодер отсчитает нужное количество импульсов:
Вложение 26943
Несложно заметить, что обмен данными сделан ассемблерными командами.
На КДС стороне эти данные принимаются по соответствующим адресамКод:ASM
SBCO (* записываем переменную в память *) cutter.state, 3 (* тут всегда 3 *), 60 (* это адрес куда писать *) , 1 (* 1 байт *)
SBCO cutter.offset, 3, 64, 4
SBCO abz.zeroDetected, 3, 68, 1
SBCO abz.position, 3, 72, 2
SBCO abz.counter, 3, 76, 2
END_ASM
Нормально?Код:VAR
TMP: DWORD;
END_VAR
PRU_FB_GetParameter(pru_num:=0, index:=15 (* == 60/4 == cutter.state *), value:=ADR(TMP));
PRU_FB_GetParameter(pru_num:=0, index:=16 (* == 64/4 == cutter.offset *), value:=ADR(OFFSET));
Готовы так PRUграммировать?
Мне не нравится, что приходится следить за адресами и соответствием PRU кода и КДС кода.
Хочется чего-то автоматического, чтобы адреса выбирались сами собой, и чтобы КДС обёртка генерировалась одновременно с PRUграммой.
Мне-то подобная работа с памятью ещё более-менее, но, думаю, для обывателя это сложновато.
Моя проблема в том, что пары имеющихся у меня программ маловато, чтобы "обобщить опыт"
Думаю пока в таком направлении: в "основной" PRUграмме отмечаем спец флагом те блоки/переменные, которые нужно передавать между КДС и PRU.
Например, так:
Вложение 26945
Ключевое слово @Export указывает имя ФБ на КДС стороне и перечень переменных, которые нужно передавать из/в КДС.
В примере выше, Hardella создаст не только PRUграмму, но и КДС блоки PRU_ABZ_ENC/PRU_FAST_INPUTS, входы-выходы которых будут обмениваться с PRU.
Как придумается адекватный подход, можно будет запускать PRUграммирование в массы.
Да, я жду плк110 М02, задача стоит в управлении ШД с ускорением и замедлением и обратная связь от энкодера.
Я так понимаю PRU мне как раз должна помочь в этом?
В прошлом году задача решена была на плк160 + драйвер onitex, управление по rs485 (modbus), теперь хочу исключить onitex.
На плк siemens (очень старом и очень давно) эти задачи решались 'мышкой' и работают по сей день, овен не очень хочет видимо делать просто и доступно.
А как решен вопрос с утилизацией быстрых дискретных входов в программе PRU для управления ШД, которые в самом ФБ не используются ?
Мне видится такие возможности их использования:
1) подключение двух энкодеров (фазы А, В без нулевой метки);
2) подключение одного энкодера (фазы А, В и нулевая метка) + один свободный вход для других нужд;
3) 4 быстрых входа для других нужд.
Во всех случаях надо предусмотреть фильтрацию входов.
Вложение 26953
Вложение 26951
Вложение 26952
Фильтрация, это, наверное, либо параметр ФБ PRU_INPUTS, либо ещё один блок.
У нас фасовочный аппарат, объёмное дозирование. Пневмоцилиндр тягает поршень (в котором продукт) и упирается в ограничитель, который и двигает ШД.
Позиция энкодера 0 ~ 3 кг продукта
Позиция энкодера 850 ~ 1.5 кг продукта и т.д.
Мне скорость не так важна как точность, так как цена одного оборота = 3 грамма продукта, ошибся на несколько оборотов = брак
Ускорение и замедление делал как раз для точности.
Машина выдаёт продукт, затем тара попадает на весы и если вес нужно подкорректировать, то ПЛК крутит в нужную сторону ШД на рассчитанное кол-во импульсов, таким образом получаем аппарат розлива с автоматической корректировкой веса.
1.Если компилятор не заточен под константное выполнение кода по времени - джиттер всегда будет. И подсчитать время исполнения при всех вариантах входных и промежуточных значениях параметров и переменных (не говоря о проверке корректности исполнения) - задача для компьютера с альфа-центавры. даже 100 бинарных переменных дают 2^100 вариантов. "Подождите, HArdella проводит тестирование кода, осталось 9999 лет 4 месяца 3 дня 5 мкс. Приятного Вам отдыха."
2. Обмен данными не используя механизм pruAccessLib, тем более более 1 регистра за раз - это гарантированные проблемы с реактивностью системы и когерентностью данных в обновляемых структурах.
Крайне трудно отлавливаемые. Это ЖЖ неспроста.
Видимый мною код Владимира об правила синхронизации просто вытирает ноги.
Есть рабочая программа ШД.
Там джиттер есть?
Я, конечно, понимаю ваш скептицизм и лень смотреть код/проводить испытания, если "и так видно, что код написан не по правилам". Но, уверяю вас, я не на пустом месте уверен в правильности кода (как минимум в части джиттера), поэтому, пожалуйста, лучше прямо говорите где именно джиттер вы там нашли.
Если джиттер реально найдётся, то поправлю эту ошибку. Но беда в том, что разговоров о джиттерах было много, а на практике их пока в моих программах никто не нашел.
Скоро будет и общедоступный механизм PRUграммирования. Вполне возможно, что это случится уже в этом году.
Джиттер можно легко рассмотреть на хорошем осциллографе. В.Филоненко, дайте вашим тестировщикам задание капитально проверить ФБ для ШД, разработанный В.Ситниковым, и всем спорам конец. Во многих практических случаях, например при управлении ШД, джиттер не страшен, что и подтвердилось при натурных испытаниях.
В.Филоненко, а когда появится ваш правильный ФБ для управления ШД ?
а что тут исследовать? Ситников прямо написал что время цикла выполнения зависит от текущей частоты выдачи шага, плюс язык высокого уровня принципиально не будет выравнивать время выполнения разных ветвей алгоритма... исследовать имеет смысл наличие случайной составляющей, а при заявленной принципиальной несовместимости - о чем речь? Поэтому использовать можно все что угодно, но не забывая что там внутри и чем это грозит в случае вроде бы применения в аналогичном случае.
У PRU ядра всего один поток команд.
Т.е. В каждый момент времени PRU берет следующую команду и выполняет. Никаких прерываний нет.
До этого я составлял программы, в которых было все-все-все. И чтение входов, и обмен данными и
ожидания, и запись выходов.
Мне, конечно так проще, да и всё равно никто другой черепахой не программировал.
Но есть "незадача". Например, для управления ШД нужно мигать выходом с разной частотой. Например, на разгоне вообще частота с каждым импульсом меняется.
В результате, пока "ждём момент для очередного ШД импульса, опрашивать энкодер все равно нужно".
Напомню: прерываний нет, и единственный вариант "переключить выход ровно через 1мкс" это "выполнить 200 каких-нибудь команд" (на частоте 200МГц как раз так получается, что 200 простых команд занимают 1 мкс".
Опрос входа -- 1 команда. Проверка "менялось ли значение за последнюю микросекунду" -- это ещё штук 5-6 команд.
В итоге, 4 входа с примитивной фильтрацией займут ~ 4*7 == 30 тактов == 30/200 мкс == 3/20 = 0.15мкс.
Ещё может случиться "обмен с HOST'ом" (он, ведь, каждую миллисекунду случаться будет?) Это ещё команды непредвиденные.
В итоге, чтобы всё успеть, нужно делать фарш из опросов входов-выходов, фильтрации, и т.п.
Я вижу как минимум следующие варианты:
1) Оставить как есть и "в первой версии PRU среды" сделать режим, когда программист пишет полную программу (включая while true). Так сказать, отложить решение проблемы. При желании всё равно можно будет и ШД и энкодер в одной программе обслуживать, но придётся в правильные моменты добавлять опрос энкодера.
2) Вынести ожидание из ШД программы, и сделать так, чтобы основной цикл всегда вызывал все действия. Тогда в ШД блоке нужно будет добавить проверку "пора ли уже". Казалось бы, вот решение, но при этом точность упадёт. выход будет не "когда нужно" переключаться, а тогда, когда дойдёт управление до ШД блока. Это может оказаться 50-100 команд, т.е. 0.25...0.5 мкс.
3) При создании PRU программы указывать: "вот этот код нужно вызывать раз в 1мкс, вот этот раз в 20мкс, а у этого расписание плавает и он сам будет говорить когда надо". Вроде, подобное в КДС называется task configuration.
4) Ещё можно подумать над тем, что PRU программы будут не сразу входы изменять, а будут говорить "через какое время вход должен измениться".
В случаях 3 и 4 код "основного цикла" будет формироваться из фрагментов, предоставленных программистом и разбавляться проверками времени.
Возможно, есть ещё варианты.
Проблемы варианта 1 в том, что "ппограммировать сложнее". Грубо говоря, у многих проблемы с пониманием цикла плк, а тут основной цикл придётся вручную целиком писать. Не домохозяйки, конечно собрались, но все же.
В варианте 2 будет рваный ритм. То туда, то сюда в зависимости от того, сколько каждый блок занял. ШД на разгоне и ШД во время основного хода занимает разное время.
Как разновидность 2го, можно всегда дополнять цикл, скажем, до 1 мкс. Выполнили все блоки, обменялись данными с хостом, и, если 1мкс ещё не прошла, то крутим пустой цикл (но с фильтрацией входов). Тут минус в том, что реакция будет привязана к этой самой "длительности цикла"
Казалось бы, вот решение. Просто указываем "длину мин цикла и радуемся". Но, нет.
На частоте 200кГц (нормальная такая частота для работы ШД) цикл нужен порядка 2.5мкс (2.5мкс единица, потом ещё столько же ноль).
Даже если умудриться сделать "минц=0.5мкс" (там сложность в том, что ШД на разгоне потребляет как раз примерно 0.5мкс), то всё равно получаемые частоты будут кратны 0.5мкс. Т.е. либо 2.5мкс (5 циклов), либо 3.0мкс (6 циклов), либо 3.5мкс, .... В частотах это:
1/(2*2.5e-6) == 200кГц, 1/(2*3.0e-6) == 167кГц, 1/(2*3.5e-6) = 143кГц, 1/(2*4.0e-6) == 125кГц.
Нехилый такой шаг. Особенно перескок 167кГц -> 200кГц. Вот если бы была возможность менять минц по ходу, то можно было гораздо точнее сделать переход.
Текущая моя ШД программа обеспечивает длительность импульса с точностью ~5-10нс (наносекунд!). И переходить от такой точности к точности "0.5мкс" как-то не по себе.
Хотя, для небольших частот, возможно, вариант с "мин ц порядка нескольких микросекунд" будет достаточным и весьма понятным для типичных инженеров (ну, тех, кто понимает что такое минц=1мс в ПЛК).
В ФБ для ШД надо ограничиться максимальной частотой в 200 кГц. Минимальный полупериод следования импульсов будет 2,5 мкс. Этого времени должно хватить для опроса быстрых входов, к которым подключены энкодеры.
Начну с варианта, когда длина цикла фиксирована.
В конце концов, это не помешает сделать переменную длину цикла, если в пользовательском коде добавить while true...
Ещё вопрос: безопасные состояния, watchdog, вот это всё, нужно?
Если, вдруг, PRU заклинило, то в основной программе должен флаг аварии взводиться? Или перезапуск PRUграммы?
Я склоняюсь к флагу "авария", а перезапуск PRU уже не автоматический, а по запросу основной программы.
Например, ионизирующее излучение, удар молнии, проблемы с питанием, ну и "кривой код" в конце концов.
Выявлять можно во время очередного обмена данными. Смысл в том, что для обмена информацией PRU и host в любом случае должны как-то координироваться. Если host будет ждать ответа от PRU бесконечно долго, то к нему самому придёт собака. Поэтому, логично при операциях обмена использовать таймаут. Скажем, 100ms. Если достигнут таймаут, то считаем, что обмен не состоялся, а pru, возможно, заклинило.
Аналогично, на стороне PRU можно следить за частотой обменов и переходить в безопасное состояние, если host давно не общался с PRU.
Вот описание энкодера буржуйского контроллера Вложение 27043
Создал пробный проект. Включил в него ФБ PRU_STEPPER, сделанный В.Ситниковым. После компиляции проекта имею такой результат. Не пойму в чем дело.
В проект нужно было добавить pruaccesslib.lib
Эту библиотеку можно взять в каком-нибудь "писании от Владислава".
Все получилось. Проект компилируется без ошибок.
Есть прогресс в "доступном всем PRUграммировании".
Программа: Вложение 27071
Эта самая программа будет автоматически вызываться в PRU цикле. Упомянутые в @Export переменные будут передаваться из/в КДС.
Например, 3 быстрых входа будут использоваться для ABZ энкодера, а 4-ый будет подаваться в КДС напрямую (для прочих целей).
"PRU configuration": Вложение 27072
Тут говорится, что "в PRU0 будет выполняться нужная нам программа с циклом в 1мкс", "в PRU1 будет программа по умолчанию", и настраивается фильтрация в 2мс для IN4 (этот вход в энкодере не участвует)
При компиляции это добро завернётся в WHILE TRUE цикл и разбавится обменом с КДС.
Примерно так:
Вложение 27073
На выходе будет готовая к употреблению КДС.exp библиотека. Т.е. импортируем её и магическим образом заработает PRU.
Я у какого-то производителя ШД (или ПЛК?) видел документацию в духе "задача-решение". "Надо двигать и считать пакеты молока? --- делай так". Что-то в таком духе.
Там совсем небольшие примеры, и на них показывались подходы.
Где видел -- не помню, но по-моему, выглядело хорошо.
Нечто похожее в документации на ОЛ, но там примеров, вроде, всего пара штук.
Собственно, вопрос в примерах. И, по-моему, лучше если примеры жизненные, а не просто "как подключить 2 энкодера".
Переведу с русского на русский: кто может -- накидывайте примеров.
Сами примеры будут доступны в самой среде. Можно будет зайти и посмотреть / скопировать и т.п.
Достаточно одного хорошего примера, в котором будет показано как пользоваться доступным всем инструментарием для PRU программирования.
Так, после некоторых танцев с бубном, PRU программа начала компилироваться.
В двух словах:
На эмуляторе работает, значит можно переходить к
1) Созданию кнопки, которая создаст КДС.exp
2) Созданию кнопки "новый PRU проект"
3) Сделать PRU1. Надо сначала протестировать подход с PRU0 в железе, а потом уже делать PRU1.
PRU configuration.
Ничего особенного, просто выполняем программу с циклом в 1мкс, и фильтруем 4-ый вход с временем фильтрации 5
Вложение 27227
Программа для PRU0.
Напомню суть задачи: при подаче "enable=true", должен запуститься мотор и выключиться тогда, когда энкодер отсчитает заданное количество импульсов.
Вложение 27221
Собственно, так и написано. Проверяем энкодер, вызываем блок "управления мотором", выход блока подаём на fast_OUT1.
Эти три действия выполняются раз в микросекунду (согласно PRU configuration выше). В оставшееся время происходит обмен данными и обработка входов.
Если думаете, что в блоке FAST_OUTPUTS будет ассемблер, то там его не найдёте:
Вложение 27222
Блок FAST_INPUTS выглядит так же (тоже пустой). Для работы с входами-выходами никакого ассемблера использовать не нужно. Есть несколько "волшебных" ФБ -- их и используем.
Код внутри блоков input/output и т.п. появляется в момент компиляции. Например, для входов там добавляется вызов фильтрации (если она указана в PRU configuration).
Как и планировалось, для передачи данных из-в КДС ассемблер тоже не нужен. Достаточно подставить @Export над теми переменными, которые понадобятся в КДС.
При компиляции получается программа для PRU и КДС библиотека.
В этом примере создался следующий блок обмена данными:
Вложение 27223
Для порядка, протестируем программу. Сделаем вид, что цикл ПЛК составляет 1мс, и попробуем пару запусков нашей машины.
Что тут видно:
1) При запуске махины (enable 0 -> 1), она запускает мотор, энкодер начинает крутиться, и всё это добро останавливается при достижении нужного количества импульсов. Я указал runLength = 326, оно так и останавливается
2) Первые 12 мс значение на 4-ом входе дёргается (это прямо в тестовой программе сделана подача морганий на fast in), и фильтрация это верно отрабатывает. Вспомним, что в PRU configuration указана фильтрация 5мс. Как раз после этих 5мс значение in4_filtered переходит в единицу.
Код:1 ms, in.enable=0, in.runLength=326, out.counter=0, out.position=0, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=0, out.in4_filtered=0, fastOut4=0, encoder=0
2 ms, in.enable=0, in.runLength=326, out.counter=0, out.position=0, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=0
3 ms, in.enable=0, in.runLength=326, out.counter=0, out.position=0, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=0, out.in4_filtered=0, fastOut4=0, encoder=0
4 ms, in.enable=0, in.runLength=326, out.counter=0, out.position=0, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=0
5 ms, in.enable=0, in.runLength=326, out.counter=0, out.position=0, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=0, out.in4_filtered=0, fastOut4=0, encoder=0
6 ms, in.enable=1, in.runLength=326, out.counter=100, out.position=100, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=101
7 ms, in.enable=1, in.runLength=326, out.counter=200, out.position=200, out.zeroDetected=0, out.offset=99, out.state=MOVING, in4_raw=0, out.in4_filtered=0, fastOut4=1, encoder=201
8 ms, in.enable=1, in.runLength=326, out.counter=300, out.position=300, out.zeroDetected=0, out.offset=199, out.state=MOVING, in4_raw=1, out.in4_filtered=0, fastOut4=1, encoder=301
9 ms, in.enable=1, in.runLength=326, out.counter=400, out.position=400, out.zeroDetected=0, out.offset=299, out.state=MOVING, in4_raw=0, out.in4_filtered=0, fastOut4=1, encoder=401
10 ms, in.enable=1, in.runLength=326, out.counter=500, out.position=500, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=501
11 ms, in.enable=1, in.runLength=326, out.counter=600, out.position=600, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=0, out.in4_filtered=0, fastOut4=0, encoder=601
12 ms, in.enable=1, in.runLength=326, out.counter=700, out.position=700, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=701
13 ms, in.enable=1, in.runLength=326, out.counter=800, out.position=800, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=801
14 ms, in.enable=1, in.runLength=326, out.counter=900, out.position=900, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=901
15 ms, in.enable=1, in.runLength=326, out.counter=1000, out.position=1000, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=1001
16 ms, in.enable=1, in.runLength=326, out.counter=1100, out.position=1100, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=0, fastOut4=0, encoder=1101
17 ms, in.enable=1, in.runLength=326, out.counter=1200, out.position=1200, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1201
18 ms, in.enable=1, in.runLength=326, out.counter=1300, out.position=1300, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1301
19 ms, in.enable=0, in.runLength=326, out.counter=1301, out.position=1301, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1301
20 ms, in.enable=0, in.runLength=326, out.counter=1301, out.position=1301, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1301
21 ms, in.enable=0, in.runLength=326, out.counter=1301, out.position=1301, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1301
22 ms, in.enable=1, in.runLength=326, out.counter=1401, out.position=1401, out.zeroDetected=0, out.offset=0, out.state=STOP, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1402
23 ms, in.enable=1, in.runLength=326, out.counter=1501, out.position=1501, out.zeroDetected=0, out.offset=99, out.state=MOVING, in4_raw=1, out.in4_filtered=1, fastOut4=1, encoder=1502
24 ms, in.enable=1, in.runLength=326, out.counter=1601, out.position=1601, out.zeroDetected=0, out.offset=199, out.state=MOVING, in4_raw=1, out.in4_filtered=1, fastOut4=1, encoder=1602
25 ms, in.enable=1, in.runLength=326, out.counter=1701, out.position=1701, out.zeroDetected=0, out.offset=299, out.state=MOVING, in4_raw=1, out.in4_filtered=1, fastOut4=1, encoder=1702
26 ms, in.enable=1, in.runLength=326, out.counter=1801, out.position=1801, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1802
27 ms, in.enable=1, in.runLength=326, out.counter=1901, out.position=1901, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=1902
28 ms, in.enable=1, in.runLength=326, out.counter=2001, out.position=2001, out.zeroDetected=0, out.offset=326, out.state=DONE, in4_raw=1, out.in4_filtered=1, fastOut4=0, encoder=2002
Из забавного, блок для фильтрации входов получился гораздо больше, чем я ожидал изначально:
Вложение 27224
Работает, и ладно. Надо будет -- соптимизируем или сделаем другую реализацию.
Давным-давно, Yegor предложил сделать возможность использования ФБ без объявления в переменных. Например, для одноразовых RTRIG это в самый раз.
Эта штука пригодилась.
Например, ФБ для обработки входов (с фильтрацией) создаётся таким образом:
Вложение 27225
PRU_DEBOUNCE не объявляется, и не захламляет "область объявления переменных".
Аналогично, в конце кода ABZ энкодера лаконично располагаем RTRIG и радуемся:
Вложение 27226