Недорогой самодельный тахометр для газонокосилок и прочих двигателей на ATTINY13A

В общем, косил я как-то траву рядом с кустом смородины, и ветка этого куста зацепилась за пружину, которая регулировала обороты двигателя газонокосилки. Пружина, видимо, растянулась, и обороты упали. Ухом-то я это услышал, но как вернуть положенные по спецификациям 2800 оборотов в минуту? Нужен тахометр! Посмотрел в интернет магазинах, в принципе, продают недорогие тахометры для двигателей лодок рублей за 300-500, но отзывы дают понять, что они чаще всего показывают рандомные цифры, никак не связанные с реальными оборотами двигателя. Да даже на более дорогие лазерные тахометры немало нелестных отзывов. А покупать профессиональный тахометр для одноразовой настройки я как-то не планировал. В общем, от закупленных оптом по дешевке пяти ATTINY13A осталось еще четыре, и я решил пустить очередную в дело.

Что было использовано.

  • ATTINY13A (c держателем для удобства перепрограммирования).
  • 4-digit display на TM1637.
  • Датчик Холла NJK-5002C (реагирует на магнит).
  • Кнопка (не обязательно, но расширяет функционал).
  • Micro USB разъем для подключению к повербанку.

Цены не даю, но все это лютый дешман, при покупке из китая в 200 рублей уложиться реально, а если половина из этого уже есть, то грех не использовать.

Почему ATTINY13A — не лучший выбор для тахометра.

Данный микроконтроллер имеет встроенный осциллятор с погрешностью плюс-минус 10%. Это много, это дает огромную погрешность в сотни оборотов в минуту! ATTINY13A не позволяет подключить к нему внешний кварцевый резонатор, в отличие от серии MEGA, но позволяет подключить внешний генератор, который стоит денег, и его еще нужно купить (или спаять самому), так что овчинка выделки не стоит, учитывая, что какая-нибудь Arduino Nano стоит несильно дороже, но уже имеет кварц на борту. Но Ардуино — это уже баян, тахометры на ардуино только ленивый не делал, поэтому хочется поработать с голыми микроконтроллерами просто ради интереса. Я начал выяснять, как повысить точность встроенного осциллятора. Сначала я проверил оставшиеся у меня четыре экземпляра ATTINY13, при измерении одного и того же значения, разброс у разных экземпляров был от 5106 до 5506 об/мин — понятно, что такая точность никуда не годится.

Калибровка.

Исследования в интернете показали, что встроенный осциллятор можно откалибровать. Вот здесь человек заморочился и сделал отдельный девайс для этого:
https://www.joyta.ru/13516-kalibrator-osc-generatora-i-ustrojstvo-vosstanovleniya-fyuzov-avr-mikrokontrollerov/
Здесь вариант калибровки попроще:
https://www-instructables-com.translate.goog/ATtiny-Oscillator-Calibration/?_x_tr_sl=auto&_x_tr_tl=ru&_x_tr_hl=ru&_x_tr_pto=wapp

Я попробовал поиграть с подобокой OSCCAL с помощью осциллографа, но оказалось, что шаг калибровки OSCCAL слишком большой, и точную частоту выставить все равно не удается. Поэтому я пошел другим (и более простым) путем, а именно софтварная корректировка измеренного значения. Поскольку NJK-5002C по сути создает низкий уровень при приближении магнита, мы может эмулировать работу этого датчика с помощью любого заслуживающего доверия генератора. Я использовал генератор, встроенный в карманный осциллограф DSO3D12. Методика следующая — подаем прямоугольный сигнал известной частоты, и смотрим, чтобы на экране отображалось правильное значение. В коде есть две константы:
UPDATE_MS — время в миллисекундах (по мнению внутреннего осциллятора), в течение которого проводится подсчет импульсов
UPDATE_MS_CALC — время в миллисекундах, которое реально прошло и используется для подсчета числа оборотов в минуту.

Изменяя UPDATE_MS_CALC в большую или меньшую сторону, добиваемся того, чтобы отображаемое на экране значение соответствовало выставленной на генераторе частоте. Это значение будет работать более или менее правильно именно на данном экземпляре микроконтроллера — при замене чипа на другой, калибровку придется провести заново. Я проверил и на низких частотах, свойственных газонокосилкам (50-60 Герц), и на 1-9 килогерцах — калибровка линейная и работает правильно на всем диапазоне, который может показать 4-позиционный экран. Впрочем, на высоких частотах значение может немного плавать вокруг реальной частоты, при желании это можно минимизировать фильтрацией и усреднением.

Ну и надо помнить, что внутренний RC-осциллятор подвержен влиянию температуры и еще много чего, поэтому не является точным даже после калибровки (показания частоты самого процессора плавают).  В идеале оптимальнее калибровать на той частоте, которую вы планируете измерять, и при той температуре, при которой вы планируете измерять. Для уменьшения эффекта самонагрева микросхемы, лучше установить частоту процессора поменьше, например, 1.2Mhz. Но для регулировки оборотов ДВС оставшимимя после калибровки погрешностями можно пренебречь.

