Pfad: Home => AVR-Übersicht => Anwendungen => Langzeit-Timer tn25 => Quellcode   This page in english: Flag EN
Langzeit-Timer mit tn25 Anwendungen von
AVR-Einchip-Prozessoren AT90S, ATtiny, ATmega und ATxmega
Assembler-Quellcode Langzeit-Timer mit ATtiny25
Logo

Assembler-Quellcode für den Langzeit-Timer mit einem ATtiny25

Langzeit-Timer mit ATtiny25 Den Quellcode im Assembler-Format gibt es hier. Die zugehörige Include-Datei mit den Tonleiter-Frequenzen gibt es hier.

;
; *********************************************
; * Langzeit-Timer mit LED/Poti/Lautsprecher  *
; * Geschrieben fuer ATtiny25, V1, Maerz 2021 *
; * (C)2021 by http://avr-asm-tutorial.net    *
; *********************************************
;
.nolist
.include "tn25def.inc" ; Kann ein ATtiny25, 45 oder 85 sein
.list
;
; Zweck: Timer mit einstellbarer Intervallzeit
;       LED ist rot ueber eine laengere Zeit mit
;       abnehmender Helligkeit und dann gruen mit
;       zunehmender Helligkeit ueber eine kuerzere
;       Zeit. Bei Umschalten der Farbe werden zwei
;       programmierbare Toene auf dem Lautsprecher
;       ausgegeben (die Tonleiterfrequenzen in
;       Milli-Hertz gibt es als Include-Datei dazu).
;       Einstellungen um Testen der Hardware folgen,
;       Einstellungen zum Timer in der Sektion
;       "Einstellbare Kostanten".
;
; **********************************
;    D E B U G   H A R D W A R E
; **********************************
;
; Debugging der Duo-LED
.equ cDebugLed = 0 ; 1 = Blinken der LED
;
; Debug des Lautsprecher-Ausgangs
.equ cDebugSpk = 0 ; 1 = An, 0 = Normal
;
; Debug des ADC-Eingangs
.equ cDebugAdc = 0 ; 1 = Debug, 0 = Normal
; 
; Debug des Jumper-Eingangs an PA2
.equ cDebugPA2 = 0 ; 1 = Debug, 0 = Normal
;
.equ cDebug =(cDebugLed+cDebugSpk+cDebugAdc+cDebugPA2)>0
;
; **********************************
;        H A R D W A R E
; **********************************
;
; Typen: ATtiny25/45/85, Packung: 8-pin-PDIP_SOIC
;
;            _________
;         1 /         |8
; Reset o--|RESET  VCC|--o + Ub
;  Poti o--|ADC3   PB2|--o Jumper 1, SCK
;   LSP o--|OC1B   PB1|--o LED rote Anode, MISO
;  - Ub o--|GND    PB0|--o LED gruene Anode, MOSI
;        4 |__________|5
;
; **********************************
;  P O R T S   U N D   P I N S
; **********************************
;
.equ pO = PORTB ; Ausgangsport
.equ pD = DDRB ; Richtungsport
.equ pI = PINB ; Eingangsport
.equ bAnRdO = PORTB1
.equ bAnRdD = DDB1
.equ bAnGnO = PORTB0
.equ bAnGnD = DDB0
.equ bSpkO = PORTB4
.equ bSpkD = DDB4
.equ bJ1O = PORTB2
.equ bJ1D = DDB2
.equ bJ1I = PINB2
;
; *********************************************
;  E I S T E L L B A R E   K O N S T A N T E N
; *********************************************
;
; Falls Toleiterfrequenzen verwendet werden sollen:
.include "gamut.inc" ; Include-Datei mit Tonleiterfrequenzen
;
; Werksseitige Taktfrequenz
.equ clock = 1000000 ; Frequenz in Hz
;
; Vorteiler fuer TC0-PWM
.equ cTc0Presc = 64 ; Kann 1, 8, oder 64 sein
; ebenfalls moeglich: 256 oder 1,024, dann blinkt aber die LED
;
; (Frequenzen siehe die Tonleiterfrequenz-Tabelle)
.equ fG2R = fA4 ; Piepfrequenz gruen nach rot = A4
.equ fR2G = fA6 ; ; Piepfrequenz rot nach gruen = A6
;
; cDurMax waehlt maximale Dauer der Intervalle in Stunden aus
.equ cDurMax = 2 ; Zwei Stunden Voreinstellung
.if (cDurMax == 0)||(cDurMax>64)
  .error "cDurMax ausserhalb zulaessiger Bereich, kann 1 bis 64 sein!"
  .endif 
