Olá,
boa iniciativa estar a estudar PIC em Assembler.
Para termos tempos exactos, temos que fazer as nossas funções de acordo com o que queremos. E se estamos a trabalhar com um oscilador de 4.0MHz, então temos que encadear CALLs de funções temporizadoras, para ter o que queremos.
E não esquecer que como o barramento de dados é só de 8-bits, só podemos contar até 0xFF ou 255.
Por exemplo, uma função que faz 500us, pode ser feita da seguinte maneira ::
list p=16f628A ;configura o tipo de CPU usado
radix hex ;pre-definicao do tipo de valores numericos usados
include "p16f628a.inc" ;
;---------------------------
temp equ 0x20 ;define endereco 0x20 com o nome de TEMP
;---------------------------
inicio
call atraso_500u ;chama rotina de atraso
call atraso_500u ;chama rotina de atraso
goto inicio ;faz temporizacao de novo
;---------------------------
atraso_500u
movlw 0xA5 ;carrega W com o valor 0xA5
movwf temp ;move valor em W para TEMP
decfsz temp, f ;decrementa TEMP em uma unidade, coloca resultado em TEMP
;e salta a instrucao seguinte, se o resultado for zero
goto $-1
return ;sai fora da rotina de ATRASO
;---------------------------
end
De cada que chama a função de 500us, entre a chamada/execução/saída passam exactamente 500us estando a usar oscilador 4.0MHz.
A função a seguir faz exactamente 100ms entre a chamada/execução/saída com oscilador de 4MHz.
list p=16f628A ;configura o tipo de CPU usado
radix hex ;pre-definicao do tipo de valores numericos usados
include "p16f628a.inc" ;
__config _CP_OFF & _DATA_CP_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_ON & _INTOSC_OSC_NOCLKOUT & _PWRTE_ON & _WDT_OFF
;---------------------------
temp equ 0x20 ;define endereco 0x20 com o nome de TEMP
temp2 equ 0x21 ;define endereco 0x21 com o nome de TEMP2
;---------------------------
inicio
;configura toda a porta A como entrada/saida DIGITAL
movlw 0x07 ;carrega W como valor de 0x07
movwf CMCON ;move de W para CMCON
;configura portas A e B como entradas/saidas
bsf STATUS,5 ;activa bit 5 do registo STATUS
movlw 0x00 ;carrega W como 0x00, todos pinos porta A como saida
movwf TRISA ;move de W para TRISA
movlw 0x00 ;carrega W como 0x00, todos pinos porta A como saida
movwf TRISB ;move de W para TRISB
bcf STATUS,5 ;desactiva bit 5 do registo STATUS
repete
movlw 0xFF ;carrega W com valor de 0xFF
xorwf PORTB, f ;inverte porta B (XOR, OU Exclusivo com o W)
call atraso_100m ;chama rotina de atraso
goto repete ;faz temporizacao de novo
;---------------------------
atraso_100m
movlw 0xC9 ;carrega W
movwf temp2 ;move valor em W para TEMP
movlw 0xA4 ;carrega W
movwf temp ;move valor em W para TEMP
decfsz temp, f ;decrementa TEMP em uma unidade, coloca resultado em TEMP
goto $-1 ;salta para uma instrucao anterior
nop
decfsz temp2, f ;decrementa TEMP em uma unidade, coloca resultado em TEMP
goto $-6 ; salta para duas instrucoes anteriores
movlw 0x20 ;carrega W
movwf temp ;move valor em W para TEMP
decfsz temp, f ;decrementa TEMP em uma unidade, coloca resultado em TEMP
goto $-1 ;salta para uma instrucao anterior
nop
return ;sai fora da rotina de ATRASO
;---------------------------
end
Como pode ver, há muitas maneiras de o fazer. Cada programador pode fazer da sua, e todas funcionam. É preciso é encontrar um método que funcione para si, e manter-se nele.
Eu, pessoalmente, faço temporizadores sempre com os TMRx e interrupções.
Criei algoritmos que me dão TICKs a cada 1ms/10ms/100ms/1s, assim posso ficar descansado com o programar principal e não tenho o CPU ocupado a "contar tempo". É +- como nos PLCs.
Para poder contar o tempo certo, tem uma ferramenta que é o STOPWATCH, no menú DEBUGGER, depois de activar o MPLAB SIM.
E para configurar o oscilador da simulação: DEBUGGER-SETTINGS.
bom trabalho! espero que tenha ajudado