;************************************************************************ ; TaPRK IR-LINK-TRANSMITTER ROTARY ENCODER VERSION 2007-02-24 ;************************************************************************ ; WITH ROTARY ENCODER FOR SPEED ;************************************************************************ LIST P = 16F628, R = HEX ;Prosessortype, radix INCLUDE "p16f628.inc" ;w,f,porta,portb etc. ERRORLEVEL -302 ;prevents some errors __CONFIG _HS_OSC & _LVP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_ON & _MCLRE_OFF ;************************************************************************ ; SCHEMATIC DIAGRAM ;************************************************************************ ; ; +---+_+---+ ; GROUP1 PORTA.2 <-|1 18|-> PORTA.1 IR-LED act.high ; GROUP2 PORTA.3 <-|2 17|-> PORTA.0 DATA_OUT ; ENC_A PORTA.4 ->|3 16|-- XTAL ; ENC_B PORTA.5 ->|4 PIC 15|-- XTAL ; Vss(GND)--|5 16F 14|-- Vdd (+5V) ; AddL/AddButton PORTB.0 ->|6 628 13|<- PORTB.7 AddH/F4Sw ; AddL/14/28Sw PORTB.1 ->|7 12|<- PORTB.6 AddH/F3Sw ; AddL/DirSw PORTB.2 ->|8 11|<- PORTB.5 AddH/F2Sw ; AddL/F0Sw PORTB.3 ->|9 10|<- PORTB.4 AddH/F1Sw ; +---------+ ; ; NOTE: Throttle ID now hard-coded! ; in #DEFINE section (PACK)! ; ;************************************************************************ ; 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 ; ; [2007-01-23] Converted to use rotary encoder instead of ; potentometer. Also converted to work with 3 bit cab id. ; NB: uses PIC16F628 now! Older version's pot and !MCLR ; pins are used as encoder input, so it is easy to convert ; old throttles from pot to encoder use! ; ; [2004-10-17] Converted the pot to have max.speed at ; low resistance -- i.e. _inverted_ the potentiometer! ; ; [2003-11-16] Hard-coded throttle ID to get more functions ; with least soldering task to get Antti's Roco ; Nohab w. sound to play more tunes (horn!) ; ; [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 (Mancester code), 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. ; ; Rotary encoder is sampled whenever the software is idle ; waiting for TMR0 to overflow, and once in the main ; program. The encoder reading is not very efficient, but ; apparently there is no need to as at worst the reading ; interval is about the length of one transmitter bit. ; When the processor is waiting for the overflow flag ; the encoder reading macro will firts check that there is ; still ample time (TMR0 is less than F0h). It means that ; there is still 16 x 8 instructions before overflow. ; Encoder uses EX_ENC to compare previous reading and by ; combining new and old one can determine if the knob was ; turned up or down or not at all. Encoder steps are recorded ; in ENCODER, and at change of value the MSB is tested and ; overflow and underflow corrected. This is then scaled to ; actual speed within main programme. ; ;************************************************************************ ; MACRO ;************************************************************************ can_read_enc MACRO ;------------------------------------------------------ ; Testing TMR0 value, if adding 0Fh will cause overflow ; this macro is skipped, else encoder is read ;------------------------------------------------------ movlw b'00001111' ;Test if TMR0 is F0 or addwf TMR0, w ;higer by adding 0F to TMR0 skpnc ;and checking if we get overflow? goto $+3 ;yes, no encoder checking anymore call read_encoder ;no, lets check the encoder goto $-5 ; ENDM ;************************************************************************ ; VARIABLES ;************************************************************************ CBLOCK H'20' Data1 ;Data from IR-receiver Parity ;Parity bitcount ;bit counter potvalue ;pot position potbase ;pot start position (for auto null) ex_enc ;old encoder switces encoder ;encoder based counter value bcd ;BCD_to_bin temp ;BCD_to_bin speed ex_speed s_count addr ex_addr a_count func ex_func f_count payload PACK_ID TOGG_3 TOGG_4 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 ENC_A PORTA, 4 ;IR ID High bit #DEFINE ENC_B PORTA, 5 ;IR ID Low bit #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 F2Sw PORTB, 5 ;F2 switch #DEFINE F3Sw PORTB, 6 ;F3 switch #DEFINE F4Sw PORTB, 7 ;F4 switch #DEFINE HALFBIT .203 ;= 255-52 #DEFINE QUADBIT .229 ;= 255-26 constant MAXSPEED =.31 ;max reading for pot #DEFINE CAB_ID .1 ;scale 0..7 (not 1..8!) #DEFINE INTERVAL .50 ;packet interval in ms ;Pairs used: Cab interval ; --- -------- ; 0 40 Done ; 1 50 Done ; 2 60 Done ; 3 70 Done ; --- ------- ; 4 45 ; 5 55 ; 6 65 ; 7 75 ; --- ------- ;************************************************************************ ;RESTART/INT-START ;************************************************************************ ORG 0 ; Restart vector goto main ORG 4 ; INT vector retfie ;------------------------------------------------------------------------ ; Initialize I/O ports, Interrupts, Timers etc ;------------------------------------------------------------------------ init BSF STATUS,RP0 ;BANK1 BCF STATUS, RP1 ; movlw b'11110000' ;Input -> I -> 1 movwf trisa ;Output-> O -> 0 movlw b'11111111' ;Input -> I -> 1 movwf trisb ;Output-> O -> 0 CLRWDT ; bsf STATUS, RP0 ;BANK1 BCF STATUS, RP1 ; movlw b'00000010' ;Pullup=on, Prescaler= 1:8 movwf OPTION_REG ; bcf STATUS, RP0 ;BANK0 MOVLW 7 ;PIC 16F628 Analog comparators: MOVWF CMCON ;Comparators off, all pins digital I/O ramc movlw 0x20 ; 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 return read_encoder: ;------------------------------------------------------------- ; Ex encoder switch position is stored in ex_eng bits 2 and 3. ; by shifting the ex_eng twice right and copying rotary encoder's ; switches to bits 2 and 3 a four bit value is formed that will ; reflect the ex state of the encoder and the present one. ; Up and down branches will increment or decrement ENCODER ; file and by testing MSB overflow is recognized ; (could have used carry, but only 7 bits are needed. ; ; As the rotary encoder advances four times at every "notch" ; the two LSB are ignored when actually using the encoder value, ; leaving five bits for speed! ;------------------------------------------------------------- rrf ex_enc, f ; rrf ex_enc, w ; andlw b'00000011' ; btfsc enc_a ; iorlw b'00000100' ; btfsc enc_b ; iorlw b'00001000' ; movwf ex_enc ; addwf PCL, f ; ;----------------------------------- ; new<-old: ; 00<-10<-11<-01<-00 = UP ; 00<-01<-11<-10<-00 = DOWN ;----------------------------------- goto speed_nop ; 00<-00 no change goto speed_down ; 00<-01 Down goto speed_up ; 00<-10 Up goto speed_err ; 00<-11 Error goto speed_up ; 01<-00 Up goto speed_nop ; 01<-01 No change goto speed_err ; 01<-10 Error goto speed_down ; 01<-11 Down goto speed_down ; 10<-00 Down goto speed_err ; 10<-01 Error goto speed_nop ; 10<-10 no change goto speed_up ; 10<-11 Up goto speed_err ; 11<-00 Error goto speed_up ; 11<-01 Up goto speed_down ; 11<-10 Down goto speed_nop ; 11<-11 No change speed_nop nop ; nop return ; speed_up incf encoder, f ;increment speed btfss encoder, 7 ;test for going too fast return movlw b'01111111' ;yes, replace encoder with movwf encoder ;max speed return speed_down decf encoder, f ;decrement speed btfss encoder, 7 ;test for going too slow return clrf encoder ;yes, replace encoder with return speed_err nop ;errors counter (if any!) 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 half 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 can_read_enc ;Test if TMR0 < F0? ;if so read the encoder btfss INTCON, T0IF ;1/2 bit time gone? goto $-1 ;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 can_read_enc ;Test if TMR0 < F0? ;if so read the encoder s11 btfss INTCON, T0IF ;1/2 bit time gone? goto $-1 ;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 ;------------------------------------------------------------------------ ; 1ms delay ;------------------------------------------------------------------------ delay1ms bcf INTCON, T0IF ;clear overflow flag movlw .130 ;new timer base addwf TMR0, f ;add to timer can_read_enc ;Test if TMR0 < F0? ;if so read the encoder d11 btfss INTCON, T0IF ;1ms gone? goto d11 ;not yet return ;------------------------------------------------------------------------ ; MAIN PROGRAMME ;------------------------------------------------------------------------ main call init ;intialize ports m0 tstf TOGG_3 ;test Toggle skpnz ;is it zero goto m0_1 ;yes, leave it that way! incfsz TOGG_3, W ;no, Increment toggle movwf TOGG_3 ;but not past the top m0_1 tstf TOGG_4 ;test Toggle skpnz ;is it zero goto m0_2 ;yes, leave it that way! incfsz TOGG_4, W ;no, Increment toggle movwf TOGG_4 ;but not past the top m0_2 movlw b'00000000' ;modify new to resemble barebone movwf addr ;IR address payload: 0AAAAAAA movlw b'11000000' ;modify new to resemble barebone movwf speed ;IR speed payload: 11DsSSSS movlw b'10000000' ;modify new to resemble barebone movwf func ;IR func payload: 10MFFFFF ;---------------------------------------------------------------- ;First get the speed settings ;---------------------------------------------------------------- call read_encoder ;read the encoder ;(encoder values 0000-0000 -> 0111-1111) movfw encoder ;get the encoder value sublw b'01110000' ;subtract 28*4 to test if too high btfsc status, c ;Did it go below zero c=0(too fast)? goto m0_3 ;no, continue movlw b'01110000' ;replace with max so that while movwf encoder ;turning knob too far cw the next step ;ccw will slow down m0_3 movfw encoder ;get the encoder value movwf potvalue ;move it to pot value and get rid rrf potvalue, f ;of two bits rrf potvalue, w ;of two bits andlw b'00111111' ;and clear possible carbage from carry 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 ;potvalue: 0, 4..31 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 ;Test if address selection button is down 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..4 and set/clear func's F1..4 bits ;---------------------------------------------------------------- btfss F1Sw ;F1 off (set) / on (clear) bsf func, 0 btfss F2Sw ;F2 off (set) / on (clear) bsf func, 1 ;---------------------------------------------------------------- ; Special Toggle action (like TMWDCC func-action)! ;---------------------------------------------------------------- F3 btfss F3Sw ;F3 off (set) / on (clear) goto F3_on ;Switch pulled: go for it! ;Switch in center position: F3_off movlw .250 ; addwf TOGG_3, w ;Add 255-5 to Toggle to see skpnc ;skip if non carry (short duration) goto F3_off_long ;yes (toggle released after long time) F3_off_short btfsc ex_func, 2 ;no (quick release) XOR the ex_function's bsf func, 2 ;resp. bit to new func byte clrf Togg_3 ;Toggle=> 0 goto F4 ;next switch F3_off_long clrf Togg_3 ;Toggle=> 0 goto F4 ;next switch F3_on tstf Togg_3 ;Test Toggle skpnz ;is zero? goto F3_on_now ;yes F3 just turned on F3_on_before btfsc ex_func, 2 ;no (done before) Copy the ex_function's bsf func, 2 ;resp.bit to new function byte goto F4 F3_on_now incf Togg_3, f ;Toggle to 1 btfss ex_func, 2 ;XOR previous function status' bsf func, 2 ;resp.bit to new ;--------- F4 btfss F4Sw ;F4 off (set) / on (clear) goto F4_on ;Switch pulled: go for it! ;Switch in center position: F4_off movlw .250 ; addwf TOGG_4, w ;Add 255-5 to Toggle to see skpnc ;skip if non carry (short duration) goto F4_off_long ;yes (toggle released after long time) F4_off_short btfsc ex_func, 3 ;no (quick release) XOR the ex_function's bsf func, 3 ;resp. bit to new func byte clrf Togg_4 ;Toggle=> 0 goto F5 ;next switch F4_off_long clrf Togg_4 ;Toggle=> 0 goto F5 ;next switch F4_on tstf Togg_4 ;Test Toggle skpnz ;is zero? goto F4_on_now ;yes F4 just turned on F4_on_before btfsc ex_func, 3 ;no (done before) Copy the ex_function's bsf func, 3 ;resp.bit to new function byte goto F5 F4_on_now incf Togg_4, f ;Toggle to 1 btfss ex_func, 3 ;XOR previous function status' bsf func, 3 ;resp.bit to new ;--------- F5 ; btfss F4Sw ;F4 off (set) / on (clear) ; bsf func, 3 ;---------------------------------------------------------------- ;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 ;---------------------------------------------------------------- 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! 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 ;---------------------------------------------------------- movlw cab_id ;PACK_ID is constant! movwf DATA1 ;save as data to be sent swapf DATA1, f ;swap ends and shift left, so rlf DATA1, f ;now three LSBs are three MSBs movlw .3 ;only 3 bits (NOTE LEFTMOST!) 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 ;---------------------------------------------------------- ;set packet interval ;---------------------------------------------------------- m7 movlw interval ;packet interval movwf BITCOUNT ;bitcount counts ms now 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