Pfad: Home => AVR-Übersicht => IR-Steuerung => IR-Empfänger => Assembler-Quellcode
IR-Transmitter

Assembler-Quellcode für den IR-Empfänger zum Schalten und Regeln mit AVR-Prozessor ATtiny13



;
; **********************************************
; * Infrared controlled switchbox              *
; * for ATtiny13, internal clock 1.2 MHz, V1   *
; * (C)2010 by http://www.avr-asm-tutorial.net *
; **********************************************
;
.NOLIST
.INCLUDE "tn13def.inc" ; Headerdatei fuer ATtiny13 
.LIST
;
; Debug-Einstellungen
;
.equ debug = 0 ; debug an: 1, aus: 0
;
; ============================================
;   H A R D W A R E I N F O R M A T I O N E N 
; ============================================
;
;              ________
;          1 /         |8
;  RESET O--|RES    VCC|--O VCC
;           |    AT    |
;  DR-CS O--|PB3    PB2|--O Relais/SCK
;           |   tiny   |
; DR-SCK O--|PB4    PB1|--O LED/IN/DR-SI/MISO
;           |    13    |
;    GND O--|GND    PB0|--O IR-IN/MOSI
;          4|__________|5
;
; ============================================
;      P O R T S   U N D   P I N S 
; ============================================
;
.equ pbRelO = PORTB2 ; Relais output pin
.equ pbLedO = PORTB1 ; LED output port
.equ pbLedD = DDB1  ; LED output direction port
.equ pbCsO  = PORTB3 ; DR-CS digital resistor CS
.equ pbSckO = PORTB4 ; DR-SCK digital resistor SCK
.equ pbSiO  = PORTB1 ; DR-SI digital resistor SI
.equ pbIrIn = PINB0  ; Infrarot-Eingang
;
; ================================================
;        P R O G R A M M A B L A U F
; ================================================
;
; Timer0 arbeitet mit Teiler durch 64 (18.750 Hz)
;   Alle 13,653 ms tritt ein Ueberlauf auf und
;   - beendet evtl. laufende IR-Signalanalyse
;   - erhoeht Timeout-Zaehler, wenn Relais aktiv ist
;
;
; ================================================
; K O N S T A N T E N   Z U M  E I N S T E L L E N
; ================================================
;
; (keine)
;
; =======================================================
;  F E S T E + A B G E L E I T E T E  K O N S T A N T E N 
; =======================================================
;
.equ clock = 1200000 ; Prozessortakt
;
; ============================================
;   R E G I S T E R D E F I N I T I O N E N
; ============================================
;
; R0 frei fuer LPM
; Frei: R1..R10
.def rPot = R11 ; Status digitales Potentiometer
.def rPotV = R12 ; Potentiometerwert
.def rTO1 = R13 ; Time-Out-Zaehler, Byte 1
.def rTO2 = R14 ; Time-Out-Zaehler, Byte 2
.def rSreg = R15 ; Register fuer SREG
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Vielzweckregister Interrupts
.def rFlg = R18 ; Flaggenregister
	.equ bIrAktiv = 0 ; IR-Analyse gestartet
	.equ bIrAdrOk = 1 ; IR-Adresse erkannt
	.equ bIrCmdOk = 2 ; Command erkannt
	.equ bIrBreak = 3 ; Abbruch wegen Fehler
.def rTO3 = R19 ; Time-Out-Zaehler, Byte 3
.def rTc = R20 ; Timer-Stand
.def rIrC = R21 ; Register fuer den IR-Cmd-Code
.def rIrCL = R22 ; Register fuer den letzten IR-Cmd-Code
; frei: R22..R29
; verwendet: Z fuer LPM
;
; ============================================
;       S R A M   D E F I N I T I O N E N
; ============================================
;
;
; ==============================================
;   R E S E T   U N D   I N T   V E K T O R E N
; ==============================================
;
.CSEG
.ORG $0000
	rjmp Main ; Reset-Vektor
	reti ; Int0 Vektor
	rjmp PcInt ; PcInt0 Vektor
	rjmp Tc0Int ; Tc0Ovf-Int Vektor
	reti ; EERDY-Int Vektor
	reti ; AnaComp-Int Vektor
	reti ; TC0COMPA-Int Vektor
	reti ; TC0COMPB-Int Vektor
	reti ; WDT-Int Vektor
	reti ; ADC-Int Vektor
