Full Version : Lamconelli's IR RC5 Decoder (ASM)
avr >>LIGHT & VISION PROJECTS >>Lamconelli's IR RC5 Decoder (ASM)


Admin3- 04-19-2006
Claudio Lanconelli's IR RC5 Program V1.02

CODE

;FILE: MACRO.INC
.LISTMAC



.MACRO  ADDI    

subi @0, -(@1)

.ENDMACRO



;skip next instruction if not equal to zero

.MACRO skipne                  

.SET    _skipne = PC+2

brne    _skipne

.ENDMACRO



;skip next instruction if carry set

.MACRO skipcs                  

.SET    _skipcs = PC+2

brcs    _skipcs

.ENDMACRO



;skip next instruction if carry clear

.MACRO skipcc

.SET    _skipcc = PC+2

brcc    _skipcc

.ENDMACRO



;skip next instruction if equal to zero

.MACRO  skipeq

.SET    _skipeq = PC+2

breq    _skipeq

.ENDMACRO



;skip next instruction if lower

.MACRO skiplo                  

.SET    _skiplo = PC+2

brlo    _skiplo

.ENDMACRO



;skip next instruction if half carry set

.MACRO skiphs                  

.SET    _skiphs = PC+2

brhs    _skiphs

.ENDMACRO



;skip next instruction if half carry clear

.MACRO skiphc

.SET    _skiphc= PC+2

brhc   _skiphc

.ENDMACRO



;skip next instruction if T set

.MACRO skipts                  

.SET    _skipts = PC+2

brts    _skipts

.ENDMACRO



;skip next instruction if T clear

.MACRO skiptc                  

.SET    _skiptc = PC+2

brtc    _skiptc

.ENDMACRO



;skip next instruction if minus

.MACRO skipmi

.SET    _skipmi = PC+2

brmi    _skipmi

.ENDMACRO



;skip next instruction always

.MACRO skipnext

.SET _skipnext = PC+2

rjmp _skipnext

.ENDMACRO



;wait two cycles but waste only one instruction

.MACRO  doublenop

.SET    _doublenop = PC+1

rjmp    _doublenop

.ENDMACRO



;compare Port bit with T, and branch to @2 if are not equal

.MACRO cpeqPortBit

sbic @0,@1

.SET _cpPortBit1 = PC+3

rjmp _cpPortBit1

brts @2

.SET _cpPortBit0 = PC+2

rjmp _cpPortBit0

brtc @2

.ENDMACRO



;compare Port bit with T, and branch to @2 if are equal

.MACRO cpnePortBit

sbic @0,@1

.SET _cpPortBit1 = PC+3

rjmp _cpPortBit1

brtc @2

.SET _cpPortBit0 = PC+2

rjmp _cpPortBit0

brts @2

.ENDMACRO



;compare Register bit with T, and branch to @2 if are not equal

.MACRO cpeqRegBit

sbrc @0,@1

.SET _cpRegBit1 = PC+3

rjmp _cpRegBit1

brts @2

.SET _cpRegBit0 = PC+2

rjmp _cpRegBit0

brtc @2

.ENDMACRO



;compare Register bit with T, and branch to @2 if are equal

.MACRO cpneRegBit

sbrc @0,@1

.SET _cpRegBit1 = PC+3

rjmp _cpRegBit1

brtc @2

.SET _cpRegBit0 = PC+2

rjmp _cpRegBit0

brts @2

.ENDMACRO


;***************************************************************************
;* IR.ASM
;*
;* Decode IR RC5 using
;* AVR MINI THREADS v.1.02
;* ---> nanoKernel for the AVR AT90S1200
;*
;* To try this code you need AvrTools from ATMEL, PonyProg and a simple
;* hardware (See schematics in PDF).
;*
;* Copyright (c) 1998 by Claudio Lanconelli
;* e-mail: lanconel@cs.unibo.it
;* WWW: http://www.cs.unibo.it/~lanconel
;*
;/ 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 version2 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 (see COPYING); if not, write to the //
;/ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //
;***************************************************************************

.DEVICE AT90S1200

.include "macro.inc"
.include "1200def.inc"

;
; The biphase decoding algorithm used here need 4 bytes + 1 bit, it doesn't need any
; external interrupts, but it needs a sampling time that you can tune to your needs.
; In practice you can use the timer interrupt with the timer interval you need in
; your application. Here the timer interrupt is used for the serial thread (4800Hz) AND
; for the RC5 thread.
;
; Follows the RC5 decoder:
;-the first thread (main) wait for a signal from the RC5 thread. When it wakes up
; blink the led and send the request to transmit the 16word received to the serial thread
;-the second thread (RC5) decodes data from the IR receiver (i.e. TFMS5360) and store
; it in IrDataH + IrDataL. This 16bit data have the following format:
; bit 15: not used, always 0
; bit 14-12: RC5 start flag, always 4 for valid RC5 codes
; bit 11: RC5 control, toggle on every key stroke (is the same for repeat)
; bit 10-6: RC5 address
; bit 5-0: RC5 command
; It wait for a signal from the timer, and when it wakes up sample the IrRx and
; decode biphase RC5 signal. When the code is complete signal the main thread.
;-the third thread (Serial) wait for a signal from the timer. When it wakes up
; send data to the SerTx line at 4800N81. Most PC don't need a MAX232 to translate the
; 5V output to 12V.
;



