O Rodrigo Pinheiro Matias, colega da PICLISTBR, teve a idéia 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, e prometi a ele reescrever o firmware desse mesmo temporizador, com exatamente as mesmas funções, em Assembly.
Esse temporizador permite programar de 1 a 99 minutos, para tal utiliza tres teclas, uma para incrementar o tempo selecionado em minutos, outra para decrementar e uma terceira para dar inicio ao processo do temporizador, acionar o relay e decrementar os minutos.
Ao decrementar até zero, o relay é desligado e o LED pisca indefinamente 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 tres 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 tres 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
; -----------------------------------------------; ; ; 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, e 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, ; os 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 CompareA Handler rjmp RESET ; TIM1_COMPB ; Timer1 CompareB 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 progrmd to inc automaticaly evry 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 |