Диапазон и точность измерений.

Погрешность измерений сильно зависит от времени, в течение которого мы подсчитываем импульсы. Так, если мы замеряем импульсы в течение одной секунды, то 1 оборот в секунду покажет 60 rpm, это и будет шагом измерений. То есть 1.3 оборота в секунду — это все равно 60 rpm, а 2 оборота в секунду — уже 120 rpm. Если же мы проводим замер в течение двух секунд, то шаг будет 30 rpm, при трех секундах шаг будет 20 rpm, то есть чем больше время замера, тем меньше шаг и меньше погрешность вычислений. Но при этом уменьшается частота обновления показаний на экране, что действует на нервы. Кроме того, целочисленные вычисления вносят бОльшие погрешности при малых числах. Поэтому, как обычно, надо искать золотую середину. Мне 60 rpm кажется многоватым, а 20 rpm — приемлемая погрешность, поэтому я оставил время замера и обновления экрана 3 секунды, в конце концов, мне тахометр нужен для регулировки двигателя газонокосилки, а не чтобы его на мотоцикл ставить, газовать и любоваться резко подпрыгивающей стрелкой.

Режимы работы.

Ясное дело, что 4-позиционный экран позволяет показать до 9999 оборотов в минуту. Для двигателей газонокосилок этого вполне достаточно, но мне хотелось сделать более универсальный прибор, который мог бы измерить больше. Например, скорость вращения патрона гравера может достигать десятков тысяч оборотов в минуту. Принимая во внимание, что даже с шагом 20 rpm у нас на конце всегда будет 0, я сделал возможность вывода значения оборотов, деленое на 10. То есть 30000 оборотов показывается как 3000 (последний ноль — в уме), а максимум мы теперь можем измерить 99999 оборотов в минуту. Существуют три режима отображения:
0: частота в герцах (просто подсчет импульсов за секунду — удобно для калибровки).
1: частота в оборотах в минуту.
2: частота в оборотах в минуту, деленая на 10.

Выбор режима осуществляется длительным нажатием на кнопку PB4 во время измерений. Выбранный режим циклически показывается на экране в течение 1 секунды, если отпустить кнопку, когда показан нужный режим, он становится активным.

Также есть выбор количества пульсаций за 1 оборот. Например, если на вашей газонокосилке стоят 2 магнита, то за один оборот вы получите 2 импульса, и частота будет умножаться вдвое. Чтобы показывать правильную частоту, нужно установить количество пульсаций за оборот на 2. Для этого перед подачей питания на тахометр зажмите кнопку PB4, цифры будут меняться от 1 до 4, выберите нужный делитель и отпустите кнопку (не знаю, есть ли косилки с 3 или 4 магнитами, но для универсальности — пусть будет, чтобы потом не перепрошивать прибор, если такая косилка попадется).

К сожалению, на ATTINY13 недостаточно памяти, чтобы реализовать сохранение настроек в EEPROM, и после каждого включения они сбрасываются на настройки по умолчанию, установленные в коде.

Измерения оборотов двигателя газонокосилки.

У моей газонокосилки уже есть встроенный магнит, который дает сигнал свече на зажигание, но он слабоватый, поэтому датчик нужно держать практически вплотную. В приципе, можно приклеить неодимовый магнит к любому вращающемуся месту, до которого вы дотянитесь, например, к крыльчатке охлаждения двигателя. На низких оборотах скотч неплохо справляется. На больших оборотах не рекомендую использовать скотч для фиксации магнита — он сразу же улетает со скоростью пули и хорошо если не в глаз 🙂 Будьте осторожны! Мой неодимовый магнит, прикрученный по глупости к ножу газонокосилки, оторвался и разлетелся в клочья, поэтому я все же использовал для настройки оборотов тот магнит, что уже был на двигателе изначально. Если же вы крепите магнит сами, лучше размещать его ближе к оси двигателя для минимизации вибраций и воздействия центробежной силы.

Выводы.

С калибровкой все работает нормально, есть плавание значений около 20 оборотов в минуту, но это по крайней мере лучше, чем то, о чем я читал в отзывах на китайские тахометры с алиэкспреса, где показываемые цифры вообще далеки от реальности. Без калибровки — не стоит даже заморачиваться, иначе со своим г*вноприбором вы переплюнете даже китайцев — если нечем калибровать, лучше сразу делать на микроконтроллере с кварцем! Я не стал придавать этому прибору законченный вид, поскольку мне не хватило возможностей ATTINY13A для реализации всех своих хотелок, он так и остался в виде прототипа.

В будущем планирую переделать прибор на ATTINY85 или ATTINY814 с OLED экранчиком — оба имеют возможность подключать внешний кварцевый резонатор и в обоих достаточно памяти для реализации того, что я не смог сделать на ATTINY13A, а именно:

  • сохранять настройки в EEPROM и восстанавливать их при включении
  • менять частоту обновления значения на экране через интерфейс без перепрошивки.
  • работать с числами с плавающей точкой для повышения точности вычислений.
  • OLED экран может показывать более 4 цифр одновременно, да даже может одновременно показывать частоту в герцах и в rpm.
  • режим засыпания при бездействии для энергосбережения  (если питание будет от аккумулятора, а не от повербанка).

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