.CSEG

;***************************************************************************
;* VARIABLE ASSIGNEMENTS
;***************************************************************************

   .def    SaveThr1Status =     r0;status copy for thread 1
   .def    SaveThr2Status =     r1  ;status copy for thread 2
   .def    SaveThr3Status =     r2  ;status copy for thread 3
   .def    SaveStatus  =     r3  ;status copy for interrupts

   .def    SerNbit  =    r16    ;serial bit to transmit
   .def    SerData  =    r17    ;serial data byte to transmit

   .def    IrCount  =    r18
   .def    IrIdx  =    r19
   .def    IrBits  =    r20
   .def    IrDataH  =    r4
   .def    IrDataL  =    r21

   .equ    lastBit  =    7

   .def    Main1  =     r24  ;Temp variable used by main program
   .def    Temp1  =    r30
   .def    MiscFlags  =     r31  ;threads status flags + signal flags

 ;flags bit definition
   .equ    Thread1Status    =     0  ; 1 --> ready, 0 --> waiting
   .equ    Thread2Status    =     1
   .equ    Thread3Status    =     2
   .equ    Thread1Signal    =     3
   .equ    Thread2Signal    =     4
   .equ    Thread3Signal    =     5

   .equ    SerRequest =     6
   .equ    IrLock =     7

   .equ    THRSTATUSMASK    =     7
   .equ    SIGNALMASK =         112


;***************************************************************************
;* Port Pin Assignements
;***************************************************************************

 ;port D bit definitions
   .equ    Led1    =    0    ;out
   .equ    Led2    =    1    ;out
   .equ    IrRx    =    2    ;in (pullup)

 ;port B bit definitions
   .equ    Key1    =    0    ;in (pullup)
   .equ    Key2    =    1    ;in (pullup)
   .equ    SerRx   =    3    ;in
   .equ    SerTx   =    4    ;out
   

;********************
; Constants
;********************
   .equ    RC5_WORDLEN    =     14

 ;the following constant is crucial for RC5 decoding. The standard bit
 ;time is 1.778 msec. RC5 thread can sample the IrRx line at any frequency
 ; from 1/1.778E-3 * 4 = 2250Hz to 1/1.778E-3 * 255 = 143KHz
 ; In this case we have the sampling frequency equal to 4800, so RC5_BITTIME
 ; is 4800 / (1/1.778E-3) = 8.53, rounded to 9.
   .equ    RC5_BITTIME    =     9

;***************************************************************************
;* VECTORS
;***************************************************************************

       rjmp RESET            ;Reset Handle
       rjmp INT0DRV            ;Ext. interrupt request 0
       rjmp TIMERDRV        ;Timer


;***************************************************************************
;*
;* MAIN PROGRAM
;*
;***************************************************************************
;* INITIALIZATION
;***************************************************************************

RESET:
       ldi    Main1, 0b00000011    ;set port D dir
       out    DDRD, Main1
       ldi    Main1, 0b00000111    ;preset output state (activate INT0 pull-up)
       out    PortD, Main1
       ldi    Main1, 0b00010000    ;set port B dir
       out    DDRB, Main1
       ldi    Main1, 0b00000011    ;turn on pullups on key inputs
       out    PortB, Main1

       ldi    Main1, 4
       out    TCCR0, Main1        ;set prescaler to CK/256
       ldi    Main1, 32+2
       out    MCUCR, Main1        ;enable sleep idle mode (ext int on falling edge)
       ldi    Main1, 2
       out    TIMSK, Main1        ;enable timer interrupt

       ldi    Main1, 0        ;64
       out    GIMSK, Main1        ;don't enable external interrupt

       ldi    Main1, 256-3        ;timer intr. freq. = 3.6864MHz / 256 / 3 = 4800Hz
       out    TCNT0, Main1

       ldi    MiscFlags,THRSTATUSMASK

       sei                ;enable interrupts


     ;Init RC-5 decoding
       set
       clr    IrIdx

       sbis    PortD,2
       nop

       in    SaveThr1Status,SREG        ;initialize status (interrupts enabled in all thread)
       mov     SaveThr2Status,SaveThr1Status
       mov     SaveThr3Status,SaveThr1Status
       
