kolibrios/contrib/toolchain/avra/examples/throttle_momentum.asm

315 lines
17 KiB
NASM
Raw Normal View History

;throttle_momentum.asm
.NOLIST
; ***************************************************************************************
; * PWM MODEL RAILROAD THROTTLE *
; * *
; * WRITTEN BY: PHILIP DEVRIES *
; * *
; * Copyright (C) 2003 Philip DeVries *
; * *
; * This program is free software; you can redistribute it and/or modify *
; * it under the terms of the GNU General Public License as published by *
; * the Free Software Foundation; either version 2 of the License, or *
; * (at your option) any later version. *
; * *
; * This program is distributed in the hope that it will be useful, *
; * but WITHOUT ANY WARRANTY; without even the implied warranty of *
; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
; * GNU General Public License for more details. *
; * *
; * You should have received a copy of the GNU General Public License *
; * along with this program; if not, write to the Free Software *
; * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
; * *
; ***************************************************************************************:
.LIST
.ifdef MOMENTUM_ENABLED
;********************************************************************************
;* MOMENTUM_ADJUST *
;* Top level routine *
;* *
;* Momentum simulates the mass of the train. Since model trains have little *
;* mass, the locomotive speed can directly follow the throttle setting; in *
;* other words, a model train can accelerate and decelerate instantly. *
;* Real trains are very massive, and therefore they do not accelerate or *
;* decelerate quickly. *
;* *
;* According to Newtons law, the acceleration is proportional to the force, *
;* and inversely proportional to the mass. Therefore, the more massive the *
;* train, the more slowly the train will accelerate or decelerate. Also, the *
;* more force the locomotive can provide, the faster the train will accelerate. *
;* Deceler depends on the braking capability of the overall train. *
;* *
;* If force were constant, Newtons law states that acceleration would be *
;* constant too. This subroutine assumes that somewhat more force is *
;* available at low speeds than at high speeds, so that acceleration will be *
;* greater at low speeds. The subroutine also assumes that opposing forces *
;* (friction, wind resistance, etc) are stronger at higher speeds. This *
;* assumption also means that acceleration will be greater at low speeds. *
;* *
;* This subroutine calculates acceleration/deceleration by this simple *
;* Method: *
;* *
;* The rate of speed change (accleration and deceleration) depends on the *
;* current speed as *
;* {Speed_Max (0xFF) - Current_Speed} / {Tau*Rate} *
;* *
;* Where T = index of sample time *
;* t = real time *
;* Tau = time constant *
;* Rate = Update rate (nominally 100Hz) *
;* *
;* That is, the acceleration/deceleration is maximum at zero speed, and *
;* approaches zero at maximum speed. *
;* *
;* When accelerating, the speed at the next sample period is *
;* Speed(T) = Speed(T-1) + {0xFF - Speed(T-1)} / {Tau*Rate} *
;* *
;* Giving an acceleration curve that looks like a normal exponential. *
;* Speed(t) = 0xFF { 1 - exp( - t / Tau) } *
;* *
;* * * *
;* * *
;* * *
;* * *
;* * *
;* * *
;* * *
;* * *
;* *
;* *
;* When decelerating, the change rate equation is the same, but the change *
;* is subtracted, as *
;* Speed(T) = Speed(T) - {0xFF - Speed(T-1)} / {Tau*Rate} *
;* *
;* Giving a deceleration curve that looks like *
;* Speed(t) = 0xFF { 1 - exp( -(T1 - t) / Tau) } *
;* which is a mirror image of the acceleration, NOT a normal exponential. *
;* *
;* * * *
;* * *
;* * *
;* * *
;* * *
;* * *
;* * *
;* *
;* *
;* In each case, the acceleration or deceleration is "clipped" at the current *
;* throttle setting so that the speed doesn't overshoot or undershoot. *
;* *
;* Three different values of Tau are used, that is *
;* Tau_accel Corresponding to acceleration under power *
;* Tau_coast Corresponding to deceleration when coasting *
;* Tau_brake Corresponding to deceleration when braking *
;* *
;* To permit finer control of momentum, the throttle setting is converted to a *
;* 16 bit number, where the 8 msb's correspond to the throttle setting from *
;* the throttle handle and sent forward. *
;* *
;* Inputs: throttle_set Throttle handle position ( 0x00 to 0xFF ) *
;* speed_hi_prev Hi byte of (T-1) throttle setting (stored) *
;* speed_lo_prev Lo byte of (T-1) throttle setting (stored) *
;* Returns: throttle_set Adjusted throttle setting (T) *
;* speed_hi_prev Hi byte of (T) throttle setting (stored) *
;* speed_lo_prev Lo byte of (T) throttle setting (stored) *
;* Changed: B_Temp *
;* B_Temp1 *
;* B_Temp2 *
;* B_Temp3 *
;* Calls: NONE *
;* Goto: MOMENTUM_ADJUST_RETURN *
;********************************************************************************
B_TEMPLOCAL2 _time_constant_adj
.ifdef TRADITIONAL_ENABLED
.ifdef LEDS_ENABLED
sbrc Flags_1,BF_brake ; If the brake flag is set,
sbi PORTB,dir_in_port ; Port Output: Indicate deceleration
.endif ;LEDS_ENABLED
.endif ;TRADITIONAL_ENABLED
;*******************************************************************
;* Adjust the value of "momentum_set".
;* This adjustment makes it easier to fine adjust low momentum settings
;* while still permitting large momentum settings.
;*
;* The ammount of momentum to apply comes in in "momentum_set"
;* which is read in READ_THROTTLE. The nominal range is
;* 0x00 to 0x40. This value is multiplied by two and squared,
;* giving a new range from 0x00 to 0x4000. The update rate is 100Hz,
;* and so the new range corresponds to a time constant from
;* 0(decimal) to 164(decimal) seconds. Since the adjustment was
;* done by performing a square, the adjusted value is non-linear
;* with the input value.
;*******************************************************************
lsl momentum_set ; multiply by two
HILOCAL1 _mset_multiplier ; supply to mpy8u
B_TEMPLOCAL _mset_multiplicand ; supply to mpy8u
mov _mset_multiplier,momentum_set ;
mov _mset_multiplicand,momentum_set ;
rcall mpy8u ; square
B_TEMPLOCAL1 _mset_hi_byte ; return from mpy8u
B_TEMPLOCAL _mset_lo_byte ; return from mpy8u
;*******************************************************************
;* Compute the difference between the maximum throttle and
;* the current throttle
;*******************************************************************
HILOCAL2 _mset_diff_hi_byte
HILOCAL1 _mset_diff_lo_byte
ldi _mset_diff_hi_byte,0xFF ; Maximum possible speed
ldi _mset_diff_lo_byte,0xFF ;
sub _mset_diff_lo_byte,speed_lo_prev ; Difference between max speed
sbc _mset_diff_hi_byte,speed_hi_prev ; and current speed
;*******************************************************************
;* Determine whether to accelerate, decelerate, or remain unchanged.
;* Compare the throttle handle setting with the actual speed
;*******************************************************************
cp throttle_set,speed_hi_prev ; Test if throttle position is larger
; or smaller than the speed.
breq EVEN_SPEED ; If the throttle position is the same
; as the speed.
brlo SETUP_DECELERATE ; If the throttle position is smaller
; than the speed, then need to decelerate.
; brsh SETUP_ACCELERATE ; If the throttle position is larger
; than the speed, then need to accelerate.
SETUP_ACCELERATE:
.ifdef TRADITIONAL_ENABLED
.ifdef LEDS_ENABLED
cpi throttle_set,accel_led_threshold ; If the throttle is less than minimum
brlo END_SET_ACCEL_LED ; don't light led
mov B_Temp2,throttle_set ; If the throttle is closer than led_threshold
subi B_Temp2,accel_led_threshold ; don't light led
cp B_Temp2,speed_hi_prev
brlo END_SET_ACCEL_LED
sbi PORTB,momentum_port ; Port Output: Indicate acceleration
END_SET_ACCEL_LED:
.endif LEDS_ENABLED
.endif TRADITIONAL_ENABLED
sbr Flags_1,F_accel ; Set accelerating flag
; Indicate acceleration
ldi _time_constant_adj,accel_offset+1 ; Acceleration time constant adjust
rjmp CHECK_BRAKE
EVEN_SPEED: ; Arrive here if throttle_set=current speed
sbrc Flags_1,BF_brake ; If the brake flag is set, decelerate
rjmp CHECK_BRAKE ;
rjmp DONE_WITH_MOMENTUM ; Otherwise adjustment is necessary
SETUP_DECELERATE:
cbr Flags_1,F_accel ; Clear accelerating flag
.ifdef TRADITIONAL_ENABLED
.ifdef LEDS_ENABLED
cpi throttle_set,0xff-decel_led_threshold ; If the throttle is more than maximum
brsh END_SET_DECEL_LED ; don't light led
mov B_Temp2,throttle_set
subi B_Temp2,0x00-decel_led_threshold ; If the throttle is closer than the led
cp B_Temp2,speed_hi_prev ; threshold, don't light the led
brsh END_SET_DECEL_LED
sbi PORTB,dir_in_port ; Port Output: Indicate deceleration
END_SET_DECEL_LED:
.endif LEDS_ENABLED
.endif TRADITIONAL_ENABLED
ldi _time_constant_adj,0+1 ; Coasting deceleration time const. adjust.
; rjmp CHECK_BRAKE
CHECK_BRAKE: ; Always check for the brake.
sbrs Flags_1,BF_brake ; If brake flag is not set,
rjmp ADJUST_TAU ; proceed.
; Brake overrides acceleration
; or coasting.
cbr Flags_1,F_accel ; clear accelerating flag
; Indicate deceleration
ldi _time_constant_adj,brake_offset+1 ; Braking deceleration time const. adjust.
; rjmp ADJUST_TAU
ADJUST_TAU:
;B_TEMP2=B_TEMPLOCAL2
dec _time_constant_adj ; Divide tau_base by 2^_time_constant_adj
breq DIVIDE_TAU ; to produce adjusted tau.
lsr _mset_hi_byte
ror _mset_lo_byte
rjmp ADJUST_TAU
DIVIDE_TAU:
sbr _mset_lo_byte,0b00000001 ; Force last bit 1. Prevent divide by zero.
rcall div16u ; Divide _mset_diff_hi_byte:_mset_diff_lo_byte
; (difference)
; by _mset_hi_byte:_mset_lo_byte (dividor)
sbrs Flags_1,BF_accel ; add or subtract change depending
rjmp SUBTRACT_CHANGE ; on F_accel flag
;rjmp ADD_CHANGE
ADD_CHANGE: ; Case accelerating
; HILOCAL2 _mset_diff_hi_byte
; HILOCAL1 _mset_diff_lo_byte
add speed_lo_prev,_mset_diff_lo_byte ; Add in the change
adc speed_hi_prev,_mset_diff_hi_byte
cp throttle_set,speed_hi_prev ; If larger than the throttle_set value
brlo USE_SET_SPEED ; clamp at throttle_set value
rjmp DONE_WITH_MOMENTUM
SUBTRACT_CHANGE: ; Case decelerating
sbrc Flags_1,BF_brake ; If the brake flag is set,
clr throttle_set ; decelerate all the way to zero
sub speed_lo_prev,_mset_diff_lo_byte ; Subtract the change
sbc speed_hi_prev,_mset_diff_hi_byte ;
brlo USE_SET_SPEED ; If less than zero
; clamp at throttle_set value
cp speed_hi_prev,throttle_set ; If less than the throttle_set value
brlo USE_SET_SPEED ; clamp at throttle_set value
rjmp DONE_WITH_MOMENTUM
USE_SET_SPEED: ; Use the throttle_set value directly
mov speed_hi_prev,throttle_set
clr speed_lo_prev
DONE_WITH_MOMENTUM:
mov throttle_set,speed_hi_prev ; Put the new value into throttle_set.
.endif ;MOMENTUM_ENABLED