Pfad: Home => AVR-DE => Anwendungen => Multitimer tn24 => Assembler Quellcode
Multitimer Klein AVR-Anwendungen

A multitimer mit ATtiny24
Assembler Quellcode
Logo

Quellcode für den Multitimer tn24

HTML-formatierter Assembler-Quellcode, der Original-Quellcode kann hier heruntergeladen werden.

;
; ***************************************
; * Multitimer mit ATtiny24 und 12 LEDs *
; * Version 1.0 August 2018             *
; * (C)2018 www.avr-asm-tutorial.net    *
; ***************************************
;
.nolist
.include "tn24def.inc"
.list
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Device: ATtiny24, Package: 14-pin-PDIP_SOIC
;
;                ______
;             1 /      |14
;       +3V o--|VCC GND|--o GND
; Taste Dwn o--|PB0 PA0|--o Led An 0
;  Taste Go o--|PB1 PA1|--o Led An 1
;     RESET o--|RES PA2|--o Led An 2
;  Taste Up o--|PB2 PA3|--o Led An 3
;        NC o--|PA7 PA4|--o Led Cat 0
; Led Cat 2 o--|PA6 PA5|--o Led Cat 1
;              |_______|
;
; *************************************
;    H A R D W A R E   T E S T I N G
; *************************************
;
; Alle Hardware Testcodes beginnen bei
;   000000 und enden in einer unendlichen
;   Schleife
;
; Testen der LEDs
;   Die LEDs LED5 bis LED420 werden
;     reihum eingeschaltet
;   Erste Runde: alle in gruen
;   Zweite Runde: alle in rot
.equ Debug_Leds = 0 ; 1 = Test, 0=Normal
;
.if Debug_Leds == 1
.equ cDelay=50000
  ldi R16,0 ; LEDs aus
  clr R18
Debug_Led1:
  ldi ZH,High(2*LedTable)
  ldi ZL,Low(2*LedTable)
  mov R17,R16
  lsl R17
  add ZL,R17
  ldi R17,0
  adc ZH,R17
  lpm R17,Z+
  eor R17,R18
  out PORTA,R17
  lpm R17,Z
  out DDRA,R17
  ldi ZH,High(cDelay)
  ldi ZL,Low(cDelay)
Debug_Led2:
  sbiw ZL,1
  brne Debug_Led2
  inc R16
  cpi R16,13
  brcs Debug_Led1
  clr R16
  ldi R17,0x7F
  eor R18,R17
  rjmp Debug_Led1
  .endif
;
; Testen der Taster
;   Liest die drei Taster
;     Status der Schalter wird angezeigt
;       LED5 (Down), LED60 (Run/Stop) und LED240 (Up)
;     solange der Taster gedrueckt ist 
.equ Debug_Switches = 0 ; 1=Test, 0=Normal
;
.if Debug_Switches == 1
Debug_Sw:
  ldi R16,0x07
  out PORTB,R16
  in R17,PINB
  ldi R16,1
  sbrs R17,0
  rjmp Debug_Sw_Nmbr
  ldi R16,5
  sbrs R17,1
  rjmp Debug_Sw_Nmbr
  ldi R16,9
  sbrs R17,2
  rjmp Debug_Sw_Nmbr
  clr R16
Debug_Sw_Nmbr:
  ldi ZH,High(2*LedTable)
  ldi ZL,Low(2*LedTable)
  lsl R16
  add ZL,R16
  ldi R16,0
  adc ZH,R18
  lpm R16,Z+
  out PORTA,R16
  lpm R16,Z+
  out DDRA,R16
  clr R16
Debug_Sw_Nmbr1:
  dec R16
  brne Debug_Sw_Nmbr1
  rjmp Debug_Sw
  .endif
