Claudio Lanconelli's WEB pages - Updated 26 Aug 98
;***************************************************************************
;* 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