В некоторых случаях бывает необходимо измерить скорость вращающегося объекта дистанционно. Для такой задачи отлично подойдет лазерный тахометр.
В рамках данного проекта мы создадим небольшой ручной тахометр, который использует лазер, чтобы измерить, как быстро объект вращается в оборотах в минуту (об/мин).
В качестве основы для программы Arduino будем использовать микроконтроллер ATtiny1614. Этот микроконтроллер имеет очень низкое энергопотребление в спящем режиме. Вся схема питается от литий-ионного аккумулятора 3,7 В и включает модуль зарядного устройства TP4056 для зарядки батареи. Принципиальная схема такого тахометра с лазером представлена далее:
Компоненты можно разместить на подобной плате:
В качестве датчика будем использовать лазер от модуля KY-008.
На лазере следует удалить резистор 33 Ом и запаять перемычку на его месте.
Собрав все на плате и разместив в соответствующем корпусе, не забудьте подключить аккумулятор.
В отличие от более ранних серий ATtiny, таких как ATtiny85, ATtiny1614 использует вывод RESET для программирования процессора. Для программирования нужен программатор UPDI. Можно сделать такой, используя Arduino Nano. Все компоненты толерантны к напряжению 5 В, поэтому при отсутствии батареи вы можете запитать их от программатора UPDI. Если батарея установлена, просто подключите провода GND и UPDI и оставьте провод VCC отключенным.
После настройки соединений в среде Arduino IDE выберите плату в меню «Инструменты».
Выберите плату ATtiny1614 в вашей среде IDE. Выберите COM-порт, к которому подключен Arduino Nano. Программатор должен быть установлен на jtag2updi (megaTinyCore). Откройте скетч и загрузите его в ATtiny1614.
#include <OneWire.h>
#include <U8g2lib.h>
#include <avr/sleep.h>
#define MEASURE_BTN 0 //PA4 D0
#define ON_BTN 1 //PA5 D1
#define SENSOR 9 //PA2 D9
#define LASER 10 //PA3 D10
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
volatile uint32_t pulseStartTime = 0;
volatile uint32_t pulseDuration = 0;
char textBuffer[20];
#define AVG_SIZE 128
#define AVG_MASK 0x7F
uint32_t runningTotal = 0;
uint16_t runningCount = 0;
uint8_t runningHead = 0;
uint8_t runningTail = 0;
uint32_t runningValues[AVG_SIZE];
#define PULSE_TIMEOUT 5000
#define UPDATE_TIME 300
uint32_t updateTimeout;
//-----------------------------------------------------------------------------
void setup()
{
//Serial.begin(9600);
pinMode(LASER, OUTPUT);
digitalWrite(LASER, LOW);
pinMode(SENSOR, INPUT);
attachInterrupt(digitalPinToInterrupt(SENSOR), SensorInterrupt, RISING);
pinMode(ON_BTN, INPUT_PULLUP);
pinMode(MEASURE_BTN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ON_BTN),SwitchInterrupt,CHANGE);
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
systemSleep();
}
//-----------------------------------------------------------------------------
void SwitchInterrupt()
{
}
//-----------------------------------------------------------------------------
void SensorInterrupt()
{
pulseDuration = micros() - pulseStartTime;
pulseStartTime = micros();
}
//--------------------------------------------------------------------
void showSplashScreen()
{
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_helvB12_tr);
u8g2.drawStr(20,14,"ATtiny1614");
u8g2.drawStr(15,29,"Laser Tacho");
u8g2.sendBuffer();
delay(3000);
strcpy(textBuffer, "Ready");
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_logisoso20_tr);
int width = u8g2.getStrWidth(textBuffer);
u8g2.drawStr((128-width)/2,26,textBuffer);
u8g2.sendBuffer();
}
//-----------------------------------------------------------------------------
void loop()
{
uint32_t started;
uint32_t duration;
uint32_t rpm;
while (digitalRead(ON_BTN) == HIGH)
{
if (digitalRead(MEASURE_BTN) == LOW)
{
digitalWrite(LASER, HIGH);
delay(10);
noInterrupts();
pulseDuration = 0;
pulseStartTime = micros();
interrupts();
runningTotal = 0;
runningCount = 0;
runningHead = 0;
runningTail = 0;
while (digitalRead(MEASURE_BTN) == LOW)
{
noInterrupts();
started = pulseStartTime;
duration = pulseDuration;
interrupts();
if (duration == 0 && (millis() - started) > PULSE_TIMEOUT)
{
rpm = 0;
}
else if (duration > 0)
{
rpm = (60000000 / duration);
if (runningCount == AVG_SIZE)
{
runningTotal = runningTotal - runningValues[runningTail];
runningTail = (runningTail + 1) & AVG_MASK;
runningCount--;
}
runningValues[runningHead] = rpm;
runningHead = (runningHead + 1) & AVG_MASK;
runningTotal = runningTotal + rpm;
runningCount++;
rpm = runningTotal / runningCount;
}
if (millis() > updateTimeout)
{
displayNumber(rpm);
updateTimeout = millis() + UPDATE_TIME;
}
}
digitalWrite(LASER, LOW);
}
delay(100);
}
while (digitalRead(ON_BTN) == LOW)
{
delay(100);
}
systemSleep();
}
//--------------------------------------------------------------------
void displayNumber(uint32_t number)
{
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_logisoso20_tr);
sprintf(textBuffer,"%5lu",number);
int twidth = u8g2.getStrWidth(textBuffer);
u8g2.setFont(u8g2_font_helvB12_tr);
strcpy(textBuffer," rpm ");
int rwidth = u8g2.getStrWidth(textBuffer);
int tx = max(128 - (twidth + rwidth), 0);
u8g2.drawStr(tx + twidth,30,textBuffer);
sprintf(textBuffer,"%5lu",number);
u8g2.setFont(u8g2_font_logisoso20_tr);
u8g2.drawStr(tx,30,textBuffer);
u8g2.sendBuffer();
}
//-----------------------------------------------------------------------------
void systemSleep()
{
interrupts();
u8g2.clearBuffer();
u8g2.sendBuffer();
u8g2.setPowerSave(true);
//cbi(ADCSRA,ADEN);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
sleep_disable();
//sbi(ADCSRA,ADEN);
u8g2.setPowerSave(false);
while (digitalRead(ON_BTN) == LOW)
{
delay(100);
}
showSplashScreen();
updateTimeout = 0;
}
Важно, чтобы поверхность, на которую вы направляете лазер, отражала свет обратно на датчик.
При измерении скорости дрели была приклеена к патрону белая отражающая бумага, чтобы датчик уловил изменение в контрасте с черным металлом.
© digitrode.ru