Подсчёт количества оборотов. Датчик препятствия

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Друзья, help!

Есть примитивная задача по подсчёту количества оборотов ролика (колеса, двигателя, тут не принципиально) с помощью датчика препятствия.  Ролик не вращается статично на одном месте, а совершает дополнительно линейное перемещение, в связи с чем в моём случае неудобно использовать датчик холла. Ниже код, который работает с датчиком холла, но не работает с оптическим. Оптический прибавляет не одну единицу, а может сразу 2, 3 или 4. Судя по всему имею эффект "дребезга", но это не точно. Помогите решить проблему, буду очень признателен!

Также хочу добавить, что часто вращения довольно высокая, около 300 об/мин.

П.С. Форум курил, честное слово.

Moderator : пожалуйста, вставьте код правильно (возможно, новым сообщением в тему), 

 
sadman41
Онлайн
Зарегистрирован: 19.10.2016

Знаете, есть ещё более примитивная задача - правильное помещение кода в свой пост. Начните с нее.

Pavel.S
Offline
Зарегистрирован: 16.12.2019
long  x;
long  y;
long  z;

void setup()
{
  Serial.begin(9600);

   x = 0;

   y = 0;

   z = 0;

  pinMode(2,INPUT);

}


void loop()
{
     x = digitalRead(2);
    if (x == 0) {
       y = 1;

    }
    if (y == 1) {
       y = 1;
      if (x == 1) {
         y = 0;
         z = z + 1;
        Serial.println(z);

      }
    }
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

А что происходит в коде, вот это жонглирование x y z - оно для чего вам?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Особенно доставляет вот это:

if(y == 1)
{
  y = 1;

Типа "я сказал единица - значит, единица!" :)))

Pavel.S
Offline
Зарегистрирован: 16.12.2019

На самом деле не думал, что возникнут вопросы. Извиняюсь. Х и Y это состояние входа, наличие (отсутствие) сигнала с датчика. Присвоение значения Y = 1 сделано для того, чтобы программа не добавляла постоянно единицы до тех пор, пока не пропадет сигнал (разомкнется ключ). Z - counter (счётчик)

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

sadman41
Онлайн
Зарегистрирован: 19.10.2016

На attachInterrupt() по FALLING подвесится и делать ++ переменной.

Pavel.S
Offline
Зарегистрирован: 16.12.2019
int counter; //счетчик

void setup() {
Serial.begin (9600);
pinMode(2,INPUT_PULLUP); //подятжка к VCC
attachInterrupt (0, myInterrupt, FALLING); //прерывание
}

void loop() {
Serial.println (counter);
}
void myInterrupt () {
  counter++;
}

 

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Пробовал с прерыванием. Вот код, с датчиком холла всё ок, работает корректно, ставлю оптический - начинает прибавлять не единицы, а 200, 300, 500 и т.п. Ткните носом пож, что не так?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Ну, тут не все OK, конечно. Volatile потеряно и атомарное чтение. Но, мне кажется, что конструкция допускает паразитную засветку оптического датчика.

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Окай, прописываю volatile int counter, ничего не меняется. Холл нормально прибавляет значения, а оптический 200,300,500. Как побороть эту паразитную засветку и почему при этой засветке он прибавляет сотни?

 

Pavel.S
Offline
Зарегистрирован: 16.12.2019
    int counter = 0;
    volatile boolean intFlag = false;   // флаг
    
    void setup() {
      pinMode(2,INPUT_PULLUP);
      Serial.begin(9600); // открыли порт для связи

      // D2 это прерывание 0
      // обработчик - функция buttonTick
      // FALLING - при нажатии на кнопку будет сигнал 0, его и ловим
      attachInterrupt(0, buttonTick, FALLING);
    }
    void buttonTick() {
      intFlag = true;   // подняли флаг прерывания
    }
    void loop() {
      if (intFlag) {
        intFlag = false;    // сбрасываем
        counter++;
        delay(50);
        Serial.println(counter);
      }  
    }

