Лазерный тахометр, проблемы с кодом.

Тема в разделе "Arduino & Shields", создана пользователем Lastik, 20 июл 2020.

  1. Lastik

    Lastik Нерд

    Доброго времени суток!
    Решил собрать лазерный тахометр на ардуино. И все вроде бы ничего, собрал, простенький скетч написал, загрузил.. Все работает, все хорошо. Но ввиду того, что скетч именно "простенький", он не считает среднее значение. И вот на просторах интернета нарвался на проект тахометра с более серьезным подходом к расчетам. Одна беда, после заливки показывает все, что угодно, но только не реальные обороты.
    Может кто посмотрит и подскажет, где косяк в коде...
    Код (C++):
    #include <OLED_I2C.h>                                        // Подключаем библиотеку OLED_I2C для работы со шрифтами и графикой
    OLED  myOLED(SDA, SCL, 8);                                   // Определяем пины I2C интерфейса
    extern uint8_t SmallFont[];                                  // Подключаем шрифт
    extern uint8_t BigNumbers[];                                 // Подключаем большие цифры

    #define LASER            5                                   // Задаем имя для Pin5 включения лазера
    const byte sensor = 7;                                       // Задаем Pin7 для датчика лазера

    volatile unsigned long t_pulse_started_volatile = 0;
    volatile unsigned long t_pulse_duration_volatile = 0;
    unsigned long t_pulse_started = 0;
    unsigned long t_pulse_duration = 0;

    long rpm_sum = 0;
    long rpm_reading[100];
    long rpm_average = 0;
    byte n_max = 0;
    byte n = 0;

    volatile bool timeout = 1;
    volatile bool newpulse = 0;

    void setup() {

    pinMode(LASER, OUTPUT);                         // Определяем пин Лазера как выход
    pinMode(sensor, INPUT);                         // Определяем пин Датчика как вход
    myOLED.begin();                                 // Инициируем дисплей

    attachInterrupt(digitalPinToInterrupt(sensor), ISR_sensor, RISING);    // Запускаем прерывания
    }
     
    void loop() {
      digitalWrite(LASER,HIGH);                           // Определяем уровень на пине Лазера как высокий
       noInterrupts();                                     // Отключаем прерывания
       t_pulse_started = t_pulse_started_volatile;
       t_pulse_duration = t_pulse_duration_volatile;
       interrupts();
     
       if(((micros() - t_pulse_started) > 2000000) && timeout == 0 && newpulse == 0) {
             
         timeout = 1;
         rpm_average = 0;
         n = 0;

       };
     
       if(timeout == 0){
       
         if(newpulse == 1){
         
           rpm_reading[n] = (60000000 / t_pulse_duration);
           n_max = constrain(map(rpm_reading[n], 60, 100000, 0, 100), 0, 100);
           n++;
           newpulse = 0;
         
           if(n > n_max){
         
             for (byte i = 0; i <= n_max; i++) {
             
               rpm_sum = rpm_sum + rpm_reading[i];
       
             };
           
             rpm_average = rpm_sum / (n_max + 1);    
             rpm_sum = 0;
             n = 0;
           
           }
         
         }
     
       }

    updatedisplay();
     
    }
    void updatedisplay() {
     
      byte x = 0;
      if(rpm_average < 10) x = 80;
      if(rpm_average >= 10 && rpm_average < 100) x = 62;
      if(rpm_average >= 100 && rpm_average < 1000) x = 44;
      if(rpm_average >= 1000 && rpm_average < 10000) x = 26;
      if(rpm_average >= 10000 && rpm_average < 100000) x = 8;
     
     
      if(rpm_average < 100000){
       
        myOLED.clrScr();                                // Стираем все с экрана
        myOLED.setFont(SmallFont);                      // Инициируем шрифты
        myOLED.print("T_A_H_O_M_E_T_R", 20, 10);        // Первая строка выводим надпись "T_A_H_O_M_E_T_R"
        myOLED.print("ob/min", 90, 50);                 // Выводим надпись "ob/min"
        myOLED.setFont(BigNumbers);                     // Инициируем цифры
        myOLED.printNumI(rpm_average, x , 30, 5);
        myOLED.update();
     
      } else {
       
        myOLED.clrScr();                                // Стираем все с экрана
        myOLED.setFont(SmallFont);                      // Инициируем шрифты
        myOLED.print("T_A_H_O_M_E_T_R", 20, 10);        // Первая строка выводим надпись "T_A_H_O_M_E_T_R"    
        myOLED.print("MAX LIMIT", CENTER, 40);
        myOLED.update();
       
        }
     
    }
    void ISR_sensor() {
      t_pulse_duration_volatile = micros() - t_pulse_started_volatile;
      t_pulse_started_volatile = micros();
      timeout = 0;
      newpulse = 1;
    }
    Если я правильно понял, он определяет число замеров для вычисления среднего значения тем больше, чем выше обороты. Соответственно собрав необходимое количество замеров суммирует их и делит на это самое количество... Вроде все верно, но на выходе показания оборотов лавинообразно увеличиваются до максимума (99 000) и опять начинаются с нуля.
     
  2. b707

    b707 Гуру

    для начала попробуйте выкинуть изменяемое число замеров, поставьте фикс. например 10
     
  3. Lastik

    Lastik Нерд

    Попробовал...
    byte n_max =10; вместо byte n_max =0;
    и закомментил расчет n_max
    Все равно выдает странное.. При оборотах кулера 2600 показывает от 5000 до 12000...

    А не может новый импульс от датчика сбивать расчет?
     
  4. parovoZZ

    parovoZZ Гуру

    а самому не написать алгоритм расчета среднего из курса алгебры за 5-ый класс?
     
    b707 нравится это.
  5. Lastik

    Lastik Нерд

    Даже при изменении n_max = 1 результат так же прыгает....

    По поводу алгоритма, в приведенном скетче он вроде как верный и логичный... Вот только понять не могу что с ним не так...
     
  6. b707

    b707 Гуру

    ну так почему не взять ваш первый скетч, который правильно считал обороты - и не добавить к нему расчет среднего, вся премудрость которого - "складываем N замеров и результат делим на N" ?
     
    parovoZZ нравится это.
  7. Lastik

    Lastik Нерд

    Можно было бы. Если с этим кодом не разберусь, так и сделаю...
    Но это не точно.