;
; ==========================================
;    I N T E R R U P T   S E R V I C E
; ==========================================
;
; Pin Change interrupt am IR-Eingang
;   liest Timer-Stand ein, setzt Timer auf Null, setzt T-Flagge
PcInt:
	sbic PINB,pbIrIn ; Eingang = 0?
	reti ; Eingang =1, ignoriere Flanke
	in rTc,TCNT0 ; lese timer
	ldi rimp,1<<PSR10 ; loesche Timer Prescaler
	out GTCCR,rimp
	ldi rimp,0 ; loesche Timer
	out TCNT0,rimp
	set ; setze Flagge
	reti
; TC0 overflow interrupt
Tc0Int:
	in rSreg,SREG ; rette SREG
	ldi rFlg,0 ; loesche IR-Erkennungsflaggen
	dec rTO1 ; Timeout-Zaehler 3,5 Sekunden
	brne Tc0IntRet
	dec rTO2 ; Timeout-Zaehler 14,9 Minuten
	brne Tc0IntRet
	inc rTO3 ; Timeout-Zaehler Viertelstunden
	cpi rTO3,20 ; 5 Stunden vorbei?
	brcs Tc0IntRet ; nein, noch nicht
	cbi PORTB,pbRelO ; Relais aus
	cbi DDRB,pbLedD ; LED aus
	sbi PORTB,pbLedO ; Pull-Up an
Tc0IntRet:
	out SREG,rSreg ; stelle SREG wieder her
	reti
;
; ============================================
;    H A U P T P R O G R A M M    I N I T
; ============================================
;
Main:
	; Stapel initiieren
	ldi rmp,LOW(RAMEND) ; setze Stapelzeiger
	out SPL,rmp
	; Flaggen alle auf Null
	clr rFlg
	; Digitalpotentiometer Takteingang
	sbi PORTB,pbCsO ; CS high
	sbi DDRB,pbCsO ; CS Output
	cbi PORTB,pbSckO ; SCK Output low
	sbi DDRB,pbSckO ; SCK Output
	cbi PORTB,pbSiO ; SI Output low
	sbi DDRB,pbSiO ; SI Output
	; LED-Ausgang Aus
	sbi PORTB,pbLedO ; LED-Port auf High, Pull-Up an
	cbi DDRB,pbLedD ; LED-Ausgang inaktiv
	; Relais-Ausgang an
	cbi PORTB,pbRelO ; Relais aus
	sbi DDRB,pbRelO ; Relaisausgang aktiv
	; Start blinken
	ldi rmp,3 ; LED drei mal blinken
	rcall LedN
	; Startwert Potentiometer setzen
	rcall ReadPot ; Potentiometerstand aus EEPROM holen
	rcall PotTab ; linear in exponentiell wandeln 
	rcall SendPot ; an Poti senden
	; Timer 0 starten
	clr rmp ; Mode 0 einstellen
	out TCCR0A,rmp
	ldi rmp,(1<<CS01)|(1<<CS00) ; Teiler durch 64
	out TCCR0B,rmp
	ldi rmp,1<<TOIE0 ; Interrupts ermoeglichen
	out TIMSK0,rmp
	; IR-Eingang Interrupts ermoeglichen
	ldi rmp,1<<pbIrIn ; Maske fuer PCINT
	out PCMSK,rmp
	ldi rmp,1<<PCIE ; Enable IR-Input int
	out GIMSK,rmp
	; SLEEP enable
	ldi rmp,1<<SE ; Enable sleep
	out MCUCR,rmp
	sei
;
; ============================================
;     P R O G R A M M - S C H L E I F E
; ============================================
;
Loop:
	sleep ; Schlafen legen
	nop ; Dummy fuer Aufwecken
	brts FSet ; Flagge gesetzt
	sbis PORTB,pbLedO ; Ausgang aktiv?
	rjmp Loop ; Ausgang aktiv
	sbic PINB,pbLedO ; Taste gedrueckt?
	rjmp Loop ; Taste nicht gedrueckt
OutAn:
	sbi PORTB,pbRelO ; Relais an
	cbi PORTB,pbLedO ; LED an
	sbi DDRB,pbLedD ; LED-Ausgang aktiv
	clr rTO1 ; clear TimeOut-Zaehler
	clr rTO2
	clr rTO3
	rjmp Loop ; fertig