;
; Tondauer
.equ cDur = 750 ; Tondauer in ms
;
; ***********************************
;  F E S T E  &  B E R E C H N E T E
; ***********************************
;
; Umwandlung des Vorteiler-Werts
.if cTc0Presc == 1
  .message "Vorteiler=1, Jumper 1 unwirksam!"
  .equ cTc0Cs = 1<<CS00
  .else
  .if cTc0Presc == 8
    .message "Vorteiler=8: Jumper 1 beschleunigt 8-fach!"
	  .equ cTc0Cs = 1<<CS01
	  .else
	  .if cTc0Presc == 64
	    .message "Vorteiler=64: Jumper 1 beschleunigt 64-fach!"
	    .equ cTc0Cs = (1<<CS01)|(1<<CS00)
	    .else
      .if cTc0Presc == 256
	      .message "Vorteiler=256: Jumper 1 beschleunigt 256-fach!"
		    .equ cTc0Cs = 1<<CS02
		    .else
        .if cTc0Presc == 1024
		      .message "Vorteiler=1.024: Jumper 1 beschleunigt 1.024-fach!"
		      .equ cTc0Cs = (1<<CS02)|(1<<CS00)
		      .else
		      .error "Falscher Vorteiler-Wert in cTc0Presc!"
		      .endif
		    .endif
	    .endif
	  .endif
  .endif 
; PWM-Charakteristiken
.equ fPwm = ((clock + 128) / 256 + cTc0Presc/2) / cTc0Presc ; Frequenz PWM in Hz
.equ tPwm = (1000000*256*cTc0Presc+clock/2)/clock ; PWM-Zyklusdauer in ms
.equ fColorChange = 1000*fPwm / 256 ; Farbwechselfrequenz in mHz
.equ tColorChange = (256 * tPwm +500) / 1000 ; Farbwechselzeit in ms
;
; Ton: Von gruen nach rot
.equ csG2R = LOG2((((1000*clock+fG2R/2)/fG2R)+128)/255)+1 ; CS-Bits
.equ cpG2R = EXP2(csG2R-1) ; Vorteiler
.equ cdG2R = (((1000*clock+fG2R/2)/fG2R+cpG2R/2)/cpG2R+1)/2-1 ; Teiler
.equ cTc1G2R = csG2R | (1<<CTC1) ; TCCR1
.equ cDurG2R = (cDur*fG2R+250000)/500000 ; Tondauer
;
; Ton: von rot nach gruen
.equ csR2G = LOG2((((1000*clock+fR2G/2)/fR2G)+128)/255)+1 ; CS-Bits
.equ cpR2G = EXP2(csR2G-1) ; Vorteiler
.equ cdR2G = (((1000*clock+fR2G/2)/fR2G+cpR2G/2)/cpR2G+1)/2-1 ; Teiler
.equ cTc1R2G = csR2G | (1<<CTC1)
.equ cDurR2G = (cDur*fR2G+250000)/500000 ; Tondauer
;
; Checks
.if (cdG2R>255)||(cdR2G>255)
  .error "Teilerwerte zu gross, Frequenz aendern!"
  .endif
.if (cDurG2R>65535)||(cDurR2G>65535)
  .error "Dauer zu lang, Frequenz oder Dauer aendern!"
  .endif