;=====================================
;THREAD 1
; main loop
THR1LOOP:
       rcall    WAIT1        ;Wait for RC5 thread signal
       cbi    PortD,Led1    ;turn led ON

     ;transmit the data from MSB first
       mov    SerData,IrDataH
       sec
       rcall    BIN2ASCII
       sbr    MiscFlags, EXP2(Thread3Signal)+EXP2(SerRequest)  ;send the request to serial thread
       rcall    WAIT1

       mov    SerData,IrDataH
       clc
       rcall    BIN2ASCII
       sbr    MiscFlags, EXP2(Thread3Signal)+EXP2(SerRequest)  ;send the request to serial thread
       rcall    WAIT1

       mov    SerData,IrDataL
       sec
       rcall    BIN2ASCII
       sbr    MiscFlags, EXP2(Thread3Signal)+EXP2(SerRequest)  ;send the request to serial thread
       rcall    WAIT1

       mov    SerData,IrDataL
       clc
       rcall    BIN2ASCII
       sbr    MiscFlags, EXP2(Thread3Signal)+EXP2(SerRequest)  ;send the request to serial thread
       rcall    WAIT1

       cbr    MiscFlags, EXP2(IrLock)        ;clear IrLock flag: we can receive another RC5 code
       sbi    PortD,Led1            ;turn led OFF
       rjmp    THR1LOOP

;-----------------
WAIT1:
     ;save status before switch to another thread
       in    SaveThr1Status,SREG
       sbrs    MiscFlags, Thread1Signal        ;check for reeceived signal
       cbr    MiscFlags, EXP2(Thread1Status)      ;thread1 in wait state
       cbr    MiscFlags, EXP2(Thread1Signal)      ;reset signal
       rjmp    SCHEDULER2
THR1WAKEUP:
       ret


;-----------------
;converts a binary byte to ASCII char
;
;if Carry == 1, most significant nibble
;altrimenti less significant nibble
;-----------------
BIN2ASCII:
       brcc    DIGIT0
       swap    SerData
DIGIT0:
       andi    SerData,15
       cpi    SerData,10
       brlo    DIGDEC
       addi    SerData,(65-10)    ;'A'
       ret
DIGDEC:
       addi    SerData,48    ;'0'
       ret


;=================================
;THREAD 2    IrReceiver thread (decode RC-5 codes)
; main loop
THR2LOOP:
     ;thread 2 WAIT
     ;we can't do a separate Wait subroutine for every thread. Note that
     ;the stack is hardware, and not accessible.
     ;save status before switch to another thread
       in    SaveThr2Status,SREG
       sbrs    MiscFlags, Thread2Signal        ;check for received signal, if we have already
                             ;received the signal don't go sleep
       cbr    MiscFlags, EXP2(Thread2Status)      ;thread2 in wait state
       cbr    MiscFlags, EXP2(Thread2Signal)
       rjmp    SCHEDULER3
THR2WAKEUP:
       sbrc    MiscFlags, IrLock
       rjmp    THR2LOOP

     ;compare last sampled status with current one
       cpeqPortBit    PinD, IrRx, RC5_NOTEQ

       tst    IrIdx        ;IrIdx zero mean "wait start"
       breq    THR2LOOP

       inc    IrCount
       cpi    IrCount,(RC5_BITTIME*3)/2
       brlo    RC5_NOTIMEOUT
     ;test if timeout was caused by stop bit (last bit was 1)
       cpi    IrIdx,RC5_WORDLEN
       breq    RC5_LONG
RC5_TIMEOUT:
       clr    IrIdx        ;discard data and go to "wait start" state
       set
RC5_NOTIMEOUT:
       rjmp    THR2LOOP

RC5_NOTEQ:
       tst    IrIdx
       brne    RC5_NOWAIT
       sbr    IrBits, EXP2(lastBit)
       ldi    IrCount,RC5_BITTIME
       clr    IrDataL
RC5_NOWAIT:
       cpi    IrCount,(RC5_BITTIME*2)/3        ;short or long status?
       brsh    RC5_LONG

       cpeqRegBit     IrBits,lastBit,RC5_L1
       lsl    IrDataL
       rol    IrDataH
       sbrc    IrBits, lastBit
       sbr    IrDataL, 1
       inc    IrIdx
RC5_L1:
       rjmp    RC5_L2
RC5_LONG:                        ;long status
       lsl    IrDataL
       rol    IrDataH
       sbrc    IrBits, lastBit
       sbr    IrDataL, 1
       inc    IrIdx

       ldi    Temp1,EXP2(lastBit)        ;one's complement of lastBit
       eor    IrBits,Temp1
RC5_L2:
       brts    RC5_L3                ;one's complement of T
       set
       skipnext