FSet: ; Flagge gesetzt
	clt ; Flagge loeschen
	sbrc rFlg,bIrBreak ; Break-Flagge gesetzt?
	rjmp Loop ; ja, ignoriere Rest
	mov rmp,rTc ; Timerstand kopieren
	sbrc rFlg,bIrAktiv ; IR-Analyse starten?
	rjmp IrAktiv
	; IR-Analye nicht aktiv, warte auf erstes langes Signal
	cpi rmp,230
	brcs Loop ; Signal zu kurz
	; IR-Analyse ist nicht aktiv, Erkennung starten
	ldi ZH,HIGH(2*IrAdrTab) ; Zeiger auf IR-Tabelle
	ldi ZL,LOW(2*IrAdrTab)
	sbr rFlg,1<<bIrAktiv ; Flagge auf aktiv setzen
IrAktiv: ; IR-Analyse ist aktiv
	sbrc rFlg,bIrAdrOk ; Adresse schon ok?
	rjmp IrAdrOk ; ja
	lpm R0,Z+ ; lese Tabellenwert niedrig
	tst R0 ; Tabellenwert = 0?
	breq IrAktivAdrOk ; Adresse korrekt
	cp rmp,R0 ; ist Wert >= kleiner Wert
	brcs IrAktivBreak1 ; nein, Break setzen
	lpm R0,Z+ ; groesseren Wert lesen
	cp rmp,R0 ; vergleichen
	brcs Loop ; Wert ist ok
IrAktivBreak2: ; zu lang
.if debug == 1
	subi ZL,Low(2*IrAdrTab)
	lsr ZL
	mov rmp,ZL
	rcall LedN
	ldi rmp,4 ; vier Mal blinken
	rjmp IrAktivBreakN
	.endif
IrAktivBreak1:
.if debug == 1
	push rmp
	subi ZL,Low(2*IrAdrTab)
	lsr ZL
	mov rmp,ZL
	rcall LedN
	mov rmp,R0
	rcall LedHex
	pop rmp
	rcall LedHex
	ldi rmp,3 ; drei Mal blinken
	rjmp IrAktivBreakN
	.endif
IrAktivBreak3:
	ldi rmp,3
IrAktivBreakN:
	sbr rFlg,1<<bIrBreak ; Break-Flagge setzen
.if debug == 1
	rcall LedN
	.endif
	rjmp Loop
IrAktivAdrOk:
	sbr rFlg,1<<bIrAdrOk ; setze Adresse ok
	ldi rIrC, 0x01 ; Starte mit einer Eins
IrAdrOk:
	cpi rmp,17 ; kleiner als Low-High-Grenze
	brcs IrAktivBreak1
	cpi rmp,45 ; laenger als High-Grenze
	brcc IrAktivBreak2
	cpi rmp,30 ; 0 oder 1?
	brcc IrAdrOk1 ; schiebe 1 rein
	clc ; schiebe 0 rein
	rjmp IrCmdShift
IrAdrOk1:
	sec
IrCmdShift:
	rol rIrC ; schiebe Carry in Code-Byte
	brcc Loop ; noch nicht fertig
	sbr rFlg,1<<bIrBreak ; setze Erkennung inaktiv
	cp rIrC,rIrCL ; letzter Command gleich
	breq IrCmdEqu
	mov rIrCL,rIrC
	rjmp Loop
IrCmdEqu:
	cpi rIrC,cOn ; ON-Command?
	brne IrCmdOff
	sbi PORTB,pbRelO ; Relais an
	cbi PORTB,pbLedO ; LED an
	sbi DDRB,pbLedD ; Porttreiber an
	clr rTO1 ; clear TimeOut-Zaehler
	clr rTO2
	clr rTO3
	rjmp IrCmdOk
IrCmdOff:
	cpi rIrC,cOff ; Off-Command?
	brne IrCmdVolUp ; Nein
	cbi PORTB,pbRelO ; Relais aus
	sbi PORTB,pbLedO ; LED aus
	cbi DDRB,pbLedD ; Porttreiber aus
	rjmp IrCmdOk
IrCmdVolUp:
	cpi rIrC,cVol_up ; Volume-Up?
	brne IrCmdVolDwn ; nein
	; Volume Up
	inc rPot
	mov rmp,rPot
	cpi rmp,cMaxPot ; Wert zu gross?
	brcs IrCmdVolUpOk
	dec rPot ; Wert wieder erniedrigen
	rjmp Loop ; ohne Bestaetigung raus
IrCmdVolUpOk:
	rcall PotTab ; linear in exponentiell wandeln
	rcall SendPot ; rPotV an Potentiometer senden
	rcall WritePot ; rPot in EEPROM schreiben
	rjmp IrCmdOk
