Pfad: Home =>
AVR-Übersicht =>
Anwendungen =>
IR-Steuerung =>
IR-Empfänger => Assembler-Quellcode
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