Фронты по алгоритму ловятся все ,а не один ,но кроме фронта нужного я еще всегда анализирую потенциал на противоположном входе ,что бы уменьшить влияние помех .
Вид для печати
Фронты по алгоритму ловятся все ,а не один ,но кроме фронта нужного я еще всегда анализирую потенциал на противоположном входе ,что бы уменьшить влияние помех .
Рад за вас и ваш алгоритм. Но в моей 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, либо ещё один блок.