;
; **********************************
;       R E G I S T E R 
; **********************************
;
; frei: R0 bis R6
.def rAdcStrt = R7 ; Beim Neustart ADC-Verzoegerung
.def rDelayL = R8 ; Aktuelle Verzoegerung, LSB
.def rDelayH = R9 ; dto., MSB
.def rRedL = R10 ; Rote Verzoegerung, LSB
.def rRedH = R11 ; dto., MSB
.def rGreenL = R12 ; Gruene Verzoegerung, LSB
.def rGreenH = R13 ; dto., MSB
.def rAdcL = R14 ; ADC-Summe, LSB
.def rAdcH = R15 ; dto., MSB
.def rmp = R16 ; Vielzweck-Register
.def rAdcCnt = R17 ; ADC-Zaehler fuer Summierung
.def rCmp = R18 ; PWM-Vergleichswert
; frei: R19 bis R23
.def rCntL = R24 ; Phasenzaehler, LSB
.def rCntH = R25 ; dto., MSB
; benutzt: X = R27:R26 als Halbwellen-Zaehler fuer Tondauer
; frei: R29:R28 = Y, R31:R30 = Z
;
; **********************************
;           S R A M
; **********************************
;
.dseg
.org SRAM_START
; (Kein SRAM in Benutzung)
;
; **********************************
;         C O D E
; **********************************
;
.cseg
.org 000000
;
; **************************************
; R E S E T  &  I N T - V E K T O R E N
; **************************************
	rjmp Main ; Reset-Vektor
	reti ; INT0, nicht verwendet
	rjmp PcioIsr ; PCI0
	reti ; OC1A, nicht verwendet
	reti ; OVF1, nicht verwendet
	rjmp Tc0OvfIsr ; OVF0
	reti ; ERDY, nicht verwendet
	reti ; ACI, nicht verwendet
	rjmp AdcIsr ; ADCC
	rjmp Oc1bIsr ; OC1B
	reti ; OC0A, nicht verwendet
	reti ; OC0B, nicht verwendet
	reti ; WDT, nicht verwendet
	reti ; USI_START, nicht verwendet
	reti ; USI_OVF, nicht verwendet
;
; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************
;
; PCINT Jumper geaendert, Aenderung Vorteiler
PcioIsr:
  sbic pI,bJ1I ; Jumper gesteckt?
  rjmp PcioIsr1 ; Nein
  ldi rmp,1<<CS00 ; Lade schnellen prescaler
  out TCCR0B,rmp
  reti
PcioIsr1:
  ldi rmp,cTc0Cs ; Lade Default-Vorteiler
  out TCCR0B,rmp
  reti
;
; Ueberlauf TC0 Interrupt Service Routine
Tc0OvfIsr:
  sbiw rCntL,1 ; Count down
  brne Tc0OvfIsrRet ; Zeit noch nicht abgelaufen
  inc rCmp ; Naechste Farbintensitaet
  out OCR0A,rCmp ; In Compare A
  out OCR0B,rCmp ; Und in Compare B
  brne Tc0OvfIsrSetCnt ; Nicht Null, Neustart Counter
  brts Tc0OvfIsrG2Ra ; Schalte von rot auf gruen
  ldi rmp,(1<<COM0B1)|(1<<COM0B0)|(1<<WGM01)|(1<<WGM00) ; Fast PWM, OC0B invertiert
  out TCCR0A,rmp ; In TC0 Kontrollregister A
  set ; Naechste Phase: von rot auf gruen umschalten
  mov rDelayH,rRedH ; Lade roten Counter-Wert in Verzoegerung, MSB
  mov rDelayL,rRedL ; dto., LSB
  cbi pO,bAnGnO ; Gruene Anode auf Low
  ldi XH,High(cDurG2R) ; Lade Tondauer, MSB
  ldi XL,Low(cDurG2R) ; dto., LSB
  ldi rmp,cdG2R ; Gruen nach rot Ton
  out OCR1C,rmp
  ldi rmp,1<<COM1B0 ; Toggle Ausgang
  out GTCCR,rmp
  ldi rmp,cTc1G2R ; TC0-Vorteiler
  out TCCR1,rmp ; Setze Vorteiler
  in rmp,TIMSK ; Lese Interruptmaske
  sbr rmp,1<<OCIE1B ; OC Interrupt enablen
  out TIMSK,rmp 
  rjmp Tc0OvfIsrSetCnt ; Sete Counter-Wert
Tc0OvfIsrG2Ra:
  ; Schalte von rot auf gruen
  ldi rmp,(1<<COM0A1)|(1<<WGM01)|(1<<WGM00) ; Fast PWM, OC0A nicht-invertiert
  out TCCR0A,rmp ; In TC0 Kontrollregister A
  clt ; Naechste Phase: auf rot umschaltem
  mov rDelayH,rGreenH ; Lad gruene Verzoegerung, MSB
  mov rDelayL,rGreenL ; dto., LSB
  cbi pO,bAnRdO ; Rote Anode auf low
  ldi XH,High(cDurR2G) ; Lade Tondauer, MSB
  ldi XL,Low(cDurR2G) ; dto., LSB
  ldi rmp,cdR2G ; Rot nach gruen Ton
  out OCR1C,rmp
  ldi rmp,1<<COM1B0 ; Toggle
  out GTCCR,rmp
  ldi rmp,cTc1R2G ; Default TC0 Vorteiler
  out TCCR1,rmp ; Setze Vorteiler
  in rmp,TIMSK ; Lese Interruptmaske
  sbr rmp,1<<OCIE1B ; OC Interrupt enablen
  out TIMSK,rmp 
Tc0OvfIsrSetCnt:
  mov rCntH,rDelayH ; Neustart Verzoegerungszaehler, MSB
  mov rCntL,rDelayL ; dto., LSB
Tc0OvfIsrRet:
  reti
;
; ADC Interrupt Service Routine
AdcIsr:
  in rmp,ADCL ; Lese LSB
  add rAdcL,rmp ; Addiere LSB
  in rmp,ADCH ; Lese MSB
  adc rAdcH,rmp ; Addiere MSB
  dec rAdcCnt ; Abwaertszaehlen
  brne AdcIsrRet ; Noch nicht bei Null
  ldi rAdcCnt,cDurMax ; Neustart Zaehler
  mov rRedH,rAdcH ; Kopiere Summe aus Messungen nach rot, MSB
  mov rRedL,rAdcL ; dto., LSB
  mov rGreenH,rAdcH ; Summe ebenfalls nach gruen 
  mov rGreenL,rAdcL
  clr rAdcH ; Neustart Summe, MSB
  clr rAdcL ; dto., LSB
  lsr rGreenH ; Dividieren durch 2, MSB
  ror rGreenL ; dto., LSB
  lsr rGreenH ; Dividieren durch 4, MSB
  ror rGreenL ; dto., LSB
  lsr rGreenH ; Dividieren durch 8, MSB
  ror rGreenL ; dto., LSB
  lsr rGreenH ; Dividieren durch 16, MSB
  ror rGreenL ; dto., LSB
  mov rmp,rGreenL ; Pruefe auf Null, LSB
  or rmp,rGreenH ; und MSB
  brne AdcIsr1 ; Nicht Null
  clr rGreenL ; Setze gruen auf 0, LSB
  inc rGreenL ; Gruen auf Eins
  clr rGreenH ; MSB Null
AdcIsr1:
  sub rRedL,rGreenL ; Subtrahiere gruen von rot, LSB
  sbc rRedH,rGreenH ; dto., MSB
  brcs AdcIsr2 ; Ueberlauf: Rot auf 1
  mov rmp,rRedL ; Pruefe rot auf Null, LSB
  or rmp,rRedH ; und MSB 
  brne AdcIsr3 ; Nicht Null
AdcIsr2:
  clr rRedL ; Sete rot auf 0
  inc rRedL ; LSB auf Eins
  clr rRedH ; MSB auf Null
AdcIsr3:
  in rmp,TIMSK ; Lese Interruptmaske
  sbrc rmp,TOIE0 ; Interrupts aus?
  rjmp AdcIsrRet ; Nein, ueberspringe Startprozedur
  dec rAdcStrt ; Verzoegerung abwaerts
  brne AdcIsrRet ; Noch nicht Null
  clr rCntH ; Provoziere Umschaltung, MSB = 0
  ldi rCntL,1 ; LSB = 1
  clr rDelayH ; dto., MSB = 0
  clr rDelayL ; LSB = 0
  inc rDelayL ; LSB = 1
  ldi rCmp,0xFF ; Compare auf max
  in rmp,TIFR ; Lese Flaggenregister
  cbr rmp,1<<TOV0 ; Loesche Overflow-Int-Bit
  out TIFR,rmp ; in Flaggenregister
  in rmp,TIMSK ; Lese Interruptmaske
  sbr rmp,1<<TOIE0 ; OVF Interrupt enablen
  out TIMSK,rmp
AdcIsrRet:
  reti
;
; OC1B ISR, zaehlt Halbwellen und schaltet Ton aus
Oc1BIsr:
  sbiw XL,1 ; Count down Halbwellen
  brne Oc1BIsrRet
  ldi rmp,1<<COM1B1 ; OC1B-Ausgang auf Clear
  out GTCCR,rmp
  in rmp,TIMSK ; Lese Interruptmaske
  cbr rmp,1<<OCIE1B ; Loesche TC1 Compare Int
  out TIMSK,rmp
Oc1BIsrRet:
  reti   
;
; *************************************
;  H A U P T P R O G R A M M - I N I T
; *************************************
;
Main:
  ; Init Ports
  sbi pD,bAnGnD ; Gruene LED-Anode ist Ausgang
  cbi pO,bAnGnO ; Gruene LED-Anode auf Null
  sbi pD,bAnRdD ; Rote LED-Anode ist Ausgang
  cbi pO,bAnRdO ; Rote LED-Anode auf Null
  sbi pD,bSpkD ; Lautsprecher ist Ausgang
  cbi pO,bSpkO ; Lautsprecher auf Null
  cbi pD,bJ1D ; Jumper als Eingang
  sbi pO,bJ1O ; Jumper Pull-up hoch 
.if cDebug
  rjmp Debug
  .endif
.ifdef SPH ; Wenn der tiny85 eingestellt ist
    ldi rmp,High(RAMEND) ; Stapel MSB
    out SPH,rmp
  .endif
  ldi rmp,Low(RAMEND) ; Stapel auf RAMEND
  out SPL,rmp ; Init LSB Stapelzeiger
  ; Starte TC1 als Tongenerator
  ldi XH,High(cDurG2R) ; Lade Tondauer, MSB
  ldi XL,Low(cDurG2R) ; dto., LSB
  ldi rmp,cdG2R ; Gruen nach Rot Ton
  out OCR1C,rmp
  ldi rmp,1<<COM1B0 ; Toggle OC1B
  out GTCCR,rmp
  ldi rmp,cTc1G2R ; Default TC0-Vorteiler
  out TCCR1,rmp ; Sete Vorteiler
  in rmp,TIMSK ; Lese Interruptmaske
  sbr rmp,1<<OCIE1B ; OC1B Interrupt enablen
  out TIMSK,rmp
  ; Starte TC0 als LED-Timer im Fast PWM-Modus
  clt ; Starte mit Umschalten auf rot
  ldi rCmp,0xFF ; Starte mit der hoechsten PWM Stufe
  out OCR0A,rCmp ; In Compare A
  out OCR0B,rCmp ; und in Compare B
  clr rCntH ; Starte mit niedrigster Verzoegerung
  ldi rCntL,1 ; LSB auf Eins
  clr rRedH ; MSB auf Null
  mov rRedL,rCntL ; dto., auf Eins
  clr rGreenH ; MSB auf Null
  mov rGreenL,rCntL ; dto., auf Eins
  clr rDelayH ; MSB auf Null
  mov rDelayL,rCntL ; dto., auf Eins
  ldi rmp,(1<<WGM01)|(1<<WGM00) ; Fast PWM, OC0-Ausgaenge aus
  out TCCR0A,rmp ; An TC0-Kontrollport port A
  ldi rmp,cTc0Cs ; Lade default prescaler
  sbis pI,bJ1I ; Jumper 1 geschlossen?
  ldi rmp,1<<CS00 ; Ja, beschleunige
  out TCCR0B,rmp ; An TC0 Kontrollport B
  ; Die Interrupts werden hier noch nicht enabled, erst wenn
  ; genuegend ADC-Messwerte vorliegen
  ; Starte ADC
  ldi rmp,3 ; Startverzoegerung
  mov rAdcStrt,rmp
  ldi rAdcCnt,cDurMax ; Anzahl Messungen Vorgabe
  ldi rmp,(1<<MUX1)|(1<<MUX0) ; MUX auf ADC3-Eingang
  out ADMUX,rmp
  ldi rmp,1<<ADTS2 ; TC0-Ueberlauf als Trigger
  out ADCSRB,rmp
  ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  out ADCSRA,rmp ; ADC starten mit Interrupts etc.
  ; Enable PCINT am PA2-Pin
  ldi rmp,1<<PCINT2 ; Pin2 Maskenbit
  out PCMSK,rmp
  ldi rmp,1<<PCIE ; Enable PCI-Interrupt
  out GIMSK,rmp
  ; Schlafmodus Idle
  ldi rmp,1<<SE ; Schlafen enablen
  out MCUCR,rmp
  ; Interrupts enablen
  sei ; Enable Interrupts