Скетч.

// библиотека для работы с дисплеем: https://github.com/lpodkalicki/attiny-tm1637-library

// PB0=DIO; PB1=CLK; PB3=PULSE; PB4=BUTTON
#define F_CPU 1200000
#include «tm1637.h»
// увеличение UPDATE_MS_CALC уменьшает показываемое значение
#define UPDATE_MS 3000 // цикл в миллисекундах для расчета и обновления значения на экране (больше = точнее, но тормознее)
#define UPDATE_MS_CALC 2798 // время для рассчетов (при неточностях осциллятора)
// 2852 для высоких частот точнее, 2850 для низких частот стабильнее
#define LED_BRIGHTNESS 7 // яркость экрана 7 — максимум
#define PULSES_PER_REV 1 // количество пульсов на оборот
static volatile uint16_t pulse_cnt;
uint8_t mode = 0; // 0 — FREQ, 1 — RPM, 2 — RPM/10
uint8_t pulses = PULSES_PER_REV;
/* ———- setup ———- */
void setup(){
// Настройка PB3 и PB4 как входы с подтяжкой
DDRB &= ~((1 << PB3) | (1 << PB4)); // Направление: вход
PORTB |= ((1 << PB3) | (1 << PB4)); // Включить pull-up
ADCSRA &= ~(1 << ADEN); // Выключить ADC
ACSR |= (1 << ACD); // Отключить аналоговый компаратор
PRR = (1 << PRTIM0) | (1 << PRADC); // Отключить таймер0 и ADC для экономии энергии
TM1637_init(1, LED_BRIGHTNESS); //1=on, 0=off; brightness 1..7
TM1637_display_colon(0);
TM1637_clear();
// настройка делителя частоты кнопкой при включении
while (!(PINB & (1 << PB4))) {
pulses++; if (pulses>4) pulses=1;
TM1637_display_digit(0, pulses);
_delay_ms(1000);
}
GIMSK |= (1 << PCIE); // разрешить прерывания
PCMSK |= (1 << PCINT3); // только на PB3
}
/* ———- loop ———- */
void loop(){
// смена режима отображения кнопкой
if (!(PINB & (1 << PB4))) {
mode++; if (mode>2) mode=0;
TM1637_clear();
TM1637_display_digit(0, mode);
_delay_ms(1000);
}
staticuint32_t tmr;
pulse_cnt = 0;
tmr = millis();
sei(); // включаем прерывания и начинаем считать импульсы
while (millis() — tmr < UPDATE_MS) { }
cli(); // прерывания с подсчетами не нужны, когда не замеряем время
uint16_t cnt = pulse_cnt;
uint16_t value;
if (mode==1) value = (uint32_t)cnt * 60000 / UPDATE_MS_CALC / pulses;
elseif (mode == 2) value = (uint32_t)cnt * 6000 / UPDATE_MS_CALC / pulses;
elseif (mode == 0 ) value = (uint32_t)cnt * 1000 / UPDATE_MS_CALC / pulses;
if (value > 9999) value = 9999;
/* — просто и понятно, но много кода — не влезает в 1 кб флеш 🙂
//TM1637_clear(); — можно использовать вместо else, если не хватает памяти
if (rpm >1000 ) TM1637_display_digit(0, rpm / 1000); else TM1637_display_segments(0,0);
if (rpm >100 ) TM1637_display_digit(1, (rpm / 100) % 10); else TM1637_display_segments(1,0);
if (rpm >10 ) TM1637_display_digit(2, (rpm / 10) % 10); else TM1637_display_segments(2,0);
TM1637_display_digit(3, rpm % 10);
*/
/* начало оптимизированного блока для отображения цифр */
uint16_t temp = value;
uint8_tdigits[4];
int i;
// Преобразуем число в цифры (в обратном порядке)
for (i = 3; i >= 0; i—) {
digits[i] = temp % 10;
temp /= 10;
}
// Отображаем с подавлением ведущих нулей
uint8_t leading_zero = 1;
for (i = 0; i < 3; i++) {
if (digits[i] > 0) leading_zero = 0;
if (leading_zero) {
TM1637_display_segments(i, 0);
} else {
TM1637_display_digit(i, digits[i]);
}
}
TM1637_display_digit(3, digits[3]); // Последняя цифра всегда отображается
/* конец оптимизированного блока для отображения цифр */
}
/* ———- счётчик импульсов ———- */
ISR(PCINT0_vect){
// считаем пульсы только при переходе с HIGH на LOW:
if (!(PINB & (1 << PB3))) pulse_cnt++;
// если датчик меняет сигнал LOW на HIGH, используем эту строку вместо предыдущей:
// if (PINB & (1 << PB3)) pulse_cnt++;
}

Leave a Reply