; ; Technical Aid to the Disabled, Australia ; Custom-Designed Aids Service, Sydney ; Project No. 98/11245 ; Client Robert Higman, aged 6. ; ; Program for PIC 12C509A microcontroller for toy train switching ; Written by James Cameron ; Revision A 19-Oct-1998 ; proto equ 0 ; we are prototyping on 16F84 ; change to zero for final burn if proto == 1 processor 16f84 list f=inhx8m include "p16f84.inc" __CONFIG _CP_OFF & _WDT_OFF & _HS_OSC else processor 12c509 list f=inhx8m include "p12c509a.inc" __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC endif ; general purpose register allocation ; first common free address as per ; p12 of 12c509 spec ; p13 of 16f84 spec tmp1 equ 0x0c ; delay loop outer counter tmp2 equ 0x0d ; delay loop inner counter cycle equ 0x0e ; duty cycle accumulators accel equ 0x0f ; acceleration actual equ 0x10 ; actual speed wanted equ 0x11 ; desired speed was equ 0x12 ; archive copy of input port is equ 0x13 ; recent copy of input port time1 equ 0x14 ; timeout accumulators time2 equ 0x15 time3 equ 0x16 beat equ 0x17 ; alive heartbeat ; timeout reset value in tenth seconds k_time1 equ 0x70 ; idle timeout 0x0870 k_time2 equ 0x08 k_time3 equ d'50' ; transmission timeout ; duty cycles, modulus 256 k_off equ d'0' ; motor off k_start equ d'55' ; motor starting k_full equ d'255' ; motor full speed ; accelerations k_up equ d'2' ; go faster k_cruise equ d'0' ; cruise control engaged k_down equ d'253' ; go slower k_timeout equ d'255' ; timeout, go slower slowest k_emergency equ d'240' ; emergency brake k_granule equ d'51' ; speed change granularity ; interface bit definitions if proto == 1 ; which port to use port equ PORTB else port equ GPIO endif b_motor equ 0x00 ; [O] gate of mosfet for motor drive b_alive equ 0x01 ; [O] orange diagnostic led b_hear equ 0x02 ; [O] green diagnostic led b_stop equ 0x03 ; [I] right pushbutton [0=closed] b_go equ 0x04 ; [I] left pushbutton [0=closed] b_vt equ 0x05 ; [I] valid transmission [1=true] m_input equ b'111000' ; input mask, which bits are input org 0x00 ; reset vector if proto != 1 movwf OSCCAL ; store oscillator calibration endif goto main dt "a 1998-10-19 james.cameron@digital.com tad 98-11245" restart: ; reset timeout counter movlw k_time1 movwf time1 ; reset LSB movlw k_time2 movwf time2 ; reset MSB retlw 0 time: ; check for timeout, called every 100ms movf wanted,w ; set zero flag if motor off btfsc STATUS,Z ; skip if not zero retlw 0 ; motor off, return without updating decfsz time1,f ; decrement the counter LSB retlw 0 ; not yet zero, return decfsz time2,f ; decrement the counter MSB retlw 0 ; not yet zero, return movlw k_timeout ; set acceleration to stop movwf accel clrf wanted retlw 0 quarter: movlw d'250' ; delay 250ms motor: ; delay "w" number of milliseconds movwf tmp1 ; copy argument motor_0: ; start of one millisecond loop movlw d'247' ; inner loop for 988 cycles movwf tmp2 ; move to register bank motor_1: nop ; [1] decfsz tmp2,f ; [1/2] decrement inner counter goto motor_1 ; [2] end of loop ; [total 4*247-1=987] ; [cumulative total 989 cycles] movf actual,w ; [1] update pulse width modulation @ 1kHz addwf cycle,f ; [1] btfsc STATUS,C ; [1/2] goto motor_2 ; [2/] bcf port,b_motor ; [/1] goto motor_3 ; [/2] motor_2: bsf port,b_motor ; [1/] ; [total 6/7] ; [cumulative total 995/996 cycles] motor_3: nop ; [1] decfsz tmp1,f ; [1] decrement outer (1mS) counter goto motor_0 ; [2] end of 1mS loop retlw 0 ; [grand total 999/1000 cycles per count] adjust: ; change speed by accel toward wanted movf accel,w ; get acceleration (set flags) btfsc STATUS,Z ; check for cruise mode retlw 0 ; zero is set, so acceleration zero btfsc accel,7 ; adjusting up or down? goto adjust_down ; sign bit set, so accel is negative adjust_up: movf actual,w ; get current speed btfsc STATUS,Z ; is it zero? movlw k_start ; yes, accelerate using starting speed adjust_up_more: addwf accel,w ; add in acceleration btfsc STATUS,C ; check for overflow goto adjust_high ; carry set, overflow on add, branch movwf actual ; store the changed speed movf wanted,w ; check for above maximum subwf actual,w btfss STATUS,C retlw 0 ; carry set, no underflow on subtract adjust_high: movf wanted,w ; set wanted speed movwf actual movlw k_cruise ; set cruise control movwf accel retlw 0 adjust_down: movf actual,w ; get current speed addwf accel,w ; add in the negative acceleration movwf actual ; store the changed speed btfss STATUS,C ; check for expected overflow goto adjust_low ; carry not set, overflow happened movf wanted,w ; check for below minimum subwf actual,w btfsc STATUS,C retlw 0 adjust_low: movf wanted,w ; set wanted speed movwf actual movlw k_cruise ; set cruise control movwf accel retlw 0 input: btfsc port,b_vt ; valid transmission? goto input_valid ; branch if so bcf port,b_hear ; turn off "we can hear" LED decfsz time3,f ; decrement valid transmission timeout goto input_not_valid movlw k_emergency ; emergency stop movwf accel clrf wanted goto input_not_valid input_valid: ; we can hear movlw k_time3 movwf time3 ; reset valid transmission timeout bsf port,b_hear ; turn on "we can hear" LED movf port,w ; get copy of port bits movwf is ; save our new copy btfss is,b_go ; is go button on? goto input_not_go ; no, branch btfsc was,b_go ; was go button off? goto input_not_go ; no, branch ; go button has turned on call restart ; restart the timeout movlw k_granule movf wanted,f ; test for stopped btfsc STATUS,Z movlw k_granule*2 ; and do double if so addwf wanted,w btfsc STATUS,C ; if it overflowed ... goto input_not_go ; ... branch to ignore request movwf wanted ; set new wanted speed movlw k_up ; set acceleration movwf accel bcf port,b_hear ; momentarily turn off "hearing" LED input_not_go: btfss is,b_stop ; is stop button on? goto input_not_stop ; no, branch btfsc was,b_stop ; was stop button off? goto input_not_stop ; no, branch ; stop button has turned on call restart ; restart the timeout movlw k_granule ; try decrementing wanted speed subwf wanted,w btfss STATUS,C ; if it would go below zero ... goto input_not_stop ; ... branch to ignore request movwf wanted ; set new wanted speed movlw k_down ; set acceleration movwf accel bcf port,b_hear ; momentarily turn off "hearing" LED input_not_stop: movf is,w ; save reviewed port bits for next look movwf was input_not_valid: retlw 0 reinitialise: movlw m_input if proto == 1 ; are we on 16f84? bsf STATUS,RP0 ; select register bank 1 movwf TRISB ; set port a direction register bcf STATUS,RP0 ; select register bank 0 else ; otherwise 12c509 tris GPIO ; set port direction register movlw b'11011111' option ; we want GP2 as output (note p15 spec) endif retlw 0 initialise: clrf actual clrf wanted clrf accel clrf was clrf port bsf port,b_hear call quarter bsf port,b_alive call quarter bcf port,b_hear call quarter bcf port,b_alive call quarter clrf beat retlw 0 alive: decf beat,f btfsc beat,1 goto alive_1 btfsc beat,2 goto alive_1 bsf port,b_alive retlw 0 alive_1: bcf port,b_alive retlw 0 main: call reinitialise ; set i/o registers call initialise ; clear variables and say hello main_1: call reinitialise ; reset i/o registers in case of EMC movlw d'100' ; check inputs every 10th second call motor ; run motor pwm for this time call time ; check for timeout call adjust ; adjust the train speed call input ; check for user input call alive ; indicate that all is well goto main_1 ; loop for another cycle end