Простой программный таймер для конечных автоматов на ассемблере MCS-51 и PIC12/16
Доброго времени суток. Навеяно статьей easyelectronics.ru/prostoj-programmnyj-tajmer-dlya-konechnyx-avtomatov.html, и особенно, прекрасной статьей we.easyelectronics.ru/AVR/taymery-i-zaderzhki-sbornik-receptov.html
Признаюсь чесно, реализовать на асме весь нужный мне функционал из 2 статьи я просто не смог. Но и то что было достигнуто, я оцениваю как успех, потому что до прочтения этих статей я не понимал всю важность максимально короткого обработчика прерывания, хотя и знал что желательно его излишне не удлиннять. Раньше у меня было 10-20 переменных таймеров, и я их все (что были не 0) декрементировал по очереди прямо в обработчике прерывания таймера, особо не парясь. После прочтения 2-х этих статей я пересмотрел свой подход, и результат можно увидеть ниже.
Основной минус по сравнению со способом проверки от Lifelovera — необходимость успеть среагировать на дотикавший таймер пока он не убежал дальше. Никаких полдиапазона, или сколько мы себе там хотели бы оставить, увеличивая диапазон задержек, как сделано в одной из задач в его статье. Типа так:
Только равно, никаких больше-меньше…
Так как период системного тика ровно 1 мс, на это есть ровно «чуть меньше 1 мс». То есть самая длинная цепочка переборов состояний (проход всех проверок в главном цикле) должна укладыватся в 1 мс. Если не уложимся — бяда. Выход — сделать прерывание раз в 10 мс — будет в 10 раз больше времени успеть))
Проект мигания 3 светодиодиками (каждый со своей частотой) для удобства оформлен в «библиотеку» и разбит на несколько файлов.
Заранее прошу прощения за табуляцию вместо пробелов.
Накатал еще такую же реализацию под PIC12/16 (тоже на родимом асме, с разбивкой на библиотеку и главный файл), может кому пригодится:
Правда под Пики LED-ы на выход не настраивал, в симуляторе и так нормально интервалы отслеживать. Кто осилит этот код, тот порты и сам сможет настроить))
Признаюсь чесно, реализовать на асме весь нужный мне функционал из 2 статьи я просто не смог. Но и то что было достигнуто, я оцениваю как успех, потому что до прочтения этих статей я не понимал всю важность максимально короткого обработчика прерывания, хотя и знал что желательно его излишне не удлиннять. Раньше у меня было 10-20 переменных таймеров, и я их все (что были не 0) декрементировал по очереди прямо в обработчике прерывания таймера, особо не парясь. После прочтения 2-х этих статей я пересмотрел свой подход, и результат можно увидеть ниже.
Основной минус по сравнению со способом проверки от Lifelovera — необходимость успеть среагировать на дотикавший таймер пока он не убежал дальше. Никаких полдиапазона, или сколько мы себе там хотели бы оставить, увеличивая диапазон задержек, как сделано в одной из задач в его статье. Типа так:
if(time_ms + t1 == current_time_ms) /* проверяем кончилась ли задержка */
Только равно, никаких больше-меньше…
Так как период системного тика ровно 1 мс, на это есть ровно «чуть меньше 1 мс». То есть самая длинная цепочка переборов состояний (проход всех проверок в главном цикле) должна укладыватся в 1 мс. Если не уложимся — бяда. Выход — сделать прерывание раз в 10 мс — будет в 10 раз больше времени успеть))
Проект мигания 3 светодиодиками (каждый со своей частотой) для удобства оформлен в «библиотеку» и разбит на несколько файлов.
Заранее прошу прощения за табуляцию вместо пробелов.
;===============================================================================
; sys_tick.inc - macro file, used with sys_tick_lib.asm
;===============================================================================
;F_CPU equ 12000
F_CPU equ 11059
;===============================================================================
if F_CPU = 12000
RELOAD_VALUE equ 65535-1000+7
elseif F_CPU = 11059
RELOAD_VALUE equ 65535-914
endif
;===============================================================================
; Timer setting macro
;===============================================================================
SET_TIMER_MS_MACRO macro name_of_timer,value
clr IE.1 ; disable Timer0 interrupt for atomic access to time_ms
mov A, time_ms+1
add A, #low(value) ; set low byte
mov name_of_timer+1,A
mov A, time_ms
setb IE.1 ; end atomic access to time_ms
addc A, #high(value) ; set high byte with carry from low byte
mov name_of_timer,A
endm
;===============================================================================
; Timer check macro
;===============================================================================
CHECK_TIMER_MACRO macro name_of_timer,task_of_timer
local No_time
push ACC
mov A, name_of_timer ; first check high byte of timer
clr IE.1 ; disable Timer0 interrupt for atomic access to time_ms
cjne A,time_ms,No_time
mov A, name_of_timer+1 ; second check low byte of timer
cjne A,time_ms+1,No_time
call task_of_timer ; time is coming, go to the Task
No_time:
setb IE.1 ; end atomic access to time_ms
pop ACC
endm
;===============================================================================
; System timer initialization macro
;===============================================================================
SYS_TICK_INIT_MACRO macro
anl TMOD, #11111101b ; Timer0 in mode 1
orl TMOD, #00000001b
setb TR0 ; Timer0 start
setb IE.1 ; Timer0 interrupt enable
setb TF0 ; call an interrupt to first set the timer
endm
;===============================================================================
;===============================================================================
; sys_tick_lib.asm - library file System timer. Need sys_tick.inc to use
;===============================================================================
public Timer0_INT
public time_ms
;===============================================================================
include 'sys_tick.inc'
;===============================================================================
sys_tick_lib_data segment data
rseg sys_tick_lib_data
time_ms: ds 2
;===============================================================================
sys_tick_lib_code segment code
rseg sys_tick_lib_code
;===============================================================================
Timer0_INT: ; System timer heandler
mov TH0, #high(RELOAD_VALUE) ; reload timer
mov TL0, #low(RELOAD_VALUE)
push ACC
inc time_ms+1 ; inc low byte of system time
mov A, time_ms+1
jnz $+4 ; if need
inc time_ms ; inc high byte of system time
pop ACC
reti
;===============================================================================
;===============================================================================
; macro.inc - to connect the necessary segments of the moved code
;===============================================================================
; The macro for initializing the stack
;===============================================================================
INIT_STACK_MACRO macro
mov SP, #stack
endm
;===============================================================================
; Memory Cleanup Macro
;===============================================================================
ClEAR_RAM_MACRO macro END_OF_RAM
mov R0, #END_OF_RAM
mov @R0, #0
djnz R0, $-2
endm
;===============================================================================
extern code (Timer0_INT)
extern data (time_ms)
include 'macro.inc'
include 'sys_tick.inc'
LED1 bit P0.0
LED2 bit P0.1
LED3 bit P0.2
main_data segment data
rseg main_data
time_1: ds 2
time_2: ds 2
time_3: ds 2
dseg at 2Fh
stack:
main_code segment code
rseg main_code
cseg at 0
jmp Init
org 03h ; INT0 interrupt
; jmp INT_0
reti
org 0Bh ; Timer0 overflow interrupt
jmp Timer0_INT
; reti
org 13h ; INT1 interrupt
; jmp INT_1
reti
org 1Bh ; Timer1 overflow interrupt
; jmp Timer1_Int
reti
org 23h ; UART interrupt
; jmp Serial_Int
reti
org 2Bh ; Timer2 overflow interrupt
; jmp Timer2_Int
reti
Init:
ClEAR_RAM_MACRO 127
INIT_STACK_MACRO
SYS_TICK_INIT_MACRO
setb EA ; interrupts enable
;===============================================================================================================================
SET_TIMER_MS_MACRO time_1,400
SET_TIMER_MS_MACRO time_2,600
SET_TIMER_MS_MACRO time_3,800
;===============================================================================================================================
Main:
;===============================================================================================================================
CHECK_TIMER_MACRO time_1,Task_1 ; check time_1 and go to then Task if time is coming
CHECK_TIMER_MACRO time_2,Task_2 ; check time_2 and go to then Task if time is coming
CHECK_TIMER_MACRO time_3,Task_3 ; check time_2 and go to then Task if time is coming
;===============================================================================================================================
jmp Main
;===============================================================================================================================
Task_1:
cpl LED1
SET_TIMER_MS_MACRO time_1,400
ret
;-------------------------------------------------------------------------------------------------------------------------------
Task_2:
cpl LED2
SET_TIMER_MS_MACRO time_2,600
ret
;-------------------------------------------------------------------------------------------------------------------------------
Task_3:
cpl LED3
SET_TIMER_MS_MACRO time_3,800
ret
;===============================================================================================================================
end
Накатал еще такую же реализацию под PIC12/16 (тоже на родимом асме, с разбивкой на библиотеку и главный файл), может кому пригодится:
;================================================================================================================
; hardware_profile.inc - CPU type definition
;================================================================================================================
#define USE_PIC12 0
if (USE_PIC12)
include p12f629.inc
list p=12f629
else
include p16f84.inc
list p=16f84
endif
;================================================================================================================
; main.asm - Main project file. Need macro.h & sys_tick_lib.h to use
;================================================================================================================
include hardware_profile.inc
include macro.h
include sys_tick_lib.h
if (USE_PIC12)
__config _CPD_OFF & _CP_OFF & _BODEN_OFF & _MCLRE_ON & _PWRTE_ON & _WDTE_OFF & _FOSC_INTRCIO
else
__config _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
endif
;================================================================================================================
if (USE_PIC12)
#define LED1 GPIO,0
#define LED2 GPIO,1
#define LED3 GPIO,2
else
#define LED1 PORTB,0
#define LED2 PORTB,1
#define LED3 PORTB,2
endif
;================================================================================================================
main_udata udata_shr
w_temp res 1
status_temp res 1
time_1 res 2
time_2 res 2
time_3 res 2
;================================================================================================================
extern time_ms
extern sys_tick_heandler
global End_Interrupt_Heandler
;================================================================================================================
RESET_VECTOR code 0h
goto Init
;================================================================================================================
ISR code 4h ; ISR Heandler
Interrupt_Heandler
PUSH_MACRO ; store context
btfsc INTCON,T0IF
call sys_tick_heandler
End_Interrupt_Heandler
POP_MACRO ; restore context
retfie
;================================================================================================================
Init
if (USE_PIC12)
banksel OPTION_REG ; bank 1
call 3FFh
movwf OSCCAL
banksel INTCON ; bank 0
movlw b'00000111'
movwf CMCON ; analog comparator disable
endif
clrf time_1
clrf time_1+1
clrf time_2
clrf time_2+1
clrf time_3
clrf time_3+1
SYS_TICK_INIT_MACRO
SET_TIMER_MS_MACRO time_1,.400
SET_TIMER_MS_MACRO time_2,.600
SET_TIMER_MS_MACRO time_3,.800
;================================================================================================================
Main
;================================================================================================================
CHECK_TIMER_MACRO time_1,Task_1 ; check time_1 and go to then Task if time is coming
CHECK_TIMER_MACRO time_2,Task_2 ; check time_2 and go to then Task if time is coming
CHECK_TIMER_MACRO time_3,Task_3 ; check time_2 and go to then Task if time is coming
;================================================================================================================
goto Main
;================================================================================================================
Task_1
TOGGLE_BIT_MACRO LED1
SET_TIMER_MS_MACRO time_1,.400
return
;----------------------------------------------------------------------------------------------------------------
Task_2
TOGGLE_BIT_MACRO LED2
SET_TIMER_MS_MACRO time_2,.600
return
;----------------------------------------------------------------------------------------------------------------
Task_3
TOGGLE_BIT_MACRO LED3
SET_TIMER_MS_MACRO time_3,.800
return
;================================================================================================================
end
;================================================================================================================
; sys_tick_lib.asm - library file System timer. Need sys_tick_lib.h to use
;================================================================================================================
include hardware_profile.inc
include sys_tick_lib.h
;================================================================================================================
sys_tick_udata udata_shr
time_ms res 2
;================================================================================================================
global time_ms
global sys_tick_heandler
;================================================================================================================
F_CPU equ 4000000
;F_CPU equ 4096000
;----------------------------------------------------------------------------------------------------------------
if F_CPU == 4000000
RELOAD_VALUE equ .9
endif
;================================================================================================================
sys_tick code
;================================================================================================================
sys_tick_heandler
if F_CPU == 4000000
movlw RELOAD_VALUE
movwf TMR0
endif
incf time_ms+1 ; inc low byte of system time
bnz $+2 ; if need
incf time_ms ; inc high byte of system time
bcf INTCON,T0IF
return
;================================================================================================================
end
;================================================================================================================
; sys_tick_lib.h - macro file, used with sys_tick_lib.asm
;================================================================================================================
; Timer setting macro
;================================================================================================================
SET_TIMER_MS_MACRO macro name_of_timer,value
bcf INTCON,T0IE ; disable Timer0 interrupt for atomic access to time_ms
movfw time_ms+1
addlw low value ; set low byte of timer
movwf name_of_timer+1
movfw time_ms
bsf INTCON,T0IE ; end atomic access to time_ms
bnc $+2
addlw 1 ; add carry from low byte of timer
addlw high value ; set high byte of timer
movwf name_of_timer
endm
;================================================================================================================
; Timer check macro
;================================================================================================================
CHECK_TIMER_MACRO macro name_of_timer,task_of_timer
local No_time
movfw name_of_timer ; first check high byte of timer
bcf INTCON,T0IE ; disable Timer0 interrupt for atomic access to time_ms
subwf time_ms,w
bnz No_time
movfw name_of_timer+1 ; second check low byte of timer
subwf time_ms+1,w
bnz No_time
call task_of_timer
No_time
bsf INTCON,T0IE ; end atomic access to time_ms
endm
;================================================================================================================
; System timer initialization macro
;================================================================================================================
SYS_TICK_INIT_MACRO macro
clrf time_ms
clrf time_ms+1
banksel OPTION_REG ; bank 1
movlw 0<<T0CS|0<<PSA|0<<PS2|0<<PS1|1<<PS0 ; set the prescaler on Timer0
movwf OPTION_REG
banksel TMR0 ; bank 0
movlw 1<<GIE|1<<T0IE ; enable Timer0 interrupt
movwf INTCON
endm
;================================================================================================================
Правда под Пики LED-ы на выход не настраивал, в симуляторе и так нормально интервалы отслеживать. Кто осилит этот код, тот порты и сам сможет настроить))
- +1
- 15 августа 2018, 23:43
- Gnusmas
VGA все правильно сказал. Но вообще, в природе существует 8051-подобный ассемблер для ПИКов. Если интересно, гуглите «parallax pic assembler». Полной совместимостью и не пахнет (да и регистры ясень пень разные), но кому учить 2 асма лень, а Си не хочется — вроде вариант. Я в свое время, начиная изучать ПИКи после 8051, посматривал в его сторону, но потом зачитавшись мануалом на MPASM, решил все таки осваивать «родной» ПИКовский асм, очень уж понравился его мощный макроассемблер. Может еще выложу проект с демонстрацией, чего я умудрился накрутить на нем.
Старикан ловко заскочил по железной лесенке в паровозную будку и очень быстро раскочегарил котел, ловко переключил все вентили и весьма эффектно стравил пар. Я расчувствовался и прослезился, но грубо утер рукавом кашемирового пальто скупую слезу. Я тоже работал когда-то на паровозе помошником, а потом даже целый год водил его сам. Паровоз попер по своему обычному пути и потащил вагон с зеваками, жующими хотдоги, в сторону тупика, который находился в километре или двух отсюда, после этого он возвращался назад задним ходом и опять набивался в течении часа двух редкими посетителями и прочими клоунами, желающими прокатиться с парком и гудками, как некогда в старину.
- well-man2000
- 16 августа 2018, 21:11
- ↑
- ↓
А где та часть, где мимо паровоза старикана проносится маглев с надписями «ARM Corp»? Если бы зеваки были внимательнее, они бы успели заметить в окнах проносящейся мимо них стальной пули счистливые лица пассажиров, находящихся на острие прогресса. Где-то там, между роскошных купе, на полу игрался ардуинкой маленький мальчик. Он воспитывался очень добрыми родителями, которые следовали принципу «до 5 лет можно все», поэтому его никто не трогал и не упрекал, хотя проходящая мимо бортпроводница в форме с логотипом STM32 Cube, снисходительно улыбалась, и поглядывая на мальчика, отметила про себя, что она очень удачно подсадила своего синышку на «дуинку не на базе AVR», и теперь может не опасается за него, оставляя дома одного, когда уезжает на работу.
Комментарии (6)
RSS свернуть / развернуть