ESP8266 jak zliczać impulsy zewnętrzne timerem program w arduino
Na ESP8266 w środowisku Arduino nie zlicza się impulsów zewnętrznych „timerem” w sensie sprzętowego licznika taktowanego z pinu, jak np. w niektórych AVR. Oficjalnie ESP8266 nie ma sprzętowego modułu licznika impulsów (PCNT), więc poprawne rozwiązanie to: przerwanie GPIO do inkrementacji licznika oraz millis() / timer programowy do wyznaczania okna pomiarowego, np. 1 s. (docs.espressif.com)
Najprostszy i praktycznie poprawny program w Arduino wygląda tak:
#include <Arduino.h>
const uint8_t PULSE_PIN = 14; // GPIO14, dostosuj do swojej płytki
const uint32_t OKNO_POMIARU_MS = 1000; // 1 s
volatile uint32_t licznikImpulsow = 0;
volatile uint32_t ostatniImpulsUs = 0;
// Ustaw 0 dla czystego sygnału cyfrowego.
// Dla kontaktronu / przycisku możesz dać np. 3000...10000 us.
const uint32_t MIN_ODSTEP_US = 0;
void IRAM_ATTR onPulse()
{
uint32_t teraz = micros();
if (MIN_ODSTEP_US == 0 || (uint32_t)(teraz - ostatniImpulsUs) >= MIN_ODSTEP_US) {
licznikImpulsow++;
ostatniImpulsUs = teraz;
}
}
void setup()
{
Serial.begin(115200);
// Jeśli źródło jest typu otwarty kolektor / kontaktron, INPUT_PULLUP ma sens.
// Dla aktywnego drivera push-pull można użyć INPUT.
pinMode(PULSE_PIN, INPUT_PULLUP);
// Wybierz odpowiednie zbocze: RISING albo FALLING.
attachInterrupt(digitalPinToInterrupt(PULSE_PIN), onPulse, FALLING);
Serial.println();
Serial.println("Start licznika impulsow ESP8266");
}
void loop()
{
static uint32_t lastMs = 0;
uint32_t nowMs = millis();
if ((uint32_t)(nowMs - lastMs) >= OKNO_POMIARU_MS) {
lastMs += OKNO_POMIARU_MS;
noInterrupts();
uint32_t impulsy = licznikImpulsow;
licznikImpulsow = 0;
interrupts();
Serial.print("Impulsy w 1 s: ");
Serial.print(impulsy);
Serial.print(" | f = ");
Serial.print(impulsy);
Serial.println(" Hz");
}
delay(1); // daje czas stosowi Wi-Fi / systemowi
}
To rozwiązanie jest zgodne z tym, jak ESP8266 jest przewidziany do takich zadań: ISR na GPIO ma być bardzo krótki, umieszczony w IRAM, a cięższe operacje robi się poza przerwaniem. (arduino-esp8266.readthedocs.io)
Najważniejszy punkt techniczny jest taki, że w ESP8266 nie ma sprzętowego PCNT, więc jedna z przykładowych odpowiedzi sugerująca „sprzętowe zliczanie TIMER0 z GPIO0” nie odpowiada standardowemu, oficjalnie opisanemu modelowi pracy tego układu w Arduino. Dokumentacja Espressif podaje wprost, że ESP8266 nie zawiera hardware pulse counter i obsługuje zliczanie impulsów przez przerwania od zbocza GPIO. Z kolei oficjalny opis hardware timera pokazuje timer jako peryferium do odmierzania czasu, ładowania wartości, odczytu licznika i generacji przerwania, a nie jako licznik impulsów zewnętrznych z pinu. To prowadzi do jednoznacznego wniosku inżynierskiego: w Arduino na ESP8266 należy liczyć impulsy w ISR od GPIO, a „timer” używać wyłącznie do bramkowania czasowego pomiaru. (docs.espressif.com)
Architektura rozwiązania jest więc następująca:
attachInterrupt() wywołuje ISR na zboczu RISING albo FALLING,To jest najlepszy kompromis dla ESP8266, ponieważ:
Polling, czyli ciągłe sprawdzanie stanu wejścia w loop(), na ESP8266 jest słabszym rozwiązaniem, bo rdzeń i stos Wi‑Fi potrzebują czasu procesora. Dokumentacja Arduino core podkreśla, że delay()/yield() są potrzebne, aby systemowe zadania mogły się wykonywać. W praktyce oznacza to, że przy polling’u możesz łatwo zgubić krótkie impulsy. Dlatego do zliczania impulsów lepsze są przerwania GPIO. (arduino-esp8266.readthedocs.io)
W ESP8266 procedura przerwania:
delay() ani yield(),String. (arduino-esp8266.readthedocs.io)Dlatego poprawny ISR wygląda praktycznie tak:
void IRAM_ATTR onPulse() {
licznikImpulsow++;
}
albo, jeśli potrzebujesz prostego antydrgania:
void IRAM_ATTR onPulse() {
uint32_t teraz = micros();
if (teraz - ostatniImpulsUs > 5000) {
licznikImpulsow++;
ostatniImpulsUs = teraz;
}
}
To drugie podejście ma sens dla kontaktronu, przycisku, przekaźnika, gdzie jedno fizyczne zdarzenie może generować kilka szybkich przejść logicznych. Sam debounce programowy działa, ale z punktu widzenia elektroniki lepsza jest filtracja sprzętowa: rezystor podciągający, filtr RC i ewentualnie bramka Schmitta. To daje czystsze zbocze i zmniejsza ryzyko fałszywych zliczeń. Wniosek o potrzebie filtracji jest klasyczną praktyką projektową dla wejść impulsowych. (arduino-esp8266.readthedocs.io)
Najczęściej wybierasz jedno z dwóch:
RISING — liczysz zbocza narastające,FALLING — liczysz zbocza opadające.Tryb CHANGE liczy każdą zmianę stanu, więc dla prostokąta dostaniesz zwykle 2 zliczenia na okres. Jeśli użytkownik oczekuje częstotliwości sygnału, a nie liczby wszystkich przejść, to CHANGE zwykle nie jest właściwym wyborem. Obsługiwane typy przerwań na ESP8266 to CHANGE, RISING, FALLING. (arduino-esp8266.readthedocs.io)
Mimo że ESP8266 jest układem 32-bitowym, praktyka poprawnego programu wymaga krótkiej sekcji krytycznej przy kopiowaniu i zerowaniu wspólnej zmiennej volatile. Typowy wzorzec:
noInterrupts();
uint32_t impulsy = licznikImpulsow;
licznikImpulsow = 0;
interrupts();
To zapobiega sytuacji, w której ISR zwiększy licznik dokładnie podczas odczytu lub zerowania. Krótkie zablokowanie przerwań na kilka instrukcji jest akceptowalne; problemem byłyby dopiero długie sekcje krytyczne, bo dokumentacja ostrzega, że długie blokowanie obsługi przerwań destabilizuje system. (arduino-esp8266.readthedocs.io)
Oficjalna dokumentacja Arduino core podaje, że przerwania pinowe działają na dowolnym GPIO poza GPIO16. Jeśli więc wybierasz pin do licznika, nie używaj GPIO16 do attachInterrupt(). (arduino-esp8266.readthedocs.io)
Dodatkowo dokumentacja przypomina, że niektóre piny pełnią inne funkcje, np. UART. Jeżeli używasz jednocześnie Serial, to warto nie mieszać wejścia impulsowego z pinami intensywnie wykorzystywanymi przez interfejs szeregowy. UART0 używa GPIO1/GPIO3, a UART1 TX jest na GPIO2. (arduino-esp8266.readthedocs.io)
Aktualna dokumentacja Espressif mówi wprost:
To bardzo ważne praktycznie. Jeżeli mierzysz:
to ESP8266 zwykle wystarczy. Jeśli jednak mówimy o szybkich impulsach, krytycznej dokładności albo pracy równoległej z aktywnym Wi‑Fi, to nowoczesnym kierunkiem jest ESP32 z PCNT. Oficjalna dokumentacja rodziny ESP32 zawiera już osobny blok peryferyjny Pulse Counter (PCNT). (docs.espressif.com)
W pytaniu pojawia się słowo „timer”, ale na ESP8266 należy rozdzielić dwa pojęcia:
Zatem poprawny model mentalny jest taki:
millis() = jak długo liczyć. (docs.espressif.com)IRAM_ATTR a ICACHE_RAM_ATTRW nowszych wersjach Arduino core dla ESP8266 stosuje się IRAM_ATTR. W starszych wersjach dokumentacja pokazywała ICACHE_RAM_ATTR. Jeżeli ktoś kompiluje stary projekt lub używa starej wersji core, może spotkać oba zapisy. (arduino-esp8266.readthedocs.io)
volatileZmienne współdzielone między ISR a loop() muszą być oznaczone jako volatile, inaczej kompilator może zoptymalizować dostęp w sposób niezgodny z oczekiwanym działaniem programu. To jest standardowa zasada dla programowania współbieżnego na mikrokontrolerach; w tym przypadku wynika bezpośrednio z użycia ISR. (arduino-esp8266.readthedocs.io)
W samym zliczaniu impulsów nie ma szczególnych problemów etycznych, ale są istotne kwestie bezpieczeństwa technicznego:
To są wymagania wynikające z dobrej praktyki inżynierskiej i bezpieczeństwa układów niskonapięciowych.
RISING albo FALLING, bez debounce.INPUT_PULLUP i licz zbocze FALLING.INPUT.Serial.print() w ISR.String w ISR.Jeżeli chcesz dodatkowo mierzyć okres między impulsami, możesz w ISR zapamiętywać micros() i różnicę czasów, ale tylko wtedy, gdy sygnał nie jest zbyt szybki. Dla szybkich przebiegów lepiej zostać przy zliczaniu w stałym oknie czasowym.
Najważniejsze zastrzeżenie: przy włączonym Wi‑Fi producent wprost ostrzega, że na ESP8266 mogą wystąpić straty zliczeń z powodu priorytetów systemowych. Jeżeli więc licznik ma mieć charakter rozliczeniowy albo ma nie gubić impulsów w żadnych warunkach, ESP8266 nie jest najlepszym wyborem. (docs.espressif.com)
Druga uwaga: sugestia o wykorzystaniu „sprzętowego licznika TIMER0 na GPIO0” nie wynika z oficjalnej, aktualnej ścieżki dokumentacyjnej Arduino-ESP8266/Espressif. W praktyce, jeśli zależy Ci na rozwiązaniu stabilnym i przenośnym, trzymaj się GPIO interrupt + okno czasowe. (docs.espressif.com)
Jeżeli chcesz rozwinąć projekt, kolejne sensowne kroki to:
To ostatnie jest już rekomendacją inżynierską: skoro ESP8266 nie ma PCNT i może gubić zliczenia przy Wi‑Fi, zewnętrzny licznik lub migracja na ESP32 są naturalnym kierunkiem projektu. (docs.espressif.com)
Najkrócej: na ESP8266 nie liczysz impulsów zewnętrznych timerem sprzętowym z pinu, tylko przerwaniem GPIO. Timer lub millis() służy tylko do wyznaczenia czasu pomiaru. ISR ma być krótki, z IRAM_ATTR, bez Serial, bez delay(), a wynik odczytujesz i zerujesz w loop(). Przy większych wymaganiach dokładności lub przy aktywnym Wi‑Fi rozważ ESP32 z PCNT. (docs.espressif.com)
Jeżeli chcesz, mogę w następnym kroku przygotować Ci: