Проблемы с библиотекой TimerOne (работает не так, как ожидалось)

VadimS
Offline
Зарегистрирован: 28.11.2018

Добрый всем день. Это первый мой пост на этом форуме, так что прошу сильно не пинать, если что..

Задачу себе придумал такую: сделать корректор показаний спидометра для мотоцикла. Дело в том, что на некоторых мотоциклах сейчас датчик скорости стоит на вторичном валу коробки и при изменении передаточного числа (просто заменили ведущую или (и) ведомую звезду на другую, с другим количеством зубьев, спидометр начинает врать). Буржуи продают устройства коррекции (SpeedoHealer). Но коробочка стоит совсем не гуманно, плюс доставка. В общем не интересно. Сенсор скорости видимо представляет собой датчик холла, который генерит импульсы напрямую в приборку. Нужно определить частоту (или длину одного импульса) и подкорректировав, передать на приборку. В сервис-мануале на мотоцикл, в разделе проверки датчика скорости, сказано, что при вращении вторичного вала, на таком то контакте (всего их 3) должен появляться сигнал 5 В. Ну как бы ардуино напрашивается сама собой. Посидел с бумажкой и карандашом и выходит, что минимальная частота, которую нужно будет генерить ~ 1.5 Гц. Поэтому по рекомендации гугла, решил использовать библиотеку TimerOne. Для моделирования собрал макет из трехпроводного кулера, пьезопищалки и китайской ардуины UNO. (картинка не моя и к сожалению, не понял, как убрать картинку под спойлер)

пищалку подключил на 9-й пин.

#include <TimerOne.h>

const byte fqPin = 2;             // Номер пина для получение аппаратного прерывания
const byte outPin = 9;            // Номер пина для генерации выходного сигнала
volatile unsigned long counter;   // Количество отсчётов.
volatile unsigned long mks;       // Время последнего отсчёта.

unsigned long oldTime;            // Время последнего отсчёта в предыдущем вычислении.
unsigned long previousMillis = 0; // храним время последнего измерения
const long interval = 100;        // интервал между измерениями в миллисекундах (0.1 сек)

// Функция для обработки прерывания.
void myISR() {
  mks = micros(); // Момент последнего отсчёта
  counter++;  // Количество отсчётов
}

void setup() {
  unsigned long rpm;
  unsigned long currentMillis;
  unsigned long tmr;
  unsigned long cnt;
  
  pinMode(outPin, OUTPUT);
  Timer1.initialize(8000000);
  Timer1.pwm(9, 8);
  //Serial.begin(115200);
  // Подключаем функцию myISR на прерывание по появлению сигнала на ноге fqPin.
  attachInterrupt(digitalPinToInterrupt(fqPin), myISR, RISING);   // Для подсчета импульсов в минуту
  // Начинаем бесконечный цикл
  while (true) {
    currentMillis = millis(); // текущий момент времени (для организации паузы вместо delay())
    
    // Если с момента прошлого измерения прошло уже больше, чем 0.65 секунды
    // т.е. стоим на месте или скорость меньше 1 км/ч
    if (currentMillis - previousMillis > 650) {
      previousMillis = currentMillis;
      // Получаем данные.
      noInterrupts();
      oldTime = mks;
      counter = 0;
      interrupts();
      Timer1.setPeriod(0);  //rpm = 0
      //Serial.println( "interval > 0.65" );
    }

    // Если с момента прошлого измерения прошло меньше 0.65 секунды,
    else {
      //Serial.println( counter );
      // Если с момента прошлого измерения прошло больше, чем interval,
      // и за это время счетчик увеличился на 2 и больше отсчетов
      if ((currentMillis - previousMillis > interval) && (counter > 1)) {
        previousMillis = currentMillis;
        // Получаем данные.
        noInterrupts();
        cnt = counter;
        counter = 0;
        tmr = mks;
        interrupts();

        // Рассчитываем среднюю длительность одного импульса в микросекундах
        rpm = ((tmr - oldTime) / cnt);//*150/100 // Таким образом можем пересчитать длину импульса на выходе
        Timer1.setPeriod(rpm);
        oldTime = tmr;
        //Serial.println( rpm );
      }
    }
  }
}

void loop() {
}

Изначально, в строке 26 стояло Timer1.pwm(9, 512); Т.е. с заполнением 50%. 

В принципе все работает, пищалка пищит , при торможении двигателя рукой частота звука соответственно меняется, НО! На некоторых оборотах щелчки пропадали совсем, тормозишь крыльчатку еще сильнее, звук появляется, даешь разогнаться, то же с какой то скорости звук появляется. Методом научного тыка выяснилось, что если уменьщить коэффициент заполнения до ~ 8 пищалка пищит во всем диапазоне от 0 до ~3000 об мин.

Для опыта написал еще тестовый скетч:

// Имитируется воспроизведение данных, полученных с датчика скорости мотоцикла
// при равномерном разгоне/торможении мотоцикла
// С ускорением а
// От скорости vStart 
// До скорости vFinish 
// С интервалом между замерами tInterval (миллисекунды - float)
// Данные по частотам предварительно собираются в массив, затем с помощью
// библиотеки TimerOne на 9 пине генерятся прямоугольные импульсы с переменной частотой.
#include <TimerOne.h>

// Глобальные константы
const float a = 9.0;                          // Задаем ускорение  (м/с2) 
const float vStart = 0.0;                     // Начальная скорость (км/ч)
const float vFinish = 180.0;                  // Конечная скорость (км/ч)
const float tInterval = 100.0;                // Интервал между измерениями (миллисекунды)

const float roundOfWheel = 1.956;             // Длина окружности колеса
const float gearRatio = 2.733;                // Передаточное число колесо -> вторичный вал коробки
const float numberOfPulsesPerRevolution = 4.0; // Количество импульсов на один оборот вала

void setup() {
  unsigned long currentMillis;                // для организации паузы вместо delay()
  unsigned long previousMillis = 0;           // храним время последнего измерения
  bool myDirection = true;                    // увеличиваем (истина) или уменьшаем (ложь) значение адреса массива i
  unsigned long rpm;

  // Предварительная подготовка массива
  float vStartMS = vStart * 1000.0 / 3600.0;   // Начальная скорость в м/с
  float vFinishMS = vFinish * 1000.0 / 3600.0; // Конечная скорость в м/с
  float vTMP = vStartMS;                       // Текущая скорость в м/с
  Serial.begin(115200);
  // Сначала подсчитаем каким будет размер массива
  int j = 0;
  while (vTMP <= vFinishMS) {
    vTMP = vTMP + a * tInterval / 1000.0;      // Новая скорость в м/с ч/з tInterval миллисекунд
    j++;
  }
  // Создаем массив для данных размером j
  long arrayPeriod[j]; 

  // Заполняем массив данными
  vTMP = vStartMS;
  j = 0;
  while (vTMP <= vFinishMS) {
    float newV = vTMP + a * tInterval / 1000.0;         // Новая скорость в м/с ч/з tInterval миллисекунд
    float sTMP = (newV * newV - vTMP * vTMP)/(2.0 * a); // Путь в м, пройденный за tInterval миллисекунд
    float numberOfPulses = sTMP / roundOfWheel * gearRatio * numberOfPulsesPerRevolution; // Количество импульсов, сгенерированных за интервал tInterval
    long lengthOfImpuls = long(1000.0 / (numberOfPulses / tInterval));  // Средняя длина одного импульса в интервале (в микросекундах)
    Serial.println( lengthOfImpuls );
    arrayPeriod[j] = lengthOfImpuls;
    vTMP = newV;                                      // Запомнили новую скорость
    j++;
  }
  //Serial.println( "===========" );
  // И запоминаем количество элементов в массиве
  int i = j;
   // Начинаем пищать в пищалку
  pinMode(9, OUTPUT);
  Timer1.initialize(8000000);
  Timer1.pwm(9, 8);   // Коэффициент заполнения ~0.78%, при большем коэффициенте заполнении на некоторых частотах звук в пищалке пропадает ???
  
  j = 0;
  unsigned long longInterval = long(tInterval);
  while (true) {
    currentMillis = millis();
    if (currentMillis - previousMillis > longInterval) {
      previousMillis = currentMillis;
      rpm = arrayPeriod[j];
      //Serial.println( rpm );
      Timer1.setPeriod(rpm);

      if (j == (i - 1)) { // Последний элемент массива, меняем направление на обратное ("тормозим")
        myDirection = false;
      }
      if (j == 0) { // Первый элемент массива, меняем направление на прямое ("разгоняемся")
        myDirection = true;
      }
      if (myDirection) {
        j++;
      }
      else {
        j--;
      }
    }
  }
}

void loop() {

}

 

VadimS
Offline
Зарегистрирован: 28.11.2018

Упс, вместо того, что бы продолжить редактирование, я пост сохранил. А редактировать можно?

Ладно, продолжу.

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

Ну собственно вопросы:

1) Почему пропадает звук? 

2) Почитав этюды для начинающих (Блинк без делэй и т.д...), понял, что можно воспроизводить не любую частоту а вполне определенный набор. хоть и достаточно большой. Как библиотека выбирает, что воспроизвести (длительность периода 397696 или 397568) если я попрошу ее воспроизвести 397700? В исходник библиотеки смотрел. Но как с чтением книги на казахском языке: вроде буквы все знакомые а смысл ускользает :)

3) И можно ли в этом случае использовать эту библиотеку для ЭТОЙ задачи?

Оценить качество воспроизводимого сигнала сам не могу, т.к. из инструментов только мультиметр.