Path:
Home =>
AVR-overview =>
Applications =>
IR Remote Control =>
Measure control signals => ATmega8 code
(Diese Seite in Deutsch:
)
Analysis of IR remote control devices with an AVR ATmega8
;
; **********************************************
; * Measure signal durations for IR receivers *
; * Version 1 as of October 2010 *
; * (C)2010 by http://www.avr-asm-tutorial.net *
; **********************************************
;
; Included header file for target AVR type
.NOLIST
.INCLUDE "m8def.inc" ; Header for ATMEGA8
.LIST
;
; ============================================
; D E B E U G G I N G I N F O R M A T I O N
; ============================================
;
.equ debug = 0 ; enable debugging infos over PORTB
;
; ============================================
; H A R D W A R E I N F O R M A T I O N
; ============================================
;
; Receive and send over the RS232 interface
; PD0 is RXD input (Pin 2)
; PD1 is TXD output (Pin 3)
;
; Time measurement over INT0
; PD2 is input for the time measurements (Pin 4)
;
; Output signal
; 1 second duration on PD3 (Pin 5)
;
; ============================================
; P O R T S A N D P I N S
; ============================================
;
; External interrupt measure pulse durations
.equ pExt0I = PIND
.equ pbExt0I = PIND2
; TxD and RxD in UART communication
.equ pTxdD = DDRD
.equ pbTxdD = DDD1
.equ pRxdD = DDRD
.equ pbRxdD = DDD0
; Time signal 1 s
.equ pOutD = DDRD
.equ pbOutD = DDD3
.equ pOutP = PORTD
.equ pbOutP = PORTD3
;
; ============================================
; C O N S T A N T S T O C H A N G E
; ============================================
;
.equ cClock = 9830400 ; Xtal clock @ 9.8304 MHz
.equ cBaud = 38400 ; Baudrate for RS232 TX and RX
;
; ============================================
; F I X + D E R I V E D C O N S T A N T S
; ============================================
;
.equ cBaudDiv = cClock / 16 / cBaud - 1 ; Baudrate generator
.equ c10us = cClock / 100000 - 1 ; time bases
.equ c100us = cClock / 10000 - 1
.equ c1ms = cClock / 1000 - 1
.equ c10ms = cClock / 100 / 8 - 1
.equ cSec = cClock / 256 / 256
;
; ============================================
; R E G I S T E R D E F I N I T I O N S
; ============================================
;
; R0 used for lpm operations
.def rRes0 = R1 ; result bytes transfer from ext int
.def rRes1 = R2
.def rRes2 = R3
.def rD0 = R4 ; decimal subtractor bytes
.def rD1 = R5
.def rD2 = R6
.def rV0 = R7 ; binary to decimal conversion
.def rV1 = R8
.def rV2 = R9
; free: R10
.def rRBInL = R11 ; result buffer in pointer
.def rRBInH = R12
.def rRBOutL= R13 ; result buffer out pointer
.def rRBOutH= R14
.def rSreg = R15 ; save/restore SREG inside ints
.def rmp = R16 ; Multipurpose register outside ints
.def rimp = R17 ; Multipurpose register inside ints
.def rFlag = R18 ; Flag register
.equ bMode0 = 0 ; Mode bit 0
.equ bMode1 = 1 ; Mode bit 1
.equ bMOn = 2 ; Measuring on
.equ bXfer = 3 ; transfer of result ready
.equ bTxData = 4 ; transmit data results
.equ bTxBusy = 5 ; RS232 TX is busy
.equ bRxLine = 6 ; RS232 end of line
; free: R19..R21
.def rSec = R22 ; Counter for second pulse
.def rCnt2 = R23 ; counter byte 2
.def rCnt0 = R24 ; counter byte 0
.def rCnt1 = R25 ; counter byte 1
; used: X as buffer pointer for RS232 transmit
; used: Y pointer outside int
; used: Z pointer outside int (inside int: restored)
;
; ============================================
; S R A M D E F I N I T I O N S
; ============================================
;
.DSEG
.ORG 0x0060 ; Start of SRAM
;
sRxBPtr: ; RS232 RX buffer pointer
.byte 2
sRxBSta: ; RS232 RX buffer start
.byte 8
sRxBEnd: ; RS232 RX buffer end
;
sTxBSta: ; RS232 TX buffer start
.byte 11
sTxBEnd: ; RS232 TX buffer end
;
sRBSta: ; Result buffer
.equ nResults = (RAMEND-sRBSta-20) / 3 ; number of packs
.byte 3*nResults
sRBEnd:
;
; Format: Label: .BYTE N ; reserve N Bytes from Label:
;
; ============================================
; R E S E T A N D I N T V E C T O R S
; ============================================
;
.CSEG
.ORG $0000
;
rjmp Main ; Reset vector
rjmp Int0Isr ; INT0 External Interrupt Request 0
reti ; INT1 External Interrupt Request 1
reti ; TIMER2 COMP Timer/Counter2 Compare Match
reti ; TIMER2 OVF Timer/Counter2 Overflow
rjmp Tc1C ; TIMER1 CAPT Timer/Counter1 Capture Event
reti ; TIMER1 COMPA Timer/Counter1 Compare Match A
reti ; TIMER1 COMPB Timer/Counter1 Compare Match B
reti ; TIMER1 OVF Timer/Counter1 Overflow
rjmp Tc0Isr ; TIMER0 OVF Timer/Counter0 Overflow
reti ; SPI, STC Serial Transfer Complete
rjmp IntRx ; USART, RXC USART, Rx Complete
rjmp IntDre ; USART, UDRE USART Data Register Empty
rjmp IntTxC ; USART, TXC USART, Tx Complete
reti ; ADC ADC Conversion Complete
reti ; EE_RDY EEPROM Ready
reti ; ANA_COMP Analog Comparator
reti ; TWI Two-wire Serial Interface
reti ; SPM_RDY Store Program Memory Ready
;
; ============================================
; I N T E R R U P T S E R V I C E S
; ============================================
;
; Int0 service routine, low/high transition
Int0Isr:
in rSreg,SREG ; save SREG
sbis pExt0I,pbExt0I ; if input is low
sbr rCnt2,0x40 ; set High-bit
sbrc rFlag,bXfer ; transfer bit clear
sbr rCnt2,0xC0 ; set X error
sbr rFlag,1<<bXfer ; set transfer flag
mov rRes2,rCnt2 ; copy result to transfer
mov rRes1,rCnt1
mov rRes0,rCnt0
clr rCnt2 ; clear counter
clr rCnt1
clr rCnt0
.if debug == 1
sbic PORTB,0
rjmp Int0Isr1
sbi PORTB,0
rjmp Int0Isr2
Int0Isr1:
cbi PORTB,0
Int0Isr2:
.endif
out SREG,rSreg ; restore SREG
reti
;
; Timer/Counter Capture Event service routine
Tc1C:
in rSreg,SREG ; save SREG
adiw rCnt0,1 ; inc counter
brne Tc1C1
inc rCnt2 ; increase msb
cpi rCnt2,0xC0 ; overflow?
brcs Tc1C1
ldi rCnt2,0x80 ; signal overflow
mov rRes2,rCnt2 ; set transfer register
clr rCnt2
clr rRes1
clr rRes0
sbr rFlag,1<<bXfer ; set transfer bit
Tc1C1:
out SREG,rSreg
reti
; TC0 interrupt service routine
Tc0Isr:
in rSreg,SREG ; save SREG
dec rSec ; decrease counter
brne Tc0Isr2
ldi rSec,cSec ; restart counter
sbic pOutP,pbOutP ; output zero?
rjmp Tc0Isr1
sbi pOutP,pbOutP ; set one
rjmp Tc0Isr2
Tc0Isr1:
cbi pOutP,pbOutP ; set zero
Tc0Isr2:
out SREG,rSreg ; restore SREG
reti
; RS232 RX service routine
IntRx:
in rSreg,SREG ; save SREG
in rimp,UCSRA ; read error flag
andi rimp,(1<<FE)|(1<<DOR)|(1<<PE) ; check
in rimp,UDR ; read data
breq IntRx1 ; no error
ldi rimp,'?' ; signal an error
IntRx1:
out UDR,rimp ; echo character
push ZH ; save Z
push ZL
lds ZL,sRxBPtr ; read RX buffer pointer
lds ZH,sRxBPtr+1
cpi rimp,0x08 ; backspace?
brne IntRx2 ; no
cpi ZL,LOW(sRxBSta) ; first buffer position?
breq IntRx4 ; yes, ignore
sbiw ZL,1 ; back one space
rjmp IntRx3 ; save pointer
IntRx2:
st Z+,rimp ; store character
cpi ZL,LOW(sRxBEnd) ; beyond buffer end?
brcc IntRx4 ; pointer beyond end
IntRx3:
sts sRxBPtr,ZL ; save pointer
sts sRxBPtr+1,ZH
IntRx4:
pop ZL ; restore Z
pop ZH
cpi rimp,$0D ; new line?
brne IntRx5
sbr rFlag,1<<bRxLine
IntRx5:
out SREG,rSreg
reti
; RS232 UDRE service routine
IntDre:
in rSreg,SREG
ld rimp,X+ ; read next char
out UDR,rimp ; sent character
.if debug == 1
sbi PORTB,2
.endif
cpi rimp,0x0A ; last character?
brne IntDre1 ; no
ldi rimp,(1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN) ; enable TXC int
out UCSRB,rimp
IntDre1:
out SREG,rSreg ; restore SREG
reti
; RS232 TX sent service routine
IntTxC:
in rSreg,SREG
cbr rFlag,1<<bTxBusy ; clear busy flag
ldi rimp,(1<<RXCIE)|(1<<RXEN)|(1<<TXEN) ; disable TXC int
out UCSRB,rimp
.if debug == 1
cbi PORTB,1
cbi PORTB,2
.endif
out SREG,rSreg
reti
;
; ============================================
; M A I N P R O G R A M I N I T
; ============================================
;
Main:
; Init stack
ldi rmp, HIGH(RAMEND) ; Init MSB stack
out SPH,rmp
ldi rmp, LOW(RAMEND) ; Init LSB stack
out SPL,rmp
; Clear flag bits
clr rFlag ; set mode 10 us
; Init result buffer pointers
ldi rmp,HIGH(sRBSta) ; set buffer pointers
mov rRBInH,rmp
mov rRBOutH,rmp
ldi rmp,LOW(sRBSta)
mov rRBInL,rmp
mov rRBOutH,rmp
; Init RS232 receive buffer
ldi rmp,HIGH(sRxBSta) ; Init buffer pointer
sts sRxBPtr+1,rmp
ldi rmp,LOW(sRxBSta)
sts sRxBPtr,rmp
; Init Port for RS232
sbi pTxdD,pbTxdD ; TX output
cbi pRxdD,pbRxdD ; RX input
; Init RS232 hardware
ldi rmp,HIGH(cBaudDiv) ; set baud rate
out UBRRH,rmp
ldi rmp,LOW(cBaudDiv)
out UBRRL,rmp
ldi rmp,(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0) ; Mode 8N1
out UCSRC,rmp
ldi rmp,(1<<RXEN)|(1<<TXEN)|(1<<RXCIE) ; RX and Int Enable
out UCSRB,rmp
; Send characters over RS232
rcall sendmenue
; Debugging info
.if debug == 1
ldi rmp,0xFF ; Port B is output
out DDRB,rmp
.endif
; Init Timer 0
sbi pOutD,pbOutD ; Port Bit for signal out
ldi rmp,1<<CS02 ; prescaler = 256, start timer
out TCCR0,rmp
; Init Timer 1
ldi rmp,HIGH(c10us) ; set CTC value
out ICR1H,rmp
ldi rmp,LOW(c10us)
out ICR1L,rmp
; switch timer off
clr rmp ; set mode timer/counter 1
out TCCR1A,rmp
ldi rmp,(1<<WGM13)|(1<<WGM12) ; stop timer
out TCCR1B,rmp
ldi rmp,1<<TOIE0 ; enable ints TC0, disable ints TC1
out TIMSK,rmp
; Init general int control
clr rmp ; external ints off
out GICR,rmp
; Enable sleep mode idle
ldi rmp,(1<<ISC00)|(1<<SE) ; sleep enable and external ints both edges on
out MCUCR,rmp
sei
;
; ============================================
; P R O G R A M L O O P
; ============================================
;
Loop:
sleep ; go to sleep
nop ; dummy for wake up
sbrc rFlag,bXfer ; transfer of data?
rcall Xfer
sbrc rFlag,bRxLine ; RX line complete?
rcall RxLine
sbrc rFlag,bTxData ; data to transfer?
rcall TxData
rjmp loop ; go back to loop
;
; Transfer a result
;
Xfer:
cli ; stop interrupts
mov rD2,rRes2 ; copy result
mov rD1,rRes1
mov rD0,rRes0
cbr rFlag,1<<bXfer ; clear transfer bit
sei ; enable interrupts
.if debug == 1
sbi PORTB,3
.endif
mov rmp,rFlag ; read flags
andi rmp,0x03
cpi rmp,0x03
brne Xfer1
mov rmp,rD2 ; copy upper error bits
andi rmp,0xC0 ; isolate error bits
mov R0,rmp ; store error bits
ldi rmp,0x3F ; clear error bits
and rD2,rmp
lsr rD2 ; div by 8
ror rD1
ror rD0
lsr rD2
ror rD1
ror rD0
lsr rD2
ror rD1
ror rD0
or rD2,R0
Xfer1:
mov ZH,rRBInH ; copy in-pointer
mov ZL,rRBInL
st Z+,rD0 ; copy result to next buffer
st Z+,rD1
st Z+,rD2
rcall Adjust
mov rRBInH,ZH ; set pointer
mov rRBInL,ZL
sbr rFlag,1<<bTxData ; set flag
TxData:
sbrc rFlag,bTxBusy ; Transmitter not busy
ret
cp rRBOutL,rRBInL ; In/Out pointer equal?
brne TxData1
cp rRBOutH,rRBInH
brne TxData1
cbr rFlag,1<<bTxData ; clear TX data flag
ret
TxData1:
mov ZH,rRBOutH ; pointer to output
mov ZL,rRBOutL
ld rV0,Z+ ; read value from buffer to register
ld rV1,Z+
ld rmp,Z+
rcall Adjust ; correct buffer pointer
mov rRBOutH,ZH ; store output pointer
mov rRBOutL,ZL
sbrc rmp,7 ; error flag?
rjmp TxData3
sbrc rmp,6 ; High/Low flag?
rjmp TxData2
mov rV2,rmp ; copy to result byte
ldi rmp,'L' ; set low char
rjmp TxData5
TxData2:
andi rmp,0x3F ; clear high bit
mov rV2,rmp
ldi rmp,'H' ; set high char
rjmp TxData5
TxData3:
sbrc rmp,6 ; V or X error?
rjmp TxData4
andi rmp,0x3F
mov rV2,rmp
ldi rmp,'V' ; set overflow error
rjmp TxData5
TxData4:
andi rmp,0x3F ; clear high bits
mov rV2,rmp
ldi rmp,'X' ; set data missed error
TxData5:
ldi YH,HIGH(sTxBSta) ; pointer to TX buffer
ldi YL,LOW(sTxBSta)
st Y+,rmp ; write first char to buffer
set ; T flag is leading zero suppression
tst rV2 ; check if Byte3 = 0
breq TxData11 ; skip triples
ldi ZH,HIGH(2*DecTab3) ; point to decimal table
ldi ZL,LOW(2*DecTab3)
TxData6:
lpm rD0,Z+ ; read decimal value from table
tst rD0 ; zero?
breq TxData10 ; end of triple conversion
lpm rD1,Z+
lpm rD2,Z+
ldi rmp,'0' ; counter
TxData7:
sub rV0,rD0 ; subtract
sbc rV1,rD1
sbc rV2,rD2
brcs TxData8 ; carry
inc rmp ; count on
rjmp TxData7
TxData8:
add rV0,rD0 ; add again
adc rV1,rD1
adc rV2,rD2
brtc TxData9
cpi rmp,'0' ; leading zero?
breq TxData6
TxData9:
st Y+,rmp
clt ; no leading zeroes any more
rjmp TxData6
TxData10:
ldi ZH,HIGH(2*DecTab1) ; next with 1.000
ldi ZL,LOW(2*DecTab1)
rjmp TxData12
TxData11:
ldi ZH,HIGH(2*DecTab2) ; next with 10.000
ldi ZL,LOW(2*DecTab2)
TxData12:
lpm rD0,Z+ ; read decimal
tst rD0 ; already at end?
breq TxData16
lpm rD1,Z+
ldi rmp,'0'
TxData13:
sub rV0,rD0 ; subtract
sbc rV1,rD1
brcs TxData14
inc rmp
rjmp TxData13
TxData14:
add rV0,rD0 ; add again
adc rV1,rD1
brtc TxData15
cpi rmp,'0'
breq TxData12
TxData15:
Bin2Dec25:
st Y+,rmp
clt
rjmp TxData12
TxData16:
ldi rmp,'0' ; last digit
add rmp,rV0
st Y+,rmp
ldi rmp,0x0D ; add ch/lf
st Y+,rmp
ldi rmp,0x0A
st Y+,rmp
ldi XH,HIGH(sTxBSta) ; point to start
ldi XL,LOW(sTxBSta)
ld rmp,X+ ; read first character
out UDR,rmp ; send first character
sbr rFlag,1<<bTxBusy ; set busy flag
nop
ldi rmp,(1<<RXCIE)|(1<<UDRIE)|(1<<RXEN)|(1<<TXEN) ; enable UDRE ints
out UCSRB,rmp
.if debug == 1
sbi PORTB,1
.endif
ret
;
; decimal table
DecTab3:
.db 0x40,0x42,0x0F,0xA0,0x86,0x01,0x10,0x27,0x00,0x00 ; 1.000.000, 100.000 and 10.000
DecTab2:
.db 0x10,0x27 ; 10.000
DecTab1:
.db 0xE8,0x03,0x64,0x00,0x0A,0x00 ; 1.000, 100, 10
.db 0x00,0x00
;
; Adjust the buffer address in Z
;
Adjust:
cpi ZL,LOW(sRBEnd) ; compare LSB
brcc Adjust1
cpi ZH,HIGH(sRBEnd)-1 ; carry, compare MSB
brcs Adjust3 ; dont adjust
rjmp Adjust2 ; start from beginning
Adjust1:
cpi ZH,HIGH(sRBEnd) ; no carry, compare MSB
brcs Adjust3 ; don't adjust
Adjust2:
ldi ZH,HIGH(sRBSta) ; start new
ldi ZL,LOW(sRBSta)
Adjust3:
ret
;
; Received a line over UART
;
RxLine:
cbr rFlag,1<<bRxLine ; clear flag
ldi ZH,HIGH(sRxBSta) ; reset pointer
ldi ZL,LOW(sRxBSta)
cli ; disable ints
sts sRxBPtr+1,ZH
sts sRxBPtr,ZL
ld rmp,Z ; read first char
sei ; enable ints
cpi rmp,'?' ; help?
brne RxLine0
rjmp sendmenue
RxLine0:
cpi rmp,'a' ; Mode 1?
brne RxLine1
andi rFlag,0xFC ; clear mode bits
ldi rmp,HIGH(c10us) ; set CTC value
out ICR1H,rmp
ldi rmp,LOW(c10us)
out ICR1L,rmp
rjmp RxLine7
RxLine1:
cpi rmp,'b' ; Mode 2?
brne RxLine2
andi rFlag,0xFC
ori rFlag,1<<bMode0
ldi rmp,HIGH(c100us) ; set CTC value
out ICR1H,rmp
ldi rmp,LOW(c100us)
out ICR1L,rmp
rjmp RxLine7
RxLine2:
cpi rmp,'c' ; Mode 3?
brne RxLine3
andi rFlag,0xFC
ori rFlag,1<<bMode1
ldi rmp,HIGH(c1ms) ; set CTC value
out ICR1H,rmp
ldi rmp,LOW(c1ms)
out ICR1L,rmp
rjmp RxLine7
RxLine3:
cpi rmp,'d' ; Mode 4?
brne RxLine4
andi rFlag,0xFC
ori rFlag,(1<<bMode1)|(1<<bMode0)
ldi rmp,HIGH(c10ms) ; set CTC value
out ICR1H,rmp
ldi rmp,LOW(c10ms)
out ICR1L,rmp
rjmp RxLine7
RxLine4:
cpi rmp,'0' ; Off?
brne RxLine5
cbr rFlag,1<<bMOn
; switch timer off
ldi rmp,(1<<WGM13)|(1<<WGM12) ; stop timer
out TCCR1B,rmp
ldi rmp,1<<TOIE0 ; disable TC1 timer interrupts
out TIMSK,rmp
clr rmp ; external ints off
out GICR,rmp
rjmp RxLine7
RxLine5:
cpi rmp,'1' ; On?
brne RxLine7
ori rFlag,1<<bMOn ; set On flag
rcall sendmenue2
ldi rmp,0x0D
rcall sendrmp
ldi rmp,0x0A
rcall sendrmp
; switch timer on
clr rCnt2 ; clear counter
clr rCnt1
clr rCnt0
mov rmp,rFlag ; read mode
andi rmp,0x03 ; mode bits both 1?
cpi rmp,0x03
ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10) ; prescale =1
brne RxLine6
ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS11) ; prescale =8
RxLine6:
out TCCR1B,rmp
ldi rmp,(1<<TICIE1)|(1<<TOIE0) ; enable ICP interrupts
out TIMSK,rmp
ldi rmp,1<<INT0 ; enable INT0
out GICR,rmp
ret
RxLine7:
rjmp sendmenue2
;
; Send Menu over RS232
;
sendmenue:
ldi ZH,HIGH(2*sendmenuetext) ; Point to text
ldi ZL,LOW(2*sendmenuetext)
sendmenue1:
lpm rmp,Z+
tst rmp ; end of text?
breq sendmenue2
rcall sendrmp ; send character
rjmp sendmenue1
sendmenue2:
ldi rmp,0x0D
rcall sendrmp
ldi rmp,0x0A
rcall sendrmp
mov rmp,rFlag ; read flag
andi rmp,0x03 ; isolate mode
subi rmp,-'A'
sbrs rFlag,bMOn ; is off?
subi rmp,-0x20
rcall sendrmp
ldi rmp,'>'
rjmp sendrmp
;
; Send character in rmp over UART
;
SendRmp:
sbis UCSRA,UDRE ; wait until buffer clear
rjmp SendRmp
out UDR,rmp ; send char in rmp
ret
; Menue Text
sendmenuetext:
;.db 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
;.db 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
;.db 0x00,0x00
;.db " ",0x00
.db 0x0C, 0x0D,0x0A,0x0D,0x0A,"Measuring device for pulse length",0x0D,0x0A
.db "------- (C)2010 by DG4FAC ------- ",0x0D,0x0A
.db "? = this menue",0x0D,0x0A
.db "0 = switch measurement off",0x0D,0x0A
.db "1 = switch measurement on ",0x0D,0x0A
.db "a = time base = 10 us ",0x0D,0x0A
.db "b = time base = 100 us",0x0D,0x0A
.db "c = time base = 1 ms",0x0D,0x0A
.db "d = time base = 10 ms",0x00
;
; End of source code
;
To the top of page
©2010 by http://www.avr-asm-tutorial.net