;
; *************************************
;  E I N S T E L L B A R E   K O N S T
; *************************************
;
.equ clock=1000000 ; Taktfrequenz
;
; Lednummer bei Programmbeginn
.equ cStart = 5 ; Kann zwischen 1 und 12 sein
;
; Entprellzyklen, in 0,1 Sekunden
.equ cDebounce = 2 ; Anzahl Perioden
;
; Inaktivitaetsdauer bis LED aus
;   in Zehntelsekunden
.equ cAutoOff = 100 ; Automatisch aus
;
; *************************************
;  F E S T E  &  A B G E L.  K O N S T
; *************************************
;
; TC1 erzeugt 0,1 s Takt
.equ cTc1Presc = 8 ; TC1 Vorteiler
.equ cTc1Div = clock / cTc1Presc / 10 ; TC1-Teiler
.equ cTc1CmpA = cTc1Div-1 ; Compare-A-Wert
;
; Tasten-Entprell-Wert
.equ cTgl = cDebounce + 1
;
; **********************************
;         R E G I S T E R
; **********************************
;
; frei: R0 bis R9
.def rOff = R10 ; Abschaltwert LED
.def rSelect = R11 ; Ausgewaehlte LED Nummer
.def rState = R12 ; Aktive LED
.def rPort = R13 ; LED Output-Wert PORTA
.def rDdr = R15 ; LED Richtungs-Wert DDRA
.def rmp = R16 ; Vielzweckregister
.def rFlag = R17 ; Flaggenregister
  .equ bRun = 0 ; Zaehler laeuft
  .equ bTimeOut = 1 ; Ende der LED-Anzeige
  .equ bLedTest = 7 ; LED-Test zu Beginn
.def rCnt = R18 ; Zaehler 0,1 Sekunden
.def rTgl = R19 ; Entprellzaehlerregister
; frei: R20 bis R23
.def rSecL = R24 ; Sekundenzaehler, LSB
.def rSecH = R25 ; dto., MSB
; benutzt: R27:R26 = X fuer Multiplikation
; frei: R29:R28 = Y
; benutzt: R31:R30 = Z fuer diverse Zwecke
;
; **********************************
;            C O D E
; **********************************
;
.cseg
;
; **************************************
; R E S E T  &  I N T - V E K T O R E N
; **************************************
  rjmp Main ; Reset Vektor
  reti ; EXT_INT0, unbenutzt
  reti ; PCI0, unbenutzt
  rjmp Pcint1Isr ; PCI1
  reti ; WATCHDOG, unbenutzt
  reti ; ICP1, unbenutzt
  rjmp Tc1CmpAIsr ; OC1A
  reti ; OC1B, unbenutzt
  reti ; OVF1, unbenutzt
  reti ; OC0A, unbenutzt
  reti ; OC0B, unbenutzt
  reti ; OVF0, unbenutzt
  reti ; ACI, unbenutzt
  reti ; ADCC, unbenutzt
  reti ; ERDY, unbenutzt
  reti ; USI_STR, unbenutzt
  reti ; USI_OVF, unbenutzt
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; PCINT1 Externer Int
;   wird bei jeder Aenderung an den
;     Tasteneingaengen ausgefuhrt
;   Identifiziert die gedrueckte Taste und
;     fuehrt entsprechende Aktionen aus
Pcint1Isr:
  tst rTgl ; Pruefe Entprellregister
  brne PcInt1Isr9 ; Nicht Null, ignoriere Taste
  in rmp,PINB ; Lese Tasten
  ori rmp,0b11111000 ; Setze alle Nicht-Tasten-Bits
  cpi rmp,0xFF ; Keine Taste gedrueckt?
  breq Pcint1Isr9 ; Ja, Ende
  ldi ZL,cTgl ; Entprellregister auf Startwert 
  mov rTgl,ZL
  sbrs rmp,1 ; Run/Stop-Taste?
  rjmp KeyRun ; Ja
  sbrc rFlag,bRun ; Laeuft Zaehler?
  reti ; Ja, keine weitere Tastenauswertung
  ldi rSecH,High(cAutoOff) ; Setze Inaktivitaetszaehler
  ldi rSecL,Low(cAutoOff)
  sbrs rmp,0 ; Down-Taste?
  rjmp KeyDown ; Ja
  sbrs rmp,2 ; Up-Taste?
  rjmp KeyUp ; Ja
PcInt1Isr9:
  reti
;
KeyDown:
  mov rmp,rSelect ; Gewaehlte LED Nummer
  cpi rmp,1 ; Bei Eins?
  breq KeyDown1 ; Ja, ignoriere Taste
  dec rSelect ; Gewaehlte LED Nummer abwaerts
  mov rState,rSelect
  rcall SetLed ; Zeige LED an
KeyDown1:
  reti
;
KeyUp:
  mov rmp,rSelect ; Gewaehlte LED Nummer
  cpi rmp,12 ; Unter 12?
  brcs KeyUp1 ; Ja
  ldi rmp,11 ; Lade Nummer 11
  mov rSelect,rmp ; in gewaehlte LED Nummer
KeyUp1:
  inc rSelect ; Erhoehe gewaehlte LED Nummer
  mov rState,rSelect ; Kopiere in angezeigte
  rcall SetLed ; Zeige LED an
  reti