;
; **********************************
;   P R O G R A M M S C H L E I F E
; **********************************
;
Loop:
  sleep
  rjmp loop ; Beim Aufwachen wieder schlafen legen
;
; **********************************
;    H A R D W A R E - D E B U G
; **********************************
;
; Alle Hardware-Debug Routinen hier
Debug:
.if cDebugLed == 1
  DebugLed:
   sbi pO,bAnRdO
    cbi pO,bAnGnO
  DebugLed1:
    sbiw ZL,1
    brne DebugLed1
    cbi pO,bAnRdO
    sbi pO,bAnGnO
  DebugLed2:
    sbiw ZL,1
    brne DebugLed2
    rjmp DebugLed
  .endif
.if cDebugSpk == 1  ;
    ldi rmp,200
    out OCR1C,rmp
    ldi rmp,1<<COM1B0 ; Toggle
    out GTCCR,rmp
    ldi rmp,(1<<CS12)|(1<<CTC1)
    out TCCR1,rmp
  DebugSpk:
    rjmp DebugSpk
  .endif
.if cDebugAdc == 1
  test:
	ldi XL,(1<<bAnGnO)|(1<<bJ1O)
    out pO,XL
    ldi rmp,(1<<MUX1)|(1<<MUX0)
	out ADMUX,rmp
	ldi rmp,0
	out ADCSRB,rmp
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
  DebugAdc:
    sbic ADCSRA,ADSC
	rjmp DebugAdc1
	in ZL,ADCL
	in ZH,ADCH
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp
	lsr ZH
	ror ZL
	ldi XL,(1<<bAnGnO)|(1<<bJ1O)
	add XL,ZH
  DebugAdc1:
    out pO,XL
	clr rmp
  DebugAdc2:
    cp rmp,ZL
	brne DebugAdc3
	ldi XH,4
	out pO,XH
  DebugAdc3:
    inc rmp
	breq DebugAdc
	rjmp DebugAdc2
  .endif
.if cDebugPA2 == 1
  DebugPA2:
    sbic pI,bJ1I
    rjmp DebugPA2a
	sbi pO,bAnRdO
	cbi pO,bAnGnO
	rjmp DebugPA2
  DebugPA2a:
	cbi pO,bAnRdO
	sbi pO,bAnGnO
	rjmp DebugPA2
  .endif
DebugLoop:
  rjmp DebugLoop
;
; Ende Quellcode
;
; Copyright Information
 .db "(C)2021 by Gerhard Schmidt  " ; Quellcode-lesbar
 .db "C(2)20 1ybG reahdrS hcimtd  " ; Maschinenformat-lesbar
;



Zum Seitenfang


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