Mostrando postagens com marcador FOTOGRÁFICO. Mostrar todas as postagens
Mostrando postagens com marcador FOTOGRÁFICO. Mostrar todas as postagens

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