;************************************************************************ ; TaPRK IR-LINK-TRANSMITTER BUTTON VERSION ;************************************************************************ LIST P = 16F84A, R = HEX ;Prosessor type, radix INCLUDE "p16f84A.inc" ;w,f,porta,portb etc. ERRORLEVEL -224 ;Prevent error message ;from "tris"-command __CONFIG _PWRTE_OFF & _HS_OSC & _WDT_OFF;4MHz crystal ;************************************************************************ ; SCHEMATIC DIAGRAM ;************************************************************************ ; +---+_+---+ ; N/C PORTA.2 ->|1 18|<- PORTA.1 N/C ; N/C PORTA.3 ->|2 17|-> PORTA.0 IR-LED act.hi ; N/C PORTA.4 ->|3 PIC 16|-- XTAL (4MHz) ; (+5V) MCLR --|4 16F 15|-- XTAL ; (GND) Vss --|5 84A 14|-- Vdd (+5V) ; ACCEL. SW PORTB.0 ->|6 13|<- PORTB.7 ADD_hi ; BRAKE SW PORTB.1 ->|7 12|<- PORTB.6 ADD_lo ; DIR. BUTTON (R) PORTB.2 ->|8 11|<- PORTB.5 CH-SWITCH ; F0/5 BUTTON (Y) PORTB.3 ->|9 10|<- PORTB.4 F1 BUTTON (G) ; +---------+ ;************************************************************************ ; MAIN IDEA ;************************************************************************ ; This is based on Holger Klabunde's design, but is completely ; rewritten in assembler. ; ; Sender timing, carrier, packet format ; ===================================== ; 26us cycle of IR-LED on/off gives 38,5kHz ; 16 cycles for 1/2 bit -> 16 x 26us= 416 us ; Prescaler of 1:8 makes TMR0 advance every 8us (@4MHz crystal) ; 1/2 bit needs 416/8 = 52 ticks of TMR0 ; 1/4 bit needs 52/2 = 26 ticks of TMR0 ; ; [2002-03-07] Error in number of "nop"s, in off-section: ; had 1 too many. Also noted that (acc. to datasheet) ; adding to TMR0 will clear prescaler contents, so always ; a bit late in timing! Hint: Use "MPLAB/Window/Stopwatch"! ; ; This is loosely following Philips RC5, exept: ; Bit length now 832us instead of 1798us. ; Only one start bit, and it is "0", only 8 data bits and ; Added parity bit -> Total of 10 bits. ; ; This is presumably similar to Lorell Joiner's ; setup, done with hardwired UART some 10 years ago, (MR Jan 1990) ; but instead of using 250 kHz carrier and on/off bits, I use the ; Bit formation similar to RC5 at 36...38 kHz carrier: ; ; +++++ +++++ ; ||||| =0 ||||| =1 ; +++++---+ +---+++++ ; ; Interval of packets needs to be somewhat random. ; 1 bit takes approx. 1 ms, one packet about 10 ms ; 1 sec => max. 100 packets ; Sending now at 40ms + (10 * address) interval: ; Address 0...3 -> 40...70 ms interval. ; ; [5/5-02] Added more comments and F1+FL ;************************************************************************ ; VARIABLES ;************************************************************************ CBLOCK H'0C' Data1 ;Data from IR-receiver Parity ;Parity bitcount ;bit counter nulcount ;repeat packet counter potvalue ;pot position oldpot ;previous pot value count_0 ;typematic counter ENDC ;************************************************************************ ; STATICS ;************************************************************************ #DEFINE IRLED PORTA, 0 ;IR LED #DEFINE ACCEL_SW PORTB, 0 ;!Faster #DEFINE BRAKE_SW PORTB, 1 ;!Slower #DEFINE DIR_BUTTON PORTB, 2 ;!Direction/Emg.Stop #DEFINE FL_BUTTON PORTB, 3 ;!FL/F5 #DEFINE F1_BUTTON PORTB, 4 ;!F1 #DEFINE CH_SWITCH PORTB, 5 ;!CH_SWITCH #DEFINE ADDRLO PORTB, 6 ;!Address, low bit #DEFINE ADDRHI PORTB, 7 ;!Address, high bit #DEFINE HALFBIT .203 ;= 255-52 #DEFINE QUADBIT .229 ;= 255-26 constant MAXSPEED =.15 ;max reading for pot ;************************************************************************ ;RESTART/INT-START ;************************************************************************ ORG 0 ; Restart vector goto main ORG 4 ; INT vector retfie ;------------------------------------------------------------------------ ; Initialize I/O ports, Interrupts, Timers etc ;------------------------------------------------------------------------ init movlw b'00000010' ;RA0=out, RA1=in RA2:4=out tris porta movlw b'11111111' ;RB0..7=in tris portb CLRWDT ; bsf STATUS, RP0 ; movlw b'00000010' ;Pullup=on, Prescaler= 1:8 movwf OPTION_REG ; bcf STATUS, RP0 ; clrf potvalue ;speed to zero movlw .10 ; movwf nulcount ;start by sending 10 packets ;------------------------------------------------------------------------ ; Increment throttle's typematic counters ; ; If faster/slower buttons are pressed long enough ; the speed will be incremented again. Count_0 ; is used for differentiating single click and continuous ; press (needing typematic updating) ;------------------------------------------------------------------------ IncCounters tstf count_0 ;test zero flag from counter skpz ;skip if zero incf count_0, f ;not yet -> increment return ;------------------------------------------------------------------------ ; Send 0 [###___] ; This will send zero bit. The zero bit consists of ; half bit length of 38,5 kHz pulses, i.e. 13 instr. ; cycles on, 13 instr. cycles off. ; The other half of byte length is "darkness" ;------------------------------------------------------------------------ send0 s01 bsf IRLED ;IR-LED "on" for 13 instr. cycles nop nop nop nop nop nop nop nop nop nop nop nop bcf IRLED ;IR-LED "off" for 13 instr. cycles nop nop nop nop nop nop nop nop nop btfss INTCON, T0IF ;1/2 bit time gone? goto s01 ;not yet, send another burst bcf INTCON, T0IF ;clear overflow flag movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer s02 btfss INTCON, T0IF ;1/2 bit time gone? goto s02 ;not yet bcf INTCON, T0IF ;clear overflow flag movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer return ;------------------------------------------------------------------------ ; Send 1 [___###] ; This will send "one" bit. The zero bit consists of ; half bit length of darkness and second half byte length of ; 38,5 kHz pulses, i.e. 13 instr. cycles on, 13 instr. cycles off. ;------------------------------------------------------------------------ send1 s11 btfss INTCON, T0IF ;1/2 bit time gone? goto s11 ;not yet incf PARITY, F ;parity++ bcf INTCON, T0IF ;clear overflow flag movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer s12 bsf IRLED ;IR-LED "on" for 13 instr. cycles nop nop nop nop nop nop nop nop nop nop nop nop bcf IRLED ;IR-LED "off" for 13 instr. cycles nop nop nop nop nop nop nop nop nop btfss INTCON, T0IF ;1/2 bit time gone? goto s12 ;not yet, send another burst bcf INTCON, T0IF ;clear overflow flag movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer return ;------------------------------------------------------------------------ ;Read switch ; This will read the speed, direction and function button presses. ; First the direction button is tested -- if speed is non-zero ; it will act as emg. brake -> potvalue to zero ; Next the acceleration and brake-buttons are tested ; and in case typematic counter has advanced to non-zero value ; nothing is done (IncCounter subroutine will increment count_0 ; until it overflows to zero). If count_0 is zero, potvalue ; is adjusted within limits and count_0 is given a non-zero seed. ; If no button is pressed, then count_0 is cleared to zero. ;------------------------------------------------------------------------ read_sw tstf potvalue ;check if speed skpnz ;is zero? goto rsw1 ;yes, no need for emg.stop btfss DIR_BUTTON ;no, check if button is pressed clrf potvalue ;yes, speed -> 0 rsw1 btfsc ACCEL_SW ;faster-bit cleared? goto rsw1s ;no let's go to test slower-button rsw1f tstf count_0 ;check if typematic still on skpz ;skip if typematic clear return ;no, pass movlw .240 ;Typematic seed is now 240 movwf count_0 ;start typematic counter incf potvalue, f ;accelerate speed btfss potvalue, 4 ;going too fast? return ;no, done with this movlw 0x0f ;top speed is 15 movwf potvalue ;and we settle for that! return ;done! rsw1s btfsc BRAKE_SW ;slower-bit cleared? goto rsw1nop ;no, no action here, goto cab_1 nop tstf count_0 ;check if typematic still on skpz ;skip if typematic clear return ;no, pass movlw .240 ;Typematic seed is now 200 movwf count_0 ;start typematic counter tstf potvalue ;test speed etting skpz ;already standstill? decf potvalue, f ;no, let's put on brakes return ;done! rsw1nop clrf count_0 ;clear typematic counter return ;and done! ;------------------------------------------------------------------------ ; 1ms delay ; This is used to make the packet intervals ;------------------------------------------------------------------------ delay1ms bcf INTCON, T0IF ;clear overflow flag movlw .130 ;new timer base addwf TMR0, f ;add to timer d11 btfss INTCON, T0IF ;1ms gone? goto d11 ;not yet return ;------------------------------------------------------------------------ ; MAIN PROGRAMME ; This will first increment count_0, read speed setting ; buttons (to adjust potvalue) and then -- in case speed value ; has been changed (compared to oldpot) or other buttons are ; pressed or in case something has changed recently -- send ; appropriate packet. Main programme keeps the nulcount counter. ;------------------------------------------------------------------------ main call init ;intialize ports m0 call IncCounters ; call read_sw ;read the accel/brake buttons (switch) ;to potvalue m1 comf PORTB, w ;copy and invert inputs to w andlw b'00111111' ;mask address bytes away to see skpz ;if any buttons pressed? goto m2 ;yes, out with it! tstf nulcount ;none! Test if all null packets sent? skpnz ;well? goto m0 ;yes, back to square one decf nulcount, f ;no, decrement nul packet count goto m2nul ;and send one! m2 movlw .3 ;prepare to send X null packets movwf nulcount ;in case no buttons pressed m2nul ;---------------------------------------------------------- ; Prepare to send packet by setting the data into DATA1: ; ; "AAWDSSSS" ; "AA" =Address ; "W" =0 speed+dir ; =1 F buttons ; "D" =direction button ; (or future channel select) ; "SSSS"=Speed from potvalue ; or F0...FL ;---------------------------------------------------------- clrf DATA1 ;clear byte to be sent m2fl btfsc FL_BUTTON ;Check FL button is pressed? goto m2f1 ;no, goto test F1 instead movlw b'00100101' ;yes, W=1, D=0, SSSS=.5 movwf DATA1 ;and the lot to DATA1 goto m2addr ;and go to settle address bits m2f1 btfsc F1_BUTTON ;Check if F1 button is pressed? goto m2ch ;no, goto channel switch test movlw b'00100001' ;yes, W=1, D=0, SSSS=.1 movwf DATA1 ;and the lot to DATA1 goto m2addr ;and go to settle address bits m2ch btfsc CH_SWITCH ;Check if CHANNEL switch is on? goto m2spd ;no, goto vanilla speed+dir movlw b'00110000' ;yes, W=1, D=0, SSSS= from pot (see "vanilla") movwf DATA1 ;and the lot to DATA1 m2spd ;------------------------------------------------------------- ;Ordinary plain vanilla speed+dir byte ;------------------------------------------------------------- btfss DIR_BUTTON ;Check for direction button bsf DATA1, 4 ;set resp. bit in data1 if grounded! movfw potvalue ;copy pot value to w andlw 0x0F ;clear top bits just to be safe iorwf data1, f ;and set resp. bits in data1 m2addr ;------------------------------------------------------------- ;Set address bits ;------------------------------------------------------------- btfss ADDRHI ;Check for address hi bit bsf DATA1, 7 ;set resp. bit in data1 if grounded! btfss ADDRLO ;Check for address lo bit bsf DATA1, 6 ;set resp. bit in data1 if grounded! ;------------------------------------------------------------- call IncCounters ; ;------------------------------------------------------------- ; Sending of the packet ; ; The packet consists of: ; -1 bit preamble (always zero) ; -8 bit payload ; -1 bit parity ; Data to be sent is put into DATA1-register. The ; DATA1-register is shifted left through carry ; and in case carry becomes set "one" is sent, ; else a "zero" is sent. ; Bitcount is used for counting the number of bits to ; rotate and send. ; Parity is counted by incrementing Parity counter ; whenever "one" is sent and the LSB of parity register ; is the parity proper, and that is sent after payload. ;-------------------------------------------------------------- clrf Parity ;clear Parity, 1's will increment movlw .8 ;all 8 bits movwf BITCOUNT ; bcf INTCON, T0IF ;clear overflow flag movlw HALFBIT ;new timer base movwf TMR0 ;move initial timer base (first round!) call Send0 ;send startbit (won't affect parity!) m3 rlf DATA1, F ;rotate through carry (MSB first) and btfsc STATUS, C ;in case of carry send 1 instead of 0 goto m4 ;carry=1 call Send0 ;carry=0 goto m5 m4 call Send1 m5 decfsz BITCOUNT, F ;all bits sent? goto m3 ;not yet, send more btfsc Parity, 0 ;in case Parity 1 send 1 instead of 0 goto m6 ;Parity=1 call Send0 ;Parity=0 goto m7 m6 call Send1 ;-------------------------------------------------------------- ; Packet interval ; ; Packet interval is 40..70ms depending on address. ; In case addresses MSB is set, 20ms is added to ; packet interval, and in case LSB is set, 10ms is ; added ;-------------------------------------------------------------- m7 movlw .40 ;count 40 ms movwf BITCOUNT ;bitcount counts ms now btfss ADDRHI ;Check for ADDRESS HI bit goto m7_ ;not grounded, goto see lo bit movlw .20 ;was grounded, add 20 ms to loop addwf BITCOUNT, f ; m7_ btfss ADDRLO ;Check for ADDRESS LO bit goto m8 ;not grounded, out with it! movlw .10 ;was grounded, add 10 ms to loop addwf BITCOUNT, f ; m8 call delay1ms decfsz BITCOUNT, F ;packet interval gone yet? goto m8 ;not yet, wait another millisec! goto m0 ;done, back to square one end