IrCmdVolDwn:
	cpi rIrC,cVol_dwn ; Volume down?
	brne IrCmdUnkn ; nein
	; Volume down
	tst rPot
	breq IrCmdVolDwn1
	dec rPot ; Lautstaerke niedriger
	rcall PotTab ; linear in exponentiell wandeln
	rcall SendPot ; rPotV an Potentiometer senden
	rcall WritePot ; rPot in EEPROM schreiben
	rjmp IrCmdOk
IrCmdVolDwn1:
	rjmp Loop
IrCmdUnkn:
.if debug == 1
	ldi rmp,2
	rcall LedN
	mov rmp,rIrc
	rcall LedHex
	.endif
	rjmp Loop
IrCmdOk:
	ldi rmp,1 ; ein Mal blinken
	rcall LedN
	rjmp Loop
;
; Tabelle fuer IR-Adresserkennung
;   erstes Byte: Signaldauer muss groesser/gleich sein
;   zweites Byte: Signaldauer muss kleiner sein
;   Ende der Tabelle (positive Erkennung): 0
IrAdrTab:
.db 230,245,17,30,17,30,17,30,17,30,31,45,31,45,31,45,31,45,80,95,0,0
; Beamer codes, H+L	
.equ cVol_dwn = 0b00100001	
.equ cOn = 0b10000010	
.equ cOff = 0b01000010	
.equ cVol_up = 0b10100001
; Neue Messung der Keycodes
; key codes binary
.equ key_on =  0b10000010
.equ key_off =  0b10100001
.equ key_vol_up =  0b11010000
.equ key_vol_dwn =  0b10010000
.equ key_keystone_volume =  0b11100001
.equ key_autoposition =  0b10100010
.equ key_menu =  0b10110000
.equ key_up =  0b10100000
.equ key_dwn =  0b11100000
.equ key_left =  0b10000000
.equ key_enter =  0b11110000
.equ key_aspect =  0b10100011
.equ key_avmute =  0b10110010
.equ key_freeze =  0b10010010
.equ key_computer =  0b11001010
.equ key_videpo =  0b11000011
;
; Lesen Potentiometerstand aus EEPROM
;
ReadPot:
	sbic EECR,EEPE ; warte auf EEPROM fertig
	rjmp ReadPot
	ldi rmp,63 ; Addresse EEPROM
	out EEARL,rmp
	sbi EECR,EERE ; setze Lesebit
	in rPot,EEDR ; lese byte
	ldi rmp,0 ; setze Adresse auf Null
	out EEARL,rmp
	rcall PotTab ; wandle in Exponentialwert um
	rcall WritePot ; schreibe Byte in Poti
	ret
;
; Potentiometerstand in EEPROM schreiben
;
WritePot:
	sbic EECR,EEPE ; warte auf EEPROM fertig
	rjmp WritePot
	ldi rmp, (0<<EEPM1)|(0<<EEPM0) ; Schreibmode
	out EECR,rmp
	ldi rmp,63 ; Addresse EEPROM
	out EEARL,rmp
	out EEDR,rPot
	cli ; stop interrupts
	sbi EECR,EEMPE ; schreibe
	sbi EECR,EEPE
	sei ; Ints wieder zulassen
WritePot1:
	sbic EECR,EEPE ; warte bis fertig
	rjmp WritePot1
	ldi rmp,0x00 ; schreibe Adresse 0
	out EEARL,rmp
	ret
;
; Potentiometer Einstellroutine
;
SendPot:
	ldi ZH,0x13 ; command byte for pot
	mov ZL,rPotV ; data byte for pot
	cbi PORTB,pbSiO ; clear SI
	cbi PORTB,pbSckO ; clear SCK
	cbi PORTB,pbCsO ; activate CS
	sec
sendpotbit:
	rol ZL ; one bit left
	rol ZH
	brcs sendpotone
	cbi PORTB,pbSiO ; clear SI
	rjmp sendpotshift
sendpotone:
	sbi PORTB,pbSiO ; set SI
sendpotshift:
	sbi PORTB,pbSckO ; set SCK
	cbi PORTB,pbSckO ; clear SCK
	cpi ZL,0 ; end of transfer?
	clc ; set next bit shifted in
	brne sendpotbit
	cpi ZH,0x80 ; last bit?
	clc ; set next bit shifted in
	brne sendpotbit
	sbi PORTB,pbCsO ; end, clear CS
	sbi PORTB,pbLedO ; LED-Ausgang inaktiv
	cbi DDRB,pbLedD ;  Porttreiber aus
	sbis PORTB,pbRelO ; Relais an?
	ret ; nein
	cbi PORTB,pbLedO ; LED-Ausgang an
	sbi DDRB,pbLedD ; Porttreiber an
	ret
