-
Notifications
You must be signed in to change notification settings - Fork 1
/
RTOS_macro.inc
672 lines (491 loc) · 30 KB
/
RTOS_macro.inc
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
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
.IFNDEF _RTOSMACRO__INCLUDED_
.EQU _RTOSMACRO__INCLUDED_ = 1
.MESSAGE "Note: <RTOS-macro.inc> have included, only once!"
.include "macrobaselib.inc" ; Библиотека базовых Макроопределений (требуется)
;=== BEGIN "RTOS-macro.inc" ================================================
; Макросы, реализующие прикладной программный интерфейс RTOS (API)
; (Назначение: здесь всё, что вам может понадобиться, от RTOS, в прикладном коде проекта.)
;***************************************************************************
;*
;* Глобальные константы и Псевдонимы регистров для RTOS
;*
;***************************************************************************
; Директивами условной компиляции можно отключать отдельные возможности RTOS, уменьшая размер ядра (оптимизация под простые задачи и слабые МК):
.EQU RTOS_FEATURE_PLANTASK = 1 ; поддержка Базовой диспетчеризации задач (последовательный запуск, в порядке очереди)
.EQU RTOS_FEATURE_PLANTIMER = 1 ; поддержка Диспетчеризации Задач по Таймеру (безусловно отложенный запуск на <X> миллисекунд)
.EQU RTOS_FEATURE_PLANWAITER = 1 ; поддержка Флаговой автоматизации (диспетчеризация Задач по состоянию бита в памяти РОН, Управляющем Регистре/Порте, или в ячейке SRAM: запуск задачи, если/как-только бит в байте установлен? или если/как-только бит в байте снят?)
;.EQU RTOS_OPTIMIZE_FOR_SMALL_MEMORY = 1 ; используйте это только для самых младших МК! (отключить в системных методах RTOS защиту регистров стеком, отключить некритичные проверки, выкинуть второстепенные методы из ядра, отключить всё что только можно - чтобы минимизировать объём кода RTOS, и главное, минимизировать используемый/требуемый объём ОЗУ под Стек)
;
; Важно: Для работы Операционной Системы RTOS (в режиме со включенной оптимизацией RTOS_OPTIMIZE_FOR_SMALL_MEMORY) - в Стеке требуется гарантированно оставить 8 (восемь) байт!
; (При этом гарантируется: Диспетчер сможет запускать прикладные Задачи... В любой момент, сможет отработать обработчик прерывания "Таймерной Службы"... И, из кода Задач, можно RCALL-вызывать Подпрограммы одного уровня вложенности, в т.ч. использовать PLAN_TASK-методы...)
; (Для пояснения: см. комментарий к "Службе таймеров RTOS", ниже...)
;
; И если, в прикладном коде Задач, вы используете вызовы Подпрограмм (RCALL) более одного уровня вложенности - обязательно просчитайте достаточную глубину Стека (как минимум, для адресов возврата; и для PUSH/POP инструкций, если таковые используются в коде)!
; Напомню: Ещё требуется выделить SRAM под "Очередь Задач", "Пулы Таймеров" и "Выжидателей" (настраиваются в <RTOS_data.inc>).
; Оставшееся ОЗУ - можно использовать для прикладных данных...
;
; Например: на МК ATtiny10 (при 32 байтах SRAM), можно использовать RTOS:
; с одним Таймером (-3байта)
; и одним Флаговым Автоматом (-3байта),
; и очередь глубиной 5шт. Задач (-5байт),
; не забыть про Счётчик "таймерной службы" (-1байт);
; ещё (-8 байт) на Стек для RTOS;
; остаётся 12 байт SRAM на прикладную логику (10 на данные + 2 про запас). Profit!
; /Замечу: при этом, в сегменте кода система будет занимать 350-450 байт - остальное на прикладную логику./
; Смотри также: константы Настройки Скорости - в макросах инициализации RTOS_INIT, USART_INIT... (их невозможно настроить автоматически, нужно править вручную)
;---------------------------------------------------------------------------
; Временные переменные, используемые в ядре RTOS:
;.def temp = R16 ; (определено в macrobaselib.inc)
;.def temp1 = R16 ; (определено в macrobaselib.inc)
;.def temp2 = R17 ; (определено в macrobaselib.inc)
;.def temp3 = R18 ; (определено в macrobaselib.inc)
;.def temp4 = R19 ; (определено в macrobaselib.inc)
; Определение параметровых регистров для подпрограмм-методов ядра RTOS:
; (указание: не используйте здесь "временные регистры" - нельзя мультиплексировать их с "параметровыми регистрами"!)
.def TaskCode = R25 ; Номер-индекс Задачи в таблице RTOS_TaskProcs
.def PoolParameter2byte = R24 ; 2-ой байт элемента Пула (описание формата см. в RTOS_data.inc) /используется для настройки Таймера и Флагового Автомата/
.def PoolParameter3byte = R23 ; 3-ий байт элемента Пула (описание формата см. в RTOS_data.inc) /используется для настройки Таймера и Флагового Автомата/
.def ErrorCode = R24 ; Код системной ошибки /передаётся в RTOS_METHOD_ErrorHandler/
;***************************************************************************
;*
;* Методы постановки Задач в очередь
;*
;***************************************************************************
.ifdef RTOS_FEATURE_PLANTASK
;---------------------------------------------------------------------------
; Базовая диспетчеризация задач (последовательный запуск, в порядке очереди)
; Добавить Задачу в очередь RTOS_TaskQueue, послать на выполнение
; Памятка: под параметры и временные переменные - портит содержимое всех регистров, упомянутых в секции "Subroutine Register Variables", соответствующего системного метода, и в зависимости от текущего режима оптимизации...
; Пример вызова: PLAN_TASK tasknumber
.MACRO PLAN_TASK
.if @0==$00
.ERROR "system <Task_Idle> is forbidden to use!"
.endif
LDI TaskCode, @0 ; Заполняем параметры
RCALL RTOS_METHOD_AddTask ; Вызываем системный метод
.ENDM
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
; Добавить Задачу в очередь RTOS_TaskQueue, послать на выполнение (БЕЗОПАСНАЯ ВЕРСИЯ)
; Памятка: данный макрос не портит содержимое РОН (даже для передачи параметров) - поскольку защищает используемые регистры Стеком.
; Пример вызова: SAFE_PLAN_TASK tasknumber
.MACRO SAFE_PLAN_TASK
PUSH TaskCode ; Здесь, сберегаем только параметровые регистры!
PLAN_TASK @0
POP TaskCode
.ENDM
.endif
.endif //RTOS_FEATURE_PLANTASK
.ifdef RTOS_FEATURE_PLANTIMER
;---------------------------------------------------------------------------
; Диспетчеризация Задач по Таймеру (безусловно отложенный запуск на <X> миллисекунд)
; Установить (добавить или обновить) Таймер в пуле RTOS_TimersPool, для отложенного запуска Задачи
; Памятка: под параметры и временные переменные - портит содержимое всех регистров, упомянутых в секции "Subroutine Register Variables", соответствующего системного метода, и в зависимости от текущего режима оптимизации...
; Пример вызова: PLAN_TIMER tasknumber,delay
.MACRO PLAN_TIMER
.if @0==$00
.ERROR "system <Task_Idle> is forbidden to use!"
.endif
.if @1==0
.ERROR "0ms time delay is forbidden for <RTOS_METHOD_AddTimer> method!"
.endif
LDI TaskCode, @0 ; Заполняем параметры
LDI PoolParameter2byte, Low (@1 -1) ; примечание: здесь, коррекция (-1) вводится, чтобы в системном методе RTOS_METHOD_TimerService: при декрементации счётчика, "заём из старшего разряда" возник уже "при нулевом значении счётчика" (так удобнее математика)
LDI PoolParameter3byte, High(@1 -1)
RCALL RTOS_METHOD_AddTimer ; Вызываем системный метод
.ENDM
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
; Установить (добавить или обновить) Таймер в пуле RTOS_TimersPool, для отложенного запуска Задачи (БЕЗОПАСНАЯ ВЕРСИЯ)
; Памятка: данный макрос не портит содержимое РОН (даже для передачи параметров) - поскольку защищает используемые регистры Стеком.
; Пример вызова: SAFE_PLAN_TIMER tasknumber,delay
.MACRO SAFE_PLAN_TIMER
PUSH TaskCode ; Здесь, сберегаем только параметровые регистры!
PUSH PoolParameter2byte
PUSH PoolParameter3byte
PLAN_TIMER @0,@1
POP PoolParameter3byte
POP PoolParameter2byte
POP TaskCode
.ENDM
.endif
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
; Убрать (удалить) Таймер из пула RTOS_TimersPool, для отмены отложенного запуска Задачи
; Памятка: под параметры и временные переменные - портит содержимое всех регистров, упомянутых в секции "Subroutine Register Variables", соответствующего системного метода, и в зависимости от текущего режима оптимизации...
; Пример вызова: REMOVE_TIMER tasknumber
.MACRO REMOVE_TIMER
.if @0==$00
.ERROR "system <Task_Idle> is forbidden to use!"
.endif
LDI TaskCode, @0 ; Заполняем параметры
RCALL RTOS_METHOD_RemoveTimer ; Вызываем системный метод
.ENDM
.endif
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
; Убрать (удалить) Таймер из пула RTOS_TimersPool, для отмены отложенного запуска Задачи (БЕЗОПАСНАЯ ВЕРСИЯ)
; Памятка: данный макрос не портит содержимое РОН (даже для передачи параметров) - поскольку защищает используемые регистры Стеком.
; Пример вызова: SAFE_REMOVE_TIMER tasknumber
.MACRO SAFE_REMOVE_TIMER
PUSH TaskCode ; Здесь, сберегаем только параметровые регистры!
REMOVE_TIMER @0
POP TaskCode
.ENDM
.endif
.endif //RTOS_FEATURE_PLANTIMER
.ifdef RTOS_FEATURE_PLANWAITER
;---------------------------------------------------------------------------
; Диспетчеризация Задач по состоянию бита в Управляющем Регистре/Порте или в ячейке SRAM (Флаговая автоматизация: запуск, если бит в байте установлен? если бит в байте снят?)
; Установить (добавить или обновить) Выжидатель в пуле RTOS_WaitersPool, для условного запуска Задачи
; Памятка: под параметры и временные переменные - портит содержимое всех регистров, упомянутых в секции "Subroutine Register Variables", соответствующего системного метода, и в зависимости от текущего режима оптимизации...
; Пример вызова: PLAN_WAITER tasknumber,address,bit,state
.MACRO PLAN_WAITER
.if @0==$00
.ERROR "system <Task_Idle> is forbidden to use!"
.endif
.if @1<0 || @1>0xFFF
.ERROR "'address' parameter must be in [0..0xFFF]!"
.endif
.if @2<0 || @2>7
.ERROR "'bit' parameter must be in [0..7]!"
.endif
.if @3!=0 && @3!=1
.ERROR "'state' parameter must be [0 or 1]!"
.endif
LDI TaskCode, @0 ; Заполняем параметры
LDI PoolParameter2byte, Low(@1)
LDI PoolParameter3byte, (@3<<WAITER_BIT_STATE)|(@2<<WAITER_BIT_POSITION)|(High(@1)<<WAITER_ADDRESS_HIGH)
RCALL RTOS_METHOD_AddWaiter ; Вызываем системный метод
.ENDM
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
; Установить (добавить или обновить) Выжидатель в пуле RTOS_WaitersPool, для условного запуска Задачи (БЕЗОПАСНАЯ ВЕРСИЯ)
; Памятка: данный макрос не портит содержимое РОН (даже для передачи параметров) - поскольку защищает используемые регистры Стеком.
; Пример вызова: SAFE_PLAN_WAITER tasknumber,address,bit,state
.MACRO SAFE_PLAN_WAITER
PUSH TaskCode ; Здесь, сберегаем только параметровые регистры!
PUSH PoolParameter2byte
PUSH PoolParameter3byte
PLAN_WAITER @0,@1,@2,@3
POP PoolParameter3byte
POP PoolParameter2byte
POP TaskCode
.ENDM
.endif
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
; Убрать (удалить) Выжидатель из пула RTOS_WaitersPool, для отмены условного запуска Задачи
; Памятка: под параметры и временные переменные - портит содержимое всех регистров, упомянутых в секции "Subroutine Register Variables", соответствующего системного метода, и в зависимости от текущего режима оптимизации...
; Пример вызова: REMOVE_WAITER tasknumber
.MACRO REMOVE_WAITER
.if @0==$00
.ERROR "system <Task_Idle> is forbidden to use!"
.endif
LDI TaskCode, @0 ; Заполняем параметры
RCALL RTOS_METHOD_RemoveWaiter ; Вызываем системный метод
.ENDM
.endif
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
; Убрать (удалить) Выжидатель из пула RTOS_WaitersPool, для отмены условного запуска Задачи (БЕЗОПАСНАЯ ВЕРСИЯ)
; Памятка: данный макрос не портит содержимое РОН (даже для передачи параметров) - поскольку защищает используемые регистры Стеком.
; Пример вызова: SAFE_REMOVE_WAITER tasknumber
.MACRO SAFE_REMOVE_WAITER
PUSH TaskCode ; Здесь, сберегаем только параметровые регистры!
REMOVE_WAITER @0
POP TaskCode
.ENDM
.endif
.endif //RTOS_FEATURE_PLANWAITER
;***************************************************************************
;*
;* Инициализация RTOS
;*
;***************************************************************************
; Инициализация RTOS
; (Внимание: этот блок должен располагаться в коде инициализации микроконтроллера, запускаться единожды после "Reset"!)
; Памятка: портит содержимое регистра TEMP1.
.MACRO RTOS_INIT
.if defined(RTOS_FEATURE_PLANTASK) ; (без поддержки "Базовой диспетчеризации задач" все функции RTOS отключены!)
.ifndef RTOS_OPTIMIZE_FOR_SMALL_MEMORY
.ifdef RTOS_FEATURE_PLANTASK
RCALL RTOS_METHOD_ClearTaskQueue ; Очистить очередь Задач RTOS
.endif
.ifdef RTOS_FEATURE_PLANTIMER
RCALL RTOS_METHOD_ClearTimers ; Очистить список Таймеров RTOS
.endif
.ifdef RTOS_FEATURE_PLANWAITER
RCALL RTOS_METHOD_ClearWaiters ; Очистить список Выжидателей RTOS
.endif
.endif //RTOS_OPTIMIZE_FOR_SMALL_MEMORY
.MESSAGE "Attention: check the main RTOS Timer setting! Are constant set correspond to CPU clock? Choose one free hardware Timer... Are Register bits correct?"
; Вспомогательные Константы для настройки основного таймера ядра RTOS:
;
; Инструкция:
; Установите фактическую частоту МК, выберите Предделитель ->
; получите итоговый "Делитель тактовой частоты" так,
; чтобы событие таймера/счётчика Overflow/Compare срабатывало каждую 1ms.
; Ограничения:
; Значения Пределителя можно использовать только поддерживаемые выбранным аппаратным Таймером (не забудьте, затем, корректно выставить ему соответствующие биты конфигурации!)
; Значение Делителя должно получиться целым числом! Но как можно меньше - чтобы помещаться в разрядную сетку таймера: для 8-bit <=255 / для 16-bit <=65535.
.equ CMainClock = 8000000 ; тактовая частота "CPU Clock" (Hz)
.equ CTimerPrescaler = 64 ; Предделитель аппаратного таймера: 1, 8, [32,] 64, [128,] 256, 1024
.equ CTimerDivider = CMainClock/CTimerPrescaler/1000 ; итоговый "делитель тактовой частоты", т.е. коэффициент её преобразования во время ~ 1ms
; Самопроверка:
.IF FRAC(CMainClock/CTimerPrescaler/1000.0)>0
.WARNING "CTimerDivider is fractional! Timings could lose accuracy..."
.ENDIF
; Самопроверка (только для 8-bit таймеров):
.IF CTimerDivider>255
.WARNING "CTimerDivider is incorrect (over byte capacity)! Use bigger CTimerPrescaler..."
.ENDIF
; Выберите, для нужд RTOS, один из доступных аппаратных Таймеров/Счётчиков.
;
; Т.к. моделей МК очень много, в большинстве несколько таймеров, и у каждого свои регистры/биты настройки,
; кроме того прикладная задача накладывает ещё свои ограничения (некоторые таймеры могут быть заняты),
; то выбор и конфигурация конкретного аппаратного Таймера - обычно, производятся самим программистом, вручную!
; Но для облегчения работы, был разработан код автоматического способа, покрывающий большинство случаев: см. режим RTOS_USE_TIMER0_OVERFLOW
.ifndef RTOS_USE_TIMER0_OVERFLOW
.ifdef RTOS_USE_TIMER2_COMPARE
; Замечу, что по "методу предложенному DI HALT'ом", вам требуется Таймер/Счётчик обязательно с "режимом компарации" - чтоб навесить "службу таймеров" на его прерывание "Timer/CounterX Compare Match".
; Идея здесь состоит в том, чтобы отсчитывать CTimerDivider тиков таймера и АВТОМАТИЧЕСКИ вызывать прерывание - точно с периодичностью 1ms.
; Это позволяет прецизионно тактировать "службу таймеров RTOS", даже не используя специальных "часовых кварцев" (со специально подобранными частотами, кратными ёмкости счётного регистра таймера).
; Данный приём работает на любой исходной "тактовой частоте" (CPU clock). И весьма точно! За исключением тех случаев, когда соотношение "тактовой частоты" CMainClock и доступных значений "предделителей" CTimerPrescaler не дозволяет добиться целого значения "делителя" CTimerDivider - в таких случаях, появляется НЕБОЛЬШАЯ погрешность счёта...
; Настройка аппаратного 8-bit Timer/Counter2:
; (Supported: ATmega8/ATmega16)
OUTI TCCR2, 1<<CTC2|4<<CS20 ; Установить режим CTC (автосброс после достижения регистра сравнения) и Предделитель = clk/64
OUTI OCR2, Low(CTimerDivider) ; Установить значение в регистр сравнения
OUTI TCNT2, 0 ; Установить начальное значение счётчика
SETB TIMSK, OCIE2 ; Разрешаем прерывание: Timer/Counter2 Output Compare Match (по достижению регистра сравнения)
.else //end of RTOS_USE_TIMER2_COMPARE
; (*** здесь вы можете вставить свой особый код инициализации таймера ***)
.endif
.else //begin of RTOS_USE_TIMER0_OVERFLOW
; Если только получилось значение Делителя CTimerDivider == 256 или 512, кратное ёмкости счётного регистра
; (что случается редко и только при использовании специальных "часовых кварцев") -
; тогда вы можете использовать любой простой Таймер/Счётчик, и его базовое прерывание "по переполнению" ("Timer/CounterX Overflow").
; Не имея возможность ограничить значение в счётчике - можно только дождаться заполнения его полностью, переполнения, и вызова прерывания "Timer/CounterX Overflow".
; Замечу, что "режим компарации" присутствует только в самых навороченных и ценных Таймер/Счётчиках, обладающих также другими полезными функциями (ШИМ, тактирование от отдельного кварца 32kHz, и др.) - жалко отдавать такой под RTOS!
; Поэтому, чтобы убить всех зайцев, хочу предложить вам другой способ: использования простого счётчика, с только базовым прерыванием "по переполнению", позволяющий адаптироваться к любому значению Делителя CTimerDivider:
; чтобы ограничить диапазон счёта - будем ограничивать значение в счётном регистре не "сверху", а "снизу"!
; Задействуем аппаратный Timer/Counter0 - который есть во всех МК, и обычно, самый простой по функционалу (не жалко).
; Службу таймеров RTOS_TIMER_SERVICE навесим на его прерывание "по переполнению" ("Timer/Counter0 Overflow").
; В этом же прерывании, по переполнении счётчика и сбросе счётного регистра в ноль, будем каждый раз преинициализировать счётчик в "дополнение до переполнения = требуемому Делителю": OUTI TCNT0, 256-Low(CTimerDivider)
; Примечание: в некоторых младших моделях ATtiny присутствует единственный таймер (16-bit Timer/Counter0), но он 16-битный!
; код преинициализации счётчика тогда будет: OUTI TCNT0, (65536-CTimerDivider)
; где "делитель тактовой частоты" допустим: CTimerDivider, для 16-bit, <=65535.
; Поэтому код инициализации таймера запрограммирован универсальным, через директивы условной компиляции (проверено на ATtiny10).
; В некоторых МК, Timer/Counter0 столь наворочен, что у него не один "Control Register TCCR0", а несколько... Причём, Источник и Предделитель обычно настраиваются через TCCR0B - вот и определим его через псевдоним, для универсализации кода.
.ifndef TCCR0
.equ TCCR0 = TCCR0B
.endif
; Аналогично, бывают несколько регистров "Timer/Counter Interrupt Mask"... Выбираем наиболее вероятный.
.ifndef TIMSK
.equ TIMSK = TIMSK0
.endif
; Автоматический выбор битов конфигурации Предделителя (эта система используется в большинстве, если не во всех, МК AVR):
.if CTimerPrescaler == 1
.equ CTimerPrescaler_CS = 0b001
.elif CTimerPrescaler == 8
.equ CTimerPrescaler_CS = 0b010
.elif CTimerPrescaler == 64
.equ CTimerPrescaler_CS = 0b011
.elif CTimerPrescaler == 256
.equ CTimerPrescaler_CS = 0b100
.elif CTimerPrescaler == 1024
.equ CTimerPrescaler_CS = 0b101
.else
.ERROR "CTimerPrescaler is incorrect! Use one of acceptable values: 1, 8, 64, 256, 1024"
.endif
; Настройка аппаратного 8-bit Timer/Counter0:
; (Supported: большинство МК AVR)
OUTI TCCR0, (CTimerPrescaler_CS<<CS00) ; Запустить таймер от Тактовой частоты, и Установить Предделитель = clk/64
SETB TIMSK, TOIE0 ; Разрешить прерывание: Overflow Interrupt Enable
.ifndef TCNT0L
; В случае 8-разрядного Timer/Counter0
OUTI TCNT0, 256-Low(CTimerDivider) ; преИнициализировать начальное значение счётчика
.else
; В случае 16-разрядного Timer/Counter0
; Note: To do a 16-bit write, the high byte must be written before the low byte. For a 16-bit read, the low byte must be read before the high byte.
; It is important to notice that accessing 16-bit registers are atomic operations. Aware interrupts, use cli/sei!
OUTI TCNT0H, High(65536-CTimerDivider)
OUTI TCNT0L, Low (65536-CTimerDivider) ; преИнициализировать начальное значение счётчика
.endif
.endif //end of RTOS_USE_TIMER0_OVERFLOW
.endif //RTOS_FEATURE_PLANTASK
.ENDM
.if defined(RTOS_FEATURE_PLANTASK) && (defined(RTOS_FEATURE_PLANTIMER) || defined(RTOS_FEATURE_PLANWAITER)) ; (служба таймеров используется только в этих режимах)
; Q: Какой аппаратный таймер задействовать для "Службы Таймеров Ядра RTOS" (Main Timer Service)?
;
; A1) Чтобы использовать простой Таймер/Счётчик0 и его базовое прерывание "по переполнению" ("Timer/Counter0 Overflow") - включите это определение.
; Данный метод рекомендуется использовать, по умолчанию!
; Универсален: подходит для большинства задач, использует минимум аппаратных ресурсов, прост в настройке.
; Самонастраиваемый: здесь, вам достаточно только установить Константы режимов, а управляющие регистры будут сконфигурированы автоматически (подходит для большинства МК AVR).
.EQU RTOS_USE_TIMER0_OVERFLOW = 1
; A2) Если вы хотите использовать 8-битный Timer/Counter2 с "режимом компарации" - включите это определение.
;.EQU RTOS_USE_TIMER2_COMPARE = 1
; A3) Если же вы хотите использовать свой иной метод - то просто закомментируйте оба верхних определения,
; и пропишите свой код в соответствующую секцию "else", между директивами условной компиляции, выше...
.endif
; Q: Заметка: Что делать, если CTimerDivider никак не получается целым числом?
; A:
; Таймер/Счётчик может сосчитать только целое число тиков!
; В принципе, можно проигнорировать и принять Делитель как есть, просто отбросив его дробную часть...
; Как показывает расчёт, при округлении дробного Делителя - потеря точности счёта не слишком велика.
; Погрешность растёт при понижении частоты, и зависит от выбора предделителя...
; Таким образом, если очень припрёт, то можно проигнорировать погрешность, возникающую при отбрасывании дробной части Делителя.
; Всё равно, "програмные таймеры RTOS" - не являются прецизионными! Да ещё и тактирование МК, зачастую, осуществляется не от Кварца...
;
; Например:
; при CPU clock=8MHz, prescaler=64 -> divider=125tic ~ 1ms, где 1tic=0,008ms -> погрешность = 0
; при CPU clock=20MHz, prescaler=128 -> divider=156,25tic ~ 1ms, где 1tic=0,0064ms -> погрешность относительная=0.25/156=0,0016 или абсолютная=128*0.25=32clk, т.е. 16us за 1ms (т.е. уходит быстрее на 0,0016сек за 1сек)
; при CPU clock=1MHz, prescaler=32 -> divider=31,25tic ~ 1ms, где 1tic=0,032ms -> погрешность относительная=0.25/31=0,008 (т.е. уходит быстрее на 0,008сек за 1сек)
; при CPU clock=1MHz, prescaler=8 -> divider=125tic ~ 1ms, где 1tic=0,008ms -> погрешность = 0
;
; Рекомендация:
; Как видите, например, при divider=31,25tic - погрешность 0.25 уже на два порядка меньше всего числа 31.0...
; Следовательно, при дробном divider, просто не допускайте значения divider<10, а лучше пусть будет divider>100. Тогда погрешность от дробной части будет минимальной...
;---------------------------------------------------------------------------
; Инициализация USART
; (Примечание: USART совершенно не требуется для RTOS! Данный макрос предоставляется исключительно как "довесок" -
; можете включить его в секцию инициализации МК, если вы используете это периферийное устройство...)
; Памятка: портит содержимое регистра TEMP1.
.MACRO USART_INIT
.MESSAGE "Attention: check USART setting! Are constant set correspond to CPU clock? Are Register bits correct?"
; Вспомогательные Константы для настройки скорости USART:
.equ CXTAL = 8000000 ; тактовая частота "CPU Clock" (Hz)
.equ CBaudRate = 9600 ; скорость передачи, опционально: 19200, 28800...
.equ CBaudDivider = CXTAL/(16*CBaudRate)-1 ; формула для "asynchronous normal mode" (U2X=0) (without "double speeding")
OUTI UBRRL, Low (CBaudDivider) ; "Делитель" позволяет подогнать CPU clock (от которого все устройства тактируются, в т.ч. USART)
OUTI UBRRH, High(CBaudDivider) ; к одной из стандартных "частот передачи" через последовательный порт (CBaudRate).
OUTI UCSRA, 0 ; (zero event flags - just cleaning)
OUTI UCSRB, (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(1<<TXCIE) ; (enable receiver and transmitter, enable it interrupts)
.ifdef URSEL
; Примечание: В ATmega8/16/32 используется мультиплексирование управляющих регистров UBRRH/UCSRC - практикуется, через изврат с URSEL...
OUTI UCSRC, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0) ; (set 8-bit character size)
.else
; В остальных МК - по нормальному...
OUTI UCSRC, (1<<UCSZ1)|(1<<UCSZ0) ; (set 8-bit character size)
.endif
.ENDM
;***************************************************************************
;*
;* Служба таймеров RTOS
;*
;***************************************************************************
.if defined(RTOS_FEATURE_PLANTASK) && (defined(RTOS_FEATURE_PLANTIMER) || defined(RTOS_FEATURE_PLANWAITER)) ; (служба таймеров используется только в этих режимах)
; Служба таймеров RTOS (располагается в прерывании аппаратного таймера и запускается каждую 1мс)
; Памятка: данный обработчик прерывания не портит содержимое РОН - поскольку защищает используемые регистры Стеком. Также, SREG сохраняется всегда.
; Внимание: данный обработчик прерывания требует/использует в Стеке 2+2=4 байт (на адрес возврата и сбережение регистров)!
; Замечу, что этот код является "самым слабым звеном" из всех методов RTOS, по потреблению памяти, что критично для самых младших AVR (с 32-64 байтами SRAM)...
; Учитывайте также, что все другие системные методы (в режиме с оптимизацией RTOS_OPTIMIZE_FOR_SMALL_MEMORY) - явно не используют Стек вообще, но каждый их вызов забирает в Стеке по +2 байта (на адрес возврата).
; Таким образом, для работы только Ядра RTOS (в режиме со включенной оптимизацией RTOS_OPTIMIZE_FOR_SMALL_MEMORY) - в стеке требуется гарантированно оставить 6 байт, только для RTOS! (При этом, Диспетчер сможет запускать прикладные Задачи... А также, в любой момент, сможет отработать обработчик прерывания "Таймерной Службы"...)
; Но из когда прикладных Задач требуется вызывать (RCALL) другие подпрограммы, хотя бы одного уровня вложенности: например, обязательно требуются системные методы PLAN_TASK - поэтому в Стеке требуется ещё +2 байта!
; Итого, минимум, для работы Системы требуется 8 байт в Стеке. (При этом, Диспетчер сможет запускать прикладные Задачи... В любой момент, сможет отработать обработчик прерывания "Таймерной Службы"... И, из кода Задач, можно вызывать Подпрограммы, одного уровня вложенности, в т.ч. PLAN_TASK-методы...)
.MACRO RTOS_TIMER_SERVICE
PUSHF ; сохранить temp и SREG в стеке
LDS temp, RTOS_TimerServiceCounter
INC temp ; Инкрементировать Счётчик количества прерываний аппаратного таймера (RTOS_TIMER_SERVICE), прошедших с момента последней отработки системного метода (RTOS_METHOD_TimerService)
BREQ Exit__RTOS_TIMER_SERVICE ; Если было Переполнение, то Не сохраняем значение счётчика, и дальнейшие тики аппаратного таймера игнорируем... (Это может быть, в случае слишком долгого исполнения подпрограммы прикладной Задачи, дольше 256мс.)
STS RTOS_TimerServiceCounter, temp
Exit__RTOS_TIMER_SERVICE:
POPF
.ENDM
.endif //RTOS_FEATURE_PLANTASK
;***************************************************************************
;*
;* Old interface aliases, for backward compatibility.
;*
;* (Deprecated: Do not use it in new code!)
;*
;***************************************************************************
.MACRO SetTimerTask
PLAN_TIMER @0,@1
.ENDM
.MACRO SetTask
PLAN_TASK @0
.ENDM
.MACRO TimerService
RTOS_TIMER_SERVICE
.ENDM
.MACRO INIT_RTOS
RTOS_INIT
.ENDM
;***************************************************************************
;*
;* Some DI HALT's macro, distributed earlier with RTOS...
;* Not recomended to usage, because makes code more obfuscated, without significant profit.
;* Provided here for backward compatibility only!
;*
;* (Note: It have not used in RTOS code, and it have not any relations to RTOS!)
;*
;***************************************************************************
;---------------------------------------------------------------------------
; SRAM memory read/write macros for Devices with AVR8L-based CPU: ATtiny10, ATtiny20, ATtiny40.
; Maked and used by DI HALT, but deprecated by Celeron.
.MACRO LDR
.MESSAGE "Deprecated: macros LDR was improper! Use macros INR (macrobaselib.inc) instead!"
;PUSH ZL
;PUSH ZH
;
;LDI ZL, Low(@1)
;LDI ZH, High(@1)
;
;LD @0, Z
;
;POP ZH
;POP ZL
INR @0, @1
.ENDM
.MACRO STR
.MESSAGE "Deprecated: macros STR was improper! Use macros OUTR (macrobaselib.inc) instead!"
;PUSH ZL
;PUSH ZH
;
;LDI ZL, Low(@0)
;LDI ZH, High(@0)
;
;ST Z, @1
;
;POP ZH
;POP ZL
OUTR @0, @1
.ENDM
;---------------------------------------------------------------------------
; FLASH memory read/write helpers.
; Read byte (unfortunately, only first in word) from Program Flash Memory, at particular Address @1 (in words), to particular Register @0.
; Usage: LDF register,label
.MACRO LDF
.MESSAGE "Deprecated: macros LDF is clumsy! Do not use it at all!"
PUSH ZL
PUSH ZH
LDI ZL, Low(@1*2)
LDI ZH, High(@1*2)
LPM @0, Z
POP ZH
POP ZL
.ENDM
; Just load Program Label Address @0 to Z-register. (Supposedly, preparing to LPM instruction?)
.MACRO LDP
LDI ZL, Low(@0*2)
LDI ZH, High(@0*2)
.ENDM
; Synonym...
.MACRO LDPA
LDP
.ENDM
;---------------------------------------------------------------------------
; SRAM memory read/write helpers.
; Just load SRAM Address @0 to X-register.
.MACRO LDX
LDI XL, Low(@0)
LDI XH, High(@0)
.ENDM
; Just load SRAM Address @0 to Y-register.
.MACRO LDY
LDI YL, Low(@0)
LDI YH, High(@0)
.ENDM
; Just load SRAM Address @0 to Z-register.
.MACRO LDZ
LDI ZL, Low(@0)
LDI ZH, High(@0)
.ENDM
;=== END "RTOS-macro.inc" ==================================================
; coded by (c) DI HALT, 2008 http://easyelectronics.ru/
; coded by (c) Celeron, 2014 http://inventproject.info/
.ENDIF