считываю обороты с оптического энкодера и вывожу данные на 8 разрядный семисегментный индикатор. для работы с индикатором использую библиотеку LedControl. есть контрольный прибор с таким же энкодером. всё хорошо. ввожу корректировочный коэффициент в виде числа float. считываю импульсы, складываю в число (long), потом умножаю на коэф и полученный результат раскладываю для вывода на индикатор если коэф сразу прописываю то счёт идёт корректно Код (C++): #include "LedControl.h" LedControl lc = LedControl(11, 13, 10, 1); int encoderPin1 = 2; int encoderPin2 = 3; volatile long lastEncoded = 0; volatile long encoderValue = 0; long lastencoderValue = 0; long lastMSB = 0; long lastLSB = 0; long val; long val_old = 0; long distance = 0; long DIST = 0; float koef = 0.9210; int n1 = 0; int n2 = 0; int n3 = 0; long n4 = 0; long n5 = 0; int a1 = 0; int a2 = 0; int a3 = 0; long a4 = 0; long a5 = 0; void setup() { lc.shutdown(0, false); lc.setIntensity(0, 8); lc.clearDisplay(0); pinMode(encoderPin1, INPUT); pinMode(encoderPin2, INPUT); digitalWrite(encoderPin1, HIGH); digitalWrite(encoderPin2, HIGH); attachInterrupt(0, updateEncoder, CHANGE); attachInterrupt(1, updateEncoder, CHANGE); } void loop() { val = encoderValue / 4; if (val > val_old) { n1 ++; if (n1 > 9) { n1 = 0; n2 ++; } if (n2 > 9) { n2 = 0; n3 ++; } if (n3 > 9) { n3 = 0; n4 ++; } if (n4 > 9) { n4 = 0; n5 ++; } if (n5 > 9) { n5 = 0; } val_old = val; } if (val < val_old) { if ( distance > 0) { n1 --; if (n1 < 0) { n1 = 9; n2 --; } if (n2 < 0) { n2 = 9; n3 --; } if (n3 < 0) { n3 = 9; n4 --; } if (n4 < 0) { n4 = 9; n5 --; } } val_old = val; } distance = n1 + n2 * 10 + n3 * 100 + n4 * 1000 + n5 * 10000; DIST = distance * koef; a1 = DIST % 10; a2 = (DIST / 10) % 10; a3 = (DIST / 100) % 10; a4 = (DIST / 1000) % 10; a5 = (DIST / 10000) % 10; if (DIST < 1000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setChar(0, 4, ' ', false); lc.setChar(0, 5, ' ', false); } if (DIST >= 1000 && DIST < 10000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setDigit(0, 4, a4, false); lc.setChar(0, 5, ' ', false); } if (DIST >= 10000 && DIST < 100000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setDigit(0, 4, a4, false); lc.setDigit(0, 5, a5, false); } } //////////////// енкодер //////////////// void updateEncoder() { int MSB = digitalRead(encoderPin1); int LSB = digitalRead(encoderPin2); int encoded = (MSB << 1) | LSB; int sum = (lastEncoded << 2) | encoded; if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++; if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --; lastEncoded = encoded; } если кнопкой переключаю режимы с разными коэф то получается погрешность да 3 процентов может с типами чисел где то ошибка... буду признателен за совет. Код (C++): #include "OneButton.h" #include "LedControl.h" LedControl lc = LedControl(11, 13, 10, 1); OneButton button(9, true); int encoderPin1 = 2; int encoderPin2 = 3; volatile long lastEncoded = 0; volatile long encoderValue = 0; long lastencoderValue = 0; long lastMSB = 0; long lastLSB = 0; long val; long val_old = 0; long distance = 0; long DIST = 0; float koef; int n1 = 0; int n2 = 0; int n3 = 0; long n4 = 0; long n5 = 0; int a1 = 0; int a2 = 0; int a3 = 0; long a4 = 0; long a5 = 0; int state = 0; void setup() { lc.shutdown(0, false); lc.setIntensity(0, 8); lc.clearDisplay(0); pinMode(encoderPin1, INPUT); pinMode(encoderPin2, INPUT); digitalWrite(encoderPin1, HIGH); digitalWrite(encoderPin2, HIGH); attachInterrupt(0, updateEncoder, CHANGE); attachInterrupt(1, updateEncoder, CHANGE); button.attachClick(Click); } void loop() { button.tick(); val = encoderValue / 4; if (val > val_old) { n1 ++; if (n1 > 9) { n1 = 0; n2 ++; } if (n2 > 9) { n2 = 0; n3 ++; } if (n3 > 9) { n3 = 0; n4 ++; } if (n4 > 9) { n4 = 0; n5 ++; } if (n5 > 9) { n5 = 0; } val_old = val; } if (val < val_old) { if ( distance > 0) { n1 --; if (n1 < 0) { n1 = 9; n2 --; } if (n2 < 0) { n2 = 9; n3 --; } if (n3 < 0) { n3 = 9; n4 --; } if (n4 < 0) { n4 = 9; n5 --; } } val_old = val; } /////////////////////////////// if (state == 0) koef = 0.9210; else if (state == 1) koef = 1.0000; lc.setDigit(0, 7, state, false); /////////////////////////////////////// distance = n1 + n2 * 10 + n3 * 100 + n4 * 1000 + n5 * 10000; DIST = distance * koef; a1 = DIST % 10; a2 = (DIST / 10) % 10; a3 = (DIST / 100) % 10; a4 = (DIST / 1000) % 10; a5 = (DIST / 10000) % 10; ///////////////////////////////////////////////////////////////// if (DIST < 1000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setChar(0, 4, ' ', false); lc.setChar(0, 5, ' ', false); } if (DIST >= 1000 && DIST < 10000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setDigit(0, 4, a4, false); lc.setChar(0, 5, ' ', false); } if (DIST >= 10000 && DIST < 100000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setDigit(0, 4, a4, false); lc.setDigit(0, 5, a5, false); } } //////////////// енкодер //////////////// void updateEncoder() { int MSB = digitalRead(encoderPin1); int LSB = digitalRead(encoderPin2); int encoded = (MSB << 1) | LSB; int sum = (lastEncoded << 2) | encoded; if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++; if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --; lastEncoded = encoded; } void Click() { state = 1 - state; }
попробовал float DIST ничего не поменялось.. вал энкодера вращается со скоростью 300 - 500 об/мин при вращении рукой всегда полное совпадение с контрольным счётчиком при вращении двигателем совпадение только в первом варианте
Странный способ подсчета шагов. По мне, так если использовать аппаратное прерывание, то достаточно посмотреть состояние второй ноги чтобы сделать вывод увеличился счетчик или нет. Или это накручено для борьбы с дребезгом? Я вот для энкодера с этой целью юзаю прерывание по таймеру (каждые 250 мкс), считываю состояние первой ноги в кольцевой буфер, через него нахожу изменение по маске 110 (или 001, что пофиг), тут же считываю состояние второй ноги и делаю вывод. Работает всегда со 100% точностью, кроме случаев когда энкодер врет аппаратно, такое иногда бывает при смене направлений, сбивается на 1 шаг изредка. Функция вызываемая по прерыванию: Код (C++): void encRead() { p[0] = p[1]; p[1] = p[2]; p[2] = digitalRead(P1); if (p[0] && p[1] && !p[2]) { digitalRead(P2) ? Step++ : Step--; } } Это для STM32, у него digitalWrite и так очень быстрый, не стал заморачиваться с портами.
Вы старый дед что ли? ))) Это их если что то устраивает, то не будут улучшать даже под страхом смерти. Надо всегда пробовать новые способы, особенно если они эффективнее и менее глючны. Я привел свой способ который раз в несколько короче и практически никогда не сбоит, при этом не реагирует на дребезг. Почему бы не попробовать? Если нужно код для запуска прерыванию по таймеру, то могу написать и его, какой у вас МК?
да уж не молодой)) вы мой пост хоть прочитали существующий код работает если сразу прописан коэффициент. если коэффициентом играться в лупе - погрешности меня это интересует. какое это имеет отношение к методу опроса энкодера
Не утерпел, уж простите. Код опроса показался громоздким и неоправданно сложным, вот и не выдержала душа поэта. ))
Может причина в разных типах данных? У вас там умножение float на long и тп. попробуйте одинаковые типы сделать или привести принудительно (float)...
Limoney уже советовал попробовал кучу вариантов, в том числе и принудительное приведение - всё равно погрешность есть а если сразу прописать - нет, всё гуд))))
Где то округляет значит, где не просят. Хотя, казалось бы разницы никакой. Может быть тут иногда статус болтается? Код (C++): if (state == 0) koef = 0.9210; else if (state == 1) koef = 1.0000; Кстати, опять много написано, так короче: Код (C++): if (!state) koef = 0.9210; else koef = 1.0000; А еще короче так: Код (C++): koef = state ? 1.0000 : 0.9210;
как у чайника в программировании знаний в этом вопросе не хватает)))) хотя koef объявлен как float - ничего при присвоении значения не должно вроде происходить
Сдается мне что проблема в этом: Код (C++): button.attachClick(Click); В упор не вижу антидребезга. То есть состояние state может меняться не так как вам надо, а хаотично, то 0 то 1, смотря как искры сверкнут. Попробуйте или аппаратно добавить RC цепь на кнопку или программно загасить дребезг кнопки.
Недоверяю я слепо разным библиотекам. Тем более что кнопки разные бывают, одни 10мс дребезжат, другие 100+. Или она тестирует кнопку предварительно?
скажу более без кнопки если прописать Код (C++): if (state == 0) { koef = 0.9210; distance = n1 + n2 * 10 + n3 * 100 + n4 * 1000 + n5 * 10000; DIST = distance * koef; a1 = DIST % 10; a2 = (DIST / 10) % 10; a3 = (DIST / 100) % 10; a4 = (DIST / 1000) % 10; a5 = (DIST / 10000) % 10; ///////////////////////////////////////////////////////////////// if (DIST < 1000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setChar(0, 4, ' ', false); lc.setChar(0, 5, ' ', false); } if (DIST >= 1000 && DIST < 10000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setDigit(0, 4, a4, false); lc.setChar(0, 5, ' ', false); } if (DIST >= 10000 && DIST < 100000) { lc.setDigit(0, 1, a1, false); lc.setDigit(0, 2, a2, false); lc.setDigit(0, 3, a3, true); lc.setDigit(0, 4, a4, false); lc.setDigit(0, 5, a5, false); } } тоже фигня получается
0.9210 умноженное на число менее 1000 и заносимое в long округляется до целого. Вы уверены, что distance всегда больше 1000?
вы так и не прочитали мой первоначальный пост если коэффициент прописан при объявлении переменных - всё считается правильно (первый код) если начинаю менять его в цикле начинаются проблемы(второй)