'************ TRAIN CONTROLLER * PICAXE 18M2 ************ begin: high b.0 'turn on min led high b.1 'turn on max led pause 1000 'delay loop0: 'disable L6203 and check control pot position low c.6 'L6203 disabled pwmout b.3,66,0 'L6203 reverse direction pwm is off pwmout b.6,66,0 'L6203 forward direction pwm is off readadc c.2,b0 'take control pot reading if b0<5 then goto set_trimpots 'if control maximum anticlockwise, go to trimpot setup if b0>97 and b0<157 then loop10 'if control is in neutral, jump from this routine b13=0 'reset infrared forward/reverse variable if requested(control not in neutral) sound c.7,(100,2) 'sound piezo(indicate control position not in neutral) pause 100 'delay goto loop0 'continue loop loop10: 'start routine sound c.7,(100,2) 'sound piezo pause 50 'delay sound c.7,(100,2) 'sound piezo low c.6 'L6203 disabled pwmout b.3,66,0 'L6203 reverse direction pwm is off pwmout b.6,66,0 'L6203 forward direction pwm is off high b.4 'bemf shorting transistor on low b.0 'turn min led off low b.1 'turn max led off b11=0 b15=0 'reset trip counters w12=0 'reset bemf counter b10=0 'reset infrared b14=0 'reset low/ high speed readadc c.1,b18 'read min speed trimpot readadc c.0,b19 'read max setting trimpot readadc b.2,b17 'read pwm% trimpot b17=b17/3+15 'convert adc 0-255 range to 15-100% b7=b17/2 'b7 is the starting pwm value=50% of pwm% trimpot b16=b18*90/100 'calculate 90% of min trimpot pause 1000 'delay loop20: gosub control_pot 'check position of control if b0<5 then goto set_trimpots'if control is max reverse go to set trimpots routine irin[20], c.5,b10 'check for infrared signal if b13=1 then: b10=19 endif 'remote has called for change of direction (from forward to reverse) if b13=2 then: b10=18 endif 'remote has called for change of direction (from reverse to forward) if b0>157 or b10=18 then forward_start 'control moved to forward min or infrared forward button pressed if b0<97 or b10=19 then reverse_start 'control moved to reverse min or infrared reverse button pressed goto loop20 'continue loop '***** START OF FORWARD ROUTINE START OF FORWARD ROUTINE START OF FORWARD ROUTINE ***** forward_start: 'this routine starts loco b13=0 'reset infrared variable sound c.7,(100,2) 'sound piezo high b.0 'turn on min led for b2=b7 to 255 'start loop(b7 is 50% of pwm% trimpot) gosub check_trip2 'check fault routine if b11>0 then trip 'check for valid trip(if b11=1 or 2 then trip) gosub control_pot 'check position of control if b0<157 and b10=0 then loop10 'control moved to neutral and remote not used (abort start) irin[20], c.5,b10 'check for infrared signal if b10=20 then sound c.7,(100,2) goto loop10 endif 'if remote brake button pressed abort start pwmout b.6,66,0 'L6203 forward direction pwm off low c.6 'L6203 bridge disabled low b.4 'bemf shorting transistor off readadc10 b.5,w13 'read the motor feedback value(w13) high b.4 'bemf shorting transistor on high c.6 'L6203 bridge enabled pwmout b.6,66,b2 'L6203 forward direction pwm on if w13>b16 then forward_run1 'loco is running(bemf w13 has reached 90% min trimpot speed setting,b16=90% of b18) next 'motor is not running, continue loop and increase pwm output(b2) forward_run1: 'motor is moving, lower pwm output(b2) to pwm% trimpot setting b7=b2*b17/100 'calculate a percentage of the start pwm value(b2) using the pwm% trimpot setting(b17) pwmout b.6,66,b7 'and immediately output this new pwm output value now b7 b0=167 'if a start has been initiated by the remote, substitute a value for the control pot forward_run2: 'this routine checks the control/remote selection and runs the loco in either min or max speed for b6=1 to 5 'run loop(**increase the value 5 to alter forward up/down ramp time) readadc c.1,b18 'update min speed trimpot readadc c.0,b19 'update max setting trimpot if b19>240 then: b19=240 endif 'restrict max speed trimpot range(dont go over 250,(ie 240+10) b16=b18*80/100 'create value for b16(extra low speed setting =80% of min trimpot b18) gosub bemf_f 'get new bemf reading gosub check_trip 'check fault routine if b11>0 then trip 'check for valid trip if w13217 then:low b.0 high b.1 b8=b19 b9=b19+10 endif'control at max, provide max.spd.low/high range, turn on max led if b0<217 and w13217 and w13>b8 then: b14=1 endif 'control set to max, if b14=1 then loco is at max speed b15=0 'reset forward_recover trip counter(>15 will trip) next 'continue loop if w13b9 then gosub slowdown 'bemf over set point (less throttle) gosub serial_terminal 'update and display variables on screen loop110: pwmout b.6,66,b7 'update and output new pwm value if changed if b0>217 and w13b9 then toggle b.1 endif 'flash max led when ramping or adjusting speed if b0<217 and w13b9 then toggle b.0 endif 'flash min led when ramping or adjusting speed goto forward_run2 'continue loop forward_recover: 'loco is slowing to extra low speed range so more throttle b7=b7+2 'increase pwm pwmout b.6,66,b7 'supply immediate power boost b15=b15+1 'increase forward_recover counter(more than 15 will trip) if b0>217then:low b.0 high b.1 endif'update leds if speed changed from low to high sertxd("FORWARD RECOVER pwm, trip counter ",#b7,44,#b15,13,10) 'display to show a recover was performed for b3=1 to 5 'start a delay loop before next forward recover irin[20,loop120], c.5,b10 'check for infrared signal during loop gosub infrared 'if infrared signal jump to routine loop120: gosub control_pot 'check position of control if b0<157 or b10=20 then forward_brake 'if control or remote in neutral, apply brake next 'continue loop, wait for loco to respond goto loop110 'return to normal loop forward_brake: 'routine slows the loco to stop b16=b18/2 'create bemf stopping value (50% of min trimpot) low b.1 'turn off max led if on high b.0 'turn on min led if off sound c.7,(100,5) 'sound piezo, aknowledge neutral/brake request for b7=b7 to 0 step-1 'start pwm decrease (reduce pwm output until bemf reaches b16 value) gosub bemf_f 'get new bemf reading if w130 then loop130 'if this is the first reading(w12=0) then multiply w13*4 let w12=w13*4 'this is a calculated working number to provide an average value for future bemf readings loop130: 'when the next reading is taken, it is added to the old readings and an average let w13=w13+w12/5 'is taken of the previous 5 readings, this ensures a more accurate bemf reading. let w12=w13*4 'w12 is loaded for the next new bemf reading(w13). return '***** START OF REVERSE ROUTINE START OF REVERSE ROUTINE START OF REVERSE ROUTINE***** ' notes are not given for reverse routine, operation is the same as forward, except for control pot ' values and pwm output.... pwm pin(b.3 for reverse,b.6 for forward) reverse_start: b13=0 sound c.7,(100,2) high b.0 for b2=b7 to 255 gosub check_trip2 if b11>0 then trip gosub control_pot if b0>97 and b10=0 then loop10 irin[20], c.5,b10 if b10=20 then sound c.7,(100,2) goto loop10 endif pwmout b.3,66,0 low c.6 low b.4 readadc10 b.5,w13 high b.4 high c.6 pwmout b.3,66,b2 if w13>b16 then reverse_run1 next reverse_run1: b7=b2*b17/100 pwmout b.3,66,b7 b0=87 reverse_run2: for b6=1 to 5 'run loop(**increase the value 5 to alter reverse up/down ramp time) readadc c.1,b18 readadc c.0,b19 if b19>240 then: b19=240 endif b16=b18*80/100 gosub bemf_r gosub check_trip if b11>0 then trip if w1397 then reverse_brake if b0>37 then:low b.1 high b.0 b8=b18 b9=b18+10 endif if b0<37 then:low b.0 high b.1 b8=b19 b9=b19+10 endif if b0>37 and w13b8 then: b14=1 endif b15=0 next if w13b9 then gosub slowdown gosub serial_terminal loop210: pwmout b.3,66,b7 if b0<37 and w13b9 then toggle b.1 endif if b0>37 and w13b9 then toggle b.0 endif goto reverse_run2 reverse_recover: b7=b7+2 pwmout b.3,66,b7 b15=b15+1 if b0<37 then:low b.0 high b.1 endif sertxd("REVERSE RECOVER, pwm, trip counter ",#b7,44,#b15,13,10) for b3=1 to 5 irin[20,loop220], c.5,b10 gosub infrared loop220: gosub control_pot if b0>97 or b10=20 then reverse_brake next goto loop210 reverse_brake: b16=b18/2 low b.1 high b.0 sound c.7,(100,5) for b7=b7 to 0 step-1 gosub bemf_r if w130 then loop230 let w12=w13*4 loop230: let w13=w13+w12/5 let w12=w13*4 ' return '******************************************************************************************************** '***** both forward and reverse operation share the speedup,slowdown,trip,serial, set trimpots, infrared, '***** control pot and serial terminal routines below *************************************************** '*****SPEEDUP AND SLOWDOWN ROUTINES***** 'the initial calculation works out the difference between bemf and setpoint.An error less 'than 20 will increase the pwm value by 1, from 20 to 39=2,40 to 59=3, and so on 'when ramping up however, the pwm value only increases by 1 speedup: 'routine for increasing pwm(throttle) variable b14 is used to check if max speed has been reached) b12=b8-w13+20/20 'check how large the error is.. (feed back compared to setpoint) if b0>157 and b0<217 and b14=0 then:b7=b7+b12 goto loop300 endif 'too slow min forward if b0>37 and b0<97 and b14=0 then:b7=b7+b12 goto loop300 endif 'too slow min reverse if b0>217 and b14=1 then:b7=b7+b12 goto loop300 endif 'too slow max forward if b0<37 and b14=1 then:b7=b7+b12 goto loop300 endif 'too slow max reverse b7=b7+1 'control changed from min to max, ramp up, increase pwm by 1 goto loop310 loop300: if b12=1 then loop310 'if value is only 1, skip delay pause 1000 'delay for values more than 1 loop310: return 'the initial calculation works out the difference between bemf and setpoint.An error less 'than 20 will decrease the pwm value by 1, from 20 to 39=2,40 to 59=3, and so on 'when ramping down however, the pwm value only decreases by 1 slowdown: 'routine for decreasing pwm(throttle) variable b14 is used to check if max speed has been reached) b12=w13-b9+20/20 'check how large the error is.. (feed back compared to setpoint) if b0>157 and b0<217 and b14=0 then:b7=b7-b12 goto loop320 endif 'too fast min forward if b0>37 and b0<97 and b14=0 then:b7=b7-b12 goto loop320 endif 'too fast min reverse if b0>217 and b14=1 then:b7=b7-b12 goto loop320 endif 'too fast max reverse if b0<37 and b14=1 then:b7=b7-b12 goto loop320 endif 'too fast max reverse b7=b7-1 'control changed from max to min, ramp down, decrease pwm by 1 goto loop330 loop320: if b12=1 then loop330 'if value is only 1, skip delay pause 1000 'delay for values more than 1 loop330: return '*****TRIP ROUTINE TRIP ROUTINE TRIP ROUTINE TRIP ROUTINE***** 'b1 is current variable, b2 loop counter, b7 pwm output, b15 speed recover loop counter trip: if b11=1 then sertxd("OVER CURRENT TRIP, b1>5= ",#b1,13,10) goto loop340 endif 'display overcurrent trip sertxd("UNDERSPEED, PWM ,LOOP COUNTER FAULT",13,10) 'display underspeed trip sertxd("b2>100, b7>100 or b15>15 ",#b2,44,#b7,44,#b15,13,10) 'display underspeed trip loop340: low c.6 'L6203 bridge disabled pwmout b.3,66,0 'L6203 reverse direction pwm off pwmout b.6,66,0 'L6203 forward direction pwm off loop350: 'routine for flashing leds and sounding piezo for b20=1 to 10 'loop1 for b21=1 to b11 'loop2 Changes alarm type as per variable b11 above(b11=1 o/current, b11=2 u/speed) if b10=20 then loop0 'remote neutral button has been pressed, reset alarm if b10>0 then loop360 'remote is controlling so jump manual control gosub control_pot 'check position of control if b0>97 and b0<157 then loop10 'control in neutral, reset alarm loop360: pause 100 'loop delay high b.1 'turn on max led high b.0 'turn on min led sound c.7,(100,10) 'sound piezo pause 50 'loop delay low b.1 'turn off max led low b.0 'turn off max led next 'next loop irin[500], c.5,b10 'check for infrared signal(alarm reset) next 'next loop goto loop350 'continue loops '*****CHECK TRIP ROUTINE CHECK TRIP ROUTINE***** check_trip: 'underspeed trips if b7>100 or b7<5 then: b11=2 endif 'pwm variable out of range if w13>=b16 then:b15=0 endif 'bemf is above extra low setting so reset trip counter if b15>15 then: b11=2 endif 'speed recover counter, if more than 15 set trip variable(b11) check_trip2: 'over current trip readadc b.7,b1 'check current if b1>5 then: b11=1 endif 'current too high, set trip variable(b11)**change value 5 for current setting if b2>100 then: b11=2 endif 'pwm range too high, set trip variable(loco failed to start) return '*****SET TRIMPOTS ROUTINE SET TRIMPOTS ROUTINE***** set_trimpots: 'sound piezo 5 times to acknowledge trimpot setting selection for b6=1 to 5 sound c.7,(120,10) 'sound piezo pause 300 'delay next 'continue loop set_trimpots2: low b.0 'turn off min led low b.1 'turn off max led set_trimpots3: b10=0 'reset infrared value gosub control_pot 'check position of control if b0>250 then loop370 'control moved from reverse max to max clockwise (settings are complete) readadc c.1,b18 'check min speed trimpot readadc c.0,b19 'check max setting trimpot readadc b.2,b17 'check pwm% trimpot b17=b17/3+15 'convert adc 0-255 range to 15-100% if b17=70 then:low b.1 toggle b.0 endif 'flash min led at 70% trimpot setting if b17=75 then:low b.0 toggle b.1 endif 'flash max led at 75% trimpot setting if b17=80 then:low b.1 high b.0 endif 'turn on min led at 80% trimpot setting if b17=85 then:low b.0 high b.1 endif 'turn on max led at 85% trimpot setting sertxd("CONTROL POSITION, PWM%, MIN, MAX ",#b0,44,#b17,"%",44,#b18,44,#b19,13,10) 'display variables if b17<>70and b17<>75and b17<>80and b17<>85 then goto set_trimpots2'turn leds off between 70,75,80 and 85 goto set_trimpots3 'continue loop loop370: goto loop0 'control pot moved fully clockwise,back to start '*****REMOTE INFRARED ROUTINE REMOTE INFRARED ROUTINE***** infrared: if b10=20 then goto loop420 'if brake requested, jump this routine sound c.7,(100,5) 'sound piezo (acknowledge remote button request) if b0<97 then loop400 'reverse request so jump to reverse button values if b10=19 then:b13=1:b10=20 goto loop410 endif 'remote has requested reverse(set variable b13 for reverse) if b10=16 then:b0=227 low b.0 high b.1 goto loop410 endif'max speed forward request, substitute values for control if b10=17 then:b0=167 low b.1 high b.0 goto loop410 endif'min speed forward request, substitute values for control goto loop410 loop400: if b10=18 then:b13=2 b10=20 goto loop410 endif 'remote has requested forward(set variable b13 for forward) if b10=16 then:b0=27 low b.0 high b.1 endif 'max speed reverse request, substitute values for control if b10=17 then:b0=87 low b.1 high b.0 endif 'min speed reverse request ,substitute values for control loop410: pause 1000 'delay loop420: return '*****CONTROL POT ROUTINE CONTROL POT ROUTINE CONTROL POT ROUTINE ***** 'an average of 10 control pot samples is taken for accuracy 'If remote control is in use, this routine is ignored control_pot: b4=b0 'store the existing control pot value(b0) in variable b4 w11=0 'clear a working variable for b5=1 to 10 'start loop to count to 10 readadc c.2,b0 'take control pot reading w11=w11+b0 'accumulate the readings next 'continue loop w11=w11/10 'average the 10 readings b0=w11 'new control pot value if remote is not in use if b10>0 then: b0=b4 endif 'if remote is in use,reload saved value (beginning of this sub routine) return '*****SERIAL TERMINAL ROUTINE SERIAL TERMINAL ROUTINE***** serial_terminal: 'display working variables readadc b.2,b17 'update pwm% trimpot b17=b17/3+15 'calculate percentage for display if b0>217 or b0<37 then loop450 'max speed is selected, so display max variables sertxd("MIN.SPEED, pwm%,pwm,lo,bemf,hi ",#b17,"%",44,#b7,44,#b8,44,#w13,44,#b9,13,10) goto loop460 loop450: sertxd("MAX.SPEED, pwm%,pwm,lo,bemf,hi ",#b17,"%",44,#b7,44,#b8,44,#w13,44,#b9,13,10) loop460: return