|
|
楼主 |
发表于 2015-3-27 09:42:22
|
显示全部楼层
;-----bko-----------------------------------------------------------------
; Unlike the normal evaluate_rc, we look here for programming mode (pulses
; above PROGRAM_RC_PULS), unless we have received I2C or UART input.
;
; With pulse width modulation (PWM) input, we have to be careful about
; oscillator drift. If we are running on a board without an external
; crystal/resonator/oscillator, the internal RC oscillator must be used,
; which can drift significantly with temperature and voltage. So, we must
; use some margins while calibrating. The internal RC speeds up when cold,
; causing arming problems if the learned pulse is too low. Likewise, the
; internal RC slows down when hot, making it impossible to reach full
; throttle.
evaluate_rc_init:
.if USE_UART
sbrc flags1, UART_MODE
rjmp evaluate_rc_uart
.endif
.if USE_I2C
sbrc flags1, I2C_MODE
rjmp evaluate_rc_i2c
.endif
.if RC_CALIBRATION && (USE_ICP || USE_INT0)
cbr flags1, (1<<EVAL_RC)
; If input is above PROGRAM_RC_PULS, we try calibrating throttle
ldi2 YL, YH, puls_high_l ; Start with high pulse calibration
sbrc flags0, NO_CALIBRATION ; Is it safe to calibrate now?
rjmp evaluate_rc_puls
rjmp rc_prog1
rc_prog0: rcall wait240ms ; Wait for stick movement to settle
; Collect average of throttle input pulse length
rc_prog1: movw temp3, rx_l ; Save the starting pulse length
wdr
rc_prog2: mul ZH, ZH ; Clear 24-bit result registers (0 * 0 -> temp5:temp6)
clr temp7
cpi YL, low(puls_high_l) ; Are we learning the high pulse?
brne rc_prog3 ; No, maybe the low pulse
cpi2 temp3, temp4, PROGRAM_RC_PULS * CPU_MHZ, temp1
brcs evaluate_rc_puls ; Lower than PROGRAM_RC_PULS - exit programming
ldi temp1, 32 * 31/32 ; Full speed pulse averaging count (slightly below exact)
rjmp rc_prog5
rc_prog3: lds temp1, puls_high_l ; If not learning the high pulse, we should stay below it
cp temp3, temp1
lds temp1, puls_high_h
cpc temp4, temp1
brcc rc_prog1 ; Restart while pulse not lower than learned high pulse
cpi YL, low(puls_low_l) ; Are we learning the low pulse?
brne rc_prog4 ; No, must be the neutral pulse
ldi temp1, 32 * 17/16 ; Stop/reverse pulse (slightly above exact)
rjmp rc_prog5
rc_prog4: lds temp1, puls_low_l
cp temp3, temp1
lds temp1, puls_low_h
cpc temp4, temp1
brcs rc_prog1 ; Restart while pulse lower than learned low pulse
ldi temp1, 32 ; Neutral pulse measurement (exact)
rc_prog5: mov tcnt2h, temp1 ; Abuse tcnt2h as pulse counter
rc_prog6: wdr
sbrs flags1, EVAL_RC ; Wait for next pulse
rjmp rc_prog6
cbr flags1, (1<<EVAL_RC)
movw temp1, rx_l ; Atomic copy of new rc pulse length
add temp5, temp1 ; Accumulate 24-bit average
adc temp6, temp2
adc temp7, ZH
sub temp1, temp3 ; Subtract the starting pulse from this one
sbc temp2, temp4 ; to find the drift since the starting pulse
; Check for excessive drift with an emulated signed comparison -
; add the drift amount to offset the negative side to 0
adiwx temp1, temp2, MAX_DRIFT_PULS * CPU_MHZ
; ..then subtract the 2*drift + 1 -- carry will be clear if
; we drifted outside of the range
sbiwx temp1, temp2, 2 * MAX_DRIFT_PULS * CPU_MHZ + 1
brcc rc_prog0 ; Wait and start over if input moved
dec tcnt2h
brne rc_prog6 ; Loop until average accumulated
ldi temp1, 3
rcall lsl_temp567 ; Multiply by 8 (so that 32 loops makes average*256)
st Y+, temp6 ; Save the top 16 bits as the result
st Y+, temp7
; One beep: high (full speed) pulse received
rcall beep_f3
cpi YL, low(puls_high_l+2)
breq rc_prog1 ; Go back to get low pulse
; Two beeps: low (stop/reverse) pulse received
rcall wait30ms
rcall beep_f3
cpi YL, low(puls_low_l+2)
.if RC_PULS_REVERSE
breq rc_prog1 ; Go back to get neutral pulse
.else
breq rc_prog_done
.endif
; Three beeps: neutral pulse received
rcall wait30ms
rcall beep_f3
rc_prog_done: rcall eeprom_write_block
rjmp puls_scale ; Calculate the new scaling factors
.endif
;-----bko-----------------------------------------------------------------
; These routines may clobber temp* and Y, but not X.
evaluate_rc:
.if USE_UART
sbrc flags1, UART_MODE
rjmp evaluate_rc_uart
.endif
.if USE_I2C
sbrc flags1, I2C_MODE
rjmp evaluate_rc_i2c
.endif
; Fall through to evaluate_rc_puls
;-----bko-----------------------------------------------------------------
.if USE_ICP || USE_INT0
evaluate_rc_puls:
cbr flags1, (1<<EVAL_RC)+(1<<REVERSE)
.if MOTOR_BRAKE || LOW_BRAKE
sts brake_want, ZH
.endif
movw temp1, rx_l ; Atomic copy of rc pulse length
.if defined(MIN_RC_PULS)
cpi2 temp1, temp2, MIN_RC_PULS * CPU_MHZ, temp3
brcc puls_long_enough
sbr flags0, (1<<RCP_ERROR)
ret
puls_long_enough:
.endif
.if LOW_BRAKE
lds YL, puls_low_l ; Lowest calibrated pulse (regardless of RC_PULS_REVERSE)
lds YH, puls_low_h
sbiwx YL, YH, RCP_LOW_DBAND * CPU_MHZ
brcs puls_not_low_brake
cp temp1, YL
cpc temp2, YH
brcc puls_not_low_brake
ldi YL, 2
sts brake_want, YL ; Set desired brake to 2 (low brake)
rjmp puls_zero
puls_not_low_brake:
.endif
lds YL, neutral_l
lds YH, neutral_h
sub temp1, YL ; Offset input to neutral
sbc temp2, YH
brcc puls_plus
.if RC_PULS_REVERSE
sbr flags1, (1<<REVERSE)
com temp2 ; Negate 16-bit value to get positive duty cycle
neg temp1
sbci temp2, -1
lds temp3, rev_scale_l ; Load reverse scaling factor
lds temp4, rev_scale_h
rjmp puls_not_zero
.endif
; Fall through to stop/zero in no reverse case
puls_zero_brake:
.if MOTOR_BRAKE
ldi YL, 1
sts brake_want, YL ; Set desired brake to 1 (neutral brake)
.endif
puls_zero: clr YL
clr YH
rjmp rc_duty_set
puls_plus:
lds temp3, fwd_scale_l ; Load forward scaling factor
lds temp4, fwd_scale_h
puls_not_zero:
.if RCP_DEADBAND
sbiwx temp1, temp2, RCP_DEADBAND * CPU_MHZ
brmi puls_zero_brake
.endif
.endif
; The following is used by all input modes
rc_do_scale: ldi2 YL, YH, MIN_DUTY ; Offset result so that 0 is MIN_DUTY
rcall mul_y_12x34 ; Scaled result is now in Y
cpi2 YL, YH, MAX_POWER, temp1
brcs rc_duty_set
ldi2 YL, YH, MAX_POWER
rc_duty_set: sts rc_duty_l, YL
sts rc_duty_h, YH
sbrs flags0, SET_DUTY
rjmp rc_no_set_duty
ldi temp1, RCP_TOT
mov rc_timeout, temp1 ; Short rc_timeout when driving
rjmp set_new_duty_l ; Skip reload into YL:YH
rc_no_set_duty: ldi temp1, 0xff
cp rc_timeout, temp1
adc rc_timeout, ZH
ret
;-----bko-----------------------------------------------------------------
.if USE_I2C
evaluate_rc_i2c:
movw YL, rx_l ; Atomic copy of 16-bit input
cbr flags1, (1<<EVAL_RC)
; Load settings from BLConfig structure (BL-Ctrl v2)
lds temp1, blc_bitconfig
bst temp1, 0 ; BitConfig bit 0: Reverse
bld flags1, REVERSE
; MK sends one or two bytes, if supported, and if low bits are
; non-zero. We store the first received byte in rx_h, second
; in rx_l. There are 3 low bits which are stored at the low
; side of the second byte, so we must shift them to line up with
; the high byte. The high bits become less significant, if set.
lsl YL ; 00000xxxb -> 0000xxx0b
swap YL ; 0000xxx0b -> xxx00000b
adiw YL, 0 ; 16-bit zero-test
breq rc_duty_set ; Power off
; Scale so that YH == 247 is MAX_POWER, to support reaching full
; power from the highest MaxGas setting in MK-Tools. Bernhard's
; original version reaches full power at around 245.
movw temp1, YL
ldi2 temp3, temp4, 0x100 * (POWER_RANGE - MIN_DUTY) / 247
rjmp rc_do_scale ; The rest of the code is common
.endif
;-----bko-----------------------------------------------------------------
.if USE_UART
evaluate_rc_uart:
mov YH, rx_h ; Copy 8-bit input
cbr flags1, (1<<EVAL_RC)+(1<<REVERSE)
ldi YL, 0
cpi YH, 0
breq rc_duty_set ; Power off
; Scale so that YH == 200 is MAX_POWER.
movw temp1, YL
ldi2 temp3, temp4, 0x100 * (POWER_RANGE - MIN_DUTY) / 200
rjmp rc_do_scale ; The rest of the code is common
.endif
;-----bko-----------------------------------------------------------------
; Calculate the neutral offset and forward (and reverse) scaling factors
; to line up with the high/low (and neutral) pulse lengths.
puls_scale:
.if RC_PULS_REVERSE
lds temp1, puls_neutral_l
lds temp2, puls_neutral_h
.else
lds temp1, puls_low_l
lds temp2, puls_low_h
.endif
sts neutral_l, temp1
sts neutral_h, temp2
; Find the distance to full throttle and fit it to match the
; distance between FULL_RC_PULS and STOP_RC_PULS by walking
; for the lowest 16.16 multiplier that just brings us in range.
lds temp3, puls_high_l
lds temp4, puls_high_h
sub temp3, temp1
sbc temp4, temp2
rcall puls_find_multiplicand
sts fwd_scale_l, temp1
sts fwd_scale_h, temp2
.if RC_PULS_REVERSE
lds temp3, puls_neutral_l
lds temp4, puls_neutral_h
lds temp1, puls_low_l
lds temp2, puls_low_h
sub temp3, temp1
sbc temp4, temp2
rcall puls_find_multiplicand
sts rev_scale_l, temp1
sts rev_scale_h, temp2
.endif
ret
;-----bko-----------------------------------------------------------------
; Find the lowest 16.16 multiplicand that brings us to full throttle
; (POWER_RANGE - MIN_DUTY) when multiplied by temp3:temp4.
; The range we are looking for is around 3000 - 10000:
; m = (POWER_RANGE - MIN_DUTY) * 65536 / (1000us * 16MHz)
; If the input range is < 100us at 8MHz, < 50us at 16MHz, we return
; too low a multiplicand (higher won't fit in 16 bits).
puls_find_multiplicand:
.if RCP_DEADBAND
sbi2 temp3, temp4, RCP_DEADBAND * CPU_MHZ
.endif
ldi2 temp1, temp2, (POWER_RANGE - MIN_DUTY) * 65536 / MAX_RC_PULS / CPU_MHZ
puls_find1: adiw temp1, 1
wdr
cpi temp2, 0xff
cpc temp1, temp2
breq puls_find_fail ; Return if we reached 0xffff
; Start with negative POWER_RANGE so that 0 is full throttle
ldi2 YL, YH, MIN_DUTY - POWER_RANGE
rcall mul_y_12x34
; We will always be increasing the result in steps of less than 1,
; so we can test for just zero rather than a range.
brne puls_find1
puls_find_fail: ret
;-----bko-----------------------------------------------------------------
update_timing:
cli
in temp1, TCNT1L
in temp2, TCNT1H
lds temp3, tcnt1x
in temp4, TIFR
sei
cpi temp2, 0x80 ; tcnt1x is right when TCNT1h[7] set;
sbrc temp4, TOV1 ; otherwise, if TOV1 is/was pending,
adc temp3, ZH ; increment our copy of tcnt1x.
; Calculate the timing from the last two zero-crossings
lds YL, last_tcnt1_l ; last -> Y
lds YH, last_tcnt1_h
lds temp7, last_tcnt1_x
sts last_tcnt1_l, temp1
sts last_tcnt1_h, temp2
sts last_tcnt1_x, temp3
lds temp5, l2_tcnt1_l ; last2 -> temp5
lds temp6, l2_tcnt1_h
lds temp4, l2_tcnt1_x
sts l2_tcnt1_l, YL
sts l2_tcnt1_h, YH
sts l2_tcnt1_x, temp7
; Cancel DC bias by starting our timing from the average of the
; last two zero-crossings. Commutation phases always alternate.
; Next start = (cur(c) - last2(a)) / 2 + last(b)
; -> start=(c-b+(c-a)/2)/2+b
;
; (c - a)
; (c - b + -------)
; 2
; start = ----------------- + b
; 2
sub temp1, temp5 ; c' = c - a
sbc temp2, temp6
sbc temp3, temp4
; Limit maximum RPM (fastest timing)
cpi3 temp1, temp2, temp3, TIMING_MAX * CPU_MHZ / 2, temp4
brcc update_timing1
ldi3 temp1, temp2, temp3, TIMING_MAX * CPU_MHZ / 2
lsr sys_control_h ; limit by reducing power
ror sys_control_l
update_timing1:
; Calculate a hopefully sane duty cycle limit from this timing,
; to prevent excessive current if high duty is requested when the
; current duty is low. This is the best we can do without a current
; sensor. The actual current will depend on motor KV and voltage,
; so this is just an approximation. It would be nice if we could
; do this with math instead of two constants, but we need a divide.
; Clobbers only temp4. Fastest in case of fastest timing.
cpi2 temp2, temp3, (TIMING_RANGE3 * CPU_MHZ / 2) >> 8, temp4
ldi2 XL, XH, MAX_POWER
brcs update_timing4
; 24.8-bit fixed-point unsigned divide, inlined with available registers:
; duty (XL:XH) = MAX_POWER * (TIMING_RANGE3 * CPU_MHZ / 2) / period (temp1:temp2:temp3)
; This takes about one microsecond per loop, but we only take this path
; when the motor is spinning slowly.
ldi XL, byte3(MAX_POWER * (TIMING_RANGE3 * CPU_MHZ / 2) / 0x100)
ldi XH, 33 ; Iteration counter
movw timing_duty_l, XL
ldi2 XL, XH, MAX_POWER * (TIMING_RANGE3 * CPU_MHZ / 2) / 0x100
mul ZH, ZH ; Zero temp5, temp6
sub temp4, temp4 ; Zero temp4, clear carry
rjmp fudiv24_ep ; Jump with carry clear
fudiv24_loop:
rol temp4
rol temp5
rol temp6
cp temp4, temp1 ; Divide by commutation period
cpc temp5, temp2
cpc temp6, temp3
brcs fudiv24_ep
sub temp4, temp1
sbc temp5, temp2
sbc temp6, temp3
fudiv24_ep:
rol XL
rol XH
rol timing_duty_l
dec timing_duty_h
brne fudiv24_loop
com XL
com XH
cpi2 XL, XH, PWR_MAX_RPM1, temp4
brcc update_timing4
ldi2 XL, XH, PWR_MAX_RPM1
update_timing4: movw timing_duty_l, XL
sts timing_l, temp1 ; Store timing (120 degrees)
sts timing_h, temp2
sts timing_x, temp3
lsr temp3 ; c'>>= 1 (shift to 60 degrees)
ror temp2
ror temp1
.if defined(DC_BIAS_CANCEL)
lds temp5, last_tcnt1_l ; restore original c as a'
lds temp6, last_tcnt1_h
lds temp4, last_tcnt1_x
sub temp5, YL ; a'-= b
sbc temp6, YH
sbc temp4, temp7
add temp5, temp1 ; a'+= c'
adc temp6, temp2
adc temp4, temp3
lsr temp4 ; a'>>= 1
ror temp6
ror temp5
add YL, temp5 ; b+= a' -> YL:YH:temp7 become filtered ZC time
adc YH, temp6
adc temp7, temp4
.else
lds YL, last_tcnt1_l ; restore original c as a'
lds YH, last_tcnt1_h
lds temp7, last_tcnt1_x
.endif
ldi temp4, (30 - MOTOR_ADVANCE) * 256 / 60
rcall update_timing_add_degrees
.if TIMING_OFFSET
sbiwx YL, YH, TIMING_OFFSET * CPU_MHZ
ldi temp4, byte3(TIMING_OFFSET * CPU_MHZ)
sbc temp7, temp4
.endif
sts com_time_l, YL ; Store start of next commutation
sts com_time_h, YH
sts com_time_x, temp7
rcall set_ocr1a_abs ; Set timer for start of next commutation
sbrc flags1, EVAL_RC
rjmp evaluate_rc ; Set new duty either way
;-----bko-----------------------------------------------------------------
; Unlike update_timing above, we try not to clobber XL, XH used as a loop
; counter in wait_for_edge.
set_new_duty: lds YL, rc_duty_l
lds YH, rc_duty_h
set_new_duty_l: cp YL, timing_duty_l
cpc YH, timing_duty_h
brcs set_new_duty10
movw YL, timing_duty_l ; Limit duty to timing_duty
set_new_duty10: cp YL, sys_control_l
cpc YH, sys_control_h
brcs set_new_duty11
movw YL, sys_control_l ; Limit duty to sys_control
set_new_duty11:
.if SLOW_THROTTLE
; If sys_control is higher than twice the current duty,
; limit it to that. This means that a steady-state duty
; cycle can double at any time, but any larger change will
; be rate-limited.
ldi2 temp1, temp2, PWR_MIN_START
cp YL, temp1
cpc YH, temp2
brcs set_new_duty12
movw temp1, YL ; temp1:temp2 >= PWR_MIN_START
set_new_duty12: lsl temp1
rol temp2
cp sys_control_l, temp1
cpc sys_control_h, temp2
brcs set_new_duty13
movw sys_control_l, temp1
set_new_duty13:
.endif
ldi2 temp1, temp2, MAX_POWER
sub temp1, YL ; Calculate OFF duty
sbc temp2, YH
breq set_new_duty_full
adiw YL, 0
breq set_new_duty_zero
; Not off and not full power
cbr flags1, (1<<FULL_POWER)
sbr flags1, (1<<POWER_ON)
; At higher PWM frequencies, halve the frequency
; when starting -- this helps hard drive startup
.if POWER_RANGE < 1000 * CPU_MHZ / 16
sbrs flags1, STARTUP
rjmp set_new_duty_set
lsl temp1
rol temp2
lsl YL
rol YH
.endif
set_new_duty_set:
; When off duty is short, skip complementary PWM; otherwise,
; compensate the off_duty time to account for the overhead.
.if COMP_PWM
set
ldi temp4, pwm_on_fast ; Short off period: skip complementary PWM
cpse temp2, ZH
ldi temp4, pwm_on_fast_high ; Off period >= 0x100
cpi2 temp1, temp2, CPWM_OVERHEAD_HIGH + CPWM_OVERHEAD_LOW, temp3
brcs set_new_duty21 ; Off period < off-to-on cycle count plus interrupt overhead
clt ; Not short off period, unset SKIP_CPWM
sbiwx temp1, temp2, CPWM_OVERHEAD_HIGH
.endif
ldi temp4, pwm_on ; Off period < 0x100
cpse temp2, ZH
ldi temp4, pwm_on_high ; Off period >= 0x100
set_new_duty21:
com YL ; Save one's complement of both
com temp1 ; low bytes for up-counting TCNT2
movw duty_l, YL ; Atomic set new ON duty for PWM interrupt
cli ; Critical section (off_duty & flags together)
movw off_duty_l, temp1 ; Set new OFF duty for PWM interrupt
sts pwm_on_ptr, temp4 ; Set Next PWM ON interrupt vector
.if COMP_PWM
bld flags2, SKIP_CPWM ; If to skip complementary PWM
.endif
sei
ret
set_new_duty_full:
; Full power
sbr flags1, (1<<FULL_POWER)+(1<<POWER_ON)
rjmp set_new_duty_set
set_new_duty_zero:
; Power off
cbr flags1, (1<<FULL_POWER)+(1<<POWER_ON)
ldi temp4, pwm_off ; Skip the on phase entirely
.if COMP_PWM
set ; Skip complementary PWM
.endif
rjmp set_new_duty21 |
| |