Аппаратных два. А программных можно наделать хоть несколько десятков.
CODE
//========================================================================
.equ ST_TCNT = TCNT0
.equ ST_TIMSK = TIMSK
.equ ST_OCIE = OCIE0
.equ ST_OCR = OCR0
.equ ST_TCCR = TCCR0
.equ CS0 = CS00
.equ CS1 = CS01
.equ CS2 = CS02
//========================================================================
//========================================================================
.equ SYS_TICK = 1 // Период системного таймера 1 мс
//========================================================================
//========================================================================
.equ ST_SIZE = 2
//========================================================================
//========================================================================
//--------------------- Флаги системных таймеров -------------------------
Def_Flag SYS_TICK_FLG
//------------------------------------------------------------------------
//------------------------------------------------------------------------
.equ ST_UNLOCK_FLG = 0 // Флаг активности таймера.
.equ ST_TIME_OUT_FLG = 1 // Время вышло.
//========================================================================
.dseg
//========================================================================
//-------------------- Очередь системных таймеров ------------------------
.set NUM_ST = -1
.set ST_QUANTITY = 0
.macro Def_Sys_Timer
.set NUM_ST = (NUM_ST+1)
.set ST_QUANTITY = (ST_QUANTITY+1)
.set @0 = NUM_ST
.message "System Timer Defined!"
.endmacro
Def_Sys_Timer ST_DRV_LCD
Def_Sys_Timer ST_KBD
Def_Sys_Timer ST_PROC_GAME_1
Def_Sys_Timer ST_PROC_GAME_2
ST_CNT_QUEUE:
.byte ST_QUANTITY*(ST_SIZE+1)
//========================================================================
.cseg
//========================================================================
.macro Init_System_Timer
clr r16
out ST_TCNT, r16
in r16, ST_TIMSK
sbr r16, 1<<ST_OCIE
out ST_TIMSK, r16
outi ST_OCR, (XTAL/64/1000)
in r16, ST_TCCR
sbr r16, 1<<CS0 | 1<<CS1
out ST_TCCR, r16
.endmacro
.macro ReRun_Sys_Timer
in r16,ST_OCR
subi r16,-(XTAL/64/1000)
out ST_OCR,r16
.endmacro
//========================================================================
//========================================================================
Sys_Timer_Comp:
push r16
in r16,SREG
push r16
ReRun_Sys_Timer
sbr FLAGS,1<<SYS_TICK_FLG
pop r16
out SREG,r16
pop r16
reti
//========================================================================
//========================================================================
//-------------------------- Установка таймера ---------------------------
.macro par_timer
.db @0, @1, tab_l(@2)
.endmacro
.macro Set_Timer
ldi r17,HIGH(@0*2)
ldi r16,LOW(@0*2)
rcall _Set_Timer
.endmacro
_Set_Timer:
push XL
push XH
push ZL
push ZH
movw ZH:ZL, r17:r16
lpm XL,Z+
mov r16,XL
lsl XL
add XL,r16
clr XH
subi XL,LOW(-ST_CNT_QUEUE)
sbci XH,HIGH(-ST_CNT_QUEUE)
ldi r17, 3
_Set_Timer_Cycle:
lpm r16, Z+
st X+, r16
dec r17
brne _Set_Timer_Cycle
pop ZH
pop ZL
pop XH
pop XL
ret
//------------------------------------------------------------------------
//-------------------------- Проверка таймера ----------------------------
.macro Proc_Timer
ldi r17,HIGH(@0*2)
ldi r16,LOW(@0*2)
rcall _Proc_Timer
.endmacro
_Proc_Timer:
push XL
push XH
push ZL
push ZH
movw ZH:ZL, r17:r16
lpm XL, Z+
mov r16, XL
lsl XL
add XL, r16
clr XH
subi XL,LOW(-ST_CNT_QUEUE)
sbci XH,HIGH(-ST_CNT_QUEUE)
clt
ld r16, X
ldi r17, 1<<ST_TIME_OUT_FLG
and r17, r16
breq Proc_Timer_End
st X, r16
set
Proc_Timer_End:
pop ZH
pop ZL
pop XH
pop XL
ret
//========================================================================
//========================================================================
Service_Timers: // Служба таймеров.
sbrs FLAGS,SYS_TICK_FLG // Флаг очередного отсчета системного таймера установлен?
ret // Нет, выход.
cbr FLAGS,1<<SYS_TICK_FLG // Установлен, сброс флага.
ldy ST_CNT_QUEUE // Указатель на начало очереди системных таймеров.
clr CNT
Service_Timers_ST_Cycle:
ld r16,Y // Считывание байта-статуса системного таймера.
sbrs r16,ST_UNLOCK_FLG // Таймер заблокирован?
rjmp Process_Next_System_Timer // Да, переход к обработке следующего таймера.
ldd r24,Y+1 // Считывание младшего байта таймера.
ldd r25,Y+2 // Считывание старшего байта таймера.
sbiw r24,1 // Уменьшаем значение таймера на единицу.
std Y+1,r24 // Сохранение значения таймера.
std Y+2,r25 // Сохранение значения таймера.
brne Process_Next_System_Timer // Если время не вышло, то переход к обработке следующего таймера.
sbr r16,1<<ST_TIME_OUT_FLG // Установка флага завершения работы таймера.
Save_ST_Flags:
st Y,r16 // Запись статуса таймера.
Process_Next_System_Timer:
adiw YL,3 // Смещение указателя на следущий таймер.
inc CNT
cpi CNT,ST_QUANTITY // Значение счетчика таймеров равно количеству таймеров?
brlo Service_Timers_ST_Cycle // Меньше, обрабатываем следующие таймеры.
Service_Timers_End:
ret
//========================================================================
Пример применения:
CODE
//========================================================================
Main:
wdr
rcall Service_Timers
rcall Drv_Lcd
rcall KBD_DRV
rcall Proc_Game
rjmp Main
//========================================================================
//========================================================================
Keys_None:
sbrs FSM_FLAGS, KEYS_PRESSED_FLG // Если что-то нажато, то пропуск команды.
ret
Read_Keys_Current
Save_Keys_Prev // Сохранение текущего состояния клавиатуры.
Set_Timer Par_Tim_Debounce // Установка задержки устранения дребезга.
Set_State _KEYS_DOWN
ret
Keys_Down:
Proc_Timer Par_Tim_Debounce
brtc Keys_Down_End
sbrs FSM_FLAGS, KEYS_PRESSED_FLG
rjmp Keys_Set_None
Comp_Last_Curr_Keys
breq Keys_Down_A
rjmp Keys_Set_None
Keys_Down_A:
setb FSM_FLAGS, DEFINE_KEYS_FLG
#if (HOLD==NO)
rjmp Keys_Set_Wait_Up
#elif (HOLD==YES)
Set_Timer Par_Tim_Hold
Set_State _KEYS_HOLD
#endif
Keys_Down_End:
ret
Par_Tim_Debounce:
par_timer ST_KBD, 1<<ST_UNLOCK_FLG, DEBOUNCE_DELAY