;
; ***************************************
; * IR-Switch 3-Channel with ATtiny13 *
; * (C)2016 by www.avr-asm-tutorial.net *
; ***************************************
;
; -------- Switches -------------
.equ cActiveH = 0 ; 1 = Outputs Aktive High
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; Hardware: ATtiny13
; _________
; / |
; +5V/10k o--|Reset VCC|--o +5V
; | |
; Key o--|PB3 PB2|--o Output ye
; | |
; Output gn o--|PB4 PB1|--o TSOP1740
; | |
; 0V o--|GND PB0|--o Output rd
; |__________|
;
; -------- Ports, Portpins ----------
; Ports
.equ pOut = PORTB ; Output port tn13
.equ pDir = DDRB ; Direction port tn13
.equ pIn = PINB ; Input port tn13
; Portbits
.equ bIrO = PORTB1 ; IR-Receiver Output
.equ bIrD = DDB1 ; IR-Receiver Direction
.equ bIrI = PINB1 ; IR-Receiver Input
.equ bRdO = PORTB0 ; Switch output red
.equ bRdD = DDB0 ; Direction red
.equ bYeO = PORTB2 ; Switch output yellow
.equ bGeD = DDB2 ; Direction yellow
.equ bGnO = PORTB4 ; Switch output green
.equ bGnD = DDB4 ; Direction green
.equ bKyO = PORTB3 ; Key Input Pullup
.equ bKyI = PINB3 ; Key Input pin
;
; -------- Timing, IR-Signals -------
; Clock: 1200000 Hz
; TC0-Prescale 64
; TC0-Tick 18,750 cs/s
; 53.33 us
; TC0-Overflow 13,563 us
.equ cTcTick = 53
;
;
; Zero/One-Level:
.equ cOne = 868/cTcTick ; tn13:16
; Header/Data-Level:
.equ cHeader = 4*cOne ; tn13:48
; Delay pause after last received bit:
.equ cChk = 1
; Half Second, MSB
.equ cSekH = 500000/cTcTick / 256 ; tn13: 36
;
; -------- Registers -----------------
; R0 not used
.def rBits= R5 ; Received bits
.def rBitsO=R6 ; Received bits, display
.def rOne= R7 ; Zero/One-level
.def rI1L = R8 ; prelast bit burst, LSB
.def rI1H = R9 ; dto., MSB
.def rI0L = R10 ; last bit burst, LSB
.def rI0H = R11 ; dto., MSB
.def rIRL = R12 ; current bit burst, LSB
.def rIRH = R13 ; dto., MSB
.def rCntH= R14 ; MSB TC0-counter
.def rSreg= R15 ; SREG Status
.def rmp = R16 ; Multi-purpose register
.def rimp = R17 ; dto., Interrupts
.def rFlag= R18 ; Flags
.equ bOvf = 0 ; Overflow, check signal
.equ bAdj = 1 ; Learning mode active
.equ bSec = 2 ; End of half second
.equ bLng = 3 ; Long confirmation
.equ bTwo = 4 ; Second measuring
.equ bEqu = 5 ; Equal burst 1 and 2
.equ bDbl = 6 ; Double received
.equ bErr = 7 ; Not equal
.def rSel = R19 ; 0: Red on, 1: Red off
; 2: Yellow on, 3: Yellow off, 4: Green on, 5: Green off
.def rCtr = R20 ; Counter EEPROM/Input
.def rmo = R21 ; for LED control
; R22 .. R25 ; unused
; Used: X for pointer operations
; Used: Y for Storing in SRAM
; Used: Z for diverse purposes
;
; -------- SRAM -------------
.DSEG
.ORG 0x0060
sBuffer: ; Data storage for averaging
.Byte 16 ; Zero and One duration
sBufferEnd:
;
sCodes:
.Byte 4 ; Detection code Red, On/Off
.Byte 4 ; dto., Yellow, On/Off
.Byte 4 ; dto., Green, On/Off
sCodesKeyEnd:
sCodesOne:
.Byte 1 ; Zero/One-level
sCodesEnd:
;
; -------- Reset- and Int-Vectors ---
.CSEG
.ORG 0x0000
rjmp Start ; RESET-Vektor
rjmp Int0Isr ; INT0 Ext. Int Request 0
reti ; PCINT0 Pin Change Int Request 0
rjmp TC0OIsr ; TIM0_OVF TC0 Overflow
reti ; EE_RDY EEPROM Ready
reti ; ANA_COMP Analog Comparator
reti ; TIM0_COMPA TC0 Compare Match A
reti ; TIM0_COMPB TC0 Compare Match B
reti ; WDT Watchdog Time-out
reti ; ADC ADC Conversion Complete
;
; -------- Int Service routines ------
; Analyze pulse from IR-receiver
Int0Isr: ; ATtiny13 INT0-Interrupt
in rSreg,SREG ; Save SREG
tst rCntH ; Long signal?
brne Int0IsrLong
in rimp,TCNT0
cpi rimp,cHeader ; longer than header
brcs Int0IsrShort
; Header signal received, start new
Int0IsrLong:
clr rimp ; restart counter
out TCNT0,rimp
clr rCntH ; clear MSB counter byte
mov rBitsO,rBits
clr rBits ; received bits to 0
clr rIRL ; clear bit sampler register
clr rIRH
out SREG,rSreg ; done
reti
Int0IsrShort:
; Short data signal
in rimp,TCNT0 ; read timer
st Y+,rimp ; store in SRAM
cp rOne,rimp ; compare with zero/one level
rol rIRL ; shift bit into sample registers
rol rIRH
inc rBits ; increase number of bits
cpi YL,Low(sBufferEnd) ; pointer end?
brne Int0IsrRet ; not beyond
ldi YH,High(sBuffer) ; New start,
ldi YL,Low(sBuffer) ; to the buffer start
Int0IsrRet:
; Return from short signal
clr rimp ; clear timer
out TCNT0,rimp
out SREG,rSreg ; restore SREG
reti
;
; Analyze counter overflows
TC0OIsr:
in rSreg,SREG ; save SREG
inc rCntH ; count overflows
mov rimp,rCntH ; longer pause?
cpi rimp,cSekH ; half second over?
brne TC0OIsr1
sbr rFlag,1<<bSec ; set second flag
clr rCntH
rjmp TC0OIsrRet
TC0OIsr1:
cpi rimp,cChk ; check?
brne TC0OIsrRet
tst rBits
breq TC0OIsrRet
mov rI0H,rIRH ; save received bits
mov rI0L,rIRL ; into zero storage
mov rBitsO,rBits ; Copy number of bits
clr rBits
TC0OIsrRet:
out SREG,rSreg ; restore SREG
reti
;
; -------- Start, Init ------
Start:
; Init stack
ldi rmp,LOW(RAMEND)
out SPL,rmp
; Init port directions
sbi pDir,bRdD ; Outputs, Direction
sbi pDir,bGeD
sbi pDir,bGnD
; Port outputs inactive
.if cActiveH == 1
cbi pOut,bRdO ; Outputs, start values
cbi pOut,bYeO
cbi pOut,bGnO
.else
sbi pOut,bRdO
sbi pOut,bYeO
sbi pOut,bGnO
.endif
; Init key and IR receiver input
sbi pOut,bIrO ; Pullup IR receiver
; Clear flags
clr rFlag
; Pre value zero/one level
ldi rmp,cOne
mov rOne,rmp
; Buffer pointer for value storage
ldi YH,High(sBuffer)
ldi YL,Low(sBuffer)
; Read detection values from EEPROM
rcall ReadCodes
lds rOne,sCodesOne
; If key active adjust
nop ; wait a while
nop
sbis pIn,bKyI ; if key input inactive
sbr rFlag,1<<bAdj ; switch into learning mode
; Start timer TC0
ldi rmp,(1<<CS01)|(1<<CS00) ; Prescale by 64
out TCCR0B,rmp
ldi rmp,1<<TOIE0 ; Overflow Int
out TIMSK0,rmp
; Sleep mode idle, INT0/PCINT0 IR signals
ldi rmp,(1<<SE)|(1<<ISC01)
out MCUCR,rmp
ldi rmp,1<<INT0 ; INT0-Interrupts
out GIMSK,rmp
; Interrupts enable
sei
Loop:
sleep ; Sleep
nop ; wake up
sbrc rFlag,bOvf ; Analyze overflow?
rcall Overflow ; yes
sbrc rFlag,bSec ; Second clock?
rcall Second ; yes
rjmp Loop
;
Second:
; Half second over
cbr rFlag,1<<bSec
; Only in Adjust mode
sbrs rFlag,bAdj
ret ; not adjust
; Long On phase?
sbrs rFlag,bLng ; Confirmation?
rjmp LedBlink ; No
; Long On phase ending?
dec rCtr ; Count five seconds
brne SecondRet ; No
; Long On phase ending
cbr rFlag,1<<bLng ; Flag clear
; Next input position
rcall LedOff ; current LED off
subi rSel,-2 ; add two
cpi rSel,6 ; until rSel = 6
brcs LedBlink ; not yet
brne SecondEnd ; all adjusted
ldi rSel,1 ; go on with Off codes
rjmp LedBlink ; blink
SecondEnd:
; all channels adjusted
rcall WriteCodes ; Write codes to EEPROM
clr rSel ; Restart with channel Zero
cbr rFlag,1<<bAdj ; Clear adjust
; All outputs off
.if cActiveH == 1
cbi pOut,bRdO ; Output low
cbi pOut,bYeO
cbi pOut,bGnO
.else
sbi pOut,bRdO ; Output high
sbi pOut,bYeO
sbi pOut,bGnO
.endif
SecondRet:
ret
;
; Blink LED in rSel
LedBlink:
rcall GetOutPin ; Aktive pin to rmp
in rmo,pOut ; Read pins
eor rmp,rmo ; Reverse polarity
out pOut,rmp ; Write pins
ret
;
; LED rSel on
LedOn:
rcall GetOutPin ; Aktive pin to rmp
in rmo,pOut ; Read pins
.if cActiveH == 1
or rmp,rmo ; Activate pin
.else
com rmp ; invert
and rmp,rmo ; Pin to zero
.endif
out pOut,rmp ; Write pins
ret
;
; LED rSel off
LedOff:
rcall GetOutPin ; Aktive pin to rmp
in rmo,pOut ; Read pins
.if cActiveH == 1
com rmp ; invert
and rmp,rmo ; Pin to zero
.else
or rmp,rmo ; Pin to one
.endif
out pOut,rmp ; Write pins
ret
;
; Read current output to rmp
GetOutPin:
mov rmp,rSel ; Current position
lsr rmp ; Shift out On/Off bit
cpi rmp,1 ; Yellow LED active?
brcs GetOutPinRt ; no, red
brne GetOutPinGn ; no, green
ldi rmp,1<<bYeO ; Yellow
ret
GetOutPinRt:
ldi rmp,1<<bRdO ; Red
ret
GetOutPinGn:
ldi rmp,1<<bGnO ; Green
ret
;
; Overflow of timer, output result
;
Overflow:
cbr rFlag,1<<bOvf ; Flagge clear
; Check if learning mode
sbrs rFlag,bAdj
rjmp OverflowCheck ; no
; From here learning mode
OverflowCalc:
clr rmp ; Ints off
out GIMSK,rmp
out TIMSK0,rmp
; Average duration of the last 16 data bits
ldi ZH,High(sBuffer) ; Buffer start
ldi ZL,Low(sBuffer)
clr XH ; X is sum
clr XL
OverflowSum:
ld rmp,Z+ ; Byte from buffer
add XL,rmp ; add
ldi rmp,0 ; Overflow treatment
adc XH,rmp
cpi ZL,LOW(sBufferEnd) ; End?
brne OverflowSum ; no, go on
lsr XH ; div by 2
ror XL
lsr XH ; div by 4
ror XL
lsr XH ; div by 8
ror XL
lsr XH ; div by 16
ror XL
mov rOne,XL ; to detection register
sts sCodesOne,XL ; and to SRAM
; Compare two measurements
sbrc rFlag,bTwo ; Second burst?
rjmp OverflowZwei ; Compare both
sbr rFlag,1<<bTwo ; Set flag
mov rI1L,rI0L ; Copy value
mov rI1H,rI0H
rjmp OverflowNormal
OverflowZwei:
; Compare second burst
cbr rFlag,(1<<bTwo)|(1<<bEqu)
cp rI1L,rI0L ; Compare LSB
brne OverflowNormal ; unequal
cp rI1H,rI0H ; Compare MSB
brne OverflowNormal ; unequal
; Equal, check already defined
sbr rFlag,1<<bEqu ; Equal flag
ldi XH,High(sCodes) ; X on codes
ldi XL,Low(sCodes) ; in SRAM
OverflowDoppel:
cpi XL,Low(sCodesKeyEnd)
breq OverflowTasteOk ; Compare end of list
ld rmp,X+ ; Read LSB
cp rmp,rI1L ; compare LSB
ld rmp,X+ ; Read MSB
brne OverflowDoppel ; Already in list
cp rmp,rI1H ; compare MSB
brne OverflowDoppel ;
sbr rFlag,1<<bDbl ; Double flag
rjmp OverflowNormal
; Store Code, start long pause
OverflowTasteOk:
ldi XH,High(sCodes) ; To code start
ldi XL,Low(sCodes)
mov rmp,rSel ; active output
lsl rmp ; multiplied by two
add XL,rmp ; Add to pointer
ldi rmp,0 ; Overflow treatment
adc XH,rmp
st X+,rI1L ; Store value
st X,rI1H
rcall LedOn ; LED on
ldi rCtr,5 ; long pause
sbr rFlag,(1<<bEqu)|(1<<bLng)
rjmp OverflowNormal
OverflowCheck:
; Check received data set
ldi XH,High(sCodes) ; Pointer to codes
ldi XL,Low(sCodes)
ser rSel ; Start witt 0xFF
OverflowCheck1:
inc rSel ; Next value
cpi rSel,6 ; End reached?
brcc OverflowNf ; yes
ld rmp,X+ ; Read value from SRAM
cp rmp,rI0L ; Compare LSB
ld rmp,X+ ; Read MSB
brne OverflowCheck1 ; unequal
cp rmp,rI0H ; Compare MSB
brne OverflowCheck1 ; unequal
; Correct code received
cbr rFlag,(1<<bErr)|(1<<bDbl)
sbrs rSel,0 ; Pin to on?
rcall LedOn ; Pin on
sbrc rSel,0 ; Pin off?
rcall LedOff ; Pin off
mov rI1H,rI0H ; Copy value
mov rI1L,rI0L
rjmp OverflowNormal
OverflowNf:
; No match found
clr rSel ; Set zero
sbr rFlag,1<<bErr ; Set flag
OverflowNormal:
; Enable interrupts
clr rCntH
ldi rmp,1<<TOIE0 ; Timer Int
out TIMSK0,rmp
ldi rmp,1<<INT0 ; INT0-Interrupts
out GIMSK,rmp
ret
;
; Read codes from EEPROM
ReadCodes:
ldi ZH,0 ; Pointer to EEPROM address
ldi ZL,0
ldi XH,High(sCodes) ; Pointer to SRAM
ldi XL,Low(sCodes)
ldi rmp,sCodesEnd-sCodes ; Number
mov rCtr,rmp
ReadCodes1:
sbic EECR,EEPE ; wait until ready
rjmp ReadCodes1
out EEARL,ZL ; Address counter
sbi EECR,EERE ; Read enable
in rmp,EEDR ; Read byte
st X+,rmp ; store in SRAM
adiw ZL,1 ; Next address
dec rCtr ; Counter
brne ReadCodes1 ; Read on
cpi rmp,0x00 ; Zero/One at zero?
breq ReadCodes2 ; yes, data is corrupt
cpi rmp,0xFF ; Zero/One on FF?
brne ReadCodes3 ; Yes, data is corrupt
ReadCodes2:
rjmp StartInput ; Data corrupt, start new
ReadCodes3: ; Data is ok
ret
;
; Write codes to EEPROM
WriteCodes:
ldi ZH,0 ; Pointer to EEPROM address
ldi ZL,0
ldi XH,High(sCodes) ; Pointer to SRAM
ldi XL,Low(sCodes)
ldi rmp,sCodesEnd-sCodes
mov rCtr,rmp
WriteCodes1:
sbic EECR,EEPE ; Wait until ready
rjmp WriteCodes1 ; Wait on
clr rmp ; Erase and write
out EECR,rmp
out EEARL,ZL ; Address write
ld rmp,X+ ; Read byte from SRAM
out EEDR,rmp ; to EEPROM data register
cli ; disable ints to avoid timeout
sbi EECR, EEMPE ; Master Program Enable
sbi EECR, EEPE ; Program enable
sei ; enable Interrupts
adiw ZL,1 ; Increase address
dec rCtr ; Byte counter
brne WriteCodes1 ; go on
ret
;
; Delete all codes
; Set default Zero/One level
; Channel zero
; Set adjust flag
StartInput:
ldi ZH,High(sCodes) ; Pointer
ldi ZL,Low(sCodes)
clr rmp ; Zero
StartInput1:
st Z+,rmp ; Delete codes
cpi ZL,Low(sCodesEnd) ; End?
brne StartInput1 ; continue
ldi rmp,cOne ; Default Zero/One
sts sCodesOne,rmp ; to SRAM
mov rOne,rmp ; And in register
clr rSel ; Restart
sbr rFlag,1<<bAdj ; Lerning mode
ret
;
; Presetting of codes
; (Example TV remote control)
.ESEG
.ORG 0x00
EeBeg:
; red, key 1 on, key 4 off
.DW 0x0835,0xC8F5
; yellow, key 2 on, key 5 off
.DW 0x88B5,0x2815
; green, key 3 on, key 6 off
.DW 0x4875,0xA895
; Zero/One level
.DB cOne
EeEnd:
;
; End of source code
;