Temporizador com AVR
Rodrigo Pinheiro Matias, colega da PICLISTBR, teve a idea de criar um Temporizador Fotográfico usando um AVR AtMega8, e escreveu o código na linguagem C.
Ele fez o layout da plaquinha PCI e lançou o trabalho dele na PICLISTBR para comentários e sugestões. Eu olhei o código do programa em C que ele escreveu, e literalmente achei muito complicado para pouca coisa. De verdade a programação C é assim mesmo. Para coisas pequenas ela fica muito grande. Então eu resolvi ajudar o Rodrigo, e me divertir ao mesmo tempo, prometi a ele re-escrever o firmware deste mesmo temporizador, com exatamente as mesmas funções, em Assembly.
Esse temporizador permite programar de 1 a 99 minutos, para tal utiliza três teclas, uma para incrementar o tempo selecionado em minutos, outra para decrementar e uma terceira para dar início ao processo do temporizador, acionar o relay e decrementar os minutos.
Ao decrementar até zero, o relay é desligado e o LED pisca indefinidamente para informar o usuário. Claro que a terceira tecla não faz nada se o temporizador está com zero minutos programado. Se o contador já estiver no limite de 99 minutos, a tecla #1 não incrementará se pressionada, e piscará o LED três vezes como informação de limite. O mesmo para a tecla #2 que decrementa, se o contador estiver com zero, essa tecla não irá decrementar, e piscará o LED três vezes para informar o usuário do limite. Não apertei muito o código, e ocupou em torno de 240 bytes na Flash, uma ninharia.
Abaixo o texto do código em assembly, e ao final o link para fazer download do arquivo hex a gravar na Flash do AtMega8. Fuses são H-Fuse:D9, L-Fuse:64, que basicamente seleciona Clock interno de 8MHz.
Essa versão de software (1.0) roda na mesma plaquinha desenhada pelo Rodrigo. A próxima versão, 7.1, permitirá incluir dois displays de sete segmentos para mostrar o valor do timer e nessa nova versão, haverá mudança de pinos para as teclas e LED, e não rodará na plaquinha atual.
Update: 28 Março 2014
O codigo fonte abaixo já é o novo, com 2 displays 7 Segmentos, e 398 bytes de código. Leia no texto abaixo a forma de uso e funcionamento.
;-----------------------------------------------;
;
; Title : AVR ATMEGA8 Relay Timer
; Author : Wagner Lipnharski
; : wagner@ustr.net
;
; Date : March 21 2014
; Versão : 7.1
;
; Comments : Original firmware in C
; : Rodrigo Pinheiro Matias
; ; rodrigopmatias@gmail.com
;
;---------------------------------------------;
/* Versão 7.1
Todas portas e pinos de I/O agora são fixos para melhor entendimento
e menos confusão educacional.
Esse firmware é para o AtMega8 servindo como um temporizador para
fotografia, ou qualquer outro uso.
Existe um LED, um Relay e tres teclas, Sw1, Sw2 e Sw3.
Ao ligar, o LED pisca 3 vezes rapidamente, o relay permanece
desativado, e o display de leds indica "00"
Ao pressionar momentaneamente Sw1, incrementa-se o tempo mostrado
no display em um minuto, o LED pisca para confirmar o incremento.
Mantendo SW1 pressionado por mais de 250ms (1/4 segundo), o contador
de tempo irá incrementar automaticamente a cada 1/4 de segundo, e só
irá parar ao liberar SW1 ou a contagem chegar a 99.
Ao pressionar momentaneamente Sw2, decrementa-se o tempo mostrado no
display em um minuto, o LED pisca para confirmar o decremento.
Mantendo SW2 pressionado por mais de 250ms (1/4 segundo), o contador
de tempo irá decrementar automaticamente a cada 1/4 de segundo, e só
irá parar ao liberar SW2 ou a contagem chegar a 00.
Ao pressionar Sw3 é dado início na temporização, o relay é energizado,
o ponto decimal do display LED irá piscar a cada segundo, o timer
decrementará até zero, quando então o relay será desligado, e o display
irá piscar 00 indefinidamente, para chamar a atenção do usuário que
deverá pressionar SW2 para reset na unidade.
Sw3 é ignorado se o contador de tempo = 00.
Com contador em zero, SW2 é ignorado e LED pisca 3 vezes.
Com contador em 99, SW2 é ignorado e LED pisca 3 vezes.
Essa versão não utiliza cristal nem oscilador externo, o AtMega8 roda
com o oscilador interno de 8MHz. Então, s pinos PB6 e PB7 estão
disponíveis para uso de I/O.
O software é feito para aceitar display 7 Segmentos LED tanto com
catodo comum, quanto anodo comum, devendo-se definir na variável
ANODE abaixo, "1" = anodo comum, e "0" se for utilizar catodo comum.
*/
; =========================== Hardware Connections
; Sw1: PortB.0 to Ground
; SW2: PortB.1 to Ground
; SW3: PortB.2 to Ground
;
; LED: PortB.7 to R330 to Ground
;
; RELAY: PortB.6 to R2k to NPNBase, NPNEmitter to Ground
; NPNCollector to Relay to +12V
;
; 7SegCommon-Digit1: PortC.0
; 7SegCommon-Digit2: PortC.1
; 7Seg.a: PortD.0 +-gg-+
; 7Seg.b: PortD.1 b f
; 7Seg.c: PortD.2 b f
; 7Seg.d: PortD.3 +-aa-+
; 7Seg.e: PortD.4 c e
; 7Seg.f: PortD.5 c e
; 7Seg.g: PortD.6 +-dd-+
; 7Seg.h: PortD.7 h
;
;---------------------------------------------------------;
;
;
;
.nolist
.include "m8def.inc"
.list
.def blink = r13
.def SREGB = r15
.def Temp = r16
.def Temp3 = r17
def Temp2 = R18
.def Relay = R20
.def RTEMPO = R21
.def Pubtim = R22
.def decalc = R23
.def SegDiv = R24
.def TIM1 = R27
.def TIM2 = R29 ; Need to be this way (YH reg)
.def TIM3 = R28 ; Need to be this way (YL reg)
.equ tempomax = 0x99
.equ anode = 1 ; enter "0" for common catode
; Switches Port: PORTB
; 7Seg Common Port: PORTC
; 7Seg Data Port: PORTD
; Relay Port: PORTB
; LED Port: PORTB
.equ SWPORT = PINB
.equ SW1PIN = 0
.equ SW2PIN = 1
.equ SW3PIN = 2
.equ SWPINS = ((1 << sw1pin) | (1 << sw2pin) | (1 << sw3pin))
.equ relayport = portb
.equ relaypin = 6
.equ ledport = portb
.equ ledpin = 7
.equ DC1Pin = 0
.equ DC2Pin = 1
; .equ DC1Pinx = 1 << Dc1Pin
; .equ DC2Pinx = 1 << Dc2Pin
.cseg
.org 0
;----------------------------------------;
; ATMEGA8 INTERRUPT TABLE ;
;----------------------------------------;
rjmp RESET ; Reset Handler
rjmp RESET ; EXT_INT0 ; IRQ0 Handler
rjmp RESET ; EXT_INT1 ; IRQ1 Handler
rjmp RESET ; TIM2_COMP ; Timer2 Compare Handler
rjmp RESET ; TIM2_OVF ; Timer2 Overflow Handler
rjmp RESET ; TIM1_CAPT ; Timer1 Capture Handler
rjmp RESET ; TIM1_COMPA ; Timer1 Compare A Handler
rjmp RESET ; TIM1_COMPB ; Timer1 Compare B Handler
rjmp RESET ; TIM1_OVF ; Timer1 Overflow Handler
rjmp TIM0_OVF ; TIM0_OVF ; Time0 Overflow Handler
; rjmp RESET ; SPI_STC ; SPI Transfer Complete Handler
; rjmp RESET ; USART_RXC ; USART RX Complete Handler
; rjmp RESET ; USART_UDRE ; UDR Empty Handler
; rjmp RESET ; USART_TXC ; USART TX Complete Handler
; rjmp RESET ; ADC ; ADC Conversion Complete Handler
; rjmp RESET ; EE_RDY ; EEPROM Ready Handler
; rjmp RESET ; ANA_COMP ; Analog Comparator Handler
; rjmp RESET ; TWSI ; Two-wire Serial Interface Handler
; rjmp RESET ; SPM_RDY ; Store Program Memory Ready Handler
;=================================================================;
;===========| |==============================;
;===========| INTERRUPT TIMER0_OVF |==============================;
;===========| |==============================;
;=================================================================;
; This interruption is called every time the Timer0 counter
; increments from xFF to x00.
; Timer0 is programed to inc automatically every 2ms
; In this interrupt:
; - PUBTIM is decremented until zero and stay.
; - 16 bits Tim2:tim3 is decremented by one, when it reaches zero,
; is reload with value 30,000, that means 1 minute, and Tim1
; (minutes) is decremented by one. When reaches zero, the Timer
; function is done, relay and LED are turned OFF.
; - Sreg is saved during this interrupt.
; - No other registers are changed.
;=================================================================;
TIM0_OVF: in sregb, sreg ; Save Status Register
tst blink
brne z2
; -------------------
; 2x 7 Seg Multiplex
; Dig0: PortC3
; Dig1: PortC4
; Segments: PD0..PD7
ldi zh, high(2*table1)
ldi zl, low(2*table1)
inc segdiv
andi segdiv,0x07
breq Dig0
cpi segdiv, 1
breq Dig1
z2: sbi ddrc, dc1pin
sbi ddrc, dc2pin
ser decalc
rjmp z1
Dig0: sbi ddrc, dc1pin
cbi ddrc, dc2pin
.if anode == 1
sbi PortC, DC1Pin
cbi PortC, DC2Pin
.else
cbi PortC, DC1Pin
sbi PortC, DC2Pin
.endif
mov decalc, tim1
rjmp seg98
Dig1: cbi ddrc, dc1pin
sbi ddrc, dc2pin
.if anode == 1
cbi PortC, DC1Pin
sbi PortC, DC2Pin
.else
sbi PortC, DC1Pin
cbi PortC, DC2Pin
.endif
seg97: mov decalc, tim1
swap decalc
seg98: andi decalc,0x0F
seg99: add zl,decalc
brcc pc+2
inc zh
lpm decalc, Z
tst segdiv
brne pc+5
tst tim1
breq pc+3
sbrc tim2,1
ori decalc, 0x80
.if anode == 1
com decalc
.endif
z1: out PortD, decalc
z0: tst pubtim ; PubTim ?
breq pc+2 ; Zero, keep PubTim zero
dec pubtim ; decrement PubTim
tst relay ; Relay Active? Decrement?
breq z9
tst tim1 ; Minutes zero?
breq z9 ; Get Out of Interruption
sbiw tim2:tim3,1 ; -2 ms from minute counter
brne z9 ; If not zero, return from Interrupt
ldi tim2,high(30000) ; Minutes not zero, reload minute counter
ldi tim3,low(30000)
subi tim1,1
brhc pc+2
subi tim1,6
tst tim1
brne pc+3
clr relay ; Timer is Zero, Relay Bit Off
cbi RelayPort, RelayPin ; Turn off Relay Port Pin
z9: out sreg, sregb ; Retrieve Old Status Register
reti ; Return from Interrupt
;===============================================;
;===========| |============;
;===========| S E T U P |============;
;===========| |============;
;===============================================;
RESET:
Start: cbi relayport, relaypin
ldi Temp, low(Ramend)
out SPL, Temp
ldi Temp, high(Ramend)
out SPH, Temp
;-------------------------------;
; CLEAR ALL RAM BYTES
;-------------------------------;
ldi YL, low(SRAM_START)
ldi YH, high(SRAM_START)
ldi XL, low(SRAM_SIZE)
ldi XH, high(SRAM_SIZE)
clr r10
st Y+, r10
sbiw XH:XL, 1
BRNE PC-2
;-------------------------------;
sbi ACSR, 7 ; Disable Analog Comparator
;-------------------------------;
ldi temp, swpins ; Switches as Input
com temp ; Invert Bits
out DDRB, Temp ; Setup Switch Port Direction
;-------------------------------;
ser Temp ; Define PORTs
out PORTC, Temp ; PortC pins HIGH or Pullup High
out PORTD, Temp ; PortD pins HIGH or Pullup High
out DDRC, Temp ;
out DDRD, Temp ;
cbr temp, (1 << relaypin) ; Drop Relay
out PORTB, Temp ; PortB pins HIGH or Pullup High
;-------------------------------;
ldi temp, 0b00000011 ;
out tccr0, temp ;
ldi temp, 0b00000001 ; TOIE0
out timsk, temp ;
;-------------------------------;
clr tim1
clr tim2
clr tim3
clr relay
clr blink
;-------------------------------;
sei ; Set Interrupt Enable
;-------------------------------;
rcall PubTim250 ; Wait 250ms
cbi LedPort, LedPin ; LED Off
;===============================================;
;===========| |============;
;===========| MAIN ROUTINE |============;
;===========| |============;
;===============================================;
TopDownLimit:
Rcall BlinkLedTrice
SS1: SBIS SWPort, SW2pin ; Skip Next if SW2 not pressed
rjmp SW2Pressed ; If SW2 Pressed Jump
SBIS SWPort, SW3pin ; Skip Next if SW3 not pressed
rjmp SW3Pressed ; If SW3 Pressed Jump
SBIC SWPort, Sw1pin ; Skip Next if SW1 pressed
rjmp SS1 ; Return to Loop
;===============================================;
;===========| |============;
;===========| SUB ROUTINES |============;
;===========| |============;
;===============================================;
SW1Pressed:
rcall DebDown ; SW1 +1 pressed, debounce pressed
cpi tim1, tempomax ; If counter = 99
brsh TopDownLimit ; LED blinks 3 times, ignore Sw1
Subi tim1, -7 ; Ic counter x9 or +xA=>x0
subi tim1,6 ; This is BCD counter
Rcall BlinkLedOnce ; Blink LED
rcall DebUp ; Debounce Switch UP
Rjmp SS1 ; After Debounce, keep waiting key
DebDown: ldi pubtim, 5 ; Debounce Switch Down
in temp3, SWPort ; Any switch down does it
andi temp3, SWPins ;
cpi temp3, SWPins ;
breq pc-3 ; If none is Down, keep waiting
tst pubtim ; If some is down, 10ms ended?
brne pc-5 ; If no, keep waiting
ret ; If 10ms ended, return
DebUP: ldi temp3, 50 ; AutoPress SW1 & SW2
dec temp3 ;
brne pc+2 ;
ret ; If Switch Pressed, Repeat
ldi pubtim, 5 ; 10ms debounce up
sbic SWPort, SW1Pin ; If Up SW1, debounce 10ms
rjmp pc+3 ;
sbis SWPort, SW2Pin ; If Up SW2, debounce 10ms
rjmp pc-7 ; If some is down, dec autopress
tst pubtim ; if debounce 10ms is ended
brne pc-5 ; if not ended
ret ; if ended, return
SW3UpDebounce:
Ldi PubTim, 5 ; Switch 3 Up Debounce is exclusive
Sbis SWPort, Sw3pin ; ... since there is not repeat
Rjmp PC-2 ;
Tst PubTim ; 10ms is ended?
Brne PC-3 ; No, keep debouncing
Ret ; Yes, return
BlinkLedTrice:
Rcall PC+2 ; Call Blink fast #1
Rcall PC+1 ; Call Blink fast #2
Sbi LedPort, LedPin ; Call Blink fast #3
Ldi PubTim, 35 ; LED On
Rcall PubTimGo ; Wait 70ms
Cbi LedPort, LedPin ; LED Off
Ldi PubTim, 50 ; Wait 100ms
Rjmp PubTimGo
BlinkLedOnce:
sbi LedPort, LedPin ; LED On
ldi PubTim, 50 ; 100ms
rcall PubTimGo ; Wait it
cbi LedPort, LedPin ; LED Off
PubTim250: ldi PubTim, 125
PubTimGo: tst PubTim ; Zero Already?
brne pc-1 ; No, keep waiting
ret ; return to caller
SW3Pressed:
rcall DebDown ; Debounce S3 Down
rcall SW3UpDebounce ; Debounce S3 Up
tst tim1 ; Selected Time > 0 ?
breq sw40 ; No, it is zero, skip everything
ldi tim2,high(30000) ; 60,000 ms counter
ldi tim3,low(30000)
sbi relayport, relaypin ; TURN ON Relay Port Pin UP
sbi ledport, ledpin ; Led UP
ser relay ; Relay Flag UP
tst relay ; Wait RElay Flag DOWN on TIMER OFF
brne pc-1 ; Relay port Pin returns OFF from interrupt
inc blink
rcall BlinkLedTrice ; Blink LED Three times
clr blink
rcall BlinkLedOnce ; Blink LED once
sbic SWPort, SW3pin ; Skip next if S3 is pressed
rjmp pc-5 ; Not Pressed, keep blinking
sw39: rcall DebDown ; Debounce S3 Down
rcall SW3UpDebounce ; Debounce S3 Up
sw40: rjmp SS1 ; REturn to Main Routine
; 0 1 2 3 4 5 6 7 8 9
; A b C d E F
Table1: .db 0x7E,0x30,0x6D,0x79,0x33,0x5b,0x5f,0x70,0x7f,0x7b
.db 0x77,0x1f,0x4e,0x3d,0x4f,0x47
.exit
Nenhum comentário:
Postar um comentário