;;; Interrupt-driven RC5 decoder for the Atmel ATmega103L. ;;; ;;; Copyright 2005 Marko Mäkelä (marko.makela (at) iki (dot) fi). ;;; ;;; This program is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 2 of the License, or ;;; (at your option) any later version. ;;; ;;; This program is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program; if not, write to the Free Software ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; ;;; The RC5 decoding algorithm is a state machine with ;;; interrupt-driven transitions. The state machine, based on the ;;; diagram at http://www.clearwater.com.au/rc5/, will measure the ;;; cycle time (T, nominally 16/9 ms) from the first start bit and ;;; detect two signal durations, short (T/2, or 1/4*T..3/4*T) and long ;;; (T, or 3/4*T..5/4*T). Durations outside the allowed range will ;;; reset the decoder state machine to the idle state, as will signal ;;; transitions not depicted in the state machine. ;;; ;;; The connected states are as follows: ;;; idle <-> measure <-> start1 <-> mid1 <-> mid0 <-> start0 ;;; From each state, there are error transitions to the idle state. ;;; ;;; The non-error transitions are as follows. ;;; ;;; idle -| measure (start timer to measure half cycle width, T/2) ;;; measure =| start1 (store the measured cycle width, set bitcnt = 13) ;;; start1 => mid1 (output '1' bit, decrement bitcnt) ;;; mid1 -> start1 ;;; mid1 ==> mid0 (output '1' bit, decrement bitcnt) ;;; mid0 --> mid1 (output '0' bit, decrement bitcnt) ;;; mid0 => start0 ;;; start0 -> mid0 (output '0' bit, decrement bitcnt) ;;; ;;; The accepting (final) states are start1 and mid0 (when bitcnt = 0). ;;; ;;; Legend: ;;; <-> connected states ;;; -| high-to-low transition ;;; =| low-to-high transition ;;; ==> long high pulse ;;; --> long low pulse ;;; => short high pulse ;;; -> short low pulse ;;; ;;; The signal is to be connected to PE7 (INT7). .equ SREG, 0x3f .equ SPH, 0x3e .equ SPL, 0x3d .equ XDIV, 0x3c .equ RAMPZ, 0x3b .equ EICR, 0x3a .equ EIMSK, 0x39 .equ EIFR, 0x38 .equ TIMSK, 0x37 .equ TIFR, 0x36 .equ MCUCR, 0x35 .equ MCUSR, 0x34 .equ TCCR0, 0x33 .equ TCNT0, 0x32 .equ OCR0, 0x31 .equ ASSR0, 0x30 .equ TCCR1A, 0x2f .equ TCCR1B, 0x2e .equ TCNT1H, 0x2d .equ TCNT1L, 0x2c .equ OCR1AH, 0x2b .equ OCR1AL, 0x2a .equ OCR1BH, 0x29 .equ OCR1BL, 0x28 .equ ICR1H, 0x27 .equ ICR1L, 0x26 .equ TCCR2, 0x25 .equ TCNT2, 0x24 .equ OCR2, 0x23 .equ WDTCR, 0x21 .equ EEARH, 0x1f .equ EEARL, 0x1e .equ EEDR, 0x1d .equ EECR, 0x1c .equ PORTA, 0x1b .equ DDRA, 0x1a .equ PINA, 0x19 .equ PORTB, 0x18 .equ DDRB, 0x17 .equ PINB, 0x16 .equ PORTC, 0x15 .equ PORTD, 0x12 .equ DDRD, 0x11 .equ PIND, 0x10 .equ SPDR, 0x0f .equ SPSR, 0x0e .equ SPCR, 0x0d .equ UDR, 0x0c .equ USR, 0x0b .equ UCR, 0x0a .equ UBRR, 0x09 .equ ACSR, 0x08 .equ ADMUX, 0x07 .equ ADCSR, 0x06 .equ ADCH, 0x05 .equ ADCL, 0x04 .equ PORTE, 0x03 .equ DDRE, 0x02 .equ PINE, 0x01 .equ PINF, 0x00 .equ RAMEND, 0x0FFF ;Last On-Chip SRAM location .equ E2END, 0x0FFF ;Last EEPROM location .equ BAUDRATE, 0 ; disable serial line ; .equ BAUDRATE, 12 ; 19200 bps at 4 MHz ;; wait for UART Data Register Empty .macro waitdre sbis USR, 5 rjmp .-4 ; wait for UART Data Register Empty (DRE) .endm .set zero, 0 ; a register that is always zero .set timer, 1 ; timer counter value at start of interrupt .set cycleQ, 2 ; quarter cycle (1/4*T, minimum signal time) .set cycleH, 3 ; half cycle (1/2*T) .set irhi, 4 ; RC5 data register, high part .set irlo, 5 ; RC5 data register, low part .set irlasthi, 6 ; last decoded RC5 data, high part .set irlastlo, 7 ; last decoded RC5 data, low part .set temp, 16 ; temporary register .set bitcnt, 17 ; bit count .set irmode, 18 ; RC5 decoder state: mode .equ RC5IDLE, 0 ; decoder idle: await low pulse .equ RC5MEAS, 1 ; measure 1st low pulse width ;; The numeric values of the following modes must be in ;; ascending sequence, and RC5ST0 must be divisible by 4. .equ RC5ST0, 4 ; start of '0' .equ RC5ST1, 5 ; start of '1' (accepting state) .equ RC5MI1, 6 ; middle of '1' .equ RC5MI0, 7 ; middle of '0' (accepting state) .text rjmp reset ;Reset vector nop rjmp error ;External Interrupt 0 nop rjmp error ;External Interrupt 1 nop rjmp error ;External Interrupt 2 nop rjmp error ;External Interrupt 3 nop rjmp error ;External Interrupt 4 nop rjmp error ;External Interrupt 5 nop rjmp error ;External Interrupt 6 nop rjmp ir_int ;External Interrupt 7 nop rjmp error ;Output Compare 2 nop rjmp error ;Overflow 2 nop rjmp error ;Input Capture 1 nop rjmp error ;Output Compare 1A nop rjmp error ;Output Compare 1B nop rjmp error ;Overflow 1 nop rjmp error ;Output Compare 0 nop rjmp ovf0 ;Overflow 0 nop rjmp error ;SPI nop rjmp error ;UART Receive Complete nop rjmp error ;UART Data Register Empty nop rjmp error ;UART Transmit Complete nop rjmp error ;ADC Conversion Complete nop rjmp error ;EEPROM Write Complete nop rjmp error ;Analog Comparator ;;; initialization reset: ldi temp, 0x80 out ACSR, temp ; disable the analog comparator (saves power) .if BAUDRATE ;; initialize the UART ldi temp, BAUDRATE out UBRR, temp ldi temp, 0x08 out UCR, temp ; enable TxD .endif ldi temp, 0x20 out MCUCR, temp ; enable idle mode sleep clr zero ir_fail: ovf0: ; timer overflow: switch to idle mode ser temp out DDRB, temp out PORTB, temp out PORTE, temp ldi temp, RAMEND & 0xff out SPL, temp ldi temp, RAMEND >> 8 out SPH, temp ; reset the stack pointer ldi temp, 0x80 out EIMSK, temp ; enable INT7 interrupt out EICR, temp ; enable INT7 interrupt on falling edge out EIFR, temp ; clear INT7 status out TCCR0, zero ; stop Timer/Counter 0 ldi temp, 1 out TIFR, temp ; clear Timer/Counter 0 overflow interrupt out TIMSK, temp ; enable Timer/Counter 0 overflow interrupt ldi irmode, RC5IDLE ; switch to idle mode sei ; enable interrupts ;; idle loop sleep rjmp .-4 ;; INT7 ir_int: in timer, TCNT0 ; read the timer out TCNT0, zero ; zero the timer ldi temp, 4 out TCCR0, temp ; start Timer/Counter 0 at 4 MHz/64 in temp, EICR subi temp, 0x40 ; toggle the triggering edge ori temp, 0x80 out EICR, temp ; trigger the interrupt on the opposite edge cpi irmode, RC5MEAS breq ir_measure brsh ir_data ;; in idle mode, awaiting falling edge andi temp, 0x40 ; read the pin state breq ir_fail ; got a rising edge: remain idle ;; measure the width of the low pulse ldi irmode, RC5MEAS reti ir_measure: andi temp, 0x40 brne ir_fail ; got a falling edge: error => go idle mov temp, timer cpi temp, 68 ; is the pulse wider than 1.1 ms? brsh ir_fail cpi temp, 43 ; is the pulse shorter than 0.7 ms? brlo ir_fail mov cycleQ, timer ; T/2 mov cycleH, timer ; T/2 lsr cycleQ ; T/4 ldi irmode, RC5ST1 ; got a '1' bit clr irlo ; clear the RC5 data registers clr irhi ldi bitcnt, 13 ; receive the 2nd start bit and 12 data bits reti ir_data: ; got a data symbol sub timer, cycleQ ; ..1/4*T brlo ir_fail ; too short pulse: error => go idle sub timer, cycleH brlo ir_short ; 1/4*T..3/4*T cp timer, cycleH brsh ir_fail ; too wide pulse: error => go idle ;; got long pulse (3/4*T..5/4*T) cpi irmode, RC5MI1 brlo ir_fail ; expected short pulse ldi temp, 1 ; RC5MI1 <-> RC5MI0 ir_bit: bst irmode, 0 ; decoded one bit rol irlo rol irhi bld irlo, 0 ; copy the bit from the RC5 mode label eor irmode, temp ; switch mode, wait for next transition dec bitcnt breq ir_lastbit ; got last bit brpl ir_mid rjmp ir_fail ; too many bits (bitcnt < 0) ir_lastbit: cpi irmode, RC5ST1 breq ir_done cpi irmode, RC5MI0 brne ir_mid ir_done: ; got the whole code word .if BAUDRATE ;; got all => display waitdre out UDR, irhi out UDR, irlo .endif ldi irmode, RC5IDLE out TCCR0, zero ; stop Timer/Counter 0 ;; got a new keypress? cpse irhi, irlasthi rjmp ir_newkey cpse irlo, irlastlo rjmp ir_newkey reti ; repeated keypress ir_short: ;; got short pulse (1/4*T..3/4*T) ldi temp, 3 ; RC5ST0 <-> RC5MI0, RC5ST1 <-> RC5MI1 cpi irmode, RC5ST1 ; read bit from status sbrs irmode, 1 ; if in RC5MI0 or RC5MI1, do nothing rjmp ir_bit ; got bit (was in RC5ST0 or RC5ST1) eor irmode, temp ; switch mode, wait for next transition tst bitcnt breq ir_lastbit ; got last half of the last bit? ir_mid: reti ir_newkey: ; got new (non-repeated) keypress mov irlasthi, irhi ; remember the keypress mov irlastlo, irlo mov temp, irhi andi temp, ~8 ; remove the toggle bit cpi temp, 0x17 ; got 2nd start bit? got system bits 111xx? brne ir_ignore mov temp, irlo cpi temp, 0xbd ; got system bits xxx10 and command 111101? brne ir_ignore ;; Power key pressed: increment PORTB in temp, PORTB inc temp out PORTB, temp reti ir_ignore: ;; Ignore the keypress: invert PORTB in temp, PORTB com temp out PORTB, temp reti ;;; catch unhandled interrupts error: in temp, PIND out PORTB, temp rjmp error ; Local variables: ; compile-command: "avr-as rc5-mega103t.s && objcopy -O ihex a.out rc5-mega103t.hex && cisp -c stk200 /dev/parport0 -e -l rc5-mega103t.hex -v rc5-mega103t.hex" ; End: