-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathTimerList.h
More file actions
304 lines (221 loc) · 11.9 KB
/
TimerList.h
File metadata and controls
304 lines (221 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#pragma once
#include <Arduino.h>
//#include <Consts.h>
using bsize_t = uint8_t; // size_t размером 1 байт
using THandle = int8_t;
static const int8_t INVALID_HANDLE = -1;
#ifdef __AVR_ATmega2560__
#define MAXTIMERSCOUNT 16 // Максимальное число зарегистрированных таймеров для Mega2560 - 16
#define TIMER0_ONE_MS 247
#endif
#ifdef __AVR_ATmega8__
#define MAXTIMERSCOUNT 8
#define _1MSCONST F_CPU/8/1000 // задержка для 1 миллисекунды 12Мгц - 1500, 16Мгц - 2000 минус накладные расходы
// между прерываниями 16 Мгц процессор выполнит примерно 10000 команд
#endif
#ifdef __AVR_ATmega328P__
#define MAXTIMERSCOUNT 10 // Максимальное число зарегистрированных таймеров для прочих Uno - 10
#define TIMER0_ONE_MS 245
#endif
#ifdef __AVR_ATmega32__
#define MAXTIMERSCOUNT 10 // Максимальное число зарегистрированных таймеров для прочих Uno - 10
#define TIMER0_ONE_MS 245
// между прерываниями 16 Мгц процессор выполнит примерно 10000 команд
#endif //
#ifdef __AVR_ATmega32U4__
#define MAXTIMERSCOUNT 10 // Максимальное число зарегистрированных таймеров для Lяnardo - 10
#define TIMER0_ONE_MS 245
#endif
#ifdef __AVR_ATmega168__
#define MAXTIMERSCOUNT 8 // Максимальное число зарегистрированных таймеров для прочих Old Uno - 8
#define TIMER0_ONE_MS 240
#endif
using pvfCallback = void(*)(void); // тип pointer to void function Callback
class TCounterDown; // опережающее обявление класса TCounterDown
using PCounterDown = TCounterDown *; // и указателя на нее
class TCounterDown {
protected:
bool fActive; // 1 byte true - если счетчик считает, false, если остановлен
uint32_t fWorkCount; // 4 bytes Рабочий счетчик, уменьшается на 1 каждый Tick()
uint32_t fInitCount; // 4 bytes Начальный счетчик, хранит значение от которого считать до 0
pvfCallback fCallback; // 2 bytes Адрес функции, которая вызовется, когда счётчик досчитает до 0
// sizeof(TCounterDown) = 11 bytes;
// гарантированно очистить (заполнить нулями) все поля структуры
void Clear(void) { memset(this, 0, sizeof(TCounterDown)); };
// пустой конструктор, не разрешен
TCounterDown() {};
public:
// конструктор. Принимает первоначальное значение, которое потом будет уменьшаться при каждом вызове
// Tick() и указатель на void функцию обратного вызова.
TCounterDown(uint32_t aTimeMS, pvfCallback aCallback) {
Clear(); //очистить все поля
fInitCount = fWorkCount = aTimeMS; // задать начальные значения
fCallback = aCallback;
fActive = true; // и сразу разрешить запуск
}
// остановить счетчик (поставить на паузу)
inline void Stop(void) {
fActive = false;
};
// продолжить считать с того же места, где остановили
inline void Start(void) {
fActive = true;
};
// сбросить счетчик, считать сначала
inline void Reset(void) {
Stop();
fWorkCount = fInitCount;
Start();
};
// отдает true - если счетчик запущен и считает, и false, если остановлен
inline bool isActive(void) const {
return fActive;
};
// отдает true - если функция Callback не назначена
inline bool isEmpty(void) const {
return (fCallback == NULL);
};
// установить новый интервал для счета
void setInterval(uint32_t anewinterval) {
Stop();
fInitCount = fWorkCount = anewinterval;
Start();
};
// Оператор постдекремента. Уменьшает рабочий счетчик на 1 за вызов, если он не остановлен.
// Если счетчик дошел до 0, и назначена функция Callback, то она вызывается
//
TCounterDown &operator --(int) {
uint8_t sreg = SREG; // на всякий случай запомним состояние прерываний
cli(); // запрещаем прерывания
if ((isActive()) && (!isEmpty()) && ((--fWorkCount) == 0)) { // если счетчик досчитал до 0
fWorkCount = fInitCount; // перезагружаем рабочий счётчик начальным значением, чтобы считать заново
sei(); // разрешаем прерывания
fCallback(); // и вызываем нашу фунцию обратного вызова
}
SREG = sreg; // восстанавливаем прерывания
return *this;
}
// то же самое в виде функции, а не оператора
void Tick(void) { (*this)--; }; // просто вызываем оператор постдекремента
uint32_t getCount() const { return fWorkCount; };
};
class TTimerList; // опережающее обьявление класса
using PTimerList = TTimerList *; // и указателя на него
extern TTimerList TimerList; // глобальная переменная нашего списка счетчиков
class TTimerList {
protected:
bool factive; // Активность всего списка, если false - все таймеры остановлены
bsize_t fcount; // Количество добавленных таймеров
PCounterDown Items[MAXTIMERSCOUNT]; // массив указателей на счетчики TCounterDown
// отдает true если THandle счётчика правильный [0..fsize-1]
// и сам счетчик не удален
bool isValid(THandle ahandle) {
return ((ahandle >= 0) && (ahandle < MAXTIMERSCOUNT)) && (Items[ahandle] != NULL);
}
// начальная инициализация аппаратного таймера
// реализация в cpp файле для разных камней разная
void Init(void);
public:
// создает пустой список размера asize, для хранения счетчиков
// при создании забивает список NULL-ами
TTimerList() {
for (bsize_t i = 0; i < MAXTIMERSCOUNT; i++)
Items[i] = NULL; // забиваем выделенную память нулями
factive = false; // пока ни один счетчик не добавлен, список неактивен
fcount = 0; // и число счётчиков пока == 0
};
/* for oversmart boys: uncomment this
~TTimerList() {
for (bsize_t i = 0; i < fsize; i++) delete Items[i];
}
*/
// добавить счетчик в список
// ainterval задается в миллисекундах
// acallback - адрес функции, которая вызывается, когда счетчик досчитает до 0
//
// возвращает Handle щёччика, или специальное значение INVALID_HANDLE, если
// добавить не удалось
THandle Add(uint32_t ainterval, pvfCallback acallback, const bool AStopped = false) {
if (fcount == MAXTIMERSCOUNT) return INVALID_HANDLE; // список заполнен до отказа, мест нет
PCounterDown counter = new TCounterDown(ainterval, acallback); // попытаемся хапнуть память под щёччик
if (counter == NULL) return INVALID_HANDLE; // не удалось, уходим с ошибкой
if (AStopped) counter->Stop();
if (fcount == 0) { // если добавляемый счетчик - первый,
Init(); // то сначала инициализируем аппаратный таймер
Start(); // и разрешаем запустить весь этот цыкал перебора
}
for (THandle i = 0; i < MAXTIMERSCOUNT; i++) {
if (Items[i] == NULL) { // ищем свободную "дырку" в массиве Items
Items[i] = counter; // если нашли - пхаем туда созданный щёччик
fcount++; // кол-во счетчиков увеличиваем на 1
return i; // возвращаем индекс добавленного счётчика как его Handle
}
}
return INVALID_HANDLE; // ну, а если места не нашлось - извините.
}
THandle AddSeconds(uint32_t aSeconds, pvfCallback aCallback, const bool AStopped = false) {
return Add(1000UL * aSeconds, aCallback, AStopped);
}
THandle AddMinutes(const uint16_t AMinutes, const pvfCallback ACallBack, const bool AStopped = false) {
return Add(1000UL * 60UL * AMinutes, ACallBack, AStopped);
}
// запустить цикл перебора счетчиков
inline void Start(void) {
factive = true;
};
// остановить цикл перебора счётчиков
inline void Stop(void) {
factive = false;
};
// отдает true - если цикл перебора запущен
inline bool isActive(void) const {
return factive;
};
// функции для работы с конкретным счётчиком в списке, по его Handle
// запустить счётчик с номером hnd
void Start(THandle hnd) {
if (isValid(hnd)) Items[hnd]->Start();
}
// остановить счётчик hnd
void Stop(THandle hnd) {
if (isValid(hnd)) Items[hnd]->Stop();
}
// Перезапустить счётчик hnd сначала
void Reset(THandle hnd) {
if (isValid(hnd)) Items[hnd]->Reset();
}
// Установить счетчику hnd новый интервал счёта
void setNewInterval(THandle hnd, uint32_t anewinterval) {
if (isValid(hnd)) Items[hnd]->setInterval(anewinterval);
}
// проверить, активен ли счётчик hnd
bool isActive(THandle hnd) {
if (isValid(hnd)) return Items[hnd]->isActive();
return false;
}
// остановить и удалить счётчик hnd из массива
void Delete(THandle hnd) {
if (!isValid(hnd)) return;
PCounterDown cntptr = Items[hnd];
if (cntptr != NULL) cntptr->Stop();
delete(cntptr);
Items[hnd] = NULL;
if (--fcount == 0) Stop(); // Если счётчиков не осталось - остановить цикл перебора
}
// отдает количество добавленных в список щёччиков
bsize_t getCount() const {
return fcount;
};
// сюда приходят тики от аппаратного таймера. Если список активен, запускается цикл
// перебора счетчиков в списке. На каждом тике каждый добавленный счётчик уменьшается на 1
void Tick(void) {
if (isActive()) {
for (bsize_t i = 0; i < MAXTIMERSCOUNT; i++)
if (Items[i] != NULL) (*Items[i])--;
}
}
uint32_t getCount(THandle hnd) {
if (isValid(hnd)) return Items[hnd]->getCount();
return 0;
}
};