Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Улучшение GTimer: "готов на старте" и экономия на "бульках" #55

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 51 additions & 29 deletions GyverTimer/GyverTimer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,85 @@
#include "GyverTimer.h"

// конструктор
GTimer::GTimer(timerType type, uint32_t interval) {
setInterval(interval); // запуск в режиме ИНТЕРВАЛА
_type = type; // установка типа
GTimer::GTimer(const timerType &type, const uint32_t &interval, const boolean &readyOnStart) {
flagWrite(GTBIT_TYPE, type); // установка типа
flagWrite(GTBIT_READYONSTART, readyOnStart); // установка флага на нулевой запуск
setInterval(interval); // запуск в режиме ИНТЕРВАЛА
}

// запуск в режиме интервала
void GTimer::setInterval(uint32_t interval) {
if (interval != 0) { // защита от ввода 0
_interval = interval; // установка
_mode = TIMER_INTERVAL; // режим "интервал"
start(); // сброс и запуск
} else stop(); // остановка, если время == 0
void GTimer::setInterval(const uint32_t &interval) {
setTimeout(interval); // задать интервал и запустить
flagWrite(GTBIT_MODE, TIMER_INTERVAL); // режим "интервал"
}

// запуск в режиме таймаута
void GTimer::setTimeout(uint32_t timeout) {
setInterval(timeout); // задать интервал и запустить
_mode = TIMER_TIMEOUT; // режим "таймаут"
void GTimer::setTimeout(const uint32_t &timeout) {
if (timeout != 0) { // защита от ввода 0
_interval = timeout; // установка
flagWrite(GTBIT_MODE, TIMER_TIMEOUT); // режим "таймаут"
start(); // сброс и запуск
} else stop(); // остановка, если время == 0
}
// изменить режим на интервал, установить значение флага нулевой готовности и запустить
void GTimer::setReadyOnStart(const bool &readyOnStart) {
flagWrite(GTBIT_READYONSTART, readyOnStart); // выставляем флаг нулевой готовности
flagWrite(GTBIT_MODE, TIMER_INTERVAL); // режим "интервал"
start(); // сброс и запуск
}

// возвращает значение интервала/таймаута
uint32_t GTimer::getInterval() {
return _interval;
}

// остановить счёт
void GTimer::stop() {
_state = false;
_resumeBuffer = ( (_type) ? millis() : micros() ) - _timer; // запомнили "время остановки"
// TODO: здесь должна быть защита от остановки остановленного таймера
// иначе в буфере будет только время последнего вызова stop()
flagClear(GTBIT_STATE);
_resumeBuffer = ( (flagRead(GTBIT_TYPE)) ? millis() : micros() ) - _timer; // запомнили "время остановки"
}

// продолжить счёт
void GTimer::resume() {
if (flagRead(GTBIT_STATE)) return; // защита от вызова, если таймер не остановлен
start();
_timer -= _resumeBuffer; // восстановили время остановки
_timer -= _resumeBuffer; // восстановили время остановки
}

// перезапустить счёт
void GTimer::start() {
_state = true;
flagSet(GTBIT_STATE);
flagWrite(GTBIT_JUSTSTARTED, flagRead(GTBIT_READYONSTART)); // поднимаем флаг нулевой готовности
reset();
}

// сброс периода
void GTimer::reset() {
_timer = (_type) ? millis() : micros();
_timer = flagRead(GTBIT_TYPE) ? millis() : micros();
}

// состояние
boolean GTimer::isEnabled() {
return _state;
return flagRead(GTBIT_STATE);
}

// проверка таймера v2.0 (соблюдение интервалов, защита от пропуска и переполнения)
boolean GTimer::isReady() {
if (!_state) return false; // если таймер остановлен
uint32_t thisTime = (_type) ? millis() : micros(); // текущее время
if (thisTime - _timer >= _interval) { // проверка времени
if (_mode) { // если режим интервалов
if (!flagRead(GTBIT_STATE)) return false; // если таймер остановлен
if (flagRead(GTBIT_JUSTSTARTED) && flagRead(GTBIT_MODE)) { // если режим интервалов и включен нулевой запуск
flagWrite(GTBIT_JUSTSTARTED, 0);
return true;
}
uint32_t thisTime = (flagRead(GTBIT_TYPE)) ? millis() : micros(); // текущее время
if (thisTime - _timer >= _interval) { // проверка времени
if (flagRead(GTBIT_MODE)) { // если режим интервалов
do {
_timer += _interval;
if (_timer < _interval) break; // защита от переполнения uint32_t
} while (_timer < thisTime - _interval); // защита от пропуска шага
} else { // если режим таймаута
_state = false; // остановить таймер
if (_timer < _interval) break; // защита от переполнения uint32_t
} while (_timer < thisTime - _interval); // защита от пропуска шага
} else { // если режим таймаута
flagClear(GTBIT_STATE); // остановить таймер
}
return true;
} else {
Expand All @@ -70,10 +89,13 @@ boolean GTimer::isReady() {
}

// сменить режим вручную
void GTimer::setMode(boolean mode) {
_mode = mode;
void GTimer::setMode(const boolean &mode) {
flagWrite(GTBIT_MODE, mode);
}




// ================== УСТАРЕЛО (но работает, для совместимости) ===================

// ====== millis ======
Expand Down
59 changes: 41 additions & 18 deletions GyverTimer/GyverTimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

/*
GTimer - полноценный таймер на базе системных millis() / micros()
Документация: https://alexgyver.ru/gyvertimer/
- Миллисекундный и микросекундный таймер
- Два режима работы:
- Режим интервала: таймер "срабатывает" каждый заданный интервал времени
Expand All @@ -30,6 +29,10 @@
- 3.2
- Добавлен isEnabled
- Возможность не запускать таймер при создании
- 3.3
- Добавлен режим "сразу готов" (isReady == true сразу после запуска)
- Добавлен getInterval()
- Экономия памяти за счет перехода с boolean на биты
*/

enum timerType {
Expand All @@ -40,32 +43,52 @@ enum timerType {
// ============== GTimer (микросекундный и миллисекундный таймер) ================
class GTimer {
public:
GTimer(timerType type = MS, uint32_t interval = 0); // объявление таймера с указанием типа и интервала (таймер не запущен, если не указывать)
void setInterval(uint32_t interval); // установка интервала работы таймера (также запустит и сбросит таймер) - режим интервала
void setTimeout(uint32_t timeout); // установка таймаута работы таймера (также запустит и сбросит таймер) - режим таймаута
boolean isReady(); // возвращает true, когда пришло время
boolean isEnabled(); // вернуть состояние таймера (остановлен/запущен)
void reset(); // сброс таймера на установленный период работы
void start(); // запустить/перезапустить (со сбросом счёта)
void stop(); // остановить таймер (без сброса счёта)
void resume(); // продолжить (без сброса счёта)
GTimer(const timerType &type = MS, const uint32_t &interval = 0, const boolean &readyOnStart = false); // объявление таймера с указанием типа и интервала (таймер не запущен, если не указывать)
void setInterval(const uint32_t &interval); // установка интервала работы таймера (также запустит и сбросит таймер) - режим интервала
void setTimeout(const uint32_t &timeout); // установка таймаута работы таймера (также запустит и сбросит таймер) - режим таймаута
void setReadyOnStart(const boolean &readyOnStart); // установка нулевой готовности таймера (поменяет на ИНТЕРВАЛ, запустит и сбросит таймер)
uint32_t getInterval(); // возвращает значение текущего интервала/таймаунта
boolean isReady(); // возвращает true, когда пришло время
boolean isEnabled(); // вернуть состояние таймера (остановлен/запущен)
void reset(); // сброс таймера на установленный период работы
void start(); // запустить/перезапустить (со сбросом счёта)
void stop(); // остановить таймер (без сброса счёта)
void resume(); // продолжить (без сброса счёта)

// служебное
void setMode(boolean mode); // установка режима работы вручную: AUTO или MANUAL (TIMER_INTERVAL / TIMER_TIMEOUT)
void setMode(const boolean &mode); // установка режима работы вручную: AUTO или MANUAL (TIMER_INTERVAL / TIMER_TIMEOUT)

private:
uint32_t _timer = 0;
uint32_t _interval = 0;
uint32_t _resumeBuffer = 0;
boolean _mode = true;
boolean _state = false;
boolean _type = true;
// boolean _mode = true; // AUTO или MANUAL
// boolean _state = false; // запущен или остановлен
// boolean _type = true; // MS или US
// boolean _readyOnStart = false; // будет ли "готов" сразу же после запуска или нет
// boolean _justStarted = false; // триггер на "готовность" сразу после старта
byte flags = 0b00010100; // один байт для всех "булек". биты начиная со старшего отвечают за (X - не используется):
// значения: [X X X _justStarted _readyOnStart _type _state _mode>]
// индексы: [7 6 5 4 3 2 1 0 >]
};

#define MANUAL 0
#define AUTO 1
#define TIMER_TIMEOUT 0
#define TIMER_INTERVAL 1
#define MANUAL 0
#define AUTO 1
#define TIMER_TIMEOUT 0
#define TIMER_INTERVAL 1

#define GTBIT_MODE 0
#define GTBIT_STATE 1
#define GTBIT_TYPE 2
#define GTBIT_READYONSTART 3
#define GTBIT_JUSTSTARTED 4

// Макросы для работы с флагами
#define flagRead(flag) bitRead(flags, flag) // получить значение флага
#define flagSet(flag) bitSet(flags, flag) // установить флаг в 1
#define flagClear(flag) bitClear(flags, flag) // установить флаг в 0
#define flagWrite(flag, val) bitWrite(flags, flag, val) // установить флаг в значение val



// ================================================================================
Expand Down
2 changes: 2 additions & 0 deletions GyverTimer/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ GyverTimer KEYWORD1

setInterval KEYWORD2
setTimeout KEYWORD2
setReadyOnStart KEYWORD2
getInterval KEYWORD2
isReady KEYWORD2
isEnabled KEYWORD2
reset KEYWORD2
Expand Down
2 changes: 1 addition & 1 deletion GyverTimer/library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=GyverTimer
version=3.2
version=3.3
author=AlexGyver <[email protected]>
maintainer=AlexGyver <[email protected]>
sentence=Simple timer with period/timeout modes on millis.
Expand Down