sábado, 25 de setembro de 2021

TIMER FOTOGRÁFICO em ASSEMBLY AVR

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