Как сделать вызов после приворота на вольт

Ждете весточки? Узнайте, как сделать вызов после приворота на вольт, чтобы заставить жертву скучать и прибежать к вам!

В этой статье мы разберёмся, как «призвать» (вызвать) функцию строго каждые 3750 мс на микроконтроллере AVR ATmega128/328, которые часто используют в проектах Arduino. Ниже приведены готовые решения, которые легко перенести и на «вольт» (вольтметр) с записью данных на SD-карту, и на любую другую задачу, где нужен точный таймер.

Почему не подойдёт простой delay

  • delay «замораживает» весь контроллер — в это время не работают ни Serial, ни SD.
  • При 3750 мс погрешность будет накапливаться, особенно если внутри loop есть ещё какие-то операции.

Аппаратный таймер Timer2 — 100 % точность без прерывания loop

Схема подключения

Для примера будем опрашивать кнопку (вместо геркона) на пине D2 (INT0), светодиод — на D13, а данные писать на SD через стандартный модуль SPI.

Калькуляция регистров

Частота кварца 16 МГц. Нужно получить прерывание каждые 3750 мс.

  1. Делитель 1024 → 16 000 000 / 1024 = 15 625 Гц;
  2. Кол-во тиков за 3,75 с: 15 625 × 3,75 = 58 593,75.
  3. Считаем от 0 до 58 593 (ближайшее целое) и сбрасываем.

Готовый скетч


#include <SD.h>
#include <SPI.h>

const uint8_t PIN_LED = 13;
const uint8_t PIN_BTN = 2;
const uint8_t CS_SD = 4;
volatile bool flag_3750ms = false; // «вызыватель» функции

// Таблица указателей на функции (если нужно выбирать разные режимы)
typedef void (*FuncPtr)(void);
FuncPtr callAfterRitual = readVoltageAndLog; // «приворот» на вольт

//-------------------------- Настройка Timer2 --------------------------
ISR(TIMER2_COMP_vect) {
 static uint16_t ticks = 0;
 if (++ticks >= 586) { // 586 × 64 мкс ≈ 37,5 мс → ×100 ≈ 3,75 с
 ticks = 0;
 flag_3750ms = true;
 }
}

void start3750msTimer {
 // Timer2: CTC, prescaler=1024
 TCCR2A = _BV(WGM21);

 TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);
 OCR2A = 249; // 249+1 = 250 тактов => 16 000 000 / 1024 / 250 = 62,5 Гц
 TIMSK |= _BV(OCIE2); // Разрешить прерывание совпадения
}
//--------------------------------------------------------------------
void setup {
 pinMode(PIN_LED, OUTPUT);
 pinMode(PIN_BTN, INPUT_PULLUP);
 Serial.begin(115200);
 SD.begin(CS_SD);
 start3750msTimer;
}

void loop {
 // 1. Работаем с Serial/SD без задержек
 if (Serial.available) handleSerial;

 // 2. «Вызов после приворота на вольт» каждые 3750 мс
 if (flag_3750ms) {
 flag_3750ms = false;
 callAfterRitual; // «магия» срабатывает точно по расписанию
 }
}

//----------------------- Замер напряжения и запись --------------------
void readVoltageAndLog {
 const float VREF = 5.0;
 uint16_t raw = analogRead(A0);
 float voltage = raw * VREF / 1024.0;
 Serial.print(F("Voltage = "));
 Serial.println(voltage, 2);

 File f = SD.open("volt.csv", FILE_WRITE);
 if (f) {
 f.print(millis);
 f.print(';');
 f.println(voltage, 2);
 f.close;
 }
 digitalWrite(PIN_LED, !digitalRead(PIN_LED)); // мигнем светодиодом
}
//--------------------------------------------------------------------

Альтернатива: millis-кадрирование (без прерываний)

Если вы предпочитаете «ардуино-стиль» без прямого доступа к регистрам, можно использовать стандартный millis:


uint32_t prev = 0;
const uint32_t PERIOD = 3750;

void loop {
 uint32_t now = millis;
 if (now ⸺ prev >= PERIOD) {
 prev = now;
 readVoltageAndLog;
 }

 // остальной код
}

Но на высоких скоростях и при большом количестве задач может появиться дрейф ±1-2 мс.

Советы по надежности

  • Всегда объявляйте функции-обработчики типом void f и помещайте до setup или в отдельный модуль.
  • Для SD-карты используйте SdFat вместо старой SD-библиотеки — быстрее и меньше прерывают таймеры.
  • Если точность критична, не используйте delay внутри обработчиков прерываний.
  • Для измерения напряжения с точностью до 0,01 В возьмите опорное напряжение 2,56 В и analogReference(INTERNAL).

С помощью аппаратного тайнера Timer2 мы «навели приворот» на микроконтроллер: теперь функция readVoltageAndLog вызывается каждые ровные 3750 мс, неважно, работает ли в это время Serial или идёт запись на SD. Такой приём пригодится и для «вольтметра», и для любых других периодических задач.

Приятной магии ваших 3,75 секунд!