;
; LED-Signale ausgeben
;
.if debug == 1
	.equ tlow = 300 ; Dauer LED aus in ms
	.equ tHigh = 300 ; Dauer LED an in ms
	.equ tPause = 500 ; Dauer Pause am Ende, max. 500
	.else
	.equ tlow = 160
	.equ tHigh = 80
	.equ tPause = 80
	.endif
.equ nTakt = clock/1000/10 ; Takte pro ms
.equ nLow = tLow*nTakt
.equ nHigh = tHigh*nTakt
.equ nPause = tPause*nTakt
LedN: ; gib N Blinksignale aus, N steht in rmp
	tst rmp ; rmp = 0?
	brne LedNGo ; nein
	ldi rmp,10 ; ersetze 0 durch 10
LedNGo:
	ldi ZH,HIGH(nLow) ; Aus-Zeit
	ldi ZL,LOW(nLow)
	sbi DDRB,pbLedD ; Porttreiber an
	sbi PORTB,pbLedO ; LED-Ausgang high
LedNLow:
	rcall LedNWait ; warte 10*Z Takte
	cbi PORTB,pbLedO ; Led an
	ldi ZH,HIGH(nHigh) ; An-Zeit
	ldi ZL,LOW(nHigh)
LedNHigh:
	rcall LedNWait ; warte 10*Z Takte
	sbi PORTB,pbLedO ; Led aus
	dec rmp
	brne LedNGo
	ldi ZH,HIGH(nPause)
	ldi ZL,LOW(nPause)
	rcall LedNWait
	sbi PORTB,pbLedO ; Pullup an
	cbi DDRB,pbLedD ; Porttreiber aus
	sbis PORTB,pbRelO ; Relais an?
	ret
	cbi PORTB,pbLedO ; LED-Port an
	sbi DDRB,pbLedD ; Porttreiber an
	ret
; Warten
LedNWait: ; warte 10*Z Takte
	nop
	nop
	nop
	nop
	nop
	nop
	sbiw ZL,1
	brne LedNWait
	ret
;
; Gibt rmp in hex aus
;
LedHex:
	push rmp ; rette original
	swap rmp ; oberes zu unteres nibble
	rcall LedHexN
	pop rmp ; stelle Originalwert wieder her
LedHexN:
	andi rmp,0x0F ; nur unteres Nibble
	cpi rmp,0x00 ; 0 = 16
	brne LedHexNO
	ldi rmp,0x10
LedHexNO:
	rjmp LedN ; gib Nibble in hex auf LED aus
;
; Potentiometerwert aus Tabelle ermitteln
;   Tabelle fuer exponentielle Potentiometercharakteristik
;   ueberprueft Tabellenwert in rPot und korrigiert diesen
;   uebersetzt 0..52 in rPot in Potentiometerwert in rPotV
;
PotTab:
	mov rmp,rPot ; kopiere Potentiometerstand
	cpi rmp,0xFF ; Unterlauf nach Verringern?
	brne PotTab1
	inc rPot ; Unterlauf auf Null stellen
	rjmp PotTab2
PotTab1:
	cpi rmp,cMaxPot ; Ueberlauf nach Erhoehen?
	brcs PotTab2
	ldi rmp,cMaxPot-1 ; lade hoechsten Wert
	mov rPot,rmp
PotTab2:
	ldi ZH,HIGH(2*TabPot) ; Zeiger auf Tabelle
	ldi ZL,LOW(2*TabPot)
	add ZL,rPot ; addiere Tabellenwert
	ldi rmp,0 ; MSB-Ueberlauf behandeln
	adc ZH,rmp
	lpm rPotV,Z
	ret
TabPot:
.db 0,1,2,3,4,5,6,7,8,9
.db 10,12,14,16,18,20,22,24,26,29
.db 32,35,38,41,44,47,51,55,59,63
.db 67,71,76,81,86,91,96,102,108,114
.db 121,128,135,143,151,160,169,179,189,200
.db 212,225,239,255,255,255
TabPotEnd:
.equ cMaxPot = TabPotEnd-TabPot ; Anzahl Werte in Tabelle
;
; Ende Quellcode
; Copyright
.db "(C)2010 by Gerhard Schmidt  " ; menschenlesbar
.db "C(2)10 0yb eGhrra dcSmhdi t " ; wortgerecht
;



An den Seitenanfang, Zur IR-RX-Seite, Zur IR-Leitseite

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