;************************************************************************ ; TaPRK IR-LINK-TRANSMITTER POT VERSION Inverted v.4i (2004-11-07) ;************************************************************************ LIST P = 16F84, R = HEX ;Prosessor type, radix INCLUDE "p16f84.inc" ;w,f,porta,portb etc. ERRORLEVEL -224 ;Prevent error message ;from "tris"-command __CONFIG _PWRTE_ON & _HS_OSC & _WDT_OFF;4MHz crystal ;************************************************************************ ; SCHEMATIC DIAGRAM ;************************************************************************ ; +---+_+---+ ; GROUP1 PORTA.2 <-|1 18|-> PORTA.0 IR-LED act.high ; GROUP2 PORTA.3 <-|2 17|<- PORTA.0 DATA_OUT ; POT&CAP PORTA.4 <>|3 16|-- XTAL ; Vdd --|4 15|-- XTAL ; Vss(GND)--|5 14|-- Vdd (+5V) ; AddL/AddButton PORTB.0 ->|6 13|<- PORTB.7 AddH/IDH ; AddL/14/28Sw PORTB.1 ->|7 12|<- PORTB.6 AddH/IDL ; AddL/DirSw PORTB.2 ->|8 11|<- PORTB.5 AddH/spare ; AddL/F0Sw PORTB.3 ->|9 10|<- PORTB.4 AddH/F1Sw ; +---------+ ;************************************************************************ ; 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 ; ; [2004-11-07] Added Emg.stop if speed reduced to ; zero very quickly. ; ; [2004-10-17] Converted the pot to have max.speed at ; low resistance -- i.e. _inverted_ the potentiometer! ; ; [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 * ID) interval: ; ID 0...3 -> 40...70 ms interval. ; ; Reading the pot ; =============== ; Reading pot values in much the same way as PC-compatible's ; joystick port! ; ; 1) Make PORTA.4 output and discharge Cap through port. ; 2) Make PORTA.4 input (schmitt!) and wait for going high ; 3) Send number of loop counts it took to go high ; ; More about PC-joystick port, see: ; http://www.epanorama.net/documents/joystick/pc_joystick.html ; ; Datasheet states max. capacitance to be connected ; to any 16F84-port as 50 pF. Used 120pF and 1M +100K ; ; Time constant: T(63%)=RC close to schmitt trip level ; ; Connecting Cap from PORTA.4 to gnd, and Pot through ; 22k resistor from Vhi to PORTA.4 ; ; Looping through counter takes approx 4 inst.cycles (4us) ; so min. time (count 1) should be about 4us, ; and max. time (count 255) about 1ms. ; ;************************************************************************ ; VARIABLES ;************************************************************************ CBLOCK H'0C' Data1 ;Data from IR-receiver Parity ;Parity bitcount ;bit counter potvalue ;pot position potbase ;pot start position (for auto null) bcd ;BCD_to_bin temp ;BCD_to_bin e_stop ;e_stop status speed ex_speed s_count addr ex_addr a_count func ex_func f_count payload ENDC ;************************************************************************ ; STATICS ;************************************************************************ #DEFINE DATA_OUT PORTA, 0 ;Data out (if IR fails) #DEFINE IRLED PORTA, 1 ;IR LED #DEFINE GROUP1 PORTA, 2 ;GROUP1, Address switch #DEFINE GROUP2 PORTA, 3 ;GROUP2, other switches #DEFINE POT PORTA, 4 ;potentiometer #DEFINE BUTTON PORTB, 0 ;Read address switches #DEFINE S_MODE PORTB, 1 ;Address mode: 14/28 steps #DEFINE DirSw PORTB, 2 ;Direction switch #DEFINE F0Sw PORTB, 3 ;F0/FL switch #DEFINE F1Sw PORTB, 4 ;F1 switch #DEFINE ID_HI PORTB, 7 ;IR ID High bit #DEFINE ID_LO PORTB, 6 ;IR ID Low bit #DEFINE HALFBIT .203 ;= 255-52 #DEFINE QUADBIT .229 ;= 255-26 constant MAXSPEED =.31 ;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'00010000' ;RA4 =in CAP&POT tris porta movlw b'11111111' ; tris portb CLRWDT ; bsf STATUS, RP0 ; movlw b'00000010' ;Pullup=on, Prescaler= 1:8 movwf OPTION_REG ; bcf STATUS, RP0 ; ramc movlw 0x0c ; start of 16F84 ram movwf fsr ramc1 clrf indf ; clear indirect location incf fsr, f ; next location btfss fsr, 7 ; cleans up to 0x7f goto ramc1 ; [From J.Niinikoski] ramdone movlw b'00000000' ;initiate address byte movwf addr movlw b'11000000' ;initiate speed byte movwf speed movlw b'10000000' ;initiate func byte movwf func call read_pot ;Read the pot for the first time movfw potvalue ;and use the reading to set the movwf potbase ;zero point for the pot return ;------------------------------------------------------------------------ ; BCD to BIN [ from www.piclist.com (Scott Dattalo) ] ;------------------------------------------------------------------------ BCD_to_BIN ; movwf bcd ; rrf bcd, W andlw b'01111000' ;W = tens*8 movwf temp clrc rrf temp, F ;temp = tens*4 rrf temp, F ;temp = tens*2 subwf bcd, W ;W = tens*16 + ones - tens*8 ;W = tens*8 + ones addwf temp, W ;W = tens*10 + ones return ;------------------------------------------------------------------------ ; Send BYTE Data to be sent in DATA1, number of bits to sed at BITCOUNT ;------------------------------------------------------------------------ send_byte sb3 rlf DATA1, F ;rotate through carry (MSB first) and btfsc STATUS, C ;in case of carry send 1 instead of 0 goto sb4 ;carry=1 call Send0 ;carry=0 goto sb5 sb4 call Send1 sb5 decfsz BITCOUNT, F ;all bits sent? goto sb3 ;not yet, send more return ;------------------------------------------------------------------------ ; Send 0 [###___] leaves TMR to falf bit interval ;------------------------------------------------------------------------ send0 bsf DATA_OUT ;direct data out 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 bcf INTCON, T0IF ;clear overflow flag movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer s02 bcf DATA_OUT ;direct link 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 [___###] ;------------------------------------------------------------------------ send1 bcf DATA_OUT ;direct link off 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 bsf DATA_OUT ;direct link on 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 bcf INTCON, T0IF ;clear overflow flag movlw HALFBIT ;new timer base addwf TMR0, f ;add to just overflowed timer bcf DATA_OUT ;direct link off return ;------------------------------------------------------------------------ ;Read pot ;------------------------------------------------------------------------ read_pot bsf STATUS, RP0 ;Select Bank1 and bcf TRISA, 4 ;choose RA4 -> output bcf STATUS, RP0 ;Select Bank0 bcf POT ;discharge cap by grounding nop ;RA4 and keep it down nop ;for a while in case of nop ;current limiting resistor clrf potvalue ;clear previous pot position bsf STATUS, RP0 ;Select Bank1 and choose bsf TRISA, 4 ;RA4 -> input for reading cap bcf STATUS, RP0 ;Select Bank0 movlw .5 ;This is a pulling procedure: movwf potvalue ;The cap (due to high R and low C null_pot ;start to charge slowly decfsz potvalue, f ;we'll kill time here goto null_pot ;to make sure we get zero as well test_pot btfsc POT ;test if cap charged return ;yes, done! incf potvalue, f ;no, increment counter goto test_pot ;and test again return ;Faster pot test routine from Scott Dattalo ;[http://home.clear.net.nz/pages/joecolquitt/0pots.html]: ; ; decf potvalue, f ;goto FF to get zero ;) ;test_pot ; incf potvalue,f ; btfss POT ;bump measure while pot pin is low ; goto test_pot ;else jump out and store count ; return ;------------------------------------------------------------------------ ; 1ms delay ;------------------------------------------------------------------------ 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 ;------------------------------------------------------------------------ main call init ;intialize ports m0 ; movfw addr ;save ex-value for comparison ; movwf ex_addr ;with the value we'll get soon movlw b'00000000' ;modify new to resemble barebone movwf addr ;IR address payload: 0AAAAAAA ; movfw speed ;save ex-value for comparison ; movwf ex_speed ;with the value we'll get soon movlw b'11000000' ;modify new to resemble barebone movwf speed ;IR speed payload: 11DsSSSS ; movfw func ;and function packet! ; movwf ex_func ;with the value we'll get soon movlw b'10000000' ;modify new to resemble barebone movwf func ;IR func payload: 10MFFFFF ;---------------------------------------------------------------- ;First get the speed settings ;---------------------------------------------------------------- call read_pot ;read the pot value ;to potvalue movfw potvalue ;get potvalue to w subwf potbase, w ;base-pot (="invert" pot values) btfss status, c ;Did it go below zero c=0(too slow)? movlw .0 ;yes, set w to zero xorlw .0 ;get flags from w ;-- skip other zero and Emg.stop -- skpz ;well, was it zero then? addlw .3 ;no, add 3 to skip other zero ;and emergency stop positions ;----------------------------------- movwf potvalue ;move result to potvalue sublw MAXSPEED ;max-pot (now testing for speeding!) btfsc status, c ;Did it go below zero c=0(too fast)? goto read_addr ;no, continue movlw MAXSPEED ;yes, replace w. MAXSPEED and movwf potvalue ;move it to potvalue read_addr ;---------------------------------------------------------------- ;Then check the addr_change button ;---------------------------------------------------------------- bcf GROUP1 ;now reading (grounding) other switches bsf GROUP2 ;not reading (releasing) address sw's nop nop btfsc BUTTON ; goto read_sw ;not pressed (clear) goto switches tstf potvalue ;Check the speed setting skpz ;it must be zero before changing address goto read_sw ;not zero, goto switches bsf GROUP1 ;not reading (releasing) other switches bcf GROUP2 ;now reading (grounding) address sw's comf portb, w ;read address switches bcf GROUP1 ;now reading (grounding) other switches bsf GROUP2 ;not reading (releasing) address sw's nop nop btfsc BUTTON ;now read the switches, is the button still down goto read_sw ;not pressed (clear) goto switches call bcd_to_bin ;convert to binary address andlw b'01111111' ;just to be sure... movwf addr ;and save as new address movwf payload ; movlw .10 ;repeat for address ; movwf a_count ;set as a dummy really... goto send_packet ;and send it! read_sw ;---------------------------------------------------------------- ;Go to read other switches ;---------------------------------------------------------------- bcf GROUP1 ;now reading (grounding) other switches bsf GROUP2 ;not reading (releasing) address sw's nop nop ;---------------------------------------------------------------- ;First check the dir switch and set/clear speed's dir bit ;---------------------------------------------------------------- btfss DirSw ;Dir reverse (set) / forward (clear) bsf speed, 5 ;---------------------------------------------------------------- ;Next check the F1 and set/clear func's F1 bit ;---------------------------------------------------------------- btfss F1Sw ;F1 off (set) / on (clear) bsf func, 0 ;---------------------------------------------------------------- ;Next check the mode switch and clear/set flag bit in func byte, ;set correct bit according to FL/F0func byte and handle speed ;and functions: ;---------------------------------------------------------------- btfss S_MODE ;Speed in 14 (set) / 28 (clear) goto m_28 m_14 ;---------------------------------------------------------------- ; 14 step mode: ; ------------- ; Speed byte: 11DsSSSS D = Dir 1=Fwd, 0=Rev ; s = F0/FL, ; S..S = Speed bits 3..0 ; Func byte: 10M0FFFF M = 0 (14 step mode) ; F..F = F4..1 ;---------------------------------------------------------------- bcf func, 5 ;speed_mode flag cleared (14 steps) bcf speed, 4 ;clear the little "s" btfss F0Sw ;FL off (set) / on (clear) bsf speed, 4 rrf potvalue,w ;get pot value, LSB to carry (discard!) andlw 0x0F ;clear top (just in case!) iorwf speed, f ; goto m1 ;and done here! m_28 ;---------------------------------------------------------------- ; 28 step mode: ; ------------- ; Speed byte: 11DsSSSS D = Dir 1=Fwd, 0=Rev ; s = speed bit 0 ; S..S = Speed bits 4..1 ; Func byte: 10MFFFFF M = 1 (28 step mode) ; F..F = F0,F4..F1 ;---------------------------------------------------------------- bsf func, 5 ;speed_mode flag set (28 steps) btfss F0Sw ;FL off (set) / on (clear) bsf func, 4 bcf speed, 4 rrf potvalue,w ;get pot value, LSB in carry skpnc ;was carry set bsf speed, 4 ;yes, set little "s" andlw 0x1F ;clear top (just in case!) iorwf speed, f ; m1 send_speed ;---------------------------------------------------------------- ;Now, lets see if speed needs sending ;---------------------------------------------------------------- tstf e_stop ;Do we have an emergency? skpz ; goto es_on ;yes, lets act upon it! movfw ex_speed ;Load old speed to w xorwf speed, w ;and compare with latest one. btfsc status, z ;Was it equal? goto send_sp1 ;yes, check for repeats! movfw speed ;Test new speed: andlw b'00001111' ;clear all but 4 speed bits skpz ;Is it zero goto send_sp0 ;no, go on movfw ex_speed ;yes, how about previous? andlw b'00001100' ;was it over 3/14 (6/28) skpnz goto send_sp0 ;no, go on movfw speed ;make sure the staus will not movwf ex_speed ;stay on for ever movlw .10 movwf e_stop ;set the e_state (10 repeats) es_on decf e_stop, f ;reduce the e_stop counter movfw speed andlw b'11110000' ;get the FL and direction movwf payload ;get this to payload bsf payload, 0 ;set bit 0 (emg stop!) movlw b'11000001' ;this is emergency packet goto send_packet send_sp0 movlw .10 ;no, lets make sure movwf s_count ;this is noticed movfw speed ;get speed to w movwf payload ;and prepare to send it movwf ex_speed ;and make ex_speed equal to present goto send_packet send_sp1 tstf s_count ;test if repeats needs to be sent? skpnz goto send_func ;no, check func next! decf s_count,f ;yes, decrement repeat counter movfw speed ;get speed and movwf payload ;and prepare to send goto send_packet ;go and send it! send_func ;---------------------------------------------------------------- ;Now, lets see if func needs sending ;---------------------------------------------------------------- movfw ex_func ;Load old func to w xorwf func, w ;and compare with latest one. btfsc status, z ;Was it equal? goto send_f1 ;yes, how about repeats? movlw .10 ;no, lets make sure movwf f_count ;this is noticed movfw func ;get func to w movwf payload ;and prepare to send it movwf ex_func ;and make ex_func equal to present goto send_packet send_f1 tstf f_count ;test if repeats needs to be sent? skpnz goto m0 ;no, back to square one! decf f_count, f ;yes, decrement repeat counter movfw func ;get func packet and movwf payload ;and prepare to send goto send_packet ;go and send it! send_packet ;---------------------------------------------------------- ;send startbit ;---------------------------------------------------------- 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!) ;---------------------------------------------------------- ;send cab number ;---------------------------------------------------------- bcf GROUP1 ;now reading (grounding) other switches bsf GROUP2 ;not reading (releasing) address sw's clrf Parity ;clear Parity, 1's will increment clrf DATA1 ;clear DATA1 btfss ID_HI ;Check for address hi bit bsf DATA1, 7 ;set resp. bit in data1 if grounded! btfss ID_LO ;Check for address lo bit bsf DATA1, 6 ;set resp. bit in data1 if grounded! movlw .2 ;only 2 bits movwf BITCOUNT ; call send_byte ;send it along! ;---------------------------------------------------------- ;send payload ;---------------------------------------------------------- movfw payload ;get the payload movwf DATA1 ; movlw .8 ;all 8 bits movwf BITCOUNT ; call send_byte ;send DATA1 ;---------------------------------------------------------- ;send parity ;---------------------------------------------------------- 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 m7 movlw .40 ;count 40 ms movwf BITCOUNT ;bitcount counts ms now btfss ID_HI ;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 ; ;---------------------------------------------------------- ;set packet interval ;---------------------------------------------------------- m7_ btfss ID_LO ;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