RC5_L3:
       clt

       clr    IrCount
       cpi    IrIdx,RC5_WORDLEN+1
       brlo    THR2LOOP
RC5_RECVOK:
       clr    IrIdx
     ;transmit IrData...
       sbr    MiscFlags, EXP2(IrLock)+EXP2(Thread1Signal)

       rjmp    THR2LOOP


;===================================
;SERIAL DRIVER THREAD
; main loop
THR3LOOP:
     ;thread 3 WAIT
     ;save status before switch to another thread
       in    SaveThr3Status,SREG
       sbrs    MiscFlags, Thread3Signal        ;check for received signal
       cbr    MiscFlags, EXP2(Thread3Status)      ;thread3 in wait state
       cbr    MiscFlags, EXP2(Thread3Signal)
       rjmp    SCHEDULER1
THR3WAKEUP:
     ;test SerRequest to know who have sent the signal (Thr1 or Timer)
       sbrc    MiscFlags, SerRequest
       rjmp    THR3_L1

     ;waked up from Timer, are some data bits to transmit?
       tst    SerNBit                    ;if zero do nothing
       breq    THR3LOOP

       dec    SerNBit
       breq    THR3_TXEND                ;if zero tx stop and finish

       cpi    SerNBit,9                ;tx start
       breq    THR3_TX0
THR3_TXDATA:
       lsr    SerData                    ;tx next bit (stored in the carry)
       brcc    THR3_TX0
THR3_TX1:
       cbi    PortB,SerTx
       rjmp    THR3LOOP
THR3_TX0:
       sbi    PortB,SerTx
       rjmp    THR3LOOP
THR3_L1:
     ;new request from Thr1
       cbr    MiscFlags, EXP2(SerRequest)
       ldi    SerNBit,10                ;tx 10 bits: 1start + 8dati + 1stop
       rjmp    THR3LOOP
THR3_TXEND:
       sbr    MiscFlags, EXP2(Thread1Signal)      ;wakeup thread 1
       rjmp    THR3_TX1


;---------------
;Interrupts routine just send a signal to a thread. If you need a VERY short interrupt
;response you can put some instructions here, then send the signal to the thread
;---------------
TIMERDRV:
       in    SaveStatus,SREG

       ldi    Main1, 256-3
       out    TCNT0, Main1
       sbr    MiscFlags, EXP2(Thread3Signal)+EXP2(Thread2Signal);wakeup thread 3 and 2

       out    SREG,SaveStatus
       reti


;---------------
INT0DRV:
       in    SaveStatus,SREG
       sbr    MiscFlags, EXP2(Thread2Signal)      ;wakeup thread 2
       out    SREG,SaveStatus
       reti


;-----------------
;This is the scheduler, the core of the nanoKernel. You can decide to go sleep
; when there's nothing to do. You can also put there the watchdog reset instruction.
;-----------------
SCHEDULER:
       sei                        ;be sure interrupts on here

     ;if you don't want to go sleep remove next 3 instr
       tst    MiscFlags
       brne    SCHEDULER1
       sleep
SCHEDULER1:
       sbrc    MiscFlags, Thread1Status        ;test if thread1 is ready
       rjmp    SCHEDRDY1
     ;check for wakeup
       sbrs    MiscFlags, Thread1Signal        ;test if thread1 received a signal
       rjmp    SCHEDULER2
       cbr    MiscFlags, EXP2(Thread1Signal)      ;clear signal
SCHEDRDY1:
     ;switch to thread 1
       sbr    MiscFlags, EXP2(Thread1Status)
       out    SREG,SaveThr1Status
       rjmp    THR1WAKEUP

SCHEDULER2:
       sbrc    MiscFlags, Thread2Status        ;test if thread2 is ready
       rjmp    SCHEDRDY2
     ;check for wakeup
       sbrs    MiscFlags, Thread2Signal        ;test if thread1 received a signal
       rjmp    SCHEDULER3
       cbr    MiscFlags, EXP2(Thread2Signal)      ;clear signal
SCHEDRDY2:
     ;switch to thread 2
       sbr    MiscFlags, EXP2(Thread2Status)
       out    SREG,SaveThr2Status
       rjmp    THR2WAKEUP
SCHEDULER3:
       sbrc    MiscFlags, Thread3Status        ;test if thread3 is ready
       rjmp    SCHEDRDY3
     ;check for wakeup
       sbrs    MiscFlags, Thread3Signal        ;test if thread1 received a signal
       rjmp    SCHEDULER
       cbr    MiscFlags, EXP2(Thread3Signal)      ;clear signal
SCHEDRDY3:
     ;switch to thread 3
       sbr    MiscFlags, EXP2(Thread3Status)
       out    SREG,SaveThr3Status
       rjmp    THR3WAKEUP





Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.