;
KeyRun:
  ldi rmp,1<<bRun ; Invertier bRun-Flagge
  eor rFlag,rmp
  sbrs rFlag,bRun ; Ueberspringe naechste Instruktion wenn bRun Eins
  rjmp KeyStop ; Stoppen
  ldi ZH,High(2*LedDur) ; Z auf Tabelle mit Zeitdauer
  ldi ZL,Low(2*LedDur)
  mov rmp,rSelect ; Gewaehlte Nummer kopieren
  lsl rmp ; Mal zwei
  add ZL,rmp ; und zur Tabellenadresse addieren, LSB
  ldi rmp,0
  adc ZH,rmp ; Ueberlauf zu MSB addieren
  lpm rSecL,Z+ ; Sekundenzaehler aus Tabelle laden, LSB
  lpm rSecH,Z ; dto., MSB
  mov rState,rSelect ; In aktuelle LED kopieren
  ldi rmp,9 ; Abschaltwert ganz kurz
  mov rOff,rmp
  ldi rmp,10 ; Zehntelsekundenzaehler auf 10
  mov rCnt,rmp
  rcall SetLed ; LED anzeigen
  reti
;
KeyStop:
  ldi rSecH,High(cAutoOff) ; Lade Inaktivitaetszeit
  ldi rSecL,Low(cAutoOff)
  mov rState,rSelect ; Gewaehlte LED in angezeigte
  rcall SetLed ; LED anzeigen
  reti
;
; TC1 Compare A Interrupt
;   wird alle 0,1 s ausgefuehrt
;   Wenn Zaehlen nicht aktiv: Zehntelsekundenzaehler abwaerts
;     Wenn Null: LED abschalten
;   Wenn Zaehlen aktiv: Zehntelsekundenzaehler abwaerts
;     Wenn nicht Null: Zehntelsekunde = Abschaltregister?
;       Wenn gleich: LED aus
;     Wenn Null: Sekundenzaehler abwaerts
;       Wenn Null: Zaehlen aus, gewaehlte LED anzeigen
;       Wenn nicht Null: Sekunde in LED umrechnen und
;         Abschaltwert ausrechnen, LED anzeigen
Tc1CmpAIsr:
  sbrs rFlag,bLedTest ; LED Testphase?
  rjmp Tc1CmpAIsrRun ; Nein
  inc rState ; Naechste LED
  ldi rmp,13 ; Letzte LED?
  cp rState,rmp ; Vergleiche
  brcs Tc1CmpAIsrLed ; Nein, weiter LED anzeigen
  rjmp Tc1CmpAIsrStart ; Ja, starte
Tc1CmpAIsrRun:
  tst rTgl ; Pruefe Prellregister
  breq Tc1CmpAIsrRun1 ; Ist null, nicht vermindern
  dec rTgl ; Prellregister vermindern
Tc1CmpAIsrRun1:
  sbrs rFlag,bRun ; Zaehlen aktiv?
  rjmp Tc1CmpAIsrAuto ; Nein, Inaktivitaet testen
  dec rCnt ; Zahler 0,1 s abwaerts
  breq Tc1CmpAIsrSec ; Null, Sekunden zaehlen
  cp rCnt,rOff ; Ende LED an?
  brne Tc1CmpAIsrReti ; Nein
  clr rmp ; Loesche LED
  out DDRA,rmp
  reti
Tc1CmpAIsrSec:
  ldi rCnt,10 ; Neustart 0,1 s Zaehler
  sbiw rSecL,1 ; Sekunden abwaerts
  breq Tc1CmpAIsrStart ; Ende Sekunden, starte neu
  rcall Sec2Led ; Umrechnen Sekunden in LED und rOff
  rjmp Tc1CmpAIsrLed ; LED anzeigen
Tc1CmpAIsrAuto:
  sbrc rFlag,bTimeOut ; Timeout Inaktivitaet erreicht?
  reti ; Nein
  sbiw rSecL,1 ; Inaktivitaetszaehler abwaerts
  brne Tc1CmpAIsrBlink ; Nicht bei Null
  clr rmp ; Schalte LED aus
  out DDRA,rmp
  sbr rFlag,1<<bTimeOut ; Setze Timeout-Flagge
  reti
Tc1CmpAIsrBlink:
  ldi rmp,0 ; Blinke gruene LED
  sbrs rSecL,0 ; Zehntelsekunde Bit 0 = 1?
  out DDRA,rmp ; Nein, aus
  sbrc rSecL,0 ; Zehntelsekunde Bit 0 = 0?
  out DDRA,rDdr ; Nein, LED an
  reti
Tc1CmpAIsrStart:
  ; Starte Ablauf neu
  cbr rFlag,(1<<bRun)|(1<<bLedtest) ; bRun und bLedtest aus
  mov rState,rSelect ; Angezeigt = Gewaehlt
  ldi rSecH,High(cAutoOff) ; Setze Inaktivitaetszaehler
  ldi rSecL,Low(cAutoOff)
Tc1CmpAIsrLed:
  rcall SetLed ; Zeige LED an
Tc1CmpAIsrReti:
  reti

; ***********************************
;  I S R   U N T E R P R O G R A M E
; ***********************************
;
;
; LED aus Sekundenzaehler berechnen
;   Sekunden in rSecH:rSecL in Aktiv-LED
;     in rState und LED-Aus-Zeit in rOff
Sec2Led:
  ; Zeit in LED wandeln
  ldi ZH,High(2*LedDur+2)
  ldi ZL,Low(2*LedDur+2)
  clr rState ; rState ist LED # Zaehler
Sec2Led1:
  inc rState ; Erhoehe Zahler
  lpm XL,Z+ ; Lese LSB Dauer aus Tabelle in X
  lpm XH,Z+
  sec ; Carry auf Eins
  cpc rSecL,XL ; Vergleiche Zeit LSB
  cpc rSecH,XH ; Vergleiche Zeit MSB
  brcc Sec2Led1 ; Wiederhole mit naechstem Tabellenwert
  ; Lese niedrigeren Tabellenwert nach X
  sbiw ZL,4 ; Auf vorletzten Tabellenwert
  lpm XL,Z+ ; Lese LSB Tabellenwert
  lpm XH,Z ; dto., MSB
  ; Differenz Zeit - Tabellenwert
  mov ZH,rSecH ; Kopiere Zeit nach Z
  mov ZL,rSecL
  sub ZL,XL ; Subtrahiere Tabellenwert
  sbc ZH,XH
  mov XH,ZH ; Kopiere nach X
  mov XL,ZL
  ; Hole Multiplikator fuer LED Nummer
  ldi ZH,High(2*MultTab) ; Z auf Tabelle Multiplikatoren
  ldi ZL,Low(2*MultTab)
  add ZL,rState ; LED Nummer addieren, LSB
  ldi rmp,0
  adc ZH,rmp ; Ueberlauf MSB
  lpm rmp,Z
  ; Teste Multiplikator = 0
  tst rmp
  brne Sec2Led2 ; Nicht Null, multipliziere
  ; LED5 or LED10
  ldi ZH,High(2*TenTable) ; Zehnertabelle
  ldi ZL,Low(2*TenTable)
  add ZL,XL ; Addiere Zeitdifferenz LSB
  adc ZH,XH ; dto., MSB
  lpm rOff,Z ; Lese rOff-Wert aus Tabelle
  ret
Sec2Led2:
  ; Ab LED20, Multiplikator nicht Null, multipliziere
  clr ZL ; Z ist Ergebnis
  clr ZH
Sec2Led3:
  tst rmp ; Fertig multipliziert?
  breq Sec2Led5 ; Ja, Ende Multiplikation
  lsr rmp ; Dividiere Multiplikator durch 2, niedrigstes Bit in Carry
  brcc Sec2Led4 ; Carry Null, nicht zum Ergebnis addieren
  add ZL,XL ; Addiere Multiplikator
  adc ZH,XH
Sec2Led4:
  lsl XL ; Multipliziere mit 2
  rol XH
  rjmp Sec2Led3 ; Weiter multiplizieren
Sec2Led5:
  inc ZH ; Plus Eins
  mov rOff,ZH ; In rOff
  ret