Есть положительная динамика при применении флажков. Но! Вместо +1, он почему-то прибавляет 2, а датчик холла прибавляет единицу как положено ((((((

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Я не телепат, не вижу что за конструктив у вас там.

Pavel.S
Offline
Зарегистрирован: 16.12.2019

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

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Оптический замыкаете?

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Угу

kostyamat
Offline
Зарегистрирован: 16.11.2017

А если так

int counter = 0;
volatile boolean intFlag = false; // флаг

void setup() {
pinMode(2,INPUT_PULLUP);
Serial.begin(9600); // открыли порт для связи

// D2 это прерывание 0
// обработчик - функция buttonTick
// FALLING - при нажатии на кнопку будет сигнал 0, его и ловим
attachInterrupt(0, buttonTick, FALLING);
}
void buttonTick() {
intFlag = true; // подняли флаг прерывания
}
void loop() {
if (intFlag) {
counter++;
Serial.println(counter);
intFlag = false; // сбрасываем
}
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Как механический контакт замыкают - понимаю. Как оптический - не очень.

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Не-а, при замыкании +2 в счётчике идёт. Всё также.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Так может у вас датчик такой? Формирует два импульса, вместо одного. Если это поведение стабильно, то делите результат на 2 да и все.

 

Хотя я тоже не понимаю какой ключ, и как вы замыкание? Вы в реальных условиях все это как-то испытывали?

Ещё одно, стоит почитать вот это https://stackoverflow.com/questions/30420758/counter-vs-counter-counter1 и без надобности не писать умные конструкции типа counter++
Стоит таки быть проще и написать counter = counter + 1
Не охота анализировать как оно отрабатывает в данном случае, под прерываниями, но написав попроще, не придется потом долго разбираться где ошибка. (Я не настаиваю, что в данном случае ошибка, просто предостеригаю, сам попадал в такое).

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Мужики, ну замыканием я называю срабатывание датчика и отправку сигнала с логического выхода на pin2. Нет блин, сижу ножницами плюс и минус замыкаю. 

Короче если делить на два, или к примеру прибавлять 0.5, то в порте отображается 0, причём дважды после срабатывания датчика.

Судя по всему он отправляет сигнал дважды при срабатывании, первый раз при появлении препятствия и второй раз при его отдалении. На плате UNO есть Led индикатор RX, на нём видно, как дважды отрабатывает сигнал.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Как у вас отрабатывает поправленный мною код?

Попробуйте заменить counter++ на обычный counter = counter + 1

Я чуть выше на счёт этого свой комментарий дополнил.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

В данном случае от ++ опасности нет, так как значение переменной перед инкрементом не используется.

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

Pavel.S
Offline
Зарегистрирован: 16.12.2019

++ или counter+1 - разницы нет. Меняя местами действия в loop ничего не меняется. Датчик отправляет сигнал при изменении логического 0 на 1 и второй раз при изменении с 1 на 0, т.е. дважды. Что за магия, шайтан...

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Как он выглядит?

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Обычный датчик препятствия, YR-63

 

 

kostyamat
Offline
Зарегистрирован: 16.11.2017

Ну в таком случае, засчитывайте единицу по каждому второму импульсу, вот http://arduino.ru/Reference/Modulo
if (counter % 2 == 0) {
// Прибавляем к какой-то другой переменной 1
}
А тупо делить int, или умножать на не целое просто нельзя. Логика простая - дробные будут отброшены. Поэтому int 1/2 = 0, а 3/2 = 1.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Где-то у меня такой валялся... Дупления не замечал за ним. Хотя, я только в качестве концевика его использовал, канеш.

Делимость на два ещё можно проверить через (false == x&1)

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Мужчины, будьте благородны, укажите что на что в коде поменять, не очень доходит.

Sadman, если есть такой датчик, умоляю, попробуйте подрубить его.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Э нет! Тут так это не работает. Попробуйте таки сами разобраться что в коде менять. Или хотя бы попробуйте. Пока свой вариант не выложите, никто за вас писать не будет. Как два импульса за один считать вам уже два раза подсказали.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Вобщем, у меня FC-03. По схеме похож, по конструктиву - нет.

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Лаааадно, завтра попробую разобраться, жена палкой гонит спать.  Спасибо мужчины в любом случае. И это, хотел добавить, в мониторе порта счётчик идёт не 2,4,6,8,10, а последовательно 1,2,3,4,5 и т.д, только вот при срабатывании датчика получаю +1 и следом ещё раз +1. При изменении логического 0 на 1 и обратно при изменении с 1 на 0. Надеюсь понятно. 

Pavel.S
Offline
Зарегистрирован: 16.12.2019

FC 03 такой же на базе оптопара. По идее разницы для эксперимента не должно быть, «я так думаю»!))

kostyamat
Offline
Зарегистрирован: 16.11.2017

Pavel.S пишет:

Лаааадно, завтра попробую разобраться, жена палкой гонит спать.  Спасибо мужчины в любом случае. И это, хотел добавить, в мониторе порта счётчик идёт не 2,4,6,8,10, а последовательно 1,2,3,4,5 и т.д, только вот при срабатывании датчика получаю +1 и следом ещё раз +1. При изменении логического 0 на 1 и обратно при изменении с 1 на 0. Надеюсь понятно. 


Ну, теперь понятно, что вы голову морочите.
Я надеюсь вы понимаете разницу между получаю count + 2, и тем, что вы только что написали?

Но так или иначе, решение для двух импульсов вам подсказали выше.

Pavel.S
Offline
Зарегистрирован: 16.12.2019

kostyamat пишет:
Ну, теперь понятно, что вы голову морочите. Я надеюсь вы понимаете разницу между получаю count + 2, и тем, что вы только что написали? Но так или иначе, решение для двух импульсов вам подсказали выше.

Сдаюсь. Не могу додумать, как в счётчик загнать каждый второй импульс. Вторую переменную сделал (количество импульсов), с обнулением и флажками запутался. Дайте еще подсказку, пжлст!

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Намудрил тут, рукалицо

 int counter=0; // счетчик импульсов
 int counter2=0; // счетчик оборотов
 
    volatile boolean intFlag = false;   // флаг

    void setup() {
      pinMode(2,INPUT_PULLUP);
      Serial.begin(9600); // открыли порт для связи

      // D2 это прерывание 0
      // обработчик - функция buttonTick
      // FALLING - при нажатии на кнопку будет сигнал 0, его и ловим
      attachInterrupt(0, buttonTick, FALLING);
    }
    void buttonTick() {
      intFlag = true;   // подняли флаг прерывания
    }
    void loop() {
      if (intFlag) {
        intFlag = false;    // сбрасываем
        counter++;
        }

        if (counter==2) {
        counter2++;
        Serial.println(counter2);
        counter=0;
        }
    

    }

 

kostyamat
Offline
Зарегистрирован: 16.11.2017

Ну почему же рукалицо, вполне рабочий вариант. Вот только заменить
if (counter=2) {
на
if (counter==2) {

Pavel.S
Offline
Зарегистрирован: 16.12.2019

kostyamat пишет:
Ну почему же рукалицо, вполне рабочий вариант. Вот только заменить if (counter=2) { на if (counter==2) {

Ок, поставил дополнительное "=" перед 2 и 0, теперь counter2 доходит до значения 1 и счёт останавливается.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Т.е. операцию присвоения от операции сравнения Вы не отличаете...

Pavel.S
Offline
Зарегистрирован: 16.12.2019

sadman41 пишет:

Т.е. операцию присвоения от операции сравнения Вы не отличаете...

Болван. Убрал одно "=" перед 0.

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Господа, спешу поделиться радостью, решение найдено. Ниже рабочий скетч. Нужно было всего лишь изменить сравниваемое значение с counter с 2 до 4. Судя по всему датчик посылает 4 импульса при переходе с логического 0 на 1 и 1 на 0. Провел полевые испытания и закралось подозрение, что датчик выполняет пропуски при высоких оборотах. Есть ли этому причины в коде? И как его возможно упростить?

 int counter=0; // счетчик импульсов
 int counter2=0; // счетчик оборотов
 
    volatile boolean intFlag = false;   // флаг

    void setup() {
      pinMode(2,INPUT_PULLUP);
      Serial.begin(9600); // открыли порт для связи

      // обработчик - функция buttonTick

      attachInterrupt(0, buttonTick, RISING);
      Serial.println(counter);
    }
    void buttonTick() {
      intFlag = true;   // подняли флаг прерывания
    }
    void loop() {
      if (intFlag) {
        intFlag = false;    // сбрасываем
        counter++;
        }

        if (counter==4) { // если значение импульсов достигло 4, то прибавляем 1 к счетчику и обнуляем счетчик импульсов
        counter2++;
        Serial.println(counter2); 
        counter=0; // сбрасываем
        }
    

    }

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Странный датчик какой-то

"Упростить" так:

#include <util/atomic.h>

const uint8_t extIntPin = 2;
const uint8_t dividerMax = 4;

volatile uint16_t isrCounter;

void handleInterrupt() {
  static uint8_t divider;
  divider++;
  if (dividerMax >= divider) {
    isrCounter++;
    divider = 0x00;
  }
}

void setup() {
  Serial.begin(115200); 
  pinMode(extIntPin , INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(extIntPin), handleInterrupt, RISING);
}

void loop() {
  static uint16_t prevCounter;
  uint16_t realCounter;

  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    realCounter = isrCounter;
  }

  if (prevCounter != realCounter) {
    prevCounter = realCounter;
    Serial.println(realCounter);
  }
}

 

kostyamat
Offline
Зарегистрирован: 16.11.2017

Ну, для начала, имхо, я бы инкриментацию count все таки перенес бы в обработчик прерываний. Во вторых, я бы не выводил каждое показание в serial, тем более на такой низкой скорости как 9600. Потому как пока у вас выводится в сериал, у вас флаг мог падать несколько раз, а вы этого не замечаете. (Вообще, почему-то все новички забывают, что serial так же кушает ресурсы МК. Сам таким был.)
В сериал можно выводить например в конце всего замера. Чтобы точно убрать его влияние.
Логика такая: запоминаем значение millis() (создаём таймер), в обработчике прерывания подымаем флаг (так как у вас сейчас, но использовать будем для другого), в основном цикле - если флаг поднят опускаем и обновляем значение таймера новым millis(). Дальше по циклу - если millis() - таймер => 1000 то выводим значение в сериал.
Таким образом - в сериал будут выводится окончательные значения, ровно через секунду после последнего замера. И вывод в сериал не будет вам тормозить весь скетч.

kostyamat
Offline
Зарегистрирован: 16.11.2017

sadman41 пишет:

Странный датчик какой-то

"Упростить" так:

#include <util/atomic.h>

const uint8_t extIntPin = 2;
const uint8_t dividerMax = 4;

volatile uint16_t isrCounter;

void handleInterrupt() {
  static uint8_t divider;
  divider++;
  if (dividerMax >= divider) {
    isrCounter++;
    divider = 0x00;
  }
}

void setup() {
  Serial.begin(115200); 
  pinMode(extIntPin , INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(extIntPin), handleInterrupt, RISING);
}

void loop() {
  static uint16_t prevCounter;
  uint16_t realCounter;

  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    realCounter = isrCounter;
  }

  if (prevCounter != realCounter) {
    prevCounter = realCounter;
    Serial.println(realCounter);
  }
}

 


Ну и? И чему человек так научится? Если даже я (уже вроде не полный профан) не до конца всю эту вашу кухню без словаря разберу?

Давайте затрем пока не видел. Потом когда сам дойдет, тогда и подарите.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Напишите по-своему, в чём проблема? Посмотрим, у кого непонятней выйдет ))

Поздно затирать. Закомментили уже.

В сущности - тут нет ничего такого, чего бы он уже сам не писал ранее. Я только местами поменял и переобозвал переменные, вот и всё.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Я не про понятность, а о том, что вы готовое дали, а я выше алгоритм предложил. Если челу, который присваивание и сравнение путает, дать готовое, он не научится сам думать. Потому и предлагаю. Как сам до чего то дойдет, то тогда это и подарите. А пока потереть лучше.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Да, действительно, я дурак, закоментил.
Надеюсь ТС хоть попробует ваш подарок разобрать и понять. И на меня пускай не обижается. Я ведь наоборот ему лучшего желаю. :)

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Я же написал, что поздно. Не комментили бы - стёр. А так всё, аллес гемахт.

Впрочем, там ещё немного ускорить можно - пусть думает ))

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Я одно не могу понять, почему такая тривиальная задача превратилась в Бог знает что? Это же самый обыкновенный датчик, на базе которого тот же Гайвер делал тахометр. Как он блин у него тогда работал... Завтра поеду в магаз и куплю ещё один датчик, а заодно и аналоговый возьму. За скетч спасибо конечно, но в нем я толком ничего не понял, хотя структура знакома.  
ПС. За наставление на путь самурая благодарю, учение свет. Только у меня нет сейчас на это времени, да и ардуино я в руках неделю держу.

ПС2. Да и тем более мужчины, мне в какой-то степени жаль отнимать ваше время на эти, как мне кажется, совсем не интересные для вас разъяснения. Думал тут нужно было внести небольшую правку, а вот видите как вышло. 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

kostyamat пишет:
Ну и? И чему человек так научится? Если даже я (уже вроде не полный профан) не до конца всю эту вашу кухню без словаря разберу? Давайте затрем пока не видел. Потом когда сам дойдет, тогда и подарите.

Все равно не в коня корм, можно не переживать ))

Pavel.S
Offline
Зарегистрирован: 16.12.2019

Короче, рылся в поисках инфо и наткнулся на проект Гайвера «колесный измеритель расстояния». Проект строился на без этого датчика. На 3.50 мин он значит говорит, что у этого датчика есть мерзкая особенность, при медленном вращении он срабатывает дважды. Ну а на 8.40 мин он говорит, мол в *опу этот датчик, причём так красноречиво )))) Угарнул конечно. 
https://youtu.be/LE-3Z7RaSww