Простой программный таймер для конечных автоматов на ассемблере 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 — необходимость успеть среагировать на дотикавший таймер пока он не убежал дальше. Никаких полдиапазона, или сколько мы себе там хотели бы оставить, увеличивая диапазон задержек, как сделано в одной из задач в его статье. Типа так:

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

Комментарии (6)

RSS свернуть / развернуть
У процессоров 8051 и PIC младших серий ассемблер одинаковый?
0
Нет. Листинги можно практически с первого взгляда опознать — который под 8051, который под пик.
0
VGA все правильно сказал. Но вообще, в природе существует 8051-подобный ассемблер для ПИКов. Если интересно, гуглите «parallax pic assembler». Полной совместимостью и не пахнет (да и регистры ясень пень разные), но кому учить 2 асма лень, а Си не хочется — вроде вариант. Я в свое время, начиная изучать ПИКи после 8051, посматривал в его сторону, но потом зачитавшись мануалом на MPASM, решил все таки осваивать «родной» ПИКовский асм, очень уж понравился его мощный макроассемблер. Может еще выложу проект с демонстрацией, чего я умудрился накрутить на нем.
0
Старикан ловко заскочил по железной лесенке в паровозную будку и очень быстро раскочегарил котел, ловко переключил все вентили и весьма эффектно стравил пар. Я расчувствовался и прослезился, но грубо утер рукавом кашемирового пальто скупую слезу. Я тоже работал когда-то на паровозе помошником, а потом даже целый год водил его сам. Паровоз попер по своему обычному пути и потащил вагон с зеваками, жующими хотдоги, в сторону тупика, который находился в километре или двух отсюда, после этого он возвращался назад задним ходом и опять набивался в течении часа двух редкими посетителями и прочими клоунами, желающими прокатиться с парком и гудками, как некогда в старину.
0
А где та часть, где мимо паровоза старикана проносится маглев с надписями «ARM Corp»? Если бы зеваки были внимательнее, они бы успели заметить в окнах проносящейся мимо них стальной пули счистливые лица пассажиров, находящихся на острие прогресса. Где-то там, между роскошных купе, на полу игрался ардуинкой маленький мальчик. Он воспитывался очень добрыми родителями, которые следовали принципу «до 5 лет можно все», поэтому его никто не трогал и не упрекал, хотя проходящая мимо бортпроводница в форме с логотипом STM32 Cube, снисходительно улыбалась, и поглядывая на мальчика, отметила про себя, что она очень удачно подсадила своего синышку на «дуинку не на базе AVR», и теперь может не опасается за него, оставляя дома одного, когда уезжает на работу.
0
Vga, Gnusmas, что-то я статью слишком по-диагонали прочитал :)
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.