;
; Zeitdauertabelle
LedDur:
.dw 0,5,10,20,30
.dw 60,90,120,180
.dw 240,300,360,420
.dw 65535 ; End of table
;
; Multiplikatortabelle
MultTab:
.db 0,0,0,230,230,77,77,77,38,38,38,38,38,1
;
; Fuer die Sekunden zwischen 10 und 1 ist es einfacher
;   die Abschaltzeit aus einer eigenen Tabelle abzulesen
TenTable:
.db 0,2,4,6,8,9
;
; Schalte die LED in rState an
SetLed:
  mov rmp,rState ; Aktuelle LED Nummer
  lsl rmp ; Multipliziere mit 2
  ldi ZH,High(2*LedTable) ; Zeige auf LED-Tabelle
  ldi ZL,Low(2*LedTable)
  add ZL,rmp ; Addiere zu LSB
  ldi rmp,0
  adc ZH,rmp ; und Ueberlauf zu MSB
  lpm rPort,Z+ ; Port lesen
  lpm rDdr,Z ; Richtung lesen
  ldi rmp,0x7F ; Invertieren in rot
  sbrc rFlag,bRun ; Ueberspringe wenn nicht zaehlen
  eor rPort,rmp ; Invertiere Port
  out PORTA,rPort ; Output schreiben
  out DDRA,rDdr ; Richtung schreiben
  cbr rFlag,1<<bTimeOut ; Timeout-Flagge loeschen
  ret
;
; LED-Tabelle der Ports
;   1. Byte: PORT, 2. Byte: DDR
LedTable:
.db 0b00000000,0b00000000 ; #0, LED aus
.db 0b00010000,0b00010001 ; #1, LED 5 gruen
.db 0b00010000,0b00010010 ; #2, LED 10 gruen
.db 0b00010000,0b00010100 ; #3, LED 20 gruen
.db 0b00010000,0b00011000 ; #4, LED 30 gruen
.db 0b00100000,0b00100001 ; #5, LED 60 gruen
.db 0b00100000,0b00100010 ; #6, LED 90 gruen
.db 0b00100000,0b00100100 ; #7, LED 120 gruen
.db 0b00100000,0b00101000 ; #8, LED 180 gruen
.db 0b01000000,0b01000001 ; #9, LED 240 gruen
.db 0b01000000,0b01000010 ; #10, LED 300 gruen
.db 0b01000000,0b01000100 ; #11, LED 360 gruen
.db 0b01000000,0b01001000 ; #12, LED 420 gruen
;
; ***********************************
;  H A U P T P R O G R A M   I N I T
; ***********************************
;
Main:
  ; Stapel initiieren
  .ifdef SPH
    ldi rmp,High(RAMEND) ; Setze SPH bei ATtiny44/84
    out SPH,rmp
    .endif
  ldi rmp,Low(RAMEND)
  out SPL,rmp ; Initiiere LSB Stapelzeiger
  ; LED = 0
  clr rState
  rcall SetLed ; LED anzeigen
  ; Setze Startwerte
  ldi rmp,cStart
  mov rSelect,rmp ; Start mit vorgewaehltem Wert
  ldi rSecH,High(cAutoOff) ; Inaktivitaetswert
  ldi rSecL,Low(cAutoOff)
  ; Initiiere PCINT fuer Tasten
  ldi rmp,(1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2) ; Pull-Ups an
  out PORTB,rmp ; und in Output-Port
  clr rmp ; Konfigurieren als Inputs
  out DDRB,rmp
  ldi rmp,(1<<PCINT8)|(1<<PCINT9)|(1<<PCINT10) ; Maskiere Tasteneingaenge
  out PCMSK1,rmp
  ldi rmp,1<<PCIE1 ; Enable Interrupt PCINT1
  out GIMSK,rmp
  ; Initiiere TC1
  ldi rFlag,(1<<bLedTest)|(1<<bRun) ; LED Testphase an
  ldi rmp,High(cTc1CmpA) ; Setze Vergleichswert CTC
  out OCR1AH,rmp ; MSB
  ldi rmp,Low(cTc1CmpA)
  out OCR1AL,rmp ; LSB
  clr rmp ; Mode TC1 Port A
  out TCCR1A,rmp ; Kontrollport TC1 A
  ldi rmp,(1<<WGM12)|(1<<CS11) ; CTC Compare A, Vorteiler=8
  out TCCR1B,rmp ; Kontrollport TC1 B
  ldi rmp,1<<OCIE1A ; TC1 Compare A Interrupt enable
  out TIMSK1,rmp ; in TC1 Interruptmaske
  ; Enable Sleep
  ldi rmp,1<<SE ; Schlafmodus Idle
  out MCUCR,rmp
  ;
  ; Enable Enterrupts
  sei ; Enable Interrupts
;
; **********************************
;   P R O G R A M M S C H L E I F E
; **********************************
;
Loop:
  sleep
  rjmp loop
;
; Ende Quellcode
;



Lobpreisungen, Fehlerberichte, Geschimpfe und Spam bitte via Kommentarseite an mich.

Zum Seitenanfang

©2018 by http://www.avr-